[REF] crm: cleanup test environment to prepare changes in assignment process
This commit contains two main preparatory changes in tests
* 1) lead_month_count will be changed to count lost lead as well. So
archiving leads before running the test doesn't reset the counter.
Lead needs to be unlinked in order to make tests reproducible;
* 2) assertMemberAssign checked the validity of the team domain. Normally
all lead assigned to the member should match the domain of its team.
This assert is not always verified since we force the team after a
merge of duplicated leads. Final merged lead may not match team domain
anymore as final properties depend on all merged leads.
Some tests are also added :
* add tests for master lead team_id and user_id values when merging leads
in assign process;
* add tests using email_normalized check in duplicate finding to ensure that
normalized emails are used for duplicates and not only exact match;
* add tests for won / lost inclusion in ``_get_lead_duplicates``, as well
as different between probability=100 and stage.is_won = True . This is
going to change in upcoming commits, hence some test to highlight it;
* add tests counting existing leads for assignment purpose;
* add tests for assignment_max maximum leads allocation as well as max=0
meaning salesperson is opt-outed from assign;
* add tests for default user / team / stage computation for leads;
* add tests about leads taken into account in assign process (currently
failing with won unassigned leads);
We also add some flush to ensure some fields notably related to probabilities
are flushed before counting queries.
LINKS
Task ID-2444908 (assign fixes)
Task ID-2489951 (assign process improvements)
COM PR odoo/odoo#70172
X-original-commit: bd3444085974f4e12287c2f5da4fb33308cd6180
Co-authored-by: Thibault François <tfr@odoo.com>
Co-authored-by: Thibault Delavallée <tde@odoo.com>
This commit is contained in:
committed by
Thibault Delavallée
co-authored by
Thibault François
Thibault Delavallée
parent
71f19d55fa
commit
2f829b0e28
@@ -198,7 +198,7 @@ class Team(models.Model):
|
||||
(member_info["team_member"], {"assigned": member_info["assigned"]})
|
||||
for member_id, member_info in members_data.items()
|
||||
)
|
||||
_logger.info('Assigned %s leads to %s salesmen' % (len(leads_done_ids), len(self)))
|
||||
_logger.info('Assigned %s leads to %s salesmen' % (len(leads_done_ids), len(members)))
|
||||
for member, member_info in result_data.items():
|
||||
_logger.info('-> member %s: assigned %d leads (%s)' % (member.id, len(member_info["assigned"]), member_info["assigned"]))
|
||||
return result_data
|
||||
|
||||
+32
-31
@@ -74,7 +74,7 @@ class TestCrmCommon(TestSalesCommon, MailCase):
|
||||
'assignment_domain': False,
|
||||
})
|
||||
|
||||
(cls.user_sales_manager | cls.user_sales_leads | cls.user_sales_salesman).write({
|
||||
(cls.user_sales_manager + cls.user_sales_leads + cls.user_sales_salesman).write({
|
||||
'groups_id': [(4, cls.env.ref('crm.group_use_lead').id)]
|
||||
})
|
||||
|
||||
@@ -139,7 +139,7 @@ class TestCrmCommon(TestSalesCommon, MailCase):
|
||||
'team_id': cls.sales_team_1.id,
|
||||
})
|
||||
cls.lead_team_1_lost.action_set_lost()
|
||||
(cls.lead_team_1_won | cls.lead_team_1_lost).flush()
|
||||
(cls.lead_team_1_won + cls.lead_team_1_lost).flush()
|
||||
|
||||
# email / phone data
|
||||
cls.test_email_data = [
|
||||
@@ -212,7 +212,7 @@ class TestCrmCommon(TestSalesCommon, MailCase):
|
||||
|
||||
def _create_leads_batch(self, lead_type='lead', count=10, email_dup_count=0,
|
||||
partner_count=0, partner_ids=None, user_ids=None,
|
||||
country_ids=None):
|
||||
country_ids=None, probabilities=None):
|
||||
""" Helper tool method creating a batch of leads, useful when dealing
|
||||
with batch processes. Please update me.
|
||||
|
||||
@@ -280,6 +280,11 @@ class TestCrmCommon(TestSalesCommon, MailCase):
|
||||
for idx, lead_data in enumerate(leads_data):
|
||||
lead_data['user_id'] = user_ids[idx % len(user_ids)]
|
||||
|
||||
# probabilities
|
||||
if probabilities:
|
||||
for idx, lead_data in enumerate(leads_data):
|
||||
lead_data['probability'] = probabilities[idx % len(probabilities)]
|
||||
|
||||
# duplicates (currently only with email)
|
||||
dups_data = []
|
||||
if email_dup_count and not partner_ids:
|
||||
@@ -303,49 +308,44 @@ class TestCrmCommon(TestSalesCommon, MailCase):
|
||||
* a lead with customer but another email
|
||||
* a lost opportunity with same email_from
|
||||
"""
|
||||
self.customer = self.env['res.partner'].create({
|
||||
customer = self.env['res.partner'].create({
|
||||
'name': 'Lead1 Email Customer',
|
||||
'email': lead.email_from,
|
||||
})
|
||||
self.lead_email_from = self.env['crm.lead'].create({
|
||||
lead_email_from = self.env['crm.lead'].create({
|
||||
'name': 'Duplicate: same email_from',
|
||||
'type': 'lead',
|
||||
'team_id': lead.team_id.id,
|
||||
'email_from': lead.email_from,
|
||||
})
|
||||
# self.lead_email_normalized = self.env['crm.lead'].create({
|
||||
# 'name': 'Duplicate: email_normalize comparison',
|
||||
# 'type': 'lead',
|
||||
# 'team_id': lead.team_id.id,
|
||||
# 'stage_id': lead.stage_id.id,
|
||||
# 'email_from': 'CUSTOMER WITH NAME <%s>' % lead.email_normalized.upper(),
|
||||
# })
|
||||
self.lead_partner = self.env['crm.lead'].create({
|
||||
lead_email_normalized = self.env['crm.lead'].create({
|
||||
'name': 'Duplicate: email_normalize comparison',
|
||||
'type': 'lead',
|
||||
'team_id': lead.team_id.id,
|
||||
'stage_id': lead.stage_id.id,
|
||||
'email_from': 'CUSTOMER WITH NAME <%s>' % lead.email_normalized.upper(),
|
||||
})
|
||||
lead_partner = self.env['crm.lead'].create({
|
||||
'name': 'Duplicate: customer ID',
|
||||
'type': 'lead',
|
||||
'team_id': lead.team_id.id,
|
||||
'partner_id': self.customer.id,
|
||||
'partner_id': customer.id,
|
||||
})
|
||||
if create_opp:
|
||||
self.opp_lost = self.env['crm.lead'].create({
|
||||
opp_lost = self.env['crm.lead'].create({
|
||||
'name': 'Duplicate: lost opportunity',
|
||||
'type': 'opportunity',
|
||||
'team_id': lead.team_id.id,
|
||||
'stage_id': lead.stage_id.id,
|
||||
'email_from': lead.email_from,
|
||||
})
|
||||
self.opp_lost.action_set_lost()
|
||||
opp_lost.action_set_lost()
|
||||
else:
|
||||
self.opp_lost = self.env['crm.lead']
|
||||
opp_lost = self.env['crm.lead']
|
||||
|
||||
# self.assertEqual(self.lead_email_from.email_normalized, self.lead_email_normalized.email_normalized)
|
||||
# self.assertTrue(lead.email_from != self.lead_email_normalized.email_from)
|
||||
# self.assertFalse(self.opp_lost.active)
|
||||
|
||||
# new_lead = self.lead_email_from | self.lead_email_normalized | self.lead_partner | self.opp_lost
|
||||
new_leads = self.lead_email_from | self.lead_partner | self.opp_lost
|
||||
new_leads = lead_email_from + lead_email_normalized + lead_partner + opp_lost
|
||||
new_leads.flush() # compute notably probability
|
||||
return new_leads
|
||||
return customer, new_leads
|
||||
|
||||
@contextmanager
|
||||
def assertLeadMerged(self, opportunity, leads, **expected):
|
||||
@@ -532,12 +532,13 @@ class TestLeadConvertCommon(TestCrmCommon):
|
||||
member_leads.filtered_domain(literal_eval(member.assignment_domain)),
|
||||
member_leads
|
||||
)
|
||||
if member.crm_team_id.assignment_domain:
|
||||
self.assertEqual(
|
||||
member_leads.filtered_domain(literal_eval(member.crm_team_id.assignment_domain)),
|
||||
member_leads,
|
||||
'Assign domain not matching: %s' % member.crm_team_id.assignment_domain
|
||||
)
|
||||
# TODO this condition is not fulfilled in case of merge, need to change merge/assignment process
|
||||
# if member.crm_team_id.assignment_domain:
|
||||
# self.assertEqual(
|
||||
# member_leads.filtered_domain(literal_eval(member.crm_team_id.assignment_domain)),
|
||||
# member_leads,
|
||||
# 'Assign domain not matching: %s' % member.crm_team_id.assignment_domain
|
||||
# )
|
||||
|
||||
class TestLeadConvertMassCommon(TestLeadConvertCommon):
|
||||
|
||||
@@ -611,4 +612,4 @@ class TestLeadConvertMassCommon(TestLeadConvertCommon):
|
||||
'stage_id': cls.stage_team1_2.id,
|
||||
'active': False,
|
||||
})
|
||||
(cls.lead_w_partner | cls.lead_w_partner_company | cls.lead_w_contact | cls.lead_w_email | cls.lead_w_email_lost).flush()
|
||||
(cls.lead_w_partner + cls.lead_w_partner_company + cls.lead_w_contact + cls.lead_w_email + cls.lead_w_email_lost).flush()
|
||||
|
||||
@@ -201,6 +201,32 @@ class TestCRMLead(TestCrmCommon):
|
||||
self.assertEqual(lead.function, empty_partner.function, "Function from partner should be set on the lead")
|
||||
self.assertEqual(lead.website, lead_data['website'], "Website should keep its initial value")
|
||||
|
||||
@users('user_sales_manager')
|
||||
def test_crm_lead_create_pipe_data(self):
|
||||
""" Test creation pipe data: user, team, stage, depending on some default
|
||||
configuration. """
|
||||
# gateway-like creation: no user, no team, generic stage
|
||||
lead = self.env['crm.lead'].with_context(default_user_id=False).create({
|
||||
'name': 'Test',
|
||||
'contact_name': 'Test Contact',
|
||||
'email_from': self.test_email,
|
||||
'phone': self.test_phone,
|
||||
})
|
||||
self.assertEqual(lead.user_id, self.env['res.users'])
|
||||
self.assertEqual(lead.team_id, self.env['crm.team'])
|
||||
self.assertEqual(lead.stage_id, self.stage_gen_1)
|
||||
|
||||
# pipe creation: current user's best team and default stage
|
||||
lead = self.env['crm.lead'].create({
|
||||
'name': 'Test',
|
||||
'contact_name': 'Test Contact',
|
||||
'email_from': self.test_email,
|
||||
'phone': self.test_phone,
|
||||
})
|
||||
self.assertEqual(lead.user_id, self.user_sales_manager)
|
||||
self.assertEqual(lead.team_id, self.sales_team_1)
|
||||
self.assertEqual(lead.stage_id, self.stage_team1_1)
|
||||
|
||||
@users('user_sales_manager')
|
||||
def test_crm_lead_partner_sync(self):
|
||||
lead, partner = self.lead_1.with_user(self.env.user), self.contact_2
|
||||
|
||||
@@ -22,11 +22,12 @@ class TestLeadAssignCommon(TestLeadConvertCommon):
|
||||
|
||||
# don't mess with existing teams, deactivate them to make tests repeatable
|
||||
cls.sales_teams = cls.sales_team_1 + cls.sales_team_convert
|
||||
cls.members = cls.sales_team_1_m1 | cls.sales_team_1_m2 | cls.sales_team_1_m3 | cls.sales_team_convert_m1 | cls.sales_team_convert_m2
|
||||
cls.members = cls.sales_team_1_m1 + cls.sales_team_1_m2 + cls.sales_team_1_m3 + cls.sales_team_convert_m1 + cls.sales_team_convert_m2
|
||||
cls.env['crm.team'].search([('id', 'not in', cls.sales_teams.ids)]).write({'active': False})
|
||||
|
||||
# don't mess with existing leads, deactivate those assigned to users used here to make tests repeatable
|
||||
cls.env['crm.lead'].search(['|', ('team_id', '=', False), ('user_id', 'in', cls.sales_teams.member_ids.ids)]).write({'active': False})
|
||||
# don't mess with existing leads, unlink those assigned to users used here to make tests
|
||||
# repeatable (archive is not sufficient because of lost leads)
|
||||
cls.env['crm.lead'].with_context(active_test=False).search(['|', ('team_id', '=', False), ('user_id', 'in', cls.sales_teams.member_ids.ids)]).unlink()
|
||||
cls.bundle_size = 5
|
||||
cls.env['ir.config_parameter'].set_param('crm.assignment.bundle', '%s' % cls.bundle_size)
|
||||
cls.env['ir.config_parameter'].set_param('crm.assignment.delay', '0')
|
||||
@@ -103,6 +104,66 @@ class TestLeadAssign(TestLeadAssignCommon):
|
||||
self.assertFalse(self.assign_cron.active)
|
||||
self.assertEqual(self.assign_cron.nextcall, datetime(2020, 11, 1, 10, 0, 0))
|
||||
|
||||
def test_assign_count(self):
|
||||
""" Test number of assigned leads when dealing with some existing data (leads
|
||||
or opportunities) as well as with opt-out management. """
|
||||
leads = self._create_leads_batch(
|
||||
lead_type='lead',
|
||||
user_ids=[False],
|
||||
partner_ids=[False, False, False, self.contact_1.id],
|
||||
probabilities=[30],
|
||||
count=8
|
||||
)
|
||||
# commit probability and related fields
|
||||
leads.flush()
|
||||
self.assertInitialData()
|
||||
|
||||
# archived members should not be taken into account
|
||||
self.sales_team_1_m1.action_archive()
|
||||
# assignment_max = 0 means opt_out
|
||||
self.sales_team_1_m2.assignment_max = 0
|
||||
|
||||
# assign probability to leads (bypass auto probability as purpose is not to test pls)
|
||||
leads = self.env['crm.lead'].search([('id', 'in', leads.ids)]) # ensure order
|
||||
for idx, lead in enumerate(leads):
|
||||
lead.probability = idx * 10
|
||||
# commit probability and related fields
|
||||
leads.flush()
|
||||
self.assertEqual(leads[0].probability, 0)
|
||||
|
||||
# create exiting leads for user_sales_salesman (sales_team_1_m3, sales_team_convert_m1)
|
||||
existing_leads = self._create_leads_batch(
|
||||
lead_type='lead', user_ids=[self.user_sales_salesman.id],
|
||||
probabilities=[10],
|
||||
count=14)
|
||||
self.assertEqual(existing_leads.team_id, self.sales_team_1, "Team should have lower sequence")
|
||||
existing_leads[0].active = False # lost
|
||||
existing_leads[1].probability = 100 # not won
|
||||
existing_leads[2].probability = 0 # not lost
|
||||
existing_leads.flush()
|
||||
|
||||
self.members.invalidate_cache(fnames=['lead_month_count'])
|
||||
self.assertEqual(self.sales_team_1_m3.lead_month_count, 12)
|
||||
self.assertEqual(self.sales_team_convert_m1.lead_month_count, 0)
|
||||
|
||||
with self.with_user('user_sales_manager'):
|
||||
self.env['crm.team'].browse(self.sales_team_1.ids)._action_assign_leads(work_days=4)
|
||||
|
||||
# salespersons assign
|
||||
self.members.invalidate_cache(fnames=['lead_month_count'])
|
||||
self.assertEqual(self.sales_team_1_m1.lead_month_count, 0) # archived do not get leads
|
||||
self.assertEqual(self.sales_team_1_m2.lead_month_count, 0) # opt-out through assignment_max = 0
|
||||
self.assertEqual(self.sales_team_1_m3.lead_month_count, 14) # 15 max on 4 days (2) + existing 12
|
||||
|
||||
with self.with_user('user_sales_manager'):
|
||||
self.env['crm.team'].browse(self.sales_team_1.ids)._action_assign_leads(work_days=4)
|
||||
|
||||
# salespersons assign
|
||||
self.members.invalidate_cache(fnames=['lead_month_count'])
|
||||
self.assertEqual(self.sales_team_1_m1.lead_month_count, 0) # archived do not get leads
|
||||
self.assertEqual(self.sales_team_1_m2.lead_month_count, 0) # opt-out through assignment_max = 0
|
||||
self.assertEqual(self.sales_team_1_m3.lead_month_count, 15) # 15 max on 4 days (2) + existing 14 but capped at 15
|
||||
|
||||
@mute_logger('odoo.models.unlink')
|
||||
def test_assign_duplicates(self):
|
||||
""" Test assign process with duplicates on partner. Allow to ensure notably
|
||||
@@ -113,6 +174,8 @@ class TestLeadAssign(TestLeadAssignCommon):
|
||||
partner_ids=[self.contact_1.id, self.contact_2.id, False, False, False],
|
||||
count=50
|
||||
)
|
||||
# commit probability and related fields
|
||||
leads.flush()
|
||||
self.assertInitialData()
|
||||
|
||||
# assign probability to leads (bypass auto probability as purpose is not to test pls)
|
||||
@@ -121,6 +184,8 @@ class TestLeadAssign(TestLeadAssignCommon):
|
||||
sliced_leads = leads[idx:len(leads):5]
|
||||
for lead in sliced_leads:
|
||||
lead.probability = (idx + 1) * 10 * ((int(lead.priority) + 1) / 2)
|
||||
# commit probability and related fields
|
||||
leads.flush()
|
||||
|
||||
with self.with_user('user_sales_manager'):
|
||||
self.env['crm.team'].browse(self.sales_teams.ids)._action_assign_leads(work_days=2)
|
||||
@@ -146,7 +211,7 @@ class TestLeadAssign(TestLeadAssignCommon):
|
||||
|
||||
# teams assign: everything should be done due to duplicates
|
||||
leads = self.env['crm.lead'].search([('id', 'in', leads.ids)]) # ensure order
|
||||
self.assertTrue(len(leads.filtered_domain([('team_id', '=', False)])) == 0)
|
||||
self.assertEqual(len(leads.filtered_domain([('team_id', '=', False)])), 0)
|
||||
|
||||
# deduplicate should have removed all duplicated linked to contact_1 and contact_2
|
||||
new_assigned_leads_wpartner = self.env['crm.lead'].search([
|
||||
@@ -163,6 +228,8 @@ class TestLeadAssign(TestLeadAssignCommon):
|
||||
partner_ids=[False],
|
||||
count=50
|
||||
)
|
||||
# commit probability and related fields
|
||||
leads.flush()
|
||||
self.assertInitialData()
|
||||
|
||||
# assign probability to leads (bypass auto probability as purpose is not to test pls)
|
||||
@@ -171,6 +238,8 @@ class TestLeadAssign(TestLeadAssignCommon):
|
||||
sliced_leads = leads[idx:len(leads):5]
|
||||
for lead in sliced_leads:
|
||||
lead.probability = (idx + 1) * 10 * ((int(lead.priority) + 1) / 2)
|
||||
# commit probability and related fields
|
||||
leads.flush()
|
||||
|
||||
with self.with_user('user_sales_manager'):
|
||||
self.env['crm.team'].browse(self.sales_teams.ids)._action_assign_leads(work_days=2)
|
||||
@@ -203,7 +272,10 @@ class TestLeadAssign(TestLeadAssignCommon):
|
||||
country_ids=[self.env.ref('base.be').id, self.env.ref('base.fr').id, False],
|
||||
count=_lead_count,
|
||||
email_dup_count=_email_dup_count)
|
||||
# commit probability and related fields
|
||||
leads.flush()
|
||||
self.assertInitialData()
|
||||
|
||||
# assign for one month, aka a lot
|
||||
self.env.ref('crm.ir_cron_crm_lead_assign').write({'interval_type': 'days', 'interval_number': 30})
|
||||
self.env['ir.config_parameter'].set_param('crm.assignment.bundle', '20')
|
||||
@@ -246,6 +318,8 @@ class TestLeadAssign(TestLeadAssignCommon):
|
||||
sliced_leads = leads[idx:len(leads):5]
|
||||
for lead in sliced_leads:
|
||||
lead.probability = (idx + 1) * 10 * ((int(lead.priority) + 1) / 2)
|
||||
# commit probability and related fields
|
||||
leads.flush()
|
||||
|
||||
with self.with_user('user_sales_manager'):
|
||||
self.env['crm.team'].browse(sales_teams.ids)._action_assign_leads(work_days=30)
|
||||
@@ -259,3 +333,78 @@ class TestLeadAssign(TestLeadAssignCommon):
|
||||
self.assertMemberAssign(sales_team_3_m1, 60) # 60 max on one month
|
||||
self.assertMemberAssign(sales_team_3_m2, 60) # 60 max on one month
|
||||
self.assertMemberAssign(sales_team_3_m3, 15) # 15 max on one month
|
||||
|
||||
def test_assign_specific_won_lost(self):
|
||||
""" Test leads taken into account in assign process: won, lost, stage
|
||||
configuration. """
|
||||
leads = self._create_leads_batch(
|
||||
lead_type='lead',
|
||||
user_ids=[False],
|
||||
partner_ids=[False, False, False, self.contact_1.id],
|
||||
probabilities=[30],
|
||||
count=6
|
||||
)
|
||||
leads[0].stage_id = self.stage_gen_won.id # is won -> should not be taken into account
|
||||
leads[1].stage_id = False
|
||||
leads[2].update({'stage_id': False, 'probability': 0})
|
||||
leads[3].update({'stage_id': False, 'probability': False})
|
||||
leads[4].active = False # is lost -> should not be taken into account
|
||||
leads[5].update({'team_id': self.sales_team_convert.id, 'user_id': self.user_sales_manager.id}) # assigned lead should not be re-assigned
|
||||
|
||||
# commit probability and related fields
|
||||
leads.flush()
|
||||
|
||||
with self.with_user('user_sales_manager'):
|
||||
self.env['crm.team'].browse(self.sales_team_1.ids)._action_assign_leads(work_days=4)
|
||||
|
||||
# self.assertEqual(leads[0].team_id, self.env['crm.team'], 'Won lead should not be assigned')
|
||||
# self.assertEqual(leads[0].user_id, self.env['res.users'], 'Won lead should not be assigned')
|
||||
for lead in leads[1:4]:
|
||||
self.assertIn(lead.user_id, self.sales_team_1.member_ids)
|
||||
self.assertEqual(lead.team_id, self.sales_team_1)
|
||||
self.assertEqual(leads[4].team_id, self.env['crm.team'], 'Lost lead should not be assigned')
|
||||
self.assertEqual(leads[4].user_id, self.env['res.users'], 'Lost lead should not be assigned')
|
||||
self.assertEqual(leads[5].team_id, self.sales_team_convert, 'Assigned lead should not be reassigned')
|
||||
self.assertEqual(leads[5].user_id, self.user_sales_manager, 'Assigned lead should not be reassigned')
|
||||
|
||||
@mute_logger('odoo.models.unlink')
|
||||
def test_merge_assign_keep_master_team(self):
|
||||
""" Check existing opportunity keep its team and salesman when merged with a new lead """
|
||||
sales_team_dupe = self.env['crm.team'].create({
|
||||
'name': 'Sales Team Dupe',
|
||||
'sequence': 15,
|
||||
'alias_name': False,
|
||||
'use_leads': True,
|
||||
'use_opportunities': True,
|
||||
'company_id': False,
|
||||
'user_id': False,
|
||||
'assignment_domain': "[]",
|
||||
})
|
||||
self.env['crm.team.member'].create({
|
||||
'user_id': self.user_sales_salesman.id,
|
||||
'crm_team_id': sales_team_dupe.id,
|
||||
'assignment_max': 10,
|
||||
'assignment_domain': "[]",
|
||||
})
|
||||
|
||||
master_opp = self.env['crm.lead'].create({
|
||||
'name': 'Master',
|
||||
'type': 'opportunity',
|
||||
'probability': 50,
|
||||
'partner_id': self.contact_1.id,
|
||||
'team_id': self.sales_team_1.id,
|
||||
'user_id': self.user_sales_manager.id,
|
||||
})
|
||||
dupe_lead = self.env['crm.lead'].create({
|
||||
'name': 'Dupe',
|
||||
'type': 'lead',
|
||||
'email_from': 'Duplicate Email <%s>' % master_opp.email_normalized,
|
||||
'probability': 10,
|
||||
'team_id': False,
|
||||
'user_id': False,
|
||||
})
|
||||
|
||||
sales_team_dupe._action_assign_leads(work_days=2)
|
||||
self.assertFalse(dupe_lead.exists())
|
||||
self.assertEqual(master_opp.team_id, sales_team_dupe, 'Opportunity: current team wins lead even if was on duplicated lead')
|
||||
self.assertEqual(master_opp.user_id, self.user_sales_manager, 'Opportunity: should keep its salesman')
|
||||
|
||||
@@ -69,6 +69,56 @@ class TestLeadConvert(crm_common.TestLeadConvertCommon):
|
||||
date = Datetime.from_string('2020-01-20 16:00:00')
|
||||
cls.crm_lead_dt_mock.now.return_value = date
|
||||
|
||||
@users('user_sales_manager')
|
||||
def test_duplicates_computation(self):
|
||||
""" Test Lead._get_lead_duplicates() and check won / probability usage """
|
||||
test_lead = self.env['crm.lead'].browse(self.lead_1.ids)
|
||||
customer, dup_leads = self._create_duplicates(test_lead)
|
||||
dup_leads += self.env['crm.lead'].create([
|
||||
{'name': 'Duplicate lead: same email_from, lost',
|
||||
'type': 'lead',
|
||||
'email_from': test_lead.email_from,
|
||||
'probability': 0, 'active': False,
|
||||
},
|
||||
{'name': 'Duplicate lead: same email_from, proba 0 but not lost',
|
||||
'type': 'lead',
|
||||
'email_from': test_lead.email_from,
|
||||
'probability': 0, 'active': True,
|
||||
},
|
||||
{'name': 'Duplicate opp: same email_from, won',
|
||||
'type': 'opportunity',
|
||||
'email_from': test_lead.email_from,
|
||||
'probability': 100, 'stage_id': self.stage_team1_won.id,
|
||||
},
|
||||
{'name': 'Duplicate opp: same email_from, proba 100 but not won',
|
||||
'type': 'opportunity',
|
||||
'email_from': test_lead.email_from,
|
||||
'probability': 100, 'stage_id': self.stage_team1_2.id,
|
||||
}
|
||||
])
|
||||
lead_lost = dup_leads.filtered(lambda lead: lead.name == 'Duplicate lead: same email_from, lost')
|
||||
opp_proba100 = dup_leads.filtered(lambda lead: lead.name == 'Duplicate opp: same email_from, proba 100 but not won')
|
||||
opp_won = dup_leads.filtered(lambda lead: lead.name == 'Duplicate opp: same email_from, won')
|
||||
opp_lost = dup_leads.filtered(lambda lead: lead.name == 'Duplicate: lost opportunity')
|
||||
|
||||
test_lead.write({'partner_id': customer.id})
|
||||
|
||||
# not include_lost = remove archived leads as well as 'won' opportunities
|
||||
result = test_lead._get_lead_duplicates(
|
||||
partner=test_lead.partner_id,
|
||||
email=test_lead.email_from,
|
||||
include_lost=False
|
||||
)
|
||||
self.assertEqual(result, test_lead + dup_leads - (lead_lost + opp_proba100 + opp_won + opp_lost))
|
||||
|
||||
# include_lost = remove archived opp only
|
||||
result = test_lead._get_lead_duplicates(
|
||||
partner=test_lead.partner_id,
|
||||
email=test_lead.email_from,
|
||||
include_lost=True
|
||||
)
|
||||
self.assertEqual(result, test_lead + dup_leads - (lead_lost))
|
||||
|
||||
def test_initial_data(self):
|
||||
""" Ensure initial data to avoid spaghetti test update afterwards """
|
||||
self.assertFalse(self.lead_1.date_conversion)
|
||||
@@ -341,35 +391,35 @@ class TestLeadConvert(crm_common.TestLeadConvertCommon):
|
||||
|
||||
@users('user_sales_manager')
|
||||
def test_lead_merge_duplicates(self):
|
||||
""" Test Lead._get_lead_duplicates() """
|
||||
""" Test Lead._get_lead_duplicates() and check: partner / email fallbacks """
|
||||
customer, dup_leads = self._create_duplicates(self.lead_1)
|
||||
lead_partner = dup_leads.filtered(lambda lead: lead.name == 'Duplicate: customer ID')
|
||||
self.assertTrue(bool(lead_partner))
|
||||
|
||||
# Check: partner / email fallbacks
|
||||
self._create_duplicates(self.lead_1)
|
||||
self.lead_1.write({
|
||||
'partner_id': self.customer.id,
|
||||
'partner_id': customer.id,
|
||||
})
|
||||
convert = self.env['crm.lead2opportunity.partner'].with_context({
|
||||
'active_model': 'crm.lead',
|
||||
'active_id': self.lead_1.id,
|
||||
'active_ids': self.lead_1.ids,
|
||||
}).create({})
|
||||
self.assertEqual(convert.partner_id, self.customer)
|
||||
# self.assertEqual(convert.duplicated_lead_ids, self.lead_1 | self.lead_email_from | self.lead_email_normalized | self.lead_partner | self.opp_lost)
|
||||
self.assertEqual(convert.duplicated_lead_ids, self.lead_1 | self.lead_email_from | self.lead_partner | self.opp_lost)
|
||||
self.assertEqual(convert.partner_id, customer)
|
||||
self.assertEqual(convert.duplicated_lead_ids, self.lead_1 | dup_leads)
|
||||
|
||||
# Check: partner fallbacks
|
||||
self.lead_1.write({
|
||||
'email_from': False,
|
||||
'partner_id': self.customer.id,
|
||||
'partner_id': customer.id,
|
||||
})
|
||||
self.customer.write({'email': False})
|
||||
customer.write({'email': False})
|
||||
convert = self.env['crm.lead2opportunity.partner'].with_context({
|
||||
'active_model': 'crm.lead',
|
||||
'active_id': self.lead_1.id,
|
||||
'active_ids': self.lead_1.ids,
|
||||
}).create({})
|
||||
self.assertEqual(convert.partner_id, self.customer)
|
||||
self.assertEqual(convert.duplicated_lead_ids, self.lead_1 | self.lead_partner)
|
||||
self.assertEqual(convert.partner_id, customer)
|
||||
self.assertEqual(convert.duplicated_lead_ids, self.lead_1 | lead_partner)
|
||||
|
||||
@users('user_sales_manager')
|
||||
def test_lead_merge_duplicates_flow(self):
|
||||
@@ -379,23 +429,22 @@ class TestLeadConvert(crm_common.TestLeadConvertCommon):
|
||||
self.lead_1.write({
|
||||
'email_from': 'Amy Wong <amy.wong@test.example.com>'
|
||||
})
|
||||
self._create_duplicates(self.lead_1)
|
||||
customer, dup_leads = self._create_duplicates(self.lead_1)
|
||||
opp_lost = dup_leads.filtered(lambda lead: lead.name == 'Duplicate: lost opportunity')
|
||||
self.assertTrue(bool(opp_lost))
|
||||
|
||||
convert = self.env['crm.lead2opportunity.partner'].with_context({
|
||||
'active_model': 'crm.lead',
|
||||
'active_id': self.lead_1.id,
|
||||
'active_ids': self.lead_1.ids,
|
||||
}).create({})
|
||||
self.assertEqual(convert.partner_id, self.customer)
|
||||
# TDE FIXME: should check for email_normalized -> lead_email_normalized not correctly found
|
||||
# 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)
|
||||
self.assertEqual(convert.partner_id, customer)
|
||||
self.assertEqual(convert.duplicated_lead_ids, self.lead_1 | dup_leads)
|
||||
|
||||
convert.action_apply()
|
||||
self.assertEqual(
|
||||
# (self.lead_1 | self.lead_email_from | self.lead_email_normalized | self.lead_partner | self.opp_lost).exists(),
|
||||
(self.lead_1 | self.lead_email_from | self.lead_partner | self.opp_lost).exists(),
|
||||
self.opp_lost)
|
||||
(self.lead_1 | dup_leads).exists(),
|
||||
opp_lost)
|
||||
|
||||
|
||||
@tagged('lead_manage')
|
||||
|
||||
@@ -109,10 +109,10 @@ class TestLeadConvertMass(crm_common.TestLeadConvertMassCommon):
|
||||
""" 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)
|
||||
_customer, lead_1_dups = self._create_duplicates(self.lead_1, create_opp=False)
|
||||
lead_1_final = self.lead_1 # after merge: same but with lower ID
|
||||
|
||||
lead_w_partner_dups = self._create_duplicates(self.lead_w_partner, create_opp=False)
|
||||
_customer2, lead_w_partner_dups = self._create_duplicates(self.lead_w_partner, create_opp=False)
|
||||
lead_w_partner_final = lead_w_partner_dups[0] # lead_w_partner has no stage -> lower in sort by confidence
|
||||
lead_w_partner_dups_partner = lead_w_partner_dups[1] # copy with a partner_id (with the same email)
|
||||
|
||||
@@ -166,7 +166,7 @@ class TestLeadConvertMass(crm_common.TestLeadConvertMassCommon):
|
||||
test_leads = self._create_leads_batch(count=50, user_ids=[False])
|
||||
user_ids = self.assign_users.ids
|
||||
|
||||
with self.assertQueryCount(user_sales_manager=1367): # still some randomness (1366 spotted) - crm only: 1357
|
||||
with self.assertQueryCount(user_sales_manager=1368): # still some randomness (1366 spotted) - crm only: ??
|
||||
mass_convert = self.env['crm.lead2opportunity.partner.mass'].with_context({
|
||||
'active_model': 'crm.lead',
|
||||
'active_ids': test_leads.ids,
|
||||
|
||||
@@ -36,6 +36,8 @@ class TestLeadAssignPerf(TestLeadAssignCommon):
|
||||
partner_ids=[self.contact_1.id, self.contact_2.id, False, False, False],
|
||||
count=50
|
||||
)
|
||||
# commit probability and related fields
|
||||
leads.flush()
|
||||
self.assertInitialData()
|
||||
|
||||
# assign probability to leads (bypass auto probability as purpose is not to test pls)
|
||||
@@ -44,9 +46,11 @@ class TestLeadAssignPerf(TestLeadAssignCommon):
|
||||
sliced_leads = leads[idx:len(leads):5]
|
||||
for lead in sliced_leads:
|
||||
lead.probability = (idx + 1) * 10 * ((int(lead.priority) + 1) / 2)
|
||||
# commit probability and related fields
|
||||
leads.flush()
|
||||
|
||||
with self.with_user('user_sales_manager'):
|
||||
with self.assertQueryCount(user_sales_manager=477): # crm only: 466
|
||||
with self.assertQueryCount(user_sales_manager=483): # crm only: ??
|
||||
self.env['crm.team'].browse(self.sales_teams.ids)._action_assign_leads(work_days=2)
|
||||
|
||||
# teams assign
|
||||
@@ -66,19 +70,12 @@ class TestLeadAssignPerf(TestLeadAssignCommon):
|
||||
|
||||
# run a second round to finish leads
|
||||
with self.with_user('user_sales_manager'):
|
||||
with self.assertQueryCount(user_sales_manager=137): # crm only: 128
|
||||
with self.assertQueryCount(user_sales_manager=137): # crm only: ??
|
||||
self.env['crm.team'].browse(self.sales_teams.ids)._action_assign_leads(work_days=2)
|
||||
|
||||
# teams assign: everything should be done due to duplicates
|
||||
leads = self.env['crm.lead'].search([('id', 'in', leads.ids)]) # ensure order
|
||||
self.assertTrue(len(leads.filtered_domain([('team_id', '=', False)])) == 0)
|
||||
|
||||
# deduplicate should have removed all duplicated linked to contact_1 and contact_2
|
||||
new_assigned_leads_wpartner = self.env['crm.lead'].search([
|
||||
('partner_id', 'in', (self.contact_1 | self.contact_2).ids),
|
||||
('id', 'in', leads.ids)
|
||||
])
|
||||
self.assertEqual(len(new_assigned_leads_wpartner), 2)
|
||||
self.assertEqual(len(leads.filtered_domain([('team_id', '=', False)])), 0)
|
||||
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.crm.models.crm_team', 'odoo.addons.crm.models.crm_team_member')
|
||||
def test_assign_perf_no_duplicates(self):
|
||||
@@ -88,6 +85,8 @@ class TestLeadAssignPerf(TestLeadAssignCommon):
|
||||
partner_ids=[False],
|
||||
count=50
|
||||
)
|
||||
# commit probability and related fields
|
||||
leads.flush()
|
||||
self.assertInitialData()
|
||||
|
||||
# assign probability to leads (bypass auto probability as purpose is not to test pls)
|
||||
@@ -98,7 +97,7 @@ class TestLeadAssignPerf(TestLeadAssignCommon):
|
||||
lead.probability = (idx + 1) * 10 * ((int(lead.priority) + 1) / 2)
|
||||
|
||||
with self.with_user('user_sales_manager'):
|
||||
with self.assertQueryCount(user_sales_manager=211): # crm only: 206
|
||||
with self.assertQueryCount(user_sales_manager=209): # crm only: 209 (seems reproducible)
|
||||
self.env['crm.team'].browse(self.sales_teams.ids)._action_assign_leads(work_days=2)
|
||||
|
||||
# teams assign
|
||||
@@ -129,7 +128,10 @@ class TestLeadAssignPerf(TestLeadAssignCommon):
|
||||
country_ids=[self.env.ref('base.be').id, self.env.ref('base.fr').id, False],
|
||||
count=_lead_count,
|
||||
email_dup_count=_email_dup_count)
|
||||
# commit probability and related fields
|
||||
leads.flush()
|
||||
self.assertInitialData()
|
||||
|
||||
# assign for one month, aka a lot
|
||||
self.env.ref('crm.ir_cron_crm_lead_assign').write({'interval_type': 'days', 'interval_number': 30})
|
||||
self.env['ir.config_parameter'].set_param('crm.assignment.bundle', '20')
|
||||
@@ -172,9 +174,11 @@ class TestLeadAssignPerf(TestLeadAssignCommon):
|
||||
sliced_leads = leads[idx:len(leads):5]
|
||||
for lead in sliced_leads:
|
||||
lead.probability = (idx + 1) * 10 * ((int(lead.priority) + 1) / 2)
|
||||
# commit probability and related fields
|
||||
leads.flush()
|
||||
|
||||
with self.with_user('user_sales_manager'):
|
||||
with self.assertQueryCount(user_sales_manager=6290): # crm only: 6287
|
||||
with self.assertQueryCount(user_sales_manager=6293): # crm only: ??
|
||||
self.env['crm.team'].browse(sales_teams.ids)._action_assign_leads(work_days=30)
|
||||
|
||||
self.members.invalidate_cache(fnames=['lead_month_count'])
|
||||
|
||||
Reference in New Issue
Block a user