Voucher API: Bulk creation

This commit is contained in:
Raphael Michel
2018-11-09 10:50:21 +01:00
parent 0fe3db634c
commit 8c34a47138
4 changed files with 246 additions and 2 deletions

View File

@@ -1,7 +1,27 @@
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from pretix.api.serializers.i18n import I18nAwareModelSerializer
from pretix.base.models import Voucher
class VoucherListSerializer(serializers.ListSerializer):
def create(self, validated_data):
codes = set()
errs = []
err = False
for voucher_data in validated_data:
if voucher_data['code'] in codes:
err = True
errs.append({'code': ['Duplicate voucher code in request.']})
else:
codes.add(voucher_data['code'])
errs.append({})
if err:
raise ValidationError(errs)
return super().create(validated_data)
class VoucherSerializer(I18nAwareModelSerializer):
class Meta:
model = Voucher
@@ -9,6 +29,7 @@ class VoucherSerializer(I18nAwareModelSerializer):
'allow_ignore_quota', 'price_mode', 'value', 'item', 'variation', 'quota',
'tag', 'comment', 'subevent')
read_only_fields = ('id', 'redeemed')
list_serializer_class = VoucherListSerializer
def validate(self, data):
data = super().validate(data)

View File

@@ -1,11 +1,12 @@
import contextlib
from django.db import transaction
from django.db.models import F, Q
from django.utils.timezone import now
from django_filters.rest_framework import (
BooleanFilter, DjangoFilterBackend, FilterSet,
)
from rest_framework import viewsets, status
from rest_framework import status, viewsets
from rest_framework.decorators import list_route
from rest_framework.exceptions import PermissionDenied
from rest_framework.filters import OrderingFilter
@@ -56,7 +57,7 @@ class VoucherViewSet(viewsets.ModelViewSet):
return False
if 'block_quota' in data and not data.get('block_quota'):
return False
return False
if instance and 'block_quota' not in data and not instance.block_quota:
return False
@@ -111,3 +112,24 @@ class VoucherViewSet(viewsets.ModelViewSet):
auth=self.request.auth,
)
super().perform_destroy(instance)
@list_route(methods=['POST'])
def batch_create(self, request, *args, **kwargs):
if any(self._predict_quota_check(d, None) for d in request.data):
lockfn = request.event.lock
else:
lockfn = contextlib.suppress # noop context manager
with lockfn():
serializer = self.get_serializer(data=request.data, many=True)
serializer.is_valid(raise_exception=True)
with transaction.atomic():
serializer.save(event=self.request.event)
for i in serializer.instance:
i.log_action(
'pretix.voucher.added',
user=self.request.user,
auth=self.request.auth,
data=self.request.data
)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

View File

@@ -879,3 +879,134 @@ def test_redeemed_is_not_writable(token_client, organizer, event, item):
},
)
assert v.redeemed == 0
@pytest.mark.django_db
def test_create_multiple_vouchers(token_client, organizer, event, item):
resp = token_client.post(
'/api/v1/organizers/{}/events/{}/vouchers/batch_create/'.format(organizer.slug, event.slug),
data=[
{
'code': 'ABCDEFGHI',
'max_usages': 1,
'valid_until': None,
'block_quota': False,
'allow_ignore_quota': False,
'price_mode': 'set',
'value': '12.00',
'item': item.pk,
'variation': None,
'quota': None,
'tag': 'Foo',
'comment': '',
'subevent': None
},
{
'code': 'JKLMNOPQR',
'max_usages': 1,
'valid_until': None,
'block_quota': True,
'allow_ignore_quota': False,
'price_mode': 'set',
'value': '12.00',
'item': item.pk,
'variation': None,
'quota': None,
'tag': 'Foo',
'comment': '',
'subevent': None
}
], format='json'
)
assert resp.status_code == 201
assert Voucher.objects.count() == 2
assert resp.data[0]['code'] == 'ABCDEFGHI'
v1 = Voucher.objects.get(code='ABCDEFGHI')
assert not v1.block_quota
assert resp.data[1]['code'] == 'JKLMNOPQR'
v2 = Voucher.objects.get(code='JKLMNOPQR')
assert v2.block_quota
@pytest.mark.django_db
def test_create_multiple_vouchers_one_invalid(token_client, organizer, event, item):
resp = token_client.post(
'/api/v1/organizers/{}/events/{}/vouchers/batch_create/'.format(organizer.slug, event.slug),
data=[
{
'code': 'ABCDEFGHI',
'max_usages': 1,
'valid_until': None,
'block_quota': False,
'allow_ignore_quota': False,
'price_mode': 'set',
'value': '12.00',
'item': item.pk,
'variation': None,
'quota': None,
'tag': 'Foo',
'comment': '',
'subevent': None
},
{
'code': 'J',
'max_usages': 1,
'valid_until': None,
'block_quota': True,
'allow_ignore_quota': False,
'price_mode': 'set',
'value': '12.00',
'item': item.pk,
'variation': None,
'quota': None,
'tag': 'Foo',
'comment': '',
'subevent': None
}
], format='json'
)
assert resp.status_code == 400
assert resp.data == [{}, {'code': ['Ensure this field has at least 5 characters.']}]
assert Voucher.objects.count() == 0
@pytest.mark.django_db
def test_create_multiple_vouchers_duplicate_code(token_client, organizer, event, item):
resp = token_client.post(
'/api/v1/organizers/{}/events/{}/vouchers/batch_create/'.format(organizer.slug, event.slug),
data=[
{
'code': 'ABCDEFGHI',
'max_usages': 1,
'valid_until': None,
'block_quota': False,
'allow_ignore_quota': False,
'price_mode': 'set',
'value': '12.00',
'item': item.pk,
'variation': None,
'quota': None,
'tag': 'Foo',
'comment': '',
'subevent': None
},
{
'code': 'ABCDEFGHI',
'max_usages': 1,
'valid_until': None,
'block_quota': True,
'allow_ignore_quota': False,
'price_mode': 'set',
'value': '12.00',
'item': item.pk,
'variation': None,
'quota': None,
'tag': 'Foo',
'comment': '',
'subevent': None
}
], format='json'
)
assert resp.status_code == 400
assert resp.data == [{}, {'code': ['Duplicate voucher code in request.']}]
assert Voucher.objects.count() == 0