From 58a9e9b28869ecaa388c6ee5c669eb1aa305eeec Mon Sep 17 00:00:00 2001 From: Mira Weller Date: Fri, 4 Apr 2025 20:30:30 +0200 Subject: [PATCH] Allow changing ticket secret via API (Z#23188201) --- src/pretix/api/serializers/orderchange.py | 6 +++++- src/pretix/base/services/orders.py | 21 +++++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/pretix/api/serializers/orderchange.py b/src/pretix/api/serializers/orderchange.py index 5998f7eebd..8c3cb896ad 100644 --- a/src/pretix/api/serializers/orderchange.py +++ b/src/pretix/api/serializers/orderchange.py @@ -251,7 +251,7 @@ class OrderPositionChangeSerializer(serializers.ModelSerializer): class Meta: model = OrderPosition fields = ( - 'item', 'variation', 'subevent', 'seat', 'price', 'tax_rule', 'valid_from', 'valid_until' + 'item', 'variation', 'subevent', 'seat', 'price', 'tax_rule', 'valid_from', 'valid_until', 'secret' ) def __init__(self, *args, **kwargs): @@ -319,6 +319,7 @@ class OrderPositionChangeSerializer(serializers.ModelSerializer): tax_rule = validated_data.get('tax_rule', instance.tax_rule) valid_from = validated_data.get('valid_from', instance.valid_from) valid_until = validated_data.get('valid_until', instance.valid_until) + secret = validated_data.get('secret', instance.secret) change_item = None if item != instance.item or variation != instance.variation: @@ -351,6 +352,9 @@ class OrderPositionChangeSerializer(serializers.ModelSerializer): if valid_until != instance.valid_until: ocm.change_valid_until(instance, valid_until) + if secret != instance.secret: + ocm.change_ticket_secret(instance, secret) + if self.context.get('commit', True): ocm.commit() instance.refresh_from_db() diff --git a/src/pretix/base/services/orders.py b/src/pretix/base/services/orders.py index 197ee22407..4be1017866 100644 --- a/src/pretix/base/services/orders.py +++ b/src/pretix/base/services/orders.py @@ -1563,7 +1563,8 @@ class OrderChangeManager: FeeValueOperation = namedtuple('FeeValueOperation', ('fee', 'value', 'price_diff')) AddFeeOperation = namedtuple('AddFeeOperation', ('fee', 'price_diff')) CancelFeeOperation = namedtuple('CancelFeeOperation', ('fee', 'price_diff')) - RegenerateSecretOperation = namedtuple('RegenerateSecretOperation', ('position',)) + RegenerateSecretOperation = namedtuple('RegenerateSecretOperation', ('position', 'force_secret')) + ChangeSecretOperation = namedtuple('ChangeSecretOperation', ('position', 'new_secret')) ChangeValidFromOperation = namedtuple('ChangeValidFromOperation', ('position', 'valid_from')) ChangeValidUntilOperation = namedtuple('ChangeValidUntilOperation', ('position', 'valid_until')) AddBlockOperation = namedtuple('AddBlockOperation', ('position', 'block_name', 'ignore_from_quota_while_blocked')) @@ -1671,6 +1672,9 @@ class OrderChangeManager: def regenerate_secret(self, position: OrderPosition): self._operations.append(self.RegenerateSecretOperation(position)) + def change_ticket_secret(self, position: OrderPosition, new_secret: str): + self._operations.append(self.ChangeSecretOperation(position, new_secret)) + def change_valid_from(self, position: OrderPosition, new_value: datetime): self._operations.append(self.ChangeValidFromOperation(position, new_value)) @@ -2428,7 +2432,7 @@ class OrderChangeManager: elif isinstance(op, self.SplitOperation): split_positions.append(op.position) elif isinstance(op, self.RegenerateSecretOperation): - op.position.web_secret = generate_secret() + op.position.web_secret = op.force_secret or generate_secret() op.position.save(update_fields=["web_secret"]) assign_ticket_secret( event=self.event, position=op.position, force_invalidate=True, save=True @@ -2441,6 +2445,19 @@ class OrderChangeManager: 'position': op.position.pk, 'positionid': op.position.positionid, }) + elif isinstance(op, self.ChangeSecretOperation): + if OrderPosition.all.filter(order__event=self.event, secret=op.new_secret).exists(): + raise OrderError('You cannot assign a position secret that already exists.') + op.position.secret = op.new_secret + op.position.save(update_fields=["secret"]) + if op.position in secret_dirty: + secret_dirty.remove(op.position) + tickets.invalidate_cache.apply_async(kwargs={'event': self.event.pk, + 'order': self.order.pk}) + self.order.log_action('pretix.event.order.changed.secret', user=self.user, auth=self.auth, data={ + 'position': op.position.pk, + 'positionid': op.position.positionid, + }) elif isinstance(op, self.ChangeValidFromOperation): self.order.log_action('pretix.event.order.changed.valid_from', user=self.user, auth=self.auth, data={ 'position': op.position.pk,