diff --git a/src/webparts/requestPortal/forms/WorkplaceSetup/WorkplaceSetupForm.tsx b/src/webparts/requestPortal/forms/WorkplaceSetup/WorkplaceSetupForm.tsx index 9e9003e..3fcced6 100644 --- a/src/webparts/requestPortal/forms/WorkplaceSetup/WorkplaceSetupForm.tsx +++ b/src/webparts/requestPortal/forms/WorkplaceSetup/WorkplaceSetupForm.tsx @@ -34,8 +34,11 @@ import { isEditableStatus, IApproverToWrite, APPROVAL_PHASE, + IResolveFieldValue, unassignedStepMessage } from '../../shared/approval'; +import { useDebouncedQuery } from '../../shared/usePersistedQuery'; +import { routeFieldValues } from './routeFields'; import { uploadAttachments, getAttachments, @@ -165,6 +168,15 @@ const WorkplaceSetupForm: React.FC = ({ // data/requests.ts) → числовой RequestTypeId, нужный approval-route API. const requestTypeIdState = useRequestTypeId(context, requestType.id); const requestTypeId = requestTypeIdState.id; + // Условные шаги маршрута зависят от организаций и ПО (см. routeFields.ts). + // ПО — свободный текст: в маршрут уходит после паузы в наборе, а не на + // каждую букву. Сравниваем по содержимому — массив пересоздаётся на каждый рендер. + const softwareForRoute = useDebouncedQuery(data.Software); + const fieldValuesKey = JSON.stringify(routeFieldValues(data, softwareForRoute)); + const fieldValues = useMemo(() => JSON.parse(fieldValuesKey) as IResolveFieldValue[], [fieldValuesKey]); + // Отдел и поля условий влияют на резолв маршрута — их смена у НОВОЙ заявки перепланирует матрицу. + const matrixKey = `${userEmail}-${editId ?? 'new'}-${data.DepartmentId}-${fieldValuesKey}`; + // У новой заявки маршрут ПЛАНИРУЕТСЯ (resolve по внешнему API), у открытой на // редактирование — читается из уже записанных строк: их могли поправить // вручную при первом сохранении, и планирование затёрло бы эти правки. @@ -174,13 +186,12 @@ const WorkplaceSetupForm: React.FC = ({ ? approval .getMatrixForRequest(editId) .then((rows) => rows.filter((r) => r.phase !== APPROVAL_PHASE.execution)) - : getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Approval', data.DepartmentId), - [context, userEmail, approval, editId, data.DepartmentId, requestTypeId] + : getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Approval', data.DepartmentId, fieldValues), + [context, userEmail, approval, editId, data.DepartmentId, requestTypeId, fieldValues] ); const matrix = useMatrixEditor( loadMatrix, - // Отдел влияет на резолв маршрута — смена отдела у НОВОЙ заявки должна перепланировать матрицу. - `${userEmail}-${editId ?? 'new'}-${data.DepartmentId}`, + matrixKey, !readOnly, !!editId || !!requestTypeId, (login) => approval.getJobTitle(login) @@ -193,12 +204,12 @@ const WorkplaceSetupForm: React.FC = ({ ? approval .getMatrixForRequest(editId) .then((rows) => rows.filter((r) => r.phase === APPROVAL_PHASE.execution)) - : getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Execution', data.DepartmentId), - [context, userEmail, approval, editId, data.DepartmentId, requestTypeId] + : getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Execution', data.DepartmentId, fieldValues), + [context, userEmail, approval, editId, data.DepartmentId, requestTypeId, fieldValues] ); const executors = useMatrixEditor( loadExecutors, - `${userEmail}-${editId ?? 'new'}-${data.DepartmentId}`, + matrixKey, canEditExecutors(context, readOnly), !!editId || !!requestTypeId, (login) => approval.getJobTitle(login) diff --git a/src/webparts/requestPortal/forms/WorkplaceSetup/routeFields.ts b/src/webparts/requestPortal/forms/WorkplaceSetup/routeFields.ts new file mode 100644 index 0000000..913db03 --- /dev/null +++ b/src/webparts/requestPortal/forms/WorkplaceSetup/routeFields.ts @@ -0,0 +1,33 @@ +import { IResolveFieldValue } from '../../shared/approval'; +import { COMPANIES_JSON_FIELD, IFormData } from './model'; + +/** + * Значения полей заявки для условных шагов маршрута (fieldValues при resolve). + * В админке approval-route условия заданы по internalName колонок: + * «WorkplaceCompaniesJson содержит АСТЕЛ-К», «Organization содержит …», + * «Software содержит AutoCAD». + * + * Организации — одной строкой, имена через пробел: бэкенд принимает только + * строку (массив → 400), а «содержит» ищет подстроку. Тот же формат, что в + * заявках на приём и подбор (см. RecruitingHireForm). + * + * Маршрут планируется при подаче, поэтому условие по Software сработает, + * только если «Необходимое ПО» указал сам инициатор (руководитель заполняет + * его позже, на своём шаге). + */ +export function routeFieldValues( + data: Pick, + software: string +): IResolveFieldValue[] { + const fieldValues: IResolveFieldValue[] = []; + const companyNames = data.Companies.map((c) => c.Name).sort().join(' '); + if (companyNames) { + fieldValues.push({ fieldName: COMPANIES_JSON_FIELD, value: companyNames }); + fieldValues.push({ fieldName: 'Organization', value: companyNames }); + } + const softwareText = software.trim(); + if (softwareText) { + fieldValues.push({ fieldName: 'Software', value: softwareText }); + } + return fieldValues; +}