TorbatYar/backend/services/accounting/app/models/reporting.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

125 lines
5.9 KiB
Python

"""Phase 5.10 — Financial Reporting & BI domain models."""
from __future__ import annotations
import uuid
from datetime import date, datetime
from decimal import Decimal
from sqlalchemy import Boolean, Date, DateTime, 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, ReportExportFormat, ReportStatus, ReportType
class FinancialReport(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
__tablename__ = "financial_reports"
report_type: Mapped[ReportType] = mapped_column(nullable=False)
name: Mapped[str] = mapped_column(String(255), nullable=False)
fiscal_period_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
fiscal_year_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
generated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
status: Mapped[ReportStatus] = mapped_column(default=ReportStatus.GENERATED, nullable=False)
report_data: Mapped[str | None] = mapped_column(Text, nullable=True)
generated_by: Mapped[str | None] = mapped_column(String(100), nullable=True)
__table_args__ = (
Index("ix_financial_reports_tenant_type", "tenant_id", "report_type"),
)
class ReportTemplate(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
__tablename__ = "report_templates"
name: Mapped[str] = mapped_column(String(255), nullable=False)
report_type: Mapped[ReportType] = mapped_column(nullable=False)
template_config: Mapped[str] = mapped_column(Text, nullable=False)
is_system: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
__table_args__ = (UniqueConstraint("tenant_id", "name", name="uq_report_template_tenant_name"),)
class Dashboard(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
__tablename__ = "dashboards"
name: Mapped[str] = mapped_column(String(255), nullable=False)
description: Mapped[str | None] = mapped_column(Text, nullable=True)
dashboard_type: Mapped[str] = mapped_column(String(50), nullable=False)
layout_config: Mapped[str | None] = mapped_column(Text, nullable=True)
is_default: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
__table_args__ = (UniqueConstraint("tenant_id", "name", name="uq_dashboard_tenant_name"),)
class DashboardWidget(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
__tablename__ = "dashboard_widgets"
dashboard_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
widget_type: Mapped[str] = mapped_column(String(50), nullable=False)
title: Mapped[str] = mapped_column(String(255), nullable=False)
config: Mapped[str | None] = mapped_column(Text, nullable=True)
position: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
class KPI(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
__tablename__ = "kpis"
code: Mapped[str] = mapped_column(String(50), nullable=False)
name: Mapped[str] = mapped_column(String(255), nullable=False)
kpi_type: Mapped[str] = mapped_column(String(50), nullable=False)
current_value: Mapped[Decimal | None] = mapped_column(Numeric(18, 4), nullable=True)
target_value: Mapped[Decimal | None] = mapped_column(Numeric(18, 4), nullable=True)
calculated_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
__table_args__ = (UniqueConstraint("tenant_id", "code", name="uq_kpi_tenant_code"),)
class ReportSnapshot(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
__tablename__ = "report_snapshots"
report_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
snapshot_date: Mapped[date] = mapped_column(Date, nullable=False)
snapshot_data: Mapped[str] = mapped_column(Text, nullable=False)
class ScheduledReport(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
__tablename__ = "scheduled_reports"
template_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
report_type: Mapped[ReportType] = mapped_column(nullable=False)
schedule_cron: Mapped[str] = mapped_column(String(100), nullable=False)
recipients: Mapped[str | None] = mapped_column(Text, nullable=True)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
last_run_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
class ReportExport(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
__tablename__ = "report_exports"
report_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
export_format: Mapped[ReportExportFormat] = mapped_column(nullable=False)
file_path: Mapped[str | None] = mapped_column(String(500), nullable=True)
exported_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
exported_by: Mapped[str | None] = mapped_column(String(100), nullable=True)
class ReportHistory(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
__tablename__ = "report_history"
report_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
action: Mapped[str] = mapped_column(String(50), nullable=False)
actor_user_id: Mapped[str] = mapped_column(String(100), nullable=False)
class ReportConfiguration(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
__tablename__ = "report_configurations"
key: Mapped[str] = mapped_column(String(100), nullable=False)
value: Mapped[str | None] = mapped_column(Text, nullable=True)
__table_args__ = (UniqueConstraint("tenant_id", "key", name="uq_report_config_tenant_key"),)