Add a club legal address, used for invoices/referee forms ahead of the home ground
New Club.legal_address/legal_zip_code/legal_city, editable from the identity page (management:club_settings). Official document headers (the dues invoice, the referee payment form) previously borrowed the club's home Location for this -- conflating "where we play" with "our registered address", which aren't always the same place. New club.services.invoicing.resolve_document_address(club) picks the club's own legal address when set, falling back to the home Location exactly as before when it isn't, so nothing breaks for a club that hasn't set one yet. Location.is_home now means only what it always should have: telling a home game from an away one. Renamed the shared "home_location" template/context variable to "document_address" on both PDFs to match -- it was never accurate once a legal address could win instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
This commit is contained in:
@@ -0,0 +1,28 @@
|
|||||||
|
# Generated by Django 6.0.6 on 2026-08-21 15:45
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('club', '0029_duesinvoice'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='club',
|
||||||
|
name='legal_address',
|
||||||
|
field=models.CharField(blank=True, help_text="Street address for official documents (invoices, the referee payment form). Falls back to the home location's address when left blank -- set this when the club's registered address isn't where it actually plays.", max_length=255, verbose_name='legal address'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='club',
|
||||||
|
name='legal_city',
|
||||||
|
field=models.CharField(blank=True, max_length=255, verbose_name='legal city'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='club',
|
||||||
|
name='legal_zip_code',
|
||||||
|
field=models.CharField(blank=True, max_length=255, verbose_name='legal zip code'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -50,6 +50,15 @@ class Club(UUIDModel):
|
|||||||
)
|
)
|
||||||
website = models.URLField(_("website"), blank=True, help_text=_("The club's own site, if it has one -- shown alongside its RosterChief pages, not used for anything else yet."))
|
website = models.URLField(_("website"), blank=True, help_text=_("The club's own site, if it has one -- shown alongside its RosterChief pages, not used for anything else yet."))
|
||||||
|
|
||||||
|
legal_address = models.CharField(
|
||||||
|
_("legal address"),
|
||||||
|
max_length=255,
|
||||||
|
blank=True,
|
||||||
|
help_text=_("Street address for official documents (invoices, the referee payment form). Falls back to the home location's address when left blank -- set this when the club's registered address isn't where it actually plays."),
|
||||||
|
)
|
||||||
|
legal_zip_code = models.CharField(_("legal zip code"), max_length=255, blank=True)
|
||||||
|
legal_city = models.CharField(_("legal city"), max_length=255, blank=True)
|
||||||
|
|
||||||
logo = models.FileField(
|
logo = models.FileField(
|
||||||
_("logo"),
|
_("logo"),
|
||||||
upload_to=club_logo_path,
|
upload_to=club_logo_path,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ membership.fee_status -- never a flag duplicated here that could drift out of st
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.mail import EmailMultiAlternatives
|
from django.core.mail import EmailMultiAlternatives
|
||||||
@@ -158,11 +159,30 @@ def render_pdf(html: str) -> bytes:
|
|||||||
return HTML(string=html).write_pdf()
|
return HTML(string=html).write_pdf()
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_document_address(club):
|
||||||
|
"""The address to print on an official document header (a dues invoice,
|
||||||
|
the referee payment form) -- the club's own ``legal_address`` when set,
|
||||||
|
else its home ground (``events.models.Location``, ``is_home=True``), so
|
||||||
|
a club that hasn't set a legal address yet still gets *something* rather
|
||||||
|
than a blank header.
|
||||||
|
|
||||||
|
``Location.is_home`` itself is purely about telling a home game from an
|
||||||
|
away one -- this is the one place its address doubles as a stand-in for
|
||||||
|
an actual registered/mailing address, and only when the club hasn't set
|
||||||
|
one of its own. Returns an object exposing ``.address``/``.zip_code``/
|
||||||
|
``.city`` either way (a plain namespace for the legal-address branch, the
|
||||||
|
real ``Location`` for the fallback), or ``None`` when neither is set.
|
||||||
|
"""
|
||||||
|
if club.legal_address:
|
||||||
|
return SimpleNamespace(address=club.legal_address, zip_code=club.legal_zip_code, city=club.legal_city)
|
||||||
|
return Location.objects.filter(club=club, is_home=True).first()
|
||||||
|
|
||||||
|
|
||||||
def invoice_pdf(invoice: DuesInvoice) -> bytes:
|
def invoice_pdf(invoice: DuesInvoice) -> bytes:
|
||||||
# Same header convention as management/event_referee_form_pdf.html: the club's
|
# Same header convention as management/event_referee_form_pdf.html: the club's
|
||||||
# legal name (official_name falls back to the everyday name when unset) and its
|
# legal name (official_name falls back to the everyday name when unset) and its
|
||||||
# home location -- never an event-specific location, since a dues invoice isn't
|
# document address -- never an event-specific location, since a dues invoice
|
||||||
# tied to any one event.
|
# isn't tied to any one event.
|
||||||
home_location = Location.objects.filter(club=invoice.club, is_home=True).first()
|
document_address = resolve_document_address(invoice.club)
|
||||||
html = render_to_string("club/dues_invoice_pdf.html", {"club": invoice.club, "invoice": invoice, "membership": invoice.membership, "member": invoice.membership.member, "home_location": home_location})
|
html = render_to_string("club/dues_invoice_pdf.html", {"club": invoice.club, "invoice": invoice, "membership": invoice.membership, "member": invoice.membership.member, "document_address": document_address})
|
||||||
return render_pdf(html)
|
return render_pdf(html)
|
||||||
|
|||||||
@@ -43,9 +43,9 @@
|
|||||||
<div class="header">
|
<div class="header">
|
||||||
<div>
|
<div>
|
||||||
<h1>{{ club.official_name }}</h1>
|
<h1>{{ club.official_name }}</h1>
|
||||||
{% if home_location %}
|
{% if document_address %}
|
||||||
<div class="muted">{{ home_location.address }}</div>
|
<div class="muted">{{ document_address.address }}</div>
|
||||||
<div class="muted">{{ home_location.zip_code }} {{ home_location.city }}</div>
|
<div class="muted">{{ document_address.zip_code }} {{ document_address.city }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if club.contact_email %}<div class="muted">{{ club.contact_email }}</div>{% endif %}
|
{% if club.contact_email %}<div class="muted">{{ club.contact_email }}</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ from .services.access import (
|
|||||||
teams_staffed_by,
|
teams_staffed_by,
|
||||||
)
|
)
|
||||||
from .services.fees import mark_as_paid, record_payment, remaining_balance
|
from .services.fees import mark_as_paid, record_payment, remaining_balance
|
||||||
from .services.invoicing import create_or_resend_invoice, invoice_pdf, invoices_due_for_reminder, recipient_for
|
from .services.invoicing import create_or_resend_invoice, invoice_pdf, invoices_due_for_reminder, recipient_for, resolve_document_address
|
||||||
from .services.onboarding import (
|
from .services.onboarding import (
|
||||||
annotate_onboarding_status,
|
annotate_onboarding_status,
|
||||||
approve_all_clean,
|
approve_all_clean,
|
||||||
@@ -1683,6 +1683,48 @@ class InvoicePdfTests(TestCase):
|
|||||||
|
|
||||||
self.assertIn(self.invoice.number, html)
|
self.assertIn(self.invoice.number, html)
|
||||||
|
|
||||||
|
def test_the_legal_address_takes_precedence_over_the_home_location(self):
|
||||||
|
Location.objects.create(club=self.club, name="Sports Hall", address="Sportlaan 1", zip_code="1000", city="Brussels", is_home=True)
|
||||||
|
self.club.legal_address = "Registered Office 5"
|
||||||
|
self.club.legal_zip_code = "9000"
|
||||||
|
self.club.legal_city = "Ghent"
|
||||||
|
self.club.save(update_fields=["legal_address", "legal_zip_code", "legal_city"])
|
||||||
|
|
||||||
|
html = self.render()
|
||||||
|
|
||||||
|
self.assertIn("Registered Office 5", html)
|
||||||
|
self.assertNotIn("Sportlaan 1", html)
|
||||||
|
|
||||||
|
|
||||||
|
class ResolveDocumentAddressTests(TestCase):
|
||||||
|
"""club.services.invoicing.resolve_document_address -- the club's own
|
||||||
|
legal_address when set, else its home Location, else None."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
cls.club = Club.objects.create(name="Ajax United", slug="ajax-united")
|
||||||
|
|
||||||
|
def test_returns_none_with_neither_set(self):
|
||||||
|
self.assertIsNone(resolve_document_address(self.club))
|
||||||
|
|
||||||
|
def test_falls_back_to_the_home_location(self):
|
||||||
|
home = Location.objects.create(club=self.club, name="Sports Hall", address="Sportlaan 1", zip_code="1000", city="Brussels", is_home=True)
|
||||||
|
|
||||||
|
self.assertEqual(resolve_document_address(self.club), home)
|
||||||
|
|
||||||
|
def test_legal_address_wins_over_the_home_location(self):
|
||||||
|
Location.objects.create(club=self.club, name="Sports Hall", address="Sportlaan 1", zip_code="1000", city="Brussels", is_home=True)
|
||||||
|
self.club.legal_address = "Registered Office 5"
|
||||||
|
self.club.legal_zip_code = "9000"
|
||||||
|
self.club.legal_city = "Ghent"
|
||||||
|
self.club.save(update_fields=["legal_address", "legal_zip_code", "legal_city"])
|
||||||
|
|
||||||
|
address = resolve_document_address(self.club)
|
||||||
|
|
||||||
|
self.assertEqual(address.address, "Registered Office 5")
|
||||||
|
self.assertEqual(address.zip_code, "9000")
|
||||||
|
self.assertEqual(address.city, "Ghent")
|
||||||
|
|
||||||
|
|
||||||
class InvoicesDueForReminderTests(TestCase):
|
class InvoicesDueForReminderTests(TestCase):
|
||||||
"""club.services.invoicing.invoices_due_for_reminder -- sent, unpaid, past
|
"""club.services.invoicing.invoices_due_for_reminder -- sent, unpaid, past
|
||||||
|
|||||||
@@ -905,7 +905,7 @@ class ClubSettingsForm(forms.ModelForm):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Club
|
model = Club
|
||||||
fields = ["name", "legal_name", "contact_email", "website", "logo", "primary_color", "secondary_color"]
|
fields = ["name", "legal_name", "legal_address", "legal_zip_code", "legal_city", "contact_email", "website", "logo", "primary_color", "secondary_color"]
|
||||||
widgets = {
|
widgets = {
|
||||||
"primary_color": forms.TextInput(attrs={"placeholder": "#1e40af"}),
|
"primary_color": forms.TextInput(attrs={"placeholder": "#1e40af"}),
|
||||||
"secondary_color": forms.TextInput(attrs={"placeholder": "#be185d"}),
|
"secondary_color": forms.TextInput(attrs={"placeholder": "#be185d"}),
|
||||||
|
|||||||
@@ -58,9 +58,9 @@ def _referee_form_pdf_context(club, request):
|
|||||||
opponent="Leuven",
|
opponent="Leuven",
|
||||||
external_game_id="BE-2026-00417",
|
external_game_id="BE-2026-00417",
|
||||||
)
|
)
|
||||||
home_location = SimpleNamespace(address="Sportlaan 1", zip_code="1000", city="Brussels")
|
document_address = SimpleNamespace(address="Sportlaan 1", zip_code="1000", city="Brussels")
|
||||||
grand_total = sum((referee.total_payable for referee in referees), Decimal("0"))
|
grand_total = sum((referee.total_payable for referee in referees), Decimal("0"))
|
||||||
return {"club": club, "event": event, "referees": referees, "home_location": home_location, "grand_total": grand_total} | referee_form_colors(club)
|
return {"club": club, "event": event, "referees": referees, "document_address": document_address, "grand_total": grand_total} | referee_form_colors(club)
|
||||||
|
|
||||||
|
|
||||||
PDF_PREVIEWS = [
|
PDF_PREVIEWS = [
|
||||||
|
|||||||
@@ -64,9 +64,13 @@
|
|||||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||||
{% form_field form.name %}
|
{% form_field form.name %}
|
||||||
{% form_field form.legal_name %}
|
{% form_field form.legal_name %}
|
||||||
|
<div class="md:col-span-2">{% form_field form.legal_address %}</div>
|
||||||
|
{% form_field form.legal_zip_code %}
|
||||||
|
{% form_field form.legal_city %}
|
||||||
{% form_field form.contact_email %}
|
{% form_field form.contact_email %}
|
||||||
{% form_field form.website %}
|
{% form_field form.website %}
|
||||||
</div>
|
</div>
|
||||||
|
<p class="mt-1 text-xs text-dim">{% trans "Used on official documents (invoices, the referee payment form). Leave blank to use the club's home location instead." %}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -69,9 +69,9 @@
|
|||||||
<div class="header">
|
<div class="header">
|
||||||
<div>
|
<div>
|
||||||
<div class="club-name">{{ club.official_name }}</div>
|
<div class="club-name">{{ club.official_name }}</div>
|
||||||
{% if home_location %}
|
{% if document_address %}
|
||||||
<div class="club-address">{{ home_location.address }}</div>
|
<div class="club-address">{{ document_address.address }}</div>
|
||||||
<div class="club-address">{{ home_location.zip_code }} {{ home_location.city }}</div>
|
<div class="club-address">{{ document_address.zip_code }} {{ document_address.city }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="doc-title">
|
<div class="doc-title">
|
||||||
|
|||||||
@@ -4863,6 +4863,28 @@ class ClubSettingsPreviewTests(ManagementTestBase):
|
|||||||
self.assertEqual(self.club.secondary_color, "#654321")
|
self.assertEqual(self.club.secondary_color, "#654321")
|
||||||
self.assertEqual(self.club.website, "https://ajax-united.example")
|
self.assertEqual(self.club.website, "https://ajax-united.example")
|
||||||
|
|
||||||
|
def test_the_legal_address_can_be_set(self):
|
||||||
|
response = self.club_post(
|
||||||
|
"club_settings",
|
||||||
|
{
|
||||||
|
"name": "Ajax United",
|
||||||
|
"legal_name": "",
|
||||||
|
"legal_address": "Registered Office 5",
|
||||||
|
"legal_zip_code": "9000",
|
||||||
|
"legal_city": "Ghent",
|
||||||
|
"contact_email": "",
|
||||||
|
"website": "",
|
||||||
|
"primary_color": "",
|
||||||
|
"secondary_color": "",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertRedirects(response, reverse("management:club_settings"))
|
||||||
|
self.club.refresh_from_db()
|
||||||
|
self.assertEqual(self.club.legal_address, "Registered Office 5")
|
||||||
|
self.assertEqual(self.club.legal_zip_code, "9000")
|
||||||
|
self.assertEqual(self.club.legal_city, "Ghent")
|
||||||
|
|
||||||
|
|
||||||
class ClubSettingsDocumentTabTests(ManagementTestBase):
|
class ClubSettingsDocumentTabTests(ManagementTestBase):
|
||||||
"""The Email and PDF tabs on the Club identity page -- every branded
|
"""The Email and PDF tabs on the Club identity page -- every branded
|
||||||
@@ -6197,7 +6219,7 @@ class EventRefereeFormPdfTests(ManagementTestBase):
|
|||||||
|
|
||||||
context = renderer.call_args[0][0]
|
context = renderer.call_args[0][0]
|
||||||
self.assertEqual(context["club"].official_name, "Ajax United VZW")
|
self.assertEqual(context["club"].official_name, "Ajax United VZW")
|
||||||
self.assertEqual(context["home_location"], self.home_ground)
|
self.assertEqual(context["document_address"], self.home_ground)
|
||||||
self.assertEqual(list(context["referees"]), [EventReferee.objects.get(event=game)])
|
self.assertEqual(list(context["referees"]), [EventReferee.objects.get(event=game)])
|
||||||
|
|
||||||
def test_the_grand_total_sums_every_referees_total_payable(self):
|
def test_the_grand_total_sums_every_referees_total_payable(self):
|
||||||
@@ -6217,7 +6239,7 @@ class EventRefereeFormPdfTests(ManagementTestBase):
|
|||||||
game = self.make_game()
|
game = self.make_game()
|
||||||
EventReferee.objects.create(event=game, external_name="Guest Referee", assigned_by=self.admin_member)
|
EventReferee.objects.create(event=game, external_name="Guest Referee", assigned_by=self.admin_member)
|
||||||
|
|
||||||
html = render_to_string("management/event_referee_form_pdf.html", {"club": self.club, "event": game, "referees": list(game.referees.all()), "home_location": self.home_ground, "grand_total": Decimal("0")})
|
html = render_to_string("management/event_referee_form_pdf.html", {"club": self.club, "event": game, "referees": list(game.referees.all()), "document_address": self.home_ground, "grand_total": Decimal("0")})
|
||||||
|
|
||||||
self.assertIn("Guest Referee", html)
|
self.assertIn("Guest Referee", html)
|
||||||
self.assertNotIn("External", html)
|
self.assertNotIn("External", html)
|
||||||
@@ -6226,7 +6248,7 @@ class EventRefereeFormPdfTests(ManagementTestBase):
|
|||||||
game = self.make_game()
|
game = self.make_game()
|
||||||
EventReferee.objects.create(event=game, member=self.referee, assigned_by=self.admin_member, fee=Decimal("25.00"), km=Decimal("40"), km_rate=Decimal("0.083"))
|
EventReferee.objects.create(event=game, member=self.referee, assigned_by=self.admin_member, fee=Decimal("25.00"), km=Decimal("40"), km_rate=Decimal("0.083"))
|
||||||
|
|
||||||
html = render_to_string("management/event_referee_form_pdf.html", {"club": self.club, "event": game, "referees": list(game.referees.all()), "home_location": self.home_ground, "grand_total": Decimal("28.320")})
|
html = render_to_string("management/event_referee_form_pdf.html", {"club": self.club, "event": game, "referees": list(game.referees.all()), "document_address": self.home_ground, "grand_total": Decimal("28.320")})
|
||||||
|
|
||||||
self.assertIn("€28.32<", html)
|
self.assertIn("€28.32<", html)
|
||||||
self.assertNotIn("28.320", html)
|
self.assertNotIn("28.320", html)
|
||||||
@@ -6237,14 +6259,14 @@ class EventRefereeFormPdfTests(ManagementTestBase):
|
|||||||
self.club.save(update_fields=["primary_color", "secondary_color"])
|
self.club.save(update_fields=["primary_color", "secondary_color"])
|
||||||
game = self.make_game()
|
game = self.make_game()
|
||||||
|
|
||||||
html = render_to_string("management/event_referee_form_pdf.html", {"club": self.club, "event": game, "referees": [], "home_location": self.home_ground, "grand_total": Decimal("0"), **referee_form_colors(self.club)})
|
html = render_to_string("management/event_referee_form_pdf.html", {"club": self.club, "event": game, "referees": [], "document_address": self.home_ground, "grand_total": Decimal("0"), **referee_form_colors(self.club)})
|
||||||
|
|
||||||
self.assertIn("--accent: #0f766e", html)
|
self.assertIn("--accent: #0f766e", html)
|
||||||
|
|
||||||
def test_the_pdf_falls_back_to_default_colours_when_unset(self):
|
def test_the_pdf_falls_back_to_default_colours_when_unset(self):
|
||||||
game = self.make_game()
|
game = self.make_game()
|
||||||
|
|
||||||
html = render_to_string("management/event_referee_form_pdf.html", {"club": self.club, "event": game, "referees": [], "home_location": self.home_ground, "grand_total": Decimal("0"), **referee_form_colors(self.club)})
|
html = render_to_string("management/event_referee_form_pdf.html", {"club": self.club, "event": game, "referees": [], "document_address": self.home_ground, "grand_total": Decimal("0"), **referee_form_colors(self.club)})
|
||||||
|
|
||||||
self.assertIn("--accent: #3730a3", html)
|
self.assertIn("--accent: #3730a3", html)
|
||||||
|
|
||||||
@@ -6254,7 +6276,7 @@ class EventRefereeFormPdfTests(ManagementTestBase):
|
|||||||
# silently renders with no background at all.
|
# silently renders with no background at all.
|
||||||
game = self.make_game()
|
game = self.make_game()
|
||||||
|
|
||||||
html = render_to_string("management/event_referee_form_pdf.html", {"club": self.club, "event": game, "referees": [], "home_location": self.home_ground, "grand_total": Decimal("0"), **referee_form_colors(self.club)})
|
html = render_to_string("management/event_referee_form_pdf.html", {"club": self.club, "event": game, "referees": [], "document_address": self.home_ground, "grand_total": Decimal("0"), **referee_form_colors(self.club)})
|
||||||
|
|
||||||
self.assertNotIn("color-mix(", html)
|
self.assertNotIn("color-mix(", html)
|
||||||
self.assertIn("--info-card-bg: #", html)
|
self.assertIn("--info-card-bg: #", html)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ from club.mixins import (
|
|||||||
from club.models import ClubMembership, ClubRole, DuesInvoice, MemberRequirementStatus, OnboardingRequirement, Season, Sponsor
|
from club.models import ClubMembership, ClubRole, DuesInvoice, MemberRequirementStatus, OnboardingRequirement, Season, Sponsor
|
||||||
from club.services.access import _guardians_only, can_edit_news, can_publish_news, current_season, groups_manageable_by, is_club_admin, members_visible_to, teams_managed_by, teams_staffed_by
|
from club.services.access import _guardians_only, can_edit_news, can_publish_news, current_season, groups_manageable_by, is_club_admin, members_visible_to, teams_managed_by, teams_staffed_by
|
||||||
from club.services.fees import mark_as_paid, record_payment, remaining_balance
|
from club.services.fees import mark_as_paid, record_payment, remaining_balance
|
||||||
from club.services.invoicing import DuesInvoicePDFError, create_or_resend_invoice, invoice_pdf, invoices_due_for_reminder, recipient_for, send_invoice_email, send_reminders
|
from club.services.invoicing import DuesInvoicePDFError, create_or_resend_invoice, invoice_pdf, invoices_due_for_reminder, recipient_for, resolve_document_address, send_invoice_email, send_reminders
|
||||||
from club.services.onboarding import annotate_onboarding_status, approve_all_clean, approve_one, blocking_event_kinds, checklist_for, is_signup_clean, mark_bypassed, mark_complete, mark_incomplete, members_with_open_requirements
|
from club.services.onboarding import annotate_onboarding_status, approve_all_clean, approve_one, blocking_event_kinds, checklist_for, is_signup_clean, mark_bypassed, mark_complete, mark_incomplete, members_with_open_requirements
|
||||||
from controlpanel.messages import notify
|
from controlpanel.messages import notify
|
||||||
from controlpanel.mixins import RedirectOnInvalidMixin
|
from controlpanel.mixins import RedirectOnInvalidMixin
|
||||||
@@ -2642,16 +2642,18 @@ class EventRefereeFeeUpdateView(ClubAdminRequiredMixin, FormView):
|
|||||||
class EventRefereeFormPdfView(ClubAdminRequiredMixin, View):
|
class EventRefereeFormPdfView(ClubAdminRequiredMixin, View):
|
||||||
"""Downloadable PDF of the referee payment form for one game, modeled on
|
"""Downloadable PDF of the referee payment form for one game, modeled on
|
||||||
the club's existing paper form -- club header (legal name if set, else
|
the club's existing paper form -- club header (legal name if set, else
|
||||||
plain name; address from the club's home Location, not this specific
|
plain name; address from the club's own legal address, or its home
|
||||||
event's, so the form still reads right even if called from a page where
|
Location when that's blank -- see club.services.invoicing.
|
||||||
the event's own location happens to be blank) plus this game's details,
|
resolve_document_address -- never this specific event's, so the form
|
||||||
referees and their fee/km breakdown, and blank signature lines."""
|
still reads right even if called from a page where the event's own
|
||||||
|
location happens to be blank) plus this game's details, referees and
|
||||||
|
their fee/km breakdown, and blank signature lines."""
|
||||||
|
|
||||||
def get(self, request, pk):
|
def get(self, request, pk):
|
||||||
event = get_object_or_404(Event.objects.filter(club=request.club).prefetch_related("teams", "referees__member"), pk=pk)
|
event = get_object_or_404(Event.objects.filter(club=request.club).prefetch_related("teams", "referees__member"), pk=pk)
|
||||||
home_location = Location.objects.filter(club=request.club, is_home=True).first()
|
document_address = resolve_document_address(request.club)
|
||||||
referees = list(event.referees.all())
|
referees = list(event.referees.all())
|
||||||
context = {"club": request.club, "event": event, "referees": referees, "home_location": home_location, "grand_total": sum((referee.total_payable for referee in referees), Decimal("0"))} | referee_form_colors(request.club)
|
context = {"club": request.club, "event": event, "referees": referees, "document_address": document_address, "grand_total": sum((referee.total_payable for referee in referees), Decimal("0"))} | referee_form_colors(request.club)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
pdf = event_referee_form_pdf(context)
|
pdf = event_referee_form_pdf(context)
|
||||||
|
|||||||
@@ -5584,6 +5584,11 @@
|
|||||||
line-height: var(--tw-leading, var(--text-xl--line-height));
|
line-height: var(--tw-leading, var(--text-xl--line-height));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.md\:col-span-2 {
|
||||||
|
@media (width >= 48rem) {
|
||||||
|
grid-column: span 2 / span 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
.md\:grid-cols-2 {
|
.md\:grid-cols-2 {
|
||||||
@media (width >= 48rem) {
|
@media (width >= 48rem) {
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
|||||||
Reference in New Issue
Block a user