feat(sprint10): Phase 4+5 — Frontend import wizard + integration testing

Phase 4 — Frontend Import Wizard:
- bank-import.ts service: types (BankImportSession, BankTransaction,
  CsvColumnMapping, ImportSessionStatus, MatchStatus) + 12 React Query hooks
  (sessions, transactions, mappings, upload/confirm/skip/assign/complete)
- /finance/import page: 4-step wizard (Upload -> Map -> Review -> Confirm)
  * Drag-and-drop upload with bank format auto-detect (MT940/CAMT.053/CSV)
  * CSV column mapping editor (saves as reusable mapping)
  * Review table with color-coded MATCHED/SUGGESTED/UNMATCHED/CONFIRMED rows,
    confidence % badges, member-assign Combobox, skip/confirm/bulk-confirm
  * Completion summary + import history table with resume action
- de.json + en.json: full bankImport.* namespace (steps, upload, map, review,
  complete, history, status, sessionStatus, actions, errors)
- Navigation: Finanzen converted to nested submenu (Uebersicht + Import)

Phase 5 — Integration Testing:
- docker compose down -v + up -d --build (clean rebuild)
- Playwright e2e/sprint10-system-test.spec.ts: verifies /finance/import
  unauthenticated -> /login?callbackUrl=%2Ffinance%2Fimport (PASS)
- Backend health + frontend route registration verified

Bugfix bundled (blocked backend startup):
- PaymentRepository: countOverdueByClubId* queries referenced non-existent
  Payment.dueDate column (regression from Sprint 9 Phase 6, commit 57f418f).
  Switched to Payment.periodTo (the implicit due date for billing periods).
