From 67fbbfd161c7459d54a841642205b716edab3f0e 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 22:02:06 +0000 Subject: [PATCH] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/settings.import.tsx | 51 ++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/src/routes/settings.import.tsx b/src/routes/settings.import.tsx index 68908fb..d0af9ae 100644 --- a/src/routes/settings.import.tsx +++ b/src/routes/settings.import.tsx @@ -1020,40 +1020,44 @@ function ImportPage() { if (extId) ctx.caseByExt.set(String(extId), data.id); return data.id; }; - // Look up an existing invoice by number for this client, or create a - // new "sent" invoice with that exact number to bucket imported items. - const invoiceByClientAndNumber = new Map(); // `${clientId}::${number}` -> invoiceId - const ensureNamedInvoice = async ( - clientId: string, - caseId: string, + // Look up an existing invoice by number (globally, not per-client) and + // create a draft invoice with status='draft' if none exists. Time/expense + // import drives invoice creation; the invoices CSV will later update + // header fields but never replace these auto-created draft invoices. + const invoiceByNumber = new Map(); // invoice_number -> invoiceId + const ensureDraftInvoice = async ( invoiceNumber: string, + clientId: string | null, + caseId: string | null, ): Promise => { - const cacheKey = `${clientId}::${invoiceNumber}`; - const cached = invoiceByClientAndNumber.get(cacheKey); + const cached = invoiceByNumber.get(invoiceNumber); if (cached) return cached; - // First try existing invoice with this number for this client const { data: existing } = await supabase .from("invoices") .select("id") - .eq("client_id", clientId) .eq("invoice_number", invoiceNumber) .maybeSingle(); if (existing?.id) { - invoiceByClientAndNumber.set(cacheKey, existing.id); + invoiceByNumber.set(invoiceNumber, existing.id); return existing.id; } + if (!clientId) { + // invoices.client_id is NOT NULL — fall back to "Imported (no client)" + clientId = await ensureArchivedClient("Imported (no client)", "imported-no-client"); + if (!clientId) return null; + } const { data, error } = await supabase .from("invoices") .insert({ client_id: clientId, case_id: caseId, invoice_number: invoiceNumber, - status: "sent", + status: "draft", issue_date: new Date().toISOString().slice(0, 10), subtotal: 0, tax: 0, total: 0, - notes: "Auto-created from import to hold items linked to this invoice number.", + notes: "Auto-created from time/expense import — header will be updated when invoices CSV is uploaded.", created_by: user.id, } as any) .select("id") @@ -1062,7 +1066,7 @@ function ImportPage() { errors.push(`Auto-create invoice "${invoiceNumber}": ${error?.message ?? "unknown"}`); return null; } - invoiceByClientAndNumber.set(cacheKey, data.id); + invoiceByNumber.set(invoiceNumber, data.id); return data.id; }; @@ -1098,7 +1102,7 @@ function ImportPage() { delete row._caseext; delete row._casenum; delete row._casename; row.case_id = caseId; - // Look up client_id for the case to attach a placeholder voided invoice + // Look up client_id for the case so a draft invoice can be created if (!clientId) { clientId = caseClientCache.get(caseId) ?? null; if (!clientId) { @@ -1112,15 +1116,20 @@ function ImportPage() { } } - // Link to a real invoice ONLY when the source row provided an - // invoice number. Otherwise leave invoice_id null so the user can - // bill these items normally later. + // If the source row gave an invoice number, ensure a draft invoice + // exists and link this row. Mark billed=true so it's tracked as + // already on an invoice. Otherwise leave invoice_id null and + // billed=false — these are open charges awaiting invoicing. const invNumber = row._invoice ? String(row._invoice).trim() : ""; delete row._invoice; - if (invNumber && clientId && !row.invoice_id) { - const invId = await ensureNamedInvoice(clientId, caseId, invNumber); - if (invId) row.invoice_id = invId; + if (invNumber && !row.invoice_id) { + const invId = await ensureDraftInvoice(invNumber, clientId, caseId); + if (invId) { + row.invoice_id = invId; + row.billed = true; + } } + if (row.billed == null) row.billed = !!row.invoice_id; resolved.push(row); }