feat: формулы ячеек на сервере, маркер fx для всех пользователей (AURORA-1896) #323

Merged
aep merged 25 commits from feat/320-aurora-1896-cell-formulas into test 2026-11-12 17:03:43 +03:00
Owner

Closes #320. Jira: AURORA-1896 (замечание заказчика №8 «Формулы и факт»).

Что сделано

Формулы ячеек теперь хранятся на сервере и видны всем пользователям, а не только в localStorage автора.

  • SQL (миграция 0060_cell_formula):
    • таблица v3.cell_formula — последняя формула ячейки: выражение, значение, автор, время;
    • FK с каскадом на budget_form и rf_project_report;
    • функции v3.set_cell_formulas и v3.v_cell_formulas;
    • триггеры очистки на budget_line, rent_detail, utility_detail, security_detail, fixed_asset_report и rf_project_report_line.
  • API:
    • необязательное поле formula в PATCH …/cell и …/cells для форм и отчётов проектов. Формула пишется в той же транзакции, что и значение;
    • запись значения без формулы удаляет формулу ячейки;
    • некорректное выражение даёт 422 invalid_formula;
    • новые GET: /form/{id}/sheet/{sheet}/formulas и /projects/{id}/report/{year}/{type}/formulas.
  • WebSocket: cell_updated несёт data.formula и data.formula_meta. Значения выставляет сервер, а не пересылает присланные клиентом, в том числе при отказе в доступе (I-1).
  • Фронт:
    • карта формул листа (formulaStore, RealtimeContext);
    • маркер fx с подсказкой «=выражение · ФИО · дата время», если значение формулы совпадает с текущим значением ячейки;
    • редактор открывается с =выражением с сервера;
    • формулы больше не хранятся в localStorage, старые ключи formula_* удаляются.

Спека: openspec/changes/aurora-1896-cell-formulas/. План: docs/superpowers/plans/2026-11-11-aurora-1896-cell-formulas.md.

⚠️ Порядок мержа: #315 → #317 → #319 → #323

Миграция — 0061_cell_formula, down_revision = "0060". Это 0060_form_phase_direction_fix из PR #315 (AURORA-1879).

Этот PR вливается только после #315, иначе Alembic не найдёт ревизию 0060. Проверено на временном слиянии всего стека (#315/#317/#319) с этой веткой, результат — в разделе «Проверка».

Обратить внимание

  • M-4. Правка core/exception_handlers.py: снимается обёртка asyncpg <class …>: перед сопоставлением префикса. Из-за этого все validation-префиксы на реальной БД теперь отвечают 422 вместо 400, и поле field заполняется. Фронт не различает 400 и 422.
  • M-7 / T3.4. Формула в текстовую колонку принимается, если value числовое. SQL не знает типов колонок (design D3).
  • design #8. Для FORM_3 нет серверной проверки окон. Дыра существовала и раньше, этот PR её не меняет.
  • T3.3. На каждую правку ячейки добавляется один SQL-вызов. Перф AURORA-1850 (p50) не замерялся.

Проверка

  • Backend. Полный pytest --init_db на чистой БД: 947 passed, 10 failed, 4 skipped. Все 10 падений — известные падения базовой test, список совпадает один в один.
  • Frontend. node --test по всем тестам: 300 pass, 1 fail — известный SMETA.test.js. npm run build проходит. Biome: новых ошибок нет.
  • openspec validate aurora-1896-cell-formulas: valid.
  • Браузер (12.11). Все 7 сценариев прошли: форма FORM_1/AHR под двумя пользователями (admin и isp2) и отчёт проекта «Лимиты». Подробности — в комментарии к PR.
  • Стек (12.11). Временное слияние #319 (включает #315 и #317) с этой веткой, чистая БД, полный pytest --init_db: 957 passed, 10 failed (те же 10 известных падений), 4 skipped. Alembic доходит до 0061 одной цепочкой.

Rulings I made

