From 5470c40cdf945e84f6c01c25f6086bd565ee8552 Mon Sep 17 00:00:00 2001 From: Ronald Portier Date: Thu, 28 Sep 2017 20:08:10 +0200 Subject: [PATCH] [IMP] New module partner_multi_relation_hierarchy --- partner_multi_relation_hierarchy/README.rst | 50 ++++++ partner_multi_relation_hierarchy/__init__.py | 4 + .../__manifest__.py | 21 +++ partner_multi_relation_hierarchy/i18n/nl.po | 149 ++++++++++++++++++ .../i18n/partner_multi_relation_hierarchy.pot | 148 +++++++++++++++++ .../models/__init__.py | 7 + .../models/res_partner.py | 39 +++++ .../models/res_partner_relation.py | 44 ++++++ .../models/res_partner_relation_hierarchy.py | 102 ++++++++++++ .../models/res_partner_relation_type.py | 22 +++ .../security/ir.model.access.csv | 2 + .../tests/__init__.py | 4 + .../tests/test_partner_hierarchy.py | 68 ++++++++ .../views/res_partner.xml | 35 ++++ .../views/res_partner_relation_type.xml | 17 ++ 15 files changed, 712 insertions(+) create mode 100644 partner_multi_relation_hierarchy/README.rst create mode 100644 partner_multi_relation_hierarchy/__init__.py create mode 100644 partner_multi_relation_hierarchy/__manifest__.py create mode 100644 partner_multi_relation_hierarchy/i18n/nl.po create mode 100644 partner_multi_relation_hierarchy/i18n/partner_multi_relation_hierarchy.pot create mode 100644 partner_multi_relation_hierarchy/models/__init__.py create mode 100644 partner_multi_relation_hierarchy/models/res_partner.py create mode 100644 partner_multi_relation_hierarchy/models/res_partner_relation.py create mode 100644 partner_multi_relation_hierarchy/models/res_partner_relation_hierarchy.py create mode 100644 partner_multi_relation_hierarchy/models/res_partner_relation_type.py create mode 100644 partner_multi_relation_hierarchy/security/ir.model.access.csv create mode 100644 partner_multi_relation_hierarchy/tests/__init__.py create mode 100644 partner_multi_relation_hierarchy/tests/test_partner_hierarchy.py create mode 100644 partner_multi_relation_hierarchy/views/res_partner.xml create mode 100644 partner_multi_relation_hierarchy/views/res_partner_relation_type.xml diff --git a/partner_multi_relation_hierarchy/README.rst b/partner_multi_relation_hierarchy/README.rst new file mode 100644 index 000000000..d1796a948 --- /dev/null +++ b/partner_multi_relation_hierarchy/README.rst @@ -0,0 +1,50 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :alt: License + +Partner Multi Relation Hierarchy +================================ + +This module extends the concept of relations between partners with the concept +of an hierarchy. + +For istance a company belonging to a larger concern would have the concern 'above' it. + +Or a person might belong to a local organisation, that in turn might belong to a +provincial organisation, which might belong to a nation organisation. + +It will not be excluded that a partner might be connected to several hierarchies. + +The hierarchical 'chains' will be made visible on the partner. + +More info +--------- + +For further information, please visit: + + * https://www.odoo.com/forum/help-1 + +Known issues / Roadmap +====================== + +Credits +======= + +Contributors +------------ + +* Ronald Portier + +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 https://github.com/oca/partner-contact diff --git a/partner_multi_relation_hierarchy/__init__.py b/partner_multi_relation_hierarchy/__init__.py new file mode 100644 index 000000000..3e51f5587 --- /dev/null +++ b/partner_multi_relation_hierarchy/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from . import models diff --git a/partner_multi_relation_hierarchy/__manifest__.py b/partner_multi_relation_hierarchy/__manifest__.py new file mode 100644 index 000000000..a073b4be2 --- /dev/null +++ b/partner_multi_relation_hierarchy/__manifest__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + "name": "Partner relation hierarchy", + "version": "10.0.0.1.0", + "author": "Therp BV,Odoo Community Association (OCA)", + "complexity": "normal", + "category": "Customer Relationship Management", + "license": "AGPL-3", + "depends": [ + "partner_multi_relation", + ], + "data": [ + 'views/res_partner.xml', + 'views/res_partner_relation_type.xml', + 'security/ir.model.access.csv', + ], + "auto_install": False, + "installable": True, +} diff --git a/partner_multi_relation_hierarchy/i18n/nl.po b/partner_multi_relation_hierarchy/i18n/nl.po new file mode 100644 index 000000000..eef381406 --- /dev/null +++ b/partner_multi_relation_hierarchy/i18n/nl.po @@ -0,0 +1,149 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * partner_multi_relation_hierarchy +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-11-01 09:31+0000\n" +"PO-Revision-Date: 2017-11-01 09:31+0000\n" +"Last-Translator: Ronald Portier , 2017\n" +"Language-Team: Dutch (https://www.transifex.com/oca/teams/23907/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_multi_relation_hierarchy +#: model:ir.model.fields,help:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_hierarchy_display +msgid "Compact representation of hierarchy" +msgstr "Compacte weergave van de hiërarchie" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_display_name +msgid "Display Name" +msgstr "Naam voor weergave" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_has_partner_above +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_users_has_partner_above +msgid "Has partner above" +msgstr "Heeft relatie boven zich" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_hierarchy_display +#: model:ir.ui.view,arch_db:partner_multi_relation_hierarchy.view_partner_form +msgid "Hierarchy" +msgstr "Hiërarchie" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_id +msgid "ID" +msgstr "ID" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy___last_update +msgid "Last Modified on" +msgstr "Laatst gewijzigd" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_level +msgid "Level" +msgstr "Niveau" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,help:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_level +msgid "Number of levels that partner above is higher up" +msgstr "Niveau verschil met bovenliggende partner" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model,name:partner_multi_relation_hierarchy.model_res_partner +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_partner_id +msgid "Partner" +msgstr "Relatie" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model,name:partner_multi_relation_hierarchy.model_res_partner_relation_type +msgid "Partner Relation Type" +msgstr "Type connectie" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_partner_above_id +msgid "Partner above" +msgstr "Bovenliggende relatie" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_partner_below_id +msgid "Partner below" +msgstr "Onderliggende relatie" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,help:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_partner_below_id +msgid "Partner immediately below partner above in the hierarchy" +msgstr "Direct bovenliggende relatie in de hiërarchie" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model,name:partner_multi_relation_hierarchy.model_res_partner_relation +msgid "Partner relation" +msgstr "Connectie" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,help:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_partner_above_id +msgid "Partner somewhere above in the hierarchy" +msgstr "Relatie één of meer plaatsen hoger in de hiërarchie" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_partner_above_ids_4248 +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_users_partner_above_ids +msgid "Partners above in hierarchy" +msgstr "Bovenliggende partners in de hiërarchie" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_relation_type_hierarchy +msgid "Partners equal, right above, or left above" +msgstr "Relaties gelijkwaardig, rechts hoger of links hoger" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model,name:partner_multi_relation_hierarchy.model_res_partner_relation_hierarchy +msgid "Partners with all their partnes above" +msgstr "Relaties met al hun bovenliggende relaties" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,help:partner_multi_relation_hierarchy.field_res_partner_relation_type_hierarchy +msgid "Select wether the relation between the partners can be considered hierarchical. And if so which side is considered 'above'." +msgstr "Geef aan of de connectie tussen de relaties een hiërarchie aangeeft. Zo ja geef aan welke kant beschouwd wordt als 'bovenliggend'" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,help:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_partner_id +msgid "The partner at the base of the hierarchy" +msgstr "De relatie aan de basis van de hiërarchie" + +#. module: partner_multi_relation_hierarchy +#: code:addons/partner_multi_relation_hierarchy/models/res_partner_relation.py:43 +#, python-format +msgid "There is already a hierarchical relation between these partners: %s" +msgstr "Er bestaat reeds een hiërarchische relatie tussen deze partners." + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_partner_above_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_users_partner_above_hierarchy +msgid "Upper level partners" +msgstr "Bovenliggende relaties" + +#. module: partner_multi_relation_hierarchy +#: selection:res.partner.relation.type,hierarchy:0 +msgid "equal" +msgstr "gelijk" + +#. module: partner_multi_relation_hierarchy +#: selection:res.partner.relation.type,hierarchy:0 +msgid "left_above_right" +msgstr "links boven rechts" + +#. module: partner_multi_relation_hierarchy +#: selection:res.partner.relation.type,hierarchy:0 +msgid "right_above_left" +msgstr "rechts boven links" + diff --git a/partner_multi_relation_hierarchy/i18n/partner_multi_relation_hierarchy.pot b/partner_multi_relation_hierarchy/i18n/partner_multi_relation_hierarchy.pot new file mode 100644 index 000000000..b13c44f86 --- /dev/null +++ b/partner_multi_relation_hierarchy/i18n/partner_multi_relation_hierarchy.pot @@ -0,0 +1,148 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * partner_multi_relation_hierarchy +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-11-01 09:31+0000\n" +"PO-Revision-Date: 2017-11-01 09:31+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_multi_relation_hierarchy +#: model:ir.model.fields,help:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_hierarchy_display +msgid "Compact representation of hierarchy" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_display_name +msgid "Display Name" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_has_partner_above +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_users_has_partner_above +msgid "Has partner above" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_hierarchy_display +#: model:ir.ui.view,arch_db:partner_multi_relation_hierarchy.view_partner_form +msgid "Hierarchy" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_id +msgid "ID" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy___last_update +msgid "Last Modified on" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_level +msgid "Level" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,help:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_level +msgid "Number of levels that partner above is higher up" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model,name:partner_multi_relation_hierarchy.model_res_partner +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_partner_id +msgid "Partner" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model,name:partner_multi_relation_hierarchy.model_res_partner_relation_type +msgid "Partner Relation Type" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_partner_above_id +msgid "Partner above" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_partner_below_id +msgid "Partner below" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,help:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_partner_below_id +msgid "Partner immediately below partner above in the hierarchy" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model,name:partner_multi_relation_hierarchy.model_res_partner_relation +msgid "Partner relation" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,help:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_partner_above_id +msgid "Partner somewhere above in the hierarchy" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_partner_above_ids_4248 +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_users_partner_above_ids +msgid "Partners above in hierarchy" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_relation_type_hierarchy +msgid "Partners equal, right above, or left above" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model,name:partner_multi_relation_hierarchy.model_res_partner_relation_hierarchy +msgid "Partners with all their partnes above" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,help:partner_multi_relation_hierarchy.field_res_partner_relation_type_hierarchy +msgid "Select wether the relation between the partners can be considered hierarchical. And if so which side is considered 'above'." +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,help:partner_multi_relation_hierarchy.field_res_partner_relation_hierarchy_partner_id +msgid "The partner at the base of the hierarchy" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: code:addons/partner_multi_relation_hierarchy/models/res_partner_relation.py:43 +#, python-format +msgid "There is already a hierarchical relation between these partners: %s" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_partner_partner_above_hierarchy +#: model:ir.model.fields,field_description:partner_multi_relation_hierarchy.field_res_users_partner_above_hierarchy +msgid "Upper level partners" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: selection:res.partner.relation.type,hierarchy:0 +msgid "equal" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: selection:res.partner.relation.type,hierarchy:0 +msgid "left_above_right" +msgstr "" + +#. module: partner_multi_relation_hierarchy +#: selection:res.partner.relation.type,hierarchy:0 +msgid "right_above_left" +msgstr "" + diff --git a/partner_multi_relation_hierarchy/models/__init__.py b/partner_multi_relation_hierarchy/models/__init__.py new file mode 100644 index 000000000..a72739cde --- /dev/null +++ b/partner_multi_relation_hierarchy/models/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from . import res_partner_relation_type +from . import res_partner_relation_hierarchy +from . import res_partner +from . import res_partner_relation diff --git a/partner_multi_relation_hierarchy/models/res_partner.py b/partner_multi_relation_hierarchy/models/res_partner.py new file mode 100644 index 000000000..aa24083ed --- /dev/null +++ b/partner_multi_relation_hierarchy/models/res_partner.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Copyright 2017-2018 Therp BV . +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import api, fields, models + + +class ResPartner(models.Model): + _inherit = 'res.partner' + + @api.multi + def _compute_partners_above(self): + """Check partners up in the hierarchy if any.""" + for this in self: + this.partner_above_hierarchy = \ + this.partner_above_ids and \ + this.partner_above_ids[0].hierarchy_display or '' + this.has_partner_above = bool(this.partner_above_ids) + + partner_above_ids = fields.One2many( + comodel_name='res.partner.relation.hierarchy', + inverse_name='partner_id', + string='Partners above in hierarchy', + readonly=True) + partner_above_hierarchy = fields.Char( + string="Upper level partners", + compute='_compute_partners_above', + readonly=True) + has_partner_above = fields.Boolean( + compute='_compute_partners_above', + readonly=True) + + @api.multi + def is_above(self, other_partner): + """Check whether this partner is above other_partner.""" + self.ensure_one() + for partner_above in other_partner.partner_above_ids: + if self.id == partner_above.partner_above_id.id: + return True + return False diff --git a/partner_multi_relation_hierarchy/models/res_partner_relation.py b/partner_multi_relation_hierarchy/models/res_partner_relation.py new file mode 100644 index 000000000..ff183313c --- /dev/null +++ b/partner_multi_relation_hierarchy/models/res_partner_relation.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# Copyright 2017-2018 Therp BV . +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import _, api, models +from odoo.exceptions import ValidationError + + +class ResPartnerRelation(models.Model): + _inherit = 'res.partner.relation' + + @api.model + def create(self, vals): + """Prevent contradictory links in hierarchy. + + We should not do this in a constraint, as those are only checked + after creation has already been done, and other modules might + fail when they process in invalid hierarchy when triggered on + create. + + TODO: Prevent this also on write. + """ + context = self.env.context + if 'left_partner_id' not in vals and context.get('active_id'): + vals['left_partner_id'] = context.get('active_id') + # Check if we have needed left partner, type and right + # partner. If not leave error handling to super + if not {'left_partner_id', 'type_id', 'right_partner_id'} <= set(vals): + return super(ResPartnerRelation, self).create(vals) + type_model = self.env['res.partner.relation.type'] + type_id = type_model.browse(vals['type_id']) + hierarchy = type_id.hierarchy + # If relation is not for a hierarchy, just return super + if hierarchy == 'equal': + return super(ResPartnerRelation, self).create(vals) + partner_model = self.env['res.partner'] + left_partner = partner_model.browse(vals['left_partner_id']) + right_partner = partner_model.browse(vals['right_partner_id']) + if ((hierarchy == 'left' and right_partner.is_above(left_partner)) or + (hierarchy == 'right' and + left_partner.is_above(right_partner))): + raise ValidationError( + _("Not allowed to create an inconsistent hierarchy")) + # Everything is OK, call super + return super(ResPartnerRelation, self).create(vals) diff --git a/partner_multi_relation_hierarchy/models/res_partner_relation_hierarchy.py b/partner_multi_relation_hierarchy/models/res_partner_relation_hierarchy.py new file mode 100644 index 000000000..33fef4760 --- /dev/null +++ b/partner_multi_relation_hierarchy/models/res_partner_relation_hierarchy.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +"""Abstract model to show each relation from two sides.""" +from psycopg2.extensions import AsIs + +from openerp import api, fields, models +from openerp.tools import drop_view_if_exists + + +class ResPartnerRelationHierarchy(models.AbstractModel): + """Abstract model to show each relation from two sides.""" + _auto = False + _log_access = False + _name = 'res.partner.relation.hierarchy' + _description = 'Partners with all their partnes above' + _order = 'partner_id, level desc, partner_above_id' + + partner_id = fields.Many2one( + comodel_name='res.partner', + string='Partner', + required=True, + readonly=True, + help="The partner at the base of the hierarchy") + partner_above_id = fields.Many2one( + comodel_name='res.partner', + string='Partner above', + required=True, + readonly=True, + help="Partner somewhere above in the hierarchy") + partner_below_id = fields.Many2one( + comodel_name='res.partner', + string='Partner below', + required=True, + readonly=True, + help="Partner immediately below partner above in the hierarchy") + level = fields.Integer( + string='Level', + required=True, + readonly=True, + help="Number of levels that partner above is higher up") + hierarchy_display = fields.Char( + string='Hierarchy', + required=True, + readonly=True, + help="Compact representation of hierarchy") + + @api.model_cr_context + def _auto_init(self): + """Create hierarchy view only taking into account active relations. + """ + cr = self._cr + drop_view_if_exists(cr, self._table) + cr.execute( + """\ +CREATE OR REPLACE VIEW %(table)s AS +WITH RECURSIVE hierarchy_relations AS ( + SELECT + left_partner_id as partner_above_id, + type_id, + right_partner_id as partner_below_id, + op.name as partner_above_name + FROM res_partner_relation rpr + JOIN res_partner_relation_type rt ON rpr.type_id = rt.id + JOIN res_partner op ON left_partner_id = op.id + WHERE rt.hierarchy = 'left' + AND (rpr.date_start is NULL OR rpr.date_start <= CURRENT_DATE) + AND (rpr.date_end is NULL OR rpr.date_end > CURRENT_DATE) + UNION + SELECT right_partner_id, type_id, left_partner_id, op.name + FROM res_partner_relation rpr + JOIN res_partner_relation_type rt ON rpr.type_id = rt.id + JOIN res_partner op ON right_partner_id = op.id + WHERE rt.hierarchy = 'right' + AND (rpr.date_start is NULL OR rpr.date_start <= CURRENT_DATE) + AND (rpr.date_end is NULL OR rpr.date_end > CURRENT_DATE) + ), + hierarchy_tree( + partner_id, partner_above_id, partner_below_id, level, hierarchy_display, + path, cycle + ) AS ( + SELECT + partner_below_id as partner_id, partner_above_id, partner_below_id, + 1 as level, partner_above_name as hierarchy_display, + ARRAY[partner_below_id], false + FROM hierarchy_relations hr + UNION ALL + SELECT htree.partner_id, hr.partner_above_id, hr.partner_below_id, + htree.level + 1, + hr.partner_above_name || '/' || htree.hierarchy_display, + htree.path || hr.partner_below_id, + hr.partner_below_id = ANY(htree.path) + FROM hierarchy_relations hr, hierarchy_tree htree + WHERE hr.partner_below_id = htree.partner_above_id + AND NOT cycle + ) + SELECT + row_number() over (order by partner_id, level, partner_above_id) as id, * + FROM hierarchy_tree ht + """, + {'table': AsIs(self._table)}) + return super(ResPartnerRelationHierarchy, self)._auto_init() diff --git a/partner_multi_relation_hierarchy/models/res_partner_relation_type.py b/partner_multi_relation_hierarchy/models/res_partner_relation_type.py new file mode 100644 index 000000000..3c565389e --- /dev/null +++ b/partner_multi_relation_hierarchy/models/res_partner_relation_type.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from openerp import fields, models + + +HIERARCHY_SELECTION = [ + ('left', 'left_above_right'), + ('equal', 'equal'), + ('right', 'right_above_left')] + + +class ResPartnerRelationType(models.Model): + _inherit = 'res.partner.relation.type' + + hierarchy = fields.Selection( + selection=HIERARCHY_SELECTION, + string='Partners equal, right above, or left above', + default='equal', + help="Select wether the relation between the partners" + " can be considered hierarchical. And if so" + " which side is considered 'above'.") diff --git a/partner_multi_relation_hierarchy/security/ir.model.access.csv b/partner_multi_relation_hierarchy/security/ir.model.access.csv new file mode 100644 index 000000000..2dfe30d66 --- /dev/null +++ b/partner_multi_relation_hierarchy/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +read_res_partner_relation_hierarchy,access_res_partner_relation_hierarchy,model_res_partner_relation_hierarchy,,1,0,0,0 diff --git a/partner_multi_relation_hierarchy/tests/__init__.py b/partner_multi_relation_hierarchy/tests/__init__.py new file mode 100644 index 000000000..4fd9c69a5 --- /dev/null +++ b/partner_multi_relation_hierarchy/tests/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from . import test_partner_hierarchy diff --git a/partner_multi_relation_hierarchy/tests/test_partner_hierarchy.py b/partner_multi_relation_hierarchy/tests/test_partner_hierarchy.py new file mode 100644 index 000000000..a504afdbf --- /dev/null +++ b/partner_multi_relation_hierarchy/tests/test_partner_hierarchy.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Therp BV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from openerp.exceptions import ValidationError +from openerp.tests import common + + +class TestPartnerHierarchy(common.TransactionCase): + + def test_hierarchy(self): + """Test hierarchy. + + Create a hierarchical relation type. Then create a hierarchy + of three levels. + Check the computation of the display name. + Check that no circular hierarchy can be created. + """ + partner_model = self.env['res.partner'] + type_model = self.env['res.partner.relation.type'] + relation_model = self.env['res.partner.relation'] + partner_multinational = partner_model.create({ + 'name': 'Big Important Multinational', + 'is_company': True, + 'ref': 'COM001'}) + partner_national = partner_model.create({ + 'name': 'National Company', + 'is_company': True, + 'ref': 'COM002'}) + partner_local = partner_model.create({ + 'name': 'Small local company', + 'is_company': True, + 'ref': 'COM003'}) + # Create a hierarchical relation type between companies: + type_company2branch = type_model.create({ + 'name': 'has daughter company', + 'name_inverse': 'has parent company', + 'contact_type_left': 'c', + 'contact_type_right': 'c', + 'hierarchy': 'left'}) + # Let the local company belong to the national company: + relation_model.create({ + 'left_partner_id': partner_national.id, + 'type_id': type_company2branch.id, + 'right_partner_id': partner_local.id}) + self.assertTrue(partner_local.has_partner_above) + # We should be able to find the national company as being above + # the local company. + self.assertEqual(len(partner_local.partner_above_ids[0]), 1) + self.assertEqual( + partner_local.partner_above_ids[0].partner_above_id, + partner_national) + # Let the national company belong to the multinational company: + relation_model.create({ + 'left_partner_id': partner_multinational.id, + 'type_id': type_company2branch.id, + 'right_partner_id': partner_national.id}) + self.env.invalidate_all() + self.assertFalse(partner_multinational.has_partner_above) + self.assertTrue(partner_multinational.is_above(partner_local)) + self.assertEqual( + partner_local.partner_above_hierarchy, + '/'.join([partner_multinational.name, partner_national.name])) + # Check error when trying to create inconsistent hierarchy: + with self.assertRaises(ValidationError): + relation_model.create({ + 'left_partner_id': partner_local.id, + 'type_id': type_company2branch.id, + 'right_partner_id': partner_multinational.id}) diff --git a/partner_multi_relation_hierarchy/views/res_partner.xml b/partner_multi_relation_hierarchy/views/res_partner.xml new file mode 100644 index 000000000..64b838cb4 --- /dev/null +++ b/partner_multi_relation_hierarchy/views/res_partner.xml @@ -0,0 +1,35 @@ + + + + + view.partner.form + + res.partner + + + + + + + + + + + + + + + + + + + + + + + diff --git a/partner_multi_relation_hierarchy/views/res_partner_relation_type.xml b/partner_multi_relation_hierarchy/views/res_partner_relation_type.xml new file mode 100644 index 000000000..772a59951 --- /dev/null +++ b/partner_multi_relation_hierarchy/views/res_partner_relation_type.xml @@ -0,0 +1,17 @@ + + + + + res.partner.relation.type + + + + + + + + +