views.py 5.6 KB

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