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

1from django.db.models import Q 

2 

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 

19 

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 

27 

28 

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

40 

41 permission_classes = [ 

42 IsAuthenticatedOrTokenHasScope, 

43 DjangoModelPermissionsOrAnonReadOnly, 

44 ] 

45 required_scopes = ["sales:read"] 

46 

47 

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"] 

56 

57 

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" 

71 

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) 

77 

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) 

83 

84 def perform_create(self, serializer): 

85 serializer.save( 

86 payer_id=self.request.member.pk, created_by_id=self.request.member.pk 

87 ) 

88 

89 def get_queryset(self): 

90 queryset = Order.objects.all() 

91 

92 pk = self.kwargs.get("pk") 

93 if pk: 

94 queryset = queryset.filter(shift=pk) 

95 

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 ) 

107 

108 return queryset.filter( 

109 Q(payer=self.request.member) | Q(created_by=self.request.member) 

110 ) 

111 

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 

119 

120 

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 } 

133 

134 def get_queryset(self): 

135 queryset = super().get_queryset() 

136 

137 if not self.request.member: 

138 queryset = queryset.none() 

139 

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 ) 

154 

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) 

159 

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) 

164 

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) 

169 

170 

171class OrderClaimView(GenericAPIView): 

172 """Claims an order to be paid by the current user.""" 

173 

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 

178 

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"] 

184 

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 ) 

190 

191 order = self.get_object() 

192 if order.payment: 

193 raise PermissionDenied(detail="This order was already paid for.") 

194 

195 if order.payer is not None and order.payer != request.member: 

196 raise PermissionDenied(detail="This order is not yours.") 

197 

198 order.payer = request.member 

199 order.save() 

200 

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 ) 

205 

206 serializer = self.get_serializer(order) 

207 return Response(serializer.data)