Posts

From: Saikiran Gogurla: Spring 21 Feature: Preset the Territory’s Timezone on the timeslot screen

The default time zone for all times shown in a Salesforce Scheduler flow would be the timezone set for the user scheduling the appointment. See more details on how this would work for the various flows here.

In Spring 21 feature, a feature provided an ability to (pre)set the timezone across multiple screens on the Outbound, Inbound & Guest User flows (Create & modify) across mobile & desktop.

One common scenario this helps you to cover is, when a Customer care agent is booking an “On-behalf” appointment and wants to see the time-slots in the timezone of their selected territory.

Now, this is easy to achieve with Clicks, not code!


Imagine a customer care agent based in Chicago (Central time) is booking an appointment for a customer who wants to meet a banker in the Denver branch (Mountain time) of Cumulus bank.

Wouldn’t the customer care agent wants to see the time slots in Mountain time automatically and book the appointment?


With Salesforce Scheduler, now the agent can easily look at the available time slots in the bank operating hours timezone by default and book the appointments. Don’t worry about the Timezone in which the territory is operating and all; it is taken care of!!
All the timings shown in my flow are configured to Cumulus Bank (Denver) operating timezone.

Question: What if some other bank is at a different location or any Service Territory for that matter? Will my flow behave like it is configured to that Service Territory dynamically?

Answer: Oh Yes!!

Let’s look at how it is done,

Configuration is the same for Outbound, Inbound, and Guest flows, illustrating for Outbound Flow.

  1. Add a new Assignment Element called Set Territory Timezone, after the Location Screen.
image
  1. Set the DefaultTimeZone variable to Timezone from ServiceTerritory Operating Hours.
Config3.png
  1. Save the flow.

— Now the flow is configured to the timezone based on the Service Territory chosen —

Screens that have an impact :

  1. Candidate Screen now shows Next Availability in the Service Territory Timezone.
Candidate Screen.png
Debug Item.png
  1. Flow Time Slot Screen, now by default shows Time Slots in Territories Timezone.
Time Slot Screen.png
  1. Flow Review Screen shows the selected time slot in the timezone in which Territory Operates.
Review Screen2.png
  1. For Inbound and Guest Flow., Next Availability and Time Slots are configured to Territory Timezone on Resource Time Slot Screen, and it looks like,
Screenshot 2021-06-09 at 5.11.57 PM.png

These are some of the use-cases that can be solved but not limited to,

  • Agents booking on-behalf appointments, want to see all time-slots in territory timezone by default.
  • Branch Managers and above, no matter from where customer books an appointment, he/she visits our site and tries to book appointment and all time-sots should be shown in my Territory supported Timezone by default.
  • Guest appointment booking, are you sending out an email to customer to book an appointment using the link and wants him to see time-slots in your territory timezone?

Your customer will never miss his/her appointment time, when booked with Territory timezone!

From Shantinath Patil: Email signature booking

Ever wondered if you can offer a link to your customers to book an appointment with you? Or a situation where you want everything to be preselected, and you will immediately jump to the timeslot screen? Well, wait no more! In this blog, we will show you how you can achieve this!

Let’s consider the first scenario. You give a link to a customer OR add a link to your email signature. Here is what you have to do.

Part 1: Prepare!

We need to build a flow such that our landing page should be the timeslot screen. To make that work, we should be able to populate all the required values. In current out of the box flows, all the screens we traverse are used to populate values required for the timeslot screen. Let’s see what all values we need to populate.

  • Work Type Group: This is the outcome from the OOB Work Type Group Selection page. This helps the internal logic to get which topic/template we are trying to book.
  • Service Territory: This is the location of your appointment.
  • Service Resource: This is the resource that will cater to the appointment request.

Since these values are needed as inputs for our flow, we can create flow variables and mark them available for input.

image.png

Once all these flow variables are created, we can create the assignment for those. Note that we need to assign Service Territory Id to a Service Appointment instance. This makes sense if you look at location screen output. That screen is giving selected territory output as “{!ServiceAppointment.ServiceTerritoryId}”. And since we cannot just set one field on the Service Appointment instance, we will have to initialise all other fields as an empty string. For this, we can refer to the OOB assignment stage.

