← back to Dw Yolo Loop
scripts/kravet-2026-coverage-audit.mjs
42 lines
#!/usr/bin/env node
/* READ-ONLY: do we have a 2026 price for every live Kravet-family item?
For each Kravet-family vendor, pull active products + their manufacturer_sku metafield,
check membership in the 2026-priced SKU set (auth_pricing.new_map>0 → /tmp/auth2026_skus.txt).
Reports per-vendor coverage + writes /tmp/kravet_2026_gaps.csv (items with NO 2026 price). */
import fs from 'node:fs';
import { rest, restAll } from './lib/shopify.mjs';
const SET = new Set(fs.readFileSync('/tmp/auth2026_skus.txt','utf8').trim().split('\n').map(s=>s.trim().toUpperCase()));
const VENDORS = ['Kravet','Kravet Couture','Kravet Design','Lee Jofa','Lee Jofa Modern','Brunschwig & Fils',
'Cole & Son','GP & J Baker','Clarke And Clarke','Mulberry','Threads','Baker Lifestyle','Gaston y Daniela','Andrew Martin'];
const norm = s => (s||'').toUpperCase().replace(/\s+/g,' ').trim();
const getAll = vendor => restAll(`/products.json?vendor=${encodeURIComponent(vendor)}&status=active&limit=250&fields=id,title,handle,variants`, 'products');
const isSample = v => (v.option1||'').toLowerCase()==='sample' || /-sample$/i.test(v.sku||'');
(async()=>{
const summary=[];
const csv=['vendor,mfr_sku,dw_sku,title,handle'];
for(const vendor of VENDORS){
let prods; try{ prods=await getAll(vendor); }catch(e){ console.log(` ${vendor}: ERR`); continue; }
if(!prods.length) continue;
let have=0, miss=0, noSku=0, n=0;
for(const p of prods){
n++;
const mf=await(await rest(`/products/${p.id}/metafields.json`)).json();
const code=norm(((mf.metafields||[]).find(x=>x.namespace==='custom'&&x.key==='manufacturer_sku')||{}).value||'');
const yard=p.variants.find(v=>!isSample(v))||p.variants[0];
if(!code){ noSku++; csv.push([vendor,'(no mfr_sku)',yard&&yard.sku||'',JSON.stringify(p.title),p.handle].join(',')); continue; }
if(SET.has(code)) have++;
else { miss++; csv.push([vendor,code,yard&&yard.sku||'',JSON.stringify(p.title),p.handle].join(',')); }
}
const pct=(100*have/prods.length).toFixed(1);
summary.push({vendor,total:prods.length,have,miss,noSku,pct});
console.log(` ${vendor.padEnd(20)} ${prods.length} total | 2026:${have} (${pct}%) | missing:${miss} | no-mfr-sku:${noSku}`);
}
fs.writeFileSync('/tmp/kravet_2026_gaps.csv', csv.join('\n'));
const T=summary.reduce((a,s)=>({total:a.total+s.total,have:a.have+s.have,miss:a.miss+s.miss,noSku:a.noSku+s.noSku}),{total:0,have:0,miss:0,noSku:0});
console.log(`\n=== TOTAL: ${T.total} live Kravet items | have 2026 price: ${T.have} (${(100*T.have/T.total).toFixed(1)}%) | missing: ${T.miss} | no mfr_sku: ${T.noSku} ===`);
console.log(`gap list → /tmp/kravet_2026_gaps.csv (${T.miss+T.noSku} rows)`);
})().catch(e=>{console.error(e);process.exit(1);});