Initial commit: JDE Scoping Tool migration project
Set up repository with legacy .NET Framework 4.8 source (OLD/), new .NET 10 Blazor solution (NEW/), OpenSpec specifications, documentation, and project configuration.
This commit is contained in:
Executable
+194
@@ -0,0 +1,194 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using OfficeOpenXml;
|
||||
using OfficeOpenXml.Table;
|
||||
using Fasterflect;
|
||||
using OfficeOpenXml.Style;
|
||||
using WorkerService.Models.Reporting;
|
||||
|
||||
namespace WorkerService.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Excel generation helper functions
|
||||
/// </summary>
|
||||
public static class ExcelHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads the given data into a new table in a new Excel tab
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of data</typeparam>
|
||||
/// <param name="workbook">Excel workbook to create new tab in</param>
|
||||
/// <param name="data">Data to load</param>
|
||||
/// <param name="tabName">Name of new Excel tab to create</param>
|
||||
/// <param name="tableName">Name of new Excel table to create</param>
|
||||
/// <param name="protect">Whether or not procection should be aded to new tab</param>
|
||||
/// <returns>Created tab</returns>
|
||||
public static ExcelWorksheet LoadTab<T>(this ExcelWorkbook workbook, IEnumerable<T> data, string tabName = null, string tableName = null, bool protect = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(tabName))
|
||||
{
|
||||
//No override tab name given => lookup from attribute
|
||||
if (typeof(T).GetCustomAttribute(typeof(OutputTableAttribute)) is OutputTableAttribute outputTableAttribute)
|
||||
{
|
||||
tabName = outputTableAttribute.TabName;
|
||||
}
|
||||
}
|
||||
|
||||
ExcelWorksheet worksheet = workbook.Worksheets.Add(tabName);
|
||||
LoadTable(worksheet.Cells[1, 1], data, tableName);
|
||||
|
||||
if (protect)
|
||||
{
|
||||
//Set worksheet/tab as protected
|
||||
worksheet.Protection.SetPassword("JDE_SCOPING_TOOL_PASS");
|
||||
}
|
||||
|
||||
return worksheet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the given data into a new table in the given range
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of data</typeparam>
|
||||
/// <param name="range">Excel range to load data into</param>
|
||||
/// <param name="data">Data to load</param>
|
||||
/// <param name="tableName">Name of new Excel table to create</param>
|
||||
/// <returns>Created table</returns>
|
||||
public static ExcelTable LoadTable<T>(this ExcelRangeBase range, IEnumerable<T> data, string tableName = null, bool? showHeader = null, string headerText = null)
|
||||
{
|
||||
OutputTableAttribute outputTableAttribute = typeof(T).GetCustomAttribute(typeof(OutputTableAttribute)) as OutputTableAttribute;
|
||||
if (string.IsNullOrEmpty(tableName))
|
||||
{
|
||||
//No override table name given => lookup from attribute
|
||||
tableName = outputTableAttribute != null ? outputTableAttribute.TableName : string.Empty;
|
||||
}
|
||||
|
||||
if (!showHeader.HasValue)
|
||||
{
|
||||
//No override show header indicator given => lookup from attribute
|
||||
showHeader = outputTableAttribute != null && outputTableAttribute.ShowHeader;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(headerText))
|
||||
{
|
||||
//No override header text given => lookup from attribute
|
||||
headerText = outputTableAttribute != null ? outputTableAttribute.TabName : string.Empty;
|
||||
}
|
||||
|
||||
//Try to load column definitions for the given data
|
||||
List<OutputColumn> columnDefinitions = new List<OutputColumn>();
|
||||
foreach (PropertyInfo propertyInfo in typeof(T).PropertiesWith(Flags.AllMembers, typeof(OutputColumnAttribute)))
|
||||
{
|
||||
columnDefinitions.Add(new OutputColumn()
|
||||
{
|
||||
Name = propertyInfo.Name,
|
||||
Property = propertyInfo,
|
||||
Attribute = propertyInfo.GetCustomAttribute<OutputColumnAttribute>()
|
||||
});
|
||||
}
|
||||
columnDefinitions = columnDefinitions.OrderBy(cd => cd.Attribute.Order).ThenBy(cd => cd.Name).ToList();
|
||||
|
||||
if (columnDefinitions.Count == 0)
|
||||
{
|
||||
//No column definitions provided => load as generic collection
|
||||
range.LoadFromCollection(data, true, TableStyles.Medium1);
|
||||
|
||||
//Find the generated table
|
||||
foreach (ExcelTable wsTable in range.Worksheet.Tables)
|
||||
{
|
||||
if (wsTable.Address.Table == range.Table)
|
||||
{
|
||||
return wsTable;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("failed to find created table in given range");
|
||||
}
|
||||
|
||||
ExcelWorksheet ws = range.Worksheet;
|
||||
int baseRow = range.Start.Row, baseColumn = range.Start.Column;
|
||||
int row, col;
|
||||
string[] propertyNames = columnDefinitions.Select(cd => cd.Name).ToArray();
|
||||
|
||||
//Write headers & formats for columns
|
||||
|
||||
if (showHeader.Value)
|
||||
{
|
||||
ExcelRangeBase mergedHeaderRange = ws.Cells[baseRow, baseColumn, baseRow, baseColumn + columnDefinitions.Count - 1];
|
||||
mergedHeaderRange.Value = headerText;
|
||||
mergedHeaderRange.Merge = true;
|
||||
baseRow++;
|
||||
}
|
||||
row = baseRow;
|
||||
col = baseColumn;
|
||||
foreach (OutputColumn outputColumn in columnDefinitions)
|
||||
{
|
||||
ws.Cells[row, col].Value = outputColumn.Attribute.HeaderText;
|
||||
|
||||
ws.Column(col).Style.WrapText = outputColumn.Attribute.WrapText;
|
||||
if (!outputColumn.Attribute.AutoWidth)
|
||||
{
|
||||
ws.Column(col).Width = outputColumn.Attribute.Width;
|
||||
}
|
||||
|
||||
col++;
|
||||
}
|
||||
|
||||
//Format headers
|
||||
ExcelRangeBase headerRange = ws.Cells[baseRow + (showHeader.Value ? -1 : 0), baseColumn, baseRow, baseColumn + columnDefinitions.Count - 1];
|
||||
headerRange.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
|
||||
headerRange.Style.Font.Bold = true;
|
||||
headerRange.Style.Fill.PatternType = ExcelFillStyle.Solid;
|
||||
headerRange.Style.Fill.BackgroundColor.SetColor(Color.Gainsboro);
|
||||
|
||||
//Write data
|
||||
List<object[]> rowDatas = new List<object[]>
|
||||
{
|
||||
columnDefinitions.Select(cd => cd.Attribute.HeaderText).ToArray()
|
||||
};
|
||||
rowDatas.AddRange(data.Select(dataItem =>
|
||||
{
|
||||
//Load values for row
|
||||
object[] rowValues = new object[columnDefinitions.Count];
|
||||
for (int i = 0; i < propertyNames.Length; i++)
|
||||
{
|
||||
rowValues[i] = dataItem.GetPropertyValue(propertyNames[i]);
|
||||
}
|
||||
|
||||
return rowValues;
|
||||
}));
|
||||
ExcelRangeBase dataRange = ws.Cells[baseRow, baseColumn].LoadFromArrays(rowDatas);
|
||||
|
||||
//Set column widths
|
||||
col = baseColumn;
|
||||
foreach (OutputColumn outputColumn in columnDefinitions)
|
||||
{
|
||||
if (outputColumn.Attribute.AutoWidth)
|
||||
{
|
||||
ws.Column(col).AutoFit();
|
||||
ws.Column(col).Width *= 1.3;
|
||||
}
|
||||
|
||||
col++;
|
||||
}
|
||||
|
||||
//Format as table
|
||||
ExcelTable table = ws.Tables.Add(dataRange, tableName);
|
||||
table.TableStyle = TableStyles.Light18;
|
||||
|
||||
int startRow = table.Address.Start.Row;
|
||||
int endRow = table.Address.End.Row;
|
||||
col = baseColumn;
|
||||
foreach (OutputColumn outputColumn in columnDefinitions)
|
||||
{
|
||||
ws.Cells[startRow, col, endRow, col].Style.Numberformat.Format = outputColumn.Attribute.Format;
|
||||
col++;
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
}
|
||||
}
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using Dapper;
|
||||
using DataModel.Models;
|
||||
using DataModel.Process;
|
||||
using Newtonsoft.Json;
|
||||
using WorkerService.Models.Reporting;
|
||||
using WorkerService.Templates;
|
||||
|
||||
namespace WorkerService.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// SearchModel class helper functions
|
||||
/// </summary>
|
||||
public static class SearchModelHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the given search to a search reporting model
|
||||
/// </summary>
|
||||
/// <param name="search">Search to convert</param>
|
||||
/// <returns>Search reporting model</returns>
|
||||
public static SearchModel ToSearchModel(this Search search)
|
||||
{
|
||||
//Load search details
|
||||
SearchModel model = new SearchModel
|
||||
{
|
||||
ID = search.ID,
|
||||
UserName = search.UserName,
|
||||
Name = search.Name,
|
||||
SubmitDT = search.SubmitDT,
|
||||
StartDT = search.StartDT,
|
||||
EndDT = search.EndDT
|
||||
};
|
||||
|
||||
//Load search critera
|
||||
SearchCriteria criteria = JsonConvert.DeserializeObject<SearchCriteria>(search.CriteriaJSON);
|
||||
model.MinimumDT = criteria.MinimumDT;
|
||||
model.MaximumDT = criteria.MaximumDT;
|
||||
model.WorkOrderFilter.AddRange(LotFinderDB.LookupWorkorders(criteria.WorkOrderNumbers).Select(wo => new WorkOrderFilterEntry() { WorkOrderNumber = wo.WorkOrderNumber, ItemNumber = wo.ItemNumber }));
|
||||
model.ItemNumberFilter.AddRange(LotFinderDB.LookupItems(criteria.ItemNumbers).Select(i => new ItemNumberFilterEntry() { ItemNumber = i.ItemNumber, ItemDescription = i.Description }));
|
||||
model.ProfitCenterFilter.AddRange(LotFinderDB.LookupProfitCenters(criteria.ProfitCenters).Select(pc => new ProfitCenterFilterEntry() { Code = pc.Code, Description = pc.Description }));
|
||||
model.WorkCenterFilter.AddRange(LotFinderDB.LookupWorkCenters(criteria.WorkCenters).Select(wc => new WorkCenterFilterEntry() { Code = wc.Code, Description = wc.Description }));
|
||||
model.ComponentLotFilter.AddRange(LotFinderDB.LookupLots(criteria.ComponentLotNumbers).Select(cl => new ComponentLotFilterEntry() { LotNumber = cl.LotNumber, ItemNumber = cl.ItemNumber }));
|
||||
model.OperatorFilter.AddRange(LotFinderDB.LookupUsers(criteria.OperatorIDs).Select(o => new OperatorFilterEntry() { AddressNumber = o.AddressNumber, UserID = o.UserID, FullName = o.FullName }));
|
||||
model.ItemOperationMisFilter.AddRange(criteria.PartOperations.Select(po => new ItemOperationMisFilterEntry() { ItemNumber = po.ItemNumber, OperationNumber = po.OperationNumber, MisNumber = po.MisNumber, MisRevision = po.MisRevision }));
|
||||
model.ExtractMisData = criteria.ExtractMisData;
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the search query for the given search model
|
||||
/// </summary>
|
||||
/// <param name="model">Search model to generate search query for</param>
|
||||
/// <returns>Search query for the given search model</returns>
|
||||
public static string GetQuery(this SearchModel model)
|
||||
{
|
||||
QueryTemplate queryTemplate = new QueryTemplate(model);
|
||||
return queryTemplate.TransformText();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if work order step data should be searched for the given search model
|
||||
/// </summary>
|
||||
/// <param name="model">Search model to evaluate</param>
|
||||
/// <returns>Whether or not work order step data should be searched for the given search model</returns>
|
||||
public static bool ShouldSearchSteps(this SearchModel model)
|
||||
{
|
||||
return model.MinimumDT.HasValue || model.MaximumDT.HasValue || model.ProfitCenterFilterEnabled || model.WorkCenterFilterEnabled || model.OperatorFilterEnabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates table-valued-parameter for search's work order filter
|
||||
/// </summary>
|
||||
/// <param name="model">Search to create work order filter parameter for</param>
|
||||
/// <returns>Table-valued-parameter for search's work order filter</returns>
|
||||
public static SqlMapper.ICustomQueryParameter CreateWorkOrderFilterParameter(this SearchModel model)
|
||||
{
|
||||
//Generate data table with filter data
|
||||
DataTable dataTable = new DataTable();
|
||||
dataTable.Columns.Add("WorkOrderNumber", typeof(long));
|
||||
|
||||
foreach (WorkOrderFilterEntry workOrderFilterEntry in model.WorkOrderFilter)
|
||||
{
|
||||
dataTable.Rows.Add(workOrderFilterEntry.WorkOrderNumber);
|
||||
}
|
||||
|
||||
return dataTable.AsTableValuedParameter("WorkOrderFilterParameter");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates table-valued-parameter for search's item number filter
|
||||
/// </summary>
|
||||
/// <param name="model">Search to create item number filter parameter for</param>
|
||||
/// <returns>Table-valued-parameter for search's item number filter</returns>
|
||||
public static SqlMapper.ICustomQueryParameter CreateItemNumberFilterParameter(this SearchModel model)
|
||||
{
|
||||
//Generate data table with filter data
|
||||
DataTable dataTable = new DataTable();
|
||||
dataTable.Columns.Add("ItemNumber", typeof(string));
|
||||
|
||||
foreach (ItemNumberFilterEntry itemNumberFilterEntry in model.ItemNumberFilter)
|
||||
{
|
||||
dataTable.Rows.Add(itemNumberFilterEntry.ItemNumber);
|
||||
}
|
||||
|
||||
return dataTable.AsTableValuedParameter("ItemNumberFilterParameter");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates table-valued-parameter for search's profit center filter
|
||||
/// </summary>
|
||||
/// <param name="model">Search to create profit center filter parameter for</param>
|
||||
/// <returns>Table-valued-parameter for search's profit center filter</returns>
|
||||
public static SqlMapper.ICustomQueryParameter CreateProfitCenterFilterParameter(this SearchModel model)
|
||||
{
|
||||
//Generate data table with filter data
|
||||
DataTable dataTable = new DataTable();
|
||||
dataTable.Columns.Add("Code", typeof(string));
|
||||
|
||||
foreach (ProfitCenterFilterEntry profitCenterFilterEntry in model.ProfitCenterFilter)
|
||||
{
|
||||
dataTable.Rows.Add(profitCenterFilterEntry.Code);
|
||||
}
|
||||
|
||||
return dataTable.AsTableValuedParameter("ProfitCenterFilterParameter");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates table-valued-parameter for search's work center filter
|
||||
/// </summary>
|
||||
/// <param name="model">Search to create work center filter parameter for</param>
|
||||
/// <returns>Table-valued-parameter for search's work center filter</returns>
|
||||
public static SqlMapper.ICustomQueryParameter CreateWorkCenterFilterParameter(this SearchModel model)
|
||||
{
|
||||
//Generate data table with filter data
|
||||
DataTable dataTable = new DataTable();
|
||||
dataTable.Columns.Add("Code", typeof(string));
|
||||
|
||||
foreach (WorkCenterFilterEntry workCenterFilterEntry in model.WorkCenterFilter)
|
||||
{
|
||||
dataTable.Rows.Add(workCenterFilterEntry.Code);
|
||||
}
|
||||
|
||||
return dataTable.AsTableValuedParameter("WorkCenterFilterParameter");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates table-valued-parameter for search's work center filter
|
||||
/// </summary>
|
||||
/// <param name="model">Search to create work center filter parameter for</param>
|
||||
/// <returns>Table-valued-parameter for search's work center filter</returns>
|
||||
public static SqlMapper.ICustomQueryParameter CreateComponentLotFilterParameter(this SearchModel model)
|
||||
{
|
||||
//Generate data table with filter data
|
||||
DataTable dataTable = new DataTable();
|
||||
dataTable.Columns.Add("ComponentLotNumber", typeof(string));
|
||||
dataTable.Columns.Add("ItemNumber", typeof(string));
|
||||
|
||||
foreach (ComponentLotFilterEntry componentLotFilterEntry in model.ComponentLotFilter)
|
||||
{
|
||||
dataTable.Rows.Add(componentLotFilterEntry.LotNumber, componentLotFilterEntry.ItemNumber);
|
||||
}
|
||||
|
||||
return dataTable.AsTableValuedParameter("ComponentLotFilterParameter");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates table-valued-parameter for search's operator filter
|
||||
/// </summary>
|
||||
/// <param name="model">Search to create operator filter parameter for</param>
|
||||
/// <returns>Table-valued-parameter for search's operator filter</returns>
|
||||
public static SqlMapper.ICustomQueryParameter CreateOperatorFilterParameter(this SearchModel model)
|
||||
{
|
||||
//Generate data table with filter data
|
||||
DataTable dataTable = new DataTable();
|
||||
dataTable.Columns.Add("UserName", typeof(string));
|
||||
|
||||
foreach (OperatorFilterEntry operatorFilterEntry in model.OperatorFilter)
|
||||
{
|
||||
dataTable.Rows.Add(operatorFilterEntry.UserID);
|
||||
}
|
||||
|
||||
return dataTable.AsTableValuedParameter("OperatorFilterParameter");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates table-valued-parameter for search's item/operation/mis filter
|
||||
/// </summary>
|
||||
/// <param name="model">Search to create item/operation/mis filter parameter for</param>
|
||||
/// <returns>Table-valued-parameter for search's item/operation/mis filter</returns>
|
||||
public static SqlMapper.ICustomQueryParameter CreateItemOperationMisFilterParameter(this SearchModel model)
|
||||
{
|
||||
//Generate data table with filter data
|
||||
DataTable dataTable = new DataTable();
|
||||
dataTable.Columns.Add("ItemNumber", typeof(string));
|
||||
dataTable.Columns.Add("OperationNumber", typeof(string));
|
||||
dataTable.Columns.Add("MisNumber", typeof(string));
|
||||
dataTable.Columns.Add("MisRevision", typeof(string));
|
||||
|
||||
foreach (ItemOperationMisFilterEntry itemOperationMisFilterEntry in model.ItemOperationMisFilter)
|
||||
{
|
||||
dataTable.Rows.Add(itemOperationMisFilterEntry.ItemNumber, itemOperationMisFilterEntry.OperationNumber, itemOperationMisFilterEntry.MisNumber, itemOperationMisFilterEntry.MisRevision);
|
||||
}
|
||||
|
||||
return dataTable.AsTableValuedParameter("ItemOperationMisFilterParameter");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user