247f614885
Начальник отдела подбора (routeEditRights) назначает исполнителей, но сама в исполнении не стоит. В матрице «Исполнение» заявки на подбор у шагов «Ждёт очереди» и «На исполнении» появились кнопки «Заменить» и «Удалить»: - замена меняет только человека в шаге, активный шаг сразу переходит к новому исполнителю, ему уходит оповещение; - при удалении активного очередь переходит к следующему исполнителю; - выполненные шаги не трогаются, последнего невыполненного исполнителя можно только заменить — иначе заявка закрылась бы без исполнения. Остальные формы не затронуты: кнопки включаются пропсами ApprovalMatrix (canReplaceRow/onReplaceRow) только в RecruitingSelectionView. notifyActiveStep получил необязательный rowId, чтобы на параллельном шаге письмо ушло именно новому исполнителю. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
96 lines
4.9 KiB
TypeScript
96 lines
4.9 KiB
TypeScript
import { AadHttpClient } from '@microsoft/sp-http';
|
|
import { WebPartContext } from '@microsoft/sp-webpart-base';
|
|
import { FLOW_RESOURCE, FLOW_NOTIFY_URL, getNotificationsEnabled, getNotifyOnlyTo } from '../config';
|
|
import { ApprovalListService } from './approvalList.service';
|
|
import { REQUEST_STATUS } from './models/statuses';
|
|
|
|
// Данные формы для тела запроса к потоку (постоянны для формы).
|
|
export interface INotifyTarget {
|
|
requestTypeId: number; // RequestTypeId (id типа заявки во внешнем API маршрутов)
|
|
dataListName: string; // ListName (список данных заявки)
|
|
approvalListName: string; // ApproveListName (список согласования)
|
|
}
|
|
|
|
// POST в поток оповещения (Entra-триггер) через AadHttpClient — обычный fetch → 401,
|
|
// AadHttpClient даёт токен Flow Service (и снимает вопрос CORS).
|
|
async function postNotify(context: WebPartContext, body: Record<string, unknown>): Promise<void> {
|
|
if (!FLOW_NOTIFY_URL) {
|
|
return;
|
|
}
|
|
const client = await context.aadHttpClientFactory.getClient(FLOW_RESOURCE);
|
|
const res = await client.post(FLOW_NOTIFY_URL, AadHttpClient.configurations.v1, {
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(body),
|
|
});
|
|
if (!res.ok) {
|
|
throw new Error(`Поток оповещения: ${res.status} ${res.statusText}`);
|
|
}
|
|
}
|
|
|
|
// Разрешена ли отправка на согласующего активной строки — по свойствам веб-части
|
|
// notificationsEnabled / notifyOnlyTo (см. setNotificationSettings в config.ts).
|
|
//
|
|
// Ограничение стоит ЗДЕСЬ, на стороне портала, а не в потоке Power Automate,
|
|
// потому что адресата поток определяет САМ по RowID — тело запроса на выбор
|
|
// получателя не влияет. Значит, единственный способ не отправить письмо, не
|
|
// трогая поток, — не дёргать его.
|
|
//
|
|
// При заданном notifyOnlyTo неизвестный адрес (пользователь не найден, поле
|
|
// пустое, запрос упал) — НЕ отправляем: сомнение трактуется в пользу «письмо
|
|
// живому человеку лучше не слать».
|
|
async function isNotifyAllowed(approval: ApprovalListService, approverId: number): Promise<boolean> {
|
|
if (!getNotificationsEnabled()) return false;
|
|
|
|
const onlyTo = getNotifyOnlyTo();
|
|
if (!onlyTo) return true;
|
|
|
|
const email = await approval.resolveUserEmail(approverId);
|
|
if (email.toLowerCase() === onlyTo.toLowerCase()) return true;
|
|
|
|
console.warn(
|
|
`[Оповещение] Рассылка ограничена одним адресом: письмо для «${email || 'адрес неизвестен'}» не отправлено ` +
|
|
`(разрешён только ${onlyTo})`
|
|
);
|
|
return false;
|
|
}
|
|
|
|
// Находит АКТИВНЫЙ шаг маршрута (На согласовании / На исполнении) и уведомляет его.
|
|
// Вызывать после отправки заявки и после каждого положительного решения. Поток по
|
|
// переданным полям сам находит согласующего/исполнителя и шлёт уведомление.
|
|
// Уведомление некритично — ошибки глотаем, чтобы не ронять основной сценарий.
|
|
//
|
|
// rowId — уведомить именно эту активную строку, а не первую попавшуюся: при
|
|
// замене/удалении исполнителя на параллельном шаге активных строк несколько, и
|
|
// письмо должно уйти новому человеку, а не тому, кто уже его получил.
|
|
export async function notifyActiveStep(
|
|
context: WebPartContext,
|
|
approval: ApprovalListService,
|
|
requestId: number,
|
|
target: INotifyTarget,
|
|
rowId?: number
|
|
): Promise<void> {
|
|
try {
|
|
const rows = await approval.getForRequest(requestId);
|
|
const active = rows.filter(
|
|
(r) =>
|
|
(r.status === REQUEST_STATUS.onApproval || r.status === REQUEST_STATUS.onExecution) &&
|
|
(rowId === undefined || r.id === rowId)
|
|
)[0];
|
|
if (!active) {
|
|
return;
|
|
}
|
|
if (!(await isNotifyAllowed(approval, active.approverId))) {
|
|
return;
|
|
}
|
|
await postNotify(context, {
|
|
RequestTypeId: target.requestTypeId,
|
|
RowID: active.id,
|
|
RequestID: requestId,
|
|
ListName: target.dataListName,
|
|
ApproveListName: target.approvalListName,
|
|
});
|
|
} catch {
|
|
// не критично — просто не отправили уведомление
|
|
}
|
|
}
|