Compare commits

..

3 Commits

Author SHA1 Message Date
Richard Schreiber
3b8dbb87fc allow same origin for cookies, etc. 2024-07-08 12:09:38 +02:00
Richard Schreiber
b1dfc6d825 allow scripts 2024-07-08 12:03:58 +02:00
Richard Schreiber
5997d3922c Fix stripe SCA redirect/sandboxing 2024-07-08 11:53:04 +02:00
67 changed files with 5165 additions and 36547 deletions

View File

@@ -5,6 +5,7 @@ on:
branches: [ master ]
paths-ignore:
- 'doc/**'
- 'src/pretix/locale/**'
pull_request:
branches: [ master ]
paths-ignore:

View File

@@ -65,7 +65,7 @@ Package dependencies
To build and run pretix, you will need the following debian packages::
# apt-get install git build-essential python3-dev python3-venv python3 python3-pip \
libxml2-dev libxslt1-dev libffi-dev zlib1g-dev libssl-dev \
python3-dev libxml2-dev libxslt1-dev libffi-dev zlib1g-dev libssl-dev \
gettext libpq-dev libjpeg-dev libopenjp2-7-dev
Config file

View File

@@ -96,8 +96,6 @@ Endpoints
:query integer page: The page number in case of a multi-page result set, default is 1
:query string secret: Only show gift cards with the given secret.
:query string value: Only show gift cards with the given value.
:query boolean expired: Filter for gift cards that are (not) expired.
:query boolean testmode: Filter for gift cards that are (not) in test mode.
:query boolean include_accepted: Also show gift cards issued by other organizers that are accepted by this organizer.
:query string expand: If you pass ``"owner_ticket"``, the respective field will be shown as a nested value instead of just an ID.

View File

@@ -164,7 +164,6 @@ Endpoints
}
:query integer page: The page number in case of a multi-page result set, default is 1
:query string search: Filter the list by the value of the variation (substring search).
:query boolean active: If set to ``true`` or ``false``, only items with this value for the field ``active`` will be
returned.
:param organizer: The ``slug`` field of the organizer to fetch

View File

@@ -392,7 +392,6 @@ Endpoints
}
:query integer page: The page number in case of a multi-page result set, default is 1
:query string search: Filter the list by internal name or name of the item (substring search).
:query boolean active: If set to ``true`` or ``false``, only items with this value for the field ``active`` will be
returned.
:query integer category: If set to the ID of a category, only items within that category will be returned.

View File

@@ -42,8 +42,6 @@ payment_date date **DEPRECATED AN
payment_provider string **DEPRECATED AND INACCURATE** Payment provider used for this order
total money (string) Total value of this order
comment string Internal comment on this order
api_meta object Meta data for that order. Only available through API, no guarantees
on the content structure. You can use this to save references to your system.
custom_followup_at date Internal date for a custom follow-up action
checkin_attention boolean If ``true``, the check-in app should show a warning
that this ticket requires special attention if a ticket
@@ -462,13 +460,10 @@ List of all orders
:query datetime modified_since: Only return orders that have changed since the given date. Be careful: We only
recommend using this in combination with ``testmode=false``, since test mode orders can vanish at any time and
you will not notice it using this method.
:query datetime created_since: Only return orders that have been created since the given date (inclusive).
:query datetime created_before: Only return orders that have been created before the given date (exclusive).
:query datetime created_since: Only return orders that have been created since the given date.
:query integer subevent: Only return orders with a position that contains this subevent ID. *Warning:* Result will also include orders if they contain mixed subevents, and it will even return orders where the subevent is only contained in a canceled position.
:query datetime subevent_after: Only return orders that contain a ticket for a subevent taking place after the given date. This is an exclusive after, and it considers the **end** of the subevent (or its start, if the end is not set).
:query datetime subevent_before: Only return orders that contain a ticket for a subevent taking place after the given date. This is an exclusive before, and it considers the **start** of the subevent.
:query string sales_channel: Only return orders with the given sales channel identifier (e.g. ``"web"``).
:query string payment_provider: Only return orders that contain a payment using the given payment provider. Note that this also searches for partial incomplete, or failed payments within the order and is not useful to get a sum of payment amounts without further processing.
:query string exclude: Exclude a field from the output, e.g. ``fees`` or ``positions.downloads``. Can be used as a performance optimization. Can be passed multiple times.
:query string include: Include only the given field in the output, e.g. ``fees`` or ``positions.downloads``. Can be used as a performance optimization. Can be passed multiple times. ``include`` is applied before ``exclude``, so ``exclude`` takes precedence.
:param organizer: The ``slug`` field of the organizer to fetch
@@ -564,7 +559,6 @@ Fetching individual orders
"fees": [],
"total": "23.00",
"comment": "",
"api_meta": {},
"custom_followup_at": null,
"checkin_attention": false,
"checkin_text": null,
@@ -745,8 +739,6 @@ Updating order fields
* ``comment``
* ``api_meta``
* ``custom_followup_at``
* ``invoice_address`` (you always need to supply the full object, or ``null`` to delete the current address)

View File

@@ -41,7 +41,6 @@ The following values for ``action_types`` are valid with pretix core:
* ``pretix.event.order.modified``
* ``pretix.event.order.contact.changed``
* ``pretix.event.order.changed.*``
* ``pretix.event.order.deleted`` (can only occur for test mode orders)
* ``pretix.event.order.refund.created``
* ``pretix.event.order.refund.created.externally``
* ``pretix.event.order.refund.requested``
@@ -116,7 +115,6 @@ Endpoints
}
:query integer page: The page number in case of a multi-page result set, default is 1
:query boolean enabled: Only show webhooks that are or are not enabled
:param organizer: The ``slug`` field of the organizer to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure

View File

@@ -39,7 +39,7 @@ Frontend
.. automodule:: pretix.presale.signals
:members: order_info, order_info_top, order_meta_from_request, order_api_meta_from_request
:members: order_info, order_info_top, order_meta_from_request
Request flow
""""""""""""

View File

@@ -1,105 +0,0 @@
GetYourGuide
============
.. note::
The GetYourGuide integration is currently in Beta. Please contact support@pretix.eu to enable the integration
for your pretix.eu organizer account.
Introduction
------------
Using third party aggregators, such als GetYourGuide, event organizers can sell tickets to their events not only on
their own ticket-shop but also on the aggregator's portal. While this service is not for free, it allows event
organizers to reacher a larger audience that would otherwise not have found their way into the organizers webshop.
Using pretix' integration with GetYourGuide, event organizers can profit from an additional sales and revenue channel,
while keeping the effort for setting up and maintaining multiple ticket shops to a minimum.
Preparing your organizer account
--------------------------------
The first step in enabling the GetYourGuide integration, is to setup a corresponding Sales Channel, which will be used
to properly attribute the sales generated. This needs to be done only once per organizer account.
To do so, log into the pretix backend, select ``Organizers`` from the navigation and then the organizer in question.
Extending the ``Settings``-menu, find the ``Sales channels`` configuration and click the ``Add a new channel`` button.
On the following page, you will be able to select ``GetYourGuide`` as the sales channel type and give it a custom name.
Preparing your event
--------------------
In order to now sell your events on GetYourGuide, you will need to configure each event in question.
1. Enabling the plugin
Within your event, extend the ``Settings`` menu and navigate to ``Plugins``. Activate the plugin in the
``Integrations`` tab.
2. Sell the event on the sales channel
Pick the sales channel or channels, on which you would like to sell your event by navigating to the event's general
settings page using the ``Sell on all sales channels`` or ``Restrict to specific sales channels`` checkboxes.
3. Configure one or more products to be sold on GetYourGuide
Either create a new or edit an existing product, that you would like to sell on GetYourGuide. To do so, you will
need to have checked the ``Sell on all sales channels`` or appropriate ``Restrict to specific sales channels``
checkbox of the product within it's ``Availability`` tab.
In addition, you will also need to set the GetYourGuide equivalent ticket category in the product's accordingly
named settings tab. Within your event, there can be only one product per ticket category. Depending on your further
configuration, you must at least select one product to be in the ``Adult`` or ``Group`` category.
4. Configuring the GetYourGuide-plugin
Once you have configured one or more products to be eligible to be sold on GetYourGuide, you'll need to configure a
few basic settings within the event (``Settings`` --> ``GetYourGuide``). The most important settings can be found
the in the ``Configuration`` tab, such as the location of the event on sale.
Ticket Categories
-----------------
While pretix only uses the ticket category term loosely to group together multiple products for nicer display,
GetYourGuide is relying on the ticket categories to price the tickets.
First of all, you need to make the decision on how you are planning on selling your tickets on GetYourGuide - in most
cases, this will reflect your current sales strategy within your pretix shop.
- Individual tickets
Every single person attending will need to purchase their own ticket. A family of two adults and two
children will have to purchase and pay for a total of 4 tickets.
In this case, you will need to offer *at least* a ticket of the ``Adult`` type, but may offer any other ticket
category type (Child, Youth, Senior, ...) in addition. But you cannot offer a ``Group`` ticket.
- Group tickets
Two groups, consisting of 10 and 20 participants respectively, won't need to purchase a total of 30 tickets, but
rather two group tickets. It is up to you to configure the group size limits within the GetYourGuide-settings of your
product.
Choosing this option, you cannot offer any other ticket categories besides ``Group``.
Setting up event dates and quotas
---------------------------------
Of course, in addition to creating products, you will also need to add them to a quota for them to be available for
sale. The process for doing this is the very same as for any regular event or event series.
.. note::
When selling individual tickets through GetYourGuide, you will not be able to offer differing quantities for
individual ticket categories.
For this reason, we recommend to place all GetYourGuide-eligible products into the same quota. Should you however opt
to create multiple quotas which create an imbalance, pretix will report only the available number of tickets for the
lowest relevant quota.
Connecting your event to GetYourGuide
-------------------------------------
Once you have set up your event and products and performed all necessary configuration, you may want to use the
Analyzer-feature of our GetYourGuide-plugin (``Settings`` -> ``GetYourGuide`` -> tab ``Analyzer``).
The Analyzer should not display any blocking error messages and at least one event date that is ready for publishing on
the GetYourGuide platform.
At this point, you will need to setup your event (called ``product`` in the GetYourGuide universe) on their
`Supplier Portal`_ and connect it with your pretix shop. To do so, please follow the
`Connecting a new product to your Reservation System`_ on the GetYourGuide Supply Partner Help Center.
Select ``pretix.eu`` as your reservation system; the required ``product ID`` can be found in the ``Configuration`` tab
of the GetYourGuide plugin settings page.
From this point on, GetYourGuide will automatically import the availabilities and products and offer them for sale.
.. _Supplier Portal: https://suppliers.getyourguide.com/
.. _Connecting a new product to your Reservation System: https://supply.getyourguide.support/hc/en-us/articles/18008029689373-Connecting-a-new-product-to-your-Reservation-system

