Share this blog!

Salesforce ~ Invoking Apex action from Flow using invocable input output variables

Hi all!

In this article, I will be discussing on integrating Apex actions in flows and how to pass variables between a Flow and an Apex Action.

Salesforce has taken programming into the next level by introducing Flows, which allows users to configure complex flows in a matter of minutes. However, comfort comes with its own drawbacks as there are certain limitations on what a Flow can do.

For example, if your flow uses nested loops, iterating over 2000 elements or performing over 100 queries is going to exceed the governor limits. The recommended solution for performing complex logic is to use Apex and the good news is, that you can still use Flows and offload only the most complex task to Apex through Apex Actions.

In this article, I will be demonstrating an example scenario, where the requirement is to create junction records between a large set of records.

Assume that we have custom objects to represent Student, Class and they have a many to many relationship. I will be using a Flow and an Apex action to iterate over a set of records and create junction records (Enrolments). The use case is creating enrolment records for a certain semester if the students have made corresponding payments.
As you may notice, this requirement needs nested looping or excessive querying to find out the matching records and gather the required mappings between the enrolling students, payments and classes. This could easily exceed the limits for flows. But we can pass the excessive processing to Apex and still achieve the outcome.


The Flow

The flow we will use is as follows. Note that this is the MVP and the actual scenario can have much complicated flow elements. 

We will be using 'Get Records' elements to gather Student, Class and Payment records. Then the idea is to pass them onto an Apex action and then retrieve the list of Enrolment records. Then use a 'Create Elements' to create the records. 

Let's take a look at the Apex code to be used before digging deep into the Flow element.


Code template

The Apex Code template (the Apex class without any processing logic) is as follows.  


The invoked method: 

The invoked method is the method that is called by the Apex Action in the Flow.

  • The @InvocableMethod annotation allows the Flow to call the method
  • Use the 'label' attribute to define the display name of the Apex Action
  • It can have only one parameter
  • You can have only one invocable method per class


Input parameters:

Although the invocable method can have only one parameter, you can use s custom type to define multiple elements, and then use that custom type as the input parameter. 
  • The @InvocableVariable annotation allows the Flow to set the variables
  • If a custom type is used as input parameter, it has to be a 'List'


Output parameters:

Similar to the input parameters, the output parameters too can be defined using a custom type, where you can define multiple elements.
  • The @InvocableVariable annotation allows the Flow to access the output variables
  • If a custom type is used as output parameter, it has to be a 'List'


public class Enrolments {
    
    @InvocableMethod(label='Get Enrolments' description='Iterate over students, classes and payments and create junction records')
    public static List<EnrolmentsResult> createEnrolments(List<EnrolmentsRequest> request){
        
        //parse inputs and variables
        List<Account> students = request.get(0).students;
        List<Class__c> classes = request.get(0).classes;
        List<Payment__c> payments = request.get(0).payments;
        List<Enrolment__c> enrolments = new List<Enrolment__c>();
        List<String> unenroledStudents = new List<String>();
        
        //start of logic

        //end of logic
        
        //parse outputs
        EnrolmentsResult result = new EnrolmentsResult();
        result.enrolments = enrolments;
        result.unenroledStudents = unenroledStudents;
        List<EnrolmentsResult> resultList = new List<EnrolmentsResult>();
        resultList.add(result);
        return resultList;
    }
    
    
    public class EnrolmentsRequest{
        
        @InvocableVariable
        public List<Account> students;

        @InvocableVariable
        public List<Class__c> classes;
        
        @InvocableVariable
        public List<Payment__c> payments;
    }
    
    public class EnrolmentsResult{
        @InvocableVariable
        public List<Enrolment__c> enrolments;
        
        @InvocableVariable
        public List<String> unenroledStudents;
    }

}



The complete solution with the logic:


public class Enrolments {
    
    @InvocableMethod(label='Get Enrolments' description='Iterate over students, classes and payments and create junction records')
    public static List<EnrolmentsResult> createEnrolments(List<EnrolmentsRequest> request){
        
        //parse inputs and variables
        List<Account> students = request.get(0).students;
        List<Class__c> classes = request.get(0).classes;
        List<Payment__c> payments = request.get(0).payments;
        List<Enrolment__c> enrolments = new List<Enrolment__c>();
        List<String> unenroledStudents = new List<String>();
        
        //start of logic
        for(Account student: students){
            for(Class__c classToEnrol : classes){
                Boolean isStudentEnroled = false;
                for(Payment__c payment: payments){
                    if(payment.payer__c == student.Id && payment.class__c == classToEnrol.Id){
                        Enrolment__c enrolment = new Enrolment__c();
                        enrolment.Class__c = classToEnrol.Id;
                        enrolment.Student__c = student.Id;
                        enrolments.add(enrolment);
                        isStudentEnroled = true;
                        break;
                    }
                }
                if(!isStudentEnroled){
                    unenroledStudents.add('\nStudent: ' + student.Name + ' has not enroled to the class: ' + classToEnrol.Name);
                }
            }
        }
        //end of logic
        
        //parse outputs
        EnrolmentsResult result = new EnrolmentsResult();
        result.enrolments = enrolments;
        result.unenroledStudents = unenroledStudents;
        List<EnrolmentsResult> resultList = new List<EnrolmentsResult>();
        resultList.add(result);
        return resultList;
    }
    
    
    public class EnrolmentsRequest{
        
        @InvocableVariable
        public List<Account> students;

        @InvocableVariable
        public List<Class__c> classes;
        
        @InvocableVariable
        public List<Payment__c> payments;
    }
    
    public class EnrolmentsResult{
        @InvocableVariable
        public List<Enrolment__c> enrolments;
        
        @InvocableVariable
        public List<String> unenroledStudents;
    }

}


Apex Action in the Flow 

Once you have the Apex code in place, you can add the Apex Action Flow element in your flow. You can see that the "label" we defined in our Apex class is the displayed name in the Apex Action.




When you add the Apex Action, you can define the input and output parameters as desired.




Hope you learned something today! Cheers!


Next PostNewer Post Previous PostOlder Post Home

0 comments:

Post a Comment