Browse Source

[MIG] 10.0 partner_contact_in_several_companies (#347)

* [MIG] inital work to 10.0

* [FIX] restore computed field

- select=1 is not usable anymore ...

* [FIX] convert _fields_sync to new api

* [FIX] restore form . import statement

* [FIX] readme with last template.

* [FIX] import statement.

* [FIX] check all tests

* [FIX] Flake8 errors fixes

* [FIX] flake 8

* [FIX] wrong obj call self -> self.contact_id

* [FIX] typo

* [FIX] flake 8

* [FIX] add index on contact_type

* [fix] use in statement on test

* [FIX] update base action with new context

* [FIX] test fixes

* [FIX] test work

* [FIX] flake 8 over indent

* [FIX] fixes on lastest comments

- better readability
- remove <data></data> tags

* [FIX] model that same pattern for _fields_sync

- add test ensure_once()

* [FIX] remove last data xml tags

* [FIX] fix action test

* [FIX] typo in import statement

* [FIX] remove logger from tests

* [FIX] new flake8 compute method name for OCA

* [FIX] minor typo

* [FIX] partner_contact_in_several_companies: remove contributor key in manifest

* [FIX] partner_contact_in_several_companies: residual onchange on partner view

* Revert "[FIX] partner_contact_in_several_companies: remove contributor key in manifest"

This reverts commit 56e511a9a7.

* [FIX] partner_contact_personal_information_page: clean manifest and authors

* [FIX] partner_contact_in_several_companies: required = True not needed
14.0
Nicolas JEUDY 8 years ago
committed by Jacob Oldfield
parent
commit
3bb88fa669
  1. 23
      partner_contact_in_several_companies/README.rst
  2. 16
      partner_contact_in_several_companies/__manifest__.py
  3. 6
      partner_contact_in_several_companies/demo/ir_actions.xml
  4. 6
      partner_contact_in_several_companies/demo/res_partner.xml
  5. 2
      partner_contact_in_several_companies/models/ir_actions.py
  6. 39
      partner_contact_in_several_companies/models/res_partner.py
  7. 217
      partner_contact_in_several_companies/tests/test_partner_contact_in_several_companies.py
  8. 18
      partner_contact_in_several_companies/views/res_partner.xml

23
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 <https://github.com/OCA/
partner-contact/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 <https://github.com/OCA/
partner-contact/issues/new?body=module:%20
partner_contact_in_serveral_companies%0Aversion:%20
9.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/partner-contact/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
------------
* Xavier ALT <xal@openerp.com> (original author)
* Xavier ALT <xal@odoo.com> (original author)
* El Hadji Dem <elhadji.dem@savoirfairelinux.com>
* TheCloneMaster <the.clone.master@gmail.com>
* Sandy Carter <bwrsandman@gmail.com>
@ -65,6 +67,7 @@ Contributors
* Sebastien Alix <sebastien.alix@osiell.com>
* Jairo Llopis <j.llopis@grupoesoc.es>
* Richard deMeester <richard@willowit.com.au>
* Nicolas JEUDY <https://github.com/njeudy>
Maintainer
----------

16
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 <xal@openerp.com>',
'El Hadji Dem <elhadji.dem@savoirfairelinux.com>',
'TheCloneMaster <the.clone.master@gmail.com>',
'Sandy Carter <bwrsandman@gmail.com>',
'Rudolf Schnapka <rs@techno-flex.de>',
'Sebastien Alix <sebastien.alix@osiell.com>',
'Jairo Llopis <j.llopis@grupoesoc.es>',
'Richard deMeester <richard@willowit.com.au>',
],
"author": "Odoo Community Association (OCA),Odoo SA",
"license": "AGPL-3",
'application': False,
'installable': False,
'installable': True,
'auto_install': False,
"depends": [
"base",

6
partner_contact_in_several_companies/demo/ir_actions.xml

@ -1,7 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>
<record id="action_partner_form" model="ir.actions.act_window">
<field name="name">All Customers in All Positions</field>
<field name="type">ir.actions.act_window</field>
@ -11,6 +9,4 @@
<field name="context">{"search_default_customer":1, 'search_show_all_positions': {'is_set': True, 'set_value': True}}</field>
<field name="search_view_id" ref="base.view_res_partner_filter"/>
</record>
</data>
</odoo>
</odoo>

6
partner_contact_in_several_companies/demo/res_partner.xml

@ -1,7 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>
<record id="res_partner_main2_position_consultant" model="res.partner">
<field name="name">Roger Scott</field>
<field name="function">Consultant</field>
@ -21,6 +19,4 @@
<field name="parent_id" ref="base.main_partner"/>
<field name="contact_id" ref="res_partner_contact1"/>
</record>
</data>
</odoo>
</odoo>

2
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):

39
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):

217
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'
)

18
partner_contact_in_several_companies/views/res_partner.xml

@ -1,7 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="view_res_partner_filter_contact" model="ir.ui.view">
<field name="name">res.partner.select.contact</field>
<field name="model">res.partner</field>
@ -75,7 +73,7 @@
</div>
<div class="oe_kanban_details">
<field name="name"/>
<div t-if="record.function.raw_value"><field name="function"/></div>
<div t-if="record.function.raw_value"><field name="function"/> at <field name="parent_id"/></div>
<div t-if="record.email.raw_value"><field name="email"/></div>
<div t-if="record.phone.raw_value">Phone: <field name="phone"/></div>
<div t-if="record.mobile.raw_value">Mobile: <field name="mobile"/></div>
@ -97,7 +95,7 @@
<field name="street" placeholder="Street..." class="o_address_street"/>
<field name="street2" placeholder="Street 2..." class="o_address_street"/>
<field name="city" placeholder="City" class="o_address_city"/>
<field name="state_id" class="o_address_state" placeholder="State" options='{"no_open": True}' on_change="onchange_state(state_id)" context="{'country_id': country_id, 'zip': zip}"/>
<field name="state_id" class="o_address_state" placeholder="State" options='{"no_open": True}' context="{'country_id': country_id, 'zip': zip}"/>
<field name="zip" placeholder="ZIP" class="o_address_zip"/>
<field name="country_id" placeholder="Country" class="o_address_country" options='{"no_open": True, "no_create": True}'/>
</div>
@ -123,12 +121,6 @@
</field>
</page>
</page>
<xpath expr="//field[@name='category_id']" position="before">
<group>
<label for="contact_type" class="oe_edit_only"/>
<field name="contact_type" readonly="0" nolabel="1"/>
</group>
</xpath>
<xpath expr="//field[@name='child_ids']/form//field[@name='name']" position="before">
<field name='contact_type' readonly='0'/>
<field name="contact_id" string="Contact"
@ -145,7 +137,6 @@
<field name="model">res.partner</field>
<field name="inherit_id" ref="partner_contact_personal_information_page.personal_information"/>
<field name="arch" type="xml">
<data>
<xpath expr="//page[@name='personal_information_page']/group[@name='personal_information_group']"
position='attributes'>
<attribute name='attrs'>{'invisible': [('contact_id','!=',False)]}</attribute>
@ -161,7 +152,6 @@
options="{'always_reload': True}"/>
</p>
</xpath>
</data>
</field>
</record>
@ -187,6 +177,4 @@
</xpath>
</field>
</record>
</data>
</odoo>
</odoo>
Loading…
Cancel
Save