feat: добавление согласующих и исполнителей в поданные заявки на подбор и приём
Алтынай Бултурукова (право canEditRoute) просила добавлять участников в уже поданный маршрут. Новый согласующий или исполнитель встаёт в конец своего этапа со статусом «Ждёт очереди» (дальше его можно поднять стрелками); согласующего — пока идёт согласование, исполнителя — пока не завершено исполнение, без дублей среди ещё не решивших. Заявка «трогается», чтобы бэкенд выдал новому участнику права на элемент данных. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -36,6 +36,7 @@ import {
|
||||
queuedStepMoveRule,
|
||||
useQueuedStepMove
|
||||
} from '../../shared/approval';
|
||||
import { useRouteStepAdd } from '../../shared/approval/useRouteStepAdd';
|
||||
import { navigate } from '../../shared/routing';
|
||||
import { effectiveUserEmail } from '../../shared/impersonation';
|
||||
import { getAttachments } from '../../shared/attachments/attachments.service';
|
||||
@@ -280,6 +281,43 @@ const RecruitingHireView: React.FC<IRecruitingHireViewProps> = ({ context, itemI
|
||||
setRouteError
|
||||
);
|
||||
|
||||
// Выше ранних return (загрузка/ошибка): зовётся и из колбэков хуков ниже.
|
||||
const notify = (): void => {
|
||||
if (!requestTypeId) return;
|
||||
notifyActiveStep(context, approval, itemId, {
|
||||
requestTypeId,
|
||||
dataListName: listId ? dataListTitleForListId(FORM_ID, listId) : dataListTitle(FORM_ID),
|
||||
approvalListName: listId ? approvalListTitleForListId(FORM_ID, listId) : approvalListTitle(FORM_ID),
|
||||
}).catch(() => undefined);
|
||||
};
|
||||
|
||||
// Добавить согласующего/исполнителя в поданную заявку — то же право
|
||||
// routeEditRights.ts; правило «в конец этапа, пока он не завершён» — routeStepAdd.ts.
|
||||
// Новый участник обычно ждёт очереди; если стал активным — сообщаем ему.
|
||||
const handleStepAdded = (activatedRowIds: number[]): void => {
|
||||
setDecisionSeq((n) => n + 1);
|
||||
if (activatedRowIds.length > 0) notify();
|
||||
};
|
||||
const handleAddApprover = useRouteStepAdd(
|
||||
approverService,
|
||||
itemId,
|
||||
APPROVAL_PHASE.approval,
|
||||
handleStepAdded,
|
||||
setRouteError
|
||||
);
|
||||
const handleAddExecutor = useRouteStepAdd(
|
||||
approverService,
|
||||
itemId,
|
||||
APPROVAL_PHASE.execution,
|
||||
handleStepAdded,
|
||||
setRouteError
|
||||
);
|
||||
// По статусу заявки: согласующих — пока идёт согласование, исполнителей —
|
||||
// пока не завершено исполнение. Сервер перепроверит по строкам (routeStepAdd.ts).
|
||||
const canAddApprover = canRemoveApprover && status === REQUEST_STATUS.onApproval;
|
||||
const canAddExecutor =
|
||||
canRemoveApprover && (status === REQUEST_STATUS.onApproval || status === REQUEST_STATUS.execution);
|
||||
|
||||
useEffect(() => {
|
||||
let alive = true;
|
||||
setLoading(true);
|
||||
@@ -321,15 +359,6 @@ const RecruitingHireView: React.FC<IRecruitingHireViewProps> = ({ context, itemI
|
||||
|
||||
const sectionProps = { data, set, context, payrollEditable, hireDateEditable };
|
||||
|
||||
const notify = (): void => {
|
||||
if (!requestTypeId) return;
|
||||
notifyActiveStep(context, approval, itemId, {
|
||||
requestTypeId,
|
||||
dataListName: listId ? dataListTitleForListId(FORM_ID, listId) : dataListTitle(FORM_ID),
|
||||
approvalListName: listId ? approvalListTitleForListId(FORM_ID, listId) : approvalListTitle(FORM_ID),
|
||||
}).catch(() => undefined);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={`${styles.toolbar} rp-no-print`}>
|
||||
@@ -410,17 +439,21 @@ const RecruitingHireView: React.FC<IRecruitingHireViewProps> = ({ context, itemI
|
||||
onRemoveRow={handleRemoveApprover}
|
||||
canMoveRow={canRemoveApprover ? queuedStepMoveRule(approvers.rows) : undefined}
|
||||
onMoveRow={handleMoveStep}
|
||||
onAddRow={canAddApprover ? handleAddApprover : undefined}
|
||||
/>
|
||||
|
||||
{!executors.loading && executors.rows.length > 0 && (
|
||||
{/* Без исполнителей блок показываем, только если их можно добавить. */}
|
||||
{!executors.loading && (executors.rows.length > 0 || canAddExecutor) && (
|
||||
<ApprovalMatrix
|
||||
editor={executors}
|
||||
search={(q) => approval.searchUsers(q)}
|
||||
title="Исполнение"
|
||||
roleLabel="Исполнитель"
|
||||
emptyText="Исполнители не назначены."
|
||||
pickerPlaceholder="Добавить исполнителя: начните вводить ФИО…"
|
||||
canMoveRow={canRemoveApprover ? queuedStepMoveRule(executors.rows) : undefined}
|
||||
onMoveRow={handleMoveStep}
|
||||
onAddRow={canAddExecutor ? handleAddExecutor : undefined}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -35,6 +35,7 @@ import {
|
||||
queuedStepMoveRule,
|
||||
useQueuedStepMove
|
||||
} from '../../shared/approval';
|
||||
import { useRouteStepAdd } from '../../shared/approval/useRouteStepAdd';
|
||||
import { getAttachments } from '../../shared/attachments/attachments.service';
|
||||
import { useAttachmentEditor } from '../../shared/attachments/useAttachmentEditor';
|
||||
import { EditableAttachments } from '../../shared/attachments/EditableAttachments';
|
||||
@@ -267,6 +268,33 @@ const RecruitingSelectionView: React.FC<IRecruitingSelectionViewProps> = ({ cont
|
||||
setRouteError
|
||||
);
|
||||
|
||||
// Добавить согласующего/исполнителя в поданную заявку — то же право
|
||||
// routeEditRights.ts; правило «в конец этапа, пока он не завершён» — routeStepAdd.ts.
|
||||
// Новый участник обычно ждёт очереди; если стал активным — сообщаем ему.
|
||||
const handleStepAdded = (activatedRowIds: number[]): void => {
|
||||
setDecisionSeq((n) => n + 1);
|
||||
activatedRowIds.forEach((id) => notify(id));
|
||||
};
|
||||
const handleAddApprover = useRouteStepAdd(
|
||||
approverService,
|
||||
itemId,
|
||||
APPROVAL_PHASE.approval,
|
||||
handleStepAdded,
|
||||
setRouteError
|
||||
);
|
||||
const handleAddExecutor = useRouteStepAdd(
|
||||
approverService,
|
||||
itemId,
|
||||
APPROVAL_PHASE.execution,
|
||||
handleStepAdded,
|
||||
setRouteError
|
||||
);
|
||||
// По статусу заявки: согласующих — пока идёт согласование, исполнителей —
|
||||
// пока не завершено исполнение. Сервер перепроверит по строкам (routeStepAdd.ts).
|
||||
const canAddApprover = canRemoveApprover && status === REQUEST_STATUS.onApproval;
|
||||
const canAddExecutor =
|
||||
canRemoveApprover && (status === REQUEST_STATUS.onApproval || status === REQUEST_STATUS.execution);
|
||||
|
||||
useEffect(() => {
|
||||
let alive = true;
|
||||
setLoading(true);
|
||||
@@ -371,15 +399,19 @@ const RecruitingSelectionView: React.FC<IRecruitingSelectionViewProps> = ({ cont
|
||||
onRemoveRow={handleRemoveApprover}
|
||||
canMoveRow={canRemoveApprover ? queuedStepMoveRule(approvers.rows) : undefined}
|
||||
onMoveRow={handleMoveStep}
|
||||
onAddRow={canAddApprover ? handleAddApprover : undefined}
|
||||
/>
|
||||
|
||||
{!executors.loading && executors.rows.length > 0 && (
|
||||
{/* Без исполнителей блок показываем, только если их можно добавить. */}
|
||||
{!executors.loading && (executors.rows.length > 0 || canAddExecutor) && (
|
||||
<ApprovalMatrix
|
||||
editor={executors}
|
||||
search={(q) => approval.searchUsers(q)}
|
||||
title="Исполнение"
|
||||
roleLabel="Исполнитель"
|
||||
emptyText="Исполнители не назначены."
|
||||
pickerPlaceholder="Добавить исполнителя: начните вводить ФИО…"
|
||||
onAddRow={canAddExecutor ? handleAddExecutor : undefined}
|
||||
canReplaceRow={canRemoveApprover ? isPendingExecutor : undefined}
|
||||
onReplaceRow={handleReplaceExecutor}
|
||||
canRemoveRow={
|
||||
|
||||
Reference in New Issue
Block a user