Compare commits

..

1 Commits

Author SHA1 Message Date
Raphael Michel
d9f0163329 Add test for no pending migrations 2024-07-01 11:27:13 +02:00
56 changed files with 1183 additions and 1390 deletions

View File

@@ -40,11 +40,6 @@ answers list of objects Answers to user
seat objects The assigned seat (or ``null``)
├ id integer Internal ID of the seat instance
├ name string Human-readable seat name
├ zone_name string Name of the zone the seat is in
├ row_name string Name/number of the row the seat is in
├ row_label string Additional label of the row (or ``null``)
├ seat_number string Number of the seat within the row
├ seat_label string Additional label of the seat (or ``null``)
└ seat_guid string Identifier of the seat within the seating plan
===================================== ========================== =======================================================

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

@@ -215,11 +215,6 @@ answers list of objects Answers to user
seat objects The assigned seat. Can be ``null``.
├ id integer Internal ID of the seat instance
├ name string Human-readable seat name
├ zone_name string Name of the zone the seat is in
├ row_name string Name/number of the row the seat is in
├ row_label string Additional label of the row (or ``null``)
├ seat_number string Number of the seat within the row
├ seat_label string Additional label of the seat (or ``null``)
└ seat_guid string Identifier of the seat within the seating plan
pdf_data object Data object required for ticket PDF generation. By default,
this field is missing. It will be added only if you add the
@@ -460,13 +455,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

View File

@@ -35,7 +35,7 @@ Frontend
--------
.. automodule:: pretix.presale.signals
:members: html_head, html_footer, footer_link, global_footer_link, front_page_top, front_page_bottom, front_page_bottom_widget, fee_calculation_for_cart, contact_form_fields, question_form_fields, contact_form_fields_overrides, question_form_fields_overrides, checkout_confirm_messages, checkout_confirm_page_content, checkout_all_optional, html_page_header, render_seating_plan, checkout_flow_steps, position_info, position_info_top, item_description, global_html_head, global_html_footer, global_html_page_header, seatingframe_html_head
:members: html_head, html_footer, footer_link, global_footer_link, front_page_top, front_page_bottom, front_page_bottom_widget, fee_calculation_for_cart, contact_form_fields, question_form_fields, contact_form_fields_overrides, question_form_fields_overrides, checkout_confirm_messages, checkout_confirm_page_content, checkout_all_optional, html_page_header, render_seating_plan, checkout_flow_steps, position_info, position_info_top, item_description, global_html_head, global_html_footer, global_html_page_header
.. automodule:: pretix.presale.signals

View File

@@ -17,7 +17,7 @@ The project pretix is split into several components. The main components are:
create and manage their events, items, orders and tickets.
**presale**
This is the ticket shop itself, containing all of the parts visible to the
This is the ticket-shop itself, containing all of the parts visible to the
end user. Also called "frontend" in parts of this documentation.
**api**

View File

