TorbatYar/backend/services/accounting/app/models/ledger.py
Mortezakoohjani 12c8615615 Ship enterprise Accounting FE/API with CRUD parity and production wiring.
Adds accounting-service PATCH/archive, fiscal helpers, COA templates and setup status, plus SuperApp Accounting UI (DS, scoreboard, masters, vouchers, ledger, ops modules) with session refresh and HTTPS public API URLs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-24 15:26:43 +03:30

147 lines
6.0 KiB
Python

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