From c518d5981d0501aee17a837b6c5bcc5dd95484b7 Mon Sep 17 00:00:00 2001 From: "gorbenko.pavel" Date: Mon, 14 Sep 2026 09:30:40 +0500 Subject: [PATCH] =?UTF-8?q?feat(=D0=B0=D0=B2=D1=82=D0=BE=D1=81=D0=BE=D0=B3?= =?UTF-8?q?=D0=BB=D0=B0=D1=81=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5):=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BB=D0=BE=D0=BD=D0=BA=D0=B0=20AutoApprove=20?= =?UTF-8?q?=D0=B2=D0=BE=20=D0=B2=D1=81=D0=B5=D1=85=20=D1=81=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=BA=D0=B0=D1=85=20=D1=81=D0=BE=D0=B3=D0=BB=D0=B0=D1=81?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Провижининг (Provision-Lists.ps1 читает эти манифесты) теперь заводит колонку «Автосогласование» (AutoApprove, Да/Нет) в каждом из 12 списков согласования — рядом с уже существующими SLAhours/AskOverdueReason. Значение приходит из resolve (approval-route API, шаг маршрута настраивается только в админке — портал само значение не выбирает, только переносит его в строку списка при отправке заявки/сохранении черновика, тем же путём, что и slaHours/askOverdueReason). Фактическое автосогласование по истечении SLA делает фоновая задача бэкенда (Hangfire), не портал. В ApprovalMatrix добавлена пометка «(авто)» рядом со значением SLA — чисто информативно, чтобы участник видел, что решение по шагу может принять не только человек. Примечание: ServiceRequest и TmcTransfer не передают ни slaHours/ askOverdueReason, ни (теперь) autoApprove из resolve в строку — это существовавший пробел (SLA на уровне шага у этих двух форм и раньше не работал), автосогласование для них наследует только это же ограничение. Co-Authored-By: Claude Sonnet 5 --- .../requestPortal/forms/Clearance/ClearanceForm.tsx | 3 ++- .../forms/Clearance/approval/provisioning.json | 1 + .../forms/ConcreteRequest/ConcreteRequestForm.tsx | 3 ++- .../forms/ConcreteRequest/approval/provisioning.json | 1 + .../requestPortal/forms/Dismissal/DismissalForm.tsx | 3 ++- .../forms/Dismissal/approval/provisioning.json | 1 + .../requestPortal/forms/Dismissal/createClearance.ts | 3 ++- .../forms/EmployeeTransfer/EmployeeTransferForm.tsx | 3 ++- .../EmployeeTransfer/approval/provisioning.json | 1 + .../forms/RecruitingHire/RecruitingHireForm.tsx | 1 + .../forms/RecruitingHire/approval/provisioning.json | 1 + .../RecruitingSelection/RecruitingSelectionForm.tsx | 3 ++- .../RecruitingSelection/approval/provisioning.json | 1 + .../requestPortal/forms/Referral/ReferralForm.tsx | 3 ++- .../forms/Referral/approval/provisioning.json | 1 + .../forms/ServiceRequest/approval/provisioning.json | 1 + .../requestPortal/forms/Ticket1C/Ticket1CForm.tsx | 3 ++- .../forms/Ticket1C/approval/provisioning.json | 1 + .../forms/TmcTransfer/approval/provisioning.json | 1 + .../forms/UnpaidLeave/UnpaidLeaveForm.tsx | 3 ++- .../forms/UnpaidLeave/approval/provisioning.json | 1 + .../requestPortal/forms/Vacation/VacationForm.tsx | 3 ++- .../forms/Vacation/approval/provisioning.json | 1 + .../forms/WorkplaceSetup/WorkplaceSetupForm.tsx | 1 + .../forms/WorkplaceSetup/approval/provisioning.json | 1 + .../shared/approval/approvalList.service.ts | 12 ++++++++++++ .../shared/approval/approvalSubmission.service.ts | 1 + .../shared/approval/models/IApprovalRouteApi.ts | 9 +++++++++ .../shared/approval/models/IMatrixRow.ts | 7 +++++++ .../shared/approval/ui/ApprovalMatrix.tsx | 7 ++++++- .../shared/approval/ui/approval.module.scss | 8 ++++++++ 31 files changed, 78 insertions(+), 11 deletions(-) diff --git a/src/webparts/requestPortal/forms/Clearance/ClearanceForm.tsx b/src/webparts/requestPortal/forms/Clearance/ClearanceForm.tsx index 1f61330..db25869 100644 --- a/src/webparts/requestPortal/forms/Clearance/ClearanceForm.tsx +++ b/src/webparts/requestPortal/forms/Clearance/ClearanceForm.tsx @@ -201,7 +201,8 @@ const ClearanceForm: React.FC = ({ // Транзитом из маршрута в строку списка: наследование бэкенд применил // при resolve, форма только переносит значения (см. IMatrixRow.slaHours). slaHours: r.slaHours, - askOverdueReason: r.askOverdueReason + askOverdueReason: r.askOverdueReason, + autoApprove: r.autoApprove })); const save = (status: string): (() => Promise) => async (): Promise => { diff --git a/src/webparts/requestPortal/forms/Clearance/approval/provisioning.json b/src/webparts/requestPortal/forms/Clearance/approval/provisioning.json index 5395c52..606125e 100644 --- a/src/webparts/requestPortal/forms/Clearance/approval/provisioning.json +++ b/src/webparts/requestPortal/forms/Clearance/approval/provisioning.json @@ -11,6 +11,7 @@ { "internalName": "Phase", "title": "Фаза", "type": "Choice", "choices": ["Согласование", "Исполнение"] }, { "internalName": "SLAhours", "title": "SLA, часов", "type": "Number" }, { "internalName": "AskOverdueReason", "title": "Опрос причины просрочки", "type": "Boolean" }, + { "internalName": "AutoApprove", "title": "Автосогласование", "type": "Boolean" }, { "internalName": "DelegatedBy", "title": "Кем переназначено", "type": "User", "userSelectionMode": "PeopleOnly" } ] } diff --git a/src/webparts/requestPortal/forms/ConcreteRequest/ConcreteRequestForm.tsx b/src/webparts/requestPortal/forms/ConcreteRequest/ConcreteRequestForm.tsx index 2536cc4..3cf1c92 100644 --- a/src/webparts/requestPortal/forms/ConcreteRequest/ConcreteRequestForm.tsx +++ b/src/webparts/requestPortal/forms/ConcreteRequest/ConcreteRequestForm.tsx @@ -199,7 +199,8 @@ const ConcreteRequestForm: React.FC = ({ // Транзитом из маршрута в строку списка: наследование бэкенд применил // при resolve, форма только переносит значения (см. IMatrixRow.slaHours). slaHours: r.slaHours, - askOverdueReason: r.askOverdueReason + askOverdueReason: r.askOverdueReason, + autoApprove: r.autoApprove })); const save = (status: string): (() => Promise) => async (): Promise => { diff --git a/src/webparts/requestPortal/forms/ConcreteRequest/approval/provisioning.json b/src/webparts/requestPortal/forms/ConcreteRequest/approval/provisioning.json index aa70b8d..750ca20 100644 --- a/src/webparts/requestPortal/forms/ConcreteRequest/approval/provisioning.json +++ b/src/webparts/requestPortal/forms/ConcreteRequest/approval/provisioning.json @@ -11,6 +11,7 @@ { "internalName": "Phase", "title": "Фаза", "type": "Choice", "choices": ["Согласование", "Исполнение"] }, { "internalName": "SLAhours", "title": "SLA, часов", "type": "Number" }, { "internalName": "AskOverdueReason", "title": "Опрос причины просрочки", "type": "Boolean" }, + { "internalName": "AutoApprove", "title": "Автосогласование", "type": "Boolean" }, { "internalName": "DelegatedBy", "title": "Кем переназначено", "type": "User", "userSelectionMode": "PeopleOnly" } ] } diff --git a/src/webparts/requestPortal/forms/Dismissal/DismissalForm.tsx b/src/webparts/requestPortal/forms/Dismissal/DismissalForm.tsx index 2be4313..4663c71 100644 --- a/src/webparts/requestPortal/forms/Dismissal/DismissalForm.tsx +++ b/src/webparts/requestPortal/forms/Dismissal/DismissalForm.tsx @@ -222,7 +222,8 @@ const DismissalForm: React.FC = ({ // Транзитом из маршрута в строку списка: наследование бэкенд применил // при resolve, форма только переносит значения (см. IMatrixRow.slaHours). slaHours: r.slaHours, - askOverdueReason: r.askOverdueReason + askOverdueReason: r.askOverdueReason, + autoApprove: r.autoApprove })); const save = (status: string): (() => Promise) => async (): Promise => { diff --git a/src/webparts/requestPortal/forms/Dismissal/approval/provisioning.json b/src/webparts/requestPortal/forms/Dismissal/approval/provisioning.json index 5244649..9041e91 100644 --- a/src/webparts/requestPortal/forms/Dismissal/approval/provisioning.json +++ b/src/webparts/requestPortal/forms/Dismissal/approval/provisioning.json @@ -11,6 +11,7 @@ { "internalName": "Phase", "title": "Фаза", "type": "Choice", "choices": ["Согласование", "Исполнение"] }, { "internalName": "SLAhours", "title": "SLA, часов", "type": "Number" }, { "internalName": "AskOverdueReason", "title": "Опрос причины просрочки", "type": "Boolean" }, + { "internalName": "AutoApprove", "title": "Автосогласование", "type": "Boolean" }, { "internalName": "DelegatedBy", "title": "Кем переназначено", "type": "User", "userSelectionMode": "PeopleOnly" } ] } diff --git a/src/webparts/requestPortal/forms/Dismissal/createClearance.ts b/src/webparts/requestPortal/forms/Dismissal/createClearance.ts index c51dea6..421655a 100644 --- a/src/webparts/requestPortal/forms/Dismissal/createClearance.ts +++ b/src/webparts/requestPortal/forms/Dismissal/createClearance.ts @@ -46,7 +46,8 @@ function toApprovers(rows: IMatrixRow[]): IApproverToWrite[] { allowReassign: r.allowReassign, order: r.order, slaHours: r.slaHours, - askOverdueReason: r.askOverdueReason + askOverdueReason: r.askOverdueReason, + autoApprove: r.autoApprove })); } diff --git a/src/webparts/requestPortal/forms/EmployeeTransfer/EmployeeTransferForm.tsx b/src/webparts/requestPortal/forms/EmployeeTransfer/EmployeeTransferForm.tsx index fe53d62..4edb360 100644 --- a/src/webparts/requestPortal/forms/EmployeeTransfer/EmployeeTransferForm.tsx +++ b/src/webparts/requestPortal/forms/EmployeeTransfer/EmployeeTransferForm.tsx @@ -188,7 +188,8 @@ const EmployeeTransferForm: React.FC = ({ // Транзитом из маршрута в строку списка: наследование бэкенд применил // при resolve, форма только переносит значения (см. IMatrixRow.slaHours). slaHours: r.slaHours, - askOverdueReason: r.askOverdueReason + askOverdueReason: r.askOverdueReason, + autoApprove: r.autoApprove })); const save = (status: string): (() => Promise) => async (): Promise => { diff --git a/src/webparts/requestPortal/forms/EmployeeTransfer/approval/provisioning.json b/src/webparts/requestPortal/forms/EmployeeTransfer/approval/provisioning.json index 04202a6..89c5b4c 100644 --- a/src/webparts/requestPortal/forms/EmployeeTransfer/approval/provisioning.json +++ b/src/webparts/requestPortal/forms/EmployeeTransfer/approval/provisioning.json @@ -11,6 +11,7 @@ { "internalName": "Phase", "title": "Фаза", "type": "Choice", "choices": ["Согласование", "Исполнение"] }, { "internalName": "SLAhours", "title": "SLA, часов", "type": "Number" }, { "internalName": "AskOverdueReason", "title": "Опрос причины просрочки", "type": "Boolean" }, + { "internalName": "AutoApprove", "title": "Автосогласование", "type": "Boolean" }, { "internalName": "DelegatedBy", "title": "Кем переназначено", "type": "User", "userSelectionMode": "PeopleOnly" } ] } diff --git a/src/webparts/requestPortal/forms/RecruitingHire/RecruitingHireForm.tsx b/src/webparts/requestPortal/forms/RecruitingHire/RecruitingHireForm.tsx index 94287db..b4651b2 100644 --- a/src/webparts/requestPortal/forms/RecruitingHire/RecruitingHireForm.tsx +++ b/src/webparts/requestPortal/forms/RecruitingHire/RecruitingHireForm.tsx @@ -214,6 +214,7 @@ const RecruitingHireForm: React.FC = ({ // при resolve, форма только переносит значения (см. IMatrixRow.slaHours). slaHours: r.slaHours, askOverdueReason: r.askOverdueReason, + autoApprove: r.autoApprove })); // Удаление уже прикреплённого файла (в правке) — сразу из заявки. diff --git a/src/webparts/requestPortal/forms/RecruitingHire/approval/provisioning.json b/src/webparts/requestPortal/forms/RecruitingHire/approval/provisioning.json index 26dd879..557401f 100644 --- a/src/webparts/requestPortal/forms/RecruitingHire/approval/provisioning.json +++ b/src/webparts/requestPortal/forms/RecruitingHire/approval/provisioning.json @@ -11,6 +11,7 @@ { "internalName": "Phase", "title": "Фаза", "type": "Choice", "choices": ["Согласование", "Исполнение"] }, { "internalName": "SLAhours", "title": "SLA, часов", "type": "Number" }, { "internalName": "AskOverdueReason", "title": "Опрос причины просрочки", "type": "Boolean" }, + { "internalName": "AutoApprove", "title": "Автосогласование", "type": "Boolean" }, { "internalName": "DelegatedBy", "title": "Кем переназначено", "type": "User", "userSelectionMode": "PeopleOnly" } ] } diff --git a/src/webparts/requestPortal/forms/RecruitingSelection/RecruitingSelectionForm.tsx b/src/webparts/requestPortal/forms/RecruitingSelection/RecruitingSelectionForm.tsx index c7565ff..cc358be 100644 --- a/src/webparts/requestPortal/forms/RecruitingSelection/RecruitingSelectionForm.tsx +++ b/src/webparts/requestPortal/forms/RecruitingSelection/RecruitingSelectionForm.tsx @@ -224,7 +224,8 @@ const RecruitingSelectionForm: React.FC = ({ // Транзитом из маршрута в строку списка: наследование бэкенд применил // при resolve, форма только переносит значения (см. IMatrixRow.slaHours). slaHours: r.slaHours, - askOverdueReason: r.askOverdueReason + askOverdueReason: r.askOverdueReason, + autoApprove: r.autoApprove })); const handleRemoveAttachment = (fileName: string): void => { diff --git a/src/webparts/requestPortal/forms/RecruitingSelection/approval/provisioning.json b/src/webparts/requestPortal/forms/RecruitingSelection/approval/provisioning.json index 9707088..6ed944a 100644 --- a/src/webparts/requestPortal/forms/RecruitingSelection/approval/provisioning.json +++ b/src/webparts/requestPortal/forms/RecruitingSelection/approval/provisioning.json @@ -11,6 +11,7 @@ { "internalName": "Phase", "title": "Фаза", "type": "Choice", "choices": ["Согласование", "Исполнение"] }, { "internalName": "SLAhours", "title": "SLA, часов", "type": "Number" }, { "internalName": "AskOverdueReason", "title": "Опрос причины просрочки", "type": "Boolean" }, + { "internalName": "AutoApprove", "title": "Автосогласование", "type": "Boolean" }, { "internalName": "DelegatedBy", "title": "Кем переназначено", "type": "User", "userSelectionMode": "PeopleOnly" } ] } diff --git a/src/webparts/requestPortal/forms/Referral/ReferralForm.tsx b/src/webparts/requestPortal/forms/Referral/ReferralForm.tsx index c5f31ed..30b09c6 100644 --- a/src/webparts/requestPortal/forms/Referral/ReferralForm.tsx +++ b/src/webparts/requestPortal/forms/Referral/ReferralForm.tsx @@ -268,7 +268,8 @@ const ReferralForm: React.FC = ({ // Транзитом из маршрута в строку списка: наследование бэкенд применил // при resolve, форма только переносит значения (см. IMatrixRow.slaHours). slaHours: r.slaHours, - askOverdueReason: r.askOverdueReason + askOverdueReason: r.askOverdueReason, + autoApprove: r.autoApprove })); const save = (status: string): (() => Promise) => async (): Promise => { diff --git a/src/webparts/requestPortal/forms/Referral/approval/provisioning.json b/src/webparts/requestPortal/forms/Referral/approval/provisioning.json index 4e58915..6937bcf 100644 --- a/src/webparts/requestPortal/forms/Referral/approval/provisioning.json +++ b/src/webparts/requestPortal/forms/Referral/approval/provisioning.json @@ -11,6 +11,7 @@ { "internalName": "Phase", "title": "Фаза", "type": "Choice", "choices": ["Согласование", "Исполнение"] }, { "internalName": "SLAhours", "title": "SLA, часов", "type": "Number" }, { "internalName": "AskOverdueReason", "title": "Опрос причины просрочки", "type": "Boolean" }, + { "internalName": "AutoApprove", "title": "Автосогласование", "type": "Boolean" }, { "internalName": "DelegatedBy", "title": "Кем переназначено", "type": "User", "userSelectionMode": "PeopleOnly" } ] } diff --git a/src/webparts/requestPortal/forms/ServiceRequest/approval/provisioning.json b/src/webparts/requestPortal/forms/ServiceRequest/approval/provisioning.json index eafb929..1f7e447 100644 --- a/src/webparts/requestPortal/forms/ServiceRequest/approval/provisioning.json +++ b/src/webparts/requestPortal/forms/ServiceRequest/approval/provisioning.json @@ -11,6 +11,7 @@ { "internalName": "Phase", "title": "Фаза", "type": "Choice", "choices": ["Согласование", "Исполнение"] }, { "internalName": "SLAhours", "title": "SLA, часов", "type": "Number" }, { "internalName": "AskOverdueReason", "title": "Опрос причины просрочки", "type": "Boolean" }, + { "internalName": "AutoApprove", "title": "Автосогласование", "type": "Boolean" }, { "internalName": "DelegatedBy", "title": "Кем переназначено", "type": "User", "userSelectionMode": "PeopleOnly" } ] } diff --git a/src/webparts/requestPortal/forms/Ticket1C/Ticket1CForm.tsx b/src/webparts/requestPortal/forms/Ticket1C/Ticket1CForm.tsx index 433fbac..905ee6e 100644 --- a/src/webparts/requestPortal/forms/Ticket1C/Ticket1CForm.tsx +++ b/src/webparts/requestPortal/forms/Ticket1C/Ticket1CForm.tsx @@ -207,7 +207,8 @@ const Ticket1CForm: React.FC = ({ // Транзитом из маршрута в строку списка: наследование бэкенд применил // при resolve, форма только переносит значения (см. IMatrixRow.slaHours). slaHours: r.slaHours, - askOverdueReason: r.askOverdueReason + askOverdueReason: r.askOverdueReason, + autoApprove: r.autoApprove })); const handleRemoveAttachment = (fileName: string): void => { diff --git a/src/webparts/requestPortal/forms/Ticket1C/approval/provisioning.json b/src/webparts/requestPortal/forms/Ticket1C/approval/provisioning.json index d144c07..a8561d0 100644 --- a/src/webparts/requestPortal/forms/Ticket1C/approval/provisioning.json +++ b/src/webparts/requestPortal/forms/Ticket1C/approval/provisioning.json @@ -11,6 +11,7 @@ { "internalName": "Phase", "title": "Фаза", "type": "Choice", "choices": ["Согласование", "Исполнение"] }, { "internalName": "SLAhours", "title": "SLA, часов", "type": "Number" }, { "internalName": "AskOverdueReason", "title": "Опрос причины просрочки", "type": "Boolean" }, + { "internalName": "AutoApprove", "title": "Автосогласование", "type": "Boolean" }, { "internalName": "DelegatedBy", "title": "Кем переназначено", "type": "User", "userSelectionMode": "PeopleOnly" } ] } diff --git a/src/webparts/requestPortal/forms/TmcTransfer/approval/provisioning.json b/src/webparts/requestPortal/forms/TmcTransfer/approval/provisioning.json index 3ebe166..356ee53 100644 --- a/src/webparts/requestPortal/forms/TmcTransfer/approval/provisioning.json +++ b/src/webparts/requestPortal/forms/TmcTransfer/approval/provisioning.json @@ -11,6 +11,7 @@ { "internalName": "Phase", "title": "Фаза", "type": "Choice", "choices": ["Согласование", "Исполнение"] }, { "internalName": "SLAhours", "title": "SLA, часов", "type": "Number" }, { "internalName": "AskOverdueReason", "title": "Опрос причины просрочки", "type": "Boolean" }, + { "internalName": "AutoApprove", "title": "Автосогласование", "type": "Boolean" }, { "internalName": "DelegatedBy", "title": "Кем переназначено", "type": "User", "userSelectionMode": "PeopleOnly" } ] } diff --git a/src/webparts/requestPortal/forms/UnpaidLeave/UnpaidLeaveForm.tsx b/src/webparts/requestPortal/forms/UnpaidLeave/UnpaidLeaveForm.tsx index 3dcd50e..d474441 100644 --- a/src/webparts/requestPortal/forms/UnpaidLeave/UnpaidLeaveForm.tsx +++ b/src/webparts/requestPortal/forms/UnpaidLeave/UnpaidLeaveForm.tsx @@ -200,7 +200,8 @@ const UnpaidLeaveForm: React.FC = ({ // Транзитом из маршрута в строку списка: наследование бэкенд применил // при resolve, форма только переносит значения (см. IMatrixRow.slaHours). slaHours: r.slaHours, - askOverdueReason: r.askOverdueReason + askOverdueReason: r.askOverdueReason, + autoApprove: r.autoApprove })); const save = (status: string): (() => Promise) => async (): Promise => { diff --git a/src/webparts/requestPortal/forms/UnpaidLeave/approval/provisioning.json b/src/webparts/requestPortal/forms/UnpaidLeave/approval/provisioning.json index cac6582..a7bafb1 100644 --- a/src/webparts/requestPortal/forms/UnpaidLeave/approval/provisioning.json +++ b/src/webparts/requestPortal/forms/UnpaidLeave/approval/provisioning.json @@ -11,6 +11,7 @@ { "internalName": "Phase", "title": "Фаза", "type": "Choice", "choices": ["Согласование", "Исполнение"] }, { "internalName": "SLAhours", "title": "SLA, часов", "type": "Number" }, { "internalName": "AskOverdueReason", "title": "Опрос причины просрочки", "type": "Boolean" }, + { "internalName": "AutoApprove", "title": "Автосогласование", "type": "Boolean" }, { "internalName": "DelegatedBy", "title": "Кем переназначено", "type": "User", "userSelectionMode": "PeopleOnly" } ] } diff --git a/src/webparts/requestPortal/forms/Vacation/VacationForm.tsx b/src/webparts/requestPortal/forms/Vacation/VacationForm.tsx index 909697a..a689433 100644 --- a/src/webparts/requestPortal/forms/Vacation/VacationForm.tsx +++ b/src/webparts/requestPortal/forms/Vacation/VacationForm.tsx @@ -198,7 +198,8 @@ const VacationForm: React.FC = ({ // Транзитом из маршрута в строку списка: наследование бэкенд применил // при resolve, форма только переносит значения (см. IMatrixRow.slaHours). slaHours: r.slaHours, - askOverdueReason: r.askOverdueReason + askOverdueReason: r.askOverdueReason, + autoApprove: r.autoApprove })); const save = (status: string): (() => Promise) => async (): Promise => { diff --git a/src/webparts/requestPortal/forms/Vacation/approval/provisioning.json b/src/webparts/requestPortal/forms/Vacation/approval/provisioning.json index 6558228..ba36be0 100644 --- a/src/webparts/requestPortal/forms/Vacation/approval/provisioning.json +++ b/src/webparts/requestPortal/forms/Vacation/approval/provisioning.json @@ -11,6 +11,7 @@ { "internalName": "Phase", "title": "Фаза", "type": "Choice", "choices": ["Согласование", "Исполнение"] }, { "internalName": "SLAhours", "title": "SLA, часов", "type": "Number" }, { "internalName": "AskOverdueReason", "title": "Опрос причины просрочки", "type": "Boolean" }, + { "internalName": "AutoApprove", "title": "Автосогласование", "type": "Boolean" }, { "internalName": "DelegatedBy", "title": "Кем переназначено", "type": "User", "userSelectionMode": "PeopleOnly" } ] } diff --git a/src/webparts/requestPortal/forms/WorkplaceSetup/WorkplaceSetupForm.tsx b/src/webparts/requestPortal/forms/WorkplaceSetup/WorkplaceSetupForm.tsx index 3b8192f..776994d 100644 --- a/src/webparts/requestPortal/forms/WorkplaceSetup/WorkplaceSetupForm.tsx +++ b/src/webparts/requestPortal/forms/WorkplaceSetup/WorkplaceSetupForm.tsx @@ -232,6 +232,7 @@ const WorkplaceSetupForm: React.FC = ({ // при resolve, форма только переносит значения (см. IMatrixRow.slaHours). slaHours: r.slaHours, askOverdueReason: r.askOverdueReason, + autoApprove: r.autoApprove })); // Удаление уже прикреплённого файла (в правке) — сразу из заявки. diff --git a/src/webparts/requestPortal/forms/WorkplaceSetup/approval/provisioning.json b/src/webparts/requestPortal/forms/WorkplaceSetup/approval/provisioning.json index f6b867d..ec88893 100644 --- a/src/webparts/requestPortal/forms/WorkplaceSetup/approval/provisioning.json +++ b/src/webparts/requestPortal/forms/WorkplaceSetup/approval/provisioning.json @@ -11,6 +11,7 @@ { "internalName": "Phase", "title": "Фаза", "type": "Choice", "choices": ["Согласование", "Исполнение"] }, { "internalName": "SLAhours", "title": "SLA, часов", "type": "Number" }, { "internalName": "AskOverdueReason", "title": "Опрос причины просрочки", "type": "Boolean" }, + { "internalName": "AutoApprove", "title": "Автосогласование", "type": "Boolean" }, { "internalName": "DelegatedBy", "title": "Кем переназначено", "type": "User", "userSelectionMode": "PeopleOnly" } ] } diff --git a/src/webparts/requestPortal/shared/approval/approvalList.service.ts b/src/webparts/requestPortal/shared/approval/approvalList.service.ts index bb26955..a320cf7 100644 --- a/src/webparts/requestPortal/shared/approval/approvalList.service.ts +++ b/src/webparts/requestPortal/shared/approval/approvalList.service.ts @@ -36,6 +36,7 @@ const FIELDS = { delegatedBy: 'DelegatedBy', // «Кем переназначено» (User) → DelegatedById slaHours: 'SLAhours', // «SLA, часов» (Number) — пусто значит «взять у типа заявки» askOverdueReason: 'AskOverdueReason', // «Опрос причины просрочки» (Да/Нет) + autoApprove: 'AutoApprove', // «Автосогласование» (Да/Нет) — см. IApproverToWrite.autoApprove }; // Согласующий для записи в список. @@ -61,6 +62,14 @@ export interface IApproverToWrite { slaHours?: number | null; // eslint-disable-next-line @rushstack/no-new-null -- см. slaHours askOverdueReason?: boolean | null; + /** + * Согласовать шаг автоматически по истечении SLA — так же, как SLA-поля выше, + * приходит из resolve с уже применённым решением (наследовать здесь нечего, + * см. ResolvedRouteStepDto.AutoApprove на бэкенде), портал только переносит + * значение в строку. undefined — «не задано» (старые строки без этого поля); + * фактическое решение принимает не портал, а фоновая задача бэкенда. + */ + autoApprove?: boolean; } // Все запросы к списку «<форма> — согласование». listTitle задаётся формой. @@ -141,6 +150,7 @@ export class ApprovalListService { // ?? null, а не || null: ноль часов — значимое значение. [FIELDS.slaHours]: a.slaHours ?? null, [FIELDS.askOverdueReason]: a.askOverdueReason ?? null, + [FIELDS.autoApprove]: a.autoApprove ?? false, }); }); await execute(); @@ -278,6 +288,7 @@ export class ApprovalListService { FIELDS.phase, FIELDS.slaHours, FIELDS.askOverdueReason, + FIELDS.autoApprove, `${FIELDS.approver}/Title`, `${FIELDS.approver}/EMail`, `${FIELDS.delegatedBy}/Title` @@ -306,6 +317,7 @@ export class ApprovalListService { // проставленный при первой отправке, обнулился бы у черновика. slaHours: (r[FIELDS.slaHours] as number | null) ?? null, askOverdueReason: (r[FIELDS.askOverdueReason] as boolean | null) ?? null, + autoApprove: (r[FIELDS.autoApprove] as boolean | null) ?? false, }; }); } diff --git a/src/webparts/requestPortal/shared/approval/approvalSubmission.service.ts b/src/webparts/requestPortal/shared/approval/approvalSubmission.service.ts index f09b1ab..2205ece 100644 --- a/src/webparts/requestPortal/shared/approval/approvalSubmission.service.ts +++ b/src/webparts/requestPortal/shared/approval/approvalSubmission.service.ts @@ -38,6 +38,7 @@ export async function getPlannedMatrix( phase, slaHours: s.slaHours, askOverdueReason: s.askOverdueReason, + autoApprove: s.autoApprove, warning: s.warning, })) ); diff --git a/src/webparts/requestPortal/shared/approval/models/IApprovalRouteApi.ts b/src/webparts/requestPortal/shared/approval/models/IApprovalRouteApi.ts index ce7eb33..9ec18bb 100644 --- a/src/webparts/requestPortal/shared/approval/models/IApprovalRouteApi.ts +++ b/src/webparts/requestPortal/shared/approval/models/IApprovalRouteApi.ts @@ -108,4 +108,13 @@ export interface IResolvedRouteStep { slaHours: number | null; // eslint-disable-next-line @rushstack/no-new-null -- см. slaHours askOverdueReason: boolean | null; + /** + * Согласовать шаг автоматически, когда истечёт его SLA — настраивается ТОЛЬКО в + * админке маршрутов (approval-route), портал это значение только читает из + * resolve и переносит в строку списка согласования, ничего не решая сам: + * фактическое автосогласование выполняет фоновая задача бэкенда (Hangfire). + * Обычный boolean, а не nullable, как SLA-поля выше, — наследовать здесь + * нечего, см. соответствующее поле на бэкенде (TemplateRouteStep.AutoApprove). + */ + autoApprove: boolean; } diff --git a/src/webparts/requestPortal/shared/approval/models/IMatrixRow.ts b/src/webparts/requestPortal/shared/approval/models/IMatrixRow.ts index 29b5810..49cfc57 100644 --- a/src/webparts/requestPortal/shared/approval/models/IMatrixRow.ts +++ b/src/webparts/requestPortal/shared/approval/models/IMatrixRow.ts @@ -31,6 +31,13 @@ export interface IMatrixRow { slaHours?: number | null; // eslint-disable-next-line @rushstack/no-new-null -- см. slaHours askOverdueReason?: boolean | null; + /** + * Согласовать шаг автоматически по истечении SLA — то же транзитное поле, что и + * slaHours/askOverdueReason выше: настраивается в админке, портал только + * переносит значение в строку списка, ничего не решая сам (см. + * IResolvedRouteStep.autoApprove). + */ + autoApprove?: boolean; /** * Предупреждение approval-route по ЭТОМУ шагу (см. IResolvedRouteStep.warning) — * например, роль не разрешилась в конкретного человека, и почему именно. diff --git a/src/webparts/requestPortal/shared/approval/ui/ApprovalMatrix.tsx b/src/webparts/requestPortal/shared/approval/ui/ApprovalMatrix.tsx index e6daf7e..1106cf5 100644 --- a/src/webparts/requestPortal/shared/approval/ui/ApprovalMatrix.tsx +++ b/src/webparts/requestPortal/shared/approval/ui/ApprovalMatrix.tsx @@ -193,7 +193,12 @@ export const ApprovalMatrix: React.FC = ({ )} {hasDates && {r.date}} {hasSla && ( - {r.slaHours || r.slaHours === 0 ? `${r.slaHours} ч` : '—'} + + {r.slaHours || r.slaHours === 0 ? `${r.slaHours} ч` : '—'} + {/* Настраивается только в админке — здесь чисто информативно, + чтобы участник видел: решение по шагу примут не только вручную. */} + {r.autoApprove && (авто)} + )} {/* Перемещение действует на ШАГ целиком (параллельные строки переезжают вместе): одна пара стрелок на весь шаг. */} diff --git a/src/webparts/requestPortal/shared/approval/ui/approval.module.scss b/src/webparts/requestPortal/shared/approval/ui/approval.module.scss index 507a2dc..ff0d849 100644 --- a/src/webparts/requestPortal/shared/approval/ui/approval.module.scss +++ b/src/webparts/requestPortal/shared/approval/ui/approval.module.scss @@ -187,6 +187,14 @@ font-weight: 500; } +// Пометка «(авто)» рядом со значением SLA — шаг настроен на автосогласование +// (см. ApprovalMatrix.tsx/r.autoApprove). Нейтральным серым, не золотым и не +// красным: это не проблема и не решение человека, а просто информация о +// правиле, действующем на шаг. +.autoApproveBadge { + color: $gray-500; +} + // Причина, по которой approval-route не назначил человека на шаг (см. // ApprovalMatrix.tsx/r.warning) — красным, как настоящая проблема маршрута // (в отличие от нейтрального addedByInline/commentInline): пустая строка -- 2.34.1