image.png

The next step is to link all required values to the timeslot screen. Again, we can refer to the OOB timeslot screen configuration. We should be doing the only change to pass our input flow variables for Service Resource and Work Type Group.

image.png

After that, configure the review screen. Make sure you map all the required attributes.

image.png

And finally, we should configure the save action. Here you can pass “{!serviceAppointmentFields}” as input, which is an output of the Review Screen. For now, we are considering invoking this flow for guest flow, so we are adding Lead as input as well.

image.png

Optionally you can configure the confirmation screen. Done! Your flow should be ready to be tested. Save and debug the flow by providing the input attributes!

image.png

Part 2: Distribute!

In a nutshell, we created a flow that takes all the parameters needed for the TimeSlot screen and process it. Once such a flow is ready, we can then use any mechanism of distribution of the flow.

Here is the link to Salesforce documentation which explains to you how can we create a flow page in the community and pass values: https://help.salesforce.com/articleView?id=sf.flow_distribute_internal_url_variable.htm&type=5

This is not the only way! You can even embed the flow in a lightning component and embed that to an external website. More detail: https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/components_using_flow_inputs_set.htm

If you take the approach of exposing this flow in a public community, consider adding a ReCaptcha. More details: https://unofficialsf.com/protect-a-flow-on-a-public-community-with-the-google-recaptcha-component/

The below demo shows all things in action. You can see that a service resource has sent details to a prospect related to Salesforce Scheduler. That email includes a link. Once the user clicks on the link, they will get redirected to our public community page, which hosts the flow we created from the above steps. All the user have to do it to select a slot, add their details and submit!

Part 3: One More Thing!

Now that we have created a flow that takes input, we surely don’t expect our Service Resources to generate the link manually! Besides, it’s easier to use existing components to build a flow that will give output as an URL to proudly put in their email signature or give to customers! Heck, they can even include that in their digital business cards!

To do this, we will reuse existing OOB components. It’s like the Service Resource will perform initial actions on behalf in OOB flow on behalf of the end-user. Following are the steps to create such a flow:

  1. We will configure the “Generate Signature URL” button on the Service Resource page. From there, one can initiate the signature generation flow. Hence we need “recordId” as an input flow variable.
  2. We will pass this record id to the existing WorkTypeGroup selection page. This will ensure that only the WTGs which are related to that resource are displayed.
  3. Next, configure the Service Territory screen. Here we need Service Resource Id and WTG Id, which will be the output of the previous screen. Having both the inputs will ensure that search results for the territory will give us the correct territory.
  4. Finally, we will generate the URL from all the selection with the help of a formula flow variable. The formula looks like this:
image.png

Note that we have considered the scenario that actual booking flow is configured in a community, hence the “communityURL” variable.

Once you build out the flow, it looks like this:

image.png

Test this flow in the flow debug, and once done, activate it. You can configure a new quick action on Service Resource and add that to its layout.

Both the flows are added to this git repo:https://github.com/snathpatil/signatureflow. You can deploy it to your salesforce org and validate!

From Shantinath Patil: Pre-filling values on the Appointment Review Screen

So you want to preload some values on the final Review screen provided by Scheduler?

Check this out…


Let’s say you want to prefill the email & phone details.

  1. Set the Email and Phone in the first assignment stage:
image (22).png

2. Assign Service Appointment record variable in the review screen. In an Outbound flow template that input is blank and that’s why making changes only mentioned in step 1 will not work.

image (23).png

That’s it!

image (24).png

Note this method works for the Outbound flow & Inbound flow and is restricted to the following fields:

  • “AdditionalInformation”
  • “AppointmentType”
  • “Comments”
  • “ParentRecordId”
  • “ServiceTerritoryId”
  • “Street”
  • “City”
  • “State”
  • “Country”
  • “PostalCode”
  • “SchedStartTime”
  • “SchedEndTime”
  • “WorkTypeId”
  • “Id”
  • “Description”
  • “Subject”
  • “Phone”
  • “Email”
  • “ContactId”
  • “IsAnonymousBooking”

