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>
Manual verification against real data (Task 3) found balanceHistory drifted
from team.balance for real teams, since team.balance carries historical
adjustments (e.g. from the removed legacy backend) that don't trace back to
the current payment/credit/expense rows. Forward-summing those rows from
zero could never be trusted to tie out.
Rewrite balanceHistory to anchor on team.balance (the authoritative current
value) and walk the movements backward, newest to oldest, undoing each one
to reconstruct earlier month-end balances. This guarantees the most recent
point equals team.balance by construction, and is mathematically identical
to the old forward sum for teams whose movements fully explain their
balance. monthlyFlow/topOutstanding are unaffected and left as-is.
Updated teams.service.spec.ts to use a fixture where team.balance
intentionally does not equal the sum of its own movements, so the tests
actually exercise the drift-handling behavior instead of a case where
forward-sum and backward-anchor happen to coincide.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The vi.mock('chart.js', ...) MockChart class was copy-pasted verbatim
between chart-canvas.spec.ts and overview.spec.ts. Extract it to
shared/chart-canvas/testing/mock-chart.ts and import it via
vi.hoisted(async () => import(...)) in each spec, since vi.mock's
factory is hoisted above regular imports and can't reference a
plain top-level import.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds three chart.js-backed KPI cards (Kassenstand-Verlauf, Einnahmen &
Ausgaben, Top-10 offene Beitraege) to the existing Uebersicht page,
consuming the new GET teams/:id/overview/stats endpoint via a new
TeamStatsApi service. Introduces a small reusable ChartCanvas shared
component that wraps the Chart.js instance lifecycle via @Input()/
ngOnChanges, following this codebase's existing input-decorator
convention rather than effect().
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds GET :id/overview/stats + TeamsService#getOverviewStats, aggregating
team-wallet and player payment transactions into a 12-month balanceHistory
(cumulative, carry-forward), monthlyFlow (income/expense), and topOutstanding
(top 10 active debtors) for the upcoming overview KPI charts. fine/levy/fee
and player-level credit are excluded, matching the "Ist-Kasse" cash-flow rule.
Replaces the unmodified NestJS-boilerplate placeholder specs for
TeamsService/TeamsController (which already failed at baseline) with real
tests using the team-access.service.spec.ts direct-construction convention.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Approved plan for the three overview charts (balance history, monthly
income/expense, top-10 outstanding players): new backend aggregation
endpoint plus a chart.js-based frontend integration on the existing
overview page.
Brainstormed with the user: three charts on the existing overview page
(balance history, monthly income/expense, top-10 outstanding players),
Chart.js as dependency-free charting lib, new backend aggregation
endpoint since none of the existing endpoints group transactions by
time or category.
Adds a catalog picker to the member-booking form in the cashbox (prefills
amount/note/type, stays editable) and a "Buchen" button on each penalty
catalog entry that jumps to the cashbox with that entry preselected via a
penaltyId query param. No backend changes — reuses the existing POST
/transactions flow, the catalog only supplies starting values.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Team managers (captain and above) can now deactivate/reactivate a player and
change their team-role from the player detail page. Deactivation zeroes the
open balance via an auditable adjustment transaction instead of overwriting
the balance field, and both actions are blocked if they would leave a team
without an active treasurer. Also hardens the existing PUT teams/:id/players
endpoint down to profile-only fields, fixing a typo bug and closing a gap
where any authenticated user could mutate a player's active/role/balance in
any team.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The Docker runtime image only ships dist/, but mail-config.service.ts
pointed at src/mail/mail-templates and nest-cli.json never copied the
.hbs files into dist/ either. This was silently masked before because
the return-before-sendMail bug meant the path was never touched; fixing
that bug now surfaces it as a hard crash on boot (readFileSync throwing
synchronously inside the MailerModule factory). Resolve the templates
dir from __dirname instead, which is correct in both dev (src/mail) and
the compiled image (dist/mail), and add the mail-templates .hbs files
to nest-cli.json's asset copy list so they actually land in dist/.