1

I have an apex class that converts leads. The class works in the sandbox, with zero errors when using the test class and all works well when triggered by a flow in the sandbox environment. When pushing to Prod I am getting some errors. I created a new DevSandbox, copying the prod org, I am getting some errors but I am not sure how to resolve them. I am extremely new to Apex, so would really appreciate some help!

Thanks!

Here is the Apex Class:

public class ConvertLead_InvokableDev {


@InvocableMethod(label = 'Convert Lead - Invokable')
public static List<LeadConvertResultWrapper> LeadAssign(List<LeadConversionRequest> leadConversionRequests) {
    List<LeadConvertResultWrapper> leadConvertResults = new List<LeadConvertResultWrapper>();

    try {
        List<Database.LeadConvert> MassLeadconvert = new List<Database.LeadConvert>();

        for (LeadConversionRequest request : leadConversionRequests) {
            Database.LeadConvert Leadconvert = new Database.LeadConvert();
            
            Leadconvert.setLeadId(request.LeadId);
            
            // Check if AccountId is not blank before setting it
            if (!String.isBlank(request.AccountId)) {
                Leadconvert.setAccountId(request.AccountId);
            }
            
            // Check if ContactId is not blank before setting it
            if (!String.isBlank(request.ContactId)) {
                Leadconvert.setContactId(request.ContactId);
            }
            
            // Only set OpportunityId when creating an opportunity
            if (!String.isBlank(request.OpportunityId)) {
                Leadconvert.setOpportunityId(request.OpportunityId);
            }
               
            // Set OpportunityName if provided
            if (!String.isBlank(request.OpportunityName) && !request.DoNotCreateOpportunity) {
                Leadconvert.setOpportunityName(request.OpportunityName);
            }
            
            Leadconvert.setConvertedStatus(request.convertedStatus);
                                           
            Leadconvert.setDoNotCreateOpportunity(request.DoNotCreateOpportunity);
            
            MassLeadconvert.add(Leadconvert);
        }

        if (!MassLeadconvert.isEmpty()) {
            Database.LeadConvertResult[] lcr = Database.convertLead(MassLeadconvert, false);

            // Process the results and add to the output variable
            for (Database.LeadConvertResult result : lcr) {
                LeadConvertResultWrapper wrapper = new LeadConvertResultWrapper();
                wrapper.isSuccess = result.isSuccess();
                wrapper.leadId = result.getLeadId();
                wrapper.contactId = result.getContactId();
                wrapper.opportunityId = result.getOpportunityId();
                wrapper.accountId = result.getAccountId();
                                    
                // Check for duplicate error
                if (!result.isSuccess() && result.getErrors()[0].getStatusCode() == StatusCode.DUPLICATE_VALUE) {
                
                    // Append duplicate error details to errorMsg
                    wrapper.errorMsg = 'Duplicate error: ' + result.getErrors()[0].getMessage();
                } else {
                    wrapper.errorMsg = result.isSuccess() ? null : 'Error during lead conversion: ' + result.getErrors()[0].getMessage();
                }
                
                leadConvertResults.add(wrapper);
            }
        }
    } catch (Exception e) {
        // Log or handle the exception as needed
        System.debug('Exception during lead conversion: ' + e.getMessage());

        // Set the error message in the wrapper
        LeadConvertResultWrapper errorWrapper = new LeadConvertResultWrapper();
        errorWrapper.errorMsg = 'Exception during lead conversion: ' + e.getMessage();
        leadConvertResults.add(errorWrapper);
    }

    return leadConvertResults;
}

public class LeadConversionRequest {
    @InvocableVariable(label = 'LeadId' required = true)
    public string LeadId;

    @InvocableVariable(label = 'AccountId' required = false)
    public string AccountId;

    @InvocableVariable(label = 'ContactId' required = false)
    public string ContactId;

    @InvocableVariable(label = 'OpportunityId' required = false)
    public string OpportunityId;

    @InvocableVariable(label = 'OpportunityName' required = false)
    public String OpportunityName;

