"""Часть 07 DBML v5.18 «Консолидация»: консолидация ГК за период, версии, маршрут, блокировка, периметр, документы. Производные консолидации (сводная ОСВ, пары ВГО, шахматка) НЕ хранятся (DECISION-5): считаются запросом из `osv_entry` × `consolidation_member` × `elimination_rule`. Версия после VERIFIED — read-only (ТЗ 6.10). Длинные имена FK на `consolidation_version` заданы явно (по конвенции R-9 > 63 символов PostgreSQL). """ import datetime import uuid from sqlalchemy import Boolean, Date, DateTime, ForeignKey, Index, Integer, SmallInteger, String, Text, text from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import Mapped, mapped_column from .base import Base, UUIDPrimaryMixin from .enums import ( ConsolidationApproach, ConsolidationScenario, ConsolidationStatus, PeriodQuarter, pg_enum, ) class Consolidation(UUIDPrimaryMixin, Base): """Логическая консолидация ГК за период (ТЗ 6.10); версии различаются периметром `consolidation_member`.""" __tablename__ = "consolidation" __table_args__ = (Index("uq_consolidation_group_year_quarter", "company_group_id", "year", "quarter", unique=True),) company_group_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("company_group.id"), nullable=False ) related_group_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("related_group.id")) year: Mapped[int] = mapped_column(SmallInteger, nullable=False) quarter: Mapped[PeriodQuarter] = mapped_column(pg_enum("period_quarter"), nullable=False) reporting_date: Mapped[datetime.date] = mapped_column(Date, nullable=False) created_at: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True)) class ConsolidationVersion(UUIDPrimaryMixin, Base): """Версия консолидации: сценарий, подход, статус, маршрут согласования, решение УОБ (ТЗ 5.1, 6.10).""" __tablename__ = "consolidation_version" __table_args__ = ( Index("uq_consolidation_version_consolidation_id_version_no", "consolidation_id", "version_no", unique=True), ) consolidation_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("consolidation.id"), nullable=False ) version_no: Mapped[int] = mapped_column(Integer, nullable=False) status: Mapped[ConsolidationStatus] = mapped_column(pg_enum("consolidation_status"), nullable=False) scenario: Mapped[ConsolidationScenario] = mapped_column(pg_enum("consolidation_scenario"), nullable=False) approach: Mapped[ConsolidationApproach] = mapped_column( pg_enum("consolidation_approach"), nullable=False, server_default=text("'BASIC'") ) goodwill_enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("false")) initiator_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id"), nullable=False) initiator_office_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("regional_office.id"), nullable=False ) approved_in_rf_by: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id")) approved_in_rf_at: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True)) verified_in_go_by: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id")) verified_in_go_at: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True)) uob_decision_no: Mapped[str | None] = mapped_column(String) uob_decision_date: Mapped[datetime.date | None] = mapped_column(Date) methodology_deviations: Mapped[str | None] = mapped_column(Text) elimination_rules_version: Mapped[int | None] = mapped_column(Integer) is_in_work: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("true")) previous_version_id: Mapped[uuid.UUID | None] = mapped_column( UUID(as_uuid=True), ForeignKey("consolidation_version.id", name="fk_consolidation_version_previous_version_id"), ) cancelled_reason: Mapped[str | None] = mapped_column(Text) created_at: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True)) created_by: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id")) updated_by: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id")) updated_at: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True)) class ConsolidationStatusHistory(UUIDPrimaryMixin, Base): """Маршрут согласования: журнал смен статуса версии (ТЗ 6.10, 8.2).""" __tablename__ = "consolidation_status_history" version_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("consolidation_version.id", name="fk_consolidation_status_history_version_id"), nullable=False, ) from_status: Mapped[ConsolidationStatus | None] = mapped_column(pg_enum("consolidation_status")) to_status: Mapped[ConsolidationStatus] = mapped_column(pg_enum("consolidation_status"), nullable=False) changed_by: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id"), nullable=False) changed_at: Mapped[datetime.datetime] = mapped_column(DateTime(timezone=True), nullable=False) comment: Mapped[str | None] = mapped_column(Text) class ConsolidationLock(UUIDPrimaryMixin, Base): """Взятие версии в работу: ответственный сейчас = активная блокировка (`released_at IS NULL`).""" __tablename__ = "consolidation_lock" __table_args__ = ( # не более одной активной блокировки на версию Index( "uq_consolidation_lock_version_active", "consolidation_version_id", unique=True, postgresql_where=text("released_at IS NULL"), ), ) consolidation_version_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("consolidation_version.id", name="fk_consolidation_lock_consolidation_version_id"), nullable=False, ) locked_by: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id"), nullable=False) locked_at: Mapped[datetime.datetime] = mapped_column(DateTime(timezone=True), nullable=False) released_at: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True)) taken_over_from: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id")) takeover_reason: Mapped[str | None] = mapped_column(Text) class ConsolidationMember(UUIDPrimaryMixin, Base): """Зафиксированный периметр версии: изменение `related_group_member` существующие версии не меняет.""" __tablename__ = "consolidation_member" __table_args__ = ( Index("uq_consolidation_member_version_entity", "consolidation_version_id", "legal_entity_id", unique=True), ) consolidation_version_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("consolidation_version.id", name="fk_consolidation_member_consolidation_version_id"), nullable=False, ) legal_entity_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("legal_entity.id"), nullable=False ) is_head_entity: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("false")) class ConsolidationDocument(UUIDPrimaryMixin, Base): """Явная привязка логических документов к версии (Прил. 2 ТЗ); чек-лист = required_document_rule × периметр.""" __tablename__ = "consolidation_document" __table_args__ = ( Index("uq_consolidation_document_version_document", "consolidation_version_id", "document_id", unique=True), ) consolidation_version_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("consolidation_version.id", name="fk_consolidation_document_consolidation_version_id"), nullable=False, ) document_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("document.id"), nullable=False)