Срез 3b: формы ББ/ОФР в ядре и API до сводной ОСВ #39

Merged
aep merged 36 commits from feat/36-slice-3b-statements-api into main 2026-10-07 14:20:49 +03:00
Owner

Closes #36

Что сделано

Срез 3b: формы ББ/ОФР в ядре и API до сводной ОСВ.

  • маппинг форм ББ/ОФР в map_file (шапка, колонки, строки, значения, статус документа, повтор формы в файле);

  • проверки §7 в ядре (domain/statement_checks.py, словарь api/rules/statement_checks.yaml — копия словаря tabular-parser);

  • GET /documents/{id}/statement, правка формы и акцепт проверок (аудит RECOGNIZED_DATA_EDIT), подтверждение формы (период и проверки §7);

  • комментарии к документу, GET /files/{id}/content (поток из S3, RFC 5987), GET /users/brief, documents_count у ЮЛ, CSV чек-листа;

  • удаление файла чистит формы и комментарии документов (C-1 финального ревью).

  • OpenSpec change: openspec/changes/slice-3b-statements-api/ (openspec validate slice-3b-statements-api --strict — valid)

  • Документация и контракт для фронта: arm-ka-docs PR #49 (RSHB/arm-ka-docs#49), issue docs #48

  • Решения без обсуждения: design.md, раздел «Decisions made without discussion» (№1–25)

make check-full

Выход 0. Лог — check-full-4.log.

  • make check: ruff + mypy чисто, 1194 passed (unit)
  • dbml-check: ORM совпадает с DBML v5.16
  • test-integration: 783 passed (386 s)
  • e2e: 1 skipped (нужен профиль services, не ошибка)
  • docker build -t arm-core .: успешно
  • web-check: biome, tsc, rsbuild build — чисто

Находки и статусы

