Действие (activity) в модуле Бизнес Процессы
Готовый код можно скачать в моем репозитории на GitFlic.
Все что исполняется в Бизнес Процесс, является действием или по другому activity, сам бизнес-процесс представляет собой набор вложенных действий. Каждое такое действие в рамках шаблона бизнес-процесса имеет уникальное имя.
С точки зрения кода действие — это класс, который наследуется от абстрактного класса CBPActivity или его потомков. Название класса должно начинаться с подстроки CBP и может состоять из латинских букв и цифр.
Расположение
Порядок поиска является приоритетным, директории будут перебираться последовательно пока не будет найдена директория с действием. Activity, могут располагаться в следующих местах:
/local/activities/local/activities/custom/bitrix/activities/custom/bitrix/activities/bitrix/bitrix/modules/bizproc/activities
В дистрибутиве по умолчанию есть несколько десятков различных действий Activity для использования их в шаблонах бизнес-процессов. Рекомендуем изучить работу некоторых базовых действий для составления полной картины, логику можно изменнить и подогнать под свои нужды:
CBPCompositeActivityабстрактный базовый класс составных действий, т.е. действий, которые могут содержать в себе дочерние действияCBPSequenceActivityпоследовательно запускает набор дочерних действийCBPCodeActivityсамое простое действие — запускает на выполнение произвольный PHP-кодCBPSetVariableActivityустанавливает значения переменных Бизнес ПроцессаCBPDelayActivityреализует ожидание, откладывая выполнение на определенный срокCBPHandleExternalEventActivityреализует действие, которое ожидает внешнее событие. Бизнес-процесс останавливается до получения данного внешнего событияCBPIfElseActivityиCBPIfElseBranchActivityреализуют функционал условия и ветки условияCBPWhileActivityреализует функционал циклаCBPListenActivityреализует ожидание одного из нескольких возможных событий. Когда одно из событий происходит, остальные перестают ожидать событий и отменяютсяCBPParallelActivityпараллельно запускает набор дочерних действий.
Классификация действий
С точки зрения выполнения, Бизнес Процессы можно классифицировать на простые и комплексные. Комплексные могут состоять из нескольких простых действий. Дополнительно действия можно разделить по характеру выполнения на немедленные, задания, событийные:
Немедленныевыполняются в рамках бизнес-процесса, не блокируют процесс выполнения в нормальном режиме работыСобытийныевыполняются, проверяют условие (необходимость) ожидания события, ставят процесс на паузу до наступления события или даты окончания ожиданияЗаданиявыполняются, останавливают свое выполнение до выполнения задания сотрудников или даты окончания ожидания
По-умолчанию любое действие является немедленным. Для того чтобы сделать действие событийным, оно должно реализовывать интерфейсы IBPEventActivity и IBPActivityExternalEventListener, задания в свою очередь можно рассматривать как событийное действие с дополнительными возможностями, нужно наследоваться от CBPCompositeActivity.
Файловая структура
Для нашего случая будет использоваться название активити phpcodeactivity, по сути я скопировал штатный activity который выполняит PHP код.
-
-
-
-
.description.phpproperties_dialog.phptestcodeactivity.php
.description.phpсодержит мета-информацию описывающую наше действие, аналог.description.phpв компонентахicon.gifаватаркаphpcodeactivity.phpсодержит основную логику нашего активити, аналогclass.phpв компонентахproperties_dialog.phpсодержит код для визуального отображения, аналогtemplate.phpв компонентах
-
-
-
Файл .description.php
Основная задача файла, установить переменную $arActivityDescription как массив описывающий действие:
/local/activities/phpcodeactivity/.description.php<?php
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true)
{
die();
}
use Bitrix\Main\Localization\Loc;
$arActivityDescription = [
'NAME' => Loc::getMessage('BPCA_DESCR_NAME'),
'DESCRIPTION' => Loc::getMessage('BPCA_DESCR_DESCR'),
'TYPE' => 'activity',
// ОБЯЗАТЕЛЬНО по названию класса, иначе работать не будет
'CLASS' => 'PhpCodeActivity',
'JSCLASS' => 'BizProcActivity',
'CATEGORY' => [
'ID' => 'other',
],
'FILTER' => [
'EXCLUDE' => CBPHelper::DISTR_B24,
],
];
Рассмотрим все возможные ключи массива $arActivityDescription:
NAME(string) локализованное название действия, отображается в списке действий, а так же в заголовке всплывающего окна настроекDESCRIPTION(string) локализованное описание действия, отображается во всплывающем окне настроекTYPE(string or array) тип действия, в случае условия может принимать толькоcondition, в случае действия может быть либоactiviy, либоrobot_activityлибо массивом из этих же элементовCLASS(string) название php-класса обработчика действия, должен совпадать с названием директорииJSCLASS(string) название js-класса обработчика действия, по умолчаниюBizProcActivityCATEGORY(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;
}
// ...
}
Файл phpcodeactivity.php
Мы создаем простое действие, нам не нужно ждать наступления какого-либо события, родительским классом в данном случае будет являться Activity, а наш класс должен иметь префикс CBP.
В классе нашего активити нам необходимо:
__constructописать конструктор, задав значения по-умолчанию для наших свойствExecuteметод который будет выполняться в запущенном бизнес-процессеGetPropertiesDialogотображение диалога настроекGetPropertiesDialogValuesсохранение введенных значений
/local/activities/phpcodeactivity/phpcodeactivity.php<?php
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true)
{
die();
}
use Bitrix\Main\Localization\Loc;
class CBPPhpCodeActivity extends CBPActivity
{
public function __construct($name)
{
// запускаем конструктор родителя
parent::__construct($name);
// значения по-умолчанию для наших свойств
$this->arProperties = [
'Title' => '',
'ExecuteCode' => '',
];
}
// метод который будет выполняться в запущенном бизнес-процессе, доступны свойства обьявленные в конструкторе
public function execute()
{
if ($this->ExecuteCode <> '')
{
// выполним php код
@eval($this->ExecuteCode);
// добавляем запись в журнал бизнес-процесса
parent::writeToTrackingService($message = "Php код выполнил", $modifiedBy = 0, $trackingType = -1);
}
// вернем результат выполнения кода
return CBPActivityExecutionStatus::Closed;
// возможные варианты ответа
// CBPActivityExecutionStatus::Executing; действие еще не завершило свою работу
// CBPActivityExecutionStatus::Closed; действие завершило свою работу
// CBPActivityExecutionStatus::Faulting; ошибка при выполнении действия, прекратить выполнение бизнес-процесса
}
// валидация
public static function validateProperties($arTestProperties = [], CBPWorkflowTemplateUser $user = null)
{
$arErrors = [];
if ($user == null || !$user->isAdmin())
{
$arErrors[] = [
'code' => 'perm',
'message' => Loc::getMessage('BPCA_NO_PERMS'),
];
}
if (empty($arTestProperties['ExecuteCode']))
{
$arErrors[] = [
'code' => 'emptyCode',
'message' => Loc::getMessage('BPCA_EMPTY_CODE'),
];
}
return array_merge($arErrors, parent::validateProperties($arTestProperties, $user));
}
// диалог настроек, показываем поля формы для заполнения
public static function GetPropertiesDialog(
$documentType,
$activityName,
$arWorkflowTemplate,
$arWorkflowParameters,
$arWorkflowVariables,
$arCurrentValues = null,
$formName = ''
)
{
$runtime = CBPRuntime::getRuntime();
if (!is_array($arWorkflowParameters))
{
$arWorkflowParameters = [];
}
if (!is_array($arWorkflowVariables))
{
$arWorkflowVariables = [];
}
if (!is_array($arCurrentValues))
{
$arCurrentValues = ['activity_php_code' => ''];
$arCurrentActivity = &CBPWorkflowTemplateLoader::FindActivityByName($arWorkflowTemplate, $activityName);
if (is_array($arCurrentActivity['Properties']))
{
$arCurrentValues['activity_php_code'] = $arCurrentActivity['Properties']['ExecuteCode'] ?? '';
}
}
// инклудим файл который выведет html-верстку, штатно это делается через менеджер текущего выполнения
return $runtime->executeResourceFile(
__FILE__,
'properties_dialog.php',
[
'arCurrentValues' => $arCurrentValues,
'formName' => $formName,
]
);
}
// сохранение введенных значений в поля формы
public static function GetPropertiesDialogValues(
$documentType,
$activityName,
&$arWorkflowTemplate,
&$arWorkflowParameters,
&$arWorkflowVariables,
$arCurrentValues,
&$arErrors
)
{
$arErrors = [];
$runtime = CBPRuntime::getRuntime();
$arProperties = ['ExecuteCode' => $arCurrentValues['activity_php_code']];
$arErrors = self::validateProperties(
$arProperties,
new CBPWorkflowTemplateUser(CBPWorkflowTemplateUser::CurrentUser)
);
if (count($arErrors) > 0)
{
return false;
}
$arCurrentActivity = &CBPWorkflowTemplateLoader::FindActivityByName($arWorkflowTemplate, $activityName);
$arCurrentActivity['Properties'] = $arProperties;
return true;
}
}
Файл properties_dialog.php
Код в файле properties_dialog.php отвечает за отображение настроек активити в дизайнере бизнес-процессов. В рамках диалога можно использовать любой набор переменных определенный и переданный в него через метод GetPropertiesDialog:
/local/activities/phpcodeactivity/properties_dialog.php<?php
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();
?>
<tr>
<td align="right" width="40%" valign="top"><span class="adm-required-field"><?= GetMessage("BPCA_PD_PHP") ?>:</span>
</td>
<td width="60%">
<?= CBPDocument::ShowParameterField('text', 'activity_php_code', $arCurrentValues['activity_php_code'], array('rows' => 20, 'cols' => 70)) ?>
</td>
</tr>