View File

@@ -25,4 +25,3 @@ If you want to **create** a plugin, please go to the
webinar
presale-saml
kulturpass
getyourguide

View File

@@ -1,4 +1,4 @@
sphinx==7.4.*
sphinx==7.3.*
jinja2==3.1.*
sphinx-rtd-theme
sphinxcontrib-httpdomain

View File

@@ -1,5 +1,5 @@
-e ../
sphinx==7.4.*
sphinx==7.3.*
jinja2==3.1.*
sphinx-rtd-theme
sphinxcontrib-httpdomain

View File

@@ -449,16 +449,6 @@ Further reading:
* `Stripe Payment Method Domain registration`_
External payment providers and Cross-Origin-Opener-Policy
---------------------------------------------------------
If you use a payment provider that opens a new window during checkout (such as PayPal), be aware that setting
``Cross-Origin-Opener-Policy: same-origin`` results in an empty popup-window being opened in the foreground. This is
due to JavaScript not having access to the opened window. To mitigate this, you either need to always open the widgets
checkout in a new tab (see :ref:`Always open a new tab`) or set ``Cross-Origin-Opener-Policy: same-origin-allow-popups``
Working with Cross-Origin-Embedder-Policy
-----------------------------------------

View File

@@ -38,13 +38,13 @@ dependencies = [
"dj-static",
"Django[argon2]==4.2.*",
"django-bootstrap3==24.2",
"django-compressor==4.5.1",
"django-compressor==4.5",
"django-countries==7.6.*",
"django-filter==24.2",
"django-formset-js-improved==0.5.0.3",
"django-formtools==2.5.1",
"django-hierarkey==1.2.*",
"django-hijack==3.6.*",
"django-hijack==3.5.*",
"django-i18nfield==1.9.*,>=1.9.4",
"django-libsass==0.9",
"django-localflavor==4.0",
@@ -82,8 +82,8 @@ dependencies = [
"pycountry",
"pycparser==2.22",
"pycryptodome==3.20.*",
"pypdf==4.3.*",
"python-bidi==0.5.*", # Support for Arabic in reportlab
"pypdf==4.2.*",
"python-bidi==0.4.*", # Support for Arabic in reportlab
"python-dateutil==2.9.*",
"pytz",
"pytz-deprecation-shim==0.1.*",
@@ -92,7 +92,7 @@ dependencies = [
"redis==5.0.*",
"reportlab==4.2.*",
"requests==2.31.*",
"sentry-sdk==2.10.*",
"sentry-sdk==2.5.*",
"sepaxml==2.6.*",
"slimit",
"static3==0.7.*",
@@ -127,7 +127,7 @@ dev = [
"pytest-rerunfailures==14.*",
"pytest-sugar",
"pytest-xdist==3.6.*",
"pytest==8.3.*",
"pytest==8.2.*",
"responses",
]

View File

@@ -21,7 +21,6 @@
#
import json
from django.db.models import prefetch_related_objects
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
@@ -81,7 +80,6 @@ class SalesChannelMigrationMixin:
def to_internal_value(self, data):
if "sales_channels" in data:
prefetch_related_objects([self.organizer], "sales_channels")
all_channels = {
s.identifier for s in
self.organizer.sales_channels.all()
@@ -111,7 +109,6 @@ class SalesChannelMigrationMixin:
def to_representation(self, value):
value = super().to_representation(value)
if value.get("all_sales_channels"):
prefetch_related_objects([self.organizer], "sales_channels")
value["sales_channels"] = sorted([
s.identifier for s in
self.organizer.sales_channels.all()

View File

@@ -726,7 +726,7 @@ class OrderSerializer(I18nAwareModelSerializer):
'code', 'event', 'status', 'testmode', 'secret', 'email', 'phone', 'locale', 'datetime', 'expires', 'payment_date',
'payment_provider', 'fees', 'total', 'comment', 'custom_followup_at', 'invoice_address', 'positions', 'downloads',
'checkin_attention', 'checkin_text', 'last_modified', 'payments', 'refunds', 'require_approval', 'sales_channel',
'url', 'customer', 'valid_if_pending', 'api_meta'
'url', 'customer', 'valid_if_pending'
)
read_only_fields = (
'code', 'status', 'testmode', 'secret', 'datetime', 'expires', 'payment_date',
@@ -786,7 +786,7 @@ class OrderSerializer(I18nAwareModelSerializer):
# Even though all fields that shouldn't be edited are marked as read_only in the serializer
# (hopefully), we'll be extra careful here and be explicit about the model fields we update.
update_fields = ['comment', 'custom_followup_at', 'checkin_attention', 'checkin_text', 'email', 'locale',
'phone', 'valid_if_pending', 'api_meta']
'phone', 'valid_if_pending']
if 'invoice_address' in validated_data:
iadata = validated_data.pop('invoice_address')
@@ -1059,7 +1059,7 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
fields = ('code', 'status', 'testmode', 'email', 'phone', 'locale', 'payment_provider', 'fees', 'comment', 'sales_channel',
'invoice_address', 'positions', 'checkin_attention', 'checkin_text', 'payment_info', 'payment_date',
'consume_carts', 'force', 'send_email', 'simulate', 'customer', 'custom_followup_at',
'require_approval', 'valid_if_pending', 'expires', 'api_meta')
'require_approval', 'valid_if_pending', 'expires')
def validate_payment_provider(self, pp):
if pp is None:

View File

@@ -443,7 +443,7 @@ class SubEventViewSet(ConditionalListView, viewsets.ModelViewSet):
elif self.request.user.is_authenticated:
qs = SubEvent.objects.filter(
event__organizer=self.request.organizer,
event__in=self.request.user.get_events_with_any_permission(request=self.request)
event__in=self.request.user.get_events_with_any_permission()
)
qs = filter_qs_by_attr(qs, self.request)

View File

@@ -56,17 +56,10 @@ from pretix.base.models import (
)
from pretix.base.services.quotas import QuotaAvailability
from pretix.helpers.dicts import merge_dicts
from pretix.helpers.i18n import i18ncomp
with scopes_disabled():
class ItemFilter(FilterSet):
tax_rate = django_filters.CharFilter(method='tax_rate_qs')
search = django_filters.CharFilter(method='search_qs')
def search_qs(self, queryset, name, value):
return queryset.filter(
Q(internal_name__icontains=value) | Q(name__icontains=i18ncomp(value))
)
def tax_rate_qs(self, queryset, name, value):
if value in ("0", "None", "0.00"):
@@ -78,18 +71,6 @@ with scopes_disabled():
model = Item
fields = ['active', 'category', 'admission', 'tax_rate', 'free_price']
class ItemVariationFilter(FilterSet):
search = django_filters.CharFilter(method='search_qs')
def search_qs(self, queryset, name, value):
return queryset.filter(
Q(value__icontains=i18ncomp(value))
)
class Meta:
model = ItemVariation
fields = ['active']
class ItemViewSet(ConditionalListView, viewsets.ModelViewSet):
serializer_class = ItemSerializer
@@ -159,7 +140,6 @@ class ItemVariationViewSet(viewsets.ModelViewSet):
serializer_class = ItemVariationSerializer
queryset = ItemVariation.objects.none()
filter_backends = (DjangoFilterBackend, TotalOrderingFilter,)
filterset_class = ItemVariationFilter
ordering_fields = ('id', 'position')
ordering = ('id',)
permission = None

View File

@@ -108,7 +108,6 @@ with scopes_disabled():
status = django_filters.CharFilter(field_name='status', lookup_expr='iexact')
modified_since = django_filters.IsoDateTimeFilter(field_name='last_modified', lookup_expr='gte')
created_since = django_filters.IsoDateTimeFilter(field_name='datetime', lookup_expr='gte')
created_before = django_filters.IsoDateTimeFilter(field_name='datetime', lookup_expr='lt')
subevent_after = django_filters.IsoDateTimeFilter(method='subevent_after_qs')
subevent_before = django_filters.IsoDateTimeFilter(method='subevent_before_qs')
search = django_filters.CharFilter(method='search_qs')
@@ -116,8 +115,6 @@ with scopes_disabled():
variation = django_filters.CharFilter(field_name='all_positions', lookup_expr='variation_id', distinct=True)
subevent = django_filters.CharFilter(field_name='all_positions', lookup_expr='subevent_id', distinct=True)
customer = django_filters.CharFilter(field_name='customer__identifier')
sales_channel = django_filters.CharFilter(field_name='sales_channel__identifier')
payment_provider = django_filters.CharFilter(method='provider_qs')
class Meta:
model = Order
@@ -141,11 +138,6 @@ with scopes_disabled():
)
return qs
def provider_qs(self, qs, name, value):
return qs.filter(Exists(
OrderPayment.objects.filter(order=OuterRef('pk'), provider=value)
))
def subevent_before_qs(self, qs, name, value):
if getattr(self.request, 'event', None):
subevents = self.request.event.subevents

View File

@@ -24,11 +24,10 @@ from decimal import Decimal
import django_filters
from django.contrib.auth.hashers import make_password
from django.db import transaction
from django.db.models import OuterRef, Q, Subquery, Sum
from django.db.models import OuterRef, Subquery, Sum
from django.db.models.functions import Coalesce
from django.shortcuts import get_object_or_404
from django.utils.functional import cached_property
from django.utils.timezone import now
from django_filters.rest_framework import DjangoFilterBackend, FilterSet
from django_scopes import scopes_disabled
from rest_framework import mixins, serializers, status, views, viewsets
@@ -137,19 +136,11 @@ class SeatingPlanViewSet(viewsets.ModelViewSet):
with scopes_disabled():
class GiftCardFilter(FilterSet):
secret = django_filters.CharFilter(field_name='secret', lookup_expr='iexact')
expired = django_filters.BooleanFilter(method='expired_qs')
value = django_filters.NumberFilter(field_name='cached_value')
class Meta:
model = GiftCard
fields = ['secret', 'testmode']
def expired_qs(self, qs, name, value):
if value:
return qs.filter(expires__isnull=False, expires__lt=now())
else:
return qs.filter(Q(expires__isnull=True) | Q(expires__gte=now()))
class GiftCardViewSet(viewsets.ModelViewSet):
serializer_class = GiftCardSerializer

View File

@@ -19,8 +19,6 @@
# 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/>.
#
import django_filters
from django_filters.rest_framework import DjangoFilterBackend, FilterSet
from rest_framework import viewsets
from pretix.api.models import WebHook
@@ -28,17 +26,11 @@ from pretix.api.serializers.webhooks import WebHookSerializer
from pretix.helpers.dicts import merge_dicts
class WebhookFilter(FilterSet):
enabled = django_filters.rest_framework.BooleanFilter()
class WebHookViewSet(viewsets.ModelViewSet):
serializer_class = WebHookSerializer
queryset = WebHook.objects.none()
permission = 'can_change_organizer_settings'
write_permission = 'can_change_organizer_settings'
filter_backends = (DjangoFilterBackend,)
filterset_class = WebhookFilter
def get_queryset(self):
return self.request.organizer.webhooks.prefetch_related('listeners')

View File

@@ -126,17 +126,6 @@ class ParametrizedOrderWebhookEvent(ParametrizedWebhookEvent):
}
class DeletedOrderWebhookEvent(ParametrizedWebhookEvent):
def build_payload(self, logentry: LogEntry):
return {
'notification_id': logentry.pk,
'organizer': logentry.organizer.slug,
'event': logentry.event.slug,
'code': logentry.parsed_data.get("code"),
'action': logentry.action_type,
}
class ParametrizedEventWebhookEvent(ParametrizedWebhookEvent):
def build_payload(self, logentry: LogEntry):
@@ -308,10 +297,6 @@ def register_default_webhook_events(sender, **kwargs):
'pretix.event.order.denied',
_('Order denied'),
),
DeletedOrderWebhookEvent(
'pretix.event.order.deleted',
_('Order deleted'),
),
ParametrizedOrderPositionCheckinWebhookEvent(
'pretix.event.checkin',
_('Ticket checked in'),

View File

@@ -256,7 +256,7 @@ class ListExporter(BaseExporter):
ws = wb.create_sheet()
self.prepare_xlsx_sheet(ws)
try:
ws.title = str(self.verbose_name)[:30]
ws.title = str(self.verbose_name)
except:
pass
total = 0
@@ -374,7 +374,7 @@ class MultiSheetListExporter(ListExporter):
wb = SafeWorkbook(write_only=True)
n_sheets = len(self.sheets)
for i_sheet, (s, l) in enumerate(self.sheets):
ws = wb.create_sheet(str(l)[:30])
ws = wb.create_sheet(str(l))
if hasattr(self, 'prepare_xlsx_sheet_' + s):
getattr(self, 'prepare_xlsx_sheet_' + s)(ws)

View File

@@ -560,7 +560,7 @@ class OrderListExporter(MultiSheetListExporter):
),
).select_related(
'order', 'order__invoice_address', 'order__customer', 'item', 'variation',
'voucher', 'tax_rule', 'addon_to',
'voucher', 'tax_rule'
).prefetch_related(
'subevent', 'subevent__meta_values',
'answers', 'answers__question', 'answers__options'
@@ -619,7 +619,6 @@ class OrderListExporter(MultiSheetListExporter):
_('Valid until'),
_('Order comment'),
_('Follow-up date'),
_('Add-on to position ID'),
]
questions = list(Question.objects.filter(event__in=self.events))
@@ -653,8 +652,7 @@ class OrderListExporter(MultiSheetListExporter):
_('VAT ID'),
]
headers += [
_('Sales channel'),
_('Order locale'),
_('Sales channel'), _('Order locale'),
_('E-mail address verified'),
_('External customer ID'),
_('Check-in lists'),
@@ -745,7 +743,6 @@ class OrderListExporter(MultiSheetListExporter):
]
row.append(order.comment)
row.append(order.custom_followup_at.strftime("%Y-%m-%d") if order.custom_followup_at else "")
row.append(op.addon_to.positionid if op.addon_to_id else "")
acache = {}
for a in op.answers.all():
# We do not want to localize Date, Time and Datetime question answers, as those can lead

