Files
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 Specification - Change Delta

This document captures modifications to the base domain-models specification for the implement-domain-models change.

Base Specification

Reference: openspec/specs/domain-models/spec.md

ADDED Requirements

Requirement: JdeDateConverter helper class

The system SHALL provide a static helper class for converting JDE date/time formats to .NET DateTime.

Methods

Method Signature Description
ToDateTime DateTime? ToDateTime(int jdeDate, int jdeTime = 0) Converts JDE CYYDDD date and HHMMSS time to DateTime

Business Rules

  • Returns null for zero or invalid date values (not 1900-01-01)
  • CYYDDD format: C = century (0=1900s, 1=2000s), YY = year, DDD = day of year
  • HHMMSS format: HH = hours (0-23), MM = minutes (0-59), SS = seconds (0-59)
  • Invalid time values are ignored (date portion still returned)
  • Parse errors return null rather than throwing exceptions

Scenario: Convert valid JDE date

  • WHEN JdeDateConverter.ToDateTime(124365, 143052) is called
  • THEN returns DateTime 2024-12-30 14:30:52

Scenario: Handle zero date

  • WHEN JdeDateConverter.ToDateTime(0, 0) is called
  • THEN returns null

Scenario: Handle invalid day of year

  • WHEN JdeDateConverter.ToDateTime(124400, 0) is called (day 400 is invalid)
  • THEN returns null

Requirement: Extension method file organization

The system SHALL organize ToViewModel extension methods in separate files by entity type.

File Structure

File Extension Methods
WorkOrderExtensions.cs WorkOrder.ToViewModel()
LotExtensions.cs Lot.ToViewModel()
ItemExtensions.cs Item.ToViewModel()
WorkCenterExtensions.cs WorkCenter.ToViewModel()
ProfitCenterExtensions.cs ProfitCenter.ToViewModel()
JdeUserExtensions.cs JdeUser.ToViewModel()

Business Rules

  • Extension methods in JdeScoping.Core.Extensions namespace
  • Each file contains a single static class with extension methods for one entity type
  • Extension methods are the only way entities project to ViewModels (no methods on entities)

Scenario: Use extension method for projection

  • WHEN a WorkOrder entity calls ToViewModel() extension method
  • THEN a WorkOrderViewModel is returned with WorkOrderNumber and ItemNumber

Requirement: ViewModel file organization

The system SHALL organize ViewModel classes in a dedicated ViewModels folder.

File Structure

File ViewModel Class
WorkOrderViewModel.cs WorkOrderViewModel record
LotViewModel.cs LotViewModel record
ItemViewModel.cs ItemViewModel record
WorkCenterViewModel.cs WorkCenterViewModel record
ProfitCenterViewModel.cs ProfitCenterViewModel record
JdeUserViewModel.cs JdeUserViewModel record
PartOperationViewModel.cs PartOperationViewModel record

Business Rules

  • ViewModels in JdeScoping.Core.ViewModels namespace
  • ViewModels are immutable DTOs (prefer record type)
  • ViewModels contain only serializable properties (no computed properties)

Scenario: ViewModel serialization

  • WHEN a WorkOrderViewModel is serialized to JSON
  • THEN all properties are included in the output

Requirement: Enum file organization

The system SHALL organize enum types in a dedicated Enums folder.

File Structure

File Enum Type
SearchStatus.cs SearchStatus enum
UpdateTypes.cs UpdateTypes enum

Business Rules

  • Enums in JdeScoping.Core.Models.Enums namespace
  • All enums that may be serialized MUST have [JsonConverter(typeof(JsonStringEnumConverter))]

Scenario: Enum JSON serialization

  • WHEN SearchStatus.Ended is serialized to JSON
  • THEN the output is "Ended" (string), not 3 (integer)

MODIFIED Requirements

Requirement: Search entity

The system SHALL store user search requests containing filter criteria and resulting Excel output, with lazy deserialization of criteria from JSON.

Properties

Property Type Description
ID int Primary key identifier
UserName string Username of user who created search
Name string User-friendly name for the search
Status SearchStatus Current search status (enum)
SubmitDT DateTime? Timestamp when search was submitted
StartDT DateTime? Timestamp when search processing started
EndDT DateTime? Timestamp when search completed
CriteriaJSON string JSON-serialized search criteria
Criteria SearchCriteria Deserialized search criteria object
Results byte[]? Excel file output (VARBINARY), nullable when not yet generated