To prefill other fields or for other flows (eg Guest Flow), you need to do it build your own screen & logic: https://unofficialsf.com/build-your-own-appointment-review-screen/

Also check out other cool changes that you could with the review screen here: Review Appointment screen: Quick Flow Customisations – Salesforce Scheduler

From Chris Albanese: Summer 21 Feature: Get Resources and Available Time Slots Through New Apex Methods

Salesforce Scheduler introduced a new feature in the Summer ’21 Release which allows developers to easily make custom time slot screen flow components that interact with external systems. The new Apex methods call the Get Appointment Candidates and Get Appointment Slots APIs. This capability helps you easily get all the service resources and available time slots or get available slots for a resource.

With this new feature developers have the choice of using the Scheduler REST APIs and the Scheduler Apex methods. The REST APIs are a powerful tool that allows you to develop custom user interfaces and applications that run on your own websites. The Apex methods offer a great option for developers building custom UI components and processes that are run by users that are already logged into Salesforce, such as running a Lightning Flow launched from a button on the Account Screen or viewing a page inside of a Salesforce Experience site.

Check this out below.

Calling the Scheduler Get Appointment Candidates API with Apex

Here’s a little concept illustrator to show you how easy it is to call the API using Apex.

Let’s say you want to ask Scheduler to return the next time slot available for a Work Type Group and Territory. The user is a call center user, talking to a customer and the customer says “give me your next available appointment please”.

Solution Architecture

  • A simple Flow embedded on the account page
  • The flow calls an @invocableMethod which uses the new API
  • The method returns the 1st time slot found
  • The flow creates a service appointment using the time slot returned

User Experience

The top left of the 2 screen shots below illustrate how the call center user can ask the scheduler to return the next available time slots and create a service appointment.

image.png
image.png

Screen Flow

As you can see in the screen shot below, the flow is pretty simple. It takes in the account id from the Account Page. It prompts the user for Work Type Group, Territory and Earliest Start Time. It passes these parameters into the Apex Action. If a slot is returned, it creates a service appointment, if not, an informational message is displayed.

image.png

Apex Class

As you probably already know, you can call Apex Methods from a Lightning Flow using the Action component. There are lots of great articles and tips on building @invocableMethods, which I won’t go into here. For this example, the method I created takes in the parameters, calls the Scheduler Apex Method named lxscheduler.SchedulerResources.getAppointmentCandidates, and returns the first result found, or returns nothing if no slots are found.

Receiving the Input Parameters from the Flow

In the example code below, to accept the input parameters from the flow, I created a class called payloadIn. It contains variables for Work Type Group Id, Service Territory Id, Scheduling Policy Id, Account Id and Start Date Time. We use these values to call the Scheduler Method. Variables like Account Id are not mandatory, I’ve just included it in my example.

Setting the Input Parameters in the Apex Method

The Get Appointment Candidates API provides an easy to use class to set the parameters that you want to pass to the Get Appointment Candidates API, lxscheduler.GetAppointmentCandidatesInput. As you can see in the example code, I simply set the parameters to the values passed in from the flow. I’ve hard coded the EndTime to be the Start Time plus 3 days. You can of course change this to be more dynamic.

Calling the Apex Method

It is super simple and is represented by this line in the example code:

String response = lxscheduler.SchedulerResources.getAppointmentCandidates(input);

Note, that no authentication code, connected apps, named credentials and other items are required to call the Apex Method. Since the running user is already authenticated, they can call the Scheduler API, just like they can call any other Apex Method they have access to.

Hooray! This is what is so awesome about this new API and makes code based development for Scheduler so much easier for these types of use cases.

Parsing the Results – they’re different from the REST API results

The method returns the results as a JSON string. I won’t go into the ins and outs of JSON and you can certainly find out more with a quick web search, but what you need to know is that the results from the lxscheduler.SchedulerResources.getAppointmentCandidates call are slightly different from the REST API call.

The REST API call for getAppointmentCandidates returns this format:

