Browse Source

Merge pull request #238 from yvaucher/9.0-port-partner-firstname

[9.0] [PORT] partner_firstname
pull/231/merge
Pedro M. Baeza 9 years ago
parent
commit
3b65772832
  1. 17
      partner_firstname/README.rst
  2. 21
      partner_firstname/__init__.py
  3. 34
      partner_firstname/__openerp__.py
  4. 21
      partner_firstname/exceptions.py
  5. 5
      partner_firstname/models/__init__.py
  6. 107
      partner_firstname/models/res_partner.py
  7. 30
      partner_firstname/models/res_user.py
  8. 39
      partner_firstname/tests/__init__.py
  9. 30
      partner_firstname/tests/base.py
  10. 82
      partner_firstname/tests/test_create.py
  11. 65
      partner_firstname/tests/test_defaults.py
  12. 38
      partner_firstname/tests/test_delete.py
  13. 48
      partner_firstname/tests/test_empty.py
  14. 3
      partner_firstname/tests/test_onchange.py
  15. 64
      partner_firstname/views/res_partner.xml
  16. 4
      partner_firstname/views/res_user.xml

17
partner_firstname/README.rst

@ -39,11 +39,28 @@ Contributors
------------
* Nicolas Bessi <nicolas.bessi@camptocamp.com>
* Yannick Vaucher <yannick.vaucher@camptocamp.com>
* Vincent Renaville <vincent.renaville@camptocamp.com>
* Guewen Baconnier <guewen.baconnier@camptocamp.com>
* Holger Brunn <hbrunn@terp.nl>
* Jonathan Nemry <jonathan.nemry@acsone.eu>
* Olivier Laurent <olivier.laurent@acsone.eu>
* Sandy Carter <sandy.carter@savoirfairelinux.com>
* Alexis de Lattre <alexis.delattre@akretion.fr>
* Lorenzo Battistini <lorenzo.battistini@agilebg.com>
* Hans Henrik Gabelgaard <hhg@gabelgaard.org>
* Jairo Llopis <j.llopis@grupoesoc.es>
* Adrien Peiffer <adrien.peiffer@acsone.eu>
* Ronald Portier <ronald@therp.nl>
* Sylvain Van Hoof
* Pedro Baeza <pedro.baeza@serviciosbaeza.com>
Translations
------------
* Danish: Hans Henrik Gabelgaard
* Italian: Leonardo Donelli
* Spanish: Antonio Espinosa
Maintainer
----------

21
partner_firstname/__init__.py

@ -1,21 +1,4 @@
# -*- coding: utf-8 -*-
# Author: Nicolas Bessi. Copyright Camptocamp SA
# Copyright (C)
# 2014: Agile Business Group (<http://www.agilebg.com>)
# 2015: Grupo ESOC <www.grupoesoc.es>
#
# 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/>.
# © 2013 Nicolas Bessi (Camptocamp SA)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import models

34
partner_firstname/__openerp__.py

@ -1,31 +1,21 @@
# -*- coding: utf-8 -*-
# Author: Nicolas Bessi. Copyright Camptocamp SA
# Copyright (C)
# 2014: Agile Business Group (<http://www.agilebg.com>)
# 2015: Grupo ESOC <www.grupoesoc.es>
#
# 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/>.
# © 2013 Nicolas Bessi (Camptocamp SA)
# © 2014 Agile Business Group (<http://www.agilebg.com>)
# © 2015 Grupo ESOC (<http://www.grupoesoc.es>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
'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': '9.0.1.0.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',
'website':
'http://www.camptocamp.com, http://www.acsone.eu, http://grupoesoc.es',
'depends': ['base'],
'data': [
'views/res_partner.xml',
@ -35,6 +25,6 @@
'demo': [],
'test': [],
'auto_install': False,
'installable': False,
'installable': True,
'images': []
}

21
partner_firstname/exceptions.py

@ -1,21 +1,6 @@
# -*- encoding: utf-8 -*-
# Odoo, Open Source Management Solution
# Copyright (C) 2014-2015 Grupo ESOC <www.grupoesoc.es>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
# © 2014-2015 Grupo ESOC (<http://www.grupoesoc.es>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import _, exceptions

