This is a technical post about learning Salesforce Flow, a type of sophisticated automation that lets you create “wizard-like” step-by-step screens and behind-the-scenes logic/magic. This is written with an as-broad-as-possible audience in mind, but warning that there might be some jargon coming your way. For my organizers and hands-on data entry peeps, I hope you can use this post to expand your imagination of what is possible! For my admin/developer peeps, I hope you’ll enjoy this peak into building my first Flow, and the concepts/obstacles that I encountered along the way.
“I’m going to tackle Flows” … she said.
“I’ll wait until the office is quiet” … she said.
“It’ll be fun and empowering” … she said.
The reality is that a lot of the time, I felt like throwing my computer out the window. Some people say experiences like this are for building resilience, but I prefer to think of it as building empathy. Perhaps analogous to working in the airline industry (rip?), working in technology sometimes brings me into contact with people at their worst. And, well, myself at my worst. I made it through this project after phoning just about every friend in my address book and losing quite a bit of sleep. Seriously – thank you Emily, Vered, David~, Steve~, Marc, Deepa, Matthew, Melanie*, Michael, Jessie, Deanne, several people on Twitter, several developers who wrote code and released it for free, and several bloggers who tried to solve similar problems and left me a breadcrumb trail. And if I kvetched about this to you, thank you too!
~Extra shout/out to Steve and David for fielding wayward support texts from me at odd hours and replying with grace, humor, and help
*Super shout/out to Melanie who boosted my confidence thru coworking time AND who’s blogpost on designing Flows helped inspire this one
Describe the problem in kid-friendly vocab
My organization has a program where we “match” donations to eligible nonprofits. The way it works is that a staff or board member submits a donation request with a receipt. In technology terms, these requests are created using a Salesforce Community (“Portal”). Then, my department evaluates the request and makes sure the nonprofit meets our criteria. Then, we update the request (mark it as closed, fill out a bunch of fields) and create a payment in our system that will be processed on the next available payment day. Each of our payments have several “background records” called Budget Contributions and Contributing Payments. I won’t go into too much detail about them, but you will see that they show up in the examples below. All together, this process results in ~15 clicks per donation, and in the month of December alone, we typically handle hundreds of them. ~Yikes~
To make this more complicated, we have two types of donation requests that are processed this way (one is called “matching gift” and the other is called “director discretionary gift”). They have slightly different rules about they are processed. You’ll see them show up in the Flows I designed later.
From a gift request, click ONE BUTTON, enter a preferred payment date, and the system will automatically update the donation request and create the payment.
The dreamier dream
From a list of gift requests, select ALL of the gifts that should be processed, click ONE BUTTON, enter a preferred payment date, and the system will automatically update all of the donation requests and create the necessary payments.
This Screen Flow is launched from a button on the Opportunity object. The button takes the Record ID and passes it into the Flow. (I’m certain that there’s a more elegant way to do this, but I couldn’t quite get it up and running and I got impatient to start writing. The example below definitely works).
I like to think that the Flow operates kind of like rolling a marble. It will follow the path according to logic rules, and end up at the end of the Flow as an approved grant with a payment and a bunch of other details all sorted out. No on-going human brainpower required!
I felt really intimidated to get started, plus I was having a crappy week for other reasons, so I fortified myself with some chocolate croissants and asked a fellow database admin pal to join me for some coworking time for mutual support and accountability. This helped me SOOOOOO much! Definitely something I want to try again in the future, and offer to others in my network, too. We worked independently and we were both able to help each other accomplish our goals. This is a tangent/teaser for my (potentially forthcoming) “no such thing as a solo admin” post…
I’m especially proud of the “Get Budget” element because it is smart enough to assign the Payment to an appropriate budget (based on year) instead of me “hardcoding” the budget record and then having an awkward time at the end of the year when we are not sure if the Flow should add payments for 2020 or 2021. No annual maintenance required! (Famous last words).
Another great feature of the Flow is the Decision Element that splits off MG and DDG record types, since they are processed according to different rules. Originally, I thought I might have to make two separate Flows for each scenario, but now we can handle it all in one. Win!
While I was working on this Flow, I learned that a Flow is “smart” enough to reference records that IT JUST CREATED in order to ALSO create related records!!! For example, in order to create a Budget Contribution record, you need to have a Payment record. Well, the Flow is responsible for *creating* the Payment record, so I wasn’t sure how to handle this. As long as the “Create Records” elements are in the right order, you can reference fields/Ids from one record to create another all in the same Flow. Pretty snazzy, right?
(Unfortunately, I couldn’t add a screenshot because the field names are cut off in the UI, so the images really aren’t much help here)
This Flow has a lot of Pink (Get/Create/Update records). These are all known as “Data” in the Flow Builder interface and they should be used sparingly since they take a lot of computing power and they can result in infinite loops if you accidentally put one of these puppies in a Loop (more on that later in this post). It’s better to store up all of your changes and then do the Data actions at the end. However, since this Flow only works on one record at a time (by design), I’m not too worried about it. We don’t have much data to store, and we aren’t going to be making actions over and over again at any point.
One enhancement that I want to add one day is a “back to record” or “back to List View” button at the end of the Flow. Unfortunately, I couldn’t quite figure that out and decided that what we have now is Good Enough.
I was able to build Flow #1 (my first Flow!!) in a day or two and I felt pretty heckin’ proud of myself. But was I satisified? Nooooooo! Even with this streamlined process, someone would still have to navigate to each record (or use Michael Kolodner’s fancy trick) and click this button to process each gift request. So, we reduce the clicks from 15 -> 1, but we still need to do that 1 click hundreds of times. Boo!
Buckle your seatbelts, caterpillars and butterflies. This one was a bumpy ride.
This Flow also starts from a Button on the Opportunity. However, this Button is a List View Button, not a button on the individual record.
And the Flow itself? Well, it’s gnarly. I had to ~zoom out~ quite a bit just to get a good pic. What in tarnation!??!!!
Let’s talk a little bit about how we got here, eh? Flow #1 takes ONE Id and passes it into a Flow. Flow #2 takes a LIST of Ids and has to handle them one at a time (hence, all of those loops). After a bunch of research, I decided to try out instructions from this blog post. Truly, the instructions were stellar (HUGE thank you to Accidental Coder SF!). With your support, I wrote my first code… ever! This felt AWESOME and I was so pumped when everything worked in the Sandbox where I was building. However, this code didn’t come with test classes and I didn’t quite know how to write them. I know that I could have learned (starting simple, of course) and/or asked for help, but I also had a sneaking suspicion that there was a better way to handle this problem without writing code (which would inevitably need to be documented, maintained, etc). This was the second time I reached out for help in this project!
Whiiiiiich led me to attempt #2 with instructions from this blog post. These were also phenomenal ideas (thank you Big Ass Force) but unfortunately, my mentor and I came to the conclusion that this idea only works for auto-launched Flows and mine was a Screen Flow.
At this point, emboldened though I may be, I went on a spiral of self doubt and started questioning the whole List View thing. Why not set up the same List View criteria inside of a Flow and use a Get Records element? Why not launch the Flow from a checkbox and let users update the checkbox (from a List View) instead of selecting the entire record and passing it into the Flow? Why not go back to Plan A and learn how to write test classes? Why not throw my computer out the window? You know the drill.
Still trying to figure out how to get the right records into the Flow in the simplest, possible way, I found two more blog posts that provided similar strategies. (One and Two). Gosh darn, the blog posts were actually similar enough that I got them mixed up which led to quite a few dilemmas testing and deploying (aka moving the Flow from my “sandbox” area to my real “production” Salesforce environment). The first one sent me down a rabbit hole of how to get their code into my org (thanks, David, for getting me out of that particular pickle). I think it was obvious for people who are used to it, but it was not at all obvious to me! The second one had some brilliant ideas, but also required a Visualforce page. That is, until I stumbled upon Richard Englhard’s other post and component that lets you query a list of records inside of a Flow! DING DING DING! I was able to download a package of Flow Components from the AppExchange and make the action that I needed available right in my Flow Builder.
Now that we got my selected records into the Flow, we have to design the Flow itself. Which is WAY, WAY, WAAAAAY more complicated for groups of records than solo records. Luckily, I had instructions from this blog post, a variety of helpful posts from Jen Lee’s blog, and technical/moral support from my friend/mentors Melanie and Steve. I also used this Trailhead project to help me learn the ropes.
To make this shebang work, I needed to use a concept called “bulkification” which (ew) sounds like bodybuilding but is actually about batch processing. Basically, in my novice understanding, bulkification is about loading up all of your changes, and then executing them all at once. The metaphor that comes to mind is that when I am folding laundry, I add each shirt to the shirt pile, and then at the end, I put the entire pile of shirts in the drawer. This saves me from having to walk to the dresser every time I fold a measly tshirt. Flows work the same way.
SO, in comparison to Flow #1 which is mostly PINK elements, Flow #2 is mostly ORANGE elements, which means that we are assigning values and processing loops (one record at a time) and storing all of the changes to make at once with a big finale. My loops are even more complicated because there are different rules depending on what type of request (remember, I said there were two?). Here are all of the “parts” of my Flow and what they do:
|Resource Type||Resource Name / Description||Documentation|
|Collection Variable||ids – this is the variable that stores all of the Record Ids selected from the initial list view||Described at length here and here|
|Decisions||Within each loop, split for MG/DDG logic (so, 4 total decision outcomes)||This is how I accounted for specific rules in how we manage the request types.|
|Formulas (2)||MGFormula, Payment Year – these store custom logic for determining payment amount and payment year||Variation on Trailhead project here|
|Record (Single) Variables (6)||Budget Contribution Individual Record, Budget Individual Record, Contributing Program Individual Record, Current Item from Loop #1, Current Item from Loop #2, Payment Individual Record||This is described well here with helpful videos of each step! The hardest part is understanding the concepts, but practice helps with that too 🙂|
|Record Collection Variables (5)||Budget Contributions To Create, Contributing Programs To Create, Opportunities To Update, Payments To Create, Selected Requests||This is described well here with helpful videos of each step! The hardest part is understanding the concepts, but practice helps with that too 🙂|
|Screen Components (2)||Start Screen (enter Payment Date), End Screen (Success)|
|Apex Action||“Get Records WHERE Id is IN List” Apex action||This is downloaded from this package|
|Loops (2)||Loop for Updating Opportunities|
Loop for Creating Payments and CPs and BC’s for each Payment
|I have one loop for my Oppty Update and one loop for creating my payment (etc) records. The loops all assign values (orange) and the Create/Update actions (pink) happen outside of the loop. Some of the best resources for this are on Jen Lee’s blog.|
The thing that is so cool and elegant about using the Apex Action from the Englehard Consulting package is that I can use it in any Flow (I can imagine that we will want to launch OTHER flows from list views down the line… especially now that I know how to do it) whereas all of the other solutions would have required code that only covers an extremely narrow scenario, and I would have had to copy it each time I wanted to make a new Flow. That’s just unsustainable and unmanageable.
Flow #2 hiccups
I hit a wide variety of roadblocks working on this project that made me want to give up more than once.
- Since the Flow was set up to run *exclusively* on ids provided through the List View, I couldn’t use the Flow debugger to figure out why something wasn’t working.
- I did not understand how to reference a record created inside of the Flow … for other, related records ALSO created inside of the Flow. Luckily, Jen Lee covers this in a blog post and my mentor Steve took the time to explain it to me.
- We had a bad code interaction in our Sandbox environment that was making the Flow fail for a few days. It was beyond my experience level to be able to figure out what was wrong.
- While trying to create requisite Apex classes (code files) based on blog post instructions, I couldn’t figure out how to generate all of the associated file types. Turns out that some of them are created automatically and are also unsearchable (how could I possible know that?!). I pinged another friend and mentor who was burning the midnight oil on his own projects and he generously helped me understand that no action was required.
- I hit a security roadblock in my org since my profile accidentally lost privileges to download apps from the AppExchange, so I needed to ask another Admin to download what I needed until I had the bandwidth to find the permission and grant it to myself (heads up – don’t do this willy nilly in your org unless you know you have permission! there might be a compliance reason why you can’t download things, but I know that wasn’t the case for me)
- Speaking of downloading things, I got two different Apex packages confused and downloaded the wrong one in Production. Then, when I tried to use a Change Set to move my Flow into Production, it was failing (miserably, might I add? yes, it was very dramatic!) However, I was CERTAIN (ly wrong…) that I had downloaded the right Action and couldn’t for the life of me interpret the error. Lo and behold, I had downloaded the Apex for one solution but not the other.
- While I was troubleshooting obstacle #6, I figured I might as well remove the offending Apex action from my Flow so that at least I could get the rest of the darn thing in Production and figure it out later (this is basically always a bad idea…). Then, I realized that I couldn’t delete the element from my Flow, even when I did a Save As!!!!!! Murderous with frustration, I called a buddy for help and in the magic process of “as soon as you ask for tech help, the problem goes away” happened. It turns out that it looks like you can’t delete elements if you are using Auto-Layout in the Flow Builder. I assume that’s because if you delete a connection, the Flow doesn’t know what else to do, but this was EXTREMELY frustrating for me! EXTREMELY!!!
7 hiccups is a LOT of hiccups to deal with and definitely pushed me to my limit. I wish everyone here (and beyond!) had access to as much support as I did, because even when I was so frustrated, I knew that I had people who could help. Dear blogfriends near and far, I read every single comment and Contact Me that comes through here, so if anything in this post is not clear, or if you just need a boost, feel free to ask me! If I can help, I will, and if I can’t, I’ll help you find someone who can 🙂
I guess if I’m really being honest, instead of feeling victorious (which I also do!), I am feeling pretty sappy! This was really, really difficult and I did it myself but I also did it with the help of community. I’ll never forget that – I think it’s the best Flow tip that I can share.