Skip to content

Commit a20a33d

Browse files
committed
Fixed handleLeadAutoConvert. Implemented tests for handleLeadAutoConvert
1 parent f0b5dea commit a20a33d

File tree

3 files changed

+221
-26
lines changed

3 files changed

+221
-26
lines changed

force-app/main/default/classes/LeadTriggerHandler.cls

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -116,42 +116,58 @@ public with sharing class LeadTriggerHandler {
116116
* - One of the errors is map related. Make sure you are using the correct contact map key
117117
*/
118118
public static void handleLeadAutoConvert(List<Lead> leads) {
119+
// Get convert status
120+
LeadStatus convertStatus = [SELECT Id, MasterLabel FROM LeadStatus WHERE IsConverted = TRUE LIMIT 1];
119121
// Step 1: Gather all lead emails
120122
Map<Id,String> leadToEmailMap = new Map<Id,String>();
121123
for (Lead lead : leads) {
122-
leadToEmailMap.put(lead.Id, lead.Email);
124+
if (!lead.IsConverted && lead.Status != convertStatus.MasterLabel && lead.Email != null) {
125+
leadToEmailMap.put(lead.Id, lead.Email);
126+
}
127+
}
128+
129+
if (leadToEmailMap.size() == 0) {
130+
return;
123131
}
124132

125133
// Step 2: Find matching contacts based on email
126-
Map<String, Contact> emailToContactMap = new Map<String, Contact>();
127-
for (Contact c : [SELECT Id, Email, AccountId FROM Contact WHERE Email IN :leadToEmailMap.values()]) {
128-
if (!emailToContactMap.containsKey(c.Email)) {
129-
emailToContactMap.put(c.Email, c);
130-
} else {
131-
// If we found another contact with the same email, we don't auto-convert.
132-
// So we remove the email from the map.
133-
emailToContactMap.remove(c.Email);
134-
}
134+
List<AggregateResult> ars = [
135+
SELECT Email
136+
FROM Contact
137+
GROUP BY Email
138+
HAVING COUNT(Id) = 1
139+
AND Email IN :leadToEmailMap.values()
140+
];
141+
List<String> singleMatchEmail = new List<String>();
142+
for (AggregateResult ar : ars) {
143+
singleMatchEmail.add((String) ar.get('Email'));
135144
}
136145

137-
// Step 3: Auto-convert leads
138-
List<Database.LeadConvert> leadConverts = new List<Database.LeadConvert>();
139-
LeadStatus convertStatus = [SELECT Id, MasterLabel FROM LeadStatus WHERE IsConverted = TRUE LIMIT 1];
140-
for (Id leadId : leadToEmailMap.keySet()) {
141-
String leadEmail = leadToEmailMap.get(leadId);
142-
if (emailToContactMap.containsKey(leadEmail)) {
143-
Database.LeadConvert lc = new Database.LeadConvert();
144-
lc.setLeadId(leadId);
145-
lc.setContactId(emailToContactMap.get(leadEmail).Id); // Use existing Contact Id
146-
lc.setAccountId(emailToContactMap.get(leadEmail).AccountId); // Use existing Account Id
147-
lc.setDoNotCreateOpportunity(true); // Assuming we don't want to create an opportunity
148-
lc.setConvertedStatus(convertStatus.MasterLabel); // Set the converted status
149-
leadConverts.add(lc);
146+
// If we have matching contacts
147+
if (singleMatchEmail.size() > 0) {
148+
Map<String, Contact> emailToContactMap = new Map<String, Contact>();
149+
for (Contact contact : [SELECT Id, Email, AccountId FROM Contact WHERE Email IN :singleMatchEmail]) {
150+
emailToContactMap.put(contact.Email, contact);
150151
}
151-
}
152152

153-
if (!leadConverts.isEmpty()) {
154-
List<Database.LeadConvertResult> lcrs = Database.convertLead(leadConverts);
153+
// Step 3: Auto-convert leads
154+
List<Database.LeadConvert> leadConverts = new List<Database.LeadConvert>();
155+
for (Id leadId : leadToEmailMap.keySet()) {
156+
if (emailToContactMap.containsKey(leadToEmailMap.get(leadId))) {
157+
Contact cont = emailToContactMap.get(leadToEmailMap.get(leadId));
158+
Database.LeadConvert lc = new Database.LeadConvert();
159+
lc.setLeadId(leadId);
160+
lc.setContactId(cont.Id); // Use existing Contact Id
161+
lc.setAccountId(cont.AccountId); // Use existing Account Id
162+
lc.setDoNotCreateOpportunity(true); // Assuming we don't want to create an opportunity
163+
lc.setConvertedStatus(convertStatus.MasterLabel); // Set the converted status
164+
leadConverts.add(lc);
165+
}
166+
}
167+
168+
if (!leadConverts.isEmpty()) {
169+
List<Database.LeadConvertResult> lcrs = Database.convertLead(leadConverts);
170+
}
155171
}
156172
}
157173
}

force-app/main/default/classes/LeadTriggerHandlerTest.cls

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,103 @@ private class LeadTriggerHandlerTest {
116116
}
117117

118118
}
119+
120+
@isTest
121+
static void testHandleLeadAutoConvert_insertPositive() {
122+
// Prepare the test data
123+
TestDataFactory.generateAccountWithContacts(50, 7); // 7 contacts with a unique email
124+
Map<String, Object> params = new Map<String, Object>{'LeadSource' => 'Web', 'Email' => 'test'};
125+
List<Lead> leads = TestDataFactory.createLeadsWithParams(50, params, false);
126+
127+
// Perform the test
128+
Test.startTest();
129+
Database.insert(leads);
130+
Test.stopTest();
131+
132+
// Request resulted Leads
133+
List<Lead> insertedLeads = [
134+
SELECT Id, Email, IsConverted
135+
FROM Lead
136+
];
137+
// Assertions
138+
Assert.isTrue(insertedLeads.size() == 50, 'Expected 50 Leads inserted');
139+
Integer converted = 0;
140+
for (Integer i = 0; i < 50; i++) {
141+
if (i > 0 && Math.mod(i, 7) == 0) {
142+
Assert.isTrue(insertedLeads[i].IsConverted, 'Lead with email ' + insertedLeads[i].Email + ' expected to be converted');
143+
converted++;
144+
} else {
145+
Assert.isFalse(insertedLeads[i].IsConverted, 'Lead with email ' + insertedLeads[i].Email + ' shouldn\t be converted');
146+
}
147+
}
148+
Assert.isTrue(converted == 7, 'Expected 7 Leads converted');
149+
}
150+
151+
@isTest
152+
static void testHandleLeadAutoConvert_insertNegative() {
153+
// Prepare the test data
154+
TestDataFactory.generateAccountWithContacts(5, 2); // 2 contacts with a unique email
155+
Map<String, Object> params = new Map<String, Object>{'LeadSource' => 'Web',
156+
'Status' => 'Closed - Converted',
157+
'Email' => 'test'};
158+
List<Lead> leads = TestDataFactory.createLeadsWithParams(5, params, false);
159+
160+
// Perform the test
161+
Test.startTest();
162+
Database.insert(leads);
163+
Test.stopTest();
164+
165+
// Request resulted Leads
166+
List<Lead> insertedLeads = [
167+
SELECT
168+
Id,
169+
Email,
170+
IsConverted
171+
FROM Lead
172+
];
173+
// Assertions
174+
Assert.isTrue(insertedLeads.size() == 5, 'Expected 5 Leads inserted');
175+
for (Lead lead : insertedLeads) {
176+
Assert.isFalse(lead.IsConverted, 'Lead should\'t be converted');
177+
}
178+
}
179+
180+
@isTest
181+
static void testHandleLeadAutoConvert_update() {
182+
// Prepare the test data
183+
TestDataFactory.generateAccountWithContacts(5, 2); // 2 contacts with a unique email
184+
Map<String, Object> params = new Map<String, Object>{'LeadSource' => 'Web'};
185+
List<Lead> leads = TestDataFactory.createLeadsWithParams(2, params, true);
186+
187+
// Change email
188+
List<Lead> leadsToUpdate = [
189+
SELECT
190+
Id,
191+
Email
192+
FROM Lead
193+
WHERE Id IN :leads
194+
AND IsConverted = false
195+
];
196+
Assert.areEqual(2, leadsToUpdate.size(), 'Expected to get 2 unconverted Leads');
197+
leadsToUpdate[0].Email = 'test0@mail.com'; // not going to be converted
198+
leadsToUpdate[1].Email = 'test1@mail.com'; // going to be converted
199+
200+
// Perform the test
201+
Test.startTest();
202+
Database.update(leadsToUpdate);
203+
Test.stopTest();
204+
205+
// Request resulted Leads
206+
List<Lead> updatedLeads = [
207+
SELECT
208+
Id,
209+
Email,
210+
IsConverted
211+
FROM Lead
212+
WHERE Id IN :leadsToUpdate
213+
];
214+
// Assertions
215+
Assert.isFalse(updatedLeads[0].IsConverted, 'Lead with email ' + updatedLeads[0].Email + ' shouldn\'t be converted');
216+
Assert.isTrue(updatedLeads[1].IsConverted, 'Lead with email ' + updatedLeads[1].Email + ' expected to be converted');
217+
}
119218
}

force-app/main/default/classes/TestDataFactory.cls

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,51 @@
44
@isTest
55
public class TestDataFactory {
66

7+
/**
8+
* Creates test Account
9+
* @param name The name value for the Account
10+
* @param doInsert Set true to insert new created records to the database
11+
*/
12+
public static Account getAccount(String name, Boolean doInsert) {
13+
Account acc = new Account(Name = name);
14+
if (doInsert) {
15+
Database.insert(acc);
16+
}
17+
return acc;
18+
}
19+
20+
/**
21+
* Generates test Account and its Contacts with emails.
22+
* @param numContacts The number of contacts to generate
23+
* @param fraction The number by which create unique emails
24+
* Ex: If generate 50 contacts with fraction 7 we will get 7 Contacts with unique emails
25+
* For fraction 5 we need 5x5+1 Contact to generate
26+
*/
27+
public static void generateAccountWithContacts(Integer numContacts, Integer fraction) {
28+
Account acc = getAccount('Default Account ltd', true);
29+
List<Contact> contacts = new List<Contact>();
30+
for(Integer i = 0; i < numContacts; i++) {
31+
if (Math.mod(i, fraction) == 0) {
32+
contacts.add(new Contact(FirstName = 'Test',
33+
LastName = 'contact' + i,
34+
accountId = acc.Id,
35+
Email = 'test' + i + '@mail.com'));
36+
} else {
37+
contacts.add(new Contact(FirstName = 'Test',
38+
LastName = 'contact' + i,
39+
accountId = acc.Id,
40+
Email = 'test' + Math.round(i / fraction) + '@mail.com'));
41+
}
42+
}
43+
Database.insert(contacts);
44+
}
45+
46+
/**
47+
* Creates new Leads.
48+
* @param titles The list of Titles
49+
* @param doInsert Set true to insert new created records to the database
50+
* We will get so many Leads as number of Titles in the list
51+
*/
752
public static List<Lead> createLeadsByTitle(List<String> titles, Boolean doInsert) {
853
List<Lead> leadsToInsert = new List<Lead>();
954
for (String title : titles) {
@@ -19,6 +64,12 @@ public class TestDataFactory {
1964
return leadsToInsert;
2065
}
2166

67+
/**
68+
* Creates new Leads.
69+
* We will get so many Leads as number of param sets passed to the method
70+
* @param params The list of maps that contains pairs of fieldName/value
71+
* @param doInsert Set true to insert new created records to the database
72+
*/
2273
public static List<Lead> createLeadsByParams(List<Map<String, Object>> params, Boolean doInsert) {
2374
List<Lead> leads = new List<Lead>();
2475
for (Integer i = 0; i < params.size(); i++) {
@@ -37,4 +88,33 @@ public class TestDataFactory {
3788

3889
return leads;
3990
}
91+
92+
/**
93+
* Creates new Leads.
94+
* get Leads with set of fields
95+
* Email field generates separately if it was set in params.
96+
* @param numLeads The number of Leads to generate
97+
* @param params The map that contains pairs of fieldName/value
98+
* @param doInsert Set true to insert new created records to the database
99+
*/
100+
public static List<Lead> createLeadsWithParams(Integer numLeads, Map<String, Object> params, Boolean doInsert) {
101+
List<Lead> leads = new List<Lead>();
102+
for(Integer i = 0; i < numLeads; i++) {
103+
Lead lead = new Lead();
104+
lead.LastName = 'Test Lead ' + i;
105+
lead.Company = 'Test Compaany';
106+
for (String key : params.keySet()) {
107+
if (key == 'Email') {
108+
lead.Email = 'test' + i + '@mail.com';
109+
} else {
110+
lead.put(key, params.get(key));
111+
}
112+
}
113+
leads.add(lead);
114+
}
115+
if (doInsert) {
116+
Database.insert(leads);
117+
}
118+
return leads;
119+
}
40120
}

0 commit comments

Comments
 (0)