SFDC Stop - Always the latest about Salesforce


Full Tutorial Series with videos, free apps, live sessions, salesforce consulting and much more.


Telegram logo   Join our Telegram Channel

Sunday 24 March 2024

FlowError: The number of results does not match the number of flow interviews | Building invocable actions The Right Way!

Hello Trailblazers,

In this post we're going to learn, how we can build apex invocable actions The Right Way. It might be possible that you've faced this issue in the past:

FlowError: The number of results does not match the number of flow interviews

Today, we're going to build an apex invocable action, call it from flow in a scenario such that it fails and then fix the error to make sure it works perfectly fine. Let's Begin!

Use Case

We have a custom object named Application (Application__c), the details of that object are provided below:
As you can see above, our name field is Application ID which is an auto number. We have 3 custom fields:

1. Candidate Name (CandidateName__c): This is a text field with length 255. It'll store the name of our candidate who has submitted the application
2. Candidate Phone (CandidatePhone__c): This is a phone field. It'll store the phone number of our candidate
3. Lead (Lead__c): This is a lookup to standard lead object. For each new application, if the candidate is not present as a lead in salesforce, we'll create a new lead in salesforce and will tag that lead to our application record. If a lead is already present in salesforce, we'll tag that existing lead to our application record as well.

Automation

How do we check whether we should create a new lead or use an existing lead?

We're going to do that using candidate's phone number in the application. If there exists a lead with the same phone number as that of a candidate, we're going to tag that lead to the application record as it's created. Otherwise, we're going to create a new lead with that phone number and candidate's name and we're going to tag that lead to our application record.

So, we can say that our Application's CandidatePhone__c will map to Lead's Phone field. We need an automation (flow) to create lead records automatically and tag them to application records as these are created. We'll create that flow soon.

Platform Encryption

For security reasons, we've turned on platform encryption in our salesforce org and the lead's Phone field is encrypted as shown below:
If we click on Select Fields button above in Encrypt Standard Fields section, we'll get the below page, where we've encrypted Lead's Phone field as shown below:
This encryption will create one challenge in building our flow, which we'll see soon. Let's build a flow now.

Building a Flow

We need a Record Triggered flow which will fire when an application record is created. It'll check if an existing lead record is present with Phone = CandidatePhone__c or not. If yes, it'll tag that record with the application record. Otherwise, it'll create one new lead record using CandidatePhone__c and CandidateName__c and will tag that new lead record to our application record.

Got to Setup, search for flow, and click on the New Flow button as shown below:
You'll get the below screen:
Choose Record-Triggered flow as shown above and click Create.

Choose the object as Application, this flow will run when an application record is created, in After context as shown below:
Click on the Save button to save the flow. The details are provided below:
Flow Label: Tag Lead to Application
Flow API Name: Tag_Lead_to_Application
Description: This flow will tag a lead record to the application record as it's created.

Click on Save button in the dialog to save the flow.

Now, our flow is saved. The first step is to query lead records where Phone = CandidatePhone__c. We're going to use a Get element to query records as shown below:
We only need the Lead Id here, so we're querying only that:

Problem in the Flow

Let's save the flow once by clicking the Save button. As you try to save the flow, you'll get an error as shown below:
It says: Because Lead's Phone field is encrypted, our flow cannot reference that field. We need to remove this reference in order to save the flow. This means that we cannot query our existing lead records by filtering through Phone field. In order to achieve this functionality now, it's time to move to Invocable Apex.

Resolution: Invocable Apex

We're going to create a new apex class which will help us in querying the leads by phone number. The code for the same is provided below:
public class LeadQueryAction {

    /**
    * This method will query leads by phone numbers and return the list of lead ids
    */
    @InvocableMethod(label='Get Leads by Phone Numbers' description='Return lead records filtered by phone numbers' category='Lead')
    public static List<Id> queryLeadsByPhoneNumbers(List<String> phoneNumbers) {
        Map<Id, Lead> leadsMap = new Map<Id, Lead>([SELECT Id FROM Lead WHERE Phone IN :phoneNumbers]);
        System.debug(phoneNumbers);
        System.debug(leadsMap.keySet());
        return new List<Id>(leadsMap.keySet());
    }
}
As you can see above, we have a class named LeadQueryAction. This class has a method named queryLeadsByPhoneNumbers(List<String> phoneNumbers). This method will receive the list of phone numbers from flow, will query the leads using those phone nunbers and will return the list of lead ids. We're querying lead records using SOQL query: [SELECT Id FROM Lead WHERE Phone IN :phoneNumbers];. We're converting the queried lead records list directly into a Map<Id, Lead> named leadsMap so that we can get the lead ids easily using this map. Finally, we're returning the list of lead ids by converting lead ids set into a list using new List<Id>(leadsMap.keySet());

