diff options
Diffstat (limited to 'addons/crm/tests/test_crm_lead.py')
| -rw-r--r-- | addons/crm/tests/test_crm_lead.py | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/addons/crm/tests/test_crm_lead.py b/addons/crm/tests/test_crm_lead.py new file mode 100644 index 00000000..f0a26716 --- /dev/null +++ b/addons/crm/tests/test_crm_lead.py @@ -0,0 +1,487 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +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.phone_validation.tools.phone_validation import phone_format +from odoo.tests.common import Form, users + + +class TestCRMLead(TestCrmCommon): + + @classmethod + def setUpClass(cls): + super(TestCRMLead, cls).setUpClass() + cls.country_ref = cls.env.ref('base.be') + cls.test_email = '"Test Email" <test.email@example.com>' + cls.test_phone = '0485112233' + + def assertLeadAddress(self, lead, street, street2, city, lead_zip, state, country): + self.assertEqual(lead.street, street) + self.assertEqual(lead.street2, street2) + self.assertEqual(lead.city, city) + self.assertEqual(lead.zip, lead_zip) + self.assertEqual(lead.state_id, state) + self.assertEqual(lead.country_id, country) + + @users('user_sales_leads') + def test_crm_lead_contact_fields_mixed(self): + """ Test mixed configuration from partner: both user input and coming + from partner, in order to ensure we do not loose information or make + it incoherent. """ + lead_data = { + 'name': 'TestMixed', + 'partner_id': self.contact_1.id, + # address + 'country_id': self.country_ref.id, + # other contact fields + 'function': 'Parmesan Rappeur', + # specific contact fields + 'email_from': self.test_email, + 'phone': self.test_phone, + } + lead = self.env['crm.lead'].create(lead_data) + # classic + self.assertEqual(lead.name, "TestMixed") + # address + self.assertLeadAddress(lead, False, False, False, False, self.env['res.country.state'], self.country_ref) + # other contact fields + for fname in set(PARTNER_FIELDS_TO_SYNC) - set(['function']): + self.assertEqual(lead[fname], self.contact_1[fname], 'No user input -> take from contact for field %s' % fname) + self.assertEqual(lead.function, 'Parmesan Rappeur', 'User input should take over partner value') + # specific contact fields + self.assertEqual(lead.partner_name, self.contact_company_1.name) + self.assertEqual(lead.contact_name, self.contact_1.name) + self.assertEqual(lead.email_from, self.test_email) + self.assertEqual(lead.phone, self.test_phone) + + # update a single address fields -> only those are updated + lead.write({'street': 'Super Street', 'city': 'Super City'}) + self.assertLeadAddress(lead, 'Super Street', False, 'Super City', False, self.env['res.country.state'], self.country_ref) + + # change partner -> whole address updated + lead.write({'partner_id': self.contact_company_1.id}) + for fname in PARTNER_ADDRESS_FIELDS_TO_SYNC: + self.assertEqual(lead[fname], self.contact_company_1[fname]) + + @users('user_sales_leads') + def test_crm_lead_creation_no_partner(self): + lead_data = { + 'name': 'Test', + 'country_id': self.country_ref.id, + 'email_from': self.test_email, + 'phone': self.test_phone, + } + lead = self.env['crm.lead'].new(lead_data) + # get the street should not trigger cache miss + lead.street + # Create the lead and the write partner_id = False: country should remain + lead = self.env['crm.lead'].create(lead_data) + self.assertEqual(lead.country_id, self.country_ref, "Country should be set on the lead") + self.assertEqual(lead.email_from, self.test_email) + self.assertEqual(lead.phone, self.test_phone) + lead.partner_id = False + self.assertEqual(lead.country_id, self.country_ref, "Country should still be set on the lead") + self.assertEqual(lead.email_from, self.test_email) + self.assertEqual(lead.phone, self.test_phone) + + @users('user_sales_manager') + def test_crm_lead_creation_partner(self): + lead = self.env['crm.lead'].create({ + 'name': 'TestLead', + 'contact_name': 'Raoulette TestContact', + 'email_from': '"Raoulette TestContact" <raoulette@test.example.com>', + }) + self.assertEqual(lead.type, 'lead') + 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) + self.assertEqual(lead.contact_name, 'Raoulette TestContact') + self.assertEqual(lead.email_from, '"Raoulette TestContact" <raoulette@test.example.com>') + + # update to a partner, should udpate address + lead.write({'partner_id': self.contact_1.id}) + self.assertEqual(lead.partner_name, self.contact_company_1.name) + self.assertEqual(lead.contact_name, self.contact_1.name) + self.assertEqual(lead.email_from, self.contact_1.email) + self.assertEqual(lead.street, self.contact_1.street) + self.assertEqual(lead.city, self.contact_1.city) + self.assertEqual(lead.zip, self.contact_1.zip) + self.assertEqual(lead.country_id, self.contact_1.country_id) + + def test_crm_lead_creation_partner_address(self): + """ Test that an address erases all lead address fields (avoid mixed addresses) """ + other_country = self.env.ref('base.fr') + empty_partner = self.env['res.partner'].create({ + 'name': 'Empty partner', + 'country_id': other_country.id, + }) + lead_data = { + 'name': 'Test', + 'street': 'My street', + 'street2': 'My street', + 'city': 'My city', + 'zip': 'test@odoo.com', + 'state_id': self.env['res.country.state'].create({ + 'name': 'My state', + 'country_id': self.country_ref.id, + 'code': 'MST', + }).id, + 'country_id': self.country_ref.id, + } + lead = self.env['crm.lead'].create(lead_data) + lead.partner_id = empty_partner + # PARTNER_ADDRESS_FIELDS_TO_SYNC + self.assertEqual(lead.street, empty_partner.street, "Street should be sync from the Partner") + self.assertEqual(lead.street2, empty_partner.street2, "Street 2 should be sync from the Partner") + self.assertEqual(lead.city, empty_partner.city, "City should be sync from the Partner") + self.assertEqual(lead.zip, empty_partner.zip, "Zip should be sync from the Partner") + self.assertEqual(lead.state_id, empty_partner.state_id, "State should be sync from the Partner") + self.assertEqual(lead.country_id, empty_partner.country_id, "Country should be sync from the Partner") + + def test_crm_lead_creation_partner_no_address(self): + """ Test that an empty address on partner does not void its lead values """ + empty_partner = self.env['res.partner'].create({ + 'name': 'Empty partner', + 'is_company': True, + 'mobile': '123456789', + 'title': self.env.ref('base.res_partner_title_mister').id, + 'function': 'My function', + }) + lead_data = { + 'name': 'Test', + 'contact_name': 'Test', + 'street': 'My street', + '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) + lead.partner_id = empty_partner + # SPECIFIC FIELDS + self.assertEqual(lead.contact_name, lead_data['contact_name'], "Contact should remain") + self.assertEqual(lead.email_from, lead_data['email_from'], "Email From should keep its initial value") + self.assertEqual(lead.partner_name, empty_partner.name, "Partner name should be set as contact is a company") + # PARTNER_ADDRESS_FIELDS_TO_SYNC + self.assertEqual(lead.street, lead_data['street'], "Street should remain since partner has no address field set") + self.assertEqual(lead.street2, False, "Street2 should remain since partner has no address field set") + self.assertEqual(lead.country_id, self.country_ref, "Country should remain since partner has no address field set") + self.assertEqual(lead.city, False, "City should remain since partner has no address field set") + self.assertEqual(lead.zip, False, "Zip should remain since partner has no address field set") + self.assertEqual(lead.state_id, self.env['res.country.state'], "State should remain since partner has no address field set") + # PARTNER_FIELDS_TO_SYNC + 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") + + @users('user_sales_manager') + def test_crm_lead_partner_sync(self): + lead, partner = self.lead_1.with_user(self.env.user), self.contact_2 + partner_email, partner_phone = self.contact_2.email, self.contact_2.phone + lead.partner_id = partner + + # email & phone must be automatically set on the lead + lead.partner_id = partner + self.assertEqual(lead.email_from, partner_email) + self.assertEqual(lead.phone, partner_phone) + + # writing on the lead field must change the partner field + lead.email_from = '"John Zoidberg" <john.zoidberg@test.example.com>' + lead.phone = '+1 202 555 7799' + self.assertEqual(partner.email, '"John Zoidberg" <john.zoidberg@test.example.com>') + self.assertEqual(partner.email_normalized, 'john.zoidberg@test.example.com') + self.assertEqual(partner.phone, '+1 202 555 7799') + + # writing on the partner must change the lead values + partner.email = partner_email + partner.phone = '+1 202 555 6666' + self.assertEqual(lead.email_from, partner_email) + self.assertEqual(lead.phone, '+1 202 555 6666') + + # resetting lead values also resets partner + lead.email_from, lead.phone = False, False + self.assertFalse(partner.email) + self.assertFalse(partner.email_normalized) + self.assertFalse(partner.phone) + + @users('user_sales_manager') + def test_crm_lead_partner_sync_email_phone(self): + """ Specifically test synchronize between a lead and its partner about + phone and email fields. Phone especially has some corner cases due to + automatic formatting (notably with onchange in form view). """ + lead, partner = self.lead_1.with_user(self.env.user), self.contact_2 + 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_formatted = phone_format(partner_phone, 'US', '1') + partner_phone_sanitized = phone_format(partner_phone, 'US', '1', force_format='E164') + partner_mobile_formatted = phone_format(partner_mobile, 'US', '1') + 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 + # as well as mobile (who does not trigger the reverse sync) + lead_form.partner_id = partner + 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.ribbon_message) + + lead_form.save() + self.assertEqual(partner.phone, partner_phone, + 'Lead / Partner: partner values sent to lead') + self.assertEqual(lead.email_from, partner_email, + 'Lead / Partner: partner values sent to lead') + self.assertEqual(lead.email_normalized, partner_email_normalized, + '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, + 'Lead: phone_sanitized computed field on mobile') + + # for email_from, if only formatting differs, warning ribbon should + # not appear and email on partner should not be updated + lead_form.email_from = '"Hermes Conrad" <%s>' % partner_email_normalized + self.assertFalse(lead_form.ribbon_message) + lead_form.save() + self.assertEqual(lead_form.partner_id.email, partner_email) + + # LEAD/PARTNER SYNC: lead updates partner + new_email = '"John Zoidberg" <john.zoidberg@test.example.com>' + new_email_normalized = 'john.zoidberg@test.example.com' + lead_form.email_from = new_email + self.assertIn('the customer email will', lead_form.ribbon_message) + new_phone = '+1 202 555 7799' + new_phone_formatted = phone_format(new_phone, 'US', '1') + lead_form.phone = new_phone + self.assertEqual(lead_form.phone, new_phone_formatted) + self.assertIn('the customer email and phone number will', lead_form.ribbon_message) + + lead_form.save() + self.assertEqual(partner.email, new_email) + 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') + lead_form.mobile = new_mobile + 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.assertIn('the customer email and phone number will', lead_form.ribbon_message) + lead_form.save() + self.assertFalse(partner.email) + self.assertFalse(partner.email_normalized) + self.assertFalse(partner.phone) + self.assertFalse(lead.phone) + self.assertFalse(lead.mobile) + self.assertFalse(lead.phone_sanitized) + self.assertEqual(partner.mobile, partner_mobile) + self.assertEqual(partner.phone_sanitized, partner_mobile_sanitized, + 'Partner sanitized should be computed on mobile') + + @users('user_sales_manager') + def test_crm_lead_partner_sync_email_phone_corner_cases(self): + """ Test corner cases of email and phone sync (False versus '', formatting + differences, wrong input, ...) """ + test_email = 'amy.wong@test.example.com' + lead = self.lead_1.with_user(self.env.user) + contact = self.env['res.partner'].create({ + 'name': 'NoContact Partner', + 'phone': '', + 'email': '', + 'mobile': '', + }) + + lead_form = Form(lead) + self.assertEqual(lead_form.email_from, test_email) + self.assertFalse(lead_form.ribbon_message) + + # email: False versus empty string + lead_form.partner_id = contact + self.assertIn('the customer email', lead_form.ribbon_message) + lead_form.email_from = '' + self.assertFalse(lead_form.ribbon_message) + lead_form.email_from = False + self.assertFalse(lead_form.ribbon_message) + + # phone: False versus empty string + lead_form.phone = '+1 202-555-0888' + self.assertIn('the customer phone', lead_form.ribbon_message) + lead_form.phone = '' + self.assertFalse(lead_form.ribbon_message) + lead_form.phone = False + self.assertFalse(lead_form.ribbon_message) + + # email/phone: formatting should not trigger ribbon + lead.write({ + 'email_from': '"My Name" <%s>' % test_email, + 'phone': '+1 202-555-0888', + }) + contact.write({ + 'email': '"My Name" <%s>' % test_email, + 'phone': '+1 202-555-0888', + }) + + lead_form = Form(lead) + self.assertFalse(lead_form.ribbon_message) + lead_form.partner_id = contact + self.assertFalse(lead_form.ribbon_message) + lead_form.email_from = '"Another Name" <%s>' % test_email # same email normalized + self.assertFalse(lead_form.ribbon_message, 'Formatting-only change should not trigger write') + lead_form.phone = '2025550888' # same number but another format + self.assertFalse(lead_form.ribbon_message, 'Formatting-only change should not trigger write') + + # wrong value are also propagated + lead_form.phone = '666 789456789456789456' + self.assertIn('the customer phone', lead_form.ribbon_message) + + @users('user_sales_manager') + def test_crm_lead_stages(self): + lead = self.lead_1.with_user(self.env.user) + self.assertEqual(lead.team_id, self.sales_team_1) + + lead.convert_opportunity(self.contact_1.id) + self.assertEqual(lead.team_id, self.sales_team_1) + + lead.action_set_won() + 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 + + @users('user_sales_leads') + def test_crm_lead_update_contact(self): + # ensure initial data, especially for corner cases + self.assertFalse(self.contact_company_1.phone) + self.assertEqual(self.contact_company_1.country_id.code, "US") + lead = self.env['crm.lead'].create({ + 'name': 'Test', + 'country_id': self.country_ref.id, + 'email_from': self.test_email, + 'phone': self.test_phone, + }) + self.assertEqual(lead.country_id, self.country_ref, "Country should be set on the lead") + lead.partner_id = False + self.assertEqual(lead.country_id, self.country_ref, "Country should still be set on the lead") + self.assertEqual(lead.email_from, self.test_email) + self.assertEqual(lead.phone, self.test_phone) + self.assertEqual(lead.email_state, 'correct') + self.assertEqual(lead.phone_state, 'correct') + + lead.partner_id = self.contact_company_1 + self.assertEqual(lead.country_id, self.contact_company_1.country_id, "Country should still be the one set on partner") + self.assertEqual(lead.email_from, self.contact_company_1.email) + self.assertEqual(lead.phone, self.test_phone) + self.assertEqual(lead.email_state, 'correct') + # currently we keep phone as partner as a void one -> may lead to inconsistencies + self.assertEqual(lead.phone_state, 'incorrect', "Belgian phone with US country -> considered as incorrect") + + lead.email_from = 'broken' + lead.phone = 'alsobroken' + self.assertEqual(lead.email_state, 'incorrect') + self.assertEqual(lead.phone_state, 'incorrect') + self.assertEqual(self.contact_company_1.email, 'broken') + self.assertEqual(self.contact_company_1.phone, 'alsobroken') + + @users('user_sales_manager') + def test_crm_team_alias(self): + new_team = self.env['crm.team'].create({ + 'name': 'TestAlias', + 'use_leads': True, + 'use_opportunities': True, + 'alias_name': 'test.alias' + }) + self.assertEqual(new_team.alias_id.alias_name, 'test.alias') + self.assertEqual(new_team.alias_name, 'test.alias') + + new_team.write({ + 'use_leads': False, + 'use_opportunities': False, + }) + # self.assertFalse(new_team.alias_id.alias_name) + # self.assertFalse(new_team.alias_name) + + def test_mailgateway(self): + new_lead = self.format_and_process( + INCOMING_EMAIL, + 'unknown.sender@test.example.com', + '%s@%s' % (self.sales_team_1.alias_name, self.alias_domain), + subject='Delivery cost inquiry', + target_model='crm.lead', + ) + self.assertEqual(new_lead.email_from, 'unknown.sender@test.example.com') + self.assertFalse(new_lead.partner_id) + self.assertEqual(new_lead.name, 'Delivery cost inquiry') + + message = new_lead.with_user(self.user_sales_manager).message_post( + 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_phone_mobile_update(self): + lead = self.env['crm.lead'].create({ + 'name': 'Lead 1', + 'country_id': self.env.ref('base.us').id, + '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]}) + self.assertFalse(lead.phone) + self.assertEqual(lead.mobile, self.test_phone_data[1]) + self.assertEqual(lead.phone_sanitized, self.test_phone_data_sanitized[1]) + + lead.write({'phone': self.test_phone_data[1], 'mobile': self.test_phone_data[2]}) + 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]) + + # 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) + + @users('user_sales_manager') + def test_phone_mobile_search(self): + lead_1 = self.env['crm.lead'].create({ + 'name': 'Lead 1', + 'country_id': self.env.ref('base.be').id, + 'phone': '+32485001122', + }) + _lead_2 = self.env['crm.lead'].create({ + 'name': 'Lead 2', + 'country_id': self.env.ref('base.be').id, + 'phone': '+32485112233', + }) + self.assertEqual(lead_1, self.env['crm.lead'].search([ + ('phone_mobile_search', 'like', '+32485001122') + ])) |
