Browse Source

Fix code style

pull/4/head
Sandy Carter 10 years ago
parent
commit
ce72556fb0
  1. 50
      asterisk_click2dial/__terp__.py
  2. 907
      asterisk_click2dial/asterisk_click2dial.py
  3. 213
      asterisk_click2dial/scripts/get_cid_name.py
  4. 240
      asterisk_click2dial/wizard/open_calling_partner.py
  5. 64
      asterisk_click2dial/wizard/reformat_all_phonenumbers.py
  6. 7
      asterisk_click2dial_crm/__init__.py
  7. 38
      asterisk_click2dial_crm/__terp__.py
  8. 23
      asterisk_click2dial_crm/asterisk_click2dial_crm.py
  9. 4
      asterisk_click2dial_crm/wizard/__init__.py
  10. 54
      asterisk_click2dial_crm/wizard/create_crm_phonecall.py

50
asterisk_click2dial/__terp__.py

@ -27,26 +27,47 @@
'license': 'AGPL-3',
'description': """This module adds 3 functionnalities :
1) It adds a 'dial' button in the partner address form view so that users can directly dial a phone number through Asterisk. This feature is usually known as 'click2dial'. Here is how it works :
. In OpenERP, the user clicks on the 'dial' button next to a phone number field in the partner address view.
. OpenERP connects to the Asterisk Manager Interface and Asterisk makes the user's phone ring.
1) It adds a 'dial' button in the partner address form view so that users can
directly dial a phone number through Asterisk. This feature is usually known
as 'click2dial'. Here is how it works :
. In OpenERP, the user clicks on the 'dial' button next to a phone number field
in the partner address view.
. OpenERP connects to the Asterisk Manager Interface and Asterisk makes the
user's phone ring.
. The user answers his own phone (if he doesn't, the process stops here).
. Asterisk dials the phone number found in OpenERP in place of the user.
. If the remote party answers, the user can talk to his correspondent.
2) It adds the ability to show the name of the calling party on the screen of your IP phone on incoming phone calls if the presented
phone number is present in the partner addresses of OpenERP. Here is how it works :
. On incoming phone calls, the Asterisk dialplan executes an AGI script "get_cid_name_timeout.sh".
. The "get_cid_name_timeout.sh" script calls the "get_cid_name.py" script with a short timeout.
. The "get_cid_name.py" script will make an XML-RPC request on the OpenERP server to try to find the name of the person corresponding to the phone number presented by the calling party.
. If it finds the name, it is set as the CallerID name of the call, so as to be presented on the IP phone of the user.
2) It adds the ability to show the name of the calling party on the screen of
your IP phone on incoming phone calls if the presented phone number is
present in the partner addresses of OpenERP. Here is how it works :
. On incoming phone calls, the Asterisk dialplan executes an AGI script
"get_cid_name_timeout.sh".
. The "get_cid_name_timeout.sh" script calls the "get_cid_name.py" script with
a short timeout.
. The "get_cid_name.py" script will make an XML-RPC request on the OpenERP
server to try to find the name of the person corresponding to the phone
number presented by the calling party.
. If it finds the name, it is set as the CallerID name of the call, so as to be
presented on the IP phone of the user.
3) It adds a button "Open calling partner" in the menu "Sales > Address book" to get the partner corresponding to the calling party in one click. Here is how it works :
. When the user clicks on the "Open calling partner" button, OpenERP sends a query to the Asterisk Manager Interface to get a list of the current phone calls
. If it finds a phone call involving the user's phone, it gets the phone number of the calling party
. It searches the phone number of the calling party in the Partner addresses of OpenERP. If a record matches, it shows the name of the related Partner and proposes to open it, or open its related sale orders or invoices. If no record matches, it proposes to create a new Contact with the presented phone number as 'Phone' or 'Mobile' number or update an existing Contact.
3) It adds a button "Open calling partner" in the menu "Sales > Address book"
to get the partner corresponding to the calling party in one click.
Here is how it works :
. When the user clicks on the "Open calling partner" button, OpenERP sends a
query to the Asterisk Manager Interface to get a list of the current phone
calls
. If it finds a phone call involving the user's phone, it gets the phone number
of the calling party
. It searches the phone number of the calling party in the Partner addresses of
OpenERP. If a record matches, it shows the name of the related Partner and
proposes to open it, or open its related sale orders or invoices. If no
record matches, it proposes to create a new Contact with the presented phone
number as 'Phone' or 'Mobile' number or update an existing Contact.
A detailed documentation for this module is available on the Akretion Web site : http://www.akretion.com/en/products-and-services/openerp-asterisk-voip-connector """,
A detailed documentation for this module is available on the Akretion Web site:
http://www.akretion.com/products-and-services/openerp-asterisk-voip-connector
""",
'author': 'Akretion',
'website': 'http://www.akretion.com/',
'depends': ['base'],
@ -63,4 +84,3 @@ A detailed documentation for this module is available on the Akretion Web site :
'installable': True,
'active': False,
}

907
asterisk_click2dial/asterisk_click2dial.py
File diff suppressed because it is too large
View File

213
asterisk_click2dial/scripts/get_cid_name.py

@ -5,9 +5,10 @@
When executed from the dialplan on an incoming phone call, it will lookup in
OpenERP's partners, 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.
corresponding name of the person and use this name as CallerID name for the
incoming call.
Requires the "asterisk_click2dial" module
Requires the "asterisk_click2dial" module
available from https://code.launchpad.net/openerp-asterisk-connector
for OpenERP version >= 5.0
@ -36,8 +37,9 @@
This user only needs to be part of the group "Asterisk CallerID", which has
read access on the 'res.partner.address' object, nothing more.
Note that this script can be used without OpenERP, with just the geolocalisation
feature : for that, don't use option --server ; only use --geoloc
Note that this script can be used without OpenERP, with just the
geolocalisation feature : for that, don't use option --server ; only use
--geoloc
"""
__author__ = "Alexis de Lattre <alexis.delattre@akretion.com>"
@ -69,18 +71,103 @@ from optparse import OptionParser
default_cid_name = "Not in OpenERP"
# Define command line options
option_server = {'names': ('-s', '--server'), 'dest': 'server', 'type': 'string', 'help': 'DNS or IP address of the OpenERP server. Default = none (will not try to connect to OpenERP)', 'action': 'store', 'default': False}
option_port = {'names': ('-p', '--port'), 'dest': 'port', 'type': 'int', 'help': "Port of OpenERP's XML-RPC interface. Default = 8069", 'action': 'store', 'default': 8069}
option_ssl = {'names': ('-e', '--ssl'), 'dest': 'ssl', 'help': "Use XML-RPC secure i.e. with SSL instead of clear XML-RPC. Default = no, use clear XML-RPC", 'action': 'store_true', 'default': False}
option_database = {'names': ('-d', '--database'), 'dest': 'database', 'type': 'string', 'help': "OpenERP database name. Default = 'openerp'", 'action': 'store', 'default': 'openerp'}
option_user = {'names': ('-u', '--user-id'), 'dest': 'user', 'type': 'int', 'help': "OpenERP user ID to use when connecting to OpenERP. Default = 2", 'action': 'store', 'default': 2}
option_password = {'names': ('-w', '--password'), 'dest': 'password', 'type': 'string', 'help': "Password of the OpenERP user. Default = 'demo'", 'action': 'store', 'default': 'demo'}
option_ascii = {'names': ('-a', '--ascii'), 'dest': 'ascii', 'help': "Convert name from UTF-8 to ASCII. Default = no, keep UTF-8", 'action': 'store_true', 'default': False}
option_geoloc = {'names': ('-g', '--geoloc'), 'dest': 'geoloc', 'help': "Try to geolocate phone numbers unknown to OpenERP. This features requires the 'phonenumbers' Python lib. To install it, run 'sudo pip install phonenumbers' Default = no", 'action': 'store_true', 'default': False}
option_geoloc_lang = {'names': ('-l', '--geoloc-lang'), 'dest': 'lang', 'help': "Language in which the name of the country and city name will be displayed by the geolocalisation database. Use the 2 letters ISO code of the language. Default = 'en'", 'action': 'store', 'default': "en"}
option_geoloc_country = {'names': ('-c', '--geoloc-country'), 'dest': 'country', 'help': "2 letters ISO code for your country e.g. 'FR' for France. This will be used by the geolocalisation system to parse the phone number of the calling party. Default = 'FR'", 'action': 'store', 'default': "FR"}
options = [option_server, option_port, option_ssl, option_database, option_user, option_password, option_ascii, option_geoloc, option_geoloc_lang, option_geoloc_country]
option_server = {
'names': ('-s', '--server'),
'dest': 'server',
'type': 'string',
'help': 'DNS or IP address of the OpenERP server. Default = none '
'(will not try to connect to OpenERP)',
'action': 'store',
'default': False,
}
option_port = {
'names': ('-p', '--port'),
'dest': 'port',
'type': 'int',
'help': "Port of OpenERP's XML-RPC interface. Default = 8069",
'action': 'store',
'default': 8069,
}
option_ssl = {
'names': ('-e', '--ssl'),
'dest': 'ssl',
'help': "Use XML-RPC secure i.e. with SSL instead of clear XML-RPC. "
"Default = no, use clear XML-RPC",
'action': 'store_true',
'default': False,
}
option_database = {
'names': ('-d', '--database'),
'dest': 'database',
'type': 'string',
'help': "OpenERP database name. Default = 'openerp'",
'action': 'store',
'default': 'openerp',
}
option_user = {
'names': ('-u', '--user-id'),
'dest': 'user',
'type': 'int',
'help': "OpenERP user ID to use when connecting to OpenERP. Default = 2",
'action': 'store',
'default': 2,
}
option_password = {
'names': ('-w', '--password'),
'dest': 'password',
'type': 'string',
'help': "Password of the OpenERP user. Default = 'demo'",
'action': 'store',
'default': 'demo',
}
option_ascii = {
'names': ('-a', '--ascii'),
'dest': 'ascii',
'help': "Convert name from UTF-8 to ASCII. Default = no, keep UTF-8",
'action': 'store_true',
'default': False,
}
option_geoloc = {
'names': ('-g', '--geoloc'),
'dest': 'geoloc',
'help': "Try to geolocate phone numbers unknown to OpenERP. This features "
"requires the 'phonenumbers' Python lib. To install it, run "
"'sudo pip install phonenumbers' Default = no",
'action': 'store_true',
'default': False,
}
option_geoloc_lang = {
'names': ('-l', '--geoloc-lang'),
'dest': 'lang',
'help': "Language in which the name of the country and city name will be "
"displayed by the geolocalisation database. Use the 2 letters ISO "
"code of the language. Default = 'en'",
'action': 'store',
'default': "en",
}
option_geoloc_country = {
'names': ('-c', '--geoloc-country'),
'dest': 'country',
'help': "2 letters ISO code for your country e.g. 'FR' for France. This "
"will be used by the geolocalisation system to parse the phone "
"number of the calling party. Default = 'FR'",
'action': 'store',
'default': "FR",
}
options = [
option_server,
option_port,
option_ssl,
option_database,
option_user,
option_password,
option_ascii,
option_geoloc,
option_geoloc_lang,
option_geoloc_country,
]
def stdout_write(string):
'''Wrapper on sys.stdout.write'''
@ -89,51 +176,62 @@ def stdout_write(string):
# When we output a command, we get an answer "200 result=1" on stdin
# Purge stdin to avoid these Asterisk error messages :
# utils.c ast_carefulwrite: write() returned error: Broken pipe
input_line = sys.stdin.readline()
sys.stdin.readline()
return True
def stderr_write(string):
'''Wrapper on sys.stderr.write'''
sys.stderr.write(string.encode(sys.stdout.encoding or 'utf-8', 'replace'))
sys.stdout.flush()
return True
def geolocate_phone_number(number, my_country_code, lang):
import phonenumbers
import phonenumbers.geocoder
res = ''
phonenum = phonenumbers.parse(number, my_country_code.upper())
city = phonenumbers.area_description_for_number(phonenum, lang.lower())
#country = phonenumbers.country_name_for_number(phonenum, lang.lower())
# country = phonenumbers.country_name_for_number(phonenum, lang.lower())
country_code = phonenumbers.region_code_for_number(phonenum)
if country_code == my_country_code.upper():
# We don't display the country name when it's my own country
# We don't display the country name when it's my own country
if city:
res = city
else:
# Convert country code to country name
country = phonenumbers.geocoder._region_display_name(country_code, lang.lower())
country = phonenumbers.geocoder._region_display_name(
country_code, lang.lower()
)
if country and city:
res = country + ' ' + city
elif country and not city:
res = country
return res
def convert_to_ascii(my_unicode):
'''Convert to ascii, with clever management of accents (é -> e, è -> e)'''
"""Convert to ascii, with clever management of accents (é -> e, è -> e)"""
import unicodedata
if isinstance(my_unicode, unicode):
my_unicode_with_ascii_chars_only = ''.join((char for char in unicodedata.normalize('NFD', my_unicode) if unicodedata.category(char) != 'Mn'))
my_unicode_with_ascii_chars_only = ''.join(
(char
for char in unicodedata.normalize('NFD', my_unicode)
if unicodedata.category(char) != 'Mn')
)
return str(my_unicode_with_ascii_chars_only)
# If the argument is already of string type, we return it with the same value
# If the argument is already of string type, we return it with the same
# value
elif isinstance(my_unicode, str):
return my_unicode
else:
return False
def main(options, arguments):
#print 'options = %s' % options
#print 'arguments = %s' % arguments
# print 'options = %s' % options
# print 'arguments = %s' % arguments
# AGI passes parameters to the script on standard input
stdinput = {}
@ -145,8 +243,8 @@ def main(options, arguments):
try:
variable, value = line.split(':')
except:
break
if variable[:4] != 'agi_': # All AGI parameters start with 'agi_'
break
if variable[:4] != 'agi_': # All AGI parameters start with 'agi_'
stderr_write("bad stdin variable : %s\n" % variable)
continue
variable = variable.strip()
@ -161,8 +259,12 @@ def main(options, arguments):
# 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
if stdinput.get('agi_calleridname') and not stdinput.get('agi_calleridname').isdigit() and stdinput.get('agi_calleridname').lower() not in ['asterisk', 'unknown', 'anonymous']:
stdout_write('VERBOSE "Incoming CallerID name is %s"\n' % stdinput.get('agi_calleridname'))
if (stdinput.get('agi_calleridname')
and not stdinput.get('agi_calleridname').isdigit()
and stdinput.get('agi_calleridname').lower()
not in ['asterisk', 'unknown', 'anonymous']):
stdout_write('VERBOSE "Incoming CallerID name is %s"\n'
% stdinput.get('agi_calleridname'))
stdout_write('VERBOSE "As it is a real name, we do not change it"\n')
return True
@ -175,24 +277,42 @@ def main(options, arguments):
# Match for particular cases and anonymous phone calls
# To test anonymous call in France, dial 3651 + number
if not input_cid_number.isdigit():
stdout_write('VERBOSE "CallerID number (%s) is not a digit"\n' % input_cid_number)
stdout_write('VERBOSE "CallerID number (%s) is not a digit"\n'
% input_cid_number)
exit(0)
stdout_write('VERBOSE "CallerID number = %s"\n' % input_cid_number)
res = False
if options.server: # Yes, this script can be used without "-s openerp_server" !
# Yes, this script can be used without "-s openerp_server" !
if options.server:
if options.ssl:
stdout_write('VERBOSE "Starting XML-RPC secure request on OpenERP %s:%s"\n' % (options.server, str(options.port)))
stdout_write(
'VERBOSE "Starting XML-RPC secure request on OpenERP %s:%s"\n'
% (options.server, str(options.port))
)
protocol = 'https'
else:
stdout_write('VERBOSE "Starting clear XML-RPC request on OpenERP %s:%s"\n' % (options.server, str(options.port)))
stdout_write(
'VERBOSE "Starting clear XML-RPC request on OpenERP %s:%s"\n'
% (options.server, str(options.port))
)
protocol = 'http'
sock = xmlrpclib.ServerProxy('%s://%s:%s/xmlrpc/object' % (protocol, options.server, str(options.port)))
sock = xmlrpclib.ServerProxy(
'%s://%s:%s/xmlrpc/object'
% (protocol, options.server, str(options.port))
)
try:
res = sock.execute(options.database, options.user, options.password, 'res.partner.address', 'get_name_from_phone_number', input_cid_number)
res = sock.execute(
options.database,
options.user,
options.password,
'res.partner.address',
'get_name_from_phone_number',
input_cid_number
)
stdout_write('VERBOSE "End of XML-RPC request on OpenERP"\n')
if not res:
stdout_write('VERBOSE "Phone number not found in OpenERP"\n')
@ -200,8 +320,8 @@ def main(options, arguments):
stdout_write('VERBOSE "Could not connect to OpenERP"\n')
res = False
# To simulate a long execution of the XML-RPC request
#import time
#time.sleep(5)
# import time
# time.sleep(5)
# Function to limit the size of the CID name to 40 chars
if res:
@ -209,14 +329,23 @@ def main(options, arguments):
res = res[0:40]
elif options.geoloc:
# if the number is not found in OpenERP, 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(input_cid_number, options.country, options.lang)
stdout_write(
'VERBOSE "Trying to geolocate with country %s and lang %s"\n'
% (options.country, options.lang)
)
res = geolocate_phone_number(
input_cid_number,
options.country,
options.lang
)
else:
# if the number is not found in OpenERP and geoloc is off, we put 'default_cid_name' as CID Name
# if the number is not found in OpenERP and geoloc is off,
# we put 'default_cid_name' as CID Name
res = default_cid_name
# All SIP phones should support UTF-8... but in case you have analog phones over TDM
# or buggy phones, you should use the command line option --ascii
# All SIP phones should support UTF-8... but in case you have analog
# phones over TDM or buggy phones, you should use the command line option
# --ascii
if options.ascii:
res = convert_to_ascii(res)

