Partner Relations

partner_multi_relation
REPOSITORY
REPOSITORYOCA/partner-contact
GIT
GIThttps://github.com/OCA/partner-contact.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/partner-contact/tree/19.0/partner_multi_relation
VERSION
VERSION 1.1.0
CATEGORY
CATEGORYCustomer Relationship Management
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Camptocamp, Therp BV
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Camptocamp, Therp BV
COMMITTERS
COMMITTERSRonald Portier, Weblate, OCA-git-bot, oca-ci
WEBSITE
WEBSITEhttps://github.com/OCA/partner-contact
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 19:40:47
ODOO DEPENDENCIES
ODOO DEPENDENCIES odoo/odoo:
    - contacts
    - base
    - mail
    - base_setup
    - web
    - bus
    - web_tour
    - html_editor
    - sales_team
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES Not have
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION
This module aims to provide generic means to model relations between
partners.

Examples would be 'is sibling of' or 'is friend of', but also 'has
contract X with' or 'is assistant of'. This way, you can encode your
knowledge about your partners directly in your partner list.

Code Analysis

Views touched (7)
XML IDNameModelTypeStatus
form_res_partner_relation res.partner.relation form New
form_res_partner_relation_type res.partner.relation.type form New
search_res_partner_relation res.partner.relation search New
tree_res_partner_relation res.partner.relation list New
tree_res_partner_relation_type res.partner.relation.type list New
view_partner_form partner_multi_relation.view_partner_form res.partner xpath Inherits base.view_partner_form
view_res_partner_filter partner_multi_relation.view_partner_filter res.partner field Inherits base.view_res_partner_filter
Models touched (3)

New fields (7)
  • relation_count Integer
    compute='_compute_relation_count'
  • relation_left_ids One2many → res.partner.relation
    comodel_name='res.partner.relation' copy=False inverse_name='left_partner_id' string='Left relations with current partner'
  • relation_right_ids One2many → res.partner.relation
    comodel_name='res.partner.relation' copy=False inverse_name='right_partner_id' string='Right relations with current partner'
  • search_relation_date Date
    compute=<expr> search='_search_relation_date' string='Relation valid'
  • search_relation_partner_category_id Many2one → res.partner.category
    comodel_name='res.partner.category' compute=<expr> search='_search_relation_partner_category_id' string='Has relation with a partner in category'
  • search_relation_partner_id Many2one → res.partner
    comodel_name='res.partner' compute=<expr> search='_search_relation_partner_id' string='Has relation with'
  • search_relation_type_id Many2one → res.partner.relation.type
    comodel_name='res.partner.relation.type' compute=<expr> search='_search_relation_type_id' string='Has relation of type'
Public methods (2)
  • action_view_relations(self)
  • search(self, domain, offset=0, limit=None, order=None)
    @api.model
    Inject searching for current relation date if we search for relation properties and no explicit date was given.

New fields (13)
  • active Boolean
    default=True help='Records with date_end in the past should be inactive' readonly=True
  • any_partner_id Many2many → res.partner
    comodel_name='res.partner' compute=<expr> search='_search_any_partner_id' string='Partner'
  • date_end Date
    args: 'Ending date'
  • date_start Date
    args: 'Starting date'
  • left_partner_id Many2one → res.partner
    bypass_search_access=True comodel_name='res.partner' ondelete='cascade' required=True string='Source Partner'
  • left_partner_id_domain Binary
    compute='_compute_left_partner_id_domain' default=[]
  • other_partner_id Many2one → res.partner
    comodel_name='res.partner' compute='_compute_other_partner_id' help='Partner shown right when no currently active partner, or connected partnes as seen from current partner.'
  • right_partner_id Many2one → res.partner
    bypass_search_access=True comodel_name='res.partner' ondelete='cascade' required=True string='Destination Partner'
  • right_partner_id_domain Binary
    compute='_compute_right_partner_id_domain' default=[]
  • this_partner_id Many2one → res.partner
    comodel_name='res.partner' compute='_compute_this_partner_id' help='Partner shown left when no currently active partner'
  • type_id Many2one → res.partner.relation.type
    bypass_search_access=True comodel_name='res.partner.relation.type' required=True string='Type'
  • type_id_display Char
    compute='_compute_type_id_display' help='In current context relation is inverse'
  • type_id_domain Binary
    compute='_compute_type_id_domain' default=[]
Public methods (2)
  • create(self, vals_list)
    @api.model_create_multi
    Override create to correct values, before being stored.
  • default_get(self, fields_list)
    @api.model

New fields (9)
  • allow_self Boolean
    default=False help='This relation can be set up with the same partner left and right' string='Reflexive'
  • handle_invalid_onchange Selection
    default='restrict' help='When adding relations criteria like partner type and category are checked.\nHowever when you change the criteria, there might be relations that do not fit the new criteria.\nSpecify how this situation should be handled.' required=True selection=HANDLE_INVALID_ONCHANGE string='Invalid relation handling'
  • is_symmetric Boolean
    default=False help='This relation is the same from right to left as from left to right' string='Symmetric'
  • left_partner_category_id Many2one → res.partner.category
    comodel_name='res.partner.category' string='Left partner category'
  • left_partner_type Selection
    selection='get_partner_types' string='Left partner type'
  • name Char
    required=True translate=True
  • name_inverse Char
    required=True string='Inverse name' translate=True
  • right_partner_category_id Many2one → res.partner.category
    comodel_name='res.partner.category' string='Right partner category'
  • right_partner_type Selection
    selection='get_partner_types' string='Right partner type'
Public methods (5)
  • check_existing(self, vals)
    Check wether records exist that do not fit new criteria.
  • create(self, vals_list)
    @api.model_create_multi
  • get_partner_types(self)
    @api.model
    A partner can be an organisation or an individual.
  • unlink(self)
    Allow delete of relation type, even when connections exist. Relations can be deleted if relation type allows it, so existing relations do not prevent unlink of relation type.
  • write(self, vals)
    Handle existing relations if conditions change.
REPOSITORY
REPOSITORYOCA/partner-contact
GIT
GIThttps://github.com/OCA/partner-contact.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/partner-contact/tree/18.0/partner_multi_relation
VERSION
VERSION 1.0.0
CATEGORY
CATEGORYCustomer Relationship Management
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Camptocamp, Therp BV
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Camptocamp, Therp BV
COMMITTERS
COMMITTERSWeblate, OCA-git-bot, oca-ci, David Moreno, mchechlacz
WEBSITE
WEBSITEhttps://github.com/OCA/partner-contact
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 19:30:10
ODOO DEPENDENCIES
ODOO DEPENDENCIES odoo/odoo:
    - contacts
    - base
    - mail
    - base_setup
    - web
    - bus
    - web_tour
    - html_editor
    - sales_team
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES Not have
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION
This module aims to provide generic means to model relations between
partners.

Examples would be 'is sibling of' or 'is friend of', but also 'has
contract X with' or 'is assistant of'. This way, you can encode your
knowledge about your partners directly in your partner list.

Code Analysis

Views touched (6)
XML IDNameModelTypeStatus
form_res_partner_relation_type res.partner.relation.type form New
search_res_partner_relation_all res.partner.relation.all search New
tree_res_partner_relation_all res.partner.relation.all list New
tree_res_partner_relation_type res.partner.relation.type list New
view_partner_form partner_multi_relation.view_partner_form res.partner xpath Inherits base.view_partner_form
view_res_partner_filter partner_multi_relation.view_partner_filter res.partner field Inherits base.view_res_partner_filter
Models touched (5)

New fields (6)
  • relation_all_ids One2many → res.partner.relation.all
    auto_join=True comodel_name='res.partner.relation.all' copy=False inverse_name='this_partner_id' search=False string='All relations with current partner'
  • relation_count Integer
    compute='_compute_relation_count'
  • search_relation_date Date
    compute=<expr> search='_search_relation_date' string='Relation valid'
  • search_relation_partner_category_id Many2one → res.partner.category
    comodel_name='res.partner.category' compute=<expr> search='_search_related_partner_category_id' string='Has relation with a partner in category'
  • search_relation_partner_id Many2one → res.partner
    comodel_name='res.partner' compute=<expr> search='_search_related_partner_id' string='Has relation with'
  • search_relation_type_id Many2one → res.partner.relation.type.selection
    comodel_name='res.partner.relation.type.selection' compute=<expr> search='_search_relation_type_id' string='Has relation of type'
Public methods (3)
  • action_view_relations(self)
  • get_partner_type(self)
    Get partner type for relation. :return: 'c' for company or 'p' for person :rtype: str
  • search(self, args, offset=0, limit=None, order=None)
    @api.model
    Inject searching for current relation date if we search for relation properties and no explicit date was given.

New fields (5)
  • date_end Date
    args: 'Ending date'
  • date_start Date
    args: 'Starting date'
  • left_partner_id Many2one → res.partner
    auto_join=True comodel_name='res.partner' ondelete='cascade' required=True string='Source Partner'
  • right_partner_id Many2one → res.partner
    auto_join=True comodel_name='res.partner' ondelete='cascade' required=True string='Destination Partner'
  • type_id Many2one → res.partner.relation.type
    auto_join=True comodel_name='res.partner.relation.type' required=True string='Type'
Public methods (1)
  • create(self, vals_list)
    @api.model_create_multi
    Override create to correct values, before being stored.

New fields (11)
  • active Boolean
    help='Records with date_end in the past are inactive' readonly=True
  • any_partner_id Many2many → res.partner
    comodel_name='res.partner' compute=<expr> search='_search_any_partner_id' string='Partner'
  • date_end Date
    args: 'Ending date'
  • date_start Date
    args: 'Starting date'
  • is_inverse Boolean
    help='Inverse relations are from right to left partner.' readonly=True string='Is reverse type?'
  • other_partner_id Many2one → res.partner
    comodel_name='res.partner' required=True
  • res_id Integer
    help='The id of the object in the model this relation is based on.' readonly=True required=True string='Resource ID'
  • res_model Char
    help='The database object this relation is based on.' readonly=True required=True string='Resource Model'
  • this_partner_id Many2one → res.partner
    comodel_name='res.partner' required=True string='One Partner'
  • type_id Many2one → res.partner.relation.type
    comodel_name='res.partner.relation.type' readonly=True required=True string='Underlying Relation Type'
  • type_selection_id Many2one → res.partner.relation.type.selection
    comodel_name='res.partner.relation.type.selection' required=True string='Relation Type'
Public methods (11)
  • create(self, vals_list)
    @api.model_create_multi
    Divert non-problematic creates to underlying table. Create a res.partner.relation but return the converted id.
  • get_base_resource(self)
    Get base resource from res_model and res_id.
  • get_register(self)
  • get_select_specification(self, base_name, is_inverse)
  • onchange_partner_id(self)
    @api.onchange('this_partner_id', 'other_partner_id')
    Set domain on type_selection_id based on partner(s) selected.
  • onchange_type_selection_id(self)
    @api.onchange('type_selection_id')
    Add domain on partners according to category and contact_type.
  • register_specification(self, register, base_name, is_inverse, select_sql)
  • unlink(self)
    For model 'res.partner.relation' call unlink on underlying model.
  • unlink_resource(self, base_resource)
    Delegate unlink to underlying model.
  • write(self, vals)
    For model 'res.partner.relation' call write on underlying model.
  • write_resource(self, base_resource, vals)
    write handled by base resource.

New fields (9)
  • allow_self Boolean
    default=False help='This relation can be set up with the same partner left and right' string='Reflexive'
  • contact_type_left Selection
    selection='get_partner_types' string='Left partner type'
  • contact_type_right Selection
    selection='get_partner_types' string='Right partner type'
  • handle_invalid_onchange Selection
    default='restrict' help='When adding relations criteria like partner type and category are checked.\nHowever when you change the criteria, there might be relations that do not fit the new criteria.\nSpecify how this situation should be handled.' required=True selection=HANDLE_INVALID_ONCHANGE string='Invalid relation handling'
  • is_symmetric Boolean
    default=False help='This relation is the same from right to left as from left to right' string='Symmetric'
  • name Char
    required=True translate=True
  • name_inverse Char
    required=True string='Inverse name' translate=True
  • partner_category_left Many2one → res.partner.category
    comodel_name='res.partner.category' string='Left partner category'
  • partner_category_right Many2one → res.partner.category
    comodel_name='res.partner.category' string='Right partner category'
Public methods (5)
  • check_existing(self, vals)
    Check wether records exist that do not fit new criteria.
  • create(self, vals_list)
    @api.model_create_multi
  • get_partner_types(self)
    @api.model
    A partner can be an organisation or an individual.
  • unlink(self)
    Allow delete of relation type, even when connections exist. Relations can be deleted if relation type allows it, so existing relations do not prevent unlink of relation type.
  • write(self, vals)
    Handle existing relations if conditions change.

New fields (9)
  • allow_self Boolean
    string='Reflexive'
  • contact_type_other Selection
    selection='get_partner_types' string="Other record's partner type"
  • contact_type_this Selection
    selection='get_partner_types' string="Current record's partner type"
  • is_inverse Boolean
    help='Inverse relations are from right to left partner.' string='Is reverse type?'
  • is_symmetric Boolean
    string='Symmetric'
  • name Char
    required=True translate=True
  • partner_category_other Many2one → res.partner.category
    comodel_name='res.partner.category' string="Other record's category"
  • partner_category_this Many2one → res.partner.category
    comodel_name='res.partner.category' string="Current record's category"
  • type_id Many2one → res.partner.relation.type
    comodel_name='res.partner.relation.type'
Public methods (2)
  • get_partner_types(self)
    @api.model
    Partner types are defined by model res.partner.relation.type.
  • name_search(self, name='', args=None, operator='ilike', limit=100)
    @api.model
    Search for name or inverse name in underlying model.
REPOSITORY
REPOSITORYOCA/partner-contact
GIT
GIThttps://github.com/OCA/partner-contact.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/partner-contact/tree/17.0/partner_multi_relation
VERSION
VERSION 1.0.0
CATEGORY
CATEGORYCustomer Relationship Management
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Camptocamp, Therp BV
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Camptocamp, Therp BV
COMMITTERS
COMMITTERSWeblate, OCA-git-bot, oca-ci, Jérémy Didderen, Joachim David
WEBSITE
WEBSITEhttps://github.com/OCA/partner-contact
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 19:20:04
ODOO DEPENDENCIES
ODOO DEPENDENCIES odoo/odoo:
    - contacts
    - base
    - mail
    - base_setup
    - web
    - bus
    - web_tour
    - sales_team
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES Not have
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION
This module aims to provide generic means to model relations between
partners.

Examples would be 'is sibling of' or 'is friend of', but also 'has
contract X with' or 'is assistant of'. This way, you can encode your
knowledge about your partners directly in your partner list.

Code Analysis

Views touched (6)
XML IDNameModelTypeStatus
form_res_partner_relation_type res.partner.relation.type form New
search_res_partner_relation_all res.partner.relation.all search New
tree_res_partner_relation_all res.partner.relation.all tree New
tree_res_partner_relation_type res.partner.relation.type tree New
view_partner_form partner_multi_relation.view_partner_form res.partner xpath Inherits base.view_partner_form
view_res_partner_filter partner_multi_relation.view_partner_filter res.partner field Inherits base.view_res_partner_filter
Models touched (5)

New fields (6)
  • relation_all_ids One2many → res.partner.relation.all
    auto_join=True comodel_name='res.partner.relation.all' copy=False inverse_name='this_partner_id' search=False string='All relations with current partner'
  • relation_count Integer
    compute='_compute_relation_count'
  • search_relation_date Date
    compute=<expr> search='_search_relation_date' string='Relation valid'
  • search_relation_partner_category_id Many2one → res.partner.category
    comodel_name='res.partner.category' compute=<expr> search='_search_related_partner_category_id' string='Has relation with a partner in category'
  • search_relation_partner_id Many2one → res.partner
    comodel_name='res.partner' compute=<expr> search='_search_related_partner_id' string='Has relation with'
  • search_relation_type_id Many2one → res.partner.relation.type.selection
    comodel_name='res.partner.relation.type.selection' compute=<expr> search='_search_relation_type_id' string='Has relation of type'
Public methods (3)
  • action_view_relations(self)
  • get_partner_type(self)
    Get partner type for relation. :return: 'c' for company or 'p' for person :rtype: str
  • search(self, args, offset=0, limit=None, order=None)
    @api.model
    Inject searching for current relation date if we search for relation properties and no explicit date was given.

New fields (5)
  • date_end Date
    args: 'Ending date'
  • date_start Date
    args: 'Starting date'
  • left_partner_id Many2one → res.partner
    auto_join=True comodel_name='res.partner' ondelete='cascade' required=True string='Source Partner'
  • right_partner_id Many2one → res.partner
    auto_join=True comodel_name='res.partner' ondelete='cascade' required=True string='Destination Partner'
  • type_id Many2one → res.partner.relation.type
    auto_join=True comodel_name='res.partner.relation.type' required=True string='Type'
Public methods (1)
  • create(self, vals_list)
    @api.model_create_multi
    Override create to correct values, before being stored.

New fields (11)
  • active Boolean
    help='Records with date_end in the past are inactive' readonly=True
  • any_partner_id Many2many → res.partner
    comodel_name='res.partner' compute=<expr> search='_search_any_partner_id' string='Partner'
  • date_end Date
    args: 'Ending date'
  • date_start Date
    args: 'Starting date'
  • is_inverse Boolean
    help='Inverse relations are from right to left partner.' readonly=True string='Is reverse type?'
  • other_partner_id Many2one → res.partner
    comodel_name='res.partner' required=True
  • res_id Integer
    help='The id of the object in the model this relation is based on.' readonly=True required=True string='Resource ID'
  • res_model Char
    help='The database object this relation is based on.' readonly=True required=True string='Resource Model'
  • this_partner_id Many2one → res.partner
    comodel_name='res.partner' required=True string='One Partner'
  • type_id Many2one → res.partner.relation.type
    comodel_name='res.partner.relation.type' readonly=True required=True string='Underlying Relation Type'
  • type_selection_id Many2one → res.partner.relation.type.selection
    comodel_name='res.partner.relation.type.selection' required=True string='Relation Type'
Public methods (11)
  • create(self, vals_list)
    @api.model_create_multi
    Divert non-problematic creates to underlying table. Create a res.partner.relation but return the converted id.
  • get_base_resource(self)
    Get base resource from res_model and res_id.
  • get_register(self)
  • get_select_specification(self, base_name, is_inverse)
  • onchange_partner_id(self)
    @api.onchange('this_partner_id', 'other_partner_id')
    Set domain on type_selection_id based on partner(s) selected.
  • onchange_type_selection_id(self)
    @api.onchange('type_selection_id')
    Add domain on partners according to category and contact_type.
  • register_specification(self, register, base_name, is_inverse, select_sql)
  • unlink(self)
    For model 'res.partner.relation' call unlink on underlying model.
  • unlink_resource(self, base_resource)
    Delegate unlink to underlying model.
  • write(self, vals)
    For model 'res.partner.relation' call write on underlying model.
  • write_resource(self, base_resource, vals)
    write handled by base resource.

New fields (9)
  • allow_self Boolean
    default=False help='This relation can be set up with the same partner left and right' string='Reflexive'
  • contact_type_left Selection
    selection='get_partner_types' string='Left partner type'
  • contact_type_right Selection
    selection='get_partner_types' string='Right partner type'
  • handle_invalid_onchange Selection
    default='restrict' help='When adding relations criteria like partner type and category are checked.\nHowever when you change the criteria, there might be relations that do not fit the new criteria.\nSpecify how this situation should be handled.' required=True selection=HANDLE_INVALID_ONCHANGE string='Invalid relation handling'
  • is_symmetric Boolean
    default=False help='This relation is the same from right to left as from left to right' string='Symmetric'
  • name Char
    required=True translate=True
  • name_inverse Char
    required=True string='Inverse name' translate=True
  • partner_category_left Many2one → res.partner.category
    comodel_name='res.partner.category' string='Left partner category'
  • partner_category_right Many2one → res.partner.category
    comodel_name='res.partner.category' string='Right partner category'
Public methods (5)
  • check_existing(self, vals)
    Check wether records exist that do not fit new criteria.
  • create(self, vals_list)
    @api.model_create_multi
  • get_partner_types(self)
    @api.model
    A partner can be an organisation or an individual.
  • unlink(self)
    Allow delete of relation type, even when connections exist. Relations can be deleted if relation type allows it.
  • write(self, vals)
    Handle existing relations if conditions change.

New fields (9)
  • allow_self Boolean
    string='Reflexive'
  • contact_type_other Selection
    selection='get_partner_types' string="Other record's partner type"
  • contact_type_this Selection
    selection='get_partner_types' string="Current record's partner type"
  • is_inverse Boolean
    help='Inverse relations are from right to left partner.' string='Is reverse type?'
  • is_symmetric Boolean
    string='Symmetric'
  • name Char
    required=True translate=True
  • partner_category_other Many2one → res.partner.category
    comodel_name='res.partner.category' string="Other record's category"
  • partner_category_this Many2one → res.partner.category
    comodel_name='res.partner.category' string="Current record's category"
  • type_id Many2one → res.partner.relation.type
    comodel_name='res.partner.relation.type'
Public methods (2)
  • get_partner_types(self)
    @api.model
    Partner types are defined by model res.partner.relation.type.
  • name_search(self, name='', args=None, operator='ilike', limit=100)
    @api.model
    Search for name or inverse name in underlying model.
