← Назад к справке

CRM

Смарт-процессы

Работа со смарт-процессами CRM через PHP и фабрики: entityTypeId, получение, добавление, изменение, удаление, поля, стадии, связи и действия операций.

Смарт-процесс — это пользовательский тип CRM. Для PHP-кода главный идентификатор — entityTypeId. Именно его передают в getFactory().

Общее понимание

Со смарт-процессами удобнее всего работать через универсальное CRM API: контейнер, фабрику, элемент и операции.

Что такое entityTypeId

Для стандартных сущностей есть константы: CCrmOwnerType::Deal, CCrmOwnerType::Company, CCrmOwnerType::Contact. Для смарт-процесса используется его числовой entityTypeId.

Сущность Тип
Лид CCrmOwnerType::Lead
Сделка CCrmOwnerType::Deal
Контакт CCrmOwnerType::Contact
Компания CCrmOwnerType::Company
Смарт-процесс Числовой entityTypeId, например 1044.

Получить фабрику

Если фабрика вернула null, значит тип не найден или для него нельзя получить фабрику.

<?php

use Bitrix\Main\Loader;
use Bitrix\Crm\Service;

const SMART_PROCESS_ENTITY_TYPE_ID = 1044;

Loader::includeModule('crm');

/**
 * Получает фабрику смарт-процесса.
 */
function fetchSmartProcessFactory(int $entity_type_id)
{
    $container = Service\Container::getInstance();

    return $container->getFactory($entity_type_id);
}

$factory = fetchSmartProcessFactory(SMART_PROCESS_ENTITY_TYPE_ID);

if ($factory === null) {
    echo 'Фабрика смарт-процесса не найдена';
}

Основные операции

Получение, добавление, изменение и удаление элементов смарт-процесса.

Получить элемент

<?php

use Bitrix\Main\Loader;
use Bitrix\Crm\Service;

const SMART_PROCESS_ENTITY_TYPE_ID = 1044;

Loader::includeModule('crm');

/**
 * Получает элемент смарт-процесса.
 */
function fetchSmartProcessItem(int $entity_type_id, int $item_id)
{
    $factory = Service\Container::getInstance()->getFactory($entity_type_id);

    if ($factory === null) {
        return null;
    }

    return $factory->getItem($item_id);
}

$item = fetchSmartProcessItem(SMART_PROCESS_ENTITY_TYPE_ID, $item_id);

if ($item !== null) {
    $item_data = $item->toArray();
    $title = $item->getTitle();

    print_r($item_data);
}

Получить список элементов

<?php

use Bitrix\Main\Loader;
use Bitrix\Crm\Service;

const SMART_PROCESS_ENTITY_TYPE_ID = 1044;
const SMART_PROCESS_LIMIT = 100;

Loader::includeModule('crm');

/**
 * Получает элементы смарт-процесса по фильтру.
 */
function fetchSmartProcessItems(int $entity_type_id, array $filter): array
{
    $factory = Service\Container::getInstance()->getFactory($entity_type_id);

    if ($factory === null) {
        return [];
    }

    return $factory->getItems([
        'select' => [
            'ID',
            'TITLE',
            'STAGE_ID',
            'ASSIGNED_BY_ID',
            'CREATED_TIME',
        ],
        'filter' => $filter,
        'order' => [
            'ID' => 'ASC',
        ],
        'limit' => SMART_PROCESS_LIMIT,
    ]);
}

$items = fetchSmartProcessItems(
    SMART_PROCESS_ENTITY_TYPE_ID,
    [
        '>ID' => 0,
    ]
);

foreach ($items as $item) {
    print_r($item->toArray());
}

Добавить элемент

<?php

use Bitrix\Main\Loader;
use Bitrix\Crm\Service;

const SMART_PROCESS_ENTITY_TYPE_ID = 1044;

Loader::includeModule('crm');

/**
 * Добавляет элемент смарт-процесса.
 */
function addSmartProcessItem(int $entity_type_id, array $item_fields): array
{
    $factory = Service\Container::getInstance()->getFactory($entity_type_id);

    if ($factory === null) {
        return [
            'is_success' => false,
            'item_id' => 0,
            'error_messages' => ['Фабрика смарт-процесса не найдена'],
        ];
    }

    $item = $factory->createItem($item_fields);

    $operation = $factory->getAddOperation($item);
    $operation_result = $operation->launch();

    return [
        'is_success' => $operation_result->isSuccess(),
        'item_id' => (int) $item->getId(),
        'error_messages' => $operation_result->getErrorMessages(),
    ];
}

$item_fields = [
    'TITLE' => $title,
    'ASSIGNED_BY_ID' => $assigned_by_id,
    'STAGE_ID' => $stage_id,
    'UF_CRM_8_1234567890' => $custom_value,
];

$add_result = addSmartProcessItem(SMART_PROCESS_ENTITY_TYPE_ID, $item_fields);

print_r($add_result);

Изменить элемент

<?php

use Bitrix\Main\Loader;
use Bitrix\Crm\Service;

const SMART_PROCESS_ENTITY_TYPE_ID = 1044;

