Как известно, Asterisk может хранить детализацию звонков, она же CDR (Call Detail Records). CDR может храниться разными способами, это и обычный csv-файл и практически любая база данных. У нас, например это обычная база на MySQL, с одной единственной табличкой — cdr. Была поставлена цель: написать скрипт который бы считал стоимость звонков.

Нам интересны поля:
Этап 2: Создаём свои таблицы.
price_russia – таблица с кодами регионов россии и ценами.
price_international – таблица с кодами стран и ценами.
clients — таблица с клиентами.
* поле rate позволяет давать скидку или ставить наценку для клиента (пишем 90 – даём скидку в 10 процентов, ставим 110 – получаем наценку в 10 процентов).
clients_ext – сопоставление клиентов и экстеншенов.
сalls – таблица с обработанными звонками.
*type – тип звонка (входящий/исходящий).
Предполагается что данный скрипт будет запускаться по расписанию, и что бы ничего не пропустить, в начале выполнения скрипта мы помечаем необработанные строки, для того что бы обрабатывать только их, и не трогать те которые могут появится пока выполняется скрипт:
Первое что мы делаем – это загружаем в массив информацию о:
1. Клиентах, их экстеншенах, группах и множителе.
2. Коды регионов, описание и цены по России.
3. Коды стран, описание и цены по миру.
Соответственно делаем мы вот что:
Теперь можем выбрать из базы, отмеченные строки и начать обработку.
Собственно обработка
Определяем направление звонка:
Теперь нам нужно определить экстеншен, по которому мы сможем определить клиента. Экстеншен можно извлечь из канала, в таблице cdr — это поля «channel» при исходящем вызове и «dstchannel» при входящим.
* знаю что тут лучше использовать регулярные выражения, но…
Теперь определяем какому клиенту принадлежит экстеншен.
и множитель
Округляем секунды до минут:
Если звонок внутренний или входящий, цену можно установить в ноль, а описание оставить пустым. А в противном случае нужно определить куда ушёл звонок.
Определяем цену звонка:
Теперь мы имеем всё что бы, записать информацию о звонке в таблицу.
И в самом конце, пометить строки как полностью в обработанные:
Вышенаписанное писалось под Москву, но легко может быть переделано под другой город или страну.
Ссылка на исходники: 77.108.85.102/habr/import.php.txt, 77.108.85.102/habr/functions.php.txt,
Этап 1: изучаем структуру таблицы cdr и что в ней хранится

