Merge pull request 'AURORA-1899: возврат тестирования — разрядность не трогает неденежные и текстовые колонки' (#308) from feat/285-aurora-1899-non-money-fix into test
This commit is contained in:
commit
dd40b9f341
@ -3,7 +3,7 @@ import React, { useMemo, useCallback, useState } from 'react';
|
||||
import { useCellLock } from '../../contexts/RealtimeContext';
|
||||
import { resolveCellTextColor } from '../../utils/cellTextColor';
|
||||
import { formatNumber } from '../../utils/numberFormat';
|
||||
import { TEXT_COLUMN_IDS } from '../../utils/moneyUtils';
|
||||
import { isTextColumn } from '../../utils/moneyUtils';
|
||||
|
||||
const BASE_CELL_STYLES = {
|
||||
width: '100%',
|
||||
@ -164,7 +164,7 @@ const CellComponent = ({
|
||||
|
||||
const { rawValue, displayValue, isNumeric, isNegative } = useMemo(() => {
|
||||
const value = cell.getValue();
|
||||
const isNum = value !== null && value !== undefined && value !== '' && !Number.isNaN(Number(value)) && !TEXT_COLUMN_IDS.has(column.id);
|
||||
const isNum = value !== null && value !== undefined && value !== '' && !Number.isNaN(Number(value)) && !isTextColumn(column);
|
||||
let display = String(value || '');
|
||||
let negative = false;
|
||||
if (isNum) {
|
||||
|
||||
@ -41,9 +41,9 @@ export const TEXT_COLUMN_IDS = new Set([
|
||||
'data.expense_item_code',
|
||||
]);
|
||||
|
||||
// Последний сегмент accessorKey (после последней точки), который никогда не денежный:
|
||||
// количества, ставки, курсы, коды, идентификаторы и т.п.
|
||||
const NON_MONEY_LAST_SEGMENTS = new Set([
|
||||
// Последний сегмент accessorKey (после последней точки) числовой, но не денежной колонки:
|
||||
// количества, площади, ставки, курсы, суммы в валюте. Форматируются как числа, не масштабируются.
|
||||
const NUMERIC_NON_MONEY_LAST_SEGMENTS = new Set([
|
||||
'qty_q1',
|
||||
'qty_q2',
|
||||
'qty_q3',
|
||||
@ -52,6 +52,13 @@ const NON_MONEY_LAST_SEGMENTS = new Set([
|
||||
'vat_rate',
|
||||
'exchange_rate',
|
||||
'amount_foreign',
|
||||
]);
|
||||
const NUMERIC_NON_MONEY_SUFFIXES = ['_qty'];
|
||||
|
||||
// Последний сегмент accessorKey текстовой колонки: коды, идентификаторы и свободный текст.
|
||||
// Не масштабируются и не форматируются как числа, даже если значение похоже на число
|
||||
// (внутренний заказ 10123, комментарий «123»).
|
||||
const TEXT_LAST_SEGMENTS = new Set([
|
||||
'section',
|
||||
'section_code',
|
||||
'section_no',
|
||||
@ -65,8 +72,70 @@ const NON_MONEY_LAST_SEGMENTS = new Set([
|
||||
'pay_year',
|
||||
'order',
|
||||
'expense_item_code',
|
||||
'id_stati',
|
||||
// Свободный текст (комментарии, обоснования, реквизиты договоров, даты, флаги).
|
||||
// Без них значение «123» в комментарии делилось на разрядность, а при разрядности
|
||||
// не «тыс.» текстовый редактор закрывался как денежный (возврат AURORA-1899, 11.11).
|
||||
// Комментарии с суффиксом *_comment ловит TEXT_SUFFIXES.
|
||||
'comment',
|
||||
'justification',
|
||||
'note',
|
||||
'name',
|
||||
'counterparty',
|
||||
'subject',
|
||||
'reference',
|
||||
'contract_ref',
|
||||
'deadline',
|
||||
'delivery_deadline',
|
||||
'date',
|
||||
'pay_date',
|
||||
'contract_end_date',
|
||||
'currency',
|
||||
'act',
|
||||
'pay_act',
|
||||
'scheme',
|
||||
'payment_scheme',
|
||||
'rf_schedule',
|
||||
'addenda',
|
||||
'proc_plan',
|
||||
'proc_method',
|
||||
'procurement_plan',
|
||||
'procurement_method',
|
||||
'property',
|
||||
'allocation_purpose',
|
||||
'protocol',
|
||||
'protocol_reference',
|
||||
'approved',
|
||||
'centralized_flag',
|
||||
'object_type',
|
||||
'unit',
|
||||
'address',
|
||||
'equipment_name',
|
||||
]);
|
||||
|
||||
const TEXT_SUFFIXES = ['_comment'];
|
||||
|
||||
const hasSuffix = (segment, suffixes) => suffixes.some((suffix) => segment.endsWith(suffix));
|
||||
|
||||
// И конфиг колонки, и MRT column: у последнего accessorKey и moneyUnit лежат в column.columnDef
|
||||
// (возврат AURORA-1899, 11.11: без этого любая колонка считалась денежной и НДС/курс/площадь
|
||||
// масштабировались).
|
||||
const toColumnDef = (column) => column?.columnDef ?? column;
|
||||
|
||||
/**
|
||||
* Текстовая колонка: значение показывается как есть, без числового форматирования
|
||||
* и без масштабирования разрядностью.
|
||||
*
|
||||
* @param {{ accessorKey?: string, columnDef?: { accessorKey?: string } }} column
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isTextColumn = (column) => {
|
||||
const accessorKey = toColumnDef(column)?.accessorKey;
|
||||
if (TEXT_COLUMN_IDS.has(accessorKey)) return true;
|
||||
const lastSegment = getLastSegment(accessorKey);
|
||||
return TEXT_LAST_SEGMENTS.has(lastSegment) || hasSuffix(lastSegment, TEXT_SUFFIXES);
|
||||
};
|
||||
|
||||
// Лист "АХР лимиты" (FORM_2/AHR_LIMIT) — нормативы хранятся в рублях, а не в тыс. руб.
|
||||
// У листа нет селектора разрядности (SettingPanel), значения показываются как есть.
|
||||
export const AHR_LIMIT_SHEET = 'AHR_LIMIT';
|
||||
@ -97,16 +166,18 @@ const getLastSegment = (accessorKey) => {
|
||||
* @param {string} [sheetName]
|
||||
* @returns {'thousand'|'rub'|null}
|
||||
*/
|
||||
export const getMoneyUnit = (columnDef, sheetName) => {
|
||||
export const getMoneyUnit = (column, sheetName) => {
|
||||
const columnDef = toColumnDef(column);
|
||||
if (columnDef && Object.hasOwn(columnDef, 'moneyUnit')) {
|
||||
return columnDef.moneyUnit;
|
||||
}
|
||||
|
||||
const accessorKey = columnDef?.accessorKey;
|
||||
if (TEXT_COLUMN_IDS.has(accessorKey)) return null;
|
||||
if (isTextColumn(columnDef)) return null;
|
||||
|
||||
const lastSegment = getLastSegment(accessorKey);
|
||||
if (lastSegment.endsWith('_qty') || NON_MONEY_LAST_SEGMENTS.has(lastSegment)) return null;
|
||||
const lastSegment = getLastSegment(columnDef?.accessorKey);
|
||||
if (NUMERIC_NON_MONEY_LAST_SEGMENTS.has(lastSegment) || hasSuffix(lastSegment, NUMERIC_NON_MONEY_SUFFIXES)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (sheetName === AHR_LIMIT_SHEET && AHR_LIMIT_RUB_LAST_SEGMENTS.has(lastSegment)) return 'rub';
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ import {
|
||||
UNIT_IN_RUB,
|
||||
getDisplayFactor,
|
||||
getMoneyUnit,
|
||||
isTextColumn,
|
||||
resolveDenomination,
|
||||
} from './moneyUtils.js';
|
||||
|
||||
@ -54,6 +55,94 @@ test('getMoneyUnit respects an explicit moneyUnit override, including null', ()
|
||||
assert.equal(getMoneyUnit({ accessorKey: 'data.qty_q1', moneyUnit: 'rub' }), 'rub');
|
||||
});
|
||||
|
||||
// AURORA-1899, возврат тестирования 11.11: tableColumns передаёт в расчёт множителя
|
||||
// MRT column (column.id + column.columnDef), у которого нет своего accessorKey.
|
||||
test('getMoneyUnit reads accessorKey from an MRT column object via columnDef', () => {
|
||||
const mrtColumn = (accessorKey) => ({ id: accessorKey, columnDef: { accessorKey } });
|
||||
assert.equal(getMoneyUnit(mrtColumn('data.contract_detail.vat_rate'), 'OPER'), null);
|
||||
assert.equal(getMoneyUnit(mrtColumn('data.contract_detail.exchange_rate'), 'OPER'), null);
|
||||
assert.equal(getMoneyUnit(mrtColumn('data.contract_detail.amount_foreign'), 'OPER'), null);
|
||||
assert.equal(getMoneyUnit(mrtColumn('data.header.internal_order'), 'OPER'), null);
|
||||
assert.equal(getMoneyUnit(mrtColumn('data.rented_area'), 'AHR_RENT'), null);
|
||||
assert.equal(getMoneyUnit(mrtColumn('data.plan.q1'), 'OPER'), 'thousand');
|
||||
assert.equal(getMoneyUnit(mrtColumn('data.amount_q1'), 'AHR_LIMIT'), 'rub');
|
||||
assert.equal(getMoneyUnit({ id: 'data.plan.q1', columnDef: { accessorKey: 'data.plan.q1', moneyUnit: null } }), null);
|
||||
});
|
||||
|
||||
test('getMoneyUnit returns null for text columns: comments, justifications and other free text', () => {
|
||||
for (const accessorKey of [
|
||||
'data.plan.comment',
|
||||
'data.q2.adj_comment',
|
||||
'data.q2.base_correction_comment',
|
||||
'data.q1.transfer_next_comment',
|
||||
'data.q1.transfer_far_comment',
|
||||
'data.seq_dfip.justification',
|
||||
'data.header.justification',
|
||||
'data.collegial.note',
|
||||
'data.header.name',
|
||||
'data.contract.counterparty',
|
||||
'data.contract.subject',
|
||||
'data.contract.reference',
|
||||
'data.contract.deadline',
|
||||
'data.contract.date',
|
||||
'data.contract.currency',
|
||||
'data.contract.act',
|
||||
'data.contract.scheme',
|
||||
'data.contract_detail.payment_scheme',
|
||||
'data.contract_detail.rf_schedule',
|
||||
'data.contract_detail.addenda',
|
||||
'data.ckk.proc_plan',
|
||||
'data.ckk.proc_method',
|
||||
'data.allocation.property',
|
||||
'data.collegial.protocol',
|
||||
'data.collegial.approved',
|
||||
'data.contract_summary.centralized_flag',
|
||||
'data.contract_end_date',
|
||||
'data.object_type',
|
||||
'data.unit',
|
||||
'data.address',
|
||||
]) {
|
||||
assert.equal(getMoneyUnit({ accessorKey }, 'OPER'), null, accessorKey);
|
||||
}
|
||||
});
|
||||
|
||||
// Возврат 11.11: внутренний заказ 10123 и комментарий «123» ожидаются как исходный текст,
|
||||
// а не «10 123,0» / «123,0»; ставки и количества остаются числами.
|
||||
test('isTextColumn marks codes and free text as text, numeric non-money columns as numbers', () => {
|
||||
const mrtColumn = (accessorKey) => ({ id: accessorKey, columnDef: { accessorKey } });
|
||||
for (const accessorKey of [
|
||||
'data.header.internal_order',
|
||||
'data.header.section_code',
|
||||
'data.header.item_id',
|
||||
'data.plan.comment',
|
||||
'data.q2.adj_comment',
|
||||
'data.seq_dfip.justification',
|
||||
'data.contract.counterparty',
|
||||
'data.article_id',
|
||||
]) {
|
||||
assert.equal(isTextColumn({ accessorKey }), true, accessorKey);
|
||||
assert.equal(isTextColumn(mrtColumn(accessorKey)), true, `mrt ${accessorKey}`);
|
||||
}
|
||||
for (const accessorKey of [
|
||||
'data.contract_detail.vat_rate',
|
||||
'data.contract_detail.exchange_rate',
|
||||
'data.contract_detail.amount_foreign',
|
||||
'data.rented_area',
|
||||
'data.qty_q1',
|
||||
'data.acquired_qty',
|
||||
'data.plan.q1',
|
||||
]) {
|
||||
assert.equal(isTextColumn({ accessorKey }), false, accessorKey);
|
||||
}
|
||||
});
|
||||
|
||||
test('getMoneyUnit keeps money columns whose names resemble text ones as thousand', () => {
|
||||
assert.equal(getMoneyUnit({ accessorKey: 'data.collegial.amount' }, 'OPER'), 'thousand');
|
||||
assert.equal(getMoneyUnit({ accessorKey: 'data.q1.transfer_q2_delay_acts' }, 'OPER'), 'thousand');
|
||||
assert.equal(getMoneyUnit({ accessorKey: 'data.approved.q1' }, 'OPER'), 'thousand');
|
||||
assert.equal(getMoneyUnit({ accessorKey: 'data.q1.pay_amount' }, 'OPER'), 'thousand');
|
||||
});
|
||||
|
||||
test('resolveDenomination falls back to thousand when nothing is stored', () => {
|
||||
assert.equal(resolveDenomination(null), DEFAULT_DENOMINATION);
|
||||
assert.equal(resolveDenomination(undefined), 'thousand');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user