PCRB webassembly

This commit is contained in:
Chase Tucker 2024-05-13 14:33:27 -07:00
parent 9b7e3ef897
commit 89790f4fc1
50 changed files with 5466 additions and 677 deletions

View File

@ -1,10 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.Http;
namespace Fab2ApprovalSystem
{
namespace Fab2ApprovalSystem {
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)

View File

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
@ -15,8 +14,8 @@ using Fab2ApprovalSystem.DMO;
using Microsoft.AspNet.Identity.Owin;
using System.Net.Http;
using Newtonsoft.Json;
using System.Net.Http.Headers;
using System.Text;
using System.Net;
namespace Fab2ApprovalSystem.Controllers {
[Authorize]
@ -129,6 +128,86 @@ namespace Fab2ApprovalSystem.Controllers {
}
[HttpPost]
[AllowAnonymous]
public async Task<HttpResponseMessage> ExternalAuthSetup(AuthAttempt authAttempt) {
try {
bool isLoginValid;
HttpClient httpClient = HttpClientFactory.Create();
httpClient.BaseAddress = new Uri(_apiBaseUrl);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "auth/refresh");
request.Content = new StringContent(JsonConvert.SerializeObject(authAttempt),
Encoding.UTF8,
"application/json");
HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(request);
if (!httpResponseMessage.IsSuccessStatusCode)
throw new Exception($"The authentication API failed, because {httpResponseMessage.ReasonPhrase}");
string responseContent = await httpResponseMessage.Content.ReadAsStringAsync();
LoginResult loginResult = JsonConvert.DeserializeObject<LoginResult>(responseContent);
#if(DEBUG)
isLoginValid = true;
#endif
#if (!DEBUG)
bool isIFX = false;
//domainProvider = Membership.Providers["NA_ADMembershipProvider"];
//isLoginValid = domainProvider.ValidateUser(model.LoginID, model.Password);
if (GlobalVars.DBConnection.ToUpper() == "TEST" || GlobalVars.DBConnection.ToUpper() == "QUALITY") {
isLoginValid = true;
} else {
isLoginValid = loginResult.IsAuthenticated;
if (isLoginValid) isIFX = true;
}
#endif
if (isLoginValid) {
UserAccountDMO userDMO = new UserAccountDMO();
LoginModel user = userDMO.GetUser(authAttempt.LoginID);
if (user != null) {
Session["JWT"] = loginResult.AuthTokens.JwtToken;
Session["RefreshToken"] = loginResult.AuthTokens.RefreshToken;
Session[GlobalVars.SESSION_USERID] = user.UserID;
Session[GlobalVars.SESSION_USERNAME] = user.FullName;
Session[GlobalVars.IS_ADMIN] = user.IsAdmin;
Session[GlobalVars.IS_MANAGER] = user.IsManager;
Session[GlobalVars.OOO] = user.OOO;
Session[GlobalVars.CAN_CREATE_PARTS_REQUEST] = user.IsAdmin || PartsRequestController.CanCreatePartsRequest(user.UserID);
FormsAuthentication.SetAuthCookie(user.LoginID, true);
return new HttpResponseMessage(HttpStatusCode.OK);
} else {
ModelState.AddModelError("", "The user name does not exist in the DB. Please contact the System Admin");
return new HttpResponseMessage(HttpStatusCode.NotFound);
}
} else {
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
}
} catch (Exception ex) {
Functions.WriteEvent(@User.Identity.Name + " " + ex.InnerException, System.Diagnostics.EventLogEntryType.Error);
EventLogDMO.Add(new WinEventLog() { IssueID = 99999, UserID = @User.Identity.Name, DocumentType = "Login", OperationType = "Error", Comments = "Reject - " + ex.Message });
ModelState.AddModelError("", ex.Message);
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
}
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register() {

View File

@ -102,53 +102,15 @@ namespace Fab2ApprovalSystem.Controllers
// GET: /MRB/Edit/5
public ActionResult Edit(int issueID)
{
MRB mrb = new MRB();
int isITARCompliant = 1;
ViewBag.Status = "Pending";
ViewBag.IsApprover = "false";
ViewBag.IsCloser = "false";
string jwt = Session["JWT"].ToString();
string encodedJwt = System.Net.WebUtility.UrlEncode(jwt);
string refreshToken = Session["RefreshToken"].ToString();
string encodedRefreshToken = System.Net.WebUtility.UrlEncode(refreshToken);
string wasmClientUrl = Environment.GetEnvironmentVariable("FabApprovalWasmClientUrl") ??
"https://localhost:7255";
string mrbUrl = $"{wasmClientUrl}/redirect?jwt={encodedJwt}&refreshToken={encodedRefreshToken}&redirectPath=/mrb/{issueID}";
//ViewBag.IsApproved = "false";
//ViewBag.IsClosed = "false";
PopulateCloseToQDB();
mrb = mrbDMO.GetMRBItem(issueID, out isITARCompliant, (int)Session[GlobalVars.SESSION_USERID]);
ViewBag.UserList = mrbDMO.GetUserList();
if (isITARCompliant == 0) // not ITAR Compliant
{
return View("UnAuthorizedAccess");
}
else
{
if (mrb.ApprovalStatus == (int)GlobalVars.ApprovalOption.Approved)
{
//ViewBag.IsApproved = "true";
ViewBag.Status = "Approved";
}
else if (mrb.ApprovalStatus == (int)GlobalVars.ApprovalOption.Closed)
{
ViewBag.Status = "Closed";
//ViewBag.IsClosed = "true";
}
List<ApproversListViewModel> userList = MiscDMO.GetApproversListByDocument(issueID, mrb.CurrentStep, (int)GlobalVars.DocumentType.MRB);
ApproversListViewModel appUser = userList.Find(delegate (ApproversListViewModel al) { return al.UserID == (int)Session[GlobalVars.SESSION_USERID]; });
if (appUser != null)
{
ViewBag.IsApprover = "true";
}
}
// can edit
ViewBag.Owners = MiscDMO.GetUserList();
ViewBag.Modules = mrbDMO.GetModules();
//ViewBag.Dispositions = mrbDMO.GetDispositions();
ViewBag.RiskAssessments = mrbDMO.GetRiskAssessments();
ViewBag.PartGroups = mrbDMO.GetPartGroups();
ViewBag.DispoTypes = mrbDMO.GetDispositions(issueID).Select(d => new { d.DispositionType });
return View(mrb);
return Redirect(mrbUrl);
}
//
@ -178,39 +140,15 @@ namespace Fab2ApprovalSystem.Controllers
/// <returns></returns>
public ActionResult ReadOnly(int issueID)
{
MRB mrb = new MRB();
int isITARCompliant = 1;
string jwt = Session["JWT"].ToString();
string encodedJwt = System.Net.WebUtility.UrlEncode(jwt);
string refreshToken = Session["RefreshToken"].ToString();
string encodedRefreshToken = System.Net.WebUtility.UrlEncode(refreshToken);
string wasmClientUrl = Environment.GetEnvironmentVariable("FabApprovalWasmClientUrl") ??
"https://localhost:7255";
string mrbUrl = $"{wasmClientUrl}/redirect?jwt={encodedJwt}&refreshToken={encodedRefreshToken}&redirectPath=/mrb/{issueID}";
try
{
if (isITARCompliant == 0) // not ITAR Compliant
{
return View("UnAuthorizedAccess");
}
else
{
mrb = mrbDMO.GetMRBItem(issueID, out isITARCompliant, (int)Session[GlobalVars.SESSION_USERID]);
ViewBag.Owners = MiscDMO.GetUserList();
ViewBag.Modules = mrbDMO.GetModules();
//ViewBag.Dispositions = mrbDMO.GetDispositions();
ViewBag.RiskAssessments = mrbDMO.GetRiskAssessments();
ViewBag.PartGroups = mrbDMO.GetPartGroups();
ViewBag.DispoTypes = mrbDMO.GetDispositions(issueID).Select(d => new { d.DispositionType });
}
return View(mrb);
}
catch (Exception e)
{
string exceptionString = e.Message.ToString().Trim().Length > 500 ? "IssueID=" + issueID.ToString() + " " + e.Message.ToString().Substring(0, 250) : e.Message.ToString();
Functions.WriteEvent(@User.Identity.Name + "\r\n ReadOnly Disposition\r\n" + e.Message.ToString(), System.Diagnostics.EventLogEntryType.Error);
EventLogDMO.Add(new WinEventLog() { IssueID = issueID, UserID = @User.Identity.Name, DocumentType = "Lot Disposition", OperationType = "Error", Comments = exceptionString });
throw new Exception(e.Message);
}
return Redirect(mrbUrl);
}
//

View File

@ -14,6 +14,6 @@ namespace Fab2ApprovalSystem.ViewModels
public string Originator { get; set; }
public DateTime? AssignedDate { get; set; }
public DateTime? DueDate { get; set; }
public string pcrMesaID { get; set; }
public string pcrMesaID { get; set; } = string.Empty;
}
}

View File

@ -227,7 +227,7 @@
)
</div>
<div class="col-sm-6 col-sm-offset-4" style="color:red;">
ECN: Qualtiy and Dept. Specific<br />MRB: Quality, Production, Engineering, OPC<br />PCRB: Quality, Production, Engineering, Dept. Specific (scope of change)
ECN: Qualtiy and Dept. Specific<br />PCRB: Quality, Production, Engineering, Dept. Specific (scope of change)
</div>
</div>
<div class="form-group">

View File

@ -171,7 +171,7 @@
)
</div>
<div class="col-sm-6 col-sm-offset-4" style="color:red;">
ECN: Qualtiy and Dept. Specific<br />MRB: Quality, Production, Engineering, OPC<br />PCRB: Quality, Production, Engineering, Dept. Specific (scope of change)
ECN: Qualtiy and Dept. Specific<br />PCRB: Quality, Production, Engineering, Dept. Specific (scope of change)
</div>
</div>
<div class="form-group">

View File

@ -259,7 +259,7 @@
)
</div>
<div class="col-sm-6 col-sm-offset-4" style="color:red;">
ECN: Qualtiy and Dept. Specific<br />MRB: Quality, Production, Engineering, OPC<br />PCRB: Quality, Production, Engineering, Dept. Specific (scope of change)
ECN: Qualtiy and Dept. Specific<br />PCRB: Quality, Production, Engineering, Dept. Specific (scope of change)
</div>
</div>
<div class="form-group">

View File

@ -92,11 +92,15 @@
"https://localhost:7255";
string mrbUrl = wasmClientUrl + "/redirect?jwt=" + encodedJwt + "&refreshToken=" + encodedRefreshToken + "&redirectPath=/mrb/new";
<li><a href="@mrbUrl">Create MRB</a></li>
@*string pcrbUrl = wasmClientUrl + "/redirect?jwt=" + encodedJwt + "&refreshToken=" + encodedRefreshToken + "&redirectPath=/pcrb/new";
<li><a href="@pcrbUrl">Create PCRB</a></li>*@
} else {
string wasmClientUrl = Environment.GetEnvironmentVariable("FabApprovalWasmClientUrl") ??
"https://localhost:7255";
string mrbUrl = wasmClientUrl + "/redirect?redirectPath=/mrb/new";
<li><a href="@mrbUrl">Create MRB</a></li>
@*string pcrbUrl = wasmClientUrl + "/redirect?redirectPath=/pcrb/new";
<li><a href="@pcrbUrl">Create PCRB</a></li>*@
}
@*<li><a href=@Url.Action("CreateWorkRequest", "LotTraveler")>Create Special Work Request</a></li>*@
@*<li><a href=@Url.Action("Create", "ChangeControl")>Create PCR</a></li>*@
@ -148,6 +152,8 @@
"https://localhost:7255";
string mrbUrl = wasmClientUrl + "/redirect?jwt=" + encodedJwt + "&refreshToken=" + encodedRefreshToken + "&redirectPath=/mrb/all";
menu.Add().Text("MRB").Url(mrbUrl);
//string pcrbUrl = wasmClientUrl + "/redirect?jwt=" + encodedJwt + "&refreshToken=" + encodedRefreshToken + "&redirectPath=/pcrb/all";
//menu.Add().Text("PCRB").Url(pcrbUrl);
//menu.Add().Text("Special Work Requests").Action("SpecialWorkRequestList", "Home");
//menu.Add().Text("PCRB").Action("ChangeControlList", "Home");
//menu.Add().Text("MRB").Action("MRBList", "Home");

View File

@ -132,6 +132,11 @@
<requestLimits maxAllowedContentLength="1073741824" />
</requestFiltering>
</security>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
</system.webServer>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">

View File

