change wallet to account

This commit is contained in:
Sayyid Hamid Mahdavi 2025-10-25 14:26:47 +03:30
parent c925100fd6
commit b89487653f
11 changed files with 204 additions and 147 deletions

View file

@ -1,5 +1,5 @@
from django.contrib import admin from django.contrib import admin
from .models import Transaction, Wallet, Category from .models import Transaction, Account, Category
class TransactionAdmin(admin.ModelAdmin): class TransactionAdmin(admin.ModelAdmin):
list_display = ['uuid', 'amount', 'created_at', 'updated_at', 'payer', 'payer_type', 'payee', 'payee_type', 'state', 'application'] list_display = ['uuid', 'amount', 'created_at', 'updated_at', 'payer', 'payer_type', 'payee', 'payee_type', 'state', 'application']
@ -7,5 +7,5 @@ class TransactionAdmin(admin.ModelAdmin):
list_filter = ['state'] list_filter = ['state']
admin.site.register(Transaction, TransactionAdmin) admin.site.register(Transaction, TransactionAdmin)
admin.site.register(Wallet) admin.site.register(Account)
admin.site.register(Category) admin.site.register(Category)

View file

@ -3,33 +3,33 @@ from django.db import models
from django.db.models import Q from django.db.models import Q
from .constans import TypeChoices from .constans import TypeChoices
from .models import Transaction, Wallet from .models import Transaction, Account
class CategoryUUIDFilter(django_filters.BaseInFilter, django_filters.UUIDFilter): class WalletUUIDFilter(django_filters.BaseInFilter, django_filters.UUIDFilter):
pass pass
class UserTransactionCategoryFilter(django_filters.BaseInFilter, django_filters.UUIDFilter): class UserTransactionWalletFilter(django_filters.BaseInFilter, django_filters.UUIDFilter):
def filter(self, queryset, value): def filter(self, queryset, value):
request = self.parent.request request = self.parent.request
user = request.user user = request.user
return queryset.filter( return queryset.filter(
Q(payer_wallet__owner_uuid=user.pk, Q(payer_account__owner_uuid=user.pk,
payer_wallet__owner_type=TypeChoices.USER, payer_account__owner_type=TypeChoices.USER,
payer_wallet__category_id__in=value, payer_account__category_id__in=value,
) | ) |
Q(payee_wallet__owner_uuid=user.pk, Q(payee_account__owner_uuid=user.pk,
payee_wallet__owner_type=TypeChoices.USER, payee_account__owner_type=TypeChoices.USER,
payee_wallet__category_id__in=value, payee_account__category_id__in=value,
)) ))
class UserTransactionFilter(django_filters.FilterSet): class UserTransactionFilter(django_filters.FilterSet):
payer = django_filters.UUIDFilter(field_name='payer_wallet__owner_uuid', lookup_expr='iexact') payer = django_filters.UUIDFilter(field_name='payer_account__owner_uuid', lookup_expr='iexact')
payee = django_filters.UUIDFilter(field_name='payee_wallet__owner_uuid', lookup_expr='iexact') payee = django_filters.UUIDFilter(field_name='payee_account__owner_uuid', lookup_expr='iexact')
wallets = UserTransactionCategoryFilter(field_name='payee_wallet__category', required=True, lookup_expr='in') wallets = UserTransactionWalletFilter(field_name='payee_account__wallet', required=True, lookup_expr='in')
class Meta: class Meta:
model = Transaction model = Transaction
@ -37,10 +37,10 @@ class UserTransactionFilter(django_filters.FilterSet):
} }
class WalletFilter(django_filters.FilterSet): class AccountFilter(django_filters.FilterSet):
wallets = CategoryUUIDFilter(field_name='category', required=True, lookup_expr='in') wallets = WalletUUIDFilter(field_name='category', required=True, lookup_expr='in')
class Meta: class Meta:
model = Wallet model = Account
fields = { fields = {
} }

View file

@ -0,0 +1,18 @@
# Generated by Django 5.1.7 on 2025-10-25 10:41
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wallet', '0012_remove_transaction__payee_id_and_more'),
]
operations = [
migrations.RenameModel(
old_name='Wallet',
new_name='Account',
),
]

View file

@ -0,0 +1,24 @@
# Generated by Django 5.1.7 on 2025-10-25 10:42
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wallet', '0013_rename_wallet_account_and_more'),
]
operations = [
migrations.RenameField(
model_name='transaction',
old_name='payee_wallet',
new_name='payee_account',
),
migrations.RenameField(
model_name='transaction',
old_name='payer_wallet',
new_name='payer_account',
),
]

View file

@ -0,0 +1,24 @@
# Generated by Django 5.1.7 on 2025-10-25 10:44
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wallet', '0014_remove_transaction_payee_wallet_and_more'),
]
operations = [
migrations.AlterField(
model_name='transaction',
name='payee_account',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='payee_account_transactions', to='wallet.account'),
),
migrations.AlterField(
model_name='transaction',
name='payer_account',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='payer_account_transactions', to='wallet.account'),
),
]

View file

