Browse Source

[IMP] partner_relations_in_tab. Clean Code.

pull/623/head
Ronald Portier 7 years ago
parent
commit
b29bbf1ff4
  1. 21
      partner_relations_in_tab/__init__.py
  2. 4
      partner_relations_in_tab/__openerp__.py
  3. 362
      partner_relations_in_tab/model/res_partner.py
  4. 86
      partner_relations_in_tab/model/res_partner_relation_type.py
  5. 8
      partner_relations_in_tab/tests/__init__.py
  6. 124
      partner_relations_in_tab/tests/test_partner_tabs.py

21
partner_relations_in_tab/__init__.py

@ -1,21 +1,4 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
# Copyright 2014-2018 Therp BV <https://therp.nl>.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import model

4
partner_relations_in_tab/__openerp__.py

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# © 2014-2017 Therp BV <http://therp.nl>.
# License AGPL-3.0 or later <http://www.gnu.org/licenses/agpl.html>.
# Copyright 2014-2018 Therp BV <https://therp.nl>.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
"name": "Show partner relations in own tab",
"version": "7.0.1.0.0",

362
partner_relations_in_tab/model/res_partner.py

@ -1,179 +1,215 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
# Copyright 2014-2018 Therp BV <https://therp.nl>.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging
from lxml import etree
from openerp.osv.orm import Model, transfer_modifiers_to_node
from openerp.osv import expression
from openerp.osv import expression, fields
from openerp.tools.translate import _
class ResPartner(Model):
_inherit = 'res.partner'
def _get_relation_ids_select(self, cr, uid, ids, field_name, arg,
context=None):
cr.execute(
'''select r.id, left_partner_id, right_partner_id
from res_partner_relation r
join res_partner_relation_type t
on r.type_id = t.id
where ((left_partner_id in %s and own_tab_left=False)
or (right_partner_id in %s and own_tab_right=False))''' +
' order by ' + self.pool['res.partner.relation']._order,
(tuple(ids), tuple(ids))
)
return cr.fetchall()
def _create_relation_type_tab(
self, cr, uid, rel_type, inverse, field_names, context=None):
'''Create an xml node containing the relation's tab to be added to the
view. Add the field(s) created on the form to field_names.'''
name = rel_type.name if not inverse else rel_type.name_inverse
contact_type = rel_type['contact_type_' +
('left' if not inverse else 'right')]
partner_category = rel_type['partner_category_' +
('left' if not inverse
else 'right')]
tab = etree.Element('page')
tab.set('string', name)
invisible = [('id', '=', False)]
if contact_type:
invisible = expression.OR([
invisible,
[('is_company', '=', contact_type != 'c')]])
if partner_category:
invisible = expression.OR([
invisible,
[('category_id', '!=', partner_category.id)]])
attrs = {
'invisible': invisible,
}
tab.set('attrs', repr(attrs))
transfer_modifiers_to_node(attrs, tab)
field_name = 'relation_ids_own_tab_%s_%s' % (
rel_type.id,
'left' if not inverse else 'right')
field_names.append(field_name)
this_partner_name = '%s_partner_id' % (
'left' if not inverse else 'right')
other_partner_name = '%s_partner_id' % (
'left' if inverse else 'right')
from openerp import SUPERUSER_ID
_logger = logging.getLogger(__name__) # pylint: disable=invalid-name
NAME_PREFIX = 'relation_ids_tab'
class Tab(object):
def __init__(self, source, side):
"""Create tab from source.
In this version source can be assumed to be a partner.relation.type.
"""
self.id = source.id
self.side = side
if side == 'left':
self.name = source.name
self.contact_type = source.contact_type_left
self.category_id = source.partner_category_left
self.other_contact_type = source.contact_type_right
self.other_category_id = source.partner_category_right
self.other_side = 'right'
else:
self.name = source.name_inverse
self.contact_type = source.contact_type_right
self.category_id = source.partner_category_right
self.other_contact_type = source.contact_type_left
self.other_category_id = source.partner_category_left
self.other_side = 'left'
def get_fieldname(self):
return '%s_%s_%s' % (NAME_PREFIX, self.id, self.side)
def get_domain(self):
return [('type_id', '=', self.id)]
def create_page(self):
tab_page = etree.Element('page')
self._set_page_attrs(tab_page)
field = etree.Element(
'field',
name=field_name,
context=('{"default_type_id": %s, "default_%s": id, '
'"active_test": False}') % (
rel_type.id,
this_partner_name))
tab.append(field)
name=self.get_fieldname(),
context=(
'{"default_type_id": %s, "default_%s_partner_id": id, '
'"active_test": False}') % (self.id, self.side))
tab_page.append(field)
tree = etree.Element('tree', editable='bottom')
field.append(tree)
onchange_type_values = self.pool['res.partner.relation']\
.on_change_type_selection_id(cr, uid, None,
rel_type.id * 10 +
(1 if inverse else 0),
context=context)
tree.append(etree.Element(
'field', name='%s_partner_id' % self.side, invisible='True'))
tree.append(etree.Element(
'field',
string=_('Partner'),
domain=repr(
onchange_type_values['domain']['partner_id_display']),
domain=repr(self._get_other_partner_domain()),
widget='many2one_clickable',
name=other_partner_name))
tree.append(etree.Element(
'field',
name='date_start'))
tree.append(etree.Element(
'field',
name='date_end'))
tree.append(etree.Element(
'field',
name='active'))
tree.append(etree.Element('field', name='type_id',
invisible='True'))
tree.append(etree.Element('field', name=this_partner_name,
invisible='True'))
return tab
def _add_relation_type_tab(
self, cr, uid, rel_type, inverse, field_names, relation_tab,
context=None):
'''add the xml node to the view'''
tab = self._create_relation_type_tab(
cr, uid, rel_type, inverse, field_names, context=context)
relation_tab.addnext(tab)
def fields_view_get(self, cr, uid, view_id=None, view_type='form',
context=None, toolbar=False, submenu=False):
if context is None:
context = {}
name='%s_partner_id' % self.other_side))
tree.append(etree.Element('field', name='date_start'))
tree.append(etree.Element('field', name='date_end'))
tree.append(etree.Element('field', name='active'))
tree.append(etree.Element('field', name='type_id', invisible='True'))
return tab_page
def _get_other_partner_domain(self):
partner_domain = []
if self.other_contact_type == 'c':
partner_domain.append(('is_company', '=', True))
if self.other_contact_type == 'p':
partner_domain.append(('is_company', '=', False))
if self.other_category_id:
partner_domain.append(
('category_id', 'child_of', self.other_category_id))
return partner_domain
def _set_page_attrs(self, tab_page):
tab_page.set('string', self.name)
invisible = [('id', '=', False)]
if self.contact_type:
invisible = expression.OR([
invisible,
[('is_company', '=', self.contact_type != 'c')]])
if self.category_id:
invisible = expression.OR([
invisible,
[('category_id', '!=', self.category_id)]])
attrs = {'invisible': invisible}
tab_page.set('attrs', repr(attrs))
transfer_modifiers_to_node(attrs, tab_page)
class ResPartner(Model):
_inherit = 'res.partner'
def _make_tab(self, source, side):
return Tab(source, side)
def _register_hook(self, cr):
"""This function is automatically called by Odoo on all models."""
self._update_tab_fields(cr)
def _update_tab_fields(self, cr):
"""Create a field for each tab that might be shown for a partner."""
deprecated_tab_fields = [
name for name in self._columns.copy()
if name.startswith(NAME_PREFIX)]
tabs = self._get_tabs(cr)
for tab in tabs:
fieldname = tab.get_fieldname()
if fieldname in self._columns:
self._update_tab_field(tab)
else:
self._add_tab_field(tab)
if fieldname in deprecated_tab_fields:
deprecated_tab_fields.remove(fieldname) # not deprecated
for fieldname in deprecated_tab_fields:
self._delete_tab_field(fieldname)
def _get_tabs(self, cr):
tabs = []
relation_type_model = self.pool['res.partner.relation.type']
relation_type_domain = [
'|',
('own_tab_left', '=', True),
('own_tab_right', '=', True)]
relation_type_ids = relation_type_model.search(
cr, SUPERUSER_ID, relation_type_domain)
for relation_type in relation_type_model.browse(
cr, SUPERUSER_ID, relation_type_ids):
if relation_type.own_tab_left:
new_tab = Tab(relation_type, 'left')
tabs.append(new_tab)
if relation_type.own_tab_right:
new_tab = Tab(relation_type, 'right')
tabs.append(new_tab)
return tabs
def _add_tab_field(self, tab):
field = fields.one2many(
'res.partner.relation',
'%s_partner_id' % tab.side,
string=tab.name,
domain=tab.get_domain())
fieldname = tab.get_fieldname()
_logger.info(_(
"Adding field %s to res.partner model.") % fieldname)
self._columns[fieldname] = field
def _update_tab_field(self, tab):
fieldname = tab.get_fieldname()
_logger.info(_(
"Updating field %s in res.partner model.") % fieldname)
self._columns[fieldname].string = tab.name
def _delete_tab_field(self, fieldname):
_logger.info(_(
"Deleting field %s from res.partner model.") % fieldname)
del self._columns[fieldname]
def fields_view_get(
self, cr, uid, view_id=None, view_type='form', context=None,
toolbar=False, submenu=False):
context = context or {}
result = super(ResPartner, self).fields_view_get(
cr, uid, view_id=view_id, view_type=view_type, context=context,
toolbar=toolbar, submenu=submenu)
if view_type == 'form' and not context.get('check_view_ids'):
res_partner_relation_type = self.pool['res.partner.relation.type']
own_tab_types = res_partner_relation_type.browse(
cr, uid,
res_partner_relation_type.search(
cr, uid,
[
'|',
('own_tab_left', '=', True),
('own_tab_right', '=', True)
],
context=context),
context=context)
view = etree.fromstring(result['arch'])
relation_tab = view.xpath(
'//field[@name="relation_ids"]/ancestor::page')
if not relation_tab:
return result
relation_tab = relation_tab[0]
field_names = []
if not view.xpath('//field[@name="id"]'):
view.append(etree.Element('field', name='id',
invisible='True'))
field_names.append('id')
for rel_type in own_tab_types:
if rel_type.own_tab_left:
self._add_relation_type_tab(
cr, uid, rel_type, False, field_names, relation_tab,
context=context)
if rel_type.own_tab_right:
self._add_relation_type_tab(
cr, uid, rel_type, True, field_names, relation_tab,
context=context)
result['arch'], fields = self\
._BaseModel__view_look_dom_arch(
cr, uid, view, result['view_id'], context=context)
for field_name in field_names:
result['fields'][field_name] = fields[field_name]
if view_type != 'form' or context.get('check_view_ids'):
return result
view = etree.fromstring(result['arch'])
extra_fields = self._add_tab_pages(cr, view)
result['arch'], view_fields = self._BaseModel__view_look_dom_arch(
cr, uid, view, result['view_id'], context=context)
for fieldname in extra_fields:
result['fields'][fieldname] = view_fields[fieldname]
return result
def _add_tab_pages(self, cr, view):
"""Adds the relevant tabs to the partner's formview."""
extra_fields = []
if not view.xpath('//field[@name="id"]'):
view.append(
etree.Element('field', name='id', invisible='True'))
extra_fields.append('id')
element_last_page_hook = view.xpath('//page[last()]')[0]
for tab in self._get_tabs(cr):
fieldname = tab.get_fieldname()
extra_fields.append(fieldname)
tab_page = tab.create_page()
_logger.debug(
_("Adding %s tab %s with arch: %s"),
tab.side, tab.name, etree.tostring(tab_page))
element_last_page_hook.addnext(tab_page)
return extra_fields
def _get_relation_ids_select(
self, cr, uid, ids, fieldname, arg, context=None):
"""Overide domain for other partner on default relations tab."""
cr.execute(
"""select r.id, left_partner_id, right_partner_id
from res_partner_relation r
join res_partner_relation_type t
on r.type_id = t.id
where ((left_partner_id in %s and own_tab_left=False)
or (right_partner_id in %s and own_tab_right=False))""" +
' order by ' + self.pool['res.partner.relation']._order,
(tuple(ids), tuple(ids)))
return cr.fetchall()

