Решения владельца 08.10.2026 (arm-core #74): несопоставленная строка свода — без ЮЛ и без ИНН из файла; `ConsolidatedRowOut.counterparty_entity_inn` (П-8). Обновлены frontend-api-guide (таблица изменений, п. 10, схема, пробелы API), api-contract-slice-4, 10-user-journey 3.1, qa-guide (контрольные цифры 1381 → 1260). Новый открытый вопрос OQ-158: строки аналитики, не являющиеся организациями, в «Осталось N». Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019qoydUVv8g1qHxc123u2RR
185 lines
29 KiB
Markdown
185 lines
29 KiB
Markdown
# Контракт API Среза 4 (консолидация) для разработчика фронта
|
||
|
||
Источник — реальная схема `GET /openapi.json` arm-core (ветка `feat/18-slice-4-consolidation`, Swagger — `/docs`); коды ошибок и
|
||
нюансы сверены с кодом и спекой `arm-core/openspec/changes/archive/2026-10-06-slice-4-consolidation/` (design D-1…D-16, решения №1–№81+).
|
||
Правила предметной области — `context/05-domain-rules.md` §2, §3, §9.1, §10; экраны — `context/10-user-journey.md`
|
||
(пути 2 и 3), номера в `[…]` — снимки макета (`mockup/INDEX.md`). Здесь — только то, что нужно, чтобы писать клиент.
|
||
|
||
## 1. Общее
|
||
|
||
- Префикс `/api/v1`, заголовок `Authorization: Bearer <JWT>` (в dev — `POST /dev/token`). Права: `CONSOLIDATION_VIEW` (чтение),
|
||
`CONSOLIDATION_EDIT` (правки); загрузка файлов — `DOCUMENT_EDIT`. Нет права — 403 строкой
|
||
`{"detail": "недостаточно прав: требуется <КОД>"}`; нет/плохой токен — 401. Скрывать кнопки по праву можно по `GET /me`.
|
||
- Доменные ошибки: `{"detail": {"code": "...", "message": "на русском", ...доп. поля}}`; по `code` работает клиент. 422 валидации
|
||
тела/параметров — массив pydantic (`detail: [{loc, msg, type}]`). Каталог кодов — `arm-core/README.md`, «Ошибки API».
|
||
- Видимость: ГК и версии вне области видимости пользователя (`ka_rf`/`rka`/`client_manager` — только свой филиал) неотличимы от
|
||
несуществующих: **404 с кодом `NOT_FOUND`**. Различие — только по тексту `message` (см. §4); клиент не должен ветвиться по тексту.
|
||
- **Деньги — строки JSON с двумя знаками** (`"100.00"`, `"0.00"`), могут быть `null` (нарастающий документ отдаётся «как в файле»).
|
||
Не парсить во `float`: суммы `numeric(18,2)`.
|
||
- Идентификаторы — uuid-строки; даты — ISO; `quarter` — `Q1`…`Q4`.
|
||
- Страницы — `{items, total, limit, offset}`, `offset ≥ 0`. Лимиты у списков разные: сводная ОСВ и строки ОСВ документа — `limit ≤ 2000`
|
||
(по умолчанию 500); файлы ЮЛ и справочники — `limit ≤ 200` (по умолчанию 50). Больше предела — 422. Файлы, документы и справочники —
|
||
`dev/api-contract-files-directories.md`.
|
||
- Перечисления: `ConsolidationStatus` — `INITIATED`, `DOCUMENTS_PROCESSING`, `DATA_CORRECTION`, `CONSOLIDATION_RF`, `ADJUSTMENT`,
|
||
`VERIFICATION_RF`, `CONSOLIDATION_GO`, `VERIFICATION_GO`, `VERIFIED`, `CANCELLED`, `REJECTED`, `ARCHIVED` (в Срезе 4 достижимы
|
||
первые три и `CANCELLED`); `CounterpartyType` — `GSK`, `GK`, `EXTERNAL`; `CounterpartyMatchKind` — `INN`, `NAME`, `ALIAS`,
|
||
`MANUAL`, `PROPAGATED`; `CellStatus` — `CONFIRMED`, `NEEDS_CHECK`, `RECOGNIZED`, `PROCESSING`, `ERROR`, `MISSING`;
|
||
`ConsolidationScenario` — `CREATE`, `CONFIRM`; `ConsolidationApproach` — `BASIC`, `EXTENDED`.
|
||
- «В работе» — `INITIATED`, `DOCUMENTS_PROCESSING`, `DATA_CORRECTION` (`VersionCard.is_in_work`).
|
||
|
||
## 2. Эндпоинты
|
||
|
||
Порядок проверок на каждом маршруте: право (403) → видимость (404) → для правок статус «в работе» и своя блокировка (409) → операция.
|
||
В таблице «Ошибки» — доменные коды и HTTP; 401/403 везде и ниже не повторяются.
|
||
|
||
| Метод и путь | Право | Запрос | Ответ | Ошибки | Экран |
|
||
|---|---|---|---|---|---|
|
||
| `POST /consolidations` | EDIT | `ConsolidationCreate {company_group_id, related_group_id, year (2000–2100), quarter, legal_entity_ids? (≤ 500)}` | 201 `VersionCard` (версия №1, `INITIATED`, `lock = null`) | 404 `NOT_FOUND` (ГК/ГСК не видна), 409 `CONSOLIDATION_EXISTS`, 422 `RELATED_GROUP_MISMATCH`, 422 `ENTITY_NOT_IN_GROUP` (`legal_entity_ids`), 422 `PERIMETER_TOO_SMALL`, 422 (pydantic-массив) при более чем 500 `legal_entity_ids` | [42] |
|
||
| `GET /company-groups/{group_id}/consolidations` | VIEW | query `in_work_only` (bool, по умолчанию `false`) | `ConsolidationPeriodOut[]` (период + `versions[]` — `VersionBrief`) | 404 `NOT_FOUND` | [34] |
|
||
| `GET /consolidations/versions/{version_id}` | VIEW | — | `VersionCard` | 404 `NOT_FOUND` | [34] панель «Организации», [36], [37] |
|
||
| `POST /consolidations/versions/{version_id}/lock` | EDIT | тело необязательно: `LockIn {takeover_reason?}` (≤ 1000) | 201 `LockOut` (новая блокировка или перехват) / 200 `LockOut` (уже ваша — идемпотентно) | 404 `NOT_FOUND`, 409 `VERSION_NOT_EDITABLE` (версия `CANCELLED`), 409 `LOCKED_BY_OTHER`, 422 (pydantic-массив) при `takeover_reason` длиннее 1000 символов | [35] |
|
||
| `DELETE /consolidations/versions/{version_id}/lock` | EDIT | — | 204 (в т. ч. если активной блокировки нет) | 404 `NOT_FOUND`, 409 `NOT_LOCK_OWNER` | [34] |
|
||
| `POST /consolidations/versions/{version_id}/status` | EDIT | `StatusIn {to_status, comment? (≤ 1000)}` | 200 `VersionCard` | 404, 409 `VERSION_NOT_EDITABLE` / `LOCK_REQUIRED` / `LOCKED_BY_OTHER` / `TRANSITION_NOT_ALLOWED`, 422 `COMMENT_REQUIRED`, 422 (pydantic-массив) при `comment` длиннее 1000 символов | [33-01], меню «…» [34] |
|
||
| `PUT /consolidations/versions/{version_id}/members` | EDIT | `MembersIn {legal_entity_ids[] (≤ 500)}` | 200 `VersionCard` | 404, 409 `VERSION_NOT_EDITABLE` / `LOCK_REQUIRED` / `LOCKED_BY_OTHER`, 422 `ENTITY_NOT_IN_GROUP` (`legal_entity_ids`), 422 `PERIMETER_TOO_SMALL`, 422 (pydantic-массив) при более чем 500 `legal_entity_ids` | [36] |
|
||
| `GET /consolidations/versions/{version_id}/checklist` | VIEW | query `format` (`json` по умолчанию / `csv`) | `ChecklistOut {rules[], rows[]}`; с `format=csv` — файл CSV (Срез 3b) | 404 | [37] |
|
||
| `GET /consolidations/versions/{version_id}/osv` | VIEW | query: `limit`, `offset`, `legal_entity_id`, `account_root`, `counterparty_type`, `unmapped` (bool), `match_kind` (повторяемый), `q` (≤ 500) | 200 `Page[ConsolidatedRowOut]` | 404, 422 (pydantic-массив) при `q` длиннее 500 символов | [30] |
|
||
| `GET /consolidations/versions/{version_id}/osv/summary` | VIEW | — | `OsvSummaryOut {rows_total, unmapped_total, auto_matched_total, documents_total, gaps[]}` | 404 | [30], баннер «Осталось: N», [31-00] |
|
||
| `GET /consolidations/versions/{version_id}/osv/export` | VIEW | те же фильтры, что у `…/osv`, без `limit`/`offset` | файл xlsx (`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`) | 404 | «Экспорт» [30] |
|
||
| `GET /consolidations/versions/{version_id}/counterparty-candidates` | VIEW | query: `q` (≤ 500), `scope` (`group` по умолчанию / `all`), `entry_id` | `CandidateOut[]` (до 100) | 404, 422 `QUERY_TOO_SHORT` (`scope=all` и `q` короче 3 символов после обрезки) | [32-osv-map-counterparty] |
|
||
| `POST /consolidations/versions/{version_id}/osv/entries/{entry_id}/counterparty` | EDIT | `MapCounterpartyIn {legal_entity_id, move_alias?=false}` | 200 `MapCounterpartyOut` | 404, 409 `VERSION_NOT_EDITABLE` / `LOCK_REQUIRED` / `LOCKED_BY_OTHER`, 409 `ALIAS_TAKEN` (`owners[] {legal_entity_id, name}`) | [32-osv-map-counterparty] «Связать» |
|
||
| `POST /legal-entities/{legal_entity_id}/files?consolidation_version_id=` | `DOCUMENT_EDIT` | multipart `files`; параметр версии необязателен | как в Срезе 2c: 201/422 `{accepted[], rejected[]}` | 404 (ЮЛ или версия), 409 `VERSION_NOT_EDITABLE`, 422 `ENTITY_NOT_IN_PERIMETER` | [39] / [40] |
|
||
|
||
Остальные маршруты, влияющие на версию: `POST /documents/{id}/confirm` (Срез 2c) привязывает подтверждённый документ ко всем
|
||
версиям «в работе» своей ГК и может перевести версию в `DATA_CORRECTION`; `DELETE /files/{id}` и автозамена — снимают привязки
|
||
(см. `context/05-domain-rules.md` §9.5).
|
||
|
||
## 3. Схемы
|
||
|
||
`Ref {id, name}`. Поля без `?` присутствуют всегда.
|
||
|
||
**`ConsolidationPeriodOut`** `{consolidation_id, year, quarter, reporting_date, versions[]}`.
|
||
**`VersionBrief`** `{id, version_no, status, scenario, approach, lock (LockOut | null), created_at (datetime | null), members_count}`.
|
||
**`LockOut`** `{locked_by, locked_by_name, locked_at, taken_over_from (uuid | null), takeover_reason (string | null)}`.
|
||
**`VersionCard`** = `VersionBrief` + `{consolidation_id, year, quarter, reporting_date, company_group (Ref), initiator (Ref),
|
||
initiator_office (Ref), goodwill_enabled, is_in_work, previous_version_id, members[], history[]}`:
|
||
- `members[]` — `{id, name, inn, entity_type ("ЮЛ" | "ИП" | null — поле `legal_entity` nullable в DBML), is_head_entity, checklist[], osv_accounts[]}` (arm-core #42):
|
||
- `checklist[]` — ячейки `ChecklistCellOut` по правилам комплекта (тот же порядок, что `ChecklistOut.rules`); ячейка
|
||
**самоописывающая** — ищите нужную по `document_type`/`account` («Бух. баланс» — `BALANCE`, «ОФР» — `INCOME`, «ОСВ 60» —
|
||
`OSV` + `"60"`), а не по индексу;
|
||
- `osv_accounts[]` — коды счетов ОСВ ЮЛ, **загруженных** к периоду версии: документы ОСВ за год версии с кварталом не позже
|
||
квартала версии, в любом статусе, кроме ошибки, из незаменённых файлов; натуральный порядок (`["01", "51", "58.03", "60"]`).
|
||
Это колонка «ОСВ» панели «Организации» [34] — «что загружено», а не «что готово»: готовность счетов комплекта — в ячейках
|
||
`checklist`. Пока файл распознаётся, его счетов в списке ещё нет (ячейки показывают `PROCESSING`);
|
||
- `history[]` — `{from_status (null у первой записи), to_status, changed_by, changed_at, comment}`.
|
||
|
||
**`ChecklistOut`** `{rules[], rows[]}`; `rules[]` — `{document_type (BALANCE/INCOME/OSV…), account (string | null — счёт ОСВ),
|
||
is_mandatory}` в порядке: ББ, ОФР, ОСВ по коду счёта (натурально: `58 < 58.03 < 60`); `rows[]` — `{legal_entity_id,
|
||
legal_entity_name, legal_entity_inn, cells[]}`, порядок `cells` = порядок `rules`. **`ChecklistCellOut`** `{document_type, account
|
||
(string | null), status (CellStatus), missing_quarters[], document_ids[]}` — `document_type`/`account` — правило ячейки (arm-core #42):
|
||
`CONFIRMED` — пара полностью покрыта привязанными подтверждёнными документами; `NEEDS_CHECK` / `RECOGNIZED` — есть
|
||
неподтверждённый подходящий документ; `PROCESSING` — у ЮЛ есть файл в обработке (по **всем** недостающим столбцам ЮЛ, пока файл
|
||
не смаплен — это известная неточность, OQ-122); `ERROR` — последний файл ЮЛ в `ERROR`; `MISSING` — нет ничего.
|
||
`missing_quarters` и `document_ids` — при частичном покрытии квартальными ОСВ (`document_ids` — только привязанные документы).
|
||
ОСВ по субсчёту (58.03) закрывает только своё правило, не «ОСВ 58».
|
||
|
||
**`ConsolidatedRowOut`** — строка свода:
|
||
`{entry_ids[], legal_entity_id, legal_entity_name, legal_entity_inn, account_code?, account_root?, period ("<год> Q<n>"),
|
||
is_cumulative, row_number?, counterparty_name?, counterparty_inn?, counterparty_id?, counterparty_entity_name?, counterparty_entity_inn?, counterparty_type,
|
||
match_kind?, match_stored, opening_debit?, opening_credit?, turnover_debit?, turnover_credit?, closing_debit?, closing_credit?,
|
||
warnings[]}` — поля с `?` могут быть `null` (ключ присутствует всегда).
|
||
`counterparty_name/inn` — как в файле ОСВ; `counterparty_id`/`counterparty_entity_name`/`counterparty_entity_inn` — сопоставленное ЮЛ справочника (id, имя, ИНН) или `null`;
|
||
`match_stored = true` — сопоставление хранится в БД, `false` — вычислено на лету (в БД не пишется); `match_kind` — способ
|
||
(`null`, если ЮЛ не найдено). Для склеенных строк `entry_ids` — записи по кварталам (порядок — от раннего к позднему).
|
||
**`OsvSummaryOut`** `{rows_total, unmapped_total, auto_matched_total, documents_total, gaps[]}`: `rows_total` — число строк свода (без фильтров); `unmapped_total` — число строк свода с пустым `counterparty_id` **после** дорезолва на лету **и** без `counterparty_inn` (то же, что `GET …/osv?unmapped=true` даёт в `total`; `unmapped=false` — все остальные строки); `auto_matched_total` — число строк с `match_kind` `NAME` или `ALIAS` независимо от `match_stored` (`INN`, `MANUAL`, `PROPAGATED` не входят); `documents_total` — число ОСВ-документов, привязанных к версии; **`gaps[]`** —
|
||
`{legal_entity_id, legal_entity_name, account_code, missing_quarters[]}` (пара ЮЛ × счёт, у которой нет ОСВ за какие-то кварталы;
|
||
строк такой пары в своде нет). Баннер «Требуется указать соответствия… Осталось: N», кнопка «Показать ЮЛ без ИНН» [32-osv-no-inn]
|
||
и предупреждение [31-00] опираются на `unmapped_total` / `…/osv?unmapped=true`: строки свода без сопоставленного ЮЛ **и** без ИНН
|
||
из файла; строка с ИНН, которого нет в справочнике, сопоставления не требует (решение владельца 08.10.2026, arm-core #74 — отмена
|
||
П-1 от 07.10.2026). **`CandidateOut`** `{id, inn, name, company_group (Ref | null), gsk (Ref | null), gsz (Ref | null),
|
||
counterparty_type, recommended}`. **`MapCounterpartyOut`** `{row (ConsolidatedRowOut | null), propagated, alias_created,
|
||
alias_moved_from (uuid | null)}`.
|
||
|
||
## 4. Нюансы, которые легко пропустить
|
||
|
||
1. **Блокировка — не «мягкая».** Любая правка версии (`PUT …/members`, `POST …/status`, ручное сопоставление) требует статуса «в
|
||
работе» **и** вашей активной блокировки; нет блокировки — 409 `LOCK_REQUIRED`, чужая — 409 `LOCKED_BY_OTHER`. Загрузка файлов и
|
||
подтверждение документов блокировкой не ограничиваются. Блокировка **не истекает по времени**; `CANCELLED` её снимает.
|
||
2. **`LOCKED_BY_OTHER` приходит в двух формах.** Из `POST …/lock` — с `locked_by`, `locked_by_name`, `locked_at`. Из правок
|
||
(`PUT …/members`, `POST …/status`, сопоставление, т. е. из `ensure_version_editable`) — только `locked_by`, `locked_at`, **без
|
||
`locked_by_name`** (доменная функция имени не знает; имя — из `lock.locked_by_name` карточки версии). `NOT_LOCK_OWNER` отдаёт
|
||
`locked_by` и `locked_at` владельца.
|
||
3. **Перехват.** `POST …/lock` с непустым `takeover_reason` при чужой блокировке снимает её и создаёт вашу (201,
|
||
`taken_over_from`, `takeover_reason`). Пустая причина или из одних пробелов считается отсутствующей (→ 409 `LOCKED_BY_OTHER`);
|
||
при свободной версии причина игнорируется. Повторный `POST` по своей блокировке — 200, а не 201.
|
||
4. **404 — всегда код `NOT_FOUND`**; различие по тексту `message`: «версия консолидации не найдена», «ГК не найдена»,
|
||
«ГСК/ГСЗ не найдена», «строка ОСВ не найдена в версии», «ЮЛ не найдено». Отдельных кодов `CONSOLIDATION_NOT_FOUND` /
|
||
`VERSION_NOT_FOUND` / `ENTRY_NOT_FOUND` **нет** (design №17, №62) — это обозначения в спеке. ГСК из чужой ГК или с видом `gsz` —
|
||
422 `RELATED_GROUP_MISMATCH`, а невидимая/несуществующая ГСК — 404.
|
||
5. **`MapCounterpartyOut.row` может быть `null`:** строка привязанного документа, которая в свод не попадает (пара с пробелом,
|
||
квартальный документ при нарастающем), сопоставляется, но строки свода для ответа нет (design №69). Если `row == null`, перечитайте
|
||
`GET …/osv` и `…/summary`; обязательно проверяйте на `null`.
|
||
6. **`match_kind` склеенной строки — от последнего квартала** (D-3, design №8). Строка свода из нескольких кварталов несёт
|
||
`entry_ids[]`; ручное сопоставление пишется на записи, чью `entry_id` вы отправили. Чтобы в ответе увидеть `MANUAL`, отправляйте
|
||
**последнюю** `entry_id` строки (`entry_ids[entry_ids.length - 1]`); для записи более раннего квартала в строке будет `PROPAGATED`
|
||
(в БД при этом одна запись `MANUAL`). Если в записях разных кварталов разные `counterparty_id`, расхождение — в `warnings`.
|
||
7. **Ручное сопоставление — только строки свода** (R-11, design №62/№63): `group_type = COUNTERPARTY`, `data_type` БУ/СУММ привязанного
|
||
ОСВ-документа версии. Строки `NU`/`ACCOUNT`, чужие и несуществующие — 404 `NOT_FOUND`. Автоприменение (`PROPAGATED`) затрагивает
|
||
строки с тем же `counterparty_name.strip()`, пустым `counterparty_id` и ИНН контрагента пустым или равным ИНН выбранного ЮЛ — только среди строк
|
||
**свода** (`COUNTERPARTY`, БУ/СУММ) ОСВ-документов, привязанных именно к ЭТОЙ версии; ранее сопоставленные строки не перевязываются;
|
||
`propagated` — число записей `osv_entry`, а не строк
|
||
свода: `unmapped_total` падает на число **склеенных** строк (R-15).
|
||
8. **`ALIAS_TAKEN`** (`owners[]`): алиас закреплён за другим ЮЛ; повторите с `move_alias: true` (право `DIRECTORY_DELETE` не нужно),
|
||
в ответе `alias_moved_from` — прежний владелец.
|
||
9. **Экспорт.** Заголовок `Content-Disposition: attachment; filename="svod-osv.xlsx"; filename*=UTF-8''svod-osv_<ГК>_<год>-<Qn>_v<n>.xlsx`
|
||
(имя percent-кодировано целиком). Забирайте файл как blob с `Authorization` (прямая ссылка `<a href>` не подойдёт). Фильтры — те
|
||
же, что у `GET …/osv`, пагинации нет, порядок строк тот же. **Лист «Пробелы» строится по своду целиком и от фильтров не зависит**
|
||
(при `unmapped=true` без совпадений лист «Сводная ОСВ» — только заголовок, «Пробелы» заполнен). Суммы — числовые ячейки
|
||
`#,##0.00`, текст не вычисляется как формула.
|
||
10. **`warnings` — не контракт.** Тексты диагностики строки (расхождение `counterparty_id` между кварталами и т. п.) могут меняться;
|
||
показывать как подсказку, не разбирать.
|
||
11. **Q1.** ОСВ за `Q1` (парсер помечает её нарастающей) подходит версии любого квартала года, в версиях Q2+ служит квартальным
|
||
источником Q1; ББ/ОФР за Q1 версиям Q2–Q4 не подходят (R-14/R-14a, OQ-124). В `ConsolidatedRowOut.is_cumulative` —
|
||
признак строки свода: `true` только для строк нарастающего документа за квартал версии.
|
||
12. **Чтение не требует блокировки** и доступно для версии любого статуса, включая `CANCELLED`.
|
||
13. **Все деньги — строки с двумя знаками**; склеенные квартальные строки не содержат `null` (пустое = `"0.00"`), строки нарастающего
|
||
документа — как в файле (могут быть `null`).
|
||
14. **`period_check` документа остаётся `UNKNOWN`**: подходящесть документа версии — свойство пары (документ, версия), показывайте её
|
||
через чек-лист версии, а не через поле документа (OQ-120).
|
||
15. **Создание не ставит блокировку.** После `POST /consolidations` версия свободна; «Взять в работу» — отдельное действие [35].
|
||
ГСК в запросе не сохраняется (`related_group_id` в версии пока пуст); члены берутся из текущего состава ГСК на момент создания.
|
||
16. **Границы входов (arm-core #26).** `MembersIn.legal_entity_ids` ≤ 500 элементов, `StatusIn.comment` ≤ 1000 символов,
|
||
`q` в `GET …/osv` и `…/osv/export` ≤ 500 символов (у `counterparty-candidates` так было и раньше). Превышение — 422 **в формате
|
||
pydantic** (`detail[] {loc, msg, type}`), без доменного `code`: показывайте как ошибку валидации формы, не ищите `code`.
|
||
Ограничивайте ввод на клиенте теми же числами.
|
||
17. **ОСВ со скользящим периодом** («Октябрь 2024 г. – Сентябрь 2025 г.», arm-tabular-parser #13, OQ-131): у документа
|
||
`period_year` = год конца диапазона, а `period_quarter` и `is_cumulative` — `null`. Такой документ не подходит ни одной версии
|
||
(привязка идёт по кварталу), в чек-листе и своде не участвует, подтверждать его можно. В списке документов показывайте год и
|
||
пометку «период не определён», не подставляйте квартал сами. Границы периода появятся в контракте v1.2 (Срез 2b).
|
||
18. **Удаление алиаса** (`DELETE /directories/legal-entities/{id}/aliases/{alias_id}`) во время переноса этого же алиаса ручным сопоставлением
|
||
(`move_alias: true`) теперь ждёт блокировку и отвечает 404 `NOT_FOUND`, если алиас уже переехал (arm-core #26, T11.3) —
|
||
это не ошибка клиента, перечитайте список алиасов.
|
||
19. **Периметр при создании** (модалка [42], arm-core #42). После выбора ГК и ГСК модалка показывает все неархивные ЮЛ ГК
|
||
(`GET /directories/legal-entities?company_group_id=<ГК>&closed=false`), отмечены члены выбранной ГСК
|
||
(`GET /directories/related-groups/{id}` → `members[]`); отметки меняются, «Создать» активна при двух и более. Отправляйте
|
||
отмеченные в `legal_entity_ids`. Без поля (или `null`) ядро берёт текущий состав ГСК; пустой список `[]` — это явный пустой
|
||
периметр → 422 `PERIMETER_TOO_SMALL`. ЮЛ не из ГК (или несуществующее) → 422 `ENTITY_NOT_IN_GROUP` с перечнем. ГСК обязательна
|
||
и проверяется (`RELATED_GROUP_MISMATCH`), но в версии **не хранится** (П-2): периметр живёт в `consolidation_member`, в
|
||
справочник переезжает при верификации (Срез 7). Колонку «ГСК» таблицы [34] до верификации заполнить нечем — оставляйте пустой.
|
||
20. **«Ответственный»** на панели «Организации» [34]: `lock.locked_by_name`, если у версии есть активная блокировка, иначе
|
||
`initiator.name`. «Утверждён в РФ» и «Верифицирован в ГО» — маршрут согласования, Срез 7 (в API пока нет; показывайте «—»).
|
||
21. **«К отчетности»** [34] доступна во всех статусах, начиная с `INITIATED` (OQ-43 пересмотрен владельцем 07.10.2026, П-3): свод
|
||
читается в любом статусе; правки свода (сопоставление контрагента) — только «в работе» со своей блокировкой.
|
||
|
||
## 5. Сквозные сценарии экранов
|
||
|
||
| Экран | Вызовы |
|
||
|---|---|
|
||
| [42] Новая консолидация | справочники: ГК → её ГСК (`…/company-groups/{id}/related-groups`, `kind = gsk`) → ЮЛ ГК и члены ГСК (нюанс 19) → `POST /consolidations` с `legal_entity_ids` |
|
||
| [34] Консолидации ГК | `GET /company-groups/{id}/consolidations` (+ `in_work_only`), клик по строке — `GET …/versions/{id}` (панель «Организации»: `members[]` — тип, ячейки ББ/ОФР, `osv_accounts`; «Ответственный» — нюанс 20); «Взять в работу» — `POST …/lock`; меню «…» → «Отмена КФО» — `POST …/status {CANCELLED, comment}` |
|
||
| [36] Состав | `GET …/versions/{id}` (члены), все ЮЛ ГК — справочники, «Сохранить» — `PUT …/members` |
|
||
| [39]/[40] Документы организации | `GET /legal-entities/{id}/files` (все файлы ЮЛ, опрос во время распознавания — им же или `GET /files/{id}`; схема — `dev/api-contract-files-directories.md`), `POST /legal-entities/{id}/files?consolidation_version_id=` |
|
||
| [37] Чек-лист | `GET …/checklist`; «Скачать CSV» — `GET …/checklist?format=csv` (Срез 3b) |
|
||
| [30] ОСВ | `GET …/osv` с фильтрами, `GET …/osv/summary`, «Экспорт» — `GET …/osv/export` |
|
||
| [32-osv-no-inn], [32-osv-map-counterparty] | `GET …/osv?unmapped=true`, `GET …/counterparty-candidates`, «Связать» — `POST …/osv/entries/{entry_id}/counterparty` |
|
||
| [31-00] Предупреждение при уходе с вкладки | `GET …/osv/summary` (`unmapped_total`) |
|