Browse Source

Add module

pull/485/head
Giovanni Francesco Capalbo 7 years ago
parent
commit
81a6a728ed
  1. 61
      partner_multi_relation_parent/README.rst
  2. 5
      partner_multi_relation_parent/__init__.py
  3. 17
      partner_multi_relation_parent/__manifest__.py
  4. 13
      partner_multi_relation_parent/data/data.xml
  5. 14
      partner_multi_relation_parent/hooks.py
  6. 5
      partner_multi_relation_parent/models/__init__.py
  7. 60
      partner_multi_relation_parent/models/res_partner.py
  8. 116
      partner_multi_relation_parent/models/res_partner_relation.py
  9. BIN
      partner_multi_relation_parent/static/description/icon.png
  10. 4
      partner_multi_relation_parent/tests/__init__.py
  11. 108
      partner_multi_relation_parent/tests/test_partner_multi_relation_parent.py

61
partner_multi_relation_parent/README.rst

@ -0,0 +1,61 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
=============================================
Parent contact Hierarchy Mapping in relations
=============================================
This module maps automatically the relations between parent partners and their
children as relations. It has an init hook that will create such relations for
existing partners and their children partner. If a child partner changes it's
parent the relation mapping will update automatically. It will automatically
create the relations "has contact" and "is contact of" for partners and their
contacts. This will allow to search using this key, and therefore have an
updated search option for partners and their contacts.
Known issues / Roadmap
======================
* hide/forbade the delition of the "Is contact of" and "Has Contact" installed
relation types
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/partner_multi_relation/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smashing it by providing a detailed and welcomed feedback.
Credits
=======
Images
------
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
Contributors
------------
* Giovanni Francesco Capalbo <giovanni@therp.nl>
Do not contact contributors directly about help with questions or problems concerning this addon, but use the `community mailing list <mailto:community@mail.odoo.com>`_ or the `appropriate specialized mailinglist <https://odoo-community.org/groups>`_ for help, and the bug tracker linked in `Bug Tracker`_ above for technical issues.
Maintainer
----------
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
To contribute to this module, please visit https://odoo-community.org.

5
partner_multi_relation_parent/__init__.py

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# © 2017 Therp BV <http://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import models
from .hooks import post_init_hook

17
partner_multi_relation_parent/__manifest__.py

@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
# © 2017 Therp BV <http://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
"name": "Partner Contact Hierarchy Mapping in relations",
"version": "10.0.1.0.0",
"author": "Therp BV,Odoo Community Association (OCA)",
"license": "AGPL-3",
"category": "CRM",
"summary": "Syncs the hierarchy partner's contacts with CRM relations",
"depends": ['partner_multi_relation'],
"data": [
'data/data.xml',
],
"post_init_hook": "post_init_hook",
"installable": True,
}

13
partner_multi_relation_parent/data/data.xml

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>
<record id="parent_relation_type"
model="res.partner.relation.type">
<field name="name">Is contact of</field>
<field name="name_inverse">Has contact</field>
<field name="contact_type_left">p</field>
<field name="contact_type_right">c</field>
<field name="handle_invalid_onchange">restrict</field>
</record>
</data>
</odoo>

14
partner_multi_relation_parent/hooks.py

@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
# © 2017 Therp BV <http://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import api, SUPERUSER_ID
def post_init_hook(cr, registry):
env = api.Environment(cr, SUPERUSER_ID, {})
partner_model = env['res.partner']
# get all fields with a parent
partners = partner_model.search(['!', ('parent_id', 'in', [False])])
import pudb
pudb.set_trace()
partners.update_relations()

5
partner_multi_relation_parent/models/__init__.py

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# © 2017 Therp BV <http://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import res_partner
from . import res_partner_relation

60
partner_multi_relation_parent/models/res_partner.py

