18 KiB
Context
Панель «Первичные документы» [39] открывается из строки ЮЛ в карточке версии консолидации и показывает
GET /legal-entities/{id}/files — все файлы ЮЛ без фильтра. У одной ГК несколько консолидаций за разные периоды, их
файлы лежат в одном списке ЮЛ. Хранится только привязка подтверждённых документов к версии (consolidation_document,
§3 п. 1); факт «файл загружен в эту консоль» не хранится: consolidation_version_id при загрузке используется для
проверки версии и перехода INITIATED → DOCUMENTS_PROCESSING и забывается.
Целевая картина (владелец, 09.10.2026):
- рабочие версии одного периода делят первичку; различаются периметром;
- верифицированная версия заморожена, первичка зафиксирована в ЕХ (
ex_snapshot_item.stored_file_id, Срез 7); - новая версия после
VERIFIEDможет менять документы, периметр сохраняется; - загрузка в консоль — маршрутом от консолидации.
Текущее состояние ядра: одна консолидация — одна версия (POST /consolidations создаёт версию 1; «новая версия» и
параллельные версии не реализованы). Единственная точка создания stored_file — services/primary_files._store
(загрузка); горячие папки scan-gateway файлов в ядре не создают. Автозамена (services/result_mapping) создаёт
SUPERSEDED у старых файлов по ключу документа (тип, счёт, год, квартал), не по консоли.
Goals / Non-Goals
Goals: панель ЮЛ в версии показывает рабочий стол консоли этого ЮЛ; файл помнит консоль загрузки; маршруты загрузки и списка в контексте консоли — от консолидации; модель совместима с целевой картиной версий без копирования связей. Non-goals: см. proposal.
Модель
stored_file.consolidation_id uuid [ref: > consolidation.id], nullable. Семантика: консолидация (ГК + год + квартал),
в контексте которой файл загружен; NULL — без контекста. Пишется один раз при загрузке. Индекс
(legal_entity_id, consolidation_id) — выборка рабочего стола.
Почему период, а не версия:
| Вариант | Плюс | Минус |
|---|---|---|
consolidation_file(version_id, file_id) |
буквально «загружено в версию» | при второй рабочей версии и новой версии после VERIFIED связи надо копировать; вторая связь рядом с consolidation_document; верифицированная версия и так заморожена привязками и ЕХ |
stored_file.uploaded_in_version_id |
дешевле таблицы | те же проблемы разделения между рабочими версиями |
stored_file.consolidation_id |
рабочие версии периода делят стол автоматически; новая версия после VERIFIED видит стол и подхватывает подтверждённое |
нет истории «в какой версии загружено» — она в FILE_UPLOAD.after_value |
Заморозка остаётся на consolidation_document (после VERIFIED привязки не снимаются) и на ex_snapshot (Срез 7).
Алгоритмы (§9.5)
Загрузка в консоль. POST /consolidations/{cid}/legal-entities/{le}/files (DOCUMENT_EDIT):
консолидация cid видима (policy по ГК)? нет → 404 NOT_FOUND («консолидация не найдена»)
ЮЛ le видимо? нет → 404 NOT_FOUND
есть версия cid в работе (IN_WORK_STATUSES)? нет → 409 CONSOLIDATION_NOT_EDITABLE
le в периметре хотя бы одной такой версии? нет → 422 ENTITY_NOT_IN_PERIMETER
приём файлов как в POST /legal-entities/{le}/files; file.consolidation_id = cid
после приёма ≥ 1 файла, отдельной транзакцией:
для каждой версии cid в работе с le в периметре и статусом INITIATED:
FOR UPDATE → перечитать статус → transition(INITIATED → DOCUMENTS_PROCESSING, changed_by = загрузивший)
Аудит FILE_UPLOAD.after_value дополняется consolidation_id; версии, переведённые в DOCUMENTS_PROCESSING, пишут
CONSOLIDATION_STATUS_CHANGE как сейчас.
Загрузка без контекста. POST /legal-entities/{le}/files — как сейчас, без query; consolidation_id = NULL.
Рабочий стол. GET /consolidations/{cid}/legal-entities/{le}/files (DOCUMENT_VIEW; 404 на невидимую
консолидацию или ЮЛ; limit, offset, include_superseded):
DESK = файлы le с consolidation_id = cid
BOUND = файлы le, у которых есть документ с consolidation_document к любой версии cid
результат = DESK ∪ BOUND, новые первыми (uploaded_at desc, id)
SUPERSEDED — только при include_superseded = true (и для DESK, и для BOUND)
BOUND нужен для файлов, загруженных без контекста или в другой период и подхваченных версией при подтверждении
(ОСВ за Q1, подходящая версиям Q2–Q4, R-14 Среза 4, §3 п. 4). Файл отдаётся той же карточкой FileOut, что и список ЮЛ.
Автозамена. Новый файл после маппинга: если consolidation_id IS NULL, берётся consolidation_id первого
заменённого файла (по порядку id), у которого он не NULL. При загрузке в контексте своё значение сохраняется.
Удаление, подтверждение, привязки — без изменений.
Backfill (миграция 0006): stored_file.consolidation_id = консолидация версии, к которой привязан любой документ
файла; при привязках к версиям разных консолидаций — самый ранний период (год, затем квартал). Файлы без привязок —
NULL. Откат — удаление колонки.
API
| Маршрут | Право | Вход | Ответ | Ошибки |
|---|---|---|---|---|
POST /consolidations/{cid}/legal-entities/{le}/files |
DOCUMENT_EDIT |
multipart files (повторяется) |
201 UploadResult; 422 UploadResult, если не принят ни один |
404; 409 CONSOLIDATION_NOT_EDITABLE; 422 ENTITY_NOT_IN_PERIMETER; 503 STORAGE_UNAVAILABLE |
GET /consolidations/{cid}/legal-entities/{le}/files |
DOCUMENT_VIEW |
limit, offset, include_superseded |
Page[FileOut] |
404 |
POST /legal-entities/{le}/files |
DOCUMENT_EDIT |
как сейчас, без consolidation_version_id |
как сейчас | как сейчас |
GET /legal-entities/{le}/files, GET /files/{id} |
DOCUMENT_VIEW |
как сейчас | FileOut + consolidation_id: uuid | null |
как сейчас |
Фронт (arm-web)
- Стор
consolidations:current_consolidation_idрядом сcurrent_version_id; ставится там же (строка списка —con_id, карточка версии —consolidation_id). services/api/files.ts:uploadFiles(legalEntityId, files, consolidationId?)иlistLegalEntityFiles(legalEntityId, consolidationId?)— с консолидацией маршруты/consolidations/{cid}/…, без неё маршруты ЮЛ.- Панель [39]: список рабочего стола; переключатель «Все документы организации» внизу переключает на список ЮЛ; у файла
с
consolidation_id, отличным от текущего, подпись «2025 К1» (год и квартал из списка консолидаций ГК в сторе), у файла без консолидации — «вне консолидации». Опрос раз в 5 с — как сейчас, по активному списку. - Кнопка «Загрузить документы» недоступна, если открытая версия не «в работе» (статус из строки списка).
- Экран [60] «Монозаёмщики» (когда появится) — панель без консолидации.
Документы
model/data-model-v5.18.dbml (+ changelog в шапке), model/entities-v5.18.md, 04-data-model-guide.md (v5.18 имена
не меняет), 05-domain-rules.md §9.5 п. 1, 3 и новый пункт «Рабочий стол консоли», 10-user-journey.md п. 2.5,
dev/frontend-api-guide.md, dev/api-contract-files-directories.md, dev/api-contract-slice-4.md, dev/qa-guide.md,
07-open-questions.md (новый вопрос о целевой модели версий), ссылки на v5.17 в CLAUDE.md workspace и
repo-templates/*.CLAUDE.md.
Risks / Trade-offs
- Файл с неподходящим периодом, загруженный в консоль, остаётся в её столе без пометки: «не подходит версии» видно только по чек-листу. Приемлемо: владелец выбрал семантику «загружено сюда».
- Два списка (стол и все файлы ЮЛ) с одинаковыми карточками — пользователь может не заметить переключатель. Подпись консолидации у файла и заголовок панели это смягчают.
- Backfill по «самому раннему периоду» для файла, привязанного к версиям разных периодов, — эвристика; таких файлов
на стенде единицы (ОСВ
Q1, подхваченные версиейQ2). - Переход
INITIATED → DOCUMENTS_PROCESSINGу нескольких версий сразу — сейчас версия одна; порядок блокировок (по возрастаниюid, R-2 Среза 4) сохраняется.
Decisions made without discussion
- 09.10.2026 — R-1:
consolidation_file(связь файл–версия) не вводится; полеstored_file.consolidation_idна уровне периода. Обсуждено с владельцем на brainstorming; здесь зафиксировано как итог. Цена ошибки: средняя — при переходе к «файлы по версиям» потребуется таблица связей и миграция данных. - 09.10.2026 — R-2: коды ошибок загрузки в консоль: 404
NOT_FOUND(консолидация/ЮЛ вне видимости, как везде), 409CONSOLIDATION_NOT_EDITABLE(нет версии «в работе»), 422ENTITY_NOT_IN_PERIMETER(существующий код). Цена ошибки: низкая — коды в README и гайде фронта. - 09.10.2026 — R-3: backfill — самый ранний период среди привязок; файлы без привязок остаются
NULL. Цена ошибки: низкая — стенд разработки, данные тестовые. - 09.10.2026 — R-4: при автозамене новый файл без контекста наследует консоль заменённого; с контекстом — своё. Цена ошибки: низкая — влияет на то, в каком столе виден файл; привязки документов не затрагиваются.
- 09.10.2026 — R-5: рабочий стол =
DESK ∪ BOUND(файлы консоли плюс файлы с документами, привязанными к любой версии консоли). БезBOUNDфайл, загруженный без контекста и подхваченный версией, отсутствовал бы в панели при заполненной ячейке чек-листа. Цена ошибки: низкая — лишние строки в панели. - 09.10.2026 — R-6: целевые правила версий («вторая рабочая версия = другой периметр», «новая версия после
VERIFIEDс тем же периметром и правом менять документы») в этом change не реализуются и не специфицируются — фиксируются вопросом в07-open-questions.mdдля среза «версии консолидации». Цена ошибки: низкая — модель файлов с ними совместима. - 09.10.2026 — R-7: замороженный список первички верифицированной версии в панели не строится (ЕХ, Срез 7);
панель из карточки версии не «в работе» показывает стол консоли без кнопки загрузки. Цена ошибки: средняя — если
Срез 7 задержится, у верифицированной версии не будет экрана «что именно использовано»; чек-лист версии частично
закрывает (
document_idsв ячейках). - 09.10.2026 — R-8: query
consolidation_version_idуPOST /legal-entities/{id}/filesудаляется, а не сохраняется для совместимости: единственный клиент — arm-web, меняется в том же выпуске. Цена ошибки: низкая. - 09.10.2026 — R-9: решение владельца 07.10.2026 «фильтра файлов ЮЛ в контексте версии нет и не будет»
(
dev/frontend-api-guide.md) считается пересмотренным решениями 09.10.2026; строка в гайде зачёркнута со ссылкой на docs #90, не удалена. Цена ошибки: низкая — документальная. - 09.10.2026 — R-10: note
recognition_job.reply_deadline(OQ-159, «20 мин») исправлен в v5.18 попутно: владелец просил править при ближайшей версии модели. Цена ошибки: нулевая. - 09.10.2026 — R-11: колонка
stored_file.consolidation_idставится последней в блокеTable stored_fileDBML (вопреки порядку в плане) —check_dbml_vs_ormв arm-core сверяет порядок колонок с физическим, аALTER TABLE ADD COLUMNв миграции 0006 добавляет колонку в конец (какkppв v5.17). Цена ошибки: падениеmake dbml-checkв arm-core; правится перестановкой. - 09.10.2026 — R-12: ссылки на текущую версию модели v5.17 → v5.18 в
context/05-domain-rules.md(заголовок) и скобки об истории версий вcontext/08-source-docs-index.md. Выполнено в коммите64ccfe3(docs #90); в задаче 5 правок не потребовалось, исторические упоминания v5.13…v5.17 не трогаются. Цена ошибки: лишняя правка исторической ссылки. - 09.10.2026 — R-13:
openspec/config.yaml(контекст схемы): ссылки на текущую модель переведены на v5.18 (data-model-v5.18.dbml,entities-v5.18.md); исторические упоминания не трогались. Цена ошибки: низкая — текст контекста для агента, не исполняемый. - 09.10.2026 — R-14 / R-15: формулировка про
SUPERSEDEDодна по смыслу вdev/frontend-api-guide.md,dev/api-contract-files-directories.mdиcontext/05-domain-rules.md§9.5 п. 11: «SUPERSEDED— только приinclude_superseded=true(скрыт по умолчанию)». Цена ошибки: разночтение, правится текстом. - 09.10.2026 — R-16: push ветки и создание PR выполняет контроллер, а не исполнитель Task 5 (шаг 6 плана). Цена ошибки: нулевая.
- 09.10.2026 — R-17: трейлер коммита Task 5 исправлен amend'ом локального непушнутого коммита на
Co-Authored-By: Claude Fable 5.1по явному требованию задачи. Цена ошибки: переписана одна локальная запись.