REPOSITORY
REPOSITORYOCA/partner-contact
GIT
GIThttps://github.com/OCA/partner-contact.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/partner-contact/tree/16.0/partner_multi_relation
VERSION
VERSION 1.5.0
CATEGORY
CATEGORYCustomer Relationship Management
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Camptocamp, Therp BV
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Camptocamp, Therp BV
COMMITTERS
COMMITTERSRonald Portier, Ronald Portier (Therp BV), Weblate, OCA-git-bot, oca-ci, Marie Lejeune
WEBSITE
WEBSITEhttps://github.com/OCA/partner-contact
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 00:53:44
ODOO DEPENDENCIES
ODOO DEPENDENCIES odoo/odoo:
    - contacts
    - base
    - mail
    - base_setup
    - web
    - bus
    - web_tour
    - sales_team
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES Not have
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION

Code Analysis

Views touched (6)
XML IDNameModelTypeStatus
form_res_partner_relation_type res.partner.relation.type form New
search_res_partner_relation_all res.partner.relation.all search New
tree_res_partner_relation_all res.partner.relation.all tree New
tree_res_partner_relation_type res.partner.relation.type tree New
view_partner_form partner_multi_relation.view_partner_form res.partner xpath Inherits base.view_partner_form
view_res_partner_filter partner_multi_relation.view_partner_filter res.partner field Inherits base.view_res_partner_filter
Models touched (5)

New fields (6)
  • relation_all_ids One2many → res.partner.relation.all
    auto_join=True comodel_name='res.partner.relation.all' copy=False inverse_name='this_partner_id' search=False string='All relations with current partner'
  • relation_count Integer
    compute='_compute_relation_count'
  • search_relation_date Date
    compute=<expr> search='_search_relation_date' string='Relation valid'
  • search_relation_partner_category_id Many2one → res.partner.category
    comodel_name='res.partner.category' compute=<expr> search='_search_related_partner_category_id' string='Has relation with a partner in category'
  • search_relation_partner_id Many2one → res.partner
    comodel_name='res.partner' compute=<expr> search='_search_related_partner_id' string='Has relation with'
  • search_relation_type_id Many2one → res.partner.relation.type.selection
    comodel_name='res.partner.relation.type.selection' compute=<expr> search='_search_relation_type_id' string='Has relation of type'
Public methods (3)
  • action_view_relations(self)
  • get_partner_type(self)
    Get partner type for relation. :return: 'c' for company or 'p' for person :rtype: str
  • search(self, args, offset=0, limit=None, order=None, count=False)
    @api.model
    Inject searching for current relation date if we search for relation properties and no explicit date was given.

New fields (5)
  • date_end Date
    args: 'Ending date'
  • date_start Date
    args: 'Starting date'
  • left_partner_id Many2one → res.partner
    auto_join=True comodel_name='res.partner' ondelete='cascade' required=True string='Source Partner'
  • right_partner_id Many2one → res.partner
    auto_join=True comodel_name='res.partner' ondelete='cascade' required=True string='Destination Partner'
  • type_id Many2one → res.partner.relation.type
    auto_join=True comodel_name='res.partner.relation.type' required=True string='Type'
Public methods (1)
  • create(self, vals_list)
    @api.model_create_multi
    Override create to correct values, before being stored.

New fields (11)
  • active Boolean
    help='Records with date_end in the past are inactive, as well as records for inactive partners.' readonly=True
  • any_partner_id Many2many → res.partner
    comodel_name='res.partner' compute=<expr> search='_search_any_partner_id' string='Partner'
  • date_end Date
    args: 'Ending date'
  • date_start Date
    args: 'Starting date'
  • is_inverse Boolean
    help='Inverse relations are from right to left partner.' readonly=True string='Is reverse type?'
  • other_partner_id Many2one → res.partner
    comodel_name='res.partner' required=True
  • res_id Integer
    help='The id of the object in the model this relation is based on.' readonly=True required=True string='Resource ID'
  • res_model Char
    help='The database object this relation is based on.' readonly=True required=True string='Resource Model'
  • this_partner_id Many2one → res.partner
    comodel_name='res.partner' required=True string='One Partner'
  • type_id Many2one → res.partner.relation.type
    comodel_name='res.partner.relation.type' readonly=True required=True string='Underlying Relation Type'
  • type_selection_id Many2one → res.partner.relation.type.selection
    comodel_name='res.partner.relation.type.selection' required=True string='Relation Type'
Public methods (12)
  • create(self, vals_list)
    @api.model_create_multi
    Divert non-problematic creates to underlying table. Create a res.partner.relation but return the converted id.
  • get_base_resource(self)
    Get base resource from res_model and res_id.
  • get_register(self)
  • get_select_specification(self, base_name, is_inverse)
  • name_get(self)
  • onchange_partner_id(self)
    @api.onchange('this_partner_id', 'other_partner_id')
    Set domain on type_selection_id based on partner(s) selected.
  • onchange_type_selection_id(self)
    @api.onchange('type_selection_id')
    Add domain on partners according to category and contact_type.
  • register_specification(self, register, base_name, is_inverse, select_sql)
  • unlink(self)
    For model 'res.partner.relation' call unlink on underlying model.
  • unlink_resource(self, base_resource)
    Delegate unlink to underlying model.
  • write(self, vals)
    For model 'res.partner.relation' call write on underlying model.
  • write_resource(self, base_resource, vals)
    write handled by base resource.

New fields (10)
  • active Boolean
    default=True
  • allow_self Boolean
    default=False help='This relation can be set up with the same partner left and right' string='Reflexive'
  • contact_type_left Selection
    selection='get_partner_types' string='Left partner type'
  • contact_type_right Selection
    selection='get_partner_types' string='Right partner type'
  • handle_invalid_onchange Selection
    default='restrict' help='When adding relations criteria like partner type and category are checked.\nHowever when you change the criteria, there might be relations that do not fit the new criteria.\nSpecify how this situation should be handled.' required=True selection=HANDLE_INVALID_ONCHANGE string='Invalid relation handling'
  • is_symmetric Boolean
    default=False help='This relation is the same from right to left as from left to right' string='Symmetric'
  • name Char
    required=True translate=True
  • name_inverse Char
    required=True string='Inverse name' translate=True
  • partner_category_left Many2one → res.partner.category
    comodel_name='res.partner.category' string='Left partner category'
  • partner_category_right Many2one → res.partner.category
    comodel_name='res.partner.category' string='Right partner category'
Public methods (5)
  • check_existing(self, vals)
    Check whether records exist that do not fit new criteria.
  • create(self, vals_list)
    @api.model_create_multi
  • get_partner_types(self)
    @api.model
    A partner can be an organisation or an individual.
  • unlink(self)
    Allow delete of relation type, even when connections exist. Relations can be deleted if relation type allows it.
  • write(self, vals)
    Handle existing relations if conditions change.

New fields (9)
  • allow_self Boolean
    string='Reflexive'
  • contact_type_other Selection
    selection='get_partner_types' string="Other record's partner type"
  • contact_type_this Selection
    selection='get_partner_types' string="Current record's partner type"
  • is_inverse Boolean
    help='Inverse relations are from right to left partner.' string='Is reverse type?'
  • is_symmetric Boolean
    string='Symmetric'
  • name Char
    required=True translate=True
  • partner_category_other Many2one → res.partner.category
    comodel_name='res.partner.category' string="Other record's category"
  • partner_category_this Many2one → res.partner.category
    comodel_name='res.partner.category' string="Current record's category"
  • type_id Many2one → res.partner.relation.type
    comodel_name='res.partner.relation.type'
Public methods (3)
  • get_partner_types(self)
    @api.model
    Partner types are defined by model res.partner.relation.type.
  • name_get(self)
    Get name or name_inverse from underlying model.
  • name_search(self, name='', args=None, operator='ilike', limit=100)
    @api.model
    Search for name or inverse name in underlying model.
REPOSITORY
REPOSITORYOCA/partner-contact
GIT
GIThttps://github.com/OCA/partner-contact.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/partner-contact/tree/15.0/partner_multi_relation
VERSION
VERSION 1.0.1
CATEGORY
CATEGORYCustomer Relationship Management
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Camptocamp, Therp BV
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Camptocamp, Therp BV
COMMITTERS
COMMITTERSGitHub, Weblate, OCA-git-bot, Raf Ven, oca-ci
WEBSITE
WEBSITEhttps://github.com/OCA/partner-contact
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 00:46:32
ODOO DEPENDENCIES
ODOO DEPENDENCIES odoo/odoo:
    - contacts
    - base
    - mail
    - base_setup
    - web
    - bus
    - web_tour
    - sales_team
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES Not have
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION

Code Analysis

Views touched (6)
XML IDNameModelTypeStatus
form_res_partner_relation_type res.partner.relation.type form New
search_res_partner_relation_all res.partner.relation.all search New
tree_res_partner_relation_all res.partner.relation.all tree New
tree_res_partner_relation_type res.partner.relation.type tree New
view_partner_form partner_multi_relation.view_partner_form res.partner xpath Inherits base.view_partner_form
view_res_partner_filter partner_multi_relation.view_partner_filter res.partner field Inherits base.view_res_partner_filter
Models touched (5)

New fields (6)
  • relation_all_ids One2many → res.partner.relation.all
    auto_join=True comodel_name='res.partner.relation.all' copy=False inverse_name='this_partner_id' search=False string='All relations with current partner'
  • relation_count Integer
    compute='_compute_relation_count'
  • search_relation_date Date
    compute=<expr> search='_search_relation_date' string='Relation valid'
  • search_relation_partner_category_id Many2one → res.partner.category
    comodel_name='res.partner.category' compute=<expr> search='_search_related_partner_category_id' string='Has relation with a partner in category'
  • search_relation_partner_id Many2one → res.partner
    comodel_name='res.partner' compute=<expr> search='_search_related_partner_id' string='Has relation with'
  • search_relation_type_id Many2one → res.partner.relation.type.selection
    comodel_name='res.partner.relation.type.selection' compute=<expr> search='_search_relation_type_id' string='Has relation of type'
Public methods (3)
  • action_view_relations(self)
  • get_partner_type(self)
    Get partner type for relation. :return: 'c' for company or 'p' for person :rtype: str
  • search(self, args, offset=0, limit=None, order=None, count=False)
    @api.model
    Inject searching for current relation date if we search for relation properties and no explicit date was given.

New fields (5)
  • date_end Date
    args: 'Ending date'
  • date_start Date
    args: 'Starting date'
  • left_partner_id Many2one → res.partner
    auto_join=True comodel_name='res.partner' ondelete='cascade' required=True string='Source Partner'
  • right_partner_id Many2one → res.partner
    auto_join=True comodel_name='res.partner' ondelete='cascade' required=True string='Destination Partner'
  • type_id Many2one → res.partner.relation.type
    auto_join=True comodel_name='res.partner.relation.type' required=True string='Type'
