Действие или activity в Битрикс24 – это составной элемент бизнес-процесса, которые имеет собственные параметры и выполняет какое-то действие.
В REST API присутствует метод “bizproc.activity.add”, он создает активити и может назначить файл с обработчиком для встраивания фрейма в интерфейс портала, что позволяет размещать «фронт» приложений в настройках вашего действия в качестве препроцессора (автоматизируем сложную логику, валидируем данные) или более удобного интерфейса (делаем списки с выбором из человекопонятных пунктов, вместо поиска id или символьного кода для внесения в поле).
Создадим действие для поиска элементов смарт процесса на определённом статусе с использованием REST, php, js. Также мы будем использовать vue.js, но это не необходимо, главное - реализуйте ajax запросы. И, разумеется, вам потребуется хостинг для размещения обработчиков с доступом по https.
Структура
Нам потребуется реализовать приложение из 3 файлов:
1. index.php – файл для установки и удаления активити;
index.php
<?php header('Content-Type: text/html; charset=UTF-8'); $protocol = $_SERVER['SERVER_PORT'] == '443' ? 'https' : 'http'; $host = explode(':', $_SERVER['HTTP_HOST']); $host = $host[0]; define('BP_APP_HANDLER', $protocol.'://'.$host.explode('?', $_SERVER['REQUEST_URI'])[0]); ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <script src="//api.bitrix24.com/api/v1/"></script> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> </head> <body> <h1 style="text-align: center;margin-bottom: 2rem;width: 100%">Активити оптим</h1> <div style="margin-left: 30px;max-width: 26rem;"> <div class="item"> <h3 style="text-align: center;">"Выборка элементов СП"</h3> <button class="btn btn-primary" style="margin-right: 8px;" onclick="installActivity();"><i class="bi bi-download"></i> Установить действие БП</button> <button class="btn btn-primary" onclick="uninstallActivity('getspel');"><i class="bi bi-x-square"></i> Удалить действие</button> </div> </div> <script type="text/javascript"> function installActivity() { var params = { 'CODE': 'getspel', //код, уникальный для портала 'HANDLER': 'https://example.com/example.app/handler.php',//ваш обработчик 'AUTH_USER_ID': 1, 'USE_SUBSCRIPTION': '', 'NAME': 'Получить элементы СП', 'USE_PLACEMENT': 'Y', 'PLACEMENT_HANDLER': 'https://example.com/example.app/setting.php',//ваш файл настроек 'DESCRIPTION': 'Принимает тип СП, категорию и стадию, выдаёт массив id элементов на стадии', 'PROPERTIES': { //здесь параметры, которые будут задаваться через setting, чтобы не отлавливать символьные коды руками 'typeSP': { 'Name': 'Тип СП', 'Type': 'string', 'Required': 'Y', 'Multiple': 'N' }, 'categoryID': { 'Name': 'Категория', 'Type': 'string', 'Required': 'Y', 'Multiple': 'N' }, 'statusID': { 'Name': 'Статус', 'Type': 'string', 'Required': 'Y', 'Multiple': 'N' }, 'sTypeSP': { 'Name': 'Тип СП', 'Type': 'string', 'Required': 'Y', 'Multiple': 'N' }, 'sCategoryID': { 'Name': 'Категория', 'Type': 'string', 'Required': 'Y', 'Multiple': 'N' }, 'sStatusID': { 'Name': 'Статус', 'Type': 'string', 'Required': 'Y', 'Multiple': 'N' } }, 'RETURN_PROPERTIES': { //вернём массив ID привязанных СП 'outputString': { 'Name': { 'ru': 'IDs', 'en': 'IDs' }, 'Type': 'string', 'Multiple': 'Y', 'Default': null } } }; BX24.callMethod( 'bizproc.activity.add', params, function(result) { if(result.error()) alert("Error: " + result.error()); else alert("Успешно: " + result.data()); } ); } function uninstallActivity(code) { let params = { 'CODE': code }; BX24.callMethod( 'bizproc.activity.delete', params, function(result) { if(result.error()) alert('Error: ' + result.error()); else alert("Успешно: " + result.data()); } ); } </script> </body> </html>
2. handler.php – обработчик, принимает параметры, возвращает результат;
handler.php
<?php $protocol = $_SERVER['SERVER_PORT'] == '443' ? 'https' : 'http'; $host = explode(':', $_SERVER['HTTP_HOST']); $host = $host[0]; define('BP_APP_HANDLER', $protocol.'://'.$host.$_SERVER['REQUEST_URI']); if (!empty($_REQUEST['workflow_id']))//добавим простые проверки - измените под себя { if (!empty($_REQUEST['properties']['typeSP'])){ $par = array( //сформируем параметры для выборки элементов нужного СП на выбранном статусе 'entityTypeId' => $_REQUEST['properties']['typeSP'], 'select' => ['id'], 'order' => null, 'filter' => ['categoryId' => $_REQUEST['properties']['categoryID'], 'stageId' => $_REQUEST['properties']['statusID']], ); //используем вебхук с правами на CRM, чтобы не отвлекаться на Crest - настраивайте под задачу $result = callB24Method('https://example.bitrix24.ru/rest/1/59i35rrrzqg0np/','crm.item.list', $par); //запрашиваем ID's элементов СП $arr = []; foreach($result['result']['items'] as $item){ //готовим простой массив $arr[] = $item['id']; } //берем авторизацию из пришедшего БП, добавляем массив, возвращаем в БП $params = array( "auth" => $_REQUEST['auth']["access_token"], "event_token" => $_REQUEST["event_token"], "log_message" => "Элементы получены", "return_values" => array( "outputString" => $arr, ) ); $r = callB24Method('https://example.bitrix24.ru/rest/','bizproc.event.send', $params); } } function callB24Method($bitrix, $method, $params){ //напишем функцию для отправки запросов через вебхук $c = curl_init($bitrix . $method . '.json'); curl_setopt($c, CURLOPT_RETURNTRANSFER, true); curl_setopt($c, CURLOPT_POST, true); curl_setopt($c, CURLOPT_POSTFIELDS, http_build_query($params)); $response = curl_exec($c); $response = json_decode($response, true); return $response; }
3. setting.php – фронтенд фрейма в настройках активити, наш препроцессор.
setting.php
<?php header('Content-Type: text/html; charset=UTF-8'); $protocol = $_SERVER['SERVER_PORT'] == '443' ? 'https' : 'http'; $host = explode(':', $_SERVER['HTTP_HOST']); $host = $host[0]; define('BP_APP_HANDLER', $protocol.'://'.$host.explode('?', $_SERVER['REQUEST_URI'])[0]); $obj = json_decode($_POST['PLACEMENT_OPTIONS']); //объект с параметрами, отрисуем сохранённые параметры $sp = $obj->current_values->sTypeSP; $category = $obj->current_values->sCategoryID; $status = $obj->current_values->sStatusID; ?> <!DOCTYPE html> <html lang="en" dir="ltr"> <head> <meta charset="utf-8"> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script src="//api.bitrix24.com/api/v1/"></script> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <style> .select { min-width: 25%; margin-bottom: 20px; padding-bottom: 3px; padding-top: 3px; } .label { font-size: 0.9rem; margin-top: 1rem; margin-left: 20rem; } </style> </head> <body> <!-- вёрстка может быть любой, главное - вызвать BX24.placement.call и сохранить новые параметры --> <div id="app" style="background-color:#f5f9f9; height: 250px;width: 853px;" > <div style="width: 635px;padding-right: 42px; float: right; display: inline-block;margin-top: 15px;"> <div> <select class="form-select select" v-model="typeSearch"> <option value="" disabled selected><?=$sp ?></option> <option v-for="type in types" v-bind:value="{ id: type.entityTypeId, name: type.title }"> {{ type.title }}</option> </select> </div> <div> <select class="form-select select" v-model="categorySearch"> <option value="" disabled selected><?=$category ?></option> <option v-for="category in categories" v-bind:value="{ id: category.id, name: category.name }"> {{ category.name }}</option> </select> </div> <div > <select class="form-select select" v-model="statusSearch"> <option value="" disabled selected><?=$status ?></option> <option v-for="status in statuses" v-bind:value="{ id: status.STATUS_ID, name: status.NAME }"> {{ status.NAME }}</option> </select> </div> </div> <div style="float: left; display: inline-block;margin-top: 15px;margin-left: 5rem;"> <div> <p style="font-size: 0.9rem;text-align: end;padding-top: 4px;">Выберите СП:</p> </div> <div> <p style="font-size: 0.9rem;text-align: end;margin-top: 29px;">Выберите воронку:</p> </div> <div> <p style="font-size: 0.9rem;text-align: end;margin-top: 29px;">Выберите статус:</p> </div> </div> </div> <script> let app = new Vue({ el: '#app', data: { types: [], categories: [], statuses: [], typeSearch: '', categorySearch: '', statusSearch: '' }, created: function() { //получаем список СП после отрисовки фрейма BX24.resizeWindow(853, 250); BX24.callMethod( 'crm.type.list', '', function(result) { if(result.error()) alert("Error: " + result.error()); else app.types = result.data().types; } ); }, watch: { typeSearch: function() { //выбрали СП - подгружаем его воронки BX24.callMethod( 'crm.category.list', {"entityTypeId": app.typeSearch.id}, function(result) { if(result.error()) alert("Error"); else app.categories = result.data().categories; } ); }, categorySearch: function() {//выбрали воронку - подгружаем статусы BX24.callMethod( 'crm.status.list', {'filter': { "ENTITY_ID": 'DYNAMIC_' + app.typeSearch.id + '_STAGE_' + app.categorySearch.id}}, function(result) { if(result.error()) alert("Error"); else app.statuses = result.data(); } ); }, statusSearch: function() { BX24.placement.call( //обновим параметры после заполнения каждого пункта 'setPropertyValue', {'typeSP': app.typeSearch.id, 'categoryID': app.categorySearch.id, 'statusID': app.statusSearch.id, 'sTypeSP': app.typeSearch.name, 'sCategoryID': app.categorySearch.name, 'sStatusID': app.statusSearch.name} ) } } }) </script> </body> </html>
Реализация на примере
Нам нужна активити, которая будет запускаться на сделке и возвращать элемент СП, находящийся на выбранной стадии, в виде массива с ID для перебора итератором. Элементы являются документами, которых очень много, СП разные, сценарии БП разные - решили делать активити с препроцессингом в настройках.
Рекомендуем скачать весь код, развернуть на сервере и следовать статье - мы осветим все ключевые моменты, требующие кастомизации примера (ссылки в index.php и handler.php)
Создадим index.php, вызываемый Б24. Файл в нашем случае содержит php проверку, html вёрстку (заголовок-кнопки) и js скрипт на кнопках установить/удалить. Ниже представлен объект с параметрами для bizproc.activity.add, обратите внимание на USE_PLACEMENT:
var params = { 'CODE': 'getspel', //код, уникальный для портала 'HANDLER': 'https:///example.com/example.app/handler.php',//ваш обработчик 'AUTH_USER_ID': 1, 'USE_SUBSCRIPTION': '', 'NAME': 'Получить элементы СП', 'USE_PLACEMENT': 'Y', 'PLACEMENT_HANDLER': 'example.com/example.app/setting.php',//ваш файл настроек 'DESCRIPTION': 'Принимает тип СП, категорию и стадию, выдаёт массив id элементов на стадии', 'PROPERTIES': { //здесь параметры, которые будут задаваться через setting, чтобы не отлавливать символьные коды руками 'typeSP': { 'Name': 'Тип СП', 'Type': 'string', 'Required': 'Y', 'Multiple': 'N' }, .... }, 'RETURN_PROPERTIES': { //вернём массив ID привязанных СП 'outputString': { 'Name': { 'ru': 'IDs', 'en': 'IDs' }, 'Type': 'string', 'Multiple': 'Y', 'Default': null } } };
Добавим локальное приложение на портал, укажем ссылку на index.php, настроим права:

Перейдем в приложение, попробуем установить (в фрейме приложения используются права пользователя, так что устанавливайте и настраивайте активити из под админа), в случае чего отработаем ошибки:

Успешно установлено! Посмотрим результат:

Активити доступна, но в настройках будет ошибка Напишем код настроек параметров, ключевой момент - принять параметры и обновить их после сохранения настроек, изменение кода не требуется:
<?php //объект с параметрами, отрисуем в вёрстке $obj = json_decode($_POST['PLACEMENT_OPTIONS']); $sp = $obj->current_values->sTypeSP; $category = $obj->current_values->sCategoryID; $status = $obj->current_values->sStatusID;
...Здесь вёрстка...
BX24.placement.call( //обновим параметры после заполнения полей 'setPropertyValue', {'typeSP': app.typeSearch.id, 'categoryID': app.categorySearch.id, 'statusID': app.statusSearch.id, 'sTypeSP': app.typeSearch.name, 'sCategoryID': app.categorySearch.name, 'sStatusID': app.statusSearch.name} )
Смотрим карточку настроек:

Заполняем параметры, сохраняем. Результат кладём в строковое множественное поле для демонстрации Теперь переходим в handler.php. Здесь потребуется заменить авторизацию или поместить свой вебхук с правами на CRM:
<?php //добавим простые проверки - измените под себя if (!empty($_REQUEST['workflow_id'])) { //сформируем параметры для выборки элементов нужного СП на выбранном статусе $par = array( 'entityTypeId' => $_REQUEST['properties']['typeSP'], 'select' => ['id'], 'order' => null, 'filter' => ['categoryId' => $_REQUEST['properties']['categoryID'],'stageId' => $_REQUEST['properties']['statusID']], ); //используем вебхук с правами на CRM, чтобы не отвлекаться на Crest - //настраивайте под задачу $result = callB24Method('https://example.bitrix24.ru/rest/1/59itd45y6rzqg0np/', 'crm.item.list', $par); //запрашиваем ID's элементов СП $arr = []; foreach($result['result']['items'] as $item){ //готовим простой массив $arr[] = $item['id']; } //берем авторизацию из пришедшего БП, добавляем массив, возвращаем в БП $params = array( "auth" => $_REQUEST['auth']["access_token"], "event_token" => $_REQUEST["event_token"], "log_message" => "Элементы получены", "return_values" => array("outputString" => $arr) ); $r = callB24Method('https://example.bitrix24.ru/rest/','bizproc.event.send',$params); }
Вызываем бизнес-процесс, смотрим результат:


На этом всё, благодарим за внимание! Будем признательны обратной связи в комментариях и постараемся ответить на вопросы.
