diff --git a/asterisk_click2dial/__init__.py b/asterisk_click2dial/__init__.py index 2a24f08..96fcdf0 100644 --- a/asterisk_click2dial/__init__.py +++ b/asterisk_click2dial/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -from . import asterisk_click2dial +from . import models from . import controller diff --git a/asterisk_click2dial/__manifest__.py b/asterisk_click2dial/__manifest__.py index 70ce432..33dd51d 100644 --- a/asterisk_click2dial/__manifest__.py +++ b/asterisk_click2dial/__manifest__.py @@ -4,7 +4,7 @@ { 'name': 'Asterisk Click2dial', - 'version': '9.0.1.0.0', + 'version': '10.0.1.0.0', 'category': 'Phone', 'license': 'AGPL-3', 'summary': 'Asterisk-Odoo connector', @@ -80,14 +80,14 @@ http://www.akretion.com/products-and-services/openerp-asterisk-voip-connector 'depends': ['base_phone'], 'external_dependencies': {'python': ['Asterisk']}, 'data': [ - 'asterisk_server_view.xml', - 'res_users_view.xml', + 'views/asterisk_server.xml', + 'views/res_users.xml', 'security/ir.model.access.csv', 'web_asterisk_click2dial.xml', ], - 'demo': ['asterisk_click2dial_demo.xml'], + 'demo': ['demo/asterisk_click2dial_demo.xml'], 'qweb': ['static/src/xml/*.xml'], 'css': ['static/src/css/*.css'], 'application': True, - 'installable': False, + 'installable': True, } diff --git a/asterisk_click2dial/controller.py b/asterisk_click2dial/controller.py index 72fb3bc..3efd84b 100644 --- a/asterisk_click2dial/controller.py +++ b/asterisk_click2dial/controller.py @@ -3,7 +3,7 @@ # © 2015-2016 Juris Malinens (port to v9) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import http +from odoo import http class AsteriskClick2dialController(http.Controller): diff --git a/asterisk_click2dial/asterisk_click2dial_demo.xml b/asterisk_click2dial/demo/asterisk_click2dial_demo.xml similarity index 97% rename from asterisk_click2dial/asterisk_click2dial_demo.xml rename to asterisk_click2dial/demo/asterisk_click2dial_demo.xml index a808e42..a3f5b05 100644 --- a/asterisk_click2dial/asterisk_click2dial_demo.xml +++ b/asterisk_click2dial/demo/asterisk_click2dial_demo.xml @@ -4,8 +4,7 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> - - + @@ -37,5 +36,4 @@ - diff --git a/asterisk_click2dial/models/__init__.py b/asterisk_click2dial/models/__init__.py new file mode 100644 index 0000000..4dec907 --- /dev/null +++ b/asterisk_click2dial/models/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- + +from . import asterisk_server +from . import res_users +from . import phone_common diff --git a/asterisk_click2dial/asterisk_click2dial.py b/asterisk_click2dial/models/asterisk_server.py similarity index 57% rename from asterisk_click2dial/asterisk_click2dial.py rename to asterisk_click2dial/models/asterisk_server.py index 36b613b..d0d7b99 100644 --- a/asterisk_click2dial/asterisk_click2dial.py +++ b/asterisk_click2dial/models/asterisk_server.py @@ -2,19 +2,19 @@ # © 2010-2016 Akretion (Alexis de Lattre ) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import models, fields, api, _ -from openerp.exceptions import UserError, ValidationError -import logging +from odoo import models, fields, api, _ +from odoo.exceptions import UserError, ValidationError from pprint import pformat +import logging +_logger = logging.getLogger(__name__) try: # pip install py-Asterisk from Asterisk import Manager except ImportError: + _logger.debug('Cannot import Asterisk') Manager = None -_logger = logging.getLogger(__name__) - class AsteriskServer(models.Model): '''Asterisk server object, stores the parameters of the Asterisk IPBXs''' @@ -236,170 +236,3 @@ class AsteriskServer(models.Model): return calling_number else: return False - - -class ResUsers(models.Model): - _inherit = "res.users" - - internal_number = fields.Char( - string='Internal Number', copy=False, - help="User's internal phone number.") - dial_suffix = fields.Char( - string='User-specific Dial Suffix', - help="User-specific dial suffix such as aa=2wb for SCCP auto answer.") - callerid = fields.Char( - string='Caller ID', copy=False, - help="Caller ID used for the calls initiated by this user.") - # You'd probably think: Asterisk should reuse the callerID of sip.conf! - # But it cannot, cf - # http://lists.digium.com/pipermail/asterisk-users/ - # 2012-January/269787.html - cdraccount = fields.Char( - string='CDR Account', - help="Call Detail Record (CDR) account used for billing this user.") - asterisk_chan_type = fields.Selection([ - ('SIP', 'SIP'), - ('IAX2', 'IAX2'), - ('DAHDI', 'DAHDI'), - ('Zap', 'Zap'), - ('Skinny', 'Skinny'), - ('MGCP', 'MGCP'), - ('mISDN', 'mISDN'), - ('H323', 'H323'), - ('SCCP', 'SCCP'), - # Local works for click2dial, but it won't work in - # _get_calling_number() when trying to identify the - # channel of the user, so it's better not to propose it - # ('Local', 'Local'), - ], string='Asterisk Channel Type', default='SIP', - help="Asterisk channel type, as used in the Asterisk dialplan. " - "If the user has a regular IP phone, the channel type is 'SIP'.") - resource = fields.Char( - string='Resource Name', copy=False, - help="Resource name for the channel type selected. For example, " - "if you use 'Dial(SIP/phone1)' in your Asterisk dialplan to ring " - "the SIP phone of this user, then the resource name for this user " - "is 'phone1'. For a SIP phone, the phone number is often used as " - "resource name, but not always.") - alert_info = fields.Char( - string='User-specific Alert-Info SIP Header', - help="Set a user-specific Alert-Info header in SIP request to " - "user's IP Phone for the click2dial feature. If empty, the " - "Alert-Info header will not be added. You can use it to have a " - "special ring tone for click2dial (a silent one !) or to " - "activate auto-answer for example.") - variable = fields.Char( - string='User-specific Variable', - help="Set a user-specific 'Variable' field in the Asterisk " - "Manager Interface 'originate' request for the click2dial " - "feature. If you want to have several variable headers, separate " - "them with '|'.") - asterisk_server_id = fields.Many2one( - 'asterisk.server', string='Asterisk Server', - help="Asterisk server on which the user's phone is connected. " - "If you leave this field empty, it will use the first Asterisk " - "server of the user's company.") - - @api.multi - @api.constrains('resource', 'internal_number', 'callerid') - def _check_validity(self): - for user in self: - strings_to_check = [ - (_('Resource Name'), user.resource), - (_('Internal Number'), user.internal_number), - (_('Caller ID'), user.callerid), - ] - for check_string in strings_to_check: - if check_string[1]: - try: - check_string[1].encode('ascii') - except UnicodeEncodeError: - raise ValidationError(_( - "The '%s' for the user '%s' should only have " - "ASCII caracters") - % (check_string[0], user.name)) - - @api.multi - def get_asterisk_server_from_user(self): - '''Returns an asterisk.server recordset''' - self.ensure_one() - # We check if the user has an Asterisk server configured - if self.asterisk_server_id: - ast_server = self.asterisk_server_id - else: - asterisk_servers = self.env['asterisk.server'].search( - [('company_id', '=', self.company_id.id)]) - # If the user doesn't have an asterisk server, - # we take the first one of the user's company - if not asterisk_servers: - raise UserError( - _("No Asterisk server configured for the company '%s'.") - % self.company_id.name) - else: - ast_server = asterisk_servers[0] - return ast_server - - -class PhoneCommon(models.AbstractModel): - _inherit = 'phone.common' - - @api.model - def click2dial(self, erp_number): - res = super(PhoneCommon, self).click2dial(erp_number) - if not erp_number: - raise UserError(_('Missing phone number')) - - user, ast_server, ast_manager = \ - self.env['asterisk.server']._connect_to_asterisk() - ast_number = self.convert_to_dial_number(erp_number) - # Add 'out prefix' - if ast_server.out_prefix: - _logger.debug('Out prefix = %s', ast_server.out_prefix) - ast_number = '%s%s' % (ast_server.out_prefix, ast_number) - _logger.debug('Number to be sent to Asterisk = %s', ast_number) - - # The user should have a CallerID - if not user.callerid: - raise UserError(_('No callerID configured for the current user')) - - variable = [] - if user.asterisk_chan_type == 'SIP': - # We can only have one alert-info header in a SIP request - if user.alert_info: - variable.append( - 'SIPAddHeader=Alert-Info: %s' % user.alert_info) - elif ast_server.alert_info: - variable.append( - 'SIPAddHeader=Alert-Info: %s' % ast_server.alert_info) - if user.variable: - for user_variable in user.variable.split('|'): - variable.append(user_variable.strip()) - channel = '%s/%s' % (user.asterisk_chan_type, user.resource) - if user.dial_suffix: - channel += '/%s' % user.dial_suffix - - try: - ast_manager.Originate( - channel, - context=ast_server.context, - extension=ast_number, - priority=unicode(ast_server.extension_priority), - timeout=unicode(ast_server.wait_time * 1000), - caller_id=user.callerid, - account=user.cdraccount, - variable=variable) - except Exception, e: - _logger.error( - "Error in the Originate request to Asterisk server %s", - ast_server.ip_address) - _logger.error( - "Here are the details of the error: '%s'", unicode(e)) - raise UserError( - _("Click to dial with Asterisk failed.\nHere is the error: " - "'%s'") - % unicode(e)) - finally: - ast_manager.Logoff() - - res['dialed_number'] = ast_number - return res diff --git a/asterisk_click2dial/models/phone_common.py b/asterisk_click2dial/models/phone_common.py new file mode 100644 index 0000000..a898f48 --- /dev/null +++ b/asterisk_click2dial/models/phone_common.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +# © 2010-2016 Akretion (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models, api, _ +from odoo.exceptions import UserError +import logging +_logger = logging.getLogger(__name__) + + +class PhoneCommon(models.AbstractModel): + _inherit = 'phone.common' + + @api.model + def click2dial(self, erp_number): + res = super(PhoneCommon, self).click2dial(erp_number) + if not erp_number: + raise UserError(_('Missing phone number')) + + user, ast_server, ast_manager = \ + self.env['asterisk.server']._connect_to_asterisk() + ast_number = self.convert_to_dial_number(erp_number) + # Add 'out prefix' + if ast_server.out_prefix: + _logger.debug('Out prefix = %s', ast_server.out_prefix) + ast_number = '%s%s' % (ast_server.out_prefix, ast_number) + _logger.debug('Number to be sent to Asterisk = %s', ast_number) + + # The user should have a CallerID + if not user.callerid: + raise UserError(_('No callerID configured for the current user')) + + variable = [] + if user.asterisk_chan_type == 'SIP': + # We can only have one alert-info header in a SIP request + if user.alert_info: + variable.append( + 'SIPAddHeader=Alert-Info: %s' % user.alert_info) + elif ast_server.alert_info: + variable.append( + 'SIPAddHeader=Alert-Info: %s' % ast_server.alert_info) + if user.variable: + for user_variable in user.variable.split('|'): + variable.append(user_variable.strip()) + channel = '%s/%s' % (user.asterisk_chan_type, user.resource) + if user.dial_suffix: + channel += '/%s' % user.dial_suffix + + try: + ast_manager.Originate( + channel, + context=ast_server.context, + extension=ast_number, + priority=unicode(ast_server.extension_priority), + timeout=unicode(ast_server.wait_time * 1000), + caller_id=user.callerid, + account=user.cdraccount, + variable=variable) + except Exception, e: + _logger.error( + "Error in the Originate request to Asterisk server %s", + ast_server.ip_address) + _logger.error( + "Here are the details of the error: '%s'", unicode(e)) + raise UserError( + _("Click to dial with Asterisk failed.\nHere is the error: " + "'%s'") + % unicode(e)) + finally: + ast_manager.Logoff() + + res['dialed_number'] = ast_number + return res diff --git a/asterisk_click2dial/models/res_users.py b/asterisk_click2dial/models/res_users.py new file mode 100644 index 0000000..fb92b65 --- /dev/null +++ b/asterisk_click2dial/models/res_users.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- +# © 2010-2016 Akretion (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models, fields, api, _ +from odoo.exceptions import UserError, ValidationError + + +class ResUsers(models.Model): + _inherit = "res.users" + + internal_number = fields.Char( + string='Internal Number', copy=False, + help="User's internal phone number.") + dial_suffix = fields.Char( + string='User-specific Dial Suffix', + help="User-specific dial suffix such as aa=2wb for SCCP auto answer.") + callerid = fields.Char( + string='Caller ID', copy=False, + help="Caller ID used for the calls initiated by this user.") + # You'd probably think: Asterisk should reuse the callerID of sip.conf! + # But it cannot, cf + # http://lists.digium.com/pipermail/asterisk-users/ + # 2012-January/269787.html + cdraccount = fields.Char( + string='CDR Account', + help="Call Detail Record (CDR) account used for billing this user.") + asterisk_chan_type = fields.Selection([ + ('SIP', 'SIP'), + ('IAX2', 'IAX2'), + ('DAHDI', 'DAHDI'), + ('Zap', 'Zap'), + ('Skinny', 'Skinny'), + ('MGCP', 'MGCP'), + ('mISDN', 'mISDN'), + ('H323', 'H323'), + ('SCCP', 'SCCP'), + # Local works for click2dial, but it won't work in + # _get_calling_number() when trying to identify the + # channel of the user, so it's better not to propose it + # ('Local', 'Local'), + ], string='Asterisk Channel Type', default='SIP', + help="Asterisk channel type, as used in the Asterisk dialplan. " + "If the user has a regular IP phone, the channel type is 'SIP'.") + resource = fields.Char( + string='Resource Name', copy=False, + help="Resource name for the channel type selected. For example, " + "if you use 'Dial(SIP/phone1)' in your Asterisk dialplan to ring " + "the SIP phone of this user, then the resource name for this user " + "is 'phone1'. For a SIP phone, the phone number is often used as " + "resource name, but not always.") + alert_info = fields.Char( + string='User-specific Alert-Info SIP Header', + help="Set a user-specific Alert-Info header in SIP request to " + "user's IP Phone for the click2dial feature. If empty, the " + "Alert-Info header will not be added. You can use it to have a " + "special ring tone for click2dial (a silent one !) or to " + "activate auto-answer for example.") + variable = fields.Char( + string='User-specific Variable', + help="Set a user-specific 'Variable' field in the Asterisk " + "Manager Interface 'originate' request for the click2dial " + "feature. If you want to have several variable headers, separate " + "them with '|'.") + asterisk_server_id = fields.Many2one( + 'asterisk.server', string='Asterisk Server', + help="Asterisk server on which the user's phone is connected. " + "If you leave this field empty, it will use the first Asterisk " + "server of the user's company.") + + @api.multi + @api.constrains('resource', 'internal_number', 'callerid') + def _check_validity(self): + for user in self: + strings_to_check = [ + (_('Resource Name'), user.resource), + (_('Internal Number'), user.internal_number), + (_('Caller ID'), user.callerid), + ] + for check_string in strings_to_check: + if check_string[1]: + try: + check_string[1].encode('ascii') + except UnicodeEncodeError: + raise ValidationError(_( + "The '%s' for the user '%s' should only have " + "ASCII caracters") + % (check_string[0], user.name)) + + @api.multi + def get_asterisk_server_from_user(self): + '''Returns an asterisk.server recordset''' + self.ensure_one() + # We check if the user has an Asterisk server configured + if self.asterisk_server_id: + ast_server = self.asterisk_server_id + else: + asterisk_servers = self.env['asterisk.server'].search( + [('company_id', '=', self.company_id.id)]) + # If the user doesn't have an asterisk server, + # we take the first one of the user's company + if not asterisk_servers: + raise UserError( + _("No Asterisk server configured for the company '%s'.") + % self.company_id.name) + else: + ast_server = asterisk_servers[0] + return ast_server diff --git a/asterisk_click2dial/scripts/openerp_popup_timeout.sh b/asterisk_click2dial/scripts/odoo_popup_timeout.sh similarity index 84% rename from asterisk_click2dial/scripts/openerp_popup_timeout.sh rename to asterisk_click2dial/scripts/odoo_popup_timeout.sh index a5b2c38..528393d 100755 --- a/asterisk_click2dial/scripts/openerp_popup_timeout.sh +++ b/asterisk_click2dial/scripts/odoo_popup_timeout.sh @@ -19,9 +19,9 @@ # In this example, we chose 2 seconds. # To test this script manually (i.e. outside of Asterisk), run : -# echo "agi_callerid:0141401242"|openerp_popup_timeout.sh +# echo "agi_callerid:0141401242"|odoo_popup_timeout.sh # where 0141401242 is a phone number that could be presented by the calling party PATH=/usr/local/sbin:/usr/local/bin:/var/lib/asterisk/agi-bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/share/asterisk/agi-bin -timeout 2s set_name_agi.py --server openerp.mycompany.com --database erp_prod --user-id 12 --password "thepasswd" --notify admin demo +timeout 2s set_name_agi.py --server odoo.mycompany.com --database erp_prod --user-id 12 --password "thepasswd" --notify admin demo diff --git a/asterisk_click2dial/scripts/set_name_agi.py b/asterisk_click2dial/scripts/set_name_agi.py index f4a8761..f02ddfd 100755 --- a/asterisk_click2dial/scripts/set_name_agi.py +++ b/asterisk_click2dial/scripts/set_name_agi.py @@ -16,7 +16,7 @@ # along with this program. If not, see . """ - Name lookup in OpenERP for incoming and outgoing calls with an + Name lookup in Odoo for incoming and outgoing calls with an Asterisk IPBX This script is designed to be used as an AGI on an Asterisk IPBX... @@ -32,12 +32,12 @@ See my 2 sample wrappers "set_name_incoming_timeout.sh" and "set_name_outgoing_timeout.sh" - It's probably a good idea to create a user in OpenERP dedicated to this task. + It's probably a good idea to create a user in Odoo dedicated to this task. This user only needs to be part of the group "Phone CallerID", which has read access on the 'res.partner' and other objects with phone numbers and names. - Note that this script can be used without OpenERP, with just the + Note that this script can be used without Odoo, with just the geolocalisation feature : for that, don't use option --server ; only use --geoloc @@ -45,14 +45,13 @@ 1) INCOMING CALLS When executed from the dialplan on an incoming phone call, it will - lookup in OpenERP's partners and other objects with phone numbers + lookup in Odoo's partners and other objects with phone numbers (leads, employees, etc...), and, if it finds the phone number, it will get the corresponding name of the person and use this name as CallerID name for the incoming call. Requires the "base_phone" module - available from https://code.launchpad.net/openerp-asterisk-connector - for OpenERP version >= 7.0 + available from https://github.com/OCA/connector-telephony Asterisk dialplan example : @@ -65,7 +64,7 @@ 2) OUTGOING CALLS When executed from the dialplan on an outgoing call, it will - lookup in OpenERP the name corresponding to the phone number + lookup in Odoo the name corresponding to the phone number that is called by the user and it will update the name of the callee on the screen of the phone of the caller. @@ -79,9 +78,9 @@ as parameter to the CONNECTEDLINE function. Here is the code that I used on the pre-process subroutine - "openerp-out-call" of the Outgoing Call of my Xivo server : + "odoo-out-call" of the Outgoing Call of my Xivo server : - [openerp-out-call] + [odoo-out-call] exten = s,1,AGI(/var/lib/asterisk/agi-bin/set_name_outgoing_timeout.sh) same = n,Set(CONNECTEDLINE(name,i)=${connectedlinename}) same = n,Set(CONNECTEDLINE(name-pres,i)=allowed) @@ -110,11 +109,11 @@ not_found_name = False options = [ {'names': ('-s', '--server'), 'dest': 'server', 'type': 'string', 'action': 'store', 'default': False, - 'help': 'DNS or IP address of the OpenERP server. Default = none ' - '(will not try to connect to OpenERP)'}, + 'help': 'DNS or IP address of the Odoo server. Default = none ' + '(will not try to connect to Odoo)'}, {'names': ('-p', '--port'), 'dest': 'port', 'type': 'int', 'action': 'store', 'default': 8069, - 'help': "Port of OpenERP's XML-RPC interface. Default = 8069"}, + 'help': "Port of Odoo's XML-RPC interface. Default = 8069"}, {'names': ('-e', '--ssl'), 'dest': 'ssl', 'help': "Use SSL connections instead of clear connections. " "Default = no, use clear XML-RPC or JSON-RPC", @@ -124,31 +123,31 @@ options = [ "Default = no, use XML-RPC", 'action': 'store_true', 'default': False}, {'names': ('-d', '--database'), 'dest': 'database', 'type': 'string', - 'action': 'store', 'default': 'openerp', - 'help': "OpenERP database name. Default = 'openerp'"}, + 'action': 'store', 'default': 'odoo', + 'help': "Odoo database name. Default = 'odoo'"}, {'names': ('-u', '--user-id'), 'dest': 'userid', 'type': 'int', 'action': 'store', 'default': 2, - 'help': "OpenERP user ID to use when connecting to OpenERP in " + 'help': "Odoo user ID to use when connecting to Odoo in " "XML-RPC. Default = 2"}, {'names': ('-t', '--username'), 'dest': 'username', 'type': 'string', 'action': 'store', 'default': 'demo', - 'help': "OpenERP username to use when connecting to OpenERP in " + 'help': "Odoo username to use when connecting to Odoo in " "JSON-RPC. Default = demo"}, {'names': ('-w', '--password'), 'dest': 'password', 'type': 'string', 'action': 'store', 'default': 'demo', - 'help': "Password of the OpenERP user. Default = 'demo'"}, + 'help': "Password of the Odoo user. Default = 'demo'"}, {'names': ('-a', '--ascii'), 'dest': 'ascii', 'action': 'store_true', 'default': False, 'help': "Convert name from UTF-8 to ASCII. Default = no, keep UTF-8"}, {'names': ('-n', '--notify'), 'dest': 'notify', 'action': 'store_true', 'default': False, - 'help': "Notify OpenERP users via a pop-up (requires the OpenERP " + 'help': "Notify Odoo users via a pop-up (requires the Odoo " "module 'base_phone_popup'). If you use this option, you must pass " - "the logins of the OpenERP users to notify as argument to the " + "the logins of the Odoo users to notify as argument to the " "script. Default = no"}, {'names': ('-g', '--geoloc'), 'dest': 'geoloc', 'action': 'store_true', 'default': False, - 'help': "Try to geolocate phone numbers unknown to OpenERP. This " + 'help': "Try to geolocate phone numbers unknown to Odoo. This " "features requires the 'phonenumbers' Python lib. To install it, " "run 'sudo pip install phonenumbers' Default = no"}, {'names': ('-l', '--geoloc-lang'), 'dest': 'lang', 'type': 'string', @@ -267,7 +266,7 @@ def main(options, arguments): else: # If we already have a "True" caller ID name # i.e. not just digits, but a real name, then we don't try to - # connect to OpenERP or geoloc, we just keep it + # connect to Odoo or geoloc, we just keep it if ( stdinput.get('agi_calleridname') and not stdinput.get('agi_calleridname').isdigit() and @@ -309,12 +308,12 @@ def main(options, arguments): method = 'get_name_from_phone_number' res = False - # Yes, this script can be used without "-s openerp_server" ! + # Yes, this script can be used without "-s odoo_server" ! if options.server and options.jsonrpc: import odoorpc proto = options.ssl and 'jsonrpc+ssl' or 'jsonrpc' stdout_write( - 'VERBOSE "Starting %s request on OpenERP %s:%d database ' + 'VERBOSE "Starting %s request on Odoo %s:%d database ' '%s username %s"\n' % ( proto.upper(), options.server, options.port, options.database, options.username)) @@ -329,11 +328,11 @@ def main(options, arguments): stdout_write('VERBOSE "Called method %s"\n' % method) except: stdout_write( - 'VERBOSE "Could not connect to OpenERP in JSON-RPC"\n') + 'VERBOSE "Could not connect to Odoo in JSON-RPC"\n') elif options.server: proto = options.ssl and 'https' or 'http' stdout_write( - 'VERBOSE "Starting %s XML-RPC request on OpenERP %s:%d ' + 'VERBOSE "Starting %s XML-RPC request on Odoo %s:%d ' 'database %s user ID %d"\n' % ( proto, options.server, options.port, options.database, options.userid)) @@ -351,7 +350,7 @@ def main(options, arguments): 'phone.common', method, phone_number) stdout_write('VERBOSE "Called method %s"\n' % method) except: - stdout_write('VERBOSE "Could not connect to OpenERP in XML-RPC"\n') + stdout_write('VERBOSE "Could not connect to Odoo in XML-RPC"\n') # To simulate a long execution of the XML-RPC request # import time # time.sleep(5) @@ -361,16 +360,16 @@ def main(options, arguments): if len(res) > options.max_size: res = res[0:options.max_size] elif options.geoloc: - # if the number is not found in OpenERP, we try to geolocate + # if the number is not found in Odoo, we try to geolocate stdout_write( 'VERBOSE "Trying to geolocate with country %s and lang %s"\n' % (options.country, options.lang)) res = geolocate_phone_number( phone_number, options.country, options.lang) else: - # if the number is not found in OpenERP and geoloc is off, + # if the number is not found in Odoo and geoloc is off, # we put 'not_found_name' as Name - stdout_write('VERBOSE "Phone number not found in OpenERP"\n') + stdout_write('VERBOSE "Phone number not found in Odoo"\n') res = not_found_name # All SIP phones should support UTF-8... @@ -391,8 +390,8 @@ if __name__ == '__main__': usage = "Usage: get_name_agi.py [options] login1 login2 login3 ..." epilog = "Script written by Alexis de Lattre. " "Published under the GNU AGPL licence." - description = "This is an AGI script that sends a query to OpenERP. " - "It can also be used without OpenERP as to geolocate phone numbers " + description = "This is an AGI script that sends a query to Odoo. " + "It can also be used without Odoo to geolocate phone numbers " "of incoming calls." parser = OptionParser(usage=usage, epilog=epilog, description=description) for option in options: diff --git a/asterisk_click2dial/scripts/set_name_incoming_timeout.sh b/asterisk_click2dial/scripts/set_name_incoming_timeout.sh index 2fa8ebe..296c0eb 100755 --- a/asterisk_click2dial/scripts/set_name_incoming_timeout.sh +++ b/asterisk_click2dial/scripts/set_name_incoming_timeout.sh @@ -29,4 +29,4 @@ PATH=/usr/local/sbin:/usr/local/bin:/var/lib/asterisk/agi-bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/share/asterisk/agi-bin:. -timeout 2s set_name_agi.py --server openerp.mycompany.com --database erp_prod --user-id 12 --password "thepasswd" --geoloc --geoloc-country "FR" --geoloc-lang "fr" +timeout 2s set_name_agi.py --server odoo.mycompany.com --database erp_prod --user-id 12 --password "thepasswd" --geoloc --geoloc-country "FR" --geoloc-lang "fr" diff --git a/asterisk_click2dial/scripts/set_name_outgoing_timeout.sh b/asterisk_click2dial/scripts/set_name_outgoing_timeout.sh index f15a0d1..bb672f3 100755 --- a/asterisk_click2dial/scripts/set_name_outgoing_timeout.sh +++ b/asterisk_click2dial/scripts/set_name_outgoing_timeout.sh @@ -29,4 +29,4 @@ PATH=/usr/local/sbin:/usr/local/bin:/var/lib/asterisk/agi-bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/share/asterisk/agi-bin:. -timeout 2s set_name_agi.py --server openerp.mycompany.com --database erp_prod --user-id 12 --password "thepasswd" --outgoing --outgoing-agi-variable extension +timeout 2s set_name_agi.py --server odoo.mycompany.com --database erp_prod --user-id 12 --password "thepasswd" --outgoing --outgoing-agi-variable extension diff --git a/asterisk_click2dial/static/src/js/asterisk_click2dial.js b/asterisk_click2dial/static/src/js/asterisk_click2dial.js index 61ad788..845b41a 100644 --- a/asterisk_click2dial/static/src/js/asterisk_click2dial.js +++ b/asterisk_click2dial/static/src/js/asterisk_click2dial.js @@ -82,6 +82,8 @@ click2dial.OpenCaller = Widget.extend({ }, }); + /* TODO port to v10: update_promise doesn't existe in + odoo10/addons/web/static/src/js/widgets/user_menu.js UserMenu.include({ do_update: function(){ this._super.apply(this, arguments); @@ -91,6 +93,6 @@ click2dial.OpenCaller = Widget.extend({ asterisk_button.appendTo($('.oe_systray')); }); }, - }); + }); */ }); diff --git a/asterisk_click2dial/static/src/xml/asterisk_click2dial.xml b/asterisk_click2dial/static/src/xml/asterisk_click2dial.xml index b133ce9..0176e2a 100644 --- a/asterisk_click2dial/static/src/xml/asterisk_click2dial.xml +++ b/asterisk_click2dial/static/src/xml/asterisk_click2dial.xml @@ -15,10 +15,12 @@ + diff --git a/asterisk_click2dial/asterisk_server_view.xml b/asterisk_click2dial/views/asterisk_server.xml similarity index 99% rename from asterisk_click2dial/asterisk_server_view.xml rename to asterisk_click2dial/views/asterisk_server.xml index 1226f9c..06e13bb 100644 --- a/asterisk_click2dial/asterisk_server_view.xml +++ b/asterisk_click2dial/views/asterisk_server.xml @@ -5,7 +5,7 @@ --> - + asterisk.server.search @@ -83,5 +83,4 @@ - diff --git a/asterisk_click2dial/res_users_view.xml b/asterisk_click2dial/views/res_users.xml similarity index 99% rename from asterisk_click2dial/res_users_view.xml rename to asterisk_click2dial/views/res_users.xml index f0cc4a9..2b3bc7b 100644 --- a/asterisk_click2dial/res_users_view.xml +++ b/asterisk_click2dial/views/res_users.xml @@ -7,7 +7,7 @@ --> - + asterisk_click2dial.res.users.form @@ -47,5 +47,4 @@ - diff --git a/asterisk_click2dial/web_asterisk_click2dial.xml b/asterisk_click2dial/web_asterisk_click2dial.xml index 9135296..55ffbac 100644 --- a/asterisk_click2dial/web_asterisk_click2dial.xml +++ b/asterisk_click2dial/web_asterisk_click2dial.xml @@ -5,7 +5,6 @@ --> - - diff --git a/base_phone/wizard/number_not_found.py b/base_phone/wizard/number_not_found.py index fa63c34..bea2d90 100644 --- a/base_phone/wizard/number_not_found.py +++ b/base_phone/wizard/number_not_found.py @@ -29,7 +29,7 @@ class NumberNotFound(models.TransientModel): number_type = fields.Selection(selection=[ ('phone', 'Fixed'), ('mobile', 'Mobile') - ], string='Fixed/Mobile', required=True) + ], string='Fixed/Mobile', required=True) to_update_partner_id = fields.Many2one( comodel_name='res.partner', string='Partner to Update', help="Partner on which the phone number will be written") diff --git a/crm_claim_phone/__init__.py b/crm_claim_phone/__init__.py deleted file mode 100644 index 4ce1f5f..0000000 --- a/crm_claim_phone/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# -*- coding: utf-8 -*- - -from . import crm_claim_phone diff --git a/crm_claim_phone/__manifest__.py b/crm_claim_phone/__manifest__.py deleted file mode 100644 index 5b1c43c..0000000 --- a/crm_claim_phone/__manifest__.py +++ /dev/null @@ -1,50 +0,0 @@ -# -*- encoding: utf-8 -*- -############################################################################## -# -# CRM Claim Phone module for Odoo/OpenERP -# Copyright (C) 2014 Alexis de Lattre -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - - -{ - 'name': 'CRM Claim Phone', - 'version': '8.0.0.1.0', - 'category': 'Phone', - 'license': 'AGPL-3', - 'summary': 'Validate phone numbers in CRM Claims', - 'description': """ -CRM Claims Phone -================ - -This module validate phone numbers in the CRM Claim module, just like the -*base_phone* module valide phone numbers in the Partner form. Please refer to -the description of the *base_phone* module for more information. - -This module is independant from the Asterisk connector. - -Please contact Alexis de Lattre from Akretion -for any help or question about this module. -""", - 'author': "Akretion,Odoo Community Association (OCA)", - 'website': 'http://www.akretion.com/', - 'depends': ['base_phone', 'crm_claim'], - 'data': ['crm_claim_view.xml'], - 'images': [], - 'installable': False, - 'auto_install': True, - 'active': False, -} diff --git a/crm_claim_phone/crm_claim_phone.py b/crm_claim_phone/crm_claim_phone.py deleted file mode 100644 index 9a60ae0..0000000 --- a/crm_claim_phone/crm_claim_phone.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -# © 2012-2016 Akretion (Alexis de Lattre ) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from openerp import models -from openerp.addons.base_phone.fields import Phone - - -class CrmClaim(models.Model): - _inherit = 'crm.claim' - - partner_phone = Phone(partner_field='partner_id') diff --git a/crm_claim_phone/crm_claim_view.xml b/crm_claim_phone/crm_claim_view.xml deleted file mode 100644 index 3992163..0000000 --- a/crm_claim_phone/crm_claim_view.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - crm_claim_phone.crm_claim.form - crm.claim - - - - phone - - - - - - diff --git a/crm_phone/__init__.py b/crm_phone/__init__.py index 1ec0331..35e7c96 100644 --- a/crm_phone/__init__.py +++ b/crm_phone/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -from . import crm_phone +from . import models from . import wizard diff --git a/crm_phone/__manifest__.py b/crm_phone/__manifest__.py index 73babbf..a0e9bdc 100644 --- a/crm_phone/__manifest__.py +++ b/crm_phone/__manifest__.py @@ -4,7 +4,7 @@ { 'name': 'CRM Phone', - 'version': '9.0.1.0.0', + 'version': '10.0.1.0.0', 'category': 'Phone', 'license': 'AGPL-3', 'summary': 'Validate phone numbers in CRM', @@ -36,6 +36,6 @@ for any help or question about this module. 'wizard/create_crm_phonecall_view.xml', ], 'demo': ['demo/crm_phonecall.xml'], - 'installable': False, + 'installable': True, 'auto_install': True, } diff --git a/crm_phone/demo/crm_phonecall.xml b/crm_phone/demo/crm_phonecall.xml index 6b79553..8d96e2a 100644 --- a/crm_phone/demo/crm_phonecall.xml +++ b/crm_phone/demo/crm_phonecall.xml @@ -1,6 +1,5 @@ - - + @@ -55,5 +54,4 @@ - diff --git a/crm_phone/models/__init__.py b/crm_phone/models/__init__.py new file mode 100644 index 0000000..2a6bc35 --- /dev/null +++ b/crm_phone/models/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- + +from . import crm_phonecall +from . import crm_lead +from . import res_partner +from . import res_users +from . import phone_common diff --git a/crm_phone/models/crm_lead.py b/crm_phone/models/crm_lead.py new file mode 100644 index 0000000..1e409fb --- /dev/null +++ b/crm_phone/models/crm_lead.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# © 2012-2016 Akretion (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models, fields, api +from odoo.addons.base_phone.fields import Phone, Fax + + +class CrmLead(models.Model): + _inherit = 'crm.lead' + _phone_name_sequence = 20 + + phone = Phone(country_field='country_id', partner_field='partner_id') + mobile = Phone(country_field='country_id', partner_field='partner_id') + fax = Fax(country_field='country_id', partner_field='partner_id') + phonecall_ids = fields.One2many( + 'crm.phonecall', 'opportunity_id', string='Phone Calls') + phonecall_count = fields.Integer( + compute='_count_phonecalls', string='Number of Phonecalls', + readonly=True) + + @api.multi + def name_get(self): + if self._context.get('callerid'): + res = [] + for lead in self: + if lead.partner_name and lead.contact_name: + name = u'%s (%s)' % (lead.contact_name, lead.partner_name) + elif lead.partner_name: + name = lead.partner_name + elif lead.contact_name: + name = lead.contact_name + else: + name = lead.name + res.append((lead.id, name)) + return res + else: + return super(CrmLead, self).name_get() + + @api.multi + @api.depends('phonecall_ids') + def _count_phonecalls(self): + cpo = self.env['crm.phonecall'] + for lead in self: + try: + lead.phonecall_count = cpo.search_count( + [('opportunity_id', '=', lead.id)]) + except: + lead.phonecall_count = 0 diff --git a/crm_phone/crm_phone.py b/crm_phone/models/crm_phonecall.py similarity index 53% rename from crm_phone/crm_phone.py rename to crm_phone/models/crm_phonecall.py index 599c563..504fe16 100644 --- a/crm_phone/crm_phone.py +++ b/crm_phone/models/crm_phonecall.py @@ -2,51 +2,8 @@ # © 2012-2016 Akretion (Alexis de Lattre ) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import models, fields, api, _ -from openerp.addons.base_phone.fields import Phone - - -class CrmLead(models.Model): - _inherit = 'crm.lead' - _phone_name_sequence = 20 - - phone = Phone(country_field='country_id', partner_field='partner_id') - mobile = Phone(country_field='country_id', partner_field='partner_id') - fax = Phone(country_field='country_id', partner_field='partner_id') - phonecall_ids = fields.One2many( - 'crm.phonecall', 'opportunity_id', string='Phone Calls') - phonecall_count = fields.Integer( - compute='_count_phonecalls', string='Number of Phonecalls', - readonly=True) - - @api.multi - def name_get(self): - if self._context.get('callerid'): - res = [] - for lead in self: - if lead.partner_name and lead.contact_name: - name = u'%s (%s)' % (lead.contact_name, lead.partner_name) - elif lead.partner_name: - name = lead.partner_name - elif lead.contact_name: - name = lead.contact_name - else: - name = lead.name - res.append((lead.id, name)) - return res - else: - return super(CrmLead, self).name_get() - - @api.multi - @api.depends('phonecall_ids') - def _count_phonecalls(self): - cpo = self.env['crm.phonecall'] - for lead in self: - try: - lead.phonecall_count = cpo.search_count( - [('opportunity_id', '=', lead.id)]) - except: - lead.phonecall_count = 0 +from odoo import models, fields, api, _ +from odoo.addons.base_phone.fields import Phone class CrmPhonecall(models.Model): @@ -134,52 +91,3 @@ class CrmPhonecall(models.Model): 'context': ctx, } return action - - -class ResPartner(models.Model): - _inherit = 'res.partner' - - phonecall_ids = fields.One2many( - 'crm.phonecall', 'partner_id', string='Phone Calls') - phonecall_count = fields.Integer( - compute='_count_phonecalls', string='Number of Phonecalls', - readonly=True) - - @api.multi - @api.depends('phonecall_ids') - def _count_phonecalls(self): - cpo = self.env['crm.phonecall'] - for partner in self: - try: - partner.phonecall_count = cpo.search_count( - [('partner_id', 'child_of', partner.id)]) - except: - partner.phonecall_count = 0 - - -class ResUsers(models.Model): - _inherit = "res.users" - - # Field name starts with 'context_' to allow modification by the user - # in his preferences, cf server/openerp/addons/base/res/res_users.py - # in "def write()" of "class res_users(osv.osv)" - context_propose_creation_crm_call = fields.Boolean( - string='Propose to create a call in CRM after a click2dial', - default=True) - - -class PhoneCommon(models.AbstractModel): - _inherit = 'phone.common' - - @api.model - def click2dial(self, erp_number): - res = super(PhoneCommon, self).click2dial(erp_number) - if ( - self.env.user.context_propose_creation_crm_call and - self.env.context.get('click2dial_model') - in ('res.partner', 'crm.lead')): - res.update({ - 'action_name': _('Create Call in CRM'), - 'action_model': 'wizard.create.crm.phonecall', - }) - return res diff --git a/crm_phone/models/phone_common.py b/crm_phone/models/phone_common.py new file mode 100644 index 0000000..58ae372 --- /dev/null +++ b/crm_phone/models/phone_common.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# © 2012-2016 Akretion (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models, api, _ + + +class PhoneCommon(models.AbstractModel): + _inherit = 'phone.common' + + @api.model + def click2dial(self, erp_number): + res = super(PhoneCommon, self).click2dial(erp_number) + if ( + self.env.user.context_propose_creation_crm_call and + self.env.context.get('click2dial_model') + in ('res.partner', 'crm.lead')): + res.update({ + 'action_name': _('Create Call in CRM'), + 'action_model': 'wizard.create.crm.phonecall', + }) + return res diff --git a/crm_phone/models/res_partner.py b/crm_phone/models/res_partner.py new file mode 100644 index 0000000..29773a5 --- /dev/null +++ b/crm_phone/models/res_partner.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# © 2012-2016 Akretion (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models, fields, api + + +class ResPartner(models.Model): + _inherit = 'res.partner' + + phonecall_ids = fields.One2many( + 'crm.phonecall', 'partner_id', string='Phone Calls') + phonecall_count = fields.Integer( + compute='_count_phonecalls', string='Number of Phonecalls', + readonly=True) + + @api.multi + @api.depends('phonecall_ids') + def _count_phonecalls(self): + cpo = self.env['crm.phonecall'] + for partner in self: + try: + partner.phonecall_count = cpo.search_count( + [('partner_id', 'child_of', partner.id)]) + except: + partner.phonecall_count = 0 diff --git a/crm_phone/models/res_users.py b/crm_phone/models/res_users.py new file mode 100644 index 0000000..c7dad5b --- /dev/null +++ b/crm_phone/models/res_users.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# © 2012-2016 Akretion (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models, fields + + +class ResUsers(models.Model): + _inherit = "res.users" + + # Field name starts with 'context_' to allow modification by the user + # in his preferences, cf odoo/odoo/addons/base/res/res_users.py + # in "def write()" of "class res_users(osv.osv)" + context_propose_creation_crm_call = fields.Boolean( + string='Propose to create a call in CRM after a click2dial', + default=True) diff --git a/crm_phone/security/ir.model.access.csv b/crm_phone/security/ir.model.access.csv index 826e898..3537707 100644 --- a/crm_phone/security/ir.model.access.csv +++ b/crm_phone/security/ir.model.access.csv @@ -1,5 +1,5 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink callerid_crm_lead_read,Read access on crm.lead,crm.model_crm_lead,base_phone.group_callerid,1,0,0,0 access_crm_phonecall_partner_manager,Full access on crm.phonecall to Contact mgr,model_crm_phonecall,base.group_partner_manager,1,1,1,1 -access_crm_phonecall_sale_manager,Full access on crm.phonecall to Sale mgr,model_crm_phonecall,base.group_sale_manager,1,1,1,1 -access_crm_phonecall_sale_user,Read/Write/Create access on crm.phonecall to Sale users,model_crm_phonecall,base.group_sale_salesman,1,1,1,0 +access_crm_phonecall_sale_manager,Full access on crm.phonecall to Sale mgr,model_crm_phonecall,sales_team.group_sale_manager,1,1,1,1 +access_crm_phonecall_sale_user,Read/Write/Create access on crm.phonecall to Sale users,model_crm_phonecall,sales_team.group_sale_salesman,1,1,1,0 diff --git a/crm_phone/security/phonecall_security.xml b/crm_phone/security/phonecall_security.xml index 42f5554..b100684 100644 --- a/crm_phone/security/phonecall_security.xml +++ b/crm_phone/security/phonecall_security.xml @@ -1,19 +1,18 @@ - - + Personal Phone Calls - + ['|', ('user_id', '=', False), ('user_id', '=', user.id)] All Phone Calls - + [(1, '=', 1)] @@ -24,5 +23,4 @@ - diff --git a/crm_phone/tests/test_crm_phone.py b/crm_phone/tests/test_crm_phone.py index bfed005..831b0ec 100644 --- a/crm_phone/tests/test_crm_phone.py +++ b/crm_phone/tests/test_crm_phone.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- # © 2016 Akretion France (Alexis de Lattre ) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -from openerp.tests.common import TransactionCase +from odoo.tests.common import TransactionCase class TestCRMPhone(TransactionCase): diff --git a/crm_phone/view/crm_lead.xml b/crm_phone/view/crm_lead.xml index 4c1948e..e85496c 100644 --- a/crm_phone/view/crm_lead.xml +++ b/crm_phone/view/crm_lead.xml @@ -5,7 +5,6 @@ --> - @@ -13,12 +12,6 @@ crm.lead - - phone - - - fax -