This commit is contained in:
Patrick Plate
2026-06-15 18:33:40 +02:00
parent 5defe42d67
commit 8c969c610f
7 changed files with 1669 additions and 3 deletions
+102
View File
@@ -958,6 +958,108 @@
"noPayments": "Noch keine Zahlungen vorhanden"
}
},
"bankImport": {
"title": "Kontoauszug-Import",
"subtitle": "Bankauszüge hochladen und automatisch Mitgliedsbeiträgen zuordnen",
"default": "Standard",
"steps": {
"upload": "Hochladen",
"map": "Spalten zuordnen",
"review": "Treffer prüfen",
"complete": "Abschließen"
},
"upload": {
"title": "Kontoauszug hochladen",
"help": "Unterstützte Formate: MT940 (.sta, .mt940, .txt), CAMT.053 (.xml), CSV (.csv). Maximale Dateigröße: 10 MB.",
"dropzone": "Datei hierher ziehen oder klicken zum Auswählen",
"formats": "MT940 • CAMT.053 • CSV",
"useMapping": "Gespeicherte CSV-Vorlage verwenden",
"autoDetect": "Automatisch erkennen"
},
"map": {
"title": "CSV-Spalten zuordnen",
"help": "Geben Sie an, welche Spalte welche Information enthält (0-basiert). Vorlage kann für künftige Imports gespeichert werden.",
"templateName": "Vorlagenname",
"templateNamePlaceholder": "z.B. Sparkasse Köln Export",
"dateColumn": "Datums-Spalte",
"amountColumn": "Betrags-Spalte",
"referenceColumn": "Verwendungszweck-Spalte",
"counterpartyColumn": "Empfänger-Spalte",
"delimiter": "Trennzeichen",
"dateFormat": "Datumsformat",
"saveAsDefault": "Als Standard-Vorlage speichern"
},
"review": {
"title": "Treffer prüfen",
"transactions": "Transaktionen",
"resumed": "Fortgesetzt",
"progress": "Bearbeitungsfortschritt",
"sortByConfidence": "Sortieren: Konfidenz",
"sortByAmount": "Sortieren: Betrag",
"sortByDate": "Sortieren: Datum",
"autoMatchedReady": "automatische Treffer bereit zur Bestätigung",
"date": "Datum",
"counterparty": "Empfänger",
"reference": "Verwendungszweck",
"amount": "Betrag",
"matchStatus": "Status",
"actions": "Aktionen",
"searchMembers": "Mitglied suchen…",
"noMembersFound": "Keine Mitglieder gefunden",
"noTransactions": "Keine Transaktionen vorhanden",
"expenseSkipReason": "Ausgabe — kein Mitgliedsbeitrag"
},
"complete": {
"title": "Import abschließen",
"help": "Nach Versiegeln können keine Änderungen mehr vorgenommen werden (GoBD §147 AO).",
"totalTransactions": "Gesamt",
"confirmed": "Bestätigt",
"skipped": "Übersprungen",
"remaining": "Verbleibend",
"warning": "Verbleibende Transaktionen werden automatisch als übersprungen markiert.",
"sealedMessage": "Sitzung wurde versiegelt. Buchungen sind unveränderlich."
},
"history": {
"title": "Import-Historie",
"date": "Datum",
"file": "Datei",
"format": "Format",
"status": "Status",
"transactions": "Bestätigt",
"actions": "Aktionen"
},
"status": {
"MATCHED": "Übereinstimmung",
"SUGGESTED": "Vorschlag",
"UNMATCHED": "Kein Treffer",
"CONFIRMED": "Bestätigt",
"SKIPPED": "Übersprungen"
},
"sessionStatus": {
"PENDING": "Ausstehend",
"IN_REVIEW": "In Prüfung",
"COMPLETED": "Abgeschlossen",
"FAILED": "Fehlgeschlagen",
"CANCELLED": "Abgebrochen"
},
"actions": {
"remove": "Entfernen",
"uploadAndParse": "Hochladen & analysieren",
"saveAndContinue": "Speichern & weiter",
"skip": "Überspringen",
"skipExpense": "Als Ausgabe überspringen",
"confirm": "Bestätigen",
"confirmAll": "Alle bestätigen",
"assignMember": "Mitglied zuweisen",
"proceedToComplete": "Weiter zum Abschluss",
"sealSession": "Sitzung versiegeln",
"newImport": "Neuer Import",
"resume": "Fortsetzen"
},
"errors": {
"uploadFailed": "Upload fehlgeschlagen. Bitte erneut versuchen."
}
},
"documents": {
"title": "Dokumentenarchiv",
"description": "Vereinsdokumente verwalten und archivieren",
+111
View File
@@ -958,6 +958,117 @@
"noPayments": "No payments yet"
}
},
"bankImport": {
"title": "Bank Statement Import",
"subtitle": "Import bank statements, match transactions to members, and confirm fee payments",
"steps": {
"upload": "Upload",
"map": "Map Columns",
"review": "Review",
"complete": "Complete"
},
"upload": {
"title": "Upload bank statement",
"description": "Supported formats: MT940 (.sta, .mt940, .txt), CAMT.053 (.xml), CSV (.csv)",
"dropzone": "Drag a file here or click to select",
"selectFile": "Select file",
"format": "Format",
"mapping": "CSV Template (optional)",
"noMapping": "Auto-detect",
"upload": "Upload",
"uploading": "Uploading…"
},
"map": {
"title": "Map CSV columns",
"description": "Specify which columns contain which data",
"dateColumn": "Date column",
"amountColumn": "Amount column",
"referenceColumn": "Reference column",
"counterpartyColumn": "Counterparty column",
"delimiter": "Delimiter",
"dateFormat": "Date format",
"saveAsTemplate": "Save as default template",
"templateName": "Template name",
"continue": "Continue",
"saving": "Saving…"
},
"review": {
"title": "Review transactions",
"description": "Confirm matches or assign members manually",
"matched": "Matched",
"suggested": "Suggested",
"unmatched": "Unmatched",
"skipped": "Skipped",
"total": "Total",
"progress": "Review progress",
"confirmAll": "Confirm all matched",
"confirmingAll": "Confirming…",
"sortBy": "Sort by",
"sortConfidence": "Confidence",
"sortAmount": "Amount",
"sortDate": "Date",
"date": "Date",
"amount": "Amount",
"counterparty": "Counterparty",
"reference": "Reference",
"match": "Match",
"actions": "Actions",
"noTransactions": "No transactions found",
"resume": "Resume"
},
"complete": {
"title": "Complete session",
"description": "Seal the session — sealed sessions are immutable (GoBD §147 AO)",
"summary": "Summary",
"confirmed": "Confirmed",
"skipped": "Skipped",
"open": "Open",
"seal": "Seal session",
"sealing": "Sealing…",
"sealed": "Session sealed",
"sealedDescription": "This session is now immutable and audit-proof.",
"back": "Back to Finance"
},
"history": {
"title": "Import history",
"description": "Previous import sessions",
"noSessions": "No imports yet",
"filename": "Filename",
"uploaded": "Uploaded",
"status": "Status",
"resume": "Resume"
},
"status": {
"MATCHED": "Matched",
"SUGGESTED": "Suggested",
"UNMATCHED": "Unmatched",
"CONFIRMED": "Confirmed",
"SKIPPED": "Skipped"
},
"sessionStatus": {
"PENDING": "Pending",
"IN_REVIEW": "In Review",
"COMPLETED": "Completed",
"FAILED": "Failed",
"CANCELLED": "Cancelled"
},
"actions": {
"confirm": "Confirm",
"assign": "Assign",
"skip": "Skip",
"searchMember": "Search member…",
"noMembersFound": "No members found",
"cancel": "Cancel"
},
"errors": {
"uploadFailed": "Upload failed",
"noFile": "Please select a file",
"confirmFailed": "Confirm failed",
"assignFailed": "Assign failed",
"skipFailed": "Skip failed",
"completeFailed": "Complete failed"
}
},
"documents": {
"title": "Document Archive",
"description": "Manage and archive club documents",