Features: 1) Add GatewayManager and GatewayQuerySet to handle advanced gateway filtering and usage checks; 2) Integrate GatewayManager with Gateway model for automatic query filtering; 3) Introduce can_be_used query annotation for gateway availability.
Fixes: 1) Default to a usable `Gateway` when processing new transactions if none is specified. Extra: 1) Add missing import for `GatewayManager` in `payments.models`; 2) Refactor gateway availability logic into the manager and query set for cleaner code organization.
This commit is contained in:
parent
33fbbc049a
commit
3fe3d571bb
3 changed files with 51 additions and 2 deletions
44
payments/managers.py
Normal file
44
payments/managers.py
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
from django.db.models import BooleanField, Case, F, Manager, Q, QuerySet, Sum, Value, When
|
||||||
|
from django.db.models.functions import Coalesce
|
||||||
|
from django.utils.timezone import now
|
||||||
|
|
||||||
|
|
||||||
|
class GatewayQuerySet(QuerySet):
|
||||||
|
def with_usage_sums(self) -> QuerySet:
|
||||||
|
today = now().date()
|
||||||
|
current_month_start = today.replace(day=1)
|
||||||
|
return self.annotate(
|
||||||
|
daily_sum=Coalesce(Sum("transactions__amount", filter=Q(transactions__created__date=today)), Value(0.0)),
|
||||||
|
monthly_sum=Coalesce(
|
||||||
|
Sum("transactions__amount", filter=Q(transactions__created__gte=current_month_start)), Value(0.0)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def can_be_used(self) -> QuerySet:
|
||||||
|
qs = self.with_usage_sums()
|
||||||
|
qs = qs.annotate(
|
||||||
|
daily_ok=Case(
|
||||||
|
When(daily_limit=0, then=Value(True)),
|
||||||
|
When(daily_sum__lt=F("daily_limit"), then=Value(True)),
|
||||||
|
default=Value(False),
|
||||||
|
output_field=BooleanField(),
|
||||||
|
),
|
||||||
|
monthly_ok=Case(
|
||||||
|
When(monthly_limit=0, then=Value(True)),
|
||||||
|
When(monthly_sum__lt=F("monthly_limit"), then=Value(True)),
|
||||||
|
default=Value(False),
|
||||||
|
output_field=BooleanField(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return qs.annotate(
|
||||||
|
can_be_used=Case(
|
||||||
|
When(daily_ok=True, monthly_ok=True, is_active=True, then=Value(True)),
|
||||||
|
default=Value(False),
|
||||||
|
output_field=BooleanField(),
|
||||||
|
)
|
||||||
|
).order_by("-priority")
|
||||||
|
|
||||||
|
|
||||||
|
class GatewayManager(Manager.from_queryset(GatewayQuerySet)):
|
||||||
|
def get_queryset(self) -> QuerySet:
|
||||||
|
return super().get_queryset().can_be_used()
|
||||||
|
|
@ -20,6 +20,7 @@ from django.utils.translation import gettext_lazy as _
|
||||||
from core.abstract import NiceModel
|
from core.abstract import NiceModel
|
||||||
from evibes.utils.misc import create_object
|
from evibes.utils.misc import create_object
|
||||||
from payments.gateways import AbstractGateway
|
from payments.gateways import AbstractGateway
|
||||||
|
from payments.managers import GatewayManager
|
||||||
|
|
||||||
|
|
||||||
class Transaction(NiceModel):
|
class Transaction(NiceModel):
|
||||||
|
|
@ -87,6 +88,7 @@ class Balance(NiceModel):
|
||||||
|
|
||||||
|
|
||||||
class Gateway(NiceModel):
|
class Gateway(NiceModel):
|
||||||
|
objects = GatewayManager()
|
||||||
name = CharField(max_length=20, null=False, blank=False, verbose_name=_("name"))
|
name = CharField(max_length=20, null=False, blank=False, verbose_name=_("name"))
|
||||||
default_currency = CharField(
|
default_currency = CharField(
|
||||||
max_length=4,
|
max_length=4,
|
||||||
|
|
@ -138,6 +140,9 @@ class Gateway(NiceModel):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def can_be_used(self) -> bool:
|
def can_be_used(self) -> bool:
|
||||||
|
if not self.is_active:
|
||||||
|
return False
|
||||||
|
|
||||||
today = now().date()
|
today = now().date()
|
||||||
current_month_start = today.replace(day=1)
|
current_month_start = today.replace(day=1)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from typing import Any
|
||||||
from django.db.models.signals import post_save
|
from django.db.models.signals import post_save
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
||||||
from payments.models import Balance, Transaction
|
from payments.models import Balance, Transaction, Gateway
|
||||||
from payments.utils.emailing import balance_deposit_email
|
from payments.utils.emailing import balance_deposit_email
|
||||||
from vibes_auth.models import User
|
from vibes_auth.models import User
|
||||||
|
|
||||||
|
|
@ -24,7 +24,7 @@ def create_balance_on_user_creation_signal(instance: User, created: bool, **kwar
|
||||||
def process_transaction_changes(instance: Transaction, created: bool, **kwargs: dict[Any, Any]) -> None:
|
def process_transaction_changes(instance: Transaction, created: bool, **kwargs: dict[Any, Any]) -> None:
|
||||||
if created:
|
if created:
|
||||||
if not instance.gateway:
|
if not instance.gateway:
|
||||||
raise ValueError("gateway is required to process a transaction")
|
instance.gateway = Gateway.objects.can_be_used().first()
|
||||||
try:
|
try:
|
||||||
gateway = instance.gateway.get_integration_class_object()
|
gateway = instance.gateway.get_integration_class_object()
|
||||||
gateway.process_transaction(instance)
|
gateway.process_transaction(instance)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue