new change state
This commit is contained in:
parent
dae2a2e518
commit
bd3438528d
1 changed files with 39 additions and 25 deletions
|
|
@ -5,6 +5,7 @@ from django.core.validators import MinValueValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import F
|
from django.db.models import F
|
||||||
|
from django.utils import timezone
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from rest_framework.exceptions import APIException
|
from rest_framework.exceptions import APIException
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
@ -20,6 +21,7 @@ from ..gooyal_oauth2.models import Application
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class CurrencyChoices(models.TextChoices):
|
class CurrencyChoices(models.TextChoices):
|
||||||
IRANIAN_RIAL = '166ab7ce33b34799950d45035a9997d6', _('IRANIAN RIAL') # uuid without dash
|
IRANIAN_RIAL = '166ab7ce33b34799950d45035a9997d6', _('IRANIAN RIAL') # uuid without dash
|
||||||
|
|
||||||
|
|
@ -27,6 +29,7 @@ class CurrencyChoices(models.TextChoices):
|
||||||
class AccountQuerySet(models.QuerySet):
|
class AccountQuerySet(models.QuerySet):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Account(BaseModel):
|
class Account(BaseModel):
|
||||||
owner_uuid = models.UUIDField(db_index=True, null=False)
|
owner_uuid = models.UUIDField(db_index=True, null=False)
|
||||||
wallet = models.ForeignKey('Wallet', on_delete=models.PROTECT, null=True)
|
wallet = models.ForeignKey('Wallet', on_delete=models.PROTECT, null=True)
|
||||||
|
|
@ -50,6 +53,7 @@ class Account(BaseModel):
|
||||||
class WalletQuerySet(models.QuerySet):
|
class WalletQuerySet(models.QuerySet):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Wallet(BaseModel):
|
class Wallet(BaseModel):
|
||||||
application = models.ForeignKey(oauth2_settings.APPLICATION_MODEL, on_delete=models.PROTECT, null=True)
|
application = models.ForeignKey(oauth2_settings.APPLICATION_MODEL, on_delete=models.PROTECT, null=True)
|
||||||
label = models.CharField(_('label'), max_length=255)
|
label = models.CharField(_('label'), max_length=255)
|
||||||
|
|
@ -153,18 +157,26 @@ class Transaction(BaseModel):
|
||||||
def payee_type(self):
|
def payee_type(self):
|
||||||
return self.payee_account.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_states: list | tuple, to_state, same_ok=False, raise_exception=True,
|
||||||
if self.state == from_state:
|
error_message=None,
|
||||||
updated = Transaction.objects.filter(pk=self.pk, state=from_state).update(
|
data: None | dict = None):
|
||||||
|
data = data or {}
|
||||||
|
data['updated_at'] = timezone.now()
|
||||||
|
|
||||||
|
if same_ok and self.state == to_state:
|
||||||
|
return self
|
||||||
|
|
||||||
|
if self.state in from_states:
|
||||||
|
updated = self.__class__.objects.filter(pk=self.pk, state__in=from_states).update(
|
||||||
state=to_state,
|
state=to_state,
|
||||||
**data
|
**data
|
||||||
)
|
)
|
||||||
if updated: # double check in db level
|
if updated: # double check in db level
|
||||||
self.state = to_state
|
self.state = to_state
|
||||||
else:
|
return self
|
||||||
raise APIException(error_message)
|
|
||||||
else:
|
if raise_exception:
|
||||||
raise APIException(error_message)
|
raise Exception(error_message)
|
||||||
|
|
||||||
def withdraw_from_payer_balance(self):
|
def withdraw_from_payer_balance(self):
|
||||||
logger.debug('start withdraw_from_payer_balance')
|
logger.debug('start withdraw_from_payer_balance')
|
||||||
|
|
@ -186,14 +198,12 @@ class Transaction(BaseModel):
|
||||||
else:
|
else:
|
||||||
raise UnprocessableEntity('not enough balance')
|
raise UnprocessableEntity('not enough balance')
|
||||||
|
|
||||||
|
|
||||||
def rollback_withdraw_to_payer_balance(self):
|
def rollback_withdraw_to_payer_balance(self):
|
||||||
logger.debug('start rollback deposit_to_payee_balance')
|
logger.debug('start rollback deposit_to_payee_balance')
|
||||||
logger.info(f'payer_account balance {self.payer_account} -> {self.amount}')
|
logger.info(f'payer_account balance {self.payer_account} -> {self.amount}')
|
||||||
self.payer_account.balance = F('balance') + self.amount
|
self.payer_account.balance = F('balance') + self.amount
|
||||||
self.payer_account.save(update_fields=['balance'])
|
self.payer_account.save(update_fields=['balance'])
|
||||||
|
|
||||||
|
|
||||||
def deposit_to_payee_balance(self):
|
def deposit_to_payee_balance(self):
|
||||||
logger.debug('start deposit_to_payee_balance')
|
logger.debug('start deposit_to_payee_balance')
|
||||||
logger.info(f'payee_account balance {self.payee_account} -> {self.payer_account.balance} + {self.amount}')
|
logger.info(f'payee_account balance {self.payee_account} -> {self.payer_account.balance} + {self.amount}')
|
||||||
|
|
@ -223,20 +233,24 @@ class Transaction(BaseModel):
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
if self.delay:
|
if self.delay:
|
||||||
self.change_state(
|
self.change_state(
|
||||||
StateChoices.CREATED,
|
[StateChoices.CREATED],
|
||||||
StateChoices.DELAYED,
|
StateChoices.DELAYED,
|
||||||
error_message='this transaction is not ready to pay',
|
error_message='this transaction is not ready to pay',
|
||||||
payer_id=payer_id,
|
data={
|
||||||
payer_type=payer_type
|
"payer_id": payer_id,
|
||||||
|
"payer_type": payer_type
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.change_state(
|
self.change_state(
|
||||||
StateChoices.CREATED,
|
[StateChoices.CREATED],
|
||||||
StateChoices.SUCCESS,
|
StateChoices.SUCCESS,
|
||||||
error_message='this transaction is not ready to pay',
|
error_message='this transaction is not ready to pay',
|
||||||
payer_id=payer_id,
|
data={
|
||||||
payer_type=payer_type
|
"payer_id": payer_id,
|
||||||
|
"payer_type": payer_type
|
||||||
|
}
|
||||||
)
|
)
|
||||||
self.deposit_to_payee_balance()
|
self.deposit_to_payee_balance()
|
||||||
|
|
||||||
|
|
@ -250,7 +264,7 @@ class Transaction(BaseModel):
|
||||||
|
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
self.change_state(
|
self.change_state(
|
||||||
StateChoices.DELAYED,
|
[StateChoices.DELAYED],
|
||||||
StateChoices.SUCCESS,
|
StateChoices.SUCCESS,
|
||||||
error_message='this transaction is not delayed'
|
error_message='this transaction is not delayed'
|
||||||
)
|
)
|
||||||
|
|
@ -265,7 +279,7 @@ class Transaction(BaseModel):
|
||||||
raise APIException('payee identification error')
|
raise APIException('payee identification error')
|
||||||
|
|
||||||
if not self.payer_account.wallet.currency == self.payee_account.wallet.currency:
|
if not self.payer_account.wallet.currency == self.payee_account.wallet.currency:
|
||||||
self.change_state(from_state=self.state, to_state=StateChoices.FAILED)
|
self.change_state([self.state], StateChoices.FAILED)
|
||||||
logger.info(f'payer({self.payer_account.wallet.currency}) and payee({self.payee_account.wallet.currency}) account currency not equal')
|
logger.info(f'payer({self.payer_account.wallet.currency}) and payee({self.payee_account.wallet.currency}) account currency not equal')
|
||||||
raise APIException('payer and payee account currency not equal')
|
raise APIException('payer and payee account currency not equal')
|
||||||
|
|
||||||
|
|
@ -276,7 +290,7 @@ class Transaction(BaseModel):
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
self.withdraw_from_payer_balance()
|
self.withdraw_from_payer_balance()
|
||||||
self.change_state(
|
self.change_state(
|
||||||
StateChoices.CREATED,
|
[StateChoices.CREATED],
|
||||||
StateChoices.PENDING,
|
StateChoices.PENDING,
|
||||||
error_message=_('this transaction is not ready to pay')
|
error_message=_('this transaction is not ready to pay')
|
||||||
)
|
)
|
||||||
|
|
@ -289,7 +303,7 @@ class Transaction(BaseModel):
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
self.deposit_to_payee_balance()
|
self.deposit_to_payee_balance()
|
||||||
self.change_state(
|
self.change_state(
|
||||||
StateChoices.PENDING,
|
[StateChoices.PENDING],
|
||||||
StateChoices.SUCCESS,
|
StateChoices.SUCCESS,
|
||||||
error_message=_('this transaction is not pending')
|
error_message=_('this transaction is not pending')
|
||||||
)
|
)
|
||||||
|
|
@ -302,7 +316,7 @@ class Transaction(BaseModel):
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
self.rollback_withdraw_to_payer_balance()
|
self.rollback_withdraw_to_payer_balance()
|
||||||
self.change_state(
|
self.change_state(
|
||||||
self.state,
|
[StateChoices.PENDING, StateChoices.DELAYED, StateChoices.INCOMPLETE],
|
||||||
StateChoices.SUCCESS,
|
StateChoices.SUCCESS,
|
||||||
error_message=_('TRANSACTION_STATE_NOT_SUITABLE_FOR_ROLLBACK')
|
error_message=_('TRANSACTION_STATE_NOT_SUITABLE_FOR_ROLLBACK')
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue