Как перенести дело между элементами одного типа

Scope: crm

Кто может выполнять метод: пользователи с правом на изменение элементов CRM

Выберите инструмент для разработки с AI-агентом:

  • используйте Битрикс24 Вайбкод, чтобы создать приложение для Битрикс24 по описанию задачи без знания языков программирования. Агент напишет код и разместит приложение на сервере без ручной настройки хостинга
  • используйте MCP-сервер, чтобы разрабатывать интеграцию через REST API в своем проекте. Агент будет обращаться к официальной REST-документации

Дела, связанные с элементами CRM, хранятся в таймлайне карточки элемента. Перенос дела из одного элемента в другой может потребоваться, когда в один лид попадает несколько писем или звонков, которые с точки зрения бизнеса относятся к разным лидам. В этом случае можно разделить исходный лид на несколько новых и перенести дела для корректного учета данных.

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

Дело можно перенести только между элементами одного типа: значения sourceEntityTypeId и targetEntityTypeId должны совпадать. Если типы разные, метод вернет ошибку SOURCE_AND_TARGET_ENTITY_TYPES_ARE_NOT_EQUAL_ERROR. Чтобы перенести дело между объектами разных типов, например из лида в компанию, используйте сценарий Как перенести дело из одного типа объекта в другой.

Для переноса дела последовательно выполним три метода:

  1. crm.activity.list — получим ID дела,

  2. crm.lead.add — создадим элемент, в который перенесем дело, в примере лид,

  3. crm.activity.binding.move — выполним перенос дела.

1. Получаем ID дела

Используем метод crm.activity.list с фильтром:

  • OWNER_TYPE_IDтип объекта, укажем 1 для лида,

  • OWNER_ID — ID элемента, из которого будем переносить дело.

В примере переносим дело из лида 1000977. ID лида виден в адресной строке его карточки, например /crm/lead/details/1000977/, или его можно получить методом crm.lead.list.

Без параметра select метод возвращает все поля дела. Чтобы сократить ответ, укажем только те поля, которые нужны сценарию: ID, OWNER_TYPE_ID, OWNER_ID, SUBJECT и DESCRIPTION. По полю DESCRIPTION в примере кода выбирается нужное дело.

Как использовать примеры в документации

import { B24Hook } from '@bitrix24/b24jssdk'
        
        const $b24 = B24Hook.fromWebhookUrl(process.env.B24_HOOK)
        // B24_HOOK = 'https://your-domain.bitrix24.ru/rest/USER_ID/TOKEN/'
        
        const result = await $b24.actions.v2.call.make({
            method: "crm.activity.list",
            params: {
                filter:
                {
                    "OWNER_TYPE_ID": 1,
                    "OWNER_ID": 1000977
                },
                select: [ "ID", "OWNER_TYPE_ID", "OWNER_ID", "SUBJECT", "DESCRIPTION" ]
            }
        });
        
require_once 'vendor/autoload.php';
        
        use Bitrix24\SDK\Services\ServiceBuilderFactory;
        use Symfony\Component\EventDispatcher\EventDispatcher;
        use Monolog\Logger;
        use Monolog\Handler\StreamHandler;
        
        $logger = new Logger('b24');
        $logger->pushHandler(new StreamHandler('php://stdout'));
        
        $serviceBuilder = (new ServiceBuilderFactory(new EventDispatcher(), $logger))
            ->initFromWebhook(getenv('B24_HOOK'));
        // B24_HOOK = 'https://your-domain.bitrix24.ru/rest/USER_ID/TOKEN/'
        
        $activities = $serviceBuilder->getCRMScope()->activity()->list(
            [],
            [
                'OWNER_TYPE_ID' => 1,
                'OWNER_ID' => 1000977,
            ],
            [
                'ID', 'OWNER_TYPE_ID', 'OWNER_ID', 'SUBJECT', 'DESCRIPTION'
            ],
            0
        )->getActivities();
        
import os
        
        from b24pysdk import BitrixWebhook, Client
        
        client = Client(
            BitrixWebhook(
                domain="your-domain.bitrix24.com",
                webhook_token=os.environ["B24_HOOK_TOKEN"],
            )
        )
        # B24_HOOK_TOKEN = 'user_id/webhook_key'
        
        result = client.crm.activity.list(
            filter={
                "OWNER_TYPE_ID": 1,
                "OWNER_ID": 1000977,
            },
            select=["ID", "OWNER_TYPE_ID", "OWNER_ID", "SUBJECT", "DESCRIPTION"],
        ).response.result
        

В результате получим все дела, связанные с указанным элементом.

