forked from CGM_Public/pretix_original
Compare commits
43 Commits
fix-state-
...
fix-invoic
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
267bac7a9d | ||
|
|
c7b951346b | ||
|
|
d8adfdd06f | ||
|
|
0279ca7d94 | ||
|
|
d1989c3cd3 | ||
|
|
61cb2e15cf | ||
|
|
f2ee1d00b3 | ||
|
|
e8e9698a31 | ||
|
|
a1bf7be244 | ||
|
|
f4ca9a5681 | ||
|
|
e6d984538f | ||
|
|
9f1ee9157f | ||
|
|
242e5af4b5 | ||
|
|
7d6e98e6da | ||
|
|
27f964f3ae | ||
|
|
84b3060c0f | ||
|
|
25dcb72f92 | ||
|
|
4b078867c6 | ||
|
|
c595a59d4a | ||
|
|
f164daeaee | ||
|
|
c6b6dd8d49 | ||
|
|
8038c87963 | ||
|
|
c45a970d32 | ||
|
|
a34517233d | ||
|
|
8fb2e5383c | ||
|
|
86a00f3338 | ||
|
|
c8c0d3e7f5 | ||
|
|
7dd455ce15 | ||
|
|
391eda25da | ||
|
|
fcff5a522d | ||
|
|
7e93d38a01 | ||
|
|
6469381899 | ||
|
|
761706c60c | ||
|
|
f91315c88e | ||
|
|
bc05afeab9 | ||
|
|
02d495d287 | ||
|
|
894878d9da | ||
|
|
5896ca0197 | ||
|
|
fe6fc8df32 | ||
|
|
9de8f3a775 | ||
|
|
c92bb9cb8b | ||
|
|
76ecec8b98 | ||
|
|
4b8416df8f |
@@ -288,6 +288,7 @@ Example::
|
||||
[django]
|
||||
secret=j1kjps5a5&4ilpn912s7a1!e2h!duz^i3&idu@_907s$wrz@x-
|
||||
debug=off
|
||||
passwords_argon2=on
|
||||
|
||||
``secret``
|
||||
The secret to be used by Django for signing and verification purposes. If this
|
||||
@@ -303,6 +304,10 @@ Example::
|
||||
|
||||
.. WARNING:: Never set this to ``True`` in production!
|
||||
|
||||
``passwords_argon``
|
||||
Use the ``argon2`` algorithm for password hashing. Disable on systems with a small number of CPU cores (currently
|
||||
less than 8).
|
||||
|
||||
``profile``
|
||||
Enable code profiling for a random subset of requests. Disabled by default, see
|
||||
:ref:`perf-monitoring` for details.
|
||||
|
||||
@@ -231,11 +231,10 @@ The following snippet is an example on how to configure a nginx proxy for pretix
|
||||
}
|
||||
}
|
||||
server {
|
||||
listen 443 default_server;
|
||||
listen [::]:443 ipv6only=on default_server;
|
||||
listen 443 ssl default_server;
|
||||
listen [::]:443 ipv6only=on ssl default_server;
|
||||
server_name pretix.mydomain.com;
|
||||
|
||||
ssl on;
|
||||
ssl_certificate /path/to/cert.chain.pem;
|
||||
ssl_certificate_key /path/to/key.pem;
|
||||
|
||||
|
||||
@@ -216,11 +216,10 @@ The following snippet is an example on how to configure a nginx proxy for pretix
|
||||
}
|
||||
}
|
||||
server {
|
||||
listen 443 default_server;
|
||||
listen [::]:443 ipv6only=on default_server;
|
||||
listen 443 ssl default_server;
|
||||
listen [::]:443 ipv6only=on ssl default_server;
|
||||
server_name pretix.mydomain.com;
|
||||
|
||||
ssl on;
|
||||
ssl_certificate /path/to/cert.chain.pem;
|
||||
ssl_certificate_key /path/to/key.pem;
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ dependencies = [
|
||||
"django-phonenumber-field==7.3.*",
|
||||
"django-redis==5.4.*",
|
||||
"django-scopes==2.0.*",
|
||||
"django-statici18n==2.5.*",
|
||||
"django-statici18n==2.6.*",
|
||||
"djangorestframework==3.15.*",
|
||||
"dnspython==2.7.*",
|
||||
"drf_ujson2==1.7.*",
|
||||
@@ -76,7 +76,7 @@ dependencies = [
|
||||
"phonenumberslite==8.13.*",
|
||||
"Pillow==11.0.*",
|
||||
"pretix-plugin-build",
|
||||
"protobuf==5.28.*",
|
||||
"protobuf==5.29.*",
|
||||
"psycopg2-binary",
|
||||
"pycountry",
|
||||
"pycparser==2.22",
|
||||
@@ -107,7 +107,7 @@ dependencies = [
|
||||
[project.optional-dependencies]
|
||||
memcached = ["pylibmc"]
|
||||
dev = [
|
||||
"aiohttp==3.10.*",
|
||||
"aiohttp==3.11.*",
|
||||
"coverage",
|
||||
"coveralls",
|
||||
"fakeredis==2.26.*",
|
||||
|
||||
@@ -19,4 +19,4 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
__version__ = "2024.11.0.dev0"
|
||||
__version__ = "2024.12.0.dev0"
|
||||
|
||||
@@ -277,6 +277,10 @@ class NamePartsFormField(forms.MultiValueField):
|
||||
return value
|
||||
|
||||
|
||||
def name_parts_is_empty(name_parts_dict):
|
||||
return not any(k != "_scheme" and v for k, v in name_parts_dict.items())
|
||||
|
||||
|
||||
class WrappedPhonePrefixSelect(Select):
|
||||
initial = None
|
||||
|
||||
@@ -1143,15 +1147,16 @@ class BaseInvoiceAddressForm(forms.ModelForm):
|
||||
validate_address # local import to prevent impact on startup time
|
||||
|
||||
data = self.cleaned_data
|
||||
|
||||
if not data.get('is_business'):
|
||||
data['company'] = ''
|
||||
data['vat_id'] = ''
|
||||
if data.get('is_business') and not ask_for_vat_id(data.get('country')):
|
||||
data['vat_id'] = ''
|
||||
if self.event.settings.invoice_address_required:
|
||||
if self.address_validation and self.event.settings.invoice_address_required and not self.all_optional:
|
||||
if data.get('is_business') and not data.get('company'):
|
||||
raise ValidationError({"company": _('You need to provide a company name.')})
|
||||
if not data.get('is_business') and not data.get('name_parts'):
|
||||
if not data.get('is_business') and name_parts_is_empty(data.get('name_parts', {})):
|
||||
raise ValidationError(_('You need to provide your name.'))
|
||||
if not data.get('street') and not data.get('zipcode') and not data.get('city'):
|
||||
raise ValidationError({"street": _('This field is required.')})
|
||||
@@ -1166,7 +1171,7 @@ class BaseInvoiceAddressForm(forms.ModelForm):
|
||||
|
||||
if all(
|
||||
not v for k, v in data.items() if k not in ('is_business', 'country', 'name_parts')
|
||||
) and len(data.get('name_parts', {})) == 1:
|
||||
) and name_parts_is_empty(data.get('name_parts', {})):
|
||||
# Do not save the country if it is the only field set -- we don't know the user even checked it!
|
||||
self.cleaned_data['country'] = ''
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ from decimal import Decimal
|
||||
import django.core.validators
|
||||
import django.db.models.deletion
|
||||
import i18nfield.fields
|
||||
from argon2.exceptions import HashingError
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.hashers import make_password
|
||||
from django.db import migrations, models
|
||||
@@ -25,7 +26,14 @@ def initial_user(apps, schema_editor):
|
||||
user = User(email='admin@localhost')
|
||||
user.is_staff = True
|
||||
user.is_superuser = True
|
||||
user.password = make_password('admin')
|
||||
try:
|
||||
user.password = make_password('admin')
|
||||
except HashingError:
|
||||
raise Exception(
|
||||
"Could not hash password of initial user with argon2id. If this is a system with less than 8 CPU cores, "
|
||||
"you might need to disable argon2id by setting `passwords_argon2=off` in the `[django]` section of the "
|
||||
"pretix.cfg configuration file."
|
||||
)
|
||||
user.save()
|
||||
|
||||
|
||||
|
||||
@@ -159,10 +159,24 @@ class Membership(models.Model):
|
||||
de = date_format(self.date_end, 'SHORT_DATE_FORMAT')
|
||||
return f'{self.membership_type.name}: {self.attendee_name} ({ds} – {de})'
|
||||
|
||||
@property
|
||||
def percentage_used(self):
|
||||
if self.membership_type.max_usages and self.usages:
|
||||
return int(self.usages / self.membership_type.max_usages * 100)
|
||||
return 0
|
||||
|
||||
@property
|
||||
def attendee_name(self):
|
||||
return build_name(self.attendee_name_parts, fallback_scheme=lambda: self.customer.organizer.settings.name_scheme)
|
||||
|
||||
@property
|
||||
def expired(self):
|
||||
return time_machine_now() > self.date_end
|
||||
|
||||
@property
|
||||
def not_yet_valid(self):
|
||||
return time_machine_now() < self.date_start
|
||||
|
||||
def is_valid(self, ev=None, ticket_valid_from=None, valid_from_not_chosen=False):
|
||||
if valid_from_not_chosen:
|
||||
return not self.canceled and self.date_end >= time_machine_now()
|
||||
|
||||
@@ -209,13 +209,24 @@ def get_best_name(position_or_address, parts=False):
|
||||
def base_placeholders(sender, **kwargs):
|
||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
||||
|
||||
def _event_sample(event):
|
||||
if event.has_subevents:
|
||||
se = event.subevents.first()
|
||||
if se:
|
||||
return se.name
|
||||
return event.name
|
||||
|
||||
ph = [
|
||||
SimpleFunctionalTextPlaceholder(
|
||||
'event', ['event'], lambda event: event.name, lambda event: event.name
|
||||
),
|
||||
SimpleFunctionalTextPlaceholder(
|
||||
'event', ['event_or_subevent'], lambda event_or_subevent: event_or_subevent.name,
|
||||
lambda event_or_subevent: event_or_subevent.name
|
||||
_event_sample,
|
||||
),
|
||||
SimpleFunctionalTextPlaceholder(
|
||||
'event_series_name', ['event', 'event_or_subevent'], lambda event, event_or_subevent: event.name,
|
||||
lambda event: event.name
|
||||
),
|
||||
SimpleFunctionalTextPlaceholder(
|
||||
'event_slug', ['event'], lambda event: event.slug, lambda event: event.slug
|
||||
|
||||
34
src/pretix/base/templatetags/icon.py
Normal file
34
src/pretix/base/templatetags/icon.py
Normal file
@@ -0,0 +1,34 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-2021 rami.io GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from django import template
|
||||
from django.utils.html import format_html
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def icon(key, *args, **kwargs):
|
||||
return format_html(
|
||||
'<span class="fa fa-{} {}" aria-hidden="true"></span>',
|
||||
key,
|
||||
kwargs["class"] if "class" in kwargs else "",
|
||||
)
|
||||
42
src/pretix/base/templatetags/textbubble.py
Normal file
42
src/pretix/base/templatetags/textbubble.py
Normal file
@@ -0,0 +1,42 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-2021 rami.io GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from django import template
|
||||
from django.utils.html import format_html, mark_safe
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def textbubble(type, *args, **kwargs):
|
||||
return format_html(
|
||||
'<span class="textbubble-{}">{}',
|
||||
type or "info",
|
||||
"" if "icon" not in kwargs else format_html(
|
||||
'<i class="fa fa-{}" aria-hidden="true"></i> ',
|
||||
kwargs["icon"]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def endtextbubble():
|
||||
return mark_safe('</span>')
|
||||
@@ -136,6 +136,11 @@ class EventWizardBasicsForm(I18nModelForm):
|
||||
choices=settings.LANGUAGES,
|
||||
label=_("Default language"),
|
||||
)
|
||||
no_taxes = forms.BooleanField(
|
||||
label=_("I don't want to specify taxes now"),
|
||||
help_text=_("You can always configure tax rates later."),
|
||||
required=False,
|
||||
)
|
||||
tax_rate = forms.DecimalField(
|
||||
label=_("Sales tax rate"),
|
||||
help_text=_("Do you need to pay sales tax on your tickets? In this case, please enter the applicable tax rate "
|
||||
@@ -223,6 +228,11 @@ class EventWizardBasicsForm(I18nModelForm):
|
||||
raise ValidationError({
|
||||
'timezone': _('Your default locale must be specified.')
|
||||
})
|
||||
if not data.get("no_taxes") and not data.get("tax_rate"):
|
||||
raise ValidationError({
|
||||
'tax_rate': _('You have not specified a tax rate. If you do not want us to compute sales taxes, please '
|
||||
'check "{field}" above.').format(field=self.fields["no_taxes"].label)
|
||||
})
|
||||
|
||||
# change timezone
|
||||
zone = ZoneInfo(data.get('timezone'))
|
||||
|
||||
@@ -41,7 +41,10 @@
|
||||
{% endif %}
|
||||
{% include "pretixcontrol/event/fragment_geodata.html" %}
|
||||
{% bootstrap_field form.currency layout="control" %}
|
||||
{% bootstrap_field form.tax_rate addon_after="%" layout="control" %}
|
||||
{% bootstrap_field form.no_taxes layout="control" %}
|
||||
<div data-display-dependency="#{{ form.no_taxes.id_for_label }}" data-inverse>
|
||||
{% bootstrap_field form.tax_rate addon_after="%" layout="control" %}
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>{% trans "Display settings" %}</legend>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-11-18 15:30+0000\n"
|
||||
"POT-Creation-Date: 2024-11-19 15:34+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
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -8,16 +8,16 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-11-08 13:45+0000\n"
|
||||
"PO-Revision-Date: 2024-10-01 22:52+0000\n"
|
||||
"PO-Revision-Date: 2024-11-28 06:00+0000\n"
|
||||
"Last-Translator: Patrick Chilton <chpatrick@gmail.com>\n"
|
||||
"Language-Team: Hungarian <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
"js/hu/>\n"
|
||||
"Language-Team: Hungarian <https://translate.pretix.eu/projects/pretix/"
|
||||
"pretix-js/hu/>\n"
|
||||
"Language: hu\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 5.7.2\n"
|
||||
"X-Generator: Weblate 5.8.3\n"
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
|
||||
@@ -779,7 +779,7 @@ msgstr "A kosár lejárt"
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:588
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:607
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
msgstr "Időzona:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:598
|
||||
msgid "Your local time:"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -381,8 +381,8 @@ class RuleForm(FormPlaceholderMixin, I18nModelForm):
|
||||
]
|
||||
)
|
||||
|
||||
self._set_field_placeholders('subject', ['event', 'order'])
|
||||
self._set_field_placeholders('template', ['event', 'order'])
|
||||
self._set_field_placeholders('subject', ['event', 'order', 'event_or_subevent'])
|
||||
self._set_field_placeholders('template', ['event', 'order', 'event_or_subevent'])
|
||||
|
||||
choices = [(e, l) for e, l in Order.STATUS_CHOICE if e != 'n']
|
||||
choices.insert(0, ('n__valid_if_pending', _('payment pending but already confirmed')))
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# Generated by Django 4.2.16 on 2024-11-13 13:43
|
||||
|
||||
from django.db import migrations
|
||||
from django.db.models import Q
|
||||
from i18nfield.strings import LazyI18nString
|
||||
|
||||
|
||||
def migrate_placeholders(apps, schema_editor):
|
||||
Rule = apps.get_model("sendmail", "Rule")
|
||||
for r in Rule.objects.filter(
|
||||
Q(template__contains="{event}") | Q(subject__contains="{event}"),
|
||||
event__has_subevents=True
|
||||
):
|
||||
r.template = LazyI18nString({
|
||||
k: v.replace("{event}", "{event_series_name}") for k, v in r.template.data.items()
|
||||
})
|
||||
r.subject = LazyI18nString({
|
||||
k: v.replace("{event}", "{event_series_name}") for k, v in r.subject.data.items()
|
||||
})
|
||||
r.save(update_fields=["template", "subject"])
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("sendmail", "008_remove_scheduled_mails"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
migrate_placeholders,
|
||||
migrations.RunPython.noop,
|
||||
)
|
||||
]
|
||||
@@ -174,7 +174,12 @@ class ScheduledMail(models.Model):
|
||||
ia = InvoiceAddress(order=o)
|
||||
|
||||
if send_to_orders and o.email:
|
||||
email_ctx = get_email_context(event=e, order=o, invoice_address=ia)
|
||||
email_ctx = get_email_context(
|
||||
event=e,
|
||||
order=o,
|
||||
invoice_address=ia,
|
||||
event_or_subevent=self.subevent or e,
|
||||
)
|
||||
try:
|
||||
o.send_mail(self.rule.subject, self.rule.template, email_ctx,
|
||||
attach_ical=self.rule.attach_ical,
|
||||
@@ -192,12 +197,23 @@ class ScheduledMail(models.Model):
|
||||
for p in positions:
|
||||
try:
|
||||
if p.attendee_email and (p.attendee_email != o.email or not o_sent):
|
||||
email_ctx = get_email_context(event=e, order=o, invoice_address=ia, position=p)
|
||||
email_ctx = get_email_context(
|
||||
event=e,
|
||||
order=o,
|
||||
invoice_address=ia,
|
||||
position=p,
|
||||
event_or_subevent=self.subevent or e,
|
||||
)
|
||||
p.send_mail(self.rule.subject, self.rule.template, email_ctx,
|
||||
attach_ical=self.rule.attach_ical,
|
||||
log_entry_type='pretix.plugins.sendmail.rule.order.position.email.sent')
|
||||
elif not o_sent and o.email:
|
||||
email_ctx = get_email_context(event=e, order=o, invoice_address=ia)
|
||||
email_ctx = get_email_context(
|
||||
event=e,
|
||||
order=o,
|
||||
invoice_address=ia,
|
||||
event_or_subevent=self.subevent or e,
|
||||
)
|
||||
o.send_mail(self.rule.subject, self.rule.template, email_ctx,
|
||||
attach_ical=self.rule.attach_ical,
|
||||
log_entry_type='pretix.plugins.sendmail.rule.order.email.sent')
|
||||
|
||||
@@ -492,12 +492,18 @@ class StripeSettingsHolder(BasePaymentProvider):
|
||||
# label=_('PayPal'),
|
||||
# disabled=self.event.currency not in [
|
||||
# 'EUR', 'GBP', 'USD', 'CHF', 'CZK', 'DKK', 'NOK', 'PLN', 'SEK', 'AUD', 'CAD', 'HKD', 'NZD', 'SGD'
|
||||
# ]
|
||||
# ],
|
||||
# help_text=_('Some payment methods might need to be enabled in the settings of your Stripe account '
|
||||
# 'before they work properly.'),
|
||||
# required=False,
|
||||
# )),
|
||||
('method_mobilepay',
|
||||
forms.BooleanField(
|
||||
label=_('MobilePay'),
|
||||
disabled=self.event.currency not in ['DKK', 'EUR', 'NOK', 'SEK'],
|
||||
help_text=_('Some payment methods might need to be enabled in the settings of your Stripe account '
|
||||
'before they work properly.'),
|
||||
required=False,
|
||||
)),
|
||||
] + extra_fields + list(super().settings_form_fields.items()) + moto_settings
|
||||
)
|
||||
|
||||
@@ -160,7 +160,7 @@ class BaseCheckoutFlowStep:
|
||||
kwargs['cart_namespace'] = request.resolver_match.kwargs['cart_namespace']
|
||||
return eventreverse(self.request.event, 'presale:event.index', kwargs=kwargs)
|
||||
else:
|
||||
return prev.get_step_url(request)
|
||||
return prev.get_step_url(request) + '?dir=prev'
|
||||
|
||||
def get_next_url(self, request):
|
||||
n = self.get_next_applicable(request)
|
||||
@@ -662,7 +662,7 @@ class AddOnsStep(CartMixin, AsyncAction, TemplateFlowStep):
|
||||
if 'async_id' in request.GET and settings.HAS_CELERY:
|
||||
return self.get_result(request)
|
||||
if len(self.forms) == 0 and len(self.cross_selling_data) == 0 and self.is_completed(request):
|
||||
return redirect(self.get_next_url(request))
|
||||
return redirect(self.get_prev_url(request) if request.GET.get('dir') == 'prev' else self.get_next_url(request))
|
||||
return TemplateFlowStep.get(self, request)
|
||||
|
||||
def _clean_category(self, form, category):
|
||||
|
||||
@@ -133,6 +133,7 @@ class InvoiceAddressForm(BaseInvoiceAddressForm):
|
||||
|
||||
|
||||
class InvoiceNameForm(InvoiceAddressForm):
|
||||
address_validation = False
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -1,31 +1,29 @@
|
||||
{% load i18n %}
|
||||
{% load bootstrap3 %}
|
||||
{% load textbubble %}
|
||||
{# Changes should be replicated in pretixcontrol/orders/fragment_order_status.html and in pretix/base/models/orders.py #}
|
||||
{% if order.status == "n" %}
|
||||
{% if order.require_approval %}
|
||||
{% trans "Approval pending" %}
|
||||
{% textbubble "warning" icon="exclamation-triangle" %}{% trans "Approval pending" %}{% endtextbubble %}
|
||||
{% elif order.total == 0 %}
|
||||
{% trans "Confirmation pending" context "order state" %}
|
||||
{% textbubble "warning" icon="exclamation-triangle" %}{% trans "Confirmation pending" context "order state" %}{% endtextbubble %}
|
||||
{% elif event.settings.payment_pending_hidden %}
|
||||
{# intentionally left blank #}
|
||||
{% elif order.valid_if_pending %}
|
||||
{% trans "Confirmed" context "order state" %}
|
||||
{% textbubble "info" icon="info-circle" %}{% trans "Confirmed" context "order state" %}{% endtextbubble %}
|
||||
{% else %}
|
||||
{% trans "Payment pending" %}
|
||||
{% endif %}
|
||||
{% if not event.settings.payment_pending_hidden %}
|
||||
<i class="status-dot fa fa-circle {% if order.valid_if_pending %}text-info{% else %}text-warning{% endif %}" aria-hidden="true"></i>
|
||||
{% textbubble "warning" icon="exclamation-triangle" %}{% trans "Payment pending" %}{% endtextbubble %}
|
||||
{% endif %}
|
||||
{% elif order.status == "p" %}
|
||||
{% if order.count_positions == 0 %}
|
||||
{% trans "Canceled (paid fee)" %} <i class="status-dot fa fa-info-circle text-info" aria-hidden="true"></i>
|
||||
{% textbubble "info" icon="info-circle" %}{% trans "Canceled (paid fee)" %}{% endtextbubble %}
|
||||
{% elif order.total == 0 %}
|
||||
{% trans "Confirmed" context "order state" %} <i class="status-dot fa fa-check-circle text-success" aria-hidden="true"></i>
|
||||
{% textbubble "success" icon="check" %}{% trans "Confirmed" context "order state" %}{% endtextbubble %}
|
||||
{% else %}
|
||||
{% trans "Paid" %} <i class="status-dot fa fa-check-circle text-success" aria-hidden="true"></i>
|
||||
{% textbubble "success" icon="check" %}{% trans "Paid" %}{% endtextbubble %}
|
||||
{% endif %}
|
||||
{% elif order.status == "e" %}
|
||||
{% trans "Expired" %} <i class="status-dot fa fa-minus-circle text-danger" aria-hidden="true"></i>
|
||||
{% textbubble "danger" icon="minus" %}{% trans "Expired" %}{% endtextbubble %}
|
||||
{% elif order.status == "c" %}
|
||||
{% trans "Canceled" %} <i class="status-dot fa fa-times-circle text-danger" aria-hidden="true"></i>
|
||||
{% textbubble "danger" icon="times" %}{% trans "Canceled" %}{% endtextbubble %}
|
||||
{% endif %}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
{% if request.organizer.settings.customer_accounts %}
|
||||
<nav class="loginstatus" aria-label="{% trans "customer account" %}">
|
||||
{% if request.customer %}
|
||||
<a href="{% abseventurl request.organizer "presale:organizer.customer.profile" %}"
|
||||
<a href="{% abseventurl request.organizer "presale:organizer.customer.index" %}"
|
||||
aria-label="{% trans "View customer account" %}" data-placement="bottom"
|
||||
title="{% trans "View user profile" %}" data-toggle="tooltip">
|
||||
title="{% trans "View customer account" %}" data-toggle="tooltip">
|
||||
<span class="fa fa-user" aria-hidden="true"></span>
|
||||
{{ request.customer.name|default:request.customer.email }}</a>
|
||||
<a href="{% if request.event_domain %}{% abseventurl request.event "presale:organizer.customer.logout" %}{% else %}{% abseventurl request.organizer "presale:organizer.customer.logout" %}{% endif %}?next={{ request.path|urlencode }}%3F{{ request.META.QUERY_STRING|urlencode }}"
|
||||
|
||||
@@ -1,33 +1,39 @@
|
||||
{% extends "pretixpresale/organizers/base.html" %}
|
||||
{% extends "pretixpresale/organizers/customer_base.html" %}
|
||||
{% load i18n %}
|
||||
{% load icon %}
|
||||
{% load eventurl %}
|
||||
{% block title %}{% trans "Delete address" %}{% endblock %}
|
||||
{% block content %}
|
||||
<h2>
|
||||
{% trans "Delete address" %}
|
||||
</h2>
|
||||
{% block inner %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
{% icon "address-card-o" %} <b>{% trans "Delete address" %}</b>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="panel-body account-addresses">
|
||||
<p>
|
||||
{% trans "Do you really want to delete the following address from your account?" %}
|
||||
</p>
|
||||
<address>
|
||||
{{ address.describe|linebreaksbr }}
|
||||
</address>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<p>
|
||||
{% trans "Do you really want to delete the following address from your account?" %}
|
||||
</p>
|
||||
<address>
|
||||
{{ address.describe|linebreaksbr }}
|
||||
</address>
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-sm-6">
|
||||
<a class="btn btn-block btn-default btn-lg"
|
||||
href="{% abseventurl request.organizer "presale:organizer.customer.profile" %}">
|
||||
href="{% abseventurl request.organizer "presale:organizer.customer.addresses" %}">
|
||||
{% trans "Go back" %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-4 col-md-offset-4 col-sm-6">
|
||||
<button class="btn btn-block btn-danger btn-lg" type="submit">
|
||||
{% icon "trash" %}
|
||||
{% trans "Delete" %}
|
||||
</button>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
{% extends "pretixpresale/organizers/customer_base.html" %}
|
||||
{% load i18n %}
|
||||
{% load icon %}
|
||||
{% load eventurl %}
|
||||
{% block title %}{% trans "Addresses" %}{% endblock %}
|
||||
{% block inner %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
{% icon "address-card-o" %}
|
||||
<b>{% trans "Addresses" %}</b> ({{ page_obj.paginator.count }})
|
||||
</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{% if invoice_addresses %}
|
||||
<ul class="full-width-list alternating-rows account-addresses">
|
||||
<li class="row">
|
||||
{% for ia in invoice_addresses %}
|
||||
{% if forloop.counter0 and forloop.counter0|divisibleby:4 %}
|
||||
</li>
|
||||
<li class="row">
|
||||
{% endif %}
|
||||
<div class="col-md-3 col-xs-12">
|
||||
<address>{{ ia.describe|linebreaksbr }}</address>
|
||||
<p class="blank-after">
|
||||
<a href="{% abseventurl request.organizer "presale:organizer.customer.address.delete" id=ia.id %}"
|
||||
class="btn btn-danger btn-sm">
|
||||
{% icon "trash" %}
|
||||
{% trans "Delete" %}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</li>
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="text-center">{% trans "You don’t have any addresses in your account yet." %}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% include "pretixcontrol/pagination.html" %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,55 @@
|
||||
{% extends "pretixpresale/organizers/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load icon %}
|
||||
{% load eventurl %}
|
||||
{% block content %}
|
||||
<h2>
|
||||
{% trans "Your account" %}
|
||||
</h2>
|
||||
<div class="blank-after">
|
||||
<dl class="row">
|
||||
<div class="col-sm-6">
|
||||
<dt>{{ customer.name }}</dt>
|
||||
<dd>{{ customer.email }}</dd>
|
||||
{% if customer.phone %}
|
||||
<dd>{% icon "phone" %} {{ customer.phone }}</dd>
|
||||
{% endif %}
|
||||
<dd>
|
||||
<ul class="list-inline">
|
||||
<li>
|
||||
<a href="{% eventurl request.organizer "presale:organizer.customer.change" %}">
|
||||
{% icon "edit" %}
|
||||
{% trans "Change account information" %}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% eventurl request.organizer "presale:organizer.customer.password" %}">
|
||||
{% icon "key" %}
|
||||
{% trans "Change password" %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dd>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="col-sm-6 text-right">
|
||||
<dt>{% trans "Customer ID" %}</dt>
|
||||
<dd>#{{ customer.identifier }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
<nav class="subnav row" aria-label="{% trans "customer account information" %}">
|
||||
<ul class="list-inline blank-after col-xs-12">
|
||||
{% for nav in sub_nav %}
|
||||
<li>
|
||||
<a href="{{ nav.url }}"{% if nav.active %} class="active"{% endif %}>
|
||||
{% icon nav.icon %}{{ nav.label }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
{% block inner %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
@@ -1,7 +1,7 @@
|
||||
{% extends "pretixpresale/organizers/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load icon %}
|
||||
{% load eventurl %}
|
||||
{% load urlreplace %}
|
||||
{% load bootstrap3 %}
|
||||
{% block title %}{% trans "Log in" %}{% endblock %}
|
||||
{% block content %}
|
||||
@@ -18,6 +18,7 @@
|
||||
{% bootstrap_form form %}
|
||||
<div class="form-group buttons">
|
||||
<button type="submit" class="btn btn-primary btn-lg btn-block">
|
||||
{% icon "sign-in" %}
|
||||
{% trans "Log in" %}
|
||||
</button>
|
||||
</div>
|
||||
@@ -35,6 +36,7 @@
|
||||
<div class="col-md-6">
|
||||
<a class="btn btn-link btn-block"
|
||||
href="{% eventurl request.organizer "presale:organizer.customer.register" %}">
|
||||
{% icon "address-book-o" %}
|
||||
{% trans "Create account" %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,96 +1,127 @@
|
||||
{% extends "pretixpresale/organizers/base.html" %}
|
||||
{% extends "pretixpresale/organizers/customer_base.html" %}
|
||||
{% load i18n %}
|
||||
{% load icon %}
|
||||
{% load eventurl %}
|
||||
{% load urlreplace %}
|
||||
{% load money %}
|
||||
{% load bootstrap3 %}
|
||||
{% load textbubble %}
|
||||
{% block title %}{% trans "Your membership" %}{% endblock %}
|
||||
{% block content %}
|
||||
<h2>
|
||||
{% trans "Your membership" %}
|
||||
</h2>
|
||||
<div class="panel panel-primary items">
|
||||
{% block inner %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
{% trans "Details" %}
|
||||
{% if membership.membership_type.transferable %}
|
||||
{% icon "users" %}
|
||||
{% else %}
|
||||
{% icon "id-badge" %}
|
||||
{% endif %}
|
||||
<b>{% trans "Your membership" %}</b>
|
||||
{% if membership.testmode %}
|
||||
<span class="h6">
|
||||
{% textbubble "warning" %}
|
||||
{% trans "TEST MODE" %}
|
||||
{% endtextbubble %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{% trans "Membership type" %}</dt>
|
||||
<dd>{{ membership.membership_type.name }}</dd>
|
||||
<dd>{% if membership.canceled %}<del>{% endif %}
|
||||
{{ membership.membership_type.name }}
|
||||
{% if membership.canceled %}</del>
|
||||
<small>
|
||||
{% textbubble "danger" icon="times" %}
|
||||
{% trans "Canceled" %}
|
||||
{% endtextbubble %}
|
||||
</small>
|
||||
{% endif %}
|
||||
<br><small class="text-muted">
|
||||
{% if membership.membership_type.transferable %}
|
||||
({% trans "transferable" %})
|
||||
{% else %}
|
||||
({% trans "not transferable" %})
|
||||
{% endif %}
|
||||
</small>
|
||||
</dd>
|
||||
<dt>{% trans "Valid from" %}</dt>
|
||||
<dd>{{ membership.date_start|date:"SHORT_DATETIME_FORMAT" }}
|
||||
<dt>{% trans "Valid until" %}</dt>
|
||||
<dd>{{ membership.date_end|date:"SHORT_DATETIME_FORMAT" }}
|
||||
<dt>{% trans "Attendee name" %}</dt>
|
||||
<dd>{{ membership.attendee_name }}
|
||||
<dd>{{ membership.attendee_name|default_if_none:"–" }}
|
||||
<dt>{% trans "Maximum usages" %}</dt>
|
||||
<dd>{{ membership.membership_type.max_usages|default_if_none:"–" }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default items">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
{% trans "Usages" %}
|
||||
</h3>
|
||||
</div>
|
||||
<table class="panel-body table table-hover">
|
||||
<caption class="sr-only">{% trans "Usages" %}</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Order code" %}</th>
|
||||
<th>{% trans "Event" %}</th>
|
||||
<th>{% trans "Product" %}</th>
|
||||
<th>{% trans "Order date" %}</th>
|
||||
<th class="text-right">{% trans "Status" %}</th>
|
||||
<th class="text-right"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<div class="panel-body">
|
||||
{% if usages %}
|
||||
<ul class="full-width-list alternating-rows">
|
||||
{% for op in usages %}
|
||||
<tr>
|
||||
<td>
|
||||
<strong>
|
||||
{{ op.order.code }}-{{ op.positionid }}
|
||||
</strong>
|
||||
{% if op.order.testmode %}
|
||||
<span class="label label-warning">{% trans "TEST MODE" %}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ op.order.event }}
|
||||
{% if op.subevent %}
|
||||
<br>
|
||||
{{ op.subevent|default:"" }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ op.item.name }}
|
||||
{% if op.variation %}– {{ op.variation }}{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ op.order.datetime|date:"SHORT_DATETIME_FORMAT" }}
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
{% if op.canceled %}
|
||||
{% trans "Canceled" %} <i class="{{ class }} fa fa-times-circle text-danger" aria-hidden="true"></i>
|
||||
{% else %}
|
||||
{% include "pretixcontrol/orders/fragment_order_status.html" with order=op.order %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
<a href="{% abseventurl op.order.event "presale:event.order" order=op.order.code secret=op.order.secret %}"
|
||||
target="_blank"
|
||||
class="btn btn-default">
|
||||
{% trans "Details" %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% include "pretixcontrol/pagination.html" %}
|
||||
<li class="row">
|
||||
<dl>
|
||||
<div class="col-md-4 col-sm-5 col-xs-12">
|
||||
<dt class="sr-only">{% trans "Order" %}</dt>
|
||||
<dd><strong>
|
||||
<a href="{% abseventurl op.order.event "presale:event.order" order=op.order.code secret=op.order.secret %}" target="_blank">
|
||||
{% icon "shopping-cart" %}
|
||||
{{ op.order.code }}-{{ op.positionid }}
|
||||
</a>
|
||||
</strong>
|
||||
<small>{% include "pretixpresale/event/fragment_order_status.html" with order=op.order event=op.order.event %}</small>
|
||||
</dd>
|
||||
<dd><time datetime="{{ op.order.datetime|date:"Y-m-d H:i" }}" class="text-muted small">{{ op.order.datetime|date:"SHORT_DATETIME_FORMAT" }}</time></dd>
|
||||
{% if op.order.testmode %}
|
||||
<dd>
|
||||
<small>
|
||||
{% textbubble "warning" %}
|
||||
{% trans "TEST MODE" %}
|
||||
{% endtextbubble %}
|
||||
</small>
|
||||
</dd>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-5 col-xs-8">
|
||||
<dt class="sr-only">{% trans "Product" %}</dt>
|
||||
<dd>{{ op.item.name }}
|
||||
{% if op.variation %} - {{ op.variation }}{% endif %}
|
||||
</dd>
|
||||
<dt class="sr-only">{% trans "Event" %}</dt>
|
||||
<dd>
|
||||
<small class="text-muted">
|
||||
{{ op.order.event }}
|
||||
{% if op.subevent %}
|
||||
<br>{{ op.subevent }}
|
||||
{% endif %}
|
||||
{% if not op.order.event.has_subevents and op.order.event.settings.show_dates_on_frontpage %}
|
||||
<br>{{ op.order.event.get_date_range_display }}
|
||||
{% endif %}
|
||||
</small>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="col-sm-2 col-xs-4">
|
||||
<dt class="sr-only">{% trans "Actions" %}</dt>
|
||||
<dd class="text-right">
|
||||
<a href="{% abseventurl op.order.event "presale:event.order" order=op.order.code secret=op.order.secret %}"
|
||||
target="_blank">
|
||||
{% icon "list-ul" %}
|
||||
{% trans "Details" %}
|
||||
</a></dd>
|
||||
</div>
|
||||
</dl>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="text-center">{% trans "You haven’t used this membership yet." %}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% include "pretixcontrol/pagination.html" %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
{% extends "pretixpresale/organizers/customer_base.html" %}
|
||||
{% load i18n %}
|
||||
{% load icon %}
|
||||
{% load eventurl %}
|
||||
{% load textbubble %}
|
||||
{% block title %}{% trans "Memberships" %}{% endblock %}
|
||||
{% block inner %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
{% icon "id-badge" %}
|
||||
<b>{% trans "Memberships" %}</b> ({{ page_obj.paginator.count }})
|
||||
</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{% if memberships %}
|
||||
<ul class="full-width-list alternating-rows">
|
||||
{% for m in memberships %}
|
||||
<li class="row">
|
||||
<dl>
|
||||
<div class="col-xs-5">
|
||||
<dt>
|
||||
{% if m.canceled %}<del>{% endif %}
|
||||
<a href="{% abseventurl request.organizer "presale:organizer.customer.membership" id=m.id %}">
|
||||
{{ m.membership_type.name }}
|
||||
</a>
|
||||
{% if m.canceled %}</del>{% endif %}
|
||||
{% if m.membership_type.transferable %}
|
||||
<span class="text-muted" data-toggle="tooltip" title="{% trans "Membership is transferable" %}">
|
||||
{% icon "users" %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</dt>
|
||||
{% if m.attendee_name %}
|
||||
<dd class="text-muted">
|
||||
{% icon "id-badge" %}
|
||||
<span class="sr-only">{% trans "Attendee name" %}:</span>
|
||||
{{ m.attendee_name }}
|
||||
</dd>
|
||||
{% endif %}
|
||||
<dd class="text-muted">
|
||||
<small>
|
||||
{% if m.canceled %}
|
||||
{% textbubble "danger" icon="times" %}
|
||||
{% trans "Canceled" %}
|
||||
{% endtextbubble %}
|
||||
{% elif m.expired %}
|
||||
{% icon "minus-square-o" %}
|
||||
{% trans "Expired since" %}
|
||||
<time datetime="{{ m.date_end|date:"Y-m-d H:i" }}">
|
||||
{{ m.date_end|date:"SHORT_DATETIME_FORMAT" }}
|
||||
</time>
|
||||
{% elif m.not_yet_valid %}
|
||||
{% icon "clock-o" %}
|
||||
{% trans "Valid from" %}
|
||||
<time datetime="{{ m.date_start|date:"Y-m-d H:i" }}">
|
||||
{{ m.date_start|date:"SHORT_DATETIME_FORMAT" }}
|
||||
</time>
|
||||
{% else %}
|
||||
{% icon "check" %}
|
||||
{% trans "Valid until" %}
|
||||
<time datetime="{{ m.date_end|date:"Y-m-d H:i" }}">
|
||||
{{ m.date_end|date:"SHORT_DATETIME_FORMAT" }}
|
||||
</time>
|
||||
{% endif %}
|
||||
</small>
|
||||
</dd>
|
||||
{% if m.testmode %}
|
||||
<dd>
|
||||
<small>
|
||||
{% textbubble "warning" %}
|
||||
{% trans "TEST MODE" %}
|
||||
{% endtextbubble %}
|
||||
</small>
|
||||
</dd>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-xs-5">
|
||||
<dd>
|
||||
<div class="quotabox full-width">
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-success progress-bar-{{ m.percentage_used }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="numbers">
|
||||
{{ m.usages }} /
|
||||
{{ m.membership_type.max_usages|default_if_none:"∞" }}
|
||||
</div>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="col-xs-2">
|
||||
<dt class="sr-only">{% trans "Actions" %}</dt>
|
||||
<dd class="text-right">
|
||||
<a href="{% abseventurl request.organizer "presale:organizer.customer.membership" id=m.id %}">
|
||||
{% icon "list-ul" %}
|
||||
{% trans "Details" %}
|
||||
</a>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="text-center">{% trans "You don’t have any memberships in your account yet." %}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% include "pretixcontrol/pagination.html" %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,83 @@
|
||||
{% extends "pretixpresale/organizers/customer_base.html" %}
|
||||
{% load i18n %}
|
||||
{% load icon %}
|
||||
{% load eventurl %}
|
||||
{% load money %}
|
||||
{% load textbubble %}
|
||||
{% block title %}{% trans "Your account" %}{% endblock %}
|
||||
{% block inner %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
{% icon "shopping-cart" %}
|
||||
<b>{% trans "Orders" %}</b> ({{ page_obj.paginator.count }})
|
||||
</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{% if orders %}
|
||||
<ul class="full-width-list alternating-rows">
|
||||
{% for o in orders %}
|
||||
<li class="row">
|
||||
<dl>
|
||||
<div class="col-md-4 col-sm-5 col-xs-8">
|
||||
<dt class="sr-only">{% trans "Order" %}</dt>
|
||||
<dd><strong>
|
||||
<a href="{% abseventurl o.event "presale:event.order" order=o.code secret=o.secret %}" target="_blank">
|
||||
{% icon "shopping-cart" %}
|
||||
{{ o.code }}</a>
|
||||
</strong>
|
||||
{% if o.customer_id != customer.pk %}
|
||||
<span class="text-muted" data-toggle="tooltip"
|
||||
title="{% trans "Matched to the account based on the email address." %}">
|
||||
{% icon "compress" %}
|
||||
</span>
|
||||
{% endif %}
|
||||
<small>{% include "pretixpresale/event/fragment_order_status.html" with order=o event=o.event %}</small>
|
||||
</dd>
|
||||
<dd><time datetime="{{ o.datetime|date:"Y-m-d H:i" }}" class="text-muted small">{{ o.datetime|date:"SHORT_DATETIME_FORMAT" }}</time></dd>
|
||||
{% if o.testmode %}
|
||||
<dd>
|
||||
<small>
|
||||
{% textbubble "warning" %}
|
||||
{% trans "TEST MODE" %}
|
||||
{% endtextbubble %}
|
||||
</small>
|
||||
</dd>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-md-2 col-sm-2 col-xs-4 text-right">
|
||||
<dt class="sr-only">{% trans "Order total" %}</dt>
|
||||
<dd>{{ o.total|money:o.event.currency }}</dd>
|
||||
<dt class="sr-only">{% trans "Positions" %}</dt>
|
||||
<dd class="text-muted"><small>{% blocktranslate count counter=o.count_positions|default_if_none:0 %}{{ counter }} item{% plural %}{{ counter }} items{% endblocktranslate %}</small>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-3 col-xs-8">
|
||||
<dt class="sr-only">{% trans "Event" %}</dt>
|
||||
<dd>
|
||||
{{ o.event }}
|
||||
{% if not o.event.has_subevents and o.event.settings.show_dates_on_frontpage %}
|
||||
<br><small class="text-muted">{{ o.event.get_date_range_display }}</small>
|
||||
{% endif %}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="col-sm-2 col-xs-4">
|
||||
<dt class="sr-only">{% trans "Actions" %}</dt>
|
||||
<dd class="text-right">
|
||||
<a href="{% abseventurl o.event "presale:event.order" order=o.code secret=o.secret %}"
|
||||
target="_blank">
|
||||
{% icon "list-ul" %}
|
||||
{% trans "Details" %}
|
||||
</a></dd>
|
||||
</div>
|
||||
</dl>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="text-center">{% trans "You don’t have any orders in your account yet." %}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% include "pretixcontrol/pagination.html" %}
|
||||
{% endblock %}
|
||||
@@ -1,253 +0,0 @@
|
||||
{% extends "pretixpresale/organizers/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load eventurl %}
|
||||
{% load urlreplace %}
|
||||
{% load money %}
|
||||
{% load bootstrap3 %}
|
||||
{% block title %}{% trans "Your account" %}{% endblock %}
|
||||
{% block content %}
|
||||
<h2>
|
||||
{% trans "Your account" %}
|
||||
</h2>
|
||||
<div class="panel panel-primary items">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
{% trans "Account information" %}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{% trans "Customer ID" %}</dt>
|
||||
<dd>#{{ customer.identifier }}</dd>
|
||||
{% if customer.provider %}
|
||||
<dt>{% trans "Login method" %}</dt>
|
||||
<dd>{{ customer.provider.name }}</dd>
|
||||
{% endif %}
|
||||
<dt>{% trans "Email" %}</dt>
|
||||
<dd>{{ customer.email }}
|
||||
</dd>
|
||||
<dt>{% trans "Name" %}</dt>
|
||||
<dd>{{ customer.name }}</dd>
|
||||
{% if customer.phone %}
|
||||
<dt>{% trans "Phone" %}</dt>
|
||||
<dd>{{ customer.phone }}</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
<div class="text-right">
|
||||
<a href="{% eventurl request.organizer "presale:organizer.customer.change" %}"
|
||||
class="btn btn-default">
|
||||
{% trans "Change account information" %}
|
||||
</a>
|
||||
{% if not customer.provider %}
|
||||
<a href="{% eventurl request.organizer "presale:organizer.customer.password" %}"
|
||||
class="btn btn-default">
|
||||
{% trans "Change password" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li role="presentation" class="active">
|
||||
<a href="#orders" aria-controls="orders" role="tab" data-toggle="tab">{% trans "Orders" %}</a>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<a href="#memberships" aria-controls="memberships" role="tab" data-toggle="tab">{% trans "Memberships" %}</a>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<a href="#addresses" aria-controls="addresses" role="tab" data-toggle="tab">{% trans "Addresses" %}</a>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<a href="#profiles" aria-controls="profiles" role="tab" data-toggle="tab">{% trans "Attendee profiles" %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div role="tabpanel" class="tab-pane active" id="orders">
|
||||
<table class="panel-body table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Order code" %}</th>
|
||||
<th>{% trans "Event" %}</th>
|
||||
<th>{% trans "Order date" %}</th>
|
||||
<th class="text-right">{% trans "Order total" %}</th>
|
||||
<th class="text-right">{% trans "Positions" %}</th>
|
||||
<th class="text-right">{% trans "Status" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for o in orders %}
|
||||
<tr>
|
||||
<td>
|
||||
<strong>
|
||||
<a href="{% abseventurl o.event "presale:event.order" order=o.code secret=o.secret %}" target="_blank">
|
||||
{{ o.code }}
|
||||
</a>
|
||||
</strong>
|
||||
{% if o.testmode %}
|
||||
<span class="label label-warning">{% trans "TEST MODE" %}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ o.event }}
|
||||
{% if not o.event.has_subevents and o.event.settings.show_dates_on_frontpage %}
|
||||
<br><small class="text-muted">{{ o.event.get_date_range_display }}</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ o.datetime|date:"SHORT_DATETIME_FORMAT" }}
|
||||
{% if o.customer_id != customer.pk %}
|
||||
<span class="fa fa-link text-muted"
|
||||
data-toggle="tooltip"
|
||||
title="{% trans "Matched to the account based on the email address." %}"
|
||||
></span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
{{ o.total|money:o.event.currency }}
|
||||
</td>
|
||||
<td class="text-right flip">{{ o.count_positions|default_if_none:"0" }}</td>
|
||||
<td class="text-right flip">{% include "pretixpresale/event/fragment_order_status.html" with order=o event=o.event %}</td>
|
||||
<td class="text-right flip">
|
||||
<a href="{% abseventurl o.event "presale:event.order" order=o.code secret=o.secret %}"
|
||||
target="_blank"
|
||||
class="btn btn-default">
|
||||
{% trans "Details" %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% include "pretixcontrol/pagination.html" %}
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="memberships">
|
||||
<table class="panel-body table table-hover">
|
||||
<caption class="sr-only">{% trans "Memberships" %}</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Membership type" %}</th>
|
||||
<th>{% trans "Valid from" %}</th>
|
||||
<th>{% trans "Valid until" %}</th>
|
||||
<th>{% trans "Attendee name" %}</th>
|
||||
<th>{% trans "Usages" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for m in memberships %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if m.canceled %}<del>{% endif %}
|
||||
{{ m.membership_type.name }}
|
||||
{% if m.canceled %}</del>{% endif %}
|
||||
{% if m.testmode %}<span class="label label-warning">{% trans "TEST MODE" %}</span>{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ m.date_start|date:"SHORT_DATETIME_FORMAT" }}
|
||||
</td>
|
||||
<td>
|
||||
{{ m.date_end|date:"SHORT_DATETIME_FORMAT" }}
|
||||
</td>
|
||||
<td>
|
||||
{{ m.attendee_name }}
|
||||
</td>
|
||||
<td>
|
||||
<div class="quotabox">
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-success progress-bar-{{ m.percent }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="numbers">
|
||||
{{ m.usages }} /
|
||||
{{ m.membership_type.max_usages|default_if_none:"∞" }}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
<a href="{% abseventurl request.organizer "presale:organizer.customer.membership" id=m.id %}"
|
||||
data-toggle="tooltip"
|
||||
title="{% trans "Details" %}"
|
||||
class="btn btn-default">
|
||||
<i class="fa fa-list"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="6" class="text-center">{% trans "No memberships are stored in your account." %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="addresses">
|
||||
<table class="panel-body table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Address" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for ia in invoice_addresses %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ ia.describe|linebreaksbr }}
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
<a href="{% abseventurl request.organizer "presale:organizer.customer.address.delete" id=ia.id %}"
|
||||
data-toggle="tooltip"
|
||||
title="{% trans "Delete" %}"
|
||||
class="btn btn-danger">
|
||||
<i class="fa fa-trash"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="2" class="text-center">
|
||||
{% trans "No addresses are stored in your account." %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="profiles">
|
||||
<table class="panel-body table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Profile" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for ap in customer.attendee_profiles.all %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ ap.describe|linebreaksbr }}
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
<a href="{% abseventurl request.organizer "presale:organizer.customer.profile.delete" id=ap.id %}"
|
||||
data-toggle="tooltip"
|
||||
title="{% trans "Delete" %}"
|
||||
class="btn btn-danger">
|
||||
<i class="fa fa-trash"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="2" class="text-center">
|
||||
{% trans "No attendee profiles are stored in your account." %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,33 +1,39 @@
|
||||
{% extends "pretixpresale/organizers/base.html" %}
|
||||
{% extends "pretixpresale/organizers/customer_base.html" %}
|
||||
{% load i18n %}
|
||||
{% load icon %}
|
||||
{% load eventurl %}
|
||||
{% block title %}{% trans "Delete profile" %}{% endblock %}
|
||||
{% block content %}
|
||||
<h2>
|
||||
{% trans "Delete profile" %}
|
||||
</h2>
|
||||
{% block inner %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
{% icon "user" %} <b>{% trans "Delete profile" %}</b>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p>
|
||||
{% trans "Do you really want to delete the following profile from your account?" %}
|
||||
</p>
|
||||
<p>
|
||||
{{ profile.describe|linebreaksbr }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<p>
|
||||
{% trans "Do you really want to delete the following profile from your account?" %}
|
||||
</p>
|
||||
<address>
|
||||
{{ profile.describe|linebreaksbr }}
|
||||
</address>
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-sm-6">
|
||||
<a class="btn btn-block btn-default btn-lg"
|
||||
href="{% abseventurl request.organizer "presale:organizer.customer.profile" %}">
|
||||
href="{% abseventurl request.organizer "presale:organizer.customer.profiles" %}">
|
||||
{% trans "Go back" %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-4 col-md-offset-4 col-sm-6">
|
||||
<button class="btn btn-block btn-danger btn-lg" type="submit">
|
||||
{% icon "trash" %}
|
||||
{% trans "Delete" %}
|
||||
</button>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
{% extends "pretixpresale/organizers/customer_base.html" %}
|
||||
{% load i18n %}
|
||||
{% load icon %}
|
||||
{% load eventurl %}
|
||||
{% block title %}{% trans "Attendee profiles" %}{% endblock %}
|
||||
{% block inner %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
{% icon "users" %}
|
||||
<b>{% trans "Attendee profiles" %}</b> ({{ page_obj.paginator.count }})
|
||||
</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{% if attendee_profiles %}
|
||||
<ol class="full-width-list alternating-rows">
|
||||
<li class="row">
|
||||
{% for ap in attendee_profiles %}
|
||||
{% if forloop.counter0 and forloop.counter0|divisibleby:4 %}
|
||||
</li>
|
||||
<li class="row">
|
||||
{% endif %}
|
||||
<div class="col-md-3 col-xs-12">
|
||||
<p>{{ ap.describe|linebreaksbr }}</p>
|
||||
<p class="blank-after">
|
||||
<a href="{% abseventurl request.organizer "presale:organizer.customer.profile.delete" id=ap.id %}"
|
||||
class="btn btn-danger btn-sm">
|
||||
{% icon "trash" %}
|
||||
{% trans "Delete" %}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</li>
|
||||
</ol>
|
||||
{% else %}
|
||||
<p class="text-center">{% trans "You don’t have any attendee profiles in your account yet." %}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% include "pretixcontrol/pagination.html" %}
|
||||
{% endblock %}
|
||||
@@ -210,10 +210,13 @@ organizer_patterns = [
|
||||
re_path(r'^account/password$', pretix.presale.views.customer.ChangePasswordView.as_view(), name='organizer.customer.password'),
|
||||
re_path(r'^account/change$', pretix.presale.views.customer.ChangeInformationView.as_view(), name='organizer.customer.change'),
|
||||
re_path(r'^account/confirmchange$', pretix.presale.views.customer.ConfirmChangeView.as_view(), name='organizer.customer.change.confirm'),
|
||||
re_path(r'^account/membership/(?P<id>\d+)/$', pretix.presale.views.customer.MembershipUsageView.as_view(), name='organizer.customer.membership'),
|
||||
re_path(r'^account/memberships$', pretix.presale.views.customer.MembershipView.as_view(), name='organizer.customer.memberships'),
|
||||
re_path(r'^account/memberships/(?P<id>\d+)/$', pretix.presale.views.customer.MembershipUsageView.as_view(), name='organizer.customer.membership'),
|
||||
re_path(r'^account/addresses$', pretix.presale.views.customer.AddressView.as_view(), name='organizer.customer.addresses'),
|
||||
re_path(r'^account/addresses/(?P<id>\d+)/delete$', pretix.presale.views.customer.AddressDeleteView.as_view(), name='organizer.customer.address.delete'),
|
||||
re_path(r'^account/profiles$', pretix.presale.views.customer.ProfileView.as_view(), name='organizer.customer.profiles'),
|
||||
re_path(r'^account/profiles/(?P<id>\d+)/delete$', pretix.presale.views.customer.ProfileDeleteView.as_view(), name='organizer.customer.profile.delete'),
|
||||
re_path(r'^account/$', pretix.presale.views.customer.ProfileView.as_view(), name='organizer.customer.profile'),
|
||||
re_path(r'^account/$', pretix.presale.views.customer.OrderView.as_view(), name='organizer.customer.index'),
|
||||
|
||||
re_path(r'^oauth2/v1/authorize$', pretix.presale.views.oidc_op.AuthorizeView.as_view(),
|
||||
name='organizer.oauth2.v1.authorize'),
|
||||
|
||||
@@ -89,7 +89,7 @@ class CheckoutView(View):
|
||||
else:
|
||||
previous_step = step
|
||||
step.c_is_before = True
|
||||
step.c_resolved_url = step.get_step_url(request)
|
||||
step.c_resolved_url = step.get_step_url(request) + '?dir=prev'
|
||||
raise Http404()
|
||||
|
||||
def redirect(self, url):
|
||||
|
||||
@@ -134,7 +134,7 @@ class LoginView(RedirectBackMixin, FormView):
|
||||
url = self.get_redirect_url()
|
||||
|
||||
if not url:
|
||||
return eventreverse(self.request.organizer, 'presale:organizer.customer.profile', kwargs={})
|
||||
return eventreverse(self.request.organizer, 'presale:organizer.customer.index', kwargs={})
|
||||
|
||||
if self.request.GET.get("request_cross_domain_customer_auth") == "true":
|
||||
otpstore = SessionStore()
|
||||
@@ -350,8 +350,42 @@ class CustomerRequiredMixin:
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
class ProfileView(CustomerRequiredMixin, ListView):
|
||||
template_name = 'pretixpresale/organizers/customer_profile.html'
|
||||
class CustomerAccountBaseMixin(CustomerRequiredMixin):
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['customer'] = self.request.customer
|
||||
url_name = self.request.resolver_match.url_name
|
||||
ctx['sub_nav'] = [
|
||||
{
|
||||
'label': _('Orders'),
|
||||
'url': eventreverse(self.request.organizer, 'presale:organizer.customer.index', kwargs={}),
|
||||
'active': url_name == 'organizer.customer.index',
|
||||
'icon': 'shopping-cart',
|
||||
},
|
||||
{
|
||||
'label': _('Memberships'),
|
||||
'url': eventreverse(self.request.organizer, 'presale:organizer.customer.memberships', kwargs={}),
|
||||
'active': url_name.startswith('organizer.customer.membership'),
|
||||
'icon': 'id-badge',
|
||||
},
|
||||
{
|
||||
'label': _('Addresses'),
|
||||
'url': eventreverse(self.request.organizer, 'presale:organizer.customer.addresses', kwargs={}),
|
||||
'active': url_name.startswith('organizer.customer.address'),
|
||||
'icon': 'address-card-o',
|
||||
},
|
||||
{
|
||||
'label': _('Attendee profiles'),
|
||||
'url': eventreverse(self.request.organizer, 'presale:organizer.customer.profiles', kwargs={}),
|
||||
'active': url_name.startswith('organizer.customer.profile'),
|
||||
'icon': 'user',
|
||||
},
|
||||
]
|
||||
return ctx
|
||||
|
||||
|
||||
class OrderView(CustomerAccountBaseMixin, ListView):
|
||||
template_name = 'pretixpresale/organizers/customer_orders.html'
|
||||
context_object_name = 'orders'
|
||||
paginate_by = 20
|
||||
|
||||
@@ -369,18 +403,6 @@ class ProfileView(CustomerRequiredMixin, ListView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['customer'] = self.request.customer
|
||||
ctx['memberships'] = self.request.customer.memberships.with_usages().select_related(
|
||||
'membership_type', 'granted_in', 'granted_in__order', 'granted_in__order__event'
|
||||
)
|
||||
ctx['invoice_addresses'] = InvoiceAddress.profiles.filter(customer=self.request.customer)
|
||||
ctx['is_paginated'] = True
|
||||
|
||||
for m in ctx['memberships']:
|
||||
if m.membership_type.max_usages:
|
||||
m.percent = int(m.usages / m.membership_type.max_usages * 100)
|
||||
else:
|
||||
m.percent = 0
|
||||
|
||||
s = OrderPosition.objects.filter(
|
||||
order=OuterRef('pk')
|
||||
@@ -404,7 +426,18 @@ class ProfileView(CustomerRequiredMixin, ListView):
|
||||
return ctx
|
||||
|
||||
|
||||
class MembershipUsageView(CustomerRequiredMixin, ListView):
|
||||
class MembershipView(CustomerAccountBaseMixin, ListView):
|
||||
template_name = 'pretixpresale/organizers/customer_memberships.html'
|
||||
context_object_name = 'memberships'
|
||||
paginate_by = 20
|
||||
|
||||
def get_queryset(self):
|
||||
return self.request.customer.memberships.with_usages().select_related(
|
||||
'membership_type', 'granted_in', 'granted_in__order', 'granted_in__order__event'
|
||||
)
|
||||
|
||||
|
||||
class MembershipUsageView(CustomerAccountBaseMixin, ListView):
|
||||
template_name = 'pretixpresale/organizers/customer_membership.html'
|
||||
context_object_name = 'usages'
|
||||
paginate_by = 20
|
||||
@@ -428,7 +461,16 @@ class MembershipUsageView(CustomerRequiredMixin, ListView):
|
||||
return ctx
|
||||
|
||||
|
||||
class AddressDeleteView(CustomerRequiredMixin, CompatDeleteView):
|
||||
class AddressView(CustomerAccountBaseMixin, ListView):
|
||||
template_name = 'pretixpresale/organizers/customer_addresses.html'
|
||||
context_object_name = 'invoice_addresses'
|
||||
paginate_by = 20
|
||||
|
||||
def get_queryset(self):
|
||||
return InvoiceAddress.profiles.filter(customer=self.request.customer)
|
||||
|
||||
|
||||
class AddressDeleteView(CustomerAccountBaseMixin, CompatDeleteView):
|
||||
template_name = 'pretixpresale/organizers/customer_address_delete.html'
|
||||
context_object_name = 'address'
|
||||
|
||||
@@ -436,10 +478,19 @@ class AddressDeleteView(CustomerRequiredMixin, CompatDeleteView):
|
||||
return get_object_or_404(InvoiceAddress.profiles, customer=self.request.customer, pk=self.kwargs.get('id'))
|
||||
|
||||
def get_success_url(self):
|
||||
return eventreverse(self.request.organizer, 'presale:organizer.customer.profile', kwargs={})
|
||||
return eventreverse(self.request.organizer, 'presale:organizer.customer.addresses', kwargs={})
|
||||
|
||||
|
||||
class ProfileDeleteView(CustomerRequiredMixin, CompatDeleteView):
|
||||
class ProfileView(CustomerAccountBaseMixin, ListView):
|
||||
template_name = 'pretixpresale/organizers/customer_profiles.html'
|
||||
context_object_name = 'attendee_profiles'
|
||||
paginate_by = 20
|
||||
|
||||
def get_queryset(self):
|
||||
return self.request.customer.attendee_profiles.all()
|
||||
|
||||
|
||||
class ProfileDeleteView(CustomerAccountBaseMixin, CompatDeleteView):
|
||||
template_name = 'pretixpresale/organizers/customer_profile_delete.html'
|
||||
context_object_name = 'profile'
|
||||
|
||||
@@ -447,10 +498,10 @@ class ProfileDeleteView(CustomerRequiredMixin, CompatDeleteView):
|
||||
return get_object_or_404(self.request.customer.attendee_profiles, pk=self.kwargs.get('id'))
|
||||
|
||||
def get_success_url(self):
|
||||
return eventreverse(self.request.organizer, 'presale:organizer.customer.profile', kwargs={})
|
||||
return eventreverse(self.request.organizer, 'presale:organizer.customer.profiles', kwargs={})
|
||||
|
||||
|
||||
class ChangePasswordView(CustomerRequiredMixin, FormView):
|
||||
class ChangePasswordView(CustomerAccountBaseMixin, FormView):
|
||||
template_name = 'pretixpresale/organizers/customer_password.html'
|
||||
form_class = ChangePasswordForm
|
||||
|
||||
@@ -465,7 +516,7 @@ class ChangePasswordView(CustomerRequiredMixin, FormView):
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_success_url(self):
|
||||
return eventreverse(self.request.organizer, 'presale:organizer.customer.profile', kwargs={})
|
||||
return eventreverse(self.request.organizer, 'presale:organizer.customer.index', kwargs={})
|
||||
|
||||
@transaction.atomic()
|
||||
def form_valid(self, form):
|
||||
@@ -483,7 +534,7 @@ class ChangePasswordView(CustomerRequiredMixin, FormView):
|
||||
return kwargs
|
||||
|
||||
|
||||
class ChangeInformationView(CustomerRequiredMixin, FormView):
|
||||
class ChangeInformationView(CustomerAccountBaseMixin, FormView):
|
||||
template_name = 'pretixpresale/organizers/customer_info.html'
|
||||
form_class = ChangeInfoForm
|
||||
|
||||
@@ -498,7 +549,7 @@ class ChangeInformationView(CustomerRequiredMixin, FormView):
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_success_url(self):
|
||||
return eventreverse(self.request.organizer, 'presale:organizer.customer.profile', kwargs={})
|
||||
return eventreverse(self.request.organizer, 'presale:organizer.customer.index', kwargs={})
|
||||
|
||||
def form_valid(self, form):
|
||||
if form.cleaned_data['email'] != self.initial_email and not self.request.customer.provider:
|
||||
@@ -581,7 +632,7 @@ class ConfirmChangeView(View):
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
|
||||
def get_success_url(self):
|
||||
return eventreverse(self.request.organizer, 'presale:organizer.customer.profile', kwargs={})
|
||||
return eventreverse(self.request.organizer, 'presale:organizer.customer.index', kwargs={})
|
||||
|
||||
|
||||
class SSOLoginView(RedirectBackMixin, View):
|
||||
@@ -641,7 +692,7 @@ class SSOLoginView(RedirectBackMixin, View):
|
||||
url = self.get_redirect_url()
|
||||
|
||||
if not url:
|
||||
return eventreverse(self.request.organizer, 'presale:organizer.customer.profile', kwargs={})
|
||||
return eventreverse(self.request.organizer, 'presale:organizer.customer.index', kwargs={})
|
||||
return url
|
||||
|
||||
|
||||
@@ -864,7 +915,7 @@ class SSOLoginReturnView(RedirectBackMixin, View):
|
||||
url = self.get_redirect_url(redirect_to)
|
||||
|
||||
if not url:
|
||||
return eventreverse(self.request.organizer, 'presale:organizer.customer.profile', kwargs={})
|
||||
return eventreverse(self.request.organizer, 'presale:organizer.customer.index', kwargs={})
|
||||
else:
|
||||
if self.request.session.get(f'pretix_customerauth_{self.provider.pk}_cross_domain_requested'):
|
||||
otpstore = SessionStore()
|
||||
|
||||
@@ -726,7 +726,11 @@ PASSWORD_HASHERS = [
|
||||
# the HistoricPassword model will not be changed automatically. In case a serious issue with a hasher
|
||||
# comes to light, dropping the contents of the HistoricPassword table might be the more risk-adequate
|
||||
# decision.
|
||||
"django.contrib.auth.hashers.Argon2PasswordHasher",
|
||||
*(
|
||||
["django.contrib.auth.hashers.Argon2PasswordHasher"]
|
||||
if config.getboolean('django', 'passwords_argon2', fallback=True)
|
||||
else []
|
||||
),
|
||||
"django.contrib.auth.hashers.PBKDF2PasswordHasher",
|
||||
"django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher",
|
||||
"django.contrib.auth.hashers.BCryptSHA256PasswordHasher",
|
||||
|
||||
@@ -20,7 +20,7 @@ $(function () {
|
||||
xhr.abort();
|
||||
}
|
||||
for (var k in dependents) dependents[k].prop("disabled", true);
|
||||
loader.fadeIn();
|
||||
loader.show();
|
||||
xhr = $.getJSON(url + '?country=' + dependency.val(), function (data) {
|
||||
var selected_value = dependents.state.prop("data-selected-value");
|
||||
if (selected_value) dependents.state.prop("data-selected-value", "");
|
||||
@@ -43,9 +43,17 @@ $(function () {
|
||||
}
|
||||
for (var k in dependents) dependents[k].prop("disabled", false);
|
||||
}).always(function() {
|
||||
loader.fadeOut();
|
||||
loader.hide();
|
||||
}).fail(function(){
|
||||
// TODO: handle failed request
|
||||
// In case of errors, show everything and require nothing, we can still handle errors in backend
|
||||
for(var k in dependents) {
|
||||
const dependent = dependents[k],
|
||||
visible = true,
|
||||
required = false;
|
||||
|
||||
dependent.closest(".form-group").toggle(visible).toggleClass('required', required);
|
||||
dependent.prop("required", required);
|
||||
}
|
||||
});
|
||||
};
|
||||
dependents.state.prop("data-selected-value", dependents.state.val());
|
||||
|
||||
@@ -8,7 +8,7 @@ $gray-lightest: lighten(#000, 97.25%);
|
||||
|
||||
$font-family-sans-serif: var(--pretix-font-family-sans-serif);
|
||||
$text-color: #222222 !default;
|
||||
$text-muted: #767676 !default;
|
||||
$text-muted: #737373 !default;
|
||||
$input-color-placeholder: lighten(#000, 70%) !default;
|
||||
|
||||
$border-radius-base: var(--pretix-border-radius-base);
|
||||
@@ -122,29 +122,29 @@ $label-warning-bg-hover: var(--pretix-brand-warning-darken-10);
|
||||
$label-danger-bg: var(--pretix-brand-danger);
|
||||
$label-danger-bg-hover: var(--pretix-brand-danger-darken-10);
|
||||
|
||||
$alert-success-bg: var(--pretix-brand-success-lighten-48);
|
||||
$alert-success-text: var(--pretix-brand-success-darken-10);
|
||||
$alert-success-bg: var(--pretix-brand-success-tint-85);
|
||||
$alert-success-text: var(--pretix-brand-success-shade-42);
|
||||
$alert-success-border: var(--pretix-brand-success);
|
||||
$alert-success-hr: var(--pretix-brand-success-darken-5);
|
||||
$alert-success-link: var(--pretix-brand-success-darken-20);
|
||||
$alert-success-link: var(--pretix-brand-success-shade-42);
|
||||
|
||||
$alert-info-bg: var(--pretix-brand-info-lighten-33);
|
||||
$alert-info-text: var(--pretix-brand-info-darken-20);
|
||||
$alert-info-bg: var(--pretix-brand-info-tint-85);
|
||||
$alert-info-text: var(--pretix-brand-info-shade-42);
|
||||
$alert-info-border: var(--pretix-brand-info);
|
||||
$alert-info-hr: var(--pretix-brand-info-darken-5);
|
||||
$alert-info-link: var(--pretix-brand-info-darken-30);
|
||||
$alert-info-link: var(--pretix-brand-info-shade-42);
|
||||
|
||||
$alert-warning-bg: var(--pretix-brand-warning-lighten-41);
|
||||
$alert-warning-text: var(--pretix-brand-warning-darken-25);
|
||||
$alert-warning-bg: var(--pretix-brand-warning-tint-85);
|
||||
$alert-warning-text: var(--pretix-brand-warning-shade-42);
|
||||
$alert-warning-border: var(--pretix-brand-warning);
|
||||
$alert-warning-hr: var(--pretix-brand-warning-darken-5);
|
||||
$alert-warning-link: var(--pretix-brand-warning-darken-35);
|
||||
$alert-warning-link: var(--pretix-brand-warning-shade-42);
|
||||
|
||||
$alert-danger-bg: var(--pretix-brand-danger-lighten-43);
|
||||
$alert-danger-text: var(--pretix-brand-danger-darken-5);
|
||||
$alert-danger-bg: var(--pretix-brand-danger-tint-85);
|
||||
$alert-danger-text: var(--pretix-brand-danger-shade-42);
|
||||
$alert-danger-border: var(--pretix-brand-danger);
|
||||
$alert-danger-hr: var(--pretix-brand-danger-darken-5);
|
||||
$alert-danger-link: var(--pretix-brand-danger-darken-15);
|
||||
$alert-danger-link: var(--pretix-brand-danger-shade-42);
|
||||
|
||||
$main-box-bg: #FFFFFF;
|
||||
|
||||
@@ -156,4 +156,4 @@ $slider-secondary-bottom: var(--pretix-brand-primary-lighten-23-saturate-2);
|
||||
/* The following vars default to $body-bg in bootstrap, which we don't want */
|
||||
$nav-tabs-active-link-hover-bg: #FFFFFF;
|
||||
$nav-tabs-justified-active-link-border-color: #FFFFFF;
|
||||
$thumbnail-bg: #FFFFFF;
|
||||
$thumbnail-bg: #FFFFFF;
|
||||
|
||||
@@ -73,7 +73,9 @@ $in-border-radius-small: 2px !default;
|
||||
--pretix-brand-success-darken-20: #{darken($in-brand-success, 20%)};
|
||||
--pretix-brand-success-darken-30: #{darken($in-brand-success, 30%)};
|
||||
--pretix-brand-success-tint-50: #{tint($in-brand-success, 50%)};
|
||||
--pretix-brand-success-tint-85: #{tint($in-brand-success, 85%)};
|
||||
--pretix-brand-success-shade-25: #{shade($in-brand-success, 25%)};
|
||||
--pretix-brand-success-shade-42: #{shade($in-brand-success, 42%)};
|
||||
|
||||
--pretix-brand-info-lighten-23: #{lighten($in-brand-info, 23%)};
|
||||
--pretix-brand-info-lighten-25: #{lighten($in-brand-info, 25%)};
|
||||
@@ -84,7 +86,9 @@ $in-border-radius-small: 2px !default;
|
||||
--pretix-brand-info-darken-17: #{darken($in-brand-info, 17%)};
|
||||
--pretix-brand-info-darken-20: #{darken($in-brand-info, 20%)};
|
||||
--pretix-brand-info-darken-30: #{darken($in-brand-info, 30%)};
|
||||
--pretix-brand-info-tint-85: #{tint($in-brand-info, 85%)};
|
||||
--pretix-brand-info-shade-25: #{shade($in-brand-info, 25%)};
|
||||
--pretix-brand-info-shade-42: #{shade($in-brand-info, 42%)};
|
||||
|
||||
--pretix-brand-warning-lighten-12: #{lighten($in-brand-warning, 12%)};
|
||||
--pretix-brand-warning-lighten-31: #{lighten($in-brand-warning, 31%)};
|
||||
@@ -101,7 +105,9 @@ $in-border-radius-small: 2px !default;
|
||||
--pretix-brand-warning-darken-30: #{darken($in-brand-warning, 30%)};
|
||||
--pretix-brand-warning-darken-35: #{darken($in-brand-warning, 35%)};
|
||||
--pretix-brand-warning-tint-50: #{tint($in-brand-warning, 50%)};
|
||||
--pretix-brand-warning-tint-85: #{tint($in-brand-warning, 85%)};
|
||||
--pretix-brand-warning-shade-25: #{shade($in-brand-warning, 25%)};
|
||||
--pretix-brand-warning-shade-42: #{shade($in-brand-warning, 42%)};
|
||||
--pretix-brand-warning-transparent-60: #{transparentize($in-brand-warning, 0.6)};
|
||||
|
||||
--pretix-brand-danger-lighten-5: #{lighten($in-brand-danger, 5%)};
|
||||
@@ -118,7 +124,9 @@ $in-border-radius-small: 2px !default;
|
||||
--pretix-brand-danger-darken-20: #{darken($in-brand-danger, 20%)};
|
||||
--pretix-brand-danger-darken-30: #{darken($in-brand-danger, 30%)};
|
||||
--pretix-brand-danger-tint-50: #{tint($in-brand-danger, 50%)};
|
||||
--pretix-brand-danger-tint-85: #{tint($in-brand-danger, 85%)};
|
||||
--pretix-brand-danger-shade-25: #{shade($in-brand-danger, 25%)};
|
||||
--pretix-brand-danger-shade-42: #{shade($in-brand-danger, 42%)};
|
||||
|
||||
--pretix-border-radius-base: #{$in-border-radius-base};
|
||||
--pretix-border-radius-large: #{$in-border-radius-large};
|
||||
@@ -140,4 +148,4 @@ $in-border-radius-small: 2px !default;
|
||||
--pretix-body-bg-white-0: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,9 +30,6 @@ $body-bg: #f5f5f5 !default;
|
||||
float: left;
|
||||
margin-right: .25em;
|
||||
}
|
||||
.status-dot {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.text-warning { color: $brand-warning; }
|
||||
.text-info { color: $brand-info; }
|
||||
.text-success { color: $brand-success; }
|
||||
@@ -98,6 +95,9 @@ footer nav .btn-link {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
}
|
||||
.subnav a.active {
|
||||
font-weight: bold;
|
||||
}
|
||||
.locales ul {
|
||||
display: inline;
|
||||
list-style: none;
|
||||
@@ -499,6 +499,9 @@ h2 .label {
|
||||
&.availability .progress-bar-success {
|
||||
background: var(--pretix-brand-success-lighten-20);
|
||||
}
|
||||
&.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-tabs {
|
||||
@@ -527,6 +530,76 @@ h2 .label {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
|
||||
.textbubble-success, .textbubble-info, .textbubble-warning, .textbubble-danger {
|
||||
padding: 0 .4em;
|
||||
border-radius: $border-radius-base;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
|
||||
&:has(>.fa:first-child) {
|
||||
padding-left: .25em;
|
||||
}
|
||||
}
|
||||
.textbubble-success {
|
||||
color: var(--pretix-brand-success-shade-42);
|
||||
background: var(--pretix-brand-success-tint-85);
|
||||
.fa {
|
||||
color: var(--pretix-brand-success);
|
||||
}
|
||||
}
|
||||
.textbubble-info {
|
||||
color: var(--pretix-brand-info-shade-42);
|
||||
background: var(--pretix-brand-info-tint-85);
|
||||
.fa {
|
||||
color: var(--pretix-brand-info);
|
||||
}
|
||||
}
|
||||
.textbubble-warning {
|
||||
color: var(--pretix-brand-warning-shade-42);
|
||||
background: var(--pretix-brand-warning-tint-85);
|
||||
.fa {
|
||||
color: var(--pretix-brand-warning);
|
||||
}
|
||||
}
|
||||
.textbubble-danger {
|
||||
color: var(--pretix-brand-danger-shade-42);
|
||||
background: var(--pretix-brand-danger-tint-85);
|
||||
.fa {
|
||||
color: var(--pretix-brand-danger);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.full-width-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
padding: 15px 0;
|
||||
}
|
||||
li+li {
|
||||
border-top: 1px solid $table-border-color;
|
||||
}
|
||||
}
|
||||
.panel-body:has(>.full-width-list:first-child) {
|
||||
padding-top: 0;
|
||||
}
|
||||
.panel-body:has(>.full-width-list:last-child) {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.alternating-rows .row:nth-child(even) {
|
||||
background-color: $table-bg-accent;
|
||||
}
|
||||
|
||||
|
||||
.account-addresses address::first-line {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
@import "_iframe.scss";
|
||||
@import "_a11y.scss";
|
||||
@import "_print.scss";
|
||||
|
||||
32
src/tests/base/test_questions.py
Normal file
32
src/tests/base/test_questions.py
Normal file
@@ -0,0 +1,32 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-2021 rami.io GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from pretix.base.forms.questions import name_parts_is_empty
|
||||
|
||||
|
||||
def test_name_parts_is_empty():
|
||||
assert name_parts_is_empty({}) is True
|
||||
assert name_parts_is_empty({"_scheme": "foo"}) is True
|
||||
assert name_parts_is_empty({"_scheme": "foo", "full_name": ""}) is True
|
||||
assert name_parts_is_empty({"full_name": None}) is True
|
||||
assert name_parts_is_empty({"full_name": "Flora Nord"}) is False
|
||||
assert name_parts_is_empty({"_scheme": "foo", "given_name": "Alice"}) is False
|
||||
assert name_parts_is_empty({"_legacy": "Alice"}) is False
|
||||
@@ -763,6 +763,7 @@ class EventsTest(SoupTest):
|
||||
'basics-location_1': 'Hamburg',
|
||||
'basics-currency': 'EUR',
|
||||
'basics-tax_rate': '',
|
||||
'basics-no_taxes': 'on',
|
||||
'basics-locale': 'en',
|
||||
'basics-timezone': 'Europe/Berlin',
|
||||
'basics-presale_start': '2016-11-01 10:00:00',
|
||||
@@ -792,6 +793,7 @@ class EventsTest(SoupTest):
|
||||
'basics-location_1': 'Hamburg',
|
||||
'basics-currency': 'EUR',
|
||||
'basics-tax_rate': '',
|
||||
'basics-no_taxes': 'on',
|
||||
'basics-locale': 'en',
|
||||
'basics-timezone': 'Europe/Berlin',
|
||||
'basics-presale_start_0': '2016-11-01',
|
||||
@@ -888,6 +890,7 @@ class EventsTest(SoupTest):
|
||||
'basics-location_1': 'Hamburg',
|
||||
'basics-currency': 'EUR',
|
||||
'basics-tax_rate': '',
|
||||
'basics-no_taxes': 'on',
|
||||
'basics-locale': 'en',
|
||||
'basics-timezone': 'Europe/Berlin',
|
||||
'basics-presale_start_0': '2016-11-01',
|
||||
@@ -1073,6 +1076,7 @@ class EventsTest(SoupTest):
|
||||
'basics-location_0': 'Hamburg',
|
||||
'basics-currency': 'EUR',
|
||||
'basics-tax_rate': '',
|
||||
'basics-no_taxes': 'on',
|
||||
'basics-locale': 'en',
|
||||
'basics-timezone': 'UTC',
|
||||
'basics-presale_start_0': '',
|
||||
@@ -1121,6 +1125,7 @@ class EventsTest(SoupTest):
|
||||
'basics-location_0': 'Hamburg',
|
||||
'basics-currency': 'EUR',
|
||||
'basics-tax_rate': '',
|
||||
'basics-no_taxes': 'on',
|
||||
'basics-locale': 'en',
|
||||
'basics-timezone': 'UTC',
|
||||
'basics-presale_start_0': '',
|
||||
@@ -1171,6 +1176,7 @@ class EventsTest(SoupTest):
|
||||
'basics-location_0': 'Hamburg',
|
||||
'basics-currency': 'EUR',
|
||||
'basics-tax_rate': '',
|
||||
'basics-no_taxes': 'on',
|
||||
'basics-locale': 'en',
|
||||
'basics-timezone': 'Europe/Berlin',
|
||||
'basics-presale_start_0': '2016-11-01',
|
||||
@@ -1200,6 +1206,7 @@ class EventsTest(SoupTest):
|
||||
'basics-location_0': 'Hamburg',
|
||||
'basics-currency': '$',
|
||||
'basics-tax_rate': '',
|
||||
'basics-no_taxes': 'on',
|
||||
'basics-locale': 'en',
|
||||
'basics-timezone': 'Europe/Berlin',
|
||||
'basics-presale_start_0': '2016-11-01',
|
||||
@@ -1229,6 +1236,7 @@ class EventsTest(SoupTest):
|
||||
'basics-location_0': 'Hamburg',
|
||||
'basics-currency': 'ASD',
|
||||
'basics-tax_rate': '',
|
||||
'basics-no_taxes': 'on',
|
||||
'basics-locale': 'en',
|
||||
'basics-timezone': 'Europe/Berlin',
|
||||
'basics-presale_start_0': '2016-11-01',
|
||||
|
||||
@@ -433,7 +433,7 @@ def test_org_sso_login_new_customer_email_conflict(env, client, provider):
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize("url", [
|
||||
"account/change",
|
||||
"account/membership/1/",
|
||||
"account/memberships/1/",
|
||||
"account/",
|
||||
])
|
||||
def test_login_required(client, env, url):
|
||||
|
||||
Reference in New Issue
Block a user