From 17def7c43f59bd1a822dcb3ad9f75be51f9d146e Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sun, 19 Apr 2026 21:50:44 +0000 Subject: [PATCH] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/settings.import.tsx | 46 +++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/src/routes/settings.import.tsx b/src/routes/settings.import.tsx index ed51685..d59fea9 100644 --- a/src/routes/settings.import.tsx +++ b/src/routes/settings.import.tsx @@ -555,28 +555,50 @@ const IMPORTERS: ImporterConfig[] = [ description: "Invoice headers (line items not imported). If the client isn't recognized, an archived client is auto-created. If the case isn't recognized, an archived placeholder case is auto-created so the reference is preserved.", table: "invoices", conflict: "external_id", - required: ["invoice_number", "client_id"], + // Only invoice_number is strictly required at the row level. client_id is + // deferred — the async step will look it up or auto-create an archived + // client. If invoice_number is also missing we auto-generate one. + required: ["invoice_number"], aliases: { id: "external_id", invoiceid: "external_id", externalid: "external_id", - number: "invoice_number", invoicenumber: "invoice_number", - companyid: "_clientext", clientid: "_clientext", company: "_clientname", - caseid: "_caseext", casenumber: "_casenum", - issuedate: "issue_date", date: "issue_date", + number: "invoice_number", invoicenumber: "invoice_number", invoiceno: "invoice_number", invno: "invoice_number", + companyid: "_clientext", clientid: "_clientext", company: "_clientname", clientname: "_clientname", client: "_clientname", + caseid: "_caseext", matterid: "_caseext", casenumber: "_casenum", matternumber: "_casenum", + issuedate: "issue_date", date: "issue_date", invoicedate: "issue_date", billdate: "issue_date", duedate: "due_date", status: "status", - subtotal: "subtotal", tax: "tax", total: "total", amount: "total", - amountpaid: "amount_paid", paid: "amount_paid", - notes: "notes", memo: "notes", + subtotal: "subtotal", tax: "tax", total: "total", amount: "total", invoicetotal: "total", grandtotal: "total", + amountpaid: "amount_paid", paid: "amount_paid", paidamount: "amount_paid", + notes: "notes", memo: "notes", description: "notes", }, numeric: ["subtotal", "tax", "total", "amount_paid"], dateCols: ["issue_date", "due_date"], transform: (r, ctx) => { r.client_id = (r._clientext && ctx.clientByExt.get(String(r._clientext))) || (r._clientname && ctx.clientByName.get(String(r._clientname).toLowerCase())) || null; r.case_id = (r._caseext && ctx.caseByExt.get(String(r._caseext))) || (r._casenum && ctx.caseByNumber.get(String(r._casenum))) || null; - // Keep hints — async pre-insert step will auto-create an archived client - // (and archived case) when no match is found. - const s = String(r.status ?? "").toLowerCase(); - r.status = ["draft", "sent", "paid", "overdue", "void"].includes(s) ? s : "draft"; + // Auto-generate invoice number if missing (uses external_id or timestamp) + if (!r.invoice_number || !String(r.invoice_number).trim()) { + r.invoice_number = r.external_id + ? `IMP-${String(r.external_id)}` + : `IMP-${Date.now()}-${Math.floor(Math.random() * 10000)}`; + } + // Broader status normalization — many legacy systems use Open/Outstanding/Partial/etc. + const s = String(r.status ?? "").toLowerCase().trim(); + const statusMap: Record = { + "": "draft", + draft: "draft", + open: "sent", outstanding: "sent", unpaid: "sent", sent: "sent", issued: "sent", billed: "sent", pending: "sent", + paid: "paid", closed: "paid", complete: "paid", completed: "paid", settled: "paid", + overdue: "overdue", late: "overdue", pastdue: "overdue", "past due": "overdue", + void: "void", voided: "void", cancelled: "void", canceled: "void", + partial: "sent", partiallypaid: "sent", "partially paid": "sent", + }; + r.status = statusMap[s] ?? "draft"; + // Ensure totals have safe defaults so non-null numeric columns don't break + if (r.total == null) r.total = (r.subtotal ?? 0) + (r.tax ?? 0); + if (r.subtotal == null) r.subtotal = r.total ?? 0; + if (r.tax == null) r.tax = 0; + if (r.amount_paid == null) r.amount_paid = 0; return r; }, },