Fixed importer edge cases
X-Lovable-Edit-ID: edt-281383bd-adb9-47d7-88bb-76c2df78327e Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
@@ -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<string, string> = {
|
||||
"": "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;
|
||||
},
|
||||
},
|
||||
@@ -898,8 +920,7 @@ function ImportPage() {
|
||||
(transformed._caseext || transformed._casenum || transformed._casename);
|
||||
const deferClientId =
|
||||
cfg.table === "invoices" &&
|
||||
!transformed.client_id &&
|
||||
(transformed._clientext || transformed._clientname);
|
||||
!transformed.client_id;
|
||||
const missing = cfg.required.find((k) => {
|
||||
if (k === "case_id" && deferCaseId) return false;
|
||||
if (k === "client_id" && deferClientId) return false;
|
||||
|
||||
Reference in New Issue
Block a user