From 34da9ae165aacfd78715c90cec07e37cd0795102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D1=83=D1=80=20=D0=93=D0=BE=D1=80=D0=BE?= =?UTF-8?q?=D0=B4=D0=B5=D1=86=D0=BA=D0=B8=D0=B9?= Date: Fri, 2 Oct 2026 11:13:07 +0500 Subject: [PATCH] =?UTF-8?q?fix(ct):=20=D0=BF=D0=BE=D1=82=D0=BE=D0=BA=20?= =?UTF-8?q?=D1=83=D0=B2=D0=B5=D0=B4=D0=BE=D0=BC=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B9=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B0=D0=B5=D1=82=20?= =?UTF-8?q?=D1=81=D1=82=D1=80=D0=BE=D0=BA=D1=83=20=D1=82=D0=BE=D0=B3=D0=BE?= =?UTF-8?q?,=20=D0=BA=D1=82=D0=BE=20=D0=B2=D0=B5=D1=80=D0=BD=D1=83=D0=BB?= =?UTF-8?q?=20=D0=BD=D0=B0=20=D0=B4=D0=BE=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=BA=D1=83=20=D0=B8=D0=BB=D0=B8=20=D0=BE=D1=82=D0=BA=D0=BB?= =?UTF-8?q?=D0=BE=D0=BD=D0=B8=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Без активного согласующего форма отправляла потоку последнюю строку маршрута — при возврате на доработку это мог быть следующий согласующий в статусе «Ждёт очереди», и в письме инициатору нельзя было указать, кто вернул и с каким комментарием. Теперь (pickRowToNotify): активный согласующий → его строка; иначе строка «На доработке» или «Отклонено»; иначе последняя. --- .../components/utils/approvalAccess.test.ts | 28 ++++++++++++++++++- .../ct/components/utils/approvalAccess.ts | 10 +++++++ .../forms/ct/services/CtService.ts | 15 +++++----- 3 files changed, 44 insertions(+), 9 deletions(-) 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 {