Also, note that in the @InvocableMethod annotation, we've specified the label as Get Leads by Phone Numbers and description as Return lead records filtered by phone numbers. We've also specified the category as Lead. This label will be visible in our flow when we'll search for our invocable action.

Our apex class is ready now, let's get back to our flow and call this method.

First of all, let's delete the Get element as we're going to query lead records using our invocable apex method now:

Click on the + icon again and search for Get Leads by Phone Numbers action as shown below:
Notice that our invocable method's label and description are visible here. Choose this action and you'll get the New Action dialog as shown below:
Alternatively, you can also search for action, click the Action option and then search for Get Leads by Phone Numbers action as shown below:
You'll get the new Action dialog here as well as shown below:
In our New Apex Action dialog, fill the following information as shown below:
Label: Get Existing Leads
API Name: Get_Existing_Leads
Description: This element will get ids of existing lead records where Phone = CandidatePhone__c

Click on Include button to pass information to phoneNumbers parameter of our invocable apex method and let's bind it with CandidatePhone__c field of our current application record {!$Record.CandidatePhone__c}.

Click on Done.

Now, our Get Existing Leads apex action will provide us the id of the lead (if it exists) which is present in our salesforce org where Phone = CandidatePhone__c for our current application record.

We need to check, if a lead id is returned by our method or not. If a lead id is returned, the existing lead id will be populated in our application's Lead__c lookup. Otherwise, we'll create a new lead record and will populate it's id in our application's Lead__c lookup field.

To check this, we are going to add a decision element as shown below:

Label: Is lead present?
API Name: Is_lead_present
Description: This decision element will check if an existing lead with the candidate's phone number is present in the system or not

Outcomes will be Yes and No (the default outcome from the decision is renamed to No). For the outcome to be Yes, we're checking that the Text from Get_Existing_Leads {!Get_Existing_Leads} (which is nothing but our Lead Id) should not be Blank.

If the outcome of the decision is Yes, we'll update our applicaton record using an update element as shown below:

We'll choose Update Triggering Record option as we want to update lead id on the same record which fired the flow:

The details added in this Update Triggering Record element are provided below:

Label: Update existing lead id on application
API Name: Update_existing_lead_id_on_application
Description: This element will update the Lead__c on application with the lead id received from Get Existing Leads invocable action
How to Find Records to Update and Set Their Values: Use the application record that triggered the flow
Set Field Values for the Application Record: Lead__c = {!Get_Existing_Leads}

If outcome of the decision is No, we'll create a new Lead record and tag that to the application record as shown below:

We'll add a Create Records element in our No path as shown above. The details added in the Create Records element are provided below:

Label: Create new lead
API Name: Create_new_lead
Description: This element will create a new lead record using the candidate details from application record
How Many Records to Create: One
How to Set the Record Fields: Use separate resources, and literal values
Object: Lead
Set Field Values for the Lead:
LastName = {!$Record.CandidateName__c}
Phone = {!$Record.CandidatePhone__c}

As Company field is mandatory on lead, let's add that as well in our Create Records element with a value as SFDC Stop as shown below:
Company = SFDC Stop
This element will create a new lead record for us. Let's update our application record with this lead id. For this, we'll add an Update Triggering Record element again as shown below:
The details added in the Update Triggering Record element are provided below:

Label: Update new lead id on application
API Name: Update_new_lead_id_on_application
Description: This element will update the Lead__c on application with the lead id received from Create new lead action
How to Find Records to Update and Set Their Values: Use the application record that triggered the flow
Set Field Values for the Application Record: Lead__c = {!Create_new_lead}

Let's save our flow by clicking the Save button.
Once saved, activate the same by clicking the Activate button.
Now, it's time to test our flow!

Let's go to the Applications tab in our salesforce org and create a new Application record as shown below:
We've kept the Lead lookup as empty. The Candidate Name is Richard Hendricks and Candidate's Phone is 9999999999. As we click on Save, our flow will create a new lead record for us, as a lead record with this phone number is not already present in salesforce and it'll tag the newly created lead to our application record as shown below:
As you can see above, a lead record named Richard Hendricks is tagged. The Phone is the same as Candidate Phone in our application record and the Company is SFDC Stop.

