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
92 lines
3.3 KiB
Python
92 lines
3.3 KiB
Python
"""Схемы 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)
|