Add partner_firstname module

This commit is contained in:
Olivier Sarrat 2018-04-18 17:23:07 +02:00
parent 9fef05c3eb
commit 700e3bb92f
33 changed files with 2225 additions and 0 deletions

View File

@ -0,0 +1,101 @@
.. 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
=====
The field *name* becomes a stored function field concatenating the *last name*
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 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).
.. 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 <https://github.com/OCA/partner-contact/issues/210>`_
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/partner-contact/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
<https://github.com/OCA/partner-contact/issues/new?body=module:%20partner_firstname%0Aversion:%208.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Credits
=======
Contributors
------------
* Nicolas Bessi <nicolas.bessi@camptocamp.com>
* Jonathan Nemry <jonathan.nemry@acsone.eu>
* Olivier Laurent <olivier.laurent@acsone.eu>
* Hans Henrik Gabelgaard <hhg@gabelgaard.org>
* Jairo Llopis <j.llopis@grupoesoc.es>
* Adrien Peiffer <adrien.peiffer@acsone.eu>
* Antonio Espinosa <antonioea@antiun.com>
Maintainer
----------
.. image:: http://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: http://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
To contribute to this module, please visit http://odoo-community.org.

View File

@ -0,0 +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/>.
from . import models

View File

@ -0,0 +1,45 @@
# -*- 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/>.
{
'name': 'Partner first name and last name',
'summary': "Split first name and last name for non company partners",
'version': '8.0.2.2.1',
"author": "Camptocamp, "
"Grupo ESOC Ingeniería de Servicios, "
"ACSONE SA/NV, "
"Odoo Community Association (OCA)",
"license": "AGPL-3",
'maintainer': 'Camptocamp, Acsone',
'category': 'Extra Tools',
'website': 'https://odoo-community.org',
'depends': ['base_setup'],
'data': [
'views/base_config_view.xml',
'views/res_partner.xml',
'views/res_user.xml',
'data/res_partner.yml',
],
'demo': [],
'test': [],
'auto_install': False,
'installable': True,
'images': []
}

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Copyright (C)
# 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/>.
- !function {model: res.partner, name: _install_partner_firstname}

View File

