private async Task <bool> DeleteTimeOffReasonAsync(SetupDetails allRequiredConfigurations, string teamsId, string timeOffId)
        {
            this.telemetryClient.TrackTrace($"Deleting {timeOffId} for {teamsId}");

            var httpClient = this.httpClientFactory.CreateClient("ShiftsAPI");
            var requestUrl = $"teams/{teamsId}/schedule/timeOffReasons/{timeOffId}";

            var response = await this.graphUtility.SendHttpRequest(allRequiredConfigurations.GraphConfigurationDetails, httpClient, HttpMethod.Delete, requestUrl).ConfigureAwait(false);

            if (response.IsSuccessStatusCode)
            {
                return(true);
            }
            else
            {
                var failedCreateTimeOffReasonsProps = new Dictionary <string, string>()
                {
                    { "TeamId", teamsId },
                    { "TeamOffId", timeOffId },
                };

                this.telemetryClient.TrackTrace($"Deleting {timeOffId} for {teamsId}", failedCreateTimeOffReasonsProps);
                return(false);
            }
        }
 /// <summary>
 /// Deletes a given reason to both Shifts and the Database.
 /// </summary>
 /// <param name="allRequiredConfigurations">Object containing the AccessToken.</param>
 /// <param name="teamsId">MS Teams Id.</param>
 /// <param name="reason">The reason.</param>
 private async Task DeleteSingleReason(SetupDetails allRequiredConfigurations, string teamsId, TimeOffReasonResponse.TimeOffReason reason)
 {
     if (await this.DeleteTimeOffReasonAsync(allRequiredConfigurations, teamsId, reason.Id).ConfigureAwait(false))
     {
         await this.timeOffReasonProvider.DeleteSpecificReasons(reason.DisplayName).ConfigureAwait(false);
     }
 }
        /// <summary>
        /// Method that approves a time off request.
        /// </summary>
        /// <param name="timeOffLookUpEntriesFoundList">The time off look up entries that are found.</param>
        /// <param name="user">The user.</param>
        /// <param name="configurationDetails">The configuration details.</param>
        /// <param name="monthPartitionKey">The monthwise partition key.</param>
        /// <param name="globalTimeOffRequestDetails">The list of global time off request details.</param>
        /// <returns>A unit of execution.</returns>
        private async Task ApproveTimeOffRequestAsync(
            List <TimeOffMappingEntity> timeOffLookUpEntriesFoundList,
            List <UserDetailsModel> user,
            SetupDetails configurationDetails,
            string monthPartitionKey,
            List <GlobalTimeOffRequestItem> globalTimeOffRequestDetails)
        {
            this.telemetryClient.TrackTrace($"ApproveTimeOffRequestAsync started for {monthPartitionKey}.");

            for (int i = 0; i < timeOffLookUpEntriesFoundList.Count; i++)
            {
                var timeOffReqCon = new
                {
                    Message = string.Empty,
                };

                var httpClient = this.httpClientFactory.CreateClient("ShiftsAPI");

                // Send Passthrough header to indicate the sender of request in outbound call.
                httpClient.DefaultRequestHeaders.Add("X-MS-WFMPassthrough", configurationDetails.WFIId);

                var requestUrl    = $"teams/{user[i].ShiftTeamId}/schedule/timeOffRequests/{timeOffLookUpEntriesFoundList[i].ShiftsRequestId}/approve";
                var requestString = JsonConvert.SerializeObject(timeOffReqCon);

                var response = await this.graphUtility.SendHttpRequest(configurationDetails.GraphConfigurationDetails, httpClient, HttpMethod.Post, requestUrl, requestString).ConfigureAwait(false);

                if (response.IsSuccessStatusCode)
                {
                    TimeOffMappingEntity timeOffMappingEntity = new TimeOffMappingEntity
                    {
                        Duration           = globalTimeOffRequestDetails[i].TimeOffPeriods.TimeOffPeriod.Duration,
                        EndDate            = globalTimeOffRequestDetails[i].TimeOffPeriods.TimeOffPeriod.EndDate,
                        StartDate          = globalTimeOffRequestDetails[i].TimeOffPeriods.TimeOffPeriod.StartDate,
                        StartTime          = globalTimeOffRequestDetails[i].TimeOffPeriods.TimeOffPeriod.StartTime,
                        PayCodeName        = globalTimeOffRequestDetails[i].TimeOffPeriods.TimeOffPeriod.PayCodeName,
                        KronosPersonNumber = globalTimeOffRequestDetails[i].Employee.PersonIdentity.PersonNumber,
                        PartitionKey       = monthPartitionKey,
                        RowKey             = globalTimeOffRequestDetails[i].Id,
                        ShiftsRequestId    = timeOffLookUpEntriesFoundList[i].ShiftsRequestId,
                        IsActive           = true,
                        KronosRequestId    = globalTimeOffRequestDetails[i].Id,
                        ShiftsStatus       = ApiConstants.ApprovedStatus,
                        KronosStatus       = ApiConstants.ApprovedStatus,
                    };

                    this.AddorUpdateTimeOffMappingAsync(timeOffMappingEntity);
                }
            }

            this.telemetryClient.TrackTrace($"ApproveTimeOffRequestAsync ended for {monthPartitionKey}.");
        }
        /// <summary>
        /// Removes all reasons in Shifts except any given.
        /// </summary>
        /// <param name="allRequiredConfigurations">Object containing the AccessToken.</param>
        /// <param name="teamsId">MS Teams Id.</param>
        /// <param name="reasons">The list of reasons to edit.</param>
        private async Task DeleteMultipleReasons(SetupDetails allRequiredConfigurations, string teamsId, List <TimeOffReasonResponse.TimeOffReason> reasons)
        {
            var successfullyRemovedReasons = new List <string>();

            foreach (var reason in reasons)
            {
                if (await this.DeleteTimeOffReasonAsync(allRequiredConfigurations, teamsId, reason.Id).ConfigureAwait(false))
                {
                    successfullyRemovedReasons.Add(reason.DisplayName);
                }
            }

            await this.timeOffReasonProvider.DeleteSpecificReasons(successfullyRemovedReasons.ToArray()).ConfigureAwait(false);
        }
        /// <summary>
        /// Adds a given reason to both Shifts and the Database.
        /// </summary>
        /// <param name="allRequiredConfigurations">Object containign the AccessToken.</param>
        /// <param name="teamsId">MS Teams Id.</param>
        /// <param name="reason">The name of the reason.</param>
        private async Task AddSingleReason(SetupDetails allRequiredConfigurations, string teamsId, string reason)
        {
            (var success, TimeOffReasonResponse.TimeOffReason reasonToAdd) = await this.CreateTimeOffReasonAsync(allRequiredConfigurations, teamsId, reason).ConfigureAwait(false);

            if (success)
            {
                var paycodeMapping = new PayCodeToTimeOffReasonsMappingEntity
                {
                    PartitionKey    = teamsId,
                    RowKey          = reasonToAdd.DisplayName,
                    TimeOffReasonId = reasonToAdd.Id,
                };

                await this.azureTableStorageHelper.InsertOrMergeTableEntityAsync(paycodeMapping, "PayCodeToTimeOffReasonsMapping").ConfigureAwait(true);
            }
        }
        /// <summary>
        /// Update mapping reasons in storage.
        /// </summary>
        /// <param name="allRequiredConfigurations">Object containing the AccessToken.</param>
        /// <param name="kronosReasons">The reasons received from Kronos.</param>
        /// <returns>An awaitable task.</returns>
        private async Task UpdateTimeOffReasonsAsync(
            SetupDetails allRequiredConfigurations,
            List <string> teamsIds,
            List <string> kronosReasons)
        {
            var removeActions = new List <Task>();
            var addActions    = new List <Task>();

            this.telemetryClient.TrackTrace("Updating time off sync.");

            foreach (var team in teamsIds)
            {
                var shiftReasons = await this.GetTimeOffReasonAsync(allRequiredConfigurations, team).ConfigureAwait(false);

                if (shiftReasons == null)
                {
                    return;
                }

                this.telemetryClient.TrackTrace($"Deleting reasons for {team}");
                foreach (var shiftReason in shiftReasons)
                {
                    if (!kronosReasons.Contains(shiftReason.DisplayName))
                    {
                        removeActions.Add(this.DeleteSingleReason(allRequiredConfigurations, team, shiftReason));
                    }
                }

                this.telemetryClient.TrackTrace($"Adding reasons for {team}");
                foreach (var reason in kronosReasons)
                {
                    if (shiftReasons.Find(c => c.DisplayName == reason) == null)
                    {
                        addActions.Add(this.AddSingleReason(allRequiredConfigurations, team, reason));
                    }
                }
            }

            await Task.WhenAll(removeActions).ConfigureAwait(false);

            await Task.WhenAll(addActions).ConfigureAwait(false);

            return;
        }
        /// <summary>
        /// Creates mapping reasons in storage.
        /// </summary>
        /// <param name="allRequiredConfigurations">Object containing the AccessToken.</param>
        /// <param name="teams">List of team ids.</param>
        /// <returns>List of TimeOffReasons.</returns>
        private async Task ClearTimeOffReasons(
            SetupDetails allRequiredConfigurations,
            List <string> teams)
        {
            this.telemetryClient.TrackTrace("Began clearing of time off reasons.");
            foreach (var team in teams)
            {
                var initialshiftReasons = await this.GetTimeOffReasonAsync(allRequiredConfigurations, team).ConfigureAwait(false);

                if (initialshiftReasons != null)
                {
                    this.telemetryClient.TrackTrace($"Deleting reasons for {team}");
                    await this.DeleteMultipleReasons(allRequiredConfigurations, team, initialshiftReasons).ConfigureAwait(false);
                }
            }

            this.telemetryClient.TrackTrace("Ended clearing of time off reasons.");
            return;
        }
        /// <summary>
        /// Method to decline the time off request.
        /// </summary>
        /// <param name="globalTimeOffRequestItem">The time off request item.</param>
        /// <param name="user">The user.</param>
        /// <param name="timeOffId">The time off Id from Kronos.</param>
        /// <param name="configurationDetails">The configuration details.</param>
        /// <param name="monthPartitionKey">The month wise partition key.</param>
        /// <returns>A unit of execution.</returns>
        private async Task DeclineTimeOffRequestAsync(
            GlobalTimeOffRequestItem globalTimeOffRequestItem,
            UserDetailsModel user,
            string timeOffId,
            SetupDetails configurationDetails,
            string monthPartitionKey)
        {
            this.telemetryClient.TrackTrace($"DeclineTimeOffRequestAsync started for time off id {timeOffId}.");

            var httpClient = this.httpClientFactory.CreateClient("ShiftsAPI");

            // Send Passthrough header to indicate the sender of request in outbound call.
            httpClient.DefaultRequestHeaders.Add("X-MS-WFMPassthrough", configurationDetails.WFIId);

            var requestUrl = $"teams/" + user.ShiftTeamId + "/schedule/timeOffRequests/" + timeOffId + "/decline";

            var response = await this.graphUtility.SendHttpRequest(configurationDetails.GraphConfigurationDetails, httpClient, HttpMethod.Post, requestUrl).ConfigureAwait(false);

            if (response.IsSuccessStatusCode)
            {
                TimeOffMappingEntity timeOffMappingEntity = new TimeOffMappingEntity
                {
                    Duration           = globalTimeOffRequestItem.TimeOffPeriods.TimeOffPeriod.Duration,
                    EndDate            = globalTimeOffRequestItem.TimeOffPeriods.TimeOffPeriod.EndDate,
                    StartDate          = globalTimeOffRequestItem.TimeOffPeriods.TimeOffPeriod.StartDate,
                    StartTime          = globalTimeOffRequestItem.TimeOffPeriods.TimeOffPeriod.StartTime,
                    PayCodeName        = globalTimeOffRequestItem.TimeOffPeriods.TimeOffPeriod.PayCodeName,
                    KronosPersonNumber = globalTimeOffRequestItem.Employee.PersonIdentity.PersonNumber,
                    PartitionKey       = monthPartitionKey,
                    RowKey             = globalTimeOffRequestItem.Id,
                    ShiftsRequestId    = timeOffId,
                    IsActive           = true,
                    KronosRequestId    = globalTimeOffRequestItem.Id,
                    ShiftsStatus       = globalTimeOffRequestItem.StatusName,
                    KronosStatus       = ApiConstants.Refused,
                };

                this.AddorUpdateTimeOffMappingAsync(timeOffMappingEntity);
            }

            this.telemetryClient.TrackTrace($"DeclineTimeOffRequestAsync ended for time off id {timeOffId}.");
        }
