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:
Joseph Doherty
2026-01-02 07:43:29 -05:00
commit 26ff8d9b4f
1761 changed files with 596509 additions and 0 deletions
+48
View File
@@ -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>
+194
View File
@@ -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
View File
@@ -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");
}
}
}
+18
View File
@@ -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; }
}
}
+40
View File
@@ -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; }
}
}
+60
View File
@@ -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; }
}
}
+28
View File
@@ -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; }
}
}
+26
View File
@@ -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; }
}
}
+28
View File
@@ -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; }
}
}
+30
View File
@@ -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
View File
@@ -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; }
}
}
+26
View File
@@ -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
View File
@@ -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; }
}
}
+25
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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; }
}
}
+21
View File
@@ -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; }
}
}
+44
View File
@@ -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>();
}
}
}
+31
View File
@@ -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>
+2601
View File
File diff suppressed because it is too large Load Diff
+75
View File
@@ -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);
}
}
}
+499
View File
@@ -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
View File
@@ -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>);
}
}
}
+191
View File
@@ -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);
}
}
}
}
+220
View File
@@ -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
View File
@@ -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");
}
}
}
}
+217
View File
@@ -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();
}
}
}
+73
View File
@@ -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();
}
}
}
+35
View File
@@ -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")]
+70
View File
@@ -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
View File
@@ -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);
File diff suppressed because it is too large Load Diff
+482
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+346
View File
@@ -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>
+26
View File
@@ -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
}
}
+26
View File
@@ -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
}
}
+26
View File
@@ -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
}
}
+26
View File
@@ -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
}
}
+26
View File
@@ -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
}
}
+26
View File
@@ -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
View File
@@ -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
}
}
+26
View File
@@ -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
}
}
+26
View File
@@ -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
}
}
+26
View File
@@ -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
}
}
+26
View File
@@ -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
}
}
+26
View File
@@ -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
}
}
+26
View File
@@ -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
}
}
+26
View File
@@ -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
View File
@@ -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
}
}
+26
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
}
+70
View File
@@ -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>
+6
View File
@@ -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
+6
View File
@@ -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
+6
View File
@@ -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
View File
@@ -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