Как правильно организовать контроль завершения нескольких параллельных вложенных бизнес-процессов в 1С?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При проектировании сложных бизнес-процессов в платформе 1С разработчики нередко сталкиваются с необходимостью динамического запуска нескольких одинаковых или параметризуемых подпроцессов из одной точки маршрута. Типичный сценарий: ведущий процесс должен разделиться на произвольное число параллельных потоков (например, согласование по списку согласующих, параллельная обработка строк заказа, независимые поручения исполнителям), дождаться завершения каждого из них и только затем продолжить движение по карте маршрута к следующей точке.

Разберем типичную проблему: при попытке сгенерировать несколько вложенных процессов под одну ведущую задачу основной процесс неожиданно уходит дальше по маршруту сразу же после завершения первого из запущенных дочерних процессов. Попытка заблокировать это поведение через установку флага Отказ = Истина в событиях ПередВыполнением ведущей задачи приводит к откату всей системной транзакции и невозможности завершить даже сам вложенный процесс. Проанализируем архитектурные особенности платформы 1С и выясним, как правильно реализовать данный сценарий.

Внутренняя механика платформы: почему возникает ошибка преждевременного завершения

Выясним причину такого системного поведения платформы. В объектной модели 1С связь между ведущей задачей и вложенным бизнес-процессом построена строго по принципу «один к одному»:

  1. В реквизите ВедущаяЗадача любого бизнес-процесса хранится ссылка на конкретный объект задачи.
  2. Сама ведущая задача платформы не содержит счетчиков запущенных процессов и коллекций ссылок на них. Она не знает, сколько именно процессов ссылается на неё.
  3. Как только любой подчиненный бизнес-процесс завершается (достигает точки завершения маршрута, и реквизит Завершен принимает значение Истина), платформа автоматически вызывает механизм выполнения для задачи, записанной в его реквизите ВедущаяЗадача.
  4. Если несколько одновременно стартовавших процессов были привязаны к одной и той же ведущей задаче, завершение самого быстрого из них немедленно выполняет ведущую задачу, переводя родительский бизнес-процесс на следующую точку маршрута.

Посмотрим на поведение транзакции: перевод вложенного процесса в статус завершенного, запись задачи вложенного процесса и выполнение ведущей задачи происходят в единой системной транзакции. Поэтому перехват события ПередВыполнением ведущей задачи с выставлением Отказ = Истина не решает проблему — платформа закономерно откатывает всю транзакцию целиком, возвращая задачу вложенного процесса в исходное незавершенное состояние.

Штатный механизм: создание отдельной ведущей задачи под каждый процесс

Рассмотрим штатный подход, заложенный разработчиками платформы 1С и используемый в типовых конфигурациях (например, в «1С:Документооборот»). Платформа ожидает перехода к следующей точке маршрута только тогда, когда выполнены все задачи текущей точки. Следовательно, правильная архитектура требует генерации отдельной ведущей задачи под каждый экземпляр вложенного бизнес-процесса.

Разберем по шагам последовательность обработки событий точки маршрута ВложенныйБизнесПроцесс:

  1. В обработчике ПередСозданиемЗадач или ПриСозданииЗадач мы генерируем массив задач. Количество создаваемых ведущих задач должно точно соответствовать количеству требуемых параллельных процессов.
  2. Платформа сохраняет задачи в информационной базе.
  3. Затем вызывается обработчик ПриСозданииВложенныхБизнесПроцессов. В этот момент платформа автоматически создает массив бизнес-процессов, где каждый процесс уже связан через реквизит ВедущаяЗадача со своей персональной ведущей задачей.
  4. В обработчике ПриСозданииВложенныхБизнесПроцессов мы настраиваем реквизиты каждого создаваемого процесса, используя переданные платформой параметры.

Посмотрим на пример кода в модуле основного бизнес-процесса:


