function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Mike McNeeley 8Mike McNeeley 8 

How to Trigger on Lead Conversion (to update a field)

I'm very, very new to Apex and at a loss. I have a business requirement to update a custom Lead picklist field called "Qualification Status" to "Converted" when a Lead is converted. I've created the following Trigger:
trigger LeadQualStatusToConvertedTrigger on Lead (before update) {
    
    List<Lead> convertedLeads = new List<Lead>();
    
    for(Lead convertedLead : Trigger.New) {
        if (convertedLead.IsConverted) {
            convertedLeads.add(convertedLead);
        }
    }

    LeadQualStatusToConverted.setQualStatusConverted(convertedLeads);
}
I've tested LeadQualStatusToConverted.setQualStatusConverted() and it works. The issue is that nothing satisfies the Trigger's If clause. Here's a relevant piece of my test class:
@testSetup
    static void setup() {
        List<Lead> testLeads = new List<Lead>();
        for(Integer i=0; i<10; i++) {
            testLeads.add(new Lead(LastName = 'Smith' + i
                                  ,Company = 'Smith Co.'
                                  ,Qualification_Status__c = 'MQL'
                                  ,IsConverted = False));
        }
        
        Database.insert(testLeads);
    }

    @isTest
    static void TestUpdatedConvertedLead() {
        Lead testLead = [Select Id, Qualification_Status__c, IsConverted
                         From Lead
                         Where LastName='Smith1'];

        Test.startTest();
        Database.LeadConvert lc = new Database.LeadConvert();
        lc.setLeadId(testLead.Id);
        lc.setConvertedStatus('Qualified');
        Database.LeadConvertResult lcr = Database.convertLead(lc);
		Test.stopTest();
        
        System.assert(True,testLead.IsConverted);
        System.assert(lcr.isSuccess());
        System.assertEquals('Converted',testLead.Qualification_Status__c);
    }
The first two asserts pass, but the one that matters (the third), does not. The test result statest that the Qualification_Status__c is still the initialized value of MQL. So, it's successfully being converted, but not making it past the Trigger's If and thus not being passed to the setQualStatusConverted() method.

I'm at a loss... How can I check for IsConverted in the Trigger?
Best Answer chosen by Mike McNeeley 8
Mike McNeeley 8Mike McNeeley 8
Okay, figured out why neither the Process was firing or the Trigger being invoked. Apparently I needed to enable the Lead Setting called "Require Validation for Converted Leads." Anyway, now the Process works and the Test Method is invoking the Trigger.

BUT...

I'm still hitting an error within the Trigger: System.SObjectException: DML statement cannot operate on trigger.new or trigger.old (occurring on Line 11 - where LeadQualStatusToConverted.setQualStatusConverted(convertedLeads) is called).
 
trigger LeadQualStatusToConvertedTrigger on Lead (before update) {
    
    List<Lead> convertedLeads = new List<Lead>();

    for(Lead convertedLead : Trigger.New) {
        if (convertedLead.IsConverted) {
            convertedLeads.add(convertedLead);
        }
    }

    LeadQualStatusToConverted.setQualStatusConverted(convertedLeads);
}

I have no idea how to fix this. Any thoughts?

All Answers

Joel Kikkert 23Joel Kikkert 23
Hi Mike, 
I believe you need to query the converted lead again. You've set the Qualification Status to Converted but didn't query it afterward. In test classes, to check your assert, you need to query the lead you just updated. 
Joel Kikkert 23Joel Kikkert 23
@isTest
    static void TestUpdatedConvertedLead() {
        Lead testLead = [Select Id, Qualification_Status__c, IsConverted
                         From Lead
                         Where LastName='Smith1'];

        Test.startTest();
        Database.LeadConvert lc = new Database.LeadConvert();
        lc.setLeadId(testLead.Id);
        lc.setConvertedStatus('Qualified');
        Database.LeadConvertResult lcr = Database.convertLead(lc);
		Test.stopTest();
        
      testLead = [Select Id, Qualification_Status__c, IsConverted
                         From Lead
                         Where LastName='Smith1'];

        System.assert(True,testLead.IsConverted);
        System.assert(lcr.isSuccess());
        System.assertEquals('Converted',testLead.Qualification_Status__c);
    }

