- RelatedGroupMember.kind (related_group_kind, NOT NULL) между legal_entity_id и valid_from, как в DBML; частичный уникальный индекс uq_related_group_member_entity_kind_current (legal_entity_id, kind) WHERE is_current — ЮЛ одновременно в одной ГСК и одной ГСЗ. - AuditAction: LEGAL_ENTITY_CHANGE (после GROUP_CHANGE), USER_CHANGE (после USER_ROLE_CHANGE). - Последовательности company_group_code_seq / related_group_code_seq в metadata (design D-5). - Миграция 0002_dbml_v5_14: related_group_member пересоздаётся с переносом строк (kind из группы), чтобы порядок колонок совпал с DBML и create_all; ALTER TYPE ADD VALUE ×2; downgrade пересоздаёт audit_action v5.13. - DBML_VERSION = 5.14, пути к data-model-v5.14.dbml (Makefile, dbml_parity), тесты: откат на шаг, перенос строк, уникальность gsk+gsz, офлайн-SQL до head. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
120 lines
6.6 KiB
Python
120 lines
6.6 KiB
Python
"""Часть 06 DBML v5.14 «Бухгалтерская отчётность»: шапка, колонки-периоды, строки и значения форм ББ/ОФР (157н).
|
||
|
||
Первичка не мутируется: `*_original_value` / `original_amount` / `is_modified` — рядом с распознанным значением.
|
||
Логические проверки (ТЗ 6.3) и «требует проверки» не хранятся — считаются на лету.
|
||
"""
|
||
|
||
import datetime
|
||
import decimal
|
||
import uuid
|
||
|
||
from sqlalchemy import Boolean, Date, DateTime, ForeignKey, Index, Numeric, 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 StatementType, pg_enum
|
||
|
||
|
||
class FinancialStatementMeta(UUIDPrimaryMixin, Base):
|
||
"""Шапка распознанной формы ББ/ОФР; 1:1 с `document`."""
|
||
|
||
__tablename__ = "financial_statement_meta"
|
||
|
||
document_id: Mapped[uuid.UUID] = mapped_column(
|
||
UUID(as_uuid=True), ForeignKey("document.id"), nullable=False, unique=True
|
||
)
|
||
recognition_job_id: Mapped[uuid.UUID] = mapped_column(
|
||
UUID(as_uuid=True), ForeignKey("recognition_job.id"), nullable=False
|
||
)
|
||
statement_type: Mapped[StatementType] = mapped_column(pg_enum("statement_type"), nullable=False)
|
||
inn: Mapped[str | None] = mapped_column(String)
|
||
inn_original_value: Mapped[str | None] = mapped_column(String)
|
||
inn_confidence: Mapped[decimal.Decimal | None] = mapped_column(Numeric(5, 2))
|
||
inn_verified: Mapped[bool | None] = mapped_column(Boolean)
|
||
inn_modified: Mapped[bool | None] = mapped_column(Boolean)
|
||
organization_name: Mapped[str | None] = mapped_column(String)
|
||
report_date: Mapped[datetime.date | None] = mapped_column(Date)
|
||
report_date_original_value: Mapped[str | None] = mapped_column(String)
|
||
report_date_verified: Mapped[bool | None] = mapped_column(Boolean)
|
||
report_date_modified: Mapped[bool | None] = mapped_column(Boolean)
|
||
units: Mapped[str | None] = mapped_column(String)
|
||
units_original_value: Mapped[str | None] = mapped_column(String)
|
||
units_verified: Mapped[bool | None] = mapped_column(Boolean)
|
||
units_modified: Mapped[bool | None] = mapped_column(Boolean)
|
||
currency: Mapped[str | None] = mapped_column(String, server_default=text("'RUB'"))
|
||
okpo: Mapped[str | None] = mapped_column(String)
|
||
okopf: Mapped[str | None] = mapped_column(String)
|
||
okfs: Mapped[str | None] = mapped_column(String)
|
||
okud: Mapped[str | None] = mapped_column(String)
|
||
okei: Mapped[str | None] = mapped_column(String)
|
||
legal_form: Mapped[str | None] = mapped_column(String)
|
||
ownership_form: Mapped[str | None] = mapped_column(String)
|
||
address: Mapped[str | None] = mapped_column(String)
|
||
subject_to_audit: Mapped[bool | None] = mapped_column(Boolean)
|
||
reviewed_by: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id"))
|
||
reviewed_at: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True))
|
||
checks_accepted_by: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id"))
|
||
checks_accepted_at: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True))
|
||
checks_accepted_comment: Mapped[str | None] = mapped_column(Text)
|
||
|
||
|
||
class FinancialStatementColumn(UUIDPrimaryMixin, Base):
|
||
"""Колонка-период формы (2–3 сравнительные даты); `period_date` нужна межформенным проверкам и гудвиллу."""
|
||
|
||
__tablename__ = "financial_statement_column"
|
||
__table_args__ = (
|
||
Index("uq_financial_statement_column_meta_id_column_index", "meta_id", "column_index", unique=True),
|
||
)
|
||
|
||
meta_id: Mapped[uuid.UUID] = mapped_column(
|
||
UUID(as_uuid=True), ForeignKey("financial_statement_meta.id"), nullable=False
|
||
)
|
||
column_index: Mapped[int] = mapped_column(SmallInteger, nullable=False)
|
||
label: Mapped[str | None] = mapped_column(String)
|
||
period_date: Mapped[datetime.date | None] = mapped_column(Date)
|
||
is_current: Mapped[bool | None] = mapped_column(Boolean)
|
||
|
||
|
||
class FinancialStatementLine(UUIDPrimaryMixin, Base):
|
||
"""Строка формы с кодом; порядок, разделы и итоговые строки — шаблон формы на фронте."""
|
||
|
||
__tablename__ = "financial_statement_line"
|
||
__table_args__ = (Index("uq_financial_statement_line_meta_id_line_code", "meta_id", "line_code", unique=True),)
|
||
|
||
meta_id: Mapped[uuid.UUID] = mapped_column(
|
||
UUID(as_uuid=True), ForeignKey("financial_statement_meta.id"), nullable=False
|
||
)
|
||
line_code: Mapped[str | None] = mapped_column(String)
|
||
line_code_original_value: Mapped[str | None] = mapped_column(String)
|
||
line_code_confidence: Mapped[decimal.Decimal | None] = mapped_column(Numeric(5, 2))
|
||
line_code_modified: Mapped[bool | None] = mapped_column(Boolean)
|
||
line_name: Mapped[str | None] = mapped_column(String)
|
||
line_name_original_value: Mapped[str | None] = mapped_column(String)
|
||
line_name_confidence: Mapped[decimal.Decimal | None] = mapped_column(Numeric(5, 2))
|
||
line_name_modified: Mapped[bool | None] = mapped_column(Boolean)
|
||
|
||
|
||
class FinancialStatementValue(UUIDPrimaryMixin, Base):
|
||
"""Распознанное значение (строка x колонка): сумма, уверенность, признак ручной правки (Прил. 2 ТЗ)."""
|
||
|
||
__tablename__ = "financial_statement_value"
|
||
__table_args__ = (Index("uq_financial_statement_value_line_id_column_id", "line_id", "column_id", unique=True),)
|
||
|
||
line_id: Mapped[uuid.UUID] = mapped_column(
|
||
UUID(as_uuid=True), ForeignKey("financial_statement_line.id"), nullable=False
|
||
)
|
||
column_id: Mapped[uuid.UUID] = mapped_column(
|
||
UUID(as_uuid=True),
|
||
ForeignKey("financial_statement_column.id", name="fk_financial_statement_value_column_id_fs_column"),
|
||
nullable=False,
|
||
)
|
||
amount: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||
string_value: Mapped[str | None] = mapped_column(String)
|
||
confidence: Mapped[decimal.Decimal | None] = mapped_column(Numeric(5, 2))
|
||
is_recognized: Mapped[bool | None] = mapped_column(Boolean)
|
||
is_modified: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("false"))
|
||
original_amount: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||
modified_by: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("app_user.id"))
|
||
modified_at: Mapped[datetime.datetime | None] = mapped_column(DateTime(timezone=True))
|