Add an API

This commit is contained in:
Raphael Michel
2019-09-18 20:48:08 +02:00
parent f22d5915ea
commit b07d9d167d
13 changed files with 408 additions and 10 deletions

View File

@@ -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')

View File

@@ -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)

View File

@@ -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.")

View 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')},
),
]

View File

@@ -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(

View File

@@ -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

View File

@@ -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,

View 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

View File

@@ -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),
]

View File

@@ -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),
]

View File

@@ -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),