some cleanup

This commit is contained in:
Sayyid Hamid Mahdavi 2025-10-21 19:58:05 +03:30
parent 4fb76234d1
commit 696fe3c4e5
6 changed files with 70 additions and 74 deletions

View file

@ -2,8 +2,8 @@ from django.contrib import admin
from .models import Transaction, Wallet, Category
class TransactionAdmin(admin.ModelAdmin):
list_display = ['uuid', 'amount', 'created_at', 'updated_at', 'payer_id', 'payer_type', 'payee_id', 'payee_type', 'state', 'application']
search_fields = ['uuid', 'created_at', 'updated_at', 'payer_id', 'payee_id', 'application__uuid']
list_display = ['uuid', 'amount', 'created_at', 'updated_at', 'payer', 'payer_type', 'payee', 'payee_type', 'state', 'application']
search_fields = ['uuid', 'created_at', 'updated_at', 'payer', 'payee', 'application__uuid']
list_filter = ['state']
admin.site.register(Transaction, TransactionAdmin)

View file

@ -134,7 +134,7 @@ class Transaction(BaseModel):
return self.payee_wallet and self.payee_wallet.category_id
@property
def payer_id(self):
def payer(self):
return self.payer_wallet.owner_uuid
@property
@ -142,7 +142,7 @@ class Transaction(BaseModel):
return self.payer_wallet.owner_type
@property
def payee_id(self):
def payee(self):
return self.payee_wallet.owner_uuid
@property

View file

