some cleanups

This commit is contained in:
Sayyid Hamid Mahdavi 2025-10-23 17:25:15 +03:30
parent 413a5497e4
commit c925100fd6
10 changed files with 269 additions and 68 deletions

View file

@ -0,0 +1,17 @@
# Generated by Django 5.1.7 on 2025-10-23 09:31
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('gooyal_oauth2', '0006_accesstoken_token_checksum_refreshtoken_token_family_and_more'),
]
operations = [
migrations.RemoveField(
model_name='application',
name='balance',
),
]

View file

@ -21,7 +21,7 @@ class OAUTHLoginRequestForm(forms.ModelForm):
fields = ['agreement'] fields = ['agreement']
class AccountUpdateForm(forms.ModelForm): # class AccountUpdateForm(forms.ModelForm):
class Meta: # class Meta:
model = UserModel # model = UserModel
fields = ['iban'] # fields = ['iban']

View file

@ -0,0 +1,25 @@
# Generated by Django 5.1.7 on 2025-10-23 09:31
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('users', '0006_alter_user_balance'),
]
operations = [
migrations.RemoveField(
model_name='user',
name='balance',
),
migrations.RemoveField(
model_name='user',
name='iban',
),
migrations.RemoveField(
model_name='user',
name='iban_verified',
),
]

View file

@ -13,7 +13,8 @@ from rest_framework import generics
from apps.gooyal_oauth2.rest_framework import IsAuthenticatedOrTokenMatchesOASRequirements from apps.gooyal_oauth2.rest_framework import IsAuthenticatedOrTokenMatchesOASRequirements
from apps.gooyal_oauth2.validators import OAuth2Validator from apps.gooyal_oauth2.validators import OAuth2Validator
from apps.users.forms import OAUTHLoginRequestForm, AccountUpdateForm from apps.users.forms import OAUTHLoginRequestForm
# from apps.users.forms import AccountUpdateForm
from apps.users.models import User, OAuthCode from apps.users.models import User, OAuthCode
UserModel = get_user_model() UserModel = get_user_model()
@ -95,7 +96,7 @@ class AccountDetailView(DetailView):
class AccountUpdateView(UpdateView): class AccountUpdateView(UpdateView):
model = User model = User
template_name = 'users/account_update.html' template_name = 'users/account_update.html'
form_class = AccountUpdateForm # form_class = AccountUpdateForm
def get_success_url(self): def get_success_url(self):
messages.success(self.request, 'عملیات انجام شد.') messages.success(self.request, 'عملیات انجام شد.')

View file

@ -1,15 +1,46 @@
import django_filters import django_filters
from django.db import models
from django.db.models import Q from django.db.models import Q
from .constans import TypeChoices
from .models import Transaction, Wallet
from .models import Transaction
class CategoryUUIDFilter(django_filters.BaseInFilter, django_filters.UUIDFilter):
pass
class UserTransactionCategoryFilter(django_filters.BaseInFilter, django_filters.UUIDFilter):
def filter(self, queryset, value):
request = self.parent.request
user = request.user
return queryset.filter(
Q(payer_wallet__owner_uuid=user.pk,
payer_wallet__owner_type=TypeChoices.USER,
payer_wallet__category_id__in=value,
) |
Q(payee_wallet__owner_uuid=user.pk,
payee_wallet__owner_type=TypeChoices.USER,
payee_wallet__category_id__in=value,
))
class UserTransactionFilter(django_filters.FilterSet): class UserTransactionFilter(django_filters.FilterSet):
payer_id = django_filters.UUIDFilter(field_name='payer_wallet__owner_uuid', lookup_expr='iexact') payer = django_filters.UUIDFilter(field_name='payer_wallet__owner_uuid', lookup_expr='iexact')
payee_id = django_filters.UUIDFilter(field_name='payee_wallet__owner_uuid', lookup_expr='iexact') payee = django_filters.UUIDFilter(field_name='payee_wallet__owner_uuid', lookup_expr='iexact')
wallets = UserTransactionCategoryFilter(field_name='payee_wallet__category', required=True, lookup_expr='in')
class Meta: class Meta:
model = Transaction model = Transaction
fields = { fields = {
} }
class WalletFilter(django_filters.FilterSet):
wallets = CategoryUUIDFilter(field_name='category', required=True, lookup_expr='in')
class Meta:
model = Wallet
fields = {
}

View file

