[IMP] crm: add comments in code about crm lead to opportunity

Try to understand a bit this code with comments. Some unnecessary code is
removed, and some parameters are added to try to understand the various flows,
but main purpose is to understand that mighty spaghetti, not destroy it.

LINKS

Side effect of Task ID 2056759 (remove crm.partner.binding mixin)
Side effect of Task ID 2088565 (crm onchange -> compute)
Community PR #43127
Enterprise PR odoo/enterpreise#7656

Related: odoo/enterprise#7656
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
Thibault Delavallée
2020-01-10 16:06:44 +00:00
parent 5f6e94165b
commit f82eadf9d0
5 changed files with 53 additions and 54 deletions
+48 -43
View File
@@ -664,15 +664,8 @@ class Lead(models.Model):
def _merge_get_result_type(self):
""" Define the type of the result of the merge. If at least one of the
element to merge is an opp, the resulting new element will be an opp.
Otherwise it will be a lead.
We'll directly use a list of browse records instead of a list of ids
for performances' sake: it will spare a second browse of the
leads/opps.
:param list opps: list of browse records containing the leads/opps to process
:return string type: the type of the final element
"""
element to merge is an opp, the resulting new element will be an opp.
Otherwise it will be a lead. """
if any(record.type == 'opportunity' for record in self):
return 'opportunity'
return 'lead'
@@ -719,19 +712,20 @@ class Lead(models.Model):
data['type'] = self._merge_get_result_type()
return data
def _mail_body(self, fields):
""" generate the message body with the changed values
:param fields : list of fields to track
:returns a list of message bodies for the corresponding leads
def _merge_notify_get_merged_fields_message(self, fields):
""" Generate the message body with the changed values
:param fields : list of fields to track
:returns a list of message bodies for the corresponding leads
"""
bodies = []
for lead in self:
title = "%s : %s\n" % (_('Merged opportunity') if lead.type == 'opportunity' else _('Merged lead'), lead.name)
body = [title]
_fields = self.env['ir.model.fields'].search([
('name', 'in', fields or []),
('model_id.model', '=', lead._name),
])
('name', 'in', fields or []),
('model_id.model', '=', lead._name),
])
for field in _fields:
value = getattr(lead, field.name, False)
if field.ttype == 'selection':
@@ -751,11 +745,11 @@ class Lead(models.Model):
return bodies
def _merge_notify(self, opportunities):
""" Create a message gathering merged leads/opps informations. Using message_post, send a
message explaining which fields has been merged and their new value. `self` is the
resulting merge crm.lead record.
:param opportunities : recordset of merged crm.lead
:returns mail.message posted on resulting crm.lead
""" Post a message gathering merged leads/opps informations. It explains
which fields has been merged and their new value. `self` is the resulting
merge crm.lead record.
:param opportunities: see ``merge_dependences``
"""
# TODO JEM: mail template should be used instead of fix body, subject text
self.ensure_one()
@@ -764,14 +758,15 @@ class Lead(models.Model):
merge_message = _('Merged leads') if result_type == 'lead' else _('Merged opportunities')
subject = merge_message + ": " + ", ".join(opportunities.mapped('name'))
# message bodies
message_bodies = opportunities._mail_body(list(CRM_LEAD_FIELDS_TO_MERGE))
message_bodies = opportunities._merge_notify_get_merged_fields_message(list(CRM_LEAD_FIELDS_TO_MERGE))
message_body = "\n\n".join(message_bodies)
return self.message_post(body=message_body, subject=subject)
def _merge_opportunity_history(self, opportunities):
""" Move mail.message from the given opportunities to the current one. `self` is the
crm.lead record destination for message of `opportunities`.
:param opportunities : recordset of crm.lead to move the messages
:param opportunities: see ``merge_dependences``
"""
self.ensure_one()
for opportunity in opportunities:
@@ -785,7 +780,8 @@ class Lead(models.Model):
def _merge_opportunity_attachments(self, opportunities):
""" Move attachments of given opportunities to the current one `self`, and rename
the attachments having same name than native ones.
:param opportunities : recordset of merged crm.lead
:param opportunities: see ``merge_dependences``
"""
self.ensure_one()
@@ -810,8 +806,9 @@ class Lead(models.Model):
def merge_dependences(self, opportunities):
""" Merge dependences (messages, attachments, ...). These dependences will be
transfered to `self`, the most important lead.
:param opportunities : recordset of opportunities to transfert. Does
not include `self`.
:param opportunities : recordset of opportunities to transfer. Does not
include `self` which is the target crm.lead being the result of the merge.
"""
self.ensure_one()
self._merge_notify(opportunities)
@@ -940,16 +937,18 @@ class Lead(models.Model):
return True
def _create_lead_partner_data(self, name, is_company, parent_id=False):
""" extract data from lead to create a partner
:param name : furtur name of the partner
:param is_company : True if the partner is a company
:param parent_id : id of the parent partner (False if no parent)
:returns res.partner record
def _create_lead_partner_data(self, partner_name, is_company=False, parent_id=False):
""" Extract data from lead to create a partner.
:param name : furtur name of the partner
:param is_company : True if the partner is a company
:param parent_id : id of the parent partner (False if no parent)
:return: dictionary of values to give at res_partner.create()
"""
email_split = tools.email_split(self.email_from)
return {
'name': name,
'name': partner_name,
'user_id': self.env.context.get('default_user_id') or self.user_id.id,
'comment': self.description,
'team_id': self.team_id.id,
@@ -980,28 +979,30 @@ class Lead(models.Model):
contact_name = Partner._parse_partner_name(self.email_from)[0] if self.email_from else False
if self.partner_name:
partner_company = Partner.create(self._create_lead_partner_data(self.partner_name, True))
partner_company = Partner.create(self._create_lead_partner_data(self.partner_name, is_company=True))
elif self.partner_id:
partner_company = self.partner_id
else:
partner_company = None
if contact_name:
return Partner.create(self._create_lead_partner_data(contact_name, False, partner_company.id if partner_company else False))
return Partner.create(self._create_lead_partner_data(contact_name, is_company=False, parent_id=partner_company.id if partner_company else False))
if partner_company:
return partner_company
return Partner.create(self._create_lead_partner_data(self.name, False))
return Partner.create(self._create_lead_partner_data(self.name, is_company=False))
def handle_partner_assignation(self, action='create', partner_id=False):
def handle_partner_assignation(self, action='create', partner_id=False):
""" Handle partner assignation during a lead conversion.
if action is 'create', create new partner with contact and assign lead to new partner_id.
otherwise assign lead to the specified partner_id
:param list ids: leads/opportunities ids to process
:param string action: what has to be done regarding partners (create it, assign an existing one, or nothing)
:param int partner_id: partner to assign if any
:return dict: dictionary organized as followed: {lead_id: partner_assigned_id}
If action is 'create', create new partner with contact and assign lead to
new partner_id. Otherwise assign lead to the specified partner_id
TDE FIXME: docstring does not match code... code seems a bit random.
:param string action: what has to be done regarding partners (create it, assign an existing one, or nothing)
:param int partner_id: partner to assign if any
:return: dict(lead_id: partner_id)
"""
partner_ids = {}
for lead in self:
@@ -1042,6 +1043,10 @@ class Lead(models.Model):
lead.write(value)
return True
# ------------------------------------------------------------
# TOOLS
# ------------------------------------------------------------
def redirect_lead_opportunity_view(self):
self.ensure_one()
return {
+1 -1
View File
@@ -78,7 +78,7 @@ class TestCRMLead(TestCrmCommon):
subtype_xmlid='mail.mt_comment')
self.assertEqual(message.author_id, self.user_sales_manager.partner_id)
new_partner_id = new_lead.handle_partner_assignation()[new_lead.id]
new_partner_id = new_lead.handle_partner_assignation(action='create')[new_lead.id]
new_partner = self.env['res.partner'].with_user(self.user_sales_manager).browse(new_partner_id)
self.assertEqual(new_partner.email, 'unknown.sender@test.example.com')
self.assertEqual(new_partner.team_id, self.sales_team_1)
+1 -7
View File
@@ -7,7 +7,6 @@ from odoo.tools.translate import _
class Lead2OpportunityPartner(models.TransientModel):
_name = 'crm.lead2opportunity.partner'
_description = 'Convert Lead to Opportunity (not in mass)'
_inherit = 'crm.partner.binding'
@@ -26,7 +25,7 @@ class Lead2OpportunityPartner(models.TransientModel):
lead = self.env['crm.lead'].browse(self._context['active_id'])
email = lead.partner_id.email if lead.partner_id else lead.email_from
tomerge.update(self._get_duplicated_leads(partner_id, email, include_lost=True).ids)
tomerge.update(self.env['crm.lead']._get_duplicated_leads_by_emails(partner_id, email, include_lost=True).ids)
if 'action' in fields and not result.get('action'):
result['action'] = 'exist' if partner_id else 'create'
@@ -73,11 +72,6 @@ class Lead2OpportunityPartner(models.TransientModel):
values = self.env['crm.lead']._onchange_user_values(self.user_id.id if self.user_id else False)
self.team_id = values.get('team_id', False)
@api.model
def _get_duplicated_leads(self, partner_id, email, include_lost=False):
""" Search for opportunities that have the same partner and that arent done or cancelled """
return self.env['crm.lead']._get_duplicated_leads_by_emails(partner_id, email, include_lost=include_lost)
# NOTE JEM : is it the good place to test this ?
@api.model
def view_init(self, fields):
@@ -43,7 +43,7 @@ class Lead2OpportunityMassConvert(models.TransientModel):
partner_ids = [(lead.partner_id.id, lead.partner_id and lead.partner_id.email or lead.email_from) for lead in active_leads]
partners_duplicated_leads = {}
for partner_id, email in partner_ids:
duplicated_leads = self._get_duplicated_leads(partner_id, email)
duplicated_leads = self.env['crm.lead']._get_duplicated_leads_by_emails(partner_id, email, include_lost=False)
if len(duplicated_leads) > 1:
partners_duplicated_leads.setdefault((partner_id, email), []).extend(duplicated_leads)
@@ -77,7 +77,7 @@ class Lead2OpportunityMassConvert(models.TransientModel):
for lead_id in lead_selected:
if lead_id not in merged_lead_ids:
lead = self.env['crm.lead'].browse(lead_id)
duplicated_leads = self._get_duplicated_leads(lead.partner_id.id, lead.partner_id.email if lead.partner_id else lead.email_from)
duplicated_leads = self.env['crm.lead']._get_duplicated_leads_by_emails(lead.partner_id.id, lead.partner_id.email if lead.partner_id else lead.email_from, include_lost=False)
if len(duplicated_leads) > 1:
lead = duplicated_leads.merge_opportunity()
merged_lead_ids.update(duplicated_leads.ids)
+1 -1
View File
@@ -50,7 +50,7 @@ class WebsiteVisitor(models.Model):
lead = self.lead_ids._sort_by_confidence_level(reverse=True)[0]
partner_id = self.partner_id.id
if not self.partner_id:
partner_id = lead.handle_partner_assignation()[lead.id]
partner_id = lead.handle_partner_assignation(action='create')[lead.id]
if not lead.partner_id:
lead.partner_id = partner_id
self.partner_id = partner_id