В практике разработки на платформе 1С:Предприятие 8.3 часто возникает задача создания гибких бизнес-процессов, где количество этапов и исполнителей заранее неизвестно и определяется пользователем непосредственно при запуске процесса. Классическая жесткая карта маршрута здесь не подходит. Рассмотрим, как реализовать механизм, при котором задачи формируются программно на основе данных табличной части ЭтапыСогласования, а переход к следующему этапу происходит только после выполнения всех задач текущего уровня.
Для реализации динамического процесса нам не нужно рисовать сложную карту с десятками точек. Разберем наиболее надежный способ — циклическую маршрутизацию. Нам потребуется создать карту маршрута, состоящую всего из нескольких ключевых узлов:
Проанализируем механику: в самом объекте БизнесПроцесс необходимо создать реквизит ТекущийЭтап (тип Число). При старте процесса значение устанавливается в 1. После прохождения точки действия процесс попадает в точку условия, которая либо возвращает его обратно в точку действия (увеличив ТекущийЭтап на единицу), либо направляет к завершению.
Основным инструментом реализации является стандартный обработчик события ПриСозданииЗадач в точке действия карты маршрута. В этом месте мы должны перехватить стандартное поведение платформы. Рассмотрим пример кода, который фильтрует строки табличной части и создает задачи для конкретных исполнителей:
Процедура СогласованиеПриСозданииЗадач(ТочкаМаршрута, ФормируемыеЗадачи, СтандартнаяОбработка)
// Отключаем автоматическое создание задач платформой
СтандартнаяОбработка = Ложь;
// Получаем текущий номер этапа из реквизита бизнес-процесса
НомерЭтапа = ЭтотОбъект.ТекущийЭтап;
// Отбираем строки из табличной части, соответствующие текущему этапу
ПараметрыОтбора = Новый Структура("НомерЭтапа", НомерЭтапа);
СтрокиЭтапа = ЭтотОбъект.ЭтапыСогласования.НайтиСтроки(ПараметрыОтбора);
Если СтрокиЭтапа.Количество() = 0 Тогда
// Если для этапа нет исполнителей, можно либо вызвать исключение,
// либо программно завершить этот шаг
Возврат;
КонецЕсли;
Для Каждого СтрокаТЧ Из СтрокиЭтапа Цикл
// Создаем новый объект задачи
НоваяЗадача = Задачи.ЗадачаИсполнителю.СоздатьЗадачу();
НоваяЗадача.БизнесПроцесс = ЭтотОбъект.Ссылка;
НоваяЗадача.ТочкаМаршрута = ТочкаМаршрута;
НоваяЗадача.Дата = ТекущаяДата();
// Назначаем исполнителя и описание
НоваяЗадача.Исполнитель = СтрокаТЧ.Согласующий;
НоваяЗадача.Наименование = "Согласование на этапе № " + НомерЭтапа;
// Добавляем дополнительные реквизиты, если они есть в задаче
// Например, НомерЭтапа, чтобы потом было проще анализировать
НоваяЗадача.НомерЭтапа = НомерЭтапа;
// Записываем задачу и добавляем в массив формируемых задач
НоваяЗадача.Записать();
ФормируемыеЗадачи.Добавить(НоваяЗадача.Ссылка);
КонецЦикла;
КонецПроцедуры
Важно помнить, что платформа 1С автоматически ожидает выполнения всех задач, созданных в одной точке маршрута и добавленных в массив ФормируемыеЗадачи. Пока последний исполнитель не отметит свою задачу как выполненную, бизнес-процесс не перейдет к следующей точке условия.
Выясним, как реализовать логику «если один отклонил — все завершается». Для этого нам понадобится обработчик в модуле задачи или в самом бизнес-процессе. Рассмотрим реализацию через событие ПриЗавершенииЗадачи в модуле БизнесПроцесс.
Проанализируем ситуацию: когда пользователь выполняет задачу, мы должны проверить его вердикт. Если он отрицательный, мы принудительно останавливаем весь процесс.
Процедура ПриЗавершенииЗадачи(Задача, Отказ)
// Проверяем результат выполнения задачи
// Предположим, в задаче есть реквизит РезультатСогласования (Перечисление)
Если Задача.РезультатСогласования = Перечисления.РезультатыСогласования.Отклонено Тогда
// Прерываем бизнес-процесс
ЭтотОбъект.Завершен = Истина;
ЭтотОбъект.Состояние = Перечисления.СостоянияБизнесПроцессов.Завершен;
ЭтотОбъект.Записать();
// Опционально: можно пометить остальные задачи этого этапа как неактуальные
// Но платформа при завершении БП сама перестанет их учитывать в маршруте
КонецЕсли;
КонецПроцедуры
Когда все задачи текущего этапа выполнены успешно, процесс переходит в точку условия. Здесь мы должны решить: идти на следующий круг или заканчивать. Разберем код для обработчика проверки условия:
Процедура ЕстьСледующийЭтапПроверкаУсловия(ТочкаМаршрута, Результат)
// Проверяем, есть ли еще записи в ТЧ с номером больше текущего
МаксимальныйЭтап = ЭтотОбъект.ЭтапыСогласования.Итог("НомерЭтапа");
Если ЭтотОбъект.ТекущийЭтап < МаксимальныйЭтап Тогда
// Увеличиваем счетчик этапа
ЭтотОбъект.ТекущийЭтап = ЭтотОбъект.ТекущийЭтап + 1;
ЭтотОбъект.Записать();
Результат = Истина; // Возвращаемся в точку действия
Иначе
Результат = Ложь; // Переходим к завершению
КонецЕсли;
КонецПроцедуры
При программном формировании задач следует учитывать несколько критически важных моментов:
ПриСозданииЗадач происходит внутри транзакции. Если в коде возникнет ошибка, транзакция откатится, и бизнес-процесс может "зависнуть" в неопределенном состоянии. Всегда используйте блоки Попытка...Исключение при сложной логике.КомплексныйПроцесс уже содержит в себе возможность настройки последовательно-параллельных этапов, что может избавить вас от написания кода вручную.Таким образом, комбинируя СтандартнаяОбработка = Ложь и циклическую структуру карты маршрута, мы получаем гибкий инструмент, позволяющий автоматизировать согласование любой сложности, сохраняя при этом прозрачность и управляемость процесса в среде 1С.