From e67ff83378ccb2ac3fc4cc1ade8af7f25d65f309 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Thu, 12 Dec 2019 14:18:47 +0100 Subject: [PATCH] Do not allow to create negative gift cards through the API --- doc/api/resources/giftcards.rst | 5 +++++ src/pretix/api/serializers/organizer.py | 4 +++- src/pretix/api/views/organizer.py | 6 ++++++ src/tests/api/test_giftcards.py | 27 +++++++++++++++++++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/doc/api/resources/giftcards.rst b/doc/api/resources/giftcards.rst index 0d441a3af8..870178a60d 100644 --- a/doc/api/resources/giftcards.rst +++ b/doc/api/resources/giftcards.rst @@ -221,9 +221,14 @@ Endpoints "value": "15.37" } + .. versionchanged:: 3.5 + + This endpoint now returns status code ``409`` if the transaction would lead to a negative gift card value. + :param organizer: The ``slug`` field of the organizer to modify :param id: The ``id`` field of the gift card to modify :statuscode 200: no error :statuscode 400: The gift card could not be modified due to invalid submitted data :statuscode 401: Authentication failure :statuscode 403: The requested organizer does not exist **or** you have no permission to change this resource. + :statuscode 409: There is not sufficient credit on the gift card. diff --git a/src/pretix/api/serializers/organizer.py b/src/pretix/api/serializers/organizer.py index 622179d601..01485e58cb 100644 --- a/src/pretix/api/serializers/organizer.py +++ b/src/pretix/api/serializers/organizer.py @@ -1,3 +1,5 @@ +from decimal import Decimal + from django.db.models import Q from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers @@ -26,7 +28,7 @@ class SeatingPlanSerializer(I18nAwareModelSerializer): class GiftCardSerializer(I18nAwareModelSerializer): - value = serializers.DecimalField(max_digits=10, decimal_places=2) + value = serializers.DecimalField(max_digits=10, decimal_places=2, min_value=Decimal('0.00')) def validate(self, data): data = super().validate(data) diff --git a/src/pretix/api/views/organizer.py b/src/pretix/api/views/organizer.py index 4f3f676e44..cba3e1aae3 100644 --- a/src/pretix/api/views/organizer.py +++ b/src/pretix/api/views/organizer.py @@ -1,3 +1,5 @@ +from decimal import Decimal + from django.db import transaction from rest_framework import filters, serializers, status, viewsets from rest_framework.decorators import action @@ -136,6 +138,10 @@ class GiftCardViewSet(viewsets.ModelViewSet): value = serializers.DecimalField(max_digits=10, decimal_places=2).to_internal_value( request.data.get('value') ) + if gc.value + value < Decimal('0.00'): + return Response({ + 'value': ['The gift card does not have sufficient credit for this operation.'] + }, status=status.HTTP_409_CONFLICT) gc.transactions.create(value=value) gc.log_action( 'pretix.giftcards.transaction.manual', diff --git a/src/tests/api/test_giftcards.py b/src/tests/api/test_giftcards.py index 4f1333068c..7dc1cff1bd 100644 --- a/src/tests/api/test_giftcards.py +++ b/src/tests/api/test_giftcards.py @@ -99,6 +99,18 @@ def test_giftcard_patch(token_client, organizer, event, giftcard): assert not giftcard.testmode +@pytest.mark.django_db +def test_giftcard_patch_min_value(token_client, organizer, event, giftcard): + resp = token_client.patch( + '/api/v1/organizers/{}/giftcards/{}/'.format(organizer.slug, giftcard.pk), + { + 'value': '-10.00', + }, + format='json' + ) + assert resp.status_code == 400 + + @pytest.mark.django_db def test_giftcard_transact(token_client, organizer, event, giftcard): resp = token_client.post( @@ -113,6 +125,21 @@ def test_giftcard_transact(token_client, organizer, event, giftcard): assert giftcard.value == Decimal('33.00') +@pytest.mark.django_db +def test_giftcard_transact_min_zero(token_client, organizer, event, giftcard): + resp = token_client.post( + '/api/v1/organizers/{}/giftcards/{}/transact/'.format(organizer.slug, giftcard.pk), + { + 'value': '-100.00', + }, + format='json' + ) + assert resp.status_code == 409 + assert resp.data == {'value': ['The gift card does not have sufficient credit for this operation.']} + giftcard.refresh_from_db() + assert giftcard.value == Decimal('23.00') + + @pytest.mark.django_db def test_giftcard_no_deletion(token_client, organizer, event, giftcard): resp = token_client.delete(