Initialize Shift Work Topics with Capacity

Initialize capacity for shift work topics in your org.

Required Editions

Available in: Lightning Experience.
Available in: Enterprise and Unlimited Editions
User Permissions Needed
Run the script:

API Enabled

AND

View All Data

AND

Author Apex

By default, after you enable capacity-based scheduling, the capacity of your shift work topics is set to 0. Use these scripts to populate values in your shift work topics so that your schedule works as it did before you enabled capacity-based scheduling. We recommend using these scripts if you have existing shift records that you want to quickly update to ensure business continuity. Alternatively, you can manually set the capacity for the shift work topics.

  1. Depending on the Scheduler for Health Cloud setting in your org, use one of these scripts.
    • Disabled
      //For Health Cloud Disabled
      global class InitialiseExistingSWTWithCapacity implements Database.Batchable<SObject> {
        global Database.QueryLocator start(Database.BatchableContext BC) {
          return Database.getQueryLocator([SELECT Id, StartTime, EndTime FROM Shift]);
        }
      
        global void execute(Database.BatchableContext BC, List<SObject> scope) {
          List<String> shiftIds = new List<String>();
      
          for (Shift shift : (List<Shift>) scope) {
            shiftIds.add(shift.Id);
          }
      
          // create maps to avoid queries in the loop
          List<Shift> shiftInfo = [
            SELECT ServiceTerritoryId, Id, StartTime, EndTime
            FROM Shift
            WHERE Id IN :shiftIds
          ];
          List<String> serviceTerriroryIds = new List<String>();
          for (Shift shift : shiftInfo) {
            serviceTerriroryIds.add(shift.ServiceTerritoryId);
          }
      
          Map<String, String> mapStToServiceAppointmentLimitType = new Map<String, String>();
          List<ServiceTerritory> sts = [
            SELECT ServiceAppointmentLimitType, Id
            FROM ServiceTerritory
            WHERE Id IN :serviceTerriroryIds
          ];
          for (ServiceTerritory st : sts) {
            mapStToServiceAppointmentLimitType.put(
              st.Id,
              st.ServiceAppointmentLimitType
            );
          }
          Map<String, String> mapShiftToServiceTerritory = new Map<String, String>();
          Map<String, Decimal> mapShiftToDuration = new Map<String, Decimal>();
      
          for (Shift shift : shiftInfo) {
            mapShiftToServiceTerritory.put(shift.Id, shift.ServiceTerritoryId);
            Decimal shiftDuration =
              (shift.EndTime.getTime() - shift.StartTime.getTime()) / (1000 * 60);
            mapShiftToDuration.put(shift.Id, shiftDuration);
          }
          Map<String, Decimal> mapWorkTypeToDuration = new Map<String, Decimal>();
      
          List<WorkType> wts = [SELECT Id, DurationInMinutes FROM WorkType];
          for (WorkType wt : wts) {
            mapWorkTypeToDuration.put(wt.Id, wt.DurationInMinutes);
          }
      
          Map<String, List<String>> mapShiftToWorkTypeGroupList = new Map<String, List<String>>();
      
          List<ShiftWorkTopic> shiftWorkTopics = [
            SELECT Id, WorkTypeGroupId, ShiftId, MaxAppointments
            FROM ShiftWorkTopic
            WHERE ShiftId IN :shiftIds AND WorkTypeGroupId != NULL
          ];
      
          for (ShiftWorkTopic swt : shiftWorkTopics) {
            List<String> existingSWTList = mapShiftToWorkTypeGroupList.get(
              swt.ShiftId
            );
            if (existingSWTList == null) {
              existingSWTList = new List<String>{ (String) swt.WorkTypeGroupId };
            } else {
              existingSWTList.add(swt.WorkTypeGroupId);
            }
            mapShiftToWorkTypeGroupList.put(swt.ShiftId, existingSWTList);
          }
      
          Map<String, String> mapWorkTypeGroupToWorkType = new Map<String, String>();
          List<WorkTypeGroupMember> wtgms = [
            SELECT Id, WorkTypeGroupId, WorkTypeId
            FROM WorkTypeGroupMember
          ];
          for (WorkTypeGroupMember wtgm : wtgms) {
            mapWorkTypeGroupToWorkType.put(wtgm.WorkTypeGroupId, wtgm.WorkTypeId);
          }
      
          Map<String, List<ShiftWorkTopic>> mapShiftToSWTList = new Map<String, List<ShiftWorkTopic>>();
      
          for (ShiftWorkTopic swt : shiftWorkTopics) {
            List<ShiftWorkTopic> existingSWTList = mapShiftToSWTList.get(swt.ShiftId);
            if (existingSWTList == null) {
              existingSWTList = new List<ShiftWorkTopic>{ swt };
            } else {
              existingSWTList.add(swt);
            }
            mapShiftToSWTList.put(swt.ShiftId, existingSWTList);
          }
          Map<String, Integer> mapSWTToConcurrency = new Map<String, Integer>();
          for (ShiftWorkTopic swt : shiftWorkTopics) {
            if (swt.MaxAppointments != null) {
              mapSWTToConcurrency.put(swt.Id, swt.MaxAppointments);
            } else {
              mapSWTToConcurrency.put(swt.Id, 1);
            }
          }
          // main code starts here
      
          for (String shiftId : shiftIds) {
            String ServiceAppointmentLimitType = mapStToServiceAppointmentLimitType.get(
              mapShiftToServiceTerritory.get(shiftId)
            );
            if (
              ServiceAppointmentLimitType == 'Maximum Limit' ||
              ServiceAppointmentLimitType == null
            ) {
              Double shiftDuration = mapShiftToDuration.get(shiftId);
              for (ShiftWorkTopic swt : mapShiftToSWTList.get(shiftId)) {
                Double workTypeDuration = mapWorkTypeToDuration.get(
                  mapWorkTypeGroupToWorkType.get((swt.WorkTypeGroupId))
                );
                Double concurrency = mapSWTToConcurrency.get(swt.Id);
                swt.ServiceAppointmentLimit = ((shiftDuration / workTypeDuration)
                    .intValue() * concurrency)
                  .intValue();
              }
            } else if (ServiceAppointmentLimitType == 'Achievable Limit') {
              List<ShiftWorkTopic> swtList = mapShiftToSWTList.get(shiftId);
              if (swtList == null) {
                // No SWT in this shift do no process
                continue;
              }
              Integer maximumCapacity = 0;
              Decimal shiftDuration = mapShiftToDuration.get(shiftId);
      
              for (ShiftWorkTopic swt : mapShiftToSWTList.get(shiftId)) {
                Integer concurrency = mapSWTToConcurrency.get(swt.Id);
                Double workDuration = mapWorkTypeToDuration.get(
                  mapWorkTypeGroupToWorkType.get((swt.WorkTypeGroupId))
                );
                maximumCapacity = Math.max(
                  maximumCapacity,
                  (shiftDuration / workDuration).intValue() * concurrency
                );
              }
              Integer finalCapacity = 0;
      
              for (Integer cap = 0; cap <= maximumCapacity; cap++) {
                Double sumLHS = 0;
      
                for (ShiftWorkTopic swt : mapShiftToSWTList.get(shiftId)) {
                  Integer concurrency = mapSWTToConcurrency.get(swt.Id);
                  Double workDuration = mapWorkTypeToDuration.get(
                    mapWorkTypeGroupToWorkType.get((swt.WorkTypeGroupId))
                  );
                  sumLHS += Math.ceil((1.0 * cap) / concurrency) * workDuration;
                }
                if (sumLHS <= shiftDuration) {
                  finalCapacity = cap;
                } else {
                  break;
                }
              }
      
              for (ShiftWorkTopic swt : mapShiftToSWTList.get(shiftId)) {
                swt.ServiceAppointmentLimit = finalCapacity;
              }
            }
          }
          update shiftWorkTopics;
        }
      
        global void finish(Database.BatchableContext BC) {
          // Send notification or log the completion
          System.debug('Batch job completed successfully.');
        }
      }
    • Enabled
      //For Health Cloud Enabled
      global class InitialiseExistingSWTWithCapacity implements Database.Batchable<SObject> {
        global Database.QueryLocator start(Database.BatchableContext BC) {
          return Database.getQueryLocator([SELECT Id, StartTime, EndTime FROM Shift]);
        }
      
        global void execute(Database.BatchableContext BC, List<SObject> scope) {
          List<String> shiftIds = new List<String>();
      
          for (Shift shift : (List<Shift>) scope) {
            shiftIds.add(shift.Id);
          }
      
          // create maps to avoid queries in the loop
          List<Shift> shiftInfo = [
            SELECT ServiceTerritoryId, Id, StartTime, EndTime
            FROM Shift
            WHERE Id IN :shiftIds
          ];
          List<String> serviceTerriroryIds = new List<String>();
          for (Shift shift : shiftInfo) {
            serviceTerriroryIds.add(shift.ServiceTerritoryId);
          }
      
          Map<String, String> mapSwtToServiceAppointmentLimitType = new Map<String, String>();
      
          Map<String, String> mapStToServiceAppointmentLimitType = new Map<String, String>();
          List<ServiceTerritory> sts = [
            SELECT ServiceAppointmentLimitType, Id
            FROM ServiceTerritory
            WHERE Id IN :serviceTerriroryIds
          ];
          for (ServiceTerritory st : sts) {
            mapStToServiceAppointmentLimitType.put(
              st.Id,
              st.ServiceAppointmentLimitType
            );
          }
          Map<String, String> mapShiftToServiceTerritory = new Map<String, String>();
          Map<String, Decimal> mapShiftToDuration = new Map<String, Decimal>();
      
          for (Shift shift : shiftInfo) {
            mapShiftToServiceTerritory.put(shift.Id, shift.ServiceTerritoryId);
            Decimal shiftDuration =
              (shift.EndTime.getTime() - shift.StartTime.getTime()) / (1000 * 60);
            mapShiftToDuration.put(shift.Id, shiftDuration);
          }
          Map<String, Decimal> mapWorkTypeToDuration = new Map<String, Decimal>();
      
          List<WorkType> wts = [SELECT Id, DurationInMinutes FROM WorkType];
          for (WorkType wt : wts) {
            mapWorkTypeToDuration.put(wt.Id, wt.DurationInMinutes);
          }
      
          Map<String, List<String>> mapShiftToWorkTypeGroupList = new Map<String, List<String>>();
      
          List<ShiftWorkTopic> shiftWorkTopics = [
            SELECT Id, WorkTypeId, ShiftId, MaxAppointments
            FROM ShiftWorkTopic
            WHERE ShiftId IN :shiftIds AND WorkTypeId != NULL
          ];
      
          for (ShiftWorkTopic swt : shiftWorkTopics) {
            List<String> existingSWTList = mapShiftToWorkTypeGroupList.get(
              swt.ShiftId
            );
            if (existingSWTList == null) {
              existingSWTList = new List<String>{ (String) swt.WorkTypeId };
            } else {
              existingSWTList.add(swt.WorkTypeId);
            }
            mapShiftToWorkTypeGroupList.put(swt.ShiftId, existingSWTList);
          }
      
          Map<String, List<ShiftWorkTopic>> mapShiftToSWTList = new Map<String, List<ShiftWorkTopic>>();
      
          for (ShiftWorkTopic swt : shiftWorkTopics) {
            List<ShiftWorkTopic> existingSWTList = mapShiftToSWTList.get(swt.ShiftId);
            if (existingSWTList == null) {
              existingSWTList = new List<ShiftWorkTopic>{ swt };
            } else {
              existingSWTList.add(swt);
            }
            mapShiftToSWTList.put(swt.ShiftId, existingSWTList);
          }
          Map<String, Integer> mapSWTToConcurrency = new Map<String, Integer>();
          for (ShiftWorkTopic swt : shiftWorkTopics) {
            if (swt.MaxAppointments != null) {
              mapSWTToConcurrency.put(swt.Id, swt.MaxAppointments);
            } else {
              mapSWTToConcurrency.put(swt.Id, 1);
            }
          }
      
          // main code starts here
      
          for (String shiftId : shiftIds) {
            String ServiceAppointmentLimitType = mapStToServiceAppointmentLimitType.get(
              mapShiftToServiceTerritory.get(shiftId)
            );
            if (
              ServiceAppointmentLimitType == 'Maximum Limit' ||
              ServiceAppointmentLimitType == null
            ) {
              if (mapShiftToSWTList.get(shiftId) == null) {
                // No SWT in this shift do no process
                continue;
              }
              Double shiftDuration = mapShiftToDuration.get(shiftId);
              for (ShiftWorkTopic swt : mapShiftToSWTList.get(shiftId)) {
                Double workTypeDuration = mapWorkTypeToDuration.get(swt.WorkTypeId);
                Double concurrency = mapSWTToConcurrency.get(swt.Id);
                swt.ServiceAppointmentLimit = ((shiftDuration / workTypeDuration)
                    .intValue() * concurrency)
                  .intValue();
              }
            } else if (ServiceAppointmentLimitType == 'Achievable Limit') {
              List<ShiftWorkTopic> swtList = mapShiftToSWTList.get(shiftId);
              if (swtList == null) {
                // No SWT in this shift do no process
                continue;
              }
              Integer maximumCapacity = 0;
              Decimal shiftDuration = mapShiftToDuration.get(shiftId);
      
              for (ShiftWorkTopic swt : mapShiftToSWTList.get(shiftId)) {
                Integer concurrency = mapSWTToConcurrency.get(swt.Id);
                Double workDuration = mapWorkTypeToDuration.get((swt.WorkTypeId));
                maximumCapacity = Math.max(
                  maximumCapacity,
                  (shiftDuration / workDuration).intValue() * concurrency
                );
              }
              Integer finalCapacity = 0;
      
              for (Integer cap = 0; cap <= maximumCapacity; cap++) {
                Double sumLHS = 0;
      
                for (ShiftWorkTopic swt : mapShiftToSWTList.get(shiftId)) {
                  Integer concurrency = mapSWTToConcurrency.get(swt.Id);
                  Double workDuration = mapWorkTypeToDuration.get(swt.WorkTypeId);
                  sumLHS += Math.ceil((1.0 * cap) / concurrency) * workDuration;
                }
                if (sumLHS <= shiftDuration) {
                  finalCapacity = cap;
                } else {
                  break;
                }
              }
      
              for (ShiftWorkTopic swt : mapShiftToSWTList.get(shiftId)) {
                swt.ServiceAppointmentLimit = finalCapacity;
              }
            }
          }
      
          update shiftWorkTopics;
        }
      
        global void finish(Database.BatchableContext BC) {
          // Send notification or log the completion
          System.debug('Batch job completed successfully.');
        }
      }
  2. Create an Apex Class called InitialiseExistingSWTWithCapacity to trigger approval workflows. See Define Apex Classes.
  3. From Setup, select Developer Console.
  4. In the Developer Console, click Debug, then click Open Execute Anonymous Window.
  5. Execute this code.
    InitialiseExistingSWTWithCapacity batchJob = new InitialiseExistingSWTWithCapacity(); 
    Integer batchSize = 200; 
    Database.executeBatch(batchJob, batchSize);
  6. To view the progress of the script, in Setup, find and select Apex Jobs.