feat: ORM-модели частей 07–09 DBML v5.13 (консолидация, КФО, аудит/ЕХ, required_document_rule)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
842bb584ee
commit
48525731ff
@ -3,7 +3,7 @@
|
||||
Импорт всех модулей моделей обязателен: он регистрирует таблицы в `Base.metadata` (create_all, alembic).
|
||||
"""
|
||||
|
||||
from . import access, accounting, directories, files, osv, statements
|
||||
from . import access, accounting, audit_ex, consolidation, directories, files, kfo, osv, statements
|
||||
from .base import Base, UUIDPrimaryMixin
|
||||
|
||||
#: Версия DBML, которой соответствуют модели (сверяется с шапкой DBML в check_dbml_vs_orm).
|
||||
@ -15,8 +15,11 @@ __all__ = [
|
||||
"UUIDPrimaryMixin",
|
||||
"access",
|
||||
"accounting",
|
||||
"audit_ex",
|
||||
"consolidation",
|
||||
"directories",
|
||||
"files",
|
||||
"kfo",
|
||||
"osv",
|
||||
"statements",
|
||||
]
|
||||
|
||||
156
api/src/models/audit_ex.py
Normal file
156
api/src/models/audit_ex.py
Normal file
@ -0,0 +1,156 @@
|
||||
"""Часть 09 DBML v5.13 «Комментарии, аудит, ЕХФД, прочее»: комментарии, журнал аудита, срезы ЕХ, чек-лист документов.
|
||||
|
||||
`audit_event` — append-only, высокообъёмная (before/after — jsonb). Срезы ЕХ самодостаточны (v5.12): FK на
|
||||
консолидацию и её результаты отсутствуют (их удаляют по сроку, срез хранится «вечно»): `ex_snapshot.source_version_id`
|
||||
— uuid без FK. `required_document_rule` (секция DBML «ПРОЧЕЕ») лежит здесь по рулингу R-4.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import uuid
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import Boolean, Date, DateTime, ForeignKey, Index, Integer, SmallInteger, String, Text
|
||||
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from .base import Base, UUIDPrimaryMixin
|
||||
from .enums import (
|
||||
AuditAction,
|
||||
AuditSection,
|
||||
ConsolidationApproach,
|
||||
DocumentType,
|
||||
ExSnapshotStatus,
|
||||
PeriodQuarter,
|
||||
pg_enum,
|
||||
)
|
||||
|
||||
|
||||
class Comment(UUIDPrimaryMixin, Base):
|
||||
"""Комментарий к объекту (документ, версия, строка КФО и т.п.; ТЗ 6.3/6.4); связь с объектом — полиморфная."""
|
||||
|
||||
__tablename__ = "comment"
|
||||
__table_args__ = (Index("ix_comment_object_type_object_id", "object_type", "object_id"),)
|
||||
|
||||
object_type: Mapped[str] = mapped_column(String, nullable=False)
|
||||
object_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), nullable=False)
|
||||
text: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
author_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id"), nullable=False)
|
||||
created_at: Mapped[datetime.datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
|
||||
|
||||
class AuditEvent(UUIDPrimaryMixin, Base):
|
||||
"""Журнал изменений (ТЗ 8.2). Append-only; `occurred_at` проставляет приложение (default в DBML нет)."""
|
||||
|
||||
__tablename__ = "audit_event"
|
||||
__table_args__ = (
|
||||
Index("ix_audit_event_version_occurred_at", "consolidation_version_id", "occurred_at"),
|
||||
Index("ix_audit_event_object_type_object_id", "object_type", "object_id"),
|
||||
Index("ix_audit_event_user_occurred_at", "user_id", "occurred_at"),
|
||||
)
|
||||
|
||||
occurred_at: Mapped[datetime.datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
user_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id"))
|
||||
action: Mapped[AuditAction] = mapped_column(pg_enum("audit_action"), nullable=False)
|
||||
section: Mapped[AuditSection] = mapped_column(pg_enum("audit_section"), nullable=False)
|
||||
object_type: Mapped[str] = mapped_column(String, nullable=False)
|
||||
object_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True))
|
||||
consolidation_version_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("consolidation_version.id")
|
||||
)
|
||||
description: Mapped[str | None] = mapped_column(Text)
|
||||
before_value: Mapped[Any | None] = mapped_column(JSONB)
|
||||
after_value: Mapped[Any | None] = mapped_column(JSONB)
|
||||
comment: Mapped[str | None] = mapped_column(Text)
|
||||
result: Mapped[str | None] = mapped_column(String)
|
||||
changed_field: Mapped[str | None] = mapped_column(String)
|
||||
ip_address: Mapped[str | None] = mapped_column(String)
|
||||
user_agent: Mapped[str | None] = mapped_column(String)
|
||||
|
||||
|
||||
class ExSnapshot(UUIDPrimaryMixin, Base):
|
||||
"""Срез ЕХ: одна ГСК на одну отчётную дату (ЕХ 6.2). Неизменяем и самодостаточен — без FK на консолидацию."""
|
||||
|
||||
__tablename__ = "ex_snapshot"
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"uq_ex_snapshot_group_year_quarter_version",
|
||||
"related_group_id",
|
||||
"year",
|
||||
"quarter",
|
||||
"version_no",
|
||||
unique=True,
|
||||
),
|
||||
Index("ix_ex_snapshot_group_year_quarter_status", "related_group_id", "year", "quarter", "status"),
|
||||
)
|
||||
|
||||
related_group_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("related_group.id"), nullable=False
|
||||
)
|
||||
company_group_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("company_group.id"), nullable=False
|
||||
)
|
||||
reporting_date: Mapped[datetime.date] = mapped_column(Date, nullable=False)
|
||||
year: Mapped[int] = mapped_column(SmallInteger, nullable=False)
|
||||
quarter: Mapped[PeriodQuarter] = mapped_column(pg_enum("period_quarter"), nullable=False)
|
||||
version_no: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
status: Mapped[ExSnapshotStatus] = mapped_column(pg_enum("ex_snapshot_status"), nullable=False)
|
||||
# id consolidation_version-источника БЕЗ FK (v5.12): источник удаляется по сроку, срез — нет
|
||||
source_version_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), nullable=False, unique=True)
|
||||
source_version_no: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
superseded_by_snapshot_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("ex_snapshot.id")
|
||||
)
|
||||
verified_at: Mapped[datetime.datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
written_at: Mapped[datetime.datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
written_by: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id"))
|
||||
archived_at: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
data_lake_ref: Mapped[str | None] = mapped_column(String)
|
||||
|
||||
|
||||
class ExSnapshotMember(Base):
|
||||
"""Периметр среза на дату (ЕХ 6.3); `inn`/`name` денормализованы; составной PK (ex_snapshot_id, legal_entity_id)."""
|
||||
|
||||
__tablename__ = "ex_snapshot_member"
|
||||
|
||||
ex_snapshot_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("ex_snapshot.id"), primary_key=True
|
||||
)
|
||||
legal_entity_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("legal_entity.id"), primary_key=True
|
||||
)
|
||||
inn: Mapped[str] = mapped_column(String, nullable=False)
|
||||
name: Mapped[str] = mapped_column(String, nullable=False)
|
||||
|
||||
|
||||
class ExSnapshotItem(UUIDPrimaryMixin, Base):
|
||||
"""Состав среза: `primary_document` -> `document` (исходники хранятся весь срок), всё остальное -> `payload`."""
|
||||
|
||||
__tablename__ = "ex_snapshot_item"
|
||||
__table_args__ = (Index("ix_ex_snapshot_item_snapshot_item_type", "ex_snapshot_id", "item_type"),)
|
||||
|
||||
ex_snapshot_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("ex_snapshot.id"), nullable=False)
|
||||
item_type: Mapped[str] = mapped_column(String, nullable=False)
|
||||
legal_entity_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("legal_entity.id"))
|
||||
document_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("document.id"))
|
||||
payload: Mapped[Any | None] = mapped_column(JSONB)
|
||||
payload_schema_version: Mapped[str | None] = mapped_column(String)
|
||||
|
||||
|
||||
class RequiredDocumentRule(UUIDPrimaryMixin, Base):
|
||||
"""Состав обязательных документов по подходу — основа чек-листа (ТЗ 5.1/6.2, сноска 12)."""
|
||||
|
||||
__tablename__ = "required_document_rule"
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"uq_required_document_rule_approach_type_account",
|
||||
"approach",
|
||||
"document_type",
|
||||
"account_id",
|
||||
unique=True,
|
||||
),
|
||||
)
|
||||
|
||||
approach: Mapped[ConsolidationApproach] = mapped_column(pg_enum("consolidation_approach"), nullable=False)
|
||||
document_type: Mapped[DocumentType] = mapped_column(pg_enum("document_type"), nullable=False)
|
||||
account_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("chart_of_account.id"))
|
||||
is_mandatory: Mapped[bool] = mapped_column(Boolean, nullable=False)
|
||||
158
api/src/models/consolidation.py
Normal file
158
api/src/models/consolidation.py
Normal file
@ -0,0 +1,158 @@
|
||||
"""Часть 07 DBML v5.13 «Консолидация»: консолидация ГК за период, версии, маршрут, блокировка, периметр, документы.
|
||||
|
||||
Производные консолидации (сводная ОСВ, пары ВГО, шахматка) НЕ хранятся (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)
|
||||
197
api/src/models/kfo.py
Normal file
197
api/src/models/kfo.py
Normal file
@ -0,0 +1,197 @@
|
||||
"""Часть 08 DBML v5.13 «КФО»: корректировки, гудвилл, результат КФО (шапка, строки, разрез по компаниям), КФО клиента.
|
||||
|
||||
Корректировки и гудвилл — ручные решения аналитика, хранятся поверх первички (overlay). Результат КФО
|
||||
замораживается при VERIFIED. Деньги — `numeric(18,2)`, проценты — `numeric(7,4)` / `numeric(9,4)`.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import decimal
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import Boolean, DateTime, ForeignKey, Index, Integer, Numeric, String, Text, text
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from .base import Base, UUIDPrimaryMixin
|
||||
from .enums import CorrectionStatus, StatementType, pg_enum
|
||||
|
||||
|
||||
class Correction(UUIDPrimaryMixin, Base):
|
||||
"""Корректировка ВГО: итог = свод первички − Σ применённых корректировок; пара — по (rule_id, компании)."""
|
||||
|
||||
__tablename__ = "correction"
|
||||
__table_args__ = (
|
||||
Index("ix_correction_consolidation_version_id", "consolidation_version_id"),
|
||||
Index(
|
||||
"ix_correction_version_rule_entities",
|
||||
"consolidation_version_id",
|
||||
"rule_id",
|
||||
"legal_entity_id",
|
||||
"counterparty_id",
|
||||
),
|
||||
)
|
||||
|
||||
consolidation_version_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("consolidation_version.id"), nullable=False
|
||||
)
|
||||
statement_type: Mapped[StatementType] = mapped_column(pg_enum("statement_type"), nullable=False)
|
||||
rule_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("elimination_rule.id"))
|
||||
parent_correction_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("correction.id"))
|
||||
legal_entity_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("legal_entity.id"))
|
||||
counterparty_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("legal_entity.id"))
|
||||
debit_line_code: Mapped[str | None] = mapped_column(String)
|
||||
debit_account_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("chart_of_account.id"))
|
||||
debit_adjustment: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
credit_line_code: Mapped[str | None] = mapped_column(String)
|
||||
credit_account_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("chart_of_account.id"))
|
||||
credit_adjustment: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
deviation_amount: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
deviation_percent: Mapped[decimal.Decimal | None] = mapped_column(Numeric(9, 4))
|
||||
reason_code: Mapped[str | None] = mapped_column(String)
|
||||
comment: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
status: Mapped[CorrectionStatus] = mapped_column(
|
||||
pg_enum("correction_status"), nullable=False, server_default=text("'DRAFT'")
|
||||
)
|
||||
version_no: Mapped[int] = mapped_column(Integer, nullable=False, server_default=text("1"))
|
||||
created_by: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id"))
|
||||
created_at: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
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 Goodwill(UUIDPrimaryMixin, Base):
|
||||
"""Гудвилл по паре «головная организация — дочерняя» (ТЗ 6.7); доля участия вводится вручную, %."""
|
||||
|
||||
__tablename__ = "goodwill"
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"uq_goodwill_version_head_subsidiary",
|
||||
"consolidation_version_id",
|
||||
"head_legal_entity_id",
|
||||
"subsidiary_legal_entity_id",
|
||||
unique=True,
|
||||
),
|
||||
)
|
||||
|
||||
consolidation_version_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("consolidation_version.id"), nullable=False
|
||||
)
|
||||
head_legal_entity_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("legal_entity.id"), nullable=False
|
||||
)
|
||||
subsidiary_legal_entity_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("legal_entity.id"), nullable=False
|
||||
)
|
||||
ownership_share: Mapped[decimal.Decimal | None] = mapped_column(Numeric(7, 4))
|
||||
financial_investment_amount: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
charter_capital_amount: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
additional_capital_amount: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
participation_amount: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
goodwill_amount: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
include_in_kfo: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("false"))
|
||||
comment: Mapped[str | None] = mapped_column(Text)
|
||||
created_by: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id"))
|
||||
created_at: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
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 KfoResult(UUIDPrimaryMixin, Base):
|
||||
"""Шапка результата КФО версии + контрольные пороги (ТЗ 6.8); `is_frozen = true` после VERIFIED."""
|
||||
|
||||
__tablename__ = "kfo_result"
|
||||
|
||||
consolidation_version_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("consolidation_version.id"), nullable=False, unique=True
|
||||
)
|
||||
computed_at: Mapped[datetime.datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
is_frozen: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("false"))
|
||||
net_assets: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
balance_total: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
revenue: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
sales_profit: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
total_deviation_bb: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
total_deviation_ofr: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
threshold_bb_exceeded: Mapped[bool | None] = mapped_column(Boolean)
|
||||
threshold_ofr_exceeded: Mapped[bool | None] = mapped_column(Boolean)
|
||||
threshold_justification: Mapped[str | None] = mapped_column(Text)
|
||||
goodwill_total: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
has_deviations: Mapped[bool | None] = mapped_column(Boolean)
|
||||
|
||||
|
||||
class KfoResultLine(UUIDPrimaryMixin, Base):
|
||||
"""Строка КФО (ФСБУ 4/2023): сумма по компаниям -> авто-элиминация -> корректировки -> итог."""
|
||||
|
||||
__tablename__ = "kfo_result_line"
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"uq_kfo_result_line_result_type_code",
|
||||
"kfo_result_id",
|
||||
"statement_type",
|
||||
"line_code",
|
||||
unique=True,
|
||||
),
|
||||
)
|
||||
|
||||
kfo_result_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("kfo_result.id"), nullable=False)
|
||||
statement_type: Mapped[StatementType] = mapped_column(pg_enum("statement_type"), nullable=False)
|
||||
line_code: Mapped[str] = mapped_column(String, nullable=False)
|
||||
line_name: Mapped[str | None] = mapped_column(String)
|
||||
sum_amount: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
eliminated_auto: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
eliminated_manual: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
goodwill_adjust: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
consolidated_amount: Mapped[decimal.Decimal] = mapped_column(Numeric(18, 2), nullable=False)
|
||||
vgo_amount: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
source_kind: Mapped[str | None] = mapped_column(String)
|
||||
deviation_amount: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
comment: Mapped[str | None] = mapped_column(Text)
|
||||
ordinal: Mapped[int | None] = mapped_column(Integer)
|
||||
|
||||
|
||||
class KfoResultLineCompany(Base):
|
||||
"""Разрез статьи КФО по компаниям (ЕХ 6.6); составной PK (kfo_result_line_id, legal_entity_id)."""
|
||||
|
||||
__tablename__ = "kfo_result_line_company"
|
||||
|
||||
kfo_result_line_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("kfo_result_line.id"), primary_key=True
|
||||
)
|
||||
legal_entity_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("legal_entity.id"), primary_key=True
|
||||
)
|
||||
amount: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
share_percent: Mapped[decimal.Decimal | None] = mapped_column(Numeric(7, 4))
|
||||
|
||||
|
||||
class ClientKfoLine(UUIDPrimaryMixin, Base):
|
||||
"""Сравнение с КФО клиента: ручной ввод сумм клиента и расхождение относительно наименьшего (Методика, сн. 14)."""
|
||||
|
||||
__tablename__ = "client_kfo_line"
|
||||
__table_args__ = (
|
||||
Index(
|
||||
"uq_client_kfo_line_version_type_code",
|
||||
"consolidation_version_id",
|
||||
"statement_type",
|
||||
"line_code",
|
||||
unique=True,
|
||||
),
|
||||
)
|
||||
|
||||
consolidation_version_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("consolidation_version.id", name="fk_client_kfo_line_consolidation_version_id"),
|
||||
nullable=False,
|
||||
)
|
||||
statement_type: Mapped[StatementType] = mapped_column(pg_enum("statement_type"), nullable=False)
|
||||
line_code: Mapped[str] = mapped_column(String, nullable=False)
|
||||
client_amount: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
client_vgo_amount: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
bank_amount: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
bank_vgo_amount: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
deviation_amount: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||||
deviation_percent: Mapped[decimal.Decimal | None] = mapped_column(Numeric(9, 4))
|
||||
deviation_reason: Mapped[str | None] = mapped_column(Text)
|
||||
comment: Mapped[str | None] = mapped_column(Text)
|
||||
entered_by: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id"))
|
||||
entered_at: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
Loading…
x
Reference in New Issue
Block a user