From 2dc738c62311708f9d250d3e7d2dce1355353fc7 Mon Sep 17 00:00:00 2001 From: Ronald Portier Date: Mon, 6 Jul 2020 19:53:32 +0200 Subject: [PATCH] [MIG] partner_multi_relation. Black, isort, pretty... --- partner_multi_relation/__manifest__.py | 19 +- partner_multi_relation/data/demo.xml | 215 ++++--- partner_multi_relation/models/res_partner.py | 181 +++--- .../models/res_partner_relation.py | 94 ++-- .../models/res_partner_relation_all.py | 329 +++++------ .../models/res_partner_relation_type.py | 190 +++---- .../res_partner_relation_type_selection.py | 79 ++- partner_multi_relation/readme/USAGE.rst | 2 +- .../tests/test_partner_relation.py | 524 ++++++++++-------- .../tests/test_partner_relation_all.py | 327 ++++++----- .../tests/test_partner_relation_common.py | 138 ++--- .../tests/test_partner_search.py | 74 +-- partner_multi_relation/views/menu.xml | 55 +- partner_multi_relation/views/res_partner.xml | 78 ++- .../views/res_partner_relation_all.xml | 174 +++--- .../views/res_partner_relation_type.xml | 95 ++-- 16 files changed, 1347 insertions(+), 1227 deletions(-) diff --git a/partner_multi_relation/__manifest__.py b/partner_multi_relation/__manifest__.py index 638094290..15bc574c9 100644 --- a/partner_multi_relation/__manifest__.py +++ b/partner_multi_relation/__manifest__.py @@ -2,25 +2,20 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { "name": "Partner Relations", - "version": "12.0.1.2.1", + "version": "13.0.1.0.0", "author": "Therp BV,Camptocamp,Odoo Community Association (OCA)", "website": "https://github.com/OCA/partner-contact", "complexity": "normal", "category": "Customer Relationship Management", "license": "AGPL-3", - "depends": [ - 'contacts', - 'sales_team', - ], - "demo": [ - "data/demo.xml", - ], + "depends": ["contacts", "sales_team"], + "demo": ["data/demo.xml"], "data": [ - 'security/ir.model.access.csv', + "security/ir.model.access.csv", "views/res_partner_relation_all.xml", - 'views/res_partner.xml', - 'views/res_partner_relation_type.xml', - 'views/menu.xml', + "views/res_partner.xml", + "views/res_partner_relation_type.xml", + "views/menu.xml", ], "auto_install": False, "installable": True, diff --git a/partner_multi_relation/data/demo.xml b/partner_multi_relation/data/demo.xml index 47318e281..ed48b2dd5 100644 --- a/partner_multi_relation/data/demo.xml +++ b/partner_multi_relation/data/demo.xml @@ -1,116 +1,111 @@ - + - - - - - Is assistant of - Has assistant - p - p - - - Is competitor of - Is competitor of - c - c - - - - Has worked for - Has former employee - p - c - - - - - Washing Companies - - - Washing Gold - - - - Washing Silver - - - - Washing Services - - - - - - Great Washing Powder Company - - 1 - 1 - 1 - Le Bourget du Lac - 73377 - +33 4 49 23 44 54 - - 93, Press Avenue - great@yourcompany.example.com - http://www.great.com - - - Best Washing Powder Company - - 1 - 1 - Champs sur Marne - 77420 - - best@yourcompany.example.com - +33 1 64 61 04 01 - 12 rue Albert Einstein - http://www.best.com/ - - - Super Washing Powder Company - - 1 - - 1 - 3rd Floor, Room 3-C, - Carretera Panamericana, Km 1, Urb. Delgado Chalbaud - Caracas - 1090 - super@yourcompany.example.com - +58 212 681 0538 - - super.com - - - - - - - - - - - - - - - - - - + + + Is assistant of + Has assistant + p + p + + + Is competitor of + Is competitor of + c + c + + + + Has worked for + Has former employee + p + c + + + + Washing Companies + + + Washing Gold + + + + Washing Silver + + + + Washing Services + + + + + Great Washing Powder Company + + 1 + 1 + 1 + Le Bourget du Lac + 73377 + +33 4 49 23 44 54 + + 93, Press Avenue + great@yourcompany.example.com + http://www.great.com + + + Best Washing Powder Company + + 1 + 1 + Champs sur Marne + 77420 + + best@yourcompany.example.com + +33 1 64 61 04 01 + 12 rue Albert Einstein + http://www.best.com/ + + + Super Washing Powder Company + + 1 + + 1 + 3rd Floor, Room 3-C, + Carretera Panamericana, Km 1, Urb. Delgado Chalbaud + Caracas + 1090 + super@yourcompany.example.com + +58 212 681 0538 + + super.com + + + + + + + + + + + + + + + + + diff --git a/partner_multi_relation/models/res_partner.py b/partner_multi_relation/models/res_partner.py index 1b2813d1e..6260fc549 100644 --- a/partner_multi_relation/models/res_partner.py +++ b/partner_multi_relation/models/res_partner.py @@ -4,51 +4,51 @@ import numbers from odoo import _, api, exceptions, fields, models -from odoo.osv.expression import is_leaf, OR, FALSE_LEAF +from odoo.osv.expression import FALSE_LEAF, OR, is_leaf class ResPartner(models.Model): """Extend partner with relations and allow to search for relations in various ways. """ + # pylint: disable=invalid-name # pylint: disable=no-member - _inherit = 'res.partner' + _inherit = "res.partner" relation_count = fields.Integer( - string='Relation Count', - compute="_compute_relation_count" + string="Relation Count", compute="_compute_relation_count" ) relation_all_ids = fields.One2many( - comodel_name='res.partner.relation.all', - inverse_name='this_partner_id', - string='All relations with current partner', + comodel_name="res.partner.relation.all", + inverse_name="this_partner_id", + string="All relations with current partner", auto_join=True, selectable=False, copy=False, ) search_relation_type_id = fields.Many2one( - comodel_name='res.partner.relation.type.selection', + comodel_name="res.partner.relation.type.selection", compute=lambda self: None, - search='_search_relation_type_id', - string='Has relation of type', + search="_search_relation_type_id", + string="Has relation of type", ) search_relation_partner_id = fields.Many2one( - comodel_name='res.partner', + comodel_name="res.partner", compute=lambda self: None, - search='_search_related_partner_id', - string='Has relation with', + search="_search_related_partner_id", + string="Has relation with", ) search_relation_date = fields.Date( compute=lambda self: None, - search='_search_relation_date', - string='Relation valid', + search="_search_relation_date", + string="Relation valid", ) search_relation_partner_category_id = fields.Many2one( - comodel_name='res.partner.category', + comodel_name="res.partner.category", compute=lambda self: None, - search='_search_related_partner_category_id', - string='Has relation with a partner in category', + search="_search_related_partner_category_id", + string="Has relation with a partner in category", ) @api.depends("relation_all_ids") @@ -58,80 +58,78 @@ class ResPartner(models.Model): Don't count inactive relations. """ for rec in self: - rec.relation_count = len(rec.relation_all_ids.filtered('active')) + rec.relation_count = len(rec.relation_all_ids.filtered("active")) @api.model def _search_relation_type_id(self, operator, value): """Search partners based on their type of relations.""" result = [] SUPPORTED_OPERATORS = ( - '=', - '!=', - 'like', - 'not like', - 'ilike', - 'not ilike', - 'in', - 'not in', + "=", + "!=", + "like", + "not like", + "ilike", + "not ilike", + "in", + "not in", ) if operator not in SUPPORTED_OPERATORS: raise exceptions.ValidationError( - _('Unsupported search operator "%s"') % operator) - type_selection_model = self.env['res.partner.relation.type.selection'] + _('Unsupported search operator "%s"') % operator + ) + type_selection_model = self.env["res.partner.relation.type.selection"] relation_type_selection = [] - if operator == '=' and isinstance(value, numbers.Integral): + if operator == "=" and isinstance(value, numbers.Integral): relation_type_selection += type_selection_model.browse(value) - elif operator == '!=' and isinstance(value, numbers.Integral): - relation_type_selection = type_selection_model.search([ - ('id', operator, value), - ]) + elif operator == "!=" and isinstance(value, numbers.Integral): + relation_type_selection = type_selection_model.search( + [("id", operator, value)] + ) else: - relation_type_selection = type_selection_model.search([ - '|', - ('type_id.name', operator, value), - ('type_id.name_inverse', operator, value), - ]) + relation_type_selection = type_selection_model.search( + [ + "|", + ("type_id.name", operator, value), + ("type_id.name_inverse", operator, value), + ] + ) if not relation_type_selection: result = [FALSE_LEAF] for relation_type in relation_type_selection: - result = OR([ - result, + result = OR( [ - ('relation_all_ids.type_selection_id.id', '=', - relation_type.id), - ], - ]) + result, + [("relation_all_ids.type_selection_id.id", "=", relation_type.id)], + ] + ) return result @api.model def _search_related_partner_id(self, operator, value): """Find partner based on relation with other partner.""" # pylint: disable=no-self-use - return [ - ('relation_all_ids.other_partner_id', operator, value), - ] + return [("relation_all_ids.other_partner_id", operator, value)] @api.model def _search_relation_date(self, operator, value): """Look only for relations valid at date of search.""" # pylint: disable=no-self-use return [ - '&', - '|', - ('relation_all_ids.date_start', '=', False), - ('relation_all_ids.date_start', '<=', value), - '|', - ('relation_all_ids.date_end', '=', False), - ('relation_all_ids.date_end', '>=', value), + "&", + "|", + ("relation_all_ids.date_start", "=", False), + ("relation_all_ids.date_start", "<=", value), + "|", + ("relation_all_ids.date_end", "=", False), + ("relation_all_ids.date_end", ">=", value), ] @api.model def _search_related_partner_category_id(self, operator, value): """Search for partner related to a partner with search category.""" # pylint: disable=no-self-use - return [ - ('relation_all_ids.other_partner_id.category_id', operator, value), - ] + return [("relation_all_ids.other_partner_id.category_id", operator, value)] @api.model def search(self, args, offset=0, limit=None, order=None, count=False): @@ -142,26 +140,34 @@ class ResPartner(models.Model): # pylint: disable=no-value-for-parameter date_args = [] for arg in args: - if (is_leaf(arg) and isinstance(arg[0], str) and - arg[0].startswith('search_relation')): - if arg[0] == 'search_relation_date': + if ( + is_leaf(arg) + and isinstance(arg[0], str) + and arg[0].startswith("search_relation") + ): + if arg[0] == "search_relation_date": date_args = [] break if not date_args: - date_args = [ - ('search_relation_date', '=', fields.Date.today()), - ] + date_args = [("search_relation_date", "=", fields.Date.today())] # because of auto_join, we have to do the active test by hand active_args = [] - if self.env.context.get('active_test', True): + if self.env.context.get("active_test", True): for arg in args: - if (is_leaf(arg) and isinstance(arg[0], str) and - arg[0].startswith('search_relation')): - active_args = [('relation_all_ids.active', '=', True)] + if ( + is_leaf(arg) + and isinstance(arg[0], str) + and arg[0].startswith("search_relation") + ): + active_args = [("relation_all_ids.active", "=", True)] break return super(ResPartner, self).search( - args + date_args + active_args, offset=offset, limit=limit, - order=order, count=count) + args + date_args + active_args, + offset=offset, + limit=limit, + order=order, + count=count, + ) @api.multi def get_partner_type(self): @@ -170,28 +176,35 @@ class ResPartner(models.Model): :rtype: str """ self.ensure_one() - return 'c' if self.is_company else 'p' + return "c" if self.is_company else "p" @api.multi def action_view_relations(self): for contact in self: - relation_model = self.env['res.partner.relation.all'] - relation_ids = relation_model.\ - search(['|', - ('this_partner_id', '=', contact.id), - ('other_partner_id', '=', contact.id)]) + relation_model = self.env["res.partner.relation.all"] + relation_ids = relation_model.search( + [ + "|", + ("this_partner_id", "=", contact.id), + ("other_partner_id", "=", contact.id), + ] + ) action = self.env.ref( - 'partner_multi_relation.action_res_partner_relation_all' + "partner_multi_relation.action_res_partner_relation_all" ).read()[0] - action['domain'] = [('id', 'in', relation_ids.ids)] - context = action.get('context', '{}').strip()[1:-1] - elements = context.split(',') if context else [] - to_add = ["""'search_default_this_partner_id': {0}, + action["domain"] = [("id", "in", relation_ids.ids)] + context = action.get("context", "{}").strip()[1:-1] + elements = context.split(",") if context else [] + to_add = [ + """'search_default_this_partner_id': {0}, 'default_this_partner_id': {0}, 'active_model': 'res.partner', 'active_id': {0}, 'active_ids': [{0}], - 'active_test': False""".format(contact.id)] - context = '{' + ', '.join(elements + to_add) + '}' - action['context'] = context + 'active_test': False""".format( + contact.id + ) + ] + context = "{" + ", ".join(elements + to_add) + "}" + action["context"] = context return action diff --git a/partner_multi_relation/models/res_partner_relation.py b/partner_multi_relation/models/res_partner_relation.py index 0907ab202..eaff1373e 100644 --- a/partner_multi_relation/models/res_partner_relation.py +++ b/partner_multi_relation/models/res_partner_relation.py @@ -15,54 +15,58 @@ class ResPartnerRelation(models.Model): two times, once for the normal relation, once for the inverse relation, will be used to maintain the data. """ - _name = 'res.partner.relation' - _description = 'Partner relation' + + _name = "res.partner.relation" + _description = "Partner relation" left_partner_id = fields.Many2one( - comodel_name='res.partner', - string='Source Partner', + comodel_name="res.partner", + string="Source Partner", required=True, auto_join=True, - ondelete='cascade', + ondelete="cascade", ) right_partner_id = fields.Many2one( - comodel_name='res.partner', - string='Destination Partner', + comodel_name="res.partner", + string="Destination Partner", required=True, auto_join=True, - ondelete='cascade', + ondelete="cascade", ) type_id = fields.Many2one( - comodel_name='res.partner.relation.type', - string='Type', + comodel_name="res.partner.relation.type", + string="Type", required=True, auto_join=True, ) - date_start = fields.Date('Starting date') - date_end = fields.Date('Ending date') + date_start = fields.Date("Starting date") + date_end = fields.Date("Ending date") @api.model def create(self, vals): """Override create to correct values, before being stored.""" context = self.env.context - if 'left_partner_id' not in vals and context.get('active_id'): - vals['left_partner_id'] = context.get('active_id') + if "left_partner_id" not in vals and context.get("active_id"): + vals["left_partner_id"] = context.get("active_id") return super(ResPartnerRelation, self).create(vals) - @api.constrains('date_start', 'date_end') + @api.constrains("date_start", "date_end") def _check_dates(self): """End date should not be before start date, if not filled :raises ValidationError: When constraint is violated """ for record in self: - if (record.date_start and record.date_end and - record.date_start > record.date_end): + if ( + record.date_start + and record.date_end + and record.date_start > record.date_end + ): raise ValidationError( - _('The starting date cannot be after the ending date.') + _("The starting date cannot be after the ending date.") ) - @api.constrains('left_partner_id', 'type_id') + @api.constrains("left_partner_id", "type_id") def _check_partner_left(self): """Check left partner for required company or person @@ -70,7 +74,7 @@ class ResPartnerRelation(models.Model): """ self._check_partner("left") - @api.constrains('right_partner_id', 'type_id') + @api.constrains("right_partner_id", "type_id") def _check_partner_right(self): """Check right partner for required company or person @@ -86,23 +90,24 @@ class ResPartnerRelation(models.Model): :raises ValidationError: When constraint is violated """ for record in self: - assert side in ['left', 'right'] + assert side in ["left", "right"] ptype = getattr(record.type_id, "contact_type_%s" % side) - partner = getattr(record, '%s_partner_id' % side) - if ((ptype == 'c' and not partner.is_company) or - (ptype == 'p' and partner.is_company)): + partner = getattr(record, "%s_partner_id" % side) + if (ptype == "c" and not partner.is_company) or ( + ptype == "p" and partner.is_company + ): raise ValidationError( - _('The %s partner is not applicable for this ' - 'relation type.') % side + _("The %s partner is not applicable for this " "relation type.") + % side ) category = getattr(record.type_id, "partner_category_%s" % side) if category and category.id not in partner.category_id.ids: raise ValidationError( - _('The %s partner does not have category %s.') % - (side, category.name) + _("The %s partner does not have category %s.") + % (side, category.name) ) - @api.constrains('left_partner_id', 'right_partner_id') + @api.constrains("left_partner_id", "right_partner_id") def _check_not_with_self(self): """Not allowed to link partner to same partner @@ -112,15 +117,11 @@ class ResPartnerRelation(models.Model): if record.left_partner_id == record.right_partner_id: if not (record.type_id and record.type_id.allow_self): raise ValidationError( - _('Partners cannot have a relation with themselves.') + _("Partners cannot have a relation with themselves.") ) @api.constrains( - 'left_partner_id', - 'type_id', - 'right_partner_id', - 'date_start', - 'date_end', + "left_partner_id", "type_id", "right_partner_id", "date_start", "date_end" ) def _check_relation_uniqueness(self): """Forbid multiple active relations of the same type between the same @@ -132,25 +133,24 @@ class ResPartnerRelation(models.Model): # pylint: disable=no-value-for-parameter for record in self: domain = [ - ('type_id', '=', record.type_id.id), - ('id', '!=', record.id), - ('left_partner_id', '=', record.left_partner_id.id), - ('right_partner_id', '=', record.right_partner_id.id), + ("type_id", "=", record.type_id.id), + ("id", "!=", record.id), + ("left_partner_id", "=", record.left_partner_id.id), + ("right_partner_id", "=", record.right_partner_id.id), ] if record.date_start: domain += [ - '|', - ('date_end', '=', False), - ('date_end', '>=', record.date_start), + "|", + ("date_end", "=", False), + ("date_end", ">=", record.date_start), ] if record.date_end: domain += [ - '|', - ('date_start', '=', False), - ('date_start', '<=', record.date_end), + "|", + ("date_start", "=", False), + ("date_start", "<=", record.date_end), ] if record.search(domain): raise ValidationError( - _('There is already a similar relation with ' - 'overlapping dates') + _("There is already a similar relation with " "overlapping dates") ) diff --git a/partner_multi_relation/models/res_partner_relation_all.py b/partner_multi_relation/models/res_partner_relation_all.py index 496a60b27..eaac88969 100644 --- a/partner_multi_relation/models/res_partner_relation_all.py +++ b/partner_multi_relation/models/res_partner_relation_all.py @@ -10,7 +10,6 @@ from odoo import _, api, fields, models from odoo.exceptions import MissingError, ValidationError from odoo.tools import drop_view_if_exists - _logger = logging.getLogger(__name__) @@ -47,97 +46,100 @@ FROM res_partner_relation rel""" class ResPartnerRelationAll(models.AbstractModel): """Abstract model to show each relation from two sides.""" + _auto = False _log_access = False - _name = 'res.partner.relation.all' - _description = 'All (non-inverse + inverse) relations between partners' - _order = \ - 'this_partner_id, type_selection_id, date_end desc, date_start desc' + _name = "res.partner.relation.all" + _description = "All (non-inverse + inverse) relations between partners" + _order = "this_partner_id, type_selection_id, date_end desc, date_start desc" res_model = fields.Char( - string='Resource Model', + string="Resource Model", readonly=True, required=True, - help="The database object this relation is based on.") + help="The database object this relation is based on.", + ) res_id = fields.Integer( - string='Resource ID', + string="Resource ID", readonly=True, required=True, - help="The id of the object in the model this relation is based on.") + help="The id of the object in the model this relation is based on.", + ) this_partner_id = fields.Many2one( - comodel_name='res.partner', - string='One Partner', - required=True) + comodel_name="res.partner", string="One Partner", required=True + ) other_partner_id = fields.Many2one( - comodel_name='res.partner', - string='Other Partner', - required=True) + comodel_name="res.partner", string="Other Partner", required=True + ) type_id = fields.Many2one( - comodel_name='res.partner.relation.type', - string='Underlying Relation Type', + comodel_name="res.partner.relation.type", + string="Underlying Relation Type", readonly=True, - required=True) - date_start = fields.Date('Starting date') - date_end = fields.Date('Ending date') + required=True, + ) + date_start = fields.Date("Starting date") + date_end = fields.Date("Ending date") is_inverse = fields.Boolean( string="Is reverse type?", readonly=True, - help="Inverse relations are from right to left partner.") + help="Inverse relations are from right to left partner.", + ) type_selection_id = fields.Many2one( - comodel_name='res.partner.relation.type.selection', - string='Relation Type', - required=True) + comodel_name="res.partner.relation.type.selection", + string="Relation Type", + required=True, + ) active = fields.Boolean( - string='Active', + string="Active", readonly=True, - help="Records with date_end in the past are inactive") + help="Records with date_end in the past are inactive", + ) any_partner_id = fields.Many2many( - comodel_name='res.partner', - string='Partner', + comodel_name="res.partner", + string="Partner", compute=lambda self: None, - search='_search_any_partner_id') + search="_search_any_partner_id", + ) - def register_specification( - self, register, base_name, is_inverse, select_sql): - _last_key_offset = register['_lastkey'] - key_name = base_name + (is_inverse and '_inverse' or '') + def register_specification(self, register, base_name, is_inverse, select_sql): + _last_key_offset = register["_lastkey"] + key_name = base_name + (is_inverse and "_inverse" or "") assert key_name not in register - assert '%%(padding)s' in select_sql - assert '%(key_offset)s' in select_sql - assert '%(is_inverse)s' in select_sql + assert "%%(padding)s" in select_sql + assert "%(key_offset)s" in select_sql + assert "%(is_inverse)s" in select_sql _last_key_offset += 1 - register['_lastkey'] = _last_key_offset + register["_lastkey"] = _last_key_offset register[key_name] = dict( base_name=base_name, is_inverse=is_inverse, key_offset=_last_key_offset, - select_sql=select_sql % { - 'key_offset': _last_key_offset, - 'is_inverse': is_inverse, - 'extra_additional_columns': - self._get_additional_relation_columns(), - }) + select_sql=select_sql + % { + "key_offset": _last_key_offset, + "is_inverse": is_inverse, + "extra_additional_columns": self._get_additional_relation_columns(), + }, + ) def get_register(self): register = collections.OrderedDict() - register['_lastkey'] = -1 - self.register_specification( - register, 'relation', False, RELATIONS_SQL) - self.register_specification( - register, 'relation', True, RELATIONS_SQL_INVERSE) + register["_lastkey"] = -1 + self.register_specification(register, "relation", False, RELATIONS_SQL) + self.register_specification(register, "relation", True, RELATIONS_SQL_INVERSE) return register def get_select_specification(self, base_name, is_inverse): register = self.get_register() - key_name = base_name + (is_inverse and '_inverse' or '') + key_name = base_name + (is_inverse and "_inverse" or "") return register[key_name] def _get_statement(self): """Allow other modules to add to statement.""" register = self.get_register() - union_select = ' UNION '.join( - [register[key]['select_sql'] - for key in register if key != '_lastkey']) + union_select = " UNION ".join( + [register[key]["select_sql"] for key in register if key != "_lastkey"] + ) return """\ CREATE OR REPLACE VIEW %%(table)s AS WITH base_selection AS (%(union_select)s) @@ -153,7 +155,9 @@ CREATE OR REPLACE VIEW %%(table)s AS FROM base_selection bas JOIN res_partner_relation_type typ ON (bas.type_id = typ.id) %%(additional_tables)s - """ % {'union_select': union_select} + """ % { + "union_select": union_select + } def _get_padding(self): """Utility function to define padding in one place.""" @@ -167,7 +171,7 @@ CREATE OR REPLACE VIEW %%(table)s AS :return: ', rel.column_a, rel.column_b_id' """ - return '' + return "" def _get_additional_view_fields(self): """Allow inherit models to add fields to view. @@ -176,7 +180,7 @@ CREATE OR REPLACE VIEW %%(table)s AS prepended by a comma, like so: return ', typ.allow_self, typ.left_partner_category' """ - return '' + return "" def _get_additional_tables(self): """Allow inherit models to add tables (JOIN's) to view. @@ -184,7 +188,7 @@ CREATE OR REPLACE VIEW %%(table)s AS Example: return 'JOIN type_extention ext ON (bas.type_id = ext.id)' """ - return '' + return "" @api.model_cr_context def _auto_init(self): @@ -192,12 +196,13 @@ CREATE OR REPLACE VIEW %%(table)s AS drop_view_if_exists(cr, self._table) cr.execute( self._get_statement(), - {'table': AsIs(self._table), - 'padding': self._get_padding(), - 'additional_view_fields': - AsIs(self._get_additional_view_fields()), - 'additional_tables': - AsIs(self._get_additional_tables())}) + { + "table": AsIs(self._table), + "padding": self._get_padding(), + "additional_view_fields": AsIs(self._get_additional_view_fields()), + "additional_tables": AsIs(self._get_additional_tables()), + }, + ) return super(ResPartnerRelationAll, self)._auto_init() @api.model @@ -205,20 +210,24 @@ CREATE OR REPLACE VIEW %%(table)s AS """Search relation with partner, no matter on which side.""" # pylint: disable=no-self-use return [ - '|', - ('this_partner_id', operator, value), - ('other_partner_id', operator, value)] + "|", + ("this_partner_id", operator, value), + ("other_partner_id", operator, value), + ] @api.multi def name_get(self): return { - this.id: '%s %s %s' % ( + this.id: "%s %s %s" + % ( this.this_partner_id.name, this.type_selection_id.display_name, this.other_partner_id.name, - ) for this in self} + ) + for this in self + } - @api.onchange('type_selection_id') + @api.onchange("type_selection_id") def onchange_type_selection_id(self): """Add domain on partners according to category and contact_type.""" @@ -228,72 +237,74 @@ CREATE OR REPLACE VIEW %%(table)s AS """ warning = {} if partner: - test_domain = [('id', '=', partner.id)] + partner_domain + test_domain = [("id", "=", partner.id)] + partner_domain else: test_domain = partner_domain - partner_model = self.env['res.partner'] + partner_model = self.env["res.partner"] partners_found = partner_model.search(test_domain, limit=1) if not partners_found: - warning['title'] = _('Error!') + warning["title"] = _("Error!") if partner: - warning['message'] = ( - _('%s partner incompatible with relation type.') % - side.title()) + warning["message"] = ( + _("%s partner incompatible with relation type.") % side.title() + ) else: - warning['message'] = ( - _('No %s partner available for relation type.') % - side) + warning["message"] = ( + _("No %s partner available for relation type.") % side + ) return warning this_partner_domain = [] other_partner_domain = [] if self.type_selection_id.contact_type_this: - this_partner_domain.append(( - 'is_company', '=', - self.type_selection_id.contact_type_this == 'c')) + this_partner_domain.append( + ("is_company", "=", self.type_selection_id.contact_type_this == "c") + ) if self.type_selection_id.partner_category_this: - this_partner_domain.append(( - 'category_id', 'in', - self.type_selection_id.partner_category_this.ids)) + this_partner_domain.append( + ("category_id", "in", self.type_selection_id.partner_category_this.ids) + ) if self.type_selection_id.contact_type_other: - other_partner_domain.append(( - 'is_company', '=', - self.type_selection_id.contact_type_other == 'c')) + other_partner_domain.append( + ("is_company", "=", self.type_selection_id.contact_type_other == "c") + ) if self.type_selection_id.partner_category_other: - other_partner_domain.append(( - 'category_id', 'in', - self.type_selection_id.partner_category_other.ids)) - result = {'domain': { - 'this_partner_id': this_partner_domain, - 'other_partner_id': other_partner_domain}} + other_partner_domain.append( + ("category_id", "in", self.type_selection_id.partner_category_other.ids) + ) + result = { + "domain": { + "this_partner_id": this_partner_domain, + "other_partner_id": other_partner_domain, + } + } # Check wether domain results in no choice or wrong choice of partners: warning = {} - partner_model = self.env['res.partner'] + partner_model = self.env["res.partner"] if this_partner_domain: this_partner = False if bool(self.this_partner_id.id): this_partner = self.this_partner_id else: - this_partner_id = \ - 'default_this_partner_id' in self.env.context and \ - self.env.context['default_this_partner_id'] or \ - 'active_id' in self.env.context and \ - self.env.context['active_id'] or \ - False + this_partner_id = ( + "default_this_partner_id" in self.env.context + and self.env.context["default_this_partner_id"] + or "active_id" in self.env.context + and self.env.context["active_id"] + or False + ) if this_partner_id: this_partner = partner_model.browse(this_partner_id) - warning = check_partner_domain( - this_partner, this_partner_domain, _('this')) + warning = check_partner_domain(this_partner, this_partner_domain, _("this")) if not warning and other_partner_domain: warning = check_partner_domain( - self.other_partner_id, other_partner_domain, _('other')) + self.other_partner_id, other_partner_domain, _("other") + ) if warning: - result['warning'] = warning + result["warning"] = warning return result - @api.onchange( - 'this_partner_id', - 'other_partner_id') + @api.onchange("this_partner_id", "other_partner_id") def onchange_partner_id(self): """Set domain on type_selection_id based on partner(s) selected.""" @@ -306,69 +317,68 @@ CREATE OR REPLACE VIEW %%(table)s AS warning = {} if not (type_selection_domain and self.type_selection_id): return warning - test_domain = ( - [('id', '=', self.type_selection_id.id)] + - type_selection_domain) - type_model = self.env['res.partner.relation.type.selection'] + test_domain = [ + ("id", "=", self.type_selection_id.id) + ] + type_selection_domain + type_model = self.env["res.partner.relation.type.selection"] types_found = type_model.search(test_domain, limit=1) if not types_found: - warning['title'] = _('Error!') - warning['message'] = _( - 'Relation type incompatible with selected partner(s).') + warning["title"] = _("Error!") + warning["message"] = _( + "Relation type incompatible with selected partner(s)." + ) return warning type_selection_domain = [] if self.this_partner_id: type_selection_domain += [ - '|', - ('contact_type_this', '=', False), - ('contact_type_this', '=', - self.this_partner_id.get_partner_type()), - '|', - ('partner_category_this', '=', False), - ('partner_category_this', 'in', - self.this_partner_id.category_id.ids)] + "|", + ("contact_type_this", "=", False), + ("contact_type_this", "=", self.this_partner_id.get_partner_type()), + "|", + ("partner_category_this", "=", False), + ("partner_category_this", "in", self.this_partner_id.category_id.ids), + ] if self.other_partner_id: type_selection_domain += [ - '|', - ('contact_type_other', '=', False), - ('contact_type_other', '=', - self.other_partner_id.get_partner_type()), - '|', - ('partner_category_other', '=', False), - ('partner_category_other', 'in', - self.other_partner_id.category_id.ids)] - result = {'domain': { - 'type_selection_id': type_selection_domain}} + "|", + ("contact_type_other", "=", False), + ("contact_type_other", "=", self.other_partner_id.get_partner_type()), + "|", + ("partner_category_other", "=", False), + ("partner_category_other", "in", self.other_partner_id.category_id.ids), + ] + result = {"domain": {"type_selection_id": type_selection_domain}} # Check wether domain results in no choice or wrong choice for # type_selection_id: warning = check_type_selection_domain(type_selection_domain) if warning: - result['warning'] = warning + result["warning"] = warning return result @api.model def _correct_vals(self, vals, type_selection): """Fill left and right partner from this and other partner.""" vals = vals.copy() - if 'type_selection_id' in vals: - vals['type_id'] = type_selection.type_id.id + if "type_selection_id" in vals: + vals["type_id"] = type_selection.type_id.id if type_selection.is_inverse: - if 'this_partner_id' in vals: - vals['right_partner_id'] = vals['this_partner_id'] - if 'other_partner_id' in vals: - vals['left_partner_id'] = vals['other_partner_id'] + if "this_partner_id" in vals: + vals["right_partner_id"] = vals["this_partner_id"] + if "other_partner_id" in vals: + vals["left_partner_id"] = vals["other_partner_id"] else: - if 'this_partner_id' in vals: - vals['left_partner_id'] = vals['this_partner_id'] - if 'other_partner_id' in vals: - vals['right_partner_id'] = vals['other_partner_id'] + if "this_partner_id" in vals: + vals["left_partner_id"] = vals["this_partner_id"] + if "other_partner_id" in vals: + vals["right_partner_id"] = vals["other_partner_id"] # Delete values not in underlying table: for key in ( - 'this_partner_id', - 'type_selection_id', - 'other_partner_id', - 'is_inverse'): + "this_partner_id", + "type_selection_id", + "other_partner_id", + "is_inverse", + ): if key in vals: del vals[key] return vals @@ -386,7 +396,7 @@ CREATE OR REPLACE VIEW %%(table)s AS self.ensure_one() # write for models other then res.partner.relation SHOULD # be handled in inherited models: - relation_model = self.env['res.partner.relation'] + relation_model = self.env["res.partner.relation"] assert self.res_model == relation_model._name base_resource.write(vals) @@ -394,14 +404,17 @@ CREATE OR REPLACE VIEW %%(table)s AS def _get_type_selection_from_vals(self, vals): """Get type_selection_id straight from vals or compute from type_id. """ - type_selection_id = vals.get('type_selection_id', False) + type_selection_id = vals.get("type_selection_id", False) if not type_selection_id: - type_id = vals.get('type_id', False) + type_id = vals.get("type_id", False) if type_id: - is_inverse = vals.get('is_inverse') + is_inverse = vals.get("is_inverse") type_selection_id = type_id * 2 + (is_inverse and 1 or 0) - return type_selection_id and self.type_selection_id.browse( - type_selection_id) or False + return ( + type_selection_id + and self.type_selection_id.browse(type_selection_id) + or False + ) @api.multi def write(self, vals): @@ -421,19 +434,20 @@ CREATE OR REPLACE VIEW %%(table)s AS @api.model def _compute_base_name(self, type_selection): """This will be overridden for each inherit model.""" - return 'relation' + return "relation" @api.model def _compute_id(self, base_resource, type_selection): """Compute id. Allow for enhancements in inherit model.""" base_name = self._compute_base_name(type_selection) key_offset = self.get_select_specification( - base_name, type_selection.is_inverse)['key_offset'] + base_name, type_selection.is_inverse + )["key_offset"] return base_resource.id * self._get_padding() + key_offset @api.model def create_resource(self, vals, type_selection): - relation_model = self.env['res.partner.relation'] + relation_model = self.env["res.partner.relation"] return relation_model.create(vals) @api.model @@ -444,8 +458,7 @@ CREATE OR REPLACE VIEW %%(table)s AS """ type_selection = self._get_type_selection_from_vals(vals) if not type_selection: # Should not happen - raise ValidationError( - _('No relation type specified in vals: %s.') % vals) + raise ValidationError(_("No relation type specified in vals: %s.") % vals) vals = self._correct_vals(vals, type_selection) base_resource = self.create_resource(vals, type_selection) res_id = self._compute_id(base_resource, type_selection) @@ -457,7 +470,7 @@ CREATE OR REPLACE VIEW %%(table)s AS self.ensure_one() # unlink for models other then res.partner.relation SHOULD # be handled in inherited models: - relation_model = self.env['res.partner.relation'] + relation_model = self.env["res.partner.relation"] assert self.res_model == relation_model._name base_resource.unlink() diff --git a/partner_multi_relation/models/res_partner_relation_type.py b/partner_multi_relation/models/res_partner_relation_type.py index dbcfd2570..31080f70a 100644 --- a/partner_multi_relation/models/res_partner_relation_type.py +++ b/partner_multi_relation/models/res_partner_relation_type.py @@ -5,83 +5,62 @@ from odoo import _, api, fields, models from odoo.exceptions import ValidationError from odoo.osv.expression import AND, OR - HANDLE_INVALID_ONCHANGE = [ - ('restrict', - _('Do not allow change that will result in invalid relations')), - ('ignore', - _('Allow existing relations that do not fit changed conditions')), - ('end', - _('End relations per today, if they do not fit changed conditions')), - ('delete', - _('Delete relations that do not fit changed conditions')), + ("restrict", _("Do not allow change that will result in invalid relations")), + ("ignore", _("Allow existing relations that do not fit changed conditions")), + ("end", _("End relations per today, if they do not fit changed conditions")), + ("delete", _("Delete relations that do not fit changed conditions")), ] class ResPartnerRelationType(models.Model): """Model that defines relation types that might exist between partners""" - _name = 'res.partner.relation.type' - _description = 'Partner Relation Type' - _order = 'name' - name = fields.Char( - string='Name', - required=True, - translate=True, - ) - name_inverse = fields.Char( - string='Inverse name', - required=True, - translate=True, - ) + _name = "res.partner.relation.type" + _description = "Partner Relation Type" + _order = "name" + + name = fields.Char(string="Name", required=True, translate=True) + name_inverse = fields.Char(string="Inverse name", required=True, translate=True) contact_type_left = fields.Selection( - selection='get_partner_types', - string='Left partner type', + selection="get_partner_types", string="Left partner type" ) contact_type_right = fields.Selection( - selection='get_partner_types', - string='Right partner type', + selection="get_partner_types", string="Right partner type" ) partner_category_left = fields.Many2one( - comodel_name='res.partner.category', - string='Left partner category', + comodel_name="res.partner.category", string="Left partner category" ) partner_category_right = fields.Many2one( - comodel_name='res.partner.category', - string='Right partner category', + comodel_name="res.partner.category", string="Right partner category" ) allow_self = fields.Boolean( - string='Reflexive', - help='This relation can be set up with the same partner left and ' - 'right', + string="Reflexive", + help="This relation can be set up with the same partner left and " "right", default=False, ) is_symmetric = fields.Boolean( - string='Symmetric', - help="This relation is the same from right to left as from left to" - " right", + string="Symmetric", + help="This relation is the same from right to left as from left to" " right", default=False, ) handle_invalid_onchange = fields.Selection( selection=HANDLE_INVALID_ONCHANGE, - string='Invalid relation handling', + string="Invalid relation handling", required=True, - default='restrict', + default="restrict", help="When adding relations criteria like partner type and category" - " are checked.\n" - "However when you change the criteria, there might be relations" - " that do not fit the new criteria.\n" - "Specify how this situation should be handled.", + " are checked.\n" + "However when you change the criteria, there might be relations" + " that do not fit the new criteria.\n" + "Specify how this situation should be handled.", ) @api.model def get_partner_types(self): """A partner can be an organisation or an individual.""" # pylint: disable=no-self-use - return [ - ('c', _('Organisation')), - ('p', _('Person')), - ] + return [("c", _("Organisation")), ("p", _("Person"))] @api.model def _end_active_relations(self, relations): @@ -102,70 +81,68 @@ class ResPartnerRelationType(models.Model): relation.unlink() elif not relation.date_end or relation.date_end > today: - relation.write({'date_end': today}) + relation.write({"date_end": today}) @api.multi def check_existing(self, vals): """Check wether records exist that do not fit new criteria.""" - relation_model = self.env['res.partner.relation'] + relation_model = self.env["res.partner.relation"] def get_type_condition(vals, side): """Add if needed check for contact type.""" - fieldname1 = 'contact_type_%s' % side - fieldname2 = '%s_partner_id.is_company' % side + fieldname1 = "contact_type_%s" % side + fieldname2 = "%s_partner_id.is_company" % side contact_type = fieldname1 in vals and vals[fieldname1] or False - if contact_type == 'c': + if contact_type == "c": # Records that are not companies are invalid: - return [(fieldname2, '=', False)] - if contact_type == 'p': + return [(fieldname2, "=", False)] + if contact_type == "p": # Records that are companies are invalid: - return [(fieldname2, '=', True)] + return [(fieldname2, "=", True)] return [] def get_category_condition(vals, side): """Add if needed check for partner category.""" - fieldname1 = 'partner_category_%s' % side - fieldname2 = '%s_partner_id.category_id' % side + fieldname1 = "partner_category_%s" % side + fieldname2 = "%s_partner_id.category_id" % side category_id = fieldname1 in vals and vals[fieldname1] or False if category_id: # Records that do not have the specified category are invalid: - return [(fieldname2, 'not in', [category_id])] + return [(fieldname2, "not in", [category_id])] return [] for this in self: handling = ( - 'handle_invalid_onchange' in vals and - vals['handle_invalid_onchange'] or - this.handle_invalid_onchange + "handle_invalid_onchange" in vals + and vals["handle_invalid_onchange"] + or this.handle_invalid_onchange ) - if handling == 'ignore': + if handling == "ignore": continue invalid_conditions = [] - for side in ['left', 'right']: - invalid_conditions = OR([ - invalid_conditions, - get_type_condition(vals, side), - ]) - invalid_conditions = OR([ - invalid_conditions, - get_category_condition(vals, side), - ]) + for side in ["left", "right"]: + invalid_conditions = OR( + [invalid_conditions, get_type_condition(vals, side)] + ) + invalid_conditions = OR( + [invalid_conditions, get_category_condition(vals, side)] + ) if not invalid_conditions: return # only look at relations for this type - invalid_domain = AND([ - [('type_id', '=', this.id)], invalid_conditions - ]) - invalid_relations = relation_model.with_context( - active_test=False - ).search(invalid_domain) + invalid_domain = AND([[("type_id", "=", this.id)], invalid_conditions]) + invalid_relations = relation_model.with_context(active_test=False).search( + invalid_domain + ) if invalid_relations: - if handling == 'restrict': + if handling == "restrict": raise ValidationError( - _('There are already relations not satisfying the' - ' conditions for partner type or category.') + _( + "There are already relations not satisfying the" + " conditions for partner type or category." + ) ) - elif handling == 'delete': + elif handling == "delete": invalid_relations.unlink() else: self._end_active_relations(invalid_relations) @@ -180,12 +157,11 @@ class ResPartnerRelationType(models.Model): SELECT id FROM res_partner_relation WHERE left_partner_id = right_partner_id AND type_id = %(relation_type_id)s - """, { - 'relation_type_id': self.id, - } + """, + {"relation_type_id": self.id}, ) reflexive_relation_ids = [r[0] for r in self.env.cr.fetchall()] - return self.env['res.partner.relation'].browse(reflexive_relation_ids) + return self.env["res.partner.relation"].browse(reflexive_relation_ids) def _check_no_existing_reflexive_relations(self): """Check that no reflexive relation exists for these relation types.""" @@ -193,13 +169,16 @@ class ResPartnerRelationType(models.Model): relations = relation_type._get_reflexive_relations() if relations: raise ValidationError( - _("Reflexivity could not be disabled for the relation " - "type {relation_type}. There are existing reflexive " - "relations defined for the following partners: " - "{partners}").format( + _( + "Reflexivity could not be disabled for the relation " + "type {relation_type}. There are existing reflexive " + "relations defined for the following partners: " + "{partners}" + ).format( relation_type=relation_type.display_name, - partners=relations.mapped( - 'left_partner_id.display_name'))) + partners=relations.mapped("left_partner_id.display_name"), + ) + ) def _delete_existing_reflexive_relations(self): """Delete existing reflexive relations for these relation types.""" @@ -216,15 +195,16 @@ class ResPartnerRelationType(models.Model): def _handle_deactivation_of_allow_self(self): """Handle the deactivation of reflexivity on these relations types.""" restrict_relation_types = self.filtered( - lambda t: t.handle_invalid_onchange == 'restrict') + lambda t: t.handle_invalid_onchange == "restrict" + ) restrict_relation_types._check_no_existing_reflexive_relations() delete_relation_types = self.filtered( - lambda t: t.handle_invalid_onchange == 'delete') + lambda t: t.handle_invalid_onchange == "delete" + ) delete_relation_types._delete_existing_reflexive_relations() - end_relation_types = self.filtered( - lambda t: t.handle_invalid_onchange == 'end') + end_relation_types = self.filtered(lambda t: t.handle_invalid_onchange == "end") end_relation_types._end_active_reflexive_relations() @api.multi @@ -235,19 +215,19 @@ class ResPartnerRelationType(models.Model): replaced by the values of the fields whose names end in `_left`. """ - vals['name_inverse'] = vals.get('name', self.name) + vals["name_inverse"] = vals.get("name", self.name) # For all left keys in model, take value for right either from # left key in vals, or if not present, from right key in self: - left_keys = [key for key in self._fields if key.endswith('_left')] + left_keys = [key for key in self._fields if key.endswith("_left")] for left_key in left_keys: - right_key = left_key.replace('_left', '_right') + right_key = left_key.replace("_left", "_right") vals[right_key] = vals.get(left_key, self[left_key]) - if hasattr(vals[right_key], 'id'): + if hasattr(vals[right_key], "id"): vals[right_key] = vals[right_key].id @api.model def create(self, vals): - if vals.get('is_symmetric'): + if vals.get("is_symmetric"): self._update_right_vals(vals) return super(ResPartnerRelationType, self).create(vals) @@ -258,11 +238,11 @@ class ResPartnerRelationType(models.Model): for rec in self: rec_vals = vals.copy() - if rec_vals.get('is_symmetric', rec.is_symmetric): + if rec_vals.get("is_symmetric", rec.is_symmetric): self._update_right_vals(rec_vals) super(ResPartnerRelationType, rec).write(rec_vals) - allow_self_disabled = 'allow_self' in vals and not vals['allow_self'] + allow_self_disabled = "allow_self" in vals and not vals["allow_self"] if allow_self_disabled: self._handle_deactivation_of_allow_self() @@ -274,13 +254,11 @@ class ResPartnerRelationType(models.Model): Relations can be deleted if relation type allows it. """ - relation_model = self.env['res.partner.relation'] + relation_model = self.env["res.partner.relation"] for rec in self: - if rec.handle_invalid_onchange == 'delete': + if rec.handle_invalid_onchange == "delete": # Automatically delete relations, so existing relations # do not prevent unlink of relation type: - relations = relation_model.search([ - ('type_id', '=', rec.id), - ]) + relations = relation_model.search([("type_id", "=", rec.id)]) relations.unlink() return super(ResPartnerRelationType, self).unlink() diff --git a/partner_multi_relation/models/res_partner_relation_type_selection.py b/partner_multi_relation/models/res_partner_relation_type_selection.py index 599988ac2..c6b54e786 100644 --- a/partner_multi_relation/models/res_partner_relation_type_selection.py +++ b/partner_multi_relation/models/res_partner_relation_type_selection.py @@ -20,51 +20,41 @@ from odoo.tools import drop_view_if_exists class ResPartnerRelationTypeSelection(models.Model): """Virtual relation types""" - _name = 'res.partner.relation.type.selection' - _description = 'All relation types' + + _name = "res.partner.relation.type.selection" + _description = "All relation types" _auto = False # Do not try to create table in _auto_init(..) _foreign_keys = [] _log_access = False - _order = 'name asc' + _order = "name asc" @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'] + rprt_model = self.env["res.partner.relation.type"] return rprt_model.get_partner_types() - type_id = fields.Many2one( - comodel_name='res.partner.relation.type', - string='Type', - ) - name = fields.Char('Name') + type_id = fields.Many2one(comodel_name="res.partner.relation.type", string="Type") + name = fields.Char("Name") contact_type_this = fields.Selection( - selection='get_partner_types', - string='Current record\'s partner type', + selection="get_partner_types", string="Current record's partner type" ) is_inverse = fields.Boolean( string="Is reverse type?", help="Inverse relations are from right to left partner.", ) contact_type_other = fields.Selection( - selection='get_partner_types', - string='Other record\'s partner type', + selection="get_partner_types", string="Other record's partner type" ) partner_category_this = fields.Many2one( - comodel_name='res.partner.category', - string='Current record\'s category', + comodel_name="res.partner.category", string="Current record's category" ) partner_category_other = fields.Many2one( - comodel_name='res.partner.category', - string='Other record\'s category', - ) - allow_self = fields.Boolean( - string='Reflexive', - ) - is_symmetric = fields.Boolean( - string='Symmetric', + comodel_name="res.partner.category", string="Other record's category" ) + allow_self = fields.Boolean(string="Reflexive") + is_symmetric = fields.Boolean(string="Symmetric") def _get_additional_view_fields(self): """Allow inherit models to add fields to view. @@ -73,7 +63,7 @@ class ResPartnerRelationTypeSelection(models.Model): prepended by a comma, like so: return ', typ.allow_self, typ.left_partner_category' """ - return '' + return "" def _get_additional_tables(self): """Allow inherit models to add tables (JOIN's) to view. @@ -81,7 +71,7 @@ class ResPartnerRelationTypeSelection(models.Model): Example: return 'JOIN type_extention ext ON (bas.type_id = ext.id)' """ - return '' + return "" @api.model_cr_context def _auto_init(self): @@ -122,29 +112,38 @@ CREATE OR REPLACE VIEW %(table)s AS JOIN res_partner_relation_type typ ON (bas.type_id = typ.id) %(additional_tables)s """, - {'table': AsIs(self._table), - 'underlying_table': AsIs('res_partner_relation_type'), - 'additional_view_fields': - AsIs(self._get_additional_view_fields()), - 'additional_tables': - AsIs(self._get_additional_tables())}) + { + "table": AsIs(self._table), + "underlying_table": AsIs("res_partner_relation_type"), + "additional_view_fields": AsIs(self._get_additional_view_fields()), + "additional_tables": AsIs(self._get_additional_tables()), + }, + ) return super(ResPartnerRelationTypeSelection, self)._auto_init() @api.multi def name_get(self): """Get name or name_inverse from underlying model.""" return [ - (this.id, - this.is_inverse and this.type_id.name_inverse or - this.type_id.display_name) - for this in self] + ( + this.id, + this.is_inverse + and this.type_id.name_inverse + or this.type_id.display_name, + ) + for this in self + ] @api.model - def name_search(self, name='', args=None, operator='ilike', limit=100): + def name_search(self, name="", args=None, operator="ilike", limit=100): """Search for name or inverse name in underlying model.""" # pylint: disable=no-value-for-parameter return self.search( - ['|', - ('type_id.name', operator, name), - ('type_id.name_inverse', operator, name)] + (args or []), - limit=limit).name_get() + [ + "|", + ("type_id.name", operator, name), + ("type_id.name_inverse", operator, name), + ] + + (args or []), + limit=limit, + ).name_get() diff --git a/partner_multi_relation/readme/USAGE.rst b/partner_multi_relation/readme/USAGE.rst index 1a377044c..2d03bce23 100644 --- a/partner_multi_relation/readme/USAGE.rst +++ b/partner_multi_relation/readme/USAGE.rst @@ -52,7 +52,7 @@ Symmetric A symetric relation has the same value for the left and right sides. -For example, in a competitor relation, both companies are competitors of each other. +For example, in a competitor relation, both companies are competitors of each other. .. image:: https://raw.githubusercontent.com/OCA/partner-contact/12.0/partner_multi_relation/static/description/relation_type_symmetric.png diff --git a/partner_multi_relation/tests/test_partner_relation.py b/partner_multi_relation/tests/test_partner_relation.py index 6eed95d01..3b98d8e6f 100644 --- a/partner_multi_relation/tests/test_partner_relation.py +++ b/partner_multi_relation/tests/test_partner_relation.py @@ -1,6 +1,7 @@ # Copyright 2016-2017 Therp BV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from datetime import datetime, date, timedelta +from datetime import date, datetime, timedelta + from dateutil.relativedelta import relativedelta from odoo import fields @@ -16,25 +17,33 @@ class TestPartnerRelation(TestPartnerRelationCommon): def test_selection_name_search(self): """Test wether we can find type selection on reverse name.""" selection_types = self.selection_model.name_search( - name=self.selection_person2company.name) + name=self.selection_person2company.name + ) self.assertTrue(selection_types) self.assertTrue( - (self.selection_person2company.id, - self.selection_person2company.name) in selection_types) + (self.selection_person2company.id, self.selection_person2company.name) + in selection_types + ) def test_self_allowed(self): """Test creation of relation to same partner when type allows.""" - type_allow = self.type_model.create({ - 'name': 'allow', - 'name_inverse': 'allow_inverse', - 'contact_type_left': 'p', - 'contact_type_right': 'p', - 'allow_self': True}) + type_allow = self.type_model.create( + { + "name": "allow", + "name_inverse": "allow_inverse", + "contact_type_left": "p", + "contact_type_right": "p", + "allow_self": True, + } + ) self.assertTrue(type_allow) - reflexive_relation = self.relation_model.create({ - 'type_id': type_allow.id, - 'left_partner_id': self.partner_01_person.id, - 'right_partner_id': self.partner_01_person.id}) + reflexive_relation = self.relation_model.create( + { + "type_id": type_allow.id, + "left_partner_id": self.partner_01_person.id, + "right_partner_id": self.partner_01_person.id, + } + ) self.assertTrue(reflexive_relation) def test_self_disallowed(self): @@ -43,18 +52,24 @@ class TestPartnerRelation(TestPartnerRelationCommon): Attempt to create a relation of a partner to the same partner should raise an error when the type of relation explicitly disallows this. """ - type_disallow = self.type_model.create({ - 'name': 'disallow', - 'name_inverse': 'disallow_inverse', - 'contact_type_left': 'p', - 'contact_type_right': 'p', - 'allow_self': False}) + type_disallow = self.type_model.create( + { + "name": "disallow", + "name_inverse": "disallow_inverse", + "contact_type_left": "p", + "contact_type_right": "p", + "allow_self": False, + } + ) self.assertTrue(type_disallow) with self.assertRaises(ValidationError): - self.relation_model.create({ - 'type_id': type_disallow.id, - 'left_partner_id': self.partner_01_person.id, - 'right_partner_id': self.partner_01_person.id}) + self.relation_model.create( + { + "type_id": type_disallow.id, + "left_partner_id": self.partner_01_person.id, + "right_partner_id": self.partner_01_person.id, + } + ) def test_self_disallowed_after_self_relation_created(self): """Test that allow_self can not be true if a reflexive relation already exists. @@ -62,17 +77,23 @@ class TestPartnerRelation(TestPartnerRelationCommon): If at least one reflexive relation exists for the given type, reflexivity can not be disallowed. """ - type_allow = self.type_model.create({ - 'name': 'allow', - 'name_inverse': 'allow_inverse', - 'contact_type_left': 'p', - 'contact_type_right': 'p', - 'allow_self': True}) + type_allow = self.type_model.create( + { + "name": "allow", + "name_inverse": "allow_inverse", + "contact_type_left": "p", + "contact_type_right": "p", + "allow_self": True, + } + ) self.assertTrue(type_allow) - reflexive_relation = self.relation_model.create({ - 'type_id': type_allow.id, - 'left_partner_id': self.partner_01_person.id, - 'right_partner_id': self.partner_01_person.id}) + reflexive_relation = self.relation_model.create( + { + "type_id": type_allow.id, + "left_partner_id": self.partner_01_person.id, + "right_partner_id": self.partner_01_person.id, + } + ) self.assertTrue(reflexive_relation) with self.assertRaises(ValidationError): type_allow.allow_self = False @@ -85,24 +106,30 @@ class TestPartnerRelation(TestPartnerRelationCommon): Non reflexive relations are not modified. """ - type_allow = self.type_model.create({ - 'name': 'allow', - 'name_inverse': 'allow_inverse', - 'contact_type_left': 'p', - 'contact_type_right': 'p', - 'allow_self': True, - 'handle_invalid_onchange': 'delete', - }) - reflexive_relation = self.relation_model.create({ - 'type_id': type_allow.id, - 'left_partner_id': self.partner_01_person.id, - 'right_partner_id': self.partner_01_person.id, - }) - normal_relation = self.relation_model.create({ - 'type_id': type_allow.id, - 'left_partner_id': self.partner_01_person.id, - 'right_partner_id': self.partner_04_volunteer.id, - }) + type_allow = self.type_model.create( + { + "name": "allow", + "name_inverse": "allow_inverse", + "contact_type_left": "p", + "contact_type_right": "p", + "allow_self": True, + "handle_invalid_onchange": "delete", + } + ) + reflexive_relation = self.relation_model.create( + { + "type_id": type_allow.id, + "left_partner_id": self.partner_01_person.id, + "right_partner_id": self.partner_01_person.id, + } + ) + normal_relation = self.relation_model.create( + { + "type_id": type_allow.id, + "left_partner_id": self.partner_01_person.id, + "right_partner_id": self.partner_04_volunteer.id, + } + ) type_allow.allow_self = False self.assertFalse(reflexive_relation.exists()) @@ -119,31 +146,39 @@ class TestPartnerRelation(TestPartnerRelationCommon): Reflexive relations with an end date prior to the current date are not modified. """ - type_allow = self.type_model.create({ - 'name': 'allow', - 'name_inverse': 'allow_inverse', - 'contact_type_left': 'p', - 'contact_type_right': 'p', - 'allow_self': True, - 'handle_invalid_onchange': 'end', - }) - reflexive_relation = self.relation_model.create({ - 'type_id': type_allow.id, - 'left_partner_id': self.partner_01_person.id, - 'right_partner_id': self.partner_01_person.id, - 'date_start': '2000-01-02', - }) - past_reflexive_relation = self.relation_model.create({ - 'type_id': type_allow.id, - 'left_partner_id': self.partner_01_person.id, - 'right_partner_id': self.partner_01_person.id, - 'date_end': '2000-01-01', - }) - normal_relation = self.relation_model.create({ - 'type_id': type_allow.id, - 'left_partner_id': self.partner_01_person.id, - 'right_partner_id': self.partner_04_volunteer.id, - }) + type_allow = self.type_model.create( + { + "name": "allow", + "name_inverse": "allow_inverse", + "contact_type_left": "p", + "contact_type_right": "p", + "allow_self": True, + "handle_invalid_onchange": "end", + } + ) + reflexive_relation = self.relation_model.create( + { + "type_id": type_allow.id, + "left_partner_id": self.partner_01_person.id, + "right_partner_id": self.partner_01_person.id, + "date_start": "2000-01-02", + } + ) + past_reflexive_relation = self.relation_model.create( + { + "type_id": type_allow.id, + "left_partner_id": self.partner_01_person.id, + "right_partner_id": self.partner_01_person.id, + "date_end": "2000-01-01", + } + ) + normal_relation = self.relation_model.create( + { + "type_id": type_allow.id, + "left_partner_id": self.partner_01_person.id, + "right_partner_id": self.partner_04_volunteer.id, + } + ) type_allow.allow_self = False self.assertEqual(reflexive_relation.date_end, fields.Date.today()) @@ -156,20 +191,24 @@ class TestPartnerRelation(TestPartnerRelationCommon): If handle_invalid_onchange is set to end, then deactivating reflexivity will delete invalid relations in the future. """ - type_allow = self.type_model.create({ - 'name': 'allow', - 'name_inverse': 'allow_inverse', - 'contact_type_left': 'p', - 'contact_type_right': 'p', - 'allow_self': True, - 'handle_invalid_onchange': 'end', - }) - future_reflexive_relation = self.relation_model.create({ - 'type_id': type_allow.id, - 'left_partner_id': self.partner_01_person.id, - 'right_partner_id': self.partner_01_person.id, - 'date_start': datetime.now() + timedelta(1), - }) + type_allow = self.type_model.create( + { + "name": "allow", + "name_inverse": "allow_inverse", + "contact_type_left": "p", + "contact_type_right": "p", + "allow_self": True, + "handle_invalid_onchange": "end", + } + ) + future_reflexive_relation = self.relation_model.create( + { + "type_id": type_allow.id, + "left_partner_id": self.partner_01_person.id, + "right_partner_id": self.partner_01_person.id, + "date_start": datetime.now() + timedelta(1), + } + ) type_allow.allow_self = False self.assertFalse(future_reflexive_relation.exists()) @@ -180,17 +219,23 @@ class TestPartnerRelation(TestPartnerRelationCommon): raise an error when the type of relation does not explicitly allow this. """ - type_default = self.type_model.create({ - 'name': 'default', - 'name_inverse': 'default_inverse', - 'contact_type_left': 'p', - 'contact_type_right': 'p'}) + type_default = self.type_model.create( + { + "name": "default", + "name_inverse": "default_inverse", + "contact_type_left": "p", + "contact_type_right": "p", + } + ) self.assertTrue(type_default) with self.assertRaises(ValidationError): - self.relation_model.create({ - 'type_id': type_default.id, - 'left_partner_id': self.partner_01_person.id, - 'right_partner_id': self.partner_01_person.id}) + self.relation_model.create( + { + "type_id": type_default.id, + "left_partner_id": self.partner_01_person.id, + "right_partner_id": self.partner_01_person.id, + } + ) def test_self_mixed(self): """Test creation of relation with wrong types. @@ -199,54 +244,65 @@ class TestPartnerRelation(TestPartnerRelationCommon): type should raise an error. """ with self.assertRaises(ValidationError): - self.relation_model.create({ - 'type_id': self.type_company2person.id, - 'left_partner_id': self.partner_01_person.id, - 'right_partner_id': self.partner_02_company.id}) + self.relation_model.create( + { + "type_id": self.type_company2person.id, + "left_partner_id": self.partner_01_person.id, + "right_partner_id": self.partner_02_company.id, + } + ) def test_symmetric(self): """Test creating symmetric relation.""" # Start out with non symmetric relation: - type_symmetric = self.type_model.create({ - 'name': 'not yet symmetric', - 'name_inverse': 'the other side of not symmetric', - 'is_symmetric': False, - 'contact_type_left': False, - 'contact_type_right': 'p'}) + type_symmetric = self.type_model.create( + { + "name": "not yet symmetric", + "name_inverse": "the other side of not symmetric", + "is_symmetric": False, + "contact_type_left": False, + "contact_type_right": "p", + } + ) # not yet symmetric relation should result in two records in # selection: - selection_symmetric = self.selection_model.search([ - ('type_id', '=', type_symmetric.id)]) + selection_symmetric = self.selection_model.search( + [("type_id", "=", type_symmetric.id)] + ) self.assertEqual(len(selection_symmetric), 2) # Now change to symmetric and test name and inverse name: with self.env.do_in_draft(): - type_symmetric.write({ - 'name': 'sym', - 'is_symmetric': True}) + type_symmetric.write({"name": "sym", "is_symmetric": True}) self.assertEqual(type_symmetric.is_symmetric, True) + self.assertEqual(type_symmetric.name_inverse, type_symmetric.name) self.assertEqual( - type_symmetric.name_inverse, - type_symmetric.name) - self.assertEqual( - type_symmetric.contact_type_right, - type_symmetric.contact_type_left) + type_symmetric.contact_type_right, type_symmetric.contact_type_left + ) # now update the database: - type_symmetric.write({ - 'name': type_symmetric.name, - 'is_symmetric': type_symmetric.is_symmetric, - 'name_inverse': type_symmetric.name_inverse, - 'contact_type_right': type_symmetric.contact_type_right}) + type_symmetric.write( + { + "name": type_symmetric.name, + "is_symmetric": type_symmetric.is_symmetric, + "name_inverse": type_symmetric.name_inverse, + "contact_type_right": type_symmetric.contact_type_right, + } + ) # symmetric relation should result in only one record in # selection: - selection_symmetric = self.selection_model.search([ - ('type_id', '=', type_symmetric.id)]) + selection_symmetric = self.selection_model.search( + [("type_id", "=", type_symmetric.id)] + ) self.assertEqual(len(selection_symmetric), 1) - relation = self.relation_all_model.create({ - 'type_selection_id': selection_symmetric.id, - 'this_partner_id': self.partner_02_company.id, - 'other_partner_id': self.partner_01_person.id}) - partners = self.partner_model.search([ - ('search_relation_type_id', '=', relation.type_selection_id.id)]) + relation = self.relation_all_model.create( + { + "type_selection_id": selection_symmetric.id, + "this_partner_id": self.partner_02_company.id, + "other_partner_id": self.partner_01_person.id, + } + ) + partners = self.partner_model.search( + [("search_relation_type_id", "=", relation.type_selection_id.id)] + ) self.assertTrue(self.partner_01_person in partners) self.assertTrue(self.partner_02_company in partners) @@ -254,124 +310,148 @@ class TestPartnerRelation(TestPartnerRelationCommon): """Test check on category in relations.""" # Check on left side: with self.assertRaises(ValidationError): - self.relation_model.create({ - 'type_id': self.type_ngo2volunteer.id, - 'left_partner_id': self.partner_02_company.id, - 'right_partner_id': self.partner_04_volunteer.id}) + self.relation_model.create( + { + "type_id": self.type_ngo2volunteer.id, + "left_partner_id": self.partner_02_company.id, + "right_partner_id": self.partner_04_volunteer.id, + } + ) # Check on right side: with self.assertRaises(ValidationError): - self.relation_model.create({ - 'type_id': self.type_ngo2volunteer.id, - 'left_partner_id': self.partner_03_ngo.id, - 'right_partner_id': self.partner_01_person.id}) + self.relation_model.create( + { + "type_id": self.type_ngo2volunteer.id, + "left_partner_id": self.partner_03_ngo.id, + "right_partner_id": self.partner_01_person.id, + } + ) def test_relation_type_change(self): """Test change in relation type conditions.""" # First create a relation type having no particular conditions. - (type_school2student, - school2student, - school2student_inverse) = \ - self._create_relation_type_selection({ - 'name': 'school has student', - 'name_inverse': 'studies at school'}) + ( + type_school2student, + school2student, + school2student_inverse, + ) = self._create_relation_type_selection( + {"name": "school has student", "name_inverse": "studies at school"} + ) # Second create relations based on those conditions. - partner_school = self.partner_model.create({ - 'name': 'Test School', - 'is_company': True, - 'ref': 'TS'}) - partner_bart = self.partner_model.create({ - 'name': 'Bart Simpson', - 'is_company': False, - 'ref': 'BS'}) - partner_lisa = self.partner_model.create({ - 'name': 'Lisa Simpson', - 'is_company': False, - 'ref': 'LS'}) - relation_school2bart = self.relation_all_model.create({ - 'this_partner_id': partner_school.id, - 'type_selection_id': school2student.id, - 'other_partner_id': partner_bart.id}) + partner_school = self.partner_model.create( + {"name": "Test School", "is_company": True, "ref": "TS"} + ) + partner_bart = self.partner_model.create( + {"name": "Bart Simpson", "is_company": False, "ref": "BS"} + ) + partner_lisa = self.partner_model.create( + {"name": "Lisa Simpson", "is_company": False, "ref": "LS"} + ) + relation_school2bart = self.relation_all_model.create( + { + "this_partner_id": partner_school.id, + "type_selection_id": school2student.id, + "other_partner_id": partner_bart.id, + } + ) self.assertTrue(relation_school2bart) - relation_school2lisa = self.relation_all_model.create({ - 'this_partner_id': partner_school.id, - 'type_selection_id': school2student.id, - 'other_partner_id': partner_lisa.id}) + relation_school2lisa = self.relation_all_model.create( + { + "this_partner_id": partner_school.id, + "type_selection_id": school2student.id, + "other_partner_id": partner_lisa.id, + } + ) self.assertTrue(relation_school2lisa) - relation_bart2lisa = self.relation_all_model.create({ - 'this_partner_id': partner_bart.id, - 'type_selection_id': school2student.id, - 'other_partner_id': partner_lisa.id}) + relation_bart2lisa = self.relation_all_model.create( + { + "this_partner_id": partner_bart.id, + "type_selection_id": school2student.id, + "other_partner_id": partner_lisa.id, + } + ) self.assertTrue(relation_bart2lisa) # Third creata a category and make it a condition for the # relation type. # - Test restriction # - Test ignore - category_student = self.category_model.create({'name': 'Student'}) + category_student = self.category_model.create({"name": "Student"}) with self.assertRaises(ValidationError): - type_school2student.write({ - 'partner_category_right': category_student.id}) + type_school2student.write({"partner_category_right": category_student.id}) self.assertFalse(type_school2student.partner_category_right.id) - type_school2student.write({ - 'handle_invalid_onchange': 'ignore', - 'partner_category_right': category_student.id}) + type_school2student.write( + { + "handle_invalid_onchange": "ignore", + "partner_category_right": category_student.id, + } + ) self.assertEqual( - type_school2student.partner_category_right.id, - category_student.id) + type_school2student.partner_category_right.id, category_student.id + ) # Fourth make company type a condition for left partner # - Test ending # - Test deletion - partner_bart.write({ - 'category_id': [(4, category_student.id)]}) - partner_lisa.write({ - 'category_id': [(4, category_student.id)]}) + partner_bart.write({"category_id": [(4, category_student.id)]}) + partner_lisa.write({"category_id": [(4, category_student.id)]}) # Future student to be deleted by end action: - partner_homer = self.partner_model.create({ - 'name': 'Homer Simpson', - 'is_company': False, - 'ref': 'HS', - 'category_id': [(4, category_student.id)]}) - relation_lisa2homer = self.relation_all_model.create({ - 'this_partner_id': partner_lisa.id, - 'type_selection_id': school2student.id, - 'other_partner_id': partner_homer.id, - 'date_start': date.today() + relativedelta(months=+6)}) + partner_homer = self.partner_model.create( + { + "name": "Homer Simpson", + "is_company": False, + "ref": "HS", + "category_id": [(4, category_student.id)], + } + ) + relation_lisa2homer = self.relation_all_model.create( + { + "this_partner_id": partner_lisa.id, + "type_selection_id": school2student.id, + "other_partner_id": partner_homer.id, + "date_start": date.today() + relativedelta(months=+6), + } + ) self.assertTrue(relation_lisa2homer) - type_school2student.write({ - 'handle_invalid_onchange': 'end', - 'contact_type_left': 'c'}) - self.assertEqual( - relation_bart2lisa.date_end, - fields.Date.today()) + type_school2student.write( + {"handle_invalid_onchange": "end", "contact_type_left": "c"} + ) + self.assertEqual(relation_bart2lisa.date_end, fields.Date.today()) self.assertFalse(relation_lisa2homer.exists()) - type_school2student.write({ - 'handle_invalid_onchange': 'delete', - 'contact_type_left': 'c', - 'contact_type_right': 'p'}) + type_school2student.write( + { + "handle_invalid_onchange": "delete", + "contact_type_left": "c", + "contact_type_right": "p", + } + ) self.assertFalse(relation_bart2lisa.exists()) def test_relation_type_unlink(self): """Test delete of relation type, including deleting relations.""" # First create a relation type having restrict particular conditions. - type_model = self.env['res.partner.relation.type'] - relation_model = self.env['res.partner.relation'] - partner_model = self.env['res.partner'] - type_school2student = type_model.create({ - 'name': 'school has student', - 'name_inverse': 'studies at school', - 'handle_invalid_onchange': 'delete'}) + type_model = self.env["res.partner.relation.type"] + relation_model = self.env["res.partner.relation"] + partner_model = self.env["res.partner"] + type_school2student = type_model.create( + { + "name": "school has student", + "name_inverse": "studies at school", + "handle_invalid_onchange": "delete", + } + ) # Second create relation based on those conditions. - partner_school = partner_model.create({ - 'name': 'Test School', - 'is_company': True, - 'ref': 'TS'}) - partner_bart = partner_model.create({ - 'name': 'Bart Simpson', - 'is_company': False, - 'ref': 'BS'}) - relation_school2bart = relation_model.create({ - 'left_partner_id': partner_school.id, - 'type_id': type_school2student.id, - 'right_partner_id': partner_bart.id}) + partner_school = partner_model.create( + {"name": "Test School", "is_company": True, "ref": "TS"} + ) + partner_bart = partner_model.create( + {"name": "Bart Simpson", "is_company": False, "ref": "BS"} + ) + relation_school2bart = relation_model.create( + { + "left_partner_id": partner_school.id, + "type_id": type_school2student.id, + "right_partner_id": partner_bart.id, + } + ) # Delete type. Relations with type should also cease to exist: type_school2student.unlink() self.assertFalse(relation_school2bart.exists()) diff --git a/partner_multi_relation/tests/test_partner_relation_all.py b/partner_multi_relation/tests/test_partner_relation_all.py index 7ebefb2b2..96125c9ce 100644 --- a/partner_multi_relation/tests/test_partner_relation_all.py +++ b/partner_multi_relation/tests/test_partner_relation_all.py @@ -2,74 +2,87 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from datetime import date + from odoo.exceptions import ValidationError from .test_partner_relation_common import TestPartnerRelationCommon class TestPartnerRelation(TestPartnerRelationCommon): - def setUp(self): super(TestPartnerRelation, self).setUp() # Create a new relation type which will not have valid relations: - category_nobody = self.category_model.create({ - 'name': 'Nobody'}) - (self.type_nobody, - self.selection_nobody, - self.selection_nobody_inverse) = ( - self._create_relation_type_selection({ - 'name': 'has relation with nobody', - 'name_inverse': 'nobody has relation with', - 'contact_type_left': 'c', - 'contact_type_right': 'p', - 'partner_category_left': category_nobody.id, - 'partner_category_right': category_nobody.id})) + category_nobody = self.category_model.create({"name": "Nobody"}) + ( + self.type_nobody, + self.selection_nobody, + self.selection_nobody_inverse, + ) = self._create_relation_type_selection( + { + "name": "has relation with nobody", + "name_inverse": "nobody has relation with", + "contact_type_left": "c", + "contact_type_right": "p", + "partner_category_left": category_nobody.id, + "partner_category_right": category_nobody.id, + } + ) def _get_empty_relation(self): """Get empty relation record for onchange tests.""" # Need English, because we will compare text - return self.relation_all_model.with_context(lang='en_US').new({}) + return self.relation_all_model.with_context(lang="en_US").new({}) def test_get_partner_types(self): """Partner types should contain at least 'c' and 'p'.""" partner_types = self.selection_model.get_partner_types() type_codes = [ptype[0] for ptype in partner_types] - self.assertTrue('c' in type_codes) - self.assertTrue('p' in type_codes) + self.assertTrue("c" in type_codes) + self.assertTrue("p" in type_codes) def test_create_with_active_id(self): """Test creation with this_partner_id from active_id.""" # Check wether we can create connection from company to person, # taking the particular company from the active records: relation = self.relation_all_model.with_context( - active_id=self.partner_02_company.id, - active_ids=self.partner_02_company.ids).create({ - 'other_partner_id': self.partner_01_person.id, - 'type_selection_id': self.selection_company2person.id}) + active_id=self.partner_02_company.id, active_ids=self.partner_02_company.ids + ).create( + { + "other_partner_id": self.partner_01_person.id, + "type_selection_id": self.selection_company2person.id, + } + ) self.assertTrue(relation) self.assertEqual(relation.this_partner_id, self.partner_02_company) # Partner should have one relation now: self.assertEqual(self.partner_01_person.relation_count, 1) # Test create without type_selection_id: with self.assertRaises(ValidationError): - self.relation_all_model.create({ - 'this_partner_id': self.partner_02_company.id, - 'other_partner_id': self.partner_01_person.id}) + self.relation_all_model.create( + { + "this_partner_id": self.partner_02_company.id, + "other_partner_id": self.partner_01_person.id, + } + ) def test_display_name(self): """Test display name""" relation = self._create_company2person_relation() self.assertEqual( - relation.display_name, '%s %s %s' % ( + relation.display_name, + "%s %s %s" + % ( relation.this_partner_id.name, relation.type_selection_id.name, - relation.other_partner_id.name)) + relation.other_partner_id.name, + ), + ) def test__regular_write(self): """Test write with valid data.""" relation = self._create_company2person_relation() - relation.write({'date_start': '2014-09-01'}) + relation.write({"date_start": "2014-09-01"}) relation.invalidate_cache(ids=relation.ids) self.assertEqual(relation.date_start, date(2014, 9, 1)) @@ -77,90 +90,120 @@ class TestPartnerRelation(TestPartnerRelationCommon): """Test write with date_end before date_start.""" relation = self._create_company2person_relation() with self.assertRaises(ValidationError): - relation.write({ - 'date_start': '2016-09-01', - 'date_end': '2016-08-01'}) + relation.write({"date_start": "2016-09-01", "date_end": "2016-08-01"}) def test_validate_overlapping_01(self): """Test create overlapping with no start / end dates.""" relation = self._create_company2person_relation() with self.assertRaises(ValidationError): # New relation with no start / end should give error - self.relation_all_model.create({ - 'this_partner_id': relation.this_partner_id.id, - 'type_selection_id': relation.type_selection_id.id, - 'other_partner_id': relation.other_partner_id.id}) + self.relation_all_model.create( + { + "this_partner_id": relation.this_partner_id.id, + "type_selection_id": relation.type_selection_id.id, + "other_partner_id": relation.other_partner_id.id, + } + ) def test_validate_overlapping_02(self): """Test create overlapping with start / end dates.""" - relation = self.relation_all_model.create({ - 'this_partner_id': self.partner_02_company.id, - 'type_selection_id': self.selection_company2person.id, - 'other_partner_id': self.partner_01_person.id, - 'date_start': '2015-09-01', - 'date_end': '2016-08-31'}) + relation = self.relation_all_model.create( + { + "this_partner_id": self.partner_02_company.id, + "type_selection_id": self.selection_company2person.id, + "other_partner_id": self.partner_01_person.id, + "date_start": "2015-09-01", + "date_end": "2016-08-31", + } + ) # New relation with overlapping start / end should give error with self.assertRaises(ValidationError): - self.relation_all_model.create({ - 'this_partner_id': relation.this_partner_id.id, - 'type_selection_id': relation.type_selection_id.id, - 'other_partner_id': relation.other_partner_id.id, - 'date_start': '2016-08-01', - 'date_end': '2017-07-30'}) + self.relation_all_model.create( + { + "this_partner_id": relation.this_partner_id.id, + "type_selection_id": relation.type_selection_id.id, + "other_partner_id": relation.other_partner_id.id, + "date_start": "2016-08-01", + "date_end": "2017-07-30", + } + ) def test_validate_overlapping_03(self): """Test create not overlapping.""" - relation = self.relation_all_model.create({ - 'this_partner_id': self.partner_02_company.id, - 'type_selection_id': self.selection_company2person.id, - 'other_partner_id': self.partner_01_person.id, - 'date_start': '2015-09-01', - 'date_end': '2016-08-31'}) - relation_another_record = self.relation_all_model.create({ - 'this_partner_id': relation.this_partner_id.id, - 'type_selection_id': relation.type_selection_id.id, - 'other_partner_id': relation.other_partner_id.id, - 'date_start': '2016-09-01', - 'date_end': '2017-08-31'}) + relation = self.relation_all_model.create( + { + "this_partner_id": self.partner_02_company.id, + "type_selection_id": self.selection_company2person.id, + "other_partner_id": self.partner_01_person.id, + "date_start": "2015-09-01", + "date_end": "2016-08-31", + } + ) + relation_another_record = self.relation_all_model.create( + { + "this_partner_id": relation.this_partner_id.id, + "type_selection_id": relation.type_selection_id.id, + "other_partner_id": relation.other_partner_id.id, + "date_start": "2016-09-01", + "date_end": "2017-08-31", + } + ) self.assertTrue(relation_another_record) def test_inverse_record(self): """Test creation of inverse record.""" relation = self._create_company2person_relation() - inverse_relation = self.relation_all_model.search([ - ('this_partner_id', '=', relation.other_partner_id.id), - ('other_partner_id', '=', relation.this_partner_id.id)]) + inverse_relation = self.relation_all_model.search( + [ + ("this_partner_id", "=", relation.other_partner_id.id), + ("other_partner_id", "=", relation.this_partner_id.id), + ] + ) self.assertEqual(len(inverse_relation), 1) self.assertEqual( - inverse_relation.type_selection_id.name, - self.selection_person2company.name) + inverse_relation.type_selection_id.name, self.selection_person2company.name + ) def test_inverse_creation(self): """Test creation of record through inverse selection.""" - relation = self.relation_all_model.create({ - 'this_partner_id': self.partner_01_person.id, - 'type_selection_id': self.selection_person2company.id, - 'other_partner_id': self.partner_02_company.id}) + relation = self.relation_all_model.create( + { + "this_partner_id": self.partner_01_person.id, + "type_selection_id": self.selection_person2company.id, + "other_partner_id": self.partner_02_company.id, + } + ) # Check wether display name is what we should expect: self.assertEqual( - relation.display_name, '%s %s %s' % ( + relation.display_name, + "%s %s %s" + % ( self.partner_01_person.name, self.selection_person2company.name, - self.partner_02_company.name)) + self.partner_02_company.name, + ), + ) def test_inverse_creation_type_id(self): """Test creation of record through inverse selection with type_id.""" - relation = self.relation_all_model.create({ - 'this_partner_id': self.partner_01_person.id, - 'type_id': self.selection_person2company.type_id.id, - 'is_inverse': True, - 'other_partner_id': self.partner_02_company.id}) + relation = self.relation_all_model.create( + { + "this_partner_id": self.partner_01_person.id, + "type_id": self.selection_person2company.type_id.id, + "is_inverse": True, + "other_partner_id": self.partner_02_company.id, + } + ) # Check wether display name is what we should expect: self.assertEqual( - relation.display_name, '%s %s %s' % ( + relation.display_name, + "%s %s %s" + % ( self.partner_01_person.name, self.selection_person2company.name, - self.partner_02_company.name)) + self.partner_02_company.name, + ), + ) def test_unlink(self): """Unlinking derived relation should unlink base relation.""" @@ -178,104 +221,114 @@ class TestPartnerRelation(TestPartnerRelationCommon): # 1. Test call with empty relation relation_empty = self._get_empty_relation() result = relation_empty.onchange_type_selection_id() - self.assertTrue('domain' in result) - self.assertFalse('warning' in result) - self.assertTrue('this_partner_id' in result['domain']) - self.assertFalse(result['domain']['this_partner_id']) - self.assertTrue('other_partner_id' in result['domain']) - self.assertFalse(result['domain']['other_partner_id']) + self.assertTrue("domain" in result) + self.assertFalse("warning" in result) + self.assertTrue("this_partner_id" in result["domain"]) + self.assertFalse(result["domain"]["this_partner_id"]) + self.assertTrue("other_partner_id" in result["domain"]) + self.assertFalse(result["domain"]["other_partner_id"]) # 2. Test call with company 2 person relation relation = self._create_company2person_relation() - domain = relation.onchange_type_selection_id()['domain'] - self.assertTrue( - ('is_company', '=', False) in domain['other_partner_id']) + domain = relation.onchange_type_selection_id()["domain"] + self.assertTrue(("is_company", "=", False) in domain["other_partner_id"]) # 3. Test with relation needing categories, # take active partner from active_id: relation_ngo_volunteer = self.relation_all_model.with_context( - active_id=self.partner_03_ngo.id).create({ - 'type_selection_id': self.selection_ngo2volunteer.id, - 'other_partner_id': self.partner_04_volunteer.id}) - domain = relation_ngo_volunteer.onchange_type_selection_id()['domain'] + active_id=self.partner_03_ngo.id + ).create( + { + "type_selection_id": self.selection_ngo2volunteer.id, + "other_partner_id": self.partner_04_volunteer.id, + } + ) + domain = relation_ngo_volunteer.onchange_type_selection_id()["domain"] self.assertTrue( - ('category_id', 'in', [self.category_01_ngo.id]) in - domain['this_partner_id']) + ("category_id", "in", [self.category_01_ngo.id]) + in domain["this_partner_id"] + ) self.assertTrue( - ('category_id', 'in', [self.category_02_volunteer.id]) in - domain['other_partner_id']) + ("category_id", "in", [self.category_02_volunteer.id]) + in domain["other_partner_id"] + ) # 4. Test with invalid or impossible combinations relation_nobody = self._get_empty_relation() with self.env.do_in_draft(): relation_nobody.type_selection_id = self.selection_nobody - warning = relation_nobody.onchange_type_selection_id()['warning'] - self.assertTrue('message' in warning) - self.assertTrue('No this partner available' in warning['message']) + warning = relation_nobody.onchange_type_selection_id()["warning"] + self.assertTrue("message" in warning) + self.assertTrue("No this partner available" in warning["message"]) with self.env.do_in_draft(): relation_nobody.this_partner_id = self.partner_02_company - warning = relation_nobody.onchange_type_selection_id()['warning'] - self.assertTrue('message' in warning) - self.assertTrue('incompatible' in warning['message']) + warning = relation_nobody.onchange_type_selection_id()["warning"] + self.assertTrue("message" in warning) + self.assertTrue("incompatible" in warning["message"]) # Allow left partner and check message for other partner: - self.type_nobody.write({'partner_category_left': False}) + self.type_nobody.write({"partner_category_left": False}) self.selection_nobody.invalidate_cache(ids=self.selection_nobody.ids) - warning = relation_nobody.onchange_type_selection_id()['warning'] - self.assertTrue('message' in warning) - self.assertTrue('No other partner available' in warning['message']) + warning = relation_nobody.onchange_type_selection_id()["warning"] + self.assertTrue("message" in warning) + self.assertTrue("No other partner available" in warning["message"]) def test_on_change_partner_id(self): """Test on_change_partner_id.""" # 1. Test call with empty relation relation_empty = self._get_empty_relation() result = relation_empty.onchange_partner_id() - self.assertTrue('domain' in result) - self.assertFalse('warning' in result) - self.assertTrue('type_selection_id' in result['domain']) - self.assertFalse(result['domain']['type_selection_id']) + self.assertTrue("domain" in result) + self.assertFalse("warning" in result) + self.assertTrue("type_selection_id" in result["domain"]) + self.assertFalse(result["domain"]["type_selection_id"]) # 2. Test call with company 2 person relation relation = self._create_company2person_relation() - domain = relation.onchange_partner_id()['domain'] - self.assertTrue( - ('contact_type_this', '=', 'c') in domain['type_selection_id']) + domain = relation.onchange_partner_id()["domain"] + self.assertTrue(("contact_type_this", "=", "c") in domain["type_selection_id"]) # 3. Test with invalid or impossible combinations relation_nobody = self._get_empty_relation() with self.env.do_in_draft(): relation_nobody.this_partner_id = self.partner_02_company relation_nobody.type_selection_id = self.selection_nobody - warning = relation_nobody.onchange_partner_id()['warning'] - self.assertTrue('message' in warning) - self.assertTrue('incompatible' in warning['message']) + warning = relation_nobody.onchange_partner_id()["warning"] + self.assertTrue("message" in warning) + self.assertTrue("incompatible" in warning["message"]) def test_write(self): """Test write. Special attention for changing type.""" relation_company2person = self._create_company2person_relation() company_partner = relation_company2person.this_partner_id # First get another worker: - partner_extra_person = self.partner_model.create({ - 'name': 'A new worker', - 'is_company': False, - 'ref': 'NW01'}) - relation_company2person.write({ - 'other_partner_id': partner_extra_person.id}) + partner_extra_person = self.partner_model.create( + {"name": "A new worker", "is_company": False, "ref": "NW01"} + ) + relation_company2person.write({"other_partner_id": partner_extra_person.id}) self.assertEqual( - relation_company2person.other_partner_id.name, - partner_extra_person.name) + relation_company2person.other_partner_id.name, partner_extra_person.name + ) # We will also change to a type going from person to company: - (type_worker2company, - selection_worker2company, - selection_company2worker) = self._create_relation_type_selection({ - 'name': 'works for', - 'name_inverse': 'has worker', - 'contact_type_left': 'p', - 'contact_type_right': 'c'}) - relation_company2person.write({ - 'this_partner_id': partner_extra_person.id, - 'type_selection_id': selection_worker2company.id, - 'other_partner_id': company_partner.id}) + ( + type_worker2company, + selection_worker2company, + selection_company2worker, + ) = self._create_relation_type_selection( + { + "name": "works for", + "name_inverse": "has worker", + "contact_type_left": "p", + "contact_type_right": "c", + } + ) + relation_company2person.write( + { + "this_partner_id": partner_extra_person.id, + "type_selection_id": selection_worker2company.id, + "other_partner_id": company_partner.id, + } + ) self.assertEqual( - relation_company2person.this_partner_id.id, - partner_extra_person.id) + relation_company2person.this_partner_id.id, partner_extra_person.id + ) self.assertEqual( - relation_company2person.type_selection_id.id, - selection_worker2company.id) + relation_company2person.type_selection_id.id, selection_worker2company.id + ) self.assertEqual( - relation_company2person.other_partner_id.id, - company_partner.id) + relation_company2person.other_partner_id.id, company_partner.id + ) diff --git a/partner_multi_relation/tests/test_partner_relation_common.py b/partner_multi_relation/tests/test_partner_relation_common.py index 441314c80..05d472926 100644 --- a/partner_multi_relation/tests/test_partner_relation_common.py +++ b/partner_multi_relation/tests/test_partner_relation_common.py @@ -4,73 +4,80 @@ from odoo.tests import common class TestPartnerRelationCommon(common.TransactionCase): - def setUp(self): super(TestPartnerRelationCommon, self).setUp() - self.partner_model = self.env['res.partner'] - self.category_model = self.env['res.partner.category'] - self.type_model = self.env['res.partner.relation.type'] - self.selection_model = self.env['res.partner.relation.type.selection'] - self.relation_model = self.env['res.partner.relation'] - self.relation_all_model = self.env['res.partner.relation.all'] - self.partner_01_person = self.partner_model.create({ - 'name': 'Test User 1', - 'is_company': False, - 'ref': 'PR01'}) - self.partner_02_company = self.partner_model.create({ - 'name': 'Test Company', - 'is_company': True, - 'ref': 'PR02'}) + self.partner_model = self.env["res.partner"] + self.category_model = self.env["res.partner.category"] + self.type_model = self.env["res.partner.relation.type"] + self.selection_model = self.env["res.partner.relation.type.selection"] + self.relation_model = self.env["res.partner.relation"] + self.relation_all_model = self.env["res.partner.relation.all"] + self.partner_01_person = self.partner_model.create( + {"name": "Test User 1", "is_company": False, "ref": "PR01"} + ) + self.partner_02_company = self.partner_model.create( + {"name": "Test Company", "is_company": True, "ref": "PR02"} + ) # Create partners with specific categories: - self.category_01_ngo = self.category_model.create({'name': 'NGO'}) - self.partner_03_ngo = self.partner_model.create({ - 'name': 'Test NGO', - 'is_company': True, - 'ref': 'PR03', - 'category_id': [(4, self.category_01_ngo.id)]}) - self.category_02_volunteer = self.category_model.create({ - 'name': 'Volunteer'}) - self.partner_04_volunteer = self.partner_model.create({ - 'name': 'Test Volunteer', - 'is_company': False, - 'ref': 'PR04', - 'category_id': [(4, self.category_02_volunteer.id)]}) + self.category_01_ngo = self.category_model.create({"name": "NGO"}) + self.partner_03_ngo = self.partner_model.create( + { + "name": "Test NGO", + "is_company": True, + "ref": "PR03", + "category_id": [(4, self.category_01_ngo.id)], + } + ) + self.category_02_volunteer = self.category_model.create({"name": "Volunteer"}) + self.partner_04_volunteer = self.partner_model.create( + { + "name": "Test Volunteer", + "is_company": False, + "ref": "PR04", + "category_id": [(4, self.category_02_volunteer.id)], + } + ) # Create a new relation type withouth categories: - (self.type_company2person, - self.selection_company2person, - self.selection_person2company) = \ - self._create_relation_type_selection({ - 'name': 'mixed', - 'name_inverse': 'mixed_inverse', - 'contact_type_left': 'c', - 'contact_type_right': 'p'}) + ( + self.type_company2person, + self.selection_company2person, + self.selection_person2company, + ) = self._create_relation_type_selection( + { + "name": "mixed", + "name_inverse": "mixed_inverse", + "contact_type_left": "c", + "contact_type_right": "p", + } + ) # Create a new relation type with categories: - (self.type_ngo2volunteer, - self.selection_ngo2volunteer, - self.selection_volunteer2ngo) = \ - self._create_relation_type_selection({ - 'name': 'NGO has volunteer', - 'name_inverse': 'volunteer works for NGO', - 'contact_type_left': 'c', - 'contact_type_right': 'p', - 'partner_category_left': self.category_01_ngo.id, - 'partner_category_right': self.category_02_volunteer.id}) + ( + self.type_ngo2volunteer, + self.selection_ngo2volunteer, + self.selection_volunteer2ngo, + ) = self._create_relation_type_selection( + { + "name": "NGO has volunteer", + "name_inverse": "volunteer works for NGO", + "contact_type_left": "c", + "contact_type_right": "p", + "partner_category_left": self.category_01_ngo.id, + "partner_category_right": self.category_02_volunteer.id, + } + ) def _create_relation_type_selection(self, vals): """Create relation type and return this with selection types.""" - assert 'name' in vals, ( - "Name missing in vals to create relation type. Vals: %s." - % vals) - assert 'name' in vals, ( - "Name_inverse missing in vals to create relation type. Vals: %s." - % vals) + assert "name" in vals, ( + "Name missing in vals to create relation type. Vals: %s." % vals + ) + assert "name" in vals, ( + "Name_inverse missing in vals to create relation type. Vals: %s." % vals + ) new_type = self.type_model.create(vals) - self.assertTrue( - new_type, - msg="No relation type created with vals %s." % vals) - selection_types = self.selection_model.search([ - ('type_id', '=', new_type.id)]) + self.assertTrue(new_type, msg="No relation type created with vals %s." % vals) + selection_types = self.selection_model.search([("type_id", "=", new_type.id)]) for st in selection_types: if st.is_inverse: inverse_type_selection = st @@ -79,16 +86,21 @@ class TestPartnerRelationCommon(common.TransactionCase): self.assertTrue( inverse_type_selection, msg="Failed to find inverse type selection based on" - " relation type created with vals %s." % vals) + " relation type created with vals %s." % vals, + ) self.assertTrue( type_selection, msg="Failed to find type selection based on" - " relation type created with vals %s." % vals) + " relation type created with vals %s." % vals, + ) return (new_type, type_selection, inverse_type_selection) def _create_company2person_relation(self): """Utility function to get a relation from company 2 partner.""" - return self.relation_all_model.create({ - 'type_selection_id': self.selection_company2person.id, - 'this_partner_id': self.partner_02_company.id, - 'other_partner_id': self.partner_01_person.id}) + return self.relation_all_model.create( + { + "type_selection_id": self.selection_company2person.id, + "this_partner_id": self.partner_02_company.id, + "other_partner_id": self.partner_01_person.id, + } + ) diff --git a/partner_multi_relation/tests/test_partner_search.py b/partner_multi_relation/tests/test_partner_search.py index d5f4ec707..71451dc51 100644 --- a/partner_multi_relation/tests/test_partner_search.py +++ b/partner_multi_relation/tests/test_partner_search.py @@ -8,68 +8,74 @@ from .test_partner_relation_common import TestPartnerRelationCommon class TestPartnerSearch(TestPartnerRelationCommon): - def test_search_relation_type(self): """Test searching on relation type.""" relation = self._create_company2person_relation() - partners = self.partner_model.search([ - ('search_relation_type_id', '=', relation.type_selection_id.id) - ]) + partners = self.partner_model.search( + [("search_relation_type_id", "=", relation.type_selection_id.id)] + ) self.assertTrue(self.partner_02_company in partners) - partners = self.partner_model.search([ - ('search_relation_type_id', '!=', relation.type_selection_id.id) - ]) + partners = self.partner_model.search( + [("search_relation_type_id", "!=", relation.type_selection_id.id)] + ) self.assertTrue(self.partner_01_person in partners) - partners = self.partner_model.search([ - ('search_relation_type_id', '=', self.type_company2person.name) - ]) + partners = self.partner_model.search( + [("search_relation_type_id", "=", self.type_company2person.name)] + ) self.assertTrue(self.partner_01_person in partners) self.assertTrue(self.partner_02_company in partners) - partners = self.partner_model.search([ - ('search_relation_type_id', '=', 'unknown relation') - ]) + partners = self.partner_model.search( + [("search_relation_type_id", "=", "unknown relation")] + ) self.assertFalse(partners) # Check error with invalid search operator: with self.assertRaises(ValidationError): - partners = self.partner_model.search([ - ('search_relation_type_id', 'child_of', 'some parent') - ]) + partners = self.partner_model.search( + [("search_relation_type_id", "child_of", "some parent")] + ) def test_search_relation_partner(self): """Test searching on related partner.""" self._create_company2person_relation() - partners = self.partner_model.search([ - ('search_relation_partner_id', '=', self.partner_02_company.id), - ]) + partners = self.partner_model.search( + [("search_relation_partner_id", "=", self.partner_02_company.id)] + ) self.assertTrue(self.partner_01_person in partners) def test_search_relation_date(self): """Test searching on relations valid on a certain date.""" self._create_company2person_relation() - partners = self.partner_model.search([ - ('search_relation_date', '=', fields.Date.today()), - ]) + partners = self.partner_model.search( + [("search_relation_date", "=", fields.Date.today())] + ) self.assertTrue(self.partner_01_person in partners) self.assertTrue(self.partner_02_company in partners) def test_search_any_partner(self): """Test searching for partner left or right.""" self._create_company2person_relation() - both_relations = self.relation_all_model.search([ - ('any_partner_id', '=', self.partner_02_company.id), - ]) + both_relations = self.relation_all_model.search( + [("any_partner_id", "=", self.partner_02_company.id)] + ) self.assertEqual(len(both_relations), 2) def test_search_partner_category(self): """Test searching for partners related to partners having category.""" - relation_ngo_volunteer = self.relation_all_model.create({ - 'this_partner_id': self.partner_03_ngo.id, - 'type_selection_id': self.selection_ngo2volunteer.id, - 'other_partner_id': self.partner_04_volunteer.id, - }) + relation_ngo_volunteer = self.relation_all_model.create( + { + "this_partner_id": self.partner_03_ngo.id, + "type_selection_id": self.selection_ngo2volunteer.id, + "other_partner_id": self.partner_04_volunteer.id, + } + ) self.assertTrue(relation_ngo_volunteer) - partners = self.partner_model.search([ - ('search_relation_partner_category_id', '=', - self.category_02_volunteer.id) - ]) + partners = self.partner_model.search( + [ + ( + "search_relation_partner_category_id", + "=", + self.category_02_volunteer.id, + ) + ] + ) self.assertTrue(self.partner_03_ngo in partners) diff --git a/partner_multi_relation/views/menu.xml b/partner_multi_relation/views/menu.xml index ad3c75bda..fb078faed 100644 --- a/partner_multi_relation/views/menu.xml +++ b/partner_multi_relation/views/menu.xml @@ -1,32 +1,27 @@ - + - - - - - - - - - + + + + diff --git a/partner_multi_relation/views/res_partner.xml b/partner_multi_relation/views/res_partner.xml index b522f49d8..d9a13a0b5 100644 --- a/partner_multi_relation/views/res_partner.xml +++ b/partner_multi_relation/views/res_partner.xml @@ -1,43 +1,41 @@ - + - - - partner_multi_relation.view_partner_filter - - res.partner - - - - - - - + + partner_multi_relation.view_partner_filter + + res.partner + + + + + + - - - - partner_multi_relation.view_partner_form - - res.partner - - - - - - - - - + + + + partner_multi_relation.view_partner_form + + res.partner + + + + + + + diff --git a/partner_multi_relation/views/res_partner_relation_all.xml b/partner_multi_relation/views/res_partner_relation_all.xml index 550bebc99..9c1614f12 100644 --- a/partner_multi_relation/views/res_partner_relation_all.xml +++ b/partner_multi_relation/views/res_partner_relation_all.xml @@ -1,101 +1,91 @@ - + - - - res.partner.relation.all - - - - - - - - - - - - - - res.partner.relation.all - - - - - - + + res.partner.relation.all + + + + + + + + + + + + + res.partner.relation.all + + + + + + + + + + + name="group_by_this_partner_id" + string="One Partner" + context="{'group_by': 'this_partner_id'}" + /> + name="group_by_type_selection_id" + string="Relationship Type" + context="{'group_by': 'type_selection_id'}" + /> - - - - - - - - - - - Relations - res.partner.relation.all - form - tree - - - {'active_test': 0} - -

+ name="group_by_other_partner_id" + string="Other Partner" + context="{'group_by': 'other_partner_id'}" + /> + + + + + + Relations + res.partner.relation.all + form + tree + + + {'active_test': 0} + +

Record and track your partners' relations. Relations may be linked to other partners with a type either directly or inversely.

-
-
- +
+
diff --git a/partner_multi_relation/views/res_partner_relation_type.xml b/partner_multi_relation/views/res_partner_relation_type.xml index b82e704b5..5e8c2dba7 100644 --- a/partner_multi_relation/views/res_partner_relation_type.xml +++ b/partner_multi_relation/views/res_partner_relation_type.xml @@ -1,56 +1,49 @@ - + - - - res.partner.relation.type - - - - - - - - - - - - - - res.partner.relation.type - -
- - - - - - - - - - - - + + res.partner.relation.type + + + + + + + + + + + + + res.partner.relation.type + + + + + + + + - - - + string="Right side of relation" + name="right" + attrs="{'invisible': [('is_symmetric', '=', True)]}" + > + + + - - - - - + + + + + + +
+ +
+