Commit Graph

121 Commits

Author SHA1 Message Date
Bastian Wagner
f1b4f7e5b4 feat: add admin log viewer, log retention cleanup, and manual job triggers
Global admins couldn't see the app's event log (no read endpoint or UI
existed for it) and had no way to clean up old entries or re-run a
scheduled job without touching the database or server directly.

Backend:
- LoggingService.findLogs() + admin-only LogsController (GET
  admin/logs) with level/event/date-range/search filtering and
  pagination, mirroring AdminUsersService.findPlayers().
- LogRetentionScheduler deletes log entries older than
  LOG_RETENTION_DAYS (default 365, via app.config.ts), following the
  existing @Cron scheduler pattern.
- Admin-only POST admin/run endpoints on CashboxExportController and
  RecurringTransactionsController that invoke the existing schedulers'
  public run methods on demand - both are safe to re-run since their
  "due" queries advance nextRunDate only after a successful run.

Frontend:
- New /logs page (global-admin gated, same pattern as /users): AG-Grid
  infinite-scroll table with level/event/date-range/search filters,
  plus buttons to trigger the two jobs now and see the result land in
  the grid immediately.
- LogsApi, and triggerRunNow() added to the existing CashboxExportApi
  and RecurringTransactionApi.
- Discoverability link from /users to /logs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 16:21:39 +02:00
Bastian Wagner
24c509c0d5 address code review: assert page count in multi-page test, document fixes
- Lock in the exact expected page count (4) for the 60+60-row pagination
  test, which previously only checked the buffer was non-trivial. This is
  the scenario most likely to expose a footer/pagination regression.
- Add short comments explaining the footerY height-bound fix and the
  pdfPageCount() regex's coupling to pdfkit's serialization format.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 13:33:48 +02:00
Bastian Wagner
4185efb83a fix: negate expense amounts and stop blank trailing pages in cashbox PDF export
Expenses were stored as positive amounts (DB convention) and buildRows()
never negated them, so they were added to the running budget total
instead of subtracted. Negate expense amounts for team-wallet
transactions, mirroring the existing signedFlowAmount() convention in
teams.service.ts.

Separately, addFooters() placed footer text inside the reserved bottom
margin without an explicit height option, which made pdfkit's
LineWrapper treat every footer draw as overflowing the page and call
continueOnNewPage() twice per page - inflating page counts 3x with
blank trailing pages. Bounding the footer text to its own small height
box prevents pdfkit's automatic pagination from firing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 13:26:45 +02:00
Bastian Wagner
cd9d7b165f pdf export 2026-08-04 13:11:14 +02:00
Bastian Wagner
6fceee5a07 feat: wire receivables into manual and recurring cashbox PDF export
Both the manual download endpoint and the recurring email subscription
now pass buildReceivableRows() output into buildPdf, so every PDF
report includes the Forderungen section regardless of how it was
generated.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 12:26:23 +02:00
Bastian Wagner
02a4d2e59d feat: redesign cashbox PDF report with styled tables and receivables section
The PDF export was an unformatted list of doc.text() lines and only
showed real cash movements (payment type). Rebuilds it as a proper
two-section report: a branded header band, a bordered/zebra-striped
table with colored amounts and bold running balance for cash
movements, and a second "Forderungen" section listing fine/levy/fee
entries created in the period with their own total. Tables paginate
across pages and every page gets a footer with page numbers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 12:20:33 +02:00
Bastian Wagner
7b499b361f feat: add buildReceivableRows for fine/levy/fee entries
Cashbox export previously only saw payment transactions. This adds
the query for fine/levy/fee entries (Forderungen) that the redesigned
PDF report will show in a separate section.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 10:22:59 +02:00
Bastian Wagner
0bad154971 fix: remove invalid default on CashboxExportSubscription.recipients
MySQL/MariaDB reject a DEFAULT value on TEXT-backed columns (TypeORM's
simple-array maps to TEXT), so table creation failed with
"BLOB, TEXT, GEOMETRY or JSON column 'recipients' can't have a default
value". The service always assigns recipients before saving, so no
DB-level default was ever needed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 09:56:39 +02:00
Bastian Wagner
da5998487a fix: address cashbox-export whole-branch review findings
- Reject dates that are not strict YYYY-MM-DD (was accepting full ISO
  datetimes, which silently produced empty exports instead of a 400)
  and reject from > to with a 400 before touching the team/DB.
