← back to Filemaker Mcp
invoice: backfill vid+mfr onto 158 pre-hook blank-mfr Otto invoice lines
478c8b21679ecfcc757b4437a0588d71063c8a79 · 2026-07-10 12:08:17 -0700 · Steve Abrams
Steve-approved backfill of the pre-hook backlog (orders Otto invoiced before
ensureWallpaper was wired at e138cce). scripts/backfill-blank-mfr.mjs finds all
Otto-imported invoice lines with a blank Detail 1 Mfr Number, groups by distinct
SKU, ensureWallpaper() once per SKU, then direct-writes VID + Detail 1 Mfr
Number onto every matching line. --dry-run does a read-only resolve (no FileMaker
writes) via the new exported resolveWallpaperSource().
Result: 167 blank-mfr lines -> 146 SKUs resolved, 158 lines updated, 3 SKUs
FLAGGED and left blank (DWAT65011, XP5, a custom framed-art order — no findable
mfr, per Steve's flag-don't-guess rule). Remaining blank: 9 (4 flagged-SKU lines
+ 5 blank-SKU money records) — the correct residue.
lib/wallpaper.js: extract read-only resolveWallpaperSource() out of
ensureWallpaper() so dry-run/preview callers get zero-write resolution.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
M lib/wallpaper.jsA scripts/backfill-blank-mfr.mjs
Diff
commit 478c8b21679ecfcc757b4437a0588d71063c8a79
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Fri Jul 10 12:08:17 2026 -0700
invoice: backfill vid+mfr onto 158 pre-hook blank-mfr Otto invoice lines
Steve-approved backfill of the pre-hook backlog (orders Otto invoiced before
ensureWallpaper was wired at e138cce). scripts/backfill-blank-mfr.mjs finds all
Otto-imported invoice lines with a blank Detail 1 Mfr Number, groups by distinct
SKU, ensureWallpaper() once per SKU, then direct-writes VID + Detail 1 Mfr
Number onto every matching line. --dry-run does a read-only resolve (no FileMaker
writes) via the new exported resolveWallpaperSource().
Result: 167 blank-mfr lines -> 146 SKUs resolved, 158 lines updated, 3 SKUs
FLAGGED and left blank (DWAT65011, XP5, a custom framed-art order — no findable
mfr, per Steve's flag-don't-guess rule). Remaining blank: 9 (4 flagged-SKU lines
+ 5 blank-SKU money records) — the correct residue.
lib/wallpaper.js: extract read-only resolveWallpaperSource() out of
ensureWallpaper() so dry-run/preview callers get zero-write resolution.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
lib/wallpaper.js | 19 +++++++++++++----
scripts/backfill-blank-mfr.mjs | 47 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 62 insertions(+), 4 deletions(-)
diff --git a/lib/wallpaper.js b/lib/wallpaper.js
index 05394b1..ef2422c 100644
--- a/lib/wallpaper.js
+++ b/lib/wallpaper.js
@@ -91,9 +91,10 @@ async function vidForSeries(series) {
return (vidCache[series] = Object.entries(counts).sort((a, b) => b[1] - a[1])[0]?.[0] || '');
}
-// Ensure/complete the WALLPAPER record for a combo sku (DW SKU, no dashes, no -Sample).
-// Returns { ok, mfr, vid, name, color, width, supplier, src } or { ok:false, flagged, reason }.
-export async function ensureWallpaper(combo) {
+// READ-ONLY resolve — what WOULD be written for a combo sku, with NO FileMaker writes.
+// Returns { ok, mfr, vid, name, color, width, supplier, jpg, src } or { ok:false, reason }.
+// Used by ensureWallpaper (below) and by dry-run/preview callers.
+export async function resolveWallpaperSource(combo) {
const p = parseCombo(combo);
if (!p) return { ok: false, flagged: combo, reason: 'unparseable sku' };
const s = sourceFor(p.dashSku);
@@ -101,6 +102,16 @@ export async function ensureWallpaper(combo) {
const 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 };
+}
+
+// Ensure/complete the WALLPAPER record for a combo sku (DW SKU, no dashes, no -Sample).
+// Returns { ok, mfr, vid, name, color, width, supplier, src } or { ok:false, flagged, reason }.
+export async function ensureWallpaper(combo) {
+ const r = await resolveWallpaperSource(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;
if (!id) {
@@ -113,5 +124,5 @@ export async function ensureWallpaper(combo) {
for (const [k, v] of [['Name of Pattern', name], ['Color of Pattern', color], ['vid', vid], ['JPG Name', jpg]]) {
if (v) { try { await fm.updateRecord('WALLPAPER', FULL, id, { [k]: v }, { dryRun: false }); } catch {} }
}
- return { ok: true, mfr: s.mfr, vid, name, color, width, supplier: s.supplier || '', src: s.src };
+ return { ok: true, mfr: s.mfr, vid, name, color, width, supplier: s.supplier || '', src: r.src };
}
diff --git a/scripts/backfill-blank-mfr.mjs b/scripts/backfill-blank-mfr.mjs
new file mode 100644
index 0000000..70bdb39
--- /dev/null
+++ b/scripts/backfill-blank-mfr.mjs
@@ -0,0 +1,47 @@
+// Backfill VID + Detail 1 Mfr Number onto EXISTING Otto-imported invoice lines that
+// have a blank mfr (the pre-hook backlog — orders Otto invoiced before ensureWallpaper
+// was wired into the live path). For each distinct SKU: ensureWallpaper() once (creates/
+// completes its FileMaker WALLPAPER master from dw_unified), then direct-write the
+// copy-lookup fields onto every blank-mfr line carrying that SKU. SKUs with no findable
+// mfr stay FLAGGED (Steve's rule — never guessed). See memory
+// filemaker-wallpaper-record-for-invoice. Reuses lib/wallpaper.js ensureWallpaper().
+//
+// node scripts/backfill-blank-mfr.mjs [--dry-run]
+import { readFileSync } from 'node:fs';
+import { fileURLToPath } from 'node:url';
+import { dirname, join } from 'node:path';
+const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
+for (const l of readFileSync(join(ROOT, '.env'), 'utf8').split('\n')) {
+ const m = l.match(/^([A-Z0-9_]+)=(.*)$/); if (m && !process.env[m[1]]) process.env[m[1]] = m[2].replace(/^['"]|['"]$/g, '');
+}
+const DRY = process.argv.includes('--dry-run');
+const fm = await import('../src/fm-client.js');
+const { ensureWallpaper, resolveWallpaperSource } = await import('../lib/wallpaper.js');
+const ISHIP = 'Order Detail - Main - Shipping';
+const combo = (f) => ((f['WC Pattern Number AKA DW SKU(1)'] ?? f['WC Pattern Number AKA DW SKU'] ?? '') + '').trim();
+
+// 1. Find the backlog: Otto-imported invoice lines with a blank Detail 1 Mfr Number.
+const { records } = await fm.findRecords('invoice', ISHIP, [{ 'Notes': 'Otto', 'Detail 1 Mfr Number': '=' }], { limit: 1000 });
+const lines = records.filter((r) => combo(r.fieldData)); // must have a SKU to resolve
+const bySku = new Map();
+for (const rec of lines) { const s = combo(rec.fieldData); if (!bySku.has(s)) bySku.set(s, []); bySku.get(s).push(rec); }
+console.log(`${DRY ? '[DRY-RUN] ' : ''}backlog: ${records.length} blank-mfr Otto records; ${lines.length} have a SKU; ${bySku.size} distinct SKU(s)\n`);
+
+let updated = 0, wpOk = 0, flagged = 0; const flagList = [];
+for (const [sku, recs] of bySku) {
+ // DRY = read-only resolve (no FileMaker writes); LIVE = ensureWallpaper (creates/completes master).
+ const wp = DRY ? await resolveWallpaperSource(sku) : await ensureWallpaper(sku);
+ if (!wp.ok) { flagged++; flagList.push(`${sku} (${wp.reason})`); console.log(` FLAG ${sku}: ${wp.reason} [${recs.length} line(s) left blank]`); continue; }
+ wpOk++;
+ if (DRY) { console.log(` WOULD ${sku} mfr=${wp.mfr} vid=${wp.vid} -> ${recs.length} line(s) [${wp.src}]`); updated += recs.length; continue; }
+ for (const rec of recs) {
+ try { await fm.updateRecord('invoice', ISHIP, rec.recordId, { 'VID': wp.vid || '', 'Detail 1 Mfr Number': wp.mfr }, { dryRun: false }); updated++; }
+ catch (e) { console.log(` ${sku} inv line err ${e.fmCode}`); }
+ }
+ console.log(` OK ${sku} mfr=${wp.mfr} vid=${wp.vid} -> ${recs.length} line(s) [${wp.src}]`);
+}
+console.log(`\n=== ${DRY ? 'DRY-RUN ' : ''}SUMMARY ===`);
+console.log(` distinct SKUs resolved (mfr found): ${wpOk}`);
+console.log(` invoice lines ${DRY ? 'that WOULD be' : ''} updated: ${updated}`);
+console.log(` SKUs FLAGGED (no findable mfr, left blank): ${flagged}`);
+if (flagList.length) console.log(` FLAGGED: ${flagList.join(' | ')}`);
← f8ad188 invoice: flag+notify (not swallow) SKUs with no findable mfr
·
back to Filemaker Mcp
·
chore: v0.2.2 (session close — invoice mfr backfill + flag-o 81207d4 →