← back to Dw Unbuyable Recovery Pilot
tk11124-dwve-velvet/inspect.mjs
99 lines
#!/usr/bin/env node
/**
* inspect.mjs — READ-ONLY verify-before-act for TK-11124.
* GET the LIVE Shopify state of the 18 DWVE targets + the working sibling DWVE-430760
* + one archived DWVE (429860) to confirm exclusions. Classifies each target as
* sample-only (eligible) vs already-has-sellable (skip). No writes.
*/
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { gql } from '../../designerwallcoverings/scripts/lib/shopify.mjs';
const __dir = path.dirname(fileURLToPath(import.meta.url));
const TARGETS = [
['DWVE-429560','6621005643827'],['DWVE-429660','6621005676595'],['DWVE-429760','6621005709363'],
['DWVE-429960','6621005774899'],['DWVE-430160','6621005873203'],['DWVE-430260','6621005905971'],
['DWVE-430360','6621005938739'],['DWVE-430460','6621005971507'],['DWVE-430560','6621006004275'],
['DWVE-430660','6621006069811'],['DWVE-430860','6621006135347'],['DWVE-430960','6621006200883'],
['DWVE-431060','6621006233651'],['DWVE-431160','6621006266419'],['DWVE-431260','6621006299187'],
['DWVE-431360','6621006331955'],['DWVE-431560','6621006397491'],['DWVE-431660','6621006463027'],
];
// reference: the working sibling (should already have a sellable variant) + one archived (exclude)
const REFS = [['DWVE-430760(WORKING-SIBLING)','6621005807667?'],['DWVE-429860(ARCHIVED)','?']];
const gidP = id => `gid://shopify/Product/${id}`;
const Q = `query($id:ID!){ product(id:$id){
id legacyResourceId title handle status vendor productType
featuredImage{url} mediaCount{count}
options{name values}
unitmf: metafield(namespace:"global", key:"unit_of_measure"){ value }
mmf: metafield(namespace:"custom", key:"manufacturer_sku"){ value }
variants(first:25){ nodes{ id sku title price
inventoryPolicy
selectedOptions{ name value }
inventoryItem{ id tracked } } } } }`;
function classify(p){
const vs = p.variants.nodes;
const sample = vs.find(v => /-sample$/i.test(v.sku||'') || parseFloat(v.price) <= 4.30);
const sellable = vs.find(v => v !== sample && parseFloat(v.price) > 4.30);
let cls;
if (!p) cls = 'NOT-FOUND';
else if (p.status !== 'ACTIVE') cls = 'NOT-ACTIVE:'+p.status;
else if (sellable) cls = 'HAS-SELLABLE(skip)';
else if (sample && vs.length === 1) cls = 'SAMPLE-ONLY(eligible)';
else if (sample) cls = 'SAMPLE+EXTRA:'+vs.length;
else cls = 'NO-SAMPLE:'+vs.length;
return { cls, sample, sellable };
}
const rows = [];
for (const [sku, id] of TARGETS) {
const d = await gql(Q, { id: gidP(id) });
const p = d?.product;
if (!p) { rows.push({ sku, id, cls:'NOT-FOUND' }); continue; }
const { cls, sample, sellable } = classify(p);
rows.push({
sku, id: p.legacyResourceId, cls, status: p.status, title: p.title, handle: p.handle,
vendor: p.vendor, productType: p.productType,
optionName: p.options?.[0]?.name, optionValues: p.options?.[0]?.values,
unit_of_measure_mf: p.unitmf?.value ?? null,
manufacturer_sku_mf: p.mmf?.value ?? null,
image: p.featuredImage?.url ? 'yes' : (p.mediaCount?.count>0?'media':'NONE'),
nvariants: p.variants.nodes.length,
sample: sample && { id: sample.id, sku: sample.sku, price: sample.price, opt: sample.selectedOptions, tracked: sample.inventoryItem?.tracked, policy: sample.inventoryPolicy },
sellable: sellable && { id: sellable.id, sku: sellable.sku, price: sellable.price, opt: sellable.selectedOptions, tracked: sellable.inventoryItem?.tracked, policy: sellable.inventoryPolicy },
allVariants: p.variants.nodes.map(v=>({sku:v.sku,price:v.price,opt:v.selectedOptions,tracked:v.inventoryItem?.tracked,policy:v.inventoryPolicy})),
});
}
// Also fetch the working sibling + archived by GID guess from the DWVE numbering — but we only
// have the excluded ids by SKU. Fetch DWVE-430760 & archived via product handle search instead.
const search = async (q) => {
const d = await gql(`query($q:String!){ products(first:3, query:$q){ nodes{
id legacyResourceId title status handle
unitmf: metafield(namespace:"global", key:"unit_of_measure"){ value }
options{name values}
variants(first:25){ nodes{ sku price inventoryPolicy selectedOptions{name value} inventoryItem{tracked} } } } } }`, { q });
return d?.products?.nodes || [];
};
const refOut = {};
for (const s of ['DWVE-430760','DWVE-429860','DWVE-430060','DWVE-431460']) {
refOut[s] = await search(`sku:${s}*`);
}
const summary = rows.reduce((a,r)=>{a[r.cls]=(a[r.cls]||0)+1;return a;},{});
const out = { ticket:'TK-11124', at:new Date().toISOString(), summary, targets: rows, references: refOut };
fs.writeFileSync(path.join(__dir,'out','inspect.json'), JSON.stringify(out,null,2));
console.log('=== TK-11124 DWVE inspection (READ-ONLY) ===');
console.log('summary:', JSON.stringify(summary));
for (const r of rows) console.log(` ${(r.cls||'').padEnd(22)} ${r.sku} opt="${r.optionName}" unit_mf="${r.unit_of_measure_mf}" img=${r.image} nvar=${r.nvariants} sample=${r.sample?('$'+r.sample.price+' '+r.sample.sku):'—'}`);
console.log('\n--- reference SKUs (working sibling + archived exclusions) ---');
for (const [s, nodes] of Object.entries(refOut)) {
for (const n of nodes) console.log(` ${s}: ${n.status.padEnd(9)} "${n.title}" opt="${n.options?.[0]?.name}" unit_mf="${n.unitmf?.value}" variants=${JSON.stringify(n.variants.nodes.map(v=>({sku:v.sku,price:v.price,opt:v.selectedOptions?.map(o=>o.name+':'+o.value).join('|'),tracked:v.inventoryItem?.tracked,policy:v.inventoryPolicy})))}`);
}
console.log('\nwrote out/inspect.json');