Flow using @InvocableMethod fails when inserting multiple records

apexbulkificationinvocable-methodvisual-workflow

I have an Apex class that matches Lead records to existing Contact records based on a phone number input. I'm using @InvocableMethod to include it in a Flow. The Flow works as designed when I insert a single Lead record, but returns the error The number of results does not match the number of interviews that were executed in a single bulk execution request when I attempt to process multiple Lead records (e.g. via Workbench).

I believe this is the same issue described here: Bulkification of Flow with invocable action – error re: number of results – how do I resolve?, but being new to Apex, I am struggling to understand how to get the input/output numbers to match for my use case.

An example of my Apex class is below:

public class matchLeadToContact{

@InvocableMethod(label='Match Lead to Contact' description='Matches incoming Leads to Contact records based on phone number' category='Lead')

public static List<String> getContactRecords(List<methodInputs> leadRecord) {

    String leadMobilePhone = leadRecord[0].MobilePhone;
    String leadHomePhone = leadRecord[0].Phone;

    leadHomePhone = leadHomePhone.replaceAll('^0-9', '');
    leadMobilePhone = leadMobilePhone.replaceAll('^0-9', '');

    List<String> contactRecord = new List<String>();
    
    List<Contact> contacts = [SELECT Id FROM Contact WHERE Phone =: leadMobilePhone OR Phone =: leadHomePhone LIMIT 1];
    
    for (Contact contact : contacts) {
        contactRecord.add(contact.Id);
    }
    
    return contactRecord;

}

//input details that come to apex from flow
public class methodInputs{
    @InvocableVariable(required=true)
    public String MobilePhone;
    @InvocableVariable(required=true)
    public String Phone;
}}

Might someone be able to explain why I am receiving the error even though my SOQL query is limited to a single result? How do I fix this?

Best Answer

You need to return a result for each input, making the input and output arrays the same size (and the same order).

Try something like:

public static List<String> getContactRecords(List<MethodInputs> leads) {
    Set<String> phones = new Set<String>();

    // Collect all the mobile and phone numbers, ignoring empty values
    for (MethodInputs lead : leads) {
        String phone = lead.MobilePhone?.replaceAll('^0-9', '');

        if (String.isNotBlank(phone)) {
            phones.add(phone);
        }

        phone = lead.Phone?.replaceAll('^0-9', '');

        if (String.isNotBlank(phone)) {
            phones.add(phone);
        }
    }

    // Query all the contacts that match either a mobile or home phone number from
    // the input "leads"
    List<Contact> contacts = [SELECT Id, Phone FROM Contact WHERE Phone IN :phones];

    // Turn this into a lookup from phone number to the Contact's ID
    Map<String, Id> contactIdsByPhoneNumber = new Map<String, Contact>();

    for (Contact contact : contacts) {
        // This assumes contacts have unique phone numbers; if there are duplicates
        // then the last one matching is stored
        contactIdsByPhoneNumber.put(contact.Phone, contact.Id);
    }

    // Now generate the results in the same order as the inputs    
    List<String> results = new List<String>();
    
    for (MethodInputs lead : leads) {
        // Find the contact ID for the input lead by Phone or MobilePhone number
        Id contactId = contactIdsByPhoneNumber.get(lead.Phone);

        if (contactId == null) {
            contactId = contactIdsByPhoneNumber.get(lead.MobilePhone);
        }

        // This may be null if there is no contact for this lead's Phone or
        // MobilePhone number
        results.add(contactId);
    }
    
    return results;
}