CRM
Сделки
Работа со сделками CRM через PHP и фабрики: получение, добавление, изменение, удаление, стадии, воронки, контакты и события.
Сделка — основная рабочая сущность CRM. При изменении стадии или важных полей лучше использовать операции фабрики, чтобы сработали проверки, история, таймлайн, роботы и бизнес-процессы.
Основные операции
Базовая работа со сделками через фабрику CRM.
Получить сделку
<?php
use Bitrix\Main\Loader;
use Bitrix\Crm\Service;
const CRM_DEAL_TYPE_ID = \CCrmOwnerType::Deal;
Loader::includeModule('crm');
/**
* Получает фабрику сделок.
*/
function fetchDealFactory()
{
return Service\Container::getInstance()->getFactory(CRM_DEAL_TYPE_ID);
}
/**
* Получает сделку по ID.
*/
function fetchDealItem(int $deal_id)
{
$factory = fetchDealFactory();
if ($factory === null) {
return null;
}
return $factory->getItem($deal_id);
}
$deal_item = fetchDealItem($deal_id);
if ($deal_item !== null) {
$deal_data = $deal_item->toArray();
$title = $deal_item->getTitle();
$stage_id = $deal_item->getStageId();
print_r($deal_data);
} Получить список сделок
<?php
use Bitrix\Main\Loader;
use Bitrix\Crm\Service;
const CRM_DEAL_TYPE_ID = \CCrmOwnerType::Deal;
const DEAL_LIMIT = 100;
Loader::includeModule('crm');
/**
* Получает фабрику сделок.
*/
function fetchDealFactory()
{
return Service\Container::getInstance()->getFactory(CRM_DEAL_TYPE_ID);
}
/**
* Получает список сделок по фильтру.
*/
function fetchDealItems(int $category_id, string $stage_id): array
{
$factory = fetchDealFactory();
if ($factory === null) {
return [];
}
return $factory->getItems([
'select' => [
'ID',
'TITLE',
'CATEGORY_ID',
'STAGE_ID',
'COMPANY_ID',
'CONTACT_ID',
'ASSIGNED_BY_ID',
'OPPORTUNITY',
'CURRENCY_ID',
],
'filter' => [
'=CATEGORY_ID' => $category_id,
'=STAGE_ID' => $stage_id,
],
'order' => [
'ID' => 'ASC',
],
'limit' => DEAL_LIMIT,
]);
}
$deal_items = fetchDealItems($category_id, $stage_id);
foreach ($deal_items as $deal_item) {
print_r($deal_item->toArray());
} Добавить сделку
<?php
use Bitrix\Main\Loader;
use Bitrix\Crm\Service;
const CRM_DEAL_TYPE_ID = \CCrmOwnerType::Deal;
Loader::includeModule('crm');
/**
* Получает фабрику сделок.
*/
function fetchDealFactory()
{
return Service\Container::getInstance()->getFactory(CRM_DEAL_TYPE_ID);
}
/**
* Создаёт сделку.
*/
function addDeal(array $deal_fields): array
{
$factory = fetchDealFactory();
if ($factory === null) {
return [
'is_success' => false,
'deal_id' => 0,
'error_messages' => ['Фабрика сделок не найдена'],
];
}
$deal_item = $factory->createItem($deal_fields);
$operation = $factory->getAddOperation($deal_item);
$operation_result = $operation->launch();
return [
'is_success' => $operation_result->isSuccess(),
'deal_id' => (int) $deal_item->getId(),
'error_messages' => $operation_result->getErrorMessages(),
];
}
$deal_fields = [
'TITLE' => $title,
'CATEGORY_ID' => $category_id,
'STAGE_ID' => $stage_id,
'COMPANY_ID' => $company_id,
'CONTACT_ID' => $contact_id,
'ASSIGNED_BY_ID' => $assigned_by_id,
'OPPORTUNITY' => $opportunity,
'CURRENCY_ID' => 'RUB',
];
$add_result = addDeal($deal_fields);
print_r($add_result); Изменить сделку
<?php
use Bitrix\Main\Loader;
use Bitrix\Crm\Service;
const CRM_DEAL_TYPE_ID = \CCrmOwnerType::Deal;
Loader::includeModule('crm');
/**
* Получает фабрику сделок.
*/
function fetchDealFactory()
{
return Service\Container::getInstance()->getFactory(CRM_DEAL_TYPE_ID);
}
/**
* Переводит сделку на другую стадию.
*/
function moveDealToStage(int $deal_id, string $stage_id): array
{
$factory = fetchDealFactory();
if ($factory === null) {
return [
'is_success' => false,
'error_messages' => ['Фабрика сделок не найдена'],
];
}
$deal_item = $factory->getItem($deal_id);
if ($deal_item === null) {
return [
'is_success' => false,
'error_messages' => ['Сделка не найдена'],
];
}
$deal_item->setStageId($stage_id);
$operation = $factory->getUpdateOperation($deal_item);
$operation_result = $operation->launch();
return [
'is_success' => $operation_result->isSuccess(),
'error_messages' => $operation_result->getErrorMessages(),
];
}
$update_result = moveDealToStage($deal_id, $stage_id);
print_r($update_result); Удалить сделку
<?php
use Bitrix\Main\Loader;
use Bitrix\Crm\Service;
const CRM_DEAL_TYPE_ID = \CCrmOwnerType::Deal;
Loader::includeModule('crm');
/**
* Получает фабрику сделок.
*/
function fetchDealFactory()
{
return Service\Container::getInstance()->getFactory(CRM_DEAL_TYPE_ID);
}
/**
* Удаляет сделку.
*/
function deleteDeal(int $deal_id): array
{
$factory = fetchDealFactory();
if ($factory === null) {
return [
'is_success' => false,
'error_messages' => ['Фабрика сделок не найдена'],
];
}
$deal_item = $factory->getItem($deal_id);
if ($deal_item === null) {
return [
'is_success' => false,
'error_messages' => ['Сделка не найдена'],
];
}
$operation = $factory->getDeleteOperation($deal_item);
$operation_result = $operation->launch();
return [
'is_success' => $operation_result->isSuccess(),
'error_messages' => $operation_result->getErrorMessages(),
];
}
$delete_result = deleteDeal($deal_id);
print_r($delete_result); Связи
У сделки может быть компания и один или несколько контактов.
Контакты сделки
<?php
use Bitrix\Main\Loader;
use Bitrix\Crm\Binding\DealContactTable;
Loader::includeModule('crm');
/**
* Получает ID контактов сделки.
*/
function fetchDealContactIds(int $deal_id): array
{
return DealContactTable::getDealContactIDs($deal_id);
}
/**
* Привязывает контакты к сделке.
*/
function bindDealContactIds(int $deal_id, array $contact_ids): void
{
DealContactTable::bindContactIDs($deal_id, $contact_ids);
}
$contact_ids = fetchDealContactIds($deal_id);
print_r($contact_ids); Компания сделки
Основная компания сделки обычно хранится в поле COMPANY_ID. Для изменения
компании можно получить сделку через фабрику и установить новое значение поля.
<?php
use Bitrix\Main\Loader;
use Bitrix\Crm\Service;
const CRM_DEAL_TYPE_ID = \CCrmOwnerType::Deal;
Loader::includeModule('crm');
/**
* Изменяет компанию сделки.
*/
function updateDealCompany(int $deal_id, int $company_id): array
{
$factory = Service\Container::getInstance()->getFactory(CRM_DEAL_TYPE_ID);
if ($factory === null) {
return [
'is_success' => false,
'error_messages' => ['Фабрика сделок не найдена'],
];
}
$deal_item = $factory->getItem($deal_id);
if ($deal_item === null) {
return [
'is_success' => false,
'error_messages' => ['Сделка не найдена'],
];
}
$deal_item->set('COMPANY_ID', $company_id);
$operation = $factory->getUpdateOperation($deal_item);
$operation_result = $operation->launch();
return [
'is_success' => $operation_result->isSuccess(),
'error_messages' => $operation_result->getErrorMessages(),
];
}
$update_result = updateDealCompany($deal_id, $company_id);
print_r($update_result); События
События сделок позволяют выполнить свой PHP-код перед добавлением, после добавления, перед изменением, после изменения, перед удалением и после удаления сделки.
Список событий
Обработчики обычно регистрируют в /local/php_interface/init.php или внутри
своего модуля. Для коробочной разработки это удобный способ реагировать на изменения
сделок без REST-приложения.
| Событие | Когда вызывается | Метод |
|---|---|---|
OnBeforeCrmDealAdd | Перед добавлением сделки. | CCrmDeal::Add |
OnAfterCrmDealAdd | После добавления сделки. | CCrmDeal::Add |
OnBeforeCrmDealUpdate | Перед изменением сделки. | CCrmDeal::Update |
OnAfterCrmDealUpdate | После изменения сделки. | CCrmDeal::Update |
OnBeforeCrmDealDelete | Перед удалением сделки. | CCrmDeal::Delete |
OnAfterCrmDealDelete | После удаления сделки. | CCrmDeal::Delete |
OnAfterExternalCrmDealAdd | После добавления внешней сделки. | CCrmDeal::Add |
OnBeforeCrmDealProductRowsSave | Перед сохранением товарных строк сделки. | CCrmDeal::SaveProductRows |
OnAfterCrmDealProductRowsSave | После сохранения товарных строк сделки. | CCrmDeal::SaveProductRows |
Перед добавлением
В OnBeforeCrmDealAdd можно изменить поля до сохранения или отменить
создание сделки. Например, можно заполнить валюту по умолчанию или запретить создание
сделки без названия.
<?php
use Bitrix\Main\EventManager;
$event_manager = EventManager::getInstance();
/**
* Регистрирует обработчик перед добавлением сделки.
*/
function registerBeforeDealAddHandler(EventManager $event_manager): void
{
$event_manager->addEventHandlerCompatible(
'crm',
'OnBeforeCrmDealAdd',
'handleBeforeDealAdd'
);
}
/**
* Обрабатывает поля перед добавлением сделки.
*/
function handleBeforeDealAdd(array &$deal_fields): bool
{
global $APPLICATION;
if (empty($deal_fields['TITLE'])) {
$APPLICATION->ThrowException('Не заполнено название сделки');
return false;
}
if (empty($deal_fields['CURRENCY_ID'])) {
$deal_fields['CURRENCY_ID'] = 'RUB';
}
return true;
}
registerBeforeDealAddHandler($event_manager);
В событиях до сохранения массив полей передаётся по ссылке. Если изменить значение в
$deal_fields, оно попадёт в сохраняемую сделку.
После изменения
OnAfterCrmDealUpdate удобно использовать для логирования, синхронизации со
своими таблицами или запуска вспомогательной логики после изменения сделки.
<?php
use Bitrix\Main\EventManager;
$event_manager = EventManager::getInstance();
/**
* Регистрирует обработчик после изменения сделки.
*/
function registerAfterDealUpdateHandler(EventManager $event_manager): void
{
$event_manager->addEventHandlerCompatible(
'crm',
'OnAfterCrmDealUpdate',
'handleAfterDealUpdate'
);
}
/**
* Обрабатывает изменение сделки.
*/
function handleAfterDealUpdate(array &$deal_fields): void
{
$deal_id = isset($deal_fields['ID']) ? (int) $deal_fields['ID'] : 0;
if ($deal_id <= 0) {
return;
}
AddMessage2Log(
[
'message' => 'Сделка изменена',
'deal_id' => $deal_id,
'fields' => $deal_fields,
],
'deal_events'
);
}
registerAfterDealUpdateHandler($event_manager);
Событие после изменения не всегда содержит полный набор полей сделки. Если нужно
проверить поле, которого нет в $deal_fields, лучше отдельно получить
сделку по ID.
Перед удалением
OnBeforeCrmDealDelete можно использовать, если удаление нужно проверить или
запретить.
<?php
use Bitrix\Main\EventManager;
$event_manager = EventManager::getInstance();
/**
* Регистрирует обработчик перед удалением сделки.
*/
function registerBeforeDealDeleteHandler(EventManager $event_manager): void
{
$event_manager->addEventHandlerCompatible(
'crm',
'OnBeforeCrmDealDelete',
'handleBeforeDealDelete'
);
}
/**
* Проверяет возможность удаления сделки.
*/
function handleBeforeDealDelete(int $deal_id): bool
{
global $APPLICATION;
$has_delete_access = true;
if (!$has_delete_access) {
$APPLICATION->ThrowException('Удаление сделки запрещено');
return false;
}
AddMessage2Log(
[
'message' => 'Проверка перед удалением сделки',
'deal_id' => $deal_id,
],
'deal_events'
);
return true;
}
registerBeforeDealDeleteHandler($event_manager); Товарные строки
Для товарных строк сделки есть отдельные события:
OnBeforeCrmDealProductRowsSave и
OnAfterCrmDealProductRowsSave. Они относятся к сохранению товаров сделки,
а не к обычному изменению полей сделки.
<?php
use Bitrix\Main\EventManager;
$event_manager = EventManager::getInstance();
/**
* Регистрирует обработчик после сохранения товарных строк сделки.
*/
function registerAfterDealProductRowsSaveHandler(EventManager $event_manager): void
{
$event_manager->addEventHandlerCompatible(
'crm',
'OnAfterCrmDealProductRowsSave',
'handleAfterDealProductRowsSave'
);
}
/**
* Обрабатывает сохранение товарных строк сделки.
*/
function handleAfterDealProductRowsSave(int $deal_id, array $product_rows): void
{
AddMessage2Log(
[
'message' => 'Товарные строки сделки сохранены',
'deal_id' => $deal_id,
'product_rows' => $product_rows,
],
'deal_product_rows'
);
}
registerAfterDealProductRowsSaveHandler($event_manager);
Если обработчик не получает товарные строки в ожидаемом виде, сначала выведите все
аргументы обработчика в лог через func_get_args() и проверьте фактическую
структуру на своей версии коробки.