@ -22,16 +22,16 @@ class CurrencyChoices(models.TextChoices):
IRANIAN_RIAL = '166ab7ce33b34799950d45035a9997d6', _('IRANIAN RIAL') # uuid without dash IRANIAN_RIAL = '166ab7ce33b34799950d45035a9997d6', _('IRANIAN RIAL') # uuid without dash
class WalletQuerySet(models.QuerySet): class AccountQuerySet(models.QuerySet):
pass pass
class Wallet(BaseModel): class Account(BaseModel):
owner_uuid = models.UUIDField(db_index=True, null=False) owner_uuid = models.UUIDField(db_index=True, null=False)
category = models.ForeignKey('Category', on_delete=models.PROTECT, null=True) category = models.ForeignKey('Category', on_delete=models.PROTECT, null=True)
owner_type = models.IntegerField(choices=TypeChoices.choices, db_index=True) owner_type = models.IntegerField(choices=TypeChoices.choices, db_index=True)
balance = models.BigIntegerField(_('balance'), default=0) balance = models.BigIntegerField(_('balance'), default=0)
objects = WalletQuerySet.as_manager() objects = AccountQuerySet.as_manager()
def get_owner(self): def get_owner(self):
if self.owner_type == TypeChoices.APPLICATION: if self.owner_type == TypeChoices.APPLICATION:
@ -85,7 +85,7 @@ class Category(BaseModel):
class TransactionQuerySet(models.QuerySet): class TransactionQuerySet(models.QuerySet):
def available(self): def available(self):
return self.select_related('payer_wallet', 'payee_wallet') return self.select_related('payer_account', 'payee_account')
class Transaction(BaseModel): class Transaction(BaseModel):
@ -94,11 +94,11 @@ class Transaction(BaseModel):
state = models.IntegerField(choices=StateChoices.choices, default=StateChoices.CREATED) state = models.IntegerField(choices=StateChoices.choices, default=StateChoices.CREATED)
application = models.ForeignKey(oauth2_settings.APPLICATION_MODEL, on_delete=models.PROTECT, application = models.ForeignKey(oauth2_settings.APPLICATION_MODEL, on_delete=models.PROTECT,
related_name='transactions', null=True) related_name='transactions', null=True)
payer_wallet = models.ForeignKey(Wallet, on_delete=models.PROTECT, null=True, payer_account = models.ForeignKey(Account, on_delete=models.PROTECT, null=True,
related_name='payer_wallet_transactions', related_name='payer_account_transactions',
db_index=True) db_index=True)
payee_wallet = models.ForeignKey(Wallet, on_delete=models.PROTECT, null=True, payee_account = models.ForeignKey(Account, on_delete=models.PROTECT, null=True,
related_name='payee_wallet_transactions', related_name='payee_account_transactions',
db_index=True) db_index=True)
details = models.JSONField(null=True, blank=True, default=dict) details = models.JSONField(null=True, blank=True, default=dict)
@ -119,34 +119,34 @@ class Transaction(BaseModel):
# unique_together = ('application', 'detail_id',) # unique_together = ('application', 'detail_id',)
def get_payer(self): def get_payer(self):
return self.payer_wallet.get_owner() return self.payer_account.get_owner()
def get_payee(self): def get_payee(self):
return self.payee_wallet.get_owner() return self.payee_account.get_owner()
@cached_property @cached_property
def payer_wallet_category(self): def payer_wallet(self):
return self.payer_wallet and self.payer_wallet.category_id return self.payer_account and self.payer_account.category_id
@cached_property @cached_property
def payee_wallet_category(self): def payee_wallet(self):
return self.payee_wallet and self.payee_wallet.category_id return self.payee_account and self.payee_account.category_id
@cached_property @cached_property
def payer(self): def payer(self):
return self.payer_wallet.owner_uuid return self.payer_account.owner_uuid
@cached_property @cached_property
def payer_type(self): def payer_type(self):
return self.payer_wallet.owner_type return self.payer_account.owner_type
@cached_property @cached_property
def payee(self): def payee(self):
return self.payee_wallet.owner_uuid return self.payee_account.owner_uuid
@cached_property @cached_property
def payee_type(self): def payee_type(self):
return self.payee_wallet.owner_type return self.payee_account.owner_type
def change_state(self, from_state, to_state, error_message=None, **data): def change_state(self, from_state, to_state, error_message=None, **data):
if self.state == from_state: if self.state == from_state:
@ -162,9 +162,9 @@ class Transaction(BaseModel):
raise APIException(error_message) raise APIException(error_message)
def withdraw_from_payer_balance(self): def withdraw_from_payer_balance(self):
payer_is_allowed_negative_balance = self.payer_wallet.owner_type == TypeChoices.APPLICATION.value and str( payer_is_allowed_negative_balance = self.payer_account.owner_type == TypeChoices.APPLICATION.value and str(
self.payer_wallet.owner_uuid) in settings.ALLOWED_NEGATIVE_BALANCE_APPLICATIONS self.payer_account.owner_uuid) in settings.ALLOWED_NEGATIVE_BALANCE_APPLICATIONS
if self.amount > self.payer_wallet.balance and not payer_is_allowed_negative_balance: if self.amount > self.payer_account.balance and not payer_is_allowed_negative_balance:
raise UnprocessableEntity('not enough balance') raise UnprocessableEntity('not enough balance')
self.payer_wallet.balance = F('balance') - self.amount self.payer_wallet.balance = F('balance') - self.amount
self.payer_wallet.save(update_fields=['balance']) self.payer_wallet.save(update_fields=['balance'])

