Browse Source

Started to implement solution d) as discussed in https://github.com/OCA/connector-telephony/issues/90

The revolution in the connector-telephony project has started !
pull/88/head
Alexis de Lattre 9 years ago
parent
commit
64382c5250
  1. 1
      base_phone/README.rst
  2. 5
      base_phone/__init__.py
  3. 6
      base_phone/__openerp__.py
  4. 311
      base_phone/base_phone.py
  5. 134
      base_phone/fields.py
  6. 6
      base_phone/models/__init__.py
  7. 0
      base_phone/models/controller.py
  8. 130
      base_phone/models/phone_common.py
  9. 28
      base_phone/models/res_company.py
  10. 30
      base_phone/models/res_partner.py
  11. 63
      base_phone/report_sxw_format.py
  12. 1397
      base_phone/static/lib/phonenumbers/PhoneFormat.js
  13. 38
      base_phone/static/src/js/phone_widget.js
  14. 48
      base_phone/static/src/xml/phone.xml
  15. 0
      base_phone/views/res_company_view.xml
  16. 0
      base_phone/views/res_partner_view.xml
  17. 0
      base_phone/views/res_users_view.xml
  18. 2
      base_phone/web_phone.xml
  19. 19
      crm_claim_phone/__init__.py
  20. 47
      crm_claim_phone/crm_claim_phone.py
  21. 9
      crm_claim_phone/crm_claim_view.xml
  22. 49
      crm_phone/crm_phone.py
  23. 19
      event_phone/event_phone.py
  24. 20
      hr_phone/hr_phone.py
  25. 18
      hr_recruitment_phone/hr_recruitment_phone.py

1
base_phone/README.rst

@ -81,6 +81,7 @@ Contributors
------------ ------------
* Alexis de Lattre <alexis.delattre@akretion.com> * Alexis de Lattre <alexis.delattre@akretion.com>
* Sébastien Beau <sebastien.beau@akretion.com>
Maintainer Maintainer
---------- ----------

5
base_phone/__init__.py

@ -1,6 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from . import base_phone
from . import fields
from . import models
from . import wizard from . import wizard
from . import report_sxw_format
from . import controller

6
base_phone/__openerp__.py

