diff --git a/apps/wallet/serializers.py b/apps/wallet/serializers.py index 5051cc8..4eb2494 100755 --- a/apps/wallet/serializers.py +++ b/apps/wallet/serializers.py @@ -223,3 +223,7 @@ class ApplicationWithdrawSerializer(serializers.ModelSerializer): ) read_only_fields = ['updated_at', 'state', 'created_at', 'payee', 'payee_type', 'payee_wallet'] + + +class TransactionGroupedSerializer(serializers.Serializer): + pass \ No newline at end of file diff --git a/apps/wallet/views/user.py b/apps/wallet/views/user.py index 1626fcf..2067f8a 100755 --- a/apps/wallet/views/user.py +++ b/apps/wallet/views/user.py @@ -1,10 +1,12 @@ from django.contrib import messages from django.contrib.auth.decorators import login_required +from django.contrib.postgres.aggregates import ArrayAgg from django.db.models import Q from django.urls import reverse from django.utils.decorators import method_decorator from django.views.generic import DetailView, ListView, UpdateView from django_filters.rest_framework import DjangoFilterBackend +from drf_spectacular.utils import extend_schema from oauth2_provider.contrib.rest_framework import TokenHasScope, IsAuthenticatedOrTokenHasScope from rest_framework import generics, permissions, mixins @@ -14,10 +16,14 @@ from rest_framework.permissions import AllowAny from rest_framework.response import Response from rest_framework.viewsets import ViewSet, GenericViewSet +from apps.wallet.models import Transaction +from django.contrib.postgres.aggregates import ArrayAgg +from django.db.models import Sum, Count, F, StringAgg + from apps.gooyal_oauth2.rest_framework import IsAuthenticatedOrTokenMatchesOASRequirements from apps.wallet.models import Transaction, Wallet, Account from apps.wallet.serializers import TransactionSerializer, DepositSerializer, WithdrawSerializer, \ - AccountSerializer + AccountSerializer, TransactionGroupedSerializer from ..constans import StateChoices, TypeChoices from ..filters import AccountFilter, UserTransactionFilter from ..forms import TransactionActionForm @@ -308,12 +314,11 @@ class WalletTransactionListView(generics.ListAPIView): class UserTransactionViewSet(mixins.ListModelMixin, - # mixins.CreateModelMixin, - # mixins.RetrieveModelMixin, - # mixins.UpdateModelMixin, - # mixins.DestroyModelMixin, - GenericViewSet): - + # mixins.CreateModelMixin, + # mixins.RetrieveModelMixin, + # mixins.UpdateModelMixin, + # mixins.DestroyModelMixin, + GenericViewSet): permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements] serializer_class = TransactionSerializer required_alternate_scopes = { @@ -331,7 +336,6 @@ class UserTransactionViewSet(mixins.ListModelMixin, return Transaction.objects.filter(Q(payee_account__owner_uuid=user.uuid) | Q(payer_account__owner_uuid=user.uuid)).all() - def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) @@ -343,32 +347,32 @@ class UserTransactionViewSet(mixins.ListModelMixin, serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) - - @action(detail=False, methods=["GET"]) + @extend_schema(responses=TransactionGroupedSerializer(many=True)) + @action(detail=False, methods=["GET"], serializer_class=TransactionGroupedSerializer) def grouped(self): - from collections import defaultdict - from django.db.models import Count, Sum - # فقط ۲ کوئری بهینه - # کوئری ۱: آمار کلی (سبک) - stats = dict(Transaction.objects.values('refrence').annotate( - total=Sum('amount'), - count=Count('id') - ).values_list('refrence', 'total', 'count')) + queryset = self.filter_queryset(self.get_queryset()) - # کوئری ۲: همه تراکنش‌ها - transactions = Transaction.objects.all().order_by('refrence', 'created_at') + grouped_transaction_list = queryset.values('details__reference_id', 'application').annotate( + total_amount=Sum('amount'), + total_count=Count('uuid'), + transaction_uuid_list=ArrayAgg('uuid'), + ) - # گروه‌بندی در پایتون - groups = defaultdict(list) - for t in transactions: - groups[t.refrence].append(t) + page = list(self.paginate_queryset(grouped_transaction_list)) - # ترکیب - result = [] - for ref, items in groups.items(): - result.append({ - 'refrence': ref, - 'stats': {'total': stats.get(ref, 0), 'count': len(items)}, - 'transactions': items - }) + transaction_uuid_list = [] + + for grouped_transaction in page: + transaction_uuid_list += grouped_transaction['transaction_uuid_list'] + + transaction_list = Transaction.objects.filter(uuid__in=transaction_uuid_list) + + transaction_dict = {t.pk: t for t in transaction_list} + + for grouped_transaction in page: + for i, transaction in enumerate(grouped_transaction['transaction_uuid_list']): + grouped_transaction['transaction_uuid_list'][i] = transaction_dict[transaction] + + serializer = self.get_serializer(page, many=True) + return self.get_paginated_response(serializer.data)