[REF] crm: improve context use and naming of fields in convert wizards
SPECIFICATIONS 1: FORCE_ASSIGNATION Move and rename force_assignation in convert wizard. Purpose is to move the force_assignation field from mass convert wizard to its base convert wizard used through _inherit. It allows to avoid relying on context propagation in some methods. This field is also renamed as assignation as other meaning in English. SPECIFICATIONS 2: OPPORTUNITY_IDS Improve search of duplicate by always ensuring duplicated are computed using active_test. Also add the active_test on the field as otherwise computed results are actually not visible to the user. Generic opportunity_ids field is also renamed to better indicated it is used to store duplicates and avoid thinking it holds data related to batch convert or merge. LINKS Task ID 2088565 (crm: from onchange to compute) Upgrade PR odoo/upgrade#781
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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':
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
<group string="Assign these opportunities to">
|
||||
<field name="team_id" kanban_view_ref="%(sales_team.crm_team_view_kanban)s"/>
|
||||
<field name="user_ids" widget="many2many_tags" domain="[('share', '=', False)]"/>
|
||||
<field name="force_assignation"/>
|
||||
<field name="force_assignment"/>
|
||||
</group>
|
||||
<label for="opportunity_ids" string="Leads with existing duplicates (for information)" help="Leads that you selected that have duplicates. If the list is empty, it means that no duplicates were found" attrs="{'invisible': [('deduplicate', '=', False)]}"/>
|
||||
<label for="duplicated_lead_ids" string="Leads with existing duplicates (for information)" help="Leads that you selected that have duplicates. If the list is empty, it means that no duplicates were found" attrs="{'invisible': [('deduplicate', '=', False)]}"/>
|
||||
<group attrs="{'invisible': [('deduplicate', '=', False)]}">
|
||||
<field name="opportunity_ids" colspan="4" nolabel="1" readonly="1">
|
||||
<field name="duplicated_lead_ids" colspan="4" nolabel="1" readonly="1">
|
||||
<tree create="false" delete="false">
|
||||
<field name="create_date" widget="date"/>
|
||||
<field name="name"/>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<field name="team_id" widget="selection"/>
|
||||
</group>
|
||||
<group string="Opportunities" attrs="{'invisible': [('name', '!=', 'merge')]}">
|
||||
<field name="opportunity_ids" nolabel="1">
|
||||
<field name="duplicated_lead_ids" nolabel="1">
|
||||
<tree>
|
||||
<field name="create_date" widget="date"/>
|
||||
<field name="name"/>
|
||||
|
||||
Reference in New Issue
Block a user