{
"candidates" : [ {
"endTime" : "2019-01-23T19:15:00.000+0000",
"resources" : [ "0HnB0000000D2DsKAK" ],
"startTime" : "2019-01-23T16:15:00.000+0000",
"territoryId" : "0HhB0000000TO9WKAW"
}, {
"endTime" : "2019-01-23T19:30:00.000+0000",
"resources" : [ "0HnB0000000D2DsKAK" ],
"startTime" : "2019-01-23T16:30:00.000+0000",
"territoryId" : "0HhB0000000TO9WKAW"
}, {
"endTime" : "2019-01-23T19:45:00.000+0000",
"resources" : [ "0HnB0000000D2DsKAK" ],
"startTime" : "2019-01-23T16:45:00.000+0000",
"territoryId" : "0HhB0000000TO9WKAW"
}]
}

The Apex Method for getAppointmentCandidates returns this format:

{{
"endTime" : "2019-01-23T19:15:00.000+0000",
"resources" : [ "0HnB0000000D2DsKAK" ],
"startTime" : "2019-01-23T16:15:00.000+0000",
"territoryId" : "0HhB0000000TO9WKAW"
}, {
"endTime" : "2019-01-23T19:30:00.000+0000",
"resources" : [ "0HnB0000000D2DsKAK" ],
"startTime" : "2019-01-23T16:30:00.000+0000",
"territoryId" : "0HhB0000000TO9WKAW"
}, {
"endTime" : "2019-01-23T19:45:00.000+0000",
"resources" : [ "0HnB0000000D2DsKAK" ],
"startTime" : "2019-01-23T16:45:00.000+0000",
"territoryId" : "0HhB0000000TO9WKAW"
}
}

Did you notice the difference? The outer wrapper for “candidates: [ …]” is missing from the results, so you need to make sure you deserialize the results using the proper format.

Here’s the class I used to deserialize the results:

public class schedulerCandidates {

public datetime startTime;
public datetime endTime;
public List<String> resources;
public String territoryId;


public static list<schedulerCandidates> parse(String json) {
return (list<schedulerCandidates>) System.JSON.deserialize(json, list<schedulerCandidates>.class);
}
}

Example Code

public with sharing class LXScheduleFirstAvailable {

@invocableMethod(label='Schedule to First Available' description='Schedule to whomever is next')
public static list<event> ScheduleIt(list<payloadIn> payloadList ){
if(payloadList == null || payloadList.size() <> 1) return null;
payloadIn pl = payloadList[0];
lxscheduler.GetAppointmentCandidatesInput input = new lxscheduler.GetAppointmentCandidatesInputBuilder()
.setWorkTypeGroupId(pl.workTypeGroupId)
.setTerritoryIds(new List<String>{pl.serviceTerritoryId})
.setStartTime(pl.startDateTime.format('yyyy-MM-dd\'T\'HH:mm:ssZ'))
.setEndTime(pl.startDateTime.addDays(3).format('yyyy-MM-dd\'T\'HH:mm:ssZ'))
.setAccountId(pl.accountId)
.setSchedulingPolicyId(pl.schedulingPolicyId)
.setApiVersion(Double.valueOf('50.0'))
.build();
//call the Apex Method - no REST API authentication or API user needed!!!
String response = lxscheduler.SchedulerResources.getAppointmentCandidates(input);
//parse the results using JSON.deserialize
if(response==null) return null;
list<schedulerCandidates> allslots = schedulerCandidates.parse(response);
//slots found, return just the first one
if(allslots!=null) {
event thisevent = new event();
thisevent.startdatetime = allslots[0].startTime;
thisevent.enddatetime = allslots[0].endTime;
thisevent.description = allslots[0].territoryId;
thisevent.whoid = allslots[0].resources[0];
return new list<event>{thisevent};
}
//no slots found, return null
return null;
}
public class payloadIn{
@invocableVariable(required=true)
public string workTypeGroupId;
@invocableVariable(required=true)
public string serviceTerritoryId;
@invocableVariable(required=true)
public string schedulingPolicyId;
@invocableVariable(required=true)
public string accountId;
@invocableVariable(required=true)
public datetime startDateTime;
}

}

Example Code Test Method

