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.
 
 

340 lines
12 KiB

# -*- coding: utf-8 -*-
# © 2013-2016 Therp BV <http://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api, exceptions, _
from openerp.osv.expression import FALSE_LEAF
from .res_partner import PADDING
class ResPartnerRelation(models.Model):
'''Model res.partner.relation is used to describe all links or relations
between partners in the database.
In many parts of the code we have to know whether the active partner is
the left partner, or the right partner. If the active partner is the
right partner we have to show the inverse name.
Because the active partner is crucial for the working of partner
relationships, we make sure on the res.partner model that the partner id
is set in the context where needed.
'''
_name = 'res.partner.relation'
_description = 'Partner relation'
_order = 'active desc, date_start desc, date_end desc'
type_selection_id = fields.Many2one(
'res.partner.relation.type.selection',
compute='_compute_fields',
fnct_inv=lambda *args: None,
string='Type',
)
partner_id_display = fields.Many2one(
'res.partner',
compute='_compute_fields',
fnct_inv=lambda *args: None,
string='Partner',
)
allow_self = fields.Boolean(related='type_id.allow_self')
left_contact_type = fields.Selection(
lambda s: s.env['res.partner.relation.type']._get_partner_types(),
'Left Partner Type',
compute='_compute_any_partner_id',
store=True,
)
right_contact_type = fields.Selection(
lambda s: s.env['res.partner.relation.type']._get_partner_types(),
'Right Partner Type',
compute='_compute_any_partner_id',
store=True,
)
any_partner_id = fields.Many2many(
'res.partner',
string='Partner',
compute='_compute_any_partner_id',
search='_search_any_partner_id'
)
left_partner_id = fields.Many2one(
'res.partner',
string='Source Partner',
required=True,
auto_join=True,
ondelete='cascade',
)
right_partner_id = fields.Many2one(
'res.partner',
string='Destination Partner',
required=True,
auto_join=True,
ondelete='cascade',
)
type_id = fields.Many2one(
'res.partner.relation.type',
string='Type',
required=True,
auto_join=True,
)
date_start = fields.Date('Starting date')
date_end = fields.Date('Ending date')
active = fields.Boolean('Active', default=True)
@api.multi
def _compute_fields(self):
for this in self:
on_right_partner = this._on_right_partner()
this.type_selection_id = self\
.env['res.partner.relation.type.selection']\
.browse(this.type_id.id * PADDING +
(on_right_partner and 1 or 0))
this.partner_id_display = (
this.left_partner_id
if on_right_partner
else this.right_partner_id
)
@api.onchange('type_selection_id')
def _onchange_type_selection_id(self):
'''Set domain on partner_id_display, when selection a relation type'''
result = {
'domain': {'partner_id_display': [FALSE_LEAF]},
}
if not self.type_selection_id:
return result
type_id, is_reverse = self.type_selection_id\
.get_type_from_selection_id()
self.type_id = self.env['res.partner.relation.type'].browse(type_id)
partner_domain = []
check_contact_type = self.type_id.contact_type_right
check_partner_category = self.type_id.partner_category_right
if is_reverse:
# partner_id_display is left partner
check_contact_type = self.type_id.contact_type_left
check_partner_category = self.type_id.partner_category_left
if check_contact_type == 'c':
partner_domain.append(('is_company', '=', True))
if check_contact_type == 'p':
partner_domain.append(('is_company', '=', False))
if check_partner_category:
partner_domain.append(
('category_id', 'child_of', check_partner_category.ids))
result['domain']['partner_id_display'] = partner_domain
return result
@api.one
@api.depends('left_partner_id', 'right_partner_id')
def _compute_any_partner_id(self):
self.left_contact_type = self.left_partner_id.get_partner_type()
self.right_contact_type = self.right_partner_id.get_partner_type()
self.any_partner_id = self.left_partner_id + self.right_partner_id
@api.model
def _search_any_partner_id(self, operator, value):
return [
'|',
('left_partner_id', operator, value),
('right_partner_id', operator, value),
]
@api.multi
def _on_right_partner(self):
'''Determine wether functions are called in a situation where the
active partner is the right partner. Default False!
'''
return set(self.mapped('right_partner_id').ids) &\
set(self.env.context.get('active_ids', []))
@api.model
def _correct_vals(self, vals):
"""Fill type and left and right partner id, according to whether
we have a normal relation type or an inverse relation type
"""
vals = vals.copy()
if 'type_selection_id' not in vals:
return vals
type_id, is_reverse = self\
.env['res.partner.relation.type.selection']\
.browse(vals['type_selection_id'])\
.get_type_from_selection_id()
vals['type_id'] = type_id
if self._context.get('active_id'):
if is_reverse:
vals['right_partner_id'] = self._context['active_id']
else:
vals['left_partner_id'] = self._context['active_id']
if vals.get('partner_id_display'):
if is_reverse:
vals['left_partner_id'] = vals['partner_id_display']
else:
vals['right_partner_id'] = vals['partner_id_display']
if vals.get('other_partner_id'):
if is_reverse:
vals['left_partner_id'] = vals['other_partner_id']
else:
vals['right_partner_id'] = vals['other_partner_id']
del vals['other_partner_id']
if vals.get('this_partner_id'):
if is_reverse:
vals['right_partner_id'] = vals['this_partner_id']
else:
vals['left_partner_id'] = vals['this_partner_id']
del vals['this_partner_id']
if vals.get('contact_type'):
del vals['contact_type']
return vals
@api.multi
def write(self, vals):
"""Override write to correct values, before being stored."""
vals = self._correct_vals(vals)
return super(ResPartnerRelation, self).write(vals)
@api.model
def create(self, vals):
"""Override create to correct values, before being stored."""
vals = self._correct_vals(vals)
return super(ResPartnerRelation, self).create(vals)
@api.one
@api.constrains('date_start', 'date_end')
def _check_dates(self):
"""End date should not be before start date, if not filled
:raises exceptions.Warning: When constraint is violated
"""
if (self.date_start and self.date_end and
self.date_start > self.date_end):
raise exceptions.Warning(
_('The starting date cannot be after the ending date.')
)
@api.one
@api.constrains('left_partner_id', 'type_id')
def _check_partner_type_left(self):
"""Check left partner for required company or person
:raises exceptions.Warning: When constraint is violated
"""
self._check_partner_type("left")
@api.one
@api.constrains('right_partner_id', 'type_id')
def _check_partner_type_right(self):
"""Check right partner for required company or person
:raises exceptions.Warning: When constraint is violated
"""
self._check_partner_type("right")
@api.one
def _check_partner_type(self, side):
"""Check partner to left or right for required company or person
:param str side: left or right
:raises exceptions.Warning: When constraint is violated
"""
assert side in ['left', 'right']
ptype = getattr(self.type_id, "contact_type_%s" % side)
company = getattr(self, '%s_partner_id' % side).is_company
if (ptype == 'c' and not company) or (ptype == 'p' and company):
raise exceptions.Warning(
_('The %s partner is not applicable for this relation type.') %
side
)
@api.one
@api.constrains('left_partner_id', 'right_partner_id')
def _check_not_with_self(self):
"""Not allowed to link partner to same partner
:raises exceptions.Warning: When constraint is violated
"""
if self.left_partner_id == self.right_partner_id:
if not self.allow_self:
raise exceptions.Warning(
_('Partners cannot have a relation with themselves.')
)
@api.one
@api.constrains('left_partner_id', 'right_partner_id', 'active')
def _check_relation_uniqueness(self):
"""Forbid multiple active relations of the same type between the same
partners
:raises exceptions.Warning: When constraint is violated
"""
if not self.active:
return
domain = [
('type_id', '=', self.type_id.id),
('active', '=', True),
('id', '!=', self.id),
('left_partner_id', '=', self.left_partner_id.id),
('right_partner_id', '=', self.right_partner_id.id),
]
if self.date_start:
domain += ['|', ('date_end', '=', False),
('date_end', '>=', self.date_start)]
if self.date_end:
domain += ['|', ('date_start', '=', False),
('date_start', '<=', self.date_end)]
if self.search(domain):
raise exceptions.Warning(
_('There is already a similar relation with overlapping dates')
)
@api.multi
def get_action_related_partners(self):
'''return a window action showing a list of partners taking part in the
relations names by ids. Context key 'partner_relations_show_side'
determines if we show 'left' side, 'right' side or 'all' (default)
partners.
If active_model is res.partner.relation.all, left=this and
right=other'''
field_names = {}
if self.env.context.get('active_model', self._name) == self._name:
field_names = {
'left': ['left'],
'right': ['right'],
'all': ['left', 'right']
}
elif self.env.context.get('active_model') ==\
'res.partner.relation.all':
field_names = {
'left': ['this'],
'right': ['other'],
'all': ['this', 'other']
}
else:
assert False, 'Unknown active_model!'
partners = self.env['res.partner'].browse([])
field_names = field_names[
self.env.context.get('partner_relations_show_side', 'all')
]
field_names = ['%s_partner_id' % n for n in field_names]
for relation in self.env[self.env.context.get('active_model')].browse(
self.ids):
for name in field_names:
partners += relation[name]
return {
'name': _('Related partners'),
'type': 'ir.actions.act_window',
'res_model': 'res.partner',
'domain': [('id', 'in', partners.ids)],
'views': [(False, 'tree'), (False, 'form')],
'view_type': 'form'
}