Маркер Решение Цена ошибки
R-1 (design #10) Трейлер коммитов Co-Authored-By: Claude Opus 5.5 по Global Constraints плана косметика
R-2 (#11) Суффиксы ошибок invalid_formula: owner|line_id|length|chars|value. Пустой line_id или column даёт invalid_formula: line_id. Владелец проверяется и в v_cell_formulas. Своя фикстура svod2_sql с откатом низкая
R-3 (#12) Minor-находки Task 1 исправлены одной пачкой после Task 4 низкая
R-4 (#13) upd_form3_cells работает по тому же правилу, что cells: exclude={"formula"}, ключ formula добавляется только если он не None нет
R-5 (#14) Принята правка exception_handlers.py: 422 вместо 400 для validation-префиксов на реальной БД клиенты, ожидавшие 400
R-6 (#16) Пачка minor-правок Task 1–4 без отдельного re-review, её проверило финальное ревью регрессия ловится на шаг позже
R-7 (#17) Клиент API — методы FormsSheetApi.getFormulas и ProjectsApi.getReportFormulas нет
R-8 (#17) T6.1 и T6.2 исправлены в Task 7 нет
R-9 (#19, #21) После rebase миграция переименована в 0060. Номер остаётся, пока не влит #315 два head Alembic
R-10 (#20) Спека GET формул отчёта проекта приведена к коду: 400 «Отчёт не найден», как у PATCH низкая
R-12 (#22) Миграция переименована в 0061 поверх 0060 из #315. Порядок мержа #315 → #317 → #319 → #323 подтвердил человек PR нельзя вливать раньше #315
R-11 (#20) Исправления по финальному ревью: I-1, I-2, M-1/M-2/M-3, M-5. Дополнительное усиление клиента по I-1 не делалось низкая

Находки

Маркер Суть Статус
T1.1 Тест каскада hard delete не изолировал FK-каскад fixed 84dbaaf
T1.2 Нет тестов триггеров сателлитов, ОС и строк отчёта проекта fixed 84dbaaf
T1.3 NaN принимается как value fixed 84dbaaf
T1.4 NULL p_sheet или неизвестный пользователь дают сырую ошибку БД deferred
T1.5 Дубликат ключа в одном вызове возвращает две строки rejected (ошибка клиента, безвредно)
T1.6 downgrade = pass rejected (как в остальных миграциях)
O-1 Красный шаг TDD в Task 1 не отработан строго rejected (замечание к процессу)
T2.1 float(value) при NULL rejected (SQL гарантирует numeric)
T2.2 Литерал plan.q1 в тестах rejected
T3.1 Нет unit-теста на обёрнутый формат asyncpg fixed 84dbaaf
T3.2 SAVEPOINT- и 403-тесты без предусловий fixed 84dbaaf
T3.3 Лишний SQL-вызов на правку, перф 1850 deferred
T3.4 Формула в текстовую колонку при числовом value rejected (D3)
T4.1 Тест 422 для проектов не проверял, что значение не записано fixed 84dbaaf
T4.2 Мёртвая логика в тесте триггера fixed 84dbaaf
T4.3 Неуклюжий portal.call в тесте rejected (косметика)
O-2 3 теста в test_projects_api_smoke пропускаются rejected (было и до этого PR)
T5.1 WS-тесты восстанавливали ячейку в None fixed 18573d6
T5.2 WS-тест не читал значение из БД до и после fixed 18573d6
T5.3 Нет WS-теста для проектного процессора fixed 18573d6
T5.4 Фолбэк hasattr(updated_at, 'isoformat') rejected (безвредно)
T6.1 «Пользователь #undefined» в подсказке fixed 3bee5e7
T6.2 Нет тестов на невалидный updated_at и нулевые id fixed 3bee5e7
T6.3 Новые API-методы возвращают r.data.result rejected (намеренно)
T7.1 Буферизованные WS-сообщения старого сокета после смены листа deferred
T7.2 GET формул при isConnected=false deferred
T7.3 fx может налезать на левый текст rejected (в браузере: формулы в числовых колонках, числа справа, наложения нет)
T7.4 5-1 без «=» в числовой колонке блокируется rejected (D7)
T7.5 Возможное мигание fx на один кадр deferred
O-3 UI не проверен в браузере fixed (проверено в браузере 12.11, см. комментарий в PR)
I-1 WS транслировал присланные клиентом formula и formula_meta без доступа fixed ecef6eb
I-2 Клиент удалял любые \s, сервер принимал только ASCII-пробел fixed e569d18
I-3 Номер миграции 0060 совпадает с #315 fixed 38036b3 (0061, порядок мержа зафиксирован)
M-1 Лишний SQL-вызов на правку, индекс не используется fixed e107fa4
M-2 v_cell_formulas не использует индекс владельца fixed e107fa4
M-3 Infinity принимается, GET листа даёт 500 fixed e107fa4
M-4 400→422 для всех validation-префиксов deferred (см. выше)
M-5 GET формул проекта без доступа: расхождение со спекой, нет теста fixed 54e60dc
M-6 403-тест тавтологичен из-за отката сессии deferred
M-7 Формула в текстовую колонку при числовом value rejected (D3)
M-8 formulaStore.size() нужен только тестам, WS-тесты привязаны к seed deferred

| O-4 | Подсказка формулы появляется только при наведении на значок fx (около 8×9 px), а не на всю ячейку | deferred (UX, на усмотрение фронта) |

Deferred (уйдут в follow-up при закрытии): T1.4, T3.3, T7.1, T7.2, T7.5, M-4, M-6, M-8, O-4.

🤖 Generated with Claude Code

Closes #320. Jira: AURORA-1896 (замечание заказчика №8 «Формулы и факт»). ## Что сделано Формулы ячеек теперь хранятся на сервере и видны всем пользователям, а не только в localStorage автора. - **SQL** (миграция `0060_cell_formula`): - таблица `v3.cell_formula` — последняя формула ячейки: выражение, значение, автор, время; - FK с каскадом на `budget_form` и `rf_project_report`; - функции `v3.set_cell_formulas` и `v3.v_cell_formulas`; - триггеры очистки на `budget_line`, `rent_detail`, `utility_detail`, `security_detail`, `fixed_asset_report` и `rf_project_report_line`. - **API**: - необязательное поле `formula` в `PATCH …/cell` и `…/cells` для форм и отчётов проектов. Формула пишется в той же транзакции, что и значение; - запись значения без формулы удаляет формулу ячейки; - некорректное выражение даёт 422 `invalid_formula`; - новые GET: `/form/{id}/sheet/{sheet}/formulas` и `/projects/{id}/report/{year}/{type}/formulas`. - **WebSocket**: `cell_updated` несёт `data.formula` и `data.formula_meta`. Значения выставляет сервер, а не пересылает присланные клиентом, в том числе при отказе в доступе (I-1). - **Фронт**: - карта формул листа (`formulaStore`, `RealtimeContext`); - маркер `fx` с подсказкой «=выражение · ФИО · дата время», если значение формулы совпадает с текущим значением ячейки; - редактор открывается с `=выражением` с сервера; - формулы больше не хранятся в localStorage, старые ключи `formula_*` удаляются. Спека: `openspec/changes/aurora-1896-cell-formulas/`. План: `docs/superpowers/plans/2026-11-11-aurora-1896-cell-formulas.md`. ## ⚠️ Порядок мержа: #315 → #317 → #319 → #323 Миграция — `0061_cell_formula`, `down_revision = "0060"`. Это `0060_form_phase_direction_fix` из PR #315 (AURORA-1879). **Этот PR вливается только после #315**, иначе Alembic не найдёт ревизию 0060. Проверено на временном слиянии всего стека (#315/#317/#319) с этой веткой, результат — в разделе «Проверка». ## Обратить внимание - **M-4.** Правка `core/exception_handlers.py`: снимается обёртка asyncpg `<class …>: ` перед сопоставлением префикса. Из-за этого **все** validation-префиксы на реальной БД теперь отвечают 422 вместо 400, и поле `field` заполняется. Фронт не различает 400 и 422. - **M-7 / T3.4.** Формула в текстовую колонку принимается, если `value` числовое. SQL не знает типов колонок (design D3). - **design #8.** Для FORM_3 нет серверной проверки окон. Дыра существовала и раньше, этот PR её не меняет. - **T3.3.** На каждую правку ячейки добавляется один SQL-вызов. Перф AURORA-1850 (p50) не замерялся. ## Проверка - **Backend.** Полный `pytest --init_db` на чистой БД: **947 passed, 10 failed, 4 skipped**. Все 10 падений — известные падения базовой `test`, список совпадает один в один. - **Frontend.** `node --test` по всем тестам: 300 pass, 1 fail — известный `SMETA.test.js`. `npm run build` проходит. Biome: новых ошибок нет. - **`openspec validate aurora-1896-cell-formulas`**: valid. - **Браузер (12.11).** Все 7 сценариев прошли: форма FORM_1/AHR под двумя пользователями (admin и isp2) и отчёт проекта «Лимиты». Подробности — в комментарии к PR. - **Стек (12.11).** Временное слияние #319 (включает #315 и #317) с этой веткой, чистая БД, полный `pytest --init_db`: 957 passed, 10 failed (те же 10 известных падений), 4 skipped. Alembic доходит до 0061 одной цепочкой. ## Rulings I made | Маркер | Решение | Цена ошибки | |---|---|---| | R-1 (design #10) | Трейлер коммитов `Co-Authored-By: Claude Opus 5.5` по Global Constraints плана | косметика | | R-2 (#11) | Суффиксы ошибок `invalid_formula: owner\|line_id\|length\|chars\|value`. Пустой `line_id` или `column` даёт `invalid_formula: line_id`. Владелец проверяется и в `v_cell_formulas`. Своя фикстура `svod2_sql` с откатом | низкая | | R-3 (#12) | Minor-находки Task 1 исправлены одной пачкой после Task 4 | низкая | | R-4 (#13) | `upd_form3_cells` работает по тому же правилу, что `cells`: `exclude={"formula"}`, ключ `formula` добавляется только если он не None | нет | | R-5 (#14) | Принята правка `exception_handlers.py`: 422 вместо 400 для validation-префиксов на реальной БД | клиенты, ожидавшие 400 | | R-6 (#16) | Пачка minor-правок Task 1–4 без отдельного re-review, её проверило финальное ревью | регрессия ловится на шаг позже | | R-7 (#17) | Клиент API — методы `FormsSheetApi.getFormulas` и `ProjectsApi.getReportFormulas` | нет | | R-8 (#17) | T6.1 и T6.2 исправлены в Task 7 | нет | | R-9 (#19, #21) | После rebase миграция переименована в 0060. Номер остаётся, пока не влит #315 | два head Alembic | | R-10 (#20) | Спека GET формул отчёта проекта приведена к коду: 400 «Отчёт не найден», как у PATCH | низкая | | R-12 (#22) | Миграция переименована в `0061` поверх `0060` из #315. Порядок мержа #315 → #317 → #319 → #323 подтвердил человек | PR нельзя вливать раньше #315 | | R-11 (#20) | Исправления по финальному ревью: I-1, I-2, M-1/M-2/M-3, M-5. Дополнительное усиление клиента по I-1 не делалось | низкая | ## Находки | Маркер | Суть | Статус | |---|---|---| | T1.1 | Тест каскада hard delete не изолировал FK-каскад | fixed 84dbaaf | | T1.2 | Нет тестов триггеров сателлитов, ОС и строк отчёта проекта | fixed 84dbaaf | | T1.3 | `NaN` принимается как value | fixed 84dbaaf | | T1.4 | NULL `p_sheet` или неизвестный пользователь дают сырую ошибку БД | deferred | | T1.5 | Дубликат ключа в одном вызове возвращает две строки | rejected (ошибка клиента, безвредно) | | T1.6 | `downgrade = pass` | rejected (как в остальных миграциях) | | O-1 | Красный шаг TDD в Task 1 не отработан строго | rejected (замечание к процессу) | | T2.1 | `float(value)` при NULL | rejected (SQL гарантирует numeric) | | T2.2 | Литерал `plan.q1` в тестах | rejected | | T3.1 | Нет unit-теста на обёрнутый формат asyncpg | fixed 84dbaaf | | T3.2 | SAVEPOINT- и 403-тесты без предусловий | fixed 84dbaaf | | T3.3 | Лишний SQL-вызов на правку, перф 1850 | deferred | | T3.4 | Формула в текстовую колонку при числовом value | rejected (D3) | | T4.1 | Тест 422 для проектов не проверял, что значение не записано | fixed 84dbaaf | | T4.2 | Мёртвая логика в тесте триггера | fixed 84dbaaf | | T4.3 | Неуклюжий `portal.call` в тесте | rejected (косметика) | | O-2 | 3 теста в `test_projects_api_smoke` пропускаются | rejected (было и до этого PR) | | T5.1 | WS-тесты восстанавливали ячейку в None | fixed 18573d6 | | T5.2 | WS-тест не читал значение из БД до и после | fixed 18573d6 | | T5.3 | Нет WS-теста для проектного процессора | fixed 18573d6 | | T5.4 | Фолбэк `hasattr(updated_at, 'isoformat')` | rejected (безвредно) | | T6.1 | «Пользователь #undefined» в подсказке | fixed 3bee5e7 | | T6.2 | Нет тестов на невалидный `updated_at` и нулевые id | fixed 3bee5e7 | | T6.3 | Новые API-методы возвращают `r.data.result` | rejected (намеренно) | | T7.1 | Буферизованные WS-сообщения старого сокета после смены листа | deferred | | T7.2 | GET формул при `isConnected=false` | deferred | | T7.3 | `fx` может налезать на левый текст | rejected (в браузере: формулы в числовых колонках, числа справа, наложения нет) | | T7.4 | `5-1` без «=» в числовой колонке блокируется | rejected (D7) | | T7.5 | Возможное мигание `fx` на один кадр | deferred | | O-3 | UI не проверен в браузере | fixed (проверено в браузере 12.11, см. комментарий в PR) | | I-1 | WS транслировал присланные клиентом formula и formula_meta без доступа | fixed ecef6eb | | I-2 | Клиент удалял любые `\s`, сервер принимал только ASCII-пробел | fixed e569d18 | | I-3 | Номер миграции 0060 совпадает с #315 | fixed 38036b3 (0061, порядок мержа зафиксирован) | | M-1 | Лишний SQL-вызов на правку, индекс не используется | fixed e107fa4 | | M-2 | `v_cell_formulas` не использует индекс владельца | fixed e107fa4 | | M-3 | `Infinity` принимается, GET листа даёт 500 | fixed e107fa4 | | M-4 | 400→422 для всех validation-префиксов | deferred (см. выше) | | M-5 | GET формул проекта без доступа: расхождение со спекой, нет теста | fixed 54e60dc | | M-6 | 403-тест тавтологичен из-за отката сессии | deferred | | M-7 | Формула в текстовую колонку при числовом value | rejected (D3) | | M-8 | `formulaStore.size()` нужен только тестам, WS-тесты привязаны к seed | deferred | | O-4 | Подсказка формулы появляется только при наведении на значок `fx` (около 8×9 px), а не на всю ячейку | deferred (UX, на усмотрение фронта) | **Deferred** (уйдут в follow-up при закрытии): T1.4, T3.3, T7.1, T7.2, T7.5, M-4, M-6, M-8, O-4. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
aep added 24 commits 2026-11-11 20:16:36 +03:00
proposal, дельта спеки cell-formula, design.md со сверкой дизайна Jira
с кодом test 5a91f35 и разделом решений без обсуждения.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SheetService.update_cell_with_formula/update_cells пишут формулу после значения в одной
транзакции, GET /form/{id}/sheet/{sheet}/formulas. Ошибки БД с обёрткой asyncpg
"<class ...>: invalid_formula: ..." теперь корректно мапятся в 422.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PATCH cell/cells отчёта проекта пишут формулу в той же транзакции, GET
.../report/{year}/{report_type}/formulas возвращает формулы отчёта.
Тесты: roundtrip, снятие формулы, каскад удаления проекта, триггер
rf_project_report_line (del_form3_line и прямой DELETE).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
T1.1 каскад hard_delete проверяется по AHR_LIMIT; T1.2 тесты триггеров
rent/utility/security/fixed_asset_report; T1.3 NaN в set_cell_formulas
отклоняется (0059 правится на месте); T3.1 unit-кейсы asyncpg-обёртки;
T3.2 предусловие SAVEPOINT-теста и комментарий к 403; T4.1 проверка
незаписи значения при 422 отчёта проекта; T4.2 убрана мёртвая логика.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- RealtimeContext: стор карты формул (formulaStore), загрузка при открытии листа и
  переподключении (формы и отчёты проектов), обновление на каждом cell_updated,
  защита от устаревших ответов при смене листа; useCellFormula; updateCell(..., { formula })
- Cell: маркер fx только при актуальной формуле, подсказка с единицей при displayFactor != 1
- EditCell: начальный текст «=выражение», выражение уходит в data.formula, без localStorage;
  ввод «100+50» без «=» не отправляется (тост «Формула начинается с =»)
- удалены resolveEditorState и clearLegacyFormulaKeys; T6.1/T6.2 в cellFormulaUtils

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
aep requested review from tsygankoviva 2026-11-11 20:16:51 +03:00
aep requested review from PotapovaA 2026-11-11 20:16:52 +03:00
aep added 1 commit 2026-11-12 00:09:04 +03:00
Порядок мержа фиксирован: #315 → #317 → #319 → #323. design.md #22.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Owner

Проверка в браузере (12.11)

Проверено локально: API и фронт из этой ветки, чистая БД с сидом.

  • Форма: FORM_1, лист АХР, направление «Поддержка». Две изолированные сессии браузера: admin (автор) и isp2 (второй пользователь).
  • Отчёт проекта: «Лимиты», две вкладки.
Сценарий Результат
Автор вводит =100+50 В ячейке 150,0 и маркер fx в левом нижнем углу
Второй пользователь без перезагрузки Видит 150,0 и fx, подсказка «=100+50 · admin · 12.11.2026 01:00»
Редактор у второго пользователя Открывается с =100+50
Второй пользователь перезаписывает ячейку числом 200 Маркер пропал у обоих
Перезагрузка страницы после =10*3 Маркер на месте, редактор открывается с =10*3
Ключи formula_* в localStorage Нет
Ввод 100+50 без «=» Подсказка «Формула начинается с =», на сервер ничего не уходит
Отчёт проекта, =7*6 42,0 и fx у автора и во второй вкладке, подсказка с автором
Таблица после слияния с #240 (Cell, tableColumns) Работает, ошибок нет
GET …/formulas для формы и отчёта Отдают сохранённые формулы с автором и временем

Наблюдения:

  • O-4 (deferred). Подсказка появляется только при наведении на сам значок fx (около 8×9 px), а не на всю ячейку. На усмотрение фронта.
  • В консоли есть React-предупреждение value prop on textarea should not be null при открытии пустой ячейки. Оно было и до PR: пустое значение передавалось в редактор как null и раньше.
  • T7.3 (наложение fx на текст) не подтвердилась: формулы живут в числовых колонках, числа выровнены вправо.

Миграция

Переименована в 0061_cell_formula поверх 0060_form_phase_direction_fix из #315 (коммит 38036b3). Порядок мержа: #315 → #317 → #319 → #323.

🤖 Generated with Claude Code

## Проверка в браузере (12.11) Проверено локально: API и фронт из этой ветки, чистая БД с сидом. - **Форма:** FORM_1, лист АХР, направление «Поддержка». Две изолированные сессии браузера: admin (автор) и isp2 (второй пользователь). - **Отчёт проекта:** «Лимиты», две вкладки. | Сценарий | Результат | |---|---| | Автор вводит `=100+50` | В ячейке 150,0 и маркер `fx` в левом нижнем углу | | Второй пользователь без перезагрузки | Видит 150,0 и `fx`, подсказка «=100+50 · admin · 12.11.2026 01:00» | | Редактор у второго пользователя | Открывается с `=100+50` | | Второй пользователь перезаписывает ячейку числом 200 | Маркер пропал у обоих | | Перезагрузка страницы после `=10*3` | Маркер на месте, редактор открывается с `=10*3` | | Ключи `formula_*` в localStorage | Нет | | Ввод `100+50` без «=» | Подсказка «Формула начинается с =», на сервер ничего не уходит | | Отчёт проекта, `=7*6` | 42,0 и `fx` у автора и во второй вкладке, подсказка с автором | | Таблица после слияния с #240 (`Cell`, `tableColumns`) | Работает, ошибок нет | | GET `…/formulas` для формы и отчёта | Отдают сохранённые формулы с автором и временем | Наблюдения: - **O-4 (deferred).** Подсказка появляется только при наведении на сам значок `fx` (около 8×9 px), а не на всю ячейку. На усмотрение фронта. - В консоли есть React-предупреждение `value prop on textarea should not be null` при открытии пустой ячейки. Оно было и до PR: пустое значение передавалось в редактор как null и раньше. - T7.3 (наложение `fx` на текст) не подтвердилась: формулы живут в числовых колонках, числа выровнены вправо. ## Миграция Переименована в `0061_cell_formula` поверх `0060_form_phase_direction_fix` из #315 (коммит 38036b3). **Порядок мержа: #315 → #317 → #319 → #323.** 🤖 Generated with [Claude Code](https://claude.com/claude-code)
tsygankoviva approved these changes 2026-11-12 14:25:28 +03:00
aep merged commit 2b780f5267 into test 2026-11-12 17:03:43 +03:00
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RSHB/DFiP_Budget_planing#323
No description provided.