diff --git a/controlpanel/templates/templatetags/field.html b/controlpanel/templates/templatetags/field.html index 67dc2a3..24679be 100644 --- a/controlpanel/templates/templatetags/field.html +++ b/controlpanel/templates/templatetags/field.html @@ -24,6 +24,7 @@ name="{{ field.html_name }}" value="{% if field.widget_type == "date" or field.widget_type == "datetime" %}{% if field.value|date:"c"|default:"" != "" %}{{ field.value|date:"c"|default:"" }}{% else %}{{ field.value|default:"" }}{% endif %}{% else %}{% if field.value == None %}{{ field.value|default:"" }}{% else %}{{ field.value }}{% endif %}{% endif %}" {% if show_placeholder %}placeholder="{{ field.label|capfirst }}"{% endif %} + {% for attr, value in field.field.widget.attrs.items %}{% if attr != "type" %}{{ attr }}="{{ value }}" {% endif %}{% endfor %} /> {% elif field_type == "checkbox" %} diff --git a/controlpanel/tests.py b/controlpanel/tests.py index 243f385..e27549d 100644 --- a/controlpanel/tests.py +++ b/controlpanel/tests.py @@ -11,6 +11,7 @@ from django.contrib.auth import get_user_model from django.contrib.messages.storage.base import Message from django.contrib.messages.storage.fallback import FallbackStorage from django.core.cache import cache +from django.template.loader import render_to_string from django.test import RequestFactory, TestCase, override_settings from django.urls import reverse from django.utils import timezone @@ -50,7 +51,7 @@ from .services.statistics import ( teams_without_a_manager, unrostered_members, ) -from .templatetags.ui import as_alert, daisy, excluded, field_icon +from .templatetags.ui import as_alert, daisy, excluded, field_icon, form_field User = get_user_model() Flag = get_waffle_flag_model() @@ -691,6 +692,20 @@ class FieldRenderingTests(TestCase): self.assertIn("input-error", rendered) + def test_the_form_field_tag_carries_widget_attrs_onto_the_input(self): + # Regression: the "input" branch of templatetags/field.html rendered type/ + # class/name/value/placeholder only, silently dropping every other widget + # attr (step, min, ...) -- e.g. NumberInput(attrs={"step": "any"}) never + # reached the page, so a decimal-only field like a per-km rate couldn't be + # typed at all. See form_field in controlpanel/templatetags/ui.py. + bound_field = self.field(forms.DecimalField(widget=forms.NumberInput(attrs={"step": "any", "min": "0"})), "rate") + + html = render_to_string("templatetags/field.html", form_field(bound_field)) + + self.assertIn('step="any"', html) + self.assertIn('min="0"', html) + self.assertEqual(html.count('type="number"'), 1) + class LoginFormRenderingTests(TestCase): def setUp(self): diff --git a/management/templates/management/event_referee_form_pdf.html b/management/templates/management/event_referee_form_pdf.html index 26d98fb..f321138 100644 --- a/management/templates/management/event_referee_form_pdf.html +++ b/management/templates/management/event_referee_form_pdf.html @@ -1,14 +1,13 @@ {% load i18n %} {% comment %} - Rendered by WeasyPrint -- modeled directly on the club's existing paper - referee payment form ("Leeg Document RefereesCompetitie25-26.pdf"): a - 3-line header (club name/address), match details, referee names, a - payment sentence, a fee+km breakdown per referee, and blank signature - lines. Header uses club.official_name (legal_name if set, else name); - the payment sentence uses the plain club name -- the original document - does the same, using a longer legal form up top and a shorter one in the - body text. + Rendered by WeasyPrint -- modeled on the club's existing paper referee + payment form ("Leeg Document RefereesCompetitie25-26.pdf"): club header, + match details, referee names, a payment sentence, a fee+km breakdown per + referee, and blank signature lines. Header uses club.official_name + (legal_name if set, else name); the payment sentence uses the plain club + name -- the original document does the same, using a longer legal form up + top and a shorter one in the body text. {% endcomment %} @@ -18,69 +17,135 @@
{% blocktrans with name=club.name %}{{ name }} pays the fee to the referee(s) for officiating the game mentioned above.{% endblocktrans %}
+{% blocktrans with name=club.name %}{{ name }} pays the fee to the referee(s) for officiating the game mentioned above.{% endblocktrans %}
| {% trans "Referee" %} | +{% trans "Fee" %} | +{% trans "Kilometers" %} | +{% trans "Rate" %} | +{% trans "Total" %} | +
|---|---|---|---|---|
| + {{ referee.display_name }} + {% if referee.is_external %}{% trans "External" %}{% endif %} + | +€{{ referee.fee|floatformat:2 }} | +{% if referee.km %}{{ referee.km }} km{% else %}—{% endif %} | +{% if referee.km %}€{{ referee.km_rate }}{% else %}—{% endif %} | +€{{ referee.total_payable|floatformat:2 }} | +
| {% trans "No referees assigned yet." %} | ||||
| {% trans "Total to pay" %} | +€{{ grand_total|floatformat:2 }} | +|||