diff --git a/doc/api/resources/checkinlists.rst b/doc/api/resources/checkinlists.rst index 1aaaec8358..099dcb230c 100644 --- a/doc/api/resources/checkinlists.rst +++ b/doc/api/resources/checkinlists.rst @@ -351,6 +351,9 @@ Order position endpoints * An additional boolean property ``require_attention`` will inform you whether either the order or the item have the ``checkin_attention`` flag set. + * If ``attendee_name`` is empty, it will automatically fall back to values from a parent product or from invoice + addresses. + **Example request**: .. sourcecode:: http @@ -453,8 +456,15 @@ Order position endpoints .. http:get:: /api/v1/organizers/(organizer)/events/(event)/checkinlists/(list)/positions/(id)/ Returns information on one order position, identified by its internal ID. - The result format is the same as the :ref:`order-position-resource`, with one important difference: the - ``checkins`` value will only include check-ins for the selected list. + The result is the same as the :ref:`order-position-resource`, with the following differences: + + * The ``checkins`` value will only include check-ins for the selected list. + + * An additional boolean property ``require_attention`` will inform you whether either the order or the item + have the ``checkin_attention`` flag set. + + * If ``attendee_name`` is empty, it will automatically fall back to values from a parent product or from invoice + addresses. **Instead of an ID, you can also use the ``secret`` field as the lookup parameter.** diff --git a/src/pretix/api/serializers/order.py b/src/pretix/api/serializers/order.py index 91d761424b..7738d99ac9 100644 --- a/src/pretix/api/serializers/order.py +++ b/src/pretix/api/serializers/order.py @@ -184,8 +184,40 @@ class RequireAttentionField(serializers.Field): return instance.order.checkin_attention or instance.item.checkin_attention +class AttendeeNameField(serializers.Field): + def to_representation(self, instance: OrderPosition): + an = instance.attendee_name + if not an: + if instance.addon_to_id: + an = instance.addon_to.attendee_name + if not an: + try: + an = instance.order.invoice_address.name + except InvoiceAddress.DoesNotExist: + pass + return an + + +class AttendeeNamePartsField(serializers.Field): + def to_representation(self, instance: OrderPosition): + an = instance.attendee_name + p = instance.attendee_name_parts + if not an: + if instance.addon_to_id: + an = instance.addon_to.attendee_name + p = instance.addon_to.attendee_name_parts + if not an: + try: + p = instance.order.invoice_address.name_parts + except InvoiceAddress.DoesNotExist: + pass + return p + + class CheckinListOrderPositionSerializer(OrderPositionSerializer): require_attention = RequireAttentionField(source='*') + attendee_name = AttendeeNameField(source='*') + attendee_name_parts = AttendeeNamePartsField(source='*') order__status = serializers.SlugRelatedField(read_only=True, slug_field='status', source='order') class Meta: diff --git a/src/pretix/api/views/checkin.py b/src/pretix/api/views/checkin.py index fc74bd8933..f02bcb93bd 100644 --- a/src/pretix/api/views/checkin.py +++ b/src/pretix/api/views/checkin.py @@ -229,7 +229,7 @@ class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet): ) )) ).select_related( - 'item', 'variation', 'item__category', 'addon_to', 'order' + 'item', 'variation', 'item__category', 'addon_to', 'order', 'order__invoice_address' ) else: qs = qs.prefetch_related( diff --git a/src/tests/api/test_checkin.py b/src/tests/api/test_checkin.py index c758f8f3a8..bf53da3902 100644 --- a/src/tests/api/test_checkin.py +++ b/src/tests/api/test_checkin.py @@ -474,6 +474,23 @@ def test_custom_datetime(token_client, organizer, clist, event, order): assert Checkin.objects.last().datetime == dt +@pytest.mark.django_db +def test_name_fallback(token_client, organizer, clist, event, order): + order.invoice_address.name_parts = {'_legacy': 'Paul'} + order.invoice_address.save() + op = order.positions.first() + op.attendee_name_cached = None + op.attendee_name_parts = {} + op.save() + resp = token_client.post('/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/{}/redeem/'.format( + organizer.slug, event.slug, clist.pk, op.pk + ), {}, format='json') + assert resp.status_code == 201 + assert resp.data['status'] == 'ok' + assert resp.data['position']['attendee_name'] == 'Paul' + assert resp.data['position']['attendee_name_parts'] == {'_legacy': 'Paul'} + + @pytest.mark.django_db def test_by_secret(token_client, organizer, clist, event, order): resp = token_client.post('/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/{}/redeem/'.format(