@ -15,10 +15,14 @@ public class MRBController : ControllerBase {
private readonly IMRBService _mrbService;
private readonly IMonInWorkerClient _monInClient;
private readonly string _mrbAttachmentPath;
public MRBController(ILogger<MRBController> logger, IMRBService mrbService, IMonInWorkerClient monInClient) {
_logger = logger ?? throw new ArgumentNullException("ILogger not injected");
_mrbService = mrbService ?? throw new ArgumentNullException("IMRBService not injected");
_monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected");
_mrbAttachmentPath = Environment.GetEnvironmentVariable("FabApprovalMrbAttachmentPath") ??
throw new ArgumentNullException("FabApprovalMrbAttachmentPath environment variable not found");
}
[HttpPost]
@ -692,6 +696,64 @@ public class MRBController : ControllerBase {
}
}
[AllowAnonymous]
[HttpGet]
[Route("mrb/actions/csvFile")]
public async Task<IActionResult> GetMRBActionsCsvFile(int mrbNumber) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get MRB actions CSC file");
if (!(await _mrbService.MRBNumberIsValid(mrbNumber)))
throw new ArgumentException($"{mrbNumber} is not a valid MRB#");
string path = $"{_mrbAttachmentPath}\\{mrbNumber}\\mrb{mrbNumber}Actions.csv";
await _mrbService.ConvertActionsToCsvFile(mrbNumber, path);
byte[] fs = System.IO.File.ReadAllBytes(path);
const string defaultContentType = "application/octet-stream";
FileExtensionContentTypeProvider contentTypeProvider = new FileExtensionContentTypeProvider();
if (!contentTypeProvider.TryGetContentType(path, out string? contentType)) {
contentType = defaultContentType;
}
return new FileContentResult(fs, contentType) {
FileDownloadName = $"mrb{mrbNumber}Actions.csv"
};
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get MRB actions CSC file, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetMRBActionsCSVFile";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
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);
}
}
}
[HttpDelete]
[Route("mrb/attach")]
public async Task<IActionResult> DeleteMRBAttachment(MRBAttachment attachment) {

View File

@ -4,6 +4,7 @@ using MesaFabApproval.Shared.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
namespace MesaFabApproval.API.Controllers;
@ -261,4 +262,865 @@ public class PCRBController : ControllerBase {
}
}
}
[HttpPost]
[Route("pcrb/attachment")]
public async Task<IActionResult> UploadAttachment([FromForm] IFormFile file, [FromForm] PCRBAttachment attachment) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to upload PCRB attachment");
if (file is null) throw new ArgumentNullException("File cannot be null");
if (file.Length <= 0) throw new ArgumentException("File size cannot be zero");
if (attachment is null) throw new ArgumentNullException("Attachment cannot be null");
await _pcrbService.UploadAttachment(file, attachment);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot upload PCRB attachment, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "UploadPCRBAttachment";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
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);
}
}
}
[HttpGet]
[Route("pcrb/attachments")]
public async Task<IActionResult> GetAttachmentsByPlanNumber(int planNumber, bool bypassCache) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to get MRB attachments for MRB {planNumber}");
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#");
List<PCRBAttachment> attachments = (await _pcrbService.GetAttachmentsByPlanNumber(planNumber, bypassCache)).ToList();
return Ok(attachments);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get attachments for PCRB Plan# {planNumber}, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetPCRBAttachments";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
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);
}
}
}
[AllowAnonymous]
[HttpGet]
[Route("pcrb/attachmentFile")]
public async Task<IActionResult> GetMRBAttachmentFile(string path, string fileName) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to get PCRB attachment file");
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Path cannot be null or empty");
if (!System.IO.File.Exists(path)) throw new ArgumentException("No file exists at provided path");
if (string.IsNullOrWhiteSpace(fileName)) throw new ArgumentException("Filename cannot be null or empty");
byte[] fs = System.IO.File.ReadAllBytes(path);
const string defaultContentType = "application/octet-stream";
FileExtensionContentTypeProvider contentTypeProvider = new FileExtensionContentTypeProvider();
if (!contentTypeProvider.TryGetContentType(path, out string? contentType)) {
contentType = defaultContentType;
}
return new FileContentResult(fs, contentType) {
FileDownloadName = fileName
};
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get PCRB attachment file, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetPCRBAttachmentFile";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
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);
}
}
}
[HttpPut]
[Route("pcrb/attachment")]
public async Task<IActionResult> UpdateAttachment(PCRBAttachment attachment) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to update an attachment");
if (attachment is null) throw new ArgumentNullException("attachment cannot be null");
await _pcrbService.UpdateAttachment(attachment);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot update attachment, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "UpdatePCRBAttachment";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
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);
}
}
}
[HttpDelete]
[Route("pcrb/attachment")]
public async Task<IActionResult> DeleteAttachment(PCRBAttachment attachment) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to delete an attachment");
if (attachment is null) throw new ArgumentNullException("attachment cannot be null");
await _pcrbService.DeleteAttachment(attachment);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot delete attachment, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "DeletePCRBAttachment";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
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);
}
}
}
[HttpPost]
[Route("pcrb/actionItem")]
public async Task<IActionResult> CreateActionItem(PCRBActionItem actionItem) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to create PCRB action item");
if (actionItem is null) throw new ArgumentNullException("action item cannot be null");
await _pcrbService.CreateNewActionItem(actionItem);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot create PCRB action item, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "CreatePCRBActionItem";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
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);
}
}
}
[HttpPut]
[Route("pcrb/actionItem")]
public async Task<IActionResult> UpdateActionItem(PCRBActionItem actionItem) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to update an action item");
if (actionItem is null) throw new ArgumentNullException("action item cannot be null");
await _pcrbService.UpdateActionItem(actionItem);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot update action item, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "UpdatePCRBActionItem";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
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);
}
}
}
[HttpDelete]
[Route("pcrb/actionItem")]
public async Task<IActionResult> DeleteActionItem(int id) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to delete an action item");
if (id <= 0) throw new ArgumentException($"{id} is not a valid PCRB action item ID");
await _pcrbService.DeleteActionItem(id);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot delete action item, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "DeletePCRBActionItem";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
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);
}
}
}
[HttpGet]
[Route("pcrb/actionItems")]
public async Task<IActionResult> GetActionItemsByPlanNumber(int planNumber, bool bypassCache) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to get PCRB action items for plan# {planNumber}");
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#");
List<PCRBActionItem> actionItems = (await _pcrbService.GetActionItemsForPlanNumber(planNumber, bypassCache)).ToList();
return Ok(actionItems);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get action items for plan# {planNumber}, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetPCRBActionItems";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
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);
}
}
}
[HttpPost]
[Route("pcrb/pcr3Document")]
public async Task<IActionResult> CreatePCR3Document(PCR3Document document) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to create PCR3 document");
if (document is null) throw new ArgumentNullException("document cannot be null");
await _pcrbService.CreatePCR3Document(document);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot create PCR3 document, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "CreatePCR3Document";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
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);
}
}
}
[HttpPut]
[Route("pcrb/pcr3Document")]
public async Task<IActionResult> UpdatePCR3Document(PCR3Document document) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to update a PCR3 document");
if (document is null) throw new ArgumentNullException("document cannot be null");
await _pcrbService.UpdatePCR3Document(document);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot update PCR3 document, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "UpdatePCR3Document";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
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);
}
}
}
[HttpGet]
[Route("pcrb/pcr3Documents")]
public async Task<IActionResult> GetPCR3DocumentsForPlanNumber(int planNumber, bool bypassCache) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to get PCR3 documents for plan# {planNumber}");
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#");
List<PCR3Document> documents = (await _pcrbService.GetPCR3DocumentsForPlanNumber(planNumber, bypassCache)).ToList();
return Ok(documents);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get PCR3 documents for plan# {planNumber}, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetPCR3Documents";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
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);
}
}
}
[HttpPost]
[Route("pcrb/attendee")]
public async Task<IActionResult> CreateAttendee(PCRBAttendee attendee) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to create new attendee");
if (attendee is null) throw new ArgumentNullException("attendee item cannot be null");
await _pcrbService.CreateNewAttendee(attendee);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot create new attendee, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "CreatePCRBAttendee";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
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);
}
}
}
[HttpPut]
[Route("pcrb/attendee")]
public async Task<IActionResult> UpdateAttendee(PCRBAttendee attendee) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to update an attendee");
if (attendee is null) throw new ArgumentNullException("attendee cannot be null");
await _pcrbService.UpdateAttendee(attendee);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot update attendee, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "UpdatePCRBAttendee";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
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);
}
}
}
[HttpDelete]
[Route("pcrb/attendee")]
public async Task<IActionResult> DeleteAttendee(int id) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to delete an attendee");
if (id <= 0) throw new ArgumentException($"{id} is not a valid PCRB attendee ID");
await _pcrbService.DeleteAttendee(id);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot delete attendee, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "DeletePCRBAttendee";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
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);
}
}
}
[HttpGet]
[Route("pcrb/attendees")]
public async Task<IActionResult> GetAttendeesByPlanNumber(int planNumber, bool bypassCache) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation($"Attempting to get attendees for plan# {planNumber}");
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#");
List<PCRBAttendee> attendees = (await _pcrbService.GetAttendeesByPlanNumber(planNumber, bypassCache)).ToList();
return Ok(attendees);
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Cannot get attendees for plan# {planNumber}, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "GetPCRBAttendees";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
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);
}
}
}
[HttpPost]
[Route("pcrb/notify/new-approvals")]
public async Task<IActionResult> NotifyNewApprovals(PCRB pcrb) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to notify new approvers");
if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null");
await _pcrbService.NotifyNewApprovals(pcrb);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Unable to notify new approvers, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "NotifyNewPCRBApprovers";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
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);
}
}
}
[HttpPost]
[Route("pcrb/notify/approvers")]
public async Task<IActionResult> NotifyApprovers(PCRBNotification notification) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to notify approvers");
if (notification is null) throw new ArgumentNullException("notification cannot be null");
if (notification.PCRB is null) throw new ArgumentNullException("PCRB cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty");
await _pcrbService.NotifyApprovers(notification);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Unable to notify approvers, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "NotifyPCRBApprovers";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
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);
}
}
}
[HttpPost]
[Route("pcrb/notify/originator")]
public async Task<IActionResult> NotifyOriginator(PCRBNotification notification) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to notify originator");
if (notification is null) throw new ArgumentNullException("MRBNotification cannot be null");
if (notification.PCRB is null) throw new ArgumentNullException("PCRB cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("Message cannot be null or empty");
await _pcrbService.NotifyOriginator(notification);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Unable to notify originator, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "NotifyPCRBOriginator";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
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);
}
}
}
[HttpPost]
[Route("pcrb/notify/responsiblePerson")]
public async Task<IActionResult> NotifyResponsiblePerson(PCRBActionItemNotification notification) {
DateTime start = DateTime.Now;
bool isArgumentError = false;
bool isInternalError = false;
string errorMessage = "";
try {
_logger.LogInformation("Attempting to notify originator");
if (notification is null) throw new ArgumentNullException("MRBNotification cannot be null");
if (notification.PCRB is null) throw new ArgumentNullException("PCRB cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("Message cannot be null or empty");
await _pcrbService.NotifyResponsiblePerson(notification);
return Ok();
} catch (ArgumentException ex) {
isArgumentError = true;
errorMessage = ex.Message;
return BadRequest(errorMessage);
} catch (Exception ex) {
isInternalError = true;
errorMessage = $"Unable to notify responsible person, because {ex.Message}";
return Problem(errorMessage);
} finally {
string metricName = "NotifyPCRBResponsiblePerson";
DateTime end = DateTime.Now;
double millisecondsDiff = (end - start).TotalMilliseconds;
_monInClient.PostAverage(metricName + "Latency", millisecondsDiff);
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);
}
}
}
}

View File

@ -12,9 +12,9 @@
<PackageReference Include="dotenv.net" Version="3.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
<PackageReference Include="NLog" Version="5.3.3" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.12" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
<PackageReference Include="NLog" Version="5.2.8" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.8" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.0" />
<PackageReference Include="System.DirectoryServices.AccountManagement" Version="8.0.0" />
</ItemGroup>

View File

