arm-ka-docs/dev/api-contract-files-directories.md

12 KiB
Raw Blame History

Контракт API: файлы и документы организации, справочники (для экранов до сводной ОСВ)

Источник — реальный код arm-core (api/src/schemas/files.py, schemas/documents.py, schemas/directories.py, schemas/common.py, api/endpoints/files/{upload,read,documents}.py, api/endpoints/directories/*.py; Swagger — /docs). Маршруты существуют со Срезов 1 и 2c; здесь они впервые описаны для фронта (arm-core #42, П-5). Общие правила (префикс, токен, формат ошибок, видимость 404) — dev/api-contract-slice-4.md §1; форма ББ/ОФР, комментарии, исходный файл — dev/api-contract-slice-3b.md; консолидации — dev/api-contract-slice-4.md. Экраны — context/10-user-journey.md, номера […] — снимки макета (mockup/INDEX.md).

1. Общее

  • Права: файлы и документы — чтение DOCUMENT_VIEW, загрузка/удаление/подтверждение — DOCUMENT_EDIT; справочники — DIRECTORY_VIEW (импорт ЕЦП — DIRECTORY_ADMIN, только у администратора: кнопку «Импорт» на [34] скрывайте по GET /me).
  • Страницы — {items, total, limit, offset}: у файлов ЮЛ и справочников limit 1…200 (по умолчанию 50), у строк ОСВ документа 1…2000 (по умолчанию 500); offset ≥ 0; вне предела — 422 (pydantic-массив).
  • ЮЛ, файл или документ вне видимости пользователя неотличимы от несуществующих: 404 NOT_FOUND.
  • Перечисления: FileType — XLS, XLSX, XLSB, PDF, PNG, JPG; FileStatus — UPLOADED, PROCESSING, PROCESSED, ERROR, SUPERSEDED; RecognitionStatus (задание) — queued, published, processing, done, needs_check, error, timeout (нет ответа к сроку), dlq (сервис не подтвердил приём после повторов); DocumentType — BALANCE, INCOME, OSV, DISCLOSURE, OTHER; DocumentStatus — RECOGNIZED, NEEDS_CHECK, CONFIRMED, ERROR; DocumentCheckResult — MATCH, MISMATCH, UNKNOWN; EntityType — ЮЛ, ИП; RelatedGroupKind — gsk, gsz.

2. Эндпоинты: файлы и документы

Метод и путь Право Запрос Ответ Ошибки Экран
GET /legal-entities/{legal_entity_id}/files VIEW query limit, offset, include_superseded (bool, по умолчанию false — заменённые скрыты) Page[FileOut], новые первыми (по убыванию uploaded_at) 404 [39] drawer «Первичные документы»
POST /legal-entities/{legal_entity_id}/files EDIT multipart, поле files (повторяется для каждого файла, не files[]); query consolidation_version_id? 201 UploadResult (принят хотя бы один файл) / 422 UploadResult (ни один) 404 (ЮЛ; версия — VERSION_NOT_FOUND), 409 VERSION_NOT_EDITABLE, 422 ENTITY_NOT_IN_PERIMETER, 503 STORAGE_UNAVAILABLE [40]
GET /files/{file_id} VIEW — FileOut 404 опрос строки файла
DELETE /files/{file_id} EDIT — 204 404, 409 FILE_HAS_CONFIRMED_DOCUMENTS, 409 FILE_SUPERSEDES_OTHERS [39] иконка «удалить»
GET /files/{file_id}/content VIEW query disposition (attachment по умолчанию / inline) поток исходного файла 404 (FILE_CONTENT_NOT_FOUND — объекта нет в хранилище), 503 [41] «Исходный документ» (см. контракт 3b)
GET /documents/{document_id} VIEW — DocumentOut 404 [41], карточка документа
GET /documents/{document_id}/entries VIEW query limit (≤ 2000, по умолчанию 500), offset Page[EntryOut] по возрастанию row_number; дерево — по parent_entry_id 404 строки ОСВ документа (только чтение)
POST /documents/{document_id}/confirm EDIT — DocumentOut 404, 409 ALREADY_CONFIRMED / FILE_SUPERSEDED / DOCUMENT_NOT_CONFIRMABLE; для ББ/ОФР — PERIOD_REQUIRED, CHECKS_NOT_PASSED «Подтвердить данные»

Фильтра «файлы в контексте версии консолидации» нет (решение владельца 07.10.2026, arm-core #42): drawer показывает все документы ЮЛ, период — в колонке «Период». Подходит ли документ версии, видно по ячейкам чек-листа версии (document_ids).

3. Схемы: файлы и документы

UploadResult {accepted[], rejected[]}: accepted[] — {file_id, job_id, name}; rejected[] — {name, code, message, file_id?}, где code — UNSUPPORTED_FILE (тип не из списка или сигнатура не совпала), FILE_TOO_LARGE (> 50 МиБ), DUPLICATE_FILE (такой файл у ЮЛ уже есть — file_id указывает на него), STORAGE_UNAVAILABLE. Два разных тела 422: при отказе всех файлов — UploadResult (смотрите rejected[]); при ЮЛ вне периметра версии — доменная ошибка {"detail": {"code": "ENTITY_NOT_IN_PERIMETER", "message": …}}. Различайте по наличию detail.

FileOut {id, legal_entity_id, original_name?, file_type, status (FileStatus), version_no, size_bytes?, content_hash?, uploaded_at?, uploaded_by?, superseded_by_file_id?, job (JobBrief | null), documents[] (DocumentBrief)} — поля с ? могут быть null. JobBrief {id, status (RecognitionStatus), error_code?, error_message?, mapping_error?, warnings[], mapping_warnings[], finished_at?} — причина ошибки сервиса распознавания (error_*) или маппинга (mapping_error). DocumentBrief {id, document_type, osv_account_code?, period_year?, period_quarter?, is_cumulative?, status (DocumentStatus), period_check?, counterparty_check?, locator?, comments_count} — порядок: тип, счёт, locator.

DocumentOut = поля DocumentBrief + {file_id, legal_entity_id, confirmed_by?, confirmed_at?, group_schema? ([{type, name?, has_inn?}]), warnings[]}. EntryOut {id, row_number?, level?, parent_entry_id?, row_value?, group_type?, group_name?, data_type?, account_code?, account_root?, account_id?, counterparty_name?, counterparty_inn?, counterparty_id?, match_kind?, opening_debit?, opening_credit?, turnover_debit?, turnover_credit?, closing_debit?, closing_credit?} — деньги строками.

Как собрать строку drawer [39]

Строка — файл (FileOut); документы файла (documents[]) — подписи и проверки:

  • «Период»: period_year + period_quarter + is_cumulative — квартальный «2024 К1», нарастающий Q3 — «2024 · 9 мес.» (Q1 — «3 мес.», Q2 — «6 мес.», Q4 — «12 мес.»); period_quarter = null — «период не определён» (dev/api-contract-slice-4.md, нюанс 17).
  • «Проверка»: «Контрагент» — counterparty_check (MATCH ✓, MISMATCH ✗, UNKNOWN/null —); «Период» — period_check сейчас всегда UNKNOWN → показывайте «—» (OQ-120).
  • «Статус» (подписи — как в чек-листе и его CSV, context/10-user-journey.md «Словарь UI»): файл UPLOADED/PROCESSING — «Обработка»; файл ERROR — «Ошибка» (причина — job.error_message / job.mapping_error; задание timeout/dlq — тоже ошибка, причина в job.error_message); иначе по документу: RECOGNIZED и NEEDS_CHECK — «Требуется проверка», CONFIRMED — «Загружен», ERROR — «Ошибка».
  • Комментарий — у документа (comments_count, GET/POST /documents/{id}/comments, контракт 3b); у файла без документов комментария нет.
  • Опрос во время распознавания: перечитывайте GET /legal-entities/{id}/files (или GET /files/{id} для одного файла), пока файл в UPLOADED/PROCESSING; разумный интервал — 3–5 с.
  • Клик по строке ББ/ОФР-документа (PDF и Excel) — экран [41] (GET /documents/{id}/statement, контракт 3b); строки ОСВ — только чтение (GET /documents/{id}/entries).

4. Эндпоинты: справочники (экраны [03], [36], [42], [60])

Все — GET, право DIRECTORY_VIEW, префикс /api/v1/directories.

Путь Query Ответ Экран
/regional-offices — RegionalOfficeOut[] {id, name, is_active} имя «Основной РФ» на [03] (по base_regional_office_id)
/company-groups q (подстрока name / crm_id / code), is_archived, limit, offset Page[CompanyGroupOut] по имени [03], выбор ГК в [42]
/company-groups/{group_id} — CompanyGroupOut заголовок [34] («N организаций» — legal_entities_count)
/company-groups/{company_group_id}/related-groups — RelatedGroupOut[] (ГСК и ГСЗ; ГСК — kind = gsk) выбор ГСК в [42], фильтр «ГСК» в [36]
/related-groups/{group_id} — RelatedGroupDetail (+ members[]) отметки [42], колонка «ГСК» в [36]
/legal-entities company_group_id, outside_group (bool), inn (точно), q (подстрока имени/ИНН), closed (bool), limit, offset Page[LegalEntityOut] по имени [36] «Показать все ЮЛ в ГК», список ЮЛ в [42], монозаёмщики [60]
/legal-entities/{entity_id} — LegalEntityDetail (+ memberships[], aliases[]) карточка ЮЛ

CompanyGroupOut {id, code, crm_id?, name, base_regional_office_id?, is_archived, valid_from, legal_entities_count, created_at?, updated_at?}. RelatedGroupOut {id, code, company_group_id, kind, name, created_date?, members_count}; RelatedGroupDetail — + members[] {legal_entity_id, inn, name, valid_from} (текущий состав). LegalEntityOut {id, inn, crm_id?, entity_type?, name, company_group_id?, regional_office_id?, close_date?, valid_from, created_at?, updated_at?, documents_count}; LegalEntityDetail — + memberships[] {related_group_id, code, kind, name, valid_from}, aliases[] {id, alias, normalized_alias, created_at?}.

Нюансы:

  • Модалка [42]: ЮЛ ГК — legal-entities?company_group_id=<ГК>&closed=false (неархивные = без close_date), отметки — члены выбранной ГСК из related-groups/{id}; создание — POST /consolidations с legal_entity_ids (dev/api-contract-slice-4.md, нюанс 19).
  • Колонка «ГСК» в [36]: у LegalEntityOut членства нет — возьмите related-groups/{id} по каждой ГСК ГК (их немного) или LegalEntityDetail.memberships.
  • Монозаёмщики [60]: legal-entities?outside_group=true; «Документы» — documents_count, статусы ОФР/ББ/ОСВ — из GET /legal-entities/{id}/files по ЮЛ (сводного поля нет; экран не отнесён к срезу, П-6).
  • Поиск ГК по имени РФ не поддерживается (q — только name/crm_id/code); пользователю филиала он не нужен (видит один РФ).