← back to York Yardgood Exec
Revert 15 Nuovo (A-Street roll goods) wrongly relabeled per-yard; decimal-width trap
9b2672b5116c43a4746efbff96a86046520fa799 · 2026-09-09 16:15:41 -0700 · Steve
Files touched
A nuovo-id.mjsA nuovo-revert.mjsA v32.mjs
Diff
commit 9b2672b5116c43a4746efbff96a86046520fa799
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Sep 9 16:15:41 2026 -0700
Revert 15 Nuovo (A-Street roll goods) wrongly relabeled per-yard; decimal-width trap
---
nuovo-id.mjs | 18 ++++++++++++++++++
nuovo-revert.mjs | 35 +++++++++++++++++++++++++++++++++++
v32.mjs | 11 +++++++++++
3 files changed, 64 insertions(+)
diff --git a/nuovo-id.mjs b/nuovo-id.mjs
new file mode 100644
index 0000000..385ad20
--- /dev/null
+++ b/nuovo-id.mjs
@@ -0,0 +1,18 @@
+import { readFileSync } from 'node:fs';
+const TOKEN=readFileSync(process.env.HOME+'/Projects/secrets-manager/.env','utf8').split('\n').find(l=>l.startsWith('SHOPIFY_ADMIN_TOKEN=')).split('=')[1].trim();
+const SHOP='https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10/graphql.json';
+const tgt=JSON.parse(readFileSync('./data/york32-target.json'));
+for(const sku of ['DWJS-92436','DWJS-91858']){ // Nuovo, Argent
+ const t=tgt.find(x=>x.sell_sku===sku); const gid=`gid://shopify/Product/${t.product_id}`;
+ const q=`{p:product(id:"${gid}"){ title vendor handle
+ mfr:metafield(namespace:"global",key:"manufacturer_sku"){value}
+ dwsku:metafield(namespace:"global",key:"dw_sku"){value}
+ mfr2:metafield(namespace:"custom",key:"manufacturer_sku"){value}
+ images(first:2){edges{node{url}}} }}`;
+ const r=await (await fetch(SHOP,{method:'POST',headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'},body:JSON.stringify({query:q})})).json();
+ const p=r.data.p;
+ const img=p.images.edges[0]?.node.url.split('/').pop().split('?')[0];
+ console.log(`${sku} ${t.pattern} | vendor='${p.vendor}' handle=${p.handle}`);
+ console.log(` mfr(global)=${p.mfr?.value} mfr(custom)=${p.mfr2?.value} dw_sku=${p.dwsku?.value}`);
+ console.log(` image=${img}`);
+}
diff --git a/nuovo-revert.mjs b/nuovo-revert.mjs
new file mode 100644
index 0000000..9084504
--- /dev/null
+++ b/nuovo-revert.mjs
@@ -0,0 +1,35 @@
+#!/usr/bin/env node
+// Revert the 15 Nuovo products wrongly relabeled Sold-Per-Yard by york32 — they are 27.55" A-Street ROLL goods.
+// Restores: uom -> old (Priced Per Single Roll), min/units -> 2, variant option -> old title. Price kept.
+// DRY-RUN default; --apply writes. Uses restore-map-32.jsonl (old uom/title) + york32-target (Nuovo ids).
+import { readFileSync } from 'node:fs';
+const APPLY=process.argv.includes('--apply');
+const TOKEN=readFileSync(process.env.HOME+'/Projects/secrets-manager/.env','utf8').split('\n').find(l=>l.startsWith('SHOPIFY_ADMIN_TOKEN=')).split('=')[1].trim();
+const SHOP='https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10/graphql.json';
+const DIR=new URL('.',import.meta.url).pathname;
+const T='single_line_text_field';
+const nuovoIds=new Set(JSON.parse(readFileSync(DIR+'data/york32-target.json')).filter(r=>r.group==='Nuovo').map(r=>String(r.product_id)));
+const rest=readFileSync(DIR+'data/restore-map-32.jsonl','utf8').trim().split('\n').map(l=>JSON.parse(l)).filter(r=>nuovoIds.has(String(r.pid)));
+const gql=(q,v)=>fetch(SHOP,{method:'POST',headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'},body:JSON.stringify({query:q,variables:v})}).then(r=>r.json());
+console.log(`Nuovo revert — ${rest.length} products (expect 15)`);
+let ok=0,fail=0;
+for(const r of rest){
+ const pid=`gid://shopify/Product/${r.pid}`;
+ const oldUom=r.old.uom||'Priced Per Single Roll'; const oldTitle=r.old.title||'Sold Per Single Roll';
+ if(!APPLY){console.log(` DRY ${r.pid} -> uom='${oldUom}' option='${oldTitle}' min=2 units=2 (price kept)`);ok++;continue;}
+ const mfs=[
+ {ownerId:pid,namespace:'global',key:'unit_of_measure',type:T,value:oldUom},
+ {ownerId:pid,namespace:'global',key:'v_prods_quantity_order_min',type:T,value:'2'},
+ {ownerId:pid,namespace:'global',key:'v_prods_quantity_order_units',type:T,value:'2'},
+ {ownerId:r.sellVid,namespace:'custom',key:'v_prod_quantity_order_min',type:T,value:'2'},
+ {ownerId:r.sellVid,namespace:'custom',key:'v_prods_quantity_order_units',type:T,value:'2'},
+ ];
+ const m=await gql(`mutation($m:[MetafieldsSetInput!]!){metafieldsSet(metafields:$m){userErrors{message}}}`,{m:mfs});
+ if(m.data.metafieldsSet.userErrors.length){console.log(' MF-ERR',r.pid,JSON.stringify(m.data.metafieldsSet.userErrors));fail++;continue;}
+ const vu=await gql(`mutation($pid:ID!,$v:[ProductVariantsBulkInput!]!){productVariantsBulkUpdate(productId:$pid,variants:$v){userErrors{message}}}`,
+ {pid,v:[{id:r.sellVid,optionValues:[{optionName:'Size',name:oldTitle}]}]});
+ if(vu.data.productVariantsBulkUpdate.userErrors.length){console.log(' VAR-ERR',r.pid,JSON.stringify(vu.data.productVariantsBulkUpdate.userErrors));fail++;continue;}
+ console.log(` OK ${r.pid} reverted to '${oldTitle}' / ${oldUom} / min2`);ok++;
+ await new Promise(x=>setTimeout(x,400));
+}
+console.log(`\n${APPLY?'REVERTED':'DRY-RUN'} — ok=${ok} fail=${fail}`);
diff --git a/v32.mjs b/v32.mjs
new file mode 100644
index 0000000..a6d26a9
--- /dev/null
+++ b/v32.mjs
@@ -0,0 +1,11 @@
+import { readFileSync } from 'node:fs';
+const TOKEN=readFileSync(process.env.HOME+'/Projects/secrets-manager/.env','utf8').split('\n').find(l=>l.startsWith('SHOPIFY_ADMIN_TOKEN=')).split('=')[1].trim();
+const SHOP='https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10/graphql.json';
+for(const [sku,pid] of [['DWJS-92088 Trousseau','92088'],['DWJS-92436 Nuovo','92436'],['DWJS-91858 Argent','91858']]){
+ const t=JSON.parse(readFileSync('./data/york32-target.json')).find(x=>x.sell_sku===sku.split(' ')[0]);
+ const gid=`gid://shopify/Product/${t.product_id}`;
+ const q=`{p:product(id:"${gid}"){uom:metafield(namespace:"global",key:"unit_of_measure"){value} min:metafield(namespace:"global",key:"v_prods_quantity_order_min"){value} variants(first:5){edges{node{title price position}}}}}`;
+ const r=await (await fetch(SHOP,{method:'POST',headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'},body:JSON.stringify({query:q})})).json();
+ const p=r.data.p; const s=p.variants.edges.map(e=>e.node).find(v=>!/sample/i.test(v.title))||p.variants.edges[0].node;
+ console.log(`${sku.padEnd(18)} $${s.price} '${s.title}' pos${s.position} uom=${p.uom?.value} min=${p.min?.value}`);
+}
← f27b650 auto-data-snapshot: 2026-09-09T16:10:41 (1 data files) — dat
·
back to York Yardgood Exec
·
(newest)