Browse Source

[MIG] partner_email_check: Migration to 13.0

14.0
Quentin Groulard 4 years ago
committed by Zar21
parent
commit
aa70ad6731
  1. 28
      partner_email_check/__manifest__.py
  2. 2
      partner_email_check/i18n/partner_email_check.pot
  3. 25
      partner_email_check/models/res_config_settings.py
  4. 70
      partner_email_check/models/res_partner.py
  5. 110
      partner_email_check/tests/test_partner_email_check.py
  6. 10
      partner_email_check/views/base_config_view.xml

28
partner_email_check/__manifest__.py

@ -2,20 +2,16 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
'name': 'Email Format Checker',
'version': '12.0.1.0.0',
'summary': 'Validate email address field',
'author': "Komit, Odoo Community Association (OCA)",
'website': 'https://github.com/OCA/partner-contact',
'category': 'Tools',
'depends': ['base_setup'],
'installable': True,
'application': False,
'license': 'AGPL-3',
'external_dependencies': {
'python': ['email_validator']
},
'data': [
'views/base_config_view.xml',
]
"name": "Email Format Checker",
"version": "13.0.1.0.0",
"summary": "Validate email address field",
"author": "Komit, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/partner-contact",
"category": "Tools",
"depends": ["base_setup"],
"installable": True,
"application": False,
"license": "AGPL-3",
"external_dependencies": {"python": ["email-validator"]},
"data": ["views/base_config_view.xml"],
}

2
partner_email_check/i18n/partner_email_check.pot

@ -4,7 +4,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 12.0\n"
"Project-Id-Version: Odoo Server 13.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: <>\n"
"Language-Team: \n"

25
partner_email_check/models/res_config_settings.py

@ -2,7 +2,7 @@ from odoo import api, fields, models
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
_inherit = "res.config.settings"
partner_email_check_filter_duplicates = fields.Boolean(
string="Filter duplicate partner email addresses",
@ -17,26 +17,27 @@ class ResConfigSettings(models.TransientModel):
@api.model
def get_values(self):
res = super(ResConfigSettings, self).get_values()
conf = self.env['ir.config_parameter'].sudo()
conf = self.env["ir.config_parameter"].sudo()
res.update(
partner_email_check_filter_duplicates=conf.get_param(
'partner_email_check_filter_duplicates', 'False'
) == 'True',
"partner_email_check_filter_duplicates", "False"
)
== "True",
partner_email_check_check_deliverability=conf.get_param(
'partner_email_check_check_deliverability', 'False'
) == 'True',
"partner_email_check_check_deliverability", "False"
)
== "True",
)
return res
@api.multi
def set_values(self):
super(ResConfigSettings, self).set_values()
conf = self.env['ir.config_parameter'].sudo()
conf = self.env["ir.config_parameter"].sudo()
conf.set_param(
'partner_email_check_filter_duplicates',
self.partner_email_check_filter_duplicates
"partner_email_check_filter_duplicates",
self.partner_email_check_filter_duplicates,
)
conf.set_param(
'partner_email_check_check_deliverability',
self.partner_email_check_check_deliverability
"partner_email_check_check_deliverability",
self.partner_email_check_check_deliverability,
)

70
partner_email_check/models/res_partner.py

