TorbatYar/backend/services/loyalty/app/validators/wallet.py
Mortezakoohjani 071c484530 Ship Loyalty phases 7.2-7.6 (points through wallet) with production deploy.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-26 21:27:22 +03:30

96 lines
3.2 KiB
Python

"""Wallet Engine validators — Phase 7.6."""
from __future__ import annotations
from shared.exceptions import AppError
from app.models.types import WalletAccountStatus, WalletLedgerEntryType
def validate_wallet_account_status(
status: WalletAccountStatus | str,
) -> WalletAccountStatus:
if isinstance(status, WalletAccountStatus):
return status
try:
return WalletAccountStatus(status)
except ValueError as exc:
raise AppError(
"وضعیت کیف پول نامعتبر است",
status_code=422,
error_code="invalid_wallet_account_status",
) from exc
def ensure_wallet_open(status: WalletAccountStatus) -> None:
if status == WalletAccountStatus.CLOSED:
raise AppError(
"کیف پول بسته است",
status_code=409,
error_code="wallet_account_closed",
)
if status == WalletAccountStatus.FROZEN:
raise AppError(
"کیف پول مسدود است",
status_code=409,
error_code="wallet_account_frozen",
)
def validate_positive_amount(amount: int, *, field: str = "amount") -> int:
if amount is None or amount <= 0:
raise AppError(
f"{field} باید بزرگ‌تر از صفر باشد",
status_code=422,
error_code="invalid_wallet_amount",
details={"field": field},
)
return amount
def validate_adjust_amount(amount: int) -> int:
if amount == 0:
raise AppError(
"مقدار تعدیل نمی‌تواند صفر باشد",
status_code=422,
error_code="invalid_wallet_adjust_amount",
)
return amount
def signed_amount(entry_type: WalletLedgerEntryType, amount: int) -> int:
if entry_type in {WalletLedgerEntryType.CREDIT, WalletLedgerEntryType.TRANSFER_IN}:
return abs(amount)
if entry_type in {WalletLedgerEntryType.DEBIT, WalletLedgerEntryType.TRANSFER_OUT}:
return -abs(amount)
# ADJUST: caller supplies signed amount
return amount
def ensure_sufficient_funds(balance: int, new_balance: int, *, requested: int) -> None:
if new_balance < 0:
raise AppError(
"موجودی کیف پول کافی نیست",
status_code=409,
error_code="wallet_insufficient_funds",
details={"balance": balance, "requested": requested},
)
def ensure_same_currency(source_currency: str, target_currency: str) -> None:
if source_currency != target_currency:
raise AppError(
"انتقال بین ارزهای متفاوت مجاز نیست",
status_code=422,
error_code="wallet_currency_mismatch",
details={"source": source_currency, "target": target_currency},
)
def ensure_not_same_account(source_id, target_id) -> None:
if source_id == target_id:
raise AppError(
"انتقال به همان کیف پول مجاز نیست",
status_code=422,
error_code="wallet_transfer_same_account",
)