using System.Collections.Generic; using System.Drawing; using System.IO; using DataModel.Models; using OfficeOpenXml; using OfficeOpenXml.Style; using OfficeOpenXml.Table; using WorkerService.Helpers; using WorkerService.Models.Reporting; namespace WorkerService.Process { /// /// Search results report writer /// public class ExcelWriter { /// /// Standard date format /// private const string DATE_FORMAT = "{0:MM/dd/yyyy}"; /// /// Standard timestamp format /// private const string TIMESTAMP_FORMAT = "[$-409]m/d/yy h:mm AM/PM;@"; /// /// Override width for cells with wrapped data /// private const double WRAPPED_CELL_WIDTH = 65; /// /// Generates Excel report for the given search criteria and results /// /// Search model to generate report for /// Excel report file contents public static byte[] Generate(SearchModel searchModel) { byte[] data; using (ExcelPackage package = new ExcelPackage()) { //Write search criteria tab WriteCriteria(package, searchModel); //Write results tab package.Workbook.LoadTab(searchModel.Results); //Write MIS tab if(searchModel.ExtractMisData) { package.Workbook.LoadTab(searchModel.MisResults); } //Write mismatch tab if (searchModel.ExtractMisData) { package.Workbook.LoadTab(searchModel.MisNonMatchResults); } //Save workbook to array using (MemoryStream memoryStream = new MemoryStream()) { package.SaveAs(memoryStream); memoryStream.Position = 0; data = memoryStream.ToArray(); } } return data; } /// /// Writes search criteria to a tab in the worksheet /// /// Excel package to write to /// Search model to generate report for private static void WriteCriteria(ExcelPackage package, SearchModel searchModel) { //Create table in workbook to hold criteria ExcelWorksheet criteriaTab = package.Workbook.Worksheets.Add("Search Criteria"); int row = 1; //Write name and revision number ApplyHeaderFormat(criteriaTab.Cells[row, 1], "Search Name"); criteriaTab.Cells[row, 2].Value = searchModel.Name; ApplyHeaderFormat(criteriaTab.Cells[++row, 1], "User Name"); criteriaTab.Cells[row, 2].Value = searchModel.UserName; //Skip row row++; //Write timestamps ApplyHeaderFormat(criteriaTab.Cells[++row, 1], "Submit timestamp"); criteriaTab.Cells[row, 2].Value = $"{searchModel.SubmitDT:MMM dd, yyyy hh:mm:ss tt} EST"; ApplyHeaderFormat(criteriaTab.Cells[++row, 1], "Start timestamp"); criteriaTab.Cells[row, 2].Value = $"{searchModel.StartDT:MMM dd, yyyy hh:mm:ss tt} EST"; ApplyHeaderFormat(criteriaTab.Cells[++row, 1], "Completed timestamp"); criteriaTab.Cells[row, 2].Value = $"{searchModel.EndDT:MMM dd, yyyy hh:mm:ss tt} EST"; //Skip row row++; /* * Write min/max times */ ExcelTable timespanFilterTable = criteriaTab.Cells[++row, 1].LoadTable( new List() { new TimespanFilter() { MinimumDT = searchModel.MinimumDT, MaximumDT = searchModel.MaximumDT } } ); row = timespanFilterTable.Address.End.Row + 3; /* * Write lot numbers */ ExcelTable workOrderFilterTable = criteriaTab.Cells[row, 1].LoadTable(searchModel.WorkOrderFilter); row = workOrderFilterTable.Address.End.Row +3; /* * Write item numbers */ ExcelTable itemNumberFilterTable = criteriaTab.Cells[row, 1].LoadTable(searchModel.ItemNumberFilter); row = itemNumberFilterTable.Address.End.Row +3; /* * Write profit centers */ ExcelTable profitCenterFilterTable = criteriaTab.Cells[row, 1].LoadTable(searchModel.ProfitCenterFilter); row = profitCenterFilterTable.Address.End.Row + 3; /* * Write work centers */ ExcelTable workCenterFilterTable = criteriaTab.Cells[row, 1].LoadTable(searchModel.WorkCenterFilter); row = workCenterFilterTable.Address.End.Row + 3; /* * Write component lot numbers */ ExcelTable componentLotFilterTable = criteriaTab.Cells[row, 1].LoadTable(searchModel.ComponentLotFilter); row = componentLotFilterTable.Address.End.Row + 3; /* * Write operators */ ExcelTable operatorFilterTable = criteriaTab.Cells[row, 1].LoadTable(searchModel.OperatorFilter); row = operatorFilterTable.Address.End.Row + 3; /* * Write item/operation/mis */ ExcelTable itemOperationMisFilterTable = criteriaTab.Cells[row, 1].LoadTable(searchModel.ItemOperationMisFilter); row = itemOperationMisFilterTable.Address.End.Row + 3; /* * Write extract MIS data option */ ApplyHeaderFormat(criteriaTab.Cells[row, 1, row, 2], "Extract MIS data?", true); criteriaTab.Cells[++row, 1].Value = searchModel.ExtractMisData ? "YES" : "NO"; //Auto-fit columns for (int column = 1; column <= 4; column++) { criteriaTab.Column(column).AutoFit(); criteriaTab.Column(column).Width = criteriaTab.Column(column).Width * 1.15; } //Set worksheet/tab as protected criteriaTab.Protection.SetPassword("JDE_SCOPING_TOOL_PASS"); } /// /// Writes search results to a tab in the worksheet /// /// Excel package to write to /// Search to extract criteria from /// Search results to write private static void WriteResults(ExcelPackage package, Search search, List results) { //Create tab in workbook to hold results ExcelWorksheet resultsTab = package.Workbook.Worksheets.Add("Search Results"); int row = 1; int col = 1; //Write header ApplyHeaderFormat(resultsTab.Cells[row, col++], "Work Order Number"); ApplyHeaderFormat(resultsTab.Cells[row, col++], "Work Order Branch Code"); ApplyHeaderFormat(resultsTab.Cells[row, col++], "Lot Number"); ApplyHeaderFormat(resultsTab.Cells[row, col++], "Item Number"); ApplyHeaderFormat(resultsTab.Cells[row, col++], "Planning Family"); ApplyHeaderFormat(resultsTab.Cells[row, col++], "Order Quantity"); ApplyHeaderFormat(resultsTab.Cells[row, col++], "Held Quantity"); ApplyHeaderFormat(resultsTab.Cells[row, col++], "Scrapped Quantity"); ApplyHeaderFormat(resultsTab.Cells[row, col++], "Shipped Quantity"); ApplyHeaderFormat(resultsTab.Cells[row, col++], "Operation Step Branch Code"); ApplyHeaderFormat(resultsTab.Cells[row, col++], "Operation Step"); ApplyHeaderFormat(resultsTab.Cells[row, col++], "Operation Step Description"); ApplyHeaderFormat(resultsTab.Cells[row, col++], "Function Operation Description"); resultsTab.Column(col).Style.Numberformat.Format = TIMESTAMP_FORMAT; ApplyHeaderFormat(resultsTab.Cells[row, col++], "Operation Step Update Timestamp"); ApplyHeaderFormat(resultsTab.Cells[row, col++], "Status Code"); ApplyHeaderFormat(resultsTab.Cells[row, col++], "Status Description"); resultsTab.Column(col).Style.Numberformat.Format = TIMESTAMP_FORMAT; ApplyHeaderFormat(resultsTab.Cells[row, col++], "Status Update Timestamp"); ApplyHeaderFormat(resultsTab.Cells[row, col++], "Inclusion Reason"); //Write data foreach (SearchResult searchResult in results) { row++; col = 1; resultsTab.Cells[row, col++].Value = searchResult.WorkOrderNumber; resultsTab.Cells[row, col++].Value = searchResult.WorkOrderBranchCode; resultsTab.Cells[row, col++].Value = searchResult.LotNumber; resultsTab.Cells[row, col++].Value = searchResult.ItemNumber; resultsTab.Cells[row, col++].Value = searchResult.PlanningFamily; resultsTab.Cells[row, col++].Value = searchResult.OrderQuantity; resultsTab.Cells[row, col++].Value = searchResult.HeldQuantity; resultsTab.Cells[row, col++].Value = searchResult.ScrappedQuantity; resultsTab.Cells[row, col++].Value = searchResult.ShippedQuantity; resultsTab.Cells[row, col++].Value = searchResult.StepBranchCode; resultsTab.Cells[row, col++].Value = searchResult.StepNumber; resultsTab.Cells[row, col++].Value = searchResult.StepDescription; resultsTab.Cells[row, col++].Value = searchResult.FunctionOperationDescription; resultsTab.Cells[row, col++].Value = searchResult.StepUpdateDT; resultsTab.Cells[row, col++].Value = searchResult.StatusCode; resultsTab.Cells[row, col++].Value = searchResult.StatusDescription; resultsTab.Cells[row, col++].Value = searchResult.StatusUpdateDT; resultsTab.Cells[row, col++].Value = searchResult.InclusionReason; } //Auto-fit columns for (int column = 1; column <= resultsTab.Dimension.Columns; column++) { resultsTab.Column(column).AutoFit(); resultsTab.Column(column).Width = resultsTab.Column(column).Width * 1.3; } //Get protected / unprotected ranges for editing ExcelRange protectedRange = resultsTab.Cells[1, 1, row, resultsTab.Dimension.Columns]; ExcelRange unprotectedRange = resultsTab.Cells[1, resultsTab.Dimension.Columns + 1, row + 1000, resultsTab.Dimension.Columns + 1000]; //Format as table ExcelTable table = resultsTab.Tables.Add(protectedRange, "Search_Results"); table.ShowTotal = false; table.TableStyle = TableStyles.Medium1; //Write-protect range resultsTab.Protection.IsProtected = true; resultsTab.ProtectedRanges.Add("Editable", unprotectedRange); resultsTab.Protection.AllowDeleteColumns = true; resultsTab.Protection.AllowDeleteRows = false; resultsTab.Protection.AllowAutoFilter = true; resultsTab.Protection.AllowAutoFilter = true; resultsTab.Protection.AllowFormatCells = true; resultsTab.Protection.AllowFormatColumns = true; resultsTab.Protection.AllowFormatRows = true; resultsTab.Protection.AllowSelectLockedCells = true; resultsTab.Protection.AllowSelectUnlockedCells = true; resultsTab.Protection.AllowEditObject = true; resultsTab.Protection.AllowSort = true; resultsTab.Protection.SetPassword("JDESCOPINGTOOL"); } /// /// Writes intermediate MIS search results to a tab in the worksheet /// /// Excel package to write to /// Intermediate MIS search results to write private static void WriteMisInfo(ExcelPackage package, List misInfo) { if (misInfo == null) { return; } //Create tab in workbook to hold results ExcelWorksheet misInfoTab = package.Workbook.Worksheets.Add("MIS Info"); int row = 1; int col = 1; //Write header ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Item Number"); ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Item Description"); ApplyHeaderFormat(misInfoTab.Cells[row, col++], "MIS Job Step Sequence Number"); ApplyHeaderFormat(misInfoTab.Cells[row, col++], "MIS Number"); ApplyHeaderFormat(misInfoTab.Cells[row, col++], "MIS Revision"); ApplyHeaderFormat(misInfoTab.Cells[row, col++], "MIS Release Status"); misInfoTab.Column(col).Style.Numberformat.Format = TIMESTAMP_FORMAT; ApplyHeaderFormat(misInfoTab.Cells[row, col++], "MIS Release Date"); ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Branch Code"); ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Job Step Sequence Number"); ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Matched Sequence Number"); ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Matched to F3112Z1?"); ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Matched to F3003?"); ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Function Operation Description"); ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Char Number"); misInfoTab.Column(col).Style.WrapText = true; misInfoTab.Column(col).Width = 65; ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Test Description"); ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Sampling Type"); ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Sampling Value"); misInfoTab.Column(col).Style.WrapText = true; misInfoTab.Column(col).Width = WRAPPED_CELL_WIDTH; ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Tools & Gauges"); misInfoTab.Column(col).Style.WrapText = true; misInfoTab.Column(col).Width = WRAPPED_CELL_WIDTH; ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Work Instructions"); //Write data foreach (MisSearchResult misData in misInfo) { row++; col = 1; misInfoTab.Cells[row, col++].Value = misData.ItemNumber; misInfoTab.Cells[row, col++].Value = misData.ItemDescription; misInfoTab.Cells[row, col++].Value = misData.SequenceNumber; misInfoTab.Cells[row, col++].Value = misData.MisNumber; misInfoTab.Cells[row, col++].Value = misData.RevID; misInfoTab.Cells[row, col++].Value = misData.Status; misInfoTab.Cells[row, col++].Value = misData.ReleaseDate; misInfoTab.Cells[row, col++].Value = misData.BranchCode; misInfoTab.Cells[row, col++].Value = misData.JobStepSequenceNumber; misInfoTab.Cells[row, col++].Value = misData.MatchedSequenceNumber; misInfoTab.Cells[row, col++].Value = misData.RoutingMatch; misInfoTab.Cells[row, col++].Value = misData.MasterMatch; misInfoTab.Cells[row, col++].Value = misData.FunctionOperationDescription; misInfoTab.Cells[row, col++].Value = misData.CharNumber; misInfoTab.Cells[row, col++].Value = misData.TestDescription; misInfoTab.Cells[row, col++].Value = misData.SamplingType; misInfoTab.Cells[row, col++].Value = misData.SamplingValue; misInfoTab.Cells[row, col++].Value = misData.ToolsGauges; misInfoTab.Cells[row, col++].Value = misData.WorkInstructions; } //Auto-fit columns for (int column = 1; column <= misInfoTab.Dimension.Columns; column++) { if (misInfoTab.Column(column).Width == WRAPPED_CELL_WIDTH) { continue; } misInfoTab.Column(column).AutoFit(); misInfoTab.Column(column).Width = misInfoTab.Column(column).Width * 1.3; } //Get protected / unprotected ranges for editing ExcelRange protectedRange = misInfoTab.Cells[1, 1, row, misInfoTab.Dimension.Columns]; ExcelRange unprotectedRange = misInfoTab.Cells[1, misInfoTab.Dimension.Columns + 1, row + 1000, misInfoTab.Dimension.Columns + 1000]; //Format as table ExcelTable table = misInfoTab.Tables.Add(protectedRange, "MIS_Info"); table.ShowTotal = false; table.TableStyle = TableStyles.Medium1; //Write-protect data ApplySecurity(misInfoTab); misInfoTab.ProtectedRanges.Add("Editable", unprotectedRange); } /// /// Writes master router mismatches to a tab in the worksheet /// /// Excel package to write to /// Master router mismatches to write private static void WriteRouterMismatches(ExcelPackage package, List misMatches) { if (misMatches == null) { return; } //Create tab in workbook to hold results ExcelWorksheet misInfoTab = package.Workbook.Worksheets.Add("Investigation"); int row = 1; int col = 1; //Write header ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Work Center Code"); ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Work Order Number"); misInfoTab.Column(col).Style.Numberformat.Format = "m/d/yyyy"; ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Work Order Start Date"); ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Job Step Number"); ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Function Operation Description"); misInfoTab.Column(col).Style.Numberformat.Format = "m/d/yyyy"; ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Job Step End Date"); ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Function Code"); ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Item Number"); ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Item Description"); ApplyHeaderFormat(misInfoTab.Cells[row, col++], "Routing Type"); //Write data foreach (MisNonMatchSearchResult misMatch in misMatches) { row++; col = 1; misInfoTab.Cells[row, col++].Value = misMatch.WorkCenterCode; misInfoTab.Cells[row, col++].Value = misMatch.WorkOrderNumber; misInfoTab.Cells[row, col++].Value = misMatch.WorkOrderStartDate; misInfoTab.Cells[row, col++].Value = misMatch.JobStepNumber; misInfoTab.Cells[row, col++].Value = misMatch.JobStepDescription; misInfoTab.Cells[row, col++].Value = misMatch.JobStepEndDate; misInfoTab.Cells[row, col++].Value = misMatch.FunctionCode; misInfoTab.Cells[row, col++].Value = misMatch.ItemNumber; misInfoTab.Cells[row, col++].Value = misMatch.ItemDescription; misInfoTab.Cells[row, col++].Value = misMatch.RoutingType; } //Auto-fit columns for (int column = 1; column <= misInfoTab.Dimension.Columns; column++) { misInfoTab.Column(column).AutoFit(); misInfoTab.Column(column).Width = misInfoTab.Column(column).Width * 1.3; } //Get protected / unprotected ranges for editing ExcelRange protectedRange = misInfoTab.Cells[1, 1, row, misInfoTab.Dimension.Columns]; ExcelRange unprotectedRange = misInfoTab.Cells[1, misInfoTab.Dimension.Columns + 1, row + 1000, misInfoTab.Dimension.Columns + 1000]; //Format as table ExcelTable table = misInfoTab.Tables.Add(protectedRange, "Investigation"); table.ShowTotal = false; table.TableStyle = TableStyles.Medium1; //Write-protect data ApplySecurity(misInfoTab); misInfoTab.ProtectedRanges.Add("Editable", unprotectedRange); } /// /// Applies header formatting to the range of cells /// /// Range of cells to format /// Text to write to cells /// Whether or not to merge the cells private static void ApplyHeaderFormat(ExcelRange range, string text = null, bool merge = false) { range.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center; range.Style.Font.Bold = true; range.Style.Fill.PatternType = ExcelFillStyle.Solid; range.Style.Fill.BackgroundColor.SetColor(Color.Gainsboro); if (!string.IsNullOrEmpty(text)) { range.Value = text; } range.Merge = merge; } /// /// Applies write protection security to given worksheet /// /// Worksheet to apply write protection security to private static void ApplySecurity(ExcelWorksheet worksheet) { worksheet.Protection.IsProtected = true; worksheet.Protection.AllowDeleteColumns = true; worksheet.Protection.AllowDeleteRows = false; worksheet.Protection.AllowAutoFilter = true; worksheet.Protection.AllowFormatCells = true; worksheet.Protection.AllowFormatColumns = true; worksheet.Protection.AllowFormatRows = true; worksheet.Protection.AllowSelectLockedCells = true; worksheet.Protection.AllowSelectUnlockedCells = true; worksheet.Protection.AllowEditObject = true; worksheet.Protection.AllowSort = true; worksheet.Protection.SetPassword("JDESCOPINGTOOL"); } } }