From f8a269a43a76a133fdc9d0b028d70b98cd6d69fe Mon Sep 17 00:00:00 2001 From: behruz-dev Date: Sat, 13 Sep 2025 17:16:39 +0500 Subject: [PATCH] add: add new statistcs api --- .../0021_incomecontract_paid_price.py | 18 ++++++++ .../0022_expencecontract_paid_price.py | 18 ++++++++ core/apps/finance/models/expence_contract.py | 1 + core/apps/finance/models/income_contract.py | 1 + core/apps/finance/urls.py | 2 + core/apps/finance/views/expence_contract.py | 46 ++++++++++++++++++- core/apps/finance/views/income_contract.py | 45 +++++++++++++++++- 7 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 core/apps/finance/migrations/0021_incomecontract_paid_price.py create mode 100644 core/apps/finance/migrations/0022_expencecontract_paid_price.py diff --git a/core/apps/finance/migrations/0021_incomecontract_paid_price.py b/core/apps/finance/migrations/0021_incomecontract_paid_price.py new file mode 100644 index 0000000..dc45077 --- /dev/null +++ b/core/apps/finance/migrations/0021_incomecontract_paid_price.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.4 on 2025-09-13 16:23 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('finance', '0020_expencecontract_incomecontract'), + ] + + operations = [ + migrations.AddField( + model_name='incomecontract', + name='paid_price', + field=models.PositiveBigIntegerField(blank=True, default=0, null=True), + ), + ] diff --git a/core/apps/finance/migrations/0022_expencecontract_paid_price.py b/core/apps/finance/migrations/0022_expencecontract_paid_price.py new file mode 100644 index 0000000..67ec226 --- /dev/null +++ b/core/apps/finance/migrations/0022_expencecontract_paid_price.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.4 on 2025-09-13 17:13 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('finance', '0021_incomecontract_paid_price'), + ] + + operations = [ + migrations.AddField( + model_name='expencecontract', + name='paid_price', + field=models.PositiveBigIntegerField(blank=True, default=0, null=True), + ), + ] diff --git a/core/apps/finance/models/expence_contract.py b/core/apps/finance/models/expence_contract.py index d0002d4..ae1c793 100644 --- a/core/apps/finance/models/expence_contract.py +++ b/core/apps/finance/models/expence_contract.py @@ -23,6 +23,7 @@ class ExpenceContract(BaseModel): price = models.PositiveBigIntegerField() currency = models.CharField(max_length=3, choices=[('uzs', 'uzs'), ('usd', 'usd')]) + paid_price = models.PositiveBigIntegerField(default=0, null=True, blank=True) date = models.DateField() comment = models.TextField(null=True, blank=True) diff --git a/core/apps/finance/models/income_contract.py b/core/apps/finance/models/income_contract.py index 39b81e8..d7c4092 100644 --- a/core/apps/finance/models/income_contract.py +++ b/core/apps/finance/models/income_contract.py @@ -23,6 +23,7 @@ class IncomeContract(BaseModel): price = models.PositiveBigIntegerField() currency = models.CharField(max_length=3, choices=[('uzs', 'uzs'), ('usd', 'usd')]) + paid_price = models.PositiveBigIntegerField(default=0, null=True, blank=True) date = models.DateField() comment = models.TextField(null=True, blank=True) diff --git a/core/apps/finance/urls.py b/core/apps/finance/urls.py index b377f47..b4bed88 100644 --- a/core/apps/finance/urls.py +++ b/core/apps/finance/urls.py @@ -71,12 +71,14 @@ urlpatterns = [ [ path('list/', ic_views.IncomeContractListApiView.as_view()), path('create/', ic_views.IncomeContractCreateApiView.as_view()), + path('statistics/', ic_views.IncomeContractStatisticsApiView.as_view()), ] )), path('expence_contract/', include( [ path('list/', ec_views.ExpenceContractListApiView.as_view()), path('create/', ec_views.ExpenceContractCreateApiView.as_view()), + path('statistics/', ec_views.ExpenceContractStatisticsApiView.as_view()), ] )) ] \ No newline at end of file diff --git a/core/apps/finance/views/expence_contract.py b/core/apps/finance/views/expence_contract.py index 51c464f..f3d8371 100644 --- a/core/apps/finance/views/expence_contract.py +++ b/core/apps/finance/views/expence_contract.py @@ -1,3 +1,6 @@ +from django.utils.timezone import now +from django.db.models import Sum, Q, F + from rest_framework import generics, views from rest_framework.response import Response @@ -43,4 +46,45 @@ class ExpenceContractListApiView(generics.GenericAPIView): page = self.paginate_queryset(self.queryset) if page is not None: serializer = self.serializer_class(page, many=True) - return self.get_paginated_response(serializer.data) \ No newline at end of file + return self.get_paginated_response(serializer.data) + + +class ExpenceContractStatisticsApiView(views.APIView): + permission_classes = [HasRolePermission] + + def get(self, request): + counterparty_id = request.query_params.get('counterparty') + if counterparty_id: + queryset = ExpenceContract.objects.filter(counterparty=counterparty_id) + else: + queryset = ExpenceContract.objects.all() + today = now().date() + usd = queryset.aggregate( + plan_payments=Sum('price', filter=Q(currency='usd')), + pending_payments=Sum( + 'price', + filter=Q(date__gte=today) & Q(paid_price__isnull=True) & Q(currency='usd') + ), + paid_payments=Sum('paid_price', filter=Q(currency='usd')), + overdue_payments=Sum( + 'price', + filter=Q(date__lt=today) & Q(paid_price__lt=F('price')) & Q(currency='usd') + ) + ) + uzs = queryset.aggregate( + plan_payments=Sum('price', filter=Q(currency='uzs')), + pending_payments=Sum( + 'price', + filter=Q(date__gte=today) & Q(paid_price__isnull=True) & Q(currency='uzs') + ), + paid_payments=Sum('paid_price', filter=Q(currency='uzs')), + overdue_payments=Sum( + 'price', + filter=Q(date__lt=today) & Q(paid_price__lt=F('price')) & Q(currency='uzs') + ) + ) + res = { + 'uzs': uzs, + 'usd': usd + } + return Response(res, status=200) \ No newline at end of file diff --git a/core/apps/finance/views/income_contract.py b/core/apps/finance/views/income_contract.py index 441898d..818abc8 100644 --- a/core/apps/finance/views/income_contract.py +++ b/core/apps/finance/views/income_contract.py @@ -1,6 +1,8 @@ from django.shortcuts import get_object_or_404 +from django.db.models import Sum, Q, F +from django.utils.timezone import now -from rest_framework import generics +from rest_framework import generics, views from rest_framework.response import Response from core.apps.accounts.permissions.permissions import HasRolePermission @@ -46,3 +48,44 @@ class IncomeContractListApiView(generics.GenericAPIView): if page is not None: serializer = self.serializer_class(page, many=True) return self.get_paginated_response(serializer.data) + + +class IncomeContractStatisticsApiView(views.APIView): + permission_classes = [HasRolePermission] + + def get(self, request): + counterparty_id = request.query_params.get('counterparty') + if counterparty_id: + queryset = IncomeContract.objects.filter(counterparty=counterparty_id) + else: + queryset = IncomeContract.objects.all() + today = now().date() + usd = queryset.aggregate( + plan_payments=Sum('price', filter=Q(currency='usd')), + pending_payments=Sum( + 'price', + filter=Q(date__gte=today) & Q(paid_price__isnull=True) & Q(currency='usd') + ), + paid_payments=Sum('paid_price', filter=Q(currency='usd')), + overdue_payments=Sum( + 'price', + filter=Q(date__lt=today) & Q(paid_price__lt=F('price')) & Q(currency='usd') + ) + ) + uzs = queryset.aggregate( + plan_payments=Sum('price', filter=Q(currency='uzs')), + pending_payments=Sum( + 'price', + filter=Q(date__gte=today) & Q(paid_price__isnull=True) & Q(currency='uzs') + ), + paid_payments=Sum('paid_price', filter=Q(currency='uzs')), + overdue_payments=Sum( + 'price', + filter=Q(date__lt=today) & Q(paid_price__lt=F('price')) & Q(currency='uzs') + ) + ) + res = { + 'uzs': uzs, + 'usd': usd + } + return Response(res, status=200) \ No newline at end of file