← back to Sample Followup Sweep
scripts/tk12109-stamp-letter-sent.mjs
53 lines
#!/usr/bin/env node
// TK-12109 — stamp FileMaker `Date Sample Request Letter Sent` = 09/23/2026 on the 3 outstanding
// records whose 10-day follow-up letters Steve SENT MANUALLY from info@ on 2026-09-23 (verified in
// the Sent folder: FABR customers@fabricut.com, WAT sophie@watts1874.co.uk, GRD orders@graduatecollection.co.uk).
// BAR was NOT sent (held), REID is arrived, MDCKEN is no-chase — none of those are stamped.
//
// This is a canonical FileMaker WRITE (the sweep's own FIELD_2ND_DATE stamp) and is blocked by the
// Claude auto-mode classifier, so it is handed to Steve to run:
// node ~/Projects/sample-followup-sweep/scripts/tk12109-stamp-letter-sent.mjs # stamp + write restore-map
// node ~/Projects/sample-followup-sweep/scripts/tk12109-stamp-letter-sent.mjs --rollback # undo (set fields back to '')
// Blast radius = 3 rows. Reversible via the restore-map (data/tk12109-stamp-restore-map.json).
import { readFileSync, writeFileSync } from 'node:fs'; import { homedir } from 'node:os'; import { join } from 'node:path';
const cfg = JSON.parse(readFileSync(join(homedir(), '.claude.json'), 'utf8'));
const fenv = cfg?.mcpServers?.filemaker?.env || {};
for (const k of ['FM_CLOUD_HOST', 'FM_CLARIS_EMAIL', 'FM_CLARIS_PASSWORD']) process.env[k] = fenv[k];
process.env.FM_READONLY = '0';
const fm = await import('/Users/macstudio3/Projects/filemaker-mcp/src/fm-client.js');
const DB = 'WALLPAPER', LAYOUT = 'Report for old memo samples', FIELD = 'Date Sample Request Letter Sent', TODAY = '09/23/2026';
const MAP = join(homedir(), 'Projects/sample-followup-sweep/data/tk12109-stamp-restore-map.json');
const targets = [
{ sku: 'DWFC230158', vendor: 'FABR/Fabricut', recordId: '541857' },
{ sku: 'DWC324296', vendor: 'WAT/Watts 1874', recordId: '541215' },
{ sku: 'GRD8806', vendor: 'GRD/Graduate Collection', recordId: '541860' },
];
const ROLLBACK = process.argv.includes('--rollback');
async function readField(sku, recordId) {
const r = await fm.findRecords(DB, LAYOUT, { 'combo sku': sku }, { limit: 20 }).catch(() => ({ records: [] }));
const rec = (r.records || []).find((x) => String(x.recordId) === recordId);
return rec ? (rec.fieldData[FIELD] || '') : '(record not found)';
}
if (ROLLBACK) {
const map = JSON.parse(readFileSync(MAP, 'utf8'));
for (const rec of map.records) {
await fm.updateRecord(DB, LAYOUT, rec.recordId, { [FIELD]: rec.oldValue }, { dryRun: false });
const back = await readField(rec.sku, rec.recordId);
console.log(` ROLLBACK ${rec.vendor} ${rec.sku} rec ${rec.recordId}: -> ${JSON.stringify(back)} ${back === rec.oldValue ? '✓' : '✗'}`);
}
console.log('rollback complete.');
} else {
const restore = [];
for (const t of targets) {
const oldVal = await readField(t.sku, t.recordId);
restore.push({ sku: t.sku, vendor: t.vendor, recordId: t.recordId, field: FIELD, oldValue: oldVal });
await fm.updateRecord(DB, LAYOUT, t.recordId, { [FIELD]: TODAY }, { dryRun: false });
const newVal = await readField(t.sku, t.recordId);
console.log(` ${t.vendor} ${t.sku} rec ${t.recordId}: old=${JSON.stringify(oldVal)} -> new=${JSON.stringify(newVal)} ${newVal === TODAY ? '✓ VERIFIED' : '✗ MISMATCH'}`);
}
writeFileSync(MAP, JSON.stringify({ ts: new Date().toISOString(), ticket: 'TK-12109', db: DB, layout: LAYOUT, field: FIELD, stampedTo: TODAY, records: restore }, null, 2));
console.log('\nrestore-map:', MAP, '\nrollback: node scripts/tk12109-stamp-letter-sent.mjs --rollback');
}