mirror of
https://github.com/pretix/pretix.git
synced 2026-05-03 14:54:04 +00:00
2013 lines
70 KiB
Python
2013 lines
70 KiB
Python
import json
|
|
from collections import OrderedDict
|
|
from datetime import datetime
|
|
from decimal import Decimal
|
|
from typing import Any
|
|
|
|
from django import forms
|
|
from django.conf import settings
|
|
from django.core.exceptions import ValidationError
|
|
from django.core.files import File
|
|
from django.core.validators import MaxValueValidator, MinValueValidator
|
|
from django.db.models import Model
|
|
from django.utils.translation import (
|
|
gettext_lazy as _, gettext_noop, pgettext, pgettext_lazy,
|
|
)
|
|
from hierarkey.models import GlobalSettingsBase, Hierarkey
|
|
from i18nfield.forms import I18nFormField, I18nTextarea, I18nTextInput
|
|
from i18nfield.strings import LazyI18nString
|
|
from rest_framework import serializers
|
|
|
|
from pretix.api.serializers.fields import ListMultipleChoiceField
|
|
from pretix.api.serializers.i18n import I18nField
|
|
from pretix.base.models.tax import TaxRule
|
|
from pretix.base.reldate import (
|
|
RelativeDateField, RelativeDateTimeField, RelativeDateWrapper,
|
|
SerializerRelativeDateField, SerializerRelativeDateTimeField,
|
|
)
|
|
from pretix.control.forms import MultipleLanguagesWidget, SingleLanguageWidget
|
|
from pretix.helpers.countries import CachedCountries
|
|
|
|
|
|
def country_choice_kwargs():
|
|
allcountries = list(CachedCountries())
|
|
allcountries.insert(0, ('', _('Select country')))
|
|
return {
|
|
'choices': allcountries
|
|
}
|
|
|
|
|
|
DEFAULTS = {
|
|
'max_items_per_order': {
|
|
'default': '10',
|
|
'type': int,
|
|
'form_class': forms.IntegerField,
|
|
'serializer_class': serializers.IntegerField,
|
|
'form_kwargs': dict(
|
|
min_value=1,
|
|
label=_("Maximum number of items per order"),
|
|
help_text=_("Add-on products will not be counted.")
|
|
)
|
|
},
|
|
'display_net_prices': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Show net prices instead of gross prices in the product list (not recommended!)"),
|
|
help_text=_("Independent of your choice, the cart will show gross prices as this is the price that needs to be "
|
|
"paid."),
|
|
|
|
)
|
|
},
|
|
'system_question_order': {
|
|
'default': {},
|
|
'type': dict,
|
|
},
|
|
'attendee_names_asked': {
|
|
'default': 'True',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Ask for attendee names"),
|
|
help_text=_("Ask for a name for all tickets which include admission to the event."),
|
|
)
|
|
},
|
|
'attendee_names_required': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Require attendee names"),
|
|
help_text=_("Require customers to fill in the names of all attendees."),
|
|
widget=forms.CheckboxInput(attrs={'data-checkbox-dependency': '#id_settings-attendee_names_asked'}),
|
|
)
|
|
},
|
|
'attendee_emails_asked': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Ask for email addresses per ticket"),
|
|
help_text=_("Normally, pretix asks for one email address per order and the order confirmation will be sent "
|
|
"only to that email address. If you enable this option, the system will additionally ask for "
|
|
"individual email addresses for every admission ticket. This might be useful if you want to "
|
|
"obtain individual addresses for every attendee even in case of group orders. However, "
|
|
"pretix will send the order confirmation by default only to the one primary email address, not to "
|
|
"the per-attendee addresses. You can however enable this in the E-mail settings."),
|
|
)
|
|
},
|
|
'attendee_emails_required': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Require email addresses per ticket"),
|
|
help_text=_("Require customers to fill in individual e-mail addresses for all admission tickets. See the "
|
|
"above option for more details. One email address for the order confirmation will always be "
|
|
"required regardless of this setting."),
|
|
widget=forms.CheckboxInput(attrs={'data-checkbox-dependency': '#id_settings-attendee_emails_asked'}),
|
|
)
|
|
},
|
|
'attendee_company_asked': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Ask for company per ticket"),
|
|
)
|
|
},
|
|
'attendee_company_required': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Require company per ticket"),
|
|
widget=forms.CheckboxInput(attrs={'data-checkbox-dependency': '#id_settings-attendee_company_asked'}),
|
|
)
|
|
},
|
|
'attendee_addresses_asked': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Ask for postal addresses per ticket"),
|
|
)
|
|
},
|
|
'attendee_addresses_required': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Require postal addresses per ticket"),
|
|
widget=forms.CheckboxInput(attrs={'data-checkbox-dependency': '#id_settings-attendee_addresses_asked'}),
|
|
)
|
|
},
|
|
'order_email_asked_twice': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Ask for the order email address twice"),
|
|
help_text=_("Require customers to fill in the primary email address twice to avoid errors."),
|
|
)
|
|
},
|
|
'invoice_address_asked': {
|
|
'default': 'True',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Ask for invoice address"),
|
|
)
|
|
},
|
|
'invoice_address_not_asked_free': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_('Do not ask for invoice address if an order is free'),
|
|
)
|
|
},
|
|
'invoice_name_required': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Require customer name"),
|
|
)
|
|
},
|
|
'invoice_attendee_name': {
|
|
'default': 'True',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Show attendee names on invoices"),
|
|
)
|
|
},
|
|
'invoice_eu_currencies': {
|
|
'default': 'True',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("On invoices from one EU country into another EU country with a different currency, print the "
|
|
"tax amounts in both currencies if possible"),
|
|
)
|
|
},
|
|
'invoice_address_required': {
|
|
'default': 'False',
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'type': bool,
|
|
'form_kwargs': dict(
|
|
label=_("Require invoice address"),
|
|
widget=forms.CheckboxInput(attrs={'data-checkbox-dependency': '#id_invoice_address_asked'}),
|
|
)
|
|
},
|
|
'invoice_address_company_required': {
|
|
'default': 'False',
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'type': bool,
|
|
'form_kwargs': dict(
|
|
label=_("Require a business addresses"),
|
|
help_text=_('This will require users to enter a company name.'),
|
|
widget=forms.CheckboxInput(attrs={'data-checkbox-dependency': '#id_invoice_address_required'}),
|
|
)
|
|
},
|
|
'invoice_address_beneficiary': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Ask for beneficiary"),
|
|
widget=forms.CheckboxInput(attrs={'data-checkbox-dependency': '#id_invoice_address_asked'}),
|
|
required=False
|
|
)
|
|
},
|
|
'invoice_address_custom_field': {
|
|
'default': '',
|
|
'type': LazyI18nString,
|
|
'form_class': I18nFormField,
|
|
'serializer_class': I18nField,
|
|
'form_kwargs': dict(
|
|
label=_("Custom address field"),
|
|
widget=I18nTextInput,
|
|
help_text=_("If you want to add a custom text field, e.g. for a country-specific registration number, to "
|
|
"your invoice address form, please fill in the label here. This label will both be used for "
|
|
"asking the user to input their details as well as for displaying the value on the invoice. "
|
|
"The field will not be required.")
|
|
)
|
|
},
|
|
'invoice_address_vatid': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Ask for VAT ID"),
|
|
help_text=_("Does only work if an invoice address is asked for. VAT ID is not required."),
|
|
widget=forms.CheckboxInput(attrs={'data-checkbox-dependency': '#id_invoice_address_asked'}),
|
|
)
|
|
},
|
|
'invoice_address_explanation_text': {
|
|
'default': '',
|
|
'type': LazyI18nString,
|
|
'form_class': I18nFormField,
|
|
'serializer_class': I18nField,
|
|
'form_kwargs': dict(
|
|
label=_("Invoice address explanation"),
|
|
widget=I18nTextarea,
|
|
widget_kwargs={'attrs': {'rows': '2'}},
|
|
help_text=_("This text will be shown above the invoice address form during checkout.")
|
|
)
|
|
},
|
|
'invoice_show_payments': {
|
|
'default': 'True',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Show paid amount on partially paid invoices"),
|
|
help_text=_("If an invoice has already been paid partially, this option will add the paid and pending "
|
|
"amount to the invoice."),
|
|
)
|
|
},
|
|
'invoice_include_free': {
|
|
'default': 'True',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Show free products on invoices"),
|
|
help_text=_("Note that invoices will never be generated for orders that contain only free "
|
|
"products."),
|
|
)
|
|
},
|
|
'invoice_include_expire_date': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Show expiration date of order"),
|
|
help_text=_("The expiration date will not be shown if the invoice is generated after the order is paid."),
|
|
)
|
|
},
|
|
'invoice_numbers_consecutive': {
|
|
'default': 'True',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Generate invoices with consecutive numbers"),
|
|
help_text=_("If deactivated, the order code will be used in the invoice number."),
|
|
)
|
|
},
|
|
'invoice_numbers_prefix': {
|
|
'default': '',
|
|
'type': str,
|
|
'form_class': forms.CharField,
|
|
'serializer_class': serializers.CharField,
|
|
'form_kwargs': dict(
|
|
label=_("Invoice number prefix"),
|
|
help_text=_("This will be prepended to invoice numbers. If you leave this field empty, your event slug will "
|
|
"be used followed by a dash. Attention: If multiple events within the same organization use the "
|
|
"same value in this field, they will share their number range, i.e. every full number will be "
|
|
"used at most once over all of your events. This setting only affects future invoices. You can "
|
|
"use %Y (with century) %y (without century) to insert the year of the invoice, or %m and %d for "
|
|
"the day of month."),
|
|
)
|
|
},
|
|
'invoice_numbers_prefix_cancellations': {
|
|
'default': '',
|
|
'type': str,
|
|
'form_class': forms.CharField,
|
|
'serializer_class': serializers.CharField,
|
|
'form_kwargs': dict(
|
|
label=_("Invoice number prefix for cancellations"),
|
|
help_text=_("This will be prepended to invoice numbers of cancellations. If you leave this field empty, "
|
|
"the same numbering scheme will be used that you configured for regular invoices."),
|
|
)
|
|
},
|
|
'invoice_renderer': {
|
|
'default': 'classic',
|
|
'type': str,
|
|
},
|
|
'reservation_time': {
|
|
'default': '30',
|
|
'type': int,
|
|
'form_class': forms.IntegerField,
|
|
'serializer_class': serializers.IntegerField,
|
|
'form_kwags': dict(
|
|
min_value=0,
|
|
label=_("Reservation period"),
|
|
help_text=_("The number of minutes the items in a user's cart are reserved for this user."),
|
|
)
|
|
},
|
|
'redirect_to_checkout_directly': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_class': forms.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_('Directly redirect to check-out after a product has been added to the cart.'),
|
|
)
|
|
},
|
|
'presale_has_ended_text': {
|
|
'default': '',
|
|
'type': LazyI18nString,
|
|
'form_class': I18nFormField,
|
|
'serializer_class': I18nField,
|
|
'form_kwargs': dict(
|
|
label=_("End of presale text"),
|
|
widget=I18nTextarea,
|
|
widget_kwargs={'attrs': {'rows': '2'}},
|
|
help_text=_("This text will be shown above the ticket shop once the designated sales timeframe for this event "
|
|
"is over. You can use it to describe other options to get a ticket, such as a box office.")
|
|
)
|
|
},
|
|
'payment_explanation': {
|
|
'default': '',
|
|
'type': LazyI18nString,
|
|
'form_class': I18nFormField,
|
|
'serializer_class': I18nField,
|
|
'form_kwargs': dict(
|
|
widget=I18nTextarea,
|
|
widget_kwargs={'attrs': {
|
|
'rows': 3,
|
|
}},
|
|
required=False,
|
|
label=_("Guidance text"),
|
|
help_text=_("This text will be shown above the payment options. You can explain the choices to the user here, "
|
|
"if you want.")
|
|
)
|
|
},
|
|
'payment_term_days': {
|
|
'default': '14',
|
|
'type': int,
|
|
'form_class': forms.IntegerField,
|
|
'serializer_class': serializers.IntegerField,
|
|
'form_kwargs': dict(
|
|
label=_('Payment term in days'),
|
|
help_text=_("The number of days after placing an order the user has to pay to preserve their reservation. If "
|
|
"you use slow payment methods like bank transfer, we recommend 14 days. If you only use real-time "
|
|
"payment methods, we recommend still setting two or three days to allow people to retry failed "
|
|
"payments."),
|
|
required=True,
|
|
validators=[MinValueValidator(0),
|
|
MaxValueValidator(1000000)]
|
|
),
|
|
'serializer_kwargs': dict(
|
|
validators=[MinValueValidator(0),
|
|
MaxValueValidator(1000000)]
|
|
)
|
|
},
|
|
'payment_term_last': {
|
|
'default': None,
|
|
'type': RelativeDateWrapper,
|
|
'form_class': RelativeDateField,
|
|
'serializer_class': SerializerRelativeDateField,
|
|
'form_kwargs': dict(
|
|
label=_('Last date of payments'),
|
|
help_text=_("The last date any payments are accepted. This has precedence over the number of "
|
|
"days configured above. If you use the event series feature and an order contains tickets for "
|
|
"multiple dates, the earliest date will be used."),
|
|
)
|
|
},
|
|
'payment_term_weekdays': {
|
|
'default': 'True',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_('Only end payment terms on weekdays'),
|
|
help_text=_("If this is activated and the payment term of any order ends on a Saturday or Sunday, it will be "
|
|
"moved to the next Monday instead. This is required in some countries by civil law. This will "
|
|
"not effect the last date of payments configured above."),
|
|
)
|
|
},
|
|
'payment_term_expire_automatically': {
|
|
'default': 'True',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_('Automatically expire unpaid orders'),
|
|
help_text=_("If checked, all unpaid orders will automatically go from 'pending' to 'expired' "
|
|
"after the end of their payment deadline. This means that those tickets go back to "
|
|
"the pool and can be ordered by other people."),
|
|
)
|
|
},
|
|
'payment_giftcard__enabled': {
|
|
'default': 'True',
|
|
'type': bool
|
|
},
|
|
'payment_resellers__restrict_to_sales_channels': {
|
|
'default': ['resellers'],
|
|
'type': list
|
|
},
|
|
'payment_term_accept_late': {
|
|
'default': 'True',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_('Accept late payments'),
|
|
help_text=_("Accept payments for orders even when they are in 'expired' state as long as enough "
|
|
"capacity is available. No payments will ever be accepted after the 'Last date of payments' "
|
|
"configured above."),
|
|
)
|
|
},
|
|
'presale_start_show_date': {
|
|
'default': 'True',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Show start date"),
|
|
help_text=_("Show the presale start date before presale has started."),
|
|
widget=forms.CheckboxInput,
|
|
)
|
|
},
|
|
'tax_rate_default': {
|
|
'default': None,
|
|
'type': TaxRule
|
|
},
|
|
'invoice_generate': {
|
|
'default': 'False',
|
|
'type': str,
|
|
'form_class': forms.ChoiceField,
|
|
'serializer_class': serializers.ChoiceField,
|
|
'serializer_kwargs': dict(
|
|
choices=(
|
|
('False', _('Do not generate invoices')),
|
|
('admin', _('Only manually in admin panel')),
|
|
('user', _('Automatically on user request')),
|
|
('True', _('Automatically for all created orders')),
|
|
('paid', _('Automatically on payment')),
|
|
),
|
|
),
|
|
'form_kwargs': dict(
|
|
label=_("Generate invoices"),
|
|
widget=forms.RadioSelect,
|
|
choices=(
|
|
('False', _('Do not generate invoices')),
|
|
('admin', _('Only manually in admin panel')),
|
|
('user', _('Automatically on user request')),
|
|
('True', _('Automatically for all created orders')),
|
|
('paid', _('Automatically on payment')),
|
|
),
|
|
help_text=_("Invoices will never be automatically generated for free orders.")
|
|
)
|
|
},
|
|
'invoice_reissue_after_modify': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Automatically cancel and reissue invoice on address changes"),
|
|
help_text=_("If customers change their invoice address on an existing order, the invoice will "
|
|
"automatically be canceled and a new invoice will be issued. This setting does not affect "
|
|
"changes made through the backend."),
|
|
)
|
|
},
|
|
'invoice_generate_sales_channels': {
|
|
'default': json.dumps(['web']),
|
|
'type': list
|
|
},
|
|
'invoice_address_from': {
|
|
'default': '',
|
|
'type': str,
|
|
'form_class': forms.CharField,
|
|
'serializer_class': serializers.CharField,
|
|
'form_kwargs': dict(
|
|
label=_("Address line"),
|
|
widget=forms.Textarea(attrs={
|
|
'rows': 2,
|
|
'placeholder': _(
|
|
'Albert Einstein Road 52'
|
|
)
|
|
}),
|
|
)
|
|
},
|
|
'invoice_address_from_name': {
|
|
'default': '',
|
|
'type': str,
|
|
'form_class': forms.CharField,
|
|
'serializer_class': serializers.CharField,
|
|
'form_kwargs': dict(
|
|
label=_("Company name"),
|
|
)
|
|
},
|
|
'invoice_address_from_zipcode': {
|
|
'default': '',
|
|
'type': str,
|
|
'form_class': forms.CharField,
|
|
'serializer_class': serializers.CharField,
|
|
'form_kwargs': dict(
|
|
widget=forms.TextInput(attrs={
|
|
'placeholder': '12345'
|
|
}),
|
|
label=_("ZIP code"),
|
|
)
|
|
},
|
|
'invoice_address_from_city': {
|
|
'default': '',
|
|
'type': str,
|
|
'form_class': forms.CharField,
|
|
'serializer_class': serializers.CharField,
|
|
'form_kwargs': dict(
|
|
widget=forms.TextInput(attrs={
|
|
'placeholder': _('Random City')
|
|
}),
|
|
label=_("City"),
|
|
)
|
|
},
|
|
'invoice_address_from_country': {
|
|
'default': '',
|
|
'type': str,
|
|
'form_class': forms.ChoiceField,
|
|
'serializer_class': serializers.ChoiceField,
|
|
'serializer_kwargs': country_choice_kwargs,
|
|
'form_kwargs': country_choice_kwargs,
|
|
},
|
|
'invoice_address_from_tax_id': {
|
|
'default': '',
|
|
'type': str,
|
|
'form_class': forms.CharField,
|
|
'serializer_class': serializers.CharField,
|
|
'form_kwargs': dict(
|
|
label=_("Domestic tax ID"),
|
|
help_text=_("e.g. tax number in Germany, ABN in Australia, …")
|
|
)
|
|
},
|
|
'invoice_address_from_vat_id': {
|
|
'default': '',
|
|
'type': str,
|
|
'form_class': forms.CharField,
|
|
'serializer_class': serializers.CharField,
|
|
'form_kwargs': dict(
|
|
label=_("EU VAT ID"),
|
|
)
|
|
},
|
|
'invoice_introductory_text': {
|
|
'default': '',
|
|
'type': LazyI18nString,
|
|
'form_class': I18nFormField,
|
|
'serializer_class': I18nField,
|
|
'form_kwargs': dict(
|
|
widget=I18nTextarea,
|
|
widget_kwargs={'attrs': {
|
|
'rows': 3,
|
|
'placeholder': _(
|
|
'e.g. With this document, we sent you the invoice for your ticket order.'
|
|
)
|
|
}},
|
|
label=_("Introductory text"),
|
|
help_text=_("Will be printed on every invoice above the invoice rows.")
|
|
)
|
|
},
|
|
'invoice_additional_text': {
|
|
'default': '',
|
|
'type': LazyI18nString,
|
|
'form_class': I18nFormField,
|
|
'serializer_class': I18nField,
|
|
'form_kwargs': dict(
|
|
widget=I18nTextarea,
|
|
widget_kwargs={'attrs': {
|
|
'rows': 3,
|
|
'placeholder': _(
|
|
'e.g. Thank you for your purchase! You can find more information on the event at ...'
|
|
)
|
|
}},
|
|
label=_("Additional text"),
|
|
help_text=_("Will be printed on every invoice below the invoice total.")
|
|
)
|
|
},
|
|
'invoice_footer_text': {
|
|
'default': '',
|
|
'type': LazyI18nString,
|
|
'form_class': I18nFormField,
|
|
'serializer_class': I18nField,
|
|
'form_kwargs': dict(
|
|
widget=I18nTextarea,
|
|
widget_kwargs={'attrs': {
|
|
'rows': 5,
|
|
'placeholder': _(
|
|
'e.g. your bank details, legal details like your VAT ID, registration numbers, etc.'
|
|
)
|
|
}},
|
|
label=_("Footer"),
|
|
help_text=_("Will be printed centered and in a smaller font at the end of every invoice page.")
|
|
)
|
|
},
|
|
'invoice_language': {
|
|
'default': '__user__',
|
|
'type': str
|
|
},
|
|
'invoice_email_attachment': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Attach invoices to emails"),
|
|
help_text=_("If invoices are automatically generated for all orders, they will be attached to the order "
|
|
"confirmation mail. If they are automatically generated on payment, they will be attached to the "
|
|
"payment confirmation mail. If they are not automatically generated, they will not be attached "
|
|
"to emails."),
|
|
)
|
|
},
|
|
'show_items_outside_presale_period': {
|
|
'default': 'True',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Show items outside presale period"),
|
|
help_text=_("Show item details before presale has started and after presale has ended"),
|
|
)
|
|
},
|
|
'timezone': {
|
|
'default': settings.TIME_ZONE,
|
|
'type': str
|
|
},
|
|
'locales': {
|
|
'default': json.dumps([settings.LANGUAGE_CODE]),
|
|
'type': list,
|
|
'serializer_class': ListMultipleChoiceField,
|
|
'serializer_kwargs': dict(
|
|
choices=settings.LANGUAGES,
|
|
required=True,
|
|
),
|
|
'form_class': forms.MultipleChoiceField,
|
|
'form_kwargs': dict(
|
|
choices=settings.LANGUAGES,
|
|
widget=MultipleLanguagesWidget,
|
|
required=True,
|
|
label=_("Available languages"),
|
|
)
|
|
},
|
|
'locale': {
|
|
'default': settings.LANGUAGE_CODE,
|
|
'type': str,
|
|
'serializer_class': serializers.ChoiceField,
|
|
'serializer_kwargs': dict(
|
|
choices=settings.LANGUAGES,
|
|
required=True,
|
|
),
|
|
'form_class': forms.ChoiceField,
|
|
'form_kwargs': dict(
|
|
choices=settings.LANGUAGES,
|
|
widget=SingleLanguageWidget,
|
|
required=True,
|
|
label=_("Default language"),
|
|
)
|
|
},
|
|
'show_dates_on_frontpage': {
|
|
'default': 'True',
|
|
'type': bool,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_class': forms.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Show event times and dates on the ticket shop"),
|
|
help_text=_("If disabled, no date or time will be shown on the ticket shop's front page. This settings "
|
|
"does however not affect the display in other locations."),
|
|
)
|
|
},
|
|
'show_date_to': {
|
|
'default': 'True',
|
|
'type': bool,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_class': forms.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Show event end date"),
|
|
help_text=_("If disabled, only event's start date will be displayed to the public."),
|
|
)
|
|
},
|
|
'show_times': {
|
|
'default': 'True',
|
|
'type': bool,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_class': forms.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Show dates with time"),
|
|
help_text=_("If disabled, the event's start and end date will be displayed without the time of day."),
|
|
)
|
|
},
|
|
'hide_sold_out': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_class': forms.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Hide all products that are sold out"),
|
|
)
|
|
},
|
|
'show_quota_left': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_class': forms.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Show number of tickets left"),
|
|
help_text=_("Publicly show how many tickets of a certain type are still available."),
|
|
)
|
|
},
|
|
'meta_noindex': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_class': forms.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_('Ask search engines not to index the ticket shop'),
|
|
)
|
|
},
|
|
'show_variations_expanded': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_class': forms.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Show variations of a product expanded by default"),
|
|
)
|
|
},
|
|
'waiting_list_enabled': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_class': forms.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Enable waiting list"),
|
|
help_text=_("Once a ticket is sold out, people can add themselves to a waiting list. As soon as a ticket "
|
|
"becomes available again, it will be reserved for the first person on the waiting list and this "
|
|
"person will receive an email notification with a voucher that can be used to buy a ticket."),
|
|
)
|
|
},
|
|
'waiting_list_auto': {
|
|
'default': 'True',
|
|
'type': bool,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_class': forms.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Automatic waiting list assignments"),
|
|
help_text=_("If ticket capacity becomes free, automatically create a voucher and send it to the first person "
|
|
"on the waiting list for that product. If this is not active, mails will not be send automatically "
|
|
"but you can send them manually via the control panel. If you disable the waiting list but keep "
|
|
"this option enabled, tickets will still be sent out."),
|
|
widget=forms.CheckboxInput(),
|
|
)
|
|
},
|
|
'waiting_list_hours': {
|
|
'default': '48',
|
|
'type': int,
|
|
'serializer_class': serializers.IntegerField,
|
|
'form_class': forms.IntegerField,
|
|
'form_kwargs': dict(
|
|
label=_("Waiting list response time"),
|
|
min_value=1,
|
|
help_text=_("If a ticket voucher is sent to a person on the waiting list, it has to be redeemed within this "
|
|
"number of hours until it expires and can be re-assigned to the next person on the list."),
|
|
widget=forms.NumberInput(),
|
|
)
|
|
},
|
|
'ticket_download': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_class': forms.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Allow users to download tickets"),
|
|
help_text=_("If this is off, nobody can download a ticket."),
|
|
)
|
|
},
|
|
'ticket_download_date': {
|
|
'default': None,
|
|
'type': RelativeDateWrapper,
|
|
'form_class': RelativeDateTimeField,
|
|
'serializer_class': SerializerRelativeDateTimeField,
|
|
'form_kwargs': dict(
|
|
label=_("Download date"),
|
|
help_text=_("Ticket download will be offered after this date. If you use the event series feature and an order "
|
|
"contains tickets for multiple event dates, download of all tickets will be available if at least "
|
|
"one of the event dates allows it."),
|
|
)
|
|
},
|
|
'ticket_download_addons': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_class': forms.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Generate tickets for add-on products"),
|
|
help_text=_('By default, tickets are only issued for products selected individually, not for add-on '
|
|
'products. With this option, a separate ticket is issued for every add-on product as well.'),
|
|
widget=forms.CheckboxInput(attrs={'data-checkbox-dependency': '#id_ticket_download',
|
|
'data-checkbox-dependency-visual': 'on'}),
|
|
)
|
|
},
|
|
'ticket_download_nonadm': {
|
|
'default': 'True',
|
|
'type': bool,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_class': forms.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Generate tickets for all products"),
|
|
help_text=_('If turned off, tickets are only issued for products that are marked as an "admission ticket"'
|
|
'in the product settings. You can also turn off ticket issuing in every product separately.'),
|
|
widget=forms.CheckboxInput(attrs={'data-checkbox-dependency': '#id_ticket_download',
|
|
'data-checkbox-dependency-visual': 'on'}),
|
|
)
|
|
},
|
|
'ticket_download_pending': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_class': forms.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Generate tickets for pending orders"),
|
|
help_text=_('If turned off, ticket downloads are only possible after an order has been marked as paid.'),
|
|
widget=forms.CheckboxInput(attrs={'data-checkbox-dependency': '#id_ticket_download',
|
|
'data-checkbox-dependency-visual': 'on'}),
|
|
)
|
|
},
|
|
'event_list_availability': {
|
|
'default': 'True',
|
|
'type': bool
|
|
},
|
|
'event_list_type': {
|
|
'default': 'list',
|
|
'type': str,
|
|
'form_class': forms.ChoiceField,
|
|
'serializer_class': serializers.ChoiceField,
|
|
'serializer_kwargs': dict(
|
|
choices=(
|
|
('list', _('List')),
|
|
('week', _('Week calendar')),
|
|
('calendar', _('Month calendar')),
|
|
)
|
|
),
|
|
'form_kwargs': dict(
|
|
label=_('Default overview style'),
|
|
choices=(
|
|
('list', _('List')),
|
|
('week', _('Week calendar')),
|
|
('calendar', _('Month calendar')),
|
|
)
|
|
),
|
|
},
|
|
'last_order_modification_date': {
|
|
'default': None,
|
|
'type': RelativeDateWrapper,
|
|
'form_class': RelativeDateTimeField,
|
|
'serializer_class': SerializerRelativeDateTimeField,
|
|
'form_kwargs': dict(
|
|
label=_('Last date of modifications'),
|
|
help_text=_("The last date users can modify details of their orders, such as attendee names or "
|
|
"answers to questions. If you use the event series feature and an order contains tickets for "
|
|
"multiple event dates, the earliest date will be used."),
|
|
)
|
|
},
|
|
'change_allow_user_variation': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Customers can change the variation of the products they purchased"),
|
|
)
|
|
},
|
|
'change_allow_user_price': {
|
|
'default': 'gt',
|
|
'type': str,
|
|
'form_class': forms.ChoiceField,
|
|
'serializer_class': serializers.ChoiceField,
|
|
'serializer_kwargs': dict(
|
|
choices=(
|
|
('gt', _('Only allow changes if the resulting price is higher or equal than the previous price.')),
|
|
('eq', _('Only allow changes if the resulting price is equal to the previous price.')),
|
|
('any', _('Allow changes regardless of price, even if this results in a refund.')),
|
|
)
|
|
),
|
|
'form_kwargs': dict(
|
|
label=_("Requirement for changed prices"),
|
|
choices=(
|
|
('gt', _('Only allow changes if the resulting price is higher or equal than the previous price.')),
|
|
('eq', _('Only allow changes if the resulting price is equal to the previous price.')),
|
|
('any', _('Allow changes regardless of price, even if this results in a refund.')),
|
|
),
|
|
widget=forms.RadioSelect,
|
|
),
|
|
},
|
|
'change_allow_user_until': {
|
|
'default': None,
|
|
'type': RelativeDateWrapper,
|
|
'form_class': RelativeDateTimeField,
|
|
'serializer_class': SerializerRelativeDateTimeField,
|
|
'form_kwargs': dict(
|
|
label=_("Do not allow changes after"),
|
|
)
|
|
},
|
|
'cancel_allow_user': {
|
|
'default': 'True',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Customers can cancel their unpaid orders"),
|
|
)
|
|
},
|
|
'cancel_allow_user_until': {
|
|
'default': None,
|
|
'type': RelativeDateWrapper,
|
|
'form_class': RelativeDateTimeField,
|
|
'serializer_class': SerializerRelativeDateTimeField,
|
|
'form_kwargs': dict(
|
|
label=_("Do not allow cancellations after"),
|
|
)
|
|
},
|
|
'cancel_allow_user_paid': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Customers can cancel their paid orders"),
|
|
help_text=_("Paid money will be automatically paid back if the payment method allows it. "
|
|
"Otherwise, a manual refund will be created for you to process manually."),
|
|
)
|
|
},
|
|
'cancel_allow_user_paid_keep': {
|
|
'default': '0.00',
|
|
'type': Decimal,
|
|
'form_class': forms.DecimalField,
|
|
'serializer_class': serializers.DecimalField,
|
|
'serializer_kwargs': dict(
|
|
max_digits=10, decimal_places=2
|
|
),
|
|
'form_kwargs': dict(
|
|
label=_("Keep a fixed cancellation fee"),
|
|
)
|
|
},
|
|
'cancel_allow_user_paid_keep_fees': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Keep payment, shipping and service fees"),
|
|
)
|
|
},
|
|
'cancel_allow_user_paid_keep_percentage': {
|
|
'default': '0.00',
|
|
'type': Decimal,
|
|
'form_class': forms.DecimalField,
|
|
'serializer_class': serializers.DecimalField,
|
|
'serializer_kwargs': dict(
|
|
max_digits=10, decimal_places=2
|
|
),
|
|
'form_kwargs': dict(
|
|
label=_("Keep a percentual cancellation fee"),
|
|
)
|
|
},
|
|
'cancel_allow_user_paid_adjust_fees': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Allow customers to voluntarily choose a lower refund"),
|
|
help_text=_("With this option enabled, your customers can choose to get a smaller refund to support you.")
|
|
)
|
|
},
|
|
'cancel_allow_user_paid_adjust_fees_explanation': {
|
|
'default': LazyI18nString.from_gettext(gettext_noop(
|
|
'However, if you want us to help keep the lights on here, please consider using the slider below to '
|
|
'request a smaller refund. Thank you!'
|
|
)),
|
|
'type': LazyI18nString,
|
|
'serializer_class': I18nField,
|
|
'form_class': I18nFormField,
|
|
'form_kwargs': dict(
|
|
label=_("Voluntary lower refund explanation"),
|
|
widget=I18nTextarea,
|
|
widget_kwargs={'attrs': {'rows': '2'}},
|
|
help_text=_("This text will be shown in between the explanation of how the refunds work and the slider "
|
|
"which your customers can use to choose the amount they would like to receive. You can use it "
|
|
"e.g. to explain choosing a lower refund will help your organization.")
|
|
)
|
|
},
|
|
'cancel_allow_user_paid_require_approval': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Customers can only request a cancellation that needs to be approved by the event organizer "
|
|
"before the order is canceled and a refund is issued."),
|
|
)
|
|
},
|
|
'cancel_allow_user_paid_refund_as_giftcard': {
|
|
'default': 'off',
|
|
'type': str,
|
|
'serializer_class': serializers.ChoiceField,
|
|
'serializer_kwargs': dict(
|
|
choices=[
|
|
('off', _('All refunds are issued to the original payment method')),
|
|
('option', _('Customers can choose between a gift card and a refund to their payment method')),
|
|
('force', _('All refunds are issued as gift cards')),
|
|
],
|
|
),
|
|
'form_class': forms.ChoiceField,
|
|
'form_kwargs': dict(
|
|
label=_('Refund method'),
|
|
choices=[
|
|
('off', _('All refunds are issued to the original payment method')),
|
|
('option', _('Customers can choose between a gift card and a refund to their payment method')),
|
|
('force', _('All refunds are issued as gift cards')),
|
|
],
|
|
widget=forms.RadioSelect,
|
|
# When adding a new ordering, remember to also define it in the event model
|
|
)
|
|
},
|
|
'cancel_allow_user_paid_until': {
|
|
'default': None,
|
|
'type': RelativeDateWrapper,
|
|
'form_class': RelativeDateTimeField,
|
|
'serializer_class': SerializerRelativeDateTimeField,
|
|
'form_kwargs': dict(
|
|
label=_("Do not allow cancellations after"),
|
|
)
|
|
},
|
|
'contact_mail': {
|
|
'default': None,
|
|
'type': str,
|
|
'serializer_class': serializers.EmailField,
|
|
'form_class': forms.EmailField,
|
|
'form_kwargs': dict(
|
|
label=_("Contact address"),
|
|
help_text=_("We'll show this publicly to allow attendees to contact you.")
|
|
)
|
|
},
|
|
'imprint_url': {
|
|
'default': None,
|
|
'type': str,
|
|
'form_class': forms.URLField,
|
|
'form_kwargs': dict(
|
|
label=_("Imprint URL"),
|
|
help_text=_("This should point e.g. to a part of your website that has your contact details and legal "
|
|
"information."),
|
|
),
|
|
'serializer_class': serializers.URLField,
|
|
},
|
|
'confirm_text': {
|
|
'default': None,
|
|
'type': LazyI18nString,
|
|
'form_class': I18nFormField,
|
|
'serializer_class': I18nField,
|
|
'form_kwargs': dict(
|
|
label=_('Confirmation text'),
|
|
help_text=_('This text needs to be confirmed by the user before a purchase is possible. You could for example '
|
|
'link your terms of service here. If you use the Pages feature to publish your terms of service, '
|
|
'you don\'t need this setting since you can configure it there.'),
|
|
widget=I18nTextarea,
|
|
)
|
|
},
|
|
'mail_html_renderer': {
|
|
'default': 'classic',
|
|
'type': str
|
|
},
|
|
'mail_attach_ical': {
|
|
'default': 'False',
|
|
'type': bool,
|
|
'form_class': forms.BooleanField,
|
|
'serializer_class': serializers.BooleanField,
|
|
'form_kwargs': dict(
|
|
label=_("Attach calendar files"),
|
|
help_text=_("If enabled, we will attach an .ics calendar file to order confirmation emails."),
|
|
)
|
|
},
|
|
'mail_prefix': {
|
|
'default': None,
|
|
'type': str,
|
|
'form_class': forms.CharField,
|
|
'serializer_class': serializers.CharField,
|
|
'form_kwargs': dict(
|
|
label=_("Subject prefix"),
|
|
help_text=_("This will be prepended to the subject of all outgoing emails, formatted as [prefix]. "
|
|
"Choose, for example, a short form of your event name."),
|
|
)
|
|
},
|
|
'mail_bcc': {
|
|
'default': None,
|
|
'type': str
|
|
},
|
|
'mail_from': {
|
|
'default': settings.MAIL_FROM,
|
|
'type': str,
|
|
'form_class': forms.EmailField,
|
|
'serializer_class': serializers.EmailField,
|
|
'form_kwargs': dict(
|
|
label=_("Sender address"),
|
|
help_text=_("Sender address for outgoing emails"),
|
|
)
|
|
},
|
|
'mail_from_name': {
|
|
'default': None,
|
|
'type': str,
|
|
'form_class': forms.CharField,
|
|
'serializer_class': serializers.CharField,
|
|
'form_kwargs': dict(
|
|
label=_("Sender name"),
|
|
help_text=_("Sender name used in conjunction with the sender address for outgoing emails. "
|
|
"Defaults to your event name."),
|
|
)
|
|
},
|
|
'mail_text_signature': {
|
|
'type': LazyI18nString,
|
|
'default': ""
|
|
},
|
|
'mail_text_resend_link': {
|
|
'type': LazyI18nString,
|
|
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
|
|
|
|
you receive this message because you asked us to send you the link
|
|
to your order for {event}.
|
|
|
|
You can change your order details and view the status of your order at
|
|
{url}
|
|
|
|
Best regards,
|
|
Your {event} team"""))
|
|
},
|
|
'mail_text_resend_all_links': {
|
|
'type': LazyI18nString,
|
|
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
|
|
|
|
somebody requested a list of your orders for {event}.
|
|
The list is as follows:
|
|
|
|
{orders}
|
|
|
|
Best regards,
|
|
Your {event} team"""))
|
|
},
|
|
'mail_text_order_free_attendee': {
|
|
'type': LazyI18nString,
|
|
'default': LazyI18nString.from_gettext(gettext_noop("""Hello {attendee_name},
|
|
|
|
you have been registered for {event} successfully.
|
|
|
|
You can view the details and status of your ticket here:
|
|
{url}
|
|
|
|
Best regards,
|
|
Your {event} team"""))
|
|
},
|
|
'mail_text_order_free': {
|
|
'type': LazyI18nString,
|
|
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
|
|
|
|
your order for {event} was successful. As you only ordered free products,
|
|
no payment is required.
|
|
|
|
You can change your order details and view the status of your order at
|
|
{url}
|
|
|
|
Best regards,
|
|
Your {event} team"""))
|
|
},
|
|
'mail_send_order_free_attendee': {
|
|
'type': bool,
|
|
'default': 'False'
|
|
},
|
|
'mail_text_order_placed_require_approval': {
|
|
'type': LazyI18nString,
|
|
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
|
|
|
|
we successfully received your order for {event}. Since you ordered
|
|
a product that requires approval by the event organizer, we ask you to
|
|
be patient and wait for our next email.
|
|
|
|
You can change your order details and view the status of your order at
|
|
{url}
|
|
|
|
Best regards,
|
|
Your {event} team"""))
|
|
},
|
|
'mail_text_order_placed': {
|
|
'type': LazyI18nString,
|
|
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
|
|
|
|
we successfully received your order for {event} with a total value
|
|
of {total_with_currency}. Please complete your payment before {expire_date}.
|
|
|
|
{payment_info}
|
|
|
|
You can change your order details and view the status of your order at
|
|
{url}
|
|
|
|
Best regards,
|
|
Your {event} team"""))
|
|
},
|
|
'mail_send_order_placed_attendee': {
|
|
'type': bool,
|
|
'default': 'False'
|
|
},
|
|
'mail_text_order_placed_attendee': {
|
|
'type': LazyI18nString,
|
|
'default': LazyI18nString.from_gettext(gettext_noop("""Hello {attendee_name},
|
|
|
|
a ticket for {event} has been ordered for you.
|
|
|
|
You can view the details and status of your ticket here:
|
|
{url}
|
|
|
|
Best regards,
|
|
Your {event} team"""))
|
|
},
|
|
'mail_text_order_changed': {
|
|
'type': LazyI18nString,
|
|
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
|
|
|
|
your order for {event} has been changed.
|
|
|
|
You can view the status of your order at
|
|
{url}
|
|
|
|
Best regards,
|
|
Your {event} team"""))
|
|
},
|
|
'mail_text_order_paid': {
|
|
'type': LazyI18nString,
|
|
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
|
|
|
|
we successfully received your payment for {event}. Thank you!
|
|
|
|
{payment_info}
|
|
|
|
You can change your order details and view the status of your order at
|
|
{url}
|
|
|
|
Best regards,
|
|
Your {event} team"""))
|
|
},
|
|
'mail_send_order_paid_attendee': {
|
|
'type': bool,
|
|
'default': 'False'
|
|
},
|
|
'mail_text_order_paid_attendee': {
|
|
'type': LazyI18nString,
|
|
'default': LazyI18nString.from_gettext(gettext_noop("""Hello {attendee_name},
|
|
|
|
a ticket for {event} that has been ordered for you is now paid.
|
|
|
|
You can view the details and status of your ticket here:
|
|
{url}
|
|
|
|
Best regards,
|
|
Your {event} team"""))
|
|
},
|
|
'mail_days_order_expire_warning': {
|
|
'type': int,
|
|
'default': '3'
|
|
},
|
|
'mail_text_order_expire_warning': {
|
|
'type': LazyI18nString,
|
|
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
|
|
|
|
we did not yet receive a full payment for your order for {event}.
|
|
Please keep in mind that we only guarantee your order if we receive
|
|
your payment before {expire_date}.
|
|
|
|
You can view the payment information and the status of your order at
|
|
{url}
|
|
|
|
Best regards,
|
|
Your {event} team"""))
|
|
},
|
|
'mail_text_waiting_list': {
|
|
'type': LazyI18nString,
|
|
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
|
|
|
|
you submitted yourself to the waiting list for {event},
|
|
for the product {product}.
|
|
|
|
We now have a ticket ready for you! You can redeem it in our ticket shop
|
|
within the next {hours} hours by entering the following voucher code:
|
|
|
|
{code}
|
|
|
|
Alternatively, you can just click on the following link:
|
|
|
|
{url}
|
|
|
|
Please note that this link is only valid within the next {hours} hours!
|
|
We will reassign the ticket to the next person on the list if you do not
|
|
redeem the voucher within that timeframe.
|
|
|
|
Best regards,
|
|
Your {event} team"""))
|
|
},
|
|
'mail_text_order_canceled': {
|
|
'type': LazyI18nString,
|
|
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
|
|
|
|
your order {code} for {event} has been canceled.
|
|
|
|
You can view the details of your order at
|
|
{url}
|
|
|
|
Best regards,
|
|
Your {event} team"""))
|
|
},
|
|
'mail_text_order_approved': {
|
|
'type': LazyI18nString,
|
|
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
|
|
|
|
we approved your order for {event} and will be happy to welcome you
|
|
at our event.
|
|
|
|
Please continue by paying for your order before {expire_date}.
|
|
|
|
You can select a payment method and perform the payment here:
|
|
|
|
{url}
|
|
|
|
Best regards,
|
|
Your {event} team"""))
|
|
},
|
|
'mail_text_order_denied': {
|
|
'type': LazyI18nString,
|
|
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
|
|
|
|
unfortunately, we denied your order request for {event}.
|
|
|
|
{comment}
|
|
|
|
You can view the details of your order here:
|
|
|
|
{url}
|
|
|
|
Best regards,
|
|
Your {event} team"""))
|
|
},
|
|
'mail_text_order_custom_mail': {
|
|
'type': LazyI18nString,
|
|
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
|
|
|
|
You can change your order details and view the status of your order at
|
|
{url}
|
|
|
|
Best regards,
|
|
Your {event} team"""))
|
|
},
|
|
'mail_days_download_reminder': {
|
|
'type': int,
|
|
'default': None
|
|
},
|
|
'mail_send_download_reminder_attendee': {
|
|
'type': bool,
|
|
'default': 'False'
|
|
},
|
|
'mail_text_download_reminder_attendee': {
|
|
'type': LazyI18nString,
|
|
'default': LazyI18nString.from_gettext(gettext_noop("""Hello {attendee_name},
|
|
|
|
you are registered for {event}.
|
|
|
|
If you did not do so already, you can download your ticket here:
|
|
{url}
|
|
|
|
Best regards,
|
|
Your {event} team"""))
|
|
},
|
|
'mail_text_download_reminder': {
|
|
'type': LazyI18nString,
|
|
'default': LazyI18nString.from_gettext(gettext_noop("""Hello,
|
|
|
|
you bought a ticket for {event}.
|
|
|
|
If you did not do so already, you can download your ticket here:
|
|
{url}
|
|
|
|
Best regards,
|
|
Your {event} team"""))
|
|
},
|
|
'smtp_use_custom': {
|
|
'default': 'False',
|
|
'type': bool
|
|
},
|
|
'smtp_host': {
|
|
'default': '',
|
|
'type': str
|
|
},
|
|
'smtp_port': {
|
|
'default': 587,
|
|
'type': int
|
|
},
|
|
'smtp_username': {
|
|
'default': '',
|
|
'type': str
|
|
},
|
|
'smtp_password': {
|
|
'default': '',
|
|
'type': str
|
|
},
|
|
'smtp_use_tls': {
|
|
'default': 'True',
|
|
'type': bool
|
|
},
|
|
'smtp_use_ssl': {
|
|
'default': 'False',
|
|
'type': bool
|
|
},
|
|
'primary_color': {
|
|
'default': '#8E44B3',
|
|
'type': str,
|
|
},
|
|
'theme_color_success': {
|
|
'default': '#50A167',
|
|
'type': str
|
|
},
|
|
'theme_color_danger': {
|
|
'default': '#D36060',
|
|
'type': str
|
|
},
|
|
'theme_color_background': {
|
|
'default': '#FFFFFF',
|
|
'type': str
|
|
},
|
|
'theme_round_borders': {
|
|
'default': 'True',
|
|
'type': bool
|
|
},
|
|
'primary_font': {
|
|
'default': 'Open Sans',
|
|
'type': str
|
|
},
|
|
'presale_css_file': {
|
|
'default': None,
|
|
'type': str
|
|
},
|
|
'presale_css_checksum': {
|
|
'default': None,
|
|
'type': str
|
|
},
|
|
'presale_widget_css_file': {
|
|
'default': None,
|
|
'type': str
|
|
},
|
|
'presale_widget_css_checksum': {
|
|
'default': None,
|
|
'type': str
|
|
},
|
|
'logo_image': {
|
|
'default': None,
|
|
'type': File
|
|
},
|
|
'logo_image_large': {
|
|
'default': 'False',
|
|
'type': bool
|
|
},
|
|
'logo_show_title': {
|
|
'default': 'True',
|
|
'type': bool
|
|
},
|
|
'organizer_logo_image': {
|
|
'default': None,
|
|
'type': File
|
|
},
|
|
'organizer_logo_image_large': {
|
|
'default': 'False',
|
|
'type': bool
|
|
},
|
|
'og_image': {
|
|
'default': None,
|
|
'type': File
|
|
},
|
|
'invoice_logo_image': {
|
|
'default': None,
|
|
'type': File
|
|
},
|
|
'frontpage_text': {
|
|
'default': '',
|
|
'type': LazyI18nString,
|
|
'serializer_class': I18nField,
|
|
'form_class': I18nFormField,
|
|
'form_kwargs': dict(
|
|
label=_("Frontpage text"),
|
|
widget=I18nTextarea
|
|
)
|
|
},
|
|
'banner_text': {
|
|
'default': '',
|
|
'type': LazyI18nString,
|
|
'serializer_class': I18nField,
|
|
'form_class': I18nFormField,
|
|
'form_kwargs': dict(
|
|
label=_("Banner text (top)"),
|
|
widget=I18nTextarea,
|
|
widget_kwargs={'attrs': {'rows': '2'}},
|
|
help_text=_("This text will be shown above every page of your shop. Please only use this for "
|
|
"very important messages.")
|
|
)
|
|
},
|
|
'banner_text_bottom': {
|
|
'default': '',
|
|
'type': LazyI18nString,
|
|
'serializer_class': I18nField,
|
|
'form_class': I18nFormField,
|
|
'form_kwargs': dict(
|
|
label=_("Banner text (bottom)"),
|
|
widget=I18nTextarea,
|
|
widget_kwargs={'attrs': {'rows': '2'}},
|
|
help_text=_("This text will be shown below every page of your shop. Please only use this for "
|
|
"very important messages.")
|
|
)
|
|
},
|
|
'voucher_explanation_text': {
|
|
'default': '',
|
|
'type': LazyI18nString,
|
|
'serializer_class': I18nField,
|
|
'form_class': I18nFormField,
|
|
'form_kwargs': dict(
|
|
label=_("Voucher explanation"),
|
|
widget=I18nTextarea,
|
|
widget_kwargs={'attrs': {'rows': '2'}},
|
|
help_text=_("This text will be shown next to the input for a voucher code. You can use it e.g. to explain "
|
|
"how to obtain a voucher code.")
|
|
)
|
|
},
|
|
'checkout_email_helptext': {
|
|
'default': LazyI18nString.from_gettext(gettext_noop(
|
|
'Make sure to enter a valid email address. We will send you an order '
|
|
'confirmation including a link that you need to access your order later.'
|
|
)),
|
|
'type': LazyI18nString,
|
|
'serializer_class': I18nField,
|
|
'form_class': I18nFormField,
|
|
'form_kwargs': dict(
|
|
label=_("Help text of the email field"),
|
|
widget_kwargs={'attrs': {'rows': '2'}},
|
|
widget=I18nTextarea
|
|
)
|
|
},
|
|
'order_import_settings': {
|
|
'default': '{}',
|
|
'type': dict
|
|
},
|
|
'organizer_info_text': {
|
|
'default': '',
|
|
'type': LazyI18nString
|
|
},
|
|
'event_team_provisioning': {
|
|
'default': 'True',
|
|
'type': bool
|
|
},
|
|
'update_check_ack': {
|
|
'default': 'False',
|
|
'type': bool
|
|
},
|
|
'update_check_email': {
|
|
'default': '',
|
|
'type': str
|
|
},
|
|
'update_check_perform': {
|
|
'default': 'True',
|
|
'type': bool
|
|
},
|
|
'update_check_result': {
|
|
'default': None,
|
|
'type': dict
|
|
},
|
|
'update_check_result_warning': {
|
|
'default': 'False',
|
|
'type': bool
|
|
},
|
|
'update_check_last': {
|
|
'default': None,
|
|
'type': datetime
|
|
},
|
|
'update_check_id': {
|
|
'default': None,
|
|
'type': str
|
|
},
|
|
'banner_message': {
|
|
'default': '',
|
|
'type': LazyI18nString
|
|
},
|
|
'banner_message_detail': {
|
|
'default': '',
|
|
'type': LazyI18nString
|
|
},
|
|
'opencagedata_apikey': {
|
|
'default': None,
|
|
'type': str
|
|
},
|
|
'leaflet_tiles': {
|
|
'default': None,
|
|
'type': str
|
|
},
|
|
'leaflet_tiles_attribution': {
|
|
'default': None,
|
|
'type': str
|
|
},
|
|
'frontpage_subevent_ordering': {
|
|
'default': 'date_ascending',
|
|
'type': str,
|
|
'serializer_class': serializers.ChoiceField,
|
|
'serializer_kwargs': dict(
|
|
choices=[
|
|
('date_ascending', _('Event start time')),
|
|
('date_descending', _('Event start time (descending)')),
|
|
('name_ascending', _('Name')),
|
|
('name_descending', _('Name (descending)')),
|
|
],
|
|
),
|
|
'form_class': forms.ChoiceField,
|
|
'form_kwargs': dict(
|
|
label=pgettext('subevent', 'Date ordering'),
|
|
choices=[
|
|
('date_ascending', _('Event start time')),
|
|
('date_descending', _('Event start time (descending)')),
|
|
('name_ascending', _('Name')),
|
|
('name_descending', _('Name (descending)')),
|
|
],
|
|
# When adding a new ordering, remember to also define it in the event model
|
|
)
|
|
},
|
|
'name_scheme': {
|
|
'default': 'full',
|
|
'type': str
|
|
},
|
|
'giftcard_length': {
|
|
'default': settings.ENTROPY['giftcard_secret'],
|
|
'type': int
|
|
},
|
|
'seating_minimal_distance': {
|
|
'default': '0',
|
|
'type': float
|
|
},
|
|
'seating_allow_blocked_seats_for_channel': {
|
|
'default': [],
|
|
'type': list
|
|
},
|
|
'seating_distance_within_row': {
|
|
'default': 'False',
|
|
'type': bool
|
|
}
|
|
}
|
|
PERSON_NAME_TITLE_GROUPS = OrderedDict([
|
|
('english_common', (_('Most common English titles'), (
|
|
'Mr',
|
|
'Ms',
|
|
'Mrs',
|
|
'Miss',
|
|
'Mx',
|
|
'Dr',
|
|
'Professor',
|
|
'Sir'
|
|
))),
|
|
('german_common', (_('Most common German titles'), (
|
|
'Dr.',
|
|
'Prof.',
|
|
'Prof. Dr.',
|
|
)))
|
|
])
|
|
PERSON_NAME_SCHEMES = OrderedDict([
|
|
('given_family', {
|
|
'fields': (
|
|
('given_name', _('Given name'), 1),
|
|
('family_name', _('Family name'), 1),
|
|
),
|
|
'concatenation': lambda d: ' '.join(str(p) for p in [d.get('given_name', ''), d.get('family_name', '')] if p),
|
|
'sample': {
|
|
'given_name': pgettext_lazy('person_name_sample', 'John'),
|
|
'family_name': pgettext_lazy('person_name_sample', 'Doe'),
|
|
'_scheme': 'given_family',
|
|
},
|
|
}),
|
|
('title_given_family', {
|
|
'fields': (
|
|
('title', pgettext_lazy('person_name', 'Title'), 1),
|
|
('given_name', _('Given name'), 2),
|
|
('family_name', _('Family name'), 2),
|
|
),
|
|
'concatenation': lambda d: ' '.join(
|
|
str(p) for p in [d.get('title', ''), d.get('given_name', ''), d.get('family_name', '')] if p
|
|
),
|
|
'sample': {
|
|
'title': pgettext_lazy('person_name_sample', 'Dr'),
|
|
'given_name': pgettext_lazy('person_name_sample', 'John'),
|
|
'family_name': pgettext_lazy('person_name_sample', 'Doe'),
|
|
'_scheme': 'title_given_family',
|
|
},
|
|
}),
|
|
('title_given_family', {
|
|
'fields': (
|
|
('title', pgettext_lazy('person_name', 'Title'), 1),
|
|
('given_name', _('Given name'), 2),
|
|
('family_name', _('Family name'), 2),
|
|
),
|
|
'concatenation': lambda d: ' '.join(
|
|
str(p) for p in [d.get('title', ''), d.get('given_name', ''), d.get('family_name', '')] if p
|
|
),
|
|
'sample': {
|
|
'title': pgettext_lazy('person_name_sample', 'Dr'),
|
|
'given_name': pgettext_lazy('person_name_sample', 'John'),
|
|
'family_name': pgettext_lazy('person_name_sample', 'Doe'),
|
|
'_scheme': 'title_given_family',
|
|
},
|
|
}),
|
|
('given_middle_family', {
|
|
'fields': (
|
|
('given_name', _('First name'), 2),
|
|
('middle_name', _('Middle name'), 1),
|
|
('family_name', _('Family name'), 2),
|
|
),
|
|
'concatenation': lambda d: ' '.join(
|
|
str(p) for p in [d.get('given_name', ''), d.get('middle_name', ''), d.get('family_name', '')] if p
|
|
),
|
|
'sample': {
|
|
'given_name': pgettext_lazy('person_name_sample', 'John'),
|
|
'middle_name': 'M',
|
|
'family_name': pgettext_lazy('person_name_sample', 'Doe'),
|
|
'_scheme': 'given_middle_family',
|
|
},
|
|
}),
|
|
('title_given_middle_family', {
|
|
'fields': (
|
|
('title', pgettext_lazy('person_name', 'Title'), 1),
|
|
('given_name', _('First name'), 2),
|
|
('middle_name', _('Middle name'), 1),
|
|
('family_name', _('Family name'), 1),
|
|
),
|
|
'concatenation': lambda d: ' '.join(
|
|
str(p) for p in [d.get('title', ''), d.get('given_name'), d.get('middle_name'), d.get('family_name')] if p
|
|
),
|
|
'sample': {
|
|
'title': pgettext_lazy('person_name_sample', 'Dr'),
|
|
'given_name': pgettext_lazy('person_name_sample', 'John'),
|
|
'middle_name': 'M',
|
|
'family_name': pgettext_lazy('person_name_sample', 'Doe'),
|
|
'_scheme': 'title_given_middle_family',
|
|
},
|
|
}),
|
|
('family_given', {
|
|
'fields': (
|
|
('family_name', _('Family name'), 1),
|
|
('given_name', _('Given name'), 1),
|
|
),
|
|
'concatenation': lambda d: ' '.join(
|
|
str(p) for p in [d.get('family_name', ''), d.get('given_name', '')] if p
|
|
),
|
|
'sample': {
|
|
'given_name': pgettext_lazy('person_name_sample', 'John'),
|
|
'family_name': pgettext_lazy('person_name_sample', 'Doe'),
|
|
'_scheme': 'family_given',
|
|
},
|
|
}),
|
|
('family_nospace_given', {
|
|
'fields': (
|
|
('given_name', _('Given name'), 1),
|
|
('family_name', _('Family name'), 1),
|
|
),
|
|
'concatenation': lambda d: ''.join(
|
|
str(p) for p in [d.get('family_name', ''), d.get('given_name', '')] if p
|
|
),
|
|
'sample': {
|
|
'given_name': '泽东',
|
|
'family_name': '毛',
|
|
'_scheme': 'family_nospace_given',
|
|
},
|
|
}),
|
|
('family_comma_given', {
|
|
'fields': (
|
|
('given_name', _('Given name'), 1),
|
|
('family_name', _('Family name'), 1),
|
|
),
|
|
'concatenation': lambda d: (
|
|
str(d.get('family_name', '')) +
|
|
str((', ' if d.get('family_name') and d.get('given_name') else '')) +
|
|
str(d.get('given_name', ''))
|
|
),
|
|
'sample': {
|
|
'given_name': pgettext_lazy('person_name_sample', 'John'),
|
|
'family_name': pgettext_lazy('person_name_sample', 'Doe'),
|
|
'_scheme': 'family_comma_given',
|
|
},
|
|
}),
|
|
('full', {
|
|
'fields': (
|
|
('full_name', _('Name'), 1),
|
|
),
|
|
'concatenation': lambda d: str(d.get('full_name', '')),
|
|
'sample': {
|
|
'full_name': pgettext_lazy('person_name_sample', 'John Doe'),
|
|
'_scheme': 'full',
|
|
},
|
|
}),
|
|
('calling_full', {
|
|
'fields': (
|
|
('calling_name', _('Calling name'), 1),
|
|
('full_name', _('Full name'), 2),
|
|
),
|
|
'concatenation': lambda d: str(d.get('full_name', '')),
|
|
'sample': {
|
|
'full_name': pgettext_lazy('person_name_sample', 'John Doe'),
|
|
'calling_name': pgettext_lazy('person_name_sample', 'John'),
|
|
'_scheme': 'calling_full',
|
|
},
|
|
}),
|
|
('full_transcription', {
|
|
'fields': (
|
|
('full_name', _('Full name'), 1),
|
|
('latin_transcription', _('Latin transcription'), 2),
|
|
),
|
|
'concatenation': lambda d: str(d.get('full_name', '')),
|
|
'sample': {
|
|
'full_name': '庄司',
|
|
'latin_transcription': 'Shōji',
|
|
'_scheme': 'full_transcription',
|
|
},
|
|
}),
|
|
])
|
|
COUNTRIES_WITH_STATE_IN_ADDRESS = {
|
|
# Source: http://www.bitboost.com/ref/international-address-formats.html
|
|
# This is not a list of countries that *have* states, this is a list of countries where states
|
|
# are actually *used* in postal addresses. This is obviously not complete and opinionated.
|
|
# Country: [(List of subdivision types as defined by pycountry), (short or long form to be used)]
|
|
'AU': (['State', 'Territory'], 'short'),
|
|
'BR': (['State'], 'short'),
|
|
'CA': (['Province', 'Territory'], 'short'),
|
|
# 'CN': (['Province', 'Autonomous region', 'Munincipality'], 'long'),
|
|
'MY': (['State'], 'long'),
|
|
'MX': (['State', 'Federal District'], 'short'),
|
|
'US': (['State', 'Outlying area', 'District'], 'short'),
|
|
}
|
|
|
|
|
|
settings_hierarkey = Hierarkey(attribute_name='settings')
|
|
|
|
for k, v in DEFAULTS.items():
|
|
settings_hierarkey.add_default(k, v['default'], v['type'])
|
|
|
|
|
|
def i18n_uns(v):
|
|
try:
|
|
return LazyI18nString(json.loads(v))
|
|
except ValueError:
|
|
return LazyI18nString(str(v))
|
|
|
|
|
|
settings_hierarkey.add_type(LazyI18nString,
|
|
serialize=lambda s: json.dumps(s.data),
|
|
unserialize=i18n_uns)
|
|
settings_hierarkey.add_type(RelativeDateWrapper,
|
|
serialize=lambda rdw: rdw.to_string(),
|
|
unserialize=lambda s: RelativeDateWrapper.from_string(s))
|
|
|
|
|
|
@settings_hierarkey.set_global(cache_namespace='global')
|
|
class GlobalSettingsObject(GlobalSettingsBase):
|
|
slug = '_global'
|
|
|
|
|
|
class SettingsSandbox:
|
|
"""
|
|
Transparently proxied access to event settings, handling your prefixes for you.
|
|
|
|
:param typestr: The first part of the pretix, e.g. ``plugin``
|
|
:param key: The prefix, e.g. the name of your plugin
|
|
:param obj: The event or organizer that should be queried
|
|
"""
|
|
|
|
def __init__(self, typestr: str, key: str, obj: Model):
|
|
self._event = obj
|
|
self._type = typestr
|
|
self._key = key
|
|
|
|
def get_prefix(self):
|
|
return '%s_%s_' % (self._type, self._key)
|
|
|
|
def _convert_key(self, key: str) -> str:
|
|
return '%s_%s_%s' % (self._type, self._key, key)
|
|
|
|
def __setitem__(self, key: str, value: Any) -> None:
|
|
self.set(key, value)
|
|
|
|
def __setattr__(self, key: str, value: Any) -> None:
|
|
if key.startswith('_'):
|
|
return super().__setattr__(key, value)
|
|
self.set(key, value)
|
|
|
|
def __getattr__(self, item: str) -> Any:
|
|
return self.get(item)
|
|
|
|
def __getitem__(self, item: str) -> Any:
|
|
return self.get(item)
|
|
|
|
def __delitem__(self, key: str) -> None:
|
|
del self._event.settings[self._convert_key(key)]
|
|
|
|
def __delattr__(self, key: str) -> None:
|
|
del self._event.settings[self._convert_key(key)]
|
|
|
|
def get(self, key: str, default: Any=None, as_type: type=str):
|
|
return self._event.settings.get(self._convert_key(key), default=default, as_type=as_type)
|
|
|
|
def set(self, key: str, value: Any):
|
|
self._event.settings.set(self._convert_key(key), value)
|
|
|
|
|
|
def validate_settings(event, settings_dict):
|
|
from pretix.base.signals import validate_event_settings
|
|
|
|
if 'locales' in settings_dict and settings_dict['locale'] not in settings_dict['locales']:
|
|
raise ValidationError({
|
|
'locale': _('Your default locale must also be enabled for your event (see box above).')
|
|
})
|
|
if settings_dict.get('attendee_names_required') and not settings_dict.get('attendee_names_asked'):
|
|
raise ValidationError({
|
|
'attendee_names_required': _('You cannot require specifying attendee names if you do not ask for them.')
|
|
})
|
|
if settings_dict.get('attendee_emails_required') and not settings_dict.get('attendee_emails_asked'):
|
|
raise ValidationError({
|
|
'attendee_emails_required': _('You have to ask for attendee emails if you want to make them required.')
|
|
})
|
|
if settings_dict.get('invoice_address_required') and not settings_dict.get('invoice_address_asked'):
|
|
raise ValidationError({
|
|
'invoice_address_required': _('You have to ask for invoice addresses if you want to make them required.')
|
|
})
|
|
if settings_dict.get('invoice_address_company_required') and not settings_dict.get('invoice_address_required'):
|
|
raise ValidationError({
|
|
'invoice_address_company_required': _('You have to require invoice addresses to require for company names.')
|
|
})
|
|
|
|
payment_term_last = settings_dict.get('payment_term_last')
|
|
if payment_term_last and event.presale_end:
|
|
if payment_term_last.date(event) < event.presale_end.date():
|
|
raise ValidationError({
|
|
'payment_term_last': _('The last payment date cannot be before the end of presale.')
|
|
})
|
|
|
|
validate_event_settings.send(sender=event, settings_dict=settings_dict)
|