/// <summary> /// Creates and stores a shift mapping entity. /// </summary> /// <param name="shift">A shift from Shifts.</param> /// <param name="user">A user mapping entity.</param> /// <param name="mappedTeam">A team mapping entity.</param> /// <param name="monthPartitionKey">The partition key for the shift.</param> /// <returns>A task.</returns> private async Task CreateAndStoreShiftMapping(ShiftsShift shift, AllUserMappingEntity user, TeamToDepartmentJobMappingEntity mappedTeam, List <string> monthPartitionKey) { var kronosUniqueId = this.utility.CreateShiftUniqueId(shift, mappedTeam.KronosTimeZone); var shiftMappingEntity = this.CreateNewShiftMappingEntity(shift, kronosUniqueId, user.RowKey, mappedTeam.TeamId); await this.shiftMappingEntityProvider.SaveOrUpdateShiftMappingEntityAsync(shiftMappingEntity, shift.Id, monthPartitionKey[0]).ConfigureAwait(false); }
/// <summary> /// Creates a shift mapping entity to be stored in the table. /// </summary> /// <param name="shift">The shift received from Shifts.</param> /// <param name="uniqueId">The unique ID for the shift.</param> /// <param name="kronoUserId">The user id of the user in Kronos.</param> /// <returns>Returns a <see cref="TeamsShiftMappingEntity"/>.</returns> public TeamsShiftMappingEntity CreateNewShiftMappingEntity( Microsoft.Teams.Shifts.Integration.API.Models.IntegrationAPI.Shift shift, string uniqueId, string kronoUserId) { var createNewShiftMappingEntityProps = new Dictionary <string, string>() { { "GraphShiftId", shift?.Id }, { "KronosUniqueId", uniqueId }, { "CallingAssembly", Assembly.GetCallingAssembly().GetName().Name }, }; var startDateTime = DateTime.SpecifyKind(shift.SharedShift.StartDateTime, DateTimeKind.Utc); var endDateTime = DateTime.SpecifyKind(shift.SharedShift.EndDateTime, DateTimeKind.Utc); TeamsShiftMappingEntity shiftMappingEntity = new TeamsShiftMappingEntity { AadUserId = shift.UserId, KronosUniqueId = uniqueId, KronosPersonNumber = kronoUserId, ShiftStartDate = startDateTime, ShiftEndDate = endDateTime, }; this.telemetryClient.TrackTrace("Creating new shift mapping entity.", createNewShiftMappingEntityProps); return(shiftMappingEntity); }
/// <summary> /// Adds the shift to Kronos and the database. /// </summary> /// <param name="shift">The shift to add.</param> /// <param name="user">The user the shift is for.</param> /// <param name="mappedTeam">The team the user is in.</param> /// <returns>A response for teams.</returns> public async Task <ShiftsIntegResponse> CreateShiftInKronosAsync(ShiftsShift shift, AllUserMappingEntity user, TeamToDepartmentJobMappingEntity mappedTeam) { // The connector does not support drafting entities as it is not possible to draft shifts in Kronos. // Likewise there is no share schedule WFI call. if (shift.DraftShift != null) { return(ResponseHelper.CreateBadResponse(shift.Id, error: "Creating a shift as a draft is not supported for your team in Teams. Please publish changes directly using the 'Share' button.")); } if (shift.SharedShift == null) { return(ResponseHelper.CreateBadResponse(shift.Id, error: "An unexpected error occured. Could not create the shift.")); } if (shift.SharedShift.Activities.Any()) { return(ResponseHelper.CreateBadResponse(shift.Id, error: "Adding activities to shifts is not supported for your team in Teams. Remove all activities and try sharing again.")); } if (user.ErrorIfNull(shift.Id, "User could not be found.", out var response)) { return(response); } var allRequiredConfigurations = await this.utility.GetAllConfigurationsAsync().ConfigureAwait(false); if ((allRequiredConfigurations?.IsAllSetUpExists).ErrorIfNull(shift.Id, "App configuration incorrect.", out response)) { return(response); } var kronosStartDateTime = this.utility.UTCToKronosTimeZone(shift.SharedShift.StartDateTime, mappedTeam.KronosTimeZone); var kronosEndDateTime = this.utility.UTCToKronosTimeZone(shift.SharedShift.EndDateTime, mappedTeam.KronosTimeZone); var commentTimeStamp = this.utility.UTCToKronosTimeZone(DateTime.UtcNow, mappedTeam.KronosTimeZone).ToString(CultureInfo.InvariantCulture); var comments = XmlHelper.GenerateKronosComments(shift.SharedShift.Notes, this.appSettings.ShiftNotesCommentText, commentTimeStamp); var creationResponse = await this.shiftsActivity.CreateShift( new Uri(allRequiredConfigurations.WfmEndPoint), allRequiredConfigurations.KronosSession, this.utility.FormatDateForKronos(kronosStartDateTime), this.utility.FormatDateForKronos(kronosEndDateTime), kronosEndDateTime.Day > kronosStartDateTime.Day, Utility.OrgJobPathKronosConversion(user.PartitionKey), user.RowKey, kronosStartDateTime.TimeOfDay.ToString(), kronosEndDateTime.TimeOfDay.ToString(), comments).ConfigureAwait(false); if (creationResponse.Status != Success) { return(ResponseHelper.CreateBadResponse(shift.Id, error: "Shift was not created successfully in Kronos.")); } var monthPartitionKey = Utility.GetMonthPartition(this.utility.FormatDateForKronos(kronosStartDateTime), this.utility.FormatDateForKronos(kronosEndDateTime)); await this.CreateAndStoreShiftMapping(shift, user, mappedTeam, monthPartitionKey).ConfigureAwait(false); return(ResponseHelper.CreateSuccessfulResponse(shift.Id)); }
/// <summary> /// This method will cause the thread to wait allowing us to retrun a success response /// for the delete WFI request. It will then share the changes. /// </summary> /// <param name="shift">The shift we want to share.</param> /// <param name="mappedTeam">The team details of the schedule we want to share.</param> /// <param name="allRequiredConfigurations">The required configuration.</param> /// <returns>A unit of execution.</returns> private async Task ShareScheduleAfterShiftDeletion(ShiftsShift shift, TeamToDepartmentJobMappingEntity mappedTeam, IntegrationApi.SetupDetails allRequiredConfigurations) { // We want to wait so that there is time to respond a success to the WFI request // meaning the shift will be deleted in Teams. Thread.Sleep(int.Parse(appSettings.AutoShareScheduleWaitTime)); // We now want to share the schedule between the start and end time of the deleted shift. await this.graphUtility.ShareSchedule( allRequiredConfigurations.GraphConfigurationDetails, mappedTeam.TeamId, shift.SharedShift.StartDateTime, shift.SharedShift.EndDateTime, false).ConfigureAwait(false); }
/// <summary> /// Deletes the shift from Kronos and the database. /// </summary> /// <param name="shift">The shift to remove.</param> /// <param name="user">The user the shift is for.</param> /// <param name="mappedTeam">The team the user is in.</param> /// <returns>A response for teams.</returns> public async Task <ShiftsIntegResponse> DeleteShiftInKronosAsync(ShiftsShift shift, AllUserMappingEntity user, TeamToDepartmentJobMappingEntity mappedTeam) { if (shift.SharedShift == null) { return(ResponseHelper.CreateBadResponse(shift.Id, error: "An unexpected error occured. Could not delete the shift.")); } if (user.ErrorIfNull(shift.Id, "User could not be found.", out var response)) { return(response); } var allRequiredConfigurations = await this.utility.GetAllConfigurationsAsync().ConfigureAwait(false); if ((allRequiredConfigurations?.IsAllSetUpExists == false).ErrorIfNull(shift.Id, "App configuration incorrect.", out response)) { return(response); } // Convert to Kronos local time. var kronosStartDateTime = this.utility.UTCToKronosTimeZone(shift.SharedShift.StartDateTime, mappedTeam.KronosTimeZone); var kronosEndDateTime = this.utility.UTCToKronosTimeZone(shift.SharedShift.EndDateTime, mappedTeam.KronosTimeZone); var deletionResponse = await this.shiftsActivity.DeleteShift( new Uri(allRequiredConfigurations.WfmEndPoint), allRequiredConfigurations.KronosSession, this.utility.FormatDateForKronos(kronosStartDateTime), this.utility.FormatDateForKronos(kronosEndDateTime), kronosEndDateTime.Day > kronosStartDateTime.Day, Utility.OrgJobPathKronosConversion(user.PartitionKey), user.RowKey, kronosStartDateTime.TimeOfDay.ToString(), kronosEndDateTime.TimeOfDay.ToString()).ConfigureAwait(false); if (deletionResponse.Status != Success) { return(ResponseHelper.CreateBadResponse(shift.Id, error: "Shift was not successfully removed from Kronos.")); } await this.DeleteShiftMapping(shift).ConfigureAwait(false); #pragma warning disable CS4014 // We do not want to await this call as we need the shift to be deleted in Teams before sharing the schedule. Task.Run(() => this.ShareScheduleAfterShiftDeletion(shift, mappedTeam, allRequiredConfigurations)); #pragma warning restore CS4014 return(ResponseHelper.CreateSuccessfulResponse(shift.Id)); }
/// <summary> /// Removes a shift mapping entity. /// </summary> /// <param name="shift">A shift from Shifts.</param> /// <returns>A task.</returns> private async Task DeleteShiftMapping(ShiftsShift shift) { var shiftMappingEntity = await this.shiftMappingEntityProvider.GetShiftMappingEntityByRowKeyAsync(shift.Id).ConfigureAwait(false); await this.shiftMappingEntityProvider.DeleteOrphanDataFromShiftMappingAsync(shiftMappingEntity).ConfigureAwait(false); }
/// <summary> /// Edits a shift in Kronos and updates the database. /// </summary> /// <param name="editedShift">The shift to edit.</param> /// <param name="user">The user the shift is for.</param> /// <param name="mappedTeam">The team the user is in.</param> /// <returns>A response for teams.</returns> public async Task <ShiftsIntegResponse> EditShiftInKronosAsync(ShiftsShift editedShift, AllUserMappingEntity user, TeamToDepartmentJobMappingEntity mappedTeam) { // The connector does not support drafting entities as it is not possible to draft shifts in Kronos. // Likewise there is no share schedule WFI call. if (editedShift.DraftShift != null) { return(ResponseHelper.CreateBadResponse(editedShift.Id, error: "Editing a shift as a draft is not supported for your team in Teams. Please publish changes directly using the 'Share' button.")); } if (editedShift.SharedShift == null) { return(ResponseHelper.CreateBadResponse(editedShift.Id, error: "An unexpected error occured. Could not edit the shift.")); } // We use the display name to indicate shift transfers. As we cannot support editing shifts // with a transfer we block edits on shifts containing the transfer string. if (editedShift.SharedShift.DisplayName.Contains(appSettings.TransferredShiftDisplayName)) { return(ResponseHelper.CreateBadResponse(editedShift.Id, error: "You can't edit a shift that includes a shift transfer. Please make your changes in Kronos")); } // We do not support editing activities in Teamsand cannot support editing shift transfers // therefore we only expect activities with the regular segment type. Anything else means the // manager has modified or added an activity. var invalidActivities = editedShift.SharedShift.Activities.Where(x => x.DisplayName != ApiConstants.RegularSegmentType); if (invalidActivities.Any()) { return(ResponseHelper.CreateBadResponse(editedShift.Id, error: "Editing shift activities is not supported for your team in Teams.")); } var allRequiredConfigurations = await this.utility.GetAllConfigurationsAsync().ConfigureAwait(false); if ((allRequiredConfigurations?.IsAllSetUpExists).ErrorIfNull(editedShift.Id, "App configuration incorrect.", out var response)) { return(response); } // We need to get all other shifts the employee works that day. var kronosStartDateTime = this.utility.UTCToKronosTimeZone(editedShift.SharedShift.StartDateTime, mappedTeam.KronosTimeZone); var kronosEndDateTime = this.utility.UTCToKronosTimeZone(editedShift.SharedShift.EndDateTime, mappedTeam.KronosTimeZone); var monthPartitionKey = Utility.GetMonthPartition(this.utility.FormatDateForKronos(kronosStartDateTime), this.utility.FormatDateForKronos(kronosEndDateTime)); var shiftToReplace = await this.shiftMappingEntityProvider.GetShiftMappingEntityByRowKeyAsync(editedShift.Id).ConfigureAwait(false); var shiftToReplaceStartDateTime = this.utility.UTCToKronosTimeZone(shiftToReplace.ShiftStartDate, mappedTeam.KronosTimeZone); var shiftToReplaceEndDateTime = this.utility.UTCToKronosTimeZone(shiftToReplace.ShiftEndDate, mappedTeam.KronosTimeZone); var commentTimeStamp = this.utility.UTCToKronosTimeZone(DateTime.UtcNow, mappedTeam.KronosTimeZone).ToString(CultureInfo.InvariantCulture); var shiftComments = XmlHelper.GenerateEditedShiftKronosComments(editedShift.SharedShift.Notes, this.appSettings.ShiftNotesCommentText, commentTimeStamp); var editResponse = await this.shiftsActivity.EditShift( new Uri(allRequiredConfigurations.WfmEndPoint), allRequiredConfigurations.KronosSession, this.utility.FormatDateForKronos(kronosStartDateTime), this.utility.FormatDateForKronos(kronosEndDateTime), kronosEndDateTime.Day > kronosStartDateTime.Day, Utility.OrgJobPathKronosConversion(user.PartitionKey), user.RowKey, kronosStartDateTime.TimeOfDay.ToString(), kronosEndDateTime.TimeOfDay.ToString(), this.utility.FormatDateForKronos(shiftToReplaceStartDateTime), this.utility.FormatDateForKronos(shiftToReplaceEndDateTime), shiftToReplaceStartDateTime.TimeOfDay.ToString(), shiftToReplaceEndDateTime.TimeOfDay.ToString(), shiftComments).ConfigureAwait(false); if (editResponse.Status != Success) { return(ResponseHelper.CreateBadResponse(editedShift.Id, error: "Shift could not be edited in Kronos.")); } await this.DeleteShiftMapping(editedShift).ConfigureAwait(false); await this.CreateAndStoreShiftMapping(editedShift, user, mappedTeam, monthPartitionKey).ConfigureAwait(false); return(ResponseHelper.CreateSuccessfulResponse(editedShift.Id)); }