split out organizer list endpoint

This commit is contained in:
Lukas Bockstaller
2026-01-28 17:31:46 +01:00
parent 0b3f3662cc
commit 087a56bc00
4 changed files with 142 additions and 143 deletions

View File

@@ -67,7 +67,7 @@ orga_router.register(r'invoices', order.InvoiceViewSet)
orga_router.register(r'scheduled_exports', exporters.ScheduledOrganizerExportViewSet)
orga_router.register(r'exporters', exporters.OrganizerExportersViewSet, basename='exporters')
orga_router.register(r'transactions', order.OrganizerTransactionViewSet)
orga_router.register(r'orderpositions', order.OrderPositionViewSet)
orga_router.register(r'orderpositions', order.OrganizerPositionViewSet, basename='orderpositions')
team_router = routers.DefaultRouter()
team_router.register(r'members', organizer.TeamMemberViewSet)
@@ -84,7 +84,7 @@ event_router.register(r'discounts', discount.DiscountViewSet)
event_router.register(r'quotas', item.QuotaViewSet)
event_router.register(r'vouchers', voucher.VoucherViewSet)
event_router.register(r'orders', order.EventOrderViewSet)
event_router.register(r'orderpositions', order.OrderPositionViewSet)
event_router.register(r'orderpositions', order.EventOrderPositionViewSet)
event_router.register(r'transactions', order.TransactionViewSet)
event_router.register(r'invoices', order.InvoiceViewSet)
event_router.register(r'revokedsecrets', order.RevokedSecretViewSet, basename='revokedsecrets')

View File

