835 Commits

Author SHA1 Message Date
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
66ceb6c5b2 fix(form4): рассчитывать перенос в фонд экономии на листах АХР/КВ/Операц
Перенос в фонд экономии (q1/q2/q3/q4.transfer_econ, столбцы 75/107/137
и их Q4-аналог формы РФ) был raw passthrough-полем budget_line_quarter.
transfer_to_economy: ничего его не считало, поэтому после сохранения
строки с планом без переносов поле оставалось пустым, а "ВСЕГО" блоков
показывало прочерк вместо суммы.

v3.v_form4_sheet_sections теперь считает transfer_econ_QN из уже
имеющегося residual_after_actual_QN с поправкой на то, что тот уже
вычитает перенос в СЛЕДУЮЩИЙ квартал один раз (через cp1/cp2/cp3):

    transfer_econ_Q1 = residual_after_actual_Q1 - transfer_q3 - transfer_q4
    transfer_econ_Q2 = residual_after_actual_Q2 - transfer_q4
    transfer_econ_Q3 = residual_after_actual_Q3
    transfer_econ_Q4 = residual_after_actual_Q4

Буквальный перенос формулы Excel (ещё и минус transfer_q2 для Q1 /
transfer_q3 для Q2) задвоил бы вычитание. АХР, КВ (CAP) и Операц (OPER)
используют одну и ту же функцию, поэтому один фикс закрывает все три
листа; STRUCTURE — агрегирующий read-only свод без INPUT-строк, его не
касается.

v3._apply_form_cell для FORM_4 больше не принимает прямую запись в
q*.transfer_econ (ValueError computed_field, как у residual_after_actual).

Тесты (api/tests/integration/test_form4_transfer_econ.py):
- репро тикета (переносы = 0);
- регрессия на двойное вычитание с ненулевыми transfer_q2/q3/q4,
  отличающая верную формулу от наивного Excel-порта;
- КВ и Операц дают те же числа, что АХР;
- запись в transfer_econ отклоняется.

Closes #255.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-31 00:24:43 +04:00
Andrey Epifancev
c1c0d73387 fix(stages): исполнитель видит только свои этапы, 404 вместо 500 на чужую форму/проект
- FormPhaseRepository.get_list и Form3PhaseRepository.get_list: новый
  необязательный параметр role для фильтрации по v3.role.code.
- FormPhaseService.get_list и Form3PhaseService.get_list: для не-админа
  передают в репозиторий role по коду роли пользователя (единая функция
  role_code_for_user в form_phase_service.py), для ADMIN фильтр не нужен.
- GET /stages/form/{form_id} и GET /stages/project/{project_id}: сначала
  проверяют доступ к форме/проекту (BudgetFormService.get /
  ProjectService.get_instance), при отсутствии доступа — 404 вместо
  AttributeError → 500.
- StagesTable.jsx: подпись роли этапа показывается только админу.

Closes #250.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-31 00:04:21 +04:00
Andrey Epifancev
b04c87f47c fix(export): выгрузка форм 1 и 4 — порядок столбцов и заголовки как на экране
Выгрузка Excel листов АХР/КВ/Операц/Структура форм 1 и 4 расходилась с экраном
по нескольким независимым причинам:

- FORM_4: _patch_form4_regular_columns() вызывался дважды — второй вызов
  стирал только что добавленные actual_rsbu_spod_q1..q3, поэтому в выгрузке
  были только «Факт 4 кв + СПОД», без «Факт 1/2/3 кв». Убрал второй вызов
  и больше не удаляю эти ключи в самом патче.
- _patch_form4_actual_rsbu_spod_columns: SQL отдаёт actual_rsbu_spod только
  для 4 кв.; для 1-3 кв. ключ теперь q{n}.actual_quarter (как на экране),
  не q{n}.actual_rsbu_spod (всегда пустой).
- Базовая раскладка FORM1_DEPTH_COLUMNS (отражается и в FORM_4, т.к. она из неё
  строится): убрал collegial.empty и группы "shift" (ключ sum, SQL не отдаёт);
  в q2/q3 добавил comment_correct (adj_comment) сразу после reserve_correct —
  раньше его не было вовсе; в q4 то же поле вынес из rev_correct в отдельный
  comment_correct на то же место (было в начале квартала вместе с комментарием
  корректировки плана). group "approved.plan" переименована из скопированного
  "Резерв Финансового Комитета" в "План {year}"; q4 "quarter_correct" — из
  ошибочного "Факт РСБУ 3 кв" в "Факт РСБУ 4 кв {year_short}".
- render_column_years: добавлен плейсхолдер year_plus_2 (year+2); contract_summary
  получил future_y2 "Платежи в {year_plus_2} году" (раньше его не было, и FORM_4
  patch всё равно дублировал его с неверным {next_year}). На экране «Операц»
  формы 1 такой колонки нет — для неё новая FORM1_OPER_DEPTH_COLUMNS без future_y2.