5
partner_firstname/models/__init__.py

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# © 2013 Nicolas Bessi (Camptocamp SA)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import res_partner
from . import res_user

107
partner_firstname/models.py → partner_firstname/models/res_partner.py

@ -1,26 +1,11 @@
# -*- coding: utf-8 -*-
# Author: Nicolas Bessi. Copyright Camptocamp SA
# Copyright (C)
# 2014: Agile Business Group (<http://www.agilebg.com>)
# 2015: Grupo ESOC <www.grupoesoc.es>
#
# 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/>.
# © 2013 Nicolas Bessi (Camptocamp SA)
# © 2014 Agile Business Group (<http://www.agilebg.com>)
# © 2015 Grupo ESOC (<http://www.grupoesoc.es>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging
from openerp import api, fields, models
from . import exceptions
from .. import exceptions
_logger = logging.getLogger(__name__)
@ -38,6 +23,56 @@ class ResPartner(models.Model):
required=False,
store=True)
@api.model
def create(self, vals):
"""Add inverted names at creation if unavailable."""
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(
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) or context.get("copy"):
vals[key] = value
# 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):
"""Invert name when getting default values."""
result = super(ResPartner, self).default_get(fields_list)
inverted = self._get_inverse_name(
self._get_whitespace_cleaned_name(result.get("name", "")),
result.get("is_company", False))
for field in inverted.keys():
if field in fields_list:
result[field] = inverted.get(field)
return result
@api.model
def _get_computed_name(self, lastname, firstname):
"""Compute the 'name' field according to splitted data.
@ -55,13 +90,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:
@ -71,9 +104,17 @@ 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):
"""Try to revert the effect of :meth:`._compute_name`.
"""Compute the inverted name.
- If the partner is a company, save it in the lastname.
- Otherwise, make a guess.
@ -90,21 +131,23 @@ class ResPartner(models.Model):
parts = [name or False, False]
# Guess name splitting
else:
parts = name.split(" ", 1)
parts = name.strip().split(" ", 1)
while len(parts) < 2:
parts.append(False)
return parts
return {"lastname": parts[0], "firstname": parts[1]}
@api.one
def _inverse_name(self):
"""Try to revert the effect of :meth:`._compute_name`."""
parts = self._get_inverse_name(self.name, self.is_company)
self.lastname, self.firstname = parts
self.lastname, self.firstname = parts["lastname"], parts["firstname"]
@api.one
@api.constrains("firstname", "lastname")
def _check_name(self):
"""Ensure at least one name is set."""
if not (self.firstname or self.lastname):
if ((self.type == 'contact' or self.is_company) and
not (self.firstname or self.lastname)):
raise exceptions.EmptyNamesError(self)
@api.one
@ -145,3 +188,11 @@ class ResPartner(models.Model):
# Force calculations there
records._inverse_name()
_logger.info("%d partners updated installing module.", len(records))
# Disabling SQL constraint givint a more explicit error using a Python
# contstraint
_sql_constraints = [(
'check_name',
"CHECK( 1=1 )",
'Contacts require a name.'
)]

30
partner_firstname/models/res_user.py

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# © 2013 Nicolas Bessi (Camptocamp SA)
# © 2014 Agile Business Group (<http://www.agilebg.com>)
# © 2015 Grupo ESOC (<http://www.grupoesoc.es>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging
from openerp import api, models
_logger = logging.getLogger(__name__)
class ResUser(models.Model):
_inherit = 'res.users'
@api.model
def default_get(self, fields_list):
"""Invert name when getting default values."""
result = super(ResUser, self).default_get(fields_list)
partner_model = self.env['res.partner']
inverted = partner_model._get_inverse_name(
partner_model._get_whitespace_cleaned_name(result.get("name", "")),
result.get("is_company", False))
for field in inverted.keys():
if field in fields_list:
result[field] = inverted.get(field)
return result

39
partner_firstname/tests/__init__.py

@ -1,31 +1,12 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Authors: Nemry Jonathan
# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
# All Rights Reserved
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs.
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contact a Free Software
# Service Company.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
# © 2014 Nemry Jonathan (Acsone SA/NV) (http://www.acsone.eu)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import test_empty, test_name, test_onchange
from . import (
test_create,
test_defaults,
test_delete,
test_empty,
test_name,
test_onchange
)

30
partner_firstname/tests/base.py

@ -1,29 +1,6 @@
# -*- coding: utf-8 -*-
# Authors: Nemry Jonathan
# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
# All Rights Reserved
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs.
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contact a Free Software
# Service Company.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# © 2014 Nemry Jonathan (Acsone SA/NV) (http://www.acsone.eu)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp.tests.common import TransactionCase
from .. import exceptions as ex
@ -71,7 +48,8 @@ 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."""

