Compare commits
	
		
			4 Commits
		
	
	
		
			05-23-b
			...
			ae8710e6d8
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| ae8710e6d8 | |||
| cc4781b990 | |||
| 4871668a90 | |||
| 65a433e9ab | 
| @ -1,6 +1,7 @@ | |||||||
| using System; | using System; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using System.Configuration; | using System.Configuration; | ||||||
|  | using System.IO; | ||||||
| using System.Linq; | using System.Linq; | ||||||
| using System.Web; | using System.Web; | ||||||
| using System.Web.Mvc; | using System.Web.Mvc; | ||||||
| @ -21,7 +22,7 @@ namespace Fab2ApprovalSystem.Controllers; | |||||||
| [Authorize] | [Authorize] | ||||||
| [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")] | [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")] | ||||||
| [SessionExpireFilter] | [SessionExpireFilter] | ||||||
| public class ECNController : PdfViewController { | public class ECNController : Controller { | ||||||
|  |  | ||||||
|     private const string ECN_PREFIX = "ECN_"; |     private const string ECN_PREFIX = "ECN_"; | ||||||
|     private const string TECN_PREFIX = "TECN_"; |     private const string TECN_PREFIX = "TECN_"; | ||||||
| @ -669,9 +670,6 @@ public class ECNController : PdfViewController { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// Get a list of Approvers and the status |  | ||||||
|     /// </summary> |  | ||||||
|     public ActionResult GetApproversList([DataSourceRequest] DataSourceRequest request, int issueID, byte step, bool isTECN, bool isEmergrncyTECN) { |     public ActionResult GetApproversList([DataSourceRequest] DataSourceRequest request, int issueID, byte step, bool isTECN, bool isEmergrncyTECN) { | ||||||
|         int isITARCompliant = 0; |         int isITARCompliant = 0; | ||||||
|         ECN ecn = new ECN(); |         ECN ecn = new ECN(); | ||||||
| @ -893,10 +891,12 @@ public class ECNController : PdfViewController { | |||||||
|             if (!di.Exists) |             if (!di.Exists) | ||||||
|                 di.Create(); |                 di.Create(); | ||||||
|  |  | ||||||
|             // To render a PDF instead of an HTML, all we need to do is call ViewPdf instead of View. This |             string htmlText; | ||||||
|             // requires the controller to be inherited from MyController instead of MVC's Controller. |             string pageTitle = string.Empty; | ||||||
|             SavePdf(ecnFolderPath + "\\ECNForm_" + outputFileName, "ECNPdf", ecn); |             htmlText = RenderViewToString("ECNPdf", ecn); | ||||||
|             SavePdf(ecnFolderPath + "\\ECNApprovalLog_" + outputFileName, "ECNApprovalPdf", ecn); |             StandardPdfRenderer.WritePortableDocumentFormatToFile(pageTitle, htmlText, $"{ecnFolderPath}\\ECNForm_{outputFileName}"); | ||||||
|  |             htmlText = RenderViewToString("ECNApprovalPdf", ecn); | ||||||
|  |             StandardPdfRenderer.WritePortableDocumentFormatToFile(pageTitle, htmlText, $"{ecnFolderPath}\\ECNApprovalLog_{outputFileName}"); | ||||||
|         } catch (Exception ex) { |         } catch (Exception ex) { | ||||||
|             EventLogDMO.Add(new WinEventLog() { IssueID = ecnNumber, UserID = @User.Identity.Name, DocumentType = "ECN", OperationType = "Generate PDF", Comments = ex.Message }); |             EventLogDMO.Add(new WinEventLog() { IssueID = ecnNumber, UserID = @User.Identity.Name, DocumentType = "ECN", OperationType = "Generate PDF", Comments = ex.Message }); | ||||||
|             ecn = null; |             ecn = null; | ||||||
| @ -906,6 +906,24 @@ public class ECNController : PdfViewController { | |||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private string RenderViewToString(string viewName, ECNPdf ecnPdf) { | ||||||
|  |         string result; | ||||||
|  |         ViewData.Model = ecnPdf; | ||||||
|  |         using (StringWriter writer = new()) { | ||||||
|  |             try { | ||||||
|  |                 ViewEngineResult viewResult = ViewEngines.Engines.FindView(ControllerContext, viewName, string.Empty); | ||||||
|  |                 if (viewResult is null) | ||||||
|  |                     return $"A view with the name '{viewName}' could not be found"; | ||||||
|  |                 ViewContext viewContext = new(ControllerContext, viewResult.View, ViewData, TempData, writer); | ||||||
|  |                 viewResult.View.Render(viewContext, writer); | ||||||
|  |                 result = writer.GetStringBuilder().ToString(); | ||||||
|  |             } catch (Exception ex) { | ||||||
|  |                 result = $"Failed - {ex.Message}"; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public bool GenerateECNPdfDifferentLocation(int ecnNumber, int folderName) { |     public bool GenerateECNPdfDifferentLocation(int ecnNumber, int folderName) { | ||||||
|         ECNPdf ecn = new ECNPdf(); |         ECNPdf ecn = new ECNPdf(); | ||||||
|         try { |         try { | ||||||
| @ -923,9 +941,9 @@ public class ECNController : PdfViewController { | |||||||
|             if (!di.Exists) |             if (!di.Exists) | ||||||
|                 di.Create(); |                 di.Create(); | ||||||
|  |  | ||||||
|             // To render a PDF instead of an HTML, all we need to do is call ViewPdf instead of View. This |             string pageTitle = string.Empty; | ||||||
|             // requires the controller to be inherited from MyController instead of MVC's Controller. |             string htmlText = RenderViewToString("ECNPdf", ecn); | ||||||
|             SavePdf(ecnFolderPath + "\\ECNForm_" + outputFileName, "ECNPdf", ecn); |             StandardPdfRenderer.WritePortableDocumentFormatToFile(pageTitle, htmlText, $"{ecnFolderPath}\\ECNForm_{outputFileName}"); | ||||||
|         } catch (Exception ex) { |         } catch (Exception ex) { | ||||||
|             EventLogDMO.Add(new WinEventLog() { IssueID = ecnNumber, UserID = @User.Identity.Name, DocumentType = "ECN", OperationType = "Generate PDF", Comments = ex.Message }); |             EventLogDMO.Add(new WinEventLog() { IssueID = ecnNumber, UserID = @User.Identity.Name, DocumentType = "ECN", OperationType = "Generate PDF", Comments = ex.Message }); | ||||||
|             ecn = null; |             ecn = null; | ||||||
| @ -941,9 +959,14 @@ public class ECNController : PdfViewController { | |||||||
|             ecn = ecnDMO.GetECNPdf(ecnNumber); |             ecn = ecnDMO.GetECNPdf(ecnNumber); | ||||||
|             ViewBag.Category = ecnDMO.GetCategoryID(ecn); |             ViewBag.Category = ecnDMO.GetCategoryID(ecn); | ||||||
|             ViewBag.TrainingNotificationTo = ecnDMO.GetTrainingNotificationTo(ecn, trainingDMO); |             ViewBag.TrainingNotificationTo = ecnDMO.GetTrainingNotificationTo(ecn, trainingDMO); | ||||||
|             // To render a PDF instead of an HTML, all we need to do is call ViewPdf instead of View. This |             string pageTitle = string.Empty; | ||||||
|             // requires the controller to be inherited from MyController instead of MVC's Controller. |             string htmlText = RenderViewToString("ECNPdf", ecn); | ||||||
|             return this.ViewPdf("", "ECNPdf", ecn); |             if (System.Diagnostics.Debugger.IsAttached) { | ||||||
|  |                 return Content(htmlText, "text/html"); | ||||||
|  |             } else { | ||||||
|  |                 byte[] buffer = StandardPdfRenderer.GetPortableDocumentFormatBytes(pageTitle, htmlText); | ||||||
|  |                 return new BinaryContentResult(buffer, "application/pdf"); | ||||||
|  |             } | ||||||
|         } catch (Exception ex) { |         } catch (Exception ex) { | ||||||
|             EventLogDMO.Add(new WinEventLog() { IssueID = ecnNumber, UserID = @User.Identity.Name, DocumentType = "ECN", OperationType = "Print PDF", Comments = ex.Message }); |             EventLogDMO.Add(new WinEventLog() { IssueID = ecnNumber, UserID = @User.Identity.Name, DocumentType = "ECN", OperationType = "Print PDF", Comments = ex.Message }); | ||||||
|             ecn = null; |             ecn = null; | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| using System; | using System; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using System.Configuration; | using System.Configuration; | ||||||
|  | using System.IO; | ||||||
| using System.Linq; | using System.Linq; | ||||||
| using System.Threading; | using System.Threading; | ||||||
| using System.Web; | using System.Web; | ||||||
| @ -20,7 +21,7 @@ namespace Fab2ApprovalSystem.Controllers; | |||||||
| [Authorize] | [Authorize] | ||||||
| [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")] | [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")] | ||||||
| [SessionExpireFilter] | [SessionExpireFilter] | ||||||
| public class LotTravelerController : PdfViewController { | public class LotTravelerController : Controller { | ||||||
|  |  | ||||||
|     LotTravelerDMO LotTravDMO = new LotTravelerDMO(); |     LotTravelerDMO LotTravDMO = new LotTravelerDMO(); | ||||||
|     string docTypeString = "LotTraveler"; |     string docTypeString = "LotTraveler"; | ||||||
| @ -199,9 +200,14 @@ public class LotTravelerController : PdfViewController { | |||||||
|         try { |         try { | ||||||
|             workRequest = LotTravDMO.GetLTWorkRequestItemPDF(workRequestID); |             workRequest = LotTravDMO.GetLTWorkRequestItemPDF(workRequestID); | ||||||
|  |  | ||||||
|             // To render a PDF instead of an HTML, all we need to do is call ViewPdf instead of View. This |             string pageTitle = string.Empty; | ||||||
|             // requires the controller to be inherited from MyController instead of MVC's Controller. |             string htmlText = RenderViewToString("WorkRequestPDF", workRequest); | ||||||
|             return this.ViewPdf("", "WorkRequestPDF", workRequest); |             if (System.Diagnostics.Debugger.IsAttached) { | ||||||
|  |                 return Content(htmlText, "text/html"); | ||||||
|  |             } else { | ||||||
|  |                 byte[] buffer = StandardPdfRenderer.GetPortableDocumentFormatBytes(pageTitle, htmlText); | ||||||
|  |                 return new BinaryContentResult(buffer, "application/pdf"); | ||||||
|  |             } | ||||||
|         } catch (Exception ex) { |         } catch (Exception ex) { | ||||||
|             Functions.WriteEvent(_AppSettings, @User.Identity.Name + "\r\n DisplayWorkRequestPDF - LotTraveler\r\n" + ex.InnerException.ToString(), System.Diagnostics.EventLogEntryType.Error); |             Functions.WriteEvent(_AppSettings, @User.Identity.Name + "\r\n DisplayWorkRequestPDF - LotTraveler\r\n" + ex.InnerException.ToString(), System.Diagnostics.EventLogEntryType.Error); | ||||||
|             EventLogDMO.Add(new WinEventLog() { IssueID = workRequest.SWRNumber, UserID = @User.Identity.Name, DocumentType = "LotTravler", OperationType = "Generate PDF", Comments = ex.Message }); |             EventLogDMO.Add(new WinEventLog() { IssueID = workRequest.SWRNumber, UserID = @User.Identity.Name, DocumentType = "LotTravler", OperationType = "Generate PDF", Comments = ex.Message }); | ||||||
| @ -210,6 +216,24 @@ public class LotTravelerController : PdfViewController { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private string RenderViewToString(string viewName, object model) { | ||||||
|  |         string result; | ||||||
|  |         ViewData.Model = model; | ||||||
|  |         using (StringWriter writer = new()) { | ||||||
|  |             try { | ||||||
|  |                 ViewEngineResult viewResult = ViewEngines.Engines.FindView(ControllerContext, viewName, string.Empty); | ||||||
|  |                 if (viewResult is null) | ||||||
|  |                     return $"A view with the name '{viewName}' could not be found"; | ||||||
|  |                 ViewContext viewContext = new(ControllerContext, viewResult.View, ViewData, TempData, writer); | ||||||
|  |                 viewResult.View.Render(viewContext, writer); | ||||||
|  |                 result = writer.GetStringBuilder().ToString(); | ||||||
|  |             } catch (Exception ex) { | ||||||
|  |                 result = $"Failed - {ex.Message}"; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public ActionResult WorkRequestRevision(int workRequestID) { |     public ActionResult WorkRequestRevision(int workRequestID) { | ||||||
|         int isITARCompliant = 1; |         int isITARCompliant = 1; | ||||||
|         LTWorkRequest workRequest = new LTWorkRequest(); |         LTWorkRequest workRequest = new LTWorkRequest(); | ||||||
| @ -251,9 +275,7 @@ public class LotTravelerController : PdfViewController { | |||||||
|  |  | ||||||
|         return Content("Successfully Saved"); |         return Content("Successfully Saved"); | ||||||
|     } |     } | ||||||
|     /// <summary> |  | ||||||
|     ///  |  | ||||||
|     /// </summary> |  | ||||||
|     public JsonResult GetBaseFlowLocations(string baseFlow) { |     public JsonResult GetBaseFlowLocations(string baseFlow) { | ||||||
|         List<BaseFlowLocation> loclist = LotTravDMO.GetBaseFlowLocations(baseFlow); |         List<BaseFlowLocation> loclist = LotTravDMO.GetBaseFlowLocations(baseFlow); | ||||||
|         return Json(loclist, JsonRequestBehavior.AllowGet); |         return Json(loclist, JsonRequestBehavior.AllowGet); | ||||||
| @ -332,9 +354,6 @@ public class LotTravelerController : PdfViewController { | |||||||
|         return Content(newWorkRequestID.ToString()); |         return Content(newWorkRequestID.ToString()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// For the Revison |  | ||||||
|     /// </summary> |  | ||||||
|     public ActionResult UpdateMaterialDetailRevision(LTWorkRequest model) { |     public ActionResult UpdateMaterialDetailRevision(LTWorkRequest model) { | ||||||
|         var modelMaterialDetail = model.LTMaterial; |         var modelMaterialDetail = model.LTMaterial; | ||||||
|         int previousMaterialID = model.LTMaterial.ID; |         int previousMaterialID = model.LTMaterial.ID; | ||||||
| @ -1232,15 +1251,19 @@ public class LotTravelerController : PdfViewController { | |||||||
|         LotTravDMO.DeleteLot(ltLotID); |         LotTravDMO.DeleteLot(ltLotID); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// |  | ||||||
|     public ActionResult DisplayLotTravlerPdf(int ltLotID, int revisionNumber) { |     public ActionResult DisplayLotTravlerPdf(int ltLotID, int revisionNumber) { | ||||||
|         LotTravelerPdf traveler = new LotTravelerPdf(); |         LotTravelerPdf traveler = new LotTravelerPdf(); | ||||||
|         try { |         try { | ||||||
|             traveler = LotTravDMO.GetLotTravlerPdf(ltLotID, revisionNumber); |             traveler = LotTravDMO.GetLotTravlerPdf(ltLotID, revisionNumber); | ||||||
|  |  | ||||||
|             // To render a PDF instead of an HTML, all we need to do is call ViewPdf instead of View. This |             string pageTitle = string.Empty; | ||||||
|             // requires the controller to be inherited from MyController instead of MVC's Controller. |             string htmlText = RenderViewToString("LotTravelerPDF", traveler); | ||||||
|             return this.ViewPdf("", "LotTravelerPDF", traveler); |             if (System.Diagnostics.Debugger.IsAttached) { | ||||||
|  |                 return Content(htmlText, "text/html"); | ||||||
|  |             } else { | ||||||
|  |                 byte[] buffer = StandardPdfRenderer.GetPortableDocumentFormatBytes(pageTitle, htmlText); | ||||||
|  |                 return new BinaryContentResult(buffer, "application/pdf"); | ||||||
|  |             } | ||||||
|         } catch (Exception ex) { |         } catch (Exception ex) { | ||||||
|             EventLogDMO.Add(new WinEventLog() { IssueID = traveler.SWRNumber, UserID = @User.Identity.Name, DocumentType = "LotTraveler", OperationType = "Generate PDF", Comments = ex.Message }); |             EventLogDMO.Add(new WinEventLog() { IssueID = traveler.SWRNumber, UserID = @User.Identity.Name, DocumentType = "LotTraveler", OperationType = "Generate PDF", Comments = ex.Message }); | ||||||
|             traveler = null; |             traveler = null; | ||||||
|  | |||||||
| @ -325,9 +325,6 @@ | |||||||
|     <Compile Include="Models\WinEventLogModel.cs" /> |     <Compile Include="Models\WinEventLogModel.cs" /> | ||||||
|     <Compile Include="Models\WorkFlowModels.cs" /> |     <Compile Include="Models\WorkFlowModels.cs" /> | ||||||
|     <Compile Include="PdfGenerator\BinaryContentResult.cs" /> |     <Compile Include="PdfGenerator\BinaryContentResult.cs" /> | ||||||
|     <Compile Include="PdfGenerator\FakeView.cs" /> |  | ||||||
|     <Compile Include="PdfGenerator\HtmlViewRenderer.cs" /> |  | ||||||
|     <Compile Include="PdfGenerator\PdfViewController.cs" /> |  | ||||||
|     <Compile Include="PdfGenerator\PrintHeaderFooter.cs" /> |     <Compile Include="PdfGenerator\PrintHeaderFooter.cs" /> | ||||||
|     <Compile Include="PdfGenerator\StandardPdfRenderer.cs" /> |     <Compile Include="PdfGenerator\StandardPdfRenderer.cs" /> | ||||||
|     <Compile Include="Properties\AssemblyInfo.cs" /> |     <Compile Include="Properties\AssemblyInfo.cs" /> | ||||||
|  | |||||||
| @ -1,28 +0,0 @@ | |||||||
| #if !NET8 |  | ||||||
|  |  | ||||||
| // -------------------------------------------------------------------------------------------------------------------- |  | ||||||
| // <copyright file="FakeView.cs" company="SemanticArchitecture"> |  | ||||||
| //   http://www.SemanticArchitecture.net pkalkie@gmail.com |  | ||||||
| // </copyright> |  | ||||||
| // <summary> |  | ||||||
| //   Defines the FakeView type. |  | ||||||
| // </summary> |  | ||||||
| // -------------------------------------------------------------------------------------------------------------------- |  | ||||||
|  |  | ||||||
| namespace Fab2ApprovalSystem.PdfGenerator { |  | ||||||
|     using System; |  | ||||||
|     using System.IO; |  | ||||||
|     using System.Web.Mvc; |  | ||||||
|  |  | ||||||
|     public class FakeView : IView { |  | ||||||
|         #region IView Members |  | ||||||
|  |  | ||||||
|         public void Render(ViewContext viewContext, TextWriter writer) { |  | ||||||
|             throw new NotImplementedException(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         #endregion |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #endif |  | ||||||
| @ -1,49 +0,0 @@ | |||||||
| #if !NET8 |  | ||||||
|  |  | ||||||
| // -------------------------------------------------------------------------------------------------------------------- |  | ||||||
| // <copyright file="HtmlViewRenderer.cs" company="SemanticArchitecture"> |  | ||||||
| //   http://www.SemanticArchitecture.net pkalkie@gmail.com |  | ||||||
| // </copyright> |  | ||||||
| // <summary> |  | ||||||
| //   This class is responsible for rendering a HTML view to a string. |  | ||||||
| // </summary> |  | ||||||
| // -------------------------------------------------------------------------------------------------------------------- |  | ||||||
|  |  | ||||||
| namespace Fab2ApprovalSystem.PdfGenerator { |  | ||||||
|     using System.IO; |  | ||||||
|     using System.Text; |  | ||||||
|     using System.Web; |  | ||||||
|     using System.Web.Mvc; |  | ||||||
|     using System.Web.Mvc.Html; |  | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// This class is responsible for rendering a HTML view into a string.  |  | ||||||
|     /// </summary> |  | ||||||
|     public class HtmlViewRenderer { |  | ||||||
|         public string RenderViewToString(Controller controller, string viewName, object viewData) { |  | ||||||
|             var renderedView = new StringBuilder(); |  | ||||||
|             using (var responseWriter = new StringWriter(renderedView)) { |  | ||||||
|                 var fakeResponse = new HttpResponse(responseWriter); |  | ||||||
|                 var fakeContext = new HttpContext(HttpContext.Current.Request, fakeResponse); |  | ||||||
|                 var fakeControllerContext = new ControllerContext(new HttpContextWrapper(fakeContext), controller.ControllerContext.RouteData, controller.ControllerContext.Controller); |  | ||||||
|  |  | ||||||
|                 var oldContext = HttpContext.Current; |  | ||||||
|                 HttpContext.Current = fakeContext; |  | ||||||
|  |  | ||||||
|                 using (var viewPage = new ViewPage()) { |  | ||||||
|                     var html = new HtmlHelper(CreateViewContext(responseWriter, fakeControllerContext), viewPage); |  | ||||||
|                     html.RenderPartial(viewName, viewData); |  | ||||||
|                     HttpContext.Current = oldContext; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             return renderedView.ToString(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         private static ViewContext CreateViewContext(TextWriter responseWriter, ControllerContext fakeControllerContext) { |  | ||||||
|             return new ViewContext(fakeControllerContext, new FakeView(), new ViewDataDictionary(), new TempDataDictionary(), responseWriter); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #endif |  | ||||||
| @ -1,54 +0,0 @@ | |||||||
| #if !NET8 |  | ||||||
|  |  | ||||||
| // -------------------------------------------------------------------------------------------------------------------- |  | ||||||
| // <copyright file="PdfViewController.cs" company="SemanticArchitecture"> |  | ||||||
| //   http://www.SemanticArchitecture.net pkalkie@gmail.com |  | ||||||
| // </copyright> |  | ||||||
| // <summary> |  | ||||||
| //   Extends the controller with functionality for rendering PDF views |  | ||||||
| // </summary> |  | ||||||
| // -------------------------------------------------------------------------------------------------------------------- |  | ||||||
|  |  | ||||||
| namespace Fab2ApprovalSystem.PdfGenerator { |  | ||||||
|     using System.Web.Mvc; |  | ||||||
|     using System.IO; |  | ||||||
|  |  | ||||||
|     /// <summary> |  | ||||||
|     /// Extends the controller with functionality for rendering PDF views |  | ||||||
|     /// </summary> |  | ||||||
|     public class PdfViewController : Controller { |  | ||||||
|         private readonly HtmlViewRenderer htmlViewRenderer; |  | ||||||
|         private readonly StandardPdfRenderer standardPdfRenderer; |  | ||||||
|  |  | ||||||
|         public PdfViewController() { |  | ||||||
|             this.htmlViewRenderer = new HtmlViewRenderer(); |  | ||||||
|             this.standardPdfRenderer = new StandardPdfRenderer(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         protected ActionResult ViewPdf(string pageTitle, string viewName, object model) { |  | ||||||
|             // Render the view html to a string. |  | ||||||
|             string htmlText = this.htmlViewRenderer.RenderViewToString(this, viewName, model); |  | ||||||
|  |  | ||||||
|             // Let the html be rendered into a PDF document through iTextSharp. |  | ||||||
|             byte[] buffer = standardPdfRenderer.Render(htmlText, pageTitle); |  | ||||||
|  |  | ||||||
|             // Return the PDF as a binary stream to the client. |  | ||||||
|             return new BinaryContentResult(buffer, "application/pdf"); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         protected void SavePdf(string fileName, string viewName, object model) { |  | ||||||
|             // Render the view html to a string. |  | ||||||
|             string htmlText = this.htmlViewRenderer.RenderViewToString(this, viewName, model); |  | ||||||
|  |  | ||||||
|             // Let the html be rendered into a PDF document through iTextSharp. |  | ||||||
|             byte[] buffer = standardPdfRenderer.Render(htmlText, ""); |  | ||||||
|  |  | ||||||
|             using (FileStream fs = new FileStream(fileName, FileMode.Create)) { |  | ||||||
|                 fs.Write(buffer, 0, buffer.Length); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #endif |  | ||||||
| @ -1,27 +1,38 @@ | |||||||
| #if !NET8 |  | ||||||
| using iTextSharp.text; | using iTextSharp.text; | ||||||
| using iTextSharp.text.html.simpleparser; | using iTextSharp.text.html.simpleparser; | ||||||
| using iTextSharp.text.pdf; | using iTextSharp.text.pdf; | ||||||
| #endif |  | ||||||
|  |  | ||||||
| using System.IO; | using System.IO; | ||||||
|  |  | ||||||
| namespace Fab2ApprovalSystem.PdfGenerator; | namespace Fab2ApprovalSystem.PdfGenerator; | ||||||
|  |  | ||||||
| /// <summary> |  | ||||||
| /// This class is responsible for rendering a html text string to a PDF document using the html renderer of iTextSharp. |  | ||||||
| /// </summary> |  | ||||||
| public class StandardPdfRenderer { | public class StandardPdfRenderer { | ||||||
|  |  | ||||||
|     private const int HorizontalMargin = 40; |     private const int HorizontalMargin = 40; | ||||||
|     private const int VerticalMargin = 40; |     private const int VerticalMargin = 40; | ||||||
|  |  | ||||||
|     public byte[] Render(string htmlText, string pageTitle) { |     public static byte[] GetPortableDocumentFormatBytes(string pageTitle, string htmlText) { | ||||||
|         byte[] renderedBuffer; |         byte[] results; | ||||||
|  |         using (MemoryStream memoryStream = GetPortableDocumentFormat(pageTitle, htmlText)) { | ||||||
|  |             results = new byte[memoryStream.Position]; | ||||||
|  |             memoryStream.Position = 0; | ||||||
|  |             memoryStream.Read(results, 0, results.Length); | ||||||
|  |         } | ||||||
|  |         return results; | ||||||
|  |     } | ||||||
|  |  | ||||||
|         using (MemoryStream outputMemoryStream = new()) { |     public static void WritePortableDocumentFormatToFile(string pageTitle, string htmlText, string path) { | ||||||
| #if !NET8 |         using (MemoryStream memoryStream = GetPortableDocumentFormat(pageTitle, htmlText)) { | ||||||
|             using (Document pdfDocument = new Document(PageSize.A4, HorizontalMargin, HorizontalMargin, VerticalMargin, VerticalMargin)) { |             using (FileStream fileStream = new(path, FileMode.Create)) { | ||||||
|                 PdfWriter pdfWriter = PdfWriter.GetInstance(pdfDocument, outputMemoryStream); |                 memoryStream.CopyTo(fileStream); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static MemoryStream GetPortableDocumentFormat(string pageTitle, string htmlText) { | ||||||
|  |         MemoryStream result = new(); | ||||||
|  |         using (Document pdfDocument = new Document(PageSize.A4, HorizontalMargin, HorizontalMargin, VerticalMargin, VerticalMargin)) { | ||||||
|  |             using (PdfWriter pdfWriter = PdfWriter.GetInstance(pdfDocument, result)) { | ||||||
|                 pdfWriter.CloseStream = false; |                 pdfWriter.CloseStream = false; | ||||||
|                 pdfWriter.PageEvent = new PrintHeaderFooter { Title = pageTitle }; |                 pdfWriter.PageEvent = new PrintHeaderFooter { Title = pageTitle }; | ||||||
|                 pdfDocument.Open(); |                 pdfDocument.Open(); | ||||||
| @ -30,15 +41,9 @@ public class StandardPdfRenderer { | |||||||
|                         htmlWorker.Parse(htmlViewReader); |                         htmlWorker.Parse(htmlViewReader); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|             } |             } | ||||||
| #endif |  | ||||||
|  |  | ||||||
|             renderedBuffer = new byte[outputMemoryStream.Position]; |  | ||||||
|             outputMemoryStream.Position = 0; |  | ||||||
|             outputMemoryStream.Read(renderedBuffer, 0, renderedBuffer.Length); |  | ||||||
|         } |         } | ||||||
|  |         return result; | ||||||
|         return renderedBuffer; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
| @ -210,7 +210,7 @@ | |||||||
|                             @Html.TextBoxFor(model => model.Title, new { id = "txtTitle", @class = "k-textbox", style = "width:100%", disabled = "disabled" }) |                             @Html.TextBoxFor(model => model.Title, new { id = "txtTitle", @class = "k-textbox", style = "width:100%", disabled = "disabled" }) | ||||||
|                         </div> |                         </div> | ||||||
|                         <div class="col-sm-6 col-sm-offset-4" style="color: red;"> |                         <div class="col-sm-6 col-sm-offset-4" style="color: red;"> | ||||||
|                             * (DO NOT USE Rev I, O, S, X, and Z) |                             * (DO NOT USE Rev I, O, Q, S, X, and Z) | ||||||
|                         </div> |                         </div> | ||||||
|                         <div class="col-sm-6 col-sm-offset-4" style="color: red;"> |                         <div class="col-sm-6 col-sm-offset-4" style="color: red;"> | ||||||
|                             Revision Y is followed by AA-AY, AY is followed by BA-BY etc. YY is followed by AAA |                             Revision Y is followed by AA-AY, AY is followed by BA-BY etc. YY is followed by AAA | ||||||
|  | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -154,7 +154,7 @@ | |||||||
|                             @Html.TextBoxFor(model => model.Title, new { id = "txtTitle", @class = "k-textbox", style = "width:100%" }) |                             @Html.TextBoxFor(model => model.Title, new { id = "txtTitle", @class = "k-textbox", style = "width:100%" }) | ||||||
|                         </div> |                         </div> | ||||||
|                         <div class="col-sm-6 col-sm-offset-4" style="color: red;"> |                         <div class="col-sm-6 col-sm-offset-4" style="color: red;"> | ||||||
|                             * (DO NOT USE Rev I, O, S, X, and Z) |                             * (DO NOT USE Rev I, O, Q, S, X, and Z) | ||||||
|                         </div> |                         </div> | ||||||
|                         <div class="col-sm-6 col-sm-offset-4" style="color: red;"> |                         <div class="col-sm-6 col-sm-offset-4" style="color: red;"> | ||||||
|                             Revision Y is followed by AA-AY, AY is followed by BA-BY etc. YY is followed by AAA |                             Revision Y is followed by AA-AY, AY is followed by BA-BY etc. YY is followed by AAA | ||||||
|  | |||||||
| @ -260,7 +260,7 @@ | |||||||
|                             @Html.TextBoxFor(model => model.Title, new { id = "txtTitle", @class = "k-textbox", style = "width:100%", disabled = "disabled" }) |                             @Html.TextBoxFor(model => model.Title, new { id = "txtTitle", @class = "k-textbox", style = "width:100%", disabled = "disabled" }) | ||||||
|                         </div> |                         </div> | ||||||
|                         <div class="col-sm-6 col-sm-offset-4" style="color: red;"> |                         <div class="col-sm-6 col-sm-offset-4" style="color: red;"> | ||||||
|                             * (DO NOT USE Rev I, O, S, X, and Z) |                             * (DO NOT USE Rev I, O, Q, S, X, and Z) | ||||||
|                         </div> |                         </div> | ||||||
|                         <div class="col-sm-6 col-sm-offset-4" style="color: red;"> |                         <div class="col-sm-6 col-sm-offset-4" style="color: red;"> | ||||||
|                             Revision Y is followed by AA-AY, AY is followed by BA-BY etc. YY is followed by AAA |                             Revision Y is followed by AA-AY, AY is followed by BA-BY etc. YY is followed by AAA | ||||||
|  | |||||||
							
								
								
									
										55
									
								
								MesaFabApproval.API.Test/ApprovalServiceTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								MesaFabApproval.API.Test/ApprovalServiceTests.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | |||||||
|  | using System; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | using MesaFabApproval.API.Services; | ||||||
|  | using MesaFabApproval.Shared.Models; | ||||||
|  |  | ||||||
|  | using Microsoft.Extensions.Caching.Memory; | ||||||
|  | using Microsoft.Extensions.Logging; | ||||||
|  |  | ||||||
|  | using Moq; | ||||||
|  |  | ||||||
|  | using Xunit; | ||||||
|  |  | ||||||
|  | namespace MesaFabApproval.API.Test; | ||||||
|  |  | ||||||
|  | public class ApprovalServiceTests { | ||||||
|  |     private readonly Mock<ILogger<ApprovalService>> _loggerMock; | ||||||
|  |     private readonly Mock<IMemoryCache> _cacheMock; | ||||||
|  |     private readonly Mock<IDalService> _dalServiceMock; | ||||||
|  |     private readonly Mock<IUserService> _userServiceMock; | ||||||
|  |     private readonly ApprovalService _approvalService; | ||||||
|  |  | ||||||
|  |     public ApprovalServiceTests() { | ||||||
|  |         _loggerMock = new Mock<ILogger<ApprovalService>>(); | ||||||
|  |         _cacheMock = new Mock<IMemoryCache>(); | ||||||
|  |         _dalServiceMock = new Mock<IDalService>(); | ||||||
|  |         _userServiceMock = new Mock<IUserService>(); | ||||||
|  |         _approvalService = new ApprovalService(_loggerMock.Object, _cacheMock.Object, _dalServiceMock.Object, _userServiceMock.Object); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task DeleteApproval_ValidApprovalID_DeletesApproval() { | ||||||
|  |         int approvalID = 1; | ||||||
|  |         _dalServiceMock.Setup(d => d.ExecuteAsync(It.IsAny<string>(), It.IsAny<object>())).ReturnsAsync(1); | ||||||
|  |  | ||||||
|  |         await _approvalService.DeleteApproval(approvalID); | ||||||
|  |  | ||||||
|  |         _dalServiceMock.Verify(d => d.ExecuteAsync(It.IsAny<string>(), It.IsAny<object>()), Times.Once); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task DeleteApproval_InvalidApprovalID_ThrowsArgumentException() { | ||||||
|  |         int approvalID = 0; | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<ArgumentException>(() => _approvalService.DeleteApproval(approvalID)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task DeleteApproval_DeletionFails_ThrowsException() { | ||||||
|  |         int approvalID = 1; | ||||||
|  |         _dalServiceMock.Setup(d => d.ExecuteAsync(It.IsAny<string>(), It.IsAny<object>())).ReturnsAsync(0); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _approvalService.DeleteApproval(approvalID)); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										18
									
								
								MesaFabApproval.API.Test/MockMemoryCacheService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								MesaFabApproval.API.Test/MockMemoryCacheService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | |||||||
|  | using Microsoft.Extensions.Caching.Memory; | ||||||
|  |  | ||||||
|  | using Moq; | ||||||
|  |  | ||||||
|  | namespace MesaFabApproval.API.Test; | ||||||
|  |  | ||||||
|  | public static class MockMemoryCacheService { | ||||||
|  |     public static Mock<IMemoryCache> GetMemoryCache(object expectedValue) { | ||||||
|  |         Mock<IMemoryCache> mockMemoryCache = new Mock<IMemoryCache>(); | ||||||
|  |         mockMemoryCache | ||||||
|  |             .Setup(x => x.TryGetValue(It.IsAny<object>(), out expectedValue)) | ||||||
|  |             .Returns(true); | ||||||
|  |         mockMemoryCache | ||||||
|  |             .Setup(x => x.CreateEntry(It.IsAny<object>())) | ||||||
|  |             .Returns(Mock.Of<ICacheEntry>()); | ||||||
|  |         return mockMemoryCache; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										196
									
								
								MesaFabApproval.API.Test/PCRBFollowUpCommentsTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								MesaFabApproval.API.Test/PCRBFollowUpCommentsTests.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,196 @@ | |||||||
|  | using MesaFabApproval.API.Services; | ||||||
|  | using MesaFabApproval.Models; | ||||||
|  | using MesaFabApproval.Shared.Models; | ||||||
|  |  | ||||||
|  | using Microsoft.Extensions.Caching.Memory; | ||||||
|  | using Microsoft.Extensions.Logging; | ||||||
|  |  | ||||||
|  | using Moq; | ||||||
|  |  | ||||||
|  | namespace MesaFabApproval.API.Test; | ||||||
|  |  | ||||||
|  | public class PCRBFollowUpCommentsTests { | ||||||
|  |     private readonly Mock<ILogger<PCRBService>> _loggerMock; | ||||||
|  |     private readonly Mock<IDalService> _dalServiceMock; | ||||||
|  |     private readonly Mock<IMemoryCache> _cacheMock; | ||||||
|  |     private readonly Mock<IUserService> _userServiceMock; | ||||||
|  |     private readonly Mock<IApprovalService> _approvalServiceMock; | ||||||
|  |     private readonly Mock<ISmtpService> _smtpServiceMock; | ||||||
|  |     private readonly PCRBService _pcrbService; | ||||||
|  |     private readonly AppSettings _appSettings; | ||||||
|  |  | ||||||
|  |     private static PCRBFollowUpComment FOLLOW_UP_COMMENT = new PCRBFollowUpComment { | ||||||
|  |         PlanNumber = 1, | ||||||
|  |         FollowUpID = 1, | ||||||
|  |         Comment = "Comment", | ||||||
|  |         UserID = 1 | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     private static IEnumerable<PCRBFollowUpComment> FOLLOW_UP_COMMENTS = new List<PCRBFollowUpComment>() { FOLLOW_UP_COMMENT }; | ||||||
|  |  | ||||||
|  |     public PCRBFollowUpCommentsTests() { | ||||||
|  |         _loggerMock = new Mock<ILogger<PCRBService>>(); | ||||||
|  |         _dalServiceMock = new Mock<IDalService>(); | ||||||
|  |         _userServiceMock = new Mock<IUserService>(); | ||||||
|  |         _approvalServiceMock = new Mock<IApprovalService>(); | ||||||
|  |         _smtpServiceMock = new Mock<ISmtpService>(); | ||||||
|  |         _cacheMock = MockMemoryCacheService.GetMemoryCache(FOLLOW_UP_COMMENTS); | ||||||
|  |         _appSettings = new AppSettings( | ||||||
|  |             Company: "Infineon", | ||||||
|  |             DbConnectionString: "connectionString", | ||||||
|  |             JwtAudience: "audience", | ||||||
|  |             JwtIssuer: "issuer", | ||||||
|  |             JwtKey: "key", | ||||||
|  |             MrbAttachmentPath: "mrbAttachmentPath", | ||||||
|  |             PcrbAttachmentPath: "pcrbAttachmentPath", | ||||||
|  |             ShouldSendEmail: false, | ||||||
|  |             SiteBaseUrl: "siteBaseUrl", | ||||||
|  |             WorkingDirectoryName: "workingDirectoryName" | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         _pcrbService = new PCRBService( | ||||||
|  |             _loggerMock.Object, | ||||||
|  |             _dalServiceMock.Object, | ||||||
|  |             _cacheMock.Object, | ||||||
|  |             _userServiceMock.Object, | ||||||
|  |             _approvalServiceMock.Object, | ||||||
|  |             _smtpServiceMock.Object, | ||||||
|  |             _appSettings | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task CreateFollowUpComment_WithValidParam_ShouldCreateFollowUp() { | ||||||
|  |         _dalServiceMock.Setup(d => d.ExecuteAsync<PCRBFollowUpComment>(It.IsAny<string>(), FOLLOW_UP_COMMENT)) | ||||||
|  |             .ReturnsAsync(1); | ||||||
|  |  | ||||||
|  |         await _pcrbService.CreateFollowUpComment(FOLLOW_UP_COMMENT); | ||||||
|  |  | ||||||
|  |         _dalServiceMock.Verify(d => d.ExecuteAsync<PCRBFollowUpComment>(It.IsAny<string>(), FOLLOW_UP_COMMENT), Times.Once); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task CreateFollowUpComment_WithNullParam_ShouldThrowException() { | ||||||
|  |         await Assert.ThrowsAsync<ArgumentNullException>(() => _pcrbService.CreateFollowUpComment(null)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task CreateFollowUpComment_WithDatabaseFailure_ShouldThrowException() { | ||||||
|  |         _dalServiceMock.Setup(d => d.ExecuteAsync<PCRBFollowUpComment>(It.IsAny<string>(), FOLLOW_UP_COMMENT)) | ||||||
|  |             .ReturnsAsync(0); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _pcrbService.CreateFollowUpComment(FOLLOW_UP_COMMENT)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task CreateFollowUpComment_WithDatabaseException_ShouldThrowException() { | ||||||
|  |         _dalServiceMock.Setup(d => d.ExecuteAsync<PCRBFollowUpComment>(It.IsAny<string>(), FOLLOW_UP_COMMENT)) | ||||||
|  |             .Throws<Exception>(); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _pcrbService.CreateFollowUpComment(FOLLOW_UP_COMMENT)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task GetFollowUpCommentsByPlanNumber_WithCacheBypass_ShouldReturnFollowUps() { | ||||||
|  |         int planNumber = 1; | ||||||
|  |  | ||||||
|  |         _dalServiceMock.Setup(d => d.QueryAsync<PCRBFollowUpComment>(It.IsAny<string>(), It.IsAny<object>())) | ||||||
|  |             .ReturnsAsync(FOLLOW_UP_COMMENTS); | ||||||
|  |  | ||||||
|  |         IEnumerable<PCRBFollowUpComment> result = await _pcrbService.GetFollowUpCommentsByPlanNumber(planNumber, true); | ||||||
|  |  | ||||||
|  |         Assert.NotNull(result); | ||||||
|  |         Assert.Single(result); | ||||||
|  |         Assert.Equal(FOLLOW_UP_COMMENTS, result); | ||||||
|  |         _dalServiceMock.Verify(d => d.QueryAsync<PCRBFollowUpComment>(It.IsAny<string>(), It.IsAny<object>()), Times.Once); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task GetFollowUpCommentsByPlanNumber_WithCacheBypass_AndDatabaseException_ShouldThrowException() { | ||||||
|  |         int planNumber = 1; | ||||||
|  |  | ||||||
|  |         _dalServiceMock.Setup(d => d.QueryAsync<PCRBFollowUpComment>(It.IsAny<string>(), It.IsAny<object>())) | ||||||
|  |             .Throws<Exception>(); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _pcrbService.GetFollowUpCommentsByPlanNumber(planNumber, true)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task GetFollowUpCommentsByPlanNumber_WithoutCacheBypass_ShouldReturnFollowUps() { | ||||||
|  |         int planNumber = 1; | ||||||
|  |  | ||||||
|  |         IEnumerable<PCRBFollowUpComment> result = await _pcrbService.GetFollowUpCommentsByPlanNumber(planNumber, false); | ||||||
|  |  | ||||||
|  |         Assert.NotNull(result); | ||||||
|  |         Assert.Single(result); | ||||||
|  |         Assert.Equal(FOLLOW_UP_COMMENTS, result); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task UpdateFollowUpComment_WithValidParam_ShouldUpdateFollowUp() { | ||||||
|  |         _dalServiceMock.Setup(d => d.ExecuteAsync<PCRBFollowUpComment>(It.IsAny<string>(), FOLLOW_UP_COMMENT)) | ||||||
|  |             .ReturnsAsync(1); | ||||||
|  |  | ||||||
|  |         await _pcrbService.UpdateFollowUpComment(FOLLOW_UP_COMMENT); | ||||||
|  |  | ||||||
|  |         _dalServiceMock.Verify(d => d.ExecuteAsync<PCRBFollowUpComment>(It.IsAny<string>(), FOLLOW_UP_COMMENT), Times.Once); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task UpdateFollowUpComment_WithNullParam_ShouldThrowException() { | ||||||
|  |         await Assert.ThrowsAsync<ArgumentNullException>(() => _pcrbService.UpdateFollowUpComment(null)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task UpdateFollowUpComment_WithDatabaseFailure_ShouldThrowException() { | ||||||
|  |         _dalServiceMock.Setup(d => d.ExecuteAsync<PCRBFollowUpComment>(It.IsAny<string>(), FOLLOW_UP_COMMENT)) | ||||||
|  |             .ReturnsAsync(0); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _pcrbService.UpdateFollowUpComment(FOLLOW_UP_COMMENT)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task UpdateFollowUpComment_WithDatabaseException_ShouldThrowException() { | ||||||
|  |         _dalServiceMock.Setup(d => d.ExecuteAsync<PCRBFollowUpComment>(It.IsAny<string>(), FOLLOW_UP_COMMENT)) | ||||||
|  |             .Throws<Exception>(); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _pcrbService.UpdateFollowUpComment(FOLLOW_UP_COMMENT)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task DeleteFollowUpComment_WithValidId_ShouldDeleteFollowUp() { | ||||||
|  |         int commentId = 1; | ||||||
|  |  | ||||||
|  |         _dalServiceMock.Setup(d => d.ExecuteAsync(It.IsAny<string>(), It.IsAny<object>())) | ||||||
|  |             .ReturnsAsync(1); | ||||||
|  |  | ||||||
|  |         await _pcrbService.DeleteFollowUpComment(commentId); | ||||||
|  |  | ||||||
|  |         _dalServiceMock.Verify(d => d.ExecuteAsync(It.IsAny<string>(), It.IsAny<object>()), Times.Once); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task DeleteFollowUpComment_WithInvalidId_ShouldThrowException() { | ||||||
|  |         await Assert.ThrowsAsync<ArgumentException>(() => _pcrbService.DeleteFollowUpComment(0)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task DeleteFollowUpComment_WithDatabaseFailure_ShouldThrowException() { | ||||||
|  |         int commentId = 1; | ||||||
|  |  | ||||||
|  |         _dalServiceMock.Setup(d => d.ExecuteAsync(It.IsAny<string>(), It.IsAny<object>())) | ||||||
|  |             .ReturnsAsync(0); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _pcrbService.DeleteFollowUpComment(commentId)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task DeleteFollowUpComment_WithDatabaseException_ShouldThrowException() { | ||||||
|  |         int commentId = 1; | ||||||
|  |  | ||||||
|  |         _dalServiceMock.Setup(d => d.ExecuteAsync(It.IsAny<string>(), It.IsAny<object>())) | ||||||
|  |             .Throws<Exception>(); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _pcrbService.DeleteFollowUpComment(commentId)); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										198
									
								
								MesaFabApproval.API.Test/PCRBFollowUpTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								MesaFabApproval.API.Test/PCRBFollowUpTests.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,198 @@ | |||||||
|  | using MesaFabApproval.API.Services; | ||||||
|  | using MesaFabApproval.Models; | ||||||
|  | using MesaFabApproval.Shared.Models; | ||||||
|  |  | ||||||
|  | using Microsoft.Extensions.Caching.Memory; | ||||||
|  | using Microsoft.Extensions.Logging; | ||||||
|  |  | ||||||
|  | using Moq; | ||||||
|  |  | ||||||
|  | namespace MesaFabApproval.API.Test; | ||||||
|  |  | ||||||
|  | public class PCRBFollowUpTests { | ||||||
|  |     private readonly Mock<ILogger<PCRBService>> _loggerMock; | ||||||
|  |     private readonly Mock<IDalService> _dalServiceMock; | ||||||
|  |     private readonly Mock<IMemoryCache> _cacheMock; | ||||||
|  |     private readonly Mock<IUserService> _userServiceMock; | ||||||
|  |     private readonly Mock<IApprovalService> _approvalServiceMock; | ||||||
|  |     private readonly Mock<ISmtpService> _smtpServiceMock; | ||||||
|  |     private readonly PCRBService _pcrbService; | ||||||
|  |     private readonly AppSettings _appSettings; | ||||||
|  |  | ||||||
|  |     private static PCRBFollowUp FOLLOW_UP = new PCRBFollowUp { | ||||||
|  |         ID = 1, | ||||||
|  |         PlanNumber = 1, | ||||||
|  |         Step = 1, | ||||||
|  |         FollowUpDate = DateTime.Now | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     private static IEnumerable<PCRBFollowUp> FOLLOW_UPS = new List<PCRBFollowUp>() {  | ||||||
|  |         new PCRBFollowUp { ID = 1, PlanNumber = 1, Step = 1, FollowUpDate = DateTime.Now } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     public PCRBFollowUpTests() { | ||||||
|  |         _loggerMock = new Mock<ILogger<PCRBService>>(); | ||||||
|  |         _dalServiceMock = new Mock<IDalService>(); | ||||||
|  |         _userServiceMock = new Mock<IUserService>(); | ||||||
|  |         _approvalServiceMock = new Mock<IApprovalService>(); | ||||||
|  |         _smtpServiceMock = new Mock<ISmtpService>(); | ||||||
|  |         _cacheMock = MockMemoryCacheService.GetMemoryCache(FOLLOW_UPS); | ||||||
|  |         _appSettings = new AppSettings( | ||||||
|  |             Company: "Infineon", | ||||||
|  |             DbConnectionString: "connectionString", | ||||||
|  |             JwtAudience: "audience", | ||||||
|  |             JwtIssuer: "issuer", | ||||||
|  |             JwtKey: "key", | ||||||
|  |             MrbAttachmentPath: "mrbAttachmentPath", | ||||||
|  |             PcrbAttachmentPath: "pcrbAttachmentPath", | ||||||
|  |             ShouldSendEmail: false, | ||||||
|  |             SiteBaseUrl: "siteBaseUrl", | ||||||
|  |             WorkingDirectoryName: "workingDirectoryName" | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         _pcrbService = new PCRBService( | ||||||
|  |             _loggerMock.Object, | ||||||
|  |             _dalServiceMock.Object, | ||||||
|  |             _cacheMock.Object, | ||||||
|  |             _userServiceMock.Object, | ||||||
|  |             _approvalServiceMock.Object, | ||||||
|  |             _smtpServiceMock.Object, | ||||||
|  |             _appSettings | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task CreateFollowUp_WithValidParam_ShouldCreateFollowUp() { | ||||||
|  |         _dalServiceMock.Setup(d => d.ExecuteAsync<PCRBFollowUp>(It.IsAny<string>(), FOLLOW_UP)) | ||||||
|  |             .ReturnsAsync(1); | ||||||
|  |  | ||||||
|  |         await _pcrbService.CreateFollowUp(FOLLOW_UP); | ||||||
|  |  | ||||||
|  |         _dalServiceMock.Verify(d => d.ExecuteAsync<PCRBFollowUp>(It.IsAny<string>(), FOLLOW_UP), Times.Once); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task CreateFollowUp_WithNullParam_ShouldThrowException() { | ||||||
|  |         await Assert.ThrowsAsync<ArgumentNullException>(() => _pcrbService.CreateFollowUp(null)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task CreateFollowUp_WithDatabaseFailure_ShouldThrowException() { | ||||||
|  |         _dalServiceMock.Setup(d => d.ExecuteAsync<PCRBFollowUp>(It.IsAny<string>(), FOLLOW_UP)) | ||||||
|  |             .ReturnsAsync(0); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _pcrbService.CreateFollowUp(FOLLOW_UP)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task CreateFollowUp_WithDatabaseException_ShouldThrowException() { | ||||||
|  |         _dalServiceMock.Setup(d => d.ExecuteAsync<PCRBFollowUp>(It.IsAny<string>(), FOLLOW_UP)) | ||||||
|  |             .Throws<Exception>(); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _pcrbService.CreateFollowUp(FOLLOW_UP)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task GetFollowUpsByPlanNumber_WithCacheBypass_ShouldReturnFollowUps() { | ||||||
|  |         int planNumber = 1; | ||||||
|  |  | ||||||
|  |         _dalServiceMock.Setup(d => d.QueryAsync<PCRBFollowUp>(It.IsAny<string>(), It.IsAny<object>())) | ||||||
|  |             .ReturnsAsync(FOLLOW_UPS); | ||||||
|  |  | ||||||
|  |         IEnumerable<PCRBFollowUp> result = await _pcrbService.GetFollowUpsByPlanNumber(planNumber, true); | ||||||
|  |  | ||||||
|  |         Assert.NotNull(result); | ||||||
|  |         Assert.Single(result); | ||||||
|  |         Assert.Equal(FOLLOW_UPS, result); | ||||||
|  |         _dalServiceMock.Verify(d => d.QueryAsync<PCRBFollowUp>(It.IsAny<string>(), It.IsAny<object>()), Times.Once); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task GetFollowUpsByPlanNumber_WithCacheBypass_AndDatabaseException_ShouldThrowException() { | ||||||
|  |         int planNumber = 1; | ||||||
|  |  | ||||||
|  |         _dalServiceMock.Setup(d => d.QueryAsync<PCRBFollowUp>(It.IsAny<string>(), It.IsAny<object>())) | ||||||
|  |             .Throws<Exception>(); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _pcrbService.GetFollowUpsByPlanNumber(planNumber, true)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task GetFollowUpsByPlanNumber_WithoutCacheBypass_ShouldReturnFollowUps() { | ||||||
|  |         int planNumber = 1; | ||||||
|  |  | ||||||
|  |         IEnumerable<PCRBFollowUp> result = await _pcrbService.GetFollowUpsByPlanNumber(planNumber, false); | ||||||
|  |  | ||||||
|  |         Assert.NotNull(result); | ||||||
|  |         Assert.Single(result); | ||||||
|  |         Assert.Equal(FOLLOW_UPS, result); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task UpdateFollowUp_WithValidParam_ShouldUpdateFollowUp() { | ||||||
|  |         _dalServiceMock.Setup(d => d.ExecuteAsync<PCRBFollowUp>(It.IsAny<string>(), FOLLOW_UP)) | ||||||
|  |             .ReturnsAsync(1); | ||||||
|  |  | ||||||
|  |         await _pcrbService.UpdateFollowUp(FOLLOW_UP); | ||||||
|  |  | ||||||
|  |         _dalServiceMock.Verify(d => d.ExecuteAsync<PCRBFollowUp>(It.IsAny<string>(), FOLLOW_UP), Times.Once); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task UpdateFollowUp_WithNullParam_ShouldThrowException() { | ||||||
|  |         await Assert.ThrowsAsync<ArgumentNullException>(() => _pcrbService.UpdateFollowUp(null)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task UpdateFollowUp_WithDatabaseFailure_ShouldThrowException() { | ||||||
|  |         _dalServiceMock.Setup(d => d.ExecuteAsync<PCRBFollowUp>(It.IsAny<string>(), FOLLOW_UP)) | ||||||
|  |             .ReturnsAsync(0); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _pcrbService.UpdateFollowUp(FOLLOW_UP)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task UpdateFollowUp_WithDatabaseException_ShouldThrowException() { | ||||||
|  |         _dalServiceMock.Setup(d => d.ExecuteAsync<PCRBFollowUp>(It.IsAny<string>(), FOLLOW_UP)) | ||||||
|  |             .Throws<Exception>(); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _pcrbService.UpdateFollowUp(FOLLOW_UP)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task DeleteFollowUp_WithValidId_ShouldDeleteFollowUp() { | ||||||
|  |         int followUpId = 1; | ||||||
|  |  | ||||||
|  |         _dalServiceMock.Setup(d => d.ExecuteAsync(It.IsAny<string>(), It.IsAny<object>())) | ||||||
|  |             .ReturnsAsync(1); | ||||||
|  |  | ||||||
|  |         await _pcrbService.DeleteFollowUp(followUpId); | ||||||
|  |  | ||||||
|  |         _dalServiceMock.Verify(d => d.ExecuteAsync(It.IsAny<string>(), It.IsAny<object>()), Times.Once); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task DeleteFollowUp_WithInvalidId_ShouldThrowException() { | ||||||
|  |         await Assert.ThrowsAsync<ArgumentException>(() => _pcrbService.DeleteFollowUp(0)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task DeleteFollowUp_WithDatabaseFailure_ShouldThrowException() { | ||||||
|  |         int followUpId = 1; | ||||||
|  |  | ||||||
|  |         _dalServiceMock.Setup(d => d.ExecuteAsync(It.IsAny<string>(), It.IsAny<object>())) | ||||||
|  |             .ReturnsAsync(0); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _pcrbService.DeleteFollowUp(followUpId)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task DeleteFollowUp_WithDatabaseException_ShouldThrowException() { | ||||||
|  |         int followUpId = 1; | ||||||
|  |  | ||||||
|  |         _dalServiceMock.Setup(d => d.ExecuteAsync(It.IsAny<string>(), It.IsAny<object>())) | ||||||
|  |             .Throws<Exception>(); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _pcrbService.DeleteFollowUp(followUpId)); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,28 +1,15 @@ | |||||||
| using MesaFabApproval.API.Services; | using MesaFabApproval.API.Services; | ||||||
| using MesaFabApproval.Models; | using MesaFabApproval.Models; | ||||||
| using MesaFabApproval.Shared.Models; | using MesaFabApproval.Shared.Models; | ||||||
|  |  | ||||||
| using Microsoft.Extensions.Caching.Memory; | using Microsoft.Extensions.Caching.Memory; | ||||||
| using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||||
|  |  | ||||||
| using Moq; | using Moq; | ||||||
|  |  | ||||||
|  | using System.Net.Mail; | ||||||
|  |  | ||||||
| namespace MesaFabApproval.API.Test; | namespace MesaFabApproval.API.Test; | ||||||
|  | public class PCRBServiceTests | ||||||
| public static class MockMemoryCacheService { | { | ||||||
|     public static Mock<IMemoryCache> GetMemoryCache(object expectedValue) { |  | ||||||
|         Mock<IMemoryCache> mockMemoryCache = new Mock<IMemoryCache>(); |  | ||||||
|         mockMemoryCache |  | ||||||
|             .Setup(x => x.TryGetValue(It.IsAny<object>(), out expectedValue)) |  | ||||||
|             .Returns(true); |  | ||||||
|         mockMemoryCache |  | ||||||
|             .Setup(x => x.CreateEntry(It.IsAny<object>())) |  | ||||||
|             .Returns(Mock.Of<ICacheEntry>()); |  | ||||||
|         return mockMemoryCache; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| public class PCRBServiceTests { |  | ||||||
|     private readonly Mock<ILogger<PCRBService>> _loggerMock; |     private readonly Mock<ILogger<PCRBService>> _loggerMock; | ||||||
|     private readonly Mock<IDalService> _dalServiceMock; |     private readonly Mock<IDalService> _dalServiceMock; | ||||||
|     private Mock<IMemoryCache> _cacheMock; |     private Mock<IMemoryCache> _cacheMock; | ||||||
| @ -47,24 +34,25 @@ public class PCRBServiceTests { | |||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     private static IEnumerable<PCRBFollowUp> FOLLOW_UPS = new List<PCRBFollowUp>() {  |     private static IEnumerable<PCRBFollowUp> FOLLOW_UPS = new List<PCRBFollowUp>() { | ||||||
|         new PCRBFollowUp { ID = 1, PlanNumber = 1, Step = 1, FollowUpDate = DateTime.Now } |         new PCRBFollowUp { ID = 1, PlanNumber = 1, Step = 1, FollowUpDate = DateTime.Now } | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     private static AppSettings appSettings = new AppSettings( |     private static AppSettings appSettings = new AppSettings( | ||||||
|             Company: "Infineon", |         Company: "Infineon", | ||||||
|             DbConnectionString: "connectionString", |         DbConnectionString: "connectionString", | ||||||
|             JwtAudience: "audience", |         JwtAudience: "audience", | ||||||
|             JwtIssuer: "issuer", |         JwtIssuer: "issuer", | ||||||
|             JwtKey: "key", |         JwtKey: "key", | ||||||
|             MrbAttachmentPath: "mrbAttachmentPath", |         MrbAttachmentPath: "mrbAttachmentPath", | ||||||
|             PcrbAttachmentPath: "pcrbAttachmentPath", |         PcrbAttachmentPath: "pcrbAttachmentPath", | ||||||
|             ShouldSendEmail: false, |         ShouldSendEmail: false, | ||||||
|             SiteBaseUrl: "siteBaseUrl", |         SiteBaseUrl: "siteBaseUrl", | ||||||
|             WorkingDirectoryName: "workingDirectoryName" |         WorkingDirectoryName: "workingDirectoryName" | ||||||
|         ); |     ); | ||||||
|  |  | ||||||
|     public PCRBServiceTests() { |     public PCRBServiceTests() | ||||||
|  |     { | ||||||
|         _loggerMock = new Mock<ILogger<PCRBService>>(); |         _loggerMock = new Mock<ILogger<PCRBService>>(); | ||||||
|         _dalServiceMock = new Mock<IDalService>(); |         _dalServiceMock = new Mock<IDalService>(); | ||||||
|         _userServiceMock = new Mock<IUserService>(); |         _userServiceMock = new Mock<IUserService>(); | ||||||
| @ -84,8 +72,10 @@ public class PCRBServiceTests { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     [Fact] |     [Fact] | ||||||
|     public async Task CreateNewPCRB_WithValidParam_ShouldCreatePCRB() { |     public async Task CreateNewPCRB_WithValidParam_ShouldCreatePCRB() | ||||||
|         var pcrb = new PCRB { |     { | ||||||
|  |         var pcrb = new PCRB | ||||||
|  |         { | ||||||
|             OwnerID = 1, |             OwnerID = 1, | ||||||
|             Title = "Test Title", |             Title = "Test Title", | ||||||
|             ChangeLevel = "Level 1", |             ChangeLevel = "Level 1", | ||||||
| @ -107,13 +97,16 @@ public class PCRBServiceTests { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     [Fact] |     [Fact] | ||||||
|     public async Task CreateNewPCRB_WithNullParam_ShouldThrowException() { |     public async Task CreateNewPCRB_WithNullParam_ShouldThrowException() | ||||||
|  |     { | ||||||
|         await Assert.ThrowsAsync<ArgumentNullException>(() => _pcrbService.CreateNewPCRB(null)); |         await Assert.ThrowsAsync<ArgumentNullException>(() => _pcrbService.CreateNewPCRB(null)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     [Fact] |     [Fact] | ||||||
|     public async Task CreateNewPCRB_WithDatabaseFailure_ShouldThrowException() { |     public async Task CreateNewPCRB_WithDatabaseFailure_ShouldThrowException() | ||||||
|         var pcrb = new PCRB { |     { | ||||||
|  |         var pcrb = new PCRB | ||||||
|  |         { | ||||||
|             OwnerID = 1, |             OwnerID = 1, | ||||||
|             Title = "Test Title", |             Title = "Test Title", | ||||||
|             ChangeLevel = "Level 1", |             ChangeLevel = "Level 1", | ||||||
| @ -133,8 +126,10 @@ public class PCRBServiceTests { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     [Fact] |     [Fact] | ||||||
|     public async Task CreateNewPCRB_WithDatabaseException_ShouldThrowException() { |     public async Task CreateNewPCRB_WithDatabaseException_ShouldThrowException() | ||||||
|         var pcrb = new PCRB { |     { | ||||||
|  |         var pcrb = new PCRB | ||||||
|  |         { | ||||||
|             OwnerID = 1, |             OwnerID = 1, | ||||||
|             Title = "Test Title", |             Title = "Test Title", | ||||||
|             ChangeLevel = "Level 1", |             ChangeLevel = "Level 1", | ||||||
| @ -154,7 +149,8 @@ public class PCRBServiceTests { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     [Fact] |     [Fact] | ||||||
|     public async Task UpdatePCRB_WithValidParam_ShouldUpdatePCRB() { |     public async Task UpdatePCRB_WithValidParam_ShouldUpdatePCRB() | ||||||
|  |     { | ||||||
|         _cacheMock = MockMemoryCacheService.GetMemoryCache(PCRBS); |         _cacheMock = MockMemoryCacheService.GetMemoryCache(PCRBS); | ||||||
|  |  | ||||||
|         _pcrbService = new PCRBService( |         _pcrbService = new PCRBService( | ||||||
| @ -167,7 +163,8 @@ public class PCRBServiceTests { | |||||||
|             appSettings |             appSettings | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         var pcrb = new PCRB { |         var pcrb = new PCRB | ||||||
|  |         { | ||||||
|             PlanNumber = 1, |             PlanNumber = 1, | ||||||
|             OwnerID = 1, |             OwnerID = 1, | ||||||
|             Title = "Test Title", |             Title = "Test Title", | ||||||
| @ -190,13 +187,16 @@ public class PCRBServiceTests { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     [Fact] |     [Fact] | ||||||
|     public async Task UpdatePCRB_WithNullParam_ShouldThrowException() { |     public async Task UpdatePCRB_WithNullParam_ShouldThrowException() | ||||||
|  |     { | ||||||
|         await Assert.ThrowsAsync<ArgumentNullException>(() => _pcrbService.UpdatePCRB(null)); |         await Assert.ThrowsAsync<ArgumentNullException>(() => _pcrbService.UpdatePCRB(null)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     [Fact] |     [Fact] | ||||||
|     public async Task UpdatePCRB_WithDatabaseFailure_ShouldThrowException() { |     public async Task UpdatePCRB_WithDatabaseFailure_ShouldThrowException() | ||||||
|         var pcrb = new PCRB { |     { | ||||||
|  |         var pcrb = new PCRB | ||||||
|  |         { | ||||||
|             PlanNumber = 1, |             PlanNumber = 1, | ||||||
|             OwnerID = 1, |             OwnerID = 1, | ||||||
|             Title = "Test Title", |             Title = "Test Title", | ||||||
| @ -217,8 +217,10 @@ public class PCRBServiceTests { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     [Fact] |     [Fact] | ||||||
|     public async Task UpdatePCRB_WithDatabaseException_ShouldThrowException() { |     public async Task UpdatePCRB_WithDatabaseException_ShouldThrowException() | ||||||
|         var pcrb = new PCRB { |     { | ||||||
|  |         var pcrb = new PCRB | ||||||
|  |         { | ||||||
|             PlanNumber = 1, |             PlanNumber = 1, | ||||||
|             OwnerID = 1, |             OwnerID = 1, | ||||||
|             Title = "Test Title", |             Title = "Test Title", | ||||||
| @ -239,176 +241,88 @@ public class PCRBServiceTests { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     [Fact] |     [Fact] | ||||||
|     public async Task CreateFollowUp_WithValidParam_ShouldCreateFollowUp() { |     public async Task NotifyApprover_ShouldSendNotification() | ||||||
|         var followUp = new PCRBFollowUp { |     { | ||||||
|             PlanNumber = 1, |         PCRBNotification notification = new PCRBNotification | ||||||
|             Step = 1, |         { | ||||||
|             FollowUpDate = DateTime.Now |             Message = "Test Message", | ||||||
|  |             PCRB = new PCRB { PlanNumber = 1, Title = "Test PCRB" }, | ||||||
|  |             Approval = new Approval | ||||||
|  |             { | ||||||
|  |                 UserID = 1, | ||||||
|  |                 IssueID = 1, | ||||||
|  |                 RoleName = "Role", | ||||||
|  |                 SubRole = "SubRole", | ||||||
|  |                 SubRoleID = 1, | ||||||
|  |                 AssignedDate = DateTime.Now | ||||||
|  |             } | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         _dalServiceMock.Setup(d => d.ExecuteAsync<PCRBFollowUp>(It.IsAny<string>(), followUp)) |         _userServiceMock.Setup(s => s.GetUserByUserId(It.IsAny<int>())) | ||||||
|             .ReturnsAsync(1); |                         .ReturnsAsync(new User | ||||||
|  |                         { | ||||||
|  |                             UserID = 1, | ||||||
|  |                             LoginID = "testLogin", | ||||||
|  |                             FirstName = "Test", | ||||||
|  |                             LastName = "User", | ||||||
|  |                             Email = "test@example.com" | ||||||
|  |                         }); | ||||||
|  |  | ||||||
|         await _pcrbService.CreateFollowUp(followUp); |         _smtpServiceMock.Setup(s => s.SendEmail(It.IsAny<IEnumerable<MailAddress>>(), | ||||||
|  |                                                 It.IsAny<IEnumerable<MailAddress>>(), | ||||||
|  |                                                 It.IsAny<string>(), | ||||||
|  |                                                 It.IsAny<string>())) | ||||||
|  |             .ReturnsAsync(true); | ||||||
|  |  | ||||||
|         _dalServiceMock.Verify(d => d.ExecuteAsync<PCRBFollowUp>(It.IsAny<string>(), followUp), Times.Once); |         _approvalServiceMock.Setup(s => s.UpdateApproval(It.IsAny<Approval>())) | ||||||
|  |             .Returns(Task.CompletedTask); | ||||||
|  |  | ||||||
|  |         await _pcrbService.NotifyApprover(notification); | ||||||
|  |  | ||||||
|  |         _smtpServiceMock.Verify(s => s.SendEmail(It.IsAny<IEnumerable<MailAddress>>(), | ||||||
|  |                                                  It.IsAny<IEnumerable<MailAddress>>(), | ||||||
|  |                                                  It.IsAny<string>(), | ||||||
|  |                                                  It.IsAny<string>()), Times.Once); | ||||||
|  |         _approvalServiceMock.Verify(s => s.UpdateApproval(It.IsAny<Approval>()), Times.Once); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     [Fact] |     [Fact] | ||||||
|     public async Task CreateFollowUp_WithNullParam_ShouldThrowException() { |     public async Task NotifyApprover_ShouldThrowException_WhenNotificationIsNull() | ||||||
|         await Assert.ThrowsAsync<ArgumentNullException>(() => _pcrbService.CreateFollowUp(null)); |     { | ||||||
|  |         await Assert.ThrowsAsync<ArgumentNullException>(() => _pcrbService.NotifyApprover(null)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     [Fact] |     [Fact] | ||||||
|     public async Task CreateFollowUp_WithDatabaseFailure_ShouldThrowException() { |     public async Task NotifyApprover_ShouldThrowException_WhenPCRBIsNull() | ||||||
|         var followUp = new PCRBFollowUp { |     { | ||||||
|             PlanNumber = 1, |         PCRBNotification notification = new PCRBNotification | ||||||
|             Step = 1, |         { | ||||||
|             FollowUpDate = DateTime.Now |             Message = "Test Message", | ||||||
|  |             PCRB = null, | ||||||
|  |             Approval = new Approval | ||||||
|  |             { | ||||||
|  |                 UserID = 1, | ||||||
|  |                 IssueID = 1, | ||||||
|  |                 RoleName = "Role", | ||||||
|  |                 SubRole = "SubRole", | ||||||
|  |                 SubRoleID = 1, | ||||||
|  |                 AssignedDate = DateTime.Now | ||||||
|  |             } | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         _dalServiceMock.Setup(d => d.ExecuteAsync<PCRBFollowUp>(It.IsAny<string>(), followUp)) |         await Assert.ThrowsAsync<ArgumentNullException>(() => _pcrbService.NotifyApprover(notification)); | ||||||
|             .ReturnsAsync(0); |  | ||||||
|  |  | ||||||
|         await Assert.ThrowsAsync<Exception>(() => _pcrbService.CreateFollowUp(followUp)); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     [Fact] |     [Fact] | ||||||
|     public async Task CreateFollowUp_WithDatabaseException_ShouldThrowException() { |     public async Task NotifyApprover_ShouldThrowException_WhenApprovalIsNull() | ||||||
|         var followUp = new PCRBFollowUp { |     { | ||||||
|             PlanNumber = 1, |         PCRBNotification notification = new PCRBNotification | ||||||
|             Step = 1, |         { | ||||||
|             FollowUpDate = DateTime.Now |             Message = "Test Message", | ||||||
|  |             PCRB = new PCRB { PlanNumber = 1, Title = "Test PCRB" }, | ||||||
|  |             Approval = null | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         _dalServiceMock.Setup(d => d.ExecuteAsync<PCRBFollowUp>(It.IsAny<string>(), followUp)) |         await Assert.ThrowsAsync<ArgumentNullException>(() => _pcrbService.NotifyApprover(notification)); | ||||||
|             .Throws<Exception>(); |  | ||||||
|  |  | ||||||
|         await Assert.ThrowsAsync<Exception>(() => _pcrbService.CreateFollowUp(followUp)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     [Fact] |  | ||||||
|     public async Task GetFollowUpsByPlanNumber_WithCacheBypass_ShouldReturnFollowUps() { |  | ||||||
|         int planNumber = 1; |  | ||||||
|  |  | ||||||
|         _dalServiceMock.Setup(d => d.QueryAsync<PCRBFollowUp>(It.IsAny<string>(), It.IsAny<object>())) |  | ||||||
|             .ReturnsAsync(FOLLOW_UPS); |  | ||||||
|  |  | ||||||
|         IEnumerable<PCRBFollowUp> result = await _pcrbService.GetFollowUpsByPlanNumber(planNumber, true); |  | ||||||
|  |  | ||||||
|         Assert.NotNull(result); |  | ||||||
|         Assert.Single(result); |  | ||||||
|         Assert.Equal(FOLLOW_UPS, result); |  | ||||||
|         _dalServiceMock.Verify(d => d.QueryAsync<PCRBFollowUp>(It.IsAny<string>(), It.IsAny<object>()), Times.Once); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     [Fact] |  | ||||||
|     public async Task GetFollowUpsByPlanNumber_WithCacheBypass_AndDatabaseException_ShouldThrowException() { |  | ||||||
|         int planNumber = 1; |  | ||||||
|  |  | ||||||
|         _dalServiceMock.Setup(d => d.QueryAsync<PCRBFollowUp>(It.IsAny<string>(), It.IsAny<object>())) |  | ||||||
|             .Throws<Exception>(); |  | ||||||
|  |  | ||||||
|         await Assert.ThrowsAsync<Exception>(() => _pcrbService.GetFollowUpsByPlanNumber(planNumber, true)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     [Fact] |  | ||||||
|     public async Task GetFollowUpsByPlanNumber_WithoutCacheBypass_ShouldReturnFollowUps() { |  | ||||||
|         int planNumber = 1; |  | ||||||
|  |  | ||||||
|         IEnumerable<PCRBFollowUp> result = await _pcrbService.GetFollowUpsByPlanNumber(planNumber, false); |  | ||||||
|  |  | ||||||
|         Assert.NotNull(result); |  | ||||||
|         Assert.Single(result); |  | ||||||
|         Assert.Equal(FOLLOW_UPS, result); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     [Fact] |  | ||||||
|     public async Task UpdateFollowUp_WithValidParam_ShouldUpdateFollowUp() { |  | ||||||
|         var followUp = new PCRBFollowUp { |  | ||||||
|             ID = 1, |  | ||||||
|             PlanNumber = 1, |  | ||||||
|             Step = 1, |  | ||||||
|             FollowUpDate = DateTime.Now |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         _dalServiceMock.Setup(d => d.ExecuteAsync<PCRBFollowUp>(It.IsAny<string>(), followUp)) |  | ||||||
|             .ReturnsAsync(1); |  | ||||||
|  |  | ||||||
|         await _pcrbService.UpdateFollowUp(followUp); |  | ||||||
|  |  | ||||||
|         _dalServiceMock.Verify(d => d.ExecuteAsync<PCRBFollowUp>(It.IsAny<string>(), followUp), Times.Once); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     [Fact] |  | ||||||
|     public async Task UpdateFollowUp_WithNullParam_ShouldThrowException() { |  | ||||||
|         await Assert.ThrowsAsync<ArgumentNullException>(() => _pcrbService.UpdateFollowUp(null)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     [Fact] |  | ||||||
|     public async Task UpdateFollowUp_WithDatabaseFailure_ShouldThrowException() { |  | ||||||
|         var followUp = new PCRBFollowUp { |  | ||||||
|             ID = 1, |  | ||||||
|             PlanNumber = 1, |  | ||||||
|             Step = 1, |  | ||||||
|             FollowUpDate = DateTime.Now |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         _dalServiceMock.Setup(d => d.ExecuteAsync<PCRBFollowUp>(It.IsAny<string>(), followUp)) |  | ||||||
|             .ReturnsAsync(0); |  | ||||||
|  |  | ||||||
|         await Assert.ThrowsAsync<Exception>(() => _pcrbService.UpdateFollowUp(followUp)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     [Fact] |  | ||||||
|     public async Task UpdateFollowUp_WithDatabaseException_ShouldThrowException() { |  | ||||||
|         var followUp = new PCRBFollowUp { |  | ||||||
|             ID = 1, |  | ||||||
|             PlanNumber = 1, |  | ||||||
|             Step = 1, |  | ||||||
|             FollowUpDate = DateTime.Now |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         _dalServiceMock.Setup(d => d.ExecuteAsync<PCRBFollowUp>(It.IsAny<string>(), followUp)) |  | ||||||
|             .Throws<Exception>(); |  | ||||||
|  |  | ||||||
|         await Assert.ThrowsAsync<Exception>(() => _pcrbService.UpdateFollowUp(followUp)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     [Fact] |  | ||||||
|     public async Task DeleteFollowUp_WithValidId_ShouldDeleteFollowUp() { |  | ||||||
|         int followUpId = 1; |  | ||||||
|  |  | ||||||
|         _dalServiceMock.Setup(d => d.ExecuteAsync(It.IsAny<string>(), It.IsAny<object>())) |  | ||||||
|             .ReturnsAsync(1); |  | ||||||
|  |  | ||||||
|         await _pcrbService.DeleteFollowUp(followUpId); |  | ||||||
|  |  | ||||||
|         _dalServiceMock.Verify(d => d.ExecuteAsync(It.IsAny<string>(), It.IsAny<object>()), Times.Once); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     [Fact] |  | ||||||
|     public async Task DeleteFollowUp_WithInvalidId_ShouldThrowException() { |  | ||||||
|         await Assert.ThrowsAsync<ArgumentException>(() => _pcrbService.DeleteFollowUp(0)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     [Fact] |  | ||||||
|     public async Task DeleteFollowUp_WithDatabaseFailure_ShouldThrowException() { |  | ||||||
|         int followUpId = 1; |  | ||||||
|  |  | ||||||
|         _dalServiceMock.Setup(d => d.ExecuteAsync(It.IsAny<string>(), It.IsAny<object>())) |  | ||||||
|             .ReturnsAsync(0); |  | ||||||
|  |  | ||||||
|         await Assert.ThrowsAsync<Exception>(() => _pcrbService.DeleteFollowUp(followUpId)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     [Fact] |  | ||||||
|     public async Task DeleteFollowUp_WithDatabaseException_ShouldThrowException() { |  | ||||||
|         int followUpId = 1; |  | ||||||
|  |  | ||||||
|         _dalServiceMock.Setup(d => d.ExecuteAsync(It.IsAny<string>(), It.IsAny<object>())) |  | ||||||
|             .Throws<Exception>(); |  | ||||||
|  |  | ||||||
|         await Assert.ThrowsAsync<Exception>(() => _pcrbService.DeleteFollowUp(followUpId)); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| using MesaFabApproval.API.Services; | using MesaFabApproval.API.Services; | ||||||
|  | using MesaFabApproval.API.Utilities; | ||||||
| using MesaFabApproval.Shared.Models; | using MesaFabApproval.Shared.Models; | ||||||
| using MesaFabApproval.Shared.Services; | using MesaFabApproval.Shared.Services; | ||||||
|  |  | ||||||
| @ -11,13 +12,13 @@ namespace MesaFabApproval.API.Controllers; | |||||||
| public class ApprovalController : ControllerBase { | public class ApprovalController : ControllerBase { | ||||||
|     private readonly ILogger<ApprovalController> _logger; |     private readonly ILogger<ApprovalController> _logger; | ||||||
|     private readonly IApprovalService _approvalService; |     private readonly IApprovalService _approvalService; | ||||||
|     private readonly IMonInWorkerClient _monInClient; |     private readonly IMonInUtils _monInUtils; | ||||||
|  |  | ||||||
|     public ApprovalController(ILogger<ApprovalController> logger, IApprovalService approvalService, |     public ApprovalController(ILogger<ApprovalController> logger, IApprovalService approvalService, | ||||||
|                               IMonInWorkerClient monInClient) { |                               IMonInUtils monInUtils) { | ||||||
|         _logger = logger ?? throw new ArgumentNullException("ILogger not injected"); |         _logger = logger ?? throw new ArgumentNullException("ILogger not injected"); | ||||||
|         _approvalService = approvalService ?? throw new ArgumentNullException("IApprovalService not injected"); |         _approvalService = approvalService ?? throw new ArgumentNullException("IApprovalService not injected"); | ||||||
|         _monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected"); |         _monInUtils = monInUtils ?? throw new ArgumentNullException("IMonInUtils not injected"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     [HttpPost] |     [HttpPost] | ||||||
| @ -39,26 +40,19 @@ public class ApprovalController : ControllerBase { | |||||||
|         } catch (ArgumentException ex) { |         } catch (ArgumentException ex) { | ||||||
|             isArgumentError = true; |             isArgumentError = true; | ||||||
|             errorMessage = ex.Message; |             errorMessage = ex.Message; | ||||||
|  |             _logger.LogWarning($"Argument error when attempting to create approval: {errorMessage}"); | ||||||
|             return BadRequest(errorMessage); |             return BadRequest(errorMessage); | ||||||
|         } catch (Exception ex) { |         } catch (Exception ex) { | ||||||
|             isInternalError = true; |             isInternalError = true; | ||||||
|             errorMessage = $"Cannot create new approval, because {ex.Message}"; |             errorMessage = $"Cannot create new approval, because {ex.Message}"; | ||||||
|  |             _logger.LogError(errorMessage); | ||||||
|             return Problem(errorMessage); |             return Problem(errorMessage); | ||||||
|         } finally { |         } finally { | ||||||
|             string metricName = "CreateApproval"; |             string metricName = "CreateApproval"; | ||||||
|             DateTime end = DateTime.Now; |             DateTime end = DateTime.Now; | ||||||
|             double millisecondsDiff = (end - start).TotalMilliseconds; |             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||||
|             _monInClient.PostAverage(metricName + "Latency", millisecondsDiff); |              | ||||||
|  |             _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError); | ||||||
|             if (isArgumentError) { |  | ||||||
|                 _logger.LogWarning(errorMessage); |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); |  | ||||||
|             } else if (isInternalError) { |  | ||||||
|                 _logger.LogError(errorMessage); |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Critical); |  | ||||||
|             } else { |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @ -81,26 +75,19 @@ public class ApprovalController : ControllerBase { | |||||||
|         } catch (ArgumentException ex) { |         } catch (ArgumentException ex) { | ||||||
|             isArgumentError = true; |             isArgumentError = true; | ||||||
|             errorMessage = ex.Message; |             errorMessage = ex.Message; | ||||||
|  |             _logger.LogWarning($"Argument error when getting approvals for issue {issueId}: {errorMessage}"); | ||||||
|             return BadRequest(errorMessage); |             return BadRequest(errorMessage); | ||||||
|         } catch (Exception ex) { |         } catch (Exception ex) { | ||||||
|             isInternalError = true; |             isInternalError = true; | ||||||
|             errorMessage = $"Cannot get approvals, because {ex.Message}"; |             errorMessage = $"Cannot get approvals, because {ex.Message}"; | ||||||
|  |             _logger.LogError(errorMessage); | ||||||
|             return Problem(errorMessage); |             return Problem(errorMessage); | ||||||
|         } finally { |         } finally { | ||||||
|             string metricName = "GetApprovalsForIssueId"; |             string metricName = "GetApprovalsForIssueId"; | ||||||
|             DateTime end = DateTime.Now; |             DateTime end = DateTime.Now; | ||||||
|             double millisecondsDiff = (end - start).TotalMilliseconds; |             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||||
|             _monInClient.PostAverage(metricName + "Latency", millisecondsDiff); |              | ||||||
|  |             _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError); | ||||||
|             if (isArgumentError) { |  | ||||||
|                 _logger.LogWarning(errorMessage); |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); |  | ||||||
|             } else if (isInternalError) { |  | ||||||
|                 _logger.LogError(errorMessage); |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Critical); |  | ||||||
|             } else { |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @ -123,26 +110,19 @@ public class ApprovalController : ControllerBase { | |||||||
|         } catch (ArgumentException ex) { |         } catch (ArgumentException ex) { | ||||||
|             isArgumentError = true; |             isArgumentError = true; | ||||||
|             errorMessage = ex.Message; |             errorMessage = ex.Message; | ||||||
|  |             _logger.LogWarning($"Argument error when getting approvals for user {userId}: {errorMessage}"); | ||||||
|             return BadRequest(errorMessage); |             return BadRequest(errorMessage); | ||||||
|         } catch (Exception ex) { |         } catch (Exception ex) { | ||||||
|             isInternalError = true; |             isInternalError = true; | ||||||
|             errorMessage = $"Cannot get approvals, because {ex.Message}"; |             errorMessage = $"Cannot get approvals, because {ex.Message}"; | ||||||
|  |             _logger.LogError(errorMessage); | ||||||
|             return Problem(errorMessage); |             return Problem(errorMessage); | ||||||
|         } finally { |         } finally { | ||||||
|             string metricName = "GetApprovalsForUserId"; |             string metricName = "GetApprovalsForUserId"; | ||||||
|             DateTime end = DateTime.Now; |             DateTime end = DateTime.Now; | ||||||
|             double millisecondsDiff = (end - start).TotalMilliseconds; |             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||||
|             _monInClient.PostAverage(metricName + "Latency", millisecondsDiff); |              | ||||||
|  |             _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError); | ||||||
|             if (isArgumentError) { |  | ||||||
|                 _logger.LogWarning(errorMessage); |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); |  | ||||||
|             } else if (isInternalError) { |  | ||||||
|                 _logger.LogError(errorMessage); |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Critical); |  | ||||||
|             } else { |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @ -165,26 +145,19 @@ public class ApprovalController : ControllerBase { | |||||||
|         } catch (ArgumentException ex) { |         } catch (ArgumentException ex) { | ||||||
|             isArgumentError = true; |             isArgumentError = true; | ||||||
|             errorMessage = ex.Message; |             errorMessage = ex.Message; | ||||||
|  |             _logger.LogWarning($"Argument error when getting approval group members for sub role {subRoleId}: {errorMessage}"); | ||||||
|             return BadRequest(errorMessage); |             return BadRequest(errorMessage); | ||||||
|         } catch (Exception ex) { |         } catch (Exception ex) { | ||||||
|             isInternalError = true; |             isInternalError = true; | ||||||
|             errorMessage = $"Cannot get approval group members, because {ex.Message}"; |             errorMessage = $"Cannot get approval group members, because {ex.Message}"; | ||||||
|  |             _logger.LogError(errorMessage); | ||||||
|             return Problem(errorMessage); |             return Problem(errorMessage); | ||||||
|         } finally { |         } finally { | ||||||
|             string metricName = "GetApprovalsGroupMembers"; |             string metricName = "GetApprovalsGroupMembers"; | ||||||
|             DateTime end = DateTime.Now; |             DateTime end = DateTime.Now; | ||||||
|             double millisecondsDiff = (end - start).TotalMilliseconds; |             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||||
|             _monInClient.PostAverage(metricName + "Latency", millisecondsDiff); |              | ||||||
|  |             _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError); | ||||||
|             if (isArgumentError) { |  | ||||||
|                 _logger.LogWarning(errorMessage); |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); |  | ||||||
|             } else if (isInternalError) { |  | ||||||
|                 _logger.LogError(errorMessage); |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Critical); |  | ||||||
|             } else { |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @ -197,7 +170,7 @@ public class ApprovalController : ControllerBase { | |||||||
|         string errorMessage = ""; |         string errorMessage = ""; | ||||||
|  |  | ||||||
|         try { |         try { | ||||||
|             _logger.LogInformation($"Attempting to update approval"); |             _logger.LogInformation("Attempting to update approval"); | ||||||
|  |  | ||||||
|             if (approval is null) throw new ArgumentNullException($"approval cannot be null"); |             if (approval is null) throw new ArgumentNullException($"approval cannot be null"); | ||||||
|  |  | ||||||
| @ -207,26 +180,54 @@ public class ApprovalController : ControllerBase { | |||||||
|         } catch (ArgumentException ex) { |         } catch (ArgumentException ex) { | ||||||
|             isArgumentError = true; |             isArgumentError = true; | ||||||
|             errorMessage = ex.Message; |             errorMessage = ex.Message; | ||||||
|  |             _logger.LogWarning($"Argument error when attempting to update approval: {errorMessage}"); | ||||||
|             return BadRequest(errorMessage); |             return BadRequest(errorMessage); | ||||||
|         } catch (Exception ex) { |         } catch (Exception ex) { | ||||||
|             isInternalError = true; |             isInternalError = true; | ||||||
|             errorMessage = $"Cannot update approval, because {ex.Message}"; |             errorMessage = $"Cannot update approval, because {ex.Message}"; | ||||||
|  |             _logger.LogError(errorMessage); | ||||||
|             return Problem(errorMessage); |             return Problem(errorMessage); | ||||||
|         } finally { |         } finally { | ||||||
|             string metricName = "UpdateApproval"; |             string metricName = "UpdateApproval"; | ||||||
|             DateTime end = DateTime.Now; |             DateTime end = DateTime.Now; | ||||||
|             double millisecondsDiff = (end - start).TotalMilliseconds; |             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||||
|             _monInClient.PostAverage(metricName + "Latency", millisecondsDiff); |  | ||||||
|  |  | ||||||
|             if (isArgumentError) { |             _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError); | ||||||
|                 _logger.LogWarning(errorMessage); |         } | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); |     } | ||||||
|             } else if (isInternalError) { |  | ||||||
|                 _logger.LogError(errorMessage); |     [HttpDelete] | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Critical); |     [Route("approval")] | ||||||
|             } else { |     public async Task<IActionResult> DeleteApproval(int approvalID) { | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); |         DateTime start = DateTime.Now; | ||||||
|             } |         bool isArgumentError = false; | ||||||
|  |         bool isInternalError = false; | ||||||
|  |         string errorMessage = ""; | ||||||
|  |  | ||||||
|  |         try { | ||||||
|  |             _logger.LogInformation($"Attempting to delete approval {approvalID}"); | ||||||
|  |  | ||||||
|  |             if (approvalID <= 0) throw new ArgumentException("Invalid approval ID"); | ||||||
|  |  | ||||||
|  |             await _approvalService.DeleteApproval(approvalID); | ||||||
|  |  | ||||||
|  |             return Ok(); | ||||||
|  |         } catch (ArgumentException ex) { | ||||||
|  |             isArgumentError = true; | ||||||
|  |             errorMessage = ex.Message; | ||||||
|  |             _logger.LogWarning($"Argument error when attempting to delete approval: {errorMessage}"); | ||||||
|  |             return BadRequest(errorMessage); | ||||||
|  |         } catch (Exception ex) { | ||||||
|  |             isInternalError = true; | ||||||
|  |             errorMessage = $"Cannot delete approval, because {ex.Message}"; | ||||||
|  |             _logger.LogError(errorMessage); | ||||||
|  |             return Problem(errorMessage); | ||||||
|  |         } finally { | ||||||
|  |             string metricName = "UpdateApproval"; | ||||||
|  |             DateTime end = DateTime.Now; | ||||||
|  |             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||||
|  |  | ||||||
|  |             _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @ -249,26 +250,19 @@ public class ApprovalController : ControllerBase { | |||||||
|         } catch (ArgumentException ex) { |         } catch (ArgumentException ex) { | ||||||
|             isArgumentError = true; |             isArgumentError = true; | ||||||
|             errorMessage = ex.Message; |             errorMessage = ex.Message; | ||||||
|  |             _logger.LogWarning($"Argument error when attempting to approve: {errorMessage}"); | ||||||
|             return BadRequest(errorMessage); |             return BadRequest(errorMessage); | ||||||
|         } catch (Exception ex) { |         } catch (Exception ex) { | ||||||
|             isInternalError = true; |             isInternalError = true; | ||||||
|             errorMessage = $"Cannot approve, because {ex.Message}"; |             errorMessage = $"Cannot approve, because {ex.Message}"; | ||||||
|  |             _logger.LogError(errorMessage); | ||||||
|             return Problem(errorMessage); |             return Problem(errorMessage); | ||||||
|         } finally { |         } finally { | ||||||
|             string metricName = "Approve"; |             string metricName = "Approve"; | ||||||
|             DateTime end = DateTime.Now; |             DateTime end = DateTime.Now; | ||||||
|             double millisecondsDiff = (end - start).TotalMilliseconds; |             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||||
|             _monInClient.PostAverage(metricName + "Latency", millisecondsDiff); |  | ||||||
|  |  | ||||||
|             if (isArgumentError) { |             _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError); | ||||||
|                 _logger.LogWarning(errorMessage); |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); |  | ||||||
|             } else if (isInternalError) { |  | ||||||
|                 _logger.LogError(errorMessage); |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Critical); |  | ||||||
|             } else { |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @ -291,26 +285,19 @@ public class ApprovalController : ControllerBase { | |||||||
|         } catch (ArgumentException ex) { |         } catch (ArgumentException ex) { | ||||||
|             isArgumentError = true; |             isArgumentError = true; | ||||||
|             errorMessage = ex.Message; |             errorMessage = ex.Message; | ||||||
|  |             _logger.LogWarning($"Argument error when attempting to deny approval: {errorMessage}"); | ||||||
|             return BadRequest(errorMessage); |             return BadRequest(errorMessage); | ||||||
|         } catch (Exception ex) { |         } catch (Exception ex) { | ||||||
|             isInternalError = true; |             isInternalError = true; | ||||||
|             errorMessage = $"Approval denial failed, because {ex.Message}"; |             errorMessage = $"Approval denial failed, because {ex.Message}"; | ||||||
|  |             _logger.LogError(errorMessage); | ||||||
|             return Problem(errorMessage); |             return Problem(errorMessage); | ||||||
|         } finally { |         } finally { | ||||||
|             string metricName = "Deny"; |             string metricName = "Deny"; | ||||||
|             DateTime end = DateTime.Now; |             DateTime end = DateTime.Now; | ||||||
|             double millisecondsDiff = (end - start).TotalMilliseconds; |             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||||
|             _monInClient.PostAverage(metricName + "Latency", millisecondsDiff); |  | ||||||
|  |  | ||||||
|             if (isArgumentError) { |             _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError); | ||||||
|                 _logger.LogWarning(errorMessage); |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); |  | ||||||
|             } else if (isInternalError) { |  | ||||||
|                 _logger.LogError(errorMessage); |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Critical); |  | ||||||
|             } else { |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @ -333,26 +320,19 @@ public class ApprovalController : ControllerBase { | |||||||
|         } catch (ArgumentException ex) { |         } catch (ArgumentException ex) { | ||||||
|             isArgumentError = true; |             isArgumentError = true; | ||||||
|             errorMessage = ex.Message; |             errorMessage = ex.Message; | ||||||
|  |             _logger.LogWarning($"Argument error when attempting to get role ID by role name: {errorMessage}"); | ||||||
|             return BadRequest(errorMessage); |             return BadRequest(errorMessage); | ||||||
|         } catch (Exception ex) { |         } catch (Exception ex) { | ||||||
|             isInternalError = true; |             isInternalError = true; | ||||||
|             errorMessage = $"Cannot get role ID, because {ex.Message}"; |             errorMessage = $"Cannot get role ID, because {ex.Message}"; | ||||||
|  |             _logger.LogError(errorMessage); | ||||||
|             return Problem(errorMessage); |             return Problem(errorMessage); | ||||||
|         } finally { |         } finally { | ||||||
|             string metricName = "GetRoleIdForRoleName"; |             string metricName = "GetRoleIdForRoleName"; | ||||||
|             DateTime end = DateTime.Now; |             DateTime end = DateTime.Now; | ||||||
|             double millisecondsDiff = (end - start).TotalMilliseconds; |             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||||
|             _monInClient.PostAverage(metricName + "Latency", millisecondsDiff); |  | ||||||
|  |  | ||||||
|             if (isArgumentError) { |             _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError); | ||||||
|                 _logger.LogWarning(errorMessage); |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); |  | ||||||
|             } else if (isInternalError) { |  | ||||||
|                 _logger.LogError(errorMessage); |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Critical); |  | ||||||
|             } else { |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @ -376,26 +356,19 @@ public class ApprovalController : ControllerBase { | |||||||
|         } catch (ArgumentException ex) { |         } catch (ArgumentException ex) { | ||||||
|             isArgumentError = true; |             isArgumentError = true; | ||||||
|             errorMessage = ex.Message; |             errorMessage = ex.Message; | ||||||
|  |             _logger.LogWarning($"Argument error when attempting to get sub roles by sub role name: {errorMessage}"); | ||||||
|             return BadRequest(errorMessage); |             return BadRequest(errorMessage); | ||||||
|         } catch (Exception ex) { |         } catch (Exception ex) { | ||||||
|             isInternalError = true; |             isInternalError = true; | ||||||
|             errorMessage = $"Cannot get role ID, because {ex.Message}"; |             errorMessage = $"Cannot get role ID, because {ex.Message}"; | ||||||
|  |             _logger.LogError(errorMessage); | ||||||
|             return Problem(errorMessage); |             return Problem(errorMessage); | ||||||
|         } finally { |         } finally { | ||||||
|             string metricName = "GetSubRoleIdForSubRoleName"; |             string metricName = "GetSubRoleIdForSubRoleName"; | ||||||
|             DateTime end = DateTime.Now; |             DateTime end = DateTime.Now; | ||||||
|             double millisecondsDiff = (end - start).TotalMilliseconds; |             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||||
|             _monInClient.PostAverage(metricName + "Latency", millisecondsDiff); |              | ||||||
|  |             _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError); | ||||||
|             if (isArgumentError) { |  | ||||||
|                 _logger.LogWarning(errorMessage); |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); |  | ||||||
|             } else if (isInternalError) { |  | ||||||
|                 _logger.LogError(errorMessage); |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Critical); |  | ||||||
|             } else { |  | ||||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -790,6 +790,42 @@ public class PCRBController : ControllerBase { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     [HttpPost] | ||||||
|  |     [Route("pcrb/notify/approver")] | ||||||
|  |     public async Task<IActionResult> NotifyApprover(PCRBNotification notification) { | ||||||
|  |         DateTime start = DateTime.Now; | ||||||
|  |         bool isArgumentError = false; | ||||||
|  |         bool isInternalError = false; | ||||||
|  |         string errorMessage = ""; | ||||||
|  |  | ||||||
|  |         try { | ||||||
|  |             _logger.LogInformation("Attempting to notify an approver"); | ||||||
|  |  | ||||||
|  |             if (notification is null) throw new ArgumentNullException("notification cannot be null"); | ||||||
|  |             if (notification.PCRB is null) throw new ArgumentNullException("PCRB cannot be null"); | ||||||
|  |             if (notification.Approval is null) throw new ArgumentNullException("approval cannot be null"); | ||||||
|  |             if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty"); | ||||||
|  |  | ||||||
|  |             await _pcrbService.NotifyApprover(notification); | ||||||
|  |  | ||||||
|  |             return Ok(); | ||||||
|  |         } catch (ArgumentException ex) { | ||||||
|  |             isArgumentError = true; | ||||||
|  |             errorMessage = ex.Message; | ||||||
|  |             return BadRequest(errorMessage); | ||||||
|  |         } catch (Exception ex) { | ||||||
|  |             isInternalError = true; | ||||||
|  |             errorMessage = $"Unable to notify an approver, because {ex.Message}"; | ||||||
|  |             return Problem(errorMessage); | ||||||
|  |         } finally { | ||||||
|  |             string metricName = "NotifyPCRBApprover"; | ||||||
|  |             DateTime end = DateTime.Now; | ||||||
|  |             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||||
|  |  | ||||||
|  |             _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     [HttpPost] |     [HttpPost] | ||||||
|     [Route("pcrb/notify/approvers")] |     [Route("pcrb/notify/approvers")] | ||||||
|     public async Task<IActionResult> NotifyApprovers(PCRBNotification notification) { |     public async Task<IActionResult> NotifyApprovers(PCRBNotification notification) { | ||||||
| @ -937,7 +973,7 @@ public class PCRBController : ControllerBase { | |||||||
|         string errorMessage = ""; |         string errorMessage = ""; | ||||||
|  |  | ||||||
|         try { |         try { | ||||||
|             _logger.LogInformation($"Attempting to get attendees for plan# {planNumber}"); |             _logger.LogInformation($"Attempting to get follow ups for plan# {planNumber}"); | ||||||
|  |  | ||||||
|             if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#"); |             if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#"); | ||||||
|  |  | ||||||
| @ -1026,4 +1062,136 @@ public class PCRBController : ControllerBase { | |||||||
|             _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError); |             _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     [HttpPost] | ||||||
|  |     [Route("pcrb/followUpComment")] | ||||||
|  |     public async Task<IActionResult> CreateFollowUpComment(PCRBFollowUpComment comment) { | ||||||
|  |         DateTime start = DateTime.Now; | ||||||
|  |         bool isArgumentError = false; | ||||||
|  |         bool isInternalError = false; | ||||||
|  |         string errorMessage = ""; | ||||||
|  |  | ||||||
|  |         try { | ||||||
|  |             _logger.LogInformation("Attempting to create follow up comment"); | ||||||
|  |  | ||||||
|  |             if (comment is null) throw new ArgumentNullException("comment cannot be null"); | ||||||
|  |  | ||||||
|  |             await _pcrbService.CreateFollowUpComment(comment); | ||||||
|  |  | ||||||
|  |             return Ok(); | ||||||
|  |         } catch (ArgumentException ex) { | ||||||
|  |             isArgumentError = true; | ||||||
|  |             errorMessage = ex.Message; | ||||||
|  |             return BadRequest(errorMessage); | ||||||
|  |         } catch (Exception ex) { | ||||||
|  |             isInternalError = true; | ||||||
|  |             errorMessage = $"Unable to create follow up comment, because {ex.Message}"; | ||||||
|  |             return Problem(errorMessage); | ||||||
|  |         } finally { | ||||||
|  |             string metricName = "CreatePCRBFollowUpComment"; | ||||||
|  |             DateTime end = DateTime.Now; | ||||||
|  |             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||||
|  |  | ||||||
|  |             _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [HttpGet] | ||||||
|  |     [Route("pcrb/followUpComments")] | ||||||
|  |     public async Task<IActionResult> GetFollowUpCommentsByPlanNumber(int planNumber, bool bypassCache) { | ||||||
|  |         DateTime start = DateTime.Now; | ||||||
|  |         bool isArgumentError = false; | ||||||
|  |         bool isInternalError = false; | ||||||
|  |         string errorMessage = ""; | ||||||
|  |  | ||||||
|  |         try { | ||||||
|  |             _logger.LogInformation($"Attempting to get follow up comments for plan# {planNumber}"); | ||||||
|  |  | ||||||
|  |             if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#"); | ||||||
|  |  | ||||||
|  |             List<PCRBFollowUpComment> comments = (await _pcrbService.GetFollowUpCommentsByPlanNumber(planNumber, bypassCache)).ToList(); | ||||||
|  |  | ||||||
|  |             return Ok(comments); | ||||||
|  |         } catch (ArgumentException ex) { | ||||||
|  |             isArgumentError = true; | ||||||
|  |             errorMessage = ex.Message; | ||||||
|  |             return BadRequest(errorMessage); | ||||||
|  |         } catch (Exception ex) { | ||||||
|  |             isInternalError = true; | ||||||
|  |             errorMessage = $"Cannot get follow up comments for plan# {planNumber}, because {ex.Message}"; | ||||||
|  |             return Problem(errorMessage); | ||||||
|  |         } finally { | ||||||
|  |             string metricName = "GetPCRBFollowUpComments"; | ||||||
|  |             DateTime end = DateTime.Now; | ||||||
|  |             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||||
|  |  | ||||||
|  |             _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [HttpPut] | ||||||
|  |     [Route("pcrb/followUpComment")] | ||||||
|  |     public async Task<IActionResult> UpdateFollowUpComment(PCRBFollowUpComment comment) { | ||||||
|  |         DateTime start = DateTime.Now; | ||||||
|  |         bool isArgumentError = false; | ||||||
|  |         bool isInternalError = false; | ||||||
|  |         string errorMessage = ""; | ||||||
|  |  | ||||||
|  |         try { | ||||||
|  |             _logger.LogInformation("Attempting to update follow up comment"); | ||||||
|  |  | ||||||
|  |             if (comment is null) throw new ArgumentNullException("comment cannot be null"); | ||||||
|  |  | ||||||
|  |             await _pcrbService.UpdateFollowUpComment(comment); | ||||||
|  |  | ||||||
|  |             return Ok(); | ||||||
|  |         } catch (ArgumentException ex) { | ||||||
|  |             isArgumentError = true; | ||||||
|  |             errorMessage = ex.Message; | ||||||
|  |             return BadRequest(errorMessage); | ||||||
|  |         } catch (Exception ex) { | ||||||
|  |             isInternalError = true; | ||||||
|  |             errorMessage = $"Unable to update follow up comment, because {ex.Message}"; | ||||||
|  |             return Problem(errorMessage); | ||||||
|  |         } finally { | ||||||
|  |             string metricName = "UpdatePCRBFollowUpComment"; | ||||||
|  |             DateTime end = DateTime.Now; | ||||||
|  |             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||||
|  |  | ||||||
|  |             _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [HttpDelete] | ||||||
|  |     [Route("pcrb/followUpComment")] | ||||||
|  |     public async Task<IActionResult> DeleteFollowUpComment(int id) { | ||||||
|  |         DateTime start = DateTime.Now; | ||||||
|  |         bool isArgumentError = false; | ||||||
|  |         bool isInternalError = false; | ||||||
|  |         string errorMessage = ""; | ||||||
|  |  | ||||||
|  |         try { | ||||||
|  |             _logger.LogInformation("Attempting to delete follow up comment"); | ||||||
|  |  | ||||||
|  |             if (id <= 0) throw new ArgumentException($"{id} is not a valid PCRB follow up comment ID"); | ||||||
|  |  | ||||||
|  |             await _pcrbService.DeleteFollowUpComment(id); | ||||||
|  |  | ||||||
|  |             return Ok(); | ||||||
|  |         } catch (ArgumentException ex) { | ||||||
|  |             isArgumentError = true; | ||||||
|  |             errorMessage = ex.Message; | ||||||
|  |             return BadRequest(errorMessage); | ||||||
|  |         } catch (Exception ex) { | ||||||
|  |             isInternalError = true; | ||||||
|  |             errorMessage = $"Unable to delete follow up comment, because {ex.Message}"; | ||||||
|  |             return Problem(errorMessage); | ||||||
|  |         } finally { | ||||||
|  |             string metricName = "DeletePCRBFollowUpComment"; | ||||||
|  |             DateTime end = DateTime.Now; | ||||||
|  |             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||||
|  |  | ||||||
|  |             _monInUtils.PostMetrics(metricName, millisecondsDiff, isArgumentError, isInternalError); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -13,6 +13,7 @@ public interface IApprovalService { | |||||||
|     Task<IEnumerable<User>> GetApprovalGroupMembers(int subRoleId); |     Task<IEnumerable<User>> GetApprovalGroupMembers(int subRoleId); | ||||||
|     Task CreateApproval(Approval approval); |     Task CreateApproval(Approval approval); | ||||||
|     Task UpdateApproval(Approval approval); |     Task UpdateApproval(Approval approval); | ||||||
|  |     Task DeleteApproval(int approvalID); | ||||||
|     Task Approve(Approval approval); |     Task Approve(Approval approval); | ||||||
|     Task Deny(Approval approval); |     Task Deny(Approval approval); | ||||||
|     Task<IEnumerable<Approval>> GetApprovalsForIssueId(int issueId, bool bypassCache); |     Task<IEnumerable<Approval>> GetApprovalsForIssueId(int issueId, bool bypassCache); | ||||||
| @ -40,12 +41,11 @@ public class ApprovalService : IApprovalService { | |||||||
|  |  | ||||||
|             StringBuilder queryBuilder = new(); |             StringBuilder queryBuilder = new(); | ||||||
|             queryBuilder.Append("insert into Approval (IssueID, RoleName, SubRole, UserID, SubRoleID, ItemStatus, "); |             queryBuilder.Append("insert into Approval (IssueID, RoleName, SubRole, UserID, SubRoleID, ItemStatus, "); | ||||||
|             queryBuilder.Append("AssignedDate, DocumentTypeID, DisplayDeniedDocument, Step, TaskID) "); |             queryBuilder.Append("AssignedDate, DocumentTypeID, DisplayDeniedDocument, Step, TaskID, CompletedDate) "); | ||||||
|             queryBuilder.Append($"values ({approval.IssueID}, '{approval.RoleName}', '{approval.SubRole}', {approval.UserID}, "); |             queryBuilder.Append("values (@IssueID, @RoleName, @SubRole, @UserID, @SubRoleID, 0, @AssignedDate, 3, 0, @Step, "); | ||||||
|             queryBuilder.Append($"{approval.SubRoleID}, 0, '{approval.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); |             queryBuilder.Append("@TaskID, @CompletedDate)"); | ||||||
|             queryBuilder.Append($"3, 0, {approval.Step}, {approval.TaskID});"); |  | ||||||
|  |  | ||||||
|             int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString()); |             int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString(), approval); | ||||||
|  |  | ||||||
|             if (rowsCreated <= 0) throw new Exception("Unable to insert approval in database"); |             if (rowsCreated <= 0) throw new Exception("Unable to insert approval in database"); | ||||||
|  |  | ||||||
| @ -70,19 +70,16 @@ public class ApprovalService : IApprovalService { | |||||||
|             if (approvals is null || approvals.Count() == 0) { |             if (approvals is null || approvals.Count() == 0) { | ||||||
|                 StringBuilder queryBuilder = new(); |                 StringBuilder queryBuilder = new(); | ||||||
|                 queryBuilder.Append("select a.*, src.SubRoleCategoryItem from Approval a "); |                 queryBuilder.Append("select a.*, src.SubRoleCategoryItem from Approval a "); | ||||||
|                 queryBuilder.Append("join SubRole sr on a.SubRoleID=sr.SubRoleID "); |                 queryBuilder.Append("left outer join SubRole sr on a.SubRoleID=sr.SubRoleID "); | ||||||
|                 queryBuilder.Append("join SubRoleCategory src on sr.SubRoleCategoryID=src.SubRoleCategoryID "); |                 queryBuilder.Append("left outer join SubRoleCategory src on sr.SubRoleCategoryID=src.SubRoleCategoryID "); | ||||||
|                 queryBuilder.Append($"where a.IssueID={issueId}"); |                 queryBuilder.Append($"where a.IssueID={issueId}"); | ||||||
|  |  | ||||||
|                 approvals = (await _dalService.QueryAsync<Approval>(queryBuilder.ToString())).ToList(); |                 approvals = (await _dalService.QueryAsync<Approval>(queryBuilder.ToString())).ToList(); | ||||||
|  |  | ||||||
|                 foreach (Approval approval in approvals) { |                 foreach (Approval approval in approvals) { | ||||||
|                     int successfulUpdates = 0; |  | ||||||
|  |  | ||||||
|                     User? user = await _userService.GetUserByUserId(approval.UserID); |                     User? user = await _userService.GetUserByUserId(approval.UserID); | ||||||
|                     if (user is not null) { |                     if (user is not null) { | ||||||
|                         approval.User = user; |                         approval.User = user; | ||||||
|                         successfulUpdates++; |  | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
|                     if (approval.ItemStatus < 0) |                     if (approval.ItemStatus < 0) | ||||||
| @ -91,6 +88,9 @@ public class ApprovalService : IApprovalService { | |||||||
|                         approval.StatusMessage = "Assigned"; |                         approval.StatusMessage = "Assigned"; | ||||||
|                     if (approval.ItemStatus > 0) |                     if (approval.ItemStatus > 0) | ||||||
|                         approval.StatusMessage = "Approved"; |                         approval.StatusMessage = "Approved"; | ||||||
|  |  | ||||||
|  |                     if (string.IsNullOrWhiteSpace(approval.SubRoleCategoryItem)) | ||||||
|  |                         approval.SubRoleCategoryItem = approval.RoleName; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 _cache.Set($"approvals{issueId}", approvals, DateTimeOffset.Now.AddMinutes(5)); |                 _cache.Set($"approvals{issueId}", approvals, DateTimeOffset.Now.AddMinutes(5)); | ||||||
| @ -217,8 +217,8 @@ public class ApprovalService : IApprovalService { | |||||||
|             if (approvals is null) { |             if (approvals is null) { | ||||||
|                 StringBuilder queryBuilder = new(); |                 StringBuilder queryBuilder = new(); | ||||||
|                 queryBuilder.Append($"select a.*, src.SubRoleCategoryItem from Approval a "); |                 queryBuilder.Append($"select a.*, src.SubRoleCategoryItem from Approval a "); | ||||||
|                 queryBuilder.Append("join SubRole sr on a.SubRoleID=sr.SubRoleID "); |                 queryBuilder.Append("left outer join SubRole sr on a.SubRoleID=sr.SubRoleID "); | ||||||
|                 queryBuilder.Append("join SubRoleCategory src on sr.SubRoleCategoryID=src.SubRoleCategoryID "); |                 queryBuilder.Append("left outer join SubRoleCategory src on sr.SubRoleCategoryID=src.SubRoleCategoryID "); | ||||||
|                 queryBuilder.Append($"where UserID={userId} and ItemStatus=0 and "); |                 queryBuilder.Append($"where UserID={userId} and ItemStatus=0 and "); | ||||||
|                 queryBuilder.Append($"(AssignedDate is not null and "); |                 queryBuilder.Append($"(AssignedDate is not null and "); | ||||||
|                 queryBuilder.Append($"AssignedDate <= '{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}' and "); |                 queryBuilder.Append($"AssignedDate <= '{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}' and "); | ||||||
| @ -265,6 +265,23 @@ public class ApprovalService : IApprovalService { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public async Task DeleteApproval(int approvalID) { | ||||||
|  |         try { | ||||||
|  |             _logger.LogInformation($"Attempting to delete approval with ID: {approvalID}"); | ||||||
|  |  | ||||||
|  |             if (approvalID <= 0) throw new ArgumentException("Invalid approval ID"); | ||||||
|  |  | ||||||
|  |             string sql = "delete from Approval where ApprovalID=@ApprovalID"; | ||||||
|  |  | ||||||
|  |             int rowsDeleted = await _dalService.ExecuteAsync(sql, new { ApprovalID = approvalID }); | ||||||
|  |  | ||||||
|  |             if (rowsDeleted <= 0) throw new Exception("unable to delete approval from database"); | ||||||
|  |         } catch (Exception ex) { | ||||||
|  |             _logger.LogError($"Unable to delete approval with ID: {approvalID}, because {ex.Message}"); | ||||||
|  |             throw; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public async Task Approve(Approval approval) { |     public async Task Approve(Approval approval) { | ||||||
|         try { |         try { | ||||||
|             _logger.LogInformation("Attempting to submit approval"); |             _logger.LogInformation("Attempting to submit approval"); | ||||||
|  | |||||||
| @ -144,7 +144,7 @@ public class AuthenticationService : IAuthenticationService { | |||||||
|                 Audience = _jwtAudience, |                 Audience = _jwtAudience, | ||||||
|                 Subject = identity, |                 Subject = identity, | ||||||
|                 NotBefore = DateTime.Now, |                 NotBefore = DateTime.Now, | ||||||
|                 Expires = DateTime.Now.AddHours(8), |                 Expires = DateTime.Now.AddDays(1), | ||||||
|                 SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) |                 SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) | ||||||
|             }; |             }; | ||||||
|  |  | ||||||
|  | |||||||
| @ -212,9 +212,9 @@ public class MRBService : IMRBService { | |||||||
|                 StringBuilder queryBuilder = new(); |                 StringBuilder queryBuilder = new(); | ||||||
|                 queryBuilder.Append("select (u.FirstName + ' ' + u.LastName) as OriginatorName, m.* "); |                 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("from MRB m join Users u on m.OriginatorID = u.UserID "); | ||||||
|                 queryBuilder.Append($"where m.Title = '{title}'"); |                 queryBuilder.Append("where m.Title = @Title"); | ||||||
|  |  | ||||||
|                 mrb = (await _dalService.QueryAsync<MRB>(queryBuilder.ToString())).FirstOrDefault(); |                 mrb = (await _dalService.QueryAsync<MRB>(queryBuilder.ToString(), new { Title=title })).FirstOrDefault(); | ||||||
|  |  | ||||||
|                 _cache.Set($"mrb{title}", mrb, DateTimeOffset.Now.AddHours(1)); |                 _cache.Set($"mrb{title}", mrb, DateTimeOffset.Now.AddHours(1)); | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -34,6 +34,7 @@ public interface IPCRBService { | |||||||
|     Task UpdatePCR3Document(PCR3Document document); |     Task UpdatePCR3Document(PCR3Document document); | ||||||
|     Task<IEnumerable<PCR3Document>> GetPCR3DocumentsForPlanNumber(int planNumber, bool bypassCache); |     Task<IEnumerable<PCR3Document>> GetPCR3DocumentsForPlanNumber(int planNumber, bool bypassCache); | ||||||
|     Task NotifyNewApprovals(PCRB pcrb); |     Task NotifyNewApprovals(PCRB pcrb); | ||||||
|  |     Task NotifyApprover(PCRBNotification notification); | ||||||
|     Task NotifyApprovers(PCRBNotification notification); |     Task NotifyApprovers(PCRBNotification notification); | ||||||
|     Task NotifyOriginator(PCRBNotification notification); |     Task NotifyOriginator(PCRBNotification notification); | ||||||
|     Task NotifyResponsiblePerson(PCRBActionItemNotification notification); |     Task NotifyResponsiblePerson(PCRBActionItemNotification notification); | ||||||
| @ -41,6 +42,10 @@ public interface IPCRBService { | |||||||
|     Task<IEnumerable<PCRBFollowUp>> GetFollowUpsByPlanNumber(int planNumber, bool bypassCache); |     Task<IEnumerable<PCRBFollowUp>> GetFollowUpsByPlanNumber(int planNumber, bool bypassCache); | ||||||
|     Task UpdateFollowUp(PCRBFollowUp followUp); |     Task UpdateFollowUp(PCRBFollowUp followUp); | ||||||
|     Task DeleteFollowUp(int id); |     Task DeleteFollowUp(int id); | ||||||
|  |     Task CreateFollowUpComment(PCRBFollowUpComment comment); | ||||||
|  |     Task<IEnumerable<PCRBFollowUpComment>> GetFollowUpCommentsByPlanNumber(int planNumber, bool bypassCache); | ||||||
|  |     Task UpdateFollowUpComment(PCRBFollowUpComment comment); | ||||||
|  |     Task DeleteFollowUpComment(int id); | ||||||
| } | } | ||||||
|  |  | ||||||
| public class PCRBService : IPCRBService { | public class PCRBService : IPCRBService { | ||||||
| @ -110,6 +115,7 @@ public class PCRBService : IPCRBService { | |||||||
|                 foreach (PCRB pcrb in allPCRBs) { |                 foreach (PCRB pcrb in allPCRBs) { | ||||||
|                     if (string.IsNullOrWhiteSpace(pcrb.OwnerName) && pcrb.OwnerID > 0) |                     if (string.IsNullOrWhiteSpace(pcrb.OwnerName) && pcrb.OwnerID > 0) | ||||||
|                         pcrb.OwnerName = (await _userService.GetUserByUserId(pcrb.OwnerID)).GetFullName(); |                         pcrb.OwnerName = (await _userService.GetUserByUserId(pcrb.OwnerID)).GetFullName(); | ||||||
|  |                     pcrb.FollowUps = await GetFollowUpsByPlanNumber(pcrb.PlanNumber, bypassCache); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 _cache.Set("allPCRBs", allPCRBs, DateTimeOffset.Now.AddHours(1)); |                 _cache.Set("allPCRBs", allPCRBs, DateTimeOffset.Now.AddHours(1)); | ||||||
| @ -144,6 +150,8 @@ public class PCRBService : IPCRBService { | |||||||
|                     if (string.IsNullOrWhiteSpace(pcrb.OwnerName) && pcrb.OwnerID > 0) |                     if (string.IsNullOrWhiteSpace(pcrb.OwnerName) && pcrb.OwnerID > 0) | ||||||
|                         pcrb.OwnerName = (await _userService.GetUserByUserId(pcrb.OwnerID)).GetFullName(); |                         pcrb.OwnerName = (await _userService.GetUserByUserId(pcrb.OwnerID)).GetFullName(); | ||||||
|  |  | ||||||
|  |                     pcrb.FollowUps = await GetFollowUpsByPlanNumber(pcrb.PlanNumber, bypassCache); | ||||||
|  |  | ||||||
|                     _cache.Set($"pcrb{planNumber}", pcrb, DateTimeOffset.Now.AddHours(1)); |                     _cache.Set($"pcrb{planNumber}", pcrb, DateTimeOffset.Now.AddHours(1)); | ||||||
|                     _cache.Set($"pcrb{pcrb.Title}", pcrb, DateTimeOffset.Now.AddHours(1)); |                     _cache.Set($"pcrb{pcrb.Title}", pcrb, DateTimeOffset.Now.AddHours(1)); | ||||||
|                 } |                 } | ||||||
| @ -169,9 +177,9 @@ public class PCRBService : IPCRBService { | |||||||
|             if (!bypassCache) pcrb = _cache.Get<PCRB>($"pcrb{title}"); |             if (!bypassCache) pcrb = _cache.Get<PCRB>($"pcrb{title}"); | ||||||
|  |  | ||||||
|             if (pcrb is null) { |             if (pcrb is null) { | ||||||
|                 string sql = $"select * from CCChangeControl where Title='{title}'"; |                 string sql = "select * from CCChangeControl where Title=@Title"; | ||||||
|  |  | ||||||
|                 pcrb = (await _dalService.QueryAsync<PCRB>(sql)).FirstOrDefault(); |                 pcrb = (await _dalService.QueryAsync<PCRB>(sql, new { Title = title })).FirstOrDefault(); | ||||||
|  |  | ||||||
|                 if (pcrb is not null) { |                 if (pcrb is not null) { | ||||||
|                     if (string.IsNullOrWhiteSpace(pcrb.OwnerName) && pcrb.OwnerID > 0) |                     if (string.IsNullOrWhiteSpace(pcrb.OwnerName) && pcrb.OwnerID > 0) | ||||||
| @ -659,6 +667,62 @@ public class PCRBService : IPCRBService { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public async Task NotifyApprover(PCRBNotification notification) { | ||||||
|  |         try { | ||||||
|  |             _logger.LogInformation("Attempting to send a notification to an approver"); | ||||||
|  |  | ||||||
|  |             if (notification is null) throw new ArgumentNullException("notification cannot be null"); | ||||||
|  |             if (notification.PCRB is null) throw new ArgumentNullException("PCRB cannot be null"); | ||||||
|  |             if (notification.Approval is null) throw new ArgumentNullException("approval cannot be null"); | ||||||
|  |  | ||||||
|  |             User user = await _userService.GetUserByUserId(notification.Approval.UserID); | ||||||
|  |  | ||||||
|  |             List<MailAddress> toAddresses = new(); | ||||||
|  |             toAddresses.Add(new MailAddress(user.Email)); | ||||||
|  |  | ||||||
|  |             List<string> ccEmails = new List<string>(); | ||||||
|  |  | ||||||
|  |             if (notification.NotifyQaPreApprover) { | ||||||
|  |                 IEnumerable<User> qaPreApprovers = await GetQAPreApprovers(); | ||||||
|  |  | ||||||
|  |                 foreach (User qaPreApprover in qaPreApprovers) { | ||||||
|  |                     if (!ccEmails.Contains(qaPreApprover.Email)) | ||||||
|  |                         ccEmails.Add(qaPreApprover.Email); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             List<MailAddress> ccAddresses = new(); | ||||||
|  |             foreach (string email in ccEmails) { | ||||||
|  |                 ccAddresses.Add(new MailAddress(email)); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             StringBuilder sb = new(); | ||||||
|  |  | ||||||
|  |             string subject = string.Empty; | ||||||
|  |             if (!string.IsNullOrWhiteSpace(notification.Subject)) { | ||||||
|  |                 subject = notification.Subject; | ||||||
|  |             } else { | ||||||
|  |                 sb.Append($"[Approval Update] Mesa Fab Approval - PCRB# {notification.PCRB.PlanNumber} - "); | ||||||
|  |                 sb.Append($"{notification.PCRB.Title}"); | ||||||
|  |  | ||||||
|  |                 subject = sb.ToString(); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             sb.Clear(); | ||||||
|  |             sb.Append($"{notification.Message} <br /> <br />"); | ||||||
|  |             sb.Append($"Click {_siteBaseUrl}/redirect?redirectPath=pcrb/{notification.PCRB.PlanNumber} "); | ||||||
|  |             sb.Append("to view the PCRB."); | ||||||
|  |  | ||||||
|  |             await _smtpService.SendEmail(toAddresses, ccAddresses, subject, sb.ToString()); | ||||||
|  |  | ||||||
|  |             notification.Approval.NotifyDate = DateTime.Now; | ||||||
|  |             await _approvalService.UpdateApproval(notification.Approval); | ||||||
|  |         } catch (Exception ex) { | ||||||
|  |             _logger.LogError($"Unable to send notification to approver, because {ex.Message}"); | ||||||
|  |             throw; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public async Task NotifyApprovers(PCRBNotification notification) { |     public async Task NotifyApprovers(PCRBNotification notification) { | ||||||
|         try { |         try { | ||||||
|             _logger.LogInformation("Attempting to send notification to approvers"); |             _logger.LogInformation("Attempting to send notification to approvers"); | ||||||
| @ -712,9 +776,28 @@ public class PCRBService : IPCRBService { | |||||||
|             List<MailAddress> toAddresses = new(); |             List<MailAddress> toAddresses = new(); | ||||||
|             toAddresses.Add(new MailAddress(user.Email)); |             toAddresses.Add(new MailAddress(user.Email)); | ||||||
|  |  | ||||||
|             List<MailAddress> ccAddresses = new(); |             List<string> ccEmails = new List<string>(); | ||||||
|  |  | ||||||
|             string subject = $"[Update] Mesa Fab Approval - PCRB# {notification.PCRB.PlanNumber} - {notification.PCRB.Title}"; |             if (notification.NotifyQaPreApprover) { | ||||||
|  |                 IEnumerable<User> qaPreApprovers = await GetQAPreApprovers(); | ||||||
|  |  | ||||||
|  |                 foreach (User qaPreApprover in qaPreApprovers) { | ||||||
|  |                     if (!ccEmails.Contains(qaPreApprover.Email)) | ||||||
|  |                         ccEmails.Add(qaPreApprover.Email); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             List<MailAddress> ccAddresses = new(); | ||||||
|  |             foreach (string email in ccEmails) { | ||||||
|  |                 ccAddresses.Add(new MailAddress(email)); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             string subject = string.Empty; | ||||||
|  |             if (!string.IsNullOrWhiteSpace(notification.Subject)) { | ||||||
|  |                 subject = notification.Subject; | ||||||
|  |             } else { | ||||||
|  |                 subject = $"[Update] Mesa Fab Approval - PCRB# {notification.PCRB.PlanNumber} - {notification.PCRB.Title}"; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             StringBuilder bodyBuilder = new(); |             StringBuilder bodyBuilder = new(); | ||||||
|             bodyBuilder.Append($"{notification.Message} <br /> <br />"); |             bodyBuilder.Append($"{notification.Message} <br /> <br />"); | ||||||
| @ -763,8 +846,8 @@ public class PCRBService : IPCRBService { | |||||||
|             if (followUp is null) throw new ArgumentNullException("follow up cannot be null"); |             if (followUp is null) throw new ArgumentNullException("follow up cannot be null"); | ||||||
|  |  | ||||||
|             StringBuilder queryBuilder = new(); |             StringBuilder queryBuilder = new(); | ||||||
|             queryBuilder.Append("insert into CCPCRBFollowUp (PlanNumber, Step, FollowUpDate, CompletedDate) "); |             queryBuilder.Append("insert into CCPCRBFollowUp (PlanNumber, Step, FollowUpDate, CompletedDate, UpdateDate) "); | ||||||
|             queryBuilder.Append("values (@PlanNumber, @Step, @FollowUpDate, @CompletedDate)"); |             queryBuilder.Append("values (@PlanNumber, @Step, @FollowUpDate, @CompletedDate, @UpdateDate)"); | ||||||
|  |  | ||||||
|             int rowsReturned = await _dalService.ExecuteAsync<PCRBFollowUp>(queryBuilder.ToString(), followUp); |             int rowsReturned = await _dalService.ExecuteAsync<PCRBFollowUp>(queryBuilder.ToString(), followUp); | ||||||
|  |  | ||||||
| @ -792,7 +875,7 @@ public class PCRBService : IPCRBService { | |||||||
|                 followUps = await _dalService.QueryAsync<PCRBFollowUp>(sql, new { PlanNumber = planNumber }); |                 followUps = await _dalService.QueryAsync<PCRBFollowUp>(sql, new { PlanNumber = planNumber }); | ||||||
|  |  | ||||||
|                 if (followUps is not null) |                 if (followUps is not null) | ||||||
|                     _cache.Set($"pcrbFollowUps{planNumber}", followUps, DateTimeOffset.Now.AddMinutes(15)); |                     _cache.Set($"pcrbFollowUps{planNumber}", followUps, DateTimeOffset.Now.AddHours(1)); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             return followUps ?? new List<PCRBFollowUp>(); |             return followUps ?? new List<PCRBFollowUp>(); | ||||||
| @ -811,7 +894,8 @@ public class PCRBService : IPCRBService { | |||||||
|  |  | ||||||
|             StringBuilder queryBuilder = new(); |             StringBuilder queryBuilder = new(); | ||||||
|             queryBuilder.Append("update CCPCRBFollowUp set Step=@Step, FollowUpDate=@FollowUpDate, IsComplete=@IsComplete, "); |             queryBuilder.Append("update CCPCRBFollowUp set Step=@Step, FollowUpDate=@FollowUpDate, IsComplete=@IsComplete, "); | ||||||
|             queryBuilder.Append("IsDeleted=@IsDeleted, CompletedDate=@CompletedDate, Comments=@Comments "); |             queryBuilder.Append("IsDeleted=@IsDeleted, CompletedDate=@CompletedDate, IsPendingApproval=@IsPendingApproval, "); | ||||||
|  |             queryBuilder.Append("UpdateDate=@UpdateDate "); | ||||||
|             queryBuilder.Append("where ID=@ID"); |             queryBuilder.Append("where ID=@ID"); | ||||||
|  |  | ||||||
|             int rowsAffected = await _dalService.ExecuteAsync<PCRBFollowUp>(queryBuilder.ToString(), followUp); |             int rowsAffected = await _dalService.ExecuteAsync<PCRBFollowUp>(queryBuilder.ToString(), followUp); | ||||||
| @ -840,6 +924,89 @@ public class PCRBService : IPCRBService { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public async Task CreateFollowUpComment(PCRBFollowUpComment comment) { | ||||||
|  |         try { | ||||||
|  |             _logger.LogInformation("Attempting to create PCRB follow up"); | ||||||
|  |  | ||||||
|  |             if (comment is null) throw new ArgumentNullException("comment cannot be null"); | ||||||
|  |  | ||||||
|  |             StringBuilder queryBuilder = new(); | ||||||
|  |             queryBuilder.Append("insert into CCPCRBFollowUpComments (PlanNumber, FollowUpID, Comment, CommentDate, UserID) "); | ||||||
|  |             queryBuilder.Append("values (@PlanNumber, @FollowUpID, @Comment, @CommentDate, @UserID)"); | ||||||
|  |  | ||||||
|  |             int rowsReturned = await _dalService.ExecuteAsync<PCRBFollowUpComment>(queryBuilder.ToString(), comment); | ||||||
|  |  | ||||||
|  |             if (rowsReturned <= 0) throw new Exception("unable to insert new follow up comment in the database"); | ||||||
|  |         } catch (Exception ex) { | ||||||
|  |             _logger.LogError($"Unable to create new follow up comment, because {ex.Message}"); | ||||||
|  |             throw; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public async Task<IEnumerable<PCRBFollowUpComment>> GetFollowUpCommentsByPlanNumber(int planNumber, bool bypassCache) { | ||||||
|  |         try { | ||||||
|  |             _logger.LogInformation($"Attempting to fetch follow up comments for PCRB {planNumber}"); | ||||||
|  |  | ||||||
|  |             if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#"); | ||||||
|  |  | ||||||
|  |             IEnumerable<PCRBFollowUpComment>? comments = new List<PCRBFollowUpComment>(); | ||||||
|  |  | ||||||
|  |             if (!bypassCache) | ||||||
|  |                 comments = _cache.Get<IEnumerable<PCRBFollowUpComment>>($"pcrbFollowUpComments{planNumber}"); | ||||||
|  |  | ||||||
|  |             if (comments is null || comments.Count() == 0) { | ||||||
|  |                 string sql = "select * from CCPCRBFollowUpComments where PlanNumber=@PlanNumber"; | ||||||
|  |  | ||||||
|  |                 comments = await _dalService.QueryAsync<PCRBFollowUpComment>(sql, new { PlanNumber = planNumber }); | ||||||
|  |  | ||||||
|  |                 if (comments is not null) | ||||||
|  |                     _cache.Set($"pcrbFollowUpComments{planNumber}", comments, DateTimeOffset.Now.AddHours(1)); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return comments ?? new List<PCRBFollowUpComment>(); | ||||||
|  |         } catch (Exception ex) { | ||||||
|  |             _logger.LogError($"Unable to fetch follow up comments for PCRB {planNumber}, because {ex.Message}"); | ||||||
|  |             throw; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public async Task UpdateFollowUpComment(PCRBFollowUpComment comment) { | ||||||
|  |         try { | ||||||
|  |             _logger.LogInformation("Attempting to update a follow up"); | ||||||
|  |  | ||||||
|  |             if (comment is null) | ||||||
|  |                 throw new ArgumentNullException("comment cannot be null"); | ||||||
|  |  | ||||||
|  |             StringBuilder queryBuilder = new(); | ||||||
|  |             queryBuilder.Append("update CCPCRBFollowUpComments set Comment=@Comment, CommentDate=@CommentDate, "); | ||||||
|  |             queryBuilder.Append("UserID=@UserID where ID=@ID"); | ||||||
|  |  | ||||||
|  |             int rowsAffected = await _dalService.ExecuteAsync<PCRBFollowUpComment>(queryBuilder.ToString(), comment); | ||||||
|  |  | ||||||
|  |             if (rowsAffected <= 0) throw new Exception("update failed in database"); | ||||||
|  |         } catch (Exception ex) { | ||||||
|  |             _logger.LogError($"Unable to update follow up comment, because {ex.Message}"); | ||||||
|  |             throw; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public async Task DeleteFollowUpComment(int id) { | ||||||
|  |         try { | ||||||
|  |             _logger.LogInformation($"Attempting to delete follow up comment {id}"); | ||||||
|  |  | ||||||
|  |             if (id <= 0) throw new ArgumentException($"{id} is not a valid follow up ID"); | ||||||
|  |  | ||||||
|  |             string sql = "delete from CCPCRBFollowUpComments 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 comment {id}, because {ex.Message}"); | ||||||
|  |             throw; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     private async Task SaveAttachmentInDb(IFormFile file, PCRBAttachment attachment) { |     private async Task SaveAttachmentInDb(IFormFile file, PCRBAttachment attachment) { | ||||||
|         try { |         try { | ||||||
|             _logger.LogInformation($"Attempting to save attachment to database"); |             _logger.LogInformation($"Attempting to save attachment to database"); | ||||||
| @ -863,4 +1030,34 @@ public class PCRBService : IPCRBService { | |||||||
|             throw; |             throw; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private async Task<IEnumerable<User>> GetQAPreApprovers() { | ||||||
|  |         try { | ||||||
|  |             _logger.LogInformation("Attempting to fetch QA Pre-Approvers"); | ||||||
|  |  | ||||||
|  |             IEnumerable<User> qaPreApprovers = new List<User>(); | ||||||
|  |  | ||||||
|  |             int qaPreApproverRoleId = await _approvalService.GetRoleIdForRoleName("QA_PRE_APPROVAL"); | ||||||
|  |  | ||||||
|  |             if (qaPreApproverRoleId > 0) { | ||||||
|  |                 IEnumerable<SubRole> qaPreApproverSubRoles = | ||||||
|  |                     await _approvalService.GetSubRolesForSubRoleName("QA_PRE_APPROVAL", qaPreApproverRoleId); | ||||||
|  |  | ||||||
|  |                 foreach (SubRole subRole in qaPreApproverSubRoles) { | ||||||
|  |                     IEnumerable<User> members = | ||||||
|  |                         await _approvalService.GetApprovalGroupMembers(subRole.SubRoleID); | ||||||
|  |  | ||||||
|  |                     foreach (User member in members) { | ||||||
|  |                         if (!qaPreApprovers.Any(u => u.UserID == member.UserID)) | ||||||
|  |                             qaPreApprovers = qaPreApprovers.Append(member); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return qaPreApprovers; | ||||||
|  |         } catch (Exception ex) { | ||||||
|  |             _logger.LogError($"Unable to fetch QA Pre-Approvers, because {ex.Message}"); | ||||||
|  |             throw; | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
							
								
								
									
										95
									
								
								MesaFabApproval.Client.Test/ApprovalServiceTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								MesaFabApproval.Client.Test/ApprovalServiceTests.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,95 @@ | |||||||
|  | using System; | ||||||
|  | using System.Net; | ||||||
|  | using System.Net.Http; | ||||||
|  | using System.Threading; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | using MesaFabApproval.Client.Services; | ||||||
|  | using MesaFabApproval.Shared.Models; | ||||||
|  |  | ||||||
|  | using Microsoft.Extensions.Caching.Memory; | ||||||
|  |  | ||||||
|  | using Moq; | ||||||
|  | using Moq.Protected; | ||||||
|  |  | ||||||
|  | using Xunit; | ||||||
|  |  | ||||||
|  | namespace MesaFabApproval.Client.Test; | ||||||
|  |  | ||||||
|  | public class ApprovalServiceTests { | ||||||
|  |     private readonly Mock<IMemoryCache> _cacheMock; | ||||||
|  |     private readonly Mock<IHttpClientFactory> _httpClientFactoryMock; | ||||||
|  |     private readonly ApprovalService _approvalService; | ||||||
|  |  | ||||||
|  |     public ApprovalServiceTests() { | ||||||
|  |         _cacheMock = new Mock<IMemoryCache>(); | ||||||
|  |         _httpClientFactoryMock = new Mock<IHttpClientFactory>(); | ||||||
|  |         _approvalService = new ApprovalService(_cacheMock.Object, _httpClientFactoryMock.Object); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task DeleteApproval_ValidApprovalID_DeletesApproval() { | ||||||
|  |         int approvalID = 1; | ||||||
|  |  | ||||||
|  |         Mock<HttpMessageHandler> handlerMock = new Mock<HttpMessageHandler>(); | ||||||
|  |  | ||||||
|  |         handlerMock | ||||||
|  |             .Protected() | ||||||
|  |             .Setup<Task<HttpResponseMessage>>( | ||||||
|  |                 "SendAsync", | ||||||
|  |                 ItExpr.Is<HttpRequestMessage>(req => req.Method == HttpMethod.Delete), | ||||||
|  |                 ItExpr.IsAny<CancellationToken>() | ||||||
|  |             ) | ||||||
|  |             .ReturnsAsync(new HttpResponseMessage { | ||||||
|  |                 StatusCode = HttpStatusCode.OK, | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = new HttpClient(handlerMock.Object) { | ||||||
|  |             BaseAddress = new Uri("https://localhost:5000") | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         _httpClientFactoryMock.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); | ||||||
|  |  | ||||||
|  |         await _approvalService.DeleteApproval(approvalID); | ||||||
|  |  | ||||||
|  |         handlerMock.Protected().Verify( | ||||||
|  |             "SendAsync", | ||||||
|  |             Times.Once(), | ||||||
|  |             ItExpr.Is<HttpRequestMessage>(req => req.Method == HttpMethod.Delete), | ||||||
|  |             ItExpr.IsAny<CancellationToken>() | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task DeleteApproval_InvalidApprovalID_ThrowsArgumentException() { | ||||||
|  |         int approvalID = 0; | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<ArgumentException>(() => _approvalService.DeleteApproval(approvalID)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task DeleteApproval_DeletionFails_ThrowsException() { | ||||||
|  |         int approvalID = 1; | ||||||
|  |  | ||||||
|  |         Mock<HttpMessageHandler> handlerMock = new Mock<HttpMessageHandler>(); | ||||||
|  |         handlerMock | ||||||
|  |             .Protected() | ||||||
|  |             .Setup<Task<HttpResponseMessage>>( | ||||||
|  |                 "SendAsync", | ||||||
|  |                 ItExpr.Is<HttpRequestMessage>(req => req.Method == HttpMethod.Delete), | ||||||
|  |                 ItExpr.IsAny<CancellationToken>() | ||||||
|  |             ) | ||||||
|  |             .ReturnsAsync(new HttpResponseMessage { | ||||||
|  |                 StatusCode = HttpStatusCode.BadRequest, | ||||||
|  |                 ReasonPhrase = "Bad Request" | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = new HttpClient(handlerMock.Object) { | ||||||
|  |             BaseAddress = new Uri("https://localhost:5000") | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         _httpClientFactoryMock.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _approvalService.DeleteApproval(approvalID)); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										18
									
								
								MesaFabApproval.Client.Test/MockMemoryCacheService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								MesaFabApproval.Client.Test/MockMemoryCacheService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | |||||||
|  | using Microsoft.Extensions.Caching.Memory; | ||||||
|  |  | ||||||
|  | using Moq; | ||||||
|  |  | ||||||
|  | namespace MesaFabApproval.Client.Test; | ||||||
|  |  | ||||||
|  | public static class MockMemoryCacheService { | ||||||
|  |     public static Mock<IMemoryCache> GetMemoryCache(object expectedValue) { | ||||||
|  |         Mock<IMemoryCache> mockMemoryCache = new Mock<IMemoryCache>(); | ||||||
|  |         mockMemoryCache | ||||||
|  |             .Setup(x => x.TryGetValue(It.IsAny<object>(), out expectedValue)) | ||||||
|  |             .Returns(true); | ||||||
|  |         mockMemoryCache | ||||||
|  |             .Setup(x => x.CreateEntry(It.IsAny<object>())) | ||||||
|  |             .Returns(Mock.Of<ICacheEntry>()); | ||||||
|  |         return mockMemoryCache; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										320
									
								
								MesaFabApproval.Client.Test/PCRBFollowUpCommentTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										320
									
								
								MesaFabApproval.Client.Test/PCRBFollowUpCommentTests.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,320 @@ | |||||||
|  | using System.Net; | ||||||
|  | using System.Text.Json; | ||||||
|  |  | ||||||
|  | using MesaFabApproval.Client.Services; | ||||||
|  | using MesaFabApproval.Shared.Models; | ||||||
|  |  | ||||||
|  | using Microsoft.Extensions.Caching.Memory; | ||||||
|  |  | ||||||
|  | using Moq; | ||||||
|  | using Moq.Protected; | ||||||
|  |  | ||||||
|  | using MudBlazor; | ||||||
|  |  | ||||||
|  | namespace MesaFabApproval.Client.Test; | ||||||
|  |  | ||||||
|  | public class PCRBFollowUpCommentTests { | ||||||
|  |     private readonly Mock<IMemoryCache> _mockCache; | ||||||
|  |     private readonly Mock<IHttpClientFactory> _mockHttpClientFactory; | ||||||
|  |     private readonly Mock<ISnackbar> _mockSnackbar; | ||||||
|  |     private readonly Mock<IUserService> _mockUserService; | ||||||
|  |     private readonly PCRBService _pcrbService; | ||||||
|  |  | ||||||
|  |     private static PCRBFollowUpComment FOLLOW_UP_COMMENT = new PCRBFollowUpComment { | ||||||
|  |         ID = 1, | ||||||
|  |         PlanNumber = 123, | ||||||
|  |         FollowUpID = 1, | ||||||
|  |         Comment = "Test Comment", | ||||||
|  |         UserID = 1 | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     private static HttpResponseMessage GET_RESPONSE = new HttpResponseMessage { | ||||||
|  |         StatusCode = HttpStatusCode.OK, | ||||||
|  |         Content = new StringContent(JsonSerializer.Serialize(new List<PCRBFollowUpComment> { FOLLOW_UP_COMMENT })) | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     private static IEnumerable<PCRBFollowUpComment> FOLLOW_UPS_COMMENTS = new List<PCRBFollowUpComment>() { FOLLOW_UP_COMMENT }; | ||||||
|  |  | ||||||
|  |     private static HttpResponseMessage SUCCESSFUL_RESPONSE = new HttpResponseMessage(HttpStatusCode.OK); | ||||||
|  |     private static HttpResponseMessage UNSUCCESSFUL_RESPONSE = new HttpResponseMessage(HttpStatusCode.InternalServerError); | ||||||
|  |  | ||||||
|  |     public PCRBFollowUpCommentTests() { | ||||||
|  |         _mockCache = MockMemoryCacheService.GetMemoryCache(FOLLOW_UPS_COMMENTS); | ||||||
|  |         _mockHttpClientFactory = new Mock<IHttpClientFactory>(); | ||||||
|  |         _mockSnackbar = new Mock<ISnackbar>(); | ||||||
|  |         _mockUserService = new Mock<IUserService>(); | ||||||
|  |  | ||||||
|  |         _pcrbService = new PCRBService( | ||||||
|  |             _mockCache.Object, | ||||||
|  |             _mockHttpClientFactory.Object, | ||||||
|  |             _mockSnackbar.Object, | ||||||
|  |             _mockUserService.Object); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task CreateFollowUpComment_WithValidParams_ShouldCallHttpPost_AndRefreshCache() { | ||||||
|  |         Mock<HttpMessageHandler> mockHttpMessageHandler = new Mock<HttpMessageHandler>(); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected() | ||||||
|  |             .Setup<Task<HttpResponseMessage>>( | ||||||
|  |                 "SendAsync", | ||||||
|  |                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Post), | ||||||
|  |                 ItExpr.IsAny<CancellationToken>()) | ||||||
|  |             .ReturnsAsync(SUCCESSFUL_RESPONSE) | ||||||
|  |             .Verifiable(); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected() | ||||||
|  |             .Setup<Task<HttpResponseMessage>>( | ||||||
|  |                 "SendAsync", | ||||||
|  |                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Get), | ||||||
|  |                 ItExpr.IsAny<CancellationToken>()) | ||||||
|  |             .ReturnsAsync(GET_RESPONSE) | ||||||
|  |             .Verifiable(); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = new HttpClient(mockHttpMessageHandler.Object) { | ||||||
|  |             BaseAddress = new Uri("https://localhost:5000") | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); | ||||||
|  |  | ||||||
|  |         await _pcrbService.CreateFollowUpComment(FOLLOW_UP_COMMENT); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected().Verify( | ||||||
|  |             "SendAsync", | ||||||
|  |             Times.Once(), | ||||||
|  |             ItExpr.Is<HttpRequestMessage>( | ||||||
|  |                 req =>  | ||||||
|  |                     req.Method == HttpMethod.Post && | ||||||
|  |                     req.RequestUri != null && | ||||||
|  |                     req.RequestUri.AbsoluteUri.Equals("https://localhost:5000/pcrb/followUpComment")), | ||||||
|  |             ItExpr.IsAny<CancellationToken>()); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected().Verify( | ||||||
|  |             "SendAsync", | ||||||
|  |             Times.Once(), | ||||||
|  |             ItExpr.Is<HttpRequestMessage>( | ||||||
|  |                 req =>  | ||||||
|  |                     req.Method == HttpMethod.Get && | ||||||
|  |                     req.RequestUri != null && | ||||||
|  |                     req.RequestUri.AbsoluteUri.Equals("https://localhost:5000/pcrb/followUpComments?planNumber=123&bypassCache=True")), | ||||||
|  |             ItExpr.IsAny<CancellationToken>()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task CreateFollowUpComment_WithBadResponse_ShouldThrowException() { | ||||||
|  |         Mock<HttpMessageHandler> mockHttpMessageHandler = new Mock<HttpMessageHandler>(); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected() | ||||||
|  |             .Setup<Task<HttpResponseMessage>>( | ||||||
|  |                 "SendAsync", | ||||||
|  |                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Post), | ||||||
|  |                 ItExpr.IsAny<CancellationToken>()) | ||||||
|  |             .ReturnsAsync(UNSUCCESSFUL_RESPONSE) | ||||||
|  |             .Verifiable(); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = new HttpClient(mockHttpMessageHandler.Object) { | ||||||
|  |             BaseAddress = new Uri("https://localhost:5000") | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _pcrbService.CreateFollowUpComment(FOLLOW_UP_COMMENT)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task CreateFollowUpComment_WithNullParam_ShouldThrowException() { | ||||||
|  |         await Assert.ThrowsAsync<ArgumentNullException>(() => _pcrbService.CreateFollowUpComment(null)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task GetFollowUpCommentsByPlanNumber_WithBypassCache_ShouldCallHttpGetAndReturnFollowUps() { | ||||||
|  |         Mock<HttpMessageHandler> mockHttpMessageHandler = new Mock<HttpMessageHandler>(); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected() | ||||||
|  |             .Setup<Task<HttpResponseMessage>>( | ||||||
|  |                 "SendAsync", | ||||||
|  |                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Get), | ||||||
|  |                 ItExpr.IsAny<CancellationToken>()) | ||||||
|  |             .ReturnsAsync(GET_RESPONSE) | ||||||
|  |             .Verifiable(); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = new HttpClient(mockHttpMessageHandler.Object) { | ||||||
|  |             BaseAddress = new Uri("https://localhost:5000") | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); | ||||||
|  |  | ||||||
|  |         IEnumerable<PCRBFollowUpComment> comments = await _pcrbService.GetFollowUpCommentsByPlanNumber(123, true); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected().Verify( | ||||||
|  |             "SendAsync", | ||||||
|  |             Times.Once(), | ||||||
|  |             ItExpr.Is<HttpRequestMessage>( | ||||||
|  |                 req => | ||||||
|  |                     req.Method == HttpMethod.Get && | ||||||
|  |                     req.RequestUri != null && | ||||||
|  |                     req.RequestUri.AbsoluteUri.Equals("https://localhost:5000/pcrb/followUpComments?planNumber=123&bypassCache=True")), | ||||||
|  |             ItExpr.IsAny<CancellationToken>()); | ||||||
|  |  | ||||||
|  |         Assert.Single(comments); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task GetFollowUpCommentsByPlanNumber_WithoutBypassCache_ShouldReturnFollowUpsFromCache() { | ||||||
|  |         IEnumerable<PCRBFollowUpComment> comments = await _pcrbService.GetFollowUpCommentsByPlanNumber(1, false); | ||||||
|  |         Assert.Single(comments); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task GetFollowUpCommentsByPlanNumber_WithBadResponse_ShouldThrowException() { | ||||||
|  |         Mock<HttpMessageHandler> mockHttpMessageHandler = new Mock<HttpMessageHandler>(); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected() | ||||||
|  |             .Setup<Task<HttpResponseMessage>>( | ||||||
|  |                 "SendAsync", | ||||||
|  |                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Get), | ||||||
|  |                 ItExpr.IsAny<CancellationToken>()) | ||||||
|  |             .ReturnsAsync(UNSUCCESSFUL_RESPONSE) | ||||||
|  |             .Verifiable(); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = new HttpClient(mockHttpMessageHandler.Object) { | ||||||
|  |             BaseAddress = new Uri("https://localhost:5000") | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _pcrbService.GetFollowUpCommentsByPlanNumber(1, true)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task UpdateFollowUpComment_WithValidParams_ShouldCallHttpPut() { | ||||||
|  |         Mock<HttpMessageHandler> mockHttpMessageHandler = new Mock<HttpMessageHandler>(); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected() | ||||||
|  |             .Setup<Task<HttpResponseMessage>>( | ||||||
|  |                 "SendAsync", | ||||||
|  |                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Put), | ||||||
|  |                 ItExpr.IsAny<CancellationToken>()) | ||||||
|  |             .ReturnsAsync(SUCCESSFUL_RESPONSE) | ||||||
|  |             .Verifiable(); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected() | ||||||
|  |             .Setup<Task<HttpResponseMessage>>( | ||||||
|  |                 "SendAsync", | ||||||
|  |                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Get), | ||||||
|  |                 ItExpr.IsAny<CancellationToken>()) | ||||||
|  |             .ReturnsAsync(GET_RESPONSE) | ||||||
|  |             .Verifiable(); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = new HttpClient(mockHttpMessageHandler.Object) { | ||||||
|  |             BaseAddress = new Uri("https://localhost:5000") | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); | ||||||
|  |  | ||||||
|  |         await _pcrbService.UpdateFollowUpComment(FOLLOW_UP_COMMENT); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected().Verify( | ||||||
|  |             "SendAsync", | ||||||
|  |             Times.Once(), | ||||||
|  |             ItExpr.Is<HttpRequestMessage>( | ||||||
|  |                 req => | ||||||
|  |                     req.Method == HttpMethod.Put && | ||||||
|  |                     req.RequestUri != null && | ||||||
|  |                     req.RequestUri.AbsoluteUri.Equals("https://localhost:5000/pcrb/followUpComment")), | ||||||
|  |             ItExpr.IsAny<CancellationToken>()); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected().Verify( | ||||||
|  |             "SendAsync", | ||||||
|  |             Times.Once(), | ||||||
|  |             ItExpr.Is<HttpRequestMessage>( | ||||||
|  |                 req => | ||||||
|  |                     req.Method == HttpMethod.Get && | ||||||
|  |                     req.RequestUri != null && | ||||||
|  |                     req.RequestUri.AbsoluteUri.Equals("https://localhost:5000/pcrb/followUpComments?planNumber=123&bypassCache=True")), | ||||||
|  |             ItExpr.IsAny<CancellationToken>()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task UpdateFollowUpComment_WithNullParam_ShouldThrowException() { | ||||||
|  |         await Assert.ThrowsAsync<ArgumentNullException>(() => _pcrbService.UpdateFollowUpComment(null)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task UpdateFollowUpComment_WithBadResponse_ShouldThrowException() { | ||||||
|  |         Mock<HttpMessageHandler> mockHttpMessageHandler = new Mock<HttpMessageHandler>(); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected() | ||||||
|  |             .Setup<Task<HttpResponseMessage>>( | ||||||
|  |                 "SendAsync", | ||||||
|  |                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Put), | ||||||
|  |                 ItExpr.IsAny<CancellationToken>()) | ||||||
|  |             .ReturnsAsync(UNSUCCESSFUL_RESPONSE) | ||||||
|  |             .Verifiable(); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = new HttpClient(mockHttpMessageHandler.Object) { | ||||||
|  |             BaseAddress = new Uri("https://localhost:5000") | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _pcrbService.UpdateFollowUpComment(FOLLOW_UP_COMMENT)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task DeleteFollowUpComment_WithValidParams_ShouldCallHttpDelete() { | ||||||
|  |         Mock<HttpMessageHandler> mockHttpMessageHandler = new Mock<HttpMessageHandler>(); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected() | ||||||
|  |             .Setup<Task<HttpResponseMessage>>( | ||||||
|  |                 "SendAsync", | ||||||
|  |                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Delete), | ||||||
|  |                 ItExpr.IsAny<CancellationToken>()) | ||||||
|  |             .ReturnsAsync(SUCCESSFUL_RESPONSE) | ||||||
|  |             .Verifiable(); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = new HttpClient(mockHttpMessageHandler.Object) { | ||||||
|  |             BaseAddress = new Uri("https://localhost:5000") | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); | ||||||
|  |  | ||||||
|  |         await _pcrbService.DeleteFollowUpComment(1); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected().Verify( | ||||||
|  |             "SendAsync", | ||||||
|  |             Times.Once(), | ||||||
|  |             ItExpr.Is<HttpRequestMessage>( | ||||||
|  |                 req => | ||||||
|  |                     req.Method == HttpMethod.Delete && | ||||||
|  |                     req.RequestUri != null && | ||||||
|  |                     req.RequestUri.AbsoluteUri.Equals("https://localhost:5000/pcrb/followUpComment?id=1")), | ||||||
|  |             ItExpr.IsAny<CancellationToken>()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task DeleteFollowUpComment_WithBadId_ShouldThrowException() { | ||||||
|  |         await Assert.ThrowsAsync<ArgumentException>(() => _pcrbService.DeleteFollowUpComment(0)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task DeleteFollowUpComment_WithBadResponse_ShouldThrowException() { | ||||||
|  |         Mock<HttpMessageHandler> mockHttpMessageHandler = new Mock<HttpMessageHandler>(); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected() | ||||||
|  |             .Setup<Task<HttpResponseMessage>>( | ||||||
|  |                 "SendAsync", | ||||||
|  |                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Delete), | ||||||
|  |                 ItExpr.IsAny<CancellationToken>()) | ||||||
|  |             .ReturnsAsync(UNSUCCESSFUL_RESPONSE) | ||||||
|  |             .Verifiable(); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = new HttpClient(mockHttpMessageHandler.Object) { | ||||||
|  |             BaseAddress = new Uri("https://localhost:5000") | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _pcrbService.DeleteFollowUpComment(1)); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										321
									
								
								MesaFabApproval.Client.Test/PCRBFollowUpTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										321
									
								
								MesaFabApproval.Client.Test/PCRBFollowUpTests.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,321 @@ | |||||||
|  | using System.Net; | ||||||
|  | using System.Text.Json; | ||||||
|  |  | ||||||
|  | using MesaFabApproval.Client.Services; | ||||||
|  | using MesaFabApproval.Shared.Models; | ||||||
|  |  | ||||||
|  | using Microsoft.Extensions.Caching.Memory; | ||||||
|  |  | ||||||
|  | using Moq; | ||||||
|  | using Moq.Protected; | ||||||
|  |  | ||||||
|  | using MudBlazor; | ||||||
|  |  | ||||||
|  | namespace MesaFabApproval.Client.Test; | ||||||
|  |  | ||||||
|  | public partial class PCRBFollowUpTests { | ||||||
|  |     private readonly Mock<IMemoryCache> _mockCache; | ||||||
|  |     private readonly Mock<IHttpClientFactory> _mockHttpClientFactory; | ||||||
|  |     private readonly Mock<ISnackbar> _mockSnackbar; | ||||||
|  |     private readonly Mock<IUserService> _mockUserService; | ||||||
|  |     private readonly PCRBService _pcrbService; | ||||||
|  |  | ||||||
|  |     private static PCRBFollowUp FOLLOW_UP = new PCRBFollowUp { | ||||||
|  |         ID = 1, | ||||||
|  |         PlanNumber = 123, | ||||||
|  |         Step = 1, | ||||||
|  |         FollowUpDate = DateTime.Now | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     private static HttpResponseMessage GET_RESPONSE = new HttpResponseMessage { | ||||||
|  |         StatusCode = HttpStatusCode.OK, | ||||||
|  |         Content = new StringContent(JsonSerializer.Serialize(new List<PCRBFollowUp> { FOLLOW_UP })) | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     private static IEnumerable<PCRBFollowUp> FOLLOW_UPS = new List<PCRBFollowUp>() { | ||||||
|  |         new PCRBFollowUp { ID = 1, PlanNumber = 1, Step = 1, FollowUpDate = DateTime.Now } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     private static HttpResponseMessage SUCCESSFUL_RESPONSE = new HttpResponseMessage(HttpStatusCode.OK); | ||||||
|  |     private static HttpResponseMessage UNSUCCESSFUL_RESPONSE = new HttpResponseMessage(HttpStatusCode.InternalServerError); | ||||||
|  |  | ||||||
|  |     public PCRBFollowUpTests() { | ||||||
|  |         _mockCache = MockMemoryCacheService.GetMemoryCache(FOLLOW_UPS); | ||||||
|  |         _mockHttpClientFactory = new Mock<IHttpClientFactory>(); | ||||||
|  |         _mockSnackbar = new Mock<ISnackbar>(); | ||||||
|  |         _mockUserService = new Mock<IUserService>(); | ||||||
|  |  | ||||||
|  |         _pcrbService = new PCRBService( | ||||||
|  |             _mockCache.Object, | ||||||
|  |             _mockHttpClientFactory.Object, | ||||||
|  |             _mockSnackbar.Object, | ||||||
|  |             _mockUserService.Object); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task CreateFollowUp_WithValidParams_ShouldCallHttpPost_AndRefreshCache() { | ||||||
|  |         Mock<HttpMessageHandler> mockHttpMessageHandler = new Mock<HttpMessageHandler>(); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected() | ||||||
|  |             .Setup<Task<HttpResponseMessage>>( | ||||||
|  |                 "SendAsync", | ||||||
|  |                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Post), | ||||||
|  |                 ItExpr.IsAny<CancellationToken>()) | ||||||
|  |             .ReturnsAsync(SUCCESSFUL_RESPONSE) | ||||||
|  |             .Verifiable(); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected() | ||||||
|  |             .Setup<Task<HttpResponseMessage>>( | ||||||
|  |                 "SendAsync", | ||||||
|  |                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Get), | ||||||
|  |                 ItExpr.IsAny<CancellationToken>()) | ||||||
|  |             .ReturnsAsync(GET_RESPONSE) | ||||||
|  |             .Verifiable(); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = new HttpClient(mockHttpMessageHandler.Object) { | ||||||
|  |             BaseAddress = new Uri("https://localhost:5000") | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); | ||||||
|  |  | ||||||
|  |         await _pcrbService.CreateFollowUp(FOLLOW_UP); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected().Verify( | ||||||
|  |             "SendAsync", | ||||||
|  |             Times.Once(), | ||||||
|  |             ItExpr.Is<HttpRequestMessage>( | ||||||
|  |                 req =>  | ||||||
|  |                     req.Method == HttpMethod.Post && | ||||||
|  |                     req.RequestUri != null && | ||||||
|  |                     req.RequestUri.AbsoluteUri.Equals("https://localhost:5000/pcrb/followUp")), | ||||||
|  |             ItExpr.IsAny<CancellationToken>()); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected().Verify( | ||||||
|  |             "SendAsync", | ||||||
|  |             Times.Once(), | ||||||
|  |             ItExpr.Is<HttpRequestMessage>( | ||||||
|  |                 req =>  | ||||||
|  |                     req.Method == HttpMethod.Get && | ||||||
|  |                     req.RequestUri != null && | ||||||
|  |                     req.RequestUri.AbsoluteUri.Equals("https://localhost:5000/pcrb/followUps?planNumber=123&bypassCache=True")), | ||||||
|  |             ItExpr.IsAny<CancellationToken>()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task CreateFollowUp_WithBadResponse_ShouldThrowException() { | ||||||
|  |         Mock<HttpMessageHandler> mockHttpMessageHandler = new Mock<HttpMessageHandler>(); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected() | ||||||
|  |             .Setup<Task<HttpResponseMessage>>( | ||||||
|  |                 "SendAsync", | ||||||
|  |                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Post), | ||||||
|  |                 ItExpr.IsAny<CancellationToken>()) | ||||||
|  |             .ReturnsAsync(UNSUCCESSFUL_RESPONSE) | ||||||
|  |             .Verifiable(); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = new HttpClient(mockHttpMessageHandler.Object) { | ||||||
|  |             BaseAddress = new Uri("https://localhost:5000") | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _pcrbService.CreateFollowUp(FOLLOW_UP)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task CreateFollowUp_WithNullParam_ShouldThrowException() { | ||||||
|  |         await Assert.ThrowsAsync<ArgumentNullException>(() => _pcrbService.CreateFollowUp(null)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task GetFollowUpsByPlanNumber_WithBypassCache_ShouldCallHttpGetAndReturnFollowUps() { | ||||||
|  |         Mock<HttpMessageHandler> mockHttpMessageHandler = new Mock<HttpMessageHandler>(); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected() | ||||||
|  |             .Setup<Task<HttpResponseMessage>>( | ||||||
|  |                 "SendAsync", | ||||||
|  |                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Get), | ||||||
|  |                 ItExpr.IsAny<CancellationToken>()) | ||||||
|  |             .ReturnsAsync(GET_RESPONSE) | ||||||
|  |             .Verifiable(); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = new HttpClient(mockHttpMessageHandler.Object) { | ||||||
|  |             BaseAddress = new Uri("https://localhost:5000") | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); | ||||||
|  |  | ||||||
|  |         IEnumerable<PCRBFollowUp> followUps = await _pcrbService.GetFollowUpsByPlanNumber(123, true); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected().Verify( | ||||||
|  |             "SendAsync", | ||||||
|  |             Times.Once(), | ||||||
|  |             ItExpr.Is<HttpRequestMessage>( | ||||||
|  |                 req => | ||||||
|  |                     req.Method == HttpMethod.Get && | ||||||
|  |                     req.RequestUri != null && | ||||||
|  |                     req.RequestUri.AbsoluteUri.Equals("https://localhost:5000/pcrb/followUps?planNumber=123&bypassCache=True")), | ||||||
|  |             ItExpr.IsAny<CancellationToken>()); | ||||||
|  |  | ||||||
|  |         Assert.Single(followUps); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task GetFollowUpsByPlanNumber_WithoutBypassCache_ShouldReturnFollowUpsFromCache() { | ||||||
|  |         IEnumerable<PCRBFollowUp> followUps = await _pcrbService.GetFollowUpsByPlanNumber(1, false); | ||||||
|  |         Assert.Single(followUps); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task GetFollowUpsByPlanNumber_WithBadResponse_ShouldThrowException() { | ||||||
|  |         Mock<HttpMessageHandler> mockHttpMessageHandler = new Mock<HttpMessageHandler>(); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected() | ||||||
|  |             .Setup<Task<HttpResponseMessage>>( | ||||||
|  |                 "SendAsync", | ||||||
|  |                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Get), | ||||||
|  |                 ItExpr.IsAny<CancellationToken>()) | ||||||
|  |             .ReturnsAsync(UNSUCCESSFUL_RESPONSE) | ||||||
|  |             .Verifiable(); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = new HttpClient(mockHttpMessageHandler.Object) { | ||||||
|  |             BaseAddress = new Uri("https://localhost:5000") | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _pcrbService.GetFollowUpsByPlanNumber(1, true)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task UpdateFollowUp_WithValidParams_ShouldCallHttpPut() { | ||||||
|  |         Mock<HttpMessageHandler> mockHttpMessageHandler = new Mock<HttpMessageHandler>(); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected() | ||||||
|  |             .Setup<Task<HttpResponseMessage>>( | ||||||
|  |                 "SendAsync", | ||||||
|  |                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Put), | ||||||
|  |                 ItExpr.IsAny<CancellationToken>()) | ||||||
|  |             .ReturnsAsync(SUCCESSFUL_RESPONSE) | ||||||
|  |             .Verifiable(); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected() | ||||||
|  |             .Setup<Task<HttpResponseMessage>>( | ||||||
|  |                 "SendAsync", | ||||||
|  |                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Get), | ||||||
|  |                 ItExpr.IsAny<CancellationToken>()) | ||||||
|  |             .ReturnsAsync(GET_RESPONSE) | ||||||
|  |             .Verifiable(); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = new HttpClient(mockHttpMessageHandler.Object) { | ||||||
|  |             BaseAddress = new Uri("https://localhost:5000") | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); | ||||||
|  |  | ||||||
|  |         await _pcrbService.UpdateFollowUp(FOLLOW_UP); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected().Verify( | ||||||
|  |             "SendAsync", | ||||||
|  |             Times.Once(), | ||||||
|  |             ItExpr.Is<HttpRequestMessage>( | ||||||
|  |                 req => | ||||||
|  |                     req.Method == HttpMethod.Put && | ||||||
|  |                     req.RequestUri != null && | ||||||
|  |                     req.RequestUri.AbsoluteUri.Equals("https://localhost:5000/pcrb/followUp")), | ||||||
|  |             ItExpr.IsAny<CancellationToken>()); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected().Verify( | ||||||
|  |             "SendAsync", | ||||||
|  |             Times.Once(), | ||||||
|  |             ItExpr.Is<HttpRequestMessage>( | ||||||
|  |                 req => | ||||||
|  |                     req.Method == HttpMethod.Get && | ||||||
|  |                     req.RequestUri != null && | ||||||
|  |                     req.RequestUri.AbsoluteUri.Equals("https://localhost:5000/pcrb/followUps?planNumber=123&bypassCache=True")), | ||||||
|  |             ItExpr.IsAny<CancellationToken>()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task UpdateFollowUp_WithNullParam_ShouldThrowException() { | ||||||
|  |         await Assert.ThrowsAsync<ArgumentNullException>(() => _pcrbService.UpdateFollowUp(null)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task UpdateFollowUp_WithBadResponse_ShouldThrowException() { | ||||||
|  |         Mock<HttpMessageHandler> mockHttpMessageHandler = new Mock<HttpMessageHandler>(); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected() | ||||||
|  |             .Setup<Task<HttpResponseMessage>>( | ||||||
|  |                 "SendAsync", | ||||||
|  |                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Put), | ||||||
|  |                 ItExpr.IsAny<CancellationToken>()) | ||||||
|  |             .ReturnsAsync(UNSUCCESSFUL_RESPONSE) | ||||||
|  |             .Verifiable(); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = new HttpClient(mockHttpMessageHandler.Object) { | ||||||
|  |             BaseAddress = new Uri("https://localhost:5000") | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _pcrbService.UpdateFollowUp(FOLLOW_UP)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task DeleteFollowUp_WithValidParams_ShouldCallHttpDelete() { | ||||||
|  |         Mock<HttpMessageHandler> mockHttpMessageHandler = new Mock<HttpMessageHandler>(); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected() | ||||||
|  |             .Setup<Task<HttpResponseMessage>>( | ||||||
|  |                 "SendAsync", | ||||||
|  |                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Delete), | ||||||
|  |                 ItExpr.IsAny<CancellationToken>()) | ||||||
|  |             .ReturnsAsync(SUCCESSFUL_RESPONSE) | ||||||
|  |             .Verifiable(); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = new HttpClient(mockHttpMessageHandler.Object) { | ||||||
|  |             BaseAddress = new Uri("https://localhost:5000") | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); | ||||||
|  |  | ||||||
|  |         await _pcrbService.DeleteFollowUp(1); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected().Verify( | ||||||
|  |             "SendAsync", | ||||||
|  |             Times.Once(), | ||||||
|  |             ItExpr.Is<HttpRequestMessage>( | ||||||
|  |                 req => | ||||||
|  |                     req.Method == HttpMethod.Delete && | ||||||
|  |                     req.RequestUri != null && | ||||||
|  |                     req.RequestUri.AbsoluteUri.Equals("https://localhost:5000/pcrb/followUp?id=1")), | ||||||
|  |             ItExpr.IsAny<CancellationToken>()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task DeleteFollowUp_WithBadId_ShouldThrowException() { | ||||||
|  |         await Assert.ThrowsAsync<ArgumentException>(() => _pcrbService.DeleteFollowUp(0)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [Fact] | ||||||
|  |     public async Task DeleteFollowUp_WithBadResponse_ShouldThrowException() { | ||||||
|  |         Mock<HttpMessageHandler> mockHttpMessageHandler = new Mock<HttpMessageHandler>(); | ||||||
|  |  | ||||||
|  |         mockHttpMessageHandler.Protected() | ||||||
|  |             .Setup<Task<HttpResponseMessage>>( | ||||||
|  |                 "SendAsync", | ||||||
|  |                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Delete), | ||||||
|  |                 ItExpr.IsAny<CancellationToken>()) | ||||||
|  |             .ReturnsAsync(UNSUCCESSFUL_RESPONSE) | ||||||
|  |             .Verifiable(); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = new HttpClient(mockHttpMessageHandler.Object) { | ||||||
|  |             BaseAddress = new Uri("https://localhost:5000") | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<Exception>(() => _pcrbService.DeleteFollowUp(1)); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,5 +1,6 @@ | |||||||
| using System.Net; | using System.Net; | ||||||
| using System.Text.Json; | using System.Net.Http; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
| using MesaFabApproval.Client.Services; | using MesaFabApproval.Client.Services; | ||||||
| using MesaFabApproval.Shared.Models; | using MesaFabApproval.Shared.Models; | ||||||
| @ -11,6 +12,8 @@ using Moq.Protected; | |||||||
|  |  | ||||||
| using MudBlazor; | using MudBlazor; | ||||||
|  |  | ||||||
|  | using Xunit; | ||||||
|  |  | ||||||
| namespace MesaFabApproval.Client.Test; | namespace MesaFabApproval.Client.Test; | ||||||
|  |  | ||||||
| public class PCRBServiceTests { | public class PCRBServiceTests { | ||||||
| @ -18,330 +21,136 @@ public class PCRBServiceTests { | |||||||
|     private readonly Mock<IHttpClientFactory> _mockHttpClientFactory; |     private readonly Mock<IHttpClientFactory> _mockHttpClientFactory; | ||||||
|     private readonly Mock<ISnackbar> _mockSnackbar; |     private readonly Mock<ISnackbar> _mockSnackbar; | ||||||
|     private readonly Mock<IUserService> _mockUserService; |     private readonly Mock<IUserService> _mockUserService; | ||||||
|  |     private readonly Mock<PCRB> _mockPCRB; | ||||||
|  |     private readonly Mock<Approval> _mockApproval; | ||||||
|  |  | ||||||
|     private readonly PCRBService _pcrbService; |     private readonly PCRBService _pcrbService; | ||||||
|  |  | ||||||
|     private static IEnumerable<PCRBFollowUp> FOLLOW_UPS = new List<PCRBFollowUp>() { |  | ||||||
|         new PCRBFollowUp { ID = 1, PlanNumber = 1, Step = 1, FollowUpDate = DateTime.Now } |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     private static HttpResponseMessage SUCCESSFUL_RESPONSE = new HttpResponseMessage(HttpStatusCode.OK); |  | ||||||
|     private static HttpResponseMessage UNSUCCESSFUL_RESPONSE = new HttpResponseMessage(HttpStatusCode.InternalServerError); |  | ||||||
|  |  | ||||||
|     public static class MockMemoryCacheService { |  | ||||||
|         public static Mock<IMemoryCache> GetMemoryCache(object expectedValue) { |  | ||||||
|             Mock<IMemoryCache> mockMemoryCache = new Mock<IMemoryCache>(); |  | ||||||
|             mockMemoryCache |  | ||||||
|                 .Setup(x => x.TryGetValue(It.IsAny<object>(), out expectedValue)) |  | ||||||
|                 .Returns(true); |  | ||||||
|             mockMemoryCache |  | ||||||
|                 .Setup(x => x.CreateEntry(It.IsAny<object>())) |  | ||||||
|                 .Returns(Mock.Of<ICacheEntry>()); |  | ||||||
|             return mockMemoryCache; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public PCRBServiceTests() { |     public PCRBServiceTests() { | ||||||
|         _mockCache = MockMemoryCacheService.GetMemoryCache(FOLLOW_UPS); |         _mockCache = new Mock<IMemoryCache>(); | ||||||
|         _mockHttpClientFactory = new Mock<IHttpClientFactory>(); |         _mockHttpClientFactory = new Mock<IHttpClientFactory>(); | ||||||
|         _mockSnackbar = new Mock<ISnackbar>(); |         _mockSnackbar = new Mock<ISnackbar>(); | ||||||
|         _mockUserService = new Mock<IUserService>(); |         _mockUserService = new Mock<IUserService>(); | ||||||
|  |         _mockPCRB = new Mock<PCRB>(); | ||||||
|  |         _mockApproval = new Mock<Approval>(); | ||||||
|  |  | ||||||
|         _pcrbService = new PCRBService( |         _pcrbService = new PCRBService( | ||||||
|             _mockCache.Object, |             _mockCache.Object, | ||||||
|             _mockHttpClientFactory.Object, |             _mockHttpClientFactory.Object, | ||||||
|             _mockSnackbar.Object, |             _mockSnackbar.Object, | ||||||
|             _mockUserService.Object); |             _mockUserService.Object | ||||||
|  |         ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     [Fact] |     [Fact] | ||||||
|     public async Task CreateFollowUp_WithValidParams_ShouldCallHttpPost_AndRefreshCache() { |     public async Task NotifyApprover_ShouldSendNotification() { | ||||||
|         PCRBFollowUp followUp = new PCRBFollowUp { |         PCRBNotification notification = new PCRBNotification { | ||||||
|             ID = 1, |             Message = "Test Message", | ||||||
|             PlanNumber = 123, |             PCRB = _mockPCRB.Object, | ||||||
|             Step = 1, |             Approval = _mockApproval.Object | ||||||
|             FollowUpDate = DateTime.Now, |  | ||||||
|             Comments = "Test" |  | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         HttpResponseMessage getResponse = new HttpResponseMessage { |         Mock<HttpMessageHandler> handlerMock = new Mock<HttpMessageHandler>(); | ||||||
|             StatusCode = HttpStatusCode.OK, |         handlerMock | ||||||
|             Content = new StringContent(JsonSerializer.Serialize(new List<PCRBFollowUp> { followUp })) |             .Protected() | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         var mockHttpMessageHandler = new Mock<HttpMessageHandler>(); |  | ||||||
|         mockHttpMessageHandler.Protected() |  | ||||||
|             .Setup<Task<HttpResponseMessage>>( |             .Setup<Task<HttpResponseMessage>>( | ||||||
|                 "SendAsync", |                 "SendAsync", | ||||||
|                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Post), |                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Post), | ||||||
|                 ItExpr.IsAny<CancellationToken>()) |                 ItExpr.IsAny<CancellationToken>() | ||||||
|             .ReturnsAsync(SUCCESSFUL_RESPONSE) |             ) | ||||||
|  |             .ReturnsAsync(new HttpResponseMessage { | ||||||
|  |                 StatusCode = HttpStatusCode.OK | ||||||
|  |             }) | ||||||
|             .Verifiable(); |             .Verifiable(); | ||||||
|  |  | ||||||
|         mockHttpMessageHandler.Protected() |         HttpClient httpClient = new HttpClient(handlerMock.Object) { | ||||||
|             .Setup<Task<HttpResponseMessage>>( |  | ||||||
|                 "SendAsync", |  | ||||||
|                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Get), |  | ||||||
|                 ItExpr.IsAny<CancellationToken>()) |  | ||||||
|             .ReturnsAsync(getResponse) |  | ||||||
|             .Verifiable(); |  | ||||||
|  |  | ||||||
|         var httpClient = new HttpClient(mockHttpMessageHandler.Object) { |  | ||||||
|             BaseAddress = new Uri("https://localhost:5000") |             BaseAddress = new Uri("https://localhost:5000") | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); |         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); | ||||||
|  |  | ||||||
|         await _pcrbService.CreateFollowUp(followUp); |         await _pcrbService.NotifyApprover(notification); | ||||||
|  |  | ||||||
|         mockHttpMessageHandler.Protected().Verify( |         handlerMock.Protected().Verify( | ||||||
|             "SendAsync", |             "SendAsync", | ||||||
|             Times.Once(), |             Times.Once(), | ||||||
|             ItExpr.Is<HttpRequestMessage>( |             ItExpr.Is<HttpRequestMessage>(req => | ||||||
|                 req =>  |                 req.Method == HttpMethod.Post && | ||||||
|                     req.Method == HttpMethod.Post && |                 req.RequestUri == new Uri("https://localhost:5000/pcrb/notify/approver") | ||||||
|                     req.RequestUri != null && |             ), | ||||||
|                     req.RequestUri.AbsoluteUri.Equals("https://localhost:5000/pcrb/followUp")), |             ItExpr.IsAny<CancellationToken>() | ||||||
|             ItExpr.IsAny<CancellationToken>()); |         ); | ||||||
|  |  | ||||||
|         mockHttpMessageHandler.Protected().Verify( |  | ||||||
|             "SendAsync", |  | ||||||
|             Times.Once(), |  | ||||||
|             ItExpr.Is<HttpRequestMessage>( |  | ||||||
|                 req =>  |  | ||||||
|                     req.Method == HttpMethod.Get && |  | ||||||
|                     req.RequestUri != null && |  | ||||||
|                     req.RequestUri.AbsoluteUri.Equals("https://localhost:5000/pcrb/followUps?planNumber=123&bypassCache=True")), |  | ||||||
|             ItExpr.IsAny<CancellationToken>()); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     [Fact] |     [Fact] | ||||||
|     public async Task CreateFollowUp_WithBadResponse_ShouldThrowException() { |     public async Task NotifyApprover_ShouldThrowException_WhenResponseIsNotSuccess() { | ||||||
|         PCRBFollowUp followUp = new PCRBFollowUp { |         PCRBNotification notification = new PCRBNotification { | ||||||
|             ID = 1, |             Message = "Test Message", | ||||||
|             PlanNumber = 123, |             PCRB = _mockPCRB.Object, | ||||||
|             Step = 1, |             Approval = _mockApproval.Object | ||||||
|             FollowUpDate = DateTime.Now, |  | ||||||
|             Comments = "Test" |  | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         var mockHttpMessageHandler = new Mock<HttpMessageHandler>(); |         var handlerMock = new Mock<HttpMessageHandler>(); | ||||||
|         mockHttpMessageHandler.Protected() |         handlerMock | ||||||
|  |             .Protected() | ||||||
|             .Setup<Task<HttpResponseMessage>>( |             .Setup<Task<HttpResponseMessage>>( | ||||||
|                 "SendAsync", |                 "SendAsync", | ||||||
|                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Post), |                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Post), | ||||||
|                 ItExpr.IsAny<CancellationToken>()) |                 ItExpr.IsAny<CancellationToken>() | ||||||
|             .ReturnsAsync(UNSUCCESSFUL_RESPONSE) |             ) | ||||||
|             .Verifiable(); |             .ReturnsAsync(new HttpResponseMessage { | ||||||
|  |                 StatusCode = HttpStatusCode.BadRequest, | ||||||
|  |                 ReasonPhrase = "Bad Request" | ||||||
|  |             }); | ||||||
|  |  | ||||||
|         var httpClient = new HttpClient(mockHttpMessageHandler.Object) { |         HttpClient httpClient = new HttpClient(handlerMock.Object) { | ||||||
|             BaseAddress = new Uri("https://localhost:5000") |  | ||||||
|         }; |  | ||||||
|         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); |  | ||||||
|  |  | ||||||
|         await Assert.ThrowsAsync<Exception>(() => _pcrbService.CreateFollowUp(followUp)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     [Fact] |  | ||||||
|     public async Task CreateFollowUp_WithNullParam_ShouldThrowException() { |  | ||||||
|         await Assert.ThrowsAsync<ArgumentNullException>(() => _pcrbService.CreateFollowUp(null)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     [Fact] |  | ||||||
|     public async Task GetFollowUpsByPlanNumber_WithBypassCache_ShouldCallHttpGetAndReturnFollowUps() { |  | ||||||
|         PCRBFollowUp followUp = new PCRBFollowUp { |  | ||||||
|             ID = 1, |  | ||||||
|             PlanNumber = 123, |  | ||||||
|             Step = 1, |  | ||||||
|             FollowUpDate = DateTime.Now, |  | ||||||
|             Comments = "Test" |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         HttpResponseMessage getResponse = new HttpResponseMessage { |  | ||||||
|             StatusCode = HttpStatusCode.OK, |  | ||||||
|             Content = new StringContent(JsonSerializer.Serialize(new List<PCRBFollowUp> { followUp })) |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         var mockHttpMessageHandler = new Mock<HttpMessageHandler>(); |  | ||||||
|  |  | ||||||
|         mockHttpMessageHandler.Protected() |  | ||||||
|             .Setup<Task<HttpResponseMessage>>( |  | ||||||
|                 "SendAsync", |  | ||||||
|                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Get), |  | ||||||
|                 ItExpr.IsAny<CancellationToken>()) |  | ||||||
|             .ReturnsAsync(getResponse) |  | ||||||
|             .Verifiable(); |  | ||||||
|  |  | ||||||
|         var httpClient = new HttpClient(mockHttpMessageHandler.Object) { |  | ||||||
|             BaseAddress = new Uri("https://localhost:5000") |  | ||||||
|         }; |  | ||||||
|         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); |  | ||||||
|  |  | ||||||
|         IEnumerable<PCRBFollowUp> followUps = await _pcrbService.GetFollowUpsByPlanNumber(123, true); |  | ||||||
|  |  | ||||||
|         mockHttpMessageHandler.Protected().Verify( |  | ||||||
|             "SendAsync", |  | ||||||
|             Times.Once(), |  | ||||||
|             ItExpr.Is<HttpRequestMessage>( |  | ||||||
|                 req => |  | ||||||
|                     req.Method == HttpMethod.Get && |  | ||||||
|                     req.RequestUri != null && |  | ||||||
|                     req.RequestUri.AbsoluteUri.Equals("https://localhost:5000/pcrb/followUps?planNumber=123&bypassCache=True")), |  | ||||||
|             ItExpr.IsAny<CancellationToken>()); |  | ||||||
|  |  | ||||||
|         Assert.Single(followUps); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     [Fact] |  | ||||||
|     public async Task GetFollowUpsByPlanNumber_WithoutBypassCache_ShouldReturnFollowUpsFromCache() { |  | ||||||
|         IEnumerable<PCRBFollowUp> followUps = await _pcrbService.GetFollowUpsByPlanNumber(1, false); |  | ||||||
|         Assert.Single(followUps); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     [Fact] |  | ||||||
|     public async Task GetFollowUpsByPlanNumber_WithBadResponse_ShouldThrowException() { |  | ||||||
|         var mockHttpMessageHandler = new Mock<HttpMessageHandler>(); |  | ||||||
|  |  | ||||||
|         mockHttpMessageHandler.Protected() |  | ||||||
|             .Setup<Task<HttpResponseMessage>>( |  | ||||||
|                 "SendAsync", |  | ||||||
|                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Get), |  | ||||||
|                 ItExpr.IsAny<CancellationToken>()) |  | ||||||
|             .ReturnsAsync(UNSUCCESSFUL_RESPONSE) |  | ||||||
|             .Verifiable(); |  | ||||||
|  |  | ||||||
|         var httpClient = new HttpClient(mockHttpMessageHandler.Object) { |  | ||||||
|             BaseAddress = new Uri("https://localhost:5000") |             BaseAddress = new Uri("https://localhost:5000") | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); |         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); | ||||||
|  |  | ||||||
|         await Assert.ThrowsAsync<Exception>(() => _pcrbService.GetFollowUpsByPlanNumber(1, true)); |         Exception exception = await Assert.ThrowsAsync<Exception>(() => _pcrbService.NotifyApprover(notification)); | ||||||
|  |         Assert.Equal("Unable to notify PCRB approver, because Bad Request", exception.Message); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     [Fact] |     [Fact] | ||||||
|     public async Task UpdateFollowUp_WithValidParams_ShouldCallHttpPut() { |     public async Task NotifyApprover_ShouldThrowException_WhenNotificationIsNull() { | ||||||
|         PCRBFollowUp followUp = new PCRBFollowUp { |         await Assert.ThrowsAsync<ArgumentNullException>(() => _pcrbService.NotifyApprover(null)); | ||||||
|             ID = 1, |  | ||||||
|             PlanNumber = 123, |  | ||||||
|             Step = 1, |  | ||||||
|             FollowUpDate = DateTime.Now, |  | ||||||
|             Comments = "Test" |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         var mockHttpMessageHandler = new Mock<HttpMessageHandler>(); |  | ||||||
|  |  | ||||||
|         mockHttpMessageHandler.Protected() |  | ||||||
|             .Setup<Task<HttpResponseMessage>>( |  | ||||||
|                 "SendAsync", |  | ||||||
|                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Put), |  | ||||||
|                 ItExpr.IsAny<CancellationToken>()) |  | ||||||
|             .ReturnsAsync(SUCCESSFUL_RESPONSE) |  | ||||||
|             .Verifiable(); |  | ||||||
|  |  | ||||||
|         var httpClient = new HttpClient(mockHttpMessageHandler.Object) { |  | ||||||
|             BaseAddress = new Uri("https://localhost:5000") |  | ||||||
|         }; |  | ||||||
|         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); |  | ||||||
|  |  | ||||||
|         await _pcrbService.UpdateFollowUp(followUp); |  | ||||||
|  |  | ||||||
|         mockHttpMessageHandler.Protected().Verify( |  | ||||||
|             "SendAsync", |  | ||||||
|             Times.Once(), |  | ||||||
|             ItExpr.Is<HttpRequestMessage>( |  | ||||||
|                 req => |  | ||||||
|                     req.Method == HttpMethod.Put && |  | ||||||
|                     req.RequestUri != null && |  | ||||||
|                     req.RequestUri.AbsoluteUri.Equals("https://localhost:5000/pcrb/followUp")), |  | ||||||
|             ItExpr.IsAny<CancellationToken>()); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     [Fact] |     [Fact] | ||||||
|     public async Task UpdateFollowUp_WithNullParam_ShouldThrowException() { |     public async Task NotifyApprover_ShouldThrowException_WhenPCRBIsNull() { | ||||||
|         await Assert.ThrowsAsync<ArgumentNullException>(() => _pcrbService.UpdateFollowUp(null)); |         PCRBNotification notification = new PCRBNotification { | ||||||
|  |             Message = "Test Message", | ||||||
|  |             PCRB = null, | ||||||
|  |             Approval = _mockApproval.Object | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         await Assert.ThrowsAsync<ArgumentNullException>(() => _pcrbService.NotifyApprover(notification)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     [Fact] |     [Fact] | ||||||
|     public async Task UpdateFollowUp_WithBadResponse_ShouldThrowException() { |     public async Task NotifyApprover_ShouldThrowException_WhenApprovalIsNull() { | ||||||
|         PCRBFollowUp followUp = new PCRBFollowUp { |         PCRBNotification notification = new PCRBNotification { | ||||||
|             ID = 1, |             Message = "Test Message", | ||||||
|             PlanNumber = 123, |             PCRB = _mockPCRB.Object, | ||||||
|             Step = 1, |             Approval = null | ||||||
|             FollowUpDate = DateTime.Now, |  | ||||||
|             Comments = "Test" |  | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         var mockHttpMessageHandler = new Mock<HttpMessageHandler>(); |         await Assert.ThrowsAsync<ArgumentNullException>(() => _pcrbService.NotifyApprover(notification)); | ||||||
|  |  | ||||||
|         mockHttpMessageHandler.Protected() |  | ||||||
|             .Setup<Task<HttpResponseMessage>>( |  | ||||||
|                 "SendAsync", |  | ||||||
|                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Put), |  | ||||||
|                 ItExpr.IsAny<CancellationToken>()) |  | ||||||
|             .ReturnsAsync(UNSUCCESSFUL_RESPONSE) |  | ||||||
|             .Verifiable(); |  | ||||||
|  |  | ||||||
|         var httpClient = new HttpClient(mockHttpMessageHandler.Object) { |  | ||||||
|             BaseAddress = new Uri("https://localhost:5000") |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); |  | ||||||
|  |  | ||||||
|         await Assert.ThrowsAsync<Exception>(() => _pcrbService.UpdateFollowUp(followUp)); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     [Fact] |     [Fact] | ||||||
|     public async Task DeleteFollowUp_WithValidParams_ShouldCallHttpDelete() { |     public async Task NotifyApprover_ShouldThrowException_WhenMessageIsNullOrEmpty() { | ||||||
|         var mockHttpMessageHandler = new Mock<HttpMessageHandler>(); |         PCRBNotification notification = new PCRBNotification { | ||||||
|  |             Message = null, | ||||||
|         mockHttpMessageHandler.Protected() |             PCRB = _mockPCRB.Object, | ||||||
|             .Setup<Task<HttpResponseMessage>>( |             Approval = _mockApproval.Object | ||||||
|                 "SendAsync", |  | ||||||
|                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Delete), |  | ||||||
|                 ItExpr.IsAny<CancellationToken>()) |  | ||||||
|             .ReturnsAsync(SUCCESSFUL_RESPONSE) |  | ||||||
|             .Verifiable(); |  | ||||||
|  |  | ||||||
|         var httpClient = new HttpClient(mockHttpMessageHandler.Object) { |  | ||||||
|             BaseAddress = new Uri("https://localhost:5000") |  | ||||||
|         }; |  | ||||||
|         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); |  | ||||||
|  |  | ||||||
|         await _pcrbService.DeleteFollowUp(1); |  | ||||||
|  |  | ||||||
|         mockHttpMessageHandler.Protected().Verify( |  | ||||||
|             "SendAsync", |  | ||||||
|             Times.Once(), |  | ||||||
|             ItExpr.Is<HttpRequestMessage>( |  | ||||||
|                 req => |  | ||||||
|                     req.Method == HttpMethod.Delete && |  | ||||||
|                     req.RequestUri != null && |  | ||||||
|                     req.RequestUri.AbsoluteUri.Equals("https://localhost:5000/pcrb/followUp?id=1")), |  | ||||||
|             ItExpr.IsAny<CancellationToken>()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     [Fact] |  | ||||||
|     public async Task DeleteFollowUp_WithBadId_ShouldThrowException() { |  | ||||||
|         await Assert.ThrowsAsync<ArgumentException>(() => _pcrbService.DeleteFollowUp(0)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     [Fact] |  | ||||||
|     public async Task DeleteFollowUp_WithBadResponse_ShouldThrowException() { |  | ||||||
|         var mockHttpMessageHandler = new Mock<HttpMessageHandler>(); |  | ||||||
|  |  | ||||||
|         mockHttpMessageHandler.Protected() |  | ||||||
|             .Setup<Task<HttpResponseMessage>>( |  | ||||||
|                 "SendAsync", |  | ||||||
|                 ItExpr.Is<HttpRequestMessage>(_ => _.Method == HttpMethod.Delete), |  | ||||||
|                 ItExpr.IsAny<CancellationToken>()) |  | ||||||
|             .ReturnsAsync(UNSUCCESSFUL_RESPONSE) |  | ||||||
|             .Verifiable(); |  | ||||||
|  |  | ||||||
|         var httpClient = new HttpClient(mockHttpMessageHandler.Object) { |  | ||||||
|             BaseAddress = new Uri("https://localhost:5000") |  | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         _mockHttpClientFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient); |         await Assert.ThrowsAsync<ArgumentException>(() => _pcrbService.NotifyApprover(notification)); | ||||||
|  |  | ||||||
|         await Assert.ThrowsAsync<Exception>(() => _pcrbService.DeleteFollowUp(1)); |         notification.Message = string.Empty; | ||||||
|  |         await Assert.ThrowsAsync<ArgumentException>(() => _pcrbService.NotifyApprover(notification)); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -15,15 +15,15 @@ | |||||||
|     </ItemGroup> |     </ItemGroup> | ||||||
|  |  | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.8" /> |     <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.14" /> | ||||||
|     <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" /> |     <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.14" /> | ||||||
|     <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.8" PrivateAssets="all" /> |     <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.14" PrivateAssets="all" /> | ||||||
|     <PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" /> |     <PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.3.0" /> | ||||||
|     <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" /> |     <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.3.0" /> | ||||||
|     <PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="8.0.8" /> |     <PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="8.0.14" /> | ||||||
|     <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" /> |     <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" /> | ||||||
|     <PackageReference Include="MudBlazor" Version="7.6.0" /> |     <PackageReference Include="MudBlazor" Version="8.3.0" /> | ||||||
|     <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.0.1" /> |     <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.6.1" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|  |  | ||||||
|     <ItemGroup> |     <ItemGroup> | ||||||
|  | |||||||
| @ -1,7 +1,9 @@ | |||||||
| using MesaFabApproval.Client.Services; | using MesaFabApproval.Client.Services; | ||||||
|  | using MesaFabApproval.Shared.Models; | ||||||
|  |  | ||||||
| using Microsoft.AspNetCore.Components; | using Microsoft.AspNetCore.Components; | ||||||
| using Microsoft.AspNetCore.WebUtilities; | using Microsoft.AspNetCore.WebUtilities; | ||||||
|  | using Microsoft.Extensions.Caching.Memory; | ||||||
|  |  | ||||||
| using MudBlazor; | using MudBlazor; | ||||||
|  |  | ||||||
| @ -13,8 +15,8 @@ public partial class AuthenticatedRedirect { | |||||||
|     [Inject] MesaFabApprovalAuthStateProvider authStateProvider { get; set; } |     [Inject] MesaFabApprovalAuthStateProvider authStateProvider { get; set; } | ||||||
|     [Inject] IAuthenticationService authService { get; set; } |     [Inject] IAuthenticationService authService { get; set; } | ||||||
|     [Inject] IUserService userService { get; set; } |     [Inject] IUserService userService { get; set; } | ||||||
|     [Inject] ISnackbar snackbar { get; set; } |  | ||||||
|     [Inject] NavigationManager navigationManager { get; set; } |     [Inject] NavigationManager navigationManager { get; set; } | ||||||
|  |     [Inject] IMemoryCache cache { get; set; } | ||||||
|  |  | ||||||
|     private string? _jwt; |     private string? _jwt; | ||||||
|     private string? _refreshToken; |     private string? _refreshToken; | ||||||
| @ -53,13 +55,16 @@ public partial class AuthenticatedRedirect { | |||||||
|                 await authStateProvider.StateHasChanged(principal); |                 await authStateProvider.StateHasChanged(principal); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             AuthTokens authTokens = await authService.GetAuthTokens(); | ||||||
|  |  | ||||||
|             if (authStateProvider.CurrentUser is not null && !string.IsNullOrWhiteSpace(_redirectPath)) { |             if (authStateProvider.CurrentUser is not null && !string.IsNullOrWhiteSpace(_redirectPath)) { | ||||||
|                 navigationManager.NavigateTo(_redirectPath); |                 navigationManager.NavigateTo(_redirectPath); | ||||||
|             } else { |             } else { | ||||||
|                 await authStateProvider.Logout(); |                 await authStateProvider.Logout(); | ||||||
|  |  | ||||||
|                 if (!string.IsNullOrWhiteSpace(_redirectPath)) { |                 if (!string.IsNullOrWhiteSpace(_redirectPath)) { | ||||||
|                     navigationManager.NavigateTo($"login/{_redirectPath}"); |                     cache.Set("redirectUrl", _redirectPath); | ||||||
|  |                     navigationManager.NavigateTo($"login?redirectPath={_redirectPath}"); | ||||||
|                 } else { |                 } else { | ||||||
|                     navigationManager.NavigateTo("login"); |                     navigationManager.NavigateTo("login"); | ||||||
|                 } |                 } | ||||||
| @ -68,7 +73,8 @@ public partial class AuthenticatedRedirect { | |||||||
|             await authStateProvider.Logout(); |             await authStateProvider.Logout(); | ||||||
|  |  | ||||||
|             if (!string.IsNullOrWhiteSpace(_redirectPath)) { |             if (!string.IsNullOrWhiteSpace(_redirectPath)) { | ||||||
|                 navigationManager.NavigateTo($"login/{_redirectPath}"); |                 cache.Set("redirectUrl", _redirectPath); | ||||||
|  |                 navigationManager.NavigateTo($"login?redirectPath={_redirectPath}"); | ||||||
|             } else { |             } else { | ||||||
|                 navigationManager.NavigateTo("login"); |                 navigationManager.NavigateTo("login"); | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -37,7 +37,7 @@ | |||||||
| </MudDialog> | </MudDialog> | ||||||
|  |  | ||||||
| @code { | @code { | ||||||
|     [CascadingParameter] MudDialogInstance MudDialog { get; set; } |     [CascadingParameter] IMudDialogInstance MudDialog { get; set; } | ||||||
|  |  | ||||||
|     [Parameter] |     [Parameter] | ||||||
|     public string comments { get; set; } = ""; |     public string comments { get; set; } = ""; | ||||||
|  | |||||||
| @ -54,7 +54,7 @@ | |||||||
| </MudDialog> | </MudDialog> | ||||||
|  |  | ||||||
| @code { | @code { | ||||||
|     [CascadingParameter] MudDialogInstance MudDialog { get; set; } |     [CascadingParameter] IMudDialogInstance MudDialog { get; set; } | ||||||
|  |  | ||||||
|     [Parameter] |     [Parameter] | ||||||
|     public string comments { get; set; } = ""; |     public string comments { get; set; } = ""; | ||||||
|  | |||||||
| @ -143,7 +143,7 @@ | |||||||
| </MudDialog> | </MudDialog> | ||||||
|  |  | ||||||
| @code { | @code { | ||||||
|     [CascadingParameter] MudDialogInstance MudDialog { get; set; } |     [CascadingParameter] IMudDialogInstance MudDialog { get; set; } | ||||||
|  |  | ||||||
|     [Parameter] |     [Parameter] | ||||||
|     public MRBAction mrbAction { get; set; } |     public MRBAction mrbAction { get; set; } | ||||||
|  | |||||||
| @ -43,7 +43,7 @@ | |||||||
| </MudOverlay> | </MudOverlay> | ||||||
|  |  | ||||||
| @code { | @code { | ||||||
|     [CascadingParameter] MudDialogInstance MudDialog { get; set; } |     [CascadingParameter] IMudDialogInstance MudDialog { get; set; } | ||||||
|  |  | ||||||
|     [Parameter] |     [Parameter] | ||||||
|     public User selectedUser { get; set; } |     public User selectedUser { get; set; } | ||||||
|  | |||||||
| @ -74,7 +74,7 @@ | |||||||
|  |  | ||||||
| @code { | @code { | ||||||
|     [CascadingParameter]  |     [CascadingParameter]  | ||||||
|     MudDialogInstance MudDialog { get; set; } |     IMudDialogInstance MudDialog { get; set; } | ||||||
|  |  | ||||||
|     [Parameter] |     [Parameter] | ||||||
|     public required PCR3Document document { get; set; } |     public required PCR3Document document { get; set; } | ||||||
|  | |||||||
| @ -73,7 +73,7 @@ | |||||||
|  |  | ||||||
| @code { | @code { | ||||||
|     [CascadingParameter]  |     [CascadingParameter]  | ||||||
|     MudDialogInstance MudDialog { get; set; } |     IMudDialogInstance MudDialog { get; set; } | ||||||
|  |  | ||||||
|     [Parameter] |     [Parameter] | ||||||
|     public int planNumber { get; set; } = 0; |     public int planNumber { get; set; } = 0; | ||||||
|  | |||||||
| @ -61,7 +61,7 @@ | |||||||
|  |  | ||||||
| @code { | @code { | ||||||
|     [CascadingParameter]  |     [CascadingParameter]  | ||||||
|     MudDialogInstance MudDialog { get; set; } |     IMudDialogInstance MudDialog { get; set; } | ||||||
|  |  | ||||||
|     [Parameter] |     [Parameter] | ||||||
|     public int planNumber { get; set; } = 0; |     public int planNumber { get; set; } = 0; | ||||||
|  | |||||||
| @ -58,7 +58,7 @@ | |||||||
| </MudDialog> | </MudDialog> | ||||||
|  |  | ||||||
| @code { | @code { | ||||||
|     [CascadingParameter] MudDialogInstance MudDialog { get; set; } |     [CascadingParameter] IMudDialogInstance MudDialog { get; set; } | ||||||
|  |  | ||||||
|     [Parameter] |     [Parameter] | ||||||
|     public int planNumber { get; set; } = 0; |     public int planNumber { get; set; } = 0; | ||||||
|  | |||||||
| @ -43,7 +43,7 @@ | |||||||
| </MudOverlay> | </MudOverlay> | ||||||
|  |  | ||||||
| @code { | @code { | ||||||
|     [CascadingParameter] MudDialogInstance MudDialog { get; set; } |     [CascadingParameter] IMudDialogInstance MudDialog { get; set; } | ||||||
|  |  | ||||||
|     [Parameter] |     [Parameter] | ||||||
|     public User selectedUser { get; set; } |     public User selectedUser { get; set; } | ||||||
|  | |||||||
| @ -26,7 +26,7 @@ | |||||||
|                                 </MudTableSortLabel> |                                 </MudTableSortLabel> | ||||||
|                             </MudTh> |                             </MudTh> | ||||||
|                             <MudTh> |                             <MudTh> | ||||||
|                                 <MudTableSortLabel SortBy="new Func<Approval,object>(x=>x.SubRoleCategoryItem)"> |                                 <MudTableSortLabel SortBy="new Func<Approval,object>(x=>GetRoleName(x))"> | ||||||
|                                     Role |                                     Role | ||||||
|                                 </MudTableSortLabel> |                                 </MudTableSortLabel> | ||||||
|                             </MudTh> |                             </MudTh> | ||||||
| @ -47,7 +47,7 @@ | |||||||
|                                     <MudLink OnClick="@(() => FollowLink(context.IssueID))">@context.IssueID</MudLink> |                                     <MudLink OnClick="@(() => FollowLink(context.IssueID))">@context.IssueID</MudLink> | ||||||
|                                 } |                                 } | ||||||
|                             </MudTd> |                             </MudTd> | ||||||
|                             <MudTd DataLabel="Role">@context.SubRoleCategoryItem</MudTd> |                             <MudTd DataLabel="Role">@(GetRoleName(context))</MudTd> | ||||||
|                             <MudTd DataLabel="Assigned Date">@DateTimeUtilities.GetDateAsStringMinDefault(context.AssignedDate)</MudTd> |                             <MudTd DataLabel="Assigned Date">@DateTimeUtilities.GetDateAsStringMinDefault(context.AssignedDate)</MudTd> | ||||||
|                             <MudTd DataLabel="Step">@context.Step</MudTd> |                             <MudTd DataLabel="Step">@context.Step</MudTd> | ||||||
|                         </RowTemplate> |                         </RowTemplate> | ||||||
|  | |||||||
| @ -118,7 +118,7 @@ public partial class Dashboard { | |||||||
|  |  | ||||||
|     private void GoTo(string page) { |     private void GoTo(string page) { | ||||||
|         cache.Set("redirectUrl", page); |         cache.Set("redirectUrl", page); | ||||||
|         navigationManager.NavigateTo("/" + page); |         navigationManager.NavigateTo(page); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private async Task GoToExternal(string url, string content) { |     private async Task GoToExternal(string url, string content) { | ||||||
| @ -154,4 +154,12 @@ public partial class Dashboard { | |||||||
|         if (step < 0 || step > (PCRB.Stages.Length - 1)) return string.Empty; |         if (step < 0 || step > (PCRB.Stages.Length - 1)) return string.Empty; | ||||||
|         else return PCRB.Stages[step]; |         else return PCRB.Stages[step]; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private string GetRoleName(Approval approval) { | ||||||
|  |         string roleName = approval.SubRoleCategoryItem; | ||||||
|  |         if (string.IsNullOrWhiteSpace(roleName)) { | ||||||
|  |             roleName = approval.RoleName; | ||||||
|  |         } | ||||||
|  |         return roleName; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,6 +1,4 @@ | |||||||
| @page "/login" | @page "/login" | ||||||
| @page "/login/{redirectUrl}" |  | ||||||
| @page "/login/{redirectUrl}/{redirectUrlSub}" |  | ||||||
| @attribute [AllowAnonymous] | @attribute [AllowAnonymous] | ||||||
| @inject MesaFabApprovalAuthStateProvider authStateProvider | @inject MesaFabApprovalAuthStateProvider authStateProvider | ||||||
| @inject NavigationManager navManager | @inject NavigationManager navManager | ||||||
| @ -46,68 +44,5 @@ | |||||||
|             } |             } | ||||||
|         </MudButton> |         </MudButton> | ||||||
|         <MudDivider /> |         <MudDivider /> | ||||||
|         @* <MudButton |  | ||||||
|             Variant="Variant.Filled" |  | ||||||
|             Color="Color.Tertiary" |  | ||||||
|             Class="m-1" |  | ||||||
|             OnClick="LoginLocal" > |  | ||||||
|         @if (processingLocal) { |  | ||||||
|             <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> |  | ||||||
|             <MudText>Processing</MudText> |  | ||||||
|         } else { |  | ||||||
|             <MudText>Log In (SSO)</MudText> |  | ||||||
|         } |  | ||||||
|         </MudButton> *@ |  | ||||||
|     </MudForm> |     </MudForm> | ||||||
| </MudPaper> | </MudPaper> | ||||||
|  |  | ||||||
| @code { |  | ||||||
|     [Parameter] |  | ||||||
|     public string? redirectUrl { get; set; } |  | ||||||
|     [Parameter] |  | ||||||
|     public string? redirectUrlSub { get; set; } |  | ||||||
|     private bool success; |  | ||||||
|     private bool processing = false; |  | ||||||
|     private bool processingLocal = false; |  | ||||||
|     private string[] errors = { }; |  | ||||||
|     private string? username; |  | ||||||
|     private string? password; |  | ||||||
|  |  | ||||||
|     private async Task SubmitLogin() { |  | ||||||
|         processing = true; |  | ||||||
|         if (string.IsNullOrWhiteSpace(username)) snackbar.Add("Username is required!", Severity.Error); |  | ||||||
|         else if (string.IsNullOrWhiteSpace(password)) snackbar.Add("Password is required!", Severity.Error); |  | ||||||
|         else { |  | ||||||
|             await authStateProvider.LoginAsync(username, password); |  | ||||||
|             if (!string.IsNullOrWhiteSpace(redirectUrl) && !string.IsNullOrWhiteSpace(redirectUrlSub)) { |  | ||||||
|                 navManager.NavigateTo($"{redirectUrl}/{redirectUrlSub}"); |  | ||||||
|             } else if (!string.IsNullOrWhiteSpace(redirectUrl)) { |  | ||||||
|                 navManager.NavigateTo(redirectUrl); |  | ||||||
|             } else { |  | ||||||
|                 navManager.NavigateTo("dashboard"); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         processing = false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private async Task SubmitIfEnter(KeyboardEventArgs e) { |  | ||||||
|         if (e.Key == "Enter" && success) { |  | ||||||
|             SubmitLogin(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private async Task LoginLocal() { |  | ||||||
|         processingLocal = true; |  | ||||||
|  |  | ||||||
|         await authStateProvider.LoginLocal(); |  | ||||||
|         if (!string.IsNullOrWhiteSpace(redirectUrl) && !string.IsNullOrWhiteSpace(redirectUrlSub)) { |  | ||||||
|             navManager.NavigateTo($"{redirectUrl}/{redirectUrlSub}"); |  | ||||||
|         } else if (!string.IsNullOrWhiteSpace(redirectUrl)) { |  | ||||||
|             navManager.NavigateTo(redirectUrl); |  | ||||||
|         } else { |  | ||||||
|             navManager.NavigateTo("dashboard"); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         processingLocal = false; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | |||||||
							
								
								
									
										53
									
								
								MesaFabApproval.Client/Pages/Login.razor.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								MesaFabApproval.Client/Pages/Login.razor.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | |||||||
|  | using Microsoft.AspNetCore.Components; | ||||||
|  | using Microsoft.AspNetCore.Components.Web; | ||||||
|  | using Microsoft.AspNetCore.WebUtilities; | ||||||
|  | using Microsoft.Extensions.Caching.Memory; | ||||||
|  |  | ||||||
|  | using MudBlazor; | ||||||
|  |  | ||||||
|  | namespace MesaFabApproval.Client.Pages; | ||||||
|  |  | ||||||
|  | public partial class Login { | ||||||
|  |     [Inject] NavigationManager navigationManager { get; set; } | ||||||
|  |     [Inject] IMemoryCache cache { get; set; } | ||||||
|  |  | ||||||
|  |     public string? _redirectPath { get; set; } | ||||||
|  |     private bool success; | ||||||
|  |     private bool processing = false; | ||||||
|  |     private string[] errors = { }; | ||||||
|  |     private string? username; | ||||||
|  |     private string? password; | ||||||
|  |  | ||||||
|  |     protected override async Task OnParametersSetAsync() { | ||||||
|  |         Uri uri = navigationManager.ToAbsoluteUri(navigationManager.Uri); | ||||||
|  |  | ||||||
|  |         if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("redirectPath", out var redirectPath)) { | ||||||
|  |             _redirectPath = System.Net.WebUtility.UrlDecode(redirectPath); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (string.IsNullOrWhiteSpace(_redirectPath)) { | ||||||
|  |             _redirectPath = cache.Get<string>("redirectUrl"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private async Task SubmitLogin() { | ||||||
|  |         processing = true; | ||||||
|  |         if (string.IsNullOrWhiteSpace(username)) snackbar.Add("Username is required!", Severity.Error); | ||||||
|  |         else if (string.IsNullOrWhiteSpace(password)) snackbar.Add("Password is required!", Severity.Error); | ||||||
|  |         else { | ||||||
|  |             await authStateProvider.LoginAsync(username, password); | ||||||
|  |             if (!string.IsNullOrWhiteSpace(_redirectPath)) { | ||||||
|  |                 navManager.NavigateTo(_redirectPath); | ||||||
|  |             } else { | ||||||
|  |                 navManager.NavigateTo("dashboard"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         processing = false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private async Task SubmitIfEnter(KeyboardEventArgs e) { | ||||||
|  |         if (e.Key == "Enter" && success) { | ||||||
|  |             SubmitLogin(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,9 +1,4 @@ | |||||||
| @page "/mrb/all" | @page "/mrb/all" | ||||||
| @using System.Globalization |  | ||||||
| @inject IMRBService mrbService |  | ||||||
| @inject ISnackbar snackbar |  | ||||||
| @inject IMemoryCache cache |  | ||||||
| @inject NavigationManager navigationManager |  | ||||||
|  |  | ||||||
| <PageTitle>MRB</PageTitle> | <PageTitle>MRB</PageTitle> | ||||||
|  |  | ||||||
| @ -78,42 +73,3 @@ | |||||||
| <MudOverlay @bind-Visible=inProcess DarkBackground="true" AutoClose="false"> | <MudOverlay @bind-Visible=inProcess DarkBackground="true" AutoClose="false"> | ||||||
|     <MudProgressCircular Color="Color.Info" Size="Size.Large" Indeterminate="true" /> |     <MudProgressCircular Color="Color.Info" Size="Size.Large" Indeterminate="true" /> | ||||||
| </MudOverlay> | </MudOverlay> | ||||||
|  |  | ||||||
| @code { |  | ||||||
|     private bool inProcess = false; |  | ||||||
|     private string searchString = ""; |  | ||||||
|     private IEnumerable<MRB> allMrbs = new List<MRB>(); |  | ||||||
|  |  | ||||||
|     protected override async Task OnParametersSetAsync() { |  | ||||||
|         inProcess = true; |  | ||||||
|         try { |  | ||||||
|             if (mrbService is null) { |  | ||||||
|                 throw new Exception("MRB service not injected!"); |  | ||||||
|             } else { |  | ||||||
|                 allMrbs = await mrbService.GetAllMRBs(false); |  | ||||||
|             } |  | ||||||
|         } catch (Exception ex) { |  | ||||||
|             snackbar.Add(ex.Message, Severity.Error); |  | ||||||
|         } |  | ||||||
|         inProcess = false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private bool FilterFuncForTable(MRB mrb) => FilterFunc(mrb, searchString); |  | ||||||
|  |  | ||||||
|     private bool FilterFunc(MRB mrb, string searchString) { |  | ||||||
|         if (string.IsNullOrWhiteSpace(searchString)) |  | ||||||
|             return true; |  | ||||||
|         if (mrb.Title.ToLower().Contains(searchString.Trim().ToLower())) |  | ||||||
|             return true; |  | ||||||
|         if (mrb.OriginatorName.ToLower().Contains(searchString.Trim().ToLower())) |  | ||||||
|             return true; |  | ||||||
|         if (mrb.MRBNumber.ToString().Contains(searchString.Trim())) |  | ||||||
|             return true; |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void GoTo(string page) { |  | ||||||
|         cache.Set("redirectUrl", page); |  | ||||||
|         navigationManager.NavigateTo(page); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | |||||||
							
								
								
									
										55
									
								
								MesaFabApproval.Client/Pages/MRBAll.razor.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								MesaFabApproval.Client/Pages/MRBAll.razor.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | |||||||
|  | using MesaFabApproval.Client.Services; | ||||||
|  | using MesaFabApproval.Shared.Models; | ||||||
|  |  | ||||||
|  | using Microsoft.AspNetCore.Components; | ||||||
|  | using Microsoft.Extensions.Caching.Memory; | ||||||
|  |  | ||||||
|  | using MudBlazor; | ||||||
|  |  | ||||||
|  | namespace MesaFabApproval.Client.Pages; | ||||||
|  |  | ||||||
|  | public partial class MRBAll { | ||||||
|  |     [Inject] IMRBService mrbService { get; set; } | ||||||
|  |     [Inject] ISnackbar snackbar { get; set; } | ||||||
|  |     [Inject] IMemoryCache cache { get; set; } | ||||||
|  |     [Inject] NavigationManager navigationManager { get; set; } | ||||||
|  |  | ||||||
|  |     private bool inProcess = false; | ||||||
|  |     private string searchString = ""; | ||||||
|  |     private IEnumerable<MRB> allMrbs = new List<MRB>(); | ||||||
|  |  | ||||||
|  |     protected override async Task OnParametersSetAsync() { | ||||||
|  |         inProcess = true; | ||||||
|  |         try { | ||||||
|  |             cache.Set("redirectUrl", "mrb/all"); | ||||||
|  |  | ||||||
|  |             if (mrbService is null) { | ||||||
|  |                 throw new Exception("MRB service not injected!"); | ||||||
|  |             } else { | ||||||
|  |                 allMrbs = await mrbService.GetAllMRBs(false); | ||||||
|  |             } | ||||||
|  |         } catch (Exception ex) { | ||||||
|  |             snackbar.Add(ex.Message, Severity.Error); | ||||||
|  |         } | ||||||
|  |         inProcess = false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private bool FilterFuncForTable(MRB mrb) => FilterFunc(mrb, searchString); | ||||||
|  |  | ||||||
|  |     private bool FilterFunc(MRB mrb, string searchString) { | ||||||
|  |         if (string.IsNullOrWhiteSpace(searchString)) | ||||||
|  |             return true; | ||||||
|  |         if (mrb.Title.ToLower().Contains(searchString.Trim().ToLower())) | ||||||
|  |             return true; | ||||||
|  |         if (mrb.OriginatorName.ToLower().Contains(searchString.Trim().ToLower())) | ||||||
|  |             return true; | ||||||
|  |         if (mrb.MRBNumber.ToString().Contains(searchString.Trim())) | ||||||
|  |             return true; | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void GoTo(string page) { | ||||||
|  |         cache.Set("redirectUrl", page); | ||||||
|  |         navigationManager.NavigateTo(page); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -65,6 +65,8 @@ public partial class MRBSingle { | |||||||
|     protected override async Task OnParametersSetAsync() { |     protected override async Task OnParametersSetAsync() { | ||||||
|         processing = true; |         processing = true; | ||||||
|         try { |         try { | ||||||
|  |             cache.Set("redirectUrl", $"mrb/{mrbNumber}"); | ||||||
|  |  | ||||||
|             allActiveUsers = await userService.GetAllActiveUsers(); |             allActiveUsers = await userService.GetAllActiveUsers(); | ||||||
|             currentUser = authStateProvider.CurrentUser; |             currentUser = authStateProvider.CurrentUser; | ||||||
|             currentUrl = navigationManager.Uri; |             currentUrl = navigationManager.Uri; | ||||||
| @ -285,7 +287,7 @@ public partial class MRBSingle { | |||||||
|                 string? comments = ""; |                 string? comments = ""; | ||||||
|  |  | ||||||
|                 DialogParameters<Comments> parameters = new DialogParameters<Comments> { { x => x.comments, comments } }; |                 DialogParameters<Comments> parameters = new DialogParameters<Comments> { { x => x.comments, comments } }; | ||||||
|                 var dialog = dialogService.Show<Comments>($"Approval Comments", parameters); |                 var dialog = await dialogService.ShowAsync<Comments>($"Approval Comments", parameters); | ||||||
|  |  | ||||||
|                 var result = await dialog.Result; |                 var result = await dialog.Result; | ||||||
|  |  | ||||||
| @ -412,7 +414,7 @@ public partial class MRBSingle { | |||||||
|                 if (currentUser is null) { |                 if (currentUser is null) { | ||||||
|                     recallInProcess = false; |                     recallInProcess = false; | ||||||
|                     snackbar.Add("You must be logged in to recall this MRB", Severity.Error); |                     snackbar.Add("You must be logged in to recall this MRB", Severity.Error); | ||||||
|                     navigationManager.NavigateTo($"login/mrb/{mrb.MRBNumber}"); |                     navigationManager.NavigateTo($"login?redirectPath=mrb/{mrb.MRBNumber}"); | ||||||
|                 } else { |                 } else { | ||||||
|                     await mrbService.RecallMRB(mrb, currentUser); |                     await mrbService.RecallMRB(mrb, currentUser); | ||||||
|                     mrbApprovals = await approvalService.GetApprovalsForIssueId(mrb.MRBNumber, true); |                     mrbApprovals = await approvalService.GetApprovalsForIssueId(mrb.MRBNumber, true); | ||||||
|  | |||||||
| @ -1,9 +1,4 @@ | |||||||
| @page "/pcrb/all" | @page "/pcrb/all" | ||||||
| @using System.Globalization |  | ||||||
| @inject IPCRBService pcrbService |  | ||||||
| @inject ISnackbar snackbar |  | ||||||
| @inject IMemoryCache cache |  | ||||||
| @inject NavigationManager navigationManager |  | ||||||
|  |  | ||||||
| <PageTitle>PCRB</PageTitle> | <PageTitle>PCRB</PageTitle> | ||||||
|  |  | ||||||
| @ -25,6 +20,7 @@ | |||||||
|                           Adornment="Adornment.Start" |                           Adornment="Adornment.Start" | ||||||
|                           AdornmentIcon="@Icons.Material.Filled.Search" |                           AdornmentIcon="@Icons.Material.Filled.Search" | ||||||
|                           IconSize="Size.Medium" |                           IconSize="Size.Medium" | ||||||
|  |                           Immediate | ||||||
|                           Class="mt-0" /> |                           Class="mt-0" /> | ||||||
|         </ToolBarContent> |         </ToolBarContent> | ||||||
|         <HeaderContent> |         <HeaderContent> | ||||||
| @ -43,19 +39,37 @@ | |||||||
|                     Owner |                     Owner | ||||||
|                 </MudTableSortLabel> |                 </MudTableSortLabel> | ||||||
|             </MudTh> |             </MudTh> | ||||||
|             <MudTh>Stage</MudTh> |             <MudTh> | ||||||
|  |                 <MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.Type)"> | ||||||
|  |                     Type | ||||||
|  |                 </MudTableSortLabel> | ||||||
|  |             </MudTh> | ||||||
|  |             <MudTh> | ||||||
|  |                 <MudTableSortLabel SortBy="new Func<PCRB, object>(x=>GetStageName(x.CurrentStep))"> | ||||||
|  |                     Stage | ||||||
|  |                 </MudTableSortLabel> | ||||||
|  |             </MudTh> | ||||||
|             <MudTh> |             <MudTh> | ||||||
|                 <MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.InsertTimeStamp)"> |                 <MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.InsertTimeStamp)"> | ||||||
|                     Submitted Date |                     Submitted Date | ||||||
|                 </MudTableSortLabel> |                 </MudTableSortLabel> | ||||||
|             </MudTh> |             </MudTh> | ||||||
|             <MudTh> |             <MudTh> | ||||||
|                 <MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.LastUpdateDate)"> |                 <MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.ClosedDate)"> | ||||||
|                     Last Updated |                     Completed Date | ||||||
|                 </MudTableSortLabel> |                 </MudTableSortLabel> | ||||||
|             </MudTh> |             </MudTh> | ||||||
|             <MudTh> |             <MudTh> | ||||||
|                 <MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.ClosedDate)"> |                 <MudTableSortLabel SortBy="new Func<PCRB, object>(x=>(x.FollowUps.FirstOrDefault() is null ?   | ||||||
|  |                                                                         DateTimeUtilities.MIN_DT:  | ||||||
|  |                                                                         x.FollowUps.First().FollowUpDate))"> | ||||||
|  |                     Follow Up Date | ||||||
|  |                 </MudTableSortLabel> | ||||||
|  |             </MudTh> | ||||||
|  |             <MudTh> | ||||||
|  |                 <MudTableSortLabel SortBy="new Func<PCRB, object>(x=>(x.FollowUps.FirstOrDefault() is null ? | ||||||
|  |                                                                         DateTimeUtilities.MIN_DT: | ||||||
|  |                                                                         x.FollowUps.First().CompletedDate))"> | ||||||
|                     Closed Date |                     Closed Date | ||||||
|                 </MudTableSortLabel> |                 </MudTableSortLabel> | ||||||
|             </MudTh> |             </MudTh> | ||||||
| @ -66,10 +80,20 @@ | |||||||
|             </MudTd> |             </MudTd> | ||||||
|             <MudTd DataLabel="Title">@context.Title</MudTd> |             <MudTd DataLabel="Title">@context.Title</MudTd> | ||||||
|             <MudTd DataLabel="Owner">@context.OwnerName</MudTd> |             <MudTd DataLabel="Owner">@context.OwnerName</MudTd> | ||||||
|  |             <MudTd DataLabel="Type">@context.Type</MudTd> | ||||||
|             <MudTd DataLabel="Stage">@(GetStageName(context.CurrentStep))</MudTd> |             <MudTd DataLabel="Stage">@(GetStageName(context.CurrentStep))</MudTd> | ||||||
|             <MudTd DataLabel="Submitted Date">@DateTimeUtilities.GetDateAsStringMinDefault(context.InsertTimeStamp)</MudTd> |             <MudTd DataLabel="Submitted Date">@DateTimeUtilities.GetDateAsStringMinDefault(context.InsertTimeStamp)</MudTd> | ||||||
|             <MudTd DataLabel="Last Updated">@DateTimeUtilities.GetDateAsStringMinDefault(context.LastUpdateDate)</MudTd> |             <MudTd DataLabel="Completed Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.ClosedDate)</MudTd> | ||||||
|             <MudTd DataLabel="Closed Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.ClosedDate)</MudTd> |             <MudTd DataLabel="Follow Up Date">@(DateTimeUtilities.GetDateAsStringMaxDefault(context.FollowUps | ||||||
|  |                                                                                                 .FirstOrDefault()?.FollowUpDate | ||||||
|  |                                                                                             ) | ||||||
|  |                                                 ) | ||||||
|  |             </MudTd> | ||||||
|  |             <MudTd DataLabel="Closed Date">@(DateTimeUtilities.GetDateAsStringMaxDefault(context.FollowUps | ||||||
|  |                                                                                             .FirstOrDefault()?.CompletedDate | ||||||
|  |                                                                                         ) | ||||||
|  |                                             ) | ||||||
|  |             </MudTd> | ||||||
|         </RowTemplate> |         </RowTemplate> | ||||||
|         <PagerContent> |         <PagerContent> | ||||||
|             <MudTablePager /> |             <MudTablePager /> | ||||||
| @ -80,47 +104,3 @@ | |||||||
| <MudOverlay @bind-Visible=inProcess DarkBackground="true" AutoClose="false"> | <MudOverlay @bind-Visible=inProcess DarkBackground="true" AutoClose="false"> | ||||||
|     <MudProgressCircular Color="Color.Info" Size="Size.Large" Indeterminate="true" /> |     <MudProgressCircular Color="Color.Info" Size="Size.Large" Indeterminate="true" /> | ||||||
| </MudOverlay> | </MudOverlay> | ||||||
|  |  | ||||||
| @code { |  | ||||||
|     private bool inProcess = false; |  | ||||||
|     private string searchString = ""; |  | ||||||
|     private IEnumerable<PCRB> allPCRBs = new List<PCRB>(); |  | ||||||
|  |  | ||||||
|     protected override async Task OnParametersSetAsync() { |  | ||||||
|         inProcess = true; |  | ||||||
|         try { |  | ||||||
|             if (pcrbService is null) { |  | ||||||
|                 throw new Exception("PCRB service not injected!"); |  | ||||||
|             } else { |  | ||||||
|                 allPCRBs = await pcrbService.GetAllPCRBs(false); |  | ||||||
|             } |  | ||||||
|         } catch (Exception ex) { |  | ||||||
|             snackbar.Add(ex.Message, Severity.Error); |  | ||||||
|         } |  | ||||||
|         inProcess = false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private bool FilterFuncForTable(PCRB pcrb) => FilterFunc(pcrb, searchString); |  | ||||||
|  |  | ||||||
|     private bool FilterFunc(PCRB pcrb, string searchString) { |  | ||||||
|         if (string.IsNullOrWhiteSpace(searchString)) |  | ||||||
|             return true; |  | ||||||
|         if (pcrb.Title.ToLower().Contains(searchString.Trim().ToLower())) |  | ||||||
|             return true; |  | ||||||
|         if (pcrb.OwnerName.ToLower().Contains(searchString.Trim().ToLower())) |  | ||||||
|             return true; |  | ||||||
|         if (pcrb.PlanNumber.ToString().Contains(searchString.Trim())) |  | ||||||
|             return true; |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void GoTo(string page) { |  | ||||||
|         cache.Set("redirectUrl", page); |  | ||||||
|         navigationManager.NavigateTo(page); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private string GetStageName(int step) { |  | ||||||
|         if (step >= PCRB.Stages.Length || step < 0) return ""; |  | ||||||
|         return PCRB.Stages[step]; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | |||||||
							
								
								
									
										64
									
								
								MesaFabApproval.Client/Pages/PCRBAll.razor.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								MesaFabApproval.Client/Pages/PCRBAll.razor.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | |||||||
|  | using MesaFabApproval.Client.Services; | ||||||
|  | using MesaFabApproval.Shared.Models; | ||||||
|  |  | ||||||
|  | using Microsoft.AspNetCore.Components; | ||||||
|  | using Microsoft.Extensions.Caching.Memory; | ||||||
|  |  | ||||||
|  | using MudBlazor; | ||||||
|  |  | ||||||
|  | namespace MesaFabApproval.Client.Pages; | ||||||
|  |  | ||||||
|  | public partial class PCRBAll { | ||||||
|  |     [Inject] IPCRBService pcrbService { get; set; } | ||||||
|  |     [Inject] ISnackbar snackbar { get; set; } | ||||||
|  |     [Inject] IMemoryCache cache { get; set; } | ||||||
|  |     [Inject] NavigationManager navigationManager { get; set; } | ||||||
|  |  | ||||||
|  |     private bool inProcess = false; | ||||||
|  |     private string searchString = ""; | ||||||
|  |     private IEnumerable<PCRB> allPCRBs = new List<PCRB>(); | ||||||
|  |  | ||||||
|  |     protected override async Task OnParametersSetAsync() { | ||||||
|  |         inProcess = true; | ||||||
|  |         try { | ||||||
|  |             cache.Set("redirectUrl", "pcrb/all"); | ||||||
|  |  | ||||||
|  |             if (pcrbService is null) { | ||||||
|  |                 throw new Exception("PCRB service not injected!"); | ||||||
|  |             } else { | ||||||
|  |                 allPCRBs = await pcrbService.GetAllPCRBs(false); | ||||||
|  |             } | ||||||
|  |         } catch (Exception ex) { | ||||||
|  |             snackbar.Add(ex.Message, Severity.Error); | ||||||
|  |         } | ||||||
|  |         inProcess = false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private bool FilterFuncForTable(PCRB pcrb) => FilterFunc(pcrb, searchString); | ||||||
|  |  | ||||||
|  |     private bool FilterFunc(PCRB pcrb, string searchString) { | ||||||
|  |         if (string.IsNullOrWhiteSpace(searchString)) | ||||||
|  |             return true; | ||||||
|  |         if (pcrb.Title.ToLower().Contains(searchString.Trim().ToLower())) | ||||||
|  |             return true; | ||||||
|  |         if (pcrb.OwnerName.ToLower().Contains(searchString.Trim().ToLower())) | ||||||
|  |             return true; | ||||||
|  |         if (pcrb.Type.ToLower().Contains(searchString.Trim().ToLower())) | ||||||
|  |             return true; | ||||||
|  |         if (GetStageName(pcrb.CurrentStep).ToLower().Contains(searchString.Trim().ToLower())) | ||||||
|  |             return true; | ||||||
|  |         if (pcrb.PlanNumber.ToString().Contains(searchString.Trim())) | ||||||
|  |             return true; | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void GoTo(string page) { | ||||||
|  |         cache.Set("redirectUrl", page); | ||||||
|  |         navigationManager.NavigateTo(page); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private string GetStageName(int step) { | ||||||
|  |         if (step >= PCRB.Stages.Length || step < 0) return ""; | ||||||
|  |         return PCRB.Stages[step]; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -6,20 +6,22 @@ | |||||||
|  |  | ||||||
| <MudPaper Class="p-2 m-2 d-flex flex-row justify-content-between"> | <MudPaper Class="p-2 m-2 d-flex flex-row justify-content-between"> | ||||||
|     <MudIconButton Icon="@Icons.Material.Filled.ChevronLeft" |     <MudIconButton Icon="@Icons.Material.Filled.ChevronLeft" | ||||||
|                    Variant="Variant.Outlined" |     Variant="Variant.Outlined" | ||||||
|                    Color="Color.Dark" |     Color="Color.Dark" | ||||||
|                    OnClick="@ReturnToAllPcrbs" |     OnClick="@ReturnToAllPcrbs" | ||||||
|                    Size="Size.Large" /> |     Size="Size.Large" /> | ||||||
|     <MudText Typo="Typo.h3" Align="Align.Center">PCRB @planNumber</MudText> |     <MudText Typo="Typo.h3" Align="Align.Center">PCRB @planNumber</MudText> | ||||||
|     <MudPaper Height="100%" Width="0.1%" Square="true" /> |     <MudPaper Height="100%" Width="0.1%" Square="true" /> | ||||||
| </MudPaper> | </MudPaper> | ||||||
|  |  | ||||||
| @if (pcrb is not null) { | @if (pcrb is not null) { | ||||||
|     <MudTimeline Class="mt-2 pt-2" TimelineOrientation="TimelineOrientation.Horizontal" |     <MudTimeline Class="mt-2 pt-2" TimelineOrientation="TimelineOrientation.Horizontal" | ||||||
|                  TimelinePosition="TimelinePosition.Bottom"> |     TimelinePosition="TimelinePosition.Bottom"> | ||||||
|         @for (int i = 0; i < PCRB.Stages.Length; i++) { |         @for (int i = 0; i < PCRB.Stages.Length; i++) { | ||||||
|             Color color; |             Color color; | ||||||
|             if (pcrb.CurrentStep > i || pcrb.CurrentStep == (PCRB.Stages.Length - 1)) { |             if (pcrb.CurrentStep > i ||  | ||||||
|  |                 (i == (int)PCRB.StagesEnum.Complete && pcrb.CurrentStep == (int)PCRB.StagesEnum.Complete) || | ||||||
|  |                 (i == (int)PCRB.StagesEnum.Closed && pcrb.CurrentStep == (int)PCRB.StagesEnum.Closed)) { | ||||||
|                 color = Color.Success; |                 color = Color.Success; | ||||||
|             } else if (pcrb.CurrentStep == i) { |             } else if (pcrb.CurrentStep == i) { | ||||||
|                 color = Color.Info; |                 color = Color.Info; | ||||||
| @ -35,14 +37,14 @@ | |||||||
|         } |         } | ||||||
|     </MudTimeline> |     </MudTimeline> | ||||||
|  |  | ||||||
|     bool pcrbIsSubmitted = pcrb.CurrentStep > 0 && pcrb.InsertTimeStamp > DateTimeUtilities.MIN_DT; |     bool pcrbIsSubmitted = pcrb.CurrentStep > (int)PCRB.StagesEnum.Draft && pcrb.InsertTimeStamp > DateTimeUtilities.MIN_DT; | ||||||
|     bool pcrbIsComplete = pcrb.ClosedDate < DateTimeUtilities.MAX_DT && pcrb.CurrentStep == (PCRB.Stages.Length - 1); |     bool pcrbIsComplete = pcrb.ClosedDate < DateTimeUtilities.MAX_DT && pcrb.CurrentStep >= (int)PCRB.StagesEnum.Complete; | ||||||
|     bool userIsOriginator = pcrb.OwnerID == authStateProvider.CurrentUser?.UserID; |     bool userIsOriginator = pcrb.OwnerID == authStateProvider.CurrentUser?.UserID; | ||||||
|     bool userIsAdmin = authStateProvider.CurrentUser is null ? false : authStateProvider.CurrentUser.IsAdmin; |     bool userIsAdmin = authStateProvider.CurrentUser is null ? false : authStateProvider.CurrentUser.IsAdmin; | ||||||
|     bool userIsApprover = UserIsApprover(); |     bool userIsApprover = UserIsApprover(); | ||||||
|  |  | ||||||
|     @if ((!pcrbIsSubmitted && !string.IsNullOrWhiteSpace(pcrb.Title) && (userIsOriginator || userIsAdmin)) || |     @if ((!pcrbIsSubmitted && !string.IsNullOrWhiteSpace(pcrb.Title) && (userIsOriginator || userIsAdmin)) || | ||||||
|          (!pcrbIsSubmitted && pcrb.PlanNumber > 0 && (userIsOriginator || userIsAdmin))) { |         (!pcrbIsSubmitted && pcrb.PlanNumber > 0 && (userIsOriginator || userIsAdmin))) { | ||||||
|         <MudPaper Outlined="true" |         <MudPaper Outlined="true" | ||||||
|         Class="p-2 m-2 d-flex flex-wrap gap-3 justify-content-center align-content-center" |         Class="p-2 m-2 d-flex flex-wrap gap-3 justify-content-center align-content-center" | ||||||
|         Elevation="10"> |         Elevation="10"> | ||||||
| @ -61,9 +63,9 @@ | |||||||
|             } |             } | ||||||
|             @if (!pcrbIsSubmitted && pcrb.PlanNumber > 0 && (userIsOriginator || userIsAdmin)) { |             @if (!pcrbIsSubmitted && pcrb.PlanNumber > 0 && (userIsOriginator || userIsAdmin)) { | ||||||
|                 <MudButton Variant="Variant.Filled" |                 <MudButton Variant="Variant.Filled" | ||||||
|                            Color="Color.Secondary" |                 Color="Color.Secondary" | ||||||
|                            Disabled="@deleteInProcess" |                 Disabled="@deleteInProcess" | ||||||
|                            OnClick=DeletePCRB> |                 OnClick=DeletePCRB> | ||||||
|                     @if (deleteInProcess) { |                     @if (deleteInProcess) { | ||||||
|                         <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> |                         <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> | ||||||
|                         <MudText>Processing</MudText> |                         <MudText>Processing</MudText> | ||||||
| @ -91,114 +93,114 @@ | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     <MudPaper Outlined="true" |     <MudPaper Outlined="true" | ||||||
|               Class="p-2 m-2 d-flex flex-wrap gap-3 justify-content-center align-content-start" |     Class="p-2 m-2 d-flex flex-wrap gap-3 justify-content-center align-content-start" | ||||||
|               Elevation="10"> |     Elevation="10"> | ||||||
|         <MudTextField @bind-Value=pcrb.PlanNumber |         <MudTextField @bind-Value=pcrb.PlanNumber | ||||||
|                       Text="@pcrb.PlanNumber.ToString()" |         Text="@pcrb.PlanNumber.ToString()" | ||||||
|                       T="int" |         T="int" | ||||||
|                       Disabled="true" |         Disabled="true" | ||||||
|                       Label="Change#" |         Label="Change#" | ||||||
|                       Required |         Required | ||||||
|                       Variant="Variant.Outlined" /> |         Variant="Variant.Outlined" /> | ||||||
|         <MudTextField @bind-Value=pcrb.Title |         <MudTextField @bind-Value=pcrb.Title | ||||||
|                       Text="@pcrb.Title" |         Text="@pcrb.Title" | ||||||
|                       Disabled="@(pcrbIsSubmitted)" |         Disabled="@(pcrbIsSubmitted)" | ||||||
|                       T="string" |         T="string" | ||||||
|                       AutoGrow |         AutoGrow | ||||||
|                       AutoFocus |         AutoFocus | ||||||
|                       Immediate |         Immediate | ||||||
|                       Clearable |         Clearable | ||||||
|                       Required |         Required | ||||||
|                       Variant="Variant.Outlined" |         Variant="Variant.Outlined" | ||||||
|                       Label="Project Name" /> |         Label="Project Name" /> | ||||||
|         <MudSelect T="User" |         <MudSelect T="User" | ||||||
|                    Label="Originator" |         Label="Originator" | ||||||
|                    Variant="Variant.Outlined" |         Variant="Variant.Outlined" | ||||||
|                    Required |         Required | ||||||
|                    Clearable |         Clearable | ||||||
|                    AnchorOrigin="Origin.BottomCenter" |         AnchorOrigin="Origin.BottomCenter" | ||||||
|                    ToStringFunc="@UserToNameConverter" |         ToStringFunc="@UserToNameConverter" | ||||||
|                    Disabled=@(pcrbIsSubmitted) |         Disabled=@(pcrbIsSubmitted) | ||||||
|                    @bind-Value=@selectedOwner> |         @bind-Value=@selectedOwner> | ||||||
|             @foreach (User user in allActiveUsers.OrderBy(u => u.LastName)) { |             @foreach (User user in allActiveUsers.OrderBy(u => u.LastName)) { | ||||||
|                 <MudSelectItem T="User" Value="@(user)" /> |                 <MudSelectItem T="User" Value="@(user)" /> | ||||||
|             } |             } | ||||||
|         </MudSelect> |         </MudSelect> | ||||||
|         <MudSelect T="string" |         <MudSelect T="string" | ||||||
|                    Variant="Variant.Outlined" |         Variant="Variant.Outlined" | ||||||
|                    Required |         Required | ||||||
|                    Clearable |         Clearable | ||||||
|                    AnchorOrigin="Origin.BottomCenter" |         AnchorOrigin="Origin.BottomCenter" | ||||||
|                    Disabled="@pcrbIsSubmitted" |         Disabled="@pcrbIsSubmitted" | ||||||
|                    @bind-Value="@pcrb.ChangeLevel" |         @bind-Value="@pcrb.ChangeLevel" | ||||||
|                    Text="@pcrb.ChangeLevel" |         Text="@pcrb.ChangeLevel" | ||||||
|                    Label="Change Level"> |         Label="Change Level"> | ||||||
|             <MudSelectItem Value="@("Global - Class 1")" /> |             <MudSelectItem Value="@("Global - Class 1")" /> | ||||||
|             <MudSelectItem Value="@("Other Site + Mesa - Class 2")" /> |             <MudSelectItem Value="@("Other Site + Mesa - Class 2")" /> | ||||||
|             <MudSelectItem Value="@("Mesa - Class 3")" /> |             <MudSelectItem Value="@("Mesa - Class 3")" /> | ||||||
|         </MudSelect> |         </MudSelect> | ||||||
|         <MudSelect T="string" |         <MudSelect T="string" | ||||||
|                    Variant="Variant.Outlined" |         Variant="Variant.Outlined" | ||||||
|                    Required |         Required | ||||||
|                    Clearable |         Clearable | ||||||
|                    AnchorOrigin="Origin.BottomCenter" |         AnchorOrigin="Origin.BottomCenter" | ||||||
|                    Disabled="@pcrbIsSubmitted" |         Disabled="@pcrbIsSubmitted" | ||||||
|                    @bind-Value="@pcrb.Type" |         @bind-Value="@pcrb.Type" | ||||||
|                    Text="@pcrb.Type" |         Text="@pcrb.Type" | ||||||
|                    Label="Change Type"> |         Label="Change Type"> | ||||||
|             <MudSelectItem Value="@("Cost Savings")" /> |             <MudSelectItem Value="@("Cost Savings")" /> | ||||||
|             <MudSelectItem Value="@("Process Efficiency")" /> |             <MudSelectItem Value="@("Process Efficiency")" /> | ||||||
|             <MudSelectItem Value="@("Process Improvement")" /> |             <MudSelectItem Value="@("Process Improvement")" /> | ||||||
|             <MudSelectItem Value="@("Business Continuity")" /> |             <MudSelectItem Value="@("Business Continuity")" /> | ||||||
|         </MudSelect> |         </MudSelect> | ||||||
|         <MudCheckBox Disabled="@pcrbIsSubmitted" |         <MudCheckBox Disabled="@pcrbIsSubmitted" | ||||||
|                      Color="Color.Tertiary" |         Color="Color.Tertiary" | ||||||
|                      @bind-Value=pcrb.IsITAR |         @bind-Value=pcrb.IsITAR | ||||||
|                      Label="Export Controlled" |         Label="Export Controlled" | ||||||
|                      LabelPosition="LabelPosition.Start" /> |         LabelPosition="LabelPosition.Start" /> | ||||||
|         <MudTextField Disabled="true" |         <MudTextField Disabled="true" | ||||||
|                       T="string" |         T="string" | ||||||
|                       Value="@DateTimeUtilities.GetDateAsStringMinDefault(pcrb.InsertTimeStamp)" |         Value="@DateTimeUtilities.GetDateAsStringMinDefault(pcrb.InsertTimeStamp)" | ||||||
|                       Label="Submit Date" |         Label="Submit Date" | ||||||
|                       Variant="Variant.Outlined" /> |         Variant="Variant.Outlined" /> | ||||||
|         <MudTextField Disabled="true" |         <MudTextField Disabled="true" | ||||||
|                       T="string" |         T="string" | ||||||
|                       Value="@DateTimeUtilities.GetDateAsStringMinDefault(pcrb.LastUpdateDate)" |         Value="@DateTimeUtilities.GetDateAsStringMinDefault(pcrb.LastUpdateDate)" | ||||||
|                       Label="Last Update" |         Label="Last Update" | ||||||
|                       Variant="Variant.Outlined" /> |         Variant="Variant.Outlined" /> | ||||||
|         <MudTextField Disabled="true" |         <MudTextField Disabled="true" | ||||||
|                       T="string" |         T="string" | ||||||
|                       Value="@DateTimeUtilities.GetDateAsStringMaxDefault(pcrb.ClosedDate)" |         Value="@DateTimeUtilities.GetDateAsStringMaxDefault(pcrb.ClosedDate)" | ||||||
|                       Label="Complete Date" |         Label="Complete Date" | ||||||
|                       Variant="Variant.Outlined" /> |         Variant="Variant.Outlined" /> | ||||||
|     </MudPaper> |     </MudPaper> | ||||||
|  |  | ||||||
|     <MudPaper Outlined="true" |     <MudPaper Outlined="true" | ||||||
|               Class="p-2 m-2 d-flex flex-wrap gap-3 justify-content-center align-content-start" |     Class="p-2 m-2 d-flex flex-wrap gap-3 justify-content-center align-content-start" | ||||||
|               Elevation="10"> |     Elevation="10"> | ||||||
|         <MudTextField @bind-Value=pcrb.ChangeDescription |         <MudTextField @bind-Value=pcrb.ChangeDescription | ||||||
|                       Text="@pcrb.ChangeDescription" |         Text="@pcrb.ChangeDescription" | ||||||
|                       Disabled="@(pcrbIsSubmitted)" |         Disabled="@(pcrbIsSubmitted)" | ||||||
|                       T="string" |         T="string" | ||||||
|                       AutoGrow |         AutoGrow | ||||||
|                       Immediate |         Immediate | ||||||
|                       Clearable |         Clearable | ||||||
|                       Required |         Required | ||||||
|                       Variant="Variant.Outlined" |         Variant="Variant.Outlined" | ||||||
|                       Label="Description Of Change" /> |         Label="Description Of Change" /> | ||||||
|         <MudTextField @bind-Value=pcrb.ReasonForChange |         <MudTextField @bind-Value=pcrb.ReasonForChange | ||||||
|                       Text="@pcrb.ReasonForChange" |         Text="@pcrb.ReasonForChange" | ||||||
|                       Disabled="@(pcrbIsSubmitted)" |         Disabled="@(pcrbIsSubmitted)" | ||||||
|                       T="string" |         T="string" | ||||||
|                       AutoGrow |         AutoGrow | ||||||
|                       Immediate |         Immediate | ||||||
|                       Clearable |         Clearable | ||||||
|                       Required |         Required | ||||||
|                       Variant="Variant.Outlined" |         Variant="Variant.Outlined" | ||||||
|                       Label="Reason For Change" /> |         Label="Reason For Change" /> | ||||||
|     </MudPaper> |     </MudPaper> | ||||||
|  |  | ||||||
|     @if (pcrb.PlanNumber > 0 && pcrb.CurrentStep > 0) { |     @if (pcrb.PlanNumber > 0 && pcrb.CurrentStep > (int)PCRB.StagesEnum.Draft) { | ||||||
|         <MudExpansionPanels MultiExpansion="true"> |         <MudExpansionPanels MultiExpansion="true"> | ||||||
|             @for (int i = 1; i < 4; i++) { |             @for (int i = 1; i < 4; i++) { | ||||||
|                 int current_i = i; |                 int current_i = i; | ||||||
| @ -229,7 +231,7 @@ | |||||||
|                 int currentStagePendingApprovalsCount = currentStageApprovals.Where(a => a.ItemStatus == 0 && a.AssignedDate > DateTimeUtilities.MIN_DT).Count(); |                 int currentStagePendingApprovalsCount = currentStageApprovals.Where(a => a.ItemStatus == 0 && a.AssignedDate > DateTimeUtilities.MIN_DT).Count(); | ||||||
|                 int currentStageApprovedApprovalsCount = currentStageApprovals.Where(a => a.ItemStatus == 1).Count(); |                 int currentStageApprovedApprovalsCount = currentStageApprovals.Where(a => a.ItemStatus == 1).Count(); | ||||||
|                 int currentStageDeniedApprovalsCount = currentStageApprovals.Where(a => a.ItemStatus == -1).Count(); |                 int currentStageDeniedApprovalsCount = currentStageApprovals.Where(a => a.ItemStatus == -1).Count(); | ||||||
|                  |  | ||||||
|                 bool currentStageApproved = currentStageApprovedApprovalsCount >= 4 && currentStageUnsubmittedApprovalCount == 0 &&  |                 bool currentStageApproved = currentStageApprovedApprovalsCount >= 4 && currentStageUnsubmittedApprovalCount == 0 &&  | ||||||
|                     currentStagePendingApprovalsCount == 0; |                     currentStagePendingApprovalsCount == 0; | ||||||
|                 bool currentStageSubmitted = (currentStageApprovals.Count() > 0 && currentStagePendingApprovalsCount > 0 && |                 bool currentStageSubmitted = (currentStageApprovals.Count() > 0 && currentStagePendingApprovalsCount > 0 && | ||||||
| @ -260,7 +262,7 @@ | |||||||
|                     <TitleContent> |                     <TitleContent> | ||||||
|                         <MudText Typo="Typo.h4" Align="Align.Center">@($"PCR {current_i}")</MudText> |                         <MudText Typo="Typo.h4" Align="Align.Center">@($"PCR {current_i}")</MudText> | ||||||
|                         @if (previousStageSubmitted && (attachmentsMissing || actionItemsIncomplete ||  |                         @if (previousStageSubmitted && (attachmentsMissing || actionItemsIncomplete ||  | ||||||
|                              affectedDocumentsIncomplete || approvalsIncomplete)) { |                             affectedDocumentsIncomplete || approvalsIncomplete)) { | ||||||
|                             StringBuilder sb = new(); |                             StringBuilder sb = new(); | ||||||
|                             int missingSectionCount = 0; |                             int missingSectionCount = 0; | ||||||
|                             sb.Append("Incomplete sections: "); |                             sb.Append("Incomplete sections: "); | ||||||
| @ -299,31 +301,31 @@ | |||||||
|                     </TitleContent> |                     </TitleContent> | ||||||
|                     <ChildContent> |                     <ChildContent> | ||||||
|                         <MudPaper Outlined="true" |                         <MudPaper Outlined="true" | ||||||
|                                   Class="p-2 m-2 d-flex flex-column justify-start"> |                         Class="p-2 m-2 d-flex flex-column justify-start"> | ||||||
|                          |  | ||||||
|                             <MudText Typo="Typo.h5" Align="Align.Center">Supporting Documents</MudText> |                             <MudText Typo="Typo.h5" Align="Align.Center">Supporting Documents</MudText> | ||||||
|                              |  | ||||||
|                             <MudTable Items="@attachments.Where(a => a.Step == current_i)" |                             <MudTable Items="@attachments.Where(a => a.Step == current_i)" | ||||||
|                                         Class="m-2" |                             Class="m-2" | ||||||
|                                         Striped="true" |                             Striped="true" | ||||||
|                                         SortLabel="Sort By" |                             SortLabel="Sort By" | ||||||
|                                         Hover="true"> |                             Hover="true"> | ||||||
|                                 <ToolBarContent> |                                 <ToolBarContent> | ||||||
|                                     <MudStack Row="true" Justify="Justify.Center" Spacing="1" Style="width: 100%"> |                                     <MudStack Row="true" Justify="Justify.Center" Spacing="1" Style="width: 100%"> | ||||||
|                                         @if (pcrb.ClosedDate >= DateTimeUtilities.MAX_DT && previousStageSubmitted && !currentStageSubmitted) { |                                         @if (pcrb.ClosedDate >= DateTimeUtilities.MAX_DT && previousStageSubmitted && !currentStageSubmitted) { | ||||||
|                                             @if (current_i == 1) { |                                             @if (current_i == 1) { | ||||||
|                                                 <MudButton Variant="Variant.Filled" |                                                 <MudButton Variant="Variant.Filled" | ||||||
|                                                            Color="Color.Tertiary" |                                                 Color="Color.Tertiary" | ||||||
|                                                            Href="https://plm.intra.infineon.com/Windchill/netmarkets/jsp/ext/infineon/dcoidreleased.jsp?obid=OR:wt.doc.WTDocument:1477717325" |                                                 Href="https://plm.intra.infineon.com/Windchill/netmarkets/jsp/ext/infineon/dcoidreleased.jsp?obid=OR:wt.doc.WTDocument:1477717325" | ||||||
|                                                            Target="_blank"> |                                                 Target="_blank"> | ||||||
|                                                     Download PCRB Template |                                                     Download PCRB Template | ||||||
|                                                 </MudButton> |                                                 </MudButton> | ||||||
|                                             } |                                             } | ||||||
|                                             <MudButton Variant="Variant.Filled" |                                             <MudButton Variant="Variant.Filled" | ||||||
|                                                        Color="Color.Tertiary" |                                             Color="Color.Tertiary" | ||||||
|                                                        OnClick="@((e) => UploadAttachment(current_i))" |                                             OnClick="@((e) => UploadAttachment(current_i))" | ||||||
|                                                        Disabled="@attachmentUploadInProcess" |                                             Disabled="@attachmentUploadInProcess" | ||||||
|                                                        StartIcon="@Icons.Material.Filled.AttachFile"> |                                             StartIcon="@Icons.Material.Filled.AttachFile"> | ||||||
|                                                 @if (attachmentUploadInProcess) { |                                                 @if (attachmentUploadInProcess) { | ||||||
|                                                     <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> |                                                     <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> | ||||||
|                                                     <MudText>Processing</MudText> |                                                     <MudText>Processing</MudText> | ||||||
| @ -354,8 +356,8 @@ | |||||||
|                                 <RowTemplate> |                                 <RowTemplate> | ||||||
|                                     <MudTd DataLabel="File Name"> |                                     <MudTd DataLabel="File Name"> | ||||||
|                                         <a href="@(@$"{config["FabApprovalApiBaseUrl"]}/pcrb/attachmentFile?path={context.Path}&fileName={context.FileName}")" |                                         <a href="@(@$"{config["FabApprovalApiBaseUrl"]}/pcrb/attachmentFile?path={context.Path}&fileName={context.FileName}")" | ||||||
|                                             download="@(context.FileName)" |                                         download="@(context.FileName)" | ||||||
|                                             target="_top"> |                                         target="_top"> | ||||||
|                                             @context.FileName |                                             @context.FileName | ||||||
|                                         </a> |                                         </a> | ||||||
|                                     </MudTd> |                                     </MudTd> | ||||||
| @ -364,9 +366,9 @@ | |||||||
|                                     @if (pcrb.ClosedDate >= DateTimeUtilities.MAX_DT && previousStageSubmitted && !currentStageSubmitted) { |                                     @if (pcrb.ClosedDate >= DateTimeUtilities.MAX_DT && previousStageSubmitted && !currentStageSubmitted) { | ||||||
|                                         <MudTd Style="text-align:center;"> |                                         <MudTd Style="text-align:center;"> | ||||||
|                                             <MudButton Color="Color.Secondary" |                                             <MudButton Color="Color.Secondary" | ||||||
|                                                         Variant="Variant.Filled" |                                             Variant="Variant.Filled" | ||||||
|                                                         Disabled="@deleteAttachmentInProcess" |                                             Disabled="@deleteAttachmentInProcess" | ||||||
|                                                         OnClick="@((e) => DeleteAttachment(context))"> |                                             OnClick="@((e) => DeleteAttachment(context))"> | ||||||
|                                                 @if (deleteAttachmentInProcess) { |                                                 @if (deleteAttachmentInProcess) { | ||||||
|                                                     <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> |                                                     <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> | ||||||
|                                                     <MudText>Deleting</MudText> |                                                     <MudText>Deleting</MudText> | ||||||
| @ -389,24 +391,24 @@ | |||||||
|                                     </MudText> |                                     </MudText> | ||||||
|                                 } |                                 } | ||||||
|                                 <MudTable Items="@actionItems.Where(a => a.Step == current_i)" |                                 <MudTable Items="@actionItems.Where(a => a.Step == current_i)" | ||||||
|                                             Class="m-2" |                                 Class="m-2" | ||||||
|                                             Striped="true" |                                 Striped="true" | ||||||
|                                             SortLabel="Sort By" |                                 SortLabel="Sort By" | ||||||
|                                             Hover="true"> |                                 Hover="true"> | ||||||
|                                     <ToolBarContent> |                                     <ToolBarContent> | ||||||
|                                         <MudStack Row="true" Justify="Justify.Center" Spacing="1" Style="width: 100%"> |                                         <MudStack Row="true" Justify="Justify.Center" Spacing="1" Style="width: 100%"> | ||||||
|                                             @if (previousStageSubmitted && !currentStageSubmitted) { |                                             @if (previousStageSubmitted && !currentStageSubmitted) { | ||||||
|                                                 <MudButton Variant="Variant.Filled" |                                                 <MudButton Variant="Variant.Filled" | ||||||
|                                                             Color="Color.Tertiary" |                                                 Color="Color.Tertiary" | ||||||
|                                                             OnClick="@((e) => CreateNewActionItem(current_i))"> |                                                 OnClick="@((e) => CreateNewActionItem(current_i))"> | ||||||
|                                                     <MudText>New Action Item</MudText> |                                                     <MudText>New Action Item</MudText> | ||||||
|                                                 </MudButton> |                                                 </MudButton> | ||||||
|                                             } |                                             } | ||||||
|                                             @if (currentStagePendingActionItemCount > 0) { |                                             @if (currentStagePendingActionItemCount > 0) { | ||||||
|                                                 <MudButton Variant="Variant.Filled" |                                                 <MudButton Variant="Variant.Filled" | ||||||
|                                                             Color="Color.Tertiary" |                                                 Color="Color.Tertiary" | ||||||
|                                                             Disabled="@processing" |                                                 Disabled="@processing" | ||||||
|                                                             OnClick="@((e) => CloseAllActionItems(current_i))"> |                                                 OnClick="@((e) => CloseAllActionItems(current_i))"> | ||||||
|                                                     <MudText>Complete All Actions</MudText> |                                                     <MudText>Complete All Actions</MudText> | ||||||
|                                                 </MudButton> |                                                 </MudButton> | ||||||
|                                             } |                                             } | ||||||
| @ -448,14 +450,14 @@ | |||||||
|                                         @if (pcrb.ClosedDate >= DateTimeUtilities.MAX_DT && context.ClosedByID == 0) { |                                         @if (pcrb.ClosedDate >= DateTimeUtilities.MAX_DT && context.ClosedByID == 0) { | ||||||
|                                             <MudTd Style="text-align:center;"> |                                             <MudTd Style="text-align:center;"> | ||||||
|                                                 <MudButton Color="Color.Tertiary" |                                                 <MudButton Color="Color.Tertiary" | ||||||
|                                                             Variant="Variant.Filled" |                                                 Variant="Variant.Filled" | ||||||
|                                                             OnClick="@((e) => UpdateActionItem(context))"> |                                                 OnClick="@((e) => UpdateActionItem(context))"> | ||||||
|                                                     <MudText>Update</MudText> |                                                     <MudText>Update</MudText> | ||||||
|                                                 </MudButton> |                                                 </MudButton> | ||||||
|                                                 @if (!currentStageSubmitted) { |                                                 @if (!currentStageSubmitted) { | ||||||
|                                                     <MudButton Color="Color.Secondary" |                                                     <MudButton Color="Color.Secondary" | ||||||
|                                                                Variant="Variant.Filled" |                                                     Variant="Variant.Filled" | ||||||
|                                                                OnClick="@((e) => DeleteActionItem(context))"> |                                                     OnClick="@((e) => DeleteActionItem(context))"> | ||||||
|                                                         <MudText>Delete</MudText> |                                                         <MudText>Delete</MudText> | ||||||
|                                                     </MudButton> |                                                     </MudButton> | ||||||
|                                                 } |                                                 } | ||||||
| @ -468,10 +470,10 @@ | |||||||
|  |  | ||||||
|                                 <MudText Typo="Typo.h5" Align="Align.Center">Affected Documents</MudText> |                                 <MudText Typo="Typo.h5" Align="Align.Center">Affected Documents</MudText> | ||||||
|                                 <MudTable Items="@pcr3Documents" |                                 <MudTable Items="@pcr3Documents" | ||||||
|                                           Class="m-2" |                                 Class="m-2" | ||||||
|                                           Striped="true" |                                 Striped="true" | ||||||
|                                           SortLabel="Sort By" |                                 SortLabel="Sort By" | ||||||
|                                           Hover="true"> |                                 Hover="true"> | ||||||
|                                     <HeaderContent> |                                     <HeaderContent> | ||||||
|                                         <MudTh> |                                         <MudTh> | ||||||
|                                             <MudTableSortLabel SortBy="new Func<PCR3Document, object>(x=>x.DocType)"> |                                             <MudTableSortLabel SortBy="new Func<PCR3Document, object>(x=>x.DocType)"> | ||||||
| @ -513,8 +515,8 @@ | |||||||
|                                                 context.GetEcnNumberString(); |                                                 context.GetEcnNumberString(); | ||||||
|                                             } else { |                                             } else { | ||||||
|                                                 <MudLink  |                                                 <MudLink  | ||||||
|                                                     Href=@($"{config["OldFabApprovalUrl"]}/ECN/Edit?IssueID={context.GetEcnNumberString()}") |                                                 Href=@($"{config["OldFabApprovalUrl"]}/ECN/Edit?IssueID={context.GetEcnNumberString()}") | ||||||
|                                                     Target="_blank"> |                                                 Target="_blank"> | ||||||
|                                                     @context.GetEcnNumberString() |                                                     @context.GetEcnNumberString() | ||||||
|                                                 </MudLink> |                                                 </MudLink> | ||||||
|                                             } |                                             } | ||||||
| @ -526,8 +528,8 @@ | |||||||
|                                         @if (pcrb.ClosedDate >= DateTimeUtilities.MAX_DT && !currentStageSubmitted) { |                                         @if (pcrb.ClosedDate >= DateTimeUtilities.MAX_DT && !currentStageSubmitted) { | ||||||
|                                             <MudTd Style="text-align:center;"> |                                             <MudTd Style="text-align:center;"> | ||||||
|                                                 <MudButton Color="Color.Tertiary" |                                                 <MudButton Color="Color.Tertiary" | ||||||
|                                                            Variant="Variant.Filled" |                                                 Variant="Variant.Filled" | ||||||
|                                                            OnClick="@((e) => UpdatePCR3Document(context))"> |                                                 OnClick="@((e) => UpdatePCR3Document(context))"> | ||||||
|                                                     <MudText>Update</MudText> |                                                     <MudText>Update</MudText> | ||||||
|                                                 </MudButton> |                                                 </MudButton> | ||||||
|                                             </MudTd> |                                             </MudTd> | ||||||
| @ -539,23 +541,23 @@ | |||||||
|                             <MudDivider DividerType="DividerType.Middle" Class="my-1" /> |                             <MudDivider DividerType="DividerType.Middle" Class="my-1" /> | ||||||
|                             <MudText Typo="Typo.h5" Align="Align.Center">Attendees</MudText> |                             <MudText Typo="Typo.h5" Align="Align.Center">Attendees</MudText> | ||||||
|                             <MudTable Items="@attendees.Where(a => a.Step == current_i)" |                             <MudTable Items="@attendees.Where(a => a.Step == current_i)" | ||||||
|                                       Class="m-2" |                             Class="m-2" | ||||||
|                                       Striped="true" |                             Striped="true" | ||||||
|                                       SortLabel="Sort By" |                             SortLabel="Sort By" | ||||||
|                                       Hover="true"> |                             Hover="true"> | ||||||
|                                 <ToolBarContent> |                                 <ToolBarContent> | ||||||
|                                     @if (pcrb.ClosedDate >= DateTimeUtilities.MAX_DT && !currentStageSubmitted) { |                                     @if (pcrb.ClosedDate >= DateTimeUtilities.MAX_DT && !currentStageSubmitted) { | ||||||
|                                         <MudStack Row="true" Justify="Justify.Center" Spacing="1" Style="width: 100%"> |                                         <MudStack Row="true" Justify="Justify.Center" Spacing="1" Style="width: 100%"> | ||||||
|                                             <MudButton Color="Color.Tertiary" |                                             <MudButton Color="Color.Tertiary" | ||||||
|                                                        Variant="Variant.Filled" |                                             Variant="Variant.Filled" | ||||||
|                                                        Class="m-1" |                                             Class="m-1" | ||||||
|                                                        OnClick="@((e) => AddAttendee(current_i))"> |                                             OnClick="@((e) => AddAttendee(current_i))"> | ||||||
|                                                 <MudText>Add Attendee</MudText> |                                                 <MudText>Add Attendee</MudText> | ||||||
|                                             </MudButton> |                                             </MudButton> | ||||||
|                                             <MudButton Color="Color.Tertiary" |                                             <MudButton Color="Color.Tertiary" | ||||||
|                                                        Variant="Variant.Filled" |                                             Variant="Variant.Filled" | ||||||
|                                                        Class="m-1" |                                             Class="m-1" | ||||||
|                                                        OnClick="@((e) => MarkAllAttended(current_i))"> |                                             OnClick="@((e) => MarkAllAttended(current_i))"> | ||||||
|                                                 <MudText>Mark All Attended</MudText> |                                                 <MudText>Mark All Attended</MudText> | ||||||
|                                             </MudButton> |                                             </MudButton> | ||||||
|                                         </MudStack> |                                         </MudStack> | ||||||
| @ -577,9 +579,9 @@ | |||||||
|                                     </MudTd> |                                     </MudTd> | ||||||
|                                     <MudTd DataLabel="Attended?"> |                                     <MudTd DataLabel="Attended?"> | ||||||
|                                         <MudIconButton Disabled="@(pcrb.ClosedDate < DateTimeUtilities.MAX_DT || currentStageSubmitted)" |                                         <MudIconButton Disabled="@(pcrb.ClosedDate < DateTimeUtilities.MAX_DT || currentStageSubmitted)" | ||||||
|                                             Icon="@(context.Attended ? Icons.Material.Filled.CheckBox : Icons.Material.Filled.CheckBoxOutlineBlank)" |                                         Icon="@(context.Attended ? Icons.Material.Filled.CheckBox : Icons.Material.Filled.CheckBoxOutlineBlank)" | ||||||
|                                             Color="Color.Tertiary" |                                         Color="Color.Tertiary" | ||||||
|                                             OnClick="@((e) => MarkAttended(context))" /> |                                         OnClick="@((e) => MarkAttended(context))" /> | ||||||
|                                     </MudTd> |                                     </MudTd> | ||||||
|                                 </RowTemplate> |                                 </RowTemplate> | ||||||
|                             </MudTable> |                             </MudTable> | ||||||
| @ -592,33 +594,33 @@ | |||||||
|                                 </MudText> |                                 </MudText> | ||||||
|                             } |                             } | ||||||
|                             <MudTable Items="@approvals.Where(a => a.Step == current_i).OrderBy(a => a.CompletedDate)" |                             <MudTable Items="@approvals.Where(a => a.Step == current_i).OrderBy(a => a.CompletedDate)" | ||||||
|                                         Class="m-2" |                             Class="m-2" | ||||||
|                                         Striped="true" |                             Striped="true" | ||||||
|                                         SortLabel="Sort By" |                             SortLabel="Sort By" | ||||||
|                                         Hover="true"> |                             Hover="true"> | ||||||
|                                 <ToolBarContent> |                                 <ToolBarContent> | ||||||
|                                     @if (pcrb.ClosedDate >= DateTimeUtilities.MAX_DT && previousStageApproved) { |                                     @if (pcrb.ClosedDate >= DateTimeUtilities.MAX_DT && previousStageApproved) { | ||||||
|                                         <MudStack Row="true" Justify="Justify.Center" Spacing="1" Style="width: 100%"> |                                         <MudStack Row="true" Justify="Justify.Center" Spacing="1" Style="width: 100%"> | ||||||
|                                             @if (!currentStageSubmitted) { |                                             @if (!currentStageSubmitted) { | ||||||
|                                                 <MudButton Variant="Variant.Filled" |                                                 <MudButton Variant="Variant.Filled" | ||||||
|                                                            Color="Color.Tertiary" |                                                 Color="Color.Tertiary" | ||||||
|                                                            OnClick="@((e) => AddApprover(current_i))"> |                                                 OnClick="@((e) => AddApprover(current_i))"> | ||||||
|                                                     <MudText>Add Approver</MudText> |                                                     <MudText>Add Approver</MudText> | ||||||
|                                                 </MudButton> |                                                 </MudButton> | ||||||
|                                             } |                                             } | ||||||
|                                             @if (previousStageSubmitted && !currentStageSubmitted && currentStageAttachments.Count() > 0 &&  |                                             @if (previousStageSubmitted && !currentStageSubmitted && currentStageAttachments.Count() > 0 &&  | ||||||
|                                                  !affectedDocumentsIncomplete && allActionItemsComplete && |                                     !affectedDocumentsIncomplete && allActionItemsComplete && | ||||||
|                                                  !previousStageHasOpenGatedActionItems && atLeastOneAttendeeAttended) { |                                     !previousStageHasOpenGatedActionItems && atLeastOneAttendeeAttended) { | ||||||
|                                                 @if (submitInProcess) { |                                                 @if (submitInProcess) { | ||||||
|                                                     <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> |                                                     <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> | ||||||
|                                                     <MudText>Submitting</MudText> |                                                     <MudText>Submitting</MudText> | ||||||
|                                                 } else { |                                                 } else { | ||||||
|                                                     <MudButton Variant="Variant.Filled" |                                                     <MudButton Variant="Variant.Filled" | ||||||
|                                                                Color="Color.Tertiary" |                                                     Color="Color.Tertiary" | ||||||
|                                                                Disabled="@submitInProcess" |                                                     Disabled="@submitInProcess" | ||||||
|                                                                OnClick="@((e) => SubmitForApproval(current_i))"> |                                                     OnClick="@((e) => SubmitForApproval(current_i))"> | ||||||
|                                                     <MudText>Submit For Approval</MudText> |                                                         <MudText>Submit For Approval</MudText> | ||||||
|                                                 </MudButton> |                                                     </MudButton> | ||||||
|                                                 } |                                                 } | ||||||
|                                             } |                                             } | ||||||
|                                         </MudStack> |                                         </MudStack> | ||||||
| @ -652,31 +654,31 @@ | |||||||
|                                     <MudTd DataLabel="Disposition Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.CompletedDate)</MudTd> |                                     <MudTd DataLabel="Disposition Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.CompletedDate)</MudTd> | ||||||
|                                     <MudTd DataLabel="Comments">@context.Comments</MudTd> |                                     <MudTd DataLabel="Comments">@context.Comments</MudTd> | ||||||
|                                     @if (pcrb.ClosedDate >= DateTimeUtilities.MAX_DT && (currentStageUnsubmittedApprovalCount > 0 ||  |                                     @if (pcrb.ClosedDate >= DateTimeUtilities.MAX_DT && (currentStageUnsubmittedApprovalCount > 0 ||  | ||||||
|                                          currentStagePendingApprovalsCount > 0)) { |                             currentStagePendingApprovalsCount > 0)) { | ||||||
|                                         <MudTd Style="text-align:center;"> |                                         <MudTd Style="text-align:center;"> | ||||||
|                                             @if (context.ItemStatus == 0 && userIsAdmin) { |                                             @if (context.ItemStatus == 0 && userIsAdmin) { | ||||||
|                                                 <MudButton Color="Color.Warning" |                                                 <MudButton Color="Color.Warning" | ||||||
|                                                            Variant="Variant.Filled" |                                                 Variant="Variant.Filled" | ||||||
|                                                            Class="m-1" |                                                 Class="m-1" | ||||||
|                                                            OnClick="@((e) => UpdateApproval(context))"> |                                                 OnClick="@((e) => UpdateApproval(context))"> | ||||||
|                                                     <MudText>Update</MudText> |                                                     <MudText>Update</MudText> | ||||||
|                                                 </MudButton> |                                                 </MudButton> | ||||||
|                                             } |                                             } | ||||||
|                                             @if ((current_i < 3 || pcr3Documents.Where(d=>d.CompletedByID == 0).Count() == 0) &&  |                                             @if ((current_i < 3 || pcr3Documents.Where(d=>d.CompletedByID == 0).Count() == 0) &&  | ||||||
|                                                  (authStateProvider.CurrentUser is not null && context.UserID == authStateProvider.CurrentUser.UserID) && |                                     (authStateProvider.CurrentUser is not null && context.UserID == authStateProvider.CurrentUser.UserID) && | ||||||
|                                                  context.ItemStatus == 0 && context.AssignedDate > DateTimeUtilities.MIN_DT) { |                                     context.ItemStatus == 0 && context.AssignedDate > DateTimeUtilities.MIN_DT) { | ||||||
|                                                 <MudButton Color="Color.Tertiary" |                                                 <MudButton Color="Color.Tertiary" | ||||||
|                                                            Variant="Variant.Filled" |                                                 Variant="Variant.Filled" | ||||||
|                                                            Class="m-1" |                                                 Class="m-1" | ||||||
|                                                            Disabled="@processing" |                                                 Disabled="@processing" | ||||||
|                                                            OnClick="@((e) => ApprovePCR(current_i))"> |                                                 OnClick="@((e) => ApprovePCR(current_i))"> | ||||||
|                                                     <MudText>Approve</MudText> |                                                     <MudText>Approve</MudText> | ||||||
|                                                 </MudButton> |                                                 </MudButton> | ||||||
|                                                 <MudButton Color="Color.Secondary" |                                                 <MudButton Color="Color.Secondary" | ||||||
|                                                            Variant="Variant.Filled" |                                                 Variant="Variant.Filled" | ||||||
|                                                            Class="m-1" |                                                 Class="m-1" | ||||||
|                                                            Disabled="@processing" |                                                 Disabled="@processing" | ||||||
|                                                            OnClick="@((e) => DenyPCR(current_i))"> |                                                 OnClick="@((e) => DenyPCR(current_i))"> | ||||||
|                                                     <MudText>Deny</MudText> |                                                     <MudText>Deny</MudText> | ||||||
|                                                 </MudButton> |                                                 </MudButton> | ||||||
|                                             } |                                             } | ||||||
| @ -688,6 +690,195 @@ | |||||||
|                     </ChildContent> |                     </ChildContent> | ||||||
|                 </MudExpansionPanel> |                 </MudExpansionPanel> | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             @if (pcrb.FollowUps.Count() > 0) { | ||||||
|  |                 <MudExpansionPanel Class="m-2" Expanded=@(pcrb.CurrentStep == (int)PCRB.StagesEnum.FollowUp &&  | ||||||
|  |                                                           !pcrb.FollowUps.First().IsComplete)> | ||||||
|  |                     <TitleContent> | ||||||
|  |                         <MudText Typo="Typo.h4" Align="Align.Center">Follow Up</MudText> | ||||||
|  |                     </TitleContent> | ||||||
|  |                     <ChildContent> | ||||||
|  |                         <MudPaper Outlined="true" | ||||||
|  |                         Class="p-2 m-2 d-flex flex-column justify-start gap-2"> | ||||||
|  |                             <MudPaper Outlined="true" | ||||||
|  |                             Class="p-1" | ||||||
|  |                             Elevation="15"> | ||||||
|  |                                 <MudText Typo="Typo.h5" Align="Align.Center">Supporting Documents</MudText> | ||||||
|  |                                 <MudTable Items="@attachments.Where(a => a.Step == 5)" | ||||||
|  |                                 Class="m-2" | ||||||
|  |                                 Striped="true" | ||||||
|  |                                 SortLabel="Sort By" | ||||||
|  |                                 Hover="true"> | ||||||
|  |                                     <ToolBarContent> | ||||||
|  |                                         <MudStack Row="true" Justify="Justify.Center" Spacing="1" Style="width: 100%"> | ||||||
|  |                                             @if (!pcrb.FollowUps.First().IsComplete && !pcrb.FollowUps.First().IsPendingApproval) { | ||||||
|  |                                                 <MudButton Variant="Variant.Filled" | ||||||
|  |                                                 Color="Color.Tertiary" | ||||||
|  |                                                 OnClick="@((e) => UploadAttachment(5))" | ||||||
|  |                                                 Disabled="@attachmentUploadInProcess" | ||||||
|  |                                                 StartIcon="@Icons.Material.Filled.AttachFile"> | ||||||
|  |                                                     @if (attachmentUploadInProcess) { | ||||||
|  |                                                         <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> | ||||||
|  |                                                         <MudText>Processing</MudText> | ||||||
|  |                                                     } | ||||||
|  |                                                     else { | ||||||
|  |                                                         <MudText>Upload Document</MudText> | ||||||
|  |                                                     } | ||||||
|  |                                                 </MudButton> | ||||||
|  |                                             } | ||||||
|  |                                             @if (!pcrb.FollowUps.First().IsPendingApproval && !pcrb.FollowUps.First().IsComplete && | ||||||
|  |                                     attachments.Where(a => a.Step == 5).Count() > 0 && | ||||||
|  |                                     approvals.Where(a => a.Step == 5 &&  | ||||||
|  |                                                          a.UserID == authStateProvider.CurrentUser.UserID && | ||||||
|  |                                                          a.ItemStatus == 0).Count() > 0) { | ||||||
|  |                                                 <MudButton Variant="Variant.Filled" | ||||||
|  |                                                 Color="Color.Tertiary" | ||||||
|  |                                                 OnClick="SubmitFollowUpForApproval" | ||||||
|  |                                                 Disabled="@followUpSubmitInProcess"> | ||||||
|  |                                                     @if (followUpSubmitInProcess) { | ||||||
|  |                                                         <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> | ||||||
|  |                                                         <MudText>Processing</MudText> | ||||||
|  |                                                     } | ||||||
|  |                                                     else { | ||||||
|  |                                                         <MudText>Submit For Closure</MudText> | ||||||
|  |                                                     } | ||||||
|  |                                                 </MudButton> | ||||||
|  |                                             } | ||||||
|  |                                             @if (pcrb.FollowUps.First().IsPendingApproval && !pcrb.FollowUps.First().IsComplete && | ||||||
|  |                                     attachments.Where(a => a.Step == 5).Count() > 0 && | ||||||
|  |                                     approvals.Where(a => a.Step == 5 && | ||||||
|  |                                                          a.UserID == authStateProvider.CurrentUser.UserID).Count() > 0) | ||||||
|  |                                                          { | ||||||
|  |                                                 @if (userIsQA) { | ||||||
|  |                                                     <MudButton Variant="Variant.Filled" | ||||||
|  |                                                                Color="Color.Tertiary" | ||||||
|  |                                                                OnClick="ApproveFollowUp" | ||||||
|  |                                                                Disabled="@followUpApproveInProcess"> | ||||||
|  |                                                         @if (followUpApproveInProcess) { | ||||||
|  |                                                             <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> | ||||||
|  |                                                             <MudText>Processing</MudText> | ||||||
|  |                                                         } else { | ||||||
|  |                                                             <MudText>Close</MudText> | ||||||
|  |                                                         } | ||||||
|  |                                                     </MudButton> | ||||||
|  |                                                     <MudButton Variant="Variant.Filled" | ||||||
|  |                                                                Color="Color.Secondary" | ||||||
|  |                                                                OnClick="@((e) => DenyFollowUp("Reject"))" | ||||||
|  |                                                                Disabled="@followUpDenyInProcess"> | ||||||
|  |                                                         @if (followUpDenyInProcess) { | ||||||
|  |                                                             <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> | ||||||
|  |                                                             <MudText>Processing</MudText> | ||||||
|  |                                                         } else { | ||||||
|  |                                                             <MudText>Reject</MudText> | ||||||
|  |                                                         } | ||||||
|  |                                                     </MudButton> | ||||||
|  |                                                 } else | ||||||
|  |                                                 { | ||||||
|  |                                                     <MudButton Variant="Variant.Filled" | ||||||
|  |                                                                Color="Color.Secondary" | ||||||
|  |                                                                OnClick="@((e) => DenyFollowUp("Recall"))" | ||||||
|  |                                                                Disabled="@followUpDenyInProcess"> | ||||||
|  |                                                         @if (followUpDenyInProcess) { | ||||||
|  |                                                             <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> | ||||||
|  |                                                             <MudText>Processing</MudText> | ||||||
|  |                                                         } else { | ||||||
|  |                                                             <MudText>Recall</MudText> | ||||||
|  |                                                         } | ||||||
|  |                                                     </MudButton> | ||||||
|  |                                                 } | ||||||
|  |                                             } | ||||||
|  |                                         </MudStack> | ||||||
|  |                                     </ToolBarContent> | ||||||
|  |                                     <HeaderContent> | ||||||
|  |                                         <MudTh> | ||||||
|  |                                             <MudTableSortLabel SortBy="new Func<PCRBAttachment, object>(x=>x.FileName)"> | ||||||
|  |                                                 File Name | ||||||
|  |                                             </MudTableSortLabel> | ||||||
|  |                                         </MudTh> | ||||||
|  |                                         <MudTh> | ||||||
|  |                                             <MudTableSortLabel SortBy="new Func<PCRBAttachment, object>(x=>x.UploadedBy is null ? string.Empty : x.UploadedBy.LastName)"> | ||||||
|  |                                                 Uploaded By | ||||||
|  |                                             </MudTableSortLabel> | ||||||
|  |                                         </MudTh> | ||||||
|  |                                         <MudTh> | ||||||
|  |                                             <MudTableSortLabel SortBy="new Func<PCRBAttachment, object>(x=>x.UploadDateTime)"> | ||||||
|  |                                                 Uploaded Date | ||||||
|  |                                             </MudTableSortLabel> | ||||||
|  |                                         </MudTh> | ||||||
|  |                                     </HeaderContent> | ||||||
|  |                                     <RowTemplate> | ||||||
|  |                                         <MudTd DataLabel="File Name"> | ||||||
|  |                                             <a href="@(@$"{config["FabApprovalApiBaseUrl"]}/pcrb/attachmentFile?path={context.Path}&fileName={context.FileName}")" | ||||||
|  |                                                download="@(context.FileName)" | ||||||
|  |                                                target="_top"> | ||||||
|  |                                                 @context.FileName | ||||||
|  |                                             </a> | ||||||
|  |                                         </MudTd> | ||||||
|  |                                         <MudTd DataLabel="Uploaded By">@(context.UploadedBy is null ? string.Empty : context.UploadedBy.GetFullName())</MudTd> | ||||||
|  |                                         <MudTd DataLabel="Uploaded Date">@context.UploadDateTime.ToString("yyyy-MM-dd HH:mm")</MudTd> | ||||||
|  |                                         @if (!pcrb.FollowUps.First().IsComplete && !pcrb.FollowUps.First().IsPendingApproval) | ||||||
|  |                                         { | ||||||
|  |                                             <MudTd Style="text-align:center;"> | ||||||
|  |                                                 <MudButton Color="Color.Secondary" | ||||||
|  |                                                            Variant="Variant.Filled" | ||||||
|  |                                                            Disabled="@deleteAttachmentInProcess" | ||||||
|  |                                                            OnClick="@((e) => DeleteAttachment(context))"> | ||||||
|  |                                                     @if (deleteAttachmentInProcess) | ||||||
|  |                                                     { | ||||||
|  |                                                         <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> | ||||||
|  |                                                         <MudText>Deleting</MudText> | ||||||
|  |                                                     } | ||||||
|  |                                                     else | ||||||
|  |                                                     { | ||||||
|  |                                                         <MudText>Delete</MudText> | ||||||
|  |                                                     } | ||||||
|  |                                                 </MudButton> | ||||||
|  |                                             </MudTd> | ||||||
|  |                                         } | ||||||
|  |                                     </RowTemplate> | ||||||
|  |                                 </MudTable> | ||||||
|  |                             </MudPaper> | ||||||
|  |                             <MudPaper Outlined="true" | ||||||
|  |                                       Class="p-2 d-flex flex-column flex-wrap"> | ||||||
|  |                                 <MudText Typo="Typo.h5" Align="Align.Center">Follow Up Date</MudText> | ||||||
|  |                                 <MudDatePicker Label="Follow Up Date" | ||||||
|  |                                                Date="pcrb.FollowUps.First().FollowUpDate" | ||||||
|  |                                                MinDate="@(pcrb.ClosedDate)" | ||||||
|  |                                                Color="@Color.Tertiary" | ||||||
|  |                                                Disabled="@(!userIsQA || | ||||||
|  |                                                            pcrb.FollowUps.Count() == 0 ||  | ||||||
|  |                                                            pcrb.FollowUps.First().IsComplete ||  | ||||||
|  |                                                            pcrb.FollowUps.First().IsPendingApproval)" | ||||||
|  |                                                Rounded="true" | ||||||
|  |                                                Error="false" | ||||||
|  |                                                Elevation="6" | ||||||
|  |                                                DateChanged="UpdateFollowUpDate" | ||||||
|  |                                                Variant="Variant.Outlined" /> | ||||||
|  |                             </MudPaper> | ||||||
|  |                             @if (followUpComments.Count() > 0) { | ||||||
|  |                                 <MudPaper Outlined="true" Class="p-1" Elevation="15"> | ||||||
|  |                                     <MudText Typo="Typo.h5" Align="Align.Center">Revision Comments</MudText> | ||||||
|  |                                     <MudTable Items="@followUpComments" | ||||||
|  |                                           Class="m-2" | ||||||
|  |                                           Striped="true" | ||||||
|  |                                           Hover="true"> | ||||||
|  |                                         <HeaderContent> | ||||||
|  |                                             <MudTh>Date</MudTh> | ||||||
|  |                                             <MudTh>User</MudTh> | ||||||
|  |                                             <MudTh>Comment</MudTh> | ||||||
|  |                                         </HeaderContent> | ||||||
|  |                                         <RowTemplate> | ||||||
|  |                                             <MudTd DataLabel="Date">@context.CommentDate.ToString("MM/dd/yyyy hh:mm")</MudTd> | ||||||
|  |                                             <MudTd DataLabel="User">@(context.User?.GetFullName() ?? string.Empty)</MudTd> | ||||||
|  |                                             <MudTd DataLabel="Comment">@context.Comment</MudTd> | ||||||
|  |                                         </RowTemplate> | ||||||
|  |                                     </MudTable> | ||||||
|  |                                 </MudPaper> | ||||||
|  |                             } | ||||||
|  |                         </MudPaper> | ||||||
|  |                     </ChildContent> | ||||||
|  |                 </MudExpansionPanel> | ||||||
|  |             } | ||||||
|         </MudExpansionPanels> |         </MudExpansionPanels> | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -34,36 +34,39 @@ public partial class PCRBSingle { | |||||||
|     private IEnumerable<PCRBAttachment> attachments = new List<PCRBAttachment>(); |     private IEnumerable<PCRBAttachment> attachments = new List<PCRBAttachment>(); | ||||||
|     private IEnumerable<PCRBActionItem> actionItems = new List<PCRBActionItem>(); |     private IEnumerable<PCRBActionItem> actionItems = new List<PCRBActionItem>(); | ||||||
|     private IEnumerable<PCR3Document> pcr3Documents = new List<PCR3Document>(); |     private IEnumerable<PCR3Document> pcr3Documents = new List<PCR3Document>(); | ||||||
|  |     private IEnumerable<PCRBFollowUpComment> followUpComments = new List<PCRBFollowUpComment>(); | ||||||
|  |  | ||||||
|  |     private DateTime? followUpDate; | ||||||
|  |  | ||||||
|     private IEnumerable<int> qualityApproverUserIds = new List<int>(); |     private IEnumerable<int> qualityApproverUserIds = new List<int>(); | ||||||
|  |  | ||||||
|     private IEnumerable<User> allActiveUsers = new List<User>(); |     private IEnumerable<User> allActiveUsers = new List<User>(); | ||||||
|     private User selectedOwner = null; |     private User selectedOwner = null; | ||||||
|  |  | ||||||
|  |     private bool userIsQA = false; | ||||||
|  |  | ||||||
|     private bool processing = false; |     private bool processing = false; | ||||||
|     private bool saveInProcess = false; |     private bool saveInProcess = false; | ||||||
|     private bool deleteInProcess = false; |     private bool deleteInProcess = false; | ||||||
|     private bool submitInProcess = false; |     private bool submitInProcess = false; | ||||||
|     private bool approvalInProcess = false; |  | ||||||
|     private bool denialInProcess = false; |  | ||||||
|     private bool recallInProcess = false; |  | ||||||
|     private bool attachmentUploadInProcess = false; |     private bool attachmentUploadInProcess = false; | ||||||
|     private bool updateAttachmentInProcess = false; |  | ||||||
|     private bool deleteAttachmentInProcess = false; |     private bool deleteAttachmentInProcess = false; | ||||||
|     private bool addActionItemInProcess = false; |     private bool followUpSubmitInProcess = false; | ||||||
|  |     private bool followUpApproveInProcess = false; | ||||||
|     private string attachmentSearchString = ""; |     private bool followUpDenyInProcess = false; | ||||||
|  |  | ||||||
|     private string actionItemSearchString = ""; |  | ||||||
|  |  | ||||||
|     protected override async Task OnParametersSetAsync() { |     protected override async Task OnParametersSetAsync() { | ||||||
|         processing = true; |         processing = true; | ||||||
|         try { |         try { | ||||||
|  |             cache.Set("redirectUrl", $"pcrb/{planNumber}"); | ||||||
|  |  | ||||||
|             allActiveUsers = await userService.GetAllActiveUsers(); |             allActiveUsers = await userService.GetAllActiveUsers(); | ||||||
|  |  | ||||||
|             if (qualityApproverUserIds.Count() == 0) |             if (qualityApproverUserIds.Count() == 0) | ||||||
|                 qualityApproverUserIds = await GetQualityApproverUserIds(); |                 qualityApproverUserIds = await GetQualityApproverUserIds(); | ||||||
|  |  | ||||||
|  |             userIsQA = qualityApproverUserIds.Contains(authStateProvider.CurrentUser?.UserID ?? -1); | ||||||
|  |  | ||||||
|             if (!string.IsNullOrWhiteSpace(planNumber) && Int32.TryParse(planNumber, out planNumberInt)) { |             if (!string.IsNullOrWhiteSpace(planNumber) && Int32.TryParse(planNumber, out planNumberInt)) { | ||||||
|                 pcrb = await pcrbService.GetPCRBByPlanNumber(planNumberInt, false); |                 pcrb = await pcrbService.GetPCRBByPlanNumber(planNumberInt, false); | ||||||
|                 approvals = await approvalService.GetApprovalsForIssueId(planNumberInt, false); |                 approvals = await approvalService.GetApprovalsForIssueId(planNumberInt, false); | ||||||
| @ -71,6 +74,10 @@ public partial class PCRBSingle { | |||||||
|                 attachments = await pcrbService.GetAttachmentsByPlanNumber(planNumberInt, false); |                 attachments = await pcrbService.GetAttachmentsByPlanNumber(planNumberInt, false); | ||||||
|                 actionItems = await pcrbService.GetActionItemsForPlanNumber(planNumberInt, false); |                 actionItems = await pcrbService.GetActionItemsForPlanNumber(planNumberInt, false); | ||||||
|                 pcr3Documents = await pcrbService.GetPCR3DocumentsForPlanNumber(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<Task> createPCR3DocumentTasks = new(); |                 List<Task> createPCR3DocumentTasks = new(); | ||||||
|                 if (pcr3Documents.Count() <= 0) { |                 if (pcr3Documents.Count() <= 0) { | ||||||
| @ -117,9 +124,9 @@ public partial class PCRBSingle { | |||||||
|  |  | ||||||
|                 if (pcrb.OwnerID > 0) selectedOwner = await userService.GetUserByUserId(pcrb.OwnerID); |                 if (pcrb.OwnerID > 0) selectedOwner = await userService.GetUserByUserId(pcrb.OwnerID); | ||||||
|  |  | ||||||
|                 if (pcrb.CurrentStep > 0 && pcrb.CurrentStep < 4) { |                 if (pcrb.CurrentStep > (int)PCRB.StagesEnum.Draft && pcrb.CurrentStep < (int)PCRB.StagesEnum.Complete) { | ||||||
|                     bool stageHasAdvanced = false; |                     bool stageHasAdvanced = false; | ||||||
|                     for (int stage = pcrb.CurrentStep; stage < 4; stage++) { |                     for (int stage = pcrb.CurrentStep; stage < (int)PCRB.StagesEnum.Complete; stage++) { | ||||||
|                         int current_stage = stage; |                         int current_stage = stage; | ||||||
|                         if (pcrb.CurrentStep == current_stage) { |                         if (pcrb.CurrentStep == current_stage) { | ||||||
|                             IEnumerable<Approval> currentStageApprovals = approvals.Where(a => a.Step == current_stage); |                             IEnumerable<Approval> currentStageApprovals = approvals.Where(a => a.Step == current_stage); | ||||||
| @ -128,7 +135,7 @@ public partial class PCRBSingle { | |||||||
|                             bool currentStageApproved = currentStageApprovedApprovalsCount >= 3 && currentStagePendingApprovalsCount == 0; |                             bool currentStageApproved = currentStageApprovedApprovalsCount >= 3 && currentStagePendingApprovalsCount == 0; | ||||||
|  |  | ||||||
|                             if (currentStageApproved) { |                             if (currentStageApproved) { | ||||||
|                                 if (pcrb.CurrentStep == 3) { |                                 if (pcrb.CurrentStep == (int)PCRB.StagesEnum.PCR3) { | ||||||
|                                     int openActionItemCount = actionItems.Where(a => a.ClosedByID == 0).Count(); |                                     int openActionItemCount = actionItems.Where(a => a.ClosedByID == 0).Count(); | ||||||
|                                     int openAffectedDocumentsCount = pcr3Documents.Where(d => d.CompletedByID == 0).Count(); |                                     int openAffectedDocumentsCount = pcr3Documents.Where(d => d.CompletedByID == 0).Count(); | ||||||
|  |  | ||||||
| @ -145,7 +152,7 @@ public partial class PCRBSingle { | |||||||
|                     } |                     } | ||||||
|  |  | ||||||
|                     if (stageHasAdvanced) { |                     if (stageHasAdvanced) { | ||||||
|                         if (pcrb.CurrentStep == 4) { |                         if (pcrb.CurrentStep == (int)PCRB.StagesEnum.Complete) { | ||||||
|                             pcrb.ClosedDate = DateTime.Now; |                             pcrb.ClosedDate = DateTime.Now; | ||||||
|  |  | ||||||
|                             string message = $"PCRB# {pcrb.PlanNumber} - {pcrb.Title} is complete"; |                             string message = $"PCRB# {pcrb.PlanNumber} - {pcrb.Title} is complete"; | ||||||
| @ -165,6 +172,34 @@ public partial class PCRBSingle { | |||||||
|                         await OnParametersSetAsync(); |                         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 { |             } else { | ||||||
|                 int ownerID = 0; |                 int ownerID = 0; | ||||||
|                 string ownerName = string.Empty; |                 string ownerName = string.Empty; | ||||||
| @ -177,7 +212,7 @@ public partial class PCRBSingle { | |||||||
|                 pcrb = new() { |                 pcrb = new() { | ||||||
|                     OwnerID = ownerID, |                     OwnerID = ownerID, | ||||||
|                     OwnerName = ownerName, |                     OwnerName = ownerName, | ||||||
|                     CurrentStep = 0 |                     CurrentStep = (int)PCRB.StagesEnum.Draft | ||||||
|                 }; |                 }; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @ -215,7 +250,7 @@ public partial class PCRBSingle { | |||||||
|     private bool PCRBReadyToSubmit(int step) { |     private bool PCRBReadyToSubmit(int step) { | ||||||
|         bool readyToSubmit = GetIncompleteFields().Count() <= 0; |         bool readyToSubmit = GetIncompleteFields().Count() <= 0; | ||||||
|  |  | ||||||
|         readyToSubmit = readyToSubmit && pcrb.CurrentStep > 0; |         readyToSubmit = readyToSubmit && pcrb.CurrentStep > (int)PCRB.StagesEnum.Draft; | ||||||
|  |  | ||||||
|         readyToSubmit = readyToSubmit && attachments.Where(a => a.Step == step).Count() > 0; |         readyToSubmit = readyToSubmit && attachments.Where(a => a.Step == step).Count() > 0; | ||||||
|  |  | ||||||
| @ -241,7 +276,7 @@ public partial class PCRBSingle { | |||||||
|                 pcrb.OwnerID = selectedOwner.UserID; |                 pcrb.OwnerID = selectedOwner.UserID; | ||||||
|                 pcrb.OwnerName = selectedOwner.GetFullName(); |                 pcrb.OwnerName = selectedOwner.GetFullName(); | ||||||
|  |  | ||||||
|                 if (pcrb.CurrentStep == 0 && GetIncompleteFields().Count() <= 0) |                 if (pcrb.CurrentStep == (int)PCRB.StagesEnum.Draft && GetIncompleteFields().Count() <= 0) | ||||||
|                     pcrb.CurrentStep++; |                     pcrb.CurrentStep++; | ||||||
|  |  | ||||||
|                 if (initialPlanNumber <= 0) { |                 if (initialPlanNumber <= 0) { | ||||||
| @ -300,34 +335,54 @@ public partial class PCRBSingle { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     private async Task<IEnumerable<int>> GetQualityApproverUserIds() { |     private async Task<IEnumerable<int>> GetQualityApproverUserIds() { | ||||||
|         List<int> qualityApproverUserIds = new(); |  | ||||||
|  |  | ||||||
|         try { |         try { | ||||||
|             int roleId = await approvalService.GetRoleIdForRoleName("Module Manager"); |             HashSet<int>? qualityApproverUserIds = cache.Get<HashSet<int>>("qualityApproverUserIds"); | ||||||
|  |  | ||||||
|             if (roleId <= 0) throw new Exception($"could not find Module Manager role ID"); |             if (qualityApproverUserIds is null || qualityApproverUserIds.Count() == 0) { | ||||||
|  |                 qualityApproverUserIds = new(); | ||||||
|  |  | ||||||
|             IEnumerable<SubRole> subRoles = await approvalService.GetSubRolesForSubRoleName("MMSubRole", roleId); |                 int roleId = await approvalService.GetRoleIdForRoleName("Module Manager"); | ||||||
|  |  | ||||||
|             foreach (SubRole subRole in subRoles) { |                 if (roleId <= 0) throw new Exception($"could not find Module Manager role ID"); | ||||||
|                 if (subRole.SubRoleCategoryItem.Equals("Quality", StringComparison.InvariantCultureIgnoreCase)) { |  | ||||||
|  |                 IEnumerable<SubRole> subRoles = await approvalService.GetSubRolesForSubRoleName("MMSubRole", roleId); | ||||||
|  |  | ||||||
|  |                 foreach (SubRole subRole in subRoles) { | ||||||
|  |                     if (subRole.SubRoleCategoryItem.Equals("Quality", StringComparison.InvariantCultureIgnoreCase)) { | ||||||
|  |                         IEnumerable<User> 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<User> subRoleMembers = await approvalService.GetApprovalGroupMembers(subRole.SubRoleID); |                     IEnumerable<User> subRoleMembers = await approvalService.GetApprovalGroupMembers(subRole.SubRoleID); | ||||||
|                     foreach (User user in subRoleMembers) qualityApproverUserIds.Add(user.UserID); |                     foreach (User user in subRoleMembers) qualityApproverUserIds.Add(user.UserID); | ||||||
|                 } |                 } | ||||||
|             } |  | ||||||
|  |  | ||||||
|             string roleName = "QA_PRE_APPROVAL"; |                 roleName = "QA_FINAL_APPROVAL"; | ||||||
|             string subRoleName = "QA_PRE_APPROVAL"; |                 subRoleName = "QA_FINAL_APPROVAL"; | ||||||
|  |  | ||||||
|             roleId = await approvalService.GetRoleIdForRoleName(roleName); |                 roleId = await approvalService.GetRoleIdForRoleName(roleName); | ||||||
|  |  | ||||||
|             if (roleId <= 0) throw new Exception($"could not find {roleName} role ID"); |                 if (roleId <= 0) throw new Exception($"could not find {roleName} role ID"); | ||||||
|  |  | ||||||
|             subRoles = await approvalService.GetSubRolesForSubRoleName(subRoleName, roleId); |                 subRoles = await approvalService.GetSubRolesForSubRoleName(subRoleName, roleId); | ||||||
|  |  | ||||||
|             foreach (SubRole subRole in subRoles) { |                 foreach (SubRole subRole in subRoles) { | ||||||
|                 IEnumerable<User> subRoleMembers = await approvalService.GetApprovalGroupMembers(subRole.SubRoleID); |                     IEnumerable<User> subRoleMembers = await approvalService.GetApprovalGroupMembers(subRole.SubRoleID); | ||||||
|                 foreach (User user in subRoleMembers) qualityApproverUserIds.Add(user.UserID); |                     foreach (User user in subRoleMembers) qualityApproverUserIds.Add(user.UserID); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 cache.Set("qualityApproverUserIds", qualityApproverUserIds); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             return qualityApproverUserIds; |             return qualityApproverUserIds; | ||||||
| @ -445,9 +500,7 @@ public partial class PCRBSingle { | |||||||
|                 Approval? latestQaPreApproval = currentStageApprovals |                 Approval? latestQaPreApproval = currentStageApprovals | ||||||
|                                             .Where(a => a.SubRoleCategoryItem.Equals("QA Pre Approver")) |                                             .Where(a => a.SubRoleCategoryItem.Equals("QA Pre Approver")) | ||||||
|                                             .OrderByDescending(a => a.AssignedDate) |                                             .OrderByDescending(a => a.AssignedDate) | ||||||
|                                             .FirstOrDefault(); |                                             .FirstOrDefault() ?? throw new Exception("QA pre approval not found"); | ||||||
|  |  | ||||||
|                 if (latestQaPreApproval is null) throw new Exception("QA pre approval not found"); |  | ||||||
|  |  | ||||||
|                 bool qaPreApprovalDenied = latestQaPreApproval.ItemStatus == -1; |                 bool qaPreApprovalDenied = latestQaPreApproval.ItemStatus == -1; | ||||||
|                 if (qaPreApprovalDenied && currentStageUnsubmittedApprovalCount >= 3) { |                 if (qaPreApprovalDenied && currentStageUnsubmittedApprovalCount >= 3) { | ||||||
| @ -493,9 +546,8 @@ public partial class PCRBSingle { | |||||||
|                     await Task.WhenAll(createCopiedApprovalsTasks); |                     await Task.WhenAll(createCopiedApprovalsTasks); | ||||||
|                 } else { |                 } else { | ||||||
|                     Approval? unassignedQaPreApproval = currentStageApprovals.Where(a => a.SubRoleCategoryItem.Equals("QA Pre Approver") && |                     Approval? unassignedQaPreApproval = currentStageApprovals.Where(a => a.SubRoleCategoryItem.Equals("QA Pre Approver") && | ||||||
|                                                                               a.AssignedDate <= DateTimeUtilities.MIN_DT).FirstOrDefault(); |                                                                               a.AssignedDate <= DateTimeUtilities.MIN_DT).FirstOrDefault() ??  | ||||||
|  |                                                                               throw new Exception("unassigned QA pre approval not found"); | ||||||
|                     if (unassignedQaPreApproval is null) throw new Exception("unassigned QA pre approval not found"); |  | ||||||
|  |  | ||||||
|                     unassignedQaPreApproval.AssignedDate = DateTime.Now; |                     unassignedQaPreApproval.AssignedDate = DateTime.Now; | ||||||
|                     await approvalService.UpdateApproval(unassignedQaPreApproval); |                     await approvalService.UpdateApproval(unassignedQaPreApproval); | ||||||
| @ -505,7 +557,7 @@ public partial class PCRBSingle { | |||||||
|  |  | ||||||
|                 await pcrbService.NotifyNewApprovals(pcrb); |                 await pcrbService.NotifyNewApprovals(pcrb); | ||||||
|  |  | ||||||
|                 if (pcrb.CurrentStep == 1) { |                 if (pcrb.CurrentStep == (int)PCRB.StagesEnum.PCR1) { | ||||||
|                     pcrb.InsertTimeStamp = DateTime.Now; |                     pcrb.InsertTimeStamp = DateTime.Now; | ||||||
|                     await pcrbService.UpdatePCRB(pcrb); |                     await pcrbService.UpdatePCRB(pcrb); | ||||||
|  |  | ||||||
| @ -525,23 +577,6 @@ public partial class PCRBSingle { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private async Task SetUserForApproval(Approval approval, User user) { |  | ||||||
|         if (approval is null) throw new ArgumentNullException("approval cannot be null"); |  | ||||||
|         if (user is null) throw new ArgumentNullException("user cannot be null"); |  | ||||||
|  |  | ||||||
|         if (approval.CompletedDate < DateTimeUtilities.MAX_DT || approval.ItemStatus != 0) |  | ||||||
|             throw new ArgumentException("cannot reassign a complete approval"); |  | ||||||
|  |  | ||||||
|         approval.UserID = user.UserID; |  | ||||||
|         approval.User = user; |  | ||||||
|         approval.NotifyDate = DateTimeUtilities.MIN_DT; |  | ||||||
|  |  | ||||||
|         await approvalService.UpdateApproval(approval); |  | ||||||
|         await approvalService.GetApprovalsForIssueId(approval.IssueID, true); |  | ||||||
|  |  | ||||||
|         await pcrbService.NotifyNewApprovals(pcrb); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private async Task ApprovePCR(int step) { |     private async Task ApprovePCR(int step) { | ||||||
|         if (!processing) { |         if (!processing) { | ||||||
|             try { |             try { | ||||||
| @ -1136,4 +1171,328 @@ public partial class PCRBSingle { | |||||||
|         if (itemStatus > 0) return "Approved"; |         if (itemStatus > 0) return "Approved"; | ||||||
|         return "Pending"; |         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<Comments> parameters = new DialogParameters<Comments> { { x => x.comments, comments } }; | ||||||
|  |                 var dialog = await dialogService.ShowAsync<Comments>($"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<Approval> 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<SubRole> allSubRoles = new(); | ||||||
|  |  | ||||||
|  |                     int roleId = await approvalService.GetRoleIdForRoleName("Module Manager"); | ||||||
|  |  | ||||||
|  |                     if (roleId <= 0) throw new Exception($"could not find Module Manager role ID"); | ||||||
|  |  | ||||||
|  |                     List<SubRole> 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<SubRole> qaFinalApprovalSubRoles =  | ||||||
|  |                         (await approvalService.GetSubRolesForSubRoleName("QA_FINAL_APPROVAL", roleId)).ToList(); | ||||||
|  |  | ||||||
|  |                     foreach (SubRole subRole in qaFinalApprovalSubRoles) | ||||||
|  |                         allSubRoles.Add(subRole); | ||||||
|  |  | ||||||
|  |                     foreach (SubRole subRole in allSubRoles) { | ||||||
|  |                         IEnumerable<User> 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<Approval> 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<Comments> parameters = new DialogParameters<Comments> { { x => x.comments, comments } }; | ||||||
|  |                 var dialog = await dialogService.ShowAsync<Comments>($"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<Approval> 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(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -13,6 +13,7 @@ public interface IApprovalService { | |||||||
|     Task<IEnumerable<User>> GetApprovalGroupMembers(int subRoleId); |     Task<IEnumerable<User>> GetApprovalGroupMembers(int subRoleId); | ||||||
|     Task CreateApproval(Approval approval); |     Task CreateApproval(Approval approval); | ||||||
|     Task UpdateApproval(Approval approval); |     Task UpdateApproval(Approval approval); | ||||||
|  |     Task DeleteApproval(int approvalID); | ||||||
|     Task Approve(Approval approval); |     Task Approve(Approval approval); | ||||||
|     Task Deny(Approval approval); |     Task Deny(Approval approval); | ||||||
|     Task<IEnumerable<Approval>> GetApprovalsForIssueId(int issueId, bool bypassCache); |     Task<IEnumerable<Approval>> GetApprovalsForIssueId(int issueId, bool bypassCache); | ||||||
| @ -156,6 +157,20 @@ public class ApprovalService : IApprovalService { | |||||||
|         await GetApprovalsForUserId(approval.UserID, true); |         await GetApprovalsForUserId(approval.UserID, true); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public async Task DeleteApproval(int approvalID) { | ||||||
|  |         if (approvalID <= 0) throw new ArgumentException("Invalid approval ID"); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||||
|  |  | ||||||
|  |         HttpRequestMessage requestMessage = new(HttpMethod.Delete, $"approval?approvalID={approvalID}"); | ||||||
|  |  | ||||||
|  |         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||||
|  |  | ||||||
|  |         if (!responseMessage.IsSuccessStatusCode) { | ||||||
|  |             throw new Exception($"Unable to delete approval, because {responseMessage.ReasonPhrase}"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public async Task Approve(Approval approval) { |     public async Task Approve(Approval approval) { | ||||||
|         if (approval is null) throw new ArgumentNullException("approval cannot be null"); |         if (approval is null) throw new ArgumentNullException("approval cannot be null"); | ||||||
|  |  | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
|  |  | ||||||
| using MesaFabApproval.Shared.Models; | using MesaFabApproval.Shared.Models; | ||||||
|  |  | ||||||
|  | using Microsoft.AspNetCore.Components; | ||||||
| using Microsoft.AspNetCore.Components.Authorization; | using Microsoft.AspNetCore.Components.Authorization; | ||||||
|  |  | ||||||
| using MudBlazor; | using MudBlazor; | ||||||
| @ -12,18 +13,22 @@ public class MesaFabApprovalAuthStateProvider : AuthenticationStateProvider, IDi | |||||||
|     private readonly IAuthenticationService _authService; |     private readonly IAuthenticationService _authService; | ||||||
|     private readonly IUserService _userService; |     private readonly IUserService _userService; | ||||||
|     private readonly ISnackbar _snackbar; |     private readonly ISnackbar _snackbar; | ||||||
|  |     private readonly NavigationManager _navigationManager; | ||||||
|  |  | ||||||
|     public User? CurrentUser { get; private set; } |     public User? CurrentUser { get; private set; } | ||||||
|  |  | ||||||
|     public MesaFabApprovalAuthStateProvider(IAuthenticationService authService, |     public MesaFabApprovalAuthStateProvider(IAuthenticationService authService, | ||||||
|                                             ISnackbar snackbar, |                                             ISnackbar snackbar, | ||||||
|                                             IUserService userService) { |                                             IUserService userService, | ||||||
|  |                                             NavigationManager navigationManager) { | ||||||
|         _authService = authService ?? |         _authService = authService ?? | ||||||
|             throw new ArgumentNullException("IAuthenticationService not injected"); |             throw new ArgumentNullException("IAuthenticationService not injected"); | ||||||
|         _snackbar = snackbar ?? |         _snackbar = snackbar ?? | ||||||
|             throw new ArgumentNullException("ISnackbar not injected"); |             throw new ArgumentNullException("ISnackbar not injected"); | ||||||
|         _userService = userService ?? |         _userService = userService ?? | ||||||
|             throw new ArgumentNullException("IUserService not injected"); |             throw new ArgumentNullException("IUserService not injected"); | ||||||
|  |         _navigationManager = navigationManager ?? | ||||||
|  |             throw new ArgumentNullException("NavigationManager not injected"); | ||||||
|         AuthenticationStateChanged += OnAuthenticationStateChangedAsync; |         AuthenticationStateChanged += OnAuthenticationStateChangedAsync; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | |||||||
| @ -36,6 +36,7 @@ public interface IPCRBService { | |||||||
|     Task UpdatePCR3Document(PCR3Document document); |     Task UpdatePCR3Document(PCR3Document document); | ||||||
|     Task<IEnumerable<PCR3Document>> GetPCR3DocumentsForPlanNumber(int planNumber, bool bypassCache); |     Task<IEnumerable<PCR3Document>> GetPCR3DocumentsForPlanNumber(int planNumber, bool bypassCache); | ||||||
|     Task NotifyNewApprovals(PCRB pcrb); |     Task NotifyNewApprovals(PCRB pcrb); | ||||||
|  |     Task NotifyApprover(PCRBNotification notification); | ||||||
|     Task NotifyApprovers(PCRBNotification notification); |     Task NotifyApprovers(PCRBNotification notification); | ||||||
|     Task NotifyOriginator(PCRBNotification notification); |     Task NotifyOriginator(PCRBNotification notification); | ||||||
|     Task NotifyResponsiblePerson(PCRBActionItemNotification notification); |     Task NotifyResponsiblePerson(PCRBActionItemNotification notification); | ||||||
| @ -43,6 +44,10 @@ public interface IPCRBService { | |||||||
|     Task<IEnumerable<PCRBFollowUp>> GetFollowUpsByPlanNumber(int planNumber, bool bypassCache); |     Task<IEnumerable<PCRBFollowUp>> GetFollowUpsByPlanNumber(int planNumber, bool bypassCache); | ||||||
|     Task UpdateFollowUp(PCRBFollowUp followUp); |     Task UpdateFollowUp(PCRBFollowUp followUp); | ||||||
|     Task DeleteFollowUp(int id); |     Task DeleteFollowUp(int id); | ||||||
|  |     Task CreateFollowUpComment(PCRBFollowUpComment comment); | ||||||
|  |     Task<IEnumerable<PCRBFollowUpComment>> GetFollowUpCommentsByPlanNumber(int planNumber, bool bypassCache); | ||||||
|  |     Task UpdateFollowUpComment(PCRBFollowUpComment comment); | ||||||
|  |     Task DeleteFollowUpComment(int id); | ||||||
| } | } | ||||||
|  |  | ||||||
| public class PCRBService : IPCRBService { | public class PCRBService : IPCRBService { | ||||||
| @ -712,6 +717,26 @@ public class PCRBService : IPCRBService { | |||||||
|             throw new Exception($"Unable to notify new PCRB approvers, because {responseMessage.ReasonPhrase}"); |             throw new Exception($"Unable to notify new PCRB approvers, because {responseMessage.ReasonPhrase}"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public async Task NotifyApprover(PCRBNotification notification) { | ||||||
|  |         if (notification is null) throw new ArgumentNullException("notification cannot be null"); | ||||||
|  |         if (notification.PCRB is null) throw new ArgumentNullException("PCRB cannot be null"); | ||||||
|  |         if (notification.Approval is null) throw new ArgumentNullException("approval cannot be null"); | ||||||
|  |         if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty"); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||||
|  |  | ||||||
|  |         HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/notify/approver") { | ||||||
|  |             Content = new StringContent(JsonSerializer.Serialize(notification), | ||||||
|  |                                                     Encoding.UTF8, | ||||||
|  |                                                     "application/json") | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||||
|  |  | ||||||
|  |         if (!responseMessage.IsSuccessStatusCode) | ||||||
|  |             throw new Exception($"Unable to notify PCRB approver, because {responseMessage.ReasonPhrase}"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public async Task NotifyApprovers(PCRBNotification notification) { |     public async Task NotifyApprovers(PCRBNotification notification) { | ||||||
|         if (notification is null) throw new ArgumentNullException("notification cannot be null"); |         if (notification is null) throw new ArgumentNullException("notification cannot be null"); | ||||||
|         if (notification.PCRB is null) throw new ArgumentNullException("PCRB cannot be null"); |         if (notification.PCRB is null) throw new ArgumentNullException("PCRB cannot be null"); | ||||||
| @ -812,7 +837,7 @@ public class PCRBService : IPCRBService { | |||||||
|                     new List<PCRBFollowUp>(); |                     new List<PCRBFollowUp>(); | ||||||
|  |  | ||||||
|                 if (followUps.Count() > 0) |                 if (followUps.Count() > 0) | ||||||
|                     _cache.Set($"pcrbFollowUps{planNumber}", followUps, DateTimeOffset.Now.AddMinutes(5)); |                     _cache.Set($"pcrbFollowUps{planNumber}", followUps, DateTimeOffset.Now.AddHours(1)); | ||||||
|             } else { |             } else { | ||||||
|                 throw new Exception(responseMessage.ReasonPhrase); |                 throw new Exception(responseMessage.ReasonPhrase); | ||||||
|             } |             } | ||||||
| @ -836,6 +861,8 @@ public class PCRBService : IPCRBService { | |||||||
|  |  | ||||||
|         if (!responseMessage.IsSuccessStatusCode) |         if (!responseMessage.IsSuccessStatusCode) | ||||||
|             throw new Exception(responseMessage.ReasonPhrase); |             throw new Exception(responseMessage.ReasonPhrase); | ||||||
|  |  | ||||||
|  |         await GetFollowUpsByPlanNumber(followUp.PlanNumber, true); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public async Task DeleteFollowUp(int id) { |     public async Task DeleteFollowUp(int id) { | ||||||
| @ -849,4 +876,93 @@ public class PCRBService : IPCRBService { | |||||||
|  |  | ||||||
|         if (!responseMessage.IsSuccessStatusCode) throw new Exception(responseMessage.ReasonPhrase); |         if (!responseMessage.IsSuccessStatusCode) throw new Exception(responseMessage.ReasonPhrase); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public async Task CreateFollowUpComment(PCRBFollowUpComment comment) { | ||||||
|  |         if (comment is null) throw new ArgumentNullException("comment up cannot be null"); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||||
|  |  | ||||||
|  |         HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/followUpComment") { | ||||||
|  |             Content = new StringContent(JsonSerializer.Serialize(comment), | ||||||
|  |                                                Encoding.UTF8, | ||||||
|  |                                                "application/json") | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||||
|  |  | ||||||
|  |         if (!responseMessage.IsSuccessStatusCode) | ||||||
|  |             throw new Exception(responseMessage.ReasonPhrase); | ||||||
|  |  | ||||||
|  |         await GetFollowUpCommentsByPlanNumber(comment.PlanNumber, true); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public async Task<IEnumerable<PCRBFollowUpComment>> GetFollowUpCommentsByPlanNumber(int planNumber, bool bypassCache) { | ||||||
|  |         if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#"); | ||||||
|  |  | ||||||
|  |         IEnumerable<PCRBFollowUpComment>? comments = null; | ||||||
|  |         if (!bypassCache) | ||||||
|  |             comments = _cache.Get<IEnumerable<PCRBFollowUpComment>>($"pcrbFollowUpComments{planNumber}"); | ||||||
|  |  | ||||||
|  |         if (comments is null) { | ||||||
|  |             HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||||
|  |  | ||||||
|  |             HttpRequestMessage requestMessage =  | ||||||
|  |                 new(HttpMethod.Get, $"pcrb/followUpComments?planNumber={planNumber}&bypassCache={bypassCache}"); | ||||||
|  |  | ||||||
|  |             HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||||
|  |  | ||||||
|  |             if (responseMessage.IsSuccessStatusCode) { | ||||||
|  |                 string responseContent = await responseMessage.Content.ReadAsStringAsync(); | ||||||
|  |  | ||||||
|  |                 JsonSerializerOptions jsonSerializerOptions = new() { | ||||||
|  |                     PropertyNameCaseInsensitive = true | ||||||
|  |                 }; | ||||||
|  |  | ||||||
|  |                 comments = JsonSerializer.Deserialize<IEnumerable<PCRBFollowUpComment>>(responseContent, jsonSerializerOptions) ?? | ||||||
|  |                     new List<PCRBFollowUpComment>(); | ||||||
|  |  | ||||||
|  |                 if (comments.Count() > 0) { | ||||||
|  |                     foreach (PCRBFollowUpComment comment in comments) | ||||||
|  |                         comment.User = await _userService.GetUserByUserId(comment.UserID); | ||||||
|  |  | ||||||
|  |                     _cache.Set($"pcrbFollowUpComments{planNumber}", comments, DateTimeOffset.Now.AddHours(1)); | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 throw new Exception(responseMessage.ReasonPhrase); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return comments; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public async Task UpdateFollowUpComment(PCRBFollowUpComment comment) { | ||||||
|  |         if (comment is null) throw new ArgumentNullException("comment up cannot be null"); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||||
|  |  | ||||||
|  |         HttpRequestMessage requestMessage = new(HttpMethod.Put, $"pcrb/followUpComment") { | ||||||
|  |             Content = new StringContent(JsonSerializer.Serialize(comment), | ||||||
|  |                                                    Encoding.UTF8, | ||||||
|  |                                                    "application/json") | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||||
|  |  | ||||||
|  |         if (!responseMessage.IsSuccessStatusCode) | ||||||
|  |             throw new Exception(responseMessage.ReasonPhrase); | ||||||
|  |  | ||||||
|  |         await GetFollowUpCommentsByPlanNumber(comment.PlanNumber, true); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public async Task DeleteFollowUpComment(int id) { | ||||||
|  |         if (id <= 0) throw new ArgumentException($"{id} is not a valid PCRB follow up comment ID"); | ||||||
|  |  | ||||||
|  |         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||||
|  |  | ||||||
|  |         HttpRequestMessage requestMessage = new(HttpMethod.Delete, $"pcrb/followUpComment?id={id}"); | ||||||
|  |  | ||||||
|  |         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||||
|  |  | ||||||
|  |         if (!responseMessage.IsSuccessStatusCode) throw new Exception(responseMessage.ReasonPhrase); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -111,7 +111,7 @@ public class ApiHttpClientHandler : DelegatingHandler { | |||||||
|                     string? redirectUrl = _cache.Get<string>("redirectUrl"); |                     string? redirectUrl = _cache.Get<string>("redirectUrl"); | ||||||
|  |  | ||||||
|                     if (!string.IsNullOrWhiteSpace(redirectUrl)) { |                     if (!string.IsNullOrWhiteSpace(redirectUrl)) { | ||||||
|                         _navigationManager.NavigateTo($"login/{redirectUrl}"); |                         _navigationManager.NavigateTo($"login?redirectPath={redirectUrl}"); | ||||||
|                     } else { |                     } else { | ||||||
|                         _navigationManager.NavigateTo("login"); |                         _navigationManager.NavigateTo("login"); | ||||||
|                     } |                     } | ||||||
|  | |||||||
| @ -8,9 +8,21 @@ public class PCRB { | |||||||
|         "PCR1", |         "PCR1", | ||||||
|         "PCR2", |         "PCR2", | ||||||
|         "PCR3", |         "PCR3", | ||||||
|         "Complete" |         "Complete", | ||||||
|  |         "Follow Up", | ||||||
|  |         "Closed" | ||||||
|     ]; |     ]; | ||||||
|  |  | ||||||
|  |     public enum StagesEnum { | ||||||
|  |         Draft = 0, | ||||||
|  |         PCR1 = 1, | ||||||
|  |         PCR2 = 2, | ||||||
|  |         PCR3 = 3, | ||||||
|  |         Complete = 4, | ||||||
|  |         FollowUp = 5, | ||||||
|  |         Closed = 6 | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public int PlanNumber { get; set; } |     public int PlanNumber { get; set; } | ||||||
|     public int OwnerID { get; set; } |     public int OwnerID { get; set; } | ||||||
|     public string OwnerName { get; set; } = ""; |     public string OwnerName { get; set; } = ""; | ||||||
| @ -24,4 +36,5 @@ public class PCRB { | |||||||
|     public DateTime LastUpdateDate { get; set; } = DateTimeUtilities.MIN_DT; |     public DateTime LastUpdateDate { get; set; } = DateTimeUtilities.MIN_DT; | ||||||
|     public DateTime ClosedDate { get; set; } = DateTimeUtilities.MAX_DT; |     public DateTime ClosedDate { get; set; } = DateTimeUtilities.MAX_DT; | ||||||
|     public string Type { get; set; } = ""; |     public string Type { get; set; } = ""; | ||||||
|  |     public IEnumerable<PCRBFollowUp> FollowUps { get; set; } = new List<PCRBFollowUp>(); | ||||||
| } | } | ||||||
| @ -9,6 +9,7 @@ public class PCRBFollowUp { | |||||||
|     public required DateTime FollowUpDate { get; set; } |     public required DateTime FollowUpDate { get; set; } | ||||||
|     public bool IsComplete { get; set; } = false; |     public bool IsComplete { get; set; } = false; | ||||||
|     public bool IsDeleted { get; set; } = false; |     public bool IsDeleted { get; set; } = false; | ||||||
|  |     public bool IsPendingApproval { get; set; } = false; | ||||||
|     public DateTime CompletedDate { get; set; } = DateTimeUtilities.MAX_DT; |     public DateTime CompletedDate { get; set; } = DateTimeUtilities.MAX_DT; | ||||||
|     public string Comments { get; set; } = string.Empty; |     public DateTime UpdateDate { get; set; } = DateTime.Now; | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										13
									
								
								MesaFabApproval.Shared/Models/PCRBFollowUpComment.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								MesaFabApproval.Shared/Models/PCRBFollowUpComment.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | using MesaFabApproval.Shared.Utilities; | ||||||
|  |  | ||||||
|  | namespace MesaFabApproval.Shared.Models; | ||||||
|  |  | ||||||
|  | public class PCRBFollowUpComment { | ||||||
|  |     public int ID { get; set; } | ||||||
|  |     public required int PlanNumber { get; set; } | ||||||
|  |     public required int FollowUpID { get; set; } | ||||||
|  |     public DateTime CommentDate { get; set; } = DateTime.Now; | ||||||
|  |     public required int UserID { get; set; } | ||||||
|  |     public User? User { get; set; } | ||||||
|  |     public string Comment { get; set; } = string.Empty; | ||||||
|  | } | ||||||
| @ -2,5 +2,8 @@ | |||||||
|  |  | ||||||
| public class PCRBNotification { | public class PCRBNotification { | ||||||
|     public required string Message { get; set; } |     public required string Message { get; set; } | ||||||
|  |     public string? Subject { get; set; } | ||||||
|     public required PCRB PCRB { get; set; } |     public required PCRB PCRB { get; set; } | ||||||
|  |     public Approval? Approval { get; set; } | ||||||
|  |     public bool NotifyQaPreApprover { get; set; } = false; | ||||||
| } | } | ||||||
		Reference in New Issue
	
	Block a user
	