A Billing tab (tiers, their dated prices, and everything we are owed), a billing panel on each club (plan, periods, payment history, invoice), and the dues on the dashboard and the club tables. Every state change goes through the billing service, and a BillingError surfaces as a message rather than a 500 -- so "that period is waived", "no price in force", "already billed for that period" and a missing PDF library all explain themselves instead of crashing. The dashboard now separates the two pots of money that were previously one word. "Revenue per month" was CLUB SHOP revenue -- members paying their clubs, which is never ours -- sitting on our dashboard under a label that implied it was income. It is now "Platform dues per month" (what clubs paid us) with the club-shop series renamed club_revenue, and the club tables carry a Plan column and what each club owes us, annotated in the same single query. Rate changes are add-only in the UI as well as the model: the price form creates a dated row and never edits the last one, and a test asserts that raising the rate leaves an already-open period at the amount it was billed at. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
13 lines
700 B
HTML
13 lines
700 B
HTML
{% extends "controlpanel/base.html" %}
|
|
|
|
{% block heading %}{% if club.is_archived %}Reactivate{% else %}Open period{% endif %} — {{ club }}{% endblock heading %}
|
|
|
|
{% block subheading %}
|
|
<p class="text-sm opacity-70">Next period starts {{ next_start|date:"j M Y" }} unless you say otherwise.</p>
|
|
{% endblock subheading %}
|
|
|
|
{% block panel %}
|
|
{% url 'controlpanel:club_detail' club.pk as club_url %}
|
|
{% include "controlpanel/_billing_form.html" with cancel_url=club_url submit_label="Open period" submit_icon="calendar-plus" blurb="By default the period continues from the end of the last one, so a lapsed year is still owed. Pick a start date to forgive the gap." %}
|
|
{% endblock panel %}
|