From 9c8871139c41f796def41f50cbb459336f9dfc2e Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Thu, 12 Nov 2026 16:05:21 +0400 Subject: [PATCH] =?UTF-8?q?fix(form2):=20=D1=84=D0=BE=D0=BD=D0=B4=20=D1=8D?= =?UTF-8?q?=D0=BA=D0=BE=D0=BD=D0=BE=D0=BC=D0=B8=D0=B8=20=D0=BD=D0=B0=20FOR?= =?UTF-8?q?M=5F2=20=D0=B2=D1=8B=D1=87=D0=B8=D1=81=D0=BB=D1=8F=D0=B5=D0=BC?= =?UTF-8?q?=D1=8B=D0=B9,=20=D0=BD=D0=B5=20=D1=80=D0=B5=D0=B4=D0=B0=D0=BA?= =?UTF-8?q?=D1=82=D0=B8=D1=80=D1=83=D0=B5=D1=82=D1=81=D1=8F=20=D0=B2=20UI?= =?UTF-8?q?=20(AURORA-1938,=20#335)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit isComputedColumn/isCellEditable принимают formType; transfer_econ вычисляемый только на FORM_2, на FORM_4 остаётся редактируемым. Co-Authored-By: Claude Opus 5.5 --- .../RealtimeTable/RealtimeTable.jsx | 6 ++-- .../components/RealtimeTable/tableColumns.jsx | 2 ++ .../RealtimeTable/utils/cellEditability.js | 15 ++++++---- .../utils/cellEditability.test.js | 29 +++++++++++++++++++ 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/web/src/components/RealtimeTable/RealtimeTable.jsx b/web/src/components/RealtimeTable/RealtimeTable.jsx index 93e456d..e6ec364 100644 --- a/web/src/components/RealtimeTable/RealtimeTable.jsx +++ b/web/src/components/RealtimeTable/RealtimeTable.jsx @@ -214,8 +214,9 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => { columnId, isAdmin: isAdminUser, stageColumns: stageColumnsSet, + formType, }), - [isAdminUser, stageColumnsSet], + [isAdminUser, stageColumnsSet, formType], ); // AURORA-1891: cellIsEditable(row, columnId) не знает про сам column.def — @@ -371,12 +372,13 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => { getCellErrors, denomination, sheetName, + formType, }); } catch (error) { console.error('Error creating columns:', error); return []; } - }, [columnsConfig, columnsCurStage, userRoleId, handleCellUpdateError, handleClickRowCell, handleUpdateCell, getCellErrors, denomination, sheetName]); + }, [columnsConfig, columnsCurStage, userRoleId, handleCellUpdateError, handleClickRowCell, handleUpdateCell, getCellErrors, denomination, sheetName, formType]); // Мемоизация ячеек MRT (memoMode: 'cells') сравнивает только объект cell. Состояние вне данных строки, // от которого зависят стили закрепления и подсветка фильтров, сбрасывает её новым массивом колонок. diff --git a/web/src/components/RealtimeTable/tableColumns.jsx b/web/src/components/RealtimeTable/tableColumns.jsx index 5cbece6..c16eb6b 100644 --- a/web/src/components/RealtimeTable/tableColumns.jsx +++ b/web/src/components/RealtimeTable/tableColumns.jsx @@ -126,6 +126,7 @@ export const getTableColumns = ({ getCellErrors, denomination = 'thousand', sheetName, + formType, }) => { const columnColors = { ...columnsConfig.colors }; const columns = structuredClone(columnsConfig.columns); @@ -159,6 +160,7 @@ export const getTableColumns = ({ columnId, isAdmin: userRoleId === ROLES_NAME_ID.admin, stageColumns, + formType, }) ) { return false; diff --git a/web/src/components/RealtimeTable/utils/cellEditability.js b/web/src/components/RealtimeTable/utils/cellEditability.js index 36efce4..07d12ba 100644 --- a/web/src/components/RealtimeTable/utils/cellEditability.js +++ b/web/src/components/RealtimeTable/utils/cellEditability.js @@ -62,7 +62,7 @@ const DATA_PREFIX = 'data.'; * OTCH9F). columnId — это React Table column.id в формате accessorKey, т.е. * всегда с префиксом "data." для полей строки (например "data.q1.booking"). */ -export const isComputedColumn = (columnId) => { +export const isComputedColumn = (columnId, formType) => { if (typeof columnId !== 'string' || !columnId.startsWith(DATA_PREFIX)) return false; const parts = columnId.slice(DATA_PREFIX.length).split('.'); @@ -81,6 +81,11 @@ export const isComputedColumn = (columnId) => { // qN. из списка вычисляемых полей квартала. if (QUARTER_SCOPE_PATTERN.test(scope) && QUARTER_COMPUTED_FIELDS.has(field)) return true; + // AURORA-1938: на FORM_2 («Смета РФ») перенос в фонд экономии считается на сервере + // (миграция 0063; _apply_form_cell отвергает запись для FORM_2). На FORM_4 + // («Проектная деятельность») transfer_econ — обычное редактируемое поле. + if (formType === 'FORM_2' && QUARTER_SCOPE_PATTERN.test(scope) && field === 'transfer_econ') return true; + // fact_qN.total — сателлиты AHR_RENT/AHR_UTILITY/AHR_SECURITY. if (FACT_QUARTER_SCOPE_PATTERN.test(scope) && field === 'total') return true; @@ -96,12 +101,12 @@ export const isComputedColumn = (columnId) => { }; /** - * isCellEditable({ rowType, columnId, isAdmin, stageColumns }) + * isCellEditable({ rowType, columnId, isAdmin, stageColumns, formType }) * - * rowType === 'INPUT' && !isComputedColumn(columnId) && (isAdmin || stageColumns.has(columnId)) + * rowType === 'INPUT' && !isComputedColumn(columnId, formType) && (isAdmin || stageColumns.has(columnId)) * * stageColumns — Set с ключами вида "data." (columnsCurStage из * useRealtimeData, отражающий окно текущего этапа пользователя). */ -export const isCellEditable = ({ rowType, columnId, isAdmin, stageColumns }) => - rowType === 'INPUT' && !isComputedColumn(columnId) && (isAdmin || stageColumns.has(columnId)); +export const isCellEditable = ({ rowType, columnId, isAdmin, stageColumns, formType }) => + rowType === 'INPUT' && !isComputedColumn(columnId, formType) && (isAdmin || stageColumns.has(columnId)); diff --git a/web/src/components/RealtimeTable/utils/cellEditability.test.js b/web/src/components/RealtimeTable/utils/cellEditability.test.js index 618a710..8d676d6 100644 --- a/web/src/components/RealtimeTable/utils/cellEditability.test.js +++ b/web/src/components/RealtimeTable/utils/cellEditability.test.js @@ -144,3 +144,32 @@ test('plain non-"data." column ids (row number, grouping headers) are never comp assert.equal(isComputedColumn(undefined), false); assert.equal(isComputedColumn(null), false); }); + +test('AURORA-1938: transfer_econ is computed on FORM_2 only (FORM_4 keeps it editable)', () => { + for (const q of ['q1', 'q2', 'q3', 'q4']) { + const columnId = `data.${q}.transfer_econ`; + assert.equal(isComputedColumn(columnId, 'FORM_2'), true, `${columnId} must be computed on FORM_2`); + assert.equal(isComputedColumn(columnId, 'FORM_4'), false, `${columnId} must stay editable on FORM_4`); + assert.equal(isComputedColumn(columnId), false); + } + assert.equal( + isCellEditable({ + rowType: 'INPUT', + columnId: 'data.q1.transfer_econ', + isAdmin: true, + stageColumns: new Set(['data.q1.transfer_econ']), + formType: 'FORM_2', + }), + false, + ); + assert.equal( + isCellEditable({ + rowType: 'INPUT', + columnId: 'data.q1.transfer_econ', + isAdmin: true, + stageColumns: new Set(), + formType: 'FORM_4', + }), + true, + ); +});