You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

345 lines
12 KiB

# -*- coding: utf-8 -*-
# © 2014-2016 Therp BV <http://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
"""Abstract model to show each relation from two sides."""
from psycopg2.extensions import AsIs
from openerp import _, api, fields, models
from openerp.tools import drop_view_if_exists
PADDING = 10
_RECORD_TYPES = [
('a', 'Left partner to right partner'),
('b', 'Right partner to left partner'),
]
class ResPartnerRelationAll(models.AbstractModel):
"""Abstract model to show each relation from two sides."""
_auto = False
_log_access = False
_name = 'res.partner.relation.all'
_description = 'All (non-inverse + inverse) relations between partners'
_order = (
'this_partner_id, type_selection_id,'
'date_end desc, date_start desc'
)
_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:
def _auto_init(self, cr, context=None):
self._additional_view_fields.append('my_field')
return super(ResPartnerRelationAll, self)._auto_init(
cr, context=context)
my_field = fields...
"""
this_partner_id = fields.Many2one(
comodel_name='res.partner',
string='One Partner',
required=True,
)
other_partner_id = fields.Many2one(
comodel_name='res.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',
readonly=True,
)
date_start = fields.Date('Starting date')
date_end = fields.Date('Ending date')
active = fields.Boolean(
string='Active',
help="Records with date_end in the past are inactive",
)
any_partner_id = fields.Many2many(
comodel_name='res.partner',
string='Partner',
compute=lambda self: None,
search='_search_any_partner_id'
)
def _auto_init(self, cr, context=None):
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(
"""\
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),
}
)
return super(ResPartnerRelationAll, self)._auto_init(
cr, context=context
)
@api.model
def _search_any_partner_id(self, operator, value):
"""Search relation with partner, no matter on which side."""
# pylint: disable=no-self-use
return [
'|',
('this_partner_id', operator, value),
('other_partner_id', operator, value),
]
@api.multi
def name_get(self):
return {
this.id: '%s %s %s' % (
this.this_partner_id.name,
this.type_selection_id.display_name,
this.other_partner_id.name,
)
for this in self
}
@api.onchange('type_selection_id')
def onchange_type_selection_id(self):
"""Add domain on partners according to category and contact_type."""
def check_partner_domain(partner, partner_domain, side):
"""Check wether partner_domain results in empty selection
for partner, or wrong selection of partner already selected.
"""
warning = {}
if partner:
test_domain = [('id', '=', partner.id)] + partner_domain
else:
test_domain = partner_domain
partner_model = self.env['res.partner']
partners_found = partner_model.search(test_domain, limit=1)
if not partners_found:
warning['title'] = _('Error!')
if partner:
warning['message'] = (
_('%s partner incompatible with relation type.') %
side.title()
)
else:
warning['message'] = (
_('No %s partner available for relation type.') %
side
)
return warning
this_partner_domain = []
other_partner_domain = []
if self.type_selection_id.contact_type_this:
this_partner_domain.append((
'is_company', '=',
self.type_selection_id.contact_type_this == 'c'
))
if self.type_selection_id.partner_category_this:
this_partner_domain.append((
'category_id', 'in',
self.type_selection_id.partner_category_this.ids
))
if self.type_selection_id.contact_type_other:
other_partner_domain.append((
'is_company', '=',
self.type_selection_id.contact_type_other == 'c'
))
if self.type_selection_id.partner_category_other:
other_partner_domain.append((
'category_id', 'in',
self.type_selection_id.partner_category_other.ids
))
result = {'domain': {
'this_partner_id': this_partner_domain,
'other_partner_id': other_partner_domain,
}}
# Check wether domain results in no choice or wrong choice of partners:
warning = {}
if this_partner_domain:
warning = check_partner_domain(
self.this_partner_id, this_partner_domain, _('this')
)
if not warning and other_partner_domain:
warning = check_partner_domain(
self.other_partner_id, other_partner_domain, _('other')
)
if warning:
result['warning'] = warning
return result
@api.onchange(
'this_partner_id',
'other_partner_id',
)
def onchange_partner_id(self):
"""Set domain on type_selection_id based on partner(s) selected."""
def check_type_selection_domain(type_selection_domain):
"""If type_selection_id already selected, check wether it
is compatible with the computed type_selection_domain. An empty
selection can practically only occur in a practically empty
database, and will not lead to problems. Therefore not tested.
"""
warning = {}
if not (type_selection_domain and self.type_selection_id):
return warning
test_domain = (
[('id', '=', self.type_selection_id.id)] +
type_selection_domain
)
type_model = self.env['res.partner.relation.type.selection']
types_found = type_model.search(test_domain, limit=1)
if not types_found:
warning['title'] = _('Error!')
warning['message'] = _(
'Relation type incompatible with selected partner(s).'
)
return warning
type_selection_domain = []
if self.this_partner_id:
type_selection_domain += [
'|',
('contact_type_this', '=', False),
('contact_type_this', '=',
self.this_partner_id.get_partner_type()),
'|',
('partner_category_this', '=', False),
('partner_category_this', 'in',
self.this_partner_id.category_id.ids),
]
if self.other_partner_id:
type_selection_domain += [
'|',
('contact_type_other', '=', False),
('contact_type_other', '=',
self.other_partner_id.get_partner_type()),
'|',
('partner_category_other', '=', False),
('partner_category_other', 'in',
self.other_partner_id.category_id.ids),
]
result = {'domain': {
'type_selection_id': type_selection_domain,
}}
# Check wether domain results in no choice or wrong choice for
# type_selection_id:
warning = check_type_selection_domain(type_selection_domain)
if warning:
result['warning'] = warning
return result
@api.model
def _correct_vals(self, vals):
"""Fill left and right partner from this and other partner."""
vals = vals.copy()
if 'this_partner_id' in vals:
vals['left_partner_id'] = vals['this_partner_id']
del vals['this_partner_id']
if 'other_partner_id' in vals:
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
return vals
@api.multi
def write(self, vals):
"""divert non-problematic writes to underlying table"""
vals = self._correct_vals(vals)
for rec in self:
rec.relation_id.write(vals)
return True
@api.model
def create(self, vals):
"""Divert non-problematic creates to underlying table.
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)
@api.multi
def unlink(self):
"""divert non-problematic creates to underlying table"""
# pylint: disable=arguments-differ
for rec in self:
rec.relation_id.unlink()
return True