← back to All Designerwallcoverings
all.dw grid: manufacturer-SKU search — backfill mfr_sku from *_catalog tables (sync leaves shopify_products.mfr_sku empty for new lines); feeds mfr_number display + search haystack
75328f4ddf5dbebd8085515a6d6583280bb7e915 · 2026-07-08 12:24:32 -0700 · steve@designerwallcoverings.com
Files touched
Diff
commit 75328f4ddf5dbebd8085515a6d6583280bb7e915
Author: steve@designerwallcoverings.com <steve@designerwallcoverings.com>
Date: Wed Jul 8 12:24:32 2026 -0700
all.dw grid: manufacturer-SKU search — backfill mfr_sku from *_catalog tables (sync leaves shopify_products.mfr_sku empty for new lines); feeds mfr_number display + search haystack
---
server.js | 37 ++++++++++++++++++++++++++++++++++---
1 file changed, 34 insertions(+), 3 deletions(-)
diff --git a/server.js b/server.js
index 7500ca6..09ca616 100644
--- a/server.js
+++ b/server.js
@@ -193,6 +193,7 @@ let FM = new Map(); // fmKey(dw_sku) → FileMaker WALLPAPER mirror
let FM_MATCHED = new Set(); // fmKeys that joined a shopify_products row this snapshot
let FM_STATS = { rows: 0, matched: 0, discontinued_hidden: 0, mismatch: 0, fm_only_live: 0 };
let STOCK = new Map(); // upper(sku) → public-safe stored stock snapshot (the /livestock FREE path)
+let MFR = new Map(); // upper(bare dw_sku) → manufacturer SKU, backfilled from *_catalog tables
let VENDOR_VIEWER = new Map(); // slugify(vendor) / slug → internal line-viewer URL (the vendor's live microsite)
let VENDOR_ITEM = new Map(); // slugify(vendor) / slug → { url, handles:Set } — the microsite's FULL handle set;
// "This Item" deep-links resolve ONLY when the row's handle is IN that set.
@@ -239,6 +240,31 @@ async function loadStock() {
STOCK = map;
}
+// Manufacturer-SKU backfill — upper(bare dw_sku) → mfr_sku, from every *_catalog table.
+// The Shopify sync populates shopify_products from variant SKUs, NOT the manufacturer_sku
+// metafield, so newer lines (e.g. Pierre Frey) have an empty mfr_sku and their manufacturer
+// SKU (BP327001) is invisible to grid search. This backfills it from the dw_unified staging
+// catalogs so mfr-SKU search + the mfr_number display work for every catalog-backed vendor.
+async function loadMfr() {
+ const map = new Map();
+ const { rows: cols } = await pool.query(
+ `SELECT table_name, column_name FROM information_schema.columns
+ WHERE table_schema='public' AND table_name ~ '^[a-z0-9_]+_catalog$'
+ AND column_name IN ('dw_sku','mfr_sku')`);
+ const byTab = {};
+ for (const c of cols) (byTab[c.table_name] ||= new Set()).add(c.column_name);
+ const parts = [];
+ for (const [t, set] of Object.entries(byTab)) {
+ if (set.has('dw_sku') && set.has('mfr_sku'))
+ parts.push(`SELECT dw_sku, mfr_sku FROM "${t}" WHERE dw_sku IS NOT NULL AND mfr_sku IS NOT NULL`);
+ }
+ if (parts.length) {
+ const { rows } = await pool.query(parts.join(' UNION ALL '));
+ for (const r of rows) { const k = String(r.dw_sku).toUpperCase(); if (!map.has(k)) map.set(k, r.mfr_sku); }
+ }
+ MFR = map;
+}
+
// FileMaker WALLPAPER mirror (dw_unified.filemaker_wallpaper, written by
// scripts/fm-wallpaper-sync.mjs). FMPro is Steve's SOURCE OF TRUTH: "is this live?"
// (Date Discontinued), the authoritative mfr number, width + specs, and the source for
@@ -310,7 +336,11 @@ function deriveRow(r) {
const fmk = fmKey(r.dw_sku || r.variant_sku || r.sku);
const fm = (fmk && FM.get(fmk)) || null;
if (fm) FM_MATCHED.add(fmk);
- const shopMfr = r.mfr_sku || null;
+ // manufacturer SKU: prefer the synced shopify_products.mfr_sku; else backfill from the
+ // *_catalog staging tables by bare dw_sku (the sync doesn't populate mfr_sku for newer
+ // lines like Pierre Frey → BP327001 would otherwise be invisible to search).
+ const bareDw = String(r.dw_sku || r.variant_sku || r.sku || '').replace(/-sample$/i, '').toUpperCase();
+ const shopMfr = r.mfr_sku || (bareDw && MFR.get(bareDw)) || null;
const fmMfr = fm ? (fm.mfr_number || null) : null;
// mfr_mismatch: both sides present AND their normalized tokens disagree → the grid flags it
// (the mfr-number verification workflow Steve does by hand in FileMaker's "verification" layouts).
@@ -326,7 +356,7 @@ function deriveRow(r) {
// Manufacturer identifiers — admin curation aid (Steve rule: new-SKU go-live gate needs a real
// mfr NAME + mfr SKU number). mfr_number is the clean, ~93%-populated field; mfr_name comes from
// supplier_name and is rendered only when present (it's blank/inconsistent for some private labels).
- mfr_number: r.mfr_sku || null,
+ mfr_number: shopMfr,
mfr_name: r.supplier_name || null,
// ── FileMaker source-of-truth enrichment (null when the SKU isn't in the WALLPAPER file) ──
fm_present: !!fm,
@@ -375,7 +405,7 @@ function deriveRow(r) {
created: r.created_at_shopify ? new Date(r.created_at_shopify).getTime() : 0,
updated: r.updated_at_shopify ? new Date(r.updated_at_shopify).getTime() : 0,
// one lowercase haystack per row so search is a single .includes() pass
- hay: [r.dw_sku, r.variant_sku, r.sku, r.mfr_sku, r.supplier_name, r.title, r.vendor, r.product_type, r.pattern_name, r.tags].filter(Boolean).join(' ').toLowerCase(),
+ hay: [r.dw_sku, r.variant_sku, r.sku, r.mfr_sku, shopMfr, r.supplier_name, r.title, r.vendor, r.product_type, r.pattern_name, r.tags].filter(Boolean).join(' ').toLowerCase(),
};
}
@@ -387,6 +417,7 @@ async function loadSnapshot() {
buildVendorViewer();
loadCoverage();
try { await loadStock(); } catch (e) { console.error('stock load failed (non-fatal):', e.message); }
+ try { await loadMfr(); } catch (e) { console.error('mfr load failed (non-fatal):', e.message); }
try { await loadFileMaker(); } catch (e) { console.error('filemaker load failed (non-fatal):', e.message); }
FM_MATCHED = new Set(); // repopulated by deriveRow as it joins each row this cycle
const res = await pool.query(await snapSql());
← de111d4 shopify-push: portable Shopify-lib resolution (env/mac3/co-l
·
back to All Designerwallcoverings
·
Full Shopify image galleries: bulk-sync product_images + ser 08c6237 →