Now, let's try creating another application record using the same details as shown below:
You'll notice that as the record is created, the same (already existing) lead is tagged to this application record:
Note: You can verify the lead ids of both the lead records tagged to application records to make sure that the same lead record is tagged to both the application records.

This means that our flow and invocable apex method is working perfectly fine. But What will happen if we have more than one application records? Let's test it!

Flow: Bulk Testing

Let's consider a scenario where we're creating two application records in a single transaction. Both the application records should be connected to the same lead record. The code snippet to create these applications is provided below:
List<Application__c> applications = new List<Application__c>();
applications.add(new Application__c(CandidateName__c = 'Richard Hendricks', CandidatePhone__c = '9999999999'));
applications.add(new Application__c(CandidateName__c = 'Richard Hendricks', CandidatePhone__c = '9999999999'));
insert applications;
As you can see above, we're inserting a list of applications with 2 records. Both the records are by the same candidate, so, ideally the same lead record should be tagged to both the applications. When we execute this code snippet, we get the result as shown below:
As you can see above, our flow is failing and we're getting an error. In our case, it's not showing the exact issue but in some scenarios it'll show the actual error like: FlowError: The number of results does not match the number of flow interviews

This error basically means that the invocable method is not returning the list of same size as it was passed to the method while calling it from flow. Let's debug this by using the below code snippet:
List<String> phoneNumbers = new List<String>{'9999999999','9999999999'};
System.debug(LeadQueryAction.queryLeadsByPhoneNumbers(phoneNumbers));
In the above snippet, we're calling our invocable action method directly and passing a list of two phone numbers which are exactly the same as of our Richard Hendricks lead record. The output when the above code snippet is executed is provided below:
If you remember, our invocable method has some System.debug() statements, I'm sharing those again below for your reference:
The first debug statement is displaying the list of phone numbers received by our invocable method in parameter. It's showing both the phone numbers as: (9999999999, 9999999999). This is perfectly all right. 

We queried our lead records using these phone numbers and got the following ids in our leadsMap.keySet() which is used in the second debug statement: {00QH40000010giZMAQ}. As you can see, we're getting only one lead id in this set as there's a single lead record present in the system with this phone number.

The third debug statement which is coming from System.debug(LeadQueryAction.queryLeadsByPhoneNumbers(phoneNumbers)); is showing the same lead id in a list instead of a set as: (00QH40000010giZMAQ). This means that the list of lead ids which is returned from our invocable method is having only one lead id, however, we passed 2 phone numbers in this method to query and get our related lead records. 

This is why our flow is failing because when our flow is running for 2 records created in the same transaction, we have 2 flow interviews. As the flow is bulkified, the phone numbers list passed to our invocable method consist of 2 phone numbers however our method returned only a single lead id in the list of ids. Therefore, The number of results (lead ids) does not match the number of flow interviews. These metrics (debug statements) are also visible in the earlier logs when we tried to create our application records as shown below:
You can see above that the phoneNumbers list has two phone numbers whereas the leadsMap.keySet() has a single lead id.

In order to make our invocable method work correctly, we need to return the list of lead ids of same size as the size of our input list i.e. the list of phone numbers. Let's fix our invocable method now.

Issue Resolution for "FlowError: The number of results does not match the number of flow interviews"

In order to fix our invocable method, we'll do the following:

1. Query our lead records.
2. Create a map of lead phone to lead id
3. Loop our phone numbers and check: If we have a lead id corresponding to the current phone number, add it to the lead ids list, otherwise, add null to the lead ids list.
4. Return the list of lead ids.

This will ensure that the order of phone numbers received and the lead ids corresponding to those phone numbers remains aligned, it's important to ensure that our flow behaves correctly. The updated code for our invocable action class is provided below:
public class LeadQueryAction {

    /**
    * This method will query leads by phone numbers and return the list of lead ids
    */
    @InvocableMethod(label='Get Leads by Phone Numbers' description='Return lead records filtered by phone numbers' category='Lead')
    public static List<Id> queryLeadsByPhoneNumbers(List<String> phoneNumbers) {
        List<Id> leadIds = new List<Id>();
        List<Lead> leads = [SELECT Id, Phone FROM Lead WHERE Phone IN :phoneNumbers];
        Map<String, Id> leadPhoneNumberToIdMap = new Map<String, Id>();
        for(Lead lead : leads) {
            leadPhoneNumberToIdMap.put(lead.Phone, (Id) lead.Id);
        }
        for(String phoneNumber : phoneNumbers) {
            if(leadPhoneNumberToIdMap.containsKey(phoneNumber)) {
                leadIds.add(leadPhoneNumberToIdMap.get(phoneNumber));
            } else {
                leadIds.add(null);
            }
        }
        return leadIds;
    }
}
As you can see above, we created a list of lead ids named leadIds. This list will store our lead ids which we'll return from our method. Then we queried the lead records as we were doing earlier. This time, we're storing it in a list named leads.

