|
@ -1,18 +1,100 @@ |
|
|
# -*- coding: utf-8 -*- |
|
|
# -*- coding: utf-8 -*- |
|
|
# © 2014-2016 Therp BV <http://therp.nl> |
|
|
|
|
|
|
|
|
# Copyright 2014-2017 Therp BV <http://therp.nl> |
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|
|
|
|
|
# pylint: disable=method-required-super |
|
|
"""Abstract model to show each relation from two sides.""" |
|
|
"""Abstract model to show each relation from two sides.""" |
|
|
|
|
|
import collections |
|
|
|
|
|
import logging |
|
|
|
|
|
|
|
|
from psycopg2.extensions import AsIs |
|
|
from psycopg2.extensions import AsIs |
|
|
|
|
|
|
|
|
from openerp import _, api, fields, models |
|
|
from openerp import _, api, fields, models |
|
|
|
|
|
from openerp.exceptions import ValidationError |
|
|
from openerp.tools import drop_view_if_exists |
|
|
from openerp.tools import drop_view_if_exists |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PADDING = 10 |
|
|
|
|
|
_RECORD_TYPES = [ |
|
|
|
|
|
('a', 'Left partner to right partner'), |
|
|
|
|
|
('b', 'Right partner to left partner'), |
|
|
|
|
|
] |
|
|
|
|
|
|
|
|
_logger = logging.getLogger(__name__) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_last_key_offset = -1 |
|
|
|
|
|
_specification_register = collections.OrderedDict() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def register_select_specification(base_name, is_inverse, select_sql): |
|
|
|
|
|
"""Register SELECT clause for rows to be included in view. |
|
|
|
|
|
|
|
|
|
|
|
Each SELECT clause must contain a first column in the form: |
|
|
|
|
|
'<base_keyfield> * %%(padding)s + %(key_offset),' |
|
|
|
|
|
The %%(padding)s will be used as a parameter for the |
|
|
|
|
|
cursor.execute function, the %(key_offset) will be replaced |
|
|
|
|
|
by dictionary replacement. |
|
|
|
|
|
The columns for each SELECT clause must be ordered as follows: |
|
|
|
|
|
id - specified as defined above: |
|
|
|
|
|
res_model: model._name of the underlying table, |
|
|
|
|
|
res_id: base key in the underlying table, |
|
|
|
|
|
this_partner_id: must refer to a partner |
|
|
|
|
|
other_partner_id: must refer to a related partner |
|
|
|
|
|
type_id: refers to res.partner.relation.type, |
|
|
|
|
|
date_start: start date of relation if relevant or NULL, |
|
|
|
|
|
date_end: end date of relation if relevant or NULL, |
|
|
|
|
|
%(is_inverse)s as is_inverse: used to determine type_selection_id, |
|
|
|
|
|
""" |
|
|
|
|
|
global _last_key_offset |
|
|
|
|
|
key_name = base_name + (is_inverse and '_inverse' or '') |
|
|
|
|
|
assert key_name not in _specification_register |
|
|
|
|
|
assert '%%(padding)s' in select_sql |
|
|
|
|
|
assert '%(key_offset)s' in select_sql |
|
|
|
|
|
assert '%(is_inverse)s' in select_sql |
|
|
|
|
|
_last_key_offset += 1 |
|
|
|
|
|
_specification_register[key_name] = dict( |
|
|
|
|
|
base_name=base_name, |
|
|
|
|
|
is_inverse=is_inverse, |
|
|
|
|
|
key_offset=_last_key_offset, |
|
|
|
|
|
select_sql=select_sql % { |
|
|
|
|
|
'key_offset': _last_key_offset, |
|
|
|
|
|
'is_inverse': is_inverse}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_select_specification(base_name, is_inverse): |
|
|
|
|
|
key_name = base_name + (is_inverse and '_inverse' or '') |
|
|
|
|
|
return _specification_register[key_name] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Register relations |
|
|
|
|
|
register_select_specification( |
|
|
|
|
|
base_name='relation', |
|
|
|
|
|
is_inverse=False, |
|
|
|
|
|
select_sql="""\ |
|
|
|
|
|
SELECT |
|
|
|
|
|
(rel.id * %%(padding)s) + %(key_offset)s AS id, |
|
|
|
|
|
'res.partner.relation' AS res_model, |
|
|
|
|
|
rel.id AS res_id, |
|
|
|
|
|
rel.left_partner_id AS this_partner_id, |
|
|
|
|
|
rel.right_partner_id AS other_partner_id, |
|
|
|
|
|
rel.type_id, |
|
|
|
|
|
rel.date_start, |
|
|
|
|
|
rel.date_end, |
|
|
|
|
|
%(is_inverse)s as is_inverse |
|
|
|
|
|
FROM res_partner_relation rel |
|
|
|
|
|
""") |
|
|
|
|
|
|
|
|
|
|
|
# Register inverse relations |
|
|
|
|
|
register_select_specification( |
|
|
|
|
|
base_name='relation', |
|
|
|
|
|
is_inverse=True, |
|
|
|
|
|
select_sql="""\ |
|
|
|
|
|
SELECT |
|
|
|
|
|
(rel.id * %%(padding)s) + %(key_offset)s AS id, |
|
|
|
|
|
'res.partner.relation', |
|
|
|
|
|
rel.id, |
|
|
|
|
|
rel.right_partner_id, |
|
|
|
|
|
rel.left_partner_id, |
|
|
|
|
|
rel.type_id, |
|
|
|
|
|
rel.date_start, |
|
|
|
|
|
rel.date_end, |
|
|
|
|
|
%(is_inverse)s as is_inverse |
|
|
|
|
|
FROM res_partner_relation rel |
|
|
|
|
|
""") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ResPartnerRelationAll(models.AbstractModel): |
|
|
class ResPartnerRelationAll(models.AbstractModel): |
|
@ -21,108 +103,116 @@ class ResPartnerRelationAll(models.AbstractModel): |
|
|
_log_access = False |
|
|
_log_access = False |
|
|
_name = 'res.partner.relation.all' |
|
|
_name = 'res.partner.relation.all' |
|
|
_description = 'All (non-inverse + inverse) relations between partners' |
|
|
_description = 'All (non-inverse + inverse) relations between partners' |
|
|
_order = ( |
|
|
|
|
|
'this_partner_id, type_selection_id,' |
|
|
|
|
|
'date_end desc, date_start desc' |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
_additional_view_fields = [] |
|
|
|
|
|
"""append to this list if you added fields to res_partner_relation that |
|
|
|
|
|
you need in this model and related fields are not adequate (ie for sorting) |
|
|
|
|
|
You must use the same name as in res_partner_relation. |
|
|
|
|
|
Don't overwrite this list in your declaration but append in _auto_init: |
|
|
|
|
|
|
|
|
|
|
|
@api.model_cr_context |
|
|
|
|
|
def _auto_init(self): |
|
|
|
|
|
self._additional_view_fields.append('my_field') |
|
|
|
|
|
return super(ResPartnerRelationAll, self)._auto_init() |
|
|
|
|
|
|
|
|
|
|
|
my_field = fields... |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
_order = \ |
|
|
|
|
|
'this_partner_id, type_selection_id, date_end desc, date_start desc' |
|
|
|
|
|
|
|
|
|
|
|
res_model = fields.Char( |
|
|
|
|
|
string='Resource Model', |
|
|
|
|
|
readonly=True, |
|
|
|
|
|
required=True, |
|
|
|
|
|
help="The database object this relation is based on.") |
|
|
|
|
|
res_id = fields.Integer( |
|
|
|
|
|
string='Resource ID', |
|
|
|
|
|
readonly=True, |
|
|
|
|
|
required=True, |
|
|
|
|
|
help="The id of the object in the model this relation is based on.") |
|
|
this_partner_id = fields.Many2one( |
|
|
this_partner_id = fields.Many2one( |
|
|
comodel_name='res.partner', |
|
|
comodel_name='res.partner', |
|
|
string='One Partner', |
|
|
string='One Partner', |
|
|
required=True, |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
required=True) |
|
|
other_partner_id = fields.Many2one( |
|
|
other_partner_id = fields.Many2one( |
|
|
comodel_name='res.partner', |
|
|
comodel_name='res.partner', |
|
|
string='Other Partner', |
|
|
string='Other Partner', |
|
|
required=True, |
|
|
|
|
|
) |
|
|
|
|
|
type_selection_id = fields.Many2one( |
|
|
|
|
|
comodel_name='res.partner.relation.type.selection', |
|
|
|
|
|
string='Relation Type', |
|
|
|
|
|
required=True, |
|
|
|
|
|
) |
|
|
|
|
|
relation_id = fields.Many2one( |
|
|
|
|
|
comodel_name='res.partner.relation', |
|
|
|
|
|
string='Relation', |
|
|
|
|
|
readonly=True, |
|
|
|
|
|
) |
|
|
|
|
|
record_type = fields.Selection( |
|
|
|
|
|
selection=_RECORD_TYPES, |
|
|
|
|
|
string='Record Type', |
|
|
|
|
|
|
|
|
required=True) |
|
|
|
|
|
type_id = fields.Many2one( |
|
|
|
|
|
comodel_name='res.partner.relation.type', |
|
|
|
|
|
string='Underlying Relation Type', |
|
|
readonly=True, |
|
|
readonly=True, |
|
|
) |
|
|
|
|
|
|
|
|
required=True) |
|
|
date_start = fields.Date('Starting date') |
|
|
date_start = fields.Date('Starting date') |
|
|
date_end = fields.Date('Ending date') |
|
|
date_end = fields.Date('Ending date') |
|
|
|
|
|
is_inverse = fields.Boolean( |
|
|
|
|
|
string="Is reverse type?", |
|
|
|
|
|
readonly=True, |
|
|
|
|
|
help="Inverse relations are from right to left partner.") |
|
|
|
|
|
type_selection_id = fields.Many2one( |
|
|
|
|
|
comodel_name='res.partner.relation.type.selection', |
|
|
|
|
|
string='Relation Type', |
|
|
|
|
|
required=True) |
|
|
active = fields.Boolean( |
|
|
active = fields.Boolean( |
|
|
string='Active', |
|
|
string='Active', |
|
|
help="Records with date_end in the past are inactive", |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
readonly=True, |
|
|
|
|
|
help="Records with date_end in the past are inactive") |
|
|
any_partner_id = fields.Many2many( |
|
|
any_partner_id = fields.Many2many( |
|
|
comodel_name='res.partner', |
|
|
comodel_name='res.partner', |
|
|
string='Partner', |
|
|
string='Partner', |
|
|
compute=lambda self: None, |
|
|
compute=lambda self: None, |
|
|
search='_search_any_partner_id' |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
search='_search_any_partner_id') |
|
|
|
|
|
|
|
|
|
|
|
def _get_active_selects(self): |
|
|
|
|
|
"""Return selects actually to be used. |
|
|
|
|
|
|
|
|
|
|
|
Selects are registered from all modules PRESENT. But should only be |
|
|
|
|
|
used to build view if module actually INSTALLED. |
|
|
|
|
|
""" |
|
|
|
|
|
return ['relation', 'relation_inverse'] |
|
|
|
|
|
|
|
|
|
|
|
def _get_statement(self): |
|
|
|
|
|
"""Allow other modules to add to statement.""" |
|
|
|
|
|
active_selects = self._get_active_selects() |
|
|
|
|
|
union_select = ' UNION '.join( |
|
|
|
|
|
[_specification_register[key]['select_sql'] |
|
|
|
|
|
for key in active_selects]) |
|
|
|
|
|
return """\ |
|
|
|
|
|
CREATE OR REPLACE VIEW %%(table)s AS |
|
|
|
|
|
WITH base_selection AS (%(union_select)s) |
|
|
|
|
|
SELECT |
|
|
|
|
|
bas.*, |
|
|
|
|
|
CASE |
|
|
|
|
|
WHEN NOT bas.is_inverse OR typ.is_symmetric |
|
|
|
|
|
THEN bas.type_id * 2 |
|
|
|
|
|
ELSE (bas.type_id * 2) + 1 |
|
|
|
|
|
END as type_selection_id, |
|
|
|
|
|
(bas.date_end IS NULL OR bas.date_end >= current_date) AS active |
|
|
|
|
|
%%(additional_view_fields)s |
|
|
|
|
|
FROM base_selection bas |
|
|
|
|
|
JOIN res_partner_relation_type typ ON (bas.type_id = typ.id) |
|
|
|
|
|
%%(additional_tables)s |
|
|
|
|
|
""" % {'union_select': union_select} |
|
|
|
|
|
|
|
|
|
|
|
def _get_padding(self): |
|
|
|
|
|
"""Utility function to define padding in one place.""" |
|
|
|
|
|
return 100 |
|
|
|
|
|
|
|
|
|
|
|
def _get_additional_view_fields(self): |
|
|
|
|
|
"""Allow inherit models to add fields to view. |
|
|
|
|
|
|
|
|
|
|
|
If fields are added, the resulting string must have each field |
|
|
|
|
|
prepended by a comma, like so: |
|
|
|
|
|
return ', typ.allow_self, typ.left_partner_category' |
|
|
|
|
|
""" |
|
|
|
|
|
return '' |
|
|
|
|
|
|
|
|
|
|
|
def _get_additional_tables(self): |
|
|
|
|
|
"""Allow inherit models to add tables (JOIN's) to view. |
|
|
|
|
|
|
|
|
|
|
|
Example: |
|
|
|
|
|
return 'JOIN type_extention ext ON (bas.type_id = ext.id)' |
|
|
|
|
|
""" |
|
|
|
|
|
return '' |
|
|
|
|
|
|
|
|
@api.model_cr_context |
|
|
@api.model_cr_context |
|
|
def _auto_init(self): |
|
|
def _auto_init(self): |
|
|
cr = self._cr |
|
|
cr = self._cr |
|
|
drop_view_if_exists(cr, self._table) |
|
|
drop_view_if_exists(cr, self._table) |
|
|
additional_view_fields = ','.join(self._additional_view_fields) |
|
|
|
|
|
additional_view_fields = (',' + additional_view_fields)\ |
|
|
|
|
|
if additional_view_fields else '' |
|
|
|
|
|
cr.execute( |
|
|
cr.execute( |
|
|
"""\ |
|
|
|
|
|
CREATE OR REPLACE VIEW %(table)s AS |
|
|
|
|
|
SELECT |
|
|
|
|
|
rel.id * %(padding)s AS id, |
|
|
|
|
|
rel.id AS relation_id, |
|
|
|
|
|
cast('a' AS CHAR(1)) AS record_type, |
|
|
|
|
|
rel.left_partner_id AS this_partner_id, |
|
|
|
|
|
rel.right_partner_id AS other_partner_id, |
|
|
|
|
|
rel.date_start, |
|
|
|
|
|
rel.date_end, |
|
|
|
|
|
(rel.date_end IS NULL OR rel.date_end >= current_date) AS active, |
|
|
|
|
|
rel.type_id * %(padding)s AS type_selection_id |
|
|
|
|
|
%(additional_view_fields)s |
|
|
|
|
|
FROM res_partner_relation rel |
|
|
|
|
|
UNION SELECT |
|
|
|
|
|
rel.id * %(padding)s + 1, |
|
|
|
|
|
rel.id, |
|
|
|
|
|
CAST('b' AS CHAR(1)), |
|
|
|
|
|
rel.right_partner_id, |
|
|
|
|
|
rel.left_partner_id, |
|
|
|
|
|
rel.date_start, |
|
|
|
|
|
rel.date_end, |
|
|
|
|
|
rel.date_end IS NULL OR rel.date_end >= current_date, |
|
|
|
|
|
CASE |
|
|
|
|
|
WHEN typ.is_symmetric THEN rel.type_id * %(padding)s |
|
|
|
|
|
ELSE rel.type_id * %(padding)s + 1 |
|
|
|
|
|
END |
|
|
|
|
|
%(additional_view_fields)s |
|
|
|
|
|
FROM res_partner_relation rel |
|
|
|
|
|
JOIN res_partner_relation_type typ ON (rel.type_id = typ.id) |
|
|
|
|
|
""", |
|
|
|
|
|
{ |
|
|
|
|
|
'table': AsIs(self._table), |
|
|
|
|
|
'padding': PADDING, |
|
|
|
|
|
'additional_view_fields': AsIs(additional_view_fields), |
|
|
|
|
|
} |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
self._get_statement(), |
|
|
|
|
|
{'table': AsIs(self._table), |
|
|
|
|
|
'padding': self._get_padding(), |
|
|
|
|
|
'additional_view_fields': |
|
|
|
|
|
AsIs(self._get_additional_view_fields()), |
|
|
|
|
|
'additional_tables': |
|
|
|
|
|
AsIs(self._get_additional_tables())}) |
|
|
return super(ResPartnerRelationAll, self)._auto_init() |
|
|
return super(ResPartnerRelationAll, self)._auto_init() |
|
|
|
|
|
|
|
|
@api.model |
|
|
@api.model |
|
@ -132,8 +222,7 @@ CREATE OR REPLACE VIEW %(table)s AS |
|
|
return [ |
|
|
return [ |
|
|
'|', |
|
|
'|', |
|
|
('this_partner_id', operator, value), |
|
|
('this_partner_id', operator, value), |
|
|
('other_partner_id', operator, value), |
|
|
|
|
|
] |
|
|
|
|
|
|
|
|
('other_partner_id', operator, value)] |
|
|
|
|
|
|
|
|
@api.multi |
|
|
@api.multi |
|
|
def name_get(self): |
|
|
def name_get(self): |
|
@ -142,9 +231,7 @@ CREATE OR REPLACE VIEW %(table)s AS |
|
|
this.this_partner_id.name, |
|
|
this.this_partner_id.name, |
|
|
this.type_selection_id.display_name, |
|
|
this.type_selection_id.display_name, |
|
|
this.other_partner_id.name, |
|
|
this.other_partner_id.name, |
|
|
) |
|
|
|
|
|
for this in self |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
) for this in self} |
|
|
|
|
|
|
|
|
@api.onchange('type_selection_id') |
|
|
@api.onchange('type_selection_id') |
|
|
def onchange_type_selection_id(self): |
|
|
def onchange_type_selection_id(self): |
|
@ -166,13 +253,11 @@ CREATE OR REPLACE VIEW %(table)s AS |
|
|
if partner: |
|
|
if partner: |
|
|
warning['message'] = ( |
|
|
warning['message'] = ( |
|
|
_('%s partner incompatible with relation type.') % |
|
|
_('%s partner incompatible with relation type.') % |
|
|
side.title() |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
side.title()) |
|
|
else: |
|
|
else: |
|
|
warning['message'] = ( |
|
|
warning['message'] = ( |
|
|
_('No %s partner available for relation type.') % |
|
|
_('No %s partner available for relation type.') % |
|
|
side |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
side) |
|
|
return warning |
|
|
return warning |
|
|
|
|
|
|
|
|
this_partner_domain = [] |
|
|
this_partner_domain = [] |
|
@ -180,45 +265,50 @@ CREATE OR REPLACE VIEW %(table)s AS |
|
|
if self.type_selection_id.contact_type_this: |
|
|
if self.type_selection_id.contact_type_this: |
|
|
this_partner_domain.append(( |
|
|
this_partner_domain.append(( |
|
|
'is_company', '=', |
|
|
'is_company', '=', |
|
|
self.type_selection_id.contact_type_this == 'c' |
|
|
|
|
|
)) |
|
|
|
|
|
|
|
|
self.type_selection_id.contact_type_this == 'c')) |
|
|
if self.type_selection_id.partner_category_this: |
|
|
if self.type_selection_id.partner_category_this: |
|
|
this_partner_domain.append(( |
|
|
this_partner_domain.append(( |
|
|
'category_id', 'in', |
|
|
'category_id', 'in', |
|
|
self.type_selection_id.partner_category_this.ids |
|
|
|
|
|
)) |
|
|
|
|
|
|
|
|
self.type_selection_id.partner_category_this.ids)) |
|
|
if self.type_selection_id.contact_type_other: |
|
|
if self.type_selection_id.contact_type_other: |
|
|
other_partner_domain.append(( |
|
|
other_partner_domain.append(( |
|
|
'is_company', '=', |
|
|
'is_company', '=', |
|
|
self.type_selection_id.contact_type_other == 'c' |
|
|
|
|
|
)) |
|
|
|
|
|
|
|
|
self.type_selection_id.contact_type_other == 'c')) |
|
|
if self.type_selection_id.partner_category_other: |
|
|
if self.type_selection_id.partner_category_other: |
|
|
other_partner_domain.append(( |
|
|
other_partner_domain.append(( |
|
|
'category_id', 'in', |
|
|
'category_id', 'in', |
|
|
self.type_selection_id.partner_category_other.ids |
|
|
|
|
|
)) |
|
|
|
|
|
|
|
|
self.type_selection_id.partner_category_other.ids)) |
|
|
result = {'domain': { |
|
|
result = {'domain': { |
|
|
'this_partner_id': this_partner_domain, |
|
|
'this_partner_id': this_partner_domain, |
|
|
'other_partner_id': other_partner_domain, |
|
|
|
|
|
}} |
|
|
|
|
|
|
|
|
'other_partner_id': other_partner_domain}} |
|
|
# Check wether domain results in no choice or wrong choice of partners: |
|
|
# Check wether domain results in no choice or wrong choice of partners: |
|
|
warning = {} |
|
|
warning = {} |
|
|
|
|
|
partner_model = self.env['res.partner'] |
|
|
if this_partner_domain: |
|
|
if this_partner_domain: |
|
|
|
|
|
this_partner = False |
|
|
|
|
|
if bool(self.this_partner_id.id): |
|
|
|
|
|
this_partner = self.this_partner_id |
|
|
|
|
|
else: |
|
|
|
|
|
this_partner_id = \ |
|
|
|
|
|
'default_this_partner_id' in self.env.context and \ |
|
|
|
|
|
self.env.context['default_this_partner_id'] or \ |
|
|
|
|
|
'active_id' in self.env.context and \ |
|
|
|
|
|
self.env.context['active_id'] or \ |
|
|
|
|
|
False |
|
|
|
|
|
if this_partner_id: |
|
|
|
|
|
this_partner = partner_model.browse(this_partner_id) |
|
|
warning = check_partner_domain( |
|
|
warning = check_partner_domain( |
|
|
self.this_partner_id, this_partner_domain, _('this') |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
this_partner, this_partner_domain, _('this')) |
|
|
if not warning and other_partner_domain: |
|
|
if not warning and other_partner_domain: |
|
|
warning = check_partner_domain( |
|
|
warning = check_partner_domain( |
|
|
self.other_partner_id, other_partner_domain, _('other') |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
self.other_partner_id, other_partner_domain, _('other')) |
|
|
if warning: |
|
|
if warning: |
|
|
result['warning'] = warning |
|
|
result['warning'] = warning |
|
|
return result |
|
|
return result |
|
|
|
|
|
|
|
|
@api.onchange( |
|
|
@api.onchange( |
|
|
'this_partner_id', |
|
|
'this_partner_id', |
|
|
'other_partner_id', |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
'other_partner_id') |
|
|
def onchange_partner_id(self): |
|
|
def onchange_partner_id(self): |
|
|
"""Set domain on type_selection_id based on partner(s) selected.""" |
|
|
"""Set domain on type_selection_id based on partner(s) selected.""" |
|
|
|
|
|
|
|
@ -233,15 +323,13 @@ CREATE OR REPLACE VIEW %(table)s AS |
|
|
return warning |
|
|
return warning |
|
|
test_domain = ( |
|
|
test_domain = ( |
|
|
[('id', '=', self.type_selection_id.id)] + |
|
|
[('id', '=', self.type_selection_id.id)] + |
|
|
type_selection_domain |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
type_selection_domain) |
|
|
type_model = self.env['res.partner.relation.type.selection'] |
|
|
type_model = self.env['res.partner.relation.type.selection'] |
|
|
types_found = type_model.search(test_domain, limit=1) |
|
|
types_found = type_model.search(test_domain, limit=1) |
|
|
if not types_found: |
|
|
if not types_found: |
|
|
warning['title'] = _('Error!') |
|
|
warning['title'] = _('Error!') |
|
|
warning['message'] = _( |
|
|
warning['message'] = _( |
|
|
'Relation type incompatible with selected partner(s).' |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
'Relation type incompatible with selected partner(s).') |
|
|
return warning |
|
|
return warning |
|
|
|
|
|
|
|
|
type_selection_domain = [] |
|
|
type_selection_domain = [] |
|
@ -254,8 +342,7 @@ CREATE OR REPLACE VIEW %(table)s AS |
|
|
'|', |
|
|
'|', |
|
|
('partner_category_this', '=', False), |
|
|
('partner_category_this', '=', False), |
|
|
('partner_category_this', 'in', |
|
|
('partner_category_this', 'in', |
|
|
self.this_partner_id.category_id.ids), |
|
|
|
|
|
] |
|
|
|
|
|
|
|
|
self.this_partner_id.category_id.ids)] |
|
|
if self.other_partner_id: |
|
|
if self.other_partner_id: |
|
|
type_selection_domain += [ |
|
|
type_selection_domain += [ |
|
|
'|', |
|
|
'|', |
|
@ -265,11 +352,9 @@ CREATE OR REPLACE VIEW %(table)s AS |
|
|
'|', |
|
|
'|', |
|
|
('partner_category_other', '=', False), |
|
|
('partner_category_other', '=', False), |
|
|
('partner_category_other', 'in', |
|
|
('partner_category_other', 'in', |
|
|
self.other_partner_id.category_id.ids), |
|
|
|
|
|
] |
|
|
|
|
|
|
|
|
self.other_partner_id.category_id.ids)] |
|
|
result = {'domain': { |
|
|
result = {'domain': { |
|
|
'type_selection_id': type_selection_domain, |
|
|
|
|
|
}} |
|
|
|
|
|
|
|
|
'type_selection_id': type_selection_domain}} |
|
|
# Check wether domain results in no choice or wrong choice for |
|
|
# Check wether domain results in no choice or wrong choice for |
|
|
# type_selection_id: |
|
|
# type_selection_id: |
|
|
warning = check_type_selection_domain(type_selection_domain) |
|
|
warning = check_type_selection_domain(type_selection_domain) |
|
@ -278,68 +363,123 @@ CREATE OR REPLACE VIEW %(table)s AS |
|
|
return result |
|
|
return result |
|
|
|
|
|
|
|
|
@api.model |
|
|
@api.model |
|
|
def _correct_vals(self, vals): |
|
|
|
|
|
|
|
|
def _correct_vals(self, vals, type_selection): |
|
|
"""Fill left and right partner from this and other partner.""" |
|
|
"""Fill left and right partner from this and other partner.""" |
|
|
vals = vals.copy() |
|
|
vals = vals.copy() |
|
|
|
|
|
if 'type_selection_id' in vals: |
|
|
|
|
|
vals['type_id'] = type_selection.type_id.id |
|
|
|
|
|
if type_selection.is_inverse: |
|
|
|
|
|
if 'this_partner_id' in vals: |
|
|
|
|
|
vals['right_partner_id'] = vals['this_partner_id'] |
|
|
|
|
|
if 'other_partner_id' in vals: |
|
|
|
|
|
vals['left_partner_id'] = vals['other_partner_id'] |
|
|
|
|
|
else: |
|
|
if 'this_partner_id' in vals: |
|
|
if 'this_partner_id' in vals: |
|
|
vals['left_partner_id'] = vals['this_partner_id'] |
|
|
vals['left_partner_id'] = vals['this_partner_id'] |
|
|
del vals['this_partner_id'] |
|
|
|
|
|
if 'other_partner_id' in vals: |
|
|
if 'other_partner_id' in vals: |
|
|
vals['right_partner_id'] = vals['other_partner_id'] |
|
|
vals['right_partner_id'] = vals['other_partner_id'] |
|
|
del vals['other_partner_id'] |
|
|
|
|
|
if 'type_selection_id' not in vals: |
|
|
|
|
|
return vals |
|
|
|
|
|
selection = self.type_selection_id.browse(vals['type_selection_id']) |
|
|
|
|
|
type_id = selection.type_id.id |
|
|
|
|
|
is_inverse = selection.is_inverse |
|
|
|
|
|
vals['type_id'] = type_id |
|
|
|
|
|
del vals['type_selection_id'] |
|
|
|
|
|
# Need to switch right and left partner if we are in reverse id: |
|
|
|
|
|
if 'left_partner_id' in vals or 'right_partner_id' in vals: |
|
|
|
|
|
if is_inverse: |
|
|
|
|
|
left_partner_id = False |
|
|
|
|
|
right_partner_id = False |
|
|
|
|
|
if 'left_partner_id' in vals: |
|
|
|
|
|
right_partner_id = vals['left_partner_id'] |
|
|
|
|
|
del vals['left_partner_id'] |
|
|
|
|
|
if 'right_partner_id' in vals: |
|
|
|
|
|
left_partner_id = vals['right_partner_id'] |
|
|
|
|
|
del vals['right_partner_id'] |
|
|
|
|
|
if left_partner_id: |
|
|
|
|
|
vals['left_partner_id'] = left_partner_id |
|
|
|
|
|
if right_partner_id: |
|
|
|
|
|
vals['right_partner_id'] = right_partner_id |
|
|
|
|
|
|
|
|
# Delete values not in underlying table: |
|
|
|
|
|
for key in ( |
|
|
|
|
|
'this_partner_id', |
|
|
|
|
|
'type_selection_id', |
|
|
|
|
|
'other_partner_id'): |
|
|
|
|
|
if key in vals: |
|
|
|
|
|
del vals[key] |
|
|
return vals |
|
|
return vals |
|
|
|
|
|
|
|
|
|
|
|
@api.multi |
|
|
|
|
|
def get_base_resource(self): |
|
|
|
|
|
"""Get base resource from res_model and res_id.""" |
|
|
|
|
|
self.ensure_one() |
|
|
|
|
|
base_model = self.env[self.res_model] |
|
|
|
|
|
return base_model.browse([self.res_id]) |
|
|
|
|
|
|
|
|
|
|
|
@api.multi |
|
|
|
|
|
def write_resource(self, base_resource, vals): |
|
|
|
|
|
"""write handled by base resource.""" |
|
|
|
|
|
self.ensure_one() |
|
|
|
|
|
# write for models other then res.partner.relation SHOULD |
|
|
|
|
|
# be handled in inherited models: |
|
|
|
|
|
relation_model = self.env['res.partner.relation'] |
|
|
|
|
|
assert self.res_model == relation_model._name |
|
|
|
|
|
base_resource.write(vals) |
|
|
|
|
|
|
|
|
|
|
|
@api.model |
|
|
|
|
|
def _get_type_selection_from_vals(self, vals): |
|
|
|
|
|
"""Get type_selection_id straight from vals or compute from type_id. |
|
|
|
|
|
""" |
|
|
|
|
|
type_selection_id = vals.get('type_selection_id', False) |
|
|
|
|
|
if not type_selection_id: |
|
|
|
|
|
type_id = vals.get('type_id', False) |
|
|
|
|
|
if type_id: |
|
|
|
|
|
is_inverse = vals.get('is_inverse') |
|
|
|
|
|
type_selection_id = type_id * 2 + (is_inverse and 1 or 0) |
|
|
|
|
|
return type_selection_id and self.type_selection_id.browse( |
|
|
|
|
|
type_selection_id) or False |
|
|
|
|
|
|
|
|
@api.multi |
|
|
@api.multi |
|
|
def write(self, vals): |
|
|
def write(self, vals): |
|
|
"""divert non-problematic writes to underlying table""" |
|
|
|
|
|
vals = self._correct_vals(vals) |
|
|
|
|
|
|
|
|
"""For model 'res.partner.relation' call write on underlying model. |
|
|
|
|
|
""" |
|
|
|
|
|
new_type_selection = self._get_type_selection_from_vals(vals) |
|
|
for rec in self: |
|
|
for rec in self: |
|
|
rec.relation_id.write(vals) |
|
|
|
|
|
|
|
|
type_selection = new_type_selection or rec.type_selection_id |
|
|
|
|
|
vals = rec._correct_vals(vals, type_selection) |
|
|
|
|
|
base_resource = rec.get_base_resource() |
|
|
|
|
|
rec.write_resource(base_resource, vals) |
|
|
|
|
|
# Invalidate cache to make res.partner.relation.all reflect changes |
|
|
|
|
|
# in underlying res.partner.relation: |
|
|
|
|
|
self.env.invalidate_all() |
|
|
return True |
|
|
return True |
|
|
|
|
|
|
|
|
|
|
|
@api.model |
|
|
|
|
|
def _compute_base_name(self, type_selection): |
|
|
|
|
|
"""This will be overridden for each inherit model.""" |
|
|
|
|
|
return 'relation' |
|
|
|
|
|
|
|
|
|
|
|
@api.model |
|
|
|
|
|
def _compute_id(self, base_resource, type_selection): |
|
|
|
|
|
"""Compute id. Allow for enhancements in inherit model.""" |
|
|
|
|
|
base_name = self._compute_base_name(type_selection) |
|
|
|
|
|
key_offset = get_select_specification( |
|
|
|
|
|
base_name, type_selection.is_inverse)['key_offset'] |
|
|
|
|
|
return base_resource.id * self._get_padding() + key_offset |
|
|
|
|
|
|
|
|
|
|
|
@api.model |
|
|
|
|
|
def create_resource(self, vals, type_selection): |
|
|
|
|
|
relation_model = self.env['res.partner.relation'] |
|
|
|
|
|
return relation_model.create(vals) |
|
|
|
|
|
|
|
|
@api.model |
|
|
@api.model |
|
|
def create(self, vals): |
|
|
def create(self, vals): |
|
|
"""Divert non-problematic creates to underlying table. |
|
|
"""Divert non-problematic creates to underlying table. |
|
|
|
|
|
|
|
|
Create a res.partner.relation but return the converted id. |
|
|
Create a res.partner.relation but return the converted id. |
|
|
""" |
|
|
""" |
|
|
is_inverse = False |
|
|
|
|
|
if 'type_selection_id' in vals: |
|
|
|
|
|
selection = self.type_selection_id.browse( |
|
|
|
|
|
vals['type_selection_id'] |
|
|
|
|
|
) |
|
|
|
|
|
is_inverse = selection.is_inverse |
|
|
|
|
|
vals = self._correct_vals(vals) |
|
|
|
|
|
res = self.relation_id.create(vals) |
|
|
|
|
|
return_id = res.id * PADDING + (is_inverse and 1 or 0) |
|
|
|
|
|
return self.browse(return_id) |
|
|
|
|
|
|
|
|
type_selection = self._get_type_selection_from_vals(vals) |
|
|
|
|
|
if not type_selection: # Should not happen |
|
|
|
|
|
raise ValidationError( |
|
|
|
|
|
_('No relation type specified in vals: %s.') % vals) |
|
|
|
|
|
vals = self._correct_vals(vals, type_selection) |
|
|
|
|
|
base_resource = self.create_resource(vals, type_selection) |
|
|
|
|
|
res_id = self._compute_id(base_resource, type_selection) |
|
|
|
|
|
return self.browse(res_id) |
|
|
|
|
|
|
|
|
|
|
|
@api.multi |
|
|
|
|
|
def unlink_resource(self, base_resource): |
|
|
|
|
|
"""Delegate unlink to underlying model.""" |
|
|
|
|
|
self.ensure_one() |
|
|
|
|
|
# unlink for models other then res.partner.relation SHOULD |
|
|
|
|
|
# be handled in inherited models: |
|
|
|
|
|
relation_model = self.env['res.partner.relation'] |
|
|
|
|
|
assert self.res_model == relation_model._name |
|
|
|
|
|
base_resource.unlink() |
|
|
|
|
|
|
|
|
@api.multi |
|
|
@api.multi |
|
|
def unlink(self): |
|
|
def unlink(self): |
|
|
"""divert non-problematic creates to underlying table""" |
|
|
|
|
|
# pylint: disable=arguments-differ |
|
|
|
|
|
|
|
|
"""For model 'res.partner.relation' call unlink on underlying model. |
|
|
|
|
|
""" |
|
|
for rec in self: |
|
|
for rec in self: |
|
|
rec.relation_id.unlink() |
|
|
|
|
|
|
|
|
base_resource = rec.get_base_resource() |
|
|
|
|
|
rec.unlink_resource(base_resource) |
|
|
return True |
|
|
return True |