function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
BobPBobP 

Salesforce Field Service Booking Appointments Duration 30 Days

I am trying to update functionality in my org to have customers self-schedule beyond 30 day limit. We hada customer lightning component and apex class built and I am trying to see if I can update the code to search from appointments beyond the 30 day limit. Does anyone know how I could update this code below to successfully search from appointment beyond 30 days? or is there any code out there that can override the saleforce default of 30 days?
public without sharing class advic_fsl_AppointmentBooking {

    @AuraEnabled
    public static CmpData createWorkOrderAndServiceAppointment() {
        CmpData retData = new CmpData();
        User runningUser = getRunningUser();
        system.debug('Who is the running user: '+runningUser.AccountId);
        retData.isEligibleCustomer = checkCustomerEligibility(runningUser.AccountId);
        if(retData.isEligibleCustomer) {
            retData.workOrderId = createWorkOrder(runningUser);
            retData.serviceAppointmentId = createServiceAppointment(runningUser, retData.workOrderId);
        }
        
        return retData;
    }
    
    @AuraEnabled
    public static CmpData getAppointmentSlots(Id workOrderId, Id serviceAppointmentId) {
        System.debug('@@@@ HERE');
        CmpData retData = new CmpData();
        retData.slots = getSlots(serviceAppointmentId);
        // We delete the created Work Order and Service Appointment in case the user quits the process before scheduling his/her appointment.
        // Once the user confirms their scheduled appointment, we will undelete the Work Order and Service Appointment.
        deleteWorkOrderAndServiceAppointment(workOrderId, serviceAppointmentId);
        System.debug('@@@@ retData: ' + retData);
        return retData;
    }
    
    @AuraEnabled
    public static void undeleteWorkOrderAndServiceAppointment(Id workOrderId, Id serviceAppointmentId) {
        List<WorkOrder> deletedWorkOrders = [SELECT Id FROM WorkOrder WHERE Id =: workOrderId ALL ROWS];
        List<ServiceAppointment> deletedServiceAppointments = [SELECT Id FROM ServiceAppointment WHERE Id =: serviceAppointmentId ALL ROWS];
        try {
            undelete deletedWorkOrders;
            undelete deletedServiceAppointments;
        } catch(DmlException e) {
            System.debug('Error in undeleteWorkOrderAndServiceAppointment: ' + e.getMessage());
        }
    }
    
    @AuraEnabled
    public static void updateServiceAppointment(String slotJSON, Id serviceAppointmentId, String detailsAccount, String detailsServiceAppt) {
        System.debug(slotJSON);
        System.debug(serviceAppointmentId);
        System.debug(detailsAccount);
        System.debug(detailsServiceAppt);
        AppointmentBookingSlot slot = (AppointmentBookingSlot)JSON.deserialize(slotJSON, advic_fsl_AppointmentBooking.AppointmentBookingSlot.class);
        try{
            ServiceAppointment serviceAppointment = [SELECT ArrivalWindowStartTime, ArrivalWindowEndTime, Status, Additional_Details__c FROM ServiceAppointment WHERE Id =: serviceAppointmentId];
            serviceAppointment.ArrivalWindowStartTime = utcToUserTimezone(slot.times.startDT);
            serviceAppointment.ArrivalWindowEndTime = utcToUserTimezone(slot.times.endDT);
            
            serviceAppointment.Status = 'Scheduled';
            ServiceAppointment.Additional_Details__c = detailsServiceAppt;
            
            User runningUser = getRunningUser();
            system.debug('Who is the running user: '+runningUser.AccountId);
            Account account = [SELECT Additional_Details__c FROM Account WHERE Id =: runningUser.AccountId];
            account.Additional_Details__c = detailsAccount;
            
            update serviceAppointment;
            update account;
        } catch(DmlException e) {
            System.debug('updateServiceAppointment: ' + serviceAppointmentId + ' Error: ' + + e.getMessage());
        }
        
    }
    
    @AuraEnabled
    public static void scheduleServiceAppointment(Id serviceAppointmentId) {
        Id schedulingPolicyId = [SELECT Id, Name FROM FSL__Scheduling_Policy__c WHERE Name = 'Customer First' LIMIT 1].Id;
        
        //Schedules the service appointment in the best available slot. If there are no available slots, the appointment isn’t scheduled.
        FSL.ScheduleResult result = new FSL.ScheduleResult();
        //Returns the result of the scheduling process.
        System.debug('svcApptId to Svc: '+ serviceAppointmentId);
        result = FSL.ScheduleService.schedule(schedulingPolicyId, serviceAppointmentId);
        try{
            System.debug('FSL.ScheduleService result: ' + JSON.serializePretty(result));
        }
        catch(Exception e){
            System.debug('JSON ERROR. On: '+ result);
        }
    }
    
    private static Boolean checkCustomerEligibility(Id accountId) {
        Account account = [SELECT Customer_Eligible__c FROM Account WHERE Id =: accountId];


    public static User getRunningUser() {
        Id runningUserId = UserInfo.getUserId();

        return [SELECT AccountId, ContactId, Account.BillingStreet, Account.BillingCity, Account.BillingState, Account.BillingCountry, Account.BillingPostalCode FROM User WHERE Id =: runningUserId];
    }

    public static Id createWorkOrder(User runningUser) {
        WorkType energyInHomeWt = [SELECT Id FROM WorkType WHERE Name = 'Energy Assessment - In Home' LIMIT 1];
        WorkOrder workOrder = new WorkOrder(AccountId=runningUser.AccountId, ContactId=runningUser.ContactId, Street=runningUser.Account.BillingStreet, WorkTypeId=energyInHomeWt.Id,
                City=runningUser.Account.BillingCity, State=runningUser.Account.BillingState, Country=runningUser.Account.BillingCountry, PostalCode=runningUser.Account.BillingPostalCode);
        try {
            insert workOrder;
        } catch(DmlException e) {
            System.debug('Error in createWorkOrder: ' + e.getMessage());
        }

        return workOrder.Id;
    }

    public static Id createServiceAppointment(User runningUser, Id workOrderId) {
        ServiceAppointment serviceAppointment = new ServiceAppointment(ContactId=runningUser.ContactId, ParentRecordId=workOrderId, EarliestStartTime=Datetime.now(), DueDate=Datetime.now().addMonths(1),
            Street=runningUser.Account.BillingStreet, City=runningUser.Account.BillingCity, State=runningUser.Account.BillingState, Country=runningUser.Account.BillingCountry,
            PostalCode=runningUser.Account.BillingPostalCode);
        try {
            insert serviceAppointment;
        } catch(DmlException e) {
            System.debug('Error in createServiceAppointment: ' + e.getMessage());
        }

        return serviceAppointment.Id;
    }

    private static Boolean checkIneligibleReasonCode(String ineligibleReasonCode) {
        Boolean codeStartsWithPp = false;
        if(String.isNotBlank(ineligibleReasonCode)) {
            if(ineligibleReasonCode.startsWith('PP')) {
                codeStartsWithPp = true;
            }
        }

        return codeStartsWithPp;
    }

    private static void deleteWorkOrderAndServiceAppointment(Id workOrderId, Id serviceAppointmentId) {
        try {
            delete [SELECT Id FROM WorkOrder WHERE Id =: workOrderId];
            delete [SELECT Id FROM ServiceAppointment WHERE Id =: serviceAppointmentId];
        } catch(DmlException e) {
            System.debug('Error in deleteWorkOrderAndServiceAppointment: ' + e.getMessage());
        }
    }

    /*
    * Appointment booking returns the available slots for a service appointment, while considering scheduling policies, work rules, and service objectives.
    * */
    public static List<AppointmentBookingSlot> getSlots(Id serviceAppointmentId) {
        System.debug('@@@@ serviceAppointmentId: ' + serviceAppointmentId);
        ServiceAppointment serviceAppointment = [SELECT Id, AccountId, ContactId, EarliestStartTime, DueDate FROM ServiceAppointment WHERE Id =: serviceAppointmentId LIMIT 5];

        Id schedulingPolicyId = [SELECT Id, Name FROM FSL__Scheduling_Policy__c LIMIT 1].Id;

        Id operatingHoursId  = [SELECT Id, Name FROM OperatingHours WHERE Name = 'Energy Assessment Calendar' LIMIT 1].Id;

        Timezone tz = UserInfo.getTimeZone();
        System.debug('getting slots with....'+ serviceAppointment.Id + '//'+schedulingPolicyId+ '//'+operatingHoursId+ '//'+tz) ;
        List<FSL.AppointmentBookingSlot> slots = FSL.AppointmentBookingService.GetSlots(serviceAppointment.Id, schedulingPolicyId, operatingHoursId, tz, false);
        System.debug('slots: ' + slots);

        List<AppointmentBookingSlot> slotList = new List<AppointmentBookingSlot>();
        for(FSL.AppointmentBookingSlot slot : slots){
            DateTime slotStartDT = slot.interval.start;
            DateTime slotFinishDT = slot.interval.finish;
            Date slotStartDay = slotStartDT.date();
            
            if(slotStartDay > Date.today() ){
                AppointmentBookingSlot newSlot = new AppointmentBookingSlot();
                Interval times = new Interval();
                times.startDT = slot.interval.start;
                times.endDT = slot.interval.finish;
                newSlot.grade = slot.grade;
                newSlot.times = times;
                slotList.add(newSlot);
            }
        }
        System.debug('slotList:: '+ slotList);
       
        
        //return setToTimeZone(slotList);
        return slotList;
    }
    
    public static DateTime utcToUserTimezone(DateTime utcDT){
        DateTime userDT = utcDT;
        String utcDtString = utcDT.format('yyyy-MM-dd HH:mm:ss', 'UTC');
        //String utcDtString = utcDT.format('yyyy-MM-dd hh:mm a', 'UTC');

        System.debug('@@@@ str: '+utcDtString);
        userDT = DateTime.valueOf(utcDtString);
        System.debug('@@@@ DT: '+userDT);
        return userDT;
    }
    
    /*public static List<AppointmentBookingSlot> setToTimezone(List<AppointmentBookingSlot> slots){
        if(slots.isEmpty()){
            return slots;
        }
        String timeZone = UserInfo.getTimeZone().getID();

        for(AppointmentBookingSlot slot: slots){
            if(slot.times != NULL){
                System.debug('~~~~~~');
                System.debug('BEFORE: '+ slot.times.endDT);
                slot.times.startDT = Datetime.valueOf(slot.times.startDT);
                slot.times.endDT = Datetime.valueOf(slot.times.endDT);
                System.debug('AFTER: '+ slot.times.endDT.format());
            }
        }
        
        return slots;
    }*/
    
    

    public class CmpData {
        @AuraEnabled
        public Id workOrderId {get;set;}
        @AuraEnabled
        public Id serviceAppointmentId {get;set;}
        @AuraEnabled
        public List<AppointmentBookingSlot> slots {get;set;}
        @AuraEnabled
        public Boolean isEligibleCustomer {get;set;}
    }
    public class AppointmentBookingSlot {
        @AuraEnabled
        public Decimal grade {get;set;}
        @AuraEnabled
        public Interval times {get;set;}
    }
    public class Interval {
        @AuraEnabled
        public DateTime startDT {get;set;}
        @AuraEnabled
        public DateTime endDT {get;set;}
    }
}

Component:
<aura:component implements="flexipage:availableForAllPageTypes,forceCommunity:availableForAllPageTypes,force:hasRecordId,flexipage:availableForRecordHome" access="global"
                controller="advic_fsl_AppointmentBooking">



    <aura:attribute name="debug" type="Boolean" default="false"/>

    <aura:attribute name="loading" type="Boolean" default="true"/>
    <aura:attribute name="showSpinner" type="Boolean" default="true"/>
    <aura:attribute name="isEligible" type="Boolean" default="true"/>
    <aura:attribute name="showError" type="Boolean" default="false"/>
    <aura:attribute name="errorMessage" type="String"/>
    <aura:attribute name="rowCount" type="Integer" default="4"/>
    <aura:attribute name="isOpen" type="boolean" default="false"/>


    <aura:attribute name="data" type="Object" default="{}"/>
    <aura:attribute name="slotList" type="List" default="[]"/>
    <aura:attribute name="allSlots" type="List" default="[]"/>
    <aura:attribute name="workOrderId" type="Id"/>
    <aura:attribute name="serviceAppointmentId" type="Id"/>
    <aura:attribute name="selectedDate" type="Date"/>
    <aura:attribute name="todayDate" type="Date"/>
    <aura:attribute name="currentUser" type="User"/>
	<force:recordData aura:id="recordLoader" recordId="{!$SObjectType.CurrentUser.Id}" fields="Account.BillingStreet, Account.BillingCity, Account.BillingState, Account.BillingCountry, Account.BillingPostalCode" targetFields="{!v.currentUser}"/> 
    <aura:handler name="init" value="{!this}" action="{!c.initCmpData}"/>
    <aura:handler name="change" value="{!v.selectedDate}" action="{!c.getFilterApptSlots}"/>
    <aura:if isTrue="{!v.debug}">
        Debug<br/>
        recId {!v.recordId}<br/>
        data {!v.data.slots.length}<br/>
        todayDate {!v.todayDate}<br/>
        selectedDate {!v.selectedDate}<br/>
        isEligible {!v.isEligible} <br/>
    </aura:if>

    <aura:if isTrue="{!v.loading}">
        <aura:if isTrue="{!v.showSpinner}">
            <lightning:spinner/>
        </aura:if>
        <aura:set attribute="else">
            <aura:if isTrue="{!v.isEligible}">
                <div class="cmpWrapper">
                    <lightning:layout multipleRows="true">
                        <lightning:layoutItem size="4" padding="around-small">
                            <span class="calWrapper">
                                
                                <center>
                                 
                                    <c:DatePicker aura:id="closeDate" label="Select a preferred appointment date to view availability"
                                                  placeholder="Date"
                                                  value="{!v.selectedDate}"
                                                  formatSpecifier="MM/dd/yyyy" />
                                </center>
                            </span>
                            
                            
                            <br/>
                             <aura:if isTrue="{!v.isEligible}">
                                 <center><br/>
            <div class="slds-float_leftX additionalOption slds-p-bottom_xx-small">
               <h2> Not seeing any results or can’t find an appointment that fits your needs? Contact us at (800) 422-5365.</h2>
                
            </div>
        </center>
        <br/>
            
    </aura:if>
    <center>
        <!-- removing create a case access-->
        <!-- <a href="./contactsupport">
            <lightning:button label="Create Case" variant="brand"/>
        </a> -->
    </center>
                            
                            
                            
                            
                        </lightning:layoutItem>
                        <lightning:layoutItem size="8" padding="around-small">
                            <span class="slotsWrapper">
                                <center>
                               
                                    <aura:iteration items="{!v.slotList}" var="slot" indexVar="i">
                                        <aura:if isTrue="{!(i > v.rowCount) == false}">
                                            <c:AdVic_BookingSlot slot="{!slot}" workOrderId="{!v.workOrderId}" serviceAppointmentId="{!v.serviceAppointmentId}"/>
                                        </aura:if>
                                    </aura:iteration>
                                    <lightning:button label="Display Additional Appointments" onclick="{!c.showMore}" variant="brand"/>
                                </center>
                            </span>
                        </lightning:layoutItem>
                    </lightning:layout>
                    
                    
                    
                    
                    
                    

                   
                </div>
                <aura:set attribute="else">
                    <lightning:icon iconName="utility:warning" alternativeText="Customer Not Eligible"
                                    variant="Warning" title="Not Eligible" class="notification-icon"/>
                    <aura:if isTrue="{!v.showError}">
                        <div id="error">
<!--                            <lightning:input value="{!v.errorMessage}" readonly="true"/>-->
                           
                        </div>
                        <aura:set attribute="else">
                            <br/>
                        </aura:set>
                    </aura:if>
                    <br/>
                    <span>{!v.errorMessage}</span>
                </aura:set>
            </aura:if>
        </aura:set>
    </aura:if>

    <br/>
    <br/>
   
</aura:component>


 
PriyaPriya (Salesforce Developers) 
Hey Bob,

Kindly follow the below steps :- 

1. Create one formula field which will calculate the no. of days (today - appoinment date)

2. Create an button on aura component and on click call the method.

3. Write a search method in apex class and in that query the record using the below sample query :- 

SELECT Id FROM ServiceAppointment where formulafield > 30

If the above steps fix your issue, then kindly mark it as the best answer.

Thanl you
BobPBobP
Thank you for helping Priya 
How would I create a the button in the aura component. I have limited ability with working with this type of code. I'm not sure wherei would add the search method in the apex class either.