{
            "result": [
                {
                    "ID": "7685",
                    "OWNER_TYPE_ID": "1",
                    "OWNER_ID": "1000977",
                    "SUBJECT": "для лидов",
                    "DESCRIPTION": "<div>письмо первое</div>\r\n"
                },
                {
                    "ID": "7687",
                    "OWNER_TYPE_ID": "1",
                    "OWNER_ID": "1000977",
                    "SUBJECT": "для лидов",
                    "DESCRIPTION": "<div>письмо второе</div>\r\n"
                }
            ],
            "total": 2
        }
        

Выберем нужное дело из списка полученных и сохраним его ID: 7687. Это значение передадим в параметр activityId на шаге 3.

2. Создаем новый элемент

Для создания нового лида, в который перенесем дело письма, выполним метод crm.lead.add с параметрами:

  • fields[TITLE] — название лида,

  • fields[ASSIGNED_BY_ID] — идентификатор ответственного за новый лид,

  • params[REGISTER_SONET_EVENT] — параметр для регистрации уведомлений, укажем Y, чтобы на новый лид сработали системные уведомления при создании.

В методе должны быть указаны все обязательные поля для лидов вашего Битрикс24, иначе лид создан не будет. Проверить, какие поля обязательные, можно методом crm.lead.fields, он вызывается без параметров.

const result = await $b24.actions.v2.call.make({
            method: "crm.lead.add",
            params: {
                fields:
                {
                    TITLE: "Второй лид",
                    ASSIGNED_BY_ID: 1,
                },
                params: {
                    REGISTER_SONET_EVENT: "Y",
                }
            }
        });
        
$newLeadId = $serviceBuilder->getCRMScope()->lead()->add(
            [
                'TITLE' => 'Второй лид',
                'ASSIGNED_BY_ID' => 1,
            ],
            [
                'REGISTER_SONET_EVENT' => 'Y',
            ]
        )->getId();
        
result = client.crm.lead.add(
            fields={
                "TITLE": "Второй лид",
                "ASSIGNED_BY_ID": 1,
            },
            params={
                "REGISTER_SONET_EVENT": "Y",
            },
        ).response.result
        

В результате получим ID созданного лида. Это значение передадим в параметр targetEntityId на шаге 3.

{
            "result": 1000979
        }
        

3. Переносим дело между элементами

Для переноса дела используем метод crm.activity.binding.move с параметрами:

  • activityId — ID дела, получили на шаге 1 в методе crm.activity.list,

  • sourceEntityTypeId — ID типа объекта, откуда переносим дело,

  • sourceEntityId — ID элемента, откуда переносим дело,

  • targetEntityTypeId — ID типа объекта, куда переносим дело,

  • targetEntityId — ID элемента, куда переносим дело, получили на шаге 2 в методе crm.lead.add.

В примере оба типа объекта равны 1 — дело переносится из лида в лид.

const result = await $b24.actions.v2.call.make({
            method: 'crm.activity.binding.move',
            params: {
                activityId: 7687,
                sourceEntityTypeId: 1,
                sourceEntityId: 1000977,
                targetEntityTypeId: 1,
                targetEntityId: 1000979
            }
        });
        
// crm.activity.binding.move не имеет типизированной обертки — вызываем через core
        $result = $serviceBuilder->core->call(
            'crm.activity.binding.move',
            [
                'activityId' => 7687,
                'sourceEntityTypeId' => 1,
                'sourceEntityId' => 1000977,
                'targetEntityTypeId' => 1,
                'targetEntityId' => 1000979
            ]
        );
        
result = client.crm.activity.binding.move(
            activity_id=7687,
            source_entity_type_id=1,
            source_entity_id=1000977,
            target_entity_type_id=1,
            target_entity_id=1000979,
        ).response.result
        

В результате получим true, перенос дела прошел успешно.

{
            "result": true
        }
        

Пример кода

