diff --git a/apps/wallet/admin.py b/apps/wallet/admin.py index a10945c..c1b2165 100755 --- a/apps/wallet/admin.py +++ b/apps/wallet/admin.py @@ -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) diff --git a/apps/wallet/models.py b/apps/wallet/models.py index 7bceb42..ca6707c 100755 --- a/apps/wallet/models.py +++ b/apps/wallet/models.py @@ -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 diff --git a/apps/wallet/serializers.py b/apps/wallet/serializers.py index cbea979..22e96af 100755 --- a/apps/wallet/serializers.py +++ b/apps/wallet/serializers.py @@ -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') - user = request.user - - return obj.get_role(user) + 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'] diff --git a/apps/wallet/tests/application.py b/apps/wallet/tests/application.py index 8f15978..161acdf 100755 --- a/apps/wallet/tests/application.py +++ b/apps/wallet/tests/application.py @@ -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, } diff --git a/apps/wallet/views/application.py b/apps/wallet/views/application.py index b6495f6..1342e0c 100755 --- a/apps/wallet/views/application.py +++ b/apps/wallet/views/application.py @@ -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) + 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) @@ -401,15 +402,16 @@ class CategoryWithdrawSubmitAPIView(generics.CreateAPIView): payer_wallet_category = get_object_or_404(Category.objects.all(), uuid=payer_wallet_category_uuid) - if not(payer_wallet_category and payer_wallet_category.currency == payee_wallet_category.currency): + 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) + 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) diff --git a/apps/wallet/views/user.py b/apps/wallet/views/user.py index a235529..42e886c 100755 --- a/apps/wallet/views/user.py +++ b/apps/wallet/views/user.py @@ -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']],