    @InvocableVariable(label = 'Converted Status' required = false)
    public String convertedStatus;
    
    @InvocableVariable(label = 'DoNotCreateOpportunity' required = false)
    public Boolean DoNotCreateOpportunity;
}

public class LeadConvertResultWrapper {
    @InvocableVariable(label = 'Is Success')
    public Boolean isSuccess;

    @InvocableVariable(label = 'Lead ID')
    public string leadId;

    @InvocableVariable(label = 'Contact ID')
    public string contactId;

    @InvocableVariable(label = 'Opportunity ID')
    public string opportunityId;

    @InvocableVariable(label = 'Account ID')
    public string accountId;

    @InvocableVariable(label = 'Error Message')
    public String errorMsg;
}

Here is the test class:

@isTest
private class ConvertLead_InvokableDev_Test {

@isTest
static void testLeadConversionSuccess() {
    // Create test Account
    Account testAccount = new Account(Name = 'Test Account');
    insert testAccount;

    // Create test Lead
    Lead testLead = new Lead(LastName = 'Test Lead', Company = 'Test Account');
    insert testLead;

    // Prepare LeadConversionRequest
    ConvertLead_InvokableDev.LeadConversionRequest request = new ConvertLead_InvokableDev.LeadConversionRequest();
    request.LeadId = testLead.Id;
    request.AccountId = testAccount.Id; // Use the Id of the created Account
    request.OpportunityName = 'Test Opportunity';
    request.convertedStatus = 'Qualified';
    request.DoNotCreateOpportunity = false;

    List<ConvertLead_InvokableDev.LeadConversionRequest> requests = new List<ConvertLead_InvokableDev.LeadConversionRequest>();
    requests.add(request);

    // Call the invocable method
    List<ConvertLead_InvokableDev.LeadConvertResultWrapper> results = ConvertLead_InvokableDev.LeadAssign(requests);

    // Add results to debug log
    System.debug('LeadConversionSuccess Results: ' + results);

    // Assert that conversion was successful
    System.assert(!results.isEmpty(), 'Result list should not be empty');
    ConvertLead_InvokableDev.LeadConvertResultWrapper result = results[0];
    System.assert(result.isSuccess, 'Lead conversion should be successful');
    System.assert(result.leadId != null, 'Lead Id should not be null');
    System.assert(result.contactId != null, 'Contact Id should not be null');
    System.assert(result.accountId != null, 'Account Id should not be null');
    System.assert(result.opportunityId != null, 'Opportunity Id should not be null');
    System.assert(result.errorMsg == null, 'Error message should be null');
}

@isTest
static void testLeadConversionError() {
    // Create test Account
    Account testAccount = new Account(Name = 'Test Account');
    insert testAccount;

    // Create test Lead
    Lead testLead = new Lead(LastName = 'Test Lead', Company = 'Test Company');
    insert testLead;

    // Prepare LeadConversionRequest with invalid OpportunityName
    ConvertLead_InvokableDev.LeadConversionRequest request = new ConvertLead_InvokableDev.LeadConversionRequest();
    request.LeadId = testLead.Id;
    request.AccountId = testAccount.Id; // Use the Id of the created Account
    request.OpportunityName = 'Test Opportunity'; // Invalid Opportunity Name, given DoNotCreateOpportunity is set to TRUE
    request.convertedStatus = 'Recycle';
    request.DoNotCreateOpportunity = true;

    List<ConvertLead_InvokableDev.LeadConversionRequest> requests = new List<ConvertLead_InvokableDev.LeadConversionRequest>();
    requests.add(request);

    // Call the invocable method
    List<ConvertLead_InvokableDev.LeadConvertResultWrapper> results = ConvertLead_InvokableDev.LeadAssign(requests);

    // Add results to debug log
    System.debug('LeadConversionError Results: ' + results);

    // Assert that conversion failed with an error message
    System.assert(!results.isEmpty(), 'Result list should not be empty');
    ConvertLead_InvokableDev.LeadConvertResultWrapper result = results[0];
    System.assert(!result.isSuccess, 'Lead conversion should fail');
    System.assert(result.errorMsg != null, 'Error message should not be null');
}

@isTest
static void testLeadConversionNoOpportunity() {
    // Create test Account
    Account testAccount = new Account(Name = 'Test Account');
    insert testAccount;

    // Create test Lead
    Lead testLead = new Lead(LastName = 'Test Lead', Company = 'Test Company');
    insert testLead;

    // Prepare LeadConversionRequest without creating an Opportunity
    ConvertLead_InvokableDev.LeadConversionRequest request = new ConvertLead_InvokableDev.LeadConversionRequest();
    request.LeadId = testLead.Id;
    request.AccountId = testAccount.Id; // Use the Id of the created Account
    request.convertedStatus = 'Recycle';
    request.DoNotCreateOpportunity = true;
    request.OpportunityName = null;

    List<ConvertLead_InvokableDev.LeadConversionRequest> requests = new List<ConvertLead_InvokableDev.LeadConversionRequest>();
    requests.add(request);

    // Call the invocable method
    List<ConvertLead_InvokableDev.LeadConvertResultWrapper> results = ConvertLead_InvokableDev.LeadAssign(requests);

    // Add results to debug log
    System.debug('LeadConversionNoOpportunity Results: ' + results);

    // Assert that conversion was successful without creating an Opportunity
    System.assert(!results.isEmpty(), 'Result list should not be empty');
    ConvertLead_InvokableDev.LeadConvertResultWrapper result = results[0];
    System.assert(result.isSuccess, 'Lead conversion should be successful');
    System.assert(result.leadId != null, 'Lead Id should not be null');
    System.assert(result.contactId != null, 'Contact Id should not be null');
    System.assert(result.accountId != null, 'Account Id should not be null');
    System.assert(result.opportunityId == null, 'Opportunity Id should be null');
    System.assert(result.errorMsg == null, 'Error message should be null');
}

Here are the errors that I am receiving:

Class.ConvertLead_InvokableDev_Test.testLeadConversionNoOpportunity: line 106, column 1