Public methods (1)
  • create(self, vals)
    @api.model
    Override create to correct values, before being stored.

New fields (11)
  • active Boolean
    help='Records with date_end in the past are inactive' readonly=True
  • any_partner_id Many2many → res.partner
    comodel_name='res.partner' compute=<expr> search='_search_any_partner_id' string='Partner'
  • date_end Date
    args: 'Ending date'
  • date_start Date
    args: 'Starting date'
  • is_inverse Boolean
    help='Inverse relations are from right to left partner.' readonly=True string='Is reverse type?'
  • other_partner_id Many2one → res.partner
    comodel_name='res.partner' required=True
  • res_id Integer
    help='The id of the object in the model this relation is based on.' readonly=True required=True string='Resource ID'
  • res_model Char
    help='The database object this relation is based on.' readonly=True required=True string='Resource Model'
  • this_partner_id Many2one → res.partner
    comodel_name='res.partner' required=True string='One Partner'
  • type_id Many2one → res.partner.relation.type
    comodel_name='res.partner.relation.type' readonly=True required=True string='Underlying Relation Type'
  • type_selection_id Many2one → res.partner.relation.type.selection
    comodel_name='res.partner.relation.type.selection' required=True string='Relation Type'
Public methods (13)
  • create(self, vals)
    @api.model
    Divert non-problematic creates to underlying table. Create a res.partner.relation but return the converted id.
  • create_resource(self, vals, type_selection)
    @api.model
  • get_base_resource(self)
    Get base resource from res_model and res_id.
  • get_register(self)
  • get_select_specification(self, base_name, is_inverse)
  • name_get(self)
  • onchange_partner_id(self)
    @api.onchange('this_partner_id', 'other_partner_id')
    Set domain on type_selection_id based on partner(s) selected.
  • onchange_type_selection_id(self)
    @api.onchange('type_selection_id')
    Add domain on partners according to category and contact_type.
  • register_specification(self, register, base_name, is_inverse, select_sql)
  • unlink(self)
    For model 'res.partner.relation' call unlink on underlying model.
  • unlink_resource(self, base_resource)
    Delegate unlink to underlying model.
  • write(self, vals)
    For model 'res.partner.relation' call write on underlying model.
  • write_resource(self, base_resource, vals)
    write handled by base resource.

New fields (9)
  • allow_self Boolean
    default=False help='This relation can be set up with the same partner left and right' string='Reflexive'
  • contact_type_left Selection
    selection='get_partner_types' string='Left partner type'
  • contact_type_right Selection
    selection='get_partner_types' string='Right partner type'
  • handle_invalid_onchange Selection
    default='restrict' help='When adding relations criteria like partner type and category are checked.\nHowever when you change the criteria, there might be relations that do not fit the new criteria.\nSpecify how this situation should be handled.' required=True selection=HANDLE_INVALID_ONCHANGE string='Invalid relation handling'
  • is_symmetric Boolean
    default=False help='This relation is the same from right to left as from left to right' string='Symmetric'
  • name Char
    required=True translate=True
  • name_inverse Char
    required=True string='Inverse name' translate=True
  • partner_category_left Many2one → res.partner.category
    comodel_name='res.partner.category' string='Left partner category'
  • partner_category_right Many2one → res.partner.category
    comodel_name='res.partner.category' string='Right partner category'
Public methods (5)
  • check_existing(self, vals)
    Check wether records exist that do not fit new criteria.
  • create(self, vals)
    @api.model
  • get_partner_types(self)
    @api.model
    A partner can be an organisation or an individual.
  • unlink(self)
    Allow delete of relation type, even when connections exist. Relations can be deleted if relation type allows it.
  • write(self, vals)
    Handle existing relations if conditions change.

New fields (9)
  • allow_self Boolean
    string='Reflexive'
  • contact_type_other Selection
    selection='get_partner_types' string="Other record's partner type"
  • contact_type_this Selection
    selection='get_partner_types' string="Current record's partner type"
  • is_inverse Boolean
    help='Inverse relations are from right to left partner.' string='Is reverse type?'
  • is_symmetric Boolean
    string='Symmetric'
  • name Char
  • partner_category_other Many2one → res.partner.category
    comodel_name='res.partner.category' string="Other record's category"
  • partner_category_this Many2one → res.partner.category
    comodel_name='res.partner.category' string="Current record's category"
  • type_id Many2one → res.partner.relation.type
    comodel_name='res.partner.relation.type'
Public methods (3)
  • get_partner_types(self)
    @api.model
    Partner types are defined by model res.partner.relation.type.
  • name_get(self)
    Get name or name_inverse from underlying model.
  • name_search(self, name='', args=None, operator='ilike', limit=100)
    @api.model
    Search for name or inverse name in underlying model.
REPOSITORY
REPOSITORYOCA/partner-contact
GIT
GIThttps://github.com/OCA/partner-contact.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/partner-contact/tree/14.0/partner_multi_relation
VERSION
VERSION 1.1.0
CATEGORY
CATEGORYCustomer Relationship Management
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Camptocamp, Therp BV
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Camptocamp, Therp BV
COMMITTERS
COMMITTERSOCA Transbot, oca-travis, Weblate, OCA-git-bot, Raf Ven, Tobias Zehntner, Alessio Renda
WEBSITE
WEBSITEhttps://github.com/OCA/partner-contact
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 00:40:56
ODOO DEPENDENCIES
ODOO DEPENDENCIES odoo/odoo:
    - contacts
    - base
    - mail
    - base_setup
    - web
    - bus
    - web_tour
    - sales_team
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES Not have
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION

Code Analysis

Views touched (6)
XML IDNameModelTypeStatus
form_res_partner_relation_type res.partner.relation.type form New
search_res_partner_relation_all res.partner.relation.all search New
tree_res_partner_relation_all res.partner.relation.all tree New
tree_res_partner_relation_type res.partner.relation.type tree New
view_partner_form partner_multi_relation.view_partner_form res.partner xpath Inherits base.view_partner_form
view_res_partner_filter partner_multi_relation.view_partner_filter res.partner field Inherits base.view_res_partner_filter
Models touched (5)

New fields (6)
  • relation_all_ids One2many → res.partner.relation.all
    auto_join=True comodel_name='res.partner.relation.all' copy=False inverse_name='this_partner_id' search=False string='All relations with current partner'
  • relation_count Integer
    compute='_compute_relation_count' string='Relation Count'
  • search_relation_date Date
    compute=<expr> search='_search_relation_date' string='Relation valid'
  • search_relation_partner_category_id Many2one → res.partner.category
    comodel_name='res.partner.category' compute=<expr> search='_search_related_partner_category_id' string='Has relation with a partner in category'
  • search_relation_partner_id Many2one → res.partner
    comodel_name='res.partner' compute=<expr> search='_search_related_partner_id' string='Has relation with'
  • search_relation_type_id Many2one → res.partner.relation.type.selection
    comodel_name='res.partner.relation.type.selection' compute=<expr> search='_search_relation_type_id' string='Has relation of type'
Public methods (3)
  • action_view_relations(self)
  • get_partner_type(self)
    Get partner type for relation. :return: 'c' for company or 'p' for person :rtype: str
  • search(self, args, offset=0, limit=None, order=None, count=False)
    @api.model
    Inject searching for current relation date if we search for relation properties and no explicit date was given.

New fields (5)
  • date_end Date
    args: 'Ending date'
  • date_start Date
    args: 'Starting date'
  • left_partner_id Many2one → res.partner
    auto_join=True comodel_name='res.partner' ondelete='cascade' required=True string='Source Partner'
  • right_partner_id Many2one → res.partner
    auto_join=True comodel_name='res.partner' ondelete='cascade' required=True string='Destination Partner'
  • type_id Many2one → res.partner.relation.type
    auto_join=True comodel_name='res.partner.relation.type' required=True string='Type'
Public methods (1)
  • create(self, vals)
    @api.model
    Override create to correct values, before being stored.

New fields (11)
  • active Boolean
    help='Records with date_end in the past are inactive' readonly=True string='Active'
  • any_partner_id Many2many → res.partner
    comodel_name='res.partner' compute=<expr> search='_search_any_partner_id' string='Partner'
  • date_end Date
    args: 'Ending date'
  • date_start Date
    args: 'Starting date'
  • is_inverse Boolean
    help='Inverse relations are from right to left partner.' readonly=True string='Is reverse type?'
  • other_partner_id Many2one → res.partner
    comodel_name='res.partner' required=True string='Other Partner'
  • res_id Integer
    help='The id of the object in the model this relation is based on.' readonly=True required=True string='Resource ID'
  • res_model Char
    help='The database object this relation is based on.' readonly=True required=True string='Resource Model'
  • this_partner_id Many2one → res.partner
    comodel_name='res.partner' required=True string='One Partner'
  • type_id Many2one → res.partner.relation.type
    comodel_name='res.partner.relation.type' readonly=True required=True string='Underlying Relation Type'
  • type_selection_id Many2one → res.partner.relation.type.selection
    comodel_name='res.partner.relation.type.selection' required=True string='Relation Type'
Public methods (13)
  • create(self, vals)
    @api.model
    Divert non-problematic creates to underlying table. Create a res.partner.relation but return the converted id.
  • create_resource(self, vals, type_selection)
    @api.model
  • get_base_resource(self)
    Get base resource from res_model and res_id.
  • get_register(self)
  • get_select_specification(self, base_name, is_inverse)
  • name_get(self)
  • onchange_partner_id(self)
    @api.onchange('this_partner_id', 'other_partner_id')
    Set domain on type_selection_id based on partner(s) selected.
  • onchange_type_selection_id(self)
    @api.onchange('type_selection_id')
    Add domain on partners according to category and contact_type.
  • register_specification(self, register, base_name, is_inverse, select_sql)
  • unlink(self)
    For model 'res.partner.relation' call unlink on underlying model.
  • unlink_resource(self, base_resource)
    Delegate unlink to underlying model.
  • write(self, vals)
    For model 'res.partner.relation' call write on underlying model.
  • write_resource(self, base_resource, vals)
    write handled by base resource.

New fields (9)
  • allow_self Boolean
    default=False help='This relation can be set up with the same partner left and right' string='Reflexive'
  • contact_type_left Selection
    selection='get_partner_types' string='Left partner type'
  • contact_type_right Selection
    selection='get_partner_types' string='Right partner type'
  • handle_invalid_onchange Selection
    default='restrict' help='When adding relations criteria like partner type and category are checked.\nHowever when you change the criteria, there might be relations that do not fit the new criteria.\nSpecify how this situation should be handled.' required=True selection=HANDLE_INVALID_ONCHANGE string='Invalid relation handling'
  • is_symmetric Boolean
    default=False help='This relation is the same from right to left as from left to right' string='Symmetric'
  • name Char
    required=True string='Name' translate=True
  • name_inverse Char
    required=True string='Inverse name' translate=True
  • partner_category_left Many2one → res.partner.category
    comodel_name='res.partner.category' string='Left partner category'
  • partner_category_right Many2one → res.partner.category
    comodel_name='res.partner.category' string='Right partner category'