Маркеры: T<n>.<k> — minor ревью задачи n; T<n>.I<k> — Important ревью задачи n (маркер введён контроллером, O-4); PA.*/PB.* — ревью пакетов доработок; C-n/I-n/M-n/N-n — финальное ревью ветки (opus); R-n (design #N) — рулинги; O-n — наблюдения.

SHA — текущие (после переписи трейлеров, O-6).

Ревью задач

Маркер Суть Статус
T1.I1 лишний skip явных соотношений без членов (statement_checks.py) fixed 4126a44
T1.1 слабые тесты проверок (per-column null, tolerance, порядок, округление) fixed 234e830
T1.2 rule not in sublines — линейный скан deferred
T1.3 docstring'и обёрнуты посреди фразы deferred
T1.4 неточности отчёта реализатора rejected (на код не влияет)
T2.1 нет тестов: равные даты колонок, колонка без даты, дубль кода, confidence → Decimal fixed 234e830
T2.2 test_value_outside_columns_skipped избыточен (требование плана) rejected (plan-mandated)
T2.3 повтор пары (строка, колонка) молча берёт первое значение fixed f9500d3 (предупреждение)
T2.4 значения дубля строки не порождают предупреждений rejected (предупреждение о дубле есть)
T2.5 параметр values_ok затеняет функцию; inn_verified=None при пустом ИНН rejected (plan-mandated, безвредно)
T3.1 if data.get("error") пропускает пустой {} fixed 234e830
T3.2 сортировка по enum даёт порядок Postgres, не алфавитный deferred
T3.3 слабый тест ocr_result fixed 234e830
T3.4 типизация result_builder rejected (косметика)
T4.1 шапка st03 не проверена в test_form_without_codes fixed 234e830
T4.2 в смешанном снимке s12 форма не проверена fixed 234e830
T4.3 нет теста «форма двигает версию» rejected (покрыт тестом Task 7)
T4.4 нет теста статуса формы при job needs_check fixed 234e830
T4.5 не проверен текст предупреждения повтора и mapping_warnings fixed 234e830
T4.6 нет теста идемпотентного повторного маппинга форм fixed 234e830
T4.7 numeric(5,2) округляет 99.996 до 100 rejected (свойство схемы БД)
T4.8 мёртвая защита code or "" rejected (безвредно)
T5.1 _pair_document не требует meta у пары fixed 38a6a1b
T5.2 лишние запросы rejected (дёшево)
T5.3 order_by(line_code) без tie-break fixed 38a6a1b
T5.4 слабый test_cross_form_check fixed 38a6a1b
T5.5 нет тестов: edition 66n/null, quarter NULL, предпочтение пары, 403 fixed 234e830
T5.6 локальный импорт в тесте fixed 38a6a1b
T6.I1 meta не перечитывается под блокировкой (устаревший акцепт при гонке) fixed 6c00a87
T6.1 слабые тесты аудита/даты/units fixed 6c00a87
T6.2 units=null молча игнорируется, пробельный проходит fixed fb457fc
T6.3 повторный акцепт даёт NOTHING_TO_ACCEPT rejected (по спеке)
T6.4 аудит сброса акцепта не хранит comment fixed fb457fc (comment); ключи value:<uuid> — deferred
T6.5 lock_cells блокирует лишние строки rejected (безвредно)
T6.6 DOCUMENT_NOT_CONFIRMABLE при правке в иных статусах rejected (как у confirm)
T7.1 формулировка «details» в спеке fixed 3f744b7
T7.2 форма без meta не покрыта тестом fixed 234e830
T7.3 нет теста ветки period_year is None fixed 234e830
T7.4 DocumentService строит StatementService ради hard_failures deferred
T8.1 before_value FILE_DELETE содержит comments_count rejected (контракт не фиксирует)
T8.2 осиротевшие комментарии при удалении файла fixed dd7f7cc
T8.3 comments_count default=0 rejected
T8.4 нет теста POST для пользователя без видимости fixed 234e830
T8.5 недостижимая ветка "" для автора rejected
T9.1 освобождение S3-ответа зависит от GC fixed e45502b
T9.2 ASCII-имя берёт расширение из original_name fixed e45502b
T9.3 S3 round trip внутри открытой транзакции rejected (цена 404 до заголовков)
T9.4 два type: ignore[attr-defined] на aclose fixed e45502b
T9.5 нет теста content с кавычкой/CRLF; nosniff fixed e45502b
T10.1 пробелы тестов /users/brief fixed 234e830
T10.2–T10.5 UserBriefOut в admin.py, ids без лимита, комментарии rejected
T11.1 _out(entity, ...) без аннотации fixed d807380
T11.2 повтор трёх строк подсчёта fixed d807380
T11.3 пробелы тестов documents_count fixed 234e830
T11.4 list vs Sequence rejected
T12.1 CSV не нейтрализует формулы Excel rejected (R-9, design #19)
T12.2 нет описания ответа text/csv в OpenAPI fixed d807380
T12.3 version_title повторяет запросы rejected (бриф разрешал)
T12.4 пробелы тестов CSV fixed 234e830
T12.5 длинное имя ГК раздувает заголовок rejected
T12.6 стиль checklist_csv fixed d807380
T13.I1 README: DISCLOSURE/OTHER «игнорируются» fixed 7578b5f
T13.I2 README: «аудит не пишется как экспорт сводной ОСВ» fixed 7578b5f
T13.I3 README: статусы файла/документа только про ОСВ fixed 7578b5f
T13.4–T13.8 оговорка о пропуске формы, STORAGE_UNAVAILABLE, confidence, tasks.md, «как экспорт» fixed 7578b5f
T14.1–T14.7 правки docs PR #49 (порядок ошибок, «другой разработчик», №1–19, разделители OQ, полнота контракта, F3, статусы 2a/2c) fixed docs 15d069c
T14.8 сверка п. 15 CSV контракта с кодом fixed docs d82c7f9
PB.1 send без типа fixed 308fbc4
PB.2 aclose не сбрасывает _first fixed 308fbc4
PB.3 лазейка GC, если ответ не вызван deferred
PB.4 aclose под shield без таймаута deferred
PB.5–PB.8 _chain как обёртка, disposition после anext, приватный _CELL_STATUS_LABELS в тесте, nosniff только inline rejected

Финальное ревью ветки (opus) и перепроверка

Маркер Суть Статус
C-1 DELETE /files/{id} с формой ББ/ОФР падает по FK fixed dd7f7cc
I-1 повтор column_index в результате роняет маппинг всего файла fixed f9500d3
M-1 «цель §7 присутствует, только если сумма не NULL» rejected (неверная посылка: оба производителя отдают значение на каждую колонку, None = 0; повторное opus-ревью подтвердило; код откатан, тест 51d322c, R-11 отозван)
M-2 разные определения has_lines fixed ffc0183
M-3 docstring порядка строк подстрок fixed e851445, 51d322c
M-4 CounterpartyIndex.load целиком при правке ИНН deferred
M-5 header.inn = null обходит MISMATCH fixed ffc0183 (422 INVALID_INN)
M-6 спека: «details» и недостижимый сценарий «Пустой результат» fixed 4a5cb87
M-7 /files/{id}/content без Cache-Control: private, no-store, inline без CSP sandbox fixed ffc0183
M-8 тест «Пара найдена» не проверял подтверждение ББ fixed ffc0183
N-1 HeaderPatch.inn nullable в OpenAPI при 422 на null fixed 51d322c
N-2 _clear_file не удаляет комментарии документов при повторном маппинге deferred

Рулинги и наблюдения

Маркер Суть Статус
R-1 … R-14 см. «Rulings I made» зафиксированы в design.md №11–25
R-2 пересчёт статуса после правки не учитывает предупреждения маппинга deferred (нужен DBML v5.17)
O-1 make venv не был выполнен, venv отсутствовал выполнено контроллером
O-2 / O-6 сабагенты ставили в Co-Authored-By модель Haiku 4.5; трейлеры переписаны до первого push fixed (перепись локальных коммитов)
O-3 AcceptedOut переименована в ChecksAcceptedOut (коллизия имён OpenAPI) fixed (форма JSON не менялась)
O-4 маркер T<n>.I<k> введён контроллером принято
O-5 спека утверждала «экспорт xlsx без аудита» — на деле пишет EXPORT fixed (design, README, docs)

Rulings I made

R design Решение Цена ошибки
R-1 №11 признак повтора формы — множество duplicated, предупреждение в statement_status; фрагмент плана со startswith не реализован нет
R-2 №12 после правки/акцепта статус формы не учитывает предупреждения маппинга (в БД они только на уровне файла) документ с предупреждением маппинга после правки может стать RECOGNIZED
R-3 №13 тест плана test_per_column_and_null_as_zero противоречил спеке — заменён тестами «отсутствующий код = 0 → нарушение» потеряно покрытие «по колонкам», затем восстановлено (T1.1)
R-4 №14 «форма без строк создаётся с колонками, если они есть в результате» нет
R-5 №15 сверка golden ОСВ ограничена документами OSV форма из s12 в этом тесте не сверяется
R-6 №16 BALANCE/INCOME без meta подтверждается без условий D-9; …/statement — 409 старый документ без данных формы подтверждается без проверок
R-7 №17 CHECKS_NOT_PASSED отдаёт нарушения в detail.failures нет
R-8 №18 комментарии удалённых файлов остаются; позже закрыто (T8.2, dd7f7cc) —
R-9 №19 CSV не нейтрализует формулы Excel формула в названии ЮЛ откроется в Excel как формула
R-10 №23 пакеты доработок A/B без полного task-ревью, покрыты финальным пропуск слабого теста (финальное ревью прошло)
R-11 №20 отозван: присутствие цели §7 = строка есть в форме, NULL = 0 —
R-12 №21 PATCH header.inn = null → 422 INVALID_INN нельзя стереть мусорный ИНН, только заменить
R-13 №22 повтор column_index/пары (строка, колонка): первое значение + предупреждение потеря второго значения
R-14 №24 откат M-1 без отдельного ревью (код побайтно как до e851445) ошибка в тесте или docstring
— №25 автозамена форм на уровне файла (ОФР заменит книгу «ББ + ОФР»), вопрос владельцу — OQ-154 (docs PR #49) замена формы вместе с соседней

Решения №1–10 — дизайн-решения, принятые на этапе спеки (см. design.md).

Deferred

  • T1.2 линейный скан rule not in sublines
  • T1.3 docstring'и обёрнуты посреди фразы
  • T3.2 порядок enum в SQL-сортировке (учтён в тестах множествами)
  • T6.4 (остаток) ключи аудита value:<uuid> без кода строки/колонки
  • T7.4 связность DocumentService → StatementService
  • PB.3 лазейка GC, если ответ не вызван; PB.4 aclose под shield без таймаута
  • M-4 CounterpartyIndex.load целиком при правке ИНН
  • N-2 комментарии документов при повторном маппинге
  • R-2 предупреждения маппинга после правки (нужен DBML v5.17)

Вопросы владельцу

  • OQ-154: допустима ли автозамена форм на уровне файла (ОФР заменяет книгу «ББ + ОФР» того же периода; подтверждённый ББ отвяжется от версий «в работе»).
  • OQ-153 (журналирование скачивания первички и CSV) — допущение «не пишем» принято.

🤖 Generated with Claude Code

https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2

Closes #36 ## Что сделано Срез 3b: формы ББ/ОФР в ядре и API до сводной ОСВ. - маппинг форм ББ/ОФР в `map_file` (шапка, колонки, строки, значения, статус документа, повтор формы в файле); - проверки §7 в ядре (`domain/statement_checks.py`, словарь `api/rules/statement_checks.yaml` — копия словаря tabular-parser); - `GET /documents/{id}/statement`, правка формы и акцепт проверок (аудит `RECOGNIZED_DATA_EDIT`), подтверждение формы (период и проверки §7); - комментарии к документу, `GET /files/{id}/content` (поток из S3, RFC 5987), `GET /users/brief`, `documents_count` у ЮЛ, CSV чек-листа; - удаление файла чистит формы и комментарии документов (C-1 финального ревью). - OpenSpec change: `openspec/changes/slice-3b-statements-api/` (`openspec validate slice-3b-statements-api --strict` — valid) - Документация и контракт для фронта: arm-ka-docs PR #49 (https://git.aepif.ru/RSHB/arm-ka-docs/pulls/49), issue docs #48 - Решения без обсуждения: `design.md`, раздел «Decisions made without discussion» (№1–25) ## make check-full Выход 0. Лог — `check-full-4.log`. - `make check`: ruff + mypy чисто, 1194 passed (unit) - `dbml-check`: ORM совпадает с DBML v5.16 - `test-integration`: 783 passed (386 s) - `e2e`: 1 skipped (нужен профиль `services`, не ошибка) - `docker build -t arm-core .`: успешно - `web-check`: biome, tsc, rsbuild build — чисто ## Находки и статусы Маркеры: `T<n>.<k>` — minor ревью задачи n; `T<n>.I<k>` — Important ревью задачи n (маркер введён контроллером, O-4); `PA.*`/`PB.*` — ревью пакетов доработок; `C-n/I-n/M-n/N-n` — финальное ревью ветки (opus); `R-n (design #N)` — рулинги; `O-n` — наблюдения. SHA — текущие (после переписи трейлеров, O-6). ### Ревью задач | Маркер | Суть | Статус | |---|---|---| | T1.I1 | лишний skip явных соотношений без членов (`statement_checks.py`) | fixed 4126a44 | | T1.1 | слабые тесты проверок (per-column null, tolerance, порядок, округление) | fixed 234e830 | | T1.2 | `rule not in sublines` — линейный скан | deferred | | T1.3 | docstring'и обёрнуты посреди фразы | deferred | | T1.4 | неточности отчёта реализатора | rejected (на код не влияет) | | T2.1 | нет тестов: равные даты колонок, колонка без даты, дубль кода, confidence → Decimal | fixed 234e830 | | T2.2 | `test_value_outside_columns_skipped` избыточен (требование плана) | rejected (plan-mandated) | | T2.3 | повтор пары (строка, колонка) молча берёт первое значение | fixed f9500d3 (предупреждение) | | T2.4 | значения дубля строки не порождают предупреждений | rejected (предупреждение о дубле есть) | | T2.5 | параметр `values_ok` затеняет функцию; `inn_verified=None` при пустом ИНН | rejected (plan-mandated, безвредно) | | T3.1 | `if data.get("error")` пропускает пустой `{}` | fixed 234e830 | | T3.2 | сортировка по enum даёт порядок Postgres, не алфавитный | deferred | | T3.3 | слабый тест `ocr_result` | fixed 234e830 | | T3.4 | типизация `result_builder` | rejected (косметика) | | T4.1 | шапка st03 не проверена в `test_form_without_codes` | fixed 234e830 | | T4.2 | в смешанном снимке s12 форма не проверена | fixed 234e830 | | T4.3 | нет теста «форма двигает версию» | rejected (покрыт тестом Task 7) | | T4.4 | нет теста статуса формы при `job needs_check` | fixed 234e830 | | T4.5 | не проверен текст предупреждения повтора и `mapping_warnings` | fixed 234e830 | | T4.6 | нет теста идемпотентного повторного маппинга форм | fixed 234e830 | | T4.7 | `numeric(5,2)` округляет 99.996 до 100 | rejected (свойство схемы БД) | | T4.8 | мёртвая защита `code or ""` | rejected (безвредно) | | T5.1 | `_pair_document` не требует meta у пары | fixed 38a6a1b | | T5.2 | лишние запросы | rejected (дёшево) | | T5.3 | `order_by(line_code)` без tie-break | fixed 38a6a1b | | T5.4 | слабый `test_cross_form_check` | fixed 38a6a1b | | T5.5 | нет тестов: edition 66n/null, quarter NULL, предпочтение пары, 403 | fixed 234e830 | | T5.6 | локальный импорт в тесте | fixed 38a6a1b | | T6.I1 | meta не перечитывается под блокировкой (устаревший акцепт при гонке) | fixed 6c00a87 | | T6.1 | слабые тесты аудита/даты/units | fixed 6c00a87 | | T6.2 | `units=null` молча игнорируется, пробельный проходит | fixed fb457fc | | T6.3 | повторный акцепт даёт `NOTHING_TO_ACCEPT` | rejected (по спеке) | | T6.4 | аудит сброса акцепта не хранит comment | fixed fb457fc (comment); ключи `value:<uuid>` — deferred | | T6.5 | `lock_cells` блокирует лишние строки | rejected (безвредно) | | T6.6 | `DOCUMENT_NOT_CONFIRMABLE` при правке в иных статусах | rejected (как у confirm) | | T7.1 | формулировка «details» в спеке | fixed 3f744b7 | | T7.2 | форма без meta не покрыта тестом | fixed 234e830 | | T7.3 | нет теста ветки `period_year is None` | fixed 234e830 | | T7.4 | `DocumentService` строит `StatementService` ради `hard_failures` | deferred | | T8.1 | `before_value` FILE_DELETE содержит `comments_count` | rejected (контракт не фиксирует) | | T8.2 | осиротевшие комментарии при удалении файла | fixed dd7f7cc | | T8.3 | `comments_count` default=0 | rejected | | T8.4 | нет теста POST для пользователя без видимости | fixed 234e830 | | T8.5 | недостижимая ветка `""` для автора | rejected | | T9.1 | освобождение S3-ответа зависит от GC | fixed e45502b | | T9.2 | ASCII-имя берёт расширение из `original_name` | fixed e45502b | | T9.3 | S3 round trip внутри открытой транзакции | rejected (цена 404 до заголовков) | | T9.4 | два `type: ignore[attr-defined]` на `aclose` | fixed e45502b | | T9.5 | нет теста content с кавычкой/CRLF; nosniff | fixed e45502b | | T10.1 | пробелы тестов `/users/brief` | fixed 234e830 | | T10.2–T10.5 | `UserBriefOut` в `admin.py`, `ids` без лимита, комментарии | rejected | | T11.1 | `_out(entity, ...)` без аннотации | fixed d807380 | | T11.2 | повтор трёх строк подсчёта | fixed d807380 | | T11.3 | пробелы тестов `documents_count` | fixed 234e830 | | T11.4 | `list` vs `Sequence` | rejected | | T12.1 | CSV не нейтрализует формулы Excel | rejected (R-9, design #19) | | T12.2 | нет описания ответа `text/csv` в OpenAPI | fixed d807380 | | T12.3 | `version_title` повторяет запросы | rejected (бриф разрешал) | | T12.4 | пробелы тестов CSV | fixed 234e830 | | T12.5 | длинное имя ГК раздувает заголовок | rejected | | T12.6 | стиль `checklist_csv` | fixed d807380 | | T13.I1 | README: DISCLOSURE/OTHER «игнорируются» | fixed 7578b5f | | T13.I2 | README: «аудит не пишется как экспорт сводной ОСВ» | fixed 7578b5f | | T13.I3 | README: статусы файла/документа только про ОСВ | fixed 7578b5f | | T13.4–T13.8 | оговорка о пропуске формы, STORAGE_UNAVAILABLE, confidence, tasks.md, «как экспорт» | fixed 7578b5f | | T14.1–T14.7 | правки docs PR #49 (порядок ошибок, «другой разработчик», №1–19, разделители OQ, полнота контракта, F3, статусы 2a/2c) | fixed docs 15d069c | | T14.8 | сверка п. 15 CSV контракта с кодом | fixed docs d82c7f9 | | PB.1 | `send` без типа | fixed 308fbc4 | | PB.2 | `aclose` не сбрасывает `_first` | fixed 308fbc4 | | PB.3 | лазейка GC, если ответ не вызван | deferred | | PB.4 | `aclose` под shield без таймаута | deferred | | PB.5–PB.8 | `_chain` как обёртка, `disposition` после `anext`, приватный `_CELL_STATUS_LABELS` в тесте, nosniff только inline | rejected | ### Финальное ревью ветки (opus) и перепроверка | Маркер | Суть | Статус | |---|---|---| | C-1 | `DELETE /files/{id}` с формой ББ/ОФР падает по FK | fixed dd7f7cc | | I-1 | повтор `column_index` в результате роняет маппинг всего файла | fixed f9500d3 | | M-1 | «цель §7 присутствует, только если сумма не NULL» | rejected (неверная посылка: оба производителя отдают значение на каждую колонку, `None` = 0; повторное opus-ревью подтвердило; код откатан, тест 51d322c, R-11 отозван) | | M-2 | разные определения `has_lines` | fixed ffc0183 | | M-3 | docstring порядка строк подстрок | fixed e851445, 51d322c | | M-4 | `CounterpartyIndex.load` целиком при правке ИНН | deferred | | M-5 | `header.inn = null` обходит MISMATCH | fixed ffc0183 (422 `INVALID_INN`) | | M-6 | спека: «details» и недостижимый сценарий «Пустой результат» | fixed 4a5cb87 | | M-7 | `/files/{id}/content` без `Cache-Control: private, no-store`, inline без CSP sandbox | fixed ffc0183 | | M-8 | тест «Пара найдена» не проверял подтверждение ББ | fixed ffc0183 | | N-1 | `HeaderPatch.inn` nullable в OpenAPI при 422 на null | fixed 51d322c | | N-2 | `_clear_file` не удаляет комментарии документов при повторном маппинге | deferred | ### Рулинги и наблюдения | Маркер | Суть | Статус | |---|---|---| | R-1 … R-14 | см. «Rulings I made» | зафиксированы в design.md №11–25 | | R-2 | пересчёт статуса после правки не учитывает предупреждения маппинга | deferred (нужен DBML v5.17) | | O-1 | `make venv` не был выполнен, venv отсутствовал | выполнено контроллером | | O-2 / O-6 | сабагенты ставили в Co-Authored-By модель Haiku 4.5; трейлеры переписаны до первого push | fixed (перепись локальных коммитов) | | O-3 | `AcceptedOut` переименована в `ChecksAcceptedOut` (коллизия имён OpenAPI) | fixed (форма JSON не менялась) | | O-4 | маркер `T<n>.I<k>` введён контроллером | принято | | O-5 | спека утверждала «экспорт xlsx без аудита» — на деле пишет `EXPORT` | fixed (design, README, docs) | ## Rulings I made | R | design | Решение | Цена ошибки | |---|---|---|---| | R-1 | №11 | признак повтора формы — множество `duplicated`, предупреждение в `statement_status`; фрагмент плана со `startswith` не реализован | нет | | R-2 | №12 | после правки/акцепта статус формы не учитывает предупреждения маппинга (в БД они только на уровне файла) | документ с предупреждением маппинга после правки может стать `RECOGNIZED` | | R-3 | №13 | тест плана `test_per_column_and_null_as_zero` противоречил спеке — заменён тестами «отсутствующий код = 0 → нарушение» | потеряно покрытие «по колонкам», затем восстановлено (T1.1) | | R-4 | №14 | «форма без строк создаётся с колонками, если они есть в результате» | нет | | R-5 | №15 | сверка golden ОСВ ограничена документами `OSV` | форма из s12 в этом тесте не сверяется | | R-6 | №16 | `BALANCE`/`INCOME` без meta подтверждается без условий D-9; `…/statement` — 409 | старый документ без данных формы подтверждается без проверок | | R-7 | №17 | `CHECKS_NOT_PASSED` отдаёт нарушения в `detail.failures` | нет | | R-8 | №18 | комментарии удалённых файлов остаются; позже закрыто (T8.2, dd7f7cc) | — | | R-9 | №19 | CSV не нейтрализует формулы Excel | формула в названии ЮЛ откроется в Excel как формула | | R-10 | №23 | пакеты доработок A/B без полного task-ревью, покрыты финальным | пропуск слабого теста (финальное ревью прошло) | | R-11 | №20 | отозван: присутствие цели §7 = строка есть в форме, `NULL` = 0 | — | | R-12 | №21 | `PATCH header.inn = null` → 422 `INVALID_INN` | нельзя стереть мусорный ИНН, только заменить | | R-13 | №22 | повтор `column_index`/пары (строка, колонка): первое значение + предупреждение | потеря второго значения | | R-14 | №24 | откат M-1 без отдельного ревью (код побайтно как до e851445) | ошибка в тесте или docstring | | — | №25 | автозамена форм на уровне файла (ОФР заменит книгу «ББ + ОФР»), вопрос владельцу — OQ-154 (docs PR #49) | замена формы вместе с соседней | Решения №1–10 — дизайн-решения, принятые на этапе спеки (см. design.md). ## Deferred - T1.2 линейный скан `rule not in sublines` - T1.3 docstring'и обёрнуты посреди фразы - T3.2 порядок enum в SQL-сортировке (учтён в тестах множествами) - T6.4 (остаток) ключи аудита `value:<uuid>` без кода строки/колонки - T7.4 связность `DocumentService` → `StatementService` - PB.3 лазейка GC, если ответ не вызван; PB.4 `aclose` под shield без таймаута - M-4 `CounterpartyIndex.load` целиком при правке ИНН - N-2 комментарии документов при повторном маппинге - R-2 предупреждения маппинга после правки (нужен DBML v5.17) ## Вопросы владельцу - OQ-154: допустима ли автозамена форм на уровне файла (ОФР заменяет книгу «ББ + ОФР» того же периода; подтверждённый ББ отвяжется от версий «в работе»). - OQ-153 (журналирование скачивания первички и CSV) — допущение «не пишем» принято. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
aep added 36 commits 2026-10-07 14:06:13 +03:00
proposal, design (D-1…D-14, решения без обсуждения №1–10), дельты osv-result-mapping (маппинг форм, статус),
statement-checks (новая: §7, межформенная, акцепт), documents-api (форма, правка, акцепт, подтверждение, комментарии,
исходный файл), consolidations-api (CSV чек-листа), directories-api (documents_count), users-admin (справочник имён).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Удалён пропуск явных правил, когда в колонке нет членов правой части: спека statement-checks
требует считать отсутствующий код нулём, при ненулевой цели — жёсткое нарушение.
Тест плана test_per_column_and_null_as_zero противоречил спеке (1600 = 1700 без строки 1700 — нарушение)
и заменён двумя тестами по спеке.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Добавлены:
- Константы GOLDEN_STATEMENTS и OCR_EXPECTED в fixtures_helpers
- Функция ocr_result для создания RecognitionResult из эталона нормализатора
- Тест test_ocr_result_wraps_normalizer_golden, проверяющий порядок документов

Изменены:
- map_golden_snapshot теперь принимает параметр result_builder для переопределения
  функции создания RecognitionResult (по умолчанию golden_result)
- Документы сортируются по (document_type, osv_account_code) вместо только
  osv_account_code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Схемы формы, StatementService (видимость, 409 DOCUMENT_NOT_STATEMENT, проверки §7 и межформенная),
репозиторий (get_meta, load_form, pair_amounts), маршрут GET с DOCUMENT_VIEW, тесты API и детерминированный
тест межформенной проверки.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
PATCH /documents/{id}/statement и POST /documents/{id}/statement/checks/accept: блокировки «файл → документ»,
409 FILE_SUPERSEDED/DOCUMENT_READONLY/DOCUMENT_NOT_STATEMENT, 422 INVALID_INN/UNKNOWN_CELL/DUPLICATE_CELL,
один audit_event на запрос, сброс акцепта правкой, пересчёт статуса D-6.

Замечания ревью Task 5: пара формы ищется только среди документов с шапкой формы (T5.1), строки формы упорядочены
по (line_code, id) (T5.3), слабый тест межформенной проверки удалён (T5.4), локальный импорт в тестах убран (T5.6).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
I-1: `_editable` брал шапку без перечитывания, и после ожидания блокировки видел устаревший акцепт и значения
шапки. `get_meta(..., refresh=True)` читает с populate_existing после lock_file и lock_document.

T6.1: тесты — содержимое аудита правки значения и акцепта, исходное значение даты отчёта, дата не на конце квартала,
сброс акцепта правкой только значения, неизменность формы после INVALID_INN, свежесть шапки под блокировкой.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Для BALANCE/INCOME подтверждение требует определённого периода (PERIOD_REQUIRED) и пройденных либо акцептованных
жёстких проверок §7 (CHECKS_NOT_PASSED, нарушения в detail.failures); записывает financial_statement_meta.reviewed_by/at.
Шапка читается под блокировкой документа с refresh, чтобы не использовать устаревший акцепт.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
POST/GET /documents/{id}/comments, счётчик комментариев в карточке файла
и документа одним запросом на пачку документов.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Поток из S3 порциями без чтения в память; нет объекта — 404 FILE_CONTENT_NOT_FOUND до заголовков;
при обрыве потребителя ответ S3 закрывается и отпускается. Content-Disposition вынесен в common/filenames.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Добавлен эндпоинт GET /api/v1/users/brief для получения краткого справочника
пользователей (id, ФИО, статус активности) по списку id. Доступен любому
аутентифицированному пользователю без отдельного права. Максимум 200 id,
иначе 422 TOO_MANY_IDS; некорректный UUID — 422 INVALID_IDS.

- Создан api/src/api/endpoints/users.py с маршрутом /brief
- Добавлен метод users_brief в repositories/users.py
- Добавлена схема UserBriefOut в schemas/admin.py
- Подключён маршрут users в build_api_router ДО admin.router
- Обновлены тесты OpenAPI: добавлены маршрут и префикс /users/brief
- Написаны интеграционные тесты в test_api_users_brief.py

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Добавлено поле documents_count в LegalEntityOut, содержащее число документов
файлов ЮЛ со статусом не SUPERSEDED. Функция documents_count_by_entity в
репозитории получает счётчики одним подзапросом для эффективности. Используется
в списке и карточке через сервис. Тест проверяет, что заменённые файлы не
учитываются в подсчёте.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Добавлен экспорт чек-листа в CSV-формате при параметре ?format=csv:
- Реализована функция render_checklist_csv в src/domain/checklist_csv.py
- UTF-8 кодировка с BOM, разделитель «;», окончание строк CRLF
- Подписи статусов: CONFIRMED→«Загружен», PROCESSING→«Обработка»,
  NEEDS_CHECK/RECOGNIZED→«Требуется проверка», ERROR→«Ошибка загрузки»,
  MISSING→«Отсутствует»
- При missing_quarters добавляется подстрока « (нет: Q1, Q2)»
- Имя файла: checklist-<ГК>-<год>-<квартал>.csv с поддержкой RFC 5987
- Используется существующая функция content_disposition из filenames.py
- Добавлена функция version_title в ConsolidationService для получения
  названия ГК, года и квартала версии

Тесты:
- test_domain_checklist_csv.py: юнит-тесты функций render и filename
- test_api_consolidation_checklist.py: интеграционный тест CSV-ответа

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
- ClosingStreamingResponse закрывает body_iterator в finally (под shield), ChainedStream закрывает
  исходный поток и до начала чтения; заголовок X-Content-Type-Options: nosniff;
- ASCII-имя в Content-Disposition строится из file_type, а не из расширения исходного имени;
- ObjectStorage.stream типизирован как AsyncGenerator, убраны два type: ignore.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
- HeaderPatch.units: обрезка пробелов и min_length=1, пробельная строка — 422; null — «не менять» (docstring);
- запись аудита сброса акцепта содержит comment сброшенного акцепта.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
- ЮЛ: service.documents_count(entity_id), _out с типом LegalEntity и обязательным documents_count;
- чек-лист: в OpenAPI описан ответ text/csv, BOM — константа, elif после return убраны,
  тест на полноту подписей CellStatus и на байты BOM.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Проверки §7 по колонкам, допуск, abs_terms, M-9, граница округления; граничные случаи маппинга колонок и
уверенности; шапка, смешанные снимки, статус при задании needs_check, предупреждение о повторе формы и
идемпотентный повторный маппинг форм; редакция формы, пара ББ/ОФР, 403 без права, подтверждение формы без шапки
и без дат; комментарий без видимости ЮЛ; справочник пользователей; documents_count; CSV чек-листа.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
aep merged commit 3931ea0173 into main 2026-10-07 14:20:49 +03:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RSHB/arm-core#39
No description provided.