К нам обратилась компания Твой Знакомый Юрист” с целью внедрения сервиса, который позволял бы их клиентам самостоятельно отслеживать статусы своих дел по аналогии с отслеживанием доставки писем в почте России.

Компания уже пользовалась crm Bitrix24, поэтому было решено брать информацию прямо из crm. Однако из-за тарифа “стандартный”, воспользоваться бизнес-процессами было невозможно.

Мы определили такой алгоритм решения задачи:

1. Создаем отдельную страницу на сайте компании,
2. В этой странице клиенты заполняют поле с номером телефона,
3. После заполнения поля отправляется запрос в Bitrix24,
4. Bitrix24 получает этот запрос и отправляет отфильтрованные данные,
5. Клиент на сайте видит нужные данные о статусе своей сделки.

Разберем реализацию подробнее.

Для отслеживания клиентами статуса своих сделок в Bitrix24 нужно:

  1. Создать пользовательское поле в Bitrix24 типа “строка”
  2. На каждую стадию сделки добавить и настроить робота “Изменить элемент”
  3. Создать отдельный файл формы с разрешением .php
  4. Стили для файла формы с разрешением .css
  5. Backend для получения информации с Bitrix 24
  6. JS скрипт для реализации вывода на видимую часть для пользователей без перезагрузки страницы
  7. Размещение на сайте

Теперь подробно разберем каждый шаг.

Создаем пользовательское поле в Bitrix24 типа “строка”

Первым делом в карточке сделок нужно создать пользовательское поле с помощью кнопки “Создать поле“. Так же создать поле можно через меню настроек:
(Настройки > Настройки CRM > Настройки форм и отчетов > Пользовательские поля)

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

На каждую стадию сделки добавить и настроить робота “Изменить элемент”

Заполняют данное поле роботы (Изменить элемент), в зависимости от стадии, содержание поля будет разным

Создаем отдельный файл формы с разрешением .php

Как создавать и редактировать подобные файлы читайте в статье на нашем сайте.

1) Создадим файл с расширением .php и html разметку в нём

Добавим поле ввода для клиентов, и кнопку для получения информации

<div class="title-form"> Узнайте статус своего дела</div>
<div class="phone-text-form"> Для этого введите номер телефона, <br>указанный в договоре с нами</div>
<form class="form-active" action='b24.php' method='post'>
    <input class="phone-number-text" type="text" id="phone">
    <button class="phone-number-button"type="button" id="button">
        Узнать статус дела
    </button>
</form>
<div id="area" class="area-form"></div>
</form>

Подключаем Jquery и наш файл скриптов

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src="scripts.js"></script>

Стилизуем нашу форму

Создаем файл с разрешением .css и подключаем к форме

<link rel="stylesheet" href="style.css">

Не забываем изначально сбросить стандартные стили, я использовал github.com/necolas/normalize.css   

Backend для получения информации с Bitrix 24

Создаем файл с расширением .php (к примеру b24.php)

Для получения информации с Битрикс 24 нам понадобятся вебхуки. Как их создавать и использовать вы можете узнать в нашей статье (статья о вебхуках).

Основная задача –  найти всех клиентов по номеру телефона, и уже по данным клиента получить его сделки. Будем использовать следующие методы REST (ссылка на документацию):

crm.duplicate.findbycomm – для поиска id контакта по номеру (в нашем случае контакт у номера всегда один)

crm.deal.list (можно сделать ссылку на метод в документации rest битрикс24) – для поиска сделок по id контакта

Получаем данные:

Используем curl запрос с 2 вебхуками (сначала crm.duplicate.findbycomm, затем crm.deal.list), на выходе будет список сделок, отфильтрованный по номеру клиента, в качестве аргумента будет переменная (номер который ввёл клиент) полученная с JS скрипта Ajax’ом

$curl = curl_init();

    curl_setopt_array(
        $curl,
        [
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_POST => true,
            CURLOPT_HEADER => false,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_URL => $queryUrl,
            CURLOPT_POSTFIELDS => http_build_query($queryData)
        ]
    );

Используем фильтры

$queryData = [
        'type' => 'PHONE',
        'values' => $phone,
        'entity_type' => 'CONTACT'
];
$queryData = [
        'filter' => [
            'CONTACT_ID' => $contactId,
            'CATEGORY_ID' => '0' //Фильтр по воронке
        ],
        'select' => ['UF_*'] //отображение пользовательских полей
];

Итоговый код функции

function get_deal($phoneForm)
{
    $queryUrl = ''; //в '' url вебхука который ищет ID контакта по по номеру

    $phone = [
        $phoneForm
    ];

    $queryData = [
        'type' => 'PHONE',
        'values' => $phone,
        'entity_type' => 'CONTACT'
    ];

    $curl = curl_init();

    curl_setopt_array(
        $curl,
        [
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_POST => true,
            CURLOPT_HEADER => false,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_URL => $queryUrl,
            CURLOPT_POSTFIELDS => http_build_query($queryData)
        ]
    );

    $contactId = curl_exec($curl);
    curl_close($curl);

    $contactId = strstr($contactId, '[');
    $contactId = strstr($contactId, ']', true);
    $contactId = trim($contactId, '['); //срезаем лишнее

    $queryUrl = ''; //в '' url вебхука который ищет сделки по контакту


    $queryData = [
        'filter' => [
            'CONTACT_ID' => $contactId,
            'CATEGORY_ID' => '0' //Воронка Защита прав потребителей
        ],
        'select' => ['UF_*'] //отображение пользовательских полей
    ];

    $curl = curl_init();

    curl_setopt_array(
        $curl,
        [
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_POST => true,
            CURLOPT_HEADER => false,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_URL => $queryUrl,
            CURLOPT_POSTFIELDS => http_build_query($queryData)
        ]
    );

    $deal = curl_exec($curl);
    curl_close($curl);

    return $deal;
}

