From 952a7ed42d32ad888424de7bd88940d701f314e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D1=83=D1=80=20=D0=93=D0=BE=D1=80=D0=BE?= =?UTF-8?q?=D0=B4=D0=B5=D1=86=D0=BA=D0=B8=D0=B9?= Date: Tue, 27 Oct 2026 15:15:44 +0500 Subject: [PATCH] =?UTF-8?q?fix(ct):=20=D0=B2=D1=8B=D0=B1=D0=BE=D1=80=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=B1=D0=B5=D0=B4=D0=B8=D1=82=D0=B5=D0=BB=D1=8F=20?= =?UTF-8?q?=D1=81=D0=BD=D0=B8=D0=BC=D0=B0=D0=B5=D1=82=D1=81=D1=8F,=20?= =?UTF-8?q?=D0=B1=D0=B5=D0=B7=20=D0=BF=D0=BE=D0=B1=D0=B5=D0=B4=D0=B8=D1=82?= =?UTF-8?q?=D0=B5=D0=BB=D1=8F=20=D0=A1=D0=A2=20=D0=BD=D0=B5=20=D1=81=D0=BE?= =?UTF-8?q?=D1=85=D1=80=D0=B0=D0=BD=D1=8F=D0=B5=D1=82=D1=81=D1=8F=20(#7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Кнопку «Выбран» нельзя было снять. А если победитель не выбран, «Сохранить» не трогала winSupplier — в шапке СТ оставался прежний (так же при исключении выбранного поставщика, #1). Теперь «Выбрать» — переключатель, победитель обязателен: «Сохранить и продолжить» без него не пускает, проверка — до диалога комментария ИИ. При открытии СТ без сессии выбор восстанавливается из winSupplier шапки, иначе в другом браузере пришлось бы выбирать заново. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../forms/ct/components/CtCreateForm.tsx | 22 ++++++---- .../Step3ComparisonTable/ComparisonMatrix.tsx | 4 +- .../Step3ComparisonTable.tsx | 10 ++++- .../step3Validation.test.ts | 41 +++++++++++++++++++ .../Step3ComparisonTable/step3Validation.ts | 30 ++++++++++++++ .../Step3ComparisonTable/useStep3Matrix.ts | 24 +++++++---- .../forms/ct/components/types/domain.ts | 2 + .../components/utils/supplierColumns.test.ts | 23 ++++++++++- .../ct/components/utils/supplierColumns.ts | 7 ++++ .../forms/ct/services/CtService.ts | 3 +- 10 files changed, 147 insertions(+), 19 deletions(-) create mode 100644 src/webparts/ctFormsPortal/forms/ct/components/Step3ComparisonTable/step3Validation.test.ts create mode 100644 src/webparts/ctFormsPortal/forms/ct/components/Step3ComparisonTable/step3Validation.ts diff --git a/src/webparts/ctFormsPortal/forms/ct/components/CtCreateForm.tsx b/src/webparts/ctFormsPortal/forms/ct/components/CtCreateForm.tsx index 653a1df..d33f566 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 } from './utils/supplierColumns'; +import { buildColumnsFromSpOffers, buildSupplierColumnId, findColumnForRecord, findWinnerColumnId } from './utils/supplierColumns'; import { buildOffersFromSpRecords } from './utils/spOffers'; import { excludedItemsToFormData } from './utils/excludedColumns'; import { isUserInApprovalChain } from './utils/approvalAccess'; @@ -184,18 +184,26 @@ const CtCreateForm: React.FC = ({ context, userDisplayName, } } + // Preserve session suppliers and offers — SP data may be incomplete or stale vs. unsaved edits. + // Only use SP-derived data when session has none, or in review mode (always fresh). + const suppliers = withNoOfferSuppliers( + formData.suppliers.length === 0 ? [...spSuppliers, ...excluded.suppliers] : formData.suppliers, + noOfferFromSp, + ); + // Победитель обязателен для «Сохранить» на Шаге 3: без сессии берём его из шапки СТ, иначе + // в другом браузере пришлось бы выбирать заново. + const winnerColumnId = formData.recommendedSupplierId === undefined + ? findWinnerColumnId(suppliers, ct.winSupplierSpId) + : undefined; + updateFormData({ ct, approvalMatrix, // Preserve session selectedPositions — overwriting changes pos.id (Запросы ID → Позиции СТ ID), // which breaks edits keys built from buildOfferKey(sup.id, pos.id) in Step 3. ...(preferSp || formData.selectedPositions.length === 0 ? { selectedPositions: positions } : {}), - // Preserve session suppliers and offers — SP data may be incomplete or stale vs. unsaved edits. - // Only use SP-derived data when session has none, or in review mode (always fresh). - suppliers: withNoOfferSuppliers( - formData.suppliers.length === 0 ? [...spSuppliers, ...excluded.suppliers] : formData.suppliers, - noOfferFromSp, - ), + suppliers, + ...(winnerColumnId !== undefined ? { recommendedSupplierId: winnerColumnId } : {}), ...(preferSp || formData.offers.length === 0 ? { offers: [...offers, ...excluded.offers] } : {}), ...(formData.currencyRates.length === 0 && spCurrencyRates.length > 0 ? { currencyRates: spCurrencyRates } : {}), ...(uploadedKpFiles.length > 0 ? { uploadedKpFiles } : {}), diff --git a/src/webparts/ctFormsPortal/forms/ct/components/Step3ComparisonTable/ComparisonMatrix.tsx b/src/webparts/ctFormsPortal/forms/ct/components/Step3ComparisonTable/ComparisonMatrix.tsx index 04743b7..16307ab 100644 --- a/src/webparts/ctFormsPortal/forms/ct/components/Step3ComparisonTable/ComparisonMatrix.tsx +++ b/src/webparts/ctFormsPortal/forms/ct/components/Step3ComparisonTable/ComparisonMatrix.tsx @@ -8,6 +8,7 @@ import { SUPPLIER_STATUS_OPTIONS, getSubCols, formatKzt } from './matrixHelpers' import { UseAdataCheckResult } from './useAdataCheck'; import { AdataIndicator } from './AdataIndicator'; import { ExcludeSupplierDialog } from './ExcludeSupplierDialog'; +import { toggleWinner } from './step3Validation'; import styles from './Step3ComparisonTable.module.scss'; const FIXED_COLS = 3; // Наименование / Ед. изм. / Кол-во @@ -113,8 +114,9 @@ export function ComparisonMatrix({ matrix, currencyRates, isRestrictedView, adat <> diff --git a/src/webparts/ctFormsPortal/forms/ct/components/Step3ComparisonTable/Step3ComparisonTable.tsx b/src/webparts/ctFormsPortal/forms/ct/components/Step3ComparisonTable/Step3ComparisonTable.tsx index 8f0d4b1..fc9c0e3 100644 --- a/src/webparts/ctFormsPortal/forms/ct/components/Step3ComparisonTable/Step3ComparisonTable.tsx +++ b/src/webparts/ctFormsPortal/forms/ct/components/Step3ComparisonTable/Step3ComparisonTable.tsx @@ -56,7 +56,8 @@ export const Step3ComparisonTable: React.FC = ({ const matrix = useStep3Matrix({ service, formData, isReadOnly: !canEdit, onSave, onNext, onStepComplete }); const { isReadOnly, newSupplierName, newSupplierCurrency, setNewSupplierName, setNewSupplierCurrency, - handleAddSupplier, handleAddSupplierFromDatabase, translateUnits, setTranslateUnits, isSaving, saveError, setSaveError, handleSave, + handleAddSupplier, handleAddSupplierFromDatabase, translateUnits, setTranslateUnits, isSaving, saveError, setSaveError, + validateBeforeSave, handleSave, extraPositions, suppliersWithOffer, columnDisplayNames, allPositions, computedOffers, selectedSupplierId, excludedColumns, handleRestoreSupplier, } = matrix; @@ -242,7 +243,12 @@ export const Step3ComparisonTable: React.FC = ({