Browse Source

Port to v12 base_phone, asterisk_click2dial, crm_phone, hr_phone, event_phone and hr_recruitment_phone

pull/177/head
Alexis de Lattre 6 years ago
parent
commit
2466a9eca5
  1. 12
      asterisk_click2dial/__manifest__.py
  2. 9
      asterisk_click2dial/controller.py
  3. 6
      asterisk_click2dial/demo/asterisk_click2dial_demo.xml
  4. 58
      asterisk_click2dial/models/asterisk_server.py
  5. 18
      asterisk_click2dial/models/phone_common.py
  6. 11
      asterisk_click2dial/models/res_users.py
  7. 18
      asterisk_click2dial/security/asterisk_security.xml
  8. 26
      asterisk_click2dial/static/src/css/asterisk_click2dial.css
  9. 25
      asterisk_click2dial/static/src/js/asterisk_click2dial.js
  10. 14
      asterisk_click2dial/static/src/scss/asterisk.scss
  11. 13
      asterisk_click2dial/static/src/xml/asterisk_click2dial.xml
  12. 6
      asterisk_click2dial/views/asterisk_server.xml
  13. 4
      asterisk_click2dial/views/res_users.xml
  14. 24
      asterisk_click2dial/views/web_asterisk_click2dial.xml
  15. 17
      asterisk_click2dial/web_asterisk_click2dial.xml
  16. 3
      base_phone/__init__.py
  17. 34
      base_phone/__manifest__.py
  18. 22
      base_phone/controllers/main.py
  19. 5
      base_phone/models/__init__.py
  20. 65
      base_phone/models/phone_common.py
  21. 18
      base_phone/models/phone_validation_mixin.py
  22. 8
      base_phone/models/res_company.py
  23. 4
      base_phone/models/res_config_settings.py
  24. 19
      base_phone/models/res_partner.py
  25. 103
      base_phone/static/src/js/phone_widget.js
  26. 28
      base_phone/static/src/xml/phone.xml
  27. 13
      base_phone/tests/test_phone.py
  28. 27
      base_phone/views/base_config_settings.xml
  29. 30
      base_phone/views/res_config_settings.xml
  30. 13
      base_phone/views/res_partner_view.xml
  31. 7
      base_phone/views/res_users_view.xml
  32. 3
      base_phone/views/web_phone.xml
  33. 18
      base_phone/wizard/number_not_found.py
  34. 13
      base_phone/wizard/number_not_found_view.xml
  35. 19
      base_phone/wizard/reformat_all_phonenumbers.py
  36. 17
      base_phone/wizard/reformat_all_phonenumbers_view.xml
  37. 2
      base_sms_client/__manifest__.py
  38. 7
      crm_phone/__manifest__.py
  39. 1
      crm_phone/demo/crm_phonecall.xml
  40. 30
      crm_phone/models/crm_lead.py
  41. 36
      crm_phone/models/crm_phonecall.py
  42. 5
      crm_phone/models/phone_common.py
  43. 23
      crm_phone/models/res_partner.py
  44. 3
      crm_phone/models/res_users.py
  45. 27
      crm_phone/tests/test_crm_phone.py
  46. 17
      crm_phone/view/crm_lead.xml
  47. 30
      crm_phone/view/crm_phonecall.xml
  48. 3
      crm_phone/view/res_partner.xml
  49. 3
      crm_phone/view/res_users.xml
  50. 19
      crm_phone/wizard/create_crm_phonecall.py
  51. 11
      crm_phone/wizard/create_crm_phonecall_view.xml
  52. 86
      crm_phone/wizard/number_not_found.py
  53. 18
      crm_phone/wizard/number_not_found_view.xml
  54. 10
      event_phone/__manifest__.py
  55. 15
      event_phone/models/event_registration.py
  56. 23
      event_phone/views/hr_registration.xml
  57. 5
      hr_phone/__manifest__.py
  58. 21
      hr_phone/models/hr_employee.py
  59. 9
      hr_recruitment_phone/__manifest__.py
  60. 22
      hr_recruitment_phone/models/hr_applicant.py
  61. 26
      hr_recruitment_phone/views/hr_applicant.xml
  62. 2
      ovh_sms_client/__manifest__.py

12
asterisk_click2dial/__manifest__.py

@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
# © 2010-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2010-2018 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'Asterisk Click2dial',
'version': '10.0.1.0.0',
'name': 'Asterisk connector',
'version': '12.0.1.0.0',
'category': 'Phone',
'license': 'AGPL-3',
'summary': 'Asterisk-Odoo connector',
@ -83,11 +83,11 @@ http://www.akretion.com/products-and-services/openerp-asterisk-voip-connector
'views/asterisk_server.xml',
'views/res_users.xml',
'security/ir.model.access.csv',
'web_asterisk_click2dial.xml',
'security/asterisk_security.xml',
'views/web_asterisk_click2dial.xml',
],
'demo': ['demo/asterisk_click2dial_demo.xml'],
'qweb': ['static/src/xml/*.xml'],
'css': ['static/src/css/*.css'],
'qweb': ['static/src/xml/asterisk_click2dial.xml'],
'application': True,
'installable': True,
}

9
asterisk_click2dial/controller.py

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# © 2014-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# © 2015-2016 Juris Malinens (port to v9)
# Copyright 2014-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# Copyright 2015-2018 Juris Malinens (port to v9)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import http
@ -9,8 +10,8 @@ from odoo import http
class AsteriskClick2dialController(http.Controller):
@http.route(
'/asterisk_click2dial/get_record_from_my_channel/',
type='json', auth='public')
'/asterisk_click2dial/get_record_from_my_channel', type='json',
auth='user')
def get_record_from_my_channel(self, **kw):
res = http.request.env['asterisk.server'].get_record_from_my_channel()
return res

6
asterisk_click2dial/demo/asterisk_click2dial_demo.xml

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2010-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
Copyright 2010-2018 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
@ -31,9 +31,5 @@
<field name="asterisk_server_id" ref="demo_ast_server"/>
</record>
<record id="base.main_partner" model="res.partner">
<field name="country_id" ref="base.fr"/>
</record>
</odoo>

58
asterisk_click2dial/models/asterisk_server.py

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# © 2010-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2010-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, fields, api, _
@ -22,8 +23,7 @@ class AsteriskServer(models.Model):
_description = "Asterisk Servers"
name = fields.Char(string='Asterisk Server Name', required=True)
active = fields.Boolean(
string='Active', default=True)
active = fields.Boolean(string='Active', default=True)
ip_address = fields.Char(
string='Asterisk IP address or DNS', required=True)
port = fields.Integer(
@ -70,7 +70,6 @@ class AsteriskServer(models.Model):
'asterisk.server'),
help="Company who uses the Asterisk server.")
@api.multi
@api.constrains(
'out_prefix', 'wait_time', 'extension_priority', 'port',
'context', 'alert_info', 'login', 'password')
@ -83,28 +82,28 @@ class AsteriskServer(models.Model):
password = ('AMI password', server.password)
if out_prefix[1] and not out_prefix[1].isdigit():
raise ValidationError(
_("Only use digits for the '%s' on the Asterisk server "
raise ValidationError(_(
"Only use digits for the '%s' on the Asterisk server "
"'%s'" % (out_prefix[0], server.name)))
if server.wait_time < 1 or server.wait_time > 120:
raise ValidationError(
_("You should set a 'Wait time' value between 1 and 120 "
raise ValidationError(_(
"You should set a 'Wait time' value between 1 and 120 "
"seconds for the Asterisk server '%s'" % server.name))
if server.extension_priority < 1:
raise ValidationError(
_("The 'extension priority' must be a positive value for "
raise ValidationError(_(
"The 'extension priority' must be a positive value for "
"the Asterisk server '%s'" % server.name))
if server.port > 65535 or server.port < 1:
raise ValidationError(
_("You should set a TCP port between 1 and 65535 for the "
raise ValidationError(_(
"You should set a TCP port between 1 and 65535 for the "
"Asterisk server '%s'" % server.name))
for check_str in [dialplan_context, alert_info, login, password]:
if check_str[1]:
try:
check_str[1].encode('ascii')
except UnicodeEncodeError:
raise ValidationError(
_("The '%s' should only have ASCII caracters for "
raise ValidationError(_(
"The '%s' should only have ASCII caracters for "
"the Asterisk server '%s'"
% (check_str[0], server.name)))
@ -119,13 +118,13 @@ class AsteriskServer(models.Model):
ast_server = user.get_asterisk_server_from_user()
# We check if the current user has a chan type
if not user.asterisk_chan_type:
raise UserError(
_('No channel type configured for the current user.'))
raise UserError(_(
'No channel type configured for the current user.'))
# We check if the current user has an internal number
if not user.resource:
raise UserError(
_('No resource name configured for the current user'))
raise UserError(_(
'No resource name configured for the current user'))
_logger.debug(
"User's phone: %s/%s", user.asterisk_chan_type, user.resource)
@ -137,18 +136,17 @@ class AsteriskServer(models.Model):
ast_manager = Manager.Manager(
(ast_server.ip_address, ast_server.port),
ast_server.login, ast_server.password)
except Exception, e:
except Exception as e:
_logger.error(
"Error in the request to the Asterisk Manager Interface %s",
ast_server.ip_address)
_logger.error("Here is the error message: %s", e)
raise UserError(
_("Problem in the request from Odoo to Asterisk. "
raise UserError(_(
"Problem in the request from Odoo to Asterisk. "
"Here is the error message: %s" % e))
return (user, ast_server, ast_manager)
@api.multi
def test_ami_connection(self):
self.ensure_one()
ast_manager = False
@ -157,9 +155,9 @@ class AsteriskServer(models.Model):
(self.ip_address, self.port),
self.login,
self.password)
except Exception, e:
raise UserError(
_("Connection Test Failed! The error message is: %s" % e))
except Exception as e:
raise UserError(_(
"Connection Test Failed! The error message is: %s" % e))
finally:
if ast_manager:
ast_manager.Logoff()
@ -207,15 +205,15 @@ class AsteriskServer(models.Model):
chan, user)
if calling_party_number:
break
except Exception, e:
except Exception as e:
_logger.error(
"Error in the Status request to Asterisk server %s",
ast_server.ip_address)
_logger.error(
"Here are the details of the error: '%s'", unicode(e))
raise UserError(
_("Can't get calling number from Asterisk.\nHere is the "
"error: '%s'" % unicode(e)))
"Here are the details of the error: '%s'", str(e))
raise UserError(_(
"Can't get calling number from Asterisk.\nHere is the "
"error: '%s'" % str(e)))
finally:
ast_manager.Logoff()

18
asterisk_click2dial/models/phone_common.py

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# © 2010-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2010-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, api, _
@ -51,21 +52,20 @@ class PhoneCommon(models.AbstractModel):
channel,
context=ast_server.context,
extension=ast_number,
priority=unicode(ast_server.extension_priority),
timeout=unicode(ast_server.wait_time * 1000),
priority=str(ast_server.extension_priority),
timeout=str(ast_server.wait_time * 1000),
caller_id=user.callerid,
account=user.cdraccount,
variable=variable)
except Exception, e:
except Exception as e:
_logger.error(
"Error in the Originate request to Asterisk server %s",
ast_server.ip_address)
_logger.error(
"Here are the details of the error: '%s'", unicode(e))
raise UserError(
_("Click to dial with Asterisk failed.\nHere is the error: "
"'%s'")
% unicode(e))
"Here are the details of the error: '%s'", str(e))
raise UserError(_(
"Click to dial with Asterisk failed.\nHere is the error: "
"'%s'") % str(e))
finally:
ast_manager.Logoff()

11
asterisk_click2dial/models/res_users.py

@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
# © 2010-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2010-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, fields, api, _
from odoo import api, fields, models, _
from odoo.exceptions import UserError, ValidationError
@ -69,7 +70,6 @@ class ResUsers(models.Model):
"If you leave this field empty, it will use the first Asterisk "
"server of the user's company.")
@api.multi
@api.constrains('resource', 'internal_number', 'callerid')
def _check_validity(self):
for user in self:
@ -88,7 +88,6 @@ class ResUsers(models.Model):
"ASCII caracters")
% (check_string[0], user.name))
@api.multi
def get_asterisk_server_from_user(self):
'''Returns an asterisk.server recordset'''
self.ensure_one()
@ -102,8 +101,8 @@ class ResUsers(models.Model):
# If the user doesn't have an asterisk server,
# we take the first one of the user's company
if not asterisk_servers:
raise UserError(
_("No Asterisk server configured for the company '%s'.")
raise UserError(_(
"No Asterisk server configured for the company '%s'.")
% self.company_id.name)
else:
ast_server = asterisk_servers[0]

18
asterisk_click2dial/security/asterisk_security.xml

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2018 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo noupdate="1">
<record id="asterisk_server_rule" model="ir.rule">
<field name="name">Asterisk server multi-company</field>
<field name="model_id" ref="model_asterisk_server"/>
<field name="domain_force">['|', ('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])]</field>
</record>
</odoo>

26
asterisk_click2dial/static/src/css/asterisk_click2dial.css

@ -1,26 +0,0 @@
/*
© 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
*/
.openerp .oe_topbar_item.oe_topbar_open_caller{
padding: 0px;
width: 32px;
height: 32px;
}
.openerp .oe_topbar_item.oe_topbar_open_caller button{
position: relative;
top: -3px;
box-sizing: border-box;
border: none;
box-shadow: none;
color: white;
background: none;
text-shadow: 0px 1px 2px black;
width: 32px;
height: 32px;
padding: 0px;
margin: 0px
border-radius: 0px;
}

25
asterisk_click2dial/static/src/js/asterisk_click2dial.js

@ -1,20 +1,23 @@
/* © 2014-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
© 2015-2016 Juris Malinens (port to v9)
/* Copyright 2014-2018 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
Copyright 2015-2018 Juris Malinens (port to v9)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
odoo.define('asterisk_click2dial.click2dial', function (require) {
odoo.define('asterisk_click2dial.systray.OpenCaller', function (require) {
"use strict";
var core = require('web.core');
var SystrayMenu = require('web.SystrayMenu');
var web_client = require('web.web_client');
var Widget = require('web.Widget');
var core = require('web.core');
var _t = core._t;
var click2dial = {};
var click2dialOpenCaller = Widget.extend({
template: 'asterisk_click2dial.OpenCaller',
var OpenCallerMenu = Widget.extend({
name: 'open_caller',
template: 'asterisk_click2dial.systray.OpenCaller',
events: {
'click': 'on_open_caller',
},
@ -26,7 +29,11 @@ var click2dialOpenCaller = Widget.extend({
on_open_caller: function (event) {
event.stopPropagation();
var self = this;
self.rpc('/asterisk_click2dial/get_record_from_my_channel', {}).done(function(r) {
var context = this.getSession().user_context;
self._rpc({
route: '/asterisk_click2dial/get_record_from_my_channel',
params: {local_context: context, },
}).done(function(r) {
// console.log('RESULT RPC r='+r);
// console.log('RESULT RPC type r='+typeof r);
// console.log('RESULT RPC isNaN r='+isNaN(r));
@ -82,6 +89,8 @@ var click2dialOpenCaller = Widget.extend({
},
});
SystrayMenu.Items.push(click2dialOpenCaller);
SystrayMenu.Items.push(OpenCallerMenu);
return OpenCallerMenu;
});

14
asterisk_click2dial/static/src/scss/asterisk.scss

@ -0,0 +1,14 @@
/*
Copyright 2016-2018 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
*/
.o_asterisk_systray_item {
> a {
opacity: 1;
> i {
font-size: larger;
}
}
}

13
asterisk_click2dial/static/src/xml/asterisk_click2dial.xml

@ -1,24 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
© 2014-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
Copyright 2014-2018 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<template>
<!-- Add phone button in top right menu for "Open Caller" -->
<t t-name="asterisk_click2dial.OpenCaller">
<li class="oe_topbar_item oe_topbar_open_caller"
title="Open Caller">
<a id="asterisk-open-caller" href="#" class="fa fa-phone"/>
<t t-name="asterisk_click2dial.systray.OpenCaller">
<li class="o_mail_systray_item">
<a id="asterisk-open-caller" href="#" title="Open Caller" role="button">
<i class="fa fa-phone" role="img" aria-label="Open Caller"/>
</a>
</li>
</t>
<!-- Add Dial button in phone widget for click2dial feature -->
<!--
<t t-extend="FieldPhone">
<t t-jquery="a.o_form_uri" t-operation="after">
<a t-if="widget.clickable" id="click2dial" href="#" class="oe_bold"/>
</t>
</t>
-->
</template>

6
asterisk_click2dial/views/asterisk_server.xml

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2010-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
Copyright 2010-2018 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
@ -42,7 +42,7 @@
<field name="password" password="True" />
<button name="test_ami_connection" type="object"
string="Test Connection to Asterisk"
icon="gtk-network" colspan="2"/>
class="btn-default" colspan="2"/>
</group>
<group name="dialplan" string="Dialplan Parameters">
<field name="context" />
@ -52,7 +52,7 @@
<label for="wait_time"/>
<div>
<field name="wait_time" class="oe_inline"/>
<label string=" seconds" class="oe_inline"/>
<label string=" seconds" class="oe_inline" for="seconds"/>
</div>
</group>
</sheet>

4
asterisk_click2dial/views/res_users.xml

@ -1,9 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2010-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
Copyright 2010-2018 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
Inherit res_users view to add the click2dial-related fields
-->
<odoo>

24
asterisk_click2dial/views/web_asterisk_click2dial.xml

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2014-2018 Akretion France
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<template id="_assets_primary_variables" name="asterisk_click2dial assets"
inherit_id="web._assets_primary_variables">
<xpath expr="//link[last()]" position="after">
<link rel="stylesheet" type="text/scss" href="/asterisk_click2dial/static/src/scss/asterisk.scss"/>
</xpath>
</template>
<template id="assets_backend" name="mail assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript"
src="/asterisk_click2dial/static/src/js/asterisk_click2dial.js"></script>
</xpath>
</template>
</odoo>

17
asterisk_click2dial/web_asterisk_click2dial.xml

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2014-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<template id="assets_backend" name="asterisk_click2dial assets"
inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript"
src="/asterisk_click2dial/static/src/js/asterisk_click2dial.js"></script>
</xpath>
</template>
</odoo>

3
base_phone/__init__.py

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

34
base_phone/__manifest__.py

@ -1,46 +1,28 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Base Phone module for Odoo
# Copyright (C) 2014-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/>.
#
##############################################################################
# Copyright 2014-2018 Akretion France
# @author: Alexis de Lattre <alexis@via.ecp.fr>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'Base Phone',
'version': '10.0.0.1.0',
'version': '12.0.1.0.0',
'category': 'Phone',
'license': 'AGPL-3',
'summary': 'Validate phone numbers',
'author': "Akretion,Odoo Community Association (OCA)",
'website': 'http://www.akretion.com/',
'depends': ['base_setup', 'web'],
'depends': ['phone_validation', 'base_setup'],
'external_dependencies': {'python': ['phonenumbers']},
'data': [
'security/phone_security.xml',
'security/ir.model.access.csv',
'views/res_partner_view.xml',
'views/base_config_settings.xml',
'views/res_config_settings.xml',
'views/res_users_view.xml',
'wizard/reformat_all_phonenumbers_view.xml',
'wizard/number_not_found_view.xml',
'web_phone.xml',
'views/web_phone.xml',
],
'qweb': ['static/src/xml/*.xml'],
'qweb': ['static/src/xml/phone.xml'],
'images': [],
'installable': True,
}

22
base_phone/controllers/main.py

@ -1,23 +1,7 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Base Phone module for Odoo
# Copyright (C) 2014-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/>.
#
##############################################################################
# Copyright (C) 2014-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import http

5
base_phone/models/__init__.py

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
from . import base
from . import phone_validation_mixin
from . import res_company
from . import base_config_settings
from . import res_config_settings
from . import res_partner
from . import phone_common
from . import ir_fields_converter

65
base_phone/models/phone_common.py

@ -1,9 +1,8 @@
# -*- coding: utf-8 -*-
# © 2010-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2010-2018 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, api
from .. import fields as phone_fields
import logging
_logger = logging.getLogger(__name__)
@ -15,6 +14,7 @@ except ImportError:
class PhoneCommon(models.AbstractModel):
_name = 'phone.common'
_description = 'Common methods for phone features'
@api.model
def get_name_from_phone_number(self, presented_number):
@ -29,19 +29,19 @@ class PhoneCommon(models.AbstractModel):
@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)')
For example : ('res.partner', 42, 'Alexis de Lattre (Akretion)')
'''
_logger.debug(
u"Call get_name_from_phone_number with number = %s"
"Call get_name_from_phone_number with number = %s"
% presented_number)
if not isinstance(presented_number, (str, unicode)):
if not isinstance(presented_number, str):
_logger.warning(
u"Number '%s' should be a 'str' or 'unicode' but it is a '%s'"
"Number '%s' should be a 'str' 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)
"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
@ -54,25 +54,30 @@ class PhoneCommon(models.AbstractModel):
sorted_phonemodels = self._get_phone_models()
for obj_dict in sorted_phonemodels:
obj = obj_dict['object']
pg_search_number = str('%' + end_number_to_match)
pg_search_number = '%' + end_number_to_match
_logger.debug(
"Will search phone and mobile numbers in %s ending with '%s'",
obj._name, end_number_to_match)
domain = []
sql = "SELECT id FROM %s WHERE " % obj._table
sql_where = []
sql_args = []
for field in obj_dict['fields']:
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:
sql_where.append("replace(%s, ' ', '') ilike %%s" % field)
sql_args.append(pg_search_number)
sql = sql + ' or '.join(sql_where)
_logger.debug("get_record_from_phone_number sql=%s", sql)
self._cr.execute(sql, tuple(sql_args))
res_sql = self._cr.fetchall()
#res_obj = obj.search(domain)
if len(res_sql) > 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]
if res_sql:
obj_id = res_sql[0][0]
res_obj = obj.browse(obj_id)
name = res_obj.display_name
res = (obj._name, res_obj.id, name)
_logger.debug(
u"Answer get_record_from_phone_number: (%s, %d, %s)",
@ -95,18 +100,19 @@ class PhoneCommon(models.AbstractModel):
continue
if (
hasattr(senv, '_phone_name_sequence') and
isinstance(senv._phone_name_sequence, int)):
phoneobj.append((senv, senv._phone_name_sequence))
phoneobj_sorted = sorted(phoneobj, key=lambda element: element[1])
isinstance(senv._phone_name_sequence, int) and
hasattr(senv, '_phone_name_fields') and
isinstance(senv._phone_name_fields, list)):
cdict = {
'object': senv,
'fields': senv._phone_name_fields,
}
phoneobj.append((senv._phone_name_sequence, cdict))
phoneobj_sorted = sorted(phoneobj, key=lambda element: element[0])
res = []
for (obj, prio) in phoneobj_sorted:
entry = {'object': obj, 'fields': []}
for field in obj._fields:
if isinstance(obj._fields[field], phone_fields.Phone):
entry['fields'].append(field)
res.append(entry)
for l in phoneobj_sorted:
res.append(l[1])
# [{'fields': ['fax', 'phone', 'mobile'], 'object': res.partner()},
# {'fields': ['fax', 'phone', 'mobile'], 'object': crm.lead()}]
return res
@ -136,6 +142,7 @@ class PhoneCommon(models.AbstractModel):
_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, ' -.()/')
to_dial_number = to_dial_number.translate(
to_dial_number.maketrans('', '', ' -.()/'))
_logger.debug('Number to be sent to phone system: %s' % to_dial_number)
return to_dial_number

18
base_phone/models/phone_validation_mixin.py

@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
# Copyright 2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models
class PhoneValidationMixin(models.AbstractModel):
_inherit = 'phone.validation.mixin'
def _phone_get_country(self):
if 'country_id' in self and self.country_id:
return self.country_id
if 'partner_id' in self and self.partner_id and self.partner_id.country_id:
return self.partner_id.country_id
return self.env.user.company_id.country_id

8
base_phone/models/res_company.py

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
# © 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2016-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, fields
from .. import fields as phone_fields
class ResCompany(models.Model):
@ -21,10 +21,6 @@ class ResCompany(models.Model):
"Odoo with the N last digits of the phone number presented "
"by the calling party. N is the value you should enter in this "
"field.")
phone = phone_fields.Phone(
country_field='country_id', partner_field='partner_id')
fax = phone_fields.Fax(
country_field='country_id', partner_field='partner_id')
_sql_constraints = [(
'number_of_digits_to_match_from_end_positive',

4
base_phone/models/base_config_settings.py → base_phone/models/res_config_settings.py

@ -5,8 +5,8 @@
from odoo import models, fields
class BaseConfigSettings(models.TransientModel):
_inherit = 'base.config.settings'
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
number_of_digits_to_match_from_end = fields.Integer(
related='company_id.number_of_digits_to_match_from_end')

19
base_phone/models/res_partner.py

@ -1,22 +1,21 @@
# -*- coding: utf-8 -*-
# © 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2016-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, api
from .. import fields
from odoo import models
class ResPartner(models.Model):
_inherit = 'res.partner'
_name = 'res.partner'
# inherit on phone.validation.mixin (same as in crm_phone_validation,
# but base_phone only depends on phone_validation,
# not on crm_phone_validation)
_inherit = ['res.partner', 'phone.validation.mixin']
_phone_name_sequence = 10
_phone_name_fields = ['phone', 'mobile']
phone = fields.Phone(country_field='country_id', partner_field='parent_id')
mobile = fields.Phone(
country_field='country_id', partner_field='parent_id')
fax = fields.Fax(country_field='country_id', partner_field='parent_id')
@api.multi
def name_get(self):
if self._context.get('callerid'):
res = []

103
base_phone/static/src/js/phone_widget.js

@ -1,51 +1,36 @@
/* Base phone module for Odoo
Copyright (C) 2013-2016 Alexis de Lattre <alexis@via.ecp.fr>
The licence is in the file __openerp__.py */
Copyright (C) 2013-2018 Akretion France
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
odoo.define('base_phone.phone_widget', function (require) {
odoo.define('base_phone.updatedphone_widget', function (require) {
"use strict";
var core = require('web.core');
var formwidgets = require('web.form_widgets');
var web_client = require('web.web_client');
var basicFields = require('web.basic_fields');
var InputField = basicFields.InputField;
var originalFieldPhone = basicFields.FieldPhone;
var fieldRegistry = require('web.field_registry');
var QWeb = core.qweb;
var _t = core._t;
var FieldFax = formwidgets.FieldEmail.extend({
template: 'FieldFax',
prefix: 'fax',
initialize_content: function() {
this._super();
var $button = this.$el.find('button');
$button.click(this.on_button_clicked);
this.setupFocus($button);
},
render_value: function() {
this._super();
if (this.get("effective_readonly") && this.clickable) {
var phone_num = this.get('value');
if(phone_num) {
phone_num = phone_num.replace(/ /g, '').replace(/-/g, '');
this.$el.attr('href', this.prefix + ':' + phone_num);
}
}
},
on_button_clicked: function() {
location.href = this.prefix + ':' + this.get('value');
}
});
var updatedFieldPhone = originalFieldPhone.extend({
/* init: function () {
this._super.apply(this, arguments);
}, */
var FieldPhone = FieldFax.extend({
template: 'FieldPhone',
prefix: 'tel',
render_value: function() {
_renderReadonly: function() {
this._super();
if (this.get("effective_readonly") && this.clickable) {
if (this.mode == "readonly") {
var self = this;
var phone_num = this.get('value');
if(phone_num) {
var phone_num = this.value;
/* if(phone_num) {
phone_num = phone_num.replace(/ /g, '').replace(/-/g, '');
}
var click2dial_text = '';
} */
/* var click2dial_text = '';
if (phone_num && !this.options.dial_button_invisible) {
click2dial_text = _t('Dial');
}
@ -53,6 +38,9 @@ odoo.define('base_phone.phone_widget', function (require) {
this.$el.filter('#click2dial')
.text(click2dial_text)
.attr('href', '#')
} */
this.$el.filter('a[href^="tel:"]').off('click');
this.$el.filter('a[href^="tel:"]')
.on('click', function(ev) {
self.do_notify(
_t('Click2dial started'),
@ -60,10 +48,13 @@ odoo.define('base_phone.phone_widget', function (require) {
false);
var arg = {
'phone_number': phone_num,
'click2dial_model': self.view.dataset.model,
'click2dial_id': self.view.datarecord.id};
self.rpc('/base_phone/click2dial', arg).done(function(r) {
// console.log('Click2dial r=%s', JSON.stringify(r));
'click2dial_model': self.model,
'click2dial_id': self.res_id};
self._rpc({
route: '/base_phone/click2dial',
params: arg,
}).done(function(r) {
// TODO: check why it never goes in there
if (r === false) {
self.do_warn("Click2dial failed");
} else if (typeof r === 'object') {
@ -73,8 +64,8 @@ odoo.define('base_phone.phone_widget', function (require) {
false);
if (r.action_model) {
var context = {
'click2dial_model': self.view.dataset.model,
'click2dial_id': self.view.datarecord.id,
'click2dial_model': self.model,
'click2dial_id': self.res_id,
'phone_number': phone_num,
};
var action = {
@ -86,7 +77,7 @@ odoo.define('base_phone.phone_widget', function (require) {
target: 'new',
context: context,
};
web_client.action_manager.do_action(action);
this.do_action(action);
}
}
});
@ -96,6 +87,7 @@ odoo.define('base_phone.phone_widget', function (require) {
});
// To avoid conflicts, we check that widgets do not exist before using
/*
if(!core.form_widget_registry.get('fax')){
core.form_widget_registry.add('fax', FieldFax);
}
@ -103,29 +95,10 @@ odoo.define('base_phone.phone_widget', function (require) {
if(!core.form_widget_registry.get('phone')){
core.form_widget_registry.add('phone', FieldPhone);
}
*/
fieldRegistry.add('phone', updatedFieldPhone);
var treewidgets = require('web.ListView');
var ColumnPhone = treewidgets.Column.extend({
// ability to add widget="phone" in TREE view
_format: function(row_data, options) {
var phone_num = row_data[this.id].value;
if (phone_num) {
var raw_phone_num = phone_num.replace(/ /g, '');
raw_phone_num = raw_phone_num.replace(/-/g, '');
return _.template("<a href='tel:<%-href%>'><%-text%></a>")({
href: raw_phone_num,
text: phone_num
});
}
return this._super(row_data, options);
}
});
if (!core.list_widget_registry.get('phone')) {
core.list_widget_registry.add('field.phone', ColumnPhone);
}
return updatedFieldPhone;
});

28
base_phone/static/src/xml/phone.xml

@ -1,28 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Base phone module for Odoo
Copyright (C) 2013-2015 Alexis de Lattre <alexis@via.ecp.fr>
The licence is in the file __openerp__.py
Copyright (C) 2013-2018 Akretion France
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<templates id="template" xml:space="preserve">
<templates>
<t t-name="FieldFax" t-extend="FieldEmail">
<t t-jquery="span" t-operation="replace">
<t t-if="widget.get('effective_readonly')">
<a t-if="widget.clickable" class="o_form_uri o_text_overflow" href="#" target="_blank"/>
<span t-if="!widget.clickable" class="o_text_overflow"/>
</t>
</t>
</t>
<t t-name="FieldPhone" t-extend="FieldFax">
<t t-jquery="span" t-operation="replace">
<t t-if="widget.get('effective_readonly')">
<a t-if="widget.clickable" class="o_form_uri o_text_overflow" href="#" target="_blank"/>
<span t-if="!widget.clickable" class="o_text_overflow"/>
</t>
</t>
<t t-name="base_phone.dial">
<span class="float-right">
<a href="#" target="_blank" class="fa fa-lg fa-phone" aria-label="Dial" title="Dial"/>
</span>
</t>
</templates>

13
base_phone/tests/test_phone.py

@ -1,7 +1,10 @@
# -*- coding: utf-8 -*-
# © 2016 Akretion France (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2016-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp.tests.common import TransactionCase
from odoo.tests.common import TransactionCase
class TestPhone(TransactionCase):
@ -18,9 +21,9 @@ class TestPhone(TransactionCase):
'mobile': '06.42.77.42.66',
'fax': '(0) 1 45 42 12 42',
})
self.assertEquals(partner1.phone, u'+33 4 72 08 87 32')
self.assertEquals(partner1.mobile, u'+33 6 42 77 42 66')
self.assertEquals(partner1.fax, u'+33 1 45 42 12 42')
self.assertEquals(partner1.phone, u'+33 4 72 08 87 32')
self.assertEquals(partner1.mobile, u'+33 6 42 77 42 66')
self.assertEquals(partner1.fax, u'+33 1 45 42 12 42')
# Create a partner with country
self.env.ref('base.res_partner_12').country_id =\
self.env.ref('base.ch').id

27
base_phone/views/base_config_settings.xml

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2014-2017 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_general_configuration" model="ir.ui.view">
<field name="name">base_phone.base.config.settings.form</field>
<field name="model">base.config.settings</field>
<field name="inherit_id" ref="base_setup.view_general_configuration" />
<field name="arch" type="xml">
<group name="report" position="before">
<group name="phone" string="Phone">
<label for="number_of_digits_to_match_from_end"/>
<div>
<field name="number_of_digits_to_match_from_end" class="oe_inline"/>
</div>
</group>
</group>
</field>
</record>
</odoo>

30
base_phone/views/res_config_settings.xml

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2014-2018 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">base_phone.base.config.settings.form</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="base_setup.res_config_settings_view_form" />
<field name="arch" type="xml">
<xpath expr="//div[hasclass('settings')]" position="inside">
<div id="phone" string="Phone">
<h2>Phone</h2>
<div class="row mt16 o_settings_container">
<label for="number_of_digits_to_match_from_end"/>
<div>
<field name="number_of_digits_to_match_from_end" class="oe_inline"/>
</div>
</div>
</div>
</xpath>
</field>
</record>
</odoo>

13
base_phone/views/res_partner_view.xml

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2010-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="base.action_partner_form" model="ir.actions.act_window">
<field name="context">{'search_default_customer': 1, 'raise_if_phone_parse_fails': True}</field>
</record>
</odoo>

7
base_phone/views/res_users_view.xml

@ -1,11 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2010-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
Copyright 2010-2018 Akretion France
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_users_form" model="ir.ui.view">
<field name="name">base_phone.res.users.telephony_tab</field>
<field name="model">res.users</field>
@ -26,7 +28,7 @@
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_form_simple_modif" />
<field name="arch" type="xml">
<xpath expr="//field[@name='email']/.." position="after">
<xpath expr="//group[@name='preference_email']/.." position="after">
<group name="phone" string="Telephony Preferences" invisible="1">
<!-- Empty group, that is used by other phone modules -->
</group>
@ -34,4 +36,5 @@
</field>
</record>
</odoo>

3
base_phone/web_phone.xml → base_phone/views/web_phone.xml

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2014-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
Copyright 2014-2018 Akretion France
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->

18
base_phone/wizard/number_not_found.py

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
# © 2010-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2010-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, fields, api, _
from .. import fields as phone_fields
from odoo.exceptions import UserError
import logging
_logger = logging.getLogger(__name__)
@ -33,12 +33,10 @@ class NumberNotFound(models.TransientModel):
to_update_partner_id = fields.Many2one(
comodel_name='res.partner', string='Partner to Update',
help="Partner on which the phone number will be written")
current_partner_phone = phone_fields.Phone(
related='to_update_partner_id.phone', string='Current Phone',
readonly=True)
current_partner_mobile = phone_fields.Phone(
related='to_update_partner_id.mobile', string='Current Mobile',
readonly=True)
current_partner_phone = fields.Char(
related='to_update_partner_id.phone', string='Current Phone')
current_partner_mobile = fields.Char(
related='to_update_partner_id.mobile', string='Current Mobile')
@api.model
def default_get(self, fields_list):
@ -61,14 +59,13 @@ class NumberNotFound(models.TransientModel):
res['number_type'] = 'mobile'
else:
res['number_type'] = 'phone'
except Exception, e:
except Exception as e:
_logger.error(
"Cannot reformat the phone number '%s': %s",
res['calling_number'], e)
pass
return res
@api.multi
def create_partner(self):
'''Function called by the related button of the wizard'''
wiz = self[0]
@ -88,7 +85,6 @@ class NumberNotFound(models.TransientModel):
}
return action
@api.multi
def update_partner(self):
self.ensure_one()
wiz = self[0]

13
base_phone/wizard/number_not_found_view.xml

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2012-2016 (Alexis de Lattre <alexis.delattre@akretion.com>)
Copyright 2012-2018 Akretion France
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
@ -28,19 +29,19 @@
<group colspan="4" col="2" name="create-update">
<group name="partner" string="Create or Update a Partner"
colspan="1" col="2">
<button name="create_partner" class="oe_highlight" colspan="2"
string="Create Partner with this Number" type="object"/>
<button name="create_partner" class="btn-primary" colspan="2"
string="Create New Partner" type="object"/>
<field name="to_update_partner_id" />
<field name="current_partner_phone"
options="{'dial_button_invisible': True}"/>
<field name="current_partner_mobile"
options="{'dial_button_invisible': True}"/>
<button name="update_partner" class="oe_highlight" colspan="2"
string="Update Partner with this Number" type="object"/>
<button name="update_partner" class="btn-primary" colspan="2"
string="Update Existing Partner" type="object"/>
</group>
</group>
<footer>
<button special="cancel" string="Close" class="oe_link"/>
<button special="cancel" string="Close" class="btn-default"/>
</footer>
</form>
</field>

19
base_phone/wizard/reformat_all_phonenumbers.py

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# © 2012-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2012-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, fields, api
@ -20,11 +21,10 @@ class ReformatAllPhonenumbers(models.TransientModel):
('done', 'Done'),
], string='State', default='draft')
@api.multi
def run_reformat_all_phonenumbers(self):
self.ensure_one()
logger.info('Starting to reformat all the phone numbers')
phonenumbers_not_reformatted = u''
phonenumbers_not_reformatted = ''
phoneobjects = self.env['phone.common']._get_phone_models()
for obj_dict in phoneobjects:
fields = obj_dict['fields']
@ -37,7 +37,7 @@ class ReformatAllPhonenumbers(models.TransientModel):
# hr.employee inherits from 'resource.resource' and
# 'resource.resource' has an active field
# As I don't know how to detect such cases, I hardcode it here
# If you know a better solution, please tell me
# If you know a better solution, please submit a pull request
domain = ['|', ('active', '=', True), ('active', '=', False)]
else:
domain = []
@ -46,9 +46,16 @@ class ReformatAllPhonenumbers(models.TransientModel):
for entry in all_entries:
vals = {}
for field in fields:
vals[field] = entry[field]
if any([value for value in vals.values()]):
if entry[field]:
new_phone = entry.phone_format(entry[field])
if new_phone != entry[field]:
vals[field] = new_phone
if vals:
entry.write(vals)
# TODO: the warnings of the phone_format() methods are logged
# in the logfile... it would be great to get them and show them to
# the user... maybe via a rewrite of phone_format()
# with a raise_expcetion=True and catch the exception
if not phonenumbers_not_reformatted:
phonenumbers_not_reformatted = \
'All phone numbers have been reformatted successfully.'

17
base_phone/wizard/reformat_all_phonenumbers_view.xml

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Base Phone module for OpenERP
Copyright (C) 2012-2013 Alexis de Lattre <alexis@via.ecp.fr>
The licence is in the file __openerp__.py
Copyright (C) 2012-2018 Akretion France
@author: Alexis de Lattre <alexis@via.ecp.fr>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
@ -13,19 +13,19 @@
<field name="arch" type="xml">
<form string="Reformat all phone numbers">
<group name="main">
<label string="This wizard reformats the phone, mobile and fax numbers of all partners in standard international format e.g. +33141981242" colspan="2" states="draft"/>
<label colspan="2" string="Phone numbers that couldn't be reformatted:" states="done"/>
<p states="draft">This wizard reformats the phone and mobile numbers of all partners in standard international format e.g. +33 1 41 98 12 42</p>
<p states="done">Phone numbers that couldn't be reformatted:</p>
<field name="phonenumbers_not_reformatted" colspan="2" nolabel="1" states="done"/>
<field name="state" invisible="1"/>
</group>
<footer>
<button name="run_reformat_all_phonenumbers"
string="Reformat all phone numbers" type="object"
class="oe_highlight" states="draft"/>
class="btn-primary" states="draft"/>
<button name="action_next" type="object" string="Close"
class="oe_highlight" states="done"/>
class="btn-primary" states="done"/>
<button special="cancel" string="Cancel"
class="oe_link" states="draft"/>
class="btn-default" states="draft"/>
</footer>
</form>
</field>
@ -46,7 +46,6 @@
<!-- Open the Reformat Phone Numbers wizard after the installation of the module -->
<record id="reformat_all_phonenumbers_module_install" model="ir.actions.todo">
<field name="action_id" ref="reformat_all_phonenumbers_action"/>
<field name="type">automatic</field>
</record>
</odoo>

2
base_sms_client/__manifest__.py

@ -35,5 +35,5 @@
"wizard/mass_sms_view.xml",
"views/smstemplate_view.xml"
],
"installable": True,
"installable": False,
}

7
crm_phone/__manifest__.py

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
# © 2014-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2014-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'CRM Phone',
'version': '10.0.1.0.0',
'version': '12.0.1.0.0',
'category': 'Phone',
'license': 'AGPL-3',
'summary': 'Validate phone numbers in CRM',
@ -23,7 +24,7 @@ for any help or question about this module.
""",
'author': "Akretion,Odoo Community Association (OCA)",
'website': 'http://www.akretion.com/',
'depends': ['base_phone', 'crm'],
'depends': ['base_phone', 'crm_phone_validation'],
'external_dependencies': {'python': ['phonenumbers']},
'conflicts': ['crm_voip'],
'data': [

1
crm_phone/demo/crm_phonecall.xml

@ -53,5 +53,4 @@
</record>
</odoo>

30
crm_phone/models/crm_lead.py

@ -1,25 +1,25 @@
# -*- coding: utf-8 -*-
# © 2012-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2012-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, fields, api
from odoo.addons.base_phone.fields import Phone, Fax
class CrmLead(models.Model):
_inherit = 'crm.lead'
_phone_name_sequence = 20
_phone_name_fields = ['phone', 'mobile']
phone = Phone(country_field='country_id', partner_field='partner_id')
mobile = Phone(country_field='country_id', partner_field='partner_id')
fax = Fax(country_field='country_id', partner_field='partner_id')
phonecall_ids = fields.One2many(
'crm.phonecall', 'opportunity_id', string='Phone Calls')
phonecall_count = fields.Integer(
compute='_count_phonecalls', string='Number of Phonecalls',
compute='_compute_phonecall_count', string='Number of Phonecalls',
readonly=True)
partner_address_mobile = fields.Char(
string='Partner Contact Mobile', related='partner_id.mobile',
readonly=True)
@api.multi
def name_get(self):
if self._context.get('callerid'):
res = []
@ -37,13 +37,11 @@ class CrmLead(models.Model):
else:
return super(CrmLead, self).name_get()
@api.multi
@api.depends('phonecall_ids')
def _count_phonecalls(self):
cpo = self.env['crm.phonecall']
for lead in self:
try:
lead.phonecall_count = cpo.search_count(
[('opportunity_id', '=', lead.id)])
except:
lead.phonecall_count = 0
def _compute_phonecall_count(self):
rg_res = self.env['crm.phonecall'].read_group(
[('opportunity_id', 'in', self.ids)],
['opportunity_id'], ['opportunity_id'])
for rg_re in rg_res:
lead = self.browse(rg_re['opportunity_id'][0])
lead.phonecall_count = rg_re['opportunity_id_count']

36
crm_phone/models/crm_phonecall.py

@ -1,14 +1,14 @@
# -*- coding: utf-8 -*-
# © 2012-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2012-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, fields, api, _
from odoo.addons.base_phone.fields import Phone
from odoo import api, fields, models
class CrmPhonecall(models.Model):
_name = 'crm.phonecall'
_inherit = ['mail.thread']
_inherit = ['mail.thread', 'phone.validation.mixin']
_order = "id desc"
# Restore the object that existed in v8
@ -28,11 +28,12 @@ class CrmPhonecall(models.Model):
default=lambda self: self.env.user)
team_id = fields.Many2one(
'crm.team', string='Sales Team', track_visibility='onchange',
default=lambda self: self.env['crm.team']._get_default_team_id())
default=lambda self: self.env['crm.team'].sudo()._get_default_team_id(
user_id=self.env.uid))
partner_id = fields.Many2one(
'res.partner', string='Contact', ondelete='cascade')
partner_phone = Phone(string='Phone', partner_field='partner_id')
partner_mobile = Phone(string='Mobile', partner_field='partner_id')
partner_phone = fields.Char(string='Phone')
partner_mobile = fields.Char(string='Mobile')
priority = fields.Selection([
('0', 'Low'),
('1', 'Normal'),
@ -70,7 +71,16 @@ class CrmPhonecall(models.Model):
self.team_id = self.opportunity_id.team_id.id
self.partner_id = self.opportunity_id.partner_id.id
@api.multi
@api.onchange('partner_phone')
def onchange_partner_phone(self):
if self.partner_phone:
self.partner_phone = self.phone_format(self.partner_phone)
@api.onchange('partner_mobile')
def onchange_partner_mobile(self):
if self.partner_mobile:
self.partner_mobile = self.phone_format(self.partner_mobile)
def schedule_another_call(self):
self.ensure_one()
cur_call = self[0]
@ -83,11 +93,11 @@ class CrmPhonecall(models.Model):
'default_partner_phone': cur_call.partner_phone,
'default_partner_mobile': cur_call.partner_mobile,
})
action = {
'name': _('Phone Call'),
'type': 'ir.actions.act_window',
'res_model': 'crm.phonecall',
action = self.env['ir.actions.act_window'].for_xml_id(
'crm_phone', 'crm_phonecall_action')
action.update({
'view_mode': 'form,tree,calendar',
'views': False,
'context': ctx,
}
})
return action

5
crm_phone/models/phone_common.py

@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
# © 2012-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2012-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, api, _
from odoo import api, models, _
class PhoneCommon(models.AbstractModel):

23
crm_phone/models/res_partner.py

@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
# © 2012-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2012-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, fields, api
from odoo import api, fields, models
class ResPartner(models.Model):
@ -11,16 +12,14 @@ class ResPartner(models.Model):
phonecall_ids = fields.One2many(
'crm.phonecall', 'partner_id', string='Phone Calls')
phonecall_count = fields.Integer(
compute='_count_phonecalls', string='Number of Phonecalls',
compute='_compute_phonecall_count', string='Number of Phonecalls',
readonly=True)
@api.multi
@api.depends('phonecall_ids')
def _count_phonecalls(self):
cpo = self.env['crm.phonecall']
for partner in self:
try:
partner.phonecall_count = cpo.search_count(
[('partner_id', 'child_of', partner.id)])
except:
partner.phonecall_count = 0
def _compute_phonecall_count(self):
rg_res = self.env['crm.phonecall'].read_group(
[('partner_id', 'in', self.ids)],
['partner_id'], ['partner_id'])
for rg_re in rg_res:
partner = self.browse(rg_re['partner_id'][0])
partner.phonecall_count = rg_re['partner_id_count']

3
crm_phone/models/res_users.py

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# © 2012-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2012-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, fields

27
crm_phone/tests/test_crm_phone.py

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# © 2016 Akretion France (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2016-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo.tests.common import TransactionCase
@ -14,11 +15,10 @@ class TestCRMPhone(TransactionCase):
'partner_name': 'Ford',
'contact_name': 'Jacques Toufaux',
'mobile': '06.42.77.42.77',
'fax': '(0) 1 45 44 42 43',
'country_id': self.env.ref('base.fr').id,
})
self.assertEquals(lead1.mobile, u'+33 6 42 77 42 77')
self.assertEquals(lead1.fax, u'+33 1 45 44 42 43')
lead1._onchange_mobile_validation()
self.assertEquals(lead1.mobile, u'+33 6 42 77 42 77')
lead2 = clo.create({
'name': u'Automobile Odoo deployment',
'partner_name': u'Kia',
@ -26,19 +26,26 @@ class TestCRMPhone(TransactionCase):
'country_id': self.env.ref('base.ch').id,
'phone': '04 31 23 45 67',
})
self.assertEquals(lead2.phone, u'+41 43 123 45 67')
lead2._onchange_phone_validation()
self.assertEquals(lead2.phone, u'+41 43 123 45 67')
lead3 = clo.create({
'name': 'Angela Strasse',
'country_id': self.env.ref('base.de').id,
'phone': '08912345678',
})
lead3._onchange_phone_validation()
self.assertEquals(lead3.phone, u'+49 89 12345678')
partner4 = self.env['res.partner'].create({
'name': 'Belgian Guy',
'country_id': self.env.ref('base.be').id,
})
lead3.write({'phone': '08912345678'})
self.assertEquals(lead3.phone, u'+49 89 12345678')
lead4 = clo.create({
'name': 'Large Odoo deployment',
'partner_id': self.env.ref('base.res_partner_2').id,
'partner_id': partner4.id,
'mobile': '(0) 2-391-43-75',
})
lead4.write({'mobile': '(0) 2-391-43-75'})
self.assertEquals(lead4.mobile, u'+32 2 391 43 75')
lead4._onchange_mobile_validation()
self.assertEquals(lead4.mobile, u'+32 2 391 43 75')
pco = self.env['phone.common']
name = pco.get_name_from_phone_number('0642774277')
self.assertEquals(name, 'Jacques Toufaux (Ford)')

17
crm_phone/view/crm_lead.xml

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2014-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
Copyright 2014-2018 Akretion France
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
@ -20,6 +21,9 @@
<field string="Calls" name="phonecall_count" widget="statinfo"/>
</button>
</div>
<field name="mobile" position="attributes">
<attribute name="widget">phone</attribute>
</field>
</field>
</record>
@ -36,6 +40,17 @@
<field string="Calls" name="phonecall_count" widget="statinfo"/>
</button>
</div>
<field name="mobile" position="replace"/>
<field name="phone" position="after">
<field name="partner_address_mobile"
attrs="{'invisible': [('partner_address_mobile', '==', False)]}"
readonly="1"
widget="phone"
string="Mobile"/>
<field name="mobile"
attrs="{'invisible': [('partner_address_mobile', '!=', False)]}"
widget="phone"/>
</field>
</field>
</record>

30
crm_phone/view/crm_phonecall.xml

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2014-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
Copyright 2014-2018 Akretion France
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
@ -32,13 +33,13 @@
<field name="date"/>
<field name="user_id"/>
<field name="partner_id"/>
<field name="partner_phone"/>
<field name="partner_mobile"/>
<field name="partner_phone" widget="phone"/>
<field name="partner_mobile" widget="phone"/>
<field name="opportunity_id"/>
</group>
<group name="right">
<field name="direction"/>
<field name="team_id" groups="base.group_multi_salesteams"/>
<field name="team_id"/>
<field name="company_id" groups="base.group_multi_company"/>
<field name="priority" widget="priority"/>
</group>
@ -66,11 +67,11 @@
<field name="partner_id"/>
<field name="opportunity_id"/>
<field name="user_id"/>
<field name="team_id" groups="base.group_multi_salesteams"/>
<field name="team_id"/>
<field name="direction"/>
<field name="state"/>
<button name="schedule_another_call" type="object"
string="Schedule Another Call" icon="terp-call-start"/>
string="Schedule Another Call" icon="fa-calendar"/>
</tree>
</field>
</record>
@ -96,24 +97,23 @@
<field name="user_id"/>
<field name="partner_id" operator="child_of"/>
<field name="opportunity_id"/>
<field name="team_id" groups="base.group_multi_salesteams"/>
<filter string="My Phonecalls" domain="[('user_id', '=', uid)]"/>
<filter string="My Team" domain="[('section_id.user_id', '=', uid)]"/>
<filter string="Unassigned" domain="[('user_id', '=', False)]"/>
<field name="team_id"/>
<filter name="my_calls" string="My Phonecalls" domain="[('user_id', '=', uid)]"/>
<filter name="my_team" string="My Team" domain="[('team_id.user_id', '=', uid)]"/>
<filter name="unassigned" string="Unassigned" domain="[('user_id', '=', False)]"/>
<separator/>
<filter string="To Do" name="open" domain="[('state', '=', 'open')]"/>
<filter string="Held" name="done" domain="[('state', '=', 'done')]"/>
<separator/>
<filter string="Inbound" domain="[('direction', '=', 'inbound')]"/>
<filter string="Outbound" domain="[('direction', '=', 'outbound')]"/>
<filter name="inbound" string="Inbound" domain="[('direction', '=', 'inbound')]"/>
<filter name="outbound" string="Outbound" domain="[('direction', '=', 'outbound')]"/>
<group string="Group By" name="groupby">
<filter name="partner_groupby" string="Partner"
context="{'group_by': 'partner_id'}"/>
<filter name="user_groupby" string="Responsible"
context="{'group_by': 'user_id'}"/>
<filter name="team_groupby" string="Team"
context="{'group_by': 'team_id'}"
groups="base.group_multi_salesteams"/>
context="{'group_by': 'team_id'}"/>
<filter name="date_groupby" string="Month"
context="{'group_by': 'date'}"/>
<filter name="state_groupby" string="State"
@ -126,7 +126,7 @@
</record>
<menuitem id="crm_phonecall_menu" name="Phone Calls"
parent="sales_team.menu_base_partner" sequence="7" groups="sales_team.group_sale_salesman"/>
parent="crm.crm_menu_root" sequence="15" groups="sales_team.group_sale_salesman"/>
<record id="crm_phonecall_action" model="ir.actions.act_window">
<field name="name">Phone Calls</field>

3
crm_phone/view/res_partner.xml

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
Copyright 2016-2018 Akretion France
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->

3
crm_phone/view/res_users.xml

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2012-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
Copyright 2012-2018 Akretion France
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->

19
crm_phone/wizard/create_crm_phonecall.py

@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
# © 2012-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2012-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, api, _
from odoo import api, models
import logging
logger = logging.getLogger(__name__)
@ -14,8 +15,8 @@ except ImportError:
class WizardCreateCrmPhonecall(models.TransientModel):
_name = "wizard.create.crm.phonecall"
_description = "Propose to create a phone call in CRM"
@api.multi
def button_create_outgoing_phonecall(self):
self.ensure_one()
return self._create_open_crm_phonecall('outbound')
@ -46,13 +47,11 @@ class WizardCreateCrmPhonecall(models.TransientModel):
else:
action_ctx['default_partner_phone'] =\
self.env.context.get('phone_number')
return {
'name': _('Phone Call'),
'type': 'ir.actions.act_window',
'res_model': 'crm.phonecall',
action = self.env['ir.actions.act_window'].for_xml_id(
'crm_phone', 'crm_phonecall_action')
action.update({
'domain': domain,
'view_mode': 'form,tree,calendar',
'nodestroy': False, # close the pop-up wizard after action
'target': 'current',
'context': action_ctx,
}
})
return action

11
crm_phone/wizard/create_crm_phonecall_view.xml

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2012-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
Copyright 2012-2018 Akretion France
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
@ -11,10 +12,12 @@
<field name="name">create_crm_phonecall.view</field>
<field name="model">wizard.create.crm.phonecall</field>
<field name="arch" type="xml">
<form string="CRM phone call" version="7.0">
<label string="Create a phone call in the CRM ?"/>
<form string="CRM Phone Call">
<group name="main">
<label string="Create a phone call in the CRM ?" for="id"/>
</group>
<footer>
<button name="button_create_outgoing_phonecall" string="Yes" type="object" class="oe_highlight"/>
<button name="button_create_outgoing_phonecall" string="Yes" type="object" class="btn-primary"/>
<button special="cancel" string="No" class="oe_link"/>
</footer>
</form>

86
crm_phone/wizard/number_not_found.py

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
# © 2010-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2010-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, fields, api, _
from odoo.addons.base_phone.fields import Phone
from odoo import fields, models, _
from odoo.exceptions import UserError
@ -11,55 +11,63 @@ class NumberNotFound(models.TransientModel):
_inherit = "number.not.found"
to_update_lead_id = fields.Many2one(
'crm.lead', string='Lead to Update',
domain=[('type', '=', 'lead')],
help="Lead on which the phone number will be written")
current_lead_phone = Phone(
related='to_update_lead_id.phone', string='Current Phone',
readonly=True)
current_lead_mobile = Phone(
related='to_update_lead_id.mobile', string='Current Mobile',
readonly=True)
'crm.lead', string='Lead or Opportunity to Update',
help="Lead or opportunity on which the phone number will be written.")
current_lead_phone = fields.Char(
related='to_update_lead_id.phone', readonly=True)
current_lead_mobile = fields.Char(
related='to_update_lead_id.mobile', readonly=True)
@api.multi
def create_lead(self):
'''Function called by the related button of the wizard'''
self.ensure_one()
action = {
'name': _('Create New Lead'),
'type': 'ir.actions.act_window',
'res_model': 'crm.lead',
'view_mode': 'form,tree',
'domain': ['|', ('type', '=', 'lead'), ('type', '=', False)],
'nodestroy': False,
'target': 'current',
action = self.env['ir.actions.act_window'].for_xml_id(
'crm', 'crm_lead_all_leads')
form_views = [viewt for viewt in action['views'] if viewt[1] == 'form']
action.update({
'view_mode': 'form',
'views': form_views,
'context': {
'default_%s' % self.number_type: self.e164_number,
'default_type': 'lead',
'stage_type': 'lead',
'needaction_menu_ref': 'crm.menu_crm_opportunities',
'search_default_type': 'lead',
'search_default_to_process': True,
},
})
return action
def create_opportunity(self):
'''Function called by the related button of the wizard'''
self.ensure_one()
action = self.env['ir.actions.act_window'].for_xml_id(
'crm', 'crm_lead_opportunities')
form_views = [viewt for viewt in action['views'] if viewt[1] == 'form']
action.update({
'view_mode': 'form',
'views': form_views,
'context': {
'default_%s' % self.number_type: self.e164_number,
'default_type': 'opportunity',
'search_default_type': 'opportunity',
},
}
})
return action
@api.multi
def update_lead(self):
self.ensure_one()
if not self.to_update_lead_id:
raise UserError(_("Select the Lead to Update."))
raise UserError(_("Select the Lead or Opportunity to Update."))
self.to_update_lead_id.write({self.number_type: self.e164_number})
action = {
'name': _('Lead: %s' % self.to_update_lead_id.name),
'type': 'ir.actions.act_window',
'res_model': 'crm.lead',
'view_mode': 'form,tree',
'nodestroy': False,
'target': 'current',
if self.to_update_lead_id.type == 'lead':
action = self.env['ir.actions.act_window'].for_xml_id(
'crm', 'crm_lead_all_leads')
else:
action = self.env['ir.actions.act_window'].for_xml_id(
'crm', 'crm_lead_opportunities')
form_views = [viewt for viewt in action['views'] if viewt[1] == 'form']
action.update({
'view_mode': 'form',
'views': form_views,
'res_id': self.to_update_lead_id.id,
'context': {
'stage_type': 'lead',
'needaction_menu_ref': 'crm.menu_crm_opportunities',
},
}
})
return action

18
crm_phone/wizard/number_not_found_view.xml

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2014-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
Copyright 2014-2018 Akretion France
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
@ -16,16 +17,19 @@
<group name="lead" string="Create or Update a Lead"
colspan="1">
<button name="create_lead" colspan="2"
string="Create Lead with this Number" type="object"
class="oe_highlight"/>
string="Create New Lead" type="object"
groups="crm.group_use_lead" class="btn-primary"/>
<button name="create_opportunity" colspan="2"
string="Create New Opportunity" type="object"
class="btn-primary"/>
<field name="to_update_lead_id"/>
<field name="current_lead_phone"
<field name="current_lead_phone" string="Current Phone"
options="{'dial_button_invisible': True}"/>
<field name="current_lead_mobile"
<field name="current_lead_mobile" string="Current Mobile"
options="{'dial_button_invisible': True}"/>
<button name="update_lead" colspan="2"
string="Update Lead with this Number" type="object"
class="oe_highlight"/>
string="Update Existing Lead/Opportunity" type="object"
class="btn-primary"/>
</group>
</group>
</field>

10
event_phone/__manifest__.py

@ -1,11 +1,12 @@
# -*- coding: utf-8 -*-
# © 2014-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2014-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'Event Phone',
'version': '10.0.1.0.0',
'version': '12.0.1.0.0',
'category': 'Phone',
'license': 'AGPL-3',
'summary': 'Validate phone numbers in Events',
@ -25,7 +26,10 @@ for any help or question about this module.
'author': "Akretion,Odoo Community Association (OCA)",
'website': 'http://www.akretion.com/',
'depends': ['base_phone', 'event'],
'data': ['security/ir.model.access.csv'],
'data': [
'security/ir.model.access.csv',
'views/hr_registration.xml',
],
'installable': True,
'auto_install': True,
}

15
event_phone/models/event_registration.py

@ -1,13 +1,18 @@
# -*- coding: utf-8 -*-
# © 2012-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2012-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models
from odoo.addons.base_phone.fields import Phone
from odoo import api, models
class EventRegistration(models.Model):
_inherit = 'event.registration'
_name = 'event.registration'
_inherit = ['event.registration', 'phone.validation.mixin']
_phone_name_sequence = 100
_phone_name_fields = ['phone']
phone = Phone(partner_field='partner_id')
@api.onchange('phone')
def phone_change(self):
if self.phone:
self.phone = self.phone_format(self.phone)

23
event_phone/views/hr_registration.xml

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2018 Akretion France
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_event_registration_form" model="ir.ui.view">
<field name="name">phone.event.registration.form</field>
<field name="model">event.registration</field>
<field name="inherit_id" ref="event.view_event_registration_form"/>
<field name="arch" type="xml">
<field name="phone" position="attributes">
<attribute name="widget">phone</attribute>
</field>
</field>
</record>
</odoo>

5
hr_phone/__manifest__.py

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
# © 2014-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2014-2016 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'HR Phone',
'version': '10.0.1.0.0',
'version': '12.0.1.0.0',
'category': 'Phone',
'license': 'AGPL-3',
'summary': 'Validate phone numbers in HR',

21
hr_phone/models/hr_employee.py

@ -1,14 +1,23 @@
# -*- coding: utf-8 -*-
# © 2012-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2012-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models
from odoo.addons.base_phone.fields import Phone
from odoo import api, models
class HrEmployee(models.Model):
_inherit = 'hr.employee'
_name = 'hr.employee'
_inherit = ['hr.employee', 'phone.validation.mixin']
_phone_name_sequence = 30
_phone_name_fields = ['work_phone', 'mobile_phone']
work_phone = Phone(country_field='country_id')
mobile_phone = Phone(country_field='country_id')
@api.onchange('work_phone')
def work_phone_change(self):
if self.work_phone:
self.work_phone = self.phone_format(self.work_phone)
@api.onchange('mobile_phone')
def mobile_phone_change(self):
if self.mobile_phone:
self.mobile_phone = self.phone_format(self.mobile_phone)

9
hr_recruitment_phone/__manifest__.py

@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
# © 2014-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2014-2018 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'HR Recruitment Phone',
'version': '10.0.1.0.0',
'version': '12.0.1.0.0',
'category': 'Phone',
'license': 'AGPL-3',
'summary': 'Validate phone numbers in HR Recruitment',
@ -24,7 +24,10 @@ for any help or question about this module.
'author': "Akretion,Odoo Community Association (OCA)",
'website': 'http://www.akretion.com/',
'depends': ['base_phone', 'hr_recruitment'],
'data': ['security/ir.model.access.csv'],
'data': [
'security/ir.model.access.csv',
'views/hr_applicant.xml',
],
'installable': True,
'auto_install': True,
}

22
hr_recruitment_phone/models/hr_applicant.py

@ -1,19 +1,27 @@
# -*- coding: utf-8 -*-
# © 2012-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2012-2018 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, api
from odoo.addons.base_phone.fields import Phone
from odoo import api, models
class HrApplicant(models.Model):
_inherit = 'hr.applicant'
_name = 'hr.applicant'
_inherit = ['hr.applicant', 'phone.validation.mixin']
_phone_name_sequence = 50
_phone_name_fields = ['partner_phone', 'partner_mobile']
partner_phone = Phone(partner_field='partner_id')
partner_mobile = Phone(partner_field='partner_id')
@api.onchange('partner_phone')
def partner_phone_change(self):
if self.partner_phone:
self.partner_phone = self.phone_format(self.partner_phone)
@api.onchange('partner_mobile')
def partner_mobile_change(self):
if self.partner_mobile:
self.partner_mobile = self.phone_format(self.partner_mobile)
@api.multi
def name_get(self):
if self._context.get('callerid'):
res = []

26
hr_recruitment_phone/views/hr_applicant.xml

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2018 Akretion France
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="crm_case_form_view_job" model="ir.ui.view">
<field name="name">phone.hr.applicant.form</field>
<field name="model">hr.applicant</field>
<field name="inherit_id" ref="hr_recruitment.crm_case_form_view_job"/>
<field name="arch" type="xml">
<field name="partner_phone" position="attributes">
<attribute name="widget">phone</attribute>
</field>
<field name="partner_mobile" position="attributes">
<attribute name="widget">phone</attribute>
</field>
</field>
</record>
</odoo>

2
ovh_sms_client/__manifest__.py

@ -23,5 +23,5 @@
'website': 'http://julius.fr',
'category': 'Tools',
'data': ['data/keychain.xml'],
'installable': True,
'installable': False,
}
Loading…
Cancel
Save