Информация по текстовым полям приходит из Б24 в виде юникода, необходимо преобразить в читабельный вариант.

Пример полученных данных с поля в Б24: “SOURCE_DESCRIPTION”:”\u041d\u0430\u0432\u044f\u0437\u0430\u043b\u0438 \u043f\u043e\u0440\u0443\u0447\u0438\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u043e \u041e\u041e\u041e \u0411\u0420\u041e\u041a\u0415\u0420 \u043f\u043e \u043a\u0440\u0435\u0434\u0438\u0442\u0443 \u0432 \u0411\u044b\u0441\u0442\u0440\u043e\u0411\u0430\u043d\u043a\u0435 \u043d\u0430 \u0441\u0443\u043c\u0443 107 \u0442\u044b\u0441\u044f\u0447

Для этого будем использовать функции раскодировки

function replace_unicode_escape_sequence($match) {
    return mb_convert_encoding(pack('H*', $match[1]), 'UTF-8', 'UCS-2BE');
} //Функции раскодировки

function unicode_decode($str) {
    return preg_replace_callback('/\\\\u([0-9a-f]{4})/i', 'replace_unicode_escape_sequence', $str);
}

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

Преобразуем строку с REST в массив, разделитель будет ‘UF_CRM_’.

$text = explode('UF_CRM_', $result);

Получаем нужные поля и смотрим их наличие

Фильтровать будем по ID (как его получить), в данном случае ID нужного нам поля 1653490399007

foreach ($text as $elem) {
        $check = stripos($elem,'1653490399007'); //пользовательское поле с б24
        if ($check === 0){ //Проверка существование поля
            $elem = str_replace('1653490399007\":\"', '' ,$elem);
            $elem = unicode_decode($elem); //Раскодируем
            $elem = str_replace($unnchars, '', $elem);
            $elem = stripslashes($elem);
            $respondent[] = 'По делу с ' . trim($elem) . ':' . '.*_'; 
            // .*_ - разделитель для JS скрипта
        }
}

В дальнейшем полученную информацию передаем JS скрипту, который выведет данные клиенту

JS скрипт

Скрипт у нас будет отдельным файлом в разрешении .js

Основная задача – вывести информацию, полученную с backend клиенту.

Создадим функцию, которая создаст div’ы, в случае пустоты в запросах
(информация с поля равна null (строковое значение)), выведем соответствующее сообщение, что информация не найдена.

Связь между JS и PHP будем выполнять с помощью Ajax.

function getData(){
    let phone = document.getElementById('phone').value;

    $.ajax({ //запрос на сервер для получения данных с б24
        type: "GET",
        url: '', //в '' название файла из шага 3, например b24.php
        data: {value: phone},
        success: function (data) {
            dataarr = data.split('.*_');
            let index = 0;
            dataarr.pop();
            clear(document.querySelectorAll('.element-form'));

            index = 0;

            let result = dataarr.filter((_, index)=>index % 2 !== 0).concat(dataarr.filter((_, index)=>index % 2 === 0));
            let deal = result.slice(result.length / 2);
            let status = result.slice(0, result.length / 2);

            let nothingFound = true;
                deal.forEach((element) => { //обход массива и создание div
                    if (status[index].indexOf('null') === -1 && deal[index].indexOf('null') === -1) {
                        let area = document.getElementById('area');
                        let newDeal = document.createElement('div');
                        newDeal.className = 'element-form';
                        area.appendChild(newDeal);
                        newDeal.textContent = element;
                        let newStatus = document.createElement('div');
                        newStatus.className = 'element-form';
                        area.appendChild(newStatus);
                        newStatus.textContent = status[index];
                        nothingFound = false;
                    }
                    index++
                })

            if (nothingFound) {
                let newDeal = document.createElement('div');
                let area = document.getElementById('area');
                newDeal.className = 'element-form';
                area.appendChild(newDeal);
                newDeal.textContent = 'Дела с таким номером телефона не найдены.  \n' +
                    'Попробуйте еще раз, проверив предварительно номер телефона: он должен совпадать с номером, указанным в договоре с нами';
            }
        }
    });
}

Необходимо назначить выполнение функции на клик мыши по кнопке, а так же на клавишу Enter.

let button = document.getElementById('button');


button.addEventListener('click', () => {
    getData();
    }
)

$(document).keypress(function (e) {
    if (e.which == 13) {
        e.preventDefault();
        getData();
    }
});

Не забываем создать функцию очистки в случае набора клиентом другого номера. Клиент может ошибиться в наборе, или ввести чужой.

Данная функция стирает ранее заполненные данные и добавляет новые.

function clear(elems) {
    if (elems.length > 0) {
        elems.forEach((clearelem) => {
            clearelem.remove();
        })
    }
}

Результат работы:

При совпадении набранного клиентом номера с номером контакта в существующей сделке выводим пользовательское поле

Обратная ситуация, когда информация по сделкам не найдена

Размещение на сайте

Для размещения на сайте нужно переместить файлы на сайт, пути разные, в зависимости от структуры и процесса работы с сайтом.

1) Это можно сделать через файловый менеджер в вашем хостинге.
2) С помощью FTP Клиента FileZilla, предварительно соединившись по данным с вашего хостинга.
3) С помощью вкладки Deployment в IDE, но данный вариант уже требует соответствующих технических знаний для настройки.

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

Если у вас есть какие-то вопросы, свяжитесь с нами.