  • System.assert(result.isSuccess, 'Lead conversion should be successful'); Class.ConvertLead_InvokableDev_Test.testLeadConversionSuccess: line 34, column 1
  • System.assert(result.isSuccess, 'Lead conversion should be successful');
2
  • I would suggest that since you wrote the code in a sandbox some changes have been made in production that interfere with what you've done. Typical examples are addition of new validation rules that don't allow for scenarios where existing records now violate those rules (so all unrelated changes to those records fail).
    – Phil W
    Apr 17 at 10:55
  • You were exactly right, it was a validation rule! Can't believe I missed that! Thank you.
    – Chelsea
    Apr 17 at 11:30

1 Answer 1

2

This is almost always a difference in orgs, such as validation rules, or other automations. It would be useful to include the error message in your code, rather than just a string.

System.assert(result.isSuccess, 'Expected successful conversion, got error: ' +
  result.errorMsg);

This way, you won't have to guess what went wrong, because you'll have an error message.

You can also troubleshoot this by opening the debug logs from the test run, or by looking at the code coverage.

3
  • Thank you! I will definitely be adding the error message in my code! I was so sure it would be the code that had something wrong that I missed what was staring me in the face! It was a validation rule! So silly of me to overlook! Thank you for the comments!
    – Chelsea
    Apr 17 at 11:29
  • And it looks like you (OP) have structured things well, so modifying the assertions (as suggested above) should also cover the scenario where you're getting into your catch block. If you do have something like an additional validation rule that you're running into, I would expect that you would be getting into the catch block (using allOrNone = false retries the records that fail the DML 2 - 3 times before giving up and giving you an exception).
    – Derek F
    Apr 17 at 11:29
  • @Chelsea No problem! It happens to all of us from time to time.
    – sfdcfox
    Apr 17 at 12:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .