AURORA-1879: одноимённые этапы «Поддержки» и «Развития» — раздельно в списке, правке, удалении и аудите #315

Open
aep wants to merge 5 commits from feat/312-aurora-1879-form-phase-direction into test
Owner

Closes #312. Jira: AURORA-1879 (замечание Фомичева №2, эпик AURORA-1876). Блокирует AURORA-1880 (фронт пойдёт отдельным PR поверх этой ветки).

Что сделано

  • ORM FormPhase: direction входит в первичный ключ (nullable). Identity map больше не склеивает одноимённые этапы «Поддержки» и «Развития» — GET /stages/form/{id} отдаёт обе записи со своими данными.
  • form_phase_repository.get_list: детерминированный порядок (sheet, direction NULLS FIRST, opens_at, phase_code). Фильтр по роли из AURORA-1888 сохранён.
  • Миграция 0060 (0060_form_phase_direction_fix; изначально 0059, перенумерована 11.11 — 0059 занята AURORA-1984, PR #306):
    • v3.del_form_phase удаляет только этап с указанным phase_code (раньше — все этапы листа/направления, при нескольких падал «query returned more than one row»);
    • add/upd/del_form_phase: phase_id в аудите = form:sheet:direction:phase_code (для NULL direction формат прежний), в upd_form_phase в payload обоих событий добавлено direction;
    • чистка дублей этапов с direction IS NULL (NOTICE с количеством) и частичный уникальный индекс form_phase_nodir_uidx.
    • Функции пересозданы из 0014 (после неё не переопределялись); сверено построчно — отличия только перечисленные. Сигнатуры не менялись.
  • FormPhaseService.create: дубль этапа на листе+направлении → 400 phase_code: «Этап с таким названием уже есть на этом листе и направлении». Одноимённые этапы в Support/Development по-прежнему разрешены.

Проверка

  • Весь набор pytest --init_db на чистой БД (миграции 0001→0059 через alembic): 873 passed, 10 failed, 4 skipped. После перенумерации и merge test f047031 (0001→0060): 904 passed, 10 failed, 4 skipped — те же 10 известных падений test.
  • Эталон — тот же прогон на test 5a91f35: 863 passed, те же 10 failed (diff пуст). Падающие (существуют в test, к задаче не относятся): test_db_functions_integration::test_backend_calls_form3_add_project_and_add_line_functions, 3× test_form2_ahr_satellite_spod_integration, 3× test_upd_form_cells_subtree, test_upd_form_cells_subtree_0037_guard, 2× test_export_service_query_params (известны, follow-up #311).
  • Новые тесты: unit test_form_phase_model.py, test_form_phase_service.py (дубль); интеграционные в test_form_phases_api_smoke.py — оба направления видны, PATCH/DELETE трогают одно направление, DELETE несуществующего → 404, дубль на OPER → 4xx, direction в аудите, частичный индекс блокирует дубль (на БД без 0059 тест красный).

Rulings I made

  • R-1 — Тела функций взяты из 0014: после неё add/upd/del_form_phase ни одна миграция до 0058 не переопределяла (проверено grep).
  • R-2 — Чистка дублей одним DELETE … WHERE EXISTS (… o.ctid > fp.ctid) в DO-блоке с GET DIAGNOSTICS для NOTICE — результат тот же, что «оставить max(ctid)».
  • R-3 — Индекс CREATE UNIQUE INDEX IF NOT EXISTS — миграция идемпотентна.
  • R-4 — В FormPhaseService.create проверка существования стоит после валидации дат: 422 за aware-даты без offset остаётся приоритетным.
  • R-5 — Фильтр по роли из AURORA-1888 в get_list не тронут, ORDER BY добавлен после фильтров.
  • R-6 — Тест аудита проверяет и direction, и phase_id = '1:AHR:Development:T_same'.
  • R-7 — Добавлен тест на nullable=True у direction.
  • R-8 — (исполнитель) код сервиса написан до красной фазы: в его окружении pytest не запускался. Тесты затем прогнаны контроллером, зелёные.
  • R-9 — (контроллер) DO-блок переведён на $do$: sql_migration._split_statements распознаёт только именованные dollar-теги, с $$ миграция не накатывалась через alembic.
  • R-10 — (контроллер) Интеграционные тесты удаления и дубля поправлены: ?sheet= без direction фильтрует direction IS NULL (действующая семантика API), а после ошибки запроса клиентская сессия откатывается целиком; проверка индекса вынесена в отдельный тест прямой вставкой.

Findings

Маркер Суть Статус
R-1…R-8 рулинги исполнителя (см. выше) fixed 9e15fad / 92f6049
R-9 $$ в DO-блоке ломал разбиение SQL в alembic fixed 8798f09
R-10 ошибочные ожидания в двух интеграционных тестах fixed 8798f09
O-1 у исполнителя-сабагента не было venv: готовое окружение — ~/projects/fomichev_claude/.venvs/dfip-api rejected: окружение, не код
O-2 alembic_version в базах perf*/verify_1936 лежит в схеме v3 rejected: справочное
O-3 существующий test_form_phases_delete теперь удаляет только Support — тест валиден rejected: поведение верное
O-4 в payload аудита add_form_phase нет task_name (в upd/del есть) — старое различие deferred
O-5 8 интеграционных тестов падают на чистой test (form3 add_project, satellite spod ×3, upd_form_cells_subtree ×3, 0037_guard) deferred
O-6 ?sheet=X без direction в GET /stages/form/{id} молча означает direction IS NULL — для AHR/CAP возвращает пусто deferred

🤖 Generated with Claude Code

Closes #312. Jira: AURORA-1879 (замечание Фомичева №2, эпик AURORA-1876). Блокирует AURORA-1880 (фронт пойдёт отдельным PR поверх этой ветки). ## Что сделано - **ORM** `FormPhase`: `direction` входит в первичный ключ (nullable). Identity map больше не склеивает одноимённые этапы «Поддержки» и «Развития» — GET `/stages/form/{id}` отдаёт обе записи со своими данными. - **`form_phase_repository.get_list`**: детерминированный порядок (sheet, direction NULLS FIRST, opens_at, phase_code). Фильтр по роли из AURORA-1888 сохранён. - **Миграция 0060** (`0060_form_phase_direction_fix`; изначально 0059, перенумерована 11.11 — 0059 занята AURORA-1984, PR #306): - `v3.del_form_phase` удаляет только этап с указанным `phase_code` (раньше — все этапы листа/направления, при нескольких падал «query returned more than one row»); - `add/upd/del_form_phase`: `phase_id` в аудите = `form:sheet:direction:phase_code` (для NULL direction формат прежний), в `upd_form_phase` в payload обоих событий добавлено `direction`; - чистка дублей этапов с `direction IS NULL` (NOTICE с количеством) и частичный уникальный индекс `form_phase_nodir_uidx`. - Функции пересозданы из 0014 (после неё не переопределялись); сверено построчно — отличия только перечисленные. Сигнатуры не менялись. - **`FormPhaseService.create`**: дубль этапа на листе+направлении → 400 `phase_code: «Этап с таким названием уже есть на этом листе и направлении»`. Одноимённые этапы в Support/Development по-прежнему разрешены. ## Проверка - Весь набор `pytest --init_db` на чистой БД (миграции 0001→0059 через alembic): **873 passed, 10 failed, 4 skipped**. После перенумерации и merge test f047031 (0001→0060): **904 passed, 10 failed, 4 skipped** — те же 10 известных падений test. - Эталон — тот же прогон на `test` 5a91f35: 863 passed, **те же 10 failed** (diff пуст). Падающие (существуют в test, к задаче не относятся): `test_db_functions_integration::test_backend_calls_form3_add_project_and_add_line_functions`, 3× `test_form2_ahr_satellite_spod_integration`, 3× `test_upd_form_cells_subtree`, `test_upd_form_cells_subtree_0037_guard`, 2× `test_export_service_query_params` (известны, follow-up #311). - Новые тесты: unit `test_form_phase_model.py`, `test_form_phase_service.py` (дубль); интеграционные в `test_form_phases_api_smoke.py` — оба направления видны, PATCH/DELETE трогают одно направление, DELETE несуществующего → 404, дубль на OPER → 4xx, `direction` в аудите, частичный индекс блокирует дубль (на БД без 0059 тест красный). ## Rulings I made - R-1 — Тела функций взяты из 0014: после неё `add/upd/del_form_phase` ни одна миграция до 0058 не переопределяла (проверено grep). - R-2 — Чистка дублей одним `DELETE … WHERE EXISTS (… o.ctid > fp.ctid)` в DO-блоке с `GET DIAGNOSTICS` для NOTICE — результат тот же, что «оставить max(ctid)». - R-3 — Индекс `CREATE UNIQUE INDEX IF NOT EXISTS` — миграция идемпотентна. - R-4 — В `FormPhaseService.create` проверка существования стоит после валидации дат: 422 за aware-даты без offset остаётся приоритетным. - R-5 — Фильтр по роли из AURORA-1888 в `get_list` не тронут, ORDER BY добавлен после фильтров. - R-6 — Тест аудита проверяет и `direction`, и `phase_id = '1:AHR:Development:T_same'`. - R-7 — Добавлен тест на `nullable=True` у `direction`. - R-8 — (исполнитель) код сервиса написан до красной фазы: в его окружении pytest не запускался. Тесты затем прогнаны контроллером, зелёные. - R-9 — (контроллер) DO-блок переведён на `$do$`: `sql_migration._split_statements` распознаёт только именованные dollar-теги, с `$$` миграция не накатывалась через alembic. - R-10 — (контроллер) Интеграционные тесты удаления и дубля поправлены: `?sheet=` без `direction` фильтрует `direction IS NULL` (действующая семантика API), а после ошибки запроса клиентская сессия откатывается целиком; проверка индекса вынесена в отдельный тест прямой вставкой. ## Findings | Маркер | Суть | Статус | |---|---|---| | R-1…R-8 | рулинги исполнителя (см. выше) | fixed 9e15fad / 92f6049 | | R-9 | `$$` в DO-блоке ломал разбиение SQL в alembic | fixed 8798f09 | | R-10 | ошибочные ожидания в двух интеграционных тестах | fixed 8798f09 | | O-1 | у исполнителя-сабагента не было venv: готовое окружение — `~/projects/fomichev_claude/.venvs/dfip-api` | rejected: окружение, не код | | O-2 | `alembic_version` в базах perf*/verify_1936 лежит в схеме `v3` | rejected: справочное | | O-3 | существующий `test_form_phases_delete` теперь удаляет только Support — тест валиден | rejected: поведение верное | | O-4 | в payload аудита `add_form_phase` нет `task_name` (в upd/del есть) — старое различие | deferred | | O-5 | 8 интеграционных тестов падают на чистой `test` (form3 add_project, satellite spod ×3, upd_form_cells_subtree ×3, 0037_guard) | deferred | | O-6 | `?sheet=X` без `direction` в GET `/stages/form/{id}` молча означает `direction IS NULL` — для AHR/CAP возвращает пусто | deferred | 🤖 Generated with [Claude Code](https://claude.com/claude-code)
aep added 3 commits 2026-11-11 15:59:42 +03:00
- ORM: direction входит в PK (nullable), одноимённые этапы Support/Development больше не склеиваются
- get_list: ORDER BY sheet, direction (NULL первыми), opens_at, phase_code
- 0059: del_form_phase фильтрует по phase_code, direction в v_phase_id (add/upd/del) и в payload upd,
  чистка дублей без направления и частичный уникальный индекс form_phase_nodir_uidx

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Интеграционные тесты одноимённых этапов, PATCH/DELETE одного этапа, дубль без направления, direction в аудите.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- sql_migration._split_statements узнаёт только $tag$, пустой $$ ломал разбиение:
  DO $$ … $$ → DO $do$ … $do$.
- test_form_phases_delete_removes_only_named_phase: ?sheet= без direction
  фильтрует direction IS NULL — читаем весь список и отбираем AHR в тесте.
- test_form_phases_duplicate_without_direction_rejected: после ошибки
  сессия клиента откатывается, число строк не проверяем; защиту БД
  проверяет новый test_form_phase_nodir_unique_index_blocks_duplicate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
aep requested review from tsygankoviva 2026-11-11 16:00:44 +03:00
aep added 2 commits 2026-11-11 18:43:56 +03:00
Author
Owner

Миграция перенумерована 0059 → 0060 (7a5c89e): номер 0059 занял AURORA-1984 (PR #306, влит в test). Ветка обновлена merge'ем origin/test (f047031). Проверка: backend 904 passed / 10 известных падений test; alembic 0001→0060 на чистой БД.

Миграция перенумерована 0059 → 0060 (7a5c89e): номер 0059 занял AURORA-1984 (PR #306, влит в test). Ветка обновлена merge'ем origin/test (f047031). Проверка: backend 904 passed / 10 известных падений test; alembic 0001→0060 на чистой БД.
This pull request can be merged automatically.
You are not authorized to merge this pull request.

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin feat/312-aurora-1879-form-phase-direction:feat/312-aurora-1879-form-phase-direction
git checkout feat/312-aurora-1879-form-phase-direction
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RSHB/DFiP_Budget_planing#315
No description provided.