Salesforce Flow and Product Schedules









One of the very first Salesforce projects I worked involved using Product Schedules. Amongst the client’s requirements was to have the schedule dates move automatically if the Close Date of the Opportunity changed. This couldn’t be done by configuration alone, so I had to ask a friendly Developer to create some code (thanks, Inês Costa!).


But now, using Workflow and Flow, there is a configuration option. This post is to show how you can tackle this requirement today with clicks only. It is a great example of how Flow is changing the life of the Salesforce Administrator.

What are Product Schedules?

Companies selling products where the revenue is recognised over time should be using Product Schedules in Salesforce. Details of implementing Product Schedules can be found here:

When Dates Move

When a Product (with a schedule set up) is added to an Opportunity, Salesforce automatically sets up Product Schedule records. These are records that have a date, quantity and revenue. The dates are based on the Opportunity Line Item’s Date or – if that is blank – the Opportunity’s Close Date. If the Close Date subsequently shifts (as it might well do!), the Product Schedule records DO NOT automatically change to reflect the new date. To correct the dates, Salesforce expects the user to go into each Opportunity Line Item and click on the “Re-Establish” button to re-calculate the schedule dates. Naturally, this does not always happen, so the schedule dates no longer reflect reality. Reports and dashboards will show wrong dates. Management will start to lose faith with the data.

Flow Solution




The solution described here uses a Custom Field, two Flows a Workflow and a Process (Sometimes called a Lightning Process). I’ve written it as a set of instructions (with screen shots) and added commentary on why certain things are done and how it all knits together.

Custom Field

In the Opportunity object, create a number field called “Days Shifted”. This is to capture the number of days the Close Date moves by when it is edited. Set the default value to be zero. The field should not be added to any page layouts as it is just being used it in the background. Remember to add a description to the field when creating it, so a future admin knows what it’s there for!

Workflow 1 – Populating Days Shifted

This simple workflow fires whenever the Close Date is changed:








The workflow action is to set the field value of Days Shifted:








Make sure that “Re-evaluate Workflow Rules After Field Change” is ticked!

Flow 1 – Update Line Schedules

As mentioned, there will be two flows. This flow will be a sub-flow in Flow 2. So at the moment, some elements of it will seem to be loose ends.

What this flow does is find all the Product Schedule records associated to an Opportunity Line record and update all the dates on those records.

[Side note – when I say “Product Schedule”, I technically mean “Opportunity Line Item Schedule”. But “Product Schedule” is shorter and more widely used!]


Here’s what is going on:

  1. Looks up all Product Schedule records that relate to an Opportunity Line and assigns them to a SObject Collection Variable.
  2. Assigns the first record in that collection to a SObject Variable and then passes that record onto…
  3. Assigns the new Date value (calculated in a formula in the Flow – see below) to that record.
  4. Adds that record to a second SObject Collection Variable.
  5. Updates all the records using the new collection’s values.




If you are new to Flow (sometimes also called Visual Flow), a lot of that may sound like double-dutch. Below is a brief explanation of each element. If you are completely new to Flows, then the Salesforce guide is here:

Other resources are available!

Flow Variables and Formula

The following variables and formula in the flow were created from the “Resources” tab:

varDaysShiftNumberWill ultimately come from the custom field created earlier.
scvLineSchedulesSObject Collection VariableDefined as part of step 1. Object is OpportunityLineItemSchedule
scvNewLineSchedulesSObject Collection VariableComes into play in steps 4 and 5. Object is OpportunityLineItemSchedule
flaNewScheduleDateFormula (Date){!svarLineSchedule.ScheduleDate} + {!varDaysShift}
svarLineScheduleSObject VariableSet in step 2. Object is OpportunityLineItemSchedule
svarOpportunityLineItemSObject VariableComes into play in the next Flow. Just create it for now Object is OpportunityLineItem.

Brief Overview of the Different Variables

