docs(arm-core): README, CLAUDE.md, tasks.md под Срез 3b
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
This commit is contained in:
parent
0c203c1d91
commit
dea64c9f1d
22
CLAUDE.md
22
CLAUDE.md
@ -25,7 +25,9 @@
|
||||
ИНН, `file_types.py` — тип файла по расширению и сигнатуре, `osv_mapping.py` — результат парсера → документ/строки
|
||||
ОСВ (§1.3, §1.5, §9.3); Срез 4 — `consolidation_periods.py` (выбор источников и приведение периодов, §2, Q1-эквивалентность ОСВ,
|
||||
R-14), `consolidated_osv.py` (чистая сборка свода: склейка кварталов, пробелы, дорезолв, фильтры, §2–§3), `checklist.py`
|
||||
(чек-лист комплекта, ячейки `CellStatus`); `policy.py` дополнен `IN_WORK_STATUSES`/`ensure_version_editable`, §10; для шахматки
|
||||
(чек-лист комплекта, ячейки `CellStatus`); Срез 3b — `statement_mapping.py` (формы ББ/ОФР: период, черновик, статус D-6),
|
||||
`statement_checks.py` (проверки §7 по словарю `api/rules/statement_checks.yaml`), `checklist_csv.py` (CSV чек-листа);
|
||||
`policy.py` дополнен `IN_WORK_STATUSES`/`ensure_version_editable`, §10; для шахматки
|
||||
и КФО — Срезы 5–6), `contract/` (модели контракта обмена), `api/scripts/` (`check_dbml_vs_orm.py`,
|
||||
`seed.py` + `seed_lib/`, `check_infra.py`), данные seed — `api/seed/`.
|
||||
- **DI** — ручные фабрики `Depends` в `src/api/dependencies/`; `storage`/`nats` — из `request.app.state`. **Сессия** —
|
||||
@ -83,6 +85,24 @@
|
||||
взаимоблокировки; автозамена дополнительно берёт advisory-lock по ЮЛ. Аудит: `FILE_UPLOAD`,
|
||||
`FILE_REPLACE` (системное, `user_id` пуст), `FILE_DELETE`, `DOCUMENT_CONFIRM`; смена статусов обмена и маппинга не аудируется
|
||||
(OQ-55). `osv_entry` пишет только маппер, один раз на файл (очистка перед вставкой); `TOTAL` не хранится.
|
||||
- **Формы ББ/ОФР (Срез 3b)** — `map_file` (`services/result_mapping.py`) мапит и формы ББ/ОФР: `domain/statement_mapping.py` (чистые
|
||||
функции) + `repositories/statements.py` (`financial_statement_meta/column/line/value`); форма создаётся с тем, что есть в результате (колонки могут отсутствовать);
|
||||
документ `BALANCE`/`INCOME` без `meta` (создан до Среза 3b) подтверждается без условий D-9, `…/statement` по нему — 409
|
||||
`DOCUMENT_NOT_STATEMENT` (решение №16). Проверки §7 — `domain/statement_checks.py` по словарю `api/rules/statement_checks.yaml`; **файл —
|
||||
копия словаря tabular-parser, при изменении правится в обоих репозиториях** (расхождение молчаливо меняет результат проверок).
|
||||
Проверки считаются на лету и не хранятся; хранится только акцепт (`checks_accepted_by/at/comment` в шапке). Правка формы и акцепт —
|
||||
`services/statements.py` (`PATCH …/statement`, `POST …/statement/checks/accept`): **только до подтверждения** (`CONFIRMED` →
|
||||
`DOCUMENT_READONLY`), аудит `RECOGNIZED_DATA_EDIT` (раздел `RECOGNITION`, `object_type=document`; один запрос — одно событие), первичка
|
||||
рядом (`original_amount`, `*_original_value`, `is_modified`/`*_modified`); любая правка сбрасывает акцепт и пересчитывает статус
|
||||
документа. **Порядок блокировок «файл → документ»**, шапка (`meta`) читается ПОД блокировкой с `refresh=True` — иначе в сессии
|
||||
остаётся устаревший акцепт. Подтверждение формы (`services/documents._check_statement`) требует период (`PERIOD_REQUIRED`) и
|
||||
пройденные/акцептованные жёсткие проверки (`CHECKS_NOT_PASSED`, `detail.failures`); мягкие (в т. ч. межформенная 1370↔2400)
|
||||
не блокируют. Комментарии к документу — `services/documents.py` (`comments`/`add_comment`, `comment(object_type='document')`,
|
||||
без аудита; `comments_count` в карточках). `GET /files/{id}/content` — `services/primary_files.content` +
|
||||
`ObjectStorage.stream` (поток из S3, первая порция до заголовков → `FILE_CONTENT_NOT_FOUND`; сессию тело потока не держит;
|
||||
имя — RFC 5987). `GET /users/brief` — `current_user` без права; `documents_count` у ЮЛ — `repositories/directories`; CSV чек-листа —
|
||||
`?format=csv` у `GET …/checklist`. Контракт для фронта — `../docs/dev/api-contract-slice-3b.md`; решения —
|
||||
`openspec/changes/slice-3b-statements-api/design.md`.
|
||||
- **Консолидации (Срез 4)** — `api/endpoints/consolidations/` (periods, versions, locks, status, members, checklist, osv, export,
|
||||
mapping), сервисы: `consolidations` (создание, список, карточка, периметр `PUT …/members`), `consolidation_guard`
|
||||
(`lock_visible_version`/`lock_editable_version`: версия `FOR UPDATE` → видимость 404 → статус «в работе» → своя блокировка),
|
||||
|
||||
107
README.md
107
README.md
@ -28,6 +28,12 @@ PostgreSQL 16, core NATS без JetStream), `web/` — фронтенд (React +
|
||||
«Первичные документы: загрузка», «Статусы файла и документа», «Цикл маппинга», «API файлов и документов», «Удаление и
|
||||
замена», «Ограничения Среза 2c», «Сквозной прогон (e2e)».
|
||||
|
||||
Срез 3b («формы ББ/ОФР в ядре и API до сводной ОСВ»): маппинг форм ББ/ОФР из результата парсера в ядро, форма для
|
||||
экрана «Проверка распознавания» с проверками §7, правка формы до подтверждения и акцепт проверок, условия подтверждения
|
||||
формы, комментарии к документам, исходный файл потоком, имена пользователей, `documents_count` у ЮЛ, CSV чек-листа.
|
||||
Спецификация — `openspec/changes/slice-3b-statements-api/`; разделы ниже: «API файлов и документов», «Формы ББ/ОФР (Срез 3b)»,
|
||||
«Ошибки API».
|
||||
|
||||
Срез 4 («консолидация»): схема по DBML v5.16, версии консолидации ГК за период, периметр, блокировка «взять в работу»,
|
||||
статусы, привязка подтверждённых документов к версии, чек-лист, сводная ОСВ, ручное сопоставление контрагента, экспорт xlsx.
|
||||
Спецификация — `openspec/changes/slice-4-consolidation/`; раздел ниже: «Консолидация (Срез 4)».
|
||||
@ -232,9 +238,9 @@ docker compose -f ../docs/dev/docker-compose.yml down -v # полный сб
|
||||
| `DELETE /directories/related-groups/{group_id}` | `DIRECTORY_DELETE` | удалить **пустую** ГСК/ГСЗ (`GROUP_NOT_EMPTY`) |
|
||||
| `POST /directories/related-groups/{group_id}/members` | `DIRECTORY_EDIT` | добавить ЮЛ (только из той же ГК) |
|
||||
| `DELETE /directories/related-groups/{group_id}/members/{legal_entity_id}` | `DIRECTORY_EDIT` | исключить ЮЛ (членство закрывается, не удаляется) |
|
||||
| `GET /directories/legal-entities` | `DIRECTORY_VIEW` | видимые ЮЛ/ИП: `company_group_id`, `outside_group`, `inn`, `q`, `closed`, `limit`/`offset` |
|
||||
| `GET /directories/legal-entities` | `DIRECTORY_VIEW` | видимые ЮЛ/ИП: `company_group_id`, `outside_group`, `inn`, `q`, `closed`, `limit`/`offset`; в каждой записи `documents_count` |
|
||||
| `POST /directories/legal-entities` | `DIRECTORY_EDIT` | создать ЮЛ/ИП (ИНН: ЮЛ — 10 цифр, ИП — 12) |
|
||||
| `GET`, `PATCH /directories/legal-entities/{entity_id}` | `DIRECTORY_VIEW` / `DIRECTORY_EDIT` | карточка (с членствами и алиасами) / изменить |
|
||||
| `GET`, `PATCH /directories/legal-entities/{entity_id}` | `DIRECTORY_VIEW` / `DIRECTORY_EDIT` | карточка (с членствами, алиасами и `documents_count`) / изменить |
|
||||
| `POST /directories/legal-entities/{entity_id}/move` | `DIRECTORY_EDIT` | перевести в другую ГК с датой `effective_date` ≥ сегодня (дата по Москве, Europe/Moscow); членства ГСК/ГСЗ закрываются |
|
||||
| `POST /directories/legal-entities/{entity_id}/withdraw` | `DIRECTORY_EDIT` | вывести из ГК: `CLOSE` (с `close_date`) или `REMOVE_FROM_GROUP` |
|
||||
| `GET /directories/legal-entities/{entity_id}/history` | `DIRECTORY_VIEW` | история ЮЛ/ИП, новые первыми |
|
||||
@ -243,6 +249,9 @@ docker compose -f ../docs/dev/docker-compose.yml down -v # полный сб
|
||||
| `POST /directories/import` | `DIRECTORY_ADMIN` | импорт выгрузки ЕЦП CRM (см. ниже) |
|
||||
| `GET /admin/users`, `POST /admin/users`, `PATCH /admin/users/{user_id}`, `PUT /admin/users/{user_id}/roles` | `USERS_ADMIN` | пользователи и их роли (см. «Администрирование») |
|
||||
| `GET /admin/roles`, `GET /admin/permissions`, `PUT /admin/roles/{code}/permissions` | `USERS_ADMIN` | матрица «роль × право» |
|
||||
| `GET /users/brief?ids=<uuid>,<uuid>` | любой аутентифицированный (права не нужны) | `[{id, full_name, is_active}]` для найденных пользователей (в т. ч. неактивных; неизвестные id пропускаются) — чтобы фронт показывал имена вместо UUID (`confirmed_by`, `locked_by`, авторы комментариев); до 200 id, больше — `TOO_MANY_IDS`, некорректный UUID — `INVALID_IDS` |
|
||||
|
||||
`documents_count` у ЮЛ — число документов его файлов, кроме файлов `SUPERSEDED` (подсчёт одним запросом на страницу).
|
||||
|
||||
Списки верхнего уровня (ГК, ЮЛ, пользователи) отдают страницу `{items, total, limit, offset}` (`limit` 1–200, по
|
||||
умолчанию 50); список ГСК/ГСЗ одной ГК — простой массив. Каждая мутация пишет `audit_event` в той же транзакции, что и
|
||||
@ -476,12 +485,12 @@ S3 (storage); без S3 — предупреждение в логе, цикл
|
||||
отвергаются);
|
||||
2. читает объект из S3 **вне транзакции** и валидирует `RecognitionResult` v1.x (`job_id`/`file_id` должны совпасть с заданием);
|
||||
3. в одной транзакции (порядок блокировок «задание → файл») удаляет прежние строки файла (идемпотентность), создаёт
|
||||
`document` на каждый лист ОСВ и пакетами вставляет `osv_entry` (без строки `TOTAL`; `parent_entry_id`, `account_root`,
|
||||
`document` на каждый лист ОСВ и на каждую форму ББ/ОФР (шапка, колонки, строки, значения; см. «Формы ББ/ОФР»), пакетами вставляет `osv_entry` (без строки `TOTAL`; `parent_entry_id`, `account_root`,
|
||||
`account_id`, `counterparty_id`), пишет `recognition_job.group_schema` и `metrics.mapping_warnings`, переводит файл
|
||||
в `PROCESSED` и выполняет автозамену (см. «Удаление и замена»).
|
||||
|
||||
Ошибки: **неповторяемые** (ключ вне папки задания, нет объекта, результат другого задания, невалидный результат, нет
|
||||
листов ОСВ) сразу переводят файл в `ERROR` с `metrics.mapping_error`; **повторяемые** (БД, S3) откатывают транзакцию,
|
||||
документов) сразу переводят файл в `ERROR` с `metrics.mapping_error`; **повторяемые** (БД, S3) откатывают транзакцию,
|
||||
увеличивают `metrics.mapping_attempts`, цикл спит `MAPPING_POLL_SECONDS` и пробует снова; после `MAPPING_MAX_ATTEMPTS`
|
||||
— `ERROR`. Пока попытки не исчерпаны, файл остаётся `PROCESSING`; повторный маппинг начинается с очистки строк файла, дублей нет.
|
||||
|
||||
@ -507,13 +516,64 @@ S3 (storage); без S3 — предупреждение в логе, цикл
|
||||
| `DELETE /files/{file_id}` | `DOCUMENT_EDIT` | удалить файл (см. «Удаление и замена»), 204 |
|
||||
| `GET /documents/{document_id}` | `DOCUMENT_VIEW` | документ: статус, проверки, `locator`, `group_schema` листа, `warnings`, `file_id` |
|
||||
| `GET /documents/{document_id}/entries` | `DOCUMENT_VIEW` | строки ОСВ по `row_number` (`limit` ≤ 2000, по умолчанию 500); дерево — по `parent_entry_id` |
|
||||
| `POST /documents/{document_id}/confirm` | `DOCUMENT_EDIT` | `RECOGNIZED`/`NEEDS_CHECK` → `CONFIRMED`, аудит `DOCUMENT_CONFIRM` |
|
||||
| `POST /documents/{document_id}/confirm` | `DOCUMENT_EDIT` | `RECOGNIZED`/`NEEDS_CHECK` → `CONFIRMED`, аудит `DOCUMENT_CONFIRM`; для формы ББ/ОФР — ещё `PERIOD_REQUIRED` и `CHECKS_NOT_PASSED` |
|
||||
| `GET /documents/{document_id}/statement` | `DOCUMENT_VIEW` | форма ББ/ОФР: шапка, колонки, строки со значениями, редакция, проверки §7 (см. «Формы ББ/ОФР») |
|
||||
| `PATCH /documents/{document_id}/statement` | `DOCUMENT_EDIT` | правка значений и шапки (`inn`, `report_date`, `units`) до подтверждения; аудит `RECOGNIZED_DATA_EDIT` |
|
||||
| `POST /documents/{document_id}/statement/checks/accept` | `DOCUMENT_EDIT` | акцепт непройденных жёстких проверок с комментарием (`{comment}`) |
|
||||
| `GET /documents/{document_id}/comments` | `DOCUMENT_VIEW` | комментарии по возрастанию `created_at`: `{id, text, author {id, full_name}, created_at}` |
|
||||
| `POST /documents/{document_id}/comments` | `DOCUMENT_EDIT` | добавить комментарий (`{text}`, 1–4000 символов после обрезки пробелов), 201; в любом статусе документа |
|
||||
| `GET /files/{file_id}/content` | `DOCUMENT_VIEW` | исходный файл потоком из S3; `?disposition=attachment` (по умолчанию) или `inline`; имя — `Content-Disposition` по RFC 5987 |
|
||||
|
||||
Карточка документа и документ в карточке файла несут `comments_count`. Просмотр и скачивание первички (`/files/{id}/content`)
|
||||
и CSV чек-листа в аудит не пишутся (как экспорт сводной ОСВ). Чужой и несуществующий файл — 404 `NOT_FOUND`; файл есть, а объекта
|
||||
в S3 нет (истёк или пропал) — 404 `FILE_CONTENT_NOT_FOUND`; S3 не подключено — 503 `STORAGE_UNAVAILABLE`.
|
||||
|
||||
Страницы — `{items, total, limit, offset}`. Суммы строк — десятичные значения без округления, как в результате парсера.
|
||||
Коды ошибок этих маршрутов — в таблице «Ошибки API» (`FILE_TOO_LARGE`, `UNSUPPORTED_FILE`, `DUPLICATE_FILE`,
|
||||
`STORAGE_UNAVAILABLE`, `FILE_SUPERSEDED`, `ALREADY_CONFIRMED`, `DOCUMENT_NOT_CONFIRMABLE`,
|
||||
`FILE_HAS_CONFIRMED_DOCUMENTS`, `FILE_SUPERSEDES_OTHERS`). События аудита: `FILE_UPLOAD`, `FILE_REPLACE`, `FILE_DELETE`,
|
||||
`DOCUMENT_CONFIRM` (раздел `PRIMARY_DOCUMENTS`).
|
||||
`FILE_HAS_CONFIRMED_DOCUMENTS`, `FILE_SUPERSEDES_OTHERS`, `FILE_CONTENT_NOT_FOUND`) и форм ББ/ОФР (`DOCUMENT_NOT_STATEMENT`,
|
||||
`DOCUMENT_READONLY`, `UNKNOWN_CELL`, `DUPLICATE_CELL`, `INVALID_INN`, `NOTHING_TO_ACCEPT`, `PERIOD_REQUIRED`,
|
||||
`CHECKS_NOT_PASSED`). События аудита: `FILE_UPLOAD`, `FILE_REPLACE`, `FILE_DELETE`, `DOCUMENT_CONFIRM` (раздел
|
||||
`PRIMARY_DOCUMENTS`), `RECOGNIZED_DATA_EDIT` (раздел `RECOGNITION`; правка формы и акцепт).
|
||||
|
||||
## Формы ББ/ОФР (Срез 3b)
|
||||
|
||||
Цикл маппинга разбирает не только листы ОСВ, но и формы ББ/ОФР результата парсера: на каждую форму создаются `document`
|
||||
(`BALANCE`/`INCOME`) и шапка `financial_statement_meta` с колонками, строками и значениями (`financial_statement_column`,
|
||||
`_line`, `_value`). Первичка не мутируется: правка аналитика пишется рядом (`is_modified`, `original_amount`,
|
||||
`modified_by/at`; у полей шапки — `*_modified`, `*_original_value`). Период формы — по дате отчёта, а при её отсутствии по самой
|
||||
поздней дате колонок; ББ/ОФР всегда нарастающие. Статус документа формы — `RECOGNIZED` только если задание `done`, нет нарушений
|
||||
проверок §7 (или они акцептованы), контрагент не `MISMATCH`, период определён, значения разобраны, есть строки и нет
|
||||
предупреждений; иначе `NEEDS_CHECK` (правило D-6 спеки). Проверки §7 (`domain/statement_checks.py`) считаются на лету по
|
||||
текущим суммам формы и не хранятся; словарь — `api/rules/statement_checks.yaml` (копия словаря tabular-parser — правится
|
||||
в обоих репозиториях).
|
||||
|
||||
- **`GET /documents/{id}/statement`** — `{document, edition, header, columns[], lines[], checks}`: `header` — `inn`,
|
||||
`report_date`, `units` вида `{value, original_value, verified, modified}` (у ИНН ещё `confidence`) и справочные поля формы
|
||||
(`organization_name`, `okud`, `okpo`, …); `lines[].values[]` — суммы строкой, `is_modified`, `original_amount`; `checks` —
|
||||
`{failed, soft_failed, cross_form, accepted}` (нарушение — `{column_index, relation, expected, actual}`, у межформенной
|
||||
проверки `column_index = -1`). Не форма (ОСВ, расшифровка, ББ/ОФР без данных формы — созданные до Среза 3b) — 409 `DOCUMENT_NOT_STATEMENT`.
|
||||
Подтверждённая форма читается.
|
||||
- **`PATCH /documents/{id}/statement`** — `{header?: {inn?, report_date?, units?}, values?: [{line_id, column_id, amount}]}`
|
||||
(до 5000 ячеек; `amount: null` очищает ячейку; не более двух знаков). Правится только неподтверждённая форма
|
||||
(`RECOGNIZED`/`NEEDS_CHECK`): `CONFIRMED` — 409 `DOCUMENT_READONLY` (исправление — новым файлом), заменённый файл —
|
||||
409 `FILE_SUPERSEDED`, статус `ERROR` — 409 `DOCUMENT_NOT_CONFIRMABLE`. Ячейка не из этой формы — 422 `UNKNOWN_CELL`,
|
||||
одна ячейка дважды — 422 `DUPLICATE_CELL`, ИНН не из 10 или 12 цифр — 422 `INVALID_INN`. Один запрос — одна транзакция и одно
|
||||
событие `RECOGNIZED_DATA_EDIT` с `before/after`; возврат ячейки к исходному значению снимает `is_modified`; правка ИНН
|
||||
пересчитывает `counterparty_check`, даты отчёта — период документа; любая правка сбрасывает акцепт и пересчитывает статус.
|
||||
Запрос без изменений — 200 без аудита. Ответ — та же форма, что у `GET`.
|
||||
- **`POST /documents/{id}/statement/checks/accept`** — `{comment}` (1–2000 символов): акцепт нарушенных жёстких проверок
|
||||
(`checks_accepted_by/at/comment`). Нарушений нет — 409 `NOTHING_TO_ACCEPT`. Акцепт сбрасывается любой правкой формы.
|
||||
- **Подтверждение формы** (`POST /documents/{id}/confirm`) дополнительно требует определённый период (иначе 409
|
||||
`PERIOD_REQUIRED`) и пройденные или акцептованные жёсткие проверки §7 (иначе 409 `CHECKS_NOT_PASSED`; в `detail.failures[]` —
|
||||
`{column_index, relation, expected, actual}`). Мягкие проверки (в т. ч. межформенная `1370 − 1370(начало года) = 2400`)
|
||||
подтверждение не блокируют. Подтверждённая форма привязывается к версиям консолидации и закрывает ячейки ББ/ОФР чек-листа, как ОСВ.
|
||||
- **Порядок блокировок** правки и акцепта — «файл → документ» (как у подтверждения); шапка перечитывается под блокировкой.
|
||||
Комментарии аудит не пишут; при удалении файла комментарии его документов остаются в таблице `comment` без владельца
|
||||
(полиморфная связь без FK, решение №18; наружу не отдаются).
|
||||
|
||||
Расчёт — `src/domain/statement_mapping.py` и `src/domain/statement_checks.py` (чистые функции), доступ к данным —
|
||||
`src/repositories/statements.py`, правка и акцепт — `src/services/statements.py`.
|
||||
|
||||
## Удаление и замена
|
||||
|
||||
@ -538,8 +598,8 @@ S3 (storage); без S3 — предупреждение в логе, цикл
|
||||
- Объекты S3 при удалении не стираются (OQ-22, OQ-111): растёт бакет; e2e оставляет ~2 объекта за прогон.
|
||||
- Общий размер multipart-запроса `POST /legal-entities/{id}/files` и число файлов в нём приложением не ограничены
|
||||
(Starlette спулит части на диск, `UPLOAD_MAX_BYTES` — лимит одного файла); лимит запроса задаётся ingress платформы.
|
||||
- Маппер Среза 2c разбирает только листы ОСВ (`osvDocument`); ББ/ОФР и результаты `scan-gateway` — следующие срезы,
|
||||
прочие документы результата (BALANCE/INCOME/OTHER) игнорируются без предупреждения.
|
||||
- Маппер Среза 2c разбирал только листы ОСВ (`osvDocument`); с Среза 3b он мапит и формы ББ/ОФР (раздел «Формы ББ/ОФР»).
|
||||
Результаты `scan-gateway` (PDF/изображения) и документы `OTHER` по-прежнему игнорируются без предупреждения.
|
||||
- Корневой сегмент счёта (`account_root`) — только цифровой (`ASSUMPTION(OQ-103)`): забалансовые «МЦ.04», «ГТД»
|
||||
получают `account_root = NULL`; `account_id` ищется точным совпадением с планом счетов seed, субсчетов в seed
|
||||
неполный набор — часть строк остаётся с `account_id = NULL`.
|
||||
@ -547,8 +607,7 @@ S3 (storage); без S3 — предупреждение в логе, цикл
|
||||
- ОСВ «нарастающая по данным Q1» (Q2–Q4 с `opening` корня, равным `opening` Q1) получает предупреждение маппинга,
|
||||
поэтому документ остаётся `NEEDS_CHECK`: аналитик подтверждает его вручную (решение R-17, ждёт подтверждения владельца).
|
||||
- `period_check` — `UNKNOWN` (OQ-120); к версии консолидации привязываются только `CONFIRMED` документы, а их файл удалить
|
||||
нельзя — удаление файла с документом в версии невозможно (OQ-110, Срез 4); FK `financial_statement_meta` и `ex_snapshot_item`
|
||||
— Срезы 3 и 7; `EntryOut` не отдаёт `is_modified` (панель правки — подсрез 2d).
|
||||
нельзя — удаление файла с документом в версии невозможно (OQ-110, Срез 4); FK `ex_snapshot_item` — Срез 7; `EntryOut` не отдаёт `is_modified` (панель правки — подсрез 2d).
|
||||
- Два экземпляра ядра могут одновременно мапить один файл (второй проиграет блокировку и пропустит кандидата);
|
||||
при уменьшении `MAPPING_MAX_ATTEMPTS` файлы с исчерпанным счётчиком остаются `PROCESSING` до ручного вмешательства.
|
||||
|
||||
@ -574,7 +633,7 @@ S3 (storage); без S3 — предупреждение в логе, цикл
|
||||
| `DELETE /consolidations/versions/{id}/lock` | EDIT | снять свою блокировку (204; нет активной — тоже 204) |
|
||||
| `POST /consolidations/versions/{id}/status` | EDIT | ручной переход: `CANCELLED` (с комментарием) или возврат в `INITIATED` |
|
||||
| `PUT /consolidations/versions/{id}/members` | EDIT | заменить периметр (ЮЛ должны принадлежать ГК, ≥ 2) |
|
||||
| `GET /consolidations/versions/{id}/checklist` | VIEW | комплект документов: ЮЛ × правила, статус ячейки |
|
||||
| `GET /consolidations/versions/{id}/checklist` | VIEW | комплект документов: ЮЛ × правила, статус ячейки; `?format=csv` — тот же чек-лист файлом CSV |
|
||||
| `GET /consolidations/versions/{id}/osv` | VIEW | страница сводной ОСВ и фильтры (`limit` ≤ 2000, по умолчанию 500) |
|
||||
| `GET /consolidations/versions/{id}/osv/summary` | VIEW | `rows_total`, `unmapped_total`, `auto_matched_total`, `documents_total`, `gaps[]` |
|
||||
| `GET /consolidations/versions/{id}/osv/export` | VIEW | xlsx: лист «Сводная ОСВ» и лист «Пробелы» |
|
||||
@ -631,6 +690,13 @@ Q2–Q4 не подходят (R-14a). Привязываются только `
|
||||
`ERROR` (последний файл ЮЛ в `ERROR`), `MISSING`; `missing_quarters` — пропущенные кварталы при частичном покрытии ОСВ.
|
||||
`PROCESSING` ставится по всем недостающим столбцам ЮЛ, пока файл не смаплен (до маппинга счёт файла неизвестен).
|
||||
|
||||
`GET …/checklist?format=csv` (Срез 3b, право `CONSOLIDATION_VIEW`) отдаёт чек-лист файлом `text/csv`: UTF-8 с BOM, разделитель `;`,
|
||||
строки через CRLF, заголовок — «Организация;ИНН;<правило>…» (подписи правил: «Бух. баланс», «ОФР», «ОСВ 66»), строки — ЮЛ, в ячейках — подписи
|
||||
статусов («Загружен», «Обработка», «Требуется проверка», «Ошибка загрузки», «Отсутствует»; при пропущенных кварталах —
|
||||
«… (нет: Q1, Q2)»); имя файла — `checklist-<ГК>-<год>-<квартал>.csv`
|
||||
в `Content-Disposition` по RFC 5987. Формат по умолчанию — `json`; другое значение `format` — 422 валидации. Скачивание в аудит не пишется
|
||||
(`domain/checklist_csv.py`, чистая функция).
|
||||
|
||||
### Сводная ОСВ
|
||||
|
||||
Чистая функция от версии (`domain/consolidated_osv.build` + `domain/consolidation_periods.select_sources`), в БД не пишется
|
||||
@ -728,7 +794,7 @@ email без учёта регистра проверяет только сер
|
||||
| `NOT_FOUND` | 404 | объекта нет или он вне видимости; в консолидациях различие — по тексту: «версия консолидации не найдена», «ГК не найдена», «ГСК/ГСЗ не найдена», «строка ОСВ не найдена в версии», «ЮЛ не найдено» |
|
||||
| `OFFICE_NOT_FOUND` | 422 | филиал из тела запроса не существует (ГК, ЮЛ, пользователь) |
|
||||
| `OFFICE_NOT_ALLOWED` | 422 | ГК можно закрепить только за своим филиалом |
|
||||
| `INVALID_INN` | 422 | ИНН не из 10 (ЮЛ) / 12 (ИП) цифр |
|
||||
| `INVALID_INN` | 422 | ИНН не из 10 (ЮЛ) / 12 (ИП) цифр; в правке формы (`PATCH …/statement`) — не из 10 или 12 цифр |
|
||||
| `EFFECTIVE_DATE_IN_PAST` | 422 | дата перевода/вывода раньше сегодняшней (по Москве) |
|
||||
| `EMPTY_ALIAS` | 422 | алиас пуст после нормализации (одни ОПФ и знаки препинания) |
|
||||
| `DIFFERENT_COMPANY_GROUP` | 422 | ЮЛ не входит в ГК этой ГСК/ГСЗ |
|
||||
@ -756,6 +822,15 @@ email без учёта регистра проверяет только сер
|
||||
| `DOCUMENT_NOT_CONFIRMABLE` | 409 | подтверждение: статус документа не `RECOGNIZED`/`NEEDS_CHECK` (например, `ERROR`) |
|
||||
| `FILE_HAS_CONFIRMED_DOCUMENTS` | 409 | удаление файла с `CONFIRMED` документом |
|
||||
| `FILE_CONTENT_NOT_FOUND` | 404 | `GET /files/{id}/content`: файл есть, объекта нет в S3 (истёк или пропал) |
|
||||
| `DOCUMENT_NOT_STATEMENT` | 409 | `…/statement`: документ — не форма ББ/ОФР (ОСВ, расшифровка, ББ/ОФР без данных формы — созданные до Среза 3b) |
|
||||
| `DOCUMENT_READONLY` | 409 | правка/акцепт формы: форма уже `CONFIRMED` (исправление — новым файлом) |
|
||||
| `UNKNOWN_CELL` | 422 | `PATCH …/statement`: `line_id`/`column_id` не принадлежат этой форме |
|
||||
| `DUPLICATE_CELL` | 422 | `PATCH …/statement`: одна ячейка указана в запросе дважды |
|
||||
| `NOTHING_TO_ACCEPT` | 409 | акцепт: непройденных жёстких проверок нет (в том числе уже акцептованы) |
|
||||
| `PERIOD_REQUIRED` | 409 | подтверждение формы: период не определён — поправьте дату отчёта |
|
||||
| `CHECKS_NOT_PASSED` | 409 | подтверждение формы: жёсткие проверки §7 не пройдены и не акцептованы; `detail.failures[] {column_index, relation, expected, actual}` |
|
||||
| `TOO_MANY_IDS` | 422 | `GET /users/brief`: больше 200 id |
|
||||
| `INVALID_IDS` | 422 | `GET /users/brief`: элемент `ids` — не UUID |
|
||||
| `FILE_SUPERSEDES_OTHERS` | 409 | удаление файла, на который ссылаются `superseded_by_file_id` других файлов |
|
||||
| `CONSOLIDATION_EXISTS` | 409 | консолидация этой ГК за год и квартал уже создана (в т. ч. гонка двух `POST /consolidations`) |
|
||||
| `PERIMETER_TOO_SMALL` | 422 | в ГСК (при создании) или в новом периметре (`PUT …/members`) меньше двух ЮЛ; ничего не создаётся и не меняется |
|
||||
@ -918,6 +993,7 @@ make migration-cycle # проверить цикл upgr
|
||||
api/
|
||||
run_server.py точка входа (uvicorn; хост и порт — из настроек)
|
||||
alembic/ миграции (0001_dbml_v5_13.py — модель v5.13, 0002_dbml_v5_14.py — v5.14, 0003_dbml_v5_15.py — v5.15, 0004_dbml_v5_16.py — v5.16)
|
||||
rules/ statement_checks.yaml — словарь проверок §7 формы ББ/ОФР (копия словаря tabular-parser; меняется в обоих репозиториях)
|
||||
seed/core/ справочники продукта (роли, права, филиалы, план счетов, правила элиминации, чек-лист)
|
||||
seed/dev/ dev-данные: филиалы и пользователи по ролям
|
||||
scripts/ check_dbml_vs_orm.py (ORM <-> DBML), seed.py и seed_lib/ (загрузчик справочников)
|
||||
@ -932,13 +1008,16 @@ api/
|
||||
repositories/ доступ к данным
|
||||
schemas/ pydantic-схемы запросов и ответов
|
||||
services/ бизнес-логика; primary_files (загрузка/удаление), result_mapping (цикл маппинга и автозамена),
|
||||
file_status (статус файла по заданию), documents (карточки, подтверждение), recognition_exchange (обмен);
|
||||
file_status (статус файла по заданию), documents (карточки, подтверждение, комментарии), statements (форма ББ/ОФР: чтение, правка, акцепт; Срез 3b),
|
||||
recognition_exchange (обмен);
|
||||
Срез 4 — consolidations (версии, периметр), consolidation_guard (общий пролог правок), consolidation_locks,
|
||||
consolidation_status (`transition`), consolidation_documents (привязка), consolidated_osv (свод, кандидаты),
|
||||
counterparty_mapping (ручное сопоставление), osv_export (xlsx)
|
||||
common/ доменные ошибки (`{detail: {code, message}}`), часы
|
||||
domain/ чистые функции по §§ правил, без БД: policy (видимость), directory_import (§13), aliases (§3), inn,
|
||||
file_types (тип файла по расширению и сигнатуре), osv_mapping (результат парсера → документы/строки, §1.3, §1.5, §9.3);
|
||||
Срез 3b — statement_mapping (формы ББ/ОФР: период, статус, §9.3, §9.5), statement_checks (проверки §7),
|
||||
checklist_csv (CSV чек-листа);
|
||||
Срез 4 — consolidation_periods (выбор источников, §2), consolidated_osv (свод), checklist (чек-лист), policy
|
||||
(`ensure_version_editable`, §10)
|
||||
tests/ pytest (юнит и integration/fixtures по маркерам)
|
||||
|
||||
@ -1,6 +1,21 @@
|
||||
# Tasks: Срез 3b — формы ББ/ОФР в ядре и API до сводной ОСВ
|
||||
|
||||
Заполняется планом (`writing-plans`) после одобрения спеки владельцем. Репозиторий — arm-core; команды — из корня
|
||||
(`make`), `pytest` — из `api/`.
|
||||
План реализации — `docs/superpowers/plans/2026-10-07-slice-3b-statements-api.md` (16 задач). Репозиторий — arm-core; команды —
|
||||
из корня (`make`), `pytest` — из `api/`.
|
||||
|
||||
- [ ] 0.1 План реализации написан и одобрен; задачи перенесены сюда из плана
|
||||
- [x] 0 Подготовка окружения (план одобрен, задачи перенесены сюда)
|
||||
- [x] 1 Проверки §7 — словарь `rules/statement_checks.yaml` и чистые функции `domain/statement_checks.py`
|
||||
- [x] 2 Чистые функции маппинга формы (`domain/statement_mapping.py`): период, черновик, статус
|
||||
- [x] 3 Помощники golden-тестов форм
|
||||
- [x] 4 Маппинг форм ББ/ОФР в `map_file` (`repositories/statements.py`): шапка, колонки, строки, значения, статус
|
||||
- [x] 5 API формы — `GET /documents/{id}/statement`
|
||||
- [x] 6 Правка формы (`PATCH …/statement`) и акцепт проверок (`POST …/statement/checks/accept`), аудит `RECOGNIZED_DATA_EDIT`
|
||||
- [x] 7 Подтверждение формы (`PERIOD_REQUIRED`, `CHECKS_NOT_PASSED`) и сквозной сценарий чек-листа
|
||||
- [x] 8 Комментарии к документу (`GET/POST /documents/{id}/comments`, `comments_count`)
|
||||
- [x] 9 Отдача исходного файла потоком (`GET /files/{id}/content`)
|
||||
- [x] 10 Справочник имён пользователей (`GET /users/brief`)
|
||||
- [x] 11 `documents_count` у ЮЛ
|
||||
- [x] 12 Чек-лист в CSV (`GET …/checklist?format=csv`)
|
||||
- [x] 13 README, CLAUDE.md, tasks.md
|
||||
- [ ] 14 Документы workspace (bounded-PR в docs): §7/§9, контракт `dev/api-contract-slice-3b.md`, OQ — PR docs
|
||||
- [ ] 15 Завершение: слияние `origin/main`, `make check-full`, `openspec validate --strict`, финальное ревью ветки, PR arm-core
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user