← back to Groundworks Activation
Bucket-2 publish script: reuse existing DWKK, re-point Kravet->Groundworks registry, authoritative MAP, 5-field gate
4129d0344a33f6e2846351006c3b3cbfa096021f · 2026-07-22 08:37:05 -0700 · Steve Abrams
Files touched
A data/publish-bucket2-results.jsonA scripts/publish-bucket2.js
Diff
commit 4129d0344a33f6e2846351006c3b3cbfa096021f
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Jul 22 08:37:05 2026 -0700
Bucket-2 publish script: reuse existing DWKK, re-point Kravet->Groundworks registry, authoritative MAP, 5-field gate
---
data/publish-bucket2-results.json | 262 ++++++++++++++++++++++++++++++++++++++
scripts/publish-bucket2.js | 224 ++++++++++++++++++++++++++++++++
2 files changed, 486 insertions(+)
diff --git a/data/publish-bucket2-results.json b/data/publish-bucket2-results.json
new file mode 100644
index 0000000..af590fd
--- /dev/null
+++ b/data/publish-bucket2-results.json
@@ -0,0 +1,262 @@
+[
+ {
+ "mfr_sku": "GWP-3417.816.0",
+ "dwSku": "DWKK-106908",
+ "title": "Channels Paper Ebony/Ivory | Groundworks",
+ "price": "747.00",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Each",
+ "tags": 10,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3302.516 NEW.0",
+ "dwSku": "DWKK-106890",
+ "title": "Channels Paper Periwinkle Oat | Groundworks",
+ "price": "747.00",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Each",
+ "tags": 10,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3417.516.0",
+ "dwSku": "DWKK-106907",
+ "title": "Channels Paper Periwinkle/Oat | Groundworks",
+ "price": "747.00",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Each",
+ "tags": 10,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3417.911.0",
+ "dwSku": "DWKK-106909",
+ "title": "Channels Paper Plum/Oatmeal | Groundworks",
+ "price": "747.00",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Each",
+ "tags": 10,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3304.816.0",
+ "dwSku": "DWKK-106897",
+ "title": "Crescent Paper Ebony/Cream | Groundworks",
+ "price": "897.00",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Per Roll - 27In Wide",
+ "tags": 10,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3304.48.0",
+ "dwSku": "DWKK-106895",
+ "title": "Crescent Paper Ebony/Gold | Groundworks",
+ "price": "897.00",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Per Roll - 27In Wide",
+ "tags": 10,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3304.416.0",
+ "dwSku": "DWKK-106894",
+ "title": "Crescent Paper Gold/Ivory | Groundworks",
+ "price": "897.00",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Per Roll - 27In Wide",
+ "tags": 10,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3304.316.0",
+ "dwSku": "DWKK-106893",
+ "title": "Crescent Paper Lake/Cream | Groundworks",
+ "price": "897.00",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Per Roll - 27In Wide",
+ "tags": 10,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3304.611.0",
+ "dwSku": "DWKK-106896",
+ "title": "Crescent Paper Taupe/Putty | Groundworks",
+ "price": "897.00",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Per Roll - 27In Wide",
+ "tags": 10,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3413.101.0",
+ "dwSku": "DWKK-106899",
+ "title": "Hutch Cream | Groundworks",
+ "price": "742.50",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Per Roll - 27In Wide",
+ "tags": 8,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3413.10.0",
+ "dwSku": "DWKK-106898",
+ "title": "Hutch Lavender | Groundworks",
+ "price": "742.50",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Per Roll - 27In Wide",
+ "tags": 8,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3413.17.0",
+ "dwSku": "DWKK-106902",
+ "title": "Hutch Pink | Groundworks",
+ "price": "742.50",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Per Roll - 27In Wide",
+ "tags": 9,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3735.1311.0",
+ "dwSku": "DWKK-106976",
+ "title": "Orchard Paper Aqua/Stone | Groundworks",
+ "price": "549.68",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Per Roll - 27.5In Wide",
+ "tags": 9,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3735.55.0",
+ "dwSku": "DWKK-106978",
+ "title": "Orchard Paper Blue/Denim | Groundworks",
+ "price": "549.68",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Per Roll - 27.5In Wide",
+ "tags": 9,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3735.421.0",
+ "dwSku": "DWKK-106977",
+ "title": "Orchard Paper Pebble/Coin | Groundworks",
+ "price": "549.68",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Per Roll - 27.5In Wide",
+ "tags": 9,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3727.635.0",
+ "dwSku": "DWKK-106963",
+ "title": "Roulade Paper Aqua/Dune | Groundworks",
+ "price": "707.18",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Per Roll - 28.35In Wide",
+ "tags": 9,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3727.640.0",
+ "dwSku": "DWKK-106964",
+ "title": "Roulade Paper Citron/Stone | Groundworks",
+ "price": "707.18",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Per Roll - 28.35In Wide",
+ "tags": 9,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3727.630.0",
+ "dwSku": "DWKK-106962",
+ "title": "Roulade Paper Juniper/Buff | Groundworks",
+ "price": "707.18",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Per Roll - 28.35In Wide",
+ "tags": 9,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3727.355.0",
+ "dwSku": "DWKK-106961",
+ "title": "Roulade Paper Navy/Teal | Groundworks",
+ "price": "707.18",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Per Roll - 28.35In Wide",
+ "tags": 9,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3727.73.0",
+ "dwSku": "DWKK-106965",
+ "title": "Roulade Paper Rose/Leaf | Groundworks",
+ "price": "707.18",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Per Roll - 28.35In Wide",
+ "tags": 9,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3726.503.0",
+ "dwSku": "DWKK-106958",
+ "title": "Twister Paper Blue/Leaf | Groundworks",
+ "price": "689.85",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Per Roll - 28.35In Wide",
+ "tags": 9,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3726.355.0",
+ "dwSku": "DWKK-106956",
+ "title": "Twister Paper Denim/Aqua | Groundworks",
+ "price": "689.85",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Per Roll - 28.35In Wide",
+ "tags": 9,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3726.352.0",
+ "dwSku": "DWKK-106955",
+ "title": "Twister Paper Kiwi/Slate | Groundworks",
+ "price": "689.85",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Per Roll - 28.35In Wide",
+ "tags": 9,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3726.357.0",
+ "dwSku": "DWKK-106957",
+ "title": "Twister Paper Petal/Teal | Groundworks",
+ "price": "689.85",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Per Roll - 28.35In Wide",
+ "tags": 9,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3726.512.0",
+ "dwSku": "DWKK-106959",
+ "title": "Twister Paper Sky/Tang | Groundworks",
+ "price": "689.85",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Per Roll - 28.35In Wide",
+ "tags": 9,
+ "before_vendor": "Kravet(registry)"
+ },
+ {
+ "mfr_sku": "GWP-3726.86.0",
+ "dwSku": "DWKK-106960",
+ "title": "Twister Paper Tobacco/Noir | Groundworks",
+ "price": "689.85",
+ "map_src": "authoritative_new_map",
+ "unit": "Sold Per Roll - 28.35In Wide",
+ "tags": 9,
+ "before_vendor": "Kravet(registry)"
+ }
+]
\ No newline at end of file
diff --git a/scripts/publish-bucket2.js b/scripts/publish-bucket2.js
new file mode 100644
index 0000000..a167649
--- /dev/null
+++ b/scripts/publish-bucket2.js
@@ -0,0 +1,224 @@
+#!/usr/bin/env node
+/**
+ * Bucket 2 — 26 registry-collision (NOT live on Shopify). Each holds a DWKK registry row
+ * under vendor "Kravet" but has no live product. Re-point that dw_sku to vendor Groundworks
+ * and PUBLISH net-new with the full 5-field gate + MAP (authoritative). Reuses the EXISTING
+ * DWKK number (no orphan, no new mint) since it is confirmed unused live.
+ *
+ * NEVER-DUPLICATE: re-checks shopify_products by mfr_sku right before create; if any turns
+ * out to actually be live, it is skipped (treat as bucket 1, handled by rebrand.js).
+ *
+ * PG-first (dw_sku_registry re-point + groundworks_catalog writeback) then Shopify authoritative.
+ *
+ * Usage: node publish-bucket2.js [--dry] [--limit N]
+ */
+const { Client } = require('pg');
+const fs = require('fs');
+
+const DRY = process.argv.includes('--dry');
+const limIdx = process.argv.indexOf('--limit');
+const LIMIT = limIdx >= 0 ? parseInt(process.argv[limIdx + 1], 10) : 0;
+
+const DOMAIN = 'designer-laboratory-sandbox.myshopify.com';
+const API = '2024-10';
+const TOKEN = (fs.readFileSync(process.env.HOME + '/Projects/secrets-manager/.env', 'utf8')
+ .split('\n').find(l => l.startsWith('SHOPIFY_ADMIN_TOKEN=')) || '').split('=').slice(1).join('=').trim();
+if (!TOKEN) { console.error('no SHOPIFY_ADMIN_TOKEN'); process.exit(2); }
+
+const GQL = `https://${DOMAIN}/admin/api/${API}/graphql.json`;
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+
+async function shopify(query, variables) {
+ for (let attempt = 0; attempt < 6; attempt++) {
+ const res = await fetch(GQL, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json', 'X-Shopify-Access-Token': TOKEN },
+ body: JSON.stringify({ query, variables }),
+ });
+ if (res.status === 429 || res.status >= 500) { await sleep(2500 * (attempt + 1)); continue; }
+ const j = await res.json();
+ if (j.errors) throw new Error('GQL: ' + JSON.stringify(j.errors).slice(0, 400));
+ const cost = j.extensions?.cost?.throttleStatus;
+ if (cost && cost.currentlyAvailable < 250) await sleep(1500);
+ return j.data;
+ }
+ throw new Error('shopify retries exhausted');
+}
+
+function titleCase(s) {
+ return (s || '').toLowerCase().replace(/\b\w/g, c => c.toUpperCase())
+ .replace(/\bWc\b/gi, '').replace(/\bWp\b/gi, '').replace(/\s{2,}/g, ' ').trim();
+}
+function parsePattern(name) {
+ const idx = name.lastIndexOf(' - ');
+ if (idx < 0) return { pattern: titleCase(name), color: '' };
+ return { pattern: titleCase(name.slice(0, idx)), color: titleCase(name.slice(idx + 3)) };
+}
+function buildTitle(row) {
+ const { pattern, color } = parsePattern(row.name);
+ const realColor = color || row.color_name || '';
+ const brand = 'Groundworks';
+ if (pattern && realColor) return `${pattern} ${realColor} | ${brand}`;
+ if (pattern) return `${pattern} | ${brand}`;
+ return `${row.mfr_sku} | ${brand}`;
+}
+function unitLabel(row) {
+ const w = row.width ? `${row.width}` : '';
+ if (row.uom === 'Yard') return `Sold Per Yard${w ? ` - ${w}In Wide` : ''}`;
+ if (row.uom === 'Each') return `Sold Each`;
+ return `Sold Per Roll${w ? ` - ${w}In Wide` : ''}`;
+}
+function buildDescription(row) {
+ const { pattern, color } = parsePattern(row.name);
+ const parts = [];
+ parts.push(`<p>${pattern}${color ? ` in ${color}` : ''} is a Groundworks wallcovering`);
+ if (row.designer) parts[0] += ` designed by ${row.designer}`;
+ if (row.collection) parts[0] += `, from the ${titleCase(row.collection)} collection`;
+ parts[0] += '.';
+ const specs = [];
+ if (row.composition) specs.push(`Composition: ${row.composition}`);
+ if (row.made_in) specs.push(`Made in ${row.made_in}`);
+ if (row.repeat_v || row.repeat_h) {
+ const rp = [row.repeat_v && `${row.repeat_v}" vertical`, row.repeat_h && `${row.repeat_h}" horizontal`].filter(Boolean).join(', ');
+ specs.push(`Repeat: ${rp}`);
+ }
+ if (row.width) specs.push(`Width: ${row.width}"`);
+ let html = parts[0];
+ if (specs.length) html += ` ${specs.join('. ')}.`;
+ html += '</p>';
+ return html;
+}
+function buildTags(row) {
+ const { pattern, color } = parsePattern(row.name);
+ const tags = new Set(['Wallcovering', 'Groundworks']);
+ if (pattern) tags.add(pattern);
+ if (color) tags.add(color);
+ if (row.color_name) tags.add(row.color_name);
+ if (row.color_bucket) tags.add(row.color_bucket);
+ if (row.collection) tags.add(titleCase(row.collection));
+ if (row.designer) tags.add(row.designer);
+ tags.add(row.mfr_sku);
+ if (row.width) tags.add(`${row.width}In`);
+ return [...tags].filter(Boolean);
+}
+
+async function main() {
+ const pg = new Client({ host: '/tmp', database: 'dw_unified' });
+ await pg.connect();
+
+ let sql = `SELECT a.*, r.dw_sku AS existing_dwkk, k.new_map AS auth_map
+ FROM groundworks_catalog a
+ JOIN dw_sku_registry r ON r.mfr_sku=a.mfr_sku
+ LEFT JOIN kravet_authoritative_pricing k ON k.mfr_sku=a.mfr_sku
+ WHERE a.status='Active' AND a.shopify_product_id IS NULL
+ AND a.mfr_sku NOT IN ('GWP-3746.194.0','GWP-3746.712.0','GWP-3746.513.0')
+ AND NOT EXISTS(SELECT 1 FROM shopify_products sp WHERE sp.mfr_sku=a.mfr_sku)
+ ORDER BY a.name`;
+ if (LIMIT) sql += ` LIMIT ${LIMIT}`;
+ const rows = (await pg.query(sql)).rows;
+ console.log(`Bucket-2 publish set: ${rows.length} SKUs DRY=${DRY}`);
+
+ const results = [];
+ for (const row of rows) {
+ // NEVER-DUPLICATE re-check: if a live product appeared, skip (it's bucket 1)
+ const dupChk = await pg.query(`SELECT 1 FROM shopify_products WHERE mfr_sku=$1 LIMIT 1`, [row.mfr_sku]);
+ if (dupChk.rowCount) { results.push({ mfr_sku: row.mfr_sku, skip: 'now-live -> bucket1' }); continue; }
+
+ const dwSku = row.existing_dwkk; // reuse existing registry DWKK
+ const title = buildTitle(row);
+ // MAP source priority: authoritative new_map, else staging map_price
+ const MAP = (row.auth_map != null && Number(row.auth_map) > 0) ? Number(row.auth_map) : Number(row.map_price);
+ const mapSrc = (row.auth_map != null && Number(row.auth_map) > 0) ? 'authoritative_new_map' : 'staging_map';
+ const price = Number(MAP).toFixed(2);
+ if (!(Number(price) > 0)) { results.push({ mfr_sku: row.mfr_sku, dwSku, skip: 'no-price' }); continue; }
+ if (!row.image_url) { results.push({ mfr_sku: row.mfr_sku, dwSku, skip: 'no-image' }); continue; }
+
+ const input = {
+ title, vendor: 'Groundworks', productType: 'Wallcovering',
+ descriptionHtml: buildDescription(row), tags: buildTags(row), handle: dwSku.toLowerCase(),
+ };
+
+ if (DRY) {
+ results.push({ mfr_sku: row.mfr_sku, dwSku, title, price, map_src: mapSrc, unit: unitLabel(row), tags: input.tags.length, before_vendor: 'Kravet(registry)' });
+ continue;
+ }
+
+ // 1) create DRAFT first
+ const cr = await shopify(`mutation($input:ProductInput!){productCreate(input:$input){product{id handle} userErrors{field message}}}`,
+ { input: { ...input, status: 'DRAFT' } });
+ const perr = cr.productCreate.userErrors;
+ if (perr && perr.length) { results.push({ mfr_sku: row.mfr_sku, dwSku, err: JSON.stringify(perr) }); continue; }
+ const pid = cr.productCreate.product.id;
+ const numericPid = pid.split('/').pop();
+
+ // 2) image (resize local, upload attachment)
+ let imgOk = false;
+ try {
+ const os = require('os'); const { execSync } = require('child_process');
+ const tmp = `${os.tmpdir()}/gw_${dwSku}.jpg`;
+ execSync(`curl -sL -A "Mozilla/5.0" -e "https://www.kravet.com/" "${row.image_url}" -o "${tmp}"`, { stdio: 'ignore' });
+ execSync(`sips -Z 2000 "${tmp}" --out "${tmp}" >/dev/null 2>&1`);
+ const b64 = fs.readFileSync(tmp).toString('base64');
+ const ir = await fetch(`https://${DOMAIN}/admin/api/${API}/products/${numericPid}/images.json`,
+ { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Shopify-Access-Token': TOKEN },
+ body: JSON.stringify({ image: { attachment: b64, filename: `${row.mfr_sku}.jpg` } }) });
+ const ij = await ir.json(); if (ij.image && ij.image.id) imgOk = true;
+ fs.unlinkSync(tmp);
+ } catch (e) { /* caught by verify */ }
+
+ // 3) variants: rename option -> sellable label, set price+sku, add sample
+ const sellLabel = unitLabel(row);
+ const prodQ = await shopify(`query($id:ID!){product(id:$id){variants(first:5){nodes{id}}options{id name optionValues{id name}}}}`, { id: pid });
+ const optId = prodQ.product.options[0].id;
+ const optValId = prodQ.product.options[0].optionValues[0].id;
+ const defVarId = prodQ.product.variants.nodes[0].id;
+
+ const rn = await shopify(`mutation($pid:ID!,$opt:OptionUpdateInput!,$vals:[OptionValueUpdateInput!]!){productOptionUpdate(productId:$pid,option:$opt,optionValuesToUpdate:$vals){userErrors{field message}}}`,
+ { pid, opt: { id: optId, name: 'Size' }, vals: [{ id: optValId, name: sellLabel }] });
+ if (rn.productOptionUpdate.userErrors.length) { results.push({ mfr_sku: row.mfr_sku, dwSku, pid: numericPid, err: 'optRename:' + JSON.stringify(rn.productOptionUpdate.userErrors) }); continue; }
+
+ const su = await shopify(`mutation($pid:ID!,$variants:[ProductVariantsBulkInput!]!){productVariantsBulkUpdate(productId:$pid,variants:$variants){userErrors{field message}}}`, {
+ pid, variants: [{ id: defVarId, price, inventoryPolicy: 'CONTINUE', inventoryItem: { sku: dwSku, tracked: false } }]
+ });
+ if (su.productVariantsBulkUpdate.userErrors.length) { results.push({ mfr_sku: row.mfr_sku, dwSku, pid: numericPid, err: 'sellUpd:' + JSON.stringify(su.productVariantsBulkUpdate.userErrors) }); continue; }
+
+ const sc = await shopify(`mutation($pid:ID!,$variants:[ProductVariantsBulkInput!]!){productVariantsBulkCreate(productId:$pid,variants:$variants){userErrors{field message}}}`, {
+ pid, variants: [{ price: '4.25', inventoryPolicy: 'CONTINUE', inventoryItem: { sku: `${dwSku}-Sample`, tracked: false }, optionValues: [{ optionName: 'Size', name: 'Sample' }] }]
+ });
+ if (sc.productVariantsBulkCreate.userErrors.length) { results.push({ mfr_sku: row.mfr_sku, dwSku, pid: numericPid, err: 'sampleCreate:' + JSON.stringify(sc.productVariantsBulkCreate.userErrors) }); continue; }
+
+ // 3b) 5-field gate: only ACTIVE if image present; else leave DRAFT + Needs-Image
+ if (imgOk) {
+ await shopify(`mutation($input:ProductInput!){productUpdate(input:$input){userErrors{field message}}}`,
+ { input: { id: pid, status: 'ACTIVE' } });
+ } else {
+ await shopify(`mutation($input:ProductInput!){productUpdate(input:$input){userErrors{field message}}}`,
+ { input: { id: pid, status: 'DRAFT', tags: [...input.tags, 'Needs-Image'] } });
+ }
+
+ // 4) PG-first writeback: re-point registry vendor Kravet -> Groundworks, set pid
+ await pg.query(`UPDATE dw_sku_registry SET vendor_name='Groundworks', vendor_prefix='DWKK',
+ shopify_product_id=$2, shopify_handle=$3, status='active' WHERE dw_sku=$1`,
+ [dwSku, numericPid, cr.productCreate.product.handle]);
+ await pg.query(`UPDATE groundworks_catalog SET dw_sku=$1, shopify_product_id=$2 WHERE mfr_sku=$3`,
+ [dwSku, numericPid, row.mfr_sku]);
+ await pg.query(`INSERT INTO kravet_master_price(mfr_sku,whls_cost,map_price,brand,source)
+ VALUES($1,$2,$3,'Groundworks','groundworks-activation-b2')
+ ON CONFLICT (mfr_sku) DO UPDATE SET map_price=EXCLUDED.map_price,brand='Groundworks'`,
+ [row.mfr_sku, row.wholesale, MAP]);
+
+ results.push({ mfr_sku: row.mfr_sku, dwSku, pid: numericPid, title, price, map_src: mapSrc, active: imgOk, before_vendor: 'Kravet(registry)' });
+ console.log(` ✓ ${dwSku} ${title} $${price} ${imgOk ? 'ACTIVE' : 'DRAFT(Needs-Image)'}`);
+ await sleep(700);
+ }
+
+ await pg.end();
+ fs.writeFileSync(__dirname + '/../data/publish-bucket2-results.json', JSON.stringify(results, null, 2));
+ const ok = results.filter(r => r.pid || (DRY && r.dwSku && !r.skip && !r.err));
+ const err = results.filter(r => r.err);
+ const skip = results.filter(r => r.skip);
+ console.log(`\nDONE: created=${ok.length} errors=${err.length} skipped=${skip.length}`);
+ if (err.length) console.log('ERRORS:', JSON.stringify(err, null, 2));
+ if (skip.length) console.log('SKIPS:', JSON.stringify(skip, null, 2));
+}
+main().catch(e => { console.error(e); process.exit(1); });
← 7a8ff86 Bucket-1 executed: 114 rebranded-in-place to Groundworks (11
·
back to Groundworks Activation
·
Bucket-3 butterfly publish script: 3 Flutter Sisal net-new ( 52e3d5d →