Все началось с того, что мои друзья попросили подобрать CRM-систему для менеджеров оптовой компании. Мы потратили уйму времени на поиски, но ничего подходящего не нашли. В итоге я предложил написать написать свою CRM. Удивительно, но готовый продукт внедрили сразу, без долгой «раскачки». Система работает и выложена в открытом доступе. В этой статье я расскажу, как ее сделал, — возможно, кому-нибудь пригодится.

Видео с демонстрацией возможностей


Для начала посмотрите ролик, мы сняли его для менеджеров. Показали, как работает CRM и какие функции в ней реализованы.




Инициация исходящего звонка


image

Исходящий звонок клиенту инициировали с помощью Python AMI Client и
Asterisk Manager Interface (AMI)
Что сделали:
  1. На Астериске прописали логин и пароль для AMI в файле /etc/asterisk/manager.conf
  2. Перезапустили Астериск и открыли на нем порт 5038 для соединения с нашим CRM-сайтом
  3. На сервере с CRM поставили AMI Client командой:
    pip install asterisk-ami

  4. Убедились, что все звонит с помощью этого кода:
    from asterisk.ami import SimpleAction
    client = AMIClient(address='your-remote.freepbx.net',port=5038)
    client.login(username='username',secret='password')
    action = SimpleAction(
        'Originate',
        Channel='SIP/974957777777',  # Внешний городской номер, на который надо позвонить
        Exten='2010',   # Инициатор вызова, внутренний номер на Астериске
        Priority=1,
        Context='default',
        CallerID='python',
    )
    client.send_action(action)
    



Затем прикрутили результат к нашему сайту на Flask, в итоге с сайта можно звонить нажатием кнопки на мышке. Работает так: менеджер кликает и у него звонит SIP-телефон. Когда он берет трубку — начинается вызов клиенту.
Астерисков у ребят два: один основной, другой — резервный, на случай выхода из строя основного. Поэтому в файле aster.py я прописал проверку на доступность с помощью socket и try.

Прием входящего звонка


image

Когда у менеджера звонит SIP-телефон, CRM показывает ему окно, как на скриншоте. Пробовали разные варианты, но написали в итоге на Perl, с помощью модуля Asterisk::AMI и EV.

use EV;
use Asterisk::AMI;

#Create your connection
my $astman = Asterisk::AMI->new(
    PeerAddr => '127.0.0.1',
    PeerPort => '5038',
    Username => 'admin',
    Secret   => 'supersecret',
    Events   => 'on',
    Handlers => { default => \&eventhandler }
);

#Alternatively you can set Blocking => 0, and set an on_error sub to catch connection errors
die "Unable to connect to asterisk" unless ($astman);

#Define the subroutines for events
sub eventhandler { my ( $ami, $event ) = @_; print 'Got Event: ', $event->{'Event'}, "\r\n"; }

#Define a subroutine for your action callback
sub actioncb { my ( $ami, $response ) = @_; print 'Got Action Reponse: ', $response->{'Response'}, "\r\n"; }

#Send an action
my $action = $astman->( { Action => 'Ping' }, \&actioncb );

#Do all of you other eventy stuff here, or before all this stuff, whichever ..............

#Start our loop
EV::loop

Кому интересно, почитайте статью о событийно-ориентированном программировании — мне она помогла.
Идем дальше. На гитхабе лежит файл с названием amievent.pl. С его помощью происходит отлов входящих звонков, занесение в memcached и в mysql базу.
На сервере у нас стоит Centos 7.
Мы демонизировали этот процесс с помощью systemd:


[Unit]
Description=Crm dancer event incomming call
After=mysql.service

[Service]
Type=simple
User=crmdancer
Group=crmdancer
Restart=always
RestartSec=30
ExecStart=/home/crmdancer/crmdancer/amievent.pl


[Install]
WantedBy=multi-user.target


Работа с таблицей клиентов


image

Для таблицы с клиентами использовали DataTables.
Этот jquery-плагин делает из обычной html-таблицы таблицу с поиском, сортировкой, фильтрами, разбиением больших таблиц на страницы и другими полезными функциями.
Это работало с небольшим количеством клиентов. Как только импортировали большую базу, увеличилось время открытия страницы, а общий размер html-таблицы составил 12 000 строк.

Загуглили и нашли в документации следующее:
Обработка на стороне клиента с помощью DOM: ~5'000 строк
Обработка на стороне клиента с загрузкой данных через Ajax: ~50'000 строк
Обработка на стороне сервера — миллион строк

Переделали загрузку на Ajax. Загрузка страницы с 4-х секунд упала до >1 секунды.

Записи звонков


image

У нас не было технической возможности сжимать записи в mp3 на FreePBX.
Поэтому сделали rsync с FreePBX на сайт с CRM.
Принцип такой: файлы перебрасываются в wav формате, потом конвертируются в mp3. Конвертируем с помощью lame и perl-скрипта convertor.pl — он находит wav файлы, которые еще не сконвертированны. Файл length.pl создает csv-файлы в формате «название mp3-файла — время звучания». Для каждого дня свой csv-файл.

Календарь событий


image

Чтобы менеджерам было удобно вести план работ, записывать предстоящие дела и события, мы установили jquery-плагин fullcalendar. К нему дописали возможность хранения событий в mysql и цветовую идентификацию событий.
Как это реализовано можно посмотреть в коде на гитхабе.

Про темплейт


При верстке использовали замечательный темплейт админки:
https://github.com/puikinsh/gentelella
Очень красивый и удобный. Здесь демо-версия:
https://colorlib.com/polygon/gentelella/index.html
Единственный минус — меню слева по умолчанию развернуто. Чтобы получить дополнительное пространства, свернули вот так:
<body class="nav-md"> заменили на  <body class="nav-sm">

Других изменений не вносили, все работает из коробки.

CRMDANCER — установка и работа



  • Ставим mysql и memcached
  • Устанавливаем python 3.x
  • Ставим виртуальное окружение
  • Устанавливаем flask
  • Копируем код с гитхаба
  • Ставим все необходимые модули
  • Создаем базу данных с готовой структурой, код sql на гитхабе
  • Правим конфиги и прописываем свои логины и пароли
  • Запускаем


P.S.


Бывает пишешь что-нибудь, используешь передовые технологии, а в итоге никто продуктом не пользуется. У нас были подобные истории, поэтому когда писали Crm Dancer, воспринимали его как прототип, не думали, что менеджерам понравится и они будут им пользоваться. Из-за этого использовали обычный ajax вместо websocket и признаем — в других местах можно было бы написать лучше.
В остальном все в порядке, скачивайте и пользуйтесь.
Поделиться с друзьями
-->

Комментарии (1)


  1. pcdesign
    27.02.2017 12:21

    Давно хотел для своих «акульих» дел crm систему. Попробую ее использовать. Спасибо.