diff --git a/partner_multi_relation_tabs/README.rst b/partner_multi_relation_tabs/README.rst new file mode 100644 index 000000000..df1d9b943 --- /dev/null +++ b/partner_multi_relation_tabs/README.rst @@ -0,0 +1,167 @@ +================================= +Show partner relations in own tab +================================= + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fpartner--contact-lightgray.png?logo=github + :target: https://github.com/OCA/partner-contact/tree/12.0/partner_multi_relation_tabs + :alt: OCA/partner-contact +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/partner-contact-12-0/partner-contact-12-0-partner_multi_relation_tabs + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/134/12.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module adds the possibility to show certain partner relations in a +tab on the partner form, in addition to in the list of all relations. + +This can be useful if certain relation types are regularly used and should +be overseeable at a glace. + +For instance you can make a tab named 'board' that will show all the board +members of a company, by showing the relations of types ceo, cto, cfo and +board member. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +Relation Type Tabs +~~~~~~~~~~~~~~~~~~ + +Before being able to show certain types of relations on a tab in the partner +form, you will have to define the tab. + +Do that in Contacts / Relations / Relation Tabs. + +.. image:: https://raw.githubusercontent.com/OCA/partner-contact/12.0/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-tab-configuration.png + +If you specify nothing, the tab will be shown on all partner forms that have +tabs (page elements). Normally you will select to show the tab only on +partners that are companies/organisations, or only for persons. You can also +select a category to further limit for which partners the tab wil be shown. + +The possibility exists to show a tab only on specific partners. For instance +on your own company partner. + +Relation Types +~~~~~~~~~~~~~~ + +In configuring the relation types, you can select which type of relation will +be shown on which tab. It is possible to show multiple types on one tab. + +Do that in Contacts / Relations / Relation Types. + +For example on a 'executive board' tab, you might want to show the CEO of a +company, but also the CFO, the CTO and normal board members. + +You might specify a tab for both the 'left side' of a relation, as for the +'right side' or inverse relation. So a relation 'company has ceo', with +inverse type 'person is ceo of' might specify the 'board' tab for the +company type of the relation, but possibly a 'positions held' tab for the +person side of the relation, so on the partner form of a person you see in +one tab all positions or functions a person has, regardless in which company +or organisation. + +For each side of a relation, the partner contact type and the partner category +must be consistent with those specified for the tab. + +.. image:: https://raw.githubusercontent.com/OCA/partner-contact/12.0/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-relation-type-configuration.png + +Partner Form +~~~~~~~~~~~~ + +The partner form will contain extra tab pages, for each tab that is +appropiate for that partner. So a company partner does not show the tabs that +are meant for persons and vice versa. Also tabs meant for partners with +a certain category/label will only show if partners have that label. + +When adding relations on a tab, only relation types appropiate for that tab +can be selected. + +Example of adding a relation: + +.. image:: https://raw.githubusercontent.com/OCA/partner-contact/12.0/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-partner-edit.png + +Example of a filled out board tab: + +.. image:: https://raw.githubusercontent.com/OCA/partner-contact/12.0/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-partner-display.png + +Deleting tabs +~~~~~~~~~~~~~ + +When a tab is deleted, this will in no way effect the existing relations. + +However the references on the relation types to the deleted tabs will also be +cleared. + +Searching Relations by Tab +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can search relations with the tab on which they are shown. For instance +to find all board members. + +Do that in Contacts / Relations / Relations. + +.. image:: https://raw.githubusercontent.com/OCA/partner-contact/12.0/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-relation-search.png + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Therp BV + +Contributors +~~~~~~~~~~~~ + +* Holger Brunn +* Alexandre Fayolle +* Stéphane Bidoul +* Ronald Portier +* George Daramouskas + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +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. + +This module is part of the `OCA/partner-contact `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/partner_multi_relation_tabs/__init__.py b/partner_multi_relation_tabs/__init__.py new file mode 100644 index 000000000..81ee77b9d --- /dev/null +++ b/partner_multi_relation_tabs/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from . import tablib +from . import models diff --git a/partner_multi_relation_tabs/__manifest__.py b/partner_multi_relation_tabs/__manifest__.py new file mode 100644 index 000000000..417ff0dbe --- /dev/null +++ b/partner_multi_relation_tabs/__manifest__.py @@ -0,0 +1,31 @@ +# Copyright 2014-2018 Therp BV . + +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +{ + "name": "Show partner relations in own tab", + "version": "12.0.1.0.0", + "author": "Therp BV,Odoo Community Association (OCA)", + "license": "AGPL-3", + "complexity": "normal", + "category": "Customer Relationship Management", + "depends": [ + 'partner_multi_relation', + ], + "demo": [ + "demo/res_partner_category_demo.xml", + "demo/res_partner_tab_demo.xml", + "demo/res_partner_demo.xml", + "demo/res_partner_relation_type_demo.xml", + "demo/res_partner_relation_demo.xml", + ], + "data": [ + "views/res_partner_tab.xml", + "views/res_partner_relation_type.xml", + "views/res_partner_relation_all.xml", + 'views/menu.xml', + 'security/ir.model.access.csv', + ], + "auto_install": False, + "installable": True, + "application": False, +} diff --git a/partner_multi_relation_tabs/demo/res_partner_category_demo.xml b/partner_multi_relation_tabs/demo/res_partner_category_demo.xml new file mode 100644 index 000000000..abbcb2d47 --- /dev/null +++ b/partner_multi_relation_tabs/demo/res_partner_category_demo.xml @@ -0,0 +1,17 @@ + + + + + + Government + + + + Functionary + + + + Department + + + diff --git a/partner_multi_relation_tabs/demo/res_partner_demo.xml b/partner_multi_relation_tabs/demo/res_partner_demo.xml new file mode 100644 index 000000000..08838cc96 --- /dev/null +++ b/partner_multi_relation_tabs/demo/res_partner_demo.xml @@ -0,0 +1,48 @@ + + + + + + Big company + 1 + + BIG + + + + + Bart Simpson + 0 + BS + + + + Homer Simpson + 0 + HS + + + + + Programming + 1 + HACKDEP + + + + + Helpdesk + 1 + HELPDEP + + + diff --git a/partner_multi_relation_tabs/demo/res_partner_relation_demo.xml b/partner_multi_relation_tabs/demo/res_partner_relation_demo.xml new file mode 100644 index 000000000..afd54e7d0 --- /dev/null +++ b/partner_multi_relation_tabs/demo/res_partner_relation_demo.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/partner_multi_relation_tabs/demo/res_partner_relation_type_demo.xml b/partner_multi_relation_tabs/demo/res_partner_relation_type_demo.xml new file mode 100644 index 000000000..540df4f25 --- /dev/null +++ b/partner_multi_relation_tabs/demo/res_partner_relation_type_demo.xml @@ -0,0 +1,41 @@ + + + + + + has ceo + is ceo of + c + + p + + + + + + has chairperson + is chairperson of + c + + p + + + + + + + + has department + is department of + + c + + + + diff --git a/partner_multi_relation_tabs/demo/res_partner_tab_demo.xml b/partner_multi_relation_tabs/demo/res_partner_tab_demo.xml new file mode 100644 index 000000000..708b9dc50 --- /dev/null +++ b/partner_multi_relation_tabs/demo/res_partner_tab_demo.xml @@ -0,0 +1,34 @@ + + + + + committee + Government committee + c + + + + + board + Company executive board + c + + + + positions + Positions held + p + + + + + + departments + Our departments + + + + diff --git a/partner_multi_relation_tabs/i18n/da.po b/partner_multi_relation_tabs/i18n/da.po new file mode 100644 index 000000000..3a1de447e --- /dev/null +++ b/partner_multi_relation_tabs/i18n/da.po @@ -0,0 +1,266 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * partner_multi_relation_tabs +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2019-05-09 07:03+0000\n" +"Last-Translator: Hans Henrik Gabelgaard \n" +"Language-Team: none\n" +"Language: da\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 3.5.1\n" + +#. module: partner_multi_relation_tabs +#: model:ir.model,name:partner_multi_relation_tabs.model_res_partner_relation_all +msgid "All (non-inverse + inverse) relations between partners" +msgstr "Alle (non-inverse + inverse) relationer mellem partnere" + +#. module: partner_multi_relation_tabs +#: model:ir.model,name:partner_multi_relation_tabs.model_res_partner_relation_type_selection +msgid "All relation types" +msgstr "Alle relationstyper" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_code +msgid "Code" +msgstr "Kode" + +#. module: partner_multi_relation_tabs +#: model:res.partner.tab,name:partner_multi_relation_tabs.tab_board +msgid "Company executive board" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model,name:partner_multi_relation_tabs.model_res_partner +msgid "Contact" +msgstr "Kontakt" + +#. module: partner_multi_relation_tabs +#: code:addons/partner_multi_relation_tabs/models/res_partner_relation_type.py:33 +#, python-format +msgid "Contact type left not compatible with left tab" +msgstr "Kontakt type venstre er ikke kompatibel med venstre fane" + +#. module: partner_multi_relation_tabs +#: code:addons/partner_multi_relation_tabs/models/res_partner_relation_type.py:53 +#, python-format +msgid "Contact type right not compatible with right tab" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_create_uid +msgid "Created by" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_create_date +msgid "Created on" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.category,name:partner_multi_relation_tabs.category_department +msgid "Department" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_display_name +msgid "Display Name" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.category,name:partner_multi_relation_tabs.category_functionary +msgid "Functionary" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.ui.view,arch_db:partner_multi_relation_tabs.form_res_partner_tab +msgid "General" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.category,name:partner_multi_relation_tabs.category_government +msgid "Government" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.tab,name:partner_multi_relation_tabs.tab_committee +msgid "Government committee" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_id +msgid "ID" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,help:partner_multi_relation_tabs.field_res_partner_tab_code +msgid "Language independent code for tab" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab___last_update +msgid "Last Modified on" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_write_uid +msgid "Last Updated by" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_write_date +msgid "Last Updated on" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_name +msgid "Name" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.tab,name:partner_multi_relation_tabs.tab_departments +msgid "Our departments" +msgstr "" + +#. module: partner_multi_relation_tabs +#: code:addons/partner_multi_relation_tabs/tablib/tab.py:56 +#, python-format +msgid "Partner" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model,name:partner_multi_relation_tabs.model_res_partner_relation_type +msgid "Partner Relation Type" +msgstr "" + +#. module: partner_multi_relation_tabs +#: code:addons/partner_multi_relation_tabs/models/res_partner_relation_type.py:38 +#, python-format +msgid "Partner category left not compatible with left tab" +msgstr "" + +#. module: partner_multi_relation_tabs +#: code:addons/partner_multi_relation_tabs/models/res_partner_relation_type.py:58 +#, python-format +msgid "Partner category right not compatible with right tab" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_partner_ids +msgid "Partners with this tab" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.tab,name:partner_multi_relation_tabs.tab_positions +msgid "Positions held" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.actions.act_window,name:partner_multi_relation_tabs.action_res_partner_tab +#: model:ir.ui.menu,name:partner_multi_relation_tabs.menu_res_partner_tab +msgid "Relation Tabs" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.ui.view,arch_db:partner_multi_relation_tabs.form_res_partner_tab +msgid "Select partner type and category for tab" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.ui.view,arch_db:partner_multi_relation_tabs.form_res_partner_tab +msgid "Select specific partners to have this tab" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_relation_all_tab_id +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_relation_type_selection_tab_id +msgid "Show relation on tab" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_relation_type_tab_right_id +msgid "Tab for inverse relation" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_relation_type_tab_left_id +msgid "Tab for this relation" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,help:partner_multi_relation_tabs.field_res_partner_relation_type_tab_right_id +msgid "Tab in which inverse relations will be visible on partner." +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,help:partner_multi_relation_tabs.field_res_partner_relation_type_tab_left_id +msgid "Tab in which these relations will be visible on partner." +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model,name:partner_multi_relation_tabs.model_res_partner_tab +msgid "Tabs to add to partner" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,help:partner_multi_relation_tabs.field_res_partner_tab_partner_ids +msgid "This tab will only show for certain partners.\n" +"Do not combine this with selection for contact type or category." +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_partner_category_id +msgid "Valid for partner category" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_contact_type +msgid "Valid for partner type" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,help:partner_multi_relation_tabs.field_res_partner_tab_name +msgid "Will provide title for tab in user language" +msgstr "" + +#. module: partner_multi_relation_tabs +#: code:addons/partner_multi_relation_tabs/models/res_partner_tab.py:49 +#, python-format +msgid "You can not both specify partner_ids and other criteria." +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.relation.type,name:partner_multi_relation_tabs.relation_type_company_has_ceo +msgid "has ceo" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.relation.type,name:partner_multi_relation_tabs.relation_type_committee_has_chairperson +msgid "has chairperson" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.relation.type,name:partner_multi_relation_tabs.relation_type_organisation_has_department +msgid "has department" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.relation.type,name_inverse:partner_multi_relation_tabs.relation_type_company_has_ceo +msgid "is ceo of" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.relation.type,name_inverse:partner_multi_relation_tabs.relation_type_committee_has_chairperson +msgid "is chairperson of" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.relation.type,name_inverse:partner_multi_relation_tabs.relation_type_organisation_has_department +msgid "is department of" +msgstr "" diff --git a/partner_multi_relation_tabs/i18n/nl.po b/partner_multi_relation_tabs/i18n/nl.po new file mode 100644 index 000000000..032dd312e --- /dev/null +++ b/partner_multi_relation_tabs/i18n/nl.po @@ -0,0 +1,276 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * partner_multi_relation_tabs +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-11-01 09:32+0000\n" +"PO-Revision-Date: 2017-11-01 09:32+0000\n" +"Last-Translator: Ronald Portier , 2017\n" +"Language-Team: Dutch (https://www.transifex.com/oca/teams/23907/nl/)\n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: partner_multi_relation_tabs +#: model:ir.model,name:partner_multi_relation_tabs.model_res_partner_relation_all +msgid "All (non-inverse + inverse) relations between partners" +msgstr "Alle connecties (van beide kanten) tussen relaties" + +#. module: partner_multi_relation_tabs +#: model:ir.model,name:partner_multi_relation_tabs.model_res_partner_relation_type_selection +msgid "All relation types" +msgstr "Alle connectie types" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_code +msgid "Code" +msgstr "Code" + +#. module: partner_multi_relation_tabs +#: model:res.partner.tab,name:partner_multi_relation_tabs.tab_board +msgid "Company executive board" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model,name:partner_multi_relation_tabs.model_res_partner +msgid "Contact" +msgstr "" + +#. module: partner_multi_relation_tabs +#: code:addons/partner_multi_relation_tabs/models/res_partner_relation_type.py:33 +#, python-format +msgid "Contact type left not compatible with left tab" +msgstr "Linker type contact is niet verenigbaar met linker tab" + +#. module: partner_multi_relation_tabs +#: code:addons/partner_multi_relation_tabs/models/res_partner_relation_type.py:53 +#, python-format +msgid "Contact type right not compatible with right tab" +msgstr "Rechter type contact is niet verenigbaar met rechter tab" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_create_uid +msgid "Created by" +msgstr "Aangemaakt door" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_create_date +msgid "Created on" +msgstr "Aangemaakt op" + +#. module: partner_multi_relation_tabs +#: model:res.partner.category,name:partner_multi_relation_tabs.category_department +msgid "Department" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_display_name +msgid "Display Name" +msgstr "Naam weergave" + +#. module: partner_multi_relation_tabs +#: model:res.partner.category,name:partner_multi_relation_tabs.category_functionary +msgid "Functionary" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.ui.view,arch_db:partner_multi_relation_tabs.form_res_partner_tab +msgid "General" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.category,name:partner_multi_relation_tabs.category_government +msgid "Government" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.tab,name:partner_multi_relation_tabs.tab_committee +msgid "Government committee" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_id +msgid "ID" +msgstr "ID" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,help:partner_multi_relation_tabs.field_res_partner_tab_code +msgid "Language independent code for tab" +msgstr "Taal onafhankelijke code voor tab" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab___last_update +msgid "Last Modified on" +msgstr "Laatst gewijzigd op" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_write_uid +msgid "Last Updated by" +msgstr "Laatst gewijzigd door" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_write_date +msgid "Last Updated on" +msgstr "Laatst gewijzigd op" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_name +msgid "Name" +msgstr "Naam" + +#. module: partner_multi_relation_tabs +#: model:res.partner.tab,name:partner_multi_relation_tabs.tab_departments +msgid "Our departments" +msgstr "" + +#. module: partner_multi_relation_tabs +#: code:addons/partner_multi_relation_tabs/tablib/tab.py:56 +#, python-format +msgid "Partner" +msgstr "Relatie" + +#. module: partner_multi_relation_tabs +#: model:ir.model,name:partner_multi_relation_tabs.model_res_partner_relation_type +msgid "Partner Relation Type" +msgstr "Type connectie" + +#. module: partner_multi_relation_tabs +#: code:addons/partner_multi_relation_tabs/models/res_partner_relation_type.py:38 +#, python-format +msgid "Partner category left not compatible with left tab" +msgstr "Categorie van linker partner is niet verenigbaar met linker tab" + +#. module: partner_multi_relation_tabs +#: code:addons/partner_multi_relation_tabs/models/res_partner_relation_type.py:58 +#, python-format +msgid "Partner category right not compatible with right tab" +msgstr "Categorie van rechter partner is niet verenigbaar met rechter tab" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_partner_ids +msgid "Partners with this tab" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.tab,name:partner_multi_relation_tabs.tab_positions +msgid "Positions held" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.actions.act_window,name:partner_multi_relation_tabs.action_res_partner_tab +#: model:ir.ui.menu,name:partner_multi_relation_tabs.menu_res_partner_tab +msgid "Relation Tabs" +msgstr "Connectie tabs" + +#. module: partner_multi_relation_tabs +#: model:ir.ui.view,arch_db:partner_multi_relation_tabs.form_res_partner_tab +msgid "Select partner type and category for tab" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.ui.view,arch_db:partner_multi_relation_tabs.form_res_partner_tab +msgid "Select specific partners to have this tab" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_relation_all_tab_id +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_relation_type_selection_tab_id +msgid "Show relation on tab" +msgstr "Toon connectie op tab" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_relation_type_tab_right_id +msgid "Tab for inverse relation" +msgstr "Tab voor de omgekeerde relatie" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_relation_type_tab_left_id +msgid "Tab for this relation" +msgstr "Tab voor deze relatie" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,help:partner_multi_relation_tabs.field_res_partner_relation_type_tab_right_id +msgid "Tab in which inverse relations will be visible on partner." +msgstr "Tab waar de omgekeerde connecties zichtbaar zullen zijn op de relatie." + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,help:partner_multi_relation_tabs.field_res_partner_relation_type_tab_left_id +msgid "Tab in which these relations will be visible on partner." +msgstr "Tab waarin deze connecties zichtbaar zullen zijn op de relaties." + +#. module: partner_multi_relation_tabs +#: model:ir.model,name:partner_multi_relation_tabs.model_res_partner_tab +msgid "Tabs to add to partner" +msgstr "Tab die aan de relatie wordt toegevoegd" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,help:partner_multi_relation_tabs.field_res_partner_tab_partner_ids +msgid "" +"This tab will only show for certain partners.\n" +"Do not combine this with selection for contact type or category." +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_partner_category_id +msgid "Valid for partner category" +msgstr "Geldig voor relatiecategorie" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_contact_type +msgid "Valid for partner type" +msgstr "Geldig voor type relatie" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,help:partner_multi_relation_tabs.field_res_partner_tab_name +msgid "Will provide title for tab in user language" +msgstr "Wordt gebruikt voor titel van tabblad in de taal van de gebruiker" + +#. module: partner_multi_relation_tabs +#: code:addons/partner_multi_relation_tabs/models/res_partner_tab.py:49 +#, python-format +msgid "You can not both specify partner_ids and other criteria." +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.relation.type,name:partner_multi_relation_tabs.relation_type_company_has_ceo +msgid "has ceo" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.relation.type,name:partner_multi_relation_tabs.relation_type_committee_has_chairperson +msgid "has chairperson" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.relation.type,name:partner_multi_relation_tabs.relation_type_organisation_has_department +msgid "has department" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.relation.type,name_inverse:partner_multi_relation_tabs.relation_type_company_has_ceo +msgid "is ceo of" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.relation.type,name_inverse:partner_multi_relation_tabs.relation_type_committee_has_chairperson +msgid "is chairperson of" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.relation.type,name_inverse:partner_multi_relation_tabs.relation_type_organisation_has_department +msgid "is department of" +msgstr "" + +#~ msgid "Adding field %s to res.partner nodel." +#~ msgstr "Veld %s wordt toegevoegd aan res.partner model." + +#~ msgid "Updating field %s in res.partner nodel." +#~ msgstr "Veld %s op res.partner model wordt bijgewerkt." + +#~ msgid "deleting field %s from res.partner nodel." +#~ msgstr "verwijderen veld % van res.partner model." diff --git a/partner_multi_relation_tabs/i18n/partner_multi_relation_tabs.pot b/partner_multi_relation_tabs/i18n/partner_multi_relation_tabs.pot new file mode 100644 index 000000000..b78d78ca8 --- /dev/null +++ b/partner_multi_relation_tabs/i18n/partner_multi_relation_tabs.pot @@ -0,0 +1,264 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * partner_multi_relation_tabs +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \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_multi_relation_tabs +#: model:ir.model,name:partner_multi_relation_tabs.model_res_partner_relation_all +msgid "All (non-inverse + inverse) relations between partners" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model,name:partner_multi_relation_tabs.model_res_partner_relation_type_selection +msgid "All relation types" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_code +msgid "Code" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.tab,name:partner_multi_relation_tabs.tab_board +msgid "Company executive board" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model,name:partner_multi_relation_tabs.model_res_partner +msgid "Contact" +msgstr "" + +#. module: partner_multi_relation_tabs +#: code:addons/partner_multi_relation_tabs/models/res_partner_relation_type.py:33 +#, python-format +msgid "Contact type left not compatible with left tab" +msgstr "" + +#. module: partner_multi_relation_tabs +#: code:addons/partner_multi_relation_tabs/models/res_partner_relation_type.py:53 +#, python-format +msgid "Contact type right not compatible with right tab" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_create_uid +msgid "Created by" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_create_date +msgid "Created on" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.category,name:partner_multi_relation_tabs.category_department +msgid "Department" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_display_name +msgid "Display Name" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.category,name:partner_multi_relation_tabs.category_functionary +msgid "Functionary" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.ui.view,arch_db:partner_multi_relation_tabs.form_res_partner_tab +msgid "General" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.category,name:partner_multi_relation_tabs.category_government +msgid "Government" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.tab,name:partner_multi_relation_tabs.tab_committee +msgid "Government committee" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_id +msgid "ID" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,help:partner_multi_relation_tabs.field_res_partner_tab_code +msgid "Language independent code for tab" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab___last_update +msgid "Last Modified on" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_write_uid +msgid "Last Updated by" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_write_date +msgid "Last Updated on" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_name +msgid "Name" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.tab,name:partner_multi_relation_tabs.tab_departments +msgid "Our departments" +msgstr "" + +#. module: partner_multi_relation_tabs +#: code:addons/partner_multi_relation_tabs/tablib/tab.py:56 +#, python-format +msgid "Partner" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model,name:partner_multi_relation_tabs.model_res_partner_relation_type +msgid "Partner Relation Type" +msgstr "" + +#. module: partner_multi_relation_tabs +#: code:addons/partner_multi_relation_tabs/models/res_partner_relation_type.py:38 +#, python-format +msgid "Partner category left not compatible with left tab" +msgstr "" + +#. module: partner_multi_relation_tabs +#: code:addons/partner_multi_relation_tabs/models/res_partner_relation_type.py:58 +#, python-format +msgid "Partner category right not compatible with right tab" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_partner_ids +msgid "Partners with this tab" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.tab,name:partner_multi_relation_tabs.tab_positions +msgid "Positions held" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.actions.act_window,name:partner_multi_relation_tabs.action_res_partner_tab +#: model:ir.ui.menu,name:partner_multi_relation_tabs.menu_res_partner_tab +msgid "Relation Tabs" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.ui.view,arch_db:partner_multi_relation_tabs.form_res_partner_tab +msgid "Select partner type and category for tab" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.ui.view,arch_db:partner_multi_relation_tabs.form_res_partner_tab +msgid "Select specific partners to have this tab" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_relation_all_tab_id +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_relation_type_selection_tab_id +msgid "Show relation on tab" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_relation_type_tab_right_id +msgid "Tab for inverse relation" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_relation_type_tab_left_id +msgid "Tab for this relation" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,help:partner_multi_relation_tabs.field_res_partner_relation_type_tab_right_id +msgid "Tab in which inverse relations will be visible on partner." +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,help:partner_multi_relation_tabs.field_res_partner_relation_type_tab_left_id +msgid "Tab in which these relations will be visible on partner." +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model,name:partner_multi_relation_tabs.model_res_partner_tab +msgid "Tabs to add to partner" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,help:partner_multi_relation_tabs.field_res_partner_tab_partner_ids +msgid "This tab will only show for certain partners.\n" +"Do not combine this with selection for contact type or category." +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_partner_category_id +msgid "Valid for partner category" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,field_description:partner_multi_relation_tabs.field_res_partner_tab_contact_type +msgid "Valid for partner type" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:ir.model.fields,help:partner_multi_relation_tabs.field_res_partner_tab_name +msgid "Will provide title for tab in user language" +msgstr "" + +#. module: partner_multi_relation_tabs +#: code:addons/partner_multi_relation_tabs/models/res_partner_tab.py:49 +#, python-format +msgid "You can not both specify partner_ids and other criteria." +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.relation.type,name:partner_multi_relation_tabs.relation_type_company_has_ceo +msgid "has ceo" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.relation.type,name:partner_multi_relation_tabs.relation_type_committee_has_chairperson +msgid "has chairperson" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.relation.type,name:partner_multi_relation_tabs.relation_type_organisation_has_department +msgid "has department" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.relation.type,name_inverse:partner_multi_relation_tabs.relation_type_company_has_ceo +msgid "is ceo of" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.relation.type,name_inverse:partner_multi_relation_tabs.relation_type_committee_has_chairperson +msgid "is chairperson of" +msgstr "" + +#. module: partner_multi_relation_tabs +#: model:res.partner.relation.type,name_inverse:partner_multi_relation_tabs.relation_type_organisation_has_department +msgid "is department of" +msgstr "" + diff --git a/partner_multi_relation_tabs/models/__init__.py b/partner_multi_relation_tabs/models/__init__.py new file mode 100644 index 000000000..e62aef72f --- /dev/null +++ b/partner_multi_relation_tabs/models/__init__.py @@ -0,0 +1,6 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from . import res_partner_tab +from . import res_partner_relation_type +from . import res_partner_relation_type_selection +from . import res_partner_relation_all +from . import res_partner diff --git a/partner_multi_relation_tabs/models/res_partner.py b/partner_multi_relation_tabs/models/res_partner.py new file mode 100644 index 000000000..cdf99b4a9 --- /dev/null +++ b/partner_multi_relation_tabs/models/res_partner.py @@ -0,0 +1,100 @@ +# Copyright 2014-2018 Therp BV . +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# pylint: disable=no-member +import logging +from lxml import etree + +from odoo import api, fields, models + + +_logger = logging.getLogger(__name__) # pylint: disable=invalid-name + + +class ResPartner(models.Model): + _inherit = 'res.partner' + + @api.multi + def browse(self, arg=None, prefetch=None): + for tab in self._get_tabs(): + fieldname = tab.get_fieldname() + if fieldname not in self._fields: + # Check this for performance reasons. + self.add_field(tab) + return super(ResPartner, self).browse(arg=arg, prefetch=prefetch) + + @api.model + def fields_view_get( + self, view_id=None, view_type='form', toolbar=False, + submenu=False): + """Override to add relation tabs to form.""" + result = super(ResPartner, self).fields_view_get( + view_id=view_id, view_type=view_type, toolbar=toolbar, + submenu=submenu) + if view_type != 'form' or self.env.context.get('check_view_ids'): + return result + view = etree.fromstring(result['arch']) + extra_fields = self._add_tab_pages(view) + view_model = self.env['ir.ui.view'] + result['arch'], original_fields = view_model.postprocess_and_fields( + self._name, view, result['view_id']) + for fieldname in extra_fields: + result['fields'][fieldname] = original_fields[fieldname] + return result + + def _add_tab_pages(self, view): + """Adds the relevant tabs to the partner's formview.""" + # pylint: disable=no-member + def add_invisible_extra_field(view, extra_fields, fieldname): + """Add invisible field to view.""" + view.append( + etree.Element('field', name=fieldname, invisible='True')) + extra_fields.append(fieldname) + + last_page_nodes = view.xpath('//page[last()]') + if not last_page_nodes: + # Nothing to do if form contains no pages/tabs. + return [] + extra_fields = [] + if not view.xpath('//field[@name="id"]'): + add_invisible_extra_field(view, extra_fields, 'id') + last_page = last_page_nodes[0] + for tab in self._get_tabs(): # get all tabs + self.add_field(tab) + add_invisible_extra_field( + view, extra_fields, tab.get_visible_fieldname()) + extra_fields.append(tab.get_fieldname()) + tab_page = tab.create_page() + last_page.addnext(tab_page) + last_page = tab_page # Keep ordering of tabs + return extra_fields + + @api.depends('is_company', 'category_id') + def _compute_tabs_visibility(self): + """Compute for all tabs wether they should be visible.""" + for tab in self._get_tabs(): # get all tabs + for this in self: + this[tab.get_visible_fieldname()] = \ + tab.compute_visibility(this) + + def _get_tabs(self): + tab_model = self.env['res.partner.tab'] + return tab_model.get_tabs() + + def add_field(self, tab): + """Add tab field to model. + + Will replace existing field if already present. + """ + # Visible field determines wether first field will be visible. + # This is because domains on many2many no longer work in 9.0 + # and above. + visible_field = fields.Boolean(compute='_compute_tabs_visibility') + self._add_field(tab.get_visible_fieldname(), visible_field) + if visible_field not in self._field_computed: + self._field_computed[visible_field] = [visible_field] + tab_field = fields.One2many( + comodel_name='res.partner.relation.all', + inverse_name='this_partner_id', + domain=[('tab_id', '=', tab.tab_record.id)], + string=tab.tab_record.name) + self._add_field(tab.get_fieldname(), tab_field) diff --git a/partner_multi_relation_tabs/models/res_partner_relation_all.py b/partner_multi_relation_tabs/models/res_partner_relation_all.py new file mode 100644 index 000000000..b15eb0447 --- /dev/null +++ b/partner_multi_relation_tabs/models/res_partner_relation_all.py @@ -0,0 +1,56 @@ +# Copyright 2014-2018 Therp BV . +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import api, fields, models + + +class ResPartnerRelationAll(models.AbstractModel): + """Abstract model to show each relation from two sides.""" + _inherit = 'res.partner.relation.all' + + tab_id = fields.Many2one( + comodel_name='res.partner.tab', + string='Show relation on tab', + readonly=True, + ) + + def _get_additional_view_fields(self): + """Add tab_id to view fields.""" + # pylint: disable=no-member + return ','.join([ + super(ResPartnerRelationAll, self)._get_additional_view_fields(), + "CASE" + " WHEN NOT bas.is_inverse" + " THEN lefttab.id" + " ELSE righttab.id" + " END as tab_id"]) + + def _get_additional_tables(self): + """Add res_partner_tab table to view.""" + # pylint: disable=no-member + return ' '.join([ + super(ResPartnerRelationAll, self)._get_additional_tables(), + "LEFT OUTER JOIN res_partner_tab lefttab" + " ON typ.tab_left_id = lefttab.id", + "LEFT OUTER JOIN res_partner_tab righttab" + " ON typ.tab_right_id = righttab.id"]) + + @api.onchange( + 'this_partner_id', + 'other_partner_id', + ) + def onchange_partner_id(self): + """Add tab if needed to type_selection_id domain. + + This method makes sure that when a relation is added to a tab, + it is with a relation type meant to be placed on that tab. + """ + # pylint: disable=no-member + result = super(ResPartnerRelationAll, self).onchange_partner_id() + if 'default_tab_id' in self.env.context: + result['domain'] = result['domain'] if 'domain' in result else {} + result['domain']['type_selection_id'] = \ + result['domain']['type_selection_id'] \ + if 'type_selection_id' in result['domain'] else {} + result['domain']['type_selection_id'].append( + ('tab_id', '=', self.env.context['default_tab_id'])) + return result diff --git a/partner_multi_relation_tabs/models/res_partner_relation_type.py b/partner_multi_relation_tabs/models/res_partner_relation_type.py new file mode 100644 index 000000000..71b740c03 --- /dev/null +++ b/partner_multi_relation_tabs/models/res_partner_relation_type.py @@ -0,0 +1,59 @@ +# Copyright 2014-2017 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +# pylint: disable=no-self-use +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class ResPartnerRelationType(models.Model): + # pylint: disable=too-few-public-methods + _inherit = 'res.partner.relation.type' + + tab_left_id = fields.Many2one( + comodel_name='res.partner.tab', + string='Tab for this relation', + help="Tab in which these relations will be visible on partner.") + tab_right_id = fields.Many2one( + comodel_name='res.partner.tab', + string='Tab for inverse relation', + help="Tab in which inverse relations will be visible on partner.") + + @api.multi + @api.constrains( + 'contact_type_left', + 'partner_category_left', + 'tab_left_id') + def _check_tab_left(self): + """Conditions for left partner should be consistent with tab.""" + for rec in self: + if not rec.tab_left_id: + continue + tab_contact_type = rec.tab_left_id.contact_type + if tab_contact_type and tab_contact_type != rec.contact_type_left: + raise ValidationError(_( + "Contact type left not compatible with left tab")) + tab_partner_category_id = rec.tab_left_id.partner_category_id + if tab_partner_category_id and \ + tab_partner_category_id != rec.partner_category_left: + raise ValidationError(_( + "Partner category left not compatible with left tab")) + + @api.multi + @api.constrains( + 'contact_type_right', + 'partner_category_right', + 'tab_right_id') + def _check_tab_right(self): + """Conditions for right partner should be consistent with tab.""" + for rec in self: + if not rec.tab_right_id: + continue + tab_contact_type = rec.tab_right_id.contact_type + if tab_contact_type and tab_contact_type != rec.contact_type_right: + raise ValidationError(_( + "Contact type right not compatible with right tab")) + tab_partner_category_id = rec.tab_right_id.partner_category_id + if tab_partner_category_id and \ + tab_partner_category_id != rec.partner_category_right: + raise ValidationError(_( + "Partner category right not compatible with right tab")) diff --git a/partner_multi_relation_tabs/models/res_partner_relation_type_selection.py b/partner_multi_relation_tabs/models/res_partner_relation_type_selection.py new file mode 100644 index 000000000..deac55a5a --- /dev/null +++ b/partner_multi_relation_tabs/models/res_partner_relation_type_selection.py @@ -0,0 +1,35 @@ +# Copyright 2013-2018 Therp BV . +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import fields, models + + +class ResPartnerRelationTypeSelection(models.Model): + """Virtual relation types""" + _inherit = 'res.partner.relation.type.selection' + + tab_id = fields.Many2one( + comodel_name='res.partner.tab', + string='Show relation on tab', + readonly=True, + ) + + def _get_additional_view_fields(self): + """Add tab_id to fields in view.""" + return ','.join([ + super(ResPartnerRelationTypeSelection, self) + ._get_additional_view_fields(), + "CASE" + " WHEN NOT bas.is_inverse" + " THEN lefttab.id" + " ELSE righttab.id" + " END as tab_id"]) + + def _get_additional_tables(self): + """Add two links to res_partner_tab.""" + return ' '.join([ + super(ResPartnerRelationTypeSelection, self) + ._get_additional_tables(), + "LEFT OUTER JOIN res_partner_tab lefttab" + " ON typ.tab_left_id = lefttab.id", + "LEFT OUTER JOIN res_partner_tab righttab" + " ON typ.tab_right_id = righttab.id"]) diff --git a/partner_multi_relation_tabs/models/res_partner_tab.py b/partner_multi_relation_tabs/models/res_partner_tab.py new file mode 100644 index 000000000..6c71671b9 --- /dev/null +++ b/partner_multi_relation_tabs/models/res_partner_tab.py @@ -0,0 +1,95 @@ +# Copyright 2017-2018 Therp BV . +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + +from ..tablib import Tab + + +class ResPartnerTab(models.Model): + """Model that defines relation types that might exist between partners""" + _name = 'res.partner.tab' + _description = 'Tabs to add to partner' + _order = 'name' + + @api.model + def get_partner_types(self): + """Partner types are defined by model res.partner.relation.type.""" + # pylint: disable=no-self-use + rprt_model = self.env['res.partner.relation.type'] + return rprt_model.get_partner_types() + + code = fields.Char( + string='Code', + required=True, + help="Language independent code for tab") + name = fields.Char( + string='Name', + required=True, + translate=True, + help="Will provide title for tab in user language") + contact_type = fields.Selection( + selection='get_partner_types', + string='Valid for partner type') + partner_category_id = fields.Many2one( + comodel_name='res.partner.category', + string='Valid for partner category') + partner_ids = fields.Many2many( + comodel_name='res.partner', + string="Partners with this tab", + help="This tab will only show for certain partners.\n" + "Do not combine this with selection for contact type or" + " category.") + + @api.constrains('contact_type', 'partner_category_id', 'partner_ids') + def _check_partner_ids(self): + """If partner_ids filled, other domain fields should be empty.""" + if self.partner_ids and \ + (self.contact_type or self.partner_category_id): + raise ValidationError(_( + "You can not both specify partner_ids and other criteria.")) + + @api.multi + def update_types(self, vals=None): + """Update types on write or unlink. + + If we have no vals, assume unlink. + """ + if vals: + contact_type = vals.get('contact_type', False) + partner_category_id = vals.get('partner_category_id', False) + type_model = self.env['res.partner.relation.type'] + for this in self: + for tab_side in ('left', 'right'): + side_tab = 'tab_%s_id' % tab_side + tab_using = type_model.search([(side_tab, '=', this.id)]) + for relation_type in tab_using: + type_value = relation_type['contact_type_%s' % tab_side] + category_value = \ + relation_type['partner_category_%s' % tab_side] + if (not vals or + (contact_type and contact_type != type_value) or + (partner_category_id and + partner_category_id != category_value.id)): + relation_type.write({side_tab: False}) + + @api.multi + def write(self, vals): + """Remove tab from types no longer satifying criteria.""" + if vals.get('contact_type', False) or \ + vals.get('partner_category_id', False): + self.update_types(vals) + result = super(ResPartnerTab, self).write(vals) + return result + + @api.multi + def unlink(self): + """Unlink should first remove references.""" + self.update_types() + return super(ResPartnerTab, self).unlink() + + @api.model + def get_tabs(self): + """Convert information on tabs in database to array of objects.""" + tabs = [Tab(tab_record) for tab_record in self.search([])] + return tabs diff --git a/partner_multi_relation_tabs/readme/CONTRIBUTORS.rst b/partner_multi_relation_tabs/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..e26dd7ee6 --- /dev/null +++ b/partner_multi_relation_tabs/readme/CONTRIBUTORS.rst @@ -0,0 +1,5 @@ +* Holger Brunn +* Alexandre Fayolle +* Stéphane Bidoul +* Ronald Portier +* George Daramouskas diff --git a/partner_multi_relation_tabs/readme/DESCRIPTION.rst b/partner_multi_relation_tabs/readme/DESCRIPTION.rst new file mode 100644 index 000000000..81e8d227f --- /dev/null +++ b/partner_multi_relation_tabs/readme/DESCRIPTION.rst @@ -0,0 +1,9 @@ +This module adds the possibility to show certain partner relations in a +tab on the partner form, in addition to in the list of all relations. + +This can be useful if certain relation types are regularly used and should +be overseeable at a glace. + +For instance you can make a tab named 'board' that will show all the board +members of a company, by showing the relations of types ceo, cto, cfo and +board member. diff --git a/partner_multi_relation_tabs/readme/USAGE.rst b/partner_multi_relation_tabs/readme/USAGE.rst new file mode 100644 index 000000000..6ad81416f --- /dev/null +++ b/partner_multi_relation_tabs/readme/USAGE.rst @@ -0,0 +1,78 @@ +Relation Type Tabs +~~~~~~~~~~~~~~~~~~ + +Before being able to show certain types of relations on a tab in the partner +form, you will have to define the tab. + +Do that in Contacts / Relations / Relation Tabs. + +.. image:: ../static/description/partner_multi_relation_tabs-tab-configuration.png + +If you specify nothing, the tab will be shown on all partner forms that have +tabs (page elements). Normally you will select to show the tab only on +partners that are companies/organisations, or only for persons. You can also +select a category to further limit for which partners the tab wil be shown. + +The possibility exists to show a tab only on specific partners. For instance +on your own company partner. + +Relation Types +~~~~~~~~~~~~~~ + +In configuring the relation types, you can select which type of relation will +be shown on which tab. It is possible to show multiple types on one tab. + +Do that in Contacts / Relations / Relation Types. + +For example on a 'executive board' tab, you might want to show the CEO of a +company, but also the CFO, the CTO and normal board members. + +You might specify a tab for both the 'left side' of a relation, as for the +'right side' or inverse relation. So a relation 'company has ceo', with +inverse type 'person is ceo of' might specify the 'board' tab for the +company type of the relation, but possibly a 'positions held' tab for the +person side of the relation, so on the partner form of a person you see in +one tab all positions or functions a person has, regardless in which company +or organisation. + +For each side of a relation, the partner contact type and the partner category +must be consistent with those specified for the tab. + +.. image:: ../static/description/partner_multi_relation_tabs-relation-type-configuration.png + +Partner Form +~~~~~~~~~~~~ + +The partner form will contain extra tab pages, for each tab that is +appropiate for that partner. So a company partner does not show the tabs that +are meant for persons and vice versa. Also tabs meant for partners with +a certain category/label will only show if partners have that label. + +When adding relations on a tab, only relation types appropiate for that tab +can be selected. + +Example of adding a relation: + +.. image:: ../static/description/partner_multi_relation_tabs-partner-edit.png + +Example of a filled out board tab: + +.. image:: ../static/description/partner_multi_relation_tabs-partner-display.png + +Deleting tabs +~~~~~~~~~~~~~ + +When a tab is deleted, this will in no way effect the existing relations. + +However the references on the relation types to the deleted tabs will also be +cleared. + +Searching Relations by Tab +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can search relations with the tab on which they are shown. For instance +to find all board members. + +Do that in Contacts / Relations / Relations. + +.. image:: ../static/description/partner_multi_relation_tabs-relation-search.png diff --git a/partner_multi_relation_tabs/security/ir.model.access.csv b/partner_multi_relation_tabs/security/ir.model.access.csv new file mode 100644 index 000000000..7875667aa --- /dev/null +++ b/partner_multi_relation_tabs/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +read_res_partner_tab,read_res_partner_tab,model_res_partner_tab,,1,0,0,0 +crud_res_partner_tab,crud_res_partner_tab,model_res_partner_tab,base.group_partner_manager,1,1,1,1 diff --git a/partner_multi_relation_tabs/static/description/icon.png b/partner_multi_relation_tabs/static/description/icon.png new file mode 100644 index 000000000..b219b220e Binary files /dev/null and b/partner_multi_relation_tabs/static/description/icon.png differ diff --git a/partner_multi_relation_tabs/static/description/index.html b/partner_multi_relation_tabs/static/description/index.html new file mode 100644 index 000000000..676baca03 --- /dev/null +++ b/partner_multi_relation_tabs/static/description/index.html @@ -0,0 +1,497 @@ + + + + + + +Show partner relations in own tab + + + +
+

