Coverage for website/sales/api/v2/views.py: 70.99%
105 statements
« prev ^ index » next coverage.py v7.6.12, created at 2026-06-21 23:59 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2026-06-21 23:59 +0000
1from django.db.models import Q
3from oauth2_provider.contrib.rest_framework import IsAuthenticatedOrTokenHasScope
4from rest_framework import exceptions
5from rest_framework import filters as framework_filters
6from rest_framework.exceptions import PermissionDenied
7from rest_framework.generics import (
8 CreateAPIView,
9 DestroyAPIView,
10 GenericAPIView,
11 ListAPIView,
12 RetrieveAPIView,
13 UpdateAPIView,
14 get_object_or_404,
15)
16from rest_framework.permissions import DjangoModelPermissionsOrAnonReadOnly
17from rest_framework.response import Response
18from rest_framework.schemas.openapi import AutoSchema
20from sales import services
21from sales.api.v2 import filters
22from sales.api.v2.admin.serializers.order import OrderListSerializer, OrderSerializer
23from sales.api.v2.serializers.user_shift import UserShiftSerializer
24from sales.models.order import Order
25from sales.models.shift import Shift
26from thaliawebsite.api.v2.permissions import IsAuthenticatedOrTokenHasScopeForMethod
29class UserShiftListView(ListAPIView):
30 serializer_class = UserShiftSerializer
31 queryset = Shift.objects.filter(selforder=True)
32 filter_backends = (
33 framework_filters.OrderingFilter,
34 framework_filters.SearchFilter,
35 filters.ShiftActiveFilter,
36 filters.ShiftLockedFilter,
37 filters.ShiftDateFilter,
38 )
39 ordering_fields = ("start", "end")
41 permission_classes = [
42 IsAuthenticatedOrTokenHasScope,
43 DjangoModelPermissionsOrAnonReadOnly,
44 ]
45 required_scopes = ["sales:read"]
48class UserShiftDetailView(RetrieveAPIView):
49 serializer_class = UserShiftSerializer
50 queryset = Shift.objects.filter(selforder=True)
51 permission_classes = [
52 IsAuthenticatedOrTokenHasScope,
53 DjangoModelPermissionsOrAnonReadOnly,
54 ]
55 required_scopes = ["sales:read"]
58class UserOrderListView(ListAPIView, CreateAPIView):
59 permission_classes = [
60 IsAuthenticatedOrTokenHasScopeForMethod,
61 ]
62 required_scopes_per_method = {
63 "GET": ["sales:read"],
64 "POST": ["sales:order"],
65 }
66 method_serializer_classes = {
67 ("GET",): OrderListSerializer,
68 ("POST",): OrderSerializer,
69 }
70 shift_lookup_field = "pk"
72 def get_serializer_class(self):
73 for methods, serializer_cls in self.method_serializer_classes.items(): 73 ↛ 76line 73 didn't jump to line 76 because the loop on line 73 didn't complete
74 if self.request.method in methods:
75 return serializer_cls
76 raise exceptions.MethodNotAllowed(self.request.method)
78 def create(self, request, *args, **kwargs):
79 shift = Shift.objects.get(pk=kwargs["pk"])
80 if not shift.user_orders_allowed: 80 ↛ 81line 80 didn't jump to line 81 because the condition on line 80 was never true
81 raise PermissionDenied
82 return super().create(request, *args, **kwargs)
84 def perform_create(self, serializer):
85 serializer.save(
86 payer_id=self.request.member.pk, created_by_id=self.request.member.pk
87 )
89 def get_queryset(self):
90 queryset = Order.objects.all()
92 pk = self.kwargs.get("pk")
93 if pk:
94 queryset = queryset.filter(shift=pk)
96 queryset = queryset.select_properties(
97 "total_amount", "subtotal", "num_items", "age_restricted"
98 ).prefetch_related(
99 "shift",
100 "shift__event",
101 "shift__product_list",
102 "order_items",
103 "order_items__product",
104 "order_items__product__product",
105 "payment",
106 )
108 return queryset.filter(
109 Q(payer=self.request.member) | Q(created_by=self.request.member)
110 )
112 def get_serializer_context(self):
113 context = super().get_serializer_context()
114 pk = self.kwargs.get("pk")
115 if pk: 115 ↛ 118line 115 didn't jump to line 118 because the condition on line 115 was always true
116 shift = get_object_or_404(Shift, pk=self.kwargs.get("pk"))
117 context.update({"shift": shift})
118 return context
121class UserOrderDetailView(RetrieveAPIView, UpdateAPIView, DestroyAPIView):
122 serializer_class = OrderSerializer
123 queryset = Order.objects.all()
124 permission_classes = [
125 IsAuthenticatedOrTokenHasScopeForMethod,
126 ]
127 required_scopes_per_method = {
128 "GET": ["sales:read"],
129 "PATCH": ["sales:order"],
130 "PUT": ["sales:order"],
131 "DELETE": ["sales:order"],
132 }
134 def get_queryset(self):
135 queryset = super().get_queryset()
137 if not self.request.member:
138 queryset = queryset.none()
140 queryset = queryset.select_properties(
141 "total_amount", "subtotal", "num_items", "age_restricted"
142 ).prefetch_related(
143 "shift",
144 "shift__event",
145 "shift__product_list",
146 "order_items",
147 "order_items__product",
148 "order_items__product__product",
149 "payment",
150 )
151 return queryset.filter(
152 Q(payer=self.request.member) | Q(created_by=self.request.member)
153 )
155 def update(self, request, *args, **kwargs):
156 if not self.get_object().user_can_modify(request.member):
157 raise PermissionDenied
158 return super().update(request, *args, **kwargs)
160 def partial_update(self, request, *args, **kwargs):
161 if not self.get_object().user_can_modify(request.member):
162 raise PermissionDenied
163 return super().partial_update(request, *args, **kwargs)
165 def destroy(self, request, *args, **kwargs):
166 if not self.get_object().user_can_modify(request.member):
167 raise PermissionDenied
168 return super().destroy(request, *args, **kwargs)
171class OrderClaimView(GenericAPIView):
172 """Claims an order to be paid by the current user."""
174 class OrderClaimViewSchema(AutoSchema):
175 def get_request_serializer(self, path, method):
176 # This endpoint does not expect any content in the request body.
177 return None
179 queryset = Order.objects.all()
180 serializer_class = OrderSerializer
181 schema = OrderClaimViewSchema(operation_id_base="claimOrder")
182 permission_classes = [IsAuthenticatedOrTokenHasScope]
183 required_scopes = ["sales:order"]
185 def patch(self, request, *args, **kwargs):
186 if request.member is None: 186 ↛ 187line 186 didn't jump to line 187 because the condition on line 186 was never true
187 raise PermissionDenied(
188 detail="You need to be a member to pay for an order."
189 )
191 order = self.get_object()
192 if order.payment:
193 raise PermissionDenied(detail="This order was already paid for.")
195 if order.payer is not None and order.payer != request.member:
196 raise PermissionDenied(detail="This order is not yours.")
198 order.payer = request.member
199 order.save()
201 if order.age_restricted and not services.is_adult(request.member):
202 raise PermissionDenied(
203 "The age restrictions on this order do not allow you to pay for this order."
204 )
206 serializer = self.get_serializer(order)
207 return Response(serializer.data)