Now, we created a Map<String, Id> named leadPhoneNumberToIdMap. This map will store our lead phone number and the corresponding lead id. We looped all the queried lead records to populate this map. Finally, we looped the phone numbers which we received in our method, we then checked: if our leadPhoneNumberToIdMap contains the current phone number as a key, we'll add the corresponding lead id to the leadIds list, otherwise, we'll add null to our leadIds list. Adding null basically means that for the current phone number, we don't have any lead present in salesforce. Note that a list can have multiple null values.

Let's save our updated apex class and run the below code snippet to test our flow once again. This time we'll create 4 application records as shown below:
List<Application__c> applications = new List<Application__c>();
applications.add(new Application__c(CandidateName__c = 'Richard Hendricks', CandidatePhone__c = '9999999999'));
applications.add(new Application__c(CandidateName__c = 'Erlich Bachman', CandidatePhone__c = '9999999991'));
applications.add(new Application__c(CandidateName__c = 'Richard Hendricks', CandidatePhone__c = '9999999999'));
applications.add(new Application__c(CandidateName__c = 'Gavin Belson', CandidatePhone__c = '9999999992'));
insert applications;
If you notice above, the first and third application records have the same candidate Richard Hendricks with the same phone number as: 9999999999, so, our flow should link the existing lead to both the application records. However, the second and fourth application records have different candidates i.e. Erlich Bachman and Gavin Belson with different phone numbers. So, our flow should create new leads and link them with our application records.

Currently, we only have 2 application records in the system that we created earlier as shown below:

Now, as we execute our code snippet, we get the below output:
Notice that we got 4 new applications created as shown above. The first and third application records are linked with the same lead Richard Hendricks, however, the second and fourth records are linked with new lead records i.e. Erlich Bachman and Gavin Belson

This time our invocable method is working Perfectly Fine! and the flow is not failing. You can add debug statements in this method to see how it works. However, I'm displaying the output we get from our invocable method as it's called internally using the below code snippet:
List<String> phoneNumbers = new List<String>{'9999999999', '9999999993', '9999999999', '9999999994'};
System.debug(LeadQueryAction.queryLeadsByPhoneNumbers(phoneNumbers));
If you notice above, the first and third phone numbers are the same i.e. 9999999999 however, the second and fourth phone numbers are different i.e. 9999999993 and 9999999994 as was there in our application records. I didn't use 9999999991 and 9999999992 here because now we have leads present in the system linked with these phone numbers. When we execute this code snippet, the output list of lead ids which we got from our method is as shown below:
As you can see above, in our output, the first and third entry in our list is the lead id of Richard Hendricks lead and it's the same. However, the second and fourth entry in our list is coming as null because there are no leads for these phone numbers present in salesforce.

Therefore, we passed 4 phone numbers and got a list of 4 elements i.e. lead ids. Now, we can say that: The number of results match the number of flow interviews.

That's all for this tutorial everyone, I hope you liked it, let me know your feedback in the comments down below.

Happy Holi! Get my Mastering Lightning Datatable in Salesforce LWC course at best price using the coupon code "HAPPYHOLI". You can get the same by clicking this link: https://www.udemy.com/course/mastering-lightning-datatable-in-salesforce-lwc/?couponCode=HAPPYHOLI

Happy Trailblazing!!

Tuesday 6 February 2024

Repeater Component (Beta) in Salesforce Flow | Spring'24 Release

Hello Trailblazers,

In this post we're going to learn about new Repeater component provided by salesforce for screen flows. Let's begin!

Screen Flow: Create contacts for Account

Let's start with a basic use case: We want to create multiple contacts for our account record in one go. The standard screen flow approach for this will be: Show contact fields (contact form) on a screen, the user can fill those, maybe have a checkbox at the end which says, do you want to create another contact? If the user checks it, reload the same screen again on clicking next button so that the user can fill details for another contact. If the user doesn't check that checkbox, create all the contact records for which the user entered information till now.

The drawback with this approach is: User cannot view the details of all the contacts on a single screen while editing. Due to this, he/she may go back and forth to update a particular contact which may lead to some other issues like: duplicate records (as you may be adding contact record to the list as well side by side - in the same loop which is used to repeat screen, with a plan that you can insert those at the end - give this a try) or there maybe some other issues too.

