35 Commits

Author SHA1 Message Date
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
622a60e4e0 AURORA-1442: Правки по раунду 1 2026-10-23 13:39:30 +03:00
PotapovaA
b7d97943b2 Merge main into add-preset-shahmatka: preserve preset and result table exports 2026-10-22 14:39:25 +03:00
4f44d34d59 AURORA-1442: ручки возобновления обработки файла или версии консолидации, ручка обновления данных по осв 2026-10-22 14:25:19 +03:00
Andrey Epifancev
63d6c1a017 Замечания ревью #159: проверки, общие реквизиты книги, change OpenSpec
- маршрут result-table/export в EXPECTED_ROUTES, ruff format
- общие VersionExportInfo, _write_details, _group_name, _xlsx_response вместо копий шапки и ответа
- сумма компании в книге без молчаливого 0 (KeyError при пропуске в расчёте)
- своё описание 422 у маршрутов результирующей таблицы в OpenAPI
- тесты: 422 RESULT_ACCOUNTS_MISSING для экспорта без события EXPORT, схема OpenAPI экспорта
- требование перенесено из openspec/specs в change intercompany-result-table-export (+ audit-trail), README

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 11:42:13 +04:00
PotapovaA
3de0c90198 refactor(matrix): удалить unpaired-osv из ветки пресетов 2026-10-21 19:32:23 +03:00
PotapovaA
53c2d5c73d пресеты для шахматки 2026-10-20 14:45:27 +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
db19b3a176 корректировка расчета шахматки, добавление эндпоинтов для получения статей, добавления эндпоинта для выгрузки xlsx 2026-10-15 13:17:54 +03:00
Andrey Epifancev
1e888d4670 api: загрузка файлов в консолидацию, переход версий «в работе»; query версии у загрузки ЮЛ удалён (#96)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-10 01:31:35 +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
d807380068 refactor(directories,consolidations): documents_count одним хелпером и описание CSV чек-листа (T11.1, T11.2, T12.2, T12.6)
- ЮЛ: 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
2026-10-07 13:39:37 +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
310e63224d feat(files): GET /files/{id}/content — исходный файл потоком из S3, RFC 5987 (D-10)
Поток из 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
2026-10-07 12:55:00 +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
Andrey Epifancev
38a6a1b0c2 feat(statements): правка формы (значения, ИНН, дата, единицы) и акцепт проверок — аудит, пересчёт статуса (D-7, D-8)
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
2026-10-07 12:09:43 +04:00
Andrey Epifancev
2b5e70f7c3 feat(statements): GET /documents/{id}/statement — шапка, колонки, строки, проверки §7, редакция (D-5, D-14)
Схемы формы, 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
2026-10-07 11:54:38 +04:00
Andrey Epifancev
227ab67809 fix(auth): схема bearerAuth в OpenAPI — кнопка Authorize в Swagger UI
Токен читался зависимостью как Header(), поэтому в openapi.json не было securitySchemes и Swagger UI не показывал
Authorize (вопрос фронта 06.10.2026). Теперь HTTPBearer(auto_error=False) со scheme_name bearerAuth; значение
заголовка собирается обратно и идёт в parse_bearer — тексты 401/403 и правила («три части» → 401) не изменились.
Тесты: securitySchemes.bearerAuth есть, все маршруты кроме /dev/token и /healthcheck помечены security.
make check: 1126 passed; интеграция auth/me/dev-token: 53 passed.

Closes #29

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
2026-10-06 15:43:33 +04:00
Andrey Epifancev
8e82c2c19f feat(consolidation): экспорт сводной ОСВ в xlsx с листом пробелов
GET /consolidations/versions/{id}/osv/export: лист «Сводная ОСВ» (15 колонок D-14, суммы — числовые
ячейки #,##0.00 из Decimal) и лист «Пробелы» по своду целиком; фильтры как у GET …/osv без пагинации;
Content-Disposition с filename* (чистый ASCII); audit_event(EXPORT) с filters и rows.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 07:04:53 +04:00
Andrey Epifancev
e246531c70 feat(consolidation): кандидаты и ручное сопоставление контрагента с алиасом и автоприменением
GET …/counterparty-candidates (scope group|all, QUERY_TOO_SHORT, recommended по
нормализованному имени строки) и POST …/osv/entries/{entry_id}/counterparty:
проверка строки свода версии, ЮЛ без policy, ALIAS_TAKEN с owners / перенос алиаса,
MANUAL строке и PROPAGATED строкам с тем же strip()-именем без противоречия ИНН,
один OSV_COUNTERPARTY_MAP и LEGAL_ENTITY_CHANGE на каждый алиас. Строки общие для
версий блокируются FOR NO KEY UPDATE ORDER BY id; advisory-блокировка алиаса общая
со справочником. Решения #62–#70 в design.md; 4.3 отмечена.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 06:46:06 +04:00
Andrey Epifancev
a2c2135811 feat(consolidation): API сводной ОСВ — страница с фильтрами и сводка с пробелами
GET /consolidations/versions/{id}/osv и /osv/summary (CONSOLIDATION_VIEW): ConsolidatedOsvService собирает свод
чистой функцией от версии (периметр, привязанные ОСВ, load_osv_rows, один CounterpartyIndex, один SELECT legal_entity)
и режет страницу в памяти; чтение любой версии, включая CANCELLED, без блокировок и аудита.
OsvFilters отделён от OsvPageParams (T2.7), суммы всегда с двумя знаками; account_sort_key устойчив к «6²»/«①» (T9.a).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 06:23:24 +04:00
Andrey Epifancev
f46cf2fec5 feat(consolidation): чек-лист комплекта документов по правилам подхода
Домен `checklist` (Rule, CellStatus, Cell, order_rules, build) — чистые функции без ORM; ячейка (ЮЛ, правило):
CONFIRMED по привязанным документам (ОСВ: нарастающий за квартал версии либо квартальные Q1..Qn, пробел →
missing_quarters; ББ/ОФР: документ за квартал версии), NEEDS_CHECK/RECOGNIZED по неподтверждённому подходящему,
PROCESSING, ERROR, иначе MISSING; правило ОСВ закрывает только точный код счёта. Репозиторий: rules_for,
unconfirmed_documents. Сервис: checklist(), ячейки в карточке версии. Маршрут GET /consolidations/versions/{id}/checklist.
Схема использует CellStatus домена (T2.8). Решения №44-49 в design.md.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 05:50:48 +04:00
Andrey Epifancev
90b7bd5c16 feat(consolidation): редактирование периметра версии с подхватом и отвязкой документов
PUT /consolidations/versions/{id}/members: замена состава ЮЛ версии, проверка принадлежности ГК
(ENTITY_NOT_IN_GROUP), минимум два ЮЛ, diff с отвязкой документов ушедших (unbind_entity) и подхватом
добавленных (bind_confirmed, заглушка до Task 6), аудит MEMBERSHIP_CHANGE. Общий пролог
«lock → видимость → редактируемость» вынесен в services/consolidation_guard.py (T4.2). Решения #25–#29.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 04:34:52 +04:00
Andrey Epifancev
b2fa59061d feat(consolidation): политика редактируемости, блокировка с перехватом, ручные статусы Среза 4
- domain/policy: IN_WORK_STATUSES, ensure_version_editable (VERSION_NOT_EDITABLE / LOCK_REQUIRED / LOCKED_BY_OTHER)
- services/consolidation_locks: take/release, перехват с причиной, release_active_lock; FOR UPDATE версии первой (D-9),
  вставка под SAVEPOINT, нарушение uq_consolidation_lock_version_active -> 409 LOCKED_BY_OTHER с владельцем
- services/consolidation_status: MANUAL_TRANSITIONS, transition (единственное место смены статуса: история + аудит,
  CANCELLED снимает блокировку), ConsolidationStatusService.change
- маршруты POST/DELETE …/lock, POST …/status; реестр constraint_conflicts
- тесты: таблица ensure_version_editable, сценарии спеки, гонка двух POST …/lock, перехват во время правки и
  аннулирования (две сессии, Review Focus 4)
- минорные T3.1 (FINISHED_STATUSES из policy в фабриках), T3.2 (удалена CONSOLIDATION_NOT_FOUND)
- план: шаги Task 4; tasks.md 2.2, 2.3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 04:16:23 +04:00
Andrey Epifancev
9411d9d2ae feat(consolidation): создание консолидации, список по ГК, карточка версии
POST /consolidations (версия №1 INITIATED, периметр из текущих членов ГСК, история,
CONSOLIDATION_CREATE с consolidation_version_id), GET /company-groups/{id}/consolidations
и GET /consolidations/versions/{id}. reporting_date по OQ-40; подхват документов — заглушка
bind_confirmed до Task 6, чек-лист в карточке пуст до Task 8. UNIQUE периода -> 409
CONSOLIDATION_EXISTS; RequestAudit.record принимает consolidation_version_id.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 03:57:52 +04:00
Andrey Epifancev
15021735e9 feat(files): удаление файла с ограничениями, expires_at у объекта S3, FILE_DELETE
DELETE /files/{file_id} (DOCUMENT_EDIT, policy): блокировки «задание → файл → документы», проверки после
блокировок (409 FILE_HAS_CONFIRMED_DOCUMENTS, FILE_SUPERSEDES_OTHERS), удаление osv_entry/document/recognition_job/
stored_file (разрыв FK-цикла), expires_at у объектов S3 файла и результатов, аудит FILE_DELETE с before_value.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-05 23:43:54 +04:00
Andrey Epifancev
20983fb3d8 feat(files): API файлов, документов и строк ОСВ; подтверждение документа
GET /legal-entities/{id}/files (include_superseded, по убыванию uploaded_at),
GET /files/{id}, GET /documents/{id} (group_schema листа, warnings — R-6),
GET /documents/{id}/entries (limit ≤ 2000, по умолчанию 500),
POST /documents/{id}/confirm (DOCUMENT_EDIT; блокировка файла, затем документа;
409 FILE_SUPERSEDED / ALREADY_CONFIRMED / DOCUMENT_NOT_CONFIRMABLE; аудит
DOCUMENT_CONFIRM). Видимость — через ЮЛ-владельца, чужое — 404 как несуществующее.
Список файлов без N+1: файлы, текущие задания и документы — пакетными запросами.
JobBrief.mapping_error — причина ERROR файла по маппингу.

Тесты: сценарии documents-api, гонка подтверждения с автозаменой (Review Focus 5),
make_entries_from_result (R-2), реальная проверка уникальности документа (T2.2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-05 23:34:35 +04:00
Andrey Epifancev
be0d1b410c feat(files): загрузка файлов организации — S3, stored_file, recognition_job в outbox, дедуп, аудит
POST /api/v1/legal-entities/{id}/files (DOCUMENT_EDIT, видимость ЮЛ → 404): тип по расширению
со сверкой сигнатуры (domain/file_types.py), лимит UPLOAD_MAX_BYTES → FILE_TOO_LARGE, дедуп по
sha256 в пределах ЮЛ (кроме SUPERSEDED) с рекомендательной блокировкой (ЮЛ, sha256) до конца
транзакции запроса, s3_object → put_object → stored_file(UPLOADED) → recognition_job(queued) →
current_recognition_job_id, аудит FILE_UPLOAD. Каждый файл — SAVEPOINT: частичный успех
(201/422), отказ S3 на файле — STORAGE_UNAVAILABLE только для него; без S3 — 503.
safe_filename/CONTENT_TYPES/read_limited вынесены в common/filenames.py (импорт справочников
их переиспользует). Схемы UploadResult/FileOut/JobBrief/DocumentBrief, PrimaryFilesService и
зависимость для Task 9–10. tasks.md 2.1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-05 21:50:29 +04:00
Andrey Epifancev
070a398c65 fix(api): гонка уникальных ограничений отдаёт тот же 409 с кодом, что и pre-check (T6.5, T7.1, T8.2, T12.7, T15.3, T15.4)
Реестр имя ограничения PostgreSQL -> (код, постоянный текст) в common/constraint_conflicts.py;
integrity_error_handler для unique_violation (23505) по известному ограничению отдаёт
{"detail": {"code", "message"}}, иначе прежняя общая строка. Имя берётся из полей драйвера
(asyncpg __cause__.constraint_name, psycopg diag), не из текста ошибки; resolved_code пишется в лог.
Тесты: unit обработчика/реестра (ключи есть в Base.metadata), интеграционные гонки (ГК/ЮЛ crm_id,
ИНН, алиас, email, членство, откат и следующий запрос, FK остаётся общим 409).
T15.3: README шаг 4 импорта; T15.4: test_components_have_no_duplicated_models.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-05 02:02:14 +04:00
Andrey Epifancev
7b75981d0f docs: README/CLAUDE.md Среза 1, тест маршрутов OpenAPI, tasks.md
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-05 01:45:13 +04:00