📜 ⬆️ ⬇️

Integration of 1C: CRM and Asterisk using PHP-AGI and 1C web services


As IP PBX, we use Askozia - this is one of the distributions of the well-known Asterisk, I wrote about Askozia in this post.
As a CRM system, product 1C is used: Trade and Customer Relationship Management 8 in client-server mode. The question has long been ripe, is it possible to integrate a call to 1C: Asset system in Asterisk dialplan: a CRM system to perform any control actions and is it possible to manage IP PBX from 1C?

The scheme is the simplest - when an incoming call, ask 1C what to do with it, and if 1C answered, then execute the command or continue the standard execution of the call route.
Let's try to perform the simplest action - set the client's name by phone number from the CRM system and write this value to the Asterisk CDR.


Initial data

')
1C: Enterprise 8.2 (8.2.17.143)
“Trade and Customer Relationship Management” (CRM + UT)
About 10,000 counterparties.
DBMS MS SQL
IIS Web Server

PBX Askozia CFE 2.1:
Asterisk 1.8.4.4
PHP 4.4.9
AGI phpagi.php, v 2.14


Askozia already has a CallerID installation mechanism based on the “notebook” data in the Asterisk internal database. To implement this option, we used the “ SDK component of the 1C communication and Asterisk ”.

An example of a function to write data to AstDB:
//     Asterisk   //  (dbFamily, dbKey, dbValue)    =   ActionID = ();  = "";   .DBPut(dbFamily, dbKey, dbValue, ActionID, )   ; ;  ("   "); ;  ("  "); ;  ;  //  


An example of a function call, applicable to the current task:
 // dbFamily - “cidname” //  “ ” // dbKey -     ,   ,    // dbValue -     ("cidname", “74952293042”, “OOO MIKO”); 


This is the first implementation. We launched it in work in our organization. After synchronization of contacts on all office phones, when you call, you can see with whom we are talking (of course, if there is a number in the database).
Of course, CallerID can only be seen on phones with a display :).

Disadvantages:
Periodic data synchronization between AstDB and 1C is required. Data is becoming obsolete.
Askozia - ReadOnly system. This is its advantage and disadvantage. After the AstDB reboot, it is listed in the “Initial” state - before recording the list of clients from 1C.

Due to these shortcomings, we abandoned this implementation option. But for naked Asterisk, this option is quite working.

The option of online interaction through AGI and Web services 1C: Enterprise 8


Create a web service 1C


When a call is received to our external number in the route of a call to Asterisk, it is possible to call an AGI script. In the script, refer to the 1C web service and set the CallerID (name) value.

Developed a simple web service 1C:
Name - MIKO_identify_number


namespace: wiki.miko.ru/doc : 1cajam: identifynumber
publication file name: 1C_MIKO_identify_number.1cws


The service will have only one simple operation (function)


operation properties:


contains one input parameter “Number”, the caller’s client number will be transmitted to it.

parameter type and return value “ string (http://www.w3.org/2001/XMLSchema) ”

We publish a web service (a separate topic on how to do this, we will not dwell on it, you can read here ). After publishing, we check the service operation using a web browser, open the wsdl description by reference:

IP_WEB_SERVER_1C/TestComponenta/ws/1C_MIKO_identify_number.1cws?wsdl

If everything is correct, the result will be as follows:


In the module of the service object, we write the simplest operation handler:
  identify(Number)  = "";  (Number);  //     , //         (Number)  = "OOO MIKO " + Number+ "";  ;  


Take cURL and check the work of our WEB service "head on."

In general, the command will look like this:
curl --header <> -d '<XML >' < >

Sending request:
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>' IP_WEB_SERVER_1C/TestComponenta/ws/1C_MIKO_identify_number.1cws


In this example, authentication is not used (in 1C there is no user).

Sample server response:
 <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> 


Works!

Create an AGI script


Unfortunately, Askozia has a very primitive PHP feature, there is not even a cURL extension, and PHP also uses the rather old 4.4.9 version. Nevertheless, we tried to overcome all restrictions, so please do not swear strongly at the code described below.

Expand code
 #!/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 } ?>​ 



Let's connect the script to Askozia, for this we will go to the application tab in the web interface and add a new PHP application.


In the “Application Logic” field, set the script text.
Note “#! / Usr / bin / php -f” - the line should be omitted. Askozia will finish it herself. Set the name and number for the application.
Now you need to know the name of the script file. To do this, the Asterisk command console (CLI) executes the command:

 # 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. =- 


We found out that in the number 1334444 the context DIALPLAN-APPLICATION-176239923050fac4c5678b9 is called

Let's display this context in order to understand the name of our PHP file:
 # 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. =- 


File found by: DIALPLAN-APPLICATION-176239923050fac4c5678b9.php

Let us proceed to editing the incoming call route and add a block of the universal command to the beginning of the route:


in the command field set AGI (DIALPLAN-APPLICATION-176239923050fac4c5678b9.php)
In summary, my call route looks like this:


When an incoming call is sent to this route, an AGI script is executed with a call to the 1C server, the server returns the name of the counterparty to the transferred number, and this name fits into the CallerID field.

As a result, before the introduction of the story looked like this:


After launching the web service, the names of clients from 1C: CRM systems appeared in the history


In addition, all Askozia notifications about a missed call or a received fax message to an e-mail became more informative, the title of the letter contains the name of the client, before it was just a number.


Conclusion

This simple example allows you to demonstrate the possibility of online integration of IP PBX and 1C, which of course is not limited to setting the client name. We can influence the call routing, send a call, bypassing the IVR immediately to the manager installed in the client’s card, play various advertising messages depending on the type of client’s activity and much, much more.

Useful links:

Source: https://habr.com/ru/post/166935/


All Articles