Files
jdescopingtool/openspec/changes/archive/2026-01-01-implement-domain-models/design.md
T
Joseph Doherty 26ff8d9b4f 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.
2026-01-02 07:43:29 -05:00

10 KiB

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 <Nullable>enable</Nullable> 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<string> 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:

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:

public static class JdeDateConverter
{
    /// <summary>
    /// Converts JDE date (CYYDDD) and time (HHMMSS) to DateTime.
    /// Returns null for zero or invalid values (changed from legacy 1900-01-01).
    /// </summary>
    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:

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:

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:

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