Andrey Epifancev e7bef704cf docs: автоподтверждение чистых документов в CLAUDE.md и README (#64)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 17:31:01 +04:00

15 KiB
Raw Blame History

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 —
8 2026-10-08 Тест form_confirmable ломает строку 2200 (строгое соотношение), а не 2400 (соотношения с 2400 в statement_checks.yaml нежёсткие) иначе тест не проверяет жёсткое нарушение только тест
9 2026-10-08 Тесты автоподтверждения, маппинга и удаления берут uploaded_by у реального пользователя (FK app_user), а не случайный uuid (ошибка плана) случайный uuid не проходит FK нет
10 2026-10-08 В tests/test_api_files_delete.py тесты, имитирующие подтверждение пользователем (test_confirmed_document_blocks_delete, test_delete_rereads_state_after_locks), получили confirmed_by иначе по правилу удаления (D-4) документ считается автоподтверждённым и удаление проходит нет
11 2026-10-08 Импорт auto_confirm в result_mapping._map_candidate — локальный цикл documents → result_mapping → auto_confirm → statements → documents техдолг: вынести form_confirmable/FORM_TYPES в модуль без обратной зависимости