Logo

Tag Archive for workaround

The Model View Controller (MVC) Method of Building EA Dashboard Interactivity With Bindings – Part 1

Einstein Analytics Dashboard MVC Design Pattern Introduction

In this “Part 1” post, I will introduce you to the concept and discuss how it works at a high level.  In part 2, we’ll cover specific code and example.

For the last few years I have been using a little hack with SAQL bindings that I like to refer as using an MVC design pattern to overcome some limitations I have encountered.  Primarily it involves using a saql step as the ‘controller.’  The use case is that I kept running into scenarios where I could not accomplish what I wanted using standard bindings.  I wanted interactivity to:

  • Swap out a custom calculated KPI (on the fly) as the measure
  • Swap out the sort using different groupings
  • Swap out the measures or time frames used in custom windowing functions – moving average, running total, etc.

For certain interaction changes you cannot just place a toggle and a value for that parameter (binding) and then replace it within the SAQL query.  In these cases, I have used this MVC design.  The first step is to create a ‘Controller’ step.  This is a custom saql query that pulls a single row (limit 1) from a dataset that will always have results.  You don’t care what dataset it is, the underlying data from that dataset does not matter.  You are just using it to write your own logic.  From here you can write case statements that reference your other static steps that are tied to your toggle widgets.  For example:

  • If toggle widget 1 == “Moving Average” && toggle widget 2 == “Dials per Rep” then … (place window function with moving average using the right measure column and referencing the right period step (15 day moving average, 30 day moving average, etc.)

Then, your primary SAQL step that provides the results for the visualizations would reference this ‘controller’ step.

All inputs are fed into the controller step and then the visualization steps/queries reference the controller.

More to come on this topic with specific examples but I hope this has piqued your interest and given you something to think about!

 

SAQL String Filter Bindings With Commenting

Salesforce Quick Tips

Use SAQL Bindings for Convenient Text Filters

Using the “asEquality” function within bindings works in use cases where your data contains the fields you want to filter by.  This is great in many but not all cases.  I have recently ran across a number of scenarios where the request is to filter by some complex logic – multiple fields, aggregations, the result of another step (only pull the top 5% of performers by the date range selected) but only if the selection to filter at all is made by the user.

In these cases you want nothing (additional) to be filtered in the query unless the proper selection is made by the user interacting with the dashboard.  In these cases I have resorted to text/string based filters.  If no selection is made or the default is used we simply add two dashes “–” to comment out the line.  If the filter is to be used, we simply pass the appropriate text string from the static step or the new term, “custom query.”

This has been very handy in a pinch when either the filter criteria is not something that is contained within the dataset already or in cases where it would not be possible to add them to the necessary dataflow to include them in the dataset.

How It Works – String SAQL Filter Bindings

This is a basic use case that does not require a string binding to filter, but I wanted a simple and clear example to explain the concept.  In my case, I am starting with a pillbox toggle widget to control the filtering.  Then I go to create my custom query:

SAQL String Filter Binding Custom Query I simply add my three values I want to display visually to the user in the “Display” column, and then add a hidden “Text2” column that will be the corresponding filter values.  When the user selects “All” I don’t want any filter so I just add two dashes to comment out the line “–” and the two filter values that I would write manually within the SAQL if the user selects the other options.

This creates a query or step that looks like this in the JSON:

SAQL String Filter Binding Step

I change this query to “Single Selection (required)” and set the initial selection to “All.”

Then I need to place the binding within my original query/step.  I do that in the highlighted section here:

SAQL String Filter Binding Placement

That’s it.  Now when I toggle between the options, the binding either passes in two dashes to comment out the line or the necessary filter line:

 

When I “Show Details” to view the “Final Query” when running the dashboard and “All” is selected, it passes the two dashes “–” as expected:

SAQL String Filter Binding Comment

When I do the same with one of the other values selected, in this case “Event” – it passes the appropriate filter:

SAQL String Filter Binding Filter

Wrap

I believe that too often the ‘asString’ binding function is glossed over and use cases that are only possible with SAQL and passing the binding as a string are missed.  You can use this almost anywhere necessary within a SAQL query.  I’ve even used it to toggle between time zones and used a string binding within every filter, grouping, select/generate, order, etc. statement within the final query.  This allows the user to change the query entirely with a single click.  Going back to the filter example, I’ve had use cases where I need a 300 character filter statement within the query most of the time, so I add it to the original query.  But in some cases the user wants to disable that complex filter.  In these cases, I create a custom query and a binding that would return either the two dashes “–” to comment out the filter or nothing/empty string “” to keep the filter in place.

Is this helpful, silly, crazy?  There are so many use cases for this and it really does come in handy while working real world scenarios.  Would love to hear your thoughts in the comments!

Wait for Dataflow to Run? Ain’t Nobody Got Time for That! Build a SOQL Step for Real-time Queries

Bypass Dataflow Schedule Using Real Time SOQL Queries

Have you ever wished you could display time-sensitive widgets on an Einstein Analytics dashboard that update in real-time?  By using SOQL query steps, you can avoid having to wait for the dataflow to finish running during the next regularly scheduled refresh.

How To Create A SOQL Step

I recommend using workbench.developerforce.com to help craft the SOQL step if you are not familiar with SOQL.  There is a bunch of good information out there on how to format your query including aggregate functions and using ‘rollup’ with grouping.  After you know the general query you need, create a new dashboard (blank dashboard).

You have a couple options to create the SOQL Step.  You could create a standard step using the UI and then jump into the JSON to convert it to a SOQL step, or you could just paste code to create a new SOQL step manually.  We are going to use the latter method here.  After following this guide, however, you will have all the skills you need to use the former method when you need to build from scratch.

CTRL/CMD+E to get into the JSON and locate this line:

 "steps": {},

Paste the following code in-between the curly braces on that line:

"soql": {
 "groups": [
 "CloseDate"
 ],
 "numbers": [
 "sumAmount",
 "NumOpps"
 ],
 "query": "SELECT sum(Amount) sumAmount, CloseDate CloseDate, COUNT(Id) NumOpps from Opportunity WHERE IsWon = True AND CloseDate > 2018-12-31 GROUP BY CloseDate ORDER BY CloseDate ASC",
 "selectMode": "single",
 "strings": [
 "CloseDate"
 ],
 "type": "soql"
 }

You should now be able to click “Done” button and navigate back to the dashboard designer.  You can drag on a chart of table widget and then drag over the new “soql” step you created on top of the chart to visualize the data that is pulled.

What did we create?  We created a step that pulls won opps with a close date post 2018-12-31. You may need to adjust this date as needed to pull results and/or ensure you are not pulling such a large range to have performance impacts.  You can change this query as needed to fit your situation.  You can even use relative dates like “= Today”.  Here is one that is grouped by year and provides the count and avg amount for opps by year.  Rollup also includes a total row for the entire range pulled.

"soql": {
 "groups": [
 "CloseDate"
 ],
 "numbers": [
 "avgAmount",
 "NumOpps"
 ],
 "query": "SELECT avg(Amount) avgAmount, calendar_year(CloseDate) CloseDate, COUNT(Id) NumOpps from Opportunity WHERE IsWon = True GROUP BY rollup (CloseDate) ORDER BY CloseDate ASC",
 "selectMode": "single",
 "strings": [
 "CloseDate"
 ],
 "type": "soql"
 }

How To Add Bindings/Interactivity

Does using a real-time step query prevent you from building in user interactivity?  No way! You can add Bindings – even in a SOQL step!

In the following example, we are able to toggle between a Year grouping and a Month grouping by using a related “groupToggle_1” static step.  This pillbox toggle uses the static step, and based on the user’s selection passes in one of two values into the “soql” step.

 "groupToggle_1": {
 "broadcastFacet": true,
 "label": "groupToggle",
 "selectMode": "singlerequired",
 "start": {
 "display": [
 "Year"
 ]
 },
 "type": "staticflex",
 "values": [
 {
 "display": "Year",
 "value": "calendar_year(CloseDate)"
 },
 {
 "display": "Month",
 "value": "calendar_month(CloseDate)"
 }
 ]
 },
 "soql": {
 "groups": [
 "CloseDate"
 ],
 "numbers": [
 "avgAmount",
 "NumOpps"
 ],
 "query": "SELECT avg(Amount) avgAmount, {{cell(groupToggle_1.selection,0,\"value\").asString()}} CloseDate, COUNT(Id) NumOpps from Opportunity WHERE IsWon = True GROUP BY rollup (CloseDate) ORDER BY CloseDate ASC",
 "selectMode": "single",
 "strings": [
 "CloseDate"
 ],
 "type": "soql",
 "start": []
 }
Einstein Analytics SOQL Step Binding by Year

Toggled by Year

Einstein Analytics SOQL Step Binding by Month

Toggled by Month

If you haven’t built a static toggle step before for this, just click the big “Create Step” button from the dashboard designer and choose “Create a Static Step with Custom Values” from the bottom of the prompt.  Then, just fill out the information you need (you can always change it later in the JSON).

Einstein Analytics SOQL Step Create Static Step

Create Static Step Through UI

Visualize Data As Chart Or Table

After you create your step and any associated bindings, format your visualization!  This is the part that you know and love about Einstein Analytics.  Modify the formatting of the chart or table for the best representation of the data you pulled.

There are a variety of use cases for this:

  • Today’s Orders
  • Today’s Activities – Calls/Meetings/etc.
  • Today’s Closed Opps
  • Today’s Cancellations
  • Today’s Approval Requests
  • Today’s New Opps
  • Today’s Urgent Cases

Anything that may be created or change in-between dataflow rebuilds that needs to be displayed or highlighted for your end user is a potential to use SOQL steps.  Primarily I could see this as being valuable for current intraday data.  What use cases have you found?  Where have you gotten stuck?  I would love to hear how this has worked for you in the comments!

Convert Decimal Time Into Hours and Minutes – HH:MM – Wave Einstein Analytics

Salesforce Quick Tips

Thought I would write about this Analytics Quick Tip as I have found zero information related to this online and I would imagine it would be a pretty common use case.

The Problem With Decimal Hours

Often you have ‘duration’ type measures stored as ‘number of seconds.’  Think of the duration of a service ticket from one stage to the next, or the duration of a sales call.  Many times you want to aggregate all of these together for a particular user or particular day.  Once you’ve aggregated them together, you can easily convert these into decimal hours by dividing by 60 to arrive at # of minutes and 60 again to arrive at # of hours (divide by 3600 once to shortcut this step).  The trouble is then you have a confusing metric of decimal hours.  3.45 hours does not equal 3 hours and 45 minutes.  It is closer to 3 hours and 30 minutes!  4.87 does not equal 4 hours and 87 minutes!  No matter how many times you try and educate your users on decimal hours it is confusing for them to discuss with their teams and properly rank and compare them to each other.

A SAQL Solution For Converting Decimal Hours to HH:MM

I think it’s helpful to break each step down into its component part before we get into the code of how we are going to accomplish this.

We want to take 4.87 and convert to 4:52.

  1. First we want to take out the # preceding the decimal point as it is already a valid # of hours. (4)
  2. Then we want to add a colon to separate hours from minutes. (4:)
  3. Then we want to take the remaining number post decimal point and multiply by 60 (this number will be formatted to only 2 digits and placed after the colon). (4:52 – we arrive at this by taking the entire 4.87 and subtracting the truncated number of 4 which leaves .87 and then we multiply that by 60)

Simple corresponding example below without formatting and where ‘decimalhour’ is already in decimal hours.  You would just replace ‘decimalhours’ with your duration measure and divide by 3600 if converting from seconds.

1. trunc(sum('decimalhours'))
2. + ":" +
3. sum('decimalhours') - trunc(sum('decimalhours')) * 60

Here is an example of the full saql w/ formatting.

 number_to_string(trunc(sum('decimalhours')),"#") + ":" + number_to_string(((sum('decimalhours')) - (trunc(sum('decimalhours')))) * 60,"0#") as 'HH:MM'

Now we have a very easy to read table in hours and minutes!

Owner Total Time
Bruce Kennedy 4:36
Catherine Brown 4:02
Chris Riley 3:55
Dennis Howard 3:29

The only issue I’ve found is when someone is right on the hour, say 4:00 and it only displays a single zero. I’ve solved for this with a case statement as in all of my use cases we are 5 hours and below so I only need 5 case statements.  This is very easy to do.  I had first tried replace but it would then replace all scenarios where there is a zero after the colon.  I’m sure there is a more elegant solution but I have not had the chance to spend very much time on this piece and the case statement solved my particular situation.

Did this solution help you?  Do you have another method to solve the same problem?  Let me know in the comments.  I would love to hear from you!

Symbols That Sort After “Z”

Why would you want a symbol that sorts after “Z” in ascending order?  Well, here is an alternate title:  How To Place the Summary or Total Row Last or at the Bottom of the Table.

I’ve run into the scenario multiple times where I am creating a total or subtotal row that summarizes the other rows in the table.  This is also something I’ve done if I have a bar chart and want one of the bars to summarize the rest.  If I leave it alone or force a “-” symbol before and after, like “- Total -” it actually places this at the very top of the table.

q1 = load "DTC_Opportunity_SAMPLE";

q1 = group q1 by 'Opportunity_Owner';
q1 = foreach q1 generate 'Opportunity_Owner' as 'Opportunity_Owner', count() as 'Count';
q1 = order q1 by ('Opportunity_Owner' asc);

z1 = group q1 by 'Opportunity_Owner';
z1 = foreach z1 generate "- Total -" as 'Opportunity_Owner', sum(sum('Count')) over([..] partition by all) as 'Count';
z1 = limit z1 1;

q1 = limit q1 2000;
q1 = union z1, q1;

q1 = group q1 by 'Opportunity_Owner';
q1 = foreach q1 generate 'Opportunity_Owner' as 'Opportunity_Owner', sum('Count') as 'Count';
q1 = order q1 by 'Opportunity_Owner' asc;

The end result is a table with the summary total at the top:

Opportunity Owner Count
– Total – 671
Bruce Kennedy 41
Catherine Brown 38
Chris Riley 47
Dennis Howard 30
. . . . . .

Sometimes we want the Summary or Total row to be at the bottom, or the summary bar (in the case of a bar chart) to be last.  We want the summary row last while still keeping the remaining rows sorted in ascending or alphabetical order.

How To Place Summary or Total Row Last / At the Bottom

Now it would be easy to place the total row at the bottom if we just change the last line of code to ‘desc’ instead of ‘asc.’  The problem with this approach is that it would sort all of the names in descending order and they would be in reverse alphabetical.

Another approach is to reorder the union so that z1 is listed second instead of first.  However, if you need to re-project after the union as you often need to do this does not help.

The solution here is to place a symbol that sorts after z as the text of the ‘Total’ row and leave it sorted in ascending order.  According to a post on stackoverflow, here are some symbols that sort after z:

  • Ω (option+z)
  • π (option+p)
  • µ (option+m)

Reference: Symbols that sort after z

If we place one of these symbols at the beginning of the name instead of “- Total -” it will ensure that the row always sorts last.  Even better, you can place it into a new “helper column” named ‘Sort’ and use it to sort and then discard it at the very end:

q1 = load "DTC_Opportunity_SAMPLE";

q1 = group q1 by 'Opportunity_Owner';
q1 = foreach q1 generate 'Opportunity_Owner' as 'Opportunity_Owner', 'Opportunity_Owner' as 'Sort', count() as 'Count';
q1 = order q1 by ('Opportunity_Owner' asc);

z1 = group q1 by 'Opportunity_Owner';
z1 = foreach z1 generate "- Total -" as 'Opportunity_Owner', "ΩTotal" as 'Sort', sum(sum('Count')) over([..] partition by all) as 'Count';
z1 = limit z1 1;

q1 = limit q1 2000;
q1 = union z1, q1;

q1 = group q1 by ('Opportunity_Owner','Sort');

q1 = foreach q1 generate 'Opportunity_Owner' as 'Opportunity_Owner', 'Sort' as 'Sort', sum('Count') as 'Count';
q1 = order q1 by 'Sort' asc;
q1 = foreach q1 generate 'Opportunity_Owner' as 'Opportunity_Owner', sum('Count') as 'Count';

This results in a table as we would expect:

Opportunity Owner Count
Bruce Kennedy 41
Catherine Brown 38
Chris Riley 47
Dennis Howard 30
. . . . . .
– Total – 671

 

To summarize, we are using one of the symbols that sorts after “z” in the name of a new column and then sorting by it to ensure that this row is always last.  Then, we discard that column so you never know it was there!

Let me know your thoughts, comments, suggestions or other workarounds you’ve found to present the data to meet the needs of your users!

Can’t Clone Date or Range Widgets – Multiple Pages Workaround

Date or Range Widgets – The Problem

I wanted to create an interactive, multiple page dashboard like the one below:

The issue is that I wanted the same date widget to be accessible on every page as your user may want to modify the dates at any time.  Typically, you can create all of the widgets (containers, numbers, charts, lists/dropdowns) on a single page and then select them to add them to other pages, as below:

The problem is you cannot do this with Date widgets.  As of Winter ’19 (in a pre-release org) you get an error message that states: “Warning! Can’t clone Date or Range widgets.”  hmmmm….OK

Warning! Can't clone Date or Range widgets.

Warning! Can’t clone Date or Range widgets.

As the video demonstrates above, the “Add to Page” feature works fine for dropdown lists for use in faceting or bindings along with many other widget types but not for dates…

Workaround #1 – Clone Entire Page

If you are lucky enough to place the date widget onto the main page and set it up prior to creating your additional pages you can just clone the entire page and your date widget will be cloned (despite the error message) onto every page using not only the same step but the same widget (a linked widget just as you would expect).

Date Widget Is Linked!

Of course, this is only an option if you have not yet created your additional pages. In my opinion, it is the preferred workaround because it create a truly ‘linked’ widget and changes to the widget will not have to be manually replicated onto additional pages.  Additionally, it is a no-code workaround which is another plus for many.

Workaround #2 – Update Steps

This workaround consists of creating a second, third, … (as many as necessary) date widgets for each additional page required in your dashboard.  Then, you will update the JSON to point these new date widgets to the original step that the original date widget is using from page 1.

After creating the date widget onto the canvas, actually set up the underlying step by clicking on the “Date” link and selecting a dataset and date field.  You can choose any date field because we will be updating the underlying step manually in a minute.

This step of actually setting up a step is necessary because you cannot drag the date step from the list of steps to the date widget.

Can’t drag date step to date widget – I guess that’s why it’s italicized!

Now that we have the original date widget and step from page 1 AND all of the additional date widgets on subsequent pages we CMD+E or Ctrl+E into the JSON!

Locate the original date widget and step from page 1 (you can also click on the widget prior to accessing the JSON and view the details there).

In this case I can see that my widget from page 1 is using the step “Last_Activity_1”.  You will want to save this value because we want to update the date widgets from the other pages to also use this step.  Once you have located the other date widgets and the steps they currently leverage you can update them.

I like to use the Find & Replace tool that is hidden until you CMD+F or Ctrl+F twice (that’s right, use the same keyboard shortcut twice in a row!  Magic!).  Now it’s simply a matter of finding the incorrect values and replacing them with the correct value.

Once I have replaced all of them (only the ‘Step’ values), the widgets are linked to the same step and the user can now update the date widget on any page and their selection will persist while navigating to any other page.

Have you encountered this before?  I suppose a third workaround may be using code to actually add the same widget (linked) onto each page through JSON however this would be a lot more work for virtually the same result.

How long will we have to live without the ability to drag date steps to the widget or use the Add to Page functionality for date widgets? (Even though we can currently clone the entire page with the date widget included).  Let me know your thoughts and your best Einstein Analytics workarounds in the comments!

Copy Salesforce Matrix Report & Other Tables Into Excel Without Check Boxes

Salesforce Quick Tips

Copy Salesforce Matrix Report Into Excel

As much as we’d like to keep and share data in the cloud it’s sometimes necessary to take your salesforce data and place it in a spreadsheet to share with others.  You can always export the detail view (tabular view with one row per record) into a .csv or .xls format.  However, sometimes it’s beneficial to be able to take your summarized matrix view and paste directly into Excel.  Especially in cases where you may have written a lot of complex report formulas (these don’t export unless you use “Printable View”) or added other aggregations: sum, min, max, avg.  In order to replicate in Excel you would need to create a pivot table on the detailed export.  Why rebuild if you don’t have to?

Check Boxes: The Issue With Copying and Pasting From Salesforce Matrix Reports

The primary issue with copying and pasting from Salesforce matrix reports is that the copy/paste includes the checkboxes on the left side of the table.  I’ll show you how to get rid of those pesky boxes and copy/paste like a pro!  Note – you can also use the “Printable View” button, however this trick works in a pinch and works for more than just reports as noted below!

Salesforce Matrix Report

Step 1: Copy the entire table as displayed below.

Copy Salesforce Matrix Report Cells

Very important to copy every cell, so start above the table if necessary to ensure you’ve copied the first and last cells.

Make Sure to Copy From Above Report Results

Step 2: Ctrl + c to Copy!

Step 3: Paste into Excel.

Paste Salesforce Matrix Report in Excel

Salesforce Matrix Report In Excel…not pretty!

See the checkboxes in column A?  You can delete the column or try selecting them with your mouse, and still you won’t be able to remove them using those methods!  We’ll show you how you can select the checkboxes, so you can remove them!

Step 4: Press F5 and click “Special…”

F5 In Excel Click Special

Step 5: Select “Objects” and click OK.

Select Objects and OK

Step 6: Now the checkboxes are selected, so hit the “Delete” key on your keyboard to get rid of them!

The Check Boxes are Selected and Delete

Step 7: Now the formatting needs some work.

Fix Excel Formatting

Step 8: Remove Column A.  Fix colors, borders, font and size.  Now you have one version of a Salesforce Matrix Report in Excel without the check boxes!

Final Excel Table Copied From Matrix Report in Salesforce

Other Salesforce Tables

This trick also works for other Salesforce tables that you want to copy and paste from, like a list of standard or custom fields on an object:

You can just highlight, copy & paste and then use the trick above to remove the “Indexed” fields and/or check box images.

Conclusion

This saves you time from rebuilding and ensures accuracy (no more copying and pasting individual #’s).  You can keep the formulas and summaries that you worked so hard to build in Salesforce and get them into Excel for your users who need them presented this way.  Please remember to also try the “Printable View” button which also may meet your needs, however it won’t work for lists of fields and other tabular data out of your Salesforce org!  Do you have any other tips and suggestions for working with Salesforce reports in Excel?  I would love to hear them!  Comment below or tweet them @SFDC_r!

Avoid Long Load Time To Edit Dashboard Component Report

Salesforce Quick Tips Edit Dashboard Component Report

Edit Dashboard Component Report

Have you ever wanted to edit the underlying report for a dashboard component and found it cumbersome?  A dashboard executes with a ‘running user’ and this user feeds into all of the underlying reports.  This is great, because it means that you can create a single set of reports and not have to edit the reports for the dashboard to provide useful data to an entire set of your users.

The Problem – Edit Dashboard Component Report

Because your user is likely placed very high up in the role hierarchy (so that you have access to all or most of the Org’s records) when you click on a dashboard component to view the underlying report, it can take many minutes (or even timeout).  Your users don’t have this issue, they click the dashboard component and the user running the report would only see records at or below them in the role hierarchy – likely running quickly.  You as the admin, however, can only finally click “Customize” to edit the report after waiting a long time (and that’s if it doesn’t timeout).

The other option to avoid the above dilemma (and wait) is to navigate to “Reports” and locate the report you want to edit.  Instead of running the report, click the arrow and “Customize.”  The issue here is that you have to know which report you want to edit, which probably requires “Editing” the Dashboard to view the exact name of the report for the specific component.  This can be time consuming and cumbersome as well.

A Better Solution – Salesforce Quick Tip!

I present to you a better solution.  Simply right click the Dashboard Component you would like to edit the underlying report for and copy the URL.  Paste the URL into your browser (probably a new tab) it should look something like this: https://[YOURSERVERURL].salesforce.com/00O61000003p9Nj if it happens to have anything after the Report ID – just remove it so it looks like the above.  Now add “/e?” to the end of the URL.

Final URL: https://[YOURSERVERURL].salesforce.com/00O61000003p9Nj/e?

Problem Solved

Now just hit Enter and you will be taken directly to editing the underlying report – and it will load quick.  Never get stuck waiting for a report to load just to edit it!

What quick tips do you have related to Dashboards or Reports?  Feel free to post in the comments or on Twitter!

Future-Proof Your Org w/ a Relative Salesforce Server URL

Salesforce Quick Tips Logo

Use A Dynamic/Relative Salesforce Server URL

Today, I’m going to show you how to future-proof your Salesforce server url.  Instead of specifying a specific Salesforce server, we will use a relative Salesforce server URL.  What does that mean exactly?  Well, have you ever created hyperlinks used in many email templates that point to a page within Salesforce?  Are they “hard links” that point to a specific Salesforce server, “https://na30.salesforce.com/” for example?

If Salesforce moves your org to a different server, you will have to go into each and every email template and make the change!

The Solution/Workaround – Relative URL’s In Email Templates

Create a formula field on the User object using the following formula:

LEFT($Api.Enterprise_Server_URL_270, FIND( "/services", 
$Api.Enterprise_Server_URL_270) -1)

Then, use this field whenever you need to reference the Salesforce Server URL.  If Salesforce moves your org to a different server, no changes necessary!

Do you have any other tips for future-proofing your org?  Post in the comments or message on Twitter!

Also check out the Go With The Salesforce Flow series!

Go With The Salesforce Flow – Using Multi-Select Picklists

Salesforce Flow Series

Using Multi-Select Picklists Within A Flow

Have you ever tried using multi-select picklists within a Salesforce Flow?  Did you update the picklist field with a new value and instead of adding the new value it replaced the existing value(s)?

I’ve found this to be a very unintuitive aspect of Salesforce Visual Flow.  If you do it wrong, you update the multi-select picklist field with new values, but wipe out the already existing values.  I have good news, however, because today I will show you a workaround!

Adding, Not Replacing, A Multi-Select Picklist Value In Flow

First store the existing picklist values as a multi-select picklist variable in your Record Lookup step.

Multi-Select Picklist Flow - Store Variable

Ensure the variable is set to “Picklist (Multi-Select)” data type.

Multi-Select Picklist Flow - Variable Type

Use a “Multi-Select Picklist” field within a Screen Element for users to interact with.

Multi-Select Picklist Flow - Choice Field

Create a new choice to be used as the default and set it to the variable that was set in the Record Lookup step.

Multi-Select Picklist Flow - Create Default Choice

Now create a new choice for every option you would like to provide your user.

Multi-Select Picklist Flow - Create Desired Choices

After you are done creating the necessary choices, ensure you set the correct one as the default choice as shown on the “Screen” image above.

Now create a Record Update step to update the desired multi-select picklist field from the value in the screen field.

Multi-Select Picklist Flow - Update Record From Multi-Select Field

 

That’s it!  But, what if you want to update using a different type of Screen Element field?  Check out below to see how you would update with a boolean field.

Create a boolean field.

Multi-Select Picklist Flow - Boolean Field to Update Multi-Select Picklist

 

Then, after creating a Decision step to determine whether the user has checked the box, create an Assignment step.  The Flow will utilize this step only if the box is checked.  Here, I’ve used another variable to store the record’s current picklist values.  Ensure that you use the “Add” operator and that you add a semicolon before the picklist value.

Multi-Select Picklist Flow - Assignment Add And SemiColon

Now add a Record Update step to update the picklist value with the variable’s value.

Multi-Select Picklist Flow - Update Field From Variable

 

Now we’re ready to run through the entire process!  Check out the Opportunity below.  The Multi-Select picklist field “Topics” currently has the values: Purchasing & Accessories.

Multi-Select Picklist Flow - Opp With Two Values Selected

Our Screen Element properly displays those as the two chosen fields, and gives use two available choices: Warranty & Service.

Multi-Select Picklist Flow - Add Two Values

I’ve gone ahead and added both of them and click “Next” in the flow.

Multi-Select Picklist Flow - Add Two Values

The Record Update takes place, and if I refresh the Opportunity, I now see the additional values were added to the two already existing values.  It didn’t wipe out what was already there…perfect!

Multi-Select Picklist Flow - Refresh Opp Values Added

Now, in my Flow I reach the boolean checkbox.  I check and click “Next.”

Multi-Select Picklist Flow - Use Boolean for Repair

Now when I refresh my Opportunity and click into the Topics field I see the following in the box and just below it.  All 5 values have been selected within the field and only “Replacement” has not been selected.

Multi-Select Picklist Flow - Opp Adds Repair

 

There you have it, we were able to successfully update a multi-select picklist within a Flow using two different methods & fields (one requiring an assignment step).  We successfully updated the values without removing or replacing the existing values.  Way to go!

Check out the rest of the Go With The Salesforce Flow Series for more Visual Flow tips & tricks!