- 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>
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>
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>
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>
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>
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>
- teams.service.spec.ts: pick a checkpoint older than the adjustment's own
month so the exclusion test actually fails without the exclusion filter
- teams.service.ts: only negate fine/levy/fee amounts when positive,
matching TeamMembersService.recomputeBalance and Transaction.setBalance()
exactly, instead of negating unconditionally
- teams.service.ts: outstanding-history helper now returns a positive
value when players owe money, matching the house convention already
established by getOverview()'s team.outstanding
- overview.spec.ts: assert the balance chart's legend becomes visible
- teams.service.spec.ts: add coverage for inactive-player exclusion and
a positive-balance (prepaid credit) case
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a second, dashed line to the existing balance-history chart that
includes currently open player dues, so managers can see at a glance
how far the actual cash balance lags behind what has been pledged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Extract the month-by-month backward-walk algorithm (used for both
team-level cash balance and player-level debt history) into a single
shared private helper `reconstructBackward()`. This eliminates code
duplication while preserving behavior:
- Team-level balanceHistory: maps movements through signedFlowAmount(),
rounds each point, calls the shared helper
- Player-level balance history: maps transactions using type.id rule,
skips rounding (only rounds at final merge to avoid compounding
errors), calls the shared helper
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Reconstructs each active player's balance per month (same backward
technique as the existing cash-balance history) so the overview stats
endpoint can report what the team balance would be if all currently
open dues had already been paid.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a second historical line to the existing cash-balance chart showing
what the balance would be if all outstanding player dues were paid,
reconstructed per month the same way the existing balance line is.
Whole-branch review findings:
1. balanceHistory/monthlyFlow always returned 12 entries, even for a
brand-new team with zero transactions, so the frontend's empty-state
(gated on .length === 0) could never fire for a real "no movements yet"
team. Now returns empty arrays when there are no relevant movements at
all (not just none in the last 12 months, so a team with older-but-real
history still gets a flat chart). Also added the same defensive
`?? []` guard on players/transactions that getOverview already has, so a
team with no players/relations loaded doesn't throw.
2. GET :id/overview/stats had no team-membership check -- any logged-in
user (RoleEnum.user is the default role) could read any other team's
financial stats by iterating ids. Injected TeamAccessService into
TeamsService (already a sibling provider in TeamsModule, no module
wiring needed) and call assertMember(actorUserId, teamId) as the first
line of getOverviewStats, threaded from the controller via @Req(). Read
access only (assertMember, not assertManager), matching who can already
view the overview page. Sibling routes with the same pre-existing gap
were left untouched, per review scope.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>