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}."); }
/// <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); }