@ -42,7 +42,7 @@ public class ApprovalService : IApprovalService {
queryBuilder.Append("insert into Approval (IssueID, RoleName, SubRole, UserID, SubRoleID, ItemStatus, ");
queryBuilder.Append("AssignedDate, DocumentTypeID, DisplayDeniedDocument, Step, TaskID) ");
queryBuilder.Append($"values ({approval.IssueID}, '{approval.RoleName}', '{approval.SubRole}', {approval.UserID}, ");
queryBuilder.Append($"{approval.SubRoleID}, 0, '{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"{approval.SubRoleID}, 0, '{approval.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"3, 0, {approval.Step}, {approval.TaskID});");
int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString());
@ -145,7 +145,7 @@ public class ApprovalService : IApprovalService {
StringBuilder queryBuilder = new();
queryBuilder.Append("select src.SubRoleCategoryID, sr.SubRole as SubRoleName, src.SubRoleCategoryItem, sr.SubRoleID ");
queryBuilder.Append("from SubRole sr join SubRoleCategory src on sr.SubRoleCategoryID=src.SubRoleCategoryID ");
queryBuilder.Append($"where sr.RoleID={roleId} and sr.SubRole='{subRoleName}'");
queryBuilder.Append($"where sr.RoleID={roleId} and sr.SubRole='{subRoleName}' and sr.Inactive=0");
subRoles = (await _dalService.QueryAsync<SubRole>(queryBuilder.ToString())).ToList();
@ -182,7 +182,7 @@ public class ApprovalService : IApprovalService {
if (memberIds is null || memberIds.Count() <= 0)
throw new Exception($"No members found in sub role {subRoleId}");
_cache.Set($"approvalMemberIds{subRoleId}", memberIds, DateTimeOffset.Now.AddHours(1));
_cache.Set($"approvalMemberIds{subRoleId}", memberIds, DateTimeOffset.Now.AddMinutes(5));
}
members = new();
@ -194,7 +194,7 @@ public class ApprovalService : IApprovalService {
if (members.Count() <= 0) throw new Exception("No users found with IDs matching those found in SubRole");
_cache.Set($"approvalMembers{subRoleId}", members, DateTimeOffset.Now.AddHours(1));
_cache.Set($"approvalMembers{subRoleId}", members, DateTimeOffset.Now.AddMinutes(5));
}
return members;
@ -249,7 +249,7 @@ public class ApprovalService : IApprovalService {
queryBuilder.Append($"NotifyDate='{approval.NotifyDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"AssignedDate='{approval.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"CompletedDate='{approval.CompletedDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"Comments='{approval.Comments}', ");
queryBuilder.Append($"Comments='{approval.Comments.Replace("'", "''")}', ");
queryBuilder.Append($"TaskID={approval.TaskID} ");
queryBuilder.Append($"where ApprovalID={approval.ApprovalID};");

View File

@ -147,7 +147,7 @@ public class AuthenticationService : IAuthenticationService {
Audience = _jwtAudience,
Subject = identity,
NotBefore = DateTime.Now,
Expires = DateTime.Now.AddHours(2),
Expires = DateTime.Now.AddHours(8),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};

View File

@ -1,7 +1,9 @@
using System.Net;
using System.Data;
using System.Net;
using System.Net.Mail;
using System.Text;
using MesaFabApproval.API.Utilities;
using MesaFabApproval.Shared.Models;
using MesaFabApproval.Shared.Utilities;
@ -30,6 +32,7 @@ public interface IMRBService {
Task NotifyOriginator(MRBNotification notification);
Task NotifyQAPreApprover(MRBNotification notification);
Task DeleteMRB(int mrbNumber);
Task ConvertActionsToCsvFile(int mrbNumber, string path);
}
public class MRBService : IMRBService {
@ -130,9 +133,6 @@ public class MRBService : IMRBService {
_cache.Set("allMrbs", allMrbs, DateTimeOffset.Now.AddHours(1));
}
if (allMrbs is null || allMrbs.Count() == 0)
throw new Exception("No MRBs found");
return allMrbs;
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to get all MRBs. Exception: {ex.Message}");
@ -236,7 +236,7 @@ public class MRBService : IMRBService {
StringBuilder queryBuilder = new();
queryBuilder.Append($"update MRB set OriginatorID = {mrb.OriginatorID}, ");
queryBuilder.Append($"Title = '{mrb.Title}', ");
queryBuilder.Append($"Title = '{mrb.Title.Replace("'", "''")}', ");
if (mrb.SubmittedDate < DateTimeUtilities.MIN_DT)
mrb.SubmittedDate = DateTimeUtilities.MIN_DT;
queryBuilder.Append($"SubmittedDate = '{mrb.SubmittedDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
@ -250,19 +250,19 @@ public class MRBService : IMRBService {
if (mrb.ApprovalDate > DateTimeUtilities.MAX_DT)
mrb.ApprovalDate = DateTimeUtilities.MAX_DT;
queryBuilder.Append($"ApprovalDate = '{mrb.ApprovalDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"IssueDescription = '{mrb.IssueDescription}', ");
queryBuilder.Append($"IssueDescription = '{mrb.IssueDescription.Replace("'", "''")}', ");
queryBuilder.Append($"CustomerImpacted = {Convert.ToInt32(mrb.CustomerImpacted)}, ");
queryBuilder.Append($"Department = '{mrb.Department}', ");
queryBuilder.Append($"Process = '{mrb.Process}', ");
queryBuilder.Append($"Department = '{mrb.Department.Replace("'", "''")}', ");
queryBuilder.Append($"Process = '{mrb.Process.Replace("'", "''")}', ");
queryBuilder.Append($"Val = {mrb.Val}, ");
queryBuilder.Append($"RMANo = {mrb.RMANo}, ");
queryBuilder.Append($"PCRBNo = '{mrb.PCRBNo}', ");
queryBuilder.Append($"SpecsImpacted = {Convert.ToInt32(mrb.SpecsImpacted)}, ");
queryBuilder.Append($"TrainingRequired = {Convert.ToInt32(mrb.TrainingRequired)}, ");
queryBuilder.Append($"Status = '{mrb.Status}', StageNo = {mrb.StageNo}, ");
queryBuilder.Append($"CustomerImpactedName = '{mrb.CustomerImpactedName}', ");
queryBuilder.Append($"CustomerImpactedName = '{mrb.CustomerImpactedName.Replace("'", "''")}', ");
queryBuilder.Append($"ProcessECNNumber = '{mrb.ProcessECNNumber}', ");
queryBuilder.Append($"Tool = '{mrb.Tool}', Category = '{mrb.Category}' ");
queryBuilder.Append($"Tool = '{mrb.Tool.Replace("'", "''")}', Category = '{mrb.Category.Replace("'", "''")}' ");
queryBuilder.Append($"where MRBNumber = {mrb.MRBNumber};");
int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString());
@ -353,10 +353,10 @@ public class MRBService : IMRBService {
StringBuilder queryBuilder = new();
queryBuilder.Append($"update MRBAction set Action = '{mrbAction.Action}', ");
queryBuilder.Append($"Customer = '{mrbAction.Customer}', ");
queryBuilder.Append($"Customer = '{mrbAction.Customer.Replace("'", "''")}', ");
queryBuilder.Append($"Quantity = {mrbAction.Quantity}, ");
queryBuilder.Append($"PartNumber = '{mrbAction.PartNumber}', ");
queryBuilder.Append($"LotNumber = '{mrbAction.LotNumber}', ");
queryBuilder.Append($"PartNumber = '{mrbAction.PartNumber.Replace("'", "''")}', ");
queryBuilder.Append($"LotNumber = '{mrbAction.LotNumber.Replace("'", "''")}', ");
if (mrbAction.AssignedDate < DateTimeUtilities.MIN_DT)
mrbAction.AssignedDate = DateTimeUtilities.MIN_DT;
queryBuilder.Append($"AssignedDate= '{mrbAction.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
@ -364,9 +364,9 @@ public class MRBService : IMRBService {
mrbAction.CompletedDate = DateTimeUtilities.MAX_DT;
queryBuilder.Append($"CompletedDate= '{mrbAction.CompletedDate.ToString("yyyy-MM-dd HH:mm:ss")}', ");
queryBuilder.Append($"CompletedByUserID={mrbAction.CompletedByUserID}, ");
queryBuilder.Append($"ConvertFrom='{mrbAction.ConvertFrom}', ");
queryBuilder.Append($"ConvertTo='{mrbAction.ConvertTo}', ");
queryBuilder.Append($"Justification='{mrbAction.Justification}' ");
queryBuilder.Append($"ConvertFrom='{mrbAction.ConvertFrom.Replace("'", "''")}', ");
queryBuilder.Append($"ConvertTo='{mrbAction.ConvertTo.Replace("'", "''")}', ");
queryBuilder.Append($"Justification='{mrbAction.Justification.Replace("'", "''")}' ");
queryBuilder.Append($"where ActionID={mrbAction.ActionID};");
int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString());
@ -427,7 +427,7 @@ public class MRBService : IMRBService {
string encodedName = WebUtility.HtmlEncode(file.FileName);
string path = $"{_mrbAttachmentPath}\\{mrbNumber}\\{encodedName}";
await SaveFileToFileSystem(file, path);
await FileUtilities.SaveFileToFileSystem(file, path);
await SaveAttachmentInDb(file, path, mrbNumber);
UploadResult uploadResult = new() {
@ -471,7 +471,7 @@ public class MRBService : IMRBService {
string encodedName = WebUtility.HtmlEncode(file.FileName);
string path = $"{_mrbAttachmentPath}\\{actionId}\\{encodedName}";
taskList.Add(SaveFileToFileSystem(file, path));
taskList.Add(FileUtilities.SaveFileToFileSystem(file, path));
taskList.Add(SaveActionAttachmentInDb(file, path, actionId));
UploadResult uploadResult = new() {
@ -755,24 +755,138 @@ public class MRBService : IMRBService {
}
}
private async Task SaveFileToFileSystem(IFormFile file, string path) {
public async Task ConvertActionsToCsvFile(int mrbNumber, string path) {
try {
_logger.LogInformation($"Attempting to save file to file system");
_logger.LogInformation($"Attempting to convert MRB {mrbNumber} actions to a CSV file");
if (file is null) throw new ArgumentNullException("File cannot be null");
if (!(await MRBNumberIsValid(mrbNumber))) throw new ArgumentException($"{mrbNumber} is not a valid ");
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Path cannot be null or empty");
if (File.Exists(path)) throw new Exception($"A file already exists with name {file.FileName}");
if (File.Exists(path)) File.Delete(path);
string? directoryPath = Path.GetDirectoryName(path);
if (!string.IsNullOrWhiteSpace(directoryPath))
Directory.CreateDirectory(directoryPath);
using (Stream stream = File.Create(path)) {
await file.CopyToAsync(stream);
IEnumerable<MRBAction> actions = await GetMRBActionsForMRB(mrbNumber, false);
DataTable dt = await ConvertActionsToDataTable(actions);
using StreamWriter sw = new StreamWriter(path, false);
for (int i = 0; i < dt.Columns.Count; i++) {
sw.Write(dt.Columns[i]);
if (i < dt.Columns.Count - 1) sw.Write(",");
}
sw.Write(sw.NewLine);
foreach (DataRow dr in dt.Rows) {
for (int i = 0; i < dt.Columns.Count; i++) {
if (!Convert.IsDBNull(dr[i])) {
string? value = dr[i].ToString();
if (value is null) {
sw.Write("");
} else if (value.Contains(',')) {
value = String.Format("\"{0}\"", value);
sw.Write(value);
} else {
sw.Write(dr[i].ToString());
}
}
if (i < dt.Columns.Count - 1) {
sw.Write(",");
}
}
sw.Write(sw.NewLine);
}
sw.Close();
} catch (Exception ex) {
_logger.LogError($"An exception occurred when attempting to save file to file system. Exception: {ex.Message}");
_logger.LogError($"Unable to convert MRB {mrbNumber} actions to a CSV file, because {ex.Message}");
throw;
}
}
private async Task<DataTable> ConvertActionsToDataTable(IEnumerable<MRBAction> actions) {
try {
_logger.LogInformation("Attempting to convert MRB actions to a DataTable");
if (actions is null) throw new ArgumentNullException("MRB actions cannot be null");
DataTable dt = new();
if (actions.Count() > 0 && actions.First().Action.Equals("Convert", StringComparison.InvariantCultureIgnoreCase)) {
dt.Columns.Add("Action", typeof(string));
dt.Columns.Add("From Customer", typeof(string));
dt.Columns.Add("From Part Number", typeof(string));
dt.Columns.Add("Batch Number / Lot Number", typeof(string));
dt.Columns.Add("Qty", typeof(string));
dt.Columns.Add("To Customer", typeof(string));
dt.Columns.Add("To Part Number", typeof(string));
dt.Columns.Add("Assigned Date", typeof(string));
dt.Columns.Add("Completed Date", typeof(string));
dt.Columns.Add("Completed By", typeof(string));
foreach (MRBAction action in actions) {
if (action.CompletedByUser is null && action.CompletedByUserID > 0)
action.CompletedByUser = await _userService.GetUserByUserId(action.CompletedByUserID);
string convertFromCustomer = string.Empty;
string convertFromPart = string.Empty;
string convertToCustomer = string.Empty;
string convertToPart = string.Empty;
string[] convertFrom = action.ConvertFrom.Split(" ");
if (convertFrom.Length > 1) {
convertFromCustomer = convertFrom[0];
foreach (string partStr in convertFrom.Skip(1))
convertFromPart += partStr;
}
string[] convertTo = action.ConvertTo.Split(" ");
if (convertTo.Length > 1) {
convertToCustomer = convertTo[0];
foreach (string partStr in convertTo.Skip(1))
convertToPart += partStr;
}
dt.Rows.Add(action.Action, convertFromCustomer, convertFromPart, action.Quantity.ToString(),
convertToCustomer, convertToPart,
DateTimeUtilities.GetDateAsStringMinDefault(action.AssignedDate),
DateTimeUtilities.GetDateAsStringMaxDefault(action.CompletedDate),
action.CompletedByUser is null ? "" : action.CompletedByUser.GetFullName());
}
} else {
dt.Columns.Add("Action", typeof(string));
dt.Columns.Add("Customer", typeof(string));
dt.Columns.Add("Qty", typeof(string));
dt.Columns.Add("Convert From", typeof(string));
dt.Columns.Add("Convert To", typeof(string));
dt.Columns.Add("Part Number", typeof(string));
dt.Columns.Add("Batch Number / Lot Number", typeof(string));
dt.Columns.Add("Justification", typeof(string));
dt.Columns.Add("Assigned Date", typeof(string));
dt.Columns.Add("Completed Date", typeof(string));
dt.Columns.Add("Completed By", typeof(string));
foreach (MRBAction action in actions) {
if (action.CompletedByUser is null && action.CompletedByUserID > 0)
action.CompletedByUser = await _userService.GetUserByUserId(action.CompletedByUserID);
dt.Rows.Add(action.Action, action.Customer, action.Quantity.ToString(), action.ConvertFrom, action.ConvertTo,
action.PartNumber, action.LotNumber, action.Justification,
DateTimeUtilities.GetDateAsStringMinDefault(action.AssignedDate),
DateTimeUtilities.GetDateAsStringMaxDefault(action.CompletedDate),
action.CompletedByUser is null ? "" : action.CompletedByUser.GetFullName());
}
}
return dt;
} catch (Exception ex) {
_logger.LogError($"Unable to convert MRB actions to a DataTable, because {ex.Message}");
throw;
}
}

View File

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

View File

@ -0,0 +1,31 @@
using Microsoft.AspNetCore.Components.Forms;
using NLog;
namespace MesaFabApproval.API.Utilities;
public class FileUtilities {
private static readonly Logger _logger = NLog.LogManager.GetCurrentClassLogger();
public static async Task SaveFileToFileSystem(IFormFile file, string path) {
try {
_logger.Info($"Attempting to save file to file system");
if (file is null) throw new ArgumentNullException("File cannot be null");
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Path cannot be null or empty");
if (File.Exists(path)) throw new Exception($"A file already exists with name {file.FileName}");
string? directoryPath = Path.GetDirectoryName(path);
if (!string.IsNullOrWhiteSpace(directoryPath))
Directory.CreateDirectory(directoryPath);
using (Stream stream = File.Create(path)) {
await file.CopyToAsync(stream);
}
} catch (Exception ex) {
_logger.Error($"Unable to save file to file system, because {ex.Message}");
throw;
}
}
}

View File

@ -28,6 +28,6 @@
<logger name="Microsoft.*" finalMinLevel="Warn" />
<logger name="Microsoft.AspNetCore.HttpLogging.*" finalMinLevel="Info" />
<logger name="System.Net.Http.HttpClient.*" finalMinLevel="Warn" />
<logger name="*" minlevel="Info" writeTo="asyncLog" />
<logger name="*" minlevel="Info" writeTo="consoleLog, appLog" />
</rules>
</nlog>

View File

@ -1,8 +1,14 @@
@inherits LayoutComponentBase
@using System.Text.Json
@using System.Text
@inherits LayoutComponentBase
@inject MesaFabApprovalAuthStateProvider authStateProvider
@inject IAuthenticationService authenticationService
@inject IConfiguration Configuration
@inject IMemoryCache cache
@inject IJSRuntime jsRuntime
@inject IHttpClientFactory httpClientFactory
@inject ISnackbar snackbar
@inject NavigationManager navManager
<MudThemeProvider />
@ -28,19 +34,22 @@
<MudDrawer @bind-Open="_drawerOpen" ClipMode="DrawerClipMode.Always" Elevation="2">
<MudNavMenu Color="Color.Info" Bordered="true" Class="d-flex flex-column justify-center p-1 gap-1">
<MudButton Variant="Variant.Filled"
Color="Color.Tertiary"
Href="@Configuration["OldFabApprovalUrl"]"
Target="_blank"
StartIcon="@Icons.Material.Filled.Home">
Color="Color.Tertiary"
Href="@Configuration["OldFabApprovalUrl"]"
Target="_blank"
StartIcon="@Icons.Material.Filled.Home">
Return to Main Site
</MudButton>
<MudDivider Class="my-1" />
@if (authStateProvider.CurrentUser is not null) {
<MudNavGroup Title="Create New">
<MudNavLink OnClick="@(() => GoTo("mrb/new"))">Create New MRB</MudNavLink>
<MudNavLink OnClick="@(() => GoTo("pcrb/new"))">Create New PCRB</MudNavLink>
</MudNavGroup>
<MudNavLink OnClick="@(() => GoTo(""))" Icon="@Icons.Material.Filled.Dashboard">Dashboard</MudNavLink>
<MudNavLink OnClick="@(() => GoTo("mrb/all"))" Icon="@Icons.Material.Filled.Ballot">MRB List</MudNavLink>
<MudNavLink OnClick="@(() => GoTo("pcrb/all"))" Icon="@Icons.Material.Filled.Ballot">PCRB List</MudNavLink>
}
</MudNavMenu>
</MudDrawer>
@ -68,4 +77,48 @@
cache.Set("redirectUrl", page);
navManager.NavigateTo(page);
}
private async Task GoToExternal(string url, string content) {
IJSObjectReference windowModule = await jsRuntime.InvokeAsync<IJSObjectReference>("import", "./js/OpenInNewWindow.js");
await windowModule.InvokeAsync<object>("OpenInNewWindow", url, content);
}
private async Task GoToOldSite() {
try {
User? currentUser = authStateProvider.CurrentUser;
AuthTokens? authTokens = await authenticationService.GetAuthTokens();
if (currentUser is null || authTokens is null) {
await authStateProvider.Logout();
navManager.NavigateTo("login");
return;
}
AuthAttempt authAttempt = new() {
LoginID = currentUser.LoginID,
AuthTokens = authTokens
};
HttpClient httpClient = httpClientFactory.CreateClient("OldSite");
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "Account/ExternalAuthSetup");
request.Content = new StringContent(JsonSerializer.Serialize(authAttempt),
Encoding.UTF8,
"application/json");
HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(request);
if (httpResponseMessage.IsSuccessStatusCode) {
snackbar.Add("Old site auth setup successful", Severity.Success);
} else {
snackbar.Add($"Old site auth setup failed, because {httpResponseMessage.ReasonPhrase}", Severity.Error);
}
await GoToExternal($"{Configuration["OldFabApprovalUrl"]}", "");
} catch (Exception ex) {
snackbar.Add($"Unable to go to old site, because {ex.Message}", Severity.Error);
}
}
}

View File

@ -10,6 +10,10 @@
<Watch Include="**\*.razor" />
</ItemGroup>
<ItemGroup>
<Watch Remove="Pages\Components\PCRBApproverForm.razor" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" />
@ -17,7 +21,7 @@
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
<PackageReference Include="MudBlazor" Version="7.6.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.0.1" />
</ItemGroup>

View File

@ -25,7 +25,7 @@
}
if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("redirectPath", out var redirectPath)) {
_redirectPath = System.Net.WebUtility.UrlDecode(redirectPath);
_redirectPath = redirectPath.ToString();
}
if (!string.IsNullOrWhiteSpace(_jwt) && !string.IsNullOrWhiteSpace(_refreshToken)) {
@ -35,11 +35,12 @@
string loginId = userService.GetLoginIdFromClaimsPrincipal(principal);
await authService.ClearCurrentUser();
await authService.ClearTokens();
await authService.SetLoginId(loginId);
await authService.SetTokens(_jwt, _refreshToken);
User? user = await userService.GetUserByLoginId(loginId);
await authService.SetCurrentUser(user);
await authService.SetCurrentUser(null);
await authStateProvider.StateHasChanged(principal);
}

View File

@ -2,7 +2,7 @@
<MudDialog>
<DialogContent>
<MudPaper Class="p-2">
<MudPaper Class="m-2 p-2">
<MudForm @bind-Errors="@errors">
<MudTextField T="string"
Label="Comments"

View File

@ -4,7 +4,7 @@
<MudDialog>
<DialogContent>
<MudPaper Class="p-2">
<MudPaper Class="m-2 p-2">
<MudForm @bind-Errors="@errors">
<MudTextField T="string"
Label="Comments"

View File

@ -4,10 +4,11 @@
<MudDialog>
<DialogContent>
<MudPaper Class="p-2">
<MudPaper Class="m-2 p-2">
<MudForm @bind-Errors="@errors">
<MudSelect T="string"
Label="Action"
Disabled="@(!string.IsNullOrWhiteSpace(mrbAction.Action))"
Required="true"
RequiredError="You must select an action!"
@bind-Value="@mrbAction.Action"
@ -19,29 +20,61 @@
<MudSelectItem Value="@("Unblock")" />
<MudSelectItem Value="@("Waiver")" />
</MudSelect>
@if (mrbAction.Action.Equals("Convert")) {
<MudTextField @bind-Value="@mrbAction.ConvertFrom"
Label="Convert From"
@if (mrbAction.Action.Equals("Convert", StringComparison.InvariantCultureIgnoreCase)) {
<MudSelect T="string"
Label="Convert From Customer"
Required
Variant="Variant.Outlined"
AnchorOrigin="Origin.BottomCenter"
@bind-Value=convertFromCustomer
Text="@convertFromCustomer">
@foreach (string customer in customerNames) {
<MudSelectItem Value="@(customer)" />
}
</MudSelect>
<MudTextField @bind-Value="@convertFromPart"
Label="Convert From Part Number"
Required
RequiredError="Conversion value required!"
Text="@mrbAction.ConvertFrom" />
<MudTextField @bind-Value="@mrbAction.ConvertTo"
Label="Convert To"
RequiredError="Part number required!"
Text="@convertFromPart" />
<MudSelect T="string"
Label="Convert To Customer"
Required
Variant="Variant.Outlined"
AnchorOrigin="Origin.BottomCenter"
@bind-Value=convertToCustomer
Text="@convertToCustomer">
@foreach (string customer in customerNames) {
<MudSelectItem Value="@(customer)" />
}
</MudSelect>
<MudTextField @bind-Value="@convertToPart"
Label="Convert To Part Number"
Required
RequiredError="Conversion value required!"
Text="@mrbAction.ConvertTo" />
RequiredError="Part number required!"
Text="@convertToPart" />
} else {
<MudSelect T="string"
Label="Affected Customer"
Required
Variant="Variant.Outlined"
AnchorOrigin="Origin.BottomCenter"
@bind-Value=mrbAction.Customer
Text="@mrbAction.Customer">
@foreach (string customer in customerNames) {
<MudSelectItem Value="@(customer)" />
}
</MudSelect>
<MudAutocomplete T="string"
Label="Part Number"
Variant="Variant.Outlined"
AnchorOrigin="Origin.BottomCenter"
@bind-Value=mrbAction.PartNumber
Text="@mrbAction.PartNumber"
SearchFunc="@PartNumberSearch"
ResetValueOnEmptyText
CoerceText />
}
<MudSelect T="string"
Label="Affected Customer"
Required
Variant="Variant.Outlined"
AnchorOrigin="Origin.BottomCenter"
@bind-Value=mrbAction.Customer
Text="@mrbAction.Customer">
@foreach (string customer in customerNames) {
<MudSelectItem Value="@(customer)" />
}
</MudSelect>
<MudTextField T="int"
InputType="@InputType.Number"
Label="Qty"
@ -49,15 +82,6 @@
RequiredError="You must supply a quantity!"
@bind-Value=mrbAction.Quantity
Text="@mrbAction.Quantity.ToString()" />
<MudAutocomplete T="string"
Label="Part Number"
Variant="Variant.Outlined"
AnchorOrigin="Origin.BottomCenter"
@bind-Value=mrbAction.PartNumber
Text="@mrbAction.PartNumber"
SearchFunc="@PartNumberSearch"
ResetValueOnEmptyText
CoerceText />
<MudAutocomplete T="string"
Label="Batch Number / Lot Number"
Variant="Variant.Outlined"
@ -130,6 +154,11 @@
private IEnumerable<string> customerNames = new List<string>();
private string convertFromCustomer = string.Empty;
private string convertFromPart = string.Empty;
private string convertToCustomer = string.Empty;
private string convertToPart = string.Empty;
private bool isVisible { get; set; }
private string[] errors = { };
private bool processingSave = false;
@ -145,10 +174,34 @@
actions = (await mrbService.GetMRBActionsForMRB(mrbAction.MRBNumber, false)).OrderByDescending(a => a.ActionID);
if (actions is not null && actions.Count() > 0) {
if (string.IsNullOrWhiteSpace(mrbAction.Action))
mrbAction.Action = actions.First().Action;
if (string.IsNullOrWhiteSpace(mrbAction.Customer))
mrbAction.Customer = actions.First().Customer;
if (string.IsNullOrWhiteSpace(mrbAction.LotNumber))
mrbAction.LotNumber = actions.First().LotNumber;
if (string.IsNullOrWhiteSpace(mrbAction.PartNumber))
mrbAction.PartNumber = actions.First().PartNumber;
if (string.IsNullOrWhiteSpace(mrbAction.Justification))
mrbAction.Justification = actions.First().Justification;
if (mrbAction.Quantity == 0)
mrbAction.Quantity = actions.First().Quantity;
if (mrbAction.Action.Equals("Convert", StringComparison.InvariantCultureIgnoreCase)) {
string[] convertFrom = actions.First().ConvertFrom.Split(" ");
if (convertFrom.Length > 1) {
convertFromCustomer = convertFrom[0];
foreach (string partStr in convertFrom.Skip(1))
convertFromPart += partStr;
}
string[] convertTo = actions.First().ConvertTo.Split(" ");
if (convertTo.Length > 1) {
convertToCustomer = convertTo[0];
foreach (string partStr in convertTo.Skip(1))
convertToPart += partStr;
}
}
}
}
@ -165,9 +218,17 @@
actionIsValid = actionIsValid && !string.IsNullOrWhiteSpace(mrbAction.Customer) &&
!string.IsNullOrWhiteSpace(mrbAction.PartNumber) &&
!string.IsNullOrWhiteSpace(mrbAction.LotNumber);
actionIsValid = actionIsValid && mrbAction.Quantity > 0;
if (mrbAction.Action.Equals("Scrap"))
return actionIsValid && !string.IsNullOrWhiteSpace(mrbAction.Justification);
if (mrbAction.Action.Equals("Convert", StringComparison.InvariantCultureIgnoreCase)) {
actionIsValid = actionIsValid && !string.IsNullOrWhiteSpace(convertFromCustomer) &&
!string.IsNullOrWhiteSpace(convertFromPart) &&
!string.IsNullOrWhiteSpace(convertToCustomer) &&
!string.IsNullOrWhiteSpace(convertToPart);
}
if (mrbAction.Action.Equals("Scrap", StringComparison.InvariantCultureIgnoreCase))
actionIsValid = actionIsValid && !string.IsNullOrWhiteSpace(mrbAction.Justification);
return actionIsValid;
}
@ -177,6 +238,11 @@
try {
if (!FormIsValid()) throw new Exception("You must complete the form before saving!");
if (mrbAction.Action.Equals("Convert", StringComparison.InvariantCultureIgnoreCase)) {
mrbAction.ConvertFrom = $"{convertFromCustomer} {convertFromPart}";
mrbAction.ConvertTo = $"{convertToCustomer} {convertToPart}";
}
if (mrbAction.MRBNumber > 0) {
if (mrbAction.ActionID <= 0) {
await mrbService.CreateMRBAction(mrbAction);

View File

@ -5,7 +5,7 @@
<MudDialog>
<DialogContent>
@if (availableApprovers is not null) {
<MudPaper Class="p-2">
<MudPaper Class="m-2 p-2">
<MudSelect T="User"
Label="Select a User"
Required

View File

@ -0,0 +1,151 @@
@inject MesaFabApprovalAuthStateProvider authStateProvider
@inject NavigationManager navigationManager
@inject IPCRBService pcrbService
@inject IUserService userService
@inject IECNService ecnService
@inject ISnackbar snackbar
<MudDialog>
<DialogContent>
<MudPaper Class="m-2 p-2">
<MudForm @bind-Errors="@errors">
<MudTextField T="string"
Label="Document Type"
@bind-Value="@document.DocType"
@bind-Text="@document.DocType"
Disabled
AutoGrow />
<MudTextField Label="Document Numbers - Rev. & Title"
@bind-Value="@document.DocNumbers"
@bind-Text="@document.DocNumbers"
Immediate
AutoGrow
AutoFocus />
@if (DocNumberIsNA()) {
<MudTextField Label="Comments"
@bind-Value="@document.Comment"
@bind-Text="@document.Comment"
Required
RequiredError="You must provide a comment"
Immediate
AutoGrow />
} else {
<MudTextField @bind-Value="@document.ECNNumber"
Required
RequiredError="You must provide a valid ECN#"
Clearable
Variant="Variant.Outlined"
InputType="@InputType.Number"
Validation="@(new Func<int, Task<string>>(ECNNoIsValid))"
Label="ECN#"
Immediate
AutoGrow />
}
<MudCheckBox Label="Complete"
Color="Color.Tertiary"
@bind-Value=complete
LabelPosition="LabelPosition.Start" />
</MudForm>
</MudPaper>
</DialogContent>
<DialogActions>
@if ((DocNumberIsNA() && !string.IsNullOrWhiteSpace(document.Comment)) ||
(!DocNumberIsNA() && ecnNoIsValid)) {
<MudButton Variant="Variant.Filled"
Color="Color.Tertiary"
Class="m1-auto"
OnClick=Save>
@if (saveInProcess) {
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
<MudText>Processing</MudText>
} else {
<MudText>Save</MudText>
}
</MudButton>
}
<MudButton Variant="Variant.Filled"
Class="grey text-black m1-auto"
OnClick=Cancel>
Cancel
</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter]
MudDialogInstance MudDialog { get; set; }
[Parameter]
public required PCR3Document document { get; set; }
private string[] errors = { };
private bool complete = false;
private bool saveInProcess = false;
private bool ecnNoIsValid = true;
protected override async Task OnParametersSetAsync() {
complete = document.CompletedByID > 0;
}
private async Task Save() {
saveInProcess = true;
try {
if (authStateProvider.CurrentUser is null) {
await authStateProvider.Logout();
navigationManager.NavigateTo("login");
return;
}
if (!complete) {
document.CompletedByID = 0;
document.CompletedBy = null;
document.CompletedDate = DateTimeUtilities.MAX_DT;
}
if (complete && document.CompletedByID <= 0) {
document.CompletedByID = authStateProvider.CurrentUser.UserID;
document.CompletedBy = authStateProvider.CurrentUser;
document.CompletedDate = DateTime.Now;
}
if (!DocNumberIsNA() && !ecnNoIsValid)
throw new Exception($"{document.ECNNumber} is not a valid ECN#");
if (DocNumberIsNA() && string.IsNullOrWhiteSpace(document.Comment))
throw new Exception("you must provide a comment");
await pcrbService.UpdatePCR3Document(document);
await pcrbService.GetPCR3DocumentsForPlanNumber(document.PlanNumber, true);
saveInProcess = false;
MudDialog.Close(DialogResult.Ok(document));
} catch (Exception ex) {
saveInProcess = false;
snackbar.Add($"Unable to save document, because {ex.Message}", Severity.Error);
}
}
private void Cancel() {
MudDialog.Close(DialogResult.Cancel());
}
private bool DocNumberIsNA() {
if (document.DocNumbers.ToLower().Equals("na") ||
document.DocNumbers.ToLower().Equals("n/a")) {
return true;
}
return false;
}
private async Task<string> ECNNoIsValid(int ecnNumber) {
string? result = await ecnService.ECNNumberIsValidStr(ecnNumber);
if (result is null) ecnNoIsValid = true;
else ecnNoIsValid = false;
StateHasChanged();
return result;
}
}

View File

@ -0,0 +1,201 @@
@inject MesaFabApprovalAuthStateProvider authStateProvider
@inject NavigationManager navigationManager
@inject IPCRBService pcrbService
@inject IUserService userService
@inject ISnackbar snackbar
<MudDialog>
<DialogContent>
<MudPaper Class="m-2 p-2">
<MudForm @bind-Errors="@errors">
<MudTextField T="string"
Label="Action"
Required
RequiredError="Enter action item"
@bind-Value="@name"
@bind-Text="@name"
Immediate
Clearable
AutoGrow
AutoFocus />
<MudCheckBox Label="Gating"
Color="Color.Tertiary"
@bind-Value=gating
LabelPosition="LabelPosition.Start" />
<MudCheckBox Label="Closed"
Color="Color.Tertiary"
@bind-Value="@closedStatus"
LabelPosition="LabelPosition.Start" />
@if (closedStatus) {
<MudDatePicker Label="Closed Date"
Color="Color.Tertiary"
@bind-Date="@closedDate"
Clearable
MinDate="@DateTimeUtilities.MIN_DT"
MaxDate="@DateTimeUtilities.MAX_DT"
Placeholder="Select a closed date" />
}
<MudSelect T="User"
Label="Responsible Person"
Variant="Variant.Outlined"
Required
RequiredError="You must select a responsible person"
Clearable
AnchorOrigin="Origin.BottomCenter"
ToStringFunc="@UserToNameConverter"
@bind-Value=@responsiblePerson>
@foreach (User user in allActiveUsers.OrderBy(u => u.FirstName)) {
<MudSelectItem T="User" Value="@(user)" />
}
</MudSelect>
</MudForm>
</MudPaper>
</DialogContent>
<DialogActions>
<MudButton Variant="Variant.Filled"
Color="Color.Tertiary"
Class="m1-auto"
OnClick=Save>
@if (saveActionItemInProcess) {
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
<MudText>Processing</MudText>
} else {
<MudText>Submit</MudText>
}
</MudButton>
<MudButton Variant="Variant.Filled"
Class="grey text-black m1-auto"
OnClick=Cancel>
Cancel
</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter]
MudDialogInstance MudDialog { get; set; }
[Parameter]
public int planNumber { get; set; } = 0;
[Parameter]
public int step { get; set; } = 0;
[Parameter]
public PCRBActionItem? actionItem { get; set; } = null;
[Parameter]
public IEnumerable<User> allActiveUsers { get; set; }
private string[] errors = { };
private string name = "";
private bool gating = false;
private DateTime? closedDate = DateTimeUtilities.MAX_DT;
private bool closedStatus = false;
private User? responsiblePerson = null;
private bool saveActionItemInProcess = false;
protected override async Task OnParametersSetAsync() {
if (authStateProvider.CurrentUser is null) {
await authStateProvider.Logout();
navigationManager.NavigateTo("login");
}
if (planNumber <= 0) {
snackbar.Add($"{planNumber} is not a valid PCRB plan#", Severity.Error);
MudDialog.Close(DialogResult.Cancel());
}
if (allActiveUsers is null || allActiveUsers.Count() <= 0)
allActiveUsers = await userService.GetAllActiveUsers();
if (actionItem is not null) {
name = actionItem.Name;
gating = actionItem.Gating;
closedStatus = actionItem.ClosedStatus;
closedDate = actionItem.ClosedDate;
if (closedDate.Equals(DateTimeUtilities.MAX_DT)) {
closedDate = DateTime.Now;
}
if (actionItem.ResponsiblePersonID > 0) {
if (actionItem.ResponsiblePerson is not null) responsiblePerson = actionItem.ResponsiblePerson;
else responsiblePerson = await userService.GetUserByUserId(actionItem.ResponsiblePersonID);
actionItem.ResponsiblePerson = responsiblePerson;
}
} else {
name = "";
gating = false;
closedStatus = false;
closedDate = DateTime.Now;
responsiblePerson = null;
}
}
private async Task Save() {
saveActionItemInProcess = true;
try {
if (authStateProvider.CurrentUser is null) {
await authStateProvider.Logout();
navigationManager.NavigateTo("login");
}
if (string.IsNullOrWhiteSpace(name)) throw new Exception("name missing");
if (actionItem is null) {
actionItem = new() {
Name = name,
UploadedBy = authStateProvider.CurrentUser,
UploadedByID = authStateProvider.CurrentUser.UserID,
PlanNumber = planNumber,
Step = step,
Gating = gating,
ClosedStatus = closedStatus,
ResponsiblePerson = responsiblePerson,
ResponsiblePersonID = responsiblePerson.UserID,
ClosedDate = closedDate,
ClosedBy = closedDate is null || closedDate >= DateTimeUtilities.MAX_DT ? null : authStateProvider.CurrentUser,
ClosedByID = closedStatus ? authStateProvider.CurrentUser.UserID : 0
};
if (actionItem.ClosedStatus == false) {
actionItem.ClosedDate = DateTimeUtilities.MAX_DT;
}
await pcrbService.CreateNewActionItem(actionItem);
} else {
actionItem.Name = name;
actionItem.Gating = gating;
actionItem.ClosedStatus = closedStatus;
actionItem.ClosedDate = closedDate;
if (closedStatus) {
actionItem.ClosedBy = authStateProvider.CurrentUser;
actionItem.ClosedByID = authStateProvider.CurrentUser.UserID;
} else {
actionItem.ClosedDate = DateTimeUtilities.MAX_DT;
}
actionItem.ResponsiblePerson = responsiblePerson;
if (responsiblePerson is not null)
actionItem.ResponsiblePersonID = responsiblePerson.UserID;
await pcrbService.UpdateActionItem(actionItem);
}
saveActionItemInProcess = false;
MudDialog.Close(DialogResult.Ok(actionItem));
} catch (Exception ex) {
saveActionItemInProcess = false;
snackbar.Add($"Unable to save action item, because {ex.Message}", Severity.Error);
}
}
private void Cancel() {
MudDialog.Close(DialogResult.Cancel());
}
private Func<User, string> UserToNameConverter = u => u is null ? string.Empty : u.GetFullName();
private Func<PCRBAttachment, string> AttachmentToFileNameConverter = a => a is null ? "" : a.FileName;
}

View File

@ -0,0 +1,250 @@
@inject MesaFabApprovalAuthStateProvider authStateProvider
@inject NavigationManager navigationManager
@inject IPCRBService pcrbService
@inject IUserService userService
@inject IApprovalService approvalService
@inject ISnackbar snackbar
<MudDialog>
<DialogContent>
<MudPaper Class="m-2 p-2">
<MudForm @bind-Errors="@errors">
<MudSelect T="string"
Label="Job Title"
Required
RequiredError="You must provide a job title"
ValueChanged="@SelectedJobTitleChanged"
@bind-Text="@selectedJobTitle"
Immediate
Disabled="@(approval is not null && !string.IsNullOrWhiteSpace(selectedJobTitle))"
Clearable
AutoFocus >
@foreach (string jt in availableJobTitles) {
<MudSelectItem Value="@jt">@jt</MudSelectItem>
}
</MudSelect>
<MudSelect T="User"
Label="Select a User"
Required
Clearable
Disabled="@(string.IsNullOrWhiteSpace(selectedJobTitle))"
Variant="Variant.Outlined"
AnchorOrigin="Origin.BottomCenter"
@bind-Value=selectedUser
ToStringFunc="@UserToNameConverter">
@foreach (User user in availableUsers) {
<MudSelectItem Value="@user">@user.GetFullName()</MudSelectItem>
}
</MudSelect>
</MudForm>
</MudPaper>
</DialogContent>
<DialogActions>
<MudButton Variant="Variant.Filled"
Color="Color.Tertiary"
Class="m1-auto"
OnClick=Save>
@if (saveAttendeeInProcess) {
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
<MudText>Processing</MudText>
} else {
<MudText>Submit</MudText>
}
</MudButton>
<MudButton Variant="Variant.Filled"
Class="grey text-black m1-auto"
OnClick=Cancel>
Cancel
</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter]
MudDialogInstance MudDialog { get; set; }
[Parameter]
public int planNumber { get; set; } = 0;
[Parameter]
public int step { get; set; } = 0;
[Parameter]
public Approval? approval { get; set; } = null;
private HashSet<string> availableJobTitles = new();
private Dictionary<string, SubRole> jobTitleToSubRoleMap = new();
private Dictionary<string, IEnumerable<User>> jobTitleToAvailableUsersMap = new();
private HashSet<User> availableUsers = new();
private string[] errors = { };
private string selectedJobTitle = "";
private User? selectedUser = null;
private bool saveAttendeeInProcess = false;
protected override async Task OnParametersSetAsync() {
if (authStateProvider.CurrentUser is null) {
await authStateProvider.Logout();
navigationManager.NavigateTo("login");
}
if (planNumber <= 0) {
snackbar.Add($"{planNumber} is not a valid PCRB plan#", Severity.Error);
MudDialog.Close(DialogResult.Cancel());
}
await GetAttendees();
if (approval is not null) {
selectedJobTitle = approval.RoleName;
if (approval.UserID > 0) {
if (approval.User is not null) {
selectedUser = approval.User;
} else {
selectedUser = await userService.GetUserByUserId(approval.UserID);
approval.User = selectedUser;
}
}
SelectedJobTitleChanged(selectedJobTitle);
} else {
selectedJobTitle = "";
selectedUser = null;
}
}
private async Task Save() {
saveAttendeeInProcess = true;
try {
if (authStateProvider.CurrentUser is null) {
await authStateProvider.Logout();
navigationManager.NavigateTo("login");
}
if (string.IsNullOrWhiteSpace(selectedJobTitle)) throw new Exception("job title missing");
if (selectedUser is null) throw new Exception("attendee not selected");
PCRB pcrb = await pcrbService.GetPCRBByPlanNumber(planNumber, false);
if (approval is null) {
jobTitleToSubRoleMap.TryGetValue($"{selectedJobTitle}{selectedUser.UserID}", out SubRole? subRole);
if (subRole is null) throw new Exception($"no approval role found for job title {selectedJobTitle}");
approval = new() {
RoleName = subRole.SubRoleCategoryItem,
SubRole = subRole.SubRoleName,
SubRoleID = subRole.SubRoleID,
IssueID = planNumber,
Step = step,
User = selectedUser,
UserID = selectedUser.UserID,
AssignedDate = DateTimeUtilities.MIN_DT
};
await approvalService.CreateApproval(approval);
} else {
int originalUserId = approval.UserID;
approval.UserID = selectedUser.UserID;
approval.User = selectedUser;
if (originalUserId != approval.UserID) {
if (approval.AssignedDate > DateTimeUtilities.MIN_DT)
approval.AssignedDate = DateTime.Now;
approval.NotifyDate = DateTimeUtilities.MIN_DT;
}
await approvalService.UpdateApproval(approval);
if (originalUserId != approval.UserID)
await pcrbService.NotifyNewApprovals(pcrb);
}
saveAttendeeInProcess = false;
MudDialog.Close(DialogResult.Ok(approval));
} catch (Exception ex) {
saveAttendeeInProcess = false;
snackbar.Add($"Unable to save attendee, because {ex.Message}", Severity.Error);
}
}
private void Cancel() {
MudDialog.Close(DialogResult.Cancel());
}
private Func<User, string> UserToNameConverter = u => u is null ? string.Empty : u.GetFullName();
private async Task GetAttendees() {
int roleId = await approvalService.GetRoleIdForRoleName("Module Manager");
if (roleId <= 0) throw new Exception($"could not find Director role ID");
availableJobTitles.Clear();
jobTitleToAvailableUsersMap.Clear();
jobTitleToSubRoleMap.Clear();
IEnumerable<SubRole> subRoles = await approvalService.GetSubRolesForSubRoleName("MMSubRole", roleId);
HashSet<string> defaultSubRoleCategoryItems = new() { "Si Production", "Si Engineering", "Quality" };
HashSet<string> unusedSubRoleCategoryItems = new() { "GaN Engineering", "GaN Operations", "Integration" };
foreach (SubRole subRole in subRoles) {
if (approval is null) {
if (!defaultSubRoleCategoryItems.Contains(subRole.SubRoleCategoryItem) &&
!unusedSubRoleCategoryItems.Contains(subRole.SubRoleCategoryItem)) {
availableJobTitles.Add(subRole.SubRoleCategoryItem);
IEnumerable<User> subRoleMembers = await approvalService.GetApprovalGroupMembers(subRole.SubRoleID);
jobTitleToAvailableUsersMap.Add(subRole.SubRoleCategoryItem, subRoleMembers);
foreach (User member in subRoleMembers) {
jobTitleToSubRoleMap.Add($"{subRole.SubRoleCategoryItem}{member.UserID}", subRole);
}
}
} else {
if (!unusedSubRoleCategoryItems.Contains(subRole.SubRoleCategoryItem)) {
IEnumerable<User> subRoleMembers = await approvalService.GetApprovalGroupMembers(subRole.SubRoleID);
jobTitleToAvailableUsersMap.Add(subRole.SubRoleCategoryItem, subRoleMembers);
foreach (User member in subRoleMembers)
availableUsers.Add(member);
}
}
}
}
private void SelectedJobTitleChanged(string jobTitle) {
selectedJobTitle = jobTitle;
selectedUser = null;
availableUsers.Clear();
if (approval is null) {
if (jobTitleToAvailableUsersMap.TryGetValue(jobTitle, out IEnumerable<User>? jobTitleMembers)) {
if (jobTitleMembers is not null) {
foreach (User member in jobTitleMembers)
availableUsers.Add(member);
}
}
} else {
foreach (IEnumerable<User> memberList in jobTitleToAvailableUsersMap.Values) {
if (memberList is not null) {
foreach(User member in memberList) {
availableUsers.Add(member);
}
}
}
}
StateHasChanged();
}
}

View File

@ -0,0 +1,140 @@
@inject MesaFabApprovalAuthStateProvider authStateProvider
@inject NavigationManager navigationManager
@inject IPCRBService pcrbService
@inject ISnackbar snackbar
<MudDialog>
<DialogContent>
<MudPaper Class="m-2 p-2">
<MudForm @bind-Errors="@errors">
<MudTextField T="string"
Label="File Name"
Disabled
Immediate
@bind-Value="@fileName"
@bind-Text="@fileName"
AutoGrow />
<MudFileUpload T="IBrowserFile"
FilesChanged="AddFile"
Required
Disabled="@(!string.IsNullOrWhiteSpace(fileName))"
RequiredError="You must select a file">
<ActivatorContent>
<MudButton Variant="Variant.Filled"
Color="Color.Tertiary"
style="margin: auto;"
StartIcon="@Icons.Material.Filled.AttachFile">
@if (addFileInProcess) {
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
<MudText>Processing</MudText>
} else {
<MudText>Add File</MudText>
}
</MudButton>
</ActivatorContent>
</MudFileUpload>
</MudForm>
</MudPaper>
</DialogContent>
<DialogActions>
<MudButton Variant="Variant.Filled"
Color="Color.Tertiary"
Class="m1-auto"
OnClick=Submit>
@if (processing) {
<MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" />
<MudText>Processing</MudText>
}
else {
<MudText>Submit</MudText>
}
</MudButton>
<MudButton Variant="Variant.Filled"
Class="grey text-black m1-auto"
OnClick=Cancel>
Cancel
</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
[Parameter]
public int planNumber { get; set; } = 0;
[Parameter]
public int step { get; set; } = 0;
private string[] errors = { };
private string fileName = "";
private IBrowserFile? file = null;
private bool addFileInProcess = false;
private bool processing = false;
protected override async Task OnParametersSetAsync() {
if (planNumber <= 0) {
snackbar.Add($"{planNumber} is not a valid PCRB plan#", Severity.Error);
MudDialog.Close(DialogResult.Cancel());
}
if (step <= 0) {
snackbar.Add($"{step} is not a valid PCRB stage#", Severity.Error);
MudDialog.Close(DialogResult.Cancel());
}
if (authStateProvider.CurrentUser is null) {
await authStateProvider.Logout();
navigationManager.NavigateTo("login");
}
}
private void AddFile(IBrowserFile newFile) {
addFileInProcess = true;
file = newFile;
fileName = newFile.Name;
addFileInProcess = false;
}
private async Task Submit() {
processing = true;
try {
if (authStateProvider.CurrentUser is null) {
await authStateProvider.Logout();
navigationManager.NavigateTo("login");
return;
}
if (string.IsNullOrWhiteSpace(fileName))
throw new Exception("file name missing");
if (file is null)
throw new Exception("file is missing");
PCRBAttachment attachment = new() {
Step = step,
UploadDateTime = DateTime.Now,
UploadedByID = authStateProvider.CurrentUser.UserID,
PlanNumber = planNumber,
File = file,
FileName = fileName
};
await pcrbService.UploadAttachment(attachment);
processing = false;
MudDialog.Close(DialogResult.Ok(attachment));
} catch (Exception ex) {
snackbar.Add($"Unable to save document, because {ex.Message}", Severity.Error);
processing = false;
}
}
private void Cancel() {
MudDialog.Close(DialogResult.Cancel());
}
}

View File

@ -7,6 +7,7 @@
@inject NavigationManager navigationManager
@inject ISnackbar snackbar
@inject IMRBService mrbService
@inject IPCRBService pcrbService
@inject IECNService ecnService
@inject ICAService caService
@inject IJSRuntime jsRuntime
@ -88,6 +89,7 @@
<MudSpacer />
<MudTextField @bind-Value="mrbSearchString"
Placeholder="Search"
Immediate
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Search"
IconSize="Size.Medium"
@ -147,21 +149,100 @@
</MudOverlay>
}
</MudTabPanel>
<MudTabPanel Text="My PCRBs" Style="min-width:50%; text-align: center;">
@if (stateProvider.CurrentUser is not null && myPCRBs is not null && !myPcrbsProcessing) {
<MudPaper Outlined="true"
Class="p-2 m-2 d-flex flex-column justify-center">
<MudText Typo="Typo.h4" Align="Align.Center">My PCRBs</MudText>
<MudDivider DividerType="DividerType.Middle" Class="my-2" />
<MudTable Items="@myPCRBs"
Class="m-2"
Striped
SortLabel="Sort by"
Filter="new Func<PCRB, bool>(FilterFuncForPCRBTable)">
<ToolBarContent>
<MudSpacer />
<MudTextField @bind-Value="pcrbSearchString"
Placeholder="Search"
Adornment="Adornment.Start"
Immediate
AdornmentIcon="@Icons.Material.Filled.Search"
IconSize="Size.Medium"
Class="mt-0" />
</ToolBarContent>
<HeaderContent>
<MudTh>
<MudTableSortLabel InitialDirection="SortDirection.Descending" SortBy="new Func<PCRB,object>(x=>x.PlanNumber)">
PCRB#
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.Title)">
Title
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.CurrentStep)">
Current Step
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.InsertTimeStamp)">
Submitted Date
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.LastUpdateDate)">
Last Updated
</MudTableSortLabel>
</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.ClosedDate)">
Completed Date
</MudTableSortLabel>
</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="PCRB#">
<MudLink OnClick="@(() => GoTo($"pcrb/{context.PlanNumber}"))">@context.PlanNumber</MudLink>
</MudTd>
<MudTd DataLabel="Title">@context.Title</MudTd>
<MudTd DataLabel="Current Step">@(GetCurrentPCRBStep(context.CurrentStep))</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>
</RowTemplate>
<PagerContent>
<MudTablePager />
</PagerContent>
</MudTable>
</MudPaper>
} else {
<MudOverlay Visible DarkBackground="true" AutoClose="false">
<MudText Align="Align.Center" Typo="Typo.h3">Processing</MudText>
<MudProgressCircular Color="Color.Info" Size="Size.Large" Indeterminate="true" />
</MudOverlay>
}
</MudTabPanel>
</MudTabs>
</MudPaper>
@code {
private IEnumerable<Approval> approvalList = new List<Approval>();
private IEnumerable<MRB> myMRBs = new List<MRB>();
private IEnumerable<PCRB> myPCRBs = new List<PCRB>();
private IEnumerable<int> ecnNumbers = new HashSet<int>();
private IEnumerable<int> caNumbers = new HashSet<int>();
private IEnumerable<int> mrbNumbers = new HashSet<int>();
private IEnumerable<int> pcrbNumbers = new HashSet<int>();
private bool myApprovalsProcessing = false;
private bool myMrbsProcessing = false;
private bool myPcrbsProcessing = false;
private string mrbSearchString = "";
private string pcrbSearchString = "";
protected async override Task OnParametersSetAsync() {
try {
@ -178,9 +259,17 @@
.ToList()
.OrderByDescending(x => x.SubmittedDate);
myMrbsProcessing = false;
myPcrbsProcessing = true;
myPCRBs = (await pcrbService.GetAllPCRBs(false)).Where(p => p.OwnerID == stateProvider.CurrentUser.UserID)
.ToList()
.OrderByDescending(p => p.InsertTimeStamp);
myPcrbsProcessing = false;
}
} catch (Exception ex) {
myApprovalsProcessing = false;
myMrbsProcessing = false;
myPcrbsProcessing = false;
snackbar.Add($"Unable to load the dashboard, because {ex.Message}", Severity.Error);
}
}
@ -191,12 +280,15 @@
bool isEcn = false;
bool isCa = false;
bool isMrb = false;
bool isPcrb = false;
if (ecnNumbers.Contains(issueId))
isEcn = true;
if (caNumbers.Contains(issueId))
isCa = true;
if (mrbNumbers.Contains(issueId))
isMrb = true;
if (pcrbNumbers.Contains(issueId))
isPcrb = true;
if (!isEcn && !isCa && !isMrb) {
Task<bool> isEcnTask = ecnService.ECNNumberIsValid(issueId);
@ -208,6 +300,9 @@
Task<bool> isMrbTask = mrbService.NumberIsValid(issueId);
tasks.Add(isMrbTask);
Task<bool> isPcrbTask = pcrbService.IdIsValid(issueId);
tasks.Add(isPcrbTask);
await Task.WhenAll(tasks);
if (isEcnTask.Result) {
@ -216,12 +311,15 @@
isCa = true;
} else if (isMrbTask.Result) {
isMrb = true;
} else if (isPcrbTask.Result) {
isPcrb = true;
}
}
if (isEcn) await GoToExternal($"{Configuration["OldFabApprovalUrl"]}/ECN/Edit?IssueID={issueId}", "");
if (isCa) await GoToExternal($"{Configuration["OldFabApprovalUrl"]}/CorrectiveAction/Edit?IssueID={issueId}", "");
if (isMrb) GoTo($"mrb/{issueId}");
if (isPcrb) GoTo($"pcrb/{issueId}");
}
private void GoTo(string page) {
@ -245,4 +343,21 @@
return true;
return false;
}
private bool FilterFuncForPCRBTable(PCRB pcrb) => PCRBFilterFunc(pcrb, pcrbSearchString);
private bool PCRBFilterFunc(PCRB pcrb, string searchString) {
if (string.IsNullOrWhiteSpace(searchString))
return true;
if (pcrb.Title.ToLower().Contains(searchString.Trim().ToLower()))
return true;
if (pcrb.PlanNumber.ToString().Contains(searchString.Trim()))
return true;
return false;
}
private string GetCurrentPCRBStep(int step) {
if (step < 0 || step > (PCRB.Stages.Length - 1)) return string.Empty;
else return PCRB.Stages[step];
}
}

File diff suppressed because it is too large Load Diff

View File

@ -30,7 +30,7 @@
<HeaderContent>
<MudTh>
<MudTableSortLabel InitialDirection="SortDirection.Descending" SortBy="new Func<PCRB,object>(x=>x.PlanNumber)">
Plan#
Change#
</MudTableSortLabel>
</MudTh>
<MudTh>
@ -43,6 +43,7 @@
Owner
</MudTableSortLabel>
</MudTh>
<MudTh>Stage</MudTh>
<MudTh>
<MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.InsertTimeStamp)">
Submitted Date
@ -65,6 +66,7 @@
</MudTd>
<MudTd DataLabel="Title">@context.Title</MudTd>
<MudTd DataLabel="Owner">@context.OwnerName</MudTd>
<MudTd DataLabel="Stage">@(GetStageName(context.CurrentStep))</MudTd>
<MudTd DataLabel="Submitted Date">@DateTimeUtilities.GetDateAsStringMinDefault(context.InsertTimeStamp)</MudTd>
<MudTd DataLabel="Last Updated">@DateTimeUtilities.GetDateAsStringMinDefault(context.LastUpdateDate)</MudTd>
<MudTd DataLabel="Closed Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.ClosedDate)</MudTd>
@ -116,4 +118,9 @@
cache.Set("redirectUrl", page);
navigationManager.NavigateTo(page);
}
private string GetStageName(int step) {
if (step >= PCRB.Stages.Length || step < 0) return "";
return PCRB.Stages[step];
}
}

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,9 @@ WebAssemblyHostBuilder builder = WebAssemblyHostBuilder.CreateDefault(args);
string _apiBaseUrl = builder.Configuration["FabApprovalApiBaseUrl"] ??
throw new NullReferenceException("FabApprovalApiBaseUrl not found in config");
string _oldSiteUrl = builder.Configuration["OldFabApprovalUrl"] ??
throw new NullReferenceException("OldFabApprovalUrl not found in config");
builder.Services.AddTransient<ApiHttpClientHandler>();
builder.Services
@ -30,6 +33,12 @@ builder.Services
})
.AddHttpMessageHandler<ApiHttpClientHandler>();
builder.Services
.AddHttpClient("OldSite", client => {
client.BaseAddress = new Uri(_oldSiteUrl);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
});
builder.Services.AddMemoryCache();
builder.Services.AddMudServices(config => {

View File

@ -289,7 +289,7 @@ public class ApprovalService : IApprovalService {
if (members is null || members.Count() <= 0)
throw new Exception($"unable to find group members for sub role {subRoleId}");
_cache.Set($"approvalMembers{subRoleId}", members, DateTimeOffset.Now.AddMinutes(15));
_cache.Set($"approvalMembers{subRoleId}", members, DateTimeOffset.Now.AddMinutes(2));
} else {
throw new Exception($"Unable to get group members, because {responseMessage.ReasonPhrase}");
}

View File

@ -165,9 +165,7 @@ public class AuthenticationService : IAuthenticationService {
await _localStorageService.AddItem("MesaFabApprovalUserId", loginId);
}
public async Task SetCurrentUser(User user) {
if (user is null) throw new ArgumentNullException("User cannot be null");
public async Task SetCurrentUser(User? user) {
_cache.Set<User>("MesaFabApprovalCurrentUser", user);
await _localStorageService.AddItem<User>("MesaFabApprovalCurrentUser", user);
}
@ -182,8 +180,10 @@ public class AuthenticationService : IAuthenticationService {
public async Task<User> GetCurrentUser() {
User? currentUser = null;
currentUser = _cache.Get<User>("MesaFabApprovalCurrentUser") ??
await _localStorageService.GetItem<User>("MesaFabApprovalCurrentUser");
currentUser = _cache.Get<User>("MesaFabApprovalCurrentUser");
if (currentUser is null)
currentUser = await _localStorageService.GetItem<User>("MesaFabApprovalCurrentUser");
return currentUser;
}

View File

@ -46,6 +46,16 @@ public class MesaFabApprovalAuthStateProvider : AuthenticationStateProvider, IDi
CurrentUser = await _authService.GetCurrentUser();
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(principal)));
if (CurrentUser is null) {
string loginId = _userService.GetLoginIdFromClaimsPrincipal(principal);
User? user = await _userService.GetUserByLoginId(loginId);
await _authService.SetCurrentUser(user);
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(principal)));
}
}
public async Task LoginAsync(string loginId, string password) {

View File

@ -1,8 +1,11 @@
using System.Text;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using MesaFabApproval.Shared.Models;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Caching.Memory;
using MudBlazor;
@ -11,28 +14,52 @@ namespace MesaFabApproval.Client.Services;
public interface IPCRBService {
Task<string> IdIsValid(string id);
Task<bool> IdIsValid(int id);
Task<IEnumerable<PCRB>> GetAllPCRBs(bool bypassCache);
Task CreateNewPCRB(PCRB pcrb);
Task<PCRB> GetPCRBByPlanNumber(int planNumber, bool bypassCache);
Task<PCRB> GetPCRBByTitle(string title, bool bypassCache);
Task UpdatePCRB(PCRB pcrb);
Task DeletePCRB(int planNumber);
Task UploadAttachment(PCRBAttachment attachment);
Task<IEnumerable<PCRBAttachment>> GetAttachmentsByPlanNumber(int planNumber, bool bypassCache);
Task UpdateAttachment(PCRBAttachment attachment);
Task DeleteAttachment(PCRBAttachment attachment);
Task CreateNewActionItem(PCRBActionItem actionItem);
Task UpdateActionItem(PCRBActionItem actionItem);
Task DeleteActionItem(int id);
Task<IEnumerable<PCRBActionItem>> GetActionItemsForPlanNumber(int planNumber, bool bypassCache);
Task CreateNewAttendee(PCRBAttendee attendee);
Task UpdateAttendee(PCRBAttendee attendee);
Task DeleteAttendee(int id);
Task<IEnumerable<PCRBAttendee>> GetAttendeesByPlanNumber(int planNumber, bool bypassCache);
Task CreatePCR3Document(PCR3Document document);
Task UpdatePCR3Document(PCR3Document document);
Task<IEnumerable<PCR3Document>> GetPCR3DocumentsForPlanNumber(int planNumber, bool bypassCache);
Task NotifyNewApprovals(PCRB pcrb);
Task NotifyApprovers(PCRBNotification notification);
Task NotifyOriginator(PCRBNotification notification);
Task NotifyResponsiblePerson(PCRBActionItemNotification notification);
}
public class PCRBService : IPCRBService {
private readonly IMemoryCache _cache;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ISnackbar _snackbar;
private readonly IUserService _userService;
public PCRBService(IMemoryCache cache, IHttpClientFactory httpClientFactory, ISnackbar snackbar) {
public PCRBService(IMemoryCache cache,
IHttpClientFactory httpClientFactory,
ISnackbar snackbar,
IUserService userService) {
_cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected");
_httpClientFactory = httpClientFactory ?? throw new ArgumentNullException("IHttpClientFactory not injected");
_snackbar = snackbar ?? throw new ArgumentNullException("ISnackbar not injected");
_userService = userService ?? throw new ArgumentNullException("IUserService not injected");
}
public async Task<string> IdIsValid(string id) {
bool isMatch = true;
if (string.IsNullOrWhiteSpace(id)) isMatch = false;
try {
@ -63,6 +90,37 @@ public class PCRBService : IPCRBService {
return null;
}
public async Task<bool> IdIsValid(int id) {
bool isMatch = true;
if (id <= 0) isMatch = false;
try {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"pcrb/getByPlanNumber?planNumber={id}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
PCRB? pcrb = JsonSerializer.Deserialize<PCRB>(responseContent, jsonSerializerOptions);
if (pcrb is null) isMatch = false;
} else {
isMatch = false;
}
} catch (Exception) {
isMatch = false;
}
return isMatch;
}
public async Task<IEnumerable<PCRB>> GetAllPCRBs(bool bypassCache) {
try {
IEnumerable<PCRB>? allPCRBs = null;
@ -219,7 +277,7 @@ public class PCRBService : IPCRBService {
}
public async Task UpdatePCRB(PCRB pcrb) {
if (pcrb is null) throw new ArgumentNullException("MRB cannot be null");
if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
@ -251,7 +309,7 @@ public class PCRBService : IPCRBService {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Delete, $"pcrb/delete?planNumber={planNumber}");
HttpRequestMessage requestMessage = new(HttpMethod.Delete, $"pcrb?planNumber={planNumber}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
@ -260,4 +318,450 @@ public class PCRBService : IPCRBService {
IEnumerable<PCRB> allPCRBs = await GetAllPCRBs(true);
_cache.Set("allPCRBs", allPCRBs);
}
public async Task UploadAttachment(PCRBAttachment attachment) {
if (attachment is null) throw new ArgumentNullException("attachment cannot be null");
if (attachment.File is null) throw new ArgumentNullException("file cannot be null");
if (attachment.File.Size <= 0) throw new ArgumentException("file size must be greater than zero");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/attachment");
using MultipartFormDataContent content = new MultipartFormDataContent();
try {
long maxFileSize = 1024L * 1024L * 1024L * 2L;
StreamContent fileContent = new StreamContent(attachment.File.OpenReadStream(maxFileSize));
FileExtensionContentTypeProvider contentTypeProvider = new FileExtensionContentTypeProvider();
const string defaultContentType = "application/octet-stream";
if (!contentTypeProvider.TryGetContentType(attachment.File.Name, out string? contentType)) {
contentType = defaultContentType;
}
fileContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);
content.Add(content: fileContent, name: "\"file\"", fileName: attachment.File.Name);
} catch (Exception ex) {
_snackbar.Add(ex.Message);
}
content.Add(new StringContent(attachment.PlanNumber.ToString()), "PlanNumber");
content.Add(new StringContent(attachment.FileName), "FileName");
content.Add(new StringContent(attachment.UploadedByID.ToString()), "UploadedByID");
content.Add(new StringContent(attachment.Title), "Title");
content.Add(new StringContent(attachment.UploadDateTime.ToString("yyyy-MM-dd HH:mm:ss")), "UploadDateTime");
content.Add(new StringContent(attachment.Step.ToString()), "Step");
requestMessage.Content = content;
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception(responseMessage.ReasonPhrase);
await GetAttachmentsByPlanNumber(attachment.PlanNumber, true);
}
public async Task<IEnumerable<PCRBAttachment>> GetAttachmentsByPlanNumber(int planNumber, bool bypassCache) {
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#");
IEnumerable<PCRBAttachment>? attachments = null;
if (!bypassCache)
attachments = _cache.Get<IEnumerable<PCRBAttachment>>($"pcrbAttachments{planNumber}");
if (attachments is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"pcrb/attachments?planNumber={planNumber}&bypassCache={bypassCache}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
attachments = JsonSerializer.Deserialize<IEnumerable<PCRBAttachment>>(responseContent, jsonSerializerOptions) ??
new List<PCRBAttachment>();
if (attachments.Count() > 0) {
foreach (PCRBAttachment attachment in attachments) {
attachment.UploadedBy = await _userService.GetUserByUserId(attachment.UploadedByID);
}
_cache.Set($"pcrbAttachments{planNumber}", attachments, DateTimeOffset.Now.AddMinutes(5));
}
} else {
throw new Exception(responseMessage.ReasonPhrase);
}
}
return attachments;
}
public async Task UpdateAttachment(PCRBAttachment attachment) {
if (attachment is null) throw new ArgumentNullException("attachment cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Put, $"pcrb/attachment");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(attachment),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception(responseMessage.ReasonPhrase);
}
public async Task DeleteAttachment(PCRBAttachment attachment) {
if (attachment is null) throw new ArgumentNullException("attachment cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Delete, $"pcrb/attachment");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(attachment),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception(responseMessage.ReasonPhrase);
}
public async Task CreateNewActionItem(PCRBActionItem actionItem) {
if (actionItem is null) throw new ArgumentNullException("action item cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/actionItem");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(actionItem),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception(responseMessage.ReasonPhrase);
await GetActionItemsForPlanNumber(actionItem.PlanNumber, true);
}
public async Task UpdateActionItem(PCRBActionItem actionItem) {
if (actionItem is null) throw new ArgumentNullException("action item cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Put, $"pcrb/actionItem");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(actionItem),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception(responseMessage.ReasonPhrase);
}
public async Task DeleteActionItem(int id) {
if (id <= 0) throw new ArgumentException($"{id} is not a valid PCRB action item ID");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Delete, $"pcrb/actionItem?id={id}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode) throw new Exception(responseMessage.ReasonPhrase);
}
public async Task<IEnumerable<PCRBActionItem>> GetActionItemsForPlanNumber(int planNumber, bool bypassCache) {
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#");
IEnumerable<PCRBActionItem>? actionItems = null;
if (!bypassCache)
actionItems = _cache.Get<IEnumerable<PCRBActionItem>>($"pcrbActionItems{planNumber}");
if (actionItems is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"pcrb/actionItems?planNumber={planNumber}&bypassCache={bypassCache}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
actionItems = JsonSerializer.Deserialize<IEnumerable<PCRBActionItem>>(responseContent, jsonSerializerOptions) ??
new List<PCRBActionItem>();
if (actionItems.Count() > 0) {
foreach (PCRBActionItem actionItem in actionItems) {
actionItem.UploadedBy = await _userService.GetUserByUserId(actionItem.UploadedByID);
if (actionItem.ResponsiblePersonID > 0)
actionItem.ResponsiblePerson = await _userService.GetUserByUserId(actionItem.ResponsiblePersonID);
if (actionItem.ClosedByID > 0)
actionItem.ClosedBy = await _userService.GetUserByUserId(actionItem.ClosedByID);
}
_cache.Set($"pcrbActionItems{planNumber}", actionItems, DateTimeOffset.Now.AddMinutes(5));
}
} else {
throw new Exception(responseMessage.ReasonPhrase);
}
}
return actionItems;
}
public async Task CreateNewAttendee(PCRBAttendee attendee) {
if (attendee is null) throw new ArgumentNullException("attendee cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/attendee");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(attendee),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception(responseMessage.ReasonPhrase);
await GetAttendeesByPlanNumber(attendee.PlanNumber, true);
}
public async Task UpdateAttendee(PCRBAttendee attendee) {
if (attendee is null) throw new ArgumentNullException("attendee cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Put, $"pcrb/attendee");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(attendee),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception(responseMessage.ReasonPhrase);
}
public async Task DeleteAttendee(int id) {
if (id <= 0) throw new ArgumentException($"{id} is not a valid PCRB attendee ID");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Delete, $"pcrb/attendee?id={id}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode) throw new Exception(responseMessage.ReasonPhrase);
}
public async Task<IEnumerable<PCRBAttendee>> GetAttendeesByPlanNumber(int planNumber, bool bypassCache) {
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#");
IEnumerable<PCRBAttendee>? attendees = null;
if (!bypassCache)
attendees = _cache.Get<IEnumerable<PCRBAttendee>>($"pcrbAttendees{planNumber}");
if (attendees is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"pcrb/attendees?planNumber={planNumber}&bypassCache={bypassCache}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
attendees = JsonSerializer.Deserialize<IEnumerable<PCRBAttendee>>(responseContent, jsonSerializerOptions) ??
new List<PCRBAttendee>();
if (attendees.Count() > 0) {
foreach (PCRBAttendee attendee in attendees)
attendee.Attendee = await _userService.GetUserByUserId(attendee.AttendeeID);
_cache.Set($"pcrbAttendees{planNumber}", attendees, DateTimeOffset.Now.AddMinutes(5));
}
} else {
throw new Exception(responseMessage.ReasonPhrase);
}
}
return attendees;
}
public async Task CreatePCR3Document(PCR3Document document) {
if (document is null) throw new ArgumentNullException("document cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/pcr3Document");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(document),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception(responseMessage.ReasonPhrase);
await GetPCR3DocumentsForPlanNumber(document.PlanNumber, true);
}
public async Task UpdatePCR3Document(PCR3Document document) {
if (document is null) throw new ArgumentNullException("document cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Put, $"pcrb/pcr3Document");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(document),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception(responseMessage.ReasonPhrase);
}
public async Task<IEnumerable<PCR3Document>> GetPCR3DocumentsForPlanNumber(int planNumber, bool bypassCache) {
if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#");
IEnumerable<PCR3Document>? documents = null;
if (!bypassCache)
documents = _cache.Get<IEnumerable<PCR3Document>>($"pcr3Documents{planNumber}");
if (documents is null) {
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Get, $"pcrb/pcr3Documents?planNumber={planNumber}&bypassCache={bypassCache}");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (responseMessage.IsSuccessStatusCode) {
string responseContent = await responseMessage.Content.ReadAsStringAsync();
JsonSerializerOptions jsonSerializerOptions = new() {
PropertyNameCaseInsensitive = true
};
documents = JsonSerializer.Deserialize<IEnumerable<PCR3Document>>(responseContent, jsonSerializerOptions) ??
new List<PCR3Document>();
if (documents.Count() > 0) {
foreach (PCR3Document document in documents) {
if (document.CompletedByID > 0)
document.CompletedBy = await _userService.GetUserByUserId(document.CompletedByID);
}
_cache.Set($"pcr3Documents{planNumber}", documents, DateTimeOffset.Now.AddMinutes(5));
}
} else {
throw new Exception(responseMessage.ReasonPhrase);
}
}
return documents;
}
public async Task NotifyNewApprovals(PCRB pcrb) {
if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/notify/new-approvals");
requestMessage.Content = new StringContent(JsonSerializer.Serialize(pcrb),
Encoding.UTF8,
"application/json");
HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage);
if (!responseMessage.IsSuccessStatusCode)
throw new Exception($"Unable to notify new PCRB approvers, because {responseMessage.ReasonPhrase}");
}
public async Task NotifyApprovers(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 (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/approvers");
requestMessage.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 approvers, because {responseMessage.ReasonPhrase}");
}
public async Task NotifyOriginator(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 (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/originator");
requestMessage.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 originator, because {responseMessage.ReasonPhrase}");
}
public async Task NotifyResponsiblePerson(PCRBActionItemNotification notification) {
if (notification is null) throw new ArgumentNullException("notification cannot be null");
if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty");
HttpClient httpClient = _httpClientFactory.CreateClient("API");
HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/notify/responsiblePerson");
requestMessage.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 responsible person, because {responseMessage.ReasonPhrase}");
}
}

View File

@ -21,7 +21,7 @@ public class MRB {
public DateTime ApprovalDate { get; set; } = DateTimeUtilities.MAX_DT;
public string IssueDescription { get; set; } = "";
public int NumberOfLotsAffected { get; set; }
public int Val { get; set; }
public double Val { get; set; }
public bool CustomerImpacted { get; set; } = false;
public string CustomerImpactedName { get; set; } = "";
public string Department { get; set; } = "";

View File

@ -0,0 +1,20 @@
using MesaFabApproval.Shared.Utilities;
namespace MesaFabApproval.Shared.Models;
public class PCR3Document {
public int ID { get; set; }
public required int PlanNumber { get; set; }
public required string DocType { get; set; }
public string DocNumbers { get; set; } = "N/A";
public DateTime CompletedDate { get; set; } = DateTimeUtilities.MAX_DT;
public int CompletedByID { get; set; } = 0;
public User? CompletedBy { get; set; }
public string Comment { get; set; } = string.Empty;
public int ECNNumber { get; set; } = 0;
public string GetEcnNumberString() {
if (this.ECNNumber > 0) return this.ECNNumber.ToString();
return string.Empty;
}
}

View File

@ -5,7 +5,6 @@ namespace MesaFabApproval.Shared.Models;
public class PCRB {
public static string[] Stages { get; } = {
"Draft",
"QA Pre Approval",
"PCR1",
"PCR2",
"PCR3",
@ -16,7 +15,7 @@ public class PCRB {
public int OwnerID { get; set; }
public string OwnerName { get; set; } = "";
public string Title { get; set; } = "";
public string ChangeLevel { get; set; } = "Mesa";
public string ChangeLevel { get; set; } = "Mesa - Class 3";
public bool IsITAR { get; set; } = false;
public int CurrentStep { get; set; } = 0;
public string ReasonForChange { get; set; } = "";

View File

@ -0,0 +1,21 @@
using MesaFabApproval.Shared.Utilities;
namespace MesaFabApproval.Shared.Models;
public class PCRBActionItem {
public int ID { get; set; }
public required string Name { get; set; }
public bool Gating { get; set; } = false;
public bool ClosedStatus { get; set; } = false;
public DateTime? ClosedDate { get; set; } = DateTimeUtilities.MAX_DT;
public int ClosedByID { get; set; } = 0;
public User? ClosedBy { get; set; }
public required int UploadedByID { get; set; }
public User? UploadedBy { get; set; }
public DateTime UploadedDateTime { get; set; } = DateTime.Now;
public int ResponsiblePersonID { get; set; } = 0;
public User? ResponsiblePerson { get; set; }
public required int PlanNumber { get; set; }
public required int Step { get; set; }
public DateTime NotifyDate { get; set; } = DateTimeUtilities.MIN_DT;
}

View File

@ -0,0 +1,7 @@
namespace MesaFabApproval.Shared.Models;
public class PCRBActionItemNotification {
public required PCRB PCRB { get; set; }
public required PCRBActionItem ActionItem { get; set; }
public required string Message { get; set; }
}

View File

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Components.Forms;
namespace MesaFabApproval.Shared.Models;
public class PCRBAttachment {
public int ID { get; set; }
public required int PlanNumber { get; set; }
public required string FileName { get; set; }
public required int UploadedByID { get; set; }
public User? UploadedBy { get; set; }
public string Title { get; set; } = "NA";
public required DateTime UploadDateTime { get; set; }
public string? Path { get; set; }
public IBrowserFile? File { get; set; }
public required int Step { get; set; }
}

View File

@ -0,0 +1,12 @@
namespace MesaFabApproval.Shared.Models;
public class PCRBAttendee {
public int ID { get; set; }
public required int PlanNumber { get; set; }
public string JobTitle { get; set; } = "";
public string Location { get; set; } = "Mesa";
public bool Attended { get; set; } = false;
public required int AttendeeID { get; set; }
public User? Attendee { get; set; }
public required int Step { get; set; }
}

View File

@ -0,0 +1,6 @@
namespace MesaFabApproval.Shared.Models;
public class PCRBNotification {
public required string Message { get; set; }
public required PCRB PCRB { get; set; }
}

View File

@ -3,7 +3,7 @@
public class Process {
public required string Name { get; set; } = "";
private static readonly Process Receiving = new Process { Name="Receiving" };
private static readonly Process Receiving = new Process { Name = "Receiving" };
private static readonly Process Kitting = new Process { Name = "Kitting" };
private static readonly Process Cleans = new Process { Name = "Cleans" };
private static readonly Process Reactor = new Process { Name = "Reactor" };
@ -15,8 +15,10 @@ public class Process {
private static readonly Process Conversion = new Process { Name = "Conversion" };
private static readonly Process RMA = new Process { Name = "RMA" };
private static readonly Process CustomerCompliant = new Process { Name = "Customer Compliant" };
private static readonly Process Nontransferrable = new Process { Name = "Nontransferrable" };
private static readonly Process PartConversion = new Process { Name = "Part Conversion" };
public static IEnumerable<Process> ProductionProcesses = new HashSet<Process> {
public static IEnumerable<Process> ProductionProcesses = new HashSet<Process> {
Cleans,
Reactor,
Metrology,
@ -51,6 +53,8 @@ public class Process {
public static IEnumerable<Process> QualityProcesses = new HashSet<Process> {
RMA,
CustomerCompliant
CustomerCompliant,
Nontransferrable,
PartConversion
};
}

View File

@ -1,9 +1,4 @@
using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
namespace MesaFabApproval.Shared.Models;
namespace MesaFabApproval.Shared.Models;
public class SubRole {
public int SubRoleID { get; set; }

View File

@ -53,7 +53,7 @@ public class MonInWorkerClient : IMonInWorkerClient {
throw new ArgumentException("MonInBackoffSeconds environment variable not an integer");
_resource = Environment.GetEnvironmentVariable("FabApprovalApiMonInResource") ??
throw new ArgumentNullException("OIWizardMonInResource environment variable not found");
throw new ArgumentNullException("FabApprovalApiMonInResource environment variable not found");
}
public async void PostStatus(string statusName, StatusValue statusValue) {

View File

@ -8,7 +8,17 @@ public class DateTimeUtilities {
return dt > MIN_DT ? dt.ToString("yyyy-MM-dd HH:mm") : "";
}
public static string GetDateAsStringMinDefault(DateTime? dt) {
DateTime copy = dt ?? MIN_DT;
return copy > MIN_DT ? copy.ToString("yyyy-MM-dd HH:mm") : "";
}
public static string GetDateAsStringMaxDefault(DateTime dt) {
return dt < MAX_DT ? dt.ToString("yyyy-MM-dd HH:mm") : "";
}
public static string GetDateAsStringMaxDefault(DateTime? dt) {
DateTime copy = dt ?? MAX_DT;
return copy < MAX_DT ? copy.ToString("yyyy-MM-dd HH:mm") : "";
}
}