@isTest
private class LXSchedulerFirstAvailableTest {
static testMethod void getAppCandidatesTest() {
String expectedResponse = '[' +
' {' +
' \"startTime\": \"2021-03-18T16:00:00.000+0000\",' +
' \"endTime\": \"2021-03-18T17:00:00.000+0000\",' +
' \"resources\": [' +
' \"0HnRM0000000Fxv0AE\"' +
' ],' +
' \"territoryId\": \"0HhRM0000000G8W0AU\"' +
' },' +
' {' +
' \"startTime\": \"2021-03-18T19:00:00.000+0000\",' +
' \"endTime\": \"2021-03-18T20:00:00.000+0000\",' +
' \"resources\": [' +
' \"0HnRM0000000Fxv0AE\"' +
' ],' +
' \"territoryId\": \"0HhRM0000000G8W0AU\"' +
' }' +
']';
lxscheduler.SchedulerResources.setAppointmentCandidatesMock(expectedResponse);
Test.startTest();
LXScheduleFirstAvailable.payloadIn pl = new LXScheduleFirstAvailable.payloadIn();
pl.workTypeGroupId = '0VSB0000000LB9TOAW';
pl.serviceTerritoryId ='0HhB0000000TrsdKAC';
pl.startDateTime = datetime.now();
pl.accountId = '001B000001KYUM3IAP';
pl.schedulingPolicyId = '0VrB0000000Kz6Z';
list<LXScheduleFirstAvailable.payloadIn> plList = new list<LXScheduleFirstAvailable.payloadIn>();
plList.add(pl);
List<event> candidateList = LXScheduleFirstAvailable.ScheduleIt(plList);
System.assertEquals(1, candidateList.size(), 'Should return only 1 record!');
Test.stopTest();
}
}

Summary

This new Apex API is awesome and simplifies calling the Scheduler API for use cases where the user is already logged into Salesforce.

Additional Notes about this Example

In my example flow, I’m using 2 create record steps to create the Service Appointment and Assigned Resource. In production, you probably want to use the Scheduler flow component called Save Appointment. I’ve described how to use that component here. This component gives you added capabilities to ensure that the time slot selected is still available at the time of record save.

From Chris Albanese: Your Branding on the Salesforce Scheduler Flows

Salesforce Scheduler flows are just flows. You can supplement the screens with any information you want with simple configuration.

image.png

Adding a display field in the same Screen element where the Select Work Type Groups is. The example includes merge fields and an image field.

From Shantinath Patil: Location Screen Tips and Tricks

Salesforce scheduler provides you a really nice component to search for a location. You can enter any address or zip code in the provided input box and get the desired territory (if it’s correctly configured! duh! ).

There are certain tricks involved in this out-of-the-box component you can play around with and make it work according to your need.

Distance measure (kms or miles):

This is a straightforward setting where you can define the search radius to be in miles or kilometers. By default, it is in miles. However, if you want to change it to kilometers, you need to change a flow variable called “distanceUnit”. This variable is of type text and valid values for this are mi or km. If you put any other value, this component will simply reject it and will show default miles. So make sure you have set it only to one of those accepted values.

Screenshot from 2021-05-01 22-45-32.png

Setting the default distance value

There is another attribute to set the default search radius as well. It’s a flow variable called “locationDistance”. Supported values for this variable are [ 5, 10, 25, 50, 100 ] if the distance unit is miles and [ 10, 20, 50, 100, 200 ] if the distance is kilometers. By default is 5 miles OR 10 kilometers depending on your distance unit setting. If you set any value other than the above specified, it will set back to its default value. So make sure you set it correctly.

Screenshot from 2021-05-01 23-43-18.png

Skip Location Screen:

Often customers want to create virtual territories or want to skip the territory selection and instead preselect a service territory in the Scheduler flows. It’s pretty simple to customize the flow (using clicks not code) to make this happen!

Here is one of the way we can do that:

  1. Copy the Service Territory Id of the Service Territory which you want to auto select in the flow. (xYou can do this by going to your Service Territory record and selecting the Id from the URL as shown below)
image.png
  1. Open your flow. Open Initial Assignment node.
Screenshot from 2021-05-04 16-33-04.png
  1. In the Edit Assignment screen, look for ServiceAppointment > Territory ID field. Put the Id of territory you have copied in earlier stages.
