Implements BILLING.md. The architecture was sound -- snapshot-on-Due,
dated prices, asymmetric dry-run commands are all kept -- so this
fixes the three hardcoded assumptions rather than rewriting.
The real defect: grace ran from period_END, so an annual club used
the whole unpaid year plus 45 days (~410 days) before anything
switched it off. Grace now runs from the period START, and every
clock is per-plan.
- Tier -> Plan (+ TierPrice -> PlanPrice, and every FK). Migration
0004 is hand-written: run non-interactively, makemigrations emits
DeleteModel+CreateModel and drops every price, subscription and
due. Its two RemoveConstraints must come first, or SQLite's
table-rebuild tries to render a constraint over a just-renamed
column. Verified by round-tripping real rows through it.
- Plan gains duration_months / renewal_lead_days / grace_days /
is_trial, with CheckConstraints and a matching clean() so the form
reports an impossible plan instead of 500ing on IntegrityError.
- Existing dues keep their stored grace_until. Re-deriving it would
put the date in the past for every open annual period and archive
the entire paying customer base on the next --commit run.
- Trials take their length from the trial plan's own duration_months;
start_trial() loses its trial_months argument.
- New BillingNotice service drives a club-facing warning: every level
on the dashboard, and on every management page once urgent.
- send_billing_reminders emails club admins, once per escalation
level so a daily cron is not a daily email. SMTP settings are
env-driven and provider-agnostic; the backend defaults to console.
- Paying does not auto-restore an archived club -- the control panel
surfaces a Reactivate prompt instead, since a club can also be
archived by hand.
Team pages now show, for the selected season: overall attendance rate,
best/worst attenders, players who missed the last 2 practices, and
no-shows (an affirmative RSVP checked in as absent). No-shows need a
real distinction the RSVP status alone can't make, so Attendance gains
a separate showed_up tri-state field, usable today via Django admin --
a full check-in screen is future work for the coaches app.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R1gj3J1QPfP38XWpnpbFpy
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.
The clubmanager.app domain was taken, so the platform is now RosterChief
(rosterchief.app). Renames the Django project package clubmanager/ ->
rosterchief/ (git tracks it as a move, so history follows), every
`from rosterchief.base import ...`, the settings/wsgi/asgi module paths,
env vars (ROSTERCHIEF_BASE_DOMAIN / ROSTERCHIEF_RP_NAME), the MFA adapter
(RosterChiefMFAAdapter), brand text, and the docs.
Two things were deliberately NOT swept:
- club.models.ClubManager stays: it is the Django manager *for Club*, not the
brand. A blind rename would have silently broken it.
- Migrations are untouched (history is not rewritten). The only reference was a
cosmetic help_text, so a normal AlterField migration carries the new domain.
Note the WebAuthn RP ID is the base domain, so moving to rosterchief.app
cryptographically invalidates any passkey enrolled under the old one; they
cannot be migrated and must be re-enrolled. Nothing is in production, so the
real cost is zero.
Add django-lucide (from bsiebens/lucide) for icons: the theme toggle now swaps
sun/moon against the effective theme, and the control panel gets icons on its
tabs, actions and stat groups. Its classifiers stop at Django 5.0, but that is
stale metadata — verified rendering on Django 6 / Python 3.14.
Also add formbuilder, shop and controlpanel to ruff's known-first-party list,
which had drifted behind the apps that landed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the ad-hoc manual order discount with a per-club catalogue of
named OrderDiscountType presets snapshotted onto AppliedDiscount rows,
with stacking, retirement, and an optional value override. Regenerate
ARCHITECTURE.pdf.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Finish relocating Member/Family/FamilyMembership from authentication into
a dedicated members app, and revert the half-applied move of member names
onto the global User.
- members: add first_name/last_name back to Member (the whole codebase —
tests, CSV importer, club app, admin — assumes them, and login-less
children in families need a name); restore local ordering/index and the
member__last_name lookups in Family.__str__ and FamilyMembership.
- authentication: drop first_name/last_name from User; get_full_name/
get_short_name delegate to the linked member, else fall back to email.
- migrations: create members.0001_initial, repoint club.ClubMembership FK
(club.0005), delete the models from authentication (0004, rewritten to
plain DeleteModel ops in child-first order to avoid a SQLite table-remake
crash).
- fix stale imports across apps (authentication/club tests, CSV importer)
and missing imports/URLs in members tests; add missing _ import in
members/admin.py; export MemberCsvImporter from members.services.
Full suite green (64 tests), ruff clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Introduce a comprehensive architecture document outlining the app's domain model, design principles, and shared conventions.
- Establish the foundation for multi-tenancy and tenant scoping via `Club` as the tenant root and `ClubScopedModel`.
- Detail the decomposition of the app into planned sub-apps and their responsibilities.
- Define model structure, relationships, and access control mechanisms (RBAC) through `ClubRole` and service-layer authorization.
- Provide a roadmap for tenant-aware features: seasons, rosters, events, shop, and dynamic forms.