Sunday, May 29, 2022

Tips for Salesforce Certified Strategy Designer Exam

A little late to the party, but I just passed the latest Strategy Designer exam. Since it's a really new one (about one week old), there isn't much guidance about it yet. I'd like to share a few tips during my preparation.

Exam Guide

https://trailhead.salesforce.com/help?article=Salesforce-Certified-Strategy-Designer-Exam-Guide

This is the most important page to understand the exam, a few things to note:

The passing score is 70% (a little higher than usual). The portions on design concepts are very high (Value Design 32% and Intagiible Deliverables 26%).

Preparation

https://trailhead.salesforce.com/users/strailhead/trailmixes/prepare-for-your-strategy-designer-credential

https://trailhead.salesforce.com/en/users/salesforceexperience/trailmixes/salesforce-design-strategist-career

These are the 2 Trailmixes provided by Trailhead, and the first one is tailored to the exam guide. However, it can take a while to complete. If you're in a hurry, the second one should equip you with enough knowledge about the content tested in a short time.

The core is Learn Strategy Design 

https://trailhead.salesforce.com/en/content/learn/trails/get-started-with-design-strategy

By doing this tail, I refreshed a lot of knowledge with proper concepts. I've been using some of the methods mentioned in my daily job but didn't know their names and details. This tail provides the concepts, best practices together with a fictitious example of Cloud Kicks' supply chain challenge. It's a delightful learning journey for the Strategy Design concept.

Final Thoughts

This exam is focusing on design, and everything related to it, such as concepts, frameworks, and techniques. You barely get tested on your Salesforce technical knowledge, but it's great complementary to that. I think if you're doing any sort of design jobs in Salesforce projects, this is a cert worthy of the effort.

Monday, April 25, 2022

How to pass record Id(s) to Screen Flow

When users run flow, the best experience is to automatically grab the context instead of asking the user to enter some input. The most common place to start a Screen Flow is probably the record detail page, but how do we pass the current record information to the flow? All we need is a text input variable called recordId (Case Sensitive) in that flow.


With this, you can simply use the standard flow quick action to invoke the flow, and that current recordId will be passed into the flow.


Alternatively, you can define an input variable with any name, for example, input.

And define a URL custom button to invoke the flow (/flow/FlowName), to which you can attach the variable at the end of the URL using ?input={!object.Id}

For example, a button on the Account detail page passing the record Id to the input variable of flow named AccountName will look like this.



Depending on the use case we usually need an action to get the corresponding record from the Id.


The biggest difference between quick action and URL button is that the former will run in a modal, and the latter runs in full screen.

Similarly, we can actually pass selected record Ids to a list button.


The text input collection variable must be ids (Case Sensitive). Then you can create a list button calling the flow, like the previous example (/flow/FlowName), and you don't need to specify this variable, it's automatic. However, you may want to attach the return URL parameter to control where the user goes after the flow is finished, such as ?retURL=001/o which will redirect to the account list view page.


More examples are listed in the documentation 

https://help.salesforce.com/s/articleView?id=sf.flow_distribute_internal_url_retURL.htm&type=5

The challenge for these flows designed for multiple records will be how to manipulate them, depending on the use case, a loop may do the job, but sometimes you may need an Apex action to process them more efficiently.

Sunday, April 24, 2022

Making Flows More Flexible with Record Choice Set

Salesforce Flow is a great tool to automate business processes, and the best part is it can combine the MVC all together in one go. So we can build a flow with screens to interact with users and add the logic to handle the data. However, changes are inevitable for all kinds of processes, which leads to the question of how to make flows easy to maintain and adapt to changes.

For example, a client wants to collect some diet preferences when users launch the flow. It will be a list of options to pick from. However, the business changes the options frequently, and based on the chosen option, users need to see different following screens like different dishes. The most straightforward way is to build everything into the flow itself, or maybe pull the options from a picklist field or custom metadata types (CMDT). But with more questions involved, this pattern may get difficult to handle.

Let me explain with some pictures.

For example, I have a form with 3 questions. Question No.1 has 2 options A and B. If the user picks A, then the next one will be Question No.2. Option B leads to Question No.3.


You can easily put the logic into flow at this scale, and there is not much difference with the new pattern. However, imagine you have more questions like this:


You can still do it in the flow directly, but it is getting messy, and harder, especially for maintenance. And what about you have 15 questions, or more? You may end up with something like this:


It might look something cool to show off, but definitely not something nice to maintain, at least not for me. So what alternative can I have?


Solution

