← back to Filemaker Mcp
Harden client dedup + add read-only single-order test
96da4967f65ae580612f09be7d2980df5743a6d4 · 2026-07-16 09:35:30 -0700 · Steve Abrams
- findDuplicates now only swallows FileMaker 401 (no-records) as an empty
result; any other find error is surfaced (per-order handler logs it) instead
of silently returning no-match, which would double-enter the client
- scripts/test-one.mjs: read-only (commit:false) single-order test proving the
Company/Name split and live dedup without writing to FileMaker
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Files touched
M lib/sync-core.jsA scripts/test-one.mjs
Diff
commit 96da4967f65ae580612f09be7d2980df5743a6d4
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Jul 16 09:35:30 2026 -0700
Harden client dedup + add read-only single-order test
- findDuplicates now only swallows FileMaker 401 (no-records) as an empty
result; any other find error is surfaced (per-order handler logs it) instead
of silently returning no-match, which would double-enter the client
- scripts/test-one.mjs: read-only (commit:false) single-order test proving the
Company/Name split and live dedup without writing to FileMaker
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---
lib/sync-core.js | 8 +++++++-
scripts/test-one.mjs | 38 ++++++++++++++++++++++++++++++++++++++
2 files changed, 45 insertions(+), 1 deletion(-)
diff --git a/lib/sync-core.js b/lib/sync-core.js
index 95ff20a..361a4d3 100644
--- a/lib/sync-core.js
+++ b/lib/sync-core.js
@@ -65,7 +65,13 @@ export async function findDuplicates({ fields }) {
for (const q of tier.queries) {
let recs = [];
try { recs = (await fm.findRecords('Clients', LAYOUT, q, { limit: 10 })).records; }
- catch (e) { continue; /* 401 = no records found */ }
+ catch (e) {
+ // 401 = "no records match" (a genuine empty result) → keep looking.
+ // ANY other error means the dedup could not run reliably; surface it
+ // rather than silently returning no-match (which would double-enter).
+ if (String(e.fmCode) === '401') continue;
+ throw new Error(`dedup find failed (${Object.keys(q)[0]}): ${e.message}`);
+ }
const why = Object.keys(q)[0];
for (const r of recs) {
// confirm phone match by normalized last-10 to avoid wildcard false positives
diff --git a/scripts/test-one.mjs b/scripts/test-one.mjs
new file mode 100644
index 0000000..5cd3ae4
--- /dev/null
+++ b/scripts/test-one.mjs
@@ -0,0 +1,38 @@
+#!/usr/bin/env node
+// Read-only single-order test of the client-sync fix (commit:false → no writes).
+// Fetches the most recent Shopify order (or #<arg>), shows the mapped Company/Name
+// field split, then runs the live FileMaker dedup and prints what it WOULD do.
+import { readFileSync } from 'node:fs';
+import { fileURLToPath } from 'node:url';
+import { dirname, join } from 'node:path';
+import { mapOrder, syncOrder } from '../lib/sync-core.js';
+
+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];
+}
+const STORE = process.env.FM_SHOPIFY_STORE || process.env.SHOPIFY_STORE;
+const TOKEN = process.env.SHOPIFY_ORDERS_TOKEN;
+
+const arg = process.argv[2];
+const url = arg
+ ? `https://${STORE}/admin/api/2024-10/orders.json?status=any&name=${encodeURIComponent('#' + arg)}`
+ : `https://${STORE}/admin/api/2024-10/orders.json?status=any&limit=1`;
+const r = await fetch(url, { headers: { 'X-Shopify-Access-Token': TOKEN } });
+if (!r.ok) throw new Error(`Shopify fetch failed: ${r.status}`);
+const order = (await r.json()).orders?.[0];
+if (!order) { console.log('no order found'); process.exit(0); }
+
+const m = mapOrder(order);
+console.log(`\nOrder #${order.order_number}`);
+console.log(' shipping.company :', JSON.stringify(order.shipping_address?.company || ''));
+console.log(' customer name :', JSON.stringify(((order.customer?.first_name||'')+' '+(order.customer?.last_name||'')).trim()));
+console.log(' → FM Company :', JSON.stringify(m.fields['Company']), '(should be the BUSINESS, or the person if no business)');
+console.log(' → FM Name :', JSON.stringify(m.fields['Name']), '(should be the CONTACT person)');
+
+const res = await syncOrder(order, { commit: false }); // read-only: dedup runs, no writes
+console.log('\nDedup result (read-only):');
+console.log(' client action :', res.client); // would-create | existing | updated
+console.log(' matched on :', res.matchedOn || '(none — no existing client)');
+console.log(' account # :', res.accountNumber || '(n/a)');
+console.log('\nNo FileMaker records were created or modified (commit:false).');
← de6eb81 Fix client double-entry + Company/Name field swap in Shopify
·
back to Filemaker Mcp
·
auto-save: 2026-07-16T09:42:29 (1 files) — scripts/audit-cli 8e0d01f →