fix: черновик без маршрута планирует маршрут заново при открытии
У части черновиков не оказалось строк маршрута, и форма показывала пустую матрицу: отправить такую заявку было некому. Теперь при открытии черновика без строк маршрут планируется заново (loadRoutePhase), во всех формах; поданные заявки по-прежнему читают записанные строки. Почему строки пропали у первоначального случая — пока не выяснено. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -19,13 +19,14 @@ import {
|
|||||||
StatusBadge,
|
StatusBadge,
|
||||||
useMatrixEditor,
|
useMatrixEditor,
|
||||||
getPlannedMatrix,
|
getPlannedMatrix,
|
||||||
|
loadRoutePhase,
|
||||||
|
isOpenedRouteReady,
|
||||||
resolveWriteListId,
|
resolveWriteListId,
|
||||||
notifyActiveStep,
|
notifyActiveStep,
|
||||||
useRequestTypeId,
|
useRequestTypeId,
|
||||||
REQUEST_STATUS,
|
REQUEST_STATUS,
|
||||||
isEditableStatus,
|
isEditableStatus,
|
||||||
IApproverToWrite,
|
IApproverToWrite,
|
||||||
APPROVAL_PHASE,
|
|
||||||
unassignedStepMessage
|
unassignedStepMessage
|
||||||
} from '../../shared/approval';
|
} from '../../shared/approval';
|
||||||
import { effectiveUserEmail } from '../../shared/impersonation';
|
import { effectiveUserEmail } from '../../shared/impersonation';
|
||||||
@@ -149,16 +150,15 @@ const ClearanceForm: React.FC<IRequestFormProps> = ({
|
|||||||
// передана» (см. TemplateRouteService.ResolveAsync в approval-route).
|
// передана» (см. TemplateRouteService.ResolveAsync в approval-route).
|
||||||
const loadMatrix = useCallback(
|
const loadMatrix = useCallback(
|
||||||
() =>
|
() =>
|
||||||
editId
|
loadRoutePhase(approval, editId, requestStatus, 'Approval', () =>
|
||||||
? approval
|
getPlannedMatrix(context, requestTypeId as number, data.Employee, approval, 'Approval', null, [
|
||||||
.getMatrixForRequest(editId)
|
{ fieldName: 'Position', value: data.Position }
|
||||||
.then((rows) => rows.filter((r) => r.phase !== APPROVAL_PHASE.execution))
|
])
|
||||||
: getPlannedMatrix(context, requestTypeId as number, data.Employee, approval, 'Approval', null, [
|
),
|
||||||
{ fieldName: 'Position', value: data.Position }
|
[context, approval, editId, requestStatus, data.Employee, data.Position, requestTypeId]
|
||||||
]),
|
|
||||||
[context, approval, editId, data.Employee, data.Position, requestTypeId]
|
|
||||||
);
|
);
|
||||||
const matrixReady = !!editId || (!!data.Employee && !!requestTypeId);
|
const matrixReady =
|
||||||
|
isOpenedRouteReady(editId, requestStatus, requestTypeId) && (!!editId || (!!data.Employee && !!requestTypeId));
|
||||||
const matrix = useMatrixEditor(
|
const matrix = useMatrixEditor(
|
||||||
loadMatrix,
|
loadMatrix,
|
||||||
`${data.Employee}-${editId ?? 'new'}-${data.Position}`,
|
`${data.Employee}-${editId ?? 'new'}-${data.Position}`,
|
||||||
@@ -172,14 +172,12 @@ const ClearanceForm: React.FC<IRequestFormProps> = ({
|
|||||||
// открытой на редактирование заявки — строки уже записаны, читаем как есть.
|
// открытой на редактирование заявки — строки уже записаны, читаем как есть.
|
||||||
const loadExecutors = useCallback(
|
const loadExecutors = useCallback(
|
||||||
() =>
|
() =>
|
||||||
editId
|
loadRoutePhase(approval, editId, requestStatus, 'Execution', () =>
|
||||||
? approval
|
getPlannedMatrix(context, requestTypeId as number, data.Employee, approval, 'Execution', null, [
|
||||||
.getMatrixForRequest(editId)
|
{ fieldName: 'Position', value: data.Position }
|
||||||
.then((rows) => rows.filter((r) => r.phase === APPROVAL_PHASE.execution))
|
])
|
||||||
: getPlannedMatrix(context, requestTypeId as number, data.Employee, approval, 'Execution', null, [
|
),
|
||||||
{ fieldName: 'Position', value: data.Position }
|
[context, approval, editId, requestStatus, data.Employee, data.Position, requestTypeId]
|
||||||
]),
|
|
||||||
[context, approval, editId, data.Employee, data.Position, requestTypeId]
|
|
||||||
);
|
);
|
||||||
const executors = useMatrixEditor(
|
const executors = useMatrixEditor(
|
||||||
loadExecutors,
|
loadExecutors,
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import {
|
|||||||
StatusBadge,
|
StatusBadge,
|
||||||
useMatrixEditor,
|
useMatrixEditor,
|
||||||
getPlannedMatrix,
|
getPlannedMatrix,
|
||||||
|
loadRoutePhase,
|
||||||
|
isOpenedRouteReady,
|
||||||
resolveWriteListId,
|
resolveWriteListId,
|
||||||
notifyActiveStep,
|
notifyActiveStep,
|
||||||
useRequestTypeId,
|
useRequestTypeId,
|
||||||
@@ -28,7 +30,6 @@ import {
|
|||||||
IApproverToWrite,
|
IApproverToWrite,
|
||||||
unassignedStepMessage,
|
unassignedStepMessage,
|
||||||
useFormExecutors,
|
useFormExecutors,
|
||||||
approvalPhaseRows,
|
|
||||||
executorsToWrite,
|
executorsToWrite,
|
||||||
initialSubmitStatus
|
initialSubmitStatus
|
||||||
} from '../../shared/approval';
|
} from '../../shared/approval';
|
||||||
@@ -172,13 +173,15 @@ const ConcreteRequestForm: React.FC<IRequestFormProps> = ({
|
|||||||
// проект не выбран (см. matrixReady). У заявки на редактировании маршрут
|
// проект не выбран (см. matrixReady). У заявки на редактировании маршрут
|
||||||
// читается из уже записанных строк, поэтому проект там уже не нужен.
|
// читается из уже записанных строк, поэтому проект там уже не нужен.
|
||||||
const projectId = data.ProjectId || null;
|
const projectId = data.ProjectId || null;
|
||||||
const matrixReady = !!editId || (!!data.SiteChiefEmail && !!projectId && !!requestTypeId);
|
const matrixReady =
|
||||||
|
isOpenedRouteReady(editId, requestStatus, requestTypeId) &&
|
||||||
|
(!!editId || (!!data.SiteChiefEmail && !!projectId && !!requestTypeId));
|
||||||
const loadMatrix = useCallback(
|
const loadMatrix = useCallback(
|
||||||
() =>
|
() =>
|
||||||
editId
|
loadRoutePhase(approval, editId, requestStatus, 'Approval', () =>
|
||||||
? approval.getMatrixForRequest(editId).then(approvalPhaseRows)
|
getPlannedMatrix(context, requestTypeId as number, data.SiteChiefEmail, approval, 'Approval', null, undefined, projectId)
|
||||||
: getPlannedMatrix(context, requestTypeId as number, data.SiteChiefEmail, approval, 'Approval', null, undefined, projectId),
|
),
|
||||||
[context, approval, editId, data.SiteChiefEmail, requestTypeId, projectId]
|
[context, approval, editId, requestStatus, data.SiteChiefEmail, requestTypeId, projectId]
|
||||||
);
|
);
|
||||||
const matrix = useMatrixEditor(
|
const matrix = useMatrixEditor(
|
||||||
loadMatrix,
|
loadMatrix,
|
||||||
@@ -197,6 +200,7 @@ const ConcreteRequestForm: React.FC<IRequestFormProps> = ({
|
|||||||
requestTypeId,
|
requestTypeId,
|
||||||
userEmail: data.SiteChiefEmail,
|
userEmail: data.SiteChiefEmail,
|
||||||
editId,
|
editId,
|
||||||
|
requestStatus,
|
||||||
projectId
|
projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import {
|
|||||||
StatusBadge,
|
StatusBadge,
|
||||||
useMatrixEditor,
|
useMatrixEditor,
|
||||||
getPlannedMatrix,
|
getPlannedMatrix,
|
||||||
|
loadRoutePhase,
|
||||||
|
isOpenedRouteReady,
|
||||||
notifyActiveStep,
|
notifyActiveStep,
|
||||||
useRequestTypeId,
|
useRequestTypeId,
|
||||||
REQUEST_STATUS,
|
REQUEST_STATUS,
|
||||||
@@ -27,7 +29,6 @@ import {
|
|||||||
IApproverToWrite,
|
IApproverToWrite,
|
||||||
unassignedStepMessage,
|
unassignedStepMessage,
|
||||||
resolveWriteListId,
|
resolveWriteListId,
|
||||||
approvalPhaseRows,
|
|
||||||
executorsToWrite,
|
executorsToWrite,
|
||||||
initialSubmitStatus,
|
initialSubmitStatus,
|
||||||
useFormExecutors
|
useFormExecutors
|
||||||
@@ -163,18 +164,18 @@ const DismissalForm: React.FC<IRequestFormProps> = ({
|
|||||||
// затереть правки инициатора.
|
// затереть правки инициатора.
|
||||||
const loadMatrix = useCallback(
|
const loadMatrix = useCallback(
|
||||||
() =>
|
() =>
|
||||||
editId
|
loadRoutePhase(approval, editId, requestStatus, 'Approval', () =>
|
||||||
? approval.getMatrixForRequest(editId).then(approvalPhaseRows)
|
getPlannedMatrix(context, requestTypeId as number, data.Employee, approval, 'Approval', null, [
|
||||||
: getPlannedMatrix(context, requestTypeId as number, data.Employee, approval, 'Approval', null, [
|
{ fieldName: 'Position', value: primaryPosition(data.Organization) }
|
||||||
{ fieldName: 'Position', value: primaryPosition(data.Organization) }
|
])
|
||||||
]),
|
),
|
||||||
[context, approval, editId, data.Employee, data.Organization, requestTypeId]
|
[context, approval, editId, requestStatus, data.Employee, data.Organization, requestTypeId]
|
||||||
);
|
);
|
||||||
const matrix = useMatrixEditor(
|
const matrix = useMatrixEditor(
|
||||||
loadMatrix,
|
loadMatrix,
|
||||||
`${data.Employee}-${editId ?? 'new'}-${primaryPosition(data.Organization)}`,
|
`${data.Employee}-${editId ?? 'new'}-${primaryPosition(data.Organization)}`,
|
||||||
!readOnly,
|
!readOnly,
|
||||||
!!editId || (!!data.Employee && !!requestTypeId),
|
isOpenedRouteReady(editId, requestStatus, requestTypeId) && (!!editId || (!!data.Employee && !!requestTypeId)),
|
||||||
(login) => approval.getJobTitle(login)
|
(login) => approval.getJobTitle(login)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -187,6 +188,7 @@ const DismissalForm: React.FC<IRequestFormProps> = ({
|
|||||||
requestTypeId,
|
requestTypeId,
|
||||||
userEmail: data.Employee,
|
userEmail: data.Employee,
|
||||||
editId,
|
editId,
|
||||||
|
requestStatus,
|
||||||
fieldValues: [{ fieldName: 'Position', value: primaryPosition(data.Organization) }]
|
fieldValues: [{ fieldName: 'Position', value: primaryPosition(data.Organization) }]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -20,10 +20,11 @@ import {
|
|||||||
useMatrixEditor,
|
useMatrixEditor,
|
||||||
useFormExecutors,
|
useFormExecutors,
|
||||||
ExecutorsMatrix,
|
ExecutorsMatrix,
|
||||||
approvalPhaseRows,
|
|
||||||
executorsToWrite,
|
executorsToWrite,
|
||||||
initialSubmitStatus,
|
initialSubmitStatus,
|
||||||
getPlannedMatrix,
|
getPlannedMatrix,
|
||||||
|
loadRoutePhase,
|
||||||
|
isOpenedRouteReady,
|
||||||
resolveWriteListId,
|
resolveWriteListId,
|
||||||
notifyActiveStep,
|
notifyActiveStep,
|
||||||
useRequestTypeId,
|
useRequestTypeId,
|
||||||
@@ -159,16 +160,16 @@ const EmployeeTransferForm: React.FC<IRequestFormProps> = ({
|
|||||||
// маршрут читается из уже записанных строк, чтобы не затереть правки инициатора.
|
// маршрут читается из уже записанных строк, чтобы не затереть правки инициатора.
|
||||||
const loadMatrix = useCallback(
|
const loadMatrix = useCallback(
|
||||||
() =>
|
() =>
|
||||||
editId
|
loadRoutePhase(approval, editId, requestStatus, 'Approval', () =>
|
||||||
? approval.getMatrixForRequest(editId).then(approvalPhaseRows)
|
getPlannedMatrix(context, requestTypeId as number, data.Employee, approval, 'Approval')
|
||||||
: getPlannedMatrix(context, requestTypeId as number, data.Employee, approval, 'Approval'),
|
),
|
||||||
[context, approval, editId, data.Employee, requestTypeId]
|
[context, approval, editId, requestStatus, data.Employee, requestTypeId]
|
||||||
);
|
);
|
||||||
const matrix = useMatrixEditor(
|
const matrix = useMatrixEditor(
|
||||||
loadMatrix,
|
loadMatrix,
|
||||||
`${data.Employee}-${editId ?? 'new'}`,
|
`${data.Employee}-${editId ?? 'new'}`,
|
||||||
!readOnly,
|
!readOnly,
|
||||||
!!editId || (!!data.Employee && !!requestTypeId),
|
isOpenedRouteReady(editId, requestStatus, requestTypeId) && (!!editId || (!!data.Employee && !!requestTypeId)),
|
||||||
(login) => approval.getJobTitle(login)
|
(login) => approval.getJobTitle(login)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -180,7 +181,8 @@ const EmployeeTransferForm: React.FC<IRequestFormProps> = ({
|
|||||||
approval,
|
approval,
|
||||||
requestTypeId,
|
requestTypeId,
|
||||||
userEmail: data.Employee,
|
userEmail: data.Employee,
|
||||||
editId
|
editId,
|
||||||
|
requestStatus
|
||||||
});
|
});
|
||||||
|
|
||||||
const isValid = !!(
|
const isValid = !!(
|
||||||
|
|||||||
@@ -28,13 +28,14 @@ import {
|
|||||||
useMatrixEditor,
|
useMatrixEditor,
|
||||||
canEditExecutors,
|
canEditExecutors,
|
||||||
getPlannedMatrix,
|
getPlannedMatrix,
|
||||||
|
loadRoutePhase,
|
||||||
|
isOpenedRouteReady,
|
||||||
resolveWriteListId,
|
resolveWriteListId,
|
||||||
notifyActiveStep,
|
notifyActiveStep,
|
||||||
useRequestTypeId,
|
useRequestTypeId,
|
||||||
REQUEST_STATUS,
|
REQUEST_STATUS,
|
||||||
isEditableStatus,
|
isEditableStatus,
|
||||||
IApproverToWrite,
|
IApproverToWrite,
|
||||||
APPROVAL_PHASE,
|
|
||||||
unassignedStepMessage,
|
unassignedStepMessage,
|
||||||
resolveApproverOrgInfo,
|
resolveApproverOrgInfo,
|
||||||
canEditRoute
|
canEditRoute
|
||||||
@@ -161,7 +162,7 @@ const RecruitingHireForm: React.FC<IRequestFormProps> = ({
|
|||||||
// (поле — мультивыбор) и ждём его выбора, как раньше.
|
// (поле — мультивыбор) и ждём его выбора, как раньше.
|
||||||
// Пустой Id — проект старой заявки, введённый вручную (см. projectsFromItem).
|
// Пустой Id — проект старой заявки, введённый вручную (см. projectsFromItem).
|
||||||
const projectId = (data.Projects.length > 0 && data.Projects[0].Id) || null;
|
const projectId = (data.Projects.length > 0 && data.Projects[0].Id) || null;
|
||||||
const matrixReady = !!editId || (!!projectId && !!requestTypeId);
|
const matrixReady = isOpenedRouteReady(editId, requestStatus, requestTypeId) &&(!!editId || (!!projectId && !!requestTypeId));
|
||||||
// Несколько шагов маршрута условны по компании (см. панель «Маршруты»:
|
// Несколько шагов маршрута условны по компании (см. панель «Маршруты»:
|
||||||
// «CompaniesJson = TOO QAZGEOMAX», «CompaniesJson = TOO CT Global», шаг с
|
// «CompaniesJson = TOO QAZGEOMAX», «CompaniesJson = TOO CT Global», шаг с
|
||||||
// сразу ДВУМЯ условиями одновременно). Companies — мультивыбор → ОДНА запись
|
// сразу ДВУМЯ условиями одновременно). Companies — мультивыбор → ОДНА запись
|
||||||
@@ -180,12 +181,10 @@ const RecruitingHireForm: React.FC<IRequestFormProps> = ({
|
|||||||
}, [companiesFieldValue, projectsFieldValue]);
|
}, [companiesFieldValue, projectsFieldValue]);
|
||||||
const loadMatrix = useCallback(
|
const loadMatrix = useCallback(
|
||||||
() =>
|
() =>
|
||||||
editId
|
loadRoutePhase(approval, editId, requestStatus, 'Approval', () =>
|
||||||
? approval
|
getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Approval', null, fieldValues, projectId)
|
||||||
.getMatrixForRequest(editId)
|
),
|
||||||
.then((rows) => rows.filter((r) => r.phase !== APPROVAL_PHASE.execution))
|
[context, userEmail, approval, editId, requestStatus, requestTypeId, fieldValues, projectId]
|
||||||
: getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Approval', null, fieldValues, projectId),
|
|
||||||
[context, userEmail, approval, editId, requestTypeId, fieldValues, projectId]
|
|
||||||
);
|
);
|
||||||
const matrix = useMatrixEditor(
|
const matrix = useMatrixEditor(
|
||||||
loadMatrix,
|
loadMatrix,
|
||||||
@@ -200,12 +199,10 @@ const RecruitingHireForm: React.FC<IRequestFormProps> = ({
|
|||||||
// Фаза исполнения: исполнители из маршрута (status='Execution'), только для чтения.
|
// Фаза исполнения: исполнители из маршрута (status='Execution'), только для чтения.
|
||||||
const loadExecutors = useCallback(
|
const loadExecutors = useCallback(
|
||||||
() =>
|
() =>
|
||||||
editId
|
loadRoutePhase(approval, editId, requestStatus, 'Execution', () =>
|
||||||
? approval
|
getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Execution', null, fieldValues, projectId)
|
||||||
.getMatrixForRequest(editId)
|
),
|
||||||
.then((rows) => rows.filter((r) => r.phase === APPROVAL_PHASE.execution))
|
[context, userEmail, approval, editId, requestStatus, requestTypeId, fieldValues, projectId]
|
||||||
: getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Execution', null, fieldValues, projectId),
|
|
||||||
[context, userEmail, approval, editId, requestTypeId, fieldValues, projectId]
|
|
||||||
);
|
);
|
||||||
const executors = useMatrixEditor(
|
const executors = useMatrixEditor(
|
||||||
loadExecutors,
|
loadExecutors,
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ import {
|
|||||||
useMatrixEditor,
|
useMatrixEditor,
|
||||||
canEditExecutors,
|
canEditExecutors,
|
||||||
getPlannedMatrix,
|
getPlannedMatrix,
|
||||||
|
loadRoutePhase,
|
||||||
|
isOpenedRouteReady,
|
||||||
resolveWriteListId,
|
resolveWriteListId,
|
||||||
notifyActiveStep,
|
notifyActiveStep,
|
||||||
useRequestTypeId,
|
useRequestTypeId,
|
||||||
@@ -36,8 +38,7 @@ import {
|
|||||||
IApproverToWrite,
|
IApproverToWrite,
|
||||||
unassignedStepMessage,
|
unassignedStepMessage,
|
||||||
resolveApproverOrgInfo,
|
resolveApproverOrgInfo,
|
||||||
canEditRoute,
|
canEditRoute
|
||||||
APPROVAL_PHASE
|
|
||||||
} from '../../shared/approval';
|
} from '../../shared/approval';
|
||||||
import {
|
import {
|
||||||
uploadAttachments,
|
uploadAttachments,
|
||||||
@@ -192,7 +193,7 @@ const RecruitingSelectionForm: React.FC<IRequestFormProps> = ({
|
|||||||
const projectId = data.ObjectProjectId || null;
|
const projectId = data.ObjectProjectId || null;
|
||||||
// Новая заявка: матрицу резолвим только когда есть и тип, и проект — иначе
|
// Новая заявка: матрицу резолвим только когда есть и тип, и проект — иначе
|
||||||
// 404 «маршрут не найден». При редактировании читаем уже записанные строки.
|
// 404 «маршрут не найден». При редактировании читаем уже записанные строки.
|
||||||
const matrixReady = !!editId || (!!projectId && !!requestTypeId);
|
const matrixReady = isOpenedRouteReady(editId, requestStatus, requestTypeId) &&(!!editId || (!!projectId && !!requestTypeId));
|
||||||
// Условные шаги маршрута (см. [[request-portal-fieldvalues-routing]] — тот же
|
// Условные шаги маршрута (см. [[request-portal-fieldvalues-routing]] — тот же
|
||||||
// разбор на RecruitingHire): fieldName = internalName колонки. Companies —
|
// разбор на RecruitingHire): fieldName = internalName колонки. Companies —
|
||||||
// мультивыбор → ОДНА запись fieldValues, value — имена выбранных компаний
|
// мультивыбор → ОДНА запись fieldValues, value — имена выбранных компаний
|
||||||
@@ -207,12 +208,10 @@ const RecruitingSelectionForm: React.FC<IRequestFormProps> = ({
|
|||||||
}, [companiesFieldValue, data.ObjectProject]);
|
}, [companiesFieldValue, data.ObjectProject]);
|
||||||
const loadMatrix = useCallback(
|
const loadMatrix = useCallback(
|
||||||
() =>
|
() =>
|
||||||
editId
|
loadRoutePhase(approval, editId, requestStatus, 'Approval', () =>
|
||||||
? approval
|
getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Approval', null, fieldValues, projectId)
|
||||||
.getMatrixForRequest(editId)
|
),
|
||||||
.then((rows) => rows.filter((r) => r.phase !== APPROVAL_PHASE.execution))
|
[context, userEmail, approval, editId, requestStatus, requestTypeId, fieldValues, projectId]
|
||||||
: getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Approval', null, fieldValues, projectId),
|
|
||||||
[context, userEmail, approval, editId, requestTypeId, fieldValues, projectId]
|
|
||||||
);
|
);
|
||||||
const matrix = useMatrixEditor(
|
const matrix = useMatrixEditor(
|
||||||
loadMatrix,
|
loadMatrix,
|
||||||
@@ -227,12 +226,10 @@ const RecruitingSelectionForm: React.FC<IRequestFormProps> = ({
|
|||||||
// Фаза исполнения: исполнители из маршрута (status='Execution'), только для чтения.
|
// Фаза исполнения: исполнители из маршрута (status='Execution'), только для чтения.
|
||||||
const loadExecutors = useCallback(
|
const loadExecutors = useCallback(
|
||||||
() =>
|
() =>
|
||||||
editId
|
loadRoutePhase(approval, editId, requestStatus, 'Execution', () =>
|
||||||
? approval
|
getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Execution', null, fieldValues, projectId)
|
||||||
.getMatrixForRequest(editId)
|
),
|
||||||
.then((rows) => rows.filter((r) => r.phase === APPROVAL_PHASE.execution))
|
[context, userEmail, approval, editId, requestStatus, requestTypeId, fieldValues, projectId]
|
||||||
: getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Execution', null, fieldValues, projectId),
|
|
||||||
[context, userEmail, approval, editId, requestTypeId, fieldValues, projectId]
|
|
||||||
);
|
);
|
||||||
const executors = useMatrixEditor(
|
const executors = useMatrixEditor(
|
||||||
loadExecutors,
|
loadExecutors,
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import {
|
|||||||
useMatrixEditor,
|
useMatrixEditor,
|
||||||
canEditExecutors,
|
canEditExecutors,
|
||||||
getPlannedMatrix,
|
getPlannedMatrix,
|
||||||
|
loadRoutePhase,
|
||||||
|
isOpenedRouteReady,
|
||||||
notifyActiveStep,
|
notifyActiveStep,
|
||||||
useRequestTypeId,
|
useRequestTypeId,
|
||||||
REQUEST_STATUS,
|
REQUEST_STATUS,
|
||||||
@@ -30,7 +32,6 @@ import {
|
|||||||
IApproverToWrite,
|
IApproverToWrite,
|
||||||
unassignedStepMessage,
|
unassignedStepMessage,
|
||||||
resolveWriteListId,
|
resolveWriteListId,
|
||||||
APPROVAL_PHASE,
|
|
||||||
initialSubmitStatus,
|
initialSubmitStatus,
|
||||||
resolveApproverOrgInfo,
|
resolveApproverOrgInfo,
|
||||||
canEditRoute
|
canEditRoute
|
||||||
@@ -210,18 +211,18 @@ const ReferralForm: React.FC<IRequestFormProps> = ({
|
|||||||
// правки инициатора.
|
// правки инициатора.
|
||||||
const loadMatrix = useCallback(
|
const loadMatrix = useCallback(
|
||||||
() =>
|
() =>
|
||||||
editId
|
loadRoutePhase(approval, editId, requestStatus, 'Approval', () =>
|
||||||
? approval
|
getPlannedMatrix(context, requestTypeId as number, data.Referrer, approval, 'Approval')
|
||||||
.getMatrixForRequest(editId)
|
),
|
||||||
.then((rows) => rows.filter((r) => r.phase !== APPROVAL_PHASE.execution))
|
[context, approval, editId, requestStatus, data.Referrer, requestTypeId]
|
||||||
: getPlannedMatrix(context, requestTypeId as number, data.Referrer, approval, 'Approval'),
|
|
||||||
[context, approval, editId, data.Referrer, requestTypeId]
|
|
||||||
);
|
);
|
||||||
|
const matrixReady =
|
||||||
|
isOpenedRouteReady(editId, requestStatus, requestTypeId) && (!!editId || (!!data.Referrer && !!requestTypeId));
|
||||||
const matrix = useMatrixEditor(
|
const matrix = useMatrixEditor(
|
||||||
loadMatrix,
|
loadMatrix,
|
||||||
`${data.Referrer}-${editId ?? 'new'}`,
|
`${data.Referrer}-${editId ?? 'new'}`,
|
||||||
!readOnly,
|
!readOnly,
|
||||||
!!editId || (!!data.Referrer && !!requestTypeId),
|
matrixReady,
|
||||||
(login) => approval.getJobTitle(login),
|
(login) => approval.getJobTitle(login),
|
||||||
resolveApproverOrgInfo(context)
|
resolveApproverOrgInfo(context)
|
||||||
);
|
);
|
||||||
@@ -232,18 +233,16 @@ const ReferralForm: React.FC<IRequestFormProps> = ({
|
|||||||
// 'Execution' getPlannedMatrix её просто не резолвит.
|
// 'Execution' getPlannedMatrix её просто не резолвит.
|
||||||
const loadExecutors = useCallback(
|
const loadExecutors = useCallback(
|
||||||
() =>
|
() =>
|
||||||
editId
|
loadRoutePhase(approval, editId, requestStatus, 'Execution', () =>
|
||||||
? approval
|
getPlannedMatrix(context, requestTypeId as number, data.Referrer, approval, 'Execution')
|
||||||
.getMatrixForRequest(editId)
|
),
|
||||||
.then((rows) => rows.filter((r) => r.phase === APPROVAL_PHASE.execution))
|
[context, approval, editId, requestStatus, data.Referrer, requestTypeId]
|
||||||
: getPlannedMatrix(context, requestTypeId as number, data.Referrer, approval, 'Execution'),
|
|
||||||
[context, approval, editId, data.Referrer, requestTypeId]
|
|
||||||
);
|
);
|
||||||
const executors = useMatrixEditor(
|
const executors = useMatrixEditor(
|
||||||
loadExecutors,
|
loadExecutors,
|
||||||
`${data.Referrer}-${editId ?? 'new'}`,
|
`${data.Referrer}-${editId ?? 'new'}`,
|
||||||
canEditExecutors(context, readOnly),
|
canEditExecutors(context, readOnly),
|
||||||
!!editId || (!!data.Referrer && !!requestTypeId),
|
matrixReady,
|
||||||
(login) => approval.getJobTitle(login),
|
(login) => approval.getJobTitle(login),
|
||||||
resolveApproverOrgInfo(context)
|
resolveApproverOrgInfo(context)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import {
|
|||||||
StatusBadge,
|
StatusBadge,
|
||||||
useMatrixEditor,
|
useMatrixEditor,
|
||||||
getPlannedMatrix,
|
getPlannedMatrix,
|
||||||
|
loadRoutePhase,
|
||||||
|
isOpenedRouteReady,
|
||||||
notifyActiveStep,
|
notifyActiveStep,
|
||||||
useRequestTypeId,
|
useRequestTypeId,
|
||||||
REQUEST_STATUS,
|
REQUEST_STATUS,
|
||||||
@@ -28,7 +30,6 @@ import {
|
|||||||
resolveWriteListId,
|
resolveWriteListId,
|
||||||
useFormExecutors,
|
useFormExecutors,
|
||||||
ExecutorsMatrix,
|
ExecutorsMatrix,
|
||||||
approvalPhaseRows,
|
|
||||||
executorsToWrite,
|
executorsToWrite,
|
||||||
initialSubmitStatus
|
initialSubmitStatus
|
||||||
} from '../../shared/approval';
|
} from '../../shared/approval';
|
||||||
@@ -183,13 +184,14 @@ const ServiceRequestForm: React.FC<IRequestFormProps> = ({
|
|||||||
// Маршрут зависит от проекта (см. approval-route: согласующие — руководитель
|
// Маршрут зависит от проекта (см. approval-route: согласующие — руководитель
|
||||||
// проекта, ПТО объекта) — пока проект не выбран, resolve вернёт 404.
|
// проекта, ПТО объекта) — пока проект не выбран, resolve вернёт 404.
|
||||||
const projectId = data.ProjectId || null;
|
const projectId = data.ProjectId || null;
|
||||||
const matrixReady = !!editId || (!!userEmail && !!projectId && !!requestTypeId);
|
const matrixReady =
|
||||||
|
isOpenedRouteReady(editId, requestStatus, requestTypeId) && (!!editId || (!!userEmail && !!projectId && !!requestTypeId));
|
||||||
const loadMatrix = useCallback(
|
const loadMatrix = useCallback(
|
||||||
() =>
|
() =>
|
||||||
editId
|
loadRoutePhase(approval, editId, requestStatus, 'Approval', () =>
|
||||||
? approval.getMatrixForRequest(editId).then(approvalPhaseRows)
|
getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Approval', null, undefined, projectId)
|
||||||
: getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Approval', null, undefined, projectId),
|
),
|
||||||
[context, userEmail, approval, editId, projectId, requestTypeId]
|
[context, userEmail, approval, editId, requestStatus, projectId, requestTypeId]
|
||||||
);
|
);
|
||||||
const matrix = useMatrixEditor(loadMatrix, `${userEmail}-${editId ?? 'new'}-${projectId}`, !readOnly, matrixReady, (login) =>
|
const matrix = useMatrixEditor(loadMatrix, `${userEmail}-${editId ?? 'new'}-${projectId}`, !readOnly, matrixReady, (login) =>
|
||||||
approval.getJobTitle(login)
|
approval.getJobTitle(login)
|
||||||
@@ -203,6 +205,7 @@ const ServiceRequestForm: React.FC<IRequestFormProps> = ({
|
|||||||
requestTypeId,
|
requestTypeId,
|
||||||
userEmail: matrixReady ? userEmail : '',
|
userEmail: matrixReady ? userEmail : '',
|
||||||
editId,
|
editId,
|
||||||
|
requestStatus,
|
||||||
projectId
|
projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -133,6 +133,8 @@ const SkudExplanationForm: React.FC<IRequestFormProps> = ({
|
|||||||
context,
|
context,
|
||||||
approval,
|
approval,
|
||||||
requestId: editId,
|
requestId: editId,
|
||||||
|
replanEmptyDraft: true,
|
||||||
|
requestStatus,
|
||||||
employeeEmail: data.Employee,
|
employeeEmail: data.Employee,
|
||||||
requestTypeId,
|
requestTypeId,
|
||||||
editable: !readOnly,
|
editable: !readOnly,
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import {
|
|||||||
StatusBadge,
|
StatusBadge,
|
||||||
useMatrixEditor,
|
useMatrixEditor,
|
||||||
getPlannedMatrix,
|
getPlannedMatrix,
|
||||||
|
loadRoutePhase,
|
||||||
|
isOpenedRouteReady,
|
||||||
resolveWriteListId,
|
resolveWriteListId,
|
||||||
notifyActiveStep,
|
notifyActiveStep,
|
||||||
useRequestTypeId,
|
useRequestTypeId,
|
||||||
@@ -29,7 +31,6 @@ import {
|
|||||||
isEditableStatus,
|
isEditableStatus,
|
||||||
IApproverToWrite,
|
IApproverToWrite,
|
||||||
unassignedStepMessage,
|
unassignedStepMessage,
|
||||||
APPROVAL_PHASE,
|
|
||||||
executorsToWrite
|
executorsToWrite
|
||||||
} from '../../shared/approval';
|
} from '../../shared/approval';
|
||||||
import {
|
import {
|
||||||
@@ -181,15 +182,14 @@ const Ticket1CForm: React.FC<IRequestFormProps> = ({
|
|||||||
[data.WorkType, data.Department]
|
[data.WorkType, data.Department]
|
||||||
);
|
);
|
||||||
const matrixKey = `${userEmail}-${editId ?? 'new'}-${data.WorkType}-${data.Department}`;
|
const matrixKey = `${userEmail}-${editId ?? 'new'}-${data.WorkType}-${data.Department}`;
|
||||||
const matrixReady = !!editId || (!!data.WorkType && !!requestTypeId);
|
const matrixReady =
|
||||||
|
isOpenedRouteReady(editId, requestStatus, requestTypeId) && (!!editId || (!!data.WorkType && !!requestTypeId));
|
||||||
const loadMatrix = useCallback(
|
const loadMatrix = useCallback(
|
||||||
() =>
|
() =>
|
||||||
editId
|
loadRoutePhase(approval, editId, requestStatus, 'Approval', () =>
|
||||||
? approval
|
getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Approval', null, fieldValues)
|
||||||
.getMatrixForRequest(editId)
|
),
|
||||||
.then((rows) => rows.filter((r) => r.phase !== APPROVAL_PHASE.execution))
|
[context, userEmail, approval, editId, requestStatus, fieldValues, requestTypeId]
|
||||||
: getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Approval', null, fieldValues),
|
|
||||||
[context, userEmail, approval, editId, fieldValues, requestTypeId]
|
|
||||||
);
|
);
|
||||||
const matrix = useMatrixEditor(loadMatrix, matrixKey, false, matrixReady, (login) => approval.getJobTitle(login));
|
const matrix = useMatrixEditor(loadMatrix, matrixKey, false, matrixReady, (login) => approval.getJobTitle(login));
|
||||||
|
|
||||||
@@ -197,12 +197,10 @@ const Ticket1CForm: React.FC<IRequestFormProps> = ({
|
|||||||
// в карточке Planner, в портале отмечает «Выполнено» (см. plannerHandoff.ts).
|
// в карточке Planner, в портале отмечает «Выполнено» (см. plannerHandoff.ts).
|
||||||
const loadExecutors = useCallback(
|
const loadExecutors = useCallback(
|
||||||
() =>
|
() =>
|
||||||
editId
|
loadRoutePhase(approval, editId, requestStatus, 'Execution', () =>
|
||||||
? approval
|
getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Execution', null, fieldValues)
|
||||||
.getMatrixForRequest(editId)
|
),
|
||||||
.then((rows) => rows.filter((r) => r.phase === APPROVAL_PHASE.execution))
|
[context, userEmail, approval, editId, requestStatus, fieldValues, requestTypeId]
|
||||||
: getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Execution', null, fieldValues),
|
|
||||||
[context, userEmail, approval, editId, fieldValues, requestTypeId]
|
|
||||||
);
|
);
|
||||||
const executors = useMatrixEditor(loadExecutors, matrixKey, false, matrixReady, (login) => approval.getJobTitle(login));
|
const executors = useMatrixEditor(loadExecutors, matrixKey, false, matrixReady, (login) => approval.getJobTitle(login));
|
||||||
// Должность нового «Руководителя» — approval-route её не знает (резолвил шаг
|
// Должность нового «Руководителя» — approval-route её не знает (резолвил шаг
|
||||||
|
|||||||
@@ -24,12 +24,13 @@ import {
|
|||||||
useMatrixEditor,
|
useMatrixEditor,
|
||||||
canEditExecutors,
|
canEditExecutors,
|
||||||
getPlannedMatrix,
|
getPlannedMatrix,
|
||||||
|
loadRoutePhase,
|
||||||
|
isOpenedRouteReady,
|
||||||
notifyActiveStep,
|
notifyActiveStep,
|
||||||
useRequestTypeId,
|
useRequestTypeId,
|
||||||
REQUEST_STATUS,
|
REQUEST_STATUS,
|
||||||
isEditableStatus,
|
isEditableStatus,
|
||||||
IApproverToWrite,
|
IApproverToWrite,
|
||||||
APPROVAL_PHASE,
|
|
||||||
unassignedStepMessage,
|
unassignedStepMessage,
|
||||||
resolveWriteListId
|
resolveWriteListId
|
||||||
} from '../../shared/approval';
|
} from '../../shared/approval';
|
||||||
@@ -301,18 +302,18 @@ const TmcTransferForm: React.FC<IRequestFormProps> = ({
|
|||||||
// Маршрут согласования зависит от заявителя и проекта-источника («Откуда»):
|
// Маршрут согласования зависит от заявителя и проекта-источника («Откуда»):
|
||||||
// пока не выбраны, resolve вернёт 404 — не грузим, показываем заглушку. У
|
// пока не выбраны, resolve вернёт 404 — не грузим, показываем заглушку. У
|
||||||
// открытой на редактирование заявки маршрут уже записан, ни то ни другое не нужно.
|
// открытой на редактирование заявки маршрут уже записан, ни то ни другое не нужно.
|
||||||
const matrixReady = !!editId || (!!userEmail && !!projectId && !!requestTypeId);
|
const openedReady = isOpenedRouteReady(editId, requestStatus, requestTypeId);
|
||||||
|
const matrixReady = openedReady && (!!editId || (!!userEmail && !!projectId && !!requestTypeId));
|
||||||
// Исполнитель определяется проектом-получателем («Куда»): показываем блок с
|
// Исполнитель определяется проектом-получателем («Куда»): показываем блок с
|
||||||
// заглушкой, пока «Куда» не выбрано. Источник и заявитель тоже нужны, иначе resolve 404.
|
// заглушкой, пока «Куда» не выбрано. Источник и заявитель тоже нужны, иначе resolve 404.
|
||||||
const executorsReady = !!editId || (!!userEmail && !!projectId && !!targetProjectId && !!requestTypeId);
|
const executorsReady =
|
||||||
|
openedReady && (!!editId || (!!userEmail && !!projectId && !!targetProjectId && !!requestTypeId));
|
||||||
const loadMatrix = useCallback(
|
const loadMatrix = useCallback(
|
||||||
() =>
|
() =>
|
||||||
editId
|
loadRoutePhase(approval, editId, requestStatus, 'Approval', () =>
|
||||||
? approval
|
getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Approval', null, undefined, projectId)
|
||||||
.getMatrixForRequest(editId)
|
),
|
||||||
.then((rows) => rows.filter((r) => r.phase !== APPROVAL_PHASE.execution))
|
[context, userEmail, approval, editId, requestStatus, projectId, requestTypeId]
|
||||||
: getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Approval', null, undefined, projectId),
|
|
||||||
[context, userEmail, approval, editId, projectId, requestTypeId]
|
|
||||||
);
|
);
|
||||||
const matrix = useMatrixEditor(loadMatrix, `${userEmail}-${editId ?? 'new'}-${projectId}`, !readOnly, matrixReady, (login) =>
|
const matrix = useMatrixEditor(loadMatrix, `${userEmail}-${editId ?? 'new'}-${projectId}`, !readOnly, matrixReady, (login) =>
|
||||||
approval.getJobTitle(login)
|
approval.getJobTitle(login)
|
||||||
@@ -321,12 +322,10 @@ const TmcTransferForm: React.FC<IRequestFormProps> = ({
|
|||||||
// Фаза исполнения: исполнители из маршрута (status='Execution'), только для чтения.
|
// Фаза исполнения: исполнители из маршрута (status='Execution'), только для чтения.
|
||||||
const loadExecutors = useCallback(
|
const loadExecutors = useCallback(
|
||||||
() =>
|
() =>
|
||||||
editId
|
loadRoutePhase(approval, editId, requestStatus, 'Execution', () =>
|
||||||
? approval
|
getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Execution', null, undefined, projectId)
|
||||||
.getMatrixForRequest(editId)
|
),
|
||||||
.then((rows) => rows.filter((r) => r.phase === APPROVAL_PHASE.execution))
|
[context, userEmail, approval, editId, requestStatus, projectId, requestTypeId]
|
||||||
: getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Execution', null, undefined, projectId),
|
|
||||||
[context, userEmail, approval, editId, projectId, requestTypeId]
|
|
||||||
);
|
);
|
||||||
const executors = useMatrixEditor(
|
const executors = useMatrixEditor(
|
||||||
loadExecutors,
|
loadExecutors,
|
||||||
|
|||||||
@@ -22,10 +22,11 @@ import {
|
|||||||
StatusBadge,
|
StatusBadge,
|
||||||
useMatrixEditor,
|
useMatrixEditor,
|
||||||
useFormExecutors,
|
useFormExecutors,
|
||||||
approvalPhaseRows,
|
|
||||||
executorsToWrite,
|
executorsToWrite,
|
||||||
initialSubmitStatus,
|
initialSubmitStatus,
|
||||||
getPlannedMatrix,
|
getPlannedMatrix,
|
||||||
|
loadRoutePhase,
|
||||||
|
isOpenedRouteReady,
|
||||||
notifyActiveStep,
|
notifyActiveStep,
|
||||||
useRequestTypeId,
|
useRequestTypeId,
|
||||||
REQUEST_STATUS,
|
REQUEST_STATUS,
|
||||||
@@ -162,16 +163,16 @@ const UnpaidLeaveForm: React.FC<IRequestFormProps> = ({
|
|||||||
// маршрут читается из уже записанных строк, чтобы не затереть правки инициатора.
|
// маршрут читается из уже записанных строк, чтобы не затереть правки инициатора.
|
||||||
const loadMatrix = useCallback(
|
const loadMatrix = useCallback(
|
||||||
() =>
|
() =>
|
||||||
editId
|
loadRoutePhase(approval, editId, requestStatus, 'Approval', () =>
|
||||||
? approval.getMatrixForRequest(editId).then(approvalPhaseRows)
|
getPlannedMatrix(context, requestTypeId as number, data.Employee, approval, 'Approval')
|
||||||
: getPlannedMatrix(context, requestTypeId as number, data.Employee, approval, 'Approval'),
|
),
|
||||||
[context, approval, editId, data.Employee, requestTypeId]
|
[context, approval, editId, requestStatus, data.Employee, requestTypeId]
|
||||||
);
|
);
|
||||||
const matrix = useMatrixEditor(
|
const matrix = useMatrixEditor(
|
||||||
loadMatrix,
|
loadMatrix,
|
||||||
`${data.Employee}-${editId ?? 'new'}`,
|
`${data.Employee}-${editId ?? 'new'}`,
|
||||||
!readOnly,
|
!readOnly,
|
||||||
!!editId || (!!data.Employee && !!requestTypeId),
|
isOpenedRouteReady(editId, requestStatus, requestTypeId) && (!!editId || (!!data.Employee && !!requestTypeId)),
|
||||||
(login) => approval.getJobTitle(login)
|
(login) => approval.getJobTitle(login)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -182,7 +183,8 @@ const UnpaidLeaveForm: React.FC<IRequestFormProps> = ({
|
|||||||
approval,
|
approval,
|
||||||
requestTypeId,
|
requestTypeId,
|
||||||
userEmail: data.Employee,
|
userEmail: data.Employee,
|
||||||
editId
|
editId,
|
||||||
|
requestStatus
|
||||||
});
|
});
|
||||||
|
|
||||||
// Проблемы полей (см. validation.ts) подсвечиваются под полями через FieldIssuesProvider.
|
// Проблемы полей (см. validation.ts) подсвечиваются под полями через FieldIssuesProvider.
|
||||||
|
|||||||
@@ -20,10 +20,11 @@ import {
|
|||||||
useMatrixEditor,
|
useMatrixEditor,
|
||||||
useFormExecutors,
|
useFormExecutors,
|
||||||
ExecutorsMatrix,
|
ExecutorsMatrix,
|
||||||
approvalPhaseRows,
|
|
||||||
executorsToWrite,
|
executorsToWrite,
|
||||||
initialSubmitStatus,
|
initialSubmitStatus,
|
||||||
getPlannedMatrix,
|
getPlannedMatrix,
|
||||||
|
loadRoutePhase,
|
||||||
|
isOpenedRouteReady,
|
||||||
resolveWriteListId,
|
resolveWriteListId,
|
||||||
notifyActiveStep,
|
notifyActiveStep,
|
||||||
useRequestTypeId,
|
useRequestTypeId,
|
||||||
@@ -170,16 +171,16 @@ const VacationForm: React.FC<IRequestFormProps> = ({
|
|||||||
// маршрут читается из уже записанных строк, чтобы не затереть правки инициатора.
|
// маршрут читается из уже записанных строк, чтобы не затереть правки инициатора.
|
||||||
const loadMatrix = useCallback(
|
const loadMatrix = useCallback(
|
||||||
() =>
|
() =>
|
||||||
editId
|
loadRoutePhase(approval, editId, requestStatus, 'Approval', () =>
|
||||||
? approval.getMatrixForRequest(editId).then(approvalPhaseRows)
|
getPlannedMatrix(context, requestTypeId as number, data.Employee, approval, 'Approval')
|
||||||
: getPlannedMatrix(context, requestTypeId as number, data.Employee, approval, 'Approval'),
|
),
|
||||||
[context, approval, editId, data.Employee, requestTypeId]
|
[context, approval, editId, requestStatus, data.Employee, requestTypeId]
|
||||||
);
|
);
|
||||||
const matrix = useMatrixEditor(
|
const matrix = useMatrixEditor(
|
||||||
loadMatrix,
|
loadMatrix,
|
||||||
`${data.Employee}-${editId ?? 'new'}`,
|
`${data.Employee}-${editId ?? 'new'}`,
|
||||||
!readOnly,
|
!readOnly,
|
||||||
!!editId || (!!data.Employee && !!requestTypeId),
|
isOpenedRouteReady(editId, requestStatus, requestTypeId) && (!!editId || (!!data.Employee && !!requestTypeId)),
|
||||||
(login) => approval.getJobTitle(login)
|
(login) => approval.getJobTitle(login)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -191,7 +192,8 @@ const VacationForm: React.FC<IRequestFormProps> = ({
|
|||||||
approval,
|
approval,
|
||||||
requestTypeId,
|
requestTypeId,
|
||||||
userEmail: data.Employee,
|
userEmail: data.Employee,
|
||||||
editId
|
editId,
|
||||||
|
requestStatus
|
||||||
});
|
});
|
||||||
|
|
||||||
const daysNum = Number(data.DaysCount);
|
const daysNum = Number(data.DaysCount);
|
||||||
|
|||||||
@@ -27,13 +27,14 @@ import {
|
|||||||
useMatrixEditor,
|
useMatrixEditor,
|
||||||
canEditExecutors,
|
canEditExecutors,
|
||||||
getPlannedMatrix,
|
getPlannedMatrix,
|
||||||
|
loadRoutePhase,
|
||||||
|
isOpenedRouteReady,
|
||||||
resolveWriteListId,
|
resolveWriteListId,
|
||||||
notifyActiveStep,
|
notifyActiveStep,
|
||||||
useRequestTypeId,
|
useRequestTypeId,
|
||||||
REQUEST_STATUS,
|
REQUEST_STATUS,
|
||||||
isEditableStatus,
|
isEditableStatus,
|
||||||
IApproverToWrite,
|
IApproverToWrite,
|
||||||
APPROVAL_PHASE,
|
|
||||||
IResolveFieldValue,
|
IResolveFieldValue,
|
||||||
unassignedStepMessage
|
unassignedStepMessage
|
||||||
} from '../../shared/approval';
|
} from '../../shared/approval';
|
||||||
@@ -182,36 +183,33 @@ const WorkplaceSetupForm: React.FC<IRequestFormProps> = ({
|
|||||||
// вручную при первом сохранении, и планирование затёрло бы эти правки.
|
// вручную при первом сохранении, и планирование затёрло бы эти правки.
|
||||||
const loadMatrix = useCallback(
|
const loadMatrix = useCallback(
|
||||||
() =>
|
() =>
|
||||||
editId
|
loadRoutePhase(approval, editId, requestStatus, 'Approval', () =>
|
||||||
? approval
|
getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Approval', data.DepartmentId, fieldValues)
|
||||||
.getMatrixForRequest(editId)
|
),
|
||||||
.then((rows) => rows.filter((r) => r.phase !== APPROVAL_PHASE.execution))
|
[context, userEmail, approval, editId, requestStatus, data.DepartmentId, requestTypeId, fieldValues]
|
||||||
: getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Approval', data.DepartmentId, fieldValues),
|
|
||||||
[context, userEmail, approval, editId, data.DepartmentId, requestTypeId, fieldValues]
|
|
||||||
);
|
);
|
||||||
|
const matrixReady = isOpenedRouteReady(editId, requestStatus, requestTypeId) && (!!editId || !!requestTypeId);
|
||||||
const matrix = useMatrixEditor(
|
const matrix = useMatrixEditor(
|
||||||
loadMatrix,
|
loadMatrix,
|
||||||
matrixKey,
|
matrixKey,
|
||||||
!readOnly,
|
!readOnly,
|
||||||
!!editId || !!requestTypeId,
|
matrixReady,
|
||||||
(login) => approval.getJobTitle(login)
|
(login) => approval.getJobTitle(login)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Фаза исполнения: исполнители из маршрута (status='Execution'), только для чтения.
|
// Фаза исполнения: исполнители из маршрута (status='Execution'), только для чтения.
|
||||||
const loadExecutors = useCallback(
|
const loadExecutors = useCallback(
|
||||||
() =>
|
() =>
|
||||||
editId
|
loadRoutePhase(approval, editId, requestStatus, 'Execution', () =>
|
||||||
? approval
|
getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Execution', data.DepartmentId, fieldValues)
|
||||||
.getMatrixForRequest(editId)
|
),
|
||||||
.then((rows) => rows.filter((r) => r.phase === APPROVAL_PHASE.execution))
|
[context, userEmail, approval, editId, requestStatus, data.DepartmentId, requestTypeId, fieldValues]
|
||||||
: getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Execution', data.DepartmentId, fieldValues),
|
|
||||||
[context, userEmail, approval, editId, data.DepartmentId, requestTypeId, fieldValues]
|
|
||||||
);
|
);
|
||||||
const executors = useMatrixEditor(
|
const executors = useMatrixEditor(
|
||||||
loadExecutors,
|
loadExecutors,
|
||||||
matrixKey,
|
matrixKey,
|
||||||
canEditExecutors(context, readOnly),
|
canEditExecutors(context, readOnly),
|
||||||
!!editId || !!requestTypeId,
|
matrixReady,
|
||||||
(login) => approval.getJobTitle(login)
|
(login) => approval.getJobTitle(login)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,8 @@ export {
|
|||||||
useFormExecutors,
|
useFormExecutors,
|
||||||
useViewExecutors
|
useViewExecutors
|
||||||
} from './useRouteExecutors';
|
} from './useRouteExecutors';
|
||||||
|
export { loadRoutePhase, isOpenedRouteReady } from './loadRoutePhase';
|
||||||
|
export type { RoutePhase } from './loadRoutePhase';
|
||||||
export { usePhasedRoute } from './usePhasedRoute';
|
export { usePhasedRoute } from './usePhasedRoute';
|
||||||
export type { IPhasedRoute } from './usePhasedRoute';
|
export type { IPhasedRoute } from './usePhasedRoute';
|
||||||
export { resolveApproverOrgInfo } from './approverOrgInfo';
|
export { resolveApproverOrgInfo } from './approverOrgInfo';
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { ApprovalListService } from './approvalList.service';
|
||||||
|
import { IMatrixRow } from './models/IMatrixRow';
|
||||||
|
import { APPROVAL_PHASE, REQUEST_STATUS } from './models/statuses';
|
||||||
|
|
||||||
|
/** Фаза маршрута, которую грузит форма заполнения. */
|
||||||
|
export type RoutePhase = 'Approval' | 'Execution';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Маршрут одной фазы для формы заполнения:
|
||||||
|
* - новая заявка (requestId нет) — планируемый маршрут из админки (plan);
|
||||||
|
* - открытая заявка — уже записанные строки: в них правки инициатора, их не затираем;
|
||||||
|
* - исключение — ЧЕРНОВИК без единой записанной строки маршрута (ни в одной
|
||||||
|
* фазе): при его сохранении маршрут не пришёл (ошибка resolve, ещё не выбран
|
||||||
|
* проект и т.п.). Такой черновик запрашивает маршрут заново, как новая заявка, —
|
||||||
|
* иначе он навсегда остался бы без согласующих. Черновик с записанной (в том
|
||||||
|
* числе поправленной инициатором) матрицей не трогаем.
|
||||||
|
*
|
||||||
|
* У открытой заявки requestStatus должен быть уже загружен — форма держит
|
||||||
|
* матрицу неготовой, пока он undefined (см. isOpenedRouteReady).
|
||||||
|
*/
|
||||||
|
export async function loadRoutePhase(
|
||||||
|
approval: ApprovalListService,
|
||||||
|
requestId: number | undefined,
|
||||||
|
requestStatus: string | undefined,
|
||||||
|
phase: RoutePhase,
|
||||||
|
plan: () => Promise<IMatrixRow[]>
|
||||||
|
): Promise<IMatrixRow[]> {
|
||||||
|
if (!requestId) return plan();
|
||||||
|
const saved = await approval.getMatrixForRequest(requestId);
|
||||||
|
if (saved.length === 0 && requestStatus === REQUEST_STATUS.draft) return plan();
|
||||||
|
const execution = phase === 'Execution';
|
||||||
|
return saved.filter((r) => (r.phase === APPROVAL_PHASE.execution) === execution);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Можно ли грузить маршрут ОТКРЫТОЙ заявки через loadRoutePhase: нужен статус
|
||||||
|
* (черновик ли это) и тип заявки (по нему черновик без строк планирует маршрут
|
||||||
|
* заново). Иначе загрузка ушла бы раньше времени и не повторилась. У новой
|
||||||
|
* заявки — всегда true: её готовность решает сама форма.
|
||||||
|
*/
|
||||||
|
export function isOpenedRouteReady(
|
||||||
|
requestId: number | undefined,
|
||||||
|
requestStatus: string | undefined,
|
||||||
|
requestTypeId: number | undefined
|
||||||
|
): boolean {
|
||||||
|
return !requestId || (requestStatus !== undefined && !!requestTypeId);
|
||||||
|
}
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { WebPartContext } from '@microsoft/sp-webpart-base';
|
import { WebPartContext } from '@microsoft/sp-webpart-base';
|
||||||
import { APPROVAL_PHASE } from './models/statuses';
|
|
||||||
import { IMatrixRow } from './models/IMatrixRow';
|
|
||||||
import { ApprovalListService } from './approvalList.service';
|
|
||||||
import { getPlannedMatrix } from './approvalSubmission.service';
|
import { getPlannedMatrix } from './approvalSubmission.service';
|
||||||
|
import { ApprovalListService } from './approvalList.service';
|
||||||
import { resolveApproverOrgInfo } from './approverOrgInfo';
|
import { resolveApproverOrgInfo } from './approverOrgInfo';
|
||||||
import { useMatrixEditor } from './useMatrixEditor';
|
import { useMatrixEditor } from './useMatrixEditor';
|
||||||
|
import { isOpenedRouteReady, loadRoutePhase, RoutePhase } from './loadRoutePhase';
|
||||||
|
|
||||||
type MatrixEditor = ReturnType<typeof useMatrixEditor>;
|
type MatrixEditor = ReturnType<typeof useMatrixEditor>;
|
||||||
|
|
||||||
@@ -21,6 +20,14 @@ interface IRouteSource {
|
|||||||
approval: ApprovalListService;
|
approval: ApprovalListService;
|
||||||
/** Заявка уже записана — маршрут читаем из её строк, а не планируем заново. */
|
/** Заявка уже записана — маршрут читаем из её строк, а не планируем заново. */
|
||||||
requestId: number | undefined;
|
requestId: number | undefined;
|
||||||
|
/**
|
||||||
|
* true — форма заполнения: черновик без записанных строк маршрута запрашивает
|
||||||
|
* маршрут заново (см. loadRoutePhase), а пока requestStatus не загружен,
|
||||||
|
* маршрут не грузится. Просмотр не включает — всегда записанные строки.
|
||||||
|
*/
|
||||||
|
replanEmptyDraft?: boolean;
|
||||||
|
/** Статус открытой заявки — нужен только при replanEmptyDraft. */
|
||||||
|
requestStatus?: string;
|
||||||
/** E-mail сотрудника, по которому резолвится планируемый маршрут. */
|
/** E-mail сотрудника, по которому резолвится планируемый маршрут. */
|
||||||
employeeEmail: string;
|
employeeEmail: string;
|
||||||
requestTypeId: number | undefined;
|
requestTypeId: number | undefined;
|
||||||
@@ -32,11 +39,6 @@ interface IRouteSource {
|
|||||||
reloadSeq?: number;
|
reloadSeq?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const byPhase =
|
|
||||||
(execution: boolean): ((rows: IMatrixRow[]) => IMatrixRow[]) =>
|
|
||||||
(rows: IMatrixRow[]): IMatrixRow[] =>
|
|
||||||
rows.filter((r) => (r.phase === APPROVAL_PHASE.execution) === execution);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Маршрут заявки по двум фазам: согласование + исполнение. Без отдельного
|
* Маршрут заявки по двум фазам: согласование + исполнение. Без отдельного
|
||||||
* запроса фазы 'Execution' getPlannedMatrix исполнителей не резолвит — шаг
|
* запроса фазы 'Execution' getPlannedMatrix исполнителей не резолвит — шаг
|
||||||
@@ -44,25 +46,36 @@ const byPhase =
|
|||||||
* approval-route, форма его только получает.
|
* approval-route, форма его только получает.
|
||||||
*/
|
*/
|
||||||
export function usePhasedRoute(source: IRouteSource): IPhasedRoute {
|
export function usePhasedRoute(source: IRouteSource): IPhasedRoute {
|
||||||
const { context, approval, requestId, employeeEmail, requestTypeId, editable, executorsEditable = false, reloadSeq = 0 } = source;
|
const {
|
||||||
const canLoad = !!requestId || (!!employeeEmail && !!requestTypeId);
|
context,
|
||||||
|
approval,
|
||||||
|
requestId,
|
||||||
|
replanEmptyDraft = false,
|
||||||
|
requestStatus,
|
||||||
|
employeeEmail,
|
||||||
|
requestTypeId,
|
||||||
|
editable,
|
||||||
|
executorsEditable = false,
|
||||||
|
reloadSeq = 0
|
||||||
|
} = source;
|
||||||
|
const statusReady = !replanEmptyDraft || isOpenedRouteReady(requestId, requestStatus, requestTypeId);
|
||||||
|
const canLoad = statusReady && (!!requestId || (!!employeeEmail && !!requestTypeId));
|
||||||
const reloadKey = `${employeeEmail}-${requestId ?? 'new'}-${reloadSeq}`;
|
const reloadKey = `${employeeEmail}-${requestId ?? 'new'}-${reloadSeq}`;
|
||||||
|
|
||||||
const loadPhase = useCallback(
|
const loadPhase = useCallback(
|
||||||
(execution: boolean) =>
|
(phase: RoutePhase) =>
|
||||||
requestId
|
loadRoutePhase(
|
||||||
? approval.getMatrixForRequest(requestId).then(byPhase(execution))
|
approval,
|
||||||
: getPlannedMatrix(
|
requestId,
|
||||||
context,
|
// Без replanEmptyDraft (просмотр) черновик не перепланируется — только записанные строки.
|
||||||
requestTypeId as number,
|
replanEmptyDraft ? requestStatus : undefined,
|
||||||
employeeEmail,
|
phase,
|
||||||
approval,
|
() => getPlannedMatrix(context, requestTypeId as number, employeeEmail, approval, phase)
|
||||||
execution ? 'Execution' : 'Approval'
|
),
|
||||||
),
|
[context, approval, requestId, replanEmptyDraft, requestStatus, employeeEmail, requestTypeId]
|
||||||
[context, approval, requestId, employeeEmail, requestTypeId]
|
|
||||||
);
|
);
|
||||||
const loadApprovers = useCallback(() => loadPhase(false), [loadPhase]);
|
const loadApprovers = useCallback(() => loadPhase('Approval'), [loadPhase]);
|
||||||
const loadExecutors = useCallback(() => loadPhase(true), [loadPhase]);
|
const loadExecutors = useCallback(() => loadPhase('Execution'), [loadPhase]);
|
||||||
const jobTitle = (login: string): Promise<string> => approval.getJobTitle(login);
|
const jobTitle = (login: string): Promise<string> => approval.getJobTitle(login);
|
||||||
|
|
||||||
const approvers = useMatrixEditor(loadApprovers, reloadKey, editable, canLoad, jobTitle, resolveApproverOrgInfo(context));
|
const approvers = useMatrixEditor(loadApprovers, reloadKey, editable, canLoad, jobTitle, resolveApproverOrgInfo(context));
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { IMatrixRow } from './models/IMatrixRow';
|
|||||||
import { APPROVAL_PHASE } from './models/statuses';
|
import { APPROVAL_PHASE } from './models/statuses';
|
||||||
import { canEditExecutors } from './routeEditRights';
|
import { canEditExecutors } from './routeEditRights';
|
||||||
import { IUseMatrixEditor, useMatrixEditor } from './useMatrixEditor';
|
import { IUseMatrixEditor, useMatrixEditor } from './useMatrixEditor';
|
||||||
|
import { isOpenedRouteReady, loadRoutePhase } from './loadRoutePhase';
|
||||||
|
|
||||||
/** Строки фазы согласования (всё, что не «Исполнение») — для таблицы согласующих. */
|
/** Строки фазы согласования (всё, что не «Исполнение») — для таблицы согласующих. */
|
||||||
export const approvalPhaseRows = (rows: IMatrixRow[]): IMatrixRow[] =>
|
export const approvalPhaseRows = (rows: IMatrixRow[]): IMatrixRow[] =>
|
||||||
@@ -43,6 +44,11 @@ interface IFormExecutorsSource {
|
|||||||
userEmail: string;
|
userEmail: string;
|
||||||
/** Редактируемая заявка — исполнители читаются из её строк, а не планируются заново. */
|
/** Редактируемая заявка — исполнители читаются из её строк, а не планируются заново. */
|
||||||
editId: number | undefined;
|
editId: number | undefined;
|
||||||
|
/**
|
||||||
|
* Статус открытой заявки: черновик без записанных строк маршрута планирует
|
||||||
|
* исполнителей заново (см. loadRoutePhase); пока не загружен — не грузим.
|
||||||
|
*/
|
||||||
|
requestStatus: string | undefined;
|
||||||
/** Те же параметры, что форма отдаёт getPlannedMatrix для согласующих. */
|
/** Те же параметры, что форма отдаёт getPlannedMatrix для согласующих. */
|
||||||
departmentId?: string | null; // eslint-disable-line @rushstack/no-new-null -- транзитом в resolveRoute
|
departmentId?: string | null; // eslint-disable-line @rushstack/no-new-null -- транзитом в resolveRoute
|
||||||
fieldValues?: IResolveFieldValue[];
|
fieldValues?: IResolveFieldValue[];
|
||||||
@@ -60,22 +66,22 @@ interface IFormExecutorsSource {
|
|||||||
* (см. canEditExecutors) может добавлять и удалять исполнителей.
|
* (см. canEditExecutors) может добавлять и удалять исполнителей.
|
||||||
*/
|
*/
|
||||||
export function useFormExecutors(source: IFormExecutorsSource): IUseMatrixEditor {
|
export function useFormExecutors(source: IFormExecutorsSource): IUseMatrixEditor {
|
||||||
const { context, approval, requestTypeId, userEmail, editId, departmentId, fieldValues, projectId, reloadSuffix = '', readOnly = false } = source;
|
const { context, approval, requestTypeId, userEmail, editId, requestStatus, departmentId, fieldValues, projectId, reloadSuffix = '', readOnly = false } = source;
|
||||||
const fieldValuesKey = JSON.stringify(fieldValues ?? []);
|
const fieldValuesKey = JSON.stringify(fieldValues ?? []);
|
||||||
const load = useCallback(
|
const load = useCallback(
|
||||||
() =>
|
() =>
|
||||||
editId
|
loadRoutePhase(approval, editId, requestStatus, 'Execution', () =>
|
||||||
? approval.getMatrixForRequest(editId).then(executionPhaseRows)
|
getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Execution', departmentId, fieldValues, projectId)
|
||||||
: getPlannedMatrix(context, requestTypeId as number, userEmail, approval, 'Execution', departmentId, fieldValues, projectId),
|
),
|
||||||
// fieldValues сравниваем по содержимому: форма пересоздаёт массив на каждый рендер.
|
// fieldValues сравниваем по содержимому: форма пересоздаёт массив на каждый рендер.
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
[context, approval, editId, requestTypeId, userEmail, departmentId, fieldValuesKey, projectId]
|
[context, approval, editId, requestStatus, requestTypeId, userEmail, departmentId, fieldValuesKey, projectId]
|
||||||
);
|
);
|
||||||
return useMatrixEditor(
|
return useMatrixEditor(
|
||||||
load,
|
load,
|
||||||
`x-${userEmail}-${editId ?? 'new'}-${departmentId ?? ''}-${projectId ?? ''}-${fieldValuesKey}-${reloadSuffix}`,
|
`x-${userEmail}-${editId ?? 'new'}-${departmentId ?? ''}-${projectId ?? ''}-${fieldValuesKey}-${reloadSuffix}`,
|
||||||
canEditExecutors(context, readOnly),
|
canEditExecutors(context, readOnly),
|
||||||
!!editId || (!!userEmail && !!requestTypeId),
|
isOpenedRouteReady(editId, requestStatus, requestTypeId) && (!!editId || (!!userEmail && !!requestTypeId)),
|
||||||
(login) => approval.getJobTitle(login),
|
(login) => approval.getJobTitle(login),
|
||||||
resolveApproverOrgInfo(context)
|
resolveApproverOrgInfo(context)
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user