diff --git a/addons/crm/models/crm_lead.py b/addons/crm/models/crm_lead.py index 189c651fbe2..b25e2fc7c73 100644 --- a/addons/crm/models/crm_lead.py +++ b/addons/crm/models/crm_lead.py @@ -912,9 +912,9 @@ class Lead(models.Model): :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; + :param boolean include_lost: if True, search includes archived opportunities + (still only active leads are considered). If False, search for active + and not won leads and opportunities; """ if not email: return self.env['crm.lead'] @@ -927,11 +927,12 @@ class Lead(models.Model): if not partner_match_domain: return self.env['crm.lead'] domain = partner_match_domain - if not include_lost: - domain += ['&', ('active', '=', True), ('probability', '<', 100)] + if include_lost: + domain += ['|', ('type', '=', 'opportunity'), ('active', '=', True)] else: - domain += ['|', '&', ('type', '=', 'lead'), ('active', '=', True), ('type', '=', 'opportunity')] - return self.search(domain) + domain += ['&', ('active', '=', True), ('probability', '<', 100)] + + return self.with_context(active_test=False).search(domain) def _create_customer(self): """ Create a partner from lead data and link it to the lead. diff --git a/addons/crm/tests/test_crm_lead_convert.py b/addons/crm/tests/test_crm_lead_convert.py index d12cb4e5ab2..382d0a00055 100644 --- a/addons/crm/tests/test_crm_lead_convert.py +++ b/addons/crm/tests/test_crm_lead_convert.py @@ -231,7 +231,7 @@ class TestLeadConvert(crm_common.TestLeadConvertCommon): }).create({}) # test internals of convert wizard - self.assertEqual(convert.opportunity_ids, self.lead_1 | leads) + self.assertEqual(convert.duplicated_lead_ids, self.lead_1 | leads) self.assertEqual(convert.user_id, self.lead_1.user_id) self.assertEqual(convert.team_id, self.lead_1.team_id) self.assertFalse(convert.partner_id) @@ -263,8 +263,8 @@ class TestLeadConvert(crm_common.TestLeadConvertCommon): }).create({}) self.assertEqual(convert.partner_id, self.customer) # TDE FIXME: should not give priority to partner email if it is void - # self.assertEqual(convert.opportunity_ids, self.lead_1 | lead_email_from | lead_email_normalized | lead_partner) - self.assertEqual(convert.opportunity_ids, self.env['crm.lead']) + # self.assertEqual(convert.duplicated_lead_ids, self.lead_1 | lead_email_from | lead_email_normalized | lead_partner) + self.assertEqual(convert.duplicated_lead_ids, self.env['crm.lead']) # Check: partner fallbacks self.lead_1.write({ @@ -278,9 +278,9 @@ class TestLeadConvert(crm_common.TestLeadConvertCommon): 'active_ids': self.lead_1.ids, }).create({}) self.assertEqual(convert.partner_id, self.customer) - # TDE FIXME: CHECKME: 2917b38f28d5c2d6c53c706e613da7b8e2ad7b52 - # self.assertEqual(convert.opportunity_ids, self.lead_1 | lead_partner) - self.assertEqual(convert.opportunity_ids, self.env['crm.lead']) + # TDE FIXME: CHECKME: 2917b38f28d5c2d6c53c706e613da7b8e2ad7b52 for lead without email + # self.assertEqual(convert.duplicated_lead_ids, self.lead_1 | lead_partner) + self.assertEqual(convert.duplicated_lead_ids, self.env['crm.lead']) @users('user_sales_manager') def test_lead_merge_duplicates_flow(self): @@ -299,9 +299,8 @@ class TestLeadConvert(crm_common.TestLeadConvertCommon): }).create({}) self.assertEqual(convert.partner_id, self.customer) # TDE FIXME: should check for email_normalized -> lead_email_normalized not correctly found - # self.assertEqual(convert.opportunity_ids, self.lead_1 | lead_email_from | lead_email_normalized | lead_partner | opp_lost) - # TDE FIXME: add a active test context on opportunity_ids fields - self.assertEqual(convert.with_context(active_test=False).opportunity_ids, self.lead_1 | self.lead_email_from | self.lead_partner | self.opp_lost) + # self.assertEqual(convert.duplicated_lead_ids, self.lead_1 | lead_email_from | lead_email_normalized | lead_partner | opp_lost) + self.assertEqual(convert.duplicated_lead_ids, self.lead_1 | self.lead_email_from | self.lead_partner | self.opp_lost) convert.action_apply() self.assertEqual( @@ -421,7 +420,7 @@ class TestLeadConvertMass(crm_common.TestLeadConvertMassCommon): }).create({ 'deduplicate': False, 'user_id': self.user_sales_salesman.id, - 'force_assignation': False, + 'force_assignment': False, }) mass_convert.onchange_action() mass_convert._onchange_user() @@ -463,7 +462,7 @@ class TestLeadConvertMass(crm_common.TestLeadConvertMassCommon): # self.assertEqual(self.lead_w_email_lost.partner_id, self.env['res.partner']) def test_mass_convert_deduplicate(self): - """ Test opportunity_ids fields having another behavior in mass convert + """ Test duplicated_lead_ids fields having another behavior in mass convert because why not. Its use is: among leads under convert, store those with duplicates if deduplicate is set to True. """ lead_1_dups = self._create_duplicates(self.lead_1, create_opp=False) @@ -483,7 +482,7 @@ class TestLeadConvertMass(crm_common.TestLeadConvertMassCommon): mass_convert._onchange_deduplicate() self.assertEqual(mass_convert.action, 'each_exist_or_create') self.assertEqual(mass_convert.name, 'convert') - self.assertEqual(mass_convert.opportunity_ids, self.lead_1 | self.lead_w_partner) + self.assertEqual(mass_convert.duplicated_lead_ids, self.lead_1 | self.lead_w_partner) mass_convert.action_mass_convert() @@ -508,7 +507,7 @@ class TestLeadConvertMass(crm_common.TestLeadConvertMassCommon): }).create({ 'deduplicate': False, 'user_ids': self.assign_users.ids, - 'force_assignation': True, + 'force_assignment': True, }) # TDE FIXME: what happens if we mix people from different sales team ? currently nothing, to check diff --git a/addons/crm/wizard/crm_lead_to_opportunity.py b/addons/crm/wizard/crm_lead_to_opportunity.py index 22385dabe85..b66ccf3e802 100644 --- a/addons/crm/wizard/crm_lead_to_opportunity.py +++ b/addons/crm/wizard/crm_lead_to_opportunity.py @@ -12,7 +12,7 @@ class Lead2OpportunityPartner(models.TransientModel): @api.model def default_get(self, fields): - """ Default get for name, opportunity_ids. + """ Default get for name, duplicated_lead_ids. If there is an exisitng partner link to the lead, find all existing opportunities links with this partner to merge all information together """ @@ -34,8 +34,8 @@ class Lead2OpportunityPartner(models.TransientModel): 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: - result['opportunity_ids'] = list(tomerge) + if 'duplicated_lead_ids' in fields and len(tomerge) >= 2: + result['duplicated_lead_ids'] = list(tomerge) if lead.user_id: result['user_id'] = lead.user_id.id if lead.team_id: @@ -55,10 +55,14 @@ class Lead2OpportunityPartner(models.TransientModel): ('nothing', 'Do not link to a customer') ], string='Related Customer', required=True) lead_id = fields.Many2one('crm.lead', "Associated Lead") - opportunity_ids = fields.Many2many('crm.lead', string='Opportunities') + duplicated_lead_ids = fields.Many2many( + 'crm.lead', string='Duplicates', context={'active_test': False}) partner_id = fields.Many2one('res.partner', 'Customer') user_id = fields.Many2one('res.users', 'Salesperson') team_id = fields.Many2one('crm.team', 'Sales Team') + force_assignment = fields.Boolean( + 'Force assignment', default=True, + help='If checked, forces salesman to be updated on updated opportunities even if already set.') @api.onchange('action') def onchange_action(self): @@ -99,17 +103,18 @@ class Lead2OpportunityPartner(models.TransientModel): return result_opportunity.redirect_lead_opportunity_view() def _action_merge(self): - result_opportunity = self.with_context(active_test=False).opportunity_ids.merge_opportunity() + result_opportunity = self.duplicated_lead_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, - }) + else: + if not result_opportunity.user_id or self.force_assignment: + result_opportunity.write({ + 'user_id': self.user_id.id, + 'team_id': self.team_id.id, + }) return result_opportunity def _action_convert(self): @@ -129,7 +134,7 @@ class Lead2OpportunityPartner(models.TransientModel): lead.convert_opportunity(lead.partner_id.id, [], False) leads_to_allocate = leads - if self._context.get('no_force_assignation'): + if not self.force_assignment: leads_to_allocate = leads_to_allocate.filtered(lambda lead: not lead.user_id) if user_ids: diff --git a/addons/crm/wizard/crm_lead_to_opportunity_mass.py b/addons/crm/wizard/crm_lead_to_opportunity_mass.py index a10ef43f31a..ce1e2a0b766 100644 --- a/addons/crm/wizard/crm_lead_to_opportunity_mass.py +++ b/addons/crm/wizard/crm_lead_to_opportunity_mass.py @@ -5,7 +5,6 @@ from odoo import api, fields, models class Lead2OpportunityMassConvert(models.TransientModel): - _name = 'crm.lead2opportunity.partner.mass' _description = 'Convert Lead to Opportunity (in mass)' _inherit = 'crm.lead2opportunity.partner' @@ -19,8 +18,8 @@ class Lead2OpportunityMassConvert(models.TransientModel): res['action'] = 'each_exist_or_create' if 'name' in fields: res['name'] = 'convert' - if 'opportunity_ids' in fields: - res['opportunity_ids'] = False + if 'duplicated_lead_ids' in fields: + res['duplicated_lead_ids'] = False return res user_ids = fields.Many2many('res.users', string='Salesmen') @@ -29,7 +28,7 @@ class Lead2OpportunityMassConvert(models.TransientModel): action = fields.Selection(selection_add=[ ('each_exist_or_create', 'Use existing partner or create'), ], string='Related Customer', required=True) - force_assignation = fields.Boolean('Force assignment', help='If unchecked, this will leave the salesman of duplicated opportunities') + force_assignment = fields.Boolean(default=False) @api.onchange('action') def _onchange_action(self): @@ -52,7 +51,7 @@ class Lead2OpportunityMassConvert(models.TransientModel): if len(partners_duplicated_leads.get(lead_tuple, [])) > 1: leads_with_duplicates.append(lead.id) - self.opportunity_ids = self.env['crm.lead'].browse(leads_with_duplicates) + self.duplicated_lead_ids = self.env['crm.lead'].browse(leads_with_duplicates) def _convert_and_allocate(self, leads, user_ids, team_id=False): """ When "massively" (more than one at a time) converting leads to @@ -87,8 +86,7 @@ class Lead2OpportunityMassConvert(models.TransientModel): active_ids = (active_ids - merged_lead_ids) | remaining_lead_ids 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() + return self.action_apply() def _convert_handle_partner(self, lead, action, partner_id): if self.action == 'each_exist_or_create': diff --git a/addons/crm/wizard/crm_lead_to_opportunity_mass_views.xml b/addons/crm/wizard/crm_lead_to_opportunity_mass_views.xml index f5d3b4a4f1b..0d0911df92b 100644 --- a/addons/crm/wizard/crm_lead_to_opportunity_mass_views.xml +++ b/addons/crm/wizard/crm_lead_to_opportunity_mass_views.xml @@ -13,11 +13,11 @@ - + -