fix(form2): фонд экономии на FORM_2 вычисляемый, не редактируется в UI (AURORA-1938, #335)
isComputedColumn/isCellEditable принимают formType; transfer_econ вычисляемый только на FORM_2, на FORM_4 остаётся редактируемым. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
24062ed729
commit
9c8871139c
@ -214,8 +214,9 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
|||||||
columnId,
|
columnId,
|
||||||
isAdmin: isAdminUser,
|
isAdmin: isAdminUser,
|
||||||
stageColumns: stageColumnsSet,
|
stageColumns: stageColumnsSet,
|
||||||
|
formType,
|
||||||
}),
|
}),
|
||||||
[isAdminUser, stageColumnsSet],
|
[isAdminUser, stageColumnsSet, formType],
|
||||||
);
|
);
|
||||||
|
|
||||||
// AURORA-1891: cellIsEditable(row, columnId) не знает про сам column.def —
|
// AURORA-1891: cellIsEditable(row, columnId) не знает про сам column.def —
|
||||||
@ -371,12 +372,13 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
|||||||
getCellErrors,
|
getCellErrors,
|
||||||
denomination,
|
denomination,
|
||||||
sheetName,
|
sheetName,
|
||||||
|
formType,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error creating columns:', error);
|
console.error('Error creating columns:', error);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}, [columnsConfig, columnsCurStage, userRoleId, handleCellUpdateError, handleClickRowCell, handleUpdateCell, getCellErrors, denomination, sheetName]);
|
}, [columnsConfig, columnsCurStage, userRoleId, handleCellUpdateError, handleClickRowCell, handleUpdateCell, getCellErrors, denomination, sheetName, formType]);
|
||||||
|
|
||||||
// Мемоизация ячеек MRT (memoMode: 'cells') сравнивает только объект cell. Состояние вне данных строки,
|
// Мемоизация ячеек MRT (memoMode: 'cells') сравнивает только объект cell. Состояние вне данных строки,
|
||||||
// от которого зависят стили закрепления и подсветка фильтров, сбрасывает её новым массивом колонок.
|
// от которого зависят стили закрепления и подсветка фильтров, сбрасывает её новым массивом колонок.
|
||||||
|
|||||||
@ -126,6 +126,7 @@ export const getTableColumns = ({
|
|||||||
getCellErrors,
|
getCellErrors,
|
||||||
denomination = 'thousand',
|
denomination = 'thousand',
|
||||||
sheetName,
|
sheetName,
|
||||||
|
formType,
|
||||||
}) => {
|
}) => {
|
||||||
const columnColors = { ...columnsConfig.colors };
|
const columnColors = { ...columnsConfig.colors };
|
||||||
const columns = structuredClone(columnsConfig.columns);
|
const columns = structuredClone(columnsConfig.columns);
|
||||||
@ -159,6 +160,7 @@ export const getTableColumns = ({
|
|||||||
columnId,
|
columnId,
|
||||||
isAdmin: userRoleId === ROLES_NAME_ID.admin,
|
isAdmin: userRoleId === ROLES_NAME_ID.admin,
|
||||||
stageColumns,
|
stageColumns,
|
||||||
|
formType,
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -62,7 +62,7 @@ const DATA_PREFIX = 'data.';
|
|||||||
* OTCH9F). columnId — это React Table column.id в формате accessorKey, т.е.
|
* OTCH9F). columnId — это React Table column.id в формате accessorKey, т.е.
|
||||||
* всегда с префиксом "data." для полей строки (например "data.q1.booking").
|
* всегда с префиксом "data." для полей строки (например "data.q1.booking").
|
||||||
*/
|
*/
|
||||||
export const isComputedColumn = (columnId) => {
|
export const isComputedColumn = (columnId, formType) => {
|
||||||
if (typeof columnId !== 'string' || !columnId.startsWith(DATA_PREFIX)) return false;
|
if (typeof columnId !== 'string' || !columnId.startsWith(DATA_PREFIX)) return false;
|
||||||
|
|
||||||
const parts = columnId.slice(DATA_PREFIX.length).split('.');
|
const parts = columnId.slice(DATA_PREFIX.length).split('.');
|
||||||
@ -81,6 +81,11 @@ export const isComputedColumn = (columnId) => {
|
|||||||
// qN.<field> из списка вычисляемых полей квартала.
|
// qN.<field> из списка вычисляемых полей квартала.
|
||||||
if (QUARTER_SCOPE_PATTERN.test(scope) && QUARTER_COMPUTED_FIELDS.has(field)) return true;
|
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.
|
// fact_qN.total — сателлиты AHR_RENT/AHR_UTILITY/AHR_SECURITY.
|
||||||
if (FACT_QUARTER_SCOPE_PATTERN.test(scope) && field === 'total') return true;
|
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.<column>" (columnsCurStage из
|
* stageColumns — Set с ключами вида "data.<column>" (columnsCurStage из
|
||||||
* useRealtimeData, отражающий окно текущего этапа пользователя).
|
* useRealtimeData, отражающий окно текущего этапа пользователя).
|
||||||
*/
|
*/
|
||||||
export const isCellEditable = ({ rowType, columnId, isAdmin, stageColumns }) =>
|
export const isCellEditable = ({ rowType, columnId, isAdmin, stageColumns, formType }) =>
|
||||||
rowType === 'INPUT' && !isComputedColumn(columnId) && (isAdmin || stageColumns.has(columnId));
|
rowType === 'INPUT' && !isComputedColumn(columnId, formType) && (isAdmin || stageColumns.has(columnId));
|
||||||
|
|||||||
@ -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(undefined), false);
|
||||||
assert.equal(isComputedColumn(null), 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,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user