using System.Data; 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 IMRBService { Task> GetAllMRBs(bool bypassCache); Task MRBNumberIsValid(int number); Task GetMRBById(int id, bool bypassCache = false); Task GetMRBByTitle(string title, bool bypassCache); Task CreateNewMRB(MRB mrb); Task UpdateMRB(MRB mrb); Task CreateMRBAction(MRBAction mrbAction); Task> GetMRBActionsForMRB(int mrbNumber, bool bypassCache); Task UpdateMRBAction(MRBAction mrbAction); Task DeleteMRBAction(int mrbActionID, int mrbNumber); Task> UploadAttachments(IEnumerable files, int mrbNumber); Task> UploadActionAttachments(IEnumerable files, int actionId); Task> GetAllAttachmentsForMRB(int mrbNumber, bool bypassCache); Task> GetAllActionAttachmentsForMRB(int mrbNumber, bool bypassCache); Task DeleteAttachment(MRBAttachment attachment); Task NotifyNewApprovals(MRB mrb); Task NotifyApprovers(MRBNotification notification); Task NotifyOriginator(MRBNotification notification); Task NotifyQAPreApprover(MRBNotification notification); Task DeleteMRB(int mrbNumber); Task ConvertActionsToCsvFile(int mrbNumber, string path); } public class MRBService : IMRBService { 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 _siteBaseUrl; private readonly string _mrbAttachmentPath; public MRBService(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; _mrbAttachmentPath = appSettings.MrbAttachmentPath; } public async Task CreateNewMRB(MRB mrb) { try { _logger.LogInformation("Attempting to generate new MRB"); if (mrb is null) throw new ArgumentNullException("MRB cannot be null"); StringBuilder queryBuilder = new(); queryBuilder.Append("insert into MRB (OriginatorID, Title, SubmittedDate, "); queryBuilder.Append("CloseDate, CancelDate, NumberOfLotsAffected, ApprovalDate, "); queryBuilder.Append("IssueDescription, CustomerImpacted, Department, Process, Val, RMANo, "); queryBuilder.Append("PCRBNo, SpecsImpacted, TrainingRequired, Status, StageNo, "); queryBuilder.Append("CustomerImpactedName, ProcessECNNumber, Tool, Category) values ("); queryBuilder.Append($"{mrb.OriginatorID}, '{mrb.Title}', "); if (mrb.SubmittedDate < DateTimeUtilities.MIN_DT) mrb.SubmittedDate = DateTimeUtilities.MIN_DT; queryBuilder.Append($"'{mrb.SubmittedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); if (mrb.CloseDate > DateTimeUtilities.MAX_DT) mrb.CloseDate = DateTimeUtilities.MAX_DT; queryBuilder.Append($"'{mrb.CloseDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); if (mrb.CancelDate > DateTimeUtilities.MAX_DT) mrb.CancelDate = DateTimeUtilities.MAX_DT; queryBuilder.Append($"'{mrb.CancelDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); queryBuilder.Append($"{mrb.NumberOfLotsAffected}, "); if (mrb.ApprovalDate > DateTimeUtilities.MAX_DT) mrb.ApprovalDate = DateTimeUtilities.MAX_DT; queryBuilder.Append($"'{mrb.ApprovalDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); queryBuilder.Append($"'{mrb.IssueDescription}', {Convert.ToUInt32(mrb.CustomerImpacted)}, "); queryBuilder.Append($"'{mrb.Department}', '{mrb.Process}', {mrb.Val}, {mrb.RMANo}, '{mrb.PCRBNo}', "); queryBuilder.Append($"{Convert.ToInt32(mrb.SpecsImpacted)}, {Convert.ToInt32(mrb.TrainingRequired)}, "); queryBuilder.Append($"'{mrb.Status}', {mrb.StageNo}, '{mrb.CustomerImpactedName}', "); queryBuilder.Append($"{mrb.ProcessECNNumber}, '{mrb.Tool}', '{mrb.Category}');"); int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString()); if (rowsCreated <= 0) throw new Exception("Unable to create new MRB"); mrb = await GetMRBByTitle(mrb.Title, true); _cache.Set($"mrb{mrb.MRBNumber}", mrb, DateTimeOffset.Now.AddHours(1)); _cache.Set($"mrb{mrb.Title}", mrb, DateTimeOffset.Now.AddHours(1)); IEnumerable? allMrbs = _cache.Get>("allMrbs"); if (allMrbs is not null) { List mrbList = allMrbs.ToList(); mrbList.Add(mrb); _cache.Set("allMrbs", mrbList, DateTimeOffset.Now.AddHours(1)); } } catch (Exception ex) { _logger.LogError($"An exception occurred when attempting to create new MRB. Exception: {ex.Message}"); throw; } } public async Task> GetAllMRBs(bool bypassCache) { try { _logger.LogInformation("Attempting to get all MRBs"); IEnumerable? allMrbs = null; if (!bypassCache) allMrbs = _cache.Get>("allMrbs"); if (allMrbs is null) { StringBuilder queryBuilder = new(); queryBuilder.Append("select (u.FirstName + ' ' + u.LastName) as OriginatorName, m.* "); queryBuilder.Append("from MRB m join Users u on m.OriginatorID = u.UserID;"); allMrbs = (await _dalService.QueryAsync(queryBuilder.ToString())).ToList(); _cache.Set("allMrbs", allMrbs, DateTimeOffset.Now.AddHours(1)); } return allMrbs; } catch (Exception ex) { _logger.LogError($"An exception occurred when attempting to get all MRBs. Exception: {ex.Message}"); throw; } } public async Task MRBNumberIsValid(int number) { try { _logger.LogInformation($"Attempting to determine if {number} is a valid MRB#"); if (number <= 0) return false; IEnumerable mrbNumbers = _cache.Get>("mrbNumbers") ?? new HashSet(); if (mrbNumbers.Contains(number)) return true; string sql = $"select count(MRBNumber) as count from MRB where MRBNumber={number}"; int rowsReturned = (await _dalService.QueryAsync(sql)).FirstOrDefault(); if (rowsReturned > 0) { mrbNumbers.Append(number); _cache.Set("mrbNumbers", mrbNumbers); return true; } return false; } catch (Exception ex) { _logger.LogError($"Unable to determine if {number} is a valid MRB#, because {ex.Message}"); throw; } } public async Task GetMRBById(int id, bool bypassCache = false) { try { _logger.LogInformation("Attempting to get an MRB by ID"); if (id < 0) throw new ArgumentException("Invalid MRB number"); MRB? mrb = null; if (!bypassCache) mrb = _cache.Get($"mrb{id}"); if (mrb is null) { StringBuilder queryBuilder = new(); queryBuilder.Append("select (u.FirstName + ' ' + u.LastName) as OriginatorName, m.* "); queryBuilder.Append("from MRB m join Users u on m.OriginatorID = u.UserID "); queryBuilder.Append($"where m.MRBNumber = {id}"); mrb = (await _dalService.QueryAsync(queryBuilder.ToString())).FirstOrDefault(); _cache.Set($"mrb{id}", mrb, DateTimeOffset.Now.AddHours(1)); } if (mrb is null) throw new Exception($"Unable to get MRB {id}"); return mrb; } catch (Exception ex) { _logger.LogError($"An exception occurred when attempting to get an MRB. Exception: {ex.Message}"); throw; } } public async Task GetMRBByTitle(string title, bool bypassCache) { try { _logger.LogInformation("Attempting to get an MRB by title"); if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Title cannot be null or empty"); MRB? mrb = null; if (!bypassCache) mrb = _cache.Get($"mrb{title}"); if (mrb is null) { StringBuilder queryBuilder = new(); queryBuilder.Append("select (u.FirstName + ' ' + u.LastName) as OriginatorName, m.* "); queryBuilder.Append("from MRB m join Users u on m.OriginatorID = u.UserID "); queryBuilder.Append($"where m.Title = '{title}'"); mrb = (await _dalService.QueryAsync(queryBuilder.ToString())).FirstOrDefault(); _cache.Set($"mrb{title}", mrb, DateTimeOffset.Now.AddHours(1)); } if (mrb is null) throw new Exception($"Unable to get MRB {title}"); return mrb; } catch (Exception ex) { _logger.LogError($"An exception occurred when attempting to get an MRB. Exception: {ex.Message}"); throw; } } public async Task UpdateMRB(MRB mrb) { try { _logger.LogInformation("Attempting to update an MRB"); if (mrb is null) throw new ArgumentNullException("MRB cannot be null"); StringBuilder queryBuilder = new(); queryBuilder.Append($"update MRB set OriginatorID = {mrb.OriginatorID}, "); queryBuilder.Append($"Title = '{mrb.Title.Replace("'", "''")}', "); if (mrb.SubmittedDate < DateTimeUtilities.MIN_DT) mrb.SubmittedDate = DateTimeUtilities.MIN_DT; queryBuilder.Append($"SubmittedDate = '{mrb.SubmittedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); if (mrb.CloseDate > DateTimeUtilities.MAX_DT) mrb.CloseDate = DateTimeUtilities.MAX_DT; queryBuilder.Append($"CloseDate = '{mrb.CloseDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); if (mrb.CancelDate > DateTimeUtilities.MAX_DT) mrb.CancelDate = DateTimeUtilities.MAX_DT; queryBuilder.Append($"CancelDate = '{mrb.CancelDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); queryBuilder.Append($"NumberOfLotsAffected = {mrb.NumberOfLotsAffected}, "); if (mrb.ApprovalDate > DateTimeUtilities.MAX_DT) mrb.ApprovalDate = DateTimeUtilities.MAX_DT; queryBuilder.Append($"ApprovalDate = '{mrb.ApprovalDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); queryBuilder.Append($"IssueDescription = '{mrb.IssueDescription.Replace("'", "''")}', "); queryBuilder.Append($"CustomerImpacted = {Convert.ToInt32(mrb.CustomerImpacted)}, "); queryBuilder.Append($"Department = '{mrb.Department.Replace("'", "''")}', "); queryBuilder.Append($"Process = '{mrb.Process.Replace("'", "''")}', "); queryBuilder.Append($"Val = {mrb.Val}, "); queryBuilder.Append($"RMANo = {mrb.RMANo}, "); queryBuilder.Append($"PCRBNo = '{mrb.PCRBNo}', "); queryBuilder.Append($"SpecsImpacted = {Convert.ToInt32(mrb.SpecsImpacted)}, "); queryBuilder.Append($"TrainingRequired = {Convert.ToInt32(mrb.TrainingRequired)}, "); queryBuilder.Append($"Status = '{mrb.Status}', StageNo = {mrb.StageNo}, "); queryBuilder.Append($"CustomerImpactedName = '{mrb.CustomerImpactedName.Replace("'", "''")}', "); queryBuilder.Append($"ProcessECNNumber = '{mrb.ProcessECNNumber}', "); queryBuilder.Append($"Tool = '{mrb.Tool.Replace("'", "''")}', Category = '{mrb.Category.Replace("'", "''")}' "); queryBuilder.Append($"where MRBNumber = {mrb.MRBNumber};"); int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString()); if (rowsAffected <= 0) throw new Exception($"Unable to update MRB {mrb.MRBNumber}"); _cache.Set($"mrb{mrb.MRBNumber}", mrb, DateTimeOffset.Now.AddHours(1)); _cache.Set($"mrb{mrb.Title}", mrb, DateTimeOffset.Now.AddHours(1)); IEnumerable? allMrbs = _cache.Get>("allMrbs"); if (allMrbs is not null) { List mrbList = allMrbs.ToList(); mrbList.RemoveAll(m => m.MRBNumber == mrb.MRBNumber); mrbList.Add(mrb); _cache.Set("allMrbs", mrbList, DateTimeOffset.Now.AddHours(1)); } } catch (Exception ex) { _logger.LogError($"An exception occurred when attempting to update an MRB. Exception: {ex.Message}"); throw; } } public async Task CreateMRBAction(MRBAction mrbAction) { try { _logger.LogInformation("Attempting to generate new MRB action"); if (mrbAction is null) throw new ArgumentNullException("MRB action cannot be null"); StringBuilder queryBuilder = new(); queryBuilder.Append("insert into MRBAction (Action, Customer, Quantity, PartNumber, LotNumber, MRBNumber, "); queryBuilder.Append("ConvertFrom, ConvertTo, Justification) "); queryBuilder.Append($"values ('{mrbAction.Action}', '{mrbAction.Customer}', {mrbAction.Quantity}, "); queryBuilder.Append($"'{mrbAction.PartNumber}', '{mrbAction.LotNumber}', {mrbAction.MRBNumber}, "); queryBuilder.Append($"'{mrbAction.ConvertFrom}', '{mrbAction.ConvertTo}', '{mrbAction.Justification}');"); int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString()); if (rowsAffected <= 0) throw new Exception("Unable to create MRB action in database"); } catch (Exception ex) { _logger.LogError($"An exception occurred when attempting to create new MRB action. Exception: {ex.Message}"); throw; } } public async Task> GetMRBActionsForMRB(int mrbNumber, bool bypassCache) { try { _logger.LogInformation($"Attempting to get MRB actions for MRB {mrbNumber}"); if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number"); IEnumerable? mrbActions = null; if (!bypassCache) _cache.Get>($"mrbActions{mrbNumber}"); if (mrbActions is null) { string sql = $"select * from MRBAction where MRBNumber = {mrbNumber}"; mrbActions = (await _dalService.QueryAsync(sql)).ToList(); if (mrbActions is not null) { foreach (MRBAction action in mrbActions) { if (action.CompletedDate < DateTime.MaxValue && action.CompletedByUserID > 0) { action.CompletedByUser = await _userService.GetUserByUserId(action.CompletedByUserID); } } _cache.Set($"mrbActions{mrbNumber}", mrbActions, DateTimeOffset.Now.AddMinutes(30)); } } if (mrbActions is null) throw new Exception($"Unable to find MRB actions for MRB {mrbNumber}"); return mrbActions; } catch (Exception ex) { _logger.LogError($"Unable to get MRB actions for MRB {mrbNumber}, because {ex.Message}"); throw; } } public async Task UpdateMRBAction(MRBAction mrbAction) { try { _logger.LogInformation("Attempting to update MRB action"); if (mrbAction is null) throw new ArgumentNullException("MRB action cannot be null"); StringBuilder queryBuilder = new(); queryBuilder.Append($"update MRBAction set Action = '{mrbAction.Action}', "); queryBuilder.Append($"Customer = '{mrbAction.Customer.Replace("'", "''")}', "); queryBuilder.Append($"Quantity = {mrbAction.Quantity}, "); queryBuilder.Append($"PartNumber = '{mrbAction.PartNumber.Replace("'", "''")}', "); queryBuilder.Append($"LotNumber = '{mrbAction.LotNumber.Replace("'", "''")}', "); if (mrbAction.AssignedDate < DateTimeUtilities.MIN_DT) mrbAction.AssignedDate = DateTimeUtilities.MIN_DT; queryBuilder.Append($"AssignedDate= '{mrbAction.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); if (mrbAction.CompletedDate > DateTimeUtilities.MAX_DT) mrbAction.CompletedDate = DateTimeUtilities.MAX_DT; queryBuilder.Append($"CompletedDate= '{mrbAction.CompletedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); queryBuilder.Append($"CompletedByUserID={mrbAction.CompletedByUserID}, "); queryBuilder.Append($"ConvertFrom='{mrbAction.ConvertFrom.Replace("'", "''")}', "); queryBuilder.Append($"ConvertTo='{mrbAction.ConvertTo.Replace("'", "''")}', "); queryBuilder.Append($"Justification='{mrbAction.Justification.Replace("'", "''")}' "); queryBuilder.Append($"where ActionID={mrbAction.ActionID};"); int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString()); if (rowsAffected <= 0) throw new Exception($"There were no MRB actions found with MRB# {mrbAction.MRBNumber}"); List? mrbActions = _cache.Get>($"mrbActions{mrbAction.MRBNumber}")?.ToList(); if (mrbActions is not null) { mrbActions.RemoveAll(m => m.ActionID == mrbAction.ActionID); mrbActions.Add(mrbAction); _cache.Set($"mrbActions{mrbAction.MRBNumber}", mrbActions, DateTimeOffset.Now.AddMinutes(30)); } } catch (Exception ex) { _logger.LogError($"Unable to update MRB action, because {ex.Message}"); throw; } } public async Task DeleteMRBAction(int mrbActionID, int mrbNumber) { try { _logger.LogInformation($"Attempting to delete MRB action {mrbActionID}"); if (mrbActionID <= 0) throw new ArgumentException($"{mrbActionID} is not a valid MRBActionID"); if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRBNumber"); string sql = $"delete from MRBAction where ActionID = {mrbActionID};"; int rowsAffected = await _dalService.ExecuteAsync(sql); if (rowsAffected <= 0) throw new Exception($"No MRB action was found to delete for ActionID {mrbActionID}"); List? mrbActions = _cache.Get>($"mrbActions{mrbNumber}")?.ToList(); if (mrbActions is not null) { mrbActions.RemoveAll(m => m.ActionID == mrbActionID); _cache.Set($"mrbActions{mrbNumber}", mrbActions, DateTimeOffset.Now.AddMinutes(30)); } } catch (Exception ex) { _logger.LogError($"An exception occurred when attempting to delete MRB action {mrbActionID}. Exception: {ex.Message}"); throw; } } public async Task> UploadAttachments(IEnumerable files, int mrbNumber) { try { _logger.LogInformation("Attempting to upload attachments"); List uploadResults = new(); if (files is null) throw new ArgumentNullException("Files cannot be null"); if (files.Count() <= 0) throw new ArgumentException("Files cannot be empty"); if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number"); foreach (IFormFile file in files) { try { if (file is null) throw new ArgumentException("File cannot be null"); if (file.Length <= 0) throw new ArgumentException("File size cannot be zero"); string encodedName = WebUtility.HtmlEncode(file.FileName); string path = $"{_mrbAttachmentPath}\\{mrbNumber}\\{encodedName}"; await FileUtilities.SaveFileToFileSystem(file, path); await SaveAttachmentInDb(file, path, mrbNumber); UploadResult uploadResult = new() { UploadSuccessful = true, FileName = file.FileName }; uploadResults.Add(uploadResult); } catch (Exception ex) { UploadResult uploadResult = new() { UploadSuccessful = false, FileName = file.FileName, Error = ex.Message }; uploadResults.Add(uploadResult); } } return uploadResults; } catch (Exception ex) { _logger.LogError($"An exception occurred when attempting to upload attachment. Exception: {ex.Message}"); throw; } } public async Task> UploadActionAttachments(IEnumerable files, int actionId) { try { _logger.LogInformation("Attempting to upload action attachments"); List uploadResults = new(); if (files is null) throw new ArgumentNullException("Files cannot be null"); if (files.Count() <= 0) throw new ArgumentException("Files cannot be empty"); if (actionId <= 0) throw new ArgumentException($"{actionId} is not a valid MRB action ID"); List taskList = new(); foreach (IFormFile file in files) { try { if (file is null) throw new ArgumentException("File cannot be null"); if (file.Length <= 0) throw new ArgumentException("File size cannot be zero"); string encodedName = WebUtility.HtmlEncode(file.FileName); string path = $"{_mrbAttachmentPath}\\{actionId}\\{encodedName}"; taskList.Add(FileUtilities.SaveFileToFileSystem(file, path)); taskList.Add(SaveActionAttachmentInDb(file, path, actionId)); UploadResult uploadResult = new() { UploadSuccessful = true, FileName = file.Name }; uploadResults.Add(uploadResult); } catch (Exception ex) { UploadResult uploadResult = new() { UploadSuccessful = false, FileName = file.Name, Error = ex.Message }; uploadResults.Add(uploadResult); } } Task.WaitAll(taskList.ToArray()); return uploadResults; } catch (Exception ex) { _logger.LogError($"An exception occurred when attempting to upload action attachment. Exception: {ex.Message}"); throw; } } public async Task> GetAllAttachmentsForMRB(int mrbNumber, bool bypassCache) { try { _logger.LogInformation($"Attempting to get all attachments for MRB {mrbNumber}"); if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number"); IEnumerable? attachments = null; if (!bypassCache) _cache.Get>($"mrbAttachments{mrbNumber}"); if (attachments is null) { string sql = $"select * from MRBAttachment where MRBNumber = {mrbNumber};"; attachments = (await _dalService.QueryAsync(sql)).ToList(); _cache.Set($"mrbAttachments{mrbNumber}", attachments, DateTimeOffset.Now.AddMinutes(15)); } return attachments; } catch (Exception ex) { StringBuilder errMsgBuilder = new(); errMsgBuilder.Append($"An error occurred when attempting to get all attachments for MRB {mrbNumber}. "); errMsgBuilder.Append($"Exception: {ex.Message}"); _logger.LogError(errMsgBuilder.ToString()); throw; } } public async Task> GetAllActionAttachmentsForMRB(int mrbNumber, bool bypassCache) { try { _logger.LogInformation($"Attempting to get all action attachments for MRB {mrbNumber}"); if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB#"); List? attachments = null; if (!bypassCache) attachments = _cache.Get>($"mrbActionAttachments{mrbNumber}"); if (attachments is null) { attachments = new List(); foreach (MRBAction action in await GetMRBActionsForMRB(mrbNumber, false)) { string sql = $"select * from MRBActionAttachment where ActionID = {action.ActionID};"; IEnumerable newAttachments = (await _dalService.QueryAsync(sql)).ToList(); attachments.AddRange(newAttachments); } _cache.Set($"mrbActionAttachments{mrbNumber}", attachments, DateTimeOffset.Now.AddMinutes(15)); } return attachments; } catch (Exception ex) { StringBuilder errMsgBuilder = new(); errMsgBuilder.Append($"An error occurred when attempting to get all attachments for MRB action {mrbNumber}. "); errMsgBuilder.Append($"Exception: {ex.Message}"); _logger.LogError(errMsgBuilder.ToString()); throw; } } public async Task DeleteAttachment(MRBAttachment attachment) { try { _logger.LogInformation("Attempting to delete 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 MRBAttachment where AttachmentID = {attachment.AttachmentID};"; int rowsDeleted = await _dalService.ExecuteAsync(sql); if (rowsDeleted <= 0) throw new Exception($"No attachments found in the database with attachment ID {attachment.AttachmentID}"); } catch (Exception ex) { _logger.LogError($"An exception occurred when attempting to delete an attachment. Exception: {ex.Message}"); throw; } } public async Task NotifyNewApprovals(MRB mrb) { try { _logger.LogInformation("Attempting to notify approvers"); if (mrb is null) throw new ArgumentNullException("MRB cannot be null"); IEnumerable approvals = await _approvalService.GetApprovalsForIssueId(mrb.MRBNumber, true); List approvalsNeedingNotification = approvals.Where(a => a.NotifyDate <= DateTimeUtilities.MIN_DT).ToList(); HashSet emailsAlreadySent = new(); foreach (Approval approval in approvalsNeedingNotification) { 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 = $"[New Task] Mesa Fab Approval - MRB# {mrb.MRBNumber} - {mrb.Title}"; StringBuilder bodyBuilder = new(); bodyBuilder.Append($"MRB# {mrb.MRBNumber} [{mrb.Title}] is ready for your approval. "); bodyBuilder.Append($"The assigned role is {approval.SubRoleCategoryItem}.

"); bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=mrb/{approval.IssueID} to view the task."); 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(MRBNotification notification) { try { _logger.LogInformation("Attempting to send notification to approvers"); if (notification is null) throw new ArgumentNullException("notification cannot be null"); if (notification.MRB is null) throw new ArgumentNullException("MRB cannot be null"); if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty"); IEnumerable approvals = await _approvalService.GetApprovalsForIssueId(notification.MRB.MRBNumber, 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 - MRB# {notification.MRB.MRBNumber} - {notification.MRB.Title}"; StringBuilder bodyBuilder = new(); bodyBuilder.Append($"{notification.Message}

"); bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=mrb/{approval.IssueID} to view the MRB."); 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(MRBNotification notification) { try { _logger.LogInformation("Attempting to send notification to originator"); if (notification is null) throw new ArgumentNullException("notification cannot be null"); if (notification.MRB is null) throw new ArgumentNullException("MRB cannot be null"); if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty"); User user = await _userService.GetUserByUserId(notification.MRB.OriginatorID); List toAddresses = new(); toAddresses.Add(new MailAddress(user.Email)); List ccAddresses = new(); string subject = $"[Update] Mesa Fab Approval - MRB# {notification.MRB.MRBNumber} - {notification.MRB.Title}"; StringBuilder bodyBuilder = new(); bodyBuilder.Append($"{notification.Message}

"); bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=mrb/{notification.MRB.MRBNumber} to view the MRB."); 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 NotifyQAPreApprover(MRBNotification notification) { try { _logger.LogInformation("Attempting to send notification to QA pre approver"); if (notification is null) throw new ArgumentNullException("notification cannot be null"); if (notification.MRB is null) throw new ArgumentNullException("MRB cannot be null"); if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty"); string roleName = "QA_PRE_APPROVAL"; int roleId = await _approvalService.GetRoleIdForRoleName(roleName); if (roleId <= 0) throw new Exception($"could not find {roleName} role ID"); SubRole? subRole = (await _approvalService.GetSubRolesForSubRoleName(roleName, roleId)).FirstOrDefault(); if (subRole is null) throw new Exception("Unable to find QA pre approver role"); IEnumerable members = await _approvalService.GetApprovalGroupMembers(subRole.SubRoleID); List toAddresses = new(); foreach (User member in members) toAddresses.Add(new MailAddress(member.Email)); List ccAddresses = new(); string subject = $"[Update] Mesa Fab Approval - MRB# {notification.MRB.MRBNumber} - {notification.MRB.Title}"; StringBuilder bodyBuilder = new(); bodyBuilder.Append($"{notification.Message}

"); bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=mrb/{notification.MRB.MRBNumber} to view the MRB."); await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString()); } catch (Exception ex) { _logger.LogError($"Unable to send notification to QA pre approver, because {ex.Message}"); throw; } } public async Task DeleteMRB(int mrbNumber) { try { _logger.LogInformation($"Attempting to delete MRB# {mrbNumber}"); if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number"); string sql = $"delete from MRB where MRBNumber={mrbNumber}"; int rowsAffected = await _dalService.ExecuteAsync(sql); if (rowsAffected <= 0) throw new Exception($"MRB {mrbNumber} not deleted from database"); IEnumerable? allMrbs = _cache.Get>("allMrbs"); if (allMrbs is not null) { List mrbList = allMrbs.ToList(); mrbList.RemoveAll(m => m.MRBNumber == mrbNumber); _cache.Set("allMrbs", mrbList, DateTimeOffset.Now.AddHours(1)); } } catch (Exception ex) { _logger.LogError($"Unable to delete MRB {mrbNumber}, because {ex.Message}"); throw; } } public async Task ConvertActionsToCsvFile(int mrbNumber, string path) { try { _logger.LogInformation($"Attempting to convert MRB {mrbNumber} actions to a CSV file"); if (!(await MRBNumberIsValid(mrbNumber))) throw new ArgumentException($"{mrbNumber} is not a valid "); if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Path cannot be null or empty"); if (File.Exists(path)) File.Delete(path); string? directoryPath = Path.GetDirectoryName(path); if (!string.IsNullOrWhiteSpace(directoryPath)) Directory.CreateDirectory(directoryPath); IEnumerable actions = await GetMRBActionsForMRB(mrbNumber, false); DataTable dt = await ConvertActionsToDataTable(actions); using StreamWriter sw = new StreamWriter(path, false); for (int i = 0; i < dt.Columns.Count; i++) { sw.Write(dt.Columns[i]); if (i < dt.Columns.Count - 1) sw.Write(","); } sw.Write(sw.NewLine); foreach (DataRow dr in dt.Rows) { for (int i = 0; i < dt.Columns.Count; i++) { if (!Convert.IsDBNull(dr[i])) { string? value = dr[i].ToString(); if (value is null) { sw.Write(""); } else if (value.Contains(',')) { value = String.Format("\"{0}\"", value); sw.Write(value); } else { sw.Write(dr[i].ToString()); } } if (i < dt.Columns.Count - 1) { sw.Write(","); } } sw.Write(sw.NewLine); } sw.Close(); } catch (Exception ex) { _logger.LogError($"Unable to convert MRB {mrbNumber} actions to a CSV file, because {ex.Message}"); throw; } } private async Task ConvertActionsToDataTable(IEnumerable actions) { try { _logger.LogInformation("Attempting to convert MRB actions to a DataTable"); if (actions is null) throw new ArgumentNullException("MRB actions cannot be null"); DataTable dt = new(); if (actions.Count() > 0 && actions.First().Action.Equals("Convert", StringComparison.InvariantCultureIgnoreCase)) { dt.Columns.Add("Action", typeof(string)); dt.Columns.Add("From Customer", typeof(string)); dt.Columns.Add("From Part Number", typeof(string)); dt.Columns.Add("Batch Number / Lot Number", typeof(string)); dt.Columns.Add("Qty", typeof(string)); dt.Columns.Add("To Customer", typeof(string)); dt.Columns.Add("To Part Number", typeof(string)); dt.Columns.Add("Assigned Date", typeof(string)); dt.Columns.Add("Completed Date", typeof(string)); dt.Columns.Add("Completed By", typeof(string)); foreach (MRBAction action in actions) { if (action.CompletedByUser is null && action.CompletedByUserID > 0) action.CompletedByUser = await _userService.GetUserByUserId(action.CompletedByUserID); string convertFromCustomer = string.Empty; string convertFromPart = string.Empty; string convertToCustomer = string.Empty; string convertToPart = string.Empty; string[] convertFrom = action.ConvertFrom.Split(" "); if (convertFrom.Length > 1) { convertFromCustomer = convertFrom[0]; foreach (string partStr in convertFrom.Skip(1)) convertFromPart += partStr; } string[] convertTo = action.ConvertTo.Split(" "); if (convertTo.Length > 1) { convertToCustomer = convertTo[0]; foreach (string partStr in convertTo.Skip(1)) convertToPart += partStr; } dt.Rows.Add(action.Action, convertFromCustomer, convertFromPart, action.Quantity.ToString(), convertToCustomer, convertToPart, DateTimeUtilities.GetDateAsStringMinDefault(action.AssignedDate), DateTimeUtilities.GetDateAsStringMaxDefault(action.CompletedDate), action.CompletedByUser is null ? "" : action.CompletedByUser.GetFullName()); } } else { dt.Columns.Add("Action", typeof(string)); dt.Columns.Add("Customer", typeof(string)); dt.Columns.Add("Qty", typeof(string)); dt.Columns.Add("Convert From", typeof(string)); dt.Columns.Add("Convert To", typeof(string)); dt.Columns.Add("Part Number", typeof(string)); dt.Columns.Add("Batch Number / Lot Number", typeof(string)); dt.Columns.Add("Justification", typeof(string)); dt.Columns.Add("Assigned Date", typeof(string)); dt.Columns.Add("Completed Date", typeof(string)); dt.Columns.Add("Completed By", typeof(string)); foreach (MRBAction action in actions) { if (action.CompletedByUser is null && action.CompletedByUserID > 0) action.CompletedByUser = await _userService.GetUserByUserId(action.CompletedByUserID); dt.Rows.Add(action.Action, action.Customer, action.Quantity.ToString(), action.ConvertFrom, action.ConvertTo, action.PartNumber, action.LotNumber, action.Justification, DateTimeUtilities.GetDateAsStringMinDefault(action.AssignedDate), DateTimeUtilities.GetDateAsStringMaxDefault(action.CompletedDate), action.CompletedByUser is null ? "" : action.CompletedByUser.GetFullName()); } } return dt; } catch (Exception ex) { _logger.LogError($"Unable to convert MRB actions to a DataTable, because {ex.Message}"); throw; } } private async Task SaveAttachmentInDb(IFormFile file, string path, int mrbNumber) { try { _logger.LogInformation($"Attempting to save attachment to database"); if (file is null) throw new ArgumentNullException("File cannot be null"); if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Path cannot be null or empty"); if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number"); StringBuilder queryBuilder = new(); queryBuilder.Append("insert into MRBAttachment (MRBNumber, FileName, UploadDate, Path) "); queryBuilder.Append($"values ({mrbNumber}, '{file.FileName}', "); queryBuilder.Append($"'{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}', '{path}');"); int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString()); if (rowsAffected <= 0) throw new Exception("Unable to insert attachment in database"); } catch (Exception ex) { _logger.LogError($"An exception occurred when attempting to save file to DB. Exception: {ex.Message}"); throw; } } private async Task SaveActionAttachmentInDb(IFormFile file, string path, int actionId) { try { _logger.LogInformation($"Attempting to save action attachment to database"); if (file is null) throw new ArgumentNullException("File cannot be null"); if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Path cannot be null or empty"); if (actionId <= 0) throw new ArgumentException($"{actionId} is not a valid MRB action ID"); StringBuilder queryBuilder = new(); queryBuilder.Append("insert into MRBActionAttachment (ActionID, FileName, UploadDate, Path) "); queryBuilder.Append($"values ({actionId}, '{file.FileName}', "); queryBuilder.Append($"'{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}', '{path}');"); int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString()); if (rowsAffected <= 0) throw new Exception("Unable to insert action attachment in database"); } catch (Exception ex) { _logger.LogError($"An exception occurred when attempting to save file to DB. Exception: {ex.Message}"); throw; } } }