Clone Records (and modify them before Insert) Declaratively with Spring ’20

I recently wrote an article outlining my discoveries around the new ‘Get All Fields’ feature of Gets in Spring ’20.

Check it out

A Graphical SOQL Query Builder for Flow, plus an Execute SOQL Action

With Spring ’20, it’s possible to create a Process Action (i.e. an Invocable Action running in Flow) that can return any kind of SObject. That means it’s finally possible to create an Action that executes any SOQL query it gets passed.

The video below has a good introduction, but also check out this excellent demonstration of Execute SOQL being used with subqueries and merge fields and without a graphical front end.

In this instance, an ExecuteSOQL action has been configured to take a SOQL string as an input and return a collection of Accounts:

This milestone means it’s now worthwhile to build a Flow Screen component that can be used to generate SOQL queries:

The SOQLBuilder LWC can take an existing SOQL string and will try to parse it and display it in the UI. It will output a string based on whatever is generated in the builder.

Note that when configuring the Execute SOQL Action, it’s necessary to specify the object type that will be returned. Make sure that the Object Type specified in the SOQLBuilder matches. In other words, if you have an Execute SOQL action in your flow that’s hard configured to return Contact records, don’t pass it a string that queries on Accounts.

Inserting Dynamic Mergefields into your SOQL Queries

You can make a dynamic date part of your SOQL query, taking the date from upstream in your Flow. Just insert standard mergefield syntax: myquery='[SELECT Account.Name FROM Contact WHERE startdate__c > {!myUpstreamFlowDate}]’ . Important: Note that you do not delimit the mergefield with an additional set of quote marks.

Using Date Literals and Ids

Query on Id string literal:
SELECT LastName from Contact WHERE Id = '1123xxx333e3e'

Query on date literal:
SELECT Id from Contact WHERE CreatedDate = LAST_n_DAYS:14

Handles nested relationship queries in the WHERE clause

The action will first check to see if at least one of those queries exists and if true, will recursively search the nested queries and do the same formatted on those; If false, it will continue on with the single query search.

SELECT Id, Name,Status_DD__c,Region__c FROM Object2__c WHERE Status_DD__c in (‘Option 1′,’Option 2′,’Option 3’) and id not in (SELECT Id FROM Object1__c WHERE CreatedDate >= 2020-04-12T15:24:00Z) and Field1__r.Custom__c = ‘1234’

SELECT Id, Status__c, Contact__c , Custom__c FROM Object_1__c WHERE Status__c like ‘%Active%’ AND Account__r.Custom__c = ‘1234’ AND Custom__c not in (select Custom__c from Object_2__c where Contact__c = ‘1234’)

Secondary Fixes

  • Strip unwanted formatting characters from the query string that come in when text template is used in flow: ‘\n’, ‘\r\n’, ‘\t’
  • Fix time stamp formatting on date strings with a time: ’00:00 am’ is an invalid format. The equivalent, valid format using regular time is ’12:00 am’. ’00:00′ is only valid for 24hr formatting.
  • Fix month name to month number conversion: prepend a 0 on the month number if the string value is < 10


  • Throw some exceptions for more informative error handling
  • Cleaned up unit suite and fixed failing tests

Potential issues not yet fixed:

It appears that after the ‘ order by ‘ substring is removed from the query, it is never added back before querying.

Use Cases:

New Features in V1.2

You can make a dynamic date part of your SOQL query, taking the date from upstream in your Flow. Just insert standard mergefield syntax: myquery='[SELECT Account.Name FROM Contact WHERE startdate__c > {!myUpstreamFlowDate}’

Works properly with Schedule Triggers

