Browse Source

Merge pull request #191 from murtuzasaleh/12.0-connector-voicent-186

[ADD] connector_voicent
pull/194/head
Maxime Chambreuil 5 years ago
committed by GitHub
parent
commit
f82bea304a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 93
      connector_voicent/README.rst
  2. 3
      connector_voicent/__init__.py
  3. 29
      connector_voicent/__manifest__.py
  4. 17
      connector_voicent/data/ir_cron.xml
  5. 205
      connector_voicent/examples/voicent.py
  6. 6
      connector_voicent/models/__init__.py
  7. 79
      connector_voicent/models/backend_voicent.py
  8. 109
      connector_voicent/models/backend_voicent_call_line.py
  9. 18
      connector_voicent/models/backend_voicent_time_line.py
  10. 11
      connector_voicent/models/res_partner.py
  11. 6
      connector_voicent/readme/CONFIGURATION.rst
  12. 3
      connector_voicent/readme/CONTRIBUTORS.rst
  13. 1
      connector_voicent/readme/DESCRIPTION.rst
  14. 3
      connector_voicent/readme/ROADMAP.rst
  15. 6
      connector_voicent/readme/USAGE.rst
  16. 6
      connector_voicent/security/ir.model.access.csv
  17. BIN
      connector_voicent/static/description/icon.png
  18. 433
      connector_voicent/static/description/index.html
  19. 3
      connector_voicent/tests/__init__.py
  20. 37
      connector_voicent/tests/test_backend_voicent.py
  21. 118
      connector_voicent/views/backend_voicent.xml
  22. 76
      connector_voicent/views/backend_voicent_call_line.xml
  23. 15
      connector_voicent/views/res_partner.xml
  24. 2
      oca_dependencies.txt

93
connector_voicent/README.rst

