← back to Designer Wallcoverings
Anthology import: UOM resolver + ready-to-push DRAFT creator (DRY-RUN default)
2a58e23b6240ab76e958a8a6ba80eac21458a73a · 2026-07-22 08:40:54 -0700 · Steve
Files touched
A shopify/scripts/cadence/anthology-import-push.mjsA shopify/scripts/cadence/sdg-resolve-attrs.mjs
Diff
commit 2a58e23b6240ab76e958a8a6ba80eac21458a73a
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Jul 22 08:40:54 2026 -0700
Anthology import: UOM resolver + ready-to-push DRAFT creator (DRY-RUN default)
---
shopify/scripts/cadence/anthology-import-push.mjs | 79 +++++++++++++++++++++++
shopify/scripts/cadence/sdg-resolve-attrs.mjs | 44 +++++++++++++
2 files changed, 123 insertions(+)
diff --git a/shopify/scripts/cadence/anthology-import-push.mjs b/shopify/scripts/cadence/anthology-import-push.mjs
new file mode 100644
index 00000000..3fccf2c1
--- /dev/null
+++ b/shopify/scripts/cadence/anthology-import-push.mjs
@@ -0,0 +1,79 @@
+#!/usr/bin/env node
+/** Ready-to-push Anthology import: creates the 48 staged anthology_catalog rows as
+ * DW-compliant Shopify products. DRAFT by default (activation is a separate gated
+ * step after validate-before-activate). DRY-RUN unless --push. Steve 2026-07-22.
+ *
+ * Each product: vendor=Anthology, productType=Wallcovering, body=description,
+ * options[Size]=[Sample,<UOM>], variants: <dw_sku>-Sample @4.25 + <dw_sku> @retail
+ * (cost=trade), images from all_images, tags (Wallcovering/Anthology/<collection>/
+ * color:<color>/New Arrival/display_variant), SEO metafields. Auto-joins the
+ * anthology-wallcoverings smart collection via vendor=Anthology.
+ */
+import { execSync } from 'node:child_process';
+import fs from 'node:fs';
+const PUSH = process.argv.includes('--push');
+const ENV = fs.readFileSync(process.env.HOME+'/Projects/secrets-manager/.env','utf8');
+const TOKEN = (ENV.match(/^SHOPIFY_ADMIN_TOKEN=(.*)$/m)||[])[1].trim();
+const API='https://designer-laboratory-sandbox.myshopify.com/admin/api/2024-10';
+const q=(sql)=>execSync(`psql -h /tmp -d dw_unified -tAc "${sql}"`,{encoding:'utf8'});
+const sleep=(ms)=>new Promise(r=>setTimeout(r,ms));
+
+// description isn't in the table; pull from the source JSON by mfr_sku
+const srcDesc={};
+try{ for(const p of JSON.parse(fs.readFileSync('/tmp/sdg/anthology_full.json'))) srcDesc[p.sku]=p.description||''; }catch{}
+const rows=JSON.parse(q(`SELECT COALESCE(json_agg(t),'[]') FROM (SELECT dw_sku,mfr_sku,pattern_name,color_name,collection,uom,price_trade,price_retail,image_url,all_images,width,length,repeat_v FROM anthology_catalog ORDER BY dw_sku) t`).trim())
+ .map(r=>({...r, description:srcDesc[r.mfr_sku]||''}));
+
+async function shopify(path,method,body){
+ const r=await fetch(`${API}${path}`,{method,headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'},body:body?JSON.stringify(body):undefined});
+ const j=await r.json().catch(()=>({}));
+ if(!r.ok) throw new Error(`${r.status} ${JSON.stringify(j).slice(0,200)}`);
+ return j;
+}
+
+function buildProduct(x){
+ const title=`Anthology - ${x.pattern_name} ${x.color_name} Wallcovering | Anthology`;
+ const imgs=(()=>{try{return JSON.parse(x.all_images||'[]');}catch{return x.image_url?[x.image_url]:[];}})();
+ const tags=['Wallcovering','Anthology',x.collection,`color:${x.color_name}`,'New Arrival','display_variant'].filter(Boolean).join(', ');
+ return {product:{
+ title, vendor:'Anthology', product_type:'Wallcovering', status:'draft',
+ body_html:x.description?`<p>${x.description}</p>`:`<p>${x.pattern_name} ${x.color_name} — Anthology wallcovering.</p>`,
+ tags,
+ options:[{name:'Size'}],
+ variants:[
+ {option1:'Sample', sku:`${x.dw_sku}-Sample`, price:'4.25', inventory_management:null, requires_shipping:true, taxable:true},
+ {option1:x.uom, sku:x.dw_sku, price:String(x.price_retail), cost:String(x.price_trade), inventory_management:null, requires_shipping:true, taxable:true},
+ ],
+ images: imgs.slice(0,6).map(src=>({src})),
+ metafields:[
+ {namespace:'global',key:'title_tag',type:'single_line_text_field',value:`${x.pattern_name} ${x.color_name} — Anthology at Designer Wallcoverings`},
+ {namespace:'global',key:'description_tag',type:'single_line_text_field',value:`Anthology ${x.pattern_name} ${x.color_name} wallcovering. Samples & purchasing at Designer Wallcoverings. 1-888-373-4564`},
+ {namespace:'custom',key:'width',type:'single_line_text_field',value:x.width||''},
+ ],
+ }};
+}
+
+(async()=>{
+ console.log(`${PUSH?'*** LIVE PUSH ***':'DRY-RUN (no writes) — pass --push to create'} | ${rows.length} products`);
+ let ok=0, fail=[];
+ for(const x of rows){
+ const payload=buildProduct(x);
+ if(!PUSH){
+ const v=payload.product.variants;
+ console.log(` ${x.dw_sku} [${x.uom}] ${payload.product.title} | sample $4.25 + ${x.uom} $${x.price_retail} (cost $${x.price_trade}) | ${payload.product.images.length} img`);
+ continue;
+ }
+ try{
+ const res=await shopify('/products.json','POST',payload);
+ // set variant cost on inventory item (REST variant cost isn't persisted on create)
+ const sell=res.product.variants.find(v=>v.sku===x.dw_sku);
+ if(sell?.inventory_item_id) await shopify(`/inventory_items/${sell.inventory_item_id}.json`,'PUT',{inventory_item:{id:sell.inventory_item_id,cost:String(x.price_trade)}}).catch(()=>{});
+ // mirror shopify id back to staging
+ execSync(`psql -h /tmp -d dw_unified -c "UPDATE anthology_catalog SET shopify_product_id='${res.product.id}' WHERE dw_sku='${x.dw_sku}'"`);
+ ok++; console.log(` ✓ ${x.dw_sku} → product ${res.product.id} (DRAFT)`);
+ }catch(e){ fail.push([x.dw_sku,e.message]); console.log(` ✗ ${x.dw_sku}: ${e.message}`); }
+ await sleep(400);
+ }
+ if(PUSH){ console.log(`\nCREATED ${ok}/${rows.length} DRAFT products.`); if(fail.length){console.log('FAILURES:');fail.forEach(f=>console.log(' ',f));}
+ console.log('Next (gated): run validate-before-activate, then activate DRAFT→ACTIVE.'); }
+})();
diff --git a/shopify/scripts/cadence/sdg-resolve-attrs.mjs b/shopify/scripts/cadence/sdg-resolve-attrs.mjs
new file mode 100644
index 00000000..0431e189
--- /dev/null
+++ b/shopify/scripts/cadence/sdg-resolve-attrs.mjs
@@ -0,0 +1,44 @@
+#!/usr/bin/env node
+/** Resolve Magento option codes (UOM 6247/6241, form 6543) → labels via the
+ * session-authorized attribute API. Also dump a sample product's price/uom for
+ * discount inference. $0 local. Steve 2026-07-22 */
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
+let chromium;
+for (const p of ['/Users/macstudio3/Projects/Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/node_modules/playwright','playwright']) { try { ({ chromium } = require(p)); break; } catch {} }
+import fs from 'node:fs';
+const OUT='/tmp/sdg';
+const B='https://trade.sandersondesigngroup.com/us/api/n';
+const log=(...a)=>console.log(new Date().toISOString().slice(11,19),...a);
+const browser=await chromium.launch({headless:true});
+const ctx=await browser.newContext({storageState:OUT+'/state.json',
+ userAgent:'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36'});
+const req=ctx.request;
+try{
+ const page=await ctx.newPage();
+ await page.goto('https://trade.sandersondesigngroup.com/us/products/wallpaper',{waitUntil:'domcontentloaded',timeout:60000}).catch(()=>{});
+ await page.waitForTimeout(2500);
+ const ids=[6247,6241,6543];
+ for(const t of ['attribute','attributeOption','option','entity']){
+ try{
+ const r=await req.get(`${B}/load?type=${t}&verbosity=2&ids=${ids.join(',')}`,{timeout:40000});
+ const j=await r.json();
+ if(j.result && j.result.length){ log(`type=${t} →`); console.log(JSON.stringify(j.result,null,1).slice(0,1500)); }
+ else log(`type=${t}: empty`);
+ }catch(e){ log(`type=${t} err`,e.message); }
+ }
+ // load the uom attribute definition (options list)
+ for(const code of ['sdb_stock_uom_refents','sdb_form_variant','sdb_stock_uom_refents_data']){
+ try{
+ const r=await req.get(`${B}/load?type=attribute&verbosity=2&ids=${code}`,{timeout:40000});
+ const j=await r.json();
+ log(`attr ${code}:`); console.log(JSON.stringify(j.result||j,null,1).slice(0,2000));
+ }catch(e){ log('attr err',e.message); }
+ }
+ // fetch a full product with verbosity 3 to see resolved uom + price block
+ const r=await req.get(`${B}/load?type=product&verbosity=3&ids=1179891`,{timeout:40000});
+ const p=(await r.json()).result?.[0]||{};
+ log('sample product verbosity3 price-related keys:');
+ for(const k of Object.keys(p)) if(/price|uom|unit|qty|pack|roll|meter|metre|yard|sample|tier/i.test(k)) console.log(' ',k,'=',JSON.stringify(p[k]).slice(0,120));
+ fs.writeFileSync(OUT+'/sample_v3.json',JSON.stringify(p,null,1));
+}catch(e){ log('FATAL',e.message);} finally{ await browser.close(); }
← 41e292c1 cadence: add Maharam launch drip script (import+activate, me
·
back to Designer Wallcoverings
·
auto-save: 2026-07-22T09:12:53 (2 files) — shopify/scripts/c 7440b0f2 →