@ -2,7 +2,8 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import logging
from odoo import api, models, _
from odoo import _, api, models
from odoo.exceptions import UserError, ValidationError
from odoo.tools import config
@ -21,29 +22,31 @@ except ImportError:
class ResPartner(models.Model):
_inherit = 'res.partner'
_inherit = "res.partner"
@api.model
def email_check(self, emails):
if (config['test_enable'] and
not self.env.context.get('test_partner_email_check')):
if config["test_enable"] and not self.env.context.get(
"test_partner_email_check"
):
return emails
return ','.join(self._normalize_email(email.strip())
for email in emails.split(','))
return ",".join(
self._normalize_email(email.strip()) for email in emails.split(",")
)
@api.constrains('email')
@api.constrains("email")
def _check_email_unique(self):
if self._should_filter_duplicates():
for rec in self.filtered("email"):
if ',' in rec.email:
if "," in rec.email:
raise UserError(
_("Field contains multiple email addresses. This is "
"not supported when duplicate email addresses are "
"not allowed.")
_(
"Field contains multiple email addresses. This is "
"not supported when duplicate email addresses are "
"not allowed."
)
)
if self.search_count(
[('email', '=', rec.email), ('id', '!=', rec.id)]
):
if self.search_count([("email", "=", rec.email), ("id", "!=", rec.id)]):
raise UserError(
_("Email '%s' is already in use.") % rec.email.strip()
)
@ -51,45 +54,46 @@ class ResPartner(models.Model):
def _normalize_email(self, email):
if validate_email is None:
_logger.warning(
'Can not validate email, '
'python dependency required "email_validator"')
"Can not validate email, "
'python dependency required "email_validator"'
)
return email
try:
result = validate_email(
email,
check_deliverability=self._should_check_deliverability(),
email, check_deliverability=self._should_check_deliverability(),
)
except EmailSyntaxError:
raise ValidationError(
_("%s is an invalid email") % email.strip()
)
raise ValidationError(_("%s is an invalid email") % email.strip())
except EmailUndeliverableError:
raise ValidationError(
_("Cannot deliver to email address %s") % email.strip()
)
return result['local'].lower() + '@' + result['domain_i18n']
return result["local"].lower() + "@" + result["domain_i18n"]
def _should_filter_duplicates(self):
conf = self.env['ir.config_parameter'].sudo().get_param(
'partner_email_check_filter_duplicates', 'False'
conf = (
self.env["ir.config_parameter"]
.sudo()
.get_param("partner_email_check_filter_duplicates", "False")
)
return conf == 'True'
return conf == "True"
def _should_check_deliverability(self):
conf = self.env['ir.config_parameter'].sudo().get_param(
'partner_email_check_check_deliverability', 'False'
conf = (
self.env["ir.config_parameter"]
.sudo()
.get_param("partner_email_check_check_deliverability", "False")
)
return conf == 'True'
return conf == "True"
@api.model
def create(self, vals):
if vals.get('email'):
vals['email'] = self.email_check(vals['email'])
if vals.get("email"):
vals["email"] = self.email_check(vals["email"])
return super(ResPartner, self).create(vals)
@api.multi
def write(self, vals):
if vals.get('email'):
vals['email'] = self.email_check(vals['email'])
if vals.get("email"):
vals["email"] = self.email_check(vals["email"])
return super(ResPartner, self).write(vals)

110
partner_email_check/tests/test_partner_email_check.py

@ -3,7 +3,7 @@
from unittest.mock import patch
from odoo.exceptions import ValidationError
from odoo.exceptions import UserError, ValidationError
from odoo.tests.common import TransactionCase
from odoo.tools.misc import mute_logger
@ -12,13 +12,11 @@ class TestPartnerEmailCheck(TransactionCase):
def setUp(self):
super(TestPartnerEmailCheck, self).setUp()
# Checks are disabled during tests unless this key is set
self.res_partner = self.env['res.partner'].with_context(
self.res_partner = self.env["res.partner"].with_context(
test_partner_email_check=True
)
self.test_partner = self.res_partner.create({
'name': 'test',
})
self.wizard = self.env['res.config.settings'].create({})
self.test_partner = self.res_partner.create({"name": "test"})
self.wizard = self.env["res.config.settings"].create({})
self.wizard.partner_email_check_filter_duplicates = False
self.wizard.partner_email_check_check_deliverability = False
self.wizard.set_values()
@ -26,50 +24,49 @@ class TestPartnerEmailCheck(TransactionCase):
def test_bad_email(self):
"""Test rejection of bad emails."""
with self.assertRaises(ValidationError):
self.test_partner.email = 'bad@email@domain..com'
self.test_partner.email = "bad@email@domain..com"
def test_good_email(self):
"""Test acceptance of good"""
self.test_partner.email = 'goodemail@domain.com'
self.test_partner.email = "goodemail@domain.com"
self.assertTrue(self.test_partner.email)
def test_bad_emails(self):
"""Test rejection of bad emails."""
with self.assertRaises(ValidationError):
self.test_partner.email = 'good@domain.com,bad@email@domain..com'
self.test_partner.email = "good@domain.com,bad@email@domain..com"
def test_good_emails(self):
"""Test acceptance of good"""
self.test_partner.email = 'goodemail@domain.com,goodemail2@domain.com'
self.test_partner.email = "goodemail@domain.com,goodemail2@domain.com"
self.assertTrue(self.test_partner.email)
def test_email_domain_normalization(self):
"""Test normalization of email domain names, including punycode."""
self.test_partner.write({'email': 'goodemail@xn--xamPle-9ua.com'})
self.assertEqual(self.test_partner.email, u'goodemail@éxample.com')
self.test_partner.write({"email": "goodemail@xn--xamPle-9ua.com"})
self.assertEqual(self.test_partner.email, u"goodemail@éxample.com")
def test_multi_email_domain_normalization(self):
"""Test normalization of email domain names of multiple addresses."""
self.test_partner.write({
'email': 'goodemail@doMAIN.com,othergood@xn--xample-9ua.com'
})
self.test_partner.write(
{"email": "goodemail@doMAIN.com,othergood@xn--xample-9ua.com"}
)
self.assertEqual(
self.test_partner.email,
u'goodemail@domain.com,othergood@éxample.com'
self.test_partner.email, u"goodemail@domain.com,othergood@éxample.com"
)
def test_email_local_normalization(self):
"""Test normalization of the local part of email addresses."""
self.test_partner.write({'email': 'Me@mail.org'})
self.test_partner.write({"email": "Me@mail.org"})
# .lower() is locale-dependent, so don't hardcode the result
self.assertEqual(self.test_partner.email, 'Me'.lower() + '@mail.org')
self.assertEqual(self.test_partner.email, "Me".lower() + "@mail.org")
def test_multi_email_local_normalization(self):
"""Test normalization of the local part of multiple addresses."""
self.test_partner.write({'email': 'You@mAiL.net,mE@mail.com'})
self.test_partner.write({"email": "You@mAiL.net,mE@mail.com"})
self.assertEqual(
self.test_partner.email,
'You'.lower() + '@mail.net,' + 'mE'.lower() + '@mail.com'
"You".lower() + "@mail.net," + "mE".lower() + "@mail.com",
)
def disallow_duplicates(self):
@ -78,33 +75,24 @@ class TestPartnerEmailCheck(TransactionCase):
def test_duplicate_addresses_disallowed(self):
self.disallow_duplicates()
self.test_partner.write({'email': 'email@domain.tld'})
with self.assertRaises(ValidationError):
self.res_partner.create({
'name': 'alsotest',
'email': 'email@domain.tld'
})
self.test_partner.write({"email": "email@domain.tld"})
with self.assertRaises(UserError):
self.res_partner.create({"name": "alsotest", "email": "email@domain.tld"})
def test_duplicate_after_normalization_addresses_disallowed(self):
self.disallow_duplicates()
self.res_partner.create({
'name': 'alsotest',
'email': 'email@doMAIN.tld'
})
with self.assertRaises(ValidationError):
self.test_partner.email = 'email@domain.tld'
self.res_partner.create({"name": "alsotest", "email": "email@doMAIN.tld"})
with self.assertRaises(UserError):
self.test_partner.email = "email@domain.tld"
def test_multiple_addresses_disallowed_when_duplicates_filtered(self):
self.disallow_duplicates()
with self.assertRaises(ValidationError):
self.test_partner.email = 'foo@bar.org,email@domain.tld'
with self.assertRaises(UserError):
self.test_partner.email = "foo@bar.org,email@domain.tld"
def test_duplicate_addresses_allowed_by_default(self):
self.res_partner.create({
'name': 'alsotest',
'email': 'email@domain.tld',
})
self.test_partner.email = 'email@domain.tld'
self.res_partner.create({"name": "alsotest", "email": "email@domain.tld"})
self.test_partner.email = "email@domain.tld"
def check_deliverability(self):
self.wizard.partner_email_check_check_deliverability = True
@ -113,7 +101,7 @@ class TestPartnerEmailCheck(TransactionCase):
def test_deliverable_addresses_allowed(self):
self.check_deliverability()
# We only need a resolving domain, not a real user
self.test_partner.email = 'gooddomain-icraglusrk@gmail.com'
self.test_partner.email = "gooddomain-icraglusrk@gmail.com"
self.assertTrue(self.test_partner.email)
def test_nondeliverable_addresses_not_allowed(self):
@ -122,40 +110,38 @@ class TestPartnerEmailCheck(TransactionCase):
# This domain may resolve by mistake on certain network setups
# At least until a new version of email-validator is released
# See https://github.com/JoshData/python-email-validator/pull/30
self.test_partner.email = 'cezrik@acoa.nrdkt'
self.test_partner.email = "cezrik@acoa.nrdkt"
def test_config_getters(self):
other_wizard = self.env['res.config.settings'].create({})
other_wizard = self.env["res.config.settings"].create({})
self.assertFalse(other_wizard.partner_email_check_check_deliverability)
self.assertFalse(other_wizard.partner_email_check_filter_duplicates)
self.disallow_duplicates()
self.check_deliverability()
other_wizard = self.env['res.config.settings'].create({})
other_wizard = self.env["res.config.settings"].create({})
self.assertTrue(other_wizard.partner_email_check_check_deliverability)
self.assertTrue(other_wizard.partner_email_check_filter_duplicates)
@mute_logger('odoo.addons.partner_email_check.models.res_partner')
@mute_logger("odoo.addons.partner_email_check.models.res_partner")
def test_lacking_dependency_does_not_halt_execution(self):
with patch('odoo.addons.partner_email_check.models.res_partner.'
'validate_email', None):
self.test_partner.email = 'notatallvalid@@domain'
with patch(
"odoo.addons.partner_email_check.models.res_partner." "validate_email", None
):
self.test_partner.email = "notatallvalid@@domain"
@mute_logger('odoo.addons.partner_email_check.models.res_partner')
@mute_logger("odoo.addons.partner_email_check.models.res_partner")
def test_lacking_dependency_keeps_uniqueness_constraint_working(self):
self.disallow_duplicates()
with patch('odoo.addons.partner_email_check.models.res_partner.'
'validate_email', None):
self.res_partner.create({
'name': 'alsotest',
'email': 'email@domain.tld'
})
with self.assertRaises(ValidationError):
self.test_partner.email = 'email@domain.tld'
with patch(
"odoo.addons.partner_email_check.models.res_partner." "validate_email", None
):
self.res_partner.create({"name": "alsotest", "email": "email@domain.tld"})
with self.assertRaises(UserError):
self.test_partner.email = "email@domain.tld"
def test_invalid_email_addresses_allowed_during_tests(self):
# Note: testing without test_partner_email_check in the context
new_partner = self.env['res.partner'].create({
'name': 'invalidly emailed',
'email': 'invalid'
})
self.assertEqual('invalid', new_partner.email)
new_partner = self.env["res.partner"].create(
{"name": "invalidly emailed", "email": "invalid"}
)
self.assertEqual("invalid", new_partner.email)

10
partner_email_check/views/base_config_view.xml

@ -1,15 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_general_configuration" model="ir.ui.view">
<field name="name">partner_email_check</field>
<field name="model">res.config.settings</field>
<field name="inherit_id"
ref="base_setup.res_config_settings_view_form" />
<field name="inherit_id" ref="base_setup.res_config_settings_view_form" />
<field name="arch" type="xml">
<xpath expr="//div[@name='multi_company']" position='after'>
<xpath expr="//div[@id='companies']" position='after'>
<h2>Email validation</h2>
<div class="row mt16 o_settings_container"
name="partner_email_check">
<div class="row mt16 o_settings_container" name="partner_email_check">
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="partner_email_check_filter_duplicates" />

Loading…
Cancel
Save