using System.Net; using System.Net.Mail; using System.Text; using MesaFabApproval.API.Utilities; using MesaFabApproval.Models; using MesaFabApproval.Shared.Models; using MesaFabApproval.Shared.Utilities; using Microsoft.Extensions.Caching.Memory; namespace MesaFabApproval.API.Services; public interface IPCRBService { Task CreateNewPCRB(PCRB pcrb); Task> GetAllPCRBs(bool bypassCache); Task GetPCRBByPlanNumber(int planNumber, bool bypassCache); Task GetPCRBByTitle(string title, bool bypassCache); Task UpdatePCRB(PCRB pcrb); Task DeletePCRB(int planNumber); Task UploadAttachment(IFormFile file, PCRBAttachment attachment); Task> GetAttachmentsByPlanNumber(int planNumber, bool bypassCache); Task UpdateAttachment(PCRBAttachment attachment); Task DeleteAttachment(PCRBAttachment attachment); Task CreateNewActionItem(PCRBActionItem actionItem); Task UpdateActionItem(PCRBActionItem actionItem); Task DeleteActionItem(int id); Task> GetActionItemsForPlanNumber(int planNumber, bool bypassCache); Task CreateNewAttendee(PCRBAttendee attendee); Task UpdateAttendee(PCRBAttendee attendee); Task DeleteAttendee(int id); Task> GetAttendeesByPlanNumber(int planNumber, bool bypassCache); Task CreatePCR3Document(PCR3Document document); Task UpdatePCR3Document(PCR3Document document); Task> GetPCR3DocumentsForPlanNumber(int planNumber, bool bypassCache); Task NotifyNewApprovals(PCRB pcrb); Task NotifyApprovers(PCRBNotification notification); Task NotifyOriginator(PCRBNotification notification); Task NotifyResponsiblePerson(PCRBActionItemNotification notification); Task CreateFollowUp(PCRBFollowUp followUp); Task> GetFollowUpsByPlanNumber(int planNumber, bool bypassCache); Task UpdateFollowUp(PCRBFollowUp followUp); Task DeleteFollowUp(int id); } public class PCRBService : IPCRBService { private readonly ILogger _logger; private readonly IDalService _dalService; private readonly IMemoryCache _cache; private readonly IUserService _userService; private readonly IApprovalService _approvalService; private readonly ISmtpService _smtpService; private readonly string _pcrbAttachmentPath; private readonly string _siteBaseUrl; public PCRBService(ILogger logger, IDalService dalService, IMemoryCache cache, IUserService userService, IApprovalService approvalService, ISmtpService smtpService, AppSettings appSettings) { _logger = logger ?? throw new ArgumentNullException("ILogger not injected"); _dalService = dalService ?? throw new ArgumentNullException("IDalService not injected"); _cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected"); _userService = userService ?? throw new ArgumentNullException("IUserService not injected"); _approvalService = approvalService ?? throw new ArgumentNullException("IApprovalService not injected"); _smtpService = smtpService ?? throw new ArgumentNullException("ISmtpService not injected"); _siteBaseUrl = appSettings.SiteBaseUrl; _pcrbAttachmentPath = appSettings.PcrbAttachmentPath; } public async Task CreateNewPCRB(PCRB pcrb) { try { _logger.LogInformation("Attempting to create new PCRB"); if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null"); StringBuilder queryBuilder = new(); queryBuilder.Append("insert into CCChangeControl (OwnerID, Title, ChangeLevel, ReasonForChange, "); queryBuilder.Append("ChangeDescription, IsITAR, CurrentStep, InsertTimeStamp, LastUpdateDate) "); queryBuilder.Append($"values ({pcrb.OwnerID}, '{pcrb.Title}', '{pcrb.ChangeLevel}', "); queryBuilder.Append($"'{pcrb.ReasonForChange}', '{pcrb.ChangeDescription}', "); queryBuilder.Append($"{Convert.ToInt32(pcrb.IsITAR)}, {pcrb.CurrentStep}, "); queryBuilder.Append($"'{pcrb.InsertTimeStamp.ToString("yyyy-MM-dd HH:mm:ss")}', "); queryBuilder.Append($"'{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}')"); int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString()); if (rowsCreated <= 0) throw new Exception("unable to insert new PCRB in the database"); } catch (Exception ex) { _logger.LogError($"Unable to create new PCRB, because {ex.Message}"); throw; } } public async Task> GetAllPCRBs(bool bypassCache) { try { _logger.LogInformation("Attempting to get all PCRBs"); IEnumerable? allPCRBs = null; if (!bypassCache) allPCRBs = _cache.Get>("allPCRBs"); if (allPCRBs is null) { string sql = "select * from CCChangeControl"; allPCRBs = (await _dalService.QueryAsync(sql)).ToList(); foreach (PCRB pcrb in allPCRBs) { if (string.IsNullOrWhiteSpace(pcrb.OwnerName) && pcrb.OwnerID > 0) pcrb.OwnerName = (await _userService.GetUserByUserId(pcrb.OwnerID)).GetFullName(); } _cache.Set("allPCRBs", allPCRBs, DateTimeOffset.Now.AddHours(1)); } if (allPCRBs is null || allPCRBs.Count() == 0) throw new Exception("no PCRBs found"); return allPCRBs; } catch (Exception ex) { _logger.LogError($"Unable to get all PCRBs, because {ex.Message}"); throw; } } public async Task GetPCRBByPlanNumber(int planNumber, bool bypassCache) { try { _logger.LogInformation("Attempting to get a PCRB by plan#"); if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB#"); PCRB? pcrb = null; if (!bypassCache) pcrb = _cache.Get($"pcrb{planNumber}"); if (pcrb is null) { string sql = $"select * from CCChangeControl where PlanNumber={planNumber}"; pcrb = (await _dalService.QueryAsync(sql)).FirstOrDefault(); if (pcrb is not null) { if (string.IsNullOrWhiteSpace(pcrb.OwnerName) && pcrb.OwnerID > 0) pcrb.OwnerName = (await _userService.GetUserByUserId(pcrb.OwnerID)).GetFullName(); _cache.Set($"pcrb{planNumber}", pcrb, DateTimeOffset.Now.AddHours(1)); _cache.Set($"pcrb{pcrb.Title}", pcrb, DateTimeOffset.Now.AddHours(1)); } } if (pcrb is null) throw new Exception($"unable to find PCRB {planNumber}"); return pcrb; } catch (Exception ex) { _logger.LogError($"Unable to get PCRB by Plan #, because {ex.Message}"); throw; } } public async Task GetPCRBByTitle(string title, bool bypassCache) { try { _logger.LogInformation("Attempting to get a PCRB by title"); if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Title cannot be null or empty"); PCRB? pcrb = null; if (!bypassCache) pcrb = _cache.Get($"pcrb{title}"); if (pcrb is null) { string sql = $"select * from CCChangeControl where Title='{title}'"; pcrb = (await _dalService.QueryAsync(sql)).FirstOrDefault(); if (pcrb is not null) { if (string.IsNullOrWhiteSpace(pcrb.OwnerName) && pcrb.OwnerID > 0) pcrb.OwnerName = (await _userService.GetUserByUserId(pcrb.OwnerID)).GetFullName(); _cache.Set($"pcrb{title}", pcrb, DateTimeOffset.Now.AddHours(1)); _cache.Set($"pcrb{pcrb.PlanNumber}", pcrb, DateTimeOffset.Now.AddHours(1)); } } if (pcrb is null) throw new Exception($"unable to find PCRB {title}"); return pcrb; } catch (Exception ex) { _logger.LogError($"Unable to get PCRB by title, because {ex.Message}"); throw; } } public async Task UpdatePCRB(PCRB pcrb) { try { _logger.LogInformation($"Attempting to update PCRB"); if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null"); StringBuilder queryBuilder = new(); queryBuilder.Append($"update CCChangeControl set OwnerID={pcrb.OwnerID}, "); queryBuilder.Append($"Title='{pcrb.Title.Replace("'", "''")}', ChangeLevel='{pcrb.ChangeLevel}', "); queryBuilder.Append($"CurrentStep={pcrb.CurrentStep}, ReasonForChange='{pcrb.ReasonForChange.Replace("'", "''")}', "); queryBuilder.Append($"ChangeDescription='{pcrb.ChangeDescription.Replace("'", "''")}', "); queryBuilder.Append($"IsITAR={Convert.ToInt32(pcrb.IsITAR)}, "); queryBuilder.Append($"InsertTimeStamp='{pcrb.InsertTimeStamp.ToString("yyyy-MM-dd HH:mm:ss")}', "); queryBuilder.Append($"ClosedDate='{pcrb.ClosedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); queryBuilder.Append($"LastUpdateDate='{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}' "); queryBuilder.Append($"where PlanNumber={pcrb.PlanNumber}"); int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString()); if (rowsAffected <= 0) throw new Exception("unable to perform update in database"); _cache.Set($"pcrb{pcrb.Title}", pcrb, DateTimeOffset.Now.AddHours(1)); _cache.Set($"pcrb{pcrb.PlanNumber}", pcrb, DateTimeOffset.Now.AddHours(1)); IEnumerable? allPCRBs = _cache.Get>("allPCRBs"); if (allPCRBs is not null) { List pcrbList = allPCRBs.ToList(); pcrbList.RemoveAll(p => p.PlanNumber == pcrb.PlanNumber); pcrbList.Add(pcrb); _cache.Set("allPCRBs", pcrbList, DateTimeOffset.Now.AddHours(1)); } } catch (Exception ex) { _logger.LogError($"Unable to update PCRB, because {ex.Message}"); throw; } } public async Task DeletePCRB(int planNumber) { try { _logger.LogInformation($"Attempting to delete PCRB {planNumber}"); if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan #"); string sql = $"delete from CCChangeControl where PlanNumber={planNumber}"; int rowsAffected = await _dalService.ExecuteAsync(sql); if (rowsAffected <= 0) throw new Exception("delete operation failed in database"); IEnumerable? allPCRBs = _cache.Get>("allPCRBs"); if (allPCRBs is not null) { List pcrbList = allPCRBs.ToList(); pcrbList.RemoveAll(p => p.PlanNumber == planNumber); _cache.Set("allPCRBs", pcrbList, DateTimeOffset.Now.AddHours(1)); } } catch (Exception ex) { _logger.LogError($"Unable to delete PCRB {planNumber}, because {ex.Message}"); throw; } } public async Task UploadAttachment(IFormFile file, PCRBAttachment attachment) { try { _logger.LogInformation("Attempting to upload attachment"); UploadResult? uploadResult = null; if (file is null) throw new ArgumentNullException("File cannot be null"); if (file.Length <= 0) throw new ArgumentException("File size cannot be zero"); if (attachment is null) throw new ArgumentNullException("Attachment cannot be null"); try { string encodedName = WebUtility.HtmlEncode(file.FileName); string path = $"{_pcrbAttachmentPath}\\{attachment.PlanNumber}\\{attachment.Step}\\{encodedName}"; attachment.Path = path; await FileUtilities.SaveFileToFileSystem(file, path); await SaveAttachmentInDb(file, attachment); uploadResult = new() { UploadSuccessful = true, FileName = file.FileName }; } catch (Exception ex) { uploadResult = new() { UploadSuccessful = false, FileName = file.FileName, Error = ex.Message }; } return uploadResult; } catch (Exception ex) { _logger.LogError($"Unable to upload attachment, because {ex.Message}"); throw; } } public async Task> GetAttachmentsByPlanNumber(int planNumber, bool bypassCache) { try { _logger.LogInformation($"Attempting to get all attachments for PCRB Plan# {planNumber}"); if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#"); IEnumerable? attachments = null; if (!bypassCache) attachments = _cache.Get>($"pcrbAttachments{planNumber}"); if (attachments is null) { string sql = $"select * from CCAttachment where PlanNumber={planNumber}"; attachments = await _dalService.QueryAsync(sql); _cache.Set($"pcrbAttachments{planNumber}", attachments, DateTimeOffset.Now.AddMinutes(15)); } return attachments; } catch (Exception ex) { _logger.LogError($"Unable to get all attachments for PCRB Plan# {planNumber}, because {ex.Message}"); throw; } } public async Task UpdateAttachment(PCRBAttachment attachment) { try { _logger.LogInformation("Attempting to update an attachment"); if (attachment is null) throw new ArgumentNullException("attachment cannot be null"); StringBuilder queryBuilder = new(); queryBuilder.Append($"update CCAttachment "); queryBuilder.Append($"set Title='{attachment.Title.Replace("'", "''")}' where ID={attachment.ID}"); int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString()); if (rowsAffected <= 0) throw new Exception("update failed in database"); } catch (Exception ex) { _logger.LogError($"Unable to update attachment, because {ex.Message}"); throw; } } public async Task DeleteAttachment(PCRBAttachment attachment) { try { _logger.LogInformation("Attempting to update an attachment"); if (attachment is null) throw new ArgumentNullException("attachment cannot be null"); if (!File.Exists(attachment.Path)) throw new FileNotFoundException("No file found at provided path"); File.Delete(attachment.Path); string sql = $"delete from CCAttachment where ID={attachment.ID}"; int rowsAffected = await _dalService.ExecuteAsync(sql); if (rowsAffected <= 0) throw new Exception("update failed in database"); } catch (Exception ex) { _logger.LogError($"Unable to update attachment, because {ex.Message}"); throw; } } public async Task CreateNewActionItem(PCRBActionItem actionItem) { try { _logger.LogInformation("Attempting to create new action item"); if (actionItem is null) throw new ArgumentNullException("action item cannot be null"); StringBuilder queryBuilder = new(); queryBuilder.Append("insert into CCPCRBActionItem (Name, Gating, ClosedStatus, ClosedDate, "); queryBuilder.Append("ClosedByID, UploadedByID, UploadedDateTime, ResponsiblePersonID, PlanNumber, "); queryBuilder.Append($"Step) values ('{actionItem.Name}', {Convert.ToInt32(actionItem.Gating)}, "); queryBuilder.Append($"{Convert.ToInt32(actionItem.ClosedStatus)}, "); DateTime closedDateCopy = actionItem.ClosedDate ?? DateTimeUtilities.MAX_DT; queryBuilder.Append($"'{closedDateCopy.ToString("yyyy-MM-dd HH:mm:ss")}', {actionItem.ClosedByID}, "); queryBuilder.Append($"{actionItem.UploadedByID}, '{actionItem.UploadedDateTime.ToString("yyyy-MM-dd HH:mm:ss")}', "); queryBuilder.Append($"{actionItem.ResponsiblePersonID}, {actionItem.PlanNumber}, "); queryBuilder.Append($"{actionItem.Step});"); int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString()); if (rowsCreated <= 0) throw new Exception("unable to insert new action item in the database"); } catch (Exception ex) { _logger.LogError($"Unable to create new action item, because {ex.Message}"); throw; } } public async Task UpdateActionItem(PCRBActionItem actionItem) { try { _logger.LogInformation("Attempting to update an action item"); if (actionItem is null) throw new ArgumentNullException("action item cannot be null"); StringBuilder queryBuilder = new(); queryBuilder.Append($"update CCPCRBActionItem set Name='{actionItem.Name.Replace("'", "''")}', Gating={Convert.ToInt32(actionItem.Gating)}, "); queryBuilder.Append($"ClosedStatus={Convert.ToInt32(actionItem.ClosedStatus)}, "); DateTime closedDateCopy = actionItem.ClosedDate ?? DateTimeUtilities.MAX_DT; queryBuilder.Append($"ClosedDate='{closedDateCopy.ToString("yyyy-MM-dd HH:mm:ss")}', "); queryBuilder.Append($"ClosedByID={actionItem.ClosedByID}, ResponsiblePersonID={actionItem.ResponsiblePersonID}, "); queryBuilder.Append($"Step={actionItem.Step} where ID={actionItem.ID}"); int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString()); if (rowsAffected <= 0) throw new Exception("update failed in database"); } catch (Exception ex) { _logger.LogError($"Unable to update attachment, because {ex.Message}"); throw; } } public async Task DeleteActionItem(int id) { try { _logger.LogInformation($"Attempting to delete action item {id}"); if (id <= 0) throw new ArgumentException($"{id} is not a valid PCRB action item ID"); string sql = $"delete from CCPCRBActionItem where ID={id}"; int rowsAffected = await _dalService.ExecuteAsync(sql); if (rowsAffected <= 0) throw new Exception("delete operation failed in database"); } catch (Exception ex) { _logger.LogError($"Unable to delete action item {id}, because {ex.Message}"); throw; } } public async Task> GetActionItemsForPlanNumber(int planNumber, bool bypassCache) { try { _logger.LogInformation($"Attempting to get all action items for PCRB plan# {planNumber}"); if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB plan#"); IEnumerable? actionItems = null; if (!bypassCache) actionItems = _cache.Get>($"pcrbActionItems{planNumber}"); if (actionItems is null) { string sql = $"select * from CCPCRBActionItem where PlanNumber={planNumber}"; actionItems = await _dalService.QueryAsync(sql); _cache.Set($"pcrbActionItems{planNumber}", actionItems, DateTimeOffset.Now.AddMinutes(15)); } return actionItems; } catch (Exception ex) { _logger.LogError($"Unable to get all action items for PCRB plan# {planNumber}, because {ex.Message}"); throw; } } public async Task CreateNewAttendee(PCRBAttendee attendee) { try { _logger.LogInformation("Attempting to create new attendee"); if (attendee is null) throw new ArgumentNullException("attendee item cannot be null"); StringBuilder queryBuilder = new(); queryBuilder.Append("insert into CCPCRBAttendee (PlanNumber, JobTitle, Location, Attended, AttendeeID, Step) "); queryBuilder.Append($"values ({attendee.PlanNumber}, '{attendee.JobTitle}', '{attendee.Location}', "); queryBuilder.Append($"{Convert.ToInt32(attendee.Attended)}, {attendee.AttendeeID}, "); queryBuilder.Append($"{attendee.Step});"); int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString()); if (rowsCreated <= 0) throw new Exception("unable to insert new attendee in the database"); } catch (Exception ex) { _logger.LogError($"Unable to create new attendee, because {ex.Message}"); throw; } } public async Task UpdateAttendee(PCRBAttendee attendee) { try { _logger.LogInformation("Attempting to update an attendee"); if (attendee is null) throw new ArgumentNullException("attendee cannot be null"); StringBuilder queryBuilder = new(); queryBuilder.Append($"update CCPCRBAttendee set JobTitle='{attendee.JobTitle}', "); queryBuilder.Append($"Location='{attendee.Location}', Attended={Convert.ToInt32(attendee.Attended)}, "); queryBuilder.Append($"AttendeeID={attendee.AttendeeID}, "); queryBuilder.Append($"Step={attendee.Step} where ID={attendee.ID}"); int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString()); if (rowsAffected <= 0) throw new Exception("update failed in database"); IEnumerable? attendees = _cache.Get>($"pcrbAttendees{attendee.PlanNumber}"); if (attendees is not null) { foreach (PCRBAttendee cachedAttendee in attendees) { if (cachedAttendee.ID == attendee.ID) { cachedAttendee.Location = attendee.Location; cachedAttendee.Attended = attendee.Attended; cachedAttendee.JobTitle = attendee.JobTitle; cachedAttendee.AttendeeID = attendee.AttendeeID; cachedAttendee.Step = attendee.Step; } } _cache.Set($"pcrbAttendees{attendee.PlanNumber}", attendees, DateTimeOffset.Now.AddMinutes(15)); } } catch (Exception ex) { _logger.LogError($"Unable to update attendee, because {ex.Message}"); throw; } } public async Task DeleteAttendee(int id) { try { _logger.LogInformation($"Attempting to delete attendee {id}"); if (id <= 0) throw new ArgumentException($"{id} is not a valid attendee ID"); string sql = $"delete from CCPCRBAttendee where ID={id}"; int rowsAffected = await _dalService.ExecuteAsync(sql); if (rowsAffected <= 0) throw new Exception("delete operation failed in database"); } catch (Exception ex) { _logger.LogError($"Unable to delete attendee {id}, because {ex.Message}"); throw; } } public async Task> GetAttendeesByPlanNumber(int planNumber, bool bypassCache) { try { _logger.LogInformation($"Attempting to get all attendees for PCRB plan# {planNumber}"); if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB plan#"); IEnumerable? attendees = null; if (!bypassCache) attendees = _cache.Get>($"pcrbAttendees{planNumber}"); if (attendees is null) { string sql = $"select * from CCPCRBAttendee where PlanNumber={planNumber}"; attendees = await _dalService.QueryAsync(sql); _cache.Set($"pcrbAttendees{planNumber}", attendees, DateTimeOffset.Now.AddMinutes(15)); } return attendees; } catch (Exception ex) { _logger.LogError($"Unable to get all attendees for PCRB plan# {planNumber}, because {ex.Message}"); throw; } } public async Task CreatePCR3Document(PCR3Document document) { try { _logger.LogInformation("Attempting to create new PCR3 document"); if (document is null) throw new ArgumentNullException("document item cannot be null"); StringBuilder queryBuilder = new(); queryBuilder.Append("insert into CCPCR3Document (PlanNumber, DocType) "); queryBuilder.Append($"values ({document.PlanNumber}, '{document.DocType}')"); int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString()); if (rowsCreated <= 0) throw new Exception("unable to insert new PCR3 document in the database"); } catch (Exception ex) { _logger.LogError($"Unable to create new PCR3 document, because {ex.Message}"); throw; } } public async Task UpdatePCR3Document(PCR3Document document) { try { _logger.LogInformation("Attempting to update a PCR3 document"); if (document is null) throw new ArgumentNullException("document cannot be null"); StringBuilder queryBuilder = new(); queryBuilder.Append($"update CCPCR3Document set DocNumbers='{document.DocNumbers}', "); queryBuilder.Append($"Comment='{document.Comment.Replace("'", "''")}', ECNNumber={document.ECNNumber}, "); queryBuilder.Append($"CompletedDate='{document.CompletedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); queryBuilder.Append($"CompletedByID={document.CompletedByID} "); queryBuilder.Append($"where ID={document.ID}"); int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString()); if (rowsAffected <= 0) throw new Exception("update failed in database"); } catch (Exception ex) { _logger.LogError($"Unable to update PCR3 document, because {ex.Message}"); throw; } } public async Task> GetPCR3DocumentsForPlanNumber(int planNumber, bool bypassCache) { try { _logger.LogInformation($"Attempting to get all PCR3 documents for PCRB plan# {planNumber}"); if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB plan#"); IEnumerable? documents = null; if (!bypassCache) documents = _cache.Get>($"pcr3Documents{planNumber}"); if (documents is null) { string sql = $"select * from CCPCR3Document where PlanNumber={planNumber}"; documents = await _dalService.QueryAsync(sql); _cache.Set($"pcr3Documents{planNumber}", documents, DateTimeOffset.Now.AddMinutes(15)); } return documents; } catch (Exception ex) { _logger.LogError($"Unable to get all PCR3 documents for PCRB plan# {planNumber}, because {ex.Message}"); throw; } } public async Task NotifyNewApprovals(PCRB pcrb) { try { _logger.LogInformation("Attempting to notify approvers"); if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null"); IEnumerable approvals = await _approvalService.GetApprovalsForIssueId(pcrb.PlanNumber, true); List approvalsNeedingNotification = approvals.Where(a => a.Step == pcrb.CurrentStep && a.NotifyDate <= DateTimeUtilities.MIN_DT && a.AssignedDate > DateTimeUtilities.MIN_DT).ToList(); HashSet emailsAlreadySent = new(); foreach (Approval approval in approvalsNeedingNotification) { User user = await _userService.GetUserByUserId(approval.UserID); if (!emailsAlreadySent.Contains(user.Email.ToLower())) { emailsAlreadySent.Add(user.Email); List toAddresses = new(); toAddresses.Add(new MailAddress(user.Email.ToLower())); List ccAddresses = new(); string subject = $"[New Task] Mesa Fab Approval - PCRB# {pcrb.PlanNumber} - {pcrb.Title}"; StringBuilder bodyBuilder = new(); bodyBuilder.Append($"PCRB# {pcrb.PlanNumber} [{pcrb.Title}] PCR{approval.Step} is ready for your approval. "); bodyBuilder.Append($"The assigned role is {approval.SubRoleCategoryItem}.

"); bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=pcrb/{approval.IssueID} to view the PCRB."); await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString()); approval.NotifyDate = DateTime.Now; await _approvalService.UpdateApproval(approval); } } } catch (Exception ex) { _logger.LogError($"Unable to notify approvers, because {ex.Message}"); throw; } } public async Task NotifyApprovers(PCRBNotification notification) { try { _logger.LogInformation("Attempting to send notification to approvers"); if (notification is null) throw new ArgumentNullException("notification cannot be null"); if (notification.PCRB is null) throw new ArgumentNullException("PCRB cannot be null"); if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty"); IEnumerable approvals = await _approvalService.GetApprovalsForIssueId(notification.PCRB.PlanNumber, true); HashSet emailsAlreadySent = new(); foreach (Approval approval in approvals) { User user = await _userService.GetUserByUserId(approval.UserID); if (!emailsAlreadySent.Contains(user.Email)) { emailsAlreadySent.Add(user.Email); List toAddresses = new(); toAddresses.Add(new MailAddress(user.Email)); List ccAddresses = new(); string subject = $"[Update] Mesa Fab Approval - PCRB# {notification.PCRB.PlanNumber} - {notification.PCRB.Title}"; StringBuilder bodyBuilder = new(); bodyBuilder.Append($"{notification.Message}

"); bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=pcrb/{approval.IssueID} to view the PCRB."); await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString()); approval.NotifyDate = DateTime.Now; await _approvalService.UpdateApproval(approval); } } } catch (Exception ex) { _logger.LogError($"Unable to send notification to originator, because {ex.Message}"); throw; } } public async Task NotifyOriginator(PCRBNotification notification) { try { _logger.LogInformation("Attempting to send notification to originator"); if (notification is null) throw new ArgumentNullException("notification cannot be null"); if (notification.PCRB is null) throw new ArgumentNullException("PCRB cannot be null"); if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty"); User user = await _userService.GetUserByUserId(notification.PCRB.OwnerID); List toAddresses = new(); toAddresses.Add(new MailAddress(user.Email)); List ccAddresses = new(); string subject = $"[Update] Mesa Fab Approval - PCRB# {notification.PCRB.PlanNumber} - {notification.PCRB.Title}"; StringBuilder bodyBuilder = new(); bodyBuilder.Append($"{notification.Message}

"); bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=pcrb/{notification.PCRB.PlanNumber} to view the PCRB."); await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString()); } catch (Exception ex) { _logger.LogError($"Unable to send notification to originator, because {ex.Message}"); throw; } } public async Task NotifyResponsiblePerson(PCRBActionItemNotification notification) { try { _logger.LogInformation("Attempting to notify responsible person"); if (notification is null) throw new ArgumentNullException("notification cannot be null"); if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty"); if (notification.ActionItem.ResponsiblePerson is null) notification.ActionItem.ResponsiblePerson = await _userService.GetUserByUserId(notification.ActionItem.ResponsiblePersonID); List toAddresses = new(); toAddresses.Add(new MailAddress(notification.ActionItem.ResponsiblePerson.Email)); List ccAddresses = new(); string subject = $"[New Task] Mesa Fab Approval - PCRB# {notification.PCRB.PlanNumber} - {notification.PCRB.Title}"; StringBuilder bodyBuilder = new(); bodyBuilder.Append($"{notification.Message}

"); bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=pcrb/{notification.PCRB.PlanNumber} to view the PCRB."); await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString()); } catch (Exception ex) { _logger.LogError($"Unable to notify responsible person, because {ex.Message}"); throw; } } public async Task CreateFollowUp(PCRBFollowUp followUp) { try { _logger.LogInformation("Attempting to create PCRB follow up"); if (followUp is null) throw new ArgumentNullException("follow up cannot be null"); StringBuilder queryBuilder = new(); queryBuilder.Append("insert into CCPCRBFollowUp (PlanNumber, Step, FollowUpDate, CompletedDate) "); queryBuilder.Append("values (@PlanNumber, @Step, @FollowUpDate, @CompletedDate)"); int rowsReturned = await _dalService.ExecuteAsync(queryBuilder.ToString(), followUp); if (rowsReturned <= 0) throw new Exception("unable to insert new follow up in the database"); } catch (Exception ex) { _logger.LogError($"Unable to create new follow up, because {ex.Message}"); throw; } } public async Task> GetFollowUpsByPlanNumber(int planNumber, bool bypassCache) { try { _logger.LogInformation($"Attempting to fetch follow ups for PCRB {planNumber}"); if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#"); IEnumerable? followUps = new List(); if (!bypassCache) followUps = _cache.Get>($"pcrbFollowUps{planNumber}"); if (followUps is null || followUps.Count() == 0) { string sql = "select * from CCPCRBFollowUp where PlanNumber=@PlanNumber"; followUps = await _dalService.QueryAsync(sql, new { PlanNumber = planNumber }); if (followUps is not null) _cache.Set($"pcrbFollowUps{planNumber}", followUps, DateTimeOffset.Now.AddMinutes(15)); } return followUps ?? new List(); } catch (Exception ex) { _logger.LogError($"Unable to fetch follow ups for PCRB {planNumber}, because {ex.Message}"); throw; } } public async Task UpdateFollowUp(PCRBFollowUp followUp) { try { _logger.LogInformation("Attempting to update a follow up"); if (followUp is null) throw new ArgumentNullException("follow up cannot be null"); StringBuilder queryBuilder = new(); queryBuilder.Append("update CCPCRBFollowUp set Step=@Step, FollowUpDate=@FollowUpDate, IsComplete=@IsComplete, "); queryBuilder.Append("IsDeleted=@IsDeleted, CompletedDate=@CompletedDate, Comments=@Comments "); queryBuilder.Append("where ID=@ID"); int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString(), followUp); if (rowsAffected <= 0) throw new Exception("update failed in database"); } catch (Exception ex) { _logger.LogError($"Unable to update follow up, because {ex.Message}"); throw; } } public async Task DeleteFollowUp(int id) { try { _logger.LogInformation($"Attempting to delete follow up {id}"); if (id <= 0) throw new ArgumentException($"{id} is not a valid follow up ID"); string sql = "delete from CCPCRBFollowUp where ID=@ID"; int rowsAffected = await _dalService.ExecuteAsync(sql, new { ID = id }); if (rowsAffected <= 0) throw new Exception("delete operation failed in database"); } catch (Exception ex) { _logger.LogError($"Unable to delete follow up {id}, because {ex.Message}"); throw; } } private async Task SaveAttachmentInDb(IFormFile file, PCRBAttachment attachment) { try { _logger.LogInformation($"Attempting to save attachment to database"); if (file is null) throw new ArgumentNullException("File cannot be null"); if (string.IsNullOrWhiteSpace(attachment.Path)) throw new ArgumentException("Path cannot be null or empty"); if (attachment.PlanNumber <= 0) throw new ArgumentException($"{attachment.PlanNumber} is not a valid PCRB Plan#"); StringBuilder queryBuilder = new(); queryBuilder.Append("insert into CCAttachment (PlanNumber, FileName, UploadDateTime, Path, UploadedByID, Title, "); queryBuilder.Append($"Step) values ({attachment.PlanNumber}, '{file.FileName}', "); queryBuilder.Append($"'{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}', '{attachment.Path}', {attachment.UploadedByID}, "); queryBuilder.Append($"'{attachment.Title}', {attachment.Step});"); int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString()); if (rowsAffected <= 0) throw new Exception("Unable to insert attachment in database"); } catch (Exception ex) { _logger.LogError($"Unable to save file to DB, because {ex.Message}"); throw; } } }