1

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');
}
}
2

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.

  • 1
    It probably fires as an afterUpdate to begin with and has a recursion problem when the lead is updated following being set to 'isConverted' in the class the trigger calls for the lead conversion. It needs to have a public static boolean flag set upon entry to prevent the recursion. – crmprogdev Sep 16 '15 at 21:37
  • Following on from crmprogdev's comment, an excellent book is Advanced Apex Programming by Dan Appleman. In chapter 2, he discusses in detail the importance of class static boolean variables. Such variables are used to indicate that a call has been already made, and can therefore be set to true afterwards. – Andy Hitchings Sep 16 '15 at 23:24
  • I added this recursive class: public class Recursive { // Static variables to avoid recursion on trigger operation private static boolean working = false; public static boolean isWorking() { return working; } // Set variable to true to mark record as working public static void setWorking() { working = true; } public static void setClosed() { working = false; } } To the trigger, but I'm now getting a testing error. – Robert Goldberg Sep 18 '15 at 14:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.