Commit Graph

10 Commits

Author SHA1 Message Date
ca2b1a11b5 Add parent claims: onboarding a roster of children with no parents on file
The migration path for a club arriving with a list of children from a
federation export and no parent records. Children import without logins, each
into a family of their own -- that shape *is* the "nobody is responsible for
this child" state, so there's no unclaimed flag to drift out of step with
reality, and a family drops off the worklist by itself the moment a parent
joins it. `family_role=child` with a blank `family_group` asks for that; any
other lone role is still a mistake in the file.

Verification is a human decision, deliberately. A parent submits a public form
with the child's name and date of birth as free text -- no search, no
autocomplete, and the same response whether or not the child was found, because
the page needs no login and anything that resolved the child would turn it into
a way to enumerate the club's children. An admin matches it from a queue
against a shortlist that only ever contains children with nobody on file, so
approving can never quietly re-parent a child who already has one.

The alternatives were worse. A claim code needs a delivery channel the club may
not have and is a bearer token besides. Matching on name plus birthday hands out
someone else's child to whoever guesses a birthday. The club is the only party
that actually knows its own families.

That form is also the registration: open self-registration is now closed
(shadowing account_signup rather than removing the route, so the URL name
allauth's templates reverse still resolves). The account is created on
approval, not on submission, so a public form can't fill the user table. An
approved parent lands as a guardian -- login and family link, no membership, no
fee -- gets a password-reset link, and a minimal "my family" page.

One bug worth recording: families_awaiting_a_parent first used
annotate(Count(..., filter=...)) over a queryset already filtered on the same
join, so Django reused that join for the counts and a parent with no
ClubMembership of their own -- exactly what a newly linked guardian is -- went
uncounted, leaving the family unclaimed forever. Exists subqueries avoid it. A
test pins both directions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 18:18:26 +02:00
309bd4d83e Add referee assignment/eligibility system, team bulk-add, member Groups, and referee management dashboard with PDF export
Builds the referee workflow end to end: club-defined RefereeLevel/RefereeProfile
eligibility tied to teams, EventReferee assignment (member or external, with
fee/km payment tracking), an admin dashboard with KPI tiles, date-grouped game
tiles and range filters, and a downloadable payment form PDF modeled on the
club's existing paper document (using Club.legal_name when set). Also lands
team roster bulk-add, member mass-upload with family linking, and the
members.Group model, developed alongside this work.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-09 21:39:19 +02:00
98b8002a04 Add billing-ending banner, events CRUD, RBIHF import, public API, team photos, and sponsors
A large batch of club-management features built up over one session:

- Club dashboard banner warning admins 1 month before billing ends
- Full Events/EventSeries CRUD (recurrence builder, occurrence lifecycle,
  per-team permissions), with match->game rename and game-specific fields
  (score, competition, live status, external game ID)
- Django-admin competition dropdown, gated per-club by feature flag
- Auto-import of RBIHF fixtures (scrape -> diff -> preview -> confirm),
  with location/opponent dropdowns suggested from existing club data
- Feature-flag-gated Shop/Forms nav sections, reusing the same flag
  machinery for the RBIHF import button
- Team roster now scoped to members active this season or next, sorted and
  grouped by position
- Club sport type (ice hockey / other), shown in the control panel's club
  subtitle
- Per-season team photo upload from the team page
- New public read-only API (Django Ninja) at /api/v1/: news, team rosters,
  upcoming/live/per-team games, and sponsors -- auto-documented via Swagger
  UI, CORS-enabled for a club's own external website
- Club sponsors: admin-only CRUD (logo, URL, active date window) plus a
  date-windowed, optionally randomized API endpoint
- Assorted fixes: NullBooleanField dropdown rendering, cross-club event
  validation timing, searchable-select chip placement, btn-neutral ->
  default button style sweep, calendar-month chart windows

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R1gj3J1QPfP38XWpnpbFpy
2026-08-06 17:36:04 +02:00
e6850232f0 Rework Teams/Positions access control and add full Locations/Opponents CRUD
Team managers/coaches now only see their own teams, can't create teams,
and can view (but not edit) positions -- admins keep full rights.
Locations and Opponents move from read-only stubs to full CRUD, gated
to admins and management-position staff, with a country dropdown
(django-countries) instead of free text. Also: the team list shows
player/staff counts, and deleting a news item's main photo promotes
another one instead of leaving the item without one.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R1gj3J1QPfP38XWpnpbFpy
2026-08-04 12:27:24 +02:00
a3e413904f Fold Roster and Staff into Teams, with a per-season switcher and real CRUD
One "Teams" nav entry now, matching how Family has none of its own and
is only reached through Members -- Roster/Staff were pure club-wide
list stubs with no way to add/remove anyone except Django admin.
Opening a team shows its roster and staff for whichever season is
selected (?season=, defaulting to the current one, same pattern as the
Memberships page); adding/editing/removing either is gated to that
team's own manager or a club admin (TeamManagerRequiredMixin, defined
earlier but never actually wired up until now).
2026-08-03 22:08:35 +02:00
9a2a06180f Add edit/delete actions to the teams list
Admin-only, matching TeamCreateView/TeamUpdateView's existing gate.
Deleting cascades away the team's roster and staff assignments -- no
ProtectedError to catch, unlike a Member that orders/invoices can still
reference. Edit links to the detail page rather than the edit form
directly, same convention as the news and member lists.
2026-08-03 21:42:23 +02:00
2261f86596 Add edit/delete actions to the news list and detail pages
Both gated the same way editing already was: broad while a draft,
editor/admin-only once published. The list's Edit link goes to the
detail page rather than straight to the edit form, matching the
member list's convention.
2026-08-03 21:38:47 +02:00
3e0c63ec36 Add a news app: coach_manager authoring, team tagging, photos, editor release flow
News and NewsPhoto (team-tagged instead of categorised, one photo taggable
as main via a partial unique constraint), gated per club/services/access.py:
any current-season coach_manager, EDITOR, or ADMIN can draft and edit a news
item; only EDITOR/ADMIN can publish it, or edit it once it's live. Publishing
takes a date so it can be scheduled ahead of time rather than only right now.

Authoring/release only for now -- no member-facing reading page or public API
yet, the visibility field (internal/external/both) is there for when those land.
2026-08-03 18:46:37 +02:00
ce35348b31 Build out Positions CRUD and rework the Roles page
Positions had list/create/edit already stubbed as list-only; give it real
forms, with a check mirroring the management_position_implies_staff_position
constraint so a bad combination reads as a form error, not a 500. Roles now
groups by role (excluding the MEMBER everyone holds automatically, which was
just noise), grants via a modal instead of a separate page, and its member
picker is a small typeahead combobox instead of a long native <select>.
2026-08-03 18:45:28 +02:00
062da00bb9 Add the management app: club-facing UI + real fee-payment tracking
Gives clubs a self-service /manage/ area for members, families, teams,
roles, and season memberships, alongside real fee-payment tracking
(FeePayment, record_payment/mark_as_paid/remaining_balance) so a
membership's paid status reflects actual money received instead of a
single manually-set flag.
2026-08-03 17:01:15 +02:00