From b08e19e3a86182531a3b99bdaacd7eb2d6f0869e Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Mon, 2 Nov 2015 11:36:49 +0100 Subject: [PATCH] Allow creation of partners from *Create and Edit*. This fixes #78 and adds new tests for it. --- partner_firstname/__openerp__.py | 4 +- partner_firstname/models.py | 42 ++++++++++--- partner_firstname/tests/__init__.py | 3 +- partner_firstname/tests/base.py | 2 +- partner_firstname/tests/test_create.py | 81 ++++++++++++++++++++++++++ 5 files changed, 120 insertions(+), 12 deletions(-) create mode 100644 partner_firstname/tests/test_create.py diff --git a/partner_firstname/__openerp__.py b/partner_firstname/__openerp__.py index 4b8d9ce9e..463b01c97 100644 --- a/partner_firstname/__openerp__.py +++ b/partner_firstname/__openerp__.py @@ -21,8 +21,8 @@ { 'name': 'Partner first name and last name', 'summary': "Split first name and last name for non company partners", - 'version': '8.0.2.0.0', - 'author': "Camptocamp, Odoo Community Association (OCA)", + 'version': '8.0.2.0.1', + 'author': "Camptocamp, Grupo ESOC, Odoo Community Association (OCA)", "license": "AGPL-3", 'maintainer': 'Camptocamp, Acsone', 'category': 'Extra Tools', diff --git a/partner_firstname/models.py b/partner_firstname/models.py index 48342b20d..4500e478d 100644 --- a/partner_firstname/models.py +++ b/partner_firstname/models.py @@ -41,17 +41,37 @@ class ResPartner(models.Model): @api.model def create(self, vals): """Add inverted names at creation if unavailable.""" - if "name" in vals: + context = dict(self.env.context) + name = vals.get("name", context.get("default_name")) + + if name is not None: + # Calculate the splitted fields inverted = self._get_inverse_name( - vals.get("name"), + self._get_whitespace_cleaned_name(name), vals.get("is_company", self.default_get(["is_company"])["is_company"])) for key, value in inverted.iteritems(): - if not vals.get(key): + if not vals.get(key) or context.get("copy"): vals[key] = value - return super(ResPartner, self).create(vals) + # Remove the combined fields + if "name" in vals: + del vals["name"] + if "default_name" in context: + del context["default_name"] + + return super(ResPartner, self.with_context(context)).create(vals) + + @api.multi + def copy(self, default=None): + """Ensure partners are copied right. + + Odoo adds ``(copy)`` to the end of :attr:`~.name`, but that would get + ignored in :meth:`~.create` because it also copies explicitly firstname + and lastname fields. + """ + return super(ResPartner, self.with_context(copy=True)).copy(default) @api.model def default_get(self, fields_list): @@ -59,7 +79,7 @@ class ResPartner(models.Model): result = super(ResPartner, self).default_get(fields_list) inverted = self._get_inverse_name( - result.get("name", ""), + self._get_whitespace_cleaned_name(result.get("name", "")), result.get("is_company", False)) for field in inverted.keys(): @@ -85,13 +105,11 @@ class ResPartner(models.Model): def _inverse_name_after_cleaning_whitespace(self): """Clean whitespace in :attr:`~.name` and split it. - Removes leading, trailing and duplicated whitespace. - The splitting logic is stored separately in :meth:`~._inverse_name`, so submodules can extend that method and get whitespace cleaning for free. """ # Remove unneeded whitespace - clean = u" ".join(self.name.split(None)) if self.name else self.name + clean = self._get_whitespace_cleaned_name(self.name) # Clean name avoiding infinite recursion if self.name != clean: @@ -101,6 +119,14 @@ class ResPartner(models.Model): else: self._inverse_name() + @api.model + def _get_whitespace_cleaned_name(self, name): + """Remove redundant whitespace from :param:`name`. + + Removes leading, trailing and duplicated whitespace. + """ + return u" ".join(name.split(None)) if name else name + @api.model def _get_inverse_name(self, name, is_company=False): """Compute the inverted name. diff --git a/partner_firstname/tests/__init__.py b/partner_firstname/tests/__init__.py index 239670f65..2bdf39b05 100644 --- a/partner_firstname/tests/__init__.py +++ b/partner_firstname/tests/__init__.py @@ -3,6 +3,7 @@ # # Authors: Nemry Jonathan # Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu) +# 2015: Grupo ESOC # All Rights Reserved # # WARNING: This program as such is intended to be used by professional @@ -28,4 +29,4 @@ # ############################################################################## -from . import test_defaults, test_empty, test_name, test_onchange +from . import test_create, test_defaults, test_empty, test_name, test_onchange diff --git a/partner_firstname/tests/base.py b/partner_firstname/tests/base.py index 85e204435..68020d3bf 100644 --- a/partner_firstname/tests/base.py +++ b/partner_firstname/tests/base.py @@ -71,7 +71,7 @@ class BaseCase(TransactionCase, MailInstalled): def test_copy(self): """Copy the partner and compare the result.""" self.expect(self.lastname, u"%s (copy)" % self.firstname) - self.changed = self.original.with_context(lang="en_US").copy() + self.changed = self.original.with_context(copy=True, lang="en_US").copy() def test_one_name(self): """Test what happens when only one name is given.""" diff --git a/partner_firstname/tests/test_create.py b/partner_firstname/tests/test_create.py new file mode 100644 index 000000000..6ffe373ac --- /dev/null +++ b/partner_firstname/tests/test_create.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- +# © 2015 Grupo ESOC Ingeniería de Servicios, S.L. - Jairo Llopis. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +"""Test default values for models.""" + +from openerp.tests.common import TransactionCase +from .base import MailInstalled + + +class PersonCase(TransactionCase): + """Test ``res.partner`` when it is a person.""" + context = {"default_is_company": False} + model = "res.partner" + + def setUp(self): + super(PersonCase, self).setUp() + self.good_values = { + "firstname": u"Núñez", + "lastname": u"Fernán", + } + self.good_values["name"] = "%s %s" % (self.good_values["lastname"], + self.good_values["firstname"]) + if "default_is_company" in self.context: + self.good_values["is_company"] = self.context["default_is_company"] + self.values = self.good_values.copy() + + def tearDown(self): + self.record = (self.env[self.model] + .with_context(self.context) + .create(self.values)) + + for key, value in self.good_values.iteritems(): + self.assertEqual( + self.record[key], + value, + "Checking key %s" % key) + + super(PersonCase, self).tearDown() + + def test_no_name(self): + """Name is calculated.""" + del self.values["name"] + + def test_wrong_name_value(self): + """Wrong name value is ignored, name is calculated.""" + self.values["name"] = u"BÄD" + + def test_wrong_name_context(self): + """Wrong name context is ignored, name is calculated.""" + del self.values["name"] + self.context["default_name"] = u"BÄD" + + def test_wrong_name_value_and_context(self): + """Wrong name value and context is ignored, name is calculated.""" + self.values["name"] = u"BÄD1" + self.context["default_name"] = u"BÄD2" + + +class CompanyCase(PersonCase): + """Test ``res.partner`` when it is a company.""" + context = {"default_is_company": True} + + def setUp(self): + return super(CompanyCase, self).setUp() + self.values.update(lastname=self.values["name"], firstname=False) + + +class UserCase(PersonCase, MailInstalled): + """Test ``res.users``.""" + model = "res.users" + context = {"default_login": "user@example.com"} + + def tearDown(self): + # Cannot create users if ``mail`` is installed + if self.mail_installed(): + # Skip tests + super(PersonCase, self).tearDown() + else: + # Run tests + super(UserCase, self).tearDown()