{% extends "controlpanel/base.html" %} {% load i18n lucide ui %} {% comment %} Platform billing: RosterChief charging the clubs (not clubs charging their members -- see billing/services). Two tables: every Plan (with its dated price history) and every Due currently owing across every club. Every action here already existed as a modal or a link before this restyle; nothing was added or removed, only reskinned to the industrial control-panel vocabulary (assets/controlpanel.css). {% endcomment %} {% block panel_title %}{% trans "Billing" %}{% endblock panel_title %} {% block breadcrumb %} billing | {{ plans|length }} plan{{ plans|length|pluralize }} | {{ owing|length }} club{{ owing|length|pluralize }} owing {% endblock breadcrumb %} {% block actions %} {% endblock actions %} {% block panel %} {% url 'controlpanel:plan_create' as plan_create_url %} {% trans "New plan" as new_plan_title %} {% trans "Create plan" as create_plan_label %} {% include "controlpanel/_modal_form.html" with modal_id="plan_create_modal" title=new_plan_title form=plan_form action_url=plan_create_url submit_label=create_plan_label submit_icon="plus" box_class="max-w-2xl" two_columns=True %} {# --- Plans ----------------------------------------------------------- #}
{% trans "Plans" %} {% comment %} Prices are dated, not edited. A rate change is a new row with a future active_from; every period already opened keeps the amount it was billed at, so raising the price cannot rewrite an invoice already sent. {% endcomment %} {% trans "rate changes apply from a future date — open periods keep the amount they were billed at" %}
{% for plan in plans %} {% comment %} Named for what each measures from, because that is the easy thing to get wrong: grace runs from the period START, not its end. {% endcomment %} {% empty %} {% endfor %}
{% trans "Plan" %} {% trans "Timing" %} {% trans "Clubs" %} {% trans "Prices" %}
{{ plan.name }} {% if plan.is_trial %}{% trans "Trial" %}{% endif %} {% if not plan.is_active %}{% trans "Retired" %}{% endif %}
{% if plan.description %}
{{ plan.description }}
{% endif %}
{% blocktrans count counter=plan.duration_months %}{{ counter }} month{% plural %}{{ counter }} months{% endblocktrans %}
{% blocktrans with days=plan.renewal_lead_days %}billed {{ days }}d before start{% endblocktrans %}
{% blocktrans with days=plan.grace_days %}grace {{ days }}d after start{% endblocktrans %}
{{ plan.club_count }} {% for price in plan.prices.all %}
€ {{ price.amount|floatformat:2 }} {% blocktrans with active_from=price.active_from|date:"Y-m-d" %}from {{ active_from }}{% endblocktrans %} {% if price.active_from > today %}{% trans "Scheduled" %}{% endif %}
{% empty %} {% trans "No price — cannot be billed" %} {% endfor %}
{% lucide "trash-2" size=13 %} {% trans "Delete" %}
{% trans "No plans yet." %}
{% comment %} Dialogs live outside the table: may only contain elements. {% endcomment %} {% for plan in plans %} {% url 'controlpanel:plan_price_create' plan.pk as plan_price_url %} {% blocktrans asvar plan_price_title with plan=plan.name %}New price — {{ plan }}{% endblocktrans %} {% trans "Add price" as add_price_label %} {% include "controlpanel/_modal_form.html" with modal_id=plan.pk|dom_id:"plan_price_modal" title=plan_price_title form=plan.price_form action_url=plan_price_url submit_label=add_price_label submit_icon="euro" %} {% url 'controlpanel:plan_update' plan.pk as plan_update_url %} {% blocktrans asvar plan_edit_title with plan=plan.name %}Edit {{ plan }}{% endblocktrans %} {% trans "Save" as save_label %} {% include "controlpanel/_modal_form.html" with modal_id=plan.pk|dom_id:"plan_edit_modal" title=plan_edit_title form=plan.edit_form action_url=plan_update_url submit_label=save_label submit_icon="check" box_class="max-w-2xl" two_columns=True %} {% endfor %} {# --- Owed -------------------------------------------------------------- #}
{% trans "Owed" %} {% blocktrans count counter=owing|length %}{{ counter }} period outstanding{% plural %}{{ counter }} periods outstanding{% endblocktrans %}
{% for due in owing %} {% empty %} {% endfor %}
{% trans "Club" %} {% trans "Period" %} {% trans "Balance" %} {% trans "Status" %}
{{ due.club.name }}
{{ due.plan.name }}
{{ due.period_start|date:"Y-m-d" }} → {{ due.period_end|date:"Y-m-d" }}
{% blocktrans with grace_until=due.grace_until|date:"Y-m-d" %}grace to {{ grace_until }}{% endblocktrans %}
€ {{ due.balance|floatformat:2 }} {% if due.grace_until < today %} {% lucide "triangle-alert" size=11 %} {% trans "Overdue" %} {% elif due.period_end < today %} {% lucide "hourglass" size=11 %} {% trans "In grace" %} {% else %} {{ due.get_status_display }} {% endif %}
{% lucide "file-down" size=13 %} {% trans "Invoice" %}
{% trans "Nothing outstanding." %}
{% comment %} Dialogs live outside the table: may only contain elements. {% endcomment %} {% for due in owing %} {% url 'controlpanel:due_pay' due.pk as due_pay_url %} {% blocktrans asvar due_pay_title with club=due.club.name %}Record payment — {{ club }}{% endblocktrans %} {% trans "Record payment" as record_payment_label %} {% include "controlpanel/_modal_form.html" with modal_id=due.pk|dom_id:"due_pay_modal" title=due_pay_title form=due.payment_form action_url=due_pay_url submit_label=record_payment_label submit_icon="banknote" %} {% endfor %} {% endblock panel %}