Flow can be used to either deal with a single record (say an Opportunity record) or multiple records all at once – a collection (e.g. all the Opportunity records relating to an Account). Different types of variable are used depending on which scenario you are in. Are you dealing with one record or many at once?

Variable is the value of a specific field in that one record (e.g. the Close Date of an Opportunity).

SObject Variable is a single record, but holds multiple field values of that record in it (e.g. an Opportunity with its Close Date, Account ID, Stage and Amount).

SObject Collection Variable is a group of records that have been pulled into or created within the flow (e.g. all Opportunities relating to an Account).


Step 1 – Fast Lookup

Fast Lookups are a way of grabbing multiple records and adding them to a collection:










This is telling the flow to find all OpportunityLineItemSchedule records (Product Schedule records) where the Opportunity Line Item ID is the same as the svarOpportunityLineItem.Id. (svarOpportunityLineItem.Id will be set in the other flow.)

All qualifying records are put into a collection (scvLineSchedules) and the ScheduleDate of these records is stored (with the record IDs) in the collection.

Step 2 – The Loop

The Loop is used to cycle through a collection of records.







Here, the loop is set to cycle through all the records in the scvLineSchedules collection and assign the next record to the SObject variable svarLineSchedule.

When drawing a connection from a loop, it gives two options: “for each value in the collection” and “when there are no more values to process”.


Step 3 – Assigning the new Date to the Record

The loop has put a single record to svarLineSchedule. The next step is to assign the new date value to that record. The formula needed was set up earlier as flaNewScheduleDate.







The truncated text in the Variable box is “{!svarLineSchedule.ScheduleDate}”. To get to the specific field in a SObject variable, click on the small black triangle next to the variable’s name.


Step 4 – Add the updated record to a new Collection

Now that the SObject variable has been updated with the new date value, it needs to be added to a new collection within the flow.






Note that the Operator is “add”. The record is being added to a collection.


The flow now points back to the loop, where the next record will be picked, the date updated and the result added to the new collection. This continues until all the records in the collection have been processed.

Step 5 – Update Salesforce

This last, simple stage is where the values in the new collection (scvNewLineSchedules) are used to update all the records in Salesforce.







Don’t forget to save the flow! In this instance, the flow has been called “flowUpdateLineSchedules”.


Flow 2 – Updating Opportunity Line Item Dates

The first flow loops through all the Product Schedule records and updates the date on each. But Product Schedules are against the Opportunity Line Items, not the Opportunity. This flow loops through the Opportunity Line Item records associated to an Opportunity and uses the first flow as a sub-flow:


  1. Looks up the Opportunity and uses the value in the Days Shifted field to populate a variable (see table below).
  2. Looks up all Opportunity Line Item records that relate to the Opportunity and assigns them to a SObject Collection Variable.
  3. Assigns the first record in that collection to a SObject Variable and then passes that record onto…
  4. Flow 1 (now you see why I created Flow 1 first!)
  5. Assigns the new Date value (calculated in a formula in the Flow – see below) to the Opportunity Line Item record.
  6. Adds that record to a second SObject Collection Variable.
  7. Updates all the records using the new collection’s values.
  8. Sets the Days Shifted value back to 0 (ready for next time).



Hopefully, you can see that there are a lot of similarities between this flow and the first. The only real differences are elements 1, 4 and 8. Also, this time it is looping through Opportunity Line Item records and not Product Schedule records.


Flow Variables and Formula

varDaysShiftNumberIs set by the Workflow that will launch this flow. Is passed on to the sub-flow in step 4.
varOpportunityIDVariable (Text)Is set by the Workflow that will launch this flow
scvOpportunityLinesSObject Collection VariableDefined as part of step 2
scvNewOpportunityLinesSObject Collection VariableComes into play in steps 6 and 7
flaNewDateFormula (Date){!svarLineSchedule.ScheduleDate} + {!varDaysShift}
svarOpportunityLineSObject VariableSet in step 3. Is passed on to the sub-flow in step 4.


