[object Object]

← back to Designer Wallcoverings

cadence: unit-aware soldBy for Kravet family (soldByUnit) — yard-priced goods (Winfield Thybony foils) label 'Yard'/'Priced Per Yard' not 'Single Roll'; additive, non-opt-in vendors unchanged

202fdad2364483213ab2cf1fb89b9370f13d8104 · 2026-06-25 17:09:32 -0700 · Steve

Files touched

Diff

commit 202fdad2364483213ab2cf1fb89b9370f13d8104
Author: Steve <steve@designerwallcoverings.com>
Date:   Thu Jun 25 17:09:32 2026 -0700

    cadence: unit-aware soldBy for Kravet family (soldByUnit) — yard-priced goods (Winfield Thybony foils) label 'Yard'/'Priced Per Yard' not 'Single Roll'; additive, non-opt-in vendors unchanged
---
 shopify/scripts/cadence/cadence-import.js | 24 ++++++++++++++++++++++--
 shopify/scripts/cadence/vendors.js        |  2 +-
 2 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/shopify/scripts/cadence/cadence-import.js b/shopify/scripts/cadence/cadence-import.js
index 75550b62..851fbb9a 100644
--- a/shopify/scripts/cadence/cadence-import.js
+++ b/shopify/scripts/cadence/cadence-import.js
@@ -392,6 +392,18 @@ function selectSkus(cfg, limit) {
     } catch { hasHouseLine = false; }
   }
   const houseLineSel = hasHouseLine ? `, (${String(cfg.houseLineCol).replace(/[^a-z0-9_]/gi,'')})::text AS house_line` : '';