82
partner_firstname/tests/test_create.py

@ -0,0 +1,82 @@
# -*- 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):
super(CompanyCase, self).setUp()
self.good_values.update(lastname=self.values["name"], firstname=False)
self.values = self.good_values.copy()
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()

65
partner_firstname/tests/test_defaults.py

@ -0,0 +1,65 @@
# -*- 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.values = {
"firstname": u"Núñez",
"lastname": u"Fernán",
}
self.values["name"] = "%s %s" % (self.values["lastname"],
self.values["firstname"])
if "default_is_company" in self.context:
self.values["is_company"] = self.context["default_is_company"]
def tearDown(self):
for key, value in self.values.iteritems():
self.assertEqual(
self.defaults.get(key),
value,
"Checking key %s" % key)
return super(PersonCase, self).tearDown()
def test_default_get(self):
"""Getting default values for fields includes new fields."""
self.defaults = (self.env[self.model]
.with_context(self.context,
default_name=self.values["name"])
.default_get(self.values.keys()))
class CompanyCase(PersonCase):
"""Test ``res.partner`` when it is a company."""
context = {"default_is_company": True}
def tearDown(self):
self.values.update(lastname=self.values["name"], firstname=False)
return super(CompanyCase, self).tearDown()
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()

38
partner_firstname/tests/test_delete.py

@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
# © 2015 Grupo ESOC
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp.tests.common import TransactionCase
from .base import MailInstalled
class CompanyCase(TransactionCase):
model = "res.partner"
context = {"default_is_company": True}
def test_computing_after_unlink(self):
"""Test what happens if recomputed after unlinking.
This test might seem useless, but really this happens when module
``partner_relations`` is installed.
See https://github.com/OCA/partner-contact/issues/154.
"""
data = {"name": u"Söme name"}
record = self.env[self.model].with_context(**self.context).create(data)
record.unlink()
record.recompute()
class PersonCase(CompanyCase):
context = {"default_is_company": False}
class UserCase(CompanyCase, MailInstalled):
model = "res.users"
context = {"default_login": "user@example.com"}
def test_computing_after_unlink(self):
# Cannot create users if ``mail`` is installed
if not self.mail_installed():
super(UserCase, self).test_computing_after_unlink()

48
partner_firstname/tests/test_empty.py

@ -1,26 +1,11 @@
# -*- encoding: utf-8 -*-
# Odoo, Open Source Management Solution
# Copyright (C) 2014-2015 Grupo ESOC <www.grupoesoc.es>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
# © 2014-2015 Grupo ESOC <www.grupoesoc.es>
# © 2016 Yannick Vaucher (Camptocamp)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
"""Test situations where names are empty.
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
@ -34,8 +19,9 @@ class CompanyCase(TransactionCase):
def tearDown(self):
try:
data = {"name": self.name}
model = self.env[self.model].with_context(**self.context)
with self.assertRaises(ex.EmptyNamesError):
self.env[self.model].with_context(**self.context).create(data)
model.create(data)
finally:
super(CompanyCase, self).tearDown()
@ -50,7 +36,7 @@ class CompanyCase(TransactionCase):
class PersonCase(CompanyCase):
"""Test ``res.partner`` when it is a person."""
context = {"default_is_company": False}
context = {"default_is_company": False, "default_type": 'contact'}
class UserCase(CompanyCase, MailInstalled):
@ -66,3 +52,23 @@ class UserCase(CompanyCase, MailInstalled):
else:
# Run tests
super(UserCase, self).tearDown()
class AddressCase(TransactionCase):
"""Test ``res.partner`` when it is a address."""
def test_new_empty_invoice_address(self):
"""Create an invoice patner without name."""
self.original = self.env["res.partner"].create({
"is_company": False,
"type": 'invoice',
"lastname": "",
"firstname": ""})
def test_new_empty_shipping_address(self):
"""Create an shipping patner without name."""
self.original = self.env["res.partner"].create({
"is_company": False,
"type": 'delivery',
"lastname": "",
"firstname": ""})

3
partner_firstname/tests/test_onchange.py

@ -1,4 +1,7 @@
# -*- coding: utf-8 -*-
# © 2015 Grupo ESOC <www.grupoesoc.es>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
"""These tests try to mimic the behavior of the UI form.
The form operates in onchange mode, with its limitations.

