Home » 2019 » July

Monthly Archives: July 2019

Trigger to convert FSL Service Appointment time to the Service Territory timezone

Use case:

In Field Service Lighting, and upon scheduling Service Appointment to Resources, the system uses the local user’s Timezone to populate the Scheduled Start and Scheduled End date/time fields: 

But what if the Serivce Appointment location is in a Territory with different Timezone than the logged-in User’s Timezone? We need to find a way to capture the Scheduled Start Time in this Timezone as opposed to the default behavior that displays the Scheduled Start date time following the logged-in User’s timezone. Why? Well, what if we want to send a notification email to the customer informing them of the Scheduled Start time? It wouldn’t make perfect sense if it was not based on their local Timezone! 

As as example, if the Salesforce Dispatcher is in Toronto (Eastern Time), and the Service Appointment belongs to a Service Territory that follows the Pacific Time, then we need to see the Scheduled start date time in the Pacific, and not only in Eastern Time, and then we will use this local Scheduled Start in the email Template sent to the customer upon Dispatching for example. 

The data model is as follows:

  • Work Order is first created. It has a lookup to the Service Territory
  • The Service Territory has a lookup to the Operating Hour
  • The Operating Hour has a field called TimeZone that specifies the Timezone of this Territory 
  • Service Appointment can be created directly with the Work Order or after the Work Order. 
  • Service Appointment has a lookup to the parent Work Order 
  • Service Appointment has a lookup to the Service Territory, which has a lookup to the Operating Hour record, which has the TimeZone 

 

 

Upon creating the Service Appointment, the Scheduled Start date time is saved as the current Salesforce User Timezone! There is no way to get the Scheduled Start time on the Territory timezone!

 

A little bit of background:

There exists a method that deals with time conversion based on Timezones! Passing a day time field to a Format method returns the date time in the format specified, and following the timezone specified. For example: 

System.debug(System.now().format('YYYY-MM-dd HH:mm:ss', 'America/Los_Angeles'));

 

Returns the current time of the America/Los_Angeles timezone. 

17:35:08:002 USER_DEBUG [1]|DEBUG|2019-06-13 14:35:08

 

You can find all the available Timezone names here: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones

 

Solution:

To sort this, we will use a trigger that will use the format method to give the date time text value.

  • On the Service Appointment object, let’s create a custom field called Local_Scheduled_Start__c,
  • Let’s then create an After Insert and After Update Service Appointment Trigger 
  • Finally, we will create the Trigger Handler class that contains the method called by the Trigger

 

The Trigger is used to simply call a method on the Handler class:

trigger ServiceAppointmentTrigger on ServiceAppointment (after insert, after update) { 
    If (trigger.isAfter && trigger.isUpdate) {
        ServiceAppointmentTriggerHandler.handleAfterInsertUpdate(trigger.new);
    }
    
    If (trigger.isAfter && trigger.isInsert) {
        ServiceAppointmentTriggerHandler.handleAfterInsertUpdate(trigger.new);
    }
}

 

And now the Apex Class Handler that contains the method called by the Trigger:

public class ServiceAppointmentTriggerHandler {
    
    //Boolean to control recursion as this is after update
    public static Boolean boolStopRun = false;
    
    public static void handleAfterInsertUpdate(List<ServiceAppointment> triggerNew) {
        //Stop the run if this code was already run
        if (boolStopRun) return;
        
        //Make boolStopRun true in order to stop the run and prevent recursion - should be here not at the end 
        boolStopRun = true;
        
        //1- get all SA Ids
        Set<Id> setSAId = new Set<Id>();
        for (ServiceAppointment sa : triggerNew) {
            if (sa.SchedStartTime != Null) {
                setSAId.add(sa.Id);
            }
        }
        
        //2- Get List of SAs including the ServiceTerritory OperatingHours TimeZone
        List<ServiceAppointment> lstSA = [SELECT Id, AppointmentNumber, SchedStartTime, ServiceTerritory.OperatingHours.TimeZone
                                          FROM ServiceAppointment 
                                          WHERE Id IN : setSAId];
                                                 
        //3- create thge list with Local Scheduled Start and update it at the end 
        List<ServiceAppointment> lstSaToUpdate = new List<ServiceAppointment>();
        for (ServiceAppointment sa : lstSA) {
            //get the Timezone, returned value example "America/Los_Angeles"
            String strSTTZ = sa.ServiceTerritory.OperatingHours.TimeZone;
            //Passing a day time field to a Format method returns the date time in the format specified, and following the timezone specified
            String strLocalSchedStartTime = sa.SchedStartTime.format('MM/dd/YYY HH:mm', strSTTZ);
            ServiceAppointment saUpdated = new ServiceAppointment();
            saUpdated.Id = sa.Id;
            saUpdated.Local_Scheduled_Start__c = strLocalSchedStartTime;
            lstSaToUpdate.add(saUpdated);
        }
        update lstSaToUpdate;       
    }
}

And here’s the result: