mirror of
https://github.com/bringout/oca-ocb-crm.git
synced 2026-04-23 14:52:02 +02:00
19.0 vanilla
This commit is contained in:
parent
dc68f80d3f
commit
7221b9ac46
610 changed files with 135477 additions and 161677 deletions
|
|
@ -5,12 +5,14 @@ from datetime import datetime
|
|||
from freezegun import freeze_time
|
||||
from unittest.mock import patch
|
||||
|
||||
from odoo import fields
|
||||
from odoo.addons.base.tests.test_format_address_mixin import FormatAddressCase
|
||||
from odoo.addons.crm.models.crm_lead import PARTNER_FIELDS_TO_SYNC, PARTNER_ADDRESS_FIELDS_TO_SYNC
|
||||
from odoo.addons.crm.tests.common import TestCrmCommon, INCOMING_EMAIL
|
||||
from odoo.addons.mail.tests.common_tracking import MailTrackingDurationMixinCase
|
||||
from odoo.addons.phone_validation.tools.phone_validation import phone_format
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tests.common import Form, tagged, users
|
||||
from odoo.tests import Form, tagged, users
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
|
||||
|
|
@ -76,6 +78,37 @@ class TestCRMLead(TestCrmCommon):
|
|||
self.assertEqual(self.contact_company_1.lang, self.lang_en.code)
|
||||
self.assertEqual(lead.lang_id, self.lang_en)
|
||||
|
||||
@users('user_sales_leads')
|
||||
def test_crm_lead_compute_commercial_partner(self):
|
||||
company_partner, child_partner, orphan_partner = self.env['res.partner'].create([
|
||||
{
|
||||
'name': 'test_crm_lead_compute_commercial_partner',
|
||||
'is_company': True,
|
||||
'email': 'test_crm_lead_compute_commercial_partner@test.lan',
|
||||
},
|
||||
{'name': 'Test Child'},
|
||||
{'name': 'Test Orphan'},
|
||||
])
|
||||
child_partner.parent_id = company_partner
|
||||
lead = self.env['crm.lead'].create({
|
||||
'name': 'Test Lead',
|
||||
'partner_name': 'test_crm_lead_compute_commercial_partner',
|
||||
})
|
||||
self.assertEqual(lead.commercial_partner_id, company_partner)
|
||||
lead.partner_id = orphan_partner
|
||||
self.assertFalse(lead.commercial_partner_id)
|
||||
lead.partner_id = child_partner
|
||||
self.assertEqual(lead.commercial_partner_id, company_partner)
|
||||
lead.write({
|
||||
'partner_id': False,
|
||||
'partner_name': False,
|
||||
})
|
||||
self.assertFalse(lead.commercial_partner_id)
|
||||
lead.partner_id = company_partner
|
||||
# this is mostly because we use it to set "parent_id" in most flows
|
||||
# and it doesn't really make sense to have it be its own parent
|
||||
self.assertFalse(lead.commercial_partner_id, "If a partner is its own commercial_partner_id, the lead is considered to have none.")
|
||||
|
||||
@users('user_sales_leads')
|
||||
def test_crm_lead_creation_no_partner(self):
|
||||
lead_data = {
|
||||
|
|
@ -184,8 +217,7 @@ class TestCRMLead(TestCrmCommon):
|
|||
'name': 'Empty partner',
|
||||
'is_company': True,
|
||||
'lang': 'en_US',
|
||||
'mobile': '123456789',
|
||||
'title': self.env.ref('base.res_partner_title_mister').id,
|
||||
'phone': '0485112233',
|
||||
'function': 'My function',
|
||||
})
|
||||
lead_data = {
|
||||
|
|
@ -195,7 +227,6 @@ class TestCRMLead(TestCrmCommon):
|
|||
'country_id': self.country_ref.id,
|
||||
'email_from': self.test_email,
|
||||
'phone': self.test_phone,
|
||||
'mobile': '987654321',
|
||||
'website': 'http://mywebsite.org',
|
||||
}
|
||||
lead = self.env['crm.lead'].create(lead_data)
|
||||
|
|
@ -214,8 +245,6 @@ class TestCRMLead(TestCrmCommon):
|
|||
# PARTNER_FIELDS_TO_SYNC
|
||||
self.assertEqual(lead.lang_id, self.lang_en)
|
||||
self.assertEqual(lead.phone, lead_data['phone'], "Phone should keep its initial value")
|
||||
self.assertEqual(lead.mobile, empty_partner.mobile, "Mobile from partner should be set on the lead")
|
||||
self.assertEqual(lead.title, empty_partner.title, "Title from partner should be set on the lead")
|
||||
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")
|
||||
|
||||
|
|
@ -247,27 +276,43 @@ class TestCRMLead(TestCrmCommon):
|
|||
|
||||
@users('user_sales_manager')
|
||||
def test_crm_lead_currency_sync(self):
|
||||
lead = self.env['crm.lead'].create({
|
||||
lead_company = self.env['res.company'].sudo().create({
|
||||
'name': 'EUR company',
|
||||
'currency_id': self.env.ref('base.EUR').id,
|
||||
})
|
||||
lead = self.env['crm.lead'].with_company(lead_company).create({
|
||||
'name': 'Lead 1',
|
||||
'company_id': self.company_main.id
|
||||
'company_id': lead_company.id
|
||||
})
|
||||
self.assertEqual(lead.company_currency, self.env.ref('base.EUR'))
|
||||
|
||||
self.company_main.currency_id = self.env.ref('base.CHF')
|
||||
lead.with_company(self.company_main).update({'company_id': False})
|
||||
lead_company.currency_id = self.env.ref('base.CHF')
|
||||
lead.update({'company_id': False})
|
||||
self.assertEqual(lead.company_currency, self.env.ref('base.CHF'))
|
||||
#set back original currency
|
||||
self.company_main.currency_id = self.env.ref('base.EUR')
|
||||
|
||||
@users('user_sales_manager')
|
||||
def test_crm_lead_date_closed(self):
|
||||
# ensure a lead created directly in a won stage gets a date_closed
|
||||
lead_in_won = self.env['crm.lead'].create({
|
||||
'name': 'Created in Won',
|
||||
'type': 'opportunity',
|
||||
'stage_id': self.stage_team1_won.id,
|
||||
'expected_revenue': 123.45,
|
||||
})
|
||||
# date_closed must be set at creation when stage is won
|
||||
self.assertTrue(lead_in_won.date_closed, "Lead created in a won stage must have date_closed set")
|
||||
self.assertIsInstance(lead_in_won.date_closed, datetime)
|
||||
# Test for one won lead
|
||||
stage_team1_won2 = self.env['crm.stage'].create({
|
||||
'name': 'Won2',
|
||||
'sequence': 75,
|
||||
'team_id': self.sales_team_1.id,
|
||||
'team_ids': [self.sales_team_1.id],
|
||||
'is_won': True,
|
||||
})
|
||||
old_date_closed = lead_in_won.date_closed
|
||||
with freeze_time('2020-02-02 18:00'):
|
||||
lead_in_won.stage_id = stage_team1_won2
|
||||
self.assertEqual(lead_in_won.date_closed, old_date_closed, 'Moving between won stages should not change existing date_closed')
|
||||
won_lead = self.lead_team_1_won.with_env(self.env)
|
||||
other_lead = self.lead_1.with_env(self.env)
|
||||
old_date_closed = won_lead.date_closed
|
||||
|
|
@ -301,6 +346,36 @@ class TestCRMLead(TestCrmCommon):
|
|||
lead.action_set_lost()
|
||||
self.assertEqual(lead.date_closed, datetime.now(), "Closed date is updated after marking lead as lost")
|
||||
|
||||
@users('user_sales_manager')
|
||||
def test_crm_lead_meeting_display_fields(self):
|
||||
lead = self.env['crm.lead'].create({'name': 'Lead With Meetings'})
|
||||
meeting_1, meeting_2, meeting_3 = self.env['calendar.event'].create([{
|
||||
'name': 'Meeting 1 of Lead',
|
||||
'opportunity_id': lead.id,
|
||||
'start': '2022-07-12 08:00:00',
|
||||
'stop': '2022-07-12 10:00:00',
|
||||
}, {
|
||||
'name': 'Meeting 2 of Lead',
|
||||
'opportunity_id': lead.id,
|
||||
'start': '2022-07-14 08:00:00',
|
||||
'stop': '2022-07-14 10:00:00',
|
||||
}, {
|
||||
'name': 'Meeting 3 of Lead',
|
||||
'opportunity_id': lead.id,
|
||||
'start': '2022-07-15 08:00:00',
|
||||
'stop': '2022-07-15 10:00:00',
|
||||
}])
|
||||
|
||||
with freeze_time('2022-07-13 11:00:00'):
|
||||
self.assertEqual(lead.meeting_display_date, fields.Date.from_string('2022-07-14'))
|
||||
self.assertEqual(lead.meeting_display_label, 'Next Meeting')
|
||||
(meeting_2 | meeting_3).unlink()
|
||||
self.assertEqual(lead.meeting_display_date, fields.Date.from_string('2022-07-12'))
|
||||
self.assertEqual(lead.meeting_display_label, 'Last Meeting')
|
||||
meeting_1.unlink()
|
||||
self.assertFalse(lead.meeting_display_date)
|
||||
self.assertEqual(lead.meeting_display_label, 'No Meeting')
|
||||
|
||||
@users('user_sales_manager')
|
||||
def test_crm_lead_partner_sync(self):
|
||||
lead, partner = self.lead_1.with_user(self.env.user), self.contact_2
|
||||
|
|
@ -325,11 +400,13 @@ class TestCRMLead(TestCrmCommon):
|
|||
self.assertEqual(lead.email_from, partner_email)
|
||||
self.assertEqual(lead.phone, '+1 202 555 6666')
|
||||
|
||||
# resetting lead values also resets partner
|
||||
# resetting lead values should not reset partner: voiding lead info (because
|
||||
# of some reasons) should not prevent from using the contact in other records
|
||||
lead.email_from, lead.phone = False, False
|
||||
self.assertFalse(partner.email)
|
||||
self.assertFalse(partner.email_normalized)
|
||||
self.assertFalse(partner.phone)
|
||||
self.assertFalse(lead.email_from)
|
||||
self.assertFalse(lead.phone)
|
||||
self.assertEqual(partner.email, partner_email)
|
||||
self.assertEqual(partner.phone, '+1 202 555 6666')
|
||||
|
||||
@users('user_sales_manager')
|
||||
def test_crm_lead_partner_sync_email_phone(self):
|
||||
|
|
@ -345,19 +422,14 @@ class TestCRMLead(TestCrmCommon):
|
|||
lead_form = Form(lead)
|
||||
|
||||
# reset partner phone to a local number and prepare formatted / sanitized values
|
||||
partner_phone, partner_mobile = self.test_phone_data[2], self.test_phone_data[1]
|
||||
partner_phone = self.test_phone_data[2]
|
||||
partner_phone_formatted = phone_format(partner_phone, 'US', '1', force_format='INTERNATIONAL')
|
||||
partner_phone_sanitized = phone_format(partner_phone, 'US', '1', force_format='E164')
|
||||
partner_mobile_formatted = phone_format(partner_mobile, 'US', '1', force_format='INTERNATIONAL')
|
||||
partner_mobile_sanitized = phone_format(partner_mobile, 'US', '1', force_format='E164')
|
||||
partner_email, partner_email_normalized = self.test_email_data[2], self.test_email_data_normalized[2]
|
||||
self.assertEqual(partner_phone_formatted, '+1 202-555-0888')
|
||||
self.assertEqual(partner_phone_sanitized, self.test_phone_data_sanitized[2])
|
||||
self.assertEqual(partner_mobile_formatted, '+1 202-555-0999')
|
||||
self.assertEqual(partner_mobile_sanitized, self.test_phone_data_sanitized[1])
|
||||
# ensure initial data
|
||||
self.assertEqual(partner.phone, partner_phone)
|
||||
self.assertEqual(partner.mobile, partner_mobile)
|
||||
self.assertEqual(partner.email, partner_email)
|
||||
|
||||
# LEAD/PARTNER SYNC: email and phone are propagated to lead
|
||||
|
|
@ -366,8 +438,6 @@ class TestCRMLead(TestCrmCommon):
|
|||
self.assertEqual(lead_form.email_from, partner_email)
|
||||
self.assertEqual(lead_form.phone, partner_phone_formatted,
|
||||
'Lead: form automatically formats numbers')
|
||||
self.assertEqual(lead_form.mobile, partner_mobile_formatted,
|
||||
'Lead: form automatically formats numbers')
|
||||
self.assertFalse(lead_form.partner_email_update)
|
||||
self.assertFalse(lead_form.partner_phone_update)
|
||||
|
||||
|
|
@ -380,9 +450,7 @@ class TestCRMLead(TestCrmCommon):
|
|||
'Lead / Partner: equal emails should lead to equal normalized emails')
|
||||
self.assertEqual(lead.phone, partner_phone_formatted,
|
||||
'Lead / Partner: partner values (formatted) sent to lead')
|
||||
self.assertEqual(lead.mobile, partner_mobile_formatted,
|
||||
'Lead / Partner: partner values (formatted) sent to lead')
|
||||
self.assertEqual(lead.phone_sanitized, partner_mobile_sanitized,
|
||||
self.assertEqual(lead.phone_sanitized, partner_phone_sanitized,
|
||||
'Lead: phone_sanitized computed field on mobile')
|
||||
|
||||
# for email_from, if only formatting differs, warning should not appear and
|
||||
|
|
@ -406,6 +474,7 @@ class TestCRMLead(TestCrmCommon):
|
|||
self.assertTrue(lead_form.partner_email_update)
|
||||
new_phone = '+1 202 555 7799'
|
||||
new_phone_formatted = phone_format(new_phone, 'US', '1', force_format="INTERNATIONAL")
|
||||
new_phone_sanitized = phone_format(new_phone, 'US', '1', force_format="E164")
|
||||
lead_form.phone = new_phone
|
||||
self.assertEqual(lead_form.phone, new_phone_formatted)
|
||||
self.assertTrue(lead_form.partner_email_update)
|
||||
|
|
@ -416,30 +485,21 @@ class TestCRMLead(TestCrmCommon):
|
|||
self.assertEqual(partner.email_normalized, new_email_normalized)
|
||||
self.assertEqual(partner.phone, new_phone_formatted)
|
||||
|
||||
# LEAD/PARTNER SYNC: mobile does not update partner
|
||||
new_mobile = '+1 202 555 6543'
|
||||
new_mobile_formatted = phone_format(new_mobile, 'US', '1', force_format="INTERNATIONAL")
|
||||
lead_form.mobile = new_mobile
|
||||
# LEAD/PARTNER SYNC: resetting lead values should not reset partner
|
||||
# # voiding lead info (because of some reasons) should not prevent
|
||||
# # from using the contact in other records
|
||||
lead_form.email_from, lead_form.phone = False, False
|
||||
self.assertFalse(lead_form.partner_email_update)
|
||||
self.assertFalse(lead_form.partner_phone_update)
|
||||
lead_form.save()
|
||||
self.assertEqual(lead.mobile, new_mobile_formatted)
|
||||
self.assertEqual(partner.mobile, partner_mobile)
|
||||
|
||||
# LEAD/PARTNER SYNC: reseting lead values also resets partner for email
|
||||
# and phone, but not for mobile
|
||||
lead_form.email_from, lead_form.phone, lead.mobile = False, False, False
|
||||
self.assertTrue(lead_form.partner_email_update)
|
||||
self.assertTrue(lead_form.partner_phone_update)
|
||||
lead_form.save()
|
||||
self.assertFalse(partner.email)
|
||||
self.assertFalse(partner.email_normalized)
|
||||
self.assertFalse(partner.phone)
|
||||
self.assertEqual(partner.email, new_email)
|
||||
self.assertEqual(partner.email_normalized, new_email_normalized)
|
||||
self.assertEqual(partner.phone, new_phone_formatted)
|
||||
self.assertFalse(lead.phone)
|
||||
self.assertFalse(lead.mobile)
|
||||
self.assertFalse(lead.phone_sanitized)
|
||||
self.assertEqual(partner.mobile, partner_mobile)
|
||||
# if SMS is uninstalled, phone_sanitized is not available on partner
|
||||
if 'phone_sanitized' in partner:
|
||||
self.assertEqual(partner.phone_sanitized, partner_mobile_sanitized,
|
||||
self.assertEqual(partner.phone_sanitized, new_phone_sanitized,
|
||||
'Partner sanitized should be computed on mobile')
|
||||
|
||||
@users('user_sales_manager')
|
||||
|
|
@ -453,7 +513,6 @@ class TestCRMLead(TestCrmCommon):
|
|||
'name': 'NoContact Partner',
|
||||
'phone': '',
|
||||
'email': '',
|
||||
'mobile': '',
|
||||
})
|
||||
|
||||
# This is a type == 'lead', not a type == 'opportunity'
|
||||
|
|
@ -550,6 +609,41 @@ class TestCRMLead(TestCrmCommon):
|
|||
self.assertEqual(lead.probability, 100.0)
|
||||
self.assertEqual(lead.stage_id, self.stage_gen_won) # generic won stage has lower sequence than team won stage
|
||||
|
||||
def test_crm_lead_stages_with_multiple_possible_teams(self):
|
||||
""" Test lead stage is properly set when switching between multiple teams. """
|
||||
self.sales_team_2 = self.env['crm.team'].create({
|
||||
'name': 'Test Sales Team 2',
|
||||
'company_id': False,
|
||||
'user_id': self.user_sales_manager.id,
|
||||
})
|
||||
self.sales_team_2_m1 = self.env['crm.team.member'].create({
|
||||
'user_id': self.user_sales_leads.id,
|
||||
'crm_team_id': self.sales_team_2.id,
|
||||
})
|
||||
|
||||
user_teams = self.env['crm.team'].search([
|
||||
('crm_team_member_all_ids.user_id', '=', self.user_sales_leads.id),
|
||||
])
|
||||
self.assertIn(self.sales_team_1, user_teams)
|
||||
self.assertIn(self.sales_team_2, user_teams)
|
||||
|
||||
self.stage_team2_1 = self.env['crm.stage'].create({
|
||||
'name': 'New (T2)',
|
||||
'team_ids': [self.sales_team_2.id],
|
||||
})
|
||||
|
||||
lead = self.env['crm.lead'].with_user(self.user_sales_leads).create({
|
||||
'name': 'Test',
|
||||
'contact_name': 'Test Contact',
|
||||
'team_id': self.sales_team_1.id,
|
||||
})
|
||||
self.assertEqual(lead.team_id, self.sales_team_1)
|
||||
self.assertEqual(lead.stage_id, self.stage_team1_1)
|
||||
|
||||
lead.team_id = self.sales_team_2
|
||||
self.assertEqual(lead.team_id, self.sales_team_2)
|
||||
self.assertEqual(lead.stage_id, self.stage_team2_1)
|
||||
|
||||
@users('user_sales_manager')
|
||||
def test_crm_lead_unlink_calendar_event(self):
|
||||
""" Test res_id / res_model is reset (and hide document button in calendar
|
||||
|
|
@ -571,7 +665,7 @@ class TestCRMLead(TestCrmCommon):
|
|||
'stop': '2022-07-13 10:00:00',
|
||||
}
|
||||
])
|
||||
self.assertEqual(lead.calendar_event_count, 1)
|
||||
self.assertEqual(len(lead.calendar_event_ids), 1)
|
||||
self.assertEqual(meetings.opportunity_id, lead)
|
||||
self.assertEqual(meetings.mapped('res_id'), [lead.id, lead.id])
|
||||
self.assertEqual(meetings.mapped('res_model'), ['crm.lead', 'crm.lead'])
|
||||
|
|
@ -728,28 +822,37 @@ class TestCRMLead(TestCrmCommon):
|
|||
"""Test that the help message is the right one if we are on multiple team with different settings."""
|
||||
# archive other teams
|
||||
self.env['crm.team'].search([]).active = False
|
||||
self.env['ir.config_parameter'].sudo().set_param("sales_team.membership_multi", True)
|
||||
|
||||
self._activate_multi_company()
|
||||
team_other_comp = self.team_company2
|
||||
|
||||
user_team_leads, team_leads, user_team_opport, team_opport = self.env['crm.team'].create([{
|
||||
'name': 'UserTeamLeads',
|
||||
'company_id': self.env.company.id,
|
||||
'use_leads': True,
|
||||
'member_ids': [(6, 0, [self.env.user.id])],
|
||||
}, {
|
||||
'name': 'TeamLeads',
|
||||
'company_id': self.env.company.id,
|
||||
'use_leads': True,
|
||||
'member_ids': [],
|
||||
}, {
|
||||
'name': 'UserTeamOpportunities',
|
||||
'company_id': self.env.company.id,
|
||||
'use_leads': False,
|
||||
'member_ids': [(6, 0, [self.env.user.id])],
|
||||
}, {
|
||||
'name': 'TeamOpportunities',
|
||||
'company_id': self.env.company.id,
|
||||
'use_leads': False,
|
||||
'member_ids': [],
|
||||
}])
|
||||
|
||||
# Additional check to ensure proper team creation
|
||||
user_team_leads.invalidate_recordset(fnames=['member_ids'])
|
||||
self.assertEqual(user_team_leads.member_ids.ids, [self.env.user.id])
|
||||
|
||||
self.env['crm.lead'].create([{
|
||||
'name': 'LeadOurTeam',
|
||||
'team_id': user_team_leads.id,
|
||||
|
|
@ -781,11 +884,10 @@ class TestCRMLead(TestCrmCommon):
|
|||
|
||||
for team in teams:
|
||||
with self.subTest(team=team):
|
||||
team_mail = f"{team.alias_name}@{team.alias_domain}"
|
||||
if team != team_other_comp:
|
||||
self.assertIn(f"<a href='mailto:{team_mail}'>{team_mail}</a>", self.env['crm.lead'].sudo().get_empty_list_help(""))
|
||||
self.assertIn(f"<a href='mailto:{team.alias_email}'>{team.alias_email}</a>", self.env['crm.lead'].sudo().get_empty_list_help(""))
|
||||
else:
|
||||
self.assertNotIn(f"<a href='mailto:{team_mail}'>{team_mail}</a>", self.env['crm.lead'].sudo().get_empty_list_help(""))
|
||||
self.assertNotIn(f"<a href='mailto:{team.alias_email}'>{team.alias_email}</a>", self.env['crm.lead'].sudo().get_empty_list_help(""))
|
||||
team.active = False
|
||||
|
||||
@mute_logger('odoo.addons.mail.models.mail_thread')
|
||||
|
|
@ -793,7 +895,7 @@ class TestCRMLead(TestCrmCommon):
|
|||
new_lead = self.format_and_process(
|
||||
INCOMING_EMAIL,
|
||||
'unknown.sender@test.example.com',
|
||||
'%s@%s' % (self.sales_team_1.alias_name, self.alias_domain),
|
||||
self.sales_team_1.alias_email,
|
||||
subject='Delivery cost inquiry',
|
||||
target_model='crm.lead',
|
||||
)
|
||||
|
|
@ -802,49 +904,12 @@ class TestCRMLead(TestCrmCommon):
|
|||
self.assertEqual(new_lead.name, 'Delivery cost inquiry')
|
||||
|
||||
message = new_lead.with_user(self.user_sales_manager).message_post(
|
||||
body='Here is my offer !',
|
||||
body='Here is my offer!',
|
||||
subtype_xmlid='mail.mt_comment')
|
||||
self.assertEqual(message.author_id, self.user_sales_manager.partner_id)
|
||||
|
||||
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)
|
||||
|
||||
@users('user_sales_manager')
|
||||
def test_message_get_suggested_recipients(self):
|
||||
"""This test checks that creating a contact from a lead with an inactive language will ignore the language
|
||||
while creating a contact from a lead with an active language will take it into account """
|
||||
ResLang = self.env['res.lang'].sudo().with_context(active_test=False)
|
||||
|
||||
# Create a lead with an inactive language -> should ignore the preset language
|
||||
lang_fr = ResLang.search([('code', '=', 'fr_FR')])
|
||||
if not lang_fr:
|
||||
lang_fr = ResLang._create_lang('fr_FR')
|
||||
# set French language as inactive then try to call "_message_get_suggested_recipients"
|
||||
# -> lang code should be ignored
|
||||
lang_fr.active = False
|
||||
lead1 = self.env['crm.lead'].create({
|
||||
'name': 'TestLead',
|
||||
'email_from': self.test_email,
|
||||
'lang_id': lang_fr.id,
|
||||
})
|
||||
data = lead1._message_get_suggested_recipients()[lead1.id]
|
||||
self.assertEqual(data, [(False, self.test_email, None, 'Customer Email')])
|
||||
|
||||
# Create a lead with an active language -> should keep the preset language for recipients
|
||||
lang_en = ResLang.search([('code', '=', 'en_US')])
|
||||
if not lang_en:
|
||||
lang_en = ResLang._create_lang('en_US')
|
||||
# set American English language as active then try to call "_message_get_suggested_recipients"
|
||||
# -> lang code should be kept
|
||||
lang_en.active = True
|
||||
lead2 = self.env['crm.lead'].create({
|
||||
'name': 'TestLead',
|
||||
'email_from': self.test_email,
|
||||
'lang_id': lang_en.id,
|
||||
})
|
||||
data = lead2._message_get_suggested_recipients()[lead2.id]
|
||||
self.assertEqual(data, [(False, self.test_email, "en_US", 'Customer Email')])
|
||||
|
||||
@users('user_sales_manager')
|
||||
def test_phone_mobile_search(self):
|
||||
|
|
@ -870,10 +935,9 @@ class TestCRMLead(TestCrmCommon):
|
|||
})
|
||||
|
||||
# search term containing less than 3 characters should throw an error (some currently not working)
|
||||
self.env['crm.lead'].search([('phone_mobile_search', 'like', '')]) # no restriction, returns all
|
||||
with self.assertRaises(UserError):
|
||||
self.env['crm.lead'].search([('phone_mobile_search', 'like', '')])
|
||||
# with self.assertRaises(UserError):
|
||||
# self.env['crm.lead'].search([('phone_mobile_search', 'like', '7 ')])
|
||||
self.env['crm.lead'].search([('phone_mobile_search', 'like', '7 ')])
|
||||
with self.assertRaises(UserError):
|
||||
self.env['crm.lead'].search([('phone_mobile_search', 'like', 'c')])
|
||||
with self.assertRaises(UserError):
|
||||
|
|
@ -960,27 +1024,193 @@ class TestCRMLead(TestCrmCommon):
|
|||
'phone': self.test_phone_data[0],
|
||||
})
|
||||
self.assertEqual(lead.phone, self.test_phone_data[0])
|
||||
self.assertFalse(lead.mobile)
|
||||
self.assertEqual(lead.phone_sanitized, self.test_phone_data_sanitized[0])
|
||||
|
||||
lead.write({'phone': False, 'mobile': self.test_phone_data[1]})
|
||||
lead.write({'phone': False})
|
||||
self.assertFalse(lead.phone)
|
||||
self.assertEqual(lead.mobile, self.test_phone_data[1])
|
||||
self.assertEqual(lead.phone_sanitized, self.test_phone_data_sanitized[1])
|
||||
self.assertEqual(lead.phone_sanitized, False)
|
||||
|
||||
lead.write({'phone': self.test_phone_data[1], 'mobile': self.test_phone_data[2]})
|
||||
lead.write({'phone': self.test_phone_data[1]})
|
||||
self.assertEqual(lead.phone, self.test_phone_data[1])
|
||||
self.assertEqual(lead.mobile, self.test_phone_data[2])
|
||||
self.assertEqual(lead.phone_sanitized, self.test_phone_data_sanitized[2])
|
||||
self.assertEqual(lead.phone_sanitized, self.test_phone_data_sanitized[1])
|
||||
|
||||
# updating country should trigger sanitize computation
|
||||
lead.write({'country_id': self.env.ref('base.be').id})
|
||||
self.assertEqual(lead.phone, self.test_phone_data[1])
|
||||
self.assertEqual(lead.mobile, self.test_phone_data[2])
|
||||
self.assertFalse(lead.phone_sanitized)
|
||||
|
||||
|
||||
class TestLeadFormatAddress(FormatAddressCase):
|
||||
class TestCRMLeadRotting(TestCrmCommon):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.stage_team1_1.rotting_threshold_days = 5
|
||||
cls.stage_team1_2.rotting_threshold_days = 3
|
||||
|
||||
@users('user_sales_manager')
|
||||
def test_leads_rotting(self):
|
||||
rotten_leads = self.env['crm.lead']
|
||||
clean_leads = self.env['crm.lead']
|
||||
|
||||
close_future = datetime(2025, 1, 24, 12, 0, 0)
|
||||
now = datetime(2025, 1, 20, 12, 0, 0)
|
||||
close_past = datetime(2025, 1, 18, 12, 0, 0)
|
||||
past = datetime(2025, 1, 10, 12, 0, 0)
|
||||
last_year = datetime(2024, 1, 20, 12, 0, 0)
|
||||
|
||||
with self.mock_datetime_and_now(past):
|
||||
rotten_leads += self.env['crm.lead'].create([
|
||||
{
|
||||
'name': 'Opportunity',
|
||||
'type': 'opportunity',
|
||||
'stage_id': self.stage_team1_1.id,
|
||||
} for x in range(3)
|
||||
])
|
||||
rotten_leads.flush_recordset(['date_last_stage_update']) # precalculate stage update
|
||||
|
||||
with self.mock_datetime_and_now(close_past):
|
||||
clean_leads += self.env['crm.lead'].create({
|
||||
'name': "Lead that won't have time to rot",
|
||||
'type': 'opportunity',
|
||||
'stage_id': self.stage_team1_1.id,
|
||||
})
|
||||
clean_leads.flush_recordset(['date_last_stage_update']) # precalculate stage update
|
||||
with self.mock_datetime_and_now(last_year):
|
||||
clean_leads += self.env['crm.lead'].create({
|
||||
'name': 'Opportuniy in Won Stage',
|
||||
'type': 'opportunity',
|
||||
'stage_id': self.stage_gen_won.id,
|
||||
})
|
||||
clean_leads.flush_recordset(['date_last_stage_update']) # precalculate stage update
|
||||
|
||||
with self.mock_datetime_and_now(now):
|
||||
for lead in rotten_leads:
|
||||
self.assertTrue(lead.is_rotting)
|
||||
self.assertEqual(lead.rotting_days, 10)
|
||||
for lead in clean_leads:
|
||||
self.assertFalse(lead.is_rotting)
|
||||
self.assertEqual(lead.rotting_days, 0)
|
||||
|
||||
rotten_leads_iterator = iter(rotten_leads)
|
||||
|
||||
lead_edited = next(rotten_leads_iterator)
|
||||
lead_edited.name = 'Edited Opportunity'
|
||||
self.assertTrue(
|
||||
lead_edited.is_rotting,
|
||||
'Editing the lead has no effect on rotting status',
|
||||
)
|
||||
|
||||
lead_changed_stage = next(rotten_leads_iterator)
|
||||
lead_changed_stage.stage_id = self.stage_team1_2.id
|
||||
self.assertFalse(
|
||||
lead_changed_stage.is_rotting,
|
||||
'Changing the stage disables rotting status',
|
||||
)
|
||||
|
||||
lead_changed_rotting_threshold = next(rotten_leads_iterator)
|
||||
old_rotting_threshold = self.stage_team1_1.rotting_threshold_days
|
||||
self.stage_team1_1.rotting_threshold_days = 50
|
||||
self.assertFalse(
|
||||
lead_changed_rotting_threshold.is_rotting,
|
||||
'Changing the rotting threshold to a higher value does affect rotten leads\' status',
|
||||
)
|
||||
self.stage_team1_1.rotting_threshold_days = old_rotting_threshold # Revert rotting threshold
|
||||
self.assertTrue(
|
||||
lead_changed_rotting_threshold.is_rotting,
|
||||
'Changing the threshold back should affect the status again',
|
||||
)
|
||||
|
||||
self.stage_team1_1.rotting_threshold_days = 0
|
||||
self.assertFalse(
|
||||
lead_changed_rotting_threshold.is_rotting,
|
||||
'A 0-day rotting threshold disables rotting',
|
||||
)
|
||||
self.stage_team1_1.rotting_threshold_days = old_rotting_threshold
|
||||
|
||||
# create a new lead in the New stage
|
||||
jan20_lead = self.env['crm.lead'].create({
|
||||
'name': 'Fresh Opportuniy',
|
||||
'type': 'opportunity',
|
||||
'stage_id': self.stage_team1_1.id,
|
||||
})
|
||||
|
||||
# 4 days later:
|
||||
with self.mock_datetime_and_now(close_future):
|
||||
rotten_leads.invalidate_recordset(['is_rotting', 'rotting_days'])
|
||||
self.assertEqual(
|
||||
lead_changed_rotting_threshold.rotting_days,
|
||||
14,
|
||||
'Since this lead has not seen a stage change, it has been rotting for 14 days total',
|
||||
)
|
||||
self.assertFalse(
|
||||
jan20_lead.is_rotting,
|
||||
'Since this lead remained in a stage with a higher threshold, it\'s not rotting yet',
|
||||
)
|
||||
self.assertTrue(
|
||||
lead_changed_stage.is_rotting,
|
||||
'As its new stage has a lower rotting threshold, this lead should be rotting 3 days after its last stage change',
|
||||
)
|
||||
self.assertEqual(lead_changed_stage.rotting_days, 4)
|
||||
|
||||
def test_search_leads_rotting(self):
|
||||
"""
|
||||
This test checks that the result of search_leads_rotting accurately matches is_rotting computation results
|
||||
"""
|
||||
past = datetime(2025, 1, 1)
|
||||
now = datetime(2025, 1, 10)
|
||||
with self.mock_datetime_and_now(past):
|
||||
all_leads = self.env['crm.lead'].create([{
|
||||
'name': 'TestLead Rotting opportunity',
|
||||
'type': 'opportunity',
|
||||
'stage_id': self.stage_team1_1.id,
|
||||
}] * 5 + [{
|
||||
'name': 'TestLead Lead',
|
||||
'type': 'lead',
|
||||
'stage_id': self.stage_team1_1.id,
|
||||
}] * 3 + [{
|
||||
'name': 'TestLead Won Opportunity',
|
||||
'type': 'opportunity',
|
||||
'stage_id': self.stage_gen_won.id,
|
||||
}] * 4)
|
||||
|
||||
all_leads.flush_recordset(['date_last_stage_update'])
|
||||
rotten_leads = all_leads.filtered(lambda lead: 'Rotting' in lead.name)
|
||||
clean_leads = all_leads - rotten_leads
|
||||
|
||||
with self.mock_datetime_and_now(now):
|
||||
rot = self.env['crm.lead'].search([
|
||||
('name', 'ilike', 'TestLead'),
|
||||
('is_rotting', '=', True),
|
||||
], order='id ASC')
|
||||
norot = self.env['crm.lead'].search([
|
||||
('name', 'ilike', 'TestLead'),
|
||||
('is_rotting', '=', False),
|
||||
], order='id ASC')
|
||||
|
||||
self.assertEqual(rot, rotten_leads)
|
||||
self.assertEqual(norot, clean_leads)
|
||||
|
||||
|
||||
@tagged('lead_internals')
|
||||
class TestLeadFormTools(FormatAddressCase):
|
||||
|
||||
def test_address_view(self):
|
||||
self.env.company.country_id = self.env.ref('base.us')
|
||||
self.assertAddressView('crm.lead')
|
||||
|
||||
|
||||
@tagged('lead_internals', 'is_query_count')
|
||||
class TestCrmLeadMailTrackingDuration(MailTrackingDurationMixinCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass('crm.lead')
|
||||
|
||||
def test_crm_lead_mail_tracking_duration(self):
|
||||
self._test_record_duration_tracking()
|
||||
|
||||
def test_crm_lead_mail_tracking_duration_batch(self):
|
||||
self._test_record_duration_tracking_batch()
|
||||
|
||||
def test_crm_lead_queries_batch_mail_tracking_duration(self):
|
||||
self._test_queries_batch_duration_tracking()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue