- 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
60 lines
3.4 KiB
Python
60 lines
3.4 KiB
Python
"""Часть 05 DBML v5.14 «ОСВ»: строка оборотно-сальдовой ведомости (ТЗ 6.2, 6.5).
|
||
|
||
Первичка не мутируется: сырые значения хранятся как пришли, правка аналитика — рядом
|
||
(`is_modified`, `original_values`, `modified_by/at` + audit_event(RECOGNIZED_DATA_EDIT)).
|
||
"""
|
||
|
||
import datetime
|
||
import decimal
|
||
import uuid
|
||
from typing import Any
|
||
|
||
from sqlalchemy import Boolean, DateTime, ForeignKey, Index, Integer, Numeric, SmallInteger, String, text
|
||
from sqlalchemy.dialects.postgresql import UUID
|
||
from sqlalchemy.orm import Mapped, mapped_column
|
||
|
||
from .base import Base, UUIDPrimaryMixin, nullable_jsonb
|
||
from .enums import OsvDataType, OsvGroupType, pg_enum
|
||
|
||
|
||
class OsvEntry(UUIDPrimaryMixin, Base):
|
||
"""Строка ОСВ; суммы — рубли, `numeric(18,2)`."""
|
||
|
||
__tablename__ = "osv_entry"
|
||
__table_args__ = (
|
||
Index("uq_osv_entry_document_id_row_number", "document_id", "row_number", unique=True),
|
||
Index("ix_osv_entry_owner_account_counterparty", "owner_legal_entity_id", "account_id", "counterparty_id"),
|
||
)
|
||
|
||
recognition_job_id: Mapped[uuid.UUID] = mapped_column(
|
||
UUID(as_uuid=True), ForeignKey("recognition_job.id"), nullable=False
|
||
)
|
||
document_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("document.id"), nullable=False)
|
||
owner_legal_entity_id: Mapped[uuid.UUID] = mapped_column(
|
||
UUID(as_uuid=True), ForeignKey("legal_entity.id"), nullable=False
|
||
)
|
||
parent_entry_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("osv_entry.id"))
|
||
group_type: Mapped[OsvGroupType | None] = mapped_column(pg_enum("osv_group_type"))
|
||
group_name: Mapped[str | None] = mapped_column(String)
|
||
row_number: Mapped[int | None] = mapped_column(Integer)
|
||
level: Mapped[int | None] = mapped_column(SmallInteger)
|
||
row_value: Mapped[str | None] = mapped_column(String)
|
||
data_type: Mapped[OsvDataType | None] = mapped_column(pg_enum("osv_data_type"))
|
||
account_code: Mapped[str | None] = mapped_column(String)
|
||
account_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("chart_of_account.id"))
|
||
account_root: Mapped[str | None] = mapped_column(String)
|
||
counterparty_name: Mapped[str | None] = mapped_column(String)
|
||
counterparty_inn: Mapped[str | None] = mapped_column(String)
|
||
counterparty_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("legal_entity.id"))
|
||
opening_debit: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||
opening_credit: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||
turnover_debit: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||
turnover_credit: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||
closing_debit: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||
closing_credit: Mapped[decimal.Decimal | None] = mapped_column(Numeric(18, 2))
|
||
is_modified: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("false"))
|
||
original_values: Mapped[Any | None] = mapped_column(nullable_jsonb())
|
||
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))
|
||
is_excisable: Mapped[bool | None] = mapped_column(Boolean)
|