240
asterisk_click2dial/wizard/open_calling_partner.py

@ -33,27 +33,62 @@ class wizard_open_calling_partner(osv.osv_memory):
_columns = {
# I can't set any field to readonly, because otherwize it would call
# default_get (and thus connect to Asterisk) a second time when the user
# clicks on one of the buttons
'calling_number': fields.char('Calling number', size=30, help="Phone number of calling party that has been obtained from Asterisk."),
'partner_address_id': fields.many2one('res.partner.address', 'Contact name', help="Partner contact related to the calling number. If there is none and you want to update an existing partner"),
'partner_id': fields.many2one('res.partner', 'Partner', help="Partner related to the calling number."),
'to_update_partner_address_id': fields.many2one('res.partner.address', 'Contact to update', help="Partner contact on which the phone or mobile number will be written"),
'current_phone': fields.related('to_update_partner_address_id', 'phone', type='char', relation='res.partner.address', string='Current phone'),
'current_mobile': fields.related('to_update_partner_address_id', 'mobile', type='char', relation='res.partner.address', string='Current mobile'),
}
# default_get (and thus connect to Asterisk) a second time when the
# user clicks on one of the buttons
'calling_number': fields.char(
'Calling number',
size=30,
help="Phone number of calling party that has been obtained from "
"Asterisk."
),
'partner_address_id': fields.many2one(
'res.partner.address',
'Contact name',
help="Partner contact related to the calling number. If there is "
"none and you want to update an existing partner"
),
'partner_id': fields.many2one(
'res.partner',
'Partner',
help="Partner related to the calling number."
),
'to_update_partner_address_id': fields.many2one(
'res.partner.address',
'Contact to update',
help="Partner contact on which the phone or mobile number will be "
"written"
),
'current_phone': fields.related(
'to_update_partner_address_id',
'phone',
type='char',
relation='res.partner.address',
string='Current phone'
),
'current_mobile': fields.related(
'to_update_partner_address_id',
'mobile',
type='char',
relation='res.partner.address',
string='Current mobile'
),
}
def default_get(self, cr, uid, fields, context=None):
'''Thanks to the default_get method, we are able to query Asterisk and
get the corresponding partner when we launch the wizard'''
"""Thanks to the default_get method, we are able to query Asterisk and
get the corresponding partner when we launch the wizard"""
res = {}
calling_number = self.pool.get('asterisk.server')._get_calling_number(cr, uid, context=context)
#To test the code without Asterisk server
#calling_number = "0141981242"
calling_number = self.pool.get('asterisk.server')._get_calling_number(
cr, uid, context=context
)
# To test the code without Asterisk server
# calling_number = "0141981242"
if calling_number:
res['calling_number'] = calling_number
partner = self.pool.get('res.partner.address').get_partner_from_phone_number(cr, uid, calling_number, context=context)
address_obj = self.pool.get('res.partner.address')
partner = address_obj.get_partner_from_phone_number(
cr, uid, calling_number, context=context
)
if partner:
res['partner_address_id'] = partner[0]
res['partner_id'] = partner[1]
@ -62,22 +97,38 @@ class wizard_open_calling_partner(osv.osv_memory):
res['partner_address_id'] = False
res['to_update_partner_address_id'] = False
else:
logger.notifyChannel('click2dial', netsvc.LOG_DEBUG, "Could not get the calling number from Asterisk.")
raise osv.except_osv(_('Error :'), _("Could not get the calling number from Asterisk. Are you currently on the phone ? If yes, check your setup and look at the OpenERP debug logs."))
logger.notifyChannel(
'click2dial',
netsvc.LOG_DEBUG,
"Could not get the calling number from Asterisk."
)
raise osv.except_osv(
_('Error :'),
_("Could not get the calling number from Asterisk. "
"Are you currently on the phone ? If yes, check your setup "
"and look at the OpenERP debug logs.")
)
return res
def open_filtered_object(self, cr, uid, ids, oerp_object, context=None):
'''Returns the action that opens the list view of the 'oerp_object'
given as argument filtered on the partner'''
"""Returns the action that opens the list view of the 'oerp_object'
given as argument filtered on the partner"""
# This module only depends on "base"
# and I don't want to add a dependancy on "sale" or "account"
# So I just check here that the model exists, to avoid a crash
if not self.pool.get('ir.model').search(cr, uid, [('model', '=', oerp_object._name)], context=context):
raise osv.except_osv(_('Error :'), _("The object '%s' is not found in your OpenERP database, probably because the related module is not installed." % oerp_object._description))
partner_id = self.browse(cr, uid, ids[0], context=context).partner_id.id
if not self.pool.get('ir.model').search(
cr, uid, [('model', '=', oerp_object._name)], context=context
):
raise osv.except_osv(
_('Error :'),
_("The object '%s' is not found in your OpenERP database, "
"probably because the related module is not installed."
% oerp_object._description)
)
partner_id = self.browse(
cr, uid, ids[0], context=context).partner_id.id
if partner_id:
action = {
'name': oerp_object._description,
@ -85,34 +136,41 @@ class wizard_open_calling_partner(osv.osv_memory):
'view_mode': 'tree,form',
'res_model': oerp_object._name,
'type': 'ir.actions.act_window',
'nodestroy': False, # close the pop-up wizard after action
'nodestroy': False, # close the pop-up wizard after action
'target': 'current',
'domain': [('partner_id', '=', partner_id)],
}
}
return action
else:
return False
def open_sale_orders(self, cr, uid, ids, context=None):
'''Function called by the related button of the wizard'''
return self.open_filtered_object(cr, uid, ids, self.pool.get('sale.order'), context=context)
"""Function called by the related button of the wizard"""
return self.open_filtered_object(
cr, uid, ids, self.pool.get('sale.order'), context=context
)
def open_invoices(self, cr, uid, ids, context=None):
'''Function called by the related button of the wizard'''
return self.open_filtered_object(cr, uid, ids, self.pool.get('account.invoice'), context=context)
"""Function called by the related button of the wizard"""
return self.open_filtered_object(
cr, uid, ids, self.pool.get('account.invoice'), context=context
)
def simple_open(self, cr, uid, ids, object_name='res.partner', context=None):
def simple_open(self, cr, uid, ids, object_name='res.partner',
context=None):
if object_name == 'res.partner':
record_id = self.browse(cr, uid, ids[0], context=context).partner_id.id
record_id = self.browse(
cr, uid, ids[0], context=context).partner_id.id
label = 'Partner'
elif object_name == 'res.partner.address':
record_id = self.browse(cr, uid, ids[0], context=context).partner_address_id.id
record_id = self.browse(
cr, uid, ids[0], context=context).partner_address_id.id
label = 'Contact'
else:
raise osv.except_osv(_('Error :'), "This object '%s' is not supported" % object_name)
raise osv.except_osv(
_('Error :'),
"This object '%s' is not supported" % object_name
)
if record_id:
return {
'name': label,
@ -120,30 +178,39 @@ class wizard_open_calling_partner(osv.osv_memory):
'view_mode': 'form,tree',
'res_model': object_name,
'type': 'ir.actions.act_window',
'nodestroy': False, # close the pop-up wizard after action
'nodestroy': False, # close the pop-up wizard after action
'target': 'current',
'res_id': record_id,
}
}
else:
return False
def open_partner(self, cr, uid, ids, context=None):
'''Function called by the related button of the wizard'''
return self.simple_open(cr, uid, ids, object_name='res.partner', context=context)
"""Function called by the related button of the wizard"""
return self.simple_open(
cr, uid, ids, object_name='res.partner', context=context
)
def open_partner_address(self, cr, uid, ids, context=None):
'''Function called by the related button of the wizard'''
return self.simple_open(cr, uid, ids, object_name='res.partner.address', context=context)
def create_partner_address(self, cr, uid, ids, phone_type='phone', context=None):
'''Function called by the related button of the wizard'''
calling_number = self.read(cr, uid, ids[0], ['calling_number'], context=context)['calling_number']
ast_server = self.pool.get('asterisk.server')._get_asterisk_server_from_user(cr, uid, context=context)
"""Function called by the related button of the wizard"""
return self.simple_open(
cr, uid, ids, object_name='res.partner.address', context=context
)
def create_partner_address(self, cr, uid, ids, phone_type='phone',
context=None):
"""Function called by the related button of the wizard"""
calling_number = self.read(
cr, uid, ids[0], ['calling_number'], context=context
)['calling_number']
ast_srv_obj = self.pool.get('asterisk.server')
ast_server = ast_srv_obj._get_asterisk_server_from_user(
cr, uid, context=context
)
# Convert the number to the international format
number_to_write = self.pool.get('asterisk.server')._convert_number_to_international_format(cr, uid, calling_number, ast_server, context=context)
number_to_write = ast_srv_obj._convert_number_to_international_format(
cr, uid, calling_number, ast_server, context=context
)
context['default_' + phone_type] = number_to_write
action = {
@ -158,22 +225,36 @@ class wizard_open_calling_partner(osv.osv_memory):
}
return action
def create_partner_address_phone(self, cr, uid, ids, context=None):
return self.create_partner_address(cr, uid, ids, phone_type='phone', context=context)
return self.create_partner_address(
cr, uid, ids, phone_type='phone', context=context
)
def create_partner_address_mobile(self, cr, uid, ids, context=None):
return self.create_partner_address(cr, uid, ids, phone_type='mobile', context=context)
return self.create_partner_address(
cr, uid, ids, phone_type='mobile', context=context
)
def update_partner_address(self, cr, uid, ids, phone_type='mobile', context=None):
def update_partner_address(self, cr, uid, ids, phone_type='mobile',
context=None):
cur_wizard = self.browse(cr, uid, ids[0], context=context)
if not cur_wizard.to_update_partner_address_id:
raise osv.except_osv(_('Error :'), _("Select the contact to update."))
ast_server = self.pool.get('asterisk.server')._get_asterisk_server_from_user(cr, uid, context=context)
number_to_write = self.pool.get('asterisk.server')._convert_number_to_international_format(cr, uid, cur_wizard.calling_number, ast_server, context=context)
self.pool.get('res.partner.address').write(cr, uid, cur_wizard.to_update_partner_address_id.id, {phone_type: number_to_write}, context=context)
raise osv.except_osv(
_('Error :'),
_("Select the contact to update.")
)
ast_srv_obj = self.pool.get('asterisk.server')
ast_server = ast_srv_obj._get_asterisk_server_from_user(
cr, uid, context=context
)
number_to_write = ast_srv_obj._convert_number_to_international_format(
cr, uid, cur_wizard.calling_number, ast_server, context=context
)
self.pool.get('res.partner.address').write(
cr, uid, cur_wizard.to_update_partner_address_id.id,
{phone_type: number_to_write},
context=context
)
action = {
'name': 'Contact: ' + cur_wizard.to_update_partner_address_id.name,
'view_type': 'form',
@ -183,27 +264,38 @@ class wizard_open_calling_partner(osv.osv_memory):
'nodestroy': False,
'target': 'current',
'res_id': cur_wizard.to_update_partner_address_id.id
}
}
return action
def update_partner_address_phone(self, cr, uid, ids, context=None):
return self.update_partner_address(cr, uid, ids, phone_type='phone', context=context)
return self.update_partner_address(
cr, uid, ids, phone_type='phone', context=context
)
def update_partner_address_mobile(self, cr, uid, ids, context=None):
return self.update_partner_address(cr, uid, ids, phone_type='mobile', context=context)
return self.update_partner_address(
cr, uid, ids, phone_type='mobile', context=context
)
def onchange_to_update_partner_address(self, cr, uid, ids, to_update_partner_address_id, context=None):
def onchange_to_update_partner_address(self, cr, uid, ids,
to_update_partner_address_id,
context=None):
res = {}
res['value'] = {}
if to_update_partner_address_id:
to_update_partner_address = self.pool.get('res.partner.address').browse(cr, uid, to_update_partner_address_id, context=context)
res['value'].update({'current_phone': to_update_partner_address.phone,
'current_mobile': to_update_partner_address.mobile})
address_obj = self.pool.get('res.partner.address')
to_update_partner_address = address_obj.browse(
cr, uid, to_update_partner_address_id, context=context
)
res['value'].update({
'current_phone': to_update_partner_address.phone,
'current_mobile': to_update_partner_address.mobile
})
else:
res['value'].update({'current_phone': False, 'current_mobile': False})
res['value'].update({
'current_phone': False,
'current_mobile': False,
})
return res
wizard_open_calling_partner()

