views.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. """
  2. Transaction
  3. """
  4. import coreschema
  5. import coreapi
  6. from django.http import Http404
  7. from rest_framework import viewsets, mixins
  8. from django.contrib.auth import get_user_model
  9. from rest_framework.schemas import AutoSchema
  10. from rest_framework.response import Response
  11. from django.db.models import (Sum, F, Case, FloatField,
  12. When)
  13. from .mixins import TransactionsMixin
  14. from .serializers import (CreateTransactionSerializer,
  15. BalanceByAccountSerializer)
  16. from .models import Transaction
  17. User = get_user_model()
  18. class CustomSchema(AutoSchema):
  19. """
  20. Custom Scheme for documentation
  21. """
  22. def __init__(self, manual_fields=None):
  23. """
  24. Parameters:
  25. Add Manual Fields
  26. """
  27. super().__init__(manual_fields=manual_fields)
  28. self._manual_fields = [
  29. coreapi.Field(
  30. name='date_from',
  31. location='query',
  32. required=False,
  33. schema=coreschema.Integer(title='date_from',
  34. description='Date Format: `YYYY-MM-DD`')
  35. ),
  36. coreapi.Field(
  37. name='date_to',
  38. location='query',
  39. required=False,
  40. schema=coreschema.Integer(title='date_from',
  41. description='Date Format: `YYYY-MM-DD`')
  42. )
  43. ]
  44. def get_path_fields(self, path, method):
  45. field = coreapi.Field(
  46. name='user_id',
  47. location='path',
  48. required=True,
  49. schema=coreschema.Integer(title='user_id',
  50. description='A unique integer value identifying this user.')
  51. )
  52. return [field]
  53. class TransactionViewSet(mixins.CreateModelMixin,
  54. viewsets.GenericViewSet):
  55. """
  56. API endpoint to create transactions
  57. Can send one or more transactions by request.
  58. Query example:
  59. ```json
  60. [
  61. {
  62. "reference": "000053",
  63. "account": "C00099",
  64. "date": "2020-01-03",
  65. "amount": -51.13,
  66. "type": "outflow",
  67. "category": "groceries",
  68. "user_id": 1
  69. },
  70. {
  71. "reference": "000054",
  72. "account": "C00099",
  73. "date": "2020-01-10",
  74. "amount": 2500.60,
  75. "type": "inflow",
  76. "category": "groceries",
  77. "user_id": 1
  78. }
  79. ]
  80. ```
  81. """
  82. queryset = Transaction.objects.all()
  83. serializer_class = CreateTransactionSerializer
  84. permission_classes = []
  85. def create(self, request, *args, **kwargs):
  86. serializer = self.get_serializer(data=request.data, many=isinstance(request.data, list))
  87. serializer.is_valid(raise_exception=True)
  88. self.perform_create(serializer)
  89. return Response(serializer.data)
  90. class AccountBalanceViewSet(viewsets.ViewSet, TransactionsMixin):
  91. """
  92. ### Retrieve summary by accounts of user.
  93. Optionally restricts the returned account balance to a given user,
  94. by filtering against a `date_from` and `date_to` query parameter in the URL.
  95. Date Format: `YYYY-MM-DD`
  96. Query example:
  97. /api/transactions/balance/account/1/?date_from=2020-01-10
  98. """
  99. permission_classes = []
  100. lookup_field = 'user_id'
  101. schema = CustomSchema()
  102. def retrieve(self, request, user_id=None, *args, **kwargs):
  103. try:
  104. self.user = User.objects.get(pk=user_id)
  105. except User.DoesNotExist:
  106. raise Http404
  107. transactions = self.get_filtered_transactions()
  108. with_annotations = transactions.values('account').\
  109. annotate(balance=Sum('amount'),
  110. total_inflow=Sum(Case(
  111. When(type='inflow', then=F('amount')),
  112. output_field=FloatField(),
  113. )),
  114. total_outflow=Sum(Case(
  115. When(type='outflow', then=F('amount')),
  116. output_field=FloatField(),
  117. )),
  118. )
  119. serializer = BalanceByAccountSerializer(with_annotations, many=True)
  120. return Response(serializer.data)
  121. class CashflowViewSet(viewsets.ViewSet, TransactionsMixin):
  122. """
  123. ### Retrieve summary by category
  124. Optionally restricts the returned cashflow to a given user,
  125. by filtering against a `date_from` and `date_to` query parameter in the URL.
  126. Date Format: `YYYY-MM-DD`
  127. Query example:
  128. /api/transactions/cashflow/1/?date_from=2020-01-10
  129. """
  130. schema = CustomSchema()
  131. permission_classes = []
  132. lookup_field = 'user_id'
  133. def retrieve(self, request, user_id=None, *args, **kwargs):
  134. try:
  135. self.user = User.objects.get(pk=user_id)
  136. except User.DoesNotExist:
  137. raise Http404
  138. transactions = self.get_filtered_transactions()
  139. inflows_qs = list(
  140. transactions.filter(type='inflow').values('category').\
  141. annotate(amount=Sum('amount')).values('category', 'amount'))
  142. outflow_qs = list(
  143. transactions.filter(type='outflow').values('category').\
  144. annotate(amount=Sum('amount')).values('category', 'amount'))
  145. inflow = {inf['category']: inf['amount'] for inf in inflows_qs}
  146. outflow = {outf['category']: outf['amount'] for outf in outflow_qs}
  147. return Response({'inflow': inflow, 'outflow': outflow})