fix(ct): поставщик Шага 3 после черновика не задваивается при открытии СТ (#8)
«Сохранить черновик» оставляет поставщика, добавленного на Шаге 3, в списке шага, а в SP он уже лежит колонкой «поставщик_заглушка». Если других поставщиков в СТ нет, при открытии из реестра колонки собирались из SP — и поставщик приходил дважды: из SP без цен и из сессии с ценами (подписи колонок именем файла-заглушки .txt и «КП 2»). Теперь колонки из SP, которые уже есть поставщиком Шага 3, не добавляются, а их офферы идут к нему. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,7 @@ import { Step1PositionSelection } from './Step1PositionSelection/Step1PositionSe
|
|||||||
import { Step2CommercialOffers } from './Step2CommercialOffers/Step2CommercialOffers';
|
import { Step2CommercialOffers } from './Step2CommercialOffers/Step2CommercialOffers';
|
||||||
import { Step3ComparisonTable } from './Step3ComparisonTable/Step3ComparisonTable';
|
import { Step3ComparisonTable } from './Step3ComparisonTable/Step3ComparisonTable';
|
||||||
import { Step4Approval } from './Step4Approval/Step4Approval';
|
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 { buildOffersFromSpRecords } from './utils/spOffers';
|
||||||
import { excludedItemsToFormData } from './utils/excludedColumns';
|
import { excludedItemsToFormData } from './utils/excludedColumns';
|
||||||
import { isUserInApprovalChain } from './utils/approvalAccess';
|
import { isUserInApprovalChain } from './utils/approvalAccess';
|
||||||
@@ -122,16 +122,22 @@ const CtCreateForm: React.FC<ICtCreateFormProps> = ({ context, userDisplayName,
|
|||||||
// КП в разных валютах. Используются, только когда в сессии колонок нет.
|
// КП в разных валютах. Используются, только когда в сессии колонок нет.
|
||||||
const currencyCodeOf = (valutaSpId?: number): string =>
|
const currencyCodeOf = (valutaSpId?: number): string =>
|
||||||
valutaSpId ? (rateById.get(valutaSpId)?.code ?? 'KZT') : 'KZT';
|
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,
|
supplierSpId: o.supplierSpId,
|
||||||
supplierName: o.supplierName,
|
supplierName: o.supplierName,
|
||||||
offerFileSpId: o.offerFileSpId,
|
offerFileSpId: o.offerFileSpId,
|
||||||
currencyCode: currencyCodeOf(o.valutaSpId),
|
currencyCode: currencyCodeOf(o.valutaSpId),
|
||||||
})));
|
}))), sessionExtras);
|
||||||
const supplierMap = new Map<number, string>();
|
const supplierMap = new Map<number, string>();
|
||||||
rawOffers.forEach(o => { if (!supplierMap.has(o.supplierSpId)) supplierMap.set(o.supplierSpId, o.supplierName); });
|
rawOffers.forEach(o => { if (!supplierMap.has(o.supplierSpId)) supplierMap.set(o.supplierSpId, o.supplierName); });
|
||||||
// Колонка оффера: из сессии, если она есть (в т.ч. старая колонка без файла КП), иначе — из SP.
|
// Колонка оффера: из сессии, если она есть (в т.ч. старая колонка без файла КП), иначе — из 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 =>
|
const columnIdOf = (supplierSpId: number, offerFileSpId?: number): string =>
|
||||||
findColumnForRecord(visibleColumns, supplierSpId, offerFileSpId)?.id
|
findColumnForRecord(visibleColumns, supplierSpId, offerFileSpId)?.id
|
||||||
?? buildSupplierColumnId(supplierSpId, offerFileSpId);
|
?? buildSupplierColumnId(supplierSpId, offerFileSpId);
|
||||||
|
|||||||
@@ -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';
|
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 (победитель из шапки СТ при открытии без сессии)', () => {
|
describe('findWinnerColumnId (победитель из шапки СТ при открытии без сессии)', () => {
|
||||||
const columns: Supplier[] = [
|
const columns: Supplier[] = [
|
||||||
{ id: '281_318', spId: 281, kpFileSpId: 318, name: 'SteelGate', hasOffer: true, exclusion: { reason: 'Дорого' } },
|
{ id: '281_318', spId: 281, kpFileSpId: 318, name: 'SteelGate', hasOffer: true, exclusion: { reason: 'Дорого' } },
|
||||||
|
|||||||
@@ -44,6 +44,22 @@ export function buildColumnsFromSpOffers(records: SpOfferColumnSource[]): Suppli
|
|||||||
return Array.from(columnsById.values());
|
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 хранится поставщик,
|
// Колонка победителя по winSupplier из шапки СТ (СТ открыта без сессии). В SP хранится поставщик,
|
||||||
// а не колонка: у поставщика с несколькими КП берём первую неисключённую колонку.
|
// а не колонка: у поставщика с несколькими КП берём первую неисключённую колонку.
|
||||||
export function findWinnerColumnId(columns: Supplier[], winSupplierSpId: number | undefined): string | undefined {
|
export function findWinnerColumnId(columns: Supplier[], winSupplierSpId: number | undefined): string | undefined {
|
||||||
|
|||||||
Reference in New Issue
Block a user