diff --git a/src/webparts/requestPortal/shared/approval/approvalList.service.ts b/src/webparts/requestPortal/shared/approval/approvalList.service.ts index 7c0acc6..3482e26 100644 --- a/src/webparts/requestPortal/shared/approval/approvalList.service.ts +++ b/src/webparts/requestPortal/shared/approval/approvalList.service.ts @@ -408,6 +408,24 @@ export class ApprovalListService { } } + // Id пользователя сайта → email. Обратное к resolveUserId: строки маршрута + // хранят согласующего числовым Id (Person-поле), а адрес нужен там, где + // решение принимается именно по почте — сейчас это заглушка рассылки + // (см. notify.service.ts). + // + // '' при любой неудаче (пользователя нет, поле пустое, запрос упал) — + // вызывающая сторона трактует это как «адрес неизвестен» и ведёт себя + // консервативно, а не считает, что проверка пройдена. + public async resolveUserEmail(userId: number): Promise { + if (!userId) return ''; + try { + const user = (await getSP().web.siteUsers.getById(userId).select('Email')()) as { Email?: string }; + return user.Email || ''; + } catch { + return ''; + } + } + private async ensure(email: string): Promise<{ Id: number; Title: string }> { const login = email.indexOf('|') >= 0 ? email : `i:0#.f|membership|${email}`; return getSP().web.ensureUser(login); diff --git a/src/webparts/requestPortal/shared/approval/notify.service.ts b/src/webparts/requestPortal/shared/approval/notify.service.ts index d97740e..ad632eb 100644 --- a/src/webparts/requestPortal/shared/approval/notify.service.ts +++ b/src/webparts/requestPortal/shared/approval/notify.service.ts @@ -4,6 +4,21 @@ import { FLOW_RESOURCE, FLOW_NOTIFY_URL } from '../config'; import { ApprovalListService } from './approvalList.service'; import { REQUEST_STATUS } from './models/statuses'; +/** + * ВРЕМЕННАЯ ЗАГЛУШКА на период обкатки рассылки: уведомление отправляется, + * только если активный шаг назначен на этот адрес. Для всех остальных + * согласующих поток не вызывается вообще — письма не будет. + * + * Ограничение стоит ЗДЕСЬ, на стороне портала, а не в потоке Power Automate, + * потому что адресата поток определяет САМ по RowID — тело запроса на выбор + * получателя не влияет. Значит, единственный способ не отправить письмо, не + * трогая поток, — не дёргать его. Переадресовать письмо на тестовый ящик + * отсюда нельзя по той же причине: поток такого параметра не принимает. + * + * Пустая строка — заглушка снята, уведомления идут всем как обычно. + */ +const NOTIFY_ONLY_TO = 'gorbenko.pavel@tnsu.kz'; + // Данные формы для тела запроса к потоку (постоянны для формы). export interface INotifyTarget { requestTypeId: number; // RequestTypeId (id типа заявки во внешнем API маршрутов) @@ -27,6 +42,24 @@ async function postNotify(context: WebPartContext, body: Record } } +// Разрешена ли отправка на согласующего активной строки (см. NOTIFY_ONLY_TO). +// +// Неизвестный адрес (пользователь не найден, поле пустое, запрос упал) — +// НЕ отправляем: пока заглушка включена, сомнение трактуется в пользу +// «письмо живому человеку лучше не слать», а не наоборот. +async function isNotifyAllowed(approval: ApprovalListService, approverId: number): Promise { + if (!NOTIFY_ONLY_TO) return true; + + const email = await approval.resolveUserEmail(approverId); + if (email.toLowerCase() === NOTIFY_ONLY_TO.toLowerCase()) return true; + + console.warn( + `[Оповещение] Включена заглушка рассылки: письмо для «${email || 'адрес неизвестен'}» не отправлено ` + + `(разрешён только ${NOTIFY_ONLY_TO})` + ); + return false; +} + // Находит АКТИВНЫЙ шаг маршрута (На согласовании / На исполнении) и уведомляет его. // Вызывать после отправки заявки и после каждого положительного решения. Поток по // переданным полям сам находит согласующего/исполнителя и шлёт уведомление. @@ -45,6 +78,9 @@ export async function notifyActiveStep( if (!active) { return; } + if (!(await isNotifyAllowed(approval, active.approverId))) { + return; + } await postNotify(context, { RequestTypeId: target.requestTypeId, RowID: active.id,