Show partner relations in own tab

+ + +

Beta License: AGPL-3 OCA/partner-contact Translate me on Weblate Try me on Runbot

+

This module adds the possibility to show certain partner relations in a +tab on the partner form, in addition to in the list of all relations.

+

This can be useful if certain relation types are regularly used and should +be overseeable at a glace.

+

For instance you can make a tab named ‘board’ that will show all the board +members of a company, by showing the relations of types ceo, cto, cfo and +board member.

+

Table of contents

+ +
+

Usage

+
+

Relation Type Tabs

+

Before being able to show certain types of relations on a tab in the partner +form, you will have to define the tab.

+

Do that in Contacts / Relations / Relation Tabs.

+https://raw.githubusercontent.com/OCA/partner-contact/12.0/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-tab-configuration.png +

If you specify nothing, the tab will be shown on all partner forms that have +tabs (page elements). Normally you will select to show the tab only on +partners that are companies/organisations, or only for persons. You can also +select a category to further limit for which partners the tab wil be shown.

+

The possibility exists to show a tab only on specific partners. For instance +on your own company partner.

+
+
+

Relation Types

+

In configuring the relation types, you can select which type of relation will +be shown on which tab. It is possible to show multiple types on one tab.

+

Do that in Contacts / Relations / Relation Types.