View file

@ -5,7 +5,7 @@ from rest_framework import serializers
from rest_framework.exceptions import APIException from rest_framework.exceptions import APIException
from .constans import TypeChoices from .constans import TypeChoices
from .models import Transaction, Wallet from .models import Transaction, Account
from ..gooyal_oauth2.models import Application from ..gooyal_oauth2.models import Application
from ..users.models import User from ..users.models import User
@ -13,8 +13,8 @@ from ..users.models import User
class TransactionSerializer(serializers.ModelSerializer): class TransactionSerializer(serializers.ModelSerializer):
payee = serializers.UUIDField(read_only=True) payee = serializers.UUIDField(read_only=True)
payee_type = serializers.IntegerField(read_only=True) payee_type = serializers.IntegerField(read_only=True)
payee_wallet = serializers.UUIDField(source='payee_wallet_category', read_only=True) # payee_wallet = serializers.UUIDField(source='payee_wallet_category', read_only=True)
payer_wallet = serializers.UUIDField(source='payer_wallet_category', read_only=True) # payer_wallet = serializers.UUIDField(source='payer_wallet_category', read_only=True)
role = serializers.SerializerMethodField() role = serializers.SerializerMethodField()
class Meta: class Meta:
@ -111,7 +111,7 @@ class WithdrawSerializer(serializers.ModelSerializer):
return transaction return transaction
class WalletBalanceSerializer(serializers.ModelSerializer): class AccountBalanceSerializer(serializers.ModelSerializer):
instance_id = serializers.UUIDField(required=True) instance_id = serializers.UUIDField(required=True)
instance_type = serializers.ChoiceField(choices=TypeChoices.choices, required=True) instance_type = serializers.ChoiceField(choices=TypeChoices.choices, required=True)
@ -160,22 +160,18 @@ class TransferVerifySerializer(serializers.ModelSerializer):
#### Application #### Application
class WalletSerializer(serializers.ModelSerializer): class AccountSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Wallet model = Account
fields = ['balance', 'category'] fields = ['balance', 'category']
class BalanceSerializer(serializers.ModelSerializer):
class Meta:
model = Wallet
fields = ['balance', 'category']
class ApplicationDepositSerializer(serializers.ModelSerializer): class ApplicationDepositSerializer(serializers.ModelSerializer):
uuid = serializers.UUIDField(required=False, validators=[]) uuid = serializers.UUIDField(required=False, validators=[])
payee = serializers.UUIDField(required=True) payee = serializers.UUIDField(required=True)
payee_type = serializers.IntegerField(required=True) payee_type = serializers.IntegerField(required=True)
payee_wallet = serializers.UUIDField(source='payee_wallet_category', required=False) # TODO: required True # payee_wallet = serializers.UUIDField(source='payee_wallet', required=False) # TODO: required True
payer_wallet = serializers.UUIDField(source='payer_wallet_category', read_only=True) # payer_wallet = serializers.UUIDField(source='payer_wallet', read_only=True)
amount = serializers.IntegerField(required=True, validators=[MinValueValidator(1)]) amount = serializers.IntegerField(required=True, validators=[MinValueValidator(1)])
# TODO: details as fields # TODO: details as fields
@ -204,8 +200,8 @@ class ApplicationWithdrawSerializer(serializers.ModelSerializer):
uuid = serializers.UUIDField(required=False, validators=[]) uuid = serializers.UUIDField(required=False, validators=[])
payer = serializers.UUIDField(required=True) payer = serializers.UUIDField(required=True)
payer_type = serializers.IntegerField(required=True) payer_type = serializers.IntegerField(required=True)
payer_wallet = serializers.UUIDField(source='payer_wallet_category',required=False) # TODO: required True # payer_wallet = serializers.UUIDField(source='payer_wallet',required=False) # TODO: required True
payee_wallet = serializers.UUIDField(source='payee_wallet_category', read_only=True) # TODO: required True # payee_wallet = serializers.UUIDField(source='payee_wallet', read_only=True) # TODO: required True
amount = serializers.IntegerField(required=True, validators=[MinValueValidator(1)]) amount = serializers.IntegerField(required=True, validators=[MinValueValidator(1)])
class Meta: class Meta:

View file