- Emit the cashbox_export_download and cashbox_export_subscription_update
  audit log events that were declared but never fired, matching the
  audit trail every sibling feature already has.
- Restore full type checking on the pdfkit import via `import = require()`
  instead of an untyped require() with an eslint-disable.
- Tighten a cashbox.spec.ts assertion to check the exact dialog class
  instead of expect.anything(), so it can't pass with the wrong dialog
  wired to the Export button.
- Style and announce the export dialogs' error messages using this
  codebase's established error-message/role=alert pattern.
2026-08-04 09:20:44 +02:00
Bastian Wagner
ce0b500d7a fix: re-check canBook() inside export dialog methods (defense in depth)
openExportDialog()/openExportSubscriptionDialog() only guarded on teamId
truthiness, relying solely on the template @if for permission gating.
Every other permission-gated method in Cashbox (submitPlayerBooking,
reverseBooking) re-checks the permission internally too. Add the same
guard here, plus a test asserting direct invocation without booking
rights does not call dialog.open.
2026-08-04 08:59:49 +02:00
Bastian Wagner
1c214c5f06 feat: wire cashbox export and subscription dialogs into Cashbox page
Adds an Export button (opens CashboxExportDialog directly) plus a
secondary menu (opens CashboxExportSubscriptionDialog) to the journal
toolbar, gated by the existing canBook permission signal.
2026-08-04 08:53:05 +02:00
Bastian Wagner
148269da11 fix: add error handling to CashboxExportSubscriptionDialog 2026-08-04 08:39:10 +02:00
Bastian Wagner
db061a7ef7 feat: add CashboxExportSubscriptionDialog 2026-08-04 08:34:01 +02:00
Bastian Wagner
11b70b9337 feat: add error handling to CashboxExportDialog 2026-08-04 08:29:22 +02:00
Bastian Wagner
5c3ff52689 feat: add CashboxExportDialog 2026-08-04 08:24:57 +02:00
Bastian Wagner
a65c7b35b3 feat: add CashboxExportApi 2026-08-04 08:19:09 +02:00
Bastian Wagner
72fa1d4331 feat: add cashbox export model and FileDownloadService 2026-08-04 08:14:58 +02:00
Bastian Wagner
d628d5e4d7 fix: add LoggingModule import to CashboxExportModule 2026-08-04 08:10:04 +02:00
Bastian Wagner
57869d5fc1 feat: register CashboxExportModule 2026-08-04 08:03:29 +02:00
Bastian Wagner
eed8266da2 fix: add error handling for CashboxExportScheduler subscription processing
- Wrap runOne(subscription) in try/catch to ensure one subscription failure doesn't block remaining subscriptions
- Log failed subscriptions with new 'cashbox_export_subscription_run_fail' event
- Add new LOGEVENT type for subscription run failures
- Add test to verify second subscription processes even when first fails (continues processing independently)
- All 7 tests passing: 6 original + 1 new failure handling test

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 07:56:39 +02:00
Bastian Wagner
5779483b21 feat: add CashboxExportScheduler for recurring PDF mailing
- Implement CashboxExportScheduler with @Cron(EVERY_DAY_AT_4AM)
- Query due subscriptions (active=true, nextRunDate <= today)
- For each subscription: fetch team, build PDF, send email, advance nextRunDate
- Support monthly/quarterly/yearly intervals via INTERVAL_MONTHS map
- Add cashbox_export_subscription_run to LOGEVENT type for logging
- All 6 tests passing: empty state, monthly/quarterly/yearly periods, multiple subscriptions

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 07:49:58 +02:00
Bastian Wagner
196898b993 feat: add MailService.cashboxExport and email template 2026-08-04 07:44:12 +02:00
Bastian Wagner
42e2bbc4ff feat: add cashbox export subscription endpoints
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 07:39:20 +02:00
Bastian Wagner
a11eb73ce9 feat: add CashboxExportSubscription entity and service 2026-08-04 07:28:01 +02:00
Bastian Wagner
009aae1f2b feat: add cashbox export download endpoint 2026-08-03 21:37:14 +02:00
Bastian Wagner
18df224386 test: enforce permission check ordering in CashboxExportService 2026-08-03 21:33:36 +02:00
Bastian Wagner
396dc29cf5 feat: add CashboxExportService.exportForUser 2026-08-03 21:30:22 +02:00
Bastian Wagner
f8857f71e1 feat: add buildPdf for cashbox export
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 21:20:57 +02:00
Bastian Wagner
0b408f7d73 fix: use symmetric rounding for negative amounts in formatGermanAmount 2026-08-03 21:16:05 +02:00
Bastian Wagner
c6df6a6d38 fix: handle negative-zero and half-cent rounding in formatGermanAmount 2026-08-03 21:12:27 +02:00
Bastian Wagner
cab04c5869 feat: add buildCsv for cashbox export 2026-08-03 21:08:03 +02:00
Bastian Wagner
cf7c3efb0f fix: add null type guards to buildRows to prevent crashes on missing types 2026-08-03 21:02:52 +02:00
Bastian Wagner
a9df62a249 feat: add buildRows for cashbox export row filtering 2026-08-03 20:57:36 +02:00
Bastian Wagner
d8883d4687 chore: add pdfkit for cashbox PDF export 2026-08-03 20:51:48 +02:00
Bastian Wagner
15d5f1d4e4 docs: add implementation plan for cashbox export feature
Task-by-task TDD plan covering manual CSV/PDF export, the recurring
PDF-mailing subscription, and the frontend wiring, grounded in the
existing recurring-transactions module and mail service as precedent.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 20:46:42 +02:00
Bastian Wagner
b8bcf329c5 docs: add design spec for cashbox export + recurring PDF mailing
Covers on-demand CSV/PDF export of real cash-affecting transactions
and an optional per-team recurring PDF mailing to arbitrary email
addresses, following the same brainstorming process used for
recurring transactions.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 20:35:19 +02:00
Bastian Wagner
9e55c0549b feat: add recurring transactions (Wiederkehrende Buchungen)
Lets treasurers/captains/coaches define recurring fee/levy dues that
are automatically booked for all active players on a monthly,
quarterly, or yearly schedule via a daily cron job, instead of having
to book them manually every cycle.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 20:20:57 +02:00
Bastian Wagner
6531f2553f fix: wrap team creation in a transaction, add more-menu entry point, fix lint
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 17:27:23 +02:00
Bastian Wagner
b03198baa6 feat: wire create-team dialog into team-select 2026-08-03 16:19:05 +02:00
Bastian Wagner
bb6b045a2a feat: add CreateTeamDialog component 2026-08-03 16:04:31 +02:00
Bastian Wagner
a05ffd0c7f feat: add MyTeamsStore.refresh for forced reloads 2026-08-03 15:56:43 +02:00
Bastian Wagner
886e4e6941 feat: add TeamsApi.createTeam 2026-08-03 15:51:38 +02:00
Bastian Wagner
c1478f07f1 feat: make team creator a captain of the new team 2026-08-03 15:44:12 +02:00
Bastian Wagner
92eacbc5bb feat: allow any logged-in user to create a team 2026-08-03 15:38:43 +02:00
Bastian Wagner
09da67b8ef Fix Mobile Layout 2026-08-03 15:25:37 +02:00
Bastian Wagner
145ebb924a docs: add implementation plan for team creation via UI
Task-by-task plan covering the loosened create-team role guard,
auto-captain membership on creation, the new TeamsApi/MyTeamsStore
methods, the CreateTeamDialog component, and wiring it into
team-select.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 15:14:47 +02:00
Bastian Wagner
560fcfc11a docs: add design spec for team creation via UI
Brainstormed with the user: any logged-in user should be able to
self-service create a team and becomes its captain, via a dialog on
team-select. Team deletion/archiving is scoped out as a separate
follow-up feature.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 15:03:59 +02:00
Bastian Wagner
84bab04523 fix export type 2026-08-03 15:01:49 +02:00
Bastian Wagner
b21641f37a ag grid 2026-08-03 14:49:35 +02:00
Bastian Wagner
71d76725a4 euro symbol 2026-08-03 11:40:42 +02:00