import { B24Hook } from '@bitrix24/b24jssdk'
        import { createInterface } from 'node:readline/promises'
        
        const $b24 = B24Hook.fromWebhookUrl(process.env.B24_HOOK)
        // B24_HOOK = 'https://your-domain.bitrix24.ru/rest/USER_ID/TOKEN/'
        
        async function call(method, params) {
            const result = await $b24.actions.v2.call.make({ method, params });
            if (!result.isSuccess) {
                throw new Error(result.getErrorMessages().join('; '));
            }
            return result.getData().result;
        }
        
        // Функция для выполнения всех шагов
        async function transferActivity(firstLeadId, searchPhrase) {
            // Шаг 1: Получаем список дел для указанного лида
            const activities = await call("crm.activity.list", {
                filter: {
                    "OWNER_TYPE_ID": 1,
                    "OWNER_ID": firstLeadId
                },
                select: [ "ID", "OWNER_TYPE_ID", "OWNER_ID", "SUBJECT", "DESCRIPTION" ]
            });
        
            const targetActivity = activities.find(activity => activity.DESCRIPTION.includes(searchPhrase));
        
            if (!targetActivity) {
                console.log(`Дело с описанием, содержащим '${searchPhrase}', не найдено.`);
                return;
            }
        
            const activityId = targetActivity.ID;
        
            // Шаг 2: Создаем новый лид
            const newLeadId = await call("crm.lead.add", {
                fields: {
                    TITLE: "Второй лид",
                    ASSIGNED_BY_ID: 1,
                },
                params: {
                    REGISTER_SONET_EVENT: "Y",
                }
            });
        
            // Шаг 3: Переносим дело
            await call('crm.activity.binding.move', {
                activityId: activityId,
                sourceEntityTypeId: 1,
                sourceEntityId: firstLeadId,
                targetEntityTypeId: 1,
                targetEntityId: newLeadId
            });
        
            console.log("Дело успешно перенесено.");
        }
        
        // Запрашиваем ID первого лида и фразу для поиска у пользователя
        const rl = createInterface({ input: process.stdin, output: process.stdout });
        const firstLeadId = await rl.question("Введите ID первого лида: ");
        const searchPhrase = await rl.question("Введите фразу для поиска по телу письма: ");
        rl.close();
        
        // Запускаем функцию
        try {
            await transferActivity(firstLeadId, searchPhrase);
        } catch (error) {
            console.error(error.message);
        }
        
<?php
        require_once 'vendor/autoload.php';
        
        use Bitrix24\SDK\Services\ServiceBuilderFactory;
        use Symfony\Component\EventDispatcher\EventDispatcher;
        use Monolog\Logger;
        use Monolog\Handler\StreamHandler;
        
        $logger = new Logger('b24');
        $logger->pushHandler(new StreamHandler('php://stdout'));
        
        $serviceBuilder = (new ServiceBuilderFactory(new EventDispatcher(), $logger))
            ->initFromWebhook(getenv('B24_HOOK'));
        // B24_HOOK = 'https://your-domain.bitrix24.ru/rest/USER_ID/TOKEN/'
        
        // Функция для выполнения всех шагов
        function transferActivity($serviceBuilder, $firstLeadId, $searchPhrase) {
            $crm = $serviceBuilder->getCRMScope();
        
            try {
                // Шаг 1: Получаем список дел для указанного лида
                $activities = $crm->activity()->list(
                    [],
                    [
                        'OWNER_TYPE_ID' => 1,
                        'OWNER_ID' => $firstLeadId,
                    ],
                    [
                        'ID', 'OWNER_TYPE_ID', 'OWNER_ID', 'SUBJECT', 'DESCRIPTION'
                    ],
                    0
                )->getActivities();
        
                $targetActivity = null;
        
                foreach ($activities as $activity) {
                    if (strpos((string)$activity->DESCRIPTION, $searchPhrase) !== false) {
                        $targetActivity = $activity;
                        break;
                    }
                }
        
                if (!$targetActivity) {
                    echo "Дело с описанием, содержащим '{$searchPhrase}', не найдено.";
                    return;
                }
        
                $activityId = $targetActivity->ID;
        
                // Шаг 2: Создаем новый лид
                $newLeadId = $crm->lead()->add(
                    [
                        'TITLE' => 'Второй лид',
                        'ASSIGNED_BY_ID' => 1,
                    ],
                    [
                        'REGISTER_SONET_EVENT' => 'Y',
                    ]
                )->getId();
        
                // Шаг 3: Переносим дело
                // crm.activity.binding.move не имеет типизированной обертки — вызываем через core
                $serviceBuilder->core->call(
                    'crm.activity.binding.move',
                    [
                        'activityId' => $activityId,
                        'sourceEntityTypeId' => 1,
                        'sourceEntityId' => $firstLeadId,
                        'targetEntityTypeId' => 1,
                        'targetEntityId' => $newLeadId
                    ]
                );
        
                echo 'Дело успешно перенесено.';
            } catch (\Throwable $e) {
                echo 'Ошибка: ' . $e->getMessage();
            }
        }
        
        // Запрашиваем ID первого лида и фразу для поиска у пользователя
        $firstLeadId = readline("Введите ID первого лида: ");
        $searchPhrase = readline("Введите фразу для поиска по телу письма: ");
        
        // Запускаем функцию
        transferActivity($serviceBuilder, $firstLeadId, $searchPhrase);
        
