Как встроить виджет во вкладку карточки CRM

Scope: placement, crm

Кто может выполнять методы:

  • placement.bind — администратор
  • crm.item.get — любой пользователь с правом чтения сделки

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

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

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

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

  1. placement.bind — зарегистрируем обработчик для вкладки CRM_DEAL_DETAIL_TAB
  2. crm.item.get — получим данные сделки по идентификатору из PLACEMENT_OPTIONS

Исходный пример и дополнительные материалы доступны в уроке Встройка в карточку CRM.

Как работает сценарий

Приложение регистрирует URL обработчика методом placement.bind и указывает код CRM_DEAL_DETAIL_TAB. После завершения установки приложения в карточке сделки появляется новая вкладка.

Когда пользователь открывает вкладку, Битрикс24 загружает обработчик в iframe и передает ему контекст вызова. В PLACEMENT_OPTIONS.ID приходит идентификатор текущей сделки. Обработчик передает этот идентификатор в crm.item.get и показывает полученные данные.

1. Подготовьте приложение

Создайте приложение с интерфейсом и добавьте ему права:

  • placement — для регистрации обработчика виджета
  • crm — для получения данных сделки

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

Разместите страницу обработчика по публичному HTTPS-адресу. В примерах используется адрес:

https://your-domain.example/deal-tab.php
        

Сервер должен разрешать открытие страницы в iframe. Проверьте заголовок X-Frame-Options и директиву frame-ancestors заголовка Content-Security-Policy: они не должны запрещать встраивание страницы в Битрикс24.

URL обработчика должен быть доступен из внешней сети. Не используйте localhost, адреса локальной сети и самоподписанные SSL-сертификаты.

Метод placement.bind работает только в контексте приложения. Входящий вебхук для регистрации вкладки не подходит.

2. Зарегистрируйте вкладку

Зарегистрируем обработчик методом placement.bind. Передадим параметры:

  • PLACEMENT — код места встраивания CRM_DEAL_DETAIL_TAB
  • HANDLER — публичный URL страницы, которая откроется во вкладке
  • TITLE — название вкладки
  • LANG_ALL — локализованные названия вкладки

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

// npm install @bitrix24/b24jssdk
        // Страница настроек приложения, открытая в iframe Битрикс24
        import { initializeB24Frame } from '@bitrix24/b24jssdk'
        
        const $b24 = await initializeB24Frame()
        
        const response = await $b24.actions.v2.call.make({
            method: 'placement.bind',
            params: {
                PLACEMENT: 'CRM_DEAL_DETAIL_TAB',
                HANDLER: 'https://your-domain.example/deal-tab.php',
                TITLE: 'Deal data',
                LANG_ALL: {
                    ru: {
                        TITLE: 'Данные сделки',
                    },
                    en: {
                        TITLE: 'Deal data',
                    },
                },
            },
            requestId: 'placement-bind',
        })
        
        if (!response.isSuccess) {
            throw new Error(response.getErrorMessages().join('; '))
        }
        
        console.info('Вкладка зарегистрирована')
        
<?php
        // composer require bitrix24/b24phpsdk:"^3.0"
        require_once 'vendor/autoload.php';
        
        use Bitrix24\SDK\Core\Exceptions\BaseException;
        
        // $b24 построен на токене приложения — см. сценарий
        // «Как встроить виджет в лид в виде пользовательского поля»
        try
        {
            $b24->getPlacementScope()->placement()->bind(
                'CRM_DEAL_DETAIL_TAB',
                'https://your-domain.example/deal-tab.php',
                [
                    'ru' => ['TITLE' => 'Данные сделки'],
                    'en' => ['TITLE' => 'Deal data'],
                ]
            );
        
            echo 'Вкладка зарегистрирована';
        }
        catch (BaseException $exception)
        {
            echo $exception->getMessage();
        }
        
# pip install b24pysdk
        # client построен на токене приложения — см. сценарий
        # «Как встроить виджет в лид в виде пользовательского поля»
        from b24pysdk.errors import BitrixAPIError
        
        try:
            bitrix_response = client.placement.bind(
                placement="CRM_DEAL_DETAIL_TAB",
                handler="https://your-domain.example/deal-tab.php",
                title="Deal data",
                lang_all={
                    "ru": {"TITLE": "Данные сделки"},
                    "en": {"TITLE": "Deal data"},
                },
            ).response
            print("Вкладка зарегистрирована:", bitrix_response.result)
        except BitrixAPIError as error:
            print(error)
        

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