+

For example on a ‘executive board’ tab, you might want to show the CEO of a +company, but also the CFO, the CTO and normal board members.

+

You might specify a tab for both the ‘left side’ of a relation, as for the +‘right side’ or inverse relation. So a relation ‘company has ceo’, with +inverse type ‘person is ceo of’ might specify the ‘board’ tab for the +company type of the relation, but possibly a ‘positions held’ tab for the +person side of the relation, so on the partner form of a person you see in +one tab all positions or functions a person has, regardless in which company +or organisation.

+

For each side of a relation, the partner contact type and the partner category +must be consistent with those specified for the tab.

+https://raw.githubusercontent.com/OCA/partner-contact/12.0/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-relation-type-configuration.png +
+
+

Partner Form

+

The partner form will contain extra tab pages, for each tab that is +appropiate for that partner. So a company partner does not show the tabs that +are meant for persons and vice versa. Also tabs meant for partners with +a certain category/label will only show if partners have that label.

+

When adding relations on a tab, only relation types appropiate for that tab +can be selected.

+

Example of adding a relation:

+https://raw.githubusercontent.com/OCA/partner-contact/12.0/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-partner-edit.png +

Example of a filled out board tab:

+https://raw.githubusercontent.com/OCA/partner-contact/12.0/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-partner-display.png +
+
+

