=== modified file 'openerp/addons/base/res/res_partner.py'
--- openerp/addons/base/res/res_partner.py	2013-04-19 16:47:28 +0000
+++ openerp/addons/base/res/res_partner.py	2013-04-21 22:41:25 +0000
@@ -30,7 +30,12 @@
 from openerp import pooler, tools
 from openerp.osv import osv, fields
 from openerp.tools.translate import _
+<<<<<<< TREE
 from openerp.tools.yaml_import import is_comment
+=======
+from openerp.osv.orm import setup_modifiers
+from lxml import etree
+>>>>>>> MERGE-SOURCE
 
 class format_address(object):
     def fields_view_get_address(self, cr, uid, arch, context={}):
@@ -164,10 +169,136 @@
 ADDRESS_FIELDS = ('street', 'street2', 'zip', 'city', 'state_id', 'country_id')
 POSTAL_ADDRESS_FIELDS = ADDRESS_FIELDS # deprecated, to remove after 7.0
 
+
+class contact_mixin_methods(osv.AbstractModel):
+    _name = 'res.contact.mixin.methods'
+
+    def create(self, cr, uid, vals, context=None):
+        vals = self.write_contact(cr, uid, [], vals, context)
+        return super(contact_mixin_methods, self).create(cr, uid, vals, context)
+
+    def read(self, cr, user, ids, fields=None, context=None,
+                           load='_classic_read'):
+        res = super(contact_mixin_methods, self).read(cr, user, ids, fields, context,
+                                              load)
+        if fields and 'contact_id' in fields:
+            if isinstance(res, list):
+                for rec in res:
+                    if not rec.get('contact_id') and rec.get('partner_id'):
+                        rec['contact_id'] = rec['partner_id']
+            else:
+                if not res.get('contact_id') and res.get('partner_id'):
+                    res['contact_id'] = res['partner_id']
+        return res
+
+    def write(self, cr, uid, ids, vals, context=None):
+        vals = self.write_contact(cr, uid, ids, vals, context)
+        return super(contact_mixin_methods, self).write(cr, uid, ids, vals, context)
+
+    def write_contact(self, cr, uid, ids, vals, context=None):
+        partner_obj = self.pool.get('res.partner')
+        if isinstance(vals.get('partner_id'), (list, tuple)):
+            partner_id = vals.get('partner_id')[0]
+        else:
+            partner_id = vals.get('partner_id')
+        if partner_id:
+            if not vals.get('contact_id'):
+                vals['contact_id'] = partner_id
+            vals['partner_id'] = partner_obj.read(cr, uid, [partner_id],
+                                                  ['commercial_entity_id']
+                                                  )[0]['commercial_entity_id'][0]
+        return vals
+
+    def onchange_contact_mixin(self, cr, uid, ids, contact_id, partner_id,
+                               context=None):
+        res = {}
+        if contact_id:
+            commercial_entity_id = self.pool.get('res.partner').read(cr, uid,
+            [contact_id], ['commercial_entity_id'])[0]['commercial_entity_id']
+            res = {'value': {'partner_id': commercial_entity_id}}
+        return res
+
+    def fields_view_get(self, cr, uid, view_id=None, view_type='form',
+                                      context=None, toolbar=False,
+                                      submenu=False):
+        res = super(contact_mixin_methods, self).fields_view_get(cr, uid, view_id,
+                                                         view_type,
+                                                         context,
+                                                         toolbar,
+                                                         submenu)
+        if view_type in ('form', 'tree', 'search'):
+            eview = etree.fromstring(res['arch'])
+            partner_fields = eview.xpath("//field[@name='partner_id']")
+            partner_descr = self.fields_get(cr, uid, ['partner_id'], context)
+            contact_descr = self.fields_get(cr, uid, ['contact_id'], context)
+            for partner in partner_fields:
+                kw = {
+                    'name': 'contact_id',
+                    'options': "{'always_reload': True}",
+                    'context': partner.attrib.get('context', ''),
+                    'domain': partner.attrib.get('domain', ''),
+                    'modifiers': partner.attrib.get('modifiers', ''),
+                }
+                if view_type in ('form', 'tree'):
+                    kw['on_change'] = \
+                    "onchange_contact_mixin(contact_id, partner_id, context)"
+                    kw['groups'] = "base.group_contact_advanced"
+                    if not self.user_has_groups(cr, uid, "base.group_contact_advanced", context):
+                        partner.set('invisible', '1')
+                        setup_modifiers(partner, partner_descr, context, view_type == 'tree')
+                        kw['string'] = partner.attrib.get('string', partner_descr['partner_id']['string'])
+                contact = etree.Element('field', **kw)
+                p_index = partner.getparent().index(partner)
+                partner.getparent().insert(p_index, contact)
+                res['fields'].update(contact_descr)
+            if view_type == 'search':
+                group_by_fields = eview.xpath("//filter[@context=\"{'group_by':'partner_id'}\"]")
+                for group_by_field in group_by_fields:
+                    kw = {
+                        'string': 'Contact',
+                        'icon': "terp-partner",
+                        'context': "{'group_by':'contact_id'}",
+                        'domain': "[]",
+                    }
+                    g_contact = etree.Element('filter', **kw)
+                    g_index = group_by_field.getparent().index(group_by_field)
+                    group_by_field.getparent().insert(g_index, g_contact)
+            res['arch'] = etree.tostring(eview, encoding="utf-8")
+        return res
+
+class contact_mixin(osv.AbstractModel):
+    _name = 'res.contact.mixin'
+    _inherit = 'res.contact.mixin.methods'
+    _columns = {
+        'contact_id': fields.many2one('res.partner', 'Contact'),
+    }
+
 class res_partner(osv.osv, format_address):
     _description = 'Partner'
     _name = "res.partner"
 