Loader::includeModule('crm');

/**
 * Изменяет поля элемента смарт-процесса.
 */
function updateSmartProcessItem(int $entity_type_id, int $item_id, array $item_fields): array
{
    $factory = Service\Container::getInstance()->getFactory($entity_type_id);

    if ($factory === null) {
        return [
            'is_success' => false,
            'error_messages' => ['Фабрика смарт-процесса не найдена'],
        ];
    }

    $item = $factory->getItem($item_id);

    if ($item === null) {
        return [
            'is_success' => false,
            'error_messages' => ['Элемент смарт-процесса не найден'],
        ];
    }

    foreach ($item_fields as $field_id => $field_value) {
        $item->set($field_id, $field_value);
    }

    $operation = $factory->getUpdateOperation($item);
    $operation_result = $operation->launch();

    return [
        'is_success' => $operation_result->isSuccess(),
        'error_messages' => $operation_result->getErrorMessages(),
    ];
}

$update_result = updateSmartProcessItem(
    SMART_PROCESS_ENTITY_TYPE_ID,
    $item_id,
    [
        'TITLE' => $title,
        'STAGE_ID' => $stage_id,
        'UF_CRM_8_1234567890' => $custom_value,
    ]
);

print_r($update_result);

Удалить элемент

<?php

use Bitrix\Main\Loader;
use Bitrix\Crm\Service;

const SMART_PROCESS_ENTITY_TYPE_ID = 1044;

Loader::includeModule('crm');

/**
 * Удаляет элемент смарт-процесса.
 */
function deleteSmartProcessItem(int $entity_type_id, int $item_id): array
{
    $factory = Service\Container::getInstance()->getFactory($entity_type_id);

    if ($factory === null) {
        return [
            'is_success' => false,
            'error_messages' => ['Фабрика смарт-процесса не найдена'],
        ];
    }

    $item = $factory->getItem($item_id);

    if ($item === null) {
        return [
            'is_success' => false,
            'error_messages' => ['Элемент смарт-процесса не найден'],
        ];
    }

    $operation = $factory->getDeleteOperation($item);
    $operation_result = $operation->launch();

    return [
        'is_success' => $operation_result->isSuccess(),
        'error_messages' => $operation_result->getErrorMessages(),
    ];
}

$delete_result = deleteSmartProcessItem(SMART_PROCESS_ENTITY_TYPE_ID, $item_id);

print_r($delete_result);

Поля и настройки

У каждого смарт-процесса свой набор полей, стадий, направлений и настроек.

Получить поля

Перед работой с незнакомым смарт-процессом полезно вывести описание обычных и пользовательских полей.

<?php

use Bitrix\Main\Loader;
use Bitrix\Crm\Service;

const SMART_PROCESS_ENTITY_TYPE_ID = 1044;

Loader::includeModule('crm');

/**
 * Получает описание полей смарт-процесса.
 */
function fetchSmartProcessFieldsInfo(int $entity_type_id): array
{
    $factory = Service\Container::getInstance()->getFactory($entity_type_id);

    if ($factory === null) {
        return [];
    }

    return [
        'fields' => $factory->getFieldsInfo(),
        'user_fields' => $factory->getUserFieldsInfo(),
    ];
}

$fields_info = fetchSmartProcessFieldsInfo(SMART_PROCESS_ENTITY_TYPE_ID);

print_r($fields_info);

Стадии и воронки

Если у смарт-процесса включены направления и стадии, их можно получить через фабрику.

<?php

use Bitrix\Main\Loader;
use Bitrix\Crm\Service;

const SMART_PROCESS_ENTITY_TYPE_ID = 1044;

Loader::includeModule('crm');

/**
 * Получает стадии смарт-процесса.
 */
function fetchSmartProcessStages(int $entity_type_id, ?int $category_id = null): array
{
    $factory = Service\Container::getInstance()->getFactory($entity_type_id);

    if ($factory === null) {
        return [];
    }

    if (!$factory->isStagesEnabled()) {
        return [];
    }

    $stages = [];
    $stage_collection = $factory->getStages($category_id);

    foreach ($stage_collection as $stage) {
        $stages[] = $stage->collectValues();
    }

    return $stages;
}

$stages = fetchSmartProcessStages(SMART_PROCESS_ENTITY_TYPE_ID, $category_id);

print_r($stages);

Родительские связи

Смарт-процесс может быть связан со сделкой, контактом, компанией или другим элементом CRM. Коды полей связи зависят от настроек типа. Перед использованием лучше вывести toArray() конкретного элемента и описание полей через getFieldsInfo().

На практике часто встречаются поля вида PARENT_ID_2 для связи со сделкой или похожие системные поля. Точный код нужно проверять на своём портале.

События и действия

У смарт-процессов есть REST-события для приложений. В коробочном PHP для изменения логики чаще используют действия операций: до сохранения, после сохранения, после удаления и так далее.

REST-события элементов

REST-события смарт-процессов срабатывают для элементов любых смарт-процессов. Поэтому в обработчике нужно проверять ENTITY_TYPE_ID и отбрасывать чужие типы.