@@ -41,12 +41,11 @@ from django.utils.translation import gettext as _
from django_filters.rest_framework import DjangoFilterBackend, FilterSet
from django_scopes import scopes_disabled
from PIL import Image
from rest_framework import serializers, status, viewsets
from rest_framework import mixins, serializers, status, viewsets
from rest_framework.decorators import action
from rest_framework.exceptions import (
APIException, NotFound, PermissionDenied, ValidationError,
)
from rest_framework.mixins import CreateModelMixin
from rest_framework.permissions import SAFE_METHODS
from rest_framework.response import Response
@@ -1065,7 +1064,7 @@ with scopes_disabled():
}
class OrderPositionViewSet(viewsets.ModelViewSet):
class OrderPositionViewSetMixin:
serializer_class = OrderPositionSerializer
queryset = OrderPosition.all.none()
filter_backends = (DjangoFilterBackend, RichOrderingFilter)
@@ -1087,10 +1086,8 @@ class OrderPositionViewSet(viewsets.ModelViewSet):
def get_serializer_context(self):
ctx = super().get_serializer_context()
if hasattr(self.request, 'event'):
ctx['event'] = self.request.event
ctx['pdf_data'] = self.request.query_params.get('pdf_data', 'false').lower() == 'true'
ctx['check_quotas'] = self.request.query_params.get('check_quotas', 'true').lower() == 'true'
ctx['pdf_data'] = False
return ctx
def get_queryset(self):
@@ -1099,22 +1096,6 @@ class OrderPositionViewSet(viewsets.ModelViewSet):
else:
qs = OrderPosition.objects
perm = self.permission if self.request.method in SAFE_METHODS else self.write_permission
if hasattr(self.request, 'event'):
qs = qs.filter(order__event=self.request.event)
else:
if isinstance(self.request.auth, (TeamAPIToken, Device)):
qs = qs.filter(
order__event__organizer=self.request.organizer,
order__event__in=self.request.auth.get_events_with_permission(perm, request=self.request)
)
elif self.request.user.is_authenticated:
qs = qs.filter(
order__event__organizer=self.request.organizer,
order__event__in=self.request.auth.get_events_with_permission(perm, request=self.request)
)
if self.request.query_params.get('pdf_data', 'false').lower() == 'true':
prefetch_related_objects([self.request.organizer], 'meta_properties')
prefetch_related_objects(
@@ -1184,6 +1165,40 @@ class OrderPositionViewSet(viewsets.ModelViewSet):
return prov
raise NotFound('Unknown output provider.')
class OrganizerPositionViewSet(OrderPositionViewSetMixin, viewsets.ReadOnlyModelViewSet):
def get_queryset(self):
qs = super().get_queryset()
perm = self.permission if self.request.method in SAFE_METHODS else self.write_permission
if isinstance(self.request.auth, (TeamAPIToken, Device)):
qs = qs.filter(
order__event__organizer=self.request.organizer,
order__event__in=self.request.auth.get_events_with_permission(perm, request=self.request)
)
elif self.request.user.is_authenticated:
qs = qs.filter(
order__event__organizer=self.request.organizer,
order__event__in=self.request.auth.get_events_with_permission(perm, request=self.request)
)
return qs
class EventOrderPositionViewSet(OrderPositionViewSetMixin, viewsets.ModelViewSet):
def get_serializer_context(self):
ctx = super().get_serializer_context()
if hasattr(self.request, 'event'):
ctx['event'] = self.request.event
ctx['pdf_data'] = self.request.query_params.get('pdf_data', 'false').lower() == 'true'
return ctx
def get_queryset(self):
qs = super().get_queryset()
qs = qs.filter(order__event=self.request.event)
return qs
@action(detail=True, methods=['POST'], url_name='price_calc')
def price_calc(self, request, *args, **kwargs):
"""
@@ -1587,7 +1602,7 @@ class OrderPositionViewSet(viewsets.ModelViewSet):
return Response(self.get_serializer_class()(instance=serializer.instance, context=self.get_serializer_context()).data)
class PaymentViewSet(CreateModelMixin, viewsets.ReadOnlyModelViewSet):
class PaymentViewSet(mixins.CreateModelMixin, viewsets.ReadOnlyModelViewSet):
serializer_class = OrderPaymentSerializer
queryset = OrderPayment.objects.none()
permission = 'can_view_orders'
@@ -1760,7 +1775,7 @@ class PaymentViewSet(CreateModelMixin, viewsets.ReadOnlyModelViewSet):
return self.retrieve(request, [], **kwargs)
class RefundViewSet(CreateModelMixin, viewsets.ReadOnlyModelViewSet):
class RefundViewSet(mixins.CreateModelMixin, viewsets.ReadOnlyModelViewSet):
serializer_class = OrderRefundSerializer
queryset = OrderRefund.objects.none()
permission = 'can_view_orders'

View File

@@ -1022,17 +1022,21 @@ def test_refund_cancel(token_client, organizer, event, order):
assert resp.status_code == 400
@pytest.mark.parametrize("endpoint_template, response_code", [('/api/v1/organizers/{}/events/{}/orderpositions/', 403),('/api/v1/organizers/{}/orderpositions/', 200)])
@pytest.mark.parametrize(
"endpoint_template, response_code",
[('/api/v1/organizers/{}/events/{}/orderpositions/', 403), ('/api/v1/organizers/{}/orderpositions/', 200)]
)
@pytest.mark.django_db
def test_orderposition_list_limited_read(endpoint_template, response_code, limited_token_client, organizer, device, event, order, item, subevent, subevent2, question):
endpoint = endpoint_template.format(organizer.slug, event.slug)
def test_orderposition_list_limited_read(
endpoint_template, response_code, limited_token_client, organizer, device, event, order, item, subevent, subevent2, question
):
endpoint = endpoint_template.format(organizer.slug, event.slug)
i2 = copy.copy(item)
i2.pk = None
i2.save()
with scopes_disabled():
var = item.variations.create(value="Children")
var2 = item.variations.create(value="Children")
res = copy.copy(TEST_ORDERPOSITION_RES)
op = order.positions.first()
op.variation = var
@@ -1052,10 +1056,13 @@ def test_orderposition_list_limited_read(endpoint_template, response_code, limit
assert resp.json() == {'detail': 'You do not have permission to perform this action.'}
@pytest.mark.parametrize("endpoint_template", [('/api/v1/organizers/{}/events/{}/orderpositions/'),('/api/v1/organizers/{}/orderpositions/')])
@pytest.mark.parametrize(
"endpoint_template",
[('/api/v1/organizers/{}/events/{}/orderpositions/'), ('/api/v1/organizers/{}/orderpositions/')]
)
@pytest.mark.django_db
def test_orderposition_list(endpoint_template, token_client, organizer, device, event, order, item, subevent, subevent2, question, django_assert_num_queries):
endpoint = endpoint_template.format(organizer.slug, event.slug)
endpoint = endpoint_template.format(organizer.slug, event.slug)
i2 = copy.copy(item)
i2.pk = None
@@ -1078,57 +1085,57 @@ def test_orderposition_list(endpoint_template, token_client, organizer, device,
assert resp.status_code == 200
assert [res] == resp.data['results']
resp = token_client.get(endpoint+'?order__status=n')
resp = token_client.get(endpoint + '?order__status=n')
assert [res] == resp.data['results']
resp = token_client.get(endpoint+'?order__status=p')
resp = token_client.get(endpoint + '?order__status=p')
assert [] == resp.data['results']
resp = token_client.get(endpoint+'?item={}'.format(item.pk))
resp = token_client.get(endpoint + '?item={}'.format(item.pk))
assert [res] == resp.data['results']
resp = token_client.get(endpoint+'?item__in={},{}'.format(item.pk, i2.pk))
resp = token_client.get(endpoint + '?item__in={},{}'.format(item.pk, i2.pk))
assert [res] == resp.data['results']
resp = token_client.get(endpoint+'?item={}'.format( i2.pk))
resp = token_client.get(endpoint + '?item={}'.format(i2.pk))
assert [] == resp.data['results']
resp = token_client.get(endpoint+'?variation={}'.format(var.pk))
resp = token_client.get(endpoint + '?variation={}'.format(var.pk))
assert [res] == resp.data['results']
resp = token_client.get(endpoint+'?variation={}'.format(var2.pk))
resp = token_client.get(endpoint + '?variation={}'.format(var2.pk))
assert [] == resp.data['results']
resp = token_client.get(endpoint+'?attendee_name=Peter')
resp = token_client.get(endpoint + '?attendee_name=Peter')
assert [res] == resp.data['results']
resp = token_client.get(endpoint+'?attendee_name=peter')
resp = token_client.get(endpoint + '?attendee_name=peter')
assert [res] == resp.data['results']
resp = token_client.get(endpoint+'?attendee_name=Mark')
resp = token_client.get(endpoint + '?attendee_name=Mark')
assert [] == resp.data['results']
resp = token_client.get(endpoint+'?secret=z3fsn8jyufm5kpk768q69gkbyr5f4h6w')
resp = token_client.get(endpoint + '?secret=z3fsn8jyufm5kpk768q69gkbyr5f4h6w')
assert [res] == resp.data['results']
resp = token_client.get(endpoint+'?secret=abc123')
resp = token_client.get(endpoint + '?secret=abc123')
assert [] == resp.data['results']
resp = token_client.get(endpoint+'?pseudonymization_id=ABCDEFGHKL')
resp = token_client.get(endpoint + '?pseudonymization_id=ABCDEFGHKL')
assert [res] == resp.data['results']
resp = token_client.get(endpoint+'?pseudonymization_id=FOO')
resp = token_client.get(endpoint + '?pseudonymization_id=FOO')
assert [] == resp.data['results']
resp = token_client.get(endpoint+'?search=FO')
resp = token_client.get(endpoint + '?search=FO')
assert [res] == resp.data['results']
resp = token_client.get(endpoint+'?search=z3fsn8j')
resp = token_client.get(endpoint + '?search=z3fsn8j')
assert [res] == resp.data['results']
resp = token_client.get(endpoint+'?search=Peter')
resp = token_client.get(endpoint + '?search=Peter')
assert [res] == resp.data['results']
resp = token_client.get(endpoint+'?search=5f4h6w')
resp = token_client.get(endpoint + '?search=5f4h6w')
assert [] == resp.data['results']
resp = token_client.get(endpoint+'?order=FOO')
resp = token_client.get(endpoint + '?order=FOO')
assert [res] == resp.data['results']
resp = token_client.get(endpoint+'?order=BAR')
resp = token_client.get(endpoint + '?order=BAR')
assert [] == resp.data['results']
resp = token_client.get(endpoint+'?has_checkin=false')
resp = token_client.get(endpoint + '?has_checkin=false')
assert [res] == resp.data['results']
resp = token_client.get(endpoint+'?has_checkin=true')
resp = token_client.get(endpoint + '?has_checkin=true')
assert [] == resp.data['results']
with scopes_disabled():
@@ -1147,32 +1154,32 @@ def test_orderposition_list(endpoint_template, token_client, organizer, device,
}]
if '/events/' in endpoint:
with django_assert_num_queries(16):
resp = token_client.get(endpoint+'?has_checkin=true')
resp = token_client.get(endpoint + '?has_checkin=true')
else:
with django_assert_num_queries(15):
resp = token_client.get(endpoint+'?has_checkin=true')
resp = token_client.get(endpoint + '?has_checkin=true')
assert [res] == resp.data['results']
op.subevent = subevent
op.save()
res['subevent'] = subevent.pk
resp = token_client.get(endpoint+'?subevent={}'.format(subevent.pk))
resp = token_client.get(endpoint + '?subevent={}'.format(subevent.pk))
assert [res] == resp.data['results']
resp = token_client.get(endpoint+'?subevent__in={},{}'.format(subevent.pk, subevent2.pk))
resp = token_client.get(endpoint + '?subevent__in={},{}'.format(subevent.pk, subevent2.pk))
assert [res] == resp.data['results']
resp = token_client.get(endpoint+'?subevent={}'.format(subevent.pk + 1))
resp = token_client.get(endpoint + '?subevent={}'.format(subevent.pk + 1))
assert [] == resp.data['results']
resp = token_client.get(endpoint+'?include_canceled_positions=false')
resp = token_client.get(endpoint + '?include_canceled_positions=false')
assert len(resp.data['results']) == 1
resp = token_client.get(endpoint+'?include_canceled_positions=true')
resp = token_client.get(endpoint + '?include_canceled_positions=true')
assert len(resp.data['results']) == 2
@pytest.mark.parametrize("endpoint_template", [('/api/v1/organizers/{}/events/{}/orderpositions/'),('/api/v1/organizers/{}/orderpositions/')])
@pytest.mark.django_db
def test_orderposition_detail(endpoint_template, token_client, organizer, event, order, item, question):
endpoint = endpoint_template.format(organizer.slug, event.slug)
def test_orderposition_detail(token_client, organizer, event, order, item, question):
endpoint = '/api/v1/organizers/{}/events/{}/orderpositions/'.format(organizer.slug, event.slug)
res = dict(TEST_ORDERPOSITION_RES)
with scopes_disabled():
@@ -1188,27 +1195,27 @@ def test_orderposition_detail(endpoint_template, token_client, organizer, event,
order.status = 'p'
order.save()
event.settings.ticketoutput_pdf__enabled = True
resp = token_client.get(endpoint+'{}/'.format(op.pk))
resp = token_client.get(endpoint + '{}/'.format(op.pk))
assert len(resp.data['downloads']) == 1
@pytest.mark.parametrize("endpoint_template", [('/api/v1/organizers/{}/events/{}/orderpositions/'),('/api/v1/organizers/{}/orderpositions/')])
@pytest.mark.django_db
def test_orderposition_detail_canceled(endpoint_template, token_client, organizer, event, order, item, question):
endpoint = endpoint_template.format(organizer.slug, event.slug)
def test_orderposition_detail_canceled(token_client, organizer, event, order, item, question):
endpoint = '/api/v1/organizers/{}/events/{}/orderpositions/'.format(organizer.slug, event.slug)
with scopes_disabled():
op = order.all_positions.filter(canceled=True).first()
resp = token_client.get(endpoint+'{}/'.format(op.pk))
resp = token_client.get(endpoint + '{}/'.format(op.pk))
assert resp.status_code == 404
resp = token_client.get(endpoint+'{}/?include_canceled_positions=true'.format(op.pk))
resp = token_client.get(endpoint + '{}/?include_canceled_positions=true'.format(op.pk))
assert resp.status_code == 200
@pytest.mark.parametrize("endpoint_template", [('/api/v1/organizers/{}/events/{}/orderpositions/'),('/api/v1/organizers/{}/orderpositions/')])
@pytest.mark.django_db
def test_orderposition_delete(endpoint_template, token_client, organizer, event, order, item, question):
endpoint = endpoint_template.format(organizer.slug, event.slug)
def test_orderposition_delete(token_client, organizer, event, order, item, question):
endpoint = '/api/v1/organizers/{}/events/{}/orderpositions/'.format(organizer.slug, event.slug)
with scopes_disabled():
op = order.positions.first()
resp = token_client.delete(endpoint+'{}/'.format(op.pk))
resp = token_client.delete(endpoint + '{}/'.format(op.pk))
assert resp.status_code == 400
assert resp.data == ['This operation would leave the order empty. Please cancel the order itself instead.']
@@ -1227,7 +1234,7 @@ def test_orderposition_delete(endpoint_template, token_client, organizer, event,
order.save()
assert order.positions.count() == 2
resp = token_client.delete(endpoint+'{}/'.format(op2.pk))
resp = token_client.delete(endpoint + '{}/'.format(op2.pk))
assert resp.status_code == 204
with scopes_disabled():
assert order.positions.count() == 1
@@ -1235,13 +1242,13 @@ def test_orderposition_delete(endpoint_template, token_client, organizer, event,
order.refresh_from_db()
assert order.total == Decimal('23.25')
@pytest.mark.parametrize("endpoint_template", [('/api/v1/organizers/{}/events/{}/orderpositions/'),('/api/v1/organizers/{}/orderpositions/')])
@pytest.mark.django_db
def test_orderposition_printlog(endpoint_template, token_client, team, organizer, event, order, item, question):
endpoint = endpoint_template.format(organizer.slug, event.slug)
def test_orderposition_printlog(token_client, team, organizer, event, order, item, question):
endpoint = '/api/v1/organizers/{}/events/{}/orderpositions/'.format(organizer.slug, event.slug)
with scopes_disabled():
op = order.positions.first()
resp = token_client.post(endpoint+'{}/printlog/'.format(op.pk), data={
resp = token_client.post(endpoint + '{}/printlog/'.format(op.pk), data={
"datetime": "2023-09-04T12:23:45+02:00",
"source": "pretixscan",
"type": "badge",
@@ -1258,23 +1265,6 @@ def test_orderposition_printlog(endpoint_template, token_client, team, organizer
assert l.api_token.team == team
assert l.datetime.isoformat() == "2023-09-04T10:23:45+00:00"
@pytest.mark.parametrize("endpoint_template, response_code", [
('/api/v1/organizers/{}/events/{}/orderpositions/', 403),
('/api/v1/organizers/{}/orderpositions/', 404)])
@pytest.mark.django_db
def test_orderposition_printlog(endpoint_template, response_code, limited_token_client, team, organizer, event, order, item, question):
endpoint = endpoint_template.format(organizer.slug, event.slug)
with scopes_disabled():
op = order.positions.first()
resp = limited_token_client.post(endpoint+'{}/printlog/'.format(op.pk), data={
"datetime": "2023-09-04T12:23:45+02:00",
"source": "pretixscan",
"type": "badge",
"info": {
"cashier": 1234,
}
}, format='json')
assert resp.status_code == response_code
@pytest.mark.django_db
def test_order_mark_paid_pending(token_client, organizer, event, order):