Display the Approval History on a Approval Notification

September 30, 2014 Appirio

Contributed by : Asset Library Team


Approval Processes are a very powerful Salesforce feature. Although we cannot dynamically create an approval processes, we can certainly do things like querying approval history, finding next set of approvers, and so on. In this post, we will query the Approval Process object and show the approval process history in an approval email.

Scenario: We have a (custom) candidate object, which holds details for a possible new hire. We have implemented an approval process on Candidate where every candidate goes through two interview rounds before being routed to a Hiring Manager to make the final decision. Each step is captured as a Approval Step

The tricky requirement is that the Hiring Manager needs to see the previous approval history and comments in the approval email.

To solve this business problem, we need to develop a custom Visualforce template, assign it to the Approval process, and dynamically display the history on email template.

Lets take a look at the Approval Process Object Model. Below is the Approval Process Object Model:

  1. Process Definition and Process Node objects act as a template and store the master configurations for Approval Process itself.
  2. Process Instance, Process Instance Step, and Process Instance Node records are created for each record that enters the approval process.

First, we need to set a custom Visualforce Email Template in the Approval


In the Approval Email Template, we need to add a custom Visualforce component with the following snippets


[snippet caption=”Approval Email Template”]

<messaging:emailTemplate subject=”Please approve the Candidate Record” recipientType=”User” relatedToType=”Candidate__c”>

<messaging:htmlEmailBody >

A new candidate record has been submitted for approval.



Previous approval history

<c:ApprovalItem recordId=”{!relatedTo.Id}” />



[snippet caption=”Apex Component”]

<apex:component controller=”ApprovalItemController” access=”global”>

<apex:attribute assignTo=”{!relatedRecordId}” name=”recordId” description=”The record Id for which the template is being generated” type=”String” />

<apex:dataTable value=”{!AllApprovalStep}” var=”step”>

<apex:column colspan=”2″ headerValue=”Action” breakbefore=”true”>

<apex:outputField value=”{!step.nodeStep.Name}” />

<apex:outputText value=”Submitted for Approval” rendered=”{!ISNULL(step.nodeStep)}” />


<apex:column headerValue=”Status”>

<apex:outputField value=”{!step.instanceStep.StepStatus}” />


<apex:column headerValue=”Date”>

<apex:outputField value=”{!step.instanceStep.CreatedDate}” />


<apex:column headerValue=”Assigned To”>

<apex:outputField value=”{!step.instanceStep.CreatedById}” />


<apex:column headerValue=”Actual Approver”>

<apex:outputField value=”{!step.instanceStep.ActorId}” />


<apex:column headerValue=”Comments”>

<apex:outputField value=”{!step.instanceStep.Comments}” />




Now we query the Process Instance and

[snippet caption=”Apex code to query Approval Process Status”]

public with sharing class ApprovalItemController {


public String relatedRecordId {get;set;}



//Main method called by the controller


public List<ApprovalStep> getAllApprovalStep() {

List<ApprovalStep> lstApprovalStep = new List<ApprovalStep>();


ProcessInstance processInstance = getProcessInstance();

Map<Id,ProcessNode> mpProcessNode = getProcessNode(processInstance);


if(processInstance == null) return lstApprovalStep;


for(ProcessInstanceStep stepInstance : processInstance.Steps){

ApprovalStep approvalStep = new ApprovalStep(stepInstance,mpProcessNode.get(stepInstance.StepNodeID));



return lstApprovalStep;




//get All Process Nodes attached to Process Instance


public Map<Id,ProcessNode> getProcessNode(ProcessInstance processInstance){

if(processInstance == null) return null;


return new Map<Id,ProcessNode>([SELECT Id,Name FROM ProcessNode

WHERE ProcessDefinitionId = :processInstance.ProcessDefinitionId]);




//Get the Process Instance Attached to the Record    //======================================================================

private ProcessInstance getProcessInstance(){

List<ProcessInstance> lstProcessInstance = [SELECT Id, ProcessDefinitionID,

(Select StepStatus,StepNodeId, OriginalActor.Name,ActorId, Actor.Name, Comments,

CreatedDate, CreatedById,CreatedBy.Name FROM Steps ORDER BY CreatedDate DESC)

FROM ProcessInstance

WHERE TargetObjectId = :relatedRecordId];

if(lstProcessInstance.size() == 0) {

return null;


return lstProcessInstance[0];






//Inner Class : Approval Process Step that holds the Step Notes and Approver Comments and Details


public class ApprovalStep {

public ProcessInstanceStep instanceStep {get;set;}

public ProcessNode nodeStep {get;set;}


public ApprovalStep(ProcessInstanceStep instanceStep,ProcessNode node){

this.instanceStep = instanceStep;

this.nodeStep = node;





Finally, when the email for approval goes out, it will contain the previous approval history:


In conclusion, customizing the Approval Assignment email template provides a great opportunity to dynamically alter text on the template depending on the Approver or Approval Step.





Previous Article
Importing MongoDB Data into Analytics Cloud Using Talend
Importing MongoDB Data into Analytics Cloud Using Talend

The Analytics Cloud is only as good as the data you put in it. BI implementations often require months to p...

Next Article
Borrowing an SFDC session for Chrome Extension
Borrowing an SFDC session for Chrome Extension

By Bryan Leboff (@leboff) Recently I was finding myself doing a lot testing in a Salesforce sandbox as a bu...