← back to Filemaker Mcp
remove dead lib/invoice-core.js (orphaned from refactor; lib/invoice.js is the single source of truth)
1f3eaf89a67c8a2e31c242102ce0a28a186ddc9e · 2026-06-29 13:42:45 -0700 · Steve
Files touched
Diff
commit 1f3eaf89a67c8a2e31c242102ce0a28a186ddc9e
Author: Steve <steve@designerwallcoverings.com>
Date: Mon Jun 29 13:42:45 2026 -0700
remove dead lib/invoice-core.js (orphaned from refactor; lib/invoice.js is the single source of truth)
---
lib/invoice-core.js | 109 ----------------------------------------------------
1 file changed, 109 deletions(-)
diff --git a/lib/invoice-core.js b/lib/invoice-core.js
deleted file mode 100644
index 2db49ce..0000000
--- a/lib/invoice-core.js
+++ /dev/null
@@ -1,109 +0,0 @@
-// Build a full FileMaker invoice from a Shopify order, applying Steve's field
-// rules. Called by the sync after the client is ensured. Separate base tables
-// in FileMaker mean some fields are written via different layouts but the SAME
-// recordId only works on its own base table — so header/line fields go via
-// 'Invoice to Client Copy', the middle SKU via 'Layout #47', WC PATTERN via
-// '1-order detail 1 Copy3'.
-import * as fm from '../src/fm-client.js';
-
-const HDR = 'Invoice to Client Copy'; // header + line items (base table that owns the record)
-const SKU_LAY = 'Layout #47'; // has 'JS Easy SKU #' (same base table as HDR)
-const WCP_LAY = '1-order detail 1 Copy3';// has 'wc pattern' (same base table as HDR)
-const RESALE_LAY = 'PROFORMA'; // has 'Resale card lookup' (same base table as HDR)
-
-const today = () => { const d = new Date(); return `${String(d.getMonth()+1).padStart(2,'0')}/${String(d.getDate()).padStart(2,'0')}/${d.getFullYear()}`; };
-const stamp = () => new Date().toLocaleString('en-US', { month:'short', day:'numeric', year:'numeric', hour:'numeric', minute:'2-digit' });
-export const baseSku = (s) => { const m=(s||'').match(/^([A-Za-z]+-?\d+)/); return m?m[1]:(s||''); };
-export const sampleStrip = (s) => (s||'').replace(/-?sample/ig,'').trim();
-function uom(li){
- const t=`${li.variant_title||''} ${li.name||''} ${li.sku||''}`.toLowerCase();
- if(/per yard|\/yard|\byard/.test(t)) return 'yards';
- if(/double roll|per roll|\/roll|\broll/.test(t)) return 'rolls';
- if(/bolt/.test(t)) return 'bolts';
- if(/sample|memo|swatch/.test(t)) return 'sample';
- if(/panel/.test(t)) return 'panels';
- return 'each';
-}
-function resaleFor(ship){
- const country=(ship.country_code||'US').toUpperCase();
- const state=(ship.province_code||'').toUpperCase();
- if(country!=='US') return 'Out of Country';
- if(state && state!=='CA') return 'Out of State';
- return 'On File';
-}
-
-async function nextInvoiceNumber(){
- const top=await fm.findRecords('invoice',HDR,{'Invoice':'10*'},{limit:25,sort:[{fieldName:'Invoice',sortOrder:'descend'}]});
- const nums=top.records.map(r=>parseInt((r.fieldData['Invoice']||'').replace(/\D/g,''))).filter(n=>n>100000&&n<200000);
- return (nums.length?Math.max(...nums):102261)+1;
-}
-
-// Create the invoice. Returns {invoiceNumber, recordId, grandTotal} or {skipped}.
-export async function createInvoiceForOrder(order, accountNumber, { commit=true } = {}){
- const c=order.customer||{}, s=order.shipping_address||order.billing_address||{};
- const fullName=((c.first_name||'')+' '+(c.last_name||'')).trim();
- const shipVia=(order.shipping_lines&&order.shipping_lines[0]&&order.shipping_lines[0].title)||'';
- const shipping=parseFloat((order.total_shipping_price_set?.shop_money?.amount) ?? (order.shipping_lines?.[0]?.price) ?? 0)||0;
- const tax=parseFloat(order.total_tax||0)||0;
- const lines=order.line_items||[];
-
- if(!commit){
- return { invoiceNumber:'(next)', would:true, header:{Cust_PO:order.order_number, account:accountNumber, sold:fullName, shipVia, shipping, tax,
- resale:resaleFor(s)}, lines: lines.map(li=>({sku:li.sku, base:baseSku(li.sku), wcPattern:sampleStrip(li.sku), qty:li.quantity, unit:uom(li), price:li.price, detail:`${baseSku(li.sku)} — ${li.name}`})) };
- }
-
- const inv=String(await nextInvoiceNumber());
- // 1) header
- const res=await fm.createRecord('invoice',HDR,{
- 'Invoice':inv,'Cust PO':String(order.order_number),'Account #':String(accountNumber||''),
- 'SOLD NAME':fullName,'SOLD COMPANY':s.company||'','SOLD ADDRESS':s.address1||'','SOLD CITY':s.city||'','SOLD STATE':s.province_code||'','SOLD ZIP':s.zip||'','TELEPHONE':s.phone||order.phone||'',
- 'Date':today()
- },{dryRun:false});
- const ID=res.recordId;
- const set=async(lay,k,v)=>{ try{ await fm.updateRecord('invoice',lay,ID,{[k]:v},{dryRun:false}); }catch(e){ /* skip calc/missing */ } };
-
- // 2) header extras + ship-to
- for(const [k,v] of Object.entries({
- 'ship name':fullName,'SHIP COMAPNY':s.company||'','SHIP ADDRESS':s.address1||'','SHIP CITY':s.city||'','SHIP STATE':s.province_code||'','SHIP ZIP':s.zip||'',
- 'Resale #':resaleFor(s),'BUYER':fullName,'SALES PERSON':'Otto','SHIP VIA':shipVia,
- 'SHIPPING HANDLING':shipping,'SALES TAX YN':(tax>0?'Y':'N'),'check Number':today()
- })) await set(HDR,k,v);
-
- // 3) line items -> repetitions 1..N
- for(let i=0;i<lines.length;i++){
- const li=lines[i], r=i+1, price=parseFloat(li.price||0)||0;
- await set(HDR,`DETAIL 1(${r})`,`${baseSku(li.sku)} — ${li.name}`);
- await set(HDR,`Q1(${r})`,li.quantity);
- await set(HDR,`Unit 1(${r})`,uom(li));
- await set(HDR,`RETAIL 1(${r})`,price);
- await set(HDR,`NET 1(${r})`,price);
- await set(HDR,`Sidemark 1(${r})`,s.company||'');
- }
- // 4) middle SKU + WC PATTERN (primary line) + resale card lookup
- const primary=lines[0]||{};
- await set(SKU_LAY,'JS Easy SKU #',baseSku(primary.sku));
- await set(WCP_LAY,'wc pattern',sampleStrip(primary.sku));
- await set(RESALE_LAY,'Resale card lookup',resaleFor(s));
-
- // 5) paid-in-full from the computed grand total, then the import stamp
- let grand=0;
- try{ grand=parseFloat((await fm.getRecord('invoice',HDR,ID)).fieldData['GRAND TOTAL']||0)||0; }catch{}
- await set(HDR,'PAID ON ACCOUNT',grand);
- await set(HDR,'Notes',`Imported by Otto — ${stamp()}`);
-
- return { invoiceNumber:inv, recordId:ID, grandTotal:grand };
-}
-
-// Post a new-order notification to Slack #general.
-export async function postSlackOrder({ order, invoiceNumber, clientName }){
- const token=process.env.SLACK_BOT_TOKEN; if(!token) return { ok:false, error:'no SLACK_BOT_TOKEN' };
- const channel=process.env.SLACK_ORDER_CHANNEL||'#general';
- const store=process.env.SHOPIFY_STORE;
- const time=new Date(order.created_at||Date.now()).toLocaleString('en-US',{hour:'numeric',minute:'2-digit'});
- const link=`https://${store}/admin/orders/${order.id}`;
- const text=`🧾 *New Order* — *${clientName}*\nInvoice #${invoiceNumber} · ${time} · *$${order.total_price}*\n<${link}|View order #${order.order_number}>`;
- const r=await fetch('https://slack.com/api/chat.postMessage',{method:'POST',
- headers:{Authorization:'Bearer '+token,'Content-Type':'application/json'},
- body:JSON.stringify({channel,text})});
- return await r.json();
-}
← f9e757d invoice: strip dashes in WC Pattern Number AKA DW SKU; write
·
back to Filemaker Mcp
·
fix: fm_find/sort JSON-string coercion (harness stringifies 7b9b08c →