Public methods (5)
  • check_existing(self, vals)
    Check wether records exist that do not fit new criteria.
  • create(self, vals)
    @api.model
  • get_partner_types(self)
    @api.model
    A partner can be an organisation or an individual.
  • unlink(self)
    Allow delete of relation type, even when connections exist. Relations can be deleted if relation type allows it.
  • write(self, vals)
    Handle existing relations if conditions change.

New fields (9)
  • allow_self Boolean
    string='Reflexive'
  • contact_type_other Selection
    selection='get_partner_types' string="Other record's partner type"
  • contact_type_this Selection
    selection='get_partner_types' string="Current record's partner type"
  • is_inverse Boolean
    help='Inverse relations are from right to left partner.' string='Is reverse type?'
  • is_symmetric Boolean
    string='Symmetric'
  • name Char
    args: 'Name'
  • partner_category_other Many2one → res.partner.category
    comodel_name='res.partner.category' string="Other record's category"
  • partner_category_this Many2one → res.partner.category
    comodel_name='res.partner.category' string="Current record's category"
  • type_id Many2one → res.partner.relation.type
    comodel_name='res.partner.relation.type' string='Type'
Public methods (3)
  • get_partner_types(self)
    @api.model
    Partner types are defined by model res.partner.relation.type.
  • name_get(self)
    Get name or name_inverse from underlying model.
  • name_search(self, name='', args=None, operator='ilike', limit=100)
    @api.model
    Search for name or inverse name in underlying model.
REPOSITORY
REPOSITORYOCA/partner-contact
GIT
GIThttps://github.com/OCA/partner-contact.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/partner-contact/tree/13.0/partner_multi_relation
VERSION
VERSION 1.1.0
CATEGORY
CATEGORYCustomer Relationship Management
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Camptocamp, Therp BV
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Camptocamp, Therp BV
COMMITTERS
COMMITTERSRonald Portier, OCA Transbot, oca-travis, Weblate, OCA-git-bot, mara1
WEBSITE
WEBSITEhttps://github.com/OCA/partner-contact
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 00:34:11
ODOO DEPENDENCIES
ODOO DEPENDENCIES odoo/odoo:
    - contacts
    - base
    - mail
    - base_setup
    - web
    - bus
    - web_tour
    - sales_team
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES Not have
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION

Code Analysis

Views touched (6)
XML IDNameModelTypeStatus
form_res_partner_relation_type res.partner.relation.type form New
search_res_partner_relation_all res.partner.relation.all search New
tree_res_partner_relation_all res.partner.relation.all tree New
tree_res_partner_relation_type res.partner.relation.type tree New
view_partner_form partner_multi_relation.view_partner_form res.partner xpath Inherits base.view_partner_form
view_res_partner_filter partner_multi_relation.view_partner_filter res.partner field Inherits base.view_res_partner_filter
Models touched (5)

New fields (6)
  • relation_all_ids One2many → res.partner.relation.all
    auto_join=True comodel_name='res.partner.relation.all' copy=False inverse_name='this_partner_id' selectable=False string='All relations with current partner'
  • relation_count Integer
    compute='_compute_relation_count' string='Relation Count'
  • search_relation_date Date
    compute=<expr> search='_search_relation_date' string='Relation valid'
  • search_relation_partner_category_id Many2one → res.partner.category
    comodel_name='res.partner.category' compute=<expr> search='_search_related_partner_category_id' string='Has relation with a partner in category'
  • search_relation_partner_id Many2one → res.partner
    comodel_name='res.partner' compute=<expr> search='_search_related_partner_id' string='Has relation with'
  • search_relation_type_id Many2one → res.partner.relation.type.selection
    comodel_name='res.partner.relation.type.selection' compute=<expr> search='_search_relation_type_id' string='Has relation of type'
Public methods (3)
  • action_view_relations(self)
  • get_partner_type(self)
    Get partner type for relation. :return: 'c' for company or 'p' for person :rtype: str
  • search(self, args, offset=0, limit=None, order=None, count=False)
    @api.model
    Inject searching for current relation date if we search for relation properties and no explicit date was given.

New fields (5)
  • date_end Date
    args: 'Ending date'
  • date_start Date
    args: 'Starting date'
  • left_partner_id Many2one → res.partner
    auto_join=True comodel_name='res.partner' ondelete='cascade' required=True string='Source Partner'
  • right_partner_id Many2one → res.partner
    auto_join=True comodel_name='res.partner' ondelete='cascade' required=True string='Destination Partner'
  • type_id Many2one → res.partner.relation.type
    auto_join=True comodel_name='res.partner.relation.type' required=True string='Type'
Public methods (1)
  • create(self, vals)
    @api.model
    Override create to correct values, before being stored.

New fields (11)
  • active Boolean
    help='Records with date_end in the past are inactive' readonly=True string='Active'
  • any_partner_id Many2many → res.partner
    comodel_name='res.partner' compute=<expr> search='_search_any_partner_id' string='Partner'
  • date_end Date
    args: 'Ending date'
  • date_start Date
    args: 'Starting date'
  • is_inverse Boolean
    help='Inverse relations are from right to left partner.' readonly=True string='Is reverse type?'
  • other_partner_id Many2one → res.partner
    comodel_name='res.partner' required=True string='Other Partner'
  • res_id Integer
    help='The id of the object in the model this relation is based on.' readonly=True required=True string='Resource ID'
  • res_model Char
    help='The database object this relation is based on.' readonly=True required=True string='Resource Model'
  • this_partner_id Many2one → res.partner
    comodel_name='res.partner' required=True string='One Partner'
  • type_id Many2one → res.partner.relation.type
    comodel_name='res.partner.relation.type' readonly=True required=True string='Underlying Relation Type'
  • type_selection_id Many2one → res.partner.relation.type.selection
    comodel_name='res.partner.relation.type.selection' required=True string='Relation Type'
Public methods (13)
  • create(self, vals)
    @api.model
    Divert non-problematic creates to underlying table. Create a res.partner.relation but return the converted id.
  • create_resource(self, vals, type_selection)
    @api.model
  • get_base_resource(self)
    Get base resource from res_model and res_id.
  • get_register(self)
  • get_select_specification(self, base_name, is_inverse)
  • name_get(self)
  • onchange_partner_id(self)
    @api.onchange('this_partner_id', 'other_partner_id')
    Set domain on type_selection_id based on partner(s) selected.
  • onchange_type_selection_id(self)
    @api.onchange('type_selection_id')
    Add domain on partners according to category and contact_type.
  • register_specification(self, register, base_name, is_inverse, select_sql)
  • unlink(self)
    For model 'res.partner.relation' call unlink on underlying model.
  • unlink_resource(self, base_resource)
    Delegate unlink to underlying model.
  • write(self, vals)
    For model 'res.partner.relation' call write on underlying model.
  • write_resource(self, base_resource, vals)
    write handled by base resource.

New fields (9)
  • allow_self Boolean
    default=False help='This relation can be set up with the same partner left and right' string='Reflexive'
  • contact_type_left Selection
    selection='get_partner_types' string='Left partner type'
  • contact_type_right Selection
    selection='get_partner_types' string='Right partner type'
  • handle_invalid_onchange Selection
    default='restrict' help='When adding relations criteria like partner type and category are checked.\nHowever when you change the criteria, there might be relations that do not fit the new criteria.\nSpecify how this situation should be handled.' required=True selection=HANDLE_INVALID_ONCHANGE string='Invalid relation handling'
  • is_symmetric Boolean
    default=False help='This relation is the same from right to left as from left to right' string='Symmetric'
  • name Char
    required=True string='Name' translate=True
  • name_inverse Char
    required=True string='Inverse name' translate=True
  • partner_category_left Many2one → res.partner.category
    comodel_name='res.partner.category' string='Left partner category'
  • partner_category_right Many2one → res.partner.category
    comodel_name='res.partner.category' string='Right partner category'
Public methods (5)
  • check_existing(self, vals)
    Check wether records exist that do not fit new criteria.
  • create(self, vals)
    @api.model
  • get_partner_types(self)
    @api.model
    A partner can be an organisation or an individual.
  • unlink(self)
    Allow delete of relation type, even when connections exist. Relations can be deleted if relation type allows it.
  • write(self, vals)
    Handle existing relations if conditions change.

New fields (9)
  • allow_self Boolean
    string='Reflexive'
  • contact_type_other Selection
    selection='get_partner_types' string="Other record's partner type"
  • contact_type_this Selection
    selection='get_partner_types' string="Current record's partner type"
  • is_inverse Boolean
    help='Inverse relations are from right to left partner.' string='Is reverse type?'
  • is_symmetric Boolean
    string='Symmetric'
  • name Char
    args: 'Name'
  • partner_category_other Many2one → res.partner.category
    comodel_name='res.partner.category' string="Other record's category"
  • partner_category_this Many2one → res.partner.category
    comodel_name='res.partner.category' string="Current record's category"
  • type_id Many2one → res.partner.relation.type
    comodel_name='res.partner.relation.type' string='Type'
Public methods (3)
  • get_partner_types(self)
    @api.model
    Partner types are defined by model res.partner.relation.type.
  • name_get(self)
    Get name or name_inverse from underlying model.
  • name_search(self, name='', args=None, operator='ilike', limit=100)
    @api.model
    Search for name or inverse name in underlying model.
REPOSITORY
REPOSITORYOCA/partner-contact
GIT
GIThttps://github.com/OCA/partner-contact.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/partner-contact/tree/12.0/partner_multi_relation
VERSION
VERSION 2.0.0
CATEGORY
CATEGORYCustomer Relationship Management
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Camptocamp, Therp BV
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Camptocamp, Therp BV
COMMITTERS
COMMITTERSRonald Portier, Pedro M. Baeza, GitHub, OCA Transbot, oca-travis, Weblate, OCA-git-bot, Simone Orsi, oca-ci, scampbell, Stephan Keller, Patrick Wilson
WEBSITE
WEBSITEhttps://github.com/OCA/partner-contact
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 00:29:16
ODOO DEPENDENCIES
ODOO DEPENDENCIES OCA/web:
    - web_domain_field
    - web_tree_many2one_clickable
odoo/odoo:
    - contacts
    - base
    - mail
    - base_setup
    - web
    - bus
    - web_tour
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES Not have
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION

Code Analysis

