Динамические подписи на переходе¶
Динамический маршрут согласования формируется с помощью хранимой SQL-процедуры, в которую передается набор параметров и которая возвращает таблицу специальной структуры. Поля возвращаемой таблицы в целом соответствуют настройкам статического маршрута.
Для перехода к настройкам динамического маршрута на странице Подписи на переходе в опции Сервис определения подписей на переходе необходимо выбрать Маршрут из sql запроса.
Настройка динамического маршрута согласования¶
При включенном параметре Кешировать полный маршрут согласования все настройки маршрута запоминаются на момент начала процесса согласования. Даже если в ходе согласования эти настройки изменятся, то данный процесс будет выполняться по прежним настройкам, а изменения вступят в силу уже при следующем согласовании.
Параметры запроса и хранимой процедуры¶
| Параметр | Описание |
|---|---|
| TaskID | ID задачи, в которой запрашивается подпись |
| StepID | ID перехода, на котором запрашивается подпись |
| RequestingUserID | ID пользователя, от имени которого запрашивается подпись |
| Stage | Этап маршрута согласования. |
ℹ️ Нумерация этапов (Stage) начинается с нуля.
Поля, возвращаемые SQL-запросом или процедурой¶
Процедура может возвращать не все перечисленные ниже параметры. Обязательные параметры отмечены знаком *.
| Возвращаемое значение | Настройка запрошенной подписи |
|---|---|
| SignatureID* | ID подписи, которая будет запрошена |
| Name | Название подписи (может быть произвольная строка) |
| AcceptorsString | Список ID пользователей-акцептантов через запятую |
| MakeAcceptorsSubscribers | Признак, назначаются ли акцептанты подписчиками задачи |
| DeadlineTime | Срок обработки подписи |
| MinutesToSign | Количество минут, отведенных на обработку подписи |
| SmartMinutesToSign | ID смарт-выражения, которое возвращает количество минут, отведенных на подписание |
| RequestReason | Причина запроса подписи |
| SmartRequestReason | ID смарт-выражения, которое возвращает причину запроса подписи |
| SmartFilterID | ID смарт-фильтра, по которому определяется, надо ли запрашивать подпись |
| GUID | GUID |
| Stage* | Номер этапа согласования |
| StoredProcedureName | Название хранимой процедуры, которая выполняется при запросе подписи |
| ExcludeAlreadySigned | Признак, надо ли запрашивать повторно подпись у акцептантов, ранее принявших положительное решение, если согласование запущено повторно |
| AcceptorsEvaluationBaseUser | Базовый пользователь, относительно которого выполняется алгоритм определения акцептантов, определяется: 0 — По заказчику; 1 — По исполнителю; 2 — По ответственному исполнителю; 3 — По запросившему; 4 — По смарт-выражению |
| AcceptorsEvaluationMethod | Метод определения акцептантов: 0 — По группам; 1 — По параметру; 2 — По руководителям; 3 — По смарт-выражению; 4 — По уровню орг структуры |
| AcceptorsExtParamID | Если акцептанты определяются по ДП, то ID ДП |
| AcceptorsTableName | Название таблицы для определения акцептантов (если акцептанты определяются по ДП и значения берутся из таблицы) |
| AcceptorsExtParamValueColumn | Название колонки значений ДП (в таблице для определения акцептантов, см. выше) |
| AcceptorsNickColumn | Название колонки со списком акцептантов для данного значения ДП (в таблице для определения акцептантов, см. выше) |
| AcceptorsSmartExpressionId | Если акцептанты определяются смарт-выражением, то ID смарт-выражения |
| AcceptorsBaseUserSmartExpressionId | ID смарт-выражения для определения базового пользователя, относительно которого выполняется алгоритм определения акцептанта |
| SplitForEachAcceptor | Признак, запрашивается ли подпись у каждого акцептанта отдельно (1 — да, 0 — нет) |
| ActionIfNotSigned | Действие при отклонении (0 — Прервать согласование; 1 -- Ожидать решения всех участников; 2 -- Продолжить согласование) |
| SetStateIfNotSigned | ID статуса, переход в который выполняется при отклонении подписи |
| MakeStepIfNotSigned | ID перехода, который выполняется при отклонении подписи |
ℹ️ Если для заданной подписи указаны параметры для автоматического определения акцептантов, то они будут иметь приоритет
ℹ️ Хранимая процедура выполняется столько раз, сколько этапов на маршруте согласования. Это сделано потому, что подписи, которые запрашиваются на последующих этапах, могут зависеть от резолюций, вынесенных на предыдущих этапах. Например, если на первом этапе юрист акцептовал подпись, то на втором этапе запрашивается подпись финансового директора. Если же на первом этапе юрист вынес другую резолюцию (например, "Подписать с замечаниями"), то на втором этапе запрашивается подпись генерального директора.
ℹ️ Хранимая процедура может возвращать не только подписи текущего этапа, но и все подписи, которые должны запрашиваться на текущем и всех последующих этапах (иногда такой код получается более простым). В любом случае система определяет минимальный этап среди всех возвращенных и запрашивает подписи только для этого этапа, поэтому минимальным должен быть текущий этап.
ℹ️ В ходе выполнения динамического маршрута согласования инициируются события, связанные с динамическими подписями: запрос, подписание и отклонение динамической подписи.
Примеры динамического маршрута согласования¶
Запрос подписей по матрице согласования¶
Часто динамический маршрут согласования используется, когда набор запрашиваемых подписей зависит от сочетания нескольких бизнес-условий.
Пример: если сумма сделки больше 1 млн. руб. и менеджер предоставил ему скидку в 10%, то запрашиваются сначала подпись финансового директора, а затем подпись генерального директора. Кроме того, если клиентский менеджер относится к региональному филиалу, то запрашивается подпись руководителя филиала, а если к центральному офису — то подпись коммерческого директора.
Условия запроса подписей могут со временем меняться — например, размер скидки, при которой требуется подпись генерального директора, изменился с 10% до 15%, минимальная сумма сделки выросла с 1 до 5 млн. руб., а от запроса подписи коммерческого директора было решено отказаться.
В этом случае на статическом маршруте администратору пришлось бы заново настраивать условия запроса подписей. Чтобы избежать этого, условия запроса подписей выносятся в отдельный справочник, а маршрут согласования строится динамически, SQL-процедурой, которая анализирует записи этого справочника. При изменении условий — например, размера скидки — ответственному пользователю достаточно изменить значение ДП в справочнике, это выполняется без участия администратора системы. Если от запроса какой-то подписи было решено отказаться, соответствующая запись справочника переводится в статус Отменено (или аналогичный).
Записи справочника "Маршрут согласования", исходные значения:
| ДП "Размер сделки больше чем" | ДП "Процент скидки больше или равен" | ДП "Региональный филиал" | Этап согласования | Запрашивается подпись |
|---|---|---|---|---|
| 1 000 000 | 10 | Не важно | 1 | Подпись финансового директора |
| 1 000 000 | 10 | Не важно | 2 | Подпись генерального директора |
| 0 | 0 | Да | 1 | Подпись руководителя филиала |
| 0 | 0 | Нет | 1 | Подпись коммерческого директора |
Записи справочника "Маршрут согласования сделки", новые значения:
| ДП "Размер сделки больше чем" | ДП "Процент скидки больше или равен" | ДП "Региональный филиал" | Этап согласования | Запрашивается подпись |
|---|---|---|---|---|
| 5 000 000 | 15 | Не важно | 1 | Подпись финансового директора |
| 5 000 000 | 15 | Не важно | 2 | Подпись генерального директора |
| 0 | 0 | Да | 1 | Подпись руководителя филиала |
SQL-процедура для построения динамического маршрута согласования:
ALTER PROCEDURE [dbo].[getSignaturesSales]
(
@TaskID int,
@RequestingUserID int,
@Stage int
)
AS
BEGIN
DECLARE @KM int -- Клиентский менеджер (КМ)
DECLARE @OrgUnit table (id int) -- Орг. единицы
SET @KM = (SELECT TOP 1 userid FROM ExtParamSelectUsersValues WHERE TaskID = @TaskID AND ExtParamID = 34)
INSERT INTO @OrgUnit
SELECT id FROM tc_GetUsersOrgUnitsRecurcive(@KM) -- используется процедура, поставляемая вместе с 1Формой
SELECT
T.ExtParam1458Value AS SignatureID,
T.ExtParam1457Value AS Stage,
'Просьба согласовать' AS RequestReason,
4 AS AcceptorsEvaluationBaseUser, -- (4 — по смарт-выражению)
1426 AS AcceptorsBaseUserSmartExpressionId,
0 AS ActionIfNotSigned, -- 0 — прервать согласование
25 AS SetStateIfNotSigned
FROM TasksInSubcat1155Denormalized T
JOIN ExtParamSelectUsersValuesOrgUnits UO ON UO.TaskID = T.TaskID
WHERE T.IsClosed = 0
AND T.ExtParam1457value > @Stage
AND CAST(ISNULL(T1.ExtParam1343NativeValue, 0) AS INT) > CAST(ISNULL(T.ExtParam1455Value, 0) AS INT)
AND CAST(ISNULL(T1.ExtParam1344NativeValue, 0) AS INT) > CAST(ISNULL(T.ExtParam1456Value, 0) AS INT)
AND T1.ExtParam1345Value = 'да' AND EXISTS (SELECT ID FROM @OrgUnit WHERE ID IN (UO.OrgStructureUnitID))
ORDER BY Stage
END
Запрос подписей с формированием списка акцептантов в виде XML¶
Данный пример является одним из вариантов запроса подписей по матрице согласования. Отличие в том, что запрашивается всегда "Подпись пользователя", а список акцептантов формируется отдельной процедурой в виде строки XML.
Основная процедура для запроса подписи:
ALTER PROCEDURE [dbo].[cm_dynamicSignature]
@TaskID int = null,
@StepID int = null,
@RequestingUserID int = null,
@Stage int = null
AS
BEGIN
DECLARE @acceptors TABLE (TaskID int, AcceptorsString varchar(1000))
INSERT INTO @acceptors
EXEC cm_matrixAcceptorsSTUFFed @TaskID = @TaskID
DECLARE @matrix TABLE (TaskID int, Stage decimal, AcTime decimal)
INSERT INTO @matrix
exec cm_matrixFilter @TaskID = @TaskID
SELECT DISTINCT
139 AS SignatureID,
'Подпись' AS NAME,
'Согласование' AS RequestReason,
dbo.tc_AddWorkingHours(GETDATE(), matrix.AcTime) AS DeadlineTime,
0 AS MakeAcceptorsSubscribers,
0 AS SplitForEachAcceptor,
0 AS ExcludeAlreadySigned,
acceptors.AcceptorsString,
0 AS ActionIfNotSigned,
matrix.Stage AS Stage
FROM @matrix AS matrix
JOIN @acceptors as acceptors ON matrix.TaskID = acceptors.TaskID
WHERE matrix.Stage > @Stage
AND acceptors.AcceptorsString is not null
END
GO
Процедура для формирования списков акцептантов для подписей:
ALTER PROCEDURE [dbo].[cm_matrixAcceptorsSTUFFed]
@TaskID int = null
AS
BEGIN
SET NOCOUNT ON;
DECLARE @matrix TABLE (TaskID int, Stage decimal, AcTime decimal)
INSERT INTO @matrix
exec cm_matrixFilter @TaskID = @TaskID
SELECT matrix.TaskID,
STUFF(
(SELECT ', ' + CAST(ISNULL(su.UserID, 1) AS varchar(100))
FROM ExtParamSelectUsersValues su
WHERE matrix.TaskID = su.TaskID
AND su.ExtParamID = 4177
FOR XML PATH ('')
), 1, 1, '') AS AcceptorsString
FROM @matrix as matrix
END
GO
Описание настроек динамического маршрута согласования в прежнем интерфейсе администрирования