Deleting tabs

+

When a tab is deleted, this will in no way effect the existing relations.

+

However the references on the relation types to the deleted tabs will also be +cleared.

+
+
+

Searching Relations by Tab

+

You can search relations with the tab on which they are shown. For instance +to find all board members.

+

Do that in Contacts / Relations / Relations.

+https://raw.githubusercontent.com/OCA/partner-contact/12.0/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-relation-search.png +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Therp BV
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

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.

+

This module is part of the OCA/partner-contact project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-partner-display.png b/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-partner-display.png new file mode 100644 index 000000000..97dfbecee Binary files /dev/null and b/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-partner-display.png differ diff --git a/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-partner-edit.png b/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-partner-edit.png new file mode 100644 index 000000000..a40f9b907 Binary files /dev/null and b/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-partner-edit.png differ diff --git a/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-relation-search.png b/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-relation-search.png new file mode 100644 index 000000000..676c3128b Binary files /dev/null and b/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-relation-search.png differ diff --git a/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-relation-type-configuration.png b/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-relation-type-configuration.png new file mode 100644 index 000000000..d8ba96546 Binary files /dev/null and b/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-relation-type-configuration.png differ diff --git a/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-tab-configuration.png b/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-tab-configuration.png new file mode 100644 index 000000000..bc0a53e98 Binary files /dev/null and b/partner_multi_relation_tabs/static/description/partner_multi_relation_tabs-tab-configuration.png differ diff --git a/partner_multi_relation_tabs/tablib/__init__.py b/partner_multi_relation_tabs/tablib/__init__.py new file mode 100644 index 000000000..00bb9564a --- /dev/null +++ b/partner_multi_relation_tabs/tablib/__init__.py @@ -0,0 +1,2 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from .tab import Tab diff --git a/partner_multi_relation_tabs/tablib/tab.py b/partner_multi_relation_tabs/tablib/tab.py new file mode 100644 index 000000000..071fe35bb --- /dev/null +++ b/partner_multi_relation_tabs/tablib/tab.py @@ -0,0 +1,83 @@ +# Copyright 2014-2018 Therp BV . +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +import logging +from lxml import etree + +from odoo import _ +from odoo.osv.orm import transfer_modifiers_to_node + + +_logger = logging.getLogger(__name__) # pylint: disable=invalid-name + + +NAME_PREFIX = 'relation_ids_tab' + + +class Tab(object): + """Encapsulate the information on a tab in the database.""" + + def __init__(self, tab_record): + """Create tab from tab_record. + + In this version tab_record can be assumed to be a partner.relation.tab. + """ + self.tab_record = tab_record + self.name = tab_record.code + + def get_fieldname(self): + return '%s_%s' % (NAME_PREFIX, self.tab_record.id) + + def get_visible_fieldname(self): + return '%s_visible' % self.get_fieldname() + + def create_page(self): + tab_page = etree.Element('page') + self._set_page_attrs(tab_page) + field = etree.Element( + 'field', + name=self.get_fieldname(), + context='{' + '"default_this_partner_id": id,' + '"default_tab_id": %d,' + '"active_test": False}' % self.tab_record.id) + tab_page.append(field) + tree = etree.Element('tree', editable='bottom') + field.append(tree) + # Now add fields for the editable tree view in the tab. + type_field = etree.Element( + 'field', + name='type_selection_id', + widget='many2one_clickable') + type_field.set('domain', repr([('tab_id', '=', self.tab_record.id)])) + type_field.set('options', repr({'no_create': True})) + tree.append(type_field) + other_partner_field = etree.Element( + 'field', + string=_('Partner'), + name='other_partner_id', + widget='many2one_clickable') + other_partner_field.set('options', repr({'no_create': True})) + tree.append(other_partner_field) + tree.append(etree.Element('field', name='date_start')) + tree.append(etree.Element('field', name='date_end')) + return tab_page + + def _set_page_attrs(self, tab_page): + tab_page.set('string', self.tab_record.name) + attrs = {'invisible': [(self.get_visible_fieldname(), '=', False)]} + tab_page.set('attrs', repr(attrs)) + transfer_modifiers_to_node(attrs, tab_page) + + def compute_visibility(self, partner): + """Compute visibility, dependent on partner and conditions.""" + tab = self.tab_record + if tab.partner_ids: + return partner in tab.partner_ids + if tab.contact_type: + is_company_tab = tab.contact_type == 'c' + if partner.is_company != is_company_tab: + return False + if tab.partner_category_id: + if tab.partner_category_id not in partner.category_id: + return False + return True diff --git a/partner_multi_relation_tabs/tests/__init__.py b/partner_multi_relation_tabs/tests/__init__.py new file mode 100644 index 000000000..3b747256c --- /dev/null +++ b/partner_multi_relation_tabs/tests/__init__.py @@ -0,0 +1,4 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from . import common +from . import test_partner_tabs +from . import test_tab diff --git a/partner_multi_relation_tabs/tests/common.py b/partner_multi_relation_tabs/tests/common.py new file mode 100644 index 000000000..0ee75ec47 --- /dev/null +++ b/partner_multi_relation_tabs/tests/common.py @@ -0,0 +1,50 @@ +# Copyright 2014-2018 Therp BV . +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo.tests import common + + +class TestCommon(common.SingleTransactionCase): + # pylint: disable=too-many-instance-attributes + post_install = True + + def setUp(self): + """Create common objects for tab tests.""" + # pylint: disable=invalid-name + super(TestCommon, self).setUp() + self.tab_model = self.env['res.partner.tab'] + self.type_model = self.env['res.partner.relation.type'] + self.partner_model = self.env['res.partner'] + self.relation_model = self.env['res.partner.relation'] + # Categories. + self.category_government = self.env.ref( + 'partner_multi_relation_tabs.category_government') + self.category_functionary = self.env.ref( + 'partner_multi_relation_tabs.category_functionary') + self.category_department = self.env.ref( + 'partner_multi_relation_tabs.category_department') + # Tabs. + self.tab_committee = self.env.ref( + 'partner_multi_relation_tabs.tab_committee') + self.tab_board = self.env.ref( + 'partner_multi_relation_tabs.tab_board') + self.tab_positions = self.env.ref( + 'partner_multi_relation_tabs.tab_positions') + self.tab_departments = self.env.ref( + 'partner_multi_relation_tabs.tab_departments') + # Types. + self.type_chairperson = self.env.ref( + 'partner_multi_relation_tabs' + '.relation_type_committee_has_chairperson') + self.type_ceo = self.env.ref( + 'partner_multi_relation_tabs' + '.relation_type_company_has_ceo') + # Partners. + self.partner_big_company = self.env.ref( + 'partner_multi_relation_tabs.partner_big_company') + self.partner_important_person = self.env.ref( + 'partner_multi_relation_tabs.partner_important_person') + self.partner_common_person = self.env.ref( + 'partner_multi_relation_tabs.partner_common_person') + # Relations. + self.relation_company_ceo = self.env.ref( + 'partner_multi_relation_tabs.relation_company_ceo') diff --git a/partner_multi_relation_tabs/tests/test_partner_tabs.py b/partner_multi_relation_tabs/tests/test_partner_tabs.py new file mode 100644 index 000000000..0b56b1261 --- /dev/null +++ b/partner_multi_relation_tabs/tests/test_partner_tabs.py @@ -0,0 +1,157 @@ +# Copyright 2014-2018 Therp BV . +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from lxml import etree + +from odoo.exceptions import ValidationError + +from . import common +from ..tablib import Tab + + +class TestPartnerTabs(common.TestCommon): + post_install = True + + def test_create_tab(self): + self.assertTrue(bool(self.tab_board)) + tab_obj = Tab(self.tab_board) + # fields_view_get should force the creation of the new tabs. + view_partner_form = self.env.ref('base.view_partner_form') + view = self.partner_model.with_context().fields_view_get( + view_id=view_partner_form.id, view_type='form') + # The form view for partner should now also contain field 'id'. + tree = etree.fromstring(view['arch']) + field = tree.xpath('//field[@name="id"]') + self.assertTrue(field, 'Id field does not exist.') + # There should now be a field in res_partner for the new tab. + fieldname = tab_obj.get_fieldname() + self.assertTrue(fieldname in self.partner_model._fields) + # And we should have a field for the tab: + field = tree.xpath('//field[@name="%s"]' % fieldname) + self.assertTrue( + field, + 'Tab field %s does not exist in %s.' % + (fieldname, etree.tostring(tree))) + # There should be no effect on the tree view: + view = self. partner_model.with_context().fields_view_get( + view_type='tree') + tree = etree.fromstring(view['arch']) + field = tree.xpath('//field[@name="%s"]' % fieldname) + self.assertFalse( + field, + 'Tab field %s should not exist in %s.' % + (fieldname, etree.tostring(tree))) + + def test_view_without_pages(self): + """Check that _add_tab_pages does not effect view without pages.""" + # pylint: disable=protected-access + view = etree.Element('view') + extra_fields = self.partner_model._add_tab_pages(view) + self.assertFalse(extra_fields) + + def test_tab_modifications(self): + tab_executive = self.tab_model.create({ + 'code': 'executive', + 'name': 'Executive members'}) + self.assertTrue(bool(tab_executive)) + type_chairperson = self.type_model.create({ + 'name': 'has chairperson', + 'name_inverse': 'is chairperson for', + 'contact_type_left': 'p', # This emulates a user mistake. + 'contact_type_right': 'p', + 'tab_left_id': tab_executive.id}) + self.assertTrue(bool(type_chairperson)) + # If we change tab now to be only valid on company partners + # the tab_left_id field should be cleared from the type: + tab_executive.write({'contact_type': 'c'}) + self.assertFalse(type_chairperson.tab_left_id.id) + # Trying to set the tab back on type should be impossible: + with self.assertRaises(ValidationError): + type_chairperson.write({'tab_left_id': tab_executive.id}) + # We should be able to change tab, if also changing contact type. + type_chairperson.write({ + 'contact_type_left': 'c', + 'tab_left_id': tab_executive.id}) + self.assertEqual( + type_chairperson.tab_left_id.id, + tab_executive.id) + # Unlinking the tab should reset the tab_left_id on relation type. + tab_executive.unlink() + self.assertEqual( + type_chairperson.tab_left_id.id, + False) + # It should not be possible to add category or contact type to as + # selection criteria to a tab meant for specific partners. + with self.assertRaises(ValidationError): + self.tab_departments.write({'contact_type': 'c'}) + with self.assertRaises(ValidationError): + self.tab_departments.write({ + 'partner_category_id': self.category_government.id}) + + def test_type_modifications(self): + self.assertTrue(bool(self.tab_board)) + self.assertTrue(bool(self.tab_positions)) + self.assertTrue(bool(self.type_chairperson)) + # Trying to clear either category should raise ValidationError: + with self.assertRaises(ValidationError): + self.type_chairperson.write({'partner_category_left': False}) + with self.assertRaises(ValidationError): + self.type_chairperson.write({'partner_category_right': False}) + # Trying to clear either contact type should raise ValidationError: + with self.assertRaises(ValidationError): + self.type_chairperson.write({'contact_type_left': False}) + with self.assertRaises(ValidationError): + self.type_chairperson.write({'contact_type_right': False}) + + def test_relations(self): + """Test relations shown on tab.""" + relation_all_model = self.env['res.partner.relation.all'] + self.assertTrue(bool(self.tab_board)) + self.assertTrue(bool(self.type_ceo)) + self.assertTrue(bool(self.partner_big_company)) + self.assertTrue(bool(self.partner_important_person)) + self.assertTrue(bool(self.relation_company_ceo)) + # Now we should be able to find the relation with the tab_id: + board_partners = relation_all_model.search([ + ('tab_id', '=', self.tab_board.id)]) + self.assertTrue(bool(board_partners)) + self.assertIn( + self.partner_big_company, + [relation.this_partner_id for relation in board_partners]) + # We should find the company on the partner through tab field: + tab_obj = Tab(self.tab_board) + fieldname = tab_obj.get_fieldname() + self.assertTrue(fieldname in self.partner_model._fields) + board_partners = self.partner_big_company[fieldname] + self.assertEqual(len(board_partners), 1) + self.assertEqual( + board_partners.other_partner_id.id, + self.partner_important_person.id) + # When adding a new relation on a tab, type must be for tab. + onchange_result = board_partners.with_context( + default_tab_id=self.tab_board.id + ).onchange_partner_id() + self.assertTrue(onchange_result) + self.assertIn('domain', onchange_result) + self.assertIn('type_selection_id', onchange_result['domain']) + self.assertEqual( + onchange_result['domain']['type_selection_id'][-1], + ('tab_id', '=', self.tab_board.id)) + + def test_compute_visibility(self): + """Check the computation of visibility on partners.""" + # pylint: disable=protected-access + main_partner = self.env.ref('base.main_partner') + main_partner._compute_tabs_visibility() + tab_obj = Tab(self.tab_departments) + fieldname = tab_obj.get_fieldname() + visible_fieldname = tab_obj.get_visible_fieldname() + self.assertIn(visible_fieldname, main_partner._fields) + self.assertIn(fieldname, main_partner._fields) + self.assertEqual(main_partner[visible_fieldname], True) + department_relations = main_partner[fieldname] + self.assertTrue(len(department_relations) >= 1) + departments = [ + relation.other_partner_id for relation in department_relations] + for department in departments: + self.assertIn( + self.category_department, department.category_id) diff --git a/partner_multi_relation_tabs/tests/test_tab.py b/partner_multi_relation_tabs/tests/test_tab.py new file mode 100644 index 000000000..fd82abbf2 --- /dev/null +++ b/partner_multi_relation_tabs/tests/test_tab.py @@ -0,0 +1,39 @@ +# Copyright 2018 Therp BV . +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from . import common + +from ..tablib import Tab + + +class TestTab(common.TestCommon): + + def test_create_page(self): + self.assertTrue(bool(self.tab_board)) + tab_obj = Tab(self.tab_board) + page = tab_obj.create_page() + # And we should have a field for (amongst others) selection_type_id. + field = page.xpath('//field[@name="type_selection_id"]') + self.assertTrue(field, 'Field selection_type_id not in page.') + + def test_visibility(self): + """Tab positions should be shown for functionaries, but not others.""" + self.assertTrue(bool(self.tab_positions)) + self.assertTrue(bool(self.partner_important_person)) + self.assertTrue(bool(self.partner_common_person)) + tab_obj = Tab(self.tab_positions) + self.assertTrue( + tab_obj.compute_visibility(self.partner_important_person), + 'Positions tab should be visible for functionary.') + self.assertFalse( + tab_obj.compute_visibility(self.partner_common_person), + 'Positions tab should not be visible for non-functionary.') + # Tab for departments should only be visible for main partner + self.assertTrue(bool(self.tab_departments)) + self.assertTrue(bool(self.partner_big_company)) + tab_obj = Tab(self.tab_departments) + self.assertTrue( + tab_obj.compute_visibility(self.env.ref('base.main_partner')), + 'Department tab should be visible for main partner.') + self.assertFalse( + tab_obj.compute_visibility(self.partner_big_company), + 'Department tab should not be visible for other partners.') diff --git a/partner_multi_relation_tabs/views/menu.xml b/partner_multi_relation_tabs/views/menu.xml new file mode 100644 index 000000000..97eb9caac --- /dev/null +++ b/partner_multi_relation_tabs/views/menu.xml @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/partner_multi_relation_tabs/views/res_partner_relation_all.xml b/partner_multi_relation_tabs/views/res_partner_relation_all.xml new file mode 100644 index 000000000..adce7fcaa --- /dev/null +++ b/partner_multi_relation_tabs/views/res_partner_relation_all.xml @@ -0,0 +1,17 @@ + + + + + res.partner.relation.all + + + + + + + + + diff --git a/partner_multi_relation_tabs/views/res_partner_relation_type.xml b/partner_multi_relation_tabs/views/res_partner_relation_type.xml new file mode 100644 index 000000000..ce8e2c6d1 --- /dev/null +++ b/partner_multi_relation_tabs/views/res_partner_relation_type.xml @@ -0,0 +1,20 @@ + + + + + res.partner.relation.type + + + + + + + + + + + + diff --git a/partner_multi_relation_tabs/views/res_partner_tab.xml b/partner_multi_relation_tabs/views/res_partner_tab.xml new file mode 100644 index 000000000..4a2d5beed --- /dev/null +++ b/partner_multi_relation_tabs/views/res_partner_tab.xml @@ -0,0 +1,44 @@ + + + + + res.partner.tab + + + + + + + + + + res.partner.tab + +
+ + + + + + + + + + + + +
+
+
+ +
diff --git a/setup/_metapackage/setup.py b/setup/_metapackage/setup.py index 636e2adcc..d7c3c99a7 100644 --- a/setup/_metapackage/setup.py +++ b/setup/_metapackage/setup.py @@ -44,6 +44,7 @@ setuptools.setup( 'odoo12-addon-partner_identification_gln', 'odoo12-addon-partner_industry_secondary', 'odoo12-addon-partner_multi_relation', + 'odoo12-addon-partner_multi_relation_tabs', 'odoo12-addon-partner_phone_extension', 'odoo12-addon-partner_phonecall_schedule', 'odoo12-addon-partner_postal_address',