@ -5,23 +5,26 @@ from rest_framework import serializers
from rest_framework.exceptions import APIException
from .constans import TypeChoices
from .models import Transaction
from .models import Transaction, Wallet
from ..gooyal_oauth2.models import Application
from ..users.models import User
class BaseTransactionSerializer(serializers.ModelSerializer):
payee_id = serializers.UUIDField(read_only=True)
payee_type = serializers.UUIDField(read_only=True)
class TransactionSerializer(serializers.ModelSerializer):
payee = serializers.UUIDField(read_only=True)
payee_type = serializers.IntegerField(read_only=True)
payee_wallet_category = serializers.UUIDField(read_only=True)
role = serializers.SerializerMethodField()
class Meta:
model = Transaction
fields = ('uuid',
'payer_id',
'payer',
'payer_type',
'payee_id',
'payer_wallet_category',
'payee',
'payee_type',
'payee_wallet_category',
"created_at",
"state",
'amount',
@ -34,18 +37,17 @@ class BaseTransactionSerializer(serializers.ModelSerializer):
read_only_fields = ['uuid', 'updated_at', 'state', 'created_at', 'details', 'role']
def to_representation(self, instance):
# TODO: performance issue
instance.update_details()
return super().to_representation(instance)
class UserTransactionSerializer(BaseTransactionSerializer):
def get_role(self, obj: Transaction) -> int:
request = self.context.get('request')
if request:
user = request.user
return obj.get_role(user)
return 0
class DepositSerializer(serializers.ModelSerializer):
@ -108,12 +110,6 @@ class WithdrawSerializer(serializers.ModelSerializer):
return transaction
class MyWalletSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['balance']
class WalletBalanceSerializer(serializers.ModelSerializer):
instance_id = serializers.UUIDField(required=True)
instance_type = serializers.ChoiceField(choices=TypeChoices.choices, required=True)
@ -164,18 +160,14 @@ class TransferVerifySerializer(serializers.ModelSerializer):
#### Application
class ApplicationBalanceSerializer(serializers.ModelSerializer):
class BalanceSerializer(serializers.ModelSerializer):
class Meta:
model = Application
fields = ['balance']
class ApplicationTransactionSerializer(BaseTransactionSerializer):
pass
model = Wallet
fields = ['balance', 'category']
class ApplicationDepositSerializer(serializers.ModelSerializer):
uuid = serializers.UUIDField(required=False, validators=[])
payee_id = serializers.UUIDField(required=True)
payee = serializers.UUIDField(required=True)
payee_type = serializers.IntegerField(required=True)
payee_wallet_category = serializers.UUIDField(required=False) # TODO: required True
amount = serializers.IntegerField(required=True, validators=[MinValueValidator(1)])
@ -185,10 +177,10 @@ class ApplicationDepositSerializer(serializers.ModelSerializer):
model = Transaction
fields = (
'uuid',
'payer_id',
'payer',
'payer_type',
'payer_wallet_category',
'payee_id',
'payee',
'payee_type',
'payee_wallet_category',
"created_at",
@ -199,12 +191,12 @@ class ApplicationDepositSerializer(serializers.ModelSerializer):
'details',
)
read_only_fields = ['updated_at', 'state', 'created_at', 'payer_id', 'payer_type', 'payer_wallet_category']
read_only_fields = ['updated_at', 'state', 'created_at', 'payer_id', 'payer_type', 'payer_wallet']
class ApplicationWithdrawSerializer(serializers.ModelSerializer):
uuid = serializers.UUIDField(required=False, validators=[])
payer_id = serializers.UUIDField(required=True)
payer = serializers.UUIDField(required=True)
payer_type = serializers.IntegerField(required=True)
payer_wallet_category = serializers.UUIDField(required=False) # TODO: required True
amount = serializers.IntegerField(required=True, validators=[MinValueValidator(1)])
@ -213,10 +205,10 @@ class ApplicationWithdrawSerializer(serializers.ModelSerializer):
model = Transaction
fields = (
'uuid',
'payer_id',
'payer',
'payer_type',
'payer_wallet_category',
'payee_id',
'payee',
'payee_type',
'payee_wallet_category',
"created_at",
@ -227,4 +219,4 @@ class ApplicationWithdrawSerializer(serializers.ModelSerializer):
'details',
)
read_only_fields = ['updated_at', 'state', 'created_at', 'payee_id', 'payee_type', 'payee_wallet_category']
read_only_fields = ['updated_at', 'state', 'created_at', 'payee', 'payee_type', 'payee_wallet_category']

View file

@ -136,7 +136,7 @@ class ApplicationApiFlowsTests(APITestCase):
# Deposit process
data = {
'amount': 100,
'payee_id': self.payee_uuid,
'payee': self.payee_uuid,
'payee_type': TypeChoices.USER,
'payee_wallet_category': self.wallet_category1_uuid,
}
@ -189,7 +189,7 @@ class ApplicationApiFlowsTests(APITestCase):
# Deposit process
data = {
'amount': 100,
'payee_id': self.payee_uuid,
'payee': self.payee_uuid,
'payee_type': TypeChoices.USER,
'payee_wallet_category': self.wallet_category1_uuid
}
@ -241,7 +241,7 @@ class ApplicationApiFlowsTests(APITestCase):
# Deposit process
data = {
'amount': 500,
'payee_id': self.payee_uuid,
'payee': self.payee_uuid,
'payee_type': TypeChoices.USER,
'payee_wallet_category': self.wallet_category1_uuid
}
@ -267,7 +267,7 @@ class ApplicationApiFlowsTests(APITestCase):
# Deposit process
data = {
'amount': 500,
'payee_id': self.payee_uuid,
'payee': self.payee_uuid,
'payee_type': TypeChoices.USER,
'payee_wallet_category': self.wallet_category1_uuid,
}
@ -280,24 +280,26 @@ class ApplicationApiFlowsTests(APITestCase):
self.assertEqual(response.status_code, 201)
def test_deposit_flow_zero_amount(self):
# TODO: category
auth = self._create_authorization_header(self.application_access_token.token)
# Deposit process
data = {
'amount': 0,
'payee_id': self.payee_uuid,
'payee': self.payee_uuid,
'payee_type': TypeChoices.USER
}
response = self.client.post(reverse('wallet:application_deposit_submit_api'), data=data, HTTP_AUTHORIZATION=auth)
self.assertEqual(response.status_code, 400)
def test_deposit_flow_negative_amount(self):
# TODO: category
auth = self._create_authorization_header(self.application_access_token.token)
# Deposit process
data = {
'amount': -100,
'payee_id': self.payee_uuid,
'payee': self.payee_uuid,
'payee_type': TypeChoices.USER
}
response = self.client.post(reverse('wallet:application_deposit_submit_api'), data=data, HTTP_AUTHORIZATION=auth)
@ -323,7 +325,7 @@ class ApplicationApiFlowsTests(APITestCase):
data = {
'uuid': "b14e8b86-8f4a-44d9-b29d-badceb470053",
'amount': 200,
'payee_id': self.payee_uuid,
'payee': self.payee_uuid,
'payee_type': TypeChoices.USER,
'payee_wallet_category': self.wallet_category1_uuid,
@ -357,7 +359,7 @@ class ApplicationApiFlowsTests(APITestCase):
# Deposit process
data = {
'amount': 100,
'payer_id': self.payer_uuid,
'payer': self.payer_uuid,
'payer_type': TypeChoices.USER,
'payer_wallet_category': self.wallet_category1_uuid,
}
@ -412,7 +414,7 @@ class ApplicationApiFlowsTests(APITestCase):
# Deposit process
data = {
'amount': 100,
'payer_id': self.payer_uuid,
'payer': self.payer_uuid,
'payer_type': TypeChoices.USER,
'payer_wallet_category': self.wallet_category1_uuid,
}
@ -452,7 +454,7 @@ class ApplicationApiFlowsTests(APITestCase):
# Deposit process
data = {
'amount': 0,
'payer_id': self.payer_uuid,
'payer': self.payer_uuid,
'payer_type': TypeChoices.USER,
'payer_wallet_category': self.wallet_category1_uuid,
}
@ -469,7 +471,7 @@ class ApplicationApiFlowsTests(APITestCase):
# Deposit process
data = {
'amount': -500,
'payer_id': self.payer_uuid,
'payer': self.payer_uuid,
'payer_type': TypeChoices.USER
}
response = self.client.post(reverse('wallet:application_withdraw_submit_api'), data=data,
@ -498,7 +500,7 @@ class ApplicationApiFlowsTests(APITestCase):
data = {
'uuid': 'b14e8b86-8f4a-44d9-b29d-badceb470053',
'amount': 200,
'payer_id': self.payer_uuid,
'payer': self.payer_uuid,
'payer_type': TypeChoices.USER,
'payer_wallet_category': self.wallet_category1_uuid,
}

View file

@ -12,7 +12,7 @@ from apps.users.models import User
from apps.wallet.constans import TypeChoices, StateChoices
from apps.wallet.models import Transaction, Category, Wallet
from apps.wallet.serializers import ApplicationDepositSerializer, ApplicationWithdrawSerializer, \
ApplicationTransactionSerializer, ApplicationBalanceSerializer, MyWalletSerializer
TransactionSerializer, BalanceSerializer
from utils.exceptions import UnprocessableEntity
from django.utils.translation import gettext_lazy as _
@ -36,7 +36,7 @@ class BalanceAPIView(generics.RetrieveAPIView):
API for getting specified user or application balance.
'''
permission_classes = [TokenHasScope]
serializer_class = ApplicationBalanceSerializer
serializer_class = BalanceSerializer
required_scopes = ['wallet.application:get_balance']
def get_object(self):
@ -48,7 +48,7 @@ class UserBalanceAPIView(generics.RetrieveAPIView):
API for getting specified user or application balance.
'''
permission_classes = [IsAuthenticatedOrTokenHasScope]
serializer_class = MyWalletSerializer
serializer_class = BalanceSerializer
required_scopes = ['wallet.application:get_user_balance']
def get_object(self):
@ -61,7 +61,7 @@ class UserBalanceAPIView(generics.RetrieveAPIView):
class TransactionDetailAPIView(generics.RetrieveAPIView):
permission_classes = [TokenHasScope]
serializer_class = ApplicationTransactionSerializer
serializer_class = TransactionSerializer
lookup_field = 'uuid'
required_scopes = ['wallet.application:get_transaction_detail']
@ -75,7 +75,7 @@ class TransactionDetailAPIView(generics.RetrieveAPIView):
class TransactionListAPIView(generics.ListAPIView):
permission_classes = [TokenHasScope]
serializer_class = ApplicationTransactionSerializer
serializer_class = TransactionSerializer
lookup_field = 'uuid'
required_scopes = ['wallet.application:get_transaction_list']
@ -184,7 +184,7 @@ class CategoryBalanceAPIView(generics.RetrieveAPIView):
API for getting specified user or application balance.
'''
permission_classes = [TokenHasScope]
serializer_class = ApplicationBalanceSerializer
serializer_class = BalanceSerializer
required_scopes = ['wallet.application:get_balance']
def get_object(self):
@ -205,7 +205,7 @@ class CategoryUserBalanceAPIView(generics.RetrieveAPIView):
API for getting specified user or application balance.
'''
permission_classes = [IsAuthenticatedOrTokenHasScope]
serializer_class = MyWalletSerializer
serializer_class = BalanceSerializer
required_scopes = ['wallet.application:get_user_balance']
def get_object(self):
@ -225,7 +225,7 @@ class CategoryUserBalanceAPIView(generics.RetrieveAPIView):
class CategoryTransactionDetailAPIView(generics.RetrieveAPIView):
permission_classes = [TokenHasScope]
serializer_class = ApplicationTransactionSerializer
serializer_class = TransactionSerializer
lookup_field = 'uuid'
required_scopes = ['wallet.application:get_transaction_detail']
@ -242,7 +242,7 @@ class CategoryTransactionDetailAPIView(generics.RetrieveAPIView):
class CategoryTransactionListAPIView(generics.ListAPIView):
permission_classes = [TokenHasScope]
serializer_class = ApplicationTransactionSerializer
serializer_class = TransactionSerializer
lookup_field = 'uuid'
required_scopes = ['wallet.application:get_transaction_list']
@ -297,12 +297,13 @@ class CategoryDepositSubmitAPIView(generics.CreateAPIView):
if not (payee_wallet_category and payee_wallet_category.currency == payer_wallet_category.currency):
raise UnprocessableEntity('payer and payee wallet currency not equal')
payee_id = serializer.validated_data.pop('payee_id')
payee_id = serializer.validated_data.pop('payee')
payee_type = serializer.validated_data.pop('payee_type')
payer_wallet, _ = Wallet.objects.get_or_create(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION,
category=payer_wallet_category)
logger.debug(f"Payer wallet: {payer_wallet.uuid}")
payee_wallet, _ = Wallet.objects.get_or_create(owner_uuid=payee_id, owner_type=payee_type, category=payee_wallet_category)
payee_wallet, _ = Wallet.objects.get_or_create(owner_uuid=payee_id, owner_type=payee_type,
category=payee_wallet_category)
logger.debug(f"Payee wallet: {payee_wallet.uuid}")
uuid = serializer.validated_data.pop('uuid', None)
@ -404,12 +405,13 @@ class CategoryWithdrawSubmitAPIView(generics.CreateAPIView):
if not (payer_wallet_category and payer_wallet_category.currency == payee_wallet_category.currency):
raise UnprocessableEntity('payer and payee wallet currency not equal')
payer_id = serializer.validated_data.pop('payer_id')
payer_id = serializer.validated_data.pop('payer')
payer_type = serializer.validated_data.pop('payer_type')
payee_wallet, _ = Wallet.objects.get_or_create(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION,
category=payee_wallet_category)
logger.debug(f"Payee wallet: {payee_wallet.uuid}")
payer_wallet, _ = Wallet.objects.get_or_create(owner_uuid=payer_id, owner_type=payer_type, category=payer_wallet_category)
payer_wallet, _ = Wallet.objects.get_or_create(owner_uuid=payer_id, owner_type=payer_type,
category=payer_wallet_category)
logger.debug(f"Payer wallet: {payer_wallet.uuid}")
uuid = serializer.validated_data.pop('uuid', None)

View file

@ -14,8 +14,8 @@ from rest_framework.response import Response
from apps.gooyal_oauth2.rest_framework import IsAuthenticatedOrTokenMatchesOASRequirements
from apps.wallet.models import Transaction, Category, Wallet
from apps.wallet.serializers import UserTransactionSerializer, DepositSerializer, WithdrawSerializer, \
MyWalletSerializer
from apps.wallet.serializers import TransactionSerializer, DepositSerializer, WithdrawSerializer, \
BalanceSerializer
from ..constans import StateChoices, TypeChoices
from ..filters import UserTransactionFilter
from ..forms import TransactionActionForm
@ -36,7 +36,7 @@ class MyWalletBalanceView(generics.RetrieveAPIView):
API for getting authenticated user balance.
'''
permission_classes = [IsAuthenticatedOrTokenHasScope]
serializer_class = MyWalletSerializer
serializer_class = BalanceSerializer
required_scopes = ['wallet.wallet:get_balance']
def get_object(self):
@ -53,7 +53,7 @@ class TransactionList(generics.ListAPIView):
'''
permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
serializer_class = UserTransactionSerializer
serializer_class = TransactionSerializer
required_alternate_scopes = {
"GET": [['wallet.user:transaction_list']],
# "POST": [['wallet.invoice:create']],
@ -76,7 +76,7 @@ class TransactionList(generics.ListAPIView):
class TransactionDetail(generics.RetrieveAPIView):
permission_classes = [permissions.IsAuthenticated, IsAuthenticatedOrTokenHasScope]
serializer_class = UserTransactionSerializer
serializer_class = TransactionSerializer
lookup_field = 'uuid'
required_scopes = ['wallet.transaction:retrieve']
@ -87,7 +87,7 @@ class TransactionDetail(generics.RetrieveAPIView):
class TransactionPay(generics.RetrieveAPIView):
permission_classes = [permissions.IsAuthenticated, IsAuthenticatedOrTokenHasScope]
serializer_class = UserTransactionSerializer
serializer_class = TransactionSerializer
lookup_field = 'uuid'
required_scopes = ['wallet.invoice:pay']
@ -104,7 +104,7 @@ class TransactionPay(generics.RetrieveAPIView):
class TransactionReceipt(generics.RetrieveAPIView):
permission_classes = [permissions.IsAuthenticated, IsAuthenticatedOrTokenHasScope]
serializer_class = UserTransactionSerializer
serializer_class = TransactionSerializer
lookup_field = 'uuid'
required_scopes = ['wallet.invoice:receipt']
@ -231,7 +231,7 @@ class CategoryMyWalletBalanceView(generics.RetrieveAPIView):
API for getting authenticated user balance.
'''
permission_classes = [IsAuthenticatedOrTokenHasScope]
serializer_class = MyWalletSerializer
serializer_class = BalanceSerializer
required_scopes = ['wallet.wallet:get_balance']
def get_object(self):
@ -257,7 +257,7 @@ class CategoryTransactionListView(generics.ListAPIView):
'''
permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
serializer_class = UserTransactionSerializer
serializer_class = TransactionSerializer
required_alternate_scopes = {
"GET": [['wallet.user:transaction_list']],
# "POST": [['wallet.invoice:create']],