Files
ss-tools/.ai/shots/critical_module.py
2026-03-11 09:08:32 +03:00

87 lines
4.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# [DEF:TransactionCore:Module]
# @TIER: CRITICAL
# @SEMANTICS: Finance, ACID, Transfer, Ledger
# @PURPOSE: Core banking transaction processor with ACID guarantees.
# @LAYER: Domain (Core)
# @RELATION: DEPENDS_ON -> [DEF:Infra:PostgresDB]
#
# @INVARIANT: Total system balance must remain constant (Double-Entry Bookkeeping).
# @INVARIANT: Negative transfers are strictly forbidden.
# --- Test Specifications ---
# @TEST_CONTRACT: TransferRequestDTO -> TransferResultDTO
# @TEST_SCENARIO: sufficient_funds -> Returns COMPLETED, balances updated.
# @TEST_FIXTURE: sufficient_funds -> file:./__tests__/fixtures/transfers.json#happy_path
# @TEST_EDGE: insufficient_funds -> Throws BusinessRuleViolation("INSUFFICIENT_FUNDS").
# @TEST_EDGE: negative_amount -> Throws BusinessRuleViolation("Transfer amount must be positive.").
# @TEST_EDGE: concurrency_conflict -> Throws DBTransactionError.
#
# @TEST_INVARIANT: total_balance_constant -> VERIFIED_BY: [sufficient_funds, concurrency_conflict]
# @TEST_INVARIANT: negative_transfer_forbidden -> VERIFIED_BY: [negative_amount]
from decimal import Decimal
from typing import NamedTuple
# GRACE: Импорт глобального логгера с семантическими методами
from ...core.logger import logger, belief_scope
from ...core.db import atomic_transaction, get_balance, update_balance
from ...core.audit import log_audit_trail
from ...core.exceptions import BusinessRuleViolation
class TransferResult(NamedTuple):
tx_id: str
status: str
new_balance: Decimal
# [DEF:execute_transfer:Function]
# @PURPOSE: Atomically move funds between accounts with audit trails.
# @DATA_CONTRACT: Input -> (sender_id: str, receiver_id: str, amount: Decimal), Output -> TransferResult
# @PRE: amount > 0; sender != receiver; sender_balance >= amount.
# @POST: sender_balance -= amount; receiver_balance += amount; Audit Record Created.
# @SIDE_EFFECT: Database mutation (Rows locked), Audit IO.
#
# @UX_STATE: Success -> Returns 200 OK + Transaction Receipt.
# @UX_STATE: Error(LowBalance) -> 422 Unprocessable -> UI shows "Top-up needed" modal.
def execute_transfer(sender_id: str, receiver_id: str, amount: Decimal) -> TransferResult:
# Guard: Input Validation (Вне belief_scope, так как это trivial проверка)
if amount <= Decimal("0.00"):
raise BusinessRuleViolation("Transfer amount must be positive.")
if sender_id == receiver_id:
raise BusinessRuleViolation("Cannot transfer to self.")
# GRACE: Используем strict Context Manager без 'as context'
with belief_scope("execute_transfer"):
# GRACE: [REASON] - Жесткая дедукция, начало алгоритма
logger.reason("Initiating transfer", extra={"from": sender_id, "to": receiver_id, "amount": amount})
try:
# @RELATION: CALLS -> atomic_transaction
with atomic_transaction():
current_balance = get_balance(sender_id, for_update=True)
if current_balance < amount:
# GRACE: [EXPLORE] - Отклонение от Happy Path (фолбэк/ошибка)
logger.explore("Insufficient funds validation hit", extra={"balance": current_balance})
raise BusinessRuleViolation("INSUFFICIENT_FUNDS")
# Mutation
new_src_bal = update_balance(sender_id, -amount)
new_dst_bal = update_balance(receiver_id, +amount)
# Audit
tx_id = log_audit_trail("TRANSFER", sender_id, receiver_id, amount)
# GRACE:[REFLECT] - Сверка с @POST перед возвратом
logger.reflect("Transfer committed successfully", extra={"tx_id": tx_id, "new_balance": new_src_bal})
return TransferResult(tx_id, "COMPLETED", new_src_bal)
except BusinessRuleViolation as e:
# Explicit re-raise for UI mapping
raise e
except Exception as e:
# GRACE: [EXPLORE] - Неожиданный сбой
logger.explore("Critical Transfer Failure", exc_info=e)
raise RuntimeError("TRANSACTION_ABORTED") from e
#[/DEF:execute_transfer:Function]
# [/DEF:TransactionCore:Module]