View File

@@ -30,7 +30,7 @@ from typing import Tuple
import bleach
import vat_moss.exchange_rates
from bidi import get_display
from bidi.algorithm import get_display
from django.contrib.staticfiles import finders
from django.db.models import Sum
from django.dispatch import receiver

View File

@@ -1,18 +0,0 @@
# Generated by Django 4.2.13 on 2024-07-17 14:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0268_remove_subevent_items_remove_subevent_variations_and_more'),
]
operations = [
migrations.AddField(
model_name='order',
name='api_meta',
field=models.JSONField(default=dict),
),
]

View File

@@ -28,9 +28,9 @@ from django.utils.translation import gettext as _, gettext_lazy, pgettext_lazy
from pretix.base.modelimport import (
BooleanColumnMixin, DatetimeColumnMixin, DecimalColumnMixin, ImportColumn,
IntegerColumnMixin, SubeventColumnMixin, i18n_flat,
IntegerColumnMixin, i18n_flat,
)
from pretix.base.models import ItemVariation, Quota, Seat, SubEvent, Voucher
from pretix.base.models import ItemVariation, Quota, Seat, Voucher
from pretix.base.signals import voucher_import_columns
@@ -55,11 +55,11 @@ class CodeColumn(ImportColumn):
obj.code = value
class SubeventColumn(SubeventColumnMixin, ImportColumn):
class SubeventColumn(ImportColumn):
identifier = 'subevent'
verbose_name = pgettext_lazy('subevents', 'Date')
def assign(self, value, obj: SubEvent, **kwargs):
def assign(self, value, obj: Voucher, **kwargs):
obj.subevent = value

View File

@@ -299,11 +299,6 @@ class Order(LockModel, LoggedModel):
verbose_name=_("Meta information"),
null=True, blank=True
)
api_meta = models.JSONField(
verbose_name=_("API meta information"),
null=False, blank=True,
default=dict
)
last_modified = models.DateTimeField(
auto_now=True, db_index=False
)

View File

@@ -49,7 +49,7 @@ from io import BytesIO
import jsonschema
import reportlab.rl_config
from bidi import get_display
from bidi.algorithm import get_display
from django.conf import settings
from django.contrib.staticfiles import finders
from django.core.exceptions import ValidationError

View File

@@ -275,7 +275,7 @@ class CartManager:
}
def __init__(self, event: Event, cart_id: str, sales_channel: SalesChannel,
invoice_address: InvoiceAddress=None, widget_data=None, expiry=None):
invoice_address: InvoiceAddress=None, widget_data=None):
self.event = event
self.cart_id = cart_id
self.real_now_dt = now()
@@ -287,7 +287,6 @@ class CartManager:
self._variations_cache = {}
self._seated_cache = {}
self._expiry = None
self._explicit_expiry = expiry
self.invoice_address = invoice_address
self._widget_data = widget_data or {}
self._sales_channel = sales_channel
@@ -306,12 +305,7 @@ class CartManager:
return self._seated_cache[item, subevent]
def _calculate_expiry(self):
if self._explicit_expiry:
self._expiry = self._explicit_expiry
else:
self._expiry = self.real_now_dt + timedelta(
minutes=self.event.settings.get('reservation_time', as_type=int)
)
self._expiry = self.real_now_dt + timedelta(minutes=self.event.settings.get('reservation_time', as_type=int))
def _check_presale_dates(self):
if self.event.presale_start and time_machine_now(self.real_now_dt) < self.event.presale_start:

View File

@@ -960,7 +960,7 @@ def _get_fees(positions: List[CartPosition], payment_requests: List[dict], addre
def _create_order(event: Event, *, email: str, positions: List[CartPosition], now_dt: datetime,
payment_requests: List[dict], sales_channel: SalesChannel, locale: str=None,
address: InvoiceAddress=None, meta_info: dict=None, shown_total=None,
customer=None, valid_if_pending=False, api_meta: dict=None):
customer=None, valid_if_pending=False):
payments = []
try:
@@ -985,7 +985,6 @@ def _create_order(event: Event, *, email: str, positions: List[CartPosition], no
total=total,
testmode=True if sales_channel.type_instance.testmode_supported and event.testmode else False,
meta_info=json.dumps(meta_info or {}),
api_meta=api_meta or {},
require_approval=require_approval,
sales_channel=sales_channel,
customer=customer,
@@ -1097,7 +1096,7 @@ def _order_placed_email_attendee(event: Event, order: Order, position: OrderPosi
def _perform_order(event: Event, payment_requests: List[dict], position_ids: List[str],
email: str, locale: str, address: int, meta_info: dict=None, sales_channel: str='web',
shown_total=None, customer=None, api_meta: dict=None):
shown_total=None, customer=None):
for p in payment_requests:
p['pprov'] = event.get_payment_providers(cached=True)[p['provider']]
if not p['pprov']:
@@ -1201,8 +1200,7 @@ def _perform_order(event: Event, payment_requests: List[dict], position_ids: Lis
sales_channel=sales_channel,
shown_total=shown_total,
customer=customer,
valid_if_pending=valid_if_pending,
api_meta=api_meta,
valid_if_pending=valid_if_pending
)
try:
@@ -2875,13 +2873,12 @@ class OrderChangeManager:
@app.task(base=ProfiledEventTask, bind=True, max_retries=5, default_retry_delay=1, throws=(OrderError,))
def perform_order(self, event: Event, payments: List[dict], positions: List[str],
email: str=None, locale: str=None, address: int=None, meta_info: dict=None,
sales_channel: str='web', shown_total=None, customer=None, override_now_dt: datetime=None,
api_meta: dict=None):
sales_channel: str='web', shown_total=None, customer=None, override_now_dt: datetime=None):
with language(locale), time_machine_now_assigned(override_now_dt):
try:
try:
return _perform_order(event, payments, positions, email, locale, address, meta_info,
sales_channel, shown_total, customer, api_meta)
sales_channel, shown_total, customer)
except LockTimeoutException:
self.retry()
except (MaxRetriesExceededError, LockTimeoutException):

