forked from CGM_Public/pretix_original
Voucher API: Bulk creation
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user