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.
 
 

157 lines
6.3 KiB

# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import api, fields, models
from odoo.osv import expression
class ResPartner(models.Model):
_inherit = "res.partner"
contact_type = fields.Selection(
[
("standalone", "Standalone Contact"),
("attached", "Attached to existing Contact"),
],
compute="_compute_contact_type",
store=True,
index=True,
default="standalone",
)
contact_id = fields.Many2one(
"res.partner",
string="Main Contact",
domain=[("is_company", "=", False), ("contact_type", "=", "standalone")],
)
other_contact_ids = fields.One2many(
"res.partner",
"contact_id",
string="Others Positions",
)
@api.depends("contact_id")
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.
Keeping it in context can result in unexpected behaviour (ex: reading
one2many might return wrong result - i.e with "attached contact"
removed even if it"s directly linked to a company).
Actually, is easier to override a dictionary value to indicate it
should be ignored...
"""
if mode != "search" and "search_show_all_positions" in self.env.context:
result = self.with_context(search_show_all_positions={"is_set": False})
else:
result = self
return result
@api.model
def search(self, args, offset=0, limit=None, order=None, count=False):
"""Display only standalone contact matching ``args`` or having
attached contact matching ``args``"""
ctx = self.env.context
if (
ctx.get("search_show_all_positions", {}).get("is_set")
and not ctx["search_show_all_positions"]["set_value"]
):
args = expression.normalize_domain(args)
attached_contact_args = expression.AND(
(args, [("contact_type", "=", "attached")])
)
attached_contacts = super(ResPartner, self).search(attached_contact_args)
args = expression.OR(
(
expression.AND(([("contact_type", "=", "standalone")], args)),
[("other_contact_ids", "in", attached_contacts.ids)],
)
)
return super(ResPartner, self).search(
args, offset=offset, limit=limit, order=order, count=count
)
@api.model
def create(self, vals):
"""When creating, use a modified self to alter the context (see
comment in _basecontact_check_context). Also, we need to ensure
that the name on an attached contact is the same as the name on the
contact it is attached to."""
modified_self = self._basecontact_check_context("create")
if not vals.get("name") and vals.get("contact_id"):
vals["name"] = modified_self.browse(vals["contact_id"]).name
return super(ResPartner, modified_self).create(vals)
def read(self, fields=None, load="_classic_read"):
modified_self = self._basecontact_check_context("read")
return super(ResPartner, modified_self).read(fields=fields, load=load)
def write(self, vals):
modified_self = self._basecontact_check_context("write")
return super(ResPartner, modified_self).write(vals)
def unlink(self):
modified_self = self._basecontact_check_context("unlink")
return super(ResPartner, modified_self).unlink()
def _compute_commercial_partner(self):
"""Returns the partner that is considered the commercial
entity of this partner. The commercial entity holds the master data
for all commercial fields (see :py:meth:`~_commercial_fields`)"""
result = super(ResPartner, self)._compute_commercial_partner()
for partner in self:
if partner.contact_type == "attached" and not partner.parent_id:
partner.commercial_partner_id = partner.contact_id
return result
def _contact_fields(self):
"""Returns the list of contact fields that are synced from the parent
when a partner is attached to him."""
return ["name", "title"]
def _contact_sync_from_parent(self):
"""Handle sync of contact fields when a new parent contact entity
is set, as if they were related fields
"""
self.ensure_one()
if self.contact_id:
contact_fields = self._contact_fields()
sync_vals = self.contact_id._update_fields_values(contact_fields)
self.write(sync_vals)
def update_contact(self, vals):
if self.env.context.get("__update_contact_lock"):
return
contact_fields = self._contact_fields()
contact_vals = {field: vals[field] for field in contact_fields if field in vals}
if contact_vals:
self.with_context(__update_contact_lock=True).write(contact_vals)
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
"""
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"):
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 = 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):
if self.contact_id:
self.name = self.contact_id.name
@api.onchange("contact_type")
def _onchange_contact_type(self):
if self.contact_type == "standalone":
self.contact_id = False