[object Object]

← back to Filemaker Mcp

audit: add --csv export (both categories, per-invoice rows)

4b94f5f28c1cd7bfe145ca2b9b5e528d30d528ba · 2026-07-07 07:19:03 -0700 · Steve Abrams

Files touched

Diff

commit 4b94f5f28c1cd7bfe145ca2b9b5e528d30d528ba
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Jul 7 07:19:03 2026 -0700

    audit: add --csv export (both categories, per-invoice rows)
---
 scripts/audit-dup-invoices.mjs | 24 +++++++++++++++++++++---
 1 file changed, 21 insertions(+), 3 deletions(-)

diff --git a/scripts/audit-dup-invoices.mjs b/scripts/audit-dup-invoices.mjs
index 1b6941f..ac7a304 100644
--- a/scripts/audit-dup-invoices.mjs
+++ b/scripts/audit-dup-invoices.mjs
@@ -4,13 +4,17 @@
 //
 //   node scripts/audit-dup-invoices.mjs           # last 21 days
 //   node scripts/audit-dup-invoices.mjs 60        # last 60 days
+import { writeFileSync } from 'node:fs';
 import { findRecords } from '../src/fm-client.js';
 import { loadEnv, num, money } from '../lib/fm-script-helpers.js';
 
 loadEnv(import.meta.url);
 
 const HDR = 'Invoice to Client Copy';
-const DAYS = parseInt(process.argv[2] || '21', 10);
+const args = process.argv.slice(2);
+const DAYS = parseInt(args.find((a) => /^\d+$/.test(a)) || '21', 10);
+const csvIdx = args.indexOf('--csv');
+const CSV_PATH = csvIdx !== -1 ? (args[csvIdx + 1] || `${process.env.HOME}/Desktop/dup-invoices-audit.csv`) : null;
 
 // Build a MM/DD/YYYY...MM/DD/YYYY FileMaker date range for the window.
 const d = new Date();
@@ -107,9 +111,23 @@ async function main() {
   }
 
   console.log(`\n============ SUMMARY ============`);
-  console.log(`A) Real double-billed orders:   ${dblOrders.length}   (${dblCount} extra invoices,  ${money(dblDollars)} double-counted)`);
+  console.log(`A) Real double-billed orders:   ${dblOrders.length}   (${dblCount} extra invoices,  up to ${money(dblDollars)} double-counted [upper bound — incl. fragmented orders])`);
   console.log(`B) $0 tracking-record clutter:  ${zeroOnly.length} orders   (${zeroCount} zero-dollar records)`);
-  console.log(`\nRead-only. Nothing deleted. Category A is the money problem; Category B is cosmetic clutter.`);
+  console.log(`\nRead-only. Nothing deleted. Category A is the money/reporting problem; Category B is cosmetic clutter.`);
+
+  // Optional CSV export — every row of every multi-invoice order, both categories.
+  if (CSV_PATH) {
+    const esc = (v) => `"${String(v ?? '').replace(/"/g, '""')}"`;
+    const cat = (o) => (o.hasDoubleBill ? 'A_double_billing' : 'B_zero_clutter');
+    const lines = ['category,order_po,customer,invoice,role,total,date,detail'];
+    for (const o of orders) {
+      for (const r of o.rows) {
+        lines.push([cat(o), o.po, esc(o.name), r.inv, r.role, r.total.toFixed(2), r.date, esc(r.dwsku || r.item)].join(','));
+      }
+    }
+    writeFileSync(CSV_PATH, lines.join('\n') + '\n');
+    console.log(`\nCSV written: ${CSV_PATH}  (${lines.length - 1} rows across ${orders.length} orders)`);
+  }
 }
 
 main().catch((e) => { console.error('audit error:', e.message, e.fmCode || ''); process.exit(1); });

← 6a8406b add read-only dup-invoice audit: group by Cust PO, split dou  ·  back to Filemaker Mcp  ·  add read-only dry-run pipeline test: proves 1-invoice-per-or 7b8fa26 →