docs: Update CLAUDE.md with CutList.Web entities, services, and pages
Comprehensive rewrite covering all three projects, entity definitions, service APIs, page routes, shared components, and key patterns including purchase flow and job locking conventions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
243
CLAUDE.md
243
CLAUDE.md
@@ -1,13 +1,22 @@
|
||||
# CLAUDE.md
|
||||
|
||||
> **IMPORTANT**: Always keep this document updated when functionality changes, entities are added/modified, new pages or services are created, or architectural patterns evolve.
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
CutList is a Windows Forms 1D bin packing optimization application that helps users optimize material cutting. It calculates efficient bin packing solutions to minimize waste when cutting stock materials into required parts.
|
||||
CutList is a 1D bin packing optimization application that helps users optimize material cutting. It calculates efficient bin packing solutions to minimize waste when cutting stock materials into required parts.
|
||||
|
||||
**Framework**: .NET 8.0 (Windows Forms)
|
||||
**Key Dependencies**: Math-Expression-Evaluator (input parsing), Newtonsoft.Json (serialization)
|
||||
The solution contains three projects:
|
||||
|
||||
| Project | Framework | Purpose |
|
||||
|---------|-----------|---------|
|
||||
| **CutList** | .NET 8.0 Windows Forms | Original desktop UI (MVP pattern) |
|
||||
| **CutList.Core** | .NET 8.0 Class Library | Domain models and packing algorithms (platform-agnostic) |
|
||||
| **CutList.Web** | .NET 8.0 Blazor Server | Web-based UI with EF Core + SQL Server |
|
||||
|
||||
**Key Dependencies**: Math-Expression-Evaluator (input parsing), Newtonsoft.Json (serialization), Entity Framework Core (data access), Bootstrap 5 + Bootstrap Icons (UI)
|
||||
|
||||
## Build Commands
|
||||
|
||||
@@ -15,12 +24,18 @@ CutList is a Windows Forms 1D bin packing optimization application that helps us
|
||||
# Build entire solution
|
||||
dotnet build CutList.sln
|
||||
|
||||
# Build specific project
|
||||
# Build specific projects
|
||||
dotnet build CutList/CutList.csproj
|
||||
dotnet build CutList.Core/CutList.Core.csproj
|
||||
dotnet build CutList.Web/CutList.Web.csproj
|
||||
|
||||
# Run the application
|
||||
dotnet run --project CutList/CutList.csproj
|
||||
# Run applications
|
||||
dotnet run --project CutList/CutList.csproj # WinForms
|
||||
dotnet run --project CutList.Web/CutList.Web.csproj # Blazor
|
||||
|
||||
# EF Core migrations (always apply immediately after creating)
|
||||
dotnet ef migrations add <Name> --project CutList.Web
|
||||
dotnet ef database update --project CutList.Web
|
||||
|
||||
# Clean build
|
||||
dotnet clean CutList.sln
|
||||
@@ -28,69 +43,179 @@ dotnet clean CutList.sln
|
||||
|
||||
## Architecture
|
||||
|
||||
### Project Structure
|
||||
### CutList.Core — Domain & Algorithms
|
||||
|
||||
- **CutList/** - Main Windows Forms application (UI layer)
|
||||
- **CutList.Core/** - Core library with domain models and packing algorithms (shareable, platform-agnostic)
|
||||
|
||||
### Key Patterns
|
||||
|
||||
**MVP (Model-View-Presenter)**
|
||||
- `MainForm` implements `IMainView` (pure UI, no business logic)
|
||||
- `MainFormPresenter` orchestrates all business logic
|
||||
- `Document` contains application state
|
||||
|
||||
**Result Pattern**
|
||||
- `Result<T>` in Common/Result.cs for standardized error handling
|
||||
- Use `Result<T>.Success(value)` and `Result<T>.Failure(error)` instead of exceptions for expected errors
|
||||
|
||||
**Factory Pattern**
|
||||
- `IEngineFactory` creates packing engines
|
||||
- Allows swapping algorithm implementations
|
||||
|
||||
### Data Flow
|
||||
|
||||
1. User enters parts/bins in MainForm
|
||||
2. MainFormPresenter calls CutListService.Pack()
|
||||
3. CutListService converts input models to core models (MultiBin, BinItem)
|
||||
4. MultiBinEngine coordinates packing across bin types
|
||||
5. AdvancedFitEngine performs 1D bin packing (first-fit decreasing with optimization)
|
||||
6. Results displayed in ResultsForm
|
||||
|
||||
### Key Services
|
||||
|
||||
- **CutListService**: Bridges UI models to core packing algorithms
|
||||
- **DocumentService**: JSON file persistence
|
||||
|
||||
### Domain Models (CutList.Core)
|
||||
|
||||
- **BinItem**: Item to be packed (with label, length)
|
||||
**Key Domain Models**:
|
||||
- **BinItem**: Item to be packed (label, length)
|
||||
- **MultiBin**: Stock bin type with length, quantity (-1 = unlimited), priority
|
||||
- **Bin**: Packed bin containing items
|
||||
- **Bin**: Packed bin containing items, tracks remaining length
|
||||
- **Tool**: Cutting tool with kerf/blade width
|
||||
|
||||
### Unit Handling
|
||||
**Packing Engine**:
|
||||
- `MultiBinEngine` coordinates packing across bin types
|
||||
- `AdvancedFitEngine` performs 1D bin packing (first-fit decreasing with optimization)
|
||||
- `PackingStrategy.AdvancedFit` is the standard strategy
|
||||
|
||||
- **ArchUnits**: Converts feet/inches/fractions to decimals (accepts "12'", "6\"", "12 1/2\"", etc.)
|
||||
- **FormatHelper**: Converts decimals to mixed fractions for display
|
||||
**Unit Handling**:
|
||||
- `ArchUnits` — Converts feet/inches/fractions to decimal inches (accepts "12'", "6\"", "12 1/2\"", etc.)
|
||||
- `FormatHelper` — Converts decimals to mixed fractions for display
|
||||
- Internal calculations use inches; format on display
|
||||
|
||||
## Important Conventions
|
||||
**Patterns**:
|
||||
- `Result<T>` for standardized error handling (Success/Failure instead of exceptions)
|
||||
- `IEngineFactory` for swappable algorithm implementations
|
||||
- Lower priority number = used first in bin selection
|
||||
|
||||
- **Nullable reference types enabled** - handle nulls explicitly
|
||||
- **Collections are encapsulated** - use AsReadOnly(), access via Add* methods
|
||||
- **Validation in domain models** - constructors and properties validate inputs
|
||||
- **Parameterless constructors** on Tool/MultiBin are for JSON serialization only
|
||||
- **Spacing property** on engines handles blade/kerf width
|
||||
- **Priority system**: Lower priority bins are used first
|
||||
### CutList (WinForms) — Desktop UI
|
||||
|
||||
- MVP pattern: `MainForm` implements `IMainView`, `MainFormPresenter` orchestrates logic
|
||||
- `Document` holds application state
|
||||
- `CutListService` bridges UI models to core packing algorithms
|
||||
- `DocumentService` handles JSON file persistence
|
||||
|
||||
### CutList.Web (Blazor Server) — Web UI
|
||||
|
||||
**Database**: SQL Server via Entity Framework Core (connection string: `DefaultConnection`)
|
||||
|
||||
**Service Registration** (Program.cs): All services registered as Scoped — MaterialService, SupplierService, StockItemService, JobService, CutListPackingService, ReportService, PurchaseItemService
|
||||
|
||||
## CutList.Web Entities
|
||||
|
||||
### Material
|
||||
- `Shape` (MaterialShape enum): Round Bar, Round Tube, Flat Bar, Square Bar, Square Tube, Rectangular Tube, Angle, Channel, I-Beam, Pipe
|
||||
- `Type` (MaterialType enum): Steel, Aluminum, Stainless, Brass, Copper
|
||||
- `Grade` (string?), `Size` (string), `Description` (string?), `IsActive` (bool), `SortOrder` (int)
|
||||
- **DisplayName**: "{Shape} - {Size}"
|
||||
- **Relationships**: `Dimensions` (1:1 MaterialDimensions), `StockItems` (1:many), `JobParts` (1:many)
|
||||
|
||||
### MaterialDimensions (TPH Inheritance)
|
||||
Abstract base; derived types per shape: `RoundBarDimensions`, `RoundTubeDimensions`, `FlatBarDimensions`, `SquareBarDimensions`, `SquareTubeDimensions`, `RectangularTubeDimensions`, `AngleDimensions`, `ChannelDimensions`, `IBeamDimensions`, `PipeDimensions`. Each generates its own `SizeString` and `SortOrder`.
|
||||
|
||||
### StockItem
|
||||
- `MaterialId`, `LengthInches` (decimal), `QuantityOnHand` (int), `IsActive`
|
||||
- **Unique constraint**: (MaterialId, LengthInches)
|
||||
- **Relationships**: `Material`, `SupplierOfferings` (1:many), `Transactions` (1:many StockTransaction)
|
||||
|
||||
### StockTransaction
|
||||
- `StockItemId`, `Quantity` (signed delta), `Type` (Received/Used/Adjustment/Scrapped/Returned)
|
||||
- Optional: `JobId`, `SupplierId`, `UnitPrice`
|
||||
|
||||
### Supplier
|
||||
- `Name` (required), `ContactInfo`, `Notes`, `IsActive`
|
||||
- **Relationships**: `Offerings` (1:many SupplierOffering)
|
||||
|
||||
### SupplierOffering
|
||||
- Links Supplier to StockItem with optional `PartNumber`, `Price`, `Notes`
|
||||
- **Unique constraint**: (SupplierId, StockItemId)
|
||||
|
||||
### CuttingTool
|
||||
- `Name`, `KerfInches` (decimal), `IsDefault` (bool), `IsActive`
|
||||
- **Seeded**: Bandsaw (0.0625"), Chop Saw (0.125"), Cold Cut Saw (0.0625"), Hacksaw (0.0625")
|
||||
|
||||
### Job
|
||||
- `JobNumber` (auto-generated "JOB-#####", unique), `Name`, `Customer`, `CuttingToolId`, `Notes`
|
||||
- `LockedAt` (DateTime?) — set when materials ordered; `IsLocked` computed property
|
||||
- **Relationships**: `Parts` (1:many JobPart), `Stock` (1:many JobStock), `CuttingTool`
|
||||
|
||||
### JobPart
|
||||
- `JobId`, `MaterialId`, `Name`, `LengthInches` (decimal), `Quantity` (int), `SortOrder`
|
||||
|
||||
### JobStock
|
||||
- `JobId`, `MaterialId`, `StockItemId?`, `LengthInches`, `Quantity` (-1 = unlimited), `IsCustomLength`, `Priority` (lower = used first), `SortOrder`
|
||||
|
||||
### PurchaseItem
|
||||
- `StockItemId`, `SupplierId?`, `JobId?`, `Quantity`, `Status` (Pending/Ordered/Received), `Notes`
|
||||
|
||||
## CutList.Web Services
|
||||
|
||||
### MaterialService
|
||||
- CRUD with soft delete, dimension management (`CreateWithDimensionsAsync`, `UpdateWithDimensionsAsync`)
|
||||
- Search by dimension (e.g., `SearchRoundBarByDiameterAsync`, `SearchAngleByLegAsync`) with tolerance
|
||||
- `CreateDimensionsForShape(shape)` factory method
|
||||
|
||||
### StockItemService
|
||||
- CRUD with soft delete
|
||||
- Stock transactions: `AddStockAsync`, `UseStockAsync`, `AdjustStockAsync`, `ScrapStockAsync`
|
||||
- `GetTransactionHistoryAsync`, `RecalculateQuantityAsync`
|
||||
- Pricing: `GetAverageCostAsync`, `GetLastPurchasePriceAsync`
|
||||
|
||||
### SupplierService
|
||||
- CRUD for suppliers and offerings
|
||||
- `GetOfferingsForStockItemAsync` — all supplier options for a stock item
|
||||
|
||||
### JobService
|
||||
- Job CRUD: `CreateAsync` (auto-generates JobNumber), `DuplicateAsync` (deep copy), `QuickCreateAsync`
|
||||
- Lock/Unlock: `LockAsync(id)`, `UnlockAsync(id)` — controls job editability after ordering
|
||||
- Parts: `AddPartAsync`, `UpdatePartAsync`, `DeletePartAsync` (all update job timestamp)
|
||||
- Stock: `AddStockAsync`, `UpdateStockAsync`, `DeleteStockAsync`
|
||||
- Cutting tools: full CRUD with single-default enforcement
|
||||
|
||||
### CutListPackingService
|
||||
- `PackAsync(parts, kerfInches, jobStock?)` — runs optimization per material group
|
||||
- Separates results into `InStockBins` (from inventory) and `ToBePurchasedBins`
|
||||
- `GetSummary(result)` — calculates total bins, pieces, waste, efficiency %
|
||||
|
||||
### PurchaseItemService
|
||||
- CRUD + `CreateBulkAsync` for batch creation from optimization results
|
||||
- `UpdateStatusAsync(id, status)`, `UpdateSupplierAsync(id, supplierId)`
|
||||
|
||||
### ReportService
|
||||
- `FormatLength(inches)`, `GroupItems(items)` for print report formatting
|
||||
|
||||
## CutList.Web Pages
|
||||
|
||||
| Route | Page | Purpose |
|
||||
|-------|------|---------|
|
||||
| `/` | Home | Welcome page with feature cards and workflow guide |
|
||||
| `/jobs` | Jobs/Index | Job list with pagination, lock icons, Quick Create, Duplicate, Delete |
|
||||
| `/jobs/new` | Jobs/Edit | New job form (details only) |
|
||||
| `/jobs/{Id}` | Jobs/Edit | Tabbed editor (Details, Parts, Stock); locked jobs show banner + disable editing |
|
||||
| `/jobs/{Id}/results` | Jobs/Results | Optimization results, summary cards, "Add to Order List" (locks job), Print Report |
|
||||
| `/materials` | Materials/Index | Material list with MaterialFilter, pagination |
|
||||
| `/materials/new`, `/materials/{Id}` | Materials/Edit | Material + dimension form (varies by shape) |
|
||||
| `/stock` | Stock/Index | Stock items with MaterialFilter, quantity badges |
|
||||
| `/stock/new`, `/stock/{Id}` | Stock/Edit | Stock item form |
|
||||
| `/orders` | Orders/Index | Tabbed (Pending/Ordered/All), supplier assignment, status transitions |
|
||||
| `/orders/add` | Orders/Add | Manual purchase item creation |
|
||||
| `/suppliers` | Suppliers/Index | Supplier list with CRUD |
|
||||
| `/suppliers/{Id}` | Suppliers/Edit | Supplier + offerings management |
|
||||
| `/tools` | Tools/Index | Cutting tools CRUD |
|
||||
|
||||
## Shared Components
|
||||
|
||||
| Component | Purpose |
|
||||
|-----------|---------|
|
||||
| `ConfirmDialog` | Modal confirmation for destructive actions (Show/Hide methods, OnConfirm callback) |
|
||||
| `LengthInput` | Architectural unit input — parses "12'", "6\"", "12 1/2\""; reformats on blur; two-way binding via `Value` or `NullableValue` |
|
||||
| `Pager` | Pagination with "Showing X-Y of Z", prev/next, smart page window with ellipsis |
|
||||
| `MaterialFilter` | Reusable filter: Shape, Type, Grade dropdowns + search text; used on Materials, Stock, Orders pages |
|
||||
|
||||
## Key Patterns & Conventions
|
||||
|
||||
- **Nullable reference types enabled** — handle nulls explicitly
|
||||
- **Soft deletes** — Materials, Suppliers, StockItems, CuttingTools use `IsActive` flag
|
||||
- **Job locking** — `LockedAt` timestamp set when materials ordered; Edit page disables all modification via `<fieldset disabled>`, hides add/edit/delete buttons; Unlock button to re-enable editing
|
||||
- **Pagination** — All list pages use `Pager` with `pageSize = 25`
|
||||
- **ConfirmDialog** — All destructive actions use the shared `ConfirmDialog` component
|
||||
- **Material selection flow** — Shape dropdown -> Size dropdown -> Length input -> Quantity (conditional dropdowns)
|
||||
- **Stock priority** — Lower number = used first; `-1` quantity = unlimited
|
||||
- **Job stock** — Jobs can use auto-discovered inventory OR define custom stock lengths
|
||||
- **Purchase flow** — Optimize job -> "Add to Order List" creates PurchaseItems + locks job -> Orders page manages status (Pending -> Ordered -> Received)
|
||||
- **Timestamps** — `CreatedAt` defaults to `GETUTCDATE()`; `UpdatedAt` set on modifications
|
||||
- **Collections** — Encapsulated in Core; use `AsReadOnly()`, access via `Add*` methods
|
||||
- **Priority system** — Lower priority bins used first in packing algorithm
|
||||
|
||||
## Key Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `Presenters/MainFormPresenter.cs` | Main business logic orchestrator |
|
||||
| `Services/CutListService.cs` | Packing algorithm interface |
|
||||
| `CutList.Core/Nesting/AdvancedFitEngine.cs` | Core packing algorithm |
|
||||
| `CutList.Core/Nesting/MultiBinEngine.cs` | Multi-bin orchestration |
|
||||
| `CutList.Core/ArchUnits.cs` | Unit conversion |
|
||||
| `CutList.Core/FormatHelper.cs` | Output formatting |
|
||||
| `CutList.Core/Nesting/AdvancedFitEngine.cs` | Core 1D bin packing algorithm |
|
||||
| `CutList.Core/Nesting/MultiBinEngine.cs` | Multi-bin type orchestration |
|
||||
| `CutList.Core/ArchUnits.cs` | Architectural unit parsing/conversion |
|
||||
| `CutList.Core/Formatting/FormatHelper.cs` | Display formatting |
|
||||
| `CutList.Web/Data/ApplicationDbContext.cs` | EF Core context with all DbSets and configuration |
|
||||
| `CutList.Web/Services/JobService.cs` | Job orchestration (CRUD, parts, stock, tools, lock/unlock) |
|
||||
| `CutList.Web/Services/CutListPackingService.cs` | Bridges web entities to Core packing engine |
|
||||
| `CutList.Web/Components/Pages/Jobs/Edit.razor` | Job editor (tabbed: Details, Parts, Stock) |
|
||||
| `CutList.Web/Components/Pages/Jobs/Results.razor` | Optimization results + order creation |
|
||||
| `CutList/Presenters/MainFormPresenter.cs` | WinForms business logic orchestrator |
|
||||
|
||||
Reference in New Issue
Block a user