Введение
Стандартный способ добавления вкладок в карточки CRM через REST API имеет несколько недостатков:
- Запросы идут через облачные серверы Битрикс24
- Вкладки загружаются медленнее из-за дополнительных сетевых вызовов
- Зависимость от внешнего API и необходимости регистрировать приложения
- Сложность с передачей параметров и отладкой
Решение
Мы добавим новую вкладку в карточку CRM, которая при клике открывает стандартный слайдер Битрикс24 с вашим контентом (например, отчёт, протокол согласования, интеграция со сторонним сервисом).
Что понадобится
- Коробочная версия Битрикс24
- Доступ к файлам сервера (через FTP или файловый менеджер)
- Базовое понимание PHP и JavaScript
Пошаговая инструкция
Шаг 1. Создаем структуру папок
text/local/
├── php_interface/
│ └── init.php # Точка входа, подключается на каждой странице
└── js/
└── custom_crm_tab/
└── script.js # Скрипт для добавления вкладки
Шаг 2. Создаем файл /local/php_interface/init.php
Этот файл проверяет URL страницы и подключает наш JavaScript-скрипт только на нужных страницах.
php<?php
// /local/php_interface/init.php
AddEventHandler("main", "OnEpilog", function() {
$request = \Bitrix\Main\Context::getCurrent()->getRequest();
$uri = $request->getRequestUri();
$path = strtok($uri, '?');
// Определяем тип сущности и ID элемента из URL
$entityTypeId = null;
$entityId = null;
// Лид (Lead)
if (preg_match('#/crm/lead/details/(\d+)/#', $path, $matches)) {
$entityTypeId = 3;
$entityId = (int)$matches[1];
}
// Контакт (Contact)
elseif (preg_match('#/crm/contact/details/(\d+)/#', $path, $matches)) {
$entityTypeId = 4;
$entityId = (int)$matches[1];
}
// Компания (Company)
elseif (preg_match('#/crm/company/details/(\d+)/#', $path, $matches)) {
$entityTypeId = 5;
$entityId = (int)$matches[1];
}
// Сделка (Deal)
elseif (preg_match('#/crm/deal/details/(\d+)/#', $path, $matches)) {
$entityTypeId = 2;
$entityId = (int)$matches[1];
}
// Смарт-процесс (Smart Process)
elseif (preg_match('#/crm/type/(\d+)/details/(\d+)/#', $path, $matches)) {
$entityTypeId = (int)$matches[1];
$entityId = (int)$matches[2];
}
// Кастомный путь для смарт-процесса
elseif (preg_match('#/page/soglasovanie/type/(\d+)/details/(\d+)/#', $path, $matches)) {
$entityTypeId = (int)$matches[1];
$entityId = (int)$matches[2];
}
// Если это не карточка CRM - выходим
if (!$entityTypeId || !$entityId) {
return;
}
// Подключаем наш JavaScript-скрипт
$asset = \Bitrix\Main\Page\Asset::getInstance();
$asset->addJs('/local/js/custom_crm_tab/script.js');
// Передаем параметры в JavaScript
$asset->addString('
<script>
BX.ready(function() {
if (typeof window.CustomCrmTab !== "undefined") {
window.CustomCrmTab.init({
entityTypeId: ' . $entityTypeId . ',
entityId: ' . $entityId . '
});
}
});
</script>
');
});
Шаг 3. Создаем файл /local/js/custom_crm_tab/script.js
Этот скрипт находит контейнер с вкладками и добавляет свою кнопку.
javascript// /local/js/custom_crm_tab/script.js
console.log("CustomCrmTab: script loaded");
window.CustomCrmTab = {
params: null,
tabId: 'custom_crm_tab',
init: function(params) {
console.log("CustomCrmTab: init", params);
this.params = params;
this.waitForTabsContainer();
},
waitForTabsContainer: function() {
var self = this;
var attempts = 0;
var maxAttempts = 30;
var checkInterval = setInterval(function() {
attempts++;
var tabsContainer = document.querySelector('.main-buttons-inner-container');
if (tabsContainer && tabsContainer.children.length > 0) {
clearInterval(checkInterval);
console.log("CustomCrmTab: tabs container found");
self.addTab(tabsContainer);
} else if (attempts >= maxAttempts) {
clearInterval(checkInterval);
console.error("CustomCrmTab: tabs container not found");
}
}, 500);
},
addTab: function(tabsContainer) {
if (document.getElementById(this.tabId)) {
console.log("CustomCrmTab: tab already exists");
return;
}
console.log("CustomCrmTab: creating tab");
var tabButton = document.createElement('div');
tabButton.id = this.tabId;
tabButton.className = 'main-buttons-item';
tabButton.setAttribute('role', 'listitem');
tabButton.innerHTML = '<a tabindex="0" class="main-buttons-item-link" href="javascript:void(0)" style="cursor: pointer;">' +
'<span class="main-buttons-item-icon"></span>' +
'<span class="main-buttons-item-text">' +
'<span class="main-buttons-item-text-title">' +
'<span class="main-buttons-item-text-box">Моя вкладка</span>' +
'</span>' +
'</span>' +
'</a>';
var self = this;
var link = tabButton.querySelector('.main-buttons-item-link');
link.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
console.log("CustomCrmTab: opening slider");
self.openSlider();
});
tabsContainer.appendChild(tabButton);
console.log("CustomCrmTab: tab added");
},
openSlider: function() {
var url = '/local/custom_content.php?' +
'entity_type_id=' + encodeURIComponent(this.params.entityTypeId) +
'&entity_id=' + encodeURIComponent(this.params.entityId);
console.log("CustomCrmTab: opening URL", url);
if (typeof BX !== 'undefined' && BX.SidePanel) {
BX.SidePanel.Instance.open(url, {
width: 1200,
cacheable: false,
allowChangeHistory: false,
title: 'Моя вкладка'
});
} else {
window.open(url, '_blank');
}
}
};
Шаг 4. Создаем файл с вашим контентом /local/custom_content.php
Это пример файла, который будет открываться в слайдере. Здесь может быть любая ваша логика.
php<?php
// /local/custom_content.php
require_once($_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php');
$entityTypeId = (int)($_GET['entity_type_id'] ?? 0);
$entityId = (int)($_GET['entity_id'] ?? 0);
if ($entityId <= 0) {
echo "Error: entity ID not provided";
return;
}
// Загружаем модуль CRM для работы с данными
if (\Bitrix\Main\Loader::includeModule('crm')) {
$title = '';
// Получаем название сущности в зависимости от типа
switch ($entityTypeId) {
case 3: // Lead
$lead = \CCrmLead::GetByID($entityId);
$title = $lead['TITLE'] ?? 'Lead #' . $entityId;
break;
case 4: // Contact
$contact = \CCrmContact::GetByID($entityId);
$title = trim(($contact['NAME'] ?? '') . ' ' . ($contact['LAST_NAME'] ?? ''));
$title = $title ?: 'Contact #' . $entityId;
break;
case 5: // Company
$company = \CCrmCompany::GetByID($entityId);
$title = $company['TITLE'] ?? 'Company #' . $entityId;
break;
case 2: // Deal
$deal = \CCrmDeal::GetByID($entityId);
$title = $deal['TITLE'] ?? 'Deal #' . $entityId;
break;
default: // Smart process
$factory = \Bitrix\Crm\Service\Container::getInstance()->getFactory($entityTypeId);
if ($factory) {
$item = $factory->getItem($entityId);
$title = $item ? ($item->getTitle() ?: 'Item #' . $entityId) : 'Item #' . $entityId;
}
break;
}
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Custom Content</title>
<style>
body {
font-family: system-ui, -apple-system, sans-serif;
padding: 20px;
background: #fff;
}
h1 {
font-size: 20px;
margin-bottom: 20px;
}
.info {
background: #f5f5f5;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
}
.info p {
margin: 5px 0;
}
</style>
</head>
<body>
<h1>Custom Tab Content</h1>
<div class="info">
<p><strong>Entity Type ID:</strong> <?= htmlspecialchars($entityTypeId) ?></p>
<p><strong>Entity ID:</strong> <?= htmlspecialchars($entityId) ?></p>
<p><strong>Title:</strong> <?= htmlspecialchars($title) ?></p>
</div>
<p>Here you can place any content: reports, approval protocols, integrations, etc.</p>
</body>
</html>
<?php
require_once($_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/epilog_after.php');
?>
Поддержка разных типов сущностей
В init.php уже добавлены все основные типы CRM:
| Сущность | Тип | Пример URL |
|---|---|---|
| Лид (Lead) | 3 |
/crm/lead/details/123/
|
| Контакт (Contact) | 4 |
/crm/contact/details/123/
|
| Компания (Company) | 5 |
/crm/company/details/123/
|
| Сделка (Deal) | 2 |
/crm/deal/details/123/
|
| Смарт-процесс | динамический |
/crm/type/1034/details/123/
|
| Кастомный путь | динамический |
/page/your-page/type/1034/details/123/
|
Как это работает
-
init.php подключается на каждой странице, проверяет URL и, если это карточка CRM, подключает JavaScript
-
script.js находит контейнер с вкладками (
.main-buttons-inner-container) и добавляет свою кнопку -
При клике на вкладку открывается стандартный слайдер Битрикс24
-
В слайдере загружается ваш PHP-файл с переданными параметрами (ID сущности и тип)
Преимущества подхода
| REST API | Наш подход |
|---|---|
| Запросы к облачным серверам Битрикс24 | Всё на вашем сервере |
| Ограничения API по количеству вызовов | Нет ограничений |
| Необходимость регистрировать приложение | 2 файла и готово |
| Зависимость от версии API | Работает в любой коробке |
| Медленная загрузка из-за iframe | Мгновенное открытие слайдера |
Возможные проблемы и решения
Вкладка не появляется
-
Очистите кэш Битрикс24:
Администрирование → Настройки продукта → Автокэширование → Очистка файлов кэша -
Проверьте права на файлы (644 для файлов, 755 для папок)
-
Откройте консоль браузера (F12) и проверьте наличие ошибок
Слайдер не открывается
Убедитесь, что ваш PHP-файл доступен по прямой ссылке:
http://ваш_портал/local/custom_content.php?entity_type_id=3&entity_id=123
Не передаются параметры
Проверьте, что в script.js в функции openSlider() параметры правильно формируются в URL.
Заключение
Этот подход уже работает в боевых проектах. Он позволяет добавить любую функциональность в карточки CRM без использования REST API, без регистрации приложений и без зависимости от облачных серверов Битрикс24.
Весь код — в двух файлах, которые легко переносить между проектами и поддерживать. При обновлениях Битрикс24 ваша вкладка продолжит работать, так как мы не трогаем ядро и не копируем стандартные компоненты.
