"""Phase 5.3 — General Ledger & fiscal management models.""" from __future__ import annotations import uuid from datetime import date from decimal import Decimal from sqlalchemy import ( Boolean, Date, ForeignKey, Index, Integer, Numeric, String, Text, UniqueConstraint, ) from sqlalchemy.orm import Mapped, mapped_column from app.core.database import Base from app.models.base import TenantMixin, TimestampMixin, UUIDPrimaryKeyMixin from app.models.types import GUID class GeneralLedger(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin): __tablename__ = "general_ledgers" account_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) fiscal_period_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) journal_entry_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) voucher_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) entry_date: Mapped[date] = mapped_column(Date, nullable=False) debit: Mapped[Decimal] = mapped_column(Numeric(18, 4), default=Decimal("0"), nullable=False) credit: Mapped[Decimal] = mapped_column(Numeric(18, 4), default=Decimal("0"), nullable=False) running_balance: Mapped[Decimal] = mapped_column( Numeric(18, 4), default=Decimal("0"), nullable=False ) description: Mapped[str | None] = mapped_column(Text, nullable=True) cost_center_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True) project_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True) __table_args__ = ( Index("ix_gl_tenant_account_period", "tenant_id", "account_id", "fiscal_period_id"), Index("ix_gl_tenant_date", "tenant_id", "entry_date"), ) class LedgerBalance(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin): __tablename__ = "ledger_balances" account_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) fiscal_period_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) opening_balance: Mapped[Decimal] = mapped_column( Numeric(18, 4), default=Decimal("0"), nullable=False ) debit_total: Mapped[Decimal] = mapped_column( Numeric(18, 4), default=Decimal("0"), nullable=False ) credit_total: Mapped[Decimal] = mapped_column( Numeric(18, 4), default=Decimal("0"), nullable=False ) closing_balance: Mapped[Decimal] = mapped_column( Numeric(18, 4), default=Decimal("0"), nullable=False ) current_balance: Mapped[Decimal] = mapped_column( Numeric(18, 4), default=Decimal("0"), nullable=False ) __table_args__ = ( UniqueConstraint( "tenant_id", "account_id", "fiscal_period_id", name="uq_ledger_balance_tenant_account_period", ), ) class TrialBalanceSnapshot(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin): __tablename__ = "trial_balance_snapshots" fiscal_period_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) generated_at: Mapped[date] = mapped_column(Date, nullable=False) total_debit: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False) total_credit: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False) difference: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False) is_balanced: Mapped[bool] = mapped_column(Boolean, nullable=False) snapshot_data: Mapped[str | None] = mapped_column(Text, nullable=True) __table_args__ = ( Index("ix_trial_balance_tenant_period", "tenant_id", "fiscal_period_id"), ) class BalanceSnapshot(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin): __tablename__ = "balance_snapshots" account_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) fiscal_period_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) snapshot_date: Mapped[date] = mapped_column(Date, nullable=False) balance: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False) snapshot_type: Mapped[str] = mapped_column(String(50), nullable=False) __table_args__ = ( Index("ix_balance_snapshot_tenant_account", "tenant_id", "account_id"), ) class ClosingEntry(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin): __tablename__ = "closing_entries" fiscal_period_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) voucher_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) entry_type: Mapped[str] = mapped_column(String(50), nullable=False) description: Mapped[str | None] = mapped_column(Text, nullable=True) __table_args__ = ( Index("ix_closing_entries_period", "tenant_id", "fiscal_period_id"), ) class OpeningEntry(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin): __tablename__ = "opening_entries" fiscal_year_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) fiscal_period_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) voucher_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) account_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False) amount: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False) is_debit: Mapped[bool] = mapped_column(Boolean, nullable=False) __table_args__ = ( Index("ix_opening_entries_year", "tenant_id", "fiscal_year_id"), ) class AccountingCalendar(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin): __tablename__ = "accounting_calendars" name: Mapped[str] = mapped_column(String(100), nullable=False) fiscal_year_start_month: Mapped[int] = mapped_column(Integer, default=1, nullable=False) periods_per_year: Mapped[int] = mapped_column(Integer, default=12, nullable=False) is_default: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) __table_args__ = ( UniqueConstraint("tenant_id", "name", name="uq_accounting_calendar_tenant_name"), )