Files

105 lines
2.9 KiB
Python

"""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")