Try this, this should do the trick.
Mike McNeeley 8Mike McNeeley 8
Hi Joel, thanks for the reply. Same assertion failure. I do not believe it's an issue with the test method, but rather the Trigger. When I look at the code coverage, it's 80% and clearly fails the IF:

User-added image
Joel Kikkert 23Joel Kikkert 23
I see, it doesn't cover the if statement. Out of curiosity, why write a trigger for this? This field update can be easily accomplished with process builder. Just set the criteria, which would be isConverted = true and have the qualification status updated to Qualified. is there a specific reason you're writing a trigger for this? 
Mike McNeeley 8Mike McNeeley 8
Hm, my undestanding is that the Lead becomes Read Only after the updated record is saved to the database, thus I thought a before update Trigger would be the only way to update the picklist before that happens. Wouldn't any Process Builder update fire after it becomes Read Only and consequently error out?
Mike McNeeley 8Mike McNeeley 8
I sort of de facto tested that when I had my Trigger as "after update" instead, and it failed with an error message stating that the record was Read Only.
Joel Kikkert 23Joel Kikkert 23
I just tested it in my Dev org and it seems to work for me. Created a custom picklist called 'convert_status__c. After conversion I ran a report and it showed as Qualified. User-added image
Mike McNeeley 8Mike McNeeley 8
Can you share the Process you made? I just tried one and it did not work (though it also did not error).

User-added image

User-added image
Joel Kikkert 23Joel Kikkert 23
User-added imageUser-added image  

That's odd. It does the trick for me. Perhaps some issue with the activation of the process? Have you checked the debug console to see if it is executed when you convert a lead?
Mike McNeeley 8Mike McNeeley 8
I added a Chatter post immediate action to test and it never posted. I tried unchecking "only when specified change are made" and I tried enabling recursion. Thus, I do not believe the node criteria are being satisfied. I have no idea why this is working for you - is there some permission or settings somewhere that might be the culprit?
Joel Kikkert 23Joel Kikkert 23
How about this one: http://docs.releasenotes.salesforce.com/en-us/spring16/release-notes/rn_sales_leads_edit_converted_leads.htm

perhaps it works if you enable this option. There is also a permission called: View and Edit Converted Leads in the permission sets. Could be very well the thing you're looking for. 
Mike McNeeley 8Mike McNeeley 8
Thanks, but unfortunately, that's not it either since I've already got all of those assigned (I have the whole time)... May have to escalate to Support.