Step 1 – Lookup Opportunity

This is a simple lookup to pull information from a single record – the Opportunity that has had its Close Date changed. This lookup finds the record in question and uses to the value in “Days Shifted” to populate the variable in the flow.




Steps 2, 3, 5, 6 and 7

These are virtually identical to steps 2, 3, 4 and 5 in the first flow, so please refer to the above sections.


Step 4 – Calling Flow 1

To put in a sub-flow, Salesforce needs to know which variables it is setting in the target flow (flowUpdateLineSchedules) and what values to set. Here the target sub-flow is identified and the two values required in the sub-flow set:








The values required for the sub-flow to work are svarOpportunityLineItem and varDaysShifted (i.e. which Opportunity Line Item record is being updated and what value is to be used in flaNewScheduleDate).


Flow insists that at least 1 output value is set, even though it is not necessary from a data point of view here. So a variable is set to be passed back.




Step 8 – Resetting the Days Shifted to zero

This is a simple single record update to change the value stored in Days_Shifted__c back to 0.







We need to do this in preparation for the next time the Close Date is modified.

Process – Firing Flow 2

To fire the Flow when the Date changes, a Process is needed. Processes are relatively new to Salesforce – they’re souped up Workflows that give us Admins many more options. In this case, a very simple Process will suffice.

Note that the ISCHANGED function is not available in Process criteria, hence we reset the value of “Days Shifted” back to zero in the second Flow.

From the “Create Process” (found very near the link to create a new Flow), give the Process a name and specify the object that the Process relates to. In this case, it’ll be Opportunity (as it is a change to an Opportunity record that is to set everything in motion).

Then set the Criteria that are to be evaluated. So an Opportunity where the Days Shifted value is not zero:







Under Immediate Actions, tell the Process to launch the Flow (Flow 2 – here called “flowRedateOpportunityItems”):







Tell the Process Action to set varOpportunityID in the flow as the ID of the Opportunity record and save your Process.

Once saved, Click on the “Activate” button (top-right) to turn the Process on.


Final Configuration

Now the solution is built, all that is left is to do some testing. So Activate the Flows, Workflow and Process and test away!


Points for Consideration

Opportunity Line Item Edits

A user can manually change the dates on an individual Opportunity Line Item record. The solution above only looks at changes made to the Opportunity Close Date. But you can see how you could set up a custom field and workflows on Opportunity Line Item and feed values directly into the first flow.

Call Limit

If you’re new to flows, you might be wandering what this collection stuff is about. Basically, it’s to do with governor limits in Salesforce. Every time a flow pulls data out of Salesforce or writes information into Salesforce, it counts as an API call. So if you’re handling 1 record at a time (rather than collections), those API calls start to rack up quite quickly. The daily limit is 250,000 calls (or 200 x licenced users if you happen to be on an org with more than 1,250 users). Although that sounds like a large number, it can easily be hit if updating each record one-by-one.

Batch Limit

Another limit that is worth noting is the batch size. A collection cannot contain more than 200 records. That is not likely to be an issue here, but could be if you have a Product Schedule set up with daily revenues over a year. In such cases, you’ll need to be a little more creative in how you are defining the collections.

Mass Updates

Any mass change of Close Dates using a data loader could easily blow the batch limit mentioned above. Changing the chunking settings (the number of records that the data loader tries to process at a time) down to 1 (normal default is 200) may work. This will significantly increase the time it takes to perform the mass update.

Change Sets

At present, Flow Triggers cannot be added to a change set. Flows can be added, but must be activated after being imported as part of an inbound change set.


So there we have it. Two Flows, a Workflow, a Process and a Custom Field. And no code! The whole thing took me about 90 minutes to build.

This makes Product Schedules a much more user-friendly tool and greatly improves forecasting accuracy.


This is just one potential application of Flow triggered by Workflow. If you’ve enhanced a Salesforce process by using Flow, please share your experiences with me.


Original Article By: Nick Spencer.