"""Wallet and Transaction ledger models for Credits virtual currency. docs/planning/economy.md US-1. """ from datetime import datetime, timezone import enum from sqlalchemy import ( BigInteger, CheckConstraint, Column, DateTime, Enum, ForeignKey, Integer, String, func, ) from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.types import JSON from sqlalchemy.orm import relationship from app.database import Base class TransactionType(str, enum.Enum): credit_purchase = "credit_purchase" marketplace_purchase = "marketplace_purchase" marketplace_sale = "marketplace_sale" escrow_release = "escrow_release" payout = "payout" refund = "refund" admin_adjustment = "admin_adjustment" class Wallet(Base): __tablename__ = "wallets" id = Column(BigInteger, primary_key=True, autoincrement=True) user_id = Column( Integer, ForeignKey("users.id", ondelete="CASCADE"), unique=True, nullable=False, index=True, ) balance = Column(BigInteger, nullable=False, default=0, server_default="0") created_at = Column( DateTime(timezone=True), nullable=False, default=lambda: datetime.now(timezone.utc), server_default=func.now(), ) updated_at = Column( DateTime(timezone=True), nullable=False, default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), server_default=func.now(), ) __table_args__ = ( CheckConstraint("balance >= 0", name="check_wallet_balance_non_negative"), ) user = relationship("User", backref="wallet", uselist=False) transactions = relationship( "Transaction", back_populates="wallet", cascade="all, delete-orphan", order_by="Transaction.created_at.desc()", ) class Transaction(Base): __tablename__ = "transactions" id = Column(BigInteger, primary_key=True, autoincrement=True) wallet_id = Column( BigInteger, ForeignKey("wallets.id", ondelete="CASCADE"), nullable=False, index=True, ) type = Column( Enum(TransactionType, name="transaction_type"), nullable=False, ) amount = Column(BigInteger, nullable=False) # signed integer: positive = credit, negative = debit balance_after = Column(BigInteger, nullable=False) reference_type = Column(String, nullable=True) # e.g. "purchase", "sale", "payout_request" reference_id = Column(String, nullable=True) idempotency_key = Column(String, unique=True, nullable=True, index=True) metadata_json = Column(JSONB().with_variant(JSON, "sqlite"), nullable=True) created_at = Column( DateTime(timezone=True), nullable=False, default=lambda: datetime.now(timezone.utc), server_default=func.now(), index=True, ) wallet = relationship("Wallet", back_populates="transactions")