mirror of
https://github.com/pretix/pretix.git
synced 2025-12-12 04:42:28 +00:00
Compare commits
18 Commits
validate-u
...
fix-state-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eff815056b | ||
|
|
45391b104a | ||
|
|
54d3d20d70 | ||
|
|
be1cb12286 | ||
|
|
c65b7f711d | ||
|
|
8e3d5511e2 | ||
|
|
9acfcf2526 | ||
|
|
32fd9f2f4d | ||
|
|
47ec03baac | ||
|
|
04884ce874 | ||
|
|
3337783d1f | ||
|
|
391bbc25f8 | ||
|
|
68017cc8cb | ||
|
|
5fee16035f | ||
|
|
6d32aa5220 | ||
|
|
3461c59cfe | ||
|
|
21b4dacee9 | ||
|
|
2663e9ff32 |
@@ -54,6 +54,7 @@ from django.core.validators import (
|
|||||||
from django.db.models import QuerySet
|
from django.db.models import QuerySet
|
||||||
from django.forms import Select, widgets
|
from django.forms import Select, widgets
|
||||||
from django.forms.widgets import FILE_INPUT_CONTRADICTION
|
from django.forms.widgets import FILE_INPUT_CONTRADICTION
|
||||||
|
from django.urls import reverse
|
||||||
from django.utils.formats import date_format
|
from django.utils.formats import date_format
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
@@ -77,7 +78,7 @@ from pretix.base.i18n import (
|
|||||||
get_babel_locale, get_language_without_region, language,
|
get_babel_locale, get_language_without_region, language,
|
||||||
)
|
)
|
||||||
from pretix.base.models import InvoiceAddress, Item, Question, QuestionOption
|
from pretix.base.models import InvoiceAddress, Item, Question, QuestionOption
|
||||||
from pretix.base.models.tax import VAT_ID_COUNTRIES, ask_for_vat_id
|
from pretix.base.models.tax import ask_for_vat_id
|
||||||
from pretix.base.services.tax import (
|
from pretix.base.services.tax import (
|
||||||
VATIDFinalError, VATIDTemporaryError, validate_vat_id,
|
VATIDFinalError, VATIDTemporaryError, validate_vat_id,
|
||||||
)
|
)
|
||||||
@@ -602,6 +603,7 @@ class BaseQuestionsForm(forms.Form):
|
|||||||
questions = pos.item.questions_to_ask
|
questions = pos.item.questions_to_ask
|
||||||
event = kwargs.pop('event')
|
event = kwargs.pop('event')
|
||||||
self.all_optional = kwargs.pop('all_optional', False)
|
self.all_optional = kwargs.pop('all_optional', False)
|
||||||
|
self.attendee_addresses_required = event.settings.attendee_addresses_required and not self.all_optional
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
@@ -676,7 +678,7 @@ class BaseQuestionsForm(forms.Form):
|
|||||||
|
|
||||||
if item.ask_attendee_data and event.settings.attendee_addresses_asked:
|
if item.ask_attendee_data and event.settings.attendee_addresses_asked:
|
||||||
add_fields['street'] = forms.CharField(
|
add_fields['street'] = forms.CharField(
|
||||||
required=event.settings.attendee_addresses_required and not self.all_optional,
|
required=self.attendee_addresses_required,
|
||||||
label=_('Address'),
|
label=_('Address'),
|
||||||
widget=forms.Textarea(attrs={
|
widget=forms.Textarea(attrs={
|
||||||
'rows': 2,
|
'rows': 2,
|
||||||
@@ -686,7 +688,7 @@ class BaseQuestionsForm(forms.Form):
|
|||||||
initial=(cartpos.street if cartpos else orderpos.street),
|
initial=(cartpos.street if cartpos else orderpos.street),
|
||||||
)
|
)
|
||||||
add_fields['zipcode'] = forms.CharField(
|
add_fields['zipcode'] = forms.CharField(
|
||||||
required=event.settings.attendee_addresses_required and not self.all_optional,
|
required=False,
|
||||||
max_length=30,
|
max_length=30,
|
||||||
label=_('ZIP code'),
|
label=_('ZIP code'),
|
||||||
initial=(cartpos.zipcode if cartpos else orderpos.zipcode),
|
initial=(cartpos.zipcode if cartpos else orderpos.zipcode),
|
||||||
@@ -695,7 +697,7 @@ class BaseQuestionsForm(forms.Form):
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
add_fields['city'] = forms.CharField(
|
add_fields['city'] = forms.CharField(
|
||||||
required=event.settings.attendee_addresses_required and not self.all_optional,
|
required=False,
|
||||||
label=_('City'),
|
label=_('City'),
|
||||||
max_length=255,
|
max_length=255,
|
||||||
initial=(cartpos.city if cartpos else orderpos.city),
|
initial=(cartpos.city if cartpos else orderpos.city),
|
||||||
@@ -707,11 +709,12 @@ class BaseQuestionsForm(forms.Form):
|
|||||||
add_fields['country'] = CountryField(
|
add_fields['country'] = CountryField(
|
||||||
countries=CachedCountries
|
countries=CachedCountries
|
||||||
).formfield(
|
).formfield(
|
||||||
required=event.settings.attendee_addresses_required and not self.all_optional,
|
required=self.attendee_addresses_required,
|
||||||
label=_('Country'),
|
label=_('Country'),
|
||||||
initial=country,
|
initial=country,
|
||||||
widget=forms.Select(attrs={
|
widget=forms.Select(attrs={
|
||||||
'autocomplete': 'country',
|
'autocomplete': 'country',
|
||||||
|
'data-country-information-url': reverse('js_helpers.states'),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
c = [('', pgettext_lazy('address', 'Select state'))]
|
c = [('', pgettext_lazy('address', 'Select state'))]
|
||||||
@@ -946,9 +949,9 @@ class BaseQuestionsForm(forms.Form):
|
|||||||
d = super().clean()
|
d = super().clean()
|
||||||
|
|
||||||
if self.address_validation:
|
if self.address_validation:
|
||||||
self.cleaned_data = d = validate_address(d, True)
|
self.cleaned_data = d = validate_address(d, all_optional=not self.attendee_addresses_required)
|
||||||
|
|
||||||
if d.get('city') and d.get('country') and str(d['country']) in COUNTRIES_WITH_STATE_IN_ADDRESS:
|
if d.get('street') and d.get('country') and str(d['country']) in COUNTRIES_WITH_STATE_IN_ADDRESS:
|
||||||
if not d.get('state'):
|
if not d.get('state'):
|
||||||
self.add_error('state', _('This field is required.'))
|
self.add_error('state', _('This field is required.'))
|
||||||
|
|
||||||
@@ -1005,7 +1008,7 @@ class BaseInvoiceAddressForm(forms.ModelForm):
|
|||||||
'street': forms.Textarea(attrs={
|
'street': forms.Textarea(attrs={
|
||||||
'rows': 2,
|
'rows': 2,
|
||||||
'placeholder': _('Street and Number'),
|
'placeholder': _('Street and Number'),
|
||||||
'autocomplete': 'street-address'
|
'autocomplete': 'street-address',
|
||||||
}),
|
}),
|
||||||
'beneficiary': forms.Textarea(attrs={'rows': 3}),
|
'beneficiary': forms.Textarea(attrs={'rows': 3}),
|
||||||
'country': forms.Select(attrs={
|
'country': forms.Select(attrs={
|
||||||
@@ -1021,7 +1024,7 @@ class BaseInvoiceAddressForm(forms.ModelForm):
|
|||||||
'data-display-dependency': '#id_is_business_1',
|
'data-display-dependency': '#id_is_business_1',
|
||||||
'autocomplete': 'organization',
|
'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,
|
'internal_reference': forms.TextInput,
|
||||||
}
|
}
|
||||||
labels = {
|
labels = {
|
||||||
@@ -1055,6 +1058,7 @@ class BaseInvoiceAddressForm(forms.ModelForm):
|
|||||||
])
|
])
|
||||||
|
|
||||||
self.fields['country'].choices = CachedCountries()
|
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 = [('', pgettext_lazy('address', 'Select state'))]
|
||||||
fprefix = self.prefix + '-' if self.prefix else ''
|
fprefix = self.prefix + '-' if self.prefix else ''
|
||||||
@@ -1083,6 +1087,10 @@ class BaseInvoiceAddressForm(forms.ModelForm):
|
|||||||
)
|
)
|
||||||
self.fields['state'].widget.is_required = True
|
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.
|
# 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:
|
if cc and not ask_for_vat_id(cc) and fprefix + 'vat_id' in self.data:
|
||||||
self.data = self.data.copy()
|
self.data = self.data.copy()
|
||||||
@@ -1142,9 +1150,11 @@ class BaseInvoiceAddressForm(forms.ModelForm):
|
|||||||
data['vat_id'] = ''
|
data['vat_id'] = ''
|
||||||
if self.event.settings.invoice_address_required:
|
if self.event.settings.invoice_address_required:
|
||||||
if data.get('is_business') and not data.get('company'):
|
if data.get('is_business') and not data.get('company'):
|
||||||
raise ValidationError(_('You need to provide a company name.'))
|
raise ValidationError({"company": _('You need to provide a company name.')})
|
||||||
if not data.get('is_business') and not data.get('name_parts'):
|
if not data.get('is_business') and not data.get('name_parts'):
|
||||||
raise ValidationError(_('You need to provide your name.'))
|
raise ValidationError(_('You need to provide your name.'))
|
||||||
|
if not data.get('street') and not data.get('zipcode') and not data.get('city'):
|
||||||
|
raise ValidationError({"street": _('This field is required.')})
|
||||||
|
|
||||||
if 'vat_id' in self.changed_data or not data.get('vat_id'):
|
if 'vat_id' in self.changed_data or not data.get('vat_id'):
|
||||||
self.instance.vat_id_validated = False
|
self.instance.vat_id_validated = False
|
||||||
|
|||||||
@@ -3204,9 +3204,9 @@ class InvoiceAddress(models.Model):
|
|||||||
company = models.CharField(max_length=255, blank=True, verbose_name=_('Company name'))
|
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_cached = models.CharField(max_length=255, verbose_name=_('Full name'), blank=True)
|
||||||
name_parts = models.JSONField(default=dict)
|
name_parts = models.JSONField(default=dict)
|
||||||
street = models.TextField(verbose_name=_('Address'), blank=False)
|
street = models.TextField(verbose_name=_('Address'), blank=True)
|
||||||
zipcode = models.CharField(max_length=30, verbose_name=_('ZIP code'), blank=False)
|
zipcode = models.CharField(max_length=30, verbose_name=_('ZIP code'), blank=True)
|
||||||
city = models.CharField(max_length=255, verbose_name=_('City'), blank=False)
|
city = models.CharField(max_length=255, verbose_name=_('City'), blank=True)
|
||||||
country_old = models.CharField(max_length=255, verbose_name=_('Country'), blank=False)
|
country_old = models.CharField(max_length=255, verbose_name=_('Country'), blank=False)
|
||||||
country = FastCountryField(verbose_name=_('Country'), blank=False, blank_label=_('Select country'),
|
country = FastCountryField(verbose_name=_('Country'), blank=False, blank_label=_('Select country'),
|
||||||
countries=CachedCountries)
|
countries=CachedCountries)
|
||||||
|
|||||||
@@ -22,16 +22,30 @@
|
|||||||
import pycountry
|
import pycountry
|
||||||
from django.http import JsonResponse
|
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
|
from pretix.base.settings import COUNTRIES_WITH_STATE_IN_ADDRESS
|
||||||
|
|
||||||
|
|
||||||
def states(request):
|
def states(request):
|
||||||
cc = request.GET.get("country", "DE")
|
cc = request.GET.get("country", "DE")
|
||||||
|
info = {
|
||||||
|
'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},
|
||||||
|
'vat_id': {'visible': cc in VAT_ID_COUNTRIES, 'required': False},
|
||||||
|
}
|
||||||
if cc not in COUNTRIES_WITH_STATE_IN_ADDRESS:
|
if cc not in COUNTRIES_WITH_STATE_IN_ADDRESS:
|
||||||
return JsonResponse({'data': []})
|
return JsonResponse({'data': [], **info, })
|
||||||
types, form = COUNTRIES_WITH_STATE_IN_ADDRESS[cc]
|
types, form = COUNTRIES_WITH_STATE_IN_ADDRESS[cc]
|
||||||
statelist = [s for s in pycountry.subdivisions.get(country_code=cc) if s.type in types]
|
statelist = [s for s in pycountry.subdivisions.get(country_code=cc) if s.type in types]
|
||||||
return JsonResponse({'data': [
|
return JsonResponse({
|
||||||
{'name': s.name, 'code': s.code[3:]}
|
'data': [
|
||||||
for s in sorted(statelist, key=lambda s: s.name)
|
{'name': s.name, 'code': s.code[3:]}
|
||||||
]})
|
for s in sorted(statelist, key=lambda s: s.name)
|
||||||
|
],
|
||||||
|
**info,
|
||||||
|
})
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
<script type="text/javascript" src="{% static "fileupload/jquery.fileupload.js" %}"></script>
|
<script type="text/javascript" src="{% static "fileupload/jquery.fileupload.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% static "lightbox/js/lightbox.js" %}"></script>
|
<script type="text/javascript" src="{% static "lightbox/js/lightbox.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% static "are-you-sure/jquery.are-you-sure.js" %}"></script>
|
<script type="text/javascript" src="{% static "are-you-sure/jquery.are-you-sure.js" %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static "pretixbase/js/addressform.js" %}"></script>
|
||||||
{% endcompress %}
|
{% endcompress %}
|
||||||
{{ html_head|safe }}
|
{{ html_head|safe }}
|
||||||
|
|
||||||
|
|||||||
@@ -46,11 +46,13 @@
|
|||||||
<div id="cp{{ pos.id }}">
|
<div id="cp{{ pos.id }}">
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
{% for form in forms %}
|
{% for form in forms %}
|
||||||
{% if form.pos.item != pos.item %}
|
<div class="profile-scope">
|
||||||
{# Add-Ons #}
|
{% if form.pos.item != pos.item %}
|
||||||
<legend>+ {{ form.pos.item }}</legend>
|
{# Add-Ons #}
|
||||||
{% endif %}
|
<legend>+ {{ form.pos.item }}</legend>
|
||||||
{% bootstrap_form form layout="control" %}
|
{% endif %}
|
||||||
|
{% bootstrap_form form layout="control" %}
|
||||||
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1076,8 +1076,8 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
|
|||||||
if warn:
|
if warn:
|
||||||
messages.warning(request, _('Please fill in answers to all required questions.'))
|
messages.warning(request, _('Please fill in answers to all required questions.'))
|
||||||
return False
|
return False
|
||||||
if cp.item.ask_attendee_data and self.request.event.settings.get('attendee_attendees_required', as_type=bool) \
|
if cp.item.ask_attendee_data and self.request.event.settings.get('attendee_addresses_required', as_type=bool) \
|
||||||
and (cp.street is None or cp.city is None or cp.country is None):
|
and (cp.street is None and cp.city is None and cp.country is None):
|
||||||
if warn:
|
if warn:
|
||||||
messages.warning(request, _('Please fill in answers to all required questions.'))
|
messages.warning(request, _('Please fill in answers to all required questions.'))
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -20,4 +20,5 @@
|
|||||||
<script type="text/javascript" src="{% static "pretixpresale/js/ui/cart.js" %}"></script>
|
<script type="text/javascript" src="{% static "pretixpresale/js/ui/cart.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% static "lightbox/js/lightbox.js" %}"></script>
|
<script type="text/javascript" src="{% static "lightbox/js/lightbox.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% static "pretixpresale/js/ui/iframe.js" %}"></script>
|
<script type="text/javascript" src="{% static "pretixpresale/js/ui/iframe.js" %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static "pretixbase/js/addressform.js" %}"></script>
|
||||||
{% endcompress %}
|
{% endcompress %}
|
||||||
|
|||||||
56
src/pretix/static/pretixbase/js/addressform.js
Normal file
56
src/pretix/static/pretixbase/js/addressform.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
$(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
$("select[data-country-information-url]").each(function () {
|
||||||
|
let xhr;
|
||||||
|
const dependency = $(this),
|
||||||
|
loader = $("<span class='fa fa-cog fa-spin'></span>").hide().prependTo(dependency.closest(".form-group").find("label")),
|
||||||
|
url = this.getAttribute('data-country-information-url'),
|
||||||
|
form = dependency.closest(".panel-body, form, .profile-scope"),
|
||||||
|
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) {
|
||||||
|
if (xhr) {
|
||||||
|
xhr.abort();
|
||||||
|
}
|
||||||
|
for (var k in dependents) dependents[k].prop("disabled", true);
|
||||||
|
loader.fadeIn();
|
||||||
|
xhr = $.getJSON(url + '?country=' + dependency.val(), function (data) {
|
||||||
|
var selected_value = dependents.state.prop("data-selected-value");
|
||||||
|
if (selected_value) dependents.state.prop("data-selected-value", "");
|
||||||
|
dependents.state.find("option:not([value=''])").remove();
|
||||||
|
if (data.data.length > 0) {
|
||||||
|
$.each(data.data, function (k, s) {
|
||||||
|
var o = $("<option>").attr("value", s.code).text(s.name);
|
||||||
|
if (selected_value == s.code) o.prop("selected", true);
|
||||||
|
dependents.state.append(o);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for(var k in dependents) {
|
||||||
|
const options = data[k],
|
||||||
|
dependent = dependents[k],
|
||||||
|
visible = 'visible' in options ? options.visible : true,
|
||||||
|
required = 'required' in options && options.required && isRequired && visible;
|
||||||
|
|
||||||
|
dependent.closest(".form-group").toggle(visible).toggleClass('required', required);
|
||||||
|
dependent.prop("required", required);
|
||||||
|
}
|
||||||
|
for (var k in dependents) dependents[k].prop("disabled", false);
|
||||||
|
}).always(function() {
|
||||||
|
loader.fadeOut();
|
||||||
|
}).fail(function(){
|
||||||
|
// TODO: handle failed request
|
||||||
|
});
|
||||||
|
};
|
||||||
|
dependents.state.prop("data-selected-value", dependents.state.val());
|
||||||
|
update();
|
||||||
|
dependency.on("change", update);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
@@ -434,60 +434,6 @@ var form_handlers = function (el) {
|
|||||||
dependency.closest('.form-group').find('input[name=' + dependency.attr("name") + ']').on("dp.change", update);
|
dependency.closest('.form-group').find('input[name=' + dependency.attr("name") + ']').on("dp.change", update);
|
||||||
});
|
});
|
||||||
|
|
||||||
$("input[name$=vat_id][data-countries-with-vat-id]").each(function () {
|
|
||||||
var dependent = $(this),
|
|
||||||
dependency_country = $(this).closest(".panel-body, form").find('select[name$=country]'),
|
|
||||||
dependency_id_is_business_1 = $(this).closest(".panel-body, form").find('input[id$=id_is_business_1]'),
|
|
||||||
update = function (ev) {
|
|
||||||
if (dependency_id_is_business_1.length && !dependency_id_is_business_1.prop("checked")) {
|
|
||||||
dependent.closest(".form-group").hide();
|
|
||||||
} else if (dependent.attr('data-countries-with-vat-id').split(',').includes(dependency_country.val())) {
|
|
||||||
dependent.closest(".form-group").show();
|
|
||||||
} else {
|
|
||||||
dependent.closest(".form-group").hide();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
update();
|
|
||||||
dependency_country.on("change", update);
|
|
||||||
dependency_id_is_business_1.on("change", update);
|
|
||||||
});
|
|
||||||
|
|
||||||
$("select[name$=state]:not([data-static])").each(function () {
|
|
||||||
var dependent = $(this),
|
|
||||||
counter = 0,
|
|
||||||
dependency = $(this).closest(".panel-body, form").find('select[name$=country]'),
|
|
||||||
update = function (ev) {
|
|
||||||
counter++;
|
|
||||||
var curCounter = counter;
|
|
||||||
dependent.prop("disabled", true);
|
|
||||||
dependency.closest(".form-group").find("label").prepend("<span class='fa fa-cog fa-spin'></span> ");
|
|
||||||
$.getJSON('/js_helpers/states/?country=' + dependency.val(), function (data) {
|
|
||||||
if (counter > curCounter) {
|
|
||||||
return; // Lost race
|
|
||||||
}
|
|
||||||
dependent.find("option").filter(function (t) {return !!$(this).attr("value")}).remove();
|
|
||||||
if (data.data.length > 0) {
|
|
||||||
$.each(data.data, function (k, s) {
|
|
||||||
dependent.append($("<option>").attr("value", s.code).text(s.name));
|
|
||||||
});
|
|
||||||
dependent.closest(".form-group").show();
|
|
||||||
dependent.prop('required', dependency.prop("required"));
|
|
||||||
} else {
|
|
||||||
dependent.closest(".form-group").hide();
|
|
||||||
dependent.prop("required", false);
|
|
||||||
}
|
|
||||||
dependent.prop("disabled", false);
|
|
||||||
dependency.closest(".form-group").find("label .fa-spin").remove();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
if (dependent.find("option").length === 1) {
|
|
||||||
dependent.closest(".form-group").hide();
|
|
||||||
} else {
|
|
||||||
dependent.prop('required', dependency.prop("required"));
|
|
||||||
}
|
|
||||||
dependency.on("change", update);
|
|
||||||
});
|
|
||||||
|
|
||||||
el.find("div.scrolling-choice:not(.no-search)").each(function () {
|
el.find("div.scrolling-choice:not(.no-search)").each(function () {
|
||||||
if ($(this).find("input[type=text]").length > 0) {
|
if ($(this).find("input[type=text]").length > 0) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -517,65 +517,6 @@ $(function () {
|
|||||||
dependency.closest('.form-group, form').find('input[name=' + dependency.attr("name") + ']').on("dp.change", update);
|
dependency.closest('.form-group, form').find('input[name=' + dependency.attr("name") + ']').on("dp.change", update);
|
||||||
});
|
});
|
||||||
|
|
||||||
$("input[name$=vat_id][data-countries-with-vat-id]").each(function () {
|
|
||||||
var dependent = $(this),
|
|
||||||
dependency_country = $(this).closest(".panel-body, form").find('select[name$=country]'),
|
|
||||||
dependency_id_is_business_1 = $(this).closest(".panel-body, form").find('input[id$=id_is_business_1]'),
|
|
||||||
update = function (ev) {
|
|
||||||
if (dependency_id_is_business_1.length && !dependency_id_is_business_1.prop("checked")) {
|
|
||||||
dependent.closest(".form-group").hide();
|
|
||||||
} else if (dependent.attr('data-countries-with-vat-id').split(',').includes(dependency_country.val())) {
|
|
||||||
dependent.closest(".form-group").show();
|
|
||||||
} else {
|
|
||||||
dependent.closest(".form-group").hide();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
update();
|
|
||||||
dependency_country.on("change", update);
|
|
||||||
dependency_id_is_business_1.on("change", update);
|
|
||||||
});
|
|
||||||
|
|
||||||
$("select[name$=state]").each(function () {
|
|
||||||
var dependent = $(this),
|
|
||||||
counter = 0,
|
|
||||||
dependency = $(this).closest(".panel-body, form").find('select[name$=country]'),
|
|
||||||
update = function (ev) {
|
|
||||||
counter++;
|
|
||||||
var curCounter = counter;
|
|
||||||
dependent.prop("disabled", true);
|
|
||||||
dependency.closest(".form-group").find("label").prepend("<span class='fa fa-cog fa-spin'></span> ");
|
|
||||||
$.getJSON('/js_helpers/states/?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();
|
|
||||||
if (data.data.length > 0) {
|
|
||||||
$.each(data.data, function (k, s) {
|
|
||||||
var o = $("<option>").attr("value", s.code).text(s.name);
|
|
||||||
if (s.code == selected_value || (selected_value && selected_value.indexOf && selected_value.indexOf(s.code) > -1)) {
|
|
||||||
o.prop("selected", true);
|
|
||||||
}
|
|
||||||
dependent.append(o);
|
|
||||||
});
|
|
||||||
dependent.closest(".form-group").show();
|
|
||||||
dependent.prop('required', dependency.prop("required"));
|
|
||||||
} else {
|
|
||||||
dependent.closest(".form-group").hide();
|
|
||||||
dependent.prop("required", false);
|
|
||||||
}
|
|
||||||
dependent.prop("disabled", false);
|
|
||||||
dependency.closest(".form-group").find("label .fa-spin").remove();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
if (dependent.find("option").length === 1) {
|
|
||||||
dependent.closest(".form-group").hide();
|
|
||||||
} else {
|
|
||||||
dependent.prop('required', dependency.prop("required"));
|
|
||||||
}
|
|
||||||
dependency.on("change", update);
|
|
||||||
});
|
|
||||||
|
|
||||||
form_handlers($("body"));
|
form_handlers($("body"));
|
||||||
|
|
||||||
var local_tz = moment.tz.guess()
|
var local_tz = moment.tz.guess()
|
||||||
|
|||||||
@@ -1207,6 +1207,65 @@ class CheckoutTestCase(BaseCheckoutTestCase, TimemachineTestMixin, TestCase):
|
|||||||
}
|
}
|
||||||
assert ia.name_cached == 'Mr John Kennedy'
|
assert ia.name_cached == 'Mr John Kennedy'
|
||||||
|
|
||||||
|
def test_invoice_address_required_no_zipcode_country(self):
|
||||||
|
self.event.settings.invoice_address_asked = True
|
||||||
|
self.event.settings.invoice_address_required = True
|
||||||
|
self.event.settings.invoice_address_not_asked_free = True
|
||||||
|
self.event.settings.set('name_scheme', 'title_given_middle_family')
|
||||||
|
|
||||||
|
with scopes_disabled():
|
||||||
|
CartPosition.objects.create(
|
||||||
|
event=self.event, cart_id=self.session_key, item=self.ticket,
|
||||||
|
price=23, expires=now() + timedelta(minutes=10)
|
||||||
|
)
|
||||||
|
response = self.client.get('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), follow=True)
|
||||||
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
||||||
|
self.assertEqual(len(doc.select('input[name="city"]')), 1)
|
||||||
|
|
||||||
|
# Not all required fields filled out, expect failure
|
||||||
|
response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), {
|
||||||
|
'is_business': 'business',
|
||||||
|
'company': 'Foo',
|
||||||
|
'name_parts_0': 'Mr',
|
||||||
|
'name_parts_1': 'John',
|
||||||
|
'name_parts_2': '',
|
||||||
|
'name_parts_3': 'Kennedy',
|
||||||
|
'street': '',
|
||||||
|
'zipcode': '',
|
||||||
|
'city': '',
|
||||||
|
'country': 'BI',
|
||||||
|
'email': 'admin@localhost'
|
||||||
|
}, follow=True)
|
||||||
|
doc = BeautifulSoup(response.content.decode(), "lxml")
|
||||||
|
self.assertGreaterEqual(len(doc.select('.has-error')), 1)
|
||||||
|
|
||||||
|
# Correct request for a country where zip code is not required in address
|
||||||
|
response = self.client.post('/%s/%s/checkout/questions/' % (self.orga.slug, self.event.slug), {
|
||||||
|
'is_business': 'business',
|
||||||
|
'company': 'Foo',
|
||||||
|
'name_parts_0': 'Mr',
|
||||||
|
'name_parts_1': 'John',
|
||||||
|
'name_parts_2': '',
|
||||||
|
'name_parts_3': 'Kennedy',
|
||||||
|
'street': 'BP 12345',
|
||||||
|
'zipcode': '',
|
||||||
|
'city': 'Bujumbura',
|
||||||
|
'country': 'BI',
|
||||||
|
'email': 'admin@localhost'
|
||||||
|
}, follow=True)
|
||||||
|
self.assertRedirects(response, '/%s/%s/checkout/payment/' % (self.orga.slug, self.event.slug),
|
||||||
|
target_status_code=200)
|
||||||
|
with scopes_disabled():
|
||||||
|
ia = InvoiceAddress.objects.last()
|
||||||
|
assert ia.name_parts == {
|
||||||
|
'title': 'Mr',
|
||||||
|
'given_name': 'John',
|
||||||
|
'middle_name': '',
|
||||||
|
'family_name': 'Kennedy',
|
||||||
|
"_scheme": "title_given_middle_family"
|
||||||
|
}
|
||||||
|
assert ia.name_cached == 'Mr John Kennedy'
|
||||||
|
|
||||||
def test_invoice_address_validated(self):
|
def test_invoice_address_validated(self):
|
||||||
self.event.settings.invoice_address_asked = True
|
self.event.settings.invoice_address_asked = True
|
||||||
self.event.settings.invoice_address_required = True
|
self.event.settings.invoice_address_required = True
|
||||||
|
|||||||
Reference in New Issue
Block a user