← back to Designer Wallcoverings
fix-dup-skus-0717: reassign 29 colliding 7/17 PR SKUs to unique stride-10 numbers (Steve go)
a67f4620fb773510f06c7e5c272910cd9a675599 · 2026-07-21 14:00:39 -0700 · Steve
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Files touched
A scripts/wallquest-refresh/data/dup-sku-remap-0717.jsonA scripts/wallquest-refresh/fix-dup-skus-0717.cjs
Diff
commit a67f4620fb773510f06c7e5c272910cd9a675599
Author: Steve <steve@designerwallcoverings.com>
Date: Tue Jul 21 14:00:39 2026 -0700
fix-dup-skus-0717: reassign 29 colliding 7/17 PR SKUs to unique stride-10 numbers (Steve go)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---
.../wallquest-refresh/data/dup-sku-remap-0717.json | 176 +++++++++++++++++++++
scripts/wallquest-refresh/fix-dup-skus-0717.cjs | 92 +++++++++++
2 files changed, 268 insertions(+)
diff --git a/scripts/wallquest-refresh/data/dup-sku-remap-0717.json b/scripts/wallquest-refresh/data/dup-sku-remap-0717.json
new file mode 100644
index 00000000..f6a42b5c
--- /dev/null
+++ b/scripts/wallquest-refresh/data/dup-sku-remap-0717.json
@@ -0,0 +1,176 @@
+[
+ {
+ "pid": "7887542517811",
+ "handle": "anhe-abaca-greige-pr-46125",
+ "old": "ABA-46125",
+ "new": "ABA-46165"
+ },
+ {
+ "pid": "7887542550579",
+ "handle": "anhe-abaca-peppercorn-pr-46135",
+ "old": "ABA-46135",
+ "new": "ABA-46175"
+ },
+ {
+ "pid": "7887542583347",
+ "handle": "meilin-grasscloth-lagoon-pr-800986",
+ "old": "GRS-800986",
+ "new": "GRS-801436"
+ },
+ {
+ "pid": "7887542616115",
+ "handle": "meilin-grasscloth-lake-forest-green-pr-800996",
+ "old": "GRS-800996",
+ "new": "GRS-801446"
+ },
+ {
+ "pid": "7887542648883",
+ "handle": "meilin-grasscloth-moss-pr-801006",
+ "old": "GRS-801006",
+ "new": "GRS-801456"
+ },
+ {
+ "pid": "7887542681651",
+ "handle": "meilin-grasscloth-pebblestone-pr-801016",
+ "old": "GRS-801016",
+ "new": "GRS-801466"
+ },
+ {
+ "pid": "7887550775347",
+ "handle": "bergamo-azure-pr-801136",
+ "old": "GRS-801136",
+ "new": "GRS-801476"
+ },
+ {
+ "pid": "7887563849779",
+ "handle": "huanzhu-leaf-stripe-chocolate-pr-801136",
+ "old": "GRS-801136",
+ "new": "GRS-801486"
+ },
+ {
+ "pid": "7887550808115",
+ "handle": "bergamo-tan-pr-801146",
+ "old": "GRS-801146",
+ "new": "GRS-801496"
+ },
+ {
+ "pid": "7887563882547",
+ "handle": "huanzhu-leaf-stripe-navy-pr-801146",
+ "old": "GRS-801146",
+ "new": "GRS-801506"
+ },
+ {
+ "pid": "7887550840883",
+ "handle": "chess-cappuccino-pr-801156",
+ "old": "GRS-801156",
+ "new": "GRS-801516"
+ },
+ {
+ "pid": "7887563915315",
+ "handle": "huanzhu-leaf-stripe-oyster-gold-pr-801156",
+ "old": "GRS-801156",
+ "new": "GRS-801526"
+ },
+ {
+ "pid": "7887550873651",
+ "handle": "chess-oat-pr-801166",
+ "old": "GRS-801166",
+ "new": "GRS-801536"
+ },
+ {
+ "pid": "7887563948083",
+ "handle": "huanzhu-leaf-stripe-sage-silver-pr-801166",
+ "old": "GRS-801166",
+ "new": "GRS-801546"
+ },
+ {
+ "pid": "7887550906419",
+ "handle": "manila-gold-glam-pr-801176",
+ "old": "GRS-801176",
+ "new": "GRS-801556"
+ },
+ {
+ "pid": "7887550939187",
+ "handle": "manila-oat-pr-801186",
+ "old": "GRS-801186",
+ "new": "GRS-801566"
+ },
+ {
+ "pid": "7887550971955",
+ "handle": "palais-gold-glam-pr-801196",
+ "old": "GRS-801196",
+ "new": "GRS-801576"
+ },
+ {
+ "pid": "7887551037491",
+ "handle": "palais-juniper-pr-801206",
+ "old": "GRS-801206",
+ "new": "GRS-801586"
+ },
+ {
+ "pid": "7887551070259",
+ "handle": "palais-new-jade-pr-801216",
+ "old": "GRS-801216",
+ "new": "GRS-801596"
+ },
+ {
+ "pid": "7887551135795",
+ "handle": "palais-peacock-pr-801226",
+ "old": "GRS-801226",
+ "new": "GRS-801606"
+ },
+ {
+ "pid": "7887551168563",
+ "handle": "palais-stone-pr-801236",
+ "old": "GRS-801236",
+ "new": "GRS-801616"
+ },
+ {
+ "pid": "7887551201331",
+ "handle": "shantung-aged-stone-pr-801246",
+ "old": "GRS-801246",
+ "new": "GRS-801626"
+ },
+ {
+ "pid": "7887551234099",
+ "handle": "shantung-celadon-pr-801256",
+ "old": "GRS-801256",
+ "new": "GRS-801636"
+ },
+ {
+ "pid": "7887551266867",
+ "handle": "shantung-deep-aqua-pr-801266",
+ "old": "GRS-801266",
+ "new": "GRS-801646"
+ },
+ {
+ "pid": "7887551299635",
+ "handle": "shantung-new-jade-pr-801276",
+ "old": "GRS-801276",
+ "new": "GRS-801656"
+ },
+ {
+ "pid": "7887551365171",
+ "handle": "sisal-pr-100227",
+ "old": "SIS-100226",
+ "new": "SIS-100746"
+ },
+ {
+ "pid": "7887551397939",
+ "handle": "sisal-pr-100237",
+ "old": "SIS-100236",
+ "new": "SIS-100756"
+ },
+ {
+ "pid": "7887551430707",
+ "handle": "sisal-pr-100247",
+ "old": "SIS-100246",
+ "new": "SIS-100766"
+ },
+ {
+ "pid": "7887551463475",
+ "handle": "sisal-pr-100257",
+ "old": "SIS-100256",
+ "new": "SIS-100776"
+ }
+]
\ No newline at end of file
diff --git a/scripts/wallquest-refresh/fix-dup-skus-0717.cjs b/scripts/wallquest-refresh/fix-dup-skus-0717.cjs
new file mode 100644
index 00000000..998c8906
--- /dev/null
+++ b/scripts/wallquest-refresh/fix-dup-skus-0717.cjs
@@ -0,0 +1,92 @@
+// Re-assign unique SKUs to the 7/17 PR ACTIVE duplicate-SKU collision groups.
+// Steve go 2026-07-21 ("use unique skus"). Rule: lowest product id keeps the SKU;
+// every other product in the group gets the next free stride-10 number in its
+// prefix series (uniqueness checked against ALL variant_skus in the mirror,
+// including the archived Lillian August ranges). Sample variant follows.
+// Usage: node fix-dup-skus-0717.cjs [--dry]
+const { execSync } = require('child_process');
+const https = require('https');
+const fs = require('fs');
+
+const TOK = process.env.SHOPIFY_ADMIN_TOKEN;
+const DOMAIN = 'designer-laboratory-sandbox.myshopify.com';
+if (!TOK) { console.error('SHOPIFY_ADMIN_TOKEN missing'); process.exit(1); }
+const DRY = process.argv.includes('--dry');
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+
+function rest(method, path, body) {
+ return new Promise((res, rej) => {
+ const data = body ? JSON.stringify(body) : null;
+ const r = https.request({
+ hostname: DOMAIN, path: `/admin/api/2024-10/${path}`, method,
+ headers: { 'X-Shopify-Access-Token': TOK, 'Content-Type': 'application/json', ...(data ? { 'Content-Length': Buffer.byteLength(data) } : {}) }
+ }, rs => { let d = ''; rs.on('data', c => d += c); rs.on('end', () => { try { res({ status: rs.statusCode, body: d ? JSON.parse(d) : {} }); } catch (e) { res({ status: rs.statusCode, body: {} }); } }); });
+ r.on('error', rej); if (data) r.write(data); r.end();
+ });
+}
+function psqlRows(sql) {
+ const out = execSync(`psql "host=/tmp dbname=dw_unified" -tA -F'\x1f' -c ${JSON.stringify(sql.replace(/\s+/g, ' ').trim())}`, { maxBuffer: 64 * 1024 * 1024 }).toString().trim();
+ return out ? out.split('\n').map(l => l.split('\x1f')) : [];
+}
+
+(async () => {
+ // all existing SKUs (global uniqueness pool)
+ const allSkus = new Set(psqlRows(`SELECT DISTINCT variant_sku FROM shopify_products WHERE variant_sku ~ '^(GRS|SIS|RAF|ABA|PWV|JUT|CORK|MIC)-'`).map(r => r[0].toUpperCase()));
+
+ // collision groups among 7/17 ACTIVE PR
+ const rows = psqlRows(`
+ SELECT variant_sku, replace(shopify_id,'gid://shopify/Product/',''), handle
+ FROM shopify_products
+ WHERE vendor='Phillipe Romano' AND created_at_shopify::date='2026-07-17' AND status='ACTIVE'
+ AND variant_sku NOT LIKE '%-Sample'
+ GROUP BY 1,2,3 ORDER BY 1, 2::bigint`);
+ const groups = {};
+ for (const [sku, pid, handle] of rows) (groups[sku] = groups[sku] || []).push({ pid, handle });
+
+ // per-prefix max number for allocation
+ const maxByPrefix = {};
+ for (const s of allSkus) {
+ const m = s.match(/^([A-Z]+)-(\d+)$/);
+ if (m) maxByPrefix[m[1]] = Math.max(maxByPrefix[m[1]] || 0, parseInt(m[2], 10));
+ }
+ const alloc = prefix => {
+ let n = (maxByPrefix[prefix] || 0) + 10;
+ while (allSkus.has(`${prefix}-${n}`)) n += 10;
+ maxByPrefix[prefix] = n;
+ const sku = `${prefix}-${n}`;
+ allSkus.add(sku);
+ return sku;
+ };
+
+ const mapping = [];
+ let changed = 0, failed = 0;
+ for (const [sku, prods] of Object.entries(groups)) {
+ if (prods.length < 2) continue;
+ const prefix = sku.split('-')[0];
+ // keeper = lowest product id (first created)
+ for (const p of prods.slice(1)) {
+ const newSku = alloc(prefix);
+ mapping.push({ pid: p.pid, handle: p.handle, old: sku, new: newSku });
+ if (DRY) { console.log(`DRY ${p.handle}: ${sku} -> ${newSku}`); continue; }
+ // fetch variants, update roll + sample
+ const vr = await rest('GET', `products/${p.pid}/variants.json`);
+ let ok = true;
+ for (const v of vr.body.variants || []) {
+ const cur = (v.sku || '').toUpperCase();
+ if (cur !== sku.toUpperCase() && cur !== `${sku.toUpperCase()}-SAMPLE`) continue;
+ const target = cur.endsWith('-SAMPLE') ? `${newSku}-Sample` : newSku;
+ const ur = await rest('PUT', `variants/${v.id}.json`, { variant: { id: v.id, sku: target } });
+ if (ur.status !== 200) { console.error(` ❌ ${p.handle} variant ${v.id}: HTTP ${ur.status}`); ok = false; }
+ await sleep(350);
+ }
+ if (ok) {
+ execSync(`psql "host=/tmp dbname=dw_unified" -q -c "UPDATE shopify_products SET variant_sku=replace(variant_sku,'${sku}','${newSku}'), dw_sku=CASE WHEN dw_sku='${sku}' THEN '${newSku}' ELSE dw_sku END WHERE shopify_id='gid://shopify/Product/${p.pid}'"`);
+ changed++;
+ console.log(` ✅ ${p.handle}: ${sku} -> ${newSku}`);
+ } else failed++;
+ await sleep(200);
+ }
+ }
+ fs.writeFileSync(__dirname + '/data/dup-sku-remap-0717.json', JSON.stringify(mapping, null, 1));
+ console.log(`\n${DRY ? 'DRY RUN — ' : ''}groups: ${Object.values(groups).filter(g => g.length > 1).length}, reassigned: ${DRY ? mapping.length + ' planned' : changed}, failed: ${failed}`);
+})().catch(e => { console.error('FATAL', e); process.exit(1); });
← 45941f84 backfill: --drafts flag; 40 PR 7/17 drafts backfilled (Steve
·
back to Designer Wallcoverings
·
chore: v1.2.4 (session close — dup-sku fix + drafts backfill 7ac09087 →