Compare commits

..
Author SHA1 Message Date
Raphael Michelandrobbi5 9150b4b271 Update doc/development/nfc/uid.rst
Co-authored-by: robbi5 <richt@rami.io>
2023-07-26 13:16:22 +02:00
Raphael Michel 20c8d8e01d Add a . 2023-07-26 13:11:39 +02:00
Raphael Michel f04d7a7274 Add documentation on NFC support 2023-07-26 13:10:36 +02:00
107 changed files with 11772 additions and 69888 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
- uses: actions/checkout@v2
- uses: harmon758/postgresql-action@v1
with:
postgresql version: '15'
postgresql version: '11'
postgresql db: 'pretix'
postgresql user: 'postgres'
postgresql password: 'postgres'
+1 -1
View File
@@ -1,4 +1,4 @@
from pretix.settings import *
LOGGING['handlers']['mail_admins']['include_html'] = True
STORAGES["staticfiles"]["BACKEND"] = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
+1 -1
View File
@@ -34,7 +34,7 @@ Checking a ticket in
:<json string secret: Scanned QR code corresponding to the ``secret`` attribute of a ticket.
:<json string source_type: Type of source the ``secret`` was obtained form. Defaults to ``"barcode"``.
:<json array lists: List of check-in list IDs to search on. No two check-in lists may be from the same event.
:<json string type: Send ``"exit"`` for an exit and ``"entry"`` (default) for an entry, ``"print"`` for a badge print scan.
:<json string type: Send ``"exit"`` for an exit and ``"entry"`` (default) for an entry.
:<json datetime datetime: Specifies the datetime of the check-in. If not supplied, the current time will be used.
:<json boolean force: Specifies that the check-in should succeed regardless of revoked barcode, previous check-ins or required
questions that have not been filled. This is usually used to upload offline scans that already happened,
+1 -1
View File
@@ -631,7 +631,7 @@ Order position endpoints
:<json boolean force: Specifies that the check-in should succeed regardless of revoked barcode, previous check-ins or required
questions that have not been filled. This is usually used to upload offline scans that already happened,
because there's no point in validating them since they happened whether they are valid or not. Defaults to ``false``.
:<json string type: Send ``"exit"`` for an exit and ``"entry"`` (default) for an entry, ``"print"`` for a badge print scan.
:<json string type: Send ``"exit"`` for an exit and ``"entry"`` (default) for an entry.
:<json boolean ignore_unpaid: Specifies that the check-in should succeed even if the order is in pending state.
Defaults to ``false`` and only works when ``include_pending`` is set on the check-in
list.
+1 -1
View File
@@ -37,7 +37,7 @@ you to execute a piece of code with a different locale:
This is very useful e.g. when sending an email to a user that has a different language than the user performing the
action that causes the mail to be sent.
.. _translation features: https://docs.djangoproject.com/en/4.2/topics/i18n/translation/
.. _translation features: https://docs.djangoproject.com/en/1.9/topics/i18n/translation/
.. _GNU gettext: https://www.gnu.org/software/gettext/
.. _strings: https://django-i18nfield.readthedocs.io/en/latest/strings.html
.. _database fields: https://django-i18nfield.readthedocs.io/en/latest/quickstart.html
+10 -23
View File
@@ -15,33 +15,25 @@ and the admin panel is available at ``https://pretix.eu/control/event/bigorg/awe
If the organizer now configures a custom domain like ``tickets.bigorg.com``, his event will
from now on be available on ``https://tickets.bigorg.com/awesomecon/``. The former URL at
``pretix.eu`` will redirect there. It's also possible to do this for just an event, in which
case the event will be available on ``https://tickets.awesomecon.org/``.
However, the admin panel will still only be available on ``pretix.eu`` for convenience and security reasons.
``pretix.eu`` will redirect there. However, the admin panel will still only be available
on ``pretix.eu`` for convenience and security reasons.
URL routing
-----------
The hard part about implementing this URL routing in Django is that
``https://pretix.eu/bigorg/awesomecon/`` contains two parameters of nearly arbitrary content
and ``https://tickets.bigorg.com/awesomecon/`` contains only one and ``https://tickets.awesomecon.org/`` does not contain any.
The only robust way to do this is by having *separate* URL configuration for those three cases.
and ``https://tickets.bigorg.com/awesomecon/`` contains only one. The only robust way to do
this is by having *separate* URL configuration for those two cases. In pretix, we call the
former our ``maindomain`` config and the latter our ``subdomain`` config. For pretix's core
modules we do some magic to avoid duplicate configuration, but for a fairly simple plugin with
only a handful of routes, we recommend just configuring the two URL sets separately.
In pretix, we therefore do not have a global URL configuration, but three, living in the following modules:
- ``pretix.multidomain.maindomain_urlconf``
- ``pretix.multidomain.organizer_domain_urlconf``
- ``pretix.multidomain.event_domain_urlconf``
We provide some helper utilities to work with these to avoid duplicate configuration of the individual URLs.
The file ``urls.py`` inside your plugin package will be loaded and scanned for URL configuration
automatically and should be provided by any plugin that provides any view.
However, unlike plain Django, we look not only for a ``urlpatterns`` attribute on the module but support other
attributes like ``event_patterns`` and ``organizer_patterns`` as well.
For example, for a simple plugin that adds one URL to the backend and one event-level URL to the frontend, you can
create the following configuration in your ``urls.py``::
A very basic example that provides one view in the admin panel and one view in the frontend
could look like this::
from django.urls import re_path
@@ -60,7 +52,7 @@ create the following configuration in your ``urls.py``::
As you can see, the view in the frontend is not included in the standard Django ``urlpatterns``
setting but in a separate list with the name ``event_patterns``. This will automatically prepend
the appropriate parameters to the regex (e.g. the event or the event and the organizer, depending
on the called domain). For organizer-level views, ``organizer_patterns`` works the same way.
on the called domain).
If you only provide URLs in the admin area, you do not need to provide a ``event_patterns`` attribute.
@@ -79,16 +71,11 @@ is a python method that emulates a behavior similar to ``reverse``:
.. autofunction:: pretix.multidomain.urlreverse.eventreverse
If you need to communicate the URL externally, you can use a different method to ensure that it is always an absolute URL:
.. autofunction:: pretix.multidomain.urlreverse.build_absolute_uri
In addition, there is a template tag that works similar to ``url`` but takes an event or organizer object
as its first argument and can be used like this::
{% load eventurl %}
<a href="{% eventurl request.event "presale:event.checkout" step="payment" %}">Pay</a>
<a href="{% abseventurl request.event "presale:event.checkout" step="payment" %}">Pay</a>
Implementation details
+1 -1
View File
@@ -36,7 +36,7 @@ dependencies = [
"css-inline==0.8.*",
"defusedcsv>=1.1.0",
"dj-static",
"Django==4.2.*",
"Django==4.1.*",
"django-bootstrap3==23.1.*",
"django-compressor==4.3.*",
"django-countries==7.5.*",
+1 -1
View File
@@ -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__ = "2023.8.0.dev0"
__version__ = "2023.7.0.dev0"
+1 -8
View File
@@ -196,14 +196,7 @@ STATICFILES_DIRS = [
STATICI18N_ROOT = os.path.join(BASE_DIR, "pretix/static")
STORAGES = {
"default": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage",
},
}
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
# if os.path.exists(os.path.join(DATA_DIR, 'static')):
# STATICFILES_DIRS.insert(0, os.path.join(DATA_DIR, 'static'))
+5 -72
View File
@@ -27,7 +27,6 @@ from decimal import Decimal
import pycountry
from django.conf import settings
from django.core.files import File
from django.db import models
from django.db.models import F, Q
from django.utils.encoding import force_str
from django.utils.timezone import now
@@ -373,15 +372,11 @@ class PdfDataSerializer(serializers.Field):
self.context['vars_images'] = get_images(self.context['event'])
for k, f in self.context['vars'].items():
if 'evaluate_bulk' in f:
# Will be evaluated later by our list serializers
res[k] = (f['evaluate_bulk'], instance)
else:
try:
res[k] = f['evaluate'](instance, instance.order, ev)
except:
logger.exception('Evaluating PDF variable failed')
res[k] = '(error)'
try:
res[k] = f['evaluate'](instance, instance.order, ev)
except:
logger.exception('Evaluating PDF variable failed')
res[k] = '(error)'
if not hasattr(ev, '_cached_meta_data'):
ev._cached_meta_data = ev.meta_data
@@ -434,38 +429,6 @@ class PdfDataSerializer(serializers.Field):
return res
class OrderPositionListSerializer(serializers.ListSerializer):
def to_representation(self, data):
# We have a custom implementation of this method because PdfDataSerializer() might keep some elements unevaluated
# with a (callable, input) tuple. We'll loop over these entries and evaluate them bulk-wise to save on SQL queries.
if isinstance(self.parent, OrderSerializer) and isinstance(self.parent.parent, OrderListSerializer):
# Do not execute our custom code because it will be executed by OrderListSerializer later for the
# full result set.
return super().to_representation(data)
iterable = data.all() if isinstance(data, models.Manager) else data
data = []
evaluate_queue = defaultdict(list)
for item in iterable:
entry = self.child.to_representation(item)
if "pdf_data" in entry:
for k, v in entry["pdf_data"].items():
if isinstance(v, tuple) and callable(v[0]):
evaluate_queue[v[0]].append((v[1], entry, k))
data.append(entry)
for func, entries in evaluate_queue.items():
results = func([item for (item, entry, k) in entries])
for (item, entry, k), result in zip(entries, results):
entry["pdf_data"][k] = result
return data
class OrderPositionSerializer(I18nAwareModelSerializer):
checkins = CheckinSerializer(many=True, read_only=True)
answers = AnswerSerializer(many=True)
@@ -477,7 +440,6 @@ class OrderPositionSerializer(I18nAwareModelSerializer):
attendee_name = serializers.CharField(required=False)
class Meta:
list_serializer_class = OrderPositionListSerializer
model = OrderPosition
fields = ('id', 'order', 'positionid', 'item', 'variation', 'price', 'attendee_name', 'attendee_name_parts',
'company', 'street', 'zipcode', 'city', 'country', 'state', 'discount',
@@ -651,34 +613,6 @@ class OrderURLField(serializers.URLField):
})
class OrderListSerializer(serializers.ListSerializer):
def to_representation(self, data):
# We have a custom implementation of this method because PdfDataSerializer() might keep some elements
# unevaluated with a (callable, input) tuple. We'll loop over these entries and evaluate them bulk-wise to
# save on SQL queries.
iterable = data.all() if isinstance(data, models.Manager) else data
data = []
evaluate_queue = defaultdict(list)
for item in iterable:
entry = self.child.to_representation(item)
for p in entry.get("positions", []):
if "pdf_data" in p:
for k, v in p["pdf_data"].items():
if isinstance(v, tuple) and callable(v[0]):
evaluate_queue[v[0]].append((v[1], p, k))
data.append(entry)
for func, entries in evaluate_queue.items():
results = func([item for (item, entry, k) in entries])
for (item, entry, k), result in zip(entries, results):
entry["pdf_data"][k] = result
return data
class OrderSerializer(I18nAwareModelSerializer):
invoice_address = InvoiceAddressSerializer(allow_null=True)
positions = OrderPositionSerializer(many=True, read_only=True)
@@ -693,7 +627,6 @@ class OrderSerializer(I18nAwareModelSerializer):
class Meta:
model = Order
list_serializer_class = OrderListSerializer
fields = (
'code', 'status', 'testmode', 'secret', 'email', 'phone', 'locale', 'datetime', 'expires', 'payment_date',
'payment_provider', 'fees', 'total', 'comment', 'custom_followup_at', 'invoice_address', 'positions', 'downloads',
+1
View File
@@ -166,6 +166,7 @@ class InitializeView(APIView):
device.software_brand = serializer.validated_data.get('software_brand')
device.software_version = serializer.validated_data.get('software_version')
device.info = serializer.validated_data.get('info')
print(serializer.validated_data, request.data)
device.rsa_pubkey = serializer.validated_data.get('rsa_pubkey')
device.api_token = generate_api_token()
device.save()
-2
View File
@@ -271,8 +271,6 @@ class SecurityMiddleware(MiddlewareMixin):
(url.url_name == "event.checkout" and url.kwargs['step'] == "payment")
):
h['script-src'].append('https://pay.google.com')
h['frame-src'].append('https://pay.google.com')
h['connect-src'].append('https://google.com/pay')
if settings.LOG_CSP:
h['report-uri'] = ["/csp_report/"]
if 'Content-Security-Policy' in resp:
+1 -1
View File
@@ -97,7 +97,7 @@ def _transactions_mark_order_dirty(order_id, using=None):
if getattr(dirty_transactions, 'order_ids', None) is None:
dirty_transactions.order_ids = set()
if _check_for_dirty_orders not in [func for (savepoint_id, func, *__) in conn.run_on_commit]:
if _check_for_dirty_orders not in [func for savepoint_id, func in conn.run_on_commit]:
transaction.on_commit(_check_for_dirty_orders, using)
dirty_transactions.order_ids.clear() # This is necessary to clean up after old threads with rollbacked transactions
+1 -3
View File
@@ -179,7 +179,7 @@ class CheckinList(LoggedModel):
# dedupliate by position and count it up.
cl = self
base_q, base_params = (
Checkin.all.filter(*c_q, successful=True, list=cl, type__in=[Checkin.TYPE_ENTRY, Checkin.TYPE_EXIT])
Checkin.all.filter(*c_q, successful=True, list=cl)
.annotate(
cnt_exists_after=Window(
expression=Count("position_id", filter=Q(type=Value("exit"))),
@@ -322,11 +322,9 @@ class Checkin(models.Model):
"""
TYPE_ENTRY = 'entry'
TYPE_EXIT = 'exit'
TYPE_PRINT = 'print'
CHECKIN_TYPES = (
(TYPE_ENTRY, _('Entry')),
(TYPE_EXIT, _('Exit')),
(TYPE_PRINT, _('Print')),
)
REASON_CANCELED = 'canceled'
+3 -1
View File
@@ -88,7 +88,9 @@ class LogEntry(models.Model):
class Meta:
ordering = ('-datetime', '-id')
indexes = [models.Index(fields=["datetime", "id"])]
index_together = [
['datetime', 'id']
]
def display(self):
from ..signals import logentry_display
+1 -4
View File
@@ -121,10 +121,7 @@ class ReusableMedium(LoggedModel):
class Meta:
unique_together = (("identifier", "type", "organizer"),)
indexes = [
models.Index(fields=("identifier", "type", "organizer")),
models.Index(fields=("updated", "id")),
]
index_together = (("identifier", "type", "organizer"), ("updated", "id"))
ordering = "identifier", "type", "organizer"
+6 -11
View File
@@ -270,9 +270,9 @@ class Order(LockModel, LoggedModel):
verbose_name = _("Order")
verbose_name_plural = _("Orders")
ordering = ("-datetime", "-pk")
indexes = [
models.Index(fields=["datetime", "id"]),
models.Index(fields=["last_modified", "id"]),
index_together = [
["datetime", "id"],
["last_modified", "id"],
]
def __str__(self):
@@ -907,11 +907,6 @@ class Order(LockModel, LoggedModel):
return self.expires
expires = self.expires.date() + timedelta(days=delay)
if self.event.settings.get('payment_term_weekdays'):
if expires.weekday() == 5:
expires += timedelta(days=2)
elif expires.weekday() == 6:
expires += timedelta(days=1)
tz = ZoneInfo(self.event.settings.timezone)
expires = make_aware(datetime.combine(
@@ -1676,7 +1671,7 @@ class OrderPayment(models.Model):
"""
Marks the order as failed and sets info to ``info``, but only if the order is in ``created`` or ``pending``
state. This is equivalent to setting ``state`` to ``OrderPayment.PAYMENT_STATE_FAILED`` and logging a failure,
but it adds strong database locking since we do not want to report a failure for an order that has just
but it adds strong database logging since we do not want to report a failure for an order that has just
been marked as paid.
:param send_mail: Whether an email should be sent to the user about this event (default: ``True``).
"""
@@ -2756,8 +2751,8 @@ class Transaction(models.Model):
class Meta:
ordering = 'datetime', 'pk'
indexes = [
models.Index(fields=['datetime', 'id'])
index_together = [
['datetime', 'id']
]
def save(self, *args, **kwargs):
+1 -1
View File
@@ -805,7 +805,7 @@ class QuestionColumn(ImportColumn):
return self.q.clean_answer(value)
def assign(self, value, order, position, invoice_address, **kwargs):
if value is not None:
if value:
if not hasattr(order, '_answers'):
order._answers = []
if isinstance(value, QuestionOption):
+4 -4
View File
@@ -773,7 +773,7 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
'blocked'
)
if type not in (Checkin.TYPE_PRINT, Checkin.TYPE_EXIT) and op.valid_from and op.valid_from > dt:
if type != Checkin.TYPE_EXIT and op.valid_from and op.valid_from > dt:
if force:
force_used = True
else:
@@ -787,7 +787,7 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
),
)
if type not in (Checkin.TYPE_PRINT, Checkin.TYPE_EXIT) and op.valid_until and op.valid_until < dt:
if type != Checkin.TYPE_EXIT and op.valid_until and op.valid_until < dt:
if force:
force_used = True
else:
@@ -886,9 +886,9 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
if isinstance(auth, Device):
device = auth
last_cis = list(op.checkins.order_by('-datetime').filter(list=clist, type__in=(Checkin.TYPE_ENTRY, Checkin.TYPE_EXIT)).only('type', 'nonce'))
last_cis = list(op.checkins.order_by('-datetime').filter(list=clist).only('type', 'nonce'))
entry_allowed = (
type in (Checkin.TYPE_EXIT, Checkin.TYPE_PRINT) or
type == Checkin.TYPE_EXIT or
clist.allow_multiple_entries or
not last_cis or
all(c.type == Checkin.TYPE_EXIT for c in last_cis) or
-5
View File
@@ -2476,11 +2476,6 @@ class OrderChangeManager:
split_order.status = Order.STATUS_PAID
else:
split_order.status = Order.STATUS_PENDING
if self.order.status == Order.STATUS_PAID:
split_order.set_expires(
now(),
list(set(p.subevent_id for p in split_positions))
)
split_order.save()
if offset_amount > Decimal('0.00'):
+3 -3
View File
@@ -941,9 +941,9 @@ DEFAULTS = {
'form_kwargs': dict(
label=_('Expiration delay'),
help_text=_("The order will only actually expire this many days after the expiration date communicated "
"to the customer. If you select \"Only end payment terms on weekdays\" above, this will also "
"be respected. However, this will not delay beyond the \"last date of payments\" "
"configured above, which is always enforced."),
"to the customer. However, this will not delay beyond the \"last date of payments\" "
"configured above, which is always enforced. The delay may also end on a weekend regardless "
"of the other settings above."),
# Every order in between the official expiry date and the delayed expiry date has a performance penalty
# for the cron job, so we limit this feature to 30 days to prevent arbitrary numbers of orders needing
# to be checked.
+1 -5
View File
@@ -683,16 +683,12 @@ dictionaries as values that contain keys like in the following example::
"product": {
"label": _("Product name"),
"editor_sample": _("Sample product"),
"evaluate": lambda orderposition, order, event: str(orderposition.item),
"evaluate_bulk": lambda orderpositions: [str(op.item) for op in orderpositions],
"evaluate": lambda orderposition, order, event: str(orderposition.item)
}
}
The ``evaluate`` member will be called with the order position, order and event as arguments. The event might
also be a subevent, if applicable.
The ``evaluate_bulk`` member is optional but can significantly improve performance in some situations because you
can perform database fetches in bulk instead of single queries for every position.
"""
+2 -2
View File
@@ -1940,7 +1940,7 @@ class VoucherFilterForm(FilterForm):
'item__category__position',
'item__category',
'item__position',
'variation__position',
'item__variation__position',
'quota__name',
),
'subevent': 'subevent__date_from',
@@ -1950,7 +1950,7 @@ class VoucherFilterForm(FilterForm):
'-item__category__position',
'-item__category',
'-item__position',
'-variation__position',
'-item__variation__position',
'-quota__name',
)
}
-15
View File
@@ -295,20 +295,6 @@ def _display_checkin(event, logentry):
posid=data.get('positionid'),
list=checkin_list
)
if data.get('type') == Checkin.TYPE_PRINT:
if show_dt:
return _('Position #{posid} has been printed out at {datetime} for list "{list}".').format(
posid=data.get('positionid'),
datetime=dt_formatted,
list=checkin_list
)
else:
return _('Position #{posid} has been printed out for list "{list}".').format(
posid=data.get('positionid'),
list=checkin_list
)
if data.get('first'):
if show_dt:
return _('Position #{posid} has been checked in at {datetime} for list "{list}".').format(
@@ -355,7 +341,6 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
'pretix.giftcards.acceptance.added': _('Gift card acceptance for another organizer has been added.'),
'pretix.giftcards.acceptance.removed': _('Gift card acceptance for another organizer has been removed.'),
'pretix.giftcards.acceptance.acceptor.invited': _('A new gift card acceptor has been invited.'),
'pretix.giftcards.acceptance.acceptor.removed': _('A gift card acceptor has been removed.'),
'pretix.giftcards.acceptance.issuer.removed': _('A gift card issuer has been removed or declined.'),
'pretix.giftcards.acceptance.issuer.accepted': _('A new gift card issuer has been accepted.'),
'pretix.webhook.created': _('The webhook has been created.'),
@@ -95,7 +95,6 @@
<td>
{% if c.type == "exit" %}<span class="fa fa-fw fa-sign-out"></span>{% endif %}
{% if c.type == "entry" %}<span class="fa fa-fw fa-sign-in"></span>{% endif %}
{% if c.type == "print" %}<span class="fa fa-fw fa-print"></span>{% endif %}
{{ c.get_type_display }}
<br>
<small>
@@ -187,7 +187,7 @@
{% endif %}
{% for f in plugin_forms %}
{% if f.is_layouts and not f.title %}
{% if f.template and not "template" in f.fields %}
{% if f.template %}
{% include f.template with form=f %}
{% else %}
{% bootstrap_form f layout="control" %}
@@ -261,7 +261,7 @@
{% bootstrap_field form.show_quota_left layout="control" %}
{% for f in plugin_forms %}
{% if not f.is_layouts and not f.title %}
{% if f.template and not "template" in f.fields %}
{% if f.template %}
{% include f.template with form=f %}
{% else %}
{% bootstrap_form f layout="control" %}
@@ -273,7 +273,7 @@
{% if not f.is_layouts and f.title %}
<fieldset>
<legend>{{ f.title }}</legend>
{% if f.template and not "template" in f.fields %}
{% if f.template %}
{% include f.template with form=f %}
{% else %}
{% bootstrap_form f layout="control" %}
@@ -385,8 +385,6 @@
{% else %}
<span class="fa fa-fw text-success fa-sign-out" data-toggle="tooltip_html" title="{{ c.list.name|force_escape|force_escape }}<br>{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Exit scan: {{ date }}{% endblocktrans %}{% if c.gate %}<br>{{ c.gate }}{% endif %}"></span>
{% endif %}
{% elif c.type == "print" %}
<span class="fa fa-fw text-success fa-print" data-toggle="tooltip_html" title="{{ c.list.name|force_escape|force_escape }}<br>{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Print scan: {{ date }}{% endblocktrans %}{% if c.gate %}<br>{{ c.gate }}{% endif %}"></span>
{% elif c.forced %}
<span class="fa fa-fw fa-warning text-warning" data-toggle="tooltip_html" title="{{ c.list.name|force_escape|force_escape }}<br>{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Additional entry scan: {{ date }}{% endblocktrans %}{% if c.gate %}<br>{{ c.gate }}{% endif %}"></span>
{% elif c.auto_checked_in %}
@@ -69,7 +69,7 @@
<td></td>
<td class="text-right flip">
<strong>
{{ sums.sum_count }}
{{ sums.count }}
</strong>
</td>
<td></td>
@@ -295,11 +295,6 @@
{% bootstrap_field sform.invoice_regenerate_allowed layout="control" %}
</fieldset>
</div>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
</button>
</div>
</div>
<div class="col-xs-12 col-lg-2">
<div class="panel panel-default">
@@ -312,5 +307,10 @@
</div>
</div>
</div>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
</button>
</div>
</form>
{% endblock %}
+2 -2
View File
@@ -783,8 +783,8 @@ class MailSettingsRendererPreview(MailSettingsPreview):
return ctx
def get(self, request, *args, **kwargs):
v = str(request.event.settings.mail_text_order_payment_failed)
v = format_map(v, self.placeholders('mail_text_order_payment_failed'))
v = str(request.event.settings.mail_text_order_placed)
v = format_map(v, self.placeholders('mail_text_order_placed'))
renderers = request.event.get_html_mail_renderers()
if request.GET.get('renderer') in renderers:
with rolledback_transaction():
+1 -1
View File
@@ -418,7 +418,7 @@ class OrderTransactions(OrderView):
'item', 'variation', 'subevent'
).order_by('datetime')
ctx['sums'] = self.order.transactions.aggregate(
sum_count=Sum('count'),
count=Sum('count'),
full_price=Sum(F('count') * F('price')),
full_tax_value=Sum(F('count') * F('tax_value')),
)
+2 -2
View File
@@ -718,7 +718,7 @@ def itemvarquota_select2(request, **kwargs):
itemqs = request.event.items.prefetch_related('variations').filter(
Q(name__icontains=i18ncomp(query)) | Q(internal_name__icontains=query)
)
quotaqs = request.event.quotas.filter(quotaf).select_related('subevent').order_by('-subevent__date_from', 'name')
quotaqs = request.event.quotas.filter(quotaf).select_related('subevent')
more = False
else:
if page == 1:
@@ -727,7 +727,7 @@ def itemvarquota_select2(request, **kwargs):
)
else:
itemqs = request.event.items.none()
quotaqs = request.event.quotas.filter(name__icontains=query).select_related('subevent').order_by('-subevent__date_from', 'name')
quotaqs = request.event.quotas.filter(name__icontains=query).select_related('subevent')
total = quotaqs.count()
pagesize = 20
offset = (page - 1) * pagesize
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-27 11:50+0000\n"
"POT-Creation-Date: 2023-07-21 13:02+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
+6 -4
View File
@@ -7,7 +7,7 @@ msgstr ""
"Project-Id-Version: French\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
"PO-Revision-Date: 2023-08-02 02:00+0000\n"
"PO-Revision-Date: 2023-07-19 17:00+0000\n"
"Last-Translator: Ronan LE MEILLAT <ronan.le_meillat@highcanfly.club>\n"
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix-js/"
"fr/>\n"
@@ -16,7 +16,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 4.18.2\n"
"X-Generator: Weblate 4.17\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
@@ -63,7 +63,7 @@ msgstr "iDEAL"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:42
msgid "SEPA Direct Debit"
msgstr "Prélèvement SEPA"
msgstr "Débit direct SEPA"
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:43
msgid "Bancontact"
@@ -679,8 +679,10 @@ msgid "Your local time:"
msgstr "Votre heure locale :"
#: pretix/static/pretixpresale/js/walletdetection.js:39
#, fuzzy
#| msgid "Apple Pay"
msgid "Google Pay"
msgstr "Google Pay"
msgstr "Apple Pay"
#: pretix/static/pretixpresale/js/widget/widget.js:17
msgctxt "widget"
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
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -14
View File
@@ -77,8 +77,6 @@ def get_event_domain(event, fallback=False, return_info=False):
def get_organizer_domain(organizer):
assert isinstance(organizer, Organizer)
if not organizer.pk:
return None
domain = getattr(organizer, '_cached_domain', None) or organizer.cache.get('domain')
if domain is None:
domains = organizer.domains.filter(event__isnull=True)
@@ -128,7 +126,7 @@ def eventreverse(obj, name, kwargs=None):
:param kwargs: A dictionary of additional keyword arguments that should be used. You do not
need to provide the organizer or event slug here, it will be added automatically as
needed.
:returns: An absolute or relative URL as a string
:returns: An absolute URL (including scheme and host) as a string
"""
from pretix.multidomain import (
event_domain_urlconf, maindomain_urlconf, organizer_domain_urlconf,
@@ -177,17 +175,6 @@ def eventreverse(obj, name, kwargs=None):
def build_absolute_uri(obj, urlname, kwargs=None):
"""
Works similar to ``eventreverse`` but always returns an absolute URL.
:param obj: An ``Event`` or ``Organizer`` object
:param name: The name of the URL route
:type name: str
:param kwargs: A dictionary of additional keyword arguments that should be used. You do not
need to provide the organizer or event slug here, it will be added automatically as
needed.
:returns: An absolute URL (including scheme and host) as a string
"""
reversedurl = eventreverse(obj, urlname, kwargs)
if '://' in reversedurl:
return reversedurl
+1 -3
View File
@@ -535,11 +535,9 @@ class BankTransfer(BasePaymentProvider):
'eu_barcodes': self.event.currency == 'EUR',
'pending_description': self.settings.get('pending_description', as_type=LazyI18nString),
'details': self.settings.get('bank_details', as_type=LazyI18nString),
'has_invoices': payment.order.invoices.exists(),
'invoice_email_enabled': self.settings.get('invoice_email', as_type=bool),
}
ctx['any_barcodes'] = ctx['swiss_qrbill'] or ctx['eu_barcodes']
return template.render(ctx, request=request)
return template.render(ctx)
def payment_control_render(self, request: HttpRequest, payment: OrderPayment) -> str:
warning = None
@@ -7,7 +7,6 @@
{% load money %}
{% load unidecode %}
{% load rich_text %}
{% load eventurl %}
{% if pending_description %}
{{ pending_description|rich_text }}
@@ -104,28 +103,3 @@ SCT
{% if swiss_qrbill %}
<link rel="stylesheet" href="{% static "pretixplugins/banktransfer/swisscross.css" %}">
{% endif %}
{% if invoice_email_enabled and has_invoices %}
<form method="post" action="{% eventurl event "plugins:banktransfer:mail_invoice" order=order.code secret=order.secret %}">
{% csrf_token %}
<p>
{% blocktrans trimmed %}
To send the invoice directly to your accounting department, please enter their email address:
{% endblocktrans %}
</p>
<div class="row">
<div class="col-md-9 col-xs-12">
<label for="mail_invoice_email" class="sr-only">{% trans "Invoice recipient email" %}:</label>
<input type="email" name="email" id="mail_invoice_email" class="form-control" value="" required
placeholder="{% trans "Email address" %}" />
</div>
<div class="col-md-3 col-xs-12">
<button class="btn btn-default btn-block">
<span class="fa fa-envelope-o" aria-hidden="true"></span>
{% trans "Send invoice via email" %}
</button>
</div>
</div>
</form>
<hr>
{% endif %}
+1 -7
View File
@@ -19,19 +19,13 @@
# 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.urls import include, re_path
from django.urls import re_path
from pretix.api.urls import orga_router
from pretix.plugins.banktransfer.api import BankImportJobViewSet
from . import views
event_patterns = [
re_path(r'^banktransfer/', include([
re_path(r'^(?P<order>[^/][^w]+)/(?P<secret>[A-Za-z0-9]+)/mail-invoice/$', views.SendInvoiceMailView.as_view(), name='mail_invoice'),
])),
]
urlpatterns = [
re_path(r'^control/organizer/(?P<organizer>[^/]+)/banktransfer/import/',
views.OrganizerImportView.as_view(),
+1 -40
View File
@@ -44,18 +44,14 @@ from typing import Set
from django import forms
from django.contrib import messages
from django.core.exceptions import ValidationError
from django.core.validators import validate_email
from django.db import transaction
from django.db.models import Count, Q, QuerySet
from django.http import FileResponse, Http404, JsonResponse
from django.http import FileResponse, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.utils.functional import cached_property
from django.utils.timezone import now
from django.utils.translation import gettext as _
from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.generic import DetailView, FormView, ListView, View
from django.views.generic.detail import SingleObjectMixin
from localflavor.generic.forms import BICFormField, IBANFormField
@@ -79,8 +75,6 @@ from pretix.plugins.banktransfer.refund_export import (
build_sepa_xml, get_refund_export_csv,
)
from pretix.plugins.banktransfer.tasks import process_banktransfers
from pretix.presale.views import EventViewMixin
from pretix.presale.views.order import OrderDetailMixin
logger = logging.getLogger('pretix.plugins.banktransfer')
@@ -892,36 +886,3 @@ class OrganizerSepaXMLExportView(OrganizerPermissionRequiredMixin, OrganizerDeta
organizer=self.request.organizer,
pk=self.kwargs.get('id')
)
@method_decorator(xframe_options_exempt, 'dispatch')
class SendInvoiceMailView(EventViewMixin, OrderDetailMixin, View):
def post(self, request, *args, **kwargs):
if not self.order:
raise Http404(_('Unknown order code or not authorized to access this order.'))
try:
validate_email(request.POST['email'])
except ValidationError:
messages.error(request, _('Please enter a valid email address.'))
return redirect(self.get_order_url())
last_payment = self.order.payments.last()
if (not last_payment
or last_payment.provider != BankTransfer.identifier
or last_payment.state != OrderPayment.PAYMENT_STATE_CREATED):
messages.error(request, _('No pending bank transfer payment found. Maybe the order has been paid already?'))
return redirect(self.get_order_url())
if not last_payment.payment_provider.settings.get('invoice_email', as_type=bool):
messages.error(request, _('Sending invoices via email is disabled by the event organizer.'))
return redirect(self.get_order_url())
last_invoice = self.order.invoices.last()
if not last_invoice:
messages.error(request, _('No invoice found, please request an invoice first.'))
return redirect(self.get_order_url())
provider = last_payment.payment_provider
provider.send_invoice_to_alternate_email(self.order, last_invoice, request.POST['email'])
messages.success(request, _('Sending the latest invoice via e-mail to {email}.').format(email=request.POST['email']))
return redirect(self.get_order_url())
@@ -5,9 +5,9 @@
<div class="panel panel-default items">
<div class="panel-heading">
<h3 class="panel-title">
<strong>{{ position.item.name }}</strong>
<strong>{{ position.item }}</strong>
{% if position.variation %}
– {{ position.variation.value }}
– {{ position.variation }}
{% endif %}
</h3>
</div>
+1 -9
View File
@@ -80,15 +80,7 @@
"DATE_INPUT_FORMATS": [
"%Y-%m-%d",
"%m/%d/%Y",
"%m/%d/%y",
"%b %d %Y",
"%b %d, %Y",
"%d %b %Y",
"%d %b, %Y",
"%B %d %Y",
"%B %d, %Y",
"%d %B %Y",
"%d %B, %Y"
"%m/%d/%y"
],
"DECIMAL_SEPARATOR": ".",
"FIRST_DAY_OF_WEEK": 0,
+69 -66
View File
@@ -8,7 +8,7 @@
"name": "pretix",
"version": "0.0.0",
"dependencies": {
"@babel/core": "^7.22.9",
"@babel/core": "^7.22.5",
"@babel/preset-env": "^7.22.9",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-node-resolve": "^15.1.0",
@@ -50,25 +50,25 @@
}
},
"node_modules/@babel/core": {
"version": "7.22.9",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz",
"integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz",
"integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==",
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.22.5",
"@babel/generator": "^7.22.9",
"@babel/helper-compilation-targets": "^7.22.9",
"@babel/helper-module-transforms": "^7.22.9",
"@babel/helpers": "^7.22.6",
"@babel/parser": "^7.22.7",
"@babel/generator": "^7.22.5",
"@babel/helper-compilation-targets": "^7.22.5",
"@babel/helper-module-transforms": "^7.22.5",
"@babel/helpers": "^7.22.5",
"@babel/parser": "^7.22.5",
"@babel/template": "^7.22.5",
"@babel/traverse": "^7.22.8",
"@babel/traverse": "^7.22.5",
"@babel/types": "^7.22.5",
"convert-source-map": "^1.7.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
"json5": "^2.2.2",
"semver": "^6.3.1"
"semver": "^6.3.0"
},
"engines": {
"node": ">=6.9.0"
@@ -98,9 +98,9 @@
}
},
"node_modules/@babel/generator": {
"version": "7.22.9",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz",
"integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz",
"integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==",
"dependencies": {
"@babel/types": "^7.22.5",
"@jridgewell/gen-mapping": "^0.3.2",
@@ -308,21 +308,21 @@
}
},
"node_modules/@babel/helper-module-transforms": {
"version": "7.22.9",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz",
"integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz",
"integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==",
"dependencies": {
"@babel/helper-environment-visitor": "^7.22.5",
"@babel/helper-module-imports": "^7.22.5",
"@babel/helper-simple-access": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6",
"@babel/helper-validator-identifier": "^7.22.5"
"@babel/helper-split-export-declaration": "^7.22.5",
"@babel/helper-validator-identifier": "^7.22.5",
"@babel/template": "^7.22.5",
"@babel/traverse": "^7.22.5",
"@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0"
}
},
"node_modules/@babel/helper-optimise-call-expression": {
@@ -447,12 +447,12 @@
}
},
"node_modules/@babel/helpers": {
"version": "7.22.6",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz",
"integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.5.tgz",
"integrity": "sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q==",
"dependencies": {
"@babel/template": "^7.22.5",
"@babel/traverse": "^7.22.6",
"@babel/traverse": "^7.22.5",
"@babel/types": "^7.22.5"
},
"engines": {
@@ -473,9 +473,9 @@
}
},
"node_modules/@babel/parser": {
"version": "7.22.7",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz",
"integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz",
"integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==",
"bin": {
"parser": "bin/babel-parser.js"
},
@@ -1631,17 +1631,17 @@
}
},
"node_modules/@babel/traverse": {
"version": "7.22.8",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz",
"integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz",
"integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==",
"dependencies": {
"@babel/code-frame": "^7.22.5",
"@babel/generator": "^7.22.7",
"@babel/generator": "^7.22.5",
"@babel/helper-environment-visitor": "^7.22.5",
"@babel/helper-function-name": "^7.22.5",
"@babel/helper-hoist-variables": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6",
"@babel/parser": "^7.22.7",
"@babel/helper-split-export-declaration": "^7.22.5",
"@babel/parser": "^7.22.5",
"@babel/types": "^7.22.5",
"debug": "^4.1.0",
"globals": "^11.1.0"
@@ -4160,25 +4160,25 @@
"integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ=="
},
"@babel/core": {
"version": "7.22.9",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz",
"integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz",
"integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==",
"requires": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.22.5",
"@babel/generator": "^7.22.9",
"@babel/helper-compilation-targets": "^7.22.9",
"@babel/helper-module-transforms": "^7.22.9",
"@babel/helpers": "^7.22.6",
"@babel/parser": "^7.22.7",
"@babel/generator": "^7.22.5",
"@babel/helper-compilation-targets": "^7.22.5",
"@babel/helper-module-transforms": "^7.22.5",
"@babel/helpers": "^7.22.5",
"@babel/parser": "^7.22.5",
"@babel/template": "^7.22.5",
"@babel/traverse": "^7.22.8",
"@babel/traverse": "^7.22.5",
"@babel/types": "^7.22.5",
"convert-source-map": "^1.7.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
"json5": "^2.2.2",
"semver": "^6.3.1"
"semver": "^6.3.0"
},
"dependencies": {
"json5": {
@@ -4194,9 +4194,9 @@
}
},
"@babel/generator": {
"version": "7.22.9",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz",
"integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz",
"integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==",
"requires": {
"@babel/types": "^7.22.5",
"@jridgewell/gen-mapping": "^0.3.2",
@@ -4355,15 +4355,18 @@
}
},
"@babel/helper-module-transforms": {
"version": "7.22.9",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz",
"integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz",
"integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==",
"requires": {
"@babel/helper-environment-visitor": "^7.22.5",
"@babel/helper-module-imports": "^7.22.5",
"@babel/helper-simple-access": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6",
"@babel/helper-validator-identifier": "^7.22.5"
"@babel/helper-split-export-declaration": "^7.22.5",
"@babel/helper-validator-identifier": "^7.22.5",
"@babel/template": "^7.22.5",
"@babel/traverse": "^7.22.5",
"@babel/types": "^7.22.5"
}
},
"@babel/helper-optimise-call-expression": {
@@ -4449,12 +4452,12 @@
}
},
"@babel/helpers": {
"version": "7.22.6",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz",
"integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.5.tgz",
"integrity": "sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q==",
"requires": {
"@babel/template": "^7.22.5",
"@babel/traverse": "^7.22.6",
"@babel/traverse": "^7.22.5",
"@babel/types": "^7.22.5"
}
},
@@ -4469,9 +4472,9 @@
}
},
"@babel/parser": {
"version": "7.22.7",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz",
"integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q=="
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz",
"integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q=="
},
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
"version": "7.22.5",
@@ -5224,17 +5227,17 @@
}
},
"@babel/traverse": {
"version": "7.22.8",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz",
"integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz",
"integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==",
"requires": {
"@babel/code-frame": "^7.22.5",
"@babel/generator": "^7.22.7",
"@babel/generator": "^7.22.5",
"@babel/helper-environment-visitor": "^7.22.5",
"@babel/helper-function-name": "^7.22.5",
"@babel/helper-hoist-variables": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6",
"@babel/parser": "^7.22.7",
"@babel/helper-split-export-declaration": "^7.22.5",
"@babel/parser": "^7.22.5",
"@babel/types": "^7.22.5",
"debug": "^4.1.0",
"globals": "^11.1.0"
+1 -1
View File
@@ -4,7 +4,7 @@
"private": true,
"scripts": {},
"dependencies": {
"@babel/core": "^7.22.9",
"@babel/core": "^7.22.5",
"@babel/preset-env": "^7.22.9",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-node-resolve": "^15.1.0",
+2 -11
View File
@@ -4,18 +4,9 @@ setup_collapsible_details = function (el) {
el.find('details.sneak-peek:not([open])').each(function() {
this.open = true;
var $elements = $("> :not(summary)", this).show().filter(':not(.sneak-peek-trigger)');
var $elements = $("> :not(summary)", this).show().filter(':not(.sneak-peek-trigger)').attr('aria-hidden', 'true');
var container = this;
if ($("> :not(summary)", this).show().filter(':not(.sneak-peek-trigger)').height() < 200) {
$(".sneak-peek-trigger", this).remove();
$(container).removeClass('sneak-peek');
container.style.removeProperty('height');
return;
}
$elements.attr('aria-hidden', 'true');
var trigger = $('summary, .sneak-peek-trigger button', container);
function onclick(e) {
e.preventDefault();
@@ -1,7 +1,7 @@
/*global $,u2f */
$(function () {
$('.sidebar .dropdown, ul.navbar-nav .dropdown, .navbar-events-collapse').on('shown.bs.collapse shown.bs.dropdown', function () {
$(this).parent().find("input").val("").trigger('forceRunQuery').focus();
$(this).parent().find("input").val("").change().focus();
});
$('.dropdown-menu .form-box input').click(function (e) {
e.stopPropagation();
@@ -11,22 +11,11 @@ $(function () {
var $container = $(this);
var $query = $(this).find('[data-typeahead-query]').length ? $(this).find('[data-typeahead-query]') : $($(this).attr("data-typeahead-field"));
$container.find("li:not(.query-holder)").remove();
var lastQuery = null;
var lastQuery = "";
var runQueryTimeout = null;
var loadIndicatorTimeout = null;
function showLoadIndicator() {
$container.find("li:not(.query-holder)").remove();
$container.append("<li class='loading'><span class='fa fa-4x fa-cog fa-spin'></span></li>");
$container.toggleClass('focused', $query.is(":focus") && $container.children().length > 0);
}
function runQuery() {
var thisQuery = $query.val();
if (thisQuery === lastQuery) return;
lastQuery = $query.val();
window.clearTimeout(loadIndicatorTimeout)
loadIndicatorTimeout = window.setTimeout(showLoadIndicator, 80)
var thisQuery = $query.val();
$.getJSON(
$container.attr("data-source") + "?query=" + encodeURIComponent($query.val()) + (typeof $container.attr("data-organizer") !== "undefined" ? "&organizer=" + $container.attr("data-organizer") : ""),
function (data) {
@@ -34,74 +23,107 @@ $(function () {
// Lost race condition
return;
}
window.clearTimeout(loadIndicatorTimeout);
$container.find("li:not(.query-holder)").remove();
$.each(data.results, function (i, res) {
let $linkContent = $("<div>");
if (res.type === "organizer") {
$linkContent.append(
$("<span>").addClass("event-name-full").append(
$("<span>").addClass("fa fa-users fa-fw")
).append(" ").append($("<div>").text(res.name).html())
)
$container.append(
$("<li>").append(
$("<a>").attr("href", res.url).append(
$("<div>").append(
$("<span>").addClass("event-name-full").append(
$("<span>").addClass("fa fa-users fa-fw")
).append(" ").append($("<div>").text(res.name).html())
)
).on("mousedown", function (event) {
if ($(this).length) {
location.href = $(this).attr("href");
}
$(this).parent().addClass("active");
event.preventDefault();
event.stopPropagation();
})
)
);
} else if (res.type === "order" || res.type === "voucher") {
$linkContent.append(
$("<span>").addClass("event-name-full").append($("<div>").text(res.title).html())
).append(
$("<span>").addClass("event-organizer").append(
$("<span>").addClass("fa fa-calendar fa-fw")
).append(" ").append($("<div>").text(res.event).html())
)
$container.append(
$("<li>").append(
$("<a>").attr("href", res.url).append(
$("<div>").append(
$("<span>").addClass("event-name-full").append($("<div>").text(res.title).html())
).append(
$("<span>").addClass("event-organizer").append(
$("<span>").addClass("fa fa-calendar fa-fw")
).append(" ").append($("<div>").text(res.event).html())
)
).on("mousedown", function (event) {
if ($(this).length) {
location.href = $(this).attr("href");
}
$(this).parent().addClass("active");
event.preventDefault();
event.stopPropagation();
})
)
);
} else if (res.type === "user") {
$linkContent.append(
$("<span>").addClass("event-name-full").append(
$("<span>").addClass("fa fa-user fa-fw")
).append(" ").append($("<div>").text(res.name).html())
)
$container.append(
$("<li>").append(
$("<a>").attr("href", res.url).append(
$("<div>").append(
$("<span>").addClass("event-name-full").append(
$("<span>").addClass("fa fa-user fa-fw")
).append(" ").append($("<div>").text(res.name).html())
)
).on("mousedown", function (event) {
if ($(this).length) {
location.href = $(this).attr("href");
}
$(this).parent().addClass("active");
event.preventDefault();
event.stopPropagation();
})
)
);
} else {
$linkContent.append(
$("<span>").addClass("event-name-full").append($("<div>").text(res.name).html())
).append(
$("<span>").addClass("event-organizer").append(
$("<span>").addClass("fa fa-users fa-fw")
).append(" ").append($("<div>").text(res.organizer).html())
).append(
$("<span>").addClass("event-daterange").append(
$("<span>").addClass("fa fa-calendar fa-fw")
).append(" ").append(res.date_range)
)
$container.append(
$("<li>").append(
$("<a>").attr("href", res.url).append(
$("<div>").append(
$("<span>").addClass("event-name-full").append($("<div>").text(res.name).html())
).append(
$("<span>").addClass("event-organizer").append(
$("<span>").addClass("fa fa-users fa-fw")
).append(" ").append($("<div>").text(res.organizer).html())
).append(
$("<span>").addClass("event-daterange").append(
$("<span>").addClass("fa fa-calendar fa-fw")
).append(" ").append(res.date_range)
)
).on("mousedown", function (event) {
if ($(this).length) {
location.href = $(this).attr("href");
}
$(this).parent().addClass("active");
event.preventDefault();
event.stopPropagation();
})
)
);
}
$container.append(
$("<li>").append(
$("<a>").attr("href", res.url).append(
$linkContent
).on("mousedown", function (event) {
if ($(this).length) {
location.href = $(this).attr("href");
}
$(this).parent().addClass("active");
event.preventDefault();
event.stopPropagation();
})
)
);
});
$container.toggleClass('focused', $query.is(":focus") && $container.children().length > 0);
}
);
}
$query.on("forceRunQuery", function () {
runQuery();
});
$query.on("input", function () {
$query.on("change", function () {
if ($container.attr("data-typeahead-field") && $query.val() === "") {
$container.removeClass('focused');
$container.find("li:not(.query-holder)").remove();
lastQuery = null;
return;
}
window.clearTimeout(runQueryTimeout)
if (runQueryTimeout != null) {
window.clearTimeout(runQueryTimeout)
}
runQueryTimeout = window.setTimeout(runQuery, 250)
});
$query.on("keydown", function (event) {
@@ -155,6 +177,8 @@ $(function () {
event.preventDefault();
event.stopPropagation();
return true;
} else {
$(this).change();
}
});
});
@@ -243,7 +243,7 @@ div.mail-preview {
}
}
.input-group-btn .btn {
padding-bottom: 8px;
padding-bottom: 7px;
}
.reldatetime {
input[type=text], select {
@@ -412,11 +412,6 @@ body.loading #wrapper {
white-space: nowrap;
overflow: hidden;
}
.loading {
padding: 10px;
text-align: center;
color: lightgrey;
}
}
.sidebar-content {
@@ -54,10 +54,10 @@ $(function () {
wallets.forEach(function(wallet) {
const labels = $('[data-wallets*='+wallet+'] + label strong, [data-wallets*='+wallet+'] + strong')
.append('<span class="wallet wallet-loading" data-wallet="'+wallet+'"> <i aria-hidden="true" class="fa fa-cog fa-spin"></i></span>')
.append('<span class="wallet wallet-loading"> <i aria-hidden="true" class="fa fa-cog fa-spin"></i></span>')
walletdetection[wallet]()
.then(function(result) {
const spans = labels.find(".wallet-loading[data-wallet=" + wallet + "]");
const spans = labels.find(".wallet-loading:nth-of-type(1)");
if (result) {
spans.removeClass('wallet-loading').hide().text(', ' + walletdetection.name_map[wallet]).fadeIn(300);
} else {
@@ -65,7 +65,7 @@ $(function () {
}
})
.catch(function(result) {
labels.find(".wallet-loading[data-wallet=" + wallet + "]").remove();
labels.find(".wallet-loading:nth-of-type(1)").remove();
})
});
});
+16 -16
View File
@@ -31,7 +31,7 @@
},
{
"type": "string",
"pattern": "^-?[0-9]+(\\.[0-9]+)?$"
"pattern": "^[0-9]+(\\.[0-9]+)?$"
}
]
},
@@ -43,7 +43,7 @@
},
{
"type": "string",
"pattern": "^-?[0-9]+(\\.[0-9]+)?$"
"pattern": "^[0-9]+(\\.[0-9]+)?$"
}
]
},
@@ -73,7 +73,7 @@
},
{
"type": "string",
"pattern": "^-?[0-9]+(\\.[0-9]+)?$"
"pattern": "^[0-9]+(\\.[0-9]+)?$"
}
]
},
@@ -110,7 +110,7 @@
},
{
"type": "string",
"pattern": "^-?[0-9]+(\\.[0-9]+)?$"
"pattern": "^[0-9]+(\\.[0-9]+)?$"
}
]
},
@@ -122,7 +122,7 @@
},
{
"type": "string",
"pattern": "^-?[0-9]+(\\.[0-9]+)?$"
"pattern": "^[0-9]+(\\.[0-9]+)?$"
}
]
},
@@ -138,7 +138,7 @@
},
{
"type": "string",
"pattern": "^-?[0-9]+(\\.[0-9]+)?$"
"pattern": "^[0-9]+(\\.[0-9]+)?$"
}
]
},
@@ -150,7 +150,7 @@
},
{
"type": "string",
"pattern": "^-?[0-9]+(\\.[0-9]+)?$"
"pattern": "^[0-9]+(\\.[0-9]+)?$"
}
]
}
@@ -188,7 +188,7 @@
},
{
"type": "string",
"pattern": "^-?[0-9]+(\\.[0-9]+)?$"
"pattern": "^[0-9]+(\\.[0-9]+)?$"
}
]
},
@@ -200,7 +200,7 @@
},
{
"type": "string",
"pattern": "^-?[0-9]+(\\.[0-9]+)?$"
"pattern": "^[0-9]+(\\.[0-9]+)?$"
}
]
},
@@ -212,7 +212,7 @@
},
{
"type": "string",
"pattern": "^-?[0-9]+(\\.[0-9]+)?$"
"pattern": "^[0-9]+(\\.[0-9]+)?$"
}
]
},
@@ -247,7 +247,7 @@
},
{
"type": "string",
"pattern": "^-?[0-9]+(\\.[0-9]+)?$"
"pattern": "^[0-9]+(\\.[0-9]+)?$"
}
]
},
@@ -259,7 +259,7 @@
},
{
"type": "string",
"pattern": "^-?[0-9]+(\\.[0-9]+)?$"
"pattern": "^[0-9]+(\\.[0-9]+)?$"
}
]
},
@@ -307,7 +307,7 @@
},
{
"type": "string",
"pattern": "^-?[0-9]+(\\.[0-9]+)?$"
"pattern": "^[0-9]+(\\.[0-9]+)?$"
}
]
}
@@ -339,7 +339,7 @@
},
{
"type": "string",
"pattern": "^-?[0-9]+(\\.[0-9]+)?$"
"pattern": "^[0-9]+(\\.[0-9]+)?$"
}
]
},
@@ -351,7 +351,7 @@
},
{
"type": "string",
"pattern": "^-?[0-9]+(\\.[0-9]+)?$"
"pattern": "^[0-9]+(\\.[0-9]+)?$"
}
]
},
@@ -371,7 +371,7 @@
},
{
"type": "string",
"pattern": "^-?[0-9]+(\\.[0-9]+)?$"
"pattern": "^[0-9]+(\\.[0-9]+)?$"
}
]
}
+1 -1
View File
@@ -42,7 +42,7 @@ EMAIL_BACKEND = EMAIL_CUSTOM_SMTP_BACKEND = 'django.core.mail.backends.locmem.Em
COMPRESS_ENABLED = COMPRESS_OFFLINE = False
COMPRESS_CACHE_BACKEND = 'testcache'
STORAGES["staticfiles"]["BACKEND"] = 'django.contrib.staticfiles.storage.StaticFilesStorage'
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage'
PRETIX_INSTANCE_NAME = 'pretix.eu'
COMPRESS_PRECOMPILERS_ORIGINAL = COMPRESS_PRECOMPILERS
-2
View File
@@ -21,8 +21,6 @@ addopts = --reruns 3 -rw
filterwarnings =
error
ignore:The 'warn' method is deprecated:DeprecationWarning
ignore::django.utils.deprecation.RemovedInDjango51Warning:django.core.files.storage
ignore:.*index_together.*:django.utils.deprecation.RemovedInDjango51Warning:
ignore::DeprecationWarning:mt940
ignore::DeprecationWarning:cbor2
ignore::DeprecationWarning:markdown
+2 -2
View File
@@ -940,8 +940,8 @@ def test_rules_reasoning_prefer_number_over_date(event, position, clist):
@pytest.mark.django_db(transaction=True)
def test_position_queries(django_assert_max_num_queries, position, clist):
with django_assert_max_num_queries(13) as captured:
def test_position_queries(django_assert_num_queries, position, clist):
with django_assert_num_queries(12 if 'sqlite' in settings.DATABASES['default']['ENGINE'] else 11) as captured:
perform_checkin(position, clist, {})
if 'sqlite' not in settings.DATABASES['default']['ENGINE']:
assert any('FOR UPDATE' in s['sql'] for s in captured)
+36 -36
View File
@@ -38,10 +38,10 @@ class I18nStringTest(TestCase):
'en': 'Hello'
}
s = LazyI18nString(data)
with translation.override('en'):
self.assertEqual(str(s), 'Hello')
with translation.override('de'):
self.assertEqual(str(s), 'Hallo')
translation.activate('en')
self.assertEqual(str(s), 'Hello')
translation.activate('de')
self.assertEqual(str(s), 'Hallo')
def test_similar_translations(self):
data = {
@@ -50,57 +50,57 @@ class I18nStringTest(TestCase):
'de-informal': 'Du'
}
s = LazyI18nString(data)
with translation.override('de'):
self.assertEqual(str(s), 'Sie')
with translation.override('de-informal'):
self.assertEqual(str(s), 'Du')
translation.activate('de')
self.assertEqual(str(s), 'Sie')
translation.activate('de-informal')
self.assertEqual(str(s), 'Du')
data = {
'en': 'You',
'de-informal': 'Du'
}
s = LazyI18nString(data)
with translation.override('de'):
self.assertEqual(str(s), 'Du')
with translation.override('de-informal'):
self.assertEqual(str(s), 'Du')
translation.activate('de')
self.assertEqual(str(s), 'Du')
translation.activate('de-informal')
self.assertEqual(str(s), 'Du')
data = {
'en': 'You',
'de': 'Sie'
}
s = LazyI18nString(data)
with translation.override('de'):
self.assertEqual(str(s), 'Sie')
with translation.override('de-informal'):
self.assertEqual(str(s), 'Sie')
translation.activate('de')
self.assertEqual(str(s), 'Sie')
translation.activate('de-informal')
self.assertEqual(str(s), 'Sie')
def test_missing_default_translation(self):
data = {
'de': 'Hallo',
}
s = LazyI18nString(data)
with translation.override('en'):
self.assertEqual(str(s), 'Hallo')
with translation.override('de'):
self.assertEqual(str(s), 'Hallo')
translation.activate('en')
self.assertEqual(str(s), 'Hallo')
translation.activate('de')
self.assertEqual(str(s), 'Hallo')
def test_missing_translation(self):
data = {
'en': 'Hello',
}
s = LazyI18nString(data)
with translation.override('en'):
self.assertEqual(str(s), 'Hello')
with translation.override('de'):
self.assertEqual(str(s), 'Hello')
translation.activate('en')
self.assertEqual(str(s), 'Hello')
translation.activate('de')
self.assertEqual(str(s), 'Hello')
def test_legacy_string(self):
s = LazyI18nString("Hello")
with translation.override('en'):
self.assertEqual(str(s), 'Hello')
with translation.override('de'):
self.assertEqual(str(s), 'Hello')
translation.activate('en')
self.assertEqual(str(s), 'Hello')
translation.activate('de')
self.assertEqual(str(s), 'Hello')
def test_none(self):
s = LazyI18nString(None)
@@ -124,10 +124,10 @@ class I18nFieldTest(TestCase):
obj = ItemCategory.objects.create(event=self.event, name="Hello")
obj = ItemCategory.objects.get(id=obj.id)
self.assertIsInstance(obj.name, LazyI18nString)
with translation.override('en'):
self.assertEqual(str(obj.name), "Hello")
with translation.override('de'):
self.assertEqual(str(obj.name), "Hello")
translation.activate('en')
self.assertEqual(str(obj.name), "Hello")
translation.activate('de')
self.assertEqual(str(obj.name), "Hello")
def test_save_load_cycle_i18n_string(self):
obj = ItemCategory.objects.create(event=self.event,
@@ -139,7 +139,7 @@ class I18nFieldTest(TestCase):
))
obj = ItemCategory.objects.get(id=obj.id)
self.assertIsInstance(obj.name, LazyI18nString)
with translation.override('en'):
self.assertEqual(str(obj.name), "Hello")
with translation.override('de'):
self.assertEqual(str(obj.name), "Hallo")
translation.activate('en')
self.assertEqual(str(obj.name), "Hello")
translation.activate('de')
self.assertEqual(str(obj.name), "Hallo")
+8 -26
View File
@@ -71,7 +71,6 @@ def inputfile_factory():
'I': 'Foo',
'J': '2021-06-28 11:00:00',
'K': '06221/32177-50',
'L': 'True'
},
{
'A': 'Daniel',
@@ -85,7 +84,6 @@ def inputfile_factory():
'I': 'Bar',
'J': '2021-06-28 11:00:00',
'K': '+4962213217750',
'L': 'False',
},
{},
{
@@ -100,11 +98,10 @@ def inputfile_factory():
'I': 'Foo,Bar',
'J': '2021-06-28 11:00:00',
'K': '',
'L': '',
},
]
f = StringIO()
w = csv.DictWriter(f, ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L'], dialect=csv.excel)
w = csv.DictWriter(f, ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K'], dialect=csv.excel)
w.writeheader()
w.writerows(d)
f.seek(0)
@@ -798,37 +795,22 @@ def test_import_question_validate(user, event, item):
@scopes_disabled()
def test_import_question_valid(user, event, item):
settings = dict(DEFAULT_SETTINGS)
q1 = event.questions.create(question='Foo', type=Question.TYPE_CHOICE_MULTIPLE)
o1 = q1.options.create(answer='Foo', identifier='Foo')
o2 = q1.options.create(answer='Bar', identifier='Bar')
q2 = event.questions.create(question='Boolean', type=Question.TYPE_BOOLEAN)
q = event.questions.create(question='Foo', type=Question.TYPE_CHOICE_MULTIPLE)
o1 = q.options.create(answer='Foo', identifier='Foo')
o2 = q.options.create(answer='Bar', identifier='Bar')
settings['item'] = 'static:{}'.format(item.pk)
settings['attendee_email'] = 'csv:C'
settings['question_{}'.format(q1.pk)] = 'csv:I'
settings['question_{}'.format(q2.pk)] = 'csv:L'
settings['question_{}'.format(q.pk)] = 'csv:I'
import_orders.apply(
args=(event.pk, inputfile_factory().id, settings, 'en', user.pk)
).get()
assert QuestionAnswer.objects.filter(question=q1).count() == 3
assert QuestionAnswer.objects.filter(question=q).count() == 3
a1 = OrderPosition.objects.get(attendee_email='schneider@example.org').answers.first()
assert a1.question == q1
assert a1.question == q
assert list(a1.options.all()) == [o1]
a3 = OrderPosition.objects.get(attendee_email__isnull=True).answers.first()
assert a3.question == q1
assert a3.question == q
assert set(a3.options.all()) == {o1, o2}
# Boolean question: One True, One False, one empty == 2 Question answers
assert QuestionAnswer.objects.filter(question=q2).count() == 2
a4 = OrderPosition.objects.get(attendee_email='schneider@example.org').answers.last()
assert a4.question == q2
assert a4.answer == 'True'
a5 = OrderPosition.objects.get(attendee_email='daniel@example.org').answers.last()
assert a5.question == q2
assert a5.answer == 'False'
a6 = OrderPosition.objects.get(attendee_email__isnull=True).answers
assert a6.count() == 1
assert a6.first().question == q1
assert a6.last().question == q1
# TODO: validate question
-19
View File
@@ -412,7 +412,6 @@ def test_expiring_auto_disabled(event):
def test_expiring_auto_delayed(event):
event.settings.set('payment_term_expire_delay_days', 3)
event.settings.set('payment_term_last', date(2023, 7, 2))
event.settings.set('payment_term_weekdays', False)
event.settings.set('timezone', 'Europe/Berlin')
o1 = Order.objects.create(
code='FOO', event=event, email='dummy@dummy.test',
@@ -459,21 +458,6 @@ def test_expiring_auto_delayed(event):
assert o2.status == Order.STATUS_EXPIRED
@pytest.mark.django_db
def test_expiring_auto_delayed_weekdays(event):
event.settings.set('payment_term_expire_delay_days', 2)
event.settings.set('payment_term_weekdays', True)
event.settings.set('timezone', 'Europe/Berlin')
o1 = Order.objects.create(
code='FOO', event=event, email='dummy@dummy.test',
status=Order.STATUS_PENDING,
datetime=datetime(2023, 6, 22, 12, 13, 14, tzinfo=zoneinfo.ZoneInfo("Europe/Berlin")),
expires=datetime(2023, 6, 30, 23, 59, 59, tzinfo=zoneinfo.ZoneInfo("Europe/Berlin")),
total=0,
)
assert o1.payment_term_expire_date == o1.expires + timedelta(days=3)
@pytest.mark.django_db
def test_do_not_expire_if_approval_pending(event):
o1 = Order.objects.create(
@@ -2228,7 +2212,6 @@ class OrderChangeManagerTests(TestCase):
@classscope(attr='o')
def test_split_and_change_higher(self):
self.order.status = Order.STATUS_PAID
self.order.expires = now() - timedelta(days=1)
self.order.save()
self.order.payments.create(
provider='manual',
@@ -2248,7 +2231,6 @@ class OrderChangeManagerTests(TestCase):
assert not self.order.fees.exists()
assert self.order.status == Order.STATUS_PAID
assert self.order.pending_sum == Decimal('0.00')
assert self.order.expires < now()
r = self.order.refunds.last()
assert r.provider == 'offsetting'
assert r.amount == Decimal('23.00')
@@ -2257,7 +2239,6 @@ class OrderChangeManagerTests(TestCase):
# New order
assert self.op2.order != self.order
o2 = self.op2.order
assert o2.expires > now()
assert o2.total == Decimal('42.00')
assert o2.status == Order.STATUS_PENDING
assert o2.positions.count() == 1
-6
View File
@@ -22,7 +22,6 @@
import inspect
import pytest
from django.utils import translation
from django_scopes import scopes_disabled
from xdist.dsession import DSession
@@ -69,8 +68,3 @@ def pytest_fixture_setup(fixturedef, request):
else:
with scopes_disabled():
yield
@pytest.fixture(autouse=True)
def reset_locale():
translation.activate("en")

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