74 Commits

Author SHA1 Message Date
Andrey Epifancev
8940f32ae3 fix: пары ВГО — суммы со знаком, процент в упаковке, deviation_amount (ревью #175, AURORA-1536)
- суммы сторон со знаком, как в шахматке; строки с суммой < 0 в упаковку не идут, причина NEGATIVE_AMOUNT;
  при min(ΣA, ΣB) ≤ 0 процент null и элиминация 0, статус списка AUTO — только при элиминации > 0
- группы одного размера выбираются по проценту расхождения к меньшей сумме (точной дробью), ничья — по равному проценту
- elimination_amount группы и тройки переименован в deviation_amount; version_no тройки обязателен
- суммы и проценты пар в JSON — с двумя знаками
- elimination-pairs по правилу с measure ≠ closing — 422 INVALID_RULE
- дельта спеки, design.md (R-7, R-8), tasks, README

make check-full: exit 0 (1896 passed; integration 990 passed).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 21:47:47 +04:00
Andrey Epifancev
b4522addc0 feat: DBML v5.23 — версия правил обязательна; список пар ВГО версии (ревью #175, AURORA-1536)
- consolidation_version.elimination_rules_version NOT NULL: миграция 0010
  проставляет существующим версиям номер правил, действовавших на дату
  создания (по Москве), иначе последних открытых; без правил — отказ;
  создание версии без действующих правил — 409 ELIMINATION_RULES_MISSING;
  ветки «правила не зафиксированы» убраны (rules, preset, is_version_fixed
  всегда true);
- GET …/elimination-totals: все тройки «правило ББ × упорядоченная пара
  периметра» с ΣA, ΣB, расхождением, суммой элиминации, статусом
  AUTO/DISCREPANCY/ONE_SIDED/MANUAL и osv_entry_ids; сводная ОСВ строится
  один раз; сбор строк сторон вынесен в _collect_rule_sides — общий
  с упаковкой тройки.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 18:42:10 +04:00
Andrey Epifancev
888ad4b401 Merge origin/main в AURORA-1536 (ревью #175)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 18:34:35 +04:00
Andrey Epifancev
e13cd88fea Merge origin/main в feat/renew-processing-timeout (ревью #161, раунд 2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 17:59:00 +04:00
PotapovaA
827b490137 тесты + немного поправлен формат выходных данных 2026-10-23 16:20:51 +03:00
PotapovaA
010c15386b добавлен группировка строк осв для правила пресета 2026-10-23 15:55:54 +03:00
4f44d34d59 AURORA-1442: ручки возобновления обработки файла или версии консолидации, ручка обновления данных по осв 2026-10-22 14:25:19 +03:00
PotapovaA
3de0c90198 refactor(matrix): удалить unpaired-osv из ветки пресетов 2026-10-21 19:32:23 +03:00
PotapovaA
e9dc67b2e9 refactor(matrix): вынести применение корректировок из ветки пресетов 2026-10-21 18:40:05 +03:00
Andrey Epifancev
367a11ed38 chore(api): format, mypy и тест версии правил; убрать мёртвое as_of (ревью #141)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 14:04:38 +04:00
PotapovaA
487a8521da Merge branch 'main' into add-preset-shahmatka 2026-10-20 14:58:34 +03:00
PotapovaA
53c2d5c73d пресеты для шахматки 2026-10-20 14:45:27 +03:00
Andrey Epifancev
028512603a fix(statements): правки ревью B1+B2 — T2.1, T1.1, T1.2, T2.4, T2.6, T2.8
T2.1: аудит lines[] включает итоговую смену parent_row_index после пересчёта.
T1.1: тест паритета редакции приёма и пересчёта для строки с confidence < 100 и code_fixed.
T1.2: edition_codes пропускает строку ok без кода.
T2.4: общий issue_column_index для ответа формы и detail отказа подтверждения.
T2.6: удалено дублирование needs_review в statement_line_edits.
T2.8: неизвестный relative:* не даёт 500 (адаптер stored_form и _resolve_relative_columns).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 21:15:21 +04:00
Andrey Epifancev
065153e470 feat(statements): пересчёт после правки точкой входа arm_statements, §7 только из document_issue
recompute_form (services/statement_recompute.py) собирает вход из БД, зовёт engine.recompute и пишет needs_review
строк и значений, form_variant, parent_row_index расшифровок, записи relation/soft и core по P3-8, статус через
form_status_v2. GET/confirm/accept читают нарушения §7 из document_issue. Правка header.report_date пересчитывает
relative:* колонки. Временный код A12/A13 удалён.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-18 20:26:40 +04:00
Andrey Epifancev
17141c0a11 feat(contract): копии контракта обоих MAJOR, модели v2.0, приём конвертов 1.x/2.0, предупреждения файла структурами
Копии api/contracts/ побайтно равны docs/contracts/ (тест идентичности плана 0): схемы v2.0 основные, .v1 рядом,
examples/ — v2.0, examples/v1/ — v1.0. Модели result_v2.py и RecognitionResultMessageV2, versions.py выбирает модель
по MAJOR. Приём конвертов принимает оба MAJOR; metrics.warnings в recognition_job пишется структурами (строки 1.x
оборачиваются в LEGACY_WARNING). JobBrief.warnings отдаёт структуры, мусор пропускается, БД не переписывается.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BJKMLHdxMwSUeYcdkwqX4W
2026-10-18 19:26:50 +04:00
Andrey Epifancev
8296c77fe5 fix(api): OpenAPI-коды ошибок PATCH формы, confirm и crm_id; organization_name:null — 422 (I-2, M-11)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BJKMLHdxMwSUeYcdkwqX4W
2026-10-18 19:04:40 +04:00
Andrey Epifancev
3753e32219 feat(statements): PATCH lines — назначение кода, расшифровка, подтверждение кода, LINE_CODE_TAKEN
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BJKMLHdxMwSUeYcdkwqX4W
2026-10-18 19:04:40 +04:00
Andrey Epifancev
f1fcb861a6 feat(statements): PATCH — подтверждение ячейки, дата колонки, наименование организации, подтверждение даты
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BJKMLHdxMwSUeYcdkwqX4W
2026-10-18 19:04:40 +04:00
Andrey Epifancev
65a833fd98 feat(api): StatementOut и DocumentOut по v5.19 — статусы строк, needs_review, issues, порядок строк
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BJKMLHdxMwSUeYcdkwqX4W
2026-10-18 19:04:40 +04:00
Andrey Epifancev
a15111ab60 feat(statements): агрегат формы по коду — проверки, межформенная, parent_code, достройка, правка по коду
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BJKMLHdxMwSUeYcdkwqX4W
2026-10-18 19:04:40 +04:00
Andrey Epifancev
b7f45c6a47 feat(directories): crm_id ГСК/ГСЗ сохраняется, 409 CRM_ID_EXISTS (arm-core#112)
crm_id пишется в колонку related_group.crm_id (v5.19), попадает в ответы и в
payload аудита; занятый значением другой ГСК/ГСЗ даёт 409 CRM_ID_EXISTS
(pre-check и uq_related_group_crm_id на гонку). Смена crm_id пишется как
GROUP_CHANGE с before/after всех изменённых полей (Р-41, P3-18).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BJKMLHdxMwSUeYcdkwqX4W
2026-10-18 19:04:40 +04:00
Andrey Epifancev
11b0216cff feat(statements): parent_code у строк формы ББ/ОФР, общая subline_parents (#118, AURORA-1527)
Правило «подстрока относится к ближайшей четырёхзначной строке выше с теми же тремя цифрами,
иначе к строке из первых четырёх цифр» вынесено в subline_parents; им пользуются проверка
«сумма подстрок = родитель» и ответ GET /documents/{id}/statement (поле lines[].parent_code,
вычисляется, в БД не хранится).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 18:48:50 +04:00
PotapovaA
f9f25e8245 правки по код ревью 2026-10-16 13:19:49 +03:00
Andrey Epifancev
1ebd5b9997 Шахматка: зелёный make check (#114)
- Убраны отладочные print() в режиме MANUAL.
- mypy: computed_field у is_debit/is_credit — с type: ignore[prop-decorator],
  как у остальных схем; флаг is_more_5_percent без сравнения Decimal с None
  (поведение прежнее).
- ruff format по трём файлам.
- test_openapi_routes: новые маршруты шахматки и правил элиминации
  в EXPECTED_ROUTES.

make check: ruff, ruff format, mypy — чисто; pytest 1303 passed, 44 skipped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 18:37:23 +04:00
PotapovaA
4390ce8f27 Merge branch 'main' into add-report-shahmatka 2026-10-15 13:18:22 +03:00
PotapovaA
db19b3a176 корректировка расчета шахматки, добавление эндпоинтов для получения статей, добавления эндпоинта для выгрузки xlsx 2026-10-15 13:17:54 +03:00
Andrey Epifancev
244d97b1df ГСК/ГСЗ: POST/PATCH принимают crm_id без сохранения (#111)
Формы 51-03/51-04/77-02 шлют «ID ГСК/ГСЗ в CRM», а extra=forbid отвечал 422.
crm_id принимается (trim, пустая строка → null), отбрасывается; в ответах
RelatedGroupOut/RelatedGroupDetail — всегда null, в аудит не попадает.
Колонка related_group.crm_id — отдельная задача #112 (DBML v5.19).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 12:40:59 +04:00
Andrey Epifancev
4973e47290 feat(directories): текущие членства ГСК/ГСЗ в списке ЮЛ (#109)
`memberships` перенесено из `LegalEntityDetail` в `LegalEntityOut`: поле приходит в списке
`GET /directories/legal-entities`, в ответах `move`/`withdraw` (после них — `[]`) и, как раньше,
в карточке. Членства набора ЮЛ читаются одним запросом
(`current_memberships_by_entities`), без N+1. Нужно карточке ГК для фильтра ЮЛ по ГСК/ГСЗ.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:06:02 +04:00
PotapovaA
d44a4ff007 Merge branch 'main' into add-report-shahmatka 2026-10-12 14:26:43 +03:00
PotapovaA
f0b5d19162 добавить расчёт шахматки и результирующую таблицу ВГО 2026-10-12 14:26:10 +03:00
Andrey Epifancev
956a730a3c api: рабочий стол консолидации для ЮЛ и consolidation_id в карточке файла (#96)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-10 01:10:21 +04:00
PotapovaA
bb6795c052 добавление ручки для получения правил шахматки 2026-10-09 16:16:24 +03:00
Andrey Epifancev
a93ab10523 feat(directories): алиасы и ГСК/ГСЗ в POST/PATCH ЮЛ (#84)
Формы «Добавить/Изменить юридическое лицо» (макет 51-02, 79) отправляют всё
одним запросом. POST/PATCH /directories/legal-entities принимают `aliases`,
`gsk_id`, `gsz_id` и применяют их в той же транзакции, что и поля ЮЛ.

- POST: алиасы и членства создаются вместе с ЮЛ; ошибка откатывает всё.
- PATCH: `aliases` — итоговый набор (сверка по normalized_alias; удаление —
  с правом DIRECTORY_DELETE, иначе 403), `gsk_id`/`gsz_id` — итоговые группы
  (`null` — выйти, другая — сменить); не переданное поле не меняется.
- Новая ошибка 422 WRONG_GROUP_KIND; дубль в списке — 409 ALIAS_EXISTS.
- Ответ POST и PATCH — LegalEntityDetail (надмножество LegalEntityOut).
- Логика ручек алиасов и состава ГСК/ГСЗ вынесена в функции модулей
  (add_alias/remove_alias, add_membership/close_membership) — форма и ручки
  ведут себя одинаково; PermissionDeniedError — единый 403 «недостаточно прав».
- Спека directories-api и README обновлены.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 13:48:14 +04:00
Andrey Epifancev
37e7f40890 feat(statements): PATCH создаёт строку и ячейку по line_code (#80)
CellPatch адресует ячейку ровно одним из line_id / line_code. Строка справочника
редакции, которой нет в форме, создаётся при первой непустой правке
(line_code_original_value = NULL — признак «не из источника»); отсутствующая
ячейка существующей строки тоже создаётся. Адресация (колонка, строка, код)
проверяется целиком до первой вставки, две ячейки новой строки делят одну
строку, повтор line_code после создания даёт 422 UNKNOWN_CELL. Аудит — одно
RECOGNIZED_DATA_EDIT на запрос, у созданной ячейки before = null.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-09 12:03:59 +04:00
Andrey Epifancev
3a543b5cd1 feat(statements): GET достраивает форму строками справочника (#80)
GET …/statement добавляет строки справочника редакции, которых нет в форме
(id = null, origin = template), и недостающие ячейки существующих строк
(id = null, все значения пусты). В БД ничего не пишется. Добавлено поле
LineOut.origin (source | template).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-09 12:00:26 +04:00
Andrey Epifancev
1b4a0859e4 feat(osv): несопоставленные — только строки без ИНН; ИНН сопоставленного ЮЛ в строке свода (#74)
Решения владельца 08.10.2026:
- отмена П-1: «Осталось N» (`unmapped_total`) и `?unmapped=true` — строки без ЮЛ после резолва и без ИНН из файла;
  строка с ИНН, которого нет в справочнике, сопоставления не требует (`domain.needs_mapping`);
- `ConsolidatedRowOut.counterparty_entity_inn` — ИНН сопоставленного ЮЛ справочника (П-8): фронту — «таблетка»
  с ИНН и пересопоставление у строк, сопоставленных без ИНН в файле.

Фикстуры РБ 2024 Q3: unmapped_total 1379 → 1260. Спека consolidated-osv и README обновлены.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 19:37:18 +04:00
Andrey Epifancev
64be2f2b5c feat: имя ГК (company_group_name) в ответах ЮЛ (#67)
В LegalEntityOut (а значит и LegalEntityDetail) добавлено company_group_name: текущее имя ГК company_group_id,
архивной тоже; у монозаёмщика null. Есть во всех ответах /directories/legal-entities: список, карточка, POST,
PATCH, move, withdraw; в снимках истории нет. Имена набора ЮЛ подгружаются одним батч-запросом
(repositories.directories.company_group_names_by_ids), число запросов списка от числа строк не зависит.
Спека directories-api и README дополнены.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-08 17:01:25 +04:00
Andrey Epifancev
a76fc3ed99 Merge main в feat/45-kpp (после #51 и docs#54)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EAKRDNzXtRrwmWz9z6PZu3
2026-10-07 19:10:20 +04:00
Andrey Epifancev
4cc926c033 КПП юридического лица: legal_entity.kpp, миграция 0005, API, импорт (DBML v5.17, #45)
- модели LegalEntity.kpp / LegalEntityHistory.kpp (последней колонкой), миграция 0005_dbml_v5_17 (upgrade/downgrade);
- domain/kpp.py: 9 символов (4 цифры, 2 цифры/латинские A-Z, 3 цифры), у ИП запрещён, иначе 422 INVALID_KPP;
- схемы Create/Update/Out/Detail/HistoryOut, PATCH пишет снимок history и аудит before/after с kpp, поиск q по КПП;
- импорт справочника: необязательная колонка КПП (синоним kpp), код INVALID_KPP, update обновляет непустой КПП;
- dev-seed: необязательный ключ kpp (значений в данных нет, не выдумываем);
- сверка с DBML, Makefile, README, CLAUDE.md, openspec/config.yaml — на v5.17.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EAKRDNzXtRrwmWz9z6PZu3
2026-10-07 18:51:50 +04:00
Andrey Epifancev
9cceb84827 feat: «Основной РФ» объектом в ответах ЮЛ и ГК (#47)
В LegalEntityOut/Detail/HistoryOut добавлено regional_office, в CompanyGroupOut/HistoryOut — base_regional_office
(RegionalOfficeBrief {id, name, is_head_office} или null); *_id оставлены. Филиалы подгружаются одним батч-запросом
на набор id (списки, карточки, history без FK); неактивный филиал отдаётся. Тесты: совпадение с *_id, null,
число запросов списка не растёт, history — один запрос на филиалы.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EAKRDNzXtRrwmWz9z6PZu3
2026-10-07 18:46:59 +04:00
Andrey Epifancev
9d187f2f83 fix(consolidations): MemberOut.entity_type nullable, как legal_entity.entity_type в DBML (I-1, #42)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 18:17:13 +04:00
Andrey Epifancev
d782637bc1 feat(consolidations): тип ЮЛ и счета ОСВ к периоду версии у участника периметра (#42)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 17:51:45 +04:00
Andrey Epifancev
678cc4566f feat(consolidations): ячейки чек-листа с document_type и account своего правила (#42)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 17:49:28 +04:00
Andrey Epifancev
b447a09982 feat(consolidations): создание с выбранным периметром legal_entity_ids, общая проверка периметра (#42)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 17:48:03 +04:00
Andrey Epifancev
51d322c8a7 fix(statements): откат M-1 — пустая сумма цели считается нулём, как в tabular-parser; описание поля inn (N-1)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 14:48:03 +04:00
Andrey Epifancev
ffc01836b7 fix(statements): единое определение has_lines, inn=null — 422, заголовки отдачи первички, тест подтверждения при нарушении 1370/2400 (M-2, M-5, M-7, M-8)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 14:31:14 +04:00
Andrey Epifancev
fb457fc06b fix(statements): валидация units при правке шапки и comment в аудите сброса акцепта (T6.2, T6.4)
- 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
2026-10-07 13:37:01 +04:00
Andrey Epifancev
6078532982 feat(directories): documents_count в списке и карточке ЮЛ (D-13)
Добавлено поле 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
2026-10-07 13:16:47 +04:00
Andrey Epifancev
58f039a91d feat(users): GET /users/brief — имена пользователей для всех аутентифицированных (D-12)
Добавлен эндпоинт 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
2026-10-07 13:08:29 +04:00
Andrey Epifancev
c9bd620a18 feat(documents): комментарии к документу и comments_count в карточке (D-11)
POST/GET /documents/{id}/comments, счётчик комментариев в карточке файла
и документа одним запросом на пачку документов.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 12:46:43 +04:00