http://unofficialsf.com/scaling-to-thousands-of-screens-with-lightning-flow/

Inspired by the great article above, I recently figured out a new way to design flows. All it requires is to save the logic into 2 custom objects or CMDT (it usually requires admin to modify and does not support Rich Text) as records.

I named them Question Node (questions) and Question Node Outcome (answers).

Both of them need a Label field to hold the text of the question/answer. I used rich text type, so I can have more flexibility to control the format.

The Question Node needs to have a type picklist field, to distinguish if it's a choice question or some other types like number or date.

The Queston Node Outcome has two lookup fields to Queston Node, and they are the key for this pattern. I named them Parent Node and Target Node.

In this pattern, Each question is a Queston Node record, and each answer is a Question Node Outcome record. For the 3 question example above, I need 3 records of Question Node for each question. For Question No.1, there are 2 Queston Node Outcome records representing A and B. Both have Question No.1 as Parent Node, but Question No.2 and No.3 as Target Node respectively.


With the new pattern, no matter you have 3 questions or 300, the flow remains the same like this:


All the complexity is handled in those records because each Node Question Outcome has a target Question Node. When user runs the above flow, an initial Question Node record is passed as an input, and then the flow dynamically "Get" the next question based on the user's interaction. And the magic comes from the Record Choice Set resource in the flow.


In the above example, the Quesiion No.1 is the curNode, and this element will get Option A and B as choices. Depending on the user's choice, the next Node will be set as Question No.2 or No.3.

Remember I mentioned the type field on the question? It provides the criteria for the flow to identify the corresponding data to proceed. So other than choices, we can potentially collect number, date, and text answers as well. And that's why I have some decision nodes to check types in the above flow.

But how to capture the data collected in the flow? That's those assignment elements for. I have a text variable to be edited in the assignment, so each question-answer pair will be added to it as a new line. And it will generate a large string like this at the end:

Question1 Answer1

Queston2 Answer2

QuestionN AnswerN

Based on the use case, you may need to create or update records using the above information, but that needs some Apex involved to parse the data.


Limitations

The biggest limitation is that you can only place one question using Record Choice Set on each screen. Otherwise, the variable related to the record will only hold the last choice you picked.

It is hard to allow multi-select, but it is possible with Apex because you need to parse the selected answer and reverse it back to a collection variable.


Variety

This pattern can be used by combining the normal way to build flow. You can use just one object to hold those parameters that change frequently, and still build the logic into flow. So you can easily have the DML features or bypass the limitation of one question on each screen.


This article only shows the core implementation of this pattern. I have some ideas to enhance it, like adding field names into those choices, so potentially I build a JSON string at the end, which is easier to create and update records of any object. Anyway, the sky is the limit. 

Thursday, April 21, 2022

Open Process Builder as Flow

 

We all know that Salesforce will sunset workflow and process builder (PB) in the near future, and for workflow migration to flow, there's an official tool that will be GA next release. There might be something similar coming to PB eventually but for the moment I think we still need a decent amount of effort to "rebuild" them into Flow.

I recently got a task to rebuild a quite big PB (with quite a few decisions). Since it's saved as Flow in the metadata, I wondered if it's possible to edit it as a Flow. After some experiments, I figured out a hack to do this.

First, run a SOQL using Tooling API, in order to get the PB's Id.

SOQL:

SELECT  VersionNumber,  Definition.DeveloperName, Status,  id from flow where Definition.DeveloperName = 'Process_Name'

Then you can see a list of all the versions of that PB with corresponding Ids. Grab the one you want to rebuild (usually the latest version or active version), which looks like

'301xxxxxxxxxxxxxxx'

Finally, click any Flow in your Flow list to open its editor, and you'll see an url like

https://yourdomain.lightning.force.com/builder_platform_interaction/flowBuilder.app?flowId=301xxxxxxxxxxxxxxx

Simply replace the Id, and voila, the PB is opened in flow builder.

The layout may be ugly and messy when you open it, but you can easily get a good-looking version by changing the UI from Freedom to Auto-Layout.

Freedom



Auto-Layout



But it's not perfect yet, we still have a bunch of work to make it a record-triggered flow, starting with Save As button on the top right corner.


You will see an error immediately because Salesforce doesn't know what object it is.



It's easy to fix by selecting the same object of PB in the Start element.


You also need to change the trigger to match the original PB.

The next thing is to add an element to assign the record to the myVariable of PB after the Start element.



Depending on the elements in PB, you may still see some warning messages on the top right corner telling you some formula does not work, which requires your attention. Maybe some other performance issues, but you should be able to activate the Flow now.



