Селектор разрядности (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>
AURORA-1848 (#239/#242) убрал null-поля из GET листа/сводов (jsonb_strip_nulls),
оставив PATCH как раньше (задокументированный breaking-change в PR, не баг).
После слияния веток в test это разошлось с тестами, написанными до #239/#242:
- sheet_contract.assert_patch_rows_match_sheet сравнивал PATCH и GET побайтно —
добавлен strip_nulls() на стороне PATCH перед сравнением.
- test_form4_sheet_sums.py сравнивал ответ API с "сырым" v3.jsonb_sum (с null-полями)
и ожидал явный null в некоторых полях ответа — эталон теперь тоже прогоняется через
strip_nulls, а прямые ожидания null заменены на .get(...) is None / отсутствие ключа.
Без изменений контракта API: PATCH продолжает отдавать null явно, как в #242.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
После 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>
form4_ids вычисляет vsp_ids и project_ids из строк FORM_4 через .get
с пропуском отсутствующих ключей, grow_form4_sheet использует её.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
Четыре теста updateCells: вставка отсутствующего блока и поля,
явная запись null, вставка отсутствующего скалярного ключа data.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#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>
При слиянии 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>
План в 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>
Три независимых 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>