AURORA-1846: FORM_4 суммы листа без рекурсивного jsonb_sum #232

Open
aep wants to merge 3 commits from feat/216-aurora-1846-form4-sheet-sum into test
Owner

Closes #216

Jira: AURORA-1846

Что сделано

  • Миграция 0050 (0050_form4_sheet_sum): CREATE OR REPLACE v3.v_form4_sheet_jsonb — полная копия тела из 0012_del_project.sql:1091-1617, CTE prj_agg/prog_agg/root_agg (рекурсивный агрегат v3.jsonb_sum) заменены на разворот листьев jsonb + GROUPING SETS по трём уровням (раздел/программа/проект) и обратную сборку. Без any_value (PG16), у подзапросов есть алиасы.
  • Поведение jsonb_numeric_sum повторено точно: числа складываются; нечисловые игнорируются, если по пути есть число; текст/null сохраняются, если путь встретился в группе один раз; при двух и более вхождениях без чисел — JSON null; отключённая секция (p_sections) — NULL; формат чисел to_jsonb(SUM(numeric)).
  • v3.jsonb_sum / v3.jsonb_numeric_sum не удалены (эталон в тесте).
  • perf/analysis/explain_manual.py: запись form4_cap_sheet читает тело из 0050.
  • Фикстура db_session вынесена из client в tests/integration/conftest.py; client от неё зависит, всё откатывается.

Тесты

  • api/tests/integration/test_form4_sheet_sums.py (9 тестов): эталон 1 — v3.jsonb_sum по INPUT-строкам в той же сессии (через API — по значению, прямым вызовом функции — точно, с масштабом); эталон 2 — сумма числовых листьев на Python. Кейсы: программа с 2 проектами по 2 строки; строки прямо на программе; проект/программа из одной строки с текстом и проект с двумя строками (null); пустые программа и проект; дробные разного масштаба, отрицательные, null; booking второго уровня; ?sections=plan,booking; один PATCH /cell.
  • Тесты были зелёными на старом коде до миграции, затем на новом.
  • Полный прогон (PG16, perf-pg): 209 passed, 4 skipped, 3 failed — те же 3 исходных падения (test_backend_calls_form3_add_project_and_add_line_functions, два в test_export_service_query_params).
  • Дополнительно: на 300 строках (5 программ x 5 проектов x 12 строк) вывод старой и новой функции совпал побайтно (md5 по всем строкам).

Проверить руками

  • alembic upgrade head проходит на PG15 (после починки 0012 в AURORA-1922) и PG16.
  • GET листа FORM_4/CAP и PATCH ячейки: суммы в ROOT/GROUP/ITEM прежние.

Замер до/после

Будет добавлен после ночного прогона на стенде perf/. Сценарий: после gen.grow — cd perf && .venv/bin/python analysis/api_split.py --scale 1 --out report-data/<tag>/analysis (FORM_4/CAP, цель <= 300 мс) и analysis/explain_manual.py (form_view_form4_cap, form4_cap_sheet). Локально на 300 строках блок агрегатов в EXPLAIN: ~0,5 с -> ~0,1 с; оставшееся время тела — v_form4_sheet_lines_jsonb.

Rulings I made

  • R-1: сборка имён групп (MAX(sec_name|prog_name|prj_name)) вынесена в отдельные CTE *_names без jsonb; данные присоединяются LEFT JOIN. Обоснование: группа без листьев (все секции NULL) должна остаться строкой, как в старом коде. Цена ошибки: пропавшая строка ROOT/GROUP/ITEM в нетипичном случае.
  • R-2: соединение данных с группами по sec_id = (не IS NOT DISTINCT FROM). Раздел depth=0 есть у каждой статьи, NULL невозможен. Цена ошибки: при NULL sec_id потеря агрегатов.
  • R-3: рекурсивный поиск раздела (0012:1141-1150) не заменялся — в EXPLAIN вносит ~15 мс.
  • R-4: в API числа отдаются как float (масштаб теряется, 3.50 -> 3.5) — существующее поведение; точное сравнение масштаба сделано прямым вызовом функции в SQL.

🤖 Generated with Claude Code

