Dynamic Approval Process

Dynamic Approval Process in Salesforce: Allocate the approvers dynamically to the record. Populate the approvers in the user lookup fields in the record.
In static approval process, we have to define the approvers in the approval process. Meaning whenever the record matches the approval process condition and approval process fires and associate the record to the approver which we define in approval process.

Always we cannot achieve business criteria with static approval process, meaning we have conditions like record should be routed to different approvers based on region, country or with some other criteria. We have to write one static approval process for each condition.

So to avoid multiple static approval process for the same requirement with same kind of implementation we will go for dynamic approval process.
– This process is full of apex and implementation is somewhat tricky but very powerful and useful for the huge projects.
– We have to write apex (triggers) to populate the approver in the record lookups.
– To achieve dynamic approval process we have to create one new object (approval matrix), which is useful to define all the conditions (region, country, etc…) and the approvers.

So once user clicks on SUBMIT button. We should fires apex code and check what and all the approval matrix matches to the record and we will take the approvers from the approval matrix and populate the approvers into the user lookups in the record, then we will fire the approval process. Inside approval process we have to define the steps like route process instance to approvers.

Example: Create an approval process on opportunity object, should be routed to base on Country. (Let say we have almost 200 countries in our project)

Steps to Implementation of dynamic approval process
1. Create User lookup fields and one picklist field (Status__c and values are Open, Submitted, Approved and Rejected).
2. Create an Approval process on Opportunity Object with Entry criteria is Status equal to Open.

3. Create one new Object called Approval Matrix to define all the conditions
4. Execute the below code on submit button.

public class OpportunityApprovalMatrix {
    public static List<Approval.ProcessResult> ApprovalMatrixMatch(Set<Id> OpportunitySet) {
        List<Opportunity> updatedOpptyList = new List<Opportunity>();
        Set<String> countrySet = new Set<String>();
        List<Id> OpptyIds = new List<Id>();

        // Query Opportunities based on the given Opportunity IDs
        List<Opportunity> opptyList = [SELECT Id, Name, Country__c, Status__c, Approver1__c, Approver2__c 
                                       FROM Opportunity WHERE Id IN :OpportunitySet];

        // Clear approvers in Opportunity records and add countries to countrySet
        for (Opportunity opptyRec : opptyList) {
            opptyRec.Approver1__c = ''; // Clear previous approvers
            opptyRec.Approver2__c = '';
            countrySet.add(opptyRec.Country__c); // Add country for filtering
        }

        // Query Approval Matrix records based on countries found in Opportunities
        Map<String, Approval_Matrix__c> approvalMatrixMap = new Map<String, Approval_Matrix__c>();
        for (Approval_Matrix__c approvalMatrix : [SELECT Country__c, Approver1__c, Approver2__c 
                                                  FROM Approval_Matrix__c WHERE Country__c IN :countrySet]) {
            approvalMatrixMap.put(approvalMatrix.Country__c, approvalMatrix);
        }

        // Match Approval Matrix records with Opportunities by Country and update approvers
        for (Opportunity opptyRec : opptyList) {
            Approval_Matrix__c approvalMatrix = approvalMatrixMap.get(opptyRec.Country__c);
            if (approvalMatrix != null) {
                opptyRec.Approver1__c = approvalMatrix.Approver1__c;
                opptyRec.Approver2__c = approvalMatrix.Approver2__c;
                opptyRec.Status__c = 'Open';
                updatedOpptyList.add(opptyRec);
                OpptyIds.add(opptyRec.Id);
            }
        }

        // Update Opportunities with assigned approvers
        if (!updatedOpptyList.isEmpty()) {
            update updatedOpptyList;
        }

        // Prepare and submit approval requests
        List<Approval.ProcessSubmitRequest> submitOpptyList = new List<Approval.ProcessSubmitRequest>();
        for (Id oppId : OpptyIds) {
            Approval.ProcessSubmitRequest req = new Approval.ProcessSubmitRequest();
            req.setComments('Submitting request for approval.');
            req.setObjectId(oppId);
            submitOpptyList.add(req);
        }

        // Process approval requests and return results
        if (!submitOpptyList.isEmpty()) {
            return Approval.process(submitOpptyList);
        } else {
            return new List<Approval.ProcessResult>(); // Return empty list if no submissions
        }
    }
}

The OpportunityApprovalMatrix class in Apex is designed to manage the approval process for a set of Opportunity records based on a custom approval matrix. Here’s an explanation of the code and its working scenarios.

Purpose of the Code

The OpportunityApprovalMatrix class contains a single method, ApprovalMatrixMatch, which:

  1. Matches each Opportunity record to approvers based on the Country__c field.
  2. Updates each Opportunity’s approver fields (Approver1__c and Approver2__c) and sets the status__c field to “Open.”
  3. Submits the Opportunities for approval in bulk.

Code Breakdown

