# Domain Models Design ## Overview This document describes the design approach for implementing domain model entities in the JDE Scoping Tool .NET 10 migration. ## Project Organization All domain models reside in the `JdeScoping.Core` project: ``` JdeScoping.Core/ ├── Models/ │ ├── Enums/ │ │ ├── SearchStatus.cs # Search processing states │ │ └── UpdateTypes.cs # Data sync frequency types │ │ │ ├── Search.cs # User search request entity │ ├── SearchCriteria.cs # Filter parameters for queries │ ├── SearchUpdate.cs # SignalR status update DTO │ │ │ ├── WorkOrder.cs # JDE work order entity │ ├── WorkOrderStep.cs # Work order operation step │ ├── WorkOrderTime.cs # F31122 time transaction │ ├── WorkOrderComponent.cs # Component usage │ ├── WorkOrderRouting.cs # Step transaction │ │ │ ├── Lot.cs # JDE lot entity │ ├── LotUsage.cs # Cardex consumption record │ ├── LotLocation.cs # Lot location tracking │ │ │ ├── Item.cs # JDE item master │ ├── WorkCenter.cs # JDE work center (IBusinessUnit) │ ├── ProfitCenter.cs # JDE profit center (IBusinessUnit) │ ├── Branch.cs # JDE branch entity │ ├── JdeUser.cs # JDE operator entity │ ├── StatusCode.cs # Work order status lookup │ ├── FunctionCode.cs # Function code lookup │ │ │ ├── DataUpdate.cs # Cache refresh tracking │ ├── OrgHierarchy.cs # Profit center to work center mapping │ ├── RouteMaster.cs # Item router master │ ├── StatusUpdate.cs # Generic process status message │ │ │ ├── MisData.cs # CMS MIS data entity │ │ │ ├── UserInfo.cs # Authenticated user info │ │ │ ├── POReceiver.cs # PO receiver record │ ├── POInspect.cs # PO inspection record │ ├── DcsLot.cs # DCS lot record │ ├── CamstarMO.cs # Camstar manufacturing order │ │ │ ├── QueryTypes.cs # Query type definitions │ ├── TableSpec.cs # Dynamic SQL table spec │ └── ColumnSpec.cs # Column specification │ ├── Interfaces/ │ └── IBusinessUnit.cs # WorkCenter/ProfitCenter interface │ ├── ViewModels/ │ ├── WorkOrderViewModel.cs # WorkOrder projection │ ├── LotViewModel.cs # Lot projection │ ├── ItemViewModel.cs # Item projection │ ├── WorkCenterViewModel.cs # WorkCenter projection │ ├── ProfitCenterViewModel.cs # ProfitCenter projection │ ├── JdeUserViewModel.cs # JdeUser projection │ └── PartOperationViewModel.cs # Item/operation/MIS combination │ ├── Extensions/ │ ├── WorkOrderExtensions.cs # WorkOrder.ToViewModel() │ ├── LotExtensions.cs # Lot.ToViewModel() │ ├── ItemExtensions.cs # Item.ToViewModel() │ ├── WorkCenterExtensions.cs # WorkCenter.ToViewModel() │ ├── ProfitCenterExtensions.cs # ProfitCenter.ToViewModel() │ └── JdeUserExtensions.cs # JdeUser.ToViewModel() │ └── Helpers/ └── JdeDateConverter.cs # JDE date/time conversion ``` ## Design Patterns ### Nullable Reference Types The project has `enable` in the .csproj. All properties follow these patterns: | Pattern | Example | Usage | |---------|---------|-------| | Required property | `public string Name { get; set; } = string.Empty;` | Non-null, initialized | | Optional property | `public string? Description { get; set; }` | Explicitly nullable | | Collection property | `public List Items { get; set; } = [];` | Never null, empty default | | Nullable byte array | `public byte[]? Results { get; set; }` | Null until populated | ### System.Text.Json Serialization All enums that may be serialized use the string converter: ```csharp using System.Text.Json.Serialization; [JsonConverter(typeof(JsonStringEnumConverter))] public enum SearchStatus { New = 0, Submitted = 1, Started = 2, Ended = 3, Error = 4 } ``` This ensures JSON output is `"Status": "Ended"` instead of `"Status": 3`. ### JDE Date Conversion Pattern JDE stores dates as integers in CYYDDD format (century + year + day of year) and times as HHMMSS: ```csharp public static class JdeDateConverter { /// /// Converts JDE date (CYYDDD) and time (HHMMSS) to DateTime. /// Returns null for zero or invalid values (changed from legacy 1900-01-01). /// public static DateTime? ToDateTime(int jdeDate, int jdeTime = 0) { if (jdeDate <= 0) return null; try { // CYYDDD format: C = century (0=1900s, 1=2000s), YY = year, DDD = day of year int century = jdeDate / 100000; int yearInCentury = (jdeDate / 1000) % 100; int dayOfYear = jdeDate % 1000; int year = 1900 + (century * 100) + yearInCentury; if (dayOfYear < 1 || dayOfYear > 366) return null; var date = new DateTime(year, 1, 1).AddDays(dayOfYear - 1); // Add time component if provided if (jdeTime > 0) { int hours = jdeTime / 10000; int minutes = (jdeTime / 100) % 100; int seconds = jdeTime % 100; if (hours >= 0 && hours < 24 && minutes >= 0 && minutes < 60 && seconds >= 0 && seconds < 60) { date = date.AddHours(hours).AddMinutes(minutes).AddSeconds(seconds); } } return date; } catch { return null; } } } ``` ### Entity Computed Properties Entities with JDE date fields use private backing fields and computed public properties: ```csharp public class WorkOrder { // Private backing fields (mapped from database via Dapper) private int LastUpdateDate { get; set; } private int LastUpdateTime { get; set; } // Public computed property public DateTime? LastUpdateDT => JdeDateConverter.ToDateTime(LastUpdateDate, LastUpdateTime); // Other properties... public long WorkOrderNumber { get; set; } public string ItemNumber { get; set; } = string.Empty; } ``` ### ToViewModel Extension Methods Projections are implemented as extension methods for separation of concerns: ```csharp public static class WorkOrderExtensions { public static WorkOrderViewModel ToViewModel(this WorkOrder workOrder) { return new WorkOrderViewModel { WorkOrderNumber = workOrder.WorkOrderNumber, ItemNumber = workOrder.ItemNumber }; } } ``` ### IBusinessUnit Interface WorkCenter and ProfitCenter share a common interface: ```csharp public interface IBusinessUnit { string Code { get; } string Description { get; } DateTime? LastUpdateDT { get; } } public class WorkCenter : IBusinessUnit { public string Code { get; set; } = string.Empty; public string Description { get; set; } = string.Empty; private int LastUpdateDate { get; set; } private int LastUpdateTime { get; set; } public DateTime? LastUpdateDT => JdeDateConverter.ToDateTime(LastUpdateDate, LastUpdateTime); } ``` ## Classes vs Records | Type | Pattern | Rationale | |------|---------|-----------| | Database entities | `class` | Mutable for Dapper mapping, private setters for computed fields | | ViewModels | `record` or `class` | Immutable DTOs, but class is fine for simple projections | | SearchUpdate | `class` with init | Constructor sets Timestamp to UtcNow | | Enums | `enum` | Standard enumeration with JsonStringEnumConverter | ## Existing Code Reconciliation The existing `JdeScoping.Core/Models/` contains placeholder implementations that differ from the spec: | Existing File | Action | Notes | |---------------|--------|-------| | Search.cs | Replace | Different property names, missing CriteriaJSON/Criteria pattern | | WorkOrder.cs | Replace | Simplified placeholder, missing JDE-specific fields | | Item.cs | Replace | Missing ShortItemNumber, nullable annotations | | Lot.cs | Replace | Placeholder implementation | | LotUsage.cs | Replace | Placeholder implementation | | WorkCenter.cs | Replace | Missing IBusinessUnit interface | | JdeUser.cs | Replace | Placeholder implementation | ## Migration from Legacy | Legacy Pattern | New Pattern | Rationale | |----------------|-------------|-----------| | `DataModel.Models` namespace | `JdeScoping.Core.Models` | .NET naming conventions | | Newtonsoft.Json attributes | System.Text.Json attributes | Built-in .NET serialization | | `[JsonConverter(typeof(StringEnumConverter))]` | `[JsonConverter(typeof(JsonStringEnumConverter))]` | System.Text.Json | | `LDAPEntry` class | `UserInfo` class | Modern auth pattern naming | | Invalid JDE dates -> 1900-01-01 | Invalid JDE dates -> null | Explicit null handling | | ToViewModel() on entity | Extension method | Separation of concerns | ## File Structure Summary After implementation, the Models folder structure: ``` JdeScoping.Core/Models/ ├── Enums/ (2 files) ├── [Entity classes] (27 files) ├── Interfaces/ (1 file - moved from Models) ├── ViewModels/ (7 files) ├── Extensions/ (6 files) └── Helpers/ (1 file) ``` Total: ~44 new/modified files in JdeScoping.Core