Fixed time-entry upsert fail

X-Lovable-Edit-ID: edt-24a83a91-fbc8-47a1-9a9d-3ae4fd67c4c9
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-04-19 00:27:58 +00:00
co-authored by renee-png
+59 -1
View File
@@ -695,6 +695,16 @@ function ImportPage() {
bumpSkip("client not found in existing clients (import companies first)");
} else if ((cfg.table === "cases" || cfg.table === "invoices") && !row.client_id) {
bumpSkip("client not found");
} else if (cfg.table === "time_entries" && !row.work_date) {
bumpSkip("missing or invalid work date");
} else if (cfg.table === "time_entries" && !row.case_id && !row._caseext && !row._casenum && !row._casename) {
bumpSkip("missing case reference");
} else if (cfg.table === "time_entries" && (row._caseext || row._casenum || row._casename) && !row.case_id) {
bumpSkip("case not found (import cases first or match case title/number)");
} else if (cfg.table === "time_entries" && (row.hours == null || row.hours <= 0) && row._time == null) {
bumpSkip("missing or invalid hours");
} else if (cfg.table === "time_entries" && !row.description && !row._activity) {
bumpSkip("missing description/activity");
} else {
bumpSkip("rejected by transform");
}
@@ -736,7 +746,55 @@ function ImportPage() {
const withConflict = chunk.filter((r) => r[cfg.conflict]);
const withoutConflict = chunk.filter((r) => !r[cfg.conflict]);
if (withConflict.length > 0) {
if (cfg.table === "time_entries" && withConflict.length > 0) {
const externalIds = withConflict.map((r) => String(r[cfg.conflict]));
const { data: existing, error: lookupError } = await supabase
.from("time_entries")
.select("id, external_id")
.in("external_id", externalIds);
if (lookupError) {
errors.push(`Batch ${i / chunkSize + 1} (lookup): ${lookupError.message}`);
} else {
const existingByExternalId = new Map(
(existing ?? []).map((entry: { id: string; external_id: string | null }) => [entry.external_id, entry.id]),
);
const toUpdate: Array<Row & { id: string }> = withConflict
.filter((r) => existingByExternalId.has(String(r.external_id)))
.map((r) => ({ ...r, id: existingByExternalId.get(String(r.external_id)) as string }));
const toInsert = withConflict.filter((r) => !existingByExternalId.has(String(r.external_id)));
if (toUpdate.length > 0) {
const updateResults = await Promise.all(
toUpdate.map(async (entry) => {
const { id, ...payload } = entry;
return supabase.from("time_entries").update(payload as any).eq("id", id).select("*").single();
}),
);
for (const result of updateResults) {
if (result.error) {
errors.push(`Batch ${i / chunkSize + 1} (update): ${result.error.message}`);
} else if (result.data) {
inserted += 1;
inserted_rows.push(result.data);
}
}
}
if (toInsert.length > 0) {
const { data, error } = await supabase
.from("time_entries")
.insert(toInsert as any)
.select("*");
if (error) {
errors.push(`Batch ${i / chunkSize + 1} (insert): ${error.message}`);
} else if (data) {
inserted += data.length;
inserted_rows.push(...data);
}
}
}
} else if (withConflict.length > 0) {
const { data, error } = await supabase
.from(cfg.table as any)
.upsert(withConflict as any, { onConflict: cfg.conflict, ignoreDuplicates: false })