image.png
  1. Since now we have already chosen a territory, we do not need to show the location screen to the user. We can simply remove it from the flow. Just join the lines from the Appointment Type Screen node to the Resource Decision box (considering this is an outbound flow).
Screenshot from 2021-05-04 16-39-28.png
  1. Hit that Save and activate button and voila! You won’t see that location screen anymore!

Just to keep in mind:

  1. Salesforce scheduler is a Precision Scheduling Engine which checks calendars in real-time so say you have 1000 resources with the same skills, the engine has to parse through calendars (both internal & external) of resources to come up with time slots. So definitively test the performance of this setup.
  2. Since you will be setting only the service territory id, you will not see Address populated on Service Appointment. So if you need that, make sure you populate those values in the Assignment screen itself ( step number 3 above).

Auto-populate Location:

There are 2 design attributes on the location screen among others. Those are called Latitude and Longitude. These attributes take values set in the flow variables “locationLatitude” and “locationLongitude” respectively. These are geolocation attributes that get set when you select a location on that screen. Since these attributes are available for input in flow builder, we can set those values beforehand so that the location component will populate with a territory as soon as it loads.

image.png

Let us take an example of a territory location: Market Street Branch in San Francisco. Geolocation for this would be 37.793872°, -122.394865°. You can get this value if you google for geo-location a certain address or simply query one of the territories with that address [SELECT Latitude, Longitude FROM ServiceTerritory]. We can set these values in the design attributes which take lat lang as input.

Screenshot from 2021-05-01 23-03-41.png
Screenshot from 2021-05-01 23-04-05.png

Just save this and you will get all the locations selected from the default radius of the search.

Now, to make it more interesting, let us try auto-populating lat lang info from the user’s browser. We can make use of simple HTML Geolocation API. Since location is related to users’ privacy, this API will return the location once the user approves it.

Since the location screen is an OOB component, we will have to create a new component that will run this HTML Geolocation API. Also, this component will have to execute before the location screen is loaded so that as soon as the location screen loads, we will get location coordinates.

Here is a sample component which will give you output as gelocation is design attributes:

<aura:component implements="lightning:availableForFlowScreens" access="global" >

<aura:attribute name="latitude" type="String" description="latitude of the browser location" />
<aura:attribute name="longitude" type="String" description="longitude of the browser location" />

<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
</aura:component>

its controller:

({
doInit : function(component, event, helper) {
navigator.geolocation.getCurrentPosition(function(position){
var geolocation = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
component.set("v.latitude", geolocation.lat);
component.set("v.longitude", geolocation.lng);
})
}
})

and design file:

<design:component>
<design:attribute name="latitude" label="Latitude" />
<design:attribute name="longitude" label="Longitude" />
</design:component>

Once the component is created, we can add that to any screen before the location screen. Since we are using OOB flow, let’s configure it at the Appointment Type Screen.

image.png

That’s it! Activate and run the flow. Once you land on the Select Appointment Type screen, our geolocation component will ask users’ consent to share the location. If the user approves the consent, then you will see service territory getting searched according to the users’ location. If there are no territories available in that geolocation, the user will get a standard message as: No results for that Work Type Group found in that service territory. Try a different address, or expand your search area. Users can then search specific locations from the search bar available on the screen.

All the above code is bundled at https://github.com/snathpatil/smartlocation. It contains the above component and a flow which makes use of it.

From Chris Albanese: Build your own appointment review screen

Do check out the existing possibilities to make changes to the appointment review screen before trying this out.

When you look at a Salesforce Scheduler flow, there’s a core action called saveAppointment. Did you ever wonder what it does, what the inputs are and whether you can tailor it?

2021-02-11_08-48-35.png
2021-02-11_08-47-44.png

What does it do?

It takes the values gathered during the flow interview and creates or updates a ServiceAppointment record and an associated AssignedResource record. If you provide optional attendees, it will also create AssignedResource records for each optional service resource id provided.

The help and training article is locate here, but let’s look at what’s inside the inputs provided this action.

What is contained in the input fields?

