[SalesForce] Reassigning Pending Approval Request thru APEX code (under standard user execution context)

Is there any way to re-assign a pending approval request thru apex code (code executing under standard user context i.e. not as sysadmin or without without sharing)?

I have an Opportunity Approval process that when kicked off will create the approval requests (in Pending status). The approval process and requests will be stored in ProcessInstance & ProcessInstance* sobjects (like ProcessInstanceNode, ProcessInstanceWorkItem etc). Active approval step/ request will have records in ProcessInstanceWorkItem sobject. I've used the following apex code that will reassign a pending approval request to another user and this code works fine as system admin (or as the current actor of the ProcessInstanceWorkitem record i.e. current assigned approver), but fails for all other profiles and users.

List<ProcessInstanceWorkitem> piWorkItemsUpdate = new List<ProcessInstanceWorkitem>();

List<ProcessInstanceWorkitem> piWorkItems = [SELECT Id, ActorId, OriginalActorId, ProcessInstance.TargetObjectId FROM ProcessInstanceWorkitem WHERE ProcessInstance.TargetObjectId = <Opportunity Record ID>];

for(ProcessInstanceWorkitem piWorkItem: piWorkItems){
     piWorkItem.ActorId = <User ID>;
     piWorkItem.OriginalActorId = <User ID>;
     piWorkItemsUpdate.add(piWorkItem);
}

if(!piWorkItemsUpdate.isEmpty()){
   Database.SaveResult[] updateResults = Database.update(piWorkItemsUpdate, false);
}

My requirement is that when a non-admin user changes value in a specific user lookup field in the opportunity record page, the corresponding pending approval request should be assigned to the new user selected in the lookup. So, I had placed the above code in opportunity trigger and this piece of code would run as the user who updates the opportunity record. As given in the following article, the code will work only for system admin or current actor of the ProcessInstanceWorkItem record (current pending approval request):
https://help.salesforce.com/articleView?id=000206576&type=1

I've tried the following options, but none worked:

  • Using Approval.ProcessSubmitRequest or Approval.ProcessWorkitemRequest doesn't help
  • There seems to be no options for enabling/ accessing Sharing settings nor setting OWD for ProcessInstanceWorkitem object or records.
  • Tried 'without sharing' keyword on the apex class
  • Shared all the records of the current assigned approver to the new approver (from Setup > Users page) with Read/ Write permission.

Any help/ ideas?

Best Answer

Although, this is a really late answer, I hope it would help someone looking for a solution to this problem. I came across this work-around in the same week this question was originally posted, but missed out to post it here. [Thanks to my co-worker Frank Dipoma who suggested this approach to me (not sure if he is on this forum)]

Per the standard approval process in salesforce, system admin or the current approver of the approval request have the ability to re-assign the request to another user and most companies don't fiddle over this standard functionality. However, the client that I was working with had a specific requirement where a standard user would update a user lookup field in an opportunity record and all the related pending approval requests should be re-assigned to this new user. The most obvious approach anybody would take is to put the code snippet (given in the question) in a class marked as WITHOUT SHARING, but this will NOT specifically work in case of approval request re-assignment (nor will any of the options mentioned in my question). Anybody interested can try this out.


Note that this workaround exploits a loophole in salesforce (I don't want to call it a bug or defect in salesforce). Since, approvals & approval re-assignments are quite critical for any business, I recommend anybody trying this solution to have a thorough understanding of the use-cases, before adopting this workaround.


Without getting into minute details of the work-around, I'll summarize the approach that worked for me. The idea is that the apex trigger will send the relevant details to a specific email service address and then the inbound email handler code will use the details from the email to process the relevant approval request records.

  • Create an apex class that implements Messaging.InboundEmailHandler and in its handleInboundEmail method, place the code snippet given in the question. (Sample code for the inbound email handler class can be found in the developer guide here, although it isn't related to opportunity object)
  • Create an Email Service Address and associate it with the apex class created as mentioned above. This can be created from Setup > Custom Code > Email Services > New Email Service. After a new email service is created, a email service address needs to be created within. Note that this set-up has other standard email related configurations like accepted FROM addresses, dis-allowing attachments etc. which may be opted.
  • In the apex trigger (opportunity trigger in my case), send an email (SingleEmailMessage) with parent record ID (or opportunity ID) in the email body or subject. This email should be sent to the service address created above.

My memory is a bit hazy on a particular point related to this solution, since its been over an year. The apex code in the inbound handler class will run as the context user specified in the email service, but I don't remember whether the LastModifiedBy of the approval request is updated with this context user name or the actual user who updated the opportunity record.

Related Topic