Block Reassign in Approval Process

I have seen this issue in several cases - standard functionality of salesforce approval process is that the current approver may reassign the approval to any other user. There is no option in the system to have validation on this action, or even to remove completely the reassign button.

The main issue is that the reassign process, doesn't update the record in approval, it's only update the internal approval record - object: ProcessInstanceWorkItem - and of course, you cannot have trigger/workflow/validation on this object.

It was raised as Idea, and 1 option to solve this issue is by completely replacing the standard Approval History related list with visualforce page. The advantage of such solution is to have full flexibility on any of the approval actions, the disadvantage is that you are breaking big part of the standard functionality, this will raise your maintainace effort and in case Salesforce will add new functionality for the process it might break your code.

Alternative approach that I found is to leave the Reassign functionality as is, and instead block the Approval/Reject of non-authorize user.

This is quite easy to implement:

Step 1:
Create 2 fields on the relevant object that the approval related to (Case object in this example).
1.Last Approval Action Date ((Date/Time)
2.Last Approver User Id (Text)

Step 2:
Create 2 Fields Update:
1.Update Last Approval Action Date
Value: Now()


2.Update Last Approver User Id
Value: $User.Id



Step 3:
Add those 2 fields update on every approve/reject step in your approval process. Meaning, after any approval/rejection step the process will update the Date/Time and the user Id who runs the action.

Step 4:
Write trigger on the object that related to the approval process.
The trigger will block the approval/rejection if the user who run the process is not the original approver for the record


trigger CaseTrigger on Case (after update) {  

 //Per each case the user who run the approval/rejection  
 map<Id, Id> m_caseLastUser = new map<id, Id>();  

 for(case caseRecord : trigger.new){  

  //Relevant records are the one that Last Approval Action Date was changed  
  if(caseRecord.Last_Approval_Action_Date__c != trigger.oldMap.get(caseRecord.id).Last_Approval_Action_Date__c){  
   m_caseLastUser.put(caseRecord.Id, caseRecord.Last_Approval_User_Id__c);  
  }  
 }  

 if(!m_caseLastUser.isEmpty()){  

  //Loop over the ProcessInstanceWorkedItem of the relevant records  
  for( ProcessInstance pi : [SELECT Id, TargetObjectId , ( SELECT Id, StepStatus, Comments, ActorId, Actor.name,  
         OriginalActorId , OriginalActor.name FROM StepsAndWorkitems)  
         FROM ProcessInstance where TargetObjectId IN :m_caseLastUser.keySet()]){  

   for(ProcessInstanceHistory pih : pi.StepsAndWorkitems){  

    //If the Approver (Actor) on the record is the same as the Last Approval User from Case  
    //and the Approver is not the OriginalActor, raise on error  
    if(pih.ActorId == m_caseLastUser.get(pi.TargetObjectId)  
     && pih.ActorId != pih.OriginalActorId){  

     trigger.newMap.get(pi.TargetObjectId).addError('You are not the original approver. Please reassign it back to: '+ pih.OriginalActor.name);  
    }  
   }  
  }  
 }  
}  


Note, this logic can be enhance further per requirement.
For example, it's possible in addition to send the approval back to the original actor automatically by updating the ProcessInstanceWorkedItem record.
Other option is to run a scheduler every hour that will find any approval that the actual approver is no the original approver - in such cases if the record is still pending it can simply reassign it back, if it's already approver it can re-submit the record and assign it again to the user who reassign his approval at first.


Retire of Permission on Profiles

If you are working as a Salesforce admin/developer you've probably heard somewhere that Salesforce is planning to make a significant cha...