public class OpportunityApprovalMatrix {
    public static List<Approval.ProcessResult> ApprovalMatrixMatch(Set<Id> OpportunitySet) {
  • The class has a public static method ApprovalMatrixMatch that accepts a set of Opportunity IDs and returns a list of Approval.ProcessResult objects, representing the outcome of the approval submission process.

Step 1: Initialize Collections and Query Opportunities

List<Opportunity> updatedOpptyList = new List<Opportunity>();
Set<String> countrySet = new Set<String>();
List<Id> OpptyIds = new List<Id>();
  • These variables are initialized to keep track of Opportunities to update, the countries present in these Opportunities, and the Opportunity IDs for approval submission.
List<Opportunity> opptyList = [SELECT Id, Name, Country__c, Status__c, Approver1__c, Approver2__c 
                               FROM Opportunity WHERE Id IN :OpportunitySet];
  • The code queries Opportunity records based on the provided IDs. It retrieves fields necessary for assigning approvers, setting statuses, and managing the approval process.

Step 2: Clear Previous Approvers and Collect Countries

for (Opportunity opptyRec : opptyList) {
    opptyRec.Approver1__c = ''; // Clear previous approvers
    opptyRec.Approver2__c = '';
    countrySet.add(opptyRec.Country__c); // Add country for filtering
}
  • Clears existing approvers on each Opportunity.
  • Collects the countries into countrySet, which will later be used to retrieve matching approval matrices.

Step 3: Query and Map Approval Matrix Records by Country

Map<String, Approval_Matrix__c> approvalMatrixMap = new Map<String, Approval_Matrix__c>();
for (Approval_Matrix__c approvalMatrix : [SELECT Country__c, Approver1__c, Approver2__c 
                                          FROM Approval_Matrix__c WHERE Country__c IN :countrySet]) {
    approvalMatrixMap.put(approvalMatrix.Country__c, approvalMatrix);
}
  • The code queries Approval_Matrix__c records where the country matches any of the countries in countrySet.
  • Creates a map (approvalMatrixMap) where each country code (Country__c) is the key, and the Approval_Matrix__c record is the value. This allows for efficient lookup.

Step 4: Assign Approvers and Update Opportunities

for (Opportunity opptyRec : opptyList) {
    Approval_Matrix__c approvalMatrix = approvalMatrixMap.get(opptyRec.Country__c);
    if (approvalMatrix != null) {
        opptyRec.Approver1__c = approvalMatrix.Approver1__c;
        opptyRec.Approver2__c = approvalMatrix.Approver2__c;
        opptyRec.Status__c = 'Open';
        updatedOpptyList.add(opptyRec);
        OpptyIds.add(opptyRec.Id);
    }
}
  • For each Opportunity, the approvalMatrixMap is checked for a matching Country__c.
  • If a match is found, the corresponding approvers are assigned to the Opportunity, and the status__c field is set to “Open.”
  • The Opportunity is added to updatedOpptyList for later update, and its ID is added to OpptyIds for approval submission.

Step 5: Update Opportunities

if (!updatedOpptyList.isEmpty()) {
    update updatedOpptyList;
}
  • Updates all Opportunities in updatedOpptyList with the new approver information and status, provided there are records to update.

Step 6: Create and Submit Approval Requests

List<Approval.ProcessSubmitRequest> submitOpptyList = new List<Approval.ProcessSubmitRequest>();
for (Id oppId : OpptyIds) {
    Approval.ProcessSubmitRequest req = new Approval.ProcessSubmitRequest();
    req.setComments('Submitting request for approval.');
    req.setObjectId(oppId);
    submitOpptyList.add(req);
}
  • A list of Approval.ProcessSubmitRequest objects is created for each Opportunity ID in OpptyIds.
  • Each request includes a comment and sets the Opportunity ID as the object for submission.

Step 7: Process and Return Approval Results

if (!submitOpptyList.isEmpty()) {
    return Approval.process(submitOpptyList);
} else {
    return new List<Approval.ProcessResult>(); // Return empty list if no submissions
}
  • The approval requests are processed in bulk, returning a list of Approval.ProcessResult objects that provide information on the status of each submission.
  • If there were no Opportunities to submit, an empty list is returned.

Working Scenarios

  1. Scenario 1: Standard Approval Based on Country
  • The ApprovalMatrixMatch method is called with a set of Opportunity IDs.
  • For each Opportunity, the method assigns approvers based on the country using the Approval_Matrix__c object, then submits each Opportunity for approval.
  • This scenario works well when there’s a clear, direct match between countries in Opportunities and the approval matrix.
  1. Scenario 2: Multiple Opportunities, Same Country
  • When multiple Opportunities share the same country, the ApprovalMatrixMatch method handles all of them by setting the same approvers.
  • This ensures consistency across Opportunities in the same region.
  1. Scenario 3: No Matching Country in Approval Matrix
  • If an Opportunity’s country has no matching Approval_Matrix__c record, the Opportunity is skipped and does not receive approvers.
  • Such records are not added to updatedOpptyList or OpptyIds, and they are not submitted for approval.
  1. Scenario 4: No Opportunities to Process
  • If the provided set of Opportunity IDs is empty or none of the Opportunities match any country in the approval matrix, the method completes without updating or submitting records.
  • In this case, an empty list of Approval.ProcessResult is returned, indicating no actions were taken.

This method is efficient for managing Opportunity approvals based on location-specific criteria, providing flexibility for bulk processing and efficient approval management in Salesforce.