Salesforce Apex & Flow

There’s a new tool (new to me at least) that I’ve discovered through a mention of it by one of our Technical Sales reps called Flow. I remember at Dreamforce 2010, it was called Visual Workflow and it was something brand new with the promise of bringing complex business logic to the Point and Click crowd. It looks like the product team has been working hard, because it has a new interface and a very bright future.

I was asked (volunteered actually) to create a way to allow certain associates the ability to vote on an award nomination. At first, I figured that one person would do the voting for a given nomination, but later was told that the entire 8-person panel needed to be able to vote on each nomination.

I thought about creating a section for each voter and then use a Trigger to accumulate the scores into an overall score, but that seemed cumbersome and not very user-friendly for those voters who were near the bottom of the page layout.

Then I had an epiphany when I realized that Flow could help me out. I envisioned a simple presentation of numeric picklists that could be launched via a custom button. I knew from my research that a Flow could read/update/delete any object in Salesforce, but I wasn’t sure how to get data into the Flow so I could update the voting results.

So, I turned to Mr. Google and found a few hints, but nothing definitive. So, I started with what I learned to be the building blocks:

  • VisualForce page to present the flow
  • Controller Classes to get the needed data into variables
  • A simple flow that contained the needed elements

The trick, as it turns out, is hooking all of these things together. Before we get to the code, here are the design considerations I was trying to solve and then the solutions I developed.

Challenge #1 | Unknown number of voters
This design challenge was centered around finding a way to allow reviewers the ability to vote without creating a separate set of fields for each reviewer.

That’s where the flow comes in – it is run via a button on the nomination record and updates the overall nomination voting scores as the flow is completed. This allows any reviewer who has access to the voting button the opportunity to vote on the nomination without any additional code or modification to the app.

Challenge #2 | Restrict reviewer to one vote
It is important to not allow skewing of the results by allowing a reviewer to vote more than once.

The reviewer User IDs are stored as a comma delimited string in a text field. This string is converted to an Apex List using a standard SPLIT function. This list is then loaded into an Apex Set, which only accepts unique entries. The List’s size (number of entries) is then compared to the Set’s size and if they are the same, then no duplicates were found and indicates that the reviewer had not previously completed a vote on this nomination. If they are of different sizes, this would indicate a duplicate in the List entries and therefore indicate the reviewer had previously completed a vote on this nomination.

There is a Boolean variable [voterFound] that is initialized to false. If the sizes don’t match, the variable is set to true. This variable is passed into the flow where it is used to determine the branching between the ability to vote and a directive to return to the nomination record.

Challenge #3 | Get nomination details into the Flow
The nomination information needs to be known by the flow so it can properly update the new voting scores in its update step.

In the Visualforce page and between the <flow> tags, Apex Params are set to values created in the Controller. These variables have a counterpart in the Flow that is named exactly the same. When the Flow is initialized, these parameters are used to set the Flow variables so calculations can take place in the Flow.

Challenge #4 | Aggregate all votes and produce an overall score
The votes for each reviewer need to be added to the values of all previous votes. The individual votes for P.A.C.T. are added up to comprise the Overall Score, which is also updated with each new vote.

Using formulas and variables in the Flow, after voting has occurred, the scores are added to the values passed in from the Controller and the new values are used to update the Nomination record in the Flow’s Record Update step.

I know, enough with the background, let’s see the code. Here you go:


public with sharing class VotingController {
  public static Impact_Review__c theReview {get; set;}
  public static Integer newPScore {get; set;}
  public static Integer newAScore {get; set;}
  public static Integer newCScore {get; set;}
  public static Integer newTScore {get; set;}
  public static List<String> voters {get; set;}
  public static Set<String> voterSet {get; set;}
  public static String voterList {get; set;}
  public static String revID {get; set;}
  public static String voterID {get; set;}
  public static Boolean voterFound {get; set;}
  public static Integer curPVal {get; set;}
  public static Integer curAVal {get; set;}
  public static Integer curCVal {get; set;}
  public static Integer curTVal {get; set;}
  public static Integer curOVal {get; set;}

  public VotingController(ApexPages.StandardController stdController){
    //Initialize the boolean to false    
    voterFound = false;

  //Get the ID of the user voting and determine if they've voted before. If not, add their ID to the string. If so, set the boolean to true.  
  private void getVoter(){
    voterID = UserInfo.getUserID();
    voterSet = new Set<String>();
    if (voters.size() == voterSet.size()) {
      Integer ctr = 0;
      for (String v:voters) {
        if (voters.size() == 1) {
          voterList = v;
        } else {
          if (ctr == 0) {
            voterList = v;
          } else {
            voterList = voterList + ',' + v;
          ctr = ctr + 1;
    } else {
      voterFound = true;

  //Look up the review the user just came from via the URL parameter
  private void getReview(){
    revID = ApexPages.CurrentPage().getParameters().get('id');
    theReview = [Select i.Impact_Review_Voters__c, i.Impact_Review_TVal__c, i.Impact_Review_Reviewer_Notes__c, i.Impact_Review_PVal__c, i.Impact_Review_Overall_Score__c, i.Impact_Review_CVal__c, i.Impact_Review_AVal__c From Impact_Review__c i WHERE i.ID =: revID];
    voters = new List<String>();
    if (theReview.Impact_Review_Voters__c != null) {
      voters = theReview.Impact_Review_Voters__c.split(',');
    curPVal = Integer.valueOf(theReview.Impact_Review_PVal__c);
    curAVal = Integer.valueOf(theReview.Impact_Review_AVal__c);
    curCVal = Integer.valueOf(theReview.Impact_Review_CVal__c);
    curTVal = Integer.valueOf(theReview.Impact_Review_TVal__c);
    if (Integer.valueOf(theReview.Impact_Review_Overall_Score__c) == null){
      curOVal = 0;
    } else {
      curOVal = Integer.valueOf(theReview.Impact_Review_Overall_Score__c);

VisualForce Page:

<apex:page docType="html-5.0" standardController="Impact_Review__c" extensions="VotingController" sidebar="false"> 
<table width="350" align="center">
    <td style="padding-top: 10px; border-top: #DDDDDD; width: 100%;">
      <flow:interview name="Impact_Review_Voting_Flow" finishLocation="/{!revID}">
        <apex:param name="voterFound" value="{!voterFound}" />
        <apex:param name="revID" value="{!revID}" />
        <apex:param name="curPVal" value="{!curPVal}" />
        <apex:param name="curAVal" value="{!curAVal}" />
        <apex:param name="curCVal" value="{!curCVal}" />
        <apex:param name="curTVal" value="{!curTVal}" />
        <apex:param name="curOVal" value="{!curOVal}" />
        <apex:param name="voterList" value="{!voterList}" />

NOTE: Keep in mind that the name of your params must equal exactly the variables you create in the Flow or it absolutely will not work. This is the key to the whole shootin’ match.

Flow Diagram:

Salesforce Voting Flow

I can’t show you everything about the flow, but you get the gist here. The decision is based on the Boolean set in the controller and there are formulas that take the voting scores from the params passed in and add them to the scores the user selects in the voting options screen. Then the update record takes those new values and updates the nomination record based on the record ID passed in as a param.

It may not be the most elegant solution or follow best practices, but it IS my first foray into Flow and it is working like a champ. You  know what they say, “Done is better than perfect.”