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:
@@ -0,0 +1,277 @@
|
||||
# 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:
|
||||
|
||||
```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
|
||||
{
|
||||
/// <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:
|
||||
|
||||
```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
|
||||
@@ -0,0 +1,68 @@
|
||||
# Implement Domain Models
|
||||
|
||||
## Summary
|
||||
|
||||
Implement all domain model entities for the JDE Scoping Tool as defined in the domain-models specification. This establishes the core business entity layer for the .NET 10 migration, providing strongly-typed classes for JDE/CMS data representation, search criteria, and data transfer.
|
||||
|
||||
## Scope
|
||||
|
||||
### In Scope
|
||||
|
||||
- **Core entities** (4): Search, SearchCriteria, SearchStatus, SearchUpdate
|
||||
- **Work order entities** (5): WorkOrder, WorkOrderStep, WorkOrderTime, WorkOrderComponent, WorkOrderRouting
|
||||
- **Lot entities** (3): Lot, LotUsage, LotLocation
|
||||
- **Reference entities** (8): Item, WorkCenter, ProfitCenter, Branch, JdeUser, StatusCode, FunctionCode, IBusinessUnit
|
||||
- **Config entities** (3): DataUpdate, OrgHierarchy, RouteMaster
|
||||
- **CMS entities** (1): MisData
|
||||
- **Auth entities** (1): UserInfo
|
||||
- **Support entities** (5): QueryTypes, TableSpec, ColumnSpec, UpdateTypes, StatusUpdate
|
||||
- **Additional types** (4): POReceiver, POInspect, DcsLot, CamstarMO
|
||||
- **ViewModels** for projections
|
||||
- **Extension methods** for ToViewModel() conversions
|
||||
- **JdeDateConverter** static helper for JDE date/time conversion
|
||||
|
||||
### Out of Scope
|
||||
|
||||
- Database repository implementations (covered by data-access spec)
|
||||
- Service layer validation logic
|
||||
- SignalR hub implementations (web-api-auth spec)
|
||||
- Database schema (separate migrate-database-schema change)
|
||||
|
||||
## Motivation
|
||||
|
||||
The domain models are foundational to all other migration work. They provide:
|
||||
- Type-safe representation of JDE/CMS manufacturing data
|
||||
- Nullable reference type annotations for improved null safety
|
||||
- System.Text.Json serialization compatibility for modern API communication
|
||||
- Extension method projections for clean DTO separation
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
1. All 52 requirements from `domain-models/spec.md` are implemented
|
||||
2. Solution builds successfully with `dotnet build`
|
||||
3. Nullable reference types enabled and all annotations applied per spec
|
||||
4. All enums use `[JsonConverter(typeof(JsonStringEnumConverter))]`
|
||||
5. JdeDateConverter handles edge cases (zero/invalid dates return null)
|
||||
6. ToViewModel() extension methods exist for entities that require them
|
||||
7. IBusinessUnit interface implemented by WorkCenter and ProfitCenter
|
||||
8. `openspec validate implement-domain-models --strict` passes
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Phase 1 (Solution Structure) - completed
|
||||
- Phase 2 (Database Schema) - in progress, but domain models are independent
|
||||
|
||||
## Risks
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| Property name mismatch with legacy | Cross-reference spec against OLD/DataModel/Models/*.cs |
|
||||
| Missing nullable annotations | Use spec's explicit nullable annotation list |
|
||||
| JDE date conversion errors | Unit tests for edge cases (zero dates, invalid formats) |
|
||||
| Serialization incompatibility | Test JSON round-trip for all entities |
|
||||
|
||||
## Related Specs
|
||||
|
||||
- `domain-models` - Primary specification for this change
|
||||
- `database-schema` - Table definitions that entities map to
|
||||
- `data-access` - Repositories that consume these entities
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
# 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`
|
||||
|
||||
#### 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: 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:
|
||||
|
||||
```csharp
|
||||
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:
|
||||
|
||||
```csharp
|
||||
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 |
|
||||
@@ -0,0 +1,282 @@
|
||||
# Tasks: Implement Domain Models
|
||||
|
||||
## Phase 1: Foundation (Enums, Interfaces, Helpers)
|
||||
|
||||
- [x] Create JdeDateConverter helper class
|
||||
- Location: `JdeScoping.Core/Helpers/JdeDateConverter.cs`
|
||||
- Implements: CYYDDD date format conversion, HHMMSS time conversion
|
||||
- Validation: Unit tests for valid dates, zero dates, invalid formats all pass
|
||||
|
||||
- [x] Create SearchStatus enum
|
||||
- Location: `JdeScoping.Core/Models/Enums/SearchStatus.cs`
|
||||
- Values: New (0), Submitted (1), Started (2), Ended (3), Error (4)
|
||||
- Validation: Has `[JsonConverter(typeof(JsonStringEnumConverter))]` attribute
|
||||
|
||||
- [x] Create UpdateTypes enum
|
||||
- Location: `JdeScoping.Core/Models/Enums/UpdateTypes.cs`
|
||||
- Values: Hourly (1), Daily (2), Mass (3)
|
||||
- Validation: Has `[JsonConverter(typeof(JsonStringEnumConverter))]` attribute
|
||||
|
||||
- [x] Create IBusinessUnit interface
|
||||
- Location: `JdeScoping.Core/Interfaces/IBusinessUnit.cs`
|
||||
- Properties: Code, Description, LastUpdateDT
|
||||
- Validation: Compiles, used by WorkCenter and ProfitCenter
|
||||
|
||||
## Phase 2: Search-Related Entities
|
||||
|
||||
- [x] Replace Search entity
|
||||
- Location: `JdeScoping.Core/Models/Search.cs`
|
||||
- Properties: ID, UserName, Name, Status, SubmitDT, StartDT, EndDT, CriteriaJSON, Criteria, Results
|
||||
- Validation: Matches spec requirement, Results is `byte[]?`
|
||||
|
||||
- [x] Create SearchCriteria entity
|
||||
- Location: `JdeScoping.Core/Models/SearchCriteria.cs`
|
||||
- Properties: MinimumDT, MaximumDT, WorkOrderNumbers, ItemNumbers, ProfitCenters, WorkCenters, OperatorIDs, ComponentLotNumbers, ExtractMisData, PartOperations
|
||||
- Validation: All list properties initialized to empty lists
|
||||
|
||||
- [x] Create SearchUpdate entity
|
||||
- Location: `JdeScoping.Core/Models/SearchUpdate.cs`
|
||||
- Properties: ID, UserName, Name, Status, SubmitDT, StartDT, EndDT, Timestamp
|
||||
- Validation: Constructor accepts Search, sets Timestamp to DateTime.UtcNow
|
||||
|
||||
- [x] Create StatusUpdate entity
|
||||
- Location: `JdeScoping.Core/Models/StatusUpdate.cs`
|
||||
- Properties: Message, Timestamp
|
||||
- Validation: Compiles, simple message DTO
|
||||
|
||||
## Phase 3: Work Order Entities
|
||||
|
||||
- [x] Replace WorkOrder entity
|
||||
- Location: `JdeScoping.Core/Models/WorkOrder.cs`
|
||||
- Properties: WorkOrderNumber, BranchCode, LotNumber?, ItemNumber, ShortItemNumber, ParentWorkOrderNumber?, OrderQuantity, HeldQuantity, ShippedQuantity, StatusCode, StatusCodeUpdateDT, IssueDate, StartDate, RoutingType, LastUpdateDT
|
||||
- Validation: Private JDE date fields, computed LastUpdateDT, nullable annotations
|
||||
|
||||
- [x] Create WorkOrderStep entity
|
||||
- Location: `JdeScoping.Core/Models/WorkOrderStep.cs`
|
||||
- Properties: WorkOrderNumber, BranchCode, WorkCenterCode, StepNumber, StepDescription?, FunctionOperationDescription?, StepTypeCode, StartDT, EndDT, FunctionCode, ScrappedQuantity, LastUpdateDT
|
||||
- Validation: Nullable annotations on optional fields
|
||||
|
||||
- [x] Create WorkOrderTime entity
|
||||
- Location: `JdeScoping.Core/Models/WorkOrderTime.cs`
|
||||
- Properties: UniqueID, WorkOrderNumber, BranchCode, WorkCenterCode, StepNumber, AddressNumber, GlDate, LastUpdateDT
|
||||
- Validation: Matches F31122 table structure
|
||||
|
||||
- [x] Create WorkOrderComponent entity
|
||||
- Location: `JdeScoping.Core/Models/WorkOrderComponent.cs`
|
||||
- Properties: UniqueID, WorkOrderNumber, LotNumber?, BranchCode, ShortItemNumber?, Quantity, LastUpdateDT
|
||||
- Validation: ShortItemNumber and LotNumber are nullable
|
||||
|
||||
- [x] Create WorkOrderRouting entity
|
||||
- Location: `JdeScoping.Core/Models/WorkOrderRouting.cs`
|
||||
- Properties: UserID, BatchNumber, TransactionNumber, LineNumber, StepNumber, WorkCenterCode, WorkOrderNumber, RoutingType, BranchCode, StepDescription?, FunctionCode, TransactionDate, LastUpdateDT
|
||||
- Validation: Compiles, nullable StepDescription
|
||||
|
||||
## Phase 4: Lot Entities
|
||||
|
||||
- [x] Replace Lot entity
|
||||
- Location: `JdeScoping.Core/Models/Lot.cs`
|
||||
- Properties: LotNumber, BranchCode, ShortItemNumber, ItemNumber, SupplierCode, StatusCode (char), Memo1?, Memo2?, Memo3?, LastUpdateDT
|
||||
- Validation: StatusCode is char type, nullable memo fields
|
||||
|
||||
- [x] Replace LotUsage entity
|
||||
- Location: `JdeScoping.Core/Models/LotUsage.cs`
|
||||
- Properties: UniqueID, WorkOrderNumber, LotNumber, BranchCode, ShortItemNumber, Quantity, LastUpdateDT
|
||||
- Validation: Cardex entry structure matches spec
|
||||
|
||||
- [x] Create LotLocation entity
|
||||
- Location: `JdeScoping.Core/Models/LotLocation.cs`
|
||||
- Properties: LotNumber, ShortItemNumber, BranchCode, Location, LastUpdateDT
|
||||
- Validation: Compiles with all properties
|
||||
|
||||
## Phase 5: Reference Entities
|
||||
|
||||
- [x] Replace Item entity
|
||||
- Location: `JdeScoping.Core/Models/Item.cs`
|
||||
- Properties: ShortItemNumber, ItemNumber, Description, PlanningFamily?, StockingType?, LastUpdateDT
|
||||
- Validation: Dual identifier pattern (ShortItemNumber + ItemNumber)
|
||||
|
||||
- [x] Replace WorkCenter entity
|
||||
- Location: `JdeScoping.Core/Models/WorkCenter.cs`
|
||||
- Properties: Code, Description, LastUpdateDT
|
||||
- Validation: Implements IBusinessUnit interface
|
||||
|
||||
- [x] Create ProfitCenter entity
|
||||
- Location: `JdeScoping.Core/Models/ProfitCenter.cs`
|
||||
- Properties: Code, Description, LastUpdateDT
|
||||
- Validation: Implements IBusinessUnit interface
|
||||
|
||||
- [x] Create Branch entity
|
||||
- Location: `JdeScoping.Core/Models/Branch.cs`
|
||||
- Properties: Code, Description, LastUpdateDT
|
||||
- Validation: Compiles, matches spec
|
||||
|
||||
- [x] Replace JdeUser entity
|
||||
- Location: `JdeScoping.Core/Models/JdeUser.cs`
|
||||
- Properties: AddressNumber, UserID, FullName, LastUpdateDT
|
||||
- Validation: AddressNumber is long, matches spec
|
||||
|
||||
- [x] Create StatusCode entity
|
||||
- Location: `JdeScoping.Core/Models/StatusCode.cs`
|
||||
- Properties: Code, Description, LastUpdateDT
|
||||
- Validation: JDE work order status lookup
|
||||
|
||||
- [x] Create FunctionCode entity
|
||||
- Location: `JdeScoping.Core/Models/FunctionCode.cs`
|
||||
- Properties: Code, Description, LastUpdateDT
|
||||
- Validation: JDE function code lookup
|
||||
|
||||
## Phase 6: Config and Mapping Entities
|
||||
|
||||
- [x] Create DataUpdate entity
|
||||
- Location: `JdeScoping.Core/Models/DataUpdate.cs`
|
||||
- Properties: ID, SourceSystem, SourceData, TableName, StartDT, EndDT, UpdateType, WasSuccessful, NumberRecords
|
||||
- Validation: UpdateType uses UpdateTypes enum
|
||||
|
||||
- [x] Create OrgHierarchy entity
|
||||
- Location: `JdeScoping.Core/Models/OrgHierarchy.cs`
|
||||
- Properties: WorkCenterCode, BranchCode, ProfitCenterCode, LastUpdateDT
|
||||
- Validation: Profit center to work center mapping
|
||||
|
||||
- [x] Create RouteMaster entity
|
||||
- Location: `JdeScoping.Core/Models/RouteMaster.cs`
|
||||
- Properties: BranchCode, ItemNumber, RoutingType, SequenceNumber, FunctionCode, WorkCenterCode, StartDate, EndDate?, LastUpdateDT
|
||||
- Validation: EndDate is nullable
|
||||
|
||||
## Phase 7: CMS and External Entities
|
||||
|
||||
- [x] Create MisData entity
|
||||
- Location: `JdeScoping.Core/Models/MisData.cs`
|
||||
- Properties: ItemNumber, BranchCode, SequenceNumber, MisNumber, RevID?, CharNumber, TestDescription?, SamplingType?, SamplingValue?, ToolsGauges?, WorkInstructions?, Status, ReleaseDate?
|
||||
- Validation: Many nullable string fields per spec
|
||||
|
||||
- [x] Create POReceiver entity
|
||||
- Location: `JdeScoping.Core/Models/POReceiver.cs`
|
||||
- Properties: OrderNumber, OrderCompany, OrderSuffix, LineNumber, NumberOfLines, InvoiceNumber, BranchCode, LotNumber?, ShortItemNumber (string!), DateReceived, Subledger?, QtyReceived, LastUpdateDT
|
||||
- Validation: ShortItemNumber is string (intentional legacy quirk)
|
||||
|
||||
- [x] Create POInspect entity
|
||||
- Location: `JdeScoping.Core/Models/POInspect.cs`
|
||||
- Properties: UniqueID, OrderNumber, OrderCompany, LineNumber, InvoiceNumber, LotNumber?, ShortItemNumber, LastUpdateDT
|
||||
- Validation: Compiles with nullable LotNumber
|
||||
|
||||
- [x] Create DcsLot entity
|
||||
- Location: `JdeScoping.Core/Models/DcsLot.cs`
|
||||
- Properties: ItemNumber, LotNumber, LotSuffix?, LastUpdateDT
|
||||
- Validation: Simple DCS lot record
|
||||
|
||||
- [x] Create CamstarMO entity
|
||||
- Location: `JdeScoping.Core/Models/CamstarMO.cs`
|
||||
- Properties: MONumber, LastUpdateDT
|
||||
- Validation: Simple Camstar manufacturing order
|
||||
|
||||
## Phase 8: Auth and User Entities
|
||||
|
||||
- [x] Create UserInfo entity
|
||||
- Location: `JdeScoping.Core/Models/UserInfo.cs`
|
||||
- Properties: Username, FirstName?, LastName?, DisplayName (computed), Title?, EmailAddress?
|
||||
- Validation: DisplayName computed property works (fallback to Username)
|
||||
|
||||
## Phase 9: Query Support Entities
|
||||
|
||||
- [x] Create QueryTypes entity
|
||||
- Location: `JdeScoping.Core/Models/QueryTypes.cs`
|
||||
- Properties: Code, Name, OrderIndex, filter flags (TimeSpanFilter, WorkOrderFilter, etc.)
|
||||
- Validation: Static dictionary pattern, Identify() method stub
|
||||
|
||||
- [x] Create TableSpec entity
|
||||
- Location: `JdeScoping.Core/Models/TableSpec.cs`
|
||||
- Properties: Name, TempTableName (computed), Columns, PrimaryKey
|
||||
- Validation: Constructor initializes lists, stub methods exist
|
||||
|
||||
- [x] Create ColumnSpec entity
|
||||
- Location: `JdeScoping.Core/Models/ColumnSpec.cs`
|
||||
- Properties: Name, Definition
|
||||
- Validation: Simple column specification
|
||||
|
||||
## Phase 10: ViewModels
|
||||
|
||||
- [x] Create WorkOrderViewModel
|
||||
- Location: `JdeScoping.Core/ViewModels/WorkOrderViewModel.cs`
|
||||
- Properties: WorkOrderNumber, ItemNumber
|
||||
- Validation: Minimal projection DTO
|
||||
|
||||
- [x] Create LotViewModel
|
||||
- Location: `JdeScoping.Core/ViewModels/LotViewModel.cs`
|
||||
- Properties: LotNumber, ItemNumber
|
||||
- Validation: Used by SearchCriteria.ComponentLotNumbers
|
||||
|
||||
- [x] Create ItemViewModel
|
||||
- Location: `JdeScoping.Core/ViewModels/ItemViewModel.cs`
|
||||
- Properties: ItemNumber, Description
|
||||
- Validation: Minimal projection DTO
|
||||
|
||||
- [x] Create WorkCenterViewModel
|
||||
- Location: `JdeScoping.Core/ViewModels/WorkCenterViewModel.cs`
|
||||
- Properties: Code, Description
|
||||
- Validation: Minimal projection DTO
|
||||
|
||||
- [x] Create ProfitCenterViewModel
|
||||
- Location: `JdeScoping.Core/ViewModels/ProfitCenterViewModel.cs`
|
||||
- Properties: Code, Description
|
||||
- Validation: Minimal projection DTO
|
||||
|
||||
- [x] Create JdeUserViewModel
|
||||
- Location: `JdeScoping.Core/ViewModels/JdeUserViewModel.cs`
|
||||
- Properties: AddressNumber, UserID, FullName
|
||||
- Validation: Minimal projection DTO
|
||||
|
||||
- [x] Create PartOperationViewModel
|
||||
- Location: `JdeScoping.Core/ViewModels/PartOperationViewModel.cs`
|
||||
- Properties: ItemNumber, OperationNumber, MisNumber, Revision
|
||||
- Validation: Used by SearchCriteria.PartOperations
|
||||
|
||||
## Phase 11: Extension Methods
|
||||
|
||||
- [x] Create WorkOrderExtensions
|
||||
- Location: `JdeScoping.Core/Extensions/WorkOrderExtensions.cs`
|
||||
- Method: ToViewModel() -> WorkOrderViewModel
|
||||
- Validation: Extension method on WorkOrder compiles and works
|
||||
|
||||
- [x] Create LotExtensions
|
||||
- Location: `JdeScoping.Core/Extensions/LotExtensions.cs`
|
||||
- Method: ToViewModel() -> LotViewModel
|
||||
- Validation: Extension method on Lot compiles and works
|
||||
|
||||
- [x] Create ItemExtensions
|
||||
- Location: `JdeScoping.Core/Extensions/ItemExtensions.cs`
|
||||
- Method: ToViewModel() -> ItemViewModel
|
||||
- Validation: Extension method on Item compiles and works
|
||||
|
||||
- [x] Create WorkCenterExtensions
|
||||
- Location: `JdeScoping.Core/Extensions/WorkCenterExtensions.cs`
|
||||
- Method: ToViewModel() -> WorkCenterViewModel
|
||||
- Validation: Extension method on WorkCenter compiles and works
|
||||
|
||||
- [x] Create ProfitCenterExtensions
|
||||
- Location: `JdeScoping.Core/Extensions/ProfitCenterExtensions.cs`
|
||||
- Method: ToViewModel() -> ProfitCenterViewModel
|
||||
- Validation: Extension method on ProfitCenter compiles and works
|
||||
|
||||
- [x] Create JdeUserExtensions
|
||||
- Location: `JdeScoping.Core/Extensions/JdeUserExtensions.cs`
|
||||
- Method: ToViewModel() -> JdeUserViewModel
|
||||
- Validation: Extension method on JdeUser compiles and works
|
||||
|
||||
## Phase 12: Verification
|
||||
|
||||
- [x] Verify solution builds
|
||||
- Command: `dotnet build NEW/src/JdeScoping.Core/JdeScoping.Core.csproj`
|
||||
- Validation: Build succeeds with no errors
|
||||
|
||||
- [x] Verify nullable reference types
|
||||
- Validation: All nullable properties use `?` annotation, no compiler warnings
|
||||
|
||||
- [x] Verify JSON serialization
|
||||
- Validation: SearchStatus and UpdateTypes serialize as strings, not integers
|
||||
|
||||
- [x] Run OpenSpec validation
|
||||
- Command: `openspec validate implement-domain-models --strict`
|
||||
- Validation: All requirements marked as covered
|
||||
|
||||
- [x] Cross-reference with spec
|
||||
- Validation: All 52 requirements from domain-models/spec.md implemented
|
||||
Reference in New Issue
Block a user