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
@@ -42,11 +42,13 @@ public interface PaymentRepository extends JpaRepository<Payment, UUID> {
"WHERE p.clubId = :clubId AND p.memberId = :memberId AND p.status = 'PAID'")
Long sumPaidByMember(@Param("clubId") UUID clubId, @Param("memberId") UUID memberId);
// Overdue = PENDING payment whose billing period has already ended (periodTo in the past).
// Payment entity has no explicit dueDate column; periodTo serves as the implicit due date.
@Query("SELECT COUNT(p) FROM Payment p WHERE p.clubId = :clubId " +
"AND p.status = 'PENDING' AND p.dueDate < :now")
"AND p.status = 'PENDING' AND p.periodTo < :now")
long countOverdueByClubId(@Param("clubId") UUID clubId, @Param("now") LocalDate now);
@Query("SELECT COUNT(p) FROM Payment p WHERE p.clubId = :clubId " +
"AND p.status = 'PENDING' AND p.dueDate < :cutoff")
"AND p.status = 'PENDING' AND p.periodTo < :cutoff")
long countOverdueByClubIdAndDaysPast(@Param("clubId") UUID clubId, @Param("cutoff") LocalDate cutoff);
}