@ -0,0 +1,65 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * partner_firstname
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: partner-contact (8.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-08-28 15:54+0000\n"
"PO-Revision-Date: 2015-07-03 11:31+0000\n"
"Last-Translator: OCA Transbot <transbot@odoo-community.org>\n"
"Language-Team: Danish (http://www.transifex.com/oca/OCA-partner-contact-8-0/language/da/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: da\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: partner_firstname
#: code:addons/partner_firstname/exceptions.py:26
#, python-format
msgid "Error(s) with partner %d's name."
msgstr ""
#. module: partner_firstname
#: field:res.partner,firstname:0
msgid "First name"
msgstr ""
#. module: partner_firstname
#: view:res.partner:partner_firstname.view_partner_form_firstname
msgid "Is a Company?"
msgstr "Er et firma?"
#. module: partner_firstname
#: field:res.partner,lastname:0
msgid "Last name"
msgstr ""
#. module: partner_firstname
#: code:addons/partner_firstname/exceptions.py:23
#, python-format
msgid "No name is set."
msgstr ""
#. module: partner_firstname
#: model:ir.model,name:partner_firstname.model_res_partner
msgid "Partner"
msgstr "Partner"
#. module: partner_firstname
#: view:res.users:partner_firstname.view_users_form
msgid "True"
msgstr ""
#. module: partner_firstname
#: view:res.partner:partner_firstname.view_partner_form_firstname
#: view:res.partner:partner_firstname.view_partner_simple_form_firstname
msgid ""
"{\n"
" 'readonly': [('is_company', '=', False)],\n"
" 'required': [('is_company', '=', True)]\n"
" }"
msgstr ""

View File

@ -0,0 +1,93 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * partner_firstname
#
# Translators:
# Rudolf Schnapka <rs@techno-flex.de>, 2015,2017
msgid ""
msgstr ""
"Project-Id-Version: partner-contact (8.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-02-21 16:57+0000\n"
"PO-Revision-Date: 2017-02-24 22:42+0000\n"
"Last-Translator: Rudolf Schnapka <rs@techno-flex.de>\n"
"Language-Team: German (http://www.transifex.com/oca/OCA-partner-contact-8-0/language/de/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: de\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: partner_firstname
#: code:addons/partner_firstname/models/exceptions.py:25
#, python-format
msgid "Error(s) with partner %d's name."
msgstr "Fehler beim Namen des Partner %d."
#. module: partner_firstname
#: field:res.partner,firstname:0
msgid "First name"
msgstr "Vorname"
#. module: partner_firstname
#: view:res.partner:partner_firstname.view_partner_form_firstname
msgid "Is a Company?"
msgstr "Ist ein Unternehmen?"
#. module: partner_firstname
#: field:res.partner,lastname:0
msgid "Last name"
msgstr "Nachname"
#. module: partner_firstname
#: code:addons/partner_firstname/models/exceptions.py:22
#, python-format
msgid "No name is set."
msgstr "Kein Name festgelegt."
#. module: partner_firstname
#: help:base.config.settings,partner_names_order:0
msgid "Order to compose partner fullname"
msgstr "Reihenfolge den vollständigen Namen zusammenzustellen"
#. module: partner_firstname
#: model:ir.model,name:partner_firstname.model_res_partner
msgid "Partner"
msgstr "Partner"
#. module: partner_firstname
#: field:base.config.settings,partner_names_order:0
msgid "Partner names order"
msgstr "Namen-Reihenfolge"
#. module: partner_firstname
#: field:base.config.settings,partner_names_order_changed:0
msgid "Partner names order changed"
msgstr "Partnernamen-Reihenfolgeänderung"
#. module: partner_firstname
#: view:base.config.settings:partner_firstname.view_general_configuration
msgid "Recalculate names"
msgstr "Namen neu ermitteln"
#. module: partner_firstname
#: view:base.config.settings:partner_firstname.view_general_configuration
msgid ""
"Recalculate names for all partners. This process could take so much time if "
"there are more than 10,000 active partners"
msgstr "Namen aller Partner neu ermitteln. Dieser Vorgang kann, je nach Menge an Partnern, ziemlich lange dauern"
#. module: partner_firstname
#: view:res.users:partner_firstname.view_users_form
msgid "True"
msgstr "Wahr"
#. module: partner_firstname
#: view:res.partner:partner_firstname.view_partner_form_firstname
#: view:res.partner:partner_firstname.view_partner_simple_form_firstname
msgid ""
"{\n"
" 'readonly': [('is_company', '=', False)],\n"
" 'required': [('is_company', '=', True)]\n"
" }"
msgstr "{\n 'readonly': [('is_company', '=', False)],\n 'required': [('is_company', '=', True)]\n }"

View File

@ -0,0 +1,93 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * partner_firstname
#
# Translators:
# Francisco Palm <francisco.palm@gmail.com>, 2015
msgid ""
msgstr ""
"Project-Id-Version: partner-contact (8.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-07-03 00:45+0000\n"
"PO-Revision-Date: 2016-07-02 21:38+0000\n"
"Last-Translator: OCA Transbot <transbot@odoo-community.org>\n"
"Language-Team: Spanish (http://www.transifex.com/oca/OCA-partner-contact-8-0/language/es/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: es\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: partner_firstname
#: code:addons/partner_firstname/models/exceptions.py:25
#, python-format
msgid "Error(s) with partner %d's name."
msgstr "Errores con el nombre de la empresa %d."
#. module: partner_firstname
#: field:res.partner,firstname:0
msgid "First name"
msgstr "Nombre"
#. module: partner_firstname
#: view:res.partner:partner_firstname.view_partner_form_firstname
msgid "Is a Company?"
msgstr "¿Es una empresa?"
#. module: partner_firstname
#: field:res.partner,lastname:0
msgid "Last name"
msgstr "Apellido"
#. module: partner_firstname
#: code:addons/partner_firstname/models/exceptions.py:22
#, python-format
msgid "No name is set."
msgstr "No se ha establecido ningún nombre."
#. module: partner_firstname
#: help:base.config.settings,partner_names_order:0
msgid "Order to compose partner fullname"
msgstr ""
#. module: partner_firstname
#: model:ir.model,name:partner_firstname.model_res_partner
msgid "Partner"
msgstr "Empresa"
#. module: partner_firstname
#: field:base.config.settings,partner_names_order:0
msgid "Partner names order"
msgstr ""
#. module: partner_firstname
#: field:base.config.settings,partner_names_order_changed:0
msgid "Partner names order changed"
msgstr ""
#. module: partner_firstname
#: view:base.config.settings:partner_firstname.view_general_configuration
msgid "Recalculate names"
msgstr ""
#. module: partner_firstname
#: view:base.config.settings:partner_firstname.view_general_configuration
msgid ""
"Recalculate names for all partners. This process could take so much time if "
"there are more than 10,000 active partners"
msgstr ""
#. module: partner_firstname
#: view:res.users:partner_firstname.view_users_form
msgid "True"
msgstr "True"
#. module: partner_firstname
#: view:res.partner:partner_firstname.view_partner_form_firstname
#: view:res.partner:partner_firstname.view_partner_simple_form_firstname
msgid ""
"{\n"
" 'readonly': [('is_company', '=', False)],\n"
" 'required': [('is_company', '=', True)]\n"
" }"
msgstr "{\n 'readonly': [('is_company', '=', False)],\n 'required': [('is_company', '=', True)]\n}"

View File

@ -0,0 +1,93 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * partner_firstname
#
# Translators:
# Esther Martín Menéndez <esthermartin001@gmail.com>, 2017
msgid ""
msgstr ""
"Project-Id-Version: partner-contact (8.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-02-21 16:57+0000\n"
"PO-Revision-Date: 2017-02-23 15:55+0000\n"
"Last-Translator: Esther Martín Menéndez <esthermartin001@gmail.com>\n"
"Language-Team: Basque (http://www.transifex.com/oca/OCA-partner-contact-8-0/language/eu/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: eu\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: partner_firstname
#: code:addons/partner_firstname/models/exceptions.py:25
#, python-format
msgid "Error(s) with partner %d's name."
msgstr ""
#. module: partner_firstname
#: field:res.partner,firstname:0
msgid "First name"
msgstr ""
#. module: partner_firstname
#: view:res.partner:partner_firstname.view_partner_form_firstname
msgid "Is a Company?"
msgstr ""
#. module: partner_firstname
#: field:res.partner,lastname:0
msgid "Last name"
msgstr ""
#. module: partner_firstname
#: code:addons/partner_firstname/models/exceptions.py:22
#, python-format
msgid "No name is set."
msgstr ""
#. module: partner_firstname
#: help:base.config.settings,partner_names_order:0
msgid "Order to compose partner fullname"
msgstr ""
#. module: partner_firstname
#: model:ir.model,name:partner_firstname.model_res_partner
msgid "Partner"
msgstr "Kidea"
#. module: partner_firstname
#: field:base.config.settings,partner_names_order:0
msgid "Partner names order"
msgstr ""
#. module: partner_firstname
#: field:base.config.settings,partner_names_order_changed:0
msgid "Partner names order changed"
msgstr ""
#. module: partner_firstname
#: view:base.config.settings:partner_firstname.view_general_configuration
msgid "Recalculate names"
msgstr ""
#. module: partner_firstname
#: view:base.config.settings:partner_firstname.view_general_configuration
msgid ""
"Recalculate names for all partners. This process could take so much time if "
"there are more than 10,000 active partners"
msgstr ""
#. module: partner_firstname
#: view:res.users:partner_firstname.view_users_form
msgid "True"
msgstr ""
#. module: partner_firstname
#: view:res.partner:partner_firstname.view_partner_form_firstname
#: view:res.partner:partner_firstname.view_partner_simple_form_firstname
msgid ""
"{\n"
" 'readonly': [('is_company', '=', False)],\n"
" 'required': [('is_company', '=', True)]\n"
" }"
msgstr ""

View File

@ -0,0 +1,65 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * partner_firstname
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: partner-contact (8.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-01-19 01:46+0000\n"
"PO-Revision-Date: 2015-05-21 21:56+0000\n"
"Last-Translator: <>\n"
"Language-Team: Finnish (http://www.transifex.com/oca/OCA-partner-contact-8-0/language/fi/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: fi\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: partner_firstname
#: code:addons/partner_firstname/exceptions.py:25
#, python-format
msgid "Error(s) with partner %d's name."
msgstr ""
#. module: partner_firstname
#: field:res.partner,firstname:0
msgid "First name"
msgstr ""
#. module: partner_firstname
#: view:res.partner:partner_firstname.view_partner_form_firstname
msgid "Is a Company?"
msgstr ""
#. module: partner_firstname
#: field:res.partner,lastname:0
msgid "Last name"
msgstr ""
#. module: partner_firstname
#: code:addons/partner_firstname/exceptions.py:22
#, python-format
msgid "No name is set."
msgstr ""
#. module: partner_firstname
#: model:ir.model,name:partner_firstname.model_res_partner
msgid "Partner"
msgstr "Kumppani"
#. module: partner_firstname
#: view:res.users:partner_firstname.view_users_form
msgid "True"
msgstr ""
#. module: partner_firstname
#: view:res.partner:partner_firstname.view_partner_form_firstname
#: view:res.partner:partner_firstname.view_partner_simple_form_firstname
msgid ""
"{\n"
" 'readonly': [('is_company', '=', False)],\n"
" 'required': [('is_company', '=', True)]\n"
" }"
msgstr ""

View File

@ -0,0 +1,93 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * partner_firstname
#
# Translators:
# Adrien Peiffer <adrien.peiffer@acsone.eu>, 2015
msgid ""
msgstr ""
"Project-Id-Version: partner-contact (8.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-07-03 00:45+0000\n"
"PO-Revision-Date: 2016-07-02 21:38+0000\n"
"Last-Translator: OCA Transbot <transbot@odoo-community.org>\n"
"Language-Team: French (http://www.transifex.com/oca/OCA-partner-contact-8-0/language/fr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. module: partner_firstname
#: code:addons/partner_firstname/models/exceptions.py:25
#, python-format
msgid "Error(s) with partner %d's name."
msgstr "Erreur(s) avec le nom du partenaire %d"
#. module: partner_firstname
#: field:res.partner,firstname:0
msgid "First name"
msgstr "Prénom"
#. module: partner_firstname
#: view:res.partner:partner_firstname.view_partner_form_firstname
msgid "Is a Company?"
msgstr "Est une société?"
#. module: partner_firstname
#: field:res.partner,lastname:0
msgid "Last name"
msgstr "Nom"
#. module: partner_firstname
#: code:addons/partner_firstname/models/exceptions.py:22
#, python-format
msgid "No name is set."
msgstr "Aucun nom n'est enregistré"
#. module: partner_firstname
#: help:base.config.settings,partner_names_order:0
msgid "Order to compose partner fullname"
msgstr ""
#. module: partner_firstname
#: model:ir.model,name:partner_firstname.model_res_partner
msgid "Partner"
msgstr "Partenaire"
#. module: partner_firstname
#: field:base.config.settings,partner_names_order:0
msgid "Partner names order"
msgstr ""
#. module: partner_firstname
#: field:base.config.settings,partner_names_order_changed:0
msgid "Partner names order changed"
msgstr ""
#. module: partner_firstname
#: view:base.config.settings:partner_firstname.view_general_configuration
msgid "Recalculate names"
msgstr ""
#. module: partner_firstname
#: view:base.config.settings:partner_firstname.view_general_configuration
msgid ""
"Recalculate names for all partners. This process could take so much time if "
"there are more than 10,000 active partners"
msgstr ""
#. module: partner_firstname
#: view:res.users:partner_firstname.view_users_form
msgid "True"
msgstr ""
#. module: partner_firstname
#: view:res.partner:partner_firstname.view_partner_form_firstname
#: view:res.partner:partner_firstname.view_partner_simple_form_firstname
msgid ""
"{\n"
" 'readonly': [('is_company', '=', False)],\n"
" 'required': [('is_company', '=', True)]\n"
" }"
msgstr ""

View File

@ -0,0 +1,94 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * partner_firstname
#
# Translators:
# Paolo Valier, 2016
# Paolo Valier, 2016
msgid ""
msgstr ""
"Project-Id-Version: partner-contact (8.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-07-03 00:45+0000\n"
"PO-Revision-Date: 2016-07-02 21:38+0000\n"
"Last-Translator: OCA Transbot <transbot@odoo-community.org>\n"
"Language-Team: Italian (http://www.transifex.com/oca/OCA-partner-contact-8-0/language/it/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: it\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: partner_firstname
#: code:addons/partner_firstname/models/exceptions.py:25
#, python-format
msgid "Error(s) with partner %d's name."
msgstr "Errore(i) con il nome del partner %d."
#. module: partner_firstname
#: field:res.partner,firstname:0
msgid "First name"
msgstr "Nome"
#. module: partner_firstname
#: view:res.partner:partner_firstname.view_partner_form_firstname
msgid "Is a Company?"
msgstr "È un'azienda?"
#. module: partner_firstname
#: field:res.partner,lastname:0
msgid "Last name"
msgstr "Cognome"
#. module: partner_firstname
#: code:addons/partner_firstname/models/exceptions.py:22
#, python-format
msgid "No name is set."
msgstr "Nessun nome impostato."
#. module: partner_firstname
#: help:base.config.settings,partner_names_order:0
msgid "Order to compose partner fullname"
msgstr ""
#. module: partner_firstname
#: model:ir.model,name:partner_firstname.model_res_partner
msgid "Partner"
msgstr "Partner"
#. module: partner_firstname
#: field:base.config.settings,partner_names_order:0
msgid "Partner names order"
msgstr ""
#. module: partner_firstname
#: field:base.config.settings,partner_names_order_changed:0
msgid "Partner names order changed"
msgstr ""
#. module: partner_firstname
#: view:base.config.settings:partner_firstname.view_general_configuration
msgid "Recalculate names"
msgstr ""
#. module: partner_firstname
#: view:base.config.settings:partner_firstname.view_general_configuration
msgid ""
"Recalculate names for all partners. This process could take so much time if "
"there are more than 10,000 active partners"
msgstr ""
#. module: partner_firstname
#: view:res.users:partner_firstname.view_users_form
msgid "True"
msgstr "Vero"
#. module: partner_firstname
#: view:res.partner:partner_firstname.view_partner_form_firstname
#: view:res.partner:partner_firstname.view_partner_simple_form_firstname
msgid ""
"{\n"
" 'readonly': [('is_company', '=', False)],\n"
" 'required': [('is_company', '=', True)]\n"
" }"
msgstr ""

View File

@ -0,0 +1,66 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * partner_firstname
#
# Translators:
# lfreeke <lfreeke@therp.nl>, 2015
msgid ""
msgstr ""
"Project-Id-Version: partner-contact (8.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-09-23 21:40+0000\n"
"PO-Revision-Date: 2015-10-01 12:22+0000\n"
"Last-Translator: lfreeke <lfreeke@therp.nl>\n"
"Language-Team: Dutch (http://www.transifex.com/oca/OCA-partner-contact-8-0/language/nl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: nl\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: partner_firstname
#: code:addons/partner_firstname/exceptions.py:26
#, python-format
msgid "Error(s) with partner %d's name."
msgstr ""
#. module: partner_firstname
#: field:res.partner,firstname:0
msgid "First name"
msgstr "Voornaam"
#. module: partner_firstname
#: view:res.partner:partner_firstname.view_partner_form_firstname
msgid "Is a Company?"
msgstr "Is een bedrijf?"
#. module: partner_firstname
#: field:res.partner,lastname:0
msgid "Last name"
msgstr "Achternaam"
#. module: partner_firstname
#: code:addons/partner_firstname/exceptions.py:23
#, python-format
msgid "No name is set."
msgstr ""
#. module: partner_firstname
#: model:ir.model,name:partner_firstname.model_res_partner
msgid "Partner"
msgstr "Relatie"
#. module: partner_firstname
#: view:res.users:partner_firstname.view_users_form
msgid "True"
msgstr ""
#. module: partner_firstname
#: view:res.partner:partner_firstname.view_partner_form_firstname
#: view:res.partner:partner_firstname.view_partner_simple_form_firstname
msgid ""
"{\n"
" 'readonly': [('is_company', '=', False)],\n"
" 'required': [('is_company', '=', True)]\n"
" }"
msgstr ""

View File

@ -0,0 +1,48 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * partner_firstname
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0-20150327\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-03-30 07:53+0000\n"
"PO-Revision-Date: 2015-03-30 07:53+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: partner_firstname
#: code:addons/partner_firstname/exceptions.py:26
#, python-format
msgid "Error(s) with partner %d's name."
msgstr ""
#. module: partner_firstname
#: field:res.partner,firstname:0
msgid "First name"
msgstr ""
#. module: partner_firstname
#: view:res.partner:partner_firstname.partner_form
msgid "Is a Company?"
msgstr ""
#. module: partner_firstname
#: field:res.partner,lastname:0
msgid "Last name"
msgstr ""
#. module: partner_firstname
#: code:addons/partner_firstname/exceptions.py:40
#, python-format
msgid "No name is set."
msgstr ""
#. module: partner_firstname
#: model:ir.model,name:partner_firstname.model_res_partner
msgid "Partner"
msgstr ""

View File

@ -0,0 +1,66 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * partner_firstname
#
# Translators:
# danimaribeiro <danimaribeiro@gmail.com>, 2015
msgid ""
msgstr ""
"Project-Id-Version: partner-contact (8.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-08-28 15:54+0000\n"
"PO-Revision-Date: 2015-07-03 11:31+0000\n"
"Last-Translator: OCA Transbot <transbot@odoo-community.org>\n"
"Language-Team: Portuguese (Brazil) (http://www.transifex.com/oca/OCA-partner-contact-8-0/language/pt_BR/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: pt_BR\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#. module: partner_firstname
#: code:addons/partner_firstname/exceptions.py:26
#, python-format
msgid "Error(s) with partner %d's name."
msgstr ""
#. module: partner_firstname
#: field:res.partner,firstname:0
msgid "First name"
msgstr ""
#. module: partner_firstname
#: view:res.partner:partner_firstname.view_partner_form_firstname
msgid "Is a Company?"
msgstr "É uma empresa?"
#. module: partner_firstname
#: field:res.partner,lastname:0
msgid "Last name"
msgstr ""
#. module: partner_firstname
#: code:addons/partner_firstname/exceptions.py:23
#, python-format
msgid "No name is set."
msgstr ""
#. module: partner_firstname
#: model:ir.model,name:partner_firstname.model_res_partner
msgid "Partner"
msgstr "Parceiro"
#. module: partner_firstname
#: view:res.users:partner_firstname.view_users_form
msgid "True"
msgstr ""
#. module: partner_firstname
#: view:res.partner:partner_firstname.view_partner_form_firstname
#: view:res.partner:partner_firstname.view_partner_simple_form_firstname
msgid ""
"{\n"
" 'readonly': [('is_company', '=', False)],\n"
" 'required': [('is_company', '=', True)]\n"
" }"
msgstr ""

View File

@ -0,0 +1,93 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * partner_firstname
#
# Translators:
# Matjaž Mozetič <m.mozetic@matmoz.si>, 2015-2016
msgid ""
msgstr ""
"Project-Id-Version: partner-contact (8.0)\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-07-03 07:56+0000\n"
"PO-Revision-Date: 2016-07-04 06:45+0000\n"
"Last-Translator: Matjaž Mozetič <m.mozetic@matmoz.si>\n"
"Language-Team: Slovenian (http://www.transifex.com/oca/OCA-partner-contact-8-0/language/sl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: sl\n"
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n"
#. module: partner_firstname
#: code:addons/partner_firstname/models/exceptions.py:25
#, python-format
msgid "Error(s) with partner %d's name."
msgstr "Napaka(e) pri nazivu partnerja %d's ."
#. module: partner_firstname
#: field:res.partner,firstname:0
msgid "First name"
msgstr "Lastno ime"
#. module: partner_firstname
#: view:res.partner:partner_firstname.view_partner_form_firstname
msgid "Is a Company?"
msgstr "Je družba?"
#. module: partner_firstname
#: field:res.partner,lastname:0
msgid "Last name"
msgstr "Priimek"
#. module: partner_firstname
#: code:addons/partner_firstname/models/exceptions.py:22
#, python-format
msgid "No name is set."
msgstr "Ime ni nastavljeno."
#. module: partner_firstname
#: help:base.config.settings,partner_names_order:0
msgid "Order to compose partner fullname"
msgstr "Vrstni red za sestavo polnega naziva partnerja"
#. module: partner_firstname
#: model:ir.model,name:partner_firstname.model_res_partner
msgid "Partner"
msgstr "Partner"
#. module: partner_firstname
#: field:base.config.settings,partner_names_order:0
msgid "Partner names order"
msgstr "Vrstni red nazivov partnerja"
#. module: partner_firstname
#: field:base.config.settings,partner_names_order_changed:0
msgid "Partner names order changed"
msgstr "Vrstni red nazivov partnerja spremenjen"
#. module: partner_firstname
#: view:base.config.settings:partner_firstname.view_general_configuration
msgid "Recalculate names"
msgstr "Preračunaj nazive"
#. module: partner_firstname
#: view:base.config.settings:partner_firstname.view_general_configuration
msgid ""
"Recalculate names for all partners. This process could take so much time if "
"there are more than 10,000 active partners"
msgstr "Preračunaj nazive za vse partnerje. Proces lahko traja zelo dolgo če je več kot 10,000 aktivnih partnerjev"
#. module: partner_firstname
#: view:res.users:partner_firstname.view_users_form
msgid "True"
msgstr "Pravilno"
#. module: partner_firstname
#: view:res.partner:partner_firstname.view_partner_form_firstname
#: view:res.partner:partner_firstname.view_partner_simple_form_firstname
msgid ""
"{\n"
" 'readonly': [('is_company', '=', False)],\n"
" 'required': [('is_company', '=', True)]\n"
" }"
msgstr "{\n 'readonly': [('is_company', '=', False)],\n 'required': [('is_company', '=', True)]\n }"

View File

@ -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

View File

@ -0,0 +1,71 @@
# -*- 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
@api.depends('partner_names_order')
def _compute_names_order_changed(self):
current = self.env['ir.config_parameter'].get_param(
'partner_names_order', self._partner_names_order_default(),
)
for record in self:
record.partner_names_order_changed = bool(
record.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

View File

@ -0,0 +1,26 @@
# -*- coding: 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/>.
from openerp import _, exceptions
class EmptyNamesError(exceptions.ValidationError):
def __init__(self, record, value=_("No name is set.")):
self.record = record
self._value = value
self.name = _("Error(s) with partner %d's name.") % record.id
self.args = (self.name, value)

View File

@ -0,0 +1,245 @@
# -*- coding: utf-8 -*-
# Copyright 2014 Agile Business Group (<http://www.agilebg.com>)
# Copyright 2015 Grupo ESOC <www.grupoesoc.es>
# Copyright 2015 Antiun Ingenieria S.L. - Antonio Espinosa
# Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
from openerp import api, fields, models
from . import exceptions
_logger = logging.getLogger(__name__)
class ResPartner(models.Model):
"""Adds last name and first name; name becomes a stored function field."""
_inherit = 'res.partner'
firstname = fields.Char("First name")
lastname = fields.Char("Last name")
name = fields.Char(
compute="_compute_name",
inverse="_inverse_name_after_cleaning_whitespace",
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 _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
"""
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")
def _compute_name(self):
"""Write the 'name' field according to splitted data."""
self.name = self._get_computed_name(self.lastname, self.firstname)
@api.one
def _inverse_name_after_cleaning_whitespace(self):
"""Clean whitespace in :attr:`~.name` and split it.
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 = self._get_whitespace_cleaned_name(self.name)
# Clean name avoiding infinite recursion
if self.name != clean:
self.name = clean
# Save name in the real fields
else:
self._inverse_name()
@api.model
def _get_whitespace_cleaned_name(self, name, comma=False):
"""Remove redundant whitespace from :param:`name`.
Removes leading, trailing and duplicated whitespace.
"""
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):
"""Compute the inverted name.
- If the partner is a company, save it in the lastname.
- Otherwise, make a guess.
This method can be easily overriden by other submodules.
You can also override this method to change the order of name's
attributes
When this method is called, :attr:`~.name` already has unified and
trimmed whitespace.
"""
# Company name goes to the lastname
if is_company or not name:
parts = [name or False, False]
# Guess name splitting
else:
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
def _inverse_name(self):
"""Try to revert the effect of :meth:`._compute_name`."""
parts = self._get_inverse_name(self.name, self.is_company)
if parts["lastname"] != self.lastname:
self.lastname = parts["lastname"]
if parts["firstname"] != self.firstname:
self.firstname = 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):
raise exceptions.EmptyNamesError(self)
@api.one
@api.onchange("firstname", "lastname")
def _onchange_subnames(self):
"""Avoid recursion when the user changes one of these fields.
This forces to skip the :attr:`~.name` inversion when the user is
setting it in a not-inverted way.
"""
# Modify self's context without creating a new Environment.
# See https://github.com/odoo/odoo/issues/7472#issuecomment-119503916.
self.env.context = self.with_context(skip_onchange=True).env.context
@api.one
@api.onchange("name")
def _onchange_name(self):
"""Ensure :attr:`~.name` is inverted in the UI."""
if self.env.context.get("skip_onchange"):
# Do not skip next onchange
self.env.context = (
self.with_context(skip_onchange=False).env.context)
else:
self._inverse_name_after_cleaning_whitespace()
@api.model
def _install_partner_firstname(self):
"""Save names correctly in the database.
Before installing the module, field ``name`` contains all full names.
When installing it, this method parses those names and saves them
correctly into the database. This can be called later too if needed.
"""
# Find records with empty firstname and lastname
records = self.search([("firstname", "=", False),
("lastname", "=", False)])
# Force calculations there
records._inverse_name()
_logger.info("%d partners updated installing module.", len(records))
@api.multi
def write(self, vals):
name = vals.get('name')
if name and all(name == partner.name for partner in self):
vals.pop('name', None)
# If vals is empty (only write name field and with the same value)
# Avoid access checking here
# https://github.com/odoo/odoo/blob/
# 8b83119fad7ccae9f091f12b6ac89c2c31e4bac3/openerp/addons/base/res/
# res_partner.py#L569
this = self.sudo() if not vals else self
return super(ResPartner, this).write(vals)

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Authors: Nemry Jonathan
# Copyright (c) 2014 Acsone SA/NV (http://www.acsone.eu)
# 2015: Grupo ESOC <www.grupoesoc.es>
# 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.
#
##############################################################################
from . import (
test_create,
test_delete,
test_defaults,
test_empty,
test_name,
test_onchange,
test_order,
test_access_check,
)

View File

@ -0,0 +1,97 @@
# -*- 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.
from openerp.tests.common import TransactionCase
from ..models import exceptions as ex
class MailInstalled():
def mail_installed(self):
"""Check if ``mail`` module is installed.``"""
return (self.env["ir.module.module"]
.search([("name", "=", "mail")])
.state == "installed")
class BaseCase(TransactionCase, MailInstalled):
def setUp(self):
super(BaseCase, self).setUp()
self.check_fields = True
self.expect(u"Núñez", u"Fernán")
self.create_original()
def create_original(self):
self.original = self.env["res.partner"].create({
"lastname": self.lastname,
"firstname": self.firstname})
def expect(self, lastname, firstname, name=None):
"""Define what is expected in each field when ending."""
self.lastname = lastname
self.firstname = firstname
self.name = name or u"%s %s" % (lastname, firstname)
def tearDown(self):
if self.check_fields:
if not hasattr(self, "changed"):
self.changed = self.original
for field in ("name", "lastname", "firstname"):
self.assertEqual(
getattr(self.changed, field),
getattr(self, field),
"Test failed with wrong %s" % field)
super(BaseCase, self).tearDown()
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(copy=True, lang="en_US")
.copy())
def test_one_name(self):
"""Test what happens when only one name is given."""
name = u"Mönty"
self.expect(name, False, name)
self.original.name = name
def test_no_names(self):
"""Test that you cannot set a partner/user without names."""
self.check_fields = False
with self.assertRaises(ex.EmptyNamesError):
self.original.firstname = self.original.lastname = False
class OnChangeCase(TransactionCase):
is_company = False
def new_partner(self):
"""Create an empty partner. Ensure it is (or not) a company."""
new = self.env["res.partner"].new()
new.is_company = self.is_company
return new

View File

@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Antonio Espinosa <antonio.espinosa@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp.tests.common import TransactionCase
from openerp.exceptions import AccessError
class TestAccessCheck(TransactionCase):
def setUp(self, *args, **kwargs):
super(TestAccessCheck, self).setUp(*args, **kwargs)
self.user_a = self.env['res.users'].create({
'name': u'User A',
'login': u'user_a',
})
self.user_b = self.env['res.users'].create({
'name': u'User B',
'login': u'user_b',
})
def test_same_name_write(self):
"""Write only same name is allowed. Bug use case"""
partner = self.user_b.partner_id.sudo(user=self.user_a.id)
partner.write({'name': u'User B'})
def test_other_name_write(self):
"""Write other name is not allowed. Normal case"""
with self.assertRaises(AccessError):
partner = self.user_b.partner_id.sudo(user=self.user_a.id)
partner.write({'name': u'User C'})
def test_other_fields_write(self):
"""Write other fields is not allowed. Normal case"""
with self.assertRaises(AccessError):
partner = self.user_b.partner_id.sudo(user=self.user_a.id)
partner.write({'email': u't@e.com'})

View File

@ -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):
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()

