← back to Dwjs Consolidation 2026 04 23
hollywood_mfr_dryrun.js
110 lines
// Dry-run: find Hollywood Wallcoverings products, look up real mfr SKU in fmpro, report what would change.
const fs = require('fs');
const TOKEN=(process.env.SHOPIFY_ADMIN_TOKEN || '');
const SHOP='designer-laboratory-sandbox.myshopify.com';
const API=`https://${SHOP}/admin/api/2024-10/graphql.json`;
// Build fmpro lookup: concatKey → { dashed, mfr, vendor_code, vendor_name }
function loadFmpro() {
const raw = fs.readFileSync('/tmp/fmpro.csv','utf8');
const recs = raw.replace(/\r\n/g,'\n').replace(/\r/g,'\n').split('\n');
const map = new Map();
for (const line of recs) {
if (!line.trim()) continue;
// CSV with quoted fields
const m = line.match(/^"([^"]*)","([^"]*)","([^"]*)","([^"]*)","?([^"]*)"?$/);
if (!m) continue;
const [,concat,dashed,mfr,vcode,vname] = m;
map.set(concat.toUpperCase(), { dashed, mfr, vcode, vname });
// Also index dashed (e.g. XWA-73265) and dashless variants
map.set(dashed.toUpperCase(), { dashed, mfr, vcode, vname });
}
return map;
}
async function gql(q, vars) {
const r = await fetch(API, {
method:'POST',
headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'},
body: JSON.stringify({ query: q, variables: vars }),
});
return r.json();
}
async function fetchAllHollywood() {
const all = [];
let cursor = null;
while (true) {
const q = `query($c:String){
products(first:100, after:$c, query:"vendor:'Hollywood Wallcoverings'") {
pageInfo { hasNextPage endCursor }
nodes {
id title status
variants(first:5) { nodes { sku } }
mfr: metafield(namespace:"custom", key:"manufacturer_sku") { value }
}
}
}`;
const j = await gql(q, { c: cursor });
const data = j.data?.products;
if (!data) { console.error('GQL err', JSON.stringify(j).slice(0,500)); break; }
all.push(...data.nodes);
if (!data.pageInfo.hasNextPage) break;
cursor = data.pageInfo.endCursor;
process.stdout.write(` fetched ${all.length}...\r`);
}
return all;
}
(async () => {
console.log('Loading fmpro...');
const fmpro = loadFmpro();
console.log(` ${fmpro.size} fmpro keys\n`);
console.log('Fetching Hollywood Wallcoverings products from Shopify...');
const products = await fetchAllHollywood();
console.log(` ${products.length} products total\n`);
let resolved=0, alreadyCorrect=0, willUpdate=0, noMatch=0, missingSku=0;
const changes = [];
const unmatched = [];
for (const p of products) {
// Take the bolt SKU first, fall back to sample-stripped
const skus = p.variants.nodes.map(v=>v.sku).filter(Boolean);
const boltSku = skus.find(s => !/-sample$/i.test(s)) || skus[0];
if (!boltSku) { missingSku++; continue; }
// Strip -sample suffix and try multiple keys
const base = boltSku.replace(/-sample$/i,'').toUpperCase();
const baseNoDash = base.replace(/-/g,'');
const fm = fmpro.get(base) || fmpro.get(baseNoDash);
if (!fm) { noMatch++; unmatched.push(boltSku); continue; }
resolved++;
const newMfr = fm.mfr;
const cur = p.mfr?.value || '';
if (cur === newMfr) { alreadyCorrect++; continue; }
willUpdate++;
changes.push({ id:p.id, title:p.title, status:p.status, sku:boltSku, currentMfr:cur, newMfr, vendor:fm.vname||fm.vcode });
}
console.log(`\nFmpro lookup:`);
console.log(` resolved: ${resolved}`);
console.log(` no fmpro match: ${noMatch}`);
console.log(` missing SKU: ${missingSku}`);
console.log(`\nMetafield delta:`);
console.log(` already correct: ${alreadyCorrect}`);
console.log(` will update: ${willUpdate}`);
if (unmatched.length) {
console.log(`\nFirst 10 unmatched SKUs:`);
unmatched.slice(0,10).forEach(s=>console.log(' -',s));
}
if (changes.length) {
console.log(`\nFirst 10 changes:`);
changes.slice(0,10).forEach(c=>console.log(` - ${c.sku.padEnd(20)} cur=${(c.currentMfr||'(none)').padEnd(15)} → new=${c.newMfr}`));
}
fs.writeFileSync(__dirname + '/hollywood_changes.json', JSON.stringify(changes,null,2));
console.log(`\nWrote ${changes.length} change rows to hollywood_changes.json`);
})();