diff --git a/club/migrations/0030_club_legal_address_club_legal_city_and_more.py b/club/migrations/0030_club_legal_address_club_legal_city_and_more.py new file mode 100644 index 0000000..45257c8 --- /dev/null +++ b/club/migrations/0030_club_legal_address_club_legal_city_and_more.py @@ -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'), + ), + ] diff --git a/club/models.py b/club/models.py index f46f6d2..eb9071c 100644 --- a/club/models.py +++ b/club/models.py @@ -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.")) + 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"), upload_to=club_logo_path, diff --git a/club/services/invoicing.py b/club/services/invoicing.py index a29b145..1264658 100644 --- a/club/services/invoicing.py +++ b/club/services/invoicing.py @@ -8,6 +8,7 @@ membership.fee_status -- never a flag duplicated here that could drift out of st """ from datetime import timedelta +from types import SimpleNamespace from django.conf import settings from django.core.mail import EmailMultiAlternatives @@ -158,11 +159,30 @@ def render_pdf(html: str) -> bytes: 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: # 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 - # home location -- never an event-specific location, since a dues invoice isn't - # tied to any one event. - home_location = Location.objects.filter(club=invoice.club, is_home=True).first() - 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}) + # document address -- never an event-specific location, since a dues invoice + # isn't tied to any one event. + 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, "document_address": document_address}) return render_pdf(html) diff --git a/club/templates/club/dues_invoice_pdf.html b/club/templates/club/dues_invoice_pdf.html index 4f14988..75b315e 100644 --- a/club/templates/club/dues_invoice_pdf.html +++ b/club/templates/club/dues_invoice_pdf.html @@ -43,9 +43,9 @@
{% trans "Used on official documents (invoices, the referee payment form). Leave blank to use the club's home location instead." %}