AURORA-1899: база разрядности — тыс. руб., масштабировать только денежные колонки #288

Merged
aep merged 2 commits from feat/285-aurora-1899-denomination-base-thousand into test 2026-11-02 20:45:42 +03:00
Owner

Проблема

Селектор разрядности (AURORA-1650, bce897f) считал базой данных рубли.
На самом деле суммы в базе хранятся в тыс. руб. (подписи «тыс. руб.» на
формах 1/4, «В тыс. рублей» на сателлитах и в Смете формы 2; после
AURORA-1897 — и лимитные строки АХР). Из-за этого:

  1. По умолчанию стояло «в руб.», и тысячи показывались под видом рублей;
    при выборе «в тыс. руб.» значения делились ещё раз (план 1 500 тыс.
    руб. показывался как 1,5).
  2. Делитель применялся не только к деньгам: к количествам (qty_q1..q4,
    *_qty), площади (rented_area), ставке НДС (vat_rate), курсу
    (exchange_rate), кодам (section_code, item_id, num_group...),
    внутреннему заказу, сумме в валюте (amount_foreign) — в основной
    таблице и в своде 2.
  3. Редактор ячейки (Cell/EditCell) не учитывал разрядность: при «в млн»
    в ячейке 1,5, а в редакторе — 1500.

Issue: #285.

Что сделано

  • web/src/components/RealtimeTable/utils/moneyUtils.js (новый модуль,
    без импорта конфигов форм — работает под node --test):
    • UNIT_IN_RUB, getDisplayFactor(baseUnit, denomination);
    • getMoneyUnit(columnDef, sheetName) → 'thousand' | 'rub' | null,
      с порядком проверки: явный columnDef.moneyUnit → TEXT_COLUMN_IDS/
      список не-денежных последних сегментов accessorKey → лист
      AHR_LIMIT (rub) → по умолчанию 'thousand';
    • TEXT_COLUMN_IDS перенесён из Cell.jsx (там теперь только импорт);
    • resolveDenomination/DEFAULT_DENOMINATION (пригодны к реюзу —
      используются useDenomination.js);
    • AHR_LIMIT_SHEET — общая константа для tableColumns.jsx и
      SettingPanel.jsx.
  • useDenomination.js: значение по умолчанию 'thousand'; ключ
    хранения — суффикс _denomination_v2 (старые сохранённые 'rub'/
    'thousand' больше не читаются со старым смыслом); getDenominationDivisor
    убран как неверный по смыслу (предполагал базу «рубли»).
  • tableColumns.jsx / Cell.jsx: displayDivisor заменён на
    displayFactor (умножение, не деление); множитель считается на
    колонку через getMoneyUnit/getDisplayFactor с учётом sheetName
    (проброшен из RealtimeTable.jsx); учтено в кеше (_hash,
    cached.displayFactor, memo-сравнение в Cell.jsx); isEditable
    запрещает редактирование денежной ячейки, если разрядность не базовая
    (factor !== 1); на листе AHR_LIMIT множитель всегда 1 независимо
    от сохранённой разрядности.
  • SettingPanel.jsx: селектор скрыт на листе AHR_LIMIT; рядом с
    селектором подсказка «Ввод сумм — в тыс. руб.».
  • SvodPage/SummaryTable.jsx, Svod2Page.jsx, ProjectSummariesPage.jsx:
    масштабирование по признаку meta.money вместо regex по id (свод 1,
    своды проектов) и вместо блок-листа текстовых id (свод 2, где он
    ошибочно задевал num_group/internal_order/vat_rate — они
    определены через textColumn и раньше тоже делились). Построители
    колонок (valueColumn/numericColumn) проставляют meta: { money: true }
    на денежных метриках.
  • DenominationSelect.jsx: «в млн руб.», «в млрд руб.» (без точки
    после «млн»/«млрд»).
  • Выгрузка в Excel не трогалась (подписи — отдельная задача AURORA-1900).

