← back to Dwjs Consolidation 2026 04 23
dwjs_mfr_dryrun.js
130 lines
// Dry-run: for each of the 893 newly-resolved DWJS products, fetch current custom.manufacturer_sku
// from Shopify, diff against the planned new value, and write dwjs_changes.json.
// No mutations.
const fs = require('fs');
const path = require('path');
const TOKEN=(process.env.SHOPIFY_ADMIN_TOKEN || '');
const SHOP='designer-laboratory-sandbox.myshopify.com';
const API=`https://${SHOP}/admin/api/2024-10/graphql.json`;
const RESOLVED_CSV = path.join(__dirname, 'dwjs_mfr_resolved_v2_2026-04-29.csv');
const OUT_CHANGES = path.join(__dirname, 'dwjs_changes.json');
function parseCsv(file) {
const text = fs.readFileSync(file, 'utf8').replace(/\r\n/g,'\n');
const lines = text.split('\n').filter(Boolean);
const headers = lines.shift().split(',');
return lines.map(line => {
// simple split — our CSV has no commas in quoted fields except titles
// handle quoted fields properly
const out = []; let cur=''; let inQ=false;
for (let i=0;i<line.length;i++) {
const c = line[i];
if (c === '"') { inQ = !inQ; continue; }
if (c === ',' && !inQ) { out.push(cur); cur=''; continue; }
cur += c;
}
out.push(cur);
return Object.fromEntries(headers.map((h,i) => [h, out[i] ?? '']));
});
}
async function gql(q, vars, retries=3) {
for (let i=0;i<retries;i++) {
try {
const r = await fetch(API, {
method:'POST',
headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'},
body: JSON.stringify({ query: q, variables: vars }),
});
const j = await r.json();
if (j.errors && j.errors.some(e => /THROTTLED|throttled/.test(JSON.stringify(e)))) {
await new Promise(r=>setTimeout(r, 2000*(i+1))); continue;
}
return j;
} catch (e) {
if (i === retries-1) throw e;
await new Promise(r=>setTimeout(r, 1000*(i+1)));
}
}
}
(async () => {
const rows = parseCsv(RESOLVED_CSV);
console.log(`Loaded ${rows.length} resolved rows from ${path.basename(RESOLVED_CSV)}`);
// Build planned map: product_id -> {sku,title,planned_mfr}
const planned = new Map();
for (const r of rows) {
planned.set(r.product_id, { sku: r.current_sku, title: r.title, planned: r.fmpro_mfr });
}
const ids = [...planned.keys()];
// Batch-fetch current custom.manufacturer_sku
const BATCH = 50;
const current = new Map(); // pid -> string|null
const missing = [];
for (let i=0; i<ids.length; i+=BATCH) {
const batch = ids.slice(i, i+BATCH);
const q = `query($ids:[ID!]!){
nodes(ids: $ids) {
... on Product {
id status vendor
mfr: metafield(namespace:"custom", key:"manufacturer_sku") { value }
}
}
}`;
const j = await gql(q, { ids: batch });
const nodes = j.data?.nodes || [];
if (j.errors) console.error('GQL errors:', JSON.stringify(j.errors).slice(0,400));
for (let k=0;k<batch.length;k++) {
const n = nodes[k];
if (!n) { missing.push(batch[k]); continue; }
current.set(batch[k], { value: n.mfr?.value || '', status: n.status, vendor: n.vendor });
}
process.stdout.write(` fetched ${Math.min(i+BATCH, ids.length)}/${ids.length}\r`);
}
console.log('');
// Build changes
let alreadyCorrect=0, willUpdate=0, missingProduct=0, statusArchived=0, vendorMismatch=0;
const changes = [];
for (const pid of ids) {
const p = planned.get(pid);
const c = current.get(pid);
if (!c) { missingProduct++; continue; }
if (c.status === 'ARCHIVED') statusArchived++; // count, but still update
if (c.vendor !== 'Jeffrey Stevens') vendorMismatch++; // count, but still update (sanity)
if ((c.value || '') === p.planned) { alreadyCorrect++; continue; }
willUpdate++;
changes.push({
id: pid,
title: p.title,
status: c.status,
vendor: c.vendor,
sku: p.sku,
currentMfr: c.value || '',
newMfr: p.planned,
});
}
console.log('\nDry-run results:');
console.log(` total resolved rows: ${ids.length}`);
console.log(` product not found in shop: ${missingProduct}`);
console.log(` already correct: ${alreadyCorrect}`);
console.log(` WILL UPDATE: ${willUpdate}`);
console.log(` status=ARCHIVED (info): ${statusArchived}`);
console.log(` vendor != JS (info): ${vendorMismatch}`);
if (missing.length) console.log(` product_id missing from nodes() response: ${missing.length}`);
if (changes.length) {
console.log(`\nFirst 10 planned changes:`);
changes.slice(0,10).forEach(c => {
console.log(` - ${c.sku.padEnd(22)} cur=${(c.currentMfr||'(none)').padEnd(18)} → new=${c.newMfr.padEnd(12)} '${c.title.slice(0,55)}'`);
});
}
fs.writeFileSync(OUT_CHANGES, JSON.stringify(changes, null, 2));
console.log(`\nWrote ${changes.length} change rows to ${path.basename(OUT_CHANGES)}`);
console.log(`(No Shopify mutations were performed.)`);
})();