- OsvDataType.NU после BU, DBML_VERSION = 5.15, Makefile/dbml_parity/docstring'и на data-model-v5.15.dbml; - миграция 0003_dbml_v5_15: ADD VALUE 'NU' AFTER 'BU' + lower(app_user.email); downgrade пересоздаёт osv_data_type (регистр email не возвращается); - email хранится в нижнем регистре: users_admin.create_user и seed (_apply_users); с UNIQUE по строчному email гонка создания в другом регистре теперь ловится индексом (тест test_api_constraint_races обновлён); - тесты: enum NU, откат на шаг, lower email на миграции, seed/админка, вставка NU, фиктивная v5.16. Co-Authored-By: Claude Sonnet 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.15 «Бухгалтерская отчётность»: шапка, колонки-периоды, строки и значения форм ББ/ОФР (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))
|