Compare commits

...
Author SHA1 Message Date
Mira Weller eff815056b Fix test case 2024-11-29 14:37:37 +01:00
Mira Weller 45391b104a Require at least one of street, zipcode, city if invoice_address_required 2024-11-29 14:15:36 +01:00
Mira Weller 54d3d20d70 Fix typo, require at least one of the address parts 2024-11-29 14:12:22 +01:00
Mira Weller be1cb12286 Add test case for country without zip code 2024-11-29 13:49:47 +01:00
Mira Weller c65b7f711d Simplify value comparison (state multiselect is not supported anyway) 2024-11-29 13:34:06 +01:00
Mira Weller 8e3d5511e2 Make the test case happy 2024-11-29 13:18:09 +01:00
9acfcf2526 Apply suggestions from code review
Co-authored-by: Richard Schreiber <schreiber@rami.io>
2024-11-27 14:42:21 +01:00
Mira Weller 32fd9f2f4d Distinguish between addon product address forms in pretixcontrol 2024-11-25 21:53:16 +01:00
Mira Weller 47ec03baac use street instead of city as indicator whether address is filled 2024-11-25 21:46:48 +01:00
Mira Weller 04884ce874 lint 2024-11-25 21:38:56 +01:00
Mira Weller 3337783d1f Optional zipcodes/cities for attendee addresses as well 2024-11-25 21:30:54 +01:00
Mira Weller 391bbc25f8 Keep selected state on load 2024-11-25 21:30:15 +01:00
Mira Weller 68017cc8cb Always mark street address as required 2024-11-25 21:00:54 +01:00
Mira Weller 5fee16035f Distinguish between addon product address forms 2024-11-25 21:00:25 +01:00
Mira Weller 6d32aa5220 Load address format information when selecting country 2024-11-25 20:52:10 +01:00
Mira Weller 3461c59cfe Move countries-with-vat-id logic to addressform.js 2024-11-25 16:08:20 +01:00
Mira Weller 21b4dacee9 Correctly apply "required" attribute to address state field
(we try to use it from the country field, which is a <select> tag with default value and therefore never has a "required" attribute - we need to take the information from the .required class of the container)
2024-11-25 13:37:54 +01:00
Mira Weller 2663e9ff32 Move country-dependent state field JS logic to separate file
(avoids code duplication for presale and control)
2024-11-25 13:35:00 +01:00
a601c75923 CheckIns: Display a source_type icon (barcode/nfc) where known (#4628)
Co-authored-by: Raphael Michel <michel@rami.io>
2024-11-18 17:50:43 +01:00
Raphael MichelandRaphael Michel f94227f00f Translations: Update German (informal) (de_Informal)
Currently translated at 100.0% (5779 of 5779 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de_Informal/

powered by weblate
2024-11-18 17:49:24 +01:00
Raphael MichelandRaphael Michel a0c1e5369c Translations: Update German
Currently translated at 100.0% (5779 of 5779 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/de/

powered by weblate
2024-11-18 17:49:24 +01:00
Jakub StribrnyandRaphael Michel 633bfcf73a Translations: Update Czech
Currently translated at 73.5% (4253 of 5781 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/cs/

powered by weblate
2024-11-18 17:49:24 +01:00
CVZ-esandRaphael Michel 0d3b5b82c1 Translations: Update Spanish
Currently translated at 100.0% (232 of 232 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/es/

powered by weblate
2024-11-18 17:49:24 +01:00
CVZ-esandRaphael Michel ab95f33546 Translations: Update Spanish
Currently translated at 100.0% (5784 of 5784 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/es/

powered by weblate
2024-11-18 17:49:24 +01:00
5034b366c5 Stripe: Disable SOFORT (#4616)
* Stripe: Disable SOFORT

* Update src/pretix/plugins/stripe/payment.py

Co-authored-by: Raphael Michel <michel@rami.io>

---------

Co-authored-by: Martin Gross <gross@rami.io>
2024-11-18 17:24:01 +01:00
Raphael MichelandGitHub 03d3c389da Fix #1674 -- Change spelling of e-mail to email (#4636)
* Fix #1674 -- Change spelling of e-mail to email

* Conflicts and word list

* Add MobilePay to wordlist

* fix usage in tests
2024-11-18 17:21:29 +01:00
3e934acfa0 Allow "open in new tab" for event typeahead (#4631)
* Allow "open in new tab" for event typeahead

* use default link-behaviour for e.g. open in new ...

* navigate in typeahead with tab, add esc to close

---------

Co-authored-by: Richard Schreiber <schreiber@rami.io>
2024-11-18 17:16:02 +01:00
Raphael Michel d2a364e848 Docs: Fix documentation of invoice API 2024-11-18 17:08:51 +01:00
2824b40299 Stripe: Add MobilePay as a supported payment method (Z#23172523) (#4635)
Co-authored-by: robbi5 <richt@rami.io>
2024-11-18 15:03:32 +01:00
DamianoandRaphael Michel c6c2c90908 Translations: Update Italian
Currently translated at 24.3% (1405 of 5781 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/it/

powered by weblate
2024-11-18 10:48:18 +01:00
Pavle ErgovićandRaphael Michel d4ae7df2ec Translations: Update Croatian
Currently translated at 1.7% (4 of 232 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/hr/

powered by weblate
2024-11-18 10:48:18 +01:00
Pavle ErgovićandRaphael Michel 79dd7fb596 Translations: Update Croatian
Currently translated at 0.3% (23 of 5781 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/hr/

powered by weblate
2024-11-18 10:48:18 +01:00
CVZ-esandRaphael Michel 5ed87cd019 Translations: Update Spanish
Currently translated at 100.0% (5784 of 5784 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/es/

powered by weblate
2024-11-18 10:48:18 +01:00
CVZ-esandRaphael Michel ccdcbe0cc5 Translations: Update French
Currently translated at 100.0% (232 of 232 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/fr/

powered by weblate
2024-11-18 10:48:18 +01:00
CVZ-esandRaphael Michel 4f8607a9db Translations: Update French
Currently translated at 100.0% (5781 of 5781 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/fr/

powered by weblate
2024-11-18 10:48:18 +01:00
Patrick ChiltonandRaphael Michel 57ecaa2676 Translations: Update Hungarian
Currently translated at 10.7% (622 of 5781 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/hu/

powered by weblate
2024-11-18 10:48:18 +01:00
CVZ-esandRaphael Michel 96fd2b1a95 Translations: Update Spanish
Currently translated at 100.0% (232 of 232 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/es/

powered by weblate
2024-11-18 10:48:18 +01:00
CVZ-esandRaphael Michel 5cf24fb6a6 Translations: Update Spanish
Currently translated at 100.0% (5784 of 5784 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/es/

powered by weblate
2024-11-18 10:48:18 +01:00
Eva-Maria ObermannandRaphael Michel 1d2ea35a39 Translations: Update French
Currently translated at 100.0% (232 of 232 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/fr/

powered by weblate
2024-11-18 10:48:18 +01:00
CVZ-esandRaphael Michel ac98ae7941 Translations: Update French
Currently translated at 100.0% (232 of 232 strings)

Translation: pretix/pretix (JavaScript parts)
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix-js/fr/

powered by weblate
2024-11-18 10:48:18 +01:00
CVZ-esandRaphael Michel a0d055e202 Translations: Update French
Currently translated at 99.3% (5742 of 5781 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/fr/

powered by weblate
2024-11-18 10:48:18 +01:00
Raphael MichelandGitHub 27ec5ca006 Change order of menu items in backend (#4633) 2024-11-18 10:45:27 +01:00
134 changed files with 16236 additions and 15557 deletions
+7 -7
View File
@@ -352,12 +352,12 @@ Fetching individual invoices
:param organizer: The ``slug`` field of the organizer to fetch :param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch :param event: The ``slug`` field of the event to fetch
:param invoice_no: The ``invoice_no`` field of the invoice to fetch :param number: The ``number`` field of the invoice to fetch
:statuscode 200: no error :statuscode 200: no error
:statuscode 401: Authentication failure :statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource. :statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/invoices/(invoice_no)/download/ .. http:get:: /api/v1/organizers/(organizer)/events/(event)/invoices/(number)/download/
Download an invoice in PDF format. Download an invoice in PDF format.
@@ -384,7 +384,7 @@ Fetching individual invoices
:param organizer: The ``slug`` field of the organizer to fetch :param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch :param event: The ``slug`` field of the event to fetch
:param invoice_no: The ``invoice_no`` field of the invoice to fetch :param number: The ``number`` field of the invoice to fetch
:statuscode 200: no error :statuscode 200: no error
:statuscode 401: Authentication failure :statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource. :statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
@@ -397,7 +397,7 @@ Modifying invoices
Invoices cannot be edited directly, but the following actions can be triggered: Invoices cannot be edited directly, but the following actions can be triggered:
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/invoices/(invoice_no)/reissue/ .. http:post:: /api/v1/organizers/(organizer)/events/(event)/invoices/(number)/reissue/
Cancels the invoice and creates a new one. Cancels the invoice and creates a new one.
@@ -419,13 +419,13 @@ Invoices cannot be edited directly, but the following actions can be triggered:
:param organizer: The ``slug`` field of the organizer to fetch :param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch :param event: The ``slug`` field of the event to fetch
:param invoice_no: The ``invoice_no`` field of the invoice to reissue :param number: The ``number`` field of the invoice to reissue
:statuscode 200: no error :statuscode 200: no error
:statuscode 400: The invoice has already been canceled :statuscode 400: The invoice has already been canceled
:statuscode 401: Authentication failure :statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to change this resource. :statuscode 403: The requested organizer/event does not exist **or** you have no permission to change this resource.
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/invoices/(invoice_no)/regenerate/ .. http:post:: /api/v1/organizers/(organizer)/events/(event)/invoices/(number)/regenerate/
Re-generates the invoice from order data. Re-generates the invoice from order data.
@@ -447,7 +447,7 @@ Invoices cannot be edited directly, but the following actions can be triggered:
:param organizer: The ``slug`` field of the organizer to fetch :param organizer: The ``slug`` field of the organizer to fetch
:param event: The ``slug`` field of the event to fetch :param event: The ``slug`` field of the event to fetch
:param invoice_no: The ``invoice_no`` field of the invoice to regenerate :param number: The ``number`` field of the invoice to regenerate
:statuscode 200: no error :statuscode 200: no error
:statuscode 400: The invoice has already been canceled :statuscode 400: The invoice has already been canceled
:statuscode 401: Authentication failure :statuscode 401: Authentication failure
+1 -1
View File
@@ -152,7 +152,7 @@ class NativeAuthBackend(BaseAuthBackend):
to log in. to log in.
""" """
d = OrderedDict([ d = OrderedDict([
('email', forms.EmailField(label=_("E-mail"), max_length=254, ('email', forms.EmailField(label=_("Email"), max_length=254,
widget=forms.EmailInput(attrs={'autofocus': 'autofocus'}))), widget=forms.EmailInput(attrs={'autofocus': 'autofocus'}))),
('password', forms.CharField(label=_("Password"), widget=forms.PasswordInput, ('password', forms.CharField(label=_("Password"), widget=forms.PasswordInput,
max_length=4096)), max_length=4096)),
+1 -1
View File
@@ -68,7 +68,7 @@ def test_custom_smtp_backend(backend: T, from_addr: str) -> None:
class BaseHTMLMailRenderer: class BaseHTMLMailRenderer:
""" """
This is the base class for all HTML e-mail renderers. This is the base class for all HTML email renderers.
""" """
def __init__(self, event: Event, organizer=None): def __init__(self, event: Event, organizer=None):
+1 -1
View File
@@ -64,7 +64,7 @@ class CustomerListExporter(OrganizerLevelExportMixin, ListExporter):
_('Customer ID'), _('Customer ID'),
_('SSO provider'), _('SSO provider'),
_('External identifier'), _('External identifier'),
_('E-mail'), _('Email'),
_('Phone number'), _('Phone number'),
_('Full name'), _('Full name'),
] ]
+2 -2
View File
@@ -199,7 +199,7 @@ class InvoiceDataExporter(InvoiceExporterMixin, MultiSheetListExporter):
_('Invoice number'), _('Invoice number'),
_('Date'), _('Date'),
_('Order code'), _('Order code'),
_('E-mail address'), _('Email address'),
_('Invoice type'), _('Invoice type'),
_('Cancellation of'), _('Cancellation of'),
_('Language'), _('Language'),
@@ -326,7 +326,7 @@ class InvoiceDataExporter(InvoiceExporterMixin, MultiSheetListExporter):
_('Event start date'), _('Event start date'),
_('Date'), _('Date'),
_('Order code'), _('Order code'),
_('E-mail address'), _('Email address'),
_('Invoice type'), _('Invoice type'),
_('Cancellation of'), _('Cancellation of'),
_('Invoice sender:') + ' ' + _('Name'), _('Invoice sender:') + ' ' + _('Name'),
+2 -2
View File
@@ -284,7 +284,7 @@ class OrderListExporter(MultiSheetListExporter):
headers.append(_('Comment')) headers.append(_('Comment'))
headers.append(_('Follow-up date')) headers.append(_('Follow-up date'))
headers.append(_('Positions')) headers.append(_('Positions'))
headers.append(_('E-mail address verified')) headers.append(_('Email address verified'))
headers.append(_('External customer ID')) headers.append(_('External customer ID'))
headers.append(_('Payment providers')) headers.append(_('Payment providers'))
if form_data.get('include_payment_amounts'): if form_data.get('include_payment_amounts'):
@@ -655,7 +655,7 @@ class OrderListExporter(MultiSheetListExporter):
headers += [ headers += [
_('Sales channel'), _('Sales channel'),
_('Order locale'), _('Order locale'),
_('E-mail address verified'), _('Email address verified'),
_('External customer ID'), _('External customer ID'),
_('Check-in lists'), _('Check-in lists'),
_('Payment providers'), _('Payment providers'),
+1 -1
View File
@@ -254,7 +254,7 @@ class PasswordRecoverForm(forms.Form):
class PasswordForgotForm(forms.Form): class PasswordForgotForm(forms.Form):
email = forms.EmailField( email = forms.EmailField(
label=_('E-mail'), label=_('Email'),
) )
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
+20 -10
View File
@@ -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
+3 -3
View File
@@ -48,10 +48,10 @@ from pretix.control.forms import SingleLanguageWidget
class UserSettingsForm(forms.ModelForm): class UserSettingsForm(forms.ModelForm):
error_messages = { error_messages = {
'duplicate_identifier': _("There already is an account associated with this e-mail address. " 'duplicate_identifier': _("There already is an account associated with this email address. "
"Please choose a different one."), "Please choose a different one."),
'pw_current': _("Please enter your current password if you want to change your e-mail " 'pw_current': _("Please enter your current password if you want to change your email address "
"address or password."), "or password."),
'pw_current_wrong': _("The current password you entered was not correct."), 'pw_current_wrong': _("The current password you entered was not correct."),
'pw_mismatch': _("Please enter the same password twice"), 'pw_mismatch': _("Please enter the same password twice"),
'rate_limit': _("For security reasons, please wait 5 minutes before you try again."), 'rate_limit': _("For security reasons, please wait 5 minutes before you try again."),
+13
View File
@@ -37,6 +37,16 @@ class BaseMediaType:
def verbose_name(self): def verbose_name(self):
raise NotImplementedError() raise NotImplementedError()
@property
def icon(self):
"""
This can be:
- The name of a Font Awesome icon to represent this channel type.
- The name of a SVG icon file that is resolvable through the static file system. We recommend to design for a size of 18x14 pixels.
"""
return "circle"
def generate_identifier(self, organizer): def generate_identifier(self, organizer):
if self.medium_created_by_server: if self.medium_created_by_server:
raise NotImplementedError() raise NotImplementedError()
@@ -59,6 +69,7 @@ class BaseMediaType:
class BarcodePlainMediaType(BaseMediaType): class BarcodePlainMediaType(BaseMediaType):
identifier = 'barcode' identifier = 'barcode'
verbose_name = _('Barcode / QR-Code') verbose_name = _('Barcode / QR-Code')
icon = 'qrcode'
medium_created_by_server = True medium_created_by_server = True
supports_giftcard = False supports_giftcard = False
supports_orderposition = True supports_orderposition = True
@@ -75,6 +86,7 @@ class BarcodePlainMediaType(BaseMediaType):
class NfcUidMediaType(BaseMediaType): class NfcUidMediaType(BaseMediaType):
identifier = 'nfc_uid' identifier = 'nfc_uid'
verbose_name = _('NFC UID-based') verbose_name = _('NFC UID-based')
icon = 'pretixbase/img/media/nfc_uid.svg'
medium_created_by_server = False medium_created_by_server = False
supports_giftcard = True supports_giftcard = True
supports_orderposition = False supports_orderposition = False
@@ -114,6 +126,7 @@ class NfcUidMediaType(BaseMediaType):
class NfcMf0aesMediaType(BaseMediaType): class NfcMf0aesMediaType(BaseMediaType):
identifier = 'nfc_mf0aes' identifier = 'nfc_mf0aes'
verbose_name = 'NFC Mifare Ultralight AES' verbose_name = 'NFC Mifare Ultralight AES'
icon = 'pretixbase/img/media/nfc_secure.svg'
medium_created_by_server = False medium_created_by_server = False
supports_giftcard = True supports_giftcard = True
supports_orderposition = False supports_orderposition = False
+1 -1
View File
@@ -29,7 +29,7 @@ class Migration(migrations.Migration):
('password', models.CharField(verbose_name='password', max_length=128)), ('password', models.CharField(verbose_name='password', max_length=128)),
('last_login', models.DateTimeField(verbose_name='last login', blank=True, null=True)), ('last_login', models.DateTimeField(verbose_name='last login', blank=True, null=True)),
('is_superuser', models.BooleanField(verbose_name='superuser status', default=False, help_text='Designates that this user has all permissions without explicitly assigning them.')), ('is_superuser', models.BooleanField(verbose_name='superuser status', default=False, help_text='Designates that this user has all permissions without explicitly assigning them.')),
('email', models.EmailField(max_length=191, blank=True, unique=True, verbose_name='E-mail', null=True, ('email', models.EmailField(max_length=191, blank=True, unique=True, verbose_name='Email', null=True,
db_index=True)), db_index=True)),
('givenname', models.CharField(verbose_name='Given name', max_length=255, blank=True, null=True)), ('givenname', models.CharField(verbose_name='Given name', max_length=255, blank=True, null=True)),
('familyname', models.CharField(verbose_name='Family name', max_length=255, blank=True, null=True)), ('familyname', models.CharField(verbose_name='Family name', max_length=255, blank=True, null=True)),
@@ -48,7 +48,7 @@ class Migration(migrations.Migration):
('password', models.CharField(max_length=128, verbose_name='password')), ('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('email', models.EmailField(blank=True, db_index=True, max_length=254, null=True, unique=True, verbose_name='E-mail')), ('email', models.EmailField(blank=True, db_index=True, max_length=254, null=True, unique=True, verbose_name='Email')),
('givenname', models.CharField(blank=True, max_length=255, null=True, verbose_name='Given name')), ('givenname', models.CharField(blank=True, max_length=255, null=True, verbose_name='Given name')),
('familyname', models.CharField(blank=True, max_length=255, null=True, verbose_name='Family name')), ('familyname', models.CharField(blank=True, max_length=255, null=True, verbose_name='Family name')),
('is_active', models.BooleanField(default=True, verbose_name='Is active')), ('is_active', models.BooleanField(default=True, verbose_name='Is active')),
@@ -232,7 +232,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(max_length=16, verbose_name='Order code')), ('code', models.CharField(max_length=16, verbose_name='Order code')),
('status', models.CharField(choices=[('n', 'pending'), ('p', 'paid'), ('e', 'expired'), ('c', 'cancelled'), ('r', 'refunded')], max_length=3, verbose_name='Status')), ('status', models.CharField(choices=[('n', 'pending'), ('p', 'paid'), ('e', 'expired'), ('c', 'cancelled'), ('r', 'refunded')], max_length=3, verbose_name='Status')),
('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='E-mail')), ('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='Email')),
('locale', models.CharField(blank=True, max_length=32, null=True, verbose_name='Locale')), ('locale', models.CharField(blank=True, max_length=32, null=True, verbose_name='Locale')),
('secret', models.CharField(default=pretix.base.models.orders.generate_secret, max_length=32)), ('secret', models.CharField(default=pretix.base.models.orders.generate_secret, max_length=32)),
('datetime', models.DateTimeField(verbose_name='Date')), ('datetime', models.DateTimeField(verbose_name='Date')),
@@ -187,7 +187,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(max_length=16, verbose_name='Order code')), ('code', models.CharField(max_length=16, verbose_name='Order code')),
('status', models.CharField(choices=[('n', 'pending'), ('p', 'paid'), ('e', 'expired'), ('c', 'cancelled'), ('r', 'refunded')], max_length=3, verbose_name='Status')), ('status', models.CharField(choices=[('n', 'pending'), ('p', 'paid'), ('e', 'expired'), ('c', 'cancelled'), ('r', 'refunded')], max_length=3, verbose_name='Status')),
('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='E-mail')), ('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='Email')),
('locale', models.CharField(blank=True, max_length=32, null=True, verbose_name='Locale')), ('locale', models.CharField(blank=True, max_length=32, null=True, verbose_name='Locale')),
('secret', models.CharField(default=pretix.base.models.orders.generate_secret, max_length=32)), ('secret', models.CharField(default=pretix.base.models.orders.generate_secret, max_length=32)),
('datetime', models.DateTimeField(verbose_name='Date')), ('datetime', models.DateTimeField(verbose_name='Date')),
@@ -20,7 +20,7 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True, verbose_name='On waiting list since')), ('created', models.DateTimeField(auto_now_add=True, verbose_name='On waiting list since')),
('email', models.EmailField(max_length=254, verbose_name='E-mail address')), ('email', models.EmailField(max_length=254, verbose_name='Email address')),
('locale', models.CharField(default='en', max_length=190)), ('locale', models.CharField(default='en', max_length=190)),
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='waitinglistentries', to='pretixbase.Event', verbose_name='Event')), ('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='waitinglistentries', to='pretixbase.Event', verbose_name='Event')),
('item', models.ForeignKey(help_text='The product the user waits for.', on_delete=django.db.models.deletion.CASCADE, related_name='waitinglistentries', to='pretixbase.Item', verbose_name='Product')), ('item', models.ForeignKey(help_text='The product the user waits for.', on_delete=django.db.models.deletion.CASCADE, related_name='waitinglistentries', to='pretixbase.Item', verbose_name='Product')),
@@ -35,7 +35,7 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True, verbose_name='On waiting list since')), ('created', models.DateTimeField(auto_now_add=True, verbose_name='On waiting list since')),
('email', models.EmailField(max_length=254, verbose_name='E-mail address')), ('email', models.EmailField(max_length=254, verbose_name='Email address')),
('locale', models.CharField(default='en', max_length=190)), ('locale', models.CharField(default='en', max_length=190)),
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='waitinglistentries', to='pretixbase.Event', verbose_name='Event')), ('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='waitinglistentries', to='pretixbase.Event', verbose_name='Event')),
('item', models.ForeignKey(help_text='The product the user waits for.', on_delete=django.db.models.deletion.CASCADE, related_name='waitinglistentries', to='pretixbase.Item', verbose_name='Product')), ('item', models.ForeignKey(help_text='The product the user waits for.', on_delete=django.db.models.deletion.CASCADE, related_name='waitinglistentries', to='pretixbase.Item', verbose_name='Product')),
@@ -163,7 +163,7 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('action_type', models.CharField(max_length=255)), ('action_type', models.CharField(max_length=255)),
('method', models.CharField(choices=[('mail', 'E-mail')], max_length=255)), ('method', models.CharField(choices=[('mail', 'Email')], max_length=255)),
('event', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, ('event', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE,
to='pretixbase.Event')), to='pretixbase.Event')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
@@ -21,7 +21,7 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('action_type', models.CharField(max_length=255)), ('action_type', models.CharField(max_length=255)),
('method', models.CharField(choices=[('mail', 'E-mail')], max_length=255)), ('method', models.CharField(choices=[('mail', 'Email')], max_length=255)),
('event', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='pretixbase.Event')), ('event', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='pretixbase.Event')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
('enabled', models.BooleanField(default=True)), ('enabled', models.BooleanField(default=True)),
+2 -2
View File
@@ -56,7 +56,7 @@ from pretix.base.signals import order_import_columns
class EmailColumn(ImportColumn): class EmailColumn(ImportColumn):
identifier = 'email' identifier = 'email'
verbose_name = gettext_lazy('E-mail address') verbose_name = gettext_lazy('Email address')
def clean(self, value, previous_values): def clean(self, value, previous_values):
if value: if value:
@@ -322,7 +322,7 @@ class AttendeeNamePart(ImportColumn):
class AttendeeEmail(ImportColumn): class AttendeeEmail(ImportColumn):
identifier = 'attendee_email' identifier = 'attendee_email'
verbose_name = gettext_lazy('Attendee e-mail address') verbose_name = gettext_lazy('Attendee email address')
def clean(self, value, previous_values): def clean(self, value, previous_values):
if value: if value:
+1 -1
View File
@@ -241,7 +241,7 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin):
REQUIRED_FIELDS = [] REQUIRED_FIELDS = []
email = models.EmailField(unique=True, db_index=True, null=True, blank=True, email = models.EmailField(unique=True, db_index=True, null=True, blank=True,
verbose_name=_('E-mail'), max_length=190) verbose_name=_('Email'), max_length=190)
fullname = models.CharField(max_length=255, blank=True, null=True, fullname = models.CharField(max_length=255, blank=True, null=True,
verbose_name=_('Full name')) verbose_name=_('Full name'))
is_active = models.BooleanField(default=True, is_active = models.BooleanField(default=True,
+2 -2
View File
@@ -91,7 +91,7 @@ class Customer(LoggedModel):
), ),
], ],
) )
email = models.EmailField(db_index=True, null=True, blank=False, verbose_name=_('E-mail'), max_length=190) email = models.EmailField(db_index=True, null=True, blank=False, verbose_name=_('Email'), max_length=190)
phone = PhoneNumberField(null=True, blank=True, verbose_name=_('Phone number')) phone = PhoneNumberField(null=True, blank=True, verbose_name=_('Phone number'))
password = models.CharField(verbose_name=_('Password'), max_length=128) password = models.CharField(verbose_name=_('Password'), max_length=128)
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)
@@ -392,7 +392,7 @@ class CustomerSSOClient(LoggedModel):
SCOPE_CHOICES = ( SCOPE_CHOICES = (
('openid', _('OpenID Connect access (required)')), ('openid', _('OpenID Connect access (required)')),
('profile', _('Profile data (name, addresses)')), ('profile', _('Profile data (name, addresses)')),
('email', _('E-mail address')), ('email', _('Email address')),
('phone', _('Phone number')), ('phone', _('Phone number')),
) )
+1 -1
View File
@@ -43,7 +43,7 @@ class NotificationSetting(models.Model):
:type enabled: bool :type enabled: bool
""" """
CHANNELS = ( CHANNELS = (
('mail', _('E-mail')), ('mail', _('Email')),
) )
user = models.ForeignKey('User', on_delete=models.CASCADE, user = models.ForeignKey('User', on_delete=models.CASCADE,
related_name='notification_settings') related_name='notification_settings')
+5 -5
View File
@@ -242,7 +242,7 @@ class Order(LockModel, LoggedModel):
) )
email = models.EmailField( email = models.EmailField(
null=True, blank=True, null=True, blank=True,
verbose_name=_('E-mail') verbose_name=_('Email')
) )
phone = PhoneNumberField( phone = PhoneNumberField(
null=True, blank=True, null=True, blank=True,
@@ -317,7 +317,7 @@ class Order(LockModel, LoggedModel):
) )
email_known_to_work = models.BooleanField( email_known_to_work = models.BooleanField(
default=False, default=False,
verbose_name=_('E-mail address verified') verbose_name=_('Email address verified')
) )
invoice_dirty = models.BooleanField( invoice_dirty = models.BooleanField(
# Invoice needs to be re-issued when the order is paid again # Invoice needs to be re-issued when the order is paid again
@@ -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)
+1 -1
View File
@@ -73,7 +73,7 @@ class WaitingListEntry(LoggedModel):
blank=True, default=dict blank=True, default=dict
) )
email = models.EmailField( email = models.EmailField(
verbose_name=_("E-mail address") verbose_name=_("Email address")
) )
phone = PhoneNumberField( phone = PhoneNumberField(
null=True, blank=True, null=True, blank=True,
+1 -1
View File
@@ -315,7 +315,7 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La
body_html = renderer.render(content_plain, signature, raw_subject, order, position) body_html = renderer.render(content_plain, signature, raw_subject, order, position)
else: else:
# Backwards compatibility # Backwards compatibility
warnings.warn('E-mail renderer called without position argument because position argument is not ' warnings.warn('Email renderer called without position argument because position argument is not '
'supported.', 'supported.',
DeprecationWarning) DeprecationWarning)
body_html = renderer.render(content_plain, signature, raw_subject, order) body_html = renderer.render(content_plain, signature, raw_subject, order)
+2 -2
View File
@@ -287,9 +287,9 @@ class PhoneNumberShredder(BaseDataShredder):
class EmailAddressShredder(BaseDataShredder): class EmailAddressShredder(BaseDataShredder):
verbose_name = _('E-mails') verbose_name = _('Emails')
identifier = 'order_emails' identifier = 'order_emails'
description = _('This will remove all e-mail addresses from orders and attendees, as well as logged email ' description = _('This will remove all email addresses from orders and attendees, as well as logged email '
'contents. This will also remove the association to customer accounts.') 'contents. This will also remove the association to customer accounts.')
def generate_files(self) -> List[Tuple[str, str, str]]: def generate_files(self) -> List[Tuple[str, str, str]]:
+19 -5
View File
@@ -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,
})
+3 -3
View File
@@ -549,7 +549,7 @@ class EventOrderExpertFilterForm(EventOrderFilterForm):
) )
email = forms.CharField( email = forms.CharField(
required=False, required=False,
label=_('E-mail address') label=_('Email address')
) )
comment = forms.CharField( comment = forms.CharField(
required=False, required=False,
@@ -563,7 +563,7 @@ class EventOrderExpertFilterForm(EventOrderFilterForm):
email_known_to_work = forms.NullBooleanField( email_known_to_work = forms.NullBooleanField(
required=False, required=False,
widget=FilterNullBooleanSelect, widget=FilterNullBooleanSelect,
label=_('E-mail address verified'), label=_('Email address verified'),
) )
total = forms.DecimalField( total = forms.DecimalField(
localize=True, localize=True,
@@ -648,7 +648,7 @@ class EventOrderExpertFilterForm(EventOrderFilterForm):
) )
self.fields['attendee_email'] = forms.CharField( self.fields['attendee_email'] = forms.CharField(
required=False, required=False,
label=_('Attendee e-mail address') label=_('Attendee email address')
) )
self.fields['attendee_address_company'] = forms.CharField( self.fields['attendee_address_company'] = forms.CharField(
required=False, required=False,
+1 -1
View File
@@ -128,7 +128,7 @@ class UpdateSettingsForm(SettingsForm):
) )
update_check_email = forms.EmailField( update_check_email = forms.EmailField(
required=False, required=False,
label=_("E-mail notifications"), label=_("Email notifications"),
help_text=_("We will notify you at this address if we detect that a new update is available. This " help_text=_("We will notify you at this address if we detect that a new update is available. This "
"address will not be transmitted to pretix.eu, the emails will be sent by this server " "address will not be transmitted to pretix.eu, the emails will be sent by this server "
"locally.") "locally.")
+1 -1
View File
@@ -40,7 +40,7 @@ class StaffSessionForm(forms.ModelForm):
class UserEditForm(forms.ModelForm): class UserEditForm(forms.ModelForm):
error_messages = { error_messages = {
'duplicate_identifier': _("There already is an account associated with this e-mail address. " 'duplicate_identifier': _("There already is an account associated with this email address. "
"Please choose a different one."), "Please choose a different one."),
'pw_mismatch': _("Please enter the same password twice"), 'pw_mismatch': _("Please enter the same password twice"),
} }
+14 -12
View File
@@ -78,7 +78,7 @@ def get_event_navigation(request: HttpRequest):
'active': url.url_name == 'event.settings.tickets', 'active': url.url_name == 'event.settings.tickets',
}, },
{ {
'label': _('E-mail'), 'label': _('Email'),
'url': reverse('control:event.settings.mail', kwargs={ 'url': reverse('control:event.settings.mail', kwargs={
'event': request.event.slug, 'event': request.event.slug,
'organizer': request.event.organizer.slug, 'organizer': request.event.organizer.slug,
@@ -132,16 +132,6 @@ def get_event_navigation(request: HttpRequest):
'icon': 'wrench', 'icon': 'wrench',
'children': event_settings 'children': event_settings
}) })
if request.event.has_subevents:
nav.append({
'label': pgettext_lazy('subevent', 'Dates'),
'url': reverse('control:event.subevents', kwargs={
'event': request.event.slug,
'organizer': request.event.organizer.slug,
}),
'active': ('event.subevent' in url.url_name),
'icon': 'calendar',
})
if 'can_change_items' in request.eventpermset: if 'can_change_items' in request.eventpermset:
nav.append({ nav.append({
@@ -197,6 +187,18 @@ def get_event_navigation(request: HttpRequest):
] ]
}) })
if 'can_change_event_settings' in request.eventpermset:
if request.event.has_subevents:
nav.append({
'label': pgettext_lazy('subevent', 'Dates'),
'url': reverse('control:event.subevents', kwargs={
'event': request.event.slug,
'organizer': request.event.organizer.slug,
}),
'active': ('event.subevent' in url.url_name),
'icon': 'calendar',
})
if 'can_view_orders' in request.eventpermset: if 'can_view_orders' in request.eventpermset:
children = [ children = [
{ {
@@ -496,7 +498,7 @@ def get_organizer_navigation(request):
'active': url.url_name.startswith('organizer.propert'), 'active': url.url_name.startswith('organizer.propert'),
}, },
{ {
'label': _('E-mail'), 'label': _('Email'),
'url': reverse('control:organizer.settings.mail', kwargs={ 'url': reverse('control:organizer.settings.mail', kwargs={
'organizer': request.organizer.slug, 'organizer': request.organizer.slug,
}), }),
@@ -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 }}
@@ -127,6 +127,7 @@
<strong> <strong>
<a href="{% url "control:event.order" event=request.event.slug organizer=request.event.organizer.slug code=c.position.order.code %}">{{ c.position.order.code }}</a>-{{ c.position.positionid }} <a href="{% url "control:event.order" event=request.event.slug organizer=request.event.organizer.slug code=c.position.order.code %}">{{ c.position.order.code }}</a>-{{ c.position.positionid }}
</strong> </strong>
{% include "pretixcontrol/checkin/fragment_checkin_source_type.html" with source_type=c.raw_source_type %}
{% if c.position.attendee_name %} {% if c.position.attendee_name %}
<br> <br>
<small> <small>
@@ -143,7 +144,7 @@
</small> </small>
{% endif %} {% endif %}
{% else %} {% else %}
<span class="fa fa-qrcode fa-fw"></span> {% include "pretixcontrol/checkin/fragment_checkin_source_type.html" with source_type=c.raw_source_type %}
<span title="{{ c.raw_barcode }}"> <span title="{{ c.raw_barcode }}">
{{ c.raw_barcode|slice:":16" }}{% if c.raw_barcode|length > 16 %}…{% endif %} {{ c.raw_barcode|slice:":16" }}{% if c.raw_barcode|length > 16 %}…{% endif %}
<button type="button" class="btn btn-xs btn-link btn-clipboard" data-clipboard-text="{{ c.raw_barcode }}"> <button type="button" class="btn btn-xs btn-link btn-clipboard" data-clipboard-text="{{ c.raw_barcode }}">
@@ -0,0 +1,15 @@
{% load i18n %}
{% load static %}
{% load getitem %}
{% if source_type %}
{% with media_types|getitem:source_type as media_type %}
{% if "." in media_type.icon %}
<img src="{% static media_type.icon %}" class="fa-like-image"
data-toggle="tooltip" title="{{ media_type.verbose_name }}">
{% else %}
<span class="fa fa-fw fa-{{ media_type.icon }} text-muted"
data-toggle="tooltip" title="{{ media_type.verbose_name }}"></span>
{% endif %}
{% endwith %}
{% endif %}
@@ -185,6 +185,7 @@
<span class="fa fa-magic text-muted" <span class="fa fa-magic text-muted"
data-toggle="tooltip" title="{% trans "Checked in automatically" %}"></span> data-toggle="tooltip" title="{% trans "Checked in automatically" %}"></span>
{% endif %} {% endif %}
{% include "pretixcontrol/checkin/fragment_checkin_source_type.html" with source_type=e.last_entry_source_type %}
{% endif %} {% endif %}
{% endif %} {% endif %}
</td> </td>
@@ -5,7 +5,7 @@
{% load static %} {% load static %}
{% block title %}{% trans "Organizer" %}{% endblock %} {% block title %}{% trans "Organizer" %}{% endblock %}
{% block content %} {% block content %}
<h1>{% trans "E-mail sending" %}</h1> <h1>{% trans "Email sending" %}</h1>
<form action="" method="post" class="form-horizontal"> <form action="" method="post" class="form-horizontal">
{% csrf_token %} {% csrf_token %}
<div class="panel-group" id="email"> <div class="panel-group" id="email">
@@ -27,7 +27,7 @@
<div class="panel-body form-horizontal"> <div class="panel-body form-horizontal">
<p> <p>
{% blocktrans trimmed %} {% blocktrans trimmed %}
E-mails will be sent through the system's default server. They will show the following Emails will be sent through the system's default server. They will show the following
sender information: sender information:
{% endblocktrans %} {% endblocktrans %}
</p> </p>
@@ -62,7 +62,7 @@
<div class="panel-body form-horizontal"> <div class="panel-body form-horizontal">
<p> <p>
{% blocktrans trimmed %} {% blocktrans trimmed %}
E-mails will be sent through the system's default server but with your own sender Emails will be sent through the system's default server but with your own sender
address. address.
This will make your emails look more personalized and coming directly from you, but it This will make your emails look more personalized and coming directly from you, but it
also might require some extra steps to ensure good deliverability. also might require some extra steps to ensure good deliverability.
@@ -5,7 +5,7 @@
{% load static %} {% load static %}
{% block title %}{% trans "Organizer" %}{% endblock %} {% block title %}{% trans "Organizer" %}{% endblock %}
{% block content %} {% block content %}
<h1>{% trans "E-mail sending" %}</h1> <h1>{% trans "Email sending" %}</h1>
<form action="" method="post" class="form-horizontal"> <form action="" method="post" class="form-horizontal">
{% csrf_token %} {% csrf_token %}
{% for k, v in request.POST.items %} {% for k, v in request.POST.items %}
@@ -5,7 +5,7 @@
{% load static %} {% load static %}
{% block title %}{% trans "Organizer" %}{% endblock %} {% block title %}{% trans "Organizer" %}{% endblock %}
{% block content %} {% block content %}
<h1>{% trans "E-mail sending" %}</h1> <h1>{% trans "Email sending" %}</h1>
<form action="" method="post" class="form-horizontal"> <form action="" method="post" class="form-horizontal">
{% csrf_token %} {% csrf_token %}
{% for k, v in request.POST.items %} {% for k, v in request.POST.items %}
@@ -4,7 +4,7 @@
{% load hierarkey_form %} {% load hierarkey_form %}
{% load static %} {% load static %}
{% block inside %} {% block inside %}
<h1>{% trans "E-mail settings" %}</h1> <h1>{% trans "Email settings" %}</h1>
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data" <form action="" method="post" class="form-horizontal" enctype="multipart/form-data"
mail-preview-url="{% url "control:event.settings.mail.preview" event=request.event.slug organizer=request.event.organizer.slug %}"> mail-preview-url="{% url "control:event.settings.mail.preview" event=request.event.slug organizer=request.event.organizer.slug %}">
{% csrf_token %} {% csrf_token %}
@@ -63,7 +63,7 @@
{% bootstrap_field form.mail_attach_ical_description layout="control" %} {% bootstrap_field form.mail_attach_ical_description layout="control" %}
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>{% trans "E-mail design" %}</legend> <legend>{% trans "Email design" %}</legend>
<div class="row"> <div class="row">
{% for r in renderers.values %} {% for r in renderers.values %}
<div class="col-md-3"> <div class="col-md-3">
@@ -84,7 +84,7 @@
</div> </div>
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>{% trans "E-mail content" %}</legend> <legend>{% trans "Email content" %}</legend>
<h4>{% trans "Text" %}</h4> <h4>{% trans "Text" %}</h4>
<div class="panel-group" id="questions_group"> <div class="panel-group" id="questions_group">
{% blocktrans asvar title_placed_order %}Placed order{% endblocktrans %} {% blocktrans asvar title_placed_order %}Placed order{% endblocktrans %}
@@ -67,7 +67,7 @@
<h4>{% trans "Customer data (once per order)" %}</h4> <h4>{% trans "Customer data (once per order)" %}</h4>
<div class="form-group"> <div class="form-group">
<label class="control-label col-md-3"> <label class="control-label col-md-3">
{% trans "E-mail" %} {% trans "Email" %}
</label> </label>
<div class="col-md-9"> <div class="col-md-9">
<div class="checkbox"> <div class="checkbox">
@@ -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>
@@ -24,7 +24,7 @@
{% endif %} {% endif %}
{% if request.method == "POST" %} {% if request.method == "POST" %}
<fieldset> <fieldset>
<legend>{% trans "E-mail preview" %}</legend> <legend>{% trans "Email preview" %}</legend>
<div class="tab-pane mail-preview-group"> <div class="tab-pane mail-preview-group">
<div lang="{{ order.locale }}" class="mail-preview"> <div lang="{{ order.locale }}" class="mail-preview">
<strong>{{ preview_output.subject }}</strong><br><br> <strong>{{ preview_output.subject }}</strong><br><br>
@@ -46,7 +46,7 @@
{% trans "active" %} {% trans "active" %}
{% endif %} {% endif %}
</dd> </dd>
<dt>{% trans "E-mail" %}</dt> <dt>{% trans "Email" %}</dt>
<dd> <dd>
{{ customer.email|default_if_none:"" }} {{ customer.email|default_if_none:"" }}
{% if customer.email and not customer.provider %} {% if customer.email and not customer.provider %}
@@ -8,7 +8,7 @@
{% endblock %} {% endblock %}
{% block title %}{% trans "Organizer" %}{% endblock %} {% block title %}{% trans "Organizer" %}{% endblock %}
{% block content %} {% block content %}
<h1>{% trans "E-mail settings" %}</h1> <h1>{% trans "Email settings" %}</h1>
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data" <form action="" method="post" class="form-horizontal" enctype="multipart/form-data"
mail-preview-url="{% url "control:organizer.settings.mail.preview" organizer=request.organizer.slug %}"> mail-preview-url="{% url "control:organizer.settings.mail.preview" organizer=request.organizer.slug %}">
@@ -55,7 +55,7 @@
{% bootstrap_field form.mail_bcc layout="control" %} {% bootstrap_field form.mail_bcc layout="control" %}
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>{% trans "E-mail content" %}</legend> <legend>{% trans "Email content" %}</legend>
<div class="panel-group" id="questions_group"> <div class="panel-group" id="questions_group">
{% blocktrans asvar title_customer_registration %}Customer account registration{% endblocktrans %} {% blocktrans asvar title_customer_registration %}Customer account registration{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="customer_registration" title=title_customer_registration items="mail_subject_customer_registration,mail_text_customer_registration" %} {% include "pretixcontrol/event/mail_settings_fragment.html" with pid="customer_registration" title=title_customer_registration items="mail_subject_customer_registration,mail_text_customer_registration" %}
@@ -54,7 +54,7 @@
<thead> <thead>
<tr> <tr>
<th>{% trans "Notification type" %}</th> <th>{% trans "Notification type" %}</th>
<th class="text-center">{% trans "E-Mail notification" %}</th> <th class="text-center">{% trans "Email notification" %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -39,7 +39,7 @@
<thead> <thead>
<tr> <tr>
<th> <th>
{% trans "E-mail address" %} {% trans "Email address" %}
<a href="?{% url_replace request 'ordering' '-email' %}"><i class="fa fa-caret-down"></i></a> <a href="?{% url_replace request 'ordering' '-email' %}"><i class="fa fa-caret-down"></i></a>
<a href="?{% url_replace request 'ordering' 'email' %}"><i class="fa fa-caret-up"></i></a> <a href="?{% url_replace request 'ordering' 'email' %}"><i class="fa fa-caret-up"></i></a>
</th> </th>
+3 -3
View File
@@ -343,7 +343,7 @@ class Forgot(TemplateView):
logger.warning('Backend password reset for unregistered e-mail \"' + email + '\" requested.') logger.warning('Backend password reset for unregistered e-mail \"' + email + '\" requested.')
except SendMailException: except SendMailException:
logger.exception('Sending password reset e-mail to \"' + email + '\" failed.') logger.exception('Sending password reset email to \"' + email + '\" failed.')
except RepeatedResetDenied: except RepeatedResetDenied:
pass pass
@@ -354,10 +354,10 @@ class Forgot(TemplateView):
finally: finally:
if has_redis: if has_redis:
messages.info(request, _('If the address is registered to valid account, then we have sent you an e-mail containing further instructions. ' messages.info(request, _('If the address is registered to valid account, then we have sent you an email containing further instructions. '
'Please note that we will send at most one email every 24 hours.')) 'Please note that we will send at most one email every 24 hours.'))
else: else:
messages.info(request, _('If the address is registered to valid account, then we have sent you an e-mail containing further instructions.')) messages.info(request, _('If the address is registered to valid account, then we have sent you an email containing further instructions.'))
return redirect('control:auth.forgot') return redirect('control:auth.forgot')
else: else:
+7 -5
View File
@@ -49,6 +49,7 @@ from django.views.generic import FormView, ListView, TemplateView
from i18nfield.strings import LazyI18nString from i18nfield.strings import LazyI18nString
from pretix.api.views.checkin import _redeem_process from pretix.api.views.checkin import _redeem_process
from pretix.base.media import MEDIA_TYPES
from pretix.base.models import Checkin, Order, OrderPosition from pretix.base.models import Checkin, Order, OrderPosition
from pretix.base.models.checkin import CheckinList from pretix.base.models.checkin import CheckinList
from pretix.base.services.checkin import ( from pretix.base.services.checkin import (
@@ -81,9 +82,7 @@ class CheckInListQueryMixin:
position_id=OuterRef('pk'), position_id=OuterRef('pk'),
list_id=self.list.pk, list_id=self.list.pk,
type=Checkin.TYPE_ENTRY type=Checkin.TYPE_ENTRY
).order_by().values('position_id').annotate( ).order_by('-datetime').values('position_id')
m=Max('datetime')
).values('m')
cqs_exit = Checkin.objects.filter( cqs_exit = Checkin.objects.filter(
position_id=OuterRef('pk'), position_id=OuterRef('pk'),
list_id=self.list.pk, list_id=self.list.pk,
@@ -103,7 +102,7 @@ class CheckInListQueryMixin:
status_q, status_q,
order__event=self.request.event, order__event=self.request.event,
).annotate( ).annotate(
last_entry=Subquery(cqs), last_entry=Subquery(cqs[:1].values('datetime')),
last_exit=Subquery(cqs_exit), last_exit=Subquery(cqs_exit),
auto_checked_in=Exists( auto_checked_in=Exists(
Checkin.objects.filter( Checkin.objects.filter(
@@ -112,7 +111,8 @@ class CheckInListQueryMixin:
list_id=self.list.pk, list_id=self.list.pk,
auto_checked_in=True auto_checked_in=True
) )
) ),
last_entry_source_type=Subquery(cqs[:1].values('raw_source_type'))
).select_related( ).select_related(
'item', 'variation', 'order', 'addon_to' 'item', 'variation', 'order', 'addon_to'
).prefetch_related( ).prefetch_related(
@@ -157,6 +157,7 @@ class CheckInListShow(EventPermissionRequiredMixin, PaginationMixin, CheckInList
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs) ctx = super().get_context_data(**kwargs)
ctx['media_types'] = MEDIA_TYPES
ctx['checkinlist'] = self.list ctx['checkinlist'] = self.list
if self.request.event.has_subevents: if self.request.event.has_subevents:
ctx['seats'] = ( ctx['seats'] = (
@@ -497,6 +498,7 @@ class CheckinListView(EventPermissionRequiredMixin, PaginationMixin, ListView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
ctx = super().get_context_data() ctx = super().get_context_data()
ctx['filter_form'] = self.filter_form ctx['filter_form'] = self.filter_form
ctx['media_types'] = MEDIA_TYPES
return ctx return ctx
+1 -1
View File
@@ -807,7 +807,7 @@ class MailSettingsRendererPreview(MailSettingsPreview):
r._csp_ignore = True r._csp_ignore = True
return r return r
else: else:
raise Http404(_('Unknown e-mail renderer.')) raise Http404(_('Unknown email renderer.'))
class TicketSettingsPreview(EventPermissionRequiredMixin, View): class TicketSettingsPreview(EventPermissionRequiredMixin, View):
+1 -1
View File
@@ -145,7 +145,7 @@ class UserResetView(AdministratorPermissionRequiredMixin, RecentAuthenticationRe
self.object.log_action('pretix.control.auth.user.forgot_password.mail_sent', self.object.log_action('pretix.control.auth.user.forgot_password.mail_sent',
user=request.user) user=request.user)
messages.success(request, _('We sent out an e-mail containing further instructions.')) messages.success(request, _('We sent out an email containing further instructions.'))
return redirect(self.get_success_url()) return redirect(self.get_success_url())
def get_success_url(self): def get_success_url(self):
+1 -1
View File
@@ -304,7 +304,7 @@ class WaitingListView(EventPermissionRequiredMixin, WaitingListQuerySetMixin, Pa
writer = csv.writer(output, quoting=csv.QUOTE_NONNUMERIC, delimiter=",") writer = csv.writer(output, quoting=csv.QUOTE_NONNUMERIC, delimiter=",")
headers = [ headers = [
_('Name'), _('E-mail address'), _('Phone number'), _('Product'), _('On list since'), _('Status'), _('Voucher code'), _('Name'), _('Email address'), _('Phone number'), _('Product'), _('On list since'), _('Status'), _('Voucher code'),
_('Language'), _('Priority') _('Language'), _('Priority')
] ]
if self.request.event.has_subevents: if self.request.event.has_subevents:
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1
View File
@@ -198,6 +198,7 @@ Mitgliedschafts
Mitgliedschaftsdauer Mitgliedschaftsdauer
min min
Mo Mo
MobilePay
MOTO MOTO
Multibanco Multibanco
MwSt MwSt
File diff suppressed because it is too large Load Diff
@@ -198,6 +198,7 @@ Mitgliedschafts
Mitgliedschaftsdauer Mitgliedschaftsdauer
min min
Mo Mo
MobilePay
MOTO MOTO
Multibanco Multibanco
MwSt MwSt
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-08 13:45+0000\n" "POT-Creation-Date: 2024-11-18 15:30+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-08 13:45+0000\n" "POT-Creation-Date: 2024-11-08 13:45+0000\n"
"PO-Revision-Date: 2024-11-13 21:00+0000\n" "PO-Revision-Date: 2024-11-18 15:48+0000\n"
"Last-Translator: CVZ-es <damien.bremont@casadevelazquez.org>\n" "Last-Translator: CVZ-es <damien.bremont@casadevelazquez.org>\n"
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/" "Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/"
"pretix-js/es/>\n" "pretix-js/es/>\n"
@@ -448,7 +448,7 @@ msgstr "Producto"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:103 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:103
msgid "Product variation" msgid "Product variation"
msgstr "Ver variaciones del producto" msgstr "Variación del producto"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:107 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:107
msgid "Gate" msgid "Gate"
@@ -645,7 +645,7 @@ msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:496 #: pretix/static/pretixcontrol/js/ui/main.js:496
#: pretix/static/pretixcontrol/js/ui/main.js:516 #: pretix/static/pretixcontrol/js/ui/main.js:516
msgid "Search query" msgid "Search query"
msgstr "Consultar búsqueda" msgstr "Consulta de búsqueda"
#: pretix/static/pretixcontrol/js/ui/main.js:514 #: pretix/static/pretixcontrol/js/ui/main.js:514
msgid "All" msgid "All"
@@ -1058,7 +1058,7 @@ msgstr "febrero"
#: pretix/static/pretixpresale/js/widget/widget.js:80 #: pretix/static/pretixpresale/js/widget/widget.js:80
msgid "March" msgid "March"
msgstr "marzo" msgstr "Marzo"
#: pretix/static/pretixpresale/js/widget/widget.js:81 #: pretix/static/pretixpresale/js/widget/widget.js:81
msgid "April" msgid "April"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+9 -13
View File
@@ -7,8 +7,8 @@ msgstr ""
"Project-Id-Version: French\n" "Project-Id-Version: French\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-08 13:45+0000\n" "POT-Creation-Date: 2024-11-08 13:45+0000\n"
"PO-Revision-Date: 2024-11-15 11:19+0000\n" "PO-Revision-Date: 2024-11-16 05:00+0000\n"
"Last-Translator: Eva-Maria Obermann <obermann@rami.io>\n" "Last-Translator: CVZ-es <damien.bremont@casadevelazquez.org>\n"
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix-js/" "Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix-js/"
"fr/>\n" "fr/>\n"
"Language: fr\n" "Language: fr\n"
@@ -325,7 +325,7 @@ msgstr "Billets valides"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:70 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:70
msgid "Currently inside" msgid "Currently inside"
msgstr "Présent actuellement" msgstr "Actuellement à l'intérieur"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:71 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:71
#: pretix/static/pretixcontrol/js/ui/question.js:137 #: pretix/static/pretixcontrol/js/ui/question.js:137
@@ -457,7 +457,7 @@ msgstr "Pont"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:111 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:111
msgid "Current date and time" msgid "Current date and time"
msgstr "Date actuelle" msgstr "Date et heure actuelle"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:115 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:115
msgid "Current day of the week (1 = Monday, 7 = Sunday)" msgid "Current day of the week (1 = Monday, 7 = Sunday)"
@@ -465,7 +465,7 @@ msgstr "Jour courant de la semaine (1 = Lundi, 7 = Dimanche)"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:119 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:119
msgid "Current entry status" msgid "Current entry status"
msgstr "" msgstr "Statut actuel de l'entrée"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:123 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:123
msgid "Number of previous entries" msgid "Number of previous entries"
@@ -488,16 +488,12 @@ msgid "Number of days with a previous entry"
msgstr "Nombre de jours avec entrée préalable" msgstr "Nombre de jours avec entrée préalable"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:143 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:143
#, fuzzy
#| msgid "Number of days with a previous entry"
msgid "Number of days with a previous entry since" msgid "Number of days with a previous entry since"
msgstr "Nombre de jours avec entrée préalable" msgstr "Nombre de jours avec entrée préalable depuis"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:147 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:147
#, fuzzy
#| msgid "Number of days with a previous entry"
msgid "Number of days with a previous entry before" msgid "Number of days with a previous entry before"
msgstr "Nombre de jours avec entrée préalable" msgstr "Nombre de jours avec entrée préalable avant"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:151 #: pretix/static/pretixcontrol/js/ui/checkinrules.js:151
msgid "Minutes since last entry (-1 on first entry)" msgid "Minutes since last entry (-1 on first entry)"
@@ -650,7 +646,7 @@ msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:496 #: pretix/static/pretixcontrol/js/ui/main.js:496
#: pretix/static/pretixcontrol/js/ui/main.js:516 #: pretix/static/pretixcontrol/js/ui/main.js:516
msgid "Search query" msgid "Search query"
msgstr "Recherche" msgstr "Requête de recherche"
#: pretix/static/pretixcontrol/js/ui/main.js:514 #: pretix/static/pretixcontrol/js/ui/main.js:514
msgid "All" msgid "All"
@@ -708,7 +704,7 @@ msgid ""
"complete your order as long as theyre available." "complete your order as long as theyre available."
msgstr "" msgstr ""
"Les articles de votre panier ne vous sont plus réservés. Vous pouvez " "Les articles de votre panier ne vous sont plus réservés. Vous pouvez "
"toujours terminer votre commande tant quils sont disponibles." "toujours compléter votre commande tant qu'ils sont disponibles."
#: pretix/static/pretixpresale/js/ui/cart.js:45 #: pretix/static/pretixpresale/js/ui/cart.js:45
msgid "Cart expired" msgid "Cart expired"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -8,17 +8,17 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-08 13:45+0000\n" "POT-Creation-Date: 2024-11-08 13:45+0000\n"
"PO-Revision-Date: 2024-01-31 04:00+0000\n" "PO-Revision-Date: 2024-11-17 00:00+0000\n"
"Last-Translator: Pavle Ergović <pavleergovic@gmail.com>\n" "Last-Translator: Pavle Ergović <pavleergovic@gmail.com>\n"
"Language-Team: Croatian <https://translate.pretix.eu/projects/pretix/pretix-" "Language-Team: Croatian <https://translate.pretix.eu/projects/pretix/"
"js/hr/>\n" "pretix-js/hr/>\n"
"Language: hr\n" "Language: hr\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Generator: Weblate 5.3.1\n" "X-Generator: Weblate 5.8.3\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56 #: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62 #: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
@@ -307,7 +307,7 @@ msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65
msgid "Order canceled" msgid "Order canceled"
msgstr "" msgstr "Narudžba otkazana"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66 #: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
msgid "Ticket code is ambiguous on list" msgid "Ticket code is ambiguous on list"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -11
View File
@@ -6,7 +6,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: 1\n" "Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-08 13:45+0000\n" "POT-Creation-Date: 2024-11-18 15:16+0000\n"
"PO-Revision-Date: 2024-10-23 14:30+0000\n" "PO-Revision-Date: 2024-10-23 14:30+0000\n"
"Last-Translator: arjan-s <github@anymore.nl>\n" "Last-Translator: arjan-s <github@anymore.nl>\n"
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix-js/" "Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -645,48 +645,38 @@ msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:496 #: pretix/static/pretixcontrol/js/ui/main.js:496
#: pretix/static/pretixcontrol/js/ui/main.js:516 #: pretix/static/pretixcontrol/js/ui/main.js:516
#: pretix/static/pretixcontrol/js/ui/main.js:495
#: pretix/static/pretixcontrol/js/ui/main.js:515
msgid "Search query" msgid "Search query"
msgstr "Zoekopdracht" msgstr "Zoekopdracht"
#: pretix/static/pretixcontrol/js/ui/main.js:514 #: pretix/static/pretixcontrol/js/ui/main.js:514
#: pretix/static/pretixcontrol/js/ui/main.js:513
msgid "All" msgid "All"
msgstr "Alle" msgstr "Alle"
#: pretix/static/pretixcontrol/js/ui/main.js:515 #: pretix/static/pretixcontrol/js/ui/main.js:515
#: pretix/static/pretixcontrol/js/ui/main.js:514
msgid "None" msgid "None"
msgstr "Geen" msgstr "Geen"
#: pretix/static/pretixcontrol/js/ui/main.js:519 #: pretix/static/pretixcontrol/js/ui/main.js:519
#: pretix/static/pretixcontrol/js/ui/main.js:518
msgid "Selected only" msgid "Selected only"
msgstr "Alleen geselecteerde" msgstr "Alleen geselecteerde"
#: pretix/static/pretixcontrol/js/ui/main.js:862 #: pretix/static/pretixcontrol/js/ui/main.js:862
#: pretix/static/pretixcontrol/js/ui/main.js:861
msgid "Enter page number between 1 and %(max)s." msgid "Enter page number between 1 and %(max)s."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:865 #: pretix/static/pretixcontrol/js/ui/main.js:865
#: pretix/static/pretixcontrol/js/ui/main.js:864
msgid "Invalid page number." msgid "Invalid page number."
msgstr "" msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1023 #: pretix/static/pretixcontrol/js/ui/main.js:1023
#: pretix/static/pretixcontrol/js/ui/main.js:1022
msgid "Use a different name internally" msgid "Use a different name internally"
msgstr "Gebruik intern een andere naam" msgstr "Gebruik intern een andere naam"
#: pretix/static/pretixcontrol/js/ui/main.js:1063 #: pretix/static/pretixcontrol/js/ui/main.js:1063
#: pretix/static/pretixcontrol/js/ui/main.js:1062
msgid "Click to close" msgid "Click to close"
msgstr "Klik om te sluiten" msgstr "Klik om te sluiten"
#: pretix/static/pretixcontrol/js/ui/main.js:1138 #: pretix/static/pretixcontrol/js/ui/main.js:1138
#: pretix/static/pretixcontrol/js/ui/main.js:1137
msgid "You have unsaved changes!" msgid "You have unsaved changes!"
msgstr "U heeft nog niet opgeslagen wijzigingen!" msgstr "U heeft nog niet opgeslagen wijzigingen!"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More