/// <summary> /// Loads and returns all the cached schedules for the team between and including the past /// and future weeks. /// </summary> /// <param name="teamId">The ID of the team to return the cached schedules for.</param> /// <param name="pastWeeks">The number of past weeks to return.</param> /// <param name="futureWeeks">The number of future weeks to return.</param> /// <param name="startDayOfWeek">The start day of the week for the team.</param> /// <param name="scheduleCacheService">The schedule cache service to use to load the schedules.</param> /// <param name="timeService">The time service to use to get the current times.</param> /// <returns>The array of cached schedules.</returns> internal static async Task <CacheModel <ShiftModel>[]> LoadSchedulesAsync(string teamId, int pastWeeks, int futureWeeks, DayOfWeek startDayOfWeek, IScheduleCacheService scheduleCacheService, ISystemTimeService timeService) { var loadScheduleTasks = timeService.UtcNow .Range(pastWeeks, futureWeeks, startDayOfWeek) .Select(w => scheduleCacheService.LoadScheduleAsync(teamId, w)); return(await Task.WhenAll(loadScheduleTasks).ConfigureAwait(false)); }
public async Task <ResultModel> Run([ActivityTrigger] WeekModel weekModel, ILogger log) { log.LogWeek(weekModel); // initialise source service var credentials = await _secretsService.GetCredentialsAsync(weekModel.TeamId); _scheduleSourceService.SetCredentials(weekModel.TeamId, credentials); // get the current set of shifts from JDA var shifts = await _scheduleSourceService.ListWeekShiftsAsync(weekModel.TeamId, weekModel.StoreId, weekModel.StartDate, weekModel.TimeZoneInfoId); log.LogShifts(weekModel, shifts); // get the last saved set of shifts var savedShifts = await _scheduleCacheService.LoadScheduleAsync(weekModel.TeamId, weekModel.StartDate); // compute the delta var delta = _scheduleDeltaService.ComputeDelta(savedShifts.Tracked, shifts); log.LogFullDelta(weekModel, delta); if (delta.HasChanges) { delta.RemoveSkipped(savedShifts.Skipped); delta.ApplyMaximum(_options.MaximumDelta); log.LogPartialDelta(weekModel, delta); var allShifts = delta.All; // set teams employee var employeeLookup = BuildEmployeeLookup(savedShifts.Tracked); await SetTeamsEmployeeIds(allShifts.Where(s => string.IsNullOrEmpty(s.TeamsEmployeeId)), employeeLookup, weekModel, log); // set job & department name var jobLookup = BuildJobLookup(savedShifts.Tracked); await SetJobAndDepartmentName(allShifts, jobLookup, weekModel, log); // set teams schedule group (N.B this must be set after teams employee id and jobs) var groupLookup = BuildScheduleGroupLookup(savedShifts.Tracked); await SetTeamsSchedulingGroupId(allShifts.Where(s => string.IsNullOrEmpty(s.TeamsSchedulingGroupId)), groupLookup, weekModel, log); await AddEmployeesToSchedulingGroups(delta, groupLookup, weekModel, log); // update teams await ApplyDeltaAsync(weekModel, delta, log); log.LogAppliedDelta(weekModel, delta); // apply the final delta to the savedShifts delta.ApplyChanges(savedShifts.Tracked); delta.ApplySkipped(savedShifts.Skipped); await _scheduleCacheService.SaveScheduleAsync(weekModel.TeamId, weekModel.StartDate, savedShifts); } return(delta.AsResult()); }
protected async Task <ShiftModel> GetOpenShift(string teamId, string openShiftId) { var loadScheduleTasks = DateTime.UtcNow .Range(_teamOptions.PastWeeks, _teamOptions.FutureWeeks, _teamOptions.StartDayOfWeek) .Select(w => _scheduleCacheService.LoadScheduleAsync(GetSaveScheduleId(teamId), w)); var cacheModels = await Task.WhenAll(loadScheduleTasks).ConfigureAwait(false); return(cacheModels .SelectMany(c => c.Tracked) .FirstOrDefault(s => s.TeamsShiftId == openShiftId)); }
public override async Task <IActionResult> HandleRequest(ChangeRequest changeRequest, ChangeItemRequest changeItemRequest, ChangeResponse changeResponse, string entityId, ILogger log, IDurableOrchestrationClient starter) { var connectionModel = await _scheduleConnectorService.GetConnectionAsync(entityId).ConfigureAwait(false); var loadScheduleTasks = DateTime.UtcNow .Range(0, _teamOptions.FutureWeeks, _teamOptions.StartDayOfWeek) // pastWeek = 0 as we don't swap shifts from previous weeks .Select(w => _scheduleCacheService.LoadScheduleAsync(entityId, w)); var cacheModels = await Task.WhenAll(loadScheduleTasks).ConfigureAwait(false); var fromShift = cacheModels.SelectMany(c => c.Tracked).FirstOrDefault(s => s.TeamsShiftId == changeItemRequest.Id); if (fromShift == null) { return(new ChangeErrorResult(changeResponse, changeItemRequest, ErrorCodes.SenderShiftNotFound, _stringLocalizer[ErrorCodes.SenderShiftNotFound], true)); } var user = await _cacheService.GetKeyAsync <EmployeeModel>(ApplicationConstants.TableNameEmployees, fromShift.WfmEmployeeId).ConfigureAwait(false); if (user == null) { return(new ChangeErrorResult(changeResponse, changeItemRequest, ErrorCodes.UserCredentialsNotFound, _stringLocalizer[ErrorCodes.UserCredentialsNotFound], true)); } try { var wfmMatches = await _wfmDataService.GetEligibleTargetsForShiftSwap(fromShift, user, connectionModel.WfmBuId).ConfigureAwait(false); var matches = new List <string>(); foreach (var wfmShiftId in wfmMatches) { var swappableTeamsShift = cacheModels.SelectMany(c => c.Tracked).FirstOrDefault(s => s.WfmShiftId == wfmShiftId); if (swappableTeamsShift != null) { matches.Add(swappableTeamsShift.TeamsShiftId); } } return(new ChangeSuccessResult(changeResponse, changeItemRequest, matches)); } catch (WfmException wex) { return(WfmErrorToActionResult(wex.Error, changeItemRequest, changeResponse)); } }
private async Task <List <ShiftModel> > GetCachedShiftsAsync(ConnectionModel connection, DateTime weekStartDate) { var cachedShifts = await _scheduleCacheService.LoadScheduleAsync(connection.TeamId, weekStartDate).ConfigureAwait(false); return(cachedShifts.Tracked.OrderBy(s => s.StartDate).ToList()); }
public override async Task <IActionResult> HandleRequest(ChangeRequest changeRequest, ChangeItemRequest changeItemRequest, ChangeResponse changeResponse, string teamId, ILogger log, IDurableOrchestrationClient starter) { // get the open shift from the change item var openShift = changeItemRequest.Body.ToObject <OpenShiftResponse>(); // get the proposed shift to be created from the open shift var proposedShift = changeRequest.Requests .Where(r => r.Method.Equals("POST", StringComparison.OrdinalIgnoreCase)) .Select(r => r.Body.ToObject <ShiftResponse>()) .First(); var connectionModel = await _scheduleConnectorService.GetConnectionAsync(teamId).ConfigureAwait(false); // get the corresponding open shift from cache var localStartDate = openShift.SharedOpenShift.StartDateTime.ApplyTimeZoneOffset(connectionModel.TimeZoneInfoId); var weekStartDate = localStartDate.StartOfWeek(_teamOptions.StartDayOfWeek); var scheduleId = teamId + ApplicationConstants.OpenShiftsSuffix; var cacheModel = await _scheduleCacheService.LoadScheduleAsync(scheduleId, weekStartDate).ConfigureAwait(false); var assignedShift = cacheModel.Tracked.FirstOrDefault(o => o.TeamsShiftId == openShift.Id); if (assignedShift == null) { // we didn't find an open shift with this ID return(new ChangeErrorResult(changeResponse, changeItemRequest, ErrorCodes.NoOpenShiftsFound, _stringLocalizer[ErrorCodes.NoOpenShiftsFound])); } // get the employee object for the manager who initiated the change var manager = await _cacheService.GetKeyAsync <EmployeeModel>(ApplicationConstants.TableNameEmployees, openShift.LastModifiedBy.User.Id).ConfigureAwait(false); if (manager == null) { return(new ChangeErrorResult(changeResponse, changeItemRequest, ErrorCodes.UserCredentialsNotFound, _stringLocalizer[ErrorCodes.UserCredentialsNotFound])); } // get the employee object for the user the open shift is assigned to var employee = await _cacheService.GetKeyAsync <EmployeeModel>(ApplicationConstants.TableNameEmployees, proposedShift.UserId).ConfigureAwait(false); if (employee == null) { return(new ChangeErrorResult(changeResponse, changeItemRequest, ErrorCodes.UserCredentialsNotFound, _stringLocalizer[ErrorCodes.UserCredentialsNotFound])); } var wfmResponse = await _wfmActionService.ManagerAssignOpenShiftAsync(assignedShift, manager, employee, connectionModel.WfmBuId, connectionModel.TimeZoneInfoId, log).ConfigureAwait(false); if (!wfmResponse.Success) { return(new ChangeErrorResult(changeResponse, changeItemRequest, wfmResponse.Error.Code, wfmResponse.Error.Message)); } var policy = GetConflictRetryPolicy(_teamOptions.RetryMaxAttempts, _teamOptions.RetryIntervalSeconds); // as it has been assigned successfully, decrement the quantity assignedShift.Quantity--; // update the open shift in the cache await policy.ExecuteAsync(() => UpdateCachedOpenShiftsAsync(scheduleId, assignedShift, weekStartDate)).ConfigureAwait(false); // convert the open shift to a shift and add it to the week shifts cache assignedShift.Quantity = 1; assignedShift.WfmEmployeeId = employee.WfmEmployeeId; assignedShift.TeamsEmployeeId = employee.TeamsEmployeeId; assignedShift.TeamsShiftId = proposedShift.Id; assignedShift.WfmShiftId = wfmResponse.NewEntityId; await policy.ExecuteAsync(() => UpdateCachedShiftsAsync(teamId, assignedShift, weekStartDate)).ConfigureAwait(false); // finally, set up a deferred action to share the schedule var deferredActionModel = new DeferredActionModel { ActionType = DeferredActionModel.DeferredActionType.ShareTeamSchedule, DelaySeconds = _teamOptions.DelayedActionSeconds, ShareStartDate = openShift.SharedOpenShift.StartDateTime.Date, ShareEndDate = openShift.SharedOpenShift.EndDateTime.Date.AddHours(23).AddMinutes(59), TeamId = teamId }; await starter.StartNewAsync(nameof(DeferredActionOrchestrator), deferredActionModel).ConfigureAwait(false); return(new ChangeSuccessResult(changeResponse)); }
protected override async Task <CacheModel <ShiftModel> > GetSavedRecordsAsync(TeamActivityModel activityModel, ILogger log) { return(await _scheduleCacheService.LoadScheduleAsync(GetSaveScheduleId(activityModel.TeamId), activityModel.StartDate).ConfigureAwait(false)); }