Until now you have a quick and dirty Flow of the old PB. This should give you a better starting point for the rebuilding process. To properly migrate the PB, it's better to look through the logic and refactor it into Flow, and that's why I say this is a hack that should be only used with your own discretion.







Saturday, February 26, 2022

My Opinions of learning Flow

I recently saw a very complicated screen flow that looks like a subway map in some big city, and different comments about it. As a fan of flow, I want to give my 2 cents here.

For a process with user interactions, Screen Flow could be a very good option. Because it covers the front and back end together, and the famous MVC is done at one time. The development is usually fast, which is very important in today's rapidly changing world. Especially it can build a POC and demo to the client for what can be done in a vivid way. I once built a Screen flow in the first meeting with the client, and it was a pretty awesome experience.

Automatic flow does not have an overwhelming advantage (other than not writing code), but a few disadvantages: low efficiency, no mandatory unit test, more limitations (such as the 2000 elements). But customers still want to use it, I also recommend them to do so, why? Because Flow is the tool matching the idea of "no-code, low-code", which the Salesforce platform has been advocated for many years. It's easier to build, and empower more citizen developers. Of cause, great power brings great responsibility, so an improperly built flow can break exiting functions. That's why I think everyone in the Salesforce ecosystem should learn some flow skills.

Is Flow a flash in the pan? I think at least from the Salesforce roadmap perspective it is not, so if you want a career related to the technical side of Salesforce, you must learn to flow well. Is Salesforce itself a flash in the pan? At least in the past 20 years, it's been a great success. In the next 20 years? Who knows, but I personally think it still is a quite good career choice in the near future. All of the original DJI (founded in 1896) stocks have been either removed or delisted, so good times do not last long. It's unlikely that a business will keep prosperity for hundred years. However, our life span as human beings does not require us to consider that long either. 

The focus of learning Flow is to know the best practices, good patterns, and in my opinion most importantly boundaries. What Flow can do, and what it can't do what it can. Flow is not a panacea, nor is Apex. There are always applicable scenarios for different tools, which is why that all-purpose saying "It depends" appears so often. What distinguishes the different skill levels is the ability to fill in the phrase of what it really depends on. 

Finally, to take a step back, if you really think that Salesforce will not last long enough for your career, there is no harm in learning Flow, because it is the same basic concepts of programming. Regardless of what tools or language you use, at the end of the day it is always about loop and control flow. If you want to deepen your knowledge, learn design patterns, that is the higher level of guidance, Flow, Apex or Java, is only a tool. However, it's necessary to have some foundation to learn the patterns, and Flow is a very friendly starting point.

Tuesday, February 15, 2022

Interesting DML Error in Salesforce Flows

 I recently saw an interesting error in one of the projects:

This error occurred: DUPLICATE_VALUE: Maximum number of duplicate updates in one batch (12 allowed). Attempt to update Id more than once in this Api call: record Id.

It came from a flow that has been running smoothly for a long time. The documentation https://help.salesforce.com/s/articleView?id=000314518&type=1 doesn't really explain my issue, because I don't have any scheduled actions or a wait element.

After some digging, I realize that if you try to update the same record multiple times in one transaction, then you may face this issue. In my case, I have flow automation on the child object that updates the corresponding parent object. When the parent has many children records (more than 12) and the values to be updated on the parent are different, I see this error. For example, the parent has 20 children namely: 1, 2, 3 ... 20, and each child is updating a text field on the parent using their own name, so the same transaction can end up assigning 1, 2, 3 ... 20 to the parent record's text field, and triggering the above error. If they update the parent in the same way (assigning the same value for the field), then there won't be any error, and that's what I missed in the past without knowing this potential issue.

Once the root cause has been determined, I can start to figure out a fix. The goal is quite simple, to avoid this recursive automation. So depending on the use case we need to add a decision element to check that field to update in the flow. For example, I want to save the maximum number then I need to compare the existing value and the target value in this decision node, if the existing value is greater than or equal to the target value, then the flow ends instead of continuing to update the parent record.

Interestingly I tried to replace the original logic with a trigger (without the decision part) using the code below, there is no error at all. 

trigger triggerContactName on Contact (after insert, after update) {

    List<Id> ids = new List<Id>();

    for (contact con: trigger.new){

        ids.add(con.accountId);

    }

    

    Map<Id, Account> accs = new Map<Id, Account>([Select Id, site From Account Where Id in: ids]);

    for (contact con: trigger.new){

        accs.get(con.accountId).site = con.lastName;      

    }

    update accs.values();

}