64
partner_firstname/views/res_partner.xml

@ -15,7 +15,7 @@
}</attribute>
</xpath>
<xpath expr="//field[@name='category_id']" position="before">
<xpath expr="//h1//field[@name='name']/.." position="before">
<group attrs="{'invisible': [('is_company', '=', True)]}">
<field name="lastname" attrs=
"{'required': [('firstname', '=', False),
@ -42,47 +42,20 @@
}</attribute>
</xpath>
<xpath expr="//field[@name='category_id']" position="before">
<group attrs="{'invisible': [('is_company', '=', True)]}">
<field name="lastname" attrs=
"{'required': [('firstname', '=', False),
('is_company', '=', False)]}"/>
<field name="firstname" attrs=
"{'required': [('lastname', '=', False),
('is_company', '=', False)]}"/>
</group>
</xpath>
<!-- Modify inner contact form of child_ids -->
<xpath expr="//field[@name='child_ids']/form
//field[@name='category_id']"
position="before">
<group attrs="{'invisible': [('is_company', '=', True)]}">
<field name="lastname" attrs=
"{'required': [('firstname', '=', False),
('is_company', '=', False)]}"/>
<field name="firstname" attrs=
"{'required': [('lastname', '=', False),
('is_company', '=', False)]}"/>
</group>
</xpath>
<xpath expr="//field[@name='child_ids']/form
//field[@name='category_id']"
position="attributes">
<attribute name="style"/>
</xpath>
<xpath expr="//field[@name='child_ids']/form//label[@for='name']"
position="before">
<xpath expr="//h1//field[@name='name']/.." position="after">
<div class="oe_edit_only">
<field name="is_company"
on_change="onchange_type(is_company)"/>
<label for="is_company"
string="Is a Company?"/>
<group attrs="{'invisible': [('is_company', '=', True)]}">
<field name="lastname" attrs=
"{'required': [('firstname', '=', False),
('is_company', '=', False)]}"/>
<field name="firstname" attrs=
"{'required': [('lastname', '=', False),
('is_company', '=', False)]}"/>
</group>
</div>
</xpath>
<!-- Modify inner contact form of child_ids -->
<xpath expr="//field[@name='child_ids']/form//field[@name='name']"
position="attributes">
<attribute name="attrs">{
@ -90,6 +63,21 @@
'required': [('is_company', '=', True)]
}</attribute>
</xpath>
<xpath expr="//field[@name='child_ids']/form//field[@name='name']"
position="after">
<div class="oe_edit_only" colspan="2">
<field name="is_company" invisible="True"/>
<group attrs="{'invisible': [('is_company', '=', True)]}">
<field name="lastname" attrs=
"{'required': [('firstname', '=', False),
('is_company', '=', False)]}"/>
<field name="firstname" attrs=
"{'required': [('lastname', '=', False),
('is_company', '=', False)]}"/>
</group>
</div>
</xpath>
</data>
</field>
</record>

4
partner_firstname/views/res_user.xml

@ -2,10 +2,6 @@
<openerp>
<data>
<!-- Required before modifying `base.vew_users_form`.
https://github.com/odoo/odoo/issues/6324#issuecomment-93534579 -->
<function model="res.groups" name="update_user_groups_view" />
<record id="view_users_form" model="ir.ui.view">
<field name="name">Add firstname and surnames</field>
<field name="model">res.users</field>

Loading…
Cancel
Save