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
@@ -0,0 +1,12 @@
namespace JdeScoping.Core.Models;
/// <summary>
/// Result of an authentication attempt
/// </summary>
/// <param name="Success">Whether authentication was successful</param>
/// <param name="User">User info if successful, null otherwise</param>
/// <param name="ErrorMessage">Error message if failed, null otherwise</param>
public record AuthResult(
bool Success,
UserInfo? User,
string? ErrorMessage);
@@ -0,0 +1,35 @@
using System.Text.Json.Serialization;
namespace JdeScoping.Core.Models.Enums;
/// <summary>
/// Status of a search request
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum SearchStatus
{
/// <summary>
/// Search has been created but not yet submitted
/// </summary>
New = 0,
/// <summary>
/// Search has been submitted and is queued for processing
/// </summary>
Queued = 1,
/// <summary>
/// Search processing is running
/// </summary>
Running = 2,
/// <summary>
/// Search processing has completed successfully
/// </summary>
Ended = 3,
/// <summary>
/// Search processing encountered an error
/// </summary>
Error = 4
}
@@ -0,0 +1,25 @@
using System.Text.Json.Serialization;
namespace JdeScoping.Core.Models.Enums;
/// <summary>
/// Types of data synchronization updates
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum UpdateTypes
{
/// <summary>
/// Hourly incremental update
/// </summary>
Hourly = 1,
/// <summary>
/// Daily update
/// </summary>
Daily = 2,
/// <summary>
/// Mass (full) data refresh
/// </summary>
Mass = 3
}
@@ -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);
}
}
@@ -0,0 +1,49 @@
using JdeScoping.Core.Helpers;
namespace JdeScoping.Core.Models.Inventory;
/// <summary>
/// JDE item (part type) master entity
/// </summary>
public class Item
{
/// <summary>
/// Item unique short number
/// </summary>
public long ShortItemNumber { get; set; }
/// <summary>
/// Item unique number
/// </summary>
public string ItemNumber { get; set; } = string.Empty;
/// <summary>
/// Item description
/// </summary>
public string Description { get; set; } = string.Empty;
/// <summary>
/// Item master planning family
/// </summary>
public string? PlanningFamily { get; set; }
/// <summary>
/// Item master stocking type code
/// </summary>
public string? StockingType { get; set; }
/// <summary>
/// JDE date of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateDate { get; set; }
/// <summary>
/// JDE time of day of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateTime { get; set; }
/// <summary>
/// Timestamp of last update to record (computed from JDE date/time)
/// </summary>
public DateTime? LastUpdateDt => JdeDateConverter.ToDateTime(LastUpdateDate, LastUpdateTime);
}
@@ -0,0 +1,69 @@
using JdeScoping.Core.Helpers;
namespace JdeScoping.Core.Models.Inventory;
/// <summary>
/// JDE lot entity
/// </summary>
public class Lot
{
/// <summary>
/// Lot unique number
/// </summary>
public string LotNumber { get; set; } = string.Empty;
/// <summary>
/// Business unit unique code
/// </summary>
public string BranchCode { get; set; } = string.Empty;
/// <summary>
/// Short item number
/// </summary>
public long ShortItemNumber { get; set; }
/// <summary>
/// Item number
/// </summary>
public string ItemNumber { get; set; } = string.Empty;
/// <summary>
/// Supplier address number
/// </summary>
public long SupplierCode { get; set; }
/// <summary>
/// Lot status code
/// </summary>
public char StatusCode { get; set; }
/// <summary>
/// Memo line 1
/// </summary>
public string? Memo1 { get; set; }
/// <summary>
/// Memo line 2
/// </summary>
public string? Memo2 { get; set; }
/// <summary>
/// Memo line 3
/// </summary>
public string? Memo3 { get; set; }
/// <summary>
/// JDE date of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateDate { get; set; }
/// <summary>
/// JDE time of day of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateTime { get; set; }
/// <summary>
/// Timestamp of last update to record (computed from JDE date/time)
/// </summary>
public DateTime? LastUpdateDt => JdeDateConverter.ToDateTime(LastUpdateDate, LastUpdateTime);
}
@@ -0,0 +1,32 @@
namespace JdeScoping.Core.Models.Inventory;
/// <summary>
/// JDE lot location entity
/// </summary>
public class LotLocation
{
/// <summary>
/// Lot unique number
/// </summary>
public string LotNumber { get; set; } = string.Empty;
/// <summary>
/// Short item number
/// </summary>
public long ShortItemNumber { get; set; }
/// <summary>
/// Business unit unique code
/// </summary>
public string BranchCode { get; set; } = string.Empty;
/// <summary>
/// Location code where lot is located
/// </summary>
public string Location { get; set; } = string.Empty;
/// <summary>
/// Timestamp of last update to record
/// </summary>
public DateTime? LastUpdateDt { get; set; }
}
@@ -0,0 +1,54 @@
using JdeScoping.Core.Helpers;
namespace JdeScoping.Core.Models.Inventory;
/// <summary>
/// Cardex (lot usage) entry entity
/// </summary>
public class LotUsage
{
/// <summary>
/// Record unique / PK ID
/// </summary>
public long UniqueId { get; set; }
/// <summary>
/// Work order unique number
/// </summary>
public long WorkOrderNumber { get; set; }
/// <summary>
/// Lot number of component
/// </summary>
public string LotNumber { get; set; } = string.Empty;
/// <summary>
/// Component issuance branch code
/// </summary>
public string BranchCode { get; set; } = string.Empty;
/// <summary>
/// Component item short number
/// </summary>
public long ShortItemNumber { get; set; }
/// <summary>
/// Transaction quantity
/// </summary>
public decimal Quantity { get; set; }
/// <summary>
/// JDE date of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateDate { get; set; }
/// <summary>
/// JDE time of day of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateTime { get; set; }
/// <summary>
/// Timestamp of last update to record (computed from JDE date/time)
/// </summary>
public DateTime? LastUpdateDt => JdeDateConverter.ToDateTime(LastUpdateDate, LastUpdateTime);
}
@@ -0,0 +1,22 @@
namespace JdeScoping.Core.Models.Lookup;
/// <summary>
/// JDE function code lookup entity
/// </summary>
public class FunctionCode
{
/// <summary>
/// Unique function code
/// </summary>
public string Code { get; set; } = string.Empty;
/// <summary>
/// Function description
/// </summary>
public string Description { get; set; } = string.Empty;
/// <summary>
/// Timestamp of last update to record
/// </summary>
public DateTime? LastUpdateDt { get; set; }
}
@@ -0,0 +1,34 @@
using JdeScoping.Core.Helpers;
namespace JdeScoping.Core.Models.Lookup;
/// <summary>
/// JDE work order status code lookup entity
/// </summary>
public class StatusCode
{
/// <summary>
/// Status code unique code
/// </summary>
public string Code { get; set; } = string.Empty;
/// <summary>
/// Status code description
/// </summary>
public string Description { get; set; } = string.Empty;
/// <summary>
/// JDE date of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateDate { get; set; }
/// <summary>
/// JDE time of day of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateTime { get; set; }
/// <summary>
/// Timestamp of last update to record (computed from JDE date/time)
/// </summary>
public DateTime? LastUpdateDt => JdeDateConverter.ToDateTime(LastUpdateDate, LastUpdateTime);
}
@@ -0,0 +1,34 @@
using JdeScoping.Core.Helpers;
namespace JdeScoping.Core.Models.Organization;
/// <summary>
/// JDE branch entity
/// </summary>
public class Branch
{
/// <summary>
/// Branch unique code
/// </summary>
public string Code { get; set; } = string.Empty;
/// <summary>
/// Branch description
/// </summary>
public string Description { get; set; } = string.Empty;
/// <summary>
/// JDE date of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateDate { get; set; }
/// <summary>
/// JDE time of day of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateTime { get; set; }
/// <summary>
/// Timestamp of last update to record (computed from JDE date/time)
/// </summary>
public DateTime? LastUpdateDt => JdeDateConverter.ToDateTime(LastUpdateDate, LastUpdateTime);
}
@@ -0,0 +1,39 @@
using JdeScoping.Core.Helpers;
namespace JdeScoping.Core.Models.Organization;
/// <summary>
/// JDE user (operator) entity
/// </summary>
public class JdeUser
{
/// <summary>
/// User unique address number
/// </summary>
public long AddressNumber { get; set; }
/// <summary>
/// User unique login ID
/// </summary>
public string UserId { get; set; } = string.Empty;
/// <summary>
/// User's full name (last, first [middle initial])
/// </summary>
public string FullName { get; set; } = string.Empty;
/// <summary>
/// JDE date of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateDate { get; set; }
/// <summary>
/// JDE time of day of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateTime { get; set; }
/// <summary>
/// Timestamp of last update to record (computed from JDE date/time)
/// </summary>
public DateTime? LastUpdateDt => JdeDateConverter.ToDateTime(LastUpdateDate, LastUpdateTime);
}
@@ -0,0 +1,39 @@
using JdeScoping.Core.Helpers;
namespace JdeScoping.Core.Models.Organization;
/// <summary>
/// Organization hierarchy (profit center to work center mapping) entity
/// </summary>
public class OrgHierarchy
{
/// <summary>
/// Work center unique code
/// </summary>
public string WorkCenterCode { get; set; } = string.Empty;
/// <summary>
/// Branch unit code
/// </summary>
public string BranchCode { get; set; } = string.Empty;
/// <summary>
/// Profit center unique code
/// </summary>
public string ProfitCenterCode { get; set; } = string.Empty;
/// <summary>
/// JDE date of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateDate { get; set; }
/// <summary>
/// JDE time of day of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateTime { get; set; }
/// <summary>
/// Timestamp of last update to record (computed from JDE date/time)
/// </summary>
public DateTime? LastUpdateDt => JdeDateConverter.ToDateTime(LastUpdateDate, LastUpdateTime);
}
@@ -0,0 +1,35 @@
using JdeScoping.Core.Helpers;
using JdeScoping.Core.Interfaces;
namespace JdeScoping.Core.Models.Organization;
/// <summary>
/// JDE profit center entity
/// </summary>
public class ProfitCenter : IBusinessUnit
{
/// <summary>
/// Profit center unique code
/// </summary>
public string Code { get; set; } = string.Empty;
/// <summary>
/// Profit center description
/// </summary>
public string Description { get; set; } = string.Empty;
/// <summary>
/// JDE date of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateDate { get; set; }
/// <summary>
/// JDE time of day of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateTime { get; set; }
/// <summary>
/// Timestamp of last update to record (computed from JDE date/time)
/// </summary>
public DateTime? LastUpdateDt => JdeDateConverter.ToDateTime(LastUpdateDate, LastUpdateTime);
}
@@ -0,0 +1,74 @@
using JdeScoping.Core.Helpers;
namespace JdeScoping.Core.Models.Organization;
/// <summary>
/// JDE item router master entity
/// </summary>
public class RouteMaster
{
/// <summary>
/// Unique code for branch
/// </summary>
public string BranchCode { get; set; } = string.Empty;
/// <summary>
/// Unique number for item
/// </summary>
public string ItemNumber { get; set; } = string.Empty;
/// <summary>
/// Router type
/// </summary>
public string RoutingType { get; set; } = string.Empty;
/// <summary>
/// Job step operation number
/// </summary>
public decimal SequenceNumber { get; set; }
/// <summary>
/// Job step function code
/// </summary>
public string FunctionCode { get; set; } = string.Empty;
/// <summary>
/// Work center unique code
/// </summary>
public string WorkCenterCode { get; set; } = string.Empty;
/// <summary>
/// JDE date representation of date record effectivity starts (private backing field for Dapper mapping)
/// </summary>
private int StartDateDate { get; set; }
/// <summary>
/// Date record effectivity starts (computed from JDE date)
/// </summary>
public DateTime? StartDate => JdeDateConverter.ToDateTime(StartDateDate);
/// <summary>
/// JDE date representation of date record effectivity ends (private backing field for Dapper mapping)
/// </summary>
private int? EndDateDate { get; set; }
/// <summary>
/// Date record effectivity ends (computed from JDE date, nullable)
/// </summary>
public DateTime? EndDate => EndDateDate.HasValue ? JdeDateConverter.ToDateTime(EndDateDate.Value) : null;
/// <summary>
/// JDE date of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateDate { get; set; }
/// <summary>
/// JDE time of day of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateTime { get; set; }
/// <summary>
/// Timestamp of last update to record (computed from JDE date/time)
/// </summary>
public DateTime? LastUpdateDt => JdeDateConverter.ToDateTime(LastUpdateDate, LastUpdateTime);
}
@@ -0,0 +1,35 @@
using JdeScoping.Core.Helpers;
using JdeScoping.Core.Interfaces;
namespace JdeScoping.Core.Models.Organization;
/// <summary>
/// JDE work center entity
/// </summary>
public class WorkCenter : IBusinessUnit
{
/// <summary>
/// Work center unique code
/// </summary>
public string Code { get; set; } = string.Empty;
/// <summary>
/// Work center description
/// </summary>
public string Description { get; set; } = string.Empty;
/// <summary>
/// JDE date of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateDate { get; set; }
/// <summary>
/// JDE time of day of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateTime { get; set; }
/// <summary>
/// Timestamp of last update to record (computed from JDE date/time)
/// </summary>
public DateTime? LastUpdateDt => JdeDateConverter.ToDateTime(LastUpdateDate, LastUpdateTime);
}
@@ -0,0 +1,72 @@
namespace JdeScoping.Core.Models.Quality;
/// <summary>
/// CMS MIS data entity
/// </summary>
public class MisData
{
/// <summary>
/// Item unique number
/// </summary>
public string ItemNumber { get; set; } = string.Empty;
/// <summary>
/// Branch unique code
/// </summary>
public string BranchCode { get; set; } = string.Empty;
/// <summary>
/// Operation job step number
/// </summary>
public string SequenceNumber { get; set; } = string.Empty;
/// <summary>
/// MIS unique number
/// </summary>
public string MisNumber { get; set; } = string.Empty;
/// <summary>
/// MIS revision ID
/// </summary>
public string? RevId { get; set; }
/// <summary>
/// Characteristic number
/// </summary>
public string CharNumber { get; set; } = string.Empty;
/// <summary>
/// Test description
/// </summary>
public string? TestDescription { get; set; }
/// <summary>
/// Type of sampling
/// </summary>
public string? SamplingType { get; set; }
/// <summary>
/// Sampling selection value
/// </summary>
public string? SamplingValue { get; set; }
/// <summary>
/// Tools and gauges for MIS
/// </summary>
public string? ToolsGauges { get; set; }
/// <summary>
/// Work instructions for MIS
/// </summary>
public string? WorkInstructions { get; set; }
/// <summary>
/// MIS release status
/// </summary>
public string Status { get; set; } = string.Empty;
/// <summary>
/// MIS release date
/// </summary>
public DateTime? ReleaseDate { get; set; }
}
@@ -0,0 +1,85 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using JdeScoping.Core.Models.Enums;
namespace JdeScoping.Core.Models.Search;
/// <summary>
/// User search request entity
/// </summary>
public class Search
{
/// <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; } = string.Empty;
/// <summary>
/// User-friendly name for search
/// </summary>
public string Name { get; set; } = string.Empty;
/// <summary>
/// Current search status
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public SearchStatus Status { 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>
/// JSON-packed search criteria (stored in database)
/// </summary>
public string CriteriaJson { get; set; } = string.Empty;
/// <summary>
/// Search criteria (deserialized from CriteriaJSON)
/// </summary>
[JsonIgnore]
public SearchCriteria? Criteria
{
get
{
if (string.IsNullOrEmpty(CriteriaJson))
return null;
try
{
return JsonSerializer.Deserialize<SearchCriteria>(CriteriaJson);
}
catch
{
return null;
}
}
set
{
CriteriaJson = value != null
? JsonSerializer.Serialize(value)
: string.Empty;
}
}
/// <summary>
/// Excel search results file (binary)
/// </summary>
public byte[]? Results { get; set; }
}
@@ -0,0 +1,59 @@
using JdeScoping.Core.ViewModels;
namespace JdeScoping.Core.Models.Search;
/// <summary>
/// JDE data filter criteria for search requests
/// </summary>
public class SearchCriteria
{
/// <summary>
/// Minimum timestamp to include in search results
/// </summary>
public DateTime? MinimumDt { get; set; }
/// <summary>
/// Maximum timestamp to include in search results
/// </summary>
public DateTime? MaximumDt { get; set; }
/// <summary>
/// Collection of work order numbers to include
/// </summary>
public List<long> WorkOrderNumbers { get; set; } = [];
/// <summary>
/// Collection of item numbers to include
/// </summary>
public List<string> ItemNumbers { get; set; } = [];
/// <summary>
/// Collection of profit center codes to include
/// </summary>
public List<string> ProfitCenters { get; set; } = [];
/// <summary>
/// Collection of work center codes to include
/// </summary>
public List<string> WorkCenters { get; set; } = [];
/// <summary>
/// Collection of operator IDs to include
/// </summary>
public List<string> OperatorIDs { get; set; } = [];
/// <summary>
/// Collection of component lot numbers to include (for upper level lot filtering)
/// </summary>
public List<LotViewModel> ComponentLotNumbers { get; set; } = [];
/// <summary>
/// Whether or not to extract MIS data from CMS
/// </summary>
public bool ExtractMisData { get; set; }
/// <summary>
/// List of part/operation/MIS combinations for filtering
/// </summary>
public List<PartOperationViewModel> PartOperations { get; set; } = [];
}
@@ -0,0 +1,75 @@
using System.Text.Json.Serialization;
using JdeScoping.Core.Models.Enums;
namespace JdeScoping.Core.Models.Search;
/// <summary>
/// Search status update message for SignalR notifications
/// </summary>
public class SearchUpdate
{
/// <summary>
/// Search PK ID
/// </summary>
public int Id { get; set; }
/// <summary>
/// User name of user that submitted search
/// </summary>
public string UserName { get; set; } = string.Empty;
/// <summary>
/// Name of the search
/// </summary>
public string Name { get; set; } = string.Empty;
/// <summary>
/// Search status code
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public SearchStatus Status { 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>
/// Timestamp when this update was generated
/// </summary>
public DateTime Timestamp { get; set; }
/// <summary>
/// Default constructor
/// </summary>
public SearchUpdate()
{
Timestamp = DateTime.UtcNow;
}
/// <summary>
/// Constructor that copies values from a Search entity
/// </summary>
/// <param name="search">Search to copy values from</param>
public SearchUpdate(Search search)
{
Id = search.Id;
UserName = search.UserName;
Name = search.Name;
Status = search.Status;
SubmitDt = search.SubmitDt;
StartDt = search.StartDt;
EndDt = search.EndDt;
Timestamp = DateTime.UtcNow;
}
}
@@ -0,0 +1,54 @@
namespace JdeScoping.Core.Models;
/// <summary>
/// Authenticated user information entity (replaces legacy LDAPEntry)
/// </summary>
public class UserInfo
{
/// <summary>
/// LDAP Distinguished Name
/// </summary>
public string Dn { get; set; } = string.Empty;
/// <summary>
/// User's login username
/// </summary>
public string Username { get; set; } = string.Empty;
/// <summary>
/// User's first name
/// </summary>
public string FirstName { get; set; } = string.Empty;
/// <summary>
/// User's last name
/// </summary>
public string LastName { get; set; } = string.Empty;
/// <summary>
/// User's display name (computed property)
/// Falls back to Username if FirstName and LastName are both empty
/// </summary>
public string DisplayName
{
get
{
if (string.IsNullOrEmpty(LastName) && string.IsNullOrEmpty(FirstName))
{
return Username;
}
return $"{FirstName} {LastName}".Trim();
}
}
/// <summary>
/// User's organization title
/// </summary>
public string Title { get; set; } = string.Empty;
/// <summary>
/// User's email address
/// </summary>
public string EmailAddress { get; set; } = string.Empty;
}
@@ -0,0 +1,94 @@
using JdeScoping.Core.Helpers;
namespace JdeScoping.Core.Models.WorkOrders;
/// <summary>
/// JDE work order entity
/// </summary>
public class WorkOrder
{
/// <summary>
/// Work order unique number
/// </summary>
public long WorkOrderNumber { get; set; }
/// <summary>
/// Work order branch code
/// </summary>
public string BranchCode { get; set; } = string.Empty;
/// <summary>
/// Work order assigned lot number
/// </summary>
public string? LotNumber { get; set; }
/// <summary>
/// Work order item number
/// </summary>
public string ItemNumber { get; set; } = string.Empty;
/// <summary>
/// Work order short item number
/// </summary>
public long ShortItemNumber { get; set; }
/// <summary>
/// Work order's parent unique number
/// </summary>
public string? ParentWorkOrderNumber { get; set; }
/// <summary>
/// Order quantity
/// </summary>
public decimal OrderQuantity { get; set; }
/// <summary>
/// Quantity on hold
/// </summary>
public decimal HeldQuantity { get; set; }
/// <summary>
/// Quantity shipped
/// </summary>
public decimal ShippedQuantity { get; set; }
/// <summary>
/// Work order status code
/// </summary>
public string StatusCode { get; set; } = string.Empty;
/// <summary>
/// Date of last update to status code
/// </summary>
public DateTime? StatusCodeUpdateDt { get; set; }
/// <summary>
/// Date work order was issued
/// </summary>
public DateTime IssueDate { get; set; }
/// <summary>
/// Date work order was started
/// </summary>
public DateTime StartDate { get; set; }
/// <summary>
/// Work order routing type
/// </summary>
public string RoutingType { get; set; } = string.Empty;
/// <summary>
/// JDE date of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateDate { get; set; }
/// <summary>
/// JDE time of day of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateTime { get; set; }
/// <summary>
/// Timestamp of last update to record (computed from JDE date/time)
/// </summary>
public DateTime? LastUpdateDt => JdeDateConverter.ToDateTime(LastUpdateDate, LastUpdateTime);
}
@@ -0,0 +1,54 @@
using JdeScoping.Core.Helpers;
namespace JdeScoping.Core.Models.WorkOrders;
/// <summary>
/// Work order component usage entity
/// </summary>
public class WorkOrderComponent
{
/// <summary>
/// Record unique / PK ID
/// </summary>
public long UniqueId { get; set; }
/// <summary>
/// Work order unique number
/// </summary>
public long WorkOrderNumber { get; set; }
/// <summary>
/// Lot number of component
/// </summary>
public string? LotNumber { get; set; }
/// <summary>
/// Component issuance branch code
/// </summary>
public string BranchCode { get; set; } = string.Empty;
/// <summary>
/// Component item short number
/// </summary>
public long? ShortItemNumber { get; set; }
/// <summary>
/// Transaction quantity
/// </summary>
public decimal Quantity { get; set; }
/// <summary>
/// JDE date of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateDate { get; set; }
/// <summary>
/// JDE time of day of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateTime { get; set; }
/// <summary>
/// Timestamp of last update to record (computed from JDE date/time)
/// </summary>
public DateTime? LastUpdateDt => JdeDateConverter.ToDateTime(LastUpdateDate, LastUpdateTime);
}
@@ -0,0 +1,89 @@
using JdeScoping.Core.Helpers;
namespace JdeScoping.Core.Models.WorkOrders;
/// <summary>
/// Work order step transaction (routing) entity
/// </summary>
public class WorkOrderRouting
{
/// <summary>
/// Transaction user ID
/// </summary>
public string UserId { get; set; } = string.Empty;
/// <summary>
/// Transaction batch number
/// </summary>
public string BatchNumber { get; set; } = string.Empty;
/// <summary>
/// Transaction number
/// </summary>
public string TransactionNumber { get; set; } = string.Empty;
/// <summary>
/// Transaction line number
/// </summary>
public int LineNumber { get; set; }
/// <summary>
/// Operation sequence number
/// </summary>
public decimal StepNumber { get; set; }
/// <summary>
/// Unique code for work center
/// </summary>
public string WorkCenterCode { get; set; } = string.Empty;
/// <summary>
/// Unique number for work order
/// </summary>
public long WorkOrderNumber { get; set; }
/// <summary>
/// Work order routing type
/// </summary>
public string RoutingType { get; set; } = string.Empty;
/// <summary>
/// Unique code for branch
/// </summary>
public string BranchCode { get; set; } = string.Empty;
/// <summary>
/// Operation sequence description
/// </summary>
public string? StepDescription { get; set; }
/// <summary>
/// Operation function code
/// </summary>
public string FunctionCode { get; set; } = string.Empty;
/// <summary>
/// JDE date representation of transaction original date (private backing field for Dapper mapping)
/// </summary>
private int TransactionDateDate { get; set; }
/// <summary>
/// Transaction original date (computed from JDE date)
/// </summary>
public DateTime? TransactionDate => JdeDateConverter.ToDateTime(TransactionDateDate);
/// <summary>
/// JDE date of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateDate { get; set; }
/// <summary>
/// JDE time of day of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateTime { get; set; }
/// <summary>
/// Timestamp of last update to record (computed from JDE date/time)
/// </summary>
public DateTime? LastUpdateDt => JdeDateConverter.ToDateTime(LastUpdateDate, LastUpdateTime);
}
@@ -0,0 +1,79 @@
using JdeScoping.Core.Helpers;
namespace JdeScoping.Core.Models.WorkOrders;
/// <summary>
/// JDE work order operation step entity
/// </summary>
public class WorkOrderStep
{
/// <summary>
/// Unique number for work order
/// </summary>
public long WorkOrderNumber { get; set; }
/// <summary>
/// Unique code for branch
/// </summary>
public string BranchCode { get; set; } = string.Empty;
/// <summary>
/// Unique code for work center
/// </summary>
public string WorkCenterCode { get; set; } = string.Empty;
/// <summary>
/// Operation sequence number
/// </summary>
public decimal StepNumber { get; set; }
/// <summary>
/// Operation sequence description
/// </summary>
public string? StepDescription { get; set; }
/// <summary>
/// Function operation description (long text)
/// </summary>
public string? FunctionOperationDescription { get; set; }
/// <summary>
/// Operation sequence type code
/// </summary>
public string StepTypeCode { get; set; } = string.Empty;
/// <summary>
/// Timestamp when work step began
/// </summary>
public DateTime? StartDt { get; set; }
/// <summary>
/// Timestamp when work step ended
/// </summary>
public DateTime? EndDt { get; set; }
/// <summary>
/// Operation function code
/// </summary>
public string FunctionCode { get; set; } = string.Empty;
/// <summary>
/// Quantity scrapped/cancelled
/// </summary>
public decimal ScrappedQuantity { get; set; }
/// <summary>
/// JDE date of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateDate { get; set; }
/// <summary>
/// JDE time of day of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateTime { get; set; }
/// <summary>
/// Timestamp of last update to record (computed from JDE date/time)
/// </summary>
public DateTime? LastUpdateDt => JdeDateConverter.ToDateTime(LastUpdateDate, LastUpdateTime);
}
@@ -0,0 +1,59 @@
using JdeScoping.Core.Helpers;
namespace JdeScoping.Core.Models.WorkOrders;
/// <summary>
/// F31122 work order time transaction record
/// </summary>
public class WorkOrderTime
{
/// <summary>
/// Unique ID for record
/// </summary>
public long UniqueId { get; set; }
/// <summary>
/// Unique number for work order
/// </summary>
public long WorkOrderNumber { get; set; }
/// <summary>
/// Unique code for branch
/// </summary>
public string BranchCode { get; set; } = string.Empty;
/// <summary>
/// Unique code for work center
/// </summary>
public string WorkCenterCode { get; set; } = string.Empty;
/// <summary>
/// Operation sequence number
/// </summary>
public decimal StepNumber { get; set; }
/// <summary>
/// Unique address number for user/operator
/// </summary>
public long AddressNumber { get; set; }
/// <summary>
/// G/L date entry was processed
/// </summary>
public DateTime? GlDate { get; set; }
/// <summary>
/// JDE date of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateDate { get; set; }
/// <summary>
/// JDE time of day of last update to record (private backing field for Dapper mapping)
/// </summary>
private int LastUpdateTime { get; set; }
/// <summary>
/// Timestamp of last update to record (computed from JDE date/time)
/// </summary>
public DateTime? LastUpdateDt => JdeDateConverter.ToDateTime(LastUpdateDate, LastUpdateTime);
}