AURORA-1895: формула из localStorage не перезаписывает чужие значения #267

Open
aep wants to merge 1 commits from feat/264-aurora-1895-formula-localstorage into test
Owner

Summary

  • Исправляет потерю данных из AURORA-1895: формула, сохранённая в localStorage автора ячейки, подставлялась при повторном открытии ячейки без сверки с текущим значением и могла молча перезаписать чужое изменение, а также переезжать на другую строку после вставки/удаления строк (ключ строился от позиционного cell.row.id, а не от line_id).
  • Открытие и закрытие ячейки без правки больше не отправляет cell_updated (ни по Enter, ни по клику вне ячейки).
  • Сохранённая формула подставляется только если calculateFormula(expr) совпадает с текущим значением ячейки (допуск 1e-9); иначе формула помечается устаревшей (stale), удаляется из localStorage, и в ячейке показывается актуальное значение.
  • Ключ формулы в localStorage переведён с formula_${tableId}_${cellId} (cellId строился от позиции строки в дереве) на formula_v2_${tableId}_${getRowId(row)_...} (от line_id/id строки). При монтировании RealtimeTable выполняется разовая очистка устаревших ключей formula_* без суффикса _v2.
  • Если введённая формула не вычисляется (#ОШИБКА), на сервер она не отправляется: показывается toast, редактор остаётся открытым.
  • Новая чистая логика вынесена в web/src/components/RealtimeTable/utils/formulaUtils.js: resolveEditorState(savedFormula, value) и isSameCellValue(a, b), покрыты юнит-тестами.

Это временная мера до AURORA-1896 (серверное хранение формул), которая полностью заменит localStorage.

Closes #264.

Test plan

  • TDD: web/src/components/RealtimeTable/utils/formulaUtils.test.js написан до реализации, наблюдал RED (отсутствующие экспорты) перед GREEN.
  • cd web && node --test src/components/RealtimeTable/utils/*.test.js — 9/9 новых тестов зелёные; rowUtils.test.js (5/5) и headerYearUtils.test.js (4/4) из того же набора тоже зелёные.
  • columnLayoutUtils.test.js в этом прогоне падает с ERR_MODULE_NOT_FOUND: @tanstack/table-core — в этом worktree не выполнен npm install (web/node_modules отсутствует полностью). Не связано с этим изменением; воспроизводится и на чистом HEAD без моих правок.
  • npx biome check по изменённым файлам точечно сверен с baseline (git show HEAD:<file> через biome на тот же путь): новых ошибок не добавлено. EditCell.jsx и formulaUtils.test.js — 0 ошибок/0 новых предупреждений. formulaUtils.js — та же 1 pre-existing ошибка (noGlobalIsFinite, непотронутая строка) и те же 2 предупреждения, что и на baseline. tableColumns.jsx/RealtimeTable.jsx — те же 4 pre-existing ошибки format/organizeImports, что и на baseline (весь web/src уже не проходит biome check на базовой ветке: 318 ошибок/87 предупреждений до этого PR). localStorageUtils.js — на 1 предупреждение больше (console.error в новой clearLegacyFormulaKeys, в стиле остальных функций файла).
  • Живая проверка в двух браузерах не выполнялась (нет окружения) — как и указано в самом тикете: «Найдено по коду, вживую не воспроизводилось».

Rulings I made

  • Тикет не указывал, использовать ли уже существующий calculateFormula или писать новый — в кодовой базе он уже был в formulaUtils.js (поддержка + - * / % ()), поэтому resolveEditorState переиспользует его как есть, без изменений.
  • Для вычисления displayValue при подстановке формулы в EditCell добился избежать расширения сигнатуры resolveEditorState сверх трёх полей, заданных в тикете (currentValue, isFormulaMode, stale): отображаемое значение формулы пересчитывается локальным хелпером computeFormulaDisplay внутри EditCell.jsx, а не добавлением четвёртого поля в возвращаемый объект resolveEditorState.
  • Функция разовой очистки устаревших ключей formula_* (clearLegacyFormulaKeys) размещена в localStorageUtils.js (а не в formulaUtils.js) и запускается в useEffect(() => {...}, []) при каждом монтировании RealtimeTable — тикет не указывал точный модуль/механизм "один раз"; после первой фактической очистки в браузере повторные вызовы находят 0 устаревших ключей, так что итоговый эффект соответствует требованию "один раз" без отдельного флага в localStorage.
  • Если введённая формула не вычисляется при выходе из ячейки (finishEditing), я не закрываю редактор ни по Enter, ни по клику вне ячейки — оба пути идут через общий handleSave, который возвращает false и не даёт finishEditing продолжить. Тикет описывал это как пункт 5 без указания, должен ли путь закрытия по Enter вести себя иначе от клика вне ячейки — сделал их одинаковыми.
## Summary - Исправляет потерю данных из AURORA-1895: формула, сохранённая в localStorage автора ячейки, подставлялась при повторном открытии ячейки без сверки с текущим значением и могла молча перезаписать чужое изменение, а также переезжать на другую строку после вставки/удаления строк (ключ строился от позиционного `cell.row.id`, а не от `line_id`). - Открытие и закрытие ячейки без правки больше не отправляет `cell_updated` (ни по Enter, ни по клику вне ячейки). - Сохранённая формула подставляется только если `calculateFormula(expr)` совпадает с текущим значением ячейки (допуск `1e-9`); иначе формула помечается устаревшей (stale), удаляется из localStorage, и в ячейке показывается актуальное значение. - Ключ формулы в localStorage переведён с `formula_${tableId}_${cellId}` (cellId строился от позиции строки в дереве) на `formula_v2_${tableId}_${getRowId(row)_...}` (от `line_id`/`id` строки). При монтировании `RealtimeTable` выполняется разовая очистка устаревших ключей `formula_*` без суффикса `_v2`. - Если введённая формула не вычисляется (`#ОШИБКА`), на сервер она не отправляется: показывается toast, редактор остаётся открытым. - Новая чистая логика вынесена в `web/src/components/RealtimeTable/utils/formulaUtils.js`: `resolveEditorState(savedFormula, value)` и `isSameCellValue(a, b)`, покрыты юнит-тестами. Это временная мера до AURORA-1896 (серверное хранение формул), которая полностью заменит localStorage. Closes #264. ## Test plan - [x] TDD: `web/src/components/RealtimeTable/utils/formulaUtils.test.js` написан до реализации, наблюдал RED (отсутствующие экспорты) перед GREEN. - [x] `cd web && node --test src/components/RealtimeTable/utils/*.test.js` — 9/9 новых тестов зелёные; `rowUtils.test.js` (5/5) и `headerYearUtils.test.js` (4/4) из того же набора тоже зелёные. - [ ] `columnLayoutUtils.test.js` в этом прогоне падает с `ERR_MODULE_NOT_FOUND: @tanstack/table-core` — в этом worktree не выполнен `npm install` (`web/node_modules` отсутствует полностью). Не связано с этим изменением; воспроизводится и на чистом HEAD без моих правок. - [x] `npx biome check` по изменённым файлам точечно сверен с baseline (`git show HEAD:<file>` через biome на тот же путь): новых ошибок не добавлено. `EditCell.jsx` и `formulaUtils.test.js` — 0 ошибок/0 новых предупреждений. `formulaUtils.js` — та же 1 pre-existing ошибка (`noGlobalIsFinite`, непотронутая строка) и те же 2 предупреждения, что и на baseline. `tableColumns.jsx`/`RealtimeTable.jsx` — те же 4 pre-existing ошибки format/organizeImports, что и на baseline (весь `web/src` уже не проходит `biome check` на базовой ветке: 318 ошибок/87 предупреждений до этого PR). `localStorageUtils.js` — на 1 предупреждение больше (console.error в новой `clearLegacyFormulaKeys`, в стиле остальных функций файла). - [ ] Живая проверка в двух браузерах не выполнялась (нет окружения) — как и указано в самом тикете: «Найдено по коду, вживую не воспроизводилось». ## Rulings I made - Тикет не указывал, использовать ли уже существующий `calculateFormula` или писать новый — в кодовой базе он уже был в `formulaUtils.js` (поддержка `+ - * / % ()`), поэтому `resolveEditorState` переиспользует его как есть, без изменений. - Для вычисления `displayValue` при подстановке формулы в `EditCell` добился избежать расширения сигнатуры `resolveEditorState` сверх трёх полей, заданных в тикете (`currentValue`, `isFormulaMode`, `stale`): отображаемое значение формулы пересчитывается локальным хелпером `computeFormulaDisplay` внутри `EditCell.jsx`, а не добавлением четвёртого поля в возвращаемый объект `resolveEditorState`. - Функция разовой очистки устаревших ключей `formula_*` (`clearLegacyFormulaKeys`) размещена в `localStorageUtils.js` (а не в `formulaUtils.js`) и запускается в `useEffect(() => {...}, [])` при каждом монтировании `RealtimeTable` — тикет не указывал точный модуль/механизм "один раз"; после первой фактической очистки в браузере повторные вызовы находят 0 устаревших ключей, так что итоговый эффект соответствует требованию "один раз" без отдельного флага в localStorage. - Если введённая формула не вычисляется при выходе из ячейки (`finishEditing`), я не закрываю редактор ни по Enter, ни по клику вне ячейки — оба пути идут через общий `handleSave`, который возвращает `false` и не даёт `finishEditing` продолжить. Тикет описывал это как пункт 5 без указания, должен ли путь закрытия по Enter вести себя иначе от клика вне ячейки — сделал их одинаковыми.
aep added 1 commit 2026-10-31 13:12:17 +03:00
Пока AURORA-1896 не перенесёт формулы на сервер, временно исправляем
потерю данных в EditCell:

- resolveEditorState/isSameCellValue (formulaUtils.js) — чистые функции:
  формула подставляется в редактор только если её результат совпадает
  с текущим значением ячейки, иначе она устарела (stale) и удаляется
  из localStorage.
- EditCell: открытие и закрытие ячейки без правки больше не шлёт
  cell_updated (сверка с initialEditorValue при выходе из редактора;
  для обычных значений — дополнительно через isSameCellValue). Если
  введённая формула не вычисляется — toast и редактор остаётся открытым,
  на сервер ничего не уходит.
- tableColumns.jsx: ключ формулы строится от getRowId(cell.row) (line_id
  из данных), а не от cell.row.id (позиционный индекс-путь в дереве,
  переезжающий при вставке/удалении строк). Префикс ключа — formula_v2_.
- RealtimeTable: при монтировании once-очистка устаревших ключей
  formula_* без суффикса _v2 — они привязаны к позиции строки и стали
  недостоверны.

Тесты: web/src/components/RealtimeTable/utils/formulaUtils.test.js,
node --test — 9/9 новых + 5/5 ранее существующих в utils/ зелёные.

Closes #264.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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/264-aurora-1895-formula-localstorage:feat/264-aurora-1895-formula-localstorage
git checkout feat/264-aurora-1895-formula-localstorage
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#267
No description provided.