Coverage for accounting/views.py: 52%
77 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-04-26 16:16 -0600
« 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)
23class MovementsViewSet(OrganizationAdminMixin, GenericViewSet, List):
24 """
25 ViewSet for movements
26 """
28 pagination_class = AccountingPagination
29 serializer_class = MovementsPolymorphicSerializer
30 filterset_fields = {"membership": ["exact"], "date": ["gte", "lte"]}
31 model = Movement
34class BaseProductChargeViewSet(GenericViewSet, List, Detail):
35 """
36 Base ViewSet for ProductCharge
37 """
39 pagination_class = ProductChargePagination
40 serializer_class = ProductChargeSerializer
41 filterset_class = ProductChargeFilter
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"))
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)
52 serializer = self.get_serializer(queryset, many=True)
53 return Response(serializer.data)
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 )
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)
68 serializer = self.get_serializer(queryset, many=True)
69 return Response(serializer.data)
72class ProductChargeViewSet(OrganizationAdminMixin, BaseProductChargeViewSet, Delete):
73 """
74 ViewSet for ProductCharge
75 """
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}"})
89class MemberProductChargeViewSet(MemberMixin, BaseProductChargeViewSet):
90 """
91 ViewSet for Member ProductCharge
92 """
94 pass
97class PublicProductChargeViewSet(GenericViewSet, Detail):
98 """
99 Publice ViewSet for ProductCharge without access token
100 """
102 queryset = ProductCharge.objects.all()
103 serializer_class = DetailProductChargeSerializer
104 permission_classes = [AllowAny]
107class AnnualProductChargeViewSet(OrganizationAdminMixin, GenericViewSet, List):
108 """
109 ViewSet for annual data on ProductCharge
110 """
112 model = ProductCharge
113 filterset_class = AnnualProductChargeFilter
114 serializer_class = AnnualProductChargeSerializer
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)