From 6b537bb5f2479bf3fd68fe15dc473485ba2636af 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 18:31:24 +0000 Subject: [PATCH 1/8] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/settings.import.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/routes/settings.import.tsx b/src/routes/settings.import.tsx index 49a7bc3..4f8b8e0 100644 --- a/src/routes/settings.import.tsx +++ b/src/routes/settings.import.tsx @@ -58,6 +58,8 @@ interface ImportCtx { caseByTitle: Map; userByName: Map; userByEmail: Map; + // Set of case ids considered "active" (not closed, not archived) + activeCaseIds: Set; // Optional fallback client id (e.g. for address-only locations CSV) defaultClientId?: string | null; // Selected contact group (for unified contacts importer) From bb41e58c526bcae4c898866ad96c69969cea7c65 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 18:31:36 +0000 Subject: [PATCH 2/8] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/settings.import.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/routes/settings.import.tsx b/src/routes/settings.import.tsx index 4f8b8e0..6f53c5b 100644 --- a/src/routes/settings.import.tsx +++ b/src/routes/settings.import.tsx @@ -666,6 +666,7 @@ function ImportPage() { caseByTitle: new Map(), userByName: new Map(), userByEmail: new Map(), + activeCaseIds: new Set(), defaultClientId: null, defaultContactType: null, }), [user?.id]); From 55b8eb487469c9e0a2a45fa3544a67a0495c933e 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 18:31:46 +0000 Subject: [PATCH 3/8] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/settings.import.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/routes/settings.import.tsx b/src/routes/settings.import.tsx index 6f53c5b..cb7fe44 100644 --- a/src/routes/settings.import.tsx +++ b/src/routes/settings.import.tsx @@ -740,14 +740,19 @@ function ImportPage() { ctx.caseByExt.clear(); ctx.caseByNumber.clear(); ctx.caseByTitle.clear(); - const cases = await fetchAllPaginated<{ id: string; case_number: string | null; title: string | null; external_id: string | null }>( + ctx.activeCaseIds.clear(); + const cases = await fetchAllPaginated<{ id: string; case_number: string | null; title: string | null; external_id: string | null; status: string | null; archived_at: string | null }>( "cases", - "id, case_number, title, external_id", + "id, case_number, title, external_id, status, archived_at", ); + const CLOSED_STATUSES = new Set(["closed", "closed_won", "closed_lost"]); for (const r of cases) { if (r.external_id) ctx.caseByExt.set(r.external_id, r.id); if (r.case_number) ctx.caseByNumber.set(r.case_number, r.id); if (r.title) ctx.caseByTitle.set(r.title.toLowerCase().trim(), r.id); + if (!r.archived_at && !CLOSED_STATUSES.has(String(r.status ?? "").toLowerCase())) { + ctx.activeCaseIds.add(r.id); + } } ctx.userByName.clear(); ctx.userByEmail.clear(); From 12f6269bdde56e0678a548332d621ecb7cc63193 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 18:31:57 +0000 Subject: [PATCH 4/8] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/settings.import.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/routes/settings.import.tsx b/src/routes/settings.import.tsx index cb7fe44..699017f 100644 --- a/src/routes/settings.import.tsx +++ b/src/routes/settings.import.tsx @@ -446,8 +446,11 @@ const IMPORTERS: ImporterConfig[] = [ (r._casenum && ctx.caseByNumber.get(String(r._casenum))) || (r._casename && ctx.caseByTitle.get(String(r._casename).toLowerCase().trim())) || null; - // NOTE: do NOT delete _caseext/_casenum/_casename here — async step needs them. - // NOTE: do NOT bail on missing case — async step will create one. + // Only accept active (non-archived, non-closed) cases. + if (r.case_id && !ctx.activeCaseIds.has(r.case_id)) { + r.case_id = null; + } + // NOTE: do NOT delete _caseext/_casenum/_casename here — pre-insert step needs them for skip reporting. // Resolve hours from "Time" (e.g. "1.5", "1:30", "01:30:00") if needed if (r.hours == null && r._time != null) { From 0258aeafff8f7c61bf6bb4c9ca788cd8e847d0d5 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 18:32:06 +0000 Subject: [PATCH 5/8] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/settings.import.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/routes/settings.import.tsx b/src/routes/settings.import.tsx index 699017f..c741bb0 100644 --- a/src/routes/settings.import.tsx +++ b/src/routes/settings.import.tsx @@ -538,7 +538,11 @@ const IMPORTERS: ImporterConfig[] = [ (r._casenum && ctx.caseByNumber.get(String(r._casenum))) || (r._casename && ctx.caseByTitle.get(String(r._casename).toLowerCase().trim())) || null; - // Keep hints; async pre-insert step may create an archived case. + // Only accept active (non-archived, non-closed) cases. + if (r.case_id && !ctx.activeCaseIds.has(r.case_id)) { + r.case_id = null; + } + // Keep hints; pre-insert step decides whether to skip. if (r.amount == null) return null; r.user_id = ctx.userId; return r; From 5268631f67abf8e2e363ca06fab1dca0d3eee737 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 18:32:21 +0000 Subject: [PATCH 6/8] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/settings.import.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/routes/settings.import.tsx b/src/routes/settings.import.tsx index c741bb0..f02332c 100644 --- a/src/routes/settings.import.tsx +++ b/src/routes/settings.import.tsx @@ -1024,6 +1024,13 @@ function ImportPage() { let clientId: string | null = null; if (!caseId) { + // For time entries / expenses, never auto-create cases. + // Skip silently and report a count by reason. + if (cfg.key === "time_entries" || cfg.key === "expenses") { + const hint = row._casename || row._casenum || row._caseext; + bumpSkip(hint ? "no matching active case" : "missing case reference"); + continue; + } // Build display strings from hints const extId = row._caseext ? String(row._caseext) : null; const caseNum = row._casenum ? String(row._casenum).trim() : null; From 059b3e053c42016ba8242982582d7adb855fb6ed 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 18:32:31 +0000 Subject: [PATCH 7/8] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/settings.import.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/settings.import.tsx b/src/routes/settings.import.tsx index f02332c..4df4bfc 100644 --- a/src/routes/settings.import.tsx +++ b/src/routes/settings.import.tsx @@ -416,7 +416,7 @@ const IMPORTERS: ImporterConfig[] = [ { key: "time_entries", label: "Time entries → Time", - description: "Expected columns: Date, Case, Time, Rate, Flat rate, Total, Description, User, Case Name, Invoice, Nonbillable. Missing clients/cases are auto-created as archived. Rows with an Invoice number are linked to (or create) an invoice with that number; rows without an Invoice number stay unbilled.", + description: "Expected columns: Date, Case, Time, Rate, Flat rate, Total, Description, User, Case Name, Invoice, Nonbillable. Only rows matching an ACTIVE (non-archived, non-closed) case by case name/number are imported. Rows referencing missing, archived, or closed cases are skipped silently with a count.", table: "time_entries", conflict: "external_id", required: ["case_id", "description", "hours"], From 3f6a4c4729b3c995b8d9b1d2f8eaa94e18022d6e 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 18:32:37 +0000 Subject: [PATCH 8/8] Changes Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com> --- src/routes/settings.import.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/settings.import.tsx b/src/routes/settings.import.tsx index 4df4bfc..acea8bb 100644 --- a/src/routes/settings.import.tsx +++ b/src/routes/settings.import.tsx @@ -515,7 +515,7 @@ const IMPORTERS: ImporterConfig[] = [ { key: "expenses", label: "Expenses → Expenses", - description: "Case-related expenses. Missing clients/cases are auto-created as archived. Rows with an Invoice number are linked to (or create) an invoice with that number; rows without an Invoice number stay unbilled.", + description: "Case-related expenses. Only rows matching an ACTIVE (non-archived, non-closed) case are imported. Rows referencing missing, archived, or closed cases are skipped silently with a count.", table: "expenses", conflict: "external_id", required: ["case_id", "description", "amount"],