64
asterisk_click2dial/wizard/reformat_all_phonenumbers.py

@ -21,7 +21,6 @@
from osv import osv, fields
import netsvc
from tools.translate import _
logger = netsvc.Logger()
@ -29,39 +28,72 @@ logger = netsvc.Logger()
class reformat_all_phonenumbers(osv.osv_memory):
_name = "reformat.all.phonenumbers"
_description = "Reformat all phone numbers"
_columns = {
'phonenumbers_not_reformatted': fields.text("Phone numbers that couldn't be reformatted"),
}
'phonenumbers_not_reformatted': fields.text(
"Phone numbers that couldn't be reformatted"
),
}
def run_reformat_all_phonenumbers(self, cr, uid, ids, context=None):
print "RUN ids=", ids
addr_obj = self.pool.get('res.partner.address')
phonefields = ['phone', 'fax', 'mobile']
logger.notifyChannel('click2dial', netsvc.LOG_INFO, 'Starting to reformat all the phone numbers')
all_addr_ids = addr_obj.search(cr, uid, ['|', ('active', '=', True), ('active', '=', False)], context=context)
logger.notifyChannel(
'click2dial',
netsvc.LOG_INFO,
'Starting to reformat all the phone numbers'
)
all_addr_ids = addr_obj.search(
cr, uid, [
'|',
('active', '=', True),
('active', '=', False)
], context=context
)
phonenumbers_not_reformatted = ''
for addr in addr_obj.read(cr, uid, all_addr_ids, ['name'] + phonefields, context=context):
for addr in addr_obj.read(
cr, uid, all_addr_ids, ['name'] + phonefields, context=context
):
init_addr = addr.copy()
# addr is _updated_ by the fonction _reformat_phonenumbers()
try:
addr_obj._reformat_phonenumbers(cr, uid, addr, context=context)
except Exception, e:
#raise osv.except_osv(_('Error :'), _("Problem on partner contact '%s'. Error message: %s" % (init_addr.get('name'), e[1])))
phonenumbers_not_reformatted += "Problem on partner contact '%s'. Error message: %s" % (init_addr.get('name'), e[1]) + "\n"
logger.notifyChannel('click2dial', netsvc.LOG_WARNING, "Problem on partner contact '%s'. Error message: %s" % (init_addr.get('name'), e[1]))
phonenumbers_not_reformatted += (
"Problem on partner contact '%s'. "
"Error message: %s" % (init_addr.get('name'), e[1]) + "\n"
)
logger.notifyChannel(
'click2dial',
netsvc.LOG_WARNING,
"Problem on partner contact '%s'. Error message: %s"
% (init_addr.get('name'), e[1])
)
continue
# Test if the phone numbers have been changed
if any([init_addr.get(field) != addr.get(field) for field in phonefields]):
if any([init_addr.get(field) != addr.get(field)
for field in phonefields]):
addr.pop('id')
addr.pop('name')
logger.notifyChannel('click2dial', netsvc.LOG_INFO, 'Reformating phone number: FROM %s TO %s' % (unicode(init_addr), unicode(addr)))
logger.notifyChannel(
'click2dial',
netsvc.LOG_INFO,
'Reformating phone number: FROM %s TO %s'
% (unicode(init_addr), unicode(addr))
)
addr_obj.write(cr, uid, init_addr['id'], addr, context=context)
if not phonenumbers_not_reformatted:
phonenumbers_not_reformatted = 'All phone numbers have been reformatted successfully.'
self.write(cr, uid, ids, {'phonenumbers_not_reformatted': phonenumbers_not_reformatted}, context=context)
logger.notifyChannel('click2dial', netsvc.LOG_INFO, 'End of the phone number reformatting wizard')
phonenumbers_not_reformatted = (
'All phone numbers have been reformatted successfully.'
)
self.write(cr, uid, ids, {
'phonenumbers_not_reformatted': phonenumbers_not_reformatted
}, context=context)
logger.notifyChannel(
'click2dial',
netsvc.LOG_INFO,
'End of the phone number reformatting wizard'
)
return True
reformat_all_phonenumbers()

