Files
Code_of_Conquest/api/app/models/transaction.py
2025-11-29 01:16:46 -06:00

182 lines
5.9 KiB
Python

"""
Transaction Model
Tracks all gold transactions for audit and analytics purposes.
Includes shop purchases, sales, quest rewards, trades, etc.
"""
from dataclasses import dataclass, field
from datetime import datetime
from typing import Dict, Any, Optional
from enum import Enum
class TransactionType(Enum):
"""Types of gold transactions."""
SHOP_PURCHASE = "shop_purchase"
SHOP_SALE = "shop_sale"
QUEST_REWARD = "quest_reward"
ENEMY_LOOT = "enemy_loot"
PLAYER_TRADE = "player_trade"
NPC_GIFT = "npc_gift"
SYSTEM_ADJUSTMENT = "system_adjustment"
@dataclass
class Transaction:
"""
Represents a gold transaction for audit logging.
Attributes:
transaction_id: Unique identifier for this transaction
transaction_type: Type of transaction (purchase, sale, reward, etc.)
character_id: Character involved in the transaction
session_id: Game session where transaction occurred (for cleanup on session delete)
amount: Gold amount (negative for expenses, positive for income)
description: Human-readable description of the transaction
timestamp: When the transaction occurred
metadata: Additional context-specific data
"""
transaction_id: str
transaction_type: TransactionType
character_id: str
session_id: Optional[str] = None # Session context for cleanup
amount: int = 0 # Negative for expenses, positive for income
description: str = ""
timestamp: datetime = field(default_factory=datetime.utcnow)
metadata: Dict[str, Any] = field(default_factory=dict)
def to_dict(self) -> Dict[str, Any]:
"""
Serialize transaction to dictionary for storage.
Returns:
Dictionary containing all transaction data
"""
return {
"transaction_id": self.transaction_id,
"transaction_type": self.transaction_type.value,
"character_id": self.character_id,
"session_id": self.session_id,
"amount": self.amount,
"description": self.description,
"timestamp": self.timestamp.isoformat(),
"metadata": self.metadata,
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'Transaction':
"""
Deserialize transaction from dictionary.
Args:
data: Dictionary containing transaction data
Returns:
Transaction instance
"""
return cls(
transaction_id=data["transaction_id"],
transaction_type=TransactionType(data["transaction_type"]),
character_id=data["character_id"],
session_id=data.get("session_id"),
amount=data["amount"],
description=data["description"],
timestamp=datetime.fromisoformat(data["timestamp"]),
metadata=data.get("metadata", {}),
)
@classmethod
def create_purchase(
cls,
transaction_id: str,
character_id: str,
shop_id: str,
item_id: str,
quantity: int,
total_cost: int,
session_id: Optional[str] = None
) -> 'Transaction':
"""
Factory method for creating a shop purchase transaction.
Args:
transaction_id: Unique transaction ID
character_id: Character making the purchase
shop_id: Shop where purchase was made
item_id: Item purchased
quantity: Number of items purchased
total_cost: Total gold spent
session_id: Optional session ID for cleanup tracking
Returns:
Transaction instance for the purchase
"""
return cls(
transaction_id=transaction_id,
transaction_type=TransactionType.SHOP_PURCHASE,
character_id=character_id,
session_id=session_id,
amount=-total_cost, # Negative because spending gold
description=f"Purchased {quantity}x {item_id} from {shop_id}",
metadata={
"shop_id": shop_id,
"item_id": item_id,
"quantity": quantity,
"unit_price": total_cost // quantity if quantity > 0 else 0,
}
)
@classmethod
def create_sale(
cls,
transaction_id: str,
character_id: str,
shop_id: str,
item_id: str,
item_name: str,
quantity: int,
total_earned: int,
session_id: Optional[str] = None
) -> 'Transaction':
"""
Factory method for creating a shop sale transaction.
Args:
transaction_id: Unique transaction ID
character_id: Character making the sale
shop_id: Shop where sale was made
item_id: Item sold
item_name: Display name of the item
quantity: Number of items sold
total_earned: Total gold earned
session_id: Optional session ID for cleanup tracking
Returns:
Transaction instance for the sale
"""
return cls(
transaction_id=transaction_id,
transaction_type=TransactionType.SHOP_SALE,
character_id=character_id,
session_id=session_id,
amount=total_earned, # Positive because receiving gold
description=f"Sold {quantity}x {item_name} to {shop_id}",
metadata={
"shop_id": shop_id,
"item_id": item_id,
"item_name": item_name,
"quantity": quantity,
"unit_price": total_earned // quantity if quantity > 0 else 0,
}
)
def __repr__(self) -> str:
"""String representation of the transaction."""
sign = "+" if self.amount >= 0 else ""
return (
f"Transaction({self.transaction_type.value}, "
f"{sign}{self.amount}g, {self.description})"
)