{
            "result": true
        }
        

После регистрации завершите установку приложения. Пока установка не завершена, вкладка недоступна обычным пользователям.

3. Обработайте открытие вкладки

При открытии вкладки Битрикс24 передает обработчику данные POST-запросом. Для карточки сделки основные параметры выглядят так:

PLACEMENT=CRM_DEAL_DETAIL_TAB
        PLACEMENT_OPTIONS={"ID":"3473"}
        

PLACEMENT_OPTIONS передается как JSON-строка. В PHP и Python преобразуйте ее в массив или словарь — например, функцией json_decode или json.loads. В B24JsSDK свойство $b24.placement.options возвращает готовый объект, а $b24.placement.placement — код места встраивания.

Параметр
тип

Описание

PLACEMENT
string

Код места встраивания. Для вкладки сделки приходит CRM_DEAL_DETAIL_TAB

PLACEMENT_OPTIONS
string

JSON-строка с контекстом открытой карточки

ID
string

Идентификатор сделки внутри PLACEMENT_OPTIONS

DOMAIN
string

Адрес Битрикс24, в котором пользователь открыл вкладку

PROTOCOL
string

Протокол для обращения к Битрикс24: 0 — HTTP, 1 — HTTPS

AUTH_ID
string

OAuth-токен пользователя, который открыл вкладку. PHP-обработчик использует токен для вызова crm.item.get с правами этого пользователя

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

4. Получите данные сделки

Вызовем crm.item.get из обработчика. Для сделки передадим:

  • entityTypeId: 2 — идентификатор типа объекта CRM «Сделка»
  • id — идентификатор из PLACEMENT_OPTIONS.ID

Метод выполняется с авторизацией пользователя, который открыл вкладку:

  • JS работает внутри iframe — initializeB24Frame берет авторизацию из контекста вкладки
  • PHP и Python строят клиент на данных запроса, которые Битрикс24 передает обработчику, включая токен AUTH_ID
<!DOCTYPE html>
        <html lang="ru">
            <head>
                <meta charset="UTF-8">
                <title>Данные сделки</title>
            </head>
            <body>
                <h2 id="deal-title">Загрузка данных сделки</h2>
                <div id="deal-stage"></div>
        
                <script type="module">
                    // npm install @bitrix24/b24jssdk
                    import { initializeB24Frame } from '@bitrix24/b24jssdk'
        
                    const $b24 = await initializeB24Frame()
        
                    const dealId = Number($b24.placement.options.ID)
        
                    if (
                        $b24.placement.placement !== 'CRM_DEAL_DETAIL_TAB'
                        || !Number.isInteger(dealId)
                        || dealId <= 0
                    ) {
                        document.getElementById('deal-title').textContent =
                            'Не удалось определить сделку'
                    } else {
                        const response = await $b24.actions.v2.call.make({
                            method: 'crm.item.get',
                            params: {
                                entityTypeId: 2,
                                id: dealId,
                            },
                            requestId: 'deal-get',
                        })
        
                        if (!response.isSuccess) {
                            document.getElementById('deal-title').textContent =
                                response.getErrorMessages().join('; ')
                        } else {
                            const deal = response.getData().result.item
        
                            document.getElementById('deal-title').textContent =
                                deal.title || 'Сделка без названия'
                            document.getElementById('deal-stage').textContent =
                                'Стадия: ' + deal.stageId
                        }
                    }
                </script>
            </body>
        </html>
        
