diff --git a/partner_firstname/README.rst b/partner_firstname/README.rst index 5602d35ee..90948d667 100644 --- a/partner_firstname/README.rst +++ b/partner_firstname/README.rst @@ -1,12 +1,33 @@ .. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 +================================ Partner first name and last name ================================ This module was written to extend the functionality of contacts to support having separate last name and first name. +Configuration +============= + +You can configure some common name patterns for the inverse function +in Settings > Configuration > General settings: + +* Lastname Firstname: For example 'Anderson Robert' +* Lastname, Firstname: For example 'Anderson, Robert' +* Firstname Lastname: For example 'Robert Anderson' + +After applying the changes, you can recalculate all partners name clicking +"Recalculate names" button. Note: This process could take so much time depending +how many partners there are in database. + +You can use *_get_inverse_name* method to get lastname and firstname from a simple string +and also *_get_computed_name* to get a name form the lastname and firstname. +These methods can be overridden to change the format specified above. + + Usage ===== @@ -16,22 +37,40 @@ and the *first name*. This avoids breaking compatibility with other modules. Users should fulfill manually the separate fields for *last name* and *first name*, but in case you edit just the *name* field in some unexpected module, there is an inverse function that tries to split that automatically. It assumes -that you write the *name* in format *"Lastname Firstname"*, but it could lead to -wrong splitting (because it's just blindly trying to guess what you meant), so -you better specify it manually. +that you write the *name* in format configured (*"Lastname Firstname"*, by default), +but it could lead to wrong splitting (because it's just blindly trying to +guess what you meant), so you better specify it manually. For the same reason, after installing, previous names for contacts will stay in the *name* field, and the first time you edit any of them you will be asked to supply the *last name* and *first name* (just once per contact). -You can use *_get_inverse_name* method to get lastname and firstname from a simple string -and also *_get_computed_name* to get a name form the lastname and firstname. -These methods can be overridden to change the format specified above +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/134/8.0 For further information, please visit: * https://www.odoo.com/forum/help-1 + +Known issues / Roadmap +====================== + +Patterns for the inverse function are configurable only at system level. Maybe +this configuration could depend on partner language, country or company, +as discussed at `this OCA issue `_ + + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smashing it by providing a detailed and welcomed feedback `here +`_. + Credits ======= @@ -44,6 +83,7 @@ Contributors * Hans Henrik Gabelgaard * Jairo Llopis * Adrien Peiffer +* Antonio Espinosa Maintainer ---------- diff --git a/partner_firstname/__openerp__.py b/partner_firstname/__openerp__.py index e99c4b899..5607246dc 100644 --- a/partner_firstname/__openerp__.py +++ b/partner_firstname/__openerp__.py @@ -21,17 +21,19 @@ { 'name': 'Partner first name and last name', 'summary': "Split first name and last name for non company partners", - 'version': '8.0.2.1.0', + 'version': '8.0.2.2.0', "author": "Camptocamp, " "Grupo ESOC Ingeniería de Servicios, " "Odoo Community Association (OCA)", "license": "AGPL-3", 'maintainer': 'Camptocamp, Acsone', 'category': 'Extra Tools', - 'website': - 'http://www.camptocamp.com, http://www.acsone.eu, http://grupoesoc.es', - 'depends': ['base'], + 'website': 'http://www.camptocamp.com, ' + 'http://www.acsone.eu, ' + 'http://grupoesoc.es', + 'depends': ['base_setup'], 'data': [ + 'views/base_config_view.xml', 'views/res_partner.xml', 'views/res_user.xml', 'data/res_partner.yml', diff --git a/partner_firstname/models/__init__.py b/partner_firstname/models/__init__.py new file mode 100644 index 000000000..aa04cb891 --- /dev/null +++ b/partner_firstname/models/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# © 2015 Antiun Ingenieria S.L. - Antonio Espinosa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import base_config_settings +from . import res_partner diff --git a/partner_firstname/models/base_config_settings.py b/partner_firstname/models/base_config_settings.py new file mode 100644 index 000000000..e79b9d5dd --- /dev/null +++ b/partner_firstname/models/base_config_settings.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +# © 2015 Antiun Ingenieria S.L. - Antonio Espinosa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import logging +from openerp import models, fields, api +_logger = logging.getLogger(__name__) + + +class BaseConfigSettings(models.TransientModel): + _inherit = 'base.config.settings' + + partner_names_order = fields.Selection( + string="Partner names order", + selection="_partner_names_order_selection", + help="Order to compose partner fullname", + required=True) + partner_names_order_changed = fields.Boolean( + readonly=True, compute="_compute_names_order_changed") + + def _partner_names_order_selection(self): + return [ + ('last_first', 'Lastname Firstname'), + ('last_first_comma', 'Lastname, Firstname'), + ('first_last', 'Firstname Lastname'), + ] + + def _partner_names_order_default(self): + return self.env['res.partner']._names_order_default() + + @api.multi + def get_default_partner_names_order(self): + return { + 'partner_names_order': self.env['ir.config_parameter'].get_param( + 'partner_names_order', self._partner_names_order_default()), + } + + @api.multi + def _compute_names_order_changed(self): + current = self.env['ir.config_parameter'].get_param( + 'partner_names_order', self._partner_names_order_default()) + return self.partner_names_order != current + + @api.onchange('partner_names_order') + def _onchange_partner_names_order(self): + self.partner_names_order_changed = self._compute_names_order_changed() + + @api.multi + def set_partner_names_order(self): + self.env['ir.config_parameter'].set_param( + 'partner_names_order', self.partner_names_order) + + @api.multi + def _partners_for_recalculating(self): + return self.env['res.partner'].search([ + ('is_company', '=', False), + ('firstname', '!=', False), ('lastname', '!=', False), + ]) + + @api.multi + def action_recalculate_partners_name(self): + partners = self._partners_for_recalculating() + _logger.info("Recalculating names for %d partners.", len(partners)) + partners._compute_name() + _logger.info("%d partners updated.", len(partners)) + return True diff --git a/partner_firstname/exceptions.py b/partner_firstname/models/exceptions.py similarity index 100% rename from partner_firstname/exceptions.py rename to partner_firstname/models/exceptions.py diff --git a/partner_firstname/models.py b/partner_firstname/models/res_partner.py similarity index 81% rename from partner_firstname/models.py rename to partner_firstname/models/res_partner.py index fe2b5aa09..912b7d95f 100644 --- a/partner_firstname/models.py +++ b/partner_firstname/models/res_partner.py @@ -4,6 +4,7 @@ # Copyright (C) # 2014: Agile Business Group () # 2015: Grupo ESOC +# 2015: Antiun Ingenieria S.L. - Antonio Espinosa # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -88,12 +89,30 @@ class ResPartner(models.Model): return result + @api.model + def _names_order_default(self): + return 'last_first' + + @api.model + def _get_names_order(self): + """Get names order configuration from system parameters. + You can override this method to read configuration from language, + country, company or other""" + return self.env['ir.config_parameter'].get_param( + 'partner_names_order', self._names_order_default()) + @api.model def _get_computed_name(self, lastname, firstname): """Compute the 'name' field according to splitted data. You can override this method to change the order of lastname and firstname the computed name""" - return u" ".join((p for p in (lastname, firstname) if p)) + order = self._get_names_order() + if order == 'last_first_comma': + return u", ".join((p for p in (lastname, firstname) if p)) + elif order == 'first_last': + return u" ".join((p for p in (firstname, lastname) if p)) + else: + return u" ".join((p for p in (lastname, firstname) if p)) @api.one @api.depends("firstname", "lastname") @@ -120,12 +139,17 @@ class ResPartner(models.Model): self._inverse_name() @api.model - def _get_whitespace_cleaned_name(self, name): + def _get_whitespace_cleaned_name(self, name, comma=False): """Remove redundant whitespace from :param:`name`. Removes leading, trailing and duplicated whitespace. """ - return u" ".join(name.split(None)) if name else name + if name: + name = u" ".join(name.split(None)) + if comma: + name = name.replace(" ,", ",") + name = name.replace(", ", ",") + return name @api.model def _get_inverse_name(self, name, is_company=False): @@ -146,9 +170,19 @@ class ResPartner(models.Model): parts = [name or False, False] # Guess name splitting else: - parts = name.strip().split(" ", 1) - while len(parts) < 2: - parts.append(False) + order = self._get_names_order() + # Remove redundant spaces + name = self._get_whitespace_cleaned_name( + name, comma=(order == 'last_first_comma')) + parts = name.split("," if order == 'last_first_comma' else " ", 1) + if len(parts) > 1: + if order == 'first_last': + parts = [u" ".join(parts[1:]), parts[0]] + else: + parts = [parts[0], u" ".join(parts[1:])] + else: + while len(parts) < 2: + parts.append(False) return {"lastname": parts[0], "firstname": parts[1]} @api.one diff --git a/partner_firstname/tests/__init__.py b/partner_firstname/tests/__init__.py index b1ca17cbc..6ad0d4ec1 100644 --- a/partner_firstname/tests/__init__.py +++ b/partner_firstname/tests/__init__.py @@ -35,5 +35,6 @@ from . import ( test_defaults, test_empty, test_name, - test_onchange + test_onchange, + test_order, ) diff --git a/partner_firstname/tests/base.py b/partner_firstname/tests/base.py index 2ca475620..d9f6f71e7 100644 --- a/partner_firstname/tests/base.py +++ b/partner_firstname/tests/base.py @@ -26,7 +26,7 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. from openerp.tests.common import TransactionCase -from .. import exceptions as ex +from ..models import exceptions as ex class MailInstalled(): diff --git a/partner_firstname/tests/test_create.py b/partner_firstname/tests/test_create.py index 8f3e06a7f..551939f21 100644 --- a/partner_firstname/tests/test_create.py +++ b/partner_firstname/tests/test_create.py @@ -29,7 +29,6 @@ class PersonCase(TransactionCase): 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], diff --git a/partner_firstname/tests/test_empty.py b/partner_firstname/tests/test_empty.py index 168d987a5..ae0d25919 100644 --- a/partner_firstname/tests/test_empty.py +++ b/partner_firstname/tests/test_empty.py @@ -22,7 +22,7 @@ To have more accurate results, remove the ``mail`` module before testing. from openerp.tests.common import TransactionCase from .base import MailInstalled -from .. import exceptions as ex +from ..models import exceptions as ex class CompanyCase(TransactionCase): diff --git a/partner_firstname/tests/test_order.py b/partner_firstname/tests/test_order.py new file mode 100644 index 000000000..afc978b05 --- /dev/null +++ b/partner_firstname/tests/test_order.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# © 2015 Antiun Ingenieria S.L. - Antonio Espinosa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openerp.tests.common import TransactionCase + + +class PartnerNamesOrder(TransactionCase): + def order_set(self, order): + return self.env['ir.config_parameter'].set_param( + 'partner_names_order', order) + + def test_get_computed_name(self): + lastname = u"García Lorca" + firstname = u"Federico" + cases = ( + ('last_first', u"García Lorca Federico"), + ('last_first_comma', u"García Lorca, Federico"), + ('first_last', u"Federico García Lorca"), + ) + + for order, name in cases: + self.order_set(order) + result = self.env['res.partner']._get_computed_name( + lastname, firstname) + self.assertEqual(result, name) + + def test_get_inverse_name(self): + lastname = u"Flanker" + firstname = u"Petër" + cases = ( + ('last_first', u"Flanker Petër"), + ('last_first_comma', u"Flanker, Petër"), + ('first_last', u"Petër Flanker"), + ) + for order, name in cases: + self.order_set(order) + result = self.env['res.partner']._get_inverse_name(name) + self.assertEqual(result['lastname'], lastname) + self.assertEqual(result['firstname'], firstname) diff --git a/partner_firstname/views/base_config_view.xml b/partner_firstname/views/base_config_view.xml new file mode 100644 index 000000000..f78b0a10f --- /dev/null +++ b/partner_firstname/views/base_config_view.xml @@ -0,0 +1,34 @@ + + + + + + + Add partner_names_order config parameter + base.config.settings + + + + + + + + + + + + diff --git a/partner_second_lastname/README.rst b/partner_second_lastname/README.rst index a7e59b726..11c1229f7 100644 --- a/partner_second_lastname/README.rst +++ b/partner_second_lastname/README.rst @@ -1,6 +1,8 @@ .. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 +======================= Partner second lastname ======================= @@ -12,6 +14,26 @@ In some countries, it's important to have a second last name for contacts. Contact partners will need to fulfill at least one of the name fields (*First name*, *First last name* or *Second last name*). +Configuration +============= + +You can configure some common name patterns for the inverse function +in Settings > Configuration > General settings: + +* Lastname SecondLastname Firstname: For example 'Anderson Lavarge Robert' +* Lastname SecondLastname, Firstname: For example 'Anderson Lavarge, Robert' +* Firstname Lastname SecondLastname: For example 'Robert Anderson Lavarge' + +After applying the changes, you can recalculate all partners name clicking +"Recalculate names" button. Note: This process could take so much time depending +how many partners there are in database. + +You can use *_get_inverse_name* method to get firstname, lastname and +second lastname from a simple string and also *_get_computed_name* to get a +name form the firstname, lastname and second lastname. +These methods can be overridden to change the format specified above. + + Usage ===== @@ -24,11 +46,7 @@ To use this module, you need to: If you directly enter the full name instead of entering the other fields separately (maybe from other form), this module will try to guess the best match for your input and split it between firstname, lastname and second -lastname. - -If the name you enter is in the form *Firstname Lastname1 Lastname2*, it will -be split as such. If you use a comma, it will understand it as *Lastname1 -Lastname2, Firstname*. +lastname using an inverse function. If you can, always enter it manually please. Automatic guessing could fail for you easily in some corner cases. @@ -37,6 +55,15 @@ you easily in some corner cases. :alt: Try me on Runbot :target: https://runbot.odoo-community.org/runbot/134/8.0 + +Known issues / Roadmap +====================== + +Patterns for the inverse function are configurable only at system level. Maybe +this configuration could depend on partner language, country or company, +as discussed at `this OCA issue `_ + + Bug Tracker =========== @@ -55,6 +82,8 @@ Contributors * `Grupo ESOC `_: * `Jairo Llopis `_. +* `Antiun Ingeniería S.L. `_: + * `Antonio Espinosa `_. Maintainer ---------- diff --git a/partner_second_lastname/__openerp__.py b/partner_second_lastname/__openerp__.py index 30a8ff1fc..4acde00b1 100644 --- a/partner_second_lastname/__openerp__.py +++ b/partner_second_lastname/__openerp__.py @@ -5,7 +5,7 @@ { "name": "Partner second last name", "summary": "Have split first and second lastnames", - "version": "8.0.4.0.0", + "version": "8.0.4.1.0", "license": "AGPL-3", "website": "https://grupoesoc.es", "author": "Grupo ESOC Ingeniería de Servicios, " @@ -20,4 +20,5 @@ "views/res_user.xml", ], "installable": True, + 'images': [], } diff --git a/partner_second_lastname/models/__init__.py b/partner_second_lastname/models/__init__.py new file mode 100644 index 000000000..aa04cb891 --- /dev/null +++ b/partner_second_lastname/models/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# © 2015 Antiun Ingenieria S.L. - Antonio Espinosa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import base_config_settings +from . import res_partner diff --git a/partner_second_lastname/models/base_config_settings.py b/partner_second_lastname/models/base_config_settings.py new file mode 100644 index 000000000..f11d8ca57 --- /dev/null +++ b/partner_second_lastname/models/base_config_settings.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# © 2015 Antiun Ingenieria S.L. - Antonio Espinosa +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openerp import models, api + + +class BaseConfigSettings(models.TransientModel): + _inherit = 'base.config.settings' + + def _partner_names_order_selection(self): + options = super( + BaseConfigSettings, self)._partner_names_order_selection() + new_labels = { + 'last_first': 'Lastname SecondLastname Firstname', + 'last_first_comma': 'Lastname SecondLastname, Firstname', + 'first_last': 'Firstname Lastname SecondLastname', + } + return [(k, new_labels[k]) if k in new_labels else (k, v) + for k, v in options] + + @api.multi + def _partners_for_recalculating(self): + return self.env['res.partner'].search([ + ('is_company', '=', False), + '|', '&', ('firstname', '!=', False), ('lastname', '!=', False), + '|', '&', ('firstname', '!=', False), ('lastname2', '!=', False), + '&', ('lastname', '!=', False), ('lastname2', '!=', False), + ]) diff --git a/partner_second_lastname/models.py b/partner_second_lastname/models/res_partner.py similarity index 58% rename from partner_second_lastname/models.py rename to partner_second_lastname/models/res_partner.py index cfec13f89..0628dcdb9 100644 --- a/partner_second_lastname/models.py +++ b/partner_second_lastname/models/res_partner.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # © 2015 Grupo ESOC Ingeniería de Servicios, S.L.U. +# © 2015 Antiun Ingenieria S.L. - Antonio Espinosa from openerp import api, fields, models -from openerp.addons.partner_firstname import exceptions +from openerp.addons.partner_firstname.models import exceptions class ResPartner(models.Model): @@ -20,17 +21,24 @@ class ResPartner(models.Model): We have 2 lastnames, so lastnames and firstname will be separated by a comma. """ + order = self._get_names_order() names = list() - - if lastname: - names.append(lastname) - if lastname2: - names.append(lastname2) - if names and firstname: - names[-1] = names[-1] + "," - if firstname: - names.append(firstname) - + if order == 'first_last': + if firstname: + names.append(firstname) + if lastname: + names.append(lastname) + if lastname2: + names.append(lastname2) + else: + if lastname: + names.append(lastname) + if lastname2: + names.append(lastname2) + if names and firstname and order == 'last_first_comma': + names[-1] = names[-1] + "," + if firstname: + names.append(firstname) return u" ".join(names) @api.one @@ -61,24 +69,31 @@ class ResPartner(models.Model): - Otherwise, make a guess. """ # Company name goes to the lastname - if is_company or not name: - parts = [False, name or False, False] - - # The comma separates the firstname - elif "," in name: - lastnames, firstname = name.split(",", 1) - parts = [firstname.strip()] + lastnames.split(" ", 1) - - # Without comma, the user wrote the firstname first - else: - parts = name.split(" ", 2) - - while len(parts) < 3: - parts.append(False) - - return {"firstname": parts[0], - "lastname": parts[1], - "lastname2": parts[2]} + result = { + 'firstname': False, + 'lastname': name or False, + 'lastname2': False, + } + if not is_company and name: + order = self._get_names_order() + result = super(ResPartner, self)._get_inverse_name( + name, is_company) + parts = [] + if order == 'last_first': + if result['firstname']: + parts = result['firstname'].split(" ", 1) + while len(parts) < 2: + parts.append(False) + result['lastname2'] = parts[0] + result['firstname'] = parts[1] + else: + if result['lastname']: + parts = result['lastname'].split(" ", 1) + while len(parts) < 2: + parts.append(False) + result['lastname'] = parts[0] + result['lastname2'] = parts[1] + return result @api.one @api.constrains("firstname", "lastname", "lastname2") diff --git a/partner_second_lastname/tests/test_name.py b/partner_second_lastname/tests/test_name.py index ba97e467d..92c963668 100644 --- a/partner_second_lastname/tests/test_name.py +++ b/partner_second_lastname/tests/test_name.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # © 2015 Grupo ESOC Ingeniería de Servicios, S.L.U. +# © 2015 Antiun Ingenieria S.L. - Antonio Espinosa from openerp.tests.common import TransactionCase from openerp.addons.partner_firstname.tests.base import MailInstalled @@ -8,6 +9,11 @@ from openerp.addons.partner_firstname.tests.base import MailInstalled class CompanyCase(TransactionCase): """Test ``res.partner`` when it is a company.""" + def setUp(self): + super(CompanyCase, self).setUp() + self.env['ir.config_parameter'].set_param( + 'partner_names_order', 'first_last') + def tearDown(self): try: new = self.env["res.partner"].create({ @@ -75,6 +81,8 @@ class PersonCase(TransactionCase): def setUp(self): super(PersonCase, self).setUp() + self.env['ir.config_parameter'].set_param( + 'partner_names_order', 'last_first_comma') self.firstname = u"Fírstname" self.lastname = u"Làstname1" @@ -113,6 +121,9 @@ class PersonCase(TransactionCase): def test_firstname_first(self): """Create a person setting his first name first.""" + self.env['ir.config_parameter'].set_param( + 'partner_names_order', 'first_last') + self.template = "%(first)s %(last1)s %(last2)s" self.params = { "is_company": False, "name": "%s %s %s" % (self.firstname, @@ -121,7 +132,7 @@ class PersonCase(TransactionCase): } def test_firstname_last(self): - """Create a persong setting his first name last.""" + """Create a person setting his first name last.""" self.params = { "is_company": False, "name": "%s %s, %s" % (self.lastname, @@ -130,25 +141,29 @@ class PersonCase(TransactionCase): } def test_firstname_only(self): - """Create a persong setting his first name only.""" - self.lastname = self.lastname2 = False - self.template = "%(first)s" + """Create a person setting his first name only.""" + self.env['ir.config_parameter'].set_param( + 'partner_names_order', 'first_last') + self.firstname = self.lastname2 = False + self.template = "%(last1)s" self.params = { "is_company": False, - "name": self.firstname, + "name": self.lastname, } def test_firstname_lastname_only(self): - """Create a persong setting his first name and last name 1 only.""" + """Create a person setting his first name and last name 1 only.""" + self.env['ir.config_parameter'].set_param( + 'partner_names_order', 'first_last') self.lastname2 = False - self.template = "%(last1)s, %(first)s" + self.template = "%(first)s %(last1)s" self.params = { "is_company": False, "name": "%s %s" % (self.firstname, self.lastname), } def test_lastname_firstname_only(self): - """Create a persong setting his last name 1 and first name only.""" + """Create a person setting his last name 1 and first name only.""" self.lastname2 = False self.template = "%(last1)s, %(first)s" self.params = { diff --git a/partner_second_lastname/tests/test_onchange.py b/partner_second_lastname/tests/test_onchange.py index fdf71a424..4d6ae367c 100644 --- a/partner_second_lastname/tests/test_onchange.py +++ b/partner_second_lastname/tests/test_onchange.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # © 2015 Grupo ESOC Ingeniería de Servicios, S.L.U. +# © 2015 Antiun Ingenieria S.L. - Antonio Espinosa + """These tests try to mimic the behavior of the UI form. The form operates in onchange mode, with its limitations. @@ -12,6 +14,11 @@ from openerp.tests.common import TransactionCase class OnChangeCase(TransactionCase): is_company = False + def setUp(self): + super(OnChangeCase, self).setUp() + self.env['ir.config_parameter'].set_param( + 'partner_names_order', 'last_first_comma') + def new_partner(self): """Create an empty partner. Ensure it is (or not) a company.""" new = self.env["res.partner"].new()