Andrey Epifancev 42c253146c test(consolidation): подходящесть неподтверждённых кандидатов чек-листа, правки spec и design по финальному ревью
I-1 (T8.2): тесты, что неподтверждённый документ даёт NEEDS_CHECK/RECOGNIZED только если подходит версии (fits):
нарастающий Q2 и квартальный Q4 при версии Q3, другой год и ЮЛ вне периметра не влияют; положительный контроль.
M-2: 404 `NOT_FOUND` с сообщением `…_NOT_FOUND`, ключ привязки с признаком нарастающего (R-1, R-14), порядок и
режимы блокировок подтверждения (R-3) в spec. M-3: в design #39 и #43 — окно коммита перехода после загрузки.
M-5: docstring lock_versions_for_entity про ЮЛ без ГК в периметре.

Co-Authored-By: Claude Sonnet <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 08:28:24 +04:00

7.0 KiB
Raw Blame History

Spec Delta: primary-files

MODIFIED Requirements

Requirement: Загрузка файлов организации

POST /api/v1/legal-entities/{legal_entity_id}/files (multipart, одно или несколько полей files; право DOCUMENT_EDIT; ЮЛ в области видимости пользователя, иначе 404) SHALL для каждого файла в отдельной транзакции: определить тип по расширению и сверить с сигнатурой содержимого (xls — BIFF8, xlsx/xlsb — zip, pdf, png, jpg); отвергнуть файл больше UPLOAD_MAX_BYTES (50 МиБ) или неподдерживаемого типа; вычислить sha256; создать s3_object (bucket_role = uploads, ключ uploads/{file_id}/{job_id}/{безопасное имя}, sha256, size_bytes, content_type, created_by = user:{id}), положить объект в S3, создать stored_file (status = UPLOADED, version_no = 1, uploaded_by, uploaded_at, content_hash, original_name) и recognition_job (status = queued, pipeline по типу: xls/xlsx/xlsb → tabular, pdf/png/jpg → image; max_attempts 3 / 1), установить stored_file.current_recognition_job_id, записать audit_event(FILE_UPLOAD, PRIMARY_DOCUMENTS). Ответ SHALL перечислять принятые файлы (accepted[] с file_id, job_id, name) и отклонённые (rejected[] с name, code, message); код ответа 201, если принят хотя бы один, иначе 422. Тип документа и период при загрузке MUST NOT запрашиваться (макет 40). Без доступного S3 — 503 STORAGE_UNAVAILABLE. Необязательный query-параметр consolidation_version_id SHALL проверяться до приёма файлов: версия видима (иначе 404 NOT_FOUND с сообщением VERSION_NOT_FOUND), статус «в работе» (иначе 409 VERSION_NOT_EDITABLE), ЮЛ ∈ периметру (иначе 422 ENTITY_NOT_IN_PERIMETER); если принят хотя бы один файл и версия в INITIATED, она SHALL перейти в DOCUMENTS_PROCESSING (changed_by = загрузивший) в отдельной транзакции после приёма файлов. Блокировка версии для загрузки MUST NOT требоваться.

Scenario: Реальная ОСВ принята и ушла в outbox

  • WHEN ka_rf загружает rb/2024-FY/ОСВ_66 за 2024.xls для ЮЛ своего филиала
  • THEN 201, accepted[0].file_id существует; stored_file.status = UPLOADED, file_type = XLS, content_hash = sha256 файла; recognition_job.status = queued, pipeline = tabular; объект в S3 по ключу uploads/{file_id}/{job_id}/ОСВ_66 за 2024.xls; публикатор outbox публикует запрос с этим ключом

Scenario: Смесь принятых и отклонённых

  • WHEN в одном запросе xlsx, файл .docx и файл 51 МиБ
  • THEN 201, accepted — один, rejected — два с кодами UNSUPPORTED_FILE и FILE_TOO_LARGE

Scenario: Расширение не совпадает с содержимым

  • WHEN загружается файл osv.xlsx, байты которого — BIFF8
  • THEN файл отклонён с кодом UNSUPPORTED_FILE

Scenario: Чужое ЮЛ

  • WHEN ka_rf загружает файл для ЮЛ другого филиала
  • THEN 404, объектов и записей нет

Scenario: Загрузка в контексте версии

  • WHEN версия в INITIATED, ЮЛ в периметре, загружен один принятый файл с ?consolidation_version_id=
  • THEN 201; версия в DOCUMENTS_PROCESSING, запись истории с changed_by = пользователь

Scenario: ЮЛ вне периметра

  • WHEN consolidation_version_id указан, ЮЛ не в периметре версии
  • THEN 422 ENTITY_NOT_IN_PERIMETER, файлы не приняты

Requirement: Автозамена прежних версий

После успешного маппинга файла ядро SHALL найти другие файлы того же ЮЛ со статусом не SUPERSEDED, у которых есть документ с теми же document_type, osv_account_code, period_year, period_quarter, и перевести их в SUPERSEDED (superseded_by_file_id = новый файл, updated_at); новый файл получает version_no = максимум среди заменённых + 1. Документы и строки заменённых файлов SHALL сохраняться (история), но по умолчанию не показываться. Привязки consolidation_document документов заменённых файлов к версиям «в работе» SHALL удаляться в той же транзакции (версии блокируются FOR UPDATE по возрастанию id после старых файлов); привязки к версиям в других статусах не трогаются; статус версии не меняется. Аудит FILE_REPLACE (object_id заменённого, after_value с новым файлом и unbound_version_ids, в description — перечень заменённых документов и признак confirmed, если заменён документ CONFIRMED). Решение владельца 05.10.2026: замена происходит и для CONFIRMED.

Scenario: Новая ОСВ 60 за 1 кв. 2025

  • WHEN у ЮЛ есть PROCESSED файл с документом OSV 60 / 2025 / Q1 (CONFIRMED), загружен и смапплен новый файл с таким же документом
  • THEN прежний файл — SUPERSEDED, superseded_by_file_id = новый, у нового version_no = 2; audit_event(FILE_REPLACE) с description, содержащим confirmed=true; список файлов ЮЛ по умолчанию показывает только новый

Scenario: Заменённый документ был в версии

  • WHEN заменённый CONFIRMED документ ОСВ 60 за 2024 Q2 был привязан к версии 2024 Q3 в DATA_CORRECTION
  • THEN привязка удалена; в чек-листе ячейка ОСВ 60 показывает missing_quarters = ["Q2"] (или NEEDS_CHECK по новому документу), пока новый документ не подтверждён; статус версии прежний