@@ -75,7 +75,7 @@ dependencies = [
"paypal-checkout-serversdk==1.0.*",
"PyJWT==2.8.*",
"phonenumberslite==8.13.*",
"Pillow==10.4.*",
"Pillow==10.3.*",
"pretix-plugin-build",
"protobuf==5.27.*",
"psycopg2-binary",

View File

@@ -21,8 +21,8 @@
#
import json
from django.core.exceptions import ValidationError
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
class AsymmetricField(serializers.Field):

View File

@@ -281,17 +281,13 @@ class EventSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
from pretix.base.plugins import get_all_plugins
plugins_available = {
p.module: p for p in get_all_plugins(self.instance)
p.module for p in get_all_plugins(self.instance)
if not p.name.startswith('.') and getattr(p, 'visible', True)
}
settings_holder = self.instance if self.instance and self.instance.pk else self.context['organizer']
for plugin in value.get('plugins'):
if plugin not in plugins_available:
raise ValidationError(_('Unknown plugin: \'{name}\'.').format(name=plugin))
if getattr(plugins_available[plugin], 'restricted', False):
if plugin not in settings_holder.settings.allowed_restricted_plugins:
raise ValidationError(_('Restricted plugin: \'{name}\'.').format(name=plugin))
return value

View File

@@ -76,9 +76,7 @@ class InlineItemVariationSerializer(SalesChannelMigrationMixin, I18nAwareModelSe
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['require_membership_types'].queryset = lazy(lambda: self.context['event'].organizer.membership_types.all(), QuerySet)
self.fields['limit_sales_channels'].child_relation.queryset = (
self.context['event'].organizer.sales_channels.all() if 'event' in self.context else SalesChannel.objects.none()
)
self.fields['limit_sales_channels'].child_relation.queryset = lazy(lambda: self.context['event'].organizer.sales_channels.all(), QuerySet)
def validate_meta_data(self, value):
for key in value['meta_data'].keys():
@@ -117,15 +115,11 @@ class ItemVariationSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializ
def create(self, validated_data):
meta_data = validated_data.pop('meta_data', None)
require_membership_types = validated_data.pop('require_membership_types', [])
limit_sales_channels = validated_data.pop('limit_sales_channels', [])
variation = ItemVariation.objects.create(**validated_data)
if require_membership_types:
variation.require_membership_types.add(*require_membership_types)
if limit_sales_channels:
variation.limit_sales_channels.add(*limit_sales_channels)
# Meta data
if meta_data is not None:
for key, value in meta_data.items():
@@ -290,7 +284,6 @@ class ItemSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
self.fields['require_membership_types'].queryset = self.context['event'].organizer.membership_types.all()
self.fields['grant_membership_type'].queryset = self.context['event'].organizer.membership_types.all()
self.fields['limit_sales_channels'].child_relation.queryset = self.context['event'].organizer.sales_channels.all()
self.fields['variations'].child.fields['limit_sales_channels'].child_relation.queryset = self.context['event'].organizer.sales_channels.all()
def validate(self, data):
data = super().validate(data)
@@ -378,13 +371,10 @@ class ItemSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
for variation_data in variations_data:
require_membership_types = variation_data.pop('require_membership_types', [])
limit_sales_channels = variation_data.pop('limit_sales_channels', [])
var_meta_data = variation_data.pop('meta_data', {})
v = ItemVariation.objects.create(item=item, **variation_data)
if require_membership_types:
v.require_membership_types.add(*require_membership_types)
if limit_sales_channels:
v.limit_sales_channels.add(*limit_sales_channels)
if var_meta_data is not None:
for key, value in var_meta_data.items():

View File

@@ -165,7 +165,7 @@ class InlineSeatSerializer(I18nAwareModelSerializer):
class Meta:
model = Seat
fields = ('id', 'name', 'seat_guid', 'zone_name', 'row_name', 'row_label', 'seat_label', 'seat_number')
fields = ('id', 'name', 'seat_guid')
class AnswerSerializer(I18nAwareModelSerializer):
@@ -585,7 +585,7 @@ class CheckinListOrderPositionSerializer(OrderPositionSerializer):
self.fields['item'] = ItemSerializer(read_only=True, context=self.context)
if 'variation' in self.context['expand']:
self.fields['variation'] = InlineItemVariationSerializer(read_only=True, context=self.context)
self.fields['variation'] = InlineItemVariationSerializer(read_only=True)
if 'answers.question' in self.context['expand']:
self.fields['answers'].child.fields['question'] = QuestionSerializer(read_only=True)

View File

@@ -406,7 +406,7 @@ def _checkin_list_position_queryset(checkinlists, ignore_status=False, ignore_pr
'item__variations').select_related('item__tax_rule')
if expand and 'variation' in expand:
qs = qs.prefetch_related('variation', 'variation__meta_values')
qs = qs.prefetch_related('variation')
return qs

View File

@@ -41,7 +41,6 @@ from django_filters.rest_framework import DjangoFilterBackend, FilterSet
from django_scopes import scopes_disabled
from rest_framework import serializers, views, viewsets
from rest_framework.exceptions import PermissionDenied, ValidationError
from rest_framework.generics import get_object_or_404
from rest_framework.response import Response
from pretix.api.auth.permission import EventCRUDPermission
@@ -163,13 +162,7 @@ class EventViewSet(viewsets.ModelViewSet):
qs = filter_qs_by_attr(qs, self.request)
if 'with_availability_for' in self.request.GET:
qs = Event.annotated(
qs,
channel=get_object_or_404(
self.request.organizer.sales_channels,
identifier=self.request.GET.get('with_availability_for')
)
)
qs = Event.annotated(qs, channel=self.request.GET.get('with_availability_for'))
return qs.prefetch_related(
'organizer',
@@ -449,13 +442,7 @@ class SubEventViewSet(ConditionalListView, viewsets.ModelViewSet):
qs = filter_qs_by_attr(qs, self.request)
if 'with_availability_for' in self.request.GET:
qs = SubEvent.annotated(
qs,
channel=get_object_or_404(
self.request.organizer.sales_channels,
identifier=self.request.GET.get('with_availability_for')
)
)
qs = SubEvent.annotated(qs, channel=self.request.GET.get('with_availability_for'))
return qs.prefetch_related(
'event',

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

@@ -1,7 +1,6 @@
# Generated by Django 4.2.8 on 2024-07-01 09:26
from django.db import migrations, models
import pretix.base.models.orders

View File

@@ -304,13 +304,10 @@ class EventMixin:
return safe_string(json.dumps(eventdict))
@classmethod
def annotated(cls, qs, channel, voucher=None):
# Channel can currently be a SalesChannel or a str, since we need that compatibility, but a SalesChannel
# makes the query SIGNIFICANTLY faster
from pretix.base.models import Item, ItemVariation, Quota, SalesChannel
assert isinstance(channel, (SalesChannel, str))
def annotated(cls, qs, channel='web', voucher=None):
from pretix.base.models import Item, ItemVariation, Quota
assert isinstance(channel, str)
sq_active_item = Item.objects.using(settings.DATABASE_REPLICA).filter_available(channel=channel, voucher=voucher).filter(
Q(variations__isnull=True)
& Q(quotas__pk=OuterRef('pk'))
@@ -320,23 +317,18 @@ class EventMixin:
q_variation = (
Q(active=True)
& Q(Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=channel))
& Q(Q(available_from__isnull=True) | Q(available_from__lte=time_machine_now()))
& Q(Q(available_until__isnull=True) | Q(available_until__gte=time_machine_now()))
& Q(item__active=True)
& Q(Q(item__available_from__isnull=True) | Q(item__available_from__lte=time_machine_now()))
& Q(Q(item__available_until__isnull=True) | Q(item__available_until__gte=time_machine_now()))
& Q(Q(item__category__isnull=True) | Q(item__category__is_addon=False))
& Q(Q(item__all_sales_channels=True) | Q(item__limit_sales_channels__identifier=channel))
& Q(item__require_bundling=False)
& Q(quotas__pk=OuterRef('pk'))
)
if isinstance(channel, str):
q_variation &= Q(Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=channel))
q_variation &= Q(Q(item__all_sales_channels=True) | Q(item__limit_sales_channels__identifier=channel))
else:
q_variation &= Q(Q(all_sales_channels=True) | Q(limit_sales_channels=channel))
q_variation &= Q(Q(item__all_sales_channels=True) | Q(item__limit_sales_channels=channel))
if voucher:
if voucher.variation_id:
q_variation &= Q(pk=voucher.variation_id)
@@ -970,7 +962,7 @@ class Event(EventMixin, LoggedModel):
):
c_items = list(d.condition_limit_products.all())
b_items = list(d.benefit_limit_products.all())
limit_sales_channels = list(d.limit_sales_channels.all())
limit_sales_channels = list(v.limit_sales_channels.all())
d.pk = None
d.event = self
d._prefetched_objects_cache = {}
@@ -1544,11 +1536,8 @@ class SubEvent(EventMixin, LoggedModel):
return qs_annotated
@classmethod
def annotated(cls, qs, channel, voucher=None):
def annotated(cls, qs, channel='web', voucher=None):
from .items import SubEventItem, SubEventItemVariation
from .organizer import SalesChannel
assert isinstance(channel, (str, SalesChannel))
qs = super().annotated(qs, channel, voucher=voucher)
qs = qs.annotate(

View File

@@ -271,24 +271,16 @@ class SubEventItemVariation(models.Model):
def filter_available(qs, channel='web', voucher=None, allow_addons=False):
# Channel can currently be a SalesChannel or a str, since we need that compatibility, but a SalesChannel
# makes the query SIGNIFICANTLY faster
from .organizer import SalesChannel
assert isinstance(channel, (SalesChannel, str))
assert isinstance(channel, str)
q = (
# IMPORTANT: If this is updated, also update the ItemVariation query
# in models/event.py: EventMixin.annotated()
Q(active=True)
& Q(Q(available_from__isnull=True) | Q(available_from__lte=time_machine_now()) | Q(available_from_mode='info'))
& Q(Q(available_until__isnull=True) | Q(available_until__gte=time_machine_now()) | Q(available_until_mode='info'))
& Q(Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=channel))
& Q(require_bundling=False)
)
if isinstance(channel, str):
q &= Q(Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=channel))
else:
q &= Q(Q(all_sales_channels=True) | Q(limit_sales_channels=channel))
if not allow_addons:
q &= Q(Q(category__isnull=True) | Q(category__is_addon=False))

View File

@@ -2042,7 +2042,7 @@ class OrderChangeManager:
# This also prevents accidental removal through the UI because a hidden product will no longer
# be part of the input.
(a.variation and a.variation.unavailability_reason(has_voucher=True, subevent=a.subevent))
or (a.variation and not a.variation.all_sales_channels and not a.variation.limit_sales_channels.contains(self.order.sales_channel))
or (a.variation and self.order.sales_channel not in a.variation.sales_channels)
or a.item.unavailability_reason(has_voucher=True, subevent=a.subevent)
or (
not item.all_sales_channels and

View File

@@ -105,7 +105,6 @@ def preview(event: int, provider: str):
order = event.orders.create(status=Order.STATUS_PENDING, datetime=now(),
email='sample@pretix.eu',
locale=event.settings.locale,
sales_channel=event.organizer.sales_channels.get(identifier="web"),
expires=now(), code="PREVIEW1234", total=119)
scheme = PERSON_NAME_SCHEMES[event.settings.name_scheme]

View File

@@ -136,15 +136,9 @@
{% if i.tax_rule and i.default_price %}
<br/>
<small class="text-muted">
{% if not i.tax_rule.price_includes_tax %}
{% blocktrans trimmed with rate=i.tax_rule.rate|floatformat:-2 taxname=i.tax_rule.name %}
<strong>plus</strong> {{ rate }}% {{ taxname }}
{% endblocktrans %}
{% else %}
{% blocktrans trimmed with rate=i.tax_rule.rate|floatformat:-2 taxname=i.tax_rule.name|default:s_taxes %}
incl. {{ rate }}% {{ taxname }}
{% endblocktrans %}
{% endif %}
{% blocktrans trimmed with rate=i.tax_rule.rate|floatformat:-2 taxname=i.tax_rule.name|default:s_taxes %}
incl. {{ rate }}% {{ taxname }}
{% endblocktrans %}
</small>
{% endif %}
</td>

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" }}

View File

@@ -400,7 +400,7 @@ class EventPlugins(EventSettingsViewMixin, EventPermissionRequiredMixin, Templat
if key.startswith("plugin:"):
module = key.split(":")[1]
if value == "enable" and module in plugins_available:
if getattr(plugins_available[module], 'restricted', False):
if getattr(plugins_available[module].app, 'restricted', False):
if module not in request.event.settings.allowed_restricted_plugins:
continue

View File

@@ -103,7 +103,7 @@ class ItemList(ListView):
def get_queryset(self):
return Item.objects.filter(
event=self.request.event
).select_related("tax_rule").annotate(
).annotate(
var_count=Count('variations')
).prefetch_related("category", "limit_sales_channels").order_by(
F('category__position').asc(nulls_first=True),

View File

@@ -3161,7 +3161,7 @@ class ChannelUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMix
}
def form_valid(self, form):
if form.has_changed():
if form.has_changed() or self.formset.has_changed():
self.object.log_action('pretix.saleschannel.changed', user=self.request.user, data={
k: getattr(self.object, k)
for k in form.changed_data

File diff suppressed because it is too large Load Diff

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-02 19:00+0000\n"
"PO-Revision-Date: 2024-06-30 21:07+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix/de/"
">\n"
@@ -3536,7 +3536,7 @@ msgstr "Sie können keinen Ticketcode verwenden, der bereits existiert."
#: pretix/base/modelimport_orders.py:490
msgid "Please enter a valid language code."
msgstr "Bitte einen gültigen Sprachcode eingeben."
msgstr "Bitte geben Sie einen gültigen Sprachcode ein."
#: pretix/base/modelimport_orders.py:558 pretix/base/modelimport_orders.py:560
msgid "Please enter a valid sales channel."
@@ -9608,7 +9608,7 @@ msgstr ""
#: pretix/base/settings.py:1346
msgid "Ask search engines not to index the ticket shop"
msgstr "Der Ticketshop soll von Suchmaschinen nicht indiziert werden"
msgstr "Der Ticket-Shop soll von Suchmaschinen nicht indiziert werden"
#: pretix/base/settings.py:1355
msgid "Show variations of a product expanded by default"
@@ -10874,7 +10874,7 @@ msgstr ""
"Sie haben sich auf die Warteliste für {event} \n"
"für das Produkt {product} eingetragen.\n"
"\n"
"Wir haben nun ein Ticket für Sie! Sie können es in unserem Ticketshop "
"Wir haben nun ein Ticket für Sie! Sie können es in unserem Ticket-Shop "
"erwerben,\n"
"indem Sie in den nächsten {hours} Stunden den folgenden Gutscheincode "
"eingeben:\n"
@@ -11971,7 +11971,7 @@ msgid ""
"If you just configured this as a domain for your ticket shop, you now need "
"to set this up as a \"custom domain\" in your organizer account."
msgstr ""
"Wenn Sie gerade diese Domain für Ihren Ticketshop eingerichtet haben, "
"Wenn Sie gerade diese Domain für Ihren Ticket-Shop eingerichtet haben, "
"müssen Sie diese nun in Ihrem Veranstalterkonto als \"eigene Domain\" "
"eintragen."
@@ -18403,7 +18403,7 @@ msgid ""
"You can instead take your shop offline. This will hide it from everyone "
"except from the organizer teams you configured to have access to the event."
msgstr ""
"Sie können stattdessen den Ticketshop abschalten. Dies wird die "
"Sie können stattdessen den Ticket-Shop abschalten. Dies wird die "
"Veranstaltung vor allen außer den zugewiesenen Veranstalter-Teams verstecken."
#: pretix/control/templates/pretixcontrol/event/delete.html:66
@@ -18629,14 +18629,14 @@ msgid ""
"Your ticket shop is currently not live. It is thus only visible to you and "
"your team, not to any visitors."
msgstr ""
"Ihr Ticketshop ist zur Zeit offline und daher nur für Sie und Ihr Team "
"Ihr Ticket-Shop ist zur Zeit offline und daher nur für Sie und Ihr Team "
"verfügbar und nicht für Besucher."
#: pretix/control/templates/pretixcontrol/event/live.html:41
msgid ""
"To publish your ticket shop, you first need to resolve the following issues:"
msgstr ""
"Um Ihren Ticketshop zu veröffentlichen, müssen Sie zuerst die folgenden "
"Um Ihren Ticket-Shop zu veröffentlichen, müssen Sie zuerst die folgenden "
"Probleme beheben:"
#: pretix/control/templates/pretixcontrol/event/live.html:51
@@ -18646,7 +18646,7 @@ msgstr "Shop veröffentlichen"
#: pretix/control/templates/pretixcontrol/event/live.html:59
msgid "If you want to, you can publish your ticket shop now."
msgstr "Sie können Ihren Ticketshop jederzeit veröffentlichen."
msgstr "Sie können Ihren Ticket-Shop jederzeit veröffentlichen."
#: pretix/control/templates/pretixcontrol/event/live.html:83
msgid ""
@@ -19488,7 +19488,7 @@ msgid ""
"website. This way, your visitors can buy their ticket right away without "
"leaving your website."
msgstr ""
"Das pretix-Widget ist ein Weg, den Ticketshop in Ihre Event-Website "
"Das pretix-Widget ist ein Weg, den Ticket-Shop in Ihre Event-Website "
"einzubetten. Auf diese Weise können Besucher Ihrer Website ein Ticket "
"erwerben, ohne die Website verlassen zu müssen."
@@ -19515,7 +19515,7 @@ msgid ""
"JavaScript is disabled in your browser. To access our ticket shop without "
"JavaScript, please &lt;a %(a_attr)s&gt;click here&lt;/a&gt;."
msgstr ""
"JavaScript ist in Ihrem Browser deaktiviert. Um unseren Ticketshop ohne "
"JavaScript ist in Ihrem Browser deaktiviert. Um unseren Ticket-Shop ohne "
"JavaScript aufzurufen, klicken Sie bitte &lt;a %(a_attr)s&gt;hier&lt;/a&gt;."
#: pretix/control/templates/pretixcontrol/event/widget.html:64
@@ -19580,7 +19580,7 @@ msgid ""
"less than 10 characters that can be easily remembered, but you can also "
"choose to use a random value."
msgstr ""
"Dies ist die Adresse, unter der Ihr Ticketshop verfügbar sein wird. Sie "
"Dies ist die Adresse, unter der Ihr Ticket-Shop verfügbar sein wird. Sie "
"sollte kurz sein und darf nur Kleinbuchstaben, Zahlen, Punkte und "
"Bindestriche enthalten. Sie muss unter Ihren Veranstaltungen einmalig sein. "
"Wir empfehlen eine Abkürzung oder ein Datum mit weniger als 10 Zeichen, das "
@@ -22464,7 +22464,7 @@ msgstr "Veröffentlichen Sie Ihren Shop"
#: pretix/control/templates/pretixcontrol/orders/index.html:27
msgid "Go to the ticket shop"
msgstr "Zum Ticketshop"
msgstr "Zum Ticket-Shop"
#: pretix/control/templates/pretixcontrol/orders/index.html:35
msgid "Search query:"
@@ -23102,7 +23102,7 @@ msgid ""
"usage, the legal details in your specific jurisdiction, or the agreements "
"you have with third parties such as payment or tracking providers."
msgstr ""
"Trotzdem bleibt es Ihre Verantwortung, dass Ihr Ticketshop allen "
"Trotzdem bleibt es Ihre Verantwortung, dass Ihr Ticket-Shop allen "
"anwendbaren Gesetzen entspricht. Wir versuchen mit diesen Einstellungen zu "
"helfen, aber können keine Haftung übernehmen, da wir unter anderem die "
"genaue Konfiguration Ihres Ticketshops, die rechtlichen Details der "
@@ -25624,7 +25624,7 @@ msgstr "{quota} übrig"
#: pretix/control/views/dashboards.py:269
msgid "Your ticket shop is"
msgstr "Ihr Ticketshop ist"
msgstr "Ihr Ticket-Shop ist"
#: pretix/control/views/dashboards.py:269
msgid "Click here to change"
@@ -31490,7 +31490,7 @@ msgstr "Warnung"
#: pretix/presale/templates/pretixpresale/event/base.html:127
#: pretix/presale/templates/pretixpresale/event/base.html:201
msgid "This ticket shop is currently in test mode."
msgstr "Dieser Ticketshop ist momentan im Testmodus."
msgstr "Dieser Ticket-Shop ist momentan im Testmodus."
#: pretix/presale/templates/pretixpresale/event/base.html:130
#: pretix/presale/templates/pretixpresale/event/base.html:204
@@ -32857,7 +32857,7 @@ msgstr "Shop ausgeschaltet"
#: pretix/presale/templates/pretixpresale/event/offline.html:9
msgid "This ticket shop is currently turned off."
msgstr "Dieser Ticketshop ist im Moment nicht aktiv."
msgstr "Dieser Ticket-Shop ist im Moment nicht aktiv."
#: pretix/presale/templates/pretixpresale/event/offline.html:10
msgid "It is only accessible to authenticated team members."
@@ -33684,7 +33684,7 @@ msgid ""
"open source ticket sales software</a>."
msgstr ""
"Dies ist eine selbst-gehostete Installation von <a %(a_attr)s>pretix, dem "
"freundlichen Open Source Ticketshop</a>."
"freundlichen Open Source Ticket-Shop</a>."
#: pretix/presale/templates/pretixpresale/index.html:15
msgid ""
@@ -34215,7 +34215,7 @@ msgstr ""
#: pretix/presale/views/widget.py:372
msgid "This ticket shop is currently disabled."
msgstr "Dieser Ticketshop ist im Moment nicht verfügbar."
msgstr "Dieser Ticket-Shop ist im Moment nicht verfügbar."
#: pretix/presale/views/widget.py:386
msgid "The selected date does not exist in this event series."
@@ -34336,7 +34336,7 @@ msgstr "Kosovo"
#~ msgstr "Sie können nicht fortfahren."
#~ msgid "The selected ticket shop is currently not available."
#~ msgstr "Der ausgewählte Ticketshop ist im Moment nicht verfügbar."
#~ msgstr "Der ausgewählte Ticket-Shop ist im Moment nicht verfügbar."
#~ msgid "Needs to be enabled in your Stripe account first."
#~ msgstr "Muss erst im Stripe-Account aktiviert werden."

View File

@@ -871,12 +871,12 @@ msgstr "minimale Bestellmenge: %s"
#: pretix/static/pretixpresale/js/widget/widget.js:39
msgctxt "widget"
msgid "Close ticket shop"
msgstr "Ticketshop schließen"
msgstr "Ticket-Shop schließen"
#: pretix/static/pretixpresale/js/widget/widget.js:40
msgctxt "widget"
msgid "The ticket shop could not be loaded."
msgstr "Der Ticketshop konnte nicht geladen werden."
msgstr "Der Ticket-Shop konnte nicht geladen werden."
#: pretix/static/pretixpresale/js/widget/widget.js:41
msgctxt "widget"
@@ -890,7 +890,7 @@ msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:43
msgctxt "widget"
msgid "Open ticket shop"
msgstr "Ticketshop öffnen"
msgstr "Ticket-Shop öffnen"
#: pretix/static/pretixpresale/js/widget/widget.js:44
msgctxt "widget"

View File

@@ -8,7 +8,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-02 19:00+0000\n"
"PO-Revision-Date: 2024-06-30 21:07+0000\n"
"Last-Translator: Raphael Michel <michel@rami.io>\n"
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
"pretix/pretix/de_Informal/>\n"
@@ -3536,7 +3536,7 @@ msgstr "Sie können keinen Ticketcode verwenden, der bereits existiert."
#: pretix/base/modelimport_orders.py:490
msgid "Please enter a valid language code."
msgstr "Bitte einen gültigen Sprachcode eingeben."
msgstr "Bitte gib einen gültigen Sprachcode ein."
#: pretix/base/modelimport_orders.py:558 pretix/base/modelimport_orders.py:560
msgid "Please enter a valid sales channel."
@@ -9594,7 +9594,7 @@ msgstr ""
#: pretix/base/settings.py:1346
msgid "Ask search engines not to index the ticket shop"
msgstr "Der Ticketshop soll von Suchmaschinen nicht indiziert werden"
msgstr "Der Ticket-Shop soll von Suchmaschinen nicht indiziert werden"
#: pretix/base/settings.py:1355
msgid "Show variations of a product expanded by default"
@@ -10856,7 +10856,7 @@ msgstr ""
"du hast dich auf die Warteliste für {event} \n"
"für das Produkt {product} eingetragen.\n"
"\n"
"Wir haben nun ein Ticket für dich! Du kannst es in unserem Ticketshop "
"Wir haben nun ein Ticket für dich! Du kannst es in unserem Ticket-Shop "
"erwerben,\n"
"indem du in den nächsten {hours} Stunden den folgenden Gutscheincode "
"eingibst:\n"
@@ -11950,7 +11950,7 @@ msgid ""
"If you just configured this as a domain for your ticket shop, you now need "
"to set this up as a \"custom domain\" in your organizer account."
msgstr ""
"Wenn du gerade diese Domain für deinen Ticketshop eingerichtet hast, musst "
"Wenn du gerade diese Domain für deinen Ticket-Shop eingerichtet hast, musst "
"du diese nun in deinem Veranstalterkonto als \"eigene Domain\" eintragen."
#: pretix/base/templates/403.html:4 pretix/base/templates/403.html:8
@@ -18371,7 +18371,7 @@ msgid ""
"You can instead take your shop offline. This will hide it from everyone "
"except from the organizer teams you configured to have access to the event."
msgstr ""
"Du kannst stattdessen den Ticketshop abschalten. Dies wird die "
"Du kannst stattdessen den Ticket-Shop abschalten. Dies wird die "
"Veranstaltung vor allen außer den zugewiesenen Veranstalter-Teams verstecken."
#: pretix/control/templates/pretixcontrol/event/delete.html:66
@@ -18596,14 +18596,14 @@ msgid ""
"Your ticket shop is currently not live. It is thus only visible to you and "
"your team, not to any visitors."
msgstr ""
"Dein Ticketshop ist zur Zeit offline und daher nur für dich und dein Team "
"Dein Ticket-Shop ist zur Zeit offline und daher nur für dich und dein Team "
"verfügbar und nicht für Besucher."
#: pretix/control/templates/pretixcontrol/event/live.html:41
msgid ""
"To publish your ticket shop, you first need to resolve the following issues:"
msgstr ""
"Um deinen Ticketshop zu veröffentlichen, musst du zuerst die folgenden "
"Um deinen Ticket-Shop zu veröffentlichen, musst du zuerst die folgenden "
"Probleme beheben:"
#: pretix/control/templates/pretixcontrol/event/live.html:51
@@ -18613,7 +18613,7 @@ msgstr "Shop veröffentlichen"
#: pretix/control/templates/pretixcontrol/event/live.html:59
msgid "If you want to, you can publish your ticket shop now."
msgstr "Du kannst deinen Ticketshop jederzeit veröffentlichen."
msgstr "Du kannst deinen Ticket-Shop jederzeit veröffentlichen."
#: pretix/control/templates/pretixcontrol/event/live.html:83
msgid ""
@@ -19454,7 +19454,7 @@ msgid ""
"website. This way, your visitors can buy their ticket right away without "
"leaving your website."
msgstr ""
"Das pretix-Widget ist ein Weg, den Ticketshop in deine Event-Website "
"Das pretix-Widget ist ein Weg, den Ticket-Shop in deine Event-Website "
"einzubetten. Auf diese Weise können Besucher deiner Website ein Ticket "
"erwerben, ohne die Website verlassen zu müssen."
@@ -19481,7 +19481,7 @@ msgid ""
"JavaScript is disabled in your browser. To access our ticket shop without "
"JavaScript, please &lt;a %(a_attr)s&gt;click here&lt;/a&gt;."
msgstr ""
"JavaScript ist in deinem Browser deaktiviert. Um unseren Ticketshop ohne "
"JavaScript ist in deinem Browser deaktiviert. Um unseren Ticket-Shop ohne "
"JavaScript aufzurufen, klicke bitte &lt;a %(a_attr)s&gt;hier&lt;/a&gt;."
#: pretix/control/templates/pretixcontrol/event/widget.html:64
@@ -19546,7 +19546,7 @@ msgid ""
"less than 10 characters that can be easily remembered, but you can also "
"choose to use a random value."
msgstr ""
"Dies ist die Adresse, unter der dein Ticketshop verfügbar sein wird. Sie "
"Dies ist die Adresse, unter der dein Ticket-Shop verfügbar sein wird. Sie "
"sollte kurz sein und darf nur Kleinbuchstaben, Zahlen, Punkte und "
"Bindestriche enthalten. Sie muss unter deinen Veranstaltungen einmalig sein. "
"Wir empfehlen eine Abkürzung oder ein Datum mit unter 10 Zeichen, das man "
@@ -22425,7 +22425,7 @@ msgstr "Veröffentliche deinen Shop"
#: pretix/control/templates/pretixcontrol/orders/index.html:27
msgid "Go to the ticket shop"
msgstr "Zum Ticketshop"
msgstr "Zum Ticket-Shop"
#: pretix/control/templates/pretixcontrol/orders/index.html:35
msgid "Search query:"
@@ -23063,7 +23063,7 @@ msgid ""
"usage, the legal details in your specific jurisdiction, or the agreements "
"you have with third parties such as payment or tracking providers."
msgstr ""
"Trotzdem bleibt es deine Verantwortung, dass dein Ticketshop allen "
"Trotzdem bleibt es deine Verantwortung, dass dein Ticket-Shop allen "
"anwendbaren Gesetzen entspricht. Wir versuchen mit diesen Einstellungen zu "
"helfen, aber können keine Haftung übernehmen, da wir unter anderem die "
"genaue Konfiguration deines Ticketshops, die rechtlichen Details der "
@@ -25579,7 +25579,7 @@ msgstr "{quota} übrig"
#: pretix/control/views/dashboards.py:269
msgid "Your ticket shop is"
msgstr "Dein Ticketshop ist"
msgstr "Dein Ticket-Shop ist"
#: pretix/control/views/dashboards.py:269
msgid "Click here to change"
@@ -31432,7 +31432,7 @@ msgstr "Warnung"
#: pretix/presale/templates/pretixpresale/event/base.html:127
#: pretix/presale/templates/pretixpresale/event/base.html:201
msgid "This ticket shop is currently in test mode."
msgstr "Dieser Ticketshop ist momentan im Testmodus."
msgstr "Dieser Ticket-Shop ist momentan im Testmodus."
#: pretix/presale/templates/pretixpresale/event/base.html:130
#: pretix/presale/templates/pretixpresale/event/base.html:204
@@ -32794,7 +32794,7 @@ msgstr "Shop ausgeschaltet"
#: pretix/presale/templates/pretixpresale/event/offline.html:9
msgid "This ticket shop is currently turned off."
msgstr "Dieser Ticketshop ist im Moment nicht aktiv."
msgstr "Dieser Ticket-Shop ist im Moment nicht aktiv."
#: pretix/presale/templates/pretixpresale/event/offline.html:10
msgid "It is only accessible to authenticated team members."
@@ -33622,7 +33622,7 @@ msgid ""
"open source ticket sales software</a>."
msgstr ""
"Dies ist eine selbst-gehostete Installation von <a %(a_attr)s>pretix, dem "
"freundlichen Open Source Ticketshop</a>."
"freundlichen Open Source Ticket-Shop</a>."
#: pretix/presale/templates/pretixpresale/index.html:15
msgid ""
@@ -34150,7 +34150,7 @@ msgstr ""
#: pretix/presale/views/widget.py:372
msgid "This ticket shop is currently disabled."
msgstr "Dieser Ticketshop ist im Moment nicht verfügbar."
msgstr "Dieser Ticket-Shop ist im Moment nicht verfügbar."
#: pretix/presale/views/widget.py:386
msgid "The selected date does not exist in this event series."
@@ -34271,7 +34271,7 @@ msgstr "Kosovo"
#~ msgstr "Du kannst daher nicht fortfahren."
#~ msgid "The selected ticket shop is currently not available."
#~ msgstr "Der ausgewählte Ticketshop ist im Moment nicht verfügbar."
#~ msgstr "Der ausgewählte Ticket-Shop ist im Moment nicht verfügbar."
#~ msgid "Needs to be enabled in your Stripe account first."
#~ msgstr "Muss erst im Stripe-Account aktiviert werden."

View File

@@ -870,12 +870,12 @@ msgstr "minimale Bestellmenge: %s"
#: pretix/static/pretixpresale/js/widget/widget.js:39
msgctxt "widget"
msgid "Close ticket shop"
msgstr "Ticketshop schließen"
msgstr "Ticket-Shop schließen"
#: pretix/static/pretixpresale/js/widget/widget.js:40
msgctxt "widget"
msgid "The ticket shop could not be loaded."
msgstr "Der Ticketshop konnte nicht geladen werden."
msgstr "Der Ticket-Shop konnte nicht geladen werden."
#: pretix/static/pretixpresale/js/widget/widget.js:41
msgctxt "widget"
@@ -889,7 +889,7 @@ msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:43
msgctxt "widget"
msgid "Open ticket shop"
msgstr "Ticketshop öffnen"
msgstr "Ticket-Shop öffnen"
#: pretix/static/pretixpresale/js/widget/widget.js:44
msgctxt "widget"

View File

@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-30 18:55+0000\n"
"PO-Revision-Date: 2024-07-01 16:00+0000\n"
"PO-Revision-Date: 2024-06-27 17:00+0000\n"
"Last-Translator: Anarion Dunedain <anarion80@gmail.com>\n"
"Language-Team: Polish <https://translate.pretix.eu/projects/pretix/pretix/pl/"
">\n"
@@ -122,7 +122,7 @@ msgstr "Słowacki"
#: pretix/_base_settings.py:103
msgid "Swedish"
msgstr "Szwedzki"
msgstr ""
#: pretix/_base_settings.py:104
msgid "Spanish"
@@ -640,15 +640,13 @@ msgstr "Sklep online"
#: pretix/base/channels.py:174
msgid "API"
msgstr "API"
msgstr ""
#: pretix/base/channels.py:175
msgid ""
"API sales channels come with no built-in functionality, but may be used for "
"custom integrations."
msgstr ""
"Kanały sprzedaży API nie mają wbudowanych funkcji, ale mogą być używane do "
"niestandardowych integracji."
#: pretix/base/context.py:45
#, python-brace-format
@@ -4043,8 +4041,10 @@ msgid "Position"
msgstr "Pozycja"
#: pretix/base/models/discount.py:68
#, fuzzy
#| msgid "Sales channels"
msgid "All supported sales channels"
msgstr "Wszystkie obsługiwane kanały sprzedaży"
msgstr "Kanały sprzedaży"
#: pretix/base/models/discount.py:89
msgid "Event series handling"
@@ -4257,8 +4257,10 @@ msgid "Seating plan"
msgstr "Plan miejsc"
#: pretix/base/models/event.py:639 pretix/base/models/items.py:618
#, fuzzy
#| msgid "Sales channels"
msgid "Sell on all sales channels"
msgstr "Sprzedawaj we wszystkich kanałach sprzedaży"
msgstr "Kanały sprzedaży"
#: pretix/base/models/event.py:644 pretix/base/models/items.py:623
#: pretix/base/models/items.py:1170 pretix/base/payment.py:417
@@ -4568,6 +4570,9 @@ msgid "{category} (Add-On products)"
msgstr "{category} (Produkty dodatkowe)"
#: pretix/base/models/items.py:128
#, fuzzy
#| msgctxt "checkoutflow"
#| msgid "Add-on products"
msgid "Add-On products"
msgstr "Produkty dodatkowe"
@@ -4854,8 +4859,11 @@ msgstr ""
"przeceny. Ustawienie kosmetyczne bez wzgędlu na faktyczną cenę sprzedaży."
#: pretix/base/models/items.py:624
#, fuzzy
#| msgid "Only sell tickets for this event on the following sales channels."
msgid "Only sell tickets for this product on the selected sales channels."
msgstr "Sprzedaj bilety na ten produkt tylko w wybranych kanałach sprzedaży."
msgstr ""
"Sprzedaj bilety tylko na to wydarzenie na następujących kanałach sprzedaży."
#: pretix/base/models/items.py:629
msgid ""
@@ -5106,8 +5114,6 @@ msgstr "Ten wariant nie będzie sprzedawany po podanej dacie."
#: pretix/base/models/items.py:1165
msgid "Sell on all sales channels the product is sold on"
msgstr ""
"Sprzedaj we wszystkich kanałach sprzedaży, w których sprzedawany jest ten "
"produkt."
#: pretix/base/models/items.py:1171
msgid ""
@@ -6047,6 +6053,9 @@ msgstr "Zaproszenie do zespołu '{team}' dla '{email}'"
#: pretix/base/models/organizer.py:538
#: pretix/control/templates/pretixcontrol/organizers/channels.html:23
#, fuzzy
#| msgctxt "reusable_medium"
#| msgid "Identifier"
msgid "Identifier"
msgstr "Identyfikator"
@@ -12041,8 +12050,6 @@ msgid ""
"This sales channel cannot be used properly since the respective plugin is "
"not active for this event."
msgstr ""
"Ten kanał sprzedaży nie może być używany prawidłowo, ponieważ odpowiednia "
"wtyczka nie jest aktywna dla tego wydarzenia."
#: pretix/base/templates/pretixbase/forms/widgets/portrait_image.html:10
msgid "Upload photo"
@@ -14054,8 +14061,6 @@ msgid ""
"You have selected dynamic validity but have not entered a time period. This "
"would render the tickets unusable."
msgstr ""
"Wybrałeś dynamiczną ważność, ale nie wprowadziłeś okresu czasu. Spowoduje "
"to, że bilety będą bezużyteczne."
#: pretix/control/forms/item.py:858
#, python-format
@@ -14802,8 +14807,10 @@ msgid "The selected organizer has already been invited."
msgstr "Wybrany organizator został już zaproszony."
#: pretix/control/forms/organizer.py:1128
#, fuzzy
#| msgid "A voucher with this code already exists."
msgid "A sales channel with the same identifier already exists."
msgstr "Kanał sprzedaży o tym samym identyfikatorze już istnieje."
msgstr "Voucher o tym kodzie już istnieje."
#: pretix/control/forms/renderers.py:56
#: pretix/control/templates/pretixcontrol/items/question_edit.html:139
@@ -15416,16 +15423,23 @@ msgid "The membership type has been deleted."
msgstr "Typ członkostwa został usunięty."
#: pretix/control/logdisplay.py:360 pretix/control/views/organizer.py:3119
#, fuzzy
#| msgctxt "subevent"
#| msgid "The new date has been created."
msgid "The sales channel has been created."
msgstr "Kanał sprzedaży został utworzony."
msgstr "Nowa data została utworzona."
#: pretix/control/logdisplay.py:361
#, fuzzy
#| msgid "The device has been changed."
msgid "The sales channel has been changed."
msgstr "Kanał sprzedaży został zmieniony."
msgstr "Urządzenie zostało zmienione."
#: pretix/control/logdisplay.py:362
#, fuzzy
#| msgid "The selected list has been deleted."
msgid "The sales channel has been deleted."
msgstr "Kanał sprzedaży został usunięty."
msgstr "Wybrana lista została usunięta."
#: pretix/control/logdisplay.py:363
msgid "The account has been created."
@@ -20176,14 +20190,14 @@ msgstr "Utwórz nową kategorię"
#: pretix/control/templates/pretixcontrol/items/index.html:146
#: pretix/control/templates/pretixcontrol/organizers/properties.html:54
msgid "Move up"
msgstr "Przesuń w górę"
msgstr ""
#: pretix/control/templates/pretixcontrol/items/categories.html:45
#: pretix/control/templates/pretixcontrol/items/discounts.html:142
#: pretix/control/templates/pretixcontrol/items/index.html:147
#: pretix/control/templates/pretixcontrol/organizers/properties.html:55
msgid "Move down"
msgstr "Przesuń w dół"
msgstr ""
#: pretix/control/templates/pretixcontrol/items/categories.html:46
#: pretix/control/templates/pretixcontrol/items/discounts.html:145
@@ -20193,8 +20207,6 @@ msgid ""
"Click and drag this button to reorder. Double click to show buttons for "
"reordering."
msgstr ""
"Kliknij i przeciągnij ten przycisk, aby zmienić kolejność. Kliknij "
"dwukrotnie, aby wyświetlić przyciski do zmiany kolejności."
#: pretix/control/templates/pretixcontrol/items/category.html:27
msgid "Category history"
@@ -20338,12 +20350,15 @@ msgstr ""
"produktu"
#: pretix/control/templates/pretixcontrol/items/discounts.html:111
#, fuzzy
#| msgctxt "discount"
#| msgid "Condition"
msgid "Condition:"
msgstr "Stan:"
msgstr "Stan"
#: pretix/control/templates/pretixcontrol/items/discounts.html:126
msgid "Applies to:"
msgstr "Dotyczy:"
msgstr ""
#: pretix/control/templates/pretixcontrol/items/fragment_quota_availability.html:3
msgid "Closed"
@@ -20383,6 +20398,12 @@ msgid "taxes"
msgstr "podatki"
#: pretix/control/templates/pretixcontrol/items/index.html:10
#, fuzzy
#| msgid ""
#| "Below, you find a list of all available products. You can click on a "
#| "product name to inspect and change product details. You can also use the "
#| "buttons on the right to change the order of products within a give "
#| "category."
msgid ""
"Below, you find a list of all available products. You can click on a product "
"name to inspect and change product details. You can also use the buttons on "
@@ -20391,8 +20412,8 @@ msgid ""
msgstr ""
"Poniżej znajduje się lista wszystkich dostępnych produktów. Możesz kliknąć "
"nazwę produktu, aby sprawdzić i zmienić jego szczegóły. Możesz także użyć "
"przycisków po prawej stronie, aby zmienić kolejność produktów lub przenieść "
"produkty do innej kategorii."
"przycisków po prawej stronie, aby zmienić kolejność produktów w danej "
"kategorii."
#: pretix/control/templates/pretixcontrol/items/index.html:19
msgid "You haven't created any products yet."
@@ -22458,34 +22479,48 @@ msgstr "Wyszukiwanie zamówień"
#: pretix/control/templates/pretixcontrol/organizers/channel_add.html:6
#: pretix/control/templates/pretixcontrol/organizers/channel_add_choice.html:6
#, fuzzy
#| msgid "Sales channel"
msgid "Add sales channel"
msgstr "Dodaj kanał sprzedaży"
msgstr "Kanał sprzedaży"
#: pretix/control/templates/pretixcontrol/organizers/channel_add.html:13
#: pretix/control/templates/pretixcontrol/organizers/channel_edit.html:13
#: pretix/control/templates/pretixcontrol/organizers/channels.html:24
#, fuzzy
#| msgid "Scan type"
msgid "Channel type"
msgstr "Typ kanału"
msgstr "Typ skanowania"
#: pretix/control/templates/pretixcontrol/organizers/channel_delete.html:5
#, fuzzy
#| msgid "Sales channel"
msgid "Delete sales channel:"
msgstr "Usuń kanał sprzedaży:"
msgstr "Kanał sprzedaży"
#: pretix/control/templates/pretixcontrol/organizers/channel_delete.html:10
#, fuzzy
#| msgid "Are you sure you want to delete the gate?"
msgid "Are you sure you want to delete this sales channel?"
msgstr "Czy na pewno chcesz usunąć ten kanał sprzedaży?"
msgstr "Czy na pewno chcesz usunąć bramkę?"
#: pretix/control/templates/pretixcontrol/organizers/channel_delete.html:15
#, fuzzy
#| msgid ""
#| "This membership cannot be deleted since it has been used in an order. "
#| "Change its end date to the past instead."
msgid ""
"This sales channel cannot be deleted since it has already been used to sell "
"orders or because it is a core element of the system."
msgstr ""
"Tego kanału sprzedaży nie można usunąć, ponieważ został już użyty do "
"sprzedaży zamówień lub jest podstawowym elementem systemu."
"Tego członkostwa nie można usunąć, ponieważ zostało użyte w zamówieniu. "
"Zamiast tego zmień jego datę końcową na przeszłą."
#: pretix/control/templates/pretixcontrol/organizers/channel_edit.html:6
#, fuzzy
#| msgid "Sales channel"
msgid "Sales channel:"
msgstr "Kanał sprzedaży:"
msgstr "Kanał sprzedaży"
#: pretix/control/templates/pretixcontrol/organizers/channels.html:8
msgid ""
@@ -22493,17 +22528,18 @@ msgid ""
"through. This is useful to unlock new revenue streams or to separate revenue "
"between different sources for reporting purchases."
msgstr ""
"Na tej stronie można zarządzać różnymi kanałami sprzedaży biletów. Jest to "
"przydatne do odblokowania nowych źródeł przychodów lub rozdzielenia "
"przychodów między różne źródła w celu raportowania zakupów."
#: pretix/control/templates/pretixcontrol/organizers/channels.html:15
#, fuzzy
#| msgid "Add a new value"
msgid "Add a new channel"
msgstr "Dodaj nowy kanał"
msgstr "Dodaj nową wartość"
#: pretix/control/templates/pretixcontrol/organizers/channels.html:22
#, fuzzy
#| msgid "Change"
msgid "Channel"
msgstr "Kanał"
msgstr "Zmień"
#: pretix/control/templates/pretixcontrol/organizers/customer.html:7
#: pretix/control/templates/pretixcontrol/organizers/customer.html:13
@@ -26740,24 +26776,34 @@ msgid "The customer account has been anonymized."
msgstr "Konto klienta zostało zanonimizowane."
#: pretix/control/views/organizer.py:3201
#, fuzzy
#| msgid "This organizer can not be deleted."
msgid "This channel can not be deleted."
msgstr "Tego kanału nie można usunąć."
msgstr "Tego organizatora nie można usunąć."
#: pretix/control/views/organizer.py:3206
#, fuzzy
#| msgid "The selected list has been deleted."
msgid "The selected sales channel has been deleted."
msgstr "Wybrany kanał sprzedaży został usunięty."
msgstr "Wybrana lista została usunięta."
#: pretix/control/views/organizer.py:3208
#, fuzzy
#| msgid ""
#| "The team could not be deleted as some constraints (e.g. data created by "
#| "plug-ins) do not allow it."
msgid ""
"The channel could not be deleted as some constraints (e.g. data created by "
"plug-ins) did not allow it."
msgstr ""
"Kanał nie mógł zostać usunięty, ponieważ niektóre ograniczenia (np. dane "
"utworzone przez wtyczki) na to nie pozwoliły."
"Zespół nie mógł zostać usunięty, ponieważ niektóre ograniczenia (np. dane "
"utworzone przez wtyczki) na to nie pozwalają."
#: pretix/control/views/organizer.py:3232
#, fuzzy
#| msgid "The order of discounts has been updated."
msgid "The order of sales channels has been updated."
msgstr "Kolejność kanałów sprzedaży została zaktualizowana."
msgstr "Kolejność rabatów została zaktualizowana."
#: pretix/control/views/pdf.py:83
msgid "The uploaded PDF file is too large."
@@ -28567,6 +28613,13 @@ msgid "Your PayPal account has been disconnected."
msgstr "Twoje konto PayPal zostało odłączone."
#: pretix/plugins/paypal2/apps.py:40
#, fuzzy
#| msgid ""
#| "Accept payments with your PayPal account. In addition to regular PayPal "
#| "payments, you can now also offer payments in a variety of local payment "
#| "methods such as giropay, SOFORT, iDEAL and many more to your customers - "
#| "they don't even need a PayPal account. PayPal is one of the most popular "
#| "payment methods world-wide."
msgid ""
"Accept payments with your PayPal account. In addition to regular PayPal "
"payments, you can now also offer payments in a variety of local payment "
@@ -28576,9 +28629,9 @@ msgid ""
msgstr ""
"Akceptuj płatności za pomocą konta PayPal. Oprócz zwykłych płatności PayPal, "
"możesz teraz oferować swoim klientom płatności za pomocą różnych lokalnych "
"metod płatności, takich jak eps, iDEAL i wiele innych - nie potrzebują oni "
"nawet konta PayPal. PayPal jest jedną z najpopularniejszych metod płatności "
"na całym świecie."
"metod płatności, takich jak giropay, SOFORT, iDEAL i wiele innych - nie "
"potrzebują oni nawet konta PayPal. PayPal jest jedną z najpopularniejszych "
"metod płatności na całym świecie."
#: pretix/plugins/paypal2/payment.py:99
msgid "PayPal Merchant ID"
@@ -28599,6 +28652,14 @@ msgid "Alternative Payment Methods"
msgstr "Alternatywne metody płatności"
#: pretix/plugins/paypal2/payment.py:151
#, fuzzy
#| msgid ""
#| "In addition to payments through a PayPal account, you can also offer your "
#| "customers the option to pay with credit cards and other, local payment "
#| "methods such as SOFORT, giropay, iDEAL, and many more - even when they do "
#| "not have a PayPal account. Eligible payment methods will be determined "
#| "based on the shoppers location. For German merchants, this is the direct "
#| "successor of PayPal Plus."
msgid ""
"In addition to payments through a PayPal account, you can also offer your "
"customers the option to pay with credit cards and other, local payment "
@@ -28609,10 +28670,10 @@ msgid ""
msgstr ""
"Oprócz płatności za pośrednictwem konta PayPal, możesz również zaoferować "
"swoim klientom możliwość płacenia kartami kredytowymi i innymi lokalnymi "
"metodami płatności, takimi jak eps, iDEAL i wiele innych - nawet jeśli nie "
"mają konta PayPal. Kwalifikujące się metody płatności zostaną określone na "
"podstawie lokalizacji kupującego. Dla niemieckich sprzedawców jest to "
"bezpośredni następca PayPal Plus."
"metodami płatności, takimi jak SOFORT, giropay, iDEAL i wiele innych - nawet "
"jeśli nie mają konta PayPal. Kwalifikujące się metody płatności zostaną "
"określone na podstawie lokalizacji kupującego. Dla niemieckich sprzedawców "
"jest to bezpośredni następca PayPal Plus."
#: pretix/plugins/paypal2/payment.py:166
msgid "Disable SEPA Direct Debit"
@@ -29819,6 +29880,11 @@ msgid "Stripe"
msgstr "Stripe"
#: pretix/plugins/stripe/apps.py:40
#, fuzzy
#| msgid ""
#| "Accept payments via Stripe, a globally popular payment service provider. "
#| "Stripe supports payments via credit cards as well as many local payment "
#| "methods such as giropay, iDEAL, Alipay,and many more."
msgid ""
"Accept payments via Stripe, a globally popular payment service provider. "
"Stripe supports payments via credit cards as well as many local payment "
@@ -33212,9 +33278,6 @@ msgid ""
"been added to the waiting list. We will only contact you once a spot opens "
"up."
msgstr ""
"Po dodaniu do listy oczekujących <strong>nie</strong> otrzymasz wiadomość e-"
"mail z potwierdzeniem. Skontaktujemy się z Tobą dopiero, gdy zwolni się "
"miejsce."
#: pretix/presale/templates/pretixpresale/event/waitinglist.html:44
msgid "Add me to the list"

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

@@ -949,9 +949,6 @@ class PaypalMethod(BasePaymentProvider):
}
})
response = self.client.execute(req)
except KeyError:
raise PaymentException(_('Refunding the amount via PayPal failed: The original payment does not contain '
'the required information to issue an automated refund.'))
except IOError as e:
refund.order.log_action('pretix.event.order.refund.failed', {
'local_id': refund.local_id,

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

@@ -84,7 +84,6 @@ seatingframe_html_head = EventPluginSignal()
"""
Arguments: ``request``
**Temporary workaround, might be removed again later.**
This signal allows you to put code inside the HTML ``<head>`` tag
of the seatingframe page in the frontend. You will get the request as the keyword argument
``request`` and are expected to return plain HTML.

View File

@@ -151,7 +151,7 @@ def get_grouped_items(event, *, channel: SalesChannel, subevent=None, voucher=No
),
).filter(
variation_q,
Q(all_sales_channels=True) | Q(limit_sales_channels=channel),
Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=channel.identifier),
active=True,
quotas__isnull=False,
subevent_disabled=False
@@ -685,7 +685,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
add_subevents_for_days(
filter_qs_by_attr(
self.request.event.subevents_annotated(
self.request.sales_channel,
self.request.sales_channel.identifier,
voucher,
).using(settings.DATABASE_REPLICA),
self.request
@@ -744,7 +744,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
add_subevents_for_days(
filter_qs_by_attr(
self.request.event.subevents_annotated(
self.request.sales_channel,
self.request.sales_channel.identifier,
voucher=voucher,
).using(settings.DATABASE_REPLICA),
self.request
@@ -793,7 +793,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
context['subevent_list'] = self.request.event.subevents_sorted(
filter_qs_by_attr(
self.request.event.subevents_annotated(
self.request.sales_channel,
self.request.sales_channel.identifier,
voucher=voucher,
).using(settings.DATABASE_REPLICA),
self.request

View File

@@ -185,7 +185,7 @@ class EventListMixin:
def _get_event_list_queryset(self):
query = Q(is_public=True) & Q(live=True)
qs = self.request.organizer.events.using(settings.DATABASE_REPLICA).filter(query)
qs = qs.filter(Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel))
qs = qs.filter(Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier))
qs = qs.annotate(
min_from=Min('subevents__date_from'),
min_to=Min('subevents__date_to'),
@@ -213,7 +213,7 @@ class EventListMixin:
).order_by('order_from')
qs = Event.annotated(filter_qs_by_attr(
qs, self.request, match_subevents_with_conditions=Q(active=True) & Q(is_public=True) & date_q
), self.request.sales_channel)
))
return qs
def _set_month_to_next_subevent(self):
@@ -724,10 +724,10 @@ class CalendarView(OrganizerViewMixin, EventListMixin, TemplateView):
ctx['has_before'], ctx['has_after'] = has_before_after(
self.request.organizer.events.filter(
Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel),
Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier),
),
SubEvent.objects.filter(
Q(event__all_sales_channels=True) | Q(event__limit_sales_channels=self.request.sales_channel),
Q(event__all_sales_channels=True) | Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier),
event__organizer=self.request.organizer,
event__is_public=True,
event__live=True,
@@ -746,14 +746,14 @@ class CalendarView(OrganizerViewMixin, EventListMixin, TemplateView):
def _events_by_day(self, before, after):
ebd = defaultdict(list)
timezones = set()
add_events_for_days(self.request, Event.annotated(self.request.organizer.events, self.request.sales_channel).using(
add_events_for_days(self.request, Event.annotated(self.request.organizer.events, 'web').using(
settings.DATABASE_REPLICA
).filter(
Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel),
Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier),
), before, after, ebd, timezones)
add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter(
Q(event__all_sales_channels=True) |
Q(event__limit_sales_channels=self.request.sales_channel),
Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier),
event__organizer=self.request.organizer,
event__is_public=True,
event__live=True,
@@ -768,7 +768,7 @@ class CalendarView(OrganizerViewMixin, EventListMixin, TemplateView):
)
)
)
), self.request.sales_channel), self.request).using(settings.DATABASE_REPLICA), before, after, ebd, timezones)
)), self.request).using(settings.DATABASE_REPLICA), before, after, ebd, timezones)
self._multiple_timezones = len(timezones) > 1
return ebd
@@ -807,11 +807,11 @@ class WeekCalendarView(OrganizerViewMixin, EventListMixin, TemplateView):
ctx['has_before'], ctx['has_after'] = has_before_after(
self.request.organizer.events.filter(
Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel),
Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier),
),
SubEvent.objects.filter(
Q(event__all_sales_channels=True) |
Q(event__limit_sales_channels=self.request.sales_channel),
Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier),
event__organizer=self.request.organizer,
event__is_public=True,
event__live=True,
@@ -842,14 +842,14 @@ class WeekCalendarView(OrganizerViewMixin, EventListMixin, TemplateView):
def _events_by_day(self, before, after):
ebd = defaultdict(list)
timezones = set()
add_events_for_days(self.request, Event.annotated(self.request.organizer.events, self.request.sales_channel).using(
add_events_for_days(self.request, Event.annotated(self.request.organizer.events, 'web').using(
settings.DATABASE_REPLICA
).filter(
Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel),
Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier),
), before, after, ebd, timezones)
add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter(
Q(event__all_sales_channels=True) |
Q(event__limit_sales_channels=self.request.sales_channel),
Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier),
event__organizer=self.request.organizer,
event__is_public=True,
event__live=True,
@@ -864,7 +864,7 @@ class WeekCalendarView(OrganizerViewMixin, EventListMixin, TemplateView):
)
)
)
), self.request.sales_channel), self.request).using(settings.DATABASE_REPLICA), before, after, ebd, timezones)
)), self.request).using(settings.DATABASE_REPLICA), before, after, ebd, timezones)
self._multiple_timezones = len(timezones) > 1
return ebd
@@ -946,11 +946,11 @@ class DayCalendarView(OrganizerViewMixin, EventListMixin, TemplateView):
ctx['has_before'], ctx['has_after'] = has_before_after(
self.request.organizer.events.filter(
Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel),
Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier),
),
SubEvent.objects.filter(
Q(event__all_sales_channels=True) |
Q(event__limit_sales_channels=self.request.sales_channel),
Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier),
event__organizer=self.request.organizer,
event__is_public=True,
event__live=True,
@@ -1194,14 +1194,14 @@ class DayCalendarView(OrganizerViewMixin, EventListMixin, TemplateView):
def _events_by_day(self, before, after):
ebd = defaultdict(list)
timezones = set()
add_events_for_days(self.request, Event.annotated(self.request.organizer.events, self.request.sales_channel).using(
add_events_for_days(self.request, Event.annotated(self.request.organizer.events, 'web').using(
settings.DATABASE_REPLICA
).filter(
Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel),
Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier),
), before, after, ebd, timezones)
add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter(
Q(event__all_sales_channels=True) |
Q(event__limit_sales_channels=self.request.sales_channel),
Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier),
event__organizer=self.request.organizer,
event__is_public=True,
event__live=True,
@@ -1216,7 +1216,7 @@ class DayCalendarView(OrganizerViewMixin, EventListMixin, TemplateView):
)
)
)
), self.request.sales_channel), self.request).using(settings.DATABASE_REPLICA), before, after, ebd, timezones)
)), self.request).using(settings.DATABASE_REPLICA), before, after, ebd, timezones)
self._multiple_timezones = len(timezones) > 1
return ebd
@@ -1229,7 +1229,7 @@ class OrganizerIcalDownload(OrganizerViewMixin, View):
filter_qs_by_attr(
self.request.organizer.events.filter(
Q(date_from__gt=cutoff) | Q(date_to__gt=cutoff),
Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel),
Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier),
is_public=True,
live=True,
has_subevents=False,
@@ -1250,7 +1250,7 @@ class OrganizerIcalDownload(OrganizerViewMixin, View):
SubEvent.objects.filter(
Q(date_from__gt=cutoff) | Q(date_to__gt=cutoff),
Q(event__all_sales_channels=True) |
Q(event__limit_sales_channels=self.request.sales_channel),
Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier),
event__organizer=self.request.organizer,
event__is_public=True,
event__live=True,

View File

@@ -545,9 +545,9 @@ class WidgetAPIProductList(EventListMixin, View):
if hasattr(self.request, 'event'):
add_subevents_for_days(
filter_qs_by_attr(
self.request.event.subevents_annotated(self.request.sales_channel).filter(
self.request.event.subevents_annotated('web').filter(
Q(event__all_sales_channels=True) |
Q(event__limit_sales_channels=self.request.sales_channel),
Q(event__limit_sales_channels__identifier=self.request.sales_channel.identifier),
), self.request
),
limit_before, after, ebd, set(), self.request.event,
@@ -558,8 +558,8 @@ class WidgetAPIProductList(EventListMixin, View):
add_events_for_days(
self.request,
filter_qs_by_attr(
Event.annotated(self.request.organizer.events, self.request.sales_channel).filter(
Q(all_sales_channels=True) | Q(limit_sales_channels=self.request.sales_channel),
Event.annotated(self.request.organizer.events, 'web').filter(
Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=self.request.sales_channel.identifier),
), self.request
),
limit_before, after, ebd, timezones
@@ -572,7 +572,7 @@ class WidgetAPIProductList(EventListMixin, View):
event__live=True,
).prefetch_related(
'event___settings_objects', 'event__organizer___settings_objects'
), self.request.sales_channel), self.request), limit_before, after, ebd, timezones)
)), self.request), limit_before, after, ebd, timezones)
data['weeks'] = weeks_for_template(ebd, self.year, self.month)
for w in data['weeks']:
@@ -605,7 +605,7 @@ class WidgetAPIProductList(EventListMixin, View):
ebd = defaultdict(list)
if hasattr(self.request, 'event'):
add_subevents_for_days(
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel), self.request),
filter_qs_by_attr(self.request.event.subevents_annotated('web'), self.request),
limit_before, after, ebd, set(), self.request.event,
kwargs.get('cart_namespace')
)
@@ -613,7 +613,7 @@ class WidgetAPIProductList(EventListMixin, View):
timezones = set()
add_events_for_days(
self.request,
filter_qs_by_attr(Event.annotated(self.request.organizer.events, self.request.sales_channel), self.request),
filter_qs_by_attr(Event.annotated(self.request.organizer.events, 'web'), self.request),
limit_before, after, ebd, timezones
)
add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter(
@@ -622,7 +622,7 @@ class WidgetAPIProductList(EventListMixin, View):
event__live=True,
).prefetch_related(
'event___settings_objects', 'event__organizer___settings_objects'
), self.request.sales_channel), self.request), limit_before, after, ebd, timezones)
)), self.request), limit_before, after, ebd, timezones)
data['days'] = days_for_template(ebd, week)
for d in data['days']:
@@ -632,7 +632,7 @@ class WidgetAPIProductList(EventListMixin, View):
limit = 50
if hasattr(self.request, 'event'):
evs = filter_qs_by_attr(
self.request.event.subevents_annotated(self.request.sales_channel),
self.request.event.subevents_annotated(self.request.sales_channel.identifier),
self.request,
match_subevents_with_conditions=(
Q(Q(date_to__isnull=True) & Q(date_from__gte=now() - timedelta(hours=24)))

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

@@ -1381,18 +1381,3 @@ def test_checkin_pdf_data_requires_permission(token_client, event, team, organiz
organizer.slug, event.slug, clist_all.pk
))
assert not resp.data['results'][0].get('pdf_data')
@pytest.mark.django_db
def test_expand(token_client, organizer, event, clist, clist_all, item, other_item, order, django_assert_max_num_queries):
with scopes_disabled():
op = order.positions.first()
var1 = item.variations.create(value="XS")
op.variation = var1
op.save()
resp = token_client.get('/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/?search=z3fsn8jyu&expand=variation'.format(
organizer.slug, event.slug, clist_all.pk
))
assert resp.status_code == 200
assert 'value' in resp.data['results'][0]['variation']

View File

@@ -763,50 +763,6 @@ def test_event_update(token_client, organizer, event, item, meta_prop):
assert cnt == event.all_logentries().count()
@pytest.mark.django_db
def test_event_update_plugins_validation(token_client, organizer, event, item, meta_prop):
resp = token_client.patch(
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
{
"plugins": ["pretix.plugins.paypal2", "unknown"]
},
format='json'
)
assert resp.status_code == 400
assert resp.data == {"plugins": ["Unknown plugin: 'unknown'."]}
resp = token_client.patch(
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
{
"plugins": ["pretix.plugins.paypal2", "tests.testdummyhidden"]
},
format='json'
)
assert resp.status_code == 400
assert resp.data == {"plugins": ["Unknown plugin: 'tests.testdummyhidden'."]}
resp = token_client.patch(
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
{
"plugins": ["pretix.plugins.paypal2", "tests.testdummyrestricted"]
},
format='json'
)
assert resp.status_code == 400
assert resp.data == {"plugins": ["Restricted plugin: 'tests.testdummyrestricted'."]}
organizer.settings.allowed_restricted_plugins = ["tests.testdummyrestricted"]
resp = token_client.patch(
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
{
"plugins": ["pretix.plugins.paypal2", "tests.testdummyrestricted"]
},
format='json'
)
assert resp.status_code == 200
@pytest.mark.django_db
def test_event_test_mode(token_client, organizer, event):
resp = token_client.patch(

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):
@@ -597,28 +590,7 @@ def test_item_create_with_variation(token_client, organizer, event, item, catego
"meta_data": {
"day": "Wednesday",
},
},
{
"value": {
"de": "web",
"en": "web"
},
"active": True,
"require_approval": True,
"checkin_attention": False,
"checkin_text": None,
"require_membership": False,
"require_membership_hidden": False,
"require_membership_types": [],
"description": None,
"position": 0,
"default_price": None,
"sales_channels": ["web"],
"price": "23.00",
"meta_data": {
"day": "Wednesday",
},
},
}
]
},
format='json'
@@ -632,8 +604,6 @@ def test_item_create_with_variation(token_client, organizer, event, item, catego
assert new_item.variations.first().all_sales_channels is True
assert not new_item.variations.first().limit_sales_channels.exists()
assert new_item.variations.first().meta_data == {"day": "Wednesday"}
assert new_item.variations.last().all_sales_channels is False
assert new_item.variations.last().limit_sales_channels.exists()
@pytest.mark.django_db
@@ -1419,21 +1389,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

@@ -414,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))
@@ -444,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

@@ -2329,8 +2329,8 @@ class EventTest(TestCase):
item2 = Item.objects.create(event=event, name='Early-bird ticket', default_price=0, active=False)
q.items.add(item)
q.items.add(item2)
assert Event.annotated(Event.objects, 'web').first().active_quotas == [q]
assert Event.annotated(Event.objects, self.organizer.sales_channels.get(identifier="bar")).first().active_quotas == []
assert Event.annotated(Event.objects).first().active_quotas == [q]
assert Event.annotated(Event.objects, 'foo').first().active_quotas == []
@classscope(attr='organizer')
def test_active_quotas_annotation_product_inactive(self):
@@ -2341,7 +2341,7 @@ class EventTest(TestCase):
q = Quota.objects.create(event=event, name='Quota', size=2)
item = Item.objects.create(event=event, name='Early-bird ticket', default_price=0, active=False)
q.items.add(item)
assert Event.annotated(Event.objects, 'web').first().active_quotas == []
assert Event.annotated(Event.objects).first().active_quotas == []
@classscope(attr='organizer')
def test_active_quotas_annotation_product_hidden_by_voucher(self):
@@ -2354,16 +2354,16 @@ class EventTest(TestCase):
q.items.add(item)
voucher = Voucher.objects.create(event=event, code='a', item=item, show_hidden_items=True)
assert Event.annotated(Event.objects, "web", voucher=voucher).first().active_quotas == [q]
assert Event.annotated(Event.objects, voucher=voucher).first().active_quotas == [q]
voucher = Voucher.objects.create(event=event, code='b', item=item, show_hidden_items=False)
assert Event.annotated(Event.objects, "web", voucher=voucher).first().active_quotas == []
assert Event.annotated(Event.objects, voucher=voucher).first().active_quotas == []
voucher = Voucher.objects.create(event=event, code='c', show_hidden_items=True)
assert Event.annotated(Event.objects, "web", voucher=voucher).first().active_quotas == [q]
assert Event.annotated(Event.objects, voucher=voucher).first().active_quotas == [q]
voucher = Voucher.objects.create(event=event, code='d', quota=q, show_hidden_items=True)
assert Event.annotated(Event.objects, "web", voucher=voucher).first().active_quotas == [q]
assert Event.annotated(Event.objects, voucher=voucher).first().active_quotas == [q]
item2 = Item.objects.create(event=event, name='Early-bird ticket', default_price=0)
var = item2.variations.create(item=item2, value='Test', hide_without_voucher=True)
@@ -2373,13 +2373,13 @@ class EventTest(TestCase):
q.variations.add(var)
voucher = Voucher.objects.create(event=event, code='e', item=item2, variation=var, show_hidden_items=True)
assert Event.annotated(Event.objects, "web", voucher=voucher).first().active_quotas == [q]
assert Event.annotated(Event.objects, voucher=voucher).first().active_quotas == [q]
voucher = Voucher.objects.create(event=event, code='f', item=item2, variation=var2, show_hidden_items=True)
assert Event.annotated(Event.objects, "web", voucher=voucher).first().active_quotas == []
assert Event.annotated(Event.objects, voucher=voucher).first().active_quotas == []
voucher = Voucher.objects.create(event=event, code='g', quota=q, show_hidden_items=True)
assert Event.annotated(Event.objects, "web", voucher=voucher).first().active_quotas == [q]
assert Event.annotated(Event.objects, voucher=voucher).first().active_quotas == [q]
@classscope(attr='organizer')
def test_active_quotas_annotation_product_addon(self):
@@ -2395,7 +2395,7 @@ class EventTest(TestCase):
item.category = cat
item.save()
q.items.add(item)
assert Event.annotated(Event.objects, 'web').first().active_quotas == []
assert Event.annotated(Event.objects).first().active_quotas == []
@classscope(attr='organizer')
def test_active_quotas_annotation_product_unavailable(self):
@@ -2407,7 +2407,7 @@ class EventTest(TestCase):
item = Item.objects.create(event=event, name='Early-bird ticket', default_price=0, active=True,
available_until=now() - timedelta(days=1))
q.items.add(item)
assert Event.annotated(Event.objects, 'web').first().active_quotas == []
assert Event.annotated(Event.objects).first().active_quotas == []
@classscope(attr='organizer')
def test_active_quotas_annotation_variation_not_in_quota(self):
@@ -2419,7 +2419,7 @@ class EventTest(TestCase):
item = Item.objects.create(event=event, name='Early-bird ticket', default_price=0, active=True)
item.variations.create(value="foo")
q.items.add(item)
assert Event.annotated(Event.objects, 'web').first().active_quotas == []
assert Event.annotated(Event.objects).first().active_quotas == []
@classscope(attr='organizer')
def test_active_quotas_annotation_variation(self):
@@ -2434,29 +2434,29 @@ class EventTest(TestCase):
v.limit_sales_channels.add(self.organizer.sales_channels.get(identifier="web"))
q.items.add(item)
q.variations.add(v)
assert Event.annotated(Event.objects, 'web').first().active_quotas == [q]
assert Event.annotated(Event.objects).first().active_quotas == [q]
item.available_until = now() - timedelta(days=1)
item.save()
assert Event.annotated(Event.objects, 'web').first().active_quotas == []
assert Event.annotated(Event.objects).first().active_quotas == []
item.available_until = None
item.available_from = now() + timedelta(days=1)
item.save()
assert Event.annotated(Event.objects, 'web').first().active_quotas == []
assert Event.annotated(Event.objects).first().active_quotas == []
item.available_until = None
item.available_from = None
item.active = False
item.save()
assert Event.annotated(Event.objects, 'web').first().active_quotas == []
assert Event.annotated(Event.objects).first().active_quotas == []
item.active = True
item.save()
assert Event.annotated(Event.objects, 'web').first().active_quotas == [q]
assert Event.annotated(Event.objects, self.organizer.sales_channels.get(identifier="bar")).first().active_quotas == []
assert Event.annotated(Event.objects).first().active_quotas == [q]
assert Event.annotated(Event.objects, 'foo').first().active_quotas == []
v.active = False
v.save()
assert Event.annotated(Event.objects, 'web').first().active_quotas == []
assert Event.annotated(Event.objects).first().active_quotas == []
item.hide_without_voucher = True
item.save()
assert Event.annotated(Event.objects, 'web').first().active_quotas == []
assert Event.annotated(Event.objects).first().active_quotas == []
class SubEventTest(TestCase):
@@ -2502,10 +2502,8 @@ class SubEventTest(TestCase):
all_sales_channels=False)
item.limit_sales_channels.add(self.organizer.sales_channels.get(identifier="web"))
q.items.add(item)
assert SubEvent.annotated(SubEvent.objects, 'web').first().active_quotas == [q]
assert SubEvent.annotated(SubEvent.objects, 'bar').first().active_quotas == []
assert SubEvent.annotated(SubEvent.objects, self.organizer.sales_channels.get(identifier="web")).first().active_quotas == [q]
assert SubEvent.annotated(SubEvent.objects, self.organizer.sales_channels.get(identifier="bar")).first().active_quotas == []
assert SubEvent.annotated(SubEvent.objects).first().active_quotas == [q]
assert SubEvent.annotated(SubEvent.objects, 'foo').first().active_quotas == []
@classscope(attr='organizer')
def test_active_quotas_annotation_no_interference(self):
@@ -2516,8 +2514,8 @@ class SubEventTest(TestCase):
subevent=se2)
item = Item.objects.create(event=self.event, name='Early-bird ticket', default_price=0, active=True)
q.items.add(item)
assert SubEvent.annotated(SubEvent.objects, 'web').filter(pk=self.se.pk).first().active_quotas == []
assert SubEvent.annotated(SubEvent.objects, 'web').filter(pk=se2.pk).first().active_quotas == [q]
assert SubEvent.annotated(SubEvent.objects).filter(pk=self.se.pk).first().active_quotas == []
assert SubEvent.annotated(SubEvent.objects).filter(pk=se2.pk).first().active_quotas == [q]
@classscope(attr='organizer')
def test_best_availability(self):
@@ -2542,7 +2540,7 @@ class SubEventTest(TestCase):
q = Quota.objects.create(event=self.event, name='Quota', size=1,
subevent=self.se)
q.items.add(item)
obj = SubEvent.annotated(SubEvent.objects, 'web').first()
obj = SubEvent.annotated(SubEvent.objects).first()
assert len(obj.active_quotas) == 1
assert obj.best_availability == (Quota.AVAILABILITY_GONE, 0, 1)
@@ -2550,14 +2548,14 @@ class SubEventTest(TestCase):
q2 = Quota.objects.create(event=self.event, name='Quota 2', size=2,
subevent=self.se)
q2.items.add(item)
obj = SubEvent.annotated(SubEvent.objects, 'web').first()
obj = SubEvent.annotated(SubEvent.objects).first()
assert len(obj.active_quotas) == 2
assert obj.best_availability == (Quota.AVAILABILITY_GONE, 0, 1)
# 2 quotas - 2 items. Higher quota wins since second item is only connected to second quota.
item2 = Item.objects.create(event=self.event, name='Regular ticket', default_price=10, active=True)
q2.items.add(item2)
obj = SubEvent.annotated(SubEvent.objects, 'web').first()
obj = SubEvent.annotated(SubEvent.objects).first()
assert len(obj.active_quotas) == 2
assert obj.best_availability == (Quota.AVAILABILITY_OK, 1, 2)
assert obj.best_availability_is_low
@@ -2566,7 +2564,7 @@ class SubEventTest(TestCase):
q.size = 10
q.save()
q2.delete()
obj = SubEvent.annotated(SubEvent.objects, 'web').first()
obj = SubEvent.annotated(SubEvent.objects).first()
assert len(obj.active_quotas) == 1
assert obj.best_availability == (Quota.AVAILABILITY_OK, 9, 10)
assert not obj.best_availability_is_low
@@ -2574,7 +2572,7 @@ class SubEventTest(TestCase):
# Unlimited quota
q.size = None
q.save()
obj = SubEvent.annotated(SubEvent.objects, 'web').first()
obj = SubEvent.annotated(SubEvent.objects).first()
assert obj.best_availability == (Quota.AVAILABILITY_OK, None, None)
assert not obj.best_availability_is_low

View File

@@ -299,8 +299,6 @@ class EventsTest(SoupTest):
doc = self.get_doc('/control/event/%s/%s/settings/plugins' % (self.orga1.slug, self.event1.slug))
self.assertIn("Stripe", doc.select(".form-plugins")[0].text)
self.assertIn("Enable", doc.select("[name=\"plugin:pretix.plugins.stripe\"]")[0].text)
assert not doc.select("[name=\"plugin:tests.testdummyrestricted\"]")
assert not doc.select("[name=\"plugin:tests.testdummyhidden\"]")
doc = self.post_doc('/control/event/%s/%s/settings/plugins' % (self.orga1.slug, self.event1.slug),
{'plugin:pretix.plugins.stripe': 'enable'})
@@ -310,23 +308,6 @@ class EventsTest(SoupTest):
{'plugin:pretix.plugins.stripe': 'disable'})
self.assertIn("Enable", doc.select("[name=\"plugin:pretix.plugins.stripe\"]")[0].text)
self.post_doc('/control/event/%s/%s/settings/plugins' % (self.orga1.slug, self.event1.slug),
{'plugin:tests.testdummyhidden': 'enable'})
self.event1.refresh_from_db()
assert "testdummyhidden" not in self.event1.plugins
self.post_doc('/control/event/%s/%s/settings/plugins' % (self.orga1.slug, self.event1.slug),
{'plugin:tests.testdummyrestricted': 'enable'})
self.event1.refresh_from_db()
assert "testdummyrestricted" not in self.event1.plugins
self.orga1.settings.allowed_restricted_plugins = ["tests.testdummyrestricted"]
self.post_doc('/control/event/%s/%s/settings/plugins' % (self.orga1.slug, self.event1.slug),
{'plugin:tests.testdummyrestricted': 'enable'})
self.event1.refresh_from_db()
assert "testdummyrestricted" in self.event1.plugins
def test_testmode_enable(self):
self.event1.testmode = False
self.event1.save()

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,56 +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","","","","","","","","","","","","","","",""
"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","","","","","","","","","","","","",""
""")

View File

@@ -28,8 +28,6 @@ TEST_DIR = os.path.dirname(__file__)
TEMPLATES[0]['DIRS'].append(os.path.join(TEST_DIR, 'templates')) # NOQA
INSTALLED_APPS.append('tests.testdummy') # NOQA
INSTALLED_APPS.append('tests.testdummyrestricted') # NOQA
INSTALLED_APPS.append('tests.testdummyhidden') # NOQA
PRETIX_AUTH_BACKENDS = [
'pretix.base.auth.NativeAuthBackend',

View File

@@ -0,0 +1,18 @@
from io import StringIO
import pytest
from django.core.management import call_command
@pytest.mark.django_db
def test_no_pending_migrations():
out = StringIO()
try:
call_command(
"makemigrations",
"--check",
stdout=out,
stderr=StringIO(),
)
except SystemExit: # pragma: no cover
raise AssertionError("Pending migrations:\n" + out.getvalue()) from None

View File

@@ -1,21 +0,0 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#

View File

@@ -1,35 +0,0 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from django.apps import AppConfig
class TestDummyHiddenApp(AppConfig):
name = 'tests.testdummyhidden'
verbose_name = 'testdummyhidden'
class PretixPluginMeta:
name = 'testdummyhidden'
version = '1.0.0'
restricted = True
def is_available(self, event):
return False

View File

@@ -1,21 +0,0 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#

View File

@@ -1,32 +0,0 @@
#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from django.apps import AppConfig
class TestDummyRestrictedApp(AppConfig):
name = 'tests.testdummyrestricted'
verbose_name = 'testdummyrestricted'
class PretixPluginMeta:
name = 'testdummyrestricted'
version = '1.0.0'
restricted = True