[REF] crm(_*): improve code of methods used in lead/opp convert/merge wizards
PURPOSE
As CRM is going to be updated (onchange -> compute, code improvements, better
management of sales teams) cleaning and improving code is a necessary first
step.
SPECIFICATIONS
Purpose of this commit is to rewrite code located and/or used in convert or
merge wizards. Indeed it holds unnecessary variables, dictionaries manipulation
that hides the real business flow, badly named variables, unclear methods, ...
Logic of those wizards seem quite simple but is hidden behind a wall of
bloated code.
Notably for wizards
* try to set explicit arguments in methods;
* less use of context values, replaced by arguments when possible;
* try to make better use of lead2opportunity wizard in its mass wizard that
inherit from it, notably by correctly calling super;
Notably for tools
* ``get_duplicated_leads`` was just a shortcut to ``_get_duplicated_leads_by
_emails``;
* ``_get_duplicated_leads_by_emails`` is renamed to ``_get_lead_duplicates``
because email is not the only way of finding duplicates;
* try to make better use of record sets instead of propagating ids, notably
for partner. For example input of ``_get_lead_duplicates`` or output of
``_find_matching_partner``;
* ``_create_lead_partner`` rename to ``_create_customer``, and its sub
method ``_create_lead_partner_data`` to ``_prepare_customer_values``;
* ``handle_partner_assignation`` was difficult as its use is not documented
and its docstring was not updated. Parameter have been renamed to be more
explicit about what it does. Method is renamed to ``handle_partner_
assignment`` as assignation has other meaning in English;
* ``allocate_salesman`` is renamed to ``handle_salesmen_assignment`` to match
the partner assignement and code is slightly cleaned;
No functional change should occur with this commit. Tests have been gradually
added to try to ensure behavior does not change.
LINKS
Task ID 2088565 (crm: from onchange to compute)
This commit is contained in:
+90
-116
@@ -870,34 +870,6 @@ class Lead(models.Model):
|
||||
|
||||
return self.sorted(key=opps_key, reverse=reverse)
|
||||
|
||||
def get_duplicated_leads(self, partner_id, include_lost=False):
|
||||
""" Search for opportunities that have the same partner and that arent done or cancelled
|
||||
:param partner_id : partner to search
|
||||
"""
|
||||
self.ensure_one()
|
||||
email = self.partner_id.email or self.email_from
|
||||
return self._get_duplicated_leads_by_emails(partner_id, email, include_lost=include_lost)
|
||||
|
||||
@api.model
|
||||
def _get_duplicated_leads_by_emails(self, partner_id, email, include_lost=False):
|
||||
""" Search for opportunities that have the same partner and that arent done or cancelled """
|
||||
if not email:
|
||||
return self.env['crm.lead']
|
||||
partner_match_domain = []
|
||||
for email in set(email_split(email) + [email]):
|
||||
partner_match_domain.append(('email_from', '=ilike', email))
|
||||
if partner_id:
|
||||
partner_match_domain.append(('partner_id', '=', partner_id))
|
||||
partner_match_domain = ['|'] * (len(partner_match_domain) - 1) + partner_match_domain
|
||||
if not partner_match_domain:
|
||||
return self.env['crm.lead']
|
||||
domain = partner_match_domain
|
||||
if not include_lost:
|
||||
domain += ['&', ('active', '=', True), ('probability', '<', 100)]
|
||||
else:
|
||||
domain += ['|', '&', ('type', '=', 'lead'), ('active', '=', True), ('type', '=', 'opportunity')]
|
||||
return self.search(domain)
|
||||
|
||||
def _convert_opportunity_data(self, customer, team_id=False):
|
||||
""" Extract the data from a lead to create the opportunity
|
||||
:param customer : res.partner record
|
||||
@@ -931,11 +903,61 @@ class Lead(models.Model):
|
||||
lead.write(vals)
|
||||
|
||||
if user_ids or team_id:
|
||||
self.allocate_salesman(user_ids, team_id)
|
||||
self.handle_salesmen_assignment(user_ids, team_id)
|
||||
|
||||
return True
|
||||
|
||||
def _create_lead_partner_data(self, partner_name, is_company=False, parent_id=False):
|
||||
def _get_lead_duplicates(self, partner=None, email=None, include_lost=False):
|
||||
""" Search for leads that seem duplicated based on partner / email.
|
||||
|
||||
:param partner : optional customer when searching duplicated
|
||||
:param email: email (possibly formatted) to search
|
||||
:param boolean include_lost: if True, search for archived leads or
|
||||
active opportunities. If False, search for active and not won leads
|
||||
and opportunities;
|
||||
"""
|
||||
if not email:
|
||||
return self.env['crm.lead']
|
||||
partner_match_domain = []
|
||||
for email in set(email_split(email) + [email]):
|
||||
partner_match_domain.append(('email_from', '=ilike', email))
|
||||
if partner:
|
||||
partner_match_domain.append(('partner_id', '=', partner.id))
|
||||
partner_match_domain = ['|'] * (len(partner_match_domain) - 1) + partner_match_domain
|
||||
if not partner_match_domain:
|
||||
return self.env['crm.lead']
|
||||
domain = partner_match_domain
|
||||
if not include_lost:
|
||||
domain += ['&', ('active', '=', True), ('probability', '<', 100)]
|
||||
else:
|
||||
domain += ['|', '&', ('type', '=', 'lead'), ('active', '=', True), ('type', '=', 'opportunity')]
|
||||
return self.search(domain)
|
||||
|
||||
def _create_customer(self):
|
||||
""" Create a partner from lead data and link it to the lead.
|
||||
|
||||
:return: newly-created partner browse record
|
||||
"""
|
||||
Partner = self.env['res.partner']
|
||||
contact_name = self.contact_name
|
||||
if not contact_name:
|
||||
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._prepare_customer_values(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._prepare_customer_values(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._prepare_customer_values(self.name, is_company=False))
|
||||
|
||||
def _prepare_customer_values(self, partner_name, is_company=False, parent_id=False):
|
||||
""" Extract data from lead to create a partner.
|
||||
|
||||
:param name : furtur name of the partner
|
||||
@@ -967,107 +989,59 @@ class Lead(models.Model):
|
||||
'type': 'contact'
|
||||
}
|
||||
|
||||
def _create_lead_partner(self):
|
||||
""" Create a partner from lead data
|
||||
:returns res.partner record
|
||||
"""
|
||||
Partner = self.env['res.partner']
|
||||
contact_name = self.contact_name
|
||||
if not contact_name:
|
||||
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, 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, 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, is_company=False))
|
||||
|
||||
def _find_matching_partner(self):
|
||||
""" Try to find a matching partner with available information on the Lead.
|
||||
Like the customer's name, email, phone number, etc.
|
||||
:return int partner_id if any, False otherwise
|
||||
""" Try to find a matching partner with available information on the
|
||||
lead, using notably customer's name, email, phone, ...
|
||||
|
||||
:return: partner browse record
|
||||
"""
|
||||
self.ensure_one()
|
||||
partner = self.partner_id
|
||||
|
||||
# find the best matching partner
|
||||
Partner = self.env['res.partner']
|
||||
if self.partner_id: # a partner is set already
|
||||
return self.partner_id.id
|
||||
if not partner and self.email_from:
|
||||
partner = self.env['res.partner'].search([('email', '=', self.email_from)], limit=1)
|
||||
|
||||
if self.email_from: # search through the existing partners based on the lead's email
|
||||
return Partner.search([('email', '=', self.email_from)], limit=1).id
|
||||
if not partner:
|
||||
# search through the existing partners based on the lead's partner or contact name
|
||||
# to be aligned with _create_customer, search on lead's name as last possibility
|
||||
for customer_potential_name in [self[field_name] for field_name in ['partner_name', 'contact_name', 'name'] if self[field_name]]:
|
||||
partner = self.env['res.partner'].search([('name', 'ilike', '%' + customer_potential_name + '%')], limit=1)
|
||||
if partner:
|
||||
break
|
||||
|
||||
search_criteria = False
|
||||
if self.partner_name: # search through the existing partners based on the lead's partner or contact name
|
||||
search_criteria = self.partner_name
|
||||
elif self.contact_name:
|
||||
search_criteria = self.contact_name
|
||||
elif self.name: # to be aligned with _create_lead_partner, search on lead's name as last possibility
|
||||
search_criteria = self.name
|
||||
return partner
|
||||
|
||||
if search_criteria:
|
||||
return Partner.search([('name', 'ilike', '%' + search_criteria + '%')], limit=1).id
|
||||
def handle_partner_assignment(self, force_partner_id=False, create_missing=True):
|
||||
""" Update customer (partner_id) of leads. Purpose is to set the same
|
||||
partner on most leads; either through a newly created partner either
|
||||
through a given partner_id.
|
||||
|
||||
return 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
|
||||
|
||||
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)
|
||||
:param int force_partner_id: if set, update all leads to that customer;
|
||||
:param create_missing: for leads without customer, create a new one
|
||||
based on lead information;
|
||||
"""
|
||||
partner_ids = {}
|
||||
for lead in self:
|
||||
if partner_id:
|
||||
lead.partner_id = partner_id
|
||||
if lead.partner_id:
|
||||
partner_ids[lead.id] = lead.partner_id.id
|
||||
continue
|
||||
if action == 'create':
|
||||
partner = lead._create_lead_partner()
|
||||
partner_id = partner.id
|
||||
partner.team_id = lead.team_id
|
||||
partner_ids[lead.id] = partner_id
|
||||
return partner_ids
|
||||
if force_partner_id:
|
||||
lead.partner_id = force_partner_id
|
||||
if not lead.partner_id and create_missing:
|
||||
partner = lead._create_customer()
|
||||
lead.partner_id = partner.id
|
||||
|
||||
def allocate_salesman(self, user_ids=None, team_id=False):
|
||||
def handle_salesmen_assignment(self, user_ids=None, team_id=False):
|
||||
""" Assign salesmen and salesteam to a batch of leads. If there are more
|
||||
leads than salesmen, these salesmen will be assigned in round-robin.
|
||||
E.g.: 4 salesmen (S1, S2, S3, S4) for 6 leads (L1, L2, ... L6). They
|
||||
will be assigned as followed: L1 - S1, L2 - S2, L3 - S3, L4 - S4,
|
||||
L5 - S1, L6 - S2.
|
||||
leads than salesmen, these salesmen will be assigned in round-robin. E.g.
|
||||
4 salesmen (S1, S2, S3, S4) for 6 leads (L1, L2, ... L6) will assigned as
|
||||
following: L1 - S1, L2 - S2, L3 - S3, L4 - S4, L5 - S1, L6 - S2.
|
||||
|
||||
:param list ids: leads/opportunities ids to process
|
||||
:param list user_ids: salesmen to assign
|
||||
:param int team_id: salesteam to assign
|
||||
:return bool
|
||||
:param list user_ids: salesmen to assign
|
||||
:param int team_id: salesteam to assign
|
||||
"""
|
||||
index = 0
|
||||
for lead in self:
|
||||
value = {}
|
||||
if team_id:
|
||||
value['team_id'] = team_id
|
||||
update_vals = {'team_id': team_id} if team_id else {}
|
||||
for index, lead in enumerate(self):
|
||||
if user_ids:
|
||||
value['user_id'] = user_ids[index]
|
||||
# Cycle through user_ids
|
||||
index = (index + 1) % len(user_ids)
|
||||
if value:
|
||||
lead.write(value)
|
||||
return True
|
||||
update_vals['user_id'] = user_ids[index % len(user_ids)]
|
||||
if update_vals:
|
||||
lead.write(update_vals)
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# TOOLS
|
||||
|
||||
@@ -78,7 +78,6 @@ 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(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)
|
||||
new_lead.handle_partner_assignment(create_missing=True)
|
||||
self.assertEqual(new_lead.partner_id.email, 'unknown.sender@test.example.com')
|
||||
self.assertEqual(new_lead.partner_id.team_id, self.sales_team_1)
|
||||
|
||||
@@ -8,6 +8,10 @@ from odoo.tests.common import tagged, users
|
||||
|
||||
@tagged('lead_manage')
|
||||
class TestLeadConvert(crm_common.TestLeadConvertCommon):
|
||||
"""
|
||||
TODO: created partner (handle assignation) has team of lead
|
||||
TODO: create partner has user_id coming from wizard
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
@@ -244,7 +248,7 @@ class TestLeadConvert(crm_common.TestLeadConvertCommon):
|
||||
|
||||
@users('user_sales_manager')
|
||||
def test_lead_merge_duplicates(self):
|
||||
""" Test Lead._get_duplicated_leads_by_emails() """
|
||||
""" Test Lead._get_lead_duplicates() """
|
||||
|
||||
# Check: partner / email fallbacks
|
||||
self._create_duplicates(self.lead_1)
|
||||
@@ -280,7 +284,7 @@ class TestLeadConvert(crm_common.TestLeadConvertCommon):
|
||||
|
||||
@users('user_sales_manager')
|
||||
def test_lead_merge_duplicates_flow(self):
|
||||
""" Test Lead._get_duplicated_leads_by_emails() + merge with active_test """
|
||||
""" Test Lead._get_lead_duplicates() + merge with active_test """
|
||||
|
||||
# Check: email formatting
|
||||
self.lead_1.write({
|
||||
@@ -431,7 +435,7 @@ class TestLeadConvertMass(crm_common.TestLeadConvertMassCommon):
|
||||
self.assertEqual(mass_convert.user_id, self.user_sales_salesman)
|
||||
self.assertEqual(mass_convert.team_id, self.sales_team_convert)
|
||||
|
||||
mass_convert.mass_convert()
|
||||
mass_convert.action_mass_convert()
|
||||
for lead in self.lead_1 | self.lead_w_partner:
|
||||
self.assertEqual(lead.type, 'opportunity')
|
||||
if lead == self.lead_w_partner:
|
||||
@@ -447,7 +451,7 @@ class TestLeadConvertMass(crm_common.TestLeadConvertMassCommon):
|
||||
mass_convert.write({
|
||||
'user_ids': self.user_sales_salesman.ids,
|
||||
})
|
||||
mass_convert.mass_convert()
|
||||
mass_convert.action_mass_convert()
|
||||
self.assertEqual(self.lead_w_partner.user_id, self.user_sales_salesman)
|
||||
self.assertEqual(self.lead_1.user_id, self.user_sales_leads) # existing value not forced
|
||||
|
||||
@@ -481,7 +485,7 @@ class TestLeadConvertMass(crm_common.TestLeadConvertMassCommon):
|
||||
self.assertEqual(mass_convert.name, 'convert')
|
||||
self.assertEqual(mass_convert.opportunity_ids, self.lead_1 | self.lead_w_partner)
|
||||
|
||||
mass_convert.mass_convert()
|
||||
mass_convert.action_mass_convert()
|
||||
|
||||
self.assertEqual(
|
||||
(lead_1_dups | lead_w_partner_dups).exists(),
|
||||
@@ -508,7 +512,7 @@ class TestLeadConvertMass(crm_common.TestLeadConvertMassCommon):
|
||||
})
|
||||
|
||||
# TDE FIXME: what happens if we mix people from different sales team ? currently nothing, to check
|
||||
mass_convert.mass_convert()
|
||||
mass_convert.action_mass_convert()
|
||||
|
||||
for idx, lead in enumerate(self.leads - self.lead_w_email_lost):
|
||||
self.assertEqual(lead.type, 'opportunity')
|
||||
|
||||
@@ -23,15 +23,15 @@ class Lead2OpportunityPartner(models.TransientModel):
|
||||
lead = self.env['crm.lead'].browse(self._context['active_id'])
|
||||
result['lead_id'] = lead.id
|
||||
|
||||
partner_id = lead._find_matching_partner()
|
||||
partner = lead._find_matching_partner()
|
||||
email = lead.partner_id.email if lead.partner_id else lead.email_from
|
||||
|
||||
tomerge.update(self.env['crm.lead']._get_duplicated_leads_by_emails(partner_id, email, include_lost=True).ids)
|
||||
tomerge.update(self.env['crm.lead']._get_lead_duplicates(partner, email, include_lost=True).ids)
|
||||
|
||||
if 'action' in fields and not result.get('action'):
|
||||
result['action'] = 'exist' if partner_id else 'create'
|
||||
result['action'] = 'exist' if partner else 'create'
|
||||
if 'partner_id' in fields:
|
||||
result['partner_id'] = partner_id
|
||||
result['partner_id'] = partner.id
|
||||
if 'name' in fields:
|
||||
result['name'] = 'merge' if len(tomerge) >= 2 else 'convert'
|
||||
if 'opportunity_ids' in fields and len(tomerge) >= 2:
|
||||
@@ -40,7 +40,7 @@ class Lead2OpportunityPartner(models.TransientModel):
|
||||
result['user_id'] = lead.user_id.id
|
||||
if lead.team_id:
|
||||
result['team_id'] = lead.team_id.id
|
||||
if not partner_id and not lead.contact_name:
|
||||
if not partner and not lead.contact_name:
|
||||
result['action'] = 'nothing'
|
||||
|
||||
return result
|
||||
@@ -63,7 +63,7 @@ class Lead2OpportunityPartner(models.TransientModel):
|
||||
@api.onchange('action')
|
||||
def onchange_action(self):
|
||||
if self.action == 'exist':
|
||||
self.partner_id = self.lead_id._find_matching_partner()
|
||||
self.partner_id = self.lead_id._find_matching_partner().id
|
||||
else:
|
||||
self.partner_id = False
|
||||
|
||||
@@ -81,79 +81,63 @@ 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)
|
||||
|
||||
# NOTE JEM : is it the good place to test this ?
|
||||
@api.model
|
||||
def view_init(self, fields):
|
||||
# JEM TDE FIXME: clean that brol
|
||||
""" Check some preconditions before the wizard executes. """
|
||||
for lead in self.env['crm.lead'].browse(self._context.get('active_ids', [])):
|
||||
if lead.probability == 100:
|
||||
raise UserError(_("Closed/Dead leads cannot be converted into opportunities."))
|
||||
return False
|
||||
|
||||
def _convert_opportunity(self, vals):
|
||||
def action_apply(self):
|
||||
if self.name == 'merge':
|
||||
result_opportunity = self._action_merge()
|
||||
else:
|
||||
result_opportunity = self._action_convert()
|
||||
|
||||
return result_opportunity.redirect_lead_opportunity_view()
|
||||
|
||||
def _action_merge(self):
|
||||
result_opportunity = self.with_context(active_test=False).opportunity_ids.merge_opportunity()
|
||||
if not result_opportunity.active:
|
||||
result_opportunity.write({'active': True, 'activity_type_id': False, 'lost_reason': False})
|
||||
|
||||
if result_opportunity.type == "lead":
|
||||
self._convert_and_allocate(result_opportunity, [self.user_id.id], team_id=self.team_id.id)
|
||||
elif not self._context.get('no_force_assignation') or not result_opportunity.user_id:
|
||||
result_opportunity.write({
|
||||
'user_id': self.user_id.id,
|
||||
'team_id': self.team_id.id,
|
||||
})
|
||||
return result_opportunity
|
||||
|
||||
def _action_convert(self):
|
||||
""" """
|
||||
result_opportunities = self.env['crm.lead'].browse(self._context.get('active_ids', []))
|
||||
self._convert_and_allocate(result_opportunities, [self.user_id.id], team_id=self.team_id.id)
|
||||
return result_opportunities[0]
|
||||
|
||||
def _convert_and_allocate(self, leads, user_ids, team_id=False):
|
||||
self.ensure_one()
|
||||
|
||||
res = False
|
||||
|
||||
leads = self.env['crm.lead'].browse(vals.get('lead_ids'))
|
||||
for lead in leads:
|
||||
self_def_user = self.with_context(default_user_id=self.user_id.id)
|
||||
if lead.active and self.action != 'nothing':
|
||||
self._convert_handle_partner(
|
||||
lead, self.action, self.partner_id.id or lead.partner_id.id)
|
||||
|
||||
partner_id = False
|
||||
if self.action != 'nothing':
|
||||
partner_id = self_def_user._create_partner(
|
||||
lead.id, self.action, vals.get('partner_id') or lead.partner_id.id)
|
||||
|
||||
res = lead.convert_opportunity(partner_id, [], False)
|
||||
user_ids = vals.get('user_ids')
|
||||
lead.convert_opportunity(lead.partner_id.id, [], False)
|
||||
|
||||
leads_to_allocate = leads
|
||||
if self._context.get('no_force_assignation'):
|
||||
leads_to_allocate = leads_to_allocate.filtered(lambda lead: not lead.user_id)
|
||||
|
||||
if user_ids:
|
||||
leads_to_allocate.allocate_salesman(user_ids, team_id=(vals.get('team_id')))
|
||||
leads_to_allocate.handle_salesmen_assignment(user_ids, team_id=team_id)
|
||||
|
||||
return res
|
||||
|
||||
def action_apply(self):
|
||||
""" Convert lead to opportunity or merge lead and opportunity and open
|
||||
the freshly created opportunity view.
|
||||
"""
|
||||
self.ensure_one()
|
||||
values = {
|
||||
'team_id': self.team_id.id,
|
||||
}
|
||||
|
||||
if self.partner_id:
|
||||
values['partner_id'] = self.partner_id.id
|
||||
|
||||
if self.name == 'merge':
|
||||
leads = self.with_context(active_test=False).opportunity_ids.merge_opportunity()
|
||||
if not leads.active:
|
||||
leads.write({'active': True, 'activity_type_id': False, 'lost_reason': False})
|
||||
if leads.type == "lead":
|
||||
values.update({'lead_ids': leads.ids, 'user_ids': [self.user_id.id]})
|
||||
self.with_context(active_ids=leads.ids)._convert_opportunity(values)
|
||||
elif not self._context.get('no_force_assignation') or not leads.user_id:
|
||||
values['user_id'] = self.user_id.id
|
||||
leads.write(values)
|
||||
else:
|
||||
leads = self.env['crm.lead'].browse(self._context.get('active_ids', []))
|
||||
values.update({'lead_ids': leads.ids, 'user_ids': [self.user_id.id]})
|
||||
self._convert_opportunity(values)
|
||||
|
||||
return leads[0].redirect_lead_opportunity_view()
|
||||
|
||||
def _create_partner(self, lead_id, action, partner_id):
|
||||
""" Create partner based on action.
|
||||
:return dict: dictionary organized as followed: {lead_id: partner_assigned_id}
|
||||
"""
|
||||
#TODO this method in only called by Lead2OpportunityPartner
|
||||
#wizard and would probably diserve to be refactored or at least
|
||||
#moved to a better place
|
||||
if action == 'each_exist_or_create':
|
||||
partner_id = self.env['crm.lead'].browse(lead_id)._find_matching_partner()
|
||||
action = 'create'
|
||||
result = self.env['crm.lead'].browse(lead_id).handle_partner_assignation(action, partner_id)
|
||||
return result.get(lead_id)
|
||||
def _convert_handle_partner(self, lead, action, partner_id):
|
||||
# used to propagate user_id (salesman) on created partners during conversion
|
||||
lead.with_context(default_user_id=self.user_id.id).handle_partner_assignment(
|
||||
force_partner_id=partner_id,
|
||||
create_missing=(action == 'create')
|
||||
)
|
||||
|
||||
@@ -39,12 +39,12 @@ class Lead2OpportunityMassConvert(models.TransientModel):
|
||||
@api.onchange('deduplicate')
|
||||
def _onchange_deduplicate(self):
|
||||
active_leads = self.env['crm.lead'].browse(self._context['active_ids'])
|
||||
partner_ids = [(lead.partner_id.id, lead.partner_id and lead.partner_id.email or lead.email_from) for lead in active_leads]
|
||||
partner_ids = [(lead.partner_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.env['crm.lead']._get_duplicated_leads_by_emails(partner_id, email, include_lost=False)
|
||||
for partner, email in partner_ids:
|
||||
duplicated_leads = self.env['crm.lead']._get_lead_duplicates(partner=partner, email=email, include_lost=False)
|
||||
if len(duplicated_leads) > 1:
|
||||
partners_duplicated_leads.setdefault((partner_id, email), []).extend(duplicated_leads)
|
||||
partners_duplicated_leads.setdefault((partner.id, email), []).extend(duplicated_leads)
|
||||
|
||||
leads_with_duplicates = []
|
||||
for lead in active_leads:
|
||||
@@ -54,20 +54,18 @@ class Lead2OpportunityMassConvert(models.TransientModel):
|
||||
|
||||
self.opportunity_ids = self.env['crm.lead'].browse(leads_with_duplicates)
|
||||
|
||||
def _convert_opportunity(self, vals):
|
||||
def _convert_and_allocate(self, leads, user_ids, team_id=False):
|
||||
""" When "massively" (more than one at a time) converting leads to
|
||||
opportunities, check the salesteam_id and salesmen_ids and update
|
||||
the values before calling super.
|
||||
opportunities, check the salesteam_id and salesmen_ids and update
|
||||
the values before calling super.
|
||||
"""
|
||||
self.ensure_one()
|
||||
salesteam_id = self.team_id.id if self.team_id else False
|
||||
salesmen_ids = []
|
||||
if self.user_ids:
|
||||
salesmen_ids = self.user_ids.ids
|
||||
vals.update({'user_ids': salesmen_ids, 'team_id': salesteam_id})
|
||||
return super(Lead2OpportunityMassConvert, self)._convert_opportunity(vals)
|
||||
return super(Lead2OpportunityMassConvert, self)._convert_and_allocate(leads, salesmen_ids, team_id=team_id)
|
||||
|
||||
def mass_convert(self):
|
||||
def action_mass_convert(self):
|
||||
self.ensure_one()
|
||||
if self.name == 'convert' and self.deduplicate:
|
||||
merged_lead_ids = set()
|
||||
@@ -76,7 +74,11 @@ 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.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)
|
||||
duplicated_leads = self.env['crm.lead']._get_lead_duplicates(
|
||||
partner=lead.partner_id,
|
||||
email=lead.partner_id.email or lead.email_from,
|
||||
include_lost=False
|
||||
)
|
||||
if len(duplicated_leads) > 1:
|
||||
lead = duplicated_leads.merge_opportunity()
|
||||
merged_lead_ids.update(duplicated_leads.ids)
|
||||
@@ -87,3 +89,9 @@ class Lead2OpportunityMassConvert(models.TransientModel):
|
||||
self = self.with_context(active_ids=list(active_ids)) # only update active_ids when there are set
|
||||
no_force_assignation = self._context.get('no_force_assignation', not self.force_assignation)
|
||||
return self.with_context(no_force_assignation=no_force_assignation).action_apply()
|
||||
|
||||
def _convert_handle_partner(self, lead, action, partner_id):
|
||||
if self.action == 'each_exist_or_create':
|
||||
partner_id = lead._find_matching_partner().id
|
||||
action = 'create'
|
||||
return super(Lead2OpportunityMassConvert, self)._convert_handle_partner(lead, action, partner_id)
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
</group>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="mass_convert" string="Convert to Opportunities" type="object" class="btn-primary"/>
|
||||
<button string="Convert to Opportunities" name="action_mass_convert" type="object" class="btn-primary"/>
|
||||
<button string="Cancel" class="btn-secondary" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
@@ -55,7 +55,7 @@
|
||||
res_model="crm.lead2opportunity.partner.mass"
|
||||
binding_model="crm.lead" binding_views="list"
|
||||
view_mode="form" target="new"
|
||||
context="{'mass_convert' : True}"
|
||||
context="{}"
|
||||
view_id="view_crm_lead2opportunity_partner_mass"
|
||||
/>
|
||||
</odoo>
|
||||
|
||||
@@ -24,7 +24,7 @@ class Opportunity2Quotation(models.TransientModel):
|
||||
lead = self.env['crm.lead'].browse(self._context['active_id'])
|
||||
if lead:
|
||||
result['lead_id'] = lead.id
|
||||
partner_id = result.get('partner_id') or lead._find_matching_partner()
|
||||
partner_id = result.get('partner_id') or lead._find_matching_partner().id
|
||||
if 'action' in fields and not result.get('action'):
|
||||
result['action'] = 'exist' if partner_id else 'create'
|
||||
if 'partner_id' in fields and not result.get('partner_id'):
|
||||
@@ -46,16 +46,6 @@ class Opportunity2Quotation(models.TransientModel):
|
||||
"""
|
||||
self.ensure_one()
|
||||
if self.action != 'nothing':
|
||||
self.lead_id.write({
|
||||
'partner_id': self.partner_id.id if self.action == 'exist' else self._create_partner()
|
||||
})
|
||||
self.lead_id.handle_partner_assignment(force_partner_id=self.partner_id.id, create_missing=(self.action == 'create'))
|
||||
self.lead_id._onchange_partner_id()
|
||||
return self.lead_id.action_new_quotation()
|
||||
|
||||
def _create_partner(self):
|
||||
""" Create partner based on action.
|
||||
:return int: created res.partner id
|
||||
"""
|
||||
self.ensure_one()
|
||||
result = self.lead_id.handle_partner_assignation(action='create')
|
||||
return result.get(self.lead_id.id)
|
||||
|
||||
@@ -51,10 +51,8 @@ class WebsiteVisitor(models.Model):
|
||||
partners = sorted_leads.mapped('partner_id')
|
||||
if not partners:
|
||||
main_lead = self.lead_ids[0]
|
||||
partner_id = main_lead.handle_partner_assignation(action='create')[main_lead.id]
|
||||
if not main_lead.partner_id:
|
||||
main_lead.partner_id = partner_id
|
||||
self.partner_id = partner_id
|
||||
main_lead.handle_partner_assignment(create_missing=True)
|
||||
self.partner_id = main_lead.partner_id.id
|
||||
return True
|
||||
return check
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ class CrmLead(models.Model):
|
||||
lead.assign_geo_localize(lead.partner_latitude, lead.partner_longitude)
|
||||
partner = self.env['res.partner'].browse(partner_id)
|
||||
if partner.user_id:
|
||||
lead.allocate_salesman(partner.user_id.ids, team_id=partner.team_id.id)
|
||||
lead.handle_salesmen_assignment(partner.user_id.ids, team_id=partner.team_id.id)
|
||||
values = {'date_assign': fields.Date.context_today(lead), 'partner_assigned_id': partner_id}
|
||||
lead.write(values)
|
||||
return res
|
||||
|
||||
Reference in New Issue
Block a user