Тесты

  • Новый web/src/components/RealtimeTable/utils/moneyUtils.test.js
    (node --test): getDisplayFactor для всех пар единиц из примеров
    тикета; getMoneyUnit для всех примеров тикета (денежные/не-денежные
    колонки, AHR_LIMIT, явный moneyUnit в т.ч. null); дополнительно —
    суффикс *_qty, TEXT_COLUMN_IDS-кейсы вне общего списка,
    resolveDenomination, состав UNIT_IN_RUB, AHR_LIMIT_SHEET.
  • Полный набор cd web && node --test src/components/RealtimeTable/utils/*.test.js:
    52/52 зелёных (42 существовавших + 10 новых).
  • npm run build проходит без ошибок.
  • Ручная проверка по коду (как в тикете): форма 1, OPER, план 1 500 →
    «в тыс.» 1 500, «в млн» 1,5, «в руб.» 1 500 000; ставка НДС 20 всегда
    20; при «в млн» ячейка плана не открывает редактор, при «в тыс.» —
    открывает; свод 2: ID группы номенклатуры/внутренний заказ/ставка НДС
    не меняются при переключении; лист «АХР лимиты» — без селектора,
    количество и рубли без изменений.

Rulings

  • Стек веток: эта задача НЕ стекуется на AURORA-1897 (бэкенд, другая
    область файлов). Нужный результат AURORA-1897 (лимитные строки АХР в
    тыс. руб.) уже описан текстом самого тикета AURORA-1899 и учтён в
    getMoneyUnit как особый случай листа AHR_LIMIT. Код-зависимости от
    AURORA-1897 нет — решение сознательное, ветка создана заново от
    origin/test.
  • AHR_LIMIT и дефолт «тыс.»: на листе AHR_LIMIT селектор скрыт, и
    сохранённая разрядность для него никогда не меняется пользователем —
    она осталась бы на дефолтном 'thousand', а база листа — 'rub'.
    Без явного обхода это дало бы множитель 0.001 (тысячи делились бы
    ещё раз на 1000) и как побочный эффект заблокировало бы редактирование
    лимитов (мой же gate «фактор ≠ 1 → не редактируется»). Решение:
    в getCellDisplayFactor для листа AHR_LIMIT множитель всегда 1
    независимо от сохранённой/дефолтной разрядности — соответствует
    акцептансу «значения в рублях, как есть».
  • Запрет редактирования денежных ячеек при не-базовой разрядности
    применён и для администратора
    (до проверки userRoleId === admin),
    а не только для обычных ролей по этапам: искажённое значение в ячейке
    не должно быть точкой записи независимо от роли — это защита от
    опечатки в данных, не от доступа.
  • getDenominationDivisor и поле divisor хука useDenomination
    удалены
    , а не оставлены для обратной совместимости: оба явно
    предполагали базу «рубли» — именно это и было источником багов 1–2 из
    тикета. Единственные потребители (SummaryTable.jsx, Svod2Page.jsx)
    переведены на getDisplayFactor('thousand', denomination).
  • Ключи localStorage для всех мест с селектором разрядности (основная
    таблица, свод 1/своды проектов, свод 2) получили суффикс _v2, не
    только основная таблица — старые сохранённые значения 'rub'/
    'thousand' имели другой смысл везде одинаково.

🤖 Generated with Claude Code

## Проблема Селектор разрядности (AURORA-1650, bce897f) считал базой данных рубли. На самом деле суммы в базе хранятся в тыс. руб. (подписи «тыс. руб.» на формах 1/4, «В тыс. рублей» на сателлитах и в Смете формы 2; после AURORA-1897 — и лимитные строки АХР). Из-за этого: 1. По умолчанию стояло «в руб.», и тысячи показывались под видом рублей; при выборе «в тыс. руб.» значения делились ещё раз (план 1 500 тыс. руб. показывался как 1,5). 2. Делитель применялся не только к деньгам: к количествам (`qty_q1..q4`, `*_qty`), площади (`rented_area`), ставке НДС (`vat_rate`), курсу (`exchange_rate`), кодам (`section_code`, `item_id`, `num_group`...), внутреннему заказу, сумме в валюте (`amount_foreign`) — в основной таблице и в своде 2. 3. Редактор ячейки (Cell/EditCell) не учитывал разрядность: при «в млн» в ячейке 1,5, а в редакторе — 1500. Issue: #285. ## Что сделано - **`web/src/components/RealtimeTable/utils/moneyUtils.js`** (новый модуль, без импорта конфигов форм — работает под `node --test`): - `UNIT_IN_RUB`, `getDisplayFactor(baseUnit, denomination)`; - `getMoneyUnit(columnDef, sheetName)` → `'thousand' | 'rub' | null`, с порядком проверки: явный `columnDef.moneyUnit` → `TEXT_COLUMN_IDS`/ список не-денежных последних сегментов `accessorKey` → лист `AHR_LIMIT` (`rub`) → по умолчанию `'thousand'`; - `TEXT_COLUMN_IDS` перенесён из `Cell.jsx` (там теперь только импорт); - `resolveDenomination`/`DEFAULT_DENOMINATION` (пригодны к реюзу — используются `useDenomination.js`); - `AHR_LIMIT_SHEET` — общая константа для `tableColumns.jsx` и `SettingPanel.jsx`. - **`useDenomination.js`**: значение по умолчанию `'thousand'`; ключ хранения — суффикс `_denomination_v2` (старые сохранённые `'rub'`/ `'thousand'` больше не читаются со старым смыслом); `getDenominationDivisor` убран как неверный по смыслу (предполагал базу «рубли»). - **`tableColumns.jsx` / `Cell.jsx`**: `displayDivisor` заменён на `displayFactor` (умножение, не деление); множитель считается на колонку через `getMoneyUnit`/`getDisplayFactor` с учётом `sheetName` (проброшен из `RealtimeTable.jsx`); учтено в кеше (`_hash`, `cached.displayFactor`, memo-сравнение в `Cell.jsx`); `isEditable` запрещает редактирование денежной ячейки, если разрядность не базовая (`factor !== 1`); на листе `AHR_LIMIT` множитель всегда `1` независимо от сохранённой разрядности. - **`SettingPanel.jsx`**: селектор скрыт на листе `AHR_LIMIT`; рядом с селектором подсказка «Ввод сумм — в тыс. руб.». - **`SvodPage/SummaryTable.jsx`, `Svod2Page.jsx`, `ProjectSummariesPage.jsx`**: масштабирование по признаку `meta.money` вместо regex по id (свод 1, своды проектов) и вместо блок-листа текстовых id (свод 2, где он ошибочно задевал `num_group`/`internal_order`/`vat_rate` — они определены через `textColumn` и раньше тоже делились). Построители колонок (`valueColumn`/`numericColumn`) проставляют `meta: { money: true }` на денежных метриках. - **`DenominationSelect.jsx`**: «в млн руб.», «в млрд руб.» (без точки после «млн»/«млрд»). - Выгрузка в Excel не трогалась (подписи — отдельная задача AURORA-1900). ## Тесты - Новый `web/src/components/RealtimeTable/utils/moneyUtils.test.js` (`node --test`): `getDisplayFactor` для всех пар единиц из примеров тикета; `getMoneyUnit` для всех примеров тикета (денежные/не-денежные колонки, `AHR_LIMIT`, явный `moneyUnit` в т.ч. `null`); дополнительно — суффикс `*_qty`, `TEXT_COLUMN_IDS`-кейсы вне общего списка, `resolveDenomination`, состав `UNIT_IN_RUB`, `AHR_LIMIT_SHEET`. - Полный набор `cd web && node --test src/components/RealtimeTable/utils/*.test.js`: **52/52 зелёных** (42 существовавших + 10 новых). - `npm run build` проходит без ошибок. - Ручная проверка по коду (как в тикете): форма 1, OPER, план 1 500 → «в тыс.» 1 500, «в млн» 1,5, «в руб.» 1 500 000; ставка НДС 20 всегда 20; при «в млн» ячейка плана не открывает редактор, при «в тыс.» — открывает; свод 2: ID группы номенклатуры/внутренний заказ/ставка НДС не меняются при переключении; лист «АХР лимиты» — без селектора, количество и рубли без изменений. ## Rulings - **Стек веток**: эта задача НЕ стекуется на AURORA-1897 (бэкенд, другая область файлов). Нужный результат AURORA-1897 (лимитные строки АХР в тыс. руб.) уже описан текстом самого тикета AURORA-1899 и учтён в `getMoneyUnit` как особый случай листа `AHR_LIMIT`. Код-зависимости от AURORA-1897 нет — решение сознательное, ветка создана заново от `origin/test`. - **AHR_LIMIT и дефолт «тыс.»**: на листе `AHR_LIMIT` селектор скрыт, и сохранённая разрядность для него никогда не меняется пользователем — она осталась бы на дефолтном `'thousand'`, а база листа — `'rub'`. Без явного обхода это дало бы множитель `0.001` (тысячи делились бы ещё раз на 1000) и как побочный эффект заблокировало бы редактирование лимитов (мой же gate «фактор ≠ 1 → не редактируется»). Решение: в `getCellDisplayFactor` для листа `AHR_LIMIT` множитель всегда `1` независимо от сохранённой/дефолтной разрядности — соответствует акцептансу «значения в рублях, как есть». - **Запрет редактирования денежных ячеек при не-базовой разрядности применён и для администратора** (до проверки `userRoleId === admin`), а не только для обычных ролей по этапам: искажённое значение в ячейке не должно быть точкой записи независимо от роли — это защита от опечатки в данных, не от доступа. - **`getDenominationDivisor` и поле `divisor` хука `useDenomination` удалены**, а не оставлены для обратной совместимости: оба явно предполагали базу «рубли» — именно это и было источником багов 1–2 из тикета. Единственные потребители (`SummaryTable.jsx`, `Svod2Page.jsx`) переведены на `getDisplayFactor('thousand', denomination)`. - Ключи `localStorage` для всех мест с селектором разрядности (основная таблица, свод 1/своды проектов, свод 2) получили суффикс `_v2`, не только основная таблица — старые сохранённые значения `'rub'`/ `'thousand'` имели другой смысл везде одинаково. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
aep added 1 commit 2026-11-01 15:09:16 +03:00
Селектор разрядности (AURORA-1650) считал базой рубли, хотя в базе суммы
хранятся в тыс. руб. (формы 1/4, сателлиты формы 2, лимитные строки АХР
после AURORA-1897). По умолчанию стояло «в руб.», и тысячи показывались
под видом рублей; при выборе «в тыс. руб.» значения делились ещё раз.
Делитель также применялся к количествам, площади, ставке НДС, курсу,
кодам и ID — не только к деньгам. Редактор ячейки не учитывал разрядность.

- web/src/components/RealtimeTable/utils/moneyUtils.js — новый модуль:
  UNIT_IN_RUB, getDisplayFactor(baseUnit, denomination), getMoneyUnit
  (денежная единица колонки по accessorKey/sheetName, с явным
  columnDef.moneyUnit как переопределением), TEXT_COLUMN_IDS (перенесён
  из Cell.jsx), resolveDenomination/DEFAULT_DENOMINATION, AHR_LIMIT_SHEET.
  Модуль не импортирует конфиги форм — работает под node --test.
- useDenomination.js — значение по умолчанию 'thousand', ключ хранения
  со суффиксом _denomination_v2 (старые 'rub'/'thousand' больше не
  читаются со старым смыслом); getDenominationDivisor убран.
- tableColumns.jsx/Cell.jsx — displayDivisor заменён на displayFactor
  (умножение, не деление), множитель считается на колонку через
  getMoneyUnit/getDisplayFactor с учётом sheetName; isEditable запрещает
  редактирование денежной ячейки, если разрядность не базовая; на листе
  AHR_LIMIT множитель всегда 1 (значения в рублях, как есть).
- SettingPanel.jsx — селектор скрыт на листе AHR_LIMIT, рядом с ним
  подсказка «Ввод сумм — в тыс. руб.».
- SvodPage/Svod2Page/ProjectSummariesPage — масштабирование по
  meta.money вместо regex по id/блок-листа текстовых id; построители
  колонок проставляют meta.money на денежных метриках.
- DenominationSelect.jsx — «в млн руб.», «в млрд руб.» без точки.

Тесты: web/src/components/RealtimeTable/utils/moneyUtils.test.js
(node --test), полный набор web/src/components/RealtimeTable/utils —
52/52 зелёных. npm run build проходит.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
PotapovaA approved these changes 2026-11-02 17:12:52 +03:00
aep added 1 commit 2026-11-02 20:45:21 +03:00
# Conflicts:
#	web/src/components/RealtimeTable/Cell/Cell/Cell.jsx
aep merged commit c55a14749a into test 2026-11-02 20:45:42 +03:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RSHB/DFiP_Budget_planing#288
No description provided.