View File

@@ -1478,10 +1478,6 @@ DEFAULTS = {
min_value=1,
required=True,
widget=forms.NumberInput(),
help_text=_('With an increased limit, a customer may request more than one ticket for a specific product '
'using the same, unique email address. However, regardless of this setting, they will need to '
'fill the waitlist form multiple times if they want more than one ticket, as every entry only '
'grants one single ticket at a time.'),
)
},
'show_checkin_number_user': {

View File

@@ -40,6 +40,7 @@ from urllib.parse import urlencode
from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.files.uploadedfile import UploadedFile
from django.db.models import Max
from django.forms.formsets import DELETION_FIELD_NAME
from django.urls import reverse
@@ -62,9 +63,9 @@ from pretix.base.models import (
from pretix.base.models.items import ItemAddOn, ItemBundle, ItemMetaValue
from pretix.base.signals import item_copy_data
from pretix.control.forms import (
ButtonGroupRadioSelect, ExtFileField, ItemMultipleChoiceField,
SalesChannelCheckboxSelectMultiple, SplitDateTimeField,
SplitDateTimePickerWidget,
ButtonGroupRadioSelect, ItemMultipleChoiceField,
SalesChannelCheckboxSelectMultiple, SizeValidationMixin,
SplitDateTimeField, SplitDateTimePickerWidget,
)
from pretix.control.forms.widgets import Select2, Select2ItemVarMulti
from pretix.helpers.models import modelcopy
@@ -561,13 +562,6 @@ class TicketNullBooleanSelect(forms.NullBooleanSelect):
class ItemUpdateForm(I18nModelForm):
picture = ExtFileField(
label=_('Product picture'),
ext_whitelist=settings.FILE_UPLOAD_EXTENSIONS_IMAGE,
max_size=settings.FILE_UPLOAD_MAX_SIZE_IMAGE,
required=False,
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['tax_rule'].queryset = self.instance.event.tax_rules.all()
@@ -757,6 +751,14 @@ class ItemUpdateForm(I18nModelForm):
return d
def clean_picture(self):
value = self.cleaned_data.get('picture')
if isinstance(value, UploadedFile) and value.size > settings.FILE_UPLOAD_MAX_SIZE_IMAGE:
raise forms.ValidationError(_("Please do not upload files larger than {size}!").format(
size=SizeValidationMixin._sizeof_fmt(settings.FILE_UPLOAD_MAX_SIZE_IMAGE)
))
return value
class Meta:
model = Item
localized_fields = '__all__'

View File

@@ -58,8 +58,7 @@ from pretix.api.models import WebHook
from pretix.api.webhooks import get_all_webhook_events
from pretix.base.customersso.oidc import oidc_validate_and_complete_config
from pretix.base.forms import (
SECRET_REDACTED, I18nMarkdownTextarea, I18nModelForm, PlaceholderValidator,
SecretKeySettingsField, SettingsForm,
I18nMarkdownTextarea, I18nModelForm, PlaceholderValidator, SettingsForm,
)
from pretix.base.forms.questions import (
NamePartsFormField, WrappedPhoneNumberPrefixWidget, get_country_by_locale,
@@ -959,7 +958,7 @@ class SSOProviderForm(I18nModelForm):
label=pgettext_lazy('sso_oidc', 'Client ID'),
required=False,
)
config_oidc_client_secret = SecretKeySettingsField(
config_oidc_client_secret = forms.CharField(
label=pgettext_lazy('sso_oidc', 'Client secret'),
required=False,
)
@@ -1016,13 +1015,7 @@ class SSOProviderForm(I18nModelForm):
if self.instance and self.instance.method == method:
f.initial = self.instance.configuration.get(suffix)
def _unmask_secret_fields(self):
for k, v in self.cleaned_data.items():
if isinstance(self.fields.get(k), SecretKeySettingsField) and self.cleaned_data.get(k) == SECRET_REDACTED:
self.cleaned_data[k] = self.fields[k].initial
def clean(self):
self._unmask_secret_fields()
data = self.cleaned_data
if not data.get("method"):
return data

View File

@@ -98,14 +98,6 @@ class ControlFieldRenderer(FieldRenderer):
attrs = ''
return '<div class="{klass}"{attrs}>{html}</div>'.format(klass=self.get_form_group_class(), html=html, attrs=attrs)
def wrap_widget(self, html):
if isinstance(self.widget, CheckboxInput):
css_class = "checkbox"
if self.field.field.disabled:
css_class += " disabled"
html = f'<div class="{css_class}">{html}</div>'
return html
class ControlFieldWithVisibilityRenderer(ControlFieldRenderer):
def __init__(self, *args, **kwargs):

View File

@@ -57,7 +57,7 @@
{% for se in request.event.subevents.all %}
<option value="{{ se.id }}"
{% if request.GET.subevent|add:0 == se.id %}selected="selected"{% endif %}>
{{ se }}
{{ se.name }} {{ se.get_date_range_display }}
</option>
{% endfor %}
</select>
@@ -119,7 +119,7 @@
{% for se in request.event.subevents.all %}
<option value="{{ se.id }}"
{% if request.GET.subevent|add:0 == se.id %}selected="selected"{% endif %}>
{{ se }}
{{ se.name }} {{ se.get_date_range_display }}
</option>
{% endfor %}
</select>
@@ -195,7 +195,7 @@
{% endif %}
</td>
{% if request.event.has_subevents %}
<td>{{ e.subevent }}</td>
<td>{{ e.subevent.name }} {{ e.subevent.get_date_range_display }}</td>
{% endif %}
<td>
{{ e.created|date:"SHORT_DATETIME_FORMAT" }}

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-24 08:20+0000\n"
"PO-Revision-Date: 2024-07-10 15:00+0000\n"
"PO-Revision-Date: 2024-05-30 17:00+0000\n"
"Last-Translator: Nikolai <nikolai@lengefeldt.de>\n"
"Language-Team: Danish <https://translate.pretix.eu/projects/pretix/pretix-js/"
"da/>\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 5.6.1\n"
"X-Generator: Weblate 5.5.5\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
@@ -261,7 +261,7 @@ msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:51
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:60
msgid "Ticket not paid"
msgstr "Billet ikke betalt"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:52
msgid "This ticket is not yet paid. Do you want to continue anyways?"
@@ -281,11 +281,11 @@ msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:56
msgid "Ticket already used"
msgstr "Billet allerede i brug"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:57
msgid "Information required"
msgstr "Kræver information"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:58
msgid "Unknown ticket"
@@ -301,15 +301,15 @@ msgstr "Adgang ikke tilladt"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:62
msgid "Ticket code revoked/changed"
msgstr "Billetkode trukket tilbage/ændret"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:63
msgid "Ticket blocked"
msgstr "Billet blokeret"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:64
msgid "Ticket not valid at this time"
msgstr "Billetten er ikke gyldig på dette tidspunkt"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65
msgid "Order canceled"
@@ -317,11 +317,11 @@ msgstr "Bestilling annulleret"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
msgid "Ticket code is ambiguous on list"
msgstr "Billetkoden er flertydig på listen"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:67
msgid "Order not approved"
msgstr "Bestilling ikke godkendt"
msgstr ""
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:68
#, fuzzy
@@ -472,7 +472,7 @@ msgstr "Produktvariation"
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:107
msgid "Gate"
msgstr "Indgang"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:111
msgid "Current date and time"
@@ -548,7 +548,7 @@ msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:186
msgid "Event admission"
msgstr "Adgang til arrangementet"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:187
msgid "custom date and time"

View File

@@ -5,7 +5,7 @@ msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-30 18:55+0000\n"
"PO-Revision-Date: 2024-07-19 22:00+0000\n"
"PO-Revision-Date: 2024-07-02 19:00+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix/de/"
">\n"
@@ -14,7 +14,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 5.6.2\n"
"X-Generator: Weblate 5.6.1\n"
"X-Poedit-Bookmarks: -1,-1,904,-1,-1,-1,-1,-1,-1,-1\n"
#: pretix/_base_settings.py:78
@@ -4921,8 +4921,8 @@ msgid ""
"The duration of the membership is the same as the duration of the event or "
"event series date"
msgstr ""
"Die Dauer der Mitgliedschaft entspricht der Dauer der Veranstaltung bzw. in "
"Veranstaltungsreihen des gebuchten Termins"
"Die Dauer der Mitgliedschaft entspricht der Dauer der Veranstaltung oder des "
"Termins"
#: pretix/base/models/items.py:660
msgid "Membership duration in days"

View File

@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-30 18:55+0000\n"
"PO-Revision-Date: 2024-07-20 13:00+0000\n"
"Last-Translator: JnnisCanis <me@jnnis.de>\n"
"PO-Revision-Date: 2024-07-02 19:00+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
"pretix/pretix/de_Informal/>\n"
"Language: de_Informal\n"
@@ -17,7 +17,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 5.6.2\n"
"X-Generator: Weblate 5.6.1\n"
#: pretix/_base_settings.py:78
msgid "English"
@@ -4917,8 +4917,8 @@ msgid ""
"The duration of the membership is the same as the duration of the event or "
"event series date"
msgstr ""
"Die Dauer der Mitgliedschaft entspricht der Dauer der Veranstaltung bzw. in "
"Veranstaltungsreihen des gebuchten Termins"
"Die Dauer der Mitgliedschaft entspricht der Dauer der Veranstaltung oder des "
"Termins"
#: pretix/base/models/items.py:660
msgid "Membership duration in days"
@@ -6671,10 +6671,11 @@ msgid ""
"rel=\"noopener\">Click here for detailed information on what this does.</a> "
"Don't forget to set the correct fees above!"
msgstr ""
"Wir empfehlen, diese Option zu aktivieren, wenn Du die Gebühren deines "
"Zahlungsdienstleisters an deine Kunden weitergeben willst. Denke daran, oben "
"die richtigen Gebührensätze des Zahlungsanbieters einzustellen. <a href=\""
"{docs_url}\" target=\"_blank\" rel=\"noopener\">Weitere Informationen</a>."
"Wir empfehlen, diese Option zu aktivieren, wenn du die Gebühren Ihres "
"Zahlungsdienstleisters an Ihre Kunden weitergeben willst. Denk daran, oben "
"die richtigen Gebührensätze des Zahlungsanbieters einzustellen. <a "
"href=\"{docs_url}\" target=\"_blank\" rel=\"noopener\">Weitere "
"Informationen</a>."
#: pretix/base/payment.py:394
msgid "Text on invoices"
@@ -9238,9 +9239,9 @@ msgid ""
msgstr ""
"Anzahl an Minuten, die ein Benutzer nach Abschicken der Bestellung hat, um "
"die Zahlung abzuschließen. Benutze dies nur, wenn du ausschließlich "
"Zahlungsmethoden mit sofortiger Bestätigung anbietest. Bitte beachte, dass "
"das tatsächliche Zeitfenster aus technischen Gründen einige Minuten länger "
"sein kann."
"Zahlungsmethoden mit sofortiger Bestätigung anbietest. Bitte beachte das das "
"tatsächliche Zeitfenster aus technischen Gründen einige Minuten länger sein "
"kann."
#: pretix/base/settings.py:920
msgid "Last date of payments"
@@ -9913,8 +9914,7 @@ msgstr "Keine Änderungen an bestehenden Bestellungen"
#: pretix/base/settings.py:1665 pretix/base/settings.py:1674
msgid "Only the person who ordered can make changes"
msgstr ""
"Nur die Person, welche die Bestellung aufgegeben hat, kann Änderungen "
"vornehmen"
"Nur die Person, die die Bestellung aufgegeben hat, kann Änderungen vornehmen"
#: pretix/base/settings.py:1666 pretix/base/settings.py:1675
msgid "Both the attendee and the person who ordered can make changes"
@@ -10854,7 +10854,7 @@ msgstr ""
"Hallo,\n"
"\n"
"du hast dich auf die Warteliste für {event} \n"
"und das Produkt {product} eingetragen.\n"
"für das Produkt {product} eingetragen.\n"
"\n"
"Wir haben nun ein Ticket für dich! Du kannst es in unserem Ticketshop "
"erwerben,\n"
@@ -10872,8 +10872,9 @@ msgstr ""
"weitergeben,\n"
"wenn du den Gutschein nicht in diesem Zeitraum einlöst.\n"
"\n"
"Wenn du das Ticket NICHT mehr möchtest, sag uns bitte durch Klicken auf "
"folgenden Link Bescheid. So können wir das Ticket schnellstmöglich an die "
"Wenn du das Ticket NICHT mehr möchtest, bitten wir dich folgenden Link zu "
"klicken\n"
"um uns Bescheid zu sagen. So können wir das Ticket schnellstmöglich an die "
"nächste\n"
"wartende Person auf der Warteliste weitergeben:\n"
"\n"
@@ -13975,7 +13976,7 @@ msgid ""
"set the name here."
msgstr ""
"Wenn du den \"powered by\"-Text in der Fußzeile anpassen willst, um den "
"Namen deiner Firma oder Organisation aufzunehmen (wenn du Änderungen an "
"Namen Ihrer Firma oder Organisation aufzunehmen (wenn du Änderungen an "
"pretix vorgenommen hast), kannst du hier den Namen eintragen."
#: pretix/control/forms/global_settings.py:205
@@ -14640,7 +14641,7 @@ msgstr ""
"Erstattungen angezeigt. Wenn diese Option mit der Option für automatische "
"Erstattungen kombiniert wird, werden ausschließlich Zahlungen mit einer "
"Zahlungsart, die keine automatischen Erstattungen unterstützt, automatisch "
"als manuelle Erstattungen auf deiner Erstattungsliste als \"zu erledigen\" "
"als manuelle Erstattungen auf Ihrer Erstattungsliste als \"zu erledigen\" "
"auftauchen. Wähle diese Option nicht, wenn du Bestellungen mit anderen "
"Bestellungen verrechnen oder als Gutschein erstatten möchtest."
@@ -17465,7 +17466,7 @@ msgstr "Filter"
#: pretix/control/templates/pretixcontrol/checkin/checkins.html:50
msgid "Your search did not match any check-ins."
msgstr "Deine Filter treffen auf keine Check-ins zu."
msgstr "Ihre Filter treffen auf keine Check-ins zu."
#: pretix/control/templates/pretixcontrol/checkin/checkins.html:52
msgid "You haven't scanned any tickets yet."
@@ -19806,7 +19807,7 @@ msgid ""
"usage of pretix is in compliance with the Enterprise license you purchased."
msgstr ""
"Der Bericht dient zwei Zwecken: Dem Sammeln nützlicher Informationen um "
"Probleme mit deiner pretix-Installation zu analysieren und um zu überprüfen, "
"Probleme mit Ihrer pretix-Installation zu analysieren und um zu überprüfen, "
"dass deine Nutzung von pretix von der erworbenen pretix-Enterprise-Lizenz "
"abgedeckt ist."
@@ -30130,7 +30131,7 @@ msgid ""
"generate you API keys with the recommended permission level for optimal "
"usage with pretix."
msgstr ""
"Der obige Button wird unsere Stripe-App in deinem Stripe-Konto installieren "
"Der Button oben wird unsere Stripe-App in Ihrem Stripe-Konto installieren "
"und API-Schlüssel mit dem empfohlenen Berechtigungslevel für die Verwendung "
"mit pretix generieren."
@@ -30381,7 +30382,7 @@ msgstr ""
#: pretix/plugins/stripe/payment.py:992
msgid "Your payment failed. Please try again."
msgstr "Deine Zahlung ist fehlgeschlagen. Bitte versuche es erneut."
msgstr "Ihre Zahlung ist fehlgeschlagen, bitte erneut versuchen."
#: pretix/plugins/stripe/payment.py:998
#, python-format
@@ -32465,7 +32466,7 @@ msgid ""
"If you click the link in our email, you will be able to download your "
"tickets here."
msgstr ""
"Wenn du den Link in deiner E-Mail anklickst, kannst du deine Tickets hier "
"Wenn du den Link in Ihrer E-Mail anklickst, kannst du deine Tickets hier "
"herunterladen."
#: pretix/presale/templates/pretixpresale/event/fragment_downloads.html:30
@@ -33690,7 +33691,7 @@ msgstr "Konto erstellen"
#: pretix/presale/templates/pretixpresale/organizers/customer_membership.html:7
#: pretix/presale/templates/pretixpresale/organizers/customer_membership.html:10
msgid "Your membership"
msgstr "Deine Mitgliedschaft"
msgstr "Ihre Mitgliedschaft"
#: pretix/presale/templates/pretixpresale/organizers/customer_password.html:6
#: pretix/presale/templates/pretixpresale/organizers/customer_resetpw.html:6

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-24 08:20+0000\n"
"PO-Revision-Date: 2024-07-19 08:56+0000\n"
"PO-Revision-Date: 2024-06-25 01:00+0000\n"
"Last-Translator: Reece Needham <nouveaureece@protonmail.com>\n"
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/"
"pretix-js/es/>\n"
@@ -17,7 +17,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 5.6.2\n"
"X-Generator: Weblate 5.5.5\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
@@ -254,7 +254,7 @@ msgstr "Cancelar"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:51
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:60
msgid "Ticket not paid"
msgstr "Entrada no pagada"
msgstr "Ticket por pagar"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:52
msgid "This ticket is not yet paid. Do you want to continue anyways?"
@@ -274,7 +274,7 @@ msgstr "Salida registrada"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:56
msgid "Ticket already used"
msgstr "Esta entrada ya fue utilizada"
msgstr "Este ticket ya fue utilizado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:57
msgid "Information required"
@@ -282,11 +282,11 @@ msgstr "Información requerida"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:58
msgid "Unknown ticket"
msgstr "Entrada desconocida"
msgstr "Ticket no encontrado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:59
msgid "Ticket type not allowed here"
msgstr "Tipo de entrada no está permitido"
msgstr "Tipo de ticket no está permitido"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:61
msgid "Entry not allowed"
@@ -294,19 +294,19 @@ msgstr "Entrada no permitida"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:62
msgid "Ticket code revoked/changed"
msgstr "Código de entrada revocada/cambiada"
msgstr "Código de ticket revocado/cambiado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:63
msgid "Ticket blocked"
msgstr "Entrada bloqueada"
msgstr "Ticket bloqueado"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:64
msgid "Ticket not valid at this time"
msgstr "Entrada no válida en este momento"
msgstr "Ticket no válido en este momento"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65
msgid "Order canceled"
msgstr "Pedido cancelado"
msgstr "Orden cancelada"
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:66
msgid "Ticket code is ambiguous on list"
@@ -591,7 +591,7 @@ msgstr "Objeto"
#: pretix/static/pretixcontrol/js/ui/editor.js:673
msgid "Ticket design"
msgstr "Diseño del entrada"
msgstr "Diseño del ticket"
#: pretix/static/pretixcontrol/js/ui/editor.js:972
msgid "Saving failed."

File diff suppressed because it is too large Load Diff

View File

@@ -7,16 +7,16 @@ msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-30 18:55+0000\n"
"PO-Revision-Date: 2024-07-11 02:00+0000\n"
"Last-Translator: Dirk-jan mollema <dirkjan@outsidersecurity.nl>\n"
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix/nl/>"
"\n"
"PO-Revision-Date: 2024-04-19 23:00+0000\n"
"Last-Translator: Christiaan de Die le Clercq <contact@techwolf12.nl>\n"
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix/nl/"
">\n"
"Language: nl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.6.1\n"
"X-Generator: Weblate 5.4.3\n"
#: pretix/_base_settings.py:78
msgid "English"
@@ -25927,8 +25927,10 @@ msgstr "Voucherhistorie"
#: pretix/control/templates/pretixcontrol/vouchers/import_start.html:6
#: pretix/control/templates/pretixcontrol/vouchers/index.html:81
#: pretix/control/templates/pretixcontrol/vouchers/index.html:94
#, fuzzy
#| msgid "Import mode"
msgid "Import vouchers"
msgstr "Importeer vouchers"
msgstr "Importmodus"
#: pretix/control/templates/pretixcontrol/vouchers/index.html:10
msgid ""
@@ -33192,8 +33194,10 @@ msgid "There are no add-ons available for this product."
msgstr "Er zijn geen add-ons beschikbaar voor dit product."
#: pretix/presale/templates/pretixpresale/event/fragment_availability.html:6
#, fuzzy
#| msgid "Enter a voucher code below to buy this ticket."
msgid "Enter a voucher code below to buy this product."
msgstr "Voer hieronder een vouchercode in om dit product te kopen."
msgstr "Voer hieronder een vouchercode in om dit ticket te kopen."
#: pretix/presale/templates/pretixpresale/event/fragment_availability.html:10
#, fuzzy

File diff suppressed because it is too large Load Diff

View File

@@ -119,7 +119,6 @@ class CheckInListMixin(BaseExporter):
choices=[
('name', _('Attendee name')),
('code', _('Order code')),
('order_datetime', _('Order date')),
] + ([
('name:{}'.format(k), _('Attendee name: {part}').format(part=label))
for k, label, w in name_scheme['fields']
@@ -230,8 +229,6 @@ class CheckInListMixin(BaseExporter):
)
elif sort == 'code':
qs = qs.order_by(*o, 'order__code')
elif sort == 'order_datetime':
qs = qs.order_by(*o, '-order__datetime')
elif sort.startswith('name:'):
part = sort[5:]
qs = qs.annotate(
@@ -519,7 +516,6 @@ class CSVCheckinList(CheckInListMixin, ListExporter):
headers.append(_('Order time'))
headers.append(_('Requires special attention'))
headers.append(_('Comment'))
headers.append(_('Check-in text'))
headers.append(_('Seat ID'))
headers.append(_('Seat name'))
headers.append(_('Seat zone'))
@@ -627,7 +623,6 @@ class CSVCheckinList(CheckInListMixin, ListExporter):
row.append(op.order.datetime.astimezone(self.event.timezone).strftime('%H:%M:%S'))
row.append(_('Yes') if op.require_checkin_attention else _('No'))
row.append(op.order.comment or "")
row.append("\n".join(text for text in [op.order.checkin_text, op.item.checkin_text] if text))
if op.seat:
row += [

View File

@@ -163,7 +163,7 @@ def signal_process_response(sender, request: HttpRequest, response: HttpResponse
# 'frame-src': ['https://www.paypal.com', 'https://www.sandbox.paypal.com', "'nonce-{}'".format(_nonce(request))],
'frame-src': ['https:', "'nonce-{}'".format(_nonce(request))],
'connect-src': ['https://www.paypal.com', 'https://www.sandbox.paypal.com'], # Or not - seems to only affect PayPal logging...
'img-src': ['https://t.paypal.com', 'https://www.paypalobjects.com'],
'img-src': ['https://t.paypal.com'],
'style-src': ["'unsafe-inline'"] # PayPal does not comply with our nonce unfortunately, see Z#23113213
}

View File

@@ -1585,7 +1585,7 @@ class StripeBancontact(StripeRedirectWithAccountNamePaymentIntentMethod):
return super().payment_presale_render(payment)
class StripeSofort(StripeRedirectMethod):
class StripeSofort(StripeMethod):
identifier = 'stripe_sofort'
verbose_name = _('SOFORT via Stripe')
public_name = _('SOFORT (instant bank transfer)')

View File

@@ -295,6 +295,7 @@ var pretixstripe = {
let iframe = document.createElement('iframe');
iframe.src = payment_intent_next_action_redirect_url;
iframe.className = 'embed-responsive-item';
iframe.setAttribute('sandbox', 'allow-same-origin allow-scripts allow-top-navigation');
$('#scacontainer').append(iframe);
$('#scacontainer iframe').on("load", function () {
waitingDialog.hide();
@@ -430,4 +431,4 @@ $(function () {
}
}
);
});
});

View File

@@ -85,7 +85,7 @@ from pretix.presale.forms.customer import AuthenticationForm, RegistrationForm
from pretix.presale.signals import (
checkout_all_optional, checkout_confirm_messages, checkout_flow_steps,
contact_form_fields, contact_form_fields_overrides,
order_api_meta_from_request, order_meta_from_request, question_form_fields,
order_meta_from_request, question_form_fields,
question_form_fields_overrides,
)
from pretix.presale.utils import customer_login
@@ -1544,14 +1544,11 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep):
str(m) for m in self.confirm_messages.values()
]
}
api_meta = {}
unlock_hashes = request.session.get('pretix_unlock_hashes', [])
if unlock_hashes:
meta_info['unlock_hashes'] = unlock_hashes
for receiver, response in order_meta_from_request.send(sender=request.event, request=request):
meta_info.update(response)
for receiver, response in order_api_meta_from_request.send(sender=request.event, request=request):
api_meta.update(response)
return self.do(
self.request.event.id,
@@ -1565,7 +1562,6 @@ class ConfirmStep(CartMixin, AsyncAction, TemplateFlowStep):
shown_total=self.cart_session.get('shown_total'),
customer=self.cart_session.get('customer'),
override_now_dt=time_machine_now(default=None),
api_meta=api_meta,
)
def get_success_message(self, value):

View File

@@ -174,11 +174,3 @@ class CheckoutFieldRenderer(FieldRenderer):
else:
attrs = ''
return '<div class="{klass}"{attrs}>{html}</div>'.format(klass=self.get_form_group_class(), html=html, attrs=attrs)
def wrap_widget(self, html):
if isinstance(self.widget, CheckboxInput):
css_class = "checkbox"
if self.field.field.disabled:
css_class += " disabled"
html = f'<div class="{css_class}">{html}</div>'
return html

View File

@@ -170,17 +170,6 @@ You will receive the request triggering the order creation as the ``request`` ke
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
order_api_meta_from_request = EventPluginSignal()
"""
Arguments: ``request``
This signal is sent before an order is created through the pretixpresale frontend. It allows you
to return a dictionary that will be merged in the api_meta attribute of the order.
You will receive the request triggering the order creation as the ``request`` keyword argument.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
checkout_confirm_page_content = EventPluginSignal()
"""
Arguments: ``request``

View File

@@ -179,7 +179,7 @@ class TicketPageMixin:
can_download = can_download and self.order.ticket_download_available
ctx['download_email_required'] = can_download and (
self.request.event.settings.ticket_download_require_validated_email and
self.order.sales_channel.type == 'web' and
self.order.sales_channel == 'web' and
not self.order.email_known_to_work
)
ctx['can_download'] = can_download and not ctx['download_email_required']
@@ -1106,7 +1106,7 @@ class OrderDownloadMixin:
if (
self.request.event.settings.ticket_download_require_validated_email and
self.order.sales_channel.type == 'web' and
self.order.sales_channel == 'web' and
not self.order.email_known_to_work
):
return self.error(OrderError(_('Please click the link we sent you via email to download your tickets.')))

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
"scripts": {},
"dependencies": {
"@babel/core": "^7.24.7",
"@babel/preset-env": "^7.24.7",
"@babel/preset-env": "^7.24.6",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-node-resolve": "^15.2.3",
"vue": "^2.7.16",

View File

@@ -195,7 +195,7 @@ input[type=number]::-webkit-outer-spin-button {
}
.alert-danger::before {
background-color: $state-danger-border;
background-image: url('data:image/svg+xml,<svg viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg" xml:space="preserve"><path d="M12.14 4.62h11.64l8.24 8.24V23.4l-8.24 8.24H12.14L3.9 23.39V12.86l8.24-8.24Zm12.6 17.98c0-.28-.11-.56-.31-.76l-3.27-3.27 3.27-3.27a1.085 1.085 0 0 0 0-1.52l-1.51-1.5a1.085 1.085 0 0 0-1.52 0l-3.27 3.26-3.27-3.27a1.085 1.085 0 0 0-1.52 0l-1.5 1.51a1.085 1.085 0 0 0 0 1.52l3.26 3.27-3.27 3.27a1.085 1.085 0 0 0 0 1.52l1.51 1.51a1.085 1.085 0 0 0 1.52 0l3.27-3.27 3.27 3.27a1.085 1.085 0 0 0 1.52 0l1.51-1.51c.2-.2.31-.48.31-.76Z" style="fill:%23fff"/></svg>');
background-image: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2036%2036'%20xmlns='http://www.w3.org/2000/svg'%20xml:space='preserve'%3E%3Cpath%20d='M12.14%204.62h11.64l8.24%208.24V23.4l-8.24%208.24H12.14L3.9%2023.39V12.86l8.24-8.24Z'%20fill='%23fff'/%3E%3Cpath%20d='M24.74%2022.6c0-.28-.11-.56-.31-.76l-3.27-3.27%203.27-3.27a1.08%201.08%200%200%200%200-1.52l-1.51-1.5a1.08%201.08%200%200%200-1.52%200l-3.27%203.26-3.27-3.27a1.08%201.08%200%200%200-1.52%200l-1.5%201.51a1.08%201.08%200%200%200%200%201.52l3.26%203.27-3.27%203.27a1.08%201.08%200%200%200%200%201.52l1.51%201.51a1.08%201.08%200%200%200%201.52%200l3.27-3.27%203.27%203.27a1.08%201.08%200%200%200%201.52%200l1.51-1.51c.2-.2.31-.48.31-.76Z'%20fill='#{url-friendly-colour($state-danger-text)}'/%3E%3C/svg%3E%0A");
}
.alert-primary::before {
background: $brand-primary !important;

View File

@@ -781,7 +781,7 @@ function setup_basics(el) {
scrollTarget.id = "panel_" + $("input", scrollTarget).attr("id");
}
} else {
label = $("label", this).first().contents().filter(function () { return this.nodeType != Node.ELEMENT_NODE || !this.classList.contains("optional") }).text();
label = $("label", this).first().text();
description = $(".help-block", this).first().text();
scrollTarget = $(":input", this).get(0);
}

View File

@@ -767,10 +767,8 @@ h1 .label {
.plugin-container {
flex-basis: 100%;
flex-shrink: 0;
padding-top: 15px;
}
.plugin-container:not(.featured-plugin) + .plugin-container {
border-top: 1px solid #ccc;
padding-top: 15px;
}
h4 {
margin-top: 0;

View File

@@ -380,28 +380,23 @@ $(function () {
" #id_city, #id_country, #id_state").change(function () {
if (copy_to_first_ticket) {
var $first_ticket_form = $(".questions-form").first().find("[data-addonidx=0]");
$first_ticket_form.find("[id$=" + this.id.substring(3) + "]").val(this.value);
if (this.placeholder) {
$first_ticket_form.find("[placeholder='" + this.placeholder + "']").val(this.value);
}
var label = $("label[for=" + this.id +"]").first().contents().filter(function () {
return this.nodeType != Node.ELEMENT_NODE || !this.classList.contains("sr-only");
}).text().trim();
if (label) {
// match to placeholder and label
$first_ticket_form.find("[placeholder='" + label + "']").val(this.value);
var v = this.value;
$first_ticket_form.find("label").each(function() {
var text = $(this).first().contents().filter(function () {
return this.nodeType != Node.ELEMENT_NODE || !this.classList.contains("sr-only");
}).text().trim();
if (text == label) {
$("#" + this.getAttribute("for")).val(v);
}
});
$first_ticket_form.find("input[id*=attendee_email]").val($("#id_email").val());
$first_ticket_form.find("input[id$=company]").val($("#id_company").val());
$first_ticket_form.find("textarea[id$=street]").val($("#id_street").val());
$first_ticket_form.find("input[id$=zipcode]").val($("#id_zipcode").val());
$first_ticket_form.find("input[id$=city]").val($("#id_city").val());
$first_ticket_form.find("select[id$=state]").val($("#id_state").val());
if ($first_ticket_form.find("select[id$=country]").val() !== $("#id_country").val()) {
$first_ticket_form.find("select[id$=country]").val($("#id_country").val()).trigger('change');
}
$first_ticket_form.find("[id*=attendee_name_parts]").each(function () {
var parts = $(this).attr("id").split("_");
var num = parts[parts.length - 1];
$(this).val($("#id_name_parts_" + num).val());
});
}
}).trigger("change");
});
attendee_address_fields.change(function () {
copy_to_first_ticket = false;
});

View File

@@ -33,7 +33,6 @@ filterwarnings =
ignore::DeprecationWarning:django
ignore::DeprecationWarning:cgi
ignore::DeprecationWarning:vat_moss
ignore::cryptography.utils.CryptographyDeprecationWarning:pypdf
ignore:.*ast.NameConstant.*:DeprecationWarning:reportlab
ignore:.*utcnow.*:DeprecationWarning:
ignore:.*PyType_Spec.*:DeprecationWarning:

View File

@@ -86,18 +86,6 @@ def test_giftcard_list(token_client, organizer, event, giftcard, other_giftcard)
assert resp.status_code == 200
assert 2 == len(resp.data['results'])
resp = token_client.get('/api/v1/organizers/{}/giftcards/?expired=false'.format(organizer.slug))
assert 1 == len(resp.data['results'])
resp = token_client.get('/api/v1/organizers/{}/giftcards/?expired=true'.format(organizer.slug))
assert 0 == len(resp.data['results'])
resp = token_client.get('/api/v1/organizers/{}/giftcards/?value=23.00'.format(organizer.slug))
assert 1 == len(resp.data['results'])
resp = token_client.get('/api/v1/organizers/{}/giftcards/?value=23'.format(organizer.slug))
assert 1 == len(resp.data['results'])
resp = token_client.get('/api/v1/organizers/{}/giftcards/?value=24'.format(organizer.slug))
assert 0 == len(resp.data['results'])
@pytest.mark.django_db
def test_giftcard_detail(token_client, organizer, event, giftcard):

View File

@@ -370,13 +370,6 @@ def test_item_list(token_client, organizer, event, team, item):
assert resp.status_code == 200
assert [] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/items/?search=Budget'.format(organizer.slug, event.slug))
assert resp.status_code == 200
assert [res] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/items/?search=Free'.format(organizer.slug, event.slug))
assert resp.status_code == 200
assert [] == resp.data['results']
@pytest.mark.django_db
def test_item_detail(token_client, organizer, event, team, item):
@@ -1419,21 +1412,6 @@ def test_variations_list(token_client, organizer, event, item, variation):
assert res['position'] == resp.data['results'][0]['position']
assert res['price'] == resp.data['results'][0]['price']
resp = token_client.get('/api/v1/organizers/{}/events/{}/items/{}/variations/?active=true'.format(organizer.slug, event.slug, item.pk))
assert resp.status_code == 200
assert res['value'] == resp.data['results'][0]['value']
resp = token_client.get(
'/api/v1/organizers/{}/events/{}/items/{}/variations/?active=false'.format(organizer.slug, event.slug, item.pk))
assert resp.status_code == 200
assert [] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/items/{}/variations/?search=Child'.format(organizer.slug, event.slug, item.pk))
assert resp.status_code == 200
assert res['value'] == resp.data['results'][0]['value']
resp = token_client.get('/api/v1/organizers/{}/events/{}/items/{}/variations/?search=Incorrect'.format(organizer.slug, event.slug, item.pk))
assert resp.status_code == 200
assert [] == resp.data['results']
@pytest.mark.django_db
def test_variations_detail(token_client, organizer, event, item, variation):

View File

@@ -255,9 +255,6 @@ def test_order_update_allowed_fields(token_client, organizer, event, order):
organizer.slug, event.slug, order.code
), format='json', data={
'comment': 'Here is a comment',
'api_meta': {
'test': 1
},
'valid_if_pending': True,
'custom_followup_at': '2021-06-12',
'checkin_attention': True,
@@ -283,9 +280,6 @@ def test_order_update_allowed_fields(token_client, organizer, event, order):
assert resp.status_code == 200
order.refresh_from_db()
assert order.comment == 'Here is a comment'
assert order.api_meta == {
'test': 1
}
assert order.custom_followup_at.isoformat() == '2021-06-12'
assert order.checkin_attention
assert order.checkin_text == 'foobar'

View File

@@ -232,9 +232,6 @@ def test_order_create(token_client, organizer, event, item, quota, question):
with scopes_disabled():
customer = organizer.customers.create()
res['customer'] = customer.identifier
res['api_meta'] = {
'test': 1
}
resp = token_client.post(
'/api/v1/organizers/{}/events/{}/orders/'.format(
organizer.slug, event.slug
@@ -254,9 +251,6 @@ def test_order_create(token_client, organizer, event, item, quota, question):
assert o.valid_if_pending
assert o.expires > now()
assert not o.testmode
assert o.api_meta == {
'test': 1
}
with scopes_disabled():
p = o.payments.first()
@@ -427,7 +421,6 @@ def test_order_create_simulate(token_client, organizer, event, item, quota, ques
],
'total': '21.75',
'comment': '',
'api_meta': {},
"custom_followup_at": None,
'invoice_address': {
'is_business': False,

View File

@@ -291,7 +291,6 @@ TEST_ORDER_RES = {
"payment_provider": "banktransfer",
"total": "23.00",
"comment": "",
"api_meta": {},
"custom_followup_at": None,
"checkin_attention": False,
"checkin_text": None,
@@ -415,16 +414,6 @@ def test_order_list(token_client, organizer, event, order, item, taxrule, questi
'/api/v1/organizers/{}/events/{}/orders/?email=foo@example.org'.format(organizer.slug, event.slug))
assert [] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?payment_provider=banktransfer'.format(organizer.slug, event.slug))
assert [res] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?payment_provider=manual'.format(organizer.slug, event.slug))
assert [] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?sales_channel=web'.format(organizer.slug, event.slug))
assert [res] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?sales_channel=bar'.format(organizer.slug, event.slug))
assert [] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?locale=en'.format(organizer.slug, event.slug))
assert [res] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?locale=de'.format(organizer.slug, event.slug))
@@ -445,36 +434,6 @@ def test_order_list(token_client, organizer, event, order, item, taxrule, questi
))
assert [] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?created_since={}'.format(
organizer.slug, event.slug,
(order.datetime - datetime.timedelta(hours=1)).isoformat().replace('+00:00', 'Z')
))
assert [res] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?created_since={}'.format(
organizer.slug, event.slug, order.datetime.isoformat().replace('+00:00', 'Z')
))
assert [res] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?created_since={}'.format(
organizer.slug, event.slug,
(order.datetime + datetime.timedelta(hours=1)).isoformat().replace('+00:00', 'Z')
))
assert [] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?created_before={}'.format(
organizer.slug, event.slug,
(order.datetime - datetime.timedelta(hours=1)).isoformat().replace('+00:00', 'Z')
))
assert [] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?created_before={}'.format(
organizer.slug, event.slug, order.datetime.isoformat().replace('+00:00', 'Z')
))
assert [] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?created_before={}'.format(
organizer.slug, event.slug,
(order.datetime + datetime.timedelta(hours=1)).isoformat().replace('+00:00', 'Z')
))
assert [res] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/?include_canceled_positions=false'.format(organizer.slug, event.slug))
assert resp.status_code == 200
assert len(resp.data['results'][0]['positions']) == 1

