Создание REST aсtivity (действий) Битрикс24 с приложением-встройкой для препроцессинга параметров

от автора

Действие или 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)

  1. Создадим 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                     }             } };
  1. Добавим локальное приложение на портал, укажем ссылку на index.php, настроим права:

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

    Успешно установлено!
    Успешно установлено!

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

    Активити доступна, но в настройках будет ошибка
    Активити доступна, но в настройках будет ошибка

  2. Напишем код настроек параметров, ключевой момент — принять параметры и обновить их после сохранения настроек, изменение кода не требуется:

<?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}                         )
  1. Смотрим карточку настроек:

    Заполняем параметры, сохраняем.  Результат кладём в строковое множественное поле для демонстрации
    Заполняем параметры, сохраняем. Результат кладём в строковое множественное поле для демонстрации
  2. Теперь переходим в 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); }
  1. Вызываем бизнес-процесс, смотрим результат:

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


ссылка на оригинал статьи https://habr.com/ru/post/694874/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *