Бизнес-процессы в 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-кода?
Когда логика повторяется в разных процессах или её должны настраивать не программисты. Собственное действие - это класс с объявленными свойствами и формой настроек в Дизайнере: бизнес-пользователь получает понятный блок с полями вместо куска кода, который никто не рискнёт трогать.
Связанные темы
- События и агенты - события ядра, не путать с событиями процесса
- Инфоблоки - документы, над которыми выполняются процессы
- Ядро D7 - базовые классы платформы
- Бизнес-процесс над элементом списка - работа с документом на практике
- Бизнес-процессы - решения по модулю
- Раздел Ядро D7