Views touched (9)
XML IDNameModelTypeStatus
form_res_partner_relation_all res.partner.relation.all.form res.partner.relation.all form New
form_res_partner_relation_type res.partner.relation.type form New
search_res_partner_relation_all res.partner.relation.all.filter res.partner.relation.all search New
search_res_partner_relation_type res.partner.relation.type search New
tree_res_partner_relation res.partner.relation tree New
tree_res_partner_relation_all res.partner.relation.all.tree res.partner.relation.all tree New
tree_res_partner_relation_type res.partner.relation.type tree New
view_partner_form partner_multi_relation.view_partner_form res.partner xpath Inherits base.view_partner_form
view_res_partner_filter partner_multi_relation.view_partner_filter res.partner field Inherits base.view_res_partner_filter
Models touched (5)

New fields (6)
  • relation_all_ids One2many → res.partner.relation.all
    auto_join=True comodel_name='res.partner.relation.all' copy=False domain=['|', ('active', '=', True), ('active', '=', False)] inverse_name='this_partner_id' selectable=False string='All relations with current partner'
  • relation_count Integer
    compute='_compute_relation_count' string='Relation Count'
  • search_relation_date Date
    compute=<expr> search='_search_relation_date' string='Relation valid'
  • search_relation_partner_category_id Many2one → res.partner.category
    comodel_name='res.partner.category' compute=<expr> search='_search_related_partner_category_id' string='Has relation with a partner in category'
  • search_relation_partner_id Many2one → res.partner
    comodel_name='res.partner' compute=<expr> search='_search_related_partner_id' string='Has relation with'
  • search_relation_type_id Many2one → res.partner.relation.type.selection
    comodel_name='res.partner.relation.type.selection' compute=<expr> search='_search_relation_type_id' string='Has relation of type'
Public methods (3)
  • action_view_relations(self)
    @api.multi
    Handle button to show relations for partner.
  • get_partner_type(self)
    @api.multi
    Get partner type for relation. :return: 'c' for organisation or 'p' for person :rtype: str
  • search(self, args, **kwargs)
    @api.model
    Handle searches that are based on the relations of the partner.

New fields (5)
  • date_end Date
    args: 'Ending date'
  • date_start Date
    args: 'Starting date'
  • left_partner_id Many2one → res.partner
    auto_join=True comodel_name='res.partner' ondelete='cascade' required=True string='Source Partner'
  • right_partner_id Many2one → res.partner
    auto_join=True comodel_name='res.partner' ondelete='cascade' required=True string='Destination Partner'
  • type_id Many2one → res.partner.relation.type
    auto_join=True comodel_name='res.partner.relation.type' required=True string='Type'
Public methods (4)
  • create(self, vals_list)
    @api.model_create_multi
    Override create to correct values, before being stored.
  • get_derived_inverse_relation(self)
    @api.multi
    Get the derived inverse relation from res.partner.relation.all.
  • get_derived_relation(self)
    @api.multi
    Get the derived relation from res.partner.relation.all.
  • name_get(self)
    @api.multi

New fields (14)
  • active Boolean
    compute='_compute_active' help='Records with date_end in the past are inactive' readonly=True search='_search_active' string='Active'
  • any_partner_id Many2many → res.partner
    comodel_name='res.partner' compute=<expr> search='_search_any_partner_id' string='Partner'
  • date_end Date
    args: 'Ending date'
  • date_start Date
    args: 'Starting date'
  • is_inverse Boolean
    help='Inverse relations are from right to left partner.' readonly=True string='Is reverse type?'
  • other_partner_id Many2one → res.partner
    comodel_name='res.partner' required=True string='Other Partner'
  • other_partner_id_domain Char
    compute='_compute_domains' readonly=True store=False
  • res_id Integer
    help='The id of the object in the model this relation is based on.' readonly=True required=True string='Resource ID'
  • res_model Char
    help='The database object this relation is based on.' readonly=True required=True string='Resource Model'
  • this_partner_id Many2one → res.partner
    comodel_name='res.partner' required=True string='One Partner'
  • this_partner_id_domain Char
    compute='_compute_domains' readonly=True store=False
  • type_id Many2one → res.partner.relation.type
    comodel_name='res.partner.relation.type' readonly=True required=True string='Underlying Relation Type'
  • type_selection_id Many2one → res.partner.relation.type.selection
    comodel_name='res.partner.relation.type.selection' required=True string='Relation Type'
  • type_selection_id_domain Char
    compute='_compute_domains' readonly=True store=False
Public methods (13)
  • create(self, vals_list)
    @api.model_create_multi
    Divert non-problematic creates to underlying table. Create underlying records but return an recordlist of this model.
  • create_resource(self, vals, type_selection)
    @api.model
    Create the underlying record for this kind of relation.
  • get_base_resource(self)
    @api.multi
    Get base resource from res_model and res_id.
  • get_register(self)
    The register contains one entry for each kind of relation.
  • get_select_specification(self, base_name, is_inverse)
    Get the key for the kind of relation in the registry.
  • name_get(self)
    @api.multi
  • onchange_partner_id(self)
    @api.onchange('this_partner_id', 'other_partner_id')
    Set domain on type_selection_id based on partner(s) selected.
  • onchange_type_selection_id(self)
    @api.onchange('type_selection_id')
    Add domain on partners according to category and contact_type.
  • register_specification(self, register, base_name, is_inverse, select_sql)
    To support adding extra records not based on res.partner.relation.
  • unlink(self)
    @api.multi
    For model 'res.partner.relation' call unlink on underlying model.
  • unlink_resource(self, base_resource)
    @api.multi
    Delegate unlink to underlying model.
  • write(self, vals)
    @api.multi
    For model 'res.partner.relation' call write on underlying model.
  • write_resource(self, base_resource, vals)
    @api.multi
    write handled by base resource.

New fields (9)
  • allow_self Boolean
    default=False help='This relation can be set up with the same partner left and right' string='Reflexive'
  • contact_type_left Selection
    selection='get_partner_types' string='Left partner type'
  • contact_type_right Selection
    selection='get_partner_types' string='Right partner type'
  • handle_invalid_onchange Selection
    default='restrict' help='When adding relations criteria like partner type and category are checked.\nHowever when you change the criteria, there might be relations that do not fit the new criteria.\nSpecify how this situation should be handled.' required=True selection=HANDLE_INVALID_ONCHANGE string='Invalid relation handling'
  • is_symmetric Boolean
    default=False help='This relation is the same from right to left as from left to right' string='Symmetric'
  • name Char
    required=True string='Name' translate=True
  • name_inverse Char
    required=True string='Inverse name' translate=True
  • partner_category_left Many2one → res.partner.category
    comodel_name='res.partner.category' string='Left partner category'
  • partner_category_right Many2one → res.partner.category
    comodel_name='res.partner.category' string='Right partner category'
Public methods (5)
  • check_existing(self, vals)
    @api.multi
    Check wether records exist that do not fit new criteria.
  • create(self, vals_list)
    @api.model_create_multi
  • get_partner_types(self)
    @api.model
    A partner can be an organisation or an individual.
  • unlink(self)
    @api.multi
    Allow delete of relation type, even when connections exist. Relations can be deleted if relation type allows it.
  • write(self, vals)
    @api.multi
    Handle existing relations if conditions change.

New fields (9)
  • allow_self Boolean
    string='Reflexive'
  • contact_type_other Selection
    selection='get_partner_types' string="Other record's partner type"
  • contact_type_this Selection
    selection='get_partner_types' string="Current record's partner type"
  • is_inverse Boolean
    help='Inverse relations are from right to left partner.' string='Is reverse type?'
  • is_symmetric Boolean
    string='Symmetric'
  • name Char
    args: 'Name'
  • partner_category_other Many2one → res.partner.category
    comodel_name='res.partner.category' string="Other record's category"
  • partner_category_this Many2one → res.partner.category
    comodel_name='res.partner.category' string="Current record's category"
  • type_id Many2one → res.partner.relation.type
    comodel_name='res.partner.relation.type' string='Type'
Public methods (3)
  • get_partner_types(self)
    @api.model
    Partner types are defined by model res.partner.relation.type.
  • name_get(self)
    @api.multi
    Get name or name_inverse from underlying model.
  • name_search(self, name='', args=None, operator='ilike', limit=100)
    @api.model
    Search for name or inverse name in underlying model.
REPOSITORY
REPOSITORYOCA/partner-contact
GIT
GIThttps://github.com/OCA/partner-contact.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/partner-contact/tree/11.0/partner_multi_relation
VERSION
VERSION 1.1.0
CATEGORY
CATEGORYCustomer Relationship Management
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Camptocamp, Therp BV
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Camptocamp, Therp BV
COMMITTERS
COMMITTERSHolger Brunn, Maxime Chambreuil, Pedro M. Baeza, OCA Transbot, oca-travis, Weblate, OCA-git-bot, Stephan Keller
WEBSITE
WEBSITEhttps://github.com/OCA/partner-contact
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 00:23:56
ODOO DEPENDENCIES
ODOO DEPENDENCIES odoo/odoo:
    - contacts
    - base
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES Not have
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION

Code Analysis

Views touched (6)
XML IDNameModelTypeStatus
form_res_partner_relation_type res.partner.relation.type form New
search_res_partner_relation_all res.partner.relation.all search New
tree_res_partner_relation_all res.partner.relation.all tree New
tree_res_partner_relation_type res.partner.relation.type tree New
view_partner_form partner_multi_relation.view_partner_form res.partner xpath Inherits base.view_partner_form
view_res_partner_filter partner_multi_relation.view_partner_filter res.partner data Inherits base.view_res_partner_filter
Models touched (5)

New fields (6)
  • relation_all_ids One2many → res.partner.relation.all
    auto_join=True comodel_name='res.partner.relation.all' copy=False inverse_name='this_partner_id' selectable=False string='All relations with current partner'
  • relation_count Integer
    compute='_compute_relation_count' string='Relation Count'
  • search_relation_date Date
    compute=<expr> search='_search_relation_date' string='Relation valid'
  • search_relation_partner_category_id Many2one → res.partner.category
    comodel_name='res.partner.category' compute=<expr> search='_search_related_partner_category_id' string='Has relation with a partner in category'
  • search_relation_partner_id Many2one → res.partner
    comodel_name='res.partner' compute=<expr> search='_search_related_partner_id' string='Has relation with'
  • search_relation_type_id Many2one → res.partner.relation.type.selection
    comodel_name='res.partner.relation.type.selection' compute=<expr> search='_search_relation_type_id' string='Has relation of type'
Public methods (2)
  • get_partner_type(self)
    @api.multi
    Get partner type for relation. :return: 'c' for company or 'p' for person :rtype: str
  • search(self, args, offset=0, limit=None, order=None, count=False)
    @api.model
    Inject searching for current relation date if we search for relation properties and no explicit date was given.

