- consolidation_version.elimination_rules_version NOT NULL: миграция 0010 проставляет существующим версиям номер правил, действовавших на дату создания (по Москве), иначе последних открытых; без правил — отказ; создание версии без действующих правил — 409 ELIMINATION_RULES_MISSING; ветки «правила не зафиксированы» убраны (rules, preset, is_version_fixed всегда true); - GET …/elimination-totals: все тройки «правило ББ × упорядоченная пара периметра» с ΣA, ΣB, расхождением, суммой элиминации, статусом AUTO/DISCREPANCY/ONE_SIDED/MANUAL и osv_entry_ids; сводная ОСВ строится один раз; сбор строк сторон вынесен в _collect_rule_sides — общий с упаковкой тройки. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
159 lines
8.5 KiB
Python
159 lines
8.5 KiB
Python
"""Часть 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] = mapped_column(Integer, nullable=False)
|
||
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)
|