@ -0,0 +1,29 @@
# Generated by Django 5.1.7 on 2025-10-23 09:31
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('wallet', '0011_category_currency'),
]
operations = [
migrations.RemoveField(
model_name='transaction',
name='_payee_id',
),
migrations.RemoveField(
model_name='transaction',
name='_payee_type',
),
migrations.RemoveField(
model_name='transaction',
name='_payer_id',
),
migrations.RemoveField(
model_name='transaction',
name='_payer_type',
),
]

View file

@ -160,6 +160,10 @@ class TransferVerifySerializer(serializers.ModelSerializer):
#### Application #### Application
class WalletSerializer(serializers.ModelSerializer):
class Meta:
model = Wallet
fields = ['balance', 'category']
class BalanceSerializer(serializers.ModelSerializer): class BalanceSerializer(serializers.ModelSerializer):
class Meta: class Meta:

View file

@ -8,37 +8,23 @@ 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.CategoryMyWalletBalanceView.as_view(), name='user_wallet_balance_api'),
path('api/transactions/', user_views.CategoryTransactionListView.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.CategoryMyWalletBalanceView.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'),
# path('api/transactions/<uuid>', TransactionDetail.as_view(), name='transaction_detail_api'),
# path('api/transactions/<uuid>/pay', TransactionPay.as_view(), name='transaction_pay_api'),
# path('api/transactions/<uuid>/receipt', TransactionReceipt.as_view(), name='transaction_receipt_api'),
# #
# path('api/deposit/', DepositSubmit.as_view(), name='deposit_submit_api'),
# path('api/deposit/<uuid>/verify', DepositVerify.as_view(), name='deposit_verify_api'),
# #
# path('api/withdraw/', WithdrawSubmit.as_view(), name='withdraw_submit_api'),
# path('api/withdraw/<uuid>/verify', WithdrawVerify.as_view(), name='withdraw_verify_api'),
# path('api/get_balance/', application_views.WalletBalanceView.as_view(), name='wallet_balance'),
] ]
application_urlpatterns = [ application_urlpatterns = [
path('api/application/balance', application_views.BalanceAPIView.as_view(), name='application_wallet_balance_api'), path('api/application/wallets', application_views.WalletListAPIView.as_view(), name='application_wallet_balance_api'),
path('api/application/user_balance/<uuid>', application_views.UserBalanceAPIView.as_view(), name='application_user_wallet_balance_api'), path('api/application/user/<user_uuid>/wallets/', application_views.UserWalletListAPIView.as_view(), name='application_user_wallet_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/', 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/<category_uuid>/wallet', application_views.BalanceAPIView.as_view(), name='application_wallet_balance_api'),
path('api/application/<category_uuid>/balance', application_views.BalanceAPIView.as_view(), name='application_wallet_balance_api'), # path('api/application/<category_uuid>/user/<user_uuid>/wallet', application_views.UserBalanceAPIView.as_view(), name='application_user_wallet_balance_api'),
path('api/application/<category_uuid>/user/<uuid>/balance', application_views.UserBalanceAPIView.as_view(), name='application_user_wallet_balance_api'),
path('api/application/<category_uuid>/deposit/', application_views.DepositSubmitAPIView.as_view(), name='application_deposit_submit_api'), path('api/application/<category_uuid>/deposit/', application_views.DepositSubmitAPIView.as_view(), name='application_deposit_submit_api'),
path('api/application/<category_uuid>/deposit/<uuid>/verify', application_views.DepositVerifyAPIView.as_view(), name='application_deposit_verify_api'), path('api/application/<category_uuid>/deposit/<uuid>/verify', application_views.DepositVerifyAPIView.as_view(), name='application_deposit_verify_api'),
@ -46,8 +32,8 @@ application_urlpatterns = [
path('api/application/<category_uuid>/withdraw/', application_views.WithdrawSubmitAPIView.as_view(), name='application_withdraw_submit_api'), path('api/application/<category_uuid>/withdraw/', application_views.WithdrawSubmitAPIView.as_view(), name='application_withdraw_submit_api'),
path('api/application/<category_uuid>/withdraw/<uuid>/verify', application_views.WithdrawVerifyAPIView.as_view(), name='application_withdraw_verify_api'), path('api/application/<category_uuid>/withdraw/<uuid>/verify', application_views.WithdrawVerifyAPIView.as_view(), name='application_withdraw_verify_api'),
path('api/application/<category_uuid>/', application_views.TransactionListAPIView.as_view(), name='application_transaction_list_api'), path('api/application/<category_uuid>/', application_views.CategoryTransactionListAPIView.as_view(), name='application_transaction_list_api'),
path('api/application/<category_uuid>/<uuid>/', application_views.TransactionDetailAPIView.as_view(), name='application_transaction_detail'), path('api/application/<category_uuid>/<uuid>/', application_views.CategoryTransactionDetailAPIView.as_view(), name='application_transaction_detail'),
] ]

View file

@ -1,6 +1,7 @@
import logging import logging
from django.db.models import Q from django.db.models import Q
from django_filters.rest_framework import DjangoFilterBackend
from oauth2_provider.contrib.rest_framework import TokenHasScope, IsAuthenticatedOrTokenHasScope from oauth2_provider.contrib.rest_framework import TokenHasScope, IsAuthenticatedOrTokenHasScope
from rest_framework import generics, permissions, mixins from rest_framework import generics, permissions, mixins
from rest_framework.exceptions import APIException from rest_framework.exceptions import APIException
@ -10,9 +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.models import Transaction, Category, Wallet from apps.wallet.models import Transaction, Category, Wallet
from apps.wallet.serializers import ApplicationDepositSerializer, ApplicationWithdrawSerializer, \ from apps.wallet.serializers import ApplicationDepositSerializer, ApplicationWithdrawSerializer, \
TransactionSerializer, BalanceSerializer TransactionSerializer, BalanceSerializer, WalletSerializer
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 _
@ -31,12 +33,14 @@ def get_wallet_category(request: Request):
pass pass
class BalanceAPIView(generics.RetrieveAPIView): class WalletListAPIView(generics.ListAPIView):
''' '''
API for getting specified user or application balance. API for getting specified user or application balance.
''' '''
permission_classes = [TokenHasScope]
serializer_class = BalanceSerializer serializer_class = BalanceSerializer
filter_backends = (DjangoFilterBackend,)
filterset_class = WalletFilter
permission_classes = [IsAuthenticatedOrTokenHasScope]
required_scopes = ['wallet.application:get_balance'] required_scopes = ['wallet.application:get_balance']
def get_object(self): def get_object(self):
@ -51,61 +55,139 @@ class BalanceAPIView(generics.RetrieveAPIView):
category=category) category=category)
return wallet return wallet
def get_queryset(self):
category_uuid = self.kwargs.get('category_uuid')
if category_uuid:
category = get_object_or_404(Category.objects.all(), uuid=category_uuid)
else:
category = None
application = get_application(self.request)
wallet, created = Wallet.objects.get_or_create(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION,
category=category)
return Wallet.objects.filter(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION).all()
class UserBalanceAPIView(generics.RetrieveAPIView):
class UserWalletListAPIView(generics.ListAPIView):
''' '''
API for getting specified user or application balance. API for getting specified user wallet.
''' '''
serializer_class = WalletSerializer
filter_backends = (DjangoFilterBackend,)
filterset_class = WalletFilter
permission_classes = [IsAuthenticatedOrTokenHasScope] permission_classes = [IsAuthenticatedOrTokenHasScope]
serializer_class = BalanceSerializer
required_scopes = ['wallet.application:get_user_balance'] required_scopes = ['wallet.application:get_user_balance']
def get_object(self): queryset = Wallet.objects.all()
def get_queryset(self):
qs = super().get_queryset()
category_uuid = self.kwargs.get('category_uuid')
user_uuid = self.kwargs.get('user_uuid')
if category_uuid:
category = get_object_or_404(Category.objects.all(), uuid=category_uuid)
else:
category = None
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,
category=category)
return qs.filter(owner_uuid=user_uuid, owner_type=TypeChoices.USER).all()
class TransactionDetailAPIView(generics.RetrieveAPIView):
serializer_class = TransactionSerializer
lookup_field = 'uuid'
filter_backends = (DjangoFilterBackend,)
filterset_class = WalletFilter
permission_classes = [TokenHasScope]
required_scopes = ['wallet.application:get_transaction_detail']
queryset = Transaction.objects.all()
def get_queryset(self):
qs = super().get_queryset()
application = get_application(self.request)
return Transaction.objects.filter(Q(application=application) |
Q(payer_wallet__owner_uuid=application.pk,
payer_wallet__owner_type=TypeChoices.APPLICATION,
) |
Q(payee_wallet__owner_uuid=application.pk,
payee_wallet__owner_type=TypeChoices.APPLICATION,
)
).all()
class TransactionListAPIView(generics.ListAPIView):
serializer_class = TransactionSerializer
lookup_field = 'uuid'
filter_backends = (DjangoFilterBackend,)
filterset_class = UserTransactionFilter
permission_classes = [IsAuthenticatedOrTokenHasScope]
required_scopes = ['wallet.application:get_transaction_list']
queryset = Transaction.objects.all()
def get_queryset(self):
qs = super().get_queryset()
application = get_application(self.request)
return qs.filter(Q(application=application) |
Q(payer_wallet__owner_uuid=application.pk,
payer_wallet__owner_type=TypeChoices.APPLICATION) |
Q(payee_wallet__owner_uuid=application.pk,
payee_wallet__owner_type=TypeChoices.APPLICATION,
)
).all()
class CategoryTransactionDetailAPIView(generics.RetrieveAPIView):
permission_classes = [TokenHasScope]
serializer_class = TransactionSerializer
lookup_field = 'uuid'
required_scopes = ['wallet.application:get_transaction_detail']
queryset = Transaction.objects.all()
def get_queryset(self):
qs = super().get_queryset()
category_uuid = self.kwargs.get('category_uuid') category_uuid = self.kwargs.get('category_uuid')
if category_uuid: if category_uuid:
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
user_uuid = self.kwargs.get('uuid')
user, created = User.objects.get_or_create(uuid=user_uuid)
application = get_application(self.request)
wallet, created = Wallet.objects.get_or_create(owner_uuid=user_uuid, owner_type=TypeChoices.USER,
category=category)
return wallet
class TransactionDetailAPIView(generics.RetrieveAPIView):
permission_classes = [TokenHasScope]
serializer_class = TransactionSerializer
lookup_field = 'uuid'
required_scopes = ['wallet.application:get_transaction_detail']
def get_queryset(self):
# TODO: what about category
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_wallet__owner_uuid=application.pk,
payer_wallet__owner_type=TypeChoices.APPLICATION) | payer_wallet__owner_type=TypeChoices.APPLICATION,
payer_wallet__category=category) |
Q(payee_wallet__owner_uuid=application.pk, Q(payee_wallet__owner_uuid=application.pk,
payee_wallet__owner_type=TypeChoices.APPLICATION) payee_wallet__owner_type=TypeChoices.APPLICATION,
payee_wallet__category=category)
).all() ).all()
class TransactionListAPIView(generics.ListAPIView): class CategoryTransactionListAPIView(generics.ListAPIView):
permission_classes = [TokenHasScope] permission_classes = [TokenHasScope]
serializer_class = TransactionSerializer serializer_class = TransactionSerializer
lookup_field = 'uuid' lookup_field = 'uuid'
required_scopes = ['wallet.application:get_transaction_list'] required_scopes = ['wallet.application:get_transaction_list']
queryset = Transaction.objects.all()
def get_queryset(self): def get_queryset(self):
# TODO: what about category? qs = super().get_queryset()
category_uuid = self.kwargs.get('category_uuid')
if category_uuid:
category = get_object_or_404(Category.objects.all(), uuid=category_uuid)
else:
category = None
application = get_application(self.request) application = get_application(self.request)
return Transaction.objects.filter(Q(application=application) | return qs.filter(Q(application=application) |
Q(payer_wallet__owner_uuid=application.pk, Q(payer_wallet__owner_uuid=application.pk,
payer_wallet__owner_type=TypeChoices.APPLICATION) | payer_wallet__owner_type=TypeChoices.APPLICATION,
payer_wallet__category=category) |
Q(payee_wallet__owner_uuid=application.pk, Q(payee_wallet__owner_uuid=application.pk,
payee_wallet__owner_type=TypeChoices.APPLICATION) payee_wallet__owner_type=TypeChoices.APPLICATION,
payee_wallet__category=category)
).all() ).all()

View file

@ -249,6 +249,32 @@ class CategoryMyWalletBalanceView(generics.RetrieveAPIView):
# this is application create invoice view # 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_wallet__owner_uuid=user.uuid) |
Q(payer_wallet__owner_uuid=user.uuid)).all()
class CategoryTransactionListView(generics.ListAPIView): class CategoryTransactionListView(generics.ListAPIView):
''' '''
GET: API for getting list of transactions for authenticated user GET: API for getting list of transactions for authenticated user