using MesaFabApproval.Client.Pages.Components; using MesaFabApproval.Client.Services; using MesaFabApproval.Shared.Models; using MesaFabApproval.Shared.Utilities; using Microsoft.AspNetCore.Components; using Microsoft.Extensions.Caching.Memory; using MudBlazor; using System.Text; namespace MesaFabApproval.Client.Pages; public partial class PCRBSingle { [Inject] ISnackbar snackbar { get; set; } [Inject] IPCRBService pcrbService { get; set; } [Inject] IUserService userService { get; set; } [Inject] IMemoryCache cache { get; set; } [Inject] IConfiguration config { get; set; } [Inject] IDialogService dialogService { get; set; } [Inject] IApprovalService approvalService { get; set; } [Inject] NavigationManager navigationManager { get; set; } [Inject] MesaFabApprovalAuthStateProvider authStateProvider { get; set; } [Parameter] public string planNumber { get; set; } = ""; private int planNumberInt = 0; private PCRB pcrb = null; private IEnumerable approvals = new List(); private IEnumerable attendees = new List(); private IEnumerable attachments = new List(); private IEnumerable actionItems = new List(); private IEnumerable pcr3Documents = new List(); private IEnumerable followUpComments = new List(); private DateTime? followUpDate; private IEnumerable qualityApproverUserIds = new List(); private IEnumerable allActiveUsers = new List(); private User selectedOwner = null; private bool userIsQA = false; private bool processing = false; private bool saveInProcess = false; private bool deleteInProcess = false; private bool submitInProcess = false; private bool attachmentUploadInProcess = false; private bool deleteAttachmentInProcess = false; private bool followUpSubmitInProcess = false; private bool followUpApproveInProcess = false; private bool followUpDenyInProcess = false; protected override async Task OnParametersSetAsync() { processing = true; try { cache.Set("redirectUrl", $"pcrb/{planNumber}"); allActiveUsers = await userService.GetAllActiveUsers(); if (qualityApproverUserIds.Count() == 0) qualityApproverUserIds = await GetQualityApproverUserIds(); userIsQA = qualityApproverUserIds.Contains(authStateProvider.CurrentUser?.UserID ?? -1); if (!string.IsNullOrWhiteSpace(planNumber) && Int32.TryParse(planNumber, out planNumberInt)) { pcrb = await pcrbService.GetPCRBByPlanNumber(planNumberInt, false); approvals = await approvalService.GetApprovalsForIssueId(planNumberInt, false); attendees = await pcrbService.GetAttendeesByPlanNumber(planNumberInt, true); attachments = await pcrbService.GetAttachmentsByPlanNumber(planNumberInt, false); actionItems = await pcrbService.GetActionItemsForPlanNumber(planNumberInt, false); pcr3Documents = await pcrbService.GetPCR3DocumentsForPlanNumber(planNumberInt, false); followUpComments = await pcrbService.GetFollowUpCommentsByPlanNumber(planNumberInt, false); if (followUpDate is null) followUpDate = pcrb.FollowUps.Count() > 0 ? pcrb.FollowUps.First().FollowUpDate : DateTimeUtilities.MAX_DT; List createPCR3DocumentTasks = new(); if (pcr3Documents.Count() <= 0) { List docTypes = new() { "FMEA", "SOPs", "Work Instructions", "Forms/OCAPs", "OpenInsight", "SPC Charts", "Spare Parts Addition", "Metrology", "Safety (system/documents)", "Other" }; foreach (string docType in docTypes) { PCR3Document document = new() { PlanNumber = planNumberInt, DocType = docType }; createPCR3DocumentTasks.Add(pcrbService.CreatePCR3Document(document)); } } List generateAttendeesTasks = new(); if (attendees.Count() == 0) { for (int stepNo = 1; stepNo < 4; stepNo++) { generateAttendeesTasks.Add(GenerateAttendeesForStep(stepNo)); } } List generateApprovalsTasks = new(); if (approvals.Count() == 0) { for (int stepNo = 1; stepNo < 4; stepNo++) { generateApprovalsTasks.Add(GenerateApprovalsForStep(stepNo)); } } await Task.WhenAll(createPCR3DocumentTasks); pcr3Documents = await pcrbService.GetPCR3DocumentsForPlanNumber(planNumberInt, true); await Task.WhenAll(generateAttendeesTasks); attendees = await pcrbService.GetAttendeesByPlanNumber(planNumberInt, true); await Task.WhenAll(generateApprovalsTasks); approvals = await approvalService.GetApprovalsForIssueId(planNumberInt, true); if (pcrb.OwnerID > 0) selectedOwner = await userService.GetUserByUserId(pcrb.OwnerID); if (pcrb.CurrentStep > (int)PCRB.StagesEnum.Draft && pcrb.CurrentStep < (int)PCRB.StagesEnum.Complete) { bool stageHasAdvanced = false; for (int stage = pcrb.CurrentStep; stage < (int)PCRB.StagesEnum.Complete; stage++) { int current_stage = stage; if (pcrb.CurrentStep == current_stage) { IEnumerable currentStageApprovals = approvals.Where(a => a.Step == current_stage); int currentStagePendingApprovalsCount = currentStageApprovals.Where(a => a.ItemStatus == 0).Count(); int currentStageApprovedApprovalsCount = currentStageApprovals.Where(a => a.ItemStatus == 1).Count(); bool currentStageApproved = currentStageApprovedApprovalsCount >= 3 && currentStagePendingApprovalsCount == 0; if (currentStageApproved) { if (pcrb.CurrentStep == (int)PCRB.StagesEnum.PCR3) { int openActionItemCount = actionItems.Where(a => a.ClosedByID == 0).Count(); int openAffectedDocumentsCount = pcr3Documents.Where(d => d.CompletedByID == 0).Count(); if (openActionItemCount == 0 && openAffectedDocumentsCount == 0) { pcrb.CurrentStep++; stageHasAdvanced = true; } } else { pcrb.CurrentStep++; stageHasAdvanced = true; } } } } if (stageHasAdvanced) { if (pcrb.CurrentStep == (int)PCRB.StagesEnum.Complete) { pcrb.ClosedDate = DateTime.Now; string message = $"PCRB# {pcrb.PlanNumber} - {pcrb.Title} is complete"; PCRBNotification notification = new() { PCRB = pcrb, Message = message }; await pcrbService.NotifyOriginator(notification); } await pcrbService.UpdatePCRB(pcrb); pcrb = await pcrbService.GetPCRBByPlanNumber(pcrb.PlanNumber, true); StateHasChanged(); await OnParametersSetAsync(); } } if (pcrb.CurrentStep == (int)PCRB.StagesEnum.Complete && pcrb.FollowUps.Count() == 0) { PCRBFollowUp followUp = new() { PlanNumber = pcrb.PlanNumber, Step = (int)PCRB.StagesEnum.FollowUp, FollowUpDate = pcrb.ClosedDate.AddMonths(6) }; await pcrbService.CreateFollowUp(followUp); pcrb = await pcrbService.GetPCRBByPlanNumber(pcrb.PlanNumber, true); if (pcrb.FollowUps.Count() == 0) throw new Exception("unable to create follow up"); StateHasChanged(); await OnParametersSetAsync(); } if (pcrb.CurrentStep == (int)PCRB.StagesEnum.Complete && pcrb.FollowUps.Count() > 0 && DateTime.Now >= pcrb.FollowUps.First().FollowUpDate.AddDays(-15)) { pcrb.CurrentStep = (int)PCRB.StagesEnum.FollowUp; await pcrbService.UpdatePCRB(pcrb); pcrb = await pcrbService.GetPCRBByPlanNumber(pcrb.PlanNumber, true); StateHasChanged(); await OnParametersSetAsync(); } } else { int ownerID = 0; string ownerName = string.Empty; if (authStateProvider.CurrentUser is not null) { selectedOwner = authStateProvider.CurrentUser; ownerID = authStateProvider.CurrentUser.UserID; ownerName = authStateProvider.CurrentUser.GetFullName(); } pcrb = new() { OwnerID = ownerID, OwnerName = ownerName, CurrentStep = (int)PCRB.StagesEnum.Draft }; } processing = false; } catch (Exception ex) { processing = false; snackbar.Add(ex.Message, Severity.Error); } } private Func UserToNameConverter = u => u is null ? string.Empty : u.GetFullName(); private void ReturnToAllPcrbs() { cache.Set("redirectUrl", $"pcrb/all"); navigationManager.NavigateTo("pcrb/all"); } private IEnumerable GetIncompleteFields() { List incompleteFields = new(); if (string.IsNullOrWhiteSpace(pcrb.Title)) incompleteFields.Add("Title"); if (selectedOwner is null || pcrb.OwnerID <= 0 || string.IsNullOrWhiteSpace(pcrb.OwnerName)) incompleteFields.Add("Originator"); if (string.IsNullOrWhiteSpace(pcrb.ChangeLevel)) incompleteFields.Add("Change Level"); if (string.IsNullOrWhiteSpace(pcrb.ChangeDescription)) incompleteFields.Add("Description of Change"); if (string.IsNullOrWhiteSpace(pcrb.ReasonForChange)) incompleteFields.Add("Reason For Change"); return incompleteFields; } private bool PCRBReadyToSubmit(int step) { bool readyToSubmit = GetIncompleteFields().Count() <= 0; readyToSubmit = readyToSubmit && pcrb.CurrentStep > (int)PCRB.StagesEnum.Draft; readyToSubmit = readyToSubmit && attachments.Where(a => a.Step == step).Count() > 0; return readyToSubmit; } private bool UserIsApprover() { bool userIsApprover = approvals.Where(a => authStateProvider.CurrentUser is not null && a.UserID == authStateProvider.CurrentUser.UserID && a.ItemStatus == 0).Count() > 0; return userIsApprover; } private async void SavePCRB() { if (!saveInProcess) { saveInProcess = true; try { if (pcrb is null) throw new Exception("PCRB cannot be null"); int initialPlanNumber = pcrb.PlanNumber; pcrb.OwnerID = selectedOwner.UserID; pcrb.OwnerName = selectedOwner.GetFullName(); if (pcrb.CurrentStep == (int)PCRB.StagesEnum.Draft && GetIncompleteFields().Count() <= 0) pcrb.CurrentStep++; if (initialPlanNumber <= 0) { await pcrbService.CreateNewPCRB(pcrb); } else { await pcrbService.UpdatePCRB(pcrb); } pcrb = await pcrbService.GetPCRBByTitle(pcrb.Title, true); cache.Set("redirectUrl", $"pcrb/{pcrb.PlanNumber}"); saveInProcess = false; StateHasChanged(); snackbar.Add($"PCRB {pcrb.PlanNumber} successfully saved", Severity.Success); if (initialPlanNumber <= 0) navigationManager.NavigateTo($"pcrb/{pcrb.PlanNumber}"); } catch (Exception ex) { saveInProcess = false; snackbar.Add($"Unable to save PCRB, because {ex.Message}", Severity.Error); } } } private async Task DeletePCRB() { if (!deleteInProcess) { deleteInProcess = true; try { bool? result = await dialogService.ShowMessageBox( "Warning", $"Are you sure you want to delete PCRB# {pcrb.PlanNumber}?", yesText: "Yes", noText: "No" ); if (result == true) { if (pcrb is null) throw new Exception("PCRB cannot be null"); await pcrbService.DeletePCRB(pcrb.PlanNumber); await pcrbService.GetAllPCRBs(true); deleteInProcess = false; snackbar.Add("PCRB successfully deleted", Severity.Success); cache.Set("redirectUrl", "pcrb/all"); navigationManager.NavigateTo($"pcrb/all"); } } catch (Exception ex) { deleteInProcess = false; snackbar.Add(ex.Message, Severity.Error); } } } private async Task> GetQualityApproverUserIds() { try { HashSet? qualityApproverUserIds = cache.Get>("qualityApproverUserIds"); if (qualityApproverUserIds is null || qualityApproverUserIds.Count() == 0) { qualityApproverUserIds = new(); int roleId = await approvalService.GetRoleIdForRoleName("Module Manager"); if (roleId <= 0) throw new Exception($"could not find Module Manager role ID"); IEnumerable subRoles = await approvalService.GetSubRolesForSubRoleName("MMSubRole", roleId); foreach (SubRole subRole in subRoles) { if (subRole.SubRoleCategoryItem.Equals("Quality", StringComparison.InvariantCultureIgnoreCase)) { IEnumerable subRoleMembers = await approvalService.GetApprovalGroupMembers(subRole.SubRoleID); foreach (User user in subRoleMembers) qualityApproverUserIds.Add(user.UserID); } } string roleName = "QA_PRE_APPROVAL"; string subRoleName = "QA_PRE_APPROVAL"; roleId = await approvalService.GetRoleIdForRoleName(roleName); if (roleId <= 0) throw new Exception($"could not find {roleName} role ID"); subRoles = await approvalService.GetSubRolesForSubRoleName(subRoleName, roleId); foreach (SubRole subRole in subRoles) { IEnumerable subRoleMembers = await approvalService.GetApprovalGroupMembers(subRole.SubRoleID); foreach (User user in subRoleMembers) qualityApproverUserIds.Add(user.UserID); } roleName = "QA_FINAL_APPROVAL"; subRoleName = "QA_FINAL_APPROVAL"; roleId = await approvalService.GetRoleIdForRoleName(roleName); if (roleId <= 0) throw new Exception($"could not find {roleName} role ID"); subRoles = await approvalService.GetSubRolesForSubRoleName(subRoleName, roleId); foreach (SubRole subRole in subRoles) { IEnumerable subRoleMembers = await approvalService.GetApprovalGroupMembers(subRole.SubRoleID); foreach (User user in subRoleMembers) qualityApproverUserIds.Add(user.UserID); } cache.Set("qualityApproverUserIds", qualityApproverUserIds); } return qualityApproverUserIds; } catch (Exception ex) { snackbar.Add($"Unable to get Quality approvers, because {ex.Message}", Severity.Error); return qualityApproverUserIds; } } private async Task GenerateApprovalsForStep(int step) { try { if (pcrb is null) throw new Exception("PCRB cannot be null"); int roleId = await approvalService.GetRoleIdForRoleName("QA_PRE_APPROVAL"); if (roleId <= 0) throw new Exception($"could not find QA_PRE_APPROVAL role ID"); IEnumerable subRoles = await approvalService.GetSubRolesForSubRoleName("QA_PRE_APPROVAL", roleId); foreach (SubRole subRole in subRoles) { IEnumerable members = await approvalService.GetApprovalGroupMembers(subRole.SubRoleID); foreach (User member in members) { Approval approval = new() { IssueID = pcrb.PlanNumber, RoleName = subRole.SubRoleCategoryItem, SubRole = subRole.SubRoleName, UserID = member.UserID, SubRoleID = subRole.SubRoleID, AssignedDate = DateTimeUtilities.MIN_DT, Step = step }; await approvalService.CreateApproval(approval); } } roleId = await approvalService.GetRoleIdForRoleName("Module Manager"); if (roleId <= 0) throw new Exception($"could not find Module Manager role ID"); subRoles = await approvalService.GetSubRolesForSubRoleName("MMSubRole", roleId); HashSet subRoleCategoryItems = new() { "Si Production", "Si Engineering", "Quality" }; foreach (SubRole subRole in subRoles) { if (subRoleCategoryItems.Contains(subRole.SubRoleCategoryItem)) { IEnumerable subRoleMembers = await approvalService.GetApprovalGroupMembers(subRole.SubRoleID); foreach (User member in subRoleMembers) { Approval approval = new() { IssueID = pcrb.PlanNumber, RoleName = subRole.SubRoleCategoryItem, SubRole = subRole.SubRoleName, UserID = member.UserID, SubRoleID = subRole.SubRoleID, AssignedDate = DateTimeUtilities.MIN_DT, Step = step }; await approvalService.CreateApproval(approval); } } } } catch (Exception ex) { snackbar.Add($"Unable to generate approvals for step {step}, because {ex.Message}", Severity.Error); } } private async Task SubmitForApproval(int step) { if (!submitInProcess) { try { submitInProcess = true; if (pcrb is null) throw new Exception("PCRB cannot be null"); if (!PCRBReadyToSubmit(step)) throw new Exception($"PCR {step} not ready to submit"); List attendeeTasks = new(); foreach (PCRBAttendee attendee in attendees.Where(a => a.Step == step)) { attendeeTasks.Add(pcrbService.UpdateAttendee(attendee)); } await Task.WhenAll(attendeeTasks); List notifyResponsiblePersonsTasks = new(); foreach (PCRBActionItem actionItem in actionItems.Where(a => a.Step == step && a.NotifyDate <= DateTimeUtilities.MIN_DT && a.ClosedStatus == false)) { StringBuilder messageBuilder = new(); messageBuilder.Append($"PCRB# {pcrb.PlanNumber} [{pcrb.Title}] PCR{step} has an action item for you to complete. "); messageBuilder.Append($"The action is {actionItem.Name}."); PCRBActionItemNotification notification = new() { PCRB = pcrb, ActionItem = actionItem, Message = messageBuilder.ToString() }; actionItem.NotifyDate = DateTime.Now; notifyResponsiblePersonsTasks.Add(pcrbService.NotifyResponsiblePerson(notification)); notifyResponsiblePersonsTasks.Add(pcrbService.UpdateActionItem(actionItem)); } await Task.WhenAll(notifyResponsiblePersonsTasks); IEnumerable currentStageApprovals = approvals.Where(a => a.Step == step); IEnumerable currentStageManagerApprovals = currentStageApprovals.Where(a => !a.SubRoleCategoryItem.Equals("QA Pre Approver")); int currentStageUnsubmittedApprovalCount = currentStageManagerApprovals.Where(a => a.AssignedDate <= DateTimeUtilities.MIN_DT).Count(); int currentStagePendingApprovalsCount = currentStageManagerApprovals.Where(a => a.ItemStatus == 0 && a.AssignedDate > DateTimeUtilities.MIN_DT).Count(); int currentStageApprovedApprovalsCount = currentStageManagerApprovals.Where(a => a.ItemStatus == 1).Count(); int currentStageDeniedApprovalsCount = currentStageManagerApprovals.Where(a => a.ItemStatus == -1).Count(); Approval? latestQaPreApproval = currentStageApprovals .Where(a => a.SubRoleCategoryItem.Equals("QA Pre Approver")) .OrderByDescending(a => a.AssignedDate) .FirstOrDefault() ?? throw new Exception("QA pre approval not found"); bool qaPreApprovalDenied = latestQaPreApproval.ItemStatus == -1; if (qaPreApprovalDenied && currentStageUnsubmittedApprovalCount >= 3) { Approval newApproval = new() { IssueID = latestQaPreApproval.IssueID, RoleName = latestQaPreApproval.RoleName, SubRole = latestQaPreApproval.SubRole, UserID = latestQaPreApproval.UserID, SubRoleID = latestQaPreApproval.SubRoleID, AssignedDate = DateTime.Now, Step = latestQaPreApproval.Step }; await approvalService.CreateApproval(newApproval); } else if (currentStageDeniedApprovalsCount > (currentStageUnsubmittedApprovalCount + currentStagePendingApprovalsCount + currentStageApprovedApprovalsCount)) { HashSet copiedApprovals = new(); List createCopiedApprovalsTasks = new(); foreach (Approval oldApproval in currentStageApprovals) { if (!copiedApprovals.Contains($"{oldApproval.RoleName}{oldApproval.UserID}")) { DateTime assignedDate = DateTimeUtilities.MIN_DT; if (oldApproval.RoleName.Equals("QA Pre Approver")) assignedDate = DateTime.Now; Approval newApproval = new() { IssueID = oldApproval.IssueID, RoleName = oldApproval.RoleName, SubRole = oldApproval.SubRole, UserID = oldApproval.UserID, SubRoleID = oldApproval.SubRoleID, AssignedDate = assignedDate, Step = oldApproval.Step }; createCopiedApprovalsTasks.Add(approvalService.CreateApproval(newApproval)); copiedApprovals.Add($"{oldApproval.RoleName}{oldApproval.UserID}"); } } await Task.WhenAll(createCopiedApprovalsTasks); } else { Approval? unassignedQaPreApproval = currentStageApprovals.Where(a => a.SubRoleCategoryItem.Equals("QA Pre Approver") && a.AssignedDate <= DateTimeUtilities.MIN_DT).FirstOrDefault() ?? throw new Exception("unassigned QA pre approval not found"); unassignedQaPreApproval.AssignedDate = DateTime.Now; await approvalService.UpdateApproval(unassignedQaPreApproval); } approvals = await approvalService.GetApprovalsForIssueId(pcrb.PlanNumber, true); await pcrbService.NotifyNewApprovals(pcrb); if (pcrb.CurrentStep == (int)PCRB.StagesEnum.PCR1) { pcrb.InsertTimeStamp = DateTime.Now; await pcrbService.UpdatePCRB(pcrb); pcrb = await pcrbService.GetPCRBByPlanNumber(pcrb.PlanNumber, true); } submitInProcess = false; snackbar.Add("PCRB successfully submitted for approval", Severity.Success); } catch (Exception ex) { submitInProcess = false; snackbar.Add($"Unable to submit for approval, because {ex.Message}", Severity.Error); } StateHasChanged(); await OnParametersSetAsync(); } } private async Task ApprovePCR(int step) { if (!processing) { try { processing = true; if (step <= 0 || step > 3) throw new Exception($"{step} is not a valid PCR#"); if (pcrb is null) throw new Exception("PCRB cannot be null"); if (pcrb.ClosedDate < DateTimeUtilities.MAX_DT) throw new Exception("cannot approve a complete PCRB"); if (authStateProvider.CurrentUser is null) { await authStateProvider.Logout(); navigationManager.NavigateTo("login"); return; } IEnumerable activeApprovalsForUser = approvals.Where(a => a.UserID == authStateProvider.CurrentUser.UserID && a.AssignedDate > DateTimeUtilities.MIN_DT && a.ItemStatus == 0 && a.Step == step); if (activeApprovalsForUser.Count() <= 0) throw new Exception($"you have no active approvals for PCR{step}"); string? comments = ""; DialogParameters parameters = new DialogParameters { { x => x.comments, comments } }; var dialog = dialogService.Show($"Approval Comments", parameters); var result = await dialog.Result; if (result.Canceled) throw new Exception("you must provide approval comments"); comments = result.Data.ToString(); foreach (Approval approval in activeApprovalsForUser) { approval.CompletedDate = DateTime.Now; approval.Comments = comments is null ? "" : comments; approval.ItemStatus = 1; await approvalService.UpdateApproval(approval); } approvals = await approvalService.GetApprovalsForIssueId(pcrb.PlanNumber, false); IEnumerable unassignedApprovals = approvals.Where(a => a.Step == step && a.AssignedDate <= DateTimeUtilities.MIN_DT); if (unassignedApprovals.Count() > 0) { List assignmentTasks = new(); foreach (Approval approval in unassignedApprovals) { approval.AssignedDate = DateTime.Now; assignmentTasks.Add(approvalService.UpdateApproval(approval)); } await Task.WhenAll(assignmentTasks); approvals = await approvalService.GetApprovalsForIssueId(pcrb.PlanNumber, false); await pcrbService.NotifyNewApprovals(pcrb); } IEnumerable remainingActiveApprovals = approvals.Where(a => a.Step == step && a.ItemStatus == 0); if (remainingActiveApprovals.Count() <= 0) { StringBuilder messageBuilder = new(); messageBuilder.Append($"PCRB# {pcrb.PlanNumber} - {pcrb.Title} - PCR{step} has been approved."); PCRBNotification notification = new() { PCRB = pcrb, Message = messageBuilder.ToString() }; await pcrbService.NotifyOriginator(notification); } processing = false; snackbar.Add($"PCR{step} successfully approved", Severity.Success); } catch (Exception ex) { processing = false; snackbar.Add($"Unable to approve PCR{step}, because {ex.Message}", Severity.Error); } StateHasChanged(); await OnParametersSetAsync(); } } private async Task DenyPCR(int step) { if (!processing) { try { processing = true; if (step <= 0 || step > 3) throw new Exception($"{step} is not a valid PCR#"); if (pcrb is null) throw new Exception("PCRB cannot be null"); if (pcrb.ClosedDate < DateTimeUtilities.MAX_DT) throw new Exception("cannot deny a complete PCRB"); if (authStateProvider.CurrentUser is null) { await authStateProvider.Logout(); navigationManager.NavigateTo("login"); return; } IEnumerable activeApprovalsForUser = approvals.Where(a => a.UserID == authStateProvider.CurrentUser.UserID && a.AssignedDate > DateTimeUtilities.MIN_DT && a.ItemStatus == 0 && a.Step == step); if (activeApprovalsForUser.Count() <= 0) throw new Exception("you have no active approvals"); string? comments = ""; DialogParameters parameters = new DialogParameters { { x => x.comments, comments } }; var dialog = dialogService.Show($"Denial Comments", parameters); var result = await dialog.Result; if (result.Canceled) throw new Exception("you must provide a comment"); comments = result.Data.ToString(); IEnumerable outstandingActiveApprovalsForStep = approvals.Where(a => a.Step == step && a.ItemStatus == 0 && a.AssignedDate > DateTimeUtilities.MIN_DT); foreach (Approval approval in outstandingActiveApprovalsForStep) { approval.CompletedDate = DateTime.Now; approval.Comments = comments is null ? "" : comments; approval.ItemStatus = -1; await approvalService.UpdateApproval(approval); } approvals = await approvalService.GetApprovalsForIssueId(pcrb.PlanNumber, false); if (step == 1) { pcrb.InsertTimeStamp = DateTimeUtilities.MIN_DT; await pcrbService.UpdatePCRB(pcrb); } StringBuilder messageBuilder = new(); messageBuilder.Append($"PCRB# {pcrb.PlanNumber} - {pcrb.Title} - PCR{step} has been denied "); messageBuilder.Append($"by {authStateProvider.CurrentUser.GetFullName()}. "); messageBuilder.AppendLine(""); messageBuilder.Append($"Comments: {comments}"); PCRBNotification notification = new() { PCRB = pcrb, Message = messageBuilder.ToString() }; await pcrbService.NotifyOriginator(notification); processing = false; snackbar.Add($"PCR{step} successfully denied", Severity.Success); } catch (Exception ex) { processing = false; snackbar.Add($"Unable to deny PCR{step}, because {ex.Message}", Severity.Error); } StateHasChanged(); await OnParametersSetAsync(); } } private async Task UploadAttachment(int step) { if (!attachmentUploadInProcess) { attachmentUploadInProcess = true; try { if (step <= 0) throw new ArgumentException($"{step} is not a valid PCRB stage#"); DialogParameters parameters = new DialogParameters { { x => x.planNumber, pcrb.PlanNumber }, { x => x.step, step } }; var dialog = dialogService.Show("Upload Attachment", parameters); var result = await dialog.Result; if (result is not null && !result.Canceled) snackbar.Add("Attachment successfully uploaded", Severity.Success); attachments = await pcrbService.GetAttachmentsByPlanNumber(planNumberInt, true); attachmentUploadInProcess = false; } catch (Exception ex) { attachmentUploadInProcess = false; snackbar.Add($"Unable to upload attachment, because {ex.Message}", Severity.Error); } StateHasChanged(); await OnParametersSetAsync(); } } private async Task DeleteAttachment(PCRBAttachment attachment) { try { processing = true; if (attachment is null) throw new ArgumentNullException("attachment cannot be null"); bool? result = await dialogService.ShowMessageBox( "Warning", $"Are you sure you want to delete PCRB attachment {attachment.FileName}?", yesText: "Yes", noText: "No" ); if (result == true) { await pcrbService.DeleteAttachment(attachment); attachments = await pcrbService.GetAttachmentsByPlanNumber(attachment.PlanNumber, true); } processing = false; snackbar.Add("Attachment successfully deleted", Severity.Success); } catch (Exception ex) { processing = false; snackbar.Add($"Unable to delete attachment, because {ex.Message}", Severity.Error); } StateHasChanged(); await OnParametersSetAsync(); } private async Task CreateNewActionItem(int step) { try { DialogParameters parameters = new DialogParameters { { x => x.planNumber, pcrb.PlanNumber }, { x => x.step, step }, { x => x.allActiveUsers, allActiveUsers } }; var dialog = dialogService.Show("New Action Item", parameters); var result = await dialog.Result; if (result is not null && !result.Canceled) snackbar.Add("Action item successfully created", Severity.Success); actionItems = await pcrbService.GetActionItemsForPlanNumber(pcrb.PlanNumber, true); } catch (Exception ex) { snackbar.Add($"Unable to create new action item, because {ex.Message}", Severity.Error); } StateHasChanged(); await OnParametersSetAsync(); } private async Task CloseAllActionItems(int step) { if (!processing) { try { processing = true; if (authStateProvider.CurrentUser is null) { await authStateProvider.Logout(); navigationManager.NavigateTo("login"); return; } IEnumerable outstandingActionItemsForStep = actionItems.Where(a => a.Step == step && a.ClosedStatus == false); foreach (PCRBActionItem actionItem in outstandingActionItemsForStep) { actionItem.ClosedStatus = true; actionItem.ClosedDate = DateTime.Now; actionItem.ClosedBy = authStateProvider.CurrentUser; actionItem.ClosedByID = authStateProvider.CurrentUser.UserID; await pcrbService.UpdateActionItem(actionItem); } actionItems = await pcrbService.GetActionItemsForPlanNumber(planNumberInt, true); processing = false; snackbar.Add("Successfully approved all action items", Severity.Success); } catch (Exception ex) { processing = false; snackbar.Add($"Unable to approve all action items, because {ex.Message}", Severity.Error); } StateHasChanged(); await OnParametersSetAsync(); } } private async Task UpdateActionItem(PCRBActionItem actionItem) { try { if (actionItem is null) throw new ArgumentNullException("action item cannot be null"); DialogParameters parameters = new DialogParameters { { x => x.planNumber, pcrb.PlanNumber }, { x => x.step, actionItem.Step }, { x => x.allActiveUsers, allActiveUsers }, { x => x.actionItem, actionItem } }; var dialog = dialogService.Show("Update Action Item", parameters); var result = await dialog.Result; if (result is not null && !result.Canceled) snackbar.Add("Action item successfully updated", Severity.Success); actionItems = await pcrbService.GetActionItemsForPlanNumber(actionItem.PlanNumber, true); } catch (Exception ex) { snackbar.Add($"Unable to update action item, because {ex.Message}", Severity.Error); } StateHasChanged(); await OnParametersSetAsync(); } private async Task DeleteActionItem(PCRBActionItem actionItem) { try { if (actionItem is null) throw new ArgumentNullException("action item cannot be null"); bool? result = await dialogService.ShowMessageBox( "Warning", $"Are you sure you want to delete PCRB action item {actionItem.Name}?", yesText: "Yes", noText: "No" ); if (result == true) { await pcrbService.DeleteActionItem(actionItem.ID); actionItems = await pcrbService.GetActionItemsForPlanNumber(pcrb.PlanNumber, true); snackbar.Add("Action item successfully deleted", Severity.Success); } } catch (Exception ex) { snackbar.Add($"Unable to delete action item, because {ex.Message}", Severity.Error); } StateHasChanged(); await OnParametersSetAsync(); } private async Task UpdatePCR3Document(PCR3Document document) { if (!processing) { try { processing = true; if (document is null) throw new Exception("affected document cannot be null"); DialogParameters parameters = new DialogParameters { { x => x.document, document }, }; var dialog = dialogService.Show("Update Affected Document Type", parameters); var result = await dialog.Result; processing = false; if (result is not null && !result.Canceled) snackbar.Add("Affected document successfully updated", Severity.Success); } catch (Exception ex) { processing = false; snackbar.Add($"Unable to update affected document, because {ex.Message}", Severity.Error); } StateHasChanged(); await OnParametersSetAsync(); } } private async Task GenerateAttendeesForStep(int step) { try { if (pcrb is null) throw new Exception("PCRB cannot be null"); int roleId = await approvalService.GetRoleIdForRoleName("PCRB Attendee"); if (roleId <= 0) throw new Exception($"could not find PCRB Attendee role ID"); IEnumerable subRoles = await approvalService.GetSubRolesForSubRoleName("PCRBAttendee", roleId); foreach (SubRole subRole in subRoles) { IEnumerable subRoleMembers = await approvalService.GetApprovalGroupMembers(subRole.SubRoleID); foreach (User member in subRoleMembers) { PCRBAttendee attendee = new() { PlanNumber = pcrb.PlanNumber, AttendeeID = member.UserID, Attendee = member, Step = step }; await pcrbService.CreateNewAttendee(attendee); } } } catch (Exception ex) { snackbar.Add($"Unable to generate attendees for step {step}, because {ex.Message}", Severity.Error); } } private async Task UpdateAttendees(IEnumerable pcrAttendees, int step) { if (!processing) { try { processing = true; List attendeeTasks = new(); foreach (PCRBAttendee attendee in pcrAttendees) { attendeeTasks.Add(pcrbService.UpdateAttendee(attendee)); } await Task.WhenAll(attendeeTasks); processing = false; snackbar.Add($"PCR{step} attendees updated", Severity.Success); } catch (Exception ex) { processing = false; snackbar.Add($"", Severity.Error); } } } private async Task AddAttendee(int step) { if (!processing) { try { processing = true; if (authStateProvider.CurrentUser is null) { await authStateProvider.Logout(); navigationManager.NavigateTo("login"); return; } if (pcrb is null) throw new Exception("PCRB cannot be null"); if (pcrb.PlanNumber == 0) throw new Exception("PCRB was never saved"); User user = authStateProvider.CurrentUser; DialogParameters parameters = new DialogParameters { { x => x.selectedUser, user } }; var dialog = dialogService.Show("Add Attendee", parameters); var result = await dialog.Result; if (result is not null && !result.Canceled && result.Data is not null) { user = (User)result.Data; if (attendees.Where(a => a.AttendeeID == user.UserID).Count() == 0) { PCRBAttendee attendee = new() { PlanNumber = pcrb.PlanNumber, Attendee = user, AttendeeID = user.UserID, Step = step }; await pcrbService.CreateNewAttendee(attendee); } } attendees = await pcrbService.GetAttendeesByPlanNumber(pcrb.PlanNumber, true); processing = false; } catch (Exception ex) { processing = false; snackbar.Add($"Unable to add attendee, because {ex.Message}", Severity.Error); } StateHasChanged(); await OnParametersSetAsync(); } } private async Task MarkAttended(PCRBAttendee attendee) { if (!processing) { try { processing = true; attendee.Attended = !attendee.Attended; await pcrbService.UpdateAttendee(attendee); processing = false; } catch (Exception ex) { processing = false; snackbar.Add($"Unable to mark attendance, because {ex.Message}", Severity.Error); } StateHasChanged(); await OnParametersSetAsync(); } } private async Task MarkAllAttended(int step) { if (!processing) { try { processing = true; List updateAttendeeTasks = new(); foreach (PCRBAttendee attendee in attendees.Where(a => a.Step == step)) { attendee.Attended = true; updateAttendeeTasks.Add(pcrbService.UpdateAttendee(attendee)); } await Task.WhenAll(updateAttendeeTasks); processing = false; } catch (Exception ex) { processing = false; snackbar.Add($"Unable to mark all attended, because {ex.Message}", Severity.Error); } StateHasChanged(); await OnParametersSetAsync(); } } private async Task AddApprover(int step) { try { DialogParameters parameters = new DialogParameters { { x => x.planNumber, pcrb.PlanNumber }, { x => x.step, step } }; var dialog = dialogService.Show("Add Approver", parameters); var result = await dialog.Result; if (result is not null && !result.Canceled) snackbar.Add("Approver successfully added", Severity.Success); approvals = await approvalService.GetApprovalsForIssueId(pcrb.PlanNumber, false); } catch (Exception ex) { snackbar.Add($"Unable to add approver, because {ex.Message}", Severity.Error); } StateHasChanged(); await OnParametersSetAsync(); } private async Task UpdateApproval(Approval approval) { try { if (approval is null) throw new ArgumentNullException("approval cannot be null"); DialogParameters parameters = new DialogParameters { { x => x.planNumber, pcrb.PlanNumber }, { x => x.step, approval.Step }, { x => x.approval, approval } }; var dialog = dialogService.Show("Update Approval", parameters); var result = await dialog.Result; if (result is not null && !result.Canceled) snackbar.Add("Approval successfully updated", Severity.Success); approvals = await approvalService.GetApprovalsForIssueId(pcrb.PlanNumber, true); } catch (Exception ex) { snackbar.Add($"Unable to update approval, because {ex.Message}", Severity.Error); } StateHasChanged(); await OnParametersSetAsync(); } private string SubRoleCategoryItemToJobTitleConverter(string subRoleCategoryItem) { if (string.IsNullOrWhiteSpace(subRoleCategoryItem)) return ""; string jobTitle = subRoleCategoryItem.Replace("Si", ""); if (!jobTitle.Contains("other", StringComparison.InvariantCultureIgnoreCase) && !jobTitle.Contains("qa pre approver", StringComparison.InvariantCultureIgnoreCase)) jobTitle = jobTitle + " Manager"; return jobTitle; } private string GetApprovalStatus(int itemStatus) { if (itemStatus < 0) return "Denied"; if (itemStatus > 0) return "Approved"; return "Pending"; } private async Task UpdateFollowUpDate(DateTime? newFollowUpDate) { if (followUpDate is not null || followUpDate <= DateTimeUtilities.MAX_DT) { try { if (newFollowUpDate is null) throw new Exception("follow up date cannot be null"); if (authStateProvider.CurrentUser is null) { snackbar.Add("You must log in to change the follow up date", Severity.Error); await authStateProvider.Logout(); return; } DateTime oldFollowUpDate = pcrb.FollowUps.First().FollowUpDate; followUpDate = newFollowUpDate; pcrb.FollowUps.First().FollowUpDate = (DateTime)newFollowUpDate; await pcrbService.UpdateFollowUp(pcrb.FollowUps.First()); pcrb = await pcrbService.GetPCRBByPlanNumber(pcrb.PlanNumber, true); string comments = ""; DialogParameters parameters = new DialogParameters { { x => x.comments, comments } }; var dialog = await dialogService.ShowAsync($"Follow Up Date Change Comment", parameters); DialogResult? result = await dialog.Result; if (result is null || result.Canceled || result.Data is null || string.IsNullOrWhiteSpace(result.Data?.ToString())) { followUpDate = oldFollowUpDate; pcrb.FollowUps.First().FollowUpDate = oldFollowUpDate; await pcrbService.UpdateFollowUp(pcrb.FollowUps.First()); pcrb = await pcrbService.GetPCRBByPlanNumber(pcrb.PlanNumber, true); throw new Exception("you must provide a comment"); } comments = result.Data?.ToString() ?? string.Empty; comments = comments.Trim(); StringBuilder commentBuilder = new(); commentBuilder.Append($"Changing follow up date from {oldFollowUpDate.ToString("MM/dd/yyyy")} "); commentBuilder.Append($"to {pcrb.FollowUps.First().FollowUpDate.ToString("MM/dd/yyyy")}. Comments: {comments}"); PCRBFollowUpComment comment = new() { PlanNumber = pcrb.FollowUps.First().PlanNumber, FollowUpID = pcrb.FollowUps.First().ID, Comment = commentBuilder.ToString(), UserID = authStateProvider.CurrentUser.UserID }; await pcrbService.CreateFollowUpComment(comment); DateTime fifteenDaysFromNow = DateTime.Now.AddDays(15); if (pcrb.FollowUps.First().FollowUpDate > fifteenDaysFromNow) { IEnumerable step5Approvals = approvals.Where(a => a.Step == 5 && a.ItemStatus == 0); foreach (Approval approval in step5Approvals) { await approvalService.DeleteApproval(approval.ApprovalID); await approvalService.GetApprovalsForUserId(approval.UserID, true); } approvals = await approvalService.GetApprovalsForIssueId(pcrb.PlanNumber, true); } else if (approvals.Where(a => a.Step == 5 && a.ItemStatus == 0).Count() == 0) { Approval newApproval = new Approval { IssueID = pcrb.PlanNumber, RoleName = "PCRB Owner Follow Up", SubRole = "PCRBOwnerFollowUp", SubRoleCategoryItem = "PCRB Owner Follow Up", UserID = pcrb.OwnerID, SubRoleID = 999, AssignedDate = DateTime.Now, TaskID = pcrb.FollowUps.First().ID, Step = 5, NotifyDate = DateTime.Now }; await approvalService.CreateApproval(newApproval); } commentBuilder.Clear(); commentBuilder.Append($"Effectiveness review date for PCRB# {pcrb.PlanNumber} - {pcrb.Title} has been changed to "); commentBuilder.Append($"{pcrb.FollowUps.First().FollowUpDate.ToString("MM/dd/yyyy")}. "); PCRBNotification notification = new() { Message = commentBuilder.ToString(), Subject = $"[PCRB Effectiveness Review Date Change] {pcrb.PlanNumber} - {pcrb.Title}", PCRB = pcrb, NotifyQaPreApprover = true }; await pcrbService.NotifyOriginator(notification); } catch (Exception ex) { snackbar.Add($"Unable to update follow up date, because {ex.Message}", Severity.Error); } StateHasChanged(); await OnParametersSetAsync(); } } private async Task SubmitFollowUpForApproval() { if (!followUpSubmitInProcess) { try { followUpSubmitInProcess = true; if (pcrb.FollowUps.Count() > 0) { PCRBFollowUp followUp = pcrb.FollowUps.First(); followUp.IsPendingApproval = true; await pcrbService.UpdateFollowUp(followUp); List allSubRoles = new(); int roleId = await approvalService.GetRoleIdForRoleName("Module Manager"); if (roleId <= 0) throw new Exception($"could not find Module Manager role ID"); List qualityMMSubRoles = (await approvalService.GetSubRolesForSubRoleName("MMSubRole", roleId)).ToList(); foreach (SubRole subRole in qualityMMSubRoles) { if (subRole.SubRoleCategoryItem.Equals("Quality")) allSubRoles.Add(subRole); } roleId = await approvalService.GetRoleIdForRoleName("QA_FINAL_APPROVAL"); if (roleId <= 0) throw new Exception($"could not find QA Final Approval role ID"); IEnumerable qaFinalApprovalSubRoles = (await approvalService.GetSubRolesForSubRoleName("QA_FINAL_APPROVAL", roleId)).ToList(); foreach (SubRole subRole in qaFinalApprovalSubRoles) allSubRoles.Add(subRole); foreach (SubRole subRole in allSubRoles) { IEnumerable subRoleMembers = await approvalService.GetApprovalGroupMembers(subRole.SubRoleID); foreach (User member in subRoleMembers) { Approval approval = new() { IssueID = pcrb.PlanNumber, RoleName = subRole.SubRoleCategoryItem, SubRole = subRole.SubRoleName, UserID = member.UserID, SubRoleID = subRole.SubRoleID, AssignedDate = DateTime.Now, Step = followUp.Step, TaskID = followUp.ID }; await approvalService.CreateApproval(approval); approvals = await approvalService.GetApprovalsForIssueId(pcrb.PlanNumber, true); approval = approvals.Where(a => a.TaskID == followUp.ID && a.RoleName.Equals(subRole.SubRoleCategoryItem) && a.UserID == member.UserID && a.Step == followUp.Step).First(); PCRBNotification notification = new() { PCRB = pcrb, Subject = $"[PCRB Follow Up] {pcrb.PlanNumber} - {pcrb.Title}", Message = $"Follow up for PCRB# {pcrb.PlanNumber} - {pcrb.Title} has been submitted for closure.", Approval = approval }; await pcrbService.NotifyApprover(notification); } } string comments = "Submitted for closure"; PCRBFollowUpComment comment = new() { PlanNumber = followUp.PlanNumber, FollowUpID = followUp.ID, Comment = comments, UserID = authStateProvider.CurrentUser.UserID }; await pcrbService.CreateFollowUpComment(comment); snackbar.Add("Follow up submitted for closure", Severity.Success); } else { throw new Exception("no follow ups available to mark as pending closure"); } followUpSubmitInProcess = false; } catch (Exception ex) { followUpSubmitInProcess = false; snackbar.Add($"Unable to submit follow up for closure, because {ex.Message}", Severity.Error); } StateHasChanged(); await OnParametersSetAsync(); } } private async Task ApproveFollowUp() { if (!followUpApproveInProcess) { try { followUpApproveInProcess = true; IEnumerable step5Approvals = approvals.Where(a => a.Step == 5 && a.ItemStatus == 0); foreach (Approval approval in step5Approvals) { approval.ItemStatus = 1; approval.Comments = "Follow up complete"; approval.CompletedDate = DateTime.Now; await approvalService.UpdateApproval(approval); } foreach (PCRBFollowUp? followUp in pcrb.FollowUps.Where(f => !f.IsComplete)) { followUp.IsComplete = true; followUp.IsPendingApproval = false; followUp.CompletedDate = DateTime.Now; await pcrbService.UpdateFollowUp(followUp); } pcrb.CurrentStep = (int)PCRB.StagesEnum.Closed; await pcrbService.UpdatePCRB(pcrb); string comments = "Follow up complete"; PCRBFollowUpComment comment = new() { PlanNumber = pcrb.PlanNumber, FollowUpID = pcrb.FollowUps.First().ID, Comment = comments, UserID = authStateProvider.CurrentUser.UserID }; await pcrbService.CreateFollowUpComment(comment); PCRBNotification notification = new() { PCRB = pcrb, Message = $"Follow up for PCRB# {pcrb.PlanNumber} - {pcrb.Title} has been closed." }; await pcrbService.NotifyOriginator(notification); followUpApproveInProcess = false; snackbar.Add("Follow up successfully approved", Severity.Success); } catch (Exception ex) { followUpApproveInProcess = false; snackbar.Add($"Unable to approve follow up, because {ex.Message}", Severity.Error); } StateHasChanged(); await OnParametersSetAsync(); } } private async Task DenyFollowUp(string action) { if (!followUpDenyInProcess) { try { string pastAction = action.ToLower().Equals("recall") ? "recalled" : "rejected"; followUpDenyInProcess = true; string comments = ""; DialogParameters parameters = new DialogParameters { { x => x.comments, comments } }; var dialog = await dialogService.ShowAsync($"Follow Up {action} Comment", parameters); DialogResult? result = await dialog.Result; if (result is null || result.Canceled || result.Data is null || string.IsNullOrWhiteSpace(result.Data?.ToString())) { throw new Exception("you must provide a comment"); } comments = result.Data?.ToString() ?? string.Empty; comments = comments.Trim(); IEnumerable step5Approvals = approvals.Where(a => a.Step == 5 && a.ItemStatus == 0 && a.UserID != pcrb.OwnerID); foreach (Approval approval in step5Approvals) { approval.ItemStatus = -1; approval.CompletedDate = DateTime.Now; approval.Comments = comments is null ? string.Empty : comments; await approvalService.UpdateApproval(approval); } foreach (var followUp in pcrb.FollowUps.Where(f => f.IsPendingApproval)) { followUp.IsPendingApproval = false; await pcrbService.UpdateFollowUp(followUp); } StringBuilder messageBuilder = new(); messageBuilder.Append($"Follow up for PCRB# {pcrb.PlanNumber} - {pcrb.Title} has been {pastAction}. "); messageBuilder.Append($"Please review the comments and make the necessary revisions. Comments: {comments}"); PCRBNotification notification = new() { PCRB = pcrb, Message = messageBuilder.ToString() }; await pcrbService.NotifyOriginator(notification); comments = $"Follow up {pastAction}. Comments: {comments}"; PCRBFollowUpComment comment = new() { PlanNumber = pcrb.PlanNumber, FollowUpID = pcrb.FollowUps.First().ID, Comment = comments, UserID = authStateProvider.CurrentUser.UserID }; await pcrbService.CreateFollowUpComment(comment); followUpDenyInProcess = false; snackbar.Add($"Follow up successfully {pastAction}", Severity.Success); } catch (Exception ex) { followUpDenyInProcess = false; snackbar.Add($"Unable to {action.ToLower()} follow up, because {ex.Message}", Severity.Error); } StateHasChanged(); await OnParametersSetAsync(); } } }