import os
        
        from b24pysdk import BitrixWebhook, Client
        from b24pysdk.errors import BitrixAPIError
        
        
        def transfer_activity(client, first_lead_id, search_phrase):
            try:
                activities = client.crm.activity.list(
                    filter={
                        "OWNER_TYPE_ID": 1,
                        "OWNER_ID": first_lead_id,
                    },
                    select=["ID", "OWNER_TYPE_ID", "OWNER_ID", "SUBJECT", "DESCRIPTION"],
                ).response.result
            except BitrixAPIError as error:
                print(f"Ошибка: {error}")
                return
        
            target_activity = None
            for activity in activities:
                if search_phrase in str(activity.get("DESCRIPTION") or ""):
                    target_activity = activity
                    break
        
            if target_activity is None:
                print(f"Дело с описанием, содержащим '{search_phrase}', не найдено.")
                return
        
            activity_id = int(target_activity["ID"])
        
            try:
                new_lead_id = client.crm.lead.add(
                    fields={
                        "TITLE": "Второй лид",
                        "ASSIGNED_BY_ID": 1,
                    },
                    params={
                        "REGISTER_SONET_EVENT": "Y",
                    },
                ).response.result
            except BitrixAPIError as error:
                print(f"Ошибка: {error}")
                return
        
            try:
                result = client.crm.activity.binding.move(
                    activity_id=activity_id,
                    source_entity_type_id=1,
                    source_entity_id=first_lead_id,
                    target_entity_type_id=1,
                    target_entity_id=new_lead_id,
                ).response.result
            except BitrixAPIError as error:
                print(f"Ошибка: {error}")
            else:
                if result:
                    print("Дело успешно перенесено.")
        
        
        client = Client(
            BitrixWebhook(
                domain="your-domain.bitrix24.com",
                webhook_token=os.environ["B24_HOOK_TOKEN"],
            )
        )
        # B24_HOOK_TOKEN = 'user_id/webhook_key'
        
        first_lead_id = int(input("Введите ID первого лида: "))
        search_phrase = input("Введите фразу для поиска по телу письма: ")
        
        transfer_activity(client, first_lead_id, search_phrase)
        

Проверим результат

Откройте карточку нового лида — в таймлайне появится перенесенное дело. В карточке исходного лида этого дела больше не будет: метод переносит связь, а не копирует ее.

Проверить результат через REST можно методом crm.activity.binding.list. Передайте в него activityId перенесенного дела — метод вернет все связи дела. После успешного переноса в ответе останется одна связь: тип объекта 1 и ID нового лида.

{
            "result": [
                {
                    "entityTypeId": 1,
                    "entityId": 1000979
                }
            ]
        }
        

Ошибки и диагностика

Если метод вернул ошибку, проверьте данные запроса.

Код

Причина и действие

SOURCE_AND_TARGET_ENTITY_TYPES_ARE_NOT_EQUAL_ERROR

Значения sourceEntityTypeId и targetEntityTypeId не совпадают. Между объектами разных типов дело переносят по сценарию Как перенести дело из одного типа объекта в другой

SOURCE_AND_TARGET_ENTITY_ID_ARE_EQUAL_ERROR

В sourceEntityId и targetEntityId передан один и тот же элемент. Укажите разные элементы

BINDING_NOT_FOUND

Дело не привязано к элементу из sourceEntityTypeId и sourceEntityId. Проверьте, из какого элемента переносите дело

ACTIVITY_IS_ALREADY_BOUND

Дело уже привязано к элементу, в который вы его переносите

NOT_FOUND

Дело или элемент CRM не найдены. Проверьте activityId, sourceEntityId и targetEntityId

ACCESS_DENIED

У пользователя нет прав на изменение элементов CRM

100

Не переданы обязательные параметры. Метод требует все пять: activityId, sourceEntityTypeId, sourceEntityId, targetEntityTypeId и targetEntityId

Что важно учитывать

  • Метод crm.activity.binding.move переносит дело только между элементами одного типа
  • Сценарий работает не только для лидов. Чтобы перенести дело между двумя сделками, укажите 2 в OWNER_TYPE_ID шага 1 и в обоих параметрах типа объекта шага 3, а целевую сделку создайте методом crm.deal.add. Значения для остальных типов — в справочнике типов объектов
  • Дело переносится вместе со всей историей: письмо или звонок исчезнет из таймлайна исходного элемента
  • Вместе со связью у дела меняются собственные поля OWNER_TYPE_ID и OWNER_ID — когда связь остается одна, владельцем становится ее элемент. Поэтому crm.activity.list с фильтром по исходному лиду перенесенное дело больше не вернет, ищите его по новому лиду
  • Метод crm.lead.add требует все обязательные поля лида вашего Битрикс24, состав полей проверяйте методом crm.lead.fields
  • Повторный запуск примера создает еще один лид. Уже перенесенное дело в исходном лиде не найдется, и пример завершится сообщением, что дело не найдено

Продолжите изучение