New Features in V1.1

  • You can now traverse to select related fields via relationships (example: [SELECT Account.Name FROM Contact...]
    • SELECT Id, Name,Status_DD__c,Region__c FROM Object2__c WHERE Status_DD__c in (‘Option 1′,’Option 2′,’Option 3’) and id not in (SELECT Id FROM Object1__c WHERE CreatedDate >= 2020-04-12T15:24:00Z) and Field1__r.Custom__c = ‘1234’
    • SELECT Id, Status__c, Contact__c , Custom__c FROM Object_1__c WHERE Status__c like ‘%Active%’ AND Account__r.Custom__c = ‘1234’ AND Custom__c not in (select Custom__c from Object_2__c where Contact__c = ‘1234’)
  • An ‘Add All’ button lets you easily create the equivalent of a Select *
  • You can lock down the object that the query is returning by setting the attribute disableObjectTypeSelection to True. This is useful when you’re pairing this component with an Execute SOQL action, because the action has to be locked to a specific action type in the Flow Builder, and this avoids the user selecting a different action and ending up with a mismatch.

Future Developments

By the end of 2020, it will be possible to add Custom Property Editors to Collection Processor Actions. That will allow the SOQL Builder UI to not just exist in running Flows but also in the Execute SOQL property editor in Flow Builder.


Unmanaged V 1.3.2 5/5/20

View Source


Old Versions

Unmanaged V 1.3.1 4/30/20

Unmanaged V 1.2

Unmanaged V 1.1

Unmanaged Packages V 1.0

Sebastiaan de Man: Create Customized Knowledge Articles with Flow

Starting with a Salesforce Knowledge article, insert variables using the {!VariableName} convention like this:

Screenshot 2019-07-02 at 12.02.39.png

Create a flow that has these variables as … variables

Screenshot 2019-07-02 at 12.03.51.png

Use the recordId variable to get the KnowledgeArticle record, based on MasterVersionId:

Screenshot 2019-07-02 at 12.05.25.png

Get the fields to show:

Screenshot 2019-07-02 at 12.05.33.png

Set the variables with the values you need to use (I used default values for this flow)

Make a screen component that show the fields:

Screenshot 2019-07-02 at 12.07.36.png

Add the flow component to the Article detail page in the community, pass over the recordId:

Screenshot 2019-07-02 at 12.08.32.png

And it will magically replace all variables in the text:

Screenshot 2019-07-02 at 12.09.16.png

Additional Resources

Salesforce Knowledge and Flow Wiki Page

The Flow Troubleshooter Pattern is useful for creating and managing large numbers of custom nodes without having to manually add a flow element for each one.

Send HTML Email with Multi-Language Support for Lightning Email Templates

This is extended functionality of Send Rich Email.

SalesForce Orgs and Communities often require multiple language support, including the ability to generate emails in the selected language of the Community anonymous user, member or Org User.

LightningFlowComponents/flow_action_components/SendHTMLEmail/ has been extended to send the appropriate language email based on the preferred language as part of a flow action to send email. This Action uses Lightning EmailTemplates, and has not been tested on Classic Templates (yet).

For this functionality, each language localization has its own template, that shares a common EmailTemplate.Name. To differentiate the localizations, add a tag to the EmailTemplate.Description field in the format: ‘Language=”en_US”‘ (or whatever corresponding to your organization localizations).

When the sendHTML templateName and templateLanguage parameters are used, the action selects the template with the matching templateName and the ‘Language=”xxx_YY”‘ tag in the Description. (Note, no spaces, exact match only)

Template Selection criteria order is prioritized using matching EmailTemplate:Name and:

  1. If templateLanguage is empty, set to Organization LanguageLocaleKey
  2. If templateLanguage tag is found in EmailTemplate.Description (e.g., ‘Language={{{templateLanguage}}}’)
  3. First matching template with matching templateName but no corresponding ‘Language=”‘ tag in EmailTemplate.Description


Step 1: Identify the Common EmailTemplate:Name for the Template Set you would like to use for localization and copy the name. (App Launcher–>Email Template[lightning]). (Note, in this case it would be “OAG OCP Complaint Received Email” – and there are two tagged locales identified in the Description Field, “en_US” and “es_MX”)

Step 2: Add the Apex action to your Flow and set the Parameters:

  • Associated RecordId(WhatId/recordId for template and activity) (In Example, a Case Record ID {!param_Case.Id}, but only needed if you are using merge fields for this record)
  • Template Language (In Example, the language chosen in the flow logic for the Contact {!param_Language})
  • Template Name (In Example, “OAG OCP Complaint Received Email” )
  • Template Target Record Id (In Example, {!param_ContactID} which is to whom the email will be sent, and any recipient merge fields used in the template)


Limitation: Enhanced Letterheads are not supported in Lightning Communities at this time. A support request has been submitted and it is being worked on by SF. If you attempt to use the Enhanced Letterhead in a Community application, you will get the following error: sObject type ‘EnhancedLetterhead’ is not supported. If you are attempting to use a custom object, be sure to append the ‘__c’ after the entity name. Please reference your WSDL or the describe call for the appropriate names.: []

Kludge Acknowledgement: The elegant solution would have a localization field in EmailTemplate (e.g., EmailTemplate.LanguageLocaleKey ). At this time, no such field exists and you cannot add custom fields to EmailTemplate Object – ergo, a tag parsed from the standard “Description” is the kludged approach.


Convert Strings to String Collections, and Vice Versa

Some input parameters requires strings in one of two forms. Send Rich Email, for example, can take a list of string email addresses but can’t (currently) take a comma-separated-list.

This package provides a set of simple actions to convert data from one form to the other.

Convert from Collection to CSV String

This takes a string collection and returns a csv string.

Convert To String Collection

ConvertToStringCollection, can use a provided character as a delimiter. For example, if you pass in the output of a multiselect picklist and set the delimiter to a semicolon, you’ll convert multiselect picklist output to a collection of strings that can be worked on later in the flow.

Convert from CSV to Collection

This has been superseded by Convert to String Collection (see above)


Version 1.1 Unmanaged

Old Versions

Version 1.0 Unmanaged

View Source


Flow Use Case – New Task Button with picklist for selecting the record type

When our org migrated to Lightning, we were faced with an interesting issue: we had many task record types and in order to enable our end users to create tasks from the activity composer, we had to create a different quick action for each task record type. The result wasn’t pretty

Too many tabs

Our users wanted a simple button where they could create a task with one click and be able to select the task record type.
When I set out to build the solution, I started by using a Get Records element to query the record type object but soon realized that I can’t filter this list based on the record types available to the current user.

I was able to provide a nice solution using the new invocable action and quickChoice component found here on this site. I used the Get Record Type Info by Object action to generate a list of record types available to the current user. Then I fed the record type labels and Ids into the Quick Choice component. Finally I created the task and used the Navigate to Record action to open the new task in edit mode.

Get the available record types
You need to check this box to store the output value
The new task Id is fed into this action
And this is the completed flow
Here is the end user experience

Preventing duplicate junction object records by GORAV SETH

GORAV SETH demonstrates how you can use the new flow before save trigger combined with duplicate rules to prevent creation of duplicate junction object records.
Check it out

Chris Van Der Merwe: Work with Business Hours via New Flow Actions

Chris has written the first of a promised series of actions that allow easy declarative, automated manipulation of Business Hours information.

Check it out!