Процедура ТочкаПараллельныхПроцессовПриСозданииЗадач(ТочкаМаршрутаБизнесПроцесса, ФормируемыеЗадачи, Отказ)
    
    // Получаем список адресатов или аналитик для параллельных веток
    ТаблицаПараметров = ПолучитьСписокВложенныхПотоков();
    
    Для Каждого СтрПараметр Из ТаблицаПараметров Цикл
        НоваяЗадача = Задачи.ЗадачаИсполнителя.СоздатьЗадачу();
        НоваяЗадача.БизнесПроцесс = ЭтотОбъект.Ссылка;
        НоваяЗадача.ТочкаМаршрута = ТочкаМаршрутаБизнесПроцесса;
        НоваяЗадача.Дата = ТекущаяДата();
        НоваяЗадача.Наименование = "Контроль выполнения: " + СокрЛП(СтрПараметр.Наименование);
        
        ФормируемыеЗадачи.Добавить(НоваяЗадача);
    КонецЦикла;
    
КонецПроцедуры

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

При таком подходе каждый дочерний процесс при своем завершении автоматически выполнит исключительно свою ведущую задачу. Родительский бизнес-процесс останется на текущей точке маршрута до тех пор, пока не завершится последний дочерний процесс и не будет закрыта последняя связанная ведущая задача.

Альтернативный вариант: оркестрация без создания множества задач

Если специфика учета или права доступа категорически запрещают плодить множество ведущих задач, рассмотрим альтернативный вариант ручного управления связью между процессами.

Разберем по шагам логику этого решения:

  1. На точке маршрута формируется единственная ведущая задача-координатор.
  2. Вложенные бизнес-процессы создаются и запускаются программно, при этом их реквизит ВедущаяЗадача намеренно оставляется пустым. Это разрывает системную автоматическую связку транзакций платформы.
  3. Связь с родительским процессом сохраняется через добавленный реквизит (например, РодительскийПроцесс) или специальный регистр сведений.
  4. В модуле дочернего бизнес-процесса в обработчике ПриЗавершении пишем проверку: есть ли еще незавершенные процессы, относящиеся к этому же родителю.
  5. Если запрос показывает, что текущий завершающийся процесс — последний, программно вызываем метод ВыполнитьЗадачу() для ведущей задачи-координатора.

Посмотрим на пример кода в обработчике завершения дочернего процесса:


Процедура ПриЗавершении()
    
    Если ЗначениеЗаполнено(РодительскийПроцесс) Тогда
        
        // Проверяем наличие параллельных активных процессов
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ ПЕРВЫЕ 1
            |	ПодчиненныеБП.Ссылка КАК Ссылка
            |ИЗ
            |	БизнесПроцесс.ВложенныйПроцесс КАК ПодчиненныеБП
            |ГДЕ
            |	ПодчиненныеБП.РодительскийПроцесс = &РодительскийПроцесс
            |	И ПодчиненныеБП.Завершен = ЛОЖЬ
            |	И ПодчиненныеБП.Ссылка <> &ТекущийПроцесс";
            
        Запрос.УстановитьПараметр("РодительскийПроцесс", РодительскийПроцесс);
        Запрос.УстановитьПараметр("ТекущийПроцесс", ЭтотОбъект.Ссылка);
        
        Результат = Запрос.Выполнить();
        
        // Если незавершенных процессов больше нет, закрываем ведущую задачу
        Если Результат.Пустой() Тогда
            ЗадачаОбъект = ПолучитьВедущуюЗадачуКоординатор(РодительскийПроцесс).ПолучитьОбъект();
            Если ЗадачаОбъект <> Неопределено И НЕ ЗадачаОбъект.Выполнена Тогда
                ЗадачаОбъект.ВыполнитьЗадачу();
            КонецЕсли;
        КонецЕсли;
        
    КонецЕсли;
    
КонецПроцедуры

Штатный механизм с формированием пула ведущих задач через ПриСозданииЗадач является наиболее надежным, прозрачным и поддерживаемым, так как он использует стандартную логику маршрутизации платформы 1С без написания сложного блокирующего кода.

← На главную