diff --git a/partner_contact_in_several_companies/README.rst b/partner_contact_in_several_companies/README.rst index 18d9a8d8c..dc32131a8 100644 --- a/partner_contact_in_several_companies/README.rst +++ b/partner_contact_in_several_companies/README.rst @@ -32,7 +32,7 @@ For further information, please visit: .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/134/9.0 + :target: https://runbot.odoo-community.org/runbot/134/10.0 Known issues / Roadmap ====================== @@ -42,22 +42,24 @@ Known issues / Roadmap Bug Tracker =========== -Bugs are tracked on `GitHub 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 `here `_. - +Bugs are tracked on `GitHub 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 `_. + + Contributors ------------ -* Xavier ALT (original author) +* Xavier ALT (original author) * El Hadji Dem * TheCloneMaster * Sandy Carter @@ -65,6 +67,7 @@ Contributors * Sebastien Alix * Jairo Llopis * Richard deMeester +* Nicolas JEUDY Maintainer ---------- diff --git a/partner_contact_in_several_companies/__manifest__.py b/partner_contact_in_several_companies/__manifest__.py index 9560f814f..6ac293a5e 100644 --- a/partner_contact_in_several_companies/__manifest__.py +++ b/partner_contact_in_several_companies/__manifest__.py @@ -4,23 +4,13 @@ { "name": "Contacts in several partners", "summary": "Allow to have one contact in several partners", - "version": "9.0.1.0.0", + "version": "10.0.1.0.0", "category": "Customer Relationship Management", "website": "https://odoo-community.org/", - "author": "Odoo Community Association (OCA)", - "contributors": [ - 'Xavier ALT ', - 'El Hadji Dem ', - 'TheCloneMaster ', - 'Sandy Carter ', - 'Rudolf Schnapka ', - 'Sebastien Alix ', - 'Jairo Llopis ', - 'Richard deMeester ', - ], + "author": "Odoo Community Association (OCA),Odoo SA", "license": "AGPL-3", 'application': False, - 'installable': False, + 'installable': True, 'auto_install': False, "depends": [ "base", diff --git a/partner_contact_in_several_companies/demo/ir_actions.xml b/partner_contact_in_several_companies/demo/ir_actions.xml index 6f69124c8..15c48cfcb 100644 --- a/partner_contact_in_several_companies/demo/ir_actions.xml +++ b/partner_contact_in_several_companies/demo/ir_actions.xml @@ -1,7 +1,5 @@ - - All Customers in All Positions ir.actions.act_window @@ -11,6 +9,4 @@ {"search_default_customer":1, 'search_show_all_positions': {'is_set': True, 'set_value': True}} - - - \ No newline at end of file + diff --git a/partner_contact_in_several_companies/demo/res_partner.xml b/partner_contact_in_several_companies/demo/res_partner.xml index 4834c791e..d09e0499d 100644 --- a/partner_contact_in_several_companies/demo/res_partner.xml +++ b/partner_contact_in_several_companies/demo/res_partner.xml @@ -1,7 +1,5 @@ - - Roger Scott Consultant @@ -21,6 +19,4 @@ - - - \ No newline at end of file + diff --git a/partner_contact_in_several_companies/models/ir_actions.py b/partner_contact_in_several_companies/models/ir_actions.py index 756163aa1..0608ca9e7 100644 --- a/partner_contact_in_several_companies/models/ir_actions.py +++ b/partner_contact_in_several_companies/models/ir_actions.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from openerp import models, api +from odoo import api, models class IRActionsWindow(models.Model): diff --git a/partner_contact_in_several_companies/models/res_partner.py b/partner_contact_in_several_companies/models/res_partner.py index 580463498..8770cf0e8 100644 --- a/partner_contact_in_several_companies/models/res_partner.py +++ b/partner_contact_in_several_companies/models/res_partner.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from openerp import fields, models, _, api -from openerp.osv import expression +from odoo import api, fields, models, _ +from odoo.osv import expression class ResPartner(models.Model): @@ -12,8 +12,9 @@ class ResPartner(models.Model): [('standalone', _('Standalone Contact')), ('attached', _('Attached to existing Contact')), ], - compute='_get_contact_type', - required=True, select=1, store=True, + compute='_compute_contact_type', + store=True, + index=True, default='standalone') contact_id = fields.Many2one('res.partner', string='Main Contact', domain=[('is_company', '=', False), @@ -23,10 +24,11 @@ class ResPartner(models.Model): other_contact_ids = fields.One2many('res.partner', 'contact_id', string='Others Positions') - @api.one + @api.multi @api.depends('contact_id') - def _get_contact_type(self): - self.contact_type = self.contact_id and 'attached' or 'standalone' + def _compute_contact_type(self): + for rec in self: + rec.contact_type = 'attached' if rec.contact_id else 'standalone' def _basecontact_check_context(self, mode): """ Remove 'search_show_all_positions' for non-search mode. @@ -115,8 +117,7 @@ class ResPartner(models.Model): self.ensure_one() if self.contact_id: contact_fields = self._contact_fields() - sync_vals = self._update_fields_values(self.contact_id, - contact_fields) + sync_vals = self.contact_id._update_fields_values(contact_fields) self.write(sync_vals) def update_contact(self, vals): @@ -129,26 +130,26 @@ class ResPartner(models.Model): if contact_vals: self.with_context(__update_contact_lock=True).write(contact_vals) - @api.model - def _fields_sync(self, partner, update_values): + @api.multi + def _fields_sync(self, update_values): """Sync commercial fields and address fields from company and to children, contact fields from contact and to attached contact after create/update, just as if those were all modeled as fields.related to the parent """ - super(ResPartner, self)._fields_sync(partner, update_values) + self.ensure_one() + super(ResPartner, self)._fields_sync(update_values) contact_fields = self._contact_fields() # 1. From UPSTREAM: sync from parent contact if update_values.get('contact_id'): - partner._contact_sync_from_parent() + self._contact_sync_from_parent() # 2. To DOWNSTREAM: sync contact fields to parent or related elif any(field in contact_fields for field in update_values): - update_ids = [ - c.id for c in partner.other_contact_ids if not c.is_company - ] - if partner.contact_id: - update_ids.append(partner.contact_id.id) - self.browse(update_ids).update_contact(update_values) + update_ids = self.other_contact_ids.filtered( + lambda p: not p.is_company) + if self.contact_id: + update_ids |= self.contact_id + update_ids.update_contact(update_values) @api.onchange('contact_id') def _onchange_contact_id(self): diff --git a/partner_contact_in_several_companies/tests/test_partner_contact_in_several_companies.py b/partner_contact_in_several_companies/tests/test_partner_contact_in_several_companies.py index 2e6714a43..d1220870b 100644 --- a/partner_contact_in_several_companies/tests/test_partner_contact_in_several_companies.py +++ b/partner_contact_in_several_companies/tests/test_partner_contact_in_several_companies.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -from openerp.tests import common +from odoo.tests import common class PartnerContactInSeveralCompaniesCase(common.TransactionCase): @@ -9,254 +8,212 @@ class PartnerContactInSeveralCompaniesCase(common.TransactionCase): def setUp(self): """*****setUp*****""" super(PartnerContactInSeveralCompaniesCase, self).setUp() - cr, uid = self.cr, self.uid - ModelData = self.registry('ir.model.data') - self.partner = self.registry('res.partner') - self.action = self.registry('ir.actions.act_window') - + self.partner = self.env['res.partner'] + self.action = self.env['ir.actions.act_window'] + current_module = 'partner_contact_in_several_companies' # Get test records reference - for attr, module, name in [ - ('main_partner_id', 'base', 'main_partner'), - ('bob_contact_id', - 'partner_contact_in_several_companies', - 'res_partner_contact1'), - ('bob_job1_id', - 'partner_contact_in_several_companies', - 'res_partner_contact1_work_position1'), - ('roger_contact_id', 'base', 'res_partner_main2'), - ('roger_job2_id', - 'partner_contact_in_several_companies', - 'res_partner_main2_position_consultant'), - ('base_partner_action_id', 'base', 'action_partner_form'), - ('custom_partner_action_id', - 'partner_contact_in_several_companies', - 'action_partner_form'), - ]: - r = ModelData.get_object_reference(cr, uid, module, name) - setattr(self, attr, r[1] if r else False) + self.main_partner = self.env.ref('base.main_partner') + self.bob_contact = self.env.ref( + '%s.res_partner_contact1' % current_module) + self.bob_job1 = self.env.ref( + '%s.res_partner_contact1_work_position1' % current_module) + self.roger_contact = self.env.ref('base.res_partner_main2') + self.roger_job2 = self.env.ref( + '%s.res_partner_main2_position_consultant' % current_module) def test_00_show_only_standalone_contact(self): """Check that only standalone contact are shown if context explicitly state to not display all positions """ - cr, uid = self.cr, self.uid ctx = {'search_show_all_positions': {'is_set': True, 'set_value': False }} - partner_ids = self.partner.search(cr, uid, [], context=ctx) - partner_ids.sort() - self.assertTrue(self.bob_job1_id not in partner_ids) - self.assertTrue(self.roger_job2_id not in partner_ids) + partner_ids = self.partner.with_context(ctx).search([]) + self.assertTrue(self.bob_job1 not in partner_ids) + self.assertTrue(self.roger_job2 not in partner_ids) def test_01_show_all_positions(self): """Check that all contact are show if context is empty or explicitly state to display all positions or the "is_set" value has been set to False. """ - cr, uid = self.cr, self.uid - partner_ids = self.partner.search(cr, uid, [], context=None) - self.assertTrue(self.bob_job1_id in partner_ids) - self.assertTrue(self.roger_job2_id in partner_ids) + partner_ids = self.partner.search([]) + self.assertTrue(self.bob_job1 in partner_ids) + self.assertTrue(self.roger_job2 in partner_ids) ctx = {'search_show_all_positions': {'is_set': False}} - partner_ids = self.partner.search(cr, uid, [], context=ctx) - self.assertTrue(self.bob_job1_id in partner_ids) - self.assertTrue(self.roger_job2_id in partner_ids) + partner_ids = self.partner.with_context(ctx).search([]) + self.assertTrue(self.bob_job1 in partner_ids) + self.assertTrue(self.roger_job2 in partner_ids) ctx = {'search_show_all_positions': {'is_set': True, 'set_value': True }} - partner_ids = self.partner.search(cr, uid, [], context=ctx) - self.assertTrue(self.bob_job1_id in partner_ids) - self.assertTrue(self.roger_job2_id in partner_ids) + partner_ids = self.partner.with_context(ctx).search([]) + self.assertTrue(self.bob_job1 in partner_ids) + self.assertTrue(self.roger_job2 in partner_ids) def test_02_reading_other_contact_one2many_show_all_positions(self): """Check that readonly partner's ``other_contact_ids`` return all values whatever the context """ - cr, uid = self.cr, self.uid - - def read_other_contacts(pid, context=None): - return self.partner.read( - cr, uid, [pid], ['other_contact_ids'], - context=context)[0]['other_contact_ids'] - - def read_contacts(pid, context=None): - return self.partner.read( - cr, uid, [pid], ['child_ids'], context=context)[0]['child_ids'] - ctx = None + ctx = {} self.assertEqual( - read_other_contacts(self.bob_contact_id, context=ctx), - [self.bob_job1_id], + self.bob_job1, self.bob_contact.with_context(ctx).other_contact_ids ) ctx = {'search_show_all_positions': {'is_set': False}} - self.assertEqual(read_other_contacts( - self.bob_contact_id, context=ctx), - [self.bob_job1_id], + self.assertEqual( + self.bob_job1, self.bob_contact.with_context(ctx).other_contact_ids ) ctx = {'search_show_all_positions': {'is_set': True, - 'set_value': False + 'set_value': False, }} - self.assertEqual(read_other_contacts( - self.bob_contact_id, context=ctx), - [self.bob_job1_id], + self.assertEqual( + self.bob_job1, self.bob_contact.with_context(ctx).other_contact_ids ) ctx = {'search_show_all_positions': {'is_set': True, - 'set_value': True + 'set_value': True, }} self.assertEqual( - read_other_contacts(self.bob_contact_id, context=ctx), - [self.bob_job1_id], + self.bob_job1, self.bob_contact.with_context(ctx).other_contact_ids ) - ctx = None + ctx = {} self.assertIn( - self.bob_job1_id, - read_contacts(self.main_partner_id, context=ctx), - ) + self.bob_job1, + self.main_partner.with_context(ctx).child_ids) + ctx = {'search_show_all_positions': {'is_set': False}} self.assertIn( - self.bob_job1_id, - read_contacts(self.main_partner_id, context=ctx), - ) + self.bob_job1, + self.main_partner.with_context(ctx).child_ids) + ctx = {'search_show_all_positions': {'is_set': True, - 'set_value': False + 'set_value': False, }} self.assertIn( - self.bob_job1_id, - read_contacts(self.main_partner_id, context=ctx), - ) + self.bob_job1, + self.main_partner.with_context(ctx).child_ids) + ctx = {'search_show_all_positions': {'is_set': True, - 'set_value': True + 'set_value': True, }} self.assertIn( - self.bob_job1_id, - read_contacts(self.main_partner_id, context=ctx), - ) + self.bob_job1, + self.main_partner.with_context(ctx).child_ids) def test_03_search_match_attached_contacts(self): """Check that searching partner also return partners having attached contacts matching search criteria """ - cr, uid = self.cr, self.uid # Bob's contact has one other position which is related to # 'YourCompany' # so search for all contacts working for 'YourCompany' # should contain Bob position. partner_ids = self.partner.search( - cr, uid, - [('parent_id', 'ilike', 'YourCompany')], - context=None - ) - self.assertIn(self.bob_job1_id, partner_ids, ) + [('parent_id', 'ilike', 'YourCompany')]) + self.assertTrue(self.bob_job1 in partner_ids) # but when searching without 'all positions', # we should get the position standalone contact instead. ctx = {'search_show_all_positions': {'is_set': True, - 'set_value': False + 'set_value': False, }} - partner_ids = self.partner.search( - cr, uid, - [('parent_id', 'ilike', 'YourCompany')], - context=ctx - ) - self.assertIn(self.bob_contact_id, partner_ids, ) + partner_ids = self.partner.with_context(ctx).search( + [('parent_id', 'ilike', 'YourCompany')]) + self.assertTrue(self.bob_contact in partner_ids) def test_04_contact_creation(self): """Check that we're begin to create a contact""" - cr, uid = self.cr, self.uid # Create a contact using only name - new_contact_id = self.partner.create(cr, uid, {'name': 'Bob Egnops'}) + new_contact = self.partner.create({'name': 'Bob Egnops'}) self.assertEqual( - self.partner.browse(cr, uid, new_contact_id).contact_type, + new_contact.contact_type, 'standalone', ) # Create a contact with only contact_id - new_contact_id = self.partner.create( - cr, uid, {'contact_id': self.bob_contact_id} + new_contact = self.partner.create( + {'contact_id': self.bob_contact.id} ) - new_contact = self.partner.browse(cr, uid, new_contact_id) - self.assertEqual(new_contact.name, 'Bob Egnops') - self.assertEqual(new_contact.contact_type, 'attached') + self.assertEqual(new_contact.name, u'Bob Egnops') + self.assertEqual(new_contact.contact_type, u'attached') # Create a contact with both contact_id and name; # contact's name should override provided value in that case - new_contact_id = self.partner.create( - cr, uid, {'contact_id': self.bob_contact_id, 'name': 'Rob Egnops'} + new_contact = self.partner.create( + {'contact_id': self.bob_contact.id, 'name': 'Rob Egnops'} ) self.assertEqual( - self.partner.browse(cr, uid, new_contact_id).name, - 'Bob Egnops' + new_contact.name, + u'Bob Egnops' ) # Reset contact to standalone - self.partner.write(cr, uid, [new_contact_id], {'contact_id': False}) + new_contact.write({'contact_id': False}) self.assertEqual( - self.partner.browse(cr, uid, new_contact_id).contact_type, - 'standalone', + new_contact.contact_type, + u'standalone', ) # Reset contact to attached, and ensure only it is unlinked (i.e. # context is ignored). - self.partner.write(cr, uid, [new_contact_id], - {'contact_id': self.bob_contact_id}) + new_contact.write({'contact_id': self.bob_contact.id}) ctx = {'search_show_all_positions': {'is_set': True, 'set_value': True }} - self.partner.unlink(cr, uid, [new_contact_id], context=ctx) - partner_ids = self.partner.search( - cr, uid, [('id', 'in', [new_contact_id, self.bob_contact_id])]) - self.assertIn(self.bob_contact_id, partner_ids) - self.assertNotIn(new_contact_id, partner_ids) + new_contact.with_context(ctx).unlink() + partner_ids = self.partner.with_context(ctx).search( + [('id', 'in', [new_contact.id, self.bob_contact.id])]) + self.assertIn(self.bob_contact, partner_ids) + self.assertNotIn(new_contact, partner_ids) def test_05_contact_fields_sync(self): """Check that contact's fields are correctly synced between parent contact or related contacts """ - cr, uid = self.cr, self.uid # Test DOWNSTREAM sync - self.partner.write( - cr, uid, [self.bob_contact_id], {'name': 'Rob Egnops'} + self.bob_contact.write( + {'name': 'Rob Egnops'} ) self.assertEqual( - self.partner.browse(cr, uid, self.bob_job1_id).name, + self.bob_job1.name, 'Rob Egnops', ) # Test UPSTREAM sync - self.partner.write(cr, uid, [self.bob_job1_id], {'name': 'Bob Egnops'}) + self.bob_job1.write({'name': 'Bob Egnops'}) self.assertEqual( - self.partner.browse(cr, uid, self.bob_contact_id).name, + self.bob_contact.name, 'Bob Egnops', ) def test_06_ir_action(self): """Check ir_action context is auto updated. """ - cr, uid = self.cr, self.uid new_context_val = "'search_show_all_positions': " \ - "{'is_set': True, 'set_value': False}," + "{'is_set': True, 'set_value': False}" + details = self.env['ir.actions.act_window'].for_xml_id( + 'base', + 'action_partner_form') - details = self.action.read( - cr, uid, [self.base_partner_action_id] - ) self.assertIn( new_context_val, - details[0]['context'], + details['context'], msg='Default actions not updated with new context' ) - details = self.action.read( - cr, uid, [self.custom_partner_action_id] - ) + details = self.env['ir.actions.act_window'].for_xml_id( + 'partner_contact_in_several_companies', + 'action_partner_form') + self.assertNotIn( new_context_val, - details[0]['context'], + details['context'], msg='Custom actions incorrectly updated with new context' ) diff --git a/partner_contact_in_several_companies/views/res_partner.xml b/partner_contact_in_several_companies/views/res_partner.xml index a56bb51b3..8485ec045 100644 --- a/partner_contact_in_several_companies/views/res_partner.xml +++ b/partner_contact_in_several_companies/views/res_partner.xml @@ -1,7 +1,5 @@ - - res.partner.select.contact res.partner @@ -75,7 +73,7 @@
-
+
at
Phone:
Mobile:
@@ -97,7 +95,7 @@ - +
@@ -123,12 +121,6 @@ - - - - res.partner - {'invisible': [('contact_id','!=',False)]} @@ -161,7 +152,6 @@ options="{'always_reload': True}"/>

-
@@ -187,6 +177,4 @@ - -
-
\ No newline at end of file +