Полный цикл в digital

Действие (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.php
          • properties_dialog.php
          • testcodeactivity.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-класса обработчика действия, по умолчанию BizProcActivity
  • CATEGORY (array) опциональное описание раздела для отображения в дизайнере БП в случае TYPE=activity
  • ROBOT_SETTINGS (array) опциональное описание раздела для отображения в роботах в случае TYPE=robot_activity
  • FILTER (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.

В классе нашего активити нам необходимо:

  1. __construct описать конструктор, задав значения по-умолчанию для наших свойств
  2. Execute метод который будет выполняться в запущенном бизнес-процессе
  3. GetPropertiesDialog отображение диалога настроек
  4. 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>

Заполните форму уже сегодня!
Для начала сотрудничества необходимо заполнить заявку или заказать обратный звонок. В ответ получите коммерческое предложение, которое будет содержать индивидуальную стратегию с учетом требований и поставленных задач
Работаем по будням с 9:00 до 18:00. Заявки, отправленные в выходные, обрабатываем в первый рабочий день до 12:00.
Спасибо, ваш запрос принят и будет обработан!