← back to Designerwallcoverings
scripts/apply-1104-map.js
35 lines
#!/usr/bin/env node
/* Apply 2026 MAP to the 1,104 unit-verified under-MAP products (/tmp/cs_1104_plan.json).
Per product: GraphQL-read variants, pick the NON-SAMPLE (Price Per Unit) variant, set its price
to MAP via productVariantsBulkUpdate. Sample variant untouched. DRY RUN unless --apply. */
const https=require('https'); const fs=require('fs');
const TOKEN=process.env.T, STORE='designer-laboratory-sandbox.myshopify.com';
const APPLY=process.argv.includes('--apply');
const sleep=ms=>new Promise(r=>setTimeout(r,ms));
function gql(q,vars){return new Promise(r=>{const rq=https.request(`https://${STORE}/admin/api/2024-10/graphql.json`,{method:'POST',headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'}},x=>{let b='';x.on('data',d=>b+=d);x.on('end',()=>{try{r(JSON.parse(b))}catch(e){r({errors:b})}})});rq.write(JSON.stringify({query:q,variables:vars||{}}));rq.end();});}
const plan=JSON.parse(fs.readFileSync('/tmp/cs_1104_plan.json','utf8'));
const isSample=o=>/sample/i.test(o||'');
(async()=>{
console.log(`${plan.length} products | mode: ${APPLY?'APPLY':'DRY RUN'}`);
let ok=0,fail=0,skip=0;
for(const it of plan){
try{
const d=await gql(`query($id:ID!){ product(id:$id){ variants(first:10){edges{node{id price selectedOptions{value}}}} } }`,{id:it.gid});
const vs=(d.data?.product?.variants?.edges||[]).map(e=>e.node);
const real=vs.filter(v=>!v.selectedOptions.some(o=>isSample(o.value)));
const v=real.sort((a,b)=>parseFloat(b.price)-parseFloat(a.price))[0];
if(!v){ skip++; continue; }
if(Math.abs(parseFloat(v.price)-it.map)<=0.5){ skip++; continue; } // already at MAP
if(!APPLY){ ok++; if(ok<=8)console.log(` $${v.price} -> $${it.map} ${it.vendor} ${it.sku}`); continue; }
const m=await gql(`mutation($pid:ID!,$vars:[ProductVariantsBulkInput!]!){ productVariantsBulkUpdate(productId:$pid, variants:$vars){ userErrors{field message} } }`,
{pid:it.gid, vars:[{id:v.id, price:String(it.map)}]});
const errs=m.data?.productVariantsBulkUpdate?.userErrors;
if(errs&&errs.length){ fail++; if(fail<=5)console.log(' ERR',it.sku,JSON.stringify(errs)); }
else { ok++; if(ok%100===0)console.log(` …${ok}/${plan.length}`); }
}catch(e){ fail++; }
await sleep(60);
}
console.log(`\nDONE — ${ok} ${APPLY?'repriced':'would reprice'}, ${fail} failed, ${skip} skipped(already-at-MAP/no-variant).`);
})().catch(e=>{console.error(e);process.exit(1);});