Compare commits

..

1 Commits

Author SHA1 Message Date
Raphael Michel
6ea8a47f17 Try to debug flaky test 2021-03-02 10:43:37 +01:00
168 changed files with 36699 additions and 62361 deletions

View File

@@ -60,7 +60,6 @@ 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));
Also, if you use our ``pretix-shipping`` plugin::

View File

@@ -15,24 +15,8 @@ number string Invoice number
order string Order code of the order this invoice belongs to
is_cancellation boolean ``true``, if this invoice is the cancellation of a
different invoice.
invoice_from_name string Sender address: Name
invoice_from string Sender address: Address lines
invoice_from_zipcode string Sender address: ZIP code
invoice_from_city string Sender address: City
invoice_from_country string Sender address: Country code
invoice_from_tax_id string Sender address: Local Tax ID
invoice_from_vat_id string Sender address: EU VAT ID
invoice_to string Full recipient address
invoice_to_company string Recipient address: Company name
invoice_to_name string Recipient address: Person name
invoice_to_street string Recipient address: Address lines
invoice_to_zipcode string Recipient address: ZIP code
invoice_to_city string Recipient address: City
invoice_to_state string Recipient address: State (only used in some countries)
invoice_to_country string Recipient address: Country code
invoice_to_vat_id string Recipient address: EU VAT ID
invoice_to_beneficiary string Invoice beneficiary
custom_field string Custom invoice address field
invoice_from string Sender address
invoice_to string Receiver address
date date Invoice date
refers string Invoice number of an invoice this invoice refers to
(for example a cancellation refers to the invoice it
@@ -46,31 +30,6 @@ footer_text string Text to be prin
lines list of objects The actual invoice contents
├ position integer Number of the line within an invoice.
├ description string Text representing the invoice line (e.g. product name)
├ item integer Product used to create this line. Note that everything
about the product might have changed since the creation
of the invoice. Can be ``null`` for all invoice lines
created before this field was introduced as well as for
all lines not created by a product (e.g. a shipping or
cancellation fee).
├ variation integer Product variation used to create this line. Note that everything
about the product might have changed since the creation
of the invoice. Can be ``null`` for all invoice lines
created before this field was introduced as well as for
all lines not created by a product (e.g. a shipping or
cancellation fee).
├ event_date_from datetime Start date of the (sub)event this line was created for as it
was set during invoice creation. Can be ``null`` for all invoice
lines created before this was introduced as well as for lines in
an event series not created by a product (e.g. shipping or
cancellation fees).
├ event_date_to datetime End date of the (sub)event this line was created for as it
was set during invoice creation. Can be ``null`` for all invoice
lines created before this was introduced as well as for lines in
an event series not created by a product (e.g. shipping or
cancellation fees) as well as whenever the respective (sub)event
has no end date set.
├ attendee_name string Attendee name at time of invoice creation. Can be ``null`` if no
name was set or if names are configured to not be added to invoices.
├ gross_value money (string) Price including taxes
├ tax_value money (string) Tax amount included
├ tax_name string Name of used tax rate (e.g. "VAT")
@@ -91,12 +50,6 @@ internal_reference string Customer's refe
The attribute ``lines.number`` has been added.
.. versionchanged:: 3.17
The attribute ``invoice_to_*``, ``invoice_from_*``, ``custom_field``, ``lines.item``, ``lines.variation``, ``lines.event_date_from``,
``lines.event_date_to``, and ``lines.attendee_name`` have been added.
``refers`` now returns an invoice number including the prefix.
Endpoints
---------
@@ -130,24 +83,8 @@ Endpoints
"number": "SAMPLECONF-00001",
"order": "ABC12",
"is_cancellation": false,
"invoice_from_name": "Big Events LLC",
"invoice_from": "Demo street 12",
"invoice_from_zipcode":"",
"invoice_from_city":"Demo town",
"invoice_from_country":"US",
"invoice_from_tax_id":"",
"invoice_from_vat_id":"",
"invoice_to": "Sample company\nJohn Doe\nTest street 12\n12345 Testington\nTestikistan\nVAT-ID: EU123456789",
"invoice_to_company": "Sample company",
"invoice_to_name": "John Doe",
"invoice_to_street": "Test street 12",
"invoice_to_zipcode": "12345",
"invoice_to_city": "Testington",
"invoice_to_state": null,
"invoice_to_country": "TE",
"invoice_to_vat_id": "EU123456789",
"invoice_to_beneficiary": "",
"custom_field": null,
"invoice_from": "Big Events LLC\nDemo street 12\nDemo town",
"invoice_to": "Sample company\nJohn Doe\nTest street 12\n12345 Testington\nTestikistan\nVAT ID: EU123456789",
"date": "2017-12-01",
"refers": null,
"locale": "en",
@@ -160,11 +97,6 @@ Endpoints
{
"position": 1,
"description": "Budget Ticket",
"item": 1234,
"variation": 245,
"event_date_from": "2017-12-27T10:00:00Z",
"event_date_to": null,
"attendee_name": null,
"gross_value": "23.00",
"tax_value": "0.00",
"tax_name": "VAT",
@@ -216,24 +148,8 @@ Endpoints
"number": "SAMPLECONF-00001",
"order": "ABC12",
"is_cancellation": false,
"invoice_from_name": "Big Events LLC",
"invoice_from": "Demo street 12",
"invoice_from_zipcode":"",
"invoice_from_city":"Demo town",
"invoice_from_country":"US",
"invoice_from_tax_id":"",
"invoice_from_vat_id":"",
"invoice_to": "Sample company\nJohn Doe\nTest street 12\n12345 Testington\nTestikistan\nVAT-ID: EU123456789",
"invoice_to_company": "Sample company",
"invoice_to_name": "John Doe",
"invoice_to_street": "Test street 12",
"invoice_to_zipcode": "12345",
"invoice_to_city": "Testington",
"invoice_to_state": null,
"invoice_to_country": "TE",
"invoice_to_vat_id": "EU123456789",
"invoice_to_beneficiary": "",
"custom_field": null,
"invoice_from": "Big Events LLC\nDemo street 12\nDemo town",
"invoice_to": "Sample company\nJohn Doe\nTest street 12\n12345 Testington\nTestikistan\nVAT ID: EU123456789",
"date": "2017-12-01",
"refers": null,
"locale": "en",
@@ -246,11 +162,6 @@ Endpoints
{
"position": 1,
"description": "Budget Ticket",
"item": 1234,
"variation": 245,
"event_date_from": "2017-12-27T10:00:00Z",
"event_date_to": null,
"attendee_name": null,
"gross_value": "23.00",
"tax_value": "0.00",
"tax_name": "VAT",

View File

@@ -13,10 +13,7 @@ Field Type Description
===================================== ========================== =======================================================
id integer Internal ID of the waiting list entry
created datetime Creation date of the waiting list entry
name string Name of the user on the waiting list (or ``null``)
name_parts object of strings Decomposition of name of the user (or ``null``)
email string Email address of the user on the waiting list
phone string Phone number of the user on the waiting list (or ``null``)
voucher integer Internal ID of the voucher sent to this user. If
this field is set, the user has been sent a voucher
and is no longer waiting. If it is ``null``, the

View File

@@ -6,8 +6,8 @@ localecompile:
./manage.py compilemessages
localegen:
./manage.py makemessages --keep-pot --ignore "pretix/helpers/*" --ignore "pretix/static/npm_dir/*" $(LNGS)
./manage.py makemessages --keep-pot -d djangojs --ignore "pretix/static/npm_dir/*" --ignore "pretix/helpers/*" --ignore "pretix/static/jsi18n/*" --ignore "pretix/static/jsi18n/*" --ignore "pretix/static.dist/*" --ignore "data/*" --ignore "pretix/static/rrule/*" --ignore "build/*" $(LNGS)
./manage.py makemessages --keep-pot --ignore "pretix/helpers/*" $(LNGS)
./manage.py makemessages --keep-pot -d djangojs --ignore "pretix/helpers/*" --ignore "pretix/static/jsi18n/*" --ignore "pretix/static/jsi18n/*" --ignore "pretix/static.dist/*" --ignore "data/*" --ignore "pretix/static/rrule/*" --ignore "build/*" $(LNGS)
staticfiles: jsi18n
./manage.py collectstatic --noinput
@@ -23,8 +23,3 @@ test:
coverage:
coverage run -m py.test
npminstall:
mkdir -p pretix/static.dist/node_prefix
npm install --prefix=pretix/static.dist/node_prefix pretix/static/npm_dir/

View File

@@ -1,4 +1,4 @@
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ugettext_lazy as _
class FullAccessSecurityProfile:

View File

@@ -614,11 +614,6 @@ class EventSettingsSerializer(SettingsSerializer):
'waiting_list_enabled',
'waiting_list_hours',
'waiting_list_auto',
'waiting_list_names_asked',
'waiting_list_names_required',
'waiting_list_phones_asked',
'waiting_list_phones_required',
'waiting_list_phones_explanation_text',
'max_items_per_order',
'reservation_time',
'contact_mail',
@@ -629,7 +624,6 @@ class EventSettingsSerializer(SettingsSerializer):
'frontpage_subevent_ordering',
'event_list_type',
'frontpage_text',
'event_info_text',
'attendee_names_asked',
'attendee_names_required',
'attendee_emails_asked',

View File

@@ -45,14 +45,6 @@ class CompatibleCountryField(serializers.Field):
return instance.country_old
class CountryField(serializers.Field):
def to_internal_value(self, data):
return {self.field_name: Country(data)}
def to_representation(self, src):
return str(src) if src else None
class InvoiceAddressSerializer(I18nAwareModelSerializer):
country = CompatibleCountryField(source='*')
name = serializers.CharField(required=False)
@@ -1330,24 +1322,17 @@ class InlineInvoiceLineSerializer(I18nAwareModelSerializer):
class Meta:
model = InvoiceLine
fields = ('position', 'description', 'item', 'variation', 'attendee_name', 'event_date_from',
'event_date_to', 'gross_value', 'tax_value', 'tax_rate', 'tax_name')
fields = ('position', 'description', 'gross_value', 'tax_value', 'tax_rate', 'tax_name')
class InvoiceSerializer(I18nAwareModelSerializer):
order = serializers.SlugRelatedField(slug_field='code', read_only=True)
refers = serializers.SlugRelatedField(slug_field='full_invoice_no', read_only=True)
refers = serializers.SlugRelatedField(slug_field='invoice_no', read_only=True)
lines = InlineInvoiceLineSerializer(many=True)
invoice_to_country = CountryField()
invoice_from_country = CountryField()
class Meta:
model = Invoice
fields = ('order', 'number', 'is_cancellation', 'invoice_from', 'invoice_from_name', 'invoice_from_zipcode',
'invoice_from_city', 'invoice_from_country', 'invoice_from_tax_id', 'invoice_from_vat_id',
'invoice_to', 'invoice_to_company', 'invoice_to_name', 'invoice_to_street', 'invoice_to_zipcode',
'invoice_to_city', 'invoice_to_state', 'invoice_to_country', 'invoice_to_vat_id', 'invoice_to_beneficiary',
'custom_field', 'date', 'refers', 'locale',
fields = ('order', 'number', 'is_cancellation', 'invoice_from', 'invoice_to', 'date', 'refers', 'locale',
'introductory_text', 'additional_text', 'payment_provider_text', 'footer_text', 'lines',
'foreign_currency_display', 'foreign_currency_rate', 'foreign_currency_rate_date',
'internal_reference')

View File

@@ -8,7 +8,7 @@ class WaitingListSerializer(I18nAwareModelSerializer):
class Meta:
model = WaitingListEntry
fields = ('id', 'created', 'name', 'name_parts', 'email', 'phone', 'voucher', 'item', 'variation', 'locale', 'subevent', 'priority')
fields = ('id', 'created', 'email', 'voucher', 'item', 'variation', 'locale', 'subevent', 'priority')
read_only_fields = ('id', 'created', 'voucher')
def validate(self, data):
@@ -32,11 +32,4 @@ class WaitingListSerializer(I18nAwareModelSerializer):
if availability[0] == 100:
raise ValidationError("This product is currently available.")
if data.get('name') and data.get('name_parts'):
raise ValidationError(
{'name': ['Do not specify name if you specified name_parts.']}
)
if data.get('name_parts') and '_scheme' not in data.get('name_parts'):
data['name_parts']['_scheme'] = event.settings.name_scheme
return data

View File

@@ -8,7 +8,9 @@ from pretix.api.models import ApiCall, WebHookCall
from pretix.base.signals import periodic_task
from pretix.helpers.periodic import minimum_interval
register_webhook_events = Signal()
register_webhook_events = Signal(
providing_args=[]
)
"""
This signal is sent out to get all known webhook events. Receivers should return an
instance of a subclass of pretix.api.webhooks.WebhookEvent or a list of such

View File

@@ -1,7 +1,7 @@
import importlib
from django.apps import apps
from django.conf.urls import include, re_path
from django.conf.urls import include, url
from rest_framework import routers
from pretix.api.views import cart
@@ -72,32 +72,30 @@ for app in apps.get_app_configs():
importlib.import_module(app.name + '.urls')
urlpatterns = [
re_path(r'^', include(router.urls)),
re_path(r'^organizers/(?P<organizer>[^/]+)/', include(orga_router.urls)),
re_path(r'^organizers/(?P<organizer>[^/]+)/settings/$', organizer.OrganizerSettingsView.as_view(),
name="organizer.settings"),
re_path(r'^organizers/(?P<organizer>[^/]+)/giftcards/(?P<giftcard>[^/]+)/', include(giftcard_router.urls)),
re_path(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/settings/$', event.EventSettingsView.as_view(),
name="event.settings"),
re_path(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/', include(event_router.urls)),
re_path(r'^organizers/(?P<organizer>[^/]+)/teams/(?P<team>[^/]+)/', include(team_router.urls)),
re_path(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/items/(?P<item>[^/]+)/',
include(item_router.urls)),
re_path(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/questions/(?P<question>[^/]+)/',
include(question_router.urls)),
re_path(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/checkinlists/(?P<list>[^/]+)/',
include(checkinlist_router.urls)),
re_path(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/orders/(?P<order>[^/]+)/',
include(order_router.urls)),
re_path(r"^oauth/authorize$", oauth.AuthorizationView.as_view(), name="authorize"),
re_path(r"^oauth/token$", oauth.TokenView.as_view(), name="token"),
re_path(r"^oauth/revoke_token$", oauth.RevokeTokenView.as_view(), name="revoke-token"),
re_path(r"^device/initialize$", device.InitializeView.as_view(), name="device.initialize"),
re_path(r"^device/update$", device.UpdateView.as_view(), name="device.update"),
re_path(r"^device/roll$", device.RollKeyView.as_view(), name="device.roll"),
re_path(r"^device/revoke$", device.RevokeKeyView.as_view(), name="device.revoke"),
re_path(r"^device/eventselection$", device.EventSelectionView.as_view(), name="device.eventselection"),
re_path(r"^upload$", upload.UploadView.as_view(), name="upload"),
re_path(r"^me$", user.MeView.as_view(), name="user.me"),
re_path(r"^version$", version.VersionView.as_view(), name="version"),
url(r'^', include(router.urls)),
url(r'^organizers/(?P<organizer>[^/]+)/', include(orga_router.urls)),
url(r'^organizers/(?P<organizer>[^/]+)/settings/$', organizer.OrganizerSettingsView.as_view(),
name="organizer.settings"),
url(r'^organizers/(?P<organizer>[^/]+)/giftcards/(?P<giftcard>[^/]+)/', include(giftcard_router.urls)),
url(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/settings/$', event.EventSettingsView.as_view(),
name="event.settings"),
url(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/', include(event_router.urls)),
url(r'^organizers/(?P<organizer>[^/]+)/teams/(?P<team>[^/]+)/', include(team_router.urls)),
url(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/items/(?P<item>[^/]+)/', include(item_router.urls)),
url(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/questions/(?P<question>[^/]+)/',
include(question_router.urls)),
url(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/checkinlists/(?P<list>[^/]+)/',
include(checkinlist_router.urls)),
url(r'^organizers/(?P<organizer>[^/]+)/events/(?P<event>[^/]+)/orders/(?P<order>[^/]+)/', include(order_router.urls)),
url(r"^oauth/authorize$", oauth.AuthorizationView.as_view(), name="authorize"),
url(r"^oauth/token$", oauth.TokenView.as_view(), name="token"),
url(r"^oauth/revoke_token$", oauth.RevokeTokenView.as_view(), name="revoke-token"),
url(r"^device/initialize$", device.InitializeView.as_view(), name="device.initialize"),
url(r"^device/update$", device.UpdateView.as_view(), name="device.update"),
url(r"^device/roll$", device.RollKeyView.as_view(), name="device.roll"),
url(r"^device/revoke$", device.RevokeKeyView.as_view(), name="device.revoke"),
url(r"^device/eventselection$", device.EventSelectionView.as_view(), name="device.eventselection"),
url(r"^upload$", upload.UploadView.as_view(), name="upload"),
url(r"^me$", user.MeView.as_view(), name="user.me"),
url(r"^version$", version.VersionView.as_view(), name="version"),
]

View File

@@ -2,12 +2,10 @@ import inspect
import logging
from datetime import timedelta
from decimal import Decimal
from itertools import groupby
from smtplib import SMTPResponseException
from django.conf import settings
from django.core.mail.backends.smtp import EmailBackend
from django.db.models import Count
from django.dispatch import receiver
from django.template.loader import get_template
from django.utils.timezone import now
@@ -130,21 +128,9 @@ class TemplateBasedMailRenderer(BaseHTMLMailRenderer):
if order:
htmlctx['order'] = order
positions = list(order.positions.select_related(
'item', 'variation', 'subevent', 'addon_to'
).annotate(
has_addons=Count('addons')
))
htmlctx['cart'] = [(k, list(v)) for k, v in groupby(
positions, key=lambda op: (
op.item, op.variation, op.subevent, op.attendee_name,
(op.pk if op.addon_to_id else None), (op.pk if op.has_addons else None)
)
)]
if position:
htmlctx['position'] = position
htmlctx['ev'] = position.subevent or self.event
tpl = get_template(self.template_name)
body_html = inline_css(tpl.render(htmlctx))
@@ -251,8 +237,6 @@ def get_email_context(**kwargs):
from pretix.base.models import InvoiceAddress
event = kwargs['event']
if 'position' in kwargs:
kwargs.setdefault("position_or_address", kwargs['position'])
if 'order' in kwargs:
try:
kwargs['invoice_address'] = kwargs['order'].invoice_address

View File

@@ -13,12 +13,12 @@ from django.utils.functional import cached_property
from django.utils.translation import gettext, gettext_lazy as _, pgettext
from pretix.base.models import Invoice, InvoiceLine, OrderPayment
from ..services.export import ExportError
from ...control.forms.filter import get_all_payment_providers
from ...helpers import GroupConcat
from ...helpers.iter import chunked_iterable
from ..exporter import BaseExporter, MultiSheetListExporter
from ..services.export import ExportError
from ..services.invoices import invoice_pdf_task
from ..signals import (
register_data_exporters, register_multievent_data_exporters,

View File

@@ -691,18 +691,13 @@ class QuotaListExporter(ListExporter):
verbose_name = gettext_lazy('Quota availabilities')
def iterate_list(self, form_data):
has_subevents = self.event.has_subevents
headers = [
_('Quota name'), _('Total quota'), _('Paid orders'), _('Pending orders'), _('Blocking vouchers'),
_('Current user\'s carts'), _('Waiting list'), _('Exited orders'), _('Current availability')
]
if has_subevents:
headers.append(pgettext('subevent', 'Date'))
headers.append(_('Start date'))
headers.append(_('End date'))
yield headers
quotas = list(self.event.quotas.select_related('subevent'))
quotas = list(self.event.quotas.all())
qa = QuotaAvailability(full_results=True)
qa.queue(*quotas)
qa.compute()
@@ -720,18 +715,6 @@ class QuotaListExporter(ListExporter):
qa.count_exited_orders[quota],
_('Infinite') if avail[1] is None else avail[1]
]
if has_subevents:
if quota.subevent:
row.append(quota.subevent.name)
row.append(quota.subevent.date_from.astimezone(self.event.timezone).strftime('%Y-%m-%d %H:%M:%S'))
if quota.subevent.date_to:
row.append(quota.subevent.date_to.astimezone(self.event.timezone).strftime('%Y-%m-%d %H:%M:%S'))
else:
row.append('')
else:
row.append('')
row.append('')
row.append('')
yield row
def get_filename(self):

View File

@@ -82,9 +82,7 @@ class WaitingListExporter(ListExporter):
headers = [
_('Date'),
_('Name'),
_('Email'),
_('Phone number'),
_('Product name'),
_('Variation'),
_('Event slug'),
@@ -119,9 +117,7 @@ class WaitingListExporter(ListExporter):
row = [
entry.created.astimezone(tz).strftime(datetime_format), # alternative: .isoformat(),
entry.name,
entry.email,
entry.phone,
str(entry.item) if entry.item else "",
str(entry.variation) if entry.variation else "",
entry.event.slug,

View File

@@ -475,7 +475,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
if self.invoice.introductory_text:
story.append(Paragraph(
self.invoice.introductory_text,
bleach.clean(self.invoice.introductory_text, tags=[]).strip().replace('\n', '<br />\n'),
self.stylesheet['Normal']
))
story.append(Spacer(1, 10 * mm))
@@ -578,13 +578,13 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
if self.invoice.payment_provider_text:
story.append(Paragraph(
self.invoice.payment_provider_text,
bleach.clean(self.invoice.payment_provider_text, tags=[]).strip().replace('\n', '<br />\n'),
self.stylesheet['Normal']
))
if self.invoice.additional_text:
story.append(Paragraph(
self.invoice.additional_text,
bleach.clean(self.invoice.additional_text, tags=[]).strip().replace('\n', '<br />\n'),
self.stylesheet['Normal']
))
story.append(Spacer(1, 15 * mm))

View File

@@ -16,8 +16,6 @@ class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument('--tasks', action='store', type=str, help='Only execute the tasks with this name '
'(dotted path, comma separation)')
parser.add_argument('--exclude', action='store', type=str, help='Exclude the tasks with this name '
'(dotted path, comma separation)')
def handle(self, *args, **options):
verbosity = int(options['verbosity'])
@@ -30,9 +28,6 @@ class Command(BaseCommand):
if options.get('tasks'):
if name not in options.get('tasks').split(','):
continue
if options.get('exclude'):
if name in options.get('exclude').split(','):
continue
if verbosity > 1:
self.stdout.write(f'INFO Running {name}')

View File

@@ -1,6 +1,6 @@
# Generated by Django 2.1 on 2018-10-17 00:24
import jsonfallback.fields
from django.core.exceptions import ImproperlyConfigured
from django.db import migrations
from django_mysql.checks import mysql_connections
@@ -77,19 +77,19 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='cartposition',
name='attendee_name_parts',
field=models.JSONField(null=False, default=dict),
field=jsonfallback.fields.FallbackJSONField(null=False, default=dict),
preserve_default=False,
),
migrations.AddField(
model_name='orderposition',
name='attendee_name_parts',
field=models.JSONField(null=False, default=dict),
field=jsonfallback.fields.FallbackJSONField(null=False, default=dict),
preserve_default=False,
),
migrations.AddField(
model_name='invoiceaddress',
name='name_parts',
field=models.JSONField(default=dict),
field=jsonfallback.fields.FallbackJSONField(default=dict),
preserve_default=False,
),
migrations.RunPython(set_attendee_name_parts, migrations.RunPython.noop)

View File

@@ -1,6 +1,7 @@
# Generated by Django 2.1.1 on 2018-11-21 12:24
import django.db.models.deletion
import jsonfallback.fields
from django.db import migrations, models
import pretix.base.models.fields

View File

@@ -2,6 +2,7 @@
import django.db.models.deletion
import django.db.models.manager
import jsonfallback.fields
from django.db import migrations, models

View File

@@ -1,6 +1,7 @@
# Generated by Django 2.1 on 2019-01-12 15:12
import django.db.models.deletion
import jsonfallback.fields
from django.db import migrations, models
import pretix.base.models.fields

View File

@@ -1,6 +1,7 @@
# Generated by Django 2.1.5 on 2019-01-29 13:37
import django.db.models.deletion
import jsonfallback.fields
from django.db import migrations, models
import pretix.base.models.fields

View File

@@ -1,6 +1,7 @@
# Generated by Django 2.1.5 on 2019-02-01 15:27
import django.db.models.deletion
import jsonfallback.fields
from django.db import migrations, models
import pretix.base.models.fields

View File

@@ -3,6 +3,7 @@
from decimal import Decimal
import django.db.models.deletion
import jsonfallback.fields
from django.conf import settings
from django.core.cache import cache
from django.db import migrations, models
@@ -189,7 +190,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='cartposition',
name='attendee_name_parts',
field=models.JSONField(default=dict),
field=jsonfallback.fields.FallbackJSONField(default=dict),
),
migrations.AlterField(
model_name='cartposition',
@@ -209,7 +210,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='invoiceaddress',
name='name_parts',
field=models.JSONField(default=dict),
field=jsonfallback.fields.FallbackJSONField(default=dict),
),
migrations.AlterField(
model_name='item',
@@ -224,7 +225,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='orderposition',
name='attendee_name_parts',
field=models.JSONField(default=dict),
field=jsonfallback.fields.FallbackJSONField(default=dict),
),
migrations.AlterField(
model_name='orderposition',

View File

@@ -1,6 +1,7 @@
# Generated by Django 2.1 on 2019-02-08 14:32
import django.db.models.deletion
import jsonfallback.fields
from django.db import migrations, models
import pretix.base.models.fields

View File

@@ -1,6 +1,7 @@
# Generated by Django 2.1.5 on 2019-02-19 12:45
import django.db.models.deletion
import jsonfallback.fields
from django.db import migrations, models
import pretix.base.models.fields

View File

@@ -1,6 +1,7 @@
# Generated by Django 2.1.5 on 2019-02-19 09:49
import django.db.models.deletion
import jsonfallback.fields
from django.db import migrations, models
import pretix.base.models.fields

View File

@@ -1,6 +1,7 @@
# Generated by Django 2.1.5 on 2019-03-12 09:42
import django.db.models.deletion
import jsonfallback.fields
from django.db import migrations, models
import pretix.base.models.fields

View File

@@ -1,6 +1,7 @@
# Generated by Django 2.1.7 on 2019-03-16 10:14
import django.db.models.deletion
import jsonfallback.fields
from django.db import migrations, models
import pretix.base.models.fields

View File

@@ -3,6 +3,7 @@
from decimal import Decimal
import django.db.models.deletion
import jsonfallback.fields
from django.db import migrations, models
import pretix.base.models.fields

View File

@@ -1,6 +1,7 @@
# Generated by Django 2.1.5 on 2019-04-02 07:22
import django.db.models.deletion
import jsonfallback.fields
from django.db import migrations, models
import pretix.base.models.fields

View File

@@ -1,6 +1,7 @@
# Generated by Django 2.2 on 2019-04-23 08:39
import django.db.models.deletion
import jsonfallback.fields
from django.db import migrations, models
import pretix.base.models.fields

View File

@@ -1,6 +1,7 @@
# Generated by Django 2.2 on 2019-05-09 06:54
import django.db.models.deletion
import jsonfallback.fields
from django.db import migrations, models
import pretix.base.models.fields

View File

@@ -1,6 +1,7 @@
# Generated by Django 2.2 on 2019-05-09 07:36
import django.db.models.deletion
import jsonfallback.fields
from django.db import migrations, models
import pretix.base.models.fields
@@ -16,7 +17,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='cartposition',
name='attendee_name_parts',
field=models.JSONField(default=dict),
field=jsonfallback.fields.FallbackJSONField(default=dict),
),
migrations.AlterField(
model_name='cartposition',
@@ -36,7 +37,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='invoiceaddress',
name='name_parts',
field=models.JSONField(default=dict),
field=jsonfallback.fields.FallbackJSONField(default=dict),
),
migrations.AlterField(
model_name='item',
@@ -51,7 +52,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='orderposition',
name='attendee_name_parts',
field=models.JSONField(default=dict),
field=jsonfallback.fields.FallbackJSONField(default=dict),
),
migrations.AlterField(
model_name='orderposition',

View File

@@ -2,6 +2,7 @@
import django.db.models.deletion
import django_countries.fields
import jsonfallback.fields
from django.db import migrations, models
import pretix.helpers.countries
@@ -42,7 +43,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='checkinlist',
name='rules',
field=models.JSONField(default=dict),
field=jsonfallback.fields.FallbackJSONField(default=dict),
),
migrations.AlterUniqueTogether(
name='checkin',

View File

@@ -1,30 +0,0 @@
# Generated by Django 3.0.10 on 2021-03-01 15:10
import phonenumber_field.modelfields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0176_auto_20210205_1512'),
]
operations = [
migrations.AddField(
model_name='waitinglistentry',
name='name_cached',
field=models.CharField(max_length=255, null=True),
),
migrations.AddField(
model_name='waitinglistentry',
name='name_parts',
field=models.JSONField(default=dict),
),
migrations.AddField(
model_name='waitinglistentry',
name='phone',
field=phonenumber_field.modelfields.PhoneNumberField(max_length=128, null=True, region=None),
),
]

View File

@@ -1,34 +0,0 @@
# Generated by Django 3.0.12 on 2021-03-08 13:26
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0177_auto_20210301_1510'),
]
operations = [
migrations.AddField(
model_name='invoiceline',
name='attendee_name',
field=models.TextField(null=True),
),
migrations.AddField(
model_name='invoiceline',
name='event_date_to',
field=models.DateTimeField(null=True),
),
migrations.AddField(
model_name='invoiceline',
name='item',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='pretixbase.Item'),
),
migrations.AddField(
model_name='invoiceline',
name='variation',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='pretixbase.ItemVariation'),
),
]

View File

@@ -4,6 +4,7 @@ from django.db.models import Exists, F, Max, OuterRef, Q, Subquery
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _, pgettext_lazy
from django_scopes import ScopedManager, scopes_disabled
from jsonfallback.fields import FallbackJSONField
from pretix.base.models import LoggedModel
from pretix.base.models.fields import MultiStringField
@@ -46,7 +47,7 @@ class CheckinList(LoggedModel):
'any of the selected sales channels. This option can be useful when tickets sold at the box office '
'are not checked again before entry and should be considered validated directly upon purchase.')
)
rules = models.JSONField(default=dict, blank=True)
rules = FallbackJSONField(default=dict, blank=True)
objects = ScopedManager(organizer='event__organizer')

View File

@@ -697,9 +697,9 @@ class Event(EventMixin, LoggedModel):
for k, v in rules.items():
if k == 'lookup':
if v[0] == 'product':
v[1] = str(item_map.get(int(v[1]), 0).pk) if int(v[1]) in item_map else "0"
v[1] = str(item_map.get(int(v[1]), 0).pk)
elif v[0] == 'variation':
v[1] = str(variation_map.get(int(v[1]), 0).pk) if int(v[1]) in variation_map else "0"
v[1] = str(variation_map.get(int(v[1]), 0).pk)
else:
_walk_rules(v)
elif isinstance(rules, list):

View File

@@ -273,14 +273,6 @@ class InvoiceLine(models.Model):
:type subevent: SubEvent
:param event_date_from: Event date of the (sub)event at the time the invoice was created
:type event_date_from: datetime
:param event_date_to: Event end date of the (sub)event at the time the invoice was created
:type event_date_to: datetime
:param item: The item this line refers to
:type item: Item
:param variation: The variation this line refers to
:type variation: ItemVariation
:param attendee_name: The attendee name at the time the invoice was created
:type attendee_name: str
"""
invoice = models.ForeignKey('Invoice', related_name='lines', on_delete=models.CASCADE)
position = models.PositiveIntegerField(default=0)
@@ -291,10 +283,6 @@ class InvoiceLine(models.Model):
tax_name = models.CharField(max_length=190)
subevent = models.ForeignKey('SubEvent', null=True, blank=True, on_delete=models.PROTECT)
event_date_from = models.DateTimeField(null=True)
event_date_to = models.DateTimeField(null=True)
item = models.ForeignKey('Item', null=True, blank=True, on_delete=models.PROTECT)
variation = models.ForeignKey('ItemVariation', null=True, blank=True, on_delete=models.PROTECT)
attendee_name = models.TextField(null=True, blank=True)
@property
def net_value(self):

View File

@@ -29,6 +29,7 @@ from django.utils.translation import gettext_lazy as _, pgettext_lazy
from django_countries.fields import Country
from django_scopes import ScopedManager, scopes_disabled
from i18nfield.strings import LazyI18nString
from jsonfallback.fields import FallbackJSONField
from phonenumber_field.modelfields import PhoneNumberField
from phonenumber_field.phonenumber import PhoneNumber
from phonenumbers import NumberParseException
@@ -1131,7 +1132,7 @@ class AbstractPosition(models.Model):
blank=True, null=True,
help_text=_("Empty, if this product is not an admission ticket")
)
attendee_name_parts = models.JSONField(
attendee_name_parts = FallbackJSONField(
blank=True, default=dict
)
attendee_email = models.EmailField(
@@ -2251,7 +2252,7 @@ class InvoiceAddress(models.Model):
is_business = models.BooleanField(default=False, verbose_name=_('Business customer'))
company = models.CharField(max_length=255, blank=True, verbose_name=_('Company name'))
name_cached = models.CharField(max_length=255, verbose_name=_('Full name'), blank=True)
name_parts = models.JSONField(default=dict)
name_parts = FallbackJSONField(default=dict)
street = models.TextField(verbose_name=_('Address'), blank=False)
zipcode = models.CharField(max_length=30, verbose_name=_('ZIP code'), blank=False)
city = models.CharField(max_length=255, verbose_name=_('City'), blank=False)

View File

@@ -5,13 +5,11 @@ from django.db import models, transaction
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _, pgettext_lazy
from django_scopes import ScopedManager
from phonenumber_field.modelfields import PhoneNumberField
from pretix.base.email import get_email_context
from pretix.base.i18n import language
from pretix.base.models import Voucher
from pretix.base.services.mail import mail
from pretix.base.settings import PERSON_NAME_SCHEMES
from .base import LoggedModel
from .event import Event, SubEvent
@@ -39,21 +37,9 @@ class WaitingListEntry(LoggedModel):
verbose_name=_("On waiting list since"),
auto_now_add=True
)
name_cached = models.CharField(
max_length=255,
verbose_name=_("Name"),
blank=True, null=True,
)
name_parts = models.JSONField(
blank=True, default=dict
)
email = models.EmailField(
verbose_name=_("E-mail address")
)
phone = PhoneNumberField(
null=True, blank=True,
verbose_name=_("Phone number")
)
voucher = models.ForeignKey(
'Voucher',
verbose_name=_("Assigned voucher"),
@@ -97,27 +83,6 @@ class WaitingListEntry(LoggedModel):
WaitingListEntry.clean_itemvar(self.event, self.item, self.variation)
WaitingListEntry.clean_subevent(self.event, self.subevent)
def save(self, *args, **kwargs):
update_fields = kwargs.get('update_fields', [])
if 'name_parts' in update_fields:
update_fields.append('name_cached')
self.name_cached = self.name
if self.name_parts is None:
self.name_parts = {}
super().save(*args, **kwargs)
@property
def name(self):
if not self.name_parts:
return None
if '_legacy' in self.name_parts:
return self.name_parts['_legacy']
if '_scheme' in self.name_parts:
scheme = PERSON_NAME_SCHEMES[self.name_parts['_scheme']]
else:
scheme = PERSON_NAME_SCHEMES[self.event.settings.name_scheme]
return scheme['concatenation'](self.name_parts).strip()
def send_voucher(self, quota_cache=None, user=None, auth=None):
availability = (
self.variation.check_quotas(count_waitinglist=False, subevent=self.subevent, _cache=quota_cache)

View File

@@ -970,17 +970,17 @@ class ManualPayment(BasePaymentProvider):
label=_('Payment process description in order confirmation emails'),
help_text=_('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}, {amount}, {currency} and {amount_with_currency}.'),
'the placeholders {order}, {total}, {currency} and {total_with_currency}.'),
widget=I18nTextarea,
validators=[PlaceholderValidator(['{order}', '{amount}', '{currency}', '{amount_with_currency}'])],
validators=[PlaceholderValidator(['{order}', '{total}', '{currency}', '{total_with_currency}'])],
)),
('pending_description', I18nFormField(
label=_('Payment process description for pending orders'),
help_text=_('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}, {amount}, {currency} and {amount_with_currency}.'),
'the placeholders {order}, {total}, {currency} and {total_with_currency}.'),
widget=I18nTextarea,
validators=[PlaceholderValidator(['{order}', '{amount}', '{currency}', '{amount_with_currency}'])],
validators=[PlaceholderValidator(['{order}', '{total}', '{currency}', '{total_with_currency}'])],
)),
] + list(super().settings_form_fields.items())
)
@@ -1001,24 +1001,21 @@ class ManualPayment(BasePaymentProvider):
def checkout_confirm_render(self, request):
return self.payment_form_render(request)
def format_map(self, order, payment):
def format_map(self, order):
return {
'order': order.code,
'amount': payment.amount,
'currency': self.event.currency,
'amount_with_currency': money_filter(payment.amount, self.event.currency),
# {total} and {total_with_currency} are deprecated
'total': order.total,
'total_with_currency': money_filter(order.total, self.event.currency),
'currency': self.event.currency,
'total_with_currency': money_filter(order.total, self.event.currency)
}
def order_pending_mail_render(self, order, payment) -> str:
msg = str(self.settings.get('email_instructions', as_type=LazyI18nString)).format_map(self.format_map(order, payment))
def order_pending_mail_render(self, order) -> str:
msg = str(self.settings.get('email_instructions', as_type=LazyI18nString)).format_map(self.format_map(order))
return msg
def payment_pending_render(self, request, payment) -> str:
return rich_text(
str(self.settings.get('pending_description', as_type=LazyI18nString)).format_map(self.format_map(payment.order, payment))
str(self.settings.get('pending_description', as_type=LazyI18nString)).format_map(self.format_map(payment.order))
)

