Конвертация - это превращение одного элемента CRM в другие элементы CRM.

Элемент на котором выполняется конвертация мы будем называть конвертируемым элементом, а элементы получившиеся в результате конвертации - результат конвертации. Существует альтернативное название для конвертации - “создание на основании”, в рамках статьи мы будем предполагать что это синонимы.

Конвертируемые элементы

На момент написания статьи конвертируемыми элементами может выступать только Лид, Сделка и Предложение, причем существуют различные модификаторы конвертации которые влияют на доступный набор результатов.

В системе зарегистрировано 3 конвертационных модификатора:

  • Ограничение по сущностям
  • Только для лида: специализированный модификатор на основании типа (обычный и повторный).
  • Генерируемый на основании ролевой карты

Модификатор ограничения по сущностям является общим модификатором который указывает на основании конвертируемого элемента какие результаты конвертации могут быть получены.

  • На основании лида можно создать: Контакт, Компанию, Сделку.
  • На основании Сделки: Счет и Предложение.
  • На основании Предложения: Счет и Сделку.

Специализированный модификатор проверяет тип лида и в случае если конвертируемый лид является повторным то на его основании можно создать только сделку.

Модификатор генерируемый на основании ролевой карты собирает максимальные права данного пользователя на создание сущностей и разрешает конвертацию только в те сущности на которые у текущего пользователя есть права добавления. Так, например, если у пользователя нет прав на создания сделки, то Лид нельзя будет конвертировать в Сделку, но будет возможность конвертировать в Контакт / Компанию и их вариации.

Если смотреть в суть вещей, то и процесс создания одного элемена на основании другого и процесс создания подобных связей без непосредственного создания новых сущностей является конвертацией, однако чтобы различать их рамках статьи мы будем рассматривать их как 2 независимых процесса:

  • Конвертация - как создание результатов на основании конвертируемого элемента
  • Псевдоконвертация - как создание связей для уже существующих элементов.

Непосредственные классы отвечающие за работу конвертации располагаются в пространстве имен \Bitrix\Crm\Conversion. В дальнейшем мы будем предполагать что это пространство имен и классы из него подключены в use-разделе.

Конвертационные схемы

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

Конвертационные схемы хранятся в таблице b_crm_conv_map (\Bitrix\Crm\Conversion\Entity\EntityConversionMapTable), однако работать напрямую с таблицей не рекомендуется.

Конвертационная схема состоит из следующих полей (описание таблицы):

Поле Тип Описание Примечание
SRC_TYPE_ID int(1) / tinyint(1) unsigned Идентификатор типа конвертируемого элемента см. \CCrmOwnerType
DST_TYPE_ID int(1) / tinyint(1) unsigned Идентификатор типа результата конвертации см. \CCrmOwnerType
LAST_UPDATED datetime Дата последнего обновления конвертационной схемы
DATA longtext Сериализованная конвертационная карта
RELATION_TYPE varchar(20) Тип конвертации По-умолчанию CONVERSION
IS_CHILDREN_LIST_ENABLED char(1) Доступность в списке связей по-умолчанию Y

Отдельно стоит остановиться на нововведениях: механизм Смарт-процессов позволяет привязывать созданный Смарт-процесс к элементам. Для реализации этой возможности были добавлены столбцы RELATION_TYPE и IS_CHILDREN_LIST_ENABLED. RELATION_TYPE - тип связи конвертационной схемы. В настоящий момент допустимы только 2 значения: \Bitrix\Crm\Relation\RelationType::CONVERSION (конвертация) и \Bitrix\Crm\Relation\RelationType::BINDING (связь). IS_CHILDREN_LIST_ENABLED - флаг обозначающий необходимость добавить список со связанными элементами в карточку конвертируемой сущности. Отдельно стоит отметить, что при типе конвертационной схеме связь, заполнение сериализованной конвертационной карты не требуется.

Сама сериализованная конвертационная карта состоит из полей:

  • srcEntityTypeID - индентификатор типа конвертируемого элемента (\CCrmOwnerType)
  • dstEntityTypeID - идентификатор типа результата конвертации
  • time - дата генерации конвертационной схемы
  • items - набор инструкций по конвертации поля конвертируемого элемента

