Although this is very common approach and lots of articles are around on this topic, still I want to delineate the topic in other way. This topic covers complete scenarios for the approval process based on the Apex class.
Agenda of this article:
- Automatically submit the record for approval on the basis of field value.
- Automatically select the next Approver.
- Approve / Reject the record on the basis of field.
Assumptions:
- Opportunity Object is used.
- Approval Process is already set on the Opportunity.
- Field “Next_Approver” will decide that who is going to approve the record.
- There are three steps in the approval process.
- There is no test class written and no check for mandatory fields needed for the trigger, as I have considered positive scenarios only.
Important URLS:
API of Approval Process classes:
- Apex process
- Apex ProcessRequest
- Apex_ProcessResult
- Apex_ProcessSubmitRequest
- Apex_ProcessWorkitemRequest
Steps of Standard approval process defined:
To achieve this, I am going to create the trigger named “AutomateApprove”.
Automatically submit the approval process using trigger – Apex:
Below method is used to automatically submit the approval process using trigger.
public void submitForApproval(Opportunity opp) { // Create an approval request for the Opportunity Approval.ProcessSubmitRequest req1 = new Approval.ProcessSubmitRequest(); req1.setComments('Submitting request for approval automatically using Trigger'); req1.setObjectId(opp.id); req1.setNextApproverIds(new Id[] {opp.Next_Approver__c}); // Submit the approval request for the Opportunity Approval.ProcessResult result = Approval.process(req1); }
Class “ProcessSubmitRequest“ is used to automatically submit the approval process. We need to set following items while submitting the approval process using trigger:
- Comment
- TargetObjectId
- NextApproverIds – if needed. Here Custom logic can be written to dynamically set approver for approval process. In this case I am using the custom field present on the Opportunity.
Automatically approve the approval process using trigger – Apex:
Below method is used to automatically approve the approval process using trigger.
/* * This method will Approve the opportunity */ public void approveRecord(Opportunity opp) { Approval.ProcessWorkitemRequest req = new Approval.ProcessWorkitemRequest(); req.setComments('Approving request using Trigger'); req.setAction('Approve'); req.setNextApproverIds(new Id[] {opp.Next_Approver__c}); Id workItemId = getWorkItemId(opp.id); if(workItemId == null) { opp.addError('Error Occured in Trigger'); } else { req.setWorkitemId(workItemId); // Submit the request for approval Approval.ProcessResult result = Approval.process(req); } }
Class “ProcessWorkitemRequest“ is used to automatically approve the approval process. We need to set following items while submitting the approval process using trigger:
- Comment
- TargetObjectId
- NextApproverIds – if needed
- WorkItemId – Custom code required to get this
Get the WorkItemId for the pending approval process of the Object:
This is the tricky part, if the Submission and approval of the record is done in single code block then it’s very easy to get the WorkItemId of the needed process.
Here the standard code snap provided:
After Submission the approval process using Apex we get the object of class “ProcessResult“.
Approval.ProcessResult result = Approval.process(req1);
And from the class we can get workitemid as :
List<Id> newWorkItemIds = result.getNewWorkitemIds();
And set the id like:
req2.setWorkitemId(newWorkItemIds.get(0));
Other method to get the “WorkItemId” :
The above code was not usable in our scenario as the submission and approval or rejection was done at different level. So I have created following utility method to get the WorkitemId of the supplied Object’s id. Here I have considered that only one workitem will present.
public Id getWorkItemId(Id targetObjectId) { Id retVal = null; for(ProcessInstanceWorkitem workItem : [Select p.Id from ProcessInstanceWorkitem p where p.ProcessInstance.TargetObjectId =: targetObjectId]) { retVal = workItem.Id; } return retVal; }
As you can see, we need to query the object “ProcessInstanceWorkitem“ to get workitemId of the object.
Automatically reject the approval process using trigger – Apex:
Following code is used to reject the approval process using code.
public void rejectRecord(Opportunity opp) { Approval.ProcessWorkitemRequest req = new Approval.ProcessWorkitemRequest(); req.setComments('Rejected request using Trigger'); req.setAction('Reject'); //req.setNextApproverIds(new Id[] {UserInfo.getUserId()}); Id workItemId = getWorkItemId(opp.id); if(workItemId == null) { opp.addError('Error Occured in Trigger'); } else { req.setWorkitemId(workItemId); // Submit the request for approval Approval.ProcessResult result = Approval.process(req); } }
Execution of Approval process using Apex and trigger:
Complete code:
trigger AutomateApprove on Opportunity(After insert, After update) { for (Integer i = 0; i < Trigger.new.size(); i++) { try { if( Trigger.isInsert || (Trigger.new[i].Next_Step__c == 'Submit' && Trigger.old[i].Next_Step__c != 'Submit')) { submitForApproval(Trigger.new[i]); } else if(Trigger.isInsert || (Trigger.new[i].Next_Step__c == 'Approve' && Trigger.old[i].Next_Step__c != 'Approve')) { approveRecord(Trigger.new[i]); } else if(Trigger.isInsert || (Trigger.new[i].Next_Step__c == 'Reject' && Trigger.old[i].Next_Step__c != 'Reject')) { rejectRecord(Trigger.new[i]); } }catch(Exception e) { Trigger.new[i].addError(e.getMessage()); } } /** * This method will submit the opportunity automatically **/ public void submitForApproval(Opportunity opp) { // Create an approval request for the Opportunity Approval.ProcessSubmitRequest req1 = new Approval.ProcessSubmitRequest(); req1.setComments('Submitting request for approval automatically using Trigger'); req1.setObjectId(opp.id); req1.setNextApproverIds(new Id[] {opp.Next_Approver__c}); // Submit the approval request for the Opportunity Approval.ProcessResult result = Approval.process(req1); } /** * Get ProcessInstanceWorkItemId using SOQL **/ public Id getWorkItemId(Id targetObjectId) { Id retVal = null; for(ProcessInstanceWorkitem workItem : [Select p.Id from ProcessInstanceWorkitem p where p.ProcessInstance.TargetObjectId =: targetObjectId]) { retVal = workItem.Id; } return retVal; } /** * This method will Approve the opportunity **/ public void approveRecord(Opportunity opp) { Approval.ProcessWorkitemRequest req = new Approval.ProcessWorkitemRequest(); req.setComments('Approving request using Trigger'); req.setAction('Approve'); req.setNextApproverIds(new Id[] {opp.Next_Approver__c}); Id workItemId = getWorkItemId(opp.id); if(workItemId == null) { opp.addError('Error Occured in Trigger'); } else { req.setWorkitemId(workItemId); // Submit the request for approval Approval.ProcessResult result = Approval.process(req); } } /** * This method will Reject the opportunity **/ public void rejectRecord(Opportunity opp) { Approval.ProcessWorkitemRequest req = new Approval.ProcessWorkitemRequest(); req.setComments('Rejected request using Trigger'); req.setAction('Reject'); Id workItemId = getWorkItemId(opp.id); if(workItemId == null) { opp.addError('Error Occured in Trigger'); } else { req.setWorkitemId(workItemId); // Submit the request for approval Approval.ProcessResult result = Approval.process(req); } } }
Note on possible errors:
1.If you have the “manual Selection of approver” enabled for your approval process/steps then you must specify the approver in the trigger, else you will get an error something like:
“System.DmlException: Process failed. First exception on row 0; first error: REQUIRED_FIELD_MISSING, missing required field: []”
2.If you set the wrong WorkitemId then may get following error:
Process failed. First exception on row 0; first error: INVALID_CROSS_REFERENCE_KEY, invalid cross reference id: []
updated on 25-March-2015
Question :
1. Can we add multiple users (Parallel Approval process) as a aprrover automated using above code?
Ans : No. Logic in above code is that we need to select next approver option as “manual”. Currently we cannot use multiple users manually in approval process, you can vote this idea for this feature support. Only solution is to have multiple steps for each approver.
Leave a Reply