In any case though, I'm still curious why my Trigger isn't working...
Joel Kikkert 23Joel Kikkert 23
How about in your second scenario, you:
1. Create a new lead. Exclude the isConverted status. It's false by default.  
2. run your test
3. query the new lead (so it loads the lead with the new values. 
4. run your assert. 

I can send over an example tomorrow. Can't test anymore since I'm travelling. 
Ashish KeshariAshish Keshari
Hi Mike,
I did a little modification to your code and I see mine has achieved 100% coverage. The updated code is - 
LeadQualStatusToConvertedTrigger
trigger LeadQualStatusToConvertedTrigger on Lead (before update) {
    
    List<Lead> convertedLeads = new List<Lead>();
    for(Lead convertedLead : Trigger.New) {
        if (convertedLead.IsConverted) {
            convertedLead.Qualification_Status__c='Converted';
            convertedLeads.add(convertedLead);
        }
    }
    //LeadQualStatusToConverted.setQualStatusConverted(convertedLeads);
}
The test class is same as what Joel has provided.


User-added image

In my Dev org, I had 2 to make 2 changes to be working for your code:

User-added image

User-added image
 
Mike McNeeley 8Mike McNeeley 8
Hi Ashish,

Thanks. The only difference I see in your code is adding the action to the Trigger and removing it from the Class, but that isn't my problem. I've verified that the method to update the Qualification Status works fine and I don't want it in the Trigger itself.

It's not clear to me how your test data is satisfying the If IsConverted criteria though. As Joel had suggested, I added an additional SOQL query after the conversion code in the test method, but it still did not work. And, to make sure it's clear, my goal is not 100% code coverage...my goal is to actually have a functioning Trigger.

User-added image
User-added image
Mike McNeeley 8Mike McNeeley 8
Again, I'm only days into learning Apex, so it's very possible that somehow the test data isn't making into the Trigger... Perhaps I've set up my test data incorrectly?
Mike McNeeley 8Mike McNeeley 8
I've added a debug statement in the Trigger to confirm the IsConverted value when the Lead(s) are passed in:

User-added image

Upon running the test, this is the debug log:

User-added image

Given that, I believe the Trigger isn't even firing upon conversion. This output suggests it's only firing on the initial @testSetup data creation. I have a feeling this is a very elementary problem with the way I'm setting up test data & running the tests...I just don't know what it is.
Mike McNeeley 8Mike McNeeley 8
Okay, figured out why neither the Process was firing or the Trigger being invoked. Apparently I needed to enable the Lead Setting called "Require Validation for Converted Leads." Anyway, now the Process works and the Test Method is invoking the Trigger.

BUT...

I'm still hitting an error within the Trigger: System.SObjectException: DML statement cannot operate on trigger.new or trigger.old (occurring on Line 11 - where LeadQualStatusToConverted.setQualStatusConverted(convertedLeads) is called).
 
trigger LeadQualStatusToConvertedTrigger on Lead (before update) {
    
    List<Lead> convertedLeads = new List<Lead>();

    for(Lead convertedLead : Trigger.New) {
        if (convertedLead.IsConverted) {
            convertedLeads.add(convertedLead);
        }
    }

    LeadQualStatusToConverted.setQualStatusConverted(convertedLeads);
}

I have no idea how to fix this. Any thoughts?
This was selected as the best answer
Ashish KeshariAshish Keshari
Hi Mike,

Glad to hear that it is firing now. Can you please paste your code to see what's happening in - LeadQualStatusToConverted.setQualStatusConverted(convertedLeads);

Also - can you please now give a try to my update trigger code earlier just to see if it's achieves your purpose ? 
Mike McNeeley 8Mike McNeeley 8
Hi Ashish,

So, as it turns out, my only problem now is the test assertion. I've confirmed the Trigger to be working by manually createding & converting a Lead. The issue I posted about the error was rather simple to fix; that is, there's no need for a DML operation in a before update since the record isn't yet saved anyway. Thus, to fix it, all I had to do was remove the DML operations. Here's the working Trigger & Helper Class:
 
trigger LeadQualStatusToConvertedTrigger on Lead (before update) {
    
    List<Lead> convertedLeads = new List<Lead>();

    for(Lead convertedLead : Trigger.New) {
        if (convertedLead.IsConverted) {
            convertedLeads.add(convertedLead);
        }
    }
    
    LeadQualStatusToConverted.setQualStatusConverted(convertedLeads);
}
public class LeadQualStatusToConverted {
    
    public static void setQualStatusConverted(List<Lead> leadsToUpdate) {
        List<Lead> updatedLeads = new List<Lead>();
        
        for(Lead leadToUpdate : leadsToUpdate) {
            leadToUpdate.Qualification_Status__c = 'Converted';
        }
    }
}
But, despite it actually working in practice and despite having 100% code coverage, I am getting the following when executing my test method: System.AssertException: Assertion Failed: Expected: Converted, Actual: MQL
@isTest
    static void TestUpdatedConvertedLead() {
        Lead testLead = [Select Id, LastName, Qualification_Status__c, IsConverted
                         From Lead
                         Where LastName='Smith1'];

        Test.startTest();
        Database.LeadConvert lc = new Database.LeadConvert();
        lc.setLeadId(testLead.Id);
        lc.setConvertedStatus('Qualified');
        Database.LeadConvertResult lcr = Database.convertLead(lc);
		Test.stopTest();
        
        System.assertEquals('Converted',testLead.Qualification_Status__c);
    }
I'm so close, yet so far...
Mike McNeeley 8Mike McNeeley 8
Actually, circling back to Joel's first suggestion fixed this problem too! All I had to do was query the updated Lead again before the assertion. Thanks guys for all the help - nothing like learning by doing!