diff --git a/doc/api/resources/orders.rst b/doc/api/resources/orders.rst index d345541506..8f0ff758a0 100644 --- a/doc/api/resources/orders.rst +++ b/doc/api/resources/orders.rst @@ -1069,8 +1069,7 @@ Creating orders * ``valid_from`` (optional, if both ``valid_from`` and ``valid_until`` are **missing** (not ``null``) the availability will be computed from the given product) * ``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) + * ``use_reusable_medium`` (optional, causes the new ticket to be connected 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`` diff --git a/src/pretix/api/serializers/order.py b/src/pretix/api/serializers/order.py index 0cfcb3a46f..eef2508a6f 100644 --- a/src/pretix/api/serializers/order.py +++ b/src/pretix/api/serializers/order.py @@ -1043,15 +1043,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) @@ -1063,8 +1061,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(): @@ -1080,9 +1076,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( @@ -1157,12 +1150,6 @@ class OrderPositionCreateSerializer(I18nAwareModelSerializer): {'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 @@ -1602,7 +1589,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 not in ('answers', '_quotas', 'use_reusable_medium')}) if simulate: pos.order = order._wrapped else: @@ -1676,7 +1663,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) @@ -1718,14 +1704,17 @@ class OrderCreateSerializer(I18nAwareModelSerializer): answ.options.add(*options) if use_reusable_medium: - for op_pk in use_reusable_medium.linked_orderpositions.values_list('pk', flat=True): - use_reusable_medium.log_action( - 'pretix.reusable_medium.linked_orderposition.removed', - data={ - 'linked_orderposition': op_pk, - } - ) - use_reusable_medium.linked_orderpositions.set([pos]) + if pos.item.media_policy not in (Item.MEDIA_POLICY_APPEND, Item.MEDIA_POLICY_APPEND_OR_NEW): + for op_pk in use_reusable_medium.linked_orderpositions.values_list('pk', flat=True): + use_reusable_medium.log_action( + 'pretix.reusable_medium.linked_orderposition.removed', + data={ + 'linked_orderposition': op_pk, + } + ) + use_reusable_medium.linked_orderpositions.set([pos]) + else: + use_reusable_medium.linked_orderpositions.add(pos) use_reusable_medium.log_action( 'pretix.reusable_medium.linked_orderposition.added', data={ @@ -1733,15 +1722,6 @@ class OrderCreateSerializer(I18nAwareModelSerializer): 'linked_orderposition': pos.pk, } ) - elif 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: diff --git a/src/tests/api/test_order_create.py b/src/tests/api/test_order_create.py index 686405c5d9..134ae844d5 100644 --- a/src/tests/api/test_order_create.py +++ b/src/tests/api/test_order_create.py @@ -3141,23 +3141,11 @@ def test_order_create_use_medium(token_client, organizer, event, item, quota, qu @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.media_policy = Item.MEDIA_POLICY_APPEND_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( @@ -3179,8 +3167,9 @@ def test_order_create_add_to_medium(token_client, organizer, event, item, quota, medium.refresh_from_db() assert medium.linked_orderpositions.count() == 2 + item.media_policy = Item.MEDIA_POLICY_REUSE_OR_NEW + item.save() 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