diff --git a/src/pretix/base/forms/questions.py b/src/pretix/base/forms/questions.py
index e7e6b6a4ea..5222dc7e6e 100644
--- a/src/pretix/base/forms/questions.py
+++ b/src/pretix/base/forms/questions.py
@@ -54,6 +54,7 @@ from django.core.validators import (
from django.db.models import QuerySet
from django.forms import Select, widgets
from django.forms.widgets import FILE_INPUT_CONTRADICTION
+from django.urls import reverse
from django.utils.formats import date_format
from django.utils.html import escape
from django.utils.safestring import mark_safe
@@ -712,6 +713,7 @@ class BaseQuestionsForm(forms.Form):
initial=country,
widget=forms.Select(attrs={
'autocomplete': 'country',
+ 'data-country-information-url': reverse('js_helpers.states'),
}),
)
c = [('', pgettext_lazy('address', 'Select state'))]
@@ -1005,7 +1007,7 @@ class BaseInvoiceAddressForm(forms.ModelForm):
'street': forms.Textarea(attrs={
'rows': 2,
'placeholder': _('Street and Number'),
- 'autocomplete': 'street-address'
+ 'autocomplete': 'street-address',
}),
'beneficiary': forms.Textarea(attrs={'rows': 3}),
'country': forms.Select(attrs={
@@ -1021,7 +1023,7 @@ class BaseInvoiceAddressForm(forms.ModelForm):
'data-display-dependency': '#id_is_business_1',
'autocomplete': 'organization',
}),
- 'vat_id': forms.TextInput(attrs={'data-display-dependency': '#id_is_business_1', 'data-countries-with-vat-id': ','.join(VAT_ID_COUNTRIES)}),
+ 'vat_id': forms.TextInput(attrs={'data-display-dependency': '#id_is_business_1'}),
'internal_reference': forms.TextInput,
}
labels = {
@@ -1055,6 +1057,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'))]
fprefix = self.prefix + '-' if self.prefix else ''
@@ -1083,6 +1086,10 @@ class BaseInvoiceAddressForm(forms.ModelForm):
)
self.fields['state'].widget.is_required = True
+ self.fields['street'].required = False
+ self.fields['zipcode'].required = False
+ self.fields['city'].required = False
+
# Without JavaScript the VAT ID field is not hidden, so we empty the field if a country outside the EU is selected.
if cc and not ask_for_vat_id(cc) and fprefix + 'vat_id' in self.data:
self.data = self.data.copy()
diff --git a/src/pretix/base/models/orders.py b/src/pretix/base/models/orders.py
index d0786d8d95..f151dbb34f 100644
--- a/src/pretix/base/models/orders.py
+++ b/src/pretix/base/models/orders.py
@@ -3204,9 +3204,9 @@ class InvoiceAddress(models.Model):
company = models.CharField(max_length=255, blank=True, verbose_name=_('Company name'))
name_cached = models.CharField(max_length=255, verbose_name=_('Full name'), blank=True)
name_parts = models.JSONField(default=dict)
- street = models.TextField(verbose_name=_('Address'), blank=False)
- zipcode = models.CharField(max_length=30, verbose_name=_('ZIP code'), blank=False)
- city = models.CharField(max_length=255, verbose_name=_('City'), blank=False)
+ street = models.TextField(verbose_name=_('Address'), blank=True)
+ zipcode = models.CharField(max_length=30, verbose_name=_('ZIP code'), blank=True)
+ city = models.CharField(max_length=255, verbose_name=_('City'), blank=True)
country_old = models.CharField(max_length=255, verbose_name=_('Country'), blank=False)
country = FastCountryField(verbose_name=_('Country'), blank=False, blank_label=_('Select country'),
countries=CachedCountries)
diff --git a/src/pretix/base/views/js_helpers.py b/src/pretix/base/views/js_helpers.py
index 458f55d44d..0ea97bc927 100644
--- a/src/pretix/base/views/js_helpers.py
+++ b/src/pretix/base/views/js_helpers.py
@@ -22,16 +22,28 @@
import pycountry
from django.http import JsonResponse
+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
def states(request):
cc = request.GET.get("country", "DE")
+ info = {
+ 'street': {'required': cc in COUNTRIES_WITH_STREET_ZIPCODE_AND_CITY_REQUIRED},
+ '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},
+ 'vat_id': {'visible': cc in VAT_ID_COUNTRIES, 'required': False},
+ }
if cc not in COUNTRIES_WITH_STATE_IN_ADDRESS:
- return JsonResponse({'data': []})
+ return JsonResponse({'data': [], **info,})
types, form = COUNTRIES_WITH_STATE_IN_ADDRESS[cc]
statelist = [s for s in pycountry.subdivisions.get(country_code=cc) if s.type in types]
- return JsonResponse({'data': [
- {'name': s.name, 'code': s.code[3:]}
- for s in sorted(statelist, key=lambda s: s.name)
- ]})
+ return JsonResponse({
+ 'data': [
+ {'name': s.name, 'code': s.code[3:]}
+ for s in sorted(statelist, key=lambda s: s.name)
+ ],
+ **info,
+ })
diff --git a/src/pretix/static/pretixbase/js/addressform.js b/src/pretix/static/pretixbase/js/addressform.js
index 89e9991783..0a5c4652af 100644
--- a/src/pretix/static/pretixbase/js/addressform.js
+++ b/src/pretix/static/pretixbase/js/addressform.js
@@ -1,65 +1,61 @@
$(function () {
"use strict";
- $("select[name$=state]:not([data-static])").each(function () {
- var dependent = $(this),
- counter = 0,
- dependency = $(this).closest(".panel-body, form").find('select[name$=country]'),
- depRequired = dependency.closest(".form-group").is(".required"),
+ $("select[data-country-information-url]").each(function () {
+ let counter = 0;
+ const dependency = $(this),
+ url = this.getAttribute('data-country-information-url'),
+ form = $(this).closest(".panel-body, form"),
+ isRequired = dependency.closest(".form-group").is(".required"),
+ dependents = {
+ 'city': form.find("input[name$=city]"),
+ 'zipcode': form.find("input[name$=zipcode]"),
+ 'street': form.find("textarea[name$=street]"),
+ 'state': form.find("select[name$=state]"),
+ 'vat_id': form.find("input[name$=vat_id]"),
+ },
update = function (ev) {
counter++;
- var curCounter = counter;
- dependent.prop("disabled", true);
+ const curCounter = counter;
+ for (var k in dependents) dependents[k].prop("disabled", true);
dependency.closest(".form-group").find("label").prepend(" ");
- $.getJSON('/js_helpers/states/?country=' + dependency.val(), function (data) {
+ $.getJSON(url + '?country=' + dependency.val(), function (data) {
if (counter > curCounter) {
return; // Lost race
}
- var selected_value = dependent.prop("data-selected-value");
- dependent.find("option").filter(function (t) {return !!$(this).attr("value")}).remove();
+ var selected_value = dependents.state.prop("data-selected-value");
+ dependents.state.find("option").filter(function (t) {return !!$(this).attr("value")}).remove();
if (data.data.length > 0) {
$.each(data.data, function (k, s) {
var o = $("