858 Commits

Author SHA1 Message Date
Andrey Epifancev
fa586c85cc feat(table): отрицательные суммы красным и правило «минус запрещён» (AURORA-1892)
Cell.jsx: formatNumber вынесен в utils/numberFormat.js и возвращает
{ text, isNegative }; отрицательные числа выделяются цветом (#C62828 на
светлом фоне, #FFCDD2 на тёмном) и font-weight 600, ноль остаётся «-».

constants/validation.js: фабрика nonNegativeRule и общий список
nonNegativeColumnKeys (план, бронь на 2026/2027, факт по месяцам и СПОД,
сумма платежа, предельная стоимость договора/ЦКК, сателлиты АХР
Аренда/Коммунальные/Безопасность, АХР Лимиты целиком). Корректировки и
переносы (adj_*, base_correction, revision_*, seq_*, target_change,
transfer_*) не включены — там минус — нормальная операция. Запрет
мягкий: подсветка и запись в «Контроль ошибок» через существующий
useValidationRules/isCellInvalid, сохранение не блокируется.

EditCell.jsx: handleSave предупреждает toast.warning при сохранении
отрицательного числа в защищённой колонке, значение всё равно
сохраняется. useValidationRules и RealtimeTable прокидывают
человекочитаемый заголовок колонки для текста предупреждения.

Тесты (node --test): numberFormat.test.js, validationUtils.test.js —
новые, 10/10 зелёных.
2026-11-01 12:20:06 +04:00
aep
8c467804be Merge pull request 'AURORA-1848: лист и своды без null-полей + фронт (B)' (#242) from feat/233-aurora-1848-sheet-json-no-pydantic-b into test 2026-11-01 11:16:08 +03: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
2436a6b3f7 docs(openspec): aurora-1848 — решения R-6..R-8 без обсуждения
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 12:14:49 +04:00
Andrey Epifancev
8015a6cc9d perf(gen): grow не падает на ответах листа без null-полей
form4_ids вычисляет vsp_ids и project_ids из строк FORM_4 через .get
с пропуском отсутствующих ключей, grow_form4_sheet использует её.

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
Andrey Epifancev
c7c679851d fix(realtime): пустая правка уходит как value: null, а не без ключа (AURORA-1848)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 12:14:49 +04:00
Andrey Epifancev
be8d41ccb5 test(realtime): правка соавтора по ключу, которого нет в строке (AURORA-1848)
Четыре теста updateCells: вставка отсутствующего блока и поля,
явная запись null, вставка отсутствующего скалярного ключа data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 12:14:49 +04:00
Andrey Epifancev
83cb364a9e fix(realtime): красная рамка у ошибки ячейки, тост без Server error:undefined (AURORA-1894)
Cell.jsx: порядок спреда стилей — base → editable/not-editable → locked →
invalid (INVALID_STYLES последним), чтобы красная рамка 2px не перетиралась
EDITABLE_STYLES у вводимых ячеек с ошибкой.

utils/wsErrorUtils.js: getWsErrorMessage(error) — строка возвращается как
есть, объект берёт message || detail, известные технические префиксы
(computed_field:, role_not_allowed:, window_closed:, org_not_assigned)
заменяются понятным текстом, прочее отдаётся без "Server error:".

RealtimeContext.js: toast.error(getWsErrorMessage(data.error)) вместо
`Server error:${data.error.message}` || '', которое всегда давало undefined
для ошибок-строк (|| не срабатывает на непустой строке).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-11-01 12:10:16 +04:00
aep
26ae77878b Merge pull request 'AURORA-1837: статичные стили ячеек в CSS, overscan' (#230) from feat/226-aurora-1837-cell-styles-css into test 2026-11-01 11:05:50 +03: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
1b8f7c7d12 fix(alembic): перенумеровать 0050→0053 во избежание конфликта с #229/#231/#244
#229, #231 и #244 уже заняли 0050-0052 к моменту слияния этого PR.
Переносим form4_sheet_sum на 0053, down_revision=0052, и правим путь
к SQL-файлу в perf/analysis/explain_manual.py.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-11-01 12:03:39 +04:00
aep
9229ee5ce3 Merge pull request 'fix(alembic): устранить дубликат revision=0051 в test (AURORA-1850)' (#274) from fix/0051-migration-collision into test 2026-11-01 11:02:15 +03:00
Andrey Epifancev
ece050d19f fix(alembic): устранить дубликат revision=0051 в test (AURORA-1850)
При слиянии PR #244 в test оказались две миграции с revision="0051"
и down_revision="0050" (upd_form_cells_full_rows из #231 и
upd_form_cells_subtree из #244) — неоднозначная цепочка alembic.
Фикс-коммит с переименованием 0051→0052 для #244 был подготовлен
локально, но потерялся при повторном force-push после блокировки
классификатором permission — слияние прошло с исходной (дублирующейся)
нумерацией. Переносим upd_form_cells_subtree на 0052.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-11-01 12:00:14 +04:00
aep
77f8957719 Merge pull request 'AURORA-1850: правка ячейки пересчитывает только затронутое поддерево' (#244) from feat/235-aurora-1850-upd-form-cells-subtree into test 2026-11-01 10:57:50 +03:00
Andrey Epifancev
2f1dce2c61 docs(openspec): aurora-1850 — рулинг R-2 заменяет skip теста track_functions (M-3)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 10:47:27 +04: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
47c758e205 docs: ARCHITECTURE — фокусная сборка ответа на правку (AURORA-1850)
Co-Authored-By: Claude Haiku 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
1e1f5e3824 perf(sql): дельта АХР через _form_affected_rows, с предками SUB_SUB_ITEM (AURORA-1850)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 10:47:27 +04:00
Andrey Epifancev
5060f32341 perf(sql): _form_affected_rows собирает только затронутое поддерево (AURORA-1850)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 10:47:27 +04:00
Andrey Epifancev
6d232064c6 perf(sql): фокус и _sort_rank в построителях FORM_1, новая v_form1_view (AURORA-1850)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 10:47:27 +04:00
Andrey Epifancev
daad7a4bc0 docs(openspec): aurora-1850 — решения Task 4 (T4.4)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 10:47:27 +04:00
Andrey Epifancev
6b17d7c076 perf(sql): фокус сателлитов, AHR_LIMIT и OTCH9F в v_form2_view (AURORA-1850)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 10:47:27 +04:00
Andrey Epifancev
8f82f5fb9b docs(openspec): aurora-1850 — решение по соединению скелета (T3.1)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 10:47:27 +04:00
Andrey Epifancev
8f45544a17 perf(sql): фокус и _sort_rank в построителях FORM_2 AHR/CAP/OPER (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
0f889c8d5e docs(openspec): aurora-1850 — решение посева снимка GET (T1.2)
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
Andrey Epifancev
f1d4f2644c docs(openspec): aurora-1850 — тела функций до 0051 (pg_get_functiondef, ревизия 0050)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 10:47:27 +04:00
Andrey Epifancev
4c493bb269 docs(openspec): aurora-1850 — рулинги контроллера R-1..R-4, R-14
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 10:47:27 +04:00
Andrey Epifancev
93ea5b5c42 docs(plan): aurora-1850 — план реализации
План в 10 задач по design.md: тестовая БД dfip_1850 на 0050 и тела функций
до 0051 (D8), красный тест эквивалентности, построители с фокусом и
_sort_rank (FORM_2, затем FORM_1 и v_form1_view), _form_affected_rows,
дельта АХР, jit = off, защита 0037, документация. Решения плана дописаны
в design.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 10:47:27 +04:00
Andrey Epifancev
661a3571b5 docs(openspec): aurora-1850 — решения 28.10 по JIT и условию мержа, решения контроллера
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 10:47:27 +04:00
Andrey Epifancev
4251490fa9 docs(openspec): aurora-1850-upd-form-cells-subtree — proposal, spec, design
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
Andrey Epifancev
8ec37fe7eb fix(alembic): перенумеровать 0050→0051 во избежание конфликта с #229
Три независимых approved PR (#229, #231, #232) добавляли миграцию
с одним и тем же revision=0050 поверх общего down_revision=0049.
#229 слит первым и занял 0050 (project_technical_number_lock),
поэтому здесь переносим upd_form_cells_full_rows на 0051.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-11-01 10:46:17 +04: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
aep
dc82239770 Merge pull request 'AURORA-1845: кэш user/формы на время WS-соединения' (#238) from feat/237-aurora-1845-ws-connection-cache into test 2026-11-01 09:41:43 +03:00
aep
8b081a4b42 Merge pull request 'AURORA-1833: явно ставить greenlet в dev-окружении' (#225) from feat/209-aurora-1833-greenlet-dev into test 2026-11-01 09:39:59 +03:00
aep
6ff979ae57 Merge pull request 'AURORA-1830: /users/me не падает на email .local' (#224) from feat/206-aurora-1830-users-me-local-email into test 2026-11-01 09:39:44 +03:00
aep
cda8f12492 Merge pull request 'AURORA-1838: updateCells без клонирования всего дерева' (#223) from feat/212-aurora-1838-ws-immutable-update into test 2026-11-01 09:38:39 +03:00
aep
1ece2f3c08 Merge pull request 'AURORA-1836: фиксированная высота строк вместо measureElement' (#222) from feat/211-aurora-1836-row-measure into test 2026-11-01 09:37:38 +03:00
aep
471d058948 Merge pull request 'AURORA-1922: алиас подзапроса в 0012 для PostgreSQL 15' (#221) from feat/205-aurora-1922-pg15-migration-0012 into test 2026-11-01 09:35:35 +03:00
aep
61c8ab7fff Merge pull request 'AURORA-1841: справочник ВСП - виртуализация строк и вынос состояния модалок' (#219) from feat/214-aurora-1841-dict-vsp-virtual into test 2026-11-01 09:35:13 +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