mirror of
https://github.com/pretix/pretix.git
synced 2026-03-06 11:22:27 +00:00
Compare commits
47 Commits
reusableme
...
timemachin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12ba49e263 | ||
|
|
e37b032d4e | ||
|
|
3c3b5529bf | ||
|
|
87b3e0c417 | ||
|
|
d3fd031639 | ||
|
|
9253327334 | ||
|
|
080b9cacaf | ||
|
|
9c2cc02df1 | ||
|
|
fceae0a2fe | ||
|
|
9fc3fdf751 | ||
|
|
04f79b7014 | ||
|
|
9d0b9387e6 | ||
|
|
b25e6f598d | ||
|
|
e8e2648f7e | ||
|
|
e0fac42225 | ||
|
|
3e9bc7675b | ||
|
|
1541033467 | ||
|
|
6b8c3ef15c | ||
|
|
135e07c183 | ||
|
|
fe97915b36 | ||
|
|
233281cea4 | ||
|
|
0300a44634 | ||
|
|
449d930565 | ||
|
|
49f49bd8a6 | ||
|
|
e896704fe0 | ||
|
|
cfee402a27 | ||
|
|
f8878e53a3 | ||
|
|
fd6a342bc6 | ||
|
|
865433276e | ||
|
|
f616f64f47 | ||
|
|
26550887b7 | ||
|
|
0f3de911b8 | ||
|
|
b648390dbf | ||
|
|
50fec0b31c | ||
|
|
e44af04e43 | ||
|
|
276c3177f5 | ||
|
|
27ac004a0b | ||
|
|
6d517d4e8d | ||
|
|
d9c3deda8a | ||
|
|
fe6add618a | ||
|
|
3615a52cc4 | ||
|
|
e3ae3b08bd | ||
|
|
959e926a67 | ||
|
|
876ddf1321 | ||
|
|
005b1d54d3 | ||
|
|
2066471086 | ||
|
|
a25bca7471 |
@@ -192,7 +192,7 @@ Cart position endpoints
|
||||
* ``attendee_email`` (optional)
|
||||
* ``subevent`` (optional)
|
||||
* ``expires`` (optional)
|
||||
* ``includes_tax`` (optional, **DEPRECATED**, do not use, will be removed)
|
||||
* ``includes_tax`` (optional, **deprecated**, do not use, will be removed)
|
||||
* ``sales_channel`` (optional)
|
||||
* ``voucher`` (optional, expect a voucher code)
|
||||
* ``addons`` (optional, expect a list of nested objects of cart positions)
|
||||
|
||||
@@ -1066,7 +1066,6 @@ Creating orders
|
||||
* ``valid_until`` (optional, if both ``valid_from`` and ``valid_until`` are **missing** (not ``null``) the availability will be computed from the given product)
|
||||
* ``requested_valid_from`` (optional, can be set **instead** of ``valid_from`` and ``valid_until`` to signal a user choice for the start time that may or may not be respected)
|
||||
* ``use_reusable_medium`` (optional, causes the new ticket to take over the given reusable medium, identified by its ID)
|
||||
* ``add_to_reusable_medium`` (optional, causes the new ticket to be added to the given reusable medium, identified by its ID)
|
||||
* ``discount`` (optional, only possible if ``price`` is set; attention: if this is set to not-``null`` on any position, automatic calculation of discounts will not run)
|
||||
* ``answers``
|
||||
|
||||
|
||||
@@ -21,16 +21,12 @@ id integer Internal ID of
|
||||
type string Type of medium, e.g. ``"barcode"``, ``"nfc_uid"`` or ``"nfc_mf0aes"``.
|
||||
organizer string Organizer slug of the organizer who "owns" this medium.
|
||||
identifier string Unique identifier of the medium. The format depends on the ``type``.
|
||||
claim_token string Secret token to claim ownership of the medium (or ``null``)
|
||||
label string Label to identify the medium, usually something human readable (or ``null``)
|
||||
active boolean Whether this medium may be used.
|
||||
created datetime Date of creation
|
||||
updated datetime Date of last modification
|
||||
expires datetime Expiry date (or ``null``)
|
||||
customer string Identifier of a customer account this medium belongs to.
|
||||
linked_orderpositions list of integer Internal IDs of tickets this medium is linked to.
|
||||
linked_orderposition integer **DEPRECATED.** ID of the ticket the medium is linked to, if it is linked to
|
||||
only one ticket. ``null``, if the medium is linked to none or multiple tickets.
|
||||
linked_orderposition integer Internal ID of a ticket this medium is linked to.
|
||||
linked_giftcard integer Internal ID of a gift card this medium is linked to.
|
||||
info object Additional data, content depends on the ``type``. Consider
|
||||
this internal to the system and don't use it for your own data.
|
||||
@@ -43,14 +39,6 @@ Existing media types are:
|
||||
- ``nfc_uid``
|
||||
- ``nfc_mf0aes``
|
||||
|
||||
|
||||
.. versionchanged:: 2025.11
|
||||
|
||||
The ``claim_token``, ``label``, ``linked_orderpositions`` attributes have been added, the ``linked_orderposition`` attribute has been
|
||||
deprecated. Note: To maintain backwards compatibility ``linked_orderposition`` contains the internal ID of the linked order position
|
||||
if the medium has exactly one order position in ``linked_orderpositions``.
|
||||
|
||||
|
||||
Endpoints
|
||||
---------
|
||||
|
||||
@@ -89,7 +77,6 @@ Endpoints
|
||||
"active": True,
|
||||
"expires": None,
|
||||
"customer": None,
|
||||
"linked_orderpositions": [],
|
||||
"linked_orderposition": None,
|
||||
"linked_giftcard": None,
|
||||
"notes": None,
|
||||
@@ -105,13 +92,10 @@ Endpoints
|
||||
:query string customer: Only show media linked to the given customer.
|
||||
:query string created_since: Only show media created since a given date.
|
||||
:query string updated_since: Only show media updated since a given date.
|
||||
:query integer linked_orderpositions: Only show media linked to the given tickets. Note: you can pass multiple ticket IDs by passing
|
||||
``linked_orderpositions`` multiple times. Any medium matching any linked orderposition will be returned.
|
||||
:query integer linked_orderposition: Only show media linked to the given ticket.
|
||||
:query integer linked_giftcard: Only show media linked to the given gift card.
|
||||
:query string expand: If you pass ``"linked_giftcard"``, ``"linked_giftcard.owner_ticket"``, ``"linked_orderpositions"``,
|
||||
``"linked_orderposition"`` (**DEPRECATED**), or ``"customer"``, the respective field will be shown
|
||||
as a nested value instead of just an ID.
|
||||
:query string expand: If you pass ``"linked_giftcard"``, ``"linked_giftcard.owner_ticket"``, ``"linked_orderposition"``,
|
||||
or ``"customer"``, the respective field will be shown as a nested value instead of just an ID.
|
||||
The nested objects are identical to the respective resources, except that order positions
|
||||
will have an attribute of the format ``"order": {"code": "ABCDE", "event": "eventslug"}`` to make
|
||||
matching easier. The parameter can be given multiple times.
|
||||
@@ -150,7 +134,6 @@ Endpoints
|
||||
"active": True,
|
||||
"expires": None,
|
||||
"customer": None,
|
||||
"linked_orderpositions": [],
|
||||
"linked_orderposition": None,
|
||||
"linked_giftcard": None,
|
||||
"notes": None,
|
||||
@@ -208,7 +191,6 @@ Endpoints
|
||||
"active": True,
|
||||
"expires": None,
|
||||
"customer": None,
|
||||
"linked_orderpositions": [],
|
||||
"linked_orderposition": None,
|
||||
"linked_giftcard": None,
|
||||
"notes": None,
|
||||
@@ -216,9 +198,9 @@ Endpoints
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to look up a medium for
|
||||
:query string expand: If you pass ``"linked_giftcard"``, ``"linked_orderpositions"``, or ``"customer"``, the respective
|
||||
:query string expand: If you pass ``"linked_giftcard"``, ``"linked_orderposition"``, oder ``"customer"``, the respective
|
||||
field will be shown as a nested value instead of just an ID. The nested objects are identical to
|
||||
the respective resources, except that the ``linked_orderpositions`` each will have an attribute of the
|
||||
the respective resources, except that the ``linked_orderposition`` will have an attribute of the
|
||||
format ``"order": {"code": "ABCDE", "event": "eventslug"}`` to make matching easier. The parameter
|
||||
can be given multiple times.
|
||||
:statuscode 201: no error
|
||||
@@ -245,7 +227,6 @@ Endpoints
|
||||
"active": True,
|
||||
"expires": None,
|
||||
"customer": None,
|
||||
"linked_orderpositions": [],
|
||||
"linked_orderposition": None,
|
||||
"linked_giftcard": None,
|
||||
"notes": None,
|
||||
@@ -270,7 +251,6 @@ Endpoints
|
||||
"active": True,
|
||||
"expires": None,
|
||||
"customer": None,
|
||||
"linked_orderpositions": [],
|
||||
"linked_orderposition": None,
|
||||
"linked_giftcard": None,
|
||||
"notes": None,
|
||||
@@ -278,7 +258,7 @@ Endpoints
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to create a medium for
|
||||
:query string expand: If you pass ``"linked_giftcard"``, ``"linked_orderpositions"``, or ``"customer"``, the respective
|
||||
:query string expand: If you pass ``"linked_giftcard"``, ``"linked_orderposition"``, oder ``"customer"``, the respective
|
||||
field will be shown as a nested value instead of just an ID. The nested objects are identical to
|
||||
the respective resources, except that the ``linked_orderposition`` will have an attribute of the
|
||||
format ``"order": {"code": "ABCDE", "event": "eventslug"}`` to make matching easier. The parameter
|
||||
@@ -307,7 +287,7 @@ Endpoints
|
||||
Content-Length: 94
|
||||
|
||||
{
|
||||
"linked_orderpositions": [13, 29]
|
||||
"linked_orderposition": 13
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
@@ -328,8 +308,7 @@ Endpoints
|
||||
"active": True,
|
||||
"expires": None,
|
||||
"customer": None,
|
||||
"linked_orderpositions": [13, 29],
|
||||
"linked_orderposition": None,
|
||||
"linked_orderposition": 13,
|
||||
"linked_giftcard": None,
|
||||
"notes": None,
|
||||
"info": {}
|
||||
@@ -337,7 +316,7 @@ Endpoints
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to modify
|
||||
:param id: The ``id`` field of the medium to modify
|
||||
:query string expand: If you pass ``"linked_giftcard"``, ``"linked_orderpositions"``, or ``"customer"``, the respective
|
||||
:query string expand: If you pass ``"linked_giftcard"``, ``"linked_orderposition"``, oder ``"customer"``, the respective
|
||||
field will be shown as a nested value instead of just an ID. The nested objects are identical to
|
||||
the respective resources, except that the ``linked_orderposition`` will have an attribute of the
|
||||
format ``"order": {"code": "ABCDE", "event": "eventslug"}`` to make matching easier. The parameter
|
||||
|
||||
@@ -65,11 +65,9 @@ class ReusableMediaSerializer(I18nAwareModelSerializer):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
expand_nested = self.context['request'].query_params.getlist('expand')
|
||||
|
||||
if 'linked_giftcard' in expand_nested:
|
||||
if 'linked_giftcard' in self.context['request'].query_params.getlist('expand'):
|
||||
self.fields['linked_giftcard'] = NestedGiftCardSerializer(read_only=True, context=self.context)
|
||||
if 'linked_giftcard.owner_ticket' in expand_nested:
|
||||
if 'linked_giftcard.owner_ticket' in self.context['request'].query_params.getlist('expand'):
|
||||
self.fields['linked_giftcard'].fields['owner_ticket'] = NestedOrderPositionSerializer(read_only=True, context=self.context)
|
||||
else:
|
||||
self.fields['linked_giftcard'] = serializers.PrimaryKeyRelatedField(
|
||||
@@ -78,27 +76,16 @@ class ReusableMediaSerializer(I18nAwareModelSerializer):
|
||||
queryset=self.context['organizer'].issued_gift_cards.all()
|
||||
)
|
||||
|
||||
# keep linked_orderposition (singular) for backwards compatibility, will be overwritten in self.validate
|
||||
self.fields['linked_orderposition'] = serializers.PrimaryKeyRelatedField(
|
||||
required=False,
|
||||
allow_null=True,
|
||||
queryset=OrderPosition.all.filter(order__event__organizer=self.context['organizer']),
|
||||
)
|
||||
|
||||
if 'linked_orderposition' in expand_nested or 'linked_orderpositions' in expand_nested:
|
||||
self.fields['linked_orderpositions'] = NestedOrderPositionSerializer(
|
||||
many=True,
|
||||
read_only=True
|
||||
)
|
||||
if 'linked_orderposition' in self.context['request'].query_params.getlist('expand'):
|
||||
self.fields['linked_orderposition'] = NestedOrderPositionSerializer(read_only=True)
|
||||
else:
|
||||
self.fields['linked_orderpositions'] = serializers.PrimaryKeyRelatedField(
|
||||
many=True,
|
||||
self.fields['linked_orderposition'] = serializers.PrimaryKeyRelatedField(
|
||||
required=False,
|
||||
allow_null=True,
|
||||
queryset=OrderPosition.all.filter(order__event__organizer=self.context['organizer']),
|
||||
)
|
||||
|
||||
if 'customer' in expand_nested:
|
||||
if 'customer' in self.context['request'].query_params.getlist('expand'):
|
||||
self.fields['customer'] = CustomerSerializer(read_only=True)
|
||||
else:
|
||||
self.fields['customer'] = serializers.SlugRelatedField(
|
||||
@@ -110,20 +97,6 @@ class ReusableMediaSerializer(I18nAwareModelSerializer):
|
||||
|
||||
def validate(self, data):
|
||||
data = super().validate(data)
|
||||
if 'linked_orderposition' in data:
|
||||
linked_orderposition = data['linked_orderposition']
|
||||
# backwards-compatibility
|
||||
if 'linked_orderpositions' in data:
|
||||
raise ValidationError({
|
||||
'linked_orderposition': _('You cannot use linked_orderposition and linked_orderpositions at the same time.')
|
||||
})
|
||||
if self.instance.linked_orderpositions.count() > 1:
|
||||
raise ValidationError({
|
||||
'linked_orderposition': _('There are more than one linked_orderposition. You need to use linked_orderpositions.')
|
||||
})
|
||||
|
||||
data['linked_orderpositions'] = [linked_orderposition] if linked_orderposition else []
|
||||
|
||||
if 'type' in data and 'identifier' in data:
|
||||
qs = self.context['organizer'].reusable_media.filter(
|
||||
identifier=data['identifier'], type=data['type']
|
||||
@@ -136,14 +109,6 @@ class ReusableMediaSerializer(I18nAwareModelSerializer):
|
||||
)
|
||||
return data
|
||||
|
||||
def to_representation(self, obj):
|
||||
r = super(ReusableMediaSerializer, self).to_representation(obj)
|
||||
ops = r.get('linked_orderpositions')
|
||||
if len(ops) < 2:
|
||||
# add linked_orderposition (singular) for backwards compatibility
|
||||
r['linked_orderposition'] = ops[0] if ops else None
|
||||
return r
|
||||
|
||||
class Meta:
|
||||
model = ReusableMedium
|
||||
fields = (
|
||||
@@ -153,12 +118,10 @@ class ReusableMediaSerializer(I18nAwareModelSerializer):
|
||||
'updated',
|
||||
'type',
|
||||
'identifier',
|
||||
'claim_token',
|
||||
'label',
|
||||
'active',
|
||||
'expires',
|
||||
'customer',
|
||||
'linked_orderpositions',
|
||||
'linked_orderposition',
|
||||
'linked_giftcard',
|
||||
'info',
|
||||
'notes',
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from collections import Counter, defaultdict
|
||||
@@ -1029,15 +1030,13 @@ class OrderPositionCreateSerializer(I18nAwareModelSerializer):
|
||||
requested_valid_from = serializers.DateTimeField(required=False, allow_null=True)
|
||||
use_reusable_medium = serializers.PrimaryKeyRelatedField(queryset=ReusableMedium.objects.none(),
|
||||
required=False, allow_null=True)
|
||||
add_to_reusable_medium = serializers.PrimaryKeyRelatedField(queryset=ReusableMedium.objects.none(),
|
||||
required=False, allow_null=True)
|
||||
|
||||
class Meta:
|
||||
model = OrderPosition
|
||||
fields = ('positionid', 'item', 'variation', 'price', 'attendee_name', 'attendee_name_parts', 'attendee_email',
|
||||
'company', 'street', 'zipcode', 'city', 'country', 'state', 'is_bundled',
|
||||
'secret', 'addon_to', 'subevent', 'answers', 'seat', 'voucher', 'valid_from', 'valid_until',
|
||||
'requested_valid_from', 'use_reusable_medium', 'add_to_reusable_medium', 'discount')
|
||||
'requested_valid_from', 'use_reusable_medium', 'discount')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -1049,8 +1048,6 @@ class OrderPositionCreateSerializer(I18nAwareModelSerializer):
|
||||
with scopes_disabled():
|
||||
if 'use_reusable_medium' in self.fields:
|
||||
self.fields['use_reusable_medium'].queryset = ReusableMedium.objects.all()
|
||||
if 'add_to_reusable_medium' in self.fields:
|
||||
self.fields['add_to_reusable_medium'].queryset = ReusableMedium.objects.all()
|
||||
|
||||
def validate_secret(self, secret):
|
||||
if secret and OrderPosition.all.filter(order__event=self.context['event'], secret=secret).exists():
|
||||
@@ -1066,9 +1063,6 @@ class OrderPositionCreateSerializer(I18nAwareModelSerializer):
|
||||
)
|
||||
return m
|
||||
|
||||
def validate_add_to_reusable_medium(self, m):
|
||||
return self.validate_use_reusable_medium(m)
|
||||
|
||||
def validate_item(self, item):
|
||||
if item.event != self.context['event']:
|
||||
raise ValidationError(
|
||||
@@ -1142,13 +1136,6 @@ class OrderPositionCreateSerializer(I18nAwareModelSerializer):
|
||||
raise ValidationError(
|
||||
{'discount': ['You can only specify a discount if you do the price computation, but price is not set.']}
|
||||
)
|
||||
|
||||
if 'use_reusable_medium' in data and 'add_to_reusable_medium' in data:
|
||||
raise ValidationError({
|
||||
'use_reusable_medium': ['You can only specify either use_reusable_medium or add_to_reusable_medium.'],
|
||||
'add_to_reusable_medium': ['You can only specify either use_reusable_medium or add_to_reusable_medium.'],
|
||||
})
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@@ -1229,6 +1216,18 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
|
||||
raise ValidationError('The given payment provider is not known.')
|
||||
return pp
|
||||
|
||||
def validate_payment_info(self, info):
|
||||
if info:
|
||||
try:
|
||||
obj = json.loads(info)
|
||||
except ValueError:
|
||||
raise ValidationError('payment_info must be valid JSON.')
|
||||
|
||||
if not isinstance(obj, dict):
|
||||
# only objects are allowed
|
||||
raise ValidationError('payment_info must be a JSON object.')
|
||||
return info
|
||||
|
||||
def validate_expires(self, expires):
|
||||
if expires < now():
|
||||
raise ValidationError('Expiration date must be in the future.')
|
||||
@@ -1564,7 +1563,7 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
|
||||
pos_data['attendee_name_parts'] = {
|
||||
'_legacy': attendee_name
|
||||
}
|
||||
pos = OrderPosition(**{k: v for k, v in pos_data.items() if k not in ('answers', '_quotas', 'use_reusable_medium', 'add_to_reusable_medium')})
|
||||
pos = OrderPosition(**{k: v for k, v in pos_data.items() if k != 'answers' and k != '_quotas' and k != 'use_reusable_medium'})
|
||||
if simulate:
|
||||
pos.order = order._wrapped
|
||||
else:
|
||||
@@ -1638,7 +1637,6 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
|
||||
for pos_data in positions_data:
|
||||
answers_data = pos_data.pop('answers', [])
|
||||
use_reusable_medium = pos_data.pop('use_reusable_medium', None)
|
||||
add_to_reusable_medium = pos_data.pop('add_to_reusable_medium', None)
|
||||
pos = pos_data['__instance']
|
||||
pos._calculate_tax(invoice_address=ia)
|
||||
|
||||
@@ -1680,7 +1678,8 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
|
||||
answ.options.add(*options)
|
||||
|
||||
if use_reusable_medium:
|
||||
use_reusable_medium.linked_orderpositions.set([pos])
|
||||
use_reusable_medium.linked_orderposition = pos
|
||||
use_reusable_medium.save(update_fields=['linked_orderposition'])
|
||||
use_reusable_medium.log_action(
|
||||
'pretix.reusable_medium.linked_orderposition.changed',
|
||||
data={
|
||||
@@ -1688,15 +1687,6 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
|
||||
'linked_orderposition': pos.pk,
|
||||
}
|
||||
)
|
||||
if add_to_reusable_medium:
|
||||
add_to_reusable_medium.linked_orderpositions.add(pos)
|
||||
add_to_reusable_medium.log_action(
|
||||
'pretix.reusable_medium.linked_orderposition.added',
|
||||
data={
|
||||
'by_order': order.code,
|
||||
'linked_orderposition': pos.pk,
|
||||
}
|
||||
)
|
||||
|
||||
if not simulate:
|
||||
for cp in delete_cps:
|
||||
|
||||
@@ -365,9 +365,10 @@ class TeamInviteSerializer(serializers.ModelSerializer):
|
||||
def _send_invite(self, instance):
|
||||
mail(
|
||||
instance.email,
|
||||
_('pretix account invitation'),
|
||||
_('Account invitation'),
|
||||
'pretixcontrol/email/invitation.txt',
|
||||
{
|
||||
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||
'user': self,
|
||||
'organizer': self.context['organizer'].name,
|
||||
'team': instance.team.name,
|
||||
|
||||
@@ -520,13 +520,11 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
|
||||
# with respecting the force option), or it's a reusable medium (-> proceed with that)
|
||||
if not op_candidates:
|
||||
try:
|
||||
media = ReusableMedium.objects.active().annotate(
|
||||
has_linked_orderpositions=Exists(ReusableMedium.linked_orderpositions.through.objects.filter(reusablemedium_id=OuterRef('pk')))
|
||||
).get(
|
||||
media = ReusableMedium.objects.select_related('linked_orderposition').active().get(
|
||||
organizer_id=checkinlists[0].event.organizer_id,
|
||||
type=source_type,
|
||||
identifier=raw_barcode,
|
||||
has_linked_orderpositions=True,
|
||||
linked_orderposition__isnull=False,
|
||||
)
|
||||
raw_barcode_for_checkin = raw_barcode
|
||||
except ReusableMedium.DoesNotExist:
|
||||
@@ -629,8 +627,7 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
|
||||
'list': MiniCheckinListSerializer(list_by_event[revoked_matches[0].event_id]).data,
|
||||
}, status=400)
|
||||
else:
|
||||
linked_event_ids = media.linked_orderpositions.values_list("order__event_id", flat=True).order_by().distinct()
|
||||
if not any(event_id in list_by_event for event_id in linked_event_ids):
|
||||
if media.linked_orderposition.order.event_id not in list_by_event:
|
||||
# Medium exists but connected ticket is for the wrong event
|
||||
if not simulate:
|
||||
checkinlists[0].event.log_action('pretix.event.checkin.unknown', data={
|
||||
@@ -656,34 +653,21 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
|
||||
'checkin_texts': [],
|
||||
'list': MiniCheckinListSerializer(checkinlists[0]).data,
|
||||
}, status=404)
|
||||
op_candidates = []
|
||||
for op in media.linked_orderpositions.all().select_related("order"):
|
||||
op_candidates.append(op)
|
||||
if list_by_event[op.order.event_id].addon_match:
|
||||
op_candidates += list(op.addons.all())
|
||||
op_candidates = [media.linked_orderposition]
|
||||
if list_by_event[media.linked_orderposition.order.event_id].addon_match:
|
||||
op_candidates += list(media.linked_orderposition.addons.all())
|
||||
|
||||
# 3. Handle the "multiple options found" case: Except for the unlikely case of a secret being also a valid primary
|
||||
# key on the same list, we're probably dealing with the ``addon_match`` case here and need to figure out
|
||||
# which add-on has the right product.
|
||||
if len(op_candidates) > 1:
|
||||
# only check addons if at most one non-addon-op is in op_candidates
|
||||
# otherwise it is likely a medium linked to multiple orderpositions, which we need to filter based on validity
|
||||
if len([op for op in op_candidates if not op.addon_to]) <= 1:
|
||||
op_candidates_matching_product = [
|
||||
op for op in op_candidates
|
||||
if (
|
||||
(list_by_event[op.order.event_id].addon_match or op.secret == raw_barcode or legacy_url_support) and
|
||||
(list_by_event[op.order.event_id].all_products or op.item_id in {i.pk for i in list_by_event[op.order.event_id].limit_products.all()})
|
||||
)
|
||||
]
|
||||
else:
|
||||
op_candidates_matching_product = [
|
||||
op for op in op_candidates
|
||||
if (
|
||||
(not op.valid_from or op.valid_from < now()) and
|
||||
(not op.valid_until or op.valid_until > now())
|
||||
)
|
||||
]
|
||||
op_candidates_matching_product = [
|
||||
op for op in op_candidates
|
||||
if (
|
||||
(list_by_event[op.order.event_id].addon_match or op.secret == raw_barcode or legacy_url_support) and
|
||||
(list_by_event[op.order.event_id].all_products or op.item_id in {i.pk for i in list_by_event[op.order.event_id].limit_products.all()})
|
||||
)
|
||||
]
|
||||
|
||||
if len(op_candidates_matching_product) == 0:
|
||||
# None of the found add-ons has the correct product, too bad! We could just error out here, but
|
||||
|
||||
@@ -53,12 +53,10 @@ with scopes_disabled():
|
||||
customer = django_filters.CharFilter(field_name='customer__identifier')
|
||||
updated_since = django_filters.IsoDateTimeFilter(field_name='updated', lookup_expr='gte')
|
||||
created_since = django_filters.IsoDateTimeFilter(field_name='created', lookup_expr='gte')
|
||||
# backwards-compatible
|
||||
linked_orderposition = django_filters.NumberFilter(field_name='linked_orderpositions__id')
|
||||
|
||||
class Meta:
|
||||
model = ReusableMedium
|
||||
fields = ['identifier', 'type', 'active', 'customer', 'linked_orderpositions', 'linked_giftcard']
|
||||
fields = ['identifier', 'type', 'active', 'customer', 'linked_orderposition', 'linked_giftcard']
|
||||
|
||||
|
||||
class ReusableMediaViewSet(viewsets.ModelViewSet):
|
||||
@@ -77,7 +75,7 @@ class ReusableMediaViewSet(viewsets.ModelViewSet):
|
||||
).order_by().values('card').annotate(s=Sum('value')).values('s')
|
||||
return self.request.organizer.reusable_media.prefetch_related(
|
||||
Prefetch(
|
||||
'linked_orderpositions',
|
||||
'linked_orderposition',
|
||||
queryset=OrderPosition.objects.select_related(
|
||||
'order', 'order__event', 'order__event__organizer', 'seat',
|
||||
).prefetch_related(
|
||||
@@ -157,6 +155,7 @@ class ReusableMediaViewSet(viewsets.ModelViewSet):
|
||||
type=s.validated_data["type"],
|
||||
identifier=s.validated_data["identifier"],
|
||||
)
|
||||
m.linked_orderposition = None # not relevant for cross-organizer
|
||||
m.customer = None # not relevant for cross-organizer
|
||||
s = self.get_serializer(m)
|
||||
return Response({"result": s.data})
|
||||
|
||||
@@ -193,7 +193,7 @@ with scopes_disabled():
|
||||
)
|
||||
).values('id')
|
||||
|
||||
matching_media = ReusableMedium.objects.filter(identifier=u).values_list('linked_orderpositions__order_id', flat=True)
|
||||
matching_media = ReusableMedium.objects.filter(identifier=u).values_list('linked_orderposition__order_id', flat=True)
|
||||
|
||||
mainq = (
|
||||
code
|
||||
@@ -1030,7 +1030,7 @@ with scopes_disabled():
|
||||
search = django_filters.CharFilter(method='search_qs')
|
||||
|
||||
def search_qs(self, queryset, name, value):
|
||||
matching_media = ReusableMedium.objects.filter(identifier=value).values_list('linked_orderpositions', flat=True)
|
||||
matching_media = ReusableMedium.objects.filter(identifier=value).values_list('linked_orderposition', flat=True)
|
||||
return queryset.filter(
|
||||
Q(secret__istartswith=value)
|
||||
| Q(attendee_name_cached__icontains=value)
|
||||
|
||||
@@ -20,13 +20,12 @@
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
from django.db.models import Prefetch
|
||||
from django.dispatch import receiver
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.translation import gettext_lazy as _, pgettext, pgettext_lazy
|
||||
|
||||
from ..exporter import ListExporter, OrganizerLevelExportMixin
|
||||
from ..models import OrderPosition, ReusableMedium
|
||||
from ..models import ReusableMedium
|
||||
from ..signals import register_multievent_data_exporters
|
||||
|
||||
|
||||
@@ -41,9 +40,7 @@ class ReusableMediaExporter(OrganizerLevelExportMixin, ListExporter):
|
||||
media = ReusableMedium.objects.filter(
|
||||
organizer=self.organizer,
|
||||
).select_related(
|
||||
'customer', 'linked_giftcard',
|
||||
).prefetch_related(
|
||||
Prefetch('linked_orderpositions', queryset=OrderPosition.objects.select_related("order"))
|
||||
'customer', 'linked_orderposition', 'linked_giftcard',
|
||||
).order_by('created')
|
||||
|
||||
headers = [
|
||||
@@ -61,16 +58,17 @@ class ReusableMediaExporter(OrganizerLevelExportMixin, ListExporter):
|
||||
yield self.ProgressSetTotal(total=media.count())
|
||||
|
||||
for medium in media.iterator(chunk_size=1000):
|
||||
yield [
|
||||
row = [
|
||||
medium.type,
|
||||
medium.identifier,
|
||||
_('Yes') if medium.active else _('No'),
|
||||
date_format(medium.expires, 'SHORT_DATETIME_FORMAT') if medium.expires else '',
|
||||
medium.customer.identifier if medium.customer_id else '',
|
||||
', '.join([f"{op.order.code}-{op.positionid}" for op in medium.linked_orderpositions.all()]),
|
||||
f"{medium.linked_orderposition.order.code}-{medium.linked_orderposition.positionid}" if medium.linked_orderposition_id else '',
|
||||
medium.linked_giftcard.secret if medium.linked_giftcard_id else '',
|
||||
medium.notes,
|
||||
]
|
||||
yield row
|
||||
|
||||
def get_filename(self):
|
||||
return f'{self.organizer.slug}_media'
|
||||
|
||||
@@ -42,6 +42,8 @@ from django.utils.html import escape
|
||||
from django.utils.timezone import get_current_timezone, now
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from pretix.helpers.format import PlainHtmlAlternativeString
|
||||
|
||||
|
||||
def replace_arabic_numbers(inp):
|
||||
if not isinstance(inp, str):
|
||||
@@ -61,11 +63,18 @@ def replace_arabic_numbers(inp):
|
||||
return inp.translate(table)
|
||||
|
||||
|
||||
def format_placeholder_help_text(placeholder_name, sample_value):
|
||||
if isinstance(sample_value, PlainHtmlAlternativeString):
|
||||
sample_value = sample_value.plain
|
||||
title = (_("Sample: %s") % sample_value) if sample_value else ""
|
||||
return ('<button type="button" class="content-placeholder" title="%s">{%s}</button>' % (escape(title), escape(placeholder_name)))
|
||||
|
||||
|
||||
def format_placeholders_help_text(placeholders, event=None):
|
||||
placeholders = [(k, v.render_sample(event) if event else v) for k, v in placeholders.items()]
|
||||
placeholders.sort(key=lambda x: x[0])
|
||||
phs = [
|
||||
'<button type="button" class="content-placeholder" title="%s">{%s}</button>' % (escape(_("Sample: %s") % v) if v else "", escape(k))
|
||||
format_placeholder_help_text(k, v)
|
||||
for k, v in placeholders
|
||||
]
|
||||
return _('Available placeholders: {list}').format(
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
# Generated by Django 4.2.26 on 2025-11-24 11:32
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("pretixbase", "0297_outgoingmail"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="reusablemedium",
|
||||
name="claim_token",
|
||||
field=models.CharField(max_length=200, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="reusablemedium",
|
||||
name="label",
|
||||
field=models.CharField(max_length=200, null=True),
|
||||
),
|
||||
# use temporary related_name "linked_mediums" for ManyToManyField, so we can migrate existing data
|
||||
migrations.AddField(
|
||||
model_name="reusablemedium",
|
||||
name="linked_orderpositions",
|
||||
field=models.ManyToManyField(
|
||||
related_name="linked_mediums", to="pretixbase.orderposition"
|
||||
),
|
||||
),
|
||||
migrations.RunSQL(
|
||||
sql="INSERT INTO pretixbase_reusablemedium_linked_orderpositions (reusablemedium_id, orderposition_id) SELECT id, linked_orderposition_id FROM pretixbase_reusablemedium WHERE linked_orderposition_id IS NOT NULL;",
|
||||
reverse_sql="DELETE FROM pretixbase_reusablemedium_linked_orderpositions;",
|
||||
),
|
||||
]
|
||||
@@ -1,44 +0,0 @@
|
||||
# Generated by Django 4.2.26 on 2025-11-24 11:32
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def reverse(apps, schema_editor):
|
||||
ReusableMedium = apps.get_model('pretixbase', 'ReusableMedium')
|
||||
|
||||
qs = ReusableMedium.linked_orderpositions.through.objects
|
||||
objs = []
|
||||
# get last added orderposition from linked_orderpositions
|
||||
for rm_id, op_id in qs.filter(id__in=qs.values("reusablemedium_id").annotate(max_id=models.Max('id')).values('max_id')).values_list("reusablemedium_id", "orderposition_id"):
|
||||
obj = ReusableMedium(
|
||||
id=rm_id,
|
||||
linked_orderposition_id=op_id,
|
||||
)
|
||||
objs.append(obj)
|
||||
|
||||
ReusableMedium.objects.bulk_update(objs, ['linked_orderposition_id'])
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("pretixbase", "0298_add_reusablemedium_label"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
# according to the docs, UPDATE FROM should run similarly on sqlite and postgres, but I could not get it to work
|
||||
# so roll back the data migration with code before deleting data from through-table in 0297
|
||||
migrations.RunPython(migrations.RunPython.noop, reverse),
|
||||
migrations.RemoveField(
|
||||
model_name="reusablemedium",
|
||||
name="linked_orderposition",
|
||||
),
|
||||
# change related_name for new ManyToManyField to previously used linked_media
|
||||
migrations.AlterField(
|
||||
model_name="reusablemedium",
|
||||
name="linked_orderpositions",
|
||||
field=models.ManyToManyField(
|
||||
related_name="linked_media", to="pretixbase.orderposition"
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -346,7 +346,8 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin):
|
||||
{
|
||||
'user': self,
|
||||
'messages': msg,
|
||||
'url': build_absolute_uri('control:user.settings')
|
||||
'url': build_absolute_uri('control:user.settings'),
|
||||
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||
},
|
||||
event=None,
|
||||
user=self,
|
||||
@@ -391,6 +392,7 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin):
|
||||
'user': self,
|
||||
'reason': msg,
|
||||
'code': code,
|
||||
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||
},
|
||||
event=None,
|
||||
user=self,
|
||||
@@ -430,6 +432,7 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin):
|
||||
mail(
|
||||
self.email, _('Password recovery'), 'pretixcontrol/email/forgot.txt',
|
||||
{
|
||||
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||
'user': self,
|
||||
'url': (build_absolute_uri('control:auth.forgot.recover')
|
||||
+ '?id=%d&token=%s' % (self.id, default_token_generator.make_token(self)))
|
||||
|
||||
@@ -72,16 +72,6 @@ class ReusableMedium(LoggedModel):
|
||||
max_length=200,
|
||||
verbose_name=pgettext_lazy('reusable_medium', 'Identifier'),
|
||||
)
|
||||
claim_token = models.CharField(
|
||||
max_length=200,
|
||||
verbose_name=pgettext_lazy('reusable_medium', 'Claim token'),
|
||||
null=True, blank=True
|
||||
)
|
||||
label = models.CharField(
|
||||
max_length=200,
|
||||
verbose_name=pgettext_lazy('reusable_medium', 'Label'),
|
||||
null=True, blank=True
|
||||
)
|
||||
|
||||
active = models.BooleanField(
|
||||
verbose_name=_('Active'),
|
||||
@@ -99,14 +89,12 @@ class ReusableMedium(LoggedModel):
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name=_('Customer account'),
|
||||
)
|
||||
linked_orderpositions = models.ManyToManyField(
|
||||
linked_orderposition = models.ForeignKey(
|
||||
OrderPosition,
|
||||
null=True, blank=True,
|
||||
related_name='linked_media',
|
||||
verbose_name=_('Linked tickets'),
|
||||
help_text=_(
|
||||
'If you link to more than one ticket, make sure there is no overlap in validity. '
|
||||
'If multiple tickets are valid at once, this will lead to failed check-ins.'
|
||||
)
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name=_('Linked ticket'),
|
||||
)
|
||||
linked_giftcard = models.ForeignKey(
|
||||
GiftCard,
|
||||
|
||||
@@ -334,7 +334,8 @@ def _check_position_constraints(
|
||||
raise CartPositionError(error_messages['voucher_invalid_subevent'])
|
||||
|
||||
# Voucher expired
|
||||
if voucher and voucher.valid_until and voucher.valid_until < time_machine_now_dt:
|
||||
# (checked using real_now_dt as vouchers influence quota calculations)
|
||||
if voucher and voucher.valid_until and voucher.valid_until < real_now_dt:
|
||||
raise CartPositionError(error_messages['voucher_expired'])
|
||||
|
||||
# Subevent has been disabled
|
||||
|
||||
@@ -3495,8 +3495,8 @@ def signal_listener_issue_media(sender: Event, order: Order, **kwargs):
|
||||
identifier=mt.generate_identifier(sender.organizer),
|
||||
active=True,
|
||||
customer=order.customer,
|
||||
linked_orderposition=p,
|
||||
)
|
||||
rm.linked_orderpositions.add(p)
|
||||
rm.log_action(
|
||||
'pretix.reusable_medium.created',
|
||||
data={
|
||||
|
||||
@@ -176,6 +176,7 @@ def shred(self, event: Event, fileid: str, confirm_code: str, user: int=None, lo
|
||||
_('Data shredding completed'),
|
||||
'pretixbase/email/shred_completed.txt',
|
||||
{
|
||||
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||
'user': user,
|
||||
'organizer': event.organizer.name,
|
||||
'event': str(event.name),
|
||||
|
||||
@@ -13,5 +13,5 @@ Start time: {{ start_time }} (new data added after this time might not have been
|
||||
|
||||
Best regards,
|
||||
|
||||
Your pretix team
|
||||
Your {{ instance }} team
|
||||
{% endblocktrans %}
|
||||
|
||||
34
src/pretix/base/templatetags/anonymize_email.py
Normal file
34
src/pretix/base/templatetags/anonymize_email.py
Normal file
@@ -0,0 +1,34 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-today pretix GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
from django import template
|
||||
from django.utils.html import mark_safe
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.filter("anon_email")
|
||||
def anon_email(value):
|
||||
"""Replaces @ with [at] and . with [dot] for anonymization."""
|
||||
if not isinstance(value, str):
|
||||
return value
|
||||
value = value.replace("@", "[at]").replace(".", "[dot]")
|
||||
return mark_safe(''.join(['&#{0};'.format(ord(char)) for char in value]))
|
||||
@@ -1744,7 +1744,7 @@ class ReusableMediaFilterForm(FilterForm):
|
||||
Q(identifier__icontains=query)
|
||||
| Q(customer__identifier__icontains=query)
|
||||
| Q(customer__external_identifier__istartswith=query)
|
||||
| Q(linked_orderpositions__order__code__icontains=query)
|
||||
| Q(linked_orderposition__order__code__icontains=query)
|
||||
| Q(linked_giftcard__secret__icontains=query)
|
||||
)
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ from pretix.control.forms import ExtFileField, SplitDateTimeField
|
||||
from pretix.control.forms.event import (
|
||||
SafeEventMultipleChoiceField, multimail_validate,
|
||||
)
|
||||
from pretix.control.forms.widgets import Select2, Select2Multiple
|
||||
from pretix.control.forms.widgets import Select2
|
||||
from pretix.multidomain.models import KnownDomain
|
||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
||||
|
||||
@@ -246,12 +246,6 @@ class SafeOrderPositionChoiceField(forms.ModelChoiceField):
|
||||
return f'{op.order.code}-{op.positionid} ({str(op.item) + ((" - " + str(op.variation)) if op.variation else "")})'
|
||||
|
||||
|
||||
class SafeOrderPositionMultipleChoiceField(forms.ModelMultipleChoiceField):
|
||||
def __init__(self, queryset, **kwargs):
|
||||
queryset = queryset.model.all.none()
|
||||
super().__init__(queryset, **kwargs)
|
||||
|
||||
|
||||
class EventMetaPropertyForm(I18nModelForm):
|
||||
class Meta:
|
||||
model = EventMetaProperty
|
||||
@@ -851,12 +845,12 @@ class ReusableMediumUpdateForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = ReusableMedium
|
||||
fields = ['active', 'expires', 'customer', 'linked_giftcard', 'linked_orderpositions', 'notes']
|
||||
fields = ['active', 'expires', 'customer', 'linked_giftcard', 'linked_orderposition', 'notes']
|
||||
field_classes = {
|
||||
'expires': SplitDateTimeField,
|
||||
'customer': SafeModelChoiceField,
|
||||
'linked_giftcard': SafeModelChoiceField,
|
||||
'linked_orderpositions': SafeOrderPositionMultipleChoiceField,
|
||||
'linked_orderposition': SafeOrderPositionChoiceField,
|
||||
}
|
||||
widgets = {
|
||||
'expires': SplitDateTimePickerWidget,
|
||||
@@ -866,8 +860,8 @@ class ReusableMediumUpdateForm(forms.ModelForm):
|
||||
super().__init__(*args, **kwargs)
|
||||
organizer = self.instance.organizer
|
||||
|
||||
self.fields['linked_orderpositions'].queryset = OrderPosition.all.filter(order__event__organizer=organizer).all()
|
||||
self.fields['linked_orderpositions'].widget = Select2Multiple(
|
||||
self.fields['linked_orderposition'].queryset = OrderPosition.all.filter(order__event__organizer=organizer).all()
|
||||
self.fields['linked_orderposition'].widget = Select2(
|
||||
attrs={
|
||||
'data-model-select2': 'generic',
|
||||
'data-select2-url': reverse('control:organizer.ticket_select2', kwargs={
|
||||
@@ -875,8 +869,8 @@ class ReusableMediumUpdateForm(forms.ModelForm):
|
||||
}),
|
||||
}
|
||||
)
|
||||
self.fields['linked_orderpositions'].widget.choices = self.fields['linked_orderpositions'].choices
|
||||
self.fields['linked_orderpositions'].required = False
|
||||
self.fields['linked_orderposition'].widget.choices = self.fields['linked_orderposition'].choices
|
||||
self.fields['linked_orderposition'].required = False
|
||||
|
||||
self.fields['linked_giftcard'].queryset = organizer.issued_gift_cards.all()
|
||||
self.fields['linked_giftcard'].widget = Select2(
|
||||
@@ -930,12 +924,12 @@ class ReusableMediumCreateForm(ReusableMediumUpdateForm):
|
||||
|
||||
class Meta:
|
||||
model = ReusableMedium
|
||||
fields = ['active', 'type', 'identifier', 'expires', 'linked_orderpositions', 'linked_giftcard', 'customer', 'notes']
|
||||
fields = ['active', 'type', 'identifier', 'expires', 'linked_orderposition', 'linked_giftcard', 'customer', 'notes']
|
||||
field_classes = {
|
||||
'expires': SplitDateTimeField,
|
||||
'customer': SafeModelChoiceField,
|
||||
'linked_giftcard': SafeModelChoiceField,
|
||||
'linked_orderpositions': SafeOrderPositionMultipleChoiceField,
|
||||
'linked_orderposition': SafeOrderPositionChoiceField,
|
||||
}
|
||||
widgets = {
|
||||
'expires': SplitDateTimePickerWidget,
|
||||
|
||||
@@ -518,6 +518,7 @@ def pretixcontrol_orderposition_blocked_display(sender: Event, orderposition, bl
|
||||
'The order requires approval before it can continue to be processed.'),
|
||||
'pretix.event.order.approved': _('The order has been approved.'),
|
||||
'pretix.event.order.denied': _('The order has been denied (comment: "{comment}").'),
|
||||
'pretix.event.order.vatid.validated': _('The customer VAT ID has been verified.'),
|
||||
'pretix.event.order.contact.changed': _('The email address has been changed from "{old_email}" '
|
||||
'to "{new_email}".'),
|
||||
'pretix.event.order.contact.confirmed': _(
|
||||
@@ -741,7 +742,6 @@ class CoreUserImpersonatedLogEntryType(UserImpersonatedLogEntryType):
|
||||
'pretix.reusable_medium.created': _('The reusable medium has been created.'),
|
||||
'pretix.reusable_medium.created.auto': _('The reusable medium has been created automatically.'),
|
||||
'pretix.reusable_medium.changed': _('The reusable medium has been changed.'),
|
||||
'pretix.reusable_medium.linked_orderposition.added': _('A new ticket has been added to the medium.'),
|
||||
'pretix.reusable_medium.linked_orderposition.changed': _('The medium has been connected to a new ticket.'),
|
||||
'pretix.reusable_medium.linked_giftcard.changed': _('The medium has been connected to a new gift card.'),
|
||||
'pretix.email.error': _('Sending of an email has failed.'),
|
||||
|
||||
@@ -9,5 +9,5 @@ Please do never give this code to another person. Our support team will never as
|
||||
If this code was not requested by you, please contact us immediately.
|
||||
|
||||
Best regards,
|
||||
Your pretix team
|
||||
Your {{ instance }} team
|
||||
{% endblocktrans %}
|
||||
|
||||
@@ -5,5 +5,5 @@ you requested a new password. Please go to the following page to reset your pass
|
||||
{{ url }}
|
||||
|
||||
Best regards,
|
||||
Your pretix team
|
||||
{% endblocktrans %}
|
||||
Your {{ instance }} team
|
||||
{% endblocktrans %}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% load i18n %}{% blocktrans with url=url|safe %}Hello,
|
||||
|
||||
you have been invited to a team on pretix, a platform to perform event
|
||||
you have been invited to a team on {{ instance }}, a platform to perform event
|
||||
ticket sales.
|
||||
|
||||
Organizer: {{ organizer }}
|
||||
@@ -13,5 +13,5 @@ If you do not want to join, you can safely ignore or delete this email.
|
||||
|
||||
Best regards,
|
||||
|
||||
Your pretix team
|
||||
Your {{ instance }} team
|
||||
{% endblocktrans %}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% load i18n %}{% blocktrans with url=url|safe messages=messages|safe %}Hello,
|
||||
|
||||
this is to inform you that the account information of your pretix account has been
|
||||
this is to inform you that the account information of your {{ instance }} account has been
|
||||
changed. In particular, the following changes have been performed:
|
||||
|
||||
{{ messages }}
|
||||
@@ -12,5 +12,5 @@ You can review and change your account settings here:
|
||||
{{ url }}
|
||||
|
||||
Best regards,
|
||||
Your pretix team
|
||||
Your {{ instance }} team
|
||||
{% endblocktrans %}
|
||||
|
||||
@@ -54,8 +54,8 @@
|
||||
<a href="?{% url_replace request 'ordering' 'identifier' %}"><i class="fa fa-caret-up"></i></a>
|
||||
</th>
|
||||
<th>{% trans "Media type" context "reusable_media" %}
|
||||
<a href="?{% url_replace request 'ordering' '-type' %}"><i class="fa fa-caret-down"></i></a>
|
||||
<a href="?{% url_replace request 'ordering' 'type' %}"><i class="fa fa-caret-up"></i></a></th>
|
||||
<a href="?{% url_replace request 'ordering' '-email' %}"><i class="fa fa-caret-down"></i></a>
|
||||
<a href="?{% url_replace request 'ordering' 'email' %}"><i class="fa fa-caret-up"></i></a></th>
|
||||
<th>{% trans "Connections" context "reusable_media" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
@@ -82,13 +82,13 @@
|
||||
</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
{% for op in m.linked_orderpositions.all %}
|
||||
{% if m.linked_orderposition %}
|
||||
<span class="helper-display-block">
|
||||
<span class="fa fa-ticket fa-fw"></span>
|
||||
<a href="{% url "control:event.order" event=op.order.event.slug organizer=request.organizer.slug code=op.order.code %}">
|
||||
{{ op.order.code }}</a>-{{ op.positionid }}
|
||||
<a href="{% url "control:event.order" event=m.linked_orderposition.order.event.slug organizer=request.organizer.slug code=m.linked_orderposition.order.code %}">
|
||||
{{ m.linked_orderposition.order.code }}</a>-{{ m.linked_orderposition.positionid }}
|
||||
</span>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if m.linked_giftcard %}
|
||||
<span class="helper-display-block">
|
||||
<span class="fa fa-credit-card fa-fw"></span>
|
||||
|
||||
@@ -28,19 +28,7 @@
|
||||
<dt>{% trans "Media type" context "reusable_media" %}</dt>
|
||||
<dd>{{ medium.get_type_display }}</dd>
|
||||
<dt>{% trans "Identifier" context "reusable_media" %}</dt>
|
||||
<dd>
|
||||
<code id="medium_identifier">{{ medium.identifier }}</code>
|
||||
<button type="button" class="btn btn-default btn-xs btn-clipboard js-only" data-clipboard-target="#medium_identifier">
|
||||
<i class="fa fa-clipboard" aria-hidden="true"></i>
|
||||
<span class="sr-only">{% trans "Copy to clipboard" %}</span>
|
||||
</button>
|
||||
{% if medium.type == "barcode" %}
|
||||
<button type="button" class="btn btn-default btn-xs js-only" data-toggle="qrcode" data-qrcode="{{ medium.identifier }}">
|
||||
<i class="fa fa-qrcode" aria-hidden="true"></i>
|
||||
<span class="sr-only">{% trans "Create QR code" %}</span>
|
||||
</button>
|
||||
{% endif %}
|
||||
</dd>
|
||||
<dd><code>{{ medium.identifier }}</code></dd>
|
||||
<dt>{% trans "Status" %}</dt>
|
||||
<dd>
|
||||
{% if not medium.active %}
|
||||
@@ -61,13 +49,13 @@
|
||||
</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
{% for op in medium.linked_orderpositions.all %}
|
||||
{% if medium.linked_orderposition %}
|
||||
<span class="helper-display-block">
|
||||
<span class="fa fa-ticket fa-fw"></span>
|
||||
<a href="{% url "control:event.order" event=op.order.event.slug organizer=request.organizer.slug code=op.order.code %}">
|
||||
{{ op.order.code }}</a>-{{ op.positionid }}
|
||||
</span>
|
||||
{% endfor %}
|
||||
<span class="fa fa-ticket fa-fw"></span>
|
||||
<a href="{% url "control:event.order" event=medium.linked_orderposition.order.event.slug organizer=request.organizer.slug code=medium.linked_orderposition.order.code %}">
|
||||
{{ medium.linked_orderposition.order.code }}</a>-{{ medium.linked_orderposition.positionid }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if medium.linked_giftcard %}
|
||||
<span class="helper-display-block">
|
||||
<span class="fa fa-credit-card fa-fw"></span>
|
||||
|
||||
@@ -1641,9 +1641,17 @@ class OrderCheckVATID(OrderView):
|
||||
|
||||
try:
|
||||
normalized_id = validate_vat_id(ia.vat_id, str(ia.country))
|
||||
ia.vat_id_validated = True
|
||||
ia.vat_id = normalized_id
|
||||
ia.save()
|
||||
with transaction.atomic():
|
||||
ia.vat_id_validated = True
|
||||
ia.vat_id = normalized_id
|
||||
ia.save()
|
||||
self.order.log_action(
|
||||
'pretix.event.order.vatid.validated',
|
||||
data={
|
||||
'vat_id': normalized_id,
|
||||
},
|
||||
user=self.request.user,
|
||||
)
|
||||
except VATIDFinalError as e:
|
||||
messages.error(self.request, e.message)
|
||||
except VATIDTemporaryError:
|
||||
|
||||
@@ -1039,9 +1039,10 @@ class TeamMemberView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin,
|
||||
def _send_invite(self, instance):
|
||||
mail(
|
||||
instance.email,
|
||||
_('pretix account invitation'),
|
||||
_('Account invitation'),
|
||||
'pretixcontrol/email/invitation.txt',
|
||||
{
|
||||
'instance': settings.PRETIX_INSTANCE_NAME,
|
||||
'user': self,
|
||||
'organizer': self.request.organizer.name,
|
||||
'team': instance.team.name,
|
||||
@@ -3338,10 +3339,8 @@ class ReusableMediaListView(OrganizerDetailViewMixin, OrganizerPermissionRequire
|
||||
|
||||
def get_queryset(self):
|
||||
qs = self.request.organizer.reusable_media.select_related(
|
||||
'customer',
|
||||
'linked_giftcard',
|
||||
).prefetch_related(
|
||||
Prefetch('linked_orderpositions', queryset=OrderPosition.objects.select_related("order", "order__event"))
|
||||
'customer', 'linked_orderposition', 'linked_orderposition__order', 'linked_orderposition__order__event',
|
||||
'linked_giftcard'
|
||||
)
|
||||
if self.filter_form.is_valid():
|
||||
qs = self.filter_form.filter_qs(qs)
|
||||
@@ -3389,14 +3388,10 @@ class ReusableMediumCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequ
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
r = super().form_valid(form)
|
||||
|
||||
data = {
|
||||
form.instance.log_action('pretix.reusable_medium.created', user=self.request.user, data={
|
||||
k: getattr(form.instance, k)
|
||||
for k in form.changed_data
|
||||
}
|
||||
if "linked_orderpositions" in data:
|
||||
data["linked_orderpositions"] = data["linked_orderpositions"].values_list("pk", flat=True)
|
||||
form.instance.log_action('pretix.reusable_medium.created', user=self.request.user, data=data)
|
||||
})
|
||||
messages.success(self.request, _('Your changes have been saved.'))
|
||||
return r
|
||||
|
||||
@@ -3422,13 +3417,10 @@ class ReusableMediumUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequ
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
if form.has_changed():
|
||||
data = {
|
||||
self.object.log_action('pretix.reusable_medium.changed', user=self.request.user, data={
|
||||
k: getattr(self.object, k)
|
||||
for k in form.changed_data
|
||||
}
|
||||
if "linked_orderpositions" in data:
|
||||
data["linked_orderpositions"] = data["linked_orderpositions"].values_list("pk", flat=True)
|
||||
self.object.log_action('pretix.reusable_medium.changed', user=self.request.user, data=data)
|
||||
})
|
||||
messages.success(self.request, _('Your changes have been saved.'))
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
@@ -8,16 +8,16 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-02-24 11:50+0000\n"
|
||||
"PO-Revision-Date: 2025-02-14 21:00+0000\n"
|
||||
"Last-Translator: deborahfoell <deborah.foell@om.org>\n"
|
||||
"Language-Team: Greek <https://translate.pretix.eu/projects/pretix/pretix/el/"
|
||||
">\n"
|
||||
"PO-Revision-Date: 2026-02-25 23:00+0000\n"
|
||||
"Last-Translator: David Ibáñez Cerdeira <dibanez@gmail.com>\n"
|
||||
"Language-Team: Greek <https://translate.pretix.eu/projects/pretix/pretix/el/>"
|
||||
"\n"
|
||||
"Language: el\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 5.9.2\n"
|
||||
"X-Generator: Weblate 5.16\n"
|
||||
|
||||
#: pretix/_base_settings.py:87
|
||||
msgid "English"
|
||||
@@ -20467,7 +20467,7 @@ msgstr "Ορίστε νέο κωδικό πρόσβασης"
|
||||
#: pretix/presale/templates/pretixpresale/organizers/customer_password.html:25
|
||||
#: pretix/presale/templates/pretixpresale/organizers/customer_setpassword.html:25
|
||||
msgid "Save"
|
||||
msgstr "Αποθηκεύση"
|
||||
msgstr "gardar"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/auth/register.html:7
|
||||
msgid "Create a new account"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-02-24 11:50+0000\n"
|
||||
"PO-Revision-Date: 2026-02-21 18:00+0000\n"
|
||||
"PO-Revision-Date: 2026-03-03 20:00+0000\n"
|
||||
"Last-Translator: CVZ-es <damien.bremont@casadevelazquez.org>\n"
|
||||
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix/"
|
||||
"es/>\n"
|
||||
@@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 5.16\n"
|
||||
"X-Generator: Weblate 5.16.1\n"
|
||||
|
||||
#: pretix/_base_settings.py:87
|
||||
msgid "English"
|
||||
@@ -17058,28 +17058,20 @@ msgid "You need to specify as many seats as voucher codes."
|
||||
msgstr "Debe especificar tantas butacas como vales de compra."
|
||||
|
||||
#: pretix/control/forms/waitinglist.py:39
|
||||
#, fuzzy
|
||||
#| msgid "Please select a valid seat."
|
||||
msgid "Select a valid choice."
|
||||
msgstr "Por favor seleccione una butaca válida."
|
||||
msgstr "Seleccione una opción válida."
|
||||
|
||||
#: pretix/control/forms/waitinglist.py:107
|
||||
#, fuzzy
|
||||
#| msgid "Active products"
|
||||
msgid "Only includes active products."
|
||||
msgstr "Productos activos"
|
||||
msgstr "Solo incluir productos activos."
|
||||
|
||||
#: pretix/control/forms/waitinglist.py:115
|
||||
#, fuzzy
|
||||
#| msgid "A voucher with this code already exists."
|
||||
msgid "A voucher for this waiting list entry was already sent out."
|
||||
msgstr "Ya existe un vale de compra con este código."
|
||||
msgstr "Ya se ha enviado un vale para esta entrada en la lista de espera."
|
||||
|
||||
#: pretix/control/forms/waitinglist.py:125
|
||||
#, fuzzy
|
||||
#| msgid "The selected product has been deactivated."
|
||||
msgid "The selected product is not active."
|
||||
msgstr "El producto seleccionado ha sido desactivado."
|
||||
msgstr "El producto seleccionado no está activo."
|
||||
|
||||
#: pretix/control/logdisplay.py:73 pretix/control/logdisplay.py:83
|
||||
msgid "The order has been changed:"
|
||||
@@ -17729,7 +17721,7 @@ msgstr ""
|
||||
|
||||
#: pretix/control/logdisplay.py:589
|
||||
msgid "The voucher has been changed."
|
||||
msgstr "EL vale de compra ha sido cambiado."
|
||||
msgstr "El vale de compra ha sido cambiado."
|
||||
|
||||
#: pretix/control/logdisplay.py:590
|
||||
msgid "The voucher has been deleted."
|
||||
@@ -18643,7 +18635,7 @@ msgstr "Entradas"
|
||||
#: pretix/control/templates/pretixcontrol/order/index.html:764
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:457
|
||||
msgid "Taxes"
|
||||
msgstr "gravámenes"
|
||||
msgstr "Impuestos"
|
||||
|
||||
#: pretix/control/navigation.py:97
|
||||
msgid "Invoicing"
|
||||
@@ -18883,6 +18875,10 @@ msgid ""
|
||||
"in repeatedly. Please check if your browser is set to block cookies, or "
|
||||
"delete all existing cookies and retry."
|
||||
msgstr ""
|
||||
"Parece que el navegador no acepta nuestras cookies y es necesario iniciar "
|
||||
"sesión repetidamente. Por favor, compruebe si el navegador está configurado "
|
||||
"para bloquear cookies o elimine todas las cookies existentes y vuelva a "
|
||||
"intentarlo."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/auth/login.html:35
|
||||
#: pretix/presale/templates/pretixpresale/fragment_login_status.html:19
|
||||
@@ -18994,8 +18990,9 @@ msgid ""
|
||||
"This application has <strong>not</strong> been reviewed by the pretix team. "
|
||||
"Granting access to your pretix account happens at your own risk."
|
||||
msgstr ""
|
||||
"Esta aplicación<strong>no</strong> ha sido revisada por el equipo de pretix. "
|
||||
"La concesión del acceso a su cuenta pretix se realiza bajo su propio riesgo."
|
||||
"Esta aplicación <strong>no</strong> ha sido revisada por el equipo de "
|
||||
"pretix. La concesión del acceso a su cuenta pretix se realiza bajo su propio "
|
||||
"riesgo."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/auth/oauth_authorization.html:54
|
||||
msgid "Error:"
|
||||
@@ -19129,8 +19126,8 @@ msgid ""
|
||||
"We've detected that you are using <strong>Microsoft Internet Explorer</"
|
||||
"strong>."
|
||||
msgstr ""
|
||||
"Hemos detectado que estás usando <strong> Microsoft Internet Explorer </"
|
||||
"strong>."
|
||||
"Hemos detectado que estás usando <strong>Microsoft Internet Explorer</strong>"
|
||||
"."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/base.html:332
|
||||
#: pretix/presale/templates/pretixpresale/base.html:54
|
||||
@@ -19169,7 +19166,7 @@ msgid ""
|
||||
"people from actually buying tickets."
|
||||
msgstr ""
|
||||
"Tu evento contiene <strong>pedidos de modo de prueba</strong> a pesar de que "
|
||||
"<strong> el modo de prueba se ha deshabilitado</strong>. Deberías eliminar "
|
||||
"<strong>el modo de prueba se ha deshabilitado</strong>. Deberías eliminar "
|
||||
"estes pedidos para asegurarte que no se muestren en tus reportes "
|
||||
"estadísticos y bloquear la compra de entradas a las personas."
|
||||
|
||||
@@ -20764,7 +20761,7 @@ msgid ""
|
||||
"duplicate payment attempts. You should review the cases and consider "
|
||||
"refunding the overpaid amount to the user."
|
||||
msgstr ""
|
||||
"Este evento contiene <strong> pedidos pagados en exceso</strong>, por "
|
||||
"Este evento contiene <strong>pedidos pagados en exceso</strong>, por "
|
||||
"ejemplo, debido a que hay intentos de pago duplicados. Debe revisar los "
|
||||
"casos y considerar la devolución de la cantidad pagada en exceso al usuario."
|
||||
|
||||
@@ -20777,7 +20774,7 @@ msgid ""
|
||||
"This event contains <strong>pending refunds</strong> that you should take "
|
||||
"care of."
|
||||
msgstr ""
|
||||
"Este evento contiene <strong>devoluciones pendientes </strong> sobre las que "
|
||||
"Este evento contiene <strong>devoluciones pendientes</strong> sobre las que "
|
||||
"debe prestar atención."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/event/index.html:50
|
||||
@@ -20815,7 +20812,7 @@ msgid ""
|
||||
"arrived. You should review the cases and consider either refunding the "
|
||||
"customer or creating more space."
|
||||
msgstr ""
|
||||
"Este evento contiene <strong> pedidos completamente pagados</strong> que no "
|
||||
"Este evento contiene <strong>pedidos completamente pagados</strong> que no "
|
||||
"están marcadas como pagados, probablemente porque no se dejo ningún cupo al "
|
||||
"momento que llegó el pago. Debería revisar estos casos y considerar, "
|
||||
"devolver el dinero o crear más espacio."
|
||||
@@ -21479,7 +21476,7 @@ msgid ""
|
||||
"as examples, you can add more in the \"Settings\" part of your event."
|
||||
msgstr ""
|
||||
"pretix soporta un <a href=\"https://pretix.eu/about/en/features/payment\" "
|
||||
"target=\"_blank\">amplio rango de proveedores de pago </a> permitiéndole "
|
||||
"target=\"_blank\">amplio rango de proveedores de pago</a> permitiéndole "
|
||||
"elegir los métodos de pago que mejor se adapten a su flujo de trabajo. Aquí "
|
||||
"hay sólo dos de ellos a modo de ejemplo, puede añadir más en la parte "
|
||||
"\"Configuración\" de su evento."
|
||||
@@ -23891,8 +23888,8 @@ msgid ""
|
||||
"Do you really want to delete this order? <strong>You really cannot revert "
|
||||
"this action and we can't either.</strong>"
|
||||
msgstr ""
|
||||
"¿Realmente quieres eliminar este pedido? <strong> No puedes revertir esta "
|
||||
"acción y tampoco nosotros. </strong>"
|
||||
"¿Realmente quieres eliminar este pedido? <strong>No puedes revertir esta "
|
||||
"acción y tampoco nosotros.</strong>"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/order/delete.html:25
|
||||
msgid "Yes, delete order"
|
||||
@@ -24516,9 +24513,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Hemos recibido la notificación de que <strong>%(amount)s</strong> ha sido "
|
||||
"devuelto a través de <strong>%(method)s</strong>. Si este reembolso está "
|
||||
"procesado, el pedido habrá sido pagado con un importe inferior "
|
||||
"en<strong>%(pending)s</strong>. El total del pedido es <strong>%(total)s</"
|
||||
"strong>."
|
||||
"procesado, el pedido habrá sido pagado con un importe inferior en <strong>%"
|
||||
"(pending)s</strong>. El total del pedido es <strong>%(total)s</strong>."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/order/refund_process.html:30
|
||||
msgid "Since the order is already canceled, this will not affect its state."
|
||||
@@ -27262,7 +27258,7 @@ msgid ""
|
||||
"Using this option will <strong>delete all current quotas</strong> from "
|
||||
"<strong>all selected dates</strong>."
|
||||
msgstr ""
|
||||
"Al utilizar esta opción se <strong> eliminarán todas las cuotas actuales </"
|
||||
"Al utilizar esta opción se <strong>eliminarán todas las cuotas actuales</"
|
||||
"strong> de <strong>todas las fechas seleccionadas</strong>."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/bulk_edit.html:277
|
||||
@@ -27998,7 +27994,7 @@ msgstr "Borrar vale de compra"
|
||||
msgid ""
|
||||
"Are you sure you want to delete the voucher <strong>%(voucher)s</strong>?"
|
||||
msgstr ""
|
||||
"¿Está seguro de que desea borrar el vale de compra<strong>%(voucher)s</"
|
||||
"¿Está seguro de que desea borrar el vale de compra <strong>%(voucher)s</"
|
||||
"strong>?"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/vouchers/delete_bulk.html:4
|
||||
@@ -28191,10 +28187,8 @@ msgstr ""
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/edit.html:4
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/edit.html:6
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:273
|
||||
#, fuzzy
|
||||
#| msgid "Entry"
|
||||
msgid "Edit entry"
|
||||
msgstr "Ingreso"
|
||||
msgstr "Editar entrada"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:17
|
||||
msgid ""
|
||||
@@ -28256,7 +28250,7 @@ msgid ""
|
||||
"quota is available) or you can press the big button below this text to send "
|
||||
"out as many vouchers as currently possible to the persons who waited longest."
|
||||
msgstr ""
|
||||
"Ha configurado que los vales de compra se enviarán <strong>no </strong> "
|
||||
"Ha configurado que los vales de compra se enviarán <strong>no</strong> "
|
||||
"automáticamente. Puede enviarlos uno por uno en un orden de su elección "
|
||||
"haciendo clic en los botones junto a una línea de esta tabla (si hay "
|
||||
"suficiente cuota disponible) o puede presionar el botón grande debajo de "
|
||||
@@ -30133,15 +30127,7 @@ msgstr ""
|
||||
"La autenticación de dos factores ahora está desactivada para su cuenta."
|
||||
|
||||
#: pretix/control/views/user.py:635
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid ""
|
||||
#| "Your emergency codes have been newly generated. Remember to store them in "
|
||||
#| "a safe place in case you lose access to your devices. You will not be "
|
||||
#| "able to view them again here.\n"
|
||||
#| "\n"
|
||||
#| "Your emergency codes:\n"
|
||||
#| "- \n"
|
||||
#| "- "
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"Your emergency codes have been newly generated. Remember to store them in a "
|
||||
"safe place in case you lose access to your devices. You will not be able to "
|
||||
@@ -30155,8 +30141,7 @@ msgstr ""
|
||||
"verlos aquí.\n"
|
||||
"\n"
|
||||
"Sus códigos de emergencias:\n"
|
||||
"- \n"
|
||||
"- "
|
||||
"{tokens}"
|
||||
|
||||
#: pretix/control/views/user.py:655
|
||||
msgid "Your notifications have been disabled."
|
||||
@@ -30326,10 +30311,8 @@ msgid "The selected entry has been deleted."
|
||||
msgstr "Se ha borrado la entrada seleccionada."
|
||||
|
||||
#: pretix/control/views/waitinglist.py:417
|
||||
#, fuzzy
|
||||
#| msgid "The waitinglist entry has been transferred."
|
||||
msgid "The waitinglist entry has been changed."
|
||||
msgstr "Las entradas de la lista de espera han sido transferidas."
|
||||
msgstr "Se ha modificado la entrada de la lista de espera."
|
||||
|
||||
#: pretix/helpers/countries.py:134
|
||||
msgid "Belarus"
|
||||
@@ -37011,8 +36994,8 @@ msgid ""
|
||||
"If you're looking to buy a ticket, you need to follow a direct link to an "
|
||||
"event or organizer profile."
|
||||
msgstr ""
|
||||
"SI buscas comprar una entrada, necesitar seguir un enlace directo a un "
|
||||
"evento o a un perfil de organizador."
|
||||
"Si busca comprar una entrada, necesita seguir un enlace directo a un evento "
|
||||
"o a un perfil de organizador."
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/index.html:20
|
||||
#, python-format
|
||||
|
||||
@@ -4,16 +4,16 @@ msgstr ""
|
||||
"Project-Id-Version: 1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-02-24 11:50+0000\n"
|
||||
"PO-Revision-Date: 2026-02-21 18:00+0000\n"
|
||||
"PO-Revision-Date: 2026-03-03 20:00+0000\n"
|
||||
"Last-Translator: CVZ-es <damien.bremont@casadevelazquez.org>\n"
|
||||
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix/fr/"
|
||||
">\n"
|
||||
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix/"
|
||||
"fr/>\n"
|
||||
"Language: fr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n > 1;\n"
|
||||
"X-Generator: Weblate 5.16\n"
|
||||
"X-Generator: Weblate 5.16.1\n"
|
||||
|
||||
#: pretix/_base_settings.py:87
|
||||
msgid "English"
|
||||
@@ -13011,7 +13011,9 @@ msgstr "Texte d’aide du champ email"
|
||||
|
||||
#: pretix/base/settings.py:3398
|
||||
msgid "Allow creating a new team during event creation"
|
||||
msgstr "Ancienne API de l’appareil d’enregistrement"
|
||||
msgstr ""
|
||||
"Autoriser la création d'une nouvelle équipe lors de la création d'un "
|
||||
"événement"
|
||||
|
||||
#: pretix/base/settings.py:3399
|
||||
msgid ""
|
||||
@@ -17204,28 +17206,20 @@ msgid "You need to specify as many seats as voucher codes."
|
||||
msgstr "Vous devez spécifier autant de sièges que de codes promotionnels."
|
||||
|
||||
#: pretix/control/forms/waitinglist.py:39
|
||||
#, fuzzy
|
||||
#| msgid "Please select a valid seat."
|
||||
msgid "Select a valid choice."
|
||||
msgstr "Veuillez sélectionner un siège valide."
|
||||
msgstr "Sélectionnez une option valide."
|
||||
|
||||
#: pretix/control/forms/waitinglist.py:107
|
||||
#, fuzzy
|
||||
#| msgid "Active products"
|
||||
msgid "Only includes active products."
|
||||
msgstr "Produits actifs"
|
||||
msgstr "Comprend uniquement les produits actifs."
|
||||
|
||||
#: pretix/control/forms/waitinglist.py:115
|
||||
#, fuzzy
|
||||
#| msgid "A voucher with this code already exists."
|
||||
msgid "A voucher for this waiting list entry was already sent out."
|
||||
msgstr "Un bon de réduction avec ce code existe déjà."
|
||||
msgstr "Un bon pour cette inscription sur la liste d'attente a déjà été envoyé."
|
||||
|
||||
#: pretix/control/forms/waitinglist.py:125
|
||||
#, fuzzy
|
||||
#| msgid "The selected product has been deactivated."
|
||||
msgid "The selected product is not active."
|
||||
msgstr "Le produit sélectionné a été désactivé."
|
||||
msgstr "Le produit sélectionné n'est pas actif."
|
||||
|
||||
#: pretix/control/logdisplay.py:73 pretix/control/logdisplay.py:83
|
||||
msgid "The order has been changed:"
|
||||
@@ -19021,6 +19015,10 @@ msgid ""
|
||||
"in repeatedly. Please check if your browser is set to block cookies, or "
|
||||
"delete all existing cookies and retry."
|
||||
msgstr ""
|
||||
"Il semble que votre navigateur n'accepte pas nos cookies et que vous deviez "
|
||||
"vous connecter à plusieurs reprises. Veuillez vérifier si votre navigateur "
|
||||
"est configuré pour bloquer les cookies, ou supprimez tous les cookies "
|
||||
"existants et réessayez."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/auth/login.html:35
|
||||
#: pretix/presale/templates/pretixpresale/fragment_login_status.html:19
|
||||
@@ -19108,8 +19106,8 @@ msgid ""
|
||||
"Do you really want to grant the application <strong>%(application)s</strong> "
|
||||
"access to your pretix account?"
|
||||
msgstr ""
|
||||
"Voulez-vous vraiment accorder à l'application <strong>1%(application)s2</"
|
||||
"strong>3 un accès à votre compte pretix ?"
|
||||
"Voulez-vous vraiment accorder à l'application <strong>%(application)s</"
|
||||
"strong> un accès à votre compte pretix ?"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/auth/oauth_authorization.html:24
|
||||
#, python-format
|
||||
@@ -19132,8 +19130,8 @@ msgid ""
|
||||
"This application has <strong>not</strong> been reviewed by the pretix team. "
|
||||
"Granting access to your pretix account happens at your own risk."
|
||||
msgstr ""
|
||||
"Cette application n'a <strong>1pas</strong>2 été véifiée par l'équipe "
|
||||
"pretix. L'accès à votre compte Pretix se fait à vos propres risques."
|
||||
"Cette application n'a <strong>pas</strong> été véifiée par l'équipe pretix. "
|
||||
"L'accès à votre compte Pretix se fait à vos propres risques."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/auth/oauth_authorization.html:54
|
||||
msgid "Error:"
|
||||
@@ -19536,7 +19534,7 @@ msgstr[0] ""
|
||||
"Êtes-vous sûr de vouloir supprimer l'enregistrement <strong>d'un billet</"
|
||||
"strong> ?"
|
||||
msgstr[1] ""
|
||||
"Êtes-vous sûr de vouloir supprimer l'enregistrement <strong>%(count)s "
|
||||
"Êtes-vous sûr de vouloir supprimer l'enregistrement <strong>%(count)s "
|
||||
"billets</strong> ?"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/checkin/bulk_revert_confirm.html:24
|
||||
@@ -21634,7 +21632,7 @@ msgid ""
|
||||
"as examples, you can add more in the \"Settings\" part of your event."
|
||||
msgstr ""
|
||||
"pretix supporte une <a href=\"https://pretix.eu/about/en/features/payment\" "
|
||||
"target=\"_blank\">large gamme de fournisseurs de paiement </a> vous "
|
||||
"target=\"_blank\">large gamme de fournisseurs de paiement</a> vous "
|
||||
"permettant de choisir les méthodes de paiement qui conviennent le mieux à "
|
||||
"votre flux de travail. En voici deux à titre d'exemple, vous pouvez en "
|
||||
"ajouter dans la partie \"Paramètres\" de votre événement."
|
||||
@@ -23578,8 +23576,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Veuillez sélectionner les produits ou les variantes de produits auxquels ce "
|
||||
"quota doit s'appliquer. Si vous appliquez deux quotas à un même produit, il "
|
||||
"ne sera seulement disponible si les quotas <strong>1 et </strong>2 ont "
|
||||
"encore de la place."
|
||||
"ne sera seulement disponible si les quotas <strong>et</strong> ont encore de "
|
||||
"la place."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/items/quota_edit.html:41
|
||||
msgid "Advanced options"
|
||||
@@ -23638,8 +23636,8 @@ msgid ""
|
||||
"Are you sure you want to disable the application <strong>%(application)s</"
|
||||
"strong> permanently?"
|
||||
msgstr ""
|
||||
"Êtes-vous sûr de vouloir désactiver l'application <strong>1%(application)s2</"
|
||||
"strong>3 en permanence ?"
|
||||
"Êtes-vous sûr de vouloir désactiver l'application <strong>%(application)s</"
|
||||
"strong> de manière permanente ?"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/oauth/app_list.html:4
|
||||
#: pretix/control/templates/pretixcontrol/oauth/app_list.html:6
|
||||
@@ -27465,8 +27463,8 @@ msgid ""
|
||||
"Using this option will <strong>delete all current quotas</strong> from "
|
||||
"<strong>all selected dates</strong>."
|
||||
msgstr ""
|
||||
"Cette option permet de <strong> supprimer tous les quotas actuels</strong> "
|
||||
"de <strong>toutes les dates sélectionnées</strong>."
|
||||
"Cette option permet de <strong>supprimer tous les quotas actuels</strong> de "
|
||||
"<strong>toutes les dates sélectionnées</strong>."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/subevents/bulk_edit.html:277
|
||||
msgid ""
|
||||
@@ -28399,10 +28397,8 @@ msgstr ""
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/edit.html:4
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/edit.html:6
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:273
|
||||
#, fuzzy
|
||||
#| msgid "Entry"
|
||||
msgid "Edit entry"
|
||||
msgstr "Entrée"
|
||||
msgstr "Modifier l'entrée"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:17
|
||||
msgid ""
|
||||
@@ -28464,7 +28460,7 @@ msgid ""
|
||||
"quota is available) or you can press the big button below this text to send "
|
||||
"out as many vouchers as currently possible to the persons who waited longest."
|
||||
msgstr ""
|
||||
"Vous avez configuré que les bons <strong>1not</strong>2 seront envoyés "
|
||||
"Vous avez configuré que les bons <strong>ne</strong> seront envoyés "
|
||||
"automatiquement. Vous pouvez soit les envoyer un par un dans l'ordre de "
|
||||
"votre choix en cliquant sur les boutons à côté d'une ligne dans ce tableau "
|
||||
"(si un quota suffisant est disponible), soit vous pouvez appuyer sur le gros "
|
||||
@@ -30368,15 +30364,7 @@ msgstr ""
|
||||
"compte."
|
||||
|
||||
#: pretix/control/views/user.py:635
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid ""
|
||||
#| "Your emergency codes have been newly generated. Remember to store them in "
|
||||
#| "a safe place in case you lose access to your devices. You will not be "
|
||||
#| "able to view them again here.\n"
|
||||
#| "\n"
|
||||
#| "Your emergency codes:\n"
|
||||
#| "- \n"
|
||||
#| "- "
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"Your emergency codes have been newly generated. Remember to store them in a "
|
||||
"safe place in case you lose access to your devices. You will not be able to "
|
||||
@@ -30390,8 +30378,7 @@ msgstr ""
|
||||
"appareils. Vous ne pourrez plus les consulter ici.\n"
|
||||
"\n"
|
||||
"Vos codes d'urgence :\n"
|
||||
"-\n"
|
||||
"- "
|
||||
"{tokens}"
|
||||
|
||||
#: pretix/control/views/user.py:655
|
||||
msgid "Your notifications have been disabled."
|
||||
@@ -30558,10 +30545,8 @@ msgid "The selected entry has been deleted."
|
||||
msgstr "L'entrée sélectionnée a été supprimée."
|
||||
|
||||
#: pretix/control/views/waitinglist.py:417
|
||||
#, fuzzy
|
||||
#| msgid "The waitinglist entry has been transferred."
|
||||
msgid "The waitinglist entry has been changed."
|
||||
msgstr "L’entrée de la liste d’attente a été transférée."
|
||||
msgstr "L'entrée dans la liste d'attente a été modifiée."
|
||||
|
||||
#: pretix/helpers/countries.py:134
|
||||
msgid "Belarus"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-01-26 09:10+0000\n"
|
||||
"PO-Revision-Date: 2025-12-03 23:00+0000\n"
|
||||
"Last-Translator: sandra r <sandrarial@gestiontickets.online>\n"
|
||||
"PO-Revision-Date: 2026-03-02 21:00+0000\n"
|
||||
"Last-Translator: Sandra Rial Pérez <sandrarial@gestiontickets.online>\n"
|
||||
"Language-Team: Galician <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
"js/gl/>\n"
|
||||
"Language: gl\n"
|
||||
@@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 5.14.3\n"
|
||||
"X-Generator: Weblate 5.16.1\n"
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
|
||||
@@ -162,12 +162,12 @@ msgstr "Pedidos pagados"
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:27
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
|
||||
msgid "Attendees (ordered)"
|
||||
msgstr ""
|
||||
msgstr "Asistentes (ordenados)"
|
||||
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:27
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
|
||||
msgid "Attendees (paid)"
|
||||
msgstr ""
|
||||
msgstr "Asistentes (de pago)"
|
||||
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:51
|
||||
msgid "Total revenue"
|
||||
@@ -732,8 +732,8 @@ msgid ""
|
||||
"The items in your cart are no longer reserved for you. You can still "
|
||||
"complete your order as long as they’re available."
|
||||
msgstr ""
|
||||
"Os artigos da túa cesta xa non están reservados para ti. Aínda podes "
|
||||
"completar o teu pedido mentres estean dispoñibles."
|
||||
"Os artigos do teu carro xa non están reservados para ti. Podes completar o "
|
||||
"teu pedido sempre que estean dispoñibles."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:49
|
||||
msgid "Cart expired"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-02-24 11:50+0000\n"
|
||||
"PO-Revision-Date: 2026-02-24 11:49+0000\n"
|
||||
"PO-Revision-Date: 2026-03-02 10:00+0000\n"
|
||||
"Last-Translator: Hijiri Umemoto <hijiri@umemoto.org>\n"
|
||||
"Language-Team: Japanese <https://translate.pretix.eu/projects/pretix/pretix/"
|
||||
"ja/>\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 5.16\n"
|
||||
"X-Generator: Weblate 5.16.1\n"
|
||||
|
||||
#: pretix/_base_settings.py:87
|
||||
msgid "English"
|
||||
@@ -6997,22 +6997,22 @@ msgstr "2006/112/EC号指令の第309条に基づいて免除"
|
||||
#: pretix/base/models/tax.py:253
|
||||
msgctxt "tax_code"
|
||||
msgid "Intra-Community acquisition from second hand means of transport"
|
||||
msgstr "中古輸送手段のEU域内取得"
|
||||
msgstr "中古輸送手段の域内取得"
|
||||
|
||||
#: pretix/base/models/tax.py:255
|
||||
msgctxt "tax_code"
|
||||
msgid "Intra-Community acquisition of second hand goods"
|
||||
msgstr "中古品のEU域内取得"
|
||||
msgstr "中古品の域内取得"
|
||||
|
||||
#: pretix/base/models/tax.py:257
|
||||
msgctxt "tax_code"
|
||||
msgid "Intra-Community acquisition of works of art"
|
||||
msgstr "芸術作品のEU域内取得"
|
||||
msgstr "芸術作品の域内取得"
|
||||
|
||||
#: pretix/base/models/tax.py:259
|
||||
msgctxt "tax_code"
|
||||
msgid "Intra-Community acquisition of collectors items and antiques"
|
||||
msgstr "コレクターアイテムおよび骨董品のEU域内取得"
|
||||
msgstr "コレクターアイテムおよび骨董品の域内取得"
|
||||
|
||||
#: pretix/base/models/tax.py:261
|
||||
msgctxt "tax_code"
|
||||
@@ -16579,28 +16579,20 @@ msgid "You need to specify as many seats as voucher codes."
|
||||
msgstr "あなたはバウチャーコードの数と同じだけの席を指定する必要があります。"
|
||||
|
||||
#: pretix/control/forms/waitinglist.py:39
|
||||
#, fuzzy
|
||||
#| msgid "Please select a valid seat."
|
||||
msgid "Select a valid choice."
|
||||
msgstr "有効な座席を選択してください。"
|
||||
msgstr "有効な選択肢を選んでください。"
|
||||
|
||||
#: pretix/control/forms/waitinglist.py:107
|
||||
#, fuzzy
|
||||
#| msgid "Active products"
|
||||
msgid "Only includes active products."
|
||||
msgstr "有効な製品"
|
||||
msgstr "有効な製品のみを含みます。"
|
||||
|
||||
#: pretix/control/forms/waitinglist.py:115
|
||||
#, fuzzy
|
||||
#| msgid "A voucher with this code already exists."
|
||||
msgid "A voucher for this waiting list entry was already sent out."
|
||||
msgstr "このコードを持つバウチャーはすでに存在しています。"
|
||||
msgstr "この空席待ちリストの内容を持つバウチャーはすでに発送済みです。"
|
||||
|
||||
#: pretix/control/forms/waitinglist.py:125
|
||||
#, fuzzy
|
||||
#| msgid "The selected product has been deactivated."
|
||||
msgid "The selected product is not active."
|
||||
msgstr "選択された製品が無効化されました。"
|
||||
msgstr "選択された製品無効です。"
|
||||
|
||||
#: pretix/control/logdisplay.py:73 pretix/control/logdisplay.py:83
|
||||
msgid "The order has been changed:"
|
||||
@@ -18348,6 +18340,9 @@ msgid ""
|
||||
"in repeatedly. Please check if your browser is set to block cookies, or "
|
||||
"delete all existing cookies and retry."
|
||||
msgstr ""
|
||||
"お使いのブラウザが私どものクッキーを受け入れていないようですので、繰り返し"
|
||||
"ログインする必要があります。ブラウザがクッキーをブロックする設定になっている"
|
||||
"か、既存のクッキーをすべて削除して再試行してください。"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/auth/login.html:35
|
||||
#: pretix/presale/templates/pretixpresale/fragment_login_status.html:19
|
||||
@@ -27400,10 +27395,8 @@ msgstr "次のエントリはすでにバウチャーが添付されているた
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/edit.html:4
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/edit.html:6
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:273
|
||||
#, fuzzy
|
||||
#| msgid "Entry"
|
||||
msgid "Edit entry"
|
||||
msgstr "入場"
|
||||
msgstr "エントリーを編集する"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:17
|
||||
msgid ""
|
||||
@@ -29283,15 +29276,7 @@ msgid "Two-factor authentication is now disabled for your account."
|
||||
msgstr "アカウントの二要素認証が無効になりました。"
|
||||
|
||||
#: pretix/control/views/user.py:635
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid ""
|
||||
#| "Your emergency codes have been newly generated. Remember to store them in "
|
||||
#| "a safe place in case you lose access to your devices. You will not be "
|
||||
#| "able to view them again here.\n"
|
||||
#| "\n"
|
||||
#| "Your emergency codes:\n"
|
||||
#| "- \n"
|
||||
#| "- "
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"Your emergency codes have been newly generated. Remember to store them in a "
|
||||
"safe place in case you lose access to your devices. You will not be able to "
|
||||
@@ -29305,8 +29290,7 @@ msgstr ""
|
||||
"ことはできません。\n"
|
||||
"\n"
|
||||
"あなたの緊急コード:\n"
|
||||
"-\n"
|
||||
"- "
|
||||
"{tokens}"
|
||||
|
||||
#: pretix/control/views/user.py:655
|
||||
msgid "Your notifications have been disabled."
|
||||
@@ -29471,10 +29455,8 @@ msgid "The selected entry has been deleted."
|
||||
msgstr "選択されたエントリは削除されました。"
|
||||
|
||||
#: pretix/control/views/waitinglist.py:417
|
||||
#, fuzzy
|
||||
#| msgid "The waitinglist entry has been transferred."
|
||||
msgid "The waitinglist entry has been changed."
|
||||
msgstr "空席待ちリスト登録が転送されました。"
|
||||
msgstr "空席待ちリスト登録が変更されました。"
|
||||
|
||||
#: pretix/helpers/countries.py:134
|
||||
msgid "Belarus"
|
||||
|
||||
@@ -7,16 +7,16 @@ msgstr ""
|
||||
"Project-Id-Version: 1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-02-24 11:50+0000\n"
|
||||
"PO-Revision-Date: 2026-02-21 03:00+0000\n"
|
||||
"PO-Revision-Date: 2026-03-03 20:00+0000\n"
|
||||
"Last-Translator: Ruud Hendrickx <ruud@leckxicon.eu>\n"
|
||||
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix/nl/"
|
||||
">\n"
|
||||
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix/nl/>"
|
||||
"\n"
|
||||
"Language: nl\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 5.16\n"
|
||||
"X-Generator: Weblate 5.16.1\n"
|
||||
|
||||
#: pretix/_base_settings.py:87
|
||||
msgid "English"
|
||||
@@ -7996,7 +7996,7 @@ msgstr "Deze cadeaubon is in de tussentijd gebruikt. Probeer het opnieuw."
|
||||
|
||||
#: pretix/base/pdf.py:98
|
||||
msgid "Ticket code (barcode content)"
|
||||
msgstr "Ticketcode (waarde van QR-code)"
|
||||
msgstr "Ticketcode (inhoud van QR-code)"
|
||||
|
||||
#: pretix/base/pdf.py:110
|
||||
msgid "Order position number"
|
||||
@@ -9664,7 +9664,7 @@ msgstr "Het bedrag is van uw kaart afgeschreven."
|
||||
|
||||
#: pretix/base/services/placeholders.py:699
|
||||
msgid "Please transfer money to this bank account: 9999-9999-9999-9999"
|
||||
msgstr "Maak het geld over naar deze bankrekening: NL13 TEST 0123 4567 89"
|
||||
msgstr "Maak het geld over naar deze bankrekening: 9999-9999-9999-9999"
|
||||
|
||||
#: pretix/base/services/placeholders.py:799
|
||||
#: pretix/control/views/organizer.py:349
|
||||
@@ -11081,8 +11081,8 @@ msgid ""
|
||||
"If turned off, ticket downloads are only possible after an order has been "
|
||||
"marked as paid."
|
||||
msgstr ""
|
||||
"Als deze optie is uitgeschakeld, kunnen tickets alleen worden gedownload "
|
||||
"nadat een bestelling als betaald is gemarkeerd."
|
||||
"Indien uitgeschakeld, kunnen tickets alleen gedownload worden als de "
|
||||
"bestelling als betaald gemarkeerd is."
|
||||
|
||||
#: pretix/base/settings.py:1763
|
||||
msgid "Do not issue ticket before email address is validated"
|
||||
@@ -13801,12 +13801,12 @@ msgstr "Altijd"
|
||||
#: pretix/base/timeline.py:60
|
||||
msgctxt "timeline"
|
||||
msgid "Your event starts"
|
||||
msgstr "Uw evenement start"
|
||||
msgstr "Start van uw evenement"
|
||||
|
||||
#: pretix/base/timeline.py:68
|
||||
msgctxt "timeline"
|
||||
msgid "Your event ends"
|
||||
msgstr "Uw evenement eindigt"
|
||||
msgstr "Einde van uw evenement"
|
||||
|
||||
#: pretix/base/timeline.py:76
|
||||
msgctxt "timeline"
|
||||
@@ -17002,28 +17002,20 @@ msgid "You need to specify as many seats as voucher codes."
|
||||
msgstr "U moet evenveel stoelnummers als vouchercodes opgeven."
|
||||
|
||||
#: pretix/control/forms/waitinglist.py:39
|
||||
#, fuzzy
|
||||
#| msgid "Please select a valid seat."
|
||||
msgid "Select a valid choice."
|
||||
msgstr "Kies een geldige beschikbare stoel."
|
||||
msgstr "Maak een geldige keuze."
|
||||
|
||||
#: pretix/control/forms/waitinglist.py:107
|
||||
#, fuzzy
|
||||
#| msgid "Active products"
|
||||
msgid "Only includes active products."
|
||||
msgstr "Actieve producten"
|
||||
msgstr "Bevat alleen actieve producten."
|
||||
|
||||
#: pretix/control/forms/waitinglist.py:115
|
||||
#, fuzzy
|
||||
#| msgid "A voucher with this code already exists."
|
||||
msgid "A voucher for this waiting list entry was already sent out."
|
||||
msgstr "Er bestaat al een voucher met deze code."
|
||||
msgstr "Er is al een voucher verzonden naar deze inschrijver op de wachtlijst."
|
||||
|
||||
#: pretix/control/forms/waitinglist.py:125
|
||||
#, fuzzy
|
||||
#| msgid "The selected product has been deactivated."
|
||||
msgid "The selected product is not active."
|
||||
msgstr "Het gekozen product is uitgeschakeld."
|
||||
msgstr "Het gekozen product is niet actief."
|
||||
|
||||
#: pretix/control/logdisplay.py:73 pretix/control/logdisplay.py:83
|
||||
msgid "The order has been changed:"
|
||||
@@ -18811,6 +18803,9 @@ msgid ""
|
||||
"in repeatedly. Please check if your browser is set to block cookies, or "
|
||||
"delete all existing cookies and retry."
|
||||
msgstr ""
|
||||
"Uw browser lijkt ons cookie niet te accepteren, waardoor u telkens opnieuw "
|
||||
"moet inloggen. Controleer of uw browser cookies blokkeert of verwijder alle "
|
||||
"opgeslagen cookies en probeer het opnieuw."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/auth/login.html:35
|
||||
#: pretix/presale/templates/pretixpresale/fragment_login_status.html:19
|
||||
@@ -22359,7 +22354,7 @@ msgstr ""
|
||||
#: pretix/control/templates/pretixcontrol/item/base.html:24
|
||||
#: pretix/control/templates/pretixcontrol/item/include_variations.html:79
|
||||
msgid "Manage quotas"
|
||||
msgstr "Vragen quotums"
|
||||
msgstr "Quota beheren"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/item/base.html:27
|
||||
#: pretix/control/templates/pretixcontrol/item/include_variations.html:82
|
||||
@@ -28077,10 +28072,8 @@ msgstr ""
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/edit.html:4
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/edit.html:6
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:273
|
||||
#, fuzzy
|
||||
#| msgid "Entry"
|
||||
msgid "Edit entry"
|
||||
msgstr "Binnenkomst"
|
||||
msgstr "Inschrijving bewerken"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:17
|
||||
msgid ""
|
||||
@@ -30020,15 +30013,7 @@ msgid "Two-factor authentication is now disabled for your account."
|
||||
msgstr "Twee-factor-authenticatie is nu uitgeschakeld voor uw account."
|
||||
|
||||
#: pretix/control/views/user.py:635
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid ""
|
||||
#| "Your emergency codes have been newly generated. Remember to store them in "
|
||||
#| "a safe place in case you lose access to your devices. You will not be "
|
||||
#| "able to view them again here.\n"
|
||||
#| "\n"
|
||||
#| "Your emergency codes:\n"
|
||||
#| "- \n"
|
||||
#| "- "
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"Your emergency codes have been newly generated. Remember to store them in a "
|
||||
"safe place in case you lose access to your devices. You will not be able to "
|
||||
@@ -30042,8 +30027,7 @@ msgstr ""
|
||||
"U kunt ze hier niet meer opnieuw laten tonen.\n"
|
||||
"\n"
|
||||
"Uw noodcodes: \n"
|
||||
"-\n"
|
||||
"- "
|
||||
"{tokens}"
|
||||
|
||||
#: pretix/control/views/user.py:655
|
||||
msgid "Your notifications have been disabled."
|
||||
@@ -30210,10 +30194,8 @@ msgid "The selected entry has been deleted."
|
||||
msgstr "De gekozen inschrijving is verwijderd."
|
||||
|
||||
#: pretix/control/views/waitinglist.py:417
|
||||
#, fuzzy
|
||||
#| msgid "The waitinglist entry has been transferred."
|
||||
msgid "The waitinglist entry has been changed."
|
||||
msgstr "De wachtlijstinschrijving is overgedragen."
|
||||
msgstr "De inschrijving op de wachtlijst is veranderd."
|
||||
|
||||
#: pretix/helpers/countries.py:134
|
||||
msgid "Belarus"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-02-24 11:50+0000\n"
|
||||
"PO-Revision-Date: 2026-02-21 03:00+0000\n"
|
||||
"PO-Revision-Date: 2026-03-02 10:00+0000\n"
|
||||
"Last-Translator: Ruud Hendrickx <ruud@leckxicon.eu>\n"
|
||||
"Language-Team: Dutch (informal) <https://translate.pretix.eu/projects/pretix/"
|
||||
"pretix/nl_Informal/>\n"
|
||||
@@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 5.16\n"
|
||||
"X-Generator: Weblate 5.16.1\n"
|
||||
|
||||
#: pretix/_base_settings.py:87
|
||||
msgid "English"
|
||||
@@ -9673,7 +9673,7 @@ msgstr "Het bedrag is van je kaart afgeschreven."
|
||||
|
||||
#: pretix/base/services/placeholders.py:699
|
||||
msgid "Please transfer money to this bank account: 9999-9999-9999-9999"
|
||||
msgstr "Maak het geld over naar deze bankrekening: NL13 TEST 0123 4567 89"
|
||||
msgstr "Maak het geld over naar deze bankrekening: 9999-9999-9999-9999"
|
||||
|
||||
#: pretix/base/services/placeholders.py:799
|
||||
#: pretix/control/views/organizer.py:349
|
||||
@@ -17032,28 +17032,20 @@ msgid "You need to specify as many seats as voucher codes."
|
||||
msgstr "Je moet evenveel stoelnummers als vouchercodes opgeven."
|
||||
|
||||
#: pretix/control/forms/waitinglist.py:39
|
||||
#, fuzzy
|
||||
#| msgid "Please select a valid seat."
|
||||
msgid "Select a valid choice."
|
||||
msgstr "Kies een geldige beschikbare stoel."
|
||||
msgstr "Maak een geldige keuze."
|
||||
|
||||
#: pretix/control/forms/waitinglist.py:107
|
||||
#, fuzzy
|
||||
#| msgid "Active products"
|
||||
msgid "Only includes active products."
|
||||
msgstr "Actieve producten"
|
||||
msgstr "Bevat alleen actieve producten."
|
||||
|
||||
#: pretix/control/forms/waitinglist.py:115
|
||||
#, fuzzy
|
||||
#| msgid "A voucher with this code already exists."
|
||||
msgid "A voucher for this waiting list entry was already sent out."
|
||||
msgstr "Er bestaat al een voucher met deze code."
|
||||
msgstr "Er is al een voucher verzonden naar deze inschrijver op de wachtlijst."
|
||||
|
||||
#: pretix/control/forms/waitinglist.py:125
|
||||
#, fuzzy
|
||||
#| msgid "The selected product has been deactivated."
|
||||
msgid "The selected product is not active."
|
||||
msgstr "Het gekozen product is uitgeschakeld."
|
||||
msgstr "Het gekozen product is niet actief."
|
||||
|
||||
#: pretix/control/logdisplay.py:73 pretix/control/logdisplay.py:83
|
||||
msgid "The order has been changed:"
|
||||
@@ -18844,6 +18836,9 @@ msgid ""
|
||||
"in repeatedly. Please check if your browser is set to block cookies, or "
|
||||
"delete all existing cookies and retry."
|
||||
msgstr ""
|
||||
"Je browser lijkt ons cookie niet te accepteren, waardoor je telkens opnieuw "
|
||||
"moet inloggen. Controleer of je browser cookies blokkeert of verwijder alle "
|
||||
"opgeslagen cookies en probeer het opnieuw."
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/auth/login.html:35
|
||||
#: pretix/presale/templates/pretixpresale/fragment_login_status.html:19
|
||||
@@ -28141,10 +28136,8 @@ msgstr ""
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/edit.html:4
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/edit.html:6
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:273
|
||||
#, fuzzy
|
||||
#| msgid "Entry"
|
||||
msgid "Edit entry"
|
||||
msgstr "Binnenkomst"
|
||||
msgstr "Inschrijving bewerken"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:17
|
||||
msgid ""
|
||||
@@ -30080,15 +30073,7 @@ msgid "Two-factor authentication is now disabled for your account."
|
||||
msgstr "Twee-factor-authenticatie is nu uitgeschakeld voor je account."
|
||||
|
||||
#: pretix/control/views/user.py:635
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid ""
|
||||
#| "Your emergency codes have been newly generated. Remember to store them in "
|
||||
#| "a safe place in case you lose access to your devices. You will not be "
|
||||
#| "able to view them again here.\n"
|
||||
#| "\n"
|
||||
#| "Your emergency codes:\n"
|
||||
#| "- \n"
|
||||
#| "- "
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"Your emergency codes have been newly generated. Remember to store them in a "
|
||||
"safe place in case you lose access to your devices. You will not be able to "
|
||||
@@ -30102,8 +30087,7 @@ msgstr ""
|
||||
"Je kunt ze hier niet meer opnieuw laten tonen.\n"
|
||||
"\n"
|
||||
"Je noodcodes:\n"
|
||||
"-\n"
|
||||
"- "
|
||||
"{tokens}"
|
||||
|
||||
#: pretix/control/views/user.py:655
|
||||
msgid "Your notifications have been disabled."
|
||||
@@ -30270,10 +30254,8 @@ msgid "The selected entry has been deleted."
|
||||
msgstr "De gekozen inschrijving is verwijderd."
|
||||
|
||||
#: pretix/control/views/waitinglist.py:417
|
||||
#, fuzzy
|
||||
#| msgid "The waitinglist entry has been transferred."
|
||||
msgid "The waitinglist entry has been changed."
|
||||
msgstr "De wachtlijstvermelding is overgedragen."
|
||||
msgstr "De inschrijving op de wachtlijst is veranderd."
|
||||
|
||||
#: pretix/helpers/countries.py:134
|
||||
msgid "Belarus"
|
||||
@@ -34075,7 +34057,7 @@ msgid ""
|
||||
"completed your payment, you can refresh this page."
|
||||
msgstr ""
|
||||
"Scan de QR-code hieronder om je WeChat-betaling uit te voeren. Als je de "
|
||||
"betaling hebt afgerond kan je deze pagina verversen."
|
||||
"betaling hebt afgerond, kan je deze pagina verversen."
|
||||
|
||||
#: pretix/plugins/stripe/templates/pretixplugins/stripe/pending.html:62
|
||||
msgid ""
|
||||
@@ -35249,7 +35231,7 @@ msgstr "incl. belasting"
|
||||
#: pretix/presale/templates/pretixpresale/event/voucher.html:359
|
||||
#, python-format
|
||||
msgid "<strong>plus</strong> %(rate)s%% %(name)s"
|
||||
msgstr "<strong>excl.</strong> %(rate)s%% %(name)s"
|
||||
msgstr "<strong>plus</strong> %(rate)s%% %(name)s"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_addon_choice.html:180
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_addon_choice.html:320
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@
|
||||
{% load eventurl %}
|
||||
{% load safelink %}
|
||||
{% load rich_text %}
|
||||
{% load anonymize_email %}
|
||||
{% block thetitle %}
|
||||
{% if messages %}
|
||||
{{ messages|join:" " }} ::
|
||||
@@ -219,7 +220,7 @@
|
||||
{% endblock %}
|
||||
{% block footernav %}
|
||||
{% if request.event.settings.contact_mail %}
|
||||
<li><a href="mailto:{{ request.event.settings.contact_mail }}" target="_blank" rel="noopener">{% trans "Contact" %}</a></li>
|
||||
<li><a href="{{ 'mailto:'|add:request.event.settings.contact_mail|anon_email }}" target="_blank" rel="noopener">{% trans "Contact" %}</a></li>
|
||||
{% endif %}
|
||||
{% if request.event.settings.privacy_url %}
|
||||
<li><a href="{% safelink request.event.settings.privacy_url %}" target="_blank" rel="noopener">{% trans "Privacy policy" %}</a></li>
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
{% bootstrap_form_errors timemachine_form "all" %}
|
||||
|
||||
<p>{% trans "Test your shop as if it were a different date and time." %}</p>
|
||||
<p>{% trans "Please note that the changed time is not taken into account for aspects of the shop that affect quotas, such as the validity period of carts and vouchers." %}</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
@@ -44,4 +45,4 @@
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -21,4 +21,5 @@
|
||||
<script type="text/javascript" src="{% static "pretixpresale/js/ui/cart.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "pretixpresale/js/ui/iframe.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "pretixbase/js/addressform.js" %}"></script>
|
||||
<script type="text/javascript" src="{% static "pretixbase/js/deanonymize_email.js" %}"></script>
|
||||
{% endcompress %}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
{% load thumb %}
|
||||
{% load eventurl %}
|
||||
{% load safelink %}
|
||||
{% load anonymize_email %}
|
||||
{% block thetitle %}
|
||||
{% block title %}{% endblock %}{% if url_name != "organizer.index" %} :: {% endif %}{{ organizer.name }}
|
||||
{% endblock %}
|
||||
@@ -97,7 +98,7 @@
|
||||
{% endblock %}
|
||||
{% block footernav %}
|
||||
{% if not request.event and request.organizer.settings.contact_mail %}
|
||||
<li><a href="mailto:{{ request.organizer.settings.contact_mail }}" target="_blank" rel="noopener">{% trans "Contact" %}</a></li>
|
||||
<li><a href="{{ 'mailto:'|add:request.organizer.settings.contact_mail|anon_email }}" target="_blank" rel="noopener">{% trans "Contact" %}</a></li>
|
||||
{% endif %}
|
||||
{% if not request.event and request.organizer.settings.privacy_url %}
|
||||
<li><a href="{% safelink request.organizer.settings.privacy_url %}" target="_blank" rel="noopener">{% trans "Privacy policy" %}</a></li>
|
||||
|
||||
7
src/pretix/static/pretixbase/js/deanonymize_email.js
Normal file
7
src/pretix/static/pretixbase/js/deanonymize_email.js
Normal file
@@ -0,0 +1,7 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.querySelectorAll('a[href^="mailto:"]').forEach(function(link) {
|
||||
// Replace [at] with @ and the [dot] with . in both the href and the displayed text (if needed)
|
||||
link.href = link.href.replace('[at]', '@').replace('[dot]', '.');
|
||||
link.textContent = link.textContent.replace('[at]', '@').replace('[dot]', '.');
|
||||
});
|
||||
});
|
||||
@@ -286,12 +286,12 @@ def test_by_secret_special_chars(token_client, organizer, clist, event, order):
|
||||
@pytest.mark.django_db
|
||||
def test_by_medium(token_client, organizer, clist, event, order):
|
||||
with scopes_disabled():
|
||||
rm = ReusableMedium.objects.create(
|
||||
ReusableMedium.objects.create(
|
||||
type="barcode",
|
||||
identifier="abcdef",
|
||||
organizer=organizer,
|
||||
linked_orderposition=order.positions.first(),
|
||||
)
|
||||
rm.linked_orderpositions.add(order.positions.first())
|
||||
resp = _redeem(token_client, organizer, clist, "abcdef", {"source_type": "barcode"})
|
||||
assert resp.status_code == 201
|
||||
assert resp.data['status'] == 'ok'
|
||||
@@ -301,48 +301,6 @@ def test_by_medium(token_client, organizer, clist, event, order):
|
||||
assert ci.raw_source_type == "barcode"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_by_medium_multiple_orderpositions(token_client, organizer, clist, event, order):
|
||||
with scopes_disabled():
|
||||
rm = ReusableMedium.objects.create(
|
||||
type="barcode",
|
||||
identifier="abcdef",
|
||||
organizer=organizer,
|
||||
)
|
||||
rm.linked_orderpositions.add(order.positions.first())
|
||||
op_item_other = order.positions.all()[1]
|
||||
rm.linked_orderpositions.add(op_item_other)
|
||||
|
||||
# multiple tickets are valid => no check-in
|
||||
resp = _redeem(token_client, organizer, clist, "abcdef", {"source_type": "barcode"})
|
||||
assert resp.status_code == 400
|
||||
assert resp.data['status'] == 'error'
|
||||
assert resp.data['reason'] == 'ambiguous'
|
||||
|
||||
with scopes_disabled():
|
||||
op_item_other.valid_from = datetime.datetime(2020, 1, 1, 12, 0, 0, tzinfo=event.timezone)
|
||||
op_item_other.valid_until = datetime.datetime(2020, 1, 1, 15, 0, 0, tzinfo=event.timezone)
|
||||
op_item_other.save()
|
||||
|
||||
with freeze_time("2020-01-01 13:45:00"):
|
||||
# multiple tickets are valid => no check-in
|
||||
resp = _redeem(token_client, organizer, clist, "abcdef", {"source_type": "barcode"})
|
||||
assert resp.status_code == 400
|
||||
assert resp.data['status'] == 'error'
|
||||
assert resp.data['reason'] == 'ambiguous'
|
||||
|
||||
with freeze_time("2020-01-01 10:45:00"):
|
||||
resp = _redeem(token_client, organizer, clist, "abcdef", {"source_type": "barcode"})
|
||||
assert resp.status_code == 201
|
||||
assert resp.data['status'] == 'ok'
|
||||
|
||||
with freeze_time("2020-01-01 15:45:00"):
|
||||
resp = _redeem(token_client, organizer, clist, "abcdef", {"source_type": "barcode"})
|
||||
assert resp.status_code == 400
|
||||
assert resp.data['status'] == 'error'
|
||||
assert resp.data['reason'] == 'already_redeemed'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_by_medium_not_connected(token_client, organizer, clist, event, order):
|
||||
with scopes_disabled():
|
||||
@@ -360,12 +318,12 @@ def test_by_medium_not_connected(token_client, organizer, clist, event, order):
|
||||
@pytest.mark.django_db
|
||||
def test_by_medium_wrong_event(token_client, organizer, clist, event, order2):
|
||||
with scopes_disabled():
|
||||
rm = ReusableMedium.objects.create(
|
||||
ReusableMedium.objects.create(
|
||||
type="barcode",
|
||||
identifier="abcdef",
|
||||
organizer=organizer,
|
||||
linked_orderposition=order2.positions.first(),
|
||||
)
|
||||
rm.linked_orderpositions.add(order2.positions.first())
|
||||
resp = _redeem(token_client, organizer, clist, "abcdef", {"source_type": "barcode"})
|
||||
assert resp.status_code == 404
|
||||
assert resp.data['status'] == 'error'
|
||||
@@ -379,12 +337,12 @@ def test_by_medium_wrong_event(token_client, organizer, clist, event, order2):
|
||||
@pytest.mark.django_db
|
||||
def test_by_medium_wrong_type(token_client, organizer, clist, event, order):
|
||||
with scopes_disabled():
|
||||
rm = ReusableMedium.objects.create(
|
||||
ReusableMedium.objects.create(
|
||||
type="nfc_uid",
|
||||
identifier="abcdef",
|
||||
organizer=organizer,
|
||||
linked_orderposition=order.positions.first(),
|
||||
)
|
||||
rm.linked_orderpositions.add(order.positions.first())
|
||||
resp = _redeem(token_client, organizer, clist, "abcdef", {"source_type": "barcode"})
|
||||
assert resp.status_code == 404
|
||||
assert resp.data['status'] == 'error'
|
||||
@@ -397,13 +355,13 @@ def test_by_medium_wrong_type(token_client, organizer, clist, event, order):
|
||||
@pytest.mark.django_db
|
||||
def test_by_medium_inactive(token_client, organizer, clist, event, order):
|
||||
with scopes_disabled():
|
||||
rm = ReusableMedium.objects.create(
|
||||
ReusableMedium.objects.create(
|
||||
type="barcode",
|
||||
identifier="abcdef",
|
||||
organizer=organizer,
|
||||
active=False,
|
||||
linked_orderposition=order.positions.first(),
|
||||
)
|
||||
rm.linked_orderpositions.add(order.positions.first())
|
||||
resp = _redeem(token_client, organizer, clist, "abcdef", {"source_type": "barcode"})
|
||||
assert resp.status_code == 404
|
||||
assert resp.data['status'] == 'error'
|
||||
|
||||
@@ -895,6 +895,41 @@ def test_order_create_payment_info_optional(token_client, organizer, event, item
|
||||
assert json.loads(p.info) == res['payment_info']
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_order_create_payment_info_valid_object(token_client, organizer, event, item, quota, question):
|
||||
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
||||
res['positions'][0]['item'] = item.pk
|
||||
res['positions'][0]['answers'][0]['question'] = question.pk
|
||||
|
||||
res["payment_info"] = [{"should": "fail"}]
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
||||
organizer.slug, event.slug
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
|
||||
res['payment_info'] = {
|
||||
'foo': {
|
||||
'bar': [1, 2],
|
||||
'test': False
|
||||
}
|
||||
}
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/{}/orders/'.format(
|
||||
organizer.slug, event.slug
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 201
|
||||
with scopes_disabled():
|
||||
o = Order.objects.get(code=resp.data['code'])
|
||||
|
||||
p = o.payments.first()
|
||||
assert p.provider == "banktransfer"
|
||||
assert p.amount == o.total
|
||||
assert json.loads(p.info) == res['payment_info']
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_order_create_position_secret_optional(token_client, organizer, event, item, quota, question):
|
||||
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
||||
@@ -3086,78 +3121,9 @@ def test_order_create_use_medium(token_client, organizer, event, item, quota, qu
|
||||
with scopes_disabled():
|
||||
o = Order.objects.get(code=resp.data['code'])
|
||||
medium.refresh_from_db()
|
||||
assert o.positions.first() == medium.linked_orderpositions.first()
|
||||
assert o.positions.first() == medium.linked_orderposition
|
||||
assert resp.data['positions'][0]['pdf_data']['medium_identifier'] == medium.identifier
|
||||
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/{}/orders/?pdf_data=true'.format(
|
||||
organizer.slug, event.slug
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 201
|
||||
with scopes_disabled():
|
||||
o = Order.objects.get(code=resp.data['code'])
|
||||
medium.refresh_from_db()
|
||||
assert medium.linked_orderpositions.count() == 1
|
||||
assert o.positions.first() == medium.linked_orderpositions.first()
|
||||
assert resp.data['positions'][0]['pdf_data']['medium_identifier'] == medium.identifier
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_order_create_add_to_medium(token_client, organizer, event, item, quota, question, medium):
|
||||
item.media_type = medium.type
|
||||
item.media_policy = Item.MEDIA_POLICY_REUSE_OR_NEW
|
||||
item.save()
|
||||
res = copy.deepcopy(ORDER_CREATE_PAYLOAD)
|
||||
res['positions'][0]['item'] = item.pk
|
||||
res['positions'][0]['use_reusable_medium'] = medium.pk
|
||||
res['positions'][0]['add_to_reusable_medium'] = medium.pk
|
||||
res['positions'][0]['answers'][0]['question'] = question.pk
|
||||
|
||||
# do not use use_reusable_medium and add_to_reusable_medium
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/{}/orders/?pdf_data=true'.format(
|
||||
organizer.slug, event.slug
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
|
||||
del res['positions'][0]['use_reusable_medium']
|
||||
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/{}/orders/?pdf_data=true'.format(
|
||||
organizer.slug, event.slug
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 201
|
||||
with scopes_disabled():
|
||||
medium.refresh_from_db()
|
||||
assert medium.linked_orderpositions.count() == 1
|
||||
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/{}/orders/?pdf_data=true'.format(
|
||||
organizer.slug, event.slug
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 201
|
||||
with scopes_disabled():
|
||||
medium.refresh_from_db()
|
||||
assert medium.linked_orderpositions.count() == 2
|
||||
|
||||
res['positions'][0]['use_reusable_medium'] = medium.pk
|
||||
del res['positions'][0]['add_to_reusable_medium']
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/{}/orders/?pdf_data=true'.format(
|
||||
organizer.slug, event.slug
|
||||
), format='json', data=res
|
||||
)
|
||||
assert resp.status_code == 201
|
||||
with scopes_disabled():
|
||||
o = Order.objects.get(code=resp.data['code'])
|
||||
medium.refresh_from_db()
|
||||
assert medium.linked_orderpositions.count() == 1
|
||||
assert o.positions.first() == medium.linked_orderpositions.first()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_order_create_use_medium_other_organizer(token_client, organizer, event, item, quota, question, medium2):
|
||||
@@ -3202,7 +3168,7 @@ def test_order_create_create_medium(token_client, organizer, event, item, quota,
|
||||
i = resp.data['positions'][0]['pdf_data']['medium_identifier']
|
||||
assert i
|
||||
m = organizer.reusable_media.get(identifier=i)
|
||||
assert m.linked_orderpositions.first() == o.positions.first()
|
||||
assert m.linked_orderposition == o.positions.first()
|
||||
assert m.type == "barcode"
|
||||
|
||||
|
||||
|
||||
@@ -89,13 +89,10 @@ TEST_MEDIUM_RES = {
|
||||
"organizer": "dummy",
|
||||
"identifier": "ABCDEFGH",
|
||||
"type": "barcode",
|
||||
"claim_token": None,
|
||||
"label": None,
|
||||
"active": True,
|
||||
"expires": None,
|
||||
"customer": None,
|
||||
"linked_orderposition": None,
|
||||
"linked_orderpositions": [],
|
||||
"linked_giftcard": None,
|
||||
"notes": None,
|
||||
"info": {},
|
||||
@@ -141,7 +138,7 @@ def test_medium_detail(token_client, organizer, event, medium, giftcard, custome
|
||||
ticket = event.items.create(name='Early-bird ticket', category=None, default_price=23, admission=True,
|
||||
personalized=True)
|
||||
op = o.positions.create(item=ticket, price=Decimal("14"))
|
||||
medium.linked_orderpositions.add(op)
|
||||
medium.linked_orderposition = op
|
||||
medium.linked_giftcard = giftcard
|
||||
medium.customer = customer
|
||||
medium.save()
|
||||
@@ -422,7 +419,7 @@ def test_medium_lookup_cross_organizer(token_client, organizer, organizer2, org2
|
||||
ticket = org2_event.items.create(name='Early-bird ticket', category=None, default_price=23, admission=True,
|
||||
personalized=True)
|
||||
op = o.positions.create(item=ticket, price=Decimal("14"))
|
||||
medium2.linked_orderpositions.add(op)
|
||||
medium2.linked_orderposition = op
|
||||
medium2.linked_giftcard = giftcard2
|
||||
medium2.save()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user