Перейти к содержимому

Бизнес-процессы в 1С-Битрикс - модуль bizproc для разработчика

Бизнес-процессы - это визуальные сценарии, которые выполняются над документом: элементом инфоблока, элементом списка, сделкой CRM. Разберём модуль bizproc глазами разработчика: как процесс запускается из кода, что можно делать в действии «PHP-код» и как написать собственное действие.

Как это работает

Документ, шаблон, экземпляр. Процесс всегда выполняется НАД конкретным документом. Схему описывает шаблон, который создают в Дизайнере бизнес-процессов, а работающий процесс - это экземпляр шаблона. Экземпляров одного шаблона может выполняться много одновременно. Важная деталь: изменения шаблона не влияют на уже запущенные экземпляры - они доигрывают по своей копии схемы.

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

Четыре способа запуска. Вручную из интерфейса; автоматически при создании или изменении документа; по REST методом bizproc.workflow.start; возобновлением после паузы или внешнего события. Программный аналог ручного запуска - CBPDocument::StartWorkflow().

Действие (activity) - кирпич процесса. Все действия наследуют CBPActivity, составные - CBPCompositeActivity. Стандартный набор включает CBPCodeActivity (PHP-код), CBPIfElseActivity (ветвление), CBPWhileActivity (цикл), CBPParallelActivity (параллельное выполнение), CBPDelayActivity (пауза), CBPHandleExternalEventActivity (ожидание внешнего события). Метод Execute() возвращает CBPActivityExecutionStatus::Closed при завершении или ::Executing, если действие продолжает работать.

События процесса и события ядра - разные вещи. Не путайте механизмы: события бизнес-процесса живут внутри bizproc, а обработчики событий ядра описаны в статье про события и агенты.

Примеры

1. Запуск процесса из кода

CBPDocument::StartWorkflow(
6, // ID шаблона (свой для каждой установки)
['iblock', 'CIBlockDocument', $documentId], // код документа
['Voters' => ['user_1']], // параметры шаблона
$arErrors // ошибки заполняются по ссылке
);

Код документа - это тройка «модуль-владелец, класс документа, идентификатор». Обратите внимание на первый элемент: класс CIBlockDocument принадлежит модулю iblock, поэтому там iblock, а не bizproc. Тип шаблона обязан совпадать с типом документа.

Выполнить задание согласования из кода тоже можно - через внешнее событие:

CBPDocument::SendExternalEvent(
'5046fe0fbf1888.64722245', // идентификатор экземпляра процесса
'Approve1', // системное имя действия-согласования
['USER_ID' => 1, 'APPROVE' => true] // false означает отклонение
);

Состав параметров смотрите в коде самого действия - угадывать здесь нельзя.

2. Действие «PHP-код»: доступ к контексту

$rootActivity = $this->GetRootActivity();
$value = $rootActivity->GetVariable('variable_name'); // чтение переменной
$param = $rootActivity->parameter_name; // чтение параметра шаблона
$const = $rootActivity->GetConstant('constant_name'); // константа, только чтение
$rootActivity->SetVariable('variable_name', $newValue); // запись переменной
$rootActivity->parameter_name = $newValue; // запись параметра

Корневая активность - единая точка доступа к контексту процесса. Поля самого документа читают через сервис документов:

$documentService = $this->workflow->GetService('DocumentService');
$document = $documentService->getDocument($this->getDocumentId());
$fieldValue = $document['NAME'];

Два правила, которые экономят часы отладки в «PHP-коде». Первое: модули там не подключены автоматически, поэтому перед любым вызовом API нужен CModule::IncludeModule(), иначе получите фатальную ошибку. Второе: не подставляйте в PHP-код значения через выражения вида {=Document:NAME} - их правят пользователи портала, и это открытая дверь для инъекций. Читайте данные через API: GetVariable() и сервис документов.

Ещё одна мелочь с большими последствиями: для множественных полей GetVariable() возвращает массив, а для единичных - скаляр. Перед перебором приводите значение к массиву явно.

3. Собственное действие

class CBPMyActivity extends CBPActivity
{
public function __construct($name)
{
parent::__construct($name);
$this->arProperties = ['Title' => '', 'MyText' => ''];
}
public function Execute()
{
$rootActivity = $this->GetRootActivity();
$this->WriteToTrackingService($rootActivity->GetVariable('Text'));
return CBPActivityExecutionStatus::Closed;
}
public static function GetPropertiesDialog(
$documentType, $activityName, $arWorkflowTemplate,
$arWorkflowParameters, $arWorkflowVariables,
$arCurrentValues = null, $formName = '')
{
// HTML формы настроек действия
}
public static function GetPropertiesDialogValues(
$documentType, $activityName, &$arWorkflowTemplate,
&$arWorkflowParameters, &$arWorkflowVariables,
$arCurrentValues, &$arErrors)
{
// разбор и сохранение значений свойств
}
}