+    def unlink(self, cr, uid, ids, context=None):
+        errors = {}
+        for model_name, model_obj in self.pool.models.iteritems():
+            if hasattr(model_obj, '_contact_mixin') and model_name != 'res.contact.mixin':
+                domain = [
+                    '|',
+                    ['contact_id', 'in', ids],
+                    ['partner_id', 'in', ids],
+                ]       
+                res_ids = model_obj.search(cr, uid, domain, context=context)
+                if res_ids:
+                    res = model_obj.browse(cr, uid, res_ids, context=context)
+                    errors[_(model_obj._description)] = res.pop(0)[model_obj._rec_name] or ""
+                    for obj in res:
+                        errors[_(model_obj._description)] += "; %s"%(obj[model_obj._rec_name] or "")
+        if errors:
+            raise osv.except_osv(_('User Error'),
+                _("Cannot delete contact %s because they are used in the following records:\n"
+                    "%s") % (ids, "\n".join(["%s : %s"%(key, errors[key]) for key in errors])))
+        return super(res_partner, self).unlink(cr, uid, ids, context=context)
+
+
     def _address_display(self, cr, uid, ids, name, args, context=None):
         res = {}
         for partner in self.browse(cr, uid, ids, context=context):
@@ -195,6 +326,7 @@
             result[obj.id] = obj.image != False
         return result
 
+<<<<<<< TREE
     def _commercial_partner_compute(self, cr, uid, ids, name, args, context=None):
         """ Returns the partner that is considered the commercial
         entity of this partner. The commercial entity holds the master data
@@ -210,8 +342,28 @@
     # indirection to avoid passing a copy of the overridable method when declaring the function field
     _commercial_partner_id = lambda self, *args, **kwargs: self._commercial_partner_compute(*args, **kwargs)
 
+=======
+    def _commercial_entity_id(self, cr, uid, ids, field_names, arg,
+                              context=None):
+        res = {}
+        for partner in self.browse(cr, uid, ids, context=context):
+            if partner.is_company:
+                res[partner.id] = partner.id
+            elif partner.parent_id:
+                commercial_entity_id = self.read(cr, uid,
+                                                 [partner.parent_id.id],
+                                                 ['commercial_entity_id']
+                                                 )[0]['commercial_entity_id']
+                res[partner.id] = commercial_entity_id
+            else:
+                res[partner.id] = partner.id
+        return res
+
+>>>>>>> MERGE-SOURCE
     _order = "name"
     _columns = {
+        'commercial_entity_id': fields.function(_commercial_entity_id,
+        string='Commercial Entity', type='many2one', relation='res.partner'),
         'name': fields.char('Name', size=128, required=True, select=True),
         'date': fields.date('Date', select=1),
         'title': fields.many2one('res.partner.title', 'Title'),

=== modified file 'openerp/addons/base/res/res_security.xml'
--- openerp/addons/base/res/res_security.xml	2012-09-26 07:03:39 +0000
+++ openerp/addons/base/res/res_security.xml	2013-04-21 22:41:25 +0000
@@ -30,5 +30,10 @@
             <field name="domain_force">[('company_ids','child_of',[user.company_id.id])]</field>
         </record>
 
+       <record id="group_contact_advanced" model="res.groups">
+            <field name="name">Advanced Contacts Management</field> 
+            <field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
+       </record>
+
     </data>
 </openerp>

