diff --git a/src/webparts/ctFormsPortal/forms/ct/components/CtCreateForm.tsx b/src/webparts/ctFormsPortal/forms/ct/components/CtCreateForm.tsx index 7a9173e..619ae6a 100644 --- a/src/webparts/ctFormsPortal/forms/ct/components/CtCreateForm.tsx +++ b/src/webparts/ctFormsPortal/forms/ct/components/CtCreateForm.tsx @@ -9,7 +9,7 @@ import { Step1PositionSelection } from './Step1PositionSelection/Step1PositionSe import { Step2CommercialOffers } from './Step2CommercialOffers/Step2CommercialOffers'; import { Step3ComparisonTable } from './Step3ComparisonTable/Step3ComparisonTable'; import { Step4Approval } from './Step4Approval/Step4Approval'; -import { buildColumnsFromSpOffers, buildSupplierColumnId, findColumnForRecord, findWinnerColumnId } from './utils/supplierColumns'; +import { buildColumnsFromSpOffers, buildSupplierColumnId, findColumnForRecord, findWinnerColumnId, withoutSessionExtras } from './utils/supplierColumns'; import { buildOffersFromSpRecords } from './utils/spOffers'; import { excludedItemsToFormData } from './utils/excludedColumns'; import { isUserInApprovalChain } from './utils/approvalAccess'; @@ -122,16 +122,22 @@ const CtCreateForm: React.FC = ({ context, userDisplayName, // КП в разных валютах. Используются, только когда в сессии колонок нет. const currencyCodeOf = (valutaSpId?: number): string => valutaSpId ? (rateById.get(valutaSpId)?.code ?? 'KZT') : 'KZT'; - const spSuppliers = buildColumnsFromSpOffers(rawOffers.map(o => ({ + // Поставщики Шага 3 из сессии (после «Сохранить черновик») уже лежат в SP — их колонки из SP + // не дублируем. + const sessionExtras = formData.step3ExtraSuppliers ?? []; + const spSuppliers = withoutSessionExtras(buildColumnsFromSpOffers(rawOffers.map(o => ({ supplierSpId: o.supplierSpId, supplierName: o.supplierName, offerFileSpId: o.offerFileSpId, currencyCode: currencyCodeOf(o.valutaSpId), - }))); + }))), sessionExtras); const supplierMap = new Map(); rawOffers.forEach(o => { if (!supplierMap.has(o.supplierSpId)) supplierMap.set(o.supplierSpId, o.supplierName); }); // Колонка оффера: из сессии, если она есть (в т.ч. старая колонка без файла КП), иначе — из SP. - const visibleColumns = formData.suppliers.length > 0 ? formData.suppliers : spSuppliers; + // Поставщики Шага 3 — тоже колонки: их офферы из SP должны попасть к ним, а не в убранную колонку. + const visibleColumns = formData.suppliers.length > 0 + ? formData.suppliers + : [...spSuppliers, ...sessionExtras.map(es => ({ id: es.id, spId: es.spId, name: es.name, hasOffer: true }))]; const columnIdOf = (supplierSpId: number, offerFileSpId?: number): string => findColumnForRecord(visibleColumns, supplierSpId, offerFileSpId)?.id ?? buildSupplierColumnId(supplierSpId, offerFileSpId); diff --git a/src/webparts/ctFormsPortal/forms/ct/components/utils/supplierColumns.test.ts b/src/webparts/ctFormsPortal/forms/ct/components/utils/supplierColumns.test.ts index 8e23794..565708c 100644 --- a/src/webparts/ctFormsPortal/forms/ct/components/utils/supplierColumns.test.ts +++ b/src/webparts/ctFormsPortal/forms/ct/components/utils/supplierColumns.test.ts @@ -1,6 +1,31 @@ -import { buildColumnDisplayNames, buildColumnLabels, buildColumnsFromSpOffers, buildSupplierColumnId, findColumnForRecord, findSiblingColumnIds, findWinnerColumnId } from './supplierColumns'; +import { buildColumnDisplayNames, buildColumnLabels, buildColumnsFromSpOffers, buildSupplierColumnId, findColumnForRecord, findSiblingColumnIds, findWinnerColumnId, withoutSessionExtras } from './supplierColumns'; import { Supplier } from '../types/domain'; +describe('withoutSessionExtras (поставщик Шага 3 после «Сохранить черновик»)', () => { + // СТ 375: единственный поставщик «fghfg» добавлен на Шаге 3, сохранён черновиком. При открытии из + // реестра колонка собиралась из SP («285_322», без цен) и ещё раз приходила из сессии (с ценами). + const fromSp: Supplier[] = [ + { id: '285_322', spId: 285, kpFileSpId: 322, name: 'fghfg', hasOffer: true }, + { id: '282_320', spId: 282, kpFileSpId: 320, name: 'Каспий Дорс', hasOffer: true }, + ]; + + it('колонку поставщика Шага 3 из SP не добавляет — она уже есть в сессии', () => { + const result = withoutSessionExtras(fromSp, [{ spId: 285, offerId: 322 }]); + + expect(result.map(c => c.id)).toEqual(['282_320']); + }); + + it('другой КП того же поставщика — отдельная колонка, остаётся', () => { + const result = withoutSessionExtras(fromSp, [{ spId: 285, offerId: 999 }]); + + expect(result.map(c => c.id)).toEqual(['285_322', '282_320']); + }); + + it('поставщик Шага 3 ещё не сохранён в SP — ничего не убирает', () => { + expect(withoutSessionExtras(fromSp, [{}])).toHaveLength(2); + }); +}); + describe('findWinnerColumnId (победитель из шапки СТ при открытии без сессии)', () => { const columns: Supplier[] = [ { id: '281_318', spId: 281, kpFileSpId: 318, name: 'SteelGate', hasOffer: true, exclusion: { reason: 'Дорого' } }, diff --git a/src/webparts/ctFormsPortal/forms/ct/components/utils/supplierColumns.ts b/src/webparts/ctFormsPortal/forms/ct/components/utils/supplierColumns.ts index 68afdbf..bf45540 100644 --- a/src/webparts/ctFormsPortal/forms/ct/components/utils/supplierColumns.ts +++ b/src/webparts/ctFormsPortal/forms/ct/components/utils/supplierColumns.ts @@ -44,6 +44,22 @@ export function buildColumnsFromSpOffers(records: SpOfferColumnSource[]): Suppli return Array.from(columnsById.values()); } +// Поставщик, добавленный на Шаге 3, в сессии: SP Id поставщика и его файла-заглушки КП. +export interface SessionExtraColumn { + spId?: number; + offerId?: number; +} + +// Колонки из SP без тех, что в сессии уже есть поставщиком Шага 3. «Сохранить черновик» оставляет +// такого поставщика в списке Шага 3 (см. buildSuppliersPatch), а в SP он уже лежит колонкой +// «поставщик_заглушка». Без этого при открытии СТ, где других поставщиков нет, колонка задваивалась: +// одна из SP без цен, вторая из сессии с ценами. +export function withoutSessionExtras(spColumns: Supplier[], extras: SessionExtraColumn[]): Supplier[] { + return spColumns.filter(column => !extras.some(extra => + extra.spId !== undefined && extra.spId === column.spId && extra.offerId === column.kpFileSpId, + )); +} + // Колонка победителя по winSupplier из шапки СТ (СТ открыта без сессии). В SP хранится поставщик, // а не колонка: у поставщика с несколькими КП берём первую неисключённую колонку. export function findWinnerColumnId(columns: Supplier[], winSupplierSpId: number | undefined): string | undefined {