@ -0,0 +1,93 @@
=================
Voicent Connector
=================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fconnector--telephony-lightgray.png?logo=github
:target: https://github.com/OCA/connector-telephony/tree/12.0/connector_voicent
:alt: OCA/connector-telephony
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/connector-telephony-12-0/connector-telephony-12-0-connector_voicent
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/228/12.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
This module allows you to connect Odoo with Voicent <https://www.voicent.com> and is meant to be extended to integrate Odoo records and processes with phone calls made by Voicent.
**Table of contents**
.. contents::
:local:
Usage
=====
To use this module, you need to:
#. Go to Connectors > Backends > Voicent Backends
#. Create a new Voicent Backend with the host and port
#. Create Call Lines to determine when (which stage in the process) calls are added to the queue
#. Create Time Line to determine when (what time) calls are made
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/connector-telephony/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/connector-telephony/issues/new?body=module:%20connector_voicent%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* Open Source Integrators
Contributors
~~~~~~~~~~~~
* Maxime Chambreuil <mchambreuil@opensourceintegrators.com>
* Youness MAAFI <youness.maafi@gmail.com>
* Murtuza Saleh <murtuza.saleh@serpentcs.com>
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
.. |maintainer-max3903| image:: https://github.com/max3903.png?size=40px
:target: https://github.com/max3903
:alt: max3903
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-max3903|
This module is part of the `OCA/connector-telephony <https://github.com/OCA/connector-telephony/tree/12.0/connector_voicent>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

3
connector_voicent/__init__.py

@ -0,0 +1,3 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import models

29
connector_voicent/__manifest__.py

@ -0,0 +1,29 @@
# Copyright (C) 2019 Open Source Integrators
# <https://www.opensourceintegrators.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'Voicent Connector',
'version': '12.0.1.0.0',
'category': 'Connector',
'license': 'AGPL-3',
'summary': """Connect Odoo with Voicent""",
"author": "Open Source Integrators, "
"Odoo Community Association (OCA)",
"website": "https://github.com/OCA/connector-telephony",
'depends': [
'connector',
],
'data': [
'security/ir.model.access.csv',
'data/ir_cron.xml',
'views/res_partner.xml',
'views/backend_voicent_call_line.xml',
'views/backend_voicent.xml',
],
'installable': True,
'maintainers': [
'max3903',
],
'development_status': 'Beta',
}

17
connector_voicent/data/ir_cron.xml

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="fetch_check_the_voicent_status" model="ir.cron">
<field name="name">Voicent: Update the next call</field>
<field name="model_id" ref="model_backend_voicent"/>
<field eval="True" name="active"/>
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">15</field>
<field name="interval_type">minutes</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
<field name="state">code</field>
<field name="code">model._run_update_next_call()</field>
</record>
</odoo>

205
connector_voicent/examples/voicent.py

@ -0,0 +1,205 @@
# Copyright (C) 2018 Voicent
# Copyright (C) 2019 Open Source Integrators
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
#
# Documentation available at https://voicent.com/developer/docs/camp-api/
import ast
import csv
import ntpath
import os
import requests
class Voicent():
def __init__(self, host="localhost", port="8155", callerid="000000000",
line="1"):
self.host_ = host
self.port_ = port
self.callerid_ = callerid
self.line_ = line
def postToGateway(self, urlstr, params, files=None):
url = "http://" + self.host_ + ":" + self.port_ + urlstr
res = requests.post(url, params, files=files)
return res.text
def getReqId(self, rcstr):
index1 = rcstr.find("[ReqId=")
if index1 == -1:
return ""
index1 += 7
index2 = rcstr.find("]", index1)
if index2 == -1:
return ""
return rcstr[index1:index2]
def callText(self, phoneno, text, selfdelete):
urlstr = "/ocall/callreqHandler.jsp"
params = {
'info': 'simple text call',
'phoneno': phoneno,
'firstocc': 10,
'txt': text,
'selfdelete': selfdelete
}
res = self.postToGateway(urlstr, params)
return self.getReqId(res)
def callAudio(self, phoneno, filename, selfdelete):
urlstr = "/ocall/callreqHandler.jsp"
params = {
'info': 'simple audio call',
'phoneno': phoneno,
'firstocc': 10,
'audiofile': filename,
'selfdelete': selfdelete
}
res = self.postToGateway(urlstr, params)
return self.getReqId(res)
def callIvr(self, phoneno, appname, selfdelete):
urlstr = "/ocall/callreqHandler.jsp"
params = {
'info': 'simple text call',
'phoneno': phoneno,
'firstocc': 10,
'startapp': appname,
'selfdelete': selfdelete
}
res = self.postToGateway(urlstr, params)
return self.getReqId(res)
def callStatus(self, reqid):
urlstr = "/ocall/callstatusHandler.jsp"
params = {'reqid': reqid}
res = self.postToGateway(urlstr, params)
return self.getReqId(res)
def callRemove(self, reqId):
urlstr = "/ocall/callremoveHandler.jsp"
params = {'reqid': reqId}
res = self.postToGateway(urlstr, params)
return self.getReqId(res)
def callTillConfirm(self, vcastexe, vocfile, wavfile, ccode):
urlstr = "/ocall/callreqHandler.jsp"
cmdline = "\""
cmdline += vocfile
cmdline += "\""
cmdline += " -startnow"
cmdline += " -confirmcode "
cmdline += ccode
cmdline += " -wavfile "
cmdline += "\""
cmdline += wavfile
cmdline += "\""
params = {
'info': 'Simple Call till Confirm',
'phoneno': '1111111',
'firstocc': 10,
'selfdelete': 0,
'startexec': vcastexe,
'cmdline': cmdline
}
res = self.postToGateway(urlstr, params)
return self.getReqId(res)
################
# CAMPAIGN API #
################
def importCampaign(self, listname, filepath):
urlstr = "/ocall/campapi"
params = {
'action': 'import',
'importfile': ntpath.basename(filepath),
'importfilepath': filepath,
'profile': 'Test',
'mod': 'cus',
'fieldsep': ',',
'row1': 1,
'mergeopt': 'empty',
'leadsrcname': listname
}
files = {
'file': (ntpath.basename(filepath), open(filepath, 'rb'))
}
res = self.postToGateway(urlstr, params, files)
return ast.literal_eval(res)
def runCampaign(self, listname):
urlstr = "/ocall/campapi"
params = {
'action': 'bbp',
'CAMP_NAME': 'Test',
'listname': listname,
'phonecols': 'Phone',
'lines': self.line_,
'calldisps': '',
'callerid': self.callerid_,
}
res = self.postToGateway(urlstr, params)
return ast.literal_eval(res)
def importAndRunCampaign(self, filepath, msgtype, msginfo):
urlstr = "/ocall/campapi"
params = {
'action': 'bbp',
# Parameters for importing the campaign
'importfile': ntpath.basename(filepath),
'importfilepath': filepath,
'mergeopt': 'skip',
# 'profile': 'Test',
'mod': 'cus',
'row1': 1,
'leadsrcname': 'Odoo Voicent Connector',
# Parameters for running the campaign
'CAMP_NAME': 'Odoo Voicent Connector',
'phonecols': 'Phone',
'lines': self.line_,
'calldisps': '',
'callerid': self.callerid_,
# Parameters for Autodialer
'msgtype': msgtype,
'msginfo': msginfo,
}
files = {
'file': (ntpath.basename(filepath), open(filepath, 'rb'))
}
res = self.postToGateway(urlstr, params, files)
return ast.literal_eval(res)
def checkStatus(self, camp_id):
urlstr = "/ocall/campapi"
params = {
'action': 'campstats',
'camp_id': camp_id
}
res = self.postToGateway(urlstr, params)
return ast.literal_eval(res)
def exportResult(self, camp_id, filename, extracols=None):
urlstr = "/ocall/campapi"
params = {
'action': 'exportcamp',
'camp_id': camp_id,
'f': 'webapps/ROOT/assets/global/' + filename,
'extracols': extracols
}
res = ast.literal_eval(self.postToGateway(urlstr, params))
if res.get('status') == 'OK':
url = 'http://' + self.host_ + ':' + self.port_ + \
'/assets/global/' + filename
res2 = requests.get(url)
with open(filename, 'wb') as local_file:
local_file.write(res2.content)
local_file.close()
reader = csv.DictReader(open(filename, 'r'))
os.remove(filename)
for row in reader:
return row
else:
return res

6
connector_voicent/models/__init__.py

@ -0,0 +1,6 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import res_partner
from . import backend_voicent_call_line
from . import backend_voicent_time_line
from . import backend_voicent

79
connector_voicent/models/backend_voicent.py

@ -0,0 +1,79 @@
# Copyright (C) 2019 Open Source Integrators
# <https://www.opensourceintegrators.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
from datetime import datetime, timedelta
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
from pytz import timezone
class BackendVoicent(models.Model):
_name = 'backend.voicent'
_description = 'Voicent Backend'
_inherit = ['connector.backend']
name = fields.Char(string='Name', required=True)
host = fields.Char(string='Host', default='localhost', required=True)
port = fields.Integer(string='Port', default='8155', required=True)
callerid = fields.Char(string='Caller ID', required=True)
line = fields.Integer(string='Number of lines', required=True)
next_call = fields.Datetime(string='Next Call', copy=False)
call_line_ids = fields.One2many(
string='Call Lines',
comodel_name='backend.voicent.call.line',
inverse_name='backend_id')
time_line_ids = fields.One2many(
string='Call Times',
comodel_name='backend.voicent.time.line',
inverse_name='backend_id')
active = fields.Boolean('Active', default=True)
@api.model
def _run_update_next_call(self):
""" This method is called from a cron job. """
cr_time_list = []
backends = self.search([('active', '=', True)])
for backend in backends:
current_dt = datetime.now(timezone('UTC'))
user_tz = timezone(
self.env.context.get('tz') or self.env.user.tz or 'UTC')
dt_value = current_dt.astimezone(user_tz)
convt_dt_strf = dt_value.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
convt_dt = datetime.strptime(
convt_dt_strf,
DEFAULT_SERVER_DATETIME_FORMAT)
current_time = convt_dt.strftime("%H:%M")
for time_line_rec in backend.time_line_ids:
hours, minutes = divmod(abs(time_line_rec.time) * 60, 60)
minutes = round(minutes)
if minutes == 60:
minutes = 0
hours += 1
line_time = '%02d:%02d' % (hours, minutes)
cr_time_list.append(line_time)
cr_time_list = sorted(cr_time_list)
next_call = False
for time_entry in cr_time_list:
if time_entry > current_time:
next_call = datetime.now().replace(
hour=int(time_entry.split(':')[0]),
minute=int(time_entry.split(':')[1]),
second=0)
break
if not next_call:
next_call = datetime.now().replace(
hour=int(cr_time_list[0].split(':')[0]),
minute=int(cr_time_list[0].split(':')[1]),
second=0) + timedelta(
days=0)
next_call_tz = timezone(self.env.context.get('tz') or
self.env.user.tz).localize(next_call,
is_dst=False)
next_call_utc = next_call_tz.astimezone(timezone('UTC'))
next_call_utc = datetime.strptime(
fields.Datetime.to_string(next_call_utc),
DEFAULT_SERVER_DATETIME_FORMAT)
backend.next_call = fields.Datetime.to_string(next_call_utc)

109
connector_voicent/models/backend_voicent_call_line.py

@ -0,0 +1,109 @@
# Copyright (C) 2019 Open Source Integrators
# <https://www.opensourceintegrators.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
VOICENT_CONTACT_COLUMNS = [('Assigned To', 'Assigned To'),
('Business', 'Business'),
('Category', 'Category'),
('Contact Status', 'Contact Status'),
('Email', 'Email'),
('First Name', 'First Name (Required)'),
('Last Name', 'Last Name'),
('Lead Source', 'Lead Source'),
('Other', 'Other'),
('Phone', 'Phone (Required)')]
VOICENT_REPLY = [('availableagents', 'Available Agents'),
('callback', 'Callback'),
('campid', 'Campaign ID'),
('campname', 'Campaign Name'),
('campsize', 'Campaign Size'),
('connected', 'Connected'),
('dnc', 'Contact DNC'),
('nophone', 'Contact No Phone'),
('disc', 'Disc. Number'),
('dropped', 'Dropped'),
('failed', 'Failed'),
('fax', 'Fax'),
('info', 'Info'),
('in', 'Interested'),
('lines', 'Lines'),
('linebusy', 'Line Busy'),
('live', 'Live Answer'),
('machine', 'Machine Answer'),
('made', 'Made'),
('maxlines', 'Max Lines'),
('noact', 'No Activity'),
('noanswer', 'No Answer'),
('notin', 'Not Interested'),
('notes', 'Notes'),
('optout', 'Opt Out'),
('serverr', 'Service Error'),
('status', 'Status'),
('totalagents', 'Total Agents'),
('wit', 'Wit')]
MSGTYPE = [('audio', 'Audio'),
('ivr', 'IVR'),
('survey', 'Survey'),
('template', 'Template'),
('tts', 'Text-To-Speech')]
class BackendVoicentCallLine(models.Model):
_name = 'backend.voicent.call.line'
_description = 'Voicent Backend Call Line'
name = fields.Char(string='Name', required=True)
sequence = fields.Integer(string='Sequence', default=0)
applies_on = fields.Selection(string='Applies on', selection=[])
msgtype = fields.Selection(MSGTYPE, string='Message Type', required=True)
msginfo = fields.Char(string='Message Info')
backend_id = fields.Many2one(
string='Backend',
comodel_name='backend.voicent',
ondelete='set null')
reply_ids = fields.One2many('backend.voicent.call.line.reply', 'line_id',
string="Replies")
contact_ids = fields.One2many('backend.voicent.call.line.contact',
'line_id',
string="Contact Info")
class BackendVoicentCallLineContact(models.Model):
_name = 'backend.voicent.call.line.contact'
_description = 'Columns of the CSV file to provide the contact list'
_order = 'sequence'
name = fields.Selection(VOICENT_CONTACT_COLUMNS, string='Voicent Field',
required=True)
other = fields.Char(string='Other')
sequence = fields.Integer(string='Sequence', default=0)
field_domain = fields.Char(string='Odoo Field',
required=True)
default_value = fields.Char(string='Default Value', required=True)
line_id = fields.Many2one(
string='Call Line',
comodel_name='backend.voicent.call.line',
ondelete='set null')
class BackendVoicentCallLineReply(models.Model):
_name = 'backend.voicent.call.line.reply'
_description = 'Reply to a Voicent Call'
name = fields.Char(string='Name', required=True)
line_id = fields.Many2one(
string='Call Line',
comodel_name='backend.voicent.call.line',
ondelete='set null')
reply_field = fields.Selection(VOICENT_REPLY, string="Voicent Reply Field",
required=True)
reply_value = fields.Char(string="Voicent Reply Value", required=True)
action_id = fields.Many2one('ir.actions.server', string="Server Action",
required=True,
help="""If the Voicent reply field is equal to
the Voicent reply value, the server action is
executed.""")

18
connector_voicent/models/backend_voicent_time_line.py

@ -0,0 +1,18 @@
# Copyright (C) 2019 Open Source Integrators
# <https://www.opensourceintegrators.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class BackendVoicentTimeLine(models.Model):
_name = 'backend.voicent.time.line'
_description = 'Voicent Backend Time Line'
_order = 'time'
name = fields.Char(string='Name', required=True)
time = fields.Float(string='Time', copy=False)
backend_id = fields.Many2one(
string='Backend',
comodel_name='backend.voicent',
ondelete='set null')

11
connector_voicent/models/res_partner.py

@ -0,0 +1,11 @@
# Copyright (C) 2019 Open Source Integrators
# <https://www.opensourceintegrators.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class ResPartner(models.Model):
_inherit = 'res.partner'
can_call = fields.Boolean(string='Accepts Voicent Calls', default=True)

6
connector_voicent/readme/CONFIGURATION.rst

@ -0,0 +1,6 @@
#. Go to Settings > Users & Companies > Users
#. Select the filter "Inactive Users" only
#. Select the user "OdooBot"
#. Click on "Edit"
#. Go to the tab "Preferences"
#. Set the timezone

3
connector_voicent/readme/CONTRIBUTORS.rst

@ -0,0 +1,3 @@
* Maxime Chambreuil <mchambreuil@opensourceintegrators.com>
* Youness MAAFI <youness.maafi@gmail.com>
* Murtuza Saleh <murtuza.saleh@serpentcs.com>

1
connector_voicent/readme/DESCRIPTION.rst

@ -0,0 +1 @@
This module allows you to connect Odoo with Voicent <https://www.voicent.com> and is meant to be extended to integrate Odoo records and processes with phone calls made by Voicent.

3
connector_voicent/readme/ROADMAP.rst

@ -0,0 +1,3 @@
* On the backend, replace the 'Applies On' selection list with a Many2one to ir.model.fields
* On the backend call line contact, use the domain widget to select the field
* Update the voicent library on Pypi with examples/voicent.py

6
connector_voicent/readme/USAGE.rst

@ -0,0 +1,6 @@
#. Go to Connectors > Backends > Voicent Backends
#. Create a new Voicent Backend with the host, port, the caller ID and the number of lines
#. Create Call Lines to determine when (which stage in the process) calls are added to the queue
#. Create Contact Info to create the structure of the CSV file to send to Voicent
#. Create Replies to determine what to do based on the replies (see example below)
#. Create Time Line to determine when (what time) calls are made

6
connector_voicent/security/ir.model.access.csv

@ -0,0 +1,6 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_backend_voicent,access_backend_voicent,model_backend_voicent,connector.group_connector_manager,1,1,1,1
access_backend_voicent_call_line,access_backend_voicent_call_line,model_backend_voicent_call_line,connector.group_connector_manager,1,1,1,1
access_backend_voicent_call_line_reply,access_backend_voicent_call_line_reply,model_backend_voicent_call_line_reply,connector.group_connector_manager,1,1,1,1
access_backend_voicent_call_line_contact,access_backend_voicent_call_line_contact,model_backend_voicent_call_line_contact,connector.group_connector_manager,1,1,1,1
access_backend_voicent_time_line,access_backend_voicent_time_line,model_backend_voicent_time_line,connector.group_connector_manager,1,1,1,1

BIN
connector_voicent/static/description/icon.png

After

Width: 210  |  Height: 76  |  Size: 22 KiB

433
connector_voicent/static/description/index.html

@ -0,0 +1,433 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.14: http://docutils.sourceforge.net/" />
<title>Voicent Connector</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="voicent-connector">
<h1 class="title">Voicent Connector</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/connector-telephony/tree/12.0/connector_voicent"><img alt="OCA/connector-telephony" src="https://img.shields.io/badge/github-OCA%2Fconnector--telephony-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/connector-telephony-12-0/connector-telephony-12-0-connector_voicent"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/228/12.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>This module allows you to connect Odoo with Voicent &lt;<a class="reference external" href="https://www.voicent.com">https://www.voicent.com</a>&gt; and is meant to be extended to integrate Odoo records and processes with phone calls made by Voicent.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#usage" id="id1">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id2">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id3">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id4">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id5">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id6">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#id1">Usage</a></h1>
<ol class="arabic simple">
<li>Go to Connectors &gt; Backends &gt; Voicent Backends</li>
<li>Create a new Voicent Backend with the host and port</li>
<li>Create Call Lines to determine when (which stage in the process) calls are added to the queue</li>
<li>Create Time Line to determine when (what time) calls are made</li>
</ol>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id2">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/connector-telephony/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/connector-telephony/issues/new?body=module:%20connector_voicent%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#id3">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id4">Authors</a></h2>
<ul class="simple">
<li>Open Source Integrators</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id5">Contributors</a></h2>
<ul class="simple">
<li>Maxime Chambreuil &lt;<a class="reference external" href="mailto:mchambreuil&#64;opensourceintegrators.com">mchambreuil&#64;opensourceintegrators.com</a>&gt;</li>
<li>Youness MAAFI &lt;<a class="reference external" href="mailto:youness.maafi&#64;gmail.com">youness.maafi&#64;gmail.com</a>&gt;</li>
<li>Murtuza Saleh &lt;<a class="reference external" href="mailto:murtuza.saleh&#64;serpentcs.com">murtuza.saleh&#64;serpentcs.com</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id6">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
<p><a class="reference external" href="https://github.com/max3903"><img alt="max3903" src="https://github.com/max3903.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/connector-telephony/tree/12.0/connector_voicent">OCA/connector-telephony</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>

3
connector_voicent/tests/__init__.py

@ -0,0 +1,3 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import test_backend_voicent

37
connector_voicent/tests/test_backend_voicent.py

@ -0,0 +1,37 @@
# Copyright (C) 2019 Open Source Integrators
# <https://www.opensourceintegrators.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo.tests.common import TransactionCase
class TestBackendVoicent(TransactionCase):
def setUp(self):
super(TestBackendVoicent, self).setUp()
self.backend_voicent_model = self.env['backend.voicent']
self.backend_voicent_id = self.backend_voicent_model.create(
{'name': 'Test',
'host': 'localhost',
'port': '8155',
'callerid': '0000000000',
'line': '1',
'active': True,
'call_line_ids': [(0, 0,
{'name': 'call 1',
'applies_on': False,
'msgtype': 'tts',
'msginfo': 'Hello World!'})],
'time_line_ids': [(0, 0, {'name': 'Call Time 1',
'time': 10.0}),
(0, 0, {'name': 'Call Time 2',
'time': 11.0}),
(0, 0, {'name': 'Call Time 3',
'time': 12.0}),
(0, 0, {'name': 'Call Time 4',
'time': 13.0})]
})
def test_run_check_the_voicent_status(self):
"""To call the scheduler method."""
self.backend_voicent_id._run_update_next_call()

118
connector_voicent/views/backend_voicent.xml

@ -0,0 +1,118 @@
<odoo>
<record id="backend_voicent_tree_view" model="ir.ui.view">
<field name="name">backend.voicent.tree.view</field>
<field name="model">backend.voicent</field>
<field name="arch" type="xml">
<tree string="Voicent Backends">
<field name="name"/>
<field name="host"/>
<field name="port"/>
<field name="callerid"/>
<field name="line"/>
<field name="next_call" groups="base.group_no_one"/>
<field name="active"/>
</tree>
</field>
</record>
<record id="backend_voicent_form_view" model="ir.ui.view">
<field name="name">backend.voicent.form.view</field>
<field name="model">backend.voicent</field>
<field name="arch" type="xml">
<form string="Voicent Backend">
<sheet>
<div class="oe_button_box" position="inside">
<button name="toggle_active" type="object" class="oe_stat_button" icon="fa-archive">
<field name="active" widget="boolean_button" options="{&quot;terminology&quot;: &quot;archive&quot;}"/>
</button>
</div>
<div class="oe_title">
<h1><field name="name" class="field_name" placeholder="Name of the backend..."/></h1>
</div>
<group>
<group>
<field name="host"/>
<field name="port"/>
</group>
<group>
<field name="callerid"/>
<field name="line"/>
</group>
</group>
<notebook>
<page string="Call Lines">
<field name="call_line_ids">
<tree string="Call Lines">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="applies_on"/>
<field name="msgtype"/>
<field name="msginfo"/>
</tree>
</field>
</page>
<page string="Call Times">
<field name="time_line_ids">
<tree string="Call Times" editable="top">
<field name="name"/>
<field name="time" widget="float_time"/>
</tree>
</field>
</page>
<page string="Debug" groups="base.group_no_one">
<group>
<group id="debug-left">
<field name="next_call"/>
</group>
<group id="debug-right"/>
</group>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="backend_voicent_search_view" model="ir.ui.view">
<field name="name">backend.voicent.search.view</field>
<field name="model">backend.voicent</field>
<field name="arch" type="xml">
<search string="Voicent Backend">
<field name="host"/>
<field name="port"/>
<field name="callerid"/>
<filter string="Archived" name="inactive"
domain="[('active', '=', False)]"/>
</search>
</field>
</record>
<record id="action_backend_voicent_act_window" model="ir.actions.act_window">
<field name="type">ir.actions.act_window</field>
<field name="name">Voicent Backend</field>
<field name="res_model">backend.voicent</field>
<field name="view_mode">tree,form</field>
<field name="view_type">form</field>
<field name="search_view_id" ref="backend_voicent_search_view"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to add new Voicent Backend
</p>
</field>
</record>
<menuitem id="menu_voicent_root"
parent="connector.menu_connector_root"
name="Voicent"
sequence="10"
groups="connector.group_connector_manager"/>
<menuitem id="menu_voicent_backend_root"
parent="connector_voicent.menu_voicent_root"
name="Backends"
sequence="10"
action="action_backend_voicent_act_window"
groups="connector.group_connector_manager"/>
</odoo>

76
connector_voicent/views/backend_voicent_call_line.xml

@ -0,0 +1,76 @@
<odoo>
<record id="backend_voicent_call_line_tree_view" model="ir.ui.view">
<field name="name">backend.voicent.call.line.tree.view</field>
<field name="model">backend.voicent.call.line</field>
<field name="arch" type="xml">
<tree string="Voicent Backend Call Lines">
<field name="name"/>
<field name="applies_on"/>
<field name="msgtype"/>
<field name="msginfo"/>
</tree>
</field>
</record>
<record id="backend_voicent_call_line_form_view" model="ir.ui.view">
<field name="name">backend.voicent.call.line.form.view</field>
<field name="model">backend.voicent.call.line</field>
<field name="arch" type="xml">
<form string="Voicent Backend Call Line">
<sheet>
<div class="oe_title">
<h1><field name="name" class="field_name" placeholder="Name of the call line..."/></h1>
</div>
<group>
<group id="main-left">
<field name="applies_on"/>
<field name="msgtype"/>
<field name="msginfo"/>
</group>
<group id="main-right">
</group>
</group>
<notebook>
<page string="Contact Info">
<field name="contact_ids">
<tree string="Contacts">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="other"/>
<field name="field_domain"/>
<field name="default_value"/>
</tree>
<form>
<field name="name"/>
<group>
<group>
<field name="other"/>
<field name="field_domain"
placeholder="partner_id.name or partner_id.phone"/>
</group>
<group>
<field name="sequence"/>
<field name="default_value"/>
</group>
</group>
</form>
</field>
</page>
<page string="Replies">
<field name="reply_ids">
<tree string="Replies" editable="top">
<field name="name"/>
<field name="reply_field" string="If"/>
<field name="reply_value" string="contains"/>
<field name="action_id" string="Then, execute"/>
</tree>
</field>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
</odoo>

15
connector_voicent/views/res_partner.xml

@ -0,0 +1,15 @@
<odoo>
<record id="view_res_partner_form" model="ir.ui.view">
<field name="name">view.res.partner.form</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<field name="user_ids" position="before">
<field name="can_call"
attrs="{'invisible': [('is_company', '=', True)]}"/>
</field>
</field>
</record>
</odoo>

2
oca_dependencies.txt

@ -1,2 +1,4 @@
connector
queue
server-tools
web
Loading…
Cancel
Save