You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

161 lines
6.7 KiB

  1. # -*- coding: utf-8 -*-
  2. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  3. from openerp import fields, models, _, api
  4. from openerp.osv import expression
  5. class ResPartner(models.Model):
  6. _inherit = 'res.partner'
  7. contact_type = fields.Selection(
  8. [('standalone', _('Standalone Contact')),
  9. ('attached', _('Attached to existing Contact')),
  10. ],
  11. compute='_get_contact_type',
  12. required=True, select=1, store=True,
  13. default='standalone')
  14. contact_id = fields.Many2one('res.partner', string='Main Contact',
  15. domain=[('is_company', '=', False),
  16. ('contact_type', '=', 'standalone'),
  17. ],
  18. )
  19. other_contact_ids = fields.One2many('res.partner', 'contact_id',
  20. string='Others Positions')
  21. @api.one
  22. @api.depends('contact_id')
  23. def _get_contact_type(self):
  24. self.contact_type = self.contact_id and 'attached' or 'standalone'
  25. def _basecontact_check_context(self, mode):
  26. """ Remove 'search_show_all_positions' for non-search mode.
  27. Keeping it in context can result in unexpected behaviour (ex: reading
  28. one2many might return wrong result - i.e with "attached contact"
  29. removed even if it's directly linked to a company).
  30. Actually, is easier to override a dictionary value to indicate it
  31. should be ignored...
  32. """
  33. if (mode != 'search' and
  34. 'search_show_all_positions' in self.env.context):
  35. result = self.with_context(
  36. search_show_all_positions={'is_set': False})
  37. else:
  38. result = self
  39. return result
  40. @api.model
  41. def search(self, args, offset=0, limit=None, order=None, count=False):
  42. """ Display only standalone contact matching ``args`` or having
  43. attached contact matching ``args`` """
  44. ctx = self.env.context
  45. if (ctx.get('search_show_all_positions', {}).get('is_set') and
  46. not ctx['search_show_all_positions']['set_value']):
  47. args = expression.normalize_domain(args)
  48. attached_contact_args = expression.AND(
  49. (args, [('contact_type', '=', 'attached')])
  50. )
  51. attached_contacts = super(ResPartner, self).search(
  52. attached_contact_args)
  53. args = expression.OR((
  54. expression.AND(([('contact_type', '=', 'standalone')], args)),
  55. [('other_contact_ids', 'in', attached_contacts.ids)],
  56. ))
  57. return super(ResPartner, self).search(args, offset=offset,
  58. limit=limit, order=order,
  59. count=count)
  60. @api.model
  61. def create(self, vals):
  62. """ When creating, use a modified self to alter the context (see
  63. comment in _basecontact_check_context). Also, we need to ensure
  64. that the name on an attached contact is the same as the name on the
  65. contact it is attached to."""
  66. modified_self = self._basecontact_check_context('create')
  67. if not vals.get('name') and vals.get('contact_id'):
  68. vals['name'] = modified_self.browse(vals['contact_id']).name
  69. return super(ResPartner, modified_self).create(vals)
  70. @api.multi
  71. def read(self, fields=None, load='_classic_read'):
  72. modified_self = self._basecontact_check_context('read')
  73. return super(ResPartner, modified_self).read(fields=fields, load=load)
  74. @api.multi
  75. def write(self, vals):
  76. modified_self = self._basecontact_check_context('write')
  77. return super(ResPartner, modified_self).write(vals)
  78. @api.multi
  79. def unlink(self):
  80. modified_self = self._basecontact_check_context('unlink')
  81. return super(ResPartner, modified_self).unlink()
  82. @api.multi
  83. def _commercial_partner_compute(self, name, args):
  84. """ Returns the partner that is considered the commercial
  85. entity of this partner. The commercial entity holds the master data
  86. for all commercial fields (see :py:meth:`~_commercial_fields`) """
  87. result = super(ResPartner, self)._commercial_partner_compute(name,
  88. args)
  89. for partner in self:
  90. if partner.contact_type == 'attached' and not partner.parent_id:
  91. result[partner.id] = partner.contact_id.id
  92. return result
  93. def _contact_fields(self):
  94. """ Returns the list of contact fields that are synced from the parent
  95. when a partner is attached to him. """
  96. return ['name', 'title']
  97. def _contact_sync_from_parent(self):
  98. """ Handle sync of contact fields when a new parent contact entity
  99. is set, as if they were related fields
  100. """
  101. self.ensure_one()
  102. if self.contact_id:
  103. contact_fields = self._contact_fields()
  104. sync_vals = self._update_fields_values(self.contact_id,
  105. contact_fields)
  106. self.write(sync_vals)
  107. def update_contact(self, vals):
  108. if self.env.context.get('__update_contact_lock'):
  109. return
  110. contact_fields = self._contact_fields()
  111. contact_vals = dict(
  112. (field, vals[field]) for field in contact_fields if field in vals
  113. )
  114. if contact_vals:
  115. self.with_context(__update_contact_lock=True).write(contact_vals)
  116. @api.model
  117. def _fields_sync(self, partner, update_values):
  118. """Sync commercial fields and address fields from company and to
  119. children, contact fields from contact and to attached contact
  120. after create/update, just as if those were all modeled as
  121. fields.related to the parent
  122. """
  123. super(ResPartner, self)._fields_sync(partner, update_values)
  124. contact_fields = self._contact_fields()
  125. # 1. From UPSTREAM: sync from parent contact
  126. if update_values.get('contact_id'):
  127. partner._contact_sync_from_parent()
  128. # 2. To DOWNSTREAM: sync contact fields to parent or related
  129. elif any(field in contact_fields for field in update_values):
  130. update_ids = [
  131. c.id for c in partner.other_contact_ids if not c.is_company
  132. ]
  133. if partner.contact_id:
  134. update_ids.append(partner.contact_id.id)
  135. self.browse(update_ids).update_contact(update_values)
  136. @api.onchange('contact_id')
  137. def _onchange_contact_id(self):
  138. if self.contact_id:
  139. self.name = self.contact_id.name
  140. @api.onchange('contact_type')
  141. def _onchange_contact_type(self):
  142. if self.contact_type == 'standalone':
  143. self.contact_id = False