diff --git a/doc/api/resources/giftcards.rst b/doc/api/resources/giftcards.rst index 870178a60d..003b127d2a 100644 --- a/doc/api/resources/giftcards.rst +++ b/doc/api/resources/giftcards.rst @@ -59,6 +59,9 @@ Endpoints } :query integer page: The page number in case of a multi-page result set, default is 1 + :query string secret: Only show gift cards with the given secret. + :query boolean testmode: Filter for gift cards that are (not) in test mode. + :query boolean include_accepted: Also show gift cards issued by other organizers that are accepted by this organizer. :param organizer: The ``slug`` field of the organizer to fetch :statuscode 200: no error :statuscode 401: Authentication failure @@ -94,6 +97,7 @@ Endpoints :param organizer: The ``slug`` field of the organizer to fetch :param id: The ``id`` field of the gift card to fetch + :query boolean include_accepted: Also show gift cards issued by other organizers that are accepted by this organizer. :statuscode 200: no error :statuscode 401: Authentication failure :statuscode 403: The requested organizer does not exist **or** you have no permission to view this resource. @@ -227,6 +231,7 @@ Endpoints :param organizer: The ``slug`` field of the organizer to modify :param id: The ``id`` field of the gift card to modify + :query boolean include_accepted: Also show gift cards issued by other organizers that are accepted by this organizer. :statuscode 200: no error :statuscode 400: The gift card could not be modified due to invalid submitted data :statuscode 401: Authentication failure diff --git a/src/pretix/api/views/organizer.py b/src/pretix/api/views/organizer.py index ea3fa1d79f..f1d41959f2 100644 --- a/src/pretix/api/views/organizer.py +++ b/src/pretix/api/views/organizer.py @@ -1,8 +1,11 @@ from decimal import Decimal +import django_filters from django.db import transaction from django.shortcuts import get_object_or_404 from django.utils.functional import cached_property +from django_filters.rest_framework import DjangoFilterBackend, FilterSet +from django_scopes import scopes_disabled from rest_framework import filters, serializers, status, viewsets from rest_framework.decorators import action from rest_framework.exceptions import MethodNotAllowed, PermissionDenied @@ -98,14 +101,29 @@ class SeatingPlanViewSet(viewsets.ModelViewSet): instance.delete() +with scopes_disabled(): + class GiftCardFilter(FilterSet): + secret = django_filters.CharFilter(field_name='secret', lookup_expr='iexact') + + class Meta: + model = GiftCard + fields = ['secret', 'testmode'] + + class GiftCardViewSet(viewsets.ModelViewSet): serializer_class = GiftCardSerializer queryset = GiftCard.objects.none() permission = 'can_manage_gift_cards' write_permission = 'can_manage_gift_cards' + filter_backends = (DjangoFilterBackend,) + filterset_class = GiftCardFilter def get_queryset(self): - return self.request.organizer.issued_gift_cards.all() + if self.request.GET.get('include_accepted') == 'true': + qs = self.request.organizer.accepted_gift_cards + else: + qs = self.request.organizer.issued_gift_cards.all() + return qs def get_serializer_context(self): ctx = super().get_serializer_context() @@ -126,6 +144,8 @@ class GiftCardViewSet(viewsets.ModelViewSet): @transaction.atomic() def perform_update(self, serializer): + if 'include_accepted' in self.request.GET: + raise PermissionDenied("Accepted gift cards cannot be updated, use transact instead.") GiftCard.objects.select_for_update().get(pk=self.get_object().pk) old_value = serializer.instance.value value = serializer.validated_data.pop('value') diff --git a/src/tests/api/test_giftcards.py b/src/tests/api/test_giftcards.py index 7dc1cff1bd..acda447b9d 100644 --- a/src/tests/api/test_giftcards.py +++ b/src/tests/api/test_giftcards.py @@ -4,7 +4,7 @@ from decimal import Decimal import pytest from django_scopes import scopes_disabled -from pretix.base.models import GiftCard +from pretix.base.models import GiftCard, Organizer @pytest.fixture @@ -14,6 +14,14 @@ def giftcard(organizer, event): return gc +@pytest.fixture +def other_giftcard(organizer, event): + o = Organizer.objects.create(name='Dummy2', slug='dummy2') + organizer.gift_card_issuer_acceptance.create(issuer=o) + gc = o.issued_gift_cards.create(secret="GHIJK", currency="EUR") + return gc + + TEST_GC_RES = { "id": 1, "secret": "ABCDEF", @@ -24,7 +32,7 @@ TEST_GC_RES = { @pytest.mark.django_db -def test_giftcard_list(token_client, organizer, event, giftcard): +def test_giftcard_list(token_client, organizer, event, giftcard, other_giftcard): res = dict(TEST_GC_RES) res["id"] = giftcard.pk res["issuance"] = giftcard.issuance.isoformat().replace('+00:00', 'Z') @@ -33,6 +41,24 @@ def test_giftcard_list(token_client, organizer, event, giftcard): assert resp.status_code == 200 assert [res] == resp.data['results'] + resp = token_client.get('/api/v1/organizers/{}/giftcards/?testmode=true'.format(organizer.slug)) + assert resp.status_code == 200 + assert [] == resp.data['results'] + resp = token_client.get('/api/v1/organizers/{}/giftcards/?testmode=false'.format(organizer.slug)) + assert resp.status_code == 200 + assert [res] == resp.data['results'] + + resp = token_client.get('/api/v1/organizers/{}/giftcards/?secret=DEF'.format(organizer.slug)) + assert resp.status_code == 200 + assert [] == resp.data['results'] + resp = token_client.get('/api/v1/organizers/{}/giftcards/?secret=ABCDEF'.format(organizer.slug)) + assert resp.status_code == 200 + assert [res] == resp.data['results'] + + resp = token_client.get('/api/v1/organizers/{}/giftcards/?include_accepted=true'.format(organizer.slug)) + assert resp.status_code == 200 + assert 2 == len(resp.data['results']) + @pytest.mark.django_db def test_giftcard_detail(token_client, organizer, event, giftcard):