My understanding is that behind the scenes Salesforce isn't really bulkifying flows in the same way of using maps in my trigger. It may just list the record multiple times and update once, hence the maximum number of 12. 

This error reminds me of another error I faced a while ago, which is worth mentioning as well.

System.TypeException: Cannot have more than 10 chunks in a single operation. Please rearrange the data to reduce chunking.

This one is also interesting because the chunk is essentially a list of records of the same object, but as indicated in the error message, the sequence matters. So we should try to keep the records of the same object together to avoid this issue. For example, the following 2 snippets are doing the same thing, but the 1st one will trigger the error, and the 2nd one works fine.

Mixed pattern:

list<sObject> records = new list<sObject>();

for (integer i=0; i<20; i++) {

    Account acc = new Account (name = 'Test' + i);

    Contact con = new Contact(lastname = 'Test' +i);

    records.add(acc);

    records.add(con);

}

insert records;

Sorted pattern:

list<sObject> records = new list<sObject>();

for (integer i=0; i<20; i++) {

    Account acc = new Account (name = 'Test' + i);

    records.add(acc);

}


for (integer i=0; i<20; i++) {

    Contact con = new Contact(lastname = 'Test' +i);

    records.add(con);

}

insert records;

Flows are great, but we need to be careful about these little traps. As I recently learned, error handling is an absolute must everywhere, because the automation will fail, it's never a question of if it fails, but when it fails :)

Wednesday, January 19, 2022

A few thoughts about Salesforce Validation Rule

 Validation rule is a good friend of admins and end-users, to avoid bad data entry. The famous quote "An ounce of prevention is worth a pound of cure", is particularly true regarding data quality. Nobody wants the rubbish data in the production org to prevent them from getting valuable insights. However, similarly to all Salesforce features, there are always considerations.

A few common ones are already listed in the documentation, such as:

a specific setting for lead conventions (Require Validation for Converted Leads),

Web2Case and Web2Lead apply validation before creating records,

Mass Transfer tool, scheduled actions won't trigger validation,

Cannot refer to auto-number field, compound field, or dependent picklist, etc.


Recently in the practice, I found another interesting behavior.

I have 4 percentage fields to split a total amount of an opportunity, namely: Share1, Share2, Share3, Share4. To ensure they always add up to 100%, I built a validation rule.

1st version: 

Share1__c + Share2__c + Share3__c + Share4__c !=1

Then I tested with all 4 numbers are entered, looked good. But when I left any of the fields blank, the error was gone!

Hmm, something went wrong with the empty field, which seems to bypass the validation.

The good side is that we have the formula BLANKVALUE() to rescue.

2nd version: 

BLANKVALUE(Share1__c,0) + BLANKVALUE(Share2__c,0) + BLANKVALUE(Share3__c,0) + BLANKVALUE(Share4__c,0) ) !=1

Seemed to be working fine until I left all 4 fields blank. This could be OK for a use case that at least one of these fields is mandatory, but I want to allow users to leave all of them blank. So I need to add more logic for checking empty fields.

3rd version:

(BLANKVALUE(Share1__c,0) + BLANKVALUE(Share2__c,0) + BLANKVALUE(Share3__c,0) + BLANKVALUE(Share4__c,0) !=1)

&&

!(ISBLANK(Share1__c) && ISBLANK(Share2__c) && ISBLANK(Share3__c) && ISBLANK(Share4__c))

Some variation, if you always populate the Share1 first when you have value for these fields, the above rule can be simplified to this:

(Share1__c + BLANKVALUE(Share2__c,0) + BLANKVALUE(Share3__c,0) + BLANKVALUE(Share4__c,0) !=1)

&&

!(ISBLANK(Share1__c) 

This may matter because the maximum length is 3900 characters, which is not easy to hit, but it's always to keep the formula as short as possible.


Another thing worth mentioning is when record types get involved, it's better to write inclusive rules instead of exclusive ones.

For example, the object has 3 record types TypeA, TypeB and TypeC, when you want to apply a rule for TypeA and TypeC, you can either use 

RecordType.Name = 'TypeA' || RecordType.Name = 'TypeC' 

or 

RecordType.Name != 'TypeB'

The latter one is even shorter, but I would recommend use the former. Why? It's for the future. If you use exclusive rules, when you add new record types that don't need validations, you have to go back and modify them. But if you use inclusive ones you don't need to worry about it.



Tips for Salesforce Certified Strategy Designer Exam

A little late to the party, but I just passed the latest Strategy Designer exam. Since it's a really new one (about one week old), there...