← back to Flock Fix Viewer
priceup-windsor.mjs
65 lines
// Price up the 20.5" WINDSOR flock sample-shells into 2-variant products, modeled on the
// active Bella's Windsor (FLOCK-1011): roll 20.5"x5.5yd @ $190.99 + sample $4.25. Reversible + ledgered.
import fs from 'fs'; import os from 'os'; import path from 'path';
const SHOP='designer-laboratory-sandbox.myshopify.com';
const TOKEN=fs.readFileSync(new URL('./.token',import.meta.url),'utf8').trim();
const LEDGER=path.join(os.homedir(),'.claude/yolo-queue/executed-reversible/ledger.jsonl');
const SNAPDIR=new URL('./windsor-snapshots/',import.meta.url); try{fs.mkdirSync(SNAPDIR,{recursive:true});}catch(e){}
const DRY=!process.argv.includes('--apply');
const API=`https://${SHOP}/admin/api/2024-10`, H={'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'};
const gql=async(q,v={})=>(await (await fetch(`${API}/graphql.json`,{method:'POST',headers:H,body:JSON.stringify({query:q,variables:v})})).json());
const sleep=ms=>new Promise(r=>setTimeout(r,ms));
const ROLL_OPT='Sold per single roll (20.5" x 5.5 yards)', ROLL_PRICE='190.99', SAMPLE_PRICE='4.25';
const SET=`mutation($input:ProductSetInput!){ productSet(synchronous:true, input:$input){ product{id handle variants(first:6){edges{node{title price sku}}}} userErrors{field message} } }`;
function buildInput(id, rollSku, sampleSku){
return { id, productType:'Wallcovering',
productOptions:[{name:'Size', values:[{name:ROLL_OPT},{name:'Sample'}]}],
variants:[
{optionValues:[{optionName:'Size',name:ROLL_OPT}], price:ROLL_PRICE, sku:rollSku, inventoryPolicy:'CONTINUE'},
{optionValues:[{optionName:'Size',name:'Sample'}], price:SAMPLE_PRICE, sku:sampleSku, inventoryPolicy:'CONTINUE'}
],
metafields:[
{namespace:'global',key:'v_prods_quantity_order_min',type:'single_line_text_field',value:'2'},
{namespace:'global',key:'v_prods_quantity_order_units',type:'single_line_text_field',value:'2'},
{namespace:'global',key:'unit_of_measure',type:'single_line_text_field',value:'Priced Per Single Roll. Ships untrimmed (30" untrimmed / 20.5" trimmed width).'},
{namespace:'global',key:'width',type:'single_line_text_field',value:'20.5 Inches Wide'},
{namespace:'custom',key:'width',type:'single_line_text_field',value:'20.5 Inches Wide'},
{namespace:'global',key:'length',type:'single_line_text_field',value:'5.5 Yards'},
{namespace:'global',key:'sqft_single_roll',type:'single_line_text_field',value:'14.09 Sq Ft'},
{namespace:'global',key:'sqft_double_roll',type:'single_line_text_field',value:'28.19 Sq Ft'},
{namespace:'custom',key:'sqft_single_roll',type:'single_line_text_field',value:'14.09 Sq Ft'},
{namespace:'custom',key:'sqft_double_roll',type:'single_line_text_field',value:'28.19 Sq Ft'},
{namespace:'global',key:'print_type',type:'single_line_text_field',value:'Flock'},
{namespace:'custom',key:'print_type',type:'single_line_text_field',value:'Flock'}
]
};
}
const maxRoll=n=>n.variants.edges.map(e=>e.node).filter(v=>!/sample/i.test(v.title||'')).reduce((m,v)=>Math.max(m,parseFloat(v.price)||0),0);
(async()=>{
let cur=null,all=[];
const Q=`query($c:String){ products(first:100, query:"windsor flock", after:$c){ pageInfo{hasNextPage endCursor} edges{node{ id handle title status variants(first:8){edges{node{title price sku}}} }}}}`;
do{const r=await gql(Q,{c:cur});const p=r?.data?.products;if(!p)break;all=all.concat(p.edges);cur=p.pageInfo.hasNextPage?p.pageInfo.endCursor:null;}while(cur);
const targets=all.map(e=>e.node).filter(n=>{
if(n.status==='ARCHIVED') return false;
if(!/windsor/i.test(n.handle+' '+n.title)) return false;
return maxRoll(n)<=10; // sample-only shells only
});
console.log(`Windsor sample-only shells: ${targets.length}`);
const list = DRY ? targets : targets;
for(const n of list){
const base=(n.variants.edges.map(e=>e.node).find(v=>v.sku)?.sku||n.handle).replace(/-sample$/i,'').replace(/-Sample$/,'');
if(DRY){ console.log(' would price up',n.status,n.handle.slice(0,46),'-> roll',base,'@ $190.99'); continue; }
fs.writeFileSync(new URL(n.handle.replace(/[^a-z0-9-]/gi,'_')+'.json',SNAPDIR), JSON.stringify(n,null,2));
const r=await gql(SET,{input:buildInput(n.id, base, base+'-Sample')});
const errs=r?.data?.productSet?.userErrors;
if(errs&&errs.length){console.log(' FAIL',n.handle,JSON.stringify(errs));continue;}
const vs=r.data.productSet.product.variants.edges.map(e=>e.node);
console.log(' OK',n.handle.slice(0,46),'->',vs.map(v=>v.title.slice(0,20)+'=$'+v.price).join(' | '));
fs.appendFileSync(LEDGER,JSON.stringify({ts:new Date().toISOString(),agent:'main:priceup-windsor',ticket:'flock-windsor-205',action:`price up ${n.handle} to 20.5" roll $190.99 + sample`,blast_radius:1,undo_cmd:`restore windsor-snapshots/${n.handle}.json`,verify:`2 variants roll $190.99 + sample $4.25, width 20.5"`})+'\n');
await sleep(400);
}
console.log(`${DRY?'DRY':'DONE'} · ${targets.length} Windsor shells`);
})();