Set up repository with legacy .NET Framework 4.8 source (OLD/), new .NET 10 Blazor solution (NEW/), OpenSpec specifications, documentation, and project configuration.
41 KiB
Domain Models Specification
Purpose
The domain model layer defines the core business entities used throughout the JDE Scoping Tool application, targeting .NET 10 with modern C# patterns. These models represent manufacturing/ERP data from JD Edwards (JDE) and CMS (Sybase) enterprise systems, cached locally in SQL Server for efficient searching. The models serve three primary purposes:
- Entity representation - Map database tables to strongly-typed C# classes with nullable reference type annotations
- Search criteria - Define filter parameters for complex manufacturing queries
- Data transfer - Support serialization for API communication via System.Text.Json and SignalR
Source Reference
| Legacy Files | Purpose |
|---|---|
| OLD/DataModel/Models/Search.cs | User search request with criteria and results |
| OLD/DataModel/Models/SearchCriteria.cs | JDE data filter criteria definition |
| OLD/DataModel/Models/SearchStatus.cs | Search status enumeration |
| OLD/DataModel/Models/SearchUpdate.cs | SignalR status update message |
| OLD/DataModel/Models/WorkOrder.cs | JDE work order entity |
| OLD/DataModel/Models/Lot.cs | JDE lot entity |
| OLD/DataModel/Models/LotUsage.cs | Cardex entry (lot consumption record) |
| OLD/DataModel/Models/LotLocation.cs | JDE lot location entity |
| OLD/DataModel/Models/Item.cs | JDE item (part type) entity |
| OLD/DataModel/Models/WorkCenter.cs | JDE work center entity |
| OLD/DataModel/Models/ProfitCenter.cs | JDE profit center entity |
| OLD/DataModel/Models/JdeUser.cs | JDE user (operator) entity |
| OLD/DataModel/Models/Branch.cs | JDE branch entity |
| OLD/DataModel/Models/WorkOrderStep.cs | JDE work order step entity |
| OLD/DataModel/Models/WorkOrderTime.cs | F31122 work order time transaction |
| OLD/DataModel/Models/WorkOrderRouting.cs | Work order step transaction |
| OLD/DataModel/Models/WorkOrderComponent.cs | Work order component usage |
| OLD/DataModel/Models/DataUpdate.cs | Cache data update tracking |
| OLD/DataModel/Models/StatusUpdate.cs | Process status update message |
| OLD/DataModel/Models/StatusCode.cs | JDE work order status code |
| OLD/DataModel/Models/FunctionCode.cs | JDE function code |
| OLD/DataModel/Models/OrgHierarchy.cs | Profit center to work center mapping |
| OLD/DataModel/Models/RouteMaster.cs | JDE item router master |
| OLD/DataModel/Models/MisData.cs | CMS MIS data entity |
| OLD/DataModel/Models/POReceiver.cs | JDE PO receiver record |
| OLD/DataModel/Models/POInspect.cs | JDE PO inspect record |
| OLD/DataModel/Models/DcsLot.cs | DCS lot record |
| OLD/DataModel/Models/CamstarMO.cs | Camstar manufacturing order |
| OLD/DataModel/Models/LDAPEntry.cs | LDAP search result (renamed to UserInfo) |
| OLD/DataModel/Models/IBusinessUnit.cs | Business unit interface |
| OLD/DataModel/Models/QueryTypes.cs | Query type definitions |
| OLD/DataModel/Models/TableSpec.cs | Database table specification |
| OLD/DataModel/Models/ColumnSpec.cs | Database column specification |
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
CriteriaJSONfor database persistence Criteriaproperty getter deserializes fromCriteriaJSONusing System.Text.Json- Setter serializes to
CriteriaJSON - If
CriteriaJSONis null or empty,Criteriareturns a new emptySearchCriteria - 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: SearchCriteria entity
The system SHALL provide filter parameters for querying JDE/CMS data.
Properties
| Property | Type | Description |
|---|---|---|
| MinimumDT | DateTime? | Minimum timestamp to include |
| MaximumDT | DateTime? | Maximum timestamp to include |
| WorkOrderNumbers | List<long> | Work order numbers to filter |
| ItemNumbers | List<string> | Item numbers to filter |
| ProfitCenters | List<string> | Profit center codes to filter |
| WorkCenters | List<string> | Work center codes to filter |
| OperatorIDs | List<string> | Operator IDs to filter |
| ComponentLotNumbers | List<LotViewModel> | Component lot numbers to filter |
| ExtractMisData | bool | Whether to extract MIS data |
| PartOperations | List<PartOperationViewModel> | Part/operation combinations for MIS filtering |
Business Rules
- All list properties MUST be initialized to empty lists in constructor
- Note: Legacy has no validation - filter criteria can all be empty (validation is application-layer decision)
- ComponentLotNumbers references
LotViewModel(lot number + item number pair) - PartOperations references
PartOperationViewModel(item number + operation number + MIS number + revision)
Scenario: Filter by work order numbers
- WHEN search criteria has WorkOrderNumbers = [123456, 789012] and search executes
- THEN only records matching those work order numbers are returned
Scenario: Filter by date range
- WHEN search criteria has MinimumDT = 2024-01-01 and MaximumDT = 2024-12-31 and search executes
- THEN only records within that date range are returned
Requirement: SearchStatus enumeration
The system SHALL provide an enumeration of search processing states.
Values
| Value | Code | Description |
|---|---|---|
| New | 0 | Search created but not submitted |
| Submitted | 1 | Search queued for processing |
| Started | 2 | Search processing in progress |
| Ended | 3 | Search completed successfully |
| Error | 4 | Search failed with error |
Business Rules
- Status transitions follow: New -> Submitted -> Started -> (Ended | Error)
- MUST be serialized as string in JSON (not integer) using
[JsonConverter(typeof(JsonStringEnumConverter))]
Example: System.Text.Json enum serialization
using System.Text.Json.Serialization;
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum SearchStatus
{
New = 0,
Submitted = 1,
Started = 2,
Ended = 3,
Error = 4
}
Scenario: Valid status transition
- WHEN a search has Status = Submitted and processing begins
- THEN Status can transition to Started
Scenario: Serialize status as string
- WHEN a search has Status = Ended and is serialized to JSON
- THEN status appears as "Ended" (string), not 3 (integer)
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.UtcNowwhen update is created - Status MUST be serialized as string for JSON via
[JsonConverter(typeof(JsonStringEnumConverter))] HasResultsis computed:Status == SearchStatus.Ended && search.Results != null
Scenario: Create SearchUpdate from Search
- 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: WorkOrder entity
The system SHALL provide a JDE work order entity representing a manufacturing order.
Properties
| Property | Type | Description |
|---|---|---|
| WorkOrderNumber | long | Unique work order identifier |
| BranchCode | string | Branch/plant code |
| LotNumber | string? | Assigned lot number (nullable) |
| ItemNumber | string | Product item number |
| ShortItemNumber | long | Numeric item identifier |
| ParentWorkOrderNumber | string? | Parent work order (if sub-assembly, nullable) |
| OrderQuantity | decimal | Quantity ordered |
| HeldQuantity | decimal | Quantity on hold |
| ShippedQuantity | decimal | Quantity shipped |
| StatusCode | string | Work order status |
| StatusCodeUpdateDT | DateTime? | Last status update timestamp |
| IssueDate | DateTime | Date work order was issued |
| StartDate | DateTime | Date work order was started |
| RoutingType | string | Routing type code |
| LastUpdateDT | DateTime | Computed from JDE date/time fields |
Relationships
- References
Itemvia ItemNumber/ShortItemNumber - References
Branchvia BranchCode - References
StatusCodevia StatusCode - Parent-child relationship via ParentWorkOrderNumber
- Has many
WorkOrderSteprecords - Has many
WorkOrderTimerecords - Has many
WorkOrderComponentrecords - Has many
LotUsagerecords
Business Rules
- LastUpdateDT is computed property using JDE date conversion helpers
- LastUpdateDate and LastUpdateTime are private (JDE-specific format)
- ToViewModel() projects to WorkOrderViewModel (WorkOrderNumber, ItemNumber)
- LotNumber and ParentWorkOrderNumber SHOULD be annotated as nullable (
string?)
Scenario: Create work order from JDE data
- WHEN JDE data has LastUpdateDate = 124365 and LastUpdateTime = 143052 and is mapped to WorkOrder entity
- THEN LastUpdateDT = 2024-12-30 14:30:52
Scenario: Navigate to parent work order
- WHEN work order 12345 has ParentWorkOrderNumber = "11111" and the parent-child relationship is traversed
- THEN the parent work order 11111 is accessible
Scenario: Project to ViewModel
- WHEN a WorkOrder with WorkOrderNumber = 12345, ItemNumber = "ABC123" calls ToViewModel()
- THEN WorkOrderViewModel contains only WorkOrderNumber and ItemNumber
Requirement: Lot entity
The system SHALL provide a JDE lot entity representing a tracked batch of materials.
Properties
| Property | Type | Description |
|---|---|---|
| LotNumber | string | Unique lot identifier |
| BranchCode | string | Business unit code |
| ShortItemNumber | long | Numeric item identifier |
| ItemNumber | string | Item number |
| SupplierCode | long | Supplier address number |
| StatusCode | char | Single-character lot status |
| Memo1 | string? | Memo line 1 (nullable) |
| Memo2 | string? | Memo line 2 (nullable) |
| Memo3 | string? | Memo line 3 (nullable) |
| LastUpdateDT | DateTime | Computed from JDE date/time |
Relationships
- References
Itemvia ItemNumber/ShortItemNumber - References
Branchvia BranchCode - Has many
LotLocationrecords - Has many
LotUsagerecords
Business Rules
- StatusCode is single character (not string)
- ToViewModel() projects to LotViewModel (LotNumber, ItemNumber)
- Memo fields SHOULD be annotated as nullable (
string?)
Scenario: Lot with single-character status
- WHEN a lot has StatusCode = 'A' (active) and the lot entity is read
- THEN StatusCode is char type, not string
Scenario: Project lot to ViewModel
- WHEN a Lot with LotNumber = "LOT001", ItemNumber = "ITEM123" calls ToViewModel()
- THEN LotViewModel contains LotNumber and ItemNumber only
Requirement: LotUsage entity
The system SHALL provide a cardex entry recording component consumption in work orders.
Properties
| Property | Type | Description |
|---|---|---|
| UniqueID | long | Primary key identifier |
| WorkOrderNumber | long | Associated work order |
| LotNumber | string | Component lot number |
| BranchCode | string | Branch code |
| ShortItemNumber | long | Component item number |
| Quantity | decimal | Transaction quantity |
| LastUpdateDT | DateTime | Computed from JDE date/time |
Relationships
- References
WorkOrdervia WorkOrderNumber - References
Lotvia LotNumber - References
Itemvia ShortItemNumber
Requirement: LotLocation entity
The system SHALL provide JDE lot location tracking.
Properties
| Property | Type | Description |
|---|---|---|
| LotNumber | string | Lot identifier |
| ShortItemNumber | long | Item identifier |
| BranchCode | string | Business unit code |
| Location | string | Physical location code |
| LastUpdateDT | DateTime | Last update timestamp |
Relationships
- References
Lotvia LotNumber - References
Itemvia ShortItemNumber - References
Branchvia BranchCode
Requirement: Item entity
The system SHALL provide a JDE item (part type) master entity.
Properties
| Property | Type | Description |
|---|---|---|
| ShortItemNumber | long | Unique numeric identifier |
| ItemNumber | string | Alphanumeric item number |
| Description | string | Item description |
| PlanningFamily | string? | Master planning family (nullable) |
| StockingType | string? | Stocking type code (nullable) |
| LastUpdateDT | DateTime | Computed from JDE date/time |
Business Rules
- ShortItemNumber is the numeric key used in JDE joins
- ItemNumber is the human-readable identifier
- ToViewModel() projects to ItemViewModel (ItemNumber, Description)
Scenario: Dual identifier pattern
- WHEN an item has ShortItemNumber = 12345 and ItemNumber = "ABC-123" and joins with work orders
- THEN ShortItemNumber is used for database joins and ItemNumber is displayed to users
Requirement: WorkCenter entity
The system SHALL provide a JDE work center entity (implements IBusinessUnit).
Properties
| Property | Type | Description |
|---|---|---|
| Code | string | Unique work center code |
| Description | string | Work center description |
| LastUpdateDT | DateTime | Computed from JDE date/time |
Business Rules
- Implements
IBusinessUnitinterface - ToViewModel() projects to WorkCenterViewModel (Code, Description)
Requirement: ProfitCenter entity
The system SHALL provide a JDE profit center entity (implements IBusinessUnit).
Properties
| Property | Type | Description |
|---|---|---|
| Code | string | Unique profit center code |
| Description | string | Profit center description |
| LastUpdateDT | DateTime | Computed from JDE date/time |
Business Rules
- Implements
IBusinessUnitinterface - ToViewModel() projects to ProfitCenterViewModel (Code, Description)
Requirement: JdeUser entity
The system SHALL provide a JDE user (operator) entity.
Properties
| Property | Type | Description |
|---|---|---|
| AddressNumber | long | Unique address number |
| UserID | string | Login identifier |
| FullName | string | Full name (last, first [middle]) |
| LastUpdateDT | DateTime | Computed from JDE date/time |
Business Rules
- AddressNumber is the JDE primary key
- UserID is the login identifier
- ToViewModel() projects to JdeUserViewModel (AddressNumber, UserID, FullName)
Requirement: Branch entity
The system SHALL provide a JDE branch entity.
Properties
| Property | Type | Description |
|---|---|---|
| Code | string | Unique branch code |
| Description | string | Branch description |
| LastUpdateDT | DateTime | Computed from JDE date/time |
Requirement: IBusinessUnit interface
The system SHALL provide an interface for business unit entities (WorkCenter, ProfitCenter).
Properties
| Property | Type | Description |
|---|---|---|
| Code | string | Business unit unique code |
| Description | string | Business unit description |
| LastUpdateDT | DateTime | Last update timestamp |
Requirement: WorkOrderStep entity
The system SHALL provide a JDE work order operation step.
Properties
| Property | Type | Description |
|---|---|---|
| WorkOrderNumber | long | Work order identifier |
| BranchCode | string | Branch code |
| WorkCenterCode | string | Work center code |
| StepNumber | decimal | Operation sequence number |
| StepDescription | string? | Step description (nullable) |
| FunctionOperationDescription | string? | Long text description (nullable) |
| StepTypeCode | string | Operation type |
| StartDT | DateTime? | Step start timestamp |
| EndDT | DateTime? | Step end timestamp |
| FunctionCode | string | Operation function code |
| ScrappedQuantity | decimal | Quantity scrapped/cancelled |
| LastUpdateDT | DateTime | Computed from JDE date/time |
Relationships
- References
WorkOrdervia WorkOrderNumber - References
WorkCentervia WorkCenterCode - References
FunctionCodevia FunctionCode
Requirement: WorkOrderTime entity
The system SHALL provide an F31122 work order time transaction record.
Properties
| Property | Type | Description |
|---|---|---|
| UniqueID | long | Primary key |
| WorkOrderNumber | long | Work order identifier |
| BranchCode | string | Branch code |
| WorkCenterCode | string | Work center code |
| StepNumber | decimal | Operation sequence |
| AddressNumber | long | Operator address number |
| GlDate | DateTime? | G/L processing date |
| LastUpdateDT | DateTime | Computed from JDE date/time |
Relationships
- References
WorkOrdervia WorkOrderNumber - References
JdeUservia AddressNumber - References
WorkCentervia WorkCenterCode
Requirement: WorkOrderRouting entity
The system SHALL provide a work order step transaction model.
Properties
| Property | Type | Description |
|---|---|---|
| UserID | string | Transaction user ID |
| BatchNumber | string | Transaction batch number |
| TransactionNumber | string | Transaction number |
| LineNumber | int | Transaction line number |
| StepNumber | decimal | Operation sequence |
| WorkCenterCode | string | Work center code |
| WorkOrderNumber | long | Work order identifier |
| RoutingType | string | Routing type |
| BranchCode | string | Branch code |
| StepDescription | string? | Step description (nullable) |
| FunctionCode | string | Function code |
| TransactionDate | DateTime | Transaction original date |
| LastUpdateDT | DateTime | Computed from JDE date/time |
Requirement: WorkOrderComponent entity
The system SHALL provide a work order component usage model.
Properties
| Property | Type | Description |
|---|---|---|
| UniqueID | long | Primary key |
| WorkOrderNumber | long | Work order identifier |
| LotNumber | string? | Component lot number (nullable) |
| BranchCode | string | Branch code |
| ShortItemNumber | long? | Component item (nullable) |
| Quantity | decimal | Transaction quantity |
| LastUpdateDT | DateTime | Computed from JDE date/time |
Business Rules
- ShortItemNumber is nullable (unlike LotUsage)
- LotNumber SHOULD be annotated as nullable (
string?)
Requirement: DataUpdate entity
The system SHALL provide a cache data update tracking entity.
Properties
| Property | Type | Description |
|---|---|---|
| ID | int | Primary key |
| SourceSystem | string | Source system name (JDE/CMS) |
| SourceData | string | Source data type |
| TableName | string | Cache table name |
| StartDT | DateTime | Update start timestamp |
| EndDT | DateTime | Update end timestamp |
| UpdateType | UpdateTypes | Type of update (enum) |
| WasSuccessful | bool | Success indicator |
| NumberRecords | long | Record count |
Business Rules
- UpdateTypes enum: Hourly (1), Daily (2), Mass (3)
- Used to track cache refresh status and timing
- UpdateType MUST use
[JsonConverter(typeof(JsonStringEnumConverter))]if serialized
Scenario: Track successful daily update
- WHEN a daily sync of WorkOrder table completes and DataUpdate is created
- THEN SourceSystem = "JDE", UpdateType = Daily (2), WasSuccessful = true
Scenario: Track failed mass update
- WHEN a mass refresh fails with error and DataUpdate is created
- THEN WasSuccessful = false, NumberRecords = 0
Requirement: UpdateTypes enumeration
The system SHALL provide an enumeration for data update frequency types.
Values
| Value | Code | Description |
|---|---|---|
| Hourly | 1 | Hourly incremental update |
| Daily | 2 | Daily incremental update |
| Mass | 3 | Full data refresh |
Business Rules
- SHOULD use
[JsonConverter(typeof(JsonStringEnumConverter))]if serialized to JSON
Requirement: StatusUpdate entity
The system SHALL provide a generic process status update message (not search-specific).
Properties
| Property | Type | Description |
|---|---|---|
| Message | string | Update message text |
| Timestamp | DateTime | Message timestamp |
Requirement: StatusCode entity
The system SHALL provide a JDE work order status code lookup.
Properties
| Property | Type | Description |
|---|---|---|
| Code | string | Unique status code |
| Description | string | Status description |
| LastUpdateDT | DateTime | Computed from JDE date/time |
Requirement: FunctionCode entity
The system SHALL provide a JDE function code lookup.
Properties
| Property | Type | Description |
|---|---|---|
| Code | string | Unique function code |
| Description | string | Function description |
| LastUpdateDT | DateTime | Last update timestamp |
Requirement: OrgHierarchy entity
The system SHALL provide organization hierarchy mapping (profit center to work center).
Properties
| Property | Type | Description |
|---|---|---|
| WorkCenterCode | string | Work center code |
| BranchCode | string | Branch unit code |
| ProfitCenterCode | string | Profit center code |
| LastUpdateDT | DateTime | Computed from JDE date/time |
Relationships
- Maps
WorkCentertoProfitCenter - References
Branchvia BranchCode
Requirement: RouteMaster entity
The system SHALL provide a JDE item router master entity.
Properties
| Property | Type | Description |
|---|---|---|
| BranchCode | string | Branch code |
| ItemNumber | string | Item number |
| RoutingType | string | Router type |
| SequenceNumber | decimal | Job step number |
| FunctionCode | string | Function code |
| WorkCenterCode | string | Work center code |
| StartDate | DateTime | Effectivity start date |
| EndDate | DateTime? | Effectivity end date (nullable) |
| LastUpdateDT | DateTime | Computed from JDE date/time |
Business Rules
- StartDate_Date and EndDate_Date store JDE integer format
- StartDate and EndDate are computed properties using JDE conversion
Requirement: MisData entity
The system SHALL provide a CMS MIS (Manufacturing Information System) data entity.
Properties
| Property | Type | Description |
|---|---|---|
| ItemNumber | string | Item number |
| BranchCode | string | Branch code |
| SequenceNumber | string | Operation job step number |
| MisNumber | string | MIS unique number |
| RevID | string? | MIS revision ID (nullable) |
| CharNumber | string | Characteristic number |
| TestDescription | string? | Test description (nullable) |
| SamplingType | string? | Type of sampling (nullable) |
| SamplingValue | string? | Sampling selection value (nullable) |
| ToolsGauges | string? | Tools and gauges (nullable) |
| WorkInstructions | string? | Work instructions (nullable) |
| Status | string | Release status |
| ReleaseDate | DateTime? | Release date |
Business Rules
- Sourced from CMS (Sybase), not JDE (Oracle)
- Only extracted when SearchCriteria.ExtractMisData = true
- Many string fields SHOULD be annotated as nullable (
string?)
Scenario: Extract MIS data when requested
- WHEN SearchCriteria has ExtractMisData = true and search executes
- THEN MisData records are fetched from CMS Sybase
Scenario: Skip MIS data when not requested
- WHEN SearchCriteria has ExtractMisData = false and search executes
- THEN MisData query is skipped (performance optimization)
Requirement: POReceiver entity
The system SHALL provide a JDE purchase order receiver record.
Properties
| Property | Type | Description |
|---|---|---|
| OrderNumber | long | PO number |
| OrderCompany | string | PO company code |
| OrderSuffix | string | PO suffix |
| LineNumber | decimal | Line number |
| NumberOfLines | int | Total lines in PO |
| InvoiceNumber | long | Invoice number |
| BranchCode | string | Receiving site |
| LotNumber | string? | Product lot number (nullable) |
| ShortItemNumber | string | Item number (string - legacy type) |
| DateReceived | DateTime | Receipt date |
| Subledger | string? | Subledger name (nullable) |
| QtyReceived | decimal | Quantity received |
| LastUpdateDT | DateTime | Last update timestamp |
Requirement: POInspect entity
The system SHALL provide a JDE purchase order inspection record.
Properties
| Property | Type | Description |
|---|---|---|
| UniqueID | long | Primary key |
| OrderNumber | long | PO number |
| OrderCompany | string | Company code |
| LineNumber | decimal | Line number |
| InvoiceNumber | long | Invoice number |
| LotNumber | string? | Lot number (nullable) |
| ShortItemNumber | string | Item number |
| LastUpdateDT | DateTime | Last update timestamp |
Requirement: DcsLot entity
The system SHALL provide a DCS lot record entity.
Properties
| Property | Type | Description |
|---|---|---|
| ItemNumber | string | Item number |
| LotNumber | string | Lot number |
| LotSuffix | string? | Lot suffix (nullable) |
| LastUpdateDT | DateTime | Last update timestamp |
Requirement: CamstarMO entity
The system SHALL provide a Camstar manufacturing order entity.
Properties
| Property | Type | Description |
|---|---|---|
| MONumber | string | Manufacturing order number |
| LastUpdateDT | DateTime | Last update timestamp |
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:
- If FirstName and LastName both have values:
$"{FirstName} {LastName}".Trim() - If only FirstName has value:
FirstName.Trim() - If only LastName has value:
LastName.Trim() - Otherwise:
Username
- If FirstName and LastName both have values:
- "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"
Requirement: QueryTypes entity
The system SHALL provide query type definitions for search filtering.
Properties
| Property | Type | Description |
|---|---|---|
| Code | string | Query type code |
| Name | string | Query type name |
| OrderIndex | int | Display order |
| TimeSpanFilter | bool | Supports date range filter |
| WorkOrderFilter | bool | Supports work order filter |
| ItemNumberFilter | bool | Supports item filter |
| ProfitCenterFilter | bool | Supports profit center filter |
| WorkCenterFilter | bool | Supports work center filter |
| ComponentLotFilter | bool | Supports lot filter |
| OperatorFilter | bool | Supports operator filter |
| ItemOperationMISFilter | bool | Supports item/operation MIS filter |
| ExtractMISFilter | bool | Supports MIS extraction |
| ReceivedItemNumberIISFilter | bool | Supports received item filter |
Business Rules
- Uses static dictionary for type registration
- Identify() method maps SearchCriteria to QueryTypes (stub implementation)
- Predefined type: WorkOrder with WorkOrderFilter = true
Requirement: TableSpec entity
The system SHALL provide a database table specification for dynamic SQL generation.
Properties
| Property | Type | Description |
|---|---|---|
| Name | string | Table name |
| TempTableName | string | Computed: #{Name} |
| Columns | List<ColumnSpec> | Table columns |
| PrimaryKey | List<ColumnSpec> | Primary key columns |
Business Rules
- Constructor initializes Columns and PrimaryKey to empty lists
- Stub methods: GenerateIndex(), GenerateDrop(), GenerateCreate(), GetColumn()
Requirement: ColumnSpec entity
The system SHALL provide a database column specification.
Properties
| Property | Type | Description |
|---|---|---|
| Name | string | Column name |
| Definition | string | Column definition |
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
nullfor zero or invalid date values (not1900-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
nullrather 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.Extensionsnamespace - 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.ViewModelsnamespace - ViewModels are immutable DTOs (prefer
recordtype) - 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.Enumsnamespace - 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)
Nullable Reference Types
The .NET 10 project MUST enable nullable reference types in the project file:
<PropertyGroup>
<Nullable>enable</Nullable>
</PropertyGroup>
Nullable Annotation Guidelines
| Pattern | Example | Usage |
|---|---|---|
| Required property | string Name { get; set; } |
Non-null, must be initialized |
| Optional property | string? Description { get; set; } |
Can be null |
| Required with default | string Name { get; set; } = string.Empty; |
Non-null with safe default |
| Collection property | List<string> Items { get; set; } = []; |
Never null, empty by default |
Properties Requiring Nullable Annotation
The following properties SHOULD be annotated as nullable based on business rules:
Search.Results->byte[]?WorkOrder.LotNumber,ParentWorkOrderNumber->string?Lot.Memo1,Memo2,Memo3->string?Item.PlanningFamily,StockingType->string?WorkOrderStep.StepDescription,FunctionOperationDescription->string?WorkOrderRouting.StepDescription->string?WorkOrderComponent.LotNumber->string?MisDataoptional fields ->string?UserInfo.FirstName,LastName,Title,EmailAddress->string?
JDE Date/Time Conversion Pattern
Many JDE entities store dates and times in integer format that must be converted:
JDE Date Format
- Integer format: CYYDDD (century + year + day of year)
- Century: 0 = 1900s, 1 = 2000s
- Example: 124365 = December 30, 2024 (1 + 24 years + 365th day)
JDE Time Format
- Integer format: HHMMSS
- Example: 143052 = 14:30:52
Edge Cases
- Invalid/Zero dates: Return
nullfor 0 or invalid date values (changed from legacy1900-01-01) - Parse errors: Return
nullrather than silently swallowing errors - Backing field access: Raw JDE integer values stored in private fields (e.g.,
TransactionDate_Date)
Implementation Pattern
// Private backing fields (mapped from database)
private int LastUpdateDate { get; }
private int LastUpdateTime { get; }
// Public computed property (nullable for invalid dates)
public DateTime? LastUpdateDT => JdeDateConverter.ToDateTime(LastUpdateDate, LastUpdateTime);
ToViewModel Extension Methods
The ToViewModel() pattern from the legacy codebase is preserved using extension methods:
public static class WorkOrderExtensions
{
public static WorkOrderViewModel ToViewModel(this WorkOrder workOrder)
{
return new WorkOrderViewModel
{
WorkOrderNumber = workOrder.WorkOrderNumber,
ItemNumber = workOrder.ItemNumber
};
}
}
Alternative Mapping Approaches
For larger mapping scenarios, consider using a mapping library:
- AutoMapper: Convention-based mapping with profiles
- Mapster: Faster alternative with code generation support
The extension method pattern is preferred for simple projections to maintain explicit, testable code.
Migration Notes
| Legacy Pattern | New Pattern | Rationale |
|---|---|---|
DataModel.Models namespace |
JdeScoping.Core.Models |
.NET naming conventions |
| Newtonsoft.Json | System.Text.Json | Built-in .NET serialization |
[JsonConverter(typeof(StringEnumConverter))] |
[JsonConverter(typeof(JsonStringEnumConverter))] |
System.Text.Json enum converter |
LDAPEntry |
UserInfo |
Renamed for modern auth patterns |
<Nullable>disable</Nullable> |
<Nullable>enable</Nullable> |
Improved null safety |
| Forms Authentication models | ASP.NET Core cookie authentication | Modern auth patterns |
| Private setters for JDE fields | Private backing fields | Maintain encapsulation |
| Extension methods for JDE conversion | Static helper class JdeDateConverter |
Cleaner separation |
List<T> for collections |
Consider IReadOnlyList<T> for immutability |
Defensive design |
| ToViewModel() methods on entities | Extension methods in separate file | Separation of concerns |
IBusinessUnit interface |
Consider sealed classes with shared base | Modern C# patterns |
JDE invalid dates return 1900-01-01 |
Return null for invalid dates |
Explicit null handling |
| SignalR (OWIN-based) | ASP.NET Core SignalR | Modern SignalR implementation |
Resolved Design Decisions
The following questions from the legacy analysis have been resolved:
| Question | Decision | Rationale |
|---|---|---|
| Search.Results storage | Keep VARBINARY storage | Blob storage adds infrastructure complexity; VARBINARY is sufficient for Excel files |
| SearchCriteria validation | Keep validation at service layer | Domain models remain clean; validation is application concern |
| JDE date conversion | Use static helper class | Dapper type handlers are fragile; explicit conversion is more testable |
| Entity vs DTO separation | Use ToViewModel extension methods | Provides separation without class explosion |
| QueryTypes.Identify() | Remove stub; implement at service layer | Query type determination belongs in business logic |
| TableSpec/ColumnSpec | Keep for now | May be needed for dynamic query generation |
| MisData source | Continue CMS integration | Active business requirement |
| POReceiver.ShortItemNumber as string | Preserve as string | Intentional legacy typing; document as known quirk |
| LDAPEntry rename | Rename to UserInfo |
Supports both LDAP and other identity providers |
| CamstarMO/DcsLot status | Keep but mark as optional | May be deprecated; implementation can skip if not needed |
| Status transition enforcement | Keep simple enum | Add state machine if needed at service layer |
| SearchUpdate.Timestamp default | Always set in constructor | Use primary constructor or init required |
| JDE invalid date handling | Return null |
Explicit null is clearer than magic date |
| JDE backing fields | Expose only DateTime properties | Consumers don't need raw JDE integers |
Codex Review Findings
The following issues were identified during code review and should be addressed:
SearchCriteria Validation Gap
- Issue: No validation exists - all criteria lists can be empty
- Impact: Users could submit searches with no filters, causing full table scans
- Recommendation: Add service-layer validation requiring at least one filter criterion
JDE Date Edge Cases
- Issue: Legacy silently returns
1900-01-01for invalid dates - Impact: Difficult to distinguish invalid data from legitimate old dates
- Recommendation: Return
nullfor invalid JDE dates; update consuming code to handle nulls
POReceiver Type Inconsistency
- Issue:
ShortItemNumberisstringin POReceiver butlongelsewhere - Impact: Potential join/comparison issues
- Recommendation: Document as intentional legacy behavior; add explicit conversion if needed
QueryTypes Incomplete Implementation
- Issue:
Identify()method is stubbed with no implementation - Impact: Query type detection doesn't work
- Recommendation: Implement at service layer based on populated criteria fields