← back to Designer Wallcoverings
auto-save: 2026-07-10T21:40:28 (4 files) — pending-approval/boost-filter-consolidation-2026-06-25 scripts/pr-contact-for-price vendor-scrapers/china-seas-refresh shopify/scripts/schu-pillow-strip-sample.js
3510746feb1ca383b833d1d7be7615c960c4c5ca · 2026-07-10 21:40:32 -0700 · Steve Abrams
Files touched
A shopify/scripts/schu-pillow-strip-sample.js
Diff
commit 3510746feb1ca383b833d1d7be7615c960c4c5ca
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Fri Jul 10 21:40:32 2026 -0700
auto-save: 2026-07-10T21:40:28 (4 files) — pending-approval/boost-filter-consolidation-2026-06-25 scripts/pr-contact-for-price vendor-scrapers/china-seas-refresh shopify/scripts/schu-pillow-strip-sample.js
---
shopify/scripts/schu-pillow-strip-sample.js | 44 +++++++++++++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/shopify/scripts/schu-pillow-strip-sample.js b/shopify/scripts/schu-pillow-strip-sample.js
new file mode 100644
index 00000000..2af719ba
--- /dev/null
+++ b/shopify/scripts/schu-pillow-strip-sample.js
@@ -0,0 +1,44 @@
+#!/usr/bin/env node
+/* Strip the leftover $4.25 Sample variant from LEGACY priced DWPIL pillows (already have a real
+ * priced variant). Steve's rule: pillows carry NO sample. DELETE only the -Sample variant, keep the
+ * real one. NEVER leave a product with 0 variants. STAGING mirror updated after each success.
+ * node schu-pillow-strip-sample.js --pilot 3 # first 3, verify
+ * node schu-pillow-strip-sample.js --rest --commit # remaining, commit
+ * node schu-pillow-strip-sample.js --all --commit
+ */
+const fs=require('fs'); const { execFileSync }=require('child_process');
+function envval(k){ for(const l of fs.readFileSync('/Users/macstudio3/Projects/secrets-manager/.env','utf8').split('\n')){ if(l.startsWith(k+'=')){ let v=l.slice(k.length+1).trim(); if((v[0]==='"'&&v.endsWith('"'))||(v[0]==="'"&&v.endsWith("'")))v=v.slice(1,-1); return v; } } return null; }
+const TOKEN=envval('SHOPIFY_ADMIN_TOKEN'); let STORE=envval('SHOPIFY_STORE')||envval('SHOPIFY_STORE_DOMAIN'); if(!/myshopify/.test(STORE))STORE='designer-laboratory-sandbox.myshopify.com';
+const args=process.argv.slice(2); const COMMIT=args.includes('--commit');
+const pi=args.indexOf('--pilot'); const PILOT=pi>-1?parseInt(args[pi+1]):0; const REST=args.includes('--rest'); const ALL=args.includes('--all');
+const psql=s=>execFileSync('psql',['-At','-F','\t','-d','dw_unified','-c',s],{encoding:'utf8',maxBuffer:1<<28}).trim();
+const q=s=>"'"+String(s).replace(/'/g,"''")+"'";
+const sleep=ms=>new Promise(r=>setTimeout(r,ms));
+async function gql(query,variables){ const r=await fetch(`https://${STORE}/admin/api/2024-10/graphql.json`,{method:'POST',headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'},body:JSON.stringify({query,variables})}); const j=await r.json(); if(j.errors)throw new Error(JSON.stringify(j.errors).slice(0,200)); return j.data; }
+
+(async()=>{
+ let rows=psql(`SELECT shopify_id, handle, dw_sku FROM shopify_products
+ WHERE (dw_sku ILIKE 'DWPIL%' OR sku ILIKE 'DWPIL%') AND status='ACTIVE'
+ AND has_sample_variant=true AND has_product_variant=true ORDER BY dw_sku;`).split('\n').filter(Boolean).map(l=>{const[a,b,c]=l.split('\t');return{gid:a,handle:b,dw:c};});
+ if(PILOT) rows=rows.slice(0,PILOT); else if(REST) rows=rows.slice(3);
+ console.log(`targets: ${rows.length} | mode ${PILOT?'PILOT':REST?'REST':ALL?'ALL':'DRY'} | commit=${COMMIT}`);
+ let done=0, skip=0, err=0;
+ for(const p of rows){
+ try{
+ const d=await gql(`query($id:ID!){product(id:$id){id status variants(first:20){edges{node{id sku price}}}}}`,{id:p.gid});
+ const vs=d.product.variants.edges.map(e=>e.node);
+ if(vs.length<2){ skip++; console.log(` SKIP ${p.handle} (only ${vs.length} variant)`); continue; }
+ const sample=vs.find(v=>/-sample$/i.test(v.sku||'')) || vs.find(v=>parseFloat(v.price)===4.25);
+ const real=vs.find(v=>v!==sample);
+ if(!sample||!real){ skip++; console.log(` SKIP ${p.handle} (no clear sample/real: ${vs.map(v=>v.sku+'@'+v.price).join(',')})`); continue; }
+ if(!COMMIT){ console.log(` DRY ${p.handle}: would delete ${sample.sku}@${sample.price}, keep ${real.sku}@${real.price}`); done++; continue; }
+ const res=await gql(`mutation($pid:ID!,$ids:[ID!]!){productVariantsBulkDelete(productId:$pid,variantsIds:$ids){userErrors{field message}}}`,{pid:p.gid,ids:[sample.id]});
+ const ue=res.productVariantsBulkDelete.userErrors;
+ if(ue&&ue.length){ err++; console.log(` ERR ${p.handle}: ${JSON.stringify(ue)}`); continue; }
+ psql(`UPDATE shopify_products SET has_sample_variant=false, updated_at_shopify=now() WHERE shopify_id=${q(p.gid)};`);
+ done++; console.log(` OK ${p.handle}: dropped ${sample.sku}, kept ${real.sku}@$${real.price}`);
+ }catch(e){ err++; console.log(` ERR ${p.handle}: ${e.message}`); }
+ await sleep(300);
+ }
+ console.log(`DONE: processed ${done}, skipped ${skip}, errors ${err}`);
+})();
← ecc70b50 auto-save: 2026-07-10T20:40:18 (4 files) — pending-approval/
·
back to Designer Wallcoverings
·
auto-save: 2026-07-10T22:40:39 (4 files) — pending-approval/ cad7004c →