7
asterisk_click2dial_crm/__init__.py

@ -2,14 +2,15 @@
##############################################################################
#
# Asterisk click2dial CRM module for OpenERP
# Copyright (c) 2011 Zikzakmedia S.L. (http://zikzakmedia.com) All Rights Reserved.
# Copyright (c) 2011 Zikzakmedia S.L. (http://zikzakmedia.com)
# All Rights Reserved.
# Copyright (c) 2012 Akretion (http://www.akretion.com)
# @author: Jesús Martín <jmartin@zikzakmedia.com>
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
#
# 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
# 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,

38
asterisk_click2dial_crm/__terp__.py

@ -2,14 +2,15 @@
##############################################################################
#
# Asterisk click2dial CRM module for OpenERP
# Copyright (c) 2011 Zikzakmedia S.L. (http://zikzakmedia.com) All Rights Reserved.
# Copyright (c) 2011 Zikzakmedia S.L. (http://zikzakmedia.com)
# All Rights Reserved.
# Copyright (c) 2012 Akretion (http://www.akretion.com)
# @author: Jesús Martín <jmartin@zikzakmedia.com>
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
#
# 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
# 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,
@ -27,27 +28,30 @@
"version": "0.1",
"author": "Zikzakmedia SL, Akretion",
"website": "http://www.zikzakmedia.com",
"license" : "AGPL-3",
"license": "AGPL-3",
"category": "Customer Relationship Management",
"description": """
This module adds CRM-specific features to the asterisk_click2dial module.
This module adds CRM-specific features to the asterisk_click2dial module.
It adds 2 features :
It adds 2 features :
First, when you do a click2dial, OpenERP will propose you to create an
outbound phone call in the CRM ; if you answer 'Yes', it will create the phone
call in the CRM and open it in a new tab. If some users don't want to be asked
to create a phone call in the CRM each time they do a click2dial, you
should disable the corresponding option in the 'Telephony' tab of the 'User'
form.
First, when you do a click2dial, OpenERP will propose you to create an
outbound phone call in the CRM ; if you answer 'Yes', it will create the phone
call in the CRM and open it in a new tab. If some users don't want to be asked
to create a phone call in the CRM each time they do a click2dial, you
should disable the corresponding option in the 'Telephony' tab of the 'User'
form.
Second, when you receive a phone call and run the wizard "Open calling partner",
if the partner is found in OpenERP, you will see a button that proposes to create
an inbound phone call in the CRM.
Second, when you receive a phone call and run the wizard
"Open calling partner", if the partner is found in OpenERP, you will see a
button that proposes to create an inbound phone call in the CRM.
This module has been initially developped by Zikzakmedia and has been enhanced by Akretion.
This module has been initially developped by Zikzakmedia and has been enhanced
by Akretion.
A detailed documentation for the OpenERP-Asterisk connector is available on the Akretion Web site : http://www.akretion.com/en/products-and-services/openerp-asterisk-voip-connector
A detailed documentation for the OpenERP-Asterisk connector is available on the
Akretion Web site :
http://www.akretion.com/products-and-services/openerp-asterisk-voip-connector
""",
"depends": [
'asterisk_click2dial',

23
asterisk_click2dial_crm/asterisk_click2dial_crm.py

@ -2,14 +2,15 @@
##############################################################################
#
# Asterisk click2dial CRM module for OpenERP
# Copyright (c) 2011 Zikzakmedia S.L. (http://zikzakmedia.com) All Rights Reserved.
# Copyright (c) 2011 Zikzakmedia S.L. (http://zikzakmedia.com)
# All Rights Reserved.
# Copyright (c) 2012 Akretion (http://www.akretion.com)
# @author: Jesús Martín <jmartin@zikzakmedia.com>
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
#
# 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
# 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,
@ -23,8 +24,7 @@
##############################################################################
from osv import osv, fields
# Lib to translate error messages
from tools.translate import _
class res_partner_address(osv.osv):
_inherit = "res.partner.address"
@ -37,7 +37,9 @@ class res_partner_address(osv.osv):
'''
if context is None:
context = {}
super(res_partner_address, self).dial(cr, uid, ids, phone_field=phone_field, context=context)
super(res_partner_address, self).dial(
cr, uid, ids, phone_field=phone_field, context=context
)
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
context['partner_address_id'] = ids[0]
action_start_wizard = {
@ -66,12 +68,13 @@ class res_users(osv.osv):
# in his preferences, cf server/openerp/addons/base/res/res_users.py
# in "def write" of "class users"
# This exists in since OpenERP 6.0 (it is not in OpenERP 5.0)
'context_propose_creation_crm_call': fields.boolean('Propose to create a call in CRM after a click2dial'),
}
'context_propose_creation_crm_call': fields.boolean(
'Propose to create a call in CRM after a click2dial'
),
}
_defaults = {
'context_propose_creation_crm_call': lambda *a: True,
}
}
res_users()

4
asterisk_click2dial_crm/wizard/__init__.py

@ -6,8 +6,8 @@
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
#
# 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
# 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,

54
asterisk_click2dial_crm/wizard/create_crm_phonecall.py

@ -2,14 +2,15 @@
##############################################################################
#
# Asterisk click2dial CRM module for OpenERP
# Copyright (c) 2011 Zikzakmedia S.L. (http://zikzakmedia.com) All Rights Reserved.
# Copyright (c) 2011 Zikzakmedia S.L. (http://zikzakmedia.com)
# All Rights Reserved.
# Copyright (c) 2012 Akretion (http://www.akretion.com)
# @author: Jesús Martín <jmartin@zikzakmedia.com>
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
#
# 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
# 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,
@ -22,33 +23,45 @@
#
##############################################################################
from osv import osv, fields
# Lib to translate error messages
from tools.translate import _
from osv import osv
class wizard_create_crm_phonecall(osv.osv_memory):
_name = "wizard.create.crm.phonecall"
def button_create_outgoing_phonecall(self, cr, uid, ids, context=None):
partner_address = self.pool.get('res.partner.address').browse(cr, uid, context.get('partner_address_id'), context=context)
return self._create_open_crm_phonecall(cr, uid, partner_address, crm_categ='Outbound', context=context)
partner_address = self.pool.get('res.partner.address').browse(
cr, uid, context.get('partner_address_id'), context=context
)
return self._create_open_crm_phonecall(
cr, uid, partner_address, crm_categ='Outbound', context=context
)
def _create_open_crm_phonecall(self, cr, uid, partner_address, crm_categ, context=None):
def _create_open_crm_phonecall(self, cr, uid, partner_address, crm_categ,
context=None):
if context is None:
context = {}
crm_phonecall_obj = self.pool.get('crm.phonecall')
categ_ids = self.pool.get('crm.case.categ').search(cr, uid, [('name','=',crm_categ)], context={'lang': 'en_US'})
case_section_ids = self.pool.get('crm.case.section').search(cr, uid, [('member_ids', 'in', uid)], context=context)
categ_ids = self.pool.get('crm.case.categ').search(
cr, uid, [('name', '=', crm_categ)], context={'lang': 'en_US'}
)
case_section_ids = self.pool.get('crm.case.section').search(
cr, uid, [('member_ids', 'in', uid)], context=context
)
context.update({
'default_partner_id': partner_address.partner_id and partner_address.partner_id.id or False,
'default_partner_id': (
partner_address.partner_id
and partner_address.partner_id.id
or False
),
'default_partner_address_id': partner_address.id,
'default_partner_contact': partner_address.name,
'default_partner_phone': partner_address.phone,
'default_partner_mobile': partner_address.mobile,
'default_categ_id': categ_ids and categ_ids[0] or False,
'default_section_id': case_section_ids and case_section_ids[0] or False,
'default_section_id': (
case_section_ids and case_section_ids[0] or False
),
})
return {
@ -58,7 +71,7 @@ class wizard_create_crm_phonecall(osv.osv_memory):
'view_type': 'form',
'view_mode': 'form,tree',
'type': 'ir.actions.act_window',
'nodestroy': False, # close the pop-up wizard after action
'nodestroy': False, # close the pop-up wizard after action
'target': 'current',
'context': context,
}
@ -70,9 +83,14 @@ class wizard_open_calling_partner(osv.osv_memory):
_inherit = "wizard.open.calling.partner"
def create_incoming_phonecall(self, cr, uid, ids, crm_categ, context=None):
'''Started by button on 'open calling partner wizard'''
partner_address = self.browse(cr, uid, ids[0], context=context).partner_address_id
action = self.pool.get('wizard.create.crm.phonecall')._create_open_crm_phonecall(cr, uid, partner_address, crm_categ='Inbound', context=context)
"""Started by button on 'open calling partner wizard"""
partner_address = (
self.browse(cr, uid, ids[0], context=context).partner_address_id
)
wizard_obj = self.pool.get('wizard.create.crm.phonecall')
action = wizard_obj._create_open_crm_phonecall(
cr, uid, partner_address, crm_categ='Inbound', context=context
)
return action
wizard_open_calling_partner()
Loading…
Cancel
Save