Exemple #9
0
        /// <summary>
        /// This method will be able to get all of the necessary prerequisites.
        /// </summary>
        /// <returns>A unit of execution that contains an object of type <see cref="SetupDetails"/>.</returns>
        public async Task <SetupDetails> GetAllConfigurationsAsync()
        {
            SetupDetails setupDetails = new SetupDetails();

            var telemetryProps = new Dictionary <string, string>()
            {
                { "CallingAssembly", Assembly.GetCallingAssembly().GetName().Name },
                { "CallingMethod", "UpdateTeam" },
            };

            this.GetTenantDetails(out string tenantId, out string clientId, out string clientSecret, out string instance);

            // Get configuration info from table.
            var configurationEntity = (await this.configurationProvider.GetConfigurationsAsync().ConfigureAwait(false)).FirstOrDefault();

            if (!string.IsNullOrEmpty(configurationEntity?.WfmApiEndpoint))
            {
                setupDetails.WfmEndPoint = configurationEntity?.WfmApiEndpoint;
            }
            else
            {
                setupDetails.ErrorMessage += Resource.KronosURLNotPresent;
            }

            if (!string.IsNullOrEmpty(configurationEntity?.AdminAadObjectId))
            {
                setupDetails.WFIId = configurationEntity?.WorkforceIntegrationId;
                var accessToken = this.graphUtility.GetAccessTokenAsync(tenantId, instance, clientId, clientSecret, configurationEntity?.AdminAadObjectId).GetAwaiter().GetResult();

                setupDetails.ShiftsAdminAadObjectId = configurationEntity?.AdminAadObjectId;

                if (!string.IsNullOrEmpty(accessToken))
                {
                    setupDetails.ShiftsAccessToken = accessToken;
                    setupDetails.TenantId          = tenantId;
                }
                else
                {
                    telemetryProps.Add("ShiftAccessToken", Resource.IssueShiftsAccessToken);
                    setupDetails.ErrorMessage += Resource.IssueShiftsAccessToken;
                }

                setupDetails.KronosSession  = this.GetKronosSessionAsync().GetAwaiter().GetResult();
                setupDetails.KronosUserName = this.appSettings.WfmSuperUsername;
                setupDetails.KronosPassword = this.appSettings.WfmSuperUserPassword;

                if (string.IsNullOrEmpty(setupDetails.KronosSession))
                {
                    telemetryProps.Add("KronosStatus", Resource.InvalidKronosCredentials);
                    setupDetails.ErrorMessage += Resource.InvalidKronosCredentials;
                }
            }
            else
            {
                setupDetails.ErrorMessage += Resource.WorkforceIntegrationAdminNotFound;
                telemetryProps.Add("WorkforceIntegrationStatus", Resource.WorkforceIntegrationAdminNotFound);
            }

            setupDetails.IsAllSetUpExists =
                !string.IsNullOrEmpty(setupDetails.WFIId) &&
                !string.IsNullOrEmpty(setupDetails.ShiftsAccessToken) &&
                !string.IsNullOrEmpty(setupDetails.KronosSession) &&
                !string.IsNullOrEmpty(setupDetails.WfmEndPoint) &&
                !string.IsNullOrEmpty(setupDetails.KronosPassword) &&
                !string.IsNullOrEmpty(setupDetails.KronosUserName);

            return(setupDetails);
        }
        /// <summary>
        /// Create TimeOff Reasons in Shifts.
        /// </summary>
        /// <param name="allRequiredConfigurations">Object containing the AccessToken.</param>
        /// <param name="teamsId">MS Teams Id.</param>
        /// <param name="payCode">Kronos payCode.</param>
        /// <returns>None.</returns>
        private async Task <(bool, TimeOffReasonResponse.TimeOffReason)> CreateTimeOffReasonAsync(SetupDetails allRequiredConfigurations, string teamsId, string payCode)
        {
            this.telemetryClient.TrackTrace($"Adding {payCode} for {teamsId}");

            TimeOffReasonRequest.TimeOffReason timeOffReason = new TimeOffReasonRequest.TimeOffReason
            {
                DisplayName = payCode,
                IconType    = "plane",
                IsActive    = true,
            };

            var httpClient = this.httpClientFactory.CreateClient("ShiftsAPI");

            var requestUrl    = $"teams/{teamsId}/schedule/timeOffReasons";
            var requestString = JsonConvert.SerializeObject(timeOffReason);

            var response = await this.graphUtility.SendHttpRequest(allRequiredConfigurations.GraphConfigurationDetails, httpClient, HttpMethod.Post, requestUrl, requestString).ConfigureAwait(false);

            if (response.IsSuccessStatusCode)
            {
                var createdTimeOffReason = JsonConvert.DeserializeObject <TimeOffReasonResponse.TimeOffReason>(
                    await response.Content.ReadAsStringAsync().ConfigureAwait(true));
                return(true, createdTimeOffReason);
            }
            else
            {
                var failedCreateTimeOffReasonsProps = new Dictionary <string, string>()
                {
                    { "TeamId", teamsId },
                    { "PayCode", payCode },
                };

                this.telemetryClient.TrackTrace($"Failed to add {payCode} for {teamsId}", failedCreateTimeOffReasonsProps);
                return(false, null);
            }
        }
        /// <summary>
        /// Get time off reasons for a team.
        /// </summary>
        /// <param name="allRequiredConfigurations">Object containing the AccessToken.</param>
        /// <param name="teamsId">MS Teams Id.</param>
        /// <returns>List of TimeOffReasons.</returns>
        private async Task <List <TimeOffReasonResponse.TimeOffReason> > GetTimeOffReasonAsync(SetupDetails allRequiredConfigurations, string teamsId)
        {
            this.telemetryClient.TrackTrace($"GetTimeOffReasonAsync for {teamsId}");

            var httpClient = this.httpClientFactory.CreateClient("ShiftsAPI");
            var requestUrl = $"teams/{teamsId}/schedule/timeOffReasons?$search=\"isActive=true\"";

            var response = await this.graphUtility.SendHttpRequest(allRequiredConfigurations.GraphConfigurationDetails, httpClient, HttpMethod.Get, requestUrl).ConfigureAwait(false);

            if (response.IsSuccessStatusCode)
            {
                string result = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                if (result != null)
                {
                    var res = JsonConvert.DeserializeObject <TimeOffReasonResponse.Temp>(result);
                    return(res.Value.Where(c => c.IsActive).ToList());
                }
            }
            else
            {
                var failedTimeOffReasonsProps = new Dictionary <string, string>()
                {
                    { "TeamId", teamsId },
                };
                this.telemetryClient.TrackTrace($"{MethodBase.GetCurrentMethod().Name}", failedTimeOffReasonsProps);
                return(null);
            }

            return(null);
        }
        /// <summary>
        /// Method that will add a time off request.
        /// </summary>
        /// <param name="configurationDetails">The configuration details.</param>
        /// <param name="userModelNotFoundList">The list of users that are not found.</param>
        /// <param name="timeOffNotFoundList">This list of time off records that are not found.</param>
        /// <param name="kronosPayCodeList">The list of Kronos WFC Paycodes.</param>
        /// <param name="monthPartitionKey">The month partition key.</param>
        /// <returns>A unit of execution.</returns>
        private async Task AddTimeOffAsync(
            SetupDetails configurationDetails,
            List <UserDetailsModel> userModelNotFoundList,
            List <GlobalTimeOffRequestItem> timeOffNotFoundList,
            List <PayCodeToTimeOffReasonsMappingEntity> kronosPayCodeList,
            string monthPartitionKey)
        {
            var telemetryProps = new Dictionary <string, string>()
            {
                { "CallingAssembly", Assembly.GetCallingAssembly().GetName().Name },
            };

            this.telemetryClient.TrackTrace($"AddTimeOffRequestAsync started for {monthPartitionKey}.", telemetryProps);

            // create entries from not found list
            for (int i = 0; i < timeOffNotFoundList.Count && kronosPayCodeList?.Count > 0; i++)
            {
                if (kronosPayCodeList[i]?.TimeOffReasonId != null)
                {
                    var timeOff = new TimeOff
                    {
                        UserId        = userModelNotFoundList[i].ShiftUserId,
                        SharedTimeOff = new TimeOffItem
                        {
                            TimeOffReasonId = kronosPayCodeList[i].TimeOffReasonId,
                            StartDateTime   = this.utility.CalculateStartDateTime(timeOffNotFoundList[i], userModelNotFoundList[i].KronosTimeZone),
                            EndDateTime     = this.CalculateEndDate(timeOffNotFoundList[i], userModelNotFoundList[i].KronosTimeZone),
                            Theme           = ScheduleEntityTheme.White,
                        },
                    };

                    var httpClient = this.httpClientFactory.CreateClient("ShiftsAPI");

                    // Send Passthrough header to indicate the sender of request in outbound call.
                    httpClient.DefaultRequestHeaders.Add("X-MS-WFMPassthrough", configurationDetails.WFIId);

                    var requestUrl    = $"teams/{userModelNotFoundList[i].ShiftTeamId}/schedule/timesOff";
                    var requestString = JsonConvert.SerializeObject(timeOff);

                    var response = await this.graphUtility.SendHttpRequest(configurationDetails.GraphConfigurationDetails, httpClient, HttpMethod.Post, requestUrl, requestString).ConfigureAwait(false);

                    if (response.IsSuccessStatusCode)
                    {
                        var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                        var timeOffResponse = JsonConvert.DeserializeObject <TimeOff>(responseContent);

                        TimeOffMappingEntity timeOffMappingEntity = new TimeOffMappingEntity
                        {
                            Duration           = timeOffNotFoundList[i].TimeOffPeriods.TimeOffPeriod.Duration,
                            EndDate            = timeOffNotFoundList[i].TimeOffPeriods.TimeOffPeriod.EndDate,
                            StartDate          = timeOffNotFoundList[i].TimeOffPeriods.TimeOffPeriod.StartDate,
                            StartTime          = timeOffNotFoundList[i].TimeOffPeriods.TimeOffPeriod.StartTime,
                            PayCodeName        = timeOffNotFoundList[i].TimeOffPeriods.TimeOffPeriod.PayCodeName,
                            KronosPersonNumber = timeOffNotFoundList[i].Employee.PersonIdentity.PersonNumber,
                            PartitionKey       = monthPartitionKey,
                            RowKey             = timeOffNotFoundList[i].Id,
                            ShiftsRequestId    = timeOffResponse.Id,
                            KronosRequestId    = timeOffNotFoundList[i].Id,
                            ShiftsStatus       = ApiConstants.Pending,
                            KronosStatus       = ApiConstants.Submitted,
                            IsActive           = true,
                        };

                        this.AddorUpdateTimeOffMappingAsync(timeOffMappingEntity);
                    }
                }
                else
                {
                    telemetryProps.Add("TimeOffReason for " + timeOffNotFoundList[i].Id, "NotFound");
                }

                this.telemetryClient.TrackTrace($"AddTimeOffRequestAsync ended for {monthPartitionKey}.", telemetryProps);
            }
        }
        /// <summary>
        /// This method processes all of the time offs in a batch manner.
        /// </summary>
        /// <param name="configurationDetails">The configuration details.</param>
        /// <param name="processKronosUsersQueueInBatch">The users in batch.</param>
        /// <param name="lookUpData">The look up data.</param>
        /// <param name="timeOffResponseDetails">The time off response details.</param>
        /// <param name="timeOffReasons">The time off reasons.</param>
        /// <param name="monthPartitionKey">The montwise partition key.</param>
        /// <returns>A unit of execution.</returns>
        private async Task ProcessTimeOffEntitiesBatchAsync(
            SetupDetails configurationDetails,
            IEnumerable <UserDetailsModel> processKronosUsersQueueInBatch,
            List <TimeOffMappingEntity> lookUpData,
            List <GlobalTimeOffRequestItem> timeOffResponseDetails,
            List <PayCodeToTimeOffReasonsMappingEntity> timeOffReasons,
            string monthPartitionKey)
        {
            this.telemetryClient.TrackTrace($"ProcessTimeOffEntitiesBatchAsync start at: {DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)}");

            var timeOffLookUpEntriesFoundList = new List <TimeOffMappingEntity>();
            var timeOffNotFoundList           = new List <GlobalTimeOffRequestItem>();
            var userModelList               = new List <UserDetailsModel>();
            var userModelNotFoundList       = new List <UserDetailsModel>();
            var kronosPayCodeList           = new List <PayCodeToTimeOffReasonsMappingEntity>();
            var timeOffRequestsPayCodeList  = new List <PayCodeToTimeOffReasonsMappingEntity>();
            var globalTimeOffRequestDetails = new List <GlobalTimeOffRequestItem>();

            foreach (var user in processKronosUsersQueueInBatch)
            {
                foreach (var timeOffRequestItem in timeOffResponseDetails.Where(x => x.Employee.PersonIdentity.PersonNumber == user.KronosPersonNumber))
                {
                    if (timeOffRequestItem.StatusName.Equals(ApiConstants.ApprovedStatus, StringComparison.Ordinal))
                    {
                        this.telemetryClient.TrackTrace($"ProcessTimeOffEntitiesBatchAsync look up count: {lookUpData.Count} ");

                        if (lookUpData.Count == 0)
                        {
                            // Getting a TimeOffReasonId object based on the TimeOff paycode from Kronos and the team ID in Shifts.
                            var timeOffReasonId = timeOffReasons.
                                                  Where(t => t.RowKey == timeOffRequestItem.TimeOffPeriods.TimeOffPeriod.PayCodeName && t.PartitionKey == user.ShiftTeamId).FirstOrDefault();
                            this.telemetryClient.TrackTrace($"ProcessTimeOffEntitiesBatchAsync PaycodeName : {timeOffRequestItem.TimeOffPeriods.TimeOffPeriod.PayCodeName} ");
                            this.telemetryClient.TrackTrace($"ProcessTimeOffEntitiesBatchAsync ReqId : {timeOffRequestItem.Id} ");
                            if (timeOffReasonId != null)
                            {
                                timeOffNotFoundList.Add(timeOffRequestItem);
                                userModelNotFoundList.Add(user);
                                kronosPayCodeList.Add(timeOffReasonId);
                            }
                            else
                            {
                                // Track in telemetry saying that the TimeOffReasonId cannot be found.
                                this.telemetryClient.TrackTrace($"Cannot find the TimeOffReason corresponding to the Kronos paycode: {timeOffRequestItem.TimeOffPeriods.TimeOffPeriod.PayCodeName}, Kronos request ID: {timeOffRequestItem.Id}");
                            }
                        }
                        else
                        {
                            var kronosUniqueIdExists = lookUpData.Where(x => x.KronosRequestId == timeOffRequestItem.Id);
                            var monthPartitions      = Utility.GetMonthPartition(
                                timeOffRequestItem.TimeOffPeriods.TimeOffPeriod.StartDate, timeOffRequestItem.TimeOffPeriods.TimeOffPeriod.EndDate);

                            this.telemetryClient.TrackTrace($"ProcessTimeOffEntitiesBatchAsync PaycodeName : {timeOffRequestItem.TimeOffPeriods.TimeOffPeriod.PayCodeName} ");
                            this.telemetryClient.TrackTrace($"ProcessTimeOffEntitiesBatchAsync ReqId : {timeOffRequestItem.Id} ");

                            if (kronosUniqueIdExists.Any() && kronosUniqueIdExists.FirstOrDefault().KronosStatus == ApiConstants.Submitted)
                            {
                                // Getting a TimeOffReasonId object based on the TimeOff paycode from Kronos and the team ID in Shifts.
                                var timeOffReasonId = timeOffReasons.
                                                      Where(t => t.RowKey == timeOffRequestItem.TimeOffPeriods.TimeOffPeriod.PayCodeName && t.PartitionKey == user.ShiftTeamId).FirstOrDefault();

                                // Kronos API does not return all the PayCodes present in Kronos UI. For such cases TimeOffReason mapping
                                // will be null and that TimeOffs will not be synced.
                                if (timeOffReasonId != null)
                                {
                                    timeOffLookUpEntriesFoundList.Add(kronosUniqueIdExists.FirstOrDefault());
                                    userModelList.Add(user);
                                    timeOffRequestsPayCodeList.Add(timeOffReasonId);
                                    globalTimeOffRequestDetails.Add(timeOffRequestItem);
                                }
                                else
                                {
                                    // Track in telemetry saying that the TimeOffReasonId cannot be found.
                                    this.telemetryClient.TrackTrace($"Cannot find the TimeOffReason corresponding to the Kronos paycode: {timeOffRequestItem.TimeOffPeriods.TimeOffPeriod.PayCodeName}, Kronos request ID: {timeOffRequestItem.Id}");
                                }
                            }
                            else if (kronosUniqueIdExists.Any() || monthPartitions?.FirstOrDefault() != monthPartitionKey)
                            {
                                continue;
                            }
                            else
                            {
                                // Getting a TimeOffReasonId object based on the TimeOff paycode from Kronos and the team ID in Shifts.
                                var timeOffReasonId = timeOffReasons.
                                                      Where(t => t.RowKey == timeOffRequestItem.TimeOffPeriods.TimeOffPeriod.PayCodeName && t.PartitionKey == user.ShiftTeamId).FirstOrDefault();

                                // Kronos API does not return all the PayCodes present in Kronos UI. For such cases TimeOffReason mapping
                                // will be null and that TimeOffs will not be synced.
                                if (timeOffReasonId != null)
                                {
                                    timeOffNotFoundList.Add(timeOffRequestItem);
                                    userModelNotFoundList.Add(user);
                                    kronosPayCodeList.Add(timeOffReasonId);
                                }
                                else
                                {
                                    // Track in telemetry saying that the TimeOffReasonId cannot be found.
                                    this.telemetryClient.TrackTrace($"Cannot find the TimeOffReason corresponding to the Kronos paycode: {timeOffRequestItem.TimeOffPeriods.TimeOffPeriod.PayCodeName}, Kronos request ID: {timeOffRequestItem.Id}");
                                }
                            }
                        }
                    }

                    if (timeOffRequestItem.StatusName.Equals(ApiConstants.Refused, StringComparison.Ordinal) ||
                        timeOffRequestItem.StatusName.Equals(ApiConstants.Retract, StringComparison.Ordinal))
                    {
                        var reqDetails = lookUpData.Where(c => c.KronosRequestId == timeOffRequestItem.Id).FirstOrDefault();
                        var timeOffReasonIdtoUpdate = timeOffReasons.Where(t => t.RowKey == timeOffRequestItem.TimeOffPeriods.TimeOffPeriod.PayCodeName && t.PartitionKey == user.ShiftTeamId).FirstOrDefault();
                        if (reqDetails != null && timeOffReasonIdtoUpdate != null && reqDetails.KronosStatus == ApiConstants.Submitted)
                        {
                            await this.DeclineTimeOffRequestAsync(
                                timeOffRequestItem,
                                user,
                                reqDetails.ShiftsRequestId,
                                configurationDetails,
                                monthPartitionKey).ConfigureAwait(false);
                        }
                        else
                        {
                            this.telemetryClient.TrackTrace($"The declined timeoff request {timeOffRequestItem.Id} is not submitted from Shifts or timeOff reason selected while submitting" +
                                                            "is not a valid paycode. Hence mapping is not present for TimeOffReason and Paycode.");
                        }
                    }
                }
            }

            await this.ApproveTimeOffRequestAsync(
                timeOffLookUpEntriesFoundList,
                userModelList,
                configurationDetails,
                monthPartitionKey,
                globalTimeOffRequestDetails).ConfigureAwait(false);

            await this.AddTimeOffAsync(
                configurationDetails,
                userModelNotFoundList,
                timeOffNotFoundList,
                kronosPayCodeList,
                monthPartitionKey).ConfigureAwait(false);

            this.telemetryClient.TrackTrace($"ProcessTimeOffEntitiesBatchAsync ended for {monthPartitionKey}.");
        }
        /// <summary>
        /// Creates a time off request that was requested in Teams.
        /// </summary>
        /// <param name="user">The user details of the time off requestor.</param>
        /// <param name="timeOffEntity">The time off to be created.</param>
        /// <param name="timeOffReason">The time off reason.</param>
        /// <param name="allRequiredConfigurations">Setup details.</param>
        /// <param name="kronosTimeZone">The kronos timezone.</param>
        /// <returns>Whether the time off request was created successfully or not.</returns>
        internal async Task <bool> CreateTimeOffRequestInKronosAsync(
            UserDetailsModel user,
            TimeOffRequestItem timeOffEntity,
            PayCodeToTimeOffReasonsMappingEntity timeOffReason,
            SetupDetails allRequiredConfigurations,
            string kronosTimeZone)
        {
            // Teams provides date times in UTC so convert to the local time.
            var localStartDateTime = this.utility.UTCToKronosTimeZone(timeOffEntity.StartDateTime, kronosTimeZone);
            var localEndDateTime   = this.utility.UTCToKronosTimeZone(timeOffEntity.EndDateTime, kronosTimeZone);

            // Construct the query date span for the Kronos request
            var queryStartDate = localStartDateTime.AddDays(
                -Convert.ToInt16(this.appSettings.CorrectedDateSpanForOutboundCalls, CultureInfo.InvariantCulture))
                                 .ToString(this.appSettings.KronosQueryDateSpanFormat, CultureInfo.InvariantCulture);

            var queryEndDate = localEndDateTime.AddDays(
                Convert.ToInt16(this.appSettings.CorrectedDateSpanForOutboundCalls, CultureInfo.InvariantCulture))
                               .ToString(this.appSettings.KronosQueryDateSpanFormat, CultureInfo.InvariantCulture);

            var timeOffReqQueryDateSpan = $"{queryStartDate}-{queryEndDate}";

            var commentTimeStamp = this.utility.UTCToKronosTimeZone(DateTime.UtcNow, kronosTimeZone).ToString(CultureInfo.InvariantCulture);
            var comments         = XmlHelper.GenerateKronosComments(timeOffEntity.SenderMessage, this.appSettings.SenderTimeOffRequestCommentText, commentTimeStamp);

            // Create the Kronos Time Off Request.
            var timeOffResponse = await this.timeOffActivity.CreateTimeOffRequestAsync(
                allRequiredConfigurations.KronosSession,
                localStartDateTime,
                localEndDateTime,
                timeOffReqQueryDateSpan,
                user.KronosPersonNumber,
                timeOffReason.RowKey,
                comments,
                new Uri(allRequiredConfigurations.WfmEndPoint)).ConfigureAwait(false);

            if (!string.IsNullOrWhiteSpace(timeOffResponse?.Error?.Message))
            {
                this.telemetryClient.TrackTrace($"Could not create the time off request : {timeOffResponse?.Error?.Message} ");
                return(false);
            }

            var submitTimeOffResponse = await this.timeOffActivity.SubmitTimeOffRequestAsync(
                allRequiredConfigurations.KronosSession,
                user.KronosPersonNumber,
                timeOffResponse?.EmployeeRequestMgm?.RequestItem?.GlobalTimeOffRequestItms?.FirstOrDefault()?.Id,
                timeOffReqQueryDateSpan,
                new Uri(allRequiredConfigurations.WfmEndPoint)).ConfigureAwait(false);

            TimeOffMappingEntity newTimeOffReq = new TimeOffMappingEntity();

            // IsActive represents whether the time off was successfully created.
            if (submitTimeOffResponse?.Status == ApiConstants.Failure)
            {
                newTimeOffReq.IsActive = false;
            }
            else
            {
                newTimeOffReq.IsActive = true;
            }

            newTimeOffReq.Duration           = timeOffResponse.EmployeeRequestMgm.RequestItem.GlobalTimeOffRequestItms.FirstOrDefault().TimeOffPeriodsList.TimeOffPerd.FirstOrDefault().Duration;
            newTimeOffReq.EndDate            = timeOffResponse.EmployeeRequestMgm.RequestItem.GlobalTimeOffRequestItms.FirstOrDefault().TimeOffPeriodsList.TimeOffPerd.FirstOrDefault().EndDate;
            newTimeOffReq.StartDate          = timeOffResponse.EmployeeRequestMgm.RequestItem.GlobalTimeOffRequestItms.FirstOrDefault().TimeOffPeriodsList.TimeOffPerd.FirstOrDefault().StartDate;
            newTimeOffReq.StartTime          = timeOffResponse.EmployeeRequestMgm.RequestItem.GlobalTimeOffRequestItms.FirstOrDefault().TimeOffPeriodsList.TimeOffPerd.FirstOrDefault().StartTime;
            newTimeOffReq.PayCodeName        = timeOffResponse.EmployeeRequestMgm.RequestItem.GlobalTimeOffRequestItms.FirstOrDefault().TimeOffPeriodsList.TimeOffPerd.FirstOrDefault().PayCodeName;
            newTimeOffReq.KronosPersonNumber = timeOffResponse.EmployeeRequestMgm.Employees.PersonIdentity.PersonNumber;
            newTimeOffReq.PartitionKey       = $"{localStartDateTime.Month}_{localStartDateTime.Year}";
            newTimeOffReq.RowKey             = timeOffResponse.EmployeeRequestMgm.RequestItem.GlobalTimeOffRequestItms.FirstOrDefault().Id;
            newTimeOffReq.ShiftsRequestId    = timeOffEntity.Id;
            newTimeOffReq.KronosRequestId    = timeOffResponse.EmployeeRequestMgm.RequestItem.GlobalTimeOffRequestItms.FirstOrDefault().Id;
            newTimeOffReq.KronosStatus       = ApiConstants.Submitted;
            newTimeOffReq.ShiftsStatus       = ApiConstants.Pending;

            this.AddorUpdateTimeOffMappingAsync(newTimeOffReq);

            // If isActive is false time off request was not submitted so return false and vice versa.
            return(newTimeOffReq.IsActive);
        }