<?php
        // composer require bitrix24/b24phpsdk:"^3.0"
        require_once 'vendor/autoload.php';
        
        use Bitrix24\SDK\Core\Credentials\ApplicationProfile;
        use Bitrix24\SDK\Core\Exceptions\BaseException;
        use Bitrix24\SDK\Services\ServiceBuilderFactory;
        use Symfony\Component\HttpFoundation\Request;
        
        $request = Request::createFromGlobals();
        
        $placement = (string)$request->request->get('PLACEMENT', '');
        $placementOptions = json_decode(
            (string)$request->request->get('PLACEMENT_OPTIONS', '[]'),
            true
        ) ?: [];
        $dealId = (int)($placementOptions['ID'] ?? 0);
        
        $error = '';
        $deal = null;
        
        if ($placement !== 'CRM_DEAL_DETAIL_TAB' || $dealId <= 0)
        {
            $error = 'Не удалось получить контекст вызова';
        }
        else
        {
            $appProfile = ApplicationProfile::initFromArray([
                'BITRIX24_PHP_SDK_APPLICATION_CLIENT_ID' => 'local.xxxxxxxx.xxxxxxxx',
                'BITRIX24_PHP_SDK_APPLICATION_CLIENT_SECRET' => 'yyyyyyyy',
                'BITRIX24_PHP_SDK_APPLICATION_SCOPE' => 'crm,placement',
            ]);
        
            try
            {
                // SDK сам возьмет DOMAIN и AUTH_ID из запроса встройки
                $b24 = ServiceBuilderFactory::createServiceBuilderFromPlacementRequest(
                    $request,
                    $appProfile
                );
        
                $deal = $b24->getCRMScope()->item()->get(2, $dealId)->item();
            }
            catch (BaseException $exception)
            {
                $error = $exception->getMessage();
            }
        }
        ?>
        <!DOCTYPE html>
        <html lang="ru">
            <head>
                <meta charset="UTF-8">
                <title>Данные сделки</title>
            </head>
            <body>
                <?php if ($error !== ''): ?>
                    <p><?=htmlspecialchars($error)?></p>
                <?php else: ?>
                    <h2><?=htmlspecialchars($deal->title ?? 'Сделка без названия')?></h2>
                    <p>Стадия: <?=htmlspecialchars($deal->stageId ?? '')?></p>
                <?php endif; ?>
            </body>
        </html>
        
# pip install b24pysdk flask
        from flask import Flask, request
        from b24pysdk import BitrixApp, BitrixToken, Client
        from b24pysdk.errors import BitrixAPIError
        import json
        
        app = Flask(__name__)
        
        bitrix_app = BitrixApp(
            client_id="local.xxxxxxxx.xxxxxxxx",
            client_secret="yyyyyyyy",
        )
        
        
        @app.post("/deal-tab")
        def deal_tab():
            placement = request.form.get("PLACEMENT", "")
            options = json.loads(request.form.get("PLACEMENT_OPTIONS", "{}") or "{}")
            deal_id = int(options.get("ID", 0))
        
            if placement != "CRM_DEAL_DETAIL_TAB" or deal_id <= 0:
                return "Не удалось получить контекст вызова"
        
            # Битрикс24 передает обработчику домен и токен пользователя
            client = Client(
                BitrixToken(
                    domain=request.args.get("DOMAIN", ""),
                    auth_token=request.form.get("AUTH_ID", ""),
                    bitrix_app=bitrix_app,
                )
            )
        
            try:
                deal = client.crm.item.get(
                    entity_type_id=2,
                    bitrix_id=deal_id,
                ).response.result["item"]
            except BitrixAPIError as error:
                return str(error)
        
            return f"{deal.get('title', 'Сделка без названия')} — стадия: {deal.get('stageId', '')}"
        

Метод вернет объект item с данными сделки, доступными пользователю, чья авторизация используется в запросе.

{
            "result": {
                "item": {
                    "id": 3473,
                    "title": "Подготовка предложения",
                    "stageId": "NEW"
                }
            }
        }
        

Идентификатор из PLACEMENT_OPTIONS можно использовать и для других действий: получить связанные контакты и компанию, запросить данные во внешней системе или показать собственный интерфейс работы со сделкой.

5. Проверьте виджет

  1. Установите приложение в тестовый Битрикс24
  2. Убедитесь, что установка приложения завершена
  3. Откройте раздел CRM
  4. Откройте любую сделку
  5. Найдите вкладку Данные сделки
  6. Проверьте, что обработчик показывает название и стадию открытой сделки

Если вкладка не появилась, проверьте регистрацию обработчика методом placement.get. В ответе должны быть код CRM_DEAL_DETAIL_TAB и URL страницы обработчика.

Другие карточки CRM

По тому же сценарию можно добавить вкладку в карточки других объектов. Замените код в PLACEMENT и укажите соответствующий entityTypeId в crm.item.get.

Объект CRM

PLACEMENT

entityTypeId

Лид

CRM_LEAD_DETAIL_TAB

1

Сделка

CRM_DEAL_DETAIL_TAB

2

Контакт

CRM_CONTACT_DETAIL_TAB

3

Компания

CRM_COMPANY_DETAIL_TAB

4

Счет

CRM_SMART_INVOICE_DETAIL_TAB

31

Коды для коммерческих предложений и смарт-процессов смотрите в описании точки CRM_XXX_DETAIL_TAB.

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