diff --git a/src/webparts/ctFormsPortal/forms/ct/components/Step3ComparisonTable/ExtraPositionRow.tsx b/src/webparts/ctFormsPortal/forms/ct/components/Step3ComparisonTable/ExtraPositionRow.tsx
index 19e4920..c8105b9 100644
--- a/src/webparts/ctFormsPortal/forms/ct/components/Step3ComparisonTable/ExtraPositionRow.tsx
+++ b/src/webparts/ctFormsPortal/forms/ct/components/Step3ComparisonTable/ExtraPositionRow.tsx
@@ -3,7 +3,8 @@ import { IconButton, TextInput } from '@tnsu/ui-kit';
import { CurrencyRate } from '../types/domain';
import { PositionRow } from './matrixTypes';
import { Step3Matrix } from './useStep3Matrix';
-import { convertQuantity, unitsEquivalent } from '../utils/unitConversion';
+import { getUnitStatus } from '../utils/unitConversion';
+import { unitStatusClass } from './unitStatusStyles';
import { formatKzt, formatNum, formatQty, round2Str, buildOfferKey } from './matrixHelpers';
import styles from './Step3ComparisonTable.module.scss';
@@ -67,8 +68,8 @@ export function ExtraPositionRow({ pos, posIndex, regularPositionsCount, matrix,
// Перевод в ед. изм. заявки (тумблер) — как в обычных позициях.
const reqUnit = pos.unit;
const supUnit = item?.unit || pos.unit;
- const tRatio = translateUnits && !unitsEquivalent(supUnit, reqUnit) ? convertQuantity(1, supUnit, reqUnit) : null;
- const cannotTranslate = translateUnits && !unitsEquivalent(supUnit, reqUnit) && tRatio === null;
+ const unitStatus = getUnitStatus(supUnit, reqUnit, translateUnits);
+ const tRatio = unitStatus.ratio ?? null;
const dispUnit = tRatio !== null ? reqUnit : (item?.unit || pos.unit);
const dispQty = (tRatio !== null && qty !== undefined) ? qty * tRatio : qty;
const dispPrice = (tRatio !== null && price !== undefined) ? price / tRatio : price;
@@ -91,7 +92,7 @@ export function ExtraPositionRow({ pos, posIndex, regularPositionsCount, matrix,
{item?.supplierProductName || pos.materialName}
)}
-
+ |
{showInput ? (
) : (
- {(dispUnit || '—') + (cannotTranslate ? ' *' : '')}
+ {dispUnit || '—'}
)}
|
diff --git a/src/webparts/ctFormsPortal/forms/ct/components/Step3ComparisonTable/MatrixRowView.tsx b/src/webparts/ctFormsPortal/forms/ct/components/Step3ComparisonTable/MatrixRowView.tsx
index 69cf9bb..8759a65 100644
--- a/src/webparts/ctFormsPortal/forms/ct/components/Step3ComparisonTable/MatrixRowView.tsx
+++ b/src/webparts/ctFormsPortal/forms/ct/components/Step3ComparisonTable/MatrixRowView.tsx
@@ -3,7 +3,8 @@ import { TextInput, Icon } from '@tnsu/ui-kit';
import { CurrencyRate } from '../types/domain';
import { PositionRow } from './matrixTypes';
import { Step3Matrix } from './useStep3Matrix';
-import { convertQuantity, unitsEquivalent } from '../utils/unitConversion';
+import { getUnitStatus } from '../utils/unitConversion';
+import { unitStatusClass } from './unitStatusStyles';
import { getSubCols, formatKzt, formatNum, formatQty, round2Str, buildOfferKey } from './matrixHelpers';
import styles from './Step3ComparisonTable.module.scss';
@@ -85,8 +86,12 @@ export function MatrixRowView({ pos, posIndex, matrix, currencyRates, isRestrict
// null — единицы совпадают или пересчёт невозможен.
const reqUnit = pos.unit;
const supUnit = item.unit || pos.unit;
- const tRatio = translateUnits && !unitsEquivalent(supUnit, reqUnit) ? convertQuantity(1, supUnit, reqUnit) : null;
- const cannotTranslate = translateUnits && !unitsEquivalent(supUnit, reqUnit) && tRatio === null;
+ const unitStatus = getUnitStatus(supUnit, reqUnit, translateUnits);
+ const tRatio = unitStatus.ratio ?? null;
+ // Подсветка: «Ед. изм.» — при любом несоответствии заявке; кол-во и цены — когда тумблер включён
+ // (они показаны пересчитанными или их нельзя сравнивать напрямую).
+ const unitCls = unitStatusClass(unitStatus.status);
+ const valueCls = translateUnits ? unitCls : '';
const dispUnit = tRatio !== null ? reqUnit : (item.unit || pos.unit);
const dispQty = (tRatio !== null && qty !== undefined) ? qty * tRatio : qty;
const dispPrice = (tRatio !== null && price !== undefined) ? price / tRatio : price;
@@ -123,7 +128,7 @@ export function MatrixRowView({ pos, posIndex, matrix, currencyRates, isRestrict
)}
|
-
+ |
{showInput ? (
) : (
- {(dispUnit || '—') + (cannotTranslate ? ' *' : '')}
+ {dispUnit || '—'}
)}
|
-
+ |
{showInput ? (
{!isRestrictedView && isForeign && (
<>
- |
+ |
{showInput ? (
>
)}
- {!isRestrictedView && |
+ {!isRestrictedView && |
{showInput ? (
= ({
{translateUnits && (
- кол-во и цена показаны в ед. изм. заявки; «*» — пересчёт невозможен (ред. недоступно)
+ кол-во и цена — в ед. изм. заявки (ред. недоступно):{' '}
+ пересчитано,{' '}
+ пересчёт невозможен
)}
diff --git a/src/webparts/ctFormsPortal/forms/ct/components/Step3ComparisonTable/unitStatusStyles.ts b/src/webparts/ctFormsPortal/forms/ct/components/Step3ComparisonTable/unitStatusStyles.ts
new file mode 100644
index 0000000..d00573f
--- /dev/null
+++ b/src/webparts/ctFormsPortal/forms/ct/components/Step3ComparisonTable/unitStatusStyles.ts
@@ -0,0 +1,15 @@
+import { UnitStatusKind } from '../utils/unitConversion';
+import styles from './Step3ComparisonTable.module.scss';
+
+// Подсветка ячеек по статусу ед. изм. поставщика (см. getUnitStatus): жёлтым — единица отличается
+// от заявки, голубым — пересчитано в ед. заявки, красным — пересчитать нельзя.
+const UNIT_STATUS_CLASS: Record = {
+ same: '',
+ mismatch: styles.unitMismatch,
+ converted: styles.unitConverted,
+ impossible: styles.unitImpossible,
+};
+
+export function unitStatusClass(status: UnitStatusKind): string {
+ return UNIT_STATUS_CLASS[status];
+}
diff --git a/src/webparts/ctFormsPortal/forms/ct/components/utils/unitConversion.test.ts b/src/webparts/ctFormsPortal/forms/ct/components/utils/unitConversion.test.ts
index 241e647..615559b 100644
--- a/src/webparts/ctFormsPortal/forms/ct/components/utils/unitConversion.test.ts
+++ b/src/webparts/ctFormsPortal/forms/ct/components/utils/unitConversion.test.ts
@@ -1,4 +1,4 @@
-import { convertQuantity, normalizeUnit, unitsEquivalent } from './unitConversion';
+import { convertQuantity, getUnitStatus, normalizeUnit, unitsEquivalent } from './unitConversion';
describe('unitsEquivalent — синонимы одной единицы', () => {
// СТ №12: в заявке «Штука», у поставщика «шт.» — тумблер помечал «шт. *» (пересчёт невозможен).
@@ -62,3 +62,30 @@ describe('normalizeUnit', () => {
expect(normalizeUnit(' шт. ')).toBe(normalizeUnit('Штука'));
});
});
+
+describe('getUnitStatus — подсветка несоответствия ед. изм. (п.6)', () => {
+ it('одинаковые единицы — ничего не подсвечиваем', () => {
+ expect(getUnitStatus('шт.', 'Штука', false)).toEqual({ status: 'same' });
+ expect(getUnitStatus('шт.', 'Штука', true)).toEqual({ status: 'same' });
+ });
+
+ // Тумблер выключен: несовпадение видно сразу, а не только по незаметной «*» при включённом тумблере.
+ it('тумблер выключен — разные единицы: несоответствие с подсказкой', () => {
+ expect(getUnitStatus('т', 'кг', false)).toEqual({ status: 'mismatch', hint: 'В заявке: кг, у поставщика: т' });
+ });
+
+ it('тумблер включён — пересчитали: коэффициент и подсказка', () => {
+ expect(getUnitStatus('т', 'кг', true)).toEqual({ status: 'converted', ratio: 1000, hint: 'Пересчитано из т в кг' });
+ });
+
+ it('тумблер включён — пересчитать нельзя: подсказка сравнивать вручную', () => {
+ expect(getUnitStatus('шт', 'кг', true)).toEqual({
+ status: 'impossible',
+ hint: 'Нельзя перевести шт → кг: сравнивайте вручную',
+ });
+ });
+
+ it('пустая единица поставщика — не несоответствие', () => {
+ expect(getUnitStatus('', 'кг', false)).toEqual({ status: 'same' });
+ });
+});
diff --git a/src/webparts/ctFormsPortal/forms/ct/components/utils/unitConversion.ts b/src/webparts/ctFormsPortal/forms/ct/components/utils/unitConversion.ts
index 71e7201..ba66903 100644
--- a/src/webparts/ctFormsPortal/forms/ct/components/utils/unitConversion.ts
+++ b/src/webparts/ctFormsPortal/forms/ct/components/utils/unitConversion.ts
@@ -96,3 +96,34 @@ export function convertQuantity(qty: number, fromUnit: string | undefined, toUni
if (!from || !to || from.dim !== to.dim) return null;
return qty * (from.toBase / to.toBase);
}
+
+// Статус ед. изм. поставщика относительно заявки — для подсветки ячеек на шаге 3:
+// same — единицы совпадают (или у поставщика не указана) — без подсветки;
+// mismatch — тумблер выключен, единицы разные — показать, что сравнивать надо внимательно;
+// converted — тумблер включён, пересчитали в ед. заявки (ratio — коэффициент кол-ва);
+// impossible — тумблер включён, пересчитать нельзя (шт ↔ кг, шт ↔ компл).
+export type UnitStatusKind = 'same' | 'mismatch' | 'converted' | 'impossible';
+
+export interface UnitStatus {
+ status: UnitStatusKind;
+ ratio?: number;
+ hint?: string;
+}
+
+export function getUnitStatus(
+ supplierUnit: string | undefined,
+ requestUnit: string | undefined,
+ translateUnits: boolean,
+): UnitStatus {
+ const supplier = (supplierUnit ?? '').trim();
+ const request = (requestUnit ?? '').trim();
+ if (!supplier || !request || unitsEquivalent(supplier, request)) return { status: 'same' };
+ if (!translateUnits) {
+ return { status: 'mismatch', hint: `В заявке: ${request}, у поставщика: ${supplier}` };
+ }
+ const ratio = convertQuantity(1, supplier, request);
+ if (ratio === null) {
+ return { status: 'impossible', hint: `Нельзя перевести ${supplier} → ${request}: сравнивайте вручную` };
+ }
+ return { status: 'converted', ratio, hint: `Пересчитано из ${supplier} в ${request}` };
+}
| | |