View File

@@ -298,11 +298,6 @@ DEFAULT_VARIABLES = OrderedDict((
"editor_sample": _("Event organizer info text"),
"evaluate": lambda op, order, ev: str(order.event.settings.organizer_info_text)
}),
("event_info_text", {
"label": _("Event info text"),
"editor_sample": _("Event info text"),
"evaluate": lambda op, order, ev: str(order.event.settings.event_info_text)
}),
("now_date", {
"label": _("Printing date"),
"editor_sample": _("2017-05-31"),

View File

@@ -171,17 +171,9 @@ def build_invoice(invoice: Invoice) -> Invoice:
if invoice.event.has_subevents:
desc += "<br />" + pgettext("subevent", "Date: {}").format(p.subevent)
InvoiceLine.objects.create(
position=i,
invoice=invoice,
description=desc,
gross_value=p.price,
tax_value=p.tax_value,
subevent=p.subevent,
item=p.item,
variation=p.variation,
attendee_name=p.attendee_name if invoice.event.settings.invoice_attendee_name else None,
event_date_from=p.subevent.date_from if invoice.event.has_subevents else invoice.event.date_from,
event_date_to=p.subevent.date_to if invoice.event.has_subevents else invoice.event.date_to,
position=i, invoice=invoice, description=desc,
gross_value=p.price, tax_value=p.tax_value,
subevent=p.subevent, event_date_from=(p.subevent.date_from if p.subevent else invoice.event.date_from),
tax_rate=p.tax_rate, tax_name=p.tax_rule.name if p.tax_rule else ''
)
@@ -206,8 +198,6 @@ def build_invoice(invoice: Invoice) -> Invoice:
invoice=invoice,
description=fee_title,
gross_value=fee.value,
event_date_from=None if invoice.event.has_subevents else invoice.event.date_from,
event_date_to=None if invoice.event.has_subevents else invoice.event.date_to,
tax_value=fee.tax_value,
tax_rate=fee.tax_rate,
tax_name=fee.tax_rule.name if fee.tax_rule else ''

View File

@@ -19,7 +19,6 @@ from django.core.mail import (
EmailMultiAlternatives, SafeMIMEMultipart, get_connection,
)
from django.core.mail.message import SafeMIMEText
from django.db import transaction
from django.template.loader import get_template
from django.utils.translation import gettext as _, pgettext
from django_scopes import scope, scopes_disabled
@@ -241,15 +240,7 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La
task_chain = []
task_chain.append(send_task)
if 'locmem' in settings.EMAIL_BACKEND:
# This clause is triggered during unit tests, because transaction.on_commit never fires due to the nature
# Django's unit tests work
chain(*task_chain).apply_async()
else:
transaction.on_commit(
lambda: chain(*task_chain).apply_async()
)
chain(*task_chain).apply_async()
class CustomEmail(EmailMultiAlternatives):

View File

@@ -327,7 +327,7 @@ def deny_order(order, comment='', user=None, send_mail: bool=True, auth=None):
def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, device=None, oauth_application=None,
cancellation_fee=None, keep_fees=None, cancel_invoice=True):
cancellation_fee=None, keep_fees=None):
"""
Mark this order as canceled
:param order: The order to change
@@ -351,10 +351,9 @@ def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, device
if not order.cancel_allowed():
raise OrderError(_('You cannot cancel this order.'))
invoices = []
if cancel_invoice:
i = order.invoices.filter(is_cancellation=False).last()
if i and not i.refered.exists():
invoices.append(generate_cancellation(i))
i = order.invoices.filter(is_cancellation=False).last()
if i and not i.refered.exists():
invoices.append(generate_cancellation(i))
for position in order.positions.all():
for gc in position.issued_gift_cards.all():
@@ -404,7 +403,7 @@ def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, device
order.cancellation_date = now()
order.save(update_fields=['status', 'cancellation_date', 'total'])
if cancel_invoice and i:
if i:
invoices.append(generate_invoice(order))
else:
with order.event.lock():
@@ -2153,12 +2152,11 @@ def _try_auto_refund(order, manual_refund=False, allow_partial=False, source=Ord
@app.task(base=ProfiledTask, bind=True, max_retries=5, default_retry_delay=1, throws=(OrderError,))
@scopes_disabled()
def cancel_order(self, order: int, user: int=None, send_mail: bool=True, api_token=None, oauth_application=None,
device=None, cancellation_fee=None, try_auto_refund=False, refund_as_giftcard=False, comment=None,
cancel_invoice=True):
device=None, cancellation_fee=None, try_auto_refund=False, refund_as_giftcard=False, comment=None):
try:
try:
ret = _cancel_order(order, user, send_mail, api_token, device, oauth_application,
cancellation_fee, cancel_invoice=cancel_invoice)
cancellation_fee)
if try_auto_refund:
_try_auto_refund(order, refund_as_giftcard=refund_as_giftcard,
comment=comment)

View File

@@ -975,61 +975,6 @@ DEFAULTS = {
widget=forms.NumberInput(),
)
},
'waiting_list_names_asked': {
'default': 'False',
'type': bool,
'form_class': forms.BooleanField,
'serializer_class': serializers.BooleanField,
'form_kwargs': dict(
label=_("Ask for a name"),
help_text=_("Ask for a name when signing up to the waiting list."),
)
},
'waiting_list_names_required': {
'default': 'False',
'type': bool,
'form_class': forms.BooleanField,
'serializer_class': serializers.BooleanField,
'form_kwargs': dict(
label=_("Require name"),
help_text=_("Require a name when signing up to the waiting list.."),
widget=forms.CheckboxInput(attrs={'data-checkbox-dependency': '#id_settings-waiting_list_names_asked'}),
)
},
'waiting_list_phones_asked': {
'default': 'False',
'type': bool,
'form_class': forms.BooleanField,
'serializer_class': serializers.BooleanField,
'form_kwargs': dict(
label=_("Ask for a phone number"),
help_text=_("Ask for a phone number when signing up to the waiting list."),
)
},
'waiting_list_phones_required': {
'default': 'False',
'type': bool,
'form_class': forms.BooleanField,
'serializer_class': serializers.BooleanField,
'form_kwargs': dict(
label=_("Require phone number"),
help_text=_("Require a phone number when signing up to the waiting list.."),
widget=forms.CheckboxInput(attrs={'data-checkbox-dependency': '#id_settings-waiting_list_phones_asked'}),
)
},
'waiting_list_phones_explanation_text': {
'default': '',
'type': LazyI18nString,
'form_class': I18nFormField,
'serializer_class': I18nField,
'form_kwargs': dict(
label=_("Phone number explanation"),
widget=I18nTextarea,
widget_kwargs={'attrs': {'rows': '2'}},
help_text=_("If you ask for a phone number, explain why you do so and what you will use the phone number for.")
)
},
'ticket_download': {
'default': 'False',
'type': bool,
@@ -2000,18 +1945,6 @@ Your {event} team"""))
widget=I18nTextarea
)
},
'event_info_text': {
'default': '',
'type': LazyI18nString,
'serializer_class': I18nField,
'form_class': I18nFormField,
'form_kwargs': dict(
label=_('Info text'),
widget=I18nTextarea,
widget_kwargs={'attrs': {'rows': '2'}},
help_text=_('Not displayed anywhere by default, but if you want to, you can use this e.g. in ticket templates.')
)
},
'banner_text': {
'default': '',
'type': LazyI18nString,

View File

@@ -203,7 +203,7 @@ class EmailAddressShredder(BaseDataShredder):
class WaitingListShredder(BaseDataShredder):
verbose_name = _('Waiting list')
identifier = 'waiting_list'
description = _('This will remove all names, email addresses, and phone numbers from the waiting list.')
description = _('This will remove all email addresses from the waiting list.')
def generate_files(self) -> List[Tuple[str, str, str]]:
yield 'waiting-list.json', 'application/json', json.dumps([
@@ -213,7 +213,7 @@ class WaitingListShredder(BaseDataShredder):
@transaction.atomic
def shred_data(self):
self.event.waitinglistentries.update(name_cached=None, name_parts={'_shredded': True}, email='', phone='')
self.event.waitinglistentries.update(email='')
for wle in self.event.waitinglistentries.select_related('voucher').filter(voucher__isnull=False):
if '@' in wle.voucher.comment:
@@ -222,14 +222,7 @@ class WaitingListShredder(BaseDataShredder):
for le in self.event.logentry_set.filter(action_type="pretix.voucher.added.waitinglist").exclude(data=""):
d = le.parsed_data
if 'name' in d:
d['name'] = ''
if 'name_parts' in d:
d['name_parts'] = {
'_legacy': ''
}
d['email'] = ''
d['phone'] = ''
le.data = json.dumps(d)
le.shredded = True
le.save(update_fields=['data', 'shredded'])

View File

@@ -164,7 +164,9 @@ class DeprecatedSignal(django.dispatch.Signal):
super().connect(receiver, sender=None, weak=True, dispatch_uid=None)
event_live_issues = EventPluginSignal()
event_live_issues = EventPluginSignal(
providing_args=[]
)
"""
This signal is sent out to determine whether an event can be taken live. If you want to
prevent the event from going live, return a string that will be displayed to the user
@@ -174,7 +176,9 @@ As with all event-plugin signals, the ``sender`` keyword argument will contain t
"""
register_payment_providers = EventPluginSignal()
register_payment_providers = EventPluginSignal(
providing_args=[]
)
"""
This signal is sent out to get all known payment providers. Receivers should return a
subclass of pretix.base.payment.BasePaymentProvider or a list of these
@@ -182,7 +186,9 @@ subclass of pretix.base.payment.BasePaymentProvider or a list of these
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
register_mail_placeholders = EventPluginSignal()
register_mail_placeholders = EventPluginSignal(
providing_args=[]
)
"""
This signal is sent out to get all known email text placeholders. Receivers should return
an instance of a subclass of pretix.base.email.BaseMailTextPlaceholder or a list of these.
@@ -190,7 +196,9 @@ an instance of a subclass of pretix.base.email.BaseMailTextPlaceholder or a list
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
register_html_mail_renderers = EventPluginSignal()
register_html_mail_renderers = EventPluginSignal(
providing_args=[]
)
"""
This signal is sent out to get all known HTML email renderers. Receivers should return a
subclass of pretix.base.email.BaseHTMLMailRenderer or a list of these
@@ -198,7 +206,9 @@ subclass of pretix.base.email.BaseHTMLMailRenderer or a list of these
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
register_invoice_renderers = EventPluginSignal()
register_invoice_renderers = EventPluginSignal(
providing_args=[]
)
"""
This signal is sent out to get all known invoice renderers. Receivers should return a
subclass of pretix.base.invoice.BaseInvoiceRenderer or a list of these
@@ -206,7 +216,9 @@ subclass of pretix.base.invoice.BaseInvoiceRenderer or a list of these
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
register_ticket_secret_generators = EventPluginSignal()
register_ticket_secret_generators = EventPluginSignal(
providing_args=[]
)
"""
This signal is sent out to get all known ticket secret generators. Receivers should return a
subclass of ``pretix.base.secrets.BaseTicketSecretGenerator`` or a list of these
@@ -214,7 +226,9 @@ subclass of ``pretix.base.secrets.BaseTicketSecretGenerator`` or a list of these
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
register_data_shredders = EventPluginSignal()
register_data_shredders = EventPluginSignal(
providing_args=[]
)
"""
This signal is sent out to get all known data shredders. Receivers should return a
subclass of pretix.base.shredder.BaseDataShredder or a list of these
@@ -222,7 +236,9 @@ subclass of pretix.base.shredder.BaseDataShredder or a list of these
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
register_ticket_outputs = EventPluginSignal()
register_ticket_outputs = EventPluginSignal(
providing_args=[]
)
"""
This signal is sent out to get all known ticket outputs. Receivers should return a
subclass of pretix.base.ticketoutput.BaseTicketOutput
@@ -230,7 +246,9 @@ subclass of pretix.base.ticketoutput.BaseTicketOutput
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
register_notification_types = EventPluginSignal()
register_notification_types = EventPluginSignal(
providing_args=[]
)
"""
This signal is sent out to get all known notification types. Receivers should return an
instance of a subclass of pretix.base.notifications.NotificationType or a list of such
@@ -241,14 +259,18 @@ however for this signal, the ``sender`` **may also be None** to allow creating t
notification settings!
"""
register_sales_channels = django.dispatch.Signal()
register_sales_channels = django.dispatch.Signal(
providing_args=[]
)
"""
This signal is sent out to get all known sales channels types. Receivers should return an
instance of a subclass of ``pretix.base.channels.SalesChannel`` or a list of such
instances.
"""
register_data_exporters = EventPluginSignal()
register_data_exporters = EventPluginSignal(
providing_args=[]
)
"""
This signal is sent out to get all known data exporters. Receivers should return a
subclass of pretix.base.exporter.BaseExporter
@@ -256,20 +278,21 @@ subclass of pretix.base.exporter.BaseExporter
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
register_multievent_data_exporters = django.dispatch.Signal()
register_multievent_data_exporters = django.dispatch.Signal(
providing_args=["event"]
)
"""
Arguments: ``event``
This signal is sent out to get all known data exporters, which support exporting data for
multiple events. Receivers should return a subclass of pretix.base.exporter.BaseExporter
The ``sender`` keyword argument will contain an organizer.
"""
validate_order = EventPluginSignal()
validate_order = EventPluginSignal(
providing_args=["payment_provider", "positions", "email", "locale", "invoice_address",
"meta_info"]
)
"""
Arguments: ``payment_provider``, ``positions``, ``email``, ``locale``, ``invoice_address``, ``meta_info``
This signal is sent out when the user tries to confirm the order, before we actually create
the order. It allows you to inspect the cart positions. Your return value will be ignored,
but you can raise an OrderError with an appropriate exception message if you like to block
@@ -278,10 +301,10 @@ the order. We strongly discourage making changes to the order here.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
validate_cart = EventPluginSignal()
validate_cart = EventPluginSignal(
providing_args=["positions"]
)
"""
Arguments: ``positions``
This signal is sent out before the user starts checkout. It includes an iterable
with the current CartPosition objects.
The response of receivers will be ignored, but you can raise a CartError with an
@@ -290,10 +313,10 @@ appropriate exception message.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
validate_cart_addons = EventPluginSignal()
validate_cart_addons = EventPluginSignal(
providing_args=["addons", "base_position", "iao"]
)
"""
Arguments: ``addons``, ``base_position``, ``iao``
This signal is sent when a user tries to select a combination of addons. In contrast to
``validate_cart``, this is executed before the cart is actually modified. You are passed
an argument ``addons`` containing a dict of ``(item, variation or None) → count`` tuples as well
@@ -305,10 +328,10 @@ appropriate exception message.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
order_placed = EventPluginSignal()
order_placed = EventPluginSignal(
providing_args=["order"]
)
"""
Arguments: ``order``
This signal is sent out every time an order is placed. The order object is given
as the first argument. This signal is *not* sent out if an order is created through
splitting an existing order, so you can not expect to see all orders by listening
@@ -317,10 +340,10 @@ to this signal.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
order_paid = EventPluginSignal()
order_paid = EventPluginSignal(
providing_args=["order"]
)
"""
Arguments: ``order``
This signal is sent out every time an order is paid. The order object is given
as the first argument. This signal is *not* sent out if an order is marked as paid
because an already-paid order has been split.
@@ -328,80 +351,80 @@ because an already-paid order has been split.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
order_canceled = EventPluginSignal()
order_canceled = EventPluginSignal(
providing_args=["order"]
)
"""
Arguments: ``order``
This signal is sent out every time an order is canceled. The order object is given
as the first argument.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
order_reactivated = EventPluginSignal()
order_reactivated = EventPluginSignal(
providing_args=["order"]
)
"""
Arguments: ``order``
This signal is sent out every time a canceled order is reactivated. The order object is given
as the first argument.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
order_expired = EventPluginSignal()
order_expired = EventPluginSignal(
providing_args=["order"]
)
"""
Arguments: ``order``
This signal is sent out every time an order is marked as expired. The order object is given
as the first argument.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
order_modified = EventPluginSignal()
order_modified = EventPluginSignal(
providing_args=["order"]
)
"""
Arguments: ``order``
This signal is sent out every time an order's information is modified. The order object is given
as the first argument.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
order_changed = EventPluginSignal()
order_changed = EventPluginSignal(
providing_args=["order"]
)
"""
Arguments: ``order``
This signal is sent out every time an order's content is changed. The order object is given
as the first argument.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
order_approved = EventPluginSignal()
order_approved = EventPluginSignal(
providing_args=["order"]
)
"""
Arguments: ``order``
This signal is sent out every time an order is being approved. The order object is given
as the first argument.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
order_denied = EventPluginSignal()
order_denied = EventPluginSignal(
providing_args=["order"]
)
"""
Arguments: ``order``
This signal is sent out every time an order is being denied. The order object is given
as the first argument.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
order_gracefully_delete = EventPluginSignal()
order_gracefully_delete = EventPluginSignal(
providing_args=["order"]
)
"""
Arguments: ``order``
This signal is sent out every time a test-mode order is being deleted. The order object
is given as the first argument.
@@ -412,10 +435,10 @@ the deletion of the order.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
checkin_created = EventPluginSignal()
checkin_created = EventPluginSignal(
providing_args=["checkin"],
)
"""
Arguments: ``checkin``
This signal is sent out every time a check-in is created (i.e. an order position is marked as
checked in). It is not send if the position was already checked in and is force-checked-in a second time.
The check-in object is given as the first argument
@@ -423,10 +446,10 @@ The check-in object is given as the first argument
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
logentry_display = EventPluginSignal()
logentry_display = EventPluginSignal(
providing_args=["logentry"]
)
"""
Arguments: ``logentry``
To display an instance of the ``LogEntry`` model to a human user,
``pretix.base.signals.logentry_display`` will be sent out with a ``logentry`` argument.
@@ -436,10 +459,10 @@ to the user. The receivers are expected to return plain text.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
logentry_object_link = EventPluginSignal()
logentry_object_link = EventPluginSignal(
providing_args=["logentry"]
)
"""
Arguments: ``logentry``
To display the relationship of an instance of the ``LogEntry`` model to another model
to a human user, ``pretix.base.signals.logentry_object_link`` will be sent out with a
``logentry`` argument.
@@ -464,10 +487,10 @@ Make sure that any user content in the HTML code you return is properly escaped!
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
requiredaction_display = EventPluginSignal()
requiredaction_display = EventPluginSignal(
providing_args=["action", "request"]
)
"""
Arguments: ``action``, ``request``
To display an instance of the ``RequiredAction`` model to a human user,
``pretix.base.signals.requiredaction_display`` will be sent out with a ``action`` argument.
You will also get the current ``request`` in a different argument.
@@ -478,10 +501,10 @@ to the user. The receivers are expected to return HTML code.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
event_copy_data = EventPluginSignal()
event_copy_data = EventPluginSignal(
providing_args=["other", "tax_map", "category_map", "item_map", "question_map", "variation_map", "checkin_list_map"]
)
"""
Arguments: "other", ``tax_map``, ``category_map``, ``item_map``, ``question_map``, ``variation_map``, ``checkin_list_map``
This signal is sent out when a new event is created as a clone of an existing event, i.e.
the settings from the older event are copied to the newer one. You can listen to this
signal to copy data or configuration stored within your plugin's models as well.
@@ -496,10 +519,10 @@ keyword argument will contain the event to **copy from**. The keyword arguments
in the new event of the respective types.
"""
item_copy_data = EventPluginSignal()
item_copy_data = EventPluginSignal(
providing_args=["source", "target"]
)
"""
Arguments: ``source``, ``target``
This signal is sent out when a new product is created as a clone of an existing product, i.e.
the settings from the older product are copied to the newer one. You can listen to this
signal to copy data or configuration stored within your plugin's models as well.
@@ -523,10 +546,10 @@ All plugins that are installed may send fields for the global settings form, as
an OrderedDict of (setting name, form field).
"""
order_fee_calculation = EventPluginSignal()
order_fee_calculation = EventPluginSignal(
providing_args=['positions', 'invoice_address', 'meta_info', 'total', 'gift_cards']
)
"""
Arguments: 'positions', 'invoice_address', 'meta_info', 'total', 'gift_cards'
This signals allows you to add fees to an order while it is being created. You are expected to
return a list of ``OrderFee`` objects that are not yet saved to the database
(because there is no order yet).
@@ -539,10 +562,10 @@ keyword argument will contain the total cart sum without any fees. You should no
the gift cards in use.
"""
order_fee_type_name = EventPluginSignal()
order_fee_type_name = EventPluginSignal(
providing_args=['request', 'fee']
)
"""
Arguments: 'request', 'fee'
This signals allows you to return a human-readable description for a fee type based on the ``fee_type``
and ``internal_type`` attributes of the ``OrderFee`` model that you get as keyword arguments. You are
expected to return a string or None, if you don't know about this fee.
@@ -550,20 +573,20 @@ expected to return a string or None, if you don't know about this fee.
As with all plugin signals, the ``sender`` keyword argument will contain the event.
"""
allow_ticket_download = EventPluginSignal()
allow_ticket_download = EventPluginSignal(
providing_args=['order']
)
"""
Arguments: 'order'
This signal is sent out to check if tickets for an order can be downloaded. If any receiver returns false,
a download will not be offered.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
email_filter = EventPluginSignal()
email_filter = EventPluginSignal(
providing_args=['message', 'order', 'user']
)
"""
Arguments: 'message', 'order', 'user'
This signal allows you to implement a middleware-style filter on all outgoing emails. You are expected to
return a (possibly modified) copy of the message object passed to you.
@@ -575,10 +598,10 @@ If the email is associated with a specific user, e.g. a notification email, the
well, otherwise it will be ``None``.
"""
global_email_filter = GlobalSignal()
global_email_filter = GlobalSignal(
providing_args=['message', 'order', 'user']
)
"""
Arguments: 'message', 'order', 'user'
This signal allows you to implement a middleware-style filter on all outgoing emails. You are expected to
return a (possibly modified) copy of the message object passed to you.
@@ -643,10 +666,10 @@ a ``subevent`` argument which might be none and you are expected to return a lis
"""
quota_availability = EventPluginSignal()
quota_availability = EventPluginSignal(
providing_args=['quota', 'result', 'count_waitinglist']
)
"""
Arguments: 'quota', 'result', 'count_waitinglist'
This signal allows you to modify the availability of a quota. You are passed the ``quota`` and an
``availability`` result calculated by pretix code or other plugins. ``availability`` is a tuple
with the first entry being one of the ``Quota.AVAILABILITY_*`` constants and the second entry being
@@ -659,23 +682,25 @@ system really bad.** Also, keep in mind that your response is subject to caching
quotas might be used for display (not for actual order processing).
"""
order_split = EventPluginSignal()
order_split = EventPluginSignal(
providing_args=["original", "split_order"]
)
"""
Arguments: ``original``, ``split_order``
This signal is sent out when an order is split into two orders and allows you to copy related models
to the new order. You will be passed the old order as ``original`` and the new order as ``split_order``.
"""
invoice_line_text = EventPluginSignal()
invoice_line_text = EventPluginSignal(
providing_args=["position"]
)
"""
Arguments: ``position``
This signal is sent out when an invoice is built for an order. You can return additional text that
should be shown on the invoice for the given ``position``.
"""
order_import_columns = EventPluginSignal()
order_import_columns = EventPluginSignal(
providing_args=[]
)
"""
This signal is sent out if the user performs an import of orders from an external source. You can use this
to define additional columns that can be read during import. You are expected to return a list of instances of
@@ -684,10 +709,10 @@ to define additional columns that can be read during import. You are expected to
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
validate_event_settings = EventPluginSignal()
validate_event_settings = EventPluginSignal(
providing_args=["settings_dict"]
)
"""
Arguments: ``settings_dict``
This signal is sent out if the user performs an update of event settings through the API or web interface.
You are passed a ``settings_dict`` dictionary with the new state of the event settings object and are expected
to raise a ``django.core.exceptions.ValidationError`` if the new state is not valid.
@@ -698,7 +723,9 @@ serializer field instead.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
api_event_settings_fields = EventPluginSignal()
api_event_settings_fields = EventPluginSignal(
providing_args=[]
)
"""
This signal is sent out to collect serializable settings fields for the API. You are expected to
return a dictionary mapping names of attributes in the settings store to DRF serializer field instances.

View File

@@ -136,31 +136,6 @@
text-decoration: none;
color: {{ color }};
}
.order-button {
padding-top: 5px
}
.order-button a.button {
font-size: 12px;
}
.order-info {
padding-bottom: 5px
}
.order {
font-size: 12px;
}
.cart-table > tr > td:first-child {
width: 40px;
}
.order-details > tr > td:first-child {
width: 20%;
}
.order-details td {
font-size: 12px;
}
{% if rtl %}
body {
direction: rtl;

View File

@@ -1,142 +0,0 @@
{% load eventurl %}
{% load i18n %}
{% if position %}
<div class="order-info">
{% trans "You are receiving this email because someone signed you up for the following event:" %}
</div>
<table class="order-details">
<tr>
<td>
<strong>{% trans "Event:" %}</strong>
</td>
<td>
{{ event.name }}
<br>
{% if event.has_subevents and ev.name|upper != event.name|upper %}{{ ev.name }}<br>{% endif %}
{{ ev.get_date_range_display }}
{% if event.settings.show_times %}
{{ ev.date_from|date:"TIME_FORMAT" }}
{% endif %}
</td>
</tr>
<tr>
<td>
<strong>{% trans "Order code:" %}</strong>
</td>
<td>
{{ order.code }} ({{ order.datetime|date:"SHORT_DATE_FORMAT" }})<br>
{% if order.email %}
{% trans "created by" %} {{ order.email }}
{% endif %}
</td>
</tr>
<tr>
<td>
<strong>{% trans "Organizer:" %}</strong>
</td>
<td>
{{ event.organizer }}
{% if event.settings.contact_mail %}
<br>
<a href="mailto:{{ event.settings.contact_mail }}">
{{ event.settings.contact_mail }}
</a>
{% endif %}
</td>
</tr>
</table>
<div class="order-button">
<a href="{% abseventurl event "presale:event.order.position" order=order.code secret=position.web_secret position=position.positionid %}" class="button">
{% trans "View registration details" %}
</a>
</div>
{% else %}
<div class="order-info">
{% trans "You are receiving this email because you placed an order for the following event:" %}
</div>
<table class="order-details">
<tr>
<td>
<strong>{% trans "Event:" %}</strong>
</td>
<td>
{{ event.name }}
{% if not event.has_subevents and event.settings.show_dates_on_frontpage %}
<br>
{{ event.get_date_range_display }}
{% if event.settings.show_times %}
{{ event.date_from|date:"TIME_FORMAT" }}
{% endif %}
{% endif %}
</td>
</tr>
<tr>
<td>
<strong>{% trans "Order code:" %}</strong>
</td>
<td>
{{ order.code }} ({{ order.datetime|date:"SHORT_DATE_FORMAT" }})
</td>
</tr>
{% if cart %}
<tr>
<td>
<strong>{% trans "Details:" %}</strong>
</td>
<td>
<table class="cart-table">
{% for groupkey, positions in cart %}
<tr>
<td>
{% if not groupkey.4 %} {# is addon #}
{{ positions|length }}x
{% endif %}
</td>
<td>
{% if groupkey.4 %} {# is addon #}
+
{% endif %}
{{ groupkey.0.name }}{% if groupkey.1 %} {{ groupkey.1.value }}{% endif %}
{% if groupkey.2 %} {# subevent #}
<br>
{% if groupkey.2.name|upper != event.name|upper %}
{{ groupkey.2.name }} &middot;
{% endif %}
{{ groupkey.2.get_date_range_display }}
{% if event.settings.show_times %}
{{ groupkey.2.date_from|date:"TIME_FORMAT" }}
{% endif %}
{% endif %}
{% if groupkey.3 %} {# attendee name #}
<br>
{{ groupkey.3.name }}
{% endif %}
</td>
</tr>
{% endfor %}
</table>
</td>
</tr>
{% endif %}
<tr>
<td>
<strong>{% trans "Organizer:" %}</strong>
</td>
<td>
{{ event.organizer }}
{% if event.settings.contact_mail %}
<br>
<a href="mailto:{{ event.settings.contact_mail }}">
{{ event.settings.contact_mail }}
</a>
{% endif %}
</td>
</tr>
</table>
<div class="order-button">
<a href="{% abseventurl event "presale:event.order.open" hash=order.email_confirm_hash order=order.code secret=order.secret %}" class="button">
{% trans "View order details" %}
</a>
</div>
{% endif %}

View File

@@ -23,7 +23,23 @@
<table cellpadding="20"><tr><td>
<![endif]-->
<div class="content">
{% include "pretixbase/email/order_details.html" %}
{% if position %}
{% trans "You are receiving this email because someone signed you up for the following event:" %}<br>
<strong>{% trans "Event:" %}</strong> {{ event.name }}<br>
<strong>{% trans "Order code:" %}</strong> {{ order.code }}<br>
<strong>{% trans "Order date:" %}</strong> {{ order.datetime|date:"SHORT_DATE_FORMAT" }}<br>
<a href="{% abseventurl event "presale:event.order.position" order=order.code secret=position.web_secret position=position.positionid %}">
{% trans "View registration details" %}
</a>
{% else %}
{% trans "You are receiving this email because you placed an order for the following event:" %}<br>
<strong>{% trans "Event:" %}</strong> {{ event.name }}<br>
<strong>{% trans "Order code:" %}</strong> {{ order.code }}<br>
<strong>{% trans "Order date:" %}</strong> {{ order.datetime|date:"SHORT_DATE_FORMAT" }}<br>
<a href="{% abseventurl event "presale:event.order.open" hash=order.email_confirm_hash order=order.code secret=order.secret %}">
{% trans "View order details" %}
</a>
{% endif %}
</div>
<!--[if gte mso 9]>
</td></tr></table>

View File

@@ -147,31 +147,6 @@
text-decoration: none;
color: {{ color }};
}
.order-button {
padding-top: 5px
}
.order-button a.button {
font-size: 12px;
}
.order-info {
padding-bottom: 5px
}
.order {
font-size: 12px;
}
.cart-table > tr > td:first-child {
width: 40px;
}
.order-details > tr > td:first-child {
width: 20%;
}
.order-details td {
font-size: 12px;
}
{% if rtl %}
body {
direction: rtl;
@@ -251,7 +226,23 @@
<table cellpadding="20"><tr><td>
<![endif]-->
<div class="content">
{% include "pretixbase/email/order_details.html" %}
{% if position %}
{% trans "You are receiving this email because someone signed you up for the following event:" %}<br>
<strong>{% trans "Event:" %}</strong> {{ event.name }}<br>
<strong>{% trans "Order code:" %}</strong> {{ order.code }}<br>
<strong>{% trans "Order date:" %}</strong> {{ order.datetime|date:"SHORT_DATE_FORMAT" }}<br>
<a href="{% abseventurl event "presale:event.order.position" order=order.code secret=position.web_secret position=position.positionid %}">
{% trans "View registration details" %}
</a>
{% else %}
{% trans "You are receiving this email because you placed an order for the following event:" %}<br>
<strong>{% trans "Event:" %}</strong> {{ event.name }}<br>
<strong>{% trans "Order code:" %}</strong> {{ order.code }}<br>
<strong>{% trans "Order date:" %}</strong> {{ order.datetime|date:"SHORT_DATE_FORMAT" }}<br>
<a href="{% abseventurl event "presale:event.order.open" hash=order.email_confirm_hash order=order.code secret=order.secret %}">
{% trans "View order details" %}
</a>
{% endif %}
</div>
<!--[if gte mso 9]>
</td></tr></table>

View File

@@ -1,7 +1,7 @@
{% load thumb %}
{% if widget.is_initial %}{{ widget.initial_text }}: <a href="{{ widget.value.url }}">{{ widget.value }}</a>{% if not widget.required %}
<input type="checkbox" name="{{ widget.checkbox_name }}" id="{{ widget.checkbox_id }}">
<label for="{{ widget.checkbox_id }}">{{ widget.clear_checkbox_label }}</label>{% endif %}{% if widget.value.is_img %}<br><a href="{{ widget.value.url }}" data-lightbox="{{ widget.name }}"><img src="{{ widget.value|thumb:"200x100" }}" /></a>{% endif %}<br>
<label for="{{ widget.checkbox_id }}">{{ widget.clear_checkbox_label }}</label>{% endif %}{% if widget.value.is_img %}<br><a href="{{ widget.value.url }}" data-lightbox="{{ widget.value.name }}"><img src="{{ widget.value|thumb:"200x100" }}" /></a>{% endif %}<br>
{{ widget.input_text }}:{% endif %}
<input type="{{ widget.type }}" name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>
{% if widget.cachedfile %}<input type="hidden" name="{{ widget.hidden_name }}" value="{{ widget.cachedfile.id }}">{% endif %}

View File

@@ -153,9 +153,9 @@ def markdown_compile_email(source):
class SnippetExtension(markdown.extensions.Extension):
def extendMarkdown(self, md, *args, **kwargs):
md.parser.blockprocessors.deregister('olist')
md.parser.blockprocessors.deregister('ulist')
md.parser.blockprocessors.deregister('quote')
del md.parser.blockprocessors['olist']
del md.parser.blockprocessors['ulist']
del md.parser.blockprocessors['quote']
def markdown_compile(source, snippet=False):

View File

@@ -7,7 +7,6 @@ from django.core.exceptions import ValidationError
from django.core.files import File
from django.core.files.uploadedfile import UploadedFile
from django.forms.utils import from_current_timezone
from django.urls import reverse
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
@@ -123,7 +122,7 @@ class CachedFileInput(forms.ClearableFileInput):
@property
def url(self):
return reverse('cachedfile.download', kwargs={'id': self.file.id})
return self.file.file.url
def value_from_datadict(self, data, files, name):
from ...base.models import CachedFile
@@ -201,8 +200,6 @@ class CachedFileField(ExtFileField):
from ...base.models import CachedFile
if isinstance(data, File):
if hasattr(data, '_uploaded_to'):
return data._uploaded_to
cf = CachedFile.objects.create(
expires=now() + datetime.timedelta(days=1),
date=now(),
@@ -212,7 +209,6 @@ class CachedFileField(ExtFileField):
)
cf.file.save(data.name, data.file)
cf.save()
data._uploaded_to = cf
return cf
return super().bound_data(data, initial)
@@ -221,8 +217,6 @@ class CachedFileField(ExtFileField):
data = super().clean(*args, **kwargs)
if isinstance(data, File):
if hasattr(data, '_uploaded_to'):
return data._uploaded_to
cf = CachedFile.objects.create(
expires=now() + datetime.timedelta(days=1),
web_download=True,
@@ -232,7 +226,6 @@ class CachedFileField(ExtFileField):
)
cf.file.save(data.name, data.file)
cf.save()
data._uploaded_to = cf
return cf
return data

View File

@@ -449,11 +449,6 @@ class EventSettingsForm(SettingsForm):
'waiting_list_enabled',
'waiting_list_hours',
'waiting_list_auto',
'waiting_list_names_asked',
'waiting_list_names_required',
'waiting_list_phones_asked',
'waiting_list_phones_required',
'waiting_list_phones_explanation_text',
'max_items_per_order',
'reservation_time',
'contact_mail',
@@ -464,7 +459,6 @@ class EventSettingsForm(SettingsForm):
'frontpage_subevent_ordering',
'event_list_type',
'frontpage_text',
'event_info_text',
'attendee_names_asked',
'attendee_names_required',
'attendee_emails_asked',

View File

@@ -5,9 +5,7 @@ from urllib.parse import urlencode
from django import forms
from django.apps import apps
from django.conf import settings
from django.db.models import (
Count, Exists, F, Max, Model, OuterRef, Q, QuerySet,
)
from django.db.models import Exists, F, Max, Model, OuterRef, Q, QuerySet
from django.db.models.functions import Coalesce, ExtractWeekDay
from django.urls import reverse, reverse_lazy
from django.utils.formats import date_format, localize
@@ -155,7 +153,6 @@ class OrderFilterForm(FilterForm):
(Order.STATUS_CANCELED, _('Canceled (fully)')),
('cp', _('Canceled (fully or with paid fee)')),
('rc', _('Cancellation requested')),
('cni', _('Fully canceled but invoice not canceled')),
)),
(_('Payment process'), (
(Order.STATUS_EXPIRED, _('Expired')),
@@ -267,18 +264,6 @@ class OrderFilterForm(FilterForm):
status=Order.STATUS_PAID,
pending_sum_t__gt=0
)
elif s == 'cni':
i = Invoice.objects.filter(
order=OuterRef('pk'),
is_cancellation=False,
refered__isnull=True,
).order_by().values('order').annotate(k=Count('id')).values('k')
qs = qs.annotate(
icnt=i
).filter(
icnt__gt=0,
status=Order.STATUS_CANCELED,
)
elif s == 'pa':
qs = qs.filter(
status=Order.STATUS_PENDING,

View File

@@ -117,11 +117,6 @@ class CancelForm(ConfirmPaymentForm):
'in your cancellation fee if you want to keep them. Please always enter a gross value, '
'tax will be calculated automatically.'),
)
cancel_invoice = forms.BooleanField(
label=_('Generate cancellation for invoice'),
initial=True,
required=False
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -135,8 +130,6 @@ class CancelForm(ConfirmPaymentForm):
self.fields['cancellation_fee'].max_value = prs
else:
del self.fields['cancellation_fee']
if not self.instance.invoices.exists():
del self.fields['cancel_invoice']
def clean_cancellation_fee(self):
val = self.cleaned_data['cancellation_fee'] or Decimal('0.00')

View File

@@ -2,7 +2,9 @@ from django.dispatch import Signal
from pretix.base.signals import DeprecatedSignal, EventPluginSignal
html_page_start = Signal()
html_page_start = Signal(
providing_args=[]
)
"""
This signal allows you to put code in the beginning of the main page for every
page in the backend. You are expected to return HTML.
@@ -10,10 +12,10 @@ page in the backend. You are expected to return HTML.
The ``sender`` keyword argument will contain the request.
"""
html_head = EventPluginSignal()
html_head = EventPluginSignal(
providing_args=["request"]
)
"""
Arguments: ``request``
This signal allows you to put code inside the HTML ``<head>`` tag
of every page in the backend. You will get the request as the keyword argument
``request`` and are expected to return plain HTML.
@@ -21,10 +23,10 @@ of every page in the backend. You will get the request as the keyword argument
As with all plugin signals, the ``sender`` keyword argument will contain the event.
"""
nav_event = EventPluginSignal()
nav_event = EventPluginSignal(
providing_args=["request"]
)
"""
Arguments: ``request``
This signal allows you to add additional views to the admin panel
navigation. You will get the request as a keyword argument ``request``.
Receivers are expected to return a list of dictionaries. The dictionaries
@@ -46,10 +48,10 @@ in pretix.
As with all plugin signals, the ``sender`` keyword argument will contain the event.
"""
nav_topbar = Signal()
nav_topbar = Signal(
providing_args=["request"]
)
"""
Arguments: ``request``
This signal allows you to add additional views to the top navigation bar.
You will get the request as a keyword argument ``request``.
Receivers are expected to return a list of dictionaries. The dictionaries
@@ -65,10 +67,10 @@ This is no ``EventPluginSignal``, so you do not get the event in the ``sender``
and you may get the signal regardless of whether your plugin is active.
"""
nav_global = Signal()
nav_global = Signal(
providing_args=["request"]
)
"""
Arguments: ``request``
This signal allows you to add additional views to the navigation bar when no event is
selected. You will get the request as a keyword argument ``request``.
Receivers are expected to return a list of dictionaries. The dictionaries
@@ -90,10 +92,10 @@ This is no ``EventPluginSignal``, so you do not get the event in the ``sender``
and you may get the signal regardless of whether your plugin is active.
"""
event_dashboard_top = EventPluginSignal()
event_dashboard_top = EventPluginSignal(
providing_args=['request']
)
"""
Arguments: 'request'
This signal is sent out to include custom HTML in the top part of the the event dashboard.
Receivers should return HTML.
@@ -101,7 +103,9 @@ As with all plugin signals, the ``sender`` keyword argument will contain the eve
An additional keyword argument ``subevent`` *can* contain a sub-event.
"""
event_dashboard_widgets = EventPluginSignal()
event_dashboard_widgets = EventPluginSignal(
providing_args=[]
)
"""
This signal is sent out to include widgets in the event dashboard. Receivers
should return a list of dictionaries, where each dictionary can have the keys:
@@ -116,10 +120,10 @@ As with all plugin signals, the ``sender`` keyword argument will contain the eve
An additional keyword argument ``subevent`` *can* contain a sub-event.
"""
user_dashboard_widgets = Signal()
user_dashboard_widgets = Signal(
providing_args=['user']
)
"""
Arguments: 'user'
This signal is sent out to include widgets in the personal user dashboard. Receivers
should return a list of dictionaries, where each dictionary can have the keys:
@@ -132,20 +136,20 @@ should return a list of dictionaries, where each dictionary can have the keys:
This is a regular django signal (no pretix event signal).
"""
voucher_form_html = EventPluginSignal()
voucher_form_html = EventPluginSignal(
providing_args=['form']
)
"""
Arguments: 'form'
This signal allows you to add additional HTML to the form that is used for modifying vouchers.
You receive the form object in the ``form`` keyword argument.
As with all plugin signals, the ``sender`` keyword argument will contain the event.
"""
voucher_form_class = EventPluginSignal()
voucher_form_class = EventPluginSignal(
providing_args=['cls']
)
"""
Arguments: 'cls'
This signal allows you to replace the form class that is used for modifying vouchers.
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.
@@ -153,10 +157,10 @@ You will receive the default form class (or the class set by a previous plugin)
As with all plugin signals, the ``sender`` keyword argument will contain the event.
"""
voucher_form_validation = EventPluginSignal()
voucher_form_validation = EventPluginSignal(
providing_args=['form']
)
"""
Arguments: 'form'
This signal allows you to add additional validation to the form that is used for
creating and modifying vouchers. You will receive the form instance in the ``form``
argument and the current data state in the ``data`` argument.
@@ -164,28 +168,28 @@ argument and the current data state in the ``data`` argument.
As with all plugin signals, the ``sender`` keyword argument will contain the event.
"""
quota_detail_html = EventPluginSignal()
quota_detail_html = EventPluginSignal(
providing_args=['quota']
)
"""
Arguments: 'quota'
This signal allows you to append HTML to a Quota's detail view. You receive the
quota as argument in the ``quota`` keyword argument.
As with all plugin signals, the ``sender`` keyword argument will contain the event.
"""
organizer_edit_tabs = DeprecatedSignal()
organizer_edit_tabs = DeprecatedSignal(
providing_args=['organizer', 'request']
)
"""
Arguments: 'organizer', 'request'
Deprecated signal, no longer works. We just keep the definition so old plugins don't
break the installation.
"""
nav_organizer = Signal()
nav_organizer = Signal(
providing_args=['organizer', 'request']
)
"""
Arguments: 'organizer', 'request'
This signal is sent out to include tab links on the detail page of an organizer.
Receivers are expected to return a list of dictionaries. The dictionaries
should contain at least the keys ``label`` and ``url``. You should also return
@@ -206,30 +210,30 @@ This is a regular django signal (no pretix event signal). Receivers will be pass
the keyword arguments ``organizer`` and ``request``.
"""
order_info = EventPluginSignal()
order_info = EventPluginSignal(
providing_args=["order", "request"]
)
"""
Arguments: ``order``, ``request``
This signal is sent out to display additional information on the order detail page
As with all plugin signals, the ``sender`` keyword argument will contain the event.
Additionally, the argument ``order`` and ``request`` are available.
"""
order_position_buttons = EventPluginSignal()
order_position_buttons = EventPluginSignal(
providing_args=["order", "position", "request"]
)
"""
Arguments: ``order``, ``position``, ``request``
This signal is sent out to display additional buttons for a single position of an order.
As with all plugin signals, the ``sender`` keyword argument will contain the event.
Additionally, the argument ``order`` and ``request`` are available.
"""
nav_event_settings = EventPluginSignal()
nav_event_settings = EventPluginSignal(
providing_args=['request']
)
"""
Arguments: 'request'
This signal is sent out to include tab links on the settings page of an event.
Receivers are expected to return a list of dictionaries. The dictionaries
should contain at least the keys ``label`` and ``url``. You should also return
@@ -244,10 +248,10 @@ As with all plugin signals, the ``sender`` keyword argument will contain the eve
A second keyword argument ``request`` will contain the request object.
"""
event_settings_widget = EventPluginSignal()
event_settings_widget = EventPluginSignal(
providing_args=['request']
)
"""
Arguments: 'request'
This signal is sent out to include template snippets on the settings page of an event
that allows generating a pretix Widget code.
@@ -255,10 +259,10 @@ As with all plugin signals, the ``sender`` keyword argument will contain the eve
A second keyword argument ``request`` will contain the request object.
"""
item_forms = EventPluginSignal()
item_forms = EventPluginSignal(
providing_args=['request', 'item']
)
"""
Arguments: 'request', 'item'
This signal allows you to return additional forms that should be rendered on the product
modification page. You are passed ``request`` and ``item`` arguments and are expected to return
an instance of a form class that you bind yourself when appropriate. Your form will be executed
@@ -268,10 +272,10 @@ styles. It is advisable to set a prefix for your form to avoid clashes with othe
As with all plugin signals, the ``sender`` keyword argument will contain the event.
"""
item_formsets = EventPluginSignal()
item_formsets = EventPluginSignal(
providing_args=['request', 'item']
)
"""
Arguments: 'request', 'item'
This signal allows you to return additional formsets that should be rendered on the product
modification page. You are passed ``request`` and ``item`` arguments and are expected to return
an instance of a formset class that you bind yourself when appropriate. Your formset will be
@@ -286,10 +290,10 @@ will be passed a ``formset`` variable with your formset.
As with all plugin signals, the ``sender`` keyword argument will contain the event.
"""
subevent_forms = EventPluginSignal()
subevent_forms = EventPluginSignal(
providing_args=['request', 'subevent', 'copy_from']
)
"""
Arguments: 'request', 'subevent', 'copy_from'
This signal allows you to return additional forms that should be rendered on the subevent creation
or modification page. You are passed ``request`` and ``subevent`` arguments and are expected to return
an instance of a form class that you bind yourself when appropriate. Your form will be executed
@@ -303,17 +307,17 @@ creation, ``copy_from`` can be a subevent that is being copied from.
As with all plugin signals, the ``sender`` keyword argument will contain the event.
"""
oauth_application_registered = Signal()
oauth_application_registered = Signal(
providing_args=["user", "application"]
)
"""
Arguments: ``user``, ``application``
This signal will be called whenever a user registers a new OAuth application.
"""
order_search_filter_q = Signal()
order_search_filter_q = Signal(
providing_args=["query"]
)
"""
Arguments: ``query``
This signal will be called whenever a free-text order search is performed. You are expected to return one
Q object that will be OR-ed with existing search queries. As order search exists on a global level as well,
this is not an Event signal and will be called even if your plugin is not active. ``sender`` will contain the
@@ -321,10 +325,10 @@ event if the search is performed within an event, and ``None`` otherwise. The se
``query``.
"""
order_search_forms = EventPluginSignal()
order_search_forms = EventPluginSignal(
providing_args=['request']
)
"""
Arguments: 'request'
This signal allows you to return additional forms that should be rendered in the advanced order search.
You are passed ``request`` argument and are expected to return an instance of a form class that you bind
yourself when appropriate. Your form will be executed as part of the standard validation and rendering

View File

@@ -193,7 +193,6 @@
{% bootstrap_field sform.checkout_phone_helptext layout="control" %}
{% bootstrap_field sform.banner_text layout="control" %}
{% bootstrap_field sform.banner_text_bottom layout="control" %}
{% bootstrap_field sform.event_info_text layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Shop design" %}</legend>
@@ -249,9 +248,6 @@
{% bootstrap_field sform.waiting_list_enabled layout="control" %}
{% bootstrap_field sform.waiting_list_auto layout="control" %}
{% bootstrap_field sform.waiting_list_hours layout="control" %}
{% bootstrap_field sform.waiting_list_names_asked_required layout="control" %}
{% bootstrap_field sform.waiting_list_phones_asked_required layout="control" %}
{% bootstrap_field sform.waiting_list_phones_explanation_text layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Item metadata" %}</legend>

View File

@@ -23,16 +23,13 @@
<input type="hidden" name="status" value="c"/>
{% bootstrap_form_errors form %}
{% bootstrap_field form.send_email layout='' %}
{% if form.cancel_invoice %}
{% bootstrap_field form.cancel_invoice layout='' %}
{% endif %}
{% if form.cancellation_fee %}
{% bootstrap_field form.cancellation_fee layout='' %}
{% endif %}
<div class="row checkout-button-row">
<div class="col-md-4">
<a class="btn btn-block btn-default btn-lg"
href="{% url "control:event.order" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}">
href="{% url "control:event.order" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}">
{% trans "No, take me back" %}
</a>
</div>

View File

@@ -170,10 +170,6 @@
</span>
{% endif %}
{{ o.total|money:request.event.currency }}
{% if o.status == "c" and o.icnt %}
<br>
<span class="label label-warning">{% trans "INVOICE NOT CANCELED" %}</span>
{% endif %}
</td>
<td class="text-right flip">{{ o.pcnt|default_if_none:"0" }}</td>
<td class="text-right flip">{% include "pretixcontrol/orders/fragment_order_status.html" with order=o %}</td>

View File

@@ -132,13 +132,7 @@
<table class="table table-condensed table-hover">
<thead>
<tr>
{% if request.event.settings.waiting_list_names_asked %}
<th>{% trans "Name" %}</th>
{% endif %}
<th>{% trans "Email" %}</th>
{% if request.event.settings.waiting_list_phones_asked %}
<th>{% trans "Phone number" %}</th>
{% endif %}
<th>{% trans "User" %}</th>
<th>{% trans "Product" %}</th>
{% if request.event.has_subevents %}
<th>{% trans "Date" context "subevent" %}</th>
@@ -152,13 +146,7 @@
<tbody>
{% for e in entries %}
<tr>
{% if request.event.settings.waiting_list_names_asked %}
<td>{{ e.name|default:"" }}</td>
{% endif %}
<td>{{ e.email }}</td>
{% if request.event.settings.waiting_list_phones_asked %}
<td>{{ e.phone|default:"" }}</td>
{% endif %}
<td>
{{ e.item }}
{% if e.variation %}

View File

@@ -1,4 +1,4 @@
from django.conf.urls import include, re_path
from django.conf.urls import include, url
from django.views.generic.base import RedirectView
from pretix.control.views import (
@@ -8,327 +8,310 @@ from pretix.control.views import (
)
urlpatterns = [
re_path(r'^logout$', auth.logout, name='auth.logout'),
re_path(r'^login$', auth.login, name='auth.login'),
re_path(r'^login/2fa$', auth.Login2FAView.as_view(), name='auth.login.2fa'),
re_path(r'^register$', auth.register, name='auth.register'),
re_path(r'^invite/(?P<token>[a-zA-Z0-9]+)$', auth.invite, name='auth.invite'),
re_path(r'^forgot$', auth.Forgot.as_view(), name='auth.forgot'),
re_path(r'^forgot/recover$', auth.Recover.as_view(), name='auth.forgot.recover'),
re_path(r'^$', dashboards.user_index, name='index'),
re_path(r'^widgets.json$', dashboards.user_index_widgets_lazy, name='index.widgets'),
re_path(r'^global/settings/$', global_settings.GlobalSettingsView.as_view(), name='global.settings'),
re_path(r'^global/update/$', global_settings.UpdateCheckView.as_view(), name='global.update'),
re_path(r'^global/message/$', global_settings.MessageView.as_view(), name='global.message'),
re_path(r'^logdetail/$', global_settings.LogDetailView.as_view(), name='global.logdetail'),
re_path(r'^logdetail/payment/$', global_settings.PaymentDetailView.as_view(), name='global.paymentdetail'),
re_path(r'^logdetail/refund/$', global_settings.RefundDetailView.as_view(), name='global.refunddetail'),
re_path(r'^geocode/$', geo.GeoCodeView.as_view(), name='global.geocode'),
re_path(r'^reauth/$', user.ReauthView.as_view(), name='user.reauth'),
re_path(r'^sudo/$', user.StartStaffSession.as_view(), name='user.sudo'),
re_path(r'^sudo/stop/$', user.StopStaffSession.as_view(), name='user.sudo.stop'),
re_path(r'^sudo/(?P<id>\d+)/$', user.EditStaffSession.as_view(), name='user.sudo.edit'),
re_path(r'^sudo/sessions/$', user.StaffSessionList.as_view(), name='user.sudo.list'),
re_path(r'^users/$', users.UserListView.as_view(), name='users'),
re_path(r'^users/select2$', typeahead.users_select2, name='users.select2'),
re_path(r'^users/add$', users.UserCreateView.as_view(), name='users.add'),
re_path(r'^users/impersonate/stop', users.UserImpersonateStopView.as_view(), name='users.impersonate.stop'),
re_path(r'^users/(?P<id>\d+)/$', users.UserEditView.as_view(), name='users.edit'),
re_path(r'^users/(?P<id>\d+)/reset$', users.UserResetView.as_view(), name='users.reset'),
re_path(r'^users/(?P<id>\d+)/impersonate', users.UserImpersonateView.as_view(), name='users.impersonate'),
re_path(r'^users/(?P<id>\d+)/anonymize', users.UserAnonymizeView.as_view(), name='users.anonymize'),
re_path(r'^pdf/editor/webfonts.css', pdf.FontsCSSView.as_view(), name='pdf.css'),
re_path(r'^settings/?$', user.UserSettings.as_view(), name='user.settings'),
re_path(r'^settings/history/$', user.UserHistoryView.as_view(), name='user.settings.history'),
re_path(r'^settings/notifications/$', user.UserNotificationsEditView.as_view(), name='user.settings.notifications'),
re_path(r'^settings/notifications/off/(?P<id>\d+)/(?P<token>[^/]+)/$', user.UserNotificationsDisableView.as_view(),
name='user.settings.notifications.off'),
re_path(r'^settings/oauth/authorized/$', oauth.AuthorizationListView.as_view(),
name='user.settings.oauth.list'),
re_path(r'^settings/oauth/authorized/(?P<pk>\d+)/revoke$', oauth.AuthorizationRevokeView.as_view(),
name='user.settings.oauth.revoke'),
re_path(r'^settings/oauth/apps/$', oauth.OAuthApplicationListView.as_view(),
name='user.settings.oauth.apps'),
re_path(r'^settings/oauth/apps/add$', oauth.OAuthApplicationRegistrationView.as_view(),
name='user.settings.oauth.apps.register'),
re_path(r'^settings/oauth/apps/(?P<pk>\d+)/$', oauth.OAuthApplicationUpdateView.as_view(),
name='user.settings.oauth.app'),
re_path(r'^settings/oauth/apps/(?P<pk>\d+)/disable$', oauth.OAuthApplicationDeleteView.as_view(),
name='user.settings.oauth.app.disable'),
re_path(r'^settings/oauth/apps/(?P<pk>\d+)/roll$', oauth.OAuthApplicationRollView.as_view(),
name='user.settings.oauth.app.roll'),
re_path(r'^settings/2fa/$', user.User2FAMainView.as_view(), name='user.settings.2fa'),
re_path(r'^settings/2fa/add$', user.User2FADeviceAddView.as_view(), name='user.settings.2fa.add'),
re_path(r'^settings/2fa/enable', user.User2FAEnableView.as_view(), name='user.settings.2fa.enable'),
re_path(r'^settings/2fa/disable', user.User2FADisableView.as_view(), name='user.settings.2fa.disable'),
re_path(r'^settings/2fa/regenemergency', user.User2FARegenerateEmergencyView.as_view(),
name='user.settings.2fa.regenemergency'),
re_path(r'^settings/2fa/totp/(?P<device>[0-9]+)/confirm', user.User2FADeviceConfirmTOTPView.as_view(),
name='user.settings.2fa.confirm.totp'),
re_path(r'^settings/2fa/webauthn/(?P<device>[0-9]+)/confirm', user.User2FADeviceConfirmWebAuthnView.as_view(),
name='user.settings.2fa.confirm.webauthn'),
re_path(r'^settings/2fa/(?P<devicetype>[^/]+)/(?P<device>[0-9]+)/delete', user.User2FADeviceDeleteView.as_view(),
name='user.settings.2fa.delete'),
re_path(r'^organizers/$', organizer.OrganizerList.as_view(), name='organizers'),
re_path(r'^organizers/add$', organizer.OrganizerCreate.as_view(), name='organizers.add'),
re_path(r'^organizers/select2$', typeahead.organizer_select2, name='organizers.select2'),
re_path(r'^organizer/(?P<organizer>[^/]+)/$', organizer.OrganizerDetail.as_view(), name='organizer'),
re_path(r'^organizer/(?P<organizer>[^/]+)/edit$', organizer.OrganizerUpdate.as_view(), name='organizer.edit'),
re_path(r'^organizer/(?P<organizer>[^/]+)/delete$', organizer.OrganizerDelete.as_view(), name='organizer.delete'),
re_path(r'^organizer/(?P<organizer>[^/]+)/settings/display$', organizer.OrganizerDisplaySettings.as_view(),
name='organizer.display'),
re_path(r'^organizer/(?P<organizer>[^/]+)/properties$', organizer.EventMetaPropertyListView.as_view(),
name='organizer.properties'),
re_path(r'^organizer/(?P<organizer>[^/]+)/property/add$', organizer.EventMetaPropertyCreateView.as_view(),
name='organizer.property.add'),
re_path(r'^organizer/(?P<organizer>[^/]+)/property/(?P<property>[^/]+)/edit$',
organizer.EventMetaPropertyUpdateView.as_view(),
name='organizer.property.edit'),
re_path(r'^organizer/(?P<organizer>[^/]+)/property/(?P<property>[^/]+)/delete$',
organizer.EventMetaPropertyDeleteView.as_view(),
name='organizer.property.delete'),
re_path(r'^organizer/(?P<organizer>[^/]+)/giftcards$', organizer.GiftCardListView.as_view(),
name='organizer.giftcards'),
re_path(r'^organizer/(?P<organizer>[^/]+)/giftcard/add$', organizer.GiftCardCreateView.as_view(),
name='organizer.giftcard.add'),
re_path(r'^organizer/(?P<organizer>[^/]+)/giftcard/(?P<giftcard>[^/]+)/$', organizer.GiftCardDetailView.as_view(),
name='organizer.giftcard'),
re_path(r'^organizer/(?P<organizer>[^/]+)/giftcard/(?P<giftcard>[^/]+)/edit$',
organizer.GiftCardUpdateView.as_view(),
name='organizer.giftcard.edit'),
re_path(r'^organizer/(?P<organizer>[^/]+)/webhooks$', organizer.WebHookListView.as_view(),
name='organizer.webhooks'),
re_path(r'^organizer/(?P<organizer>[^/]+)/webhook/add$', organizer.WebHookCreateView.as_view(),
name='organizer.webhook.add'),
re_path(r'^organizer/(?P<organizer>[^/]+)/webhook/(?P<webhook>[^/]+)/edit$', organizer.WebHookUpdateView.as_view(),
name='organizer.webhook.edit'),
re_path(r'^organizer/(?P<organizer>[^/]+)/webhook/(?P<webhook>[^/]+)/logs$', organizer.WebHookLogsView.as_view(),
name='organizer.webhook.logs'),
re_path(r'^organizer/(?P<organizer>[^/]+)/devices$', organizer.DeviceListView.as_view(), name='organizer.devices'),
re_path(r'^organizer/(?P<organizer>[^/]+)/device/add$', organizer.DeviceCreateView.as_view(),
name='organizer.device.add'),
re_path(r'^organizer/(?P<organizer>[^/]+)/device/(?P<device>[^/]+)/edit$', organizer.DeviceUpdateView.as_view(),
name='organizer.device.edit'),
re_path(r'^organizer/(?P<organizer>[^/]+)/device/(?P<device>[^/]+)/connect$', organizer.DeviceConnectView.as_view(),
name='organizer.device.connect'),
re_path(r'^organizer/(?P<organizer>[^/]+)/device/(?P<device>[^/]+)/revoke$', organizer.DeviceRevokeView.as_view(),
name='organizer.device.revoke'),
re_path(r'^organizer/(?P<organizer>[^/]+)/device/(?P<device>[^/]+)/logs$', organizer.DeviceLogView.as_view(),
name='organizer.device.logs'),
re_path(r'^organizer/(?P<organizer>[^/]+)/gates$', organizer.GateListView.as_view(), name='organizer.gates'),
re_path(r'^organizer/(?P<organizer>[^/]+)/gate/add$', organizer.GateCreateView.as_view(),
name='organizer.gate.add'),
re_path(r'^organizer/(?P<organizer>[^/]+)/gate/(?P<gate>[^/]+)/edit$', organizer.GateUpdateView.as_view(),
name='organizer.gate.edit'),
re_path(r'^organizer/(?P<organizer>[^/]+)/gate/(?P<gate>[^/]+)/delete$', organizer.GateDeleteView.as_view(),
name='organizer.gate.delete'),
re_path(r'^organizer/(?P<organizer>[^/]+)/teams$', organizer.TeamListView.as_view(), name='organizer.teams'),
re_path(r'^organizer/(?P<organizer>[^/]+)/team/add$', organizer.TeamCreateView.as_view(),
name='organizer.team.add'),
re_path(r'^organizer/(?P<organizer>[^/]+)/team/(?P<team>[^/]+)/$', organizer.TeamMemberView.as_view(),
name='organizer.team'),
re_path(r'^organizer/(?P<organizer>[^/]+)/team/(?P<team>[^/]+)/edit$', organizer.TeamUpdateView.as_view(),
name='organizer.team.edit'),
re_path(r'^organizer/(?P<organizer>[^/]+)/team/(?P<team>[^/]+)/delete$', organizer.TeamDeleteView.as_view(),
name='organizer.team.delete'),
re_path(r'^organizer/(?P<organizer>[^/]+)/slugrng', main.SlugRNG.as_view(), name='events.add.slugrng'),
re_path(r'^organizer/(?P<organizer>[^/]+)/logs', organizer.LogView.as_view(), name='organizer.log'),
re_path(r'^organizer/(?P<organizer>[^/]+)/export/$', organizer.ExportView.as_view(), name='organizer.export'),
re_path(r'^organizer/(?P<organizer>[^/]+)/export/do$', organizer.ExportDoView.as_view(),
name='organizer.export.do'),
re_path(r'^nav/typeahead/$', typeahead.nav_context_list, name='nav.typeahead'),
re_path(r'^events/$', main.EventList.as_view(), name='events'),
re_path(r'^events/add$', main.EventWizard.as_view(), name='events.add'),
re_path(r'^events/typeahead/$', typeahead.event_list, name='events.typeahead'),
re_path(r'^events/typeahead/meta/$', typeahead.meta_values, name='events.meta.typeahead'),
re_path(r'^search/orders/$', search.OrderSearch.as_view(), name='search.orders'),
re_path(r'^event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/', include([
re_path(r'^$', dashboards.event_index, name='event.index'),
re_path(r'^widgets.json$', dashboards.event_index_widgets_lazy, name='event.index.widgets'),
re_path(r'^live/$', event.EventLive.as_view(), name='event.live'),
re_path(r'^logs/$', event.EventLog.as_view(), name='event.log'),
re_path(r'^delete/$', event.EventDelete.as_view(), name='event.delete'),
re_path(r'^requiredactions/$', event.EventActions.as_view(), name='event.requiredactions'),
re_path(r'^requiredactions/(?P<id>\d+)/discard$', event.EventActionDiscard.as_view(),
name='event.requiredaction.discard'),
re_path(r'^comment/$', event.EventComment.as_view(),
name='event.comment'),
re_path(r'^quickstart/$', event.QuickSetupView.as_view(), name='event.quick'),
re_path(r'^settings/$', event.EventUpdate.as_view(), name='event.settings'),
re_path(r'^settings/plugins$', event.EventPlugins.as_view(), name='event.settings.plugins'),
re_path(r'^settings/payment/(?P<provider>[^/]+)$', event.PaymentProviderSettings.as_view(),
name='event.settings.payment.provider'),
re_path(r'^settings/payment$', event.PaymentSettings.as_view(), name='event.settings.payment'),
re_path(r'^settings/tickets$', event.TicketSettings.as_view(), name='event.settings.tickets'),
re_path(r'^settings/tickets/preview/(?P<output>[^/]+)$', event.TicketSettingsPreview.as_view(),
name='event.settings.tickets.preview'),
re_path(r'^settings/email$', event.MailSettings.as_view(), name='event.settings.mail'),
re_path(r'^settings/email/preview$', event.MailSettingsPreview.as_view(), name='event.settings.mail.preview'),
re_path(r'^settings/email/layoutpreview$', event.MailSettingsRendererPreview.as_view(),
name='event.settings.mail.preview.layout'),
re_path(r'^settings/cancel', event.CancelSettings.as_view(), name='event.settings.cancel'),
re_path(r'^settings/invoice$', event.InvoiceSettings.as_view(), name='event.settings.invoice'),
re_path(r'^settings/invoice/preview$', event.InvoicePreview.as_view(), name='event.settings.invoice.preview'),
re_path(r'^settings/display', event.DisplaySettings.as_view(), name='event.settings.display'),
re_path(r'^settings/tax/$', event.TaxList.as_view(), name='event.settings.tax'),
re_path(r'^settings/tax/(?P<rule>\d+)/$', event.TaxUpdate.as_view(), name='event.settings.tax.edit'),
re_path(r'^settings/tax/add$', event.TaxCreate.as_view(), name='event.settings.tax.add'),
re_path(r'^settings/tax/(?P<rule>\d+)/delete$', event.TaxDelete.as_view(), name='event.settings.tax.delete'),
re_path(r'^settings/widget$', event.WidgetSettings.as_view(), name='event.settings.widget'),
re_path(r'^pdf/editor/webfonts.css', pdf.FontsCSSView.as_view(), name='pdf.css'),
re_path(r'^pdf/editor/(?P<filename>[^/]+).pdf$', pdf.PdfView.as_view(), name='pdf.background'),
re_path(r'^subevents/$', subevents.SubEventList.as_view(), name='event.subevents'),
re_path(r'^subevents/select2$', typeahead.subevent_select2, name='event.subevents.select2'),
re_path(r'^subevents/(?P<subevent>\d+)/$', subevents.SubEventUpdate.as_view(), name='event.subevent'),
re_path(r'^subevents/(?P<subevent>\d+)/delete$', subevents.SubEventDelete.as_view(),
name='event.subevent.delete'),
re_path(r'^subevents/add$', subevents.SubEventCreate.as_view(), name='event.subevents.add'),
re_path(r'^subevents/bulk_add$', subevents.SubEventBulkCreate.as_view(), name='event.subevents.bulk'),
re_path(r'^subevents/bulk_action$', subevents.SubEventBulkAction.as_view(), name='event.subevents.bulkaction'),
re_path(r'^subevents/bulk_edit$', subevents.SubEventBulkEdit.as_view(), name='event.subevents.bulkedit'),
re_path(r'^items/$', item.ItemList.as_view(), name='event.items'),
re_path(r'^items/add$', item.ItemCreate.as_view(), name='event.items.add'),
re_path(r'^items/(?P<item>\d+)/$', item.ItemUpdateGeneral.as_view(), name='event.item'),
re_path(r'^items/(?P<item>\d+)/up$', item.item_move_up, name='event.items.up'),
re_path(r'^items/(?P<item>\d+)/down$', item.item_move_down, name='event.items.down'),
re_path(r'^items/(?P<item>\d+)/delete$', item.ItemDelete.as_view(), name='event.items.delete'),
re_path(r'^items/typeahead/meta/$', typeahead.item_meta_values, name='event.items.meta.typeahead'),
re_path(r'^items/select2$', typeahead.items_select2, name='event.items.select2'),
re_path(r'^items/select2/variation$', typeahead.variations_select2, name='event.items.variations.select2'),
re_path(r'^categories/$', item.CategoryList.as_view(), name='event.items.categories'),
re_path(r'^categories/select2$', typeahead.category_select2, name='event.items.categories.select2'),
re_path(r'^categories/(?P<category>\d+)/delete$', item.CategoryDelete.as_view(),
name='event.items.categories.delete'),
re_path(r'^categories/(?P<category>\d+)/up$', item.category_move_up, name='event.items.categories.up'),
re_path(r'^categories/(?P<category>\d+)/down$', item.category_move_down,
name='event.items.categories.down'),
re_path(r'^categories/(?P<category>\d+)/$', item.CategoryUpdate.as_view(),
name='event.items.categories.edit'),
re_path(r'^categories/add$', item.CategoryCreate.as_view(), name='event.items.categories.add'),
re_path(r'^questions/$', item.QuestionList.as_view(), name='event.items.questions'),
re_path(r'^questions/reorder$', item.reorder_questions, name='event.items.questions.reorder'),
re_path(r'^questions/(?P<question>\d+)/delete$', item.QuestionDelete.as_view(),
name='event.items.questions.delete'),
re_path(r'^questions/(?P<question>\d+)/$', item.QuestionView.as_view(),
name='event.items.questions.show'),
re_path(r'^questions/(?P<question>\d+)/change$', item.QuestionUpdate.as_view(),
name='event.items.questions.edit'),
re_path(r'^questions/add$', item.QuestionCreate.as_view(), name='event.items.questions.add'),
re_path(r'^quotas/$', item.QuotaList.as_view(), name='event.items.quotas'),
re_path(r'^quotas/(?P<quota>\d+)/$', item.QuotaView.as_view(), name='event.items.quotas.show'),
re_path(r'^quotas/select$', typeahead.quotas_select2, name='event.items.quotas.select2'),
re_path(r'^quotas/(?P<quota>\d+)/change$', item.QuotaUpdate.as_view(), name='event.items.quotas.edit'),
re_path(r'^quotas/(?P<quota>\d+)/delete$', item.QuotaDelete.as_view(),
name='event.items.quotas.delete'),
re_path(r'^quotas/add$', item.QuotaCreate.as_view(), name='event.items.quotas.add'),
re_path(r'^vouchers/$', vouchers.VoucherList.as_view(), name='event.vouchers'),
re_path(r'^vouchers/tags/$', vouchers.VoucherTags.as_view(), name='event.vouchers.tags'),
re_path(r'^vouchers/rng$', vouchers.VoucherRNG.as_view(), name='event.vouchers.rng'),
re_path(r'^vouchers/item_select$', typeahead.itemvarquota_select2, name='event.vouchers.itemselect2'),
re_path(r'^vouchers/(?P<voucher>\d+)/$', vouchers.VoucherUpdate.as_view(), name='event.voucher'),
re_path(r'^vouchers/(?P<voucher>\d+)/delete$', vouchers.VoucherDelete.as_view(),
name='event.voucher.delete'),
re_path(r'^vouchers/add$', vouchers.VoucherCreate.as_view(), name='event.vouchers.add'),
re_path(r'^vouchers/go$', vouchers.VoucherGo.as_view(), name='event.vouchers.go'),
re_path(r'^vouchers/bulk_add$', vouchers.VoucherBulkCreate.as_view(), name='event.vouchers.bulk'),
re_path(r'^vouchers/bulk_action$', vouchers.VoucherBulkAction.as_view(), name='event.vouchers.bulkaction'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/transition$', orders.OrderTransition.as_view(),
name='event.order.transition'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/resend$', orders.OrderResendLink.as_view(),
name='event.order.resendlink'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/(?P<position>\d+)/resend$', orders.OrderResendLink.as_view(),
name='event.order.resendlink'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/invoice$', orders.OrderInvoiceCreate.as_view(),
name='event.order.geninvoice'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/invoices/(?P<id>\d+)/regenerate$',
orders.OrderInvoiceRegenerate.as_view(),
name='event.order.regeninvoice'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/invoices/(?P<id>\d+)/reissue$', orders.OrderInvoiceReissue.as_view(),
name='event.order.reissueinvoice'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/download/(?P<position>\d+)/(?P<output>[^/]+)/$',
orders.OrderDownload.as_view(),
name='event.order.download.ticket'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/answer/(?P<answer>[^/]+)/$',
orders.AnswerDownload.as_view(),
name='event.order.download.answer'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/checkvatid', orders.OrderCheckVATID.as_view(),
name='event.order.checkvatid'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/extend$', orders.OrderExtend.as_view(),
name='event.order.extend'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/reactivate$', orders.OrderReactivate.as_view(),
name='event.order.reactivate'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/contact$', orders.OrderContactChange.as_view(),
name='event.order.contact'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/locale', orders.OrderLocaleChange.as_view(),
name='event.order.locale'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/comment$', orders.OrderComment.as_view(),
name='event.order.comment'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/change$', orders.OrderChange.as_view(),
name='event.order.change'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/approve', orders.OrderApprove.as_view(),
name='event.order.approve'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/deny$', orders.OrderDeny.as_view(),
name='event.order.deny'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/delete$', orders.OrderDelete.as_view(),
name='event.order.delete'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/info', orders.OrderModifyInformation.as_view(),
name='event.order.info'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/sendmail$', orders.OrderSendMail.as_view(),
name='event.order.sendmail'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/(?P<position>[0-9A-Z]+)/sendmail$',
orders.OrderPositionSendMail.as_view(),
name='event.order.position.sendmail'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/mail_history$', orders.OrderEmailHistory.as_view(),
name='event.order.mail_history'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/payments/(?P<payment>\d+)/cancel$', orders.OrderPaymentCancel.as_view(),
name='event.order.payments.cancel'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/payments/(?P<payment>\d+)/confirm$', orders.OrderPaymentConfirm.as_view(),
name='event.order.payments.confirm'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/refund$', orders.OrderRefundView.as_view(),
name='event.order.refunds.start'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/refunds/(?P<refund>\d+)/cancel$', orders.OrderRefundCancel.as_view(),
name='event.order.refunds.cancel'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/refunds/(?P<refund>\d+)/process$', orders.OrderRefundProcess.as_view(),
name='event.order.refunds.process'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/refunds/(?P<refund>\d+)/done$', orders.OrderRefundDone.as_view(),
name='event.order.refunds.done'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/cancellationrequests/(?P<req>\d+)/delete$',
orders.OrderCancellationRequestDelete.as_view(),
name='event.order.cancellationrequests.delete'),
re_path(r'^orders/(?P<code>[0-9A-Z]+)/$', orders.OrderDetail.as_view(), name='event.order'),
re_path(r'^invoice/(?P<invoice>[^/]+)$', orders.InvoiceDownload.as_view(),
name='event.invoice.download'),
re_path(r'^orders/overview/$', orders.OverView.as_view(), name='event.orders.overview'),
re_path(r'^orders/import/$', orderimport.ImportView.as_view(), name='event.orders.import'),
re_path(r'^orders/import/(?P<file>[^/]+)/$', orderimport.ProcessView.as_view(),
name='event.orders.import.process'),
re_path(r'^orders/export/$', orders.ExportView.as_view(), name='event.orders.export'),
re_path(r'^orders/export/do$', orders.ExportDoView.as_view(), name='event.orders.export.do'),
re_path(r'^orders/refunds/$', orders.RefundList.as_view(), name='event.orders.refunds'),
re_path(r'^orders/go$', orders.OrderGo.as_view(), name='event.orders.go'),
re_path(r'^orders/$', orders.OrderList.as_view(), name='event.orders'),
re_path(r'^orders/search$', orders.OrderSearch.as_view(), name='event.orders.search'),
re_path(r'^dangerzone/$', event.DangerZone.as_view(), name='event.dangerzone'),
re_path(r'^cancel/$', orders.EventCancel.as_view(), name='event.cancel'),
re_path(r'^shredder/$', shredder.StartShredView.as_view(), name='event.shredder.start'),
re_path(r'^shredder/export$', shredder.ShredExportView.as_view(), name='event.shredder.export'),
re_path(r'^shredder/download/(?P<file>[^/]+)/$', shredder.ShredDownloadView.as_view(),
name='event.shredder.download'),
re_path(r'^shredder/shred', shredder.ShredDoView.as_view(), name='event.shredder.shred'),
re_path(r'^waitinglist/$', waitinglist.WaitingListView.as_view(), name='event.orders.waitinglist'),
re_path(r'^waitinglist/auto_assign$', waitinglist.AutoAssign.as_view(), name='event.orders.waitinglist.auto'),
re_path(r'^waitinglist/(?P<entry>\d+)/delete$', waitinglist.EntryDelete.as_view(),
name='event.orders.waitinglist.delete'),
re_path(r'^checkinlists/$', checkin.CheckinListList.as_view(), name='event.orders.checkinlists'),
re_path(r'^checkinlists/add$', checkin.CheckinListCreate.as_view(), name='event.orders.checkinlists.add'),
re_path(r'^checkinlists/select2$', typeahead.checkinlist_select2, name='event.orders.checkinlists.select2'),
re_path(r'^checkinlists/(?P<list>\d+)/$', checkin.CheckInListShow.as_view(),
name='event.orders.checkinlists.show'),
re_path(r'^checkinlists/(?P<list>\d+)/change$', checkin.CheckinListUpdate.as_view(),
name='event.orders.checkinlists.edit'),
re_path(r'^checkinlists/(?P<list>\d+)/delete$', checkin.CheckinListDelete.as_view(),
name='event.orders.checkinlists.delete'),
url(r'^logout$', auth.logout, name='auth.logout'),
url(r'^login$', auth.login, name='auth.login'),
url(r'^login/2fa$', auth.Login2FAView.as_view(), name='auth.login.2fa'),
url(r'^register$', auth.register, name='auth.register'),
url(r'^invite/(?P<token>[a-zA-Z0-9]+)$', auth.invite, name='auth.invite'),
url(r'^forgot$', auth.Forgot.as_view(), name='auth.forgot'),
url(r'^forgot/recover$', auth.Recover.as_view(), name='auth.forgot.recover'),
url(r'^$', dashboards.user_index, name='index'),
url(r'^widgets.json$', dashboards.user_index_widgets_lazy, name='index.widgets'),
url(r'^global/settings/$', global_settings.GlobalSettingsView.as_view(), name='global.settings'),
url(r'^global/update/$', global_settings.UpdateCheckView.as_view(), name='global.update'),
url(r'^global/message/$', global_settings.MessageView.as_view(), name='global.message'),
url(r'^logdetail/$', global_settings.LogDetailView.as_view(), name='global.logdetail'),
url(r'^logdetail/payment/$', global_settings.PaymentDetailView.as_view(), name='global.paymentdetail'),
url(r'^logdetail/refund/$', global_settings.RefundDetailView.as_view(), name='global.refunddetail'),
url(r'^geocode/$', geo.GeoCodeView.as_view(), name='global.geocode'),
url(r'^reauth/$', user.ReauthView.as_view(), name='user.reauth'),
url(r'^sudo/$', user.StartStaffSession.as_view(), name='user.sudo'),
url(r'^sudo/stop/$', user.StopStaffSession.as_view(), name='user.sudo.stop'),
url(r'^sudo/(?P<id>\d+)/$', user.EditStaffSession.as_view(), name='user.sudo.edit'),
url(r'^sudo/sessions/$', user.StaffSessionList.as_view(), name='user.sudo.list'),
url(r'^users/$', users.UserListView.as_view(), name='users'),
url(r'^users/select2$', typeahead.users_select2, name='users.select2'),
url(r'^users/add$', users.UserCreateView.as_view(), name='users.add'),
url(r'^users/impersonate/stop', users.UserImpersonateStopView.as_view(), name='users.impersonate.stop'),
url(r'^users/(?P<id>\d+)/$', users.UserEditView.as_view(), name='users.edit'),
url(r'^users/(?P<id>\d+)/reset$', users.UserResetView.as_view(), name='users.reset'),
url(r'^users/(?P<id>\d+)/impersonate', users.UserImpersonateView.as_view(), name='users.impersonate'),
url(r'^users/(?P<id>\d+)/anonymize', users.UserAnonymizeView.as_view(), name='users.anonymize'),
url(r'^pdf/editor/webfonts.css', pdf.FontsCSSView.as_view(), name='pdf.css'),
url(r'^settings/?$', user.UserSettings.as_view(), name='user.settings'),
url(r'^settings/history/$', user.UserHistoryView.as_view(), name='user.settings.history'),
url(r'^settings/notifications/$', user.UserNotificationsEditView.as_view(), name='user.settings.notifications'),
url(r'^settings/notifications/off/(?P<id>\d+)/(?P<token>[^/]+)/$', user.UserNotificationsDisableView.as_view(),
name='user.settings.notifications.off'),
url(r'^settings/oauth/authorized/$', oauth.AuthorizationListView.as_view(),
name='user.settings.oauth.list'),
url(r'^settings/oauth/authorized/(?P<pk>\d+)/revoke$', oauth.AuthorizationRevokeView.as_view(),
name='user.settings.oauth.revoke'),
url(r'^settings/oauth/apps/$', oauth.OAuthApplicationListView.as_view(),
name='user.settings.oauth.apps'),
url(r'^settings/oauth/apps/add$', oauth.OAuthApplicationRegistrationView.as_view(),
name='user.settings.oauth.apps.register'),
url(r'^settings/oauth/apps/(?P<pk>\d+)/$', oauth.OAuthApplicationUpdateView.as_view(),
name='user.settings.oauth.app'),
url(r'^settings/oauth/apps/(?P<pk>\d+)/disable$', oauth.OAuthApplicationDeleteView.as_view(),
name='user.settings.oauth.app.disable'),
url(r'^settings/oauth/apps/(?P<pk>\d+)/roll$', oauth.OAuthApplicationRollView.as_view(),
name='user.settings.oauth.app.roll'),
url(r'^settings/2fa/$', user.User2FAMainView.as_view(), name='user.settings.2fa'),
url(r'^settings/2fa/add$', user.User2FADeviceAddView.as_view(), name='user.settings.2fa.add'),
url(r'^settings/2fa/enable', user.User2FAEnableView.as_view(), name='user.settings.2fa.enable'),
url(r'^settings/2fa/disable', user.User2FADisableView.as_view(), name='user.settings.2fa.disable'),
url(r'^settings/2fa/regenemergency', user.User2FARegenerateEmergencyView.as_view(),
name='user.settings.2fa.regenemergency'),
url(r'^settings/2fa/totp/(?P<device>[0-9]+)/confirm', user.User2FADeviceConfirmTOTPView.as_view(),
name='user.settings.2fa.confirm.totp'),
url(r'^settings/2fa/webauthn/(?P<device>[0-9]+)/confirm', user.User2FADeviceConfirmWebAuthnView.as_view(),
name='user.settings.2fa.confirm.webauthn'),
url(r'^settings/2fa/(?P<devicetype>[^/]+)/(?P<device>[0-9]+)/delete', user.User2FADeviceDeleteView.as_view(),
name='user.settings.2fa.delete'),
url(r'^organizers/$', organizer.OrganizerList.as_view(), name='organizers'),
url(r'^organizers/add$', organizer.OrganizerCreate.as_view(), name='organizers.add'),
url(r'^organizers/select2$', typeahead.organizer_select2, name='organizers.select2'),
url(r'^organizer/(?P<organizer>[^/]+)/$', organizer.OrganizerDetail.as_view(), name='organizer'),
url(r'^organizer/(?P<organizer>[^/]+)/edit$', organizer.OrganizerUpdate.as_view(), name='organizer.edit'),
url(r'^organizer/(?P<organizer>[^/]+)/delete$', organizer.OrganizerDelete.as_view(), name='organizer.delete'),
url(r'^organizer/(?P<organizer>[^/]+)/settings/display$', organizer.OrganizerDisplaySettings.as_view(),
name='organizer.display'),
url(r'^organizer/(?P<organizer>[^/]+)/properties$', organizer.EventMetaPropertyListView.as_view(), name='organizer.properties'),
url(r'^organizer/(?P<organizer>[^/]+)/property/add$', organizer.EventMetaPropertyCreateView.as_view(),
name='organizer.property.add'),
url(r'^organizer/(?P<organizer>[^/]+)/property/(?P<property>[^/]+)/edit$', organizer.EventMetaPropertyUpdateView.as_view(),
name='organizer.property.edit'),
url(r'^organizer/(?P<organizer>[^/]+)/property/(?P<property>[^/]+)/delete$', organizer.EventMetaPropertyDeleteView.as_view(),
name='organizer.property.delete'),
url(r'^organizer/(?P<organizer>[^/]+)/giftcards$', organizer.GiftCardListView.as_view(), name='organizer.giftcards'),
url(r'^organizer/(?P<organizer>[^/]+)/giftcard/add$', organizer.GiftCardCreateView.as_view(), name='organizer.giftcard.add'),
url(r'^organizer/(?P<organizer>[^/]+)/giftcard/(?P<giftcard>[^/]+)/$', organizer.GiftCardDetailView.as_view(), name='organizer.giftcard'),
url(r'^organizer/(?P<organizer>[^/]+)/giftcard/(?P<giftcard>[^/]+)/edit$', organizer.GiftCardUpdateView.as_view(),
name='organizer.giftcard.edit'),
url(r'^organizer/(?P<organizer>[^/]+)/webhooks$', organizer.WebHookListView.as_view(), name='organizer.webhooks'),
url(r'^organizer/(?P<organizer>[^/]+)/webhook/add$', organizer.WebHookCreateView.as_view(),
name='organizer.webhook.add'),
url(r'^organizer/(?P<organizer>[^/]+)/webhook/(?P<webhook>[^/]+)/edit$', organizer.WebHookUpdateView.as_view(),
name='organizer.webhook.edit'),
url(r'^organizer/(?P<organizer>[^/]+)/webhook/(?P<webhook>[^/]+)/logs$', organizer.WebHookLogsView.as_view(),
name='organizer.webhook.logs'),
url(r'^organizer/(?P<organizer>[^/]+)/devices$', organizer.DeviceListView.as_view(), name='organizer.devices'),
url(r'^organizer/(?P<organizer>[^/]+)/device/add$', organizer.DeviceCreateView.as_view(),
name='organizer.device.add'),
url(r'^organizer/(?P<organizer>[^/]+)/device/(?P<device>[^/]+)/edit$', organizer.DeviceUpdateView.as_view(),
name='organizer.device.edit'),
url(r'^organizer/(?P<organizer>[^/]+)/device/(?P<device>[^/]+)/connect$', organizer.DeviceConnectView.as_view(),
name='organizer.device.connect'),
url(r'^organizer/(?P<organizer>[^/]+)/device/(?P<device>[^/]+)/revoke$', organizer.DeviceRevokeView.as_view(),
name='organizer.device.revoke'),
url(r'^organizer/(?P<organizer>[^/]+)/device/(?P<device>[^/]+)/logs$', organizer.DeviceLogView.as_view(),
name='organizer.device.logs'),
url(r'^organizer/(?P<organizer>[^/]+)/gates$', organizer.GateListView.as_view(), name='organizer.gates'),
url(r'^organizer/(?P<organizer>[^/]+)/gate/add$', organizer.GateCreateView.as_view(), name='organizer.gate.add'),
url(r'^organizer/(?P<organizer>[^/]+)/gate/(?P<gate>[^/]+)/edit$', organizer.GateUpdateView.as_view(),
name='organizer.gate.edit'),
url(r'^organizer/(?P<organizer>[^/]+)/gate/(?P<gate>[^/]+)/delete$', organizer.GateDeleteView.as_view(),
name='organizer.gate.delete'),
url(r'^organizer/(?P<organizer>[^/]+)/teams$', organizer.TeamListView.as_view(), name='organizer.teams'),
url(r'^organizer/(?P<organizer>[^/]+)/team/add$', organizer.TeamCreateView.as_view(), name='organizer.team.add'),
url(r'^organizer/(?P<organizer>[^/]+)/team/(?P<team>[^/]+)/$', organizer.TeamMemberView.as_view(),
name='organizer.team'),
url(r'^organizer/(?P<organizer>[^/]+)/team/(?P<team>[^/]+)/edit$', organizer.TeamUpdateView.as_view(),
name='organizer.team.edit'),
url(r'^organizer/(?P<organizer>[^/]+)/team/(?P<team>[^/]+)/delete$', organizer.TeamDeleteView.as_view(),
name='organizer.team.delete'),
url(r'^organizer/(?P<organizer>[^/]+)/slugrng', main.SlugRNG.as_view(), name='events.add.slugrng'),
url(r'^organizer/(?P<organizer>[^/]+)/logs', organizer.LogView.as_view(), name='organizer.log'),
url(r'^organizer/(?P<organizer>[^/]+)/export/$', organizer.ExportView.as_view(), name='organizer.export'),
url(r'^organizer/(?P<organizer>[^/]+)/export/do$', organizer.ExportDoView.as_view(), name='organizer.export.do'),
url(r'^nav/typeahead/$', typeahead.nav_context_list, name='nav.typeahead'),
url(r'^events/$', main.EventList.as_view(), name='events'),
url(r'^events/add$', main.EventWizard.as_view(), name='events.add'),
url(r'^events/typeahead/$', typeahead.event_list, name='events.typeahead'),
url(r'^events/typeahead/meta/$', typeahead.meta_values, name='events.meta.typeahead'),
url(r'^search/orders/$', search.OrderSearch.as_view(), name='search.orders'),
url(r'^event/(?P<organizer>[^/]+)/(?P<event>[^/]+)/', include([
url(r'^$', dashboards.event_index, name='event.index'),
url(r'^widgets.json$', dashboards.event_index_widgets_lazy, name='event.index.widgets'),
url(r'^live/$', event.EventLive.as_view(), name='event.live'),
url(r'^logs/$', event.EventLog.as_view(), name='event.log'),
url(r'^delete/$', event.EventDelete.as_view(), name='event.delete'),
url(r'^requiredactions/$', event.EventActions.as_view(), name='event.requiredactions'),
url(r'^requiredactions/(?P<id>\d+)/discard$', event.EventActionDiscard.as_view(),
name='event.requiredaction.discard'),
url(r'^comment/$', event.EventComment.as_view(),
name='event.comment'),
url(r'^quickstart/$', event.QuickSetupView.as_view(), name='event.quick'),
url(r'^settings/$', event.EventUpdate.as_view(), name='event.settings'),
url(r'^settings/plugins$', event.EventPlugins.as_view(), name='event.settings.plugins'),
url(r'^settings/payment/(?P<provider>[^/]+)$', event.PaymentProviderSettings.as_view(),
name='event.settings.payment.provider'),
url(r'^settings/payment$', event.PaymentSettings.as_view(), name='event.settings.payment'),
url(r'^settings/tickets$', event.TicketSettings.as_view(), name='event.settings.tickets'),
url(r'^settings/tickets/preview/(?P<output>[^/]+)$', event.TicketSettingsPreview.as_view(),
name='event.settings.tickets.preview'),
url(r'^settings/email$', event.MailSettings.as_view(), name='event.settings.mail'),
url(r'^settings/email/preview$', event.MailSettingsPreview.as_view(), name='event.settings.mail.preview'),
url(r'^settings/email/layoutpreview$', event.MailSettingsRendererPreview.as_view(),
name='event.settings.mail.preview.layout'),
url(r'^settings/cancel', event.CancelSettings.as_view(), name='event.settings.cancel'),
url(r'^settings/invoice$', event.InvoiceSettings.as_view(), name='event.settings.invoice'),
url(r'^settings/invoice/preview$', event.InvoicePreview.as_view(), name='event.settings.invoice.preview'),
url(r'^settings/display', event.DisplaySettings.as_view(), name='event.settings.display'),
url(r'^settings/tax/$', event.TaxList.as_view(), name='event.settings.tax'),
url(r'^settings/tax/(?P<rule>\d+)/$', event.TaxUpdate.as_view(), name='event.settings.tax.edit'),
url(r'^settings/tax/add$', event.TaxCreate.as_view(), name='event.settings.tax.add'),
url(r'^settings/tax/(?P<rule>\d+)/delete$', event.TaxDelete.as_view(), name='event.settings.tax.delete'),
url(r'^settings/widget$', event.WidgetSettings.as_view(), name='event.settings.widget'),
url(r'^pdf/editor/webfonts.css', pdf.FontsCSSView.as_view(), name='pdf.css'),
url(r'^pdf/editor/(?P<filename>[^/]+).pdf$', pdf.PdfView.as_view(), name='pdf.background'),
url(r'^subevents/$', subevents.SubEventList.as_view(), name='event.subevents'),
url(r'^subevents/select2$', typeahead.subevent_select2, name='event.subevents.select2'),
url(r'^subevents/(?P<subevent>\d+)/$', subevents.SubEventUpdate.as_view(), name='event.subevent'),
url(r'^subevents/(?P<subevent>\d+)/delete$', subevents.SubEventDelete.as_view(),
name='event.subevent.delete'),
url(r'^subevents/add$', subevents.SubEventCreate.as_view(), name='event.subevents.add'),
url(r'^subevents/bulk_add$', subevents.SubEventBulkCreate.as_view(), name='event.subevents.bulk'),
url(r'^subevents/bulk_action$', subevents.SubEventBulkAction.as_view(), name='event.subevents.bulkaction'),
url(r'^subevents/bulk_edit$', subevents.SubEventBulkEdit.as_view(), name='event.subevents.bulkedit'),
url(r'^items/$', item.ItemList.as_view(), name='event.items'),
url(r'^items/add$', item.ItemCreate.as_view(), name='event.items.add'),
url(r'^items/(?P<item>\d+)/$', item.ItemUpdateGeneral.as_view(), name='event.item'),
url(r'^items/(?P<item>\d+)/up$', item.item_move_up, name='event.items.up'),
url(r'^items/(?P<item>\d+)/down$', item.item_move_down, name='event.items.down'),
url(r'^items/(?P<item>\d+)/delete$', item.ItemDelete.as_view(), name='event.items.delete'),
url(r'^items/typeahead/meta/$', typeahead.item_meta_values, name='event.items.meta.typeahead'),
url(r'^items/select2$', typeahead.items_select2, name='event.items.select2'),
url(r'^items/select2/variation$', typeahead.variations_select2, name='event.items.variations.select2'),
url(r'^categories/$', item.CategoryList.as_view(), name='event.items.categories'),
url(r'^categories/select2$', typeahead.category_select2, name='event.items.categories.select2'),
url(r'^categories/(?P<category>\d+)/delete$', item.CategoryDelete.as_view(),
name='event.items.categories.delete'),
url(r'^categories/(?P<category>\d+)/up$', item.category_move_up, name='event.items.categories.up'),
url(r'^categories/(?P<category>\d+)/down$', item.category_move_down,
name='event.items.categories.down'),
url(r'^categories/(?P<category>\d+)/$', item.CategoryUpdate.as_view(),
name='event.items.categories.edit'),
url(r'^categories/add$', item.CategoryCreate.as_view(), name='event.items.categories.add'),
url(r'^questions/$', item.QuestionList.as_view(), name='event.items.questions'),
url(r'^questions/reorder$', item.reorder_questions, name='event.items.questions.reorder'),
url(r'^questions/(?P<question>\d+)/delete$', item.QuestionDelete.as_view(),
name='event.items.questions.delete'),
url(r'^questions/(?P<question>\d+)/$', item.QuestionView.as_view(),
name='event.items.questions.show'),
url(r'^questions/(?P<question>\d+)/change$', item.QuestionUpdate.as_view(),
name='event.items.questions.edit'),
url(r'^questions/add$', item.QuestionCreate.as_view(), name='event.items.questions.add'),
url(r'^quotas/$', item.QuotaList.as_view(), name='event.items.quotas'),
url(r'^quotas/(?P<quota>\d+)/$', item.QuotaView.as_view(), name='event.items.quotas.show'),
url(r'^quotas/select$', typeahead.quotas_select2, name='event.items.quotas.select2'),
url(r'^quotas/(?P<quota>\d+)/change$', item.QuotaUpdate.as_view(), name='event.items.quotas.edit'),
url(r'^quotas/(?P<quota>\d+)/delete$', item.QuotaDelete.as_view(),
name='event.items.quotas.delete'),
url(r'^quotas/add$', item.QuotaCreate.as_view(), name='event.items.quotas.add'),
url(r'^vouchers/$', vouchers.VoucherList.as_view(), name='event.vouchers'),
url(r'^vouchers/tags/$', vouchers.VoucherTags.as_view(), name='event.vouchers.tags'),
url(r'^vouchers/rng$', vouchers.VoucherRNG.as_view(), name='event.vouchers.rng'),
url(r'^vouchers/item_select$', typeahead.itemvarquota_select2, name='event.vouchers.itemselect2'),
url(r'^vouchers/(?P<voucher>\d+)/$', vouchers.VoucherUpdate.as_view(), name='event.voucher'),
url(r'^vouchers/(?P<voucher>\d+)/delete$', vouchers.VoucherDelete.as_view(),
name='event.voucher.delete'),
url(r'^vouchers/add$', vouchers.VoucherCreate.as_view(), name='event.vouchers.add'),
url(r'^vouchers/go$', vouchers.VoucherGo.as_view(), name='event.vouchers.go'),
url(r'^vouchers/bulk_add$', vouchers.VoucherBulkCreate.as_view(), name='event.vouchers.bulk'),
url(r'^vouchers/bulk_action$', vouchers.VoucherBulkAction.as_view(), name='event.vouchers.bulkaction'),
url(r'^orders/(?P<code>[0-9A-Z]+)/transition$', orders.OrderTransition.as_view(),
name='event.order.transition'),
url(r'^orders/(?P<code>[0-9A-Z]+)/resend$', orders.OrderResendLink.as_view(),
name='event.order.resendlink'),
url(r'^orders/(?P<code>[0-9A-Z]+)/(?P<position>\d+)/resend$', orders.OrderResendLink.as_view(),
name='event.order.resendlink'),
url(r'^orders/(?P<code>[0-9A-Z]+)/invoice$', orders.OrderInvoiceCreate.as_view(),
name='event.order.geninvoice'),
url(r'^orders/(?P<code>[0-9A-Z]+)/invoices/(?P<id>\d+)/regenerate$', orders.OrderInvoiceRegenerate.as_view(),
name='event.order.regeninvoice'),
url(r'^orders/(?P<code>[0-9A-Z]+)/invoices/(?P<id>\d+)/reissue$', orders.OrderInvoiceReissue.as_view(),
name='event.order.reissueinvoice'),
url(r'^orders/(?P<code>[0-9A-Z]+)/download/(?P<position>\d+)/(?P<output>[^/]+)/$',
orders.OrderDownload.as_view(),
name='event.order.download.ticket'),
url(r'^orders/(?P<code>[0-9A-Z]+)/answer/(?P<answer>[^/]+)/$',
orders.AnswerDownload.as_view(),
name='event.order.download.answer'),
url(r'^orders/(?P<code>[0-9A-Z]+)/checkvatid', orders.OrderCheckVATID.as_view(),
name='event.order.checkvatid'),
url(r'^orders/(?P<code>[0-9A-Z]+)/extend$', orders.OrderExtend.as_view(),
name='event.order.extend'),
url(r'^orders/(?P<code>[0-9A-Z]+)/reactivate$', orders.OrderReactivate.as_view(),
name='event.order.reactivate'),
url(r'^orders/(?P<code>[0-9A-Z]+)/contact$', orders.OrderContactChange.as_view(),
name='event.order.contact'),
url(r'^orders/(?P<code>[0-9A-Z]+)/locale', orders.OrderLocaleChange.as_view(),
name='event.order.locale'),
url(r'^orders/(?P<code>[0-9A-Z]+)/comment$', orders.OrderComment.as_view(),
name='event.order.comment'),
url(r'^orders/(?P<code>[0-9A-Z]+)/change$', orders.OrderChange.as_view(),
name='event.order.change'),
url(r'^orders/(?P<code>[0-9A-Z]+)/approve', orders.OrderApprove.as_view(),
name='event.order.approve'),
url(r'^orders/(?P<code>[0-9A-Z]+)/deny$', orders.OrderDeny.as_view(),
name='event.order.deny'),
url(r'^orders/(?P<code>[0-9A-Z]+)/delete$', orders.OrderDelete.as_view(),
name='event.order.delete'),
url(r'^orders/(?P<code>[0-9A-Z]+)/info', orders.OrderModifyInformation.as_view(),
name='event.order.info'),
url(r'^orders/(?P<code>[0-9A-Z]+)/sendmail$', orders.OrderSendMail.as_view(),
name='event.order.sendmail'),
url(r'^orders/(?P<code>[0-9A-Z]+)/(?P<position>[0-9A-Z]+)/sendmail$', orders.OrderPositionSendMail.as_view(),
name='event.order.position.sendmail'),
url(r'^orders/(?P<code>[0-9A-Z]+)/mail_history$', orders.OrderEmailHistory.as_view(),
name='event.order.mail_history'),
url(r'^orders/(?P<code>[0-9A-Z]+)/payments/(?P<payment>\d+)/cancel$', orders.OrderPaymentCancel.as_view(),
name='event.order.payments.cancel'),
url(r'^orders/(?P<code>[0-9A-Z]+)/payments/(?P<payment>\d+)/confirm$', orders.OrderPaymentConfirm.as_view(),
name='event.order.payments.confirm'),
url(r'^orders/(?P<code>[0-9A-Z]+)/refund$', orders.OrderRefundView.as_view(),
name='event.order.refunds.start'),
url(r'^orders/(?P<code>[0-9A-Z]+)/refunds/(?P<refund>\d+)/cancel$', orders.OrderRefundCancel.as_view(),
name='event.order.refunds.cancel'),
url(r'^orders/(?P<code>[0-9A-Z]+)/refunds/(?P<refund>\d+)/process$', orders.OrderRefundProcess.as_view(),
name='event.order.refunds.process'),
url(r'^orders/(?P<code>[0-9A-Z]+)/refunds/(?P<refund>\d+)/done$', orders.OrderRefundDone.as_view(),
name='event.order.refunds.done'),
url(r'^orders/(?P<code>[0-9A-Z]+)/cancellationrequests/(?P<req>\d+)/delete$',
orders.OrderCancellationRequestDelete.as_view(),
name='event.order.cancellationrequests.delete'),
url(r'^orders/(?P<code>[0-9A-Z]+)/$', orders.OrderDetail.as_view(), name='event.order'),
url(r'^invoice/(?P<invoice>[^/]+)$', orders.InvoiceDownload.as_view(),
name='event.invoice.download'),
url(r'^orders/overview/$', orders.OverView.as_view(), name='event.orders.overview'),
url(r'^orders/import/$', orderimport.ImportView.as_view(), name='event.orders.import'),
url(r'^orders/import/(?P<file>[^/]+)/$', orderimport.ProcessView.as_view(), name='event.orders.import.process'),
url(r'^orders/export/$', orders.ExportView.as_view(), name='event.orders.export'),
url(r'^orders/export/do$', orders.ExportDoView.as_view(), name='event.orders.export.do'),
url(r'^orders/refunds/$', orders.RefundList.as_view(), name='event.orders.refunds'),
url(r'^orders/go$', orders.OrderGo.as_view(), name='event.orders.go'),
url(r'^orders/$', orders.OrderList.as_view(), name='event.orders'),
url(r'^orders/search$', orders.OrderSearch.as_view(), name='event.orders.search'),
url(r'^dangerzone/$', event.DangerZone.as_view(), name='event.dangerzone'),
url(r'^cancel/$', orders.EventCancel.as_view(), name='event.cancel'),
url(r'^shredder/$', shredder.StartShredView.as_view(), name='event.shredder.start'),
url(r'^shredder/export$', shredder.ShredExportView.as_view(), name='event.shredder.export'),
url(r'^shredder/download/(?P<file>[^/]+)/$', shredder.ShredDownloadView.as_view(), name='event.shredder.download'),
url(r'^shredder/shred', shredder.ShredDoView.as_view(), name='event.shredder.shred'),
url(r'^waitinglist/$', waitinglist.WaitingListView.as_view(), name='event.orders.waitinglist'),
url(r'^waitinglist/auto_assign$', waitinglist.AutoAssign.as_view(), name='event.orders.waitinglist.auto'),
url(r'^waitinglist/(?P<entry>\d+)/delete$', waitinglist.EntryDelete.as_view(),
name='event.orders.waitinglist.delete'),
url(r'^checkinlists/$', checkin.CheckinListList.as_view(), name='event.orders.checkinlists'),
url(r'^checkinlists/add$', checkin.CheckinListCreate.as_view(), name='event.orders.checkinlists.add'),
url(r'^checkinlists/select2$', typeahead.checkinlist_select2, name='event.orders.checkinlists.select2'),
url(r'^checkinlists/(?P<list>\d+)/$', checkin.CheckInListShow.as_view(), name='event.orders.checkinlists.show'),
url(r'^checkinlists/(?P<list>\d+)/change$', checkin.CheckinListUpdate.as_view(),
name='event.orders.checkinlists.edit'),
url(r'^checkinlists/(?P<list>\d+)/delete$', checkin.CheckinListDelete.as_view(),
name='event.orders.checkinlists.delete'),
])),
re_path(r'^event/(?P<organizer>[^/]+)/$', RedirectView.as_view(pattern_name='control:organizer'),
name='event.organizerredirect'),
url(r'^event/(?P<organizer>[^/]+)/$', RedirectView.as_view(pattern_name='control:organizer'), name='event.organizerredirect'),
]

View File

@@ -1,4 +1,4 @@
import collections.abc
import collections
import warnings
from django.core.paginator import (
@@ -65,7 +65,7 @@ class PaginationMixin:
return ctx
class LargeResultSetPage(collections.abc.Sequence):
class LargeResultSetPage(collections.Sequence):
def __init__(self, object_list, number, paginator):
self.object_list = object_list

View File

@@ -691,14 +691,14 @@ class MailSettingsRendererPreview(MailSettingsPreview):
expires=now(), code="PREVIEW", total=119)
item = request.event.items.create(name=gettext("Sample product"), default_price=42.23,
description=gettext("Sample product description"))
order.positions.create(item=item, attendee_name_parts={'_legacy': gettext("John Doe")},
price=item.default_price, subevent=request.event.subevents.last())
p = order.positions.create(item=item, attendee_name_parts={'_legacy': gettext("John Doe")},
price=item.default_price)
v = renderers[request.GET.get('renderer')].render(
v,
str(request.event.settings.mail_text_signature),
gettext('Your order: %(code)s') % {'code': order.code},
order,
position=None
position=p
)
r = HttpResponse(v, content_type='text/html')
r._csp_ignore = True

View File

@@ -151,11 +151,6 @@ class OrderList(OrderSearchMixin, EventPermissionRequiredMixin, PaginationMixin,
s = OrderPosition.objects.filter(
order=OuterRef('pk')
).order_by().values('order').annotate(k=Count('id')).values('k')
i = Invoice.objects.filter(
order=OuterRef('pk'),
is_cancellation=False,
refered__isnull=True,
).order_by().values('order').annotate(k=Count('id')).values('k')
annotated = {
o['pk']: o
for o in
@@ -163,11 +158,10 @@ class OrderList(OrderSearchMixin, EventPermissionRequiredMixin, PaginationMixin,
pk__in=[o.pk for o in ctx['orders']]
).annotate(
pcnt=Subquery(s, output_field=IntegerField()),
icnt=Subquery(i, output_field=IntegerField()),
has_cancellation_request=Exists(CancellationRequest.objects.filter(order=OuterRef('pk')))
).values(
'pk', 'pcnt', 'is_overpaid', 'is_underpaid', 'is_pending_with_full_payment', 'has_external_refund',
'has_pending_refund', 'has_cancellation_request', 'computed_payment_refund_sum', 'icnt'
'has_pending_refund', 'has_cancellation_request', 'computed_payment_refund_sum'
)
}
@@ -183,7 +177,6 @@ class OrderList(OrderSearchMixin, EventPermissionRequiredMixin, PaginationMixin,
o.has_pending_refund = annotated.get(o.pk)['has_pending_refund']
o.has_cancellation_request = annotated.get(o.pk)['has_cancellation_request']
o.computed_payment_refund_sum = annotated.get(o.pk)['computed_payment_refund_sum']
o.icnt = annotated.get(o.pk)['icnt']
o.sales_channel_obj = scs[o.sales_channel]
if ctx['page_obj'].paginator.count < 1000:
@@ -1141,7 +1134,6 @@ class OrderTransition(OrderView):
try:
cancel_order(self.order.pk, user=self.request.user,
send_mail=self.mark_canceled_form.cleaned_data['send_email'],
cancel_invoice=self.mark_canceled_form.cleaned_data.get('cancel_invoice', True),
cancellation_fee=self.mark_canceled_form.cleaned_data.get('cancellation_fee'))
except OrderError as e:
messages.error(self.request, str(e))

View File

@@ -938,7 +938,6 @@ class GiftCardListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixi
template_name = 'pretixcontrol/organizers/giftcards.html'
permission = 'can_manage_gift_cards'
context_object_name = 'giftcards'
paginate_by = 50
def get_queryset(self):
s = GiftCardTransaction.objects.filter(

View File

@@ -1184,7 +1184,7 @@ class SubEventBulkEdit(SubEventQueryMixin, EventPermissionRequiredMixin, FormVie
for fname in ('size', 'name', 'release_after_exit'):
setattr(q, fname, f.cleaned_data.get(fname))
q.save(clear_cache=False)
if 'itemvars' in f.changed_data:
if 'itemvar' in f.changed_data:
q.items.set(selected_items)
q.variations.set(selected_variations)
log_entries.append(

View File

@@ -255,7 +255,7 @@ def subevent_select2(request, **kwargs):
qs = request.event.subevents.filter(
qf
).order_by('-date_from', 'name', 'pk')
).order_by('-date_from')
total = qs.count()
pagesize = 20

View File

@@ -211,7 +211,7 @@ class WaitingListView(EventPermissionRequiredMixin, PaginationMixin, ListView):
writer = csv.writer(output, quoting=csv.QUOTE_NONNUMERIC, delimiter=",")
headers = [
_('Name'), _('E-mail address'), _('Phone number'), _('Product'), _('On list since'), _('Status'), _('Voucher code'),
_('E-mail address'), _('Product'), _('On list since'), _('Status'), _('Voucher code'),
_('Language'), _('Priority')
]
if self.request.event.has_subevents:
@@ -235,9 +235,7 @@ class WaitingListView(EventPermissionRequiredMixin, PaginationMixin, ListView):
status = _('Waiting')
row = [
w.name,
w.email,
w.phone,
prod,
w.created.isoformat(),
status,

View File

@@ -1,6 +1,5 @@
import copy
from django.core.files import File
from django.db import models
@@ -17,8 +16,7 @@ def modelcopy(obj: models.Model):
n = obj.__class__()
for f in obj._meta.fields:
val = getattr(obj, f.name)
print(f, f.name, val)
if isinstance(val, (models.Model, File)):
if isinstance(val, models.Model):
setattr(n, f.name, copy.copy(val))
else:
setattr(n, f.name, copy.deepcopy(val))

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-08 16:40+0000\n"
"POT-Creation-Date: 2021-02-26 09:06+0000\n"
"PO-Revision-Date: 2020-07-30 19:00+0000\n"
"Last-Translator: Abdullah <abdullah.gumaijan@gmail.com>\n"
"Language-Team: Arabic <https://translate.pretix.eu/projects/pretix/pretix-js/"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-08 16:40+0000\n"
"POT-Creation-Date: 2021-02-26 09:06+0000\n"
"PO-Revision-Date: 2020-12-19 07:00+0000\n"
"Last-Translator: albert <albert.serra.monner@gmail.com>\n"
"Language-Team: Catalan <https://translate.pretix.eu/projects/pretix/pretix-"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-08 16:40+0000\n"
"POT-Creation-Date: 2021-02-26 09:06+0000\n"
"PO-Revision-Date: 2020-12-14 10:00+0000\n"
"Last-Translator: Ondřej Sokol <osokol@treesoft.cz>\n"
"Language-Team: Czech <https://translate.pretix.eu/projects/pretix/pretix-js/"

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-08 16:40+0000\n"
"POT-Creation-Date: 2021-02-26 09:06+0000\n"
"PO-Revision-Date: 2020-09-15 02:00+0000\n"
"Last-Translator: Mie Frydensbjerg <mif@aarhus.dk>\n"
"Language-Team: Danish <https://translate.pretix.eu/projects/pretix/pretix-js/"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-08 16:40+0000\n"
"POT-Creation-Date: 2021-02-26 09:06+0000\n"
"PO-Revision-Date: 2020-08-25 02:00+0000\n"
"Last-Translator: Dennis Lichtenthäler <lichtenthaeler@rami.io>\n"
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix-js/"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-08 16:40+0000\n"
"POT-Creation-Date: 2021-02-26 09:06+0000\n"
"PO-Revision-Date: 2020-08-25 02:00+0000\n"
"Last-Translator: Dennis Lichtenthäler <lichtenthaeler@rami.io>\n"
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-08 16:40+0000\n"
"POT-Creation-Date: 2021-02-26 09:06+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-08 16:40+0000\n"
"POT-Creation-Date: 2021-02-26 09:06+0000\n"
"PO-Revision-Date: 2019-10-03 19:00+0000\n"
"Last-Translator: Chris Spy <chrispiropoulou@hotmail.com>\n"
"Language-Team: Greek <https://translate.pretix.eu/projects/pretix/pretix-js/"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-08 16:40+0000\n"
"POT-Creation-Date: 2021-02-26 09:06+0000\n"
"PO-Revision-Date: 2020-04-27 20:00+0000\n"
"Last-Translator: Gonzalo Gabriel Perez <zalitoar@gmail.com>\n"
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix-"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-08 16:40+0000\n"
"POT-Creation-Date: 2021-02-26 09:06+0000\n"
"PO-Revision-Date: 2021-01-20 16:10+0000\n"
"Last-Translator: Jaakko Rinta-Filppula <jaakko@r-f.fi>\n"
"Language-Team: Finnish <https://translate.pretix.eu/projects/pretix/pretix-"

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: French\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-08 16:40+0000\n"
"POT-Creation-Date: 2021-02-26 09:06+0000\n"
"PO-Revision-Date: 2020-09-15 17:00+0000\n"
"Last-Translator: Martin Gross <martin@pc-coholic.de>\n"
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix-js/"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-08 16:40+0000\n"
"POT-Creation-Date: 2021-02-26 09:06+0000\n"
"PO-Revision-Date: 2020-01-24 08:00+0000\n"
"Last-Translator: Prokaj Miklós <mixolid0@gmail.com>\n"
"Language-Team: Hungarian <https://translate.pretix.eu/projects/pretix/pretix-"

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-08 16:40+0000\n"
"POT-Creation-Date: 2021-02-26 09:06+0000\n"
"PO-Revision-Date: 2020-06-12 20:00+0000\n"
"Last-Translator: Frank <webappconcept@gmail.com>\n"
"Language-Team: Italian <https://translate.pretix.eu/projects/pretix/pretix-"

File diff suppressed because it is too large Load Diff

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