We can solve these problems using the new Repeater component provided by salesforce for screen flows. Please note that this component is presently in beta.

Creating the base flow with empty screen

1. Go to Setup and search for Flows. Click on the New Flow button to create a new flow
2. On the New Flow screen, choose Screen Flow and click on Create button
3. Click on the + icon between Start and End element and choose Screen to add a screen element to our flow
4. Add a label for the screen as Create Contacts for Account, the API name will auto populate to Create_Contacts_for_Account. Click on Done
5. Before proceeding forward, let's save our flow. Click on the Save button at the top right corner. In the Save the flow popup, populate the Flow Label as Create Contacts for Account, the Flow API Name will autopopulate as Create_Contacts_for_Account. Click the Save button in the popup screen to save the flow.
6. Your flow will look as shown below

Populate the screen with repeater component with contact fields inside it

Open the screen element and add repeater component to it as shown below:
The API name for the same is: ContactDetailsRepeater. Now, let's add some contact fields in our repeater component.

Let's add a text field as shown below, the Label of the field can be: First Name with the API Name as First_Name.
Similarly, let's add the last name field as well with Label as Last Name and API Name as Last_Name. We'll also make this field Required by checking the Require checkbox as shown below:
Click on Done.

Iterate contacts to create a contact list

The repeater component will provide us a list of contact records. Let's iterate that list by using a loop. Click on the + icon again and add a loop element as shown below:
Loop Label can be Iterate Contacts and API name can be Iterate_Contacts. For the Collection Variable you can have it as {!ContactDetailsRepeater.AllItems} as shown in the below images:

The final screen will look like this:
Now, let's add an assignment element inside the loop to create our contact record. Click on the circle (+ element) inside the loop and choose assignment as shown below:
Our assignment Label can be Create Contact Record and API name will be Create_Contact_Record as shown below:
Now, to create a new contact and add it to the list, we need some resources. Let's create a new resource named Contact which will store the contact record as shown below:
Resource Type: Variable
API Name: Contact
Data Type: Record
Object: Contact

Click on Done and create another resource to store list of contacts as shown below:
Resource Type: Variable
API Name: ContactList
Data Type: Record
Object: Contact
Allow multiple values (collection): True

Click on Done. We also want to associate our contacts to the account record. Let's create another resource which will store the id of the account as shown below:
Resource Type: Variable
API Name: AccountId
Data Type: Text
Availability Outside the Flow: Available for input

Click on Done and let's update our assignment element as shown below:
{!Contact.FirstName} Equals {!Iterate_Contacts.First_Name}
{!Contact.LastName} Equals {!Iterate_Contacts.Last_Name}
{!Contact.AccountId} Equals {!AccountId}
{!ContactList} Add {!Contact}

We assigned the first name and last name values from current item of the list provided by repeater to the first name and last name of the contact record. Then we assigned the value of AccountId in our contact record to the value of AccountId variable. Finally, we added our contact record to the ContactList.

Inserting Contacts in Salesforce

Now, we need to insert this list, click on the circle (+ element) outside the loop and choose Create Records element as shown below:
In the New Create Records section, add the label as Create Contacts, the API name as: Create_Contacts. The value for How Many Records to Create should be Multiple and in the Record Collection field, we can choose our ContactList collection as shown below:
Now, we can save and activate the flow. Let's go to our account record's detail page and embed the flow there.

Adding Screen Flow to Lightning Record Page

Go to any account record in salesforce. Click on Gear icon and choose Edit Page.
In the Lightning App Builder, search for Flow in the components section in the left, drag and drop it anywhere on the page you like. In my case, I've added it to the right. Choose our Create Contacts for Account flow and for the AccountId variable, check the Pass record ID to this variable checkbox as shown below:
Click on Save. Activate the page if needed and go back. You'll notice that the flow is present on the page as shown below:

Demo

Our changes are done. Let's take a look at the demo:
As you can see above, as we click on the + Add button, the section inside the repeater component is repeated so that we can create multiple contacts in one go. Similarly, we can also click the Remove button to remove a section. As we clicked Next button, 2 contacts: Richard Hendricks and Erlich Bachman were created and attached to the current account record.

Note: The Repeater component is currently in Beta and supports these components inside it as provided by salesforce: Checkbox, Checkbox Group, Currency, Date, Date & Time, Long Text Area, Multi-Select Picklist, Number, Password, Picklist, Radio Buttons, Text, and Display Text

That's all for this tutorial everyone, I hope you liked it, let me know your feedback in the comments down below.

Happy Trailblazing!!