Нам интересны поля:
- src- источник;
- dst – назначение;
- billsec – тарифицируемые секунды (секунды после снятия трубки);
- channel — используемый канал;
- dstchannel – канал направления;
- calldate – дата и время;
- uniqueid – уникальный идентификатор;
- disposition — что случилось с вызовом: ANSWERED, NO ANSWER, BUSY, FAILED.
- userfield – свободное поле.
Этап 2: Создаём свои таблицы.
price_russia – таблица с кодами регионов россии и ценами.
CREATE TABLE IF NOT EXISTS `price_russia ` ( `code` int(10) NOT NULL COMMENT 'Код области', `cost` varchar(10) NOT NULL COMMENT 'Цена', `region` varchar(100) NOT NULL COMMENT 'Регион', UNIQUE KEY `code` (`code`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
price_international – таблица с кодами стран и ценами.
CREATE TABLE IF NOT EXISTS ` price_international` ( `code` int(10) NOT NULL COMMENT 'Код страны', `price` varchar(10) NOT NULL COMMENT 'Цена', `country` varchar(100) NOT NULL COMMENT 'Страна', UNIQUE KEY `code` (`code`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
clients — таблица с клиентами.
CREATE TABLE IF NOT EXISTS `clients` ( `login` varchar(32) NOT NULL COMMENT 'Логин', `password` varchar(32) NOT NULL COMMENT 'Пароль', `email` varchar(40) NOT NULL COMMENT 'Email', `rate` smallint(4) NOT NULL COMMENT 'Множитель', UNIQUE KEY `login` (`login`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
* поле rate позволяет давать скидку или ставить наценку для клиента (пишем 90 – даём скидку в 10 процентов, ставим 110 – получаем наценку в 10 процентов).
clients_ext – сопоставление клиентов и экстеншенов.
CREATE TABLE IF NOT EXISTS `clients_ext` ( `login` varchar(32) NOT NULL, `ext` int(6) NOT NULL, UNIQUE KEY `ext` (`ext`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
сalls – таблица с обработанными звонками.
CREATE TABLE IF NOT EXISTS `calls` ( `uniqueid` varchar(32) NOT NULL, `date` datetime NOT NULL, `login` varchar(32) NOT NULL, `rate` bigint(10) NOT NULL, `ext` bigint(10) NOT NULL, `dst` bigint(20) NOT NULL, `src` bigint(20) NOT NULL, `type` varchar(20) NOT NULL, `minutes` int(10) NOT NULL, `seconds` int(100) NOT NULL, `cost` int(10) NOT NULL, `description` varchar(100) NOT NULL, UNIQUE KEY `uniqueid` (`uniqueid`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
*type – тип звонка (входящий/исходящий).
Этап 3: Обработка данных.
Предполагается что данный скрипт будет запускаться по расписанию, и что бы ничего не пропустить, в начале выполнения скрипта мы помечаем необработанные строки, для того что бы обрабатывать только их, и не трогать те которые могут появится пока выполняется скрипт:
mysql_query("UPDATE cdr SET userfield=`step1` WHERE userfield=``;");
Первое что мы делаем – это загружаем в массив информацию о:
1. Клиентах, их экстеншенах, группах и множителе.
function get_clients(){ // На выходе получаем массив с экстеншенами, кому они принадлежат и какой там множитель. $query=mysql_query("SELECT rate,ext,login FROM clients, clients_ext WHERE clients_ext.login = clients.login"); while($row=mysql_fetch_assoc($query)){ $clients[$row['ext']]['rate']=$row['rate']; $clients[$row['ext']]['user']=$row['login']; } return $clients; }
2. Коды регионов, описание и цены по России.
function get_price_russia(){ // Получаем массив с ценами по России $query=mysql_query("SELECT * FROM price_russia"); while($row=mysql_fetch_assoc($query)){ $price[$row['code']]['cost']=$row['price']; $price[$row['code']]['description']=$row['region']; } return $price; }
3. Коды стран, описание и цены по миру.
function get_price_international(){ //Получаем массив с ценами по миру. $query=mysql_query("SELECT * FROM price_international"); while($row=mysql_fetch_assoc($query)){ $price[$row['code']]['cost']=$row['price']; $price[$row['code']]['description']=$row['country']; } return $price; }
Соответственно делаем мы вот что:
$clients=get_clients(); $price['russia']=get_price_russia(); $price['international']=get_price_international();
Теперь можем выбрать из базы, отмеченные строки и начать обработку.
$query=mysql_query("SELECT * FROM cdr WHERE userfield=`step1`"); while($row=mysql_fetch_assoc($row)){ //обработка }
Собственно обработка
(перед первым вызовом функции я привожу это функцию, хотя естественно они должны располагаться или в начале файла или в соседнем файле.):Определяем направление звонка:
function get_call_type($dst,$src){ $dst=strlen($dst); $src=strlen($src); if($scr<7 && $dst<7)$type='internal'; //Внутренний вызов. if($src<7 && $dst>=7)$type='outcoming'; // Исходящий if($src>=7 && $dst<7)$type='incoming'; //Входящий return $type; } $type=get_call_type($row['dst'],$row['src']);
Теперь нам нужно определить экстеншен, по которому мы сможем определить клиента. Экстеншен можно извлечь из канала, в таблице cdr — это поля «channel» при исходящем вызове и «dstchannel» при входящим.
function get_ext_from_channel($channel){ // Достаём ext из channel $channel=split("/",$channel); $channel=split("-",$channel[1]); return($channel[0]); } switch ($type) { case 'internal': $ext=$row['src']; break; case 'incoming': $ext=get_ext_from_channel($row['dstchannel']); break; case 'outcoming': $ext=get_ext_from_channel($row['channel']); break; }
* знаю что тут лучше использовать регулярные выражения, но…
Теперь определяем какому клиенту принадлежит экстеншен.
$login=$clients[$ext]['login']
и множитель
$rate=$clients[$ext]['rate']
Округляем секунды до минут:
$minutes=ceil($row['billsec']/60);
Если звонок внутренний или входящий, цену можно установить в ноль, а описание оставить пустым. А в противном случае нужно определить куда ушёл звонок.
if($type=='outcoming'){ //Определени звонка. }else{ $cost=0; $description=0; }
Определяем цену звонка:
//Дописываем 8495 если в номере только 7 цифр. function check_for_moscow($num){ if(strlen($num)==7)$num='8495'.$num; return $num; } $dst=check_for_moscow($row['dst']); //Теперь определим какой применять прайс, российский или международный. Мы применили в настройках атс стандартную схему: что вход на международку через 10. Поэтому номера в которых 11 и меньше цифр - российские, а больше - международные. function get_country_type($number){ if(strlen($number)<=11){ $return='russia'; }else{ $return='international'; } return $return; } $country=get_country_type($dst); $cost=''; $description=''; $i=1; //Если Россия - то код региона начинается со второга символа (8xxx), если международный то с 4 (810xxx) if($country=='international'){$s=3;}else{$s=1;} //В коде города от трёх цифр, кроме тех у которых код начинается с 9 - это сотовые. Международные коды 2-3 цифры. while($cost==''){ $code=substr($dst,$s,$i); $cost=$price[$country][$code]['cost']; $description=$price[$country][$code]['description']; $i++; }
Теперь мы имеем всё что бы, записать информацию о звонке в таблицу.
mysql_query("INSERT INTO calls(uniqueid, date, login, rate, ext, dst, src, type, minutes, seconds, cost, description) VALUES ('$row[uniqueid]', '$row[calldate]', '$login', '$rate', '$ext', '$dst', '$row[src]', '$type', '$minutes', '$row[billsec]', '$cost', '$description')");
И в самом конце, пометить строки как полностью в обработанные:
mysql_query("UPDATE cdr SET userfield=`done` WHERE userfield=`step1`");
Вышенаписанное писалось под Москву, но легко может быть переделано под другой город или страну.
Ссылка на исходники: 77.108.85.102/habr/import.php.txt, 77.108.85.102/habr/functions.php.txt,