forked from CGM_Public/pretix_original
Add an API
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
from django.db.models import Q
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
from pretix.api.serializers.i18n import I18nAwareModelSerializer
|
||||
from pretix.api.serializers.order import CompatibleJSONField
|
||||
from pretix.base.models import Organizer, SeatingPlan
|
||||
from pretix.base.models import GiftCard, Organizer, SeatingPlan
|
||||
from pretix.base.models.seating import SeatingPlanLayoutValidator
|
||||
|
||||
|
||||
@@ -18,3 +23,27 @@ class SeatingPlanSerializer(I18nAwareModelSerializer):
|
||||
class Meta:
|
||||
model = SeatingPlan
|
||||
fields = ('id', 'name', 'layout')
|
||||
|
||||
|
||||
class GiftCardSerializer(I18nAwareModelSerializer):
|
||||
value = serializers.DecimalField(max_digits=10, decimal_places=2)
|
||||
|
||||
def validate(self, data):
|
||||
data = super().validate(data)
|
||||
s = data['secret']
|
||||
qs = GiftCard.objects.filter(
|
||||
secret=s
|
||||
).filter(
|
||||
Q(issuer=self.context["organizer"]) | Q(issuer__gift_card_collector_acceptance__collector=self.context["organizer"])
|
||||
)
|
||||
if self.instance:
|
||||
qs = qs.exclude(pk=self.instance.pk)
|
||||
if qs.exists():
|
||||
raise ValidationError(
|
||||
{'secret': _('A gift card with the same secret already exists in your or an affiliated organizer account.')}
|
||||
)
|
||||
return data
|
||||
|
||||
class Meta:
|
||||
model = GiftCard
|
||||
fields = ('id', 'secret', 'issuance', 'value', 'currency')
|
||||
|
||||
@@ -19,6 +19,7 @@ orga_router.register(r'events', event.EventViewSet)
|
||||
orga_router.register(r'subevents', event.SubEventViewSet)
|
||||
orga_router.register(r'webhooks', webhooks.WebHookViewSet)
|
||||
orga_router.register(r'seatingplans', organizer.SeatingPlanViewSet)
|
||||
orga_router.register(r'giftcards', organizer.GiftCardViewSet)
|
||||
|
||||
event_router = routers.DefaultRouter()
|
||||
event_router.register(r'subevents', event.SubEventViewSet)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
from django.db import transaction
|
||||
from rest_framework import filters, viewsets
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
from rest_framework.exceptions import MethodNotAllowed, PermissionDenied
|
||||
|
||||
from pretix.api.models import OAuthAccessToken
|
||||
from pretix.api.serializers.organizer import (
|
||||
OrganizerSerializer, SeatingPlanSerializer,
|
||||
GiftCardSerializer, OrganizerSerializer, SeatingPlanSerializer,
|
||||
)
|
||||
from pretix.base.models import Organizer, SeatingPlan
|
||||
from pretix.base.models import GiftCard, Organizer, SeatingPlan
|
||||
from pretix.helpers.dicts import merge_dicts
|
||||
|
||||
|
||||
@@ -81,3 +82,48 @@ class SeatingPlanViewSet(viewsets.ModelViewSet):
|
||||
data={'id': instance.pk}
|
||||
)
|
||||
instance.delete()
|
||||
|
||||
|
||||
class GiftCardViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = GiftCardSerializer
|
||||
queryset = GiftCard.objects.none()
|
||||
permission = 'can_manage_gift_cards'
|
||||
write_permission = 'can_manage_gift_cards'
|
||||
|
||||
def get_queryset(self):
|
||||
return self.request.organizer.issued_gift_cards.all()
|
||||
|
||||
def get_serializer_context(self):
|
||||
ctx = super().get_serializer_context()
|
||||
ctx['organizer'] = self.request.organizer
|
||||
return ctx
|
||||
|
||||
@transaction.atomic()
|
||||
def perform_create(self, serializer):
|
||||
value = serializer.validated_data.pop('value')
|
||||
inst = serializer.save(issuer=self.request.organizer)
|
||||
inst.transactions.create(value=value)
|
||||
self.request.organizer.log_action(
|
||||
'pretix.giftcards.transaction.manual',
|
||||
user=self.request.user,
|
||||
auth=self.request.auth,
|
||||
data=merge_dicts(self.request.data, {'id': inst.pk})
|
||||
)
|
||||
|
||||
@transaction.atomic()
|
||||
def perform_update(self, serializer):
|
||||
old_value = serializer.instance.value
|
||||
value = serializer.validated_data.pop('value')
|
||||
inst = serializer.save(secret=serializer.instance.secret, currency=serializer.instance.currency)
|
||||
diff = value - old_value
|
||||
inst.transactions.create(value=diff)
|
||||
self.request.organizer.log_action(
|
||||
'pretix.giftcards.transaction.manual',
|
||||
user=self.request.user,
|
||||
auth=self.request.auth,
|
||||
data={'value': diff}
|
||||
)
|
||||
return inst
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
raise MethodNotAllowed("Gift cards cannot be deleted.")
|
||||
|
||||
26
src/pretix/base/migrations/0137_auto_20190918_1820.py
Normal file
26
src/pretix/base/migrations/0137_auto_20190918_1820.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# Generated by Django 2.2.1 on 2019-09-18 18:20
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
import pretix.base.models.fields
|
||||
import pretix.base.models.giftcards
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0136_auto_20190918_1537'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='giftcard',
|
||||
name='secret',
|
||||
field=models.CharField(db_index=True, default=pretix.base.models.giftcards.gen_giftcard_secret, max_length=190),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='giftcard',
|
||||
unique_together={('secret', 'issuer')},
|
||||
),
|
||||
]
|
||||
@@ -48,7 +48,6 @@ class GiftCard(LoggedModel):
|
||||
secret = models.CharField(
|
||||
max_length=190,
|
||||
default=gen_giftcard_secret,
|
||||
unique=True,
|
||||
db_index=True,
|
||||
verbose_name=_('Gift card code'),
|
||||
)
|
||||
@@ -62,6 +61,9 @@ class GiftCard(LoggedModel):
|
||||
def value(self):
|
||||
return self.transactions.aggregate(s=Sum('value'))['s'] or Decimal('0.00')
|
||||
|
||||
class Meta:
|
||||
unique_together = (('secret', 'issuer'),)
|
||||
|
||||
|
||||
class GiftCardTransaction(models.Model):
|
||||
card = models.ForeignKey(
|
||||
|
||||
@@ -341,7 +341,7 @@ class GiftCardCreateForm(forms.ModelForm):
|
||||
self.organizer = kwargs.pop('organizer')
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def clean_secret(self):
|
||||
def validate_secret(self):
|
||||
s = self.cleaned_data['secret']
|
||||
if GiftCard.objects.filter(
|
||||
secret=s
|
||||
|
||||
@@ -70,6 +70,7 @@ def event3(organizer, meta_prop):
|
||||
def team(organizer):
|
||||
return Team.objects.create(
|
||||
organizer=organizer,
|
||||
can_manage_gift_cards=True,
|
||||
can_change_items=True,
|
||||
can_create_events=True,
|
||||
can_change_event_settings=True,
|
||||
|
||||
103
src/tests/api/test_giftcards.py
Normal file
103
src/tests/api/test_giftcards.py
Normal file
@@ -0,0 +1,103 @@
|
||||
import copy
|
||||
from decimal import Decimal
|
||||
|
||||
import pytest
|
||||
from django_scopes import scopes_disabled
|
||||
|
||||
from pretix.base.models import GiftCard
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def giftcard(organizer, event):
|
||||
gc = organizer.issued_gift_cards.create(secret="ABCDEF", currency="EUR")
|
||||
gc.transactions.create(value=Decimal('23.00'))
|
||||
return gc
|
||||
|
||||
|
||||
TEST_GC_RES = {
|
||||
"id": 1,
|
||||
"secret": "ABCDEF",
|
||||
"value": "23.00",
|
||||
"currency": "EUR"
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_giftcard_list(token_client, organizer, event, giftcard):
|
||||
res = dict(TEST_GC_RES)
|
||||
res["id"] = giftcard.pk
|
||||
res["issuance"] = giftcard.issuance.isoformat().replace('+00:00', 'Z')
|
||||
|
||||
resp = token_client.get('/api/v1/organizers/{}/giftcards/'.format(organizer.slug))
|
||||
assert resp.status_code == 200
|
||||
assert [res] == resp.data['results']
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_giftcard_detail(token_client, organizer, event, giftcard):
|
||||
res = dict(TEST_GC_RES)
|
||||
res["id"] = giftcard.pk
|
||||
res["issuance"] = giftcard.issuance.isoformat().replace('+00:00', 'Z')
|
||||
resp = token_client.get('/api/v1/organizers/{}/giftcards/{}/'.format(organizer.slug, giftcard.pk))
|
||||
assert resp.status_code == 200
|
||||
assert res == resp.data
|
||||
|
||||
|
||||
TEST_GIFTCARD_CREATE_PAYLOAD = {
|
||||
"secret": "DEFABC",
|
||||
"value": "12.00",
|
||||
"currency": "EUR",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_giftcard_create(token_client, organizer, event):
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/giftcards/'.format(organizer.slug),
|
||||
TEST_GIFTCARD_CREATE_PAYLOAD,
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 201
|
||||
with scopes_disabled():
|
||||
gc = GiftCard.objects.get(pk=resp.data['id'])
|
||||
assert gc.issuer == organizer
|
||||
assert gc.value == Decimal('12.00')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_giftcard_duplicate_secert(token_client, organizer, event, giftcard):
|
||||
res = copy.copy(TEST_GIFTCARD_CREATE_PAYLOAD)
|
||||
res['secret'] = 'ABCDEF'
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/giftcards/'.format(organizer.slug),
|
||||
res,
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {'secret': ['A gift card with the same secret already exists in your or an affiliated organizer account.']}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_giftcard_patch(token_client, organizer, event, giftcard):
|
||||
resp = token_client.patch(
|
||||
'/api/v1/organizers/{}/giftcards/{}/'.format(organizer.slug, giftcard.pk),
|
||||
{
|
||||
'secret': 'foo',
|
||||
'value': '10.00',
|
||||
'currency': 'USD'
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
giftcard.refresh_from_db()
|
||||
assert giftcard.value == Decimal('10.00')
|
||||
assert giftcard.secret == "ABCDEF"
|
||||
assert giftcard.currency == "EUR"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_giftcard_no_deletion(token_client, organizer, event, giftcard):
|
||||
resp = token_client.delete(
|
||||
'/api/v1/organizers/{}/giftcards/{}/'.format(organizer.slug, giftcard.pk),
|
||||
)
|
||||
assert resp.status_code == 405
|
||||
@@ -137,6 +137,11 @@ org_permission_sub_urls = [
|
||||
('put', 'can_change_organizer_settings', 'webhooks/1/', 404),
|
||||
('patch', 'can_change_organizer_settings', 'webhooks/1/', 404),
|
||||
('delete', 'can_change_organizer_settings', 'webhooks/1/', 404),
|
||||
('get', 'can_manage_gift_cards', 'giftcards/', 200),
|
||||
('post', 'can_manage_gift_cards', 'giftcards/', 400),
|
||||
('get', 'can_manage_gift_cards', 'giftcards/1/', 404),
|
||||
('put', 'can_manage_gift_cards', 'giftcards/1/', 404),
|
||||
('patch', 'can_manage_gift_cards', 'giftcards/1/', 404),
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -141,6 +141,9 @@ organizer_urls = [
|
||||
'organizer/abc/webhook/add',
|
||||
'organizer/abc/webhook/1/edit',
|
||||
'organizer/abc/webhook/1/logs',
|
||||
'organizer/abc/giftcards',
|
||||
'organizer/abc/giftcard/add',
|
||||
'organizer/abc/giftcard/1/',
|
||||
]
|
||||
|
||||
|
||||
@@ -390,10 +393,9 @@ organizer_permission_urls = [
|
||||
("can_change_organizer_settings", "organizer/dummy/device/1/edit", 404),
|
||||
("can_change_organizer_settings", "organizer/dummy/device/1/connect", 404),
|
||||
("can_change_organizer_settings", "organizer/dummy/device/1/revoke", 404),
|
||||
("can_change_organizer_settings", "organizer/dummy/webhooks", 200),
|
||||
("can_change_organizer_settings", "organizer/dummy/webhook/add", 200),
|
||||
("can_change_organizer_settings", "organizer/dummy/webhook/1/edit", 404),
|
||||
("can_change_organizer_settings", "organizer/dummy/webhook/1/logs", 404),
|
||||
("can_manage_gift_cards", "organizer/dummy/giftcards", 200),
|
||||
("can_manage_gift_cards", "organizer/dummy/giftcard/add", 200),
|
||||
("can_manage_gift_cards", "organizer/dummy/giftcard/1/", 404),
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -102,6 +102,7 @@ def logged_in_client(client, event):
|
||||
('/control/organizer/{orga}/teams', 200),
|
||||
('/control/organizer/{orga}/devices', 200),
|
||||
('/control/organizer/{orga}/webhooks', 200),
|
||||
('/control/organizer/{orga}/giftcards', 200),
|
||||
|
||||
('/control/events/', 200),
|
||||
('/control/events/add', 200),
|
||||
|
||||
Reference in New Issue
Block a user