[object Object]

← back to Dw Yolo Loop

ROOT CAUSE + FIX: Kravet MAP push now unit-aware (ROLL-only gate)

f1da71632f5c06d6c1cc90c8bfedba941b39ff10 · 2026-06-15 09:28:25 -0700 · Steve Abrams

Root cause (DTD-confirmed, 2026-06-15): kravet_authoritative_pricing / April Kravet
sheet store MAP per the item's unit_of_measure, which is MIXED — ~51% YARD (fabric,
e.g. Brunschwig 8013149 = $201.60/yard), ~48% ROLL (wallcovering), 2% EACH. The
vendor-scoped push applied per-yard fabric MAP onto per-roll Shopify products -> ~3x
underpricing. Fix: gate on unit_of_measure='ROLL' (from kravet_sheet_import_2026_04_20,
join authoritative.mfr_sku=april.item). Corrected target: 998 -> 179 real roll
corrections (3,061 YARD fabrics + 142 EACH + 265 unknown excluded). 448 bad writes
already reverted (0 err). Memory: kravet-map-table-mixes-yard-and-roll-units.

Files touched

Diff

commit f1da71632f5c06d6c1cc90c8bfedba941b39ff10
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Mon Jun 15 09:28:25 2026 -0700

    ROOT CAUSE + FIX: Kravet MAP push now unit-aware (ROLL-only gate)
    
    Root cause (DTD-confirmed, 2026-06-15): kravet_authoritative_pricing / April Kravet
    sheet store MAP per the item's unit_of_measure, which is MIXED — ~51% YARD (fabric,
    e.g. Brunschwig 8013149 = $201.60/yard), ~48% ROLL (wallcovering), 2% EACH. The
    vendor-scoped push applied per-yard fabric MAP onto per-roll Shopify products -> ~3x
    underpricing. Fix: gate on unit_of_measure='ROLL' (from kravet_sheet_import_2026_04_20,
    join authoritative.mfr_sku=april.item). Corrected target: 998 -> 179 real roll
    corrections (3,061 YARD fabrics + 142 EACH + 265 unknown excluded). 448 bad writes
    already reverted (0 err). Memory: kravet-map-table-mixes-yard-and-roll-units.
---
 scripts/kravet-cost/push-map-prices.mjs | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/scripts/kravet-cost/push-map-prices.mjs b/scripts/kravet-cost/push-map-prices.mjs
index e52be4e..7e2681f 100644
--- a/scripts/kravet-cost/push-map-prices.mjs
+++ b/scripts/kravet-cost/push-map-prices.mjs
@@ -41,6 +41,17 @@ async function gql(q, v) {
 const norm = s => String(s == null ? '' : s).toUpperCase().trim();
 const isSample = v => /(sample|memo|swatch)/i.test([v.title, v.sku].join(' ')) || /-sample$/i.test(v.sku || '');
 
+// UNIT GATE (root cause 2026-06-15): the Kravet MAP table mixes YARD (fabric) and ROLL
+// (wallcovering) units; MAP is per-unit. Only ROLL items may have MAP pushed to a roll
+// price. YARD/EACH/etc are excluded (per-yard fabric MAP on a roll = ~3x underpricing).
+const UNIT = new Map();
+try {
+  for (const line of fs.readFileSync('data/kravet-cost/unit-map.csv', 'utf8').trim().split('\n')) {
+    const [item, u] = line.split(','); if (item) UNIT.set(item.trim(), (u || '').trim());
+  }
+} catch (e) { console.error('FATAL: unit-map.csv missing — run the unit export first'); process.exit(1); }
+const unitOf = mfr => UNIT.get(norm(mfr)) || '(unknown)';
+
 // 1) MAP by mfr_sku
 const mapBy = new Map();
 for (const line of fs.readFileSync('data/kravet-cost/MAP-resolved-consolidated.csv', 'utf8').trim().split('\n').slice(1)) {
@@ -56,10 +67,20 @@ for (const line of fs.readFileSync('/tmp/kf_gid_mfr.csv', 'utf8').trim().split('
 }
 // de-dup by gid (one product = one roll)
 const seen = new Set(); targets = targets.filter(t => seen.has(t.gid) ? false : seen.add(t.gid));
+
+// UNIT GATE: keep only ROLL items; tally what we drop and why.
+const unitDrop = {};
+const before = targets.length;
+targets = targets.filter(t => {
+  const u = unitOf(t.mfr);
+  if (u === 'ROLL') return true;
+  unitDrop[u] = (unitDrop[u] || 0) + 1; return false;
+});
 if (Number.isFinite(LIMIT)) targets = targets.slice(0, LIMIT);
 
 console.log(`push-map-prices — mode: ${APPLY ? '⚠️  LIVE APPLY' : 'DRY-RUN'}${VENDOR ? ` | vendor=${VENDOR}` : ''}`);
-console.log(`resolved MAP targets: ${targets.length}\n`);
+console.log(`unit gate: ${before} resolved -> ${targets.length} ROLL (excluded non-ROLL: ${JSON.stringify(unitDrop)})`);
+console.log(`resolved ROLL MAP targets: ${targets.length}\n`);
 
 const Q = `query($ids:[ID!]!){ nodes(ids:$ids){ ... on Product { id variants(first:30){ nodes { id title sku price } } } } }`;
 const M = `mutation($pid:ID!,$variants:[ProductVariantsBulkInput!]!){ productVariantsBulkUpdate(productId:$pid, variants:$variants){ userErrors{ field message } } }`;

← 182a368 Kravet MAP revert tool (DTD verdict B): restore the 448 writ  ·  back to Dw Yolo Loop  ·  GMC $4.25 feed-price blast-radius count (read-only audit); r d9b77f9 →