The class below has a method that runs on an After Update Trigger for a custom object. It attempts to automatically Reject the approval process when a picklist field (Work_Sub_Status__c) on its Target Object gets a certain value ("Cancelled"). It compiles just fine, but when I test it, it throws an INVALID_CROSS_REFERENCE_KEY error when calling the Approval.process() method. I worked down a list of possible reasons why this could be:
Was I using the wrong Id type for setWorkItemId()? No, the Id is definitely that of a ProcessInstanceWorkItem, and when I test and look at the item in my org, it is an open, actionable work item that I can choose to Approve or Reject manually.
When I make the query for a ProcessInstanceWorkItem, am I retrieving the wrong one? No - when testing, there is only one ProcessInstanceWorkItem to query for on the record's ProcessInstance, and as above, it is actionable. There is no way that the query could get the wrong item.
Is there some other field that is the root cause of this issue? This doesn't seem possible, as the Id in setWorkitemId() is the only Id field that I am providing the Approval.process() method.
I have tried tons of different methods to fix this, mostly from online recommendations. At this point I would be extremely surprised if you could dig up another StackOverflow or Salesforce Developer Forum post that is suggesting something that I have not already tried (and that did not work).
The code: (please ignore the sloppy queries and printing -- most of this was for debugging, and I will clean it up once it works)
public class CreditApprovalAfterUpdateHandler {
public static void run(Map<Id,Credit_Approval__c> caMap){
if (caMap.isEmpty()) return;
ProcessInstance pi;
ProcessInstanceWorkItem piWorkItem;
List<Approval.ProcessWorkitemRequest> pWorkItemRequestList = new List<Approval.ProcessWorkitemRequest>();
for (Id caId : caMap.keyset()){
if (caMap.get(caId).Work_Sub_Status__c == 'Cancelled'){
pi = [SELECT Id, Status FROM ProcessInstance WHERE TargetObjectId = :caId AND Status = 'Pending' LIMIT 1];
System.debug('Process Instance: ' + pi);
List<ProcessInstanceWorkItem> lol = [SELECT Id FROM ProcessInstanceWOrkItem WHERE ProcessInstanceId = :pi.Id];
printList(lol);
piWorkItem = [SELECT Id, ProcessInstanceId, OriginalActorId, ActorId, IsDeleted, CreatedDate, CreatedById, SystemModstamp
FROM ProcessInstanceWorkItem WHERE ProcessInstanceId = :pi.Id ORDER BY SystemModStamp DESC LIMIT 1];
if (piWorkItem != null){
Approval.ProcessWorkitemRequest pWorkItemRequest = new Approval.ProcessWorkitemRequest();
pWorkItemRequest.setWorkitemId(piWorkItem.Id);
pWorkItemRequest.setAction('Reject');
//pWorkItemRequest.setComments('This record was cancelled, so the approval process was recalled.');
//pWorkItemRequest.setNextApproverIds(new Id[]{UserInfo.getUserId()});
pWorkItemRequestList.add(pWorkItemRequest);
}
}
}
Approval.ProcessResult[] resultList = Approval.process(pWorkItemRequestList);
for (Approval.ProcessResult result : resultList){
System.debug('Success: ' + result.isSuccess());
System.debug('Status afterward: ' + result.getInstanceStatus());
}
}
private static void printList(List<ProcessInstanceWorkItem> input){
for (integer i = 0; i < input.size(); i++){
System.debug('ProcessInstanceWorkItem ' + i + ': ' + input[i]);
}
}
}
And the error message:
CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, CreditApprovalTrigger: execution of AfterUpdate caused by: System.DmlException: Process failed. First exception on row 0; first error: INVALID_CROSS_REFERENCE_KEY, invalid cross reference id: []