New fields (5)
  • date_end Date
    args: 'Ending date'
  • date_start Date
    args: 'Starting date'
  • left_partner_id Many2one → res.partner
    auto_join=True comodel_name='res.partner' ondelete='cascade' required=True string='Source Partner'
  • right_partner_id Many2one → res.partner
    auto_join=True comodel_name='res.partner' ondelete='cascade' required=True string='Destination Partner'
  • type_id Many2one → res.partner.relation.type
    auto_join=True comodel_name='res.partner.relation.type' required=True string='Type'
Public methods (3)
  • check_is_unique(self, vals)
    @api.multi
  • create(self, vals)
    @api.model
    Override create to correct values, before being stored. Check to make sure each unique relation stays unique
  • write(self, vals)
    @api.model
    Check to make sure each unique relation stays unique

New fields (11)
  • active Boolean
    help='Records with date_end in the past are inactive' readonly=True string='Active'
  • any_partner_id Many2many → res.partner
    comodel_name='res.partner' compute=<expr> search='_search_any_partner_id' string='Partner'
  • date_end Date
    args: 'Ending date'
  • date_start Date
    args: 'Starting date'
  • is_inverse Boolean
    help='Inverse relations are from right to left partner.' readonly=True string='Is reverse type?'
  • other_partner_id Many2one → res.partner
    comodel_name='res.partner' required=True string='Other Partner'
  • res_id Integer
    help='The id of the object in the model this relation is based on.' readonly=True required=True string='Resource ID'
  • res_model Char
    help='The database object this relation is based on.' readonly=True required=True string='Resource Model'
  • this_partner_id Many2one → res.partner
    comodel_name='res.partner' required=True string='One Partner'
  • type_id Many2one → res.partner.relation.type
    comodel_name='res.partner.relation.type' readonly=True required=True string='Underlying Relation Type'
  • type_selection_id Many2one → res.partner.relation.type.selection
    comodel_name='res.partner.relation.type.selection' required=True string='Relation Type'
Public methods (13)
  • create(self, vals)
    @api.model
    Divert non-problematic creates to underlying table. Create a res.partner.relation but return the converted id.
  • create_resource(self, vals, type_selection)
    @api.model
  • get_base_resource(self)
    @api.multi
    Get base resource from res_model and res_id.
  • get_register(self)
  • get_select_specification(self, base_name, is_inverse)
  • name_get(self)
    @api.multi
  • onchange_partner_id(self)
    @api.onchange('this_partner_id', 'other_partner_id')
    Set domain on type_selection_id based on partner(s) selected.
  • onchange_type_selection_id(self)
    @api.onchange('type_selection_id')
    Add domain on partners according to category and contact_type.
  • register_specification(self, register, base_name, is_inverse, select_sql)
  • unlink(self)
    @api.multi
    For model 'res.partner.relation' call unlink on underlying model.
  • unlink_resource(self, base_resource)
    @api.multi
    Delegate unlink to underlying model.
  • write(self, vals)
    @api.multi
    For model 'res.partner.relation' call write on underlying model.
  • write_resource(self, base_resource, vals)
    @api.multi
    write handled by base resource.

New fields (10)
  • allow_self Boolean
    default=False help='This relation can be set up with the same partner left and right' string='Reflexive'
  • contact_type_left Selection
    selection='get_partner_types' string='Left partner type'
  • contact_type_right Selection
    selection='get_partner_types' string='Right partner type'
  • handle_invalid_onchange Selection
    default='restrict' help='When adding relations criteria like partner type and category are checked.\nHowever when you change the criteria, there might be relations that do not fit the new criteria.\nSpecify how this situation should be handled.' required=True selection=HANDLE_INVALID_ONCHANGE string='Invalid relation handling'
  • is_symmetric Boolean
    default=False help='This relation is the same from right to left as from left to right' string='Symmetric'
  • is_unique Boolean
    help='If a relation type is unique, then there cannot be 2relations with the same parties and the same type.' string='Is Unique'
  • name Char
    required=True string='Name' translate=True
  • name_inverse Char
    required=True string='Inverse name' translate=True
  • partner_category_left Many2one → res.partner.category
    comodel_name='res.partner.category' string='Left partner category'
  • partner_category_right Many2one → res.partner.category
    comodel_name='res.partner.category' string='Right partner category'
Public methods (5)
  • check_existing(self, vals)
    @api.multi
    Check wether records exist that do not fit new criteria.
  • create(self, vals)
    @api.model
  • get_partner_types(self)
    @api.model
    A partner can be an organisation or an individual.
  • unlink(self)
    @api.multi
    Allow delete of relation type, even when connections exist. Relations can be deleted if relation type allows it.
  • write(self, vals)
    @api.multi
    Handle existing relations if conditions change.

New fields (9)
  • allow_self Boolean
    string='Reflexive'
  • contact_type_other Selection
    selection='get_partner_types' string="Other record's partner type"
  • contact_type_this Selection
    selection='get_partner_types' string="Current record's partner type"
  • is_inverse Boolean
    help='Inverse relations are from right to left partner.' string='Is reverse type?'
  • is_symmetric Boolean
    string='Symmetric'
  • name Char
    args: 'Name'
  • partner_category_other Many2one → res.partner.category
    comodel_name='res.partner.category' string="Other record's category"
  • partner_category_this Many2one → res.partner.category
    comodel_name='res.partner.category' string="Current record's category"
  • type_id Many2one → res.partner.relation.type
    comodel_name='res.partner.relation.type' string='Type'
Public methods (3)
  • get_partner_types(self)
    @api.model
    Partner types are defined by model res.partner.relation.type.
  • name_get(self)
    @api.multi
    Get name or name_inverse from underlying model.
  • name_search(self, name='', args=None, operator='ilike', limit=100)
    @api.model
    Search for name or inverse name in underlying model.
REPOSITORY
REPOSITORYOCA/partner-contact
GIT
GIThttps://github.com/OCA/partner-contact.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/partner-contact/tree/10.0/partner_multi_relation
VERSION
VERSION 1.0.2
CATEGORY
CATEGORYCustomer Relationship Management
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Camptocamp, Therp BV
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Camptocamp, Therp BV
COMMITTERS
COMMITTERSRonald Portier, Holger Brunn, Pedro M. Baeza, GitHub, Stéphane Bidoul (ACSONE), OCA Transbot, oca-travis, Weblate
WEBSITE
WEBSITEhttps://github.com/oca/partner-contact
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 00:19:55
ODOO DEPENDENCIES
ODOO DEPENDENCIES odoo/odoo:
    - sales_team
    - base
    - mail
    - base_setup
    - web_kanban
    - web
    - bus
    - web_tour
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES Not have
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION

Code Analysis

Views touched (6)
XML IDNameModelTypeStatus
form_res_partner_relation_type res.partner.relation.type form New
search_res_partner_relation_all res.partner.relation.all search New
tree_res_partner_relation_all res.partner.relation.all tree New
tree_res_partner_relation_type res.partner.relation.type tree New
view_partner_form partner_multi_relation.view_partner_form res.partner xpath Inherits base.view_partner_form
view_res_partner_filter partner_multi_relation.view_partner_filter res.partner data Inherits base.view_res_partner_filter
Models touched (5)

New fields (6)
  • relation_all_ids One2many → res.partner.relation.all
    auto_join=True comodel_name='res.partner.relation.all' copy=False inverse_name='this_partner_id' selectable=False string='All relations with current partner'
  • relation_count Integer
    compute='_compute_relation_count' string='Relation Count'
  • search_relation_date Date
    compute=<expr> search='_search_relation_date' string='Relation valid'
  • search_relation_partner_category_id Many2one → res.partner.category
    comodel_name='res.partner.category' compute=<expr> search='_search_related_partner_category_id' string='Has relation with a partner in category'
  • search_relation_partner_id Many2one → res.partner
    comodel_name='res.partner' compute=<expr> search='_search_related_partner_id' string='Has relation with'
  • search_relation_type_id Many2one → res.partner.relation.type.selection
    comodel_name='res.partner.relation.type.selection' compute=<expr> search='_search_relation_type_id' string='Has relation of type'
Public methods (2)
  • get_partner_type(self)
    @api.multi
    Get partner type for relation. :return: 'c' for company or 'p' for person :rtype: str
  • search(self, args, offset=0, limit=None, order=None, count=False)
    @api.model
    Inject searching for current relation date if we search for relation properties and no explicit date was given.

New fields (5)
  • date_end Date
    args: 'Ending date'
  • date_start Date
    args: 'Starting date'
  • left_partner_id Many2one → res.partner
    auto_join=True comodel_name='res.partner' ondelete='cascade' required=True string='Source Partner'
  • right_partner_id Many2one → res.partner
    auto_join=True comodel_name='res.partner' ondelete='cascade' required=True string='Destination Partner'
  • type_id Many2one → res.partner.relation.type
    auto_join=True comodel_name='res.partner.relation.type' required=True string='Type'
Public methods (1)
  • create(self, vals)
    @api.model
    Override create to correct values, before being stored.

New fields (11)
  • active Boolean
    help='Records with date_end in the past are inactive' readonly=True string='Active'
  • any_partner_id Many2many → res.partner
    comodel_name='res.partner' compute=<expr> search='_search_any_partner_id' string='Partner'
  • date_end Date
    args: 'Ending date'
  • date_start Date
    args: 'Starting date'
  • is_inverse Boolean
    help='Inverse relations are from right to left partner.' readonly=True string='Is reverse type?'
  • other_partner_id Many2one → res.partner
    comodel_name='res.partner' required=True string='Other Partner'
  • res_id Integer
    help='The id of the object in the model this relation is based on.' readonly=True required=True string='Resource ID'
  • res_model Char
    help='The database object this relation is based on.' readonly=True required=True string='Resource Model'
  • this_partner_id Many2one → res.partner
    comodel_name='res.partner' required=True string='One Partner'
  • type_id Many2one → res.partner.relation.type
    comodel_name='res.partner.relation.type' readonly=True required=True string='Underlying Relation Type'
  • type_selection_id Many2one → res.partner.relation.type.selection
    comodel_name='res.partner.relation.type.selection' required=True string='Relation Type'
Public methods (14)
  • create(self, vals)
    @api.model
    Divert non-problematic creates to underlying table. Create a res.partner.relation but return the converted id.
  • create_resource(self, vals, type_selection)
    @api.model
  • fields_get(self, allfields=None, attributes=None)
    @api.model
    Set type_selection_id field to not exportable. Trying to export type_selection_id would result in a crash as it does not refer to a real table.
  • get_base_resource(self)
    @api.multi
    Get base resource from res_model and res_id.
  • get_register(self)
  • get_select_specification(self, base_name, is_inverse)
  • name_get(self)
    @api.multi
  • onchange_partner_id(self)
    @api.onchange('this_partner_id', 'other_partner_id')
    Set domain on type_selection_id based on partner(s) selected.
  • onchange_type_selection_id(self)
    @api.onchange('type_selection_id')
    Add domain on partners according to category and contact_type.
  • register_specification(self, register, base_name, is_inverse, select_sql)
  • unlink(self)
    @api.multi
    For model 'res.partner.relation' call unlink on underlying model.
  • unlink_resource(self, base_resource)
    @api.multi
    Delegate unlink to underlying model.
  • write(self, vals)
    @api.multi
    For model 'res.partner.relation' call write on underlying model.
  • write_resource(self, base_resource, vals)
    @api.multi
    write handled by base resource.

