diff --git a/src/webparts/ctFormsPortal/forms/ct/components/utils/approvalAccess.test.ts b/src/webparts/ctFormsPortal/forms/ct/components/utils/approvalAccess.test.ts index b1fd81e..db78bd4 100644 --- a/src/webparts/ctFormsPortal/forms/ct/components/utils/approvalAccess.test.ts +++ b/src/webparts/ctFormsPortal/forms/ct/components/utils/approvalAccess.test.ts @@ -1,4 +1,4 @@ -import { findReworkRow, isUserInApprovalChain } from './approvalAccess'; +import { findReworkRow, isUserInApprovalChain, pickRowToNotify } from './approvalAccess'; import { IMatrixRow } from '../../../../shared/approval'; function makeRow(overrides: Partial): IMatrixRow { @@ -48,3 +48,29 @@ describe('findReworkRow (кто вернул СТ на доработку и ч expect(findReworkRow([makeRow({ status: 'Согласовано' })])).toBeUndefined(); }); }); + +describe('pickRowToNotify (какую строку маршрута передать потоку уведомлений)', () => { + const row = (id: number, status: string): { id: number; status: string } => ({ id, status }); + + it('есть активный согласующий — его строку («поступила на согласование»)', () => { + expect(pickRowToNotify([row(1, 'Согласовано'), row(2, 'На согласовании'), row(3, 'Ждёт очереди')])?.id).toBe(2); + }); + + // Раньше при возврате уходила последняя строка (напр. «Ждёт очереди»), и поток не знал, кто вернул и с каким + // комментарием. + it('возврат на доработку — строку того, кто вернул', () => { + expect(pickRowToNotify([row(1, 'Согласовано'), row(2, 'На доработке'), row(3, 'Ждёт очереди')])?.id).toBe(2); + }); + + it('отклонение — строку того, кто отклонил', () => { + expect(pickRowToNotify([row(1, 'Отклонено'), row(2, 'Ждёт очереди')])?.id).toBe(1); + }); + + it('все согласовали — последнюю строку', () => { + expect(pickRowToNotify([row(1, 'Согласовано'), row(2, 'Согласовано')])?.id).toBe(2); + }); + + it('пустой маршрут — нечего отправлять', () => { + expect(pickRowToNotify([])).toBeUndefined(); + }); +}); diff --git a/src/webparts/ctFormsPortal/forms/ct/components/utils/approvalAccess.ts b/src/webparts/ctFormsPortal/forms/ct/components/utils/approvalAccess.ts index c261f0c..7b7fdf3 100644 --- a/src/webparts/ctFormsPortal/forms/ct/components/utils/approvalAccess.ts +++ b/src/webparts/ctFormsPortal/forms/ct/components/utils/approvalAccess.ts @@ -22,3 +22,13 @@ export function isUserInApprovalChain(matrix: IMatrixRow[], user: CurrentUser): export function findReworkRow(matrix: IMatrixRow[]): IMatrixRow | undefined { return matrix.find(row => row.status === REQUEST_STATUS.rework); } + +// Строка маршрута для потока уведомлений (он по ней выходит на СТ, её автора и комментарий): +// активный согласующий — «поступила на согласование»; иначе строка того, кто вернул на доработку или +// отклонил, — чтобы в письме инициатору были его имя и комментарий; иначе (все согласовали) — последняя. +export function pickRowToNotify(rows: T[]): T | undefined { + return rows.find(r => r.status === REQUEST_STATUS.onApproval) + ?? rows.find(r => r.status === REQUEST_STATUS.rework) + ?? rows.find(r => r.status === REQUEST_STATUS.rejected) + ?? rows[rows.length - 1]; +} diff --git a/src/webparts/ctFormsPortal/forms/ct/services/CtService.ts b/src/webparts/ctFormsPortal/forms/ct/services/CtService.ts index cfff997..09420a1 100644 --- a/src/webparts/ctFormsPortal/forms/ct/services/CtService.ts +++ b/src/webparts/ctFormsPortal/forms/ct/services/CtService.ts @@ -31,6 +31,7 @@ import { parseOfferItemIndex, } from "../../../shared/offers/offerRecords"; import { CriteriaRecord, plainTextFromSp } from "../components/utils/criteria"; +import { pickRowToNotify } from "../components/utils/approvalAccess"; import { NoOfferSupplierEntry, NoOfferSupplierRecord, @@ -746,14 +747,12 @@ export class CtService { async notifyActiveApprover(ctId: string): Promise { const rows = await this.approval.getForRequest(parseInt(ctId, 10)); if (rows.length === 0) return; - // Есть активный согласующий → уведомляем его («поступила на согласование»). - // Иначе финал (все согласовали / кто-то отклонил): активной строки уже нет, но поток - // сам выбирает получателя (инициатора) по статусу СТ — ему нужен лишь любой RowID этой СТ, - // чтобы выйти на саму СТ и её автора. Берём последнюю строку маршрута. Без этого - // «Согласовано»/«Отклонено» инициатору не уходили (поток вообще не вызывался). - const active = rows.filter((r) => r.status === REQUEST_STATUS.onApproval)[0]; - const rowToNotify = active ?? rows[rows.length - 1]; - await this.triggerApprovalNotification(rowToNotify.id); + // Есть активный согласующий → уведомляем его («поступила на согласование»). Иначе — строка того, + // кто вернул на доработку или отклонил (поток берёт из неё имя и комментарий для письма инициатору), + // а если все согласовали — последняя: поток по ней выходит на СТ и её автора. Раньше при возврате + // уходила последняя строка (напр. «Ждёт очереди»), и кто вернул и почему, поток не знал. + const rowToNotify = pickRowToNotify(rows); + if (rowToNotify) await this.triggerApprovalNotification(rowToNotify.id); } async triggerApprovalNotification(spItemId: number): Promise {