Closes #216 Jira: AURORA-1846 ## Что сделано - Миграция 0050 (`0050_form4_sheet_sum`): `CREATE OR REPLACE v3.v_form4_sheet_jsonb` — полная копия тела из `0012_del_project.sql:1091-1617`, CTE `prj_agg`/`prog_agg`/`root_agg` (рекурсивный агрегат `v3.jsonb_sum`) заменены на разворот листьев jsonb + `GROUPING SETS` по трём уровням (раздел/программа/проект) и обратную сборку. Без `any_value` (PG16), у подзапросов есть алиасы. - Поведение `jsonb_numeric_sum` повторено точно: числа складываются; нечисловые игнорируются, если по пути есть число; текст/null сохраняются, если путь встретился в группе один раз; при двух и более вхождениях без чисел — JSON null; отключённая секция (`p_sections`) — NULL; формат чисел `to_jsonb(SUM(numeric))`. - `v3.jsonb_sum` / `v3.jsonb_numeric_sum` не удалены (эталон в тесте). - `perf/analysis/explain_manual.py`: запись `form4_cap_sheet` читает тело из 0050. - Фикстура `db_session` вынесена из `client` в `tests/integration/conftest.py`; `client` от неё зависит, всё откатывается. ## Тесты - `api/tests/integration/test_form4_sheet_sums.py` (9 тестов): эталон 1 — `v3.jsonb_sum` по INPUT-строкам в той же сессии (через API — по значению, прямым вызовом функции — точно, с масштабом); эталон 2 — сумма числовых листьев на Python. Кейсы: программа с 2 проектами по 2 строки; строки прямо на программе; проект/программа из одной строки с текстом и проект с двумя строками (null); пустые программа и проект; дробные разного масштаба, отрицательные, null; booking второго уровня; `?sections=plan,booking`; один PATCH /cell. - Тесты были зелёными на старом коде до миграции, затем на новом. - Полный прогон (PG16, perf-pg): 209 passed, 4 skipped, 3 failed — те же 3 исходных падения (`test_backend_calls_form3_add_project_and_add_line_functions`, два в `test_export_service_query_params`). - Дополнительно: на 300 строках (5 программ x 5 проектов x 12 строк) вывод старой и новой функции совпал побайтно (md5 по всем строкам). ## Проверить руками - `alembic upgrade head` проходит на PG15 (после починки 0012 в AURORA-1922) и PG16. - GET листа FORM_4/CAP и PATCH ячейки: суммы в ROOT/GROUP/ITEM прежние. ## Замер до/после Будет добавлен после ночного прогона на стенде perf/. Сценарий: после `gen.grow` — `cd perf && .venv/bin/python analysis/api_split.py --scale 1 --out report-data/<tag>/analysis` (FORM_4/CAP, цель <= 300 мс) и `analysis/explain_manual.py` (`form_view_form4_cap`, `form4_cap_sheet`). Локально на 300 строках блок агрегатов в EXPLAIN: ~0,5 с -> ~0,1 с; оставшееся время тела — `v_form4_sheet_lines_jsonb`. ## Rulings I made - R-1: сборка имён групп (`MAX(sec_name|prog_name|prj_name)`) вынесена в отдельные CTE `*_names` без jsonb; данные присоединяются LEFT JOIN. Обоснование: группа без листьев (все секции NULL) должна остаться строкой, как в старом коде. Цена ошибки: пропавшая строка ROOT/GROUP/ITEM в нетипичном случае. - R-2: соединение данных с группами по `sec_id =` (не `IS NOT DISTINCT FROM`). Раздел depth=0 есть у каждой статьи, NULL невозможен. Цена ошибки: при NULL sec_id потеря агрегатов. - R-3: рекурсивный поиск раздела (0012:1141-1150) не заменялся — в EXPLAIN вносит ~15 мс. - R-4: в API числа отдаются как float (масштаб теряется, `3.50` -> `3.5`) — существующее поведение; точное сравнение масштаба сделано прямым вызовом функции в SQL. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
aep added 3 commits 2026-10-28 17:34:25 +03:00
Author
Owner

Проверка на PostgreSQL 15.12 (контроллер, 28.10): ветка + коммиты AURORA-1922 (PR #221), пустая БД, --init_db — миграция 0050 проходит; полный pytest: 211 passed, 4 skipped, 3 failed (исходные падения test), новых нет.

Наблюдение: 9 новых тестов test_form4_sheet_sums.py идут ~13–24 с каждый, одинаково на PG15 (137 с) и PG16 (128 с) — регрессии версии нет, но весь набор вырос с ~17 с до ~2,5 мин. Это согласуется с O-1 из описания: v_form4_sheet_lines_jsonb дорогая даже на малых данных. Цель ≤ 300 мс для GET FORM_4/CAP проверим ночным EXPLAIN (form_view_form4_cap, form4_cap_sheet); если упрётся в v_form4_sheet_lines_jsonb — отдельная задача.

Проверка на PostgreSQL 15.12 (контроллер, 28.10): ветка + коммиты AURORA-1922 (PR #221), пустая БД, --init_db — миграция 0050 проходит; полный pytest: 211 passed, 4 skipped, 3 failed (исходные падения test), новых нет. Наблюдение: 9 новых тестов test_form4_sheet_sums.py идут ~13–24 с каждый, одинаково на PG15 (137 с) и PG16 (128 с) — регрессии версии нет, но весь набор вырос с ~17 с до ~2,5 мин. Это согласуется с O-1 из описания: v_form4_sheet_lines_jsonb дорогая даже на малых данных. Цель ≤ 300 мс для GET FORM_4/CAP проверим ночным EXPLAIN (form_view_form4_cap, form4_cap_sheet); если упрётся в v_form4_sheet_lines_jsonb — отдельная задача.
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/216-aurora-1846-form4-sheet-sum:feat/216-aurora-1846-form4-sheet-sum
git checkout feat/216-aurora-1846-form4-sheet-sum
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#232
No description provided.