← back to Designerwallcoverings
snapshot before restart: preserve in-flight work (auto-saved by /restart pre-reboot)
09fa48a6060c5b35964824514123d55248bf789a · 2026-08-17 06:52:32 -0700 · Steve
Files touched
A scripts/greenland-onboard/inspect-miyago-meta.mjsA scripts/greenland-onboard/inspect-miyago.mjsA scripts/tk10349-flush-page-cache.shA shopify/scripts/js-sample-leak-scan.mjsA shopify/scripts/reprice-knoll15-belowcost.mjsA shopify/scripts/reprice-med-956-below-map.mjs
Diff
commit 09fa48a6060c5b35964824514123d55248bf789a
Author: Steve <steve@designerwallcoverings.com>
Date: Mon Aug 17 06:52:32 2026 -0700
snapshot before restart: preserve in-flight work (auto-saved by /restart pre-reboot)
---
scripts/greenland-onboard/inspect-miyago-meta.mjs | 31 +++++++++
scripts/greenland-onboard/inspect-miyago.mjs | 37 ++++++++++
scripts/tk10349-flush-page-cache.sh | 35 ++++++++++
shopify/scripts/js-sample-leak-scan.mjs | 83 +++++++++++++++++++++++
shopify/scripts/reprice-knoll15-belowcost.mjs | 59 ++++++++++++++++
shopify/scripts/reprice-med-956-below-map.mjs | 59 ++++++++++++++++
6 files changed, 304 insertions(+)
diff --git a/scripts/greenland-onboard/inspect-miyago-meta.mjs b/scripts/greenland-onboard/inspect-miyago-meta.mjs
new file mode 100644
index 0000000..9c574fa
--- /dev/null
+++ b/scripts/greenland-onboard/inspect-miyago-meta.mjs
@@ -0,0 +1,31 @@
+#!/usr/bin/env node
+/** READ-ONLY: metafields + variant min/step for the 6 ACTIVE Mr. Miyago products + siblings. */
+import { gql } from '../lib/shopify.mjs';
+
+const IDS = {
+ 'Cork-216222': '6672902553651',
+ 'Cork-216223': '6672899768371',
+ 'Cork-216224': '6672899866675',
+ 'Cork-216225': '6672900063283',
+ 'Cork-216226': '6672900161587',
+ 'Cork-216227': '6672900325427',
+};
+
+for (const [sku, id] of Object.entries(IDS)) {
+ const q = `{ product(id:"gid://shopify/Product/${id}"){
+ title status
+ metafields(first:30){ nodes{ namespace key value } }
+ variants(first:5){ nodes{ sku title price
+ metafields(first:20){ nodes{ namespace key value } } } }
+ }}`;
+ const d = await gql(q);
+ if (d?.__err) { console.log(sku, 'ERR', JSON.stringify(d.__err)); continue; }
+ const p = d.product;
+ console.log(`\n### ${sku} — ${p.title} [${p.status}]`);
+ const pm = p.metafields.nodes.filter(m => /quantity_order|Brand|width|unit_of_measure|manufacturer_sku|brand|color/i.test(m.key));
+ console.log(' product-metafields:', pm.map(m => `${m.namespace}.${m.key}=${m.value}`).join(' | ') || '(none of interest)');
+ for (const v of p.variants.nodes) {
+ const vm = v.metafields.nodes.filter(m => /quantity_order/i.test(m.key));
+ console.log(` variant ${v.sku} [${v.title}] $${v.price} ${vm.map(m => `${m.namespace}.${m.key}=${m.value}`).join(' | ') || '(no qty metafields)'}`);
+ }
+}
diff --git a/scripts/greenland-onboard/inspect-miyago.mjs b/scripts/greenland-onboard/inspect-miyago.mjs
new file mode 100644
index 0000000..6daec6f
--- /dev/null
+++ b/scripts/greenland-onboard/inspect-miyago.mjs
@@ -0,0 +1,37 @@
+#!/usr/bin/env node
+/**
+ * READ-ONLY inspection of the Mr. Miyago cork DRAFT samples Cork-216222..216227.
+ * Searches Shopify by variant SKU, dumps full record shape for the go/no-go decision.
+ * node inspect-miyago.mjs
+ */
+import { gql } from '../lib/shopify.mjs';
+
+const SKUS = ['Cork-216222', 'Cork-216223', 'Cork-216224', 'Cork-216225', 'Cork-216226', 'Cork-216227'];
+
+async function findBySku(sku) {
+ const q = `query($q:String!){
+ products(first:5, query:$q){
+ nodes{
+ id legacyResourceId title handle status vendor productType tags
+ descriptionHtml
+ onlineStoreUrl
+ publishedAt
+ featuredImage{ url altText }
+ images(first:10){ nodes{ id url altText } }
+ variants(first:25){ nodes{ id sku title price inventoryQuantity inventoryPolicy inventoryItem{ tracked } } }
+ collections(first:20){ nodes{ title handle } }
+ resourcePublicationsCount{ count }
+ }
+ }
+ }`;
+ const d = await gql(q, { q: `sku:${sku}` });
+ if (d?.__err) { console.error(sku, 'ERR', JSON.stringify(d.__err)); return []; }
+ return d.products.nodes;
+}
+
+const out = {};
+for (const sku of SKUS) {
+ const nodes = await findBySku(sku);
+ out[sku] = nodes;
+}
+console.log(JSON.stringify(out, null, 2));
diff --git a/scripts/tk10349-flush-page-cache.sh b/scripts/tk10349-flush-page-cache.sh
new file mode 100644
index 0000000..730e4b3
--- /dev/null
+++ b/scripts/tk10349-flush-page-cache.sh
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+# TK-10349 — flush Shopify page cache for the 5 category pages so the ALREADY-DEPLOYED
+# corrected templates render for customers. The live theme assets are confirmed fixed
+# (!= blank guard + 5 mislabel repoints, updated 2026-08-08 07:48); Shopify's page cache
+# is holding stale HTML that template edits didn't bust. Re-saving each Page record
+# (no-op template_suffix set) bumps updated_at and invalidates that page's cache.
+# Safe: no content change, reversible. Verifies convergence + reports.
+set -euo pipefail
+cd ~/Projects/secrets-manager
+FA=$(grep -E '^SHOPIFY_FULL_ACCESS_TOKEN=' .env | cut -d= -f2- | tr -d '"'"'"' \r')
+HOST="designer-laboratory-sandbox.myshopify.com"; API="2024-10"
+# handle -> page id : template_suffix
+declare -A PID=( [shop-by-material]=182153838643 [brands]=208010608691 [designer-wallpaper-styles]=176660709427 [collections]=27297808449 [commercial-wallcoverings]=182116286515 )
+declare -A SUF=( [shop-by-material]=material [brands]=brands [designer-wallpaper-styles]=styles [collections]=collections [commercial-wallcoverings]=commercial )
+echo ">> Touching 5 pages to flush cache..."
+for h in shop-by-material brands designer-wallpaper-styles collections commercial-wallcoverings; do
+ code=$(curl -s -o /dev/null -w '%{http_code}' -X PUT "https://$HOST/admin/api/$API/pages/${PID[$h]}.json" \
+ -H "X-Shopify-Access-Token: $FA" -H 'Content-Type: application/json' \
+ -d "{\"page\":{\"id\":${PID[$h]},\"template_suffix\":\"${SUF[$h]}\"}}")
+ echo " $h -> HTTP $code"
+done
+echo ">> Verifying (polls up to ~2.5 min)..."
+UA='Mozilla/5.0 AppleWebKit/537.36 Chrome/120'
+for i in $(seq 1 10); do
+ sleep 15
+ tot=0
+ for p in brands collections designer-wallpaper-styles; do
+ n=$(curl -s -A "$UA" "https://designerwallcoverings.com/pages/$p?_x=$RANDOM$i" | grep -c 'Liquid error' || true)
+ tot=$((tot+n))
+ done
+ jute=$(curl -s -A "$UA" "https://designerwallcoverings.com/pages/shop-by-material?_x=$RANDOM$i" | grep -c '/collections/jute"' || true)
+ echo " attempt $i: liquidErrors(3 pages)=$tot material-jute-new=$jute"
+ if [ "$tot" -eq 0 ] && [ "$jute" -gt 0 ]; then echo ">> ✅ CONVERGED — customers now see the fix."; exit 0; fi
+done
+echo ">> Not fully converged yet; page cache may need a few more minutes. Re-run to re-check."
diff --git a/shopify/scripts/js-sample-leak-scan.mjs b/shopify/scripts/js-sample-leak-scan.mjs
new file mode 100644
index 0000000..f5c752a
--- /dev/null
+++ b/shopify/scripts/js-sample-leak-scan.mjs
@@ -0,0 +1,83 @@
+#!/usr/bin/env node
+// TK (sample-price-as-sellable leak) — enumerate Jeffrey Stevens SELLABLE variants priced $4.25
+// (the memo-sample price leaking as the real roll price → sold at a loss + GMC $4.25 disapproval).
+// READ-ONLY by default. --apply reprices to the real roll price (jeffrey_stevens_catalog.price_retail).
+// --limit N caps how many are repriced (canary). Reversibility ledger records prior price.
+// Store: designer-laboratory-sandbox.myshopify.com (LIVE). Vendor scoped to Jeffrey Stevens only.
+import fs from 'node:fs';
+import { execSync } from 'node:child_process';
+
+const APPLY = process.argv.includes('--apply');
+const LIMIT = (()=>{ const i=process.argv.indexOf('--limit'); return i>=0?parseInt(process.argv[i+1]):Infinity; })();
+const TT = execSync("grep -m1 '^SHOPIFY_ADMIN_TOKEN=' ~/Projects/secrets-manager/.env | cut -d= -f2-", {shell:'/bin/zsh'}).toString().trim();
+const PSQL = '/opt/homebrew/Cellar/postgresql@14/14.23/bin/psql';
+const SHOP = 'designer-laboratory-sandbox.myshopify.com';
+const API = '2024-10';
+const DIR = '/Users/macstudio3/Projects/designerwallcoverings/shopify/scripts/data/js-sample-leak';
+fs.mkdirSync(DIR, {recursive:true});
+const LEDGER = `${DIR}/js-sample-leak-ledger-2026-08-09.json`;
+const FOUND = `${DIR}/js-sample-leak-found-2026-08-09.csv`;
+
+const sleep = ms => new Promise(r=>setTimeout(r,ms));
+async function gql(query, variables){
+ for(let a=0;a<6;a++){
+ const r = await fetch(`https://${SHOP}/admin/api/${API}/graphql.json`,{method:'POST',
+ headers:{'X-Shopify-Access-Token':TT,'Content-Type':'application/json'},
+ body:JSON.stringify({query,variables})});
+ const j = await r.json();
+ if(j.errors && JSON.stringify(j.errors).includes('THROTTLED')){ await sleep(2000); continue; }
+ const th=j?.extensions?.cost?.throttleStatus; if(th && th.currentlyAvailable<400){ await sleep(1500); }
+ return j;
+ }
+ throw new Error('gql retries exhausted');
+}
+// real roll price per SKU from the catalog (price_retail)
+function realPrice(sku){
+ try{ const out=execSync(`${PSQL} -h /tmp -d dw_unified -tAc "select price_retail from jeffrey_stevens_catalog where sku='${sku.replace(/'/g,"''")}' and price_retail is not null limit 1"`).toString().trim();
+ const v=parseFloat(out); return isFinite(v)&&v>4.25?v:null; }catch{ return null; }
+}
+
+const Q = `query($cursor:String){products(first:40,query:"vendor:'Jeffrey Stevens' status:active",after:$cursor){
+ pageInfo{hasNextPage endCursor} edges{node{id title
+ variants(first:20){edges{node{id sku title price}}}}}}}`;
+
+const leaks=[]; let scanned=0, cursor=null, hasNext=true;
+while(hasNext){
+ const d = await gql(Q,{cursor});
+ const conn = d?.data?.products; if(!conn){ console.error('ERR',JSON.stringify(d).slice(0,300)); break; }
+ for(const e of conn.edges){
+ scanned++;
+ for(const ve of e.node.variants.edges){
+ const v=ve.node; const isSample=/sample/i.test(v.sku||'')||/sample/i.test(v.title||'');
+ if(!isSample && parseFloat(v.price)===4.25){
+ leaks.push({product:e.node.title, sku:v.sku, variantTitle:v.title, price:4.25,
+ variant_id:v.id, product_id:e.node.id, real:realPrice(v.sku)});
+ }
+ }
+ }
+ hasNext = conn.pageInfo.hasNextPage; cursor = conn.pageInfo.endCursor;
+}
+
+fs.writeFileSync(FOUND, 'sku,product,variantTitle,real_price\n'+leaks.map(l=>`${l.sku},"${l.product}","${l.variantTitle}",${l.real??''}`).join('\n'));
+const withReal = leaks.filter(l=>l.real!=null);
+console.log(`SCANNED ${scanned} JS active products | LEAKS(sellable @ $4.25)=${leaks.length} | with real price_retail=${withReal.length} | missing real=${leaks.length-withReal.length}`);
+console.log(`found list: ${FOUND}`);
+
+if(!APPLY){ console.log('READ-ONLY (no --apply). Nothing written.'); process.exit(0); }
+
+const MUT=`mutation($productId:ID!,$variants:[ProductVariantsBulkInput!]!){productVariantsBulkUpdate(productId:$productId,variants:$variants){productVariants{id price}userErrors{field message}}}`;
+const ledger=[]; let fixed=0, skipped_noreal=0, failed=0, capped=0;
+for(const l of leaks){
+ if(l.real==null){ skipped_noreal++; ledger.push({sku:l.sku,action:'SKIP_NO_REAL_PRICE'}); continue; }
+ if(fixed>=LIMIT){ capped++; continue; }
+ try{
+ const res=await gql(MUT,{productId:l.product_id, variants:[{id:l.variant_id, price:l.real.toFixed(2)}]});
+ const ue=res?.data?.productVariantsBulkUpdate?.userErrors||[];
+ const np=res?.data?.productVariantsBulkUpdate?.productVariants?.[0]?.price;
+ if(ue.length===0 && np && Math.abs(parseFloat(np)-l.real)<=0.01){ fixed++; ledger.push({sku:l.sku,action:'FIXED',prior:4.25,new:l.real,variant_id:l.variant_id.split('/').pop()}); }
+ else { failed++; ledger.push({sku:l.sku,action:'FAIL',userErrors:ue,np}); }
+ }catch(err){ failed++; ledger.push({sku:l.sku,action:'ERROR',error:String(err)}); }
+}
+fs.writeFileSync(LEDGER, JSON.stringify({ts:'2026-08-09',ticket:'js-sample-leak',approved:'Steve email 2026-08-09 work-on-top-3-gated Dust2026',limit:LIMIT,counts:{leaks:leaks.length,fixed,skipped_noreal,failed,capped},results:ledger},null,2));
+console.log(`APPLY done. fixed=${fixed} skipped_noreal=${skipped_noreal} failed=${failed} capped(over --limit)=${capped}`);
+console.log(`ledger: ${LEDGER}`);
diff --git a/shopify/scripts/reprice-knoll15-belowcost.mjs b/shopify/scripts/reprice-knoll15-belowcost.mjs
new file mode 100644
index 0000000..1c00ddf
--- /dev/null
+++ b/shopify/scripts/reprice-knoll15-belowcost.mjs
@@ -0,0 +1,59 @@
+#!/usr/bin/env node
+// TK-10014 — raise the 1,141 HIGH_two_source below-MAP DWKK roll variants to MAP on LIVE Shopify.
+// Steve-approved in-session 2026-08-08 ("do the 1,141"). Safeguards: live re-fetch current price,
+// SKIP any already >= MAP, raise rest to MAP, full prior->new reversibility ledger, throttle-aware.
+import fs from 'node:fs';
+import { execSync } from 'node:child_process';
+
+const TT = execSync("grep -m1 '^SHOPIFY_ADMIN_TOKEN=' ~/Projects/secrets-manager/.env | cut -d= -f2-", {shell:'/bin/zsh'}).toString().trim();
+const SHOP = 'designer-laboratory-sandbox.myshopify.com';
+const API = '2024-10';
+const DIR = '/Users/macstudio3/Projects/designerwallcoverings/shopify/scripts/data/knoll';
+const LEDGER = `${DIR}/knoll15-belowcost-reprice-ledger-2026-08-09.json`;
+const LOG = `${DIR}/knoll15-belowcost-progress.log`;
+
+const rows = fs.readFileSync('/tmp/knoll15.csv','utf8').trim().split('\n').map(l=>{const[sku,map]=l.split(',');return{sku,map:parseFloat(map)};});
+const log = (m)=>{ const line=`[${new Date().toISOString()}] ${m}`; fs.appendFileSync(LOG, line+'\n'); console.log(line); };
+
+async function gql(query, variables){
+ for(let attempt=0; attempt<6; attempt++){
+ const r = await fetch(`https://${SHOP}/admin/api/${API}/graphql.json`, {method:'POST',
+ headers:{'X-Shopify-Access-Token':TT,'Content-Type':'application/json'},
+ body: JSON.stringify({query, variables})});
+ const j = await r.json();
+ const th = j?.extensions?.cost?.throttleStatus;
+ if(j.errors && JSON.stringify(j.errors).includes('THROTTLED')){ await sleep(2000); continue; }
+ if(th && th.currentlyAvailable < 600){ await sleep(1500); }
+ return j;
+ }
+ throw new Error('gql retries exhausted');
+}
+const sleep = ms => new Promise(r=>setTimeout(r,ms));
+
+const READ = `query($q:String!){productVariants(first:10,query:$q){edges{node{id sku price product{id status}}}}}`;
+const MUT = `mutation($productId:ID!,$variants:[ProductVariantsBulkInput!]!){productVariantsBulkUpdate(productId:$productId,variants:$variants){productVariants{id price}userErrors{field message}}}`;
+
+const ledger=[]; let raised=0, skipped_atmap=0, skipped_inactive=0, notfound=0, failed=0;
+
+for(let i=0;i<rows.length;i++){
+ const t = rows[i];
+ try{
+ const d = await gql(READ,{q:`sku:${t.sku}`});
+ const edges = d?.data?.productVariants?.edges || [];
+ const roll = edges.find(e=>e.node.sku===t.sku);
+ if(!roll){ notfound++; ledger.push({sku:t.sku,action:'NOT_FOUND'}); continue; }
+ const n=roll.node, cur=parseFloat(n.price), pid=n.product.id, vid=n.id;
+ if(n.product.status!=='ACTIVE'){ skipped_inactive++; ledger.push({sku:t.sku,action:'SKIP_INACTIVE',status:n.product.status,prior:cur}); continue; }
+ if(cur >= t.map-0.01){ skipped_atmap++; ledger.push({sku:t.sku,action:'SKIP_ALREADY_GE_MAP',prior:cur,map:t.map}); continue; }
+ const res = await gql(MUT,{productId:pid, variants:[{id:vid, price:t.map.toFixed(2)}]});
+ const ue = res?.data?.productVariantsBulkUpdate?.userErrors || [];
+ const newp = res?.data?.productVariantsBulkUpdate?.productVariants?.[0]?.price;
+ const ok = ue.length===0 && newp!=null && Math.abs(parseFloat(newp)-t.map)<=0.01;
+ if(ok){ raised++; ledger.push({sku:t.sku,variant_id:vid.split('/').pop(),action:'RAISED',prior:cur,new:parseFloat(newp),map:t.map}); }
+ else { failed++; ledger.push({sku:t.sku,action:'FAIL',prior:cur,map:t.map,userErrors:ue,newp}); log(`FAIL ${t.sku}: ${JSON.stringify(ue)}`); }
+ }catch(e){ failed++; ledger.push({sku:t.sku,action:'ERROR',error:String(e)}); log(`ERROR ${t.sku}: ${e}`); }
+ if((i+1)%50===0){ log(`progress ${i+1}/${rows.length} | raised=${raised} skip_atmap=${skipped_atmap} skip_inactive=${skipped_inactive} notfound=${notfound} fail=${failed}`); fs.writeFileSync(LEDGER, JSON.stringify({ts:'2026-08-08',ticket:'TK-10302-belowcost',approved:'Steve email 2026-08-09 work-on-top-3-gated Dust2026',partial:true,counts:{raised,skipped_atmap,skipped_inactive,notfound,failed},results:ledger},null,2)); }
+}
+fs.writeFileSync(LEDGER, JSON.stringify({ts:'2026-08-08',ticket:'TK-10302-belowcost',approved:'Steve email 2026-08-09 work-on-top-3-gated Dust2026',partial:false,counts:{raised,skipped_atmap,skipped_inactive,notfound,failed,total:rows.length},results:ledger},null,2));
+log(`DONE. total=${rows.length} raised=${raised} skip_atmap=${skipped_atmap} skip_inactive=${skipped_inactive} notfound=${notfound} fail=${failed}`);
+log(`Ledger: ${LEDGER}`);
diff --git a/shopify/scripts/reprice-med-956-below-map.mjs b/shopify/scripts/reprice-med-956-below-map.mjs
new file mode 100644
index 0000000..b02528b
--- /dev/null
+++ b/shopify/scripts/reprice-med-956-below-map.mjs
@@ -0,0 +1,59 @@
+#!/usr/bin/env node
+// TK-10014 — raise the 1,141 HIGH_two_source below-MAP DWKK roll variants to MAP on LIVE Shopify.
+// Steve-approved in-session 2026-08-08 ("do the 1,141"). Safeguards: live re-fetch current price,
+// SKIP any already >= MAP, raise rest to MAP, full prior->new reversibility ledger, throttle-aware.
+import fs from 'node:fs';
+import { execSync } from 'node:child_process';
+
+const TT = execSync("grep -m1 '^SHOPIFY_ADMIN_TOKEN=' ~/Projects/secrets-manager/.env | cut -d= -f2-", {shell:'/bin/zsh'}).toString().trim();
+const SHOP = 'designer-laboratory-sandbox.myshopify.com';
+const API = '2024-10';
+const DIR = '/Users/macstudio3/Projects/designerwallcoverings/shopify/scripts/data/kravet-map-pricing';
+const LEDGER = `${DIR}/med-579-reprice-ledger-2026-08-09.json`;
+const LOG = `${DIR}/med-579-reprice-progress.log`;
+
+const rows = fs.readFileSync('/tmp/MEDFILE.csv','utf8').trim().split('\n').map(l=>{const[sku,map]=l.split(',');return{sku,map:parseFloat(map)};});
+const log = (m)=>{ const line=`[${new Date().toISOString()}] ${m}`; fs.appendFileSync(LOG, line+'\n'); console.log(line); };
+
+async function gql(query, variables){
+ for(let attempt=0; attempt<6; attempt++){
+ const r = await fetch(`https://${SHOP}/admin/api/${API}/graphql.json`, {method:'POST',
+ headers:{'X-Shopify-Access-Token':TT,'Content-Type':'application/json'},
+ body: JSON.stringify({query, variables})});
+ const j = await r.json();
+ const th = j?.extensions?.cost?.throttleStatus;
+ if(j.errors && JSON.stringify(j.errors).includes('THROTTLED')){ await sleep(2000); continue; }
+ if(th && th.currentlyAvailable < 600){ await sleep(1500); }
+ return j;
+ }
+ throw new Error('gql retries exhausted');
+}
+const sleep = ms => new Promise(r=>setTimeout(r,ms));
+
+const READ = `query($q:String!){productVariants(first:10,query:$q){edges{node{id sku price product{id status}}}}}`;
+const MUT = `mutation($productId:ID!,$variants:[ProductVariantsBulkInput!]!){productVariantsBulkUpdate(productId:$productId,variants:$variants){productVariants{id price}userErrors{field message}}}`;
+
+const ledger=[]; let raised=0, skipped_atmap=0, skipped_inactive=0, notfound=0, failed=0;
+
+for(let i=0;i<rows.length;i++){
+ const t = rows[i];
+ try{
+ const d = await gql(READ,{q:`sku:${t.sku}`});
+ const edges = d?.data?.productVariants?.edges || [];
+ const roll = edges.find(e=>e.node.sku===t.sku);
+ if(!roll){ notfound++; ledger.push({sku:t.sku,action:'NOT_FOUND'}); continue; }
+ const n=roll.node, cur=parseFloat(n.price), pid=n.product.id, vid=n.id;
+ if(n.product.status!=='ACTIVE'){ skipped_inactive++; ledger.push({sku:t.sku,action:'SKIP_INACTIVE',status:n.product.status,prior:cur}); continue; }
+ if(cur >= t.map-0.01){ skipped_atmap++; ledger.push({sku:t.sku,action:'SKIP_ALREADY_GE_MAP',prior:cur,map:t.map}); continue; }
+ const res = await gql(MUT,{productId:pid, variants:[{id:vid, price:t.map.toFixed(2)}]});
+ const ue = res?.data?.productVariantsBulkUpdate?.userErrors || [];
+ const newp = res?.data?.productVariantsBulkUpdate?.productVariants?.[0]?.price;
+ const ok = ue.length===0 && newp!=null && Math.abs(parseFloat(newp)-t.map)<=0.01;
+ if(ok){ raised++; ledger.push({sku:t.sku,variant_id:vid.split('/').pop(),action:'RAISED',prior:cur,new:parseFloat(newp),map:t.map}); }
+ else { failed++; ledger.push({sku:t.sku,action:'FAIL',prior:cur,map:t.map,userErrors:ue,newp}); log(`FAIL ${t.sku}: ${JSON.stringify(ue)}`); }
+ }catch(e){ failed++; ledger.push({sku:t.sku,action:'ERROR',error:String(e)}); log(`ERROR ${t.sku}: ${e}`); }
+ if((i+1)%50===0){ log(`progress ${i+1}/${rows.length} | raised=${raised} skip_atmap=${skipped_atmap} skip_inactive=${skipped_inactive} notfound=${notfound} fail=${failed}`); fs.writeFileSync(LEDGER, JSON.stringify({ts:'2026-08-08',ticket:'TK-10014',approved:'Steve email 2026-08-09 Dust2026 go continue (MED 579)',partial:true,counts:{raised,skipped_atmap,skipped_inactive,notfound,failed},results:ledger},null,2)); }
+}
+fs.writeFileSync(LEDGER, JSON.stringify({ts:'2026-08-08',ticket:'TK-10014',approved:'Steve email 2026-08-09 Dust2026 go continue (MED 579)',partial:false,counts:{raised,skipped_atmap,skipped_inactive,notfound,failed,total:rows.length},results:ledger},null,2));
+log(`DONE. total=${rows.length} raised=${raised} skip_atmap=${skipped_atmap} skip_inactive=${skipped_inactive} notfound=${notfound} fail=${failed}`);
+log(`Ledger: ${LEDGER}`);
← 12c12d4 auto-data-snapshot: 2026-08-17T04:29:37 (1 data files) — dat
·
back to Designerwallcoverings
·
auto-data-snapshot: 2026-08-17T08:03:25 (2 data files) — scr 1d601f8 →