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:
@@ -0,0 +1,35 @@
|
||||
namespace JdeScoping.Core.Models.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
/// Database column specification
|
||||
/// </summary>
|
||||
public class ColumnSpec
|
||||
{
|
||||
/// <summary>
|
||||
/// Column name
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Column definition (SQL type and constraints)
|
||||
/// </summary>
|
||||
public string Definition { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
public ColumnSpec()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with name and definition
|
||||
/// </summary>
|
||||
/// <param name="name">Column name</param>
|
||||
/// <param name="definition">Column definition</param>
|
||||
public ColumnSpec(string name, string definition)
|
||||
{
|
||||
Name = name;
|
||||
Definition = definition;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using JdeScoping.Core.Models.Enums;
|
||||
|
||||
namespace JdeScoping.Core.Models.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
/// Cache data update tracking entity
|
||||
/// </summary>
|
||||
public class DataUpdate
|
||||
{
|
||||
/// <summary>
|
||||
/// PK ID of record
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of source system (JDE/CMS/etc)
|
||||
/// </summary>
|
||||
public string SourceSystem { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Name of source data (WORK_ORDER, WORK_ORDER_STEP, etc)
|
||||
/// </summary>
|
||||
public string SourceData { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Cache table name
|
||||
/// </summary>
|
||||
public string TableName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp at start of update
|
||||
/// </summary>
|
||||
public DateTime StartDt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp at end of update
|
||||
/// </summary>
|
||||
public DateTime EndDt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of data update (Hourly, Daily, Mass)
|
||||
/// </summary>
|
||||
public UpdateTypes UpdateType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the update was successful
|
||||
/// </summary>
|
||||
public bool WasSuccessful { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of records in update
|
||||
/// </summary>
|
||||
public long NumberRecords { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
namespace JdeScoping.Core.Models.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
/// Static query type definitions
|
||||
/// </summary>
|
||||
public partial class QueryTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// Work Order - Filter by work order only
|
||||
/// </summary>
|
||||
public static QueryTypes WorkOrder { get; } = new("WorkOrder", "Work Order", 10)
|
||||
{
|
||||
WorkOrderFilter = true
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Component Lot - Filter by component lot only
|
||||
/// </summary>
|
||||
public static QueryTypes ComponentLot { get; } = new("ComponentLot", "Component Lot", 20)
|
||||
{
|
||||
ComponentLotFilter = true
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Time Span + Profit Center
|
||||
/// </summary>
|
||||
public static QueryTypes TimeSpanProfitCenter { get; } = new("TimeSpanProfitCenter", "Time Span + Profit Center", 30)
|
||||
{
|
||||
TimeSpanFilter = true,
|
||||
ProfitCenterFilter = true
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Time Span + Work Center
|
||||
/// </summary>
|
||||
public static QueryTypes TimeSpanWorkCenter { get; } = new("TimeSpanWorkCenter", "Time Span + Work Center", 40)
|
||||
{
|
||||
TimeSpanFilter = true,
|
||||
WorkCenterFilter = true
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Time Span + Operator
|
||||
/// </summary>
|
||||
public static QueryTypes TimeSpanOperator { get; } = new("TimeSpanOperator", "Time Span + Operator", 50)
|
||||
{
|
||||
TimeSpanFilter = true,
|
||||
OperatorFilter = true
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Time Span + Profit Center + Item Number
|
||||
/// </summary>
|
||||
public static QueryTypes TimeSpanProfitCenterItem { get; } = new("TimeSpanProfitCenterItem", "Time Span + Profit Center + Item Number", 60)
|
||||
{
|
||||
TimeSpanFilter = true,
|
||||
ItemNumberFilter = true,
|
||||
ProfitCenterFilter = true
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Time Span + Profit Center + Item/Operation/MIS
|
||||
/// </summary>
|
||||
public static QueryTypes TimeSpanProfitCenterIOM { get; } = new("TimeSpanProfitCenterIOM", "Time Span + Profit Center + Item/Operation/MIS", 70)
|
||||
{
|
||||
TimeSpanFilter = true,
|
||||
ProfitCenterFilter = true,
|
||||
ItemOperationMisFilter = true
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Time Span + Profit Center + Work Order + Item/Operation/MIS
|
||||
/// </summary>
|
||||
public static QueryTypes TimeSpanProfitCenterWorkOrderIOM { get; } = new("TimeSpanProfitCenterWorkOrderIOM", "Time Span + Profit Center + Work Order + Item/Operation/MIS", 80)
|
||||
{
|
||||
TimeSpanFilter = true,
|
||||
WorkOrderFilter = true,
|
||||
ProfitCenterFilter = true,
|
||||
ItemOperationMisFilter = true
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Time Span + Profit Center + Extract MIS
|
||||
/// </summary>
|
||||
public static QueryTypes TimeSpanProfitCenterExtractMIS { get; } = new("TimeSpanProfitCenterExtractMIS", "Time Span + Profit Center + Extract MIS", 90)
|
||||
{
|
||||
TimeSpanFilter = true,
|
||||
ProfitCenterFilter = true,
|
||||
ExtractMisFilter = true
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Time Span + Work Center + Item Number
|
||||
/// </summary>
|
||||
public static QueryTypes TimeSpanWorkCenterItem { get; } = new("TimeSpanWorkCenterItem", "Time Span + Work Center + Item Number", 100)
|
||||
{
|
||||
TimeSpanFilter = true,
|
||||
ItemNumberFilter = true,
|
||||
WorkCenterFilter = true
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Time Span + Work Center + Extract MIS
|
||||
/// </summary>
|
||||
public static QueryTypes TimeSpanWorkCenterExtractMIS { get; } = new("TimeSpanWorkCenterExtractMIS", "Time Span + Work Center + Extract MIS", 110)
|
||||
{
|
||||
TimeSpanFilter = true,
|
||||
WorkCenterFilter = true,
|
||||
ExtractMisFilter = true
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Time Span + Work Center + Item/Operation/MIS
|
||||
/// </summary>
|
||||
public static QueryTypes TimeSpanWorkCenterIOM { get; } = new("TimeSpanWorkCenterIOM", "Time Span + Work Center + Item/Operation/MIS", 120)
|
||||
{
|
||||
TimeSpanFilter = true,
|
||||
WorkCenterFilter = true,
|
||||
ItemOperationMisFilter = true
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Time Span + Work Center + Work Order + Item/Operation/MIS
|
||||
/// </summary>
|
||||
public static QueryTypes TimeSpanWorkCenterWorkOrderIOM { get; } = new("TimeSpanWorkCenterWorkOrderIOM", "Time Span + Work Center + Work Order + Item/Operation/MIS", 130)
|
||||
{
|
||||
TimeSpanFilter = true,
|
||||
WorkOrderFilter = true,
|
||||
WorkCenterFilter = true,
|
||||
ItemOperationMisFilter = true
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Time Span + Item Number
|
||||
/// </summary>
|
||||
public static QueryTypes TimeSpanItem { get; } = new("TimeSpanItem", "Time Span + Item Number", 140)
|
||||
{
|
||||
TimeSpanFilter = true,
|
||||
ItemNumberFilter = true
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Time Span + Work Center + Operator
|
||||
/// </summary>
|
||||
public static QueryTypes TimeSpanWorkCenterOperator { get; } = new("TimeSpanWorkCenterOperator", "Time Span + Work Center + Operator", 150)
|
||||
{
|
||||
TimeSpanFilter = true,
|
||||
WorkCenterFilter = true,
|
||||
OperatorFilter = true
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Time Span + Profit Center + Operator
|
||||
/// </summary>
|
||||
public static QueryTypes TimeSpanProfitCenterOperator { get; } = new("TimeSpanProfitCenterOperator", "Time Span + Profit Center + Operator", 160)
|
||||
{
|
||||
TimeSpanFilter = true,
|
||||
ProfitCenterFilter = true,
|
||||
OperatorFilter = true
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
using JdeScoping.Core.Models.Search;
|
||||
|
||||
namespace JdeScoping.Core.Models.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
/// Query type definition with filter flags.
|
||||
/// Defines valid combinations of search filters that can be applied together.
|
||||
/// </summary>
|
||||
public partial class QueryTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// Query type unique code
|
||||
/// </summary>
|
||||
public string Code { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Query type display name
|
||||
/// </summary>
|
||||
public string Name { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Sort order index for display
|
||||
/// </summary>
|
||||
public int OrderIndex { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this query type supports time span filtering
|
||||
/// </summary>
|
||||
public bool TimeSpanFilter { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this query type supports work order filtering
|
||||
/// </summary>
|
||||
public bool WorkOrderFilter { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this query type supports item number filtering
|
||||
/// </summary>
|
||||
public bool ItemNumberFilter { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this query type supports profit center filtering
|
||||
/// </summary>
|
||||
public bool ProfitCenterFilter { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this query type supports work center filtering
|
||||
/// </summary>
|
||||
public bool WorkCenterFilter { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this query type supports component lot filtering
|
||||
/// </summary>
|
||||
public bool ComponentLotFilter { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this query type supports operator filtering
|
||||
/// </summary>
|
||||
public bool OperatorFilter { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this query type supports item/operation/MIS filtering
|
||||
/// </summary>
|
||||
public bool ItemOperationMisFilter { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this query type supports MIS data extraction
|
||||
/// </summary>
|
||||
public bool ExtractMisFilter { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this query type supports received item number IIS filtering
|
||||
/// </summary>
|
||||
public bool ReceivedItemNumberIisFilter { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// Static dictionary of all defined query types
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, QueryTypes> _definedTypes = new();
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor for serialization
|
||||
/// </summary>
|
||||
public QueryTypes()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with code, name, and order index
|
||||
/// </summary>
|
||||
private QueryTypes(string code, string name, int orderIndex)
|
||||
{
|
||||
Code = code;
|
||||
Name = name;
|
||||
OrderIndex = orderIndex;
|
||||
_definedTypes[code] = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the appropriate query type based on search criteria flags.
|
||||
/// </summary>
|
||||
/// <param name="criteria">Search criteria to evaluate</param>
|
||||
/// <returns>Matching QueryTypes or null if no match found</returns>
|
||||
public static QueryTypes? Identify(SearchCriteria criteria)
|
||||
{
|
||||
if (criteria == null)
|
||||
return null;
|
||||
|
||||
// Determine which filters are active
|
||||
bool hasTimeSpan = criteria.MinimumDt.HasValue || criteria.MaximumDt.HasValue;
|
||||
bool hasWorkOrder = criteria.WorkOrderNumbers?.Count > 0;
|
||||
bool hasItemNumber = criteria.ItemNumbers?.Count > 0;
|
||||
bool hasProfitCenter = criteria.ProfitCenters?.Count > 0;
|
||||
bool hasWorkCenter = criteria.WorkCenters?.Count > 0;
|
||||
bool hasComponentLot = criteria.ComponentLotNumbers?.Count > 0;
|
||||
bool hasOperator = criteria.OperatorIDs?.Count > 0;
|
||||
bool hasItemOperationMis = criteria.PartOperations?.Count > 0;
|
||||
bool hasExtractMis = criteria.ExtractMisData;
|
||||
|
||||
// Find matching query type
|
||||
foreach (var queryType in _definedTypes.Values)
|
||||
{
|
||||
if (queryType.Matches(hasTimeSpan, hasWorkOrder, hasItemNumber, hasProfitCenter,
|
||||
hasWorkCenter, hasComponentLot, hasOperator, hasItemOperationMis, hasExtractMis))
|
||||
{
|
||||
return queryType;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the given filter flags match this query type's configuration.
|
||||
/// </summary>
|
||||
private bool Matches(bool timeSpan, bool workOrder, bool itemNumber, bool profitCenter,
|
||||
bool workCenter, bool componentLot, bool operatorFilter, bool itemOperationMis, bool extractMis)
|
||||
{
|
||||
return TimeSpanFilter == timeSpan &&
|
||||
WorkOrderFilter == workOrder &&
|
||||
ItemNumberFilter == itemNumber &&
|
||||
ProfitCenterFilter == profitCenter &&
|
||||
WorkCenterFilter == workCenter &&
|
||||
ComponentLotFilter == componentLot &&
|
||||
OperatorFilter == operatorFilter &&
|
||||
ItemOperationMisFilter == itemOperationMis &&
|
||||
ExtractMisFilter == extractMis;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all defined query types as a read-only dictionary
|
||||
/// </summary>
|
||||
public static IReadOnlyDictionary<string, QueryTypes> DefinedTypes => _definedTypes;
|
||||
|
||||
/// <summary>
|
||||
/// Gets all defined query types sorted by OrderIndex
|
||||
/// </summary>
|
||||
public static IEnumerable<QueryTypes> GetAll() =>
|
||||
_definedTypes.Values.OrderBy(qt => qt.OrderIndex);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a query type by its code
|
||||
/// </summary>
|
||||
/// <param name="code">The query type code</param>
|
||||
/// <returns>The matching QueryTypes or null if not found</returns>
|
||||
public static QueryTypes? GetByCode(string code) =>
|
||||
_definedTypes.TryGetValue(code, out var queryType) ? queryType : null;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
namespace JdeScoping.Core.Models.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
/// Generic process status update message
|
||||
/// </summary>
|
||||
public class StatusUpdate
|
||||
{
|
||||
/// <summary>
|
||||
/// Status message to display
|
||||
/// </summary>
|
||||
public string Message { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp when message was generated
|
||||
/// </summary>
|
||||
public DateTime Timestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
public StatusUpdate()
|
||||
{
|
||||
Timestamp = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with message
|
||||
/// </summary>
|
||||
/// <param name="message">Status message</param>
|
||||
public StatusUpdate(string message)
|
||||
{
|
||||
Message = message;
|
||||
Timestamp = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
namespace JdeScoping.Core.Models.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
/// Database table specification for dynamic SQL generation
|
||||
/// </summary>
|
||||
public class TableSpec
|
||||
{
|
||||
/// <summary>
|
||||
/// Table name
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Temporary table name (computed as # + 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>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
public TableSpec()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with table name
|
||||
/// </summary>
|
||||
/// <param name="name">Table name</param>
|
||||
public TableSpec(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates SQL for creating an index on the primary key (stub)
|
||||
/// </summary>
|
||||
/// <returns>SQL CREATE INDEX statement</returns>
|
||||
public string GenerateIndex()
|
||||
{
|
||||
// Stub implementation - to be expanded based on spec
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates SQL for dropping the table (stub)
|
||||
/// </summary>
|
||||
/// <returns>SQL DROP TABLE statement</returns>
|
||||
public string GenerateDrop()
|
||||
{
|
||||
// Stub implementation - to be expanded based on spec
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates SQL for creating the table (stub)
|
||||
/// </summary>
|
||||
/// <returns>SQL CREATE TABLE statement</returns>
|
||||
public string GenerateCreate()
|
||||
{
|
||||
// Stub implementation - to be expanded based on spec
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a column specification by name (stub)
|
||||
/// </summary>
|
||||
/// <param name="columnName">Name of column to find</param>
|
||||
/// <returns>ColumnSpec or null if not found</returns>
|
||||
public ColumnSpec? GetColumn(string columnName)
|
||||
{
|
||||
return Columns.Find(c => c.Name == columnName);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user