diff --git a/src/routes/settings.import.tsx b/src/routes/settings.import.tsx index ca77f3e..3bf7d90 100644 --- a/src/routes/settings.import.tsx +++ b/src/routes/settings.import.tsx @@ -1206,17 +1206,49 @@ function ImportPage() { return data.id; }; + // Pre-load existing invoices by number so we can defer client resolution + // when the invoice already exists with a real client_id (don't clobber + // it with a placeholder). + const invNumbers = mapped.map((r) => String(r.invoice_number ?? "")).filter(Boolean); + const existingInvClient = new Map(); // invoice_number -> client_id + for (let k = 0; k < invNumbers.length; k += 500) { + const slice = invNumbers.slice(k, k + 500); + const { data } = await supabase + .from("invoices") + .select("invoice_number, client_id") + .in("invoice_number", slice); + for (const row of (data ?? []) as { invoice_number: string; client_id: string }[]) { + if (row.client_id) existingInvClient.set(row.invoice_number, row.client_id); + } + } + const resolved: Row[] = []; for (const row of mapped) { let clientId: string | null = (row.client_id as string | null) ?? null; if (!clientId) { const cExt = row._clientext ? String(row._clientext).trim() : null; const cName = row._clientname ? String(row._clientname).trim() : null; - const nameGuess = cName || cExt || "Imported (no client)"; - clientId = await ensureArchivedClient(nameGuess, cExt); - if (!clientId) { bumpSkip("auto-create client failed"); continue; } + // If this invoice already exists with a real client, inherit it + // instead of falling back to a placeholder — prevents wiping good + // client links during a re-import of the invoices CSV. + const invNum = row.invoice_number ? String(row.invoice_number) : ""; + const existingClient = invNum ? existingInvClient.get(invNum) : null; + if (existingClient) { + clientId = existingClient; + } else if (cName || cExt) { + // Only auto-create a placeholder when the CSV actually had a + // client hint we couldn't resolve. Pure-blank rows are left + // unset so the merge step skips client_id entirely. + const nameGuess = cName || cExt!; + clientId = await ensureArchivedClient(nameGuess, cExt); + if (!clientId) { bumpSkip("auto-create client failed"); continue; } + } else { + // No client info at all — leave client_id unset. The merge step + // will skip it (preserving any existing value); inserts will + // route through the catch-all placeholder below. + } } - row.client_id = clientId; + if (clientId) row.client_id = clientId; // If the invoice mentions a case but we don't have it, create an // archived placeholder case so the reference is preserved.