The easiest way to see what is passed into the Save Action is to run the flow in debug mode. There you will see the values that are stored into each field passed into the Save Action.

2021-02-11_13-45-58.png
  • Look in the right hand panel of the debugger. You can see the value of each field.
  • On a given screen, you will have to press next to see what the values are for that screen.
  • You can scroll up and down in the debug details panel to review the values and every step.

After you press finish, scroll up and check out what is in the Save Appointment step.

2021-02-11_13-48-07.png

What do we see here?
Inputs:

  • optionalAttendees = a list of comma separate values of Service Resource Ids.
  • serviceAppointmentFields = a JSON string of the Service Appointment fields, including fields such as:
    • start and end times
    • service territory id
    • Service Resource ID for the service resource the appointment is for
    • ParentRecordId = the Account Id (or the opportunity or lead id)
    • other fields on the page layout – there’s a custom field I added to the Service Appointment object and page layout called My_Custom_Field
  • selectedTimezone – the timezone selected by or defaulted for the user

Output:

  • the Service Appointment Id

How do these values get set?

In a Salesforce Scheduler flow, the Screen steps each have components on them that save values to variables in the flow. For example, the Select Location screen has a screen component that saves the Service Territory Id and Address fields to the corresponding fields in the ServiceAppointment variable, as you can see in the screen shot below.

2021-02-11_14-34-04.png

But how does the serviceAppointmentFields variable get set?

This field is the bulk of what is passed into the Save Action.

It gets set by the Review Screen component that is located on the Review Screen.

2021-02-11_14-41-42.png
  • This component is located in the Review Screen.
  • It gives the user the ability to review the appointment prior to saving. For example, they can enter a value into the Subject field or other fields, including custom ones.
  • But this component is really a black box.

What if I wanted to set the values for the serviceAppointmentFields variable myself?

So as I have written above, the Review Screen component is really a black box. It takes the fields from the review screen along with other fields in the ServiceAppointment object variable and the WorkTypeGroupId variable and creates a JSON string from them.

What is JSON you ask? You’ve probably heard of it and you can definitely Google it and find out more, but it’s essentially a collection of name:value pairs, and you see that in the debugger output above. It’s very powerful and allows you to store lots of rich information, including arrays, in a text field.

You can create your own JSON string using a formula field. You can pass that formula field into the Save Appointment action. Or you can assign it to the serviceAppointmentFields variable in an assignment step.

Here’s an example of a formula field that I have used to pass into the Save Appointment action:

'{"Description":"' &{!ServiceAppointment.Description}& 
'",'& '"SchedStartTime":"' & substitute(substitute(text({!ServiceAppointment.SchedStartTime}),' ','T'),'Z','.000Z')&
'",'& '"SchedEndTime":"' & substitute(substitute(text({!ServiceAppointment.SchedEndTime}),' ','T'),'Z','.000Z')&
'",'& '"Subject":"' & {!ServiceAppointment.Subject}&
'",'& '"AdditionalInformation":"' & {!ServiceAppointment.AdditionalInformation}&
'",'& '"AppointmentType":"' & text({!ServiceAppointment.AppointmentType})&
'",'& '"Comments":"' & {!ServiceAppointment.Comments}&
'",'& '"ParentRecordId":"' & {!ServiceAppointment.ParentRecordId}&
'",'& '"Street":"' & {!ServiceAppointment.Street}&
'",'& '"City":"' & {!ServiceAppointment.City}&
'",'& '"State":"' & {!ServiceAppointment.State}&
'",'& '"PostalCode":"' & {!ServiceAppointment.PostalCode}&
'",'& '"Country":"' & {!ServiceAppointment.Country}&
'",'& '"WorkTypeGroupId":"' & {!WorkTypeGroupId}&
'",'& '"ServiceTerritoryId":"' & {!ServiceAppointment.ServiceTerritoryId}&
'",'& '"ServiceResourceId":"' & {!ServiceResourceId}&
'",'& '"Phone":"' & {!ServiceAppointment.Phone}&
'",'& '"Email":"' & {!ServiceAppointment.Email}&
'",'& '"IsAnonymousBooking":"' & if({!ServiceAppointment.IsAnonymousBooking},"True","False")&
'",'& '"isSlotChanged":"' & "False"&
'"}'

