Coverage for accounting/views.py: 52%

77 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-04-26 16:16 -0600

1from django.db.models import ProtectedError 

2from collections import defaultdict 

3from django.db.models import F, Sum 

4from rest_framework.decorators import action 

5from rest_framework.mixins import ListModelMixin as List, RetrieveModelMixin as Detail, DestroyModelMixin as Delete 

6from rest_framework.viewsets import GenericViewSet 

7from rest_framework.permissions import AllowAny 

8from rest_framework.response import Response 

9from rest_framework.exceptions import ValidationError 

10from memberships.mixins import MemberMixin 

11from organizations.mixins import OrganizationAdminMixin 

12from .filters import ProductChargeFilter, AnnualProductChargeFilter 

13from .models import Movement, ProductCharge 

14from .pagination import AccountingPagination, ProductChargePagination 

15from .serializers import ( 

16 MovementsPolymorphicSerializer, 

17 ProductChargeSerializer, 

18 DetailProductChargeSerializer, 

19 AnnualProductChargeSerializer, 

20) 

21 

22 

23class MovementsViewSet(OrganizationAdminMixin, GenericViewSet, List): 

24 """ 

25 ViewSet for movements 

26 """ 

27 

28 pagination_class = AccountingPagination 

29 serializer_class = MovementsPolymorphicSerializer 

30 filterset_fields = {"membership": ["exact"], "date": ["gte", "lte"]} 

31 model = Movement 

32 

33 

34class BaseProductChargeViewSet(GenericViewSet, List, Detail): 

35 """ 

36 Base ViewSet for ProductCharge 

37 """ 

38 

39 pagination_class = ProductChargePagination 

40 serializer_class = ProductChargeSerializer 

41 filterset_class = ProductChargeFilter 

42 

43 @action(methods=["GET"], detail=False) 

44 def payables(self, request): 

45 queryset = self.filter_queryset(self.get_queryset()).filter(amount__gt=F("paid_amount")) 

46 

47 page = self.paginate_queryset(queryset) 

48 if page is not None: 

49 serializer = self.get_serializer(page, many=True) 

50 return self.get_paginated_response(serializer.data) 

51 

52 serializer = self.get_serializer(queryset, many=True) 

53 return Response(serializer.data) 

54 

55 @action(methods=["GET"], detail=False) 

56 def invoicables(self, request): 

57 queryset = ( 

58 self.filter_queryset(self.get_queryset()) 

59 .filter(paid_amount__gt=0) 

60 .filter(paid_amount__gt=F("invoiced_amount")) 

61 ) 

62 

63 page = self.paginate_queryset(queryset) 

64 if page is not None: 

65 serializer = self.get_serializer(page, many=True) 

66 return self.get_paginated_response(serializer.data) 

67 

68 serializer = self.get_serializer(queryset, many=True) 

69 return Response(serializer.data) 

70 

71 

72class ProductChargeViewSet(OrganizationAdminMixin, BaseProductChargeViewSet, Delete): 

73 """ 

74 ViewSet for ProductCharge 

75 """ 

76 

77 def destroy(self, request, *args, **kwargs): 

78 product_charge = self.get_object() 

79 if product_charge.payment_allocations.count() > 0: 

80 raise ValidationError({"detail": "No se puede eliminar un cargo que tiene pagos aplicados"}) 

81 if product_charge.invoices.count() > 0: 

82 raise ValidationError({"detail": "No se puede eliminar un cargo facturado"}) 

83 try: 

84 return super().destroy(request, *args, **kwargs) 

85 except ProtectedError: 

86 raise ValidationError({"detail": f"No se puede eliminar el cargo {product_charge.concept}"}) 

87 

88 

89class MemberProductChargeViewSet(MemberMixin, BaseProductChargeViewSet): 

90 """ 

91 ViewSet for Member ProductCharge 

92 """ 

93 

94 pass 

95 

96 

97class PublicProductChargeViewSet(GenericViewSet, Detail): 

98 """ 

99 Publice ViewSet for ProductCharge without access token 

100 """ 

101 

102 queryset = ProductCharge.objects.all() 

103 serializer_class = DetailProductChargeSerializer 

104 permission_classes = [AllowAny] 

105 

106 

107class AnnualProductChargeViewSet(OrganizationAdminMixin, GenericViewSet, List): 

108 """ 

109 ViewSet for annual data on ProductCharge 

110 """ 

111 

112 model = ProductCharge 

113 filterset_class = AnnualProductChargeFilter 

114 serializer_class = AnnualProductChargeSerializer 

115 

116 def list(self, request, *args, **kwargs): 

117 annual_data = [] 

118 queryset = self.filter_queryset(self.get_queryset()) 

119 for month in range(1, 13): 

120 month_data = defaultdict(int) 

121 month_queryset = queryset.filter(date__month=month) 

122 month_data["month"] = month 

123 if queryset.exists(): 

124 month_data["amount"] = month_queryset.aggregate(total_amount=Sum("amount")).get("total_amount") or 0 

125 month_data["paid_amount"] = ( 

126 month_queryset.aggregate(total_paid_amount=Sum("paid_amount")).get("total_paid_amount") or 0 

127 ) 

128 month_data["invoiced_amount"] = ( 

129 month_queryset.aggregate(total_invoiced_amount=Sum("invoiced_amount")).get("total_invoiced_amount") 

130 or 0 

131 ) 

132 annual_data.append(month_data) 

133 serializer = self.get_serializer(annual_data, many=True) 

134 return Response(serializer.data)