93 Commits

Author SHA1 Message Date
Andrey Epifancev
9a151a19eb Merge remote-tracking branch 'origin/test' into feat/272-aurora-1887-ahr-hide-satellite-rows
# Conflicts:
#	web/src/components/RealtimeTable/RealtimeTable.jsx
#	web/src/components/RealtimeTable/utils/rowUtils.test.js
2026-11-02 21:50:17 +04:00
aep
296f868c4c Merge pull request 'AURORA-1907: признак источника строки АХР и раздельные суммы лимита/сверх лимита' (#271) from feat/270-aurora-1907-source-sheet into test 2026-11-02 20:32:54 +03:00
aep
69e333cf9f Merge pull request 'AURORA-1909: добавление записи на сателлите не захватывает ручную строку АХР' (#268) from feat/265-aurora-1909-ahr-manual-row-capture into test 2026-11-02 20:31:57 +03:00
aep
5317c04d3a Merge pull request 'AURORA-1906: колонка «СПОД» в листах аренды, коммуналки и охраны АХР' (#263) from feat/254-aurora-1906-spod-column into test 2026-11-02 20:30:23 +03:00
Andrey Epifancev
d5cbd151c5 fix(tests): согласовать AURORA-1844/1846/1850 с null-strip из AURORA-1848
AURORA-1848 (#239/#242) убрал null-поля из GET листа/сводов (jsonb_strip_nulls),
оставив PATCH как раньше (задокументированный breaking-change в PR, не баг).
После слияния веток в test это разошлось с тестами, написанными до #239/#242:

- sheet_contract.assert_patch_rows_match_sheet сравнивал PATCH и GET побайтно —
  добавлен strip_nulls() на стороне PATCH перед сравнением.
- test_form4_sheet_sums.py сравнивал ответ API с "сырым" v3.jsonb_sum (с null-полями)
  и ожидал явный null в некоторых полях ответа — эталон теперь тоже прогоняется через
  strip_nulls, а прямые ожидания null заменены на .get(...) is None / отсутствие ключа.

Без изменений контракта API: PATCH продолжает отдавать null явно, как в #242.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-11-01 12:39:14 +04:00
Andrey Epifancev
7514ac9803 fix(realtime): getDataRowId возвращает null вместо undefined, тесты M-8/M-9
После jsonb_strip_nulls у групп/позиций нет ключа line_id, getDataRowId
возвращал undefined, и row_id в сообщении удаления сериализовался в {}
(KeyError на сервере). Возвращаем null, как до фазы B.
Добавлены тесты updateCells (глубокое отсутствие блока, два обновления в батче)
и сверка jsonb_strip_nulls с _strip_nulls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 12:14:49 +04:00
Andrey Epifancev
a34ccdcb9c perf(api): лист и своды без null-полей в data (jsonb_strip_nulls)
row_json_select(strip_nulls) оборачивает data в jsonb_strip_nulls внутри json_build_object;
флаг пробрасывается через get_json/get_svod1_json/get_svod2_json, роутеры листа и сводов
включают его. Проекты, PATCH/POST/DELETE не затронуты.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 12:14:49 +04:00
aep
9c7a5a4575 Merge pull request 'AURORA-1846: FORM_4 суммы листа без рекурсивного jsonb_sum' (#232) from feat/216-aurora-1846-form4-sheet-sum into test 2026-11-01 11:04:08 +03:00
Andrey Epifancev
b267905d09 test(sheet): ничья ключа сортировки по-настоящему, F1-AHR-plan, комментарий 0051 (AURORA-1850)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 10:47:27 +04:00
Andrey Epifancev
e396b5a053 test(sheet): защита правки 0037 после 0051 (AURORA-1850)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 10:47:27 +04:00
Andrey Epifancev
1d18cf812f perf(sql): jit = off на построителях и пути правки (AURORA-1850)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 10:47:27 +04:00
Andrey Epifancev
17f3de602a test(sheet): усиление красных тестов эквивалентности по ревью Task 2 (AURORA-1850)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 10:47:27 +04:00
Andrey Epifancev
5b6c7e244e test(sheet): эквивалентность ответа на правку и полной сборки — красные тесты (AURORA-1850)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 10:47:27 +04:00
aep
f2c503c292 Merge pull request 'AURORA-1844: upd_form_cells возвращает строки целиком' (#231) from feat/215-aurora-1844-upd-form-cells-full-rows into test 2026-11-01 09:46:45 +03:00
aep
37bb563f5d Merge pull request 'AURORA-1831: гонка technical_number при параллельном POST /projects' (#229) from feat/207-aurora-1831-project-tn-race into test 2026-11-01 09:45:32 +03:00
aep
15c6402ad3 Merge pull request 'AURORA-1832: aware-даты этапов форм и проектов (часовой пояс)' (#228) from feat/208-aurora-1832-phase-dates-tz into test 2026-11-01 09:43:46 +03:00
aep
f1b4f6d306 Merge pull request 'AURORA-1848: тяжёлые GET — готовый JSON из Postgres без pydantic-модели на строку (A)' (#239) from feat/233-aurora-1848-sheet-json-no-pydantic into test 2026-11-01 09:42:06 +03:00
Andrey Epifancev
234103606b feat(form2): скрыть сателлитные строки на листе АХР и заблокировать добавление для аренды/коммуналки/охраны (AURORA-1887)
Упрощённый дизайн (согласован в чате 2026-10-31, расходится с архитектурным
предложением из тикета): без слияния budget_line, без миграции данных, без
изменения протокола WS, без изменения v_svod2_rf.

- v3.v_form2_sheet_jsonb (0054): при p_sheet='AHR' не возвращает INPUT-строки
  с budget_line.source_sheet IN ('AHR_LIMIT','AHR_RENT','AHR_UTILITY',
  'AHR_SECURITY'); ручные строки (включая «сверх лимита», AURORA-1907)
  остаются видимыми. Листы-источники не затронуты. Суммы на заголовках статей
  считаются независимо от видимости строк — не меняются.
- header.can_add_row (новое поле, только p_sheet='AHR'): false для статей
  аренды/коммуналки/охраны (v3._form2_satellite_expense_item, новый helper),
  true для лимитных и обычных статей. Фронт (RealtimeTable.jsx handleAddRow,
  utils/rowUtils.js canAddRowForHeader) блокирует «Добавить строку» по этому
  флагу — тот же паттерн, что у FormsNoCanAddRow/isVspAdditionRow (без
  серверной валидации в add_budget_line).
- Обновлены тесты, которые проверяли суммы/источник через теперь скрытые
  INPUT-строки на AHR (test_form2_ahr_limit.py,
  test_form2_ahr_satellite_spod_integration.py,
  test_form2_ahr_satellite_integration.py) — сравнение переведено на
  заголовок статьи (до/после патча) и на лист-источник.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-31 21:59:04 +04:00
Andrey Epifancev
fe9dfbc7e1 feat(form2): признак источника строки АХР и раздельные суммы лимита/сверх лимита (AURORA-1907)
Миграция 0053: v3.budget_line.source_sheet (NULL | AHR_LIMIT | AHR_RENT |
AHR_UTILITY | AHR_SECURITY) — явный признак источника строки вместо
сопоставления по совпадению имени с LEAF limit_template. Backfill
существующих строк: сателлиты — из v3.form2_ahr_satellite_line_source,
лимитные — минимальный id в группе (форма, статья, имя LEAF), остальные
совпавшие по имени становятся ручными (RAISE NOTICE со списком).

_ensure_form2_ahr_limit_sync_line теперь ищет строку по source_sheet='AHR_LIMIT'
(а не по имени) — тот же класс бага, что чинил #265/AURORA-1909 для сателлитов
аренды/коммуналки/охраны, теперь для лимитов. is_form2_ahr_satellite_line,
del_budget_line (guard на удаление) и get_form2_ahr_satellite_delta переведены
на признак источника. Новая v3._form2_expense_item_has_limit выносит правило
«у статьи есть лимит» в отдельную функцию.

v_form2_sheet_jsonb: header.source ('AHR_LIMIT'/'AHR_RENT'/'AHR_UTILITY'/
'AHR_SECURITY'/'MANUAL'), header.is_over_limit для ручных строк на статьях
с лимитом; plan.limit_year/over_limit_year (только на листе AHR, свёрнуты по
дереву статей через mv_expense_item_tree.desc_ids) — plan.year не меняется,
остаётся суммой всех строк.

Фронт: AHR.js — колонки «В пределах лимита, год»/«Сверх лимита, год» после
«План, год» (нередактируемые — нет случая в v3._apply_form_cell); Cell.jsx —
чип «сверх лимита» в ячейке наименования по header.is_over_limit, без заливки
строки. export_mappings.py — те же два поля в экспорт АХР.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-31 17:54:55 +04:00
Andrey Epifancev
ab765f028d fix(form2): лимиты АХР — годовые нормативы делятся на 4, сумма без НДС, тыс. руб. в АХР
AURORA-1897: лист «АХР лимиты» формы 2 дублировал годовой норматив ДМС/оргтехники
в каждом квартале вместо деления на 4, считал сумму от лимита с НДС (смета
ведётся без НДС) и переносил рубли в план АХР, где всё остальное в тыс. руб.

Миграция 0052_form2_ahr_limit_period_vat_units:
- v3.limit_template.period (YEAR/QUARTER/MONTH) + точечные правки справочника
  (id 24/25 ДМС и id 13-15 принтеры/МФУ/копиры — YEAR; id 7/8 SIM-карты —
  заполнен limit_without_vat; id 21 — unit по аналогии с бумагой).
- v3._form2_ahr_limit_amount(period, limit, qty) — рубли без НДС, без
  проверки по названию; старая 4-арг. сигнатура удалена.
- v3.v_form2_view (ветка AHR_LIMIT), v3.sync_form2_ahr_satellite_lines(_line)
  пересозданы: amount/plan считаются от limit_without_vat, план АХР делится
  на 1000, добавлены amount_year и строка «Итого» (id=0, row_type ROOT).
- v3._limit_template_path_set дописывает id=0 в path-set, чтобы «Итого»
  попадала в diff PATCH-ответа.
- Пересинхронизированы все существующие формы 2.

Ветки аренды/коммуналки/охраны и add_budget_line (AURORA-1887/1909) не тронуты.

Тесты: api/tests/integration/test_form2_ahr_limit.py (7 тестов, числа из
тикета: id24 qty10→69645/69.645, id3→2000/2.0, id7→7500/7.5, id13→6471.875/
6.471875, id17→7500/7.5, «Итого», регрессия АХР_RENT без деления).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-31 15:35:23 +04:00
Andrey Epifancev
91cbe99c32 fix(form2): добавление записи на сателлите АХР не захватывает ручную строку
v3.add_budget_line (ветка сателлитов AHR_RENT/AHR_UTILITY/AHR_SECURITY)
искала любую существующую строку статьи без ORDER BY и без исключения
ручных/уже привязанных строк, и переиспользовала её как заготовку для
новой записи. Если у статьи ещё не было строк в
v3.form2_ahr_satellite_line_source (типично — первая запись при наличии
ручной строки той же статьи), sync переименовывал и перезаписывал план/факт
чужой ручной строки, блокируя её для правки.

Теперь для каждой новой записи сателлита всегда создаётся отдельная
пустая строка-заготовка — sync забирает её как строку именно этой записи.
Заодно удалён мёртвый код (_ensure_form2_ahr_sync_line,
_upsert_form2_ahr_sync_values), не имевший вызывающих мест.

AURORA-1907 (лимитная часть) и AURORA-1887 (статьи 314/24/779 в коде) —
отдельные задачи, не затронуты.

Closes #265.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-31 15:07:44 +04:00
Andrey Epifancev
a5ffd476a6 fix(tests): реальный прогон интеграционных тестов СПОД против Postgres
Прогнал api/tests/integration/test_form2_ahr_satellite_spod_integration.py
против живого Postgres (perf/perf/perf на localhost:55432, БД dfip_1906,
alembic upgrade head + фикстура) — нашёл и поправил два бага самого теста
(фичу/миграцию не трогал):

- _find_row не учитывал служебную META-строку (editable/role/user_id без
  id/header), которую v3.v_form_view добавляет первой в любой лист —
  KeyError: 'id' на первой же выборке. Теперь ищет только среди INPUT-строк.
- test_satellite_spod_phase_gates_non_admin_user делал один тест из
  "запрет без fact_q4.spod → добавили ключ → разрешено": неадминский
  403/400 от isp1 внутри теста вызывает db_ref.rollback() в
  tests/integration/conftest.py (per-request откат на ошибке), который
  стирает все несохранённые изменения этого же теста — включая только
  что созданные строку сателлита и form_phase. Разбил на два независимых
  теста (test_satellite_spod_rejected_before_phase_includes_spod_column /
  test_satellite_spod_allowed_once_phase_includes_spod_column), в каждом
  ровно один запрос, которому разрешено провалиться.

pytest tests/integration — 157 passed, 1 failed (pre-existing,
не связан: test_backend_calls_form3_add_project_and_add_line_functions,
FORM_3/add_project, 400 на /api/v1/projects/.../report/.../line), 4 skipped.
Новый файл — 15/15.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-31 00:41:10 +04:00
Andrey Epifancev
d41668dc27 feat(form2): колонка «СПОД» в сателлитах АХР (аренда/коммуналка/охрана)
Добавлен столбец «СПОД» между «Декабрь» и «Итог IV кв.» на листах
AHR_RENT/AHR_UTILITY/AHR_SECURITY формы 2, с переносом значения в
budget_line_quarter.actual_spod синхронизируемой строки АХР (Support)
и, через уже существующую логику, в итог 4 кв. и года АХР/Смет/сводов.

- rent_detail/utility_detail/security_detail: колонка actual_spod
  (ALTER TABLE + ORM).
- v3.v_form2_view: fact_q4.spod и fact_q4.total с учётом spod в ветках
  AHR_RENT/AHR_UTILITY/AHR_SECURITY.
- v3._apply_form_cell: fact_q4.spod разрешён как обычное поле месяца.
- v3._upsert_form2_ahr_detail_sync_values: DROP+CREATE с новым
  параметром p_actual_spod (DROP обязателен из-за неоднозначности
  перегрузки с DEFAULT).
- v3.sync_form2_ahr_satellite_lines/line: actual_spod передаётся из
  rd/ud/sd в апсерт.
- v3.v_svod2_go: строка аренды также считается заполненной по
  actual_spod.
- Data-миграция: 'fact_q4.spod' добавлен в column_keys этапов, где уже
  есть 'fact_q4.dec' (phase_template/form_phase), идемпотентно.
- Тело CREATE OR REPLACE для v_form2_view/sync_form2_ahr_satellite_*
  собрано вручную по миграциям 0033/0034 + regexp-патчам из 0037
  (pg_get_functiondef на живой БД недоступен в этой среде — см.
  Rulings в PR).

Тесты: node --test на новых accessorKey в конфигах фронта + unit-тест
на 0049/0050 (v_svod2_go теперь засчитывает actual_spod для аренды).
Интеграционные тесты добавлены, но не выполнены — нет доступа к
Postgres в этой рабочей среде (см. Rulings в PR).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-31 00:24:56 +04:00
Andrey Epifancev
05fbfd1e1d perf(form4): jit=off на v_form4_sheet_jsonb — компиляция JIT дороже выполнения
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-29 12:11:25 +04:00
Andrey Epifancev
b612178b12 fix(tests): тесты тяжёлых GET — непустые данные свода 2, gzip, правки докстрингов
Фикстура свода 2 достроена до 6 строк на GO и RF по двум орг. единицам с разными section_code;
все случаи SVOD2_CASES проверяют непустоту. Тест rf-rollup переименован (фиксирует существующий дефект),
добавлен тест gzip, в api_split.py оставлены только GET-места.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:43:31 +04:00
Andrey Epifancev
604e277371 perf(api): отчёты и сводные проектов отдают готовый JSON из Postgres
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:24:14 +04:00
Andrey Epifancev
ca7160124a perf(api): GET сводов 1 и 2 отдают готовый JSON из Postgres
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:16:36 +04:00
Andrey Epifancev
d8dba2f7be perf(api): GET листа отдаёт готовый JSON из Postgres без SheetResponse на строку
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:12:09 +04:00
Andrey Epifancev
0175759f95 test(api): фикстура db_session и снимок схемы OpenAPI тяжёлых GET
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:06:16 +04:00
Andrey Epifancev
4dae72da2b test(ws): кэш соединения - unit и интеграционный через websocket_connect
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 20:17:56 +04:00
Andrey Epifancev
55ba4e7e96 test(form4): суммы ROOT/GROUP/ITEM листа CAP против v3.jsonb_sum и Python, фикстура db_session
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:33:58 +04:00
Andrey Epifancev
f0cac30622 test(sheet): контрактный тест ответа PATCH /cell с полными строками (AURORA-1844)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 18:18:07 +04:00
Andrey Epifancev
663f45ee04 test(projects): гонка technical_number и переполнение счётчика (AURORA-1831)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 18:14:33 +04:00
Andrey Epifancev
d73333705c test(stages): aware-даты этапов форм и проектов
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:13:13 +04:00
172b343c69 export-optimize-refresh: очередь загрузки при экспорте 2026-09-30 20:40:21 +03:00
5f887e4261 phase-template-edit: редактирование шаблонов этапов 2026-09-11 16:40:10 +03:00
b34f1f7cad tasks-filter: фильтры для задач форм 1 2 4 и обратная сортировка 2026-09-02 12:30:14 +03:00
5dfb9653c2 delete-from-cell: удаление данных из ячейки numeric делает значение 0 2026-08-28 12:38:27 +03:00
02b746c5ad Merge pull request 'svod: свод 2' (#128) from svod into test
Reviewed-on: #128
2026-08-27 17:01:10 +03:00
4708acbd20 svod: свод 2 2026-08-27 16:52:58 +03:00
658bfe6515 Merge pull request 'добавление пагинация для архивных проектов' (#125) from server-side-archived-projects into test
Reviewed-on: #125
2026-08-26 13:59:42 +03:00
PotapovaA
f7dc90e63b добавление пагинация для архивных проектов 2026-08-26 13:52:53 +03:00
d551939e99 Merge pull request 'svod: свод 1' (#123) from svod into test
Reviewed-on: #123
2026-08-26 13:14:29 +03:00
6f6f35c8b1 svod: свод 1 2026-08-26 13:09:54 +03:00
1789f58581 projects-filters: offset, limit, status_in фильтры для проектов в ручке with-reports 2026-08-25 16:56:17 +03:00
9095cad992 project-back-fix: экпорт проектов, нельзя ставить отрицательные числа в полях проектов, создание года только на следующий год 2026-08-20 21:27:08 +03:00
4e8be39df1 tech-num-fix: поправил формирование технического номера проекта, исправил шаблоны для формы 4, поправил получение стадий у проектов 2026-08-19 17:31:58 +03:00
0245b0c3b2 project-add-year-handler: ручка добавления года 2026-08-14 15:03:18 +03:00
d4c11243b9 projects-handlers: ручки со списком проектов со сметами и их обновление 2026-08-13 13:45:56 +03:00
57f919ac0c phase-directions: направления в фазах, год в зависимости от года формы или проекта, фикс с колонками для редактирования 2026-08-03 18:46:16 +03:00