arm-core/api/src/schemas/documents.py
Andrey Epifancev eaee3ec5f9 feat(model): DBML v5.16 — counterparty_match_kind, миграция 0004, шаг резолва в маппинге
Enum counterparty_match_kind (INN, NAME, ALIAS, MANUAL, PROPAGATED) и nullable-колонка osv_entry.counterparty_match_kind
(NULL <=> counterparty_id NULL). Миграция 0004_dbml_v5_16: enum, колонка, backfill тремя UPDATE по уже смапленным
строкам (ИНН без пробелов -> INN, trim(имя) -> NAME, остальные -> ALIAS), откат удаляет колонку и тип.
CounterpartyIndex.resolve возвращает Resolution(entity_id, kind) (определение в domain/osv_mapping, R-4);
маппинг пишет kind; EntryOut.match_kind. DBML_VERSION 5.16, Makefile/dbml_parity/CLAUDE/README/config.yaml.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 03:23:02 +04:00

92 lines
3.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Схемы API документов и строк ОСВ (spec documents-api «Документ и строки», «Подтверждение документа»; design D-11).
`DocumentOut` — карточка документа: поля `document`, `file_id`, схема группировок листа из
`recognition_job.group_schema` (элемент с `locator` документа) и предупреждения маппинга файла целиком (R-6).
`EntryOut` — строка `osv_entry`; суммы — `Decimal` (в JSON — строка, без `float`). `EntriesPageParams` — пагинация
строк: `limit` ≤ 2000, по умолчанию 500.
"""
import datetime
import uuid
from decimal import Decimal
from pydantic import BaseModel, ConfigDict, Field
from src.models.enums import (
CounterpartyMatchKind,
DocumentCheckResult,
DocumentStatus,
DocumentType,
OsvDataType,
OsvGroupType,
PeriodQuarter,
)
DEFAULT_ENTRIES_LIMIT = 500
MAX_ENTRIES_LIMIT = 2000
class GroupOut(BaseModel):
"""Группировка из шапки листа ОСВ (как в результате распознавания): тип, исходный текст, есть ли ИНН."""
type: str
name: str | None = None
has_inn: bool | None = None
class DocumentOut(BaseModel):
"""Карточка документа: статус, проверки, `locator`, схема группировок листа и предупреждения маппинга файла."""
id: uuid.UUID
file_id: uuid.UUID
legal_entity_id: uuid.UUID
document_type: DocumentType
osv_account_code: str | None
period_year: int | None
period_quarter: PeriodQuarter | None
is_cumulative: bool | None
locator: str | None
status: DocumentStatus
period_check: DocumentCheckResult | None
counterparty_check: DocumentCheckResult | None
confirmed_by: uuid.UUID | None
confirmed_at: datetime.datetime | None
group_schema: list[GroupOut] | None
warnings: list[str]
class EntryOut(BaseModel):
"""Строка ОСВ (`osv_entry`); дерево восстанавливается по `parent_entry_id`."""
model_config = ConfigDict(from_attributes=True)
id: uuid.UUID
row_number: int | None
level: int | None
parent_entry_id: uuid.UUID | None
row_value: str | None
group_type: OsvGroupType | None
group_name: str | None
data_type: OsvDataType | None
account_code: str | None
account_root: str | None
account_id: uuid.UUID | None
counterparty_name: str | None
counterparty_inn: str | None
counterparty_id: uuid.UUID | None
# способ сопоставления контрагента (v5.16); в API — `match_kind`, в ORM — `counterparty_match_kind`
match_kind: CounterpartyMatchKind | None = Field(validation_alias="counterparty_match_kind")
opening_debit: Decimal | None
opening_credit: Decimal | None
turnover_debit: Decimal | None
turnover_credit: Decimal | None
closing_debit: Decimal | None
closing_credit: Decimal | None
class EntriesPageParams(BaseModel):
"""Пагинация строк документа из query: `Annotated[EntriesPageParams, Query()]`."""
limit: int = Field(DEFAULT_ENTRIES_LIMIT, ge=1, le=MAX_ENTRIES_LIMIT)
offset: int = Field(0, ge=0)