docs(openspec): change front-api-gaps — периметр при создании, ячейки чек-листа, участник периметра (#42)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
This commit is contained in:
parent
4395c1a3f3
commit
0101bca165
2
openspec/changes/front-api-gaps/.openspec.yaml
Normal file
2
openspec/changes/front-api-gaps/.openspec.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-10-07
|
||||
105
openspec/changes/front-api-gaps/design.md
Normal file
105
openspec/changes/front-api-gaps/design.md
Normal file
@ -0,0 +1,105 @@
|
||||
# Design: API до сводной ОСВ — пробелы по макету и комментариям фронтенда
|
||||
|
||||
## Context
|
||||
|
||||
Срезы 4 и 3b закрыли API пути пользователя до сводной ОСВ по `10-user-journey.md`. Аудит «экраны макета ↔ API»
|
||||
(07.10.2026, находки П-1…П-14) и комментарии фронтенд-разработчика показали три места, где экран собирается только
|
||||
цепочкой вызовов или не собирается вовсе:
|
||||
|
||||
- модалка [42] «Новая консолидация» — в коде макета (`docs/mockup/dkb_24.09.26/dkb_24.09.26.html`, блок «ЮЛ
|
||||
консолидации») после выбора ГК и ГСК показывает все неархивные ЮЛ ГК, отмечены члены ГСК, отметки меняются, минимум
|
||||
два; создаётся консолидация с отмеченным списком. Снимок `42-new-consolidation.png` снят до выбора ГК, поэтому
|
||||
`10-user-journey.md` шаг 2.1 и `mockup/INDEX.md` этот блок не описывают (П-17);
|
||||
- панель «Организации» [34] — ячейки чек-листа участника позиционные (П-15); нет «Тип» и перечня счетов ОСВ (П-9);
|
||||
- комментарий фронта «нет ручки для файлов ЮЛ» — эндпоинт есть (`GET /legal-entities/{id}/files`, Срез 2c), но не описан
|
||||
в контракте для фронта (П-5).
|
||||
|
||||
Модель данных не меняется: всё вычисляется из существующих таблиц DBML v5.16.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:** экран [42] создаёт консолидацию одним запросом с выбранным периметром; экран [34] строится из одной карточки
|
||||
версии; обратная совместимость ответов (поля только добавляются, вход без `legal_entity_ids` ведёт себя как прежде).
|
||||
|
||||
**Non-Goals:** см. `proposal.md` «Non-goals» — хранение ГСК версии, поля Среза 7, `period_check` (OQ-120), фильтр
|
||||
файлов по версии, П-4/П-6/П-11/П-12/П-13, DBML и контракт распознавания.
|
||||
|
||||
## Decisions
|
||||
|
||||
### D-1. Периметр при создании — необязательное `legal_entity_ids` (решение владельца, П-17)
|
||||
|
||||
`ConsolidationCreate.legal_entity_ids: list[uuid] | None = Field(default=None, max_length=MAX_MEMBERS)`. Без поля —
|
||||
прежнее поведение (члены ГСК, `PERIMETER_TOO_SMALL` по ГСК). С полем — ровно выбранные ЮЛ ГК. ГСК остаётся
|
||||
обязательной: модалка требует её всегда, по ней предзаполняются отметки, проверка `RELATED_GROUP_MISMATCH` сохраняется.
|
||||
ГСК в версии не хранится (П-2): `consolidation.related_group_id` — NULL до верификации.
|
||||
|
||||
Проверка списка — та же, что у `PUT …/members`: дубли схлопываются в порядке первого вхождения,
|
||||
`repo.entities_in_group` → 422 `ENTITY_NOT_IN_GROUP` с перечнем, затем `PERIMETER_TOO_SMALL`. Выношу её в функцию
|
||||
сервиса `_validate_perimeter(company_group_id, ids) -> list[uuid]`, которую вызывают `create` и `set_members`: правила не
|
||||
разойдутся. В `create` проверка стоит на месте проверки числа членов ГСК — после ГСК, до проверки периода (порядок
|
||||
ошибок не меняется для запросов без списка).
|
||||
|
||||
`_insert` получает список ЮЛ (сейчас — сущности ГСК); подхват документов и аудит — как сейчас, по фактическому периметру.
|
||||
Архивность ЮЛ отдельно не проверяется: принадлежность ГК определяется `legal_entity.company_group_id`, как в
|
||||
`PUT …/members` (выведенное из ГК ЮЛ ГК уже не принадлежит).
|
||||
|
||||
Альтернатива «фронт делает create → lock → PUT members» отвергнута: не атомарна, два события аудита, не позволяет ГСК
|
||||
из одного ЮЛ.
|
||||
|
||||
### D-2. Самоописывающие ячейки чек-листа (подход B)
|
||||
|
||||
`ChecklistCellOut` получает `document_type: DocumentType` и `account: str | None` правила. `_cell_out(rule, cell)`
|
||||
заполняет их в обоих местах — карточка версии и `GET …/checklist`; порядок ячеек прежний. Доменный `checklist.build`
|
||||
и `checklist_csv` не меняются (CSV строится из доменных ячеек и правил).
|
||||
|
||||
Альтернатива A (`rules[]` на уровне карточки) отвергнута: сохраняет позиционную связь, на которой споткнулся фронт.
|
||||
Повтор двух коротких полей в каждой ячейке дешевле ошибки сопоставления по индексу.
|
||||
|
||||
### D-3. Участник периметра: `entity_type` и `osv_accounts` (вариант B)
|
||||
|
||||
`MemberOut.entity_type: EntityType` — из `legal_entity`. `MemberOut.osv_accounts: list[str]` — новый запрос репозитория
|
||||
`osv_accounts(session, entity_ids, year, quarter) -> dict[uuid, list[str]]` одним `SELECT DISTINCT legal_entity_id,
|
||||
osv_account_code` по `document ⋈ stored_file`: `document_type = OSV`, `period_year = year`, `period_quarter IN
|
||||
quarters_through(quarter)`, `document.status != ERROR`, `stored_file.status NOT IN (SUPERSEDED, ERROR)`,
|
||||
`osv_account_code IS NOT NULL`. Сортировка — в Python `account_sort_key` (та же натуральная, что у столбцов чек-листа);
|
||||
ЮЛ без документов — пустой список.
|
||||
|
||||
Смысл колонки — «какие счета загружены к периоду версии», а не «что подойдёт своду»: подходящесть по §3 (нарастающая
|
||||
vs квартальная, Q1-эквивалентность) здесь не применяется, готовность счетов комплекта видна по ячейкам чек-листа.
|
||||
Квартал «не позже квартала версии», потому что свод склеивает квартальные ОСВ Q1…Qn.
|
||||
|
||||
### D-4. Документы — bounded-PR в docs после реализации
|
||||
|
||||
Контракт для фронта (файлы и документы, справочники, новые поля, правило «Ответственный» = `lock.locked_by_name` при
|
||||
активной блокировке, иначе `initiator.name`), OQ-43 (П-3), формулировка баннера (П-1), шаг 2.1 пути и [42] в
|
||||
`mockup/INDEX.md`, устаревшее «CSV в Срезе 4 нет». Сквозного change в docs нет: модель и контракт обмена не меняются.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Шире ответ карточки** — `osv_accounts` добавляет один запрос на карточку (индексы `document(legal_entity_id)` есть);
|
||||
на реальных группах — десятки ЮЛ, нагрузка несущественна.
|
||||
- **Фронт может спутать `osv_accounts` с «готово для свода»** — в контракте прямо сказать, что готовность — в ячейках
|
||||
чек-листа.
|
||||
- **Выбранный периметр без членов ГСК** допустим (отметки можно снять все, кроме двух любых ЮЛ ГК) — так в макете; ГСК
|
||||
версии в Срезе 7 будет определяться периметром, а не выбранной ГСК (П-2).
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Миграций нет. Ответы только расширяются; запросы без `legal_entity_ids` работают как прежде. Откат — revert PR.
|
||||
|
||||
## Open Questions
|
||||
|
||||
Новых нет. Решения П-1/П-2/П-3 владельца — в docs-PR (OQ-43 переписывается).
|
||||
|
||||
## Decisions made without discussion
|
||||
|
||||
| № | Дата | Решение | Обоснование | Цена ошибки |
|
||||
|---|---|---|---|---|
|
||||
| 1 | 2026-10-07 | Явный периметр не обязан включать членов выбранной ГСК | так в макете (отметки ГСК снимаются); ГСК версии не хранится (П-2) | версия без ЮЛ ГСК — исправляется `PUT …/members` |
|
||||
| 2 | 2026-10-07 | Архивность ЮЛ при создании не проверяется отдельно — только `company_group_id`, как в `PUT …/members` | одно правило периметра для создания и правки | фронт может отправить ЮЛ, которое в макете скрыто как архивное; ядро примет, если ЮЛ ещё в ГК |
|
||||
| 3 | 2026-10-07 | `legal_entity_ids: null` равнозначно отсутствию поля | pydantic `None` по умолчанию; фронту проще | — |
|
||||
| 4 | 2026-10-07 | `osv_accounts` не учитывает документы без квартала и без кода счёта | их не к чему отнести в колонке; пустой код — дефект распознавания, виден в drawer | ОСВ без периода не видна в колонке «ОСВ» до правки периода |
|
||||
| 5 | 2026-10-07 | Порядок проверок создания: ГК → ГСК → периметр → период | периметр зависит от ГК и ГСК, конфликт периода — последним, как сейчас | при одновременной ошибке периметра и занятом периоде клиент увидит 422, а не 409 |
|
||||
| 6 | 2026-10-07 | Фильтр файлов ЮЛ по версии не делается (решение владельца, вопрос 1, вариант A) | drawer [39] показывает все документы ЮЛ; OQ-120 | — |
|
||||
| 7 | 2026-10-07 | `osv_accounts` — документы за год версии с кварталом ≤ версии в любом статусе, кроме `ERROR` (решение владельца, вопрос 2, вариант B) | «что загружено к периоду»; свод склеивает кварталы | — |
|
||||
| 8 | 2026-10-07 | Ячейки чек-листа самоописывающие, без `rules[]` в карточке (решение владельца, подход B) | фронт не сопоставляет по индексу | — |
|
||||
65
openspec/changes/front-api-gaps/proposal.md
Normal file
65
openspec/changes/front-api-gaps/proposal.md
Normal file
@ -0,0 +1,65 @@
|
||||
# Proposal: API до сводной ОСВ — пробелы по макету и комментариям фронтенда
|
||||
|
||||
**Срез:** доработка Срезов 4 и 3b (`../docs/context/09-delivery-plan.md`) — API пути пользователя до сводной ОСВ.
|
||||
**Репозиторий:** arm-core. **Issue:** RSHB/arm-core#42. Документация — bounded-PR в docs (контракт для фронта, OQ-43,
|
||||
`10-user-journey.md`, `mockup/INDEX.md`) после реализации; сквозного change нет: DBML и контракт распознавания не
|
||||
меняются.
|
||||
|
||||
## Why
|
||||
|
||||
Аудит «экраны макета ↔ API» (07.10.2026) и первые комментарии фронтенд-разработчика показали, что три экрана пути
|
||||
пользователя нельзя собрать без обходных цепочек вызовов:
|
||||
|
||||
- **[42] «Новая консолидация»** — в макете после выбора ГК и ГСК открывается блок «ЮЛ консолидации»: все неархивные ЮЛ
|
||||
ГК, отмечены члены выбранной ГСК, отметки меняются, «Создать» активна при двух и более отмеченных. `POST /consolidations`
|
||||
берёт периметр только из состава ГСК; обход «создать → взять в работу → `PUT …/members`» не атомарен, пишет два события
|
||||
аудита и не спасает ГСК из одного ЮЛ (422 `PERIMETER_TOO_SMALL` по ГСК).
|
||||
- **[34] панель «Организации»** — ячейки `members[].checklist[]` карточки версии позиционные: какая ячейка ББ, ОФР или
|
||||
ОСВ 60, фронт узнаёт только из второго запроса `GET …/checklist` (`rules[]`). Колонок «Тип» и «ОСВ» (перечень счетов
|
||||
загруженных ОСВ, в макете — и вне комплекта: 51, 52, 55) взять неоткуда.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **`POST /consolidations`** — необязательное `legal_entity_ids` (≤ 500): без него периметр — текущие члены ГСК, как
|
||||
сейчас; с ним — ровно выбранные ЮЛ ГК (422 `ENTITY_NOT_IN_GROUP`, `PERIMETER_TOO_SMALL` — по выбранным после
|
||||
схлопывания дублей). ГСК остаётся обязательной и проверяется (`RELATED_GROUP_MISMATCH`), в версии не хранится.
|
||||
Одна транзакция, одно событие `CONSOLIDATION_CREATE` с выбранным периметром.
|
||||
- **Ячейки чек-листа самоописывающие** — `ChecklistCellOut` получает `document_type` и `account` своего правила; меняются
|
||||
и карточка версии, и `GET …/checklist` (поля только добавляются; CSV не меняется).
|
||||
- **Участник периметра в карточке** — `MemberOut.entity_type` (`ЮЛ`/`ИП`) и `MemberOut.osv_accounts` — счета действующих
|
||||
ОСВ-документов ЮЛ за год версии с кварталом не позже квартала версии, в натуральном порядке.
|
||||
|
||||
Таблицы DBML v5.16 (только чтение, без изменений): `consolidation`, `consolidation_version`, `consolidation_member`,
|
||||
`legal_entity`, `related_group`, `related_group_member`, `document`, `stored_file`, `required_document_rule`,
|
||||
`audit_event`. Правила: `05-domain-rules.md` §9 (статусы версии), §3 п. 1 (подходящесть документа версии — не меняется).
|
||||
|
||||
## Решения владельца (07.10.2026)
|
||||
|
||||
- **П-1:** баннер «Осталось: N» и «Показать ЮЛ без ИНН» — все несопоставленные строки свода (`unmapped`,
|
||||
`unmapped_total`); API не меняется, меняется описание в docs.
|
||||
- **П-2:** ГСК в версии не хранится; периметр живёт в `consolidation_member`, в справочник переезжает по факту
|
||||
верификации (Срез 7). Колонка «ГСК» на [34] до верификации не заполняется.
|
||||
- **П-3:** допущение OQ-43 пересматривается — «К отчетности» доступна и в статусах «в работе» (только docs; API читает
|
||||
свод в любом статусе).
|
||||
- Фильтр файлов ЮЛ по версии не делается: drawer [39] в макете показывает все документы ЮЛ; существующий
|
||||
`GET /legal-entities/{id}/files` описывается в контракте; «Проверка: Период» остаётся «—» (OQ-120).
|
||||
- Колонка «ОСВ» — действующие документы за год версии до её квартала в любом статусе (вариант B).
|
||||
- Ячейки чек-листа — самоописывающие, без `rules[]` в карточке (подход B).
|
||||
|
||||
## Non-goals
|
||||
|
||||
- UI (делает другой разработчик).
|
||||
- Хранение ГСК версии, «Утверждён в РФ» / «Верифицирован в ГО» / «Дата верификации» (Срез 7).
|
||||
- `period_check` документа относительно версии (OQ-120), фильтр файлов по версии.
|
||||
- Шапка формы ОКЕИ/ОПФ/форма собственности/аудит (П-4, нужна новая версия контракта), монозаёмщики [60] (П-6),
|
||||
поиск ГК по РФ (П-11), «Прочие документы» в чек-листе (П-12), единицы в экспорте (П-13).
|
||||
- Изменение DBML, миграции, контракт распознавания.
|
||||
|
||||
## Impact
|
||||
|
||||
- `api/src/schemas/consolidations.py` (`ConsolidationCreate`, `ChecklistCellOut`, `MemberOut`),
|
||||
`api/src/services/consolidations.py` (`create`, `set_members`, `_card`, `checklist`, `_cell_out`),
|
||||
`api/src/repositories/consolidations.py` (счета ОСВ участников).
|
||||
- Тесты: `test_api_consolidations.py`, `test_api_consolidation_members.py`, `test_api_consolidation_checklist.py`,
|
||||
`test_schemas_consolidations.py`, `test_openapi_routes.py`.
|
||||
- Фронт: новые поля только добавляются; существующие клиенты не ломаются.
|
||||
147
openspec/changes/front-api-gaps/specs/consolidations-api/spec.md
Normal file
147
openspec/changes/front-api-gaps/specs/consolidations-api/spec.md
Normal file
@ -0,0 +1,147 @@
|
||||
# Spec Delta: consolidations-api
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Создание консолидации
|
||||
`POST /consolidations` (`CONSOLIDATION_EDIT`, policy по ГК) с телом `{company_group_id, related_group_id, year, quarter,
|
||||
legal_entity_ids?}` SHALL в одной транзакции создать `consolidation` (`reporting_date`: Q1 → 01.04.year, Q2 → 01.07.year,
|
||||
Q3 → 01.10.year, Q4 → 01.01.year+1; ASSUMPTION(OQ-40)), `consolidation_version` №1 (`INITIATED`, `scenario = CREATE`,
|
||||
`approach = BASIC`, `goodwill_enabled = false`, `initiator_id` и `initiator_office_id` из пользователя,
|
||||
`is_in_work = true`), `consolidation_member` периметра (`is_head_entity = false`), первую запись
|
||||
`consolidation_status_history` (`from_status` NULL → `INITIATED`), подхват подтверждённых документов ЮЛ периметра (см.
|
||||
`documents-api` «Подхват документов») и одно событие `audit_event(CONSOLIDATION_CREATE)`, у которого
|
||||
`after_value.members` — отсортированный список ЮЛ периметра; ответ 201 с карточкой версии. Периметр SHALL быть: без
|
||||
`legal_entity_ids` (поле отсутствует или `null`) — текущие члены ГСК; с `legal_entity_ids` — ровно эти ЮЛ после
|
||||
схлопывания дублей (порядок первого вхождения), каждое SHALL принадлежать ГК консолидации
|
||||
(`legal_entity.company_group_id`), иначе 422 `ENTITY_NOT_IN_GROUP` с перечнем `legal_entity_ids` чужих в порядке
|
||||
запроса; состав ГСК при явном списке не проверяется и не меняется. ГСК SHALL быть обязательной, принадлежать ГК и иметь
|
||||
`kind = gsk`, иначе 422 `RELATED_GROUP_MISMATCH`. Периметр меньше двух ЮЛ (членов ГСК без списка либо выбранных ЮЛ после
|
||||
схлопывания) → 422 `PERIMETER_TOO_SMALL`; пара (ГК, год, квартал) уже есть → 409 `CONSOLIDATION_EXISTS` (в том числе при
|
||||
гонке двух запросов — UNIQUE `(company_group_id, year, quarter)`); `year` вне 2000..2100 → 422. Порядок проверок: ГК
|
||||
видима (404) → ГСК видима (404) и соответствует ГК (422) → периметр (422) → период свободен (409). Блокировка на
|
||||
создателя MUST NOT ставиться; `MEMBERSHIP_CHANGE` при создании MUST NOT писаться. `related_group_id` в `consolidation`
|
||||
SHALL оставаться NULL (фиксируется при верификации, Срез 7); `related_group_member` MUST NOT изменяться.
|
||||
|
||||
#### Scenario: Консолидация РБ за Q3 2024
|
||||
- **WHEN** `ka_rf` Санкт-Петербургского РФ создаёт консолидацию ГК «Русский бисквит», ГСК из двух ЮЛ, 2024, Q3, без
|
||||
`legal_entity_ids`
|
||||
- **THEN** 201; версия №1 `INITIATED`, `reporting_date = 2024-10-01`, два `consolidation_member`, история с одной записью,
|
||||
`audit_event(CONSOLIDATION_CREATE)` с `consolidation_version_id`; активной блокировки нет
|
||||
|
||||
#### Scenario: Выбранный периметр шире ГСК
|
||||
- **WHEN** в ГК три ЮЛ, в ГСК два из них, создаётся консолидация с `legal_entity_ids` из всех трёх ЮЛ ГК
|
||||
- **THEN** 201; в `members` три ЮЛ; `after_value.members` события `CONSOLIDATION_CREATE` — три ЮЛ; состав ГСК не изменился
|
||||
|
||||
#### Scenario: ГСК из одного ЮЛ, второе отмечено вручную
|
||||
- **WHEN** в ГСК один текущий член, в `legal_entity_ids` он и ещё одно ЮЛ той же ГК
|
||||
- **THEN** 201; в `members` два ЮЛ
|
||||
|
||||
#### Scenario: ГСК из одного ЮЛ без списка
|
||||
- **WHEN** в ГСК один текущий член, `legal_entity_ids` не передан
|
||||
- **THEN** 422 `PERIMETER_TOO_SMALL`; `consolidation` и версия не созданы
|
||||
|
||||
#### Scenario: ЮЛ другой ГК в списке
|
||||
- **WHEN** в `legal_entity_ids` попало ЮЛ, `company_group_id` которого не равен ГК консолидации
|
||||
- **THEN** 422 `ENTITY_NOT_IN_GROUP` с этим ЮЛ в `legal_entity_ids`; `consolidation` не создана
|
||||
|
||||
#### Scenario: Дубли схлопываются до одного ЮЛ
|
||||
- **WHEN** `legal_entity_ids` — один и тот же идентификатор дважды
|
||||
- **THEN** 422 `PERIMETER_TOO_SMALL`
|
||||
|
||||
#### Scenario: Повтор периода
|
||||
- **WHEN** консолидация ГК за 2024 Q3 уже есть, создаётся ещё одна
|
||||
- **THEN** 409 `CONSOLIDATION_EXISTS`
|
||||
|
||||
### Requirement: Список консолидаций ГК и карточка версии
|
||||
`GET /company-groups/{id}/consolidations` (`CONSOLIDATION_VIEW`, policy) SHALL возвращать периоды ГК (`consolidation_id`,
|
||||
`year`, `quarter`, `reporting_date`) по убыванию года и квартала, в каждом — версии по убыванию `version_no`
|
||||
(`id`, `version_no`, `status`, `scenario`, `approach`, `lock` — активная блокировка или null, `created_at`,
|
||||
`members_count`); параметр `in_work_only=true` SHALL оставлять только версии со статусом не из
|
||||
{`VERIFIED`, `CANCELLED`, `ARCHIVED`, `REJECTED`}. `GET /consolidations/versions/{id}` SHALL отдавать карточку: поля
|
||||
версии и консолидации, `company_group {id, name}`, инициатора и его офис, `lock`, `members[]`, `history[]`
|
||||
(`from_status`, `to_status`, `changed_by`, `changed_at`, `comment`). Член периметра SHALL содержать `id`, `name`, `inn`,
|
||||
`entity_type` (`ЮЛ`/`ИП`, из `legal_entity`), `is_head_entity`, `checklist[]` (ячейки по правилам подхода, см.
|
||||
«Чек-лист комплекта») и `osv_accounts[]` — уникальные `osv_account_code` документов ЮЛ, у которых `document_type = OSV`,
|
||||
`period_year` равен году версии, `period_quarter` не позже квартала версии, статус документа не `ERROR`, файл не в
|
||||
`SUPERSEDED` и не в `ERROR`; документы без квартала или без кода счёта не учитываются; статус подтверждения и привязка к
|
||||
версии не важны; порядок — натуральная сортировка кода счёта (как столбцы чек-листа: `01 < 51 < 58.03 < 60`). Консолидация
|
||||
ГК вне видимости и несуществующий идентификатор — 404 `NOT_FOUND` с сообщением `CONSOLIDATION_NOT_FOUND` /
|
||||
`VERSION_NOT_FOUND` и одинаковым телом (так же во всех спецификациях среза: идентификаторы `…_NOT_FOUND` — текст
|
||||
сообщения при фиксированном коде `NOT_FOUND`).
|
||||
|
||||
#### Scenario: Дерево версий
|
||||
- **WHEN** у ГК консолидации 2024 Q1 (версии 1 и 2) и 2024 Q3 (версия 1), запрошен список
|
||||
- **THEN** первым период 2024 Q3, затем 2024 Q1 с версиями `[2, 1]`; `members_count` у каждой версии
|
||||
|
||||
#### Scenario: Чужой филиал
|
||||
- **WHEN** `ka_rf` другого филиала запрашивает карточку версии
|
||||
- **THEN** 404 `NOT_FOUND` (сообщение `VERSION_NOT_FOUND`), тело совпадает с ответом на случайный `uuid`
|
||||
|
||||
#### Scenario: Счета ОСВ участника
|
||||
- **WHEN** версия за 2024 Q3; у ЮЛ есть ОСВ 60 за Q1 2024 (`RECOGNIZED`), ОСВ 51 за Q3 2024 (`CONFIRMED`), ОСВ 58.03 за
|
||||
Q3 2024 (`NEEDS_CHECK`), ОСВ 62 за Q4 2024, ОСВ 01 за 2023, ОСВ 76 в заменённом (`SUPERSEDED`) файле и ОСВ 90 в
|
||||
документе `ERROR`
|
||||
- **THEN** `osv_accounts = ["51", "58.03", "60"]`
|
||||
|
||||
#### Scenario: Тип ЮЛ
|
||||
- **WHEN** в периметре индивидуальный предприниматель
|
||||
- **THEN** у него `entity_type = "ИП"`, у остальных — `"ЮЛ"`
|
||||
|
||||
### Requirement: Чек-лист комплекта
|
||||
`GET /consolidations/versions/{id}/checklist` (`CONSOLIDATION_VIEW`, policy) SHALL вернуть `rules[]` (правила
|
||||
`required_document_rule` подхода версии: `document_type`, `account`, `is_mandatory`; порядок — `BALANCE`, `INCOME`,
|
||||
затем ОСВ по коду счёта) и `rows[]` по ЮЛ периметра (по имени) с `cells[]` в порядке `rules`. Ячейка `{document_type,
|
||||
account, status, missing_quarters, document_ids}`: `document_type` и `account` — правила ячейки (ячейка
|
||||
самоописывающая, фронт не опирается на позицию); `CONFIRMED` — пара (ЮЛ, правило) полностью покрыта привязанными
|
||||
документами (ОСВ: по правилу выбора источников `consolidated-osv`; ББ/ОФР: документ за квартал версии);
|
||||
`NEEDS_CHECK` / `RECOGNIZED` — есть неподтверждённый документ ЮЛ того же типа, счёта и года, подходящий версии
|
||||
(приоритет `NEEDS_CHECK`); `PROCESSING` — у ЮЛ есть файл `UPLOADED` или `PROCESSING`; `ERROR` — последний по
|
||||
`uploaded_at` файл ЮЛ в `ERROR`; иначе `MISSING`. `missing_quarters` SHALL перечислять кварталы без квартальной ОСВ при
|
||||
частичном покрытии. Правило ОСВ SHALL закрываться только документом с `osv_account_code`, равным `account` правила
|
||||
(OQ-04). Те же ячейки (с `document_type` и `account`, в том же порядке) SHALL отдаваться в карточке версии у каждого
|
||||
члена. С `?format=csv` SHALL отдаваться тот же чек-лист файлом `text/csv; charset=utf-8` с BOM и разделителем `;`:
|
||||
заголовок «Организация;ИНН;<правило>…» (правила — «Бух. баланс», «ОФР», «ОСВ <счёт>»), ячейки — подписи макета:
|
||||
`CONFIRMED` «Загружен», `PROCESSING` «Обработка», `NEEDS_CHECK`/`RECOGNIZED` «Требуется проверка», `ERROR` «Ошибка
|
||||
загрузки», `MISSING` «Отсутствует» (с « (нет: Q1, Q2)» при `missing_quarters`); `Content-Disposition` — `attachment` с
|
||||
именем `checklist-<ГК>-<год>-<квартал>.csv` в RFC 5987.
|
||||
|
||||
#### Scenario: Пробел Q2
|
||||
- **WHEN** у ЮЛ привязаны квартальные ОСВ 60 за Q1 и Q3 2024, версия за 2024 Q3
|
||||
- **THEN** ячейка ОСВ 60: `document_type = OSV`, `account = "60"`, `status = MISSING`, `missing_quarters = ["Q2"]`,
|
||||
`document_ids` — два документа
|
||||
|
||||
#### Scenario: Субсчёт не закрывает правило
|
||||
- **WHEN** у ЮЛ подтверждена только ОСВ 58.03 за квартал версии
|
||||
- **THEN** ячейка ОСВ 58 — `MISSING`
|
||||
|
||||
#### Scenario: Файл в обработке
|
||||
- **WHEN** у ЮЛ загружен файл, задание ещё не завершено, других документов нет
|
||||
- **THEN** все ячейки ЮЛ — `PROCESSING`
|
||||
|
||||
#### Scenario: Ячейки карточки подписаны
|
||||
- **WHEN** запрошена карточка версии подхода `BASIC`
|
||||
- **THEN** у каждого члена `checklist[i].document_type` и `checklist[i].account` равны `rules[i]` чек-листа той же
|
||||
версии; ячейка ББ — `document_type = BALANCE`, `account = null`
|
||||
|
||||
#### Scenario: Чек-лист в CSV
|
||||
- **WHEN** запрошен чек-лист версии с `format=csv`, у ЮЛ подтверждён ББ, ОСВ 60 отсутствует за Q2
|
||||
- **THEN** файл начинается с BOM, первая строка — «Организация;ИНН;Бух. баланс;ОФР;ОСВ 60…», в строке ЮЛ — «Загружен» и «Отсутствует (нет: Q2)»
|
||||
|
||||
### Requirement: Границы входных данных
|
||||
Входы консолидаций SHALL иметь верхние границы: `legal_entity_ids` тела `PUT …/members` и тела `POST /consolidations` —
|
||||
не больше 500 идентификаторов, `comment` тела `POST …/status` — не больше 1000 символов, `q` фильтров `GET …/osv` и
|
||||
экспорта — не больше 500 символов. Превышение SHALL давать 422 со списком ошибок валидации (`detail[].loc` указывает
|
||||
поле) до обращения к БД. Версия консолидации SHALL проверяться на видимость до блокировки `FOR UPDATE`: невидимая
|
||||
версия отвечает 404 сразу, не ожидая транзакцию, которая держит её строку.
|
||||
|
||||
#### Scenario: Периметр длиннее предела
|
||||
- **WHEN** в `PUT …/members` передано 501 ЮЛ
|
||||
- **THEN** 422, `detail[0].loc = ["body", "legal_entity_ids"]`, периметр не изменился
|
||||
|
||||
#### Scenario: Периметр создания длиннее предела
|
||||
- **WHEN** в `POST /consolidations` передано 501 ЮЛ в `legal_entity_ids`
|
||||
- **THEN** 422, `detail[0].loc = ["body", "legal_entity_ids"]`, консолидация не создана
|
||||
|
||||
#### Scenario: Невидимая версия при занятой строке
|
||||
- **WHEN** версию чужого филиала держит `FOR UPDATE` другая транзакция, пользователь без видимости вызывает `POST …/lock`
|
||||
- **THEN** 404 `VERSION_NOT_FOUND` без ожидания
|
||||
17
openspec/changes/front-api-gaps/tasks.md
Normal file
17
openspec/changes/front-api-gaps/tasks.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Tasks: API до сводной ОСВ — пробелы по макету и комментариям фронтенда
|
||||
|
||||
План реализации — `docs/superpowers/plans/2026-10-07-front-api-gaps.md` (пишется после одобрения спеки). Репозиторий —
|
||||
arm-core; команды — из корня (`make`), `pytest` — из `api/`.
|
||||
|
||||
- [ ] 1 Создание консолидации с выбранным периметром: `ConsolidationCreate.legal_entity_ids`, общая проверка периметра
|
||||
для `create` и `set_members` (`services/consolidations.py`), тесты `test_api_consolidations.py`,
|
||||
`test_schemas_consolidations.py`
|
||||
- [ ] 2 Самоописывающие ячейки чек-листа: `ChecklistCellOut.document_type/account`, `_cell_out(rule, cell)`, тесты
|
||||
`test_api_consolidation_checklist.py`, карточка версии
|
||||
- [ ] 3 Участник периметра: `MemberOut.entity_type`, `MemberOut.osv_accounts` (`repositories/consolidations.osv_accounts`),
|
||||
тесты карточки версии
|
||||
- [ ] 4 OpenAPI (`test_openapi_routes.py`), README «Консолидации», CLAUDE.md репозитория
|
||||
- [ ] 5 Документы workspace (bounded-PR в docs): контракт для фронта (файлы/документы, справочники, новые поля,
|
||||
«Ответственный»), OQ-43 (П-3), баннер (П-1), `10-user-journey.md` шаг 2.1 и строка про CSV,
|
||||
`mockup/INDEX.md` [42], `dev/api-contract-slice-4.md`
|
||||
- [ ] 6 Завершение: `make check-full`, `openspec validate --strict`, финальное ревью ветки, PR arm-core (Closes #42)
|
||||
Loading…
x
Reference in New Issue
Block a user