При автоматизации бизнес-процессов (БП) в среде «1С:Предприятие 8» разработчики регулярно сталкиваются с задачами адресации и логирования. Один из самых частых вопросов: как на определенном шаге процесса определить, какой именно пользователь запустил текущий бизнес-процесс, или кто выполнил задачу на предыдущем этапе? Рассмотрим детально, как решаются эти задачи с помощью встроенных механизмов платформы, готовых конфигураций и оптимизации архитектуры базы данных.
В простейшем случае, когда нам нужно узнать создателя текущего бизнес-процесса из контекста выполняющейся задачи, мы можем обратиться к реквизитам связанных объектов. Разберем по шагам, где хранится эта информация.
У стандартной задачи всегда есть реквизит, указывающий на бизнес-процесс, в рамках которого она была создана. Обычно это реквизит БизнесПроцесс. У самого объекта бизнес-процесса по умолчанию (или в соответствии со стандартами разработки) присутствует реквизит Автор (или Инициатор). Таким образом, простейшее обращение из контекста задачи выглядит следующим образом:
// Получаем автора бизнес-процесса через задачу
ИнициаторПроцесса = СсылкаНаЗадачу.БизнесПроцесс.Автор;
Если же мы работаем в конфигурации «1С:Документооборот» (1С:ДО), структура усложняется из-за вложенности процессов. В этой системе задачи могут порождаться вложенными бизнес-процессами. Чтобы добраться до первоначального автора всей цепочки, нам придется использовать реквизит ГлавнаяЗадача. Связка для получения автора в 1С:ДО будет выглядеть так:
// Получение автора главного бизнес-процесса в 1С:ДО
Инициатор = СсылкаНаЗадачу.БизнесПроцесс.ГлавнаяЗадача.БизнесПроцесс.Автор;
Если вы разрабатываете собственную конфигурацию с нуля или существенно дорабатываете существующую, постоянное обращение через несколько «точек» (обращение к реквизитам через другие ссылки) может привести к снижению производительности системы при росте базы данных. Рассмотрим более изящное архитектурное решение.
Для оптимизации рекомендуется добавить в каждую задачу и в каждый бизнес-процесс два дополнительных реквизита:
БизнесПроцессСсылка) — прямая ссылка на самый первый (корневой) бизнес-процесс в цепочке вложений.СправочникСсылка.Пользователи) — ссылка на пользователя, который запустил текущий объект.При таком подходе вам не придется строить длинные цепочки обращений. Вы сможете мгновенно получить нужные данные:
СсылкаНаЗадачу.Инициатор вернет пользователя, который создал конкретную задачу.СсылкаНаЗадачу.ГлавныйБП.Инициатор вернет того, кто запустил самый первый бизнес-процесс, независимо от уровня вложенности текущей задачи.Вернемся к более сложному вопросу: как определить пользователя, который выполнил задачу на предыдущем шаге бизнес-процесса? Платформа 1С не предоставляет для этого единой готовой функции, но мы можем решить эту задачу несколькими надежными способами.
Проанализируем самый простой программный метод, который не требует изменения метаданных конфигурации. Мы можем сделать запрос к таблице задач, выбрав последнюю по времени выполненную задачу в рамках нашего бизнес-процесса.
Выясним важную деталь: у стандартных задач в типовых конфигурациях (особенно использующих БСП) есть реквизит Исполнитель и реквизит ФактическийИсполнитель. Реквизит Исполнитель может содержать роль (например, «Отдел продаж» или «Бухгалтер»), а вот ФактическийИсполнитель хранит ссылку на конкретного пользователя (физическое лицо), который нажал кнопку «Выполнено» в форме задачи. Нам нужен именно он.
Напишем и разберем следующий запрос:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Задача.ФактическийИсполнитель КАК Исполнитель,
| Задача.ТочкаМаршрута КАК ТочкаМаршрута,
| Задача.ДатаИсполнения КАК ДатаИсполнения
|ИЗ
| Задача.ЗадачаИсполнителя КАК Задача
|ГДЕ
| Задача.БизнесПроцесс = &ТекущийБизнесПроцесс
| И Задача.Выполнена = ИСТИНА
|УПОРЯДОЧИТЬ ПО
| ДатаИсполнения УБЫВ";
Запрос.УстановитьПараметр("ТекущийБизнесПроцесс", Объект.Ссылка);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
ПредыдущийИсполнитель = Выборка.Исполнитель;
КонецЕсли;
Важное ограничение: Этот способ отлично работает на линейных маршрутах. Однако, если ваш бизнес-процесс содержит параллельные ветки, сортировка по дате выполнения может вернуть задачу из соседней ветки, которая не является прямой предшественницей текущего этапа.
Для сложных бизнес-процессов с ветвлениями и параллельными действиями применим более строгий подход. Проанализируем структуру карты маршрута программно.
У каждой точки маршрута бизнес-процесса есть метод, позволяющий получить массив всех точек, которые ведут к ней непосредственно. Алгоритм выглядит следующим образом:
ПолучитьВходящиеТочки().Посмотрим на пример реализации такого алгоритма:
// 1. Получаем массив входящих точек маршрута
ТекущаяТочка = Элементы.ИмяТекущейТочкиМаршрута; // или из контекста задачи
МассивВходящихТочек = ТекущаяТочка.ПолучитьВходящиеТочки();
// 2. Делаем запрос с отбором по точкам-предшественникам
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Задача.ФактическийИсполнитель КАК Исполнитель
|ИЗ
| Задача.ЗадачаИсполнителя КАК Задача
|ГДЕ
| Задача.БизнесПроцесс = &ТекущийБизнесПроцесс
| И Задача.ТочкаМаршрута В (&МассивВходящихТочек)
| И Задача.Выполнена = ИСТИНА
|УПОРЯДОЧИТЬ ПО
| Задача.ДатаИсполнения УБЫВ";
Запрос.УстановитьПараметр("ТекущийБизнесПроцесс", Объект.Ссылка);
Запрос.УстановитьПараметр("МассивВходящихТочек", МассивВходящихТочек);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
ПредыдущийИсполнитель = Выборка.Исполнитель;
КонецЕсли;
Этот метод гарантирует стопроцентную точность определения предшественника даже в комплексных разветвленных схемах.
Если бизнес-процессы выполняются интенсивно, постоянные динамические запросы к базе данных при открытии каждой задачи могут замедлить работу интерфейса. Рассмотрим наиболее производительный и надежный с архитектурной точки зрения способ — событийную модель.
Создадим в самом бизнес-процессе реквизит ИсполнительПредыдущегоЭтапа (тип СправочникСсылка.Пользователи). Теперь нам достаточно записывать туда значение в момент, когда пользователь завершает выполнение задачи.
Откроем модуль задачи и найдем предопределенный обработчик события ПередВыполнением (или ПриВыполнении). Пропишем в нем следующий код:
Процедура ПередВыполнением(Отказ)
// Блокируем и получаем объект бизнес-процесса для записи
БизнесПроцессОбъект = ЭтотОбъект.БизнесПроцесс.ПолучитьОбъект();
Если БизнесПроцессОбъект <> Неопределено Тогда
// Записываем текущего пользователя сеанса в реквизит БП
БизнесПроцессОбъект.ИсполнительПредыдущегоЭтапа = ПараметрыСеанса.ТекущийПользователь;
Попытка
БизнесПроцессОбъект.Записать();
Исключение
ВызватьИсключение "Не удалось обновить информацию о предыдущем исполнителе: " + ОписаниеОшибки();
КонецПопытки;
КонецЕсли;
КонецПроцедуры
Преимущества этого способа очевидны: при формировании задачи на следующем этапе или при открытии ее формы системе больше не нужно выполнять тяжелые поисковые запросы к СУБД. Мы мгновенно получаем исполнителя предыдущего шага простым обращением к реквизиту бизнес-процесса.
Если ваша конфигурация разработана на основе БСП или вы работаете непосредственно в «1С:Документооборот», обратите внимание на то, что там уже реализована встроенная подсистема хранения истории выполнения задач.
В 1С:ДО за это отвечает специализированный регистр сведений, фиксирующий все действия с бизнес-процессами. Для извлечения истории вам не нужно писать собственные запросы. Достаточно использовать функции программного интерфейса (API) общих модулей, таких как РаботаСБизнесПроцессами или БизнесПроцессыИЗадачиСервер. Эти методы позволяют одной строкой получить полную цепочку исполнителей со всеми комментариями и датами согласования.
Мы подробно разобрали несколько подходов к определению исполнителей на разных этапах работы бизнес-процесса. Выбор конкретного метода зависит от архитектуры вашего решения: