From 0cca053d45449d9572cdeb116cfc978bb7ad902b Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Mon, 17 Mar 2025 10:36:29 +0100 Subject: [PATCH] Address form: Add provinces for Italy --- src/pretix/base/forms/questions.py | 13 ++++++++----- src/pretix/base/settings.py | 8 ++++++++ src/pretix/base/views/js_helpers.py | 11 +++++++++-- src/pretix/control/context.py | 3 ++- src/pretix/control/forms/event.py | 4 +++- .../templates/pretixcontrol/order/index.html | 5 +++-- src/pretix/presale/context.py | 3 ++- .../pretixpresale/event/checkout_confirm.html | 3 ++- .../pretixpresale/event/fragment_cart.html | 2 +- .../templates/pretixpresale/event/order.html | 3 ++- src/pretix/static/pretixbase/js/addressform.js | 4 ++++ 11 files changed, 44 insertions(+), 15 deletions(-) diff --git a/src/pretix/base/forms/questions.py b/src/pretix/base/forms/questions.py index aee2ada677..f223ee952a 100644 --- a/src/pretix/base/forms/questions.py +++ b/src/pretix/base/forms/questions.py @@ -83,8 +83,8 @@ from pretix.base.services.tax import ( VATIDFinalError, VATIDTemporaryError, validate_vat_id, ) from pretix.base.settings import ( - COUNTRIES_WITH_STATE_IN_ADDRESS, PERSON_NAME_SALUTATIONS, - PERSON_NAME_SCHEMES, PERSON_NAME_TITLE_GROUPS, + COUNTRIES_WITH_STATE_IN_ADDRESS, COUNTRY_STATE_LABEL, + PERSON_NAME_SALUTATIONS, PERSON_NAME_SCHEMES, PERSON_NAME_TITLE_GROUPS, ) from pretix.base.templatetags.rich_text import rich_text from pretix.base.timemachine import time_machine_now @@ -721,7 +721,7 @@ class BaseQuestionsForm(forms.Form): 'data-country-information-url': reverse('js_helpers.states'), }), ) - c = [('', pgettext_lazy('address', 'Select state'))] + c = [('', '---')] fprefix = str(self.prefix) + '-' if self.prefix is not None and self.prefix != '-' else '' cc = None state = None @@ -1079,7 +1079,7 @@ class BaseInvoiceAddressForm(forms.ModelForm): self.fields['country'].choices = CachedCountries() self.fields['country'].widget.attrs['data-country-information-url'] = reverse('js_helpers.states') - c = [('', pgettext_lazy('address', 'Select state'))] + c = [('', '---')] fprefix = self.prefix + '-' if self.prefix else '' cc = None if fprefix + 'country' in self.data: @@ -1088,16 +1088,19 @@ class BaseInvoiceAddressForm(forms.ModelForm): cc = str(self.initial['country']) elif self.instance and self.instance.country: cc = str(self.instance.country) + state_label = pgettext_lazy('address', 'State') if cc and cc in COUNTRIES_WITH_STATE_IN_ADDRESS: types, form = COUNTRIES_WITH_STATE_IN_ADDRESS[cc] statelist = [s for s in pycountry.subdivisions.get(country_code=cc) if s.type in types] c += sorted([(s.code[3:], s.name) for s in statelist], key=lambda s: s[1]) + if cc in COUNTRY_STATE_LABEL: + state_label = COUNTRY_STATE_LABEL[cc] elif fprefix + 'state' in self.data: self.data = self.data.copy() del self.data[fprefix + 'state'] self.fields['state'] = forms.ChoiceField( - label=pgettext_lazy('address', 'State'), + label=state_label, required=False, choices=c, widget=forms.Select(attrs={ diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py index 2311e62a46..9faa33722b 100644 --- a/src/pretix/base/settings.py +++ b/src/pretix/base/settings.py @@ -3712,6 +3712,14 @@ COUNTRIES_WITH_STATE_IN_ADDRESS = { 'MY': (['State', 'Federal territory'], 'long'), 'MX': (['State', 'Federal district'], 'short'), 'US': (['State', 'Outlying area', 'District'], 'short'), + 'IT': (['Province', 'Free municipal consortium', 'Metropolitan city', 'Autonomous province', + 'Free municipal consortium', 'Decentralized regional entity'], 'short'), +} +COUNTRY_STATE_LABEL = { + # Countries in which the "State" field should not be called "State" + 'CA': pgettext_lazy('address', 'Province'), + 'JP': pgettext_lazy('address', 'Prefecture'), + 'IT': pgettext_lazy('address', 'Province'), } settings_hierarkey = Hierarkey(attribute_name='settings') diff --git a/src/pretix/base/views/js_helpers.py b/src/pretix/base/views/js_helpers.py index be180e9cd5..303578ec78 100644 --- a/src/pretix/base/views/js_helpers.py +++ b/src/pretix/base/views/js_helpers.py @@ -21,12 +21,15 @@ # import pycountry from django.http import JsonResponse +from django.utils.translation import pgettext from pretix.base.addressvalidation import ( COUNTRIES_WITH_STREET_ZIPCODE_AND_CITY_REQUIRED, ) from pretix.base.models.tax import VAT_ID_COUNTRIES -from pretix.base.settings import COUNTRIES_WITH_STATE_IN_ADDRESS +from pretix.base.settings import ( + COUNTRIES_WITH_STATE_IN_ADDRESS, COUNTRY_STATE_LABEL, +) def states(request): @@ -35,7 +38,11 @@ def states(request): 'street': {'required': True}, 'zipcode': {'required': cc in COUNTRIES_WITH_STREET_ZIPCODE_AND_CITY_REQUIRED}, 'city': {'required': cc in COUNTRIES_WITH_STREET_ZIPCODE_AND_CITY_REQUIRED}, - 'state': {'visible': cc in COUNTRIES_WITH_STATE_IN_ADDRESS, 'required': cc in COUNTRIES_WITH_STATE_IN_ADDRESS}, + 'state': { + 'visible': cc in COUNTRIES_WITH_STATE_IN_ADDRESS, + 'required': cc in COUNTRIES_WITH_STATE_IN_ADDRESS, + 'label': COUNTRY_STATE_LABEL.get(cc, pgettext('address', 'State')), + }, 'vat_id': {'visible': cc in VAT_ID_COUNTRIES, 'required': False}, } if cc not in COUNTRIES_WITH_STATE_IN_ADDRESS: diff --git a/src/pretix/control/context.py b/src/pretix/control/context.py index 3cdfc1b0bf..5183b13ce1 100644 --- a/src/pretix/control/context.py +++ b/src/pretix/control/context.py @@ -43,7 +43,7 @@ from django.utils.translation import get_language from django_scopes import scope from pretix.base.models.auth import StaffSession -from pretix.base.settings import GlobalSettingsObject +from pretix.base.settings import COUNTRY_STATE_LABEL, GlobalSettingsObject from pretix.control.navigation import ( get_event_navigation, get_global_navigation, get_organizer_navigation, ) @@ -140,6 +140,7 @@ def _default_context(request): ctx['js_time_format'] = get_javascript_format('TIME_INPUT_FORMATS') ctx['js_locale'] = get_moment_locale() ctx['select2locale'] = get_language()[:2] + ctx['COUNTRY_STATE_LABEL'] = COUNTRY_STATE_LABEL ctx['warning_update_available'] = False ctx['warning_update_check_active'] = False diff --git a/src/pretix/control/forms/event.py b/src/pretix/control/forms/event.py index 98ba32d7af..f947638161 100644 --- a/src/pretix/control/forms/event.py +++ b/src/pretix/control/forms/event.py @@ -1475,7 +1475,9 @@ class CountriesAndEUAndStates(CountriesAndEU): def __iter__(self): for country_code, country_name in super().__iter__(): yield country_code, country_name - if country_code in COUNTRIES_WITH_STATE_IN_ADDRESS: + if country_code in COUNTRIES_WITH_STATE_IN_ADDRESS and country_code not in {"IT"}: + # Special case for Italy: Provinces are used in addresses, but are too low-level to + # have influence on taxes, so we avoid the bloat in the list of selectable countries. types, form = COUNTRIES_WITH_STATE_IN_ADDRESS[country_code] yield from sorted(((state.code, country_name + " - " + state.name) for state in pycountry.subdivisions.get(country_code=country_code) diff --git a/src/pretix/control/templates/pretixcontrol/order/index.html b/src/pretix/control/templates/pretixcontrol/order/index.html index af221e38e0..24c5dd964f 100644 --- a/src/pretix/control/templates/pretixcontrol/order/index.html +++ b/src/pretix/control/templates/pretixcontrol/order/index.html @@ -9,6 +9,7 @@ {% load eventsignal %} {% load l10n %} {% load phone_format %} +{% load getitem %} {% block title %} {% blocktrans trimmed with code=order.code %} Order details: {{ code }} @@ -560,8 +561,8 @@ {% if line.street or line.zipcode or line.city or line.country %} {{ line.street|default_if_none:""|linebreaksbr }}
{{ line.zipcode|default_if_none:"" }} {{ line.city|default_if_none:"" }}
+ {% if line.state %}{{ line.state_for_address }}
{% endif %} {{ line.country.name|default_if_none:"" }} - {% if line.state %}
{{ line.state }}{% endif %} {% else %} {% trans "not answered" %} {% endif %} @@ -959,7 +960,7 @@
{% trans "Country" %}
{{ order.invoice_address.country.name|default:order.invoice_address.country_old }}
{% if order.invoice_address.state %} -
{% trans "State" context "address" %}
+
{% trans "State" context "address" as state_label %}{{ COUNTRY_STATE_LABEL|getitem:order.invoice_address.country.code|default:state_label }}
{{ order.invoice_address.state_name }}
{% endif %} {% if request.event.settings.invoice_address_vatid %} diff --git a/src/pretix/presale/context.py b/src/pretix/presale/context.py index 80a9a2f945..2bfeac2c50 100644 --- a/src/pretix/presale/context.py +++ b/src/pretix/presale/context.py @@ -40,7 +40,7 @@ from django.utils.translation import get_language_info from django_scopes import get_scope from i18nfield.strings import LazyI18nString -from pretix.base.settings import GlobalSettingsObject +from pretix.base.settings import COUNTRY_STATE_LABEL, GlobalSettingsObject from pretix.helpers.i18n import ( get_javascript_format_without_seconds, get_moment_locale, ) @@ -187,6 +187,7 @@ def _default_context(request): ctx['html_locale'] = translation.get_language_info(get_language_without_region()).get('public_code', translation.get_language()) ctx['settings'] = pretix_settings ctx['django_settings'] = settings + ctx['COUNTRY_STATE_LABEL'] = COUNTRY_STATE_LABEL ctx['ie_deprecation_warning'] = 'MSIE' in request.headers.get('User-Agent', '') or 'Trident/' in request.headers.get('User-Agent', '') diff --git a/src/pretix/presale/templates/pretixpresale/event/checkout_confirm.html b/src/pretix/presale/templates/pretixpresale/event/checkout_confirm.html index b74d399aac..42156a81ed 100644 --- a/src/pretix/presale/templates/pretixpresale/event/checkout_confirm.html +++ b/src/pretix/presale/templates/pretixpresale/event/checkout_confirm.html @@ -4,6 +4,7 @@ {% load money %} {% load eventurl %} {% load eventsignal %} +{% load getitem %} {% block title %}{% trans "Review order" %}{% endblock %} {% block content %}
@@ -92,7 +93,7 @@
{% trans "Country" %}
{{ addr.country.name }}
{% if addr.state %} -
{% trans "State" context "address" %}
+
{% trans "State" context "address" as state_label %}{{ COUNTRY_STATE_LABEL|getitem:addr.country.code|default:state_label }}
{{ addr.state_name }}
{% endif %} {% if request.event.settings.invoice_address_vatid and addr.vat_id %} diff --git a/src/pretix/presale/templates/pretixpresale/event/fragment_cart.html b/src/pretix/presale/templates/pretixpresale/event/fragment_cart.html index 23dae50c8a..dc24d42d3a 100644 --- a/src/pretix/presale/templates/pretixpresale/event/fragment_cart.html +++ b/src/pretix/presale/templates/pretixpresale/event/fragment_cart.html @@ -200,8 +200,8 @@ {{ line.street|default_if_none:""|linebreaksbr }}
{{ line.zipcode|default_if_none:"" }} {{ line.city|default_if_none:"" }}
+ {% if line.state %}{{ line.state_for_address }}
{% endif %} {{ line.country.name|default_if_none:"" }} - {% if line.state %}
{{ line.state }}{% endif %}
{% endif %} diff --git a/src/pretix/presale/templates/pretixpresale/event/order.html b/src/pretix/presale/templates/pretixpresale/event/order.html index 62c9c11a83..7750f3c2cd 100644 --- a/src/pretix/presale/templates/pretixpresale/event/order.html +++ b/src/pretix/presale/templates/pretixpresale/event/order.html @@ -7,6 +7,7 @@ {% load eventurl %} {% load phone_format %} {% load rich_text %} +{% load getitem %} {% block title %} {% if "thanks" in request.GET or "paid" in request.GET %} {% trans "Thank you!" %} @@ -319,7 +320,7 @@
{% trans "Country" %}
{{ order.invoice_address.country.name|default:order.invoice_address.country_old }}
{% if order.invoice_address.state %} -
{% trans "State" context "address" %}
+
{% trans "State" context "address" as state_label %}{{ COUNTRY_STATE_LABEL|getitem:order.invoice_address.country.code|default:state_label }}
{{ order.invoice_address.state_name }}
{% endif %} {% if request.event.settings.invoice_address_vatid and order.invoice_address.vat_id %} diff --git a/src/pretix/static/pretixbase/js/addressform.js b/src/pretix/static/pretixbase/js/addressform.js index 5cae95100d..fe29408037 100644 --- a/src/pretix/static/pretixbase/js/addressform.js +++ b/src/pretix/static/pretixbase/js/addressform.js @@ -44,6 +44,10 @@ $(function () { ); } + if ('label' in options) { + dependent.closest(".form-group").find(".control-label").text(options.label); + } + const required = 'required' in options && options.required && isRequired && visible; dependent.closest(".form-group").toggle(visible).toggleClass('required', required); dependent.prop("required", required);