mirror of
https://github.com/pretix/pretix.git
synced 2026-04-14 21:52:28 +00:00
Compare commits
30 Commits
django31
...
waitlist_s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f8e6a2a4b | ||
|
|
c084b91ab3 | ||
|
|
8baaa0a8c6 | ||
|
|
53070f5d4b | ||
|
|
5685a349ea | ||
|
|
1af69d5c76 | ||
|
|
adddc7a71e | ||
|
|
11f23c3fd2 | ||
|
|
954fece6cf | ||
|
|
8ef6adc3d5 | ||
|
|
88ba7ab53a | ||
|
|
eae55e4b5a | ||
|
|
5ae839f62e | ||
|
|
7314d32422 | ||
|
|
97d6ae8e55 | ||
|
|
13063cb9d2 | ||
|
|
2792813d95 | ||
|
|
d6aeefdf09 | ||
|
|
13056ef477 | ||
|
|
6e2b5eae9a | ||
|
|
4cfb10b254 | ||
|
|
ebd336e8cb | ||
|
|
1357b010de | ||
|
|
09b2e69178 | ||
|
|
5e34032821 | ||
|
|
46cee890f0 | ||
|
|
4a2ac110b3 | ||
|
|
7eefd3dc59 | ||
|
|
fdca62685c | ||
|
|
7ae38b5e97 |
@@ -60,7 +60,10 @@ Here is the currently recommended set of commands::
|
||||
CREATE INDEX CONCURRENTLY pretix_addidx_ia_company
|
||||
ON pretixbase_invoiceaddress
|
||||
USING gin (upper("company") gin_trgm_ops);
|
||||
CREATE INDEX CONCURRENTLY pretix_addidx_orderpos_email_upper ON public.pretixbase_orderposition (upper((attendee_email)::text));
|
||||
CREATE INDEX CONCURRENTLY pretix_addidx_orderpos_email_upper
|
||||
ON public.pretixbase_orderposition (upper((attendee_email)::text));
|
||||
CREATE INDEX CONCURRENTLY pretix_addidx_voucher_code_upper
|
||||
ON public.pretixbase_voucher (upper((code)::text));
|
||||
|
||||
|
||||
Also, if you use our ``pretix-shipping`` plugin::
|
||||
|
||||
@@ -10,7 +10,7 @@ class FullAccessSecurityProfile:
|
||||
|
||||
|
||||
class AllowListSecurityProfile:
|
||||
allowlist = tuple()
|
||||
allowlist = ()
|
||||
|
||||
def is_allowed(self, request):
|
||||
key = (request.method, f"{request.resolver_match.namespace}:{request.resolver_match.url_name}")
|
||||
@@ -95,6 +95,8 @@ class PretixPosSecurityProfile(AllowListSecurityProfile):
|
||||
('GET', 'api-v1:taxrule-list'),
|
||||
('GET', 'api-v1:ticketlayout-list'),
|
||||
('GET', 'api-v1:ticketlayoutitem-list'),
|
||||
('GET', 'api-v1:badgelayout-list'),
|
||||
('GET', 'api-v1:badgeitem-list'),
|
||||
('GET', 'api-v1:order-list'),
|
||||
('POST', 'api-v1:order-list'),
|
||||
('GET', 'api-v1:order-detail'),
|
||||
|
||||
@@ -309,7 +309,7 @@ class EventSerializer(I18nAwareModelSerializer):
|
||||
|
||||
# Item Meta properties
|
||||
if item_meta_properties is not None:
|
||||
current = [imp for imp in event.item_meta_properties.all()]
|
||||
current = list(event.item_meta_properties.all())
|
||||
for key, value in item_meta_properties.items():
|
||||
prop = self.item_meta_props.get(key)
|
||||
if prop in current:
|
||||
|
||||
@@ -18,18 +18,18 @@ class FormFieldWrapperField(serializers.Field):
|
||||
|
||||
|
||||
simple_mappings = (
|
||||
(forms.DateField, serializers.DateField, tuple()),
|
||||
(forms.TimeField, serializers.TimeField, tuple()),
|
||||
(forms.SplitDateTimeField, serializers.DateTimeField, tuple()),
|
||||
(forms.DateTimeField, serializers.DateTimeField, tuple()),
|
||||
(forms.DateField, serializers.DateField, ()),
|
||||
(forms.TimeField, serializers.TimeField, ()),
|
||||
(forms.SplitDateTimeField, serializers.DateTimeField, ()),
|
||||
(forms.DateTimeField, serializers.DateTimeField, ()),
|
||||
(forms.DecimalField, serializers.DecimalField, ('max_digits', 'decimal_places', 'min_value', 'max_value')),
|
||||
(forms.FloatField, serializers.FloatField, tuple()),
|
||||
(forms.IntegerField, serializers.IntegerField, tuple()),
|
||||
(forms.EmailField, serializers.EmailField, tuple()),
|
||||
(forms.UUIDField, serializers.UUIDField, tuple()),
|
||||
(forms.URLField, serializers.URLField, tuple()),
|
||||
(forms.NullBooleanField, serializers.NullBooleanField, tuple()),
|
||||
(forms.BooleanField, serializers.BooleanField, tuple()),
|
||||
(forms.FloatField, serializers.FloatField, ()),
|
||||
(forms.IntegerField, serializers.IntegerField, ()),
|
||||
(forms.EmailField, serializers.EmailField, ()),
|
||||
(forms.UUIDField, serializers.UUIDField, ()),
|
||||
(forms.URLField, serializers.URLField, ()),
|
||||
(forms.NullBooleanField, serializers.NullBooleanField, ()),
|
||||
(forms.BooleanField, serializers.BooleanField, ()),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -53,8 +53,8 @@ class DeviceSerializer(serializers.ModelSerializer):
|
||||
|
||||
|
||||
class InitializeView(APIView):
|
||||
authentication_classes = tuple()
|
||||
permission_classes = tuple()
|
||||
authentication_classes = ()
|
||||
permission_classes = ()
|
||||
|
||||
def post(self, request, format=None):
|
||||
serializer = InitializationRequestSerializer(data=request.data)
|
||||
|
||||
@@ -100,7 +100,7 @@ class NamePartsWidget(forms.MultiWidget):
|
||||
if not isinstance(value, list):
|
||||
value = self.decompress(value)
|
||||
output = []
|
||||
final_attrs = self.build_attrs(attrs or dict())
|
||||
final_attrs = self.build_attrs(attrs or {})
|
||||
if 'required' in final_attrs:
|
||||
del final_attrs['required']
|
||||
id_ = final_attrs.get('id', None)
|
||||
@@ -122,6 +122,8 @@ class NamePartsWidget(forms.MultiWidget):
|
||||
these_attrs.pop('data-no-required-attr', None)
|
||||
these_attrs['autocomplete'] = (self.attrs.get('autocomplete', '') + ' ' + self.autofill_map.get(self.scheme['fields'][i][0], 'off')).strip()
|
||||
these_attrs['data-size'] = self.scheme['fields'][i][2]
|
||||
if len(self.widgets) > 1:
|
||||
these_attrs['aria-label'] = self.scheme['fields'][i][1]
|
||||
else:
|
||||
these_attrs = final_attrs
|
||||
output.append(widget.render(name + '_%s' % i, widget_value, these_attrs, renderer=renderer))
|
||||
@@ -220,7 +222,7 @@ class WrappedPhonePrefixSelect(Select):
|
||||
country_name = locale.territories.get(country_code)
|
||||
if country_name:
|
||||
choices.append((prefix, "{} {}".format(country_name, prefix)))
|
||||
super().__init__(choices=sorted(choices, key=lambda item: item[1]))
|
||||
super().__init__(choices=sorted(choices, key=lambda item: item[1]), attrs={'aria-label': pgettext_lazy('phonenumber', 'International area code')})
|
||||
|
||||
def render(self, name, value, *args, **kwargs):
|
||||
return super().render(name, value or self.initial, *args, **kwargs)
|
||||
@@ -243,7 +245,10 @@ class WrappedPhonePrefixSelect(Select):
|
||||
class WrappedPhoneNumberPrefixWidget(PhoneNumberPrefixWidget):
|
||||
|
||||
def __init__(self, attrs=None, initial=None):
|
||||
widgets = (WrappedPhonePrefixSelect(initial), forms.TextInput())
|
||||
attrs = {
|
||||
'aria-label': pgettext_lazy('phonenumber', 'Phone number (without international area code)')
|
||||
}
|
||||
widgets = (WrappedPhonePrefixSelect(initial), forms.TextInput(attrs=attrs))
|
||||
super(PhoneNumberPrefixWidget, self).__init__(widgets, attrs)
|
||||
|
||||
def render(self, name, value, attrs=None, renderer=None):
|
||||
|
||||
@@ -445,7 +445,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
if self.invoice.custom_field:
|
||||
story.append(Paragraph(
|
||||
'{}: {}'.format(
|
||||
bleach.clean(self.invoice.event.settings.invoice_address_custom_field, tags=[]).strip().replace('\n', '<br />\n'),
|
||||
bleach.clean(str(self.invoice.event.settings.invoice_address_custom_field), tags=[]).strip().replace('\n', '<br />\n'),
|
||||
bleach.clean(self.invoice.custom_field, tags=[]).strip().replace('\n', '<br />\n'),
|
||||
),
|
||||
self.stylesheet['Normal']
|
||||
|
||||
49
src/pretix/base/migrations/0179_auto_20210311_1653.py
Normal file
49
src/pretix/base/migrations/0179_auto_20210311_1653.py
Normal file
@@ -0,0 +1,49 @@
|
||||
# Generated by Django 3.0.10 on 2021-03-11 16:53
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def clean_duplicates(apps, schema_editor):
|
||||
while True:
|
||||
delete_options = """
|
||||
DELETE
|
||||
FROM pretixbase_questionanswer_options
|
||||
WHERE questionanswer_id IN (
|
||||
SELECT MIN(qa.id)
|
||||
FROM pretixbase_questionanswer qa
|
||||
GROUP BY qa.cartposition_id, qa.orderposition_id, qa.question_id
|
||||
HAVING COUNT(*) > 1
|
||||
);
|
||||
"""
|
||||
delete_answers = """
|
||||
DELETE
|
||||
FROM pretixbase_questionanswer
|
||||
WHERE pretixbase_questionanswer.id IN (
|
||||
SELECT MIN(qa.id)
|
||||
FROM pretixbase_questionanswer qa
|
||||
GROUP BY qa.cartposition_id, qa.orderposition_id, qa.question_id
|
||||
HAVING COUNT(*) > 1
|
||||
);
|
||||
"""
|
||||
with schema_editor.connection.cursor() as cursor:
|
||||
cursor.execute(delete_options)
|
||||
cursor.execute(delete_answers)
|
||||
if cursor.rowcount == 0:
|
||||
return
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0178_auto_20210308_1326'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
clean_duplicates,
|
||||
migrations.RunPython.noop,
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='questionanswer',
|
||||
unique_together={('orderposition', 'question'), ('cartposition', 'question')},
|
||||
),
|
||||
]
|
||||
@@ -983,6 +983,9 @@ class QuestionAnswer(models.Model):
|
||||
|
||||
objects = ScopedManager(organizer='question__event__organizer')
|
||||
|
||||
class Meta:
|
||||
unique_together = [['orderposition', 'question'], ['cartposition', 'question']]
|
||||
|
||||
@property
|
||||
def backend_file_url(self):
|
||||
if self.file:
|
||||
|
||||
@@ -241,7 +241,7 @@ class CartManager:
|
||||
raise CartError(_(error_messages['max_items']) % (self.event.settings.max_items_per_order,))
|
||||
|
||||
def _check_item_constraints(self, op, current_ops=[]):
|
||||
if isinstance(op, self.AddOperation) or isinstance(op, self.ExtendOperation):
|
||||
if isinstance(op, (self.AddOperation, self.ExtendOperation)):
|
||||
if not (
|
||||
(isinstance(op, self.AddOperation) and op.addon_to == 'FAKE') or
|
||||
(isinstance(op, self.ExtendOperation) and op.position.is_bundled)
|
||||
@@ -863,7 +863,7 @@ class CartManager:
|
||||
op.position.addons.all().delete()
|
||||
op.position.delete()
|
||||
|
||||
elif isinstance(op, self.AddOperation) or isinstance(op, self.ExtendOperation):
|
||||
elif isinstance(op, (self.AddOperation, self.ExtendOperation)):
|
||||
# Create a CartPosition for as much items as we can
|
||||
requested_count = quota_available_count = voucher_available_count = op.count
|
||||
|
||||
|
||||
@@ -3,22 +3,21 @@ from i18nfield.strings import LazyI18nString
|
||||
|
||||
from pretix.base.email import get_email_context
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import Event, User, Voucher
|
||||
from pretix.base.models import Event, LogEntry, User, Voucher
|
||||
from pretix.base.services.mail import mail
|
||||
from pretix.base.services.tasks import TransactionAwareProfiledEventTask
|
||||
from pretix.celery_app import app
|
||||
|
||||
|
||||
@app.task(base=TransactionAwareProfiledEventTask, acks_late=True)
|
||||
def vouchers_send(event: Event, vouchers: list, subject: str, message: str, recipients: list, user: int) -> None:
|
||||
def vouchers_send(event: Event, vouchers: list, subject: str, message: str, recipients: list, user: int,
|
||||
progress=None) -> None:
|
||||
vouchers = list(Voucher.objects.filter(id__in=vouchers).order_by('id'))
|
||||
user = User.objects.get(pk=user)
|
||||
for r in recipients:
|
||||
for ir, r in enumerate(recipients):
|
||||
voucher_list = []
|
||||
for i in range(r['number']):
|
||||
voucher_list.append(vouchers.pop())
|
||||
with language(event.settings.locale):
|
||||
email_context = get_email_context(event=event, name=r.get('name') or '', voucher_list=[v.code for v in voucher_list])
|
||||
email_context = get_email_context(event=event, name=r.get('name') or '',
|
||||
voucher_list=[v.code for v in voucher_list])
|
||||
mail(
|
||||
r['email'],
|
||||
subject,
|
||||
@@ -27,14 +26,14 @@ def vouchers_send(event: Event, vouchers: list, subject: str, message: str, reci
|
||||
event,
|
||||
locale=event.settings.locale,
|
||||
)
|
||||
logs = []
|
||||
for v in voucher_list:
|
||||
if r.get('tag') and r.get('tag') != v.tag:
|
||||
v.tag = r.get('tag')
|
||||
if v.comment:
|
||||
v.comment += '\n\n'
|
||||
v.comment = gettext('The voucher has been sent to {recipient}.').format(recipient=r['email'])
|
||||
v.save(update_fields=['tag', 'comment'])
|
||||
v.log_action(
|
||||
logs.append(v.log_action(
|
||||
'pretix.voucher.sent',
|
||||
user=user,
|
||||
data={
|
||||
@@ -42,5 +41,11 @@ def vouchers_send(event: Event, vouchers: list, subject: str, message: str, reci
|
||||
'name': r.get('name'),
|
||||
'subject': subject,
|
||||
'message': message,
|
||||
}
|
||||
)
|
||||
},
|
||||
save=False
|
||||
))
|
||||
Voucher.objects.bulk_update(voucher_list, fields=['comment', 'tag'], batch_size=500)
|
||||
LogEntry.objects.bulk_create(logs, batch_size=500)
|
||||
|
||||
if progress and ir % 50 == 0:
|
||||
progress(ir / len(recipients))
|
||||
|
||||
@@ -1799,7 +1799,7 @@ Your {event} team"""))
|
||||
),
|
||||
},
|
||||
'theme_color_danger': {
|
||||
'default': '#D36060',
|
||||
'default': '#C44F4F',
|
||||
'type': str,
|
||||
'form_class': forms.CharField,
|
||||
'serializer_class': serializers.CharField,
|
||||
|
||||
@@ -11,7 +11,7 @@ register = template.Library()
|
||||
|
||||
@register.filter("money")
|
||||
def money_filter(value: Decimal, arg='', hide_currency=False):
|
||||
if isinstance(value, float) or isinstance(value, int):
|
||||
if isinstance(value, (float, int)):
|
||||
value = Decimal(value)
|
||||
if not isinstance(value, Decimal):
|
||||
if value == '':
|
||||
@@ -47,7 +47,7 @@ def money_filter(value: Decimal, arg='', hide_currency=False):
|
||||
|
||||
@register.filter("money_numberfield")
|
||||
def money_numberfield_filter(value: Decimal, arg=''):
|
||||
if isinstance(value, float) or isinstance(value, int):
|
||||
if isinstance(value, (float, int)):
|
||||
value = Decimal(value)
|
||||
if not isinstance(value, Decimal):
|
||||
raise TypeError("Invalid data type passed to money filter: %r" % type(value))
|
||||
|
||||
@@ -48,7 +48,7 @@ def page_not_found(request, exception):
|
||||
except (AttributeError, IndexError):
|
||||
pass
|
||||
else:
|
||||
if isinstance(message, str) or isinstance(message, Promise):
|
||||
if isinstance(message, (str, Promise)):
|
||||
exception_repr = str(message)
|
||||
context = {
|
||||
'request_path': request.path,
|
||||
|
||||
@@ -4,43 +4,25 @@ import celery.exceptions
|
||||
from celery.result import AsyncResult
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import redirect, render
|
||||
from django.test import RequestFactory
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import FormView
|
||||
|
||||
from pretix.base.models import User
|
||||
from pretix.base.services.tasks import ProfiledEventTask
|
||||
from pretix.celery_app import app
|
||||
|
||||
logger = logging.getLogger('pretix.base.tasks')
|
||||
|
||||
|
||||
class AsyncAction:
|
||||
task = None
|
||||
class AsyncMixin:
|
||||
success_url = None
|
||||
error_url = None
|
||||
known_errortypes = []
|
||||
|
||||
def do(self, *args, **kwargs):
|
||||
if not isinstance(self.task, app.Task):
|
||||
raise TypeError('Method has no task attached')
|
||||
|
||||
try:
|
||||
res = self.task.apply_async(args=args, kwargs=kwargs)
|
||||
except ConnectionError:
|
||||
# Task very likely not yet sent, due to redis restarting etc. Let's try once agan
|
||||
res = self.task.apply_async(args=args, kwargs=kwargs)
|
||||
|
||||
if 'ajax' in self.request.GET or 'ajax' in self.request.POST:
|
||||
data = self._return_ajax_result(res)
|
||||
data['check_url'] = self.get_check_url(res.id, True)
|
||||
return JsonResponse(data)
|
||||
else:
|
||||
if res.ready():
|
||||
if res.successful() and not isinstance(res.info, Exception):
|
||||
return self.success(res.info)
|
||||
else:
|
||||
return self.error(res.info)
|
||||
return redirect(self.get_check_url(res.id, False))
|
||||
|
||||
def get_success_url(self, value):
|
||||
return self.success_url
|
||||
|
||||
@@ -50,11 +32,6 @@ class AsyncAction:
|
||||
def get_check_url(self, task_id, ajax):
|
||||
return self.request.path + '?async_id=%s' % task_id + ('&ajax=1' if ajax else '')
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
if 'async_id' in request.GET and settings.HAS_CELERY:
|
||||
return self.get_result(request)
|
||||
return self.http_method_not_allowed(request)
|
||||
|
||||
def _ajax_response_data(self):
|
||||
return {}
|
||||
|
||||
@@ -86,7 +63,7 @@ class AsyncAction:
|
||||
if smes:
|
||||
messages.success(self.request, smes)
|
||||
# TODO: Do not store message if the ajax client states that it will not redirect
|
||||
# but handle the mssage itself
|
||||
# but handle the message itself
|
||||
data.update({
|
||||
'redirect': self.get_success_url(res.info),
|
||||
'success': True,
|
||||
@@ -95,7 +72,7 @@ class AsyncAction:
|
||||
else:
|
||||
messages.error(self.request, self.get_error_message(res.info))
|
||||
# TODO: Do not store message if the ajax client states that it will not redirect
|
||||
# but handle the mssage itself
|
||||
# but handle the message itself
|
||||
data.update({
|
||||
'redirect': self.get_error_url(),
|
||||
'success': False,
|
||||
@@ -159,3 +136,124 @@ class AsyncAction:
|
||||
|
||||
def get_success_message(self, value):
|
||||
return _('The task has been completed.')
|
||||
|
||||
|
||||
class AsyncAction(AsyncMixin):
|
||||
task = None
|
||||
|
||||
def do(self, *args, **kwargs):
|
||||
if not isinstance(self.task, app.Task):
|
||||
raise TypeError('Method has no task attached')
|
||||
|
||||
try:
|
||||
res = self.task.apply_async(args=args, kwargs=kwargs)
|
||||
except ConnectionError:
|
||||
# Task very likely not yet sent, due to redis restarting etc. Let's try once again
|
||||
res = self.task.apply_async(args=args, kwargs=kwargs)
|
||||
|
||||
if 'ajax' in self.request.GET or 'ajax' in self.request.POST:
|
||||
data = self._return_ajax_result(res)
|
||||
data['check_url'] = self.get_check_url(res.id, True)
|
||||
return JsonResponse(data)
|
||||
else:
|
||||
if res.ready():
|
||||
if res.successful() and not isinstance(res.info, Exception):
|
||||
return self.success(res.info)
|
||||
else:
|
||||
return self.error(res.info)
|
||||
return redirect(self.get_check_url(res.id, False))
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
if 'async_id' in request.GET and settings.HAS_CELERY:
|
||||
return self.get_result(request)
|
||||
return self.http_method_not_allowed(request)
|
||||
|
||||
|
||||
class AsyncFormView(AsyncMixin, FormView):
|
||||
"""
|
||||
FormView variant in which instead of ``form_valid``, an ``async_form_valid``
|
||||
is executed in a celery task. Note that this places some severe limitations
|
||||
on the form and the view, e.g. neither ``get_form*`` nor the form itself
|
||||
may depend on the request object unless specifically supported by this class.
|
||||
Also, all form keyword arguments except ``instance`` need to be serializable.
|
||||
"""
|
||||
known_errortypes = ['ValidationError']
|
||||
|
||||
def __init_subclass__(cls):
|
||||
def async_execute(self, request_path, form_kwargs, organizer=None, event=None, user=None):
|
||||
view_instance = cls()
|
||||
view_instance.request = RequestFactory().post(request_path)
|
||||
if organizer:
|
||||
view_instance.request.event = event
|
||||
if organizer:
|
||||
view_instance.request.organizer = organizer
|
||||
if user:
|
||||
view_instance.request.user = User.objects.get(pk=user)
|
||||
|
||||
form_class = view_instance.get_form_class()
|
||||
if form_kwargs.get('instance'):
|
||||
cls.model.objects.get(pk=form_kwargs['instance'])
|
||||
|
||||
form_kwargs = view_instance.get_async_form_kwargs(form_kwargs, organizer, event)
|
||||
|
||||
form = form_class(**form_kwargs)
|
||||
return view_instance.async_form_valid(self, form)
|
||||
|
||||
cls.async_execute = app.task(
|
||||
base=ProfiledEventTask,
|
||||
bind=True,
|
||||
name=cls.__module__ + '.' + cls.__name__ + '.async_execute',
|
||||
throws=(ValidationError,)
|
||||
)(async_execute)
|
||||
|
||||
def async_form_valid(self, task, form):
|
||||
pass
|
||||
|
||||
def get_async_form_kwargs(self, form_kwargs, organizer=None, event=None):
|
||||
return form_kwargs
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
if 'async_id' in request.GET and settings.HAS_CELERY:
|
||||
return self.get_result(request)
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def form_valid(self, form):
|
||||
if form.files:
|
||||
raise TypeError('File upload currently not supported in AsyncFormView')
|
||||
form_kwargs = {
|
||||
k: v for k, v in self.get_form_kwargs().items()
|
||||
}
|
||||
if form_kwargs.get('instance'):
|
||||
if form_kwargs['instance'].pk:
|
||||
form_kwargs['instance'] = form_kwargs['instance'].pk
|
||||
else:
|
||||
form_kwargs['instance'] = None
|
||||
form_kwargs.setdefault('data', {})
|
||||
kwargs = {
|
||||
'request_path': self.request.path,
|
||||
'form_kwargs': form_kwargs,
|
||||
}
|
||||
if hasattr(self.request, 'organizer'):
|
||||
kwargs['organizer'] = self.request.organizer.pk
|
||||
if self.request.user.is_authenticated:
|
||||
kwargs['user'] = self.request.user.pk
|
||||
if hasattr(self.request, 'event'):
|
||||
kwargs['event'] = self.request.event.pk
|
||||
|
||||
try:
|
||||
res = type(self).async_execute.apply_async(kwargs=kwargs)
|
||||
except ConnectionError:
|
||||
# Task very likely not yet sent, due to redis restarting etc. Let's try once again
|
||||
res = type(self).async_execute.apply_async(kwargs=kwargs)
|
||||
|
||||
if 'ajax' in self.request.GET or 'ajax' in self.request.POST:
|
||||
data = self._return_ajax_result(res)
|
||||
data['check_url'] = self.get_check_url(res.id, True)
|
||||
return JsonResponse(data)
|
||||
else:
|
||||
if res.ready():
|
||||
if res.successful() and not isinstance(res.info, Exception):
|
||||
return self.success(res.info)
|
||||
else:
|
||||
return self.error(res.info)
|
||||
return redirect(self.get_check_url(res.id, False))
|
||||
|
||||
@@ -1439,6 +1439,8 @@ class VoucherFilterForm(FilterForm):
|
||||
s = fdata.get('tag').strip()
|
||||
if s == '<>':
|
||||
qs = qs.filter(Q(tag__isnull=True) | Q(tag=''))
|
||||
elif s[0] == '"' and s[-1] == '"':
|
||||
qs = qs.filter(tag__iexact=s[1:-1])
|
||||
else:
|
||||
qs = qs.filter(tag__icontains=s)
|
||||
|
||||
|
||||
@@ -337,7 +337,7 @@ class ItemCreateForm(I18nModelForm):
|
||||
setattr(self.instance, f, getattr(self.cleaned_data['copy_from'], f))
|
||||
else:
|
||||
# Add to all sales channels by default
|
||||
self.instance.sales_channels = [k for k in get_all_sales_channels().keys()]
|
||||
self.instance.sales_channels = list(get_all_sales_channels().keys())
|
||||
|
||||
self.instance.position = (self.event.items.aggregate(p=Max('position'))['p'] or 0) + 1
|
||||
instance = super().save(*args, **kwargs)
|
||||
|
||||
@@ -75,7 +75,7 @@ class ExtendForm(I18nModelForm):
|
||||
return super().save(commit)
|
||||
|
||||
|
||||
class ConfirmPaymentForm(forms.Form):
|
||||
class ForceQuotaConfirmationForm(forms.Form):
|
||||
force = forms.BooleanField(
|
||||
label=_('Overbook quota and ignore late payment'),
|
||||
help_text=_('If you check this box, this operation will be performed even if it leads to an overbooked quota '
|
||||
@@ -101,7 +101,15 @@ class ConfirmPaymentForm(forms.Form):
|
||||
del self.fields['force']
|
||||
|
||||
|
||||
class CancelForm(ConfirmPaymentForm):
|
||||
class ConfirmPaymentForm(ForceQuotaConfirmationForm):
|
||||
pass
|
||||
|
||||
|
||||
class ReactivateOrderForm(ForceQuotaConfirmationForm):
|
||||
pass
|
||||
|
||||
|
||||
class CancelForm(ForceQuotaConfirmationForm):
|
||||
send_email = forms.BooleanField(
|
||||
required=False,
|
||||
label=_('Notify customer by email'),
|
||||
|
||||
@@ -5,7 +5,7 @@ from io import StringIO
|
||||
from django import forms
|
||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||
from django.core.validators import EmailValidator
|
||||
from django.db.models.functions import Lower
|
||||
from django.db.models.functions import Upper
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _, pgettext_lazy
|
||||
from django_scopes.forms import SafeModelChoiceField
|
||||
@@ -346,8 +346,8 @@ class VoucherBulkForm(VoucherForm):
|
||||
data = super().clean()
|
||||
|
||||
vouchers = self.instance.event.vouchers.annotate(
|
||||
code_lower=Lower('code')
|
||||
).filter(code_lower__in=[c.lower() for c in data['codes']])
|
||||
code_upper=Upper('code')
|
||||
).filter(code_upper__in=[c.upper() for c in data['codes']])
|
||||
if vouchers.exists():
|
||||
raise ValidationError(_('A voucher with one of these codes already exists.'))
|
||||
|
||||
@@ -377,26 +377,5 @@ class VoucherBulkForm(VoucherForm):
|
||||
|
||||
return data
|
||||
|
||||
def save(self, event, *args, **kwargs):
|
||||
objs = []
|
||||
for code in self.cleaned_data['codes']:
|
||||
obj = modelcopy(self.instance)
|
||||
obj.event = event
|
||||
obj.code = code
|
||||
try:
|
||||
obj.seat = self.cleaned_data['seats'].pop()
|
||||
obj.item = obj.seat.product
|
||||
except IndexError:
|
||||
pass
|
||||
data = dict(self.cleaned_data)
|
||||
data['code'] = code
|
||||
data['bulk'] = True
|
||||
del data['codes']
|
||||
objs.append(obj)
|
||||
Voucher.objects.bulk_create(objs, batch_size=200)
|
||||
objs = []
|
||||
for v in event.vouchers.filter(code__in=self.cleaned_data['codes']):
|
||||
# We need to query them again as bulk_create does not fill in .pk values on databases
|
||||
# other than PostgreSQL
|
||||
objs.append(v)
|
||||
return objs
|
||||
def post_bulk_save(self, objs):
|
||||
pass
|
||||
|
||||
@@ -7,7 +7,7 @@ from django.utils.translation import gettext as _
|
||||
|
||||
|
||||
def current_url(request):
|
||||
if len(request.GET):
|
||||
if request.GET:
|
||||
return request.path + '?' + request.GET.urlencode()
|
||||
else:
|
||||
return request.path
|
||||
|
||||
@@ -154,6 +154,11 @@ This signal allows you to replace the form class that is used for modifying vouc
|
||||
You will receive the default form class (or the class set by a previous plugin) in the
|
||||
``cls`` argument so that you can inherit from it.
|
||||
|
||||
Note that this is also called for the voucher bulk creation form, which is executed in
|
||||
an asynchronous context. For the bulk creation form, ``save()`` is not called. Instead,
|
||||
you can implement ``post_bulk_save(saved_vouchers)`` which may be called multiple times
|
||||
for every batch persisted to the database.
|
||||
|
||||
As with all plugin signals, the ``sender`` keyword argument will contain the event.
|
||||
"""
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
{% if request.event.has_subevents %}
|
||||
<form class="form-inline helper-display-inline" action="" method="get">
|
||||
<form class="form-inline helper-display-inline" action="" method="get">
|
||||
{% include "pretixcontrol/event/fragment_subevent_choice_simple.html" %}
|
||||
{% include "pretixcontrol/event/fragment_subevent_choice_simple.html" with auto_submit=True %}
|
||||
</form>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% load i18n %}
|
||||
<p>
|
||||
<select name="subevent" class="form-control simple-subevent-choice" data-model-select2="event"
|
||||
<select name="subevent" class="form-control{% if auto_submit %} simple-subevent-choice{% endif %}" data-model-select2="event"
|
||||
data-select2-url="{% url "control:event.subevents.select2" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||
data-placeholder="{% trans "All dates" context "subevent" %}">
|
||||
{% for se in selected_subevents %}
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
|
||||
{% if request.event.has_subevents %}
|
||||
<form class="form-inline helper-display-inline" action="" method="get">
|
||||
{% include "pretixcontrol/event/fragment_subevent_choice_simple.html" %}
|
||||
{% include "pretixcontrol/event/fragment_subevent_choice_simple.html" with auto_submit=True %}
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if not request.event.has_subevents or subevent %}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</p>
|
||||
{% if request.event.has_subevents %}
|
||||
<form class="form-inline helper-display-inline" action="" method="get">
|
||||
{% include "pretixcontrol/event/fragment_subevent_choice_simple.html" %}
|
||||
{% include "pretixcontrol/event/fragment_subevent_choice_simple.html" with auto_submit=True %}
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if quotas|length == 0 %}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
<form method="post" class="form-horizontal" href="">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form layout='horizontal' horizontal_label_class='sr-only' horizontal_field_class='col-md-12' %}
|
||||
<div class="form-group submit-group">
|
||||
<a class="btn btn-default btn-lg"
|
||||
href="{% url "control:event.order" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}">
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
{% block title %}{% trans "Voucher" %}{% endblock %}
|
||||
{% block inside %}
|
||||
<h1>{% trans "Create multiple vouchers" %}</h1>
|
||||
<form action="" method="post" class="form-horizontal">
|
||||
<form action="" method="post" class="form-horizontal" data-asynctask>
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form_errors form %}
|
||||
<fieldset>
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
<td>
|
||||
<strong>
|
||||
{% if t.tag %}
|
||||
<a href="{% url "control:event.vouchers" organizer=request.event.organizer.slug event=request.event.slug %}?tag={{ t.tag|urlencode }}">
|
||||
<a href="{% url "control:event.vouchers" organizer=request.event.organizer.slug event=request.event.slug %}?tag={{ '"'|add:t.tag|add:'"'|urlencode }}">
|
||||
{{ t.tag }}
|
||||
</a>
|
||||
{% else %}
|
||||
|
||||
@@ -48,15 +48,9 @@
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if request.event.has_subevents %}
|
||||
<select name="subevent" class="form-control">
|
||||
<option value="">{% trans "All dates" context "subevent" %}</option>
|
||||
{% for se in request.event.subevents.all %}
|
||||
<option value="{{ se.id }}"
|
||||
{% if request.GET.subevent|add:0 == se.id %}selected="selected"{% endif %}>
|
||||
{{ se.name }} – {{ se.get_date_range_display }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<div class="col-md-6">
|
||||
{% include "pretixcontrol/event/fragment_subevent_choice_simple.html" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<button class="btn btn-large btn-primary" type="submit">
|
||||
{% trans "Send as many vouchers as possible" %}
|
||||
@@ -80,52 +74,50 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<form class="form-inline helper-display-inline" action="" method="get">
|
||||
<select name="status" class="form-control">
|
||||
<option value="a"
|
||||
{% if request.GET.status == "p" %}selected="selected"{% endif %}>{% trans "All entries" %}</option>
|
||||
<option value="w"
|
||||
{% if request.GET.status == "w" or not request.GET.status %}selected="selected"{% endif %}>
|
||||
{% trans "Waiting for a voucher" %}</option>
|
||||
<option value="s"
|
||||
{% if request.GET.status == "s" %}selected="selected"{% endif %}>{% trans "Voucher assigned" %}</option>
|
||||
<option value="v"
|
||||
{% if request.GET.status == "v" %}selected="selected"{% endif %}>
|
||||
{% trans "Waiting for redemption" %}</option>
|
||||
<option value="r"
|
||||
{% if request.GET.status == "r" %}selected="selected"{% endif %}>
|
||||
{% trans "Successfully redeemed" %}</option>
|
||||
<option value="e"
|
||||
{% if request.GET.status == "e" %}selected="selected"{% endif %}>
|
||||
{% trans "Voucher expired" %}</option>
|
||||
</select>
|
||||
<select name="item" class="form-control">
|
||||
<option value="">{% trans "All products" %}</option>
|
||||
{% for item in items %}
|
||||
<option value="{{ item.id }}"
|
||||
{% if request.GET.item|add:0 == item.id %}selected="selected"{% endif %}>
|
||||
{{ item }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% if request.event.has_subevents %}
|
||||
<select name="subevent" class="form-control">
|
||||
<option value="">{% trans "All dates" context "subevent" %}</option>
|
||||
{% for se in request.event.subevents.all %}
|
||||
<option value="{{ se.id }}"
|
||||
{% if request.GET.subevent|add:0 == se.id %}selected="selected"{% endif %}>
|
||||
{{ se.name }} – {{ se.get_date_range_display }}
|
||||
<form class="row filter-form" action="" method="get">
|
||||
<div class="col-lg-2 col-md-3 col-xs-6">
|
||||
<select name="status" class="form-control">
|
||||
<option value="a"
|
||||
{% if request.GET.status == "p" %}selected="selected"{% endif %}>{% trans "All entries" %}</option>
|
||||
<option value="w"
|
||||
{% if request.GET.status == "w" or not request.GET.status %}selected="selected"{% endif %}>
|
||||
{% trans "Waiting for a voucher" %}</option>
|
||||
<option value="s"
|
||||
{% if request.GET.status == "s" %}selected="selected"{% endif %}>{% trans "Voucher assigned" %}</option>
|
||||
<option value="v"
|
||||
{% if request.GET.status == "v" %}selected="selected"{% endif %}>
|
||||
{% trans "Waiting for redemption" %}</option>
|
||||
<option value="r"
|
||||
{% if request.GET.status == "r" %}selected="selected"{% endif %}>
|
||||
{% trans "Successfully redeemed" %}</option>
|
||||
<option value="e"
|
||||
{% if request.GET.status == "e" %}selected="selected"{% endif %}>
|
||||
{% trans "Voucher expired" %}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-lg-2 col-md-3 col-xs-6">
|
||||
<select name="item" class="form-control">
|
||||
<option value="">{% trans "All products" %}</option>
|
||||
{% for item in items %}
|
||||
<option value="{{ item.id }}"
|
||||
{% if request.GET.item|add:0 == item.id %}selected="selected"{% endif %}>
|
||||
{{ item }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{% if request.event.has_subevents %}
|
||||
<div class="col-lg-4 col-md-6 col-sm-12 col-xs-12">
|
||||
{% include "pretixcontrol/event/fragment_subevent_choice_simple.html" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<button class="btn btn-primary" type="submit">{% trans "Filter" %}</button>
|
||||
<a href="?{% url_replace request "download" "yes" %}"
|
||||
<div class="col-lg-4 col-md-6 col-sm-12 col-xs-12">
|
||||
<button class="btn btn-primary" type="submit"><span class="fa fa-filter"></span> {% trans "Filter" %}</button>
|
||||
<a href="?{% url_replace request "download" "yes" %}"
|
||||
class="btn btn-default"><i class="fa fa-download"></i>
|
||||
{% trans "Download list" %}</a>
|
||||
{% trans "Download list" %}</a>
|
||||
</div>
|
||||
</form>
|
||||
</p>
|
||||
<form method="post" action="?next={{ request.get_full_path|urlencode }}">
|
||||
{% csrf_token %}
|
||||
<div class="table-responsive">
|
||||
@@ -166,7 +158,7 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if request.event.has_subevents %}
|
||||
<td>{{ e.subevent.name }} – {{ e.subevent.get_date_range_display }}</td>
|
||||
<td>{{ e.subevent.name }} – {{ e.subevent.get_date_range_display }} {{ e.subevent.date_from|date:"TIME_FORMAT" }}</td>
|
||||
{% endif %}
|
||||
<td>
|
||||
{{ e.created|date:"SHORT_DATETIME_FORMAT" }}
|
||||
|
||||
@@ -955,11 +955,10 @@ class EventDelete(RecentAuthenticationRequiredMixin, EventPermissionRequiredMixi
|
||||
return reverse('control:index')
|
||||
|
||||
|
||||
class EventLog(EventPermissionRequiredMixin, ListView):
|
||||
class EventLog(EventPermissionRequiredMixin, PaginationMixin, ListView):
|
||||
template_name = 'pretixcontrol/event/logs.html'
|
||||
model = LogEntry
|
||||
context_object_name = 'logs'
|
||||
paginate_by = 20
|
||||
|
||||
def get_queryset(self):
|
||||
qs = self.request.event.logentry_set.all().select_related(
|
||||
@@ -1363,7 +1362,7 @@ class QuickSetupView(FormView):
|
||||
tax_rule=tax_rule,
|
||||
admission=True,
|
||||
position=i,
|
||||
sales_channels=[k for k in get_all_sales_channels().keys()]
|
||||
sales_channels=list(get_all_sales_channels().keys())
|
||||
)
|
||||
item.log_action('pretix.event.item.added', user=self.request.user, data=dict(f.cleaned_data))
|
||||
if f.cleaned_data['quota'] or not form.cleaned_data['total_quota']:
|
||||
|
||||
@@ -83,7 +83,7 @@ from pretix.control.forms.orders import (
|
||||
ExtendForm, MarkPaidForm, OrderContactForm, OrderFeeChangeForm,
|
||||
OrderLocaleForm, OrderMailForm, OrderPositionAddForm,
|
||||
OrderPositionAddFormset, OrderPositionChangeForm, OrderPositionMailForm,
|
||||
OrderRefundForm, OtherOperationsForm,
|
||||
OrderRefundForm, OtherOperationsForm, ReactivateOrderForm,
|
||||
)
|
||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||
from pretix.control.signals import order_search_forms
|
||||
@@ -1424,11 +1424,24 @@ class OrderExtend(OrderView):
|
||||
class OrderReactivate(OrderView):
|
||||
permission = 'can_change_orders'
|
||||
|
||||
@cached_property
|
||||
def reactivate_form(self):
|
||||
return ReactivateOrderForm(
|
||||
instance=self.order,
|
||||
data=self.request.POST if self.request.method == "POST" else None,
|
||||
)
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
if not self.reactivate_form.is_valid():
|
||||
return render(self.request, 'pretixcontrol/order/reactivate.html', {
|
||||
'form': self.reactivate_form,
|
||||
'order': self.order,
|
||||
})
|
||||
try:
|
||||
reactivate_order(
|
||||
self.order,
|
||||
user=self.request.user
|
||||
user=self.request.user,
|
||||
force=self.reactivate_form.cleaned_data.get('force', False)
|
||||
)
|
||||
messages.success(self.request, _('The order has been reactivated.'))
|
||||
except OrderError as e:
|
||||
@@ -1453,6 +1466,7 @@ class OrderReactivate(OrderView):
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
return render(self.request, 'pretixcontrol/order/reactivate.html', {
|
||||
'form': self.reactivate_form,
|
||||
'order': self.order,
|
||||
})
|
||||
|
||||
|
||||
@@ -1438,12 +1438,11 @@ class EventMetaPropertyDeleteView(OrganizerDetailViewMixin, OrganizerPermissionR
|
||||
return redirect(success_url)
|
||||
|
||||
|
||||
class LogView(OrganizerPermissionRequiredMixin, ListView):
|
||||
class LogView(OrganizerPermissionRequiredMixin, PaginationMixin, ListView):
|
||||
template_name = 'pretixcontrol/organizers/logs.html'
|
||||
permission = 'can_change_organizer_settings'
|
||||
model = LogEntry
|
||||
context_object_name = 'logs'
|
||||
paginate_by = 20
|
||||
|
||||
def get_queryset(self):
|
||||
qs = self.request.organizer.all_logentries().select_related(
|
||||
|
||||
@@ -3,7 +3,8 @@ import io
|
||||
from defusedcsv import csv
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.db import transaction
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import connection, transaction
|
||||
from django.db.models import Sum
|
||||
from django.http import (
|
||||
Http404, HttpResponse, HttpResponseBadRequest, HttpResponseRedirect,
|
||||
@@ -21,7 +22,9 @@ from django.views.generic import (
|
||||
|
||||
from pretix.base.models import CartPosition, LogEntry, OrderPosition, Voucher
|
||||
from pretix.base.models.vouchers import _generate_random_code
|
||||
from pretix.base.services.locking import NoLockManager
|
||||
from pretix.base.services.vouchers import vouchers_send
|
||||
from pretix.base.views.tasks import AsyncFormView
|
||||
from pretix.control.forms.filter import VoucherFilterForm, VoucherTagFilterForm
|
||||
from pretix.control.forms.vouchers import VoucherBulkForm, VoucherForm
|
||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||
@@ -287,13 +290,19 @@ class VoucherGo(EventPermissionRequiredMixin, View):
|
||||
return redirect('control:event.vouchers', event=request.event.slug, organizer=request.event.organizer.slug)
|
||||
|
||||
|
||||
class VoucherBulkCreate(EventPermissionRequiredMixin, CreateView):
|
||||
class VoucherBulkCreate(EventPermissionRequiredMixin, AsyncFormView):
|
||||
model = Voucher
|
||||
template_name = 'pretixcontrol/vouchers/bulk.html'
|
||||
permission = 'can_change_vouchers'
|
||||
context_object_name = 'voucher'
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
def get_success_url(self, value) -> str:
|
||||
return reverse('control:event.vouchers', kwargs={
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
'event': self.request.event.slug,
|
||||
})
|
||||
|
||||
def get_error_url(self):
|
||||
return reverse('control:event.vouchers', kwargs={
|
||||
'organizer': self.request.event.organizer.slug,
|
||||
'event': self.request.event.slug,
|
||||
@@ -316,34 +325,84 @@ class VoucherBulkCreate(EventPermissionRequiredMixin, CreateView):
|
||||
i.redeemed = 0
|
||||
kwargs['instance'] = i
|
||||
else:
|
||||
kwargs['instance'] = Voucher(event=self.request.event)
|
||||
kwargs['instance'] = Voucher(event=self.request.event, code=None)
|
||||
return kwargs
|
||||
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
log_entries = []
|
||||
objs = form.save(self.request.event)
|
||||
def get_async_form_kwargs(self, form_kwargs, organizer=None, event=None):
|
||||
if not form_kwargs.get('instance'):
|
||||
form_kwargs['instance'] = Voucher(event=self.request.event, code=None)
|
||||
return form_kwargs
|
||||
|
||||
def async_form_valid(self, task, form):
|
||||
lockfn = NoLockManager
|
||||
if form.data.get('block_quota'):
|
||||
lockfn = self.request.event.lock
|
||||
batch_size = 500
|
||||
total_num = 1 # will be set later
|
||||
|
||||
def set_progress(percent):
|
||||
if not task.request.called_directly:
|
||||
task.update_state(
|
||||
state='PROGRESS',
|
||||
meta={'value': percent}
|
||||
)
|
||||
|
||||
def process_batch(batch_vouchers, voucherids):
|
||||
Voucher.objects.bulk_create(batch_vouchers)
|
||||
if not connection.features.can_return_rows_from_bulk_insert:
|
||||
batch_vouchers = list(self.request.event.vouchers.filter(code__in=[v.code for v in batch_vouchers]))
|
||||
|
||||
log_entries = []
|
||||
for v in batch_vouchers:
|
||||
voucherids.append(v.pk)
|
||||
data = dict(form.cleaned_data)
|
||||
data['code'] = code
|
||||
data['bulk'] = True
|
||||
del data['codes']
|
||||
log_entries.append(
|
||||
v.log_action('pretix.voucher.added', data=data, user=self.request.user, save=False)
|
||||
)
|
||||
LogEntry.objects.bulk_create(log_entries)
|
||||
form.post_bulk_save(batch_vouchers)
|
||||
batch_vouchers.clear()
|
||||
set_progress(len(voucherids) / total_num * (50. if form.cleaned_data['send'] else 100.))
|
||||
|
||||
voucherids = []
|
||||
for v in objs:
|
||||
log_entries.append(
|
||||
v.log_action('pretix.voucher.added', data=form.cleaned_data, user=self.request.user, save=False)
|
||||
)
|
||||
voucherids.append(v.pk)
|
||||
LogEntry.objects.bulk_create(log_entries, batch_size=200)
|
||||
with lockfn(), transaction.atomic():
|
||||
if not form.is_valid():
|
||||
raise ValidationError(form.errors)
|
||||
total_num = len(form.cleaned_data['codes'])
|
||||
|
||||
batch_vouchers = []
|
||||
for code in form.cleaned_data['codes']:
|
||||
if len(batch_vouchers) > batch_size:
|
||||
process_batch(batch_vouchers, voucherids)
|
||||
|
||||
obj = modelcopy(form.instance, code=None)
|
||||
obj.event = self.request.event
|
||||
obj.code = code
|
||||
try:
|
||||
obj.seat = form.cleaned_data['seats'].pop()
|
||||
obj.item = obj.seat.product
|
||||
except IndexError:
|
||||
pass
|
||||
batch_vouchers.append(obj)
|
||||
|
||||
process_batch(batch_vouchers, voucherids)
|
||||
|
||||
if form.cleaned_data['send']:
|
||||
vouchers_send.apply_async(kwargs={
|
||||
'event': self.request.event.pk,
|
||||
'vouchers': voucherids,
|
||||
'subject': form.cleaned_data['send_subject'],
|
||||
'message': form.cleaned_data['send_message'],
|
||||
'recipients': [r._asdict() for r in form.cleaned_data['send_recipients']],
|
||||
'user': self.request.user.pk,
|
||||
})
|
||||
messages.success(self.request, _('The new vouchers have been created and will be sent out shortly.'))
|
||||
else:
|
||||
messages.success(self.request, _('The new vouchers have been created.'))
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
vouchers_send(
|
||||
event=self.request.event,
|
||||
vouchers=voucherids,
|
||||
subject=form.cleaned_data['send_subject'],
|
||||
message=form.cleaned_data['send_message'],
|
||||
recipients=[r._asdict() for r in form.cleaned_data['send_recipients']],
|
||||
user=self.request.user.pk,
|
||||
progress=lambda p: set_progress(50. + p * 50.)
|
||||
)
|
||||
|
||||
def get_success_message(self, value):
|
||||
return _('The new vouchers have been created.')
|
||||
|
||||
def get_form_class(self):
|
||||
form_class = VoucherBulkForm
|
||||
@@ -357,11 +416,6 @@ class VoucherBulkCreate(EventPermissionRequiredMixin, CreateView):
|
||||
ctx['code_length'] = settings.ENTROPY['voucher_code']
|
||||
return ctx
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
# TODO: Transform this into an asynchronous call?
|
||||
with request.event.lock():
|
||||
return super().post(request, *args, **kwargs)
|
||||
|
||||
|
||||
class VoucherRNG(EventPermissionRequiredMixin, View):
|
||||
permission = 'can_change_vouchers'
|
||||
|
||||
@@ -94,7 +94,7 @@ def merge(*args):
|
||||
"""Implements the 'merge' operator for merging lists."""
|
||||
ret = []
|
||||
for arg in args:
|
||||
if isinstance(arg, list) or isinstance(arg, tuple):
|
||||
if isinstance(arg, (list, tuple)):
|
||||
ret += list(arg)
|
||||
else:
|
||||
ret.append(arg)
|
||||
|
||||
@@ -12,8 +12,8 @@ class Thumbnail(models.Model):
|
||||
unique_together = (('source', 'size'),)
|
||||
|
||||
|
||||
def modelcopy(obj: models.Model):
|
||||
n = obj.__class__()
|
||||
def modelcopy(obj: models.Model, **kwargs):
|
||||
n = obj.__class__(**kwargs)
|
||||
for f in obj._meta.fields:
|
||||
val = getattr(obj, f.name)
|
||||
if isinstance(val, models.Model):
|
||||
|
||||
@@ -7,16 +7,16 @@ msgstr ""
|
||||
"Project-Id-Version: 1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-03-08 16:39+0000\n"
|
||||
"PO-Revision-Date: 2021-01-29 11:23+0000\n"
|
||||
"Last-Translator: Maarten van den Berg <maartenberg1@gmail.com>\n"
|
||||
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix/nl/"
|
||||
">\n"
|
||||
"PO-Revision-Date: 2021-03-14 17:33+0000\n"
|
||||
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
||||
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix/nl/>"
|
||||
"\n"
|
||||
"Language: nl\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 3.10.3\n"
|
||||
"X-Generator: Weblate 4.4.2\n"
|
||||
|
||||
#: htmlcov/pretix_control_views_dashboards_py.html:898
|
||||
#: pretix/control/templates/pretixcontrol/events/index.html:144
|
||||
@@ -154,10 +154,10 @@ msgid "Meta data property '{name}' does not exist."
|
||||
msgstr "Metadataeigenschap '{name}' bestaat niet."
|
||||
|
||||
#: pretix/api/serializers/event.py:182 pretix/api/serializers/event.py:466
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid "Meta data property '{name}' does not exist."
|
||||
#, python-brace-format
|
||||
msgid "Meta data property '{name}' does not allow value '{value}'."
|
||||
msgstr "Metadataeigenschap '{name}' bestaat niet."
|
||||
msgstr ""
|
||||
"De waarde '{value}' is niet toegestaan voor de metadataeigenschap '{name}'."
|
||||
|
||||
#: pretix/api/serializers/event.py:225
|
||||
#, python-brace-format
|
||||
@@ -2246,7 +2246,7 @@ msgstr "U moet ten minste één quotum instellen om iets te verkopen."
|
||||
#: pretix/base/models/event.py:960
|
||||
#, python-brace-format
|
||||
msgid "You need to fill the meta parameter \"{property}\"."
|
||||
msgstr ""
|
||||
msgstr "U moet de meta-eigenschap \"{property}\" invullen."
|
||||
|
||||
#: pretix/base/models/event.py:1065
|
||||
msgid ""
|
||||
@@ -2324,50 +2324,49 @@ msgstr ""
|
||||
"onderstrepingstekens bevatten."
|
||||
|
||||
#: pretix/base/models/event.py:1379
|
||||
#, fuzzy
|
||||
#| msgid "Default language"
|
||||
msgid "Default value"
|
||||
msgstr "Standaardtaal"
|
||||
msgstr "Standaardwaarde"
|
||||
|
||||
#: pretix/base/models/event.py:1381
|
||||
#, fuzzy
|
||||
#| msgid "Can change organizer settings"
|
||||
msgid "Can only be changed by organizer-level administrators"
|
||||
msgstr "Kan organisatorinstellingen wijzigen"
|
||||
msgstr "Kan alleen worden gewijzigd door beheerders van deze organisator"
|
||||
|
||||
#: pretix/base/models/event.py:1383
|
||||
#, fuzzy
|
||||
#| msgid "Search for events"
|
||||
msgid "Required for events"
|
||||
msgstr "Zoek naar evenementen"
|
||||
msgstr "Verplicht voor evenementen"
|
||||
|
||||
#: pretix/base/models/event.py:1384
|
||||
msgid ""
|
||||
"If checked, an event can only be taken live if the property is set. In event "
|
||||
"series, its always optional to set a value for individual dates"
|
||||
msgstr ""
|
||||
"Als deze optie is ingeschakeld kan een evenement alleen live worden gezet "
|
||||
"als deze eigenschap een waarde heeft. In evenementenreeksen is het altijd "
|
||||
"optioneel om een waarde voor individuele datums in te stellen."
|
||||
|
||||
#: pretix/base/models/event.py:1389
|
||||
#, fuzzy
|
||||
#| msgid "Total value"
|
||||
msgid "Valid values"
|
||||
msgstr "Totaalwaarde"
|
||||
msgstr "Toegestane waarden"
|
||||
|
||||
#: pretix/base/models/event.py:1390
|
||||
msgid ""
|
||||
"If you keep this empty, any value is allowed. Otherwise, enter one possible "
|
||||
"value per line."
|
||||
msgstr ""
|
||||
"Voer hier een toegestane waarde per regel in. Als u dit veld leeg laat wordt "
|
||||
"iedere waarde toegestaan."
|
||||
|
||||
#: pretix/base/models/event.py:1396
|
||||
msgid "A property can either be required or have a default value, not both."
|
||||
msgstr ""
|
||||
"Een eigenschap kan niet verplicht zijn en tegelijkertijd een standaardwaarde "
|
||||
"hebben."
|
||||
|
||||
#: pretix/base/models/event.py:1398
|
||||
#, fuzzy
|
||||
#| msgid "You cannot select a quota that belongs to a different event."
|
||||
msgid "You cannot set a default value that is not a valid value."
|
||||
msgstr "U kunt geen quotum selecteren dat bij een ander evenement hoort."
|
||||
msgstr ""
|
||||
"U kunt geen standaardwaarde instellen die niet in de lijst met toegestane "
|
||||
"waarden staat."
|
||||
|
||||
#: pretix/base/models/fields.py:12
|
||||
msgid "No value can contain the delimiter character."
|
||||
@@ -4421,12 +4420,7 @@ msgid "Payment process description in order confirmation emails"
|
||||
msgstr "Beschrijving van betalingsproces in bevestigingsmails"
|
||||
|
||||
#: pretix/base/payment.py:971
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid ""
|
||||
#| "This text will be included for the {payment_info} placeholder in order "
|
||||
#| "confirmation mails. It should instruct the user on how to proceed with "
|
||||
#| "the payment. You can use the placeholders {order}, {total}, {currency} "
|
||||
#| "and {total_with_currency}."
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"This text will be included for the {payment_info} placeholder in order "
|
||||
"confirmation mails. It should instruct the user on how to proceed with the "
|
||||
@@ -4436,7 +4430,7 @@ msgstr ""
|
||||
"Deze tekst zal worden ingevoegd op de plaats van de {payment_info}-"
|
||||
"plaatsaanduiding in bevestigingsmails voor een bestelling. De tekst moet de "
|
||||
"gebruiker informeren hoe verder te gaan met de betaling. U kunt hier de "
|
||||
"plaatsaanduidingen {order}, {total}, {currency} en {total_with_currency} "
|
||||
"plaatsaanduidingen {order}, {amount}, {currency} en {amount_with_currency} "
|
||||
"gebruiken."
|
||||
|
||||
#: pretix/base/payment.py:978
|
||||
@@ -4444,12 +4438,7 @@ msgid "Payment process description for pending orders"
|
||||
msgstr "Beschrijving van betalingsproces voor openstaande bestellingen"
|
||||
|
||||
#: pretix/base/payment.py:979
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid ""
|
||||
#| "This text will be shown on the order confirmation page for pending "
|
||||
#| "orders. It should instruct the user on how to proceed with the payment. "
|
||||
#| "You can use the placeholders {order}, {total}, {currency} and "
|
||||
#| "{total_with_currency}."
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"This text will be shown on the order confirmation page for pending orders. "
|
||||
"It should instruct the user on how to proceed with the payment. You can use "
|
||||
@@ -4457,8 +4446,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Deze tekst zal worden getoond op de bevestigingspagina van openstaande "
|
||||
"bestellingen. De tekst moet de gebruiker informeren hoe verder te gaan met "
|
||||
"de betaling. U kunt hier de plaatsaanduidingen {order}, {total}, {currency} "
|
||||
"en {total_with_currency} gebruiken."
|
||||
"de betaling. U kunt hier de plaatsaanduidingen {order}, {amount}, {currency} "
|
||||
"en {amount_with_currency} gebruiken."
|
||||
|
||||
#: pretix/base/payment.py:1028
|
||||
msgid "Offsetting"
|
||||
@@ -4804,13 +4793,11 @@ msgstr "Informatietekst organisator"
|
||||
|
||||
#: pretix/base/pdf.py:298
|
||||
msgid "Event organizer info text"
|
||||
msgstr "Informatietekst van de organisator van het evenement"
|
||||
msgstr "Informatie over de organisator van het evenement"
|
||||
|
||||
#: pretix/base/pdf.py:302 pretix/base/pdf.py:303
|
||||
#, fuzzy
|
||||
#| msgid "Event organizer info text"
|
||||
msgid "Event info text"
|
||||
msgstr "Informatietekst van de organisator van het evenement"
|
||||
msgstr "Informatie over het evenement"
|
||||
|
||||
#: pretix/base/pdf.py:307
|
||||
msgid "Printing date"
|
||||
@@ -6454,64 +6441,52 @@ msgstr ""
|
||||
"aan de volgende persoon op de lijst."
|
||||
|
||||
#: pretix/base/settings.py:984
|
||||
#, fuzzy
|
||||
#| msgid "Ask for attendee names"
|
||||
msgid "Ask for a name"
|
||||
msgstr "Vraag om namen van gasten"
|
||||
msgstr "Vraag om namen"
|
||||
|
||||
#: pretix/base/settings.py:985
|
||||
#, fuzzy
|
||||
#| msgid "An entry has been changed on the waiting list."
|
||||
msgid "Ask for a name when signing up to the waiting list."
|
||||
msgstr "Een inschrijving op de wachtlijst is aangepast."
|
||||
msgstr "Vraag om een naam bij het aanmelden voor de wachtlijst."
|
||||
|
||||
#: pretix/base/settings.py:994
|
||||
#, fuzzy
|
||||
#| msgid "Require customer name"
|
||||
msgid "Require name"
|
||||
msgstr "Verplicht klantnaam"
|
||||
msgstr "Verplicht namen"
|
||||
|
||||
#: pretix/base/settings.py:995
|
||||
#, fuzzy
|
||||
#| msgid "An entry has been changed on the waiting list."
|
||||
msgid "Require a name when signing up to the waiting list.."
|
||||
msgstr "Een inschrijving op de wachtlijst is aangepast."
|
||||
msgstr ""
|
||||
"Maakt het opgeven van een naam verplicht om in te schrijven voor de "
|
||||
"wachtlijst."
|
||||
|
||||
#: pretix/base/settings.py:1005
|
||||
#, fuzzy
|
||||
#| msgid "Ask for a phone number per order"
|
||||
msgid "Ask for a phone number"
|
||||
msgstr "Vraag om een telefoonnummer bij bestelling"
|
||||
msgstr "Vraag om een telefoonnummer"
|
||||
|
||||
#: pretix/base/settings.py:1006
|
||||
#, fuzzy
|
||||
#| msgid "An entry has been changed on the waiting list."
|
||||
msgid "Ask for a phone number when signing up to the waiting list."
|
||||
msgstr "Een inschrijving op de wachtlijst is aangepast."
|
||||
msgstr "Vraag om een telefoonnummer bij het inschrijven voor de wachtlijst."
|
||||
|
||||
#: pretix/base/settings.py:1015
|
||||
#, fuzzy
|
||||
#| msgid "Require a phone number per order"
|
||||
msgid "Require phone number"
|
||||
msgstr "Verplicht het opgeven van een telefoonnummer"
|
||||
msgstr "Verplicht telefoonnummer"
|
||||
|
||||
#: pretix/base/settings.py:1016
|
||||
#, fuzzy
|
||||
#| msgid "An entry has been changed on the waiting list."
|
||||
msgid "Require a phone number when signing up to the waiting list.."
|
||||
msgstr "Een inschrijving op de wachtlijst is aangepast."
|
||||
msgstr ""
|
||||
"Maakt het opgeven van een telefoonnummer verplicht bij het inschrijven voor "
|
||||
"de wachtlijst."
|
||||
|
||||
#: pretix/base/settings.py:1026
|
||||
#, fuzzy
|
||||
#| msgid "Voucher explanation"
|
||||
msgid "Phone number explanation"
|
||||
msgstr "Voucher-uitleg"
|
||||
msgstr "Uitleg voor telefoonnummer"
|
||||
|
||||
#: pretix/base/settings.py:1029
|
||||
msgid ""
|
||||
"If you ask for a phone number, explain why you do so and what you will use "
|
||||
"the phone number for."
|
||||
msgstr ""
|
||||
"Als u om een telefoonnummer vraagt kunt u in dit veld uitleggen waarom en "
|
||||
"hoe u de verzamelde telefoonnummers zult gebruiken."
|
||||
|
||||
#: pretix/base/settings.py:1039
|
||||
msgid "Allow users to download tickets"
|
||||
@@ -6641,6 +6616,7 @@ msgstr ""
|
||||
#: pretix/base/settings.py:1147
|
||||
msgid "Allow customers to modify their information after they checked in."
|
||||
msgstr ""
|
||||
"Sta klanten toe om hun informatie aan te passen nadat ze ingecheckt zijn."
|
||||
|
||||
#: pretix/base/settings.py:1156
|
||||
msgid "Last date of modifications"
|
||||
@@ -6836,10 +6812,8 @@ msgstr ""
|
||||
"contactinformatie en eventuele wettelijk verplichte informatie bevat."
|
||||
|
||||
#: pretix/base/settings.py:1387
|
||||
#, fuzzy
|
||||
#| msgid "Cached ticket files"
|
||||
msgid "Attach ticket files"
|
||||
msgstr "Gecachete ticketbestanden"
|
||||
msgstr "Ticketbestanden bijvoegen bij e-mails"
|
||||
|
||||
#: pretix/base/settings.py:1389
|
||||
#, python-brace-format
|
||||
@@ -6847,6 +6821,8 @@ msgid ""
|
||||
"Tickets will never be attached if they're larger than {size} to avoid email "
|
||||
"delivery problems."
|
||||
msgstr ""
|
||||
"Tickets worden nooit bijgevoegd bij een e-mail als ze groter zijn dan {size}"
|
||||
", om problemen met het versturen van de e-mail te voorkomen."
|
||||
|
||||
#: pretix/base/settings.py:1400
|
||||
msgid "Attach calendar files"
|
||||
@@ -7583,22 +7559,16 @@ msgstr ""
|
||||
"informatie vraagt."
|
||||
|
||||
#: pretix/base/settings.py:2073
|
||||
#, fuzzy
|
||||
#| msgid "Additional fee"
|
||||
msgid "Additional success message"
|
||||
msgstr "Extra kosten"
|
||||
msgstr "Extra succesbericht na het plaatsen van een bestelling"
|
||||
|
||||
#: pretix/base/settings.py:2074
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "This text will be shown on the order confirmation page for pending orders "
|
||||
#| "in addition to the standard text."
|
||||
msgid ""
|
||||
"This message will be shown after an order has been created successfully. It "
|
||||
"will be shown in additional to the default text."
|
||||
msgstr ""
|
||||
"Deze tekst zal naast de standaardtekst worden getoond op de "
|
||||
"bevestigingspagina voor openstaande bestellingen."
|
||||
"Deze tekst zal worden getoond nadat een klant een bestelling heeft "
|
||||
"geplaatst. Deze tekst wordt samen met de standaardtekst getoond."
|
||||
|
||||
#: pretix/base/settings.py:2086
|
||||
msgid "Help text of the phone number field"
|
||||
@@ -7883,12 +7853,12 @@ msgstr ""
|
||||
"met opgeslagen emailinhoud."
|
||||
|
||||
#: pretix/base/shredder.py:206
|
||||
#, fuzzy
|
||||
#| msgid "This will remove all email addresses from the waiting list."
|
||||
msgid ""
|
||||
"This will remove all names, email addresses, and phone numbers from the "
|
||||
"waiting list."
|
||||
msgstr "Dit zal alle e-mailadressen van de wachtlijst verwijderen."
|
||||
msgstr ""
|
||||
"Dit zal alle namen, e-mailadressen en telefoonnummers van de wachtlijst "
|
||||
"verwijderen."
|
||||
|
||||
#: pretix/base/shredder.py:239
|
||||
msgid "Attendee info"
|
||||
@@ -8093,25 +8063,18 @@ msgid "Order code:"
|
||||
msgstr "Bestelcode:"
|
||||
|
||||
#: pretix/base/templates/pretixbase/email/order_details.html:30
|
||||
#, fuzzy
|
||||
#| msgctxt "payment_state"
|
||||
#| msgid "created"
|
||||
msgid "created by"
|
||||
msgstr "aangemaakt"
|
||||
msgstr "aangemaakt door"
|
||||
|
||||
#: pretix/base/templates/pretixbase/email/order_details.html:36
|
||||
#: pretix/base/templates/pretixbase/email/order_details.html:92
|
||||
#, fuzzy
|
||||
#| msgid "Order status"
|
||||
msgid "Order status:"
|
||||
msgstr "Bestelstatus"
|
||||
msgstr "Bestelstatus:"
|
||||
|
||||
#: pretix/base/templates/pretixbase/email/order_details.html:44
|
||||
#: pretix/base/templates/pretixbase/email/order_details.html:140
|
||||
#, fuzzy
|
||||
#| msgid "Organizer"
|
||||
msgid "Organizer:"
|
||||
msgstr "Organisator"
|
||||
msgstr "Organisator:"
|
||||
|
||||
#: pretix/base/templates/pretixbase/email/order_details.html:59
|
||||
msgid "View registration details"
|
||||
@@ -8126,10 +8089,8 @@ msgstr ""
|
||||
"geplaatst:"
|
||||
|
||||
#: pretix/base/templates/pretixbase/email/order_details.html:101
|
||||
#, fuzzy
|
||||
#| msgid "Details"
|
||||
msgid "Details:"
|
||||
msgstr "Details"
|
||||
msgstr "Details:"
|
||||
|
||||
#: pretix/base/templates/pretixbase/forms/widgets/reldate.html:15
|
||||
#: pretix/base/templates/pretixbase/forms/widgets/reldatetime.html:19
|
||||
@@ -8379,10 +8340,9 @@ msgid "Do not copy"
|
||||
msgstr "Niet kopiëren"
|
||||
|
||||
#: pretix/control/forms/event.py:274 pretix/control/forms/subevents.py:309
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid "Default language"
|
||||
#, python-brace-format
|
||||
msgid "Default ({value})"
|
||||
msgstr "Standaardtaal"
|
||||
msgstr "Standaard ({value})"
|
||||
|
||||
#: pretix/control/forms/event.py:326 pretix/control/forms/organizer.py:84
|
||||
msgid "Custom domain"
|
||||
@@ -8902,7 +8862,7 @@ msgstr "Annulering aangevraagd"
|
||||
|
||||
#: pretix/control/forms/filter.py:158
|
||||
msgid "Fully canceled but invoice not canceled"
|
||||
msgstr ""
|
||||
msgstr "Compleet geannuleerd, maar factuur niet geannuleerd"
|
||||
|
||||
#: pretix/control/forms/filter.py:160
|
||||
msgid "Payment process"
|
||||
@@ -9532,10 +9492,8 @@ msgstr ""
|
||||
"betalingen worden uitgevoerd."
|
||||
|
||||
#: pretix/control/forms/orders.py:107 pretix/control/forms/orders.py:151
|
||||
#, fuzzy
|
||||
#| msgid "Notify user by e-mail"
|
||||
msgid "Notify customer by email"
|
||||
msgstr "Stel de gebruiker per e-mail op de hoogte"
|
||||
msgstr "Stel de klant per e-mail op de hoogte"
|
||||
|
||||
#: pretix/control/forms/orders.py:114
|
||||
msgid "Keep a cancellation fee of"
|
||||
@@ -9556,10 +9514,8 @@ msgstr ""
|
||||
"betalen. Voer een bruto bedrag in, belasting zal automatisch worden berekend."
|
||||
|
||||
#: pretix/control/forms/orders.py:121
|
||||
#, fuzzy
|
||||
#| msgid "Generate cancellation"
|
||||
msgid "Generate cancellation for invoice"
|
||||
msgstr "Genereer annulering"
|
||||
msgstr "Genereer annulering voor factuur"
|
||||
|
||||
#: pretix/control/forms/orders.py:158
|
||||
msgid "Payment amount"
|
||||
@@ -9934,21 +9890,17 @@ msgstr "Optioneel"
|
||||
#: pretix/control/templates/pretixcontrol/subevents/bulk_edit.html:48
|
||||
#: pretix/control/templates/pretixcontrol/subevents/bulk_edit.html:181
|
||||
#: pretix/control/templates/pretixcontrol/subevents/bulk_edit.html:266
|
||||
#, fuzzy
|
||||
#| msgid "Change"
|
||||
msgctxt "form_bulk"
|
||||
msgid "change"
|
||||
msgstr "Veranderen"
|
||||
msgstr "veranderen"
|
||||
|
||||
#: pretix/control/forms/subevents.py:96
|
||||
#, fuzzy
|
||||
#| msgid "Current value"
|
||||
msgid "Keep the current values"
|
||||
msgstr "Huidige waarde"
|
||||
msgstr "Houd huidige waarden"
|
||||
|
||||
#: pretix/control/forms/subevents.py:113 pretix/control/forms/subevents.py:121
|
||||
msgid "Selection contains various values"
|
||||
msgstr ""
|
||||
msgstr "Selectie bevat verschillende waarden"
|
||||
|
||||
#: pretix/control/forms/subevents.py:369
|
||||
msgid "Exclude these dates instead of adding them."
|
||||
@@ -10359,40 +10311,28 @@ msgid "This object has been created by cloning."
|
||||
msgstr "Dit object is aangemaakt via kopiëren."
|
||||
|
||||
#: pretix/control/logdisplay.py:276
|
||||
#, fuzzy
|
||||
#| msgid "The order has been changed."
|
||||
msgid "The organizer has been changed."
|
||||
msgstr "De bestelling is aangepast."
|
||||
msgstr "De organisator is veranderd."
|
||||
|
||||
#: pretix/control/logdisplay.py:277
|
||||
#, fuzzy
|
||||
#| msgid "The team settings have been changed."
|
||||
msgid "The organizer settings have been changed."
|
||||
msgstr "De teaminstellingen zijn aangepast."
|
||||
msgstr "De instellingen van de organisator zijn aangepast."
|
||||
|
||||
#: pretix/control/logdisplay.py:278
|
||||
#, fuzzy
|
||||
#| msgid "The new organizer has been created."
|
||||
msgid "Gift card acceptance for another organizer has been added."
|
||||
msgstr "De nieuwe organisator is aangemaakt."
|
||||
msgstr "Cadeaubonacceptatie voor een andere organisator is toegevoegd."
|
||||
|
||||
#: pretix/control/logdisplay.py:279
|
||||
#, fuzzy
|
||||
#| msgid "The new organizer has been created."
|
||||
msgid "Gift card acceptance for another organizer has been removed."
|
||||
msgstr "De nieuwe organisator is aangemaakt."
|
||||
msgstr "Cadeaubonacceptatie voor een andere organisator is verwijderd."
|
||||
|
||||
#: pretix/control/logdisplay.py:280
|
||||
#, fuzzy
|
||||
#| msgid "The user has been created."
|
||||
msgid "The webhook has been created."
|
||||
msgstr "De gebruiker is aangemaakt."
|
||||
msgstr "De webhook is aangemaakt."
|
||||
|
||||
#: pretix/control/logdisplay.py:281
|
||||
#, fuzzy
|
||||
#| msgid "The gate has been changed."
|
||||
msgid "The webhook has been changed."
|
||||
msgstr "De toegangslocatie is aangepast."
|
||||
msgstr "De webhook is aangepast."
|
||||
|
||||
#: pretix/control/logdisplay.py:282
|
||||
msgid "The event's internal comment has been updated."
|
||||
@@ -10403,10 +10343,8 @@ msgid "The event has been canceled."
|
||||
msgstr "Het evenement is geannuleerd."
|
||||
|
||||
#: pretix/control/logdisplay.py:284
|
||||
#, fuzzy
|
||||
#| msgid "The event has been deleted."
|
||||
msgid "An event has been deleted."
|
||||
msgstr "Dit evenement is verwijderd."
|
||||
msgstr "Een webhook is verwijderd."
|
||||
|
||||
#: pretix/control/logdisplay.py:285
|
||||
msgid "The order details have been changed."
|
||||
@@ -12437,25 +12375,19 @@ msgstr "Geocoding-data © OpenStreetMap"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/event/fragment_geodata_autoupdate.html:4
|
||||
msgid "Failed to retrieve geo coordinates"
|
||||
msgstr ""
|
||||
msgstr "Geo-coördinaten ophalen mislukt"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/event/fragment_geodata_autoupdate.html:5
|
||||
#, fuzzy
|
||||
#| msgid "Geo coordinates"
|
||||
msgid "Retrieving geo coordinates …"
|
||||
msgstr "Geo-coördinaten"
|
||||
msgstr "Geo-coördinaten ophalen…"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/event/fragment_geodata_autoupdate.html:6
|
||||
#, fuzzy
|
||||
#| msgid "Geo coordinates"
|
||||
msgid "Geo coordinates updated"
|
||||
msgstr "Geo-coördinaten"
|
||||
msgstr "Geo-coördinaten bijgewerkt"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/event/fragment_geodata_autoupdate.html:7
|
||||
#, fuzzy
|
||||
#| msgid "Update comment"
|
||||
msgid "Update map?"
|
||||
msgstr "Commentaar bijwerken"
|
||||
msgstr "Kaart bijwerken?"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/event/fragment_timeline.html:5
|
||||
msgid "Your timeline"
|
||||
@@ -15564,7 +15496,7 @@ msgstr "VOLLEDIG BETAALD"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/orders/index.html:175
|
||||
msgid "INVOICE NOT CANCELED"
|
||||
msgstr ""
|
||||
msgstr "FACTUUR NIET GEANNULEERD"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/orders/index.html:186
|
||||
msgid "Sum over all pages"
|
||||
@@ -15950,10 +15882,8 @@ msgstr ""
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/organizers/logs.html:4
|
||||
#: pretix/control/templates/pretixcontrol/organizers/logs.html:6
|
||||
#, fuzzy
|
||||
#| msgid "Organizers"
|
||||
msgid "Organizer logs"
|
||||
msgstr "Organisatoren"
|
||||
msgstr "Organisatorlogs"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/organizers/properties.html:7
|
||||
msgid ""
|
||||
@@ -15967,34 +15897,24 @@ msgstr ""
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/organizers/properties.html:15
|
||||
#: pretix/control/templates/pretixcontrol/organizers/property_edit.html:8
|
||||
#, fuzzy
|
||||
#| msgid "Create a new product"
|
||||
msgid "Create a new property"
|
||||
msgstr "Nieuw product aanmaken"
|
||||
msgstr "Nieuwe eigenschap aanmaken"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/organizers/properties.html:20
|
||||
#, fuzzy
|
||||
#| msgid "Add property"
|
||||
msgid "Property"
|
||||
msgstr "Eigenschap toevoegen"
|
||||
msgstr "Eigenschap"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/organizers/property_delete.html:5
|
||||
#, fuzzy
|
||||
#| msgid "Delete product"
|
||||
msgid "Delete property:"
|
||||
msgstr "Product verwijderen"
|
||||
msgstr "Verwijder eigenschap:"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/organizers/property_delete.html:8
|
||||
#, fuzzy
|
||||
#| msgid "Are you sure you want to delete the gate?"
|
||||
msgid "Are you sure you want to delete the property?"
|
||||
msgstr "Weet u zeker dat u de toegangslocatie wilt verwijderen?"
|
||||
msgstr "Weet u zeker dat u de eigenschap wilt verwijderen?"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/organizers/property_edit.html:6
|
||||
#, fuzzy
|
||||
#| msgid "Add property"
|
||||
msgid "Property:"
|
||||
msgstr "Eigenschap toevoegen"
|
||||
msgstr "Eigenschap:"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/organizers/team_delete.html:5
|
||||
msgid "Delete team:"
|
||||
@@ -16374,10 +16294,8 @@ msgid "Light"
|
||||
msgstr "Licht"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/pdf/index.html:349
|
||||
#, fuzzy
|
||||
#| msgid "E-mail content"
|
||||
msgid "Image content"
|
||||
msgstr "E-mailinhoud"
|
||||
msgstr "Afbeeldingsinhoud"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/pdf/index.html:360
|
||||
msgid "Text content"
|
||||
@@ -16413,7 +16331,7 @@ msgstr "pretix-logo"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/pdf/index.html:410
|
||||
msgid "Dynamic image"
|
||||
msgstr ""
|
||||
msgstr "Dynamische afbeelding"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/search/orders.html:104
|
||||
msgid ""
|
||||
@@ -16666,18 +16584,14 @@ msgstr "Voeg een nieuwe inchecklijst toe"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/bulk_edit.html:8
|
||||
#: pretix/control/templates/pretixcontrol/subevents/bulk_edit.html:11
|
||||
#, fuzzy
|
||||
#| msgctxt "subevent"
|
||||
#| msgid "Create multiple dates"
|
||||
msgctxt "subevent"
|
||||
msgid "Change multiple dates"
|
||||
msgstr "Maak meerdere datums aan"
|
||||
msgstr "Meerdere datums veranderen"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/bulk_edit.html:13
|
||||
#, fuzzy, python-format
|
||||
#| msgid "Enable selected"
|
||||
#, python-format
|
||||
msgid "%(number)s selected"
|
||||
msgstr "Schakel geselecteerde in"
|
||||
msgstr "%(number)s geselecteerd"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/bulk_edit.html:171
|
||||
msgid ""
|
||||
@@ -16686,12 +16600,18 @@ msgid ""
|
||||
"new set of quotas to <strong>replace</strong> the quota setup of all "
|
||||
"selected dates."
|
||||
msgstr ""
|
||||
"De quotuminstellingen van de gekozen datums verschillen en kunnen hierom "
|
||||
"niet in één keer aangepast worden. Als u dit wilt kunt u wel nieuwe quota "
|
||||
"definiëren om de quotuminstellingen van de geselecteerde datums "
|
||||
"<strong>vervangen</strong>."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/bulk_edit.html:257
|
||||
msgid ""
|
||||
"You selected a set of dates that currently have different check-in list "
|
||||
"setups. You can therefore not change their check-in lists in bulk."
|
||||
msgstr ""
|
||||
"De inchecklijstinstellingen van de gekozen datums verschillen en kunnen "
|
||||
"hierom niet in één keer aangepast worden."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/delete.html:4
|
||||
#: pretix/control/templates/pretixcontrol/subevents/delete.html:6
|
||||
@@ -16751,7 +16671,7 @@ msgstr "Maak meerdere nieuwe datums"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/index.html:70
|
||||
msgid "select all rows for batch-operation"
|
||||
msgstr ""
|
||||
msgstr "Selecteer alle rijen voor batch-handeling"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/index.html:77
|
||||
msgid "Begin"
|
||||
@@ -16759,11 +16679,11 @@ msgstr "Begin"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/index.html:100
|
||||
msgid "Select all results on other pages as well"
|
||||
msgstr ""
|
||||
msgstr "Selecteer ook alle resultaten op andere pagina's"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/index.html:111
|
||||
msgid "select row for batch-operation"
|
||||
msgstr ""
|
||||
msgstr "Selecteer rij voor batch-handeling"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/index.html:152
|
||||
msgctxt "subevent"
|
||||
@@ -16781,23 +16701,16 @@ msgid "Delete selected"
|
||||
msgstr "Verwijder geselecteerde"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/index.html:176
|
||||
#, fuzzy
|
||||
#| msgctxt "subevent"
|
||||
#| msgid "No date selected."
|
||||
msgid "Edit selected"
|
||||
msgstr "Geen datum geselecteerd."
|
||||
msgstr "Bewerk geselecteerde"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/index.html:179
|
||||
#, fuzzy
|
||||
#| msgid "Disable selected"
|
||||
msgid "Activate selected"
|
||||
msgstr "Schakel geselecteerde uit"
|
||||
msgstr "Activeer geselecteerde"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/index.html:182
|
||||
#, fuzzy
|
||||
#| msgid "Delete selected"
|
||||
msgid "Deactivate selected"
|
||||
msgstr "Verwijder geselecteerde"
|
||||
msgstr "Deactiveer geselecteerde"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_add.html:4
|
||||
#: pretix/control/templates/pretixcontrol/user/2fa_add.html:6
|
||||
@@ -18695,16 +18608,12 @@ msgid "The selected gate has been deleted."
|
||||
msgstr "De geselecteerde toegangslocatie is verwijderd."
|
||||
|
||||
#: pretix/control/views/organizer.py:1375
|
||||
#, fuzzy
|
||||
#| msgid "The product has been created."
|
||||
msgid "The property has been created."
|
||||
msgstr "Het product is aangemaakt."
|
||||
msgstr "De eigenschap is aangemaakt."
|
||||
|
||||
#: pretix/control/views/organizer.py:1437
|
||||
#, fuzzy
|
||||
#| msgid "The selected product has been deleted."
|
||||
msgid "The selected property has been deleted."
|
||||
msgstr "Het gekozen product is verwijderd."
|
||||
msgstr "De eigenschap is verwijderd."
|
||||
|
||||
#: pretix/control/views/pdf.py:53
|
||||
msgid "The uploaded PDF file is too large."
|
||||
@@ -20457,19 +20366,14 @@ msgid "Only send to customers of dates starting before"
|
||||
msgstr "Stuur alleen naar klanten van subevenementen die beginnen voor"
|
||||
|
||||
#: pretix/plugins/sendmail/forms.py:70
|
||||
#, fuzzy
|
||||
#| msgid "Send to customers with order status"
|
||||
msgctxt "subevent"
|
||||
msgid "Only send to customers with orders created after"
|
||||
msgstr "Stuur naar klanten met bestelstatus"
|
||||
msgstr "Stuur alleen naar klanten met bestellingen geplaatst na"
|
||||
|
||||
#: pretix/plugins/sendmail/forms.py:75
|
||||
#, fuzzy
|
||||
#| msgctxt "subevent"
|
||||
#| msgid "Only send to customers of dates starting before"
|
||||
msgctxt "subevent"
|
||||
msgid "Only send to customers with orders created before"
|
||||
msgstr "Stuur alleen naar klanten van subevenementen die beginnen voor"
|
||||
msgstr "Stuur alleen naar klanten met bestellingen geplaatst voor"
|
||||
|
||||
#: pretix/plugins/sendmail/forms.py:108
|
||||
msgid "Everyone who created a ticket order"
|
||||
@@ -20553,16 +20457,13 @@ msgid "There are no orders matching this selection."
|
||||
msgstr "Er zijn geen bestellingen die overeenkomen met deze selectie."
|
||||
|
||||
#: pretix/plugins/sendmail/views.py:180
|
||||
#, fuzzy, python-format
|
||||
#| msgid ""
|
||||
#| "Your message has been queued and will be sent to the contact addresses of "
|
||||
#| "%d orders in the next minutes."
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Your message has been queued and will be sent to the contact addresses of %d "
|
||||
"orders in the next few minutes."
|
||||
msgstr ""
|
||||
"Uw bericht is in de wachtrij gezet, en zal in de komende minuten naar de e-"
|
||||
"mailadressen van %d bestellingen worden verstuurd."
|
||||
"Uw bericht is in de wachtrij gezet en zal binnenkort naar de e-mailadressen "
|
||||
"van %d bestellingen worden verstuurd."
|
||||
|
||||
#: pretix/plugins/statistics/__init__.py:9
|
||||
#: pretix/plugins/statistics/__init__.py:12
|
||||
@@ -21553,12 +21454,9 @@ msgid "No other variations of this product exist."
|
||||
msgstr "Er bestaan geen andere varianten van dit product."
|
||||
|
||||
#: pretix/presale/forms/renderers.py:32
|
||||
#, fuzzy
|
||||
#| msgctxt "attendee_data"
|
||||
#| msgid "Required"
|
||||
msgctxt "form"
|
||||
msgid "required"
|
||||
msgstr "Verplicht"
|
||||
msgstr "verplicht"
|
||||
|
||||
#: pretix/presale/ical.py:54
|
||||
#, python-brace-format
|
||||
@@ -21576,10 +21474,8 @@ msgid "Organizer: {organizer}"
|
||||
msgstr "Organisator: {organizer}"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/base.html:59
|
||||
#, fuzzy
|
||||
#| msgid "Toggle navigation"
|
||||
msgid "Footer Navigation"
|
||||
msgstr "Navigatie schakelen"
|
||||
msgstr "Footer-navigatie"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/base_footer.html:36
|
||||
#, python-format
|
||||
@@ -21596,10 +21492,8 @@ msgstr "Zet uw winkel nu live"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/base.html:40
|
||||
#: pretix/presale/templates/pretixpresale/event/base.html:86
|
||||
#, fuzzy
|
||||
#| msgid "Use languages"
|
||||
msgid "select language"
|
||||
msgstr "Gebruik talen"
|
||||
msgstr "taal kiezen"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/base.html:52
|
||||
#, python-format
|
||||
@@ -21758,7 +21652,7 @@ msgstr "Ga terug"
|
||||
#: pretix/presale/templates/pretixpresale/event/checkout_base.html:7
|
||||
#, python-format
|
||||
msgid "Step %(current)s of %(total)s: %(label)s"
|
||||
msgstr ""
|
||||
msgstr "Stap %(current)s van %(total)s: %(label)s"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/checkout_base.html:12
|
||||
#: pretix/presale/templates/pretixpresale/event/checkout_base.html:57
|
||||
@@ -22030,16 +21924,12 @@ msgid "Redeem voucher"
|
||||
msgstr "Voucher inwisselen"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_checkoutflow.html:13
|
||||
#, fuzzy
|
||||
#| msgid "Completion date"
|
||||
msgid "Completed:"
|
||||
msgstr "Voltooiingsdatum"
|
||||
msgstr "Voltooid:"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_checkoutflow.html:15
|
||||
#, fuzzy
|
||||
#| msgid "Currency"
|
||||
msgid "Current:"
|
||||
msgstr "Munteenheid"
|
||||
msgstr "Huidig:"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_checkoutflow.html:29
|
||||
msgctxt "checkoutflow"
|
||||
@@ -22053,39 +21943,27 @@ msgstr ""
|
||||
"tickets te ontvangen."
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_downloads.html:16
|
||||
#, fuzzy
|
||||
#| msgid "Please check your email account, we've sent you your tickets."
|
||||
msgid "Please check your email account, we've sent you an email."
|
||||
msgstr ""
|
||||
"We hebben uw tickets per e-mail naar u verzonden. Kijk in uw inbox om uw "
|
||||
"tickets te ontvangen."
|
||||
msgstr "We hebben een e-mail naar u verzonden."
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_downloads.html:21
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "You can also download them right here as soon as the person who placed "
|
||||
#| "the order clicked the link in the email they received to confirm the "
|
||||
#| "email address is valid."
|
||||
msgid ""
|
||||
"You can download your tickets right here as soon as the person who placed "
|
||||
"the order clicked the link in the email they received to confirm the email "
|
||||
"address is valid."
|
||||
msgstr ""
|
||||
"U kunt uw tickets ook op deze pagina downloaden zodra het opgegeven e-"
|
||||
"mailadres is bevestigd. Dit kan de persoon die de bestelling heeft geplaatst "
|
||||
"doen door te klikken op de link in de toegezonden email."
|
||||
"De persoon die de bestelling heeft geplaatst heeft een e-mail ontvangen met "
|
||||
"een link om het opgegeven e-mailadres te controleren. U kunt uw tickets op "
|
||||
"deze pagina downloaden zodra de persoon die de bestelling heeft geplaatst op "
|
||||
"deze link heeft geklikt."
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_downloads.html:26
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "If the email has no attachment, click the link in our email and you will "
|
||||
#| "be able to download them from here."
|
||||
msgid ""
|
||||
"If you click the link in our email, you will be able to download your "
|
||||
"tickets here."
|
||||
msgstr ""
|
||||
"Als de email geen bijlage heeft kunt u op de link in de mail klikken om de "
|
||||
"tickets te downloaden."
|
||||
"U kunt uw tickets op deze pagina downloaden zodra u op de link in onze e-"
|
||||
"mail klikt."
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_downloads.html:30
|
||||
msgid ""
|
||||
@@ -22139,17 +22017,14 @@ msgid "Confirmed"
|
||||
msgstr "Bevestigd"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:9
|
||||
#, fuzzy
|
||||
#| msgid "Uncategorized"
|
||||
msgid "Uncategorized products"
|
||||
msgstr "Ongecategoriseerd"
|
||||
msgstr "Ongecategoriseerde producten"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:28
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:187
|
||||
#, fuzzy, python-format
|
||||
#| msgid "Show all events of %(name)s"
|
||||
#, python-format
|
||||
msgid "Show full-size image of %(item)s"
|
||||
msgstr "Toon alle evenementen van %(name)s"
|
||||
msgstr "Toon volledige afbeelding van %(item)s"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:63
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_product_list.html:120
|
||||
@@ -22217,10 +22092,12 @@ msgstr "Nog niet in de verkoop"
|
||||
#: pretix/presale/templates/pretixpresale/event/index.html:27
|
||||
msgid "Your cart, general information, add products to your cart"
|
||||
msgstr ""
|
||||
"Uw winkelwagen, algemene informatie, nieuwe producten aan winkelwagen "
|
||||
"toevoegen"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/index.html:27
|
||||
msgid "General information, add products to your cart"
|
||||
msgstr ""
|
||||
msgstr "Algemene informatie, nieuwe producten aan winkelwagen toevoegen"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/index.html:65
|
||||
#: pretix/presale/templates/pretixpresale/event/index.html:82
|
||||
|
||||
@@ -63,7 +63,7 @@ def eventurl(parser, token, absolute=False):
|
||||
asvar = bits[-1]
|
||||
bits = bits[:-2]
|
||||
|
||||
if len(bits):
|
||||
if bits:
|
||||
for bit in bits:
|
||||
match = kwarg_re.match(bit)
|
||||
if not match:
|
||||
|
||||
@@ -159,7 +159,7 @@ class CheckInListMixin(BaseExporter):
|
||||
), self.event.timezone)
|
||||
qs = qs.filter(subevent__date_from__lt=dt)
|
||||
|
||||
o = tuple()
|
||||
o = ()
|
||||
if self.event.has_subevents and not cl.subevent:
|
||||
o = ('subevent__date_from', 'subevent__name')
|
||||
|
||||
|
||||
@@ -616,7 +616,7 @@ class OrderTaxListReport(MultiSheetListExporter):
|
||||
elif sheet == 'companies':
|
||||
yield from self.iterate_companies(form_data)
|
||||
|
||||
def _combine(self, *qs, keys=tuple()):
|
||||
def _combine(self, *qs, keys=()):
|
||||
cache = {}
|
||||
|
||||
def kf(r):
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<h1>{% trans "Statistics" %}</h1>
|
||||
{% if request.event.has_subevents %}
|
||||
<form class="form-inline helper-display-inline" action="" method="get">
|
||||
{% include "pretixcontrol/event/fragment_subevent_choice_simple.html" %}
|
||||
{% include "pretixcontrol/event/fragment_subevent_choice_simple.html" with auto_submit=True %}
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if has_orders %}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
from bootstrap3.renderers import FieldRenderer
|
||||
from bootstrap3.text import text_value
|
||||
from bootstrap3.utils import add_css_class
|
||||
from django.forms import CheckboxInput
|
||||
from django.forms import CheckboxInput, CheckboxSelectMultiple, RadioSelect
|
||||
from django.forms.utils import flatatt
|
||||
from django.utils.html import escape, format_html, strip_tags
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import pgettext
|
||||
|
||||
|
||||
def render_label(content, label_for=None, label_class=None, label_title='', optional=False):
|
||||
def render_label(content, label_for=None, label_class=None, label_title='', label_id='', optional=False, is_valid=None):
|
||||
"""
|
||||
Render a label with content
|
||||
"""
|
||||
@@ -19,6 +19,17 @@ def render_label(content, label_for=None, label_class=None, label_title='', opti
|
||||
attrs['class'] = label_class
|
||||
if label_title:
|
||||
attrs['title'] = label_title
|
||||
if label_id:
|
||||
attrs['id'] = label_id
|
||||
|
||||
opt = ""
|
||||
|
||||
if is_valid is not None:
|
||||
if is_valid:
|
||||
validation_text = pgettext('form', 'is valid')
|
||||
else:
|
||||
validation_text = pgettext('form', 'has errors')
|
||||
opt += '<strong class="sr-only"> {}</strong>'.format(validation_text)
|
||||
|
||||
if text_value(content) == ' ':
|
||||
# Empty label, e.g. checkbox
|
||||
@@ -26,17 +37,17 @@ def render_label(content, label_for=None, label_class=None, label_title='', opti
|
||||
attrs['class'] += ' label-empty'
|
||||
# usually checkboxes have overall empty labels and special labels per checkbox
|
||||
# => remove for-attribute as well as "required"-text appended to label
|
||||
del(attrs['for'])
|
||||
opt = ""
|
||||
if 'for' in attrs:
|
||||
del(attrs['for'])
|
||||
else:
|
||||
opt = mark_safe('<i class="sr-only"> {}</i>'.format(pgettext('form', 'required'))) if not optional else ''
|
||||
opt += '<i class="sr-only label-required">, {}</i>'.format(pgettext('form', 'required')) if not optional else ''
|
||||
|
||||
builder = '<{tag}{attrs}>{content}{opt}</{tag}>'
|
||||
return format_html(
|
||||
builder,
|
||||
tag='label',
|
||||
attrs=mark_safe(flatatt(attrs)) if attrs else '',
|
||||
opt=opt,
|
||||
opt=mark_safe(opt),
|
||||
content=text_value(content),
|
||||
)
|
||||
|
||||
@@ -45,6 +56,7 @@ class CheckoutFieldRenderer(FieldRenderer):
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['layout'] = 'horizontal'
|
||||
super().__init__(*args, **kwargs)
|
||||
self.is_group_widget = isinstance(self.widget, (CheckboxSelectMultiple, RadioSelect, )) or (self.is_multi_widget and len(self.widget.widgets) > 1)
|
||||
|
||||
def get_form_group_class(self):
|
||||
form_group_class = self.form_group_class
|
||||
@@ -64,6 +76,26 @@ class CheckoutFieldRenderer(FieldRenderer):
|
||||
)
|
||||
return form_group_class
|
||||
|
||||
def append_to_field(self, html):
|
||||
help_text_and_errors = []
|
||||
help_text_and_errors += self.field_errors
|
||||
if self.field_help:
|
||||
help_text_and_errors.append(self.field_help)
|
||||
for idx, text in enumerate(help_text_and_errors):
|
||||
html += '<div class="help-block" id="help-for-{id}-{idx}">{text}</div>'.format(id=self.field.id_for_label, text=text, idx=idx)
|
||||
return html
|
||||
|
||||
def add_help_attrs(self, widget=None):
|
||||
super().add_help_attrs(widget)
|
||||
if widget is None:
|
||||
widget = self.widget
|
||||
help_cnt = len(self.field_errors)
|
||||
if self.field_help:
|
||||
help_cnt += 1
|
||||
if help_cnt > 0:
|
||||
help_ids = ["help-for-{id}-{idx}".format(id=self.field.id_for_label, idx=idx) for idx in range(help_cnt)]
|
||||
widget.attrs["aria-describedby"] = " ".join(help_ids)
|
||||
|
||||
def add_label(self, html):
|
||||
label = self.get_label()
|
||||
|
||||
@@ -73,11 +105,25 @@ class CheckoutFieldRenderer(FieldRenderer):
|
||||
else:
|
||||
required = self.field.field.required
|
||||
|
||||
if self.field.form.is_bound:
|
||||
is_valid = len(self.field.errors) == 0
|
||||
else:
|
||||
is_valid = None
|
||||
|
||||
if self.is_group_widget:
|
||||
label_for = ""
|
||||
label_id = "legend-{}".format(self.field.html_name)
|
||||
else:
|
||||
label_for = self.field.id_for_label
|
||||
label_id = ""
|
||||
|
||||
html = render_label(
|
||||
label,
|
||||
label_for=self.field.id_for_label,
|
||||
label_for=label_for,
|
||||
label_class=self.get_label_class(),
|
||||
optional=not required and not isinstance(self.widget, CheckboxInput)
|
||||
label_id=label_id,
|
||||
optional=not required and not isinstance(self.widget, CheckboxInput),
|
||||
is_valid=is_valid
|
||||
) + html
|
||||
return html
|
||||
|
||||
@@ -88,3 +134,10 @@ class CheckoutFieldRenderer(FieldRenderer):
|
||||
label_for=self.field.id_for_label,
|
||||
label_title=escape(strip_tags(self.field_help)),
|
||||
)
|
||||
|
||||
def wrap_label_and_field(self, html):
|
||||
if self.is_group_widget:
|
||||
attrs = ' role="group" aria-labelledby="legend-{}"'.format(self.field.html_name)
|
||||
else:
|
||||
attrs = ''
|
||||
return '<div class="{klass}"{attrs}>{html}</div>'.format(klass=self.get_form_group_class(), html=html, attrs=attrs)
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
<div class="panel-heading">
|
||||
{% if payment_provider.identifier != "free" %}
|
||||
<div class="pull-right flip">
|
||||
<a href="{% eventurl request.event "presale:event.checkout" step="payment" cart_namespace=cart_namespace|default_if_none:"" %}">
|
||||
<a href="{% eventurl request.event "presale:event.checkout" step="payment" cart_namespace=cart_namespace|default_if_none:"" %}" aria-label="{% trans "Modify payment" %}">
|
||||
<span class="fa fa-edit" aria-hidden="true"></span>
|
||||
{% trans "Modify" %}
|
||||
</a>
|
||||
@@ -79,7 +79,7 @@
|
||||
<div class="panel panel-primary panel-contact">
|
||||
<div class="panel-heading">
|
||||
<div class="pull-right flip">
|
||||
<a href="{% eventurl request.event "presale:event.checkout" step="questions" cart_namespace=cart_namespace|default_if_none:"" %}?invoice=1">
|
||||
<a href="{% eventurl request.event "presale:event.checkout" step="questions" cart_namespace=cart_namespace|default_if_none:"" %}?invoice=1" aria-label="{% trans "Modify invoice information" %}">
|
||||
<span class="fa fa-edit" aria-hidden="true"></span>
|
||||
{% trans "Modify" %}
|
||||
</a>
|
||||
@@ -129,7 +129,7 @@
|
||||
<div class="panel panel-primary panel-contact">
|
||||
<div class="panel-heading">
|
||||
<div class="pull-right flip">
|
||||
<a href="{% eventurl request.event "presale:event.checkout" step="questions" cart_namespace=cart_namespace|default_if_none:"" %}">
|
||||
<a href="{% eventurl request.event "presale:event.checkout" step="questions" cart_namespace=cart_namespace|default_if_none:"" %}" aria-label="{% trans "Modify contact information" %}">
|
||||
<span class="fa fa-edit" aria-hidden="true"></span>
|
||||
{% trans "Modify" %}
|
||||
</a>
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
</summary>
|
||||
<div id="contact">
|
||||
<div class="panel-body">
|
||||
{% bootstrap_form contact_form layout="horizontal" %}
|
||||
{% bootstrap_form contact_form layout="checkout" %}
|
||||
{% if not invoice_address_asked and event.settings.invoice_name_required %}
|
||||
{% bootstrap_form invoice_form layout="horizontal" %}
|
||||
{% bootstrap_form invoice_form layout="checkout" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -46,7 +46,7 @@
|
||||
{{ event.settings.invoice_address_explanation_text|rich_text }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% bootstrap_form invoice_form layout="horizontal" %}
|
||||
{% bootstrap_form invoice_form layout="checkout" %}
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
@@ -51,49 +51,47 @@
|
||||
</dt>
|
||||
<dd class="toplevel">
|
||||
<span data-toggle="tooltip" title="{% trans "Attendee name" %}">
|
||||
{% if line.attendee_name %}{{ line.attendee_name }}{% else %}<em>{% trans "not answered" %}</em>{% endif %}
|
||||
{% if line.attendee_name %}{{ line.attendee_name }}{% else %}<em>{% trans "No attendee name provided" %}</em>{% endif %}
|
||||
</span>
|
||||
</dd>
|
||||
{% endif %}
|
||||
{% if line.item.admission and event.settings.attendee_emails_asked %}
|
||||
{% if line.item.admission and event.settings.attendee_emails_asked and line.attendee_email %}
|
||||
<dt class="sr-only">
|
||||
{% trans "Attendee email" %}
|
||||
</dt>
|
||||
<dd class="toplevel">
|
||||
<span data-toggle="tooltip" title="{% trans "Attendee email" %}">
|
||||
{% if line.attendee_email %}{{ line.attendee_email }}{% else %}<em>{% trans "not answered" %}</em>{% endif %}
|
||||
{{ line.attendee_email }}
|
||||
</span>
|
||||
</dd>
|
||||
{% endif %}
|
||||
{% if line.item.admission and event.settings.attendee_addresses_asked %}
|
||||
<br>
|
||||
{% endif %}
|
||||
{% if line.item.admission and event.settings.attendee_company_asked %}
|
||||
{% if line.item.admission and event.settings.attendee_company_asked and line.company %}
|
||||
<dt class="sr-only">
|
||||
{% trans "Attendee company" %}
|
||||
</dt>
|
||||
<dd class="toplevel">
|
||||
<span data-toggle="tooltip" title="{% trans "Attendee company" %}">
|
||||
{% if line.company %}{{ line.company }}{% else %}<em>{% trans "not answered" %}</em>{% endif %}
|
||||
{{ line.company }}
|
||||
</span>
|
||||
</dd>
|
||||
{% endif %}
|
||||
{% if line.item.admission and event.settings.attendee_addresses_asked %}
|
||||
<dt class="sr-only">
|
||||
{% trans "Attendee address" %}
|
||||
</dt>
|
||||
<dd class="toplevel">
|
||||
<span data-toggle="tooltip" title="{% trans "Attendee address" %}">
|
||||
{% if line.street or line.zipcode or line.city or line.country %}
|
||||
{% if line.street or line.zipcode or line.city %}
|
||||
<dt class="sr-only">
|
||||
{% trans "Attendee address" %}
|
||||
</dt>
|
||||
<dd class="toplevel">
|
||||
<span data-toggle="tooltip" title="{% trans "Attendee address" %}">
|
||||
{{ line.street|default_if_none:""|linebreaksbr }}<br>
|
||||
{{ line.zipcode|default_if_none:"" }} {{ line.city|default_if_none:"" }}<br>
|
||||
{{ line.country.name|default_if_none:"" }}
|
||||
{% if line.state %}<br>{{ line.state }}{% endif %}
|
||||
{% else %}
|
||||
<em>{% trans "not answered" %}</em>
|
||||
{% endif %}
|
||||
</span>
|
||||
</dd>
|
||||
</span>
|
||||
</dd>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% for q in line.questions %}
|
||||
<dt>{{ q.question }}</dt>
|
||||
@@ -140,6 +138,7 @@
|
||||
{% elif line.addon_to %}
|
||||
<div class="count"> </div>
|
||||
<div class="singleprice price">
|
||||
<span class="sr-only">{% trans "price per item" %}</span>
|
||||
{% if event.settings.display_net_prices %}
|
||||
{{ line.net_price|money:event.currency }}
|
||||
{% else %}
|
||||
@@ -163,6 +162,7 @@
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
<span class="sr-only">{% trans "quantity" %}</span>
|
||||
{{ line.count }}
|
||||
{% if editable %}
|
||||
<form action="{% eventurl event "presale:event.cart.add" cart_namespace=cart_namespace|default_if_none:"" %}"
|
||||
@@ -189,6 +189,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="singleprice price">
|
||||
<span class="sr-only">{% trans "price per item" %}</span>
|
||||
{% if event.settings.display_net_prices %}
|
||||
{{ line.net_price|money:event.currency }}
|
||||
{% else %}
|
||||
@@ -197,6 +198,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="totalprice price">
|
||||
<span class="sr-only">{% trans "price" %}</span>
|
||||
{% if event.settings.display_net_prices %}
|
||||
<strong>{{ line.net_total|money:event.currency }}</strong>
|
||||
{% if line.tax_rate and line.total %}
|
||||
@@ -306,11 +308,12 @@
|
||||
data-asynctask-headline="{% trans "We're applying this voucher to your cart..." %}"
|
||||
method="post" data-asynctask class="apply-voucher">
|
||||
{% csrf_token %}
|
||||
<label for="voucher_code" class="sr-only">{% trans "Voucher code" %}</label>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" name="voucher" placeholder="{% trans "Voucher code" %}" aria-label="{% trans "Voucher code" %}">
|
||||
<input type="text" class="form-control" name="voucher" id="voucher_code" placeholder="{% trans "Voucher code" %}" aria-label="{% trans "Voucher code" %}">
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary" type="submit" aria-label="{% trans "Redeem voucher" %}">
|
||||
<span class="fa fa-check" aria-hidden="true"></span>
|
||||
<button class="btn btn-primary" type="submit">
|
||||
<span class="fa fa-check" aria-hidden="true"></span><span class="sr-only"> {% trans "Redeem voucher" %}</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -6,12 +6,14 @@
|
||||
{% load eventsignal %}
|
||||
{% load rich_text %}
|
||||
{% for tup in items_by_category %}
|
||||
<section{% if tup.0 %} aria-labelledby="category-{{ tup.0.id }}"{% else %} aria-label="{% trans "Uncategorized products" %}"{% endif %}{% if tup.0.description %} aria-describedby="category-info-{{ tup.0.id }}"{% endif %}>
|
||||
<section aria-labelledby="category-{% if tup.0 %}{{ tup.0.id }}{% else %}none{% endif %}"{% if tup.0.description %} aria-describedby="category-info-{{ tup.0.id }}"{% endif %}>
|
||||
{% if tup.0 %}
|
||||
<h3 id="category-{{ tup.0.id }}">{{ tup.0.name }}</h3>
|
||||
{% if tup.0.description %}
|
||||
<div id="category-info-{{ tup.0.id }}">{{ tup.0.description|localize|rich_text }}</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<h3 id="category-none" class="sr-only">{% trans "Uncategorized products" %}</h3>
|
||||
{% endif %}
|
||||
{% for item in tup.1 %}
|
||||
{% if item.has_variations %}
|
||||
|
||||
@@ -105,19 +105,19 @@
|
||||
|
||||
{% if subevent and "year" not in request.GET %}
|
||||
{% if show_cart %}
|
||||
<a class="subevent-toggle btn btn-primary btn-block btn-lg">
|
||||
<a class="subevent-toggle btn btn-primary btn-block btn-lg" href="#subevent-list">
|
||||
<span class="fa fa-reply" aria-hidden="true"></span>
|
||||
{% trans "Add tickets for a different date" %}
|
||||
</a>
|
||||
{% else %}
|
||||
<a class="subevent-toggle btn btn-default btn-block">
|
||||
<a class="subevent-toggle btn btn-default btn-block" href="#subevent-list">
|
||||
{% trans "View other date" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<h3>{% trans "Choose date to book a ticket" %}</h3>
|
||||
{% endif %}
|
||||
<div class="panel panel-default subevent-list">
|
||||
<div class="panel panel-default subevent-list" id="subevent-list">
|
||||
<div class="panel-heading">
|
||||
{% if subevent %}
|
||||
{% trans "Other dates" context "subevent" %}
|
||||
@@ -298,10 +298,11 @@
|
||||
<form method="get" action="{% eventurl event "presale:event.redeem" cart_namespace=cart_namespace %}">
|
||||
<div class="row-voucher">
|
||||
<div class="col-md-8 col-sm-6 col-xs-12">
|
||||
<label for="voucher" class="sr-only">{% trans "Voucher code" %}</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><i class="fa fa-ticket fa-fw" aria-hidden="true"></i></span>
|
||||
<input type="text" class="form-control" name="voucher" id="voucher"
|
||||
placeholder="{% trans "Voucher code" %}" aria-label="{% trans "Voucher code" %}">
|
||||
placeholder="{% trans "Voucher code" %}">
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="subevent" value="{{ subevent.id|default_if_none:"" }}" />
|
||||
|
||||
@@ -186,7 +186,7 @@
|
||||
<div class="panel-heading">
|
||||
{% if order.can_modify_answers %}
|
||||
<div class="pull-right flip">
|
||||
<a href="{% eventurl event "presale:event.order.modify" secret=order.secret order=order.code %}">
|
||||
<a href="{% eventurl event "presale:event.order.modify" secret=order.secret order=order.code %}" aria-label="{% trans "Change ordered items" %}">
|
||||
<span class="fa fa-edit" aria-hidden="true"></span>
|
||||
{% trans "Change details" %}
|
||||
</a>
|
||||
@@ -280,7 +280,7 @@
|
||||
{% if invoice_address_asked or request.event.settings.invoice_name_required %}
|
||||
{% if order.can_modify_answers %}
|
||||
<div class="pull-right flip">
|
||||
<a href="{% eventurl event "presale:event.order.modify" secret=order.secret order=order.code %}">
|
||||
<a href="{% eventurl event "presale:event.order.modify" secret=order.secret order=order.code %}" aria-label="{% trans "Change your information" %}">
|
||||
<span class="fa fa-edit" aria-hidden="true"></span>
|
||||
{% trans "Change details" %}
|
||||
</a>
|
||||
|
||||
@@ -653,7 +653,7 @@ LOGGING = {
|
||||
'django.db.backends': {
|
||||
'handlers': ['file', 'console'],
|
||||
'level': 'INFO', # Do not output all the queries
|
||||
'propagate': True,
|
||||
'propagate': False,
|
||||
},
|
||||
'asyncio': {
|
||||
'handlers': ['file', 'console'],
|
||||
|
||||
@@ -208,7 +208,7 @@ $input-border-radius-small: $border-radius-small !default;
|
||||
$input-border-focus: #66afe9 !default;
|
||||
|
||||
//** Placeholder text color
|
||||
$input-color-placeholder: #999 !default;
|
||||
$input-color-placeholder: #767676 !default;
|
||||
|
||||
//** Default `.form-control` height
|
||||
$input-height-base: ($line-height-computed + ($padding-base-vertical * 2) + 2) !default;
|
||||
|
||||
@@ -9,19 +9,19 @@
|
||||
$gray-darker: lighten(#000, 13.5%);
|
||||
$gray-dark: lighten(#000, 20%);
|
||||
$gray: lighten(#000, 33.5%);
|
||||
$gray-light: lighten(#000, 60%);
|
||||
$gray-light: lighten(#000, 55%);
|
||||
$gray-lighter: lighten(#000, 93.5%);
|
||||
$gray-lightest: lighten(#000, 97.25%);
|
||||
|
||||
$font-family-sans-serif: "Open Sans", "OpenSans", "Helvetica Neue", Helvetica, Arial, sans-serif !default;
|
||||
$text-color: #222222 !default;
|
||||
$text-muted: #999999 !default;
|
||||
$text-muted: #767676 !default;
|
||||
|
||||
$brand-primary: #7f5a91 !default;
|
||||
$brand-success: #50a167 !default;
|
||||
$brand-info: #5f9cd4 !default;
|
||||
$brand-warning: #ffb419 !default;
|
||||
$brand-danger: #d36060 !default;
|
||||
$brand-danger: #c44f4f !default;
|
||||
|
||||
$btn-default-border: #CCCCCC;
|
||||
|
||||
|
||||
@@ -45,9 +45,9 @@ $(function () {
|
||||
if (data_type === 'B') {
|
||||
var colors;
|
||||
if (data[0].answer_bool) {
|
||||
colors = ['#50A167', '#D36060'];
|
||||
colors = ['#50A167', '#C44F4F'];
|
||||
} else {
|
||||
colors = ['#D36060', '#50A167'];
|
||||
colors = ['#C44F4F', '#50A167'];
|
||||
}
|
||||
new Morris.Donut({
|
||||
element: 'question_chart',
|
||||
@@ -65,7 +65,7 @@ $(function () {
|
||||
'#50A167',
|
||||
'#FFB419',
|
||||
'#5F9CD4',
|
||||
'#D36060',
|
||||
'#C44F4F',
|
||||
'#83FFFA',
|
||||
'#FF6C38',
|
||||
'#1f5b8e',
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
.help-block {
|
||||
color: $text-muted;
|
||||
}
|
||||
|
||||
td > .form-group {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ $(function () {
|
||||
$(".apply-voucher-toggle").click(function (e) {
|
||||
$(".apply-voucher-toggle").hide();
|
||||
$(".apply-voucher").show();
|
||||
$(".apply-voucher input[ŧype=text]").first().focus();
|
||||
$(".apply-voucher input[type=text]").first().focus();
|
||||
e.preventDefault();
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -292,6 +292,7 @@ $(function () {
|
||||
|
||||
$("input[data-required-if], select[data-required-if], textarea[data-required-if]").each(function () {
|
||||
var dependent = $(this),
|
||||
dependentLabel = $("label[for="+this.id+"]"),
|
||||
dependency = $($(this).attr("data-required-if")),
|
||||
update = function (ev) {
|
||||
var enabled = (dependency.attr("type") === 'checkbox' || dependency.attr("type") === 'radio') ? dependency.prop('checked') : !!dependency.val();
|
||||
@@ -299,6 +300,12 @@ $(function () {
|
||||
dependent.prop('required', enabled);
|
||||
}
|
||||
dependent.closest('.form-group').toggleClass('required', enabled);
|
||||
if (enabled) {
|
||||
dependentLabel.append('<i class="sr-only label-required">, ' + gettext('required') + '</i>');
|
||||
}
|
||||
else {
|
||||
dependentLabel.find(".label-required").remove();
|
||||
}
|
||||
};
|
||||
update();
|
||||
dependency.closest('.form-group').find('input[name=' + dependency.attr("name") + ']').on("change", update);
|
||||
|
||||
@@ -229,7 +229,9 @@ Vue.component('availbox', {
|
||||
},
|
||||
amount_selected: {
|
||||
get: function () {
|
||||
return this.item.has_variations ? this.variation.amount_selected : this.item.amount_selected
|
||||
var selected = this.item.has_variations ? this.variation.amount_selected : this.item.amount_selected
|
||||
if (selected === 0) return undefined;
|
||||
return selected
|
||||
},
|
||||
set: function (value) {
|
||||
// Unary operator to force boolean to integer conversion, as the HTML form submission
|
||||
@@ -612,7 +614,12 @@ var shared_methods = {
|
||||
},
|
||||
resume: function () {
|
||||
var redirect_url;
|
||||
redirect_url = this.$root.target_url + 'w/' + widget_id + '/?iframe=1&locale=' + lang;
|
||||
redirect_url = this.$root.target_url + 'w/' + widget_id + '/';
|
||||
if (this.$root.subevent && !this.$root.cart_id) {
|
||||
// button with subevent but no items
|
||||
redirect_url += this.$root.subevent + '/';
|
||||
}
|
||||
redirect_url += '?iframe=1&locale=' + lang;
|
||||
if (this.$root.cart_id) {
|
||||
redirect_url += '&take_cart_id=' + this.$root.cart_id;
|
||||
}
|
||||
@@ -659,7 +666,9 @@ var shared_iframe_fragment = (
|
||||
+ ' :name="$root.parent.widget_id" src="about:blank" v-once>'
|
||||
+ 'Please enable frames in your browser!'
|
||||
+ '</iframe>'
|
||||
+ '<div class="pretix-widget-frame-close"><a href="#" @click.prevent="close">X</a></div>'
|
||||
+ '<div class="pretix-widget-frame-close"><a href="#" @click.prevent="close">'
|
||||
+ '<svg height="16px" viewBox="0 0 512 512" width="16px" xmlns="http://www.w3.org/2000/svg"><path fill="#fff" d="M437.5,386.6L306.9,256l130.6-130.6c14.1-14.1,14.1-36.8,0-50.9c-14.1-14.1-36.8-14.1-50.9,0L256,205.1L125.4,74.5 c-14.1-14.1-36.8-14.1-50.9,0c-14.1,14.1-14.1,36.8,0,50.9L205.1,256L74.5,386.6c-14.1,14.1-14.1,36.8,0,50.9 c14.1,14.1,36.8,14.1,50.9,0L256,306.9l130.6,130.6c14.1,14.1,36.8,14.1,50.9,0C451.5,423.4,451.5,400.6,437.5,386.6z"/></svg>'
|
||||
+ '</a></div>'
|
||||
+ '</div>'
|
||||
+ '</div>'
|
||||
);
|
||||
@@ -1338,7 +1347,7 @@ var shared_root_methods = {
|
||||
url += '&voucher=' + encodeURIComponent(this.$root.voucher_code);
|
||||
}
|
||||
if (cart_id) {
|
||||
url += "&cart_id=" + cart_id;
|
||||
url += "&cart_id=" + encodeURIComponent(cart_id);
|
||||
}
|
||||
if (this.$root.date !== null) {
|
||||
url += "&year=" + this.$root.date.substr(0, 4) + "&month=" + this.$root.date.substr(5, 2);
|
||||
@@ -1346,18 +1355,21 @@ var shared_root_methods = {
|
||||
url += "&year=" + this.$root.week[0] + "&week=" + this.$root.week[1];
|
||||
}
|
||||
if (this.$root.style !== null) {
|
||||
url = url + '&style=' + this.$root.style;
|
||||
url = url + '&style=' + encodeURIComponent(this.$root.style);
|
||||
}
|
||||
var root = this.$root;
|
||||
api._getJSON(url, function (data, xhr) {
|
||||
if (typeof xhr.responseURL !== "undefined" && xhr.responseURL !== url) {
|
||||
if (typeof xhr.responseURL !== "undefined") {
|
||||
var new_url = xhr.responseURL.substr(0, xhr.responseURL.indexOf("/widget/product_list?") + 1);
|
||||
if (root.subevent) {
|
||||
new_url = new_url.substr(0, new_url.lastIndexOf("/", new_url.length - 1) + 1);
|
||||
var old_url = url.substr(0, url.indexOf("/widget/product_list?") + 1);
|
||||
if (new_url !== old_url) {
|
||||
if (root.subevent) {
|
||||
new_url = new_url.substr(0, new_url.lastIndexOf("/", new_url.length - 1) + 1);
|
||||
}
|
||||
root.target_url = new_url;
|
||||
root.reload();
|
||||
return;
|
||||
}
|
||||
root.target_url = new_url;
|
||||
root.reload();
|
||||
return;
|
||||
}
|
||||
if (data.weeks !== undefined) {
|
||||
root.weeks = data.weeks;
|
||||
@@ -1503,7 +1515,7 @@ var shared_root_computed = {
|
||||
return form_target
|
||||
},
|
||||
useIframe: function () {
|
||||
return !this.disable_iframe && Math.min(screen.width, window.innerWidth) >= 800 && (this.skip_ssl || site_is_secure());
|
||||
return !this.disable_iframe && (this.skip_ssl || site_is_secure());
|
||||
},
|
||||
showPrices: function () {
|
||||
var has_priced = false;
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
.help-block {
|
||||
color: $text-muted;
|
||||
}
|
||||
|
||||
a.btn, button.btn {
|
||||
max-width: 100%;
|
||||
white-space: normal;
|
||||
|
||||
@@ -788,5 +788,30 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.pretix-widget-frame-holder .pretix-widget-frame-inner {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
background: $brand-primary;
|
||||
border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
-webkit-border-radius: 0;
|
||||
box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
padding: 40px 0 0 0;
|
||||
}
|
||||
.pretix-widget-frame-holder .pretix-widget-frame-close {
|
||||
right: 20px;
|
||||
top: 20px;
|
||||
background: white;
|
||||
svg path {
|
||||
fill: $brand-primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/Akryum/vue-resize/blob/master/dist/vue-resize.css
|
||||
.resize-observer[data-v-b329ee4c]{position:absolute;top:0;left:0;z-index:-1;width:100%;height:100%;border:none;background-color:transparent;pointer-events:none;display:block;overflow:hidden;opacity:0}.resize-observer[data-v-b329ee4c] object{display:block;position:absolute;top:0;left:0;height:100%;width:100%;overflow:hidden;pointer-events:none;z-index:-1}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
django-debug-toolbar==3.2
|
||||
django-debug-toolbar==2.1
|
||||
# Testing requirements
|
||||
pycodestyle==2.6.*
|
||||
pyflakes==2.2.*
|
||||
pycodestyle==2.5.*
|
||||
pyflakes==2.1.*
|
||||
pep8-naming
|
||||
flake8==3.8.*
|
||||
flake8==3.7.*
|
||||
codecov
|
||||
coverage
|
||||
pytest-cov
|
||||
@@ -11,12 +11,12 @@ pytest==6.*
|
||||
pytest-django==4.*
|
||||
isort
|
||||
pytest-rerunfailures==9.*
|
||||
pytest-mock==3.5.*
|
||||
pytest-mock==2.0.*
|
||||
responses
|
||||
potypo
|
||||
freezegun
|
||||
|
||||
# Not really required, just nice to have
|
||||
pytest-xdist==2.2.*
|
||||
pytest-xdist==1.31.*
|
||||
pytest-cache
|
||||
pytest-sugar
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
# Functional requirements
|
||||
Django==3.1.*
|
||||
djangorestframework==3.12.*
|
||||
Django==3.0.*,>=3.0.9
|
||||
djangorestframework==3.11.*
|
||||
python-dateutil==2.8.*
|
||||
isoweek
|
||||
requests==2.25.0
|
||||
requests==2.24.0
|
||||
pytz
|
||||
django-bootstrap3==14.0.*
|
||||
django-bootstrap3==12.0.*
|
||||
django-formset-js-improved==0.5.0.2
|
||||
django-compressor==2.4.*
|
||||
django-hierarkey==1.0.*,>=1.0.4
|
||||
django-filter==2.4.*
|
||||
django-filter==2.2.*
|
||||
django-scopes==1.2.*
|
||||
reportlab>=3.5.65
|
||||
reportlab>=3.5.18
|
||||
PyPDF2==1.26.*
|
||||
Pillow==8.*
|
||||
Pillow==7.*
|
||||
django-libsass==0.8
|
||||
libsass==0.20.*
|
||||
django-otp==0.7.*,>=0.7.5
|
||||
@@ -22,9 +22,9 @@ webauthn==0.4.*
|
||||
django-formtools==2.2
|
||||
celery==4.4.*
|
||||
kombu==4.6.*
|
||||
django-statici18n==2.0.*
|
||||
django-statici18n==1.9.*
|
||||
inlinestyler==0.2.*
|
||||
BeautifulSoup4==4.9.*
|
||||
BeautifulSoup4==4.8.*
|
||||
slimit
|
||||
lxml
|
||||
static3==0.7.*
|
||||
@@ -35,38 +35,39 @@ markdown==3.3.*
|
||||
bleach==3.3.*
|
||||
sentry-sdk==0.14.*
|
||||
babel
|
||||
django-i18nfield==1.9.*
|
||||
django-hijack==2.3.*
|
||||
django-i18nfield>=1.7.0
|
||||
django-hijack>=2.1.10,<2.2.0
|
||||
jsonschema
|
||||
openpyxl==3.0.*
|
||||
django-oauth-toolkit==1.4.*
|
||||
django-oauth-toolkit==1.2.*
|
||||
oauthlib==3.1.*
|
||||
django-jsonfallback>=2.1.2
|
||||
psycopg2-binary
|
||||
tqdm==4.*
|
||||
# Stripe
|
||||
stripe==2.56.*
|
||||
stripe==2.42.*
|
||||
# PayPal
|
||||
paypalrestsdk==1.13.*
|
||||
pycparser==2.13 # https://github.com/eliben/pycparser/issues/147
|
||||
# Banktransfer
|
||||
chardet==4.0.*
|
||||
chardet<3.1.0,>=3.0.2
|
||||
mt-940==3.2
|
||||
vobject==0.9.*
|
||||
pycountry
|
||||
django-countries==7.*
|
||||
django-countries>=6.0
|
||||
pyuca # for better sorting of country names in django-countries
|
||||
defusedcsv>=1.1.0
|
||||
vat_moss_forked==2020.3.20.0.11.0
|
||||
django-localflavor==3.0.*
|
||||
django-redis==4.12.*
|
||||
redis==3.5.*
|
||||
django-phonenumber-field==5.0.*
|
||||
phonenumberslite==8.12.*
|
||||
django-localflavor>=2.2
|
||||
django-redis==4.11.*
|
||||
redis==3.4.*
|
||||
django-phonenumber-field==4.0.*
|
||||
phonenumberslite==8.11.*
|
||||
python-bidi==0.4.* # Support for arabic in reportlab
|
||||
arabic-reshaper==2.0.15 # Support for Aabic in reportlab
|
||||
packaging
|
||||
tlds>=2021031000
|
||||
tlds>=2020041600
|
||||
text-unidecode==1.*
|
||||
protobuf==3.15.*
|
||||
protobuf==3.13.*
|
||||
cryptography>=3.4.2
|
||||
sepaxml==2.4.*,>=2.4.1
|
||||
|
||||
55
src/setup.py
55
src/setup.py
@@ -88,19 +88,19 @@ setup(
|
||||
|
||||
keywords='tickets web shop ecommerce',
|
||||
install_requires=[
|
||||
'Django==3.1.*',
|
||||
'djangorestframework==3.12.*',
|
||||
'Django==3.0.*,>=3.0.9',
|
||||
'djangorestframework==3.11.*',
|
||||
'python-dateutil==2.8.*',
|
||||
'isoweek',
|
||||
'requests==2.25.*',
|
||||
'requests==2.24.*',
|
||||
'pytz',
|
||||
'django-bootstrap3==14.0.*',
|
||||
'django-bootstrap3==12.0.*',
|
||||
'django-formset-js-improved==0.5.0.2',
|
||||
'django-compressor==2.4.*',
|
||||
'django-hierarkey==1.0.*,>=1.0.4',
|
||||
'django-filter==2.4.*',
|
||||
'django-filter==2.2.*',
|
||||
'django-scopes==1.2.*',
|
||||
'reportlab>=3.5.65',
|
||||
'reportlab>=3.5.18',
|
||||
'Pillow==7.*',
|
||||
'PyPDF2==1.26.*',
|
||||
'django-libsass==0.8',
|
||||
@@ -111,9 +111,9 @@ setup(
|
||||
'django-formtools==2.2',
|
||||
'celery==4.4.*',
|
||||
'kombu==4.6.*',
|
||||
'django-statici18n==2.0.*',
|
||||
'django-statici18n==1.9.*',
|
||||
'inlinestyler==0.2.*',
|
||||
'BeautifulSoup4==4.9.*',
|
||||
'BeautifulSoup4==4.8.*',
|
||||
'slimit',
|
||||
'lxml',
|
||||
'static3==0.7.*',
|
||||
@@ -125,52 +125,53 @@ setup(
|
||||
'sentry-sdk==0.14.*',
|
||||
'babel',
|
||||
'paypalrestsdk==1.13.*',
|
||||
'django-redis==4.12.*',
|
||||
'redis==3.5.*',
|
||||
'stripe==2.56.*',
|
||||
'chardet==4.0.*',
|
||||
'pycparser==2.13',
|
||||
'django-redis==4.11.*',
|
||||
'redis==3.4.*',
|
||||
'stripe==2.42.*',
|
||||
'chardet<3.1.0,>=3.0.2',
|
||||
'mt-940==3.2',
|
||||
'django-i18nfield==1.9.*',
|
||||
'django-i18nfield>=1.7.0',
|
||||
'django-jsonfallback>=2.1.2',
|
||||
'psycopg2-binary',
|
||||
'tqdm==4.*',
|
||||
'vobject==0.9.*',
|
||||
'pycountry',
|
||||
'django-countries==7.*',
|
||||
'django-countries>=6.0',
|
||||
'pyuca',
|
||||
'defusedcsv>=1.1.0',
|
||||
'vat_moss_forked==2020.3.20.0.11.0',
|
||||
'django-localflavor==3.0.*',
|
||||
'django-localflavor>=2.2',
|
||||
'jsonschema',
|
||||
'django-hijack==2.3.*',
|
||||
'django-hijack>=2.1.10,<2.2.0',
|
||||
'openpyxl==3.0.*',
|
||||
'django-oauth-toolkit==1.4.*',
|
||||
'django-oauth-toolkit==1.2.*',
|
||||
'oauthlib==3.1.*',
|
||||
'django-phonenumber-field==5.0.*',
|
||||
'phonenumberslite==8.12.*',
|
||||
'django-phonenumber-field==4.0.*',
|
||||
'phonenumberslite==8.11.*',
|
||||
'python-bidi==0.4.*', # Support for Arabic in reportlab
|
||||
'arabic-reshaper==2.0.15', # Support for Arabic in reportlab
|
||||
'packaging',
|
||||
'tlds>=2021031000',
|
||||
'tlds>=2020041600',
|
||||
'text-unidecode==1.*',
|
||||
'protobuf==3.15.*',
|
||||
'protobuf==3.13.*',
|
||||
'cryptography>=3.4.2',
|
||||
'sepaxml==2.4.*,>=2.4.1',
|
||||
],
|
||||
extras_require={
|
||||
'dev': [
|
||||
'django-debug-toolbar==3.2',
|
||||
'pycodestyle==2.6.*',
|
||||
'pyflakes==2.2.*',
|
||||
'flake8==3.8.*',
|
||||
'django-debug-toolbar==2.1',
|
||||
'pycodestyle==2.5.*',
|
||||
'pyflakes==2.1.*',
|
||||
'flake8==3.7.*',
|
||||
'pep8-naming',
|
||||
'coveralls',
|
||||
'coverage',
|
||||
'pytest==6.*',
|
||||
'pytest-django==4.*',
|
||||
'pytest-xdist==2.2.*',
|
||||
'pytest-xdist==1.31.*',
|
||||
'isort',
|
||||
'pytest-mock==3.5.*',
|
||||
'pytest-mock==2.0.*',
|
||||
'pytest-rerunfailures==9.*',
|
||||
'responses',
|
||||
'potypo',
|
||||
|
||||
@@ -19,7 +19,7 @@ from pretix.testutils.mock import mocker_context
|
||||
|
||||
@pytest.fixture
|
||||
def variations(item):
|
||||
v = list()
|
||||
v = []
|
||||
v.append(item.variations.create(value="ChildA1"))
|
||||
v.append(item.variations.create(value="ChildA2"))
|
||||
return v
|
||||
|
||||
@@ -1055,7 +1055,7 @@ def test_items_with_cart_position_delete(token_client, organizer, event, item, c
|
||||
|
||||
@pytest.fixture
|
||||
def variations(item):
|
||||
v = list()
|
||||
v = []
|
||||
v.append(item.variations.create(value="ChildA1"))
|
||||
v.append(item.variations.create(value="ChildA2"))
|
||||
return v
|
||||
@@ -1063,7 +1063,7 @@ def variations(item):
|
||||
|
||||
@pytest.fixture
|
||||
def variations2(item2):
|
||||
v = list()
|
||||
v = []
|
||||
v.append(item2.variations.create(value="ChildB1"))
|
||||
v.append(item2.variations.create(value="ChildB2"))
|
||||
return v
|
||||
|
||||
@@ -15,7 +15,7 @@ from pretix.base.models.orders import OrderFee
|
||||
|
||||
@pytest.fixture
|
||||
def variations(item):
|
||||
v = list()
|
||||
v = []
|
||||
v.append(item.variations.create(value="ChildA1", default_price='12.00'))
|
||||
v.append(item.variations.create(value="ChildA2", default_price='13.00'))
|
||||
return v
|
||||
@@ -23,7 +23,7 @@ def variations(item):
|
||||
|
||||
@pytest.fixture
|
||||
def variations2(item2):
|
||||
v = list()
|
||||
v = []
|
||||
v.append(item2.variations.create(value="ChildB1", default_price='12.00'))
|
||||
v.append(item2.variations.create(value="ChildB2", default_price='13.00'))
|
||||
return v
|
||||
|
||||
Reference in New Issue
Block a user