add reference field
This commit is contained in:
parent
4393e3878a
commit
c24b52e341
4 changed files with 158 additions and 8 deletions
18
apps/core/migrations/0002_alter_charge_state.py
Normal file
18
apps/core/migrations/0002_alter_charge_state.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 6.0 on 2026-02-07 05:57
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='charge',
|
||||
name='state',
|
||||
field=models.IntegerField(choices=[(1, 'created'), (2, 'delayed'), (3, 'pending'), (4, 'incomplete'), (5, 'success'), (6, 'failed'), (7, 'expected_failure'), (8, 'ROLLED_BACK')], default=1),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# Generated by Django 6.0 on 2026-02-07 05:57
|
||||
|
||||
import django.contrib.postgres.indexes
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wallet', '0016_rename_category_wallet_and_more'),
|
||||
migrations.swappable_dependency(settings.OAUTH2_PROVIDER_APPLICATION_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='transaction',
|
||||
name='reference',
|
||||
field=models.UUIDField(db_index=True, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='transaction',
|
||||
name='state',
|
||||
field=models.IntegerField(choices=[(1, 'created'), (2, 'delayed'), (3, 'pending'), (4, 'incomplete'), (5, 'success'), (6, 'failed'), (7, 'expected_failure'), (8, 'ROLLED_BACK')], default=1),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='transaction',
|
||||
index=django.contrib.postgres.indexes.GinIndex(fields=['details'], name='wallet_transaction_details_a7ff79aa'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import logging
|
||||
from functools import cached_property
|
||||
|
||||
from django.contrib.postgres.indexes import GinIndex
|
||||
from django.core.validators import MinValueValidator
|
||||
from django.db import models
|
||||
from django.db import transaction
|
||||
|
|
@ -32,8 +33,8 @@ class AccountQuerySet(models.QuerySet):
|
|||
|
||||
class Account(BaseModel):
|
||||
owner_uuid = models.UUIDField(db_index=True, null=False)
|
||||
wallet = models.ForeignKey('Wallet', on_delete=models.PROTECT, null=True)
|
||||
owner_type = models.IntegerField(choices=TypeChoices.choices, db_index=True)
|
||||
wallet = models.ForeignKey('Wallet', on_delete=models.PROTECT, null=True)
|
||||
balance = models.BigIntegerField(_('balance'), default=0)
|
||||
|
||||
# TODO: unique together wallet and owner_uuid
|
||||
|
|
@ -111,6 +112,8 @@ class Transaction(BaseModel):
|
|||
db_index=True)
|
||||
details = models.JSONField(null=True, blank=True, default=dict)
|
||||
|
||||
reference = models.UUIDField(db_index=True, null=True)
|
||||
|
||||
# detail = {'payer_name', 'payee_name', 'description', 'application_name', 'payer_avatar_url', 'payee_avatar_url',
|
||||
# 'reference_id', 'application_details_url'}
|
||||
|
||||
|
|
@ -118,6 +121,13 @@ class Transaction(BaseModel):
|
|||
|
||||
class Meta:
|
||||
ordering = ['-created_at']
|
||||
indexes = [
|
||||
GinIndex(
|
||||
fields=['details'],
|
||||
# hashlib.md5("wallet_transaction_details_GinIndex".encode()).hexdigest()[:8] used for hash
|
||||
name='wallet_transaction_details_a7ff79aa'
|
||||
),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
try:
|
||||
|
|
@ -381,3 +391,29 @@ class Transaction(BaseModel):
|
|||
if is_updated:
|
||||
print('update details')
|
||||
self.save()
|
||||
|
||||
# TODO: bulk
|
||||
# class PaymentRequest(BaseModel):
|
||||
# state = models.IntegerField(choices=StateChoices.choices, default=StateChoices.CREATED)
|
||||
# application = models.ForeignKey(oauth2_settings.APPLICATION_MODEL, on_delete=models.PROTECT,
|
||||
# related_name='transactions', null=True)
|
||||
#
|
||||
# payer_uuid = models.UUIDField(db_index=True, null=False)
|
||||
# payer_type = models.IntegerField(choices=TypeChoices.choices, db_index=True)
|
||||
#
|
||||
# payee_uuid = models.UUIDField(db_index=True, null=False)
|
||||
# payee_type = models.IntegerField(choices=TypeChoices.choices, db_index=True)
|
||||
#
|
||||
# details = models.JSONField(null=True, blank=True, default=dict)
|
||||
#
|
||||
# def submit(self):
|
||||
# with transaction.atomic():
|
||||
# pass
|
||||
#
|
||||
# def verify(self):
|
||||
# with transaction.atomic():
|
||||
# pass
|
||||
#
|
||||
# def rollback(self):
|
||||
# with transaction.atomic():
|
||||
# pass
|
||||
|
|
|
|||
|
|
@ -7,10 +7,12 @@ 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
|
||||
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
|
||||
|
|
@ -57,7 +59,7 @@ class TransactionList(generics.ListAPIView):
|
|||
required_alternate_scopes = {
|
||||
"GET": [['wallet.user:transaction_list']],
|
||||
# "POST": [['wallet.invoice:create']],
|
||||
}
|
||||
}
|
||||
|
||||
filter_backends = (DjangoFilterBackend,)
|
||||
filterset_class = UserTransactionFilter
|
||||
|
|
@ -66,7 +68,6 @@ class TransactionList(generics.ListAPIView):
|
|||
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
|
||||
|
|
@ -166,6 +167,7 @@ class WithdrawSubmit(generics.CreateAPIView):
|
|||
payee = self.request.auth.application.user
|
||||
serializer.save(payee=payee, application=get_application(self.request))
|
||||
|
||||
|
||||
# TODO: refactor
|
||||
class WithdrawVerify(generics.RetrieveAPIView):
|
||||
permission_classes = [TokenHasScope]
|
||||
|
|
@ -226,7 +228,6 @@ class TransactionDetailView(UpdateView):
|
|||
return super().form_valid(form)
|
||||
|
||||
|
||||
|
||||
class WalletAccountListAPIView(generics.ListAPIView):
|
||||
'''
|
||||
API for getting authenticated user balance.
|
||||
|
|
@ -238,7 +239,7 @@ class WalletAccountListAPIView(generics.ListAPIView):
|
|||
required_alternate_scopes = {
|
||||
"GET": [['wallet.user:transaction_list']],
|
||||
# "POST": [['wallet.invoice:create']],
|
||||
}
|
||||
}
|
||||
queryset = Account.objects.all()
|
||||
|
||||
def get_queryset(self):
|
||||
|
|
@ -267,7 +268,7 @@ class TransactionListAPIView(generics.ListAPIView):
|
|||
required_alternate_scopes = {
|
||||
"GET": [['wallet.user:transaction_list']],
|
||||
# "POST": [['wallet.invoice:create']],
|
||||
}
|
||||
}
|
||||
|
||||
filter_backends = (DjangoFilterBackend,)
|
||||
filterset_class = UserTransactionFilter
|
||||
|
|
@ -293,7 +294,7 @@ class WalletTransactionListView(generics.ListAPIView):
|
|||
required_alternate_scopes = {
|
||||
"GET": [['wallet.user:transaction_list']],
|
||||
# "POST": [['wallet.invoice:create']],
|
||||
}
|
||||
}
|
||||
|
||||
filter_backends = (DjangoFilterBackend,)
|
||||
filterset_class = UserTransactionFilter
|
||||
|
|
@ -306,3 +307,68 @@ class WalletTransactionListView(generics.ListAPIView):
|
|||
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
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue