docs(openspec): change auto-confirm-clean-documents — спека автоподтверждения чистых документов (#64)
Proposal, design (D-1…D-6, решения без обсуждения №1–7) и дельты osv-result-mapping, primary-files, audit-trail: чистый документ при маппинге сразу CONFIRMED (система, confirmed_by NULL; переход версии — от загрузившего), удаление автоподтверждённых файлов до DATA_CORRECTION включительно. Решение владельца 08.10.2026, ТЗ 03_9 п. 4. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
d8bf5b22dc
commit
3623358fb4
@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-10-08
|
||||
111
openspec/changes/auto-confirm-clean-documents/design.md
Normal file
111
openspec/changes/auto-confirm-clean-documents/design.md
Normal file
@ -0,0 +1,111 @@
|
||||
# Design: автоподтверждение чистых документов
|
||||
|
||||
## Context
|
||||
|
||||
Маппинг результата (`services/result_mapping.py`, `_map_candidate`) в одной транзакции: `map_file` создаёт документы
|
||||
файла со статусом `RECOGNIZED` или `NEEDS_CHECK` (ОСВ — `domain/osv_mapping.document_status`, §9.3; формы —
|
||||
`domain/statement_mapping`, §9.5 D-6; `DISCLOSURE`/`OTHER` — всегда `RECOGNIZED`), автозамена снимает привязки
|
||||
документов прежних файлов (`on_documents_superseded`), затем хук `on_documents_mapped` переводит версии в `DATA_CORRECTION`
|
||||
по документам `NEEDS_CHECK` (`changed_by = file.uploaded_by`, design №2 Среза 4). Привязки к версиям создаёт только
|
||||
ручное подтверждение (`DocumentService.confirm` → `on_document_confirmed`) и подхват при создании версии и правке
|
||||
периметра (`bind_confirmed`). Удаление файла (`PrimaryFilesService.delete`) запрещено при любом `CONFIRMED` документе.
|
||||
|
||||
Порядок блокировок (`CLAUDE.md` arm-core): задание → файл → документы → advisory ГК → версии по `id`. Маппинг уже
|
||||
держит задание, файл и документы, когда вызывает хуки версий.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:** чистый документ сразу `CONFIRMED` и в своде без действий аналитика; поведение привязки и статусов версии —
|
||||
то же, что при ручном подтверждении; ошибочно загруженный чистый файл можно удалить, пока версия не ушла дальше
|
||||
корректировки.
|
||||
|
||||
**Non-Goals:** см. proposal — новые поля, UI, миграция данных, пересмотр `NEEDS_CHECK`.
|
||||
|
||||
## Decisions
|
||||
|
||||
### D-1. Где: шаг маппинга после автозамены
|
||||
`_map_candidate`: `map_file` → автозамена (как сейчас) → **`auto_confirm(session, file, documents, now)`** →
|
||||
`on_documents_mapped`. После автозамены — чтобы привязки прежних версий файла были уже сняты, а замена по ключу (R-1)
|
||||
работала как при ручном подтверждении. До `on_documents_mapped` — тот смотрит только `NEEDS_CHECK`, автоподтверждённые
|
||||
его не касаются. Всё в одной транзакции маппинга: при ошибке откатывается и маппинг, и подтверждение (повтор задания —
|
||||
по счётчику попыток, как сейчас).
|
||||
|
||||
### D-2. Кто: система за документ, загрузивший — за переход версии (решение владельца 08.10.2026)
|
||||
`document.confirmed_by = NULL`, `confirmed_at = now`; у формы `financial_statement_meta.reviewed_by = NULL`,
|
||||
`reviewed_at = now`. `audit_event(DOCUMENT_CONFIRM, PRIMARY_DOCUMENTS, object_type = document)`: `user_id = NULL`,
|
||||
`before_value.status = RECOGNIZED`, `after_value.status = CONFIRMED`, `description = «автоподтверждение: без замечаний»`
|
||||
(DBML: `audit_event.user_id` — «NULL для системных событий»). Переход версии — через `consolidation_status.transition`
|
||||
с `changed_by = stored_file.uploaded_by` (`consolidation_status_history.changed_by` NOT NULL в DBML) и `comment =
|
||||
«автоподтверждение документа <тип> <счёт>»`. `on_document_confirmed` получает `changed_by: uuid | None` и `comment`
|
||||
от вызывающего: ручное подтверждение передаёт пользователя и `None`, автоподтверждение — `file.uploaded_by` и
|
||||
комментарий; при `changed_by = None` привязка создаётся, переход пропускается с записью в лог.
|
||||
|
||||
Признак автоподтверждения — `CONFIRMED` и `confirmed_by IS NULL`: ручное подтверждение всегда пишет пользователя
|
||||
(`DOCUMENT_EDIT` на маршруте).
|
||||
|
||||
### D-3. Что: документ, ставший `RECOGNIZED` при маппинге, и только он
|
||||
Кандидаты — документы файла со статусом `RECOGNIZED` сразу после `map_file`. Для `BALANCE`/`INCOME` с шапкой
|
||||
дополнительно — условия ручного подтверждения (D-9 Среза 3b): `period_year` и `period_quarter` определены, жёстких
|
||||
нарушений §7 нет (на момент маппинга акцептов нет). Условия выносятся из `DocumentService._check_statement` в общую
|
||||
функцию (одна точка правды; ручной путь продолжает отвечать 409 `PERIOD_REQUIRED`/`CHECKS_NOT_PASSED`, автоматический —
|
||||
просто оставляет `RECOGNIZED`). По §9.5 D-6 `RECOGNIZED` формы уже требует период и пройденные проверки, так что
|
||||
расхождение на практике — только защита от рассинхронизации правил. `DISCLOSURE`/`OTHER` (`RECOGNIZED` по D-3 Среза 3b)
|
||||
подтверждаются тоже: проверять в них нечего, в версии они не привязываются (`fits` — ОСВ и формы).
|
||||
|
||||
Пересчёт статуса формы после правки или акцепта (`RECOGNIZED`/`NEEDS_CHECK`) автоподтверждения не запускает: после
|
||||
исправлений аналитик нажимает «Подтвердить данные» (ТЗ 03_9, 10_10).
|
||||
|
||||
### D-4. Удаление: только автоподтверждённое и только до согласования
|
||||
`PrimaryFilesService.delete` под существующими блокировками (задания → файл → документы) проверяет: если у файла есть
|
||||
`CONFIRMED` документ с `confirmed_by IS NOT NULL` — 409 `FILE_HAS_CONFIRMED_DOCUMENTS` (как сейчас). Если все
|
||||
`CONFIRMED` документы автоподтверждены — дальше блокируются версии ЮЛ (`lock_versions_for_entity` по всем статусам, где
|
||||
есть привязки этих документов; порядок «… → документы → advisory ГК → версии» соблюдён) и проверяется, что каждая
|
||||
версия с привязкой в `INITIATED`/`DOCUMENTS_PROCESSING`/`DATA_CORRECTION`; иначе 409 `FILE_HAS_CONFIRMED_DOCUMENTS` с
|
||||
текстом «документ уже участвует в версии на согласовании». Затем привязки снимаются (`consolidation_document`), файл
|
||||
удаляется как сейчас; `FILE_DELETE.after_value = {unbound_version_ids: [...]}` (по возрастанию; пусто — пустой список),
|
||||
`before_value` — карточка файла, как сейчас. Статус версии не меняется (свод — чистая функция, пересчитается).
|
||||
`FILE_SUPERSEDES_OTHERS` — без изменений.
|
||||
|
||||
### D-5. Блокировки
|
||||
`auto_confirm` вызывается под заданием, файлом и документами маппинга; версии берёт `on_document_confirmed` через
|
||||
`lock_versions_for_entity` (advisory ГК → версии `FOR UPDATE` по `id`) — тот же путь, что у ручного подтверждения.
|
||||
Несколько документов файла — повторные вызовы в одной транзакции по одному ЮЛ: блокировки уже взяты, порядок не
|
||||
нарушается. Документы файла уже под `FOR NO KEY UPDATE` маппинга (R-3 совместимость со вставкой привязки).
|
||||
|
||||
### D-6. Данные и совместимость
|
||||
Миграции схемы и данных нет. Старые `RECOGNIZED` остаются и подтверждаются кнопкой. API не меняется:
|
||||
`DocumentOut.confirmed_by` уже `nullable`; `POST …/confirm` для автоподтверждённого отвечает 409 `ALREADY_CONFIRMED`.
|
||||
Чек-лист: ячейка с автоподтверждённым документом — `CONFIRMED` («Загружен»).
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Неверный, но «чистый» файл сразу в своде** (например, ОСВ не того ЮЛ с совпавшим ИНН шапки). Раньше его ловил
|
||||
взгляд аналитика при подтверждении. Смягчение: удаление разрешено до согласования (D-4), автозамена новой версией
|
||||
файла работает как раньше; документ виден в панели и чек-листе как «Загружен».
|
||||
- **Нагрузка на транзакцию маппинга** растёт на блокировку версий ЮЛ для каждого документа — то же, что делает хук
|
||||
`on_documents_mapped`; в комплекте десятки документов, не тысячи.
|
||||
- **Журнал**: событие `DOCUMENT_CONFIRM` без пользователя — системное, как `FILE_REPLACE`; фильтр журнала по
|
||||
пользователю его не покажет.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Выкатка обычная: миграций нет. После обновления ядра новые результаты подтверждаются автоматически; уже загруженные
|
||||
`RECOGNIZED` — вручную (или перезагрузкой файла).
|
||||
|
||||
## Open Questions
|
||||
|
||||
- OQ-157 (новый, docs): автоподтверждение чистых документов — решено владельцем 08.10.2026; пересматривает OQ-05
|
||||
(`RECOGNIZED` остаётся статусом модели, но после маппинга на нём документ не задерживается) и уточняет OQ-110
|
||||
(удаление при консолидациях — для автоподтверждённых до `DATA_CORRECTION` включительно).
|
||||
|
||||
## Decisions made without discussion
|
||||
|
||||
| № | Дата | Решение | Обоснование | Цена ошибки |
|
||||
|---|---|---|---|---|
|
||||
| 1 | 2026-10-08 | Автоподтверждение — после автозамены и до `on_documents_mapped` в той же транзакции маппинга | замена привязки по ключу работает как при ручном подтверждении; откат целиком при ошибке | неверный порядок — лишняя/потерянная привязка, ловится тестом автозамены |
|
||||
| 2 | 2026-10-08 | `DISCLOSURE`/`OTHER` тоже подтверждаются автоматически | в них нечего проверять; ТЗ 03_9 п. 4 не различает типы | расшифровку нельзя удалить без условий D-4 — те же, что у ОСВ |
|
||||
| 3 | 2026-10-08 | Условия формы — общая функция с ручным подтверждением; при невыполнении документ остаётся `RECOGNIZED` без ошибки | одна точка правды; автоматический путь не должен падать | форма без периода ждёт кнопку, как раньше |
|
||||
| 4 | 2026-10-08 | После правки/акцепта формы автоподтверждения нет | ТЗ: «Подтвердить данные» после исправлений | лишний клик после правки — осознанный |
|
||||
| 5 | 2026-10-08 | Существующие `RECOGNIZED` не мигрируются | контур без продовых данных; миграция данных с аудитом без пользователя — лишний риск | на стенде старые документы подтверждаются кнопкой |
|
||||
| 6 | 2026-10-08 | Комментарий перехода версии — «автоподтверждение документа <тип> <счёт>», описание аудита — «автоподтверждение: без замечаний» | история версии и журнал объясняют, откуда переход без клика | тексты меняются без миграции |
|
||||
| 7 | 2026-10-08 | Удаление: проверка статусов версий только тех, к которым привязаны документы файла; `FILE_DELETE.after_value.unbound_version_ids` | симметрично `FILE_REPLACE.unbound_version_ids` | — |
|
||||
55
openspec/changes/auto-confirm-clean-documents/proposal.md
Normal file
55
openspec/changes/auto-confirm-clean-documents/proposal.md
Normal file
@ -0,0 +1,55 @@
|
||||
# Proposal: автоподтверждение чистых документов
|
||||
|
||||
**Срез:** доработка Срезов 2c, 3b и 4 (`../docs/context/09-delivery-plan.md`) — путь «загрузка → свод» без пустых кликов.
|
||||
**Репозиторий:** arm-core. **Issue:** RSHB/arm-core#64. Правило и OQ — bounded-PR в `../docs` (`05-domain-rules.md` §9.3,
|
||||
§9.5; `07-open-questions.md` OQ-157, пометка к OQ-05 и OQ-110; гайды `dev/qa-guide.md`, `dev/frontend-api-guide.md`).
|
||||
DBML и контракты обмена не меняются.
|
||||
|
||||
## Why
|
||||
|
||||
Документ без замечаний сейчас получает `RECOGNIZED` и попадает в консолидацию только после ручного «Подтвердить»
|
||||
(`CONFIRMED`, §3 п. 1). Для ОСВ этот клик ничего не проверяет: строки ОСВ не правятся (OQ-114), ошибка исправляется
|
||||
перезагрузкой файла. На комплекте РБ + Счастливочная за 1 кв. 2025 это 16 нажатий подряд (прогон владельца 08.10.2026).
|
||||
ТЗ этого шага не требует: 03_9, п. 4 загрузки — «при отсутствии замечаний документ получает статус „Загружен“, при
|
||||
замечаниях — „Требуется проверка“»; 10_10 — «Загружен» при прохождении всех проверок; «Подтвердить данные» — шаг после
|
||||
исправлений. Решение владельца 08.10.2026: чистый документ сразу «Загружен».
|
||||
|
||||
## What Changes
|
||||
|
||||
- **Автоподтверждение при маппинге.** Документ, который маппинг сделал бы `RECOGNIZED`, сразу становится `CONFIRMED`:
|
||||
`confirmed_by = NULL`, `confirmed_at = now`, у формы ББ/ОФР `reviewed_by = NULL`, `reviewed_at = now`;
|
||||
`audit_event(DOCUMENT_CONFIRM)` с `user_id = NULL` и `description = «автоподтверждение: без замечаний»`. Форма
|
||||
подтверждается автоматически только при условиях ручного подтверждения (период определён, жёсткие проверки пройдены),
|
||||
иначе остаётся `RECOGNIZED`. `DISCLOSURE`/`OTHER` — тоже сразу `CONFIRMED`.
|
||||
- **Привязка и статус версии — как при ручном подтверждении.** Документ привязывается ко всем подходящим версиям «в
|
||||
работе» с заменой по ключу (R-1); версия из `INITIATED`/`DOCUMENTS_PROCESSING` переходит в `DATA_CORRECTION` с
|
||||
`changed_by = stored_file.uploaded_by` и комментарием «автоподтверждение документа …». Файл без `uploaded_by` —
|
||||
привязка есть, перехода нет, запись в лог (как у хука `on_documents_mapped`, design №2 Среза 4).
|
||||
- **`NEEDS_CHECK` и правки — вручную, как раньше.** Форма, ставшая чистой после правки или акцепта, подтверждается
|
||||
кнопкой (ТЗ 03_9: «Подтвердить данные» после исправлений). `RECOGNIZED` остаётся в модели: старые документы и формы
|
||||
после правок.
|
||||
- **Удаление файла с автоподтверждёнными документами.** `DELETE /files/{id}` разрешён, если все `CONFIRMED` документы
|
||||
файла подтверждены автоматически (`confirmed_by IS NULL`) и каждая версия, к которой они привязаны, в `INITIATED`,
|
||||
`DOCUMENTS_PROCESSING` или `DATA_CORRECTION`; привязки снимаются, статус версии не меняется,
|
||||
`FILE_DELETE.after_value.unbound_version_ids`. Иначе — 409 `FILE_HAS_CONFIRMED_DOCUMENTS`, как сейчас.
|
||||
|
||||
Таблицы DBML v5.17: `document` (`status`, `confirmed_by`, `confirmed_at`), `financial_statement_meta` (`reviewed_by`,
|
||||
`reviewed_at`), `consolidation_document`, `consolidation_version`, `consolidation_status_history`, `audit_event`,
|
||||
`stored_file` (`uploaded_by`). Новых полей нет.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Признак «подтверждено автоматически» отдельным полем или в API сверх `confirmed_by = null` — не вводится.
|
||||
- Подсказка в UI arm-web «подтверждено системой», кнопка удаления файла в UI — вне объёма (arm-web).
|
||||
- Миграция данных: существующие `RECOGNIZED` документы не подтверждаются задним числом.
|
||||
- Повторный автозапуск после правки формы, межформенная проверка как условие — нет (мягкая, §7).
|
||||
- Изменение правил `NEEDS_CHECK` (OQ-116, предупреждения маппинга) — нет.
|
||||
|
||||
## Impact
|
||||
|
||||
- Код: `services/result_mapping.py` (`_map_candidate`), `services/consolidation_documents.py`
|
||||
(`on_document_confirmed` с внешним `changed_by`/`comment`, новый `auto_confirm`), `services/documents.py` (общая
|
||||
проверка условий формы), `services/primary_files.py` (`delete`), `repositories/files.py` (снятие привязок).
|
||||
- Спеки: `osv-result-mapping`, `documents-api`, `primary-files`, `audit-trail`.
|
||||
- Поведение для фронта: чистые документы приходят сразу `CONFIRMED` («Загружен»); `confirmed_by = null` — признак
|
||||
автоподтверждения.
|
||||
@ -0,0 +1,28 @@
|
||||
# Spec Delta: audit-trail
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: События файлов и документов
|
||||
Загрузка, автозамена, удаление файла и подтверждение документа SHALL писать `audit_event` через сервис аудита:
|
||||
|
||||
| Действие | `action` | `section` | `object_type` | `user_id` |
|
||||
|---|---|---|---|---|
|
||||
| файл принят | `FILE_UPLOAD` | `PRIMARY_DOCUMENTS` | `stored_file` | инициатор |
|
||||
| прежний файл заменён новым (автозамена после маппинга) | `FILE_REPLACE` | `PRIMARY_DOCUMENTS` | `stored_file` (заменённый) | NULL (система; инициатор загрузки — в `description`) |
|
||||
| файл удалён | `FILE_DELETE` | `PRIMARY_DOCUMENTS` | `stored_file` | инициатор |
|
||||
| документ подтверждён пользователем | `DOCUMENT_CONFIRM` | `PRIMARY_DOCUMENTS` | `document` | инициатор |
|
||||
| документ подтверждён автоматически при маппинге (чистый) | `DOCUMENT_CONFIRM` | `PRIMARY_DOCUMENTS` | `document` | NULL (система; `description` = «автоподтверждение: без замечаний») |
|
||||
|
||||
`FILE_REPLACE` SHALL содержать в `after_value` новый `file_id`, в `description` — перечень заменённых документов и
|
||||
`confirmed=true`, если среди них был `CONFIRMED`. `FILE_DELETE` SHALL содержать в `after_value.unbound_version_ids`
|
||||
версии, где сняты привязки удалённых автоподтверждённых документов. Смена статусов задания и файла в обмене и в цикле
|
||||
маппинга `audit_event` не пишет (`ASSUMPTION(OQ-55)`).
|
||||
|
||||
#### Scenario: Загрузка двух файлов
|
||||
- **WHEN** принято два файла одним запросом
|
||||
- **THEN** два события `FILE_UPLOAD` с `object_id` каждого файла и `after_value` (`original_name`, `content_hash`, `job_id`)
|
||||
|
||||
#### Scenario: Автоподтверждение
|
||||
- **WHEN** смаплен чистый результат ОСВ
|
||||
- **THEN** одно событие `DOCUMENT_CONFIRM` с `object_id` документа, `user_id` NULL, `before_value.status = RECOGNIZED`,
|
||||
`after_value.status = CONFIRMED`, `description = «автоподтверждение: без замечаний»`
|
||||
@ -0,0 +1,140 @@
|
||||
# Spec Delta: osv-result-mapping
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Автоподтверждение чистого документа
|
||||
В транзакции маппинга после автозамены прежних версий файла и до перехода версий по `NEEDS_CHECK` цикл маппинга SHALL
|
||||
перевести каждый документ файла, получивший при маппинге статус `RECOGNIZED`, в `CONFIRMED` (ТЗ 03_9 п. 4, 10_10;
|
||||
решение владельца 08.10.2026, OQ-157): `confirmed_by = NULL`, `confirmed_at = now`; у формы `BALANCE`/`INCOME` —
|
||||
`financial_statement_meta.reviewed_by = NULL`, `reviewed_at = now`; `audit_event(DOCUMENT_CONFIRM, PRIMARY_DOCUMENTS,
|
||||
object_type = document)` с `user_id = NULL`, `before_value.status = RECOGNIZED`, `after_value.status = CONFIRMED` и
|
||||
`description = «автоподтверждение: без замечаний»`. Форма `BALANCE`/`INCOME` с шапкой SHALL подтверждаться
|
||||
автоматически, только если выполнены условия ручного подтверждения (`period_year` и `period_quarter` определены, нет
|
||||
непройденных неакцептованных жёстких проверок §7); иначе она MUST оставаться `RECOGNIZED` без ошибки. Документы
|
||||
`NEEDS_CHECK` MUST NOT подтверждаться автоматически.
|
||||
|
||||
Автоподтверждённый документ SHALL привязываться к версиям так же, как при ручном подтверждении (`documents-api`
|
||||
«Подтверждение документа»: все подходящие версии «в работе», замена привязки того же ключа R-1), и каждая версия,
|
||||
получившая привязку, из `INITIATED` или `DOCUMENTS_PROCESSING` SHALL переходить в `DATA_CORRECTION` с `changed_by =
|
||||
stored_file.uploaded_by` и `comment = «автоподтверждение документа <document_type> <osv_account_code>»` (счёт — только у
|
||||
ОСВ). Если `uploaded_by` NULL, привязка SHALL создаваться, а переход — пропускаться с записью в лог. Пересчёт статуса
|
||||
формы после правки или акцепта MUST NOT подтверждать документ автоматически.
|
||||
|
||||
#### Scenario: Чистая ОСВ сразу «Загружен»
|
||||
- **WHEN** смаплен результат `fixtures/osv/rb/2025-Q1/ОСВ сч 60 за 1 кв 2025 год.xlsx` для ЮЛ АО «Русский бисквит»
|
||||
(задание `done`, шапка совпадает с владельцем, нарушений и предупреждений нет)
|
||||
- **THEN** документ `OSV` 60 — `CONFIRMED`, `confirmed_by` NULL, `confirmed_at` заполнен; есть `audit_event(DOCUMENT_CONFIRM)`
|
||||
с `user_id` NULL и описанием «автоподтверждение: без замечаний»
|
||||
|
||||
#### Scenario: Привязка и переход версии
|
||||
- **WHEN** версия ГК «Русский Бисквит» 2025 Q1 в `INITIATED`, РБ в периметре; смаплен чистый результат ОСВ 60 РБ за 2025 Q1,
|
||||
файл загрузил `ka_go`
|
||||
- **THEN** `consolidation_document(version, document)` создан; версия в `DATA_CORRECTION`; запись истории
|
||||
`INITIATED → DATA_CORRECTION` с `changed_by` = `ka_go` и комментарием «автоподтверждение документа OSV 60»
|
||||
|
||||
#### Scenario: Документ с замечанием ждёт аналитика
|
||||
- **WHEN** файл ОСВ загружен для ЮЛ «Пекарня Счастливочная», а в шапке `АО "Русский бисквит"`
|
||||
- **THEN** документ `NEEDS_CHECK`, `confirmed_at` пуст, события `DOCUMENT_CONFIRM` нет, привязки нет
|
||||
|
||||
#### Scenario: Чистая форма Excel
|
||||
- **WHEN** смаплен `statements-xlsx/mezheninovskaya/2025-Q3/1_Форма2_бух_09` (ОФР, `done`, соотношения сходятся, период определён)
|
||||
- **THEN** документ `INCOME` `CONFIRMED`, `reviewed_by` NULL, `reviewed_at` заполнен
|
||||
|
||||
#### Scenario: Форма OCR не подтверждается сама
|
||||
- **WHEN** смаплен эталон `ocr/expected_normalize/БО 1 кв 2025` (уверенность значений < 100, дата отчёта не разобрана)
|
||||
- **THEN** документы `BALANCE` и `INCOME` — `NEEDS_CHECK`, не `CONFIRMED`
|
||||
|
||||
#### Scenario: Файл без загрузившего
|
||||
- **WHEN** версия в `INITIATED`, ЮЛ в периметре; смаплен чистый результат файла с `uploaded_by` NULL
|
||||
- **THEN** документ `CONFIRMED` и привязан к версии; версия осталась `INITIATED`; в логе — строка о пропуске перехода
|
||||
|
||||
#### Scenario: Автозамена чистой версией файла
|
||||
- **WHEN** документ ОСВ 60 РБ за 2025 Q1 из файла A привязан к версии 2025 Q1; загружен и смаплен файл B того же ключа
|
||||
(чистый), A заменён автозаменой
|
||||
- **THEN** привязка документа A снята, документ B `CONFIRMED` и привязан к версии
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Проверки шапки и статус документа
|
||||
`counterparty_check` SHALL быть `MATCH`, если ИНН шапки равен ИНН владельца, или (без ИНН) нормализованное имя шапки
|
||||
совпадает с нормализованным именем или алиасом владельца; `MISMATCH` иначе; `UNKNOWN` при пустой шапке.
|
||||
`period_check` SHALL быть `UNKNOWN` до появления версии консолидации (Срез 4). Для `period_quarter ∈ {Q2, Q3, Q4}` с
|
||||
`is_cumulative = false` ядро SHALL сверить сальдо на начало корневой строки с документом `Q1` того же года, ЮЛ и
|
||||
счёта (файл не `SUPERSEDED`) и при совпадении (±0.01) установить `is_cumulative = true` с предупреждением
|
||||
(`ASSUMPTION(OQ-02)`). Статус документа при маппинге SHALL быть `RECOGNIZED`, если задание `done`, `checks` без
|
||||
нарушений, `counterparty_check ≠ MISMATCH` и нет предупреждений маппинга; иначе `NEEDS_CHECK` (OQ-05). Документ
|
||||
`RECOGNIZED` в той же транзакции SHALL подтверждаться автоматически (требование «Автоподтверждение чистого документа»).
|
||||
|
||||
#### Scenario: Шапка другой организации
|
||||
- **WHEN** файл загружен для ЮЛ «Пекарня Счастливочная», а в шапке `АО "Русский бисквит"`
|
||||
- **THEN** `counterparty_check = MISMATCH`, `status = NEEDS_CHECK`
|
||||
|
||||
#### Scenario: Нарастающая по данным
|
||||
- **WHEN** у ЮЛ есть документ ОСВ 60 за 2024 Q1 (`opening_*` корня = X) и маппится ОСВ 60 «за 3 квартал 2024» с `opening_*` корня = X
|
||||
- **THEN** `is_cumulative = true`, предупреждение «нарастающая по данным Q1»
|
||||
|
||||
#### Scenario: Чистый результат
|
||||
- **WHEN** задание `done`, шапка совпадает с владельцем, нарушений нет
|
||||
- **THEN** после маппинга `status = CONFIRMED` (автоподтверждение), `counterparty_check = MATCH`, `period_check = UNKNOWN`
|
||||
|
||||
### Requirement: Документ NEEDS_CHECK переводит версию в корректировку
|
||||
После фиксации документов файла и автоподтверждения чистых документов цикл маппинга SHALL для каждой версии в
|
||||
`DOCUMENTS_PROCESSING`, в периметр которой входит ЮЛ файла, проверить документы файла: если среди них есть документ
|
||||
`NEEDS_CHECK`, подходящий версии по периоду (правила привязки `documents-api`), версия SHALL перейти в `DATA_CORRECTION`
|
||||
с `changed_by = stored_file.uploaded_by` в той же транзакции маппинга (версии блокируются `FOR UPDATE` по `id` после
|
||||
файла). Если `uploaded_by` NULL, переход SHALL пропускаться с записью в лог. Привязка `consolidation_document` для
|
||||
документа `NEEDS_CHECK` при маппинге MUST NOT создаваться (привязку при маппинге создаёт только автоподтверждение).
|
||||
|
||||
#### Scenario: Предупреждение маппинга двигает статус
|
||||
- **WHEN** версия 2024 Q3 в `DOCUMENTS_PROCESSING`; смапплен файл ЮЛ периметра с ОСВ 62 за 2024 Q3 в `NEEDS_CHECK`
|
||||
- **THEN** версия в `DATA_CORRECTION`, запись истории с `changed_by` = загрузивший; `consolidation_document` нет
|
||||
|
||||
#### Scenario: Документ RECOGNIZED
|
||||
- **WHEN** версия 2024 Q3 в `DOCUMENTS_PROCESSING`; смапплен файл с единственным чистым документом за 2024 Q3
|
||||
- **THEN** документ `CONFIRMED` и привязан; переход версии в `DATA_CORRECTION` записан автоподтверждением (комментарий
|
||||
«автоподтверждение документа …»), этот хук второго перехода не делает
|
||||
|
||||
#### Scenario: Документ не подходит версии
|
||||
- **WHEN** версия 2024 Q3 в `DOCUMENTS_PROCESSING`; смапплен файл с единственным документом `NEEDS_CHECK` за 2025 Q1
|
||||
- **THEN** статус версии не изменился
|
||||
|
||||
### Requirement: Статус документа формы
|
||||
Документ `BALANCE`/`INCOME` SHALL быть `RECOGNIZED`, только если одновременно: задание `done`; нет непройденных
|
||||
неакцептованных жёстких проверок §7 (`statement-checks`); `counterparty_check ≠ MISMATCH`; `period_year` и
|
||||
`period_quarter` определены; у каждого значения `is_modified = true` либо (`is_recognized` не `false` и `confidence`
|
||||
равна 100 или `NULL`); нет предупреждений маппинга по документу; есть хотя бы одна строка. Иначе `NEEDS_CHECK`. Статус
|
||||
SHALL пересчитываться при маппинге, после правки формы и после акцепта, кроме документа `CONFIRMED`. Документ
|
||||
`RECOGNIZED` после маппинга SHALL подтверждаться автоматически (требование «Автоподтверждение чистого документа»);
|
||||
`RECOGNIZED`, полученный пересчётом после правки или акцепта, — только вручную. Документ формы `NEEDS_CHECK` SHALL
|
||||
участвовать в переходе версии в `DATA_CORRECTION` наравне с ОСВ (требование «Документ NEEDS_CHECK переводит версию в
|
||||
корректировку»).
|
||||
|
||||
#### Scenario: Чистая форма Excel
|
||||
- **WHEN** смаплен `mezheninovskaya/2025-Q3/1_Форма2_бух_09` (ОФР, `done`, соотношения сходятся, ИНН шапки = ИНН владельца файла)
|
||||
- **THEN** документ `INCOME` `CONFIRMED` (автоподтверждение)
|
||||
|
||||
#### Scenario: Форма OCR
|
||||
- **WHEN** смаплен эталон `ocr/expected_normalize/aep_3` (уверенность значений < 100, ИНН шапки ≠ ИНН владельца)
|
||||
- **THEN** документ `BALANCE` `NEEDS_CHECK`, `counterparty_check = MISMATCH`
|
||||
|
||||
#### Scenario: Форма двигает версию
|
||||
- **WHEN** версия ГК 2025 Q3 в `DOCUMENTS_PROCESSING`, в периметре ЮЛ файла; смаплена форма `BALANCE` за 2025 Q3 в `NEEDS_CHECK`
|
||||
- **THEN** версия в `DATA_CORRECTION`, привязки `consolidation_document` нет
|
||||
|
||||
#### Scenario: Чистая после правки — вручную
|
||||
- **WHEN** форма `NEEDS_CHECK` исправлена аналитиком (`PATCH …/statement`) и пересчёт дал `RECOGNIZED`
|
||||
- **THEN** документ остаётся `RECOGNIZED` до `POST …/confirm`
|
||||
|
||||
### Requirement: Повтор формы в файле и прочие документы
|
||||
Второй документ `BALANCE` или `INCOME` в одном файле MUST NOT создаваться: предупреждение маппинга, первый документ —
|
||||
`NEEDS_CHECK`. Документы `DISCLOSURE` и `OTHER` результата SHALL давать по одному `document` каждого типа на файл без
|
||||
данных (`locator` — все `locator` этого типа через запятую, статус при маппинге `RECOGNIZED`, затем автоподтверждение →
|
||||
`CONFIRMED`). Форма `BALANCE`/`INCOME` без строк SHALL создаваться с шапкой и колонками (если колонки есть в результате).
|
||||
|
||||
#### Scenario: Расшифровки
|
||||
- **WHEN** смаплен синтетический снимок `st06_header_variants` (форма `INCOME` и пять `DISCLOSURE`)
|
||||
- **THEN** у файла два документа: `INCOME` и один `DISCLOSURE` с пятью `locator` через запятую; `DISCLOSURE` — `CONFIRMED`
|
||||
|
||||
#### Scenario: Форма без колонки кодов
|
||||
- **WHEN** смаплен синтетический снимок `st03_no_codes` (`BALANCE`, строк 0)
|
||||
- **THEN** документ `BALANCE` `NEEDS_CHECK` с шапкой, строк нет (колонок в снимке тоже нет — создаются те, что есть в результате)
|
||||
@ -0,0 +1,36 @@
|
||||
# Spec Delta: primary-files
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Удаление файла
|
||||
`DELETE /api/v1/files/{file_id}` (`DOCUMENT_EDIT`, policy) SHALL удалить строки ОСВ, документы, задания и `stored_file`,
|
||||
проставить `s3_object.expires_at = now()` (объект физически не удаляется, OQ-22) и записать `audit_event(FILE_DELETE,
|
||||
before_value = карточка файла, after_value = {unbound_version_ids})`. Удаление MUST быть отклонено 409
|
||||
`FILE_HAS_CONFIRMED_DOCUMENTS`, если у файла есть документ `CONFIRMED`, подтверждённый пользователем (`confirmed_by` не
|
||||
NULL), или если автоподтверждённый документ файла (`confirmed_by` NULL) привязан к версии в статусе, отличном от
|
||||
`INITIATED`, `DOCUMENTS_PROCESSING`, `DATA_CORRECTION` (OQ-157, уточняет OQ-110); и 409 `FILE_SUPERSEDES_OTHERS`, если на
|
||||
файл ссылаются `superseded_by_file_id`. При удалении файла с автоподтверждёнными документами их привязки
|
||||
`consolidation_document` SHALL сниматься, статус версий MUST NOT меняться, `after_value.unbound_version_ids` — id версий,
|
||||
где привязки сняты (по возрастанию; пустой список, если привязок не было). Блокировки: задания → файл → документы →
|
||||
advisory-блокировка привязок ГК → версии по `id`. Файл `SUPERSEDED` удалить можно.
|
||||
|
||||
#### Scenario: Удаление ошибочного файла
|
||||
- **WHEN** файл в `ERROR` удаляется владельцем
|
||||
- **THEN** 204; `stored_file`, `recognition_job` отсутствуют; `s3_object.expires_at` заполнен; `audit_event(FILE_DELETE)`
|
||||
с `after_value.unbound_version_ids = []`
|
||||
|
||||
#### Scenario: Подтверждённый документ
|
||||
- **WHEN** удаляется файл с документом `CONFIRMED`, у которого `confirmed_by` = пользователь
|
||||
- **THEN** 409 `FILE_HAS_CONFIRMED_DOCUMENTS`, ничего не изменилось
|
||||
|
||||
#### Scenario: Автоподтверждённый документ в корректировке
|
||||
- **WHEN** версия 2025 Q1 в `DATA_CORRECTION`, к ней привязан автоподтверждённый документ ОСВ 60 РБ; файл удаляется
|
||||
- **THEN** 204; привязка снята, версия осталась в `DATA_CORRECTION`; `FILE_DELETE.after_value.unbound_version_ids` = [id версии]
|
||||
|
||||
#### Scenario: Версия ушла на согласование
|
||||
- **WHEN** автоподтверждённый документ файла привязан к версии в `CONSOLIDATION_RF`
|
||||
- **THEN** 409 `FILE_HAS_CONFIRMED_DOCUMENTS`, привязка и файл на месте
|
||||
|
||||
#### Scenario: Автоподтверждённый без привязок
|
||||
- **WHEN** удаляется файл, все документы которого автоподтверждены и ни к одной версии не привязаны
|
||||
- **THEN** 204; `after_value.unbound_version_ids = []`
|
||||
Loading…
x
Reference in New Issue
Block a user