Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invocable approval helper #1252

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
public with sharing class InvocableApprovalHelper {
@InvocableMethod(
label='Approval Request (Action)'
description='Submit Approval Request Actions via Flow action.'
category='Approvals'
iconName='slds:standard:approval'
)
public static List<Result> execute(List<Request> requests) {
List<Result> results = new List<Result>();
List<Approval.ProcessWorkitemRequest> approvalWorkItems = new List<Approval.ProcessWorkitemRequest>();

// make ProcessWorkitemRequest from the submitted requests
for (Request curRequest : requests) {
Approval.ProcessWorkitemRequest approvalWorkItem = new Approval.ProcessWorkitemRequest();

approvalWorkItem.setAction(curRequest.action);

if (String.isNotBlank(curRequest.comments)) {
approvalWorkItem.setComments(curRequest.comments);
}

if (curRequest.nextApproverIds != null && !curRequest.nextApproverIds.isEmpty()) {
approvalWorkItem.setNextApproverIds(curRequest.nextApproverIds);
}

approvalWorkItem.setWorkitemId(
String.isBlank(curRequest.approvalRequestId)
? getApprovalRequestId(curRequest.recordId)
: curRequest.approvalRequestId
);

system.debug('approvalWorkItem ' + approvalWorkItem);
approvalWorkItems.add(approvalWorkItem);
}

// process all requests at once (allOrNone = false errors will be returned to the flow)
List<Approval.ProcessResult> approvalResults = Approval.process(approvalWorkItems, false);

// Process the results
Integer i = 0;
for (Request curRequest : requests) {
Approval.ProcessResult approvalResult = approvalResults[i];
// Submit the request for approval

system.debug('ApprovalResult ' + approvalResult);

//Wrap the Results object in a List container (an extra step added to allow this interface to also support bulkification)
results.add(new Result(approvalResult));
i++;
}

// return the results to the flow
return results;
}

// create a string from the errors (if any)
public static String getErrorInfo(List<Database.Error> errors) {
if (errors == null || errors.isEmpty()) {
// no errors
return null;
}

String errorStrings = 'There was an error processing the approval request:';
for (Database.Error error : errors) {
errorStrings += '\n' + error.getMessage();
}

return errorStrings;
}

public static Id getapprovalRequestId(String recordId) {
return new NoSharing().getapprovalRequestId(recordId);
}

public without sharing class NoSharing {
public Id getapprovalRequestId(String recordId) {
String objName = ((Id) recordId).getSobjectType().getDescribe().getLocalName();
String userId = UserInfo.getUserId();
List<ProcessInstanceWorkitem> piws = [
SELECT Id
FROM ProcessInstanceWorkitem
WHERE
ProcessInstance.TargetObjectId = :recordId
AND ProcessInstance.ProcessDefinition.Type = 'Approval'
AND ProcessInstance.ProcessDefinition.TableEnumOrId = :objName
AND ProcessInstance.Status = 'Pending'
AND ActorId = :UserInfo.getUserId()
LIMIT 1
];

// the user is allowed to approve/reject if the user is not a listed approver but has read all to the approval object
if (piws.isEmpty() && hasReadAllPermission(objName)) {
piws = [
SELECT id
FROM ProcessInstanceWorkitem
WHERE
ProcessInstance.TargetObjectId = :recordId
AND ProcessInstance.ProcessDefinition.Type = 'Approval'
AND ProcessInstance.ProcessDefinition.TableEnumOrId = :objName
AND ProcessInstance.Status = 'Pending'
LIMIT 1
];
}
return piws.isEmpty() ? null : piws[0].Id;
}
}

public class Request {
@InvocableVariable(
label='1. Action, Valid values: \'Approve\', \'Reject\', \'Removed\' (Removed is for admins only)'
required=true
)
public String action; // 'Approve', 'Reject'. 'Removed' https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_ProcessWorkitemRequest.htm#apex_Approval_ProcessWorkitemRequest_setAction

@InvocableVariable(label='2. Record Id (The current Approval Request Id will be found for you)')
public String recordId;

@InvocableVariable(label='3. Approval Request Id (use either this or the Record Id)')
public String approvalRequestId;

@InvocableVariable(label='4. Comments')
public String comments;

@InvocableVariable(label='5. Next Approver Ids')
public List<String> nextApproverIds;
}

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

@InvocableVariable(label='Error')
public String errorString;

@InvocableVariable(
label='Current Approval Process Status returns: \'Approved\', \'Rejected\', \'Removed\', \'Pending\')'
)
public String currentApprovalProcessStatus; //Approved, Rejected, Removed or Pending.

public Result(Approval.ProcessResult approvalResult) {
this.isSuccess = approvalResult.isSuccess();
this.errorString = getErrorInfo(approvalResult.getErrors()); //warning. only hacking out the first error
this.currentApprovalProcessStatus = approvalResult.getInstanceStatus();
}
}

// report whether a given user has 'Read all' permission on a particular type of sObject
public static Boolean hasReadAllPermission(String sObjectName) {
return ![
SELECT Id
FROM PermissionSetAssignment
WHERE
PermissionSetId IN (
SELECT ParentId
FROM ObjectPermissions
WHERE SObjectType = :sObjectName AND PermissionsViewAllRecords = TRUE
)
AND Assignee.Id = :UserInfo.getUserId()
]
.isEmpty();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>52.0</apiVersion>
<apiVersion>55.0</apiVersion>
<status>Active</status>
</ApexClass>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@isTest
public with sharing class InvocableApprovalHelperTest {
@isTest
public static void doApproveAction() {
Account acc = new Account(Name = 'testAcc');
insert acc;

// Create Request Object
List<InvocableApprovalHelper.Request> requests = new List<InvocableApprovalHelper.Request>();

InvocableApprovalHelper.Request req = new InvocableApprovalHelper.Request();
req.action = 'Approve';
req.comments = 'Test Comments';
req.recordId = acc.Id;
req.nextApproverIds = new List<String>{ UserInfo.getUserId() };

// Add req to requests
requests.add(req);

// Run Test
Test.startTest();
List<InvocableApprovalHelper.Result> results = InvocableApprovalHelper.execute(requests);
Test.stopTest();

System.assertEquals(1, results.size(), 'there should be 1 result for 1 request');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>55.0</apiVersion>
<status>Active</status>
</ApexClass>

This file was deleted.

This file was deleted.

Loading