Business Rules

  • Status MUST be serialized as string using [JsonConverter(typeof(JsonStringEnumConverter))]
  • Criteria is stored as JSON in CriteriaJSON for database persistence
  • Criteria property getter deserializes from CriteriaJSON using System.Text.Json
  • Setter serializes to CriteriaJSON
  • If CriteriaJSON is null or empty, Criteria returns a new empty SearchCriteria
  • Deserialization errors return empty SearchCriteria (fail gracefully)
  • Results contains binary Excel file data only when Status = Ended
  • Results property MUST be annotated as byte[]? since it is null until processing completes

Scenario: Lazy deserialization of Criteria

  • WHEN Search.CriteriaJSON = '{"MinimumDT":"2024-01-01"}' and Criteria is accessed
  • THEN Criteria.MinimumDT = 2024-01-01

Scenario: Handle empty CriteriaJSON

  • WHEN Search.CriteriaJSON = null and Criteria is accessed
  • THEN Criteria returns new SearchCriteria() with all empty lists

Requirement: SearchUpdate entity

The system SHALL provide a real-time status update message for ASP.NET Core SignalR broadcast with factory method construction.

Properties

Property Type Description
ID int Search primary key
UserName string Username of search submitter
Name string Search name
Status SearchStatus Current status
SubmitDT DateTime? Submit timestamp
StartDT DateTime? Start timestamp
EndDT DateTime? End timestamp
Timestamp DateTime When update was generated
HasResults bool Indicates if search has Results

Business Rules

  • Primary constructor: SearchUpdate(Search search) copies all fields and sets Timestamp
  • Timestamp MUST be set to DateTime.UtcNow when update is created
  • Status MUST be serialized as string for JSON via [JsonConverter(typeof(JsonStringEnumConverter))]
  • HasResults is computed: Status == SearchStatus.Ended && search.Results != null
  • WHEN SearchUpdate is created from a Search with ID=1, Status=Ended
  • THEN SearchUpdate.ID = 1, SearchUpdate.Status = Ended, SearchUpdate.Timestamp = current UTC time

Scenario: HasResults computation

  • WHEN SearchUpdate is created from Search with Status=Ended and Results is not null
  • THEN HasResults = true

Requirement: UserInfo entity

The system SHALL provide authenticated user information with computed display name for ASP.NET Core Identity integration.

Properties

Property Type Description
Username string User's login identifier
FirstName string? User's first name (nullable)
LastName string? User's last name (nullable)
DisplayName string Computed display name
Title string? Organization title (nullable)
EmailAddress string? Email address (nullable)

Business Rules

  • DisplayName computation:
    1. If FirstName and LastName both have values: $"{FirstName} {LastName}".Trim()
    2. If only FirstName has value: FirstName.Trim()
    3. If only LastName has value: LastName.Trim()
    4. Otherwise: Username
  • "Has value" means not null and not whitespace-only
  • Used for authentication context, populated from ASP.NET Core Identity claims or LDAP provider
  • DN (Distinguished Name) property removed; use ClaimsPrincipal for identity information

Scenario: Compute display name from both names

  • WHEN UserInfo has FirstName = "John", LastName = "Doe" and DisplayName is accessed
  • THEN DisplayName = "John Doe"

Scenario: Compute display name from first name only

  • WHEN UserInfo has FirstName = "John", LastName = null and DisplayName is accessed
  • THEN DisplayName = "John"

Scenario: Fallback to username when names empty

  • WHEN UserInfo has FirstName = null, LastName = null, Username = "jdoe" and DisplayName is accessed
  • THEN DisplayName = "jdoe"

CLARIFICATIONS

Private JDE Date Fields

Entities with JDE date fields use this pattern:

public class SomeEntity
{
    // These are mapped by Dapper from database columns
    // but not exposed publicly
    private int LastUpdateDate { get; set; }
    private int LastUpdateTime { get; set; }

    // Public computed property
    public DateTime? LastUpdateDT => JdeDateConverter.ToDateTime(LastUpdateDate, LastUpdateTime);
}

The private setters allow Dapper to populate the values during query mapping, while the computed property provides the converted DateTime.

Collection Initialization

All collection properties use C# 12 collection expression syntax:

public List<string> Items { get; set; } = [];

This ensures collections are never null and always initialized to empty.

Namespace Organization

Folder Namespace
Models/ JdeScoping.Core.Models
Models/Enums/ JdeScoping.Core.Models.Enums
ViewModels/ JdeScoping.Core.ViewModels
Extensions/ JdeScoping.Core.Extensions
Interfaces/ JdeScoping.Core.Interfaces
Helpers/ JdeScoping.Core.Helpers