В качестве IP АТС мы используем Askozia — это один из дистрибутивов широко известного Asterisk, про Askozia я писал в этом посте.
В качестве CRM системы используется продукт 1С: Управление торговлей и взаимоотношениями с клиентами 8 в режиме клиент-сервер. Давно зрел вопрос, можно ли встроить в диалплан Asterisk обращение к 1С:CRM системе для выполнения каких-либо управляющих действий и можно ли из 1С управлять IP АТС?
Схема работы простейшая — при входящем звонке спросить у 1С что с ним делать, и если 1С ответила, то выполнить команду или продолжить стандартное выполнение маршрута вызова.
Попробуем выполнить простейшее действие — установить название клиента по номеру телефона из CRM системы и записать это значение в CDR базу Asterisk.
Исходные данные
1С: Предприятие 8.2 (8.2.17.143)
“Управление торговлей и взаимоотношениями с клиентами” ( CRM + УТ )
Около 10000 контрагентов.
СУБД MS SQL
Web сервер IISАТС Askozia CFE 2.1:
Asterisk 1.8.4.4
PHP 4.4.9
AGI phpagi.php,v 2.14
В Askozia уже имеется механизм установки CallerID на основании данных “записной книжки” во внутренней базе данных Asterisk. Для реализации этого варианта мы использовали “SDK компонента связи 1С и Asterisk”.
Пример функции, позволяющей записать данные в AstDB:
// Записывает в базу данных Asterisk контактную информацию // Функция ЗаписатьПараметрВБД(dbFamily, dbKey, dbValue) Если НЕ Компонент = Неопределено Тогда ActionID = Строка(УИД); Результат = ""; Попытка Если Компонент.DBPut(dbFamily, dbKey, dbValue, ActionID, Результат) Тогда Возврат Истина; КонецЕсли; Исключение Сообщить("Ошибка при попытке записиконтакта"); КонецПопытки; Иначе Сообщить("Компонент не подключен"); КонецЕсли; Возврат Ложь; КонецФункции // ЗаписатьПараметрВБД
Пример вызова функции, применимо к текущей задаче:
// dbFamily - “cidname” // наша “записная книжка” // dbKey - Ключ переменной помещаемой в семейство, номер телефона строкой, содержит только цифры // dbValue - Имя контакта латинскими буквами ЗаписатьПараметрВБД("cidname", “74952293042”, “OOO MIKO”);
Это первый вариант реализации. Мы запустили его в работу в нашей организации. После синхронизации контактов на всех телефонах офиса при звонке видно с кем мы разговариваем (конечно, если есть номер в базе).
Разумеется CallerID возможно видеть только на телефонах с дисплеем :).
Недостатки:
Необходима периодическая синхронизация данных AstDB и 1С. Данные устаревают.
Askozia — ReadOnly система. В этом ее преимущество и недостаток. После перезагрузки AstDB приводится в “Исходное” состояние — до записи списка клиентов из 1С.
В силу перечисленных недостатков мы отказались от этого варианта реализации. Но для голого Asterisk этот вариант вполне рабочий.
Вариант онлайн взаимодействия через AGI и Web сервисы 1С: Предприятия 8
Создаем веб-сервис 1С
При поступлении звонка на наш внешний номер в маршруте вызова Asterisk возможно вызвать AGI скрипт. В скрипте обратиться к веб-сервису 1С и установить значение CallerID(name).
Разработан простой web сервис 1С:
Имя — MIKO_identify_number
пространство имен: wiki.miko.ru/doc:1cajam:identifynumber
имя файла публикации: 1C_MIKO_identify_number.1cws
У сервиса будет только одна простая операция (функция),
свойства операции:
содержит один входной параметр «Number», собственно в него и будет передаваться номер звонящего клиента.
тип параметра и возвращаемого значения “string (http://www.w3.org/2001/XMLSchema)”
Публикуем веб-сервис (отдельная тема, как это сделать, не будем на ней останавливаться, можно почитать здесь). После публикации проверим работу сервиса с помощью веб браузера, откроем wsdl описание по ссылке:
IP_WEB_SERVER_1C/TestComponenta/ws/1C_MIKO_identify_number.1cws?wsdl
Если все правильно, то результат будет таким:
В модуле объекта сервиса пропишем простейший обработчик операции:
Функция identify(Number) Результат = ""; Возврат ПолучитьИмяКонтатаПоНомеруТелефона(Number); КонецФункции // примитивный пример получения имени контакта, // реальный код зависит от конкретной информационной базы Функция ПолучитьИмяКонтатаПоНомеруТелефона(Number) ОтветСервера = "OOO MIKO " + Number+ ""; Возврат ОтветСервера; КонецФункции
Возьмем cURL и проверим работу нашего WEB сервиса «в лоб».
В общем случае команда будет выглядеть следующим образом:
curl --header <заголовки> -d '<XML структура>' <адрес сервиса>
Отправляем запрос:
curl --header "Content-Type: text/xml; charset=utf-8" -d '<?xml version="1.0" encoding="UTF-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><m:identify xmlns:m="http://wiki.miko.ru/doc:1cajam:identifynumber"><m:Number>74952293042</m:Number></m:identify></soap:Body></soap:Envelope>' http://IP_WEB_SERVER_1C/TestComponenta/ws/1C_MIKO_identify_number.1cws
В этом примере не используется аутентификация (в базе 1С нет пользователя).
Пример ответа сервера:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header/> <soap:Body> <m:identifyResponse xmlns:m="http://wiki.miko.ru/doc:1cajam:identifynumber"> <m:return xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">OOO MIKO 74952293042</m:return> </m:identifyResponse> </soap:Body> </soap:Envelope>
Работает!
Создаем AGI скрипт
К сожалению, в Askozia очень примитивные возможности PHP, нет даже cURL расширения, да и PHP используется достаточно древней 4.4.9 версии. Тем не менее, мы попробовали победить все ограничения, потому просьба сильно не ругаться на код описанные ниже.
#!/usr/bin/php -f <?php require('phpagi.php'); // получение переменной канала // AGI phpagi.php,v 2.14 get_variable содержит только один параметр и возвращает массив $start_found = false; $end_found = false; $ret_value = ''; function tagStart($parse, $name, $attribs){ global $start_found; if($name == 'M:RETURN'){ // echo($name); $start_found = true; } } function tagEnd($parser, $name){ global $start_found; global $end_found; if($name == 'M:RETURN'){ $end_found = true; } } function dataGet($parser, $data){ global $start_found; global $ret_value; global $end_found; if($start_found&&!$end_found){ $ret_value = ''.$data; } } function parse_response($response){ // создаем xml парсер $xml_parse = xml_parser_create(); xml_set_element_handler($xml_parse, 'tagStart', 'tagEnd'); xml_set_character_data_handler ($xml_parse, 'dataGet'); xml_parse($xml_parse, $response); // освобождаем память, занятую парсером xml_parser_free($xml_parse); } function GetVarChannnel($agi, $_varName){ $v = $agi->get_variable($_varName); if(!$v['result'] == 0){ $agi->verbose($_varName.' ---> '.$v['data'], 10); return $v['data']; } else{ $agi->verbose($_varName.' not set', 10); return ""; } } // GetVarChannnel($_agi, $_varName) // объект класса agi, его описание содержит файл phpagi.php $agi = new AGI(); // базовые переменные, параметры подключения к сервису $path = '/TestComponenta/ws/1C_MIKO_identify_number.1cws'; $server = 'IP_WEB_SERVER_1C'; $port = 80; $number = GetVarChannnel($agi, "CALLERID(num)");; $user_1c= "1C_USERNAME"; $pass_1c= '1C_PASSWORD'; $auth = base64_encode($user_1c.':'.$pass_1c); $crlf = "\r\n"; // данные для передачи - непосредственно сам SOAP запрос $xmlDocument = ( '<?xml version="1.0" encoding="UTF-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <m:identify xmlns:m="http://wiki.miko.ru/doc:1cajam:identifynumber"> <m:Number>'.$number.'</m:Number> </m:identify> </soap:Body> </soap:Envelope>'); $contentLength = strlen($xmlDocument); // создаем сокет if (($http_soket = @fsockopen($server, $port, $errno, $errstr,1.5)) == false) return; $query = "POST $path HTTP/1.1" .$crlf; $query .= "Host: $server" .$crlf; $query .= "Content-Type: text/xml; charset=utf-8" .$crlf; $query .= "Authorization: Basic $auth" .$crlf; $query .= "Content-Length: $contentLength" .$crlf; $query .= $crlf; $query .= $xmlDocument; // устанавливаем таймаут на поток 1секунда stream_set_timeout($http_soket, 1, 0); // отправляем запрос fputs($http_soket, $query); $result = ''; // считываем ответ while ($line = fgets($http_soket)) $result .= $line; // отсекаем заголовки ответа $result = substr($result, strpos($result, $crlf.$crlf) + 4); fclose($http_soket); // разбор ответа parse_response($result); if($ret_value != ''){ $agi->set_variable('CALLERID(name)', $ret_value); // Если 1С вернула значение, то записываем это значение в таблицу CDR } ?>
Подключим скрипт к Askozia, для этого в веб -интерфейсе перейдем на закладку приложения и добавим новое PHP приложение.
В поле “Логика приложения” устанавливаем текст скрипта.
Обратите внимание “#!/usr/bin/php -f” — строка должна быть опущена. Askozia сама ее допишет. Устанавливаем имя и номер для приложения.
Теперь необходимо узнать имя файла скрипта. Для этого командной консоли (CLI) Asterisk выполним команду:
# dialplan show 1334444@internal [ Context 'internal' created by 'pbx_config' ] '1334444' => 1. NoOp(internal calling application: AGI_App_CallerID) [pbx_config] 2. Set(CDR(InternalCalleridNum)=1334444) [pbx_config] 3. Goto(DIALPLAN-APPLICATION-176239923050fac4c5678b9,${EXTEN},1) [pbx_config] -= 1 extension (3 priorities) in 1 context. =-
Мы выяснили, что в номере 1334444 вызывается контекст DIALPLAN-APPLICATION-176239923050fac4c5678b9
Выведем этот контекст, чтобы понять как называется наш PHP файл:
# dialplan show DIALPLAN-APPLICATION-176239923050fac4c5678b9 [ Context 'DIALPLAN-APPLICATION-176239923050fac4c5678b9' created by 'pbx_config' ] 'h' => 1. Hangup() 's' => 1. AGI(DIALPLAN-APPLICATION-176239923050fac4c5678b9.php) 2. Hangup() '_[0-9a-zA-Z*#]!' =>1. AGI(DIALPLAN-APPLICATION-176239923050fac4c5678b9.php) 2. Hangup() -= 3 extensions (5 priorities) in 1 context. =-
Искомый файл найден: DIALPLAN-APPLICATION-176239923050fac4c5678b9.php
Перейдем к редактированию входящего маршрута вызова и добавим в начало маршрута блок универсальной команды:
в поле команды установить AGI(DIALPLAN-APPLICATION-176239923050fac4c5678b9.php)
в итоге мой маршрут вызова выглядеть следующим образом:
При входящем звонке, направленном на этот маршрут, происходит выполнение AGI скрипта с обращением к серверу 1С, сервер по переданному номеру возвращает название контрагента, и это название вписывается в поле CallerID.
В результате до внедрения история выглядела так:
После запуска web-сервиса в истории появились названия клиентов из 1С:CRM системы:
Помимо этого все нотификации Askozia о пропущенном звонке или полученном факсимильном сообщении на электронную почту стали более информативными, в заголовке письма есть название клиента, раньше был просто номер.
Заключение
Этот простой пример позволяет продемонстрировать возможность онлайн интеграции IP АТС и 1C, которая естественно не ограничивается только установкой имени клиента. Мы можем влиять на маршрутизацию звонка, отправлять звонок, минуя IVR сразу на менеджера, установленного в карточке клиента, воспроизводить различные рекламные сообщения в зависимости от вида деятельности клиента и многое-многое другое.
Полезные ссылки:
- Более подробно про Askozia можно почитать в этом посте или на сайте www.askozia.ru
- Про интеграцию 1С и Asterisk на сайте www.telefon1c.ru
- Про PHP-AGI можно почитать здесь phpagi.sourceforge.net
- Про веб-сервисы 1С на официальном сайте.
ссылка на оригинал статьи http://habrahabr.ru/post/166935/
Добавить комментарий