What do you see in the field above?

Lot’s of care taken to make sure each name:value pair is enclosed in double quotes and that each pair has a colon between it and is separated by a comma from the next pair. And the whole string is wrapped in curly braces {}. Note the added complexity of the SchedStartTime and SchedEndTime fields, to convert them from datetime data types to a text type in a “”yyyy-MM-dd’T’HH:mm:ss.SSSZ”” format. I’ve also had to use a text() function to convert the picklist field called AppointmentType to a string value.

Can I replace the out of the box review screen with my own review screen?

Yes you can! You can display whatever content you like using standard flow components such as Display Text and you and capture user input using standard flow components such as Input Text or Date.

You just need to make sure you create the JSON string to pass into the Save Appointment action.

Minimum values required: make sure the at least the following fields are passed in order to get the desired results :

{"ParentRecordId": "001xx0000000000000",
"ServiceTerritoryId": "0Hhxx0000000000000",
"ServiceResourceId": "0Hnxx0000000000000",
"WorkTypeGroupId": "0VSxx0000000000000", (for new)
"WorkTypeId":"08qxx0000000000000" (for modify)
"SchedStartTime": "2021-02-21T20:30:00.000Z",
"SchedEndTime": "2021-02-21T21:00:00.000Z",
"IsAnonymousBooking": <VALUE SET IN FLOW VARIABLE>,
"isSlotChanged": <false for new, true for modify>,
"schedulingPolicyName": "<same policy name set in flow>"
}

Details of the Save Appointment inputs

  • Lead – use this for guest inbound flows (unauthenticated) only. This is a record (single) variable containing the new Lead record created for the guest user running the flow.
  • Optional Service Resource Ids – comma separated list of Service Resource Ids. This represents the optional resources for the service appointment, if present.
  • Selected Timezone – the timezone selected by or defaulted for the user – see this article for the list and use the value for the Timezone (the part of the TIME ZONE NAME field in parenthesis, such as America/Los_Angeles).
  • Service Appointment Fields – a single text field containing the JSON for the Service Appointment – extensively described above. For guest inbound flows, do not provide a value for parentRecordId, as this will be provided via the Lead object described above.
  • Service Resources – if using Multi-Resource scheduling – this is a single text field containing the JSON for the list of Service Resources and Territory Members selected, including the primary resource. Example below (record ids are obfuscated in this example).
  • [{“UserPhoto”:”https://xxxx–c.documentforce.com/profilephoto/729B0000XXXXXXXX/F”,”ServiceTerritoryMemberId”:”0HuB0000000XXXXXXXX”,”IsActive”:true,”ResourceType”:”T”,”RelatedRecordId”:”005B000000XXXXXXXX”,”Id”:”0HnB0000000XXXXXXXX”,”Name”:”Connie Ruiz”,”sobjectType”:”ServiceResource”,”AttendanceType”:”Primary”},
    {“UserPhoto”:”https://xxxx–c.documentforce.com/profilephoto/729B0000XXXXXXXX/F&#8221;,”ServiceTerritoryMemberId”:”0HuB0000000XXXXXXXX”,”IsActive”:true,”ResourceType”:”T”,”RelatedRecordId”:”005B000000XXXXXXXX”,”Id”:”0HnB0000000XXXXXXXX”,”Name”:”Anita Gonzalez”,”sobjectType”:”ServiceResource”,”AttendanceType”:”Required”},
    {“UserPhoto”:”https://xxxx–c.documentforce.com/profilephoto/729B0000XXXXXXXX/F&#8221;,”ServiceTerritoryMemberId”:”0HuB0000000XXXXXXXX”,”IsActive”:true,”ResourceType”:”T”,”RelatedRecordId”:”005B000000XXXXXXXX”,”Id”:”0HnB0000000XXXXXXXX”,”Name”:”Mark Mayo”,”sobjectType”:”ServiceResource”,”AttendanceType”:”Required”}]
  • WorkType – if using a Modify flow, this is a record (single) variable containing the Work Type for the Service Appointment being rescheduled. It is not used for new appointments.