Событие Когда вызывается Что приходит
onCrmDynamicItemAdd После добавления элемента смарт-процесса. ID и ENTITY_TYPE_ID.
onCrmDynamicItemUpdate После изменения элемента смарт-процесса. ID и ENTITY_TYPE_ID.
onCrmDynamicItemDelete После удаления элемента смарт-процесса. ID и ENTITY_TYPE_ID.
onCrmDynamicItemAdd_1044 Вариант подписки на конкретный тип. Может работать при подписке через приложение, но в интерфейсе вебхуков обычно не отображается.

Для коробочной PHP-логики эти события не самый удобный вариант. Они больше подходят для REST-приложений и внешних интеграций.

Действия операций в PHP

В новом CRM API логику смарт-процесса обычно добавляют не через старые события вида OnBeforeCrmDealUpdate, а через операции и действия. Действие можно подключить к операции добавления, изменения или удаления.

Точка Когда выполняется Для чего подходит
Operation::ACTION_BEFORE_SAVE Перед сохранением элемента. Проверка полей, запрет изменения, служебная валидация.
Operation::ACTION_AFTER_SAVE После сохранения элемента. Логирование, синхронизация, вспомогательные действия.

Такой подход обычно требует своего класса фабрики и подмены фабрики для конкретного entityTypeId. Это уже не разовый PHP-скрипт, а код для /local или локального модуля.

Действие перед сохранением

Пример действия, которое запрещает перевод элемента на определённую стадию для конкретного пользователя. Такой класс можно положить, например, в свой модуль или в /local/php_interface/classes.

<?php

namespace Local\Crm\SmartProcess\Operation\Action;

use Bitrix\Crm\Item;
use Bitrix\Crm\Service\Container;
use Bitrix\Crm\Service\Operation\Action;
use Bitrix\Main\Error;
use Bitrix\Main\Result;

class RestrictStageChangeAction extends Action
{
    private const RESTRICTED_USER_ID = 222;
    private const RESTRICTED_STAGE_ID = 'D1044_1:CLIENT';

    /**
     * Проверяет изменение стадии перед сохранением.
     */
    public function process(Item $item): Result
    {
        $result = new Result();
        $user_id = Container::getInstance()->getContext()->getUserId();

        if (
            $user_id === self::RESTRICTED_USER_ID
            && $item->isChangedStageId()
            && $item->getStageId() === self::RESTRICTED_STAGE_ID
        ) {
            $result->addError(
                new Error('Пользователю запрещено переводить элемент на эту стадию')
            );
        }

        return $result;
    }
}

Чтобы действие выполнилось, его нужно добавить к операции обновления в своей фабрике.

<?php

namespace Local\Crm\SmartProcess;

use Bitrix\Crm;
use Bitrix\Crm\Service\Factory\Dynamic;
use Bitrix\Crm\Service\Operation;
use Local\Crm\SmartProcess\Operation\Action\RestrictStageChangeAction;

class Factory extends Dynamic
{
    /**
     * Добавляет проверку перед сохранением элемента.
     */
    public function getUpdateOperation(
        Crm\Item $item,
        Crm\Service\Context $context = null
    ): Crm\Service\Operation\Update {
        $operation = parent::getUpdateOperation($item, $context);

        $operation->addAction(
            Operation::ACTION_BEFORE_SAVE,
            new RestrictStageChangeAction()
        );

        return $operation;
    }
}

Действие после удаления

Пример действия, которое пишет в лог ID удалённого элемента смарт-процесса. Подходит для служебного аудита или синхронизации со своей таблицей.

<?php

namespace Local\Crm\SmartProcess\Operation\Action;

use Bitrix\Crm\Item;
use Bitrix\Crm\Service\Operation\Action;
use Bitrix\Main\Result;

class LogDeleteAction extends Action
{
    /**
     * Записывает удаление элемента в лог.
     */
    public function process(Item $item): Result
    {
        AddMessage2Log(
            [
                'message' => 'Элемент смарт-процесса удалён',
                'item_id' => $item->getId(),
                'entity_type_id' => $item->getEntityTypeId(),
            ],
            'smart_process_delete'
        );

        return new Result();
    }
}

Действие добавляется к операции удаления.

<?php

namespace Local\Crm\SmartProcess;

use Bitrix\Crm;
use Bitrix\Crm\Service\Factory\Dynamic;
use Bitrix\Crm\Service\Operation;
use Local\Crm\SmartProcess\Operation\Action\LogDeleteAction;

class Factory extends Dynamic
{
    /**
     * Добавляет логирование после удаления элемента.
     */
    public function getDeleteOperation(
        Crm\Item $item,
        Crm\Service\Context $context = null
    ): Crm\Service\Operation\Delete {
        $operation = parent::getDeleteOperation($item, $context);

        $operation->addAction(
            Operation::ACTION_AFTER_SAVE,
            new LogDeleteAction()
        );

        return $operation;
    }
}

Если нужна полная подмена фабрики для конкретного смарт-процесса, это лучше вынести в отдельную статью про кастомизацию фабрик и контейнера CRM.

Источники