Действия
Table of Contents
Все, что исполняется в бизнес-процессе, является действием (по-другому активити), а сам бизнес-процесс представляет собой набор вложенных действий. Каждое такое действие в рамках шаблона бизнес-процесса имеет уникальное имя.
С точки зрения кода действие — это php-класс, который наследуется от абстрактного класса CBPActivity или его потомков. Его название класса должно начинаться с подстроки CBP и может состоять из латинских букв и цифр.
Например:
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die();
class CBPMyActivity1
extends \CBPActivity
{
// . . .
}
В дистрибутиве по умолчанию есть несколько десятков различных действий (Activity) для использования их в шаблонах бизнес-процессов.
Для любопытных рекомендуем изучить работу некоторых базовых действий для составления полной картины:
CBPCompositeActivity- абстрактный базовый класс составных действий, т.е. действий, которые могут содержать в себе дочерние действия.CBPSequenceActivity- Последовательно запускает набор дочерних действий.CBPCodeActivity- самое простое действие - запускает на выполнение произвольный PHP-код.CBPSetVariableActivity- устанавливает значения переменных бизнес-процесса.CBPDelayActivity- Реализует ожидание, откладывая выполнение на определенный срок.CBPHandleExternalEventActivity- Реализует действие, которое ожидает внешнее событие. Бизнес-процесс останавливается до получения данного внешнего события.CBPIfElseActivityиCBPIfElseBranchActivity- реализуют функционал условия и ветки условия.CBPWhileActivity- Реализует функционал цикла.CBPListenActivity- Реализует ожидание одного из нескольких возможных событий. Когда одно из событий происходит, остальные перестают ожидать событий и отменяются.CBPParallelActivity- Параллельно запускает набор дочерних действий.
Несмотря на наличие большого количества поставляемых по-умолчанию действий иногда возникает потребность в создании собственных.
Классификация действий
С точки зрения выполнения бизнес-процесса каждое действие можно классифицировать на простые и комплексные. Комплексные (композитные) могут состоять из нескольких простых действий.
Дополнительно действия можно разделить по характеру выполнения на немедленные, задания и событийные.
- Немедленные - выполняются в рамках бизнес-процесса, не блокируют процесс выполнения в нормальном режиме работы.
- Событийные - выполняются, проверяют условие (необходимость) ожидания события, ставят процесс на паузу до наступления события или даты окончания ожидания.
- Задания - выполняются, останавливают свое выполнение до выполнения задания сотрудников или даты окончания ожидания.
По-умолчанию любое действие является немедленным.
Для того чтобы сделать действие событийным, оно должно реализовывать интерфейсы IBPEventActivity и IBPActivityExternalEventListener, о событийных действиях мы поговорим позже.
Задания в свою очередь можно рассматривать как событийное действие с дополнительными возможностями - нужно наследоваться от CBPCompositeActivity, содержать дополнительные методы и реализовывать те же интерфейсы, что и событийная модель.
Создание своего действия
Свои действия располагаются в одной из директорий (перечислены в порядке приоритета от корня сайта):
/local/activities/local/activities/custom/bitrix/activities/custom/bitrix/activities/bitrix/bitrix/modules/bizproc/activities
Каждое действие располагается в отдельной директории и ее название должно совпадать с именем класса действия, но без первых символов CBP. Кроме того имя директории должно быть записано строчными буквами (в нижнем регистре).
В директории действия могут располагаться и другие необходимые действию файлы: файл с описанием, локализация, изображения или ресурсы. Рассмотрим содержимое директории действия.
.description.php
В файле .description.php описывается переменная $arActivityDescription которая содержит мета-описание необходимое для корректной работы действия.
Пример:
<?if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die();
$arActivityDescription = array(
"NAME" => GetMessage("MYACTIVITY_DESCR_NAME"),
"DESCRIPTION" => GetMessage("MYACTIVITY_DESCR_DESCR"),
"TYPE" => "activity",
"CLASS" => "MyActivity",
"JSCLASS" => "BizProcActivity",
"CATEGORY" => array(
"ID" => "other",
),
);
?>
Рассмотрим все возможные ключи массива $arActivityDescription:
NAME(string) - Локализованное название действия - отображается в списке действий, а так же в заголовке всплывающего окна настроекDESCRIPTION(string) - Локализованное описание действия - отображается во всплывающем окне настроекTYPE(string or array)- Тип действия. В случае условия может принимать толькоcondition, в случае действия может быть либоactiviy, либоrobot_activityлибо массивом из этих же элементовCLASS(string) - название php-класса обработчика действия. Должен совпадать с названием директорииJSCLASS(string) - название js-класса обработчика действия. По-умолчаниюBizProcActivity.CATEGORY(array) - опциональное описание раздела для отображения в дизайнере БП в случаеTYPE=activityROBOT_SETTINGS(array) - опциональное описание раздела для отображения в роботах в случаеTYPE=robot_activityFILTER(array) - структура описывающая условия отображения действияRETURN(array) - структура описывающая возвращаемые значенияADDITIONAL_RESULT(array) - набор из кодов свойств действия, возвращающихся как дополнительные значения, которые могут быть переданы в другие действия во время выполнения бизнес-процесса.
CATEGORY
Для размещения свое действия в дизайнере бизнес-процесса необходимо указать в каком разделе оно будет отображаться. Обычно разрабатываемые действия отображаются в категории “Другое”, что можно описать как:
'CATEGORY' => [
'ID' => 'other',
]
Это является минимально необходимым описанием для размещения действия в панели редактора шаблона бизнес-процесса.
Однако иногда бывает полезным создать собственный раздел. Для этого в категории необходимо указать еще несколько ключей: OWN_ID (string) - симв. код нового раздела и OWN_NAME (string) - отображаемое название раздела
Например, так:
'CATEGORY' => [
'ID' => 'own_super_group',
'OWN_ID' => 'own_super_group',
'OWN_NAME' => 'My own super group',
],
ROBOT_SETTINGS
Аналогично структуре CATEGORY есть структура описывающая расположение карточки робота в интерфейсе выбора роботов:
'ROBOT_SETTINGS' => [
'GROUP' => ['elementControl'],
'SORT' => 2800,
],
Описание структуры:
GROUP- набор групп в которых отображается робот.SORT- приоритет отображения робота в группе.
Список доступных групп (на момент написания статьи):
clientCommunication- Коммуникация с клиентомinformingEmployee- Информирование сотрудниковemployeeControl- Контроль сотрудниковpaperwork- Оформление документовpayment- Оплата товаров и услугdelivery- Управление доставкойrepeatSales- Повторные продажиads- Запуск рекламыelementControl- Управление элементомclientData- Данные о клиентахtaskManagement- Управление задачамиmodificationData- Хранение и изменение данныхdigitalWorkplace- Автоматизация рабочих местother- Другие роботы
FILTER
Некоторые действия имеют смысл только в определенных документах, поэтому существует специальные ограничения для сокрытия и показа таких действий. За подобную работу отвечает ключ FILTER, который может содержать 2 ключа:
INCLUDE- описывает типы документов к которым применимо данное активитиEXCLUDE- описывает типы документов к не применимо данное активити
Например, разрешить действие только для шаблонов по сделкам:
"FILTER" => [
'INCLUDE' => [
['crm', 'CCrmDocumentDeal'],
]
],
RETURN
Действие может возвращать значения в ходе своего выполнения. Для описания возвращаемых значений в мета-описании действия необходимо использовать ключ RETURN, содержащий структуры возвращаемых значений.
Например:
'RETURN' => [
'ElementId' => [
'NAME' => 'Displayed name for ElementId property',
'TYPE' => 'int',
],
'ErrorMessage' => [
'NAME' => 'Displayed name for Error message',
'TYPE' => 'string',
],
],
Доступные типы (TYPE) возвращаемых значений описаны константами в классе Bitrix\Bizproc\FieldType.
Например:
FieldType::BOOL(bool)FieldType::DATE(date)FieldType::DATETIME(datetime)FieldType::DOUBLE(double)FieldType::FILE(file)FieldType::INT(int)FieldType::SELECT(select)FieldType::INTERNALSELECT(internalselect)FieldType::STRING(string)FieldType::TEXT(text)FieldType::USER(user)FieldType::TIME(time)
Значения возвращаемые в
RESULTбез перечисления вADDITIONAL_RESULTнедоступны для использования в других действиях бизнес-процессов.
ADDITIONAL_RESULT
Возвращаемые значения действия (RESULT) имеют ряд недостатков:
- Они должны быть объявлены явно, т.е. нет возможности динамически определять их состав
- Их нельзя использовать при настройке других действий бизнес-процессов
Чтобы избежать этой ситуации, разработчики добавили специальный ключ ADDITIONAL_RESULT, содержащий перечисление кодов свойств действия, которые будут транслированы в дизайнер бизнес-процессов и могут быть использованы для вставки в параметры других действий через инструмент “Вставка значения”.
Пример использования в мета-файле:
'ADDITIONAL_RESULT' => [
'FieldsMap'
],
В самом FieldsMap должен содержаться ассоциативный массив, в качестве ключей которого должны выступать свойства действия, а значениями - описание типа значения.
В качестве примера рассмотрим действие “Получить информацию об элементе списка” (GetListsDocumentActivity).
В мета-описании файла определено возвращаемое значение FieldsMap.
Рассмотрим части файла связанные непосредственно с обработкой FieldsMap:
class CBPGetListsDocumentActivity extends CBPActivity
{
// ...
public function __construct($name)
{
// ..
$this->arProperties = [
// ...
"Fields" => null,
"FieldsMap" => null,
];
}
// ..
public function ReInitialize()
{
// ...
$fields = $this->Fields;
if ($fields && is_array($fields))
{
foreach ($fields as $field)
{
$this->{$field} = null;
}
}
}
// ...
public function Execute()
{
// ...
$map = $this->FieldsMap;
// ...
$this->SetPropertiesTypes($map);
$values = [];
foreach ($map as $id => $field)
{
// ...
$this->arProperties[$id] = $document[$id];
}
// ...
}
// ...
public static function GetPropertiesDialogValues($documentType, $activityName, &$arWorkflowTemplate,
&$arWorkflowParameters, &$arWorkflowVariables, $arCurrentValues, &$errors)
{
// ..
$properties['FieldsMap'] = self::buildFieldsMap($properties['DocumentType'], $properties['Fields']);
$arCurrentActivity = &CBPWorkflowTemplateLoader::FindActivityByName($arWorkflowTemplate, $activityName);
$arCurrentActivity["Properties"] = $properties;
// ..
}
// ...
private static function buildFieldsMap(array $documentType, $fields)
{
// ...
$map = [];
foreach ($fields as $field)
{
// ...
$map[$field] = \Bitrix\Bizproc\FieldType::normalizeProperty($documentFields[$field]);
// ...
}
return $map;
}
// ...
}
Файл класса
В указанной главе мы рассмотрим лишь базовые методы для создания действия (не робота), рассмотрим обязательные параметры и доступные настройки.
В классе нашего активити нам необходимо:
- Описать конструктор
__construct, задав значения по-умолчанию для наших свойств - Разработать код для метода
Execute- это тот метод, который будет выполняться в запущенном бизнес-процессе - Разработать код для методов
GetPropertiesDialogиGetPropertiesDialogValues- методы используемые для отображения диалога настроек и сохранения введенных значений
Предположим, наша задача состоит в разработки действия которое будет записывать информацию в определенный (заранее созданный файл /dump.txt).
В конструкторе мы определим необходимые переменные:
public function __construct($name)
{
parent::__construct($name);
$this->arProperties = [
"Title" => "",
"MyText" => ""
];
}
Мы выполнили родительский метод конструктора (совместимость с CBPActivity), а так же определили заранее свойства с которыми будем работать.
Title- название активитиMyText- текст который будем записывать в файл
Поскольку мы хотим задавать этот текст в настройках нашего действия необходимо вначале реализовать методы которые нужны для работы активити в дизайнере бизнес-процесса. Статический метод GetPropertiesDialog должен вернуть html верстку, которая будет встроена в диалог настроек.
public static function GetPropertiesDialog(
$documentType,
$activityName,
$arWorkflowTemplate,
$arWorkflowParameters,
$arWorkflowVariables,
$arCurrentValues = null,
$formName = "",
$form,
$currentSiteId,
$arWorkflowConstants
)
{
// .. code here
}
Нам не обязательно нужно формировать html-верстку прямо в этом файле - мы можем подключить любой файл для формирования. Штатно это делается через менеджер текущего выполнения, например так:
$runtime = \CBPRuntime::GetRuntime();
return $runtime->ExecuteResourceFile(
__FILE__,
"properties_dialog.php",
array(
"arCurrentValues" => $arCurrentValues,
"formName" => $formName,
)
);
В результате выполнения данного фрагмента будет подключен файл properties_dialog.php в директории файла __FILE__ и внутри него будут доступны переменные arCurrentValues и formName.
В нашем случае действие будет выглядеть так:
public static function GetPropertiesDialog(
$documentType,
$activityName,
$arWorkflowTemplate,
$arWorkflowParameters,
$arWorkflowVariables,
$arCurrentValues = null,
$formName = "",
$form,
$currentSiteId,
$arWorkflowConstants
)
{
$runtime = \CBPRuntime::GetRuntime();
if ( !is_array($arWorkflowParameters) )
$arWorkflowParameters = [];
if ( !is_array($arWorkflowVariables) )
$arWorkflowVariables = [];
if ( !is_array($arCurrentValues) )
{
$arCurrentValues = ["my_text" => ""];
$arCurrentActivity= &CBPWorkflowTemplateLoader::FindActivityByName(
$arWorkflowTemplate,
$activityName
);
if ( is_array($arCurrentActivity["Properties"]) )
{
$arCurrentValues["my_text"] = $arCurrentActivity["Properties"]["MyText"];
}
return $runtime->ExecuteResourceFile(
__FILE__,
"properties_dialog.php",
array(
"arCurrentValues" => $arCurrentValues,
"formName" => $formName,
)
);
}
Теперь когда мы реализовали метод открытия диалога необходимо реализовать метод отвечающий за сохранение введенных значений. В классе действия за это отвечает статический метод GetPropertiesDialogValues
Пример:
public static function GetPropertiesDialogValues(
$documentType,
$activityName,
&$arWorkflowTemplate,
&$arWorkflowParameters,
&$arWorkflowVariables,
$arCurrentValues,
&$arErrors,
$arWorkflowConstants,
)
{
$arErrors = [];
$runtime = \CBPRuntime::GetRuntime();
if ( mb_strlen($arCurrentValues["my_text"]) <= 0 )
{
$arErrors[] = [
"code" => "emptyCode",
"message" => GetMessage("MYACTIVITY_EMPTY_TEXT"),
];
return false;
}
$arProperties = ["MyText" => $arCurrentValues["my_text"]];
$arCurrentActivity = &CBPWorkflowTemplateLoader::FindActivityByName(
$arWorkflowTemplate,
$activityName
);
$arCurrentActivity["Properties"] = $arProperties;
return true;
}
Осталось только описать метод, который будет срабатывать во время выполнения действия:
public function Execute()
{
file_put_contents($_SERVER["DOCUMENT_ROOT"]."/dump.txt", $this->MyText, FILE_APPEND);
// Возвратим исполняющей системе указание, что действие завершено
return \CBPActivityExecutionStatus::Closed;
}
Активити в результате своей работы должно вернуть один из следующих статусов:
CBPActivityExecutionStatus::Executing- действие еще не завершило свою работуCBPActivityExecutionStatus::Closed- действие завершило свою работуCBPActivityExecutionStatus::Faulting- ошибка при выполнении действия, прекратить выполнение бизнес-процесса
properties_dialog.php
Код в файле properties_dialog.php отвечает за отображение настроек активити дизайнере бизнес-процессов.
Поскольку на момент написания статьи дизайнер использует табличную верстку, то содержимое диалога так же должно состоять из контента с табличной версткой
Пример:
<?if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die();
?>
<tr>
<td align="right" width="40%"><span style="color:#FF0000;">*</span> :</td>
<td width="60%">
<textarea name="my_text" id="id_my_text " rows="5" cols="40"><?= htmlspecialchars($arCurrentValues["my_text"]) ?></textarea>
<input type="button" value="..." onclick="BPAShowSelector('id_my_text', 'string');">
</td>
</tr>
В рамках диалога можно использовать любой набор переменных определенный и переданный в него через метод GetPropertiesDialog.
В текущем примере пользователь, настраивающий действие в редакторе, может ввести в поле my_text явное значение или выбрать одно из значений с помощью диалога, открывающегося по управляющей кнопке-бургеру слева.
robot_properties_dialog.php
Подобно properties_dialog.php, файл robot_properties_dialog.php отвечает за отображение настроек робота.
Пример:
<?php
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();
/** @var \Bitrix\Bizproc\Activity\PropertiesDialog $dialog */
$elementId = $dialog->getMap()['ElementId'];
?>
<div class="bizproc-automation-popup-settings">
<span class="bizproc-automation-popup-settings-title"><?=htmlspecialcharsbx($elementId['Name'])?>: </span>
<?=$dialog->renderFieldControl($elementId)?>
</div>