wallet/apps/wallet/views/user.py
Sayyid Hamid Mahdavi c24b52e341 add reference field
2026-02-07 09:28:06 +03:30

374 lines
13 KiB
Python
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from django.contrib import messages
from django.contrib.auth.decorators import login_required
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 oauth2_provider.contrib.rest_framework import TokenHasScope, IsAuthenticatedOrTokenHasScope
from rest_framework import generics, permissions, mixins
from rest_framework.decorators import action
from rest_framework.generics import get_object_or_404
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.viewsets import ViewSet, GenericViewSet
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
from ..constans import StateChoices, TypeChoices
from ..filters import AccountFilter, UserTransactionFilter
from ..forms import TransactionActionForm
from django.utils.translation import gettext_lazy as _
def get_application(request):
try:
application = request.auth.application
except:
application = None
return application
class UserAccountBalanceView(generics.RetrieveAPIView):
'''
API for getting authenticated user balance.
'''
permission_classes = [IsAuthenticatedOrTokenHasScope]
serializer_class = AccountSerializer
required_scopes = ['wallet.wallet:get_balance']
def get_object(self):
return self.request.user
# this is application create invoice view
class TransactionList(generics.ListAPIView):
'''
GET: API for getting list of transactions for authenticated user
POST: API for creating transaction for transferring funds from authenticated user to application owner. (deprecated)
args:
- user: authenticated user
'''
permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
serializer_class = TransactionSerializer
required_alternate_scopes = {
"GET": [['wallet.user:transaction_list']],
# "POST": [['wallet.invoice:create']],
}
filter_backends = (DjangoFilterBackend,)
filterset_class = UserTransactionFilter
def get_queryset(self):
user = self.request.user
return Transaction.objects.filter(Q(payee_id=user.pk, payee_type=TypeChoices.USER.value) | Q(payer_id=user.pk, payer_type=TypeChoices.USER.value)).all()
# TODO:
# def perform_create(self, serializer):
# payer = self.request.user
# payee = self.request.auth.application_owner
# serializer.save(payer_id=payer.pk, payee=payee, application=get_application(self.request))
class TransactionDetail(generics.RetrieveAPIView):
permission_classes = [permissions.IsAuthenticated, IsAuthenticatedOrTokenHasScope]
serializer_class = TransactionSerializer
lookup_field = 'uuid'
required_scopes = ['wallet.transaction:retrieve']
def get_queryset(self):
user = self.request.user
return Transaction.objects.filter(Q(payee=user) | Q(payer=user) | Q(payer__isnull=True)).all()
class TransactionPay(generics.RetrieveAPIView):
permission_classes = [permissions.IsAuthenticated, IsAuthenticatedOrTokenHasScope]
serializer_class = TransactionSerializer
lookup_field = 'uuid'
required_scopes = ['wallet.invoice:pay']
def get_queryset(self):
user = self.request.user
return Transaction.objects.filter(Q(payer=user) | Q(payer__isnull=True)).all()
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
instance.pay(payer=self.request.user)
serializer = self.get_serializer(instance)
return Response(serializer.data)
class TransactionReceipt(generics.RetrieveAPIView):
permission_classes = [permissions.IsAuthenticated, IsAuthenticatedOrTokenHasScope]
serializer_class = TransactionSerializer
lookup_field = 'uuid'
required_scopes = ['wallet.invoice:receipt']
def get_queryset(self):
user = self.request.user
return Transaction.objects.filter(payer=user).all()
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
instance.receipt()
serializer = self.get_serializer(instance)
return Response(serializer.data)
class DepositSubmit(generics.CreateAPIView):
permission_classes = [TokenHasScope]
serializer_class = DepositSerializer
required_scopes = ['wallet.deposit:submit']
def get_queryset(self):
application = get_application(self.request)
return Transaction.objects.filter(application=application).all()
def perform_create(self, serializer):
payer = self.request.auth.application.user
serializer.save(payer=payer, application=get_application(self.request))
# TODO: refactore
class DepositVerify(generics.RetrieveAPIView):
permission_classes = [TokenHasScope]
serializer_class = DepositSerializer
lookup_field = 'uuid'
required_scopes = ['wallet.deposit:verify']
def get_queryset(self):
application = get_application(self.request)
return Transaction.objects.filter(application=application).all()
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
if instance.state == StateChoices.PENDING:
instance.verify()
serializer = self.get_serializer(instance)
return Response(serializer.data)
class WithdrawSubmit(generics.CreateAPIView):
permission_classes = [TokenHasScope]
serializer_class = WithdrawSerializer
required_scopes = ['wallet.withdraw:submit']
def get_queryset(self):
application = get_application(self.request)
return Transaction.objects.filter(application=application).all()
def perform_create(self, serializer):
payee = self.request.auth.application.user
serializer.save(payee=payee, application=get_application(self.request))
# TODO: refactor
class WithdrawVerify(generics.RetrieveAPIView):
permission_classes = [TokenHasScope]
serializer_class = WithdrawSerializer
lookup_field = 'uuid'
required_scopes = ['wallet.withdraw:verify']
def get_queryset(self):
application = get_application(self.request)
return Transaction.objects.filter(application=application).all()
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
if instance.state == StateChoices.PENDING:
instance.verify()
serializer = self.get_serializer(instance)
return Response(serializer.data)
@method_decorator(login_required, name='dispatch')
class TransactionListView(ListView):
model = Transaction
template_name = 'wallet/transaction_list.html'
def get_queryset(self):
user = self.request.user
qs = super().get_queryset()
return qs.filter(Q(payee_id=user.pk) | Q(payer_id=user.pk)).all()
@method_decorator(login_required, name='dispatch')
class TransactionDetailView(UpdateView):
model = Transaction
form_class = TransactionActionForm
template_name = 'wallet/transaction_detail.html'
def get_context_data(self, **kwargs):
context_data = super().get_context_data(**kwargs)
context_data['StateChoices'] = StateChoices
return context_data
def get_queryset(self):
user = self.request.user
return Transaction.objects.filter(Q(payee=user) | Q(payer=user) | Q(payer__isnull=True)).all()
def get_success_url(self):
return reverse('wallet:transaction_detail', kwargs={'pk': self.get_object().pk})
def form_valid(self, form):
transaction: Transaction = form.instance
if 'pay' in self.request.POST:
result = transaction.pay(payer=self.request.user)
messages.error(self.request, _('success.'))
if 'receipt' in self.request.POST:
result = transaction.receipt()
messages.error(self.request, _('success.'))
return super().form_valid(form)
class WalletAccountListAPIView(generics.ListAPIView):
'''
API for getting authenticated user balance.
'''
permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
serializer_class = AccountSerializer
filter_backends = (DjangoFilterBackend,)
filterset_class = AccountFilter
required_alternate_scopes = {
"GET": [['wallet.user:transaction_list']],
# "POST": [['wallet.invoice:create']],
}
queryset = Account.objects.all()
def get_queryset(self):
user = self.request.user
return Account.objects.filter(owner_uuid=user.uuid).all()
def get_object(self):
user = self.request.user
wallet_uuid = self.kwargs.get('wallet')
wallet = get_object_or_404(Wallet.objects.all(), uuid=wallet_uuid)
wallet, created = Account.objects.get_or_create(owner_uuid=user.uuid, owner_type=TypeChoices.USER, wallet=wallet)
return wallet
# this is application create invoice view
class TransactionListAPIView(generics.ListAPIView):
'''
GET: API for getting list of transactions for authenticated user
POST: API for creating transaction for transferring funds from authenticated user to application owner. (deprecated)
args:
- user: authenticated user
'''
permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
serializer_class = TransactionSerializer
required_alternate_scopes = {
"GET": [['wallet.user:transaction_list']],
# "POST": [['wallet.invoice:create']],
}
filter_backends = (DjangoFilterBackend,)
filterset_class = UserTransactionFilter
queryset = Transaction.objects.all()
# TODO: redundant
def get_queryset(self):
user = self.request.user
return Transaction.objects.filter(Q(payee_account__owner_uuid=user.uuid) |
Q(payer_account__owner_uuid=user.uuid)).all()
class WalletTransactionListView(generics.ListAPIView):
'''
GET: API for getting list of transactions for authenticated user
POST: API for creating transaction for transferring funds from authenticated user to application owner. (deprecated)
args:
- user: authenticated user
'''
permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
serializer_class = TransactionSerializer
required_alternate_scopes = {
"GET": [['wallet.user:transaction_list']],
# "POST": [['wallet.invoice:create']],
}
filter_backends = (DjangoFilterBackend,)
filterset_class = UserTransactionFilter
def get_queryset(self):
user = self.request.user
wallet_uuid = self.kwargs.get('wallet')
wallet = get_object_or_404(Wallet.objects.all(), uuid=wallet_uuid)
account, created = Account.objects.get_or_create(owner_uuid=user.uuid, owner_type=TypeChoices.USER, wallet=wallet)
return Transaction.objects.filter(Q(payee_account=account) | Q(payer_account=account)).all()
class UserTransactionViewSet(mixins.ListModelMixin,
# mixins.CreateModelMixin,
# mixins.RetrieveModelMixin,
# mixins.UpdateModelMixin,
# mixins.DestroyModelMixin,
GenericViewSet):
permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
serializer_class = TransactionSerializer
required_alternate_scopes = {
"GET": [['wallet.user:transaction_list']],
# "POST": [['wallet.invoice:create']],
}
filter_backends = (DjangoFilterBackend,)
filterset_class = UserTransactionFilter
queryset = Transaction.objects.all()
# TODO: redundant
def get_queryset(self):
user = self.request.user
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())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
@action(detail=False, methods=["GET"])
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'))
# کوئری ۲: همه تراکنش‌ها
transactions = Transaction.objects.all().order_by('refrence', 'created_at')
# گروه‌بندی در پایتون
groups = defaultdict(list)
for t in transactions:
groups[t.refrence].append(t)
# ترکیب
result = []
for ref, items in groups.items():
result.append({
'refrence': ref,
'stats': {'total': stats.get(ref, 0), 'count': len(items)},
'transactions': items
})