From 362be6aace464d723ea6a9ec1e7b81bfce9f2b52 Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 1 Nov 2022 12:04:21 +0100 Subject: [PATCH 1/6] flow does not invoke the approval req. method --- .../flows/Auto_Approve_Request.flow-meta.xml | 145 ------------------ 1 file changed, 145 deletions(-) delete mode 100644 flow_action_components/ApprovalProcessActions/force-app/main/default/flows/Auto_Approve_Request.flow-meta.xml diff --git a/flow_action_components/ApprovalProcessActions/force-app/main/default/flows/Auto_Approve_Request.flow-meta.xml b/flow_action_components/ApprovalProcessActions/force-app/main/default/flows/Auto_Approve_Request.flow-meta.xml deleted file mode 100644 index 8171bef28..000000000 --- a/flow_action_components/ApprovalProcessActions/force-app/main/default/flows/Auto_Approve_Request.flow-meta.xml +++ /dev/null @@ -1,145 +0,0 @@ - - - - Auto_Approve_Request - - 176 - 398 - ResolveApprovalRequest - apex - CurrentTransaction - - action - - Approve - - - - approvalRequestId - - Extract_Approval_Request_ID.foundString - - - true - - - Extract_Approval_Request_ID - - 176 - 158 - FindText - apex - - Post_Incoming_Vars_to_Chatter - - CurrentTransaction - - inputString - - Email_TextBody - - - - length - - 15 - - - - prefix - - id= - - - - searchType - - prefixSearch - - - true - - - Post_Incoming_Vars_to_Chatter - - 176 - 278 - chatterPost - chatterPost - - Auto_Approve_Request - - CurrentTransaction - - text - - StatusInfo - - - - subjectNameOrId - - $User.Username - - - - type - - User - - - true - - 53.0 - Auto Approve Request {!$Flow.CurrentDateTime} - - - BuilderType - - LightningFlowBuilder - - - - CanvasMode - - AUTO_LAYOUT_CANVAS - - - - OriginBuilderType - - LightningFlowBuilder - - - AutoLaunchedFlow - - 50 - 0 - - Extract_Approval_Request_ID - - - Draft - - StatusInfo - true - Email Text Body: {!Email_TextBody} -Email HTML Body: {!Email_HTMLBody} - -extracted id = {!Extract_Approval_Request_ID.foundString} - - - Email_HTMLBody - String - false - true - false - - - Email_TextBody - String - false - true - false - - From c48df6fe02c1ade0412fd9565fd48d3d3224ad31 Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 1 Nov 2022 12:08:38 +0100 Subject: [PATCH 2/6] invocable approval helper added --- .../classes/InvocableApprovalHelper.cls | 134 ++++++++++++++++++ .../InvocableApprovalHelper.cls-meta.xml | 5 + .../classes/InvocableApprovalHelperTest.cls | 32 +++++ .../InvocableApprovalHelperTest.cls-meta.xml | 5 + 4 files changed, 176 insertions(+) create mode 100644 flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelper.cls create mode 100644 flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelper.cls-meta.xml create mode 100644 flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelperTest.cls create mode 100644 flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelperTest.cls-meta.xml diff --git a/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelper.cls b/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelper.cls new file mode 100644 index 000000000..7682e8fa7 --- /dev/null +++ b/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelper.cls @@ -0,0 +1,134 @@ +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 execute(List requests) { + List results = new List(); + List approvalWorkItems = new List(); + + // 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 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 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) { + String objName = ((Id) recordId).getSobjectType().getDescribe().getLocalName(); + List piws = [ + SELECT + id, + ActorId, + CreatedDate, + OriginalActorId, + OriginalActor.name, + ProcessInstanceId, + ProcessInstance.Status, + ProcessInstance.SubmittedById, + ProcessInstance.ProcessDefinition.TableEnumOrId, + ProcessInstance.ProcessDefinition.Type + 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. Record Id' required=true) + public String recordId; + + @InvocableVariable( + label='2. 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='3. Comments') + public String comments; + + @InvocableVariable(label='4. Approval Request Id (if multiple might exist on same record)') + public String approvalRequestId; + + @InvocableVariable(label='5. Next Approver Ids') + public List 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(); + } + } +} diff --git a/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelper.cls-meta.xml b/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelper.cls-meta.xml new file mode 100644 index 000000000..4b0bc9f38 --- /dev/null +++ b/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelper.cls-meta.xml @@ -0,0 +1,5 @@ + + + 55.0 + Active + diff --git a/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelperTest.cls b/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelperTest.cls new file mode 100644 index 000000000..704b270cb --- /dev/null +++ b/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelperTest.cls @@ -0,0 +1,32 @@ +@isTest +public with sharing class InvocableApprovalHelperTest { + @TestSetup + static void makeData() { + TestHelper.createTestData(); + } + + @isTest + public static void doApproveAction() { + Account acc = new Account(Name='testAcc'); + insert acc; + + // Create Request Object + List requests = new List(); + + InvocableApprovalHelper.Request req = new InvocableApprovalHelper.Request(); + req.action = 'Approve'; + req.comments = 'Test Comments'; + req.recordId = acc.Id; + req.nextApproverIds = new List{ UserInfo.getUserId() }; + + // Add req to requests + requests.add(req); + + // Run Test + Test.startTest(); + List results = InvocableApprovalHelper.execute(requests); + Test.stopTest(); + + System.assertEquals(1, results.size(), 'there should be 1 result for 1 request'); + } +} diff --git a/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelperTest.cls-meta.xml b/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelperTest.cls-meta.xml new file mode 100644 index 000000000..4b0bc9f38 --- /dev/null +++ b/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelperTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 55.0 + Active + From 2f9f4551893d1bbb535e7c6f2a5df281a09b5553 Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 1 Nov 2022 12:18:36 +0100 Subject: [PATCH 3/6] allow non record Id processing --- .../main/default/classes/InvocableApprovalHelper.cls | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelper.cls b/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelper.cls index 7682e8fa7..42b398926 100644 --- a/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelper.cls +++ b/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelper.cls @@ -94,21 +94,21 @@ public with sharing class InvocableApprovalHelper { } public class Request { - @InvocableVariable(label='1. Record Id' required=true) + @InvocableVariable(label='1. Record Id (The current Approval Request Id will be found for you)') public String recordId; + @InvocableVariable(label='2. Approval Request Id (use either this or the Record Id)') + public String approvalRequestId; + @InvocableVariable( - label='2. Action, Valid values: \'Approve\', \'Reject\', \'Removed\' (Removed is for admins only)' + label='3. 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='3. Comments') + @InvocableVariable(label='4. Comments') public String comments; - @InvocableVariable(label='4. Approval Request Id (if multiple might exist on same record)') - public String approvalRequestId; - @InvocableVariable(label='5. Next Approver Ids') public List nextApproverIds; } From daf48bddef6d967caaf55b754b9268e9d39ec64e Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 1 Nov 2022 12:22:32 +0100 Subject: [PATCH 4/6] remove old not working approval request invocable --- .../classes/ResolveApprovalRequest.cls | 100 ------------------ .../ResolveApprovalRequest.cls-meta.xml | 5 - .../classes/ResolveApprovalRequestsTest.cls | 36 ------- 3 files changed, 141 deletions(-) delete mode 100644 flow_action_components/ApprovalProcessActions/force-app/main/default/classes/ResolveApprovalRequest.cls delete mode 100644 flow_action_components/ApprovalProcessActions/force-app/main/default/classes/ResolveApprovalRequest.cls-meta.xml delete mode 100644 flow_action_components/ApprovalProcessActions/force-app/main/default/classes/ResolveApprovalRequestsTest.cls diff --git a/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/ResolveApprovalRequest.cls b/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/ResolveApprovalRequest.cls deleted file mode 100644 index f6dc2b184..000000000 --- a/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/ResolveApprovalRequest.cls +++ /dev/null @@ -1,100 +0,0 @@ -public with sharing class ResolveApprovalRequest { - @InvocableMethod - public static List execute(List requests) { - List responseWrapper = new List(); - String approverId = ''; - for (Requests curRequest : requests) { - Results response = new Results(); - Approval.ProcessWorkitemRequest approvalWorkItem = new Approval.ProcessWorkitemRequest(); - approvalWorkItem.setComments(curRequest.comments); - approvalWorkItem.setAction(curRequest.action); - approvalWorkItem.setNextApproverIds(curRequest.nextApproverIds); - if(String.isBlank(curRequest.approvalRequestId)){ - approverId = getapprovalRequestID(curRequest.recordId,curRequest.ObjName).Id; - } - - approvalWorkItem.setWorkitemId(approverId); - - system.debug('approvalWorkItem ' + approvalWorkItem); - - // Submit the request for approval - Approval.ProcessResult[] approvalResult = Approval.process(approvalWorkItem); - - system.debug('ApprovalResult ' + approvalResult); - - // Verify the results - System.assert(approvalResult.isSuccess(), 'Result Status:'+ approvalResult.isSuccess()); - - System.assertEquals( - 'Approved', approvalResult.getInstanceStatus(), - 'Instance Status'+approvalResult.getInstanceStatus()); - - - response.isSuccess = approvalResult.isSuccess(); - Database.Error[] errors = approvalResult.getErrors(); - response.errorString = getErrorInfo(errors); //warning. only hacking out the first error - response.currentApprovalProcessStatus = approvalResult.getInstanceStatus(); - - //Wrap the Results object in a List container (an extra step added to allow this interface to also support bulkification) - responseWrapper.add(response); - } - return responseWrapper; - - } - - public static String getErrorInfo(Database.Error[] errors) { - String errorStrings = ''; - if (errors != null) { - for(Database.Error error : errors) { - errorStrings = errorStrings + ' ' + error.getMessage(); - } - } - return errorStrings; - } - - public static ProcessInstanceWorkitem getapprovalRequestID (String recordId,String objName){ - return [select id,ActorId, CreatedDate, OriginalActorId,OriginalActor.name, ProcessInstanceId, ProcessInstance.Status, ProcessInstance.SubmittedById, ProcessInstance.ProcessDefinition.TableEnumOrId,ProcessInstance.ProcessDefinition.Type - from ProcessInstanceWorkitem where ProcessInstance.TargetObjectId =:recordId - and ProcessInstance.ProcessDefinition.Type ='Approval' - and ProcessInstance.ProcessDefinition.TableEnumOrId= :objName - and ProcessInstance.Status ='Pending' limit 1]; - } - - public class InvocableErrorException extends Exception { - } - - - public class Requests { - - @InvocableVariable - public String recordId; - - @InvocableVariable - public String objName; - - @InvocableVariable - public String approvalRequestId; - - @InvocableVariable - public String comments; - - @InvocableVariable - public String action; // 'Approved', 'Rejected'. 'Removed' https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_ProcessWorkitemRequest.htm#apex_Approval_ProcessWorkitemRequest_setAction - - @InvocableVariable - public List nextApproverIds; - } - - public class Results { - - @InvocableVariable - public Boolean isSuccess; - - @InvocableVariable - public String errorString; - - @InvocableVariable - public String currentApprovalProcessStatus; //Approved, Rejected, Removed or Pending. - - } -} diff --git a/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/ResolveApprovalRequest.cls-meta.xml b/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/ResolveApprovalRequest.cls-meta.xml deleted file mode 100644 index dd61d1f91..000000000 --- a/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/ResolveApprovalRequest.cls-meta.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - 52.0 - Active - diff --git a/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/ResolveApprovalRequestsTest.cls b/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/ResolveApprovalRequestsTest.cls deleted file mode 100644 index 66689e215..000000000 --- a/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/ResolveApprovalRequestsTest.cls +++ /dev/null @@ -1,36 +0,0 @@ -@isTest -public with sharing class ResolveApprovalRequestsTest { - // Create New Request and Test Error Response - @isTest(SeeAllData=true) - public static void executeTest() { - // Get Current User Id - String userId = UserInfo.getUserId(); - - // Find Acme (Sample) Account - Account a = [SELECT Id FROM Account WHERE Name = 'Acme (Sample)']; - - // Find Process Definition - ProcessDefinition pd = [SELECT Id FROM ProcessDefinition WHERE DeveloperName = 'Test_Process_Definition']; - - // Find New Process Instance - ProcessInstance pi = [SELECT Id FROM ProcessInstance WHERE ProcessDefinitionId = :pd.Id LIMIT 1]; - - // Create Request Object - List requests = new List(); - ResolveApprovalRequest.Requests req = new ResolveApprovalRequest.Requests(); - req.action = 'Approve'; - req.comments = 'Test Comments'; - req.recordId = a.Id; - req.objName = 'Account'; - req.nextApproverIds = new List{userId}; - - // Add req to requests - requests.add(req); - - // Run Test - Test.startTest(); - //ResolveApprovalRequest r = new ResolveApprovalRequest(); - ResolveApprovalRequest.execute(requests); - Test.stopTest(); - } -} \ No newline at end of file From 9edd4668dfd95c66f4c7f947a43c23af63b2d893 Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 3 Nov 2022 10:34:32 +0100 Subject: [PATCH 5/6] removed testhelper --- .../main/default/classes/InvocableApprovalHelperTest.cls | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelperTest.cls b/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelperTest.cls index 704b270cb..d11eb3969 100644 --- a/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelperTest.cls +++ b/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelperTest.cls @@ -1,15 +1,10 @@ @isTest public with sharing class InvocableApprovalHelperTest { - @TestSetup - static void makeData() { - TestHelper.createTestData(); - } - @isTest public static void doApproveAction() { - Account acc = new Account(Name='testAcc'); + Account acc = new Account(Name = 'testAcc'); insert acc; - + // Create Request Object List requests = new List(); From feb654d9d3d2d5dd384726efbe214f7c40fecbf8 Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 3 Nov 2022 16:54:56 +0100 Subject: [PATCH 6/6] add read all override and permission check --- .../classes/InvocableApprovalHelper.cls | 86 ++++++++++++------- 1 file changed, 57 insertions(+), 29 deletions(-) diff --git a/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelper.cls b/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelper.cls index 42b398926..a5e684f00 100644 --- a/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelper.cls +++ b/flow_action_components/ApprovalProcessActions/force-app/main/default/classes/InvocableApprovalHelper.cls @@ -69,43 +69,55 @@ public with sharing class InvocableApprovalHelper { } public static Id getapprovalRequestId(String recordId) { - String objName = ((Id) recordId).getSobjectType().getDescribe().getLocalName(); - List piws = [ - SELECT - id, - ActorId, - CreatedDate, - OriginalActorId, - OriginalActor.name, - ProcessInstanceId, - ProcessInstance.Status, - ProcessInstance.SubmittedById, - ProcessInstance.ProcessDefinition.TableEnumOrId, - ProcessInstance.ProcessDefinition.Type - 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; + return new NoSharing().getapprovalRequestId(recordId); } - public class Request { - @InvocableVariable(label='1. Record Id (The current Approval Request Id will be found for you)') - public String recordId; - - @InvocableVariable(label='2. Approval Request Id (use either this or the Record Id)') - public String approvalRequestId; + public without sharing class NoSharing { + public Id getapprovalRequestId(String recordId) { + String objName = ((Id) recordId).getSobjectType().getDescribe().getLocalName(); + String userId = UserInfo.getUserId(); + List 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='3. Action, Valid values: \'Approve\', \'Reject\', \'Removed\' (Removed is for admins only)' + 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; @@ -131,4 +143,20 @@ public with sharing class InvocableApprovalHelper { 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(); + } }