From 0e4d19e8bcd765273859ef243a5ddcdb34cafb79 Mon Sep 17 00:00:00 2001 From: CCS Admin Date: Tue, 24 Feb 2026 09:01:42 +0000 Subject: [PATCH] fix: use savepoints for row-by-row error isolation in Excel import Prevents a single duplicate fall_id from rolling back the entire import session. Each row insert now uses db.begin_nested() so constraint violations are isolated to the offending row. Co-Authored-By: Claude Opus 4.6 --- backend/app/services/excel_import.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/backend/app/services/excel_import.py b/backend/app/services/excel_import.py index f674758..6a646d8 100644 --- a/backend/app/services/excel_import.py +++ b/backend/app/services/excel_import.py @@ -494,12 +494,22 @@ def import_abrechnung_sheet( abrechnung_datum=abrechnung_datum, import_source=f"Abrechnung_DAK.xlsx:{sheet_name}", ) - db.add(case) - imported += 1 - - # Flush in batches of 100 to catch constraint violations early - if imported % 100 == 0: + # Use savepoint so a single row failure doesn't break the session + nested = db.begin_nested() + try: + db.add(case) db.flush() + nested.commit() + imported += 1 + except Exception as flush_err: + nested.rollback() + errors.append(f"Row {row_num} ({nachname}): {flush_err}") + logger.warning( + "Import error in sheet '%s' row %d: %s", + sheet_name, row_num, flush_err, + ) + skipped += 1 + continue except Exception as e: nachname_display = _str_or_none(_get(row, col_map, "nachname")) or "?" @@ -509,10 +519,6 @@ def import_abrechnung_sheet( sheet_name, row_num, e, ) - # Final flush - if imported > 0: - db.flush() - logger.info( "Sheet '%s': %d imported, %d skipped, %d errors", sheet_name, imported, skipped, len(errors),