I-2: delta MODIFIED consolidations-api «Статусы Среза 4» (загрузка в консолидацию). T1.1: шапки моделей — DBML v5.18. T3.1: lock_versions_of_consolidation — двухшаговая блокировка (FOR UPDATE без членства, затем выборка членов); тесты гонки и выборки. M-1: тест миграции 0006 — порядок кварталов внутри года. M-2: тест 404 для ЮЛ чужого филиала на столе своей консолидации. T4.1: docstring supersede_previous — перенос строк. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
198 lines
11 KiB
Python
198 lines
11 KiB
Python
"""Часть 08 DBML v5.18 «КФО»: корректировки, гудвилл, результат КФО (шапка, строки, разрез по компаниям), КФО клиента.
|
||
|
||
Корректировки и гудвилл — ручные решения аналитика, хранятся поверх первички (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))
|