@ -0,0 +1,60 @@
# -*- coding: utf-8 -*-
# © 2017 Therp BV <http://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import api, models
class ResPartner(models.Model):
_inherit = 'res.partner'
# called without parameters from the init hook
def find_current_relation(self, left, type_relation, right):
par_rel_mod = self.env['res.partner.relation']
return par_rel_mod.search([
('left_partner_id', '=', left),
('type_id', '=', type_relation),
('right_partner_id', '=', right)
])
def update_relations(self, old_parent_id=None, parent_id=None):
par_rel_mod = self.env['res.partner.relation']
type_relation = self.env.ref(
'partner_multi_relation_parent.parent_relation_type'
).id
for this in self:
if not parent_id:
parent_id = this.parent_id.id
if not old_parent_id:
old_parent_id = this.parent_id.id
# unlink previous relation
if old_parent_id:
previous = self.find_current_relation(
this.id, type_relation, old_parent_id
)
previous.unlink()
# create new relations
par_rel_mod.create(
{'left_partner_id' : this.id,
'type_id': type_relation,
'right_partner_id': parent_id,
}
)
@api.model
def create(self, vals):
res = super(ResPartner, self).create(vals=vals)
if "parent_id" in vals:
res.update_relations(None, vals['parent_id'])
return res
@api.multi
def write(self, vals):
if self.env.context.get('relation_create'):
for this in self:
if "parent_id" in vals and vals(['parent_id']):
this.update_relations(self.parent_id.id, vals['parent_id'])
res = super(ResPartner, self).write(vals=vals)
return res

116
partner_multi_relation_parent/models/res_partner_relation.py

@ -0,0 +1,116 @@
# -*- coding: utf-8 -*-
# © 2017 Therp BV <http://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import _, api, models
from odoo.exceptions import UserError
class ResPartnerRelation(models.Model):
_inherit = 'res.partner.relation'
def relation_exists(self, left, type_rel, right):
relation = self.search([
('left_partner_id', '=', left),
('type_id', '=', type_rel),
('right_partner_id', '=', right)
])
return relation
@api.multi
def write(self, vals):
part_mod = self.env['res.partner']
type_relation = self.env.ref(
'partner_multi_relation_parent.parent_relation_type'
).id
for this in self:
if this.type_id != type_relation:
continue
# check that whatever relation will come out of this write does
# not exist already , but check only if type_id doesn't change, if
# it does we don't care about uniqueness
if vals.get('type_id', this.type_id) == type_relation:
relation = this.relation_exists(
vals.get('left_partner_id', this.left_partner_id),
type_relation,
vals.get('right_partner_id', this.right_partner_id)
)
if relation:
raise UserError(_(
"The relation you are creating exists and has id %s"
"there can only be one relation of type %s" % (
str(relation.id), relation.type_id.name
)))
if 'type_id' in vals and vals['type_id'] != type_relation:
this.left_partner_id.with_context(
relation_create=False
).write({'parent_id' : False})
elif 'right_partner_id' in vals and 'left_partner_id' not in vals:
new_parent = vals.get('right_partner_id')
contact_id = this.left_partner_id
contact_id.with_context(relation_create=False).write(
{'parent_id' : new_parent}
)
elif 'left_partner_id' in vals and 'right_partner_id' not in vals:
old_contact_id = part_mod.browse(this.left_partner_id)
old_contact_id.with_context(relation_create=False).write(
{'parent_id': False}
)
contact_id = part_mod.browse(vals['left_partner_id'])
contact_id.with_context(relation_create=False).write(
{'parent_id': this.right_partner_id}
)
elif 'left_partner_id' in vals and 'right_partner_id' in vals:
old_contact_id = this.left_partner_id
old_contact_id.with_context(relation_create=False).write(
{'parent_id': False}
)
contact_id = part_mod.browse(vals['left_partner_id'])
contact_id.with_context(relation_create=False).write(
{'parent_id': vals['right_partner_id']},
)
res = super(ResPartnerRelation, self).write(vals=vals)
return res
@api.multi
def unlink(self):
type_relation = self.env.ref(
'partner_multi_relation_parent.parent_relation_type'
).id
for this in self:
if this.type_id.id != type_relation:
continue
this.left_partner_id.with_context(relation_create=False).write(
{'parent_id': False}
)
res = super(ResPartnerRelation, self).unlink()
return res
@api.model
def create(self, vals):
type_relation = self.env.ref(
'partner_multi_relation_parent.parent_relation_type'
).id
current = self.relation_exists(
vals['left_partner_id'],
type_relation,
vals['right_partner_id']
)
if current:
# we are creating a relation but one already exists, raise an
# exception to warn the user relations of type_relation must be
# unique.
raise UserError(_(
"The relation you are creating exists and has id %s"
"there can only be one relation of type %s" % (
str(current.id), current.type_id.name
)))
# there is no relation, so we can create it, but we must update
# the parent_id of the left contact of this new relation
res = super(ResPartnerRelation, self).create(vals=vals)
res.left_partner_id.with_context(relation_create=False).write(
{'parent_id': vals['right_partner_id']}
)
return res

