Compare commits

..
Author SHA1 Message Date
MiraandGitHub ae169ee03a Update payment.py 2023-08-31 14:51:34 +02:00
robbi5andGitHub 826c54bce7 Questions: Enable phonenumber type during checkin (#3557) 2023-08-31 13:46:19 +02:00
Ash SoandRaphael Michel 0cf2f06fcf Translations: Update Chinese (Traditional)
Currently translated at 99.7% (5385 of 5400 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/zh_Hant/

powered by weblate
2023-08-31 11:02:05 +02:00
Raphael MichelandGitHub 12095c0c9c Sendmail: Rename "automated emails" to "scheduled emails" (#3556) 2023-08-31 11:01:50 +02:00
bca4cd1e23 Update translations (#3553)
Co-authored-by: Ash So <ashs@vankaifong.com>
Co-authored-by: Martin Gross <gross@rami.io>
2023-08-31 09:38:28 +02:00
Raphael Michel 53e84dfb08 API: Fix validation of duplicate customer email addresses 2023-08-30 16:57:15 +02:00
Raphael MichelandGitHub 2e8447486c Improve edge cases in handling of check-in nonces (#3516) 2023-08-30 10:43:24 +02:00
5b184bb1a0 Fix leaflet osm tile suggestion (#3549)
Co-authored-by: Raphael Michel <michel@rami.io>
2023-08-30 09:53:41 +02:00
Ash SoandRaphael Michel 8c6f0a5dc1 Translations: Update Chinese (Traditional)
Currently translated at 99.7% (5384 of 5400 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/zh_Hant/

powered by weblate
2023-08-30 09:46:11 +02:00
AlainandRaphael Michel 6a53091b91 Translations: Update Dutch
Currently translated at 84.4% (4559 of 5400 strings)

Translation: pretix/pretix
Translate-URL: https://translate.pretix.eu/projects/pretix/pretix/nl/

powered by weblate
2023-08-30 09:46:11 +02:00
MiraandGitHub be4bc9a6f3 TemplateBasedMailRenderer: make markdown compiler call overridable (#3550) 2023-08-30 09:41:34 +02:00
Raphael Michel efb1141d59 PayPal: Add missing payment.fail() statements 2023-08-29 15:10:05 +02:00
Raphael Michel 322a730eb2 PayPal: Fix incorrect Decimal comparison 2023-08-29 15:06:03 +02:00
Raphael MichelandGitHub 8d2224e725 API: Allow organizer-level access of orders and invoices (#3547) 2023-08-28 16:54:42 +02:00
Raphael Michel 5b819b76f0 Check-in: Fix N+1 query issue identified by sentry 2023-08-28 16:54:09 +02:00
Raphael MichelandGitHub 5d90a42acf Discounts: Allow "buy X to get Y" with different product sets for X and Y (#3543) 2023-08-28 16:21:52 +02:00
Raphael Michel 5398671fde Fix crash in invoice address detection (PRETIXEU-8XE) 2023-08-28 11:45:30 +02:00
87 changed files with 1358 additions and 633 deletions
+33 -2
View File
@@ -31,9 +31,9 @@ subevent_mode strings Determines h
``"same"`` (discount is only applied for groups within
the same date), or ``"distinct"`` (discount is only applied
for groups with no two same dates).
condition_all_products boolean If ``true``, the discount applies to all items.
condition_all_products boolean If ``true``, the discount condition applies to all items.
condition_limit_products list of integers If ``condition_all_products`` is not set, this is a list
of internal item IDs that the discount applies to.
of internal item IDs that the discount condition applies to.
condition_apply_to_addons boolean If ``true``, the discount applies to add-on products as well,
otherwise it only applies to top-level items. The discount never
applies to bundled products.
@@ -48,6 +48,17 @@ benefit_discount_matching_percent decimal (string) The percenta
benefit_only_apply_to_cheapest_n_matches integer If set higher than 0, the discount will only be applied to
the cheapest matches. Useful for a "3 for 2"-style discount.
Cannot be combined with ``condition_min_value``.
benefit_same_products boolean If ``true``, the discount benefit applies to the same set of items
as the condition (see above).
benefit_limit_products list of integers If ``benefit_same_products`` is not set, this is a list
of internal item IDs that the discount benefit applies to.
benefit_apply_to_addons boolean (Only used if ``benefit_same_products`` is ``false``.)
If ``true``, the discount applies to add-on products as well,
otherwise it only applies to top-level items. The discount never
applies to bundled products.
benefit_ignore_voucher_discounted boolean (Only used if ``benefit_same_products`` is ``false``.)
If ``true``, the discount does not apply to products which have
been discounted by a voucher.
======================================== ========================== =======================================================
@@ -94,6 +105,10 @@ Endpoints
"condition_ignore_voucher_discounted": false,
"condition_min_count": 3,
"condition_min_value": "0.00",
"benefit_same_products": true,
"benefit_limit_products": [],
"benefit_apply_to_addons": true,
"benefit_ignore_voucher_discounted": false,
"benefit_discount_matching_percent": "100.00",
"benefit_only_apply_to_cheapest_n_matches": 1
}
@@ -146,6 +161,10 @@ Endpoints
"condition_ignore_voucher_discounted": false,
"condition_min_count": 3,
"condition_min_value": "0.00",
"benefit_same_products": true,
"benefit_limit_products": [],
"benefit_apply_to_addons": true,
"benefit_ignore_voucher_discounted": false,
"benefit_discount_matching_percent": "100.00",
"benefit_only_apply_to_cheapest_n_matches": 1
}
@@ -184,6 +203,10 @@ Endpoints
"condition_ignore_voucher_discounted": false,
"condition_min_count": 3,
"condition_min_value": "0.00",
"benefit_same_products": true,
"benefit_limit_products": [],
"benefit_apply_to_addons": true,
"benefit_ignore_voucher_discounted": false,
"benefit_discount_matching_percent": "100.00",
"benefit_only_apply_to_cheapest_n_matches": 1
}
@@ -211,6 +234,10 @@ Endpoints
"condition_ignore_voucher_discounted": false,
"condition_min_count": 3,
"condition_min_value": "0.00",
"benefit_same_products": true,
"benefit_limit_products": [],
"benefit_apply_to_addons": true,
"benefit_ignore_voucher_discounted": false,
"benefit_discount_matching_percent": "100.00",
"benefit_only_apply_to_cheapest_n_matches": 1
}
@@ -267,6 +294,10 @@ Endpoints
"condition_ignore_voucher_discounted": false,
"condition_min_count": 3,
"condition_min_value": "0.00",
"benefit_same_products": true,
"benefit_limit_products": [],
"benefit_apply_to_addons": true,
"benefit_ignore_voucher_discounted": false,
"benefit_discount_matching_percent": "100.00",
"benefit_only_apply_to_cheapest_n_matches": 1
}
+59 -2
View File
@@ -12,6 +12,7 @@ The invoice resource contains the following public fields:
Field Type Description
===================================== ========================== =======================================================
number string Invoice number (with prefix)
event string The slug of the parent event
order string Order code of the order this invoice belongs to
is_cancellation boolean ``true``, if this invoice is the cancellation of a
different invoice.
@@ -121,9 +122,13 @@ internal_reference string Customer's refe
The attribute ``lines.subevent`` has been added.
.. versionchanged:: 2023.8
Endpoints
---------
The ``event`` attribute has been added. The organizer-level endpoint has been added.
List of all invoices
--------------------
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/invoices/
@@ -152,6 +157,7 @@ Endpoints
"results": [
{
"number": "SAMPLECONF-00001",
"event": "sampleconf",
"order": "ABC12",
"is_cancellation": false,
"invoice_from_name": "Big Events LLC",
@@ -221,6 +227,50 @@ Endpoints
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
.. http:get:: /api/v1/organizers/(organizer)/invoices/
Returns a list of all invoices within all events of a given organizer (with sufficient access permissions).
Supported query parameters and output format of this endpoint are identical to the list endpoint within an event.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/events/sampleconf/invoices/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"number": "SAMPLECONF-00001",
"event": "sampleconf",
"order": "ABC12",
...
]
}
:param organizer: The ``slug`` field of the organizer to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
Fetching individual invoices
----------------------------
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/invoices/(number)/
Returns information on one invoice, identified by its invoice number.
@@ -243,6 +293,7 @@ Endpoints
{
"number": "SAMPLECONF-00001",
"event": "sampleconf",
"order": "ABC12",
"is_cancellation": false,
"invoice_from_name": "Big Events LLC",
@@ -337,6 +388,12 @@ Endpoints
:statuscode 409: The file is not yet ready and will now be prepared. Retry the request after waiting for a few
seconds.
Modifying invoices
------------------
Invoices cannot be edited directly, but the following actions can be triggered:
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/invoices/(invoice_no)/reissue/
Cancels the invoice and creates a new one.
+49
View File
@@ -20,6 +20,7 @@ The order resource contains the following public fields:
Field Type Description
===================================== ========================== =======================================================
code string Order code
event string The slug of the parent event
status string Order status, one of:
* ``n`` pending
@@ -130,6 +131,10 @@ last_modified datetime Last modificati
The ``valid_if_pending`` attribute has been added.
.. versionchanged:: 2023.8
The ``event`` attribute has been added. The organizer-level endpoint has been added.
.. _order-position-resource:
@@ -289,6 +294,7 @@ List of all orders
"results": [
{
"code": "ABC12",
"event": "sampleconf",
"status": "p",
"testmode": false,
"secret": "k24fiuwvu8kxz3y1",
@@ -441,6 +447,48 @@ List of all orders
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
.. http:get:: /api/v1/organizers/(organizer)/orders/
Returns a list of all orders within all events of a given organizer (with sufficient access permissions).
Supported query parameters and output format of this endpoint are identical to the list endpoint within an event,
with the exception that the ``pdf_data`` parameter is not supported here.
**Example request**:
.. sourcecode:: http
GET /api/v1/organizers/bigevents/orders/ HTTP/1.1
Host: pretix.eu
Accept: application/json, text/javascript
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Vary: Accept
Content-Type: application/json
X-Page-Generated: 2017-12-01T10:00:00Z
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"code": "ABC12",
"event": "sampleconf",
...
}
]
}
:param organizer: The ``slug`` field of the organizer to fetch
:statuscode 200: no error
:statuscode 401: Authentication failure
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
Fetching individual orders
--------------------------
@@ -466,6 +514,7 @@ Fetching individual orders
{
"code": "ABC12",
"event": "sampleconf",
"status": "p",
"testmode": false,
"secret": "k24fiuwvu8kxz3y1",
+2 -2
View File
@@ -1,10 +1,10 @@
Automated email rules
Scheduled email rules
=====================
Resource description
--------------------
Automated email rules that specify emails that the system will send automatically at a specific point in time, e.g.
Scheduled email rules that specify emails that the system will send automatically at a specific point in time, e.g.
the day of the event.
.. rst-class:: rest-resource-table
+3 -1
View File
@@ -32,11 +32,13 @@ class DiscountSerializer(I18nAwareModelSerializer):
'available_until', 'subevent_mode', 'condition_all_products', 'condition_limit_products',
'condition_apply_to_addons', 'condition_min_count', 'condition_min_value',
'benefit_discount_matching_percent', 'benefit_only_apply_to_cheapest_n_matches',
'condition_ignore_voucher_discounted')
'benefit_same_products', 'benefit_limit_products', 'benefit_apply_to_addons',
'benefit_ignore_voucher_discounted', 'condition_ignore_voucher_discounted')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['condition_limit_products'].queryset = self.context['event'].items.all()
self.fields['benefit_limit_products'].queryset = self.context['event'].items.all()
def validate(self, data):
data = super().validate(data)
+8 -5
View File
@@ -284,11 +284,12 @@ class FailedCheckinSerializer(I18nAwareModelSerializer):
raw_item = serializers.PrimaryKeyRelatedField(queryset=Item.objects.none(), required=False, allow_null=True)
raw_variation = serializers.PrimaryKeyRelatedField(queryset=ItemVariation.objects.none(), required=False, allow_null=True)
raw_subevent = serializers.PrimaryKeyRelatedField(queryset=SubEvent.objects.none(), required=False, allow_null=True)
nonce = serializers.CharField(required=False, allow_null=True)
class Meta:
model = Checkin
fields = ('error_reason', 'error_explanation', 'raw_barcode', 'raw_item', 'raw_variation',
'raw_subevent', 'datetime', 'type', 'position')
'raw_subevent', 'nonce', 'datetime', 'type', 'position')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -614,7 +615,7 @@ class PaymentURLField(serializers.URLField):
def to_representation(self, instance: OrderPayment):
if instance.state != OrderPayment.PAYMENT_STATE_CREATED:
return None
return build_absolute_uri(self.context['event'], 'presale:event.order.pay', kwargs={
return build_absolute_uri(instance.order.event, 'presale:event.order.pay', kwargs={
'order': instance.order.code,
'secret': instance.order.secret,
'payment': instance.pk,
@@ -659,7 +660,7 @@ class OrderRefundSerializer(I18nAwareModelSerializer):
class OrderURLField(serializers.URLField):
def to_representation(self, instance: Order):
return build_absolute_uri(self.context['event'], 'presale:event.order', kwargs={
return build_absolute_uri(instance.event, 'presale:event.order', kwargs={
'order': instance.code,
'secret': instance.secret,
})
@@ -694,6 +695,7 @@ class OrderListSerializer(serializers.ListSerializer):
class OrderSerializer(I18nAwareModelSerializer):
event = SlugRelatedField(slug_field='slug', read_only=True)
invoice_address = InvoiceAddressSerializer(allow_null=True)
positions = OrderPositionSerializer(many=True, read_only=True)
fees = OrderFeeSerializer(many=True, read_only=True)
@@ -709,7 +711,7 @@ class OrderSerializer(I18nAwareModelSerializer):
model = Order
list_serializer_class = OrderListSerializer
fields = (
'code', 'status', 'testmode', 'secret', 'email', 'phone', 'locale', 'datetime', 'expires', 'payment_date',
'code', 'event', 'status', 'testmode', 'secret', 'email', 'phone', 'locale', 'datetime', 'expires', 'payment_date',
'payment_provider', 'fees', 'total', 'comment', 'custom_followup_at', 'invoice_address', 'positions', 'downloads',
'checkin_attention', 'last_modified', 'payments', 'refunds', 'require_approval', 'sales_channel',
'url', 'customer', 'valid_if_pending'
@@ -1593,6 +1595,7 @@ class InlineInvoiceLineSerializer(I18nAwareModelSerializer):
class InvoiceSerializer(I18nAwareModelSerializer):
event = SlugRelatedField(slug_field='slug', read_only=True)
order = serializers.SlugRelatedField(slug_field='code', read_only=True)
refers = serializers.SlugRelatedField(slug_field='full_invoice_no', read_only=True)
lines = InlineInvoiceLineSerializer(many=True)
@@ -1601,7 +1604,7 @@ class InvoiceSerializer(I18nAwareModelSerializer):
class Meta:
model = Invoice
fields = ('order', 'number', 'is_cancellation', 'invoice_from', 'invoice_from_name', 'invoice_from_zipcode',
fields = ('event', 'order', 'number', 'is_cancellation', 'invoice_from', 'invoice_from_name', 'invoice_from_zipcode',
'invoice_from_city', 'invoice_from_country', 'invoice_from_tax_id', 'invoice_from_vat_id',
'invoice_to', 'invoice_to_company', 'invoice_to_name', 'invoice_to_street', 'invoice_to_zipcode',
'invoice_to_city', 'invoice_to_state', 'invoice_to_country', 'invoice_to_vat_id', 'invoice_to_beneficiary',
+8
View File
@@ -94,6 +94,14 @@ class CustomerSerializer(I18nAwareModelSerializer):
data['name_parts']['_scheme'] = self.context['request'].organizer.settings.name_scheme
return data
def validate_email(self, value):
qs = Customer.objects.filter(organizer=self.context['organizer'], email__iexact=value)
if self.instance and self.instance.pk:
qs = qs.exclude(pk=self.instance.pk)
if qs.exists():
raise ValidationError(_("An account with this email address is already registered."))
return value
class CustomerCreateSerializer(CustomerSerializer):
send_email = serializers.BooleanField(default=False, required=False, allow_null=True)
+3 -1
View File
@@ -61,6 +61,8 @@ orga_router.register(r'membershiptypes', organizer.MembershipTypeViewSet)
orga_router.register(r'reusablemedia', media.ReusableMediaViewSet)
orga_router.register(r'teams', organizer.TeamViewSet)
orga_router.register(r'devices', organizer.DeviceViewSet)
orga_router.register(r'orders', order.OrganizerOrderViewSet)
orga_router.register(r'invoices', order.InvoiceViewSet)
orga_router.register(r'exporters', exporters.OrganizerExportersViewSet, basename='exporters')
team_router = routers.DefaultRouter()
@@ -77,7 +79,7 @@ event_router.register(r'questions', item.QuestionViewSet)
event_router.register(r'discounts', discount.DiscountViewSet)
event_router.register(r'quotas', item.QuotaViewSet)
event_router.register(r'vouchers', voucher.VoucherViewSet)
event_router.register(r'orders', order.OrderViewSet)
event_router.register(r'orders', order.EventOrderViewSet)
event_router.register(r'orderpositions', order.OrderPositionViewSet)
event_router.register(r'invoices', order.InvoiceViewSet)
event_router.register(r'revokedsecrets', order.RevokedSecretViewSet, basename='revokedsecrets')
+14 -1
View File
@@ -164,8 +164,21 @@ class CheckinListViewSet(viewsets.ModelViewSet):
secret=serializer.validated_data['raw_barcode']
).first()
clist = self.get_object()
if serializer.validated_data.get('nonce'):
if kwargs.get('position'):
prev = kwargs['position'].all_checkins.filter(nonce=serializer.validated_data['nonce']).first()
else:
prev = clist.checkins.filter(
nonce=serializer.validated_data['nonce'],
raw_barcode=serializer.validated_data['raw_barcode'],
).first()
if prev:
# Ignore because nonce is already handled
return Response(serializer.data, status=201)
c = serializer.save(
list=self.get_object(),
list=clist,
successful=False,
forced=True,
force_sent=True,
+71 -25
View File
@@ -44,6 +44,7 @@ from rest_framework.exceptions import (
APIException, NotFound, PermissionDenied, ValidationError,
)
from rest_framework.mixins import CreateModelMixin
from rest_framework.permissions import SAFE_METHODS
from rest_framework.response import Response
from pretix.api.models import OAuthAccessToken
@@ -185,7 +186,7 @@ with scopes_disabled():
)
class OrderViewSet(viewsets.ModelViewSet):
class OrderViewSetMixin:
serializer_class = OrderSerializer
queryset = Order.objects.none()
filter_backends = (DjangoFilterBackend, TotalOrderingFilter)
@@ -193,19 +194,12 @@ class OrderViewSet(viewsets.ModelViewSet):
ordering_fields = ('datetime', 'code', 'status', 'last_modified')
filterset_class = OrderFilter
lookup_field = 'code'
permission = 'can_view_orders'
write_permission = 'can_change_orders'
def get_serializer_context(self):
ctx = super().get_serializer_context()
ctx['event'] = self.request.event
ctx['pdf_data'] = self.request.query_params.get('pdf_data', 'false') == 'true'
ctx['exclude'] = self.request.query_params.getlist('exclude')
ctx['include'] = self.request.query_params.getlist('include')
return ctx
def get_base_queryset(self):
raise NotImplementedError()
def get_queryset(self):
qs = self.request.event.orders
qs = self.get_base_queryset()
if 'fees' not in self.request.GET.getlist('exclude'):
if self.request.query_params.get('include_canceled_fees', 'false') == 'true':
fqs = OrderFee.all
@@ -227,11 +221,12 @@ class OrderViewSet(viewsets.ModelViewSet):
opq = OrderPosition.all
else:
opq = OrderPosition.objects
if request.query_params.get('pdf_data', 'false') == 'true':
if request.query_params.get('pdf_data', 'false') == 'true' and getattr(request, 'event', None):
prefetch_related_objects([request.organizer], 'meta_properties')
prefetch_related_objects(
[request.event],
Prefetch('meta_values', queryset=EventMetaValue.objects.select_related('property'), to_attr='meta_values_cached'),
Prefetch('meta_values', queryset=EventMetaValue.objects.select_related('property'),
to_attr='meta_values_cached'),
'questions',
'item_meta_properties',
)
@@ -266,13 +261,12 @@ class OrderViewSet(viewsets.ModelViewSet):
)
)
def _get_output_provider(self, identifier):
responses = register_ticket_outputs.send(self.request.event)
for receiver, response in responses:
prov = response(self.request.event)
if prov.identifier == identifier:
return prov
raise NotFound('Unknown output provider.')
def get_serializer_context(self):
ctx = super().get_serializer_context()
ctx['exclude'] = self.request.query_params.getlist('exclude')
ctx['include'] = self.request.query_params.getlist('include')
ctx['pdf_data'] = False
return ctx
@scopes_disabled() # we are sure enough that get_queryset() is correct, so we save some perforamnce
def list(self, request, **kwargs):
@@ -289,6 +283,45 @@ class OrderViewSet(viewsets.ModelViewSet):
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data, headers={'X-Page-Generated': date})
class OrganizerOrderViewSet(OrderViewSetMixin, viewsets.ReadOnlyModelViewSet):
def get_base_queryset(self):
perm = "can_view_orders" if self.request.method in SAFE_METHODS else "can_change_orders"
if isinstance(self.request.auth, (TeamAPIToken, Device)):
return Order.objects.filter(
event__organizer=self.request.organizer,
event__in=self.request.auth.get_events_with_permission(perm)
)
elif self.request.user.is_authenticated:
return Order.objects.filter(
event__organizer=self.request.organizer,
event__in=self.request.user.get_events_with_permission(perm)
)
else:
raise PermissionDenied()
class EventOrderViewSet(OrderViewSetMixin, viewsets.ModelViewSet):
permission = 'can_view_orders'
write_permission = 'can_change_orders'
def get_serializer_context(self):
ctx = super().get_serializer_context()
ctx['event'] = self.request.event
ctx['pdf_data'] = self.request.query_params.get('pdf_data', 'false') == 'true'
return ctx
def get_base_queryset(self):
return self.request.event.orders
def _get_output_provider(self, identifier):
responses = register_ticket_outputs.send(self.request.event)
for receiver, response in responses:
prov = response(self.request.event)
if prov.identifier == identifier:
return prov
raise NotFound('Unknown output provider.')
@action(detail=True, url_name='download', url_path='download/(?P<output>[^/]+)')
def download(self, request, output, **kwargs):
provider = self._get_output_provider(output)
@@ -1782,11 +1815,24 @@ class InvoiceViewSet(viewsets.ReadOnlyModelViewSet):
write_permission = 'can_change_orders'
def get_queryset(self):
return self.request.event.invoices.prefetch_related('lines').select_related('order', 'refers').annotate(
perm = "can_view_orders" if self.request.method in SAFE_METHODS else "can_change_orders"
if getattr(self.request, 'event', None):
qs = self.request.event.invoices
elif isinstance(self.request.auth, (TeamAPIToken, Device)):
qs = Invoice.objects.filter(
event__organizer=self.request.organizer,
event__in=self.request.auth.get_events_with_permission(perm)
)
elif self.request.user.is_authenticated:
qs = Invoice.objects.filter(
event__organizer=self.request.organizer,
event__in=self.request.user.get_events_with_permission(perm)
)
return qs.prefetch_related('lines').select_related('order', 'refers').annotate(
nr=Concat('prefix', 'invoice_no')
)
@action(detail=True, )
@action(detail=True)
def download(self, request, **kwargs):
invoice = self.get_object()
@@ -1805,7 +1851,7 @@ class InvoiceViewSet(viewsets.ReadOnlyModelViewSet):
return resp
@action(detail=True, methods=['POST'])
def regenerate(self, request, **kwarts):
def regenerate(self, request, **kwargs):
inv = self.get_object()
if inv.canceled:
raise ValidationError('The invoice has already been canceled.')
@@ -1815,7 +1861,7 @@ class InvoiceViewSet(viewsets.ReadOnlyModelViewSet):
raise PermissionDenied('The invoice file is no longer stored on the server.')
elif inv.sent_to_organizer:
raise PermissionDenied('The invoice file has already been exported.')
elif now().astimezone(self.request.event.timezone).date() - inv.date > datetime.timedelta(days=1):
elif now().astimezone(inv.event.timezone).date() - inv.date > datetime.timedelta(days=1):
raise PermissionDenied('The invoice file is too old to be regenerated.')
else:
inv = regenerate_invoice(inv)
@@ -1830,7 +1876,7 @@ class InvoiceViewSet(viewsets.ReadOnlyModelViewSet):
return Response(status=204)
@action(detail=True, methods=['POST'])
def reissue(self, request, **kwarts):
def reissue(self, request, **kwargs):
inv = self.get_object()
if inv.canceled:
raise ValidationError('The invoice has already been canceled.')
+5 -2
View File
@@ -134,8 +134,11 @@ class TemplateBasedMailRenderer(BaseHTMLMailRenderer):
def template_name(self):
raise NotImplementedError()
def compile_markdown(self, plaintext):
return markdown_compile_email(plaintext)
def render(self, plain_body: str, plain_signature: str, subject: str, order, position) -> str:
body_md = markdown_compile_email(plain_body)
body_md = self.compile_markdown(plain_body)
htmlctx = {
'site': settings.PRETIX_INSTANCE_NAME,
'site_url': settings.SITE_URL,
@@ -153,7 +156,7 @@ class TemplateBasedMailRenderer(BaseHTMLMailRenderer):
if plain_signature:
signature_md = plain_signature.replace('\n', '<br>\n')
signature_md = markdown_compile_email(signature_md)
signature_md = self.compile_markdown(signature_md)
htmlctx['signature'] = signature_md
if order:
@@ -0,0 +1,34 @@
# Generated by Django 4.2.4 on 2023-08-28 12:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pretixbase", "0244_mediumkeyset"),
]
operations = [
migrations.AddField(
model_name="discount",
name="benefit_apply_to_addons",
field=models.BooleanField(default=True),
),
migrations.AddField(
model_name="discount",
name="benefit_ignore_voucher_discounted",
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name="discount",
name="benefit_limit_products",
field=models.ManyToManyField(
related_name="benefit_discounts", to="pretixbase.item"
),
),
migrations.AddField(
model_name="discount",
name="benefit_same_products",
field=models.BooleanField(default=True),
),
]
+74 -23
View File
@@ -99,7 +99,7 @@ class Discount(LoggedModel):
)
condition_apply_to_addons = models.BooleanField(
default=True,
verbose_name=_("Apply to add-on products"),
verbose_name=_("Count add-on products"),
help_text=_("Discounts never apply to bundled products"),
)
condition_ignore_voucher_discounted = models.BooleanField(
@@ -107,7 +107,7 @@ class Discount(LoggedModel):
verbose_name=_("Ignore products discounted by a voucher"),
help_text=_("If this option is checked, products that already received a discount through a voucher will not "
"be considered for this discount. However, products that use a voucher only to e.g. unlock a "
"hidden product or gain access to sold-out quota will still receive the discount."),
"hidden product or gain access to sold-out quota will still be considered."),
)
condition_min_count = models.PositiveIntegerField(
verbose_name=_('Minimum number of matching products'),
@@ -120,6 +120,19 @@ class Discount(LoggedModel):
default=Decimal('0.00'),
)
benefit_same_products = models.BooleanField(
default=True,
verbose_name=_("Apply discount to same set of products"),
help_text=_("By default, the discount is applied across the same selection of products than the condition for "
"the discount given above. If you want, you can however also select a different selection of "
"products.")
)
benefit_limit_products = models.ManyToManyField(
'Item',
verbose_name=_("Apply discount to specific products"),
related_name='benefit_discounts',
blank=True
)
benefit_discount_matching_percent = models.DecimalField(
verbose_name=_('Percentual discount on matching products'),
decimal_places=2,
@@ -139,6 +152,18 @@ class Discount(LoggedModel):
blank=True,
validators=[MinValueValidator(1)],
)
benefit_apply_to_addons = models.BooleanField(
default=True,
verbose_name=_("Apply to add-on products"),
help_text=_("Discounts never apply to bundled products"),
)
benefit_ignore_voucher_discounted = models.BooleanField(
default=False,
verbose_name=_("Ignore products discounted by a voucher"),
help_text=_("If this option is checked, products that already received a discount through a voucher will not "
"be discounted. However, products that use a voucher only to e.g. unlock a hidden product or gain "
"access to sold-out quota will still receive the discount."),
)
# more feature ideas:
# - max_usages_per_order
@@ -187,6 +212,14 @@ class Discount(LoggedModel):
'on a minimum value.')
)
if data.get('subevent_mode') == cls.SUBEVENT_MODE_DISTINCT and not data.get('benefit_same_products'):
raise ValidationError(
{'benefit_same_products': [
_('You cannot apply the discount to a different set of products if the discount is only valid '
'for bookings of different dates.')
]}
)
def allow_delete(self):
return not self.orderposition_set.exists()
@@ -197,6 +230,7 @@ class Discount(LoggedModel):
'condition_min_value': self.condition_min_value,
'benefit_only_apply_to_cheapest_n_matches': self.benefit_only_apply_to_cheapest_n_matches,
'subevent_mode': self.subevent_mode,
'benefit_same_products': self.benefit_same_products,
})
def is_available_by_time(self, now_dt=None) -> bool:
@@ -207,14 +241,14 @@ class Discount(LoggedModel):
return False
return True
def _apply_min_value(self, positions, idx_group, result):
if self.condition_min_value and sum(positions[idx][2] for idx in idx_group) < self.condition_min_value:
def _apply_min_value(self, positions, condition_idx_group, benefit_idx_group, result):
if self.condition_min_value and sum(positions[idx][2] for idx in condition_idx_group) < self.condition_min_value:
return
if self.condition_min_count or self.benefit_only_apply_to_cheapest_n_matches:
raise ValueError('Validation invariant violated.')
for idx in idx_group:
for idx in benefit_idx_group:
previous_price = positions[idx][2]
new_price = round_decimal(
previous_price * (Decimal('100.00') - self.benefit_discount_matching_percent) / Decimal('100.00'),
@@ -222,8 +256,8 @@ class Discount(LoggedModel):
)
result[idx] = new_price
def _apply_min_count(self, positions, idx_group, result):
if len(idx_group) < self.condition_min_count:
def _apply_min_count(self, positions, condition_idx_group, benefit_idx_group, result):
if len(condition_idx_group) < self.condition_min_count:
return
if not self.condition_min_count or self.condition_min_value:
@@ -233,15 +267,17 @@ class Discount(LoggedModel):
if not self.condition_min_count:
raise ValueError('Validation invariant violated.')
idx_group = sorted(idx_group, key=lambda idx: (positions[idx][2], -idx)) # sort by line_price
condition_idx_group = sorted(condition_idx_group, key=lambda idx: (positions[idx][2], -idx)) # sort by line_price
benefit_idx_group = sorted(benefit_idx_group, key=lambda idx: (positions[idx][2], -idx)) # sort by line_price
# Prevent over-consuming of items, i.e. if our discount is "buy 2, get 1 free", we only
# want to match multiples of 3
consume_idx = idx_group[:len(idx_group) // self.condition_min_count * self.condition_min_count]
benefit_idx = idx_group[:len(idx_group) // self.condition_min_count * self.benefit_only_apply_to_cheapest_n_matches]
n_groups = min(len(condition_idx_group) // self.condition_min_count, len(benefit_idx_group))
consume_idx = condition_idx_group[:n_groups * self.condition_min_count]
benefit_idx = benefit_idx_group[:n_groups * self.benefit_only_apply_to_cheapest_n_matches]
else:
consume_idx = idx_group
benefit_idx = idx_group
consume_idx = condition_idx_group
benefit_idx = benefit_idx_group
for idx in benefit_idx:
previous_price = positions[idx][2]
@@ -276,7 +312,7 @@ class Discount(LoggedModel):
limit_products = {p.pk for p in self.condition_limit_products.all()}
# First, filter out everything not even covered by our product scope
initial_candidates = [
condition_candidates = [
idx
for idx, (item_id, subevent_id, line_price_gross, is_addon_to, voucher_discount) in positions.items()
if (
@@ -286,11 +322,25 @@ class Discount(LoggedModel):
)
]
if self.benefit_same_products:
benefit_candidates = list(condition_candidates)
else:
benefit_products = {p.pk for p in self.benefit_limit_products.all()}
benefit_candidates = [
idx
for idx, (item_id, subevent_id, line_price_gross, is_addon_to, voucher_discount) in positions.items()
if (
item_id in benefit_products and
(self.benefit_apply_to_addons or not is_addon_to) and
(not self.benefit_ignore_voucher_discounted or voucher_discount is None or voucher_discount == Decimal('0.00'))
)
]
if self.subevent_mode == self.SUBEVENT_MODE_MIXED: # also applies to non-series events
if self.condition_min_count:
self._apply_min_count(positions, initial_candidates, result)
self._apply_min_count(positions, condition_candidates, benefit_candidates, result)
else:
self._apply_min_value(positions, initial_candidates, result)
self._apply_min_value(positions, condition_candidates, benefit_candidates, result)
elif self.subevent_mode == self.SUBEVENT_MODE_SAME:
def key(idx):
@@ -299,17 +349,18 @@ class Discount(LoggedModel):
# Build groups of candidates with the same subevent, then apply our regular algorithm
# to each group
_groups = groupby(sorted(initial_candidates, key=key), key=key)
candidate_groups = [list(g) for k, g in _groups]
_groups = groupby(sorted(condition_candidates, key=key), key=key)
candidate_groups = [(k, list(g)) for k, g in _groups]
for g in candidate_groups:
for subevent_id, g in candidate_groups:
benefit_g = [idx for idx in benefit_candidates if positions[idx][1] == subevent_id]
if self.condition_min_count:
self._apply_min_count(positions, g, result)
self._apply_min_count(positions, g, benefit_g, result)
else:
self._apply_min_value(positions, g, result)
self._apply_min_value(positions, g, benefit_g, result)
elif self.subevent_mode == self.SUBEVENT_MODE_DISTINCT:
if self.condition_min_value:
if self.condition_min_value or not self.benefit_same_products:
raise ValueError('Validation invariant violated.')
# Build optimal groups of candidates with distinct subevents, then apply our regular algorithm
@@ -336,7 +387,7 @@ class Discount(LoggedModel):
candidates = []
cardinality = None
for se, l in subevent_to_idx.items():
l = [ll for ll in l if ll in initial_candidates and ll not in current_group]
l = [ll for ll in l if ll in condition_candidates and ll not in current_group]
if cardinality and len(l) != cardinality:
continue
if se not in {positions[idx][1] for idx in current_group}:
@@ -373,5 +424,5 @@ class Discount(LoggedModel):
break
for g in candidate_groups:
self._apply_min_count(positions, g, result)
self._apply_min_count(positions, g, g, result)
return result
+6 -2
View File
@@ -907,14 +907,18 @@ class Event(EventMixin, LoggedModel):
self.items.filter(hidden_if_available_id=oldid).update(hidden_if_available=q)
for d in Discount.objects.filter(event=other).prefetch_related('condition_limit_products'):
items = list(d.condition_limit_products.all())
c_items = list(d.condition_limit_products.all())
b_items = list(d.benefit_limit_products.all())
d.pk = None
d.event = self
d.save(force_insert=True)
d.log_action('pretix.object.cloned')
for i in items:
for i in c_items:
if i.pk in item_map:
d.condition_limit_products.add(item_map[i.pk])
for i in b_items:
if i.pk in item_map:
d.benefit_limit_products.add(item_map[i.pk])
question_map = {}
for q in Question.objects.filter(event=other).prefetch_related('items', 'options'):
+1 -1
View File
@@ -1463,7 +1463,7 @@ class Question(LoggedModel):
(TYPE_PHONENUMBER, _("Phone number")),
)
UNLOCALIZED_TYPES = [TYPE_DATE, TYPE_TIME, TYPE_DATETIME]
ASK_DURING_CHECKIN_UNSUPPORTED = [TYPE_PHONENUMBER]
ASK_DURING_CHECKIN_UNSUPPORTED = []
event = models.ForeignKey(
Event,
+37 -60
View File
@@ -336,12 +336,6 @@ class BasePaymentProvider:
help_text=_('Users will not be able to choose this payment provider after the given date.'),
required=False,
)),
('_availability_start',
RelativeDateField(
label=_('Available from'),
help_text=_('Users will not be able to choose this payment provider before the given date.'),
required=False,
)),
('_total_min',
forms.DecimalField(
label=_('Minimum order total'),
@@ -545,57 +539,40 @@ class BasePaymentProvider:
return form
def _convert_availability_date_to_absolute(self, rel_date, cart_id=None, order=None):
if not rel_date:
return None
# In an event series, we use min() here, which makes it less restrictive than max() and thus makes
# it harder to put one self into a situation where no payment provider is available.
if self.event.has_subevents and cart_id:
dates = [
rel_date.datetime(se).date()
for se in self.event.subevents.filter(
id__in=CartPosition.objects.filter(
cart_id=cart_id, event=self.event
).values_list('subevent', flat=True)
)
]
return min(dates) if dates else None
elif self.event.has_subevents and order:
dates = [
rel_date.datetime(se).date()
for se in self.event.subevents.filter(
id__in=order.positions.values_list('subevent', flat=True)
)
]
return min(dates) if dates else None
elif self.event.has_subevents:
raise NotImplementedError('Payment provider is not subevent-ready.')
else:
return rel_date.datetime(self.event).date()
def _is_available_by_time(self, now_dt=None, cart_id=None, order=None):
def _is_still_available(self, now_dt=None, cart_id=None, order=None):
now_dt = now_dt or now()
tz = ZoneInfo(self.event.settings.timezone)
try:
availability_start = self._convert_availability_date_to_absolute(
self.settings.get('_availability_start', as_type=RelativeDateWrapper), cart_id, order)
availability_date = self.settings.get('_availability_date', as_type=RelativeDateWrapper)
if availability_date:
if self.event.has_subevents and cart_id:
dates = [
availability_date.datetime(se).date()
for se in self.event.subevents.filter(
id__in=CartPosition.objects.filter(
cart_id=cart_id, event=self.event
).values_list('subevent', flat=True)
)
]
availability_date = min(dates) if dates else None
elif self.event.has_subevents and order:
dates = [
availability_date.datetime(se).date()
for se in self.event.subevents.filter(
id__in=order.positions.values_list('subevent', flat=True)
)
]
availability_date = min(dates) if dates else None
elif self.event.has_subevents:
logger.error('Payment provider is not subevent-ready.')
return False
else:
availability_date = availability_date.datetime(self.event).date()
if availability_start:
if availability_start > now_dt.astimezone(tz).date():
return False
if availability_date:
return availability_date >= now_dt.astimezone(tz).date()
availability_end = self._convert_availability_date_to_absolute(
self.settings.get('_availability_date', as_type=RelativeDateWrapper), cart_id, order)
if availability_end:
if availability_end < now_dt.astimezone(tz).date():
return False
return True
except NotImplementedError:
logger.exception('Unable to check availability')
return False
return True
def is_allowed(self, request: HttpRequest, total: Decimal=None) -> bool:
"""
@@ -604,9 +581,9 @@ class BasePaymentProvider:
user will not be able to select this payment method. This will only be called
during checkout, not on retrying.
The default implementation checks for the ``_availability_date`` setting to be either unset or in the future
and for the ``_availability_from``, ``_total_max``, and ``_total_min`` requirements to be met. It also checks
the ``_restrict_countries`` and ``_restrict_to_sales_channels`` setting.
The default implementation checks for the _availability_date setting to be either unset or in the future
and for the _total_max and _total_min requirements to be met. It also checks the ``_restrict_countries``
and ``_restrict_to_sales_channels`` setting.
:param total: The total value without the payment method fee, after taxes.
@@ -615,7 +592,7 @@ class BasePaymentProvider:
The ``total`` parameter has been added. For backwards compatibility, this method is called again
without this parameter if it raises a ``TypeError`` on first try.
"""
timing = self._is_available_by_time(cart_id=get_or_create_cart_id(request))
timing = self._is_still_available(cart_id=get_or_create_cart_id(request))
pricing = True
if (self.settings._total_max is not None or self.settings._total_min is not None) and total is None:
@@ -765,7 +742,7 @@ class BasePaymentProvider:
the amount of money that should be paid.
If you need any special behavior, you can return a string containing the URL the user will be redirected to.
If you are done with your process you should return the user to the order's detail page. Redirection is not
If you are done with your process you should return the user to the order's detail page. Redirection is only
allowed if you set ``execute_payment_needs_user`` to ``True``.
If the payment is completed, you should call ``payment.confirm()``. Please note that this might
@@ -799,8 +776,8 @@ class BasePaymentProvider:
Will be called to check whether it is allowed to change the payment method of
an order to this one.
The default implementation checks for the ``_availability_date`` setting to be either unset or in the future,
as well as for the ``_availabilty_from``, ``_total_max``, ``_total_min``, and ``_restricted_countries`` settings.
The default implementation checks for the _availability_date setting to be either unset or in the future,
as well as for the _total_max, _total_min and _restricted_countries settings.
:param order: The order object
"""
@@ -827,7 +804,7 @@ class BasePaymentProvider:
if order.sales_channel not in self.settings.get('_restrict_to_sales_channels', as_type=list, default=['web']):
return False
return self._is_available_by_time(order=order)
return self._is_still_available(order=order)
def payment_prepare(self, request: HttpRequest, payment: OrderPayment) -> Union[bool, str]:
"""
+1 -1
View File
@@ -886,7 +886,7 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
if isinstance(auth, Device):
device = auth
last_cis = list(op.checkins.order_by('-datetime').filter(list=clist).only('type', 'nonce'))
last_cis = list(op.checkins.order_by('-datetime').filter(list=clist).only('type', 'nonce', 'position_id'))
entry_allowed = (
type == Checkin.TYPE_EXIT or
clist.allow_multiple_entries or
+1 -1
View File
@@ -171,7 +171,7 @@ def apply_discounts(event: Event, sales_channel: str,
Q(available_until__isnull=True) | Q(available_until__gte=now()),
sales_channels__contains=sales_channel,
active=True,
).prefetch_related('condition_limit_products').order_by('position', 'pk')
).prefetch_related('condition_limit_products', 'benefit_limit_products').order_by('position', 'pk')
for discount in discount_qs:
result = discount.apply({
idx: (item_id, subevent_id, line_price_gross, is_addon_to, voucher_discount)
+3 -3
View File
@@ -292,7 +292,7 @@ class LinkifyAndCleanExtension(Extension):
)
def markdown_compile_email(source):
def markdown_compile_email(source, allowed_tags=ALLOWED_TAGS, allowed_attributes=ALLOWED_ATTRIBUTES):
linker = bleach.Linker(
url_re=URL_RE,
email_re=EMAIL_RE,
@@ -306,8 +306,8 @@ def markdown_compile_email(source):
EmailNl2BrExtension(),
LinkifyAndCleanExtension(
linker,
tags=ALLOWED_TAGS,
attributes=ALLOWED_ATTRIBUTES,
tags=allowed_tags,
attributes=allowed_attributes,
protocols=ALLOWED_PROTOCOLS,
strip=False,
)
+10 -1
View File
@@ -50,11 +50,16 @@ class DiscountForm(I18nModelForm):
'condition_ignore_voucher_discounted',
'benefit_discount_matching_percent',
'benefit_only_apply_to_cheapest_n_matches',
'benefit_same_products',
'benefit_limit_products',
'benefit_apply_to_addons',
'benefit_ignore_voucher_discounted',
]
field_classes = {
'available_from': SplitDateTimeField,
'available_until': SplitDateTimeField,
'condition_limit_products': ItemMultipleChoiceField,
'benefit_limit_products': ItemMultipleChoiceField,
}
widgets = {
'subevent_mode': forms.RadioSelect,
@@ -64,11 +69,14 @@ class DiscountForm(I18nModelForm):
'data-inverse-dependency': '<[name$=all_products]',
'class': 'scrolling-multiple-choice',
}),
'benefit_limit_products': forms.CheckboxSelectMultiple(attrs={
'class': 'scrolling-multiple-choice',
}),
'benefit_only_apply_to_cheapest_n_matches': forms.NumberInput(
attrs={
'data-display-dependency': '#id_condition_min_count',
}
)
),
}
def __init__(self, *args, **kwargs):
@@ -85,6 +93,7 @@ class DiscountForm(I18nModelForm):
widget=forms.CheckboxSelectMultiple,
)
self.fields['condition_limit_products'].queryset = self.event.items.all()
self.fields['benefit_limit_products'].queryset = self.event.items.all()
self.fields['condition_min_count'].required = False
self.fields['condition_min_count'].widget.is_required = False
self.fields['condition_min_value'].required = False
+4 -2
View File
@@ -86,12 +86,14 @@ class GlobalSettingsForm(SettingsForm):
('leaflet_tiles', forms.CharField(
required=False,
label=_("Leaflet tiles URL pattern"),
help_text=_("e.g. {sample}").format(sample="https://a.tile.openstreetmap.org/{z}/{x}/{y}.png")
help_text=_("e.g. {sample}").format(sample="https://tile.openstreetmap.org/{z}/{x}/{y}.png")
)),
('leaflet_tiles_attribution', forms.CharField(
required=False,
label=_("Leaflet tiles attribution"),
help_text=_("e.g. {sample}").format(sample='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors')
help_text=_("e.g. {sample}").format(
sample='&copy; &lt;a href=&quot;https://www.openstreetmap.org/copyright&quot;&gt;OpenStreetMap&lt;/a&gt; contributors'
)
)),
])
responses = register_global_settings.send(self)
@@ -48,6 +48,12 @@
</fieldset>
<fieldset>
<legend>{% trans "Benefit" context "discount" %}</legend>
{% bootstrap_field form.benefit_same_products layout="control" %}
<div data-display-dependency="#id_benefit_same_products" data-inverse>
{% bootstrap_field form.benefit_limit_products layout="control" %}
{% bootstrap_field form.benefit_apply_to_addons layout="control" %}
{% bootstrap_field form.benefit_ignore_voucher_discounted layout="control" %}
</div>
{% bootstrap_field form.benefit_discount_matching_percent layout="control" addon_after="%" %}
{% bootstrap_field form.benefit_only_apply_to_cheapest_n_matches layout="control" %}
</fieldset>
+3 -3
View File
@@ -24799,7 +24799,7 @@ msgstr[0] ""
msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr ""
#: pretix/plugins/sendmail/signals.py:122
@@ -24877,7 +24877,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -24924,7 +24924,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -28366,7 +28366,7 @@ msgstr[5] ""
#: pretix/plugins/sendmail/signals.py:99
#, fuzzy
#| msgid "Attendee email"
msgid "Automated emails"
msgid "Scheduled emails"
msgstr "البريد الإلكتروني للحاضر"
#: pretix/plugins/sendmail/signals.py:122
@@ -28460,7 +28460,7 @@ msgstr "إنشاء مستخدم جديد"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -28523,7 +28523,7 @@ msgstr "اختيار البيانات"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
#, fuzzy
#| msgid "Attendee e-mail address"
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr "عنوان البريد الإلكتروني للحاضر"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -28023,7 +28023,7 @@ msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
#, fuzzy
#| msgid "Attendee email"
msgid "Automated emails"
msgid "Scheduled emails"
msgstr "Correu electrònic de l'assistent"
#: pretix/plugins/sendmail/signals.py:122
@@ -28119,7 +28119,7 @@ msgstr "Crear un nou usuari"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -28176,7 +28176,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
#, fuzzy
#| msgid "Attendee e-mail address"
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr "Correu electrònic de l'assistent"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -27060,7 +27060,7 @@ msgstr[1] ""
msgstr[2] ""
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr ""
#: pretix/plugins/sendmail/signals.py:122
@@ -27138,7 +27138,7 @@ msgstr "Vytvořit e-mailové pravidlo"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -27185,7 +27185,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -24800,7 +24800,7 @@ msgstr[0] ""
msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr ""
#: pretix/plugins/sendmail/signals.py:122
@@ -24878,7 +24878,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -24925,7 +24925,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -27171,7 +27171,7 @@ msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
#, fuzzy
msgid "Automated emails"
msgid "Scheduled emails"
msgstr "E-mailadresse"
#: pretix/plugins/sendmail/signals.py:122
@@ -27258,7 +27258,7 @@ msgstr "Opret en ny bruger"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -27311,7 +27311,7 @@ msgstr "intet valgt"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
#, fuzzy
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr "E-mailadresse"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+6 -6
View File
@@ -28103,8 +28103,8 @@ msgstr[0] "%(count)d Tag vor Veranstaltungsbeginn um %(time)s"
msgstr[1] "%(count)d Tage vor Veranstaltungsbeginn um %(time)s"
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgstr "Automatisierte E-Mails"
msgid "Scheduled emails"
msgstr "Geplante E-Mails"
#: pretix/plugins/sendmail/signals.py:122
msgid "Mass email was sent to customers or attendees."
@@ -28183,9 +28183,9 @@ msgstr "Neue Regel erstellen"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
"Automatische E-Mails werden nicht verschickt, solange Ihr Ticketshop offline "
"Geplante E-Mails werden nicht verschickt, solange Ihr Ticketshop offline "
"ist."
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -28236,8 +28236,8 @@ msgstr "Letzte Berechnung"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgstr "Regeln für automatisierte E-Mails"
msgid "Scheduled email rules"
msgstr "Regeln für geplante E-Mails"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
msgid ""
@@ -28055,8 +28055,8 @@ msgstr[0] "%(count)d Tag vor Veranstaltungsbeginn um %(time)s"
msgstr[1] "%(count)d Tage vor Veranstaltungsbeginn um %(time)s"
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgstr "Automatisierte E-Mails"
msgid "Scheduled emails"
msgstr "Geplante E-Mails"
#: pretix/plugins/sendmail/signals.py:122
msgid "Mass email was sent to customers or attendees."
@@ -28135,9 +28135,9 @@ msgstr "Neue Regel erstellen"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
"Automatische E-Mails werden nicht verschickt, solange Ihr Ticketshop offline "
"Geplante E-Mails werden nicht verschickt, solange Ihr Ticketshop offline "
"ist."
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -28188,8 +28188,8 @@ msgstr "Letzte Berechnung"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgstr "Regeln für automatisierte E-Mails"
msgid "Scheduled email rules"
msgstr "Regeln für geplante E-Mails"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
msgid ""
+3 -3
View File
@@ -24800,7 +24800,7 @@ msgstr[0] ""
msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr ""
#: pretix/plugins/sendmail/signals.py:122
@@ -24878,7 +24878,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -24925,7 +24925,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -29895,7 +29895,7 @@ msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
#, fuzzy
#| msgid "Attendee email"
msgid "Automated emails"
msgid "Scheduled emails"
msgstr "Email συμμετεχόντος"
#: pretix/plugins/sendmail/signals.py:122
@@ -29994,7 +29994,7 @@ msgstr "Δημιουργία νέου χρήστη"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -30060,7 +30060,7 @@ msgstr "Επιλογή δεδομένων"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
#, fuzzy
#| msgid "Attendee email"
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr "Email συμμετεχόντος"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -24799,7 +24799,7 @@ msgstr[0] ""
msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr ""
#: pretix/plugins/sendmail/signals.py:122
@@ -24877,7 +24877,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -24924,7 +24924,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -29453,7 +29453,7 @@ msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
#, fuzzy
#| msgid "Attendee email"
msgid "Automated emails"
msgid "Scheduled emails"
msgstr "Correo electrónico del participante"
#: pretix/plugins/sendmail/signals.py:122
@@ -29556,7 +29556,7 @@ msgstr "Crear un nuevo usuario"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -29617,7 +29617,7 @@ msgstr "Selección de datos"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
#, fuzzy
#| msgid "Attendee email"
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr "Correo electrónico del participante"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -25621,7 +25621,7 @@ msgstr[0] ""
msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr ""
#: pretix/plugins/sendmail/signals.py:122
@@ -25700,7 +25700,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -25747,7 +25747,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -28317,7 +28317,7 @@ msgstr[0] "%(count)d jour avant le début de l'événement à %(time)s"
msgstr[1] "%(count)d jours avant le début de l'événement à %(time)s"
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr "E-mails automatisés"
#: pretix/plugins/sendmail/signals.py:122
@@ -28398,7 +28398,7 @@ msgstr "Créer une règle de-mail"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
"Les e-mails automatisés ne sont pas envoyés tant que votre billetterie est "
"hors ligne."
@@ -28451,7 +28451,7 @@ msgstr "Calcul du dernier planning"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr "Règles de-mails automatiques"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -30019,7 +30019,7 @@ msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
#, fuzzy
msgid "Automated emails"
msgid "Scheduled emails"
msgstr "Correo electrónico del participante"
#: pretix/plugins/sendmail/signals.py:122
@@ -30113,7 +30113,7 @@ msgstr "Crear un nuevo usuario"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -30168,7 +30168,7 @@ msgstr "Selección de datos"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
#, fuzzy
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr "Correo electrónico del participante"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -24825,7 +24825,7 @@ msgstr[0] ""
msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr ""
#: pretix/plugins/sendmail/signals.py:122
@@ -24903,7 +24903,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -24950,7 +24950,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -24809,7 +24809,7 @@ msgstr[0] ""
msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr ""
#: pretix/plugins/sendmail/signals.py:122
@@ -24887,7 +24887,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -24934,7 +24934,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -25417,7 +25417,7 @@ msgstr[0] ""
msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr ""
#: pretix/plugins/sendmail/signals.py:122
@@ -25503,7 +25503,7 @@ msgstr "Kifizetett megrendelések"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -25552,7 +25552,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -24799,7 +24799,7 @@ msgstr[0] ""
msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr ""
#: pretix/plugins/sendmail/signals.py:122
@@ -24877,7 +24877,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -24924,7 +24924,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -25888,7 +25888,7 @@ msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
#, fuzzy
msgid "Automated emails"
msgid "Scheduled emails"
msgstr "Email partecipante"
#: pretix/plugins/sendmail/signals.py:122
@@ -25978,7 +25978,7 @@ msgstr "Data di creazione"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -26028,7 +26028,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
#, fuzzy
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr "Email partecipante"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -25094,7 +25094,7 @@ msgid_plural "%(count)d days before event start at %(time)s"
msgstr[0] ""
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr ""
#: pretix/plugins/sendmail/signals.py:122
@@ -25172,7 +25172,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -25219,7 +25219,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -24800,7 +24800,7 @@ msgstr[0] ""
msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr ""
#: pretix/plugins/sendmail/signals.py:122
@@ -24878,7 +24878,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -24925,7 +24925,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -24801,7 +24801,7 @@ msgstr[0] ""
msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr ""
#: pretix/plugins/sendmail/signals.py:122
@@ -24879,7 +24879,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -24926,7 +24926,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -26285,7 +26285,7 @@ msgstr[2] ""
#: pretix/plugins/sendmail/signals.py:99
#, fuzzy
#| msgid "Attendee email"
msgid "Automated emails"
msgid "Scheduled emails"
msgstr "Apmeklētāja e-pasts"
#: pretix/plugins/sendmail/signals.py:122
@@ -26365,7 +26365,7 @@ msgstr "Datums un laiks"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -26422,7 +26422,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
#, fuzzy
#| msgid "Attendee email"
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr "Apmeklētāja e-pasts"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -24799,7 +24799,7 @@ msgstr[0] ""
msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr ""
#: pretix/plugins/sendmail/signals.py:122
@@ -24877,7 +24877,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -24924,7 +24924,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
@@ -25337,7 +25337,7 @@ msgstr[0] ""
msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr ""
#: pretix/plugins/sendmail/signals.py:122
@@ -25419,7 +25419,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -25466,7 +25466,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+25 -44
View File
@@ -7,7 +7,7 @@ msgstr ""
"Project-Id-Version: 1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-27 11:49+0000\n"
"PO-Revision-Date: 2023-08-24 04:00+0000\n"
"PO-Revision-Date: 2023-08-25 04:00+0000\n"
"Last-Translator: Alain <alain@waag.org>\n"
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix/nl/>"
"\n"
@@ -411,7 +411,7 @@ msgstr "Bestelling is verlopen"
#: pretix/api/webhooks.py:234
msgid "Order expiry date changed"
msgstr "Verloopdatum aangepast."
msgstr "Verloopdatum aangepast"
#: pretix/api/webhooks.py:238 pretix/base/notifications.py:269
msgid "Order information changed"
@@ -7045,38 +7045,30 @@ msgstr ""
"door u gekozen hoeveelheid. Zie hieronder voor de details."
#: pretix/base/services/cart.py:114
#, fuzzy, python-format
#| msgid "You cannot select more than %s items per order."
#, python-format
msgid "You cannot select more than %s item per order."
msgid_plural "You cannot select more than %s items per order."
msgstr[0] "U kunt niet meer dan %s items per bestelling kiezen."
msgstr[0] "U kunt niet meer dan %s item per bestelling kiezen."
msgstr[1] "U kunt niet meer dan %s items per bestelling kiezen."
#: pretix/base/services/cart.py:118 pretix/base/services/orders.py:1468
#, fuzzy, python-format
#| msgid ""
#| "You cannot select more than %(max)s items of the product %(product)s."
#, python-format
msgid "You cannot select more than %(max)s item of the product %(product)s."
msgid_plural ""
"You cannot select more than %(max)s items of the product %(product)s."
msgstr[0] "U kunt niet meer dan %(max)s items van product %(product)s kiezen."
msgstr[0] "U kunt niet meer dan %(max)s item van product %(product)s kiezen."
msgstr[1] "U kunt niet meer dan %(max)s items van product %(product)s kiezen."
#: pretix/base/services/cart.py:123 pretix/base/services/orders.py:1473
#, fuzzy, python-format
#| msgid ""
#| "You need to select at least %(min)s items of the product %(product)s."
#, python-format
msgid "You need to select at least %(min)s item of the product %(product)s."
msgid_plural ""
"You need to select at least %(min)s items of the product %(product)s."
msgstr[0] "U moet ten minste %(min)s items van product %(product)s kiezen."
msgstr[0] "U moet ten minste %(min)s item van product %(product)s kiezen."
msgstr[1] "U moet ten minste %(min)s items van product %(product)s kiezen."
#: pretix/base/services/cart.py:128
#, fuzzy, python-format
#| msgid ""
#| "We removed %(product)s from your cart as you can not buy less than "
#| "%(min)s items of it."
#, python-format
msgid ""
"We removed %(product)s from your cart as you can not buy less than %(min)s "
"item of it."
@@ -7085,10 +7077,10 @@ msgid_plural ""
"items of it."
msgstr[0] ""
"We hebben %(product)s uit uw winkelwagen verwijderd, omdat u niet minder dan "
"%(min)s ervan kunt kopen."
"%(min)s item ervan kunt kopen."
msgstr[1] ""
"We hebben %(product)s uit uw winkelwagen verwijderd, omdat u niet minder dan "
"%(min)s ervan kunt kopen."
"%(min)s items ervan kunt kopen."
#: pretix/base/services/cart.py:132 pretix/base/services/orders.py:146
#: pretix/presale/templates/pretixpresale/event/index.html:157
@@ -7250,10 +7242,7 @@ msgid "You can not select two variations of the same add-on product."
msgstr "U kunt niet twee varianten van hetzelfde add-on-product selecteren."
#: pretix/base/services/cart.py:185 pretix/base/services/orders.py:184
#, fuzzy, python-format
#| msgid ""
#| "You can select at most %(max)s add-ons from the category %(cat)s for the "
#| "product %(base)s."
#, python-format
msgid ""
"You can select at most %(max)s add-on from the category %(cat)s for the "
"product %(base)s."
@@ -7261,17 +7250,14 @@ msgid_plural ""
"You can select at most %(max)s add-ons from the category %(cat)s for the "
"product %(base)s."
msgstr[0] ""
"U kunt maximaal %(max)s add-ons van de categorie %(cat)s selecteren voor het "
"U kunt maximaal %(max)s add-on van de categorie %(cat)s selecteren voor het "
"product %(base)s."
msgstr[1] ""
"U kunt maximaal %(max)s add-ons van de categorie %(cat)s selecteren voor het "
"product %(base)s."
#: pretix/base/services/cart.py:190 pretix/base/services/orders.py:189
#, fuzzy, python-format
#| msgid ""
#| "You need to select at least %(min)s add-ons from the category %(cat)s for "
#| "the product %(base)s."
#, python-format
msgid ""
"You need to select at least %(min)s add-on from the category %(cat)s for the "
"product %(base)s."
@@ -7279,7 +7265,7 @@ msgid_plural ""
"You need to select at least %(min)s add-ons from the category %(cat)s for "
"the product %(base)s."
msgstr[0] ""
"U moet minimaal %(min)s add-ons van de categorie %(cat)s selecteren voor het "
"U moet minimaal %(min)s add-on van de categorie %(cat)s selecteren voor het "
"product %(base)s."
msgstr[1] ""
"U moet minimaal %(min)s add-ons van de categorie %(cat)s selecteren voor het "
@@ -7444,16 +7430,14 @@ msgid "This ticket has been blocked."
msgstr "Dit ticket werd reeds eenmaal gebruikt."
#: pretix/base/services/checkin.py:781 pretix/base/services/checkin.py:785
#, fuzzy, python-brace-format
#| msgid "Only allowed after {datetime}"
#, python-brace-format
msgid "This ticket is only valid after {datetime}."
msgstr "Alleen toegestaan vanaf {datetime}"
msgstr "Dit ticket is geldig vanaf {datetime}."
#: pretix/base/services/checkin.py:795 pretix/base/services/checkin.py:799
#, fuzzy, python-brace-format
#| msgid "This ticket has already been redeemed."
#, python-brace-format
msgid "This ticket was only valid before {datetime}."
msgstr "Dit ticket is al gebruikt."
msgstr "Dit ticket was geldig vòòr {datetime}."
#: pretix/base/services/checkin.py:830
msgid "This order position has an invalid product for this check-in list."
@@ -7514,10 +7498,8 @@ msgid "Export failed"
msgstr "Geëxporteerde bestanden"
#: pretix/base/services/export.py:206
#, fuzzy
#| msgid "Permission denied"
msgid "Permission denied."
msgstr "Geen toestemming"
msgstr "Geen toestemming."
#: pretix/base/services/export.py:221
msgid "Your exported data exceeded the size limit for scheduled exports."
@@ -7548,11 +7530,10 @@ msgstr ""
"{country}"
#: pretix/base/services/invoices.py:220 pretix/base/services/invoices.py:257
#, fuzzy, python-brace-format
#| msgid "Event location"
#, python-brace-format
msgctxt "invoice"
msgid "Event location: {location}"
msgstr "Evenementlocatie"
msgstr "Evenementlocatie: {location}"
#: pretix/base/services/invoices.py:236
#, python-brace-format
@@ -28498,7 +28479,7 @@ msgstr[0] "%(count)d dag voor de start van het evenement om %(time)s"
msgstr[1] "%(count)d dagen voor de start van het evenement om %(time)s"
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr "Automatische e-mails"
#: pretix/plugins/sendmail/signals.py:122
@@ -28586,7 +28567,7 @@ msgstr "Maak e-mailregel"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -28641,7 +28622,7 @@ msgstr "Laatst uitgevoerd"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr "Automatische e-mailregels"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
@@ -24801,7 +24801,7 @@ msgstr[0] ""
msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr ""
#: pretix/plugins/sendmail/signals.py:122
@@ -24879,7 +24879,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -24926,7 +24926,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
@@ -28783,7 +28783,7 @@ msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
#, fuzzy
#| msgid "Attendee email"
msgid "Automated emails"
msgid "Scheduled emails"
msgstr "E-mailadres van gast"
#: pretix/plugins/sendmail/signals.py:122
@@ -28877,7 +28877,7 @@ msgstr "Maak een nieuwe gebruiker aan"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -28941,7 +28941,7 @@ msgstr "Datakeuze"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
#, fuzzy
#| msgid "Attendee e-mail address"
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr "E-mailadres van gast"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -26566,7 +26566,7 @@ msgstr[2] ""
#: pretix/plugins/sendmail/signals.py:99
#, fuzzy
#| msgid "Attendee email"
msgid "Automated emails"
msgid "Scheduled emails"
msgstr "Adres email uczestnika"
#: pretix/plugins/sendmail/signals.py:122
@@ -26660,7 +26660,7 @@ msgstr "Data stworzenia"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -26711,7 +26711,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
#, fuzzy
#| msgid "Attendee email"
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr "Adres email uczestnika"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
@@ -24820,7 +24820,7 @@ msgstr[1] ""
msgstr[2] ""
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr ""
#: pretix/plugins/sendmail/signals.py:122
@@ -24898,7 +24898,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -24945,7 +24945,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -25093,7 +25093,7 @@ msgstr[0] ""
msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr ""
#: pretix/plugins/sendmail/signals.py:122
@@ -25171,7 +25171,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -25218,7 +25218,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
@@ -27358,7 +27358,7 @@ msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
#, fuzzy
#| msgid "Attendee email"
msgid "Automated emails"
msgid "Scheduled emails"
msgstr "E-mail do participante"
#: pretix/plugins/sendmail/signals.py:122
@@ -27452,7 +27452,7 @@ msgstr "Tipo de dispositivo"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -27506,7 +27506,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
#, fuzzy
#| msgid "Attendee email"
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr "E-mail do participante"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
@@ -28193,7 +28193,7 @@ msgstr[0] "%(count)d dia antes do início do evento às %(time)s"
msgstr[1] "%(count)d dias antes do início do evento às %(time)s"
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr "E -mails automatizados"
#: pretix/plugins/sendmail/signals.py:122
@@ -28275,7 +28275,7 @@ msgstr "Crie regra de email"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -28326,7 +28326,7 @@ msgstr "Última programação Computação"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr "Regras de email automatizadas"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -28777,7 +28777,7 @@ msgstr[1] "cu %(count)d zile înainte de începerea evenimentului la %(time)s"
msgstr[2] "cu %(count)d zile înainte de începerea evenimentului la %(time)s"
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr "E-mailuri automate"
#: pretix/plugins/sendmail/signals.py:122
@@ -28861,7 +28861,7 @@ msgstr "Creați o regulă de e-mail"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -28914,7 +28914,7 @@ msgstr "Calculul ultimului program"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr "Reguli automatizate de e-mail"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -27155,7 +27155,7 @@ msgstr[2] ""
#: pretix/plugins/sendmail/signals.py:99
#, fuzzy
#| msgid "Attendee email"
msgid "Automated emails"
msgid "Scheduled emails"
msgstr "Адрес электронной почты посетителя"
#: pretix/plugins/sendmail/signals.py:122
@@ -27241,7 +27241,7 @@ msgstr "Дата и время"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -27297,7 +27297,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
#, fuzzy
#| msgid "Attendee email"
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr "Адрес электронной почты посетителя"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -24872,7 +24872,7 @@ msgstr[0] ""
msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr ""
#: pretix/plugins/sendmail/signals.py:122
@@ -24950,7 +24950,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -24997,7 +24997,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -26953,7 +26953,7 @@ msgstr[3] ""
#: pretix/plugins/sendmail/signals.py:99
#, fuzzy
#| msgid "Attendee email"
msgid "Automated emails"
msgid "Scheduled emails"
msgstr "Email udeleženca"
#: pretix/plugins/sendmail/signals.py:122
@@ -27041,7 +27041,7 @@ msgstr "Datum ustvarjenja"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -27094,7 +27094,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
#, fuzzy
#| msgid "Attendee e-mail address"
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr "E-poštni naslov udeleženca"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -26970,7 +26970,7 @@ msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
#, fuzzy
#| msgid "Attendee email"
msgid "Automated emails"
msgid "Scheduled emails"
msgstr "Epost till deltagare"
#: pretix/plugins/sendmail/signals.py:122
@@ -27056,7 +27056,7 @@ msgstr "Avbryt beställning"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -27107,7 +27107,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
#, fuzzy
#| msgid "Attendee email"
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr "Epost till deltagare"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -24802,7 +24802,7 @@ msgstr[0] ""
msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr ""
#: pretix/plugins/sendmail/signals.py:122
@@ -24880,7 +24880,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -24927,7 +24927,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -29881,7 +29881,7 @@ msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
#, fuzzy
#| msgid "Attendee email"
msgid "Automated emails"
msgid "Scheduled emails"
msgstr "Katılımcı e-postası"
#: pretix/plugins/sendmail/signals.py:122
@@ -29986,7 +29986,7 @@ msgstr "Yeni bir kullanıcı oluştur"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -30050,7 +30050,7 @@ msgstr "Veri seçimi"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
#, fuzzy
#| msgid "Attendee email"
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr "Katılımcı e-postası"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -27634,7 +27634,7 @@ msgstr[1] ""
msgstr[2] ""
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr "Автоматизовані електронні листи"
#: pretix/plugins/sendmail/signals.py:122
@@ -27718,7 +27718,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -27768,7 +27768,7 @@ msgstr "Останній розрахунок графіка"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+3 -3
View File
@@ -24809,7 +24809,7 @@ msgstr[0] ""
msgstr[1] ""
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr ""
#: pretix/plugins/sendmail/signals.py:122
@@ -24887,7 +24887,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -24934,7 +24934,7 @@ msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
@@ -28499,7 +28499,7 @@ msgstr[0] ""
#: pretix/plugins/sendmail/signals.py:99
#, fuzzy
#| msgid "Attendee email"
msgid "Automated emails"
msgid "Scheduled emails"
msgstr "观众Email"
#: pretix/plugins/sendmail/signals.py:122
@@ -28604,7 +28604,7 @@ msgstr "创建一个新用户"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr ""
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -28667,7 +28667,7 @@ msgstr "数据选择"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
#, fuzzy
#| msgid "Attendee email"
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr "观众Email"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
+108 -201
View File
@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-27 11:49+0000\n"
"PO-Revision-Date: 2023-06-28 06:00+0000\n"
"Last-Translator: Yucheng Lin <yuchenglinedu@gmail.com>\n"
"PO-Revision-Date: 2023-08-31 07:34+0000\n"
"Last-Translator: Martin Gross <gross@rami.io>\n"
"Language-Team: Chinese (Traditional) <https://translate.pretix.eu/projects/"
"pretix/pretix/zh_Hant/>\n"
"Language: zh_Hant\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Weblate 4.17\n"
"X-Generator: Weblate 4.18.2\n"
#: pretix/_base_settings.py:78
msgid "English"
@@ -170,7 +170,7 @@ msgstr "目標URL"
#: pretix/api/models.py:113 pretix/base/models/devices.py:123
#: pretix/base/models/organizer.py:264
msgid "All events (including newly created ones)"
msgstr "所有事件(包括新建立的事件"
msgstr "所有活動(包括新建立的活動"
#: pretix/api/models.py:114 pretix/base/models/devices.py:124
#: pretix/base/models/organizer.py:265
@@ -4004,10 +4004,10 @@ msgstr "要此產品,使用者需要直接或透過額度,購買適用於此
#: pretix/base/models/items.py:507
msgid ""
"If this product is part of an order, the order will be put into an "
"\"approval\" state and will need to be confirmed by you before it can be "
"paid and completed. You can use this e.g. for discounted tickets that are "
"only available to specific groups."
"If this product is part of an order, the order will be put into an \"approval"
"\" state and will need to be confirmed by you before it can be paid and "
"completed. You can use this e.g. for discounted tickets that are only "
"available to specific groups."
msgstr ""
"如果此產品是訂單的一部分,則該訂單將進入“批准”狀態,需要你確認才能付款和完"
"成。例如,你可以將其用於僅適用於特定團體的折扣門票。"
@@ -5876,24 +5876,18 @@ msgid "Ambiguous option selected."
msgstr "選擇不明確的選項。"
#: pretix/base/orderimport.py:845
#, fuzzy
#| msgid "No matching seat was found."
msgid "No matching customer was found."
msgstr "未找到符合的座位。"
msgstr "未找到符合的客戶。"
#: pretix/base/payment.py:86
#, fuzzy
#| msgid "Apply"
msgctxt "payment"
msgid "Apple Pay"
msgstr "應用"
msgstr "Apple Pay"
#: pretix/base/payment.py:87
#, fuzzy
#| msgid "Android (Google Play)"
msgctxt "payment"
msgid "Google Pay"
msgstr "安卓(Google Play"
msgstr "安卓(Google Pay"
#: pretix/base/payment.py:256
#: pretix/presale/templates/pretixpresale/event/order.html:115
@@ -5955,13 +5949,13 @@ msgstr "根據包括費用在內的總價值計算費用。"
#, python-brace-format
msgid ""
"We recommend to enable this if you want your users to pay the payment fees "
"of your payment provider. <a href=\"{docs_url}\" target=\"_blank\" "
"rel=\"noopener\">Click here for detailed information on what this does.</a> "
"of your payment provider. <a href=\"{docs_url}\" target=\"_blank\" rel="
"\"noopener\">Click here for detailed information on what this does.</a> "
"Don't forget to set the correct fees above!"
msgstr ""
"如果你想要你的使用者付支付提供者的手續費,我們建議你啟用。<a "
"href=\"{docs_url}\" target=\"_blank\" rel=\"noopener\">點這邊來取得所代表意義"
"的詳細資訊。</a>請不要忘記在上面設定正確的手續費!"
"如果你想要你的使用者付支付提供者的手續費,我們建議你啟用。<a href="
"\"{docs_url}\" target=\"_blank\" rel=\"noopener\">點這邊來取得所代表意義的詳"
"細資訊。</a>請不要忘記在上面設定正確的手續費!"
#: pretix/base/payment.py:388
msgid "Text on invoices"
@@ -6424,16 +6418,12 @@ msgid "List of Add-Ons"
msgstr "附加組件清單"
#: pretix/base/pdf.py:364
#, fuzzy
#| msgid ""
#| "Add-on 1\n"
#| "Add-on 2"
msgid ""
"Add-on 1\n"
"2x Add-on 2"
msgstr ""
"附加1\n"
"附加2"
"2x附加2"
#: pretix/base/pdf.py:370 pretix/control/forms/filter.py:1275
#: pretix/control/forms/filter.py:1277
@@ -7677,8 +7667,6 @@ msgstr "禮品卡貨幣"
#: pretix/base/settings.py:277
#, fuzzy
#| msgid ""
#| "Automatically create a new gift card if a previously unknown chip is seen"
msgid "Automatically create a new gift card if a new chip is encoded"
msgstr "如果看到以前未知的晶片,則自動建立新的禮品卡"
@@ -7974,8 +7962,9 @@ msgid ""
msgstr ""
"這將附加到發票編號的前面。如果將此欄位留空,則將使用該活動 Slug值和破折號。注"
"意:如果同一組織內的多個活動在此欄位中使用相同的值,它們將共用其編號範圍,即"
"每個完整數位最多在您的所有事件中使用一次。此設置僅影響將來的帳單。可以使用 %Y"
"(帶世紀)%y(不帶世紀)插入發票的年份,或使用 %m 和 %d 插入月份中的某一天。"
"每個完整數位最多在您的所有活動中使用一次。此設置僅影響將來的帳單。可以使用 "
"%Y(帶世紀)%y(不帶世紀)插入發票的年份,或使用 %m 和 %d 插入月份中的某一"
"天。"
#: pretix/base/settings.py:682 pretix/base/settings.py:704
#, python-brace-format
@@ -8141,7 +8130,6 @@ msgstr ""
#: pretix/base/settings.py:942
#, fuzzy
#| msgid "Expiration date"
msgid "Expiration delay"
msgstr "過期日期"
@@ -9176,12 +9164,12 @@ msgstr "新訂單附件"
#, python-brace-format
msgid ""
"This file will be attached to the first email that we send for every new "
"order. Therefore it will be combined with the \"Placed order\", \"Free "
"order\", or \"Received order\" texts from above. It will be sent to both "
"order contacts and attendees. You can use this e.g. to send your terms of "
"service. Do not use it to send non-public information as this file might be "
"sent before payment is confirmed or the order is approved. To avoid this "
"vital email going to spam, you can only upload PDF files of up to {size} MB."
"order. Therefore it will be combined with the \"Placed order\", \"Free order"
"\", or \"Received order\" texts from above. It will be sent to both order "
"contacts and attendees. You can use this e.g. to send your terms of service. "
"Do not use it to send non-public information as this file might be sent "
"before payment is confirmed or the order is approved. To avoid this vital "
"email going to spam, you can only upload PDF files of up to {size} MB."
msgstr ""
"此檔將附加到我們為每個新訂單發送的第一封電子郵件中。因此,該檔案將與上面的"
"「已下訂單」、「免費訂單」或「已收到訂單」文本相結合。檔案將同時發送給訂單聯"
@@ -9410,25 +9398,12 @@ msgstr ""
"你的{event} 團隊"
#: pretix/base/settings.py:2349
#, fuzzy, python-brace-format
#| msgid "Payment received for your order: {code}"
#, python-brace-format
msgid "Payment failed for your order: {code}"
msgstr "收到的訂單付款:{code}"
msgstr "訂單付款失敗{code}"
#: pretix/base/settings.py:2353
#, fuzzy, python-brace-format
#| msgid ""
#| "Hello,\n"
#| "\n"
#| "we did not yet receive a full payment for your order for {event}.\n"
#| "Please keep in mind that we only guarantee your order if we receive\n"
#| "your payment before {expire_date}.\n"
#| "\n"
#| "You can view the payment information and the status of your order at\n"
#| "{url}\n"
#| "\n"
#| "Best regards, \n"
#| "Your {event} team"
#, python-brace-format
msgid ""
"Hello,\n"
"\n"
@@ -9446,11 +9421,12 @@ msgid ""
msgstr ""
"你好\n"
"\n"
"我們尚未收到你的 {event} 訂單的全額付款。\n"
"請記住,我們僅在收到時保證你的訂單\n"
"你在 {expire_date} 之前的付款。\n"
"你在 {event} 訂單付款未能成功。\n"
"\n"
"你可以在以下位置查看付款資訊與訂單狀態:\n"
"您的訂單仍然有效,您可以嘗試使用相同或不同的付款方式再次進行支付,唯請在 "
"{expire_date} 前完成付款程序。\n"
"\n"
"您可以重新嘗試付款,並在以下網址檢視您的訂單狀態:\n"
"{url}\n"
"\n"
"敬此\n"
@@ -9786,7 +9762,7 @@ msgstr ""
#: pretix/base/settings.py:2586
#, python-brace-format
msgid "Confirm email address for your account at {organizer}"
msgstr "在{organizer}}確認你帳戶的電子郵件位址"
msgstr "在{organizer}確認你帳戶的電子郵件位址"
#: pretix/base/settings.py:2590
#, python-brace-format
@@ -10576,7 +10552,7 @@ msgstr "如果這需要超過幾分鐘的時間,請刷新此頁面或與我們
#: pretix/base/templates/pretixbase/email/email_footer.html:3
#, python-format
msgid "powered by <a %(a_attr)s>pretix</a>"
msgstr "由%(a_attr)spretix 提供支援</a>"
msgstr "由 <%(a_attr)s>pretix 提供支援</a>"
#: pretix/base/templates/pretixbase/email/export_failed.txt:2
msgid "Your export failed."
@@ -13568,7 +13544,7 @@ msgstr "已為位置 #{posid} 生成一個新密鑰。"
#, python-brace-format
msgid ""
"The validity start date for position #{posid} has been changed to {value}."
msgstr "位置 #{posid} 的有效開始日期已更改為{value}"
msgstr "位置 #{posid} 的有效開始日期已更改為{value}"
#: pretix/control/logdisplay.py:171
#, python-brace-format
@@ -13846,10 +13822,8 @@ msgid "The medium has been connected to a new ticket."
msgstr "媒體已連接到新票證。"
#: pretix/control/logdisplay.py:371
#, fuzzy
#| msgid "The medium has been connected to a new ticket."
msgid "The medium has been connected to a new gift card."
msgstr "媒體已連接到新票證。"
msgstr "媒體已連接到新的禮品卡。"
#: pretix/control/logdisplay.py:372 pretix/control/logdisplay.py:413
msgid "Sending of an email has failed."
@@ -14085,11 +14059,8 @@ msgid ""
msgstr "包含訂單詳細資訊頁面連結的電子郵件已重新發送給使用者。"
#: pretix/control/logdisplay.py:436
#, fuzzy
#| msgid ""
#| "An email has been sent to notify the user that payment has been received."
msgid "An email has been sent to notify the user that the payment failed."
msgstr "已發送一封電子郵件通知使用者已收到付款。"
msgstr "已發送一封電子郵件通知使用者未能成功付款。"
#: pretix/control/logdisplay.py:437
#, python-brace-format
@@ -16788,10 +16759,8 @@ msgid "Payment reminder"
msgstr "付款提醒"
#: pretix/control/templates/pretixcontrol/event/mail.html:108
#, fuzzy
#| msgid "Payment fee"
msgid "Payment failed"
msgstr "支付費用"
msgstr "支付失敗"
#: pretix/control/templates/pretixcontrol/event/mail.html:111
msgid "Waiting list notification"
@@ -16845,8 +16814,6 @@ msgid "Deadlines"
msgstr "期限"
#: pretix/control/templates/pretixcontrol/event/payment.html:68
#, fuzzy
#| msgid "days"
msgctxt "unit"
msgid "days"
msgstr "日"
@@ -17044,9 +17011,9 @@ msgid ""
"the payment methods that fit your workflow best. Here are just two of them "
"as examples, you can add more in the \"Settings\" part of your event."
msgstr ""
"Pretix 支援<a href=\"https://pretix.eu/about/en/features/payment\" "
"target=\"_blank\">>廣泛的支付提供者</a>,允許你選擇最適合你工作流程的支付方"
"式。這裡只是其中兩個作為範例,你可以在活動的“設定”部分添加更多內容。"
"Pretix 支援<a href=\"https://pretix.eu/about/en/features/payment\" target="
"\"_blank\">>廣泛的支付提供者</a>,允許你選擇最適合你工作流程的支付方式。這裡"
"只是其中兩個作為範例,你可以在活動的“設定”部分添加更多內容。"
#: pretix/control/templates/pretixcontrol/event/quick_setup.html:173
msgid ""
@@ -17282,10 +17249,10 @@ msgstr "稅務規則%(name)s"
#: pretix/control/templates/pretixcontrol/event/tax_edit.html:34
#, python-format
msgid ""
"These settings are intended for advanced users. See the <a "
"href=\"%(docs)s\">documentation</a> for more information. Note that we are "
"not responsible for the correct handling of taxes in your ticket shop. If in "
"doubt, please contact a lawyer or tax consultant."
"These settings are intended for advanced users. See the <a href=\"%(docs)s"
"\">documentation</a> for more information. Note that we are not responsible "
"for the correct handling of taxes in your ticket shop. If in doubt, please "
"contact a lawyer or tax consultant."
msgstr ""
"這些設定適用於進階使用者。有關詳細資訊,請參閱 <a href=\"%(docs)s\">文檔</"
"a>。請注意,我們不負責在你的售票處正確處理稅款。如有疑問,請聯繫律師或稅務顧"
@@ -17567,7 +17534,7 @@ msgid ""
"The list below shows all events you have administrative access to. Click on "
"the event name to access event details."
msgstr ""
"下面的清單顯示你具有管理訪問許可權的所有事件。按兩下事件名稱,以訪問事件詳細"
"下面的清單顯示你具有管理訪問許可權的所有活動。按兩下活動名稱,以訪問活動詳細"
"資訊。"
#: pretix/control/templates/pretixcontrol/events/index.html:12
@@ -17909,9 +17876,9 @@ msgstr ""
#: pretix/control/templates/pretixcontrol/item/create.html:132
msgid ""
"Examples: Ticket category with variations for \"full price\" and "
"\"reduced\", merchandise with variations for different sizes, workshop add-"
"on with variations for simultaneous workshops."
"Examples: Ticket category with variations for \"full price\" and \"reduced"
"\", merchandise with variations for different sizes, workshop add-on with "
"variations for simultaneous workshops."
msgstr ""
"範例:包含「全價」和「折扣價」類型的門票類別、具有不同尺寸類型的商品、包含同"
"時研討會類型的維修中心附加元件。"
@@ -18198,8 +18165,8 @@ msgstr "自動折扣"
msgid ""
"With automatic discounts, you can automatically apply a discount to "
"purchases from your customers based on certain conditions. For example, you "
"can create group discounts like \"get 20%% off if you buy 3 or more "
"tickets\" or \"buy 2 tickets, get 1 free\"."
"can create group discounts like \"get 20%% off if you buy 3 or more tickets"
"\" or \"buy 2 tickets, get 1 free\"."
msgstr ""
"使用自動折扣,你可以根據特定條件自動將折扣應用於客戶的購買。例如,你可以創建"
"團體折扣,例如“如果你購買 3 張或更多門票,可享受 20%%的折扣”或“買 2 張門票,"
@@ -18210,8 +18177,8 @@ msgid ""
"Automatic discounts are available to all customers as long as they are "
"active. If you want to offer special prices only to specific customers, you "
"can use vouchers instead. If you want to offer discounts across multiple "
"purchases (\"buy a package of 10 you can turn into individual tickets "
"later\"), you can use customer accounts and memberships instead."
"purchases (\"buy a package of 10 you can turn into individual tickets later"
"\"), you can use customer accounts and memberships instead."
msgstr ""
"只要所有客戶處於活動狀態,就可以享受自動折扣。如果你只想向特定客戶提供特價,"
"則可以改用優惠券。如果你想為多次購買提供折扣(“購買 10 張套餐,以後可以變成個"
@@ -19573,8 +19540,8 @@ msgstr "標記為完成"
msgid ""
"We received notice that <strong>%(amount)s</strong> have been refunded via "
"<strong>%(method)s</strong>. If this refund is processed, the order will be "
"underpaid by <strong>%(pending)s</strong>. The order total is "
"<strong>%(total)s</strong>."
"underpaid by <strong>%(pending)s</strong>. The order total is <strong>"
"%(total)s</strong>."
msgstr ""
"我們收到通知,(<strong>%(amount)s)已</strong>透過<strong>%(method)s</strong>"
"退款。如果處理此退款,訂單將少付<strong>%(pending)s。</strong>訂單總數為 "
@@ -20488,7 +20455,7 @@ msgstr "召集人頁面"
#: pretix/control/templates/pretixcontrol/organizers/edit.html:68
msgid "The links you configure here will also be shown on all of your events."
msgstr "你在此處設定的連結也將顯示在你的所有事件中。"
msgstr "你在此處設定的連結也將顯示在你的所有活動中。"
#: pretix/control/templates/pretixcontrol/organizers/edit.html:142
msgid ""
@@ -21981,11 +21948,11 @@ msgstr "兩步驟狀態"
#: pretix/control/templates/pretixcontrol/user/2fa_main.html:44
msgid "Two-factor authentication is currently enabled."
msgstr "兩步驟驗證目前啟用"
msgstr "兩步驟驗證目前啟用"
#: pretix/control/templates/pretixcontrol/user/2fa_main.html:60
msgid "Two-factor authentication is currently disabled."
msgstr "兩步驟驗證目前停用"
msgstr "兩步驟驗證目前停用"
#: pretix/control/templates/pretixcontrol/user/2fa_main.html:63
msgid "To enable it, you need to configure at least one device below."
@@ -22285,7 +22252,7 @@ msgstr "刪除優惠券"
#, python-format
msgid ""
"Are you sure you want to delete the voucher <strong>%(voucher)s</strong>?"
msgstr "是否確實要刪除憑證 <strong>%(voucher)s</strong>"
msgstr "是否確實要刪除憑證 <strong>%(voucher)s</strong>"
#: pretix/control/templates/pretixcontrol/vouchers/delete_bulk.html:4
#: pretix/control/templates/pretixcontrol/vouchers/delete_bulk.html:6
@@ -22310,8 +22277,8 @@ msgstr "刪除購物車"
#: pretix/control/templates/pretixcontrol/vouchers/delete_carts.html:9
#, python-format
msgid ""
"Are you sure you want to delete any cart positions with voucher "
"<strong>%(voucher)s</strong>?"
"Are you sure you want to delete any cart positions with voucher <strong>"
"%(voucher)s</strong>?"
msgstr "是否確實要刪除憑證 <strong>%(voucher)s的任何</strong>購物車位置?"
#: pretix/control/templates/pretixcontrol/vouchers/delete_carts.html:10
@@ -22432,8 +22399,8 @@ msgstr "刪除條目"
#: pretix/control/templates/pretixcontrol/waitinglist/delete.html:9
#, python-format
msgid ""
"Are you sure you want to delete the following waiting list entry "
"<strong>%(entry)s</strong>?"
"Are you sure you want to delete the following waiting list entry <strong>"
"%(entry)s</strong>?"
msgstr "是否確實要刪除以下候補名單條目 <strong>%(entry)s</strong>"
#: pretix/control/templates/pretixcontrol/waitinglist/delete_bulk.html:4
@@ -22505,8 +22472,8 @@ msgstr "銷售估計"
#, python-format
msgid ""
"If you can make enough room at your event to fit all the persons on the "
"waiting list in, you could sell tickets worth an additional "
"<strong>%(amount)s</strong>."
"waiting list in, you could sell tickets worth an additional <strong>"
"%(amount)s</strong>."
msgstr ""
"如果你能在活動中騰出足夠的空間來容納等候名單上的所有人,你可以出售價值額外"
"<strong>%(amount)s</strong>的票."
@@ -22529,20 +22496,14 @@ msgstr "此條目的優先順序已修改。此數字越高,此人將越早獲
msgid ""
"For safety reasons, the waiting list does not run if the quota is set to "
"unlimited."
msgstr ""
msgstr "出於安全考慮,如果額度設定為無限制,將不設等候名單。"
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:219
#, fuzzy
#| msgid "Quota name"
msgid "Quota unlimited"
msgstr "額度名稱"
msgstr "無限額度"
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:225
#, fuzzy, python-format
#| msgid ""
#| "\n"
#| " Waiting, product %(num)sx available\n"
#| " "
#, python-format
msgid ""
"\n"
" Waiting, product %(num)sx "
@@ -22550,8 +22511,8 @@ msgid ""
" "
msgstr ""
"\n"
" 等待中,產品 %(num)s可用\n"
" "
" 等待中,產品 %(num)s可用\n"
" "
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:231
msgid "Waiting, product unavailable"
@@ -23267,12 +23228,6 @@ msgstr "所選產品已停用。"
#: pretix/control/views/mailsetup.py:195
#, fuzzy
#| msgid ""
#| "We could not find an SPF record set for the domain you are trying to use. "
#| "You can still proceed, but it will increase the chance of emails going to "
#| "spam or being rejected. We strongly recommend setting an SPF record on "
#| "the domain. You can do so through the DNS settings at the provider you "
#| "registered your domain with."
msgid ""
"We could not find an SPF record set for the domain you are trying to use. "
"This means that there is a very high change most of the emails will be "
@@ -23377,9 +23332,7 @@ msgstr "我們無法將上傳的檔解析為 CSV 檔。"
#: pretix/control/views/orders.py:330
msgid "Your invoice"
msgid_plural "Your invoices"
msgstr[0] ""
"你的發票,\n"
"你的發票"
msgstr[0] "你的發票"
#: pretix/control/views/orders.py:332
#, python-brace-format
@@ -23678,7 +23631,7 @@ msgstr "訂單已更改,使用者已收到通知。"
#: pretix/control/views/orders.py:1828 pretix/control/views/orders.py:1962
#: pretix/control/views/orders.py:1999 pretix/presale/views/order.py:1538
msgid "The order has been changed."
msgstr "訂單順序已更改"
msgstr "訂單順序已更改"
#: pretix/control/views/orders.py:1855 pretix/presale/checkoutflow.py:881
#: pretix/presale/views/order.py:799
@@ -23960,7 +23913,7 @@ msgstr "禮品卡已建立,現在可以使用了。"
#: pretix/control/views/organizer.py:1659
msgid "All events (that I have access to)"
msgstr "所有事件(我有權存取)"
msgstr "所有活動(我有權存取)"
#: pretix/control/views/organizer.py:2024
msgid "The selected gate has been deleted."
@@ -25099,7 +25052,7 @@ msgid_plural ""
"You are trying to download a refund export from %(date)s with %(cnt)s order "
"and a total of %(sum)s."
msgstr[0] ""
"你正在嘗試從%(date)s 下載退款匯出,其中包含%(cnt)s的訂單和總計%(sum)s"
"你正在嘗試從%(date)s 下載退款匯出,其中包含%(cnt)s的訂單和總計%(sum)s"
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/sepa_export.html:23
msgid ""
@@ -25274,7 +25227,7 @@ msgstr "值機清單 PDF"
#: pretix/plugins/checkinlists/exporters.py:648
msgctxt "export_category"
msgid "Check-in"
msgstr "Check-in"
msgstr "簽到"
#: pretix/plugins/checkinlists/exporters.py:286
msgid ""
@@ -25731,7 +25684,7 @@ msgstr "完成抓取。"
#: pretix/plugins/paypal2/signals.py:67
msgctxt "paypal"
msgid "Capture pending."
msgstr "抓取處理中"
msgstr "抓取處理中"
#: pretix/plugins/paypal2/signals.py:76
msgid "PayPal reported an event: {}"
@@ -26107,9 +26060,6 @@ msgid "Restrict to event dates starting before"
msgstr "限制為早於之前開始的活動日期"
#: pretix/plugins/sendmail/forms.py:170
#, fuzzy
#| msgctxt "sendmail_from"
#| msgid "Send to"
msgctxt "sendmail_form"
msgid "Send to"
msgstr "傳送到"
@@ -26124,9 +26074,6 @@ msgid "Filter check-in status"
msgstr "篩選簽到狀態"
#: pretix/plugins/sendmail/forms.py:189
#, fuzzy
#| msgctxt "sendmail_from"
#| msgid "Restrict to recipients without check-in"
msgctxt "sendmail_form"
msgid "Restrict to recipients without check-in"
msgstr "僅限未簽到的收件者"
@@ -26176,17 +26123,11 @@ msgid "pending with payment overdue"
msgstr "待處理,付款逾期"
#: pretix/plugins/sendmail/forms.py:258
#, fuzzy
#| msgctxt "sendmail_from"
#| msgid "Restrict to orders with status"
msgctxt "sendmail_form"
msgid "Restrict to orders with status"
msgstr "限制為具有狀態的訂單"
msgstr "限具有狀態的訂單"
#: pretix/plugins/sendmail/forms.py:283 pretix/plugins/sendmail/forms.py:287
#, fuzzy
#| msgctxt "sendmail_from"
#| msgid "Restrict to recipients with check-in on list"
msgctxt "sendmail_form"
msgid "Restrict to recipients with check-in on list"
msgstr "僅限在清單中簽到的收件者"
@@ -26257,11 +26198,8 @@ msgid "Limit products"
msgstr "限制商品"
#: pretix/plugins/sendmail/models.py:218
#, fuzzy
#| msgctxt "sendmail_from"
#| msgid "Restrict to orders with status"
msgid "Restrict to orders with status"
msgstr "限制為具有狀態的訂單"
msgstr "限具有狀態的訂單"
#: pretix/plugins/sendmail/models.py:228
msgid "Send date"
@@ -26313,7 +26251,7 @@ msgstr[0] ""
"%(count)d 事件在%(time)s開始的前一天 , 事件在%(time)s開始前的%(count)d天"
#: pretix/plugins/sendmail/signals.py:99
msgid "Automated emails"
msgid "Scheduled emails"
msgstr "自動電子郵件"
#: pretix/plugins/sendmail/signals.py:122
@@ -26391,7 +26329,7 @@ msgstr "建立電子郵件規則"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html:14
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:15
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html:10
msgid "Automated emails are not sent as long as your ticket shop is offline."
msgid "Scheduled emails are not sent as long as your ticket shop is offline."
msgstr "只要你的售票店處於離線狀態,就不會發送自動電子郵件。"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html:44
@@ -26440,7 +26378,7 @@ msgstr "最後排程計算"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:4
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:6
msgid "Automated email rules"
msgid "Scheduled email rules"
msgstr "自動電子郵件規則"
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_list.html:8
@@ -26710,10 +26648,10 @@ msgstr "從Stripe斷線"
#: pretix/plugins/stripe/payment.py:200
msgid ""
"Please configure a <a href=\"https://dashboard.stripe.com/account/"
"webhooks\">Stripe Webhook</a> to the following endpoint in order to "
"automatically cancel orders when charges are refunded externally and to "
"process asynchronous payment methods like SOFORT."
"Please configure a <a href=\"https://dashboard.stripe.com/account/webhooks"
"\">Stripe Webhook</a> to the following endpoint in order to automatically "
"cancel orders when charges are refunded externally and to process "
"asynchronous payment methods like SOFORT."
msgstr ""
"請將<a href=\"https://dashboard.stripe.com/account/webhooks\">Stripe Webhook "
"配置到</a>以下節點,以便在外部退款時,自動取消訂單,並處理非同步支付方式,如 "
@@ -26840,27 +26778,19 @@ msgid "Bancontact"
msgstr "Bancontact"
#: pretix/plugins/stripe/payment.py:357
#, fuzzy
#| msgid "Disable SEPA Direct Debit"
msgid "SEPA Direct Debit"
msgstr "禁用 SEPA 直接扣款"
msgstr "SEPA 直接轉帳"
#: pretix/plugins/stripe/payment.py:362
#, fuzzy
#| msgid ""
#| "Despite the name, Sofort payments via Stripe are <strong>not</strong> "
#| "processed instantly but might take up to <strong>14 days</strong> to be "
#| "confirmed in some cases. Please only activate this payment method if your "
#| "payment term allows for this lag."
msgid ""
"SEPA Direct Debit payments via Stripe are <strong>not</strong> processed "
"instantly but might take up to <strong>14 days</strong> to be confirmed in "
"some cases. Please only activate this payment method if your payment term "
"allows for this lag."
msgstr ""
"儘管有這個名字,但通過 Stripe 進行的 Sofort 付款<strong>不會</strong>立即處"
"理,但在某些情況下可能需要長達 <strong>14 天</strong>才能確認。請僅在你的付款"
"期限,允許此延遲時啟動此付款方式。"
"通過 Stripe 進行的 SEPA 直接轉帳<strong>不會</strong>立即處理,但在某些情況下"
"可能需要長達 <strong>14 天</strong>才能確認。請只有在您的付款期限允許此延遲的"
"情況下,才啟用此付款方式。"
#: pretix/plugins/stripe/payment.py:370
msgid "SEPA Creditor Mandate Name"
@@ -26975,44 +26905,33 @@ msgid "Credit card"
msgstr "信用卡"
#: pretix/plugins/stripe/payment.py:1157
#, fuzzy
#| msgid "EPS via Stripe"
msgid "SEPA Debit via Stripe"
msgstr "EPS透過Stripe"
msgstr "透過Stripe進行SEPA直接扣款"
#: pretix/plugins/stripe/payment.py:1158
msgid "SEPA Debit"
msgstr ""
msgstr "SEPA扣款"
#: pretix/plugins/stripe/payment.py:1197
#, fuzzy
#| msgid "Account holder"
msgid "Account Holder Name"
msgstr "帳戶持有人"
msgstr "帳戶持有人名稱"
#: pretix/plugins/stripe/payment.py:1202
#, fuzzy
#| msgid "Account holder"
msgid "Account Holder Street"
msgstr "帳戶持有人"
msgstr "帳戶持有人街道"
#: pretix/plugins/stripe/payment.py:1214
#, fuzzy
#| msgid "Account holder"
msgid "Account Holder Postal Code"
msgstr "帳戶持有人"
#: pretix/plugins/stripe/payment.py:1226
#, fuzzy
#| msgid "Account holder"
msgid "Account Holder City"
msgstr "帳戶持有人"
msgstr "帳戶持有人城市"
#: pretix/plugins/stripe/payment.py:1238
#, fuzzy
#| msgid "Account holder"
msgid "Account Holder Country"
msgstr "帳戶持有人"
msgstr "帳戶持有人國家"
#: pretix/plugins/stripe/payment.py:1282
msgid "giropay via Stripe"
@@ -27183,10 +27102,8 @@ msgid "Card type"
msgstr "卡片類型"
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html:14
#, fuzzy
#| msgid "The total amount will be withdrawn from your credit card."
msgid "The total amount will be withdrawn from your bank account."
msgstr "總金額將從你的信用卡中提取。"
msgstr "總金額將從你的銀行戶口中提取。"
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html:18
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_sepadirectdebit.html:20
@@ -27195,10 +27112,8 @@ msgstr ""
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html:20
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_sepadirectdebit.html:22
#, fuzzy
#| msgid "Account holder"
msgid "Account number"
msgstr "帳戶持有人"
msgstr "帳戶號碼"
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_confirm.html:24
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_simple.html:4
@@ -27241,25 +27156,18 @@ msgstr ""
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_sepadirectdebit.html:10
#, fuzzy
#| msgid "For a credit card payment, please turn on JavaScript."
msgid "For a SEPA Debit payment, please turn on JavaScript."
msgstr "對於信用卡付款,請打開JavaScript。"
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_sepadirectdebit.html:16
#, fuzzy
#| msgid ""
#| "You already entered a card number that we will use to charge the payment "
#| "amount."
msgid ""
"You already entered a bank account that we will use to charge the payment "
"amount."
msgstr "已經輸入了一個卡號,我們將使用該卡號來收取付款金額。"
msgstr "已經輸入了一個我們將用來扣除支付金額的銀行帳戶。"
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_sepadirectdebit.html:27
#, fuzzy
#| msgid "Use a different card"
msgid "Use a different account"
msgstr "使用不同卡片"
msgstr "使用不同帳戶"
#: pretix/plugins/stripe/templates/pretixplugins/stripe/checkout_payment_form_sepadirectdebit.html:51
#, python-format
@@ -27539,7 +27447,7 @@ msgstr "網上check-in"
#: pretix/plugins/webcheckin/templates/pretixplugins/webcheckin/index.html:10
msgid "Check-in"
msgstr "Check-in"
msgstr "簽到"
#: pretix/presale/checkoutflow.py:107
msgctxt "checkoutflow"
@@ -27837,7 +27745,7 @@ msgstr "網站以%(language)s"
#: pretix/presale/templates/pretixpresale/event/base.html:59
#, python-format
msgid "Show all events of %(name)s"
msgstr "顯示%(name)s的所有事件"
msgstr "顯示%(name)s的所有活動"
#: pretix/presale/templates/pretixpresale/event/base.html:73
#: pretix/presale/templates/pretixpresale/event/base.html:78
@@ -29093,7 +29001,7 @@ msgstr "重新傳送訂單連結"
#: pretix/presale/templates/pretixpresale/event/order.html:12
#: pretix/presale/templates/pretixpresale/event/order.html:29
msgid "Thank you!"
msgstr "感謝你"
msgstr "感謝你"
#: pretix/presale/templates/pretixpresale/event/order.html:14
#: pretix/presale/templates/pretixpresale/event/order.html:32
@@ -29310,9 +29218,8 @@ msgstr ""
#: pretix/presale/templates/pretixpresale/event/order.html:412
#, python-format
msgid ""
"You can cancel this order. In this case, a cancellation fee of "
"<strong>%(fee)s</strong> will be kept and you will receive a refund of the "
"remainder."
"You can cancel this order. In this case, a cancellation fee of <strong>"
"%(fee)s</strong> will be kept and you will receive a refund of the remainder."
msgstr ""
"你可以取消此訂單。在這種情況下,將保留 <strong>%(fee)s</strong> 的取消費用,"
"你將收到剩餘部分的退款。"
@@ -29334,8 +29241,8 @@ msgid ""
"You can cancel this order. As per our cancellation policy, you will still be "
"required to pay a cancellation fee of <strong>%(fee)s</strong>."
msgstr ""
"你可以取消此訂單。根據我們的取消政策,你仍然需要支付以下取消費用: "
"<strong>%(fee)s</strong>."
"你可以取消此訂單。根據我們的取消政策,你仍然需要支付以下取消費用: <strong>"
"%(fee)s</strong>."
#: pretix/presale/templates/pretixpresale/event/order.html:463
msgid "You can cancel this order using the following button."
@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-21 11:46+0000\n"
"PO-Revision-Date: 2023-06-26 23:00+0000\n"
"Last-Translator: Yucheng Lin <yuchenglinedu@gmail.com>\n"
"PO-Revision-Date: 2023-08-31 07:00+0000\n"
"Last-Translator: Ash So <ashs@vankaifong.com>\n"
"Language-Team: Chinese (Traditional) <https://translate.pretix.eu/projects/"
"pretix/pretix-js/zh_Hant/>\n"
"Language: zh_Hant\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Weblate 4.17\n"
"X-Generator: Weblate 4.18.2\n"
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
@@ -662,10 +662,8 @@ msgid "Your local time:"
msgstr "你的當地時間:"
#: pretix/static/pretixpresale/js/walletdetection.js:39
#, fuzzy
#| msgid "Apple Pay"
msgid "Google Pay"
msgstr "Apple Pay"
msgstr "Google Pay"
#: pretix/static/pretixpresale/js/widget/widget.js:17
msgctxt "widget"
+19 -1
View File
@@ -607,6 +607,12 @@ class PaypalMethod(BasePaymentProvider):
response = self.client.execute(req)
except IOError as e:
logger.exception('PayPal OrdersGetRequest: {}'.format(str(e)))
payment.fail(info={
"error": {
"name": "IOError",
"message": str(e),
}
})
raise PaymentException(_('We had trouble communicating with PayPal'))
else:
pp_captured_order = response.result
@@ -615,9 +621,15 @@ class PaypalMethod(BasePaymentProvider):
ReferencedPayPalObject.objects.get_or_create(order=payment.order, payment=payment, reference=pp_captured_order.id)
except ReferencedPayPalObject.MultipleObjectsReturned:
pass
if str(pp_captured_order.purchase_units[0].amount.value) != str(payment.amount) or \
if Decimal(pp_captured_order.purchase_units[0].amount.value) != payment.amount or \
pp_captured_order.purchase_units[0].amount.currency_code != self.event.currency:
logger.error('Value mismatch: Payment %s vs paypal trans %s' % (payment.id, str(pp_captured_order.dict())))
payment.fail(info={
"error": {
"name": "ValidationError",
"message": "Value mismatch",
}
})
raise PaymentException(_('We were unable to process your payment. See below for details on how to '
'proceed.'))
@@ -660,6 +672,12 @@ class PaypalMethod(BasePaymentProvider):
self.client.execute(patchreq)
except IOError as e:
messages.error(request, _('We had trouble communicating with PayPal'))
payment.fail(info={
"error": {
"name": "IOError",
"message": str(e),
}
})
logger.exception('PayPal OrdersPatchRequest: {}'.format(str(e)))
return
+1 -1
View File
@@ -96,7 +96,7 @@ def control_nav_import(sender, request=None, **kwargs):
'active': (url.namespace == 'plugins:sendmail' and url.url_name.startswith('send')),
},
{
'label': _('Automated emails'),
'label': _('Scheduled emails'),
'url': reverse('plugins:sendmail:rule.list', kwargs={
'event': request.event.slug,
'organizer': request.event.organizer.slug,
@@ -6,7 +6,7 @@
<h1>{% trans "Create Email Rule" %}</h1>
{% if not request.event.live %}
<div class="alert alert-warning">
{% trans "Automated emails are not sent as long as your ticket shop is offline." %}
{% trans "Scheduled emails are not sent as long as your ticket shop is offline." %}
</div>
{% endif %}
{% block inner %}
@@ -11,7 +11,7 @@
</p>
{% if not request.event.live %}
<div class="alert alert-warning">
{% trans "Automated emails are not sent as long as your ticket shop is offline." %}
{% trans "Scheduled emails are not sent as long as your ticket shop is offline." %}
</div>
{% endif %}
<dl class="dl-horizontal">
@@ -1,9 +1,9 @@
{% extends "pretixcontrol/event/base.html" %}
{% load i18n %}
{% load static %}
{% block title %}{% trans "Automated email rules" %}{% endblock %}
{% block title %}{% trans "Scheduled email rules" %}{% endblock %}
{% block content %}
<h1>{% trans "Automated email rules" %}</h1>
<h1>{% trans "Scheduled email rules" %}</h1>
<p>
{% blocktrans trimmed %}
Email rules allow you to automatically send emails to your customers at a specific time before or after
@@ -12,7 +12,7 @@
</p>
{% if not request.event.live %}
<div class="alert alert-warning">
{% trans "Automated emails are not sent as long as your ticket shop is offline." %}
{% trans "Scheduled emails are not sent as long as your ticket shop is offline." %}
</div>
{% endif %}
@@ -7,7 +7,7 @@
<h1>{% trans "Update Email Rule" %}</h1>
{% if not request.event.live %}
<div class="alert alert-warning">
{% trans "Automated emails are not sent as long as your ticket shop is offline." %}
{% trans "Scheduled emails are not sent as long as your ticket shop is offline." %}
</div>
{% endif %}
{% block inner %}
+6
View File
@@ -554,6 +554,9 @@ class StripeMethod(BasePaymentProvider):
ctx = {'request': request, 'event': self.event, 'settings': self.settings, 'provider': self}
return template.render(ctx)
def payment_can_retry(self, payment):
return self._is_still_available(order=payment.order)
def _charge_source(self, request, source, payment):
try:
params = {}
@@ -1578,6 +1581,9 @@ class StripeSofort(StripeMethod):
return True
return False
def payment_can_retry(self, payment):
return payment.state != OrderPayment.PAYMENT_STATE_PENDING and self._is_still_available(order=payment.order)
def payment_presale_render(self, payment: OrderPayment) -> str:
pi = payment.info_data or {}
try:
+1 -1
View File
@@ -1038,7 +1038,7 @@ class QuestionsStep(QuestionsViewMixin, CartMixin, TemplateFlowStep):
def reduce_initial(v):
if isinstance(v, dict):
# try to flatten objects such as name_parts to a single string to determine whether they have any value set
return ''.join([v for k, v in v.items() if not k.startswith('_')])
return ''.join([v for k, v in v.items() if not k.startswith('_') and v])
else:
return v
+44
View File
@@ -794,6 +794,26 @@ def test_reupload_same_nonce(token_client, organizer, clist, event, order):
resp = _redeem(token_client, organizer, clist, p.pk, {'nonce': 'foobar'})
assert resp.status_code == 201
assert resp.data['status'] == 'ok'
with scopes_disabled():
assert p.all_checkins.count() == 1
@pytest.mark.django_db
def test_reupload_same_nonce_not_ignored_after_failed(token_client, organizer, clist, event, order):
with scopes_disabled():
p = order.positions.first()
p.all_checkins.create(
type=Checkin.TYPE_ENTRY,
nonce='foobar',
successful=False,
list=clist,
)
resp = _redeem(token_client, organizer, clist, p.pk, {'nonce': 'foobar'})
assert resp.status_code == 201
assert resp.data['status'] == 'ok'
with scopes_disabled():
assert p.all_checkins.count() == 2
@pytest.mark.django_db
@@ -1105,6 +1125,7 @@ def test_store_failed(token_client, organizer, clist, event, order):
organizer.slug, event.slug, clist.pk,
), {
'raw_barcode': '123456',
'nonce': '4321',
'error_reason': 'invalid'
}, format='json')
assert resp.status_code == 201
@@ -1115,6 +1136,7 @@ def test_store_failed(token_client, organizer, clist, event, order):
organizer.slug, event.slug, clist.pk,
), {
'raw_barcode': '123456',
'nonce': '1234',
'position': p.pk,
'error_reason': 'unpaid'
}, format='json')
@@ -1122,6 +1144,28 @@ def test_store_failed(token_client, organizer, clist, event, order):
with scopes_disabled():
assert p.all_checkins.filter(successful=False).count() == 1
# Ignore sending the same nonces again
resp = token_client.post('/api/v1/organizers/{}/events/{}/checkinlists/{}/failed_checkins/'.format(
organizer.slug, event.slug, clist.pk,
), {
'raw_barcode': '123456',
'nonce': '4321',
'error_reason': 'invalid'
}, format='json')
assert resp.status_code == 201
resp = token_client.post('/api/v1/organizers/{}/events/{}/checkinlists/{}/failed_checkins/'.format(
organizer.slug, event.slug, clist.pk,
), {
'raw_barcode': '123456',
'nonce': '1234',
'position': p.pk,
'error_reason': 'unpaid'
}, format='json')
assert resp.status_code == 201
with scopes_disabled():
assert Checkin.all.filter(successful=False).count() == 2
resp = token_client.post('/api/v1/organizers/{}/events/{}/checkinlists/{}/failed_checkins/'.format(
organizer.slug, event.slug, clist.pk,
), {
+1 -1
View File
@@ -211,7 +211,7 @@ def _redeem(token_client, org, clist, p, body=None, query=''):
def test_query_load(token_client, organizer, clist, event, order, django_assert_max_num_queries):
with scopes_disabled():
p = order.positions.first()
with django_assert_max_num_queries(30):
with django_assert_max_num_queries(29):
resp = _redeem(token_client, organizer, clist, p.secret)
assert resp.status_code == 201
assert resp.data['status'] == 'ok'
+28
View File
@@ -104,6 +104,34 @@ def test_customer_create(token_client, organizer):
assert len(djmail.outbox) == 0
@pytest.mark.django_db
def test_customer_create_email_unique(token_client, organizer):
resp = token_client.post(
'/api/v1/organizers/{}/customers/'.format(organizer.slug),
format='json',
data={
'identifier': 'IGNORED',
'email': 'bar@example.com',
'password': 'foobar',
'is_active': True,
'is_verified': True,
}
)
assert resp.status_code == 201
resp = token_client.post(
'/api/v1/organizers/{}/customers/'.format(organizer.slug),
format='json',
data={
'identifier': 'IGNORED',
'email': 'bar@example.com',
'password': 'foobar',
'is_active': True,
'is_verified': True,
}
)
assert resp.status_code == 400
@pytest.mark.django_db
def test_customer_create_send_email(token_client, organizer):
resp = token_client.post(
+5 -1
View File
@@ -52,7 +52,11 @@ TEST_DISCOUNT_RES = {
"condition_min_count": 3,
"condition_min_value": "0.00",
"benefit_discount_matching_percent": "100.00",
"benefit_only_apply_to_cheapest_n_matches": 1
"benefit_only_apply_to_cheapest_n_matches": 1,
"benefit_same_products": True,
"benefit_limit_products": [],
"benefit_apply_to_addons": True,
"benefit_ignore_voucher_discounted": False,
}
+69
View File
@@ -137,6 +137,37 @@ def order(event, item, taxrule, question):
return o
@pytest.fixture
def order2(event2, item2):
testtime = datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=datetime.timezone.utc)
with mock.patch('django.utils.timezone.now') as mock_now:
mock_now.return_value = testtime
o = Order.objects.create(
code='BAR', event=event2, email='dummy@dummy.test',
status=Order.STATUS_PENDING, secret="asd436cvbfd1",
datetime=datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=datetime.timezone.utc),
expires=datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=datetime.timezone.utc),
total=23, locale='en'
)
o.payments.create(
provider='banktransfer',
state='pending',
amount=Decimal('23.00'),
)
OrderPosition.objects.create(
order=o,
item=item2,
variation=None,
price=Decimal("23"),
attendee_name_parts={"full_name": "Peter", "_scheme": "full"},
secret="asdlfksdgdfgxcbfgdhfg",
pseudonymization_id="AC892345",
positionid=1,
)
return o
@pytest.fixture
def invoice(order):
testtime = datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=datetime.timezone.utc)
@@ -146,8 +177,18 @@ def invoice(order):
return generate_invoice(order)
@pytest.fixture
def invoice2(order2):
testtime = datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=datetime.timezone.utc)
with mock.patch('django.utils.timezone.now') as mock_now:
mock_now.return_value = testtime
return generate_invoice(order2)
TEST_INVOICE_RES = {
"order": "FOO",
"event": "dummy",
"number": "DUMMY-00001",
"is_cancellation": False,
"invoice_from_name": "",
@@ -268,6 +309,34 @@ def test_invoice_list(token_client, organizer, event, order, item, invoice):
assert [] == resp.data['results']
@pytest.mark.django_db
def test_organizer_level(token_client, organizer, team, event, event2, invoice, invoice2):
resp = token_client.get('/api/v1/organizers/{}/invoices/'.format(organizer.slug))
assert resp.status_code == 200
assert len(resp.data['results']) == 2
resp = token_client.get('/api/v1/organizers/{}/invoices/{}/'.format(organizer.slug, invoice.number))
assert resp.status_code == 200
resp = token_client.get('/api/v1/organizers/{}/invoices/{}/'.format(organizer.slug, invoice2.number))
assert resp.status_code == 200
with scopes_disabled():
team.all_events = False
team.save()
team.limit_events.set([event2])
resp = token_client.get('/api/v1/organizers/{}/invoices/'.format(organizer.slug))
assert resp.status_code == 200
assert len(resp.data['results']) == 1
resp = token_client.get('/api/v1/organizers/{}/invoices/{}/'.format(organizer.slug, invoice.number))
assert resp.status_code == 404
resp = token_client.get('/api/v1/organizers/{}/invoices/{}/'.format(organizer.slug, invoice2.number))
assert resp.status_code == 200
@pytest.mark.django_db
def test_invoice_detail(token_client, organizer, event, item, invoice):
res = dict(TEST_INVOICE_RES)
+1
View File
@@ -420,6 +420,7 @@ def test_order_create_invoice(token_client, organizer, event, order):
pos = order.positions.first()
assert json.loads(json.dumps(resp.data)) == {
'order': 'FOO',
'event': 'dummy',
'number': 'DUMMY-00001',
'is_cancellation': False,
"invoice_from_name": "",
+1
View File
@@ -309,6 +309,7 @@ def test_order_create_simulate(token_client, organizer, event, item, quota, ques
del d['positions'][0]['secret']
assert d == {
'code': 'PREVIEW',
'event': 'dummy',
'status': 'n',
'testmode': False,
'email': 'dummy@dummy.test',
+60
View File
@@ -139,6 +139,37 @@ def order(event, item, taxrule, question):
return o
@pytest.fixture
def order2(event2, item2):
testtime = datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=datetime.timezone.utc)
with mock.patch('django.utils.timezone.now') as mock_now:
mock_now.return_value = testtime
o = Order.objects.create(
code='BAR', event=event2, email='dummy@dummy.test',
status=Order.STATUS_PENDING, secret="asd436cvbfd1",
datetime=datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=datetime.timezone.utc),
expires=datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=datetime.timezone.utc),
total=23, locale='en'
)
o.payments.create(
provider='banktransfer',
state='pending',
amount=Decimal('23.00'),
)
OrderPosition.objects.create(
order=o,
item=item2,
variation=None,
price=Decimal("23"),
attendee_name_parts={"full_name": "Peter", "_scheme": "full"},
secret="asdlfksdgdfgxcbfgdhfg",
pseudonymization_id="AC892345",
positionid=1,
)
return o
@pytest.fixture
def clist_autocheckin(event):
c = event.checkin_lists.create(name="Default", all_products=True, auto_checkin_sales_channels=['web'])
@@ -228,6 +259,7 @@ TEST_REFUNDS_RES = [
]
TEST_ORDER_RES = {
"code": "FOO",
"event": "dummy",
"status": "n",
"testmode": False,
"secret": "k24fiuwvu8kxz3y1",
@@ -460,6 +492,34 @@ def test_order_detail(token_client, organizer, event, order, item, taxrule, ques
assert len(resp.data['fees']) == 2
@pytest.mark.django_db
def test_organizer_level(token_client, organizer, team, event, event2, order, order2):
resp = token_client.get('/api/v1/organizers/{}/orders/'.format(organizer.slug))
assert resp.status_code == 200
assert len(resp.data['results']) == 2
resp = token_client.get('/api/v1/organizers/{}/orders/FOO/'.format(organizer.slug))
assert resp.status_code == 200
resp = token_client.get('/api/v1/organizers/{}/orders/BAR/'.format(organizer.slug))
assert resp.status_code == 200
with scopes_disabled():
team.all_events = False
team.save()
team.limit_events.set([event2])
resp = token_client.get('/api/v1/organizers/{}/orders/'.format(organizer.slug))
assert resp.status_code == 200
assert len(resp.data['results']) == 1
resp = token_client.get('/api/v1/organizers/{}/orders/FOO/'.format(organizer.slug))
assert resp.status_code == 404
resp = token_client.get('/api/v1/organizers/{}/orders/BAR/'.format(organizer.slug))
assert resp.status_code == 200
@pytest.mark.django_db
def test_include_exclude_fields(token_client, organizer, event, order, item, taxrule, question):
resp = token_client.get('/api/v1/organizers/{}/events/{}/orders/{}/?exclude=positions.secret'.format(
+12 -45
View File
@@ -97,15 +97,7 @@ def test_payment_fee_reverse_percent_and_abs_default(event):
def test_availability_date_available(event):
prov = DummyPaymentProvider(event)
prov.settings.set('_availability_date', datetime.date.today() + datetime.timedelta(days=1))
result = prov._is_available_by_time()
assert result
@pytest.mark.django_db
def test_availability_start_available(event):
prov = DummyPaymentProvider(event)
prov.settings.set('_availability_start', datetime.date.today() - datetime.timedelta(days=1))
result = prov._is_available_by_time()
result = prov._is_still_available()
assert result
@@ -113,15 +105,7 @@ def test_availability_start_available(event):
def test_availability_date_not_available(event):
prov = DummyPaymentProvider(event)
prov.settings.set('_availability_date', datetime.date.today() - datetime.timedelta(days=1))
result = prov._is_available_by_time()
assert not result
@pytest.mark.django_db
def test_availability_start_not_available(event):
prov = DummyPaymentProvider(event)
prov.settings.set('_availability_start', datetime.date.today() + datetime.timedelta(days=1))
result = prov._is_available_by_time()
result = prov._is_still_available()
assert not result
@@ -137,26 +121,9 @@ def test_availability_date_relative(event):
))
utc = datetime.timezone.utc
assert prov._is_available_by_time(datetime.datetime(2016, 11, 30, 23, 0, 0, tzinfo=tz).astimezone(utc))
assert prov._is_available_by_time(datetime.datetime(2016, 12, 1, 23, 59, 0, tzinfo=tz).astimezone(utc))
assert not prov._is_available_by_time(datetime.datetime(2016, 12, 2, 0, 0, 1, tzinfo=tz).astimezone(utc))
@pytest.mark.django_db
def test_availability_start_relative(event):
event.settings.set('timezone', 'US/Pacific')
tz = ZoneInfo('US/Pacific')
event.date_from = datetime.datetime(2016, 12, 3, 12, 0, 0, tzinfo=tz)
event.save()
prov = DummyPaymentProvider(event)
prov.settings.set('_availability_start', RelativeDateWrapper(
RelativeDate(days_before=2, time=datetime.time(12, 0), base_date_name='date_from', minutes_before=None)
))
utc = datetime.timezone.utc
assert not prov._is_available_by_time(datetime.datetime(2016, 11, 30, 23, 0, 0, tzinfo=tz).astimezone(utc))
assert prov._is_available_by_time(datetime.datetime(2016, 12, 1, 0, 0, tzinfo=tz).astimezone(utc))
assert prov._is_available_by_time(datetime.datetime(2016, 12, 2, 0, 0, 1, tzinfo=tz).astimezone(utc))
assert prov._is_still_available(datetime.datetime(2016, 11, 30, 23, 0, 0, tzinfo=tz).astimezone(utc))
assert prov._is_still_available(datetime.datetime(2016, 12, 1, 23, 59, 0, tzinfo=tz).astimezone(utc))
assert not prov._is_still_available(datetime.datetime(2016, 12, 2, 0, 0, 1, tzinfo=tz).astimezone(utc))
@pytest.mark.django_db
@@ -167,9 +134,9 @@ def test_availability_date_timezones(event):
tz = ZoneInfo('US/Pacific')
utc = ZoneInfo('UTC')
assert prov._is_available_by_time(datetime.datetime(2016, 11, 30, 23, 0, 0, tzinfo=tz).astimezone(utc))
assert prov._is_available_by_time(datetime.datetime(2016, 12, 1, 23, 59, 0, tzinfo=tz).astimezone(utc))
assert not prov._is_available_by_time(datetime.datetime(2016, 12, 2, 0, 0, 1, tzinfo=tz).astimezone(utc))
assert prov._is_still_available(datetime.datetime(2016, 11, 30, 23, 0, 0, tzinfo=tz).astimezone(utc))
assert prov._is_still_available(datetime.datetime(2016, 12, 1, 23, 59, 0, tzinfo=tz).astimezone(utc))
assert not prov._is_still_available(datetime.datetime(2016, 12, 2, 0, 0, 1, tzinfo=tz).astimezone(utc))
@pytest.mark.django_db
@@ -195,12 +162,12 @@ def test_availability_date_cart_relative_subevents(event):
prov.settings.set('_availability_date', RelativeDateWrapper(
RelativeDate(days_before=3, time=None, base_date_name='date_from', minutes_before=None)
))
assert prov._is_available_by_time(cart_id="123")
assert prov._is_still_available(cart_id="123")
prov.settings.set('_availability_date', RelativeDateWrapper(
RelativeDate(days_before=4, time=None, base_date_name='date_from', minutes_before=None)
))
assert not prov._is_available_by_time(cart_id="123")
assert not prov._is_still_available(cart_id="123")
@pytest.mark.django_db
@@ -234,9 +201,9 @@ def test_availability_date_order_relative_subevents(event):
prov.settings.set('_availability_date', RelativeDateWrapper(
RelativeDate(days_before=3, time=None, base_date_name='date_from', minutes_before=None)
))
assert prov._is_available_by_time(order=order)
assert prov._is_still_available(order=order)
prov.settings.set('_availability_date', RelativeDateWrapper(
RelativeDate(days_before=4, time=None, base_date_name='date_from', minutes_before=None)
))
assert not prov._is_available_by_time(order=order)
assert not prov._is_still_available(order=order)
+301
View File
@@ -1012,3 +1012,304 @@ def test_available_until(event, item):
)
assert sorted([p for p, d in apply_discounts(event, 'web', positions)]) == [Decimal('50.00'), Decimal('50.00')]
@pytest.mark.django_db
@scopes_disabled()
def test_discount_other_products_min_count(event, item, item2):
# "For every 5 item2, one item1 gets in for free"
d1 = Discount(
event=event,
condition_min_count=5,
condition_all_products=False,
benefit_discount_matching_percent=100,
benefit_only_apply_to_cheapest_n_matches=1,
benefit_same_products=False,
)
d1.save()
d1.condition_limit_products.add(item2)
d1.benefit_limit_products.add(item)
positions = (
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('90.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('90.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('90.00'), False, False, Decimal('0.00')),
)
expected = (
Decimal('100.00'),
Decimal('100.00'),
Decimal('100.00'),
Decimal('100.00'),
Decimal('100.00'),
Decimal('100.00'),
Decimal('100.00'),
Decimal('100.00'),
Decimal('100.00'),
Decimal('100.00'),
Decimal('100.00'),
Decimal('100.00'),
Decimal('90.00'),
Decimal('0.00'),
Decimal('0.00'),
)
new_prices = [p for p, d in apply_discounts(event, 'web', positions)]
assert sorted(new_prices) == sorted(expected)
@pytest.mark.django_db
@scopes_disabled()
def test_discount_other_products_min_count_no_addon(event, item, item2):
# "For every 2 item2, one item1 gets in for free, but no addons"
d1 = Discount(
event=event,
condition_min_count=2,
condition_all_products=False,
benefit_discount_matching_percent=100,
benefit_only_apply_to_cheapest_n_matches=1,
benefit_same_products=False,
benefit_apply_to_addons=False,
)
d1.save()
d1.condition_limit_products.add(item2)
d1.benefit_limit_products.add(item)
positions = (
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('90.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('90.00'), True, False, Decimal('0.00')),
)
expected = (
Decimal('100.00'),
Decimal('100.00'),
Decimal('100.00'),
Decimal('100.00'),
Decimal('90.00'),
Decimal('0.00'),
)
new_prices = [p for p, d in apply_discounts(event, 'web', positions)]
assert sorted(new_prices) == sorted(expected)
@pytest.mark.django_db
@scopes_disabled()
def test_discount_other_products_min_count_no_voucher(event, item, item2):
# "For every 2 item2, one item1 gets in for free, but no discount on already discounted"
d1 = Discount(
event=event,
condition_min_count=2,
condition_all_products=False,
benefit_discount_matching_percent=100,
benefit_only_apply_to_cheapest_n_matches=1,
benefit_same_products=False,
benefit_ignore_voucher_discounted=True,
)
d1.save()
d1.condition_limit_products.add(item2)
d1.benefit_limit_products.add(item)
positions = (
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('40.00'), False, False, Decimal('50.00')),
(item.pk, None, Decimal('40.00'), False, False, Decimal('50.00')),
(item.pk, None, Decimal('90.00'), False, False, Decimal('0.00')),
)
expected = (
Decimal('100.00'),
Decimal('100.00'),
Decimal('100.00'),
Decimal('100.00'),
Decimal('40.00'),
Decimal('40.00'),
Decimal('0.00'),
)
new_prices = [p for p, d in apply_discounts(event, 'web', positions)]
assert sorted(new_prices) == sorted(expected)
@pytest.mark.django_db
@scopes_disabled()
def test_discount_subgroup_cheapest_n_min_count(event, item, item2):
# "For every 4 products, you get one free, but only of type item"
d1 = Discount(
event=event,
condition_min_count=4,
condition_all_products=False,
benefit_discount_matching_percent=100,
benefit_only_apply_to_cheapest_n_matches=1,
benefit_same_products=False,
)
d1.save()
d1.condition_limit_products.add(item)
d1.condition_limit_products.add(item2)
d1.benefit_limit_products.add(item)
positions = (
# 11 items of item2, which contribute to the total count of 15, but do not get reduced
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('100.00'), False, False, Decimal('0.00')),
# 4 items of item, of which 3 of the cheapest will be reduced
(item.pk, None, Decimal('110.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('110.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('90.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('90.00'), False, False, Decimal('0.00')),
)
expected = (
Decimal('100.00'),
Decimal('100.00'),
Decimal('100.00'),
Decimal('100.00'),
Decimal('100.00'),
Decimal('100.00'),
Decimal('100.00'),
Decimal('100.00'),
Decimal('100.00'),
Decimal('100.00'),
Decimal('110.00'),
Decimal('0.00'),
Decimal('0.00'),
Decimal('0.00'),
)
new_prices = [p for p, d in apply_discounts(event, 'web', positions)]
assert sorted(new_prices) == sorted(expected)
@pytest.mark.django_db
@scopes_disabled()
def test_discount_other_products_min_value(event, item, item2):
# "If you buy item1 for at least €99, you get all item2 for 20% off"
d1 = Discount(
event=event,
condition_min_value=99,
condition_all_products=False,
benefit_discount_matching_percent=20,
benefit_same_products=False,
)
d1.save()
d1.condition_limit_products.add(item)
d1.benefit_limit_products.add(item2)
positions = (
(item.pk, None, Decimal('50.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('23.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('23.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('23.00'), False, False, Decimal('0.00')),
)
expected = (
Decimal('50.00'),
Decimal('23.00'),
Decimal('23.00'),
Decimal('23.00'),
)
new_prices = [p for p, d in apply_discounts(event, 'web', positions)]
assert sorted(new_prices) == sorted(expected)
positions = (
(item.pk, None, Decimal('50.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('50.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('23.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('23.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('23.00'), False, False, Decimal('0.00')),
)
expected = (
Decimal('50.00'),
Decimal('50.00'),
Decimal('18.40'),
Decimal('18.40'),
Decimal('18.40'),
)
new_prices = [p for p, d in apply_discounts(event, 'web', positions)]
assert sorted(new_prices) == sorted(expected)
@pytest.mark.django_db
@scopes_disabled()
def test_multiple_discounts_with_benefit_condition_overlap(event, item, item2):
# "For every 5 item2, you get one item1 for 20 % off." + "For every two item1, you get one 10% off."
d1 = Discount(
event=event,
condition_min_count=5,
condition_all_products=False,
benefit_only_apply_to_cheapest_n_matches=1,
benefit_discount_matching_percent=20,
benefit_same_products=False,
position=1,
)
d1.save()
d1.condition_limit_products.add(item2)
d1.benefit_limit_products.add(item)
d2 = Discount(
event=event,
condition_min_count=2,
condition_all_products=False,
benefit_only_apply_to_cheapest_n_matches=1,
benefit_discount_matching_percent=10,
benefit_same_products=True,
position=2,
)
d2.save()
d2.condition_limit_products.add(item)
positions = (
(item2.pk, None, Decimal('50.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('50.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('50.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('50.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('50.00'), False, False, Decimal('0.00')),
(item2.pk, None, Decimal('50.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('23.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('23.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('23.00'), False, False, Decimal('0.00')),
(item.pk, None, Decimal('23.00'), False, False, Decimal('0.00')),
)
expected = (
# item2 remains untouched
Decimal('50.00'),
Decimal('50.00'),
Decimal('50.00'),
Decimal('50.00'),
Decimal('50.00'),
Decimal('50.00'),
# one item is reduced 20% because we have >5 item2
Decimal('18.40'),
# one item is reduced 10% because it's part of a group of two
Decimal('20.70'),
# two remain full price
Decimal('23.00'),
Decimal('23.00'),
)
new_prices = [p for p, d in apply_discounts(event, 'web', positions)]
assert sorted(new_prices) == sorted(expected)
+99 -56
View File
@@ -22,79 +22,122 @@
import pytest
from pretix.base.templatetags.rich_text import (
markdown_compile_email, rich_text, rich_text_snippet,
ALLOWED_ATTRIBUTES, ALLOWED_TAGS, markdown_compile_email, rich_text,
rich_text_snippet,
)
@pytest.mark.parametrize("link", [
# Test link detection
("google.com",
'<a href="http://google.com" rel="noopener" target="_blank">google.com</a>'),
# Test link escaping
("google\\.com", 'google.com'),
# Test abslink_callback
("[Call](tel:+12345)",
'<a href="tel:+12345" rel="nofollow">Call</a>'),
("[Foo](/foo)",
'<a href="http://example.com/foo" rel="noopener" target="_blank">Foo</a>'),
("mail@example.org",
'<a href="mailto:mail@example.org">mail@example.org</a>'),
# Test truelink_callback
('evilsite.com',
'<a href="http://evilsite.com" rel="noopener" target="_blank">evilsite.com</a>'),
('cool-example.eu',
'<a href="http://cool-example.eu" rel="noopener" target="_blank">cool-example.eu</a>'),
('<a href="https://evilsite.com">Evil GmbH & Co. KG</a>',
'<a href="https://evilsite.com" rel="noopener" target="_blank">Evil GmbH &amp; Co. KG</a>'),
('<a href="https://evilsite.com">Evil Site</a>',
'<a href="https://evilsite.com" rel="noopener" target="_blank">Evil Site</a>'),
('<a href="https://evilsite.com">evilsite.com</a>',
'<a href="https://evilsite.com" rel="noopener" target="_blank">evilsite.com</a>'),
('<a href="https://evilsite.com">goodsite.com</a>',
'<a href="https://evilsite.com" rel="noopener" target="_blank">https://evilsite.com</a>'),
('<a href="https://goodsite.com.evilsite.com">goodsite.com</a>',
'<a href="https://goodsite.com.evilsite.com" rel="noopener" target="_blank">https://goodsite.com.evilsite.com</a>'),
('<a href="https://evilsite.com/deep/path">evilsite.com</a>',
'<a href="https://evilsite.com/deep/path" rel="noopener" target="_blank">evilsite.com</a>'),
('<a>broken</a>', '<a>broken</a>'),
])
@pytest.mark.parametrize(
"link",
[
# Test link detection
(
"google.com",
'<a href="http://google.com" rel="noopener" target="_blank">google.com</a>',
),
# Test link escaping
("google\\.com", "google.com"),
# Test abslink_callback
("[Call](tel:+12345)", '<a href="tel:+12345" rel="nofollow">Call</a>'),
(
"[Foo](/foo)",
'<a href="http://example.com/foo" rel="noopener" target="_blank">Foo</a>',
),
("mail@example.org", '<a href="mailto:mail@example.org">mail@example.org</a>'),
# Test truelink_callback
(
"evilsite.com",
'<a href="http://evilsite.com" rel="noopener" target="_blank">evilsite.com</a>',
),
(
"cool-example.eu",
'<a href="http://cool-example.eu" rel="noopener" target="_blank">cool-example.eu</a>',
),
(
'<a href="https://evilsite.com">Evil GmbH & Co. KG</a>',
'<a href="https://evilsite.com" rel="noopener" target="_blank">Evil GmbH &amp; Co. KG</a>',
),
(
'<a href="https://evilsite.com">Evil Site</a>',
'<a href="https://evilsite.com" rel="noopener" target="_blank">Evil Site</a>',
),
(
'<a href="https://evilsite.com">evilsite.com</a>',
'<a href="https://evilsite.com" rel="noopener" target="_blank">evilsite.com</a>',
),
(
'<a href="https://evilsite.com">goodsite.com</a>',
'<a href="https://evilsite.com" rel="noopener" target="_blank">https://evilsite.com</a>',
),
(
'<a href="https://goodsite.com.evilsite.com">goodsite.com</a>',
'<a href="https://goodsite.com.evilsite.com" rel="noopener" target="_blank">https://goodsite.com.evilsite.com</a>',
),
(
'<a href="https://evilsite.com/deep/path">evilsite.com</a>',
'<a href="https://evilsite.com/deep/path" rel="noopener" target="_blank">evilsite.com</a>',
),
("<a>broken</a>", "<a>broken</a>"),
],
)
def test_linkify_abs(link):
input, output = link
assert rich_text_snippet(input, safelinks=False) == output
assert rich_text(input, safelinks=False) == f'<p>{output}</p>'
assert markdown_compile_email(input) == f'<p>{output}</p>'
assert rich_text(input, safelinks=False) == f"<p>{output}</p>"
assert markdown_compile_email(input) == f"<p>{output}</p>"
@pytest.mark.parametrize("content,result", [
('a\nb', '<p>a<br>\nb</p>'),
('a \nb', '<p>a<br>\nb</p>'),
('a\n\nb', '<p>a</p>\n<p>b</p>'),
])
@pytest.mark.parametrize(
"content,result",
[
("a\nb", "<p>a<br>\nb</p>"),
("a \nb", "<p>a<br>\nb</p>"),
("a\n\nb", "<p>a</p>\n<p>b</p>"),
],
)
def test_newline_handling(content, result):
assert rich_text(content, safelinks=False) == result
@pytest.mark.parametrize("content,result", [
('a\nb', '<p>a\nb</p>'),
('a \nb', '<p>a<br>\nb</p>'),
('a\n\nb', '<p>a</p>\n<p>b</p>'),
])
@pytest.mark.parametrize(
"content,result",
[
("a\nb", "<p>a\nb</p>"),
("a \nb", "<p>a<br>\nb</p>"),
("a\n\nb", "<p>a</p>\n<p>b</p>"),
],
)
def test_newline_handling_email(content, result):
assert markdown_compile_email(content) == result
@pytest.mark.parametrize("content,result,result_snippet", [
# attributes
('<a onclick="javascript:foo()">foo</a>', '<p><a>foo</a></p>', '<a>foo</a>'),
('<strong color="red">foo</strong>',
'<p><strong>foo</strong></p>',
'<strong>foo</strong>'),
# protocols
('<a href="javascript:foo()">foo</a>', '<p><a>foo</a></p>', '<a>foo</a>'),
# tags
('<script>foo</script>', '&lt;script&gt;foo&lt;/script&gt;', 'foo'),
])
@pytest.mark.parametrize(
"content,result,result_snippet",
[
# attributes
('<a onclick="javascript:foo()">foo</a>', "<p><a>foo</a></p>", "<a>foo</a>"),
(
'<strong color="red">foo</strong>',
"<p><strong>foo</strong></p>",
"<strong>foo</strong>",
),
# protocols
('<a href="javascript:foo()">foo</a>', "<p><a>foo</a></p>", "<a>foo</a>"),
# tags
("<script>foo</script>", "&lt;script&gt;foo&lt;/script&gt;", "foo"),
],
)
def test_cleanup(content, result, result_snippet):
assert rich_text(content) == result
assert rich_text_snippet(content) == result_snippet
assert markdown_compile_email(content) == result
def test_markdown_email_custom_allowlist():
source = "![my image](https://example.org/my-image.jpg)"
html = markdown_compile_email(
source,
allowed_tags=ALLOWED_TAGS + ["img"],
allowed_attributes=dict(ALLOWED_ATTRIBUTES, img=["src", "alt", "title"]),
)
assert html == '<p><img alt="my image" src="https://example.org/my-image.jpg"></p>'