Не трудно заметить что поля srcEntityTypeID, dstEntityTypeID и time копирую поведение полей базы данных SRC_TYPE_ID, DST_TYPE_ID и LAST_UPDATED соответственно. Это не ошибка, а необходимость для быстрого извлечения и удобства при разработке в публичной части.

Каждая инструкция по конвертации поля состоит из следующего набора полей:

  • srcField - код поля в конвертируемом элементе
  • dstField - код поля в элементе результата конвертации
  • altSrcFields - набор кодов полей, откуда будет произведена попытка получения значений, в случае если в srcField не будет значения.
  • isLocked - флаг указывающий на то, что изменение элемента в публичной части при вызове формы невозможен (поле будет заблокировано)
  • isRequired - флаг указывающий на то, что заполнение этого поля обязательно при конвертации.

Не стоит обольщаться наличием флагов isLocked и isRequired их значения в конверсионной карте никак не обрабатываются, поэтому не имеет значения установите вы параметры в true или false, так как результат будет одинаковый.

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

  1. Строго определенной стандартной частью
  2. Работой с пользовательскими полями

Таким образом для стандартных полей конвертируемой сущности у битрикса есть строго определенная часть которая указывает в какие именно поля результата необходимо переносить значения.

Для работы с пользовательскими полями битрикс использует механизм синхронизации полей, когда имеющиеся пользовательские поля в лиде будут созданы во всех результатах конвертации. Например, если работе с CRM в Лиде вы создали дополнительное поле “Тема обращения”, то данное поле при конвертации в Сделку+Контакт будет создано И в сделке И в контакте, хотя семантически оно может быть там совершенно не нужно. Перенос полей происходит по принципу 1к1, т.е. если поле с таким кодом существует и в лиде и в сделке, то в сделке это поле не будет создано, а будет проставлена связь между полями.

До версии битрикса 20 включительно подобной схеме были подвержены все пользовательские поля, однако после обрабатываются только поля у которых в коде присутствует префикс UF_CRM_.

Простой способ конвертации

Наиболее простым способом конвертации является использование средств автоматизации предоставляемых в пространстве имен \Bitrix\Crm\Automation\Converter.

Описанный класс настолько прост, что не имеет смысла описывать его возможности технически. Рассмотрим пример кода который должен на основании лида (ID:1) от лица пользователя (ID:1) конвертировать в сделку определенного направления (ID:1) и контакт.


use \Bitrix\Crm\Automation;

/**
 * Income lead id
 * @var integer
 */
$leadId = 1;

/**
 * User id who start convertation
 * @var integer
 */
$convertUserId = 1;

/**
 * Deal category id
 * @var integer
 */
$dealCategoryId = 1;

/**
 * True if need to complete all activities after convertation process
 * @var boolean
 */
$completeActivityAfterConveration = true;

try
{
    $converter = Automation\Converter\Factory::create(
        \CCrmOwnerType::Lead,
        $leadId
    );

    $converter->enableActivityCompletion($completeActivityAfterConveration);

    $converter->setTargetItem(
        \CCrmOwnerType::Deal,
        ['categoryId' => (int)$dealCategoryId]
    );

    $converter->setTargetItem(
        \CCrmOwnerType::Contact,
        []
    );

    $conversionResult = $converter->execute([
        'USER_ID' => $convertUserId
    ]);

    /**
     * Register convertation relations
     */
    Automation\Factory::registerConversionResult(
        \CCrmOwnerType::Lead,
        $leadId,
        $conversionResult
    );

    if(!$conversionResult->isSuccess())
    {
        throw new \Exception(
            "Conversion error: "
            .implode(', ', $conversionResult->getErrorMessages())
        );
    }
}
catch (\Throwable $e)
{
    var_dump($e);
}

Конвертационный процесс

Технически конвертация сводится к последовательности действий выполняемых для определенной сущности:

  • Создание конфигурации (Наследник EntityConversionConfig)
  • Создание мастера (Наследник EntityConversionWizard)
  • Установка необходимых параметров конвертации
  • Запуск мастера конвертации

Конвертация лида

Выполним конвертацию ранее описанного примера явно, не используя конструкции предоставляемые автоматизацией crm.

