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
+48
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
|
||||
</startup>
|
||||
<runtime>
|
||||
<loadFromRemoteSources enabled="true"/>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
<connectionStrings>
|
||||
<add name="JDE" connectionString="Data Source=JE2PRD_APP;User ID=SCOPING_TOOL;Password=ibj77wrpujxOEqPGbpduVw==;"/>
|
||||
<!--<add name="JDE" connectionString="Data Source=JE1QA_APP;User ID=DOHERTJ2;Password=Jri2ZYCVNotsqrazeXs//g==;" />!-->
|
||||
<add name="GIW" connectionString="HOST=giwprd1.zmr.zimmer.com;Service Name=giwprd1_app;Port=1521;User ID=MFGREPORTING;Password=hS1mVvXwdRgbLVz48PD2wS9mFHD5XNheBYB+PIurOxQ=;"/>
|
||||
<add name="CMS" connectionString="HOST=ha-iman;Service Name=imanprd;Fetch Array Size=1280000;Port=1522;User ID=app_teamcenter;Password=8mjtcCypgFya2i3njdhm9w==;"/>
|
||||
<!--<add name="CMS" connectionString="HOST=sbracqa01-scan.nam.zimmer.com;Service Name=GIWQA1_USER;Fetch Array Size=1280000;Port=1521;User ID=JDE_SCOPING;Password=r39Pw67AmLxsmjBgob0d3g==;" />!-->
|
||||
<!--<add name="LotFinderDB" connectionString="Server=sbconsqlprd03.zmr.zimmer.com;Database=ScopingTool;User Id=scoping;Password=X4Uxe6hggSnlFTWwzBhodg==;" providerName="System.Data.SqlClient" />!-->
|
||||
<!--<add name="LotFinderDB" connectionString="Server=JDESCP-SQL-VP01;Database=ScopingTool;User Id=ScopingToolPrd;Password=8AC/5qz4KbUnad9O/Ghn+g==;" providerName="System.Data.SqlClient" />!-->
|
||||
<add name="LotFinderDB" connectionString="Server=JDESCP-SQL-VQ01.zmr.zimmer.com;Database=ScopingTool;User Id=ScopingTool;Password=wIk7KexO9sIEO+b+ILpfcA==;" providerName="System.Data.SqlClient"/>
|
||||
<!--<add name="LotFinderDB" connectionString="Server=wwtest\MSSQL2014;Database=ScopingTool;User Id=sa;Password=4DacH/qWG7DGGCGpZ1XnJw==;" providerName="System.Data.SqlClient" />!-->
|
||||
</connectionStrings>
|
||||
<appSettings>
|
||||
<add key="HubHost" value="http://sbtriappprd01.zmr.zimmer.com/jdescopetool"/>
|
||||
<!--<add key="HubHost" value="http://sbtriwebdev01.zmr.zimmer.com/jdescopingtoolv4" />!-->
|
||||
<!--<add key="HubHost" value="http://localhost:39148" />!-->
|
||||
<add key="querytimeout" value="600"/>
|
||||
<add key="JDE_QUERY_REPO" value="JdeQueries"/>
|
||||
<add key="CMS_QUERY_REPO" value="CmsQueries"/>
|
||||
</appSettings>
|
||||
<system.web>
|
||||
<membership defaultProvider="ClientAuthenticationMembershipProvider">
|
||||
<providers>
|
||||
<add name="ClientAuthenticationMembershipProvider" type="System.Web.ClientServices.Providers.ClientFormsAuthenticationMembershipProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri=""/>
|
||||
</providers>
|
||||
</membership>
|
||||
<roleManager defaultProvider="ClientRoleProvider" enabled="true">
|
||||
<providers>
|
||||
<add name="ClientRoleProvider" type="System.Web.ClientServices.Providers.ClientRoleProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" cacheTimeout="86400"/>
|
||||
</providers>
|
||||
</roleManager>
|
||||
</system.web>
|
||||
|
||||
</configuration>
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
namespace WorkerService.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Database column specification
|
||||
/// </summary>
|
||||
public class ColumnSpec
|
||||
{
|
||||
/// <summary>
|
||||
/// Column name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Column definition
|
||||
/// </summary>
|
||||
public string Definition { get; set; }
|
||||
}
|
||||
}
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WorkerService.Models
|
||||
{
|
||||
public class DataSource
|
||||
{
|
||||
public int DataSourceID { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string HostName { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class DataLoad
|
||||
{
|
||||
public int DataLoadID { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
public int DataSourceID { get; set; }
|
||||
|
||||
public bool IsEnabled { get; set; }
|
||||
public List<DataLoadSchedule> Schedules { get; set; }
|
||||
}
|
||||
|
||||
public class DataLoadSchedule
|
||||
{
|
||||
public int DataLoadScheduleID { get; set; }
|
||||
public int DataLoadID { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
public bool PurgeBeforeLoad { get; set; }
|
||||
public bool ReIndexAfterLoad { get; set; }
|
||||
public bool IsDaily { get; set; }
|
||||
}
|
||||
}
|
||||
Executable
+60
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using WorkerService.Process;
|
||||
|
||||
namespace WorkerService.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Database update configuration
|
||||
/// </summary>
|
||||
public class DataSourceConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of source data system
|
||||
/// </summary>
|
||||
public string SourceSystem { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of source data set
|
||||
/// </summary>
|
||||
public string SourceData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of table being updated
|
||||
/// </summary>
|
||||
public string TableName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the data source is enabled
|
||||
/// </summary>
|
||||
public bool IsEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Source data fetch function
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(FunctionConverter<DateTime?, IEnumerable<dynamic>>))]
|
||||
public Func<DateTime?, IEnumerable<dynamic>> DataFetchFunction { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Post data update processing action
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(ActionConverter))]
|
||||
public Action PostProcessingAction { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Data update entry for mass update typ
|
||||
/// </summary>
|
||||
public DataUpdateConfig MassUpdateConfig { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Data update entry for daily update typ
|
||||
/// </summary>
|
||||
public DataUpdateConfig DailyUpdateConfig { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Data update entry for hourly update typ
|
||||
/// </summary>
|
||||
public DataUpdateConfig HourlyUpdateConfig { get; set; }
|
||||
}
|
||||
}
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
namespace WorkerService.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Database update entry
|
||||
/// </summary>
|
||||
public class DataUpdateConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether or not the update config is enabled
|
||||
/// </summary>
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Interval (in minutes) between updates
|
||||
/// </summary>
|
||||
public int Interval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the table should be purged prior to update
|
||||
/// </summary>
|
||||
public bool PrepurgeData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the table should be re-indexed after update
|
||||
/// </summary>
|
||||
public bool ReIndexData { get; set; }
|
||||
}
|
||||
}
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using DataModel.Models;
|
||||
|
||||
namespace WorkerService.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Cached data update task
|
||||
/// </summary>
|
||||
public class DataUpdateTask
|
||||
{
|
||||
/// <summary>
|
||||
/// Data update configuration
|
||||
/// </summary>
|
||||
public DataSourceConfig Configuration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of update needed
|
||||
/// </summary>
|
||||
public UpdateTypes UpdateType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum timestamp to import changeset from
|
||||
/// </summary>
|
||||
public DateTime? MinimumDT { get; set; }
|
||||
}
|
||||
}
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
namespace WorkerService.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Table index
|
||||
/// </summary>
|
||||
public class Index
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of index
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the index is a primary key
|
||||
/// </summary>
|
||||
public bool IsPrimaryKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the index is unique
|
||||
/// </summary>
|
||||
public bool IsUnique { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the index is unique constraint
|
||||
/// </summary>
|
||||
public bool IsUniqueConstraint { get; set; }
|
||||
}
|
||||
}
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
|
||||
namespace WorkerService.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Set of last successful data updates for table
|
||||
/// </summary>
|
||||
public class LastDataUpdate
|
||||
{
|
||||
/// <summary>
|
||||
/// Data table name
|
||||
/// </summary>
|
||||
public string TableName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp of last successful mass data update
|
||||
/// </summary>
|
||||
public DateTime MassUpdateDT { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp of last successful daily data update
|
||||
/// </summary>
|
||||
public DateTime DailyUpdateDT { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp of last successful hourly data update
|
||||
/// </summary>
|
||||
public DateTime HourlyUpdateDT { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using DataModel.ViewModels;
|
||||
|
||||
namespace WorkerService.Models.Reporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Component lot search filter entry
|
||||
/// </summary>
|
||||
[OutputTable(TabName = "Component Lot Filter", ShowHeader = true, TableName = "Component_Lot_Filter")]
|
||||
public class ComponentLotFilterEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Component lot number
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 10, HeaderText = "Lot Number")]
|
||||
public string LotNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Component lot item number
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 20, HeaderText = "Item Number")]
|
||||
public string ItemNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Implicit view model conversion
|
||||
/// </summary>
|
||||
/// <param name="lotViewModel">View model to convert</param>
|
||||
public static implicit operator ComponentLotFilterEntry(LotViewModel lotViewModel)
|
||||
{
|
||||
return new ComponentLotFilterEntry() { LotNumber = lotViewModel.LotNumber, ItemNumber = lotViewModel.ItemNumber };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace WorkerService.Models.Reporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Item number search filter entry
|
||||
/// </summary>
|
||||
[OutputTable(TabName = "Item Number Filter", ShowHeader = true, TableName = "Item_Number_Filter")]
|
||||
public class ItemNumberFilterEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Item number
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 10, HeaderText = "Item Number")]
|
||||
public string ItemNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Item description
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 20, HeaderText = "Item Description")]
|
||||
public string ItemDescription { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
namespace WorkerService.Models.Reporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Item/operation/MIS search filter entry
|
||||
/// </summary>
|
||||
[OutputTable(TabName = "Item/Operation/MIS Filter", ShowHeader = true, TableName = "Item_Operation_MIS_Filter")]
|
||||
public class ItemOperationMisFilterEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Part's item number
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 10, HeaderText = "Item Number")]
|
||||
public string ItemNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Operation's job step number
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 20, HeaderText = "Operation Number")]
|
||||
public string OperationNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// MIS number
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 30, HeaderText = "MIS Number")]
|
||||
public string MisNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// MIS revision
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 40, HeaderText = "MIS Revision")]
|
||||
public string MisRevision { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
|
||||
namespace WorkerService.Models.Reporting
|
||||
{
|
||||
/// <summary>
|
||||
/// MIS non-match reporting model
|
||||
/// </summary>
|
||||
[OutputTable(TabName = "Investigation", TableName = "Investigation")]
|
||||
public class MisNonMatchSearchResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Work order job step work center code
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 10, HeaderText = "Work Center Code")]
|
||||
public string WorkCenterCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Work order unique number
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 20, HeaderText = "Work Order Number")]
|
||||
public long WorkOrderNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Work order start date
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 30, HeaderText = "Work Order Start Date", Format = OutputColumnAttribute.DATE_FORMAT)]
|
||||
public DateTime WorkOrderStartDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Work order job step number
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 40, HeaderText = "Job Step Number")]
|
||||
public decimal JobStepNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Work order job step description
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 50, HeaderText = "Function Operation Description")]
|
||||
public string JobStepDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Work order job step completion date
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 60, HeaderText = "Job Step End Date", Format = OutputColumnAttribute.DATE_FORMAT)]
|
||||
public DateTime? JobStepEndDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Work order job step function code
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 70, HeaderText = "Function Code")]
|
||||
public string FunctionCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Work order job step function code
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 75, HeaderText = "Was Job Step Added?")]
|
||||
public bool WasJobStepAdded { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Matched work order job step number (match to original router by work order number, work center code, and function code)
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 76, HeaderText = "Matched Job Step Number")]
|
||||
public decimal? MatchedJobStepNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Work order item number
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 80, HeaderText = "Item Number")]
|
||||
public string ItemNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Work order item description
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 90, HeaderText = "Item Description")]
|
||||
public string ItemDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Work order router type
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 100, HeaderText = "Routing Type")]
|
||||
public string RoutingType { get; set; }
|
||||
}
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
|
||||
namespace WorkerService.Models.Reporting
|
||||
{
|
||||
/// <summary>
|
||||
/// MIS data reporting model
|
||||
/// </summary>
|
||||
[OutputTable(TabName = "MIS Info", TableName = "MIS_Info")]
|
||||
public class MisSearchResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Item unique number
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 10, HeaderText = "Item Number")]
|
||||
public string ItemNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Item description
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 50, HeaderText = "Item Description")]
|
||||
public string ItemDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Operation job step number
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 20, HeaderText = "MIS Job Step Sequence Number")]
|
||||
public string SequenceNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// MIS unique number
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 30, HeaderText = "MIS Number")]
|
||||
public string MisNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// MIS revision ID
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 40, HeaderText = "MIS Revision")]
|
||||
public string RevID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// MIS release status
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 60, HeaderText = "MIS Release Status")]
|
||||
public string Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// MIS release date
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 70, HeaderText = "MIS Release Date", Format = OutputColumnAttribute.TIMESTAMP_FORMAT)]
|
||||
public DateTime? ReleaseDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Branch unique code
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 80, HeaderText = "Branch Code")]
|
||||
public string BranchCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Job step number
|
||||
/// </summary>
|
||||
[OutputColumn(Order =90, HeaderText = "Job Step Sequence Number")]
|
||||
public decimal JobStepSequenceNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Job step number for matched F3112Z1 / F3111 record
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 100, HeaderText = "Matched Sequence Number")]
|
||||
public decimal? MatchedSequenceNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the job step was matched to F3112Z1 record
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 110, HeaderText = "Matched to F3112Z1?")]
|
||||
public bool RoutingMatch { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the job step was matched to F3111 record
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 120, HeaderText = "Matched to F3003?")]
|
||||
public bool MasterMatch { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Job step function description
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 130, HeaderText = "Function Operation Description")]
|
||||
public string FunctionOperationDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Characteristic number
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 140, HeaderText = "Char Number")]
|
||||
public string CharNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Test description
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 150, HeaderText = "Test Description", AutoWidth = false, Width = OutputColumnAttribute.WRAPPED_COLUMN_WIDTH, WrapText = true)]
|
||||
public string TestDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of sampling
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 160, HeaderText = "Sampling Type")]
|
||||
public string SamplingType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sampling selection value
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 170, HeaderText = "Sampling Value")]
|
||||
public string SamplingValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tools & gauges for MIS
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 180, HeaderText = "Tools & Gauges", AutoWidth = false, Width = OutputColumnAttribute.WRAPPED_COLUMN_WIDTH, WrapText = true)]
|
||||
public string ToolsGauges { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Instructions for MIS
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 190, HeaderText = "Work Instructions", AutoWidth = false, Width = OutputColumnAttribute.WRAPPED_COLUMN_WIDTH, WrapText = true)]
|
||||
public string WorkInstructions { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace WorkerService.Models.Reporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Operator search filter entry
|
||||
/// </summary>
|
||||
[OutputTable(TabName = "Operator Filter", ShowHeader = true, TableName = "Operator_Filter")]
|
||||
public class OperatorFilterEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Operator unique JDE address number
|
||||
/// </summary>
|
||||
public long AddressNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Operator login user ID
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 10, HeaderText = "Username")]
|
||||
public string UserID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Operator full name (FIRST + LAST)
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 20, HeaderText = "Name")]
|
||||
public string FullName { get; set; }
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace WorkerService.Models.Reporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Report output column setting
|
||||
/// </summary>
|
||||
public class OutputColumn
|
||||
{
|
||||
/// <summary>
|
||||
/// Column name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Property to bind to column
|
||||
/// </summary>
|
||||
public PropertyInfo Property { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Output column settings attribute
|
||||
/// </summary>
|
||||
public OutputColumnAttribute Attribute { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
|
||||
namespace WorkerService.Models.Reporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Excel output column specification
|
||||
/// </summary>
|
||||
public class OutputColumnAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Order to display column
|
||||
/// </summary>
|
||||
public int Order { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Override text to display for column
|
||||
/// </summary>
|
||||
public string HeaderText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Column format (Excel formatting string)
|
||||
/// </summary>
|
||||
public string Format { get; set; } = STD_FORMAT;
|
||||
|
||||
/// <summary>
|
||||
/// Standard format
|
||||
/// </summary>
|
||||
public const string STD_FORMAT = "@";
|
||||
|
||||
/// <summary>
|
||||
/// Standard date format
|
||||
/// </summary>
|
||||
public const string DATE_FORMAT = "[$-409]MM/dd/yyyy;@";
|
||||
|
||||
/// <summary>
|
||||
/// Standard timestamp format
|
||||
/// </summary>
|
||||
public const string TIMESTAMP_FORMAT = "[$-409]m/d/yy h:mm AM/PM;@";
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not width should be set automatically
|
||||
/// </summary>
|
||||
public bool AutoWidth { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Manually set width (only used if AutoWidth = FALSE)
|
||||
/// </summary>
|
||||
public double Width { get;set; }
|
||||
|
||||
/// <summary>
|
||||
/// Wrapped text column default width
|
||||
/// </summary>
|
||||
public const double WRAPPED_COLUMN_WIDTH = 65;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not text should be wrapped
|
||||
/// </summary>
|
||||
public bool WrapText { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace WorkerService.Models.Reporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Excel output table specification
|
||||
/// </summary>
|
||||
public class OutputTableAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Output tab name
|
||||
/// </summary>
|
||||
public string TabName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Table name
|
||||
/// </summary>
|
||||
public string TableName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not merged header should be shown
|
||||
/// </summary>
|
||||
public bool ShowHeader { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace WorkerService.Models.Reporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Profit center search filter entry
|
||||
/// </summary>
|
||||
[OutputTable(TabName = "Profit Center Filter", ShowHeader = true, TableName = "Profit_Center_Filter")]
|
||||
public class ProfitCenterFilterEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Profit center code
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 10, HeaderText = "Profit Center")]
|
||||
public string Code { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Profit center description
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 20, HeaderText = "Description")]
|
||||
public string Description { get; set; }
|
||||
}
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace WorkerService.Models.Reporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Reporting search data model
|
||||
/// </summary>
|
||||
public class SearchModel
|
||||
{
|
||||
/// <summary>
|
||||
/// PK ID of search
|
||||
/// </summary>
|
||||
public int ID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// User name of user that created search
|
||||
/// </summary>
|
||||
public string UserName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// User-friendly name for search
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp search was submitted
|
||||
/// </summary>
|
||||
public DateTime? SubmitDT { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp search was started
|
||||
/// </summary>
|
||||
public DateTime? StartDT { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp search was completed
|
||||
/// </summary>
|
||||
public DateTime? EndDT { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum timestamp to include
|
||||
/// </summary>
|
||||
public DateTime? MinimumDT { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maxmimum timestamp to include
|
||||
/// </summary>
|
||||
public DateTime? MaximumDT { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not timespan filter is enabled
|
||||
/// </summary>
|
||||
public bool TimespanFilterEnabled => (MinimumDT.HasValue || MaximumDT.HasValue);
|
||||
|
||||
/// <summary>
|
||||
/// Collection of workorder numbers to include
|
||||
/// </summary>
|
||||
public List<WorkOrderFilterEntry> WorkOrderFilter { get; set; } = new List<WorkOrderFilterEntry>();
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not work order filter is enabled
|
||||
/// </summary>
|
||||
public bool WorkOrderFilterEnabled => WorkOrderFilter != null && WorkOrderFilter.Any();
|
||||
|
||||
/// <summary>
|
||||
/// Collection of item numbers to include
|
||||
/// </summary>
|
||||
public List<ItemNumberFilterEntry> ItemNumberFilter { get; set; } = new List<ItemNumberFilterEntry>();
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not item number filter is enabled
|
||||
/// </summary>
|
||||
public bool ItemNumberFilterEnabled => ItemNumberFilter != null && ItemNumberFilter.Any();
|
||||
|
||||
/// <summary>
|
||||
/// Collection of included profit centers
|
||||
/// </summary>
|
||||
public List<ProfitCenterFilterEntry> ProfitCenterFilter { get; set; } = new List<ProfitCenterFilterEntry>();
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not profit center filter is enabled
|
||||
/// </summary>
|
||||
public bool ProfitCenterFilterEnabled => ProfitCenterFilter != null && ProfitCenterFilter.Any();
|
||||
|
||||
/// <summary>
|
||||
/// Collection of included work centers
|
||||
/// </summary>
|
||||
public List<WorkCenterFilterEntry> WorkCenterFilter { get; set; } = new List<WorkCenterFilterEntry>();
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not work center filter is enabled
|
||||
/// </summary>
|
||||
public bool WorkCenterFilterEnabled => WorkCenterFilter != null && WorkCenterFilter.Any();
|
||||
|
||||
/// <summary>
|
||||
/// Collection of included operator IDs
|
||||
/// </summary>
|
||||
public List<OperatorFilterEntry> OperatorFilter { get; set; } = new List<OperatorFilterEntry>();
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not operator filter is enabled
|
||||
/// </summary>
|
||||
public bool OperatorFilterEnabled => OperatorFilter != null && OperatorFilter.Any();
|
||||
|
||||
/// <summary>
|
||||
/// Collection of included upper level lot numbers
|
||||
/// </summary>
|
||||
public List<ComponentLotFilterEntry> ComponentLotFilter { get; set; } = new List<ComponentLotFilterEntry>();
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not component lot filter is enabled
|
||||
/// </summary>
|
||||
public bool ComponentLotFilterEnabled => ComponentLotFilter != null && ComponentLotFilter.Any();
|
||||
|
||||
/// <summary>
|
||||
/// List of part/operation combinations for MIS filtering
|
||||
/// </summary>
|
||||
public List<ItemOperationMisFilterEntry> ItemOperationMisFilter { get; set; } = new List<ItemOperationMisFilterEntry>();
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not item/operation/mis filter is enabled
|
||||
/// </summary>
|
||||
public bool ItemOperationMisFilterEnabled => ItemOperationMisFilter != null && ItemOperationMisFilter.Any();
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not to extract MIS data
|
||||
/// </summary>
|
||||
public bool ExtractMisData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Work order search results
|
||||
/// </summary>
|
||||
public List<SearchResult> Results { get; set; } = new List<SearchResult>();
|
||||
|
||||
/// <summary>
|
||||
/// MIS results
|
||||
/// </summary>
|
||||
public List<MisSearchResult> MisResults { get; set; } = new List<MisSearchResult>();
|
||||
|
||||
/// <summary>
|
||||
/// MIS no match found results
|
||||
/// </summary>
|
||||
public List<MisNonMatchSearchResult> MisNonMatchResults { get; set; } = new List<MisNonMatchSearchResult>();
|
||||
}
|
||||
}
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
using System;
|
||||
|
||||
namespace WorkerService.Models.Reporting
|
||||
{
|
||||
/// <summary>
|
||||
/// JDE search result reporting model
|
||||
/// </summary>
|
||||
[OutputTable(TabName = "Search Results", TableName = "Search_Results")]
|
||||
public class SearchResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Order unique number
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 10, HeaderText = "Work Order Number")]
|
||||
public long WorkOrderNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Order branch code
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 20, HeaderText = "Work Order Branch Code")]
|
||||
public string WorkOrderBranchCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Order lot number
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 30, HeaderText = "Lot Number")]
|
||||
public string LotNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Order item number
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 40, HeaderText = "Item Number")]
|
||||
public string ItemNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Item master planning family
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 50, HeaderText = "Planning Family")]
|
||||
public string PlanningFamily { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Item master stocking type
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 55, HeaderText = "Stocking Type")]
|
||||
public string StockingType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Order quantity
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 60, HeaderText = "Order Quantity")]
|
||||
public decimal OrderQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Quantity on hold
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 70, HeaderText = "Held Quantity")]
|
||||
public decimal HeldQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Quantity scrapped/cancelled
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 80, HeaderText = "Scrapped Quantity")]
|
||||
public decimal ScrappedQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Quantity shipped
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 90, HeaderText = "Shipped Quantity")]
|
||||
public decimal ShippedQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Operation branch code
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 100, HeaderText = "Operation Step Branch Code")]
|
||||
public string StepBranchCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Operation step number
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 110, HeaderText = "Operation Step")]
|
||||
public decimal StepNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Operation step description
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 120, HeaderText = "Operation Step Description")]
|
||||
public string StepDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Function operation description (long text)
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 130, HeaderText = "Function Operation Description")]
|
||||
public string FunctionOperationDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp of last update to operation step number
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 140, HeaderText = "Operation Step Update Timestamp", Format = OutputColumnAttribute.TIMESTAMP_FORMAT)]
|
||||
public DateTime StepUpdateDT { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Order status code
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 150, HeaderText = "Status Code")]
|
||||
public string StatusCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Order status description
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 160, HeaderText = "Status Description")]
|
||||
public string StatusDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Timestam of last update to order status
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 170, HeaderText = "Status Update Timestamp", Format = OutputColumnAttribute.DATE_FORMAT)]
|
||||
public DateTime? StatusUpdateDT { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Work order was included because it was manually specified
|
||||
/// </summary>
|
||||
public bool ManuallySpecified { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Work order was included because it was split from a from flagged work order
|
||||
/// </summary>
|
||||
public bool SplitOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Work order was included because it received parts from a flagged work order (CARDEX / F4111)
|
||||
/// </summary>
|
||||
public bool CARDEX { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Work order was included because it received parts from a flagged work order (parts list / F3111)
|
||||
/// </summary>
|
||||
public bool PartsList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Work order was included because it met the filter criteria
|
||||
/// </summary>
|
||||
public bool Flagged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reason work order was included in results
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 180, HeaderText = "Inclusion Reason")]
|
||||
public string InclusionReason
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ManuallySpecified)
|
||||
{
|
||||
return "ManuallySpecified";
|
||||
}
|
||||
if (Flagged)
|
||||
{
|
||||
return "Flagged";
|
||||
}
|
||||
if (CARDEX && PartsList)
|
||||
{
|
||||
return "ComponentUsage (CARDEX + Parts List)";
|
||||
}
|
||||
if (CARDEX && !PartsList)
|
||||
{
|
||||
return "ComponentUsage (CARDEX)";
|
||||
}
|
||||
if (!CARDEX && PartsList)
|
||||
{
|
||||
return "ComponentUsage (Parts List)";
|
||||
}
|
||||
if (SplitOrder)
|
||||
{
|
||||
return "Split order";
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace WorkerService.Models.Reporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Timespan search filter
|
||||
/// </summary>
|
||||
[OutputTable(TabName = "Timespan Filter", ShowHeader = true, TableName = "Timespan_Filter")]
|
||||
public class TimespanFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Minimum date for timespan search filter
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 10, HeaderText = "Minimum Date", Format = OutputColumnAttribute.DATE_FORMAT)]
|
||||
public DateTime? MinimumDT { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum date for timespan search filter
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 20, HeaderText = "Maximum Date", Format = OutputColumnAttribute.DATE_FORMAT)]
|
||||
public DateTime? MaximumDT { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace WorkerService.Models.Reporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Work center search filter entry
|
||||
/// </summary>
|
||||
[OutputTable(TabName = "Work Center Filter", ShowHeader = true, TableName = "Work_Center_Filter")]
|
||||
public class WorkCenterFilterEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Work center code
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 10, HeaderText = "Work Center")]
|
||||
public string Code { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Work center description
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 20, HeaderText = "Description")]
|
||||
public string Description { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace WorkerService.Models.Reporting
|
||||
{
|
||||
/// <summary>
|
||||
/// Work order search filter entry
|
||||
/// </summary>
|
||||
[OutputTable(TabName = "Work Order Filter", ShowHeader = true, TableName = "Work_Order_Filter")]
|
||||
public class WorkOrderFilterEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Work order number
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 10, HeaderText = "Work Order Number")]
|
||||
public long WorkOrderNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Work order item number
|
||||
/// </summary>
|
||||
[OutputColumn(Order = 20, HeaderText = "Item Number")]
|
||||
public string ItemNumber { get; set; }
|
||||
}
|
||||
}
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WorkerService.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Database table specification
|
||||
/// </summary>
|
||||
public class TableSpec
|
||||
{
|
||||
/// <summary>
|
||||
/// Table name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Data staging table name
|
||||
/// </summary>
|
||||
public string StagingTableName => $"#Staging{Name}";
|
||||
|
||||
/// <summary>
|
||||
/// Temporary table name
|
||||
/// </summary>
|
||||
public string TempTableName => $"#{Name}";
|
||||
|
||||
/// <summary>
|
||||
/// Table columns
|
||||
/// </summary>
|
||||
public List<ColumnSpec> Columns { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Table columns that form the primary key
|
||||
/// </summary>
|
||||
public List<ColumnSpec> PrimaryKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public TableSpec()
|
||||
{
|
||||
Columns = new List<ColumnSpec>();
|
||||
PrimaryKey = new List<ColumnSpec>();
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
||||
<targets>
|
||||
<target name="coloredConsole" xsi:type="ColoredConsole" useDefaultRowHighlightingRules="false"
|
||||
layout="${longdate}|${logger}|${pad:padding=5:inner=${level:uppercase=true}}|${message}" >
|
||||
<highlight-row condition="level == LogLevel.Debug" foregroundColor="DarkGray" />
|
||||
<highlight-row condition="level == LogLevel.Info" foregroundColor="Gray" />
|
||||
<highlight-row condition="level == LogLevel.Warn" foregroundColor="Yellow" />
|
||||
<highlight-row condition="level == LogLevel.Error" foregroundColor="Red" />
|
||||
<highlight-row condition="level == LogLevel.Fatal" foregroundColor="Red" backgroundColor="White" />
|
||||
</target>
|
||||
|
||||
<target name="f" xsi:type="File"
|
||||
layout="${longdate} ${logger} ${message} ${exception:format=tostring}"
|
||||
fileName="${basedir}/logs/current.log"
|
||||
archiveFileName="${basedir}/logs/archive.{#}.log"
|
||||
archiveEvery="Day"
|
||||
archiveNumbering="Rolling"
|
||||
maxArchiveFiles="7"
|
||||
concurrentWrites="true"
|
||||
keepFileOpen="false"
|
||||
encoding="iso-8859-2" />
|
||||
</targets>
|
||||
|
||||
<rules>
|
||||
<logger name="*" minlevel="Error" writeTo="f" />
|
||||
<logger name="*" minlevel="Trace" writeTo="coloredConsole" />
|
||||
</rules>
|
||||
</nlog>
|
||||
Executable
+2601
File diff suppressed because it is too large
Load Diff
Executable
+75
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace WorkerService.Process
|
||||
{
|
||||
/// <summary>
|
||||
/// Action (void function) JSON converter
|
||||
/// </summary>
|
||||
public class ActionConverter : JsonConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes the JSON representation of the object
|
||||
/// </summary>
|
||||
/// <param name="writer">The JsonWriter to write to</param>
|
||||
/// <param name="value">The value to write</param>
|
||||
/// <param name="serializer">The calling serializer</param>
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
if (value is Action action)
|
||||
{
|
||||
writer.WriteValue($"{action.Method.DeclaringType}.{action.Method.Name}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Reads the JSON representation of the object.</summary>
|
||||
/// <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader" /> to read from.</param>
|
||||
/// <param name="objectType">Type of the object.</param>
|
||||
/// <param name="existingValue">The existing value of object being read.</param>
|
||||
/// <param name="serializer">The calling serializer.</param>
|
||||
/// <returns>The object value.</returns>
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
//Get the method's full path
|
||||
string fullMethodPath = (string)reader.Value;
|
||||
if (string.IsNullOrEmpty(fullMethodPath))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//Extract class and method's names
|
||||
string className = fullMethodPath.Substring(0, fullMethodPath.LastIndexOf(".", StringComparison.Ordinal));
|
||||
string methodName = fullMethodPath.Substring(fullMethodPath.LastIndexOf(".", StringComparison.Ordinal) + 1);
|
||||
|
||||
//Get the class type
|
||||
Type classType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).FirstOrDefault(t => string.Equals(t.FullName, className, StringComparison.CurrentCultureIgnoreCase));
|
||||
if (classType == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//Get the function's method info
|
||||
MethodInfo methodInfo = classType.GetMethod(methodName);
|
||||
if (methodInfo == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return (Action)methodInfo.CreateDelegate(typeof(Action));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this instance can convert the specified object type
|
||||
/// </summary>
|
||||
/// <param name="objectType">Type of the object</param>
|
||||
/// <returns>
|
||||
/// Whether or not this instance can convert ot the specified object type
|
||||
/// </returns>
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType == typeof(Action);
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+499
@@ -0,0 +1,499 @@
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// Search results report writer
|
||||
/// </summary>
|
||||
public class ExcelWriter
|
||||
{
|
||||
/// <summary>
|
||||
/// Standard date format
|
||||
/// </summary>
|
||||
private const string DATE_FORMAT = "{0:MM/dd/yyyy}";
|
||||
|
||||
/// <summary>
|
||||
/// Standard timestamp format
|
||||
/// </summary>
|
||||
private const string TIMESTAMP_FORMAT = "[$-409]m/d/yy h:mm AM/PM;@";
|
||||
|
||||
/// <summary>
|
||||
/// Override width for cells with wrapped data
|
||||
/// </summary>
|
||||
private const double WRAPPED_CELL_WIDTH = 65;
|
||||
|
||||
/// <summary>
|
||||
/// Generates Excel report for the given search criteria and results
|
||||
/// </summary>
|
||||
/// <param name="searchModel">Search model to generate report for</param>
|
||||
/// <returns>Excel report file contents</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes search criteria to a tab in the worksheet
|
||||
/// </summary>
|
||||
/// <param name="package">Excel package to write to</param>
|
||||
/// <param name="searchModel">Search model to generate report for</param>
|
||||
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<TimespanFilter>()
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes search results to a tab in the worksheet
|
||||
/// </summary>
|
||||
/// <param name="package">Excel package to write to</param>
|
||||
/// <param name="search">Search to extract criteria from</param>
|
||||
/// <param name="results">Search results to write</param>
|
||||
private static void WriteResults(ExcelPackage package, Search search, List<SearchResult> 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");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes intermediate MIS search results to a tab in the worksheet
|
||||
/// </summary>
|
||||
/// <param name="package">Excel package to write to</param>
|
||||
/// <param name="misInfo">Intermediate MIS search results to write</param>
|
||||
private static void WriteMisInfo(ExcelPackage package, List<MisSearchResult> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes master router mismatches to a tab in the worksheet
|
||||
/// </summary>
|
||||
/// <param name="package">Excel package to write to</param>
|
||||
/// <param name="misMatches">Master router mismatches to write</param>
|
||||
private static void WriteRouterMismatches(ExcelPackage package, List<MisNonMatchSearchResult> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies header formatting to the range of cells
|
||||
/// </summary>
|
||||
/// <param name="range">Range of cells to format</param>
|
||||
/// <param name="text">Text to write to cells</param>
|
||||
/// <param name="merge">Whether or not to merge the cells</param>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies write protection security to given worksheet
|
||||
/// </summary>
|
||||
/// <param name="worksheet">Worksheet to apply write protection security to</param>
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace WorkerService.Process
|
||||
{
|
||||
/// <summary>
|
||||
/// Function JSON converter
|
||||
/// </summary>
|
||||
/// <typeparam name="TParameter">Function input type</typeparam>
|
||||
/// <typeparam name="TOutput">Function output type</typeparam>
|
||||
public class FunctionConverter<TParameter, TOutput> : JsonConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes the JSON representation of the object
|
||||
/// </summary>
|
||||
/// <param name="writer">The JsonWriter to write to</param>
|
||||
/// <param name="value">The value to write</param>
|
||||
/// <param name="serializer">The calling serializer</param>
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
if (value is Func<TParameter, TOutput> function)
|
||||
{
|
||||
writer.WriteValue($"{function.Method.DeclaringType}.{function.Method.Name}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Reads the JSON representation of the object.</summary>
|
||||
/// <param name="reader">JsonReader to read from</param>
|
||||
/// <param name="objectType">Type of the object</param>
|
||||
/// <param name="existingValue">The existing value of object being read</param>
|
||||
/// <param name="serializer">The calling serializer</param>
|
||||
/// <returns>The object value</returns>
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
//Get the function's full path
|
||||
string fullFunctionPath = (string)reader.Value;
|
||||
if (string.IsNullOrEmpty(fullFunctionPath))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//Extract class and function names
|
||||
string className = fullFunctionPath.Substring(0, fullFunctionPath.LastIndexOf(".", StringComparison.Ordinal));
|
||||
string functionName = fullFunctionPath.Substring(fullFunctionPath.LastIndexOf(".", StringComparison.Ordinal) + 1);
|
||||
|
||||
//Get the class type
|
||||
Type classType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).FirstOrDefault(t => string.Equals(t.FullName, className, StringComparison.CurrentCultureIgnoreCase));
|
||||
if (classType == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//Get the function's method info
|
||||
MethodInfo methodInfo = classType.GetMethod(functionName);
|
||||
if (methodInfo == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return (Func<TParameter, TOutput>)methodInfo.CreateDelegate(typeof(Func<TParameter, TOutput>));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this instance can convert the specified object type
|
||||
/// </summary>
|
||||
/// <param name="objectType">Type of the object</param>
|
||||
/// <returns>
|
||||
/// Whether or not this instance can convert ot the specified object type
|
||||
/// </returns>
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType == typeof(Func<TParameter, TOutput>);
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+191
@@ -0,0 +1,191 @@
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using System.IO;
|
||||
using Dapper;
|
||||
using DataModel.Helpers;
|
||||
using DataModel.Models;
|
||||
using DataModel.Process;
|
||||
using WorkerService.Helpers;
|
||||
using WorkerService.Models.Reporting;
|
||||
|
||||
namespace WorkerService.Process
|
||||
{
|
||||
/// <summary>
|
||||
/// Worker service-specific functionality for LotFinderDB interface
|
||||
/// </summary>
|
||||
public class LotFinderDBExt : LotFinderDB
|
||||
{
|
||||
/// <summary>
|
||||
/// Query to get next queued search
|
||||
/// </summary>
|
||||
private const string SQL_GET_NEXT_SEARCH = @"
|
||||
SELECT TOP 1 s.ID,
|
||||
s.UserName,
|
||||
s.Name,
|
||||
s.Status,
|
||||
s.SubmitDT,
|
||||
s.StartDT,
|
||||
s.EndDT,
|
||||
s.Criteria AS CriteriaJSON
|
||||
FROM dbo.Search s
|
||||
WHERE s.Status = 1
|
||||
ORDER BY s.SubmitDT";
|
||||
|
||||
/// <summary>
|
||||
/// Gets next queued search
|
||||
/// </summary>
|
||||
/// <returns>Next queue search</returns>
|
||||
public static Search GetNextSearch()
|
||||
{
|
||||
Search nextSearch = null;
|
||||
|
||||
try
|
||||
{
|
||||
using (SqlConnection connection = GetConnection())
|
||||
{
|
||||
nextSearch = connection.QueryFirstOrDefault<Search>(SQL_GET_NEXT_SEARCH);
|
||||
|
||||
if (nextSearch != null && !string.IsNullOrEmpty(nextSearch.CriteriaJSON))
|
||||
{
|
||||
nextSearch.Criteria = JsonHelpers.FromJSON<SearchCriteria>(nextSearch.CriteriaJSON);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
//Log but do not forward error
|
||||
logger.Error("GetNextSearch: failed to get next queued search: {0}.", error.Message);
|
||||
}
|
||||
|
||||
return nextSearch;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the status of partially completed searches
|
||||
/// </summary>
|
||||
public static void ResetPartialSearches()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (SqlConnection connection = GetConnection())
|
||||
{
|
||||
connection.Execute("ResetPartialSearches", commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
//Log but do not forward error
|
||||
logger.Error("ResetPartialSearches: failed to reset partial searches: {0}.", error.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the status of the search to 'Start'
|
||||
/// </summary>
|
||||
/// <param name="search">Search to start</param>
|
||||
public static void StartSearch(Search search)
|
||||
{
|
||||
try
|
||||
{
|
||||
search.Status = SearchStatus.Started;
|
||||
search.StartDT = DateTime.Now;
|
||||
|
||||
using (SqlConnection connection = GetConnection())
|
||||
{
|
||||
connection.Execute("StartSearch", new { p_SearchID = search.ID }, commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
//Log but do not forward error
|
||||
logger.Error("StartSearch: failed to mark search as started: {0}.", error.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the status of the search to 'Complete' and stores the results
|
||||
/// </summary>
|
||||
/// <param name="search">Search to complete</param>
|
||||
/// <param name="wasSuccessful">Whether or not the search was successful</param>
|
||||
public static void CompleteSearch(Search search, bool wasSuccessful)
|
||||
{
|
||||
try
|
||||
{
|
||||
search.Status = wasSuccessful ? SearchStatus.Ended : SearchStatus.Error;
|
||||
|
||||
using (SqlConnection connection = GetConnection())
|
||||
{
|
||||
connection.Execute("CompleteSearch", new { p_SearchID = search.ID, p_WasSuccessful = wasSuccessful, p_Results = search.Results }, commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
//Log but do not forward error
|
||||
logger.Error("CompleteSearch: failed to mark search as completed: {0}.", error.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs search and extracts results into given model
|
||||
/// </summary>
|
||||
/// <param name="searchModel">Search parameter & result model</param>
|
||||
public static void Search(SearchModel searchModel)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Get configured timeout
|
||||
int queryTimeout = 600;
|
||||
try
|
||||
{
|
||||
string queryTimeoutStr = ConfigurationManager.AppSettings["querytimeout"];
|
||||
queryTimeout = int.Parse(queryTimeoutStr);
|
||||
}
|
||||
catch
|
||||
{
|
||||
//Ignore
|
||||
}
|
||||
|
||||
//Generate query to execute
|
||||
string query = searchModel.GetQuery();
|
||||
File.WriteAllText($"search_{searchModel.ID}.sql", query);
|
||||
|
||||
searchModel.StartDT = DateTime.Now;
|
||||
using (SqlConnection connection = GetConnection())
|
||||
{
|
||||
var results = connection.QueryMultiple(query, new
|
||||
{
|
||||
p_MinimumDT = searchModel.MinimumDT,
|
||||
p_MaximumDT = searchModel.MaximumDT,
|
||||
p_WorkOrderFilter = searchModel.CreateWorkOrderFilterParameter(),
|
||||
p_ItemNumberFilter = searchModel.CreateItemNumberFilterParameter(),
|
||||
p_ProfitCenterFilter = searchModel.CreateProfitCenterFilterParameter(),
|
||||
p_WorkCenterFilter = searchModel.CreateWorkCenterFilterParameter(),
|
||||
p_ComponentLotFilter = searchModel.CreateComponentLotFilterParameter(),
|
||||
p_OperatorFilter = searchModel.CreateOperatorFilterParameter(),
|
||||
p_ItemOperationMisFilter = searchModel.CreateItemOperationMisFilterParameter(),
|
||||
p_ExtractMisData = searchModel.ExtractMisData
|
||||
}, commandTimeout: queryTimeout);
|
||||
|
||||
//Parse search results
|
||||
searchModel.Results.AddRange(results.Read<SearchResult>());
|
||||
|
||||
//Parse MIS data if extracted
|
||||
if (searchModel.ExtractMisData)
|
||||
{
|
||||
searchModel.MisResults.AddRange(results.Read<MisSearchResult>());
|
||||
searchModel.MisNonMatchResults.AddRange(results.Read<MisNonMatchSearchResult>());
|
||||
}
|
||||
}
|
||||
searchModel.EndDT = DateTime.Now;
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
//Log but do not forward error
|
||||
logger.Error("Search: failed to perform search: {0}.", error.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using Dapper;
|
||||
using DataModel.Models;
|
||||
using DataModel.Process;
|
||||
using WorkerService.Models;
|
||||
|
||||
namespace WorkerService.Process
|
||||
{
|
||||
/// <summary>
|
||||
/// Data update entry management functionality for data update processor
|
||||
/// </summary>
|
||||
public partial class UpdateProcessor
|
||||
{
|
||||
/// <summary>
|
||||
/// SQL to close any open update entries (identified by number records = -2)
|
||||
/// </summary>
|
||||
private const string SQL_CLOSE_OPEN_UPDATE_ENTRIES = @"
|
||||
UPDATE dbo.DataUpdate
|
||||
SET EndDT = GETDATE(), WasSuccessful = 0, NumberRecords = -1
|
||||
WHERE NumberRecords = -2";
|
||||
|
||||
/// <summary>
|
||||
/// Closes any open data update entries
|
||||
/// </summary>
|
||||
private static void CloseOpenUpdateEntries()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (SqlConnection connection = LotFinderDB.GetConnection())
|
||||
{
|
||||
//Close any open update entries
|
||||
connection.Execute(SQL_CLOSE_OPEN_UPDATE_ENTRIES);
|
||||
}
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
//Log but do not forward error
|
||||
logger.Error("CloseOpenUpdateEntries: failed to close open data update entries: {0}.", error.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL to purge data update records
|
||||
/// </summary>
|
||||
private const string SQL_PURGE_UPDATE_ENTRIES = @"
|
||||
DELETE FROM dbo.DataUpdate
|
||||
WHERE StartDT < DATEADD(DAY, @maxAge * -1, GETDATE())";
|
||||
|
||||
/// <summary>
|
||||
/// Purges any data update entries older than given max age
|
||||
/// </summary>
|
||||
/// <param name="maxAge">Maximum entry age (in days)</param>
|
||||
private static void PurgeUpdateEntries(int maxAge)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (SqlConnection connection = LotFinderDB.GetConnection())
|
||||
{
|
||||
//Purge the records
|
||||
connection.Execute(SQL_PURGE_UPDATE_ENTRIES, new { maxAge });
|
||||
}
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
//Log but do not forward error
|
||||
logger.Error("PurgeUpdateEntries: failed to purge data update entries older than {0} days: {1}.", maxAge, error.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL to insert data update record
|
||||
/// </summary>
|
||||
private const string SQL_LOG_DATA_UPDATE_START = @"
|
||||
INSERT INTO dbo.DataUpdate(SourceSystem, SourceData, TableName, StartDT, EndDT, UpdateType, WasSuccessful, NumberRecords)
|
||||
OUTPUT INSERTED.*
|
||||
VALUES(@sourceSystem, @sourceData, @tableName, GETDATE(), '1970-01-01', @updateType, 0, -2);";
|
||||
|
||||
/// <summary>
|
||||
/// Logs the data update entry at the start of the update
|
||||
/// </summary>
|
||||
/// <param name="connection">SQL connection to execute commands on</param>
|
||||
/// <param name="dataUpdate">Data update entry to log</param>
|
||||
public static void LogDataUpdateStart(SqlConnection connection, DataUpdate dataUpdate)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Update the record
|
||||
DataUpdate inserted = connection.QueryFirst<DataUpdate>(SQL_LOG_DATA_UPDATE_START,
|
||||
new
|
||||
{
|
||||
sourceSystem = dataUpdate.SourceSystem,
|
||||
sourceData = dataUpdate.SourceData,
|
||||
tableName = dataUpdate.TableName,
|
||||
updateType = dataUpdate.UpdateType
|
||||
});
|
||||
|
||||
//Copy output values to model
|
||||
dataUpdate.ID = inserted.ID;
|
||||
dataUpdate.StartDT = inserted.StartDT;
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
//Log but do not forward error
|
||||
logger.Error("LogDataUpdateStart: failed to log starting data update entry: {0}.", error.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL to update data update record with results
|
||||
/// </summary>
|
||||
private const string SQL_LOG_DATA_UPDATE_END = @"
|
||||
UPDATE dbo.DataUpdate
|
||||
SET EndDT = GETDATE(), WasSuccessful = @wasSuccessful, NumberRecords = @numberRecords
|
||||
OUTPUT INSERTED.*
|
||||
WHERE ID = @id";
|
||||
|
||||
/// <summary>
|
||||
/// Logs the data update entry at the end of the update
|
||||
/// </summary>
|
||||
/// <param name="connection">SQL connection to execute commands on</param>
|
||||
/// <param name="dataUpdate">Data update entry to log</param>
|
||||
public static void LogDataUpdateEnd(SqlConnection connection, DataUpdate dataUpdate)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Update the record
|
||||
DataUpdate updated = connection.QueryFirst<DataUpdate>(SQL_LOG_DATA_UPDATE_END,
|
||||
new
|
||||
{
|
||||
id = dataUpdate.ID,
|
||||
wasSuccessful = dataUpdate.WasSuccessful,
|
||||
numberRecords = dataUpdate.NumberRecords
|
||||
});
|
||||
|
||||
//Copy output values to model
|
||||
dataUpdate.EndDT = updated.EndDT;
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
//Log but do not forward error
|
||||
logger.Error("LogDataUpdateEnd: failed to log ending data update entry: {0}.", error.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets set of last successful data updates for all tables
|
||||
/// </summary>
|
||||
/// <param name="connection">SQL connection to execute commands on</param>
|
||||
/// <returns>Set of last successful data updates for all tables</returns>
|
||||
private static List<LastDataUpdate> GetLastDataUpdates(SqlConnection connection)
|
||||
{
|
||||
return connection.Query<LastDataUpdate>("SELECT * FROM dbo.LastDataUpdates").ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Dapper;
|
||||
using WorkerService.Models;
|
||||
|
||||
namespace WorkerService.Process
|
||||
{
|
||||
/// <summary>
|
||||
/// Table management functionality for data update processor
|
||||
/// </summary>
|
||||
public partial class UpdateProcessor
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates staging table with matching column layout of given table
|
||||
/// </summary>
|
||||
/// <param name="connection">SQL connection to execute commands on</param>
|
||||
/// <param name="tableName">Name of table to create staging table for</param>
|
||||
/// <returns>Name of created temporary table</returns>
|
||||
public static string CreateStagingTable(SqlConnection connection, string tableName)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Get table specification
|
||||
TableSpec tableSpec = GetTableSpec(connection, tableName);
|
||||
|
||||
//Drop temp table if it already exists
|
||||
connection.Execute($"IF OBJECT_ID('tempdb..{tableSpec.StagingTableName}') IS NOT NULL DROP TABLE {tableSpec.StagingTableName};");
|
||||
|
||||
//Create temp table
|
||||
connection.Execute($"CREATE TABLE {tableSpec.StagingTableName}({string.Join(",", tableSpec.Columns.Select(c => $"{c.Name} {c.Definition}"))});");
|
||||
|
||||
//Create indicies on temp table
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.Append($"CREATE INDEX IDX_STAGING_{tableSpec.Name} ON {tableSpec.StagingTableName}(");
|
||||
builder.Append(string.Join(",", tableSpec.PrimaryKey.Select(c => $"{c.Name}")));
|
||||
if (tableSpec.Columns.Any(c => c.Name.Equals("LastUpdateDT", StringComparison.CurrentCultureIgnoreCase)))
|
||||
{
|
||||
builder.Append(", LastUpdateDT DESC");
|
||||
}
|
||||
else if (tableSpec.Columns.Any(c => c.Name.Equals("ReleaseDate", StringComparison.CurrentCultureIgnoreCase)))
|
||||
{
|
||||
builder.Append(", ReleaseDate DESC");
|
||||
}
|
||||
builder.Append(");");
|
||||
|
||||
connection.Execute(builder.ToString());
|
||||
|
||||
//Disable indicies on temp table
|
||||
DisableIndicies(connection, tableSpec.StagingTableName);
|
||||
|
||||
return tableSpec.StagingTableName;
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
//Log and forward error
|
||||
logger.Error("GetStagingTable: failed to create staging table for '{0}': {1}.", tableName, error.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates temporary table with matching column layout of given table
|
||||
/// </summary>
|
||||
/// <param name="connection">SQL connection to execute commands on</param>
|
||||
/// <param name="tableName">Name of table to create temporary table for</param>
|
||||
/// <returns>Name of created temporary table</returns>
|
||||
public static string CreateTempTable(SqlConnection connection, string tableName)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Get table specification
|
||||
TableSpec tableSpec = GetTableSpec(connection, tableName);
|
||||
|
||||
//Drop temp table if it already exists
|
||||
connection.Execute($"IF OBJECT_ID('tempdb..{tableSpec.TempTableName}') IS NOT NULL DROP TABLE {tableSpec.TempTableName};");
|
||||
|
||||
//Create temp table
|
||||
connection.Execute($"CREATE TABLE {tableSpec.TempTableName}({string.Join(",", tableSpec.Columns.Select(c => $"{c.Name} {c.Definition}"))}, CONSTRAINT PK_{tableSpec.TempTableName} PRIMARY KEY CLUSTERED({string.Join(",", tableSpec.PrimaryKey.Select(c => $"{c.Name}"))}));");
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.AppendLine("WITH StagingCTE AS (");
|
||||
builder.Append($"SELECT st.*, ROW_NUMBER() OVER(PARTITION BY {string.Join(", ", tableSpec.PrimaryKey.Select(c=>c.Name))} ORDER BY {tableSpec.Columns.FirstOrDefault(c => c.Name.Equals("LastUpdateDT", StringComparison.CurrentCultureIgnoreCase) || c.Name.Equals("ReleaseDate", StringComparison.CurrentCultureIgnoreCase))?.Name}) RN FROM {tableSpec.StagingTableName} st");
|
||||
builder.AppendLine(")");
|
||||
builder.AppendLine($"INSERT INTO {tableSpec.TempTableName}({string.Join(", ", tableSpec.Columns.Select(c=>c.Name))})");
|
||||
builder.AppendLine($"SELECT {string.Join(", ", tableSpec.Columns.Select(c => c.Name))} FROM StagingCTE WHERE RN = 1 ORDER BY {string.Join(", ", tableSpec.PrimaryKey.Select(c=>c.Name))};");
|
||||
|
||||
connection.Execute(builder.ToString(),commandTimeout:600);
|
||||
|
||||
return tableSpec.TempTableName;
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
//Log and forward error
|
||||
logger.Error("CreateTableTable: failed to create temporary table for '{0}': {1}.", tableName, error.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates merge statement for given table
|
||||
/// </summary>
|
||||
/// <param name="connection">SQL connection to execute commands on</param>
|
||||
/// <param name="tableName">Name of table to generate merge statement for</param>
|
||||
/// <returns>Merge statement for given table</returns>
|
||||
private static string GenerateMerge(SqlConnection connection, string tableName)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Get table specification
|
||||
TableSpec tableSpec = GetTableSpec(connection, tableName);
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.AppendFormat("MERGE {0} AS TARGET", tableSpec.Name);
|
||||
builder.AppendLine("");
|
||||
|
||||
builder.AppendFormat("USING {0} AS SOURCE ON({1})", tableSpec.TempTableName, string.Join(" AND ", tableSpec.PrimaryKey.Select(c => $"TARGET.{c.Name} = SOURCE.{c.Name}")));
|
||||
builder.AppendLine("");
|
||||
|
||||
builder.Append("WHEN MATCHED");
|
||||
if (tableSpec.Columns.Exists(c => c.Name.Equals("LastUpdateDT", StringComparison.CurrentCultureIgnoreCase)))
|
||||
{
|
||||
builder.Append(" AND TARGET.LastUpdateDT < SOURCE.LastUpdateDT");
|
||||
}
|
||||
builder.AppendLine(" THEN");
|
||||
|
||||
builder.Append("UPDATE SET ");
|
||||
builder.Append(string.Join(", ", tableSpec.Columns.Where(c => !tableSpec.PrimaryKey.Contains(c)).Select(c => $"TARGET.{c.Name} = SOURCE.{c.Name}")));
|
||||
builder.AppendLine();
|
||||
|
||||
builder.AppendLine("WHEN NOT MATCHED BY TARGET THEN");
|
||||
|
||||
builder.Append("INSERT(");
|
||||
builder.Append(string.Join(", ", tableSpec.Columns.Select(c => c.Name)));
|
||||
builder.AppendLine(")");
|
||||
|
||||
builder.Append("VALUES(");
|
||||
builder.Append(string.Join(", ", tableSpec.Columns.Select(c => $"SOURCE.{c.Name}")));
|
||||
builder.AppendLine(");");
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
//Log and forward error
|
||||
logger.Error("GenerateMerge: failed to generate merge statement for '{0}': {1}.", tableName, error.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates bulk copy specification for given table
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Data type being bulk copied</typeparam>
|
||||
/// <param name="connection">SQL connection to execute commands on</param>
|
||||
/// <param name="tableName">Name of table to generate bulk copy for</param>
|
||||
/// <returns>Bulk copy for given table</returns>
|
||||
private static SqlBulkCopy GenerateBulkCopy<T>(SqlConnection connection, string tableName)
|
||||
{
|
||||
return GenerateBulkCopy(connection, tableName, typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates bulk copy specification for given table
|
||||
/// </summary>
|
||||
/// <param name="connection">SQL connection to execute commands on</param>
|
||||
/// <param name="tableName">Name of table to generate bulk copy for</param>
|
||||
/// <param name="type">Data type being bulk copied</param>
|
||||
/// <returns>Bulk copy for given table</returns>
|
||||
private static SqlBulkCopy GenerateBulkCopy(SqlConnection connection, string tableName, Type type)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Get table specification
|
||||
TableSpec tableSpec = GetTableSpec(connection, tableName);
|
||||
|
||||
//Get class properties
|
||||
List<string> properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Select(p => p.Name).ToList();
|
||||
|
||||
//Build bulk copy specification
|
||||
SqlBulkCopy bulkCopy = new SqlBulkCopy(connection)
|
||||
{
|
||||
BatchSize = 10000,
|
||||
NotifyAfter = 5000,
|
||||
EnableStreaming = true,
|
||||
DestinationTableName = tableSpec.StagingTableName
|
||||
};
|
||||
|
||||
foreach (ColumnSpec columnSpec in tableSpec.Columns)
|
||||
{
|
||||
string property = properties.FirstOrDefault(p => p.Equals(columnSpec.Name, StringComparison.CurrentCultureIgnoreCase));
|
||||
if (!string.IsNullOrEmpty(property))
|
||||
{
|
||||
bulkCopy.ColumnMappings.Add(property, columnSpec.Name);
|
||||
}
|
||||
}
|
||||
|
||||
return bulkCopy;
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
//Log and forward error
|
||||
logger.Error("GenerateBulkCopy: failed to generate bulk copy for '{0}': {1}.", tableName, error.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL to get the columns for the given table
|
||||
/// </summary>
|
||||
private const string SQL_GET_TABLE_COLUMNS = @"
|
||||
SELECT c.name AS Name,
|
||||
CASE t2.name
|
||||
WHEN 'char' THEN 'CHAR(' + CAST(c.max_length AS VARCHAR(10)) + ')'
|
||||
WHEN 'varchar' THEN 'VARCHAR(' + CASE c.max_length WHEN -1 THEN 'MAX' ELSE CAST(c.max_length AS VARCHAR(10)) END + ')'
|
||||
WHEN 'decimal' THEN 'DECIMAL(' + CAST(c.precision AS VARCHAR(4)) + ',' + CAST(c.scale AS VARCHAR(4)) + ')'
|
||||
ELSE UPPER(t2.name)
|
||||
END AS Definition
|
||||
FROM sys.columns c INNER JOIN
|
||||
sys.types AS t2 ON (c.system_type_id = t2.system_type_id) INNER JOIN
|
||||
sys.tables t ON (c.object_id = t.object_id)
|
||||
WHERE t.name = @tableName
|
||||
ORDER BY c.column_id";
|
||||
|
||||
/// <summary>
|
||||
/// SQL to get the primary key columns for the given table
|
||||
/// </summary>
|
||||
private const string SQL_GET_TABLE_PRIMARY_KEY = @"
|
||||
SELECT COLUMN_NAME AS Name
|
||||
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
|
||||
WHERE OBJECTPROPERTY(OBJECT_ID(CONSTRAINT_SCHEMA + '.' + QUOTENAME(CONSTRAINT_NAME)), 'IsPrimaryKey') = 1 AND
|
||||
TABLE_NAME = @tableName
|
||||
ORDER BY ORDINAL_POSITION";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the table specification for the given table
|
||||
/// </summary>
|
||||
/// <param name="connection">SQL connection to execute commands on</param>
|
||||
/// <param name="tableName">Name of table to get specification for</param>
|
||||
/// <returns>Table specification for the given table</returns>
|
||||
public static TableSpec GetTableSpec(SqlConnection connection, string tableName)
|
||||
{
|
||||
TableSpec tableSpec = new TableSpec() { Name = tableName };
|
||||
|
||||
//Load columns
|
||||
tableSpec.Columns.AddRange(connection.Query<ColumnSpec>(SQL_GET_TABLE_COLUMNS, new { tableName }));
|
||||
|
||||
//Load primary key
|
||||
tableSpec.PrimaryKey.AddRange(connection.Query<string>(SQL_GET_TABLE_PRIMARY_KEY, new { tableName }).Select(cn => tableSpec.Columns.First(c => c.Name.Equals(cn, StringComparison.CurrentCultureIgnoreCase))));
|
||||
|
||||
return tableSpec;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Truncates given table
|
||||
/// </summary>
|
||||
/// <param name="connection">SQL connection to execute commands on</param>
|
||||
/// <param name="tableName">Name of table to truncate</param>
|
||||
private static void TruncateTable(SqlConnection connection, string tableName)
|
||||
{
|
||||
try
|
||||
{
|
||||
logger.Debug("TruncateTable: truncating table '{0}'", tableName);
|
||||
|
||||
//Generate and execute SQL to truncate table
|
||||
string sql = $"TRUNCATE TABLE {tableName};";
|
||||
connection.Execute(sql);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
//Log but do not forward error
|
||||
logger.Error("TruncateTable: failed to truncate table '{0}': {1}.", tableName, error.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL to get indices on given table
|
||||
/// </summary>
|
||||
private const string SQL_GET_INDICES = @"
|
||||
SELECT DISTINCT
|
||||
ind.name AS Name,
|
||||
ind.is_primary_key AS IsPrimaryKey,
|
||||
ind.is_unique AS IsUnique,
|
||||
ind.is_unique_constraint AS IsUniqueConstraint
|
||||
FROM sys.indexes ind INNER JOIN
|
||||
sys.index_columns ic ON (ind.object_id = ic.object_id AND ind.index_id = ic.index_id) INNER JOIN
|
||||
sys.columns col ON (ic.object_id = col.object_id AND ic.column_id = col.column_id) INNER JOIN
|
||||
sys.tables t ON (ind.object_id = t.object_id)
|
||||
WHERE t.name = @tableName";
|
||||
|
||||
/// <summary>
|
||||
/// Disables all non-PK indices on given table
|
||||
/// </summary>
|
||||
/// <param name="connection">SQL connection to execute commands on</param>
|
||||
/// <param name="tableName">Name of table to disable non-PK indices for</param>
|
||||
private static void DisableIndicies(SqlConnection connection, string tableName)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Get all indices on table
|
||||
List<Index> indices = connection.Query<Index>(SQL_GET_INDICES, new { tableName }).ToList();
|
||||
|
||||
//Loop through all non-PK/non-cluster indices
|
||||
foreach (Index index in indices.Where(i => !i.IsPrimaryKey && !i.IsUnique && !i.IsUniqueConstraint))
|
||||
{
|
||||
//Generate and execute SQL to disable index
|
||||
string sql = $"ALTER INDEX {index.Name} ON {tableName} DISABLE;";
|
||||
connection.Execute(sql);
|
||||
}
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
//Log but do not forward error
|
||||
logger.Error("DisableIndicies: failed to disable non-PK indicies on table '{0}': {1}.", tableName, error.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rebuilds all indices on given table
|
||||
/// </summary>
|
||||
/// <param name="connection">SQL connection to execute commands on</param>So
|
||||
/// <param name="tableName">Name of table to rebuild indices for</param>
|
||||
private static void RebuildIndicies(SqlConnection connection, string tableName)
|
||||
{
|
||||
try
|
||||
{
|
||||
//Get all indices on table
|
||||
List<Index> indices = connection.Query<Index>(SQL_GET_INDICES, new { tableName }).ToList();
|
||||
|
||||
//Loop through indices
|
||||
foreach (Index index in indices.Where(i => !i.IsPrimaryKey && !i.IsUnique && !i.IsUniqueConstraint))
|
||||
{
|
||||
//Generate and execute SQL to rebuild index
|
||||
string sql = $"ALTER INDEX {index.Name} ON {tableName} REBUILD;";
|
||||
connection.Execute(sql);
|
||||
}
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
//Log but do not forward error
|
||||
logger.Error("RebuildIndicies: failed to rebuild indicies on table '{0}': {1}.", tableName, error.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+220
@@ -0,0 +1,220 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Dapper;
|
||||
using DataModel.Helpers;
|
||||
using DataModel.Models;
|
||||
using DataModel.Process;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using WorkerService.Models;
|
||||
|
||||
namespace WorkerService.Process
|
||||
{
|
||||
/// <summary>
|
||||
/// Data update processor
|
||||
/// </summary>
|
||||
public partial class UpdateProcessor
|
||||
{
|
||||
/// <summary>
|
||||
/// Shared logger instance
|
||||
/// </summary>
|
||||
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
/// <summary>
|
||||
/// Collection of configured data source update configurations
|
||||
/// </summary>
|
||||
public static readonly List<DataSourceConfig> configs = new List<DataSourceConfig>();
|
||||
|
||||
/// <summary>
|
||||
/// Static class initializer
|
||||
/// </summary>
|
||||
static UpdateProcessor()
|
||||
{
|
||||
foreach (string configFileName in Directory.GetFiles("dsconfig"))
|
||||
{
|
||||
//continue;
|
||||
DataSourceConfig config = JsonConvert.DeserializeObject<DataSourceConfig>(File.ReadAllText(configFileName));
|
||||
|
||||
if (config.IsEnabled) { configs.Add(config); }
|
||||
}
|
||||
}
|
||||
|
||||
public static void DoUpdate(string tableName, UpdateTypes updateType = UpdateTypes.Mass)
|
||||
{
|
||||
DataSourceConfig dataSourceConfig = configs.FirstOrDefault(c => c.TableName.Equals(tableName, StringComparison.CurrentCultureIgnoreCase));
|
||||
|
||||
//Get last data updates
|
||||
List<LastDataUpdate> lastDataUpdates = null;
|
||||
using (SqlConnection connection = LotFinderDB.GetConnection())
|
||||
{
|
||||
lastDataUpdates = GetLastDataUpdates(connection);
|
||||
}
|
||||
|
||||
LastDataUpdate lastDataUpdate = lastDataUpdates.FirstOrDefault(ldu => ldu.TableName.Equals(dataSourceConfig.TableName));
|
||||
DateTime? minDT = null;
|
||||
switch (updateType)
|
||||
{
|
||||
case UpdateTypes.Mass:
|
||||
minDT = null;
|
||||
break;
|
||||
case UpdateTypes.Daily:
|
||||
minDT = lastDataUpdate.DailyUpdateDT.AddMinutes(-3 * dataSourceConfig.DailyUpdateConfig.Interval);
|
||||
break;
|
||||
case UpdateTypes.Hourly:
|
||||
minDT = lastDataUpdate.HourlyUpdateDT.AddMinutes(-3 * dataSourceConfig.HourlyUpdateConfig.Interval);
|
||||
break;
|
||||
}
|
||||
|
||||
DoUpdate(dataSourceConfig, updateType, minDT);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets list of pending data update tasks
|
||||
/// </summary>
|
||||
/// <returns>Pending data update tasks</returns>
|
||||
public static List<DataUpdateTask> GetPendingUpdateTasks()
|
||||
{
|
||||
List<DataUpdateTask> pending = new List<DataUpdateTask>();
|
||||
|
||||
using (SqlConnection connection = LotFinderDB.GetConnection())
|
||||
{
|
||||
//Get last data updates
|
||||
List<LastDataUpdate> lastDataUpdates = GetLastDataUpdates(connection);
|
||||
|
||||
foreach (DataSourceConfig dataSourceConfig in configs)
|
||||
{
|
||||
LastDataUpdate lastDataUpdate = lastDataUpdates.FirstOrDefault(ldu => ldu.TableName.Equals(dataSourceConfig.TableName));
|
||||
|
||||
if (lastDataUpdate == null || (dataSourceConfig.MassUpdateConfig.Enabled && DateTime.Now > lastDataUpdate.MassUpdateDT.AddMinutes(dataSourceConfig.MassUpdateConfig.Interval)))
|
||||
{
|
||||
pending.Add(new DataUpdateTask() { Configuration = dataSourceConfig, UpdateType = UpdateTypes.Mass });
|
||||
}
|
||||
else if (dataSourceConfig.DailyUpdateConfig.Enabled && DateTime.Now > lastDataUpdate.DailyUpdateDT.AddMinutes(dataSourceConfig.DailyUpdateConfig.Interval))
|
||||
{
|
||||
pending.Add(new DataUpdateTask() { Configuration = dataSourceConfig, UpdateType = UpdateTypes.Daily, MinimumDT = lastDataUpdate.DailyUpdateDT.AddMinutes(-3 * dataSourceConfig.DailyUpdateConfig.Interval) });
|
||||
}
|
||||
else if (dataSourceConfig.HourlyUpdateConfig.Enabled && DateTime.Now > lastDataUpdate.HourlyUpdateDT.AddMinutes(dataSourceConfig.HourlyUpdateConfig.Interval))
|
||||
{
|
||||
pending.Add(new DataUpdateTask() { Configuration = dataSourceConfig, UpdateType = UpdateTypes.Hourly, MinimumDT = lastDataUpdate.DailyUpdateDT.AddMinutes(-3 * dataSourceConfig.DailyUpdateConfig.Interval) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pending;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the data update
|
||||
/// </summary>
|
||||
/// <param name="dataUpdateTask">Data update task to execute</param>
|
||||
/// <returns>Data update results</returns>
|
||||
public static DataUpdate DoUpdate(DataUpdateTask dataUpdateTask)
|
||||
{
|
||||
logger.Info($"Starting [{dataUpdateTask.UpdateType}] data update for {dataUpdateTask.Configuration.TableName}...");
|
||||
return DoUpdate(dataUpdateTask.Configuration, dataUpdateTask.UpdateType, dataUpdateTask.MinimumDT);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the data update
|
||||
/// </summary>
|
||||
/// <param name="config">Data source configuration</param>
|
||||
/// <param name="updateType">Type of update to perform</param>
|
||||
/// <param name="minDT">Minimum timestamp to update data from</param>
|
||||
/// <returns>Data update results</returns>
|
||||
public static DataUpdate DoUpdate(DataSourceConfig config, UpdateTypes updateType, DateTime? minDT)
|
||||
{
|
||||
//Log start of data update
|
||||
DataUpdate dataUpdate = new DataUpdate()
|
||||
{
|
||||
SourceSystem = config.SourceSystem,
|
||||
SourceData = config.SourceData,
|
||||
TableName = config.TableName,
|
||||
UpdateType = updateType,
|
||||
StartDT = DateTime.Now,
|
||||
NumberRecords = 0
|
||||
};
|
||||
|
||||
//Get data update configuration details
|
||||
DataUpdateConfig updateConfig;
|
||||
switch (updateType)
|
||||
{
|
||||
case UpdateTypes.Hourly:
|
||||
updateConfig = config.HourlyUpdateConfig;
|
||||
break;
|
||||
case UpdateTypes.Daily:
|
||||
updateConfig = config.DailyUpdateConfig;
|
||||
break;
|
||||
case UpdateTypes.Mass:
|
||||
updateConfig = config.MassUpdateConfig;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(updateType), updateType, null);
|
||||
}
|
||||
|
||||
using (SqlConnection connection = LotFinderDB.GetConnection())
|
||||
{
|
||||
LogDataUpdateStart(connection, dataUpdate);
|
||||
|
||||
//Clear destination table if needed
|
||||
if (updateConfig.PrepurgeData)
|
||||
{
|
||||
TruncateTable(connection, config.TableName);
|
||||
}
|
||||
|
||||
Type sourceType = config.DataFetchFunction.Method.ReturnType.GenericTypeArguments[0];
|
||||
|
||||
//Fetch data
|
||||
IEnumerable<dynamic> data = config.DataFetchFunction(minDT);
|
||||
|
||||
//Generate SQL to merge temp data to destination table
|
||||
string mergeSQL = GenerateMerge(connection, config.TableName);
|
||||
|
||||
foreach (var batch in data.BatchGroup(1000000))
|
||||
{
|
||||
//Setup temp table
|
||||
string stagingTableName = CreateStagingTable(connection, config.TableName);
|
||||
|
||||
//Copy data to temp table
|
||||
SqlBulkCopy bulkCopy = GenerateBulkCopy(connection, config.TableName, sourceType);
|
||||
IDataReader reader = new GenericListDataReader(batch, sourceType);
|
||||
bulkCopy.WriteToServer(reader);
|
||||
dataUpdate.NumberRecords += batch.Count;
|
||||
|
||||
//Index temp table
|
||||
RebuildIndicies(connection, stagingTableName);
|
||||
|
||||
//Copy to temp table
|
||||
string tempTableName = CreateTempTable(connection, config.TableName);
|
||||
|
||||
//Merge data from temp table to destination table
|
||||
connection.Execute(mergeSQL, commandTimeout: 6000);
|
||||
|
||||
logger.Debug("DoUpdate: {0:n0} rows merged to {1}", dataUpdate.NumberRecords, config.TableName);
|
||||
}
|
||||
|
||||
//Run post processing action if configured
|
||||
if (config.PostProcessingAction != null)
|
||||
{
|
||||
config.PostProcessingAction();
|
||||
}
|
||||
|
||||
//Re-index destination table if needed
|
||||
if (updateConfig.ReIndexData)
|
||||
{
|
||||
RebuildIndicies(connection, dataUpdate.TableName);
|
||||
}
|
||||
|
||||
//Update data update entry
|
||||
dataUpdate.WasSuccessful = true;
|
||||
LogDataUpdateEnd(connection, dataUpdate);
|
||||
}
|
||||
|
||||
return dataUpdate;
|
||||
}
|
||||
}
|
||||
}
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SQLite;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Commons.Helpers;
|
||||
using Commons.Models;
|
||||
using Commons.Process;
|
||||
using Dapper;
|
||||
|
||||
namespace WorkerService.Process
|
||||
{
|
||||
public partial class Updater
|
||||
{
|
||||
private const string SQL_GET_MIS_DATA = @"
|
||||
select
|
||||
ItemNumber, BranchCode, SequenceNumber, MisNumber, RevID,
|
||||
CharNumber, TestGroup, TestDefinition, TestDescription, SamplingType, SamplingValue, ToolsGauges, WorkInstructions,
|
||||
Status, ReleaseDate, ObsoleteDate
|
||||
FROM infodb.MisInfo info INNER JOIN
|
||||
relationdb.MisRelation rel ON (info.RevFK = rel.RevFK) INNER JOIN
|
||||
detaildb.MisDetail det ON (rel.MisFK = det.MisFK) INNER JOIN
|
||||
partdb.MisPart part ON (det.PartFK = part.PartFK)
|
||||
ORDER BY ItemNumber, BranchCode, SequenceNumber, MisNumber, RevID, Status, CharNumber";
|
||||
|
||||
public static IEnumerable<MisData> GetMisData()
|
||||
{
|
||||
Task misInfoTask = Task.Run(() => PrepareMisInfo());
|
||||
Task misRelationTask = Task.Run(() => PrepareMisRelation());
|
||||
Task misDetailTask = Task.Run(() => PrepareMisDetail());
|
||||
Task misPartTask = Task.Run(() => PrepareMisPart());
|
||||
|
||||
Task.WaitAll(misInfoTask, misRelationTask, misDetailTask, misPartTask);
|
||||
|
||||
if (File.Exists("misdata.db"))
|
||||
{
|
||||
File.Delete("misdata.db");
|
||||
}
|
||||
SQLiteConnection.CreateFile("misdata.db");
|
||||
using (SQLiteConnection sqlite = new SQLiteConnection(@"Data Source=misdata.db;Version=3;Journal Mode=Off;"))
|
||||
{
|
||||
sqlite.Open();
|
||||
|
||||
//Attach databases
|
||||
sqlite.Execute("ATTACH DATABASE 'misinfo.db' AS infodb;");
|
||||
sqlite.Execute("ATTACH DATABASE 'misrelation.db' AS relationdb;");
|
||||
sqlite.Execute("ATTACH DATABASE 'misdetail.db' AS detaildb;");
|
||||
sqlite.Execute("ATTACH DATABASE 'mispart.db' AS partdb;");
|
||||
|
||||
foreach (MisData misData in sqlite.Query<MisData>(SQL_GET_MIS_DATA, buffered: false))
|
||||
{
|
||||
yield return misData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void PrepareMisInfo()
|
||||
{
|
||||
return;
|
||||
if (File.Exists("misinfo.db"))
|
||||
{
|
||||
File.Delete("misinfo.db");
|
||||
}
|
||||
SQLiteConnection.CreateFile("misinfo.db");
|
||||
|
||||
//Fetch MIS info
|
||||
List<MisInfo> misInfos = CMS.GetMisInfos().ToList();
|
||||
|
||||
//Lookup obsolete date by backlevel release
|
||||
foreach (var group in misInfos.GroupBy(m => new { m.MisNumber, m.RevID }))
|
||||
{
|
||||
DateTime? obsoleteDate = group.Where(g => g.Status.Equals("BackLevel") && g.ReleaseDate != null).Select(g => g.ReleaseDate).FirstOrDefault();
|
||||
|
||||
foreach (MisInfo current in group.Where(g => g.Status.Equals("Current")))
|
||||
{
|
||||
current.ObsoleteDate = obsoleteDate;
|
||||
}
|
||||
}
|
||||
|
||||
//Lookup obsolete date by next revision release
|
||||
var lookup = misInfos.Where(m => m.ReleaseDate != null).GroupBy(m => new { m.MisNumber, m.RevID, m.Status }).Select(g => new { g.Key.MisNumber, g.Key.RevID, g.Key.Status, ReleaseDate = g.Min(m => m.ReleaseDate) }).ToDictionary(g => new { g.MisNumber, g.RevID, g.Status }, g => g.ReleaseDate);
|
||||
foreach (MisInfo misInfo in misInfos.Where(m => !m.ObsoleteDate.HasValue))
|
||||
{
|
||||
DateTime? obsoleteDate;
|
||||
if (lookup.TryGetValue(new { misInfo.MisNumber, misInfo.RevID, misInfo.Status }, out obsoleteDate))
|
||||
{
|
||||
misInfo.ObsoleteDate = obsoleteDate;
|
||||
}
|
||||
}
|
||||
|
||||
//Write MIS info to database
|
||||
using (SQLiteConnection sqlite = new SQLiteConnection(@"Data Source=misinfo.db;Version=3;Journal Mode=Off;"))
|
||||
{
|
||||
sqlite.Open();
|
||||
|
||||
//Create table for MIS info
|
||||
sqlite.Execute("CREATE TABLE MisInfo(RevFK TEXT, MisNumber TEXT, RevID Text, Status TEXT, ReleaseDate DATETIME, ObsoleteDate DATETIME);");
|
||||
|
||||
foreach (var batch in misInfos.BatchGroup(10000))
|
||||
{
|
||||
using (SQLiteTransaction transaction = sqlite.BeginTransaction())
|
||||
{
|
||||
foreach (MisInfo misInfo in batch)
|
||||
{
|
||||
sqlite.Execute("INSERT INTO MisInfo(RevFK, MisNumber, RevID, Status, ReleaseDate, ObsoleteDate) VALUES(@RevFK, @MisNumber, @RevID, @Status, @ReleaseDate, @ObsoleteDate)", misInfo);
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
//Add index on FK field
|
||||
sqlite.Execute("CREATE INDEX IDX_MisInfo_RevFK ON MisInfo(RevFK);");
|
||||
|
||||
sqlite.Execute("VACUUM");
|
||||
}
|
||||
}
|
||||
|
||||
private static void PrepareMisRelation()
|
||||
{
|
||||
return;
|
||||
if (File.Exists("misrelation.db"))
|
||||
{
|
||||
File.Delete("misrelation.db");
|
||||
}
|
||||
SQLiteConnection.CreateFile("misrelation.db");
|
||||
|
||||
IEnumerable<MisRelation> misRelations = CMS.GetMisRelations();
|
||||
|
||||
//Write MIS relation to database
|
||||
using (SQLiteConnection sqlite = new SQLiteConnection(@"Data Source=misrelation.db;Version=3;Journal Mode=Off;"))
|
||||
{
|
||||
sqlite.Open();
|
||||
|
||||
//Create table for MIS relation
|
||||
sqlite.Execute("CREATE TABLE MisRelation(RevFK TEXT, MisFK TEXT);");
|
||||
|
||||
foreach (var batch in misRelations.BatchGroup(10000))
|
||||
{
|
||||
using (SQLiteTransaction transaction = sqlite.BeginTransaction())
|
||||
{
|
||||
foreach (MisRelation misRelation in batch)
|
||||
{
|
||||
sqlite.Execute("INSERT INTO MisRelation(RevFK, MisFK) VALUES(@RevFK, @MisFK)", misRelation);
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
//Add indices on FK fields
|
||||
sqlite.Execute("CREATE INDEX IDX_MisRelation_RevFK ON MisRelation(RevFK);");
|
||||
sqlite.Execute("CREATE INDEX IDX_MisRelation_MisFK ON MisRelation(MisFK);");
|
||||
|
||||
sqlite.Execute("VACUUM");
|
||||
}
|
||||
}
|
||||
|
||||
public static void PrepareMisDetail()
|
||||
{
|
||||
return;
|
||||
if (File.Exists("misdetail.db"))
|
||||
{
|
||||
File.Delete("misdetail.db");
|
||||
}
|
||||
SQLiteConnection.CreateFile("misdetail.db");
|
||||
|
||||
IEnumerable<MisDetail> misDetails = CMS.GetMisDetails();
|
||||
|
||||
//Write MIS relation to database
|
||||
using (SQLiteConnection sqlite = new SQLiteConnection(@"Data Source=misdetail.db;Version=3;Journal Mode=Off;"))
|
||||
{
|
||||
sqlite.Open();
|
||||
|
||||
//Create table for MIS detail
|
||||
sqlite.Execute("CREATE TABLE MisDetail(MisFK TEXT, PartFK TEXT, CharNumber TEXT, TestGroup TEXT, TestDefinition TEXT, TestDescription TEXT, SamplingType TEXT, SamplingValue TEXT, ToolsGauges TEXT, WorkInstructions TEXT);");
|
||||
|
||||
foreach (var batch in misDetails.BatchGroup(250000))
|
||||
{
|
||||
using (SQLiteTransaction transaction = sqlite.BeginTransaction())
|
||||
{
|
||||
foreach (MisDetail misDetail in batch)
|
||||
{
|
||||
sqlite.Execute("INSERT INTO MisDetail(MisFK, PartFK, CharNumber, TestGroup, TestDefinition, TestDescription, SamplingType, SamplingValue, ToolsGauges, WorkInstructions) VALUES(@MisFK, @PartFK, @CharNumber, @TestGroup, @TestDefinition, @TestDescription, @SamplingType, @SamplingValue, @ToolsGauges, @WorkInstructions)", misDetail);
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
//Add indices on FK fields
|
||||
sqlite.Execute("CREATE INDEX IDX_MisDetail_MisFK ON MisDetail(MisFK);");
|
||||
sqlite.Execute("CREATE INDEX IDX_MisDetail_PartFK ON MisDetail(PartFK);");
|
||||
|
||||
sqlite.Execute("VACUUM");
|
||||
}
|
||||
}
|
||||
|
||||
private static void PrepareMisPart()
|
||||
{
|
||||
return;
|
||||
if (File.Exists("mispart.db"))
|
||||
{
|
||||
File.Delete("mispart.db");
|
||||
}
|
||||
SQLiteConnection.CreateFile("mispart.db");
|
||||
|
||||
IEnumerable<MisPart> misParts = CMS.GetMisParts();
|
||||
|
||||
//Write MIS relation to database
|
||||
using (SQLiteConnection sqlite = new SQLiteConnection(@"Data Source=mispart.db;Version=3;Journal Mode=Off;"))
|
||||
{
|
||||
sqlite.Open();
|
||||
|
||||
//Create table for MIS detail
|
||||
sqlite.Execute("CREATE TABLE MisPart(PartFK TEXT, ItemNumber TEXT, BranchCode TEXT, SequenceNumber TEXT);");
|
||||
|
||||
foreach (var batch in misParts.BatchGroup(100000))
|
||||
{
|
||||
using (SQLiteTransaction transaction = sqlite.BeginTransaction())
|
||||
{
|
||||
foreach (MisPart misPart in batch)
|
||||
{
|
||||
sqlite.Execute("INSERT INTO MisPart(PartFK, ItemNumber, BranchCode, SequenceNumber) VALUES(@PartFK, @ItemNumber, @BranchCode, @SequenceNumber);", misPart);
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
//Add indices on FK fields
|
||||
sqlite.Execute("CREATE INDEX IDX_MisPart_PartFK ON MisPart(PartFK);");
|
||||
|
||||
sqlite.Execute("VACUUM");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+217
@@ -0,0 +1,217 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using DataModel.Models;
|
||||
using Microsoft.AspNet.SignalR.Client;
|
||||
using NLog;
|
||||
using WorkerService.Helpers;
|
||||
using WorkerService.Models;
|
||||
using WorkerService.Models.Reporting;
|
||||
|
||||
namespace WorkerService.Process
|
||||
{
|
||||
/// <summary>
|
||||
/// Background work processor
|
||||
/// </summary>
|
||||
public class WorkProcessor : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Default wait/sleep interval between processing runs
|
||||
/// </summary>
|
||||
private const int WAIT_INTERVAL = 5000;
|
||||
|
||||
/// <summary>
|
||||
/// Shared logger instance
|
||||
/// </summary>
|
||||
private static readonly Logger logger = LogManager.GetLogger("WorkProcessor");
|
||||
|
||||
/// <summary>
|
||||
/// Thread cancellation flag
|
||||
/// </summary>
|
||||
private readonly ManualResetEvent cancel = new ManualResetEvent(false);
|
||||
|
||||
/// <summary>
|
||||
/// Current work status message
|
||||
/// </summary>
|
||||
public string Status
|
||||
{
|
||||
get => _status;
|
||||
set
|
||||
{
|
||||
if (string.Equals(_status, value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_status = value;
|
||||
logger.Info("Status: {0}", _status);
|
||||
PublishStatus(_status);
|
||||
}
|
||||
}
|
||||
private string _status;
|
||||
|
||||
/// <summary>
|
||||
/// Publishes status message update to the hub
|
||||
/// </summary>
|
||||
/// <param name="status">Status message to publish</param>
|
||||
private async void PublishStatus(string status)
|
||||
{
|
||||
try
|
||||
{
|
||||
string hubHost = ConfigurationManager.AppSettings["HubHost"];
|
||||
using (HubConnection hubConnection = new HubConnection(hubHost))
|
||||
{
|
||||
IHubProxy hubProxy = hubConnection.CreateHubProxy("StatusHub");
|
||||
await hubConnection.Start();
|
||||
|
||||
StatusUpdate update = new StatusUpdate()
|
||||
{
|
||||
Message = status,
|
||||
Timestamp = DateTime.Now
|
||||
};
|
||||
await hubProxy.Invoke("SetStatus", update);
|
||||
}
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
logger.Error("PublishStatus: failed to publish status update to hub: {0}.", error.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Publishes update for the search to the hub
|
||||
/// </summary>
|
||||
/// <param name="search">Search to publish update for</param>
|
||||
private async void PublishSearchUpdate(Search search)
|
||||
{
|
||||
try
|
||||
{
|
||||
string hubHost = ConfigurationManager.AppSettings["HubHost"];
|
||||
using (HubConnection hubConnection = new HubConnection(hubHost))
|
||||
{
|
||||
IHubProxy hubProxy = hubConnection.CreateHubProxy("StatusHub");
|
||||
await hubConnection.Start();
|
||||
|
||||
SearchUpdate searchUpdate = new SearchUpdate(search);
|
||||
await hubProxy.Invoke("PublishSearchUpdate", searchUpdate);
|
||||
}
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
logger.Error("PublishSearchUpdate: failed to publish search update to hub: {0}.", error.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the worker thread
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
logger.Info("Background processing thread starting...");
|
||||
|
||||
Thread workThread = new Thread(() =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
DoWork();
|
||||
|
||||
//Wait to continue
|
||||
if (cancel.WaitOne(WAIT_INTERVAL))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
workThread.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the worker thread
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
logger.Info("Background processing thread stopping...");
|
||||
|
||||
cancel.Set();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Work processing action
|
||||
/// </summary>
|
||||
private void DoWork()
|
||||
{
|
||||
try
|
||||
{
|
||||
//Verify all data sources up to date
|
||||
List<DataUpdateTask> pending = UpdateProcessor.GetPendingUpdateTasks();
|
||||
if (pending.Any())
|
||||
{
|
||||
Status = "Updating data cache";
|
||||
Parallel.ForEach(pending, new ParallelOptions() { MaxDegreeOfParallelism = 8 }, pendingTask => { UpdateProcessor.DoUpdate(pendingTask); });
|
||||
}
|
||||
else
|
||||
{
|
||||
//Reset any partially completed searches
|
||||
LotFinderDBExt.ResetPartialSearches();
|
||||
|
||||
//Check for queued searches
|
||||
Search search = LotFinderDBExt.GetNextSearch();
|
||||
if (search != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Status = $"Processing search #{search.ID}";
|
||||
|
||||
//Start search
|
||||
LotFinderDBExt.StartSearch(search);
|
||||
PublishSearchUpdate(search);
|
||||
|
||||
//Do search
|
||||
SearchModel searchModel = search.ToSearchModel();
|
||||
LotFinderDBExt.Search(searchModel);
|
||||
|
||||
//Record end timestamp
|
||||
search.EndDT = DateTime.Now;
|
||||
|
||||
//Generate output
|
||||
search.Results = ExcelWriter.Generate(searchModel);
|
||||
|
||||
File.WriteAllBytes($"search_{search.ID}.xlsx", search.Results);
|
||||
|
||||
//Complete search
|
||||
LotFinderDBExt.CompleteSearch(search, true);
|
||||
PublishSearchUpdate(search);
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
//Log error and mark search as failed
|
||||
logger.Error("DoWork: failed to process search: {0}.", error.Message);
|
||||
search.EndDT = DateTime.Now;
|
||||
LotFinderDBExt.CompleteSearch(search, false);
|
||||
PublishSearchUpdate(search);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Status = "Idle";
|
||||
}
|
||||
catch (Exception error)
|
||||
{
|
||||
//Log but do not forward error
|
||||
logger.Error("DoWork: work processing run failed: {0}.", error.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops worker thread
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+73
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Dapper;
|
||||
using DataModel;
|
||||
using Topshelf;
|
||||
using WorkerService.Helpers;
|
||||
using WorkerService.Models;
|
||||
using WorkerService.Models.Reporting;
|
||||
using WorkerService.Process;
|
||||
|
||||
namespace WorkerService
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
string cs = Config.LotFinderDBCS;
|
||||
|
||||
//Load app domain assemblies
|
||||
var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => !a.IsDynamic).ToList();
|
||||
var loadedPaths = loadedAssemblies.Select(a => a.Location).ToArray();
|
||||
|
||||
var referencedPaths = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll");
|
||||
var toLoad = referencedPaths.Where(r => !loadedPaths.Contains(r, StringComparer.InvariantCultureIgnoreCase)).ToList();
|
||||
toLoad.ForEach(path => loadedAssemblies.Add(AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(path))));
|
||||
|
||||
//Override defaults
|
||||
SqlMapper.Settings.CommandTimeout = 1200 * 50;
|
||||
Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
|
||||
|
||||
DataSourceConfig dsConfig = UpdateProcessor.configs.FirstOrDefault(c => c.TableName.Contains("User"));
|
||||
UpdateProcessor.DoUpdate(dsConfig.TableName);
|
||||
|
||||
return;
|
||||
|
||||
if (args.Length > 0 && args[0].Equals("REFRESH_ARCHIVE", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
Parallel.ForEach(UpdateProcessor.configs.Where(c => c.TableName.Contains("Hist")), config =>
|
||||
{
|
||||
UpdateProcessor.DoUpdate(config.TableName);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Host host = HostFactory.New(x =>
|
||||
{
|
||||
x.UseNLog();
|
||||
|
||||
x.StartAutomatically();
|
||||
x.EnableServiceRecovery(rc => { rc.RestartService(1); });
|
||||
x.EnableShutdown();
|
||||
|
||||
x.Service<WorkProcessor>(s =>
|
||||
{
|
||||
s.ConstructUsing(name => new WorkProcessor());
|
||||
s.WhenStarted(tc => tc.Start());
|
||||
s.WhenStopped(tc => tc.Stop());
|
||||
});
|
||||
x.RunAsLocalSystem();
|
||||
|
||||
x.SetDescription("JDE scoping search processor");
|
||||
x.SetDisplayName("JDE_SearchProcessor");
|
||||
x.SetServiceName("JDE_SearchProcessor");
|
||||
});
|
||||
|
||||
host.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("WorkerService")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("WorkerService")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("cd1dcd5b-5d89-445d-a8ed-9d8bada0e36d")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("2.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("2.0.0.0")]
|
||||
Executable
+70
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="MyApplication.app" />
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<!-- UAC Manifest Options
|
||||
If you want to change the Windows User Account Control level replace the
|
||||
requestedExecutionLevel node with one of the following.
|
||||
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
|
||||
|
||||
Specifying requestedExecutionLevel element will disable file and registry virtualization.
|
||||
Remove this element if your application requires this virtualization for backwards
|
||||
compatibility.
|
||||
-->
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
</requestedPrivileges>
|
||||
<applicationRequestMinimum>
|
||||
<PermissionSet class="System.Security.PermissionSet" version="1" Unrestricted="true" ID="Custom" SameSite="site" />
|
||||
<defaultAssemblyRequest permissionSetReference="Custom" />
|
||||
</applicationRequestMinimum>
|
||||
</security>
|
||||
</trustInfo>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- A list of the Windows versions that this application has been tested on and is
|
||||
is designed to work with. Uncomment the appropriate elements and Windows will
|
||||
automatically selected the most compatible environment. -->
|
||||
<!-- Windows Vista -->
|
||||
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
|
||||
<!-- Windows 7 -->
|
||||
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
|
||||
<!-- Windows 8 -->
|
||||
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
|
||||
<!-- Windows 8.1 -->
|
||||
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
|
||||
<!-- Windows 10 -->
|
||||
<!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->
|
||||
</application>
|
||||
</compatibility>
|
||||
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
|
||||
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
|
||||
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
|
||||
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. -->
|
||||
<!--
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
-->
|
||||
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
|
||||
<!--
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
processorArchitecture="*"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
-->
|
||||
</assembly>
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
--Add any work orders issued material from flagged work orders
|
||||
WITH CWO_D AS(
|
||||
SELECT DISTINCT wo.WorkOrderNumber,
|
||||
wo.LotNumber,
|
||||
wo.BranchCode,
|
||||
wo.ShortItemNumber
|
||||
FROM dbo.WorkOrderComponent AS woc INNER JOIN
|
||||
dbo.WorkOrder AS wo ON (woc.WorkOrderNumber = wo.WorkOrderNumber) INNER JOIN
|
||||
#Temp_WO AS t_wo ON (woc.LotNumber = t_wo.LotNumber AND woc.ShortItemNumber = t_wo.ShortItemNumber)
|
||||
)
|
||||
MERGE #Temp_WO AS TARGET
|
||||
USING CWO_D AS SOURCE
|
||||
ON (TARGET.WorkOrderNumber = SOURCE.WorkOrderNumber)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET TARGET.PartsList = 1
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (WorkOrderNumber, LotNumber, BranchCode, ShortItemNumber, PartsList)
|
||||
VALUES (SOURCE.WorkOrderNumber, COALESCE(SOURCE.LotNumber, CAST(SOURCE.WorkOrderNumber AS VARCHAR(8))), SOURCE.BranchCode, SOURCE.ShortItemNumber, 1);
|
||||
|
||||
WITH CWO_D AS(
|
||||
SELECT DISTINCT wo.WorkOrderNumber,
|
||||
wo.LotNumber,
|
||||
wo.BranchCode,
|
||||
wo.ShortItemNumber
|
||||
FROM dbo.LotUsage AS lu INNER JOIN
|
||||
dbo.WorkOrder AS wo ON (lu.WorkOrderNumber = wo.WorkOrderNumber) INNER JOIN
|
||||
#Temp_WO AS t_wo ON (lu.LotNumber = t_wo.LotNumber AND lu.ShortItemNumber = t_wo.ShortItemNumber)
|
||||
)
|
||||
MERGE #Temp_WO AS TARGET
|
||||
USING CWO_D AS SOURCE
|
||||
ON (TARGET.WorkOrderNumber = SOURCE.WorkOrderNumber)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET TARGET.CARDEX = 1
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (WorkOrderNumber, LotNumber, BranchCode, ShortItemNumber, CARDEX)
|
||||
VALUES (SOURCE.WorkOrderNumber, COALESCE(SOURCE.LotNumber, CAST(SOURCE.WorkOrderNumber AS VARCHAR(8))), SOURCE.BranchCode, SOURCE.ShortItemNumber, 1);
|
||||
Executable
+1017
File diff suppressed because it is too large
Load Diff
Executable
+482
@@ -0,0 +1,482 @@
|
||||
<#@ template language="C#" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ import namespace="System.Linq" #>
|
||||
<#@ import namespace="System.Text" #>
|
||||
<#@ import namespace="System.Collections.Generic" #>
|
||||
<#@ import namespace="WorkerService.Models.Reporting" #>
|
||||
<#@ import namespace="WorkerService.Helpers" #>
|
||||
--Setup flagged work order temp table
|
||||
IF OBJECT_ID('tempdb.dbo.#Temp_WO', 'U') IS NOT NULL
|
||||
BEGIN
|
||||
DROP TABLE #Temp_WO;
|
||||
END
|
||||
CREATE TABLE #Temp_WO (
|
||||
WorkOrderNumber BIGINT NOT NULL,
|
||||
LotNumber VARCHAR(30) NULL,
|
||||
BranchCode VARCHAR(12) NULL,
|
||||
ShortItemNumber BIGINT NOT NULL,
|
||||
ManuallySpecified BIT NOT NULL DEFAULT 0,
|
||||
SplitOrder BIT NOT NULL DEFAULT 0,
|
||||
CARDEX BIT NOT NULL DEFAULT 0,
|
||||
PartsList BIT NOT NULL DEFAULT 0,
|
||||
Flagged BIT NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY CLUSTERED(WorkOrderNumber)
|
||||
);
|
||||
CREATE INDEX TIX_Temp_WO_Lookup ON #Temp_WO(LotNumber, BranchCode, ShortItemNumber);
|
||||
<# if(Model.WorkOrderFilterEnabled) { #>
|
||||
|
||||
--Add manually specified work order numbers to flagged list
|
||||
WITH WOP_CTE AS(
|
||||
SELECT DISTINCT wo.WorkOrderNumber,
|
||||
wo.LotNumber,
|
||||
wo.BranchCode,
|
||||
wo.ShortItemNumber
|
||||
FROM dbo.WorkOrder AS wo INNER JOIN
|
||||
@p_WorkOrderFilter AS pwof ON (wo.WorkOrderNumber = pwof.WorkOrderNumber)
|
||||
)
|
||||
MERGE #Temp_WO AS TARGET
|
||||
USING WOP_CTE AS SOURCE
|
||||
ON (TARGET.WorkOrderNumber = SOURCE.WorkOrderNumber)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET TARGET.ManuallySpecified = 1
|
||||
WHEN NOT MATCHED BY TARGET THEN
|
||||
INSERT(WorkOrderNumber, LotNumber, BranchCode, ShortItemNumber, ManuallySpecified)
|
||||
VALUES(SOURCE.WorkOrderNumber, SOURCE.LotNumber, SOURCE.BranchCode, SOURCE.ShortItemNumber, 1);
|
||||
|
||||
<#@ include file="SplitWorkOrderQuery.txt" #>
|
||||
<# } #>
|
||||
<# if(Model.ItemNumberFilterEnabled) { #>
|
||||
|
||||
--Setup item number filter temp table
|
||||
IF OBJECT_ID('tempdb.dbo.#P_ItemNumbers', 'U') IS NOT NULL
|
||||
BEGIN
|
||||
DROP TABLE #P_ItemNumbers;
|
||||
END
|
||||
CREATE TABLE #P_ItemNumbers (
|
||||
ItemNumber VARCHAR(25) NOT NULL,
|
||||
PRIMARY KEY CLUSTERED(ItemNumber)
|
||||
);
|
||||
|
||||
INSERT INTO #P_ItemNumbers(ItemNumber)
|
||||
SELECT DISTINCT LTRIM(RTRIM(pinf.ItemNumber))
|
||||
FROM @p_ItemNumberFilter AS pinf
|
||||
WHERE LTRIM(RTRIM(pinf.ItemNumber)) IS NOT NULL;
|
||||
<# } #>
|
||||
<# if(Model.ProfitCenterFilterEnabled || Model.WorkCenterFilterEnabled) { #>
|
||||
|
||||
--Setup profit center / work center filter temp table
|
||||
IF OBJECT_ID('tempdb.dbo.#P_WorkCenters', 'U') IS NOT NULL
|
||||
BEGIN
|
||||
DROP TABLE #P_WorkCenters;
|
||||
END
|
||||
CREATE TABLE #P_WorkCenters (
|
||||
Code VARCHAR(12) NOT NULL,
|
||||
PRIMARY KEY CLUSTERED(Code)
|
||||
);
|
||||
<# } #>
|
||||
<# if(Model.ProfitCenterFilterEnabled) { #>
|
||||
WITH WCF_CTE AS(
|
||||
SELECT LTRIM(RTRIM(oh.WorkCenterCode)) AS Code
|
||||
FROM @p_ProfitCenterFilter AS ppcf INNER JOIN
|
||||
dbo.OrgHierarchy AS oh ON (LTRIM(RTRIM(ppcf.Code)) = oh.ProfitCenterCode)
|
||||
)
|
||||
MERGE INTO #P_WorkCenters AS TARGET
|
||||
USING WCF_CTE AS SOURCE
|
||||
ON (TARGET.Code = SOURCE.Code)
|
||||
WHEN NOT MATCHED BY TARGET THEN
|
||||
INSERT(Code)
|
||||
VALUES(SOURCE.Code);
|
||||
<# } #>
|
||||
<# if(Model.WorkCenterFilterEnabled) { #>
|
||||
WITH WCF_CTE AS(
|
||||
SELECT DISTINCT pwcf.Code
|
||||
FROM @p_WorkCenterFilter AS pwcf
|
||||
WHERE LTRIM(RTRIM(pwcf.Code)) IS NOT NULL
|
||||
)
|
||||
MERGE INTO #P_WorkCenters AS TARGET
|
||||
USING WCF_CTE AS SOURCE
|
||||
ON (TARGET.Code = SOURCE.Code)
|
||||
WHEN NOT MATCHED BY TARGET THEN
|
||||
INSERT(Code)
|
||||
VALUES(SOURCE.Code);
|
||||
<# } #>
|
||||
<# if(Model.ComponentLotFilterEnabled) { #>
|
||||
|
||||
--Add downstream product for manually specified component lots
|
||||
WITH CLN_CTE AS(
|
||||
SELECT DISTINCT l.LotNumber,
|
||||
l.ShortItemNumber,
|
||||
l.BranchCode
|
||||
FROM @p_ComponentLotFilter AS pclf INNER JOIN
|
||||
dbo.Lot AS l ON (LTRIM(RTRIM(pclf.ComponentLotNumber)) = l.LotNumber AND LTRIM(RTRIM(pclf.ItemNumber)) = l.ItemNumber)
|
||||
),
|
||||
CLN_WO AS(
|
||||
SELECT wo.WorkOrderNumber,
|
||||
wo.BranchCode,
|
||||
wo.LotNumber,
|
||||
wo.ShortItemNumber
|
||||
FROM CLN_CTE cln INNER JOIN
|
||||
dbo.WorkOrderComponent AS woc ON (cln.LotNumber = woc.LotNumber AND cln.ShortItemNumber = woc.ShortItemNumber AND cln.BranchCode = woc.BranchCode) INNER JOIN
|
||||
dbo.WorkOrder AS wo ON (woc.WorkOrderNumber = wo.WorkOrderNumber)
|
||||
UNION ALL
|
||||
SELECT wo.WorkOrderNumber,
|
||||
wo.BranchCode,
|
||||
wo.LotNumber,
|
||||
wo.ShortItemNumber
|
||||
FROM CLN_CTE cln INNER JOIN
|
||||
dbo.LotUsage AS lu ON(cln.LotNumber = lu.LotNumber AND cln.ShortItemNumber = lu.ShortItemNumber AND cln.BranchCode = lu.BranchCode) INNER JOIN
|
||||
dbo.WorkOrder AS wo ON(lu.WorkOrderNumber = wo.WorkOrderNumber)
|
||||
),
|
||||
CLN_FILTERED_WO AS(
|
||||
SELECT DISTINCT cln.WorkOrderNumber,
|
||||
cln.BranchCode,
|
||||
cln.LotNumber,
|
||||
cln.ShortItemNumber
|
||||
FROM CLN_WO cln
|
||||
)
|
||||
MERGE INTO #Temp_WO AS TARGET
|
||||
USING CLN_FILTERED_WO AS SOURCE
|
||||
ON (TARGET.WorkOrderNumber = SOURCE.WorkOrderNumber AND TARGET.BranchCode = SOURCE.BranchCode)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET TARGET.CARDEX = 1
|
||||
WHEN NOT MATCHED BY TARGET THEN
|
||||
INSERT (WorkOrderNumber, LotNumber, BranchCode, ShortItemNumber, CARDEX)
|
||||
VALUES (SOURCE.WorkOrderNumber, COALESCE(SOURCE.LotNumber, CAST(SOURCE.WorkOrderNumber AS VARCHAR(8))), SOURCE.BranchCode, SOURCE.ShortItemNumber, 1);
|
||||
<#@ include file="SplitWorkOrderQuery.txt" #>
|
||||
<# } #>
|
||||
<# if(Model.OperatorFilterEnabled) { #>
|
||||
|
||||
--Setup operator filter temp table
|
||||
IF OBJECT_ID('tempdb.dbo.#P_OperatorIDs', 'U') IS NOT NULL
|
||||
BEGIN
|
||||
DROP TABLE #P_OperatorIDs;
|
||||
END
|
||||
CREATE TABLE #P_OperatorIDs(
|
||||
AddressNumber BIGINT NOT NULL,
|
||||
UserID VARCHAR(10) NULL,
|
||||
PRIMARY KEY CLUSTERED(AddressNumber)
|
||||
);
|
||||
|
||||
WITH O_CTE AS(
|
||||
SELECT ju.AddressNumber,
|
||||
ju.UserID,
|
||||
ROW_NUMBER() OVER(PARTITION BY ju.AddressNumber ORDER BY ju.UserID DESC) RN
|
||||
FROM @p_OperatorFilter AS pof INNER JOIN
|
||||
dbo.JdeUser AS ju ON (LTRIM(RTRIM(pof.UserName)) = ju.UserID)
|
||||
)
|
||||
INSERT INTO #P_OperatorIDs(AddressNumber, UserID)
|
||||
SELECT o.AddressNumber,
|
||||
o.UserID
|
||||
FROM O_CTE o
|
||||
WHERE o.RN = 1;
|
||||
<# } #>
|
||||
<# if(Model.ItemOperationMisFilterEnabled) { #>
|
||||
|
||||
--Setup item/operation/mis filter temp table
|
||||
IF OBJECT_ID('tempdb.dbo.#P_PartOperations', 'U') IS NOT NULL
|
||||
BEGIN
|
||||
DROP TABLE #P_PartOperations;
|
||||
END
|
||||
CREATE TABLE #P_PartOperations(
|
||||
ItemNumber VARCHAR(32) NOT NULL,
|
||||
OperationNumber VARCHAR(32) NOT NULL,
|
||||
MisNumber VARCHAR(32) NOT NULL,
|
||||
MisRevision VARCHAR(32) NOT NULL,
|
||||
PRIMARY KEY CLUSTERED(ItemNumber,OperationNumber, MisNumber, MisRevision)
|
||||
);
|
||||
|
||||
INSERT INTO #P_PartOperations(ItemNumber, OperationNumber, MisNumber, MisRevision)
|
||||
SELECT DISTINCT LTRIM(RTRIM(piomf.ItemNumber)),
|
||||
LTRIM(RTRIM(piomf.OperationNumber)),
|
||||
LTRIM(RTRIM(piomf.MisNumber)),
|
||||
LTRIM(RTRIM(piomf.MisRevision))
|
||||
FROM @p_ItemOperationMisFilter AS piomf
|
||||
WHERE LTRIM(RTRIM(piomf.ItemNumber)) IS NOT NULL AND
|
||||
LTRIM(RTRIM(piomf.OperationNumber)) IS NOT NULL AND
|
||||
LTRIM(RTRIM(piomf.MisNumber)) IS NOT NULL AND
|
||||
LTRIM(RTRIM(piomf.MisRevision)) IS NOT NULL;
|
||||
<# } #>
|
||||
<# if(Model.ExtractMisData) { #>
|
||||
IF OBJECT_ID('tempdb.dbo.#TempMisData', 'U') IS NOT NULL
|
||||
BEGIN
|
||||
DROP TABLE #TempMisData;
|
||||
END
|
||||
CREATE TABLE #TempMisData (
|
||||
WorkOrderNumber BIGINT,
|
||||
ItemNumber VARCHAR(25),
|
||||
ItemDescription VARCHAR(30),
|
||||
BranchCode VARCHAR(12),
|
||||
WorkCenterCode VARCHAR(12),
|
||||
StepTimestamp DATETIME,
|
||||
SequenceNumber DECIMAL(7, 2),
|
||||
FunctionCode VARCHAR(15),
|
||||
FunctionOperationDescription VARCHAR(80),
|
||||
MatchedSequenceNumber DECIMAL(7, 2),
|
||||
RoutingMatch BIT,
|
||||
MasterMatch BIT,
|
||||
MisNumber VARCHAR(32),
|
||||
RevID VARCHAR(32),
|
||||
CharNumber VARCHAR(32),
|
||||
MisSequenceNumber VARCHAR(32),
|
||||
TestDescription VARCHAR(2000),
|
||||
SamplingType VARCHAR(32),
|
||||
SamplingValue VARCHAR(32),
|
||||
ToolsGauges VARCHAR(2000),
|
||||
WorkInstructions VARCHAR(2000),
|
||||
Status VARCHAR(32),
|
||||
ReleaseDate DATETIME
|
||||
);
|
||||
<# } #>
|
||||
<# if(Model.ShouldSearchSteps()) { #>
|
||||
|
||||
--Query data
|
||||
WITH LU_WO AS(
|
||||
SELECT DISTINCT
|
||||
step.WorkOrderNumber,
|
||||
step.LotNumber,
|
||||
step.BranchCode,
|
||||
step.ShortItemNumber
|
||||
FROM (
|
||||
SELECT DISTINCT
|
||||
wo.WorkOrderNumber,
|
||||
COALESCE(wo.LotNumber, CAST(wo.WorkOrderNumber AS VARCHAR(8))) AS LotNumber,
|
||||
wo.BranchCode,
|
||||
wo.ShortItemNumber
|
||||
FROM dbo.WorkOrder wo INNER JOIN <# if(Model.ItemNumberFilterEnabled) { #>
|
||||
#P_ItemNumbers p_in ON (wo.ItemNumber = p_in.ItemNumber) INNER JOIN<# } #>
|
||||
dbo.WorkOrderStep wos ON (wo.WorkOrderNumber = wos.WorkOrderNumber AND wo.BranchCode = wos.BranchCode) LEFT OUTER JOIN
|
||||
dbo.WorkOrderTime wot ON (wos.WorkOrderNumber = wot.WorkOrderNumber AND wos.StepNumber = wot.StepNumber AND wos.BranchCode = wot.BranchCode)<# if(Model.ProfitCenterFilterEnabled || Model.WorkCenterFilterEnabled) { #> INNER JOIN
|
||||
#P_WorkCenters p_wc ON (wos.WorkCenterCode = p_wc.Code)<# } if(Model.OperatorFilterEnabled) { #> INNER JOIN
|
||||
#P_OperatorIDs p_oi ON (wot.AddressNumber = p_oi.AddressNumber)<# } if(Model.ItemOperationMisFilterEnabled) { #> CROSS APPLY
|
||||
dbo.MatchMIS(wo.WorkOrderNumber, wo.ItemNumber, wo.BranchCode, wo.RoutingType, wo.IssueDate, wos.WorkCenterCode,
|
||||
wos.StepNumber, wos.EndDT, wos.FunctionCode, wos.FunctionOperationDescription) AS mm INNER JOIN
|
||||
#P_PartOperations p_po on (mm.ItemNumber = p_po.ItemNumber AND mm.MisSequenceNumber = p_po.OperationNumber AND mm.MisNumber = p_po.MisNumber AND mm.RevID = p_po.MisRevision) <# } #>
|
||||
<# this.Write("\r\n");
|
||||
if(Model.TimespanFilterEnabled) { #>
|
||||
WHERE (wos.EndDT <= @p_MaximumDT AND wos.EndDT >= @p_MinimumDT) OR
|
||||
(wot.GlDate <= @p_MaximumDT AND wot.GlDate >= @p_MinimumDT)
|
||||
<# } if(!Model.ItemOperationMisFilterEnabled) { #>
|
||||
UNION
|
||||
SELECT DISTINCT
|
||||
wo.WorkOrderNumber,
|
||||
COALESCE(wo.LotNumber, CAST(wo.WorkOrderNumber AS VARCHAR(8))) AS LotNumber,
|
||||
wo.BranchCode,
|
||||
wo.ShortItemNumber
|
||||
FROM dbo.WorkOrder wo INNER JOIN <# if(Model.ItemNumberFilterEnabled) { #>
|
||||
#P_ItemNumbers p_in ON (wo.ItemNumber = p_in.ItemNumber) INNER JOIN<# } #>
|
||||
dbo.WorkOrderTime wot ON (wo.WorkOrderNumber = wot.WorkOrderNumber AND wo.BranchCode = wot.BranchCode)<# if(Model.ProfitCenterFilterEnabled || Model.WorkCenterFilterEnabled) { #> INNER JOIN
|
||||
#P_WorkCenters p_wc ON (wot.WorkCenterCode = p_wc.Code)<# } if(Model.OperatorFilterEnabled) { #> INNER JOIN
|
||||
#P_OperatorIDs p_oi ON (wot.AddressNumber = p_oi.AddressNumber)<# } #>
|
||||
<# this.Write("\r\n");
|
||||
if(Model.MinimumDT.HasValue && Model.MaximumDT.HasValue) { #>
|
||||
WHERE (wot.GlDate <= @p_MaximumDT AND wot.GlDate >= @p_MinimumDT)
|
||||
<# } } #>
|
||||
) step
|
||||
)
|
||||
MERGE INTO #Temp_WO AS TARGET
|
||||
USING LU_WO AS SOURCE
|
||||
ON (TARGET.WorkOrderNumber = SOURCE.WorkOrderNumber AND TARGET.BranchCode = SOURCE.BranchCode)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET TARGET.Flagged = 1
|
||||
WHEN NOT MATCHED BY TARGET THEN
|
||||
INSERT(WorkOrderNumber, LotNumber, BranchCode, ShortItemNumber, Flagged)
|
||||
VALUES(SOURCE.WorkOrderNumber, SOURCE.LotNumber, SOURCE.BranchCode, SOURCE.ShortItemNumber, 1);
|
||||
<# } #>
|
||||
|
||||
--Add downlevel work orders that were issued material from flagged work orders
|
||||
DECLARE @c_MAX_RUNS INT = 20;
|
||||
DECLARE @v_NumWO INT = -1;
|
||||
DECLARE @v_NewNumWO INT;
|
||||
DECLARE @v_NumRuns INT = 0;
|
||||
|
||||
WHILE(1 = 1) BEGIN
|
||||
SET @v_NumWO = @v_NewNumWO;
|
||||
|
||||
<# this.PushIndent(" "); #>
|
||||
<#@ include file="ComponentUsageQuery.txt" #>
|
||||
|
||||
<#@ include file="SplitWorkOrderQuery.txt" #>
|
||||
<# this.PopIndent(); #>
|
||||
|
||||
SELECT @v_NewNumWO = COUNT(*) FROM #Temp_WO;
|
||||
SET @v_NumRuns = @v_NumRuns + 1;
|
||||
|
||||
IF(@v_NumWO = @v_NewNumWO OR @v_NumRuns = @c_MAX_RUNS) BEGIN
|
||||
BREAK;
|
||||
END
|
||||
END;
|
||||
|
||||
--Lookup flagged work order details
|
||||
WITH LAST_WOS AS
|
||||
(
|
||||
SELECT wos.WorkOrderNumber,
|
||||
wos.BranchCode,
|
||||
wos.StepNumber,
|
||||
wos.StepDescription,
|
||||
wos.FunctionOperationDescription,
|
||||
wos.LastUpdateDT,
|
||||
ROW_NUMBER() OVER (PARTITION BY wos.WorkOrderNumber, wos.BranchCode ORDER BY wos.EndDT DESC, wos.StepNumber DESC) AS RN
|
||||
FROM dbo.WorkOrderStep AS wos LEFT OUTER JOIN
|
||||
#Temp_WO t_wo ON (wos.WorkOrderNumber = t_wo.WorkOrderNumber AND wos.BranchCode = t_wo.BranchCode)
|
||||
)
|
||||
SELECT wo.WorkOrderNumber,
|
||||
wo.BranchCode AS WorkOrderBranchCode,
|
||||
wo.LotNumber,
|
||||
wo.ItemNumber,
|
||||
i.PlanningFamily,
|
||||
i.StockingType,
|
||||
t_wo.ManuallySpecified,
|
||||
t_wo.SplitOrder,
|
||||
t_wo.CARDEX,
|
||||
t_wo.PartsList,
|
||||
t_wo.Flagged,
|
||||
wo.OrderQuantity,
|
||||
wo.HeldQuantity,
|
||||
COALESCE(wots.TotalScrappedQuantity, 0) AS ScrappedQuantity,
|
||||
wo.ShippedQuantity,
|
||||
LTRIM(RTRIM(lwos.BranchCode)) AS StepBranchCode,
|
||||
lwos.StepNumber,
|
||||
lwos.StepDescription,
|
||||
lwos.FunctionOperationDescription,
|
||||
lwos.LastUpdateDT AS StepUpdateDT,
|
||||
wo.StatusCode,
|
||||
sc.Description AS StatusDescription,
|
||||
wo.StatusCodeUpdateDT AS StatusUpdateDT
|
||||
FROM dbo.WorkOrder AS wo INNER JOIN
|
||||
#Temp_WO AS t_wo ON (t_wo.WorkOrderNumber = wo.WorkOrderNumber AND t_wo.BranchCode = wo.BranchCode) LEFT OUTER JOIN
|
||||
dbo.Item i on (wo.ShortItemNumber = i.ShortItemNumber) INNER JOIN
|
||||
dbo.StatusCode AS sc ON (sc.Code = wo.StatusCode) LEFT OUTER JOIN
|
||||
LAST_WOS lwos ON (wo.WorkOrderNumber = lwos.WorkOrderNumber AND wo.BranchCode = lwos.BranchCode) LEFT OUTER JOIN
|
||||
dbo.WorkOrderTotalScrap wots ON (wo.WorkOrderNumber = wots.WorkOrderNumber AND wo.BranchCode = wots.BranchCode)
|
||||
WHERE lwos.RN IS NULL OR
|
||||
lwos.RN = 1;
|
||||
<# if(Model.ExtractMisData) { #>
|
||||
WITH MIS_CTE AS(
|
||||
SELECT DISTINCT wo.WorkOrderNumber,
|
||||
wo.ItemNumber,
|
||||
wo.BranchCode,
|
||||
wo.RoutingType,
|
||||
wo.IssueDate,
|
||||
wos.WorkCenterCode,
|
||||
wos.StepNumber,
|
||||
wos.EndDT,
|
||||
wos.FunctionCode,
|
||||
wos.FunctionOperationDescription
|
||||
FROM dbo.WorkOrderStep AS wos INNER JOIN
|
||||
dbo.WorkOrder AS wo ON (wos.WorkOrderNumber = wo.WorkOrderNumber AND LTRIM(wos.BranchCode) = wo.BranchCode) LEFT OUTER JOIN
|
||||
dbo.WorkOrderTime AS wot ON (wos.WorkOrderNumber = wot.WorkOrderNumber AND LTRIM(wos.BranchCode) = wot.BranchCode AND wos.StepNumber = wot.StepNumber) <# if(Model.ItemNumberFilterEnabled) { #> INNER JOIN
|
||||
#P_ItemNumbers p_in ON (wo.ItemNumber = p_in.ItemNumber)<# } if(Model.ProfitCenterFilterEnabled || Model.WorkCenterFilterEnabled) { #> INNER JOIN
|
||||
#P_WorkCenters p_wc ON (wos.WorkCenterCode = p_wc.Code)<# } if(Model.OperatorFilterEnabled) { #> INNER JOIN
|
||||
#P_OperatorIDs p_oi ON (wot.AddressNumber = p_oi.AddressNumber)<# } #>
|
||||
<# if(Model.MinimumDT.HasValue && Model.MaximumDT.HasValue) { #>
|
||||
WHERE (((wos.EndDT <= @p_MaximumDT) AND (wos.EndDT >= @p_MinimumDT)) OR
|
||||
((wot.GlDate <= @p_MaximumDT) AND (wot.GlDate >= @p_MinimumDT)))
|
||||
<# } else if(Model.MinimumDT.HasValue && !Model.MaximumDT.HasValue) { #>
|
||||
WHERE (wos.EndDT >= @p_MinimumDT OR wot.GlDate >= @p_MinimumDT)
|
||||
<# } else if(!Model.MinimumDT.HasValue && Model.MaximumDT.HasValue) { #>
|
||||
WHERE (wos.EndDT <= @p_MaximumDT OR wot.GlDate <= @p_MaximumDT))
|
||||
<# } #>
|
||||
)
|
||||
INSERT INTO #TempMISData
|
||||
(
|
||||
WorkOrderNumber,
|
||||
ItemNumber,
|
||||
ItemDescription,
|
||||
BranchCode,
|
||||
WorkCenterCode,
|
||||
StepTimestamp,
|
||||
SequenceNumber,
|
||||
FunctionCode,
|
||||
FunctionOperationDescription,
|
||||
MatchedSequenceNumber,
|
||||
RoutingMatch,
|
||||
MasterMatch,
|
||||
MisNumber,
|
||||
RevID,
|
||||
CharNumber,
|
||||
MisSequenceNumber,
|
||||
TestDescription,
|
||||
SamplingType,
|
||||
SamplingValue,
|
||||
ToolsGauges,
|
||||
WorkInstructions,
|
||||
Status,
|
||||
ReleaseDate
|
||||
)
|
||||
SELECT mm.WorkOrderNumber,
|
||||
mm.ItemNumber,
|
||||
mm.ItemDescription,
|
||||
mm.BranchCode,
|
||||
mm.WorkCenterCode,
|
||||
mm.StepTimestamp,
|
||||
mm.SequenceNumber,
|
||||
mm.FunctionCode,
|
||||
mm.FunctionOperationDescription,
|
||||
mm.MatchedSequenceNumber,
|
||||
mm.RoutingMatch,
|
||||
mm.MasterMatch,
|
||||
mm.MisNumber,
|
||||
mm.RevID,
|
||||
mm.CharNumber,
|
||||
mm.MisSequenceNumber,
|
||||
mm.TestDescription,
|
||||
mm.SamplingType,
|
||||
mm.SamplingValue,
|
||||
mm.ToolsGauges,
|
||||
mm.WorkInstructions,
|
||||
mm.Status,
|
||||
mm.ReleaseDate
|
||||
FROM MIS_CTE c CROSS APPLY
|
||||
dbo.MatchMIS(c.WorkOrderNumber, c.ItemNumber, c.BranchCode, c.RoutingType,
|
||||
c.IssueDate, c.WorkCenterCode, c.StepNumber, c.EndDT,
|
||||
c.FunctionCode, c.FunctionOperationDescription) AS mm;
|
||||
|
||||
--Get MIS search results
|
||||
SELECT DISTINCT tmd.ItemNumber,
|
||||
tmd.ItemDescription,
|
||||
tmd.BranchCode,
|
||||
tmd.MisSequenceNumber AS SequenceNumber,
|
||||
tmd.FunctionCode,
|
||||
tmd.FunctionOperationDescription,
|
||||
tmd.SequenceNumber AS JobStepSequenceNumber,
|
||||
tmd.MatchedSequenceNumber,
|
||||
tmd.RoutingMatch,
|
||||
tmd.MasterMatch,
|
||||
tmd.MisNumber,
|
||||
tmd.RevID,
|
||||
tmd.CharNumber,
|
||||
tmd.TestDescription,
|
||||
tmd.SamplingType,
|
||||
tmd.SamplingValue,
|
||||
tmd.ToolsGauges,
|
||||
tmd.WorkInstructions,
|
||||
tmd.Status,
|
||||
tmd.ReleaseDate
|
||||
FROM #TempMisData AS tmd
|
||||
ORDER BY tmd.ItemNumber,
|
||||
tmd.BranchCode,
|
||||
tmd.SequenceNumber,
|
||||
tmd.MatchedSequenceNumber;
|
||||
|
||||
--Get no-match MIS search results
|
||||
SELECT DISTINCT tmd.WorkCenterCode,
|
||||
tmd.WorkOrderNumber,
|
||||
wo.IssueDate AS WorkOrderStartDate,
|
||||
tmd.SequenceNumber AS JobStepNumber,
|
||||
tmd.FunctionOperationDescription AS JobStepDescription,
|
||||
tmd.StepTimestamp AS JobStepEndDate,
|
||||
CASE WHEN wo.RoutingType='NMR' OR NOT EXISTS(SELECT * FROM dbo.WorkOrderRouting wor WHERE tmd.WorkOrderNumber = wor.WorkOrderNumber AND wor.StepNumber = tmd.SequenceNumber) THEN 1 ELSE 0 END AS WasJobStepAdded,
|
||||
CASE WHEN wo.RoutingType='NMR' THEN NULL ELSE (SELECT TOP 1 wor.StepNumber FROM dbo.WorkOrderRouting wor WHERE (tmd.WorkOrderNumber = wor.WorkOrderNumber AND tmd.WorkCenterCode = wor.WorkCenterCode AND tmd.FunctionCode = wor.FunctionCode) AND wor.StepNumber <> tmd.SequenceNumber) END AS MatchedJobStepNumber,
|
||||
tmd.FunctionCode,
|
||||
tmd.ItemNumber,
|
||||
i.Description AS ItemDescription,
|
||||
wo.RoutingType
|
||||
FROM #TempMisData AS tmd INNER JOIN
|
||||
dbo.WorkOrder AS wo ON (tmd.WorkOrderNumber = wo.WorkOrderNumber AND tmd.BranchCode = wo.BranchCode) LEFT OUTER JOIN
|
||||
dbo.Item AS i ON (wo.ShortItemNumber = i.ShortItemNumber)
|
||||
WHERE (tmd.RoutingMatch = 0 AND
|
||||
tmd.MasterMatch = 0) OR
|
||||
tmd.MisNumber IS NULL
|
||||
ORDER BY tmd.WorkOrderNumber,
|
||||
tmd.SequenceNumber;
|
||||
<# } #>
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
using WorkerService.Models.Reporting;
|
||||
|
||||
namespace WorkerService.Templates
|
||||
{
|
||||
/// <summary>
|
||||
/// Query template extensions
|
||||
/// </summary>
|
||||
public partial class QueryTemplate
|
||||
{
|
||||
/// <summary>
|
||||
/// Template data model
|
||||
/// </summary>
|
||||
public SearchModel Model { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="model">Template data model</param>
|
||||
public QueryTemplate(SearchModel? model = null)
|
||||
{
|
||||
Model = model ?? new SearchModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
--Add any work orders split from flagged work orders
|
||||
WITH SP_WO AS
|
||||
(
|
||||
SELECT DISTINCT wo.WorkOrderNumber,
|
||||
wo.LotNumber,
|
||||
wo.BranchCode,
|
||||
wo.ShortItemNumber
|
||||
FROM dbo.WorkOrder AS wo INNER JOIN
|
||||
#Temp_WO AS tw_o ON (wo.ParentWorkOrderNumber = CAST(tw_o.WorkOrderNumber AS VARCHAR(8)) AND wo.BranchCode = tw_o.BranchCode)
|
||||
)
|
||||
MERGE #Temp_WO AS TARGET
|
||||
USING SP_WO AS SOURCE
|
||||
ON (TARGET.WorkOrderNumber = SOURCE.WorkOrderNumber AND TARGET.BranchCode = SOURCE.BranchCode)
|
||||
WHEN MATCHED THEN
|
||||
UPDATE SET TARGET.SplitOrder = 1
|
||||
WHEN NOT MATCHED BY TARGET THEN
|
||||
INSERT (WorkOrderNumber, LotNumber, BranchCode, ShortItemNumber, SplitOrder)
|
||||
VALUES (SOURCE.WorkOrderNumber, SOURCE.LotNumber, SOURCE.BranchCode, SOURCE.ShortItemNumber, 1);
|
||||
Executable
+346
@@ -0,0 +1,346 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{CD1DCD5B-5D89-445D-A8ED-9D8BADA0E36D}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>WorkerService</RootNamespace>
|
||||
<AssemblyName>WorkerService</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<TargetFrameworkProfile />
|
||||
<SccProjectName>
|
||||
</SccProjectName>
|
||||
<SccLocalPath>
|
||||
</SccLocalPath>
|
||||
<SccAuxPath>
|
||||
</SccAuxPath>
|
||||
<SccProvider>
|
||||
</SccProvider>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<PublishUrl>D:\ValidationWork\Deployment\SERVICE\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>true</MapFileExtensions>
|
||||
<ApplicationRevision>1</ApplicationRevision>
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<PublishWizardCompleted>true</PublishWizardCompleted>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ManifestCertificateThumbprint>3FECA0F7B932F0FA819C95ABF83644F144C950C4</ManifestCertificateThumbprint>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ManifestKeyFile>WorkerService_TemporaryKey.pfx</ManifestKeyFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<GenerateManifests>false</GenerateManifests>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SignManifests>false</SignManifests>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetZone>LocalIntranet</TargetZone>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationManifest>Properties\app.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Dapper, Version=1.50.2.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Dapper.1.50.2\lib\net451\Dapper.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="DDTek.Oracle, Version=4.2.0.0, Culture=neutral, PublicKeyToken=c84cd5c63851e072, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\..\..\..\..\DDTek.Oracle.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="EPPlus, Version=4.1.0.0, Culture=neutral, PublicKeyToken=ea159fdaa78159a1, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\EPPlus.4.1.0\lib\net40\EPPlus.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Fasterflect, Version=2.1.3.0, Culture=neutral, PublicKeyToken=38d18473284c1ca7, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\fasterflect.2.1.3\lib\net40\Fasterflect.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AspNet.SignalR.Client, Version=2.2.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNet.SignalR.Client.2.2.1\lib\net45\Microsoft.AspNet.SignalR.Client.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.AspNetCore.Html.Abstractions, Version=1.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNetCore.Html.Abstractions.1.1.1\lib\netstandard1.0\Microsoft.AspNetCore.Html.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="Microsoft.Owin, Version=2.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Owin.2.0.2\lib\net45\Microsoft.Owin.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.3.5\lib\net45\NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Buffers, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Buffers.4.3.0\lib\netstandard1.1\System.Buffers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Collections.Immutable, Version=1.1.37.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Collections.Immutable.1.1.37\lib\dotnet\System.Collections.Immutable.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.Net.Http.Formatting, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Reflection.Metadata, Version=1.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Reflection.Metadata.1.2.0\lib\portable-net45+win8\System.Reflection.Metadata.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.3.0\lib\netstandard1.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Text.Encodings.Web, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Text.Encodings.Web.4.3.0\lib\netstandard1.0\System.Text.Encodings.Web.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.Http, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.Http.Owin, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNet.WebApi.Owin.5.2.3\lib\net45\System.Web.Http.Owin.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Topshelf, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b800c4cfcdeea87b, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Topshelf.4.0.1\lib\net452\Topshelf.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Topshelf.NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b800c4cfcdeea87b, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Topshelf.NLog.4.0.1\lib\net452\Topshelf.NLog.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Helpers\ExcelHelpers.cs" />
|
||||
<Compile Include="Helpers\SearchModelHelpers.cs" />
|
||||
<Compile Include="Models\ColumnSpec.cs" />
|
||||
<Compile Include="Models\DataLoad.cs" />
|
||||
<Compile Include="Models\DataSourceConfig.cs" />
|
||||
<Compile Include="Models\DataUpdateConfig.cs" />
|
||||
<Compile Include="Models\DataUpdateTask.cs" />
|
||||
<Compile Include="Models\Index.cs" />
|
||||
<Compile Include="Models\LastDataUpdate.cs" />
|
||||
<Compile Include="Models\Reporting\ComponentLotFilterEntry.cs" />
|
||||
<Compile Include="Models\Reporting\ItemNumberFilterEntry.cs" />
|
||||
<Compile Include="Models\Reporting\ItemOperationMisFilterEntry.cs" />
|
||||
<Compile Include="Models\Reporting\MisNonMatchSearchResult.cs" />
|
||||
<Compile Include="Models\Reporting\MisSearchResult.cs" />
|
||||
<Compile Include="Models\Reporting\OperatorFilterEntry.cs" />
|
||||
<Compile Include="Models\Reporting\OutputColumn.cs" />
|
||||
<Compile Include="Models\Reporting\OutputColumnAttribute.cs" />
|
||||
<Compile Include="Models\Reporting\OutputTableAttribute.cs" />
|
||||
<Compile Include="Models\Reporting\ProfitCenterFilterEntry.cs" />
|
||||
<Compile Include="Models\Reporting\SearchModel.cs" />
|
||||
<Compile Include="Models\Reporting\SearchResult.cs" />
|
||||
<Compile Include="Models\Reporting\TimespanFilter.cs" />
|
||||
<Compile Include="Models\Reporting\WorkCenterFilterEntry.cs" />
|
||||
<Compile Include="Models\Reporting\WorkOrderFilterEntry.cs" />
|
||||
<Compile Include="Models\TableSpec.cs" />
|
||||
<Compile Include="Process\ActionConverter.cs" />
|
||||
<Compile Include="Process\ExcelWriter.cs" />
|
||||
<Compile Include="Process\FunctionConverter.cs" />
|
||||
<Compile Include="Process\LotFinderDBExt.cs" />
|
||||
<Compile Include="Process\UpdateProcessor.cs" />
|
||||
<Compile Include="Process\UpdateProcessor.DataUpdateEntry.cs">
|
||||
<DependentUpon>UpdateProcessor.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Process\UpdateProcessor.TableManagement.cs">
|
||||
<DependentUpon>UpdateProcessor.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Process\WorkProcessor.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Templates\QueryTemplate.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>QueryTemplate.tt</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Templates\QueryTemplateExtension.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Templates\ComponentUsageQuery.txt" />
|
||||
<Content Include="Templates\QueryTemplate.tt">
|
||||
<Generator>TextTemplatingFilePreprocessor</Generator>
|
||||
<LastGenOutput>QueryTemplate.cs</LastGenOutput>
|
||||
</Content>
|
||||
<Content Include="Templates\SplitWorkOrderQuery.txt" />
|
||||
<None Include="App.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<Content Include="NLog.config">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Include="dsconfig\FunctionCode.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="dsconfig\Branch.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="dsconfig\Item.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="dsconfig\JdeUser.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="dsconfig\Lot.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="dsconfig\LotUsage_Archive.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="dsconfig\LotUsage.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="dsconfig\MisData.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="dsconfig\OrgHierarchy.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="dsconfig\ProfitCenter.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="dsconfig\RouteMaster.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="dsconfig\StatusCode.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="dsconfig\WorkCenter.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="dsconfig\WorkOrderComponent_Archive.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="dsconfig\WorkOrderStep_Archive.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="dsconfig\WorkOrderTime_Archive.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="dsconfig\WorkOrder_Archive.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="dsconfig\WorkOrder.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="dsconfig\WorkOrderComponent.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="dsconfig\WorkOrderRouting.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="dsconfig\WorkOrderStep.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="dsconfig\WorkOrderTime.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="NLog.xsd">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<None Include="packages.config" />
|
||||
<None Include="Properties\app.manifest" />
|
||||
<None Include="scripts\script_install.bat">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="scripts\script_start.bat">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="scripts\script_stop.bat">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="scripts\script_uninstall.bat">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DataModel\Commons.csproj">
|
||||
<Project>{aec46648-af1b-4eed-8580-1cf7afe3adcd}</Project>
|
||||
<Name>Commons</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include=".NETFramework,Version=v4.5.2">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>Microsoft .NET Framework 4.5.2 %28x86 and x64%29</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Analyzer Include="..\packages\Microsoft.CodeAnalysis.Analyzers.1.1.0\analyzers\dotnet\cs\Microsoft.CodeAnalysis.Analyzers.dll" />
|
||||
<Analyzer Include="..\packages\Microsoft.CodeAnalysis.Analyzers.1.1.0\analyzers\dotnet\cs\Microsoft.CodeAnalysis.CSharp.Analyzers.dll" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"SourceSystem": "JDE",
|
||||
"SourceData": "BRANCH",
|
||||
"TableName": "Branch",
|
||||
"IsEnabled": true,
|
||||
"DataFetchFunction": "Commons.Process.JDE.GetBranches",
|
||||
"PostProcessingAction": null,
|
||||
"MassUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 10080,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"DailyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 1440,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
},
|
||||
"HourlyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 60,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
}
|
||||
}
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"SourceSystem": "JDE",
|
||||
"SourceData": "FUNCTIONCODE",
|
||||
"TableName": "FunctionCode",
|
||||
"IsEnabled": true,
|
||||
"DataFetchFunction": "Commons.Process.JDE.GetFunctionCodes",
|
||||
"PostProcessingAction": null,
|
||||
"MassUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 10080,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"DailyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 1440,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"HourlyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 60,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
}
|
||||
}
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"SourceSystem": "JDE",
|
||||
"SourceData": "ITEM",
|
||||
"TableName": "Item",
|
||||
"IsEnabled": true,
|
||||
"DataFetchFunction": "Commons.Process.JDE.GetItems",
|
||||
"PostProcessingAction": null,
|
||||
"MassUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 10080,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"DailyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 1440,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
},
|
||||
"HourlyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 60,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
}
|
||||
}
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"SourceSystem": "JDE",
|
||||
"SourceData": "USER",
|
||||
"TableName": "JdeUser",
|
||||
"IsEnabled": true,
|
||||
"DataFetchFunction": "Commons.Process.JDE.GetUsers",
|
||||
"PostProcessingAction": null,
|
||||
"MassUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 10080,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"DailyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 1440,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
},
|
||||
"HourlyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 60,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
}
|
||||
}
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"SourceSystem": "JDE",
|
||||
"SourceData": "LOT",
|
||||
"TableName": "Lot",
|
||||
"IsEnabled": true,
|
||||
"DataFetchFunction": "Commons.Process.JDE.GetLots",
|
||||
"PostProcessingAction": null,
|
||||
"MassUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 10080,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"DailyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 1440,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
},
|
||||
"HourlyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 60,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
}
|
||||
}
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"SourceSystem": "JDE",
|
||||
"SourceData": "LOTUSAGE",
|
||||
"TableName": "LotUsage_Curr",
|
||||
"IsEnabled": true,
|
||||
"DataFetchFunction": "Commons.Process.JDE.GetLotUsages",
|
||||
"PostProcessingAction": null,
|
||||
"MassUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 10080,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"DailyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 1440,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
},
|
||||
"HourlyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 60,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"SourceSystem": "JDE",
|
||||
"SourceData": "LOTUSAGEARCHIVE",
|
||||
"TableName": "LotUsage_Hist",
|
||||
"IsEnabled": true,
|
||||
"DataFetchFunction": "Commons.Process.JDE.GetLotUsagesArchive",
|
||||
"PostProcessingAction": null,
|
||||
"MassUpdateConfig": {
|
||||
"Enabled": false,
|
||||
"Interval": 10080,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"DailyUpdateConfig": {
|
||||
"Enabled": false,
|
||||
"Interval": 1440,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
},
|
||||
"HourlyUpdateConfig": {
|
||||
"Enabled": false,
|
||||
"Interval": 60,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
}
|
||||
}
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"SourceSystem": "CMS",
|
||||
"SourceData": "MISDATA",
|
||||
"TableName": "MisData",
|
||||
"IsEnabled": true,
|
||||
"DataFetchFunction": "Commons.Process.CMS.GetMisData",
|
||||
"PostProcessingAction": "Commons.Process.LotFinderDB.PostProcessMisData",
|
||||
"MassUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 100800,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"DailyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 1440,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
},
|
||||
"HourlyUpdateConfig": {
|
||||
"Enabled": false,
|
||||
"Interval": 60,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
}
|
||||
}
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"SourceSystem": "JDE",
|
||||
"SourceData": "ORGHIERARCHY",
|
||||
"TableName": "OrgHierarchy",
|
||||
"IsEnabled": true,
|
||||
"DataFetchFunction": "Commons.Process.JDE.GetOrgHierarchy",
|
||||
"PostProcessingAction": null,
|
||||
"MassUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 10080,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"DailyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 1440,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
},
|
||||
"HourlyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 60,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
}
|
||||
}
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"SourceSystem": "JDE",
|
||||
"SourceData": "PROFITCENTER",
|
||||
"TableName": "ProfitCenter",
|
||||
"IsEnabled": true,
|
||||
"DataFetchFunction": "Commons.Process.JDE.GetProfitCenters",
|
||||
"PostProcessingAction": null,
|
||||
"MassUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 10080,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"DailyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 1440,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
},
|
||||
"HourlyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 60,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
}
|
||||
}
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"SourceSystem": "JDE",
|
||||
"SourceData": "ROUTEMASTER",
|
||||
"TableName": "RouteMaster",
|
||||
"IsEnabled": true,
|
||||
"DataFetchFunction": "Commons.Process.JDE.GetRouteMasters",
|
||||
"PostProcessingAction": null,
|
||||
"MassUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 10080,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"DailyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 1440,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
},
|
||||
"HourlyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 60,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
}
|
||||
}
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"SourceSystem": "JDE",
|
||||
"SourceData": "STATUSCODE",
|
||||
"TableName": "StatusCode",
|
||||
"IsEnabled": true,
|
||||
"DataFetchFunction": "Commons.Process.JDE.GetStatusCodes",
|
||||
"PostProcessingAction": null,
|
||||
"MassUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 10080,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"DailyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 1440,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
},
|
||||
"HourlyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 60,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
}
|
||||
}
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"SourceSystem": "JDE",
|
||||
"SourceData": "WORKCENTER",
|
||||
"TableName": "WorkCenter",
|
||||
"IsEnabled": true,
|
||||
"DataFetchFunction": "Commons.Process.JDE.GetWorkCenters",
|
||||
"PostProcessingAction": null,
|
||||
"MassUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 10080,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"DailyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 1440,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
},
|
||||
"HourlyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 60,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
}
|
||||
}
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"SourceSystem": "JDE",
|
||||
"SourceData": "WORKORDER",
|
||||
"TableName": "WorkOrder_Curr",
|
||||
"IsEnabled": true,
|
||||
"DataFetchFunction": "Commons.Process.JDE.GetWorkOrders",
|
||||
"PostProcessingAction": null,
|
||||
"MassUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 10080,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"DailyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 1440,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
},
|
||||
"HourlyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 60,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"SourceSystem": "JDE",
|
||||
"SourceData": "WORKORDERCOMPONENT",
|
||||
"TableName": "WorkOrderComponent_Curr",
|
||||
"IsEnabled": true,
|
||||
"DataFetchFunction": "Commons.Process.JDE.GetWorkOrderComponents",
|
||||
"PostProcessingAction": null,
|
||||
"MassUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 10080,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"DailyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 1440,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
},
|
||||
"HourlyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 60,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"SourceSystem": "JDE",
|
||||
"SourceData": "WORKORDERCOMPONENTARCHIVE",
|
||||
"TableName": "WorkOrderComponent_Hist",
|
||||
"IsEnabled": true,
|
||||
"DataFetchFunction": "Commons.Process.JDE.GetWorkOrderComponentsArchive",
|
||||
"PostProcessingAction": null,
|
||||
"MassUpdateConfig": {
|
||||
"Enabled": false,
|
||||
"Interval": 10080,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"DailyUpdateConfig": {
|
||||
"Enabled": false,
|
||||
"Interval": 1440,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
},
|
||||
"HourlyUpdateConfig": {
|
||||
"Enabled": false,
|
||||
"Interval": 60,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"SourceSystem": "JDE",
|
||||
"SourceData": "WORKORDERROUTING",
|
||||
"TableName": "WorkOrderRouting",
|
||||
"IsEnabled": true,
|
||||
"DataFetchFunction": "Commons.Process.JDE.GetWorkOrderRoutings",
|
||||
"PostProcessingAction": null,
|
||||
"MassUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 10080,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"DailyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 1440,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
},
|
||||
"HourlyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 60,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"SourceSystem": "JDE",
|
||||
"SourceData": "WORKORDERSTEP",
|
||||
"TableName": "WorkOrderStep_Curr",
|
||||
"IsEnabled": true,
|
||||
"DataFetchFunction": "Commons.Process.JDE.GetWorkOrderSteps",
|
||||
"PostProcessingAction": null,
|
||||
"MassUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 10080,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"DailyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 1440,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
},
|
||||
"HourlyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 60,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"SourceSystem": "JDE",
|
||||
"SourceData": "WORKORDERSTEPARCHIVE",
|
||||
"TableName": "WorkOrderStep_Hist",
|
||||
"IsEnabled": true,
|
||||
"DataFetchFunction": "Commons.Process.JDE.GetWorkOrderStepsArchive",
|
||||
"PostProcessingAction": null,
|
||||
"MassUpdateConfig": {
|
||||
"Enabled": false,
|
||||
"Interval": 10080,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"DailyUpdateConfig": {
|
||||
"Enabled": false,
|
||||
"Interval": 1440,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
},
|
||||
"HourlyUpdateConfig": {
|
||||
"Enabled": false,
|
||||
"Interval": 60,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"SourceSystem": "JDE",
|
||||
"SourceData": "WORKORDERTIME",
|
||||
"TableName": "WorkOrderTime_Curr",
|
||||
"IsEnabled": true,
|
||||
"DataFetchFunction": "Commons.Process.JDE.GetWorkOrderTimes",
|
||||
"PostProcessingAction": null,
|
||||
"MassUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 10080,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"DailyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 1440,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
},
|
||||
"HourlyUpdateConfig": {
|
||||
"Enabled": true,
|
||||
"Interval": 60,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"SourceSystem": "JDE",
|
||||
"SourceData": "WORKORDERTIMEARCHIVE",
|
||||
"TableName": "WorkOrderTime_Hist",
|
||||
"IsEnabled": true,
|
||||
"DataFetchFunction": "Commons.Process.JDE.GetWorkOrderTimesArchive",
|
||||
"PostProcessingAction": null,
|
||||
"MassUpdateConfig": {
|
||||
"Enabled": false,
|
||||
"Interval": 10080,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"DailyUpdateConfig": {
|
||||
"Enabled": false,
|
||||
"Interval": 1440,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
},
|
||||
"HourlyUpdateConfig": {
|
||||
"Enabled": false,
|
||||
"Interval": 60,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"SourceSystem": "JDE",
|
||||
"SourceData": "WORKORDERARCHIVE",
|
||||
"TableName": "WorkOrder_Hist",
|
||||
"IsEnabled": true,
|
||||
"DataFetchFunction": "Commons.Process.JDE.GetWorkOrdersArchive",
|
||||
"PostProcessingAction": null,
|
||||
"MassUpdateConfig": {
|
||||
"Enabled": false,
|
||||
"Interval": 10080,
|
||||
"PrepurgeData": true,
|
||||
"ReIndexData": true
|
||||
},
|
||||
"DailyUpdateConfig": {
|
||||
"Enabled": false,
|
||||
"Interval": 1440,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
},
|
||||
"HourlyUpdateConfig": {
|
||||
"Enabled": false,
|
||||
"Interval": 60,
|
||||
"PrepurgeData": false,
|
||||
"ReIndexData": false
|
||||
}
|
||||
}
|
||||
Executable
+70
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Dapper" version="1.50.2" targetFramework="net452" />
|
||||
<package id="EPPlus" version="4.1.0" targetFramework="net45" />
|
||||
<package id="fasterflect" version="2.1.3" targetFramework="net452" />
|
||||
<package id="Microsoft.AspNet.SignalR.Client" version="2.2.1" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net452" />
|
||||
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net452" />
|
||||
<package id="Microsoft.AspNet.WebApi.Owin" version="5.2.3" targetFramework="net452" />
|
||||
<package id="Microsoft.AspNetCore.Html.Abstractions" version="1.1.1" targetFramework="net452" />
|
||||
<package id="Microsoft.AspNetCore.Razor" version="1.1.1" targetFramework="net452" />
|
||||
<package id="Microsoft.AspNetCore.Razor.Runtime" version="1.1.1" targetFramework="net452" />
|
||||
<package id="Microsoft.CodeAnalysis.Analyzers" version="1.1.0" targetFramework="net452" />
|
||||
<package id="Microsoft.CodeAnalysis.Common" version="1.3.0" targetFramework="net452" />
|
||||
<package id="Microsoft.CodeAnalysis.CSharp" version="1.3.0" targetFramework="net452" />
|
||||
<package id="Microsoft.Extensions.Caching.Abstractions" version="1.1.1" targetFramework="net452" />
|
||||
<package id="Microsoft.Extensions.Caching.Memory" version="1.1.1" targetFramework="net452" />
|
||||
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="1.1.0" targetFramework="net452" />
|
||||
<package id="Microsoft.Extensions.FileProviders.Abstractions" version="1.1.0" targetFramework="net452" />
|
||||
<package id="Microsoft.Extensions.FileProviders.Physical" version="1.1.0" targetFramework="net452" />
|
||||
<package id="Microsoft.Extensions.FileSystemGlobbing" version="1.1.0" targetFramework="net452" />
|
||||
<package id="Microsoft.Extensions.Options" version="1.1.1" targetFramework="net452" />
|
||||
<package id="Microsoft.Extensions.Primitives" version="1.1.0" targetFramework="net452" />
|
||||
<package id="Microsoft.NETCore.Platforms" version="1.1.0" targetFramework="net452" />
|
||||
<package id="Microsoft.Owin" version="2.0.2" targetFramework="net452" />
|
||||
<package id="NETStandard.Library" version="1.6.1" targetFramework="net452" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
|
||||
<package id="NLog" version="4.3.5" targetFramework="net45" />
|
||||
<package id="NLog.Config" version="4.3.5" targetFramework="net45" />
|
||||
<package id="NLog.Schema" version="4.3.4" targetFramework="net45" />
|
||||
<package id="Owin" version="1.0" targetFramework="net452" />
|
||||
<package id="System.Buffers" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Collections" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Collections.Concurrent" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Collections.Immutable" version="1.1.37" targetFramework="net452" />
|
||||
<package id="System.ComponentModel" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Diagnostics.Tools" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Diagnostics.Tracing" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Globalization" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.IO" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.IO.Compression" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Linq" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Linq.Expressions" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Net.Http" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Net.Primitives" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.ObjectModel" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Reflection" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Reflection.Extensions" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Reflection.Metadata" version="1.2.0" targetFramework="net452" />
|
||||
<package id="System.Reflection.Primitives" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Resources.ResourceManager" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Runtime" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Runtime.Extensions" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Runtime.InteropServices" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Runtime.Numerics" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Text.Encoding" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Text.Encoding.Extensions" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Text.Encodings.Web" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Text.RegularExpressions" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Threading" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Threading.Tasks" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Threading.Timer" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="net452" />
|
||||
<package id="System.Xml.XDocument" version="4.3.0" targetFramework="net452" />
|
||||
<package id="Topshelf" version="4.0.1" targetFramework="net452" />
|
||||
<package id="Topshelf.NLog" version="4.0.1" targetFramework="net452" />
|
||||
</packages>
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
ECHO OFF
|
||||
CLS
|
||||
ECHO Installing JDE scoping search processor service...
|
||||
..\workerservice install
|
||||
ECHO Operation complete, press any key to close window.
|
||||
PAUSE
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
ECHO OFF
|
||||
CLS
|
||||
ECHO Starting JDE scoping search processor service...
|
||||
..\workerservice start
|
||||
ECHO Operation complete, press any key to close window.
|
||||
PAUSE
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
ECHO OFF
|
||||
CLS
|
||||
ECHO Stopping JDE scoping search processor service...
|
||||
..\workerservice stop
|
||||
ECHO Operation complete, press any key to close window.
|
||||
PAUSE
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
ECHO OFF
|
||||
CLS
|
||||
ECHO Uninstalling JDE scoping search processor service...
|
||||
..\workerservice uninstall
|
||||
ECHO Operation complete, press any key to close window.
|
||||
PAUSE
|
||||
Reference in New Issue
Block a user