using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using Fab2ApprovalSystem.DMO; using Fab2ApprovalSystem.Misc; using Fab2ApprovalSystem.Models; using Kendo.Mvc.Extensions; using Kendo.Mvc.UI; namespace Fab2ApprovalSystem.Controllers; [Authorize] [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")] [SessionExpireFilter] public class PartsRequestController : Controller { const int WorkflowNumber = 1; PartsRequestDMO prDMO = new PartsRequestDMO(); UserAccountDMO userDMO = new UserAccountDMO(); WorkflowDMO wfDMO = new WorkflowDMO(); private readonly AppSettings _AppSettings = GlobalVars.AppSettings; public PartsRequestController() { ViewBag.ShowReAssignApprovers = false; } protected ActionResult HandleValidationError(string msg) { Response.StatusCode = (int)System.Net.HttpStatusCode.BadRequest; return Json(new { result = "Invalid", detail = msg }); } protected ActionResult HandleAPIException(int issueID, Exception ex, string additionalKeys = "") { HandleException(issueID, ex, additionalKeys); Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError; return Json(new { result = "Error", issueID = issueID, detail = ex.Message }); } protected void HandleException(int issueID, Exception ex, string additionalKeys = "") { var st = new System.Diagnostics.StackTrace(); var sf = st.GetFrame(1); String controller = sf.GetMethod().DeclaringType.Name.Replace("Controller", ""); String method = sf.GetMethod().Name; string detailedException = String.Format( "Exception for issue # {0}\r\n" + "Controller: {1}, Method: {2}, User: {3}, Keys: {4}\r\n" + "=====\r\n", issueID, controller, method, User?.Identity?.Name, additionalKeys); Exception x = ex; while (x != null) { detailedException += x.ToString(); detailedException += "\r\n=====\r\n"; x = x.InnerException; } Functions.WriteEvent(_AppSettings, detailedException, System.Diagnostics.EventLogEntryType.Error); EventLogDMO.Add(new WinEventLog() { IssueID = issueID, UserID = @User.Identity.Name, DocumentType = controller, OperationType = "Error", Comments = detailedException }); } // GET: PartsRequest public ActionResult Index() { return View(); } public ActionResult Edit(int issueID) { var pr = new PartsRequest(); try { pr = prDMO.Get(issueID); if (pr == null) { ViewBag.ErrorDescription = "Document does not exist"; return View("Error"); } else if (pr.CurrentStep < 0) { ViewBag.ErrorDescription = "Document is deleted"; return View("Error"); } else if (pr.CurrentStep >= 1) { return RedirectToAction("EditApproval", new { issueID = issueID }); } else { if (pr.OriginatorID != (int)Session[GlobalVars.SESSION_USERID]) { if (Convert.ToBoolean(Session[GlobalVars.IS_ADMIN]) == false) { return RedirectToAction("ReadOnly", new { issueID = issueID }); } } ViewBag.UserList = userDMO.GetAllUsers(); return View(pr); } } catch (Exception e) { HandleException(issueID, e); return View("Error"); } } [HttpPost] public ActionResult Edit(PartsRequest pr) { try { var pr_srv = prDMO.Get(pr.PRNumber); if (pr_srv == null) { return new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest, "Document does not exist"); } if (pr_srv.CurrentStep < 0) { return new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest, "Document is deleted"); } if (pr_srv.CurrentStep >= 1) { return new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest, "Parts Request is not editable"); } prDMO.Update(pr); return Content(""); } catch (Exception e) { return HandleAPIException(pr.PRNumber, e); } } public ActionResult EditApproval(int issueID) { var pr = new PartsRequest(); try { int myUserID = (int)Session[GlobalVars.SESSION_USERID]; pr = prDMO.Get(issueID); if (pr == null) { ViewBag.ErrorDescription = "Document does not exist"; return View("Error"); } if (pr.CurrentStep < 0) { ViewBag.ErrorDescription = "Document is deleted"; return View("Error"); } var wfStep = wfDMO.GetWorkflowStep((int)GlobalVars.DocumentType.PartsRequest, WorkflowNumber, pr.CurrentStep); var userList = MiscDMO.GetPendingApproversListByDocument(issueID, Convert.ToByte(pr.CurrentStep), (int)GlobalVars.DocumentType.PartsRequest); ViewBag.IsApprover = (userList.Count(u => u.UserID == myUserID) > 0); if (ViewBag.IsApprover == false) { if (pr.OriginatorID != myUserID) { if (Convert.ToBoolean(Session[GlobalVars.IS_ADMIN]) == false) { return RedirectToAction("ReadOnly", new { issueID = issueID }); } } } ViewBag.IsAdmin = Convert.ToBoolean(Session[GlobalVars.IS_ADMIN]); ViewBag.IsOriginator = (pr.OriginatorID == myUserID); ViewBag.AllowReject = (wfStep != null ? (wfStep.AllowReject.HasValue && wfStep.AllowReject.Value) : false); ViewBag.ShowReAssignApprovers = ViewBag.IsAdmin || ViewBag.IsOriginator; ViewBag.ShowAddApprovers = ViewBag.IsAdmin || ViewBag.IsApprover || ViewBag.IsOriginator; ViewBag.UserList = userDMO.GetAllUsers(); return View(pr); } catch (Exception e) { HandleException(issueID, e); return View("Error"); } } public ActionResult ReadOnly(int issueID) { var pr = new PartsRequest(); try { int myUserID = (int)Session[GlobalVars.SESSION_USERID]; pr = prDMO.Get(issueID); if (pr == null) { ViewBag.ErrorDescription = "Document does not exist"; return View("Error"); } if (pr.CurrentStep < 0) { ViewBag.ErrorDescription = "Document is deleted"; return View("Error"); } ViewBag.IsAdmin = Convert.ToBoolean(Session[GlobalVars.IS_ADMIN]); ViewBag.IsOriginator = (pr.OriginatorID == myUserID); ViewBag.UserList = userDMO.GetAllUsers(); return View(pr); } catch (Exception e) { HandleException(issueID, e); return View("Error"); } } public ActionResult Create() { var pr = new PartsRequest(); try { pr.OriginatorID = (int)Session[GlobalVars.SESSION_USERID]; if (!CanCreatePartsRequest(pr.OriginatorID) && Convert.ToBoolean(Session[GlobalVars.CAN_CREATE_PARTS_REQUEST]) == false) throw new Exception("User does not have permission to create Parts Request"); prDMO.Insert(pr); return RedirectToAction("Edit", new { issueID = pr.PRNumber }); } catch (Exception e) { return HandleAPIException(pr.PRNumber, e); } } public static bool CanCreatePartsRequest(int userID) { var adminDMO = new AdminDMO(); var role = adminDMO.GetSubRoles().Where(r => string.Equals(r.RoleName, "Parts Request", StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); if (role != null) { var subrole = role.SubRoles.FirstOrDefault(sr => string.Equals(sr.SubRoleCategoryItem, "Originator", StringComparison.OrdinalIgnoreCase)); if (subrole != null) { var users = adminDMO.GetAllUsersBySubRole(subrole.SubRoleID); if (users.Count(u => u.UserID == userID) > 0) { return true; } } } return false; } public ActionResult Attachment_Read([DataSourceRequest] DataSourceRequest request, int prNumber) { try { return Json(prDMO.GetAttachments(prNumber).ToDataSourceResult(request), JsonRequestBehavior.AllowGet); } catch (Exception ex) { return HandleAPIException(prNumber, ex); } } [HttpPost] public ActionResult AttachSave(IEnumerable files, int prNumber) { // The Name of the Upload component is "files" if (files != null) { int userId = (int)Session[GlobalVars.SESSION_USERID]; foreach (var file in files) { PartsRequestHelper.AttachSave(_AppSettings, prDMO, prNumber, userId, file.FileName, file.InputStream); } } return Content(""); } public FileResult DownloadFile(string attachmentID, string prNumber) { string fileName = prDMO.GetFileName(attachmentID); string folderPath = _AppSettings.AttachmentFolder + "PartsRequest\\" + prNumber.ToString(); var sDocument = System.IO.Path.Combine(folderPath, fileName); var FDir_AppData = _AppSettings.AttachmentFolder; if (!sDocument.StartsWith(FDir_AppData)) { // Ensure that we are serving file only inside the Fab2ApprovalAttachments folder // and block requests outside like "../web.config" throw new HttpException(403, "Forbidden"); } if (!System.IO.File.Exists(sDocument)) return null; return File(sDocument, System.Net.Mime.MediaTypeNames.Application.Octet, fileName); } [HttpPost] public ActionResult DeleteAttachment(int attachmentID, string fileName, int prNumber) { try { if (ModelState.IsValid) { prDMO.DeleteAttachment(attachmentID); var physicalPath = System.IO.Path.Combine(_AppSettings.AttachmentFolder + @"PartsRequest\" + prNumber.ToString(), fileName); if (System.IO.File.Exists(physicalPath)) { System.IO.File.Delete(physicalPath); } } return Content(""); } catch (Exception e) { return HandleAPIException(prNumber, e, "AttachmentID=" + attachmentID.ToString()); } } [HttpPost] public ActionResult Submit(int prNumber) { try { int myUserID = (int)Session[GlobalVars.SESSION_USERID]; var pr = prDMO.Get(prNumber); if (pr == null) return new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest, "Document does not exist"); if (String.IsNullOrWhiteSpace(pr.Title)) return HandleValidationError("Title is required"); if (pr.RequestorID <= 0) return HandleValidationError("Requestor is required"); if (pr.TechLeadID <= 0) return HandleValidationError("Tech Lead is required"); prDMO.Submit(prNumber, myUserID); var approvers = MiscDMO.GetApprovalsByDocument(prNumber, (int)GlobalVars.DocumentType.PartsRequest); if (approvers.Count(a => a.Step.HasValue && a.Step.Value == 1 && a.UserID == myUserID) > 0) { // Auto-Approve if the user is an approver for workflow step 1 var c = Approve(prNumber, 1, "Auto-Approve"); if (c != null && c is ContentResult) { var result = ((ContentResult)c).Content; if (!String.Equals(result, "OK")) throw new Exception(result); } if (c != null && c is JsonResult) return c; } else { // Do step 1 notification NotifyApprovers(prNumber, 1); } if (Request.IsAjaxRequest()) { return Content("Redirect"); } else { return Content("Invalid"); } } catch (Exception e) { return HandleAPIException(prNumber, e); } } public ActionResult GetApproversList([DataSourceRequest] DataSourceRequest request, int issueID, byte step) { try { return Json(MiscDMO.GetApproversListByDocument(issueID, step, (int)GlobalVars.DocumentType.PartsRequest).ToDataSourceResult(request)); } catch (Exception e) { return HandleAPIException(issueID, e, "Step=" + step.ToString()); } } public ActionResult GetAllUsersList() { try { UserAccountDMO userDMO = new UserAccountDMO(); IEnumerable userlist = userDMO.GetAllUsers(); return Json(userlist, JsonRequestBehavior.AllowGet); } catch (Exception e) { return HandleAPIException(0, e); } } [HttpPost] public ActionResult ReAssignApproval(int issueID, int fromUserID, int userIDs, byte step) { try { String email = wfDMO.ReAssignApproval( issueID, fromUserID, userIDs, step, (int)GlobalVars.DocumentType.PartsRequest); NotifyReAssignment(issueID, email); return Content("OK"); } catch (Exception e) { return HandleAPIException(issueID, e); } } [HttpPost] public ActionResult Approve(int prNumber, byte currentStep, string comments) { try { bool lastStep = false; bool lastApproverInCurrentStep = false; int myUserID = (int)Session[GlobalVars.SESSION_USERID]; var pr = prDMO.Get(prNumber); if (pr == null) return new HttpStatusCodeResult(System.Net.HttpStatusCode.BadRequest, "Document does not exist"); while (true) { lastApproverInCurrentStep = wfDMO.Approve( _AppSettings, prNumber, currentStep, comments, out lastStep, (int)Session[GlobalVars.SESSION_USERID], (int)GlobalVars.DocumentType.PartsRequest, WorkflowNumber); if (!lastApproverInCurrentStep) break; if (lastStep) { NotifyCompletion(prNumber); break; } currentStep++; var approvers = MiscDMO.GetApprovalsByDocument(prNumber, (int)GlobalVars.DocumentType.PartsRequest); if (approvers.Count(a => a.Step.HasValue && a.Step.Value == currentStep) == 0) return Content("No approvers found for next step, contact support!"); // only continue with approving if the next step has me as an approver also if (approvers.Count(a => a.Step.HasValue && a.Step.Value == currentStep && a.UserID == myUserID) == 0) { NotifyApprovers(prNumber, currentStep); break; } } return Content("OK"); } catch (Exception e) { return HandleAPIException(prNumber, e); } } protected void NotifyReAssignment(int prNumber, string email) { var pr = prDMO.Get(prNumber); if (pr == null) return; string username = Session[GlobalVars.SESSION_USERNAME].ToString(); PartsRequestHelper.SendEmailNotification(_AppSettings, username, subject: String.Format("Parts Request Re-Assignment notice for # {0} - {1}", pr.PRNumber, pr.Title), prNumber: prNumber, toEmail: email, emailTemplate: "PRReAssigned.txt"); EventLogDMO.Add(new WinEventLog() { IssueID = prNumber, UserID = @User.Identity.Name, DocumentType = "PR", OperationType = "Email", Comments = "ReAssigned Approver: " + email }); } protected void NotifyCompletion(int prNumber) { var pr = prDMO.Get(prNumber); if (pr == null) return; var u = userDMO.GetUserByID(pr.RequestorID); if ((u != null) && (!String.IsNullOrWhiteSpace(u.Email))) { string username = Session[GlobalVars.SESSION_USERNAME].ToString(); PartsRequestHelper.SendEmailNotification(_AppSettings, username, subject: String.Format("Parts Request Completion notice for # {0} - {1}", pr.PRNumber, pr.Title), prNumber: prNumber, toEmail: u.Email, emailTemplate: "PRCompleted.txt"); EventLogDMO.Add(new WinEventLog() { IssueID = prNumber, UserID = @User.Identity.Name, DocumentType = "PR", OperationType = "Email", Comments = "Completed: " + u.Email }); } } public void NotifyRejection(int prNumber) { var pr = prDMO.Get(prNumber); if (pr == null) return; var u = userDMO.GetUserByID(pr.OriginatorID); if ((u != null) && (!String.IsNullOrWhiteSpace(u.Email))) { string username = Session[GlobalVars.SESSION_USERNAME].ToString(); PartsRequestHelper.SendEmailNotification(_AppSettings, username, subject: String.Format("Parts Request Rejection notice for # {0} - {1}", pr.PRNumber, pr.Title), prNumber: prNumber, toEmail: u.Email, emailTemplate: "PRReject.txt"); EventLogDMO.Add(new WinEventLog() { IssueID = prNumber, UserID = @User.Identity.Name, DocumentType = "PR", OperationType = "Email", Comments = "Rejected: " + u.Email }); } } protected void NotifyApprovers(int prNumber, byte step) { try { string emailSentList = ""; var pr = prDMO.Get(prNumber); if (pr == null) throw new Exception("Invalid pr#"); List emailList = MiscDMO.GetApproverEmailListByDocument( prNumber, step, (int)GlobalVars.DocumentType.PartsRequest).Distinct().ToList(); foreach (string email in emailList) { try { string username = Session[GlobalVars.SESSION_USERNAME].ToString(); PartsRequestHelper.SendEmailNotification(_AppSettings, username, subject: String.Format("Parts Request Assignment notice for # {0} - {1}", pr.PRNumber, pr.Title), prNumber: prNumber, toEmail: email, emailTemplate: "PRAssigned.txt"); } catch (Exception ex) { HandleException(prNumber, ex, "email=" + email); } emailSentList += email + ","; } try { EventLogDMO.Add(new WinEventLog() { IssueID = prNumber, UserID = @User.Identity.Name, DocumentType = "PR", OperationType = "Email", Comments = "Approvers for Step " + step.ToString() + ":" + emailSentList }); } catch { } } catch (Exception e) { HandleException(prNumber, e, "Step=" + step.ToString()); } } [HttpPost] public ActionResult Reject(int prNumber, byte currentStep, string comments) { try { if (Session[GlobalVars.SESSION_USERID] != null) { wfDMO.Reject(prNumber, currentStep, comments, (int)Session[GlobalVars.SESSION_USERID], (int)GlobalVars.DocumentType.PartsRequest); NotifyRejection(prNumber); } else { Response.Redirect("~/Account/Login"); } return Content("OK"); } catch (Exception e) { return HandleAPIException(prNumber, e); } } public ActionResult ApprovalLogHistory_Read([DataSourceRequest] DataSourceRequest request, int prNumber) { return Json(prDMO.GetApprovalLogHistory(prNumber).ToDataSourceResult(request), JsonRequestBehavior.AllowGet); } protected void NotifyAssignment(int prNumber, string email) { var pr = prDMO.Get(prNumber); if (pr == null) return; string username = Session[GlobalVars.SESSION_USERNAME].ToString(); PartsRequestHelper.SendEmailNotification(_AppSettings, username, subject: String.Format("Parts Request Assignment notice for # {0} - {1}", pr.PRNumber, pr.Title), prNumber: prNumber, toEmail: email, emailTemplate: "PRAssigned.txt"); EventLogDMO.Add(new WinEventLog() { IssueID = prNumber, UserID = @User.Identity.Name, DocumentType = "PR", OperationType = "Email", Comments = "Assigned Approver: " + email }); } [HttpPost] public void AddAdditionalApproval(int issueID, byte step, string userIDs) { var emailArray = ""; try { emailArray = wfDMO.AddAdditionalApproval(issueID, userIDs, step, (int)GlobalVars.DocumentType.PartsRequest); } catch (Exception e) { HandleAPIException(issueID, e); } string emailSentList = ""; string[] emaiList = emailArray.Split(new char[] { '~' }); foreach (string email in emaiList) { if (email.Length > 0) { NotifyAssignment(issueID, email); emailSentList += email + ","; } } try { EventLogDMO.Add(new WinEventLog() { IssueID = issueID, UserID = @User.Identity.Name, DocumentType = "PR", OperationType = "Email", Comments = "Additional Approver: " + emailSentList }); } catch { } } }