use \Bitrix\Crm\Conversion,
    \Bitrix\Crm\Synchronization
    ;

/**
 * Converted lead id
 * @var integer
 */
$leadId = 1;

/**
 * User id who start convertation
 * @var integer
 */
$convertUserId = 1;

/**
 * Deal category id
 * @var integer
 */
$dealCategoryId = 1;

/**
 * Execution context
 * @var array
 */
$contextData = [
    'USER_ID' => $convertUserId
];

$conversionConfig = new Conversion\LeadConversionConfig();

/**
 * Disable user field check to prevent create exceptions
 */
$conversionConfig->enablePermissionCheck(false);

// For contact
$item = $conversionConfig->getItem( \CCrmOwnerType::Contact );
if ($item)
{
    // Activate convertation to contact
    $item->setActive(true);

    // Enable syncronization fields
    $item->enableSynchronization(true);

    // Setup initial data. Accepted only `defaultName`
    $item->setInitData([
        'defaultName' => 'Default contact name',
    ]);
}
unset($item);

// For deal in category
$item = $conversionConfig->getItem( \CCrmOwnerType::Deal );
if ($item)
{
    // Activate convertation to contact
    $item->setActive(true);

    // Enable syncronization fields
    $item->enableSynchronization(true);

    // Setup initial data. Accepted only `categoryId`
    $item->setInitData([
        'categoryId' => (int) $dealCategoryId,
    ]);
}
unset($item);

$conversionWizard = new Conversion\LeadConversionWizard(
    $leadId,
    $conversionConfig
);
 
/**
 * Disable user field synchronization.
 */
$conversionWizard->enableUserFieldCheck(false);

/**
 * Disable workflow new entity check. 
 * If exist workflow with autostart option and parameters
 * wizard throws an error
 */
$conversionWizard->enableBizProcCheck(false);

/**
 * If crm lead entity exist bizproc with option autostart when update
 * need start this workflows?
 */
$conversionWizard->setSkipBizProcAutoStart(true);

/**
 * Need to complete all activities after convertation process
 */
$conversionWizard->enableActivityCompletion(true);

/**
 * Process user field synchronization to prevent the fields from diverging
 */
$srcEntityTypeId = \CCrmOwnerType::Lead;
foreach ($conversionConfig->getItems() as $item)
{
    $dstEntityTypeId = (int)$item->getEntityTypeID();
    if( !Synchronization\UserFieldSynchronizer::needForSynchronization(
        $srcEntityTypeId,
        $dstEntityTypeId
    ))
    {
        continue;
    }

    if ($item->isSynchronizationEnabled())
    {
        Synchronization\UserFieldSynchronizer::synchronize(
            $srcEntityTypeId,
            $dstEntityTypeId
        );
    }
    else
    {
        Synchronization\UserFieldSynchronizer::markAsSynchronized(
            $srcEntityTypeId,
            $dstEntityTypeId
        );
    }
}

if ( !$conversionWizard->execute($contextData) )
{
    /**
     * Caught some error. You can see error text (string)
     * with `$conversionWizard->getErrorText()`
     */
}
else
{
    /**
     * Ok. You can get result data from 
     * $conversionWizard->getResultData()
     */
}

Конвертационные связи

Помимо выполнения конвертационного процесса существует и псевдоконцвертация, т.е. создание связей без создания элементов. В разных сущностях этот процесс характеризуется своими особенностями.

Поле LEAD_ID является универсальным для Сделок, Контактов и Компаний, однако это соверешенно не значит что механизм его обработки одинаковый. Не пытайтесь заполнить этот поле в СУБД через SQL запрос! Используйте старое api для его заполнения.

Если получилась такая ситуация, что поле LEAD_ID в какой-либо сущности было заполнено прямым SQL-запросом, то для данного лида необходимо пересчитать статистические данные методом:

use \Bitrix\Crm\Statistics;

Statistics\LeadConversionStatisticsEntry::processBindingsChange($leadID);

Таким образом для того чтобы проставить связи между существующими Контактом, Компанией или Сделкой с лидом достаточно просто обновить данный соответствующей сущности добавив в поле LEAD_ID идентификатор лида.