View File

@ -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()

View File

@ -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()

View File

@ -0,0 +1,67 @@
# -*- coding: 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/>.
"""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 ..models import exceptions as ex
class CompanyCase(TransactionCase):
"""Test ``res.partner`` when it is a company."""
model = "res.partner"
context = {"default_is_company": True}
def tearDown(self):
try:
data = {"name": self.name}
with self.assertRaises(ex.EmptyNamesError):
self.env[self.model].with_context(**self.context).create(data)
finally:
super(CompanyCase, self).tearDown()
def test_name_empty_string(self):
"""Test what happens when the name is an empty string."""
self.name = ""
def test_name_false(self):
"""Test what happens when the name is ``False``."""
self.name = False
class PersonCase(CompanyCase):
"""Test ``res.partner`` when it is a person."""
context = {"default_is_company": False}
class UserCase(CompanyCase, 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(CompanyCase, self).tearDown()
else:
# Run tests
super(UserCase, self).tearDown()

View File

@ -0,0 +1,90 @@
# -*- 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.
"""Test naming logic.
To have more accurate results, remove the ``mail`` module before testing.
"""
from .base import BaseCase
class PartnerContactCase(BaseCase):
def test_update_lastname(self):
"""Change lastname."""
self.expect(u"newlästname", self.firstname)
self.original.name = self.name
def test_update_firstname(self):
"""Change firstname."""
self.expect(self.lastname, u"newfïrstname")
self.original.name = self.name
def test_whitespace_cleanup(self):
"""Check that whitespace in name gets cleared."""
self.expect(u"newlästname", u"newfïrstname")
self.original.name = " newlästname newfïrstname "
# Need this to refresh the ``name`` field
self.original.invalidate_cache()
class PartnerCompanyCase(BaseCase):
def create_original(self):
super(PartnerCompanyCase, self).create_original()
self.original.is_company = True
def test_copy(self):
"""Copy the partner and compare the result."""
super(PartnerCompanyCase, self).test_copy()
self.expect(self.name, False, self.name)
def test_company_inverse(self):
"""Test the inverse method in a company record."""
name = u"Thïs is a Companŷ"
self.expect(name, False, name)
self.original.name = name
class UserCase(PartnerContactCase):
def create_original(self):
name = u"%s %s" % (self.lastname, self.firstname)
# Cannot create users if ``mail`` is installed
if self.mail_installed():
self.original = self.env.ref("base.user_demo")
self.original.name = name
else:
self.original = self.env["res.users"].create({
"name": name,
"login": "firstnametest@example.com"})
def test_copy(self):
"""Copy the partner and compare the result."""
# Skip if ``mail`` is installed
if not self.mail_installed():
super(UserCase, self).test_copy()

View File

@ -0,0 +1,102 @@
# -*- coding: utf-8 -*-
"""These tests try to mimic the behavior of the UI form.
The form operates in onchange mode, with its limitations.
"""
from .base import OnChangeCase
class PartnerCompanyCase(OnChangeCase):
is_company = True
def test_create_from_form(self):
"""A user creates a company from the form."""
name = u"Sôme company"
with self.env.do_in_onchange():
# User presses ``new``
partner = self.new_partner()
# User sets a name, which triggers onchanges
partner.name = name
partner._onchange_name()
self.assertEqual(partner.name, name)
self.assertEqual(partner.firstname, False)
self.assertEqual(partner.lastname, name)
def test_empty_name_and_subnames(self):
"""If the user empties ``name``, subnames must be ``False``.
Otherwise, the ``required`` attr will not work as expected.
"""
with self.env.do_in_onchange():
# User presses ``new``
partner = self.new_partner()
# User sets a name, which triggers onchanges
partner.name = u"Foó"
partner._onchange_name()
# User unsets name, which triggers onchanges
partner.name = u""
partner._onchange_name()
self.assertEqual(partner.firstname, False)
self.assertEqual(partner.lastname, False)
class PartnerContactCase(OnChangeCase):
def test_create_from_form_only_firstname(self):
"""A user creates a contact with only the firstname from the form."""
firstname = u"Fïrst"
with self.env.do_in_onchange():
# User presses ``new``
partner = self.new_partner()
# Changes firstname, which triggers onchanges
partner.firstname = firstname
partner._onchange_subnames()
partner._onchange_name()
self.assertEqual(partner.lastname, False)
self.assertEqual(partner.firstname, firstname)
self.assertEqual(partner.name, firstname)
def test_create_from_form_only_lastname(self):
"""A user creates a contact with only the lastname from the form."""
lastname = u"Läst"
with self.env.do_in_onchange():
# User presses ``new``
partner = self.new_partner()
# Changes lastname, which triggers onchanges
partner.lastname = lastname
partner._onchange_subnames()
partner._onchange_name()
self.assertEqual(partner.firstname, False)
self.assertEqual(partner.lastname, lastname)
self.assertEqual(partner.name, lastname)
def test_create_from_form_all(self):
"""A user creates a contact with all names from the form."""
firstname = u"Fïrst"
lastname = u"Läst"
with self.env.do_in_onchange():
# User presses ``new``
partner = self.new_partner()
# Changes firstname, which triggers onchanges
partner.firstname = firstname
partner._onchange_subnames()
partner._onchange_name()
# Changes lastname, which triggers onchanges
partner.lastname = lastname
partner._onchange_subnames()
partner._onchange_name()
self.assertEqual(partner.lastname, lastname)
self.assertEqual(partner.firstname, firstname)
self.assertEqual(partner.name, u" ".join((lastname, firstname)))

View File

@ -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)

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- © 2015 Antiun Ingenieria S.L. - Antonio Espinosa
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<openerp>
<data>
<record id="view_general_configuration" model="ir.ui.view">
<field name="name">Add partner_names_order config parameter</field>
<field name="model">base.config.settings</field>
<field name="inherit_id" ref="base_setup.view_general_configuration"/>
<field name="arch" type="xml">
<xpath expr="//label[@string='Email']/.." position='after'>
<group>
<label for="partner_names_order" />
<div>
<div>
<field name="partner_names_order" class="oe_inline" />
<field name="partner_names_order_changed" invisible="1"/>
<button name="action_recalculate_partners_name"
string="Recalculate names"
icon="gtk-execute"
type="object"
help="Recalculate names for all partners. This process could take so much time if there are more than 10,000 active partners"
attrs="{'invisible': [('partner_names_order_changed', '=', True)]}"/>
</div>
</div>
</group>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_partner_simple_form_firstname" model="ir.ui.view">
<field name="name">Add firstname and lastname</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_simple_form"/>
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='name']" position="attributes">
<attribute name="attrs">{
'readonly': [('is_company', '=', False)],
'required': [('is_company', '=', True)]
}</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>
</data>
</field>
</record>
<record id="view_partner_form_firstname" model="ir.ui.view">
<field name="name">Add firstname and surnames</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='name']" position="attributes">
<attribute name="attrs">{
'readonly': [('is_company', '=', False)],
'required': [('is_company', '=', True)]
}</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">
<div class="oe_edit_only">
<field name="is_company"
on_change="onchange_type(is_company)"/>
<label for="is_company"
string="Is a Company?"/>
</div>
</xpath>
<xpath expr="//field[@name='child_ids']/form//field[@name='name']"
position="attributes">
<attribute name="attrs">{
'readonly': [('is_company', '=', False)],
'required': [('is_company', '=', True)]
}</attribute>
</xpath>
</data>
</field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<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>
<field name="inherit_id" ref="base.view_users_form"/>
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='name']" position="attributes">
<attribute name="readonly">True</attribute>
</xpath>
<xpath expr="//field[@name='email']" position="after">
<group>
<field name="lastname"
attrs="{'required': [('firstname', '=', False)]}"/>
<field name="firstname"
attrs="{'required': [('lastname', '=', False)]}"/>
</group>
</xpath>
</data>
</field>
</record>
</data>
</openerp>