← back to Filemaker Mcp
Fix invoice dup-SKU + wrong-VID at the source (lib/wallpaper.js)
47d22bccdfedcd1d3b4eeb63c62e9dc6c6aab81a · 2026-07-30 07:52:13 -0700 · Steve
lib/wallpaper.js was the single source of both reported invoice bugs.
WRONG VID — vid was ALWAYS the "most common vid among 25 same-Series
records" (vidForSeries), cached process-wide. That pushed a series-level
guess — or a blank, when the find errored — onto invoice lines instead of
the SKU's real vendor code. resolveWallpaperSource now prefers the existing
WALLPAPER master's OWN stored vid, and only falls back to a hardened
series-modal (samples 200, never caches a blank or an error-derived result)
for a genuinely-new SKU.
DUPLICATE MASTERS — ensureWallpaper's existence check (1) collapsed EVERY
FileMaker error into "not found" (auth blip/timeout -> duplicate create) —
the same bug already fixed in sync-core.js's client dedup but missed here;
and (2) only matched the current-prefix `combo sku`, so a line renamed
Cork<->DWGL<->DWLG (combo sku is a Series&JS-Pattern CALC) hid the existing
master and minted a duplicate. New findExistingMaster is alias-aware (checks
combo sku + comboskuwithdash across renamed prefixes) and error-honest (only
fmCode 401/102/105/106 = try-next/not-found; any real error means existence
UNVERIFIED and we refuse to create).
Safe-local: changes only what future runs compute/write; no backfill of
already-corrupted masters/VIDs (gated, separate). Read-only smoke test:
DWKK120156 -> vid KRA from its own master (was a series guess);
DWSW5003662 (no master) -> hardened series fallback, verified-absent.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
Diff
commit 47d22bccdfedcd1d3b4eeb63c62e9dc6c6aab81a
Author: Steve <steve@designerwallcoverings.com>
Date: Thu Jul 30 07:52:13 2026 -0700
Fix invoice dup-SKU + wrong-VID at the source (lib/wallpaper.js)
lib/wallpaper.js was the single source of both reported invoice bugs.
WRONG VID — vid was ALWAYS the "most common vid among 25 same-Series
records" (vidForSeries), cached process-wide. That pushed a series-level
guess — or a blank, when the find errored — onto invoice lines instead of
the SKU's real vendor code. resolveWallpaperSource now prefers the existing
WALLPAPER master's OWN stored vid, and only falls back to a hardened
series-modal (samples 200, never caches a blank or an error-derived result)
for a genuinely-new SKU.
DUPLICATE MASTERS — ensureWallpaper's existence check (1) collapsed EVERY
FileMaker error into "not found" (auth blip/timeout -> duplicate create) —
the same bug already fixed in sync-core.js's client dedup but missed here;
and (2) only matched the current-prefix `combo sku`, so a line renamed
Cork<->DWGL<->DWLG (combo sku is a Series&JS-Pattern CALC) hid the existing
master and minted a duplicate. New findExistingMaster is alias-aware (checks
combo sku + comboskuwithdash across renamed prefixes) and error-honest (only
fmCode 401/102/105/106 = try-next/not-found; any real error means existence
UNVERIFIED and we refuse to create).
Safe-local: changes only what future runs compute/write; no backfill of
already-corrupted masters/VIDs (gated, separate). Read-only smoke test:
DWKK120156 -> vid KRA from its own master (was a series guess);
DWSW5003662 (no master) -> hardened series fallback, verified-absent.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
lib/wallpaper.js | 68 +++++++++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 60 insertions(+), 8 deletions(-)
diff --git a/lib/wallpaper.js b/lib/wallpaper.js
index ef2422c..7e20f71 100644
--- a/lib/wallpaper.js
+++ b/lib/wallpaper.js
@@ -26,6 +26,41 @@ function parseCombo(combo) {
return m ? { prefix: m[1].toUpperCase(), num: m[2], dashSku: `${m[1].toUpperCase()}-${m[2]}` } : null;
}
+// Series that are the SAME physical line under different DW prefixes (renamed over
+// time — e.g. Greenland cork bounced Cork<->DWGL<->DWLG). `combo sku` = Series & JS
+// Pattern is a FileMaker CALC, so a renamed master hides from a current-prefix "=="
+// lookup and a duplicate master gets minted. Check these aliases before creating.
+const SERIES_ALIASES = { CORK: ['DWGL', 'DWLG'], DWGL: ['CORK', 'DWLG'], DWLG: ['CORK', 'DWGL'] };
+// FileMaker codes that mean "this query found nothing / doesn't apply" (try the next
+// query) — as opposed to a REAL error (auth/timeout/lock) that means existence is
+// UNVERIFIABLE and we must NOT create. Swallowing a real error as "not found" is what
+// minted duplicates. 401=no match, 102=field not on layout, 105=layout, 106=table.
+const FM_SKIP_CODES = new Set(['401', '102', '105', '106']);
+
+// Alias- and error-aware existence check for a WALLPAPER master.
+// Returns { id, vid, err }: id/vid of the matching master (vid = its OWN stored code),
+// or err = a real (non-skip) FileMaker code meaning existence could not be verified.
+async function findExistingMaster(combo, p) {
+ const prefixes = [p.prefix, ...(SERIES_ALIASES[p.prefix] || [])];
+ const queries = [];
+ for (const pre of prefixes) {
+ queries.push({ 'combo sku': '==' + pre + p.num });
+ queries.push({ comboskuwithdash: '==' + pre + '-' + p.num });
+ }
+ for (const q of queries) {
+ try {
+ const r = await fm.findRecords('WALLPAPER', FULL, q, { limit: 1 });
+ const rec = r.records?.[0];
+ if (rec) return { id: rec.recordId, vid: (rec.fieldData.vid || '').trim(), err: null };
+ } catch (e) {
+ const code = String(e.fmCode || '');
+ if (FM_SKIP_CODES.has(code)) continue; // no-match / field not on layout -> next query
+ return { id: null, vid: '', err: code || e.message }; // real error -> UNVERIFIABLE
+ }
+ }
+ return { id: null, vid: '', err: null };
+}
+
// shopify_products SELECT expression (metafields-first) — reused by both lookups.
// Metafields come in TWO shapes: nested {type,value} objects AND flat scalar strings
// (e.g. Designtex rows store "manufacturer_sku":"8405252" directly). Read both:
@@ -81,14 +116,22 @@ function sourceFor(dashSku) {
return c ? { ...c, src: 'connie_our_catalog' } : null;
}
-// vendor vid code = the most common vid among existing WALLPAPER records of this Series
+// LAST-RESORT vid guess for a genuinely-new SKU: the most common vid among existing
+// WALLPAPER records of this Series. Only reached when the SKU has no master of its own
+// to read a real vid from (see resolveWallpaperSource). Hardened: sample broadly,
+// NEVER cache a blank, and NEVER cache/return a series result derived from a failed
+// find — a real error must not freeze a wrong/blank vid for the whole run.
const vidCache = {};
async function vidForSeries(series) {
if (series in vidCache) return vidCache[series];
- const r = await fm.findRecords('WALLPAPER', FULL, [{ Series: series, vid: '*' }], { limit: 25 }).catch(() => ({ records: [] }));
+ let recs = [];
+ try { recs = (await fm.findRecords('WALLPAPER', FULL, [{ Series: series, vid: '*' }], { limit: 200 })).records || []; }
+ catch (e) { if (String(e.fmCode) !== '401') return ''; } // real error -> don't cache, allow a later retry
const counts = {};
- for (const rec of r.records) { const v = (rec.fieldData.vid || '').trim(); if (v) counts[v] = (counts[v] || 0) + 1; }
- return (vidCache[series] = Object.entries(counts).sort((a, b) => b[1] - a[1])[0]?.[0] || '');
+ for (const rec of recs) { const v = (rec.fieldData.vid || '').trim(); if (v) counts[v] = (counts[v] || 0) + 1; }
+ const best = Object.entries(counts).sort((a, b) => b[1] - a[1])[0]?.[0] || '';
+ if (best) vidCache[series] = best; // cache only a real, non-blank result
+ return best;
}
// READ-ONLY resolve — what WOULD be written for a combo sku, with NO FileMaker writes.
@@ -99,10 +142,15 @@ export async function resolveWallpaperSource(combo) {
if (!p) return { ok: false, flagged: combo, reason: 'unparseable sku' };
const s = sourceFor(p.dashSku);
if (!s || !s.mfr) return { ok: false, flagged: combo, reason: `no mfr number (${s ? s.src : 'not in dw_unified'})` };
- const vid = await vidForSeries(p.prefix);
+ // vid is the SKU's OWN vendor code. Prefer the existing WALLPAPER master's stored vid
+ // (authoritative, per-SKU); fall back to the series-modal guess ONLY for a genuinely
+ // -new SKU. Previously vid was ALWAYS the series guess — which pushed the wrong (or a
+ // blank) VID onto invoices for products that already had a correct code on file.
+ const ex = await findExistingMaster(combo, p);
+ const vid = ex.vid || await vidForSeries(p.prefix);
const name = s.pattern || '', color = s.color || '', width = widthClean(s.width);
const jpg = [name, color].filter(Boolean).join(' - ');
- return { ok: true, mfr: s.mfr, vid, name, color, width, supplier: s.supplier || '', jpg, src: s.src, _p: p };
+ return { ok: true, mfr: s.mfr, vid, name, color, width, supplier: s.supplier || '', jpg, src: s.src, _p: p, _existing: ex };
}
// Ensure/complete the WALLPAPER record for a combo sku (DW SKU, no dashes, no -Sample).
@@ -112,8 +160,12 @@ export async function ensureWallpaper(combo) {
if (!r.ok) return r;
const p = r._p, s = { mfr: r.mfr, supplier: r.supplier };
const vid = r.vid, name = r.name, color = r.color, width = r.width, jpg = r.jpg;
- const ex = await fm.findRecords('WALLPAPER', FULL, { 'combo sku': '==' + combo }, { limit: 1 }).catch(() => ({ records: [] }));
- let id = ex.records[0]?.recordId;
+ // Reuse the alias-/error-aware lookup already done in resolveWallpaperSource.
+ // NEVER create when existence could not be verified (auth blip / timeout / lock):
+ // treating a real error as "not found" is exactly what minted duplicate masters.
+ const ex = r._existing || { id: null, err: null };
+ if (!ex.id && ex.err) return { ok: false, flagged: combo, reason: `existence unverified (${ex.err}) — not creating (would risk a duplicate)` };
+ let id = ex.id;
if (!id) {
const res = await fm.createRecord('WALLPAPER', ENTRY, { 'Mfr Pattern': s.mfr, 'JS Pattern': p.num, Series: p.prefix, Supplier: s.supplier || '', Width: width }, { dryRun: false }).catch((e) => ({ err: e.fmCode }));
if (res.err) return { ok: false, flagged: combo, reason: `create failed ${res.err}` };
← 1d1f33b auto-save: 2026-07-29T20:11:57 (1 files) — package-lock.json
·
back to Filemaker Mcp
·
chore: v0.2.5 (session close — invoice SKU/VID audit + fix) 92722e0 →