+  // Unit-aware soldBy (cfg.soldByUnit): Kravet-family staging mixes ROLL- and YARD-priced
+  // goods in one table (Winfield Thybony foils sell per yard). Select unit_of_measure so
+  // resolveType() labels each row by its real unit, not the cfg 'Single Roll' constant.
+  // Probed for existence like all_images so vendors without the column are unaffected. (2026-06-25)
+  let hasUnit = false;
+  if (cfg.soldByUnit) {
+    try {
+      const t = String(cfg.table).replace(/[^a-z0-9_]/gi,'');
+      hasUnit = (psql(`SELECT 1 FROM information_schema.columns WHERE table_name='${t}' AND column_name='unit_of_measure' LIMIT 1;`) || '').trim() === '1';
+    } catch { hasUnit = false; }
+  }
+  const unitSel = hasUnit ? `, unit_of_measure::text AS unit_of_measure` : '';
   // sampleOnly (Quadrille-house, no cost): select net-new rows scoped by brand, NO cost column,
   // NO cost>0 filter. description pulled from ai_description / description for the activation gate.
   if (cfg.sampleOnly) {
@@ -409,13 +421,13 @@ function selectSkus(cfg, limit) {
     const keys = [...STD, 'vendor_desc', ...(hasAllImg ? ['all_images'] : []), ...(hasHouseLine ? ['house_line'] : [])];
     return out.split('\n').map(line => { const v=line.split('\t'); const o={}; keys.forEach((k,i)=>o[k]=v[i]); o.material=mfVal(o.material); o.cost=0; o.sampleOnly=true; return o; });
   }
-  const sql = `SELECT ${cols}, (${cfg.costExpr})::numeric AS cost${sellSel}${allImgSel}${houseLineSel}
+  const sql = `SELECT ${cols}, (${cfg.costExpr})::numeric AS cost${sellSel}${allImgSel}${houseLineSel}${unitSel}
     FROM ${cfg.table}
     WHERE coalesce(shopify_product_id::text,'')='' AND (${cfg.costExpr})::numeric > 0${dedupSkipClause(cfg.table)}
     ORDER BY dw_sku LIMIT ${limit};`;
   const out = psql(sql);
   if (!out) return [];
-  const keys = [...STD, 'cost', ...(cfg.sellExpr ? ['sell'] : []), ...(hasAllImg ? ['all_images'] : []), ...(hasHouseLine ? ['house_line'] : [])];
+  const keys = [...STD, 'cost', ...(cfg.sellExpr ? ['sell'] : []), ...(hasAllImg ? ['all_images'] : []), ...(hasHouseLine ? ['house_line'] : []), ...(hasUnit ? ['unit_of_measure'] : [])];
   return out.split('\n').map(line => { const v=line.split('\t'); const o={}; keys.forEach((k,i)=>o[k]=v[i]); o.material=mfVal(o.material); o.cost=parseFloat(o.cost); if(cfg.sellExpr) o.sell=parseFloat(o.sell); return o; });
 }
 
@@ -545,6 +557,14 @@ function resolveType(cfg, row) {
     }
     if (hit && hit.productType && hit.soldBy) return { productType: hit.productType, soldBy: hit.soldBy };
   }
+  // Unit-aware soldBy (cfg.soldByUnit): one staging table mixes ROLL- and YARD-priced
+  // wallcoverings (Kravet family — Winfield Thybony foils sell per yard). Derive soldBy
+  // from the row's unit_of_measure so yard goods aren't mislabeled "Single Roll". (2026-06-25)
+  if (cfg && cfg.soldByUnit && row && row.unit_of_measure) {
+    const u = String(row.unit_of_measure).toUpperCase();
+    if (u.includes('YARD') || u === 'YD') return { productType: cfg.productType || 'Wallcovering', soldBy: 'Yard' };
+    if (u.includes('ROLL'))               return { productType: cfg.productType || 'Wallcovering', soldBy: 'Single Roll' };
+  }
   return { productType: cfg.productType || 'Wallcovering', soldBy: cfg.soldBy || 'Single Roll' };
 }
 
diff --git a/shopify/scripts/cadence/vendors.js b/shopify/scripts/cadence/vendors.js
index 284f5b4e..8c32dc5b 100644
--- a/shopify/scripts/cadence/vendors.js
+++ b/shopify/scripts/cadence/vendors.js
@@ -64,7 +64,7 @@ module.exports = {
   // Mulberry, Threads, Baker Lifestyle, Gaston y Daniela, Clarke & Clarke, etc.):
   // Steve 2026-06-11 "use the MAP pricing for all kravet and subbrands" — sell = MAP
   // (price_retail_new = NEW MAP from Jan-2026 list, fallback price_retail), NOT cost/0.65/0.85.
-  'Kravet':     { table: 'kravet_catalog',           costExpr: 'cost_price', sellExpr: 'COALESCE(NULLIF(price_retail_new,0),NULLIF(price_retail,0))', soldBy: 'Single Roll', productType: 'Wallcovering', discountNote: 'cost from GDrive Jan-2026 wholesale list (incl 5% tariff); SELL AT MAP' },
+  'Kravet':     { table: 'kravet_catalog',           costExpr: 'cost_price', sellExpr: 'COALESCE(NULLIF(price_retail_new,0),NULLIF(price_retail,0))', soldBy: 'Single Roll', soldByUnit: true, productType: 'Wallcovering', discountNote: 'cost from GDrive Jan-2026 wholesale list (incl 5% tariff); SELL AT MAP. soldByUnit:true → YARD rows (Winfield Thybony foils) labeled per-yard, not Single Roll (2026-06-25)' },
   // Schumacher: Designer Net (vendor_registry discount 0.00, confirmed) — cost = priceUsd
   // from the authenticated API recrawl (2026-06-11). retail = cost/0.65/0.85.
   'Schumacher': { table: 'schumacher_catalog',       costExpr: 'cost',                 soldBy: 'Single Roll', productType: 'Wallcovering', discountNote: 'Designer Net 0% — cost=priceUsd (authenticated API recrawl)' },

← 8bb3a5cd docs(filter-topbar): record Material=Contents data finding +  ·  back to Designer Wallcoverings  ·  Disable stage-winfield-thybony.py — keyed off stale/collided 96401581 →