BIN
partner_multi_relation_parent/static/description/icon.png

After

Width: 90  |  Height: 90  |  Size: 13 KiB

4
partner_multi_relation_parent/tests/__init__.py

@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# © 2017 Therp BV <http://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import test_partner_multi_relation_parent

108
partner_multi_relation_parent/tests/test_partner_multi_relation_parent.py

@ -0,0 +1,108 @@
# -*- coding: utf-8 -*-
# © 2017 Therp BV <http://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp.addons.partner_multi_relation.tests.test_partner_relation_common \
import TestPartnerRelationCommon
class TestPartnerMultiRelationParent(TestPartnerRelationCommon):
# pylint: disable=invalid-name
def setUp(self):
super(TestPartnerMultiRelationParent, self).setUp()
self.par_rel_mod = self.env['res.partner.relation']
self.type_relation = self.env.ref(
'partner_multi_relation_parent.parent_relation_type'
).id
# By default it will be false, this makes it run after the modules are
# installed
post_install = True
def count_partner_relation_left_right(self, partner_id, parent_id=None):
# returns number relations , should be always 1
# todo extend this with relations_all calculations (left, right)
if not parent_id:
return 0
hits = self.par_rel_mod.search([
('left_partner_id', '=', partner_id),
('type_id', '=', self.type_relation),
('right_partner_id', '=', parent_id)
])
return len(hits)
def count_relations_of_parent(self, parent_id):
hits = self.par_rel_mod.search([
('type_id', '=', self.type_relation),
('right_partner_id', '=', parent_id)
])
return len(hits)
def test_partner_multi_relation_parent(self):
#verify that existing partners do have relations
relations = self.count_partner_relation_left_right(
self.partner_01_person.id, self.partner_01_person.parent_id.id
)
self.assertEqual(relations, 0)
# create a contact for ngo , partner n03
ngo_contact = self.partner_model.create({
'name': '03 NGO ACCOUNTANT',
'is_company': False,
'ref' : 'PR03C01',
'parent_id': self.partner_03_ngo.id
})
relations = self.count_partner_relation_left_right(
ngo_contact.id, ngo_contact.parent_id.id
)
self.assertEqual(relations, 1)
# then modify partner and verify it
old_parent = ngo_contact.parent_id.id
ngo_contact.write({'parent_id': self.partner_02_company.id})
# check no more relations with old_parent
relations = self.count_partner_relation_left_right(
ngo_contact.id, old_parent
)
self.assertEqual(relations, 0)
# check relations are there with current parent
relations = self.count_partner_relation_left_right(
ngo_contact.id, ngo_contact.parent_id.id
)
self.assertEqual(relations, 1)
# delete NGO ACCOUNTANT
old_id = ngo_contact.id
old_parent_id = ngo_contact.parent_id.id
ngo_contact.unlink()
relations = self.count_partner_relation_left_right(
old_id, old_parent_id
)
self.assertEqual(relations, 0)
# test with multiple contacts
# 15 for ngo and 15 for company
for partners in range(30):
if partners % 2 == 0:
self.partner_model.create({
'name': '03 NGO %s' % str(partners),
'is_company': False,
'ref' : 'PR03C%s' % str(partners),
'parent_id': self.partner_03_ngo.id
})
continue
self.partner_model.create({
'name': '02 Company %s' % str(partners),
'is_company': False,
'ref' : 'PR02C%s' % str(partners),
'parent_id': self.partner_02_company.id
})
# try to delete the has contact type , forbade
contact_relations = self.count_relations_of_parent(
self.partner_02_company.id
)
self.assertEqual(contact_relations, 15)
#TODO Modify that changing the relation.all transient model will change
#the actual relation and it's partner
Loading…
Cancel
Save