@ -8,7 +8,7 @@ from rest_framework.test import APITestCase, override_settings
from apps.users.models import User from apps.users.models import User
from apps.wallet.constans import TypeChoices from apps.wallet.constans import TypeChoices
from apps.wallet.models import Wallet from apps.wallet.models import Account
from apps.wallet.models import Category from apps.wallet.models import Category
AccessToken = get_access_token_model() AccessToken = get_access_token_model()
@ -41,9 +41,9 @@ class ApplicationApiFlowsTests(APITestCase):
def setUp(self): def setUp(self):
self.application_user, _ = User.objects.get_or_create(pk=self.application_uuid) self.application_user, _ = User.objects.get_or_create(pk=self.application_uuid)
self.payer_user, _ = User.objects.get_or_create(pk=self.payer_uuid) self.payer_user, _ = User.objects.get_or_create(pk=self.payer_uuid)
self.application_wallet, _ = Wallet.objects.get_or_create(owner_uuid=self.application_uuid, owner_type=TypeChoices.APPLICATION, balance=300) self.application_wallet, _ = Account.objects.get_or_create(owner_uuid=self.application_uuid, owner_type=TypeChoices.APPLICATION, balance=300)
self.payee_wallet, _ = Wallet.objects.get_or_create(owner_uuid=self.payee_uuid, owner_type=TypeChoices.USER) self.payee_wallet, _ = Account.objects.get_or_create(owner_uuid=self.payee_uuid, owner_type=TypeChoices.USER)
self.payer_wallet, _ = Wallet.objects.get_or_create(owner_uuid=self.payer_uuid, owner_type=TypeChoices.USER, balance=300) self.payer_wallet, _ = Account.objects.get_or_create(owner_uuid=self.payer_uuid, owner_type=TypeChoices.USER, balance=300)
expire_datetime = timezone.now() + timedelta(seconds=3600) expire_datetime = timezone.now() + timedelta(seconds=3600)
expire_datetime.isoformat() expire_datetime.isoformat()
@ -77,12 +77,12 @@ class ApplicationApiFlowsTests(APITestCase):
) )
self.wallet_category1, _ = Category.objects.get_or_create(application=self.application, uuid=self.wallet_category1_uuid) self.wallet_category1, _ = Category.objects.get_or_create(application=self.application, uuid=self.wallet_category1_uuid)
self.wallet_category2, _ = Category.objects.get_or_create(application=self.application, uuid=self.wallet_category2_uuid) self.wallet_category2, _ = Category.objects.get_or_create(application=self.application, uuid=self.wallet_category2_uuid)
self.application_wallet1, _ = Wallet.objects.get_or_create(owner_uuid=self.application_uuid, owner_type=TypeChoices.APPLICATION, balance=300, category=self.wallet_category1) self.application_wallet1, _ = Account.objects.get_or_create(owner_uuid=self.application_uuid, owner_type=TypeChoices.APPLICATION, balance=300, category=self.wallet_category1)
self.application_wallet2, _ = Wallet.objects.get_or_create(owner_uuid=self.application_uuid, owner_type=TypeChoices.APPLICATION, balance=300, category=self.wallet_category2) self.application_wallet2, _ = Account.objects.get_or_create(owner_uuid=self.application_uuid, owner_type=TypeChoices.APPLICATION, balance=300, category=self.wallet_category2)
self.payee_wallet1, _ = Wallet.objects.get_or_create(owner_uuid=self.payee_uuid, owner_type=TypeChoices.USER, category=self.wallet_category1) self.payee_wallet1, _ = Account.objects.get_or_create(owner_uuid=self.payee_uuid, owner_type=TypeChoices.USER, category=self.wallet_category1)
self.payee_wallet2, _ = Wallet.objects.get_or_create(owner_uuid=self.payee_uuid, owner_type=TypeChoices.USER, category=self.wallet_category2) self.payee_wallet2, _ = Account.objects.get_or_create(owner_uuid=self.payee_uuid, owner_type=TypeChoices.USER, category=self.wallet_category2)
self.payer_wallet1, _ = Wallet.objects.get_or_create(owner_uuid=self.payer_uuid, owner_type=TypeChoices.USER, balance=300, category=self.wallet_category1) self.payer_wallet1, _ = Account.objects.get_or_create(owner_uuid=self.payer_uuid, owner_type=TypeChoices.USER, balance=300, category=self.wallet_category1)
self.payer_wallet2, _ = Wallet.objects.get_or_create(owner_uuid=self.payer_uuid, owner_type=TypeChoices.USER, balance=300, category=self.wallet_category2) self.payer_wallet2, _ = Account.objects.get_or_create(owner_uuid=self.payer_uuid, owner_type=TypeChoices.USER, balance=300, category=self.wallet_category2)
def tearDown(self): def tearDown(self):
super().tearDown() super().tearDown()

View file