86
partner_relations_in_tab/model/res_partner_relation_type.py

@ -1,26 +1,8 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# This module copyright (C) 2014 Therp BV (<http://therp.nl>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
# Copyright 2014-2018 Therp BV <https://therp.nl>.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp.osv.orm import Model
from openerp.osv import fields
from openerp import SUPERUSER_ID
class ResPartnerRelationType(Model):
@ -36,55 +18,29 @@ class ResPartnerRelationType(Model):
'own_tab_right': False,
}
def _update_res_partner_fields(self, cr):
field_name_prefix = 'relation_ids_own_tab_'
field_name_format = field_name_prefix + '%s_%s'
res_partner = self.pool['res.partner']
for field_name in res_partner._columns.copy():
if field_name.startswith(field_name_prefix):
del res_partner._columns[field_name]
def add_field(relation, inverse):
field = fields.one2many(
'res.partner.relation',
'%s_partner_id' % ('left' if not inverse else 'right'),
string=relation['name' if not inverse else 'name_inverse'],
domain=[('type_id', '=', relation.id),
'|',
('active', '=', True),
('active', '=', False)])
field_name = field_name_format % (
relation.id,
'left' if not inverse else 'right')
res_partner._columns[field_name] = field
for relation in self.browse(
cr, SUPERUSER_ID,
self.search(
cr, SUPERUSER_ID,
[
'|',
('own_tab_left', '=', True),
('own_tab_right', '=', True),
])):
if relation.own_tab_left:
add_field(relation, False)
if relation.own_tab_right:
add_field(relation, True)
def _register_hook(self, cr):
self._update_res_partner_fields(cr)
def create(self, cr, uid, vals, context=None):
result = super(ResPartnerRelationType, self).create(
relation_type_id = super(ResPartnerRelationType, self).create(
cr, uid, vals, context=context)
if vals.get('own_tab_left') or vals.get('own_tab_right'):
self._update_res_partner_fields(cr)
return result
relation_type = self.browse(cr, uid, relation_type_id, context=context)
partner_model = self.pool['res.partner']
if relation_type.own_tab_left:
tab = partner_model._make_tab(relation_type, 'left')
partner_model._add_tab_field(tab)
if relation_type.own_tab_right:
tab = partner_model._make_tab(relation_type, 'right')
partner_model._add_tab_field(tab)
return relation_type_id
def write(self, cr, uid, ids, vals, context=None):
result = super(ResPartnerRelationType, self).write(
cr, uid, ids, vals, context=context)
if 'own_tab_left' in vals or 'own_tab_right' in vals:
self._update_res_partner_fields(cr)
partner_model = self.pool['res.partner']
partner_model._update_tab_fields(cr)
return result
def unlink(self, cr, uid, ids, context=None):
result = super(ResPartnerRelationType, self).unlink(
cr, uid, ids, context=context)
partner_model = self.pool['res.partner']
partner_model._update_tab_fields(cr)
return result

8
partner_relations_in_tab/tests/__init__.py

@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
# Copyright 2017-2018 Therp BV <https://therp.nl>.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import test_partner_tabs
checks = [
test_partner_tabs,
]

124
partner_relations_in_tab/tests/test_partner_tabs.py

@ -0,0 +1,124 @@
# -*- coding: utf-8 -*-
# Copyright 2014-2018 Therp BV <https://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from lxml import etree
from openerp.tests import common
class TestPartnerTabs(common.SingleTransactionCase):
post_install = True
def _get_tab_fieldname(self, relation_type, side):
tab = self._get_tab(relation_type, side)
return tab.get_fieldname()
def _get_tab(self, relation_type, side):
partner_model = self.registry('res.partner')
return partner_model._make_tab(relation_type, side)
def test_create_tab(self):
cr, uid = self.cr, self.uid
type_model = self.registry('res.partner.relation.type')
partner_model = self.registry('res.partner')
type_has_chairperson_id = type_model.create(
cr, uid, {
'name': 'has chairperson',
'name_inverse': 'is chairperson for',
'contact_type_left': 'c',
'own_tab_left': True,
'contact_type_right': 'p'})
type_has_chairperson = type_model.browse(
cr, uid, type_has_chairperson_id)
self.assertTrue(bool(type_has_chairperson))
# There should now be a field in res_partner for the new tab:
fieldname = self._get_tab_fieldname(type_has_chairperson, 'left')
self.assertTrue(fieldname in partner_model._columns)
# The form view for partner should now also contain the tab:
view = partner_model.fields_view_get(cr, uid, view_type='form')
tree = etree.fromstring(view['arch'])
field = tree.xpath('//field[@name="id"]')
self.assertTrue(field, 'Id field does not exist.')
# And we should have a field for the tab:
field = tree.xpath('//field[@name="%s"]' % fieldname)
self.assertTrue(
field,
'Tab field %s does not exist in %s.' %
(fieldname, etree.tostring(tree)))
# There should be no effect on the tree view:
view = partner_model.fields_view_get(cr, uid, view_type='tree')
tree = etree.fromstring(view['arch'])
field = tree.xpath('//field[@name="%s"]' % fieldname)
self.assertFalse(
field,
'Tab field %s should not exist in %s.' %
(fieldname, etree.tostring(tree)))
def test_relations(self):
"""Test relations shown on tab."""
cr, uid = self.cr, self.uid
type_model = self.registry('res.partner.relation.type')
relation_model = self.registry('res.partner.relation')
partner_model = self.registry('res.partner')
type_has_chairperson_id = type_model.create(
cr, uid, {
'name': 'has chairperson',
'name_inverse': 'is chairperson for',
'contact_type_left': 'c',
'own_tab_left': True,
'contact_type_right': 'p'})
type_has_chairperson = type_model.browse(
cr, uid, type_has_chairperson_id)
self.assertTrue(bool(type_has_chairperson))
big_company_id = partner_model.create(
cr, uid, {
'name': 'Big company',
'is_company': True,
'ref': 'BIG'})
big_company = partner_model.browse(cr, uid, big_company_id)
self.assertTrue(bool(big_company))
important_person_id = partner_model.create(
cr, uid, {
'name': 'Bart Simpson',
'is_company': False,
'ref': 'BS'})
important_person = partner_model.browse(cr, uid, important_person_id)
self.assertTrue(bool(important_person))
relation_company_chair_id = relation_model.create(
cr, uid, {
'left_partner_id': big_company.id,
'type_id': type_has_chairperson.id,
'right_partner_id': important_person.id})
relation_company_chair = relation_model.browse(
cr, uid, relation_company_chair_id)
self.assertTrue(bool(relation_company_chair))
# There should now be a field in res_partner for the new tab:
fieldname = self._get_tab_fieldname(type_has_chairperson, 'left')
self.assertTrue(fieldname in partner_model._columns)
# We should find the chairperson of the company through the tab:
executive_partners = big_company[fieldname]
self.assertEqual(len(executive_partners), 1)
self.assertEqual(
executive_partners[0].right_partner_id.id,
important_person.id)
def test_update_tabs(self):
"""Test the function that will create tabs during module loading."""
cr, uid = self.cr, self.uid
type_model = self.registry('res.partner.relation.type')
partner_model = self.registry('res.partner')
type_has_chairperson_id = type_model.create(
cr, uid, {
'name': 'has chairperson',
'name_inverse': 'is chairperson for',
'contact_type_left': 'c',
'own_tab_left': True,
'contact_type_right': 'p'})
type_has_chairperson = type_model.browse(
cr, uid, type_has_chairperson_id)
# Now call hook method
partner_model._register_hook(cr)
# There should now be a field in res_partner for the new tab:
fieldname = self._get_tab_fieldname(type_has_chairperson, 'left')
self.assertTrue(fieldname in partner_model._columns)
Loading…
Cancel
Save