View File

@@ -60,11 +60,6 @@ def test_hook_list(token_client, organizer, event, webhook):
assert resp.status_code == 200
assert [res] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/webhooks/?enabled=true'.format(organizer.slug))
assert [res] == resp.data['results']
resp = token_client.get('/api/v1/organizers/{}/webhooks/?enabled=false'.format(organizer.slug))
assert [] == resp.data['results']
@pytest.mark.django_db
def test_hook_detail(token_client, organizer, event, webhook):

View File

@@ -91,12 +91,12 @@ def test_csv_simple(event):
assert clean(content.decode()) == clean(""""Order code","Attendee name","Attendee name: Title","Attendee name:
First name","Attendee name: Middle name","Attendee name: Family name","Product","Price","Checked in","Checked out","Automatically
checked in","Secret","E-mail","Phone number","Company","Voucher code","Order date","Order time","Requires special attention",
"Comment","Check-in text","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until","Address","ZIP code",
"Comment","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until","Address","ZIP code",
"City","Country","State"
"FOO","Mr Peter A Jones","Mr","Peter","A","Jones","Ticket","23.00","","","No","hutjztuxhkbtwnesv2suqv26k6ttytxx",
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
"FOO","Mrs Andrea J Zulu","Mrs","Andrea","J","Zulu","Ticket","13.00","","","No","ggsngqtnmhx74jswjngw3fk8pfwz2a7k",
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
""")
@@ -113,12 +113,12 @@ def test_csv_order_by_name_parts(event): # noqa
assert clean(content.decode()) == clean(""""Order code","Attendee name","Attendee name: Title",
"Attendee name: First name","Attendee name: Middle name","Attendee name: Family name","Product","Price",
"Checked in","Checked out","Automatically checked in","Secret","E-mail","Phone number","Company","Voucher code","Order date","Order time","Requires special
attention","Comment","Check-in text","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until",
attention","Comment","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until",
"Address","ZIP code","City","Country","State"
"FOO","Mrs Andrea J Zulu","Mrs","Andrea","J","Zulu","Ticket","13.00","","","No","ggsngqtnmhx74jswjngw3fk8pfwz2a7k",
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
"FOO","Mr Peter A Jones","Mr","Peter","A","Jones","Ticket","23.00","","","No","hutjztuxhkbtwnesv2suqv26k6ttytxx",
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
""")
c = CSVCheckinList(event, organizer=event.organizer)
_, _, content = c.render({
@@ -131,12 +131,12 @@ def test_csv_order_by_name_parts(event): # noqa
assert clean(content.decode()) == clean(""""Order code","Attendee name","Attendee name: Title",
"Attendee name: First name","Attendee name: Middle name","Attendee name: Family name","Product","Price",
"Checked in","Checked out","Automatically checked in","Secret","E-mail","Phone number","Company","Voucher code","Order date","Order time","Requires special
attention","Comment","Check-in text","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until",
attention","Comment","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until",
"Address","ZIP code","City","Country","State"
"FOO","Mr Peter A Jones","Mr","Peter","A","Jones","Ticket","23.00","","","No","hutjztuxhkbtwnesv2suqv26k6ttytxx",
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
"FOO","Mrs Andrea J Zulu","Mrs","Andrea","J","Zulu","Ticket","13.00","","","No","ggsngqtnmhx74jswjngw3fk8pfwz2a7k",
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
""")
@@ -184,12 +184,12 @@ def test_csv_order_by_inherited_name_parts(event): # noqa
assert clean(content.decode()) == clean(""""Order code","Attendee name","Attendee name: Title",
"Attendee name: First name","Attendee name: Middle name","Attendee name: Family name","Product","Price",
"Checked in","Checked out","Automatically checked in","Secret","E-mail","Phone number","Company","Voucher code","Order date","Order time","Requires special
attention","Comment","Check-in text","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until",
attention","Comment","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until",
"Address","ZIP code","City","Country","State"
"BAR","Mr Albert J Zulu","Mr","Albert","J","Zulu","Ticket","23.00","","","No","hutjztuxhkbtwnesv2suqv26k6ttytyy",
"dummy@dummy.test","'+498912345678","BARCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","BARCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
"FOO","Mr Paul A Jones","Mr","Paul","A","Jones","Ticket","23.00","","","No","hutjztuxhkbtwnesv2suqv26k6ttytxx",
"dummy@dummy.test","'+498912345678","FOOCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","FOOCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
""")
c = CSVCheckinList(event, organizer=event.organizer)
_, _, content = c.render({
@@ -202,12 +202,12 @@ def test_csv_order_by_inherited_name_parts(event): # noqa
assert clean(content.decode()) == clean(""""Order code","Attendee name","Attendee name: Title",
"Attendee name: First name","Attendee name: Middle name","Attendee name: Family name","Product","Price",
"Checked in","Checked out","Automatically checked in","Secret","E-mail","Phone number","Company","Voucher code","Order date","Order time","Requires special
attention","Comment","Check-in text","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until",
attention","Comment","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until",
"Address","ZIP code","City","Country","State"
"BAR","Mr Albert J Zulu","Mr","Albert","J","Zulu","Ticket","23.00","","","No","hutjztuxhkbtwnesv2suqv26k6ttytyy",
"dummy@dummy.test","'+498912345678","BARCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","BARCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
"FOO","Mr Paul A Jones","Mr","Paul","A","Jones","Ticket","23.00","","","No","hutjztuxhkbtwnesv2suqv26k6ttytxx",
"dummy@dummy.test","'+498912345678","FOOCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","FOOCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
""")
c = CSVCheckinList(event, organizer=event.organizer)
_, _, content = c.render({
@@ -220,55 +220,10 @@ def test_csv_order_by_inherited_name_parts(event): # noqa
assert clean(content.decode()) == clean(""""Order code","Attendee name","Attendee name: Title",
"Attendee name: First name","Attendee name: Middle name","Attendee name: Family name","Product","Price",
"Checked in","Checked out","Automatically checked in","Secret","E-mail","Phone number","Company","Voucher code","Order date","Order time","Requires special
attention","Comment","Check-in text","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until",
attention","Comment","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until",
"Address","ZIP code","City","Country","State"
"FOO","Mr Paul A Jones","Mr","Paul","A","Jones","Ticket","23.00","","","No","hutjztuxhkbtwnesv2suqv26k6ttytxx",
"dummy@dummy.test","'+498912345678","FOOCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","FOOCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
"BAR","Mr Albert J Zulu","Mr","Albert","J","Zulu","Ticket","23.00","","","No","hutjztuxhkbtwnesv2suqv26k6ttytyy",
"dummy@dummy.test","'+498912345678","BARCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","","",""
""")
@pytest.mark.django_db
def test_csv_order_by_orderdatetime(event):
order1 = event.orders.first()
order1.checkin_text = 'meow'
order1.save()
order2 = Order.objects.create(
code='FOO2', event=event, email='dummy@dummy.test', phone="+498912345678",
status=Order.STATUS_PAID,
datetime=datetime.datetime(2019, 2, 22, 22, 0, 0, tzinfo=datetime.timezone.utc),
expires=now() + datetime.timedelta(days=10),
total=33, locale='en', checkin_text='beep',
sales_channel=event.organizer.sales_channels.get(identifier="web"),
)
item_ticket = Item.objects.create(event=event, name="Ticket2", default_price=23, admission=True, checkin_text='boop')
OrderPosition.objects.create(
order=order2,
item=item_ticket,
variation=None,
price=Decimal("23"),
attendee_name_parts={"title": "Mx", "given_name": "Alex", "middle_name": "F", "family_name": "Nord"},
secret='asdfasdfasdfasdfasdfasdfasfdasdf'
)
c = CSVCheckinList(event, organizer=event.organizer)
_, _, content = c.render({
'list': event.checkin_lists.first().pk,
'secrets': True,
'sort': 'order_datetime',
'_format': 'default',
'questions': []
})
assert clean(content.decode()) == clean(""""Order code","Attendee name","Attendee name: Title","Attendee name:
First name","Attendee name: Middle name","Attendee name: Family name","Product","Price","Checked in","Checked out","Automatically
checked in","Secret","E-mail","Phone number","Company","Voucher code","Order date","Order time","Requires special attention",
"Comment","Check-in text","Seat ID","Seat name","Seat zone","Seat row","Seat number","Blocked","Valid from","Valid until","Address","ZIP code",
"City","Country","State"
"FOO2","Mx Alex F Nord","Mx","Alex","F","Nord","Ticket2","23.00","","","No","asdfasdfasdfasdfasdfasdfasfdasdf",
"dummy@dummy.test","'+498912345678","","","2019-02-22","22:00:00","No","","beep\nboop","","","","","","","","","","","","",""
"FOO","Mr Peter A Jones","Mr","Peter","A","Jones","Ticket","23.00","","","No","hutjztuxhkbtwnesv2suqv26k6ttytxx",
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","meow","","","","","","","","","","","","",""
"FOO","Mrs Andrea J Zulu","Mrs","Andrea","J","Zulu","Ticket","13.00","","","No","ggsngqtnmhx74jswjngw3fk8pfwz2a7k",
"dummy@dummy.test","'+498912345678","","","2019-02-22","14:00:00","No","","meow","","","","","","","","","","","","",""
"dummy@dummy.test","'+498912345678","BARCORP","","2019-02-22","14:00:00","No","","","","","","","","","","","","","",""
""")