Coverage for website/payments/api/v2/admin/views.py: 45.65%

84 statements  

« prev     ^ index     » next       coverage.py v7.6.7, created at 2025-08-14 10:31 +0000

1from django.apps import apps 

2from django.http import Http404 

3from django.utils.translation import gettext_lazy as _ 

4 

5import rest_framework.filters as framework_filters 

6from oauth2_provider.contrib.rest_framework import IsAuthenticatedOrTokenHasScope 

7from rest_framework import serializers, status 

8from rest_framework.exceptions import PermissionDenied, ValidationError 

9from rest_framework.generics import get_object_or_404 

10from rest_framework.permissions import IsAdminUser 

11from rest_framework.response import Response 

12from rest_framework.settings import api_settings 

13from rest_framework.views import APIView 

14 

15from payments import services 

16from payments.api.v2 import filters 

17from payments.api.v2.admin.serializers.payable_create import ( 

18 PayableCreateAdminSerializer, 

19) 

20from payments.api.v2.admin.serializers.payable_detail import PayableAdminSerializer 

21from payments.api.v2.admin.serializers.payment import ( 

22 PaymentAdminSerializer, 

23 PaymentCreateSerializer, 

24) 

25from payments.exceptions import PaymentError 

26from payments.models import Payment, PaymentUser 

27from payments.payables import NotRegistered, payables 

28from thaliawebsite.api.v2.admin import ( 

29 AdminCreateAPIView, 

30 AdminDestroyAPIView, 

31 AdminListAPIView, 

32 AdminRetrieveAPIView, 

33) 

34 

35 

36class PaymentListCreateView(AdminListAPIView, AdminCreateAPIView): 

37 """View that allows you to create and list payments as admin.""" 

38 

39 queryset = Payment.objects.prefetch_related( 

40 "paid_by__profile", 

41 "paid_by__membership_set", 

42 "processed_by__profile", 

43 "processed_by__membership_set", 

44 ) 

45 

46 required_scopes = ["payments:admin"] 

47 filter_backends = ( 

48 framework_filters.OrderingFilter, 

49 filters.CreatedAtFilter, 

50 filters.PaymentTypeFilter, 

51 ) 

52 ordering_fields = ("created_at",) 

53 

54 def get_serializer_class(self): 

55 if self.request.method.lower() == "post": 

56 return PaymentCreateSerializer 

57 return PaymentAdminSerializer 

58 

59 def create(self, request, *args, **kwargs): 

60 serializer = self.get_serializer(data=request.data) 

61 serializer.is_valid(raise_exception=True) 

62 self.perform_create(serializer) 

63 return Response( 

64 PaymentAdminSerializer( 

65 serializer.instance, context=self.get_serializer_context() 

66 ).data, 

67 status=status.HTTP_201_CREATED, 

68 ) 

69 

70 

71class PaymentDetailView(AdminRetrieveAPIView, AdminDestroyAPIView): 

72 """View that allows you to manage a single payment as admin.""" 

73 

74 queryset = Payment.objects.all() 

75 serializer_class = PaymentAdminSerializer 

76 permission_classes = [IsAuthenticatedOrTokenHasScope] 

77 required_scopes = ["payments:admin"] 

78 

79 def delete(self, request, *args, **kwargs): 

80 if self.get_object().batch and self.get_object().batch.processed: 

81 raise PermissionDenied("This payment cannot be deleted.") 

82 return super().delete(request, *args, **kwargs) 

83 

84 

85class PayableDetailView(APIView): 

86 """View that allows you to manipulate the payment for the payable. 

87 

88 Permissions of this view are based on the payable. 

89 """ 

90 

91 required_scopes = ["payments:admin"] 

92 permission_classes = [IsAuthenticatedOrTokenHasScope, IsAdminUser] 

93 

94 def get_serializer_context(self): 

95 return {"request": self.request, "format": self.format_kwarg, "view": self} 

96 

97 def get_payable(self): 

98 app_label = self.kwargs["app_label"] 

99 model_name = self.kwargs["model_name"] 

100 payable_pk = self.kwargs["payable_pk"] 

101 

102 try: 

103 payable_model = apps.get_model(app_label=app_label, model_name=model_name) 

104 payable = payables.get_payable( 

105 get_object_or_404(payable_model, pk=payable_pk) 

106 ) 

107 except (LookupError, NotRegistered) as e: 

108 raise serializers.ValidationError( 

109 {api_settings.NON_FIELD_ERRORS_KEY: [_("Payable model not found")]} 

110 ) from e 

111 

112 if not payable.can_manage_payment(self.request.member): 

113 raise PermissionDenied( 

114 detail=_("You do not have permission to perform this action.") 

115 ) 

116 

117 return payable 

118 

119 def get(self, request, *args, **kwargs): 

120 """Get information about a payable.""" 

121 serializer = PayableAdminSerializer( 

122 self.get_payable(), context=self.get_serializer_context() 

123 ) 

124 return Response(serializer.data, status=status.HTTP_200_OK) 

125 

126 def delete(self, request, *args, **kwargs): 

127 """Remove the current payment for a payable.""" 

128 payable = self.get_payable() 

129 

130 if not payable.model.payment: 

131 raise Http404 

132 

133 try: 

134 services.delete_payment( 

135 payable.model, 

136 request.member, 

137 ) 

138 payable.model.save() 

139 except PaymentError as e: 

140 raise PermissionDenied(detail=str(e)) from e 

141 

142 return Response(status=status.HTTP_204_NO_CONTENT) 

143 

144 def patch(self, request, *args, **kwargs): 

145 """Mark the payable as paid by creating a payment for it.""" 

146 serializer = PayableCreateAdminSerializer( 

147 data=request.data, context=self.get_serializer_context() 

148 ) 

149 serializer.is_valid(raise_exception=True) 

150 

151 payable = self.get_payable() 

152 

153 try: 

154 services.create_payment( 

155 payable, 

156 PaymentUser.objects.get(pk=request.user.pk), 

157 serializer.data["payment_type"], 

158 ) 

159 except PaymentError as e: 

160 raise ValidationError( 

161 detail={api_settings.NON_FIELD_ERRORS_KEY: [str(e)]} 

162 ) from e 

163 

164 payable.model.refresh_from_db() 

165 return Response( 

166 PayableAdminSerializer(payable, context=self.get_serializer_context()).data, 

167 status=status.HTTP_201_CREATED, 

168 )