@ -8,16 +8,16 @@ app_name = "wallet"
user_urlpatterns = [ user_urlpatterns = [
path('transactions/', user_views.TransactionListView.as_view(), name='user_transaction_list_page'), path('transactions/', user_views.TransactionListView.as_view(), name='user_transaction_list_page'),
path('transactions/<pk>', user_views.TransactionDetailView.as_view(), name='user_transaction_detail_page'), path('transactions/<pk>', user_views.TransactionDetailView.as_view(), name='user_transaction_detail_page'),
# path('api/wallet/balance', user_views.CategoryMyWalletBalanceView.as_view(), name='user_wallet_balance_api'), # path('api/wallet/balance', user_views.CategoryMyAccountBalanceView.as_view(), name='user_wallet_balance_api'),
path('api/transactions/', user_views.TransactionListAPIView.as_view(), name='transaction_list_api'), path('api/transactions/', user_views.TransactionListAPIView.as_view(), name='transaction_list_api'),
# path('api/user/<category_uuid>/balance', user_views.CategoryMyWalletBalanceView.as_view(), name='user_wallet_balance_api'), # path('api/user/<category_uuid>/balance', user_views.CategoryMyAccountBalanceView.as_view(), name='user_wallet_balance_api'),
path('api/user/<category_uuid>/transactions/', user_views.CategoryTransactionListView.as_view(), name='transaction_list_api'), path('api/user/<category_uuid>/transactions/', user_views.CategoryTransactionListView.as_view(), name='transaction_list_api'),
] ]
application_urlpatterns = [ application_urlpatterns = [
path('api/application/wallets', application_views.WalletListAPIView.as_view(), name='application_wallet_balance_api'), path('api/application/accounts/', application_views.AccountListAPIView.as_view(), name='application_accounts_balance_api'),
path('api/application/user/<user_uuid>/wallets/', application_views.UserWalletListAPIView.as_view(), name='application_user_wallet_api'), path('api/application/user/<user_uuid>/accounts/', application_views.UserAccountListAPIView.as_view(), name='application_user_account_api'),
# path('api/application/', application_views.TransactionListAPIView.as_view(), name='application_transaction_list_api'), # path('api/application/', application_views.TransactionListAPIView.as_view(), name='application_transaction_list_api'),
# path('api/application/<uuid>/', application_views.TransactionDetailAPIView.as_view(), name='application_transaction_detail'), # path('api/application/<uuid>/', application_views.TransactionDetailAPIView.as_view(), name='application_transaction_detail'),

View file

@ -11,10 +11,10 @@ from rest_framework.response import Response
from apps.users.models import User from apps.users.models import User
from apps.wallet.constans import TypeChoices, StateChoices from apps.wallet.constans import TypeChoices, StateChoices
from apps.wallet.filters import WalletFilter, UserTransactionFilter from apps.wallet.filters import AccountFilter, UserTransactionFilter
from apps.wallet.models import Transaction, Category, Wallet from apps.wallet.models import Transaction, Category, Account
from apps.wallet.serializers import ApplicationDepositSerializer, ApplicationWithdrawSerializer, \ from apps.wallet.serializers import ApplicationDepositSerializer, ApplicationWithdrawSerializer, \
TransactionSerializer, BalanceSerializer, WalletSerializer TransactionSerializer, AccountSerializer
from utils.exceptions import UnprocessableEntity from utils.exceptions import UnprocessableEntity
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -29,17 +29,13 @@ def get_application(request):
return application return application
def get_wallet_category(request: Request): class AccountListAPIView(generics.ListAPIView):
pass
class WalletListAPIView(generics.ListAPIView):
''' '''
API for getting specified user or application balance. API for getting specified user or application balance.
''' '''
serializer_class = BalanceSerializer serializer_class = AccountSerializer
filter_backends = (DjangoFilterBackend,) filter_backends = (DjangoFilterBackend,)
filterset_class = WalletFilter filterset_class = AccountFilter
permission_classes = [IsAuthenticatedOrTokenHasScope] permission_classes = [IsAuthenticatedOrTokenHasScope]
required_scopes = ['wallet.application:get_balance'] required_scopes = ['wallet.application:get_balance']
@ -51,9 +47,9 @@ class WalletListAPIView(generics.ListAPIView):
category = None category = None
application = get_application(self.request) application = get_application(self.request)
wallet, created = Wallet.objects.get_or_create(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION, account, created = Account.objects.get_or_create(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION,
category=category) category=category)
return wallet return account
def get_queryset(self): def get_queryset(self):
category_uuid = self.kwargs.get('category_uuid') category_uuid = self.kwargs.get('category_uuid')
@ -62,22 +58,22 @@ class WalletListAPIView(generics.ListAPIView):
else: else:
category = None category = None
application = get_application(self.request) application = get_application(self.request)
wallet, created = Wallet.objects.get_or_create(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION, wallet, created = Account.objects.get_or_create(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION,
category=category) category=category)
return Wallet.objects.filter(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION).all() return Account.objects.filter(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION).all()
class UserWalletListAPIView(generics.ListAPIView): class UserAccountListAPIView(generics.ListAPIView):
''' '''
API for getting specified user wallet. API for getting specified user wallet.
''' '''
serializer_class = WalletSerializer serializer_class = AccountSerializer
filter_backends = (DjangoFilterBackend,) filter_backends = (DjangoFilterBackend,)
filterset_class = WalletFilter filterset_class = AccountFilter
permission_classes = [IsAuthenticatedOrTokenHasScope] permission_classes = [IsAuthenticatedOrTokenHasScope]
required_scopes = ['wallet.application:get_user_balance'] required_scopes = ['wallet.application:get_user_balance']
queryset = Wallet.objects.all() queryset = Account.objects.all()
def get_queryset(self): def get_queryset(self):
qs = super().get_queryset() qs = super().get_queryset()
@ -88,7 +84,7 @@ class UserWalletListAPIView(generics.ListAPIView):
else: else:
category = None category = None
user, created = User.objects.get_or_create(uuid=self.kwargs.get('uuid')) user, created = User.objects.get_or_create(uuid=self.kwargs.get('uuid'))
wallet, created = Wallet.objects.get_or_create(owner_uuid=user_uuid, owner_type=TypeChoices.USER, wallet, created = Account.objects.get_or_create(owner_uuid=user_uuid, owner_type=TypeChoices.USER,
category=category) category=category)
return qs.filter(owner_uuid=user_uuid, owner_type=TypeChoices.USER).all() return qs.filter(owner_uuid=user_uuid, owner_type=TypeChoices.USER).all()
@ -97,7 +93,7 @@ class TransactionDetailAPIView(generics.RetrieveAPIView):
serializer_class = TransactionSerializer serializer_class = TransactionSerializer
lookup_field = 'uuid' lookup_field = 'uuid'
filter_backends = (DjangoFilterBackend,) filter_backends = (DjangoFilterBackend,)
filterset_class = WalletFilter filterset_class = AccountFilter
permission_classes = [TokenHasScope] permission_classes = [TokenHasScope]
required_scopes = ['wallet.application:get_transaction_detail'] required_scopes = ['wallet.application:get_transaction_detail']
queryset = Transaction.objects.all() queryset = Transaction.objects.all()
@ -155,12 +151,12 @@ class CategoryTransactionDetailAPIView(generics.RetrieveAPIView):
application = get_application(self.request) application = get_application(self.request)
return Transaction.objects.filter(Q(application=application) | return Transaction.objects.filter(Q(application=application) |
Q(payer_wallet__owner_uuid=application.pk, Q(payer_account__owner_uuid=application.pk,
payer_wallet__owner_type=TypeChoices.APPLICATION, payer_account__owner_type=TypeChoices.APPLICATION,
payer_wallet__category=category) | payer_account__category=category) |
Q(payee_wallet__owner_uuid=application.pk, Q(payee_account__owner_uuid=application.pk,
payee_wallet__owner_type=TypeChoices.APPLICATION, payee_account__owner_type=TypeChoices.APPLICATION,
payee_wallet__category=category) payee_account__category=category)
).all() ).all()
@ -182,12 +178,12 @@ class CategoryTransactionListAPIView(generics.ListAPIView):
application = get_application(self.request) application = get_application(self.request)
return qs.filter(Q(application=application) | return qs.filter(Q(application=application) |
Q(payer_wallet__owner_uuid=application.pk, Q(payer_account__owner_uuid=application.pk,
payer_wallet__owner_type=TypeChoices.APPLICATION, payer_account__owner_type=TypeChoices.APPLICATION,
payer_wallet__category=category) | payer_account__category=category) |
Q(payee_wallet__owner_uuid=application.pk, Q(payee_account__owner_uuid=application.pk,
payee_wallet__owner_type=TypeChoices.APPLICATION, payee_account__owner_type=TypeChoices.APPLICATION,
payee_wallet__category=category) payee_account__category=category)
).all() ).all()
@ -205,9 +201,9 @@ class DepositSubmitAPIView(generics.CreateAPIView):
else: else:
category = None category = None
application = get_application(self.request) application = get_application(self.request)
wallet, created = Wallet.objects.get_or_create(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION, payer_account, created = Account.objects.get_or_create(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION,
category=category) category=category)
return Transaction.objects.filter(application=application, payer_wallet=wallet).all() return Transaction.objects.filter(application=application, payer_account=payer_account).all()
def perform_create(self, serializer): def perform_create(self, serializer):
# TODO: get_or_create instead of create if uuid given # TODO: get_or_create instead of create if uuid given
@ -216,29 +212,29 @@ class DepositSubmitAPIView(generics.CreateAPIView):
application = get_application(self.request) application = get_application(self.request)
logger.debug(f"Application UUID: {application.uuid}") logger.debug(f"Application UUID: {application.uuid}")
payee_wallet_category_uuid = serializer.validated_data.pop('payee_wallet_category') payee_wallet_uuid = serializer.validated_data.pop('payee_wallet')
payee_wallet_category_uuid = payee_wallet_category_uuid and str(payee_wallet_category_uuid) payee_wallet_uuid = payee_wallet_uuid and str(payee_wallet_uuid)
payer_wallet_category_uuid = self.kwargs.get('category_uuid') payer_wallet_uuid = self.kwargs.get('wallet_uuid')
if payer_wallet_category_uuid: if payer_wallet_uuid:
payer_wallet_category = get_object_or_404(Category.objects.all(), uuid=payer_wallet_category_uuid) payer_wallet = get_object_or_404(Category.objects.all(), uuid=payer_wallet_uuid)
else: else:
payer_wallet_category = None payer_wallet = None
payee_wallet_category = get_object_or_404(Category.objects.all(), uuid=payee_wallet_category_uuid) payee_wallet = get_object_or_404(Category.objects.all(), uuid=payee_wallet_uuid)
if not (payee_wallet_category and payee_wallet_category.currency == payer_wallet_category.currency): if not (payee_wallet and payee_wallet.currency == payer_wallet.currency):
raise UnprocessableEntity('payer and payee wallet currency not equal') raise UnprocessableEntity('payer and payee wallet currency not equal')
payee_id = serializer.validated_data.pop('payee') payee_id = serializer.validated_data.pop('payee')
payee_type = serializer.validated_data.pop('payee_type') payee_type = serializer.validated_data.pop('payee_type')
payer_wallet, _ = Wallet.objects.get_or_create(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION, payer_account, _ = Account.objects.get_or_create(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION,
category=payer_wallet_category) category=payer_wallet)
logger.debug(f"Payer wallet: {payer_wallet.uuid}") logger.debug(f"Payer account: {payer_account.uuid}")
payee_wallet, _ = Wallet.objects.get_or_create(owner_uuid=payee_id, owner_type=payee_type, payee_account, _ = Account.objects.get_or_create(owner_uuid=payee_id, owner_type=payee_type,
category=payee_wallet_category) category=payee_wallet)
logger.debug(f"Payee wallet: {payee_wallet.uuid}") logger.debug(f"Payee account: {payee_account.uuid}")
uuid = serializer.validated_data.pop('uuid', None) uuid = serializer.validated_data.pop('uuid', None)
details = serializer.validated_data.get('details', {}) details = serializer.validated_data.get('details', {})
@ -246,8 +242,8 @@ class DepositSubmitAPIView(generics.CreateAPIView):
logger.debug(f"Transaction UUID: {uuid}, Reference ID: {reference_id}") logger.debug(f"Transaction UUID: {uuid}, Reference ID: {reference_id}")
transaction_params = { transaction_params = {
'application': application, 'application': application,
'payer_wallet': payer_wallet, 'payer_account': payer_account,
'payee_wallet': payee_wallet, 'payee_account': payee_account,
'amount': serializer.validated_data.get('amount'), 'amount': serializer.validated_data.get('amount'),
} }
if uuid: if uuid:
@ -267,7 +263,6 @@ class DepositSubmitAPIView(generics.CreateAPIView):
if instance.state == StateChoices.PENDING: if instance.state == StateChoices.PENDING:
return return
# TODO: status code 200 if not created before
if created or instance.state == StateChoices.CREATED: if created or instance.state == StateChoices.CREATED:
instance.submit() instance.submit()
else: else:
@ -288,8 +283,8 @@ class DepositVerifyAPIView(generics.RetrieveAPIView):
category = get_object_or_404(Category.objects.all(), uuid=category_uuid) category = get_object_or_404(Category.objects.all(), uuid=category_uuid)
else: else:
category = None category = None
wallet = Wallet.objects.get(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION, category=category) account = Account.objects.get(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION, category=category)
return Transaction.objects.filter(application=application, payer_wallet=wallet).all() return Transaction.objects.filter(application=application, payer_account=account).all()
def retrieve(self, request, *args, **kwargs): def retrieve(self, request, *args, **kwargs):
instance = self.get_object() instance = self.get_object()
@ -313,9 +308,9 @@ class WithdrawSubmitAPIView(generics.CreateAPIView):
else: else:
category = None category = None
application = get_application(self.request) application = get_application(self.request)
wallet, created = Wallet.objects.get_or_create(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION, payee_account, created = Account.objects.get_or_create(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION,
category=category) category=category)
return Transaction.objects.filter(application=application, wallet=wallet).all() return Transaction.objects.filter(application=application, payee_account=payee_account).all()
def perform_create(self, serializer): def perform_create(self, serializer):
# TODO: get_or_create instead of create if uuid given # TODO: get_or_create instead of create if uuid given
@ -324,29 +319,29 @@ class WithdrawSubmitAPIView(generics.CreateAPIView):
application = get_application(self.request) application = get_application(self.request)
logger.debug(f"Application UUID: {application.uuid}") logger.debug(f"Application UUID: {application.uuid}")
payer_wallet_category_uuid = serializer.validated_data.pop('payer_wallet_category') payer_wallet_uuid = serializer.validated_data.pop('payer_wallet')
payer_wallet_category_uuid = payer_wallet_category_uuid and str(payer_wallet_category_uuid) payer_wallet_uuid = payer_wallet_uuid and str(payer_wallet_uuid)
payee_wallet_category_uuid = self.kwargs.get('category_uuid') payee_wallet_uuid = self.kwargs.get('category_uuid')
if payee_wallet_category_uuid: if payee_wallet_uuid:
payee_wallet_category = get_object_or_404(Category.objects.all(), uuid=payee_wallet_category_uuid) payee_wallet = get_object_or_404(Category.objects.all(), uuid=payee_wallet_uuid)
else: else:
payee_wallet_category = None payee_wallet = None
payer_wallet_category = get_object_or_404(Category.objects.all(), uuid=payer_wallet_category_uuid) payer_wallet = get_object_or_404(Category.objects.all(), uuid=payer_wallet_uuid)
if not (payer_wallet_category and payer_wallet_category.currency == payee_wallet_category.currency): if not (payer_wallet and payer_wallet.currency == payee_wallet.currency):
raise UnprocessableEntity('payer and payee wallet currency not equal') raise UnprocessableEntity('payer and payee wallet currency not equal')
payer_id = serializer.validated_data.pop('payer') payer_id = serializer.validated_data.pop('payer')
payer_type = serializer.validated_data.pop('payer_type') payer_type = serializer.validated_data.pop('payer_type')
payee_wallet, _ = Wallet.objects.get_or_create(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION, payee_account, _ = Account.objects.get_or_create(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION,
category=payee_wallet_category) category=payee_wallet)
logger.debug(f"Payee wallet: {payee_wallet.uuid}") logger.debug(f"Payee account: {payee_account.uuid}")
payer_wallet, _ = Wallet.objects.get_or_create(owner_uuid=payer_id, owner_type=payer_type, payer_account, _ = Account.objects.get_or_create(owner_uuid=payer_id, owner_type=payer_type,
category=payer_wallet_category) category=payer_wallet)
logger.debug(f"Payer wallet: {payer_wallet.uuid}") logger.debug(f"Payer account: {payer_account.uuid}")
uuid = serializer.validated_data.pop('uuid', None) uuid = serializer.validated_data.pop('uuid', None)
@ -355,8 +350,8 @@ class WithdrawSubmitAPIView(generics.CreateAPIView):
logger.debug(f"Transaction UUID: {uuid}, Reference ID: {reference_id}") logger.debug(f"Transaction UUID: {uuid}, Reference ID: {reference_id}")
transaction_params = { transaction_params = {
'application': application, 'application': application,
'payer_wallet': payer_wallet, 'payer_account': payer_account,
'payee_wallet': payee_wallet, 'payee_account': payee_account,
'amount': serializer.validated_data.get('amount'), 'amount': serializer.validated_data.get('amount'),
} }
if uuid: if uuid:
@ -398,8 +393,8 @@ class WithdrawVerifyAPIView(generics.RetrieveAPIView):
category = get_object_or_404(Category.objects.all(), uuid=category_uuid) category = get_object_or_404(Category.objects.all(), uuid=category_uuid)
else: else:
category = None category = None
wallet = Wallet.objects.get(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION, category=category) payee_account = Account.objects.get(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION, category=category)
return Transaction.objects.filter(application=application, payee_wallet=wallet).all() return Transaction.objects.filter(application=application, payee_wallet=payee_account).all()
def retrieve(self, request, *args, **kwargs): def retrieve(self, request, *args, **kwargs):
instance = self.get_object() instance = self.get_object()

View file

@ -13,9 +13,9 @@ from rest_framework.permissions import AllowAny
from rest_framework.response import Response from rest_framework.response import Response
from apps.gooyal_oauth2.rest_framework import IsAuthenticatedOrTokenMatchesOASRequirements from apps.gooyal_oauth2.rest_framework import IsAuthenticatedOrTokenMatchesOASRequirements
from apps.wallet.models import Transaction, Category, Wallet from apps.wallet.models import Transaction, Category, Account
from apps.wallet.serializers import TransactionSerializer, DepositSerializer, WithdrawSerializer, \ from apps.wallet.serializers import TransactionSerializer, DepositSerializer, WithdrawSerializer, \
BalanceSerializer AccountSerializer
from ..constans import StateChoices, TypeChoices from ..constans import StateChoices, TypeChoices
from ..filters import UserTransactionFilter from ..filters import UserTransactionFilter
from ..forms import TransactionActionForm from ..forms import TransactionActionForm
@ -31,12 +31,12 @@ def get_application(request):
return application return application
class MyWalletBalanceView(generics.RetrieveAPIView): class UserAccountBalanceView(generics.RetrieveAPIView):
''' '''
API for getting authenticated user balance. API for getting authenticated user balance.
''' '''
permission_classes = [IsAuthenticatedOrTokenHasScope] permission_classes = [IsAuthenticatedOrTokenHasScope]
serializer_class = BalanceSerializer serializer_class = AccountSerializer
required_scopes = ['wallet.wallet:get_balance'] required_scopes = ['wallet.wallet:get_balance']
def get_object(self): def get_object(self):
@ -227,12 +227,12 @@ class TransactionDetailView(UpdateView):
class CategoryMyWalletBalanceView(generics.RetrieveAPIView): class CategoryMyAccountBalanceView(generics.RetrieveAPIView):
''' '''
API for getting authenticated user balance. API for getting authenticated user balance.
''' '''
permission_classes = [IsAuthenticatedOrTokenHasScope] permission_classes = [IsAuthenticatedOrTokenHasScope]
serializer_class = BalanceSerializer serializer_class = AccountSerializer
required_scopes = ['wallet.wallet:get_balance'] required_scopes = ['wallet.wallet:get_balance']
def get_object(self): def get_object(self):
@ -243,7 +243,7 @@ class CategoryMyWalletBalanceView(generics.RetrieveAPIView):
else: else:
category = None category = None
wallet, created = Wallet.objects.get_or_create(owner_uuid=user.uuid, owner_type=TypeChoices.USER, wallet, created = Account.objects.get_or_create(owner_uuid=user.uuid, owner_type=TypeChoices.USER,
category=category) category=category)
return wallet return wallet
@ -301,7 +301,7 @@ class CategoryTransactionListView(generics.ListAPIView):
else: else:
category = None category = None
wallet, created = Wallet.objects.get_or_create(owner_uuid=user.uuid, owner_type=TypeChoices.USER, wallet, created = Account.objects.get_or_create(owner_uuid=user.uuid, owner_type=TypeChoices.USER,
category=category) category=category)
return Transaction.objects.filter(Q(payee_wallet=wallet) | return Transaction.objects.filter(Q(payee_wallet=wallet) |