Свойства объявляют в конструкторе через arProperties и читают как обычные поля объекта. Форму настроек в Дизайнере рисуют статические методы GetPropertiesDialog и GetPropertiesDialogValues. Рядом с классом кладут файл описания .description.php.

Для длительных операций возвращают Executing - тогда действие остаётся активным и будет продолжено позже.

Справочник API

APIНазначениеОсобенности
CBPDocument::StartWorkflow()запуск процесса из кодакод документа - тройка «модуль, класс, ID»; ошибки по ссылке
CBPDocument::SendExternalEvent()выполнение задания из кодапараметры смотреть в коде действия
bizproc.workflow.startзапуск по REST
CBPActivityбазовый класс действиясвойства в arProperties, точка входа Execute()
CBPCompositeActivityбазовый класс составного действияможет содержать дочерние
CBPActivityExecutionStatus::Closed / ::Executingрезультат Execute()второй - когда действие продолжает работать
GetRootActivity()контекст процессаGetVariable, SetVariable, GetConstant, параметры шаблона
$this->workflow->GetService('DocumentService')сервис документовактуальные поля документа
WriteToTrackingService()запись в журнал процессавидно в истории выполнения
GetPropertiesDialog() / GetPropertiesDialogValues()форма настроек действиястатические методы
CCrmBizProcHelper::AutoStartWorkflows()автозапуск в CRMв коробке с CRM инициатор - модуль CRM, не bizproc
Crm\Automation\Starterзапуск роботов CRMметоды runOnAdd() и runOnUpdate()

Частые ошибки

«Бизнес-процесс заблокирован другим процессом». Один экземпляр выполняется только в одной копии: попытка запустить второй до завершения первого даёт эту ошибку.

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

Бесконечный цикл согласования. Цикл без корректного условия выхода не остановится. Заводите управляющую переменную-флаг и меняйте её действием «Изменение документа» до входа в цикл и внутри веток.

Подстановка значений прямо в PHP-код. Выражения вида {=Document:FIELD} внутри «PHP-кода» подставляют данные, которые редактируют пользователи портала. Это прямой путь к инъекции. Читайте значения через API.

Фатальная ошибка в «PHP-коде». В этом действии нет автоматически подключённых модулей - вызывайте CModule::IncludeModule() перед обращением к любому API.

Переменная оказалась не тем, чем казалась. Для множественного поля GetVariable() вернёт массив, для единичного - скаляр. Приводите к массиву перед перебором.

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

Частые вопросы

Последовательный процесс или со статусами - что выбрать?

Последовательный подходит для линейного алгоритма с началом и концом: согласовали, уведомили, записали поле, завершились. Процесс со статусами нужен, когда документ живёт в разных состояниях и переходит между ними по правилам - заявка, сделка, задача на производстве. Второй тип к тому же даёт автоматическое управление правами доступа в зависимости от статуса.

Как запустить бизнес-процесс из своего кода?

Методом CBPDocument::StartWorkflow(): передаёте идентификатор шаблона, код документа в виде тройки «модуль, класс документа, ID», параметры шаблона и переменную для ошибок. Помните, что идентификатор шаблона свой для каждой установки - не хардкодьте его вслепую при переносе решения между площадками. В коробке с CRM автозапуск инициирует модуль CRM собственными методами.

Что нельзя делать в действии PHP-код?

Нельзя рассчитывать на подключённые модули - вызывайте CModule::IncludeModule() сами. Нельзя подставлять в код значения через выражения бизнес-процесса: их редактируют пользователи портала, это дыра в безопасности, читайте данные через GetVariable() и сервис документов. И не стоит держать в PHP-коде сложную логику: её лучше вынести в собственное действие или в модуль, где есть версионирование и тесты.

Когда писать собственное действие вместо PHP-кода?

Когда логика повторяется в разных процессах или её должны настраивать не программисты. Собственное действие - это класс с объявленными свойствами и формой настроек в Дизайнере: бизнес-пользователь получает понятный блок с полями вместо куска кода, который никто не рискнёт трогать.

Связанные темы

Первоисточники