New fields (9)
  • allow_self Boolean
    default=False help='This relation can be set up with the same partner left and right' string='Reflexive'
  • contact_type_left Selection
    selection='get_partner_types' string='Left partner type'
  • contact_type_right Selection
    selection='get_partner_types' string='Right partner type'
  • handle_invalid_onchange Selection
    default='restrict' help='When adding relations criteria like partner type and category are checked.\nHowever when you change the criteria, there might be relations that do not fit the new criteria.\nSpecify how this situation should be handled.' required=True selection=HANDLE_INVALID_ONCHANGE string='Invalid relation handling'
  • is_symmetric Boolean
    default=False help='This relation is the same from right to left as from left to right' string='Symmetric'
  • name Char
    required=True string='Name' translate=True
  • name_inverse Char
    required=True string='Inverse name' translate=True
  • partner_category_left Many2one → res.partner.category
    comodel_name='res.partner.category' string='Left partner category'
  • partner_category_right Many2one → res.partner.category
    comodel_name='res.partner.category' string='Right partner category'
Public methods (5)
  • check_existing(self, vals)
    @api.multi
    Check wether records exist that do not fit new criteria.
  • create(self, vals)
    @api.model
  • get_partner_types(self)
    @api.model
    A partner can be an organisation or an individual.
  • unlink(self)
    @api.multi
    Allow delete of relation type, even when connections exist. Relations can be deleted if relation type allows it.
  • write(self, vals)
    @api.multi
    Handle existing relations if conditions change.

New fields (9)
  • allow_self Boolean
    string='Reflexive'
  • contact_type_other Selection
    selection='get_partner_types' string="Other record's partner type"
  • contact_type_this Selection
    selection='get_partner_types' string="Current record's partner type"
  • is_inverse Boolean
    help='Inverse relations are from right to left partner.' string='Is reverse type?'
  • is_symmetric Boolean
    string='Symmetric'
  • name Char
    args: 'Name'
  • partner_category_other Many2one → res.partner.category
    comodel_name='res.partner.category' string="Other record's category"
  • partner_category_this Many2one → res.partner.category
    comodel_name='res.partner.category' string="Current record's category"
  • type_id Many2one → res.partner.relation.type
    comodel_name='res.partner.relation.type' string='Type'
Public methods (3)
  • get_partner_types(self)
    @api.model
    Partner types are defined by model res.partner.relation.type.
  • name_get(self)
    @api.multi
    Get name or name_inverse from underlying model.
  • name_search(self, name='', args=None, operator='ilike', limit=100)
    @api.model
    Search for name or inverse name in underlying model.
REPOSITORY
REPOSITORYOCA/partner-contact
GIT
GIThttps://github.com/OCA/partner-contact.git
GIT FOLDER
GIT FOLDERhttps://github.com/OCA/partner-contact/tree/9.0/partner_multi_relation
VERSION
VERSION 1.1.1
CATEGORY
CATEGORYCustomer Relationship Management
LICENSE
LICENSEAGPL-3
APPLICATION
APPLICATIONNo
AUTO-INSTALLABLE
AUTO-INSTALLABLENo
AUTHORS
AUTHORSOdoo Community Association (OCA), Camptocamp, Therp BV
MAINTAINERS
MAINTAINERSOdoo Community Association (OCA), Camptocamp, Therp BV
COMMITTERS
COMMITTERSPedro M. Baeza, Stéphane Bidoul (ACSONE), OCA Transbot, oca-travis, Weblate
WEBSITE
WEBSITE
LAST TRACKING UPDATE
LAST TRACKING UPDATE2026-07-06 00:15:19
ODOO DEPENDENCIES
ODOO DEPENDENCIES odoo/odoo:
    - base
PYTHON DEPENDENCIES
PYTHON DEPENDENCIES Not have
SYSTEM DEPENDENCIES
SYSTEM DEPENDENCIES Not have
DESCRIPTION
DESCRIPTION

Code Analysis

Views touched (6)
XML IDNameModelTypeStatus
form_res_partner_relation_type res.partner.relation.type form New
search_res_partner_relation_all res.partner.relation.all search New
tree_res_partner_relation_all res.partner.relation.all tree New
tree_res_partner_relation_type res.partner.relation.type tree New
view_partner_form partner_multi_relation.view_partner_form res.partner xpath Inherits base.view_partner_form
view_res_partner_filter partner_multi_relation.view_partner_filter res.partner data Inherits base.view_res_partner_filter
Models touched (5)

New fields (6)
  • relation_all_ids One2many → res.partner.relation.all
    auto_join=True comodel_name='res.partner.relation.all' copy=False inverse_name='this_partner_id' selectable=False string='All relations with current partner'
  • relation_count Integer
    compute='_compute_relation_count' string='Relation Count'
  • search_relation_date Date
    compute=<expr> search='_search_relation_date' string='Relation valid'
  • search_relation_partner_category_id Many2one → res.partner.category
    comodel_name='res.partner.category' compute=<expr> search='_search_related_partner_category_id' string='Has relation with a partner in category'
  • search_relation_partner_id Many2one → res.partner
    comodel_name='res.partner' compute=<expr> search='_search_related_partner_id' string='Has relation with'
  • search_relation_type_id Many2one → res.partner.relation.type.selection
    comodel_name='res.partner.relation.type.selection' compute=<expr> search='_search_relation_type_id' string='Has relation of type'
Public methods (2)
  • get_partner_type(self)
    @api.multi
    Get partner type for relation. :return: 'c' for company or 'p' for person :rtype: str
  • search(self, args, offset=0, limit=None, order=None, count=False)
    @api.model
    Inject searching for current relation date if we search for relation properties and no explicit date was given.

New fields (5)
  • date_end Date
    args: 'Ending date'
  • date_start Date
    args: 'Starting date'
  • left_partner_id Many2one → res.partner
    auto_join=True comodel_name='res.partner' ondelete='cascade' required=True string='Source Partner'
  • right_partner_id Many2one → res.partner
    auto_join=True comodel_name='res.partner' ondelete='cascade' required=True string='Destination Partner'
  • type_id Many2one → res.partner.relation.type
    auto_join=True comodel_name='res.partner.relation.type' required=True string='Type'
Public methods (1)
  • create(self, vals)
    @api.model
    Override create to correct values, before being stored.

New fields (9)
  • active Boolean
    help='Records with date_end in the past are inactive' string='Active'
  • any_partner_id Many2many → res.partner
    comodel_name='res.partner' compute=<expr> search='_search_any_partner_id' string='Partner'
  • date_end Date
    args: 'Ending date'
  • date_start Date
    args: 'Starting date'
  • other_partner_id Many2one → res.partner
    comodel_name='res.partner' required=True string='Other Partner'
  • record_type Selection
    readonly=True selection=_RECORD_TYPES string='Record Type'
  • relation_id Many2one → res.partner.relation
    comodel_name='res.partner.relation' readonly=True string='Relation'
  • this_partner_id Many2one → res.partner
    comodel_name='res.partner' required=True string='One Partner'
  • type_selection_id Many2one → res.partner.relation.type.selection
    comodel_name='res.partner.relation.type.selection' required=True string='Relation Type'
Public methods (6)
  • create(self, vals)
    @api.model
    Divert non-problematic creates to underlying table. Create a res.partner.relation but return the converted id.
  • name_get(self)
    @api.multi
  • onchange_partner_id(self)
    @api.onchange('this_partner_id', 'other_partner_id')
    Set domain on type_selection_id based on partner(s) selected.
  • onchange_type_selection_id(self)
    @api.onchange('type_selection_id')
    Add domain on partners according to category and contact_type.
  • unlink(self)
    @api.multi
    divert non-problematic creates to underlying table
  • write(self, vals)
    @api.multi
    divert non-problematic writes to underlying table

New fields (9)
  • allow_self Boolean
    default=False help='This relation can be set up with the same partner left and right' string='Reflexive'
  • contact_type_left Selection
    selection='get_partner_types' string='Left partner type'
  • contact_type_right Selection
    selection='get_partner_types' string='Right partner type'
  • handle_invalid_onchange Selection
    default='restrict' help='When adding relations criteria like partner type and category are checked.\nHowever when you change the criteria, there might be relations that do not fit the new criteria.\nSpecify how this situation should be handled.' required=True selection=HANDLE_INVALID_ONCHANGE string='Invalid relation handling'
  • is_symmetric Boolean
    default=False help='This relation is the same from right to left as from left to right' string='Symmetric'
  • name Char
    required=True string='Name' translate=True
  • name_inverse Char
    required=True string='Inverse name' translate=True
  • partner_category_left Many2one → res.partner.category
    comodel_name='res.partner.category' string='Left partner category'
  • partner_category_right Many2one → res.partner.category
    comodel_name='res.partner.category' string='Right partner category'
Public methods (4)
  • check_existing(self, vals)
    @api.multi
    Check wether records exist that do not fit new criteria.
  • get_partner_types(self)
    @api.model
    A partner can be an organisation or an individual.
  • onchange_is_symmetric(self)
    @api.onchange('is_symmetric')
    Set right side to left side if symmetric.
  • write(self, vals)
    @api.multi
    Handle existing relations if conditions change.

New fields (9)
  • allow_self Boolean
    string='Reflexive'
  • contact_type_other Selection
    selection=ResPartnerRelationType.get_partner_types.im_func string="Other record's partner type"
  • contact_type_this Selection
    selection=ResPartnerRelationType.get_partner_types.im_func string="Current record's partner type"
  • is_inverse Boolean
    help='Inverse relations are from right to left partner.' string='Is reverse type?'
  • is_symmetric Boolean
    string='Symmetric'
  • name Char
    args: 'Name'
  • partner_category_other Many2one → res.partner.category
    comodel_name='res.partner.category' string="Other record's category"
  • partner_category_this Many2one → res.partner.category
    comodel_name='res.partner.category' string="Current record's category"
  • type_id Many2one → res.partner.relation.type
    comodel_name='res.partner.relation.type' string='Type'
Public methods (2)
  • name_get(self)
    @api.multi
    Get name or name_inverse from underlying model.
  • name_search(self, name='', args=None, operator='ilike', limit=100)
    @api.model
    Search for name or inverse name in underlying model.