Browse Source
Now uses the phonenumber lib to reformat numbers in "clean" format and store them in E.164 format. Start to modify the code to take advantage of this great lib.
Now uses the phonenumber lib to reformat numbers in "clean" format and store them in E.164 format. Start to modify the code to take advantage of this great lib.
Thanks to Ludovic Gasc for pointing this lib to me during PyconFR 2012 in Paris ! Fix a bug with Open Calling Partner when CallerID has non-ASCII chars. Move all code of wizard to wizard directory. IMPORTANT : when you upgrade to this revision, run the wizard "Reformat all phone numbers" from Settings > Configuration > Asterisk.pull/26/head
Alexis de Lattre
12 years ago
10 changed files with 539 additions and 315 deletions
-
2asterisk_click2dial/__init__.py
-
2asterisk_click2dial/__openerp__.py
-
380asterisk_click2dial/asterisk_click2dial.py
-
3asterisk_click2dial/asterisk_server_view.xml
-
56asterisk_click2dial/res_partner_view.xml
-
23asterisk_click2dial/wizard/__init__.py
-
218asterisk_click2dial/wizard/open_calling_partner.py
-
68asterisk_click2dial/wizard/open_calling_partner_view.xml
-
63asterisk_click2dial/wizard/reformat_all_phonenumbers.py
-
39asterisk_click2dial/wizard/reformat_all_phonenumbers_view.xml
@ -0,0 +1,23 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Asterisk Click2Dial module for OpenERP |
|||
# Copyright (C) 2012 Alexis de Lattre <alexis@via.ecp.fr> |
|||
# |
|||
# 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 <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
import reformat_all_phonenumbers |
|||
import open_calling_partner |
@ -0,0 +1,218 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Asterisk Click2dial module for OpenERP |
|||
# Copyright (C) 2010-2012 Alexis de Lattre <alexis@via.ecp.fr> |
|||
# |
|||
# 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 <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
from osv import osv, fields |
|||
# Lib required to open a socket (needed to communicate with Asterisk server) |
|||
import logging |
|||
# Lib to translate error messages |
|||
from tools.translate import _ |
|||
|
|||
_logger = logging.getLogger(__name__) |
|||
|
|||
|
|||
class wizard_open_calling_partner(osv.osv_memory): |
|||
_name = "wizard.open.calling.partner" |
|||
_description = "Open calling partner" |
|||
|
|||
_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'), |
|||
} |
|||
|
|||
|
|||
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''' |
|||
res = {} |
|||
calling_number = self.pool.get('asterisk.server')._connect_to_asterisk(cr, uid, method='get_calling_number', context=context) |
|||
#To test the code without Asterisk server |
|||
#calling_number = "0141981242" |
|||
if calling_number: |
|||
res['calling_number'] = calling_number |
|||
# We match only on the end of the phone number |
|||
if len(calling_number) >= 9: |
|||
number_to_search = calling_number[-9:len(calling_number)] |
|||
else: |
|||
number_to_search = calling_number |
|||
partner = self.pool.get('res.partner.address').get_partner_from_phone_number(cr, uid, number_to_search, context=context) |
|||
if partner: |
|||
res['partner_address_id'] = partner[0] |
|||
res['partner_id'] = partner[1] |
|||
else: |
|||
res['partner_id'] = False |
|||
res['partner_address_id'] = False |
|||
res['to_update_partner_address_id'] = False |
|||
else: |
|||
_logger.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''' |
|||
# 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 = self.read(cr, uid, ids[0], ['partner_id'], context=context)['partner_id'] |
|||
if partner: |
|||
action = { |
|||
'name': oerp_object._description, |
|||
'view_type': 'form', |
|||
'view_mode': 'tree,form', |
|||
'res_model': oerp_object._name, |
|||
'type': 'ir.actions.act_window', |
|||
'nodestroy': False, # close the pop-up wizard after action |
|||
'target': 'current', |
|||
'domain': [('partner_id', '=', partner[0])], |
|||
} |
|||
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) |
|||
|
|||
|
|||
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) |
|||
|
|||
|
|||
def simple_open(self, cr, uid, ids, object_name='res.partner', context=None): |
|||
if object_name == 'res.partner': |
|||
field = 'partner_id' |
|||
label = 'Partner' |
|||
elif object_name == 'res.partner.address': |
|||
field = 'partner_address_id' |
|||
label = 'Contact' |
|||
else: |
|||
raise osv.except_osv(_('Error :'), "This object '%s' is not supported" % object_name) |
|||
record_to_open = self.read(cr, uid, ids[0], [field], context=context)[field] |
|||
if record_to_open: |
|||
return { |
|||
'name': label, |
|||
'view_type': 'form', |
|||
'view_mode': 'form,tree', |
|||
'res_model': object_name, |
|||
'type': 'ir.actions.act_window', |
|||
'nodestroy': False, # close the pop-up wizard after action |
|||
'target': 'current', |
|||
'res_id': record_to_open[0], |
|||
} |
|||
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) |
|||
|
|||
|
|||
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'] |
|||
user = self.pool.get('res.users').browse(cr, uid, uid, context=context) |
|||
ast_server = self.pool.get('asterisk.server')._get_asterisk_server_from_user(cr, uid, user, 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) |
|||
|
|||
new_partner_address_id = self.pool.get('res.partner.address').create(cr, uid, {phone_type: number_to_write}, context=context) |
|||
action = { |
|||
'name': 'Create new contact', |
|||
'view_type': 'form', |
|||
'view_mode': 'form,tree', |
|||
'res_model': 'res.partner.address', |
|||
'type': 'ir.actions.act_window', |
|||
'nodestroy': False, |
|||
'target': 'current', |
|||
'res_id': new_partner_address_id, |
|||
} |
|||
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) |
|||
|
|||
|
|||
def create_partner_address_mobile(self, cr, uid, ids, context=None): |
|||
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): |
|||
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.")) |
|||
user = self.pool.get('res.users').browse(cr, uid, uid, context=context) |
|||
ast_server = self.pool.get('asterisk.server')._get_asterisk_server_from_user(cr, uid, user, 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) |
|||
action = { |
|||
'name': 'Contact: ' + cur_wizard.to_update_partner_address_id.name, |
|||
'view_type': 'form', |
|||
'view_mode': 'form,tree', |
|||
'res_model': 'res.partner.address', |
|||
'type': 'ir.actions.act_window', |
|||
'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) |
|||
|
|||
|
|||
def update_partner_address_mobile(self, cr, uid, ids, context=None): |
|||
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): |
|||
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}) |
|||
else: |
|||
res['value'].update({'current_phone': False, 'current_mobile': False}) |
|||
return res |
|||
|
|||
wizard_open_calling_partner() |
@ -0,0 +1,68 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<!-- |
|||
Asterisk Click2dial module for OpenERP |
|||
Copyright (C) 2012 Alexis de Lattre <alexis@via.ecp.fr> |
|||
The licence is in the file __openerp__.py |
|||
--> |
|||
|
|||
<openerp> |
|||
<data> |
|||
|
|||
<!-- Get partner from incoming phone call --> |
|||
<record id="view_open_calling_partner" model="ir.ui.view"> |
|||
<field name="name">view_open_calling_partner</field> |
|||
<field name="model">wizard.open.calling.partner</field> |
|||
<field name="type">form</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="Open calling partner"> |
|||
<field name="calling_number" colspan="4"/> |
|||
<field name="partner_address_id" attrs="{'invisible':[('partner_address_id','=',False)]}"/> |
|||
<field name="partner_id" attrs="{'invisible':[('partner_address_id','=',False)]}" /> <!-- I want it visible when partner_address_id != False and partner_id = False, so that the user can see that this partner_address doesn't have a partner --> |
|||
<newline /> |
|||
<group colspan="4" attrs="{'invisible':[('partner_id','=',False)]}"> |
|||
<button name="open_partner" icon="gtk-go-forward" string="Partner form" type="object" /> |
|||
<button name="open_sale_orders" icon="gtk-go-forward" string="Related sale orders" type="object" /> |
|||
<button name="open_invoices" icon="gtk-go-forward" string="Related invoices" type="object" /> |
|||
<button special="cancel" icon="gtk-cancel" string="Cancel" /> |
|||
</group> |
|||
<!-- I display the button "Contact form" when the partner address exists but not the partner --> |
|||
<group colspan="4" attrs="{'invisible':['|', '&', ('partner_address_id','!=',False), ('partner_id','!=',False), ('partner_address_id','=',False)]}"> |
|||
<button name="open_partner_address" icon="gtk-go-forward" string="Contact form" type="object" colspan="2"/> |
|||
<button special="cancel" icon="gtk-cancel" string="Cancel" colspan="2"/> |
|||
</group> |
|||
|
|||
<group attrs="{'invisible':[('partner_address_id','!=',False)]}" colspan="4" col="8"> |
|||
<label string="No partner contact found in OpenERP with this number" colspan="8" /> |
|||
<separator string="Create a new contact" colspan="8" /> |
|||
<button name="create_partner_address_phone" icon="gtk-new" string="with calling number as phone" type="object" colspan="4"/> |
|||
<button name="create_partner_address_mobile" icon="gtk-new" string="with calling number as mobile" type="object" colspan="4"/> |
|||
<newline /> |
|||
|
|||
<separator string="Update an existing contact" colspan="8" /> |
|||
<field name="to_update_partner_address_id" colspan="8" on_change="onchange_to_update_partner_address(to_update_partner_address_id)"/> |
|||
<field name="current_phone" colspan="6"/> |
|||
<button name="update_partner_address_phone" icon="gtk-convert" string="Update phone" type="object" colspan="2"/> |
|||
<field name="current_mobile" colspan="6"/> |
|||
<button name="update_partner_address_mobile" icon="gtk-convert" string="Update mobile" type="object" colspan="2"/> |
|||
<newline /> |
|||
|
|||
<button special="cancel" icon="gtk-cancel" string="Cancel" colspan="8" /> |
|||
</group> |
|||
<!-- I repeat the cancel button for layout reasons --> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
|
|||
|
|||
<record id="action_open_calling_partner" model="ir.actions.act_window"> |
|||
<field name="name">Open calling partner</field> |
|||
<field name="res_model">wizard.open.calling.partner</field> |
|||
<field name="view_type">form</field> |
|||
<field name="view_mode">form</field> |
|||
<field name="target">new</field> |
|||
</record> |
|||
|
|||
<menuitem id="menu_open_calling_partner_sales" parent="base.menu_address_book" action="action_open_calling_partner" sequence="50" /> |
|||
|
|||
</data> |
|||
</openerp> |
@ -0,0 +1,63 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# Asterisk Click2dial module for OpenERP |
|||
# Copyright (C) 2012 Alexis de Lattre <alexis@via.ecp.fr> |
|||
# |
|||
# 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 <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
|
|||
from osv import osv, fields |
|||
import logging |
|||
from tools.translate import _ |
|||
|
|||
_logger = logging.getLogger(__name__) |
|||
|
|||
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"), |
|||
} |
|||
|
|||
|
|||
def run_reformat_all_phonenumbers(self, cr, uid, ids, context=None): |
|||
addr_obj = self.pool.get('res.partner.address') |
|||
phonefields = ['phone', 'fax', 'mobile'] |
|||
_logger.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): |
|||
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.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]): |
|||
addr.pop('id') |
|||
addr.pop('name') |
|||
_logger.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[0], {'phonenumbers_not_reformatted': phonenumbers_not_reformatted}, context=context) |
|||
_logger.info('End of the phone number reformatting wizard') |
|||
return True |
@ -0,0 +1,39 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<!-- |
|||
Asterisk Click2dial module for OpenERP |
|||
Copyright (C) 2012 Alexis de Lattre <alexis@via.ecp.fr> |
|||
The licence is in the file __openerp__.py |
|||
--> |
|||
|
|||
<openerp> |
|||
<data> |
|||
|
|||
<record id="reformat_all_phonenumbers_form" model="ir.ui.view"> |
|||
<field name="name">reformat_all_phonenumbers.form</field> |
|||
<field name="model">reformat.all.phonenumbers</field> |
|||
<field name="type">form</field> |
|||
<field name="arch" type="xml"> |
|||
<form string="Reformat all phone numbers"> |
|||
<label string="This wizard reformats the phone, mobile and fax numbers of all partner contacts in standard international format e.g. +33141981242" colspan="4"/> |
|||
<button special="cancel" icon="gtk-cancel" string="Close" /> |
|||
<button name="run_reformat_all_phonenumbers" icon="gtk-ok" string="Reformat all phone numbers" type="object" /> |
|||
<label colspan="4" string="Phone numbers that couldn't be reformatted:"/> |
|||
<field name="phonenumbers_not_reformatted" colspan="4" nolabel="1"/> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
|
|||
<record id="reformat_all_phonenumbers_action" model="ir.actions.act_window"> |
|||
<field name="name">Reformat all phone numbers</field> |
|||
<field name="res_model">reformat.all.phonenumbers</field> |
|||
<field name="view_type">form</field> |
|||
<field name="view_mode">form</field> |
|||
<field name="target">new</field> |
|||
</record> |
|||
|
|||
<!-- Menu entry under Settings > Configuration --> |
|||
<menuitem id="reformat_all_phonenumbers_menu" action="reformat_all_phonenumbers_action" parent="menu_asterisk_servers" sequence="100"/> |
|||
|
|||
|
|||
</data> |
|||
</openerp> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue