"""ORM tables of the prepaid credit ledger (v2). Use ``from app.core.database import Base`` and the helpers in ``app.infrastructure.db.columns`` (``pk()``, ``created()``). Do NOT import from ``app.infrastructure.db.models`` here: that module imports this one (import cycle). Foreign keys to existing tables are written as plain strings, e.g. ``ForeignKey("organizations.id", ondelete="CASCADE")``. ``credit_transactions`` is the history of an organization's prepaid wallet (``credit_wallets.balance``). Every change to the balance writes one row here, in the same transaction, with the balance it left behind: * ``owner_credit``, ``starting_credit`` and ``adjustment`` move the balance by a signed amount (credit positive, debit negative); * ``usage`` is the debit for one usage row. ``usage_event_id`` is unique, so one usage row debits the wallet at most once, even when it is written twice. """ from __future__ import annotations from datetime import datetime from sqlalchemy import Float, ForeignKey, Index, String, UniqueConstraint from sqlalchemy.orm import Mapped, mapped_column from app.core.database import Base from app.infrastructure.db.columns import created, pk class CreditTransactionRow(Base): __tablename__ = "credit_transactions" __table_args__ = ( Index("ix_credit_tx_org_created", "organization_id", "created_at"), UniqueConstraint("usage_event_id", name="uq_credit_transactions_usage_event_id"), ) id: Mapped[str] = pk() organization_id: Mapped[str] = mapped_column( ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False, index=True ) #: owner_credit | starting_credit | usage | adjustment (see ``CreditKind``). kind: Mapped[str] = mapped_column(String(16), nullable=False) #: Signed: a credit is positive, a debit is negative. amount: Mapped[float] = mapped_column(Float, nullable=False) balance_after: Mapped[float] = mapped_column(Float, nullable=False) reason: Mapped[str] = mapped_column(String(500), default="", nullable=False) actor_user_id: Mapped[str | None] = mapped_column(String(32), nullable=True) #: The usage row this debit paid for. Unique: the idempotency of a usage debit. usage_event_id: Mapped[str | None] = mapped_column(String(32), nullable=True) created_at: Mapped[datetime] = created()