[SalesForce] Lead Conversion Trigger Issue (After Update/After Insert)

I've spent the last week on this now, and it's gone from an annoyance to a horrific pain. We have a process wherein leads can either be (1) created with a unique identifier and then are auto-converted (a process which works fine) or (2) are updated after creation (could be hours or days later) with information from our external system, that adds a unique identifier.

I am trying to write a trigger that will recognize this change, and then subsequently convert the lead.

Tests fine, works in sandbox. In production, I get this error:

Lead DetailError:Apex trigger AgentAdded caused an unexpected exception, contact your administrator: AgentAdded: execution of AfterUpdate caused by: System.DmlException: ConvertLead failed. First exception on row 0; first error: UNKNOWN_EXCEPTION, System.DmlException: Update failed. First exception on row 0 with id 00Qo000000J8ITAEA3; first error: CANNOT_UPDATE_CONVERTED_LEAD, cannot reference converted lead: [] (System Code) : []: Trigger.AgentAdded: line 21, column 1

If anyone can advise on how I should update my trigger, it would be a massive help. My trigger & test class are below. Thanks!

Trigger:

Trigger AgentAdded on Lead (after insert, after update) {
LeadStatus convertStatus = [
select MasterLabel
from LeadStatus
where IsConverted = true
limit 1
];
List<Database.LeadConvert> leadConverts = new List<Database.LeadConvert>();  
for (Lead lead: Trigger.new) {
if (!lead.isConverted && Lead.Agent_ID__c!='0' && Lead.Agent_ID__c !=NULL && Lead.Manually_Routed__c!='TRUE' && Lead.Auto_Assigned__c==FALSE && Lead.Auto_Route__c!='TRUE')
{Database.LeadConvert lc = new Database.LeadConvert();
String oppName = lead.Name;
lc.setLeadId(lead.Id);
lc.setOpportunityName(oppName);
lc.setConvertedStatus(convertStatus.MasterLabel);
leadConverts.add(lc);}
}
if (!leadConverts.isEmpty()) {
List<Database.LeadConvertResult> lcr = Database.convertLead(leadConverts);
}
}

TestClass:

@IsTest
private class AgentAddTest {
private static Integer LEAD_COUNT = 0;
private static Lead createLead() {
LEAD_COUNT += 1;<br>
return new Lead(<br>
FirstName = '_unittest_firstname_: ' + LEAD_COUNT,
LastName = '_unittest_lastname_: ' + LEAD_COUNT,
Metro__c='New Jersey'+ LEAD_COUNT,
Status = 'Unread'
);
}
public static void makeFreeTrial(Lead lead) {
lead.Agent_Added__c = TRUE;
lead.agent_id__c='100345678';
}
public static List<Lead> fetchLeads(Set<Id> ids) {
return [
select isConverted
from Lead
where Id in :ids
];
}
public static testMethod void trialConvert() {
Lead testLead = createLead();
makeFreeTrial(testLead);
Test.startTest();
insert testLead;
Test.stopTest();
List<Lead> results = fetchLeads(new Set<Id>{testLead.Id});
System.assertEquals(1, results.size(), 'Did not get the right number of leads back');
System.assert(results.get(0).isConverted, 'The lead should have been converted since it was a "Free Trial"');
}
public static testMethod void nonTrialNoConvert() {
Lead testLead = createLead();
Test.startTest();
insert testLead;
Test.stopTest();
List<Lead> results = fetchLeads(new Set<Id>{testLead.Id});
System.assertEquals(1, results.size(), 'Did not get the right number of leads back');
System.assert(!results.get(0).isConverted, 'The lead should not have been converted since it was not a "Free Trial"');
}
public static testMethod void bulkTest() {
List<Lead> shouldBeConverted = new List<Lead>();
List<Lead> shouldNotBeConverted = new List<Lead>();
for (Integer i = 0; i < 50; i++) {
Lead testLeadNonConvert = createLead();
Lead testLeadConvert = createLead();
makeFreeTrial(testLeadConvert);
shouldBeConverted.add(testLeadConvert);
shouldNotBeConverted.add(testLeadNonConvert);
}
List<Lead> toInsert = new List<Lead>();
toInsert.addAll(shouldBeConverted);
toInsert.addAll(shouldNotBeConverted);
Test.startTest();
insert toInsert;
Test.stopTest();
Map<Id, Lead> expectedConversions = new Map<Id, Lead>(shouldBeConverted);
Map<Id, Lead> expectedNonConversions = new Map<Id, Lead>(shouldNotBeConverted);
Set<Id> leadIds = new Set<Id>();
leadIds.addAll(expectedConversions.keySet());
leadIds.addAll(expectedNonConversions.keySet());
for (Lead result: fetchLeads(leadIds)) {
if (expectedConversions.containsKey(result.Id)) {
System.assert(result.isConverted, 'This lead should have been converted ' + result);
expectedConversions.remove(result.Id);
} else if (expectedNonConversions.containsKey(result.Id)) {
System.assert(!result.isConverted, 'This lead should not have been converted ' + result);<br>
expectedNonConversions.remove(result.Id);
} else {
System.assert(false, 'We got a Lead we did not expect to get back ' + result);
}
}
System.assert(expectedConversions.isEmpty(), 'We did not get back all the converted leads we expected');
System.assert(expectedNonConversions.isEmpty(), 'We did not get back all the non converted leads we expected');
}
}

Best Answer

Is it possible that your Trigger is firing twice on the same record, once After Insert and again After Update? When it reaches the After Update transaction, the Lead is already set to IsConverted and therefore could throw that error. Try removing After Update and running your tests to confirm that. If you also need this to operate on After Update you'll have to add some logic to handle that transaction distinctly.

Related Topic