Compare commits

...

5 Commits

Author SHA1 Message Date
luelista
d5e2aea766 Add note to docs 2025-04-07 11:07:37 +02:00
luelista
1ddfe06b92 Apply suggestions from code review 2025-04-07 09:50:57 +02:00
Mira Weller
600009e6fb Add to docs 2025-04-04 20:38:19 +02:00
Mira Weller
866dffe617 Add test case 2025-04-04 20:36:16 +02:00
Mira Weller
58a9e9b288 Allow changing ticket secret via API (Z#23188201) 2025-04-04 20:30:30 +02:00
4 changed files with 52 additions and 1 deletions

View File

@@ -1943,9 +1943,14 @@ Manipulating individual positions
* ``valid_until``
* ``secret``
Changing parameters such as ``item`` or ``price`` will **not** automatically trigger creation of a new invoice,
you need to take care of that yourself.
Changing ``secret`` does not cause a new PDF ticket to be sent to the customer, nor does it cause the old secret
to be added to the revocation list, even if your ticket generator uses one.
**Example request**:
.. sourcecode:: http

View File

@@ -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()

View File

@@ -1564,6 +1564,7 @@ class OrderChangeManager:
AddFeeOperation = namedtuple('AddFeeOperation', ('fee', 'price_diff'))
CancelFeeOperation = namedtuple('CancelFeeOperation', ('fee', 'price_diff'))
RegenerateSecretOperation = namedtuple('RegenerateSecretOperation', ('position',))
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))
@@ -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,

View File

@@ -1468,6 +1468,31 @@ def test_position_update_change_price_and_tax_rule(token_client, organizer, even
assert op.tax_rule == tr
@pytest.mark.django_db
def test_position_update_secret(token_client, organizer, event, order, item):
with scopes_disabled():
order.positions.create(item=item, price=Decimal('23.00'), secret='alreadyused')
p = order.positions.first()
psw = p.web_secret
resp = token_client.patch(
'/api/v1/organizers/{}/events/{}/orderpositions/{}/'.format(
organizer.slug, event.slug, p.pk,
), format='json', data={'secret': 'nobodyknows'}
)
assert resp.status_code == 200
p.refresh_from_db()
with scopes_disabled():
assert 'nobodyknows' == p.secret
assert psw == p.web_secret
resp = token_client.patch(
'/api/v1/organizers/{}/events/{}/orderpositions/{}/'.format(
organizer.slug, event.slug, p.pk,
), format='json', data={'secret': 'alreadyused'}
)
assert resp.status_code == 400
@pytest.mark.django_db
def test_position_add_simple(token_client, organizer, event, order, quota, item):
with scopes_disabled():