Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
14 KiB
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без ожидания