@ -33,9 +33,9 @@
'data': [ 'data': [
'security/phone_security.xml', 'security/phone_security.xml',
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'res_partner_view.xml',
'res_company_view.xml',
'res_users_view.xml',
'views/res_partner_view.xml',
'views/res_company_view.xml',
'views/res_users_view.xml',
'wizard/reformat_all_phonenumbers_view.xml', 'wizard/reformat_all_phonenumbers_view.xml',
'wizard/number_not_found_view.xml', 'wizard/number_not_found_view.xml',
'web_phone.xml', 'web_phone.xml',

311
base_phone/base_phone.py

@ -1,311 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Base Phone module for Odoo
# Copyright (C) 2010-2015 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 openerp import models, fields, api, _
from openerp.tools.safe_eval import safe_eval
from openerp.exceptions import UserError
import logging
# Lib for phone number reformating -> pip install phonenumbers
import phonenumbers
_logger = logging.getLogger(__name__)
class PhoneCommon(models.AbstractModel):
_name = 'phone.common'
@api.model
def _reformat_phonenumbers_create(self, vals):
assert isinstance(self._phone_fields, list),\
'self._phone_fields must be a list'
if any([vals.get(field) for field in self._phone_fields]):
countrycode = self._get_countrycode_from_vals(vals)
countrycode = self._countrycode_fallback(countrycode)
vals = self._reformat_phonenumbers(vals, countrycode)
return vals
@api.multi
def _reformat_phonenumbers_write(self, vals):
assert isinstance(self._phone_fields, list),\
'self._phone_fields must be a list'
if any([vals.get(field) for field in self._phone_fields]):
countrycode = self._get_countrycode_from_vals(vals)
if not countrycode:
if self._country_field:
country = safe_eval(
'self.' + self._country_field, {'self': self})
countrycode = country and country.code or None
elif self._partner_field:
partner = safe_eval(
'self.' + self._partner_field, {'self': self})
if partner:
countrycode = partner.country_id and\
partner.country_id.code or None
countrycode = self._countrycode_fallback(countrycode)
vals = self._reformat_phonenumbers(vals, countrycode)
return vals
@api.model
def _get_countrycode_from_vals(self, vals):
assert isinstance(self._country_field, (str, unicode, type(None))),\
'Wrong self._country_field'
assert isinstance(self._partner_field, (str, unicode, type(None))),\
'Wrong self._partner_field'
countrycode = None
if self._country_field and vals.get(self._country_field):
# When updating a partner in the frontend of the POS
# Odoo generate a write() with the ID of the country as unicode !!!
# example : vals = {u'country_id': u'9'}
# So we have to convert it to an integer before browsing
country = self.env['res.country'].browse(
int(vals[self._country_field]))
countrycode = country.code
elif self._partner_field and vals.get(self._partner_field):
partner = self.env['res.partner'].browse(
vals[self._partner_field])
countrycode = partner.country_id.code or False
return countrycode
@api.model
def _countrycode_fallback(self, countrycode):
if not countrycode:
if self.env.user.company_id.country_id:
countrycode = self.env.user.company_id.country_id.code
else:
_logger.error(
"You should set a country on the company '%s' "
"to allow the reformat of phone numbers",
self.env.user.company_id.name)
return countrycode
@api.model
def _reformat_phonenumbers(self, vals, countrycode):
for field in self._phone_fields:
if vals.get(field):
init_value = vals.get(field)
try:
res_parse = phonenumbers.parse(
vals.get(field), countrycode.upper())
vals[field] = phonenumbers.format_number(
res_parse, phonenumbers.PhoneNumberFormat.E164)
if init_value != vals[field]:
_logger.info(
"%s initial value: '%s' updated value: '%s'"
% (field, init_value, vals[field]))
except Exception, e:
# I do BOTH logger and raise, because:
# raise is usefull when the record is created/written
# by a user via the Web interface
# logger is usefull when the record is created/written
# via the webservices
_logger.error(
"Cannot reformat the phone number '%s' to "
"international format with region=%s",
vals.get(field), countrycode)
if self.env.context.get('raise_if_phone_parse_fails'):
raise UserError(
_("Cannot reformat the phone number '%s' to "
"international format. Error message: %s")
% (vals.get(field), e))
return vals
@api.model
def get_name_from_phone_number(self, presented_number):
'''Function to get name from phone number. Usefull for use from IPBX
to add CallerID name to incoming calls.'''
res = self.get_record_from_phone_number(presented_number)
if res:
return res[2]
else:
return False
@api.model
def get_record_from_phone_number(self, presented_number):
'''If it finds something, it returns (object name, ID, record name)
For example : ('res.partner', 42, u'Alexis de Lattre (Akretion)')
'''
_logger.debug(
u"Call get_name_from_phone_number with number = %s"
% presented_number)
if not isinstance(presented_number, (str, unicode)):
_logger.warning(
u"Number '%s' should be a 'str' or 'unicode' but it is a '%s'"
% (presented_number, type(presented_number)))
return False
if not presented_number.isdigit():
_logger.warning(
u"Number '%s' should only contain digits." % presented_number)
nr_digits_to_match_from_end = \
self.env.user.company_id.number_of_digits_to_match_from_end
if len(presented_number) >= nr_digits_to_match_from_end:
end_number_to_match = presented_number[
-nr_digits_to_match_from_end:len(presented_number)]
else:
end_number_to_match = presented_number
phoneobjects = self._get_phone_fields()
phonefieldslist = [] # [('res.parter', 10), ('crm.lead', 20)]
for objname in phoneobjects:
if (
'_phone_name_sequence' in dir(self.env[objname]) and
self.env[objname]._phone_name_sequence):
phonefieldslist.append(
(objname, self.env[objname]._phone_name_sequence))
phonefieldslist_sorted = sorted(
phonefieldslist,
key=lambda element: element[1])
_logger.debug('phonefieldslist_sorted=%s' % phonefieldslist_sorted)
for (objname, prio) in phonefieldslist_sorted:
obj = self.with_context(callerid=True).env[objname]
pg_search_number = str('%' + end_number_to_match)
_logger.debug(
"Will search phone and mobile numbers in %s ending with '%s'"
% (objname, end_number_to_match))
domain = []
for phonefield in obj._phone_fields:
domain.append((phonefield, '=like', pg_search_number))
if len(obj._phone_fields) > 1:
domain = ['|'] * (len(obj._phone_fields) - 1) + domain
res_obj = obj.search(domain)
if len(res_obj) > 1:
_logger.warning(
u"There are several %s (IDS = %s) with a phone number "
"ending with '%s'. Taking the first one."
% (objname, res_obj.ids, end_number_to_match))
res_obj = res_obj[0]
if res_obj:
name = res_obj.name_get()[0][1]
res = (objname, res_obj.id, name)
_logger.debug(
u"Answer get_record_from_phone_number: (%s, %d, %s)"
% (res[0], res[1], res[2]))
return res
else:
_logger.debug(
u"No match on %s for end of phone number '%s'"
% (objname, end_number_to_match))
return False
@api.model
def _get_phone_fields(self):
'''Returns a dict with key = object name
and value = list of phone fields'''
models = self.env['ir.model'].search([('transient', '=', False)])
res = []
for model in models:
senv = False
try:
senv = self.env[model.model]
except:
continue
if (
'_phone_fields' in dir(senv) and
isinstance(senv._phone_fields, list)):
res.append(model.model)
return res
@api.model
def click2dial(self, erp_number):
'''This function is designed to be overridden in IPBX-specific
modules, such as asterisk_click2dial or ovh_telephony_connector'''
return {'dialed_number': erp_number}
@api.model
def convert_to_dial_number(self, erp_number):
'''
This function is dedicated to the transformation of the number
available in Odoo to the number that can be dialed.
You may have to inherit this function in another module specific
for your company if you are not happy with the way I reformat
the numbers.
'''
assert(erp_number), 'Missing phone number'
_logger.debug('Number before reformat = %s' % erp_number)
# erp_number are supposed to be in E.164 format, so no need to
# give a country code here
parsed_num = phonenumbers.parse(erp_number, None)
country_code = self.env.user.company_id.country_id.code
assert(country_code), 'Missing country on company'
_logger.debug('Country code = %s' % country_code)
to_dial_number = phonenumbers.format_out_of_country_calling_number(
parsed_num, country_code.upper())
to_dial_number = str(to_dial_number).translate(None, ' -.()/')
_logger.debug('Number to be sent to phone system: %s' % to_dial_number)
return to_dial_number
class ResPartner(models.Model):
_name = 'res.partner'
_inherit = ['res.partner', 'phone.common']
_phone_fields = ['phone', 'mobile', 'fax']
_phone_name_sequence = 10
_country_field = 'country_id'
_partner_field = None
@api.model
def create(self, vals):
vals_reformated = self._reformat_phonenumbers_create(vals)
return super(ResPartner, self).create(vals_reformated)
@api.multi
def write(self, vals):
vals_reformated = self._reformat_phonenumbers_write(vals)
return super(ResPartner, self).write(vals_reformated)
@api.multi
def name_get(self):
if self._context.get('callerid'):
res = []
for partner in self:
if partner.parent_id and partner.parent_id.is_company:
name = u'%s (%s)' % (partner.name, partner.parent_id.name)
else:
name = partner.name
res.append((partner.id, name))
return res
else:
return super(ResPartner, self).name_get()
class ResCompany(models.Model):
_inherit = 'res.company'
number_of_digits_to_match_from_end = fields.Integer(
string='Number of Digits To Match From End',
default=8,
help="In several situations, OpenERP will have to find a "
"Partner/Lead/Employee/... from a phone number presented by the "
"calling party. As the phone numbers presented by your phone "
"operator may not always be displayed in a standard format, "
"the best method to find the related Partner/Lead/Employee/... "
"in OpenERP is to try to match the end of the phone number in "
"OpenERP with the N last digits of the phone number presented "
"by the calling party. N is the value you should enter in this "
"field.")
_sql_constraints = [(
'number_of_digits_to_match_from_end_positive',
'CHECK (number_of_digits_to_match_from_end > 0)',
"The value of the field 'Number of Digits To Match From End' must "
"be positive."),
]

134
base_phone/fields.py

@ -0,0 +1,134 @@
# -*- coding: utf-8 -*-
# © 2016 Akretion (http://www.akretion.com)
# Sébastien BEAU <sebastien.beau@akretion.com>
# Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import api, fields, models
from operator import attrgetter
import phonenumbers
import logging
_logger = logging.getLogger(__name__)
class Phone(fields.Char):
_slots = {
'country_field': None,
'partner_field': None,
}
def __init__(
self, string=None, country_field=None, partner_field=None,
**kwargs):
super(Phone, self).__init__(
string=string, country_field=country_field,
partner_field=partner_field, **kwargs)
_related_country_field = property(attrgetter('country_field'))
_related_partner_field = property(attrgetter('partner_field'))
def _setup_regular_full(self, model):
super(Phone, self)._setup_regular_full(model)
assert self.country_field in model._fields or \
self.partner_field in model._fields, \
"field %s with unknown country_field and partner_field" % self
def convert_to_cache(self, value, record, validate=True):
res = super(Phone, self).convert_to_cache(
value, record, validate=validate)
# print 'db value', res
if res:
try:
res_parse = phonenumbers.parse(res)
res = phonenumbers.format_number(
res_parse, phonenumbers.PhoneNumberFormat.INTERNATIONAL)
# print "after parse+intl res=", res
except:
pass
# print 'cache value', res
return res
def convert_phone_field(value, country_code):
_logger.debug(
'convert_phone_field value=%s country=%s', value, country_code)
try:
res_parse = phonenumbers.parse(
value, country_code)
_logger.debug('res_parse=%s', res_parse)
new_value = phonenumbers.format_number(
res_parse, phonenumbers.PhoneNumberFormat.E164)
_logger.debug('new_value=%s', new_value)
except:
_logger.error(
"Cannot reformat the phone number '%s' to "
"international format with region=%s",
value, country_code)
new_value = value
return new_value
def convert_all_phone_fields(self, vals, fields_to_convert):
loc_vals = vals.copy()
for field in fields_to_convert:
country_key = self._fields[field].country_field
partner_key = self._fields[field].partner_field
country = False
if country_key:
if country_key in loc_vals:
country = self.env['res.country'].browse(vals[country_key])
else:
country = self[country_key]
elif partner_key:
if partner_key in loc_vals:
partner = self.env['res.partner'].browse(vals[partner_key])
else:
partner = self[partner_key]
if partner:
country = partner.country_id
if not country:
country = self.env.user.company_id.country_id
country_code = False
if country:
country_code = country.code.upper()
loc_vals[field] = convert_phone_field(loc_vals[field], country_code)
return loc_vals
def get_phone_fields(self, vals):
fields_to_convert = []
for key in vals:
if isinstance(self._fields[key], Phone):
fields_to_convert.append(key)
return fields_to_convert
original_write = models.Model.write
original_create = models.Model.create
@api.multi
def write(self, vals):
fields_to_convert = get_phone_fields(self, vals)
if fields_to_convert:
for record in self:
loc_vals = convert_all_phone_fields(
record, vals, fields_to_convert)
original_write(record, loc_vals)
return True
else:
return original_write(self, vals)
@api.model
@api.returns('self', lambda value: value.id)
def create(self, vals):
fields_to_convert = get_phone_fields(self, vals)
if fields_to_convert:
vals = convert_all_phone_fields(self, vals, fields_to_convert)
return original_create(self, vals)
models.Model.write = write
models.Model.create = create

6
base_phone/models/__init__.py

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from . import controller
from . import res_company
from . import res_partner
from . import phone_common

0
base_phone/controller.py → base_phone/models/controller.py

130
base_phone/models/phone_common.py

@ -0,0 +1,130 @@
# -*- coding: utf-8 -*-
# © 2010-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import models, api
from openerp.addons.base_phone.fields import Phone
import logging
# Lib for phone number reformating -> pip install phonenumbers
import phonenumbers
_logger = logging.getLogger(__name__)
class PhoneCommon(models.AbstractModel):
_name = 'phone.common'
@api.model
def get_name_from_phone_number(self, presented_number):
'''Function to get name from phone number. Usefull for use from IPBX
to add CallerID name to incoming calls.'''
res = self.get_record_from_phone_number(presented_number)
if res:
return res[2]
else:
return False
@api.model
def get_record_from_phone_number(self, presented_number):
'''If it finds something, it returns (object name, ID, record name)
For example : ('res.partner', 42, u'Alexis de Lattre (Akretion)')
'''
_logger.debug(
u"Call get_name_from_phone_number with number = %s"
% presented_number)
if not isinstance(presented_number, (str, unicode)):
_logger.warning(
u"Number '%s' should be a 'str' or 'unicode' but it is a '%s'"
% (presented_number, type(presented_number)))
return False
if not presented_number.isdigit():
_logger.warning(
u"Number '%s' should only contain digits." % presented_number)
nr_digits_to_match_from_end = \
self.env.user.company_id.number_of_digits_to_match_from_end
if len(presented_number) >= nr_digits_to_match_from_end:
end_number_to_match = presented_number[
-nr_digits_to_match_from_end:len(presented_number)]
else:
end_number_to_match = presented_number
sorted_phonemodels = self._get_phone_models()
# [('res.parter', 10), ('crm.lead', 20)]
for (obj, prio) in sorted_phonemodels:
pg_search_number = str('%' + end_number_to_match)
_logger.debug(
"Will search phone and mobile numbers in %s ending with '%s'",
obj._name, end_number_to_match)
domain = []
for field in obj._fields:
if isinstance(obj._fields[field], Phone):
domain.append((field, '=like', pg_search_number))
if len(domain) > 1:
domain = ['|'] * (len(domain) - 1) + domain
_logger.debug('searching on %s with domain=%s', obj._name, domain)
res_obj = obj.search(domain)
if len(res_obj) > 1:
_logger.warning(
u"There are several %s (IDS = %s) with a phone number "
"ending with '%s'. Taking the first one.",
obj._name, res_obj.ids, end_number_to_match)
res_obj = res_obj[0]
if res_obj:
name = res_obj.name_get()[0][1]
res = (obj._name, res_obj.id, name)
_logger.debug(
u"Answer get_record_from_phone_number: (%s, %d, %s)",
res[0], res[1], res[2])
return res
else:
_logger.debug(
u"No match on %s for end of phone number '%s'",
obj._name, end_number_to_match)
return False
@api.model
def _get_phone_models(self):
res = []
for model_name in self.env.registry.keys():
senv = False
try:
senv = self.with_context(callerid=True).env[model_name]
except:
continue
if (
hasattr(senv, '_phone_name_sequence') and
isinstance(senv._phone_name_sequence, int)):
res.append((senv, senv._phone_name_sequence))
phonemodels_sorted = sorted(res, key=lambda element: element[1])
return phonemodels_sorted
@api.model
def click2dial(self, erp_number):
'''This function is designed to be overridden in IPBX-specific
modules, such as asterisk_click2dial or ovh_telephony_connector'''
return {'dialed_number': erp_number}
@api.model
def convert_to_dial_number(self, erp_number):
'''
This function is dedicated to the transformation of the number
available in Odoo to the number that can be dialed.
You may have to inherit this function in another module specific
for your company if you are not happy with the way I reformat
the numbers.
'''
assert(erp_number), 'Missing phone number'
_logger.debug('Number before reformat = %s' % erp_number)
# erp_number are supposed to be in E.164 format, so no need to
# give a country code here
parsed_num = phonenumbers.parse(erp_number, None)
country_code = self.env.user.company_id.country_id.code
assert(country_code), 'Missing country on company'
_logger.debug('Country code = %s' % country_code)
to_dial_number = phonenumbers.format_out_of_country_calling_number(
parsed_num, country_code.upper())
to_dial_number = str(to_dial_number).translate(None, ' -.()/')
_logger.debug('Number to be sent to phone system: %s' % to_dial_number)
return to_dial_number

28
base_phone/models/res_company.py

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# © 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import models, fields
class ResCompany(models.Model):
_inherit = 'res.company'
number_of_digits_to_match_from_end = fields.Integer(
string='Number of Digits To Match From End',
default=8,
help="In several situations, OpenERP will have to find a "
"Partner/Lead/Employee/... from a phone number presented by the "
"calling party. As the phone numbers presented by your phone "
"operator may not always be displayed in a standard format, "
"the best method to find the related Partner/Lead/Employee/... "
"in OpenERP is to try to match the end of the phone number in "
"OpenERP with the N last digits of the phone number presented "
"by the calling party. N is the value you should enter in this "
"field.")
_sql_constraints = [(
'number_of_digits_to_match_from_end_positive',
'CHECK (number_of_digits_to_match_from_end > 0)',
"The value of the field 'Number of Digits To Match From End' must "
"be positive.")]

30
base_phone/models/res_partner.py

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# © 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import models, api
from openerp.addons.base_phone import fields
class ResPartner(models.Model):
_inherit = 'res.partner'
_phone_name_sequence = 10
phone = fields.Phone(country_field='country_id', partner_field=None)
mobile = fields.Phone(country_field='country_id', partner_field=None)
fax = fields.Phone(country_field='country_id', partner_field=None)
@api.multi
def name_get(self):
if self._context.get('callerid'):
res = []
for partner in self:
if partner.parent_id and partner.parent_id.is_company:
name = u'%s (%s)' % (partner.name, partner.parent_id.name)
else:
name = partner.name
res.append((partner.id, name))
return res
else:
return super(ResPartner, self).name_get()

63
base_phone/report_sxw_format.py

@ -1,63 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Base Phone module for OpenERP
# Copyright (C) 2014 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 openerp import models
from openerp.report import report_sxw
import phonenumbers
class BasePhoneInstalled(models.AbstractModel):
'''When you use monkey patching, the code is executed when the module
is in the addons_path of the OpenERP server, even is the module is not
installed ! In order to avoid the side-effects it can create,
we create an AbstractModel inside the module and we test the
availability of this Model in the code of the monkey patching below.
At Akretion, we call this the "Guewen trick", in reference
to a trick used by Guewen Baconnier in the "connector" module.
'''
_name = "base.phone.installed"
format_original = report_sxw.rml_parse.format
def format(
self, text, oldtag=None, phone=False, phone_format='international'):
if self.pool.get('base.phone.installed') and phone and text:
# text should already be in E164 format, so we don't have
# to give a country code to phonenumbers.parse()
phone_number = phonenumbers.parse(text)
if phone_format == 'international':
res = phonenumbers.format_number(
phone_number, phonenumbers.PhoneNumberFormat.INTERNATIONAL)
elif phone_format == 'national':
res = phonenumbers.format_number(
phone_number, phonenumbers.PhoneNumberFormat.NATIONAL)
elif phone_format == 'e164':
res = phonenumbers.format_number(
phone_number, phonenumbers.PhoneNumberFormat.E164)
else:
res = text
else:
res = format_original(self, text, oldtag=oldtag)
return res
report_sxw.rml_parse.format = format

1397
base_phone/static/lib/phonenumbers/PhoneFormat.js
File diff suppressed because it is too large
View File

38
base_phone/static/src/js/phone_widget.js

@ -10,7 +10,6 @@ var formwidgets = require('web.form_widgets');
var web_client = require('web.web_client'); var web_client = require('web.web_client');
var _t = core._t; var _t = core._t;
var FieldPhone = formwidgets.FieldChar.extend({ var FieldPhone = formwidgets.FieldChar.extend({
template: 'FieldPhone', template: 'FieldPhone',
initialize_content: function() { initialize_content: function() {
@ -25,22 +24,20 @@ var FieldPhone = formwidgets.FieldChar.extend({
} else { } else {
var self = this; var self = this;
var phone_num = this.get('value'); var phone_num = this.get('value');
// console.log('BASE_PHONE phone_num = %s', phone_num);
console.log('BASE_PHONE phone_num = %s', phone_num);
var href = '#'; var href = '#';
var href_text = '';
if (phone_num) { if (phone_num) {
href = 'tel:' + phone_num; href = 'tel:' + phone_num;
href_text = formatInternational('', phone_num) || '';
} }
if (href_text) {
this.$el.find('a.oe_form_uri').attr('href', href).text(href_text);
this.$el.find('span.oe_form_char_content').text('');
} else {
this.$el.find('a.oe_form_uri').attr('href', '').text('');
this.$el.find('span.oe_form_char_content').text(phone_num || '');
if (phone_num) {
this.$el.find('a').attr('href', href).text(phone_num);
}
else {
this.$el.find('a').attr('href', '').text('');
} }
/*
var click2dial_text = ''; var click2dial_text = '';
if (href_text && !this.options.dial_button_invisible) {
if (formatted_phone_num && !this.options.dial_button_invisible) {
click2dial_text = _t('Dial'); click2dial_text = _t('Dial');
} }
this.$el.find('#click2dial').off('click'); this.$el.find('#click2dial').off('click');
@ -81,7 +78,7 @@ var FieldPhone = formwidgets.FieldChar.extend({
} }
} }
}); });
});
}); */
} }
}, },
on_button_clicked: function() { on_button_clicked: function() {
@ -105,17 +102,14 @@ var FieldFax = formwidgets.FieldChar.extend({
var fax_num = this.get('value'); var fax_num = this.get('value');
// console.log('BASE_PHONE fax_num = %s', fax_num); // console.log('BASE_PHONE fax_num = %s', fax_num);
var href = '#'; var href = '#';
var href_text = '';
if (fax_num) { if (fax_num) {
href = 'fax:' + fax_num; href = 'fax:' + fax_num;
href_text = formatInternational('', fax_num) || '';
} }
if (href_text) {
this.$el.find('a.oe_form_uri').attr('href', href).text(href_text);
this.$el.find('span.oe_form_char_content').text('');
} else {
this.$el.find('a.oe_form_uri').attr('href', '').text('');
this.$el.find('span.oe_form_char_content').text(fax_num || '');
if (fax_num) {
this.$el.find('a').attr('href', href).text(fax_num);
}
else {
this.$el.find('a').attr('href', '').text('');
} }
} }
}, },
@ -125,6 +119,10 @@ var FieldFax = formwidgets.FieldChar.extend({
}); });
// To avoid conflicts, we check that widgets do not exist before using // To avoid conflicts, we check that widgets do not exist before using
console.log("core.form_widget_registry.get('fax')=" + core.form_widget_registry.get('fax'));
console.log("core.form_widget_registry.get('PHONE')=" + core.form_widget_registry.get('phone'));
if(!core.form_widget_registry.get('fax')){ if(!core.form_widget_registry.get('fax')){
core.form_widget_registry.add('fax', FieldFax); core.form_widget_registry.add('fax', FieldFax);
} }

48
base_phone/static/src/xml/phone.xml

@ -7,48 +7,16 @@
<templates id="template" xml:space="preserve"> <templates id="template" xml:space="preserve">
<t t-name="FieldPhone">
<span class="oe_form_field oe_form_field_email" t-att-style="widget.node.attrs.style">
<t t-if="widget.get('effective_readonly')">
<a href="#" class="oe_form_uri"/><span class="oe_form_char_content"/>
<!--
You should add the following "<a .. />" in your IPBX-specific
module (such as asterisk_click2dial or ovh_telephony_connector)
via <t t-extend="FieldPhone">
<a id="click2dial" href="#" class="oe_bold"/>
-->
</t>
<t t-if="!widget.get('effective_readonly')">
<div>
<input type="text"
t-att-id="widget.id_for_label"
t-att-tabindex="widget.node.attrs.tabindex"
t-att-autofocus="widget.node.attrs.autofocus"
t-att-placeholder="widget.node.attrs.placeholder"
t-att-maxlength="widget.field.size"
/>
</div>
</t>
</span>
<t t-name="FieldPhone" t-extend="FieldEmail">
<t t-jquery="span:first">
this.removeClass('oe_form_field_email').addClass('oe_form_field_phone');
</t>
</t> </t>
<t t-name="FieldFax">
<span class="oe_form_field oe_form_field_email" t-att-style="widget.node.attrs.style">
<t t-if="widget.get('effective_readonly')">
<a href="#" class="oe_form_uri"/><span class="oe_form_char_content"/>
</t>
<t t-if="!widget.get('effective_readonly')">
<div>
<input type="text"
t-att-id="widget.id_for_label"
t-att-tabindex="widget.node.attrs.tabindex"
t-att-autofocus="widget.node.attrs.autofocus"
t-att-placeholder="widget.node.attrs.placeholder"
t-att-maxlength="widget.field.size"
/>
</div>
</t>
</span>
<t t-name="FieldFax" t-extend="FieldEmail">
<t t-jquery="span:first">
this.removeClass('oe_form_field_email').addClass('oe_form_field_fax');
</t>
</t> </t>
</templates> </templates>

0
base_phone/res_company_view.xml → base_phone/views/res_company_view.xml

0
base_phone/res_partner_view.xml → base_phone/views/res_partner_view.xml

0
base_phone/res_users_view.xml → base_phone/views/res_users_view.xml

2
base_phone/web_phone.xml

@ -11,8 +11,6 @@
<template id="assets_backend" name="base_phone assets" <template id="assets_backend" name="base_phone assets"
inherit_id="web.assets_backend"> inherit_id="web.assets_backend">
<xpath expr="." position="inside"> <xpath expr="." position="inside">
<script type="text/javascript"
src="/base_phone/static/lib/phonenumbers/PhoneFormat.js"></script>
<script type="text/javascript" <script type="text/javascript"
src="/base_phone/static/src/js/phone_widget.js"></script> src="/base_phone/static/src/js/phone_widget.js"></script>
</xpath> </xpath>

19
crm_claim_phone/__init__.py

@ -1,22 +1,3 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
##############################################################################
#
# CRM Claim Phone module for Odoo/OpenERP
# Copyright (C) 2014 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 . import crm_claim_phone from . import crm_claim_phone

47
crm_claim_phone/crm_claim_phone.py

@ -1,43 +1,12 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# CRM Claim Phone module for Odoo/OpenERP
# Copyright (c) 2012-2014 Akretion (http://www.akretion.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
# (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/>.
#
##############################################################################
# -*- coding: utf-8 -*-
# © 2012-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp.osv import orm
from openerp import models
from openerp.addons.base_phone.fields import Phone
class crm_claim(orm.Model):
_name = 'crm.claim'
_inherit = ['crm.claim', 'phone.common']
_phone_fields = ['partner_phone']
_country_field = None
_partner_field = 'partner_id'
class CrmClaim(models.Model):
_inherit = 'crm.claim'
def create(self, cr, uid, vals, context=None):
vals_reformated = self._generic_reformat_phonenumbers(
cr, uid, None, vals, context=context)
return super(crm_claim, self).create(
cr, uid, vals_reformated, context=context)
def write(self, cr, uid, ids, vals, context=None):
vals_reformated = self._generic_reformat_phonenumbers(
cr, uid, ids, vals, context=context)
return super(crm_claim, self).write(
cr, uid, ids, vals_reformated, context=context)
partner_phone = Phone(partner_field='partner_id')

9
crm_claim_phone/crm_claim_view.xml

@ -1,11 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--
CRM Claim Phone module for OpenERP
Copyright (C) 2014 Alexis de Lattre <alexis@via.ecp.fr>
The licence is in the file __openerp__.py
© 2014-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
--> -->
<openerp>
<odoo>
<data> <data>
<record id="crm_case_claims_form_view" model="ir.ui.view"> <record id="crm_case_claims_form_view" model="ir.ui.view">
@ -20,4 +19,4 @@
</record> </record>
</data> </data>
</openerp>
</odoo>

49
crm_phone/crm_phone.py

@ -3,25 +3,21 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import models, fields, api, _ from openerp import models, fields, api, _
from openerp.addons.base_phone.fields import Phone
class CrmLead(models.Model): class CrmLead(models.Model):
_name = 'crm.lead'
_inherit = ['crm.lead', 'phone.common']
_phone_fields = ['phone', 'mobile', 'fax']
_inherit = 'crm.lead'
_phone_name_sequence = 20 _phone_name_sequence = 20
_country_field = 'country_id'
_partner_field = None
@api.model
def create(self, vals):
vals_reformated = self._reformat_phonenumbers_create(vals)
return super(CrmLead, self).create(vals_reformated)
@api.multi
def write(self, vals):
vals_reformated = self._reformat_phonenumbers_write(vals)
return super(CrmLead, self).write(vals_reformated)
phone = Phone(country_field='country_id')
mobile = Phone(country_field='country_id')
phone = Phone(country_field='country_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 @api.multi
def name_get(self): def name_get(self):
@ -41,12 +37,6 @@ class CrmLead(models.Model):
else: else:
return super(CrmLead, self).name_get() return super(CrmLead, self).name_get()
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 @api.multi
@api.depends('phonecall_ids') @api.depends('phonecall_ids')
def _count_phonecalls(self): def _count_phonecalls(self):
@ -61,11 +51,8 @@ class CrmLead(models.Model):
class CrmPhonecall(models.Model): class CrmPhonecall(models.Model):
_name = 'crm.phonecall' _name = 'crm.phonecall'
_inherit = ['phone.common', 'mail.thread']
_inherit = ['mail.thread']
_order = "id desc" _order = "id desc"
_phone_fields = ['partner_phone', 'partner_mobile']
_country_field = None
_partner_field = 'partner_id'
# Restore the object that existed in v8 # Restore the object that existed in v8
# and doesn't exist in v9 community any more # and doesn't exist in v9 community any more
@ -87,8 +74,8 @@ class CrmPhonecall(models.Model):
default=lambda self: self.env['crm.team']._get_default_team_id()) default=lambda self: self.env['crm.team']._get_default_team_id())
partner_id = fields.Many2one( partner_id = fields.Many2one(
'res.partner', string='Contact', ondelete='cascade') 'res.partner', string='Contact', ondelete='cascade')
partner_phone = fields.Char(string='Phone')
partner_mobile = fields.Char(string='Mobile')
partner_phone = Phone(string='Phone', partner_field='partner_id')
partner_mobile = Phone(string='Mobile', partner_field='partner_id')
priority = fields.Selection([ priority = fields.Selection([
('0', 'Low'), ('0', 'Low'),
('1', 'Normal'), ('1', 'Normal'),
@ -112,16 +99,6 @@ class CrmPhonecall(models.Model):
('outbound', 'Outbound'), ('outbound', 'Outbound'),
], string='Type', required=True, default='outbound') ], string='Type', required=True, default='outbound')
@api.model
def create(self, vals):
vals_reformated = self._reformat_phonenumbers_create(vals)
return super(CrmPhonecall, self).create(vals_reformated)
@api.multi
def write(self, vals):
vals_reformated = self._reformat_phonenumbers_write(vals)
return super(CrmPhonecall, self).write(vals_reformated)
@api.onchange('partner_id') @api.onchange('partner_id')
def onchange_partner_id(self): def onchange_partner_id(self):
if self.partner_id: if self.partner_id:

19
event_phone/event_phone.py

@ -2,23 +2,12 @@
# © 2012-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>) # © 2012-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import models, api
from openerp import models
from openerp.addons.base_phone.fields import Phone
class EventRegistration(models.Model): class EventRegistration(models.Model):
_name = 'event.registration'
_inherit = ['event.registration', 'phone.common']
_phone_fields = ['phone']
_inherit = 'event.registration'
_phone_name_sequence = 100 _phone_name_sequence = 100
_country_field = None
_partner_field = 'partner_id'
@api.model
def create(self, vals):
vals_reformated = self._reformat_phonenumbers_create(vals)
return super(EventRegistration, self).create(vals_reformated)
@api.multi
def write(self, vals):
vals_reformated = self._reformat_phonenumbers_write(vals)
return super(EventRegistration, self).write(vals_reformated)
phone = Phone(partner_field='partner_id')

20
hr_phone/hr_phone.py

@ -2,23 +2,13 @@
# © 2012-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>) # © 2012-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import models, api
from openerp import models
from openerp.addons.base_phone.fields import Phone
class HrEmployee(models.Model): class HrEmployee(models.Model):
_name = 'hr.employee'
_inherit = ['hr.employee', 'phone.common']
_phone_fields = ['work_phone', 'mobile_phone']
_inherit = 'hr.employee'
_phone_name_sequence = 30 _phone_name_sequence = 30
_country_field = 'country_id'
_partner_field = None
@api.model
def create(self, vals):
vals_reformated = self._reformat_phonenumbers_create(vals)
return super(HrEmployee, self).create(vals_reformated)
@api.multi
def write(self, vals):
vals_reformated = self._reformat_phonenumbers_write(vals)
return super(HrEmployee, self).write(vals_reformated)
work_phone = Phone(country_field='country_id')
mobile_phone = Phone(country_field='country_id')

18
hr_recruitment_phone/hr_recruitment_phone.py

@ -3,25 +3,15 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import models, api from openerp import models, api
from openerp.addons.base_phone.fields import Phone
class HrApplicant(models.Model): class HrApplicant(models.Model):
_name = 'hr.applicant'
_inherit = ['hr.applicant', 'phone.common']
_phone_fields = ['partner_phone', 'partner_mobile']
_inherit = 'hr.applicant'
_phone_name_sequence = 50 _phone_name_sequence = 50
_country_field = None
_partner_field = 'partner_id'
@api.model
def create(self, vals):
vals_reformated = self._reformat_phonenumbers_create(vals)
return super(HrApplicant, self).create(vals_reformated)
@api.multi
def write(self, vals):
vals_reformated = self._reformat_phonenumbers_write(vals)
return super(HrApplicant, self).write(vals_reformated)
partner_phone = Phone(partner_field='partner_id')
partner_mobile = Phone(partner_field='partner_id')
@api.multi @api.multi
def name_get(self): def name_get(self):

Loading…
Cancel
Save