- FORM4_OPER: proc_plan/proc_method после переименования уезжали в конец блока
  ckk (pop+переприсвоение добавляет ключ в конец словаря) — вернул порядок
  экрана через _reorder_children.
- web/.../FORM_1/{AHR,CAP,OPER}.js: в q4 узел "Комментарий" (adj_comment, GG)
  шёл после "Скорректированный план" (corrected_plan, GH) — переставил перед,
  как в 1-3 кв.

Issue: #253 (AURORA-1905, эпик AURORA-1876, Высокий приоритет)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-30 22:04:02 +04:00
Andrey Epifancev
daed54d436 fix(export): Смета РФ — выгрузка Операц/АХР/КВ своей раскладкой, не формы 1
Листы Операц/АХР/КВ формы «Смета РФ» (FORM_2) выгружались раскладкой формы 1
(FORM1_DEPTH_COLUMNS, 180 колонок формы 1) — из 163 колонок экрана совпадали
по ключу только 83, 97 колонок выгрузки были всегда пустыми (блоки формы 1,
которых нет в данных формы 2), заголовки групп — от формы 1. Также на экране
был лишний столбец «Адрес ВСП», которого нет в исходной форме РФ, и ВСП
выгружался бы числовым id вместо номера, который виден на экране.

- FORM2_DEPTH_COLUMNS/FORM2_COLORS (export_mappings.py) — сгенерированы из
  web/src/.../FORM_2/{OPER,AHR,CAP}.js (колонки идентичны у всех трёх листов,
  проверено программно; различаются только цвета). 162 листа дерева —
  байт-в-байт тот же порядок, что на экране, минус header.vsp_address.
  Каждая группа — чисто визуальная обёртка ("key": None), каждый лист несёт
  полный путь данных как "key" (например "booking.y2027.q1"), поэтому порядок
  вложенности групп для Excel-заголовков не завязан на реальный путь к данным.
- Form2DepthRowNormalizer (export_normalizers.py) — в отличие от
  Form1DepthRowNormalizer, не поднимает header.* на верхний уровень: FORM_2
  ожидает их как header.section_code, header.vsp_id и т.д.
- _create_xlsxwriter_sheet_writers: отдельная ветка для FormTypeEnum.FORM_2
  (раньше FORM_2 попадала в общий else вместе с FORM_1). Сателлиты АХР
  (AHR_RENT/UTILITY/SECURITY, AURORA-1904) и STRUCTURE не трогал.
- VSPRepository.get_reg_numbers_by_ids + ExportService._resolve_form2_vsp_numbers:
  один запрос резолвит все встретившиеся header.vsp_id (включая закрытые ВСП)
  в registration_number перед записью в Excel, как уже показывает экран.
- web/.../FORM_2/{OPER,AHR,CAP}.js: убран столбец «Адрес ВСП» (E1) и его цвет —
  по умолчанию, т.к. его нет в исходной форме РФ (см. Rulings в PR). Убрана
  мёртвая ссылка на него в Stages/constants.js.

Issue: #252 (AURORA-1902, эпик AURORA-1876, Высокий приоритет)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-30 21:47:24 +04:00
Andrey Epifancev
c01356736f fix(form2): Смета РФ — итоги групп и IV кв. на свои места
На листе «Смета» формы «Смета РФ» итоговые колонки каждой группы
(план/утверждённые/факт «Итого: Поддержка» и IV квартал скорректированной
сметы) были оторваны от своих групп и собраны в отдельную группу
«В тыс. рублей» справа — хотя в шаблоне идут сразу после своего квартала.

- Перенёс H/N/T (data.*.support.year) в конец соответствующих групп план/
  утверждённые/факт, X (data.corrected.support.q4) — в конец группы
  скорректированной сметы после III квартала.
- Удалил опустевшую группу «В тыс. рублей»; accessorKey групп
  (v_tys_rubley_*) нигде больше не использовались (grep по web/src).
  columnLetter и leaf accessorKey (data.*) не менял — видимые значения
  те же, меняется только расположение колонок.
- Тест читает FORM_2/SMETA.js как текст (модуль не грузится напрямую —
  импортирует '../columnColors' без расширения) и проверяет порядок
  accessorKey листьев и отсутствие заголовка «В тыс. рублей».

Issue: #251 (AURORA-1901, эпик AURORA-1876, Высокий приоритет)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-30 18:12:29 +04:00
Andrey Epifancev
edaed04a03 fix(auth): session no longer drops after access-token expiry
Access-токен жил 60 минут без обновления: фронт не слал response-interceptor
на 401, refresh-токен лежал в localStorage неиспользуемым, WebSocket-сокеты
(форма, статусы выгрузки) не переподключались после закрытия с auth-кодом,
а после редиректа на /login пользователь терял исходную страницу.

- tokenRefresh.js: чистые getTokenExp/msUntilRefresh/createSingleFlight (TDD).
- auth.js: refreshTokens — обновление токена вне интерсепторов api (без
  рекурсии), централизованно чистит токены и шлёт auth:expired при неудаче.
- client.js: response-interceptor повторяет запрос после обновления токена.
- AuthProvider: при старте и по 'auth:expired'/'auth:refreshed' не выкидывает
  из системы, пока жив refresh; проактивный таймер обновления + visibilitychange.
- Routes/LoginPage: PrivateRoute сохраняет исходный location, вход возвращает
  туда же вместо /tasks; убрана мёртвая ветка loading.
- useWebSocket/exports: переподключение после 1008/4401 с одной попыткой
  refreshTokens() перед тем как сдаться.

Issue: #248 (AURORA-1877, эпик AURORA-1876, Критичный приоритет)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-30 17:18:11 +04:00
Andrey Epifancev
dfccd209a7 fix(realtime-table): плавная прокрутка колесом с накоплением цели вместо мгновенного прыжка
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 18:37:02 +04:00
Andrey Epifancev
36d8192221 perf(realtime-table): шаг прокрутки колесом мыши ×2 (AURORA-1944)
Хук useWheelScrollStep перехватывает щелчок колеса мыши над таблицей и
прокручивает вдвое больше (~100 px браузера -> 200 px). Тачпад, Ctrl и
Shift + колесо, горизонтальная прокрутка и поля ввода не меняются.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 18:26:04 +04:00
Andrey Epifancev
ef83a6c361 test(realtime-table): шаг прокрутки колесом — чистые функции
Чистые функции getWheelScrollDelta и canScrollBy для шага прокрутки
колесом мыши в таблице форм (AURORA-1944), с тестами node:test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 18:26:04 +04:00
Andrey Epifancev
7b67740029 fix(dict-vsp): заголовок «Действия» на одной линии с остальными в grid-раскладке
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-29 12:23:02 +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
bf64fa0dc6 fix(dict-vsp): ширины колонок как в test — grid-раскладка не должна менять вид справочника
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 12:10:47 +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
38990dcaf6 docs(openspec): aurora-1848 — решения R-1..R-5 без обсуждения
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:40:58 +04:00
Andrey Epifancev
88c37f97fa fix(perf): compare_responses — устойчивый dump, не-JSON тела, strip_response без result
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:30:49 +04:00
Andrey Epifancev
b3899cf6dc perf(analysis): compare_responses — снимок и сравнение ответов тяжёлых GET
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:29:05 +04:00
Andrey Epifancev
d9aa47388a perf(analysis): api_split — размер ответа на проводе (wire_bytes)
Цикл замера вынесен в measure(); добавлен wire_bytes (num_bytes_downloaded,
байты после сжатия) рядом с декодированным размером. В .md - колонка
«на проводе, МБ». Docstring: актуальные места X-DB-Time-Ms и остаток после
задач A (склейка строк, gzip, передача, распаковка; без pydantic).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:27:08 +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
7094fe839f perf(api): хелпер ответа из готовых JSON-строк и SQL-выражение строки
Добавлены list_json_response (обёртка BaseListResponse из готовых строк),
ROW_COLUMNS, row_json_select и fetch_row_texts для тяжёлых GET (AURORA-1848).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:07:40 +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
1d885d1bbd docs(plan): aurora-1848 — план реализации
Две фазы: A (feat/233-aurora-1848-sheet-json-no-pydantic, PR в test) и B (feat/233-aurora-1848-sheet-json-no-pydantic-b, стеком на A). Решения планирования дописаны в design.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:02:48 +04:00
Andrey Epifancev
3c5487c458 docs(openspec): aurora-1848-sheet-json — пороги, порядок PR, окружение тестов
Порог: свод 2 GO −30 %, лист FORM_2/AHR −10 % и меньший размер; пороги
не условие мержа, корректность (сравнение exact/strip-nulls) — жёсткое.
Решения контроллера: план в две фазы, ветка B стеком на A, archive после
merge B, база сравнения, замеры контроллером, pytest на PG16.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 20:41:01 +04:00
Andrey Epifancev
b701a4f5ff docs(openspec): aurora-1848-sheet-json — proposal, spec, design
Тяжёлые GET листов, сводов и отчётов проектов: готовый JSON из Postgres
без pydantic-модели на строку (PR A) и без null-полей в data листа и
сводов с правкой фронта (PR B). Требования — sheet-read-api и
realtime-sheet-updates; дизайн, риски, тесты и сценарий замера — design.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 20:24:23 +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
1a3d25afce perf(ws): кэш пользователя и доступа к форме на время соединения листа формы
AURORA-1845. process() больше не читает user_service.get / bf_service.get на
каждое сообщение: результат кэшируется в ConnectionCache (TTL 30 с, только
положительные результаты).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 20:17:56 +04:00
Andrey Epifancev
95661c0ae5 perf(analysis): form4_cap_sheet читает тело из миграции 0050
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:33:58 +04:00
Andrey Epifancev
b2a022953d perf(form4): v_form4_sheet_jsonb без рекурсивного агрегата jsonb_sum (GROUPING SETS)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:33:58 +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
6855e9163a perf(sheet): upd_form_cells возвращает затронутые строки целиком, без снимка до и _jsonb_diff (AURORA-1844)
Выборка строк вынесена в v3._form_affected_rows; FORM_4 отбирает ROOT, GROUP и ITEM только изменённых строк; при sections убираются ключи исключённых разделов.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 18:18:07 +04:00
Andrey Epifancev
f7e553a7ab style(table): переименовать параметр escape в activeCellCss (biome)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 18:14:53 +04:00
Andrey Epifancev
21aded26cc perf(table): статичные стили ячеек в CSS контейнера, overscan строк 2 и колонок 3
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 18:14:38 +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
95b74bc5e5 fix(projects): advisory lock и ошибка переполнения счётчика technical_number в add_project (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
Andrey Epifancev
bc89789026 fix(stages): нормализовать aware-даты этапов к поясу оргединицы
asyncpg не принимает aware-datetime для CAST AS TIMESTAMP WITHOUT TIME ZONE (400).
Даты переводятся в пояс оргединицы и становятся naive, SQL приписывает тот же offset.
timezone_from_offset вынесен в src/core/timezones.py, добавлен to_org_local_naive.
Aware-дата при NULL utc_offset оргединицы даёт 422.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:13:13 +04:00
Andrey Epifancev
b0dcc0b0f5 fix(deps): явно ставить greenlet в dev-окружении через sqlalchemy[asyncio]
SQLAlchemy не объявляет greenlet без extra asyncio. На arm64 (macOS)
и на Python >= 3.13 greenlet не ставится, и async-движок падает с
"the greenlet library is required". Тест фиксирует наличие greenlet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:11:10 +04:00
Andrey Epifancev
2dc9b4df03 fix(users): выходные схемы пользователя без входных ограничений (/users/me 500 на .local)
UserInDB наследует новую UserReadBase (email/username/full_name без
валидации формата). Вход (UserBase/UserCreate/UserUpdate) не менялся.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 18:11:06 +04:00
Andrey Epifancev
4393e964f4 perf(realtime): убрать console.log каждого WebSocket-сообщения
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 18:10:30 +04:00
Andrey Epifancev
17dddc21d3 perf(realtime): updateCells без structuredClone и мутации входа
Копируются только строки на пути к затронутой, остальные сохраняют идентичность;
без изменений возвращается тот же data. Отсутствующие в row.data ключи теперь
вставляются (нужно для AURORA-1844/1848). Тесты с паритетом к прежней реализации.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 18:10:30 +04:00
Andrey Epifancev
12a315b04a perf(table): фиксированная высота строк вместо measureElement (лист, свод 1, свод 2)
Высота строки и ячейки задана явно в rem, виртуализатор не читает offsetHeight.
Мёртвая muiTableRowProps удалена. Overscan строк свода 2: 8 -> 3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:10:21 +04:00
Andrey Epifancev
bdb00afc5a test(table): хелпер createFixedRowVirtualizerOptions без чтения layout
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:10:21 +04:00
Andrey Epifancev
eae445ca78 fix(alembic): алиас подзапроса в 0012 can_delete_project (PostgreSQL 15)
На PostgreSQL 15 подзапрос в FROM без алиаса не принимается, миграция
падала. Добавлен алиас active_reports, смысл не меняется. Правка старой
миграции: 0012 выполняется раньше любой новой, на базах, где 0012 уже
применена, ничего не меняет.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-28 18:10:13 +04:00
Andrey Epifancev
5e7ac886aa test(alembic): регрессия на подзапросы в FROM без алиаса (PostgreSQL 15)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-28 18:10:13 +04:00
Andrey Epifancev
b0de763259 perf(dict-vsp): виртуализация строк, состояние модалок вынесено из таблицы
Модалки в DictVspModals (forwardRef), таблица в memo со стабильными колбэками,
enableRowVirtualization, size/minSize/maxSize колонок выверены под grid-раскладку.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:09:52 +04:00
Andrey Epifancev
3ab229f6f8 test(dict-vsp): фильтрация ВСП по ССП вынесена в чистую функцию
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 18:09:52 +04:00