Compare commits
49 Commits
5cc088ea6b
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 7d3c92226c | |||
| c31769a746 | |||
| f04bf02c42 | |||
| dac2833dd1 | |||
| a226a1f652 | |||
| 5000021193 | |||
| 02e936febb | |||
| e13f876da6 | |||
| 1f3eb67eb7 | |||
| 2fdf006a8e | |||
| eee38a8473 | |||
| 59f86c8e79 | |||
| 891b214b29 | |||
| c5f366a3ef | |||
| 8926d44969 | |||
| c23c92e852 | |||
| 2586f99c63 | |||
| 5f4e36c688 | |||
| ed705625e9 | |||
| 1ccdeb6817 | |||
| 69b282aaf3 | |||
| f932e8ba13 | |||
| 141176cc5d | |||
| 3fd354aff0 | |||
| b603a4b3e7 | |||
| 5468b2748d | |||
| 8ed10939d4 | |||
| 2a94ad63cb | |||
| b0a9d7fdcc | |||
| f20770d03e | |||
| 4aec4c2275 | |||
| 261f64a895 | |||
| 9b757acac3 | |||
| 177affabf0 | |||
| 17f16901ef | |||
| 21d50e7c20 | |||
| f723661696 | |||
| c795c129e5 | |||
| 30071469bc | |||
| c9a2583f26 | |||
| 0e5b63c557 | |||
| 6388e003d3 | |||
| c5da5dda98 | |||
| 21cddb22c7 | |||
| 3b036308c8 | |||
| 4f6d986dc9 | |||
| 254066c989 | |||
| ce14dd50cb | |||
| dfc767320a |
28
.claude/settings.local.json
Normal file
28
.claude/settings.local.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Skill(roslyn-bridge)",
|
||||
"Bash(dotnet build:*)",
|
||||
"SlashCommand(/rb)",
|
||||
"mcp__roslyn-bridge__get_projects",
|
||||
"Bash(dotnet tool install:*)",
|
||||
"Bash(dotnet ilspy:*)",
|
||||
"Bash(dotnet add package:*)",
|
||||
"Bash(git -C /c/Users/AJ/Desktop/Projects/CutList add CutList.Core/BinComparer.cs CutList.Core/BinItem.cs CutList.Core/MultiBin.cs CutList/Tool.cs)",
|
||||
"Bash(git -C /c/Users/AJ/Desktop/Projects/CutList commit --amend --no-edit)",
|
||||
"mcp__roslyn-bridge__get_code_smells",
|
||||
"mcp__roslyn-bridge__get_duplicates",
|
||||
"mcp__roslyn-bridge__get_code_smell_summary",
|
||||
"mcp__cutlist__create_cutlist",
|
||||
"Bash(dotnet run:*)",
|
||||
"mcp__roslyn-bridge__get_files",
|
||||
"mcp__roslyn-bridge__refresh_workspace",
|
||||
"mcp__roslyn-bridge__get_diagnostics",
|
||||
"Bash(dotnet ef database update:*)",
|
||||
"mcp__roslyn-bridge__search_symbol",
|
||||
"Bash(dotnet ef migrations add:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
}
|
||||
}
|
||||
246
CLAUDE.md
246
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,182 @@ 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 (TPC Inheritance)
|
||||
Abstract base with TPC (Table Per Concrete type) mapping — each shape gets its own standalone table (`DimAngle`, `DimChannel`, `DimFlatBar`, `DimIBeam`, `DimPipe`, `DimRectangularTube`, `DimRoundBar`, `DimRoundTube`, `DimSquareBar`, `DimSquareTube`) with no base table. Each table has its own `Id` (shared sequence) and `MaterialId` FK. 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
|
||||
- `OptimizationResultJson` (string?, nvarchar(max)) — serialized optimization results
|
||||
- `OptimizedAt` (DateTime?) — when optimization was last run
|
||||
- **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 + clear optimization results)
|
||||
- Stock: `AddStockAsync`, `UpdateStockAsync`, `DeleteStockAsync` (all clear optimization results)
|
||||
- Optimization: `SaveOptimizationResultAsync`, `ClearOptimizationResultAsync`
|
||||
- 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 %
|
||||
- `SerializeResult(result)` / `LoadSavedResult(json)` — JSON round-trip via DTO layer (`SavedOptimizationResult` etc.)
|
||||
|
||||
### 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, Results); locked jobs show banner + disable editing |
|
||||
| `/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
|
||||
- **Optimization persistence** — Results saved as JSON in `Job.OptimizationResultJson`; DTO layer (`SavedOptimizationResult` etc.) handles serialization since Core types use encapsulated collections; results auto-cleared when parts, stock, or cutting tool change
|
||||
- **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, Results) |
|
||||
| `CutList/Presenters/MainFormPresenter.cs` | WinForms business logic orchestrator |
|
||||
|
||||
@@ -28,7 +28,17 @@ namespace CutList.Core.Formatting
|
||||
var match2 = regex.Match(input);
|
||||
|
||||
if (!match2.Success)
|
||||
{
|
||||
// If no unit symbols, try to parse as plain inches (e.g., "0.5" or "1/2" converted to "0.5")
|
||||
if (!input.Contains("'") && !input.Contains("\""))
|
||||
{
|
||||
if (double.TryParse(input.Trim(), out var plainInches))
|
||||
{
|
||||
return Math.Round(plainInches, 8);
|
||||
}
|
||||
}
|
||||
throw new Exception("Input is not in a valid format.");
|
||||
}
|
||||
|
||||
var feet = match2.Groups["Feet"];
|
||||
var inches = match2.Groups["Inches"];
|
||||
|
||||
@@ -39,6 +39,12 @@ namespace CutList.Core.Formatting
|
||||
return wholeNumber.ToString();
|
||||
}
|
||||
|
||||
// If whole number is 0, just show the fraction
|
||||
if (wholeNumber == 0)
|
||||
{
|
||||
return $"{numerator}/{denominator}";
|
||||
}
|
||||
|
||||
return $"{wholeNumber}-{numerator}/{denominator}";
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@ namespace CutList.Core.Nesting
|
||||
{
|
||||
/// <summary>
|
||||
/// Default maximum number of items before falling back to AdvancedFitEngine.
|
||||
/// Testing showed 20 items is safe (~100ms worst case), while 21+ can take seconds.
|
||||
/// Testing showed 25 items is safe (~84ms worst case), while 30+ can take seconds.
|
||||
/// </summary>
|
||||
public const int DefaultMaxItems = 20;
|
||||
public const int DefaultMaxItems = 25;
|
||||
|
||||
private readonly IEngine _fallbackEngine;
|
||||
private readonly int _maxItems;
|
||||
@@ -67,7 +67,15 @@ namespace CutList.Core.Nesting
|
||||
BinCount = 0
|
||||
};
|
||||
|
||||
Search(sortedItems, 0, currentState, bestSolution, request);
|
||||
// Precompute suffix sums of item lengths (including spacing per item)
|
||||
// for lower-bound pruning. suffixVolume[i] = total volume of items[i..n-1].
|
||||
var suffixVolume = new double[sortedItems.Count + 1];
|
||||
for (int i = sortedItems.Count - 1; i >= 0; i--)
|
||||
{
|
||||
suffixVolume[i] = suffixVolume[i + 1] + sortedItems[i].Length + request.Spacing;
|
||||
}
|
||||
|
||||
Search(sortedItems, 0, currentState, bestSolution, request, suffixVolume);
|
||||
|
||||
// Build result from best solution
|
||||
var result = new PackResult();
|
||||
@@ -101,7 +109,8 @@ namespace CutList.Core.Nesting
|
||||
int itemIndex,
|
||||
SearchState current,
|
||||
SearchState best,
|
||||
PackingRequest request)
|
||||
PackingRequest request,
|
||||
double[] suffixVolume)
|
||||
{
|
||||
// All items placed - check if this is better
|
||||
if (itemIndex >= items.Count)
|
||||
@@ -123,6 +132,18 @@ namespace CutList.Core.Nesting
|
||||
if (current.BinCount >= request.MaxBinCount)
|
||||
return;
|
||||
|
||||
// Lower-bound pruning: remaining items need at least this many additional bins
|
||||
double remainingVolume = suffixVolume[itemIndex];
|
||||
double availableInExisting = 0;
|
||||
for (int b = 0; b < current.Bins.Count; b++)
|
||||
{
|
||||
availableInExisting += request.StockLength - GetBinUsedLength(current.Bins[b], request.Spacing);
|
||||
}
|
||||
double overflow = remainingVolume - availableInExisting;
|
||||
int additionalBinsNeeded = overflow > 0 ? (int)Math.Ceiling(overflow / request.StockLength) : 0;
|
||||
if (current.BinCount + additionalBinsNeeded >= best.BinCount)
|
||||
return;
|
||||
|
||||
var item = items[itemIndex];
|
||||
|
||||
// Symmetry breaking: if this item has the same length as the previous item,
|
||||
@@ -148,7 +169,7 @@ namespace CutList.Core.Nesting
|
||||
current.Bins[i].Add(item);
|
||||
var prevBinIndex = current.LastBinIndexUsed;
|
||||
current.LastBinIndexUsed = i;
|
||||
Search(items, itemIndex + 1, current, best, request);
|
||||
Search(items, itemIndex + 1, current, best, request, suffixVolume);
|
||||
current.LastBinIndexUsed = prevBinIndex;
|
||||
current.Bins[i].RemoveAt(current.Bins[i].Count - 1);
|
||||
}
|
||||
@@ -162,7 +183,7 @@ namespace CutList.Core.Nesting
|
||||
current.BinCount++;
|
||||
var prevBinIndex = current.LastBinIndexUsed;
|
||||
current.LastBinIndexUsed = newBinIndex;
|
||||
Search(items, itemIndex + 1, current, best, request);
|
||||
Search(items, itemIndex + 1, current, best, request, suffixVolume);
|
||||
current.LastBinIndexUsed = prevBinIndex;
|
||||
current.Bins.RemoveAt(current.Bins.Count - 1);
|
||||
current.BinCount--;
|
||||
|
||||
444
CutList.Mcp/ApiClient.cs
Normal file
444
CutList.Mcp/ApiClient.cs
Normal file
@@ -0,0 +1,444 @@
|
||||
using System.Net.Http.Json;
|
||||
|
||||
namespace CutList.Mcp;
|
||||
|
||||
/// <summary>
|
||||
/// Typed HTTP client for calling the CutList.Web REST API.
|
||||
/// </summary>
|
||||
public class ApiClient
|
||||
{
|
||||
private readonly HttpClient _http;
|
||||
|
||||
public ApiClient(HttpClient http)
|
||||
{
|
||||
_http = http;
|
||||
}
|
||||
|
||||
#region Suppliers
|
||||
|
||||
public async Task<List<ApiSupplierDto>> GetSuppliersAsync(bool includeInactive = false)
|
||||
{
|
||||
var url = $"api/suppliers?includeInactive={includeInactive}";
|
||||
return await _http.GetFromJsonAsync<List<ApiSupplierDto>>(url) ?? [];
|
||||
}
|
||||
|
||||
public async Task<ApiSupplierDto?> CreateSupplierAsync(string name, string? contactInfo, string? notes)
|
||||
{
|
||||
var response = await _http.PostAsJsonAsync("api/suppliers", new { Name = name, ContactInfo = contactInfo, Notes = notes });
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadFromJsonAsync<ApiSupplierDto>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Materials
|
||||
|
||||
public async Task<List<ApiMaterialDto>> GetMaterialsAsync(string? shape = null, bool includeInactive = false)
|
||||
{
|
||||
var url = $"api/materials?includeInactive={includeInactive}";
|
||||
if (!string.IsNullOrEmpty(shape))
|
||||
url += $"&shape={Uri.EscapeDataString(shape)}";
|
||||
return await _http.GetFromJsonAsync<List<ApiMaterialDto>>(url) ?? [];
|
||||
}
|
||||
|
||||
public async Task<ApiMaterialDto?> CreateMaterialAsync(string shape, string? size, string? description,
|
||||
string? type, string? grade, Dictionary<string, decimal>? dimensions)
|
||||
{
|
||||
var body = new
|
||||
{
|
||||
Shape = shape,
|
||||
Size = size,
|
||||
Description = description,
|
||||
Type = type,
|
||||
Grade = grade,
|
||||
Dimensions = dimensions
|
||||
};
|
||||
var response = await _http.PostAsJsonAsync("api/materials", body);
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.Conflict)
|
||||
{
|
||||
var error = await response.Content.ReadAsStringAsync();
|
||||
throw new ApiConflictException(error);
|
||||
}
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadFromJsonAsync<ApiMaterialDto>();
|
||||
}
|
||||
|
||||
public async Task<List<ApiMaterialDto>> SearchMaterialsAsync(string shape, decimal targetValue, decimal tolerance)
|
||||
{
|
||||
var response = await _http.PostAsJsonAsync("api/materials/search", new
|
||||
{
|
||||
Shape = shape,
|
||||
TargetValue = targetValue,
|
||||
Tolerance = tolerance
|
||||
});
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadFromJsonAsync<List<ApiMaterialDto>>() ?? [];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stock Items
|
||||
|
||||
public async Task<List<ApiStockItemDto>> GetStockItemsAsync(int? materialId = null, bool includeInactive = false)
|
||||
{
|
||||
var url = $"api/stock-items?includeInactive={includeInactive}";
|
||||
if (materialId.HasValue)
|
||||
url += $"&materialId={materialId.Value}";
|
||||
return await _http.GetFromJsonAsync<List<ApiStockItemDto>>(url) ?? [];
|
||||
}
|
||||
|
||||
public async Task<ApiStockItemDto?> CreateStockItemAsync(int materialId, string length, string? name, int quantityOnHand, string? notes)
|
||||
{
|
||||
var body = new
|
||||
{
|
||||
MaterialId = materialId,
|
||||
Length = length,
|
||||
Name = name,
|
||||
QuantityOnHand = quantityOnHand,
|
||||
Notes = notes
|
||||
};
|
||||
var response = await _http.PostAsJsonAsync("api/stock-items", body);
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.Conflict)
|
||||
{
|
||||
var error = await response.Content.ReadAsStringAsync();
|
||||
throw new ApiConflictException(error);
|
||||
}
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadFromJsonAsync<ApiStockItemDto>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Jobs
|
||||
|
||||
public async Task<List<ApiJobDto>> GetJobsAsync()
|
||||
{
|
||||
return await _http.GetFromJsonAsync<List<ApiJobDto>>("api/jobs") ?? [];
|
||||
}
|
||||
|
||||
public async Task<ApiJobDetailDto?> GetJobAsync(int id)
|
||||
{
|
||||
return await _http.GetFromJsonAsync<ApiJobDetailDto>($"api/jobs/{id}");
|
||||
}
|
||||
|
||||
public async Task<ApiJobDetailDto?> CreateJobAsync(string? name, string? customer, int? cuttingToolId, string? notes)
|
||||
{
|
||||
var response = await _http.PostAsJsonAsync("api/jobs", new { Name = name, Customer = customer, CuttingToolId = cuttingToolId, Notes = notes });
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadFromJsonAsync<ApiJobDetailDto>();
|
||||
}
|
||||
|
||||
public async Task<ApiJobDetailDto?> UpdateJobAsync(int id, string? name, string? customer, int? cuttingToolId, string? notes)
|
||||
{
|
||||
var response = await _http.PutAsJsonAsync($"api/jobs/{id}", new { Name = name, Customer = customer, CuttingToolId = cuttingToolId, Notes = notes });
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadFromJsonAsync<ApiJobDetailDto>();
|
||||
}
|
||||
|
||||
public async Task DeleteJobAsync(int id)
|
||||
{
|
||||
var response = await _http.DeleteAsync($"api/jobs/{id}");
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
public async Task<ApiJobPartDto?> AddJobPartAsync(int jobId, int materialId, string name, string length, int quantity)
|
||||
{
|
||||
var response = await _http.PostAsJsonAsync($"api/jobs/{jobId}/parts", new { MaterialId = materialId, Name = name, Length = length, Quantity = quantity });
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadFromJsonAsync<ApiJobPartDto>();
|
||||
}
|
||||
|
||||
public async Task<ApiJobPartDto?> UpdateJobPartAsync(int jobId, int partId, int? materialId, string? name, string? length, int? quantity)
|
||||
{
|
||||
var response = await _http.PutAsJsonAsync($"api/jobs/{jobId}/parts/{partId}", new { MaterialId = materialId, Name = name, Length = length, Quantity = quantity });
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadFromJsonAsync<ApiJobPartDto>();
|
||||
}
|
||||
|
||||
public async Task DeleteJobPartAsync(int jobId, int partId)
|
||||
{
|
||||
var response = await _http.DeleteAsync($"api/jobs/{jobId}/parts/{partId}");
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
public async Task<ApiJobStockDto?> AddJobStockAsync(int jobId, int materialId, int? stockItemId, string length, int quantity, bool isCustomLength, int priority)
|
||||
{
|
||||
var response = await _http.PostAsJsonAsync($"api/jobs/{jobId}/stock", new
|
||||
{
|
||||
MaterialId = materialId,
|
||||
StockItemId = stockItemId,
|
||||
Length = length,
|
||||
Quantity = quantity,
|
||||
IsCustomLength = isCustomLength,
|
||||
Priority = priority
|
||||
});
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadFromJsonAsync<ApiJobStockDto>();
|
||||
}
|
||||
|
||||
public async Task DeleteJobStockAsync(int jobId, int stockId)
|
||||
{
|
||||
var response = await _http.DeleteAsync($"api/jobs/{jobId}/stock/{stockId}");
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
public async Task<ApiPackResponseDto?> PackJobAsync(int jobId, decimal? kerfOverride = null)
|
||||
{
|
||||
var response = await _http.PostAsJsonAsync($"api/jobs/{jobId}/pack", new { KerfOverride = kerfOverride });
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadFromJsonAsync<ApiPackResponseDto>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cutting Tools
|
||||
|
||||
public async Task<List<ApiCuttingToolDto>> GetCuttingToolsAsync(bool includeInactive = false)
|
||||
{
|
||||
return await _http.GetFromJsonAsync<List<ApiCuttingToolDto>>($"api/cutting-tools?includeInactive={includeInactive}") ?? [];
|
||||
}
|
||||
|
||||
public async Task<ApiCuttingToolDto?> GetDefaultCuttingToolAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _http.GetFromJsonAsync<ApiCuttingToolDto>("api/cutting-tools/default");
|
||||
}
|
||||
catch (HttpRequestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Offerings
|
||||
|
||||
public async Task<List<ApiOfferingDto>> GetOfferingsForSupplierAsync(int supplierId)
|
||||
{
|
||||
return await _http.GetFromJsonAsync<List<ApiOfferingDto>>($"api/suppliers/{supplierId}/offerings") ?? [];
|
||||
}
|
||||
|
||||
public async Task<List<ApiOfferingDto>> GetOfferingsForStockItemAsync(int stockItemId)
|
||||
{
|
||||
return await _http.GetFromJsonAsync<List<ApiOfferingDto>>($"api/stock-items/{stockItemId}/offerings") ?? [];
|
||||
}
|
||||
|
||||
public async Task<ApiOfferingDto?> CreateOfferingAsync(int supplierId, int stockItemId,
|
||||
string? partNumber, string? supplierDescription, decimal? price, string? notes)
|
||||
{
|
||||
var body = new
|
||||
{
|
||||
StockItemId = stockItemId,
|
||||
PartNumber = partNumber,
|
||||
SupplierDescription = supplierDescription,
|
||||
Price = price,
|
||||
Notes = notes
|
||||
};
|
||||
var response = await _http.PostAsJsonAsync($"api/suppliers/{supplierId}/offerings", body);
|
||||
if (response.StatusCode == System.Net.HttpStatusCode.Conflict)
|
||||
{
|
||||
var error = await response.Content.ReadAsStringAsync();
|
||||
throw new ApiConflictException(error);
|
||||
}
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadFromJsonAsync<ApiOfferingDto>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Thrown when the API returns 409 Conflict (duplicate resource).
|
||||
/// </summary>
|
||||
public class ApiConflictException : Exception
|
||||
{
|
||||
public ApiConflictException(string message) : base(message) { }
|
||||
}
|
||||
|
||||
#region API Response DTOs — Jobs & Cutting Tools
|
||||
|
||||
public class ApiJobDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string JobNumber { get; set; } = string.Empty;
|
||||
public string? Name { get; set; }
|
||||
public string? Customer { get; set; }
|
||||
public int? CuttingToolId { get; set; }
|
||||
public string? CuttingToolName { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public int PartCount { get; set; }
|
||||
public int StockCount { get; set; }
|
||||
}
|
||||
|
||||
public class ApiJobDetailDto : ApiJobDto
|
||||
{
|
||||
public List<ApiJobPartDto> Parts { get; set; } = new();
|
||||
public List<ApiJobStockDto> Stock { get; set; } = new();
|
||||
}
|
||||
|
||||
public class ApiJobPartDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int JobId { get; set; }
|
||||
public int MaterialId { get; set; }
|
||||
public string MaterialName { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public decimal LengthInches { get; set; }
|
||||
public string LengthFormatted { get; set; } = string.Empty;
|
||||
public int Quantity { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
}
|
||||
|
||||
public class ApiJobStockDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int JobId { get; set; }
|
||||
public int MaterialId { get; set; }
|
||||
public string MaterialName { get; set; } = string.Empty;
|
||||
public int? StockItemId { get; set; }
|
||||
public decimal LengthInches { get; set; }
|
||||
public string LengthFormatted { get; set; } = string.Empty;
|
||||
public int Quantity { get; set; }
|
||||
public bool IsCustomLength { get; set; }
|
||||
public int Priority { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
}
|
||||
|
||||
public class ApiCuttingToolDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public decimal KerfInches { get; set; }
|
||||
public bool IsDefault { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
|
||||
public class ApiPackResponseDto
|
||||
{
|
||||
public List<ApiMaterialPackResultDto> Materials { get; set; } = new();
|
||||
public ApiPackingSummaryDto Summary { get; set; } = new();
|
||||
}
|
||||
|
||||
public class ApiMaterialPackResultDto
|
||||
{
|
||||
public int MaterialId { get; set; }
|
||||
public string MaterialName { get; set; } = string.Empty;
|
||||
public List<ApiPackedBinDto> InStockBins { get; set; } = new();
|
||||
public List<ApiPackedBinDto> ToBePurchasedBins { get; set; } = new();
|
||||
public List<ApiPackedItemDto> ItemsNotPlaced { get; set; } = new();
|
||||
public ApiMaterialPackingSummaryDto Summary { get; set; } = new();
|
||||
}
|
||||
|
||||
public class ApiPackedBinDto
|
||||
{
|
||||
public double LengthInches { get; set; }
|
||||
public string LengthFormatted { get; set; } = string.Empty;
|
||||
public double UsedInches { get; set; }
|
||||
public string UsedFormatted { get; set; } = string.Empty;
|
||||
public double WasteInches { get; set; }
|
||||
public string WasteFormatted { get; set; } = string.Empty;
|
||||
public double Efficiency { get; set; }
|
||||
public List<ApiPackedItemDto> Items { get; set; } = new();
|
||||
}
|
||||
|
||||
public class ApiPackedItemDto
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public double LengthInches { get; set; }
|
||||
public string LengthFormatted { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class ApiPackingSummaryDto
|
||||
{
|
||||
public int TotalInStockBins { get; set; }
|
||||
public int TotalToBePurchasedBins { get; set; }
|
||||
public int TotalPieces { get; set; }
|
||||
public double TotalMaterialInches { get; set; }
|
||||
public string TotalMaterialFormatted { get; set; } = string.Empty;
|
||||
public double TotalUsedInches { get; set; }
|
||||
public string TotalUsedFormatted { get; set; } = string.Empty;
|
||||
public double TotalWasteInches { get; set; }
|
||||
public string TotalWasteFormatted { get; set; } = string.Empty;
|
||||
public double Efficiency { get; set; }
|
||||
public int TotalItemsNotPlaced { get; set; }
|
||||
public List<ApiMaterialPackingSummaryDto> MaterialSummaries { get; set; } = new();
|
||||
}
|
||||
|
||||
public class ApiMaterialPackingSummaryDto
|
||||
{
|
||||
public int MaterialId { get; set; }
|
||||
public string MaterialName { get; set; } = string.Empty;
|
||||
public int InStockBins { get; set; }
|
||||
public int ToBePurchasedBins { get; set; }
|
||||
public int TotalPieces { get; set; }
|
||||
public double TotalMaterialInches { get; set; }
|
||||
public double TotalUsedInches { get; set; }
|
||||
public double TotalWasteInches { get; set; }
|
||||
public double Efficiency { get; set; }
|
||||
public int ItemsNotPlaced { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region API Response DTOs — Inventory
|
||||
|
||||
public class ApiSupplierDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string? ContactInfo { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
|
||||
public class ApiMaterialDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Shape { get; set; } = string.Empty;
|
||||
public string Type { get; set; } = string.Empty;
|
||||
public string? Grade { get; set; }
|
||||
public string Size { get; set; } = string.Empty;
|
||||
public string? Description { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public ApiMaterialDimensionsDto? Dimensions { get; set; }
|
||||
}
|
||||
|
||||
public class ApiMaterialDimensionsDto
|
||||
{
|
||||
public string DimensionType { get; set; } = string.Empty;
|
||||
public Dictionary<string, decimal> Values { get; set; } = new();
|
||||
}
|
||||
|
||||
public class ApiStockItemDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int MaterialId { get; set; }
|
||||
public string MaterialName { get; set; } = string.Empty;
|
||||
public decimal LengthInches { get; set; }
|
||||
public string LengthFormatted { get; set; } = string.Empty;
|
||||
public string? Name { get; set; }
|
||||
public int QuantityOnHand { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
|
||||
public class ApiOfferingDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int SupplierId { get; set; }
|
||||
public string? SupplierName { get; set; }
|
||||
public int StockItemId { get; set; }
|
||||
public string? MaterialName { get; set; }
|
||||
public decimal? LengthInches { get; set; }
|
||||
public string? LengthFormatted { get; set; }
|
||||
public string? PartNumber { get; set; }
|
||||
public string? SupplierDescription { get; set; }
|
||||
public decimal? Price { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.4" />
|
||||
<PackageReference Include="ModelContextProtocol" Version="0.7.0-preview.1" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
816
CutList.Mcp/InventoryTools.cs
Normal file
816
CutList.Mcp/InventoryTools.cs
Normal file
@@ -0,0 +1,816 @@
|
||||
using System.ComponentModel;
|
||||
using CutList.Core.Formatting;
|
||||
using ModelContextProtocol.Server;
|
||||
|
||||
namespace CutList.Mcp;
|
||||
|
||||
/// <summary>
|
||||
/// MCP tools for inventory management - suppliers, materials, stock items, and offerings.
|
||||
/// All calls go through the CutList.Web REST API via ApiClient.
|
||||
/// </summary>
|
||||
[McpServerToolType]
|
||||
public class InventoryTools
|
||||
{
|
||||
private readonly ApiClient _api;
|
||||
|
||||
public InventoryTools(ApiClient api)
|
||||
{
|
||||
_api = api;
|
||||
}
|
||||
|
||||
#region Suppliers
|
||||
|
||||
[McpServerTool(Name = "list_suppliers"), Description("Lists all suppliers in the system.")]
|
||||
public async Task<SupplierListResult> ListSuppliers(
|
||||
[Description("Include inactive suppliers (default false)")]
|
||||
bool includeInactive = false)
|
||||
{
|
||||
var suppliers = await _api.GetSuppliersAsync(includeInactive);
|
||||
|
||||
return new SupplierListResult
|
||||
{
|
||||
Success = true,
|
||||
Suppliers = suppliers.Select(s => new SupplierDto
|
||||
{
|
||||
Id = s.Id,
|
||||
Name = s.Name,
|
||||
ContactInfo = s.ContactInfo,
|
||||
Notes = s.Notes,
|
||||
IsActive = s.IsActive
|
||||
}).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "add_supplier"), Description("Adds a new supplier to the system.")]
|
||||
public async Task<SupplierResult> AddSupplier(
|
||||
[Description("Supplier name (e.g., 'O'Neal Steel')")]
|
||||
string name,
|
||||
[Description("Contact info - website, phone, email, etc.")]
|
||||
string? contactInfo = null,
|
||||
[Description("Notes about the supplier")]
|
||||
string? notes = null)
|
||||
{
|
||||
var supplier = await _api.CreateSupplierAsync(name, contactInfo, notes);
|
||||
|
||||
return new SupplierResult
|
||||
{
|
||||
Success = true,
|
||||
Supplier = supplier != null ? new SupplierDto
|
||||
{
|
||||
Id = supplier.Id,
|
||||
Name = supplier.Name,
|
||||
ContactInfo = supplier.ContactInfo,
|
||||
Notes = supplier.Notes,
|
||||
IsActive = supplier.IsActive
|
||||
} : null
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Materials
|
||||
|
||||
[McpServerTool(Name = "list_materials"), Description("Lists all materials (shape/size combinations) in the system.")]
|
||||
public async Task<MaterialListResult> ListMaterials(
|
||||
[Description("Filter by shape (e.g., 'Angle', 'FlatBar', 'RoundTube')")]
|
||||
string? shape = null,
|
||||
[Description("Include inactive materials (default false)")]
|
||||
bool includeInactive = false)
|
||||
{
|
||||
var materials = await _api.GetMaterialsAsync(shape, includeInactive);
|
||||
|
||||
return new MaterialListResult
|
||||
{
|
||||
Success = true,
|
||||
Materials = materials.Select(MapMaterial).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "add_material"), Description("Adds a new material (shape/size combination) to the system with optional parsed dimensions.")]
|
||||
public async Task<MaterialResult> AddMaterial(
|
||||
[Description("Material shape (e.g., 'Angle', 'FlatBar', 'RoundTube', 'SquareTube', 'Channel', 'IBeam', 'Pipe')")]
|
||||
string shape,
|
||||
[Description("Material size string (e.g., '2 x 2 x 1/4'). If not provided, will be auto-generated from dimensions.")]
|
||||
string? size = null,
|
||||
[Description("Optional description")]
|
||||
string? description = null,
|
||||
[Description("Diameter in inches (for Round Bar)")]
|
||||
double? diameter = null,
|
||||
[Description("Outer diameter in inches (for Round Tube)")]
|
||||
double? outerDiameter = null,
|
||||
[Description("Width in inches (for Flat Bar, Rectangular Tube)")]
|
||||
double? width = null,
|
||||
[Description("Height in inches (for Rectangular Tube, Channel, I-Beam)")]
|
||||
double? height = null,
|
||||
[Description("Size in inches (for Square Bar, Square Tube - the side length)")]
|
||||
double? squareSize = null,
|
||||
[Description("Thickness in inches (for Flat Bar, Angle)")]
|
||||
double? thickness = null,
|
||||
[Description("Wall thickness in inches (for tubes, pipe)")]
|
||||
double? wall = null,
|
||||
[Description("Leg 1 length in inches (for Angle)")]
|
||||
double? leg1 = null,
|
||||
[Description("Leg 2 length in inches (for Angle)")]
|
||||
double? leg2 = null,
|
||||
[Description("Flange width in inches (for Channel)")]
|
||||
double? flange = null,
|
||||
[Description("Web thickness in inches (for Channel)")]
|
||||
double? web = null,
|
||||
[Description("Weight per foot in lbs (for I-Beam)")]
|
||||
double? weightPerFoot = null,
|
||||
[Description("Nominal pipe size in inches (for Pipe)")]
|
||||
double? nominalSize = null,
|
||||
[Description("Schedule (for Pipe, e.g., '40', '80', 'STD')")]
|
||||
string? schedule = null)
|
||||
{
|
||||
// Build dimensions dictionary from individual parameters
|
||||
var dimensions = new Dictionary<string, decimal>();
|
||||
if (diameter.HasValue) dimensions["Diameter"] = (decimal)diameter.Value;
|
||||
if (outerDiameter.HasValue) dimensions["OuterDiameter"] = (decimal)outerDiameter.Value;
|
||||
if (width.HasValue) dimensions["Width"] = (decimal)width.Value;
|
||||
if (height.HasValue) dimensions["Height"] = (decimal)height.Value;
|
||||
if (squareSize.HasValue) dimensions["Size"] = (decimal)squareSize.Value;
|
||||
if (thickness.HasValue) dimensions["Thickness"] = (decimal)thickness.Value;
|
||||
if (wall.HasValue) dimensions["Wall"] = (decimal)wall.Value;
|
||||
if (leg1.HasValue) dimensions["Leg1"] = (decimal)leg1.Value;
|
||||
if (leg2.HasValue) dimensions["Leg2"] = (decimal)leg2.Value;
|
||||
if (flange.HasValue) dimensions["Flange"] = (decimal)flange.Value;
|
||||
if (web.HasValue) dimensions["Web"] = (decimal)web.Value;
|
||||
if (weightPerFoot.HasValue) dimensions["WeightPerFoot"] = (decimal)weightPerFoot.Value;
|
||||
if (nominalSize.HasValue) dimensions["NominalSize"] = (decimal)nominalSize.Value;
|
||||
|
||||
try
|
||||
{
|
||||
var material = await _api.CreateMaterialAsync(
|
||||
shape, size, description, null, null,
|
||||
dimensions.Count > 0 ? dimensions : null);
|
||||
|
||||
if (material == null)
|
||||
return new MaterialResult { Success = false, Error = "Failed to create material" };
|
||||
|
||||
return new MaterialResult
|
||||
{
|
||||
Success = true,
|
||||
Material = MapMaterial(material)
|
||||
};
|
||||
}
|
||||
catch (ApiConflictException ex)
|
||||
{
|
||||
return new MaterialResult { Success = false, Error = ex.Message };
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
return new MaterialResult { Success = false, Error = ex.Message };
|
||||
}
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "search_materials"), Description("Search for materials by shape with a target dimension value and tolerance. The primary dimension for each shape is searched (e.g., diameter for RoundBar, leg size for Angle).")]
|
||||
public async Task<MaterialListResult> SearchMaterials(
|
||||
[Description("Shape to search (e.g., 'RoundBar', 'Angle', 'FlatBar')")]
|
||||
string shape,
|
||||
[Description("Target dimension value in inches (or lbs for weightPerFoot)")]
|
||||
double targetValue,
|
||||
[Description("Tolerance value - returns results within +/- this amount (default 0.1)")]
|
||||
double tolerance = 0.1)
|
||||
{
|
||||
var materials = await _api.SearchMaterialsAsync(shape, (decimal)targetValue, (decimal)tolerance);
|
||||
|
||||
return new MaterialListResult
|
||||
{
|
||||
Success = true,
|
||||
Materials = materials.Select(MapMaterial).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stock Items
|
||||
|
||||
[McpServerTool(Name = "list_stock_items"), Description("Lists stock items (material lengths available in inventory).")]
|
||||
public async Task<StockItemListResult> ListStockItems(
|
||||
[Description("Filter by material ID")]
|
||||
int? materialId = null,
|
||||
[Description("Filter by shape (e.g., 'Angle')")]
|
||||
string? shape = null,
|
||||
[Description("Include inactive stock items (default false)")]
|
||||
bool includeInactive = false)
|
||||
{
|
||||
List<ApiStockItemDto> items;
|
||||
|
||||
if (materialId.HasValue)
|
||||
{
|
||||
items = await _api.GetStockItemsAsync(materialId, includeInactive);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(shape))
|
||||
{
|
||||
// Get materials for this shape, then get stock items for each
|
||||
var materials = await _api.GetMaterialsAsync(shape, includeInactive);
|
||||
var allItems = new List<ApiStockItemDto>();
|
||||
foreach (var mat in materials)
|
||||
{
|
||||
var matItems = await _api.GetStockItemsAsync(mat.Id, includeInactive);
|
||||
allItems.AddRange(matItems);
|
||||
}
|
||||
items = allItems;
|
||||
}
|
||||
else
|
||||
{
|
||||
items = await _api.GetStockItemsAsync(includeInactive: includeInactive);
|
||||
}
|
||||
|
||||
return new StockItemListResult
|
||||
{
|
||||
Success = true,
|
||||
StockItems = items.Select(s => new StockItemDto
|
||||
{
|
||||
Id = s.Id,
|
||||
MaterialId = s.MaterialId,
|
||||
MaterialName = s.MaterialName,
|
||||
LengthInches = s.LengthInches,
|
||||
LengthFormatted = s.LengthFormatted,
|
||||
Name = s.Name,
|
||||
QuantityOnHand = s.QuantityOnHand,
|
||||
Notes = s.Notes,
|
||||
IsActive = s.IsActive
|
||||
}).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "add_stock_item"), Description("Adds a new stock item (a specific length of material that can be stocked).")]
|
||||
public async Task<StockItemResult> AddStockItem(
|
||||
[Description("Material ID (use list_materials to find IDs)")]
|
||||
int materialId,
|
||||
[Description("Stock length (e.g., '20'', '240', '20 ft')")]
|
||||
string length,
|
||||
[Description("Optional name/label for this stock item")]
|
||||
string? name = null,
|
||||
[Description("Initial quantity on hand (default 0)")]
|
||||
int quantityOnHand = 0,
|
||||
[Description("Notes")]
|
||||
string? notes = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var stockItem = await _api.CreateStockItemAsync(materialId, length, name, quantityOnHand, notes);
|
||||
|
||||
if (stockItem == null)
|
||||
return new StockItemResult { Success = false, Error = "Failed to create stock item" };
|
||||
|
||||
return new StockItemResult
|
||||
{
|
||||
Success = true,
|
||||
StockItem = new StockItemDto
|
||||
{
|
||||
Id = stockItem.Id,
|
||||
MaterialId = stockItem.MaterialId,
|
||||
MaterialName = stockItem.MaterialName,
|
||||
LengthInches = stockItem.LengthInches,
|
||||
LengthFormatted = stockItem.LengthFormatted,
|
||||
Name = stockItem.Name,
|
||||
QuantityOnHand = stockItem.QuantityOnHand,
|
||||
Notes = stockItem.Notes,
|
||||
IsActive = stockItem.IsActive
|
||||
}
|
||||
};
|
||||
}
|
||||
catch (ApiConflictException ex)
|
||||
{
|
||||
return new StockItemResult { Success = false, Error = ex.Message };
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
return new StockItemResult { Success = false, Error = ex.Message };
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Supplier Offerings
|
||||
|
||||
[McpServerTool(Name = "list_supplier_offerings"), Description("Lists supplier offerings (what suppliers sell for each stock item).")]
|
||||
public async Task<SupplierOfferingListResult> ListSupplierOfferings(
|
||||
[Description("Filter by supplier ID")]
|
||||
int? supplierId = null,
|
||||
[Description("Filter by stock item ID")]
|
||||
int? stockItemId = null,
|
||||
[Description("Filter by material ID")]
|
||||
int? materialId = null)
|
||||
{
|
||||
List<ApiOfferingDto> offerings;
|
||||
|
||||
if (supplierId.HasValue)
|
||||
{
|
||||
offerings = await _api.GetOfferingsForSupplierAsync(supplierId.Value);
|
||||
// Apply additional filters client-side
|
||||
if (stockItemId.HasValue)
|
||||
offerings = offerings.Where(o => o.StockItemId == stockItemId.Value).ToList();
|
||||
if (materialId.HasValue)
|
||||
{
|
||||
// Need to get stock items for this material to filter
|
||||
var stockItems = await _api.GetStockItemsAsync(materialId);
|
||||
var stockItemIds = stockItems.Select(s => s.Id).ToHashSet();
|
||||
offerings = offerings.Where(o => stockItemIds.Contains(o.StockItemId)).ToList();
|
||||
}
|
||||
}
|
||||
else if (stockItemId.HasValue)
|
||||
{
|
||||
offerings = await _api.GetOfferingsForStockItemAsync(stockItemId.Value);
|
||||
}
|
||||
else if (materialId.HasValue)
|
||||
{
|
||||
// Get stock items for this material, then aggregate offerings
|
||||
var stockItems = await _api.GetStockItemsAsync(materialId);
|
||||
var allOfferings = new List<ApiOfferingDto>();
|
||||
foreach (var si in stockItems)
|
||||
{
|
||||
var siOfferings = await _api.GetOfferingsForStockItemAsync(si.Id);
|
||||
allOfferings.AddRange(siOfferings);
|
||||
}
|
||||
offerings = allOfferings;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No filter - get all suppliers then aggregate
|
||||
var suppliers = await _api.GetSuppliersAsync();
|
||||
var allOfferings = new List<ApiOfferingDto>();
|
||||
foreach (var s in suppliers)
|
||||
{
|
||||
var sOfferings = await _api.GetOfferingsForSupplierAsync(s.Id);
|
||||
allOfferings.AddRange(sOfferings);
|
||||
}
|
||||
offerings = allOfferings;
|
||||
}
|
||||
|
||||
return new SupplierOfferingListResult
|
||||
{
|
||||
Success = true,
|
||||
Offerings = offerings.Select(o => new SupplierOfferingDto
|
||||
{
|
||||
Id = o.Id,
|
||||
SupplierId = o.SupplierId,
|
||||
SupplierName = o.SupplierName ?? string.Empty,
|
||||
StockItemId = o.StockItemId,
|
||||
MaterialName = o.MaterialName ?? string.Empty,
|
||||
LengthFormatted = o.LengthFormatted ?? string.Empty,
|
||||
PartNumber = o.PartNumber,
|
||||
SupplierDescription = o.SupplierDescription,
|
||||
Price = o.Price,
|
||||
Notes = o.Notes
|
||||
}).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "add_supplier_offering"), Description("Adds a supplier offering - links a supplier to a stock item with their part number and pricing.")]
|
||||
public async Task<SupplierOfferingResult> AddSupplierOffering(
|
||||
[Description("Supplier ID (use list_suppliers to find)")]
|
||||
int supplierId,
|
||||
[Description("Stock item ID (use list_stock_items to find)")]
|
||||
int stockItemId,
|
||||
[Description("Supplier's part number")]
|
||||
string? partNumber = null,
|
||||
[Description("Supplier's description of the item")]
|
||||
string? supplierDescription = null,
|
||||
[Description("Price per unit")]
|
||||
decimal? price = null,
|
||||
[Description("Notes")]
|
||||
string? notes = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var offering = await _api.CreateOfferingAsync(supplierId, stockItemId, partNumber, supplierDescription, price, notes);
|
||||
|
||||
if (offering == null)
|
||||
return new SupplierOfferingResult { Success = false, Error = "Failed to create offering" };
|
||||
|
||||
return new SupplierOfferingResult
|
||||
{
|
||||
Success = true,
|
||||
Offering = new SupplierOfferingDto
|
||||
{
|
||||
Id = offering.Id,
|
||||
SupplierId = offering.SupplierId,
|
||||
SupplierName = offering.SupplierName ?? string.Empty,
|
||||
StockItemId = offering.StockItemId,
|
||||
MaterialName = offering.MaterialName ?? string.Empty,
|
||||
LengthFormatted = offering.LengthFormatted ?? string.Empty,
|
||||
PartNumber = offering.PartNumber,
|
||||
SupplierDescription = offering.SupplierDescription,
|
||||
Price = offering.Price,
|
||||
Notes = offering.Notes
|
||||
}
|
||||
};
|
||||
}
|
||||
catch (ApiConflictException ex)
|
||||
{
|
||||
return new SupplierOfferingResult { Success = false, Error = ex.Message };
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
return new SupplierOfferingResult { Success = false, Error = ex.Message };
|
||||
}
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "add_stock_with_offering"), Description("Convenience method: adds a material (if needed), stock item (if needed), and supplier offering all at once.")]
|
||||
public async Task<AddStockWithOfferingResult> AddStockWithOffering(
|
||||
[Description("Supplier ID (use list_suppliers or add_supplier first)")]
|
||||
int supplierId,
|
||||
[Description("Material shape (e.g., 'Angle', 'FlatBar')")]
|
||||
string shape,
|
||||
[Description("Material size (e.g., '2 x 2 x 1/4')")]
|
||||
string size,
|
||||
[Description("Stock length (e.g., '20'', '240')")]
|
||||
string length,
|
||||
[Description("Material type: Steel, Aluminum, Stainless, Brass, Copper (default: Steel)")]
|
||||
string type = "Steel",
|
||||
[Description("Grade or specification (e.g., 'A36', 'Hot Roll', '304', '6061-T6')")]
|
||||
string? grade = null,
|
||||
[Description("Supplier's part number")]
|
||||
string? partNumber = null,
|
||||
[Description("Supplier's description")]
|
||||
string? supplierDescription = null,
|
||||
[Description("Price per unit")]
|
||||
decimal? price = null)
|
||||
{
|
||||
// Parse length for formatted display
|
||||
double lengthInches;
|
||||
try
|
||||
{
|
||||
lengthInches = double.TryParse(length.Trim(), out var plain)
|
||||
? plain
|
||||
: ArchUnits.ParseToInches(length);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new AddStockWithOfferingResult
|
||||
{
|
||||
Success = false,
|
||||
Error = $"Could not parse length: {length}"
|
||||
};
|
||||
}
|
||||
|
||||
// Step 1: Find or create material
|
||||
bool materialCreated = false;
|
||||
ApiMaterialDto? material = null;
|
||||
|
||||
// Search for existing material by shape and size
|
||||
var materials = await _api.GetMaterialsAsync(shape);
|
||||
material = materials.FirstOrDefault(m =>
|
||||
m.Size.Equals(size, StringComparison.OrdinalIgnoreCase) &&
|
||||
m.Type.Equals(type, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(m.Grade, grade, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (material == null)
|
||||
{
|
||||
// Parse dimensions from size string for the API
|
||||
var dimensions = ParseSizeStringToDimensions(shape, size);
|
||||
|
||||
try
|
||||
{
|
||||
material = await _api.CreateMaterialAsync(shape, size, null, type, grade, dimensions);
|
||||
materialCreated = true;
|
||||
}
|
||||
catch (ApiConflictException)
|
||||
{
|
||||
// Race condition - material was created between check and create, re-fetch
|
||||
materials = await _api.GetMaterialsAsync(shape);
|
||||
material = materials.FirstOrDefault(m =>
|
||||
m.Size.Equals(size, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
return new AddStockWithOfferingResult { Success = false, Error = $"Failed to create material: {ex.Message}" };
|
||||
}
|
||||
}
|
||||
|
||||
if (material == null)
|
||||
return new AddStockWithOfferingResult { Success = false, Error = "Failed to find or create material" };
|
||||
|
||||
// Step 2: Find or create stock item
|
||||
bool stockItemCreated = false;
|
||||
var stockItems = await _api.GetStockItemsAsync(material.Id);
|
||||
var stockItem = stockItems.FirstOrDefault(s => Math.Abs((double)s.LengthInches - lengthInches) < 0.01);
|
||||
|
||||
if (stockItem == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
stockItem = await _api.CreateStockItemAsync(material.Id, length, null, 0, null);
|
||||
stockItemCreated = true;
|
||||
}
|
||||
catch (ApiConflictException)
|
||||
{
|
||||
// Race condition - re-fetch
|
||||
stockItems = await _api.GetStockItemsAsync(material.Id);
|
||||
stockItem = stockItems.FirstOrDefault(s => Math.Abs((double)s.LengthInches - lengthInches) < 0.01);
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
return new AddStockWithOfferingResult
|
||||
{
|
||||
Success = false,
|
||||
Error = $"Failed to create stock item: {ex.Message}",
|
||||
MaterialCreated = materialCreated
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (stockItem == null)
|
||||
return new AddStockWithOfferingResult
|
||||
{
|
||||
Success = false,
|
||||
Error = "Failed to find or create stock item",
|
||||
MaterialCreated = materialCreated
|
||||
};
|
||||
|
||||
// Step 3: Create offering
|
||||
try
|
||||
{
|
||||
var offering = await _api.CreateOfferingAsync(supplierId, stockItem.Id, partNumber, supplierDescription, price, null);
|
||||
|
||||
return new AddStockWithOfferingResult
|
||||
{
|
||||
Success = true,
|
||||
MaterialId = material.Id,
|
||||
MaterialName = $"{material.Shape} - {material.Size}",
|
||||
MaterialCreated = materialCreated,
|
||||
StockItemId = stockItem.Id,
|
||||
StockItemCreated = stockItemCreated,
|
||||
LengthFormatted = ArchUnits.FormatFromInches(lengthInches),
|
||||
OfferingId = offering?.Id ?? 0,
|
||||
PartNumber = partNumber,
|
||||
SupplierDescription = supplierDescription,
|
||||
Price = price
|
||||
};
|
||||
}
|
||||
catch (ApiConflictException)
|
||||
{
|
||||
return new AddStockWithOfferingResult
|
||||
{
|
||||
Success = false,
|
||||
Error = $"Offering for this supplier and stock item already exists",
|
||||
MaterialCreated = materialCreated,
|
||||
StockItemCreated = stockItemCreated
|
||||
};
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
return new AddStockWithOfferingResult
|
||||
{
|
||||
Success = false,
|
||||
Error = $"Failed to create offering: {ex.Message}",
|
||||
MaterialCreated = materialCreated,
|
||||
StockItemCreated = stockItemCreated
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
private static MaterialDto MapMaterial(ApiMaterialDto m)
|
||||
{
|
||||
var dto = new MaterialDto
|
||||
{
|
||||
Id = m.Id,
|
||||
Shape = m.Shape,
|
||||
Size = m.Size,
|
||||
Description = m.Description,
|
||||
DisplayName = $"{m.Shape} - {m.Size}",
|
||||
IsActive = m.IsActive,
|
||||
};
|
||||
|
||||
if (m.Dimensions != null)
|
||||
{
|
||||
dto.Dimensions = MapDimensions(m.Dimensions);
|
||||
}
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
private static MaterialDimensionsDto MapDimensions(ApiMaterialDimensionsDto d)
|
||||
{
|
||||
var dto = new MaterialDimensionsDto();
|
||||
var v = d.Values;
|
||||
|
||||
if (v.TryGetValue("Diameter", out var diameter)) dto.Diameter = (double)diameter;
|
||||
if (v.TryGetValue("OuterDiameter", out var od)) dto.OuterDiameter = (double)od;
|
||||
if (v.TryGetValue("Width", out var width)) dto.Width = (double)width;
|
||||
if (v.TryGetValue("Height", out var height)) dto.Height = (double)height;
|
||||
if (v.TryGetValue("Size", out var size)) dto.Size = (double)size;
|
||||
if (v.TryGetValue("Thickness", out var thickness)) dto.Thickness = (double)thickness;
|
||||
if (v.TryGetValue("Wall", out var wall)) dto.Wall = (double)wall;
|
||||
if (v.TryGetValue("Leg1", out var leg1)) dto.Leg1 = (double)leg1;
|
||||
if (v.TryGetValue("Leg2", out var leg2)) dto.Leg2 = (double)leg2;
|
||||
if (v.TryGetValue("Flange", out var flange)) dto.Flange = (double)flange;
|
||||
if (v.TryGetValue("Web", out var web)) dto.Web = (double)web;
|
||||
if (v.TryGetValue("WeightPerFoot", out var wpf)) dto.WeightPerFoot = (double)wpf;
|
||||
if (v.TryGetValue("NominalSize", out var ns)) dto.NominalSize = (double)ns;
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a size string into a dimensions dictionary based on shape.
|
||||
/// Format: values separated by 'x' (e.g., "1 1/2 x 1/8", "2 x 2 x 1/4")
|
||||
/// </summary>
|
||||
private static Dictionary<string, decimal>? ParseSizeStringToDimensions(string shape, string sizeString)
|
||||
{
|
||||
var parts = sizeString.Split('x', StringSplitOptions.TrimEntries)
|
||||
.Select(ParseDimension)
|
||||
.ToArray();
|
||||
|
||||
if (parts.Length == 0 || parts.All(p => !p.HasValue))
|
||||
return null;
|
||||
|
||||
decimal D(int i) => (decimal)parts[i]!.Value;
|
||||
|
||||
var shapeLower = shape.ToLowerInvariant().Replace(" ", "");
|
||||
|
||||
return shapeLower switch
|
||||
{
|
||||
"roundbar" when parts.Length >= 1 && parts[0].HasValue
|
||||
=> new Dictionary<string, decimal> { ["Diameter"] = D(0) },
|
||||
|
||||
"roundtube" when parts.Length >= 2 && parts[0].HasValue && parts[1].HasValue
|
||||
=> new Dictionary<string, decimal> { ["OuterDiameter"] = D(0), ["Wall"] = D(1) },
|
||||
|
||||
"flatbar" when parts.Length >= 2 && parts[0].HasValue && parts[1].HasValue
|
||||
=> new Dictionary<string, decimal> { ["Width"] = D(0), ["Thickness"] = D(1) },
|
||||
|
||||
"squarebar" when parts.Length >= 1 && parts[0].HasValue
|
||||
=> new Dictionary<string, decimal> { ["Size"] = D(0) },
|
||||
|
||||
"squaretube" when parts.Length >= 2 && parts[0].HasValue && parts[1].HasValue
|
||||
=> new Dictionary<string, decimal> { ["Size"] = D(0), ["Wall"] = D(1) },
|
||||
|
||||
"rectangulartube" or "recttube" when parts.Length >= 3 && parts[0].HasValue && parts[1].HasValue && parts[2].HasValue
|
||||
=> new Dictionary<string, decimal> { ["Width"] = D(0), ["Height"] = D(1), ["Wall"] = D(2) },
|
||||
|
||||
"angle" when parts.Length >= 3 && parts[0].HasValue && parts[1].HasValue && parts[2].HasValue
|
||||
=> new Dictionary<string, decimal> { ["Leg1"] = D(0), ["Leg2"] = D(1), ["Thickness"] = D(2) },
|
||||
|
||||
"channel" when parts.Length >= 3 && parts[0].HasValue && parts[1].HasValue && parts[2].HasValue
|
||||
=> new Dictionary<string, decimal> { ["Height"] = D(0), ["Flange"] = D(1), ["Web"] = D(2) },
|
||||
|
||||
"ibeam" when parts.Length >= 2 && parts[0].HasValue && parts[1].HasValue
|
||||
=> new Dictionary<string, decimal> { ["Height"] = D(0), ["WeightPerFoot"] = D(1) },
|
||||
|
||||
"pipe" when parts.Length >= 1 && parts[0].HasValue
|
||||
=> new Dictionary<string, decimal> { ["NominalSize"] = D(0) },
|
||||
|
||||
_ => null
|
||||
};
|
||||
|
||||
static double? ParseDimension(string value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) return null;
|
||||
var processed = Fraction.ReplaceFractionsWithDecimals(value.Trim());
|
||||
return processed.Split(' ', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Sum(part => double.TryParse(part, out var d) ? d : 0) is > 0 and var total ? total : null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region DTOs
|
||||
|
||||
public class SupplierDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string? ContactInfo { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
|
||||
public class SupplierListResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string? Error { get; set; }
|
||||
public List<SupplierDto> Suppliers { get; set; } = new();
|
||||
}
|
||||
|
||||
public class SupplierResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string? Error { get; set; }
|
||||
public SupplierDto? Supplier { get; set; }
|
||||
}
|
||||
|
||||
public class MaterialDimensionsDto
|
||||
{
|
||||
public double? Diameter { get; set; }
|
||||
public double? OuterDiameter { get; set; }
|
||||
public double? Width { get; set; }
|
||||
public double? Height { get; set; }
|
||||
public double? Size { get; set; }
|
||||
public double? Thickness { get; set; }
|
||||
public double? Wall { get; set; }
|
||||
public double? Leg1 { get; set; }
|
||||
public double? Leg2 { get; set; }
|
||||
public double? Flange { get; set; }
|
||||
public double? Web { get; set; }
|
||||
public double? WeightPerFoot { get; set; }
|
||||
public double? NominalSize { get; set; }
|
||||
public string? Schedule { get; set; }
|
||||
}
|
||||
|
||||
public class MaterialDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Shape { get; set; } = string.Empty;
|
||||
public string Size { get; set; } = string.Empty;
|
||||
public string? Description { get; set; }
|
||||
public string DisplayName { get; set; } = string.Empty;
|
||||
public bool IsActive { get; set; }
|
||||
public MaterialDimensionsDto? Dimensions { get; set; }
|
||||
}
|
||||
|
||||
public class MaterialListResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string? Error { get; set; }
|
||||
public List<MaterialDto> Materials { get; set; } = new();
|
||||
}
|
||||
|
||||
public class MaterialResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string? Error { get; set; }
|
||||
public MaterialDto? Material { get; set; }
|
||||
}
|
||||
|
||||
public class StockItemDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int MaterialId { get; set; }
|
||||
public string MaterialName { get; set; } = string.Empty;
|
||||
public decimal LengthInches { get; set; }
|
||||
public string LengthFormatted { get; set; } = string.Empty;
|
||||
public string? Name { get; set; }
|
||||
public int QuantityOnHand { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
|
||||
public class StockItemListResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string? Error { get; set; }
|
||||
public List<StockItemDto> StockItems { get; set; } = new();
|
||||
}
|
||||
|
||||
public class StockItemResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string? Error { get; set; }
|
||||
public StockItemDto? StockItem { get; set; }
|
||||
}
|
||||
|
||||
public class SupplierOfferingDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int SupplierId { get; set; }
|
||||
public string SupplierName { get; set; } = string.Empty;
|
||||
public int StockItemId { get; set; }
|
||||
public string MaterialName { get; set; } = string.Empty;
|
||||
public string LengthFormatted { get; set; } = string.Empty;
|
||||
public string? PartNumber { get; set; }
|
||||
public string? SupplierDescription { get; set; }
|
||||
public decimal? Price { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
}
|
||||
|
||||
public class SupplierOfferingListResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string? Error { get; set; }
|
||||
public List<SupplierOfferingDto> Offerings { get; set; } = new();
|
||||
}
|
||||
|
||||
public class SupplierOfferingResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string? Error { get; set; }
|
||||
public SupplierOfferingDto? Offering { get; set; }
|
||||
}
|
||||
|
||||
public class AddStockWithOfferingResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string? Error { get; set; }
|
||||
public int MaterialId { get; set; }
|
||||
public string MaterialName { get; set; } = string.Empty;
|
||||
public bool MaterialCreated { get; set; }
|
||||
public int StockItemId { get; set; }
|
||||
public bool StockItemCreated { get; set; }
|
||||
public string LengthFormatted { get; set; } = string.Empty;
|
||||
public int OfferingId { get; set; }
|
||||
public string? PartNumber { get; set; }
|
||||
public string? SupplierDescription { get; set; }
|
||||
public decimal? Price { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
619
CutList.Mcp/JobTools.cs
Normal file
619
CutList.Mcp/JobTools.cs
Normal file
@@ -0,0 +1,619 @@
|
||||
using System.ComponentModel;
|
||||
using ModelContextProtocol.Server;
|
||||
|
||||
namespace CutList.Mcp;
|
||||
|
||||
/// <summary>
|
||||
/// MCP tools for job management - creating jobs, managing parts/stock, and running optimization.
|
||||
/// All calls go through the CutList.Web REST API via ApiClient.
|
||||
/// </summary>
|
||||
[McpServerToolType]
|
||||
public class JobTools
|
||||
{
|
||||
private readonly ApiClient _api;
|
||||
|
||||
public JobTools(ApiClient api)
|
||||
{
|
||||
_api = api;
|
||||
}
|
||||
|
||||
#region Jobs
|
||||
|
||||
[McpServerTool(Name = "list_jobs"), Description("Lists all jobs in the system with summary info (job number, name, customer, part/stock counts).")]
|
||||
public async Task<JobListResult> ListJobs()
|
||||
{
|
||||
var jobs = await _api.GetJobsAsync();
|
||||
|
||||
return new JobListResult
|
||||
{
|
||||
Success = true,
|
||||
Jobs = jobs.Select(j => new JobSummaryDto
|
||||
{
|
||||
Id = j.Id,
|
||||
JobNumber = j.JobNumber,
|
||||
Name = j.Name,
|
||||
Customer = j.Customer,
|
||||
CuttingToolId = j.CuttingToolId,
|
||||
CuttingToolName = j.CuttingToolName,
|
||||
Notes = j.Notes,
|
||||
CreatedAt = j.CreatedAt,
|
||||
UpdatedAt = j.UpdatedAt,
|
||||
PartCount = j.PartCount,
|
||||
StockCount = j.StockCount
|
||||
}).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "get_job"), Description("Gets full job details including all parts and stock assignments.")]
|
||||
public async Task<JobDetailResult> GetJob(
|
||||
[Description("Job ID")]
|
||||
int jobId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var job = await _api.GetJobAsync(jobId);
|
||||
if (job == null)
|
||||
return new JobDetailResult { Success = false, Error = $"Job {jobId} not found" };
|
||||
|
||||
return new JobDetailResult
|
||||
{
|
||||
Success = true,
|
||||
Job = MapJobDetail(job)
|
||||
};
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
return new JobDetailResult { Success = false, Error = ex.Message };
|
||||
}
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "create_job"), Description("Creates a new job. Returns the created job with its auto-generated job number.")]
|
||||
public async Task<JobDetailResult> CreateJob(
|
||||
[Description("Job name/description")]
|
||||
string? name = null,
|
||||
[Description("Customer name")]
|
||||
string? customer = null,
|
||||
[Description("Cutting tool ID (use list_cutting_tools to find IDs). If not set, uses the default tool.")]
|
||||
int? cuttingToolId = null,
|
||||
[Description("Notes about the job")]
|
||||
string? notes = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var job = await _api.CreateJobAsync(name, customer, cuttingToolId, notes);
|
||||
if (job == null)
|
||||
return new JobDetailResult { Success = false, Error = "Failed to create job" };
|
||||
|
||||
return new JobDetailResult
|
||||
{
|
||||
Success = true,
|
||||
Job = MapJobDetail(job)
|
||||
};
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
return new JobDetailResult { Success = false, Error = ex.Message };
|
||||
}
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "update_job"), Description("Updates job details (name, customer, cutting tool, notes). Only provided fields are updated.")]
|
||||
public async Task<JobDetailResult> UpdateJob(
|
||||
[Description("Job ID")]
|
||||
int jobId,
|
||||
[Description("New job name")]
|
||||
string? name = null,
|
||||
[Description("New customer name")]
|
||||
string? customer = null,
|
||||
[Description("New cutting tool ID")]
|
||||
int? cuttingToolId = null,
|
||||
[Description("New notes")]
|
||||
string? notes = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var job = await _api.UpdateJobAsync(jobId, name, customer, cuttingToolId, notes);
|
||||
if (job == null)
|
||||
return new JobDetailResult { Success = false, Error = $"Job {jobId} not found" };
|
||||
|
||||
return new JobDetailResult
|
||||
{
|
||||
Success = true,
|
||||
Job = MapJobDetail(job)
|
||||
};
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
return new JobDetailResult { Success = false, Error = ex.Message };
|
||||
}
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "delete_job"), Description("Deletes a job and all its parts and stock assignments.")]
|
||||
public async Task<SimpleResult> DeleteJob(
|
||||
[Description("Job ID")]
|
||||
int jobId)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _api.DeleteJobAsync(jobId);
|
||||
return new SimpleResult { Success = true };
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
return new SimpleResult { Success = false, Error = ex.Message };
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Parts
|
||||
|
||||
[McpServerTool(Name = "add_job_part"), Description("Adds a single part to a job.")]
|
||||
public async Task<JobPartResult> AddJobPart(
|
||||
[Description("Job ID")]
|
||||
int jobId,
|
||||
[Description("Material ID (use list_materials to find IDs)")]
|
||||
int materialId,
|
||||
[Description("Part name/label (e.g., 'Top Rail', 'Picket')")]
|
||||
string name,
|
||||
[Description("Part length (e.g., '36\"', '4\\' 6\"', '54.5')")]
|
||||
string length,
|
||||
[Description("Quantity needed (default 1)")]
|
||||
int quantity = 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
var part = await _api.AddJobPartAsync(jobId, materialId, name, length, quantity);
|
||||
if (part == null)
|
||||
return new JobPartResult { Success = false, Error = "Failed to add part" };
|
||||
|
||||
return new JobPartResult
|
||||
{
|
||||
Success = true,
|
||||
Part = MapPart(part)
|
||||
};
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
return new JobPartResult { Success = false, Error = ex.Message };
|
||||
}
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "add_job_parts"), Description("Batch adds multiple parts to a job. Ideal for entering a full BOM (bill of materials). Returns the complete job state after all parts are added.")]
|
||||
public async Task<JobDetailResult> AddJobParts(
|
||||
[Description("Job ID")]
|
||||
int jobId,
|
||||
[Description("Array of parts to add. Each needs: materialId (int), name (string), length (string like \"36\\\"\" or \"4' 6\\\"\"), quantity (int)")]
|
||||
PartEntry[] parts)
|
||||
{
|
||||
var errors = new List<string>();
|
||||
int added = 0;
|
||||
|
||||
foreach (var part in parts)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _api.AddJobPartAsync(jobId, part.MaterialId, part.Name, part.Length, part.Quantity);
|
||||
added++;
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
errors.Add($"Failed to add '{part.Name}': {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// Reload the full job state
|
||||
try
|
||||
{
|
||||
var job = await _api.GetJobAsync(jobId);
|
||||
if (job == null)
|
||||
return new JobDetailResult { Success = false, Error = $"Job {jobId} not found after adding parts" };
|
||||
|
||||
var result = new JobDetailResult
|
||||
{
|
||||
Success = errors.Count == 0,
|
||||
Job = MapJobDetail(job)
|
||||
};
|
||||
|
||||
if (errors.Count > 0)
|
||||
result.Error = $"Added {added}/{parts.Length} parts. Errors: {string.Join("; ", errors)}";
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
return new JobDetailResult { Success = false, Error = ex.Message };
|
||||
}
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "delete_job_part"), Description("Removes a part from a job.")]
|
||||
public async Task<SimpleResult> DeleteJobPart(
|
||||
[Description("Job ID")]
|
||||
int jobId,
|
||||
[Description("Part ID (from get_job results)")]
|
||||
int partId)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _api.DeleteJobPartAsync(jobId, partId);
|
||||
return new SimpleResult { Success = true };
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
return new SimpleResult { Success = false, Error = ex.Message };
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stock
|
||||
|
||||
[McpServerTool(Name = "add_job_stock"), Description("Adds a stock material assignment to a job. Stock defines what material lengths are available for cutting.")]
|
||||
public async Task<JobStockResult> AddJobStock(
|
||||
[Description("Job ID")]
|
||||
int jobId,
|
||||
[Description("Material ID (must match the material used by parts)")]
|
||||
int materialId,
|
||||
[Description("Stock length (e.g., '20'', '240', '20 ft')")]
|
||||
string length,
|
||||
[Description("Quantity available (-1 for unlimited, default -1)")]
|
||||
int quantity = -1,
|
||||
[Description("Stock item ID from inventory (optional - links to tracked inventory)")]
|
||||
int? stockItemId = null,
|
||||
[Description("True if this is a custom length not from inventory (default false)")]
|
||||
bool isCustomLength = false,
|
||||
[Description("Priority - lower number = used first (default 10)")]
|
||||
int priority = 10)
|
||||
{
|
||||
try
|
||||
{
|
||||
var stock = await _api.AddJobStockAsync(jobId, materialId, stockItemId, length, quantity, isCustomLength, priority);
|
||||
if (stock == null)
|
||||
return new JobStockResult { Success = false, Error = "Failed to add stock" };
|
||||
|
||||
return new JobStockResult
|
||||
{
|
||||
Success = true,
|
||||
Stock = MapStock(stock)
|
||||
};
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
return new JobStockResult { Success = false, Error = ex.Message };
|
||||
}
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "delete_job_stock"), Description("Removes a stock assignment from a job.")]
|
||||
public async Task<SimpleResult> DeleteJobStock(
|
||||
[Description("Job ID")]
|
||||
int jobId,
|
||||
[Description("Stock ID (from get_job results)")]
|
||||
int stockId)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _api.DeleteJobStockAsync(jobId, stockId);
|
||||
return new SimpleResult { Success = true };
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
return new SimpleResult { Success = false, Error = ex.Message };
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Optimization
|
||||
|
||||
[McpServerTool(Name = "optimize_job"), Description("Runs bin packing optimization on a job. The job must have parts defined. If stock is defined, it will be used; otherwise the optimizer uses available inventory. Returns optimized cut layouts per material with efficiency stats.")]
|
||||
public async Task<OptimizeJobResult> OptimizeJob(
|
||||
[Description("Job ID")]
|
||||
int jobId,
|
||||
[Description("Optional kerf override in inches (e.g., 0.125). If not set, uses the job's cutting tool kerf.")]
|
||||
double? kerfOverride = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _api.PackJobAsync(jobId, kerfOverride.HasValue ? (decimal)kerfOverride.Value : null);
|
||||
if (result == null)
|
||||
return new OptimizeJobResult { Success = false, Error = "Optimization returned no results" };
|
||||
|
||||
return new OptimizeJobResult
|
||||
{
|
||||
Success = true,
|
||||
Materials = result.Materials.Select(m => new OptMaterialResultDto
|
||||
{
|
||||
MaterialId = m.MaterialId,
|
||||
MaterialName = m.MaterialName,
|
||||
InStockBins = m.InStockBins.Select(MapBin).ToList(),
|
||||
ToBePurchasedBins = m.ToBePurchasedBins.Select(MapBin).ToList(),
|
||||
ItemsNotPlaced = m.ItemsNotPlaced.Select(i => new OptItemDto { Name = i.Name, LengthInches = i.LengthInches, LengthFormatted = i.LengthFormatted }).ToList(),
|
||||
Summary = MapMaterialSummary(m.Summary)
|
||||
}).ToList(),
|
||||
Summary = new OptSummaryDto
|
||||
{
|
||||
TotalInStockBins = result.Summary.TotalInStockBins,
|
||||
TotalToBePurchasedBins = result.Summary.TotalToBePurchasedBins,
|
||||
TotalPieces = result.Summary.TotalPieces,
|
||||
TotalMaterialFormatted = result.Summary.TotalMaterialFormatted,
|
||||
TotalUsedFormatted = result.Summary.TotalUsedFormatted,
|
||||
TotalWasteFormatted = result.Summary.TotalWasteFormatted,
|
||||
Efficiency = result.Summary.Efficiency,
|
||||
TotalItemsNotPlaced = result.Summary.TotalItemsNotPlaced
|
||||
}
|
||||
};
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
return new OptimizeJobResult { Success = false, Error = ex.Message };
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cutting Tools
|
||||
|
||||
[McpServerTool(Name = "list_cutting_tools"), Description("Lists all available cutting tools with their kerf (blade width) values.")]
|
||||
public async Task<CuttingToolListResult> ListCuttingTools(
|
||||
[Description("Include inactive tools (default false)")]
|
||||
bool includeInactive = false)
|
||||
{
|
||||
var tools = await _api.GetCuttingToolsAsync(includeInactive);
|
||||
|
||||
return new CuttingToolListResult
|
||||
{
|
||||
Success = true,
|
||||
Tools = tools.Select(t => new CuttingToolSummaryDto
|
||||
{
|
||||
Id = t.Id,
|
||||
Name = t.Name,
|
||||
KerfInches = t.KerfInches,
|
||||
IsDefault = t.IsDefault,
|
||||
IsActive = t.IsActive
|
||||
}).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Mapping Helpers
|
||||
|
||||
private static JobDetailDto MapJobDetail(ApiJobDetailDto j) => new()
|
||||
{
|
||||
Id = j.Id,
|
||||
JobNumber = j.JobNumber,
|
||||
Name = j.Name,
|
||||
Customer = j.Customer,
|
||||
CuttingToolId = j.CuttingToolId,
|
||||
CuttingToolName = j.CuttingToolName,
|
||||
Notes = j.Notes,
|
||||
CreatedAt = j.CreatedAt,
|
||||
UpdatedAt = j.UpdatedAt,
|
||||
PartCount = j.PartCount,
|
||||
StockCount = j.StockCount,
|
||||
Parts = j.Parts.Select(MapPart).ToList(),
|
||||
Stock = j.Stock.Select(MapStock).ToList()
|
||||
};
|
||||
|
||||
private static JobPartSummaryDto MapPart(ApiJobPartDto p) => new()
|
||||
{
|
||||
Id = p.Id,
|
||||
MaterialId = p.MaterialId,
|
||||
MaterialName = p.MaterialName,
|
||||
Name = p.Name,
|
||||
LengthInches = p.LengthInches,
|
||||
LengthFormatted = p.LengthFormatted,
|
||||
Quantity = p.Quantity
|
||||
};
|
||||
|
||||
private static JobStockSummaryDto MapStock(ApiJobStockDto s) => new()
|
||||
{
|
||||
Id = s.Id,
|
||||
MaterialId = s.MaterialId,
|
||||
MaterialName = s.MaterialName,
|
||||
StockItemId = s.StockItemId,
|
||||
LengthInches = s.LengthInches,
|
||||
LengthFormatted = s.LengthFormatted,
|
||||
Quantity = s.Quantity,
|
||||
IsCustomLength = s.IsCustomLength,
|
||||
Priority = s.Priority
|
||||
};
|
||||
|
||||
private static OptBinDto MapBin(ApiPackedBinDto b) => new()
|
||||
{
|
||||
LengthFormatted = b.LengthFormatted,
|
||||
UsedFormatted = b.UsedFormatted,
|
||||
WasteFormatted = b.WasteFormatted,
|
||||
Efficiency = Math.Round(b.Efficiency, 1),
|
||||
Items = b.Items.Select(i => new OptItemDto
|
||||
{
|
||||
Name = i.Name,
|
||||
LengthInches = i.LengthInches,
|
||||
LengthFormatted = i.LengthFormatted
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
private static OptMaterialSummaryDto MapMaterialSummary(ApiMaterialPackingSummaryDto s) => new()
|
||||
{
|
||||
InStockBins = s.InStockBins,
|
||||
ToBePurchasedBins = s.ToBePurchasedBins,
|
||||
TotalPieces = s.TotalPieces,
|
||||
Efficiency = Math.Round(s.Efficiency, 1),
|
||||
ItemsNotPlaced = s.ItemsNotPlaced
|
||||
};
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region Job Tool DTOs
|
||||
|
||||
public class PartEntry
|
||||
{
|
||||
public int MaterialId { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Length { get; set; } = string.Empty;
|
||||
public int Quantity { get; set; } = 1;
|
||||
}
|
||||
|
||||
public class SimpleResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string? Error { get; set; }
|
||||
}
|
||||
|
||||
public class JobSummaryDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string JobNumber { get; set; } = string.Empty;
|
||||
public string? Name { get; set; }
|
||||
public string? Customer { get; set; }
|
||||
public int? CuttingToolId { get; set; }
|
||||
public string? CuttingToolName { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public int PartCount { get; set; }
|
||||
public int StockCount { get; set; }
|
||||
}
|
||||
|
||||
public class JobDetailDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string JobNumber { get; set; } = string.Empty;
|
||||
public string? Name { get; set; }
|
||||
public string? Customer { get; set; }
|
||||
public int? CuttingToolId { get; set; }
|
||||
public string? CuttingToolName { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public int PartCount { get; set; }
|
||||
public int StockCount { get; set; }
|
||||
public List<JobPartSummaryDto> Parts { get; set; } = new();
|
||||
public List<JobStockSummaryDto> Stock { get; set; } = new();
|
||||
}
|
||||
|
||||
public class JobPartSummaryDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int MaterialId { get; set; }
|
||||
public string MaterialName { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public decimal LengthInches { get; set; }
|
||||
public string LengthFormatted { get; set; } = string.Empty;
|
||||
public int Quantity { get; set; }
|
||||
}
|
||||
|
||||
public class JobStockSummaryDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int MaterialId { get; set; }
|
||||
public string MaterialName { get; set; } = string.Empty;
|
||||
public int? StockItemId { get; set; }
|
||||
public decimal LengthInches { get; set; }
|
||||
public string LengthFormatted { get; set; } = string.Empty;
|
||||
public int Quantity { get; set; }
|
||||
public bool IsCustomLength { get; set; }
|
||||
public int Priority { get; set; }
|
||||
}
|
||||
|
||||
public class JobListResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string? Error { get; set; }
|
||||
public List<JobSummaryDto> Jobs { get; set; } = new();
|
||||
}
|
||||
|
||||
public class JobDetailResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string? Error { get; set; }
|
||||
public JobDetailDto? Job { get; set; }
|
||||
}
|
||||
|
||||
public class JobPartResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string? Error { get; set; }
|
||||
public JobPartSummaryDto? Part { get; set; }
|
||||
}
|
||||
|
||||
public class JobStockResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string? Error { get; set; }
|
||||
public JobStockSummaryDto? Stock { get; set; }
|
||||
}
|
||||
|
||||
public class CuttingToolSummaryDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public decimal KerfInches { get; set; }
|
||||
public bool IsDefault { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
|
||||
public class CuttingToolListResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string? Error { get; set; }
|
||||
public List<CuttingToolSummaryDto> Tools { get; set; } = new();
|
||||
}
|
||||
|
||||
// Optimization result DTOs — streamlined for LLM consumption
|
||||
public class OptimizeJobResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string? Error { get; set; }
|
||||
public List<OptMaterialResultDto> Materials { get; set; } = new();
|
||||
public OptSummaryDto? Summary { get; set; }
|
||||
}
|
||||
|
||||
public class OptMaterialResultDto
|
||||
{
|
||||
public int MaterialId { get; set; }
|
||||
public string MaterialName { get; set; } = string.Empty;
|
||||
public List<OptBinDto> InStockBins { get; set; } = new();
|
||||
public List<OptBinDto> ToBePurchasedBins { get; set; } = new();
|
||||
public List<OptItemDto> ItemsNotPlaced { get; set; } = new();
|
||||
public OptMaterialSummaryDto Summary { get; set; } = new();
|
||||
}
|
||||
|
||||
public class OptBinDto
|
||||
{
|
||||
public string LengthFormatted { get; set; } = string.Empty;
|
||||
public string UsedFormatted { get; set; } = string.Empty;
|
||||
public string WasteFormatted { get; set; } = string.Empty;
|
||||
public double Efficiency { get; set; }
|
||||
public List<OptItemDto> Items { get; set; } = new();
|
||||
}
|
||||
|
||||
public class OptItemDto
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public double LengthInches { get; set; }
|
||||
public string LengthFormatted { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class OptSummaryDto
|
||||
{
|
||||
public int TotalInStockBins { get; set; }
|
||||
public int TotalToBePurchasedBins { get; set; }
|
||||
public int TotalPieces { get; set; }
|
||||
public string TotalMaterialFormatted { get; set; } = string.Empty;
|
||||
public string TotalUsedFormatted { get; set; } = string.Empty;
|
||||
public string TotalWasteFormatted { get; set; } = string.Empty;
|
||||
public double Efficiency { get; set; }
|
||||
public int TotalItemsNotPlaced { get; set; }
|
||||
}
|
||||
|
||||
public class OptMaterialSummaryDto
|
||||
{
|
||||
public int InStockBins { get; set; }
|
||||
public int ToBePurchasedBins { get; set; }
|
||||
public int TotalPieces { get; set; }
|
||||
public double Efficiency { get; set; }
|
||||
public int ItemsNotPlaced { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -1,9 +1,16 @@
|
||||
using CutList.Mcp;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using ModelContextProtocol.Server;
|
||||
|
||||
var builder = Host.CreateApplicationBuilder(args);
|
||||
|
||||
// Register HttpClient for API calls to CutList.Web
|
||||
builder.Services.AddHttpClient<ApiClient>(client =>
|
||||
{
|
||||
client.BaseAddress = new Uri("http://localhost:5009");
|
||||
});
|
||||
|
||||
builder.Services
|
||||
.AddMcpServer()
|
||||
.WithStdioServerTransport()
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<base href="/" />
|
||||
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" />
|
||||
<link rel="stylesheet" href="css/app.css" />
|
||||
<link rel="stylesheet" href="css/report.css" />
|
||||
<link rel="stylesheet" href="CutList.Web.styles.css" />
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
</NavLink>
|
||||
</div>
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="projects">
|
||||
<span class="bi bi-list-check-nav-menu" aria-hidden="true"></span> Projects
|
||||
<NavLink class="nav-link" href="jobs">
|
||||
<span class="bi bi-list-check-nav-menu" aria-hidden="true"></span> Jobs
|
||||
</NavLink>
|
||||
</div>
|
||||
<div class="nav-item px-3">
|
||||
@@ -28,6 +28,11 @@
|
||||
<span class="bi bi-boxes-nav-menu" aria-hidden="true"></span> Stock Items
|
||||
</NavLink>
|
||||
</div>
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="orders">
|
||||
<span class="bi bi-cart-nav-menu" aria-hidden="true"></span> Orders
|
||||
</NavLink>
|
||||
</div>
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="suppliers">
|
||||
<span class="bi bi-building-nav-menu" aria-hidden="true"></span> Suppliers
|
||||
|
||||
@@ -46,6 +46,14 @@
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-box' viewBox='0 0 16 16'%3E%3Cpath d='M8.186 1.113a.5.5 0 0 0-.372 0L1.846 3.5 8 5.961 14.154 3.5 8.186 1.113zM15 4.239l-6.5 2.6v7.922l6.5-2.6V4.24zM7.5 14.762V6.838L1 4.239v7.923l6.5 2.6zM7.443.184a1.5 1.5 0 0 1 1.114 0l7.129 2.852A.5.5 0 0 1 16 3.5v8.662a1 1 0 0 1-.629.928l-7.185 2.874a.5.5 0 0 1-.372 0L.63 13.09a1 1 0 0 1-.63-.928V3.5a.5.5 0 0 1 .314-.464L7.443.184z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-boxes-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-boxes' viewBox='0 0 16 16'%3E%3Cpath d='M7.752.066a.5.5 0 0 1 .496 0l3.75 2.143a.5.5 0 0 1 .252.434v3.995l3.498 2A.5.5 0 0 1 16 9.07v4.286a.5.5 0 0 1-.252.434l-3.75 2.143a.5.5 0 0 1-.496 0l-3.502-2-3.502 2.001a.5.5 0 0 1-.496 0l-3.75-2.143A.5.5 0 0 1 0 13.357V9.071a.5.5 0 0 1 .252-.434L3.75 6.638V2.643a.5.5 0 0 1 .252-.434zM4.25 7.504 1.508 9.071l2.742 1.567 2.742-1.567zM7.5 9.933l-2.75 1.571v3.134l2.75-1.571zm1 3.134 2.75 1.571v-3.134L8.5 9.933zm.508-3.996 2.742 1.567 2.742-1.567-2.742-1.567zm2.242-2.433V3.504L8.5 5.076V8.21zM7.5 8.21V5.076L4.75 3.504v3.134zM5.258 2.643 8 4.21l2.742-1.567L8 1.076zM15 9.933l-2.75 1.571v3.134L15 13.067zM3.75 14.638v-3.134L1 9.933v3.134z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-cart-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-cart3' viewBox='0 0 16 16'%3E%3Cpath d='M0 1.5A.5.5 0 0 1 .5 1H2a.5.5 0 0 1 .485.379L2.89 3H14.5a.5.5 0 0 1 .49.598l-1 5a.5.5 0 0 1-.465.401l-9.397.472L4.415 11H13a.5.5 0 0 1 0 1H4a.5.5 0 0 1-.491-.408L2.01 3.607 1.61 2H.5a.5.5 0 0 1-.5-.5zM3.102 4l.84 4.479 9.144-.459L13.89 4H3.102zM5 12a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm7 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm-7 1a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm7 0a1 1 0 1 1 0 2 1 1 0 0 1 0-2z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-building-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-building' viewBox='0 0 16 16'%3E%3Cpath d='M4 2.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1Zm3 0a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1Zm3.5-.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-1ZM4 5.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1ZM7.5 5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-1Zm2.5.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1ZM4.5 8a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-1Zm2.5.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1Zm3.5-.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-1Z'/%3E%3Cpath d='M2 1a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V1Zm11 0H3v14h3v-2.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 .5.5V15h3V1Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
<div class="col-md-6 col-lg-3 mb-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Projects</h5>
|
||||
<p class="card-text">Create and manage cut list projects. Add parts and stock bins, then optimize to minimize waste.</p>
|
||||
<a href="projects" class="btn btn-primary">Go to Projects</a>
|
||||
<h5 class="card-title">Jobs</h5>
|
||||
<p class="card-text">Create and manage cut list jobs. Add parts and stock bins, then optimize to minimize waste.</p>
|
||||
<a href="jobs" class="btn btn-primary">Go to Jobs</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -51,7 +51,7 @@
|
||||
<ol>
|
||||
<li><strong>Set up materials</strong> - Define the shapes and sizes of materials you work with</li>
|
||||
<li><strong>Add suppliers</strong> - Track which stock lengths are available from your suppliers</li>
|
||||
<li><strong>Create a project</strong> - Add the parts you need to cut with their lengths and quantities</li>
|
||||
<li><strong>Create a job</strong> - Add the parts you need to cut with their lengths and quantities</li>
|
||||
<li><strong>Add stock bins</strong> - Specify which stock lengths to cut from (import from supplier or add manually)</li>
|
||||
<li><strong>Optimize</strong> - Run the optimizer to find the best cutting pattern</li>
|
||||
<li><strong>Print report</strong> - Generate a printable cut list to take to the shop</li>
|
||||
|
||||
1567
CutList.Web/Components/Pages/Jobs/Edit.razor
Normal file
1567
CutList.Web/Components/Pages/Jobs/Edit.razor
Normal file
File diff suppressed because it is too large
Load Diff
142
CutList.Web/Components/Pages/Jobs/Index.razor
Normal file
142
CutList.Web/Components/Pages/Jobs/Index.razor
Normal file
@@ -0,0 +1,142 @@
|
||||
@page "/jobs"
|
||||
@inject JobService JobService
|
||||
@inject NavigationManager Navigation
|
||||
|
||||
<PageTitle>Jobs</PageTitle>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h1>Jobs</h1>
|
||||
<div class="d-flex gap-2">
|
||||
<button class="btn btn-success" @onclick="QuickCreateJob" disabled="@creating">
|
||||
@if (creating)
|
||||
{
|
||||
<span class="spinner-border spinner-border-sm me-1"></span>
|
||||
}
|
||||
Quick Create
|
||||
</button>
|
||||
<a href="jobs/new" class="btn btn-primary">New Job</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-muted mb-4">
|
||||
Jobs organize the parts you need to cut for a project. Add parts with their required lengths and quantities,
|
||||
assign stock materials, then run the optimizer to generate an efficient cut list that minimizes waste.
|
||||
</p>
|
||||
|
||||
@if (loading)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else if (jobs.Count == 0)
|
||||
{
|
||||
<div class="alert alert-info">
|
||||
No jobs found. <a href="jobs/new">Create your first job</a>.
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Job #</th>
|
||||
<th>Name</th>
|
||||
<th>Customer</th>
|
||||
<th>Cutting Tool</th>
|
||||
<th>Last Modified</th>
|
||||
<th style="width: 150px;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var job in pagedJobs)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
<a href="jobs/@job.Id">@job.JobNumber</a>
|
||||
@if (job.IsLocked)
|
||||
{
|
||||
<i class="bi bi-lock-fill text-warning ms-1" title="Locked — materials ordered"></i>
|
||||
}
|
||||
</td>
|
||||
<td>@(job.Name ?? "-")</td>
|
||||
<td>@(job.Customer ?? "-")</td>
|
||||
<td>@(job.CuttingTool?.Name ?? "-")</td>
|
||||
<td>@((job.UpdatedAt ?? job.CreatedAt).ToLocalTime().ToString("g"))</td>
|
||||
<td>
|
||||
<a href="jobs/@job.Id" class="btn btn-sm btn-outline-primary" title="Edit"><i class="bi bi-pencil"></i></a>
|
||||
<button class="btn btn-sm btn-outline-secondary" @onclick="() => DuplicateJob(job)" title="Copy"><i class="bi bi-copy"></i></button>
|
||||
<button class="btn btn-sm btn-outline-danger" @onclick="() => ConfirmDelete(job)" title="Delete"><i class="bi bi-trash"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<Pager TotalCount="jobs.Count" PageSize="pageSize" CurrentPage="currentPage" CurrentPageChanged="OnPageChanged" />
|
||||
}
|
||||
|
||||
<ConfirmDialog @ref="deleteDialog"
|
||||
Title="Delete Job"
|
||||
Message="@deleteMessage"
|
||||
ConfirmText="Delete"
|
||||
OnConfirm="DeleteConfirmed" />
|
||||
|
||||
@code {
|
||||
private List<Job> jobs = new();
|
||||
private bool loading = true;
|
||||
private bool creating = false;
|
||||
private int currentPage = 1;
|
||||
private int pageSize = 25;
|
||||
private ConfirmDialog deleteDialog = null!;
|
||||
private Job? jobToDelete;
|
||||
private string deleteMessage = "";
|
||||
|
||||
private IEnumerable<Job> pagedJobs => jobs.Skip((currentPage - 1) * pageSize).Take(pageSize);
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
jobs = await JobService.GetAllAsync();
|
||||
loading = false;
|
||||
}
|
||||
|
||||
private async Task QuickCreateJob()
|
||||
{
|
||||
creating = true;
|
||||
try
|
||||
{
|
||||
var job = await JobService.QuickCreateAsync();
|
||||
Navigation.NavigateTo($"jobs/{job.Id}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
creating = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfirmDelete(Job job)
|
||||
{
|
||||
jobToDelete = job;
|
||||
deleteMessage = $"Are you sure you want to delete \"{job.DisplayName}\"? This will also delete all parts.";
|
||||
deleteDialog.Show();
|
||||
}
|
||||
|
||||
private async Task DeleteConfirmed()
|
||||
{
|
||||
if (jobToDelete != null)
|
||||
{
|
||||
await JobService.DeleteAsync(jobToDelete.Id);
|
||||
jobs = await JobService.GetAllAsync();
|
||||
|
||||
var totalPages = (int)Math.Ceiling((double)jobs.Count / pageSize);
|
||||
if (currentPage > totalPages && totalPages > 0)
|
||||
currentPage = totalPages;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPageChanged(int page) => currentPage = page;
|
||||
|
||||
private async Task DuplicateJob(Job job)
|
||||
{
|
||||
var duplicate = await JobService.DuplicateAsync(job.Id);
|
||||
Navigation.NavigateTo($"jobs/{duplicate.Id}");
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
@inject MaterialService MaterialService
|
||||
@inject NavigationManager Navigation
|
||||
@using CutList.Core.Formatting
|
||||
@using CutList.Web.Data.Entities
|
||||
@using CutList.Web.Components.Shared
|
||||
|
||||
<PageTitle>@(IsNew ? "Add Material" : "Edit Material")</PageTitle>
|
||||
|
||||
@@ -26,21 +28,45 @@ else
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Shape</label>
|
||||
<InputSelect class="form-select" @bind-Value="material.Shape">
|
||||
<InputSelect class="form-select" @bind-Value="selectedShape" @bind-Value:after="OnShapeChanged" disabled="@(!IsNew)">
|
||||
<option value="">-- Select Shape --</option>
|
||||
@foreach (var shape in MaterialService.CommonShapes)
|
||||
@foreach (var shape in Enum.GetValues<MaterialShape>())
|
||||
{
|
||||
<option value="@shape">@shape</option>
|
||||
<option value="@shape">@shape.GetDisplayName()</option>
|
||||
}
|
||||
</InputSelect>
|
||||
@if (!IsNew)
|
||||
{
|
||||
<div class="form-text text-muted">Shape cannot be changed after creation.</div>
|
||||
}
|
||||
<ValidationMessage For="@(() => material.Shape)" />
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Type</label>
|
||||
<InputSelect class="form-select" @bind-Value="material.Type">
|
||||
@foreach (var type in Enum.GetValues<MaterialType>())
|
||||
{
|
||||
<option value="@type">@type</option>
|
||||
}
|
||||
</InputSelect>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Grade</label>
|
||||
<InputText class="form-control" @bind-Value="material.Grade" placeholder="e.g., A36, Hot Roll, 304" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (selectedShape != null)
|
||||
{
|
||||
@RenderDimensionInputs()
|
||||
}
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Size</label>
|
||||
<InputText class="form-control" @bind-Value="material.Size" placeholder="e.g., 1" OD x 0.065 wall" />
|
||||
<ValidationMessage For="@(() => material.Size)" />
|
||||
<div class="form-text">Examples: "1" OD x 0.065 wall", "2x2", "1.5 x 1.5 x 0.125"</div>
|
||||
<label class="form-label">Size Display (auto-generated)</label>
|
||||
<InputText class="form-control" @bind-Value="material.Size" placeholder="Will be auto-generated from dimensions" />
|
||||
<div class="form-text">Leave blank to auto-generate from dimensions, or customize as needed.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@@ -68,82 +94,36 @@ else
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (!IsNew)
|
||||
@if (selectedShape != null)
|
||||
{
|
||||
<div class="col-lg-6">
|
||||
<div class="col-lg-6 mb-4">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Available Stock Lengths</h5>
|
||||
<button class="btn btn-sm btn-primary" @onclick="ShowAddStockForm">Add Length</button>
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Preview</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if (showStockForm)
|
||||
{
|
||||
<div class="border rounded p-3 mb-3 bg-light">
|
||||
<h6>@(editingStock == null ? "Add Stock Length" : "Edit Stock Length")</h6>
|
||||
<div class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Length</label>
|
||||
<LengthInput @bind-Value="newStock.LengthInches" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Qty in Stock</label>
|
||||
<input type="number" class="form-control" @bind="newStock.Quantity" min="0" />
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label class="form-label">Notes (optional)</label>
|
||||
<InputText class="form-control" @bind-Value="newStock.Notes" />
|
||||
</div>
|
||||
</div>
|
||||
@if (!string.IsNullOrEmpty(stockErrorMessage))
|
||||
{
|
||||
<div class="alert alert-danger mt-2 mb-0">@stockErrorMessage</div>
|
||||
}
|
||||
<div class="mt-3 d-flex gap-2">
|
||||
<button class="btn btn-primary btn-sm" @onclick="SaveStockAsync" disabled="@savingStock">
|
||||
@if (savingStock)
|
||||
{
|
||||
<span class="spinner-border spinner-border-sm me-1"></span>
|
||||
}
|
||||
@(editingStock == null ? "Add" : "Save")
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" @onclick="CancelStockForm">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-sm-4">Shape</dt>
|
||||
<dd class="col-sm-8">@selectedShape.Value.GetDisplayName()</dd>
|
||||
|
||||
@if (stockLengths.Count == 0)
|
||||
{
|
||||
<p class="text-muted">No stock lengths configured yet.</p>
|
||||
<p class="text-muted small">Add common stock lengths for this material (e.g., 20', 24') to quickly populate project stock bins.</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Length</th>
|
||||
<th>Qty</th>
|
||||
<th>Notes</th>
|
||||
<th style="width: 100px;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var stock in stockLengths)
|
||||
{
|
||||
<tr>
|
||||
<td>@ArchUnits.FormatFromInches((double)stock.LengthInches)</td>
|
||||
<td>@stock.Quantity</td>
|
||||
<td>@(stock.Notes ?? "-")</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-primary" @onclick="() => EditStock(stock)">Edit</button>
|
||||
<button class="btn btn-sm btn-outline-danger" @onclick="() => ConfirmDeleteStock(stock)">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
<dt class="col-sm-4">Type</dt>
|
||||
<dd class="col-sm-8">@material.Type</dd>
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(material.Grade))
|
||||
{
|
||||
<dt class="col-sm-4">Grade</dt>
|
||||
<dd class="col-sm-8">@material.Grade</dd>
|
||||
}
|
||||
|
||||
<dt class="col-sm-4">Size</dt>
|
||||
<dd class="col-sm-8">@GetPreviewSize()</dd>
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(material.Description))
|
||||
{
|
||||
<dt class="col-sm-4">Description</dt>
|
||||
<dd class="col-sm-8">@material.Description</dd>
|
||||
}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -151,33 +131,27 @@ else
|
||||
</div>
|
||||
}
|
||||
|
||||
<ConfirmDialog @ref="deleteStockDialog"
|
||||
Title="Delete Stock Length"
|
||||
Message="@deleteStockMessage"
|
||||
ConfirmText="Delete"
|
||||
OnConfirm="DeleteStockConfirmed" />
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public int? Id { get; set; }
|
||||
|
||||
private Material material = new();
|
||||
private List<MaterialStockLength> stockLengths = new();
|
||||
private MaterialShape? selectedShape;
|
||||
private bool loading = true;
|
||||
private bool saving;
|
||||
private string? errorMessage;
|
||||
|
||||
// Stock form
|
||||
private bool showStockForm;
|
||||
private bool savingStock;
|
||||
private MaterialStockLength newStock = new();
|
||||
private MaterialStockLength? editingStock;
|
||||
private string? stockErrorMessage;
|
||||
|
||||
// Delete dialog
|
||||
private ConfirmDialog deleteStockDialog = null!;
|
||||
private MaterialStockLength? stockToDelete;
|
||||
private string deleteStockMessage = "";
|
||||
// Typed dimension objects for each shape
|
||||
private RoundBarDimensions roundBarDims = new();
|
||||
private RoundTubeDimensions roundTubeDims = new();
|
||||
private FlatBarDimensions flatBarDims = new();
|
||||
private SquareBarDimensions squareBarDims = new();
|
||||
private SquareTubeDimensions squareTubeDims = new();
|
||||
private RectangularTubeDimensions rectTubeDims = new();
|
||||
private AngleDimensions angleDims = new();
|
||||
private ChannelDimensions channelDims = new();
|
||||
private IBeamDimensions ibeamDims = new();
|
||||
private PipeDimensions pipeDims = new();
|
||||
|
||||
private bool IsNew => !Id.HasValue;
|
||||
|
||||
@@ -192,11 +166,201 @@ else
|
||||
return;
|
||||
}
|
||||
material = existing;
|
||||
stockLengths = await MaterialService.GetStockLengthsAsync(Id.Value);
|
||||
selectedShape = existing.Shape;
|
||||
LoadDimensionsFromMaterial(existing);
|
||||
}
|
||||
loading = false;
|
||||
}
|
||||
|
||||
private void LoadDimensionsFromMaterial(Material m)
|
||||
{
|
||||
if (m.Dimensions == null) return;
|
||||
|
||||
switch (m.Dimensions)
|
||||
{
|
||||
case RoundBarDimensions d: roundBarDims = d; break;
|
||||
case RoundTubeDimensions d: roundTubeDims = d; break;
|
||||
case FlatBarDimensions d: flatBarDims = d; break;
|
||||
case SquareBarDimensions d: squareBarDims = d; break;
|
||||
case SquareTubeDimensions d: squareTubeDims = d; break;
|
||||
case RectangularTubeDimensions d: rectTubeDims = d; break;
|
||||
case AngleDimensions d: angleDims = d; break;
|
||||
case ChannelDimensions d: channelDims = d; break;
|
||||
case IBeamDimensions d: ibeamDims = d; break;
|
||||
case PipeDimensions d: pipeDims = d; break;
|
||||
}
|
||||
}
|
||||
|
||||
private MaterialDimensions GetCurrentDimensions() => selectedShape switch
|
||||
{
|
||||
MaterialShape.RoundBar => roundBarDims,
|
||||
MaterialShape.RoundTube => roundTubeDims,
|
||||
MaterialShape.FlatBar => flatBarDims,
|
||||
MaterialShape.SquareBar => squareBarDims,
|
||||
MaterialShape.SquareTube => squareTubeDims,
|
||||
MaterialShape.RectangularTube => rectTubeDims,
|
||||
MaterialShape.Angle => angleDims,
|
||||
MaterialShape.Channel => channelDims,
|
||||
MaterialShape.IBeam => ibeamDims,
|
||||
MaterialShape.Pipe => pipeDims,
|
||||
_ => throw new InvalidOperationException("No shape selected")
|
||||
};
|
||||
|
||||
private void OnShapeChanged()
|
||||
{
|
||||
if (selectedShape.HasValue)
|
||||
{
|
||||
material.Shape = selectedShape.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetPreviewSize()
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(material.Size))
|
||||
{
|
||||
return material.Size;
|
||||
}
|
||||
|
||||
if (selectedShape.HasValue)
|
||||
{
|
||||
try
|
||||
{
|
||||
var generated = GetCurrentDimensions().GenerateSizeString();
|
||||
return string.IsNullOrWhiteSpace(generated) ? "(enter dimensions)" : generated;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "(enter dimensions)";
|
||||
}
|
||||
}
|
||||
|
||||
return "(select shape and enter dimensions)";
|
||||
}
|
||||
|
||||
private RenderFragment RenderDimensionInputs() => __builder =>
|
||||
{
|
||||
switch (selectedShape!.Value)
|
||||
{
|
||||
case MaterialShape.RoundBar:
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Diameter</label>
|
||||
<LengthInput @bind-Value="roundBarDims.Diameter" Placeholder="e.g., 1/2" or 0.5" />
|
||||
</div>
|
||||
break;
|
||||
|
||||
case MaterialShape.RoundTube:
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Outer Diameter</label>
|
||||
<LengthInput @bind-Value="roundTubeDims.OuterDiameter" Placeholder="e.g., 1" or 1.0" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Wall Thickness</label>
|
||||
<LengthInput @bind-Value="roundTubeDims.Wall" Placeholder="e.g., 0.065 or 1/16"" />
|
||||
</div>
|
||||
break;
|
||||
|
||||
case MaterialShape.FlatBar:
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Width</label>
|
||||
<LengthInput @bind-Value="flatBarDims.Width" Placeholder="e.g., 2" or 2.0" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Thickness</label>
|
||||
<LengthInput @bind-Value="flatBarDims.Thickness" Placeholder="e.g., 1/4" or 0.25" />
|
||||
</div>
|
||||
break;
|
||||
|
||||
case MaterialShape.SquareBar:
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Size</label>
|
||||
<LengthInput @bind-Value="squareBarDims.Size" Placeholder="e.g., 1" or 1.0" />
|
||||
</div>
|
||||
break;
|
||||
|
||||
case MaterialShape.SquareTube:
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Size</label>
|
||||
<LengthInput @bind-Value="squareTubeDims.Size" Placeholder="e.g., 2" or 2.0" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Wall Thickness</label>
|
||||
<LengthInput @bind-Value="squareTubeDims.Wall" Placeholder="e.g., 0.125 or 1/8"" />
|
||||
</div>
|
||||
break;
|
||||
|
||||
case MaterialShape.RectangularTube:
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Width</label>
|
||||
<LengthInput @bind-Value="rectTubeDims.Width" Placeholder="e.g., 2" or 2.0" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Height</label>
|
||||
<LengthInput @bind-Value="rectTubeDims.Height" Placeholder="e.g., 3" or 3.0" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Wall Thickness</label>
|
||||
<LengthInput @bind-Value="rectTubeDims.Wall" Placeholder="e.g., 0.125 or 1/8"" />
|
||||
</div>
|
||||
break;
|
||||
|
||||
case MaterialShape.Angle:
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Leg 1</label>
|
||||
<LengthInput @bind-Value="angleDims.Leg1" Placeholder="e.g., 2" or 2.0" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Leg 2</label>
|
||||
<LengthInput @bind-Value="angleDims.Leg2" Placeholder="e.g., 2" or 2.0" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Thickness</label>
|
||||
<LengthInput @bind-Value="angleDims.Thickness" Placeholder="e.g., 1/4" or 0.25" />
|
||||
</div>
|
||||
break;
|
||||
|
||||
case MaterialShape.Channel:
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Height</label>
|
||||
<LengthInput @bind-Value="channelDims.Height" Placeholder="e.g., 4" or 4.0" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Flange Width</label>
|
||||
<LengthInput @bind-Value="channelDims.Flange" Placeholder="e.g., 1.58" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Web Thickness</label>
|
||||
<LengthInput @bind-Value="channelDims.Web" Placeholder="e.g., 0.18" />
|
||||
</div>
|
||||
break;
|
||||
|
||||
case MaterialShape.IBeam:
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Height (nominal)</label>
|
||||
<LengthInput @bind-Value="ibeamDims.Height" Placeholder="e.g., 8" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Weight per Foot (lbs)</label>
|
||||
<input type="number" class="form-control" step="0.01" @bind="ibeamDims.WeightPerFoot" placeholder="e.g., 31" />
|
||||
</div>
|
||||
break;
|
||||
|
||||
case MaterialShape.Pipe:
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Nominal Pipe Size (NPS)</label>
|
||||
<LengthInput @bind-Value="pipeDims.NominalSize" Placeholder="e.g., 1" or 1.0" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Schedule (optional)</label>
|
||||
<InputText class="form-control" @bind-Value="pipeDims.Schedule" placeholder="e.g., 40, 80, STD" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Wall Thickness (if no schedule)</label>
|
||||
<LengthInput @bind-NullableValue="pipeDims.Wall" Placeholder="e.g., 0.133" />
|
||||
</div>
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
private async Task SaveAsync()
|
||||
{
|
||||
errorMessage = null;
|
||||
@@ -204,15 +368,24 @@ else
|
||||
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(material.Shape))
|
||||
if (!selectedShape.HasValue)
|
||||
{
|
||||
errorMessage = "Shape is required";
|
||||
return;
|
||||
}
|
||||
|
||||
material.Shape = selectedShape.Value;
|
||||
var dimensions = GetCurrentDimensions();
|
||||
|
||||
// Auto-generate Size if empty
|
||||
if (string.IsNullOrWhiteSpace(material.Size))
|
||||
{
|
||||
errorMessage = "Size is required";
|
||||
material.Size = dimensions.GenerateSizeString();
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(material.Size))
|
||||
{
|
||||
errorMessage = "Size is required. Please enter dimensions or provide a size string.";
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -225,12 +398,12 @@ else
|
||||
|
||||
if (IsNew)
|
||||
{
|
||||
var created = await MaterialService.CreateAsync(material);
|
||||
var created = await MaterialService.CreateWithDimensionsAsync(material, dimensions);
|
||||
Navigation.NavigateTo($"materials/{created.Id}");
|
||||
}
|
||||
else
|
||||
{
|
||||
await MaterialService.UpdateAsync(material);
|
||||
await MaterialService.UpdateWithDimensionsAsync(material, dimensions);
|
||||
}
|
||||
}
|
||||
finally
|
||||
@@ -238,94 +411,4 @@ else
|
||||
saving = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Stock length methods
|
||||
private void ShowAddStockForm()
|
||||
{
|
||||
editingStock = null;
|
||||
newStock = new MaterialStockLength { MaterialId = Id!.Value };
|
||||
showStockForm = true;
|
||||
stockErrorMessage = null;
|
||||
}
|
||||
|
||||
private void EditStock(MaterialStockLength stock)
|
||||
{
|
||||
editingStock = stock;
|
||||
newStock = new MaterialStockLength
|
||||
{
|
||||
Id = stock.Id,
|
||||
MaterialId = stock.MaterialId,
|
||||
LengthInches = stock.LengthInches,
|
||||
Quantity = stock.Quantity,
|
||||
Notes = stock.Notes
|
||||
};
|
||||
showStockForm = true;
|
||||
stockErrorMessage = null;
|
||||
}
|
||||
|
||||
private void CancelStockForm()
|
||||
{
|
||||
showStockForm = false;
|
||||
editingStock = null;
|
||||
stockErrorMessage = null;
|
||||
}
|
||||
|
||||
private async Task SaveStockAsync()
|
||||
{
|
||||
stockErrorMessage = null;
|
||||
savingStock = true;
|
||||
|
||||
try
|
||||
{
|
||||
if (newStock.LengthInches <= 0)
|
||||
{
|
||||
stockErrorMessage = "Length must be greater than zero";
|
||||
return;
|
||||
}
|
||||
|
||||
var exists = await MaterialService.StockLengthExistsAsync(
|
||||
newStock.MaterialId,
|
||||
newStock.LengthInches,
|
||||
editingStock?.Id);
|
||||
|
||||
if (exists)
|
||||
{
|
||||
stockErrorMessage = "This stock length already exists for this material";
|
||||
return;
|
||||
}
|
||||
|
||||
if (editingStock == null)
|
||||
{
|
||||
await MaterialService.AddStockLengthAsync(newStock);
|
||||
}
|
||||
else
|
||||
{
|
||||
await MaterialService.UpdateStockLengthAsync(newStock);
|
||||
}
|
||||
|
||||
stockLengths = await MaterialService.GetStockLengthsAsync(Id!.Value);
|
||||
showStockForm = false;
|
||||
editingStock = null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
savingStock = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfirmDeleteStock(MaterialStockLength stock)
|
||||
{
|
||||
stockToDelete = stock;
|
||||
deleteStockMessage = $"Are you sure you want to delete the {ArchUnits.FormatFromInches((double)stock.LengthInches)} stock length?";
|
||||
deleteStockDialog.Show();
|
||||
}
|
||||
|
||||
private async Task DeleteStockConfirmed()
|
||||
{
|
||||
if (stockToDelete != null)
|
||||
{
|
||||
await MaterialService.DeleteStockLengthAsync(stockToDelete.Id);
|
||||
stockLengths = await MaterialService.GetStockLengthsAsync(Id!.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,22 @@
|
||||
<a href="materials/new" class="btn btn-primary">Add Material</a>
|
||||
</div>
|
||||
|
||||
<p class="text-muted mb-4">
|
||||
Manage your material catalog here. Materials define the types of stock you work with — shape, size, type, and
|
||||
grade. Once added, materials can be assigned to jobs and used to generate optimized cut lists.
|
||||
</p>
|
||||
|
||||
@if (loading)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(errorMessage))
|
||||
{
|
||||
<div class="alert alert-danger">
|
||||
<strong>Error loading materials:</strong>
|
||||
<pre style="white-space: pre-wrap;">@errorMessage</pre>
|
||||
</div>
|
||||
}
|
||||
else if (materials.Count == 0)
|
||||
{
|
||||
<div class="alert alert-info">
|
||||
@@ -21,30 +33,47 @@ else if (materials.Count == 0)
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Shape</th>
|
||||
<th>Size</th>
|
||||
<th>Description</th>
|
||||
<th style="width: 120px;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var material in materials)
|
||||
{
|
||||
<MaterialFilter AvailableGrades="availableGrades" Value="filterState" ValueChanged="OnFilterChanged" />
|
||||
|
||||
@if (filteredMaterials.Count == 0)
|
||||
{
|
||||
<div class="alert alert-warning">
|
||||
No materials match your filters.
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>@material.Shape</td>
|
||||
<td>@material.Size</td>
|
||||
<td>@material.Description</td>
|
||||
<td>
|
||||
<a href="materials/@material.Id" class="btn btn-sm btn-outline-primary">Edit</a>
|
||||
<button class="btn btn-sm btn-outline-danger" @onclick="() => ConfirmDelete(material)">Delete</button>
|
||||
</td>
|
||||
<th>Shape</th>
|
||||
<th>Type</th>
|
||||
<th>Grade</th>
|
||||
<th>Size</th>
|
||||
<th style="width: 100px;">Actions</th>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var material in pagedMaterials)
|
||||
{
|
||||
<tr>
|
||||
<td>@material.Shape.GetDisplayName()</td>
|
||||
<td>@material.Type</td>
|
||||
<td>@material.Grade</td>
|
||||
<td>@material.Size</td>
|
||||
<td>
|
||||
<div class="d-flex gap-1">
|
||||
<a href="materials/@material.Id" class="btn btn-sm btn-outline-primary" title="Edit"><i class="bi bi-pencil"></i></a>
|
||||
<button class="btn btn-sm btn-outline-danger" @onclick="() => ConfirmDelete(material)" title="Delete"><i class="bi bi-trash"></i></button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<Pager TotalCount="filteredMaterials.Count" PageSize="pageSize" CurrentPage="currentPage" CurrentPageChanged="OnPageChanged" />
|
||||
}
|
||||
}
|
||||
|
||||
<ConfirmDialog @ref="deleteDialog"
|
||||
@@ -56,14 +85,64 @@ else
|
||||
@code {
|
||||
private List<Material> materials = new();
|
||||
private bool loading = true;
|
||||
private string? errorMessage;
|
||||
private int currentPage = 1;
|
||||
private int pageSize = 25;
|
||||
private ConfirmDialog deleteDialog = null!;
|
||||
private Material? materialToDelete;
|
||||
private string deleteMessage = "";
|
||||
private MaterialFilterState filterState = new();
|
||||
|
||||
private List<Material> filteredMaterials => materials.Where(m =>
|
||||
{
|
||||
if (filterState.Shape.HasValue && m.Shape != filterState.Shape.Value)
|
||||
return false;
|
||||
if (filterState.Type.HasValue && m.Type != filterState.Type.Value)
|
||||
return false;
|
||||
if (!string.IsNullOrEmpty(filterState.Grade) && m.Grade != filterState.Grade)
|
||||
return false;
|
||||
if (!string.IsNullOrWhiteSpace(filterState.SearchText))
|
||||
{
|
||||
var search = filterState.SearchText.Trim();
|
||||
if (!Contains(m.Size, search)
|
||||
&& !Contains(m.Grade, search)
|
||||
&& !Contains(m.Description, search)
|
||||
&& !Contains(m.Shape.GetDisplayName(), search))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}).ToList();
|
||||
|
||||
private IEnumerable<string> availableGrades => materials
|
||||
.Select(m => m.Grade)
|
||||
.Where(g => !string.IsNullOrEmpty(g))
|
||||
.Distinct()
|
||||
.OrderBy(g => g)!;
|
||||
|
||||
private IEnumerable<Material> pagedMaterials => filteredMaterials
|
||||
.Skip((currentPage - 1) * pageSize)
|
||||
.Take(pageSize);
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
materials = await MaterialService.GetAllAsync();
|
||||
loading = false;
|
||||
try
|
||||
{
|
||||
materials = await MaterialService.GetAllAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errorMessage = ex.ToString();
|
||||
}
|
||||
finally
|
||||
{
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFilterChanged(MaterialFilterState state)
|
||||
{
|
||||
filterState = state;
|
||||
currentPage = 1;
|
||||
}
|
||||
|
||||
private void ConfirmDelete(Material material)
|
||||
@@ -79,6 +158,15 @@ else
|
||||
{
|
||||
await MaterialService.DeleteAsync(materialToDelete.Id);
|
||||
materials = await MaterialService.GetAllAsync();
|
||||
|
||||
var totalPages = (int)Math.Ceiling((double)filteredMaterials.Count / pageSize);
|
||||
if (currentPage > totalPages && totalPages > 0)
|
||||
currentPage = totalPages;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPageChanged(int page) => currentPage = page;
|
||||
|
||||
private static bool Contains(string? value, string search) =>
|
||||
value != null && value.Contains(search, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
151
CutList.Web/Components/Pages/Orders/Add.razor
Normal file
151
CutList.Web/Components/Pages/Orders/Add.razor
Normal file
@@ -0,0 +1,151 @@
|
||||
@page "/orders/add"
|
||||
@inject PurchaseItemService PurchaseItemService
|
||||
@inject StockItemService StockItemService
|
||||
@inject SupplierService SupplierService
|
||||
@inject JobService JobService
|
||||
@inject NavigationManager Navigation
|
||||
@using CutList.Core.Formatting
|
||||
@using CutList.Web.Data.Entities
|
||||
|
||||
<PageTitle>Add Order Item</PageTitle>
|
||||
|
||||
<h1>Add Order Item</h1>
|
||||
|
||||
@if (loading)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Order Item Details</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<EditForm Model="item" OnValidSubmit="SaveAsync">
|
||||
<DataAnnotationsValidator />
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Stock Item</label>
|
||||
<select class="form-select" @bind="item.StockItemId">
|
||||
<option value="0">-- Select Stock Item --</option>
|
||||
@foreach (var group in stockItemGroups)
|
||||
{
|
||||
<optgroup label="@group.Key">
|
||||
@foreach (var si in group.Value)
|
||||
{
|
||||
<option value="@si.Id">@si.Material.Size - @ArchUnits.FormatFromInches((double)si.LengthInches)</option>
|
||||
}
|
||||
</optgroup>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Quantity</label>
|
||||
<InputNumber class="form-control" @bind-Value="item.Quantity" min="1" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Supplier (optional)</label>
|
||||
<select class="form-select" @bind="item.SupplierId">
|
||||
<option value="">-- Select Supplier --</option>
|
||||
@foreach (var supplier in suppliers)
|
||||
{
|
||||
<option value="@supplier.Id">@supplier.Name</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Job (optional)</label>
|
||||
<select class="form-select" @bind="item.JobId">
|
||||
<option value="">-- Select Job --</option>
|
||||
@foreach (var job in jobs)
|
||||
{
|
||||
<option value="@job.Id">@job.DisplayName</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Notes (optional)</label>
|
||||
<InputText class="form-control" @bind-Value="item.Notes" placeholder="Any notes about this order" />
|
||||
</div>
|
||||
|
||||
@if (!string.IsNullOrEmpty(errorMessage))
|
||||
{
|
||||
<div class="alert alert-danger">@errorMessage</div>
|
||||
}
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary" disabled="@saving">
|
||||
@if (saving)
|
||||
{
|
||||
<span class="spinner-border spinner-border-sm me-1"></span>
|
||||
}
|
||||
Add to Order List
|
||||
</button>
|
||||
<a href="orders" class="btn btn-outline-secondary">Cancel</a>
|
||||
</div>
|
||||
</EditForm>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
private PurchaseItem item = new() { Quantity = 1 };
|
||||
private List<StockItem> stockItems = new();
|
||||
private Dictionary<string, List<StockItem>> stockItemGroups = new();
|
||||
private List<Supplier> suppliers = new();
|
||||
private List<Job> jobs = new();
|
||||
private bool loading = true;
|
||||
private bool saving;
|
||||
private string? errorMessage;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
stockItems = await StockItemService.GetAllAsync();
|
||||
suppliers = await SupplierService.GetAllAsync();
|
||||
jobs = await JobService.GetAllAsync();
|
||||
|
||||
stockItemGroups = stockItems
|
||||
.GroupBy(s => s.Material.Shape.GetDisplayName())
|
||||
.OrderBy(g => g.Key)
|
||||
.ToDictionary(g => g.Key, g => g.OrderBy(s => s.Material.Size).ThenBy(s => s.LengthInches).ToList());
|
||||
|
||||
loading = false;
|
||||
}
|
||||
|
||||
private async Task SaveAsync()
|
||||
{
|
||||
errorMessage = null;
|
||||
saving = true;
|
||||
|
||||
try
|
||||
{
|
||||
if (item.StockItemId == 0)
|
||||
{
|
||||
errorMessage = "Please select a stock item";
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.Quantity <= 0)
|
||||
{
|
||||
errorMessage = "Quantity must be at least 1";
|
||||
return;
|
||||
}
|
||||
|
||||
await PurchaseItemService.CreateAsync(item);
|
||||
Navigation.NavigateTo("orders");
|
||||
}
|
||||
finally
|
||||
{
|
||||
saving = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
291
CutList.Web/Components/Pages/Orders/Index.razor
Normal file
291
CutList.Web/Components/Pages/Orders/Index.razor
Normal file
@@ -0,0 +1,291 @@
|
||||
@page "/orders"
|
||||
@inject PurchaseItemService PurchaseItemService
|
||||
@inject SupplierService SupplierService
|
||||
@inject NavigationManager Navigation
|
||||
@using CutList.Core.Formatting
|
||||
@using CutList.Web.Data.Entities
|
||||
|
||||
<PageTitle>Orders</PageTitle>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h1>To Be Ordered</h1>
|
||||
<a href="orders/add" class="btn btn-primary">Add Item</a>
|
||||
</div>
|
||||
|
||||
<p class="text-muted mb-4">
|
||||
Track material that needs to be ordered from suppliers. Items are added manually or automatically from job optimization results.
|
||||
</p>
|
||||
|
||||
@if (loading)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<ul class="nav nav-tabs mb-3">
|
||||
<li class="nav-item">
|
||||
<button class="nav-link @(activeTab == "pending" ? "active" : "")" @onclick='() => SetTab("pending")'>
|
||||
Pending
|
||||
@if (pendingCount > 0)
|
||||
{
|
||||
<span class="badge bg-warning text-dark ms-1">@pendingCount</span>
|
||||
}
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<button class="nav-link @(activeTab == "ordered" ? "active" : "")" @onclick='() => SetTab("ordered")'>
|
||||
Ordered
|
||||
@if (orderedCount > 0)
|
||||
{
|
||||
<span class="badge bg-primary ms-1">@orderedCount</span>
|
||||
}
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<button class="nav-link @(activeTab == "all" ? "active" : "")" @onclick='() => SetTab("all")'>All</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@if (tabItems.Count == 0)
|
||||
{
|
||||
<div class="alert alert-info">
|
||||
@if (activeTab == "pending")
|
||||
{
|
||||
<span>No pending items. <a href="orders/add">Add an item</a> or use "Add to Order List" from a job's results page.</span>
|
||||
}
|
||||
else if (activeTab == "ordered")
|
||||
{
|
||||
<span>No ordered items.</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>No order items found. <a href="orders/add">Add your first item</a>.</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MaterialFilter AvailableGrades="availableGrades" Value="filterState" ValueChanged="OnFilterChanged" />
|
||||
|
||||
@if (filteredItems.Count == 0)
|
||||
{
|
||||
<div class="alert alert-warning">
|
||||
No items match your filters.
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Material</th>
|
||||
<th>Length</th>
|
||||
<th>Qty</th>
|
||||
<th>Supplier</th>
|
||||
<th>Job</th>
|
||||
<th>Status</th>
|
||||
<th>Notes</th>
|
||||
<th style="width: 140px;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var item in pagedItems)
|
||||
{
|
||||
<tr>
|
||||
<td>@item.StockItem.Material.DisplayName</td>
|
||||
<td>@ArchUnits.FormatFromInches((double)item.StockItem.LengthInches)</td>
|
||||
<td>@item.Quantity</td>
|
||||
<td>
|
||||
<select class="form-select form-select-sm" style="min-width: 140px;"
|
||||
value="@(item.SupplierId?.ToString() ?? "")"
|
||||
@onchange="(e) => OnSupplierChanged(item, e)">
|
||||
<option value="">-- Select --</option>
|
||||
@foreach (var supplier in suppliers)
|
||||
{
|
||||
<option value="@supplier.Id">@supplier.Name</option>
|
||||
}
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
@if (item.Job != null)
|
||||
{
|
||||
<a href="jobs/@item.Job.Id">@item.Job.DisplayName</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="text-muted">-</span>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge @GetStatusBadgeClass(item.Status)">@item.Status</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="text-muted small">@(item.Notes ?? "-")</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex gap-1">
|
||||
@if (item.Status == PurchaseItemStatus.Pending)
|
||||
{
|
||||
<button class="btn btn-sm btn-outline-primary" @onclick="() => MarkOrdered(item)" title="Mark Ordered">
|
||||
<i class="bi bi-truck"></i>
|
||||
</button>
|
||||
}
|
||||
@if (item.Status == PurchaseItemStatus.Ordered)
|
||||
{
|
||||
<button class="btn btn-sm btn-outline-success" @onclick="() => MarkReceived(item)" title="Mark Received">
|
||||
<i class="bi bi-check-lg"></i>
|
||||
</button>
|
||||
}
|
||||
<button class="btn btn-sm btn-outline-danger" @onclick="() => ConfirmDelete(item)" title="Delete">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<Pager TotalCount="filteredItems.Count" PageSize="pageSize" CurrentPage="currentPage" CurrentPageChanged="OnPageChanged" />
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<ConfirmDialog @ref="deleteDialog"
|
||||
Title="Delete Order Item"
|
||||
Message="@deleteMessage"
|
||||
ConfirmText="Delete"
|
||||
OnConfirm="DeleteConfirmed" />
|
||||
|
||||
@code {
|
||||
private List<PurchaseItem> allItems = new();
|
||||
private List<Supplier> suppliers = new();
|
||||
private bool loading = true;
|
||||
private string activeTab = "pending";
|
||||
private int currentPage = 1;
|
||||
private int pageSize = 25;
|
||||
private ConfirmDialog deleteDialog = null!;
|
||||
private PurchaseItem? itemToDelete;
|
||||
private string deleteMessage = "";
|
||||
private MaterialFilterState filterState = new();
|
||||
|
||||
private int pendingCount => allItems.Count(i => i.Status == PurchaseItemStatus.Pending);
|
||||
private int orderedCount => allItems.Count(i => i.Status == PurchaseItemStatus.Ordered);
|
||||
|
||||
private List<PurchaseItem> tabItems => activeTab switch
|
||||
{
|
||||
"pending" => allItems.Where(i => i.Status == PurchaseItemStatus.Pending).ToList(),
|
||||
"ordered" => allItems.Where(i => i.Status == PurchaseItemStatus.Ordered).ToList(),
|
||||
_ => allItems
|
||||
};
|
||||
|
||||
private List<PurchaseItem> filteredItems => tabItems.Where(i =>
|
||||
{
|
||||
var m = i.StockItem.Material;
|
||||
if (filterState.Shape.HasValue && m.Shape != filterState.Shape.Value)
|
||||
return false;
|
||||
if (filterState.Type.HasValue && m.Type != filterState.Type.Value)
|
||||
return false;
|
||||
if (!string.IsNullOrEmpty(filterState.Grade) && m.Grade != filterState.Grade)
|
||||
return false;
|
||||
if (!string.IsNullOrWhiteSpace(filterState.SearchText))
|
||||
{
|
||||
var search = filterState.SearchText.Trim();
|
||||
if (!Contains(m.Size, search)
|
||||
&& !Contains(m.Grade, search)
|
||||
&& !Contains(m.Shape.GetDisplayName(), search)
|
||||
&& !Contains(i.Notes, search)
|
||||
&& !Contains(i.Job?.DisplayName, search)
|
||||
&& !Contains(i.Supplier?.Name, search))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}).ToList();
|
||||
|
||||
private IEnumerable<string> availableGrades => tabItems
|
||||
.Select(i => i.StockItem.Material.Grade)
|
||||
.Where(g => !string.IsNullOrEmpty(g))
|
||||
.Distinct()
|
||||
.OrderBy(g => g)!;
|
||||
|
||||
private IEnumerable<PurchaseItem> pagedItems => filteredItems
|
||||
.Skip((currentPage - 1) * pageSize)
|
||||
.Take(pageSize);
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
allItems = await PurchaseItemService.GetAllAsync();
|
||||
suppliers = await SupplierService.GetAllAsync();
|
||||
loading = false;
|
||||
}
|
||||
|
||||
private void SetTab(string tab)
|
||||
{
|
||||
activeTab = tab;
|
||||
currentPage = 1;
|
||||
filterState = new();
|
||||
}
|
||||
|
||||
private void OnFilterChanged(MaterialFilterState state)
|
||||
{
|
||||
filterState = state;
|
||||
currentPage = 1;
|
||||
}
|
||||
|
||||
private async Task OnSupplierChanged(PurchaseItem item, ChangeEventArgs e)
|
||||
{
|
||||
int? supplierId = int.TryParse(e.Value?.ToString(), out var id) && id > 0 ? id : null;
|
||||
await PurchaseItemService.UpdateSupplierAsync(item.Id, supplierId);
|
||||
item.SupplierId = supplierId;
|
||||
item.Supplier = supplierId.HasValue ? suppliers.FirstOrDefault(s => s.Id == supplierId.Value) : null;
|
||||
}
|
||||
|
||||
private async Task MarkOrdered(PurchaseItem item)
|
||||
{
|
||||
await PurchaseItemService.UpdateStatusAsync(item.Id, PurchaseItemStatus.Ordered);
|
||||
item.Status = PurchaseItemStatus.Ordered;
|
||||
}
|
||||
|
||||
private async Task MarkReceived(PurchaseItem item)
|
||||
{
|
||||
await PurchaseItemService.UpdateStatusAsync(item.Id, PurchaseItemStatus.Received);
|
||||
allItems = await PurchaseItemService.GetAllAsync();
|
||||
|
||||
var totalPages = (int)Math.Ceiling((double)filteredItems.Count / pageSize);
|
||||
if (currentPage > totalPages && totalPages > 0)
|
||||
currentPage = totalPages;
|
||||
}
|
||||
|
||||
private void ConfirmDelete(PurchaseItem item)
|
||||
{
|
||||
itemToDelete = item;
|
||||
deleteMessage = $"Are you sure you want to delete this order item ({item.StockItem.Material.DisplayName} - {ArchUnits.FormatFromInches((double)item.StockItem.LengthInches)} x{item.Quantity})?";
|
||||
deleteDialog.Show();
|
||||
}
|
||||
|
||||
private async Task DeleteConfirmed()
|
||||
{
|
||||
if (itemToDelete != null)
|
||||
{
|
||||
await PurchaseItemService.DeleteAsync(itemToDelete.Id);
|
||||
allItems = await PurchaseItemService.GetAllAsync();
|
||||
|
||||
var totalPages = (int)Math.Ceiling((double)filteredItems.Count / pageSize);
|
||||
if (currentPage > totalPages && totalPages > 0)
|
||||
currentPage = totalPages;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPageChanged(int page) => currentPage = page;
|
||||
|
||||
private static string GetStatusBadgeClass(PurchaseItemStatus status) => status switch
|
||||
{
|
||||
PurchaseItemStatus.Pending => "bg-warning text-dark",
|
||||
PurchaseItemStatus.Ordered => "bg-primary",
|
||||
PurchaseItemStatus.Received => "bg-success",
|
||||
_ => "bg-secondary"
|
||||
};
|
||||
|
||||
private static bool Contains(string? value, string search) =>
|
||||
value != null && value.Contains(search, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
@@ -1,399 +0,0 @@
|
||||
@page "/projects/new"
|
||||
@page "/projects/{Id:int}"
|
||||
@inject ProjectService ProjectService
|
||||
@inject MaterialService MaterialService
|
||||
@inject NavigationManager Navigation
|
||||
@using CutList.Core.Formatting
|
||||
|
||||
<PageTitle>@(IsNew ? "New Project" : project.Name)</PageTitle>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h1>@(IsNew ? "New Project" : project.Name)</h1>
|
||||
@if (!IsNew)
|
||||
{
|
||||
<a href="projects/@Id/results" class="btn btn-success">Run Optimization</a>
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (loading)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else if (IsNew)
|
||||
{
|
||||
<!-- New Project: Simple form -->
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
@RenderDetailsForm()
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<!-- Existing Project: Tabbed interface -->
|
||||
<ul class="nav nav-tabs mb-3" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link @(activeTab == Tab.Details ? "active" : "")"
|
||||
@onclick="() => SetTab(Tab.Details)" type="button">
|
||||
Details
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link @(activeTab == Tab.Parts ? "active" : "")"
|
||||
@onclick="() => SetTab(Tab.Parts)" type="button">
|
||||
Parts
|
||||
@if (project.Parts.Count > 0)
|
||||
{
|
||||
<span class="badge bg-secondary ms-1">@project.Parts.Sum(p => p.Quantity)</span>
|
||||
}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
@if (activeTab == Tab.Details)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
@RenderDetailsForm()
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else if (activeTab == Tab.Parts)
|
||||
{
|
||||
@RenderPartsTab()
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
private enum Tab { Details, Parts }
|
||||
|
||||
[Parameter]
|
||||
public int? Id { get; set; }
|
||||
|
||||
private Project project = new();
|
||||
private List<Material> materials = new();
|
||||
private List<CuttingTool> cuttingTools = new();
|
||||
|
||||
private bool loading = true;
|
||||
private bool savingProject;
|
||||
private string? projectErrorMessage;
|
||||
private Tab activeTab = Tab.Details;
|
||||
|
||||
private void SetTab(Tab tab) => activeTab = tab;
|
||||
|
||||
// Parts form
|
||||
private bool showPartForm;
|
||||
private ProjectPart newPart = new();
|
||||
private ProjectPart? editingPart;
|
||||
private string? partErrorMessage;
|
||||
private string selectedShape = string.Empty;
|
||||
|
||||
private IEnumerable<string> DistinctShapes => materials.Select(m => m.Shape).Distinct().OrderBy(s => s);
|
||||
private IEnumerable<Material> FilteredMaterials => string.IsNullOrEmpty(selectedShape)
|
||||
? Enumerable.Empty<Material>()
|
||||
: materials.Where(m => m.Shape == selectedShape).OrderBy(m => m.Size);
|
||||
|
||||
private bool IsNew => !Id.HasValue;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
materials = await MaterialService.GetAllAsync();
|
||||
cuttingTools = await ProjectService.GetCuttingToolsAsync();
|
||||
|
||||
if (Id.HasValue)
|
||||
{
|
||||
var existing = await ProjectService.GetByIdAsync(Id.Value);
|
||||
if (existing == null)
|
||||
{
|
||||
Navigation.NavigateTo("projects");
|
||||
return;
|
||||
}
|
||||
project = existing;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set default cutting tool for new projects
|
||||
var defaultTool = await ProjectService.GetDefaultCuttingToolAsync();
|
||||
if (defaultTool != null)
|
||||
{
|
||||
project.CuttingToolId = defaultTool.Id;
|
||||
}
|
||||
}
|
||||
|
||||
loading = false;
|
||||
}
|
||||
|
||||
private RenderFragment RenderDetailsForm() => __builder =>
|
||||
{
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Project Details</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<EditForm Model="project" OnValidSubmit="SaveProjectAsync">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Project Name</label>
|
||||
<InputText class="form-control" @bind-Value="project.Name" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Customer</label>
|
||||
<InputText class="form-control" @bind-Value="project.Customer" placeholder="Customer name" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Cutting Tool</label>
|
||||
<InputSelect class="form-select" @bind-Value="project.CuttingToolId">
|
||||
<option value="">-- Select Tool --</option>
|
||||
@foreach (var tool in cuttingTools)
|
||||
{
|
||||
<option value="@tool.Id">@tool.Name (@tool.KerfInches" kerf)</option>
|
||||
}
|
||||
</InputSelect>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Notes</label>
|
||||
<InputTextArea class="form-control" @bind-Value="project.Notes" rows="3" />
|
||||
</div>
|
||||
|
||||
@if (!string.IsNullOrEmpty(projectErrorMessage))
|
||||
{
|
||||
<div class="alert alert-danger">@projectErrorMessage</div>
|
||||
}
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary" disabled="@savingProject">
|
||||
@if (savingProject)
|
||||
{
|
||||
<span class="spinner-border spinner-border-sm me-1"></span>
|
||||
}
|
||||
@(IsNew ? "Create Project" : "Save")
|
||||
</button>
|
||||
<a href="projects" class="btn btn-outline-secondary">Back</a>
|
||||
</div>
|
||||
</EditForm>
|
||||
</div>
|
||||
</div>
|
||||
};
|
||||
|
||||
private RenderFragment RenderPartsTab() => __builder =>
|
||||
{
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Parts to Cut</h5>
|
||||
<button class="btn btn-primary" @onclick="ShowAddPartForm">Add Part</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if (showPartForm)
|
||||
{
|
||||
<div class="border rounded p-3 mb-3 bg-light">
|
||||
<h6>@(editingPart == null ? "Add Part" : "Edit Part")</h6>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Shape</label>
|
||||
<select class="form-select" @bind="selectedShape" @bind:after="OnShapeChanged">
|
||||
<option value="">-- Select --</option>
|
||||
@foreach (var shape in DistinctShapes)
|
||||
{
|
||||
<option value="@shape">@shape</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Size</label>
|
||||
<select class="form-select" @bind="newPart.MaterialId" disabled="@string.IsNullOrEmpty(selectedShape)">
|
||||
<option value="0">-- Select --</option>
|
||||
@foreach (var material in FilteredMaterials)
|
||||
{
|
||||
<option value="@material.Id">@material.Size</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Length</label>
|
||||
<LengthInput @bind-Value="newPart.LengthInches" />
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Qty</label>
|
||||
<input type="number" class="form-control" @bind="newPart.Quantity" min="1" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Name <span class="text-muted fw-normal">(optional)</span></label>
|
||||
<input type="text" class="form-control" @bind="newPart.Name" placeholder="Part name" />
|
||||
</div>
|
||||
</div>
|
||||
@if (!string.IsNullOrEmpty(partErrorMessage))
|
||||
{
|
||||
<div class="alert alert-danger mt-3 mb-0">@partErrorMessage</div>
|
||||
}
|
||||
<div class="mt-3 d-flex gap-2">
|
||||
<button class="btn btn-primary" @onclick="SavePartAsync">@(editingPart == null ? "Add Part" : "Save Changes")</button>
|
||||
<button class="btn btn-outline-secondary" @onclick="CancelPartForm">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (project.Parts.Count == 0)
|
||||
{
|
||||
<div class="text-center py-4 text-muted">
|
||||
<p class="mb-2">No parts added yet.</p>
|
||||
<p class="small">Add the parts you need to cut, selecting the material for each.</p>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Material</th>
|
||||
<th>Length</th>
|
||||
<th>Qty</th>
|
||||
<th>Name</th>
|
||||
<th style="width: 120px;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var part in project.Parts)
|
||||
{
|
||||
<tr>
|
||||
<td>@part.Material.DisplayName</td>
|
||||
<td>@ArchUnits.FormatFromInches((double)part.LengthInches)</td>
|
||||
<td>@part.Quantity</td>
|
||||
<td>@(string.IsNullOrWhiteSpace(part.Name) ? "-" : part.Name)</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-primary me-1" @onclick="() => EditPart(part)">Edit</button>
|
||||
<button class="btn btn-sm btn-outline-danger" @onclick="() => DeletePart(part)">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mt-3 text-muted">
|
||||
Total: @project.Parts.Sum(p => p.Quantity) pieces
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
};
|
||||
|
||||
private async Task SaveProjectAsync()
|
||||
{
|
||||
projectErrorMessage = null;
|
||||
savingProject = true;
|
||||
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(project.Name))
|
||||
{
|
||||
projectErrorMessage = "Project name is required";
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsNew)
|
||||
{
|
||||
var created = await ProjectService.CreateAsync(project);
|
||||
Navigation.NavigateTo($"projects/{created.Id}");
|
||||
}
|
||||
else
|
||||
{
|
||||
await ProjectService.UpdateAsync(project);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
savingProject = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Parts methods
|
||||
private void ShowAddPartForm()
|
||||
{
|
||||
editingPart = null;
|
||||
newPart = new ProjectPart { ProjectId = Id!.Value, Quantity = 1 };
|
||||
selectedShape = string.Empty;
|
||||
showPartForm = true;
|
||||
partErrorMessage = null;
|
||||
}
|
||||
|
||||
private void OnShapeChanged()
|
||||
{
|
||||
newPart.MaterialId = 0;
|
||||
}
|
||||
|
||||
private void EditPart(ProjectPart part)
|
||||
{
|
||||
editingPart = part;
|
||||
newPart = new ProjectPart
|
||||
{
|
||||
Id = part.Id,
|
||||
ProjectId = part.ProjectId,
|
||||
MaterialId = part.MaterialId,
|
||||
Name = part.Name,
|
||||
LengthInches = part.LengthInches,
|
||||
Quantity = part.Quantity,
|
||||
SortOrder = part.SortOrder
|
||||
};
|
||||
selectedShape = part.Material?.Shape ?? string.Empty;
|
||||
showPartForm = true;
|
||||
partErrorMessage = null;
|
||||
}
|
||||
|
||||
private void CancelPartForm()
|
||||
{
|
||||
showPartForm = false;
|
||||
editingPart = null;
|
||||
}
|
||||
|
||||
private async Task SavePartAsync()
|
||||
{
|
||||
partErrorMessage = null;
|
||||
|
||||
if (string.IsNullOrEmpty(selectedShape))
|
||||
{
|
||||
partErrorMessage = "Please select a shape";
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPart.MaterialId == 0)
|
||||
{
|
||||
partErrorMessage = "Please select a size";
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPart.LengthInches <= 0)
|
||||
{
|
||||
partErrorMessage = "Length must be greater than zero";
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPart.Quantity < 1)
|
||||
{
|
||||
partErrorMessage = "Quantity must be at least 1";
|
||||
return;
|
||||
}
|
||||
|
||||
if (editingPart == null)
|
||||
{
|
||||
await ProjectService.AddPartAsync(newPart);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ProjectService.UpdatePartAsync(newPart);
|
||||
}
|
||||
|
||||
project = (await ProjectService.GetByIdAsync(Id!.Value))!;
|
||||
showPartForm = false;
|
||||
editingPart = null;
|
||||
}
|
||||
|
||||
private async Task DeletePart(ProjectPart part)
|
||||
{
|
||||
await ProjectService.DeletePartAsync(part.Id);
|
||||
project = (await ProjectService.GetByIdAsync(Id!.Value))!;
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
@page "/projects"
|
||||
@inject ProjectService ProjectService
|
||||
@inject NavigationManager Navigation
|
||||
|
||||
<PageTitle>Projects</PageTitle>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h1>Projects</h1>
|
||||
<a href="projects/new" class="btn btn-primary">New Project</a>
|
||||
</div>
|
||||
|
||||
@if (loading)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else if (projects.Count == 0)
|
||||
{
|
||||
<div class="alert alert-info">
|
||||
No projects found. <a href="projects/new">Create your first project</a>.
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Customer</th>
|
||||
<th>Cutting Tool</th>
|
||||
<th>Last Modified</th>
|
||||
<th style="width: 200px;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var project in projects)
|
||||
{
|
||||
<tr>
|
||||
<td><a href="projects/@project.Id">@project.Name</a></td>
|
||||
<td>@(project.Customer ?? "-")</td>
|
||||
<td>@(project.CuttingTool?.Name ?? "-")</td>
|
||||
<td>@((project.UpdatedAt ?? project.CreatedAt).ToLocalTime().ToString("g"))</td>
|
||||
<td>
|
||||
<a href="projects/@project.Id" class="btn btn-sm btn-outline-primary">Edit</a>
|
||||
<a href="projects/@project.Id/results" class="btn btn-sm btn-success">Optimize</a>
|
||||
<button class="btn btn-sm btn-outline-secondary" @onclick="() => DuplicateProject(project)">Copy</button>
|
||||
<button class="btn btn-sm btn-outline-danger" @onclick="() => ConfirmDelete(project)">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
|
||||
<ConfirmDialog @ref="deleteDialog"
|
||||
Title="Delete Project"
|
||||
Message="@deleteMessage"
|
||||
ConfirmText="Delete"
|
||||
OnConfirm="DeleteConfirmed" />
|
||||
|
||||
@code {
|
||||
private List<Project> projects = new();
|
||||
private bool loading = true;
|
||||
private ConfirmDialog deleteDialog = null!;
|
||||
private Project? projectToDelete;
|
||||
private string deleteMessage = "";
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
projects = await ProjectService.GetAllAsync();
|
||||
loading = false;
|
||||
}
|
||||
|
||||
private void ConfirmDelete(Project project)
|
||||
{
|
||||
projectToDelete = project;
|
||||
deleteMessage = $"Are you sure you want to delete \"{project.Name}\"? This will also delete all parts and stock bins.";
|
||||
deleteDialog.Show();
|
||||
}
|
||||
|
||||
private async Task DeleteConfirmed()
|
||||
{
|
||||
if (projectToDelete != null)
|
||||
{
|
||||
await ProjectService.DeleteAsync(projectToDelete.Id);
|
||||
projects = await ProjectService.GetAllAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DuplicateProject(Project project)
|
||||
{
|
||||
var duplicate = await ProjectService.DuplicateAsync(project.Id);
|
||||
Navigation.NavigateTo($"projects/{duplicate.Id}");
|
||||
}
|
||||
}
|
||||
@@ -1,257 +0,0 @@
|
||||
@page "/projects/{Id:int}/results"
|
||||
@inject ProjectService ProjectService
|
||||
@inject CutListPackingService PackingService
|
||||
@inject NavigationManager Navigation
|
||||
@inject IJSRuntime JS
|
||||
@using CutList.Core
|
||||
@using CutList.Core.Nesting
|
||||
@using CutList.Core.Formatting
|
||||
|
||||
<PageTitle>Results - @(project?.Name ?? "Project")</PageTitle>
|
||||
|
||||
@if (loading)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else if (project == null)
|
||||
{
|
||||
<div class="alert alert-danger">Project not found.</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div>
|
||||
<h1>@project.Name</h1>
|
||||
@if (!string.IsNullOrWhiteSpace(project.Customer))
|
||||
{
|
||||
<p class="text-muted mb-0">Customer: @project.Customer</p>
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
<a href="projects/@Id" class="btn btn-outline-secondary me-2">Edit Project</a>
|
||||
<button class="btn btn-primary" @onclick="PrintReport">Print Report</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (!CanOptimize)
|
||||
{
|
||||
<div class="alert alert-warning">
|
||||
<h4>Cannot Optimize</h4>
|
||||
<ul class="mb-0">
|
||||
@if (project.Parts.Count == 0)
|
||||
{
|
||||
<li>No parts defined. <a href="projects/@Id">Add parts to the project</a>.</li>
|
||||
}
|
||||
@if (project.CuttingToolId == null)
|
||||
{
|
||||
<li>No cutting tool selected. <a href="projects/@Id">Select a cutting tool</a>.</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
else if (packResult != null)
|
||||
{
|
||||
@if (summary!.TotalItemsNotPlaced > 0)
|
||||
{
|
||||
<div class="alert alert-warning">
|
||||
<h5>Items Not Placed</h5>
|
||||
<p>Some items could not be placed. This usually means no stock lengths are configured for the material, or parts are too long.</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Overall Summary Cards -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-3 col-6 mb-3">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title mb-0">@(summary.TotalInStockBins + summary.TotalToBePurchasedBins)</h2>
|
||||
<p class="card-text text-muted">Total Stock Bars</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-6 mb-3">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title mb-0">@summary.TotalPieces</h2>
|
||||
<p class="card-text text-muted">Total Pieces</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-6 mb-3">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title mb-0">@ArchUnits.FormatFromInches(summary.TotalWaste)</h2>
|
||||
<p class="card-text text-muted">Total Waste</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-6 mb-3">
|
||||
<div class="card text-center">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title mb-0">@summary.Efficiency.ToString("F1")%</h2>
|
||||
<p class="card-text text-muted">Efficiency</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stock Summary -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6 mb-3">
|
||||
<div class="card border-success">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h5 class="mb-0">In Stock</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h3>@summary.TotalInStockBins bars</h3>
|
||||
<p class="text-muted mb-0">Ready to cut from existing inventory</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<div class="card border-warning">
|
||||
<div class="card-header bg-warning">
|
||||
<h5 class="mb-0">To Be Purchased</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h3>@summary.TotalToBePurchasedBins bars</h3>
|
||||
<p class="text-muted mb-0">Need to order from supplier</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Results by Material -->
|
||||
@foreach (var materialResult in packResult.MaterialResults)
|
||||
{
|
||||
var materialSummary = summary.MaterialSummaries.First(s => s.Material.Id == materialResult.Material.Id);
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h4 class="mb-0">@materialResult.Material.DisplayName</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Material Summary -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-2 col-4">
|
||||
<strong>@(materialSummary.InStockBins + materialSummary.ToBePurchasedBins)</strong> bars
|
||||
</div>
|
||||
<div class="col-md-2 col-4">
|
||||
<strong>@materialSummary.TotalPieces</strong> pieces
|
||||
</div>
|
||||
<div class="col-md-2 col-4">
|
||||
<strong>@materialSummary.Efficiency.ToString("F1")%</strong> efficiency
|
||||
</div>
|
||||
<div class="col-md-3 col-6">
|
||||
<span class="text-success">@materialSummary.InStockBins in stock</span>
|
||||
</div>
|
||||
<div class="col-md-3 col-6">
|
||||
<span class="text-warning">@materialSummary.ToBePurchasedBins to purchase</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (materialResult.PackResult.ItemsNotUsed.Count > 0)
|
||||
{
|
||||
<div class="alert alert-danger">
|
||||
<strong>@materialResult.PackResult.ItemsNotUsed.Count items not placed</strong> -
|
||||
No stock lengths available or parts too long.
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (materialResult.InStockBins.Count > 0)
|
||||
{
|
||||
<h5 class="text-success mt-3">In Stock (@materialResult.InStockBins.Count bars)</h5>
|
||||
@RenderBinList(materialResult.InStockBins)
|
||||
}
|
||||
|
||||
@if (materialResult.ToBePurchasedBins.Count > 0)
|
||||
{
|
||||
<h5 class="text-warning mt-3">To Be Purchased (@materialResult.ToBePurchasedBins.Count bars)</h5>
|
||||
@RenderBinList(materialResult.ToBePurchasedBins)
|
||||
|
||||
<!-- Purchase Summary -->
|
||||
<div class="mt-3 p-3 bg-light rounded">
|
||||
<strong>Order Summary:</strong>
|
||||
<ul class="mb-0 mt-2">
|
||||
@foreach (var group in materialResult.ToBePurchasedBins.GroupBy(b => b.Length).OrderByDescending(g => g.Key))
|
||||
{
|
||||
<li>@group.Count() x @ArchUnits.FormatFromInches(group.Key)</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public int Id { get; set; }
|
||||
|
||||
private Project? project;
|
||||
private MultiMaterialPackResult? packResult;
|
||||
private MultiMaterialPackingSummary? summary;
|
||||
private bool loading = true;
|
||||
|
||||
private bool CanOptimize => project != null &&
|
||||
project.Parts.Count > 0 &&
|
||||
project.CuttingToolId != null;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
project = await ProjectService.GetByIdAsync(Id);
|
||||
|
||||
if (project != null && CanOptimize)
|
||||
{
|
||||
var kerf = project.CuttingTool?.KerfInches ?? 0.125m;
|
||||
packResult = await PackingService.PackAsync(project.Parts, kerf);
|
||||
summary = PackingService.GetSummary(packResult);
|
||||
}
|
||||
|
||||
loading = false;
|
||||
}
|
||||
|
||||
private RenderFragment RenderBinList(List<Bin> bins) => __builder =>
|
||||
{
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 80px;">#</th>
|
||||
<th>Stock Length</th>
|
||||
<th>Cuts</th>
|
||||
<th>Waste</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@{ var binNumber = 1; }
|
||||
@foreach (var bin in bins)
|
||||
{
|
||||
<tr>
|
||||
<td>@binNumber</td>
|
||||
<td>@ArchUnits.FormatFromInches(bin.Length)</td>
|
||||
<td>
|
||||
@foreach (var item in bin.Items)
|
||||
{
|
||||
<span class="badge bg-primary me-1">
|
||||
@(string.IsNullOrWhiteSpace(item.Name) ? ArchUnits.FormatFromInches(item.Length) : $"{item.Name} ({ArchUnits.FormatFromInches(item.Length)})")
|
||||
</span>
|
||||
}
|
||||
</td>
|
||||
<td>@ArchUnits.FormatFromInches(bin.RemainingLength)</td>
|
||||
</tr>
|
||||
binNumber++;
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
};
|
||||
|
||||
private async Task PrintReport()
|
||||
{
|
||||
var filename = $"CutList - {project!.Name} - {DateTime.Now:yyyy-MM-dd}";
|
||||
await JS.InvokeVoidAsync("printWithTitle", filename);
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,11 @@ else
|
||||
<InputText class="form-control" @bind-Value="stockItem.Name" placeholder="Custom display name" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Notes (optional)</label>
|
||||
<InputText class="form-control" @bind-Value="stockItem.Notes" placeholder="Internal notes" />
|
||||
</div>
|
||||
|
||||
@if (!string.IsNullOrEmpty(errorMessage))
|
||||
{
|
||||
<div class="alert alert-danger">@errorMessage</div>
|
||||
@@ -76,7 +81,111 @@ else
|
||||
|
||||
@if (!IsNew)
|
||||
{
|
||||
<div class="col-lg-6">
|
||||
<div class="col-lg-6 mb-4">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">
|
||||
Inventory
|
||||
<span class="badge @(stockItem.QuantityOnHand > 0 ? "bg-success" : "bg-secondary") ms-2">@stockItem.QuantityOnHand on hand</span>
|
||||
</h5>
|
||||
<button class="btn btn-sm btn-primary" @onclick="ShowStockForm">Add/Adjust Stock</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if (showStockForm)
|
||||
{
|
||||
<div class="border rounded p-3 mb-3 bg-light">
|
||||
<h6>Stock Transaction</h6>
|
||||
<div class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Type</label>
|
||||
<select class="form-select" @bind="stockTransactionType">
|
||||
<option value="add">Receive Stock</option>
|
||||
<option value="adjust">Set Quantity</option>
|
||||
<option value="scrap">Scrap/Waste</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">@(stockTransactionType == "adjust" ? "New Quantity" : "Quantity")</label>
|
||||
<input type="number" class="form-control" @bind="stockQuantity" min="0" />
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Notes</label>
|
||||
<InputText class="form-control" @bind-Value="stockNotes" />
|
||||
</div>
|
||||
@if (stockTransactionType == "add")
|
||||
{
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Supplier (optional)</label>
|
||||
<select class="form-select" @bind="stockSupplierId">
|
||||
<option value="0">-- Select Supplier --</option>
|
||||
@foreach (var supplier in suppliers)
|
||||
{
|
||||
<option value="@supplier.Id">@supplier.Name</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Unit Price (optional)</label>
|
||||
<input type="number" class="form-control" @bind="stockUnitPrice" step="0.01" min="0" placeholder="0.00" />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@if (!string.IsNullOrEmpty(stockFormErrorMessage))
|
||||
{
|
||||
<div class="alert alert-danger mt-2 mb-0">@stockFormErrorMessage</div>
|
||||
}
|
||||
<div class="mt-3 d-flex gap-2">
|
||||
<button class="btn btn-primary btn-sm" @onclick="SaveStockTransactionAsync" disabled="@savingStockTransaction">
|
||||
@if (savingStockTransaction)
|
||||
{
|
||||
<span class="spinner-border spinner-border-sm me-1"></span>
|
||||
}
|
||||
Save
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" @onclick="CancelStockForm">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (transactions.Count == 0)
|
||||
{
|
||||
<p class="text-muted">No transaction history yet.</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Type</th>
|
||||
<th>Qty</th>
|
||||
<th>Supplier</th>
|
||||
<th>Price</th>
|
||||
<th>Notes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var txn in transactions)
|
||||
{
|
||||
<tr>
|
||||
<td>@txn.CreatedAt.ToLocalTime().ToString("MM/dd/yy HH:mm")</td>
|
||||
<td>
|
||||
<span class="badge @GetTransactionBadgeClass(txn.Type)">@txn.Type</span>
|
||||
</td>
|
||||
<td class="@(txn.Quantity >= 0 ? "text-success" : "text-danger")">
|
||||
@(txn.Quantity >= 0 ? "+" : "")@txn.Quantity
|
||||
</td>
|
||||
<td>@(txn.Supplier?.Name ?? "-")</td>
|
||||
<td>@(txn.UnitPrice.HasValue ? txn.UnitPrice.Value.ToString("C") : "-")</td>
|
||||
<td>@(txn.Notes ?? "-")</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Supplier Offerings</h5>
|
||||
@@ -155,8 +264,8 @@ else
|
||||
<td>@(offering.PartNumber ?? "-")</td>
|
||||
<td>@(offering.Price.HasValue ? offering.Price.Value.ToString("C") : "-")</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-primary" @onclick="() => EditOffering(offering)">Edit</button>
|
||||
<button class="btn btn-sm btn-outline-danger" @onclick="() => ConfirmDeleteOffering(offering)">Delete</button>
|
||||
<button class="btn btn-sm btn-outline-primary" @onclick="() => EditOffering(offering)" title="Edit"><i class="bi bi-pencil"></i></button>
|
||||
<button class="btn btn-sm btn-outline-danger" @onclick="() => ConfirmDeleteOffering(offering)" title="Delete"><i class="bi bi-trash"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
@@ -184,6 +293,7 @@ else
|
||||
private List<Material> materials = new();
|
||||
private List<Supplier> suppliers = new();
|
||||
private List<SupplierOffering> offerings = new();
|
||||
private List<StockTransaction> transactions = new();
|
||||
private bool loading = true;
|
||||
private bool saving;
|
||||
private bool savingOffering;
|
||||
@@ -194,6 +304,16 @@ else
|
||||
private SupplierOffering newOffering = new();
|
||||
private SupplierOffering? editingOffering;
|
||||
|
||||
// Stock transaction form
|
||||
private bool showStockForm;
|
||||
private bool savingStockTransaction;
|
||||
private string stockTransactionType = "add";
|
||||
private int stockQuantity;
|
||||
private int stockSupplierId;
|
||||
private decimal? stockUnitPrice;
|
||||
private string? stockNotes;
|
||||
private string? stockFormErrorMessage;
|
||||
|
||||
private ConfirmDialog deleteOfferingDialog = null!;
|
||||
private SupplierOffering? offeringToDelete;
|
||||
private string deleteOfferingMessage = "";
|
||||
@@ -215,10 +335,90 @@ else
|
||||
}
|
||||
stockItem = existing;
|
||||
offerings = existing.SupplierOfferings.Where(o => o.IsActive).ToList();
|
||||
transactions = await StockItemService.GetTransactionHistoryAsync(Id.Value, 20);
|
||||
}
|
||||
loading = false;
|
||||
}
|
||||
|
||||
private string GetTransactionBadgeClass(StockTransactionType type) => type switch
|
||||
{
|
||||
StockTransactionType.Received => "bg-success",
|
||||
StockTransactionType.Used => "bg-primary",
|
||||
StockTransactionType.Adjustment => "bg-warning text-dark",
|
||||
StockTransactionType.Scrapped => "bg-danger",
|
||||
StockTransactionType.Returned => "bg-info",
|
||||
_ => "bg-secondary"
|
||||
};
|
||||
|
||||
private void ShowStockForm()
|
||||
{
|
||||
stockTransactionType = "add";
|
||||
stockQuantity = 0;
|
||||
stockSupplierId = 0;
|
||||
stockUnitPrice = null;
|
||||
stockNotes = null;
|
||||
stockFormErrorMessage = null;
|
||||
showStockForm = true;
|
||||
}
|
||||
|
||||
private void CancelStockForm()
|
||||
{
|
||||
showStockForm = false;
|
||||
stockFormErrorMessage = null;
|
||||
}
|
||||
|
||||
private async Task SaveStockTransactionAsync()
|
||||
{
|
||||
stockFormErrorMessage = null;
|
||||
savingStockTransaction = true;
|
||||
|
||||
try
|
||||
{
|
||||
if (stockQuantity <= 0 && stockTransactionType != "adjust")
|
||||
{
|
||||
stockFormErrorMessage = "Quantity must be greater than zero";
|
||||
return;
|
||||
}
|
||||
|
||||
if (stockTransactionType == "adjust" && stockQuantity < 0)
|
||||
{
|
||||
stockFormErrorMessage = "Quantity cannot be negative";
|
||||
return;
|
||||
}
|
||||
|
||||
switch (stockTransactionType)
|
||||
{
|
||||
case "add":
|
||||
await StockItemService.AddStockAsync(
|
||||
Id!.Value,
|
||||
stockQuantity,
|
||||
stockSupplierId > 0 ? stockSupplierId : null,
|
||||
stockUnitPrice,
|
||||
stockNotes);
|
||||
break;
|
||||
case "adjust":
|
||||
await StockItemService.AdjustStockAsync(Id!.Value, stockQuantity, stockNotes);
|
||||
break;
|
||||
case "scrap":
|
||||
await StockItemService.ScrapStockAsync(Id!.Value, stockQuantity, stockNotes);
|
||||
break;
|
||||
}
|
||||
|
||||
// Refresh
|
||||
var updated = await StockItemService.GetByIdAsync(Id!.Value);
|
||||
if (updated != null)
|
||||
{
|
||||
stockItem = updated;
|
||||
}
|
||||
transactions = await StockItemService.GetTransactionHistoryAsync(Id!.Value, 20);
|
||||
showStockForm = false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
savingStockTransaction = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveStockItemAsync()
|
||||
{
|
||||
errorMessage = null;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
@inject StockItemService StockItemService
|
||||
@inject NavigationManager Navigation
|
||||
@using CutList.Core.Formatting
|
||||
@using CutList.Web.Data.Entities
|
||||
|
||||
<PageTitle>Stock Items</PageTitle>
|
||||
|
||||
@@ -10,6 +11,11 @@
|
||||
<a href="stock/new" class="btn btn-primary">Add Stock Item</a>
|
||||
</div>
|
||||
|
||||
<p class="text-muted mb-4">
|
||||
Stock items represent the specific lengths of material you have available for cutting. Each stock item links
|
||||
a material to a length and tracks how many pieces you have on hand.
|
||||
</p>
|
||||
|
||||
@if (loading)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
@@ -22,30 +28,60 @@ else if (stockItems.Count == 0)
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Material</th>
|
||||
<th>Length</th>
|
||||
<th>Name</th>
|
||||
<th style="width: 120px;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var item in stockItems)
|
||||
{
|
||||
<MaterialFilter AvailableGrades="availableGrades" Value="filterState" ValueChanged="OnFilterChanged" />
|
||||
|
||||
@if (filteredItems.Count == 0)
|
||||
{
|
||||
<div class="alert alert-warning">
|
||||
No stock items match your filters.
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>@item.Material.DisplayName</td>
|
||||
<td>@ArchUnits.FormatFromInches((double)item.LengthInches)</td>
|
||||
<td>@(item.Name ?? "-")</td>
|
||||
<td>
|
||||
<a href="stock/@item.Id" class="btn btn-sm btn-outline-primary">Edit</a>
|
||||
<button class="btn btn-sm btn-outline-danger" @onclick="() => ConfirmDelete(item)">Delete</button>
|
||||
</td>
|
||||
<th>Shape</th>
|
||||
<th>Type</th>
|
||||
<th>Grade</th>
|
||||
<th>Size</th>
|
||||
<th>Length</th>
|
||||
<th>On Hand</th>
|
||||
<th style="width: 100px;">Actions</th>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var item in pagedItems)
|
||||
{
|
||||
<tr>
|
||||
<td>@item.Material.Shape.GetDisplayName()</td>
|
||||
<td>@item.Material.Type</td>
|
||||
<td>@item.Material.Grade</td>
|
||||
<td>@item.Material.Size</td>
|
||||
<td>@ArchUnits.FormatFromInches((double)item.LengthInches)</td>
|
||||
<td>
|
||||
@if (item.QuantityOnHand > 0)
|
||||
{
|
||||
<span class="badge bg-success">@item.QuantityOnHand</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="badge bg-secondary">0</span>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex gap-1">
|
||||
<a href="stock/@item.Id" class="btn btn-sm btn-outline-primary" title="Edit"><i class="bi bi-pencil"></i></a>
|
||||
<button class="btn btn-sm btn-outline-danger" @onclick="() => ConfirmDelete(item)" title="Delete"><i class="bi bi-trash"></i></button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<Pager TotalCount="filteredItems.Count" PageSize="pageSize" CurrentPage="currentPage" CurrentPageChanged="OnPageChanged" />
|
||||
}
|
||||
}
|
||||
|
||||
<ConfirmDialog @ref="deleteDialog"
|
||||
@@ -57,9 +93,45 @@ else
|
||||
@code {
|
||||
private List<StockItem> stockItems = new();
|
||||
private bool loading = true;
|
||||
private int currentPage = 1;
|
||||
private int pageSize = 25;
|
||||
private ConfirmDialog deleteDialog = null!;
|
||||
private StockItem? itemToDelete;
|
||||
private string deleteMessage = "";
|
||||
private MaterialFilterState filterState = new();
|
||||
|
||||
private List<StockItem> filteredItems => stockItems.Where(s =>
|
||||
{
|
||||
var m = s.Material;
|
||||
if (filterState.Shape.HasValue && m.Shape != filterState.Shape.Value)
|
||||
return false;
|
||||
if (filterState.Type.HasValue && m.Type != filterState.Type.Value)
|
||||
return false;
|
||||
if (!string.IsNullOrEmpty(filterState.Grade) && m.Grade != filterState.Grade)
|
||||
return false;
|
||||
if (!string.IsNullOrWhiteSpace(filterState.SearchText))
|
||||
{
|
||||
var search = filterState.SearchText.Trim();
|
||||
if (!Contains(m.Size, search)
|
||||
&& !Contains(m.Grade, search)
|
||||
&& !Contains(m.Description, search)
|
||||
&& !Contains(m.Shape.GetDisplayName(), search)
|
||||
&& !Contains(s.Name, search)
|
||||
&& !Contains(s.Notes, search))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}).ToList();
|
||||
|
||||
private IEnumerable<string> availableGrades => stockItems
|
||||
.Select(s => s.Material.Grade)
|
||||
.Where(g => !string.IsNullOrEmpty(g))
|
||||
.Distinct()
|
||||
.OrderBy(g => g)!;
|
||||
|
||||
private IEnumerable<StockItem> pagedItems => filteredItems
|
||||
.Skip((currentPage - 1) * pageSize)
|
||||
.Take(pageSize);
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
@@ -67,6 +139,12 @@ else
|
||||
loading = false;
|
||||
}
|
||||
|
||||
private void OnFilterChanged(MaterialFilterState state)
|
||||
{
|
||||
filterState = state;
|
||||
currentPage = 1;
|
||||
}
|
||||
|
||||
private void ConfirmDelete(StockItem item)
|
||||
{
|
||||
itemToDelete = item;
|
||||
@@ -80,6 +158,15 @@ else
|
||||
{
|
||||
await StockItemService.DeleteAsync(itemToDelete.Id);
|
||||
stockItems = await StockItemService.GetAllAsync();
|
||||
|
||||
var totalPages = (int)Math.Ceiling((double)filteredItems.Count / pageSize);
|
||||
if (currentPage > totalPages && totalPages > 0)
|
||||
currentPage = totalPages;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPageChanged(int page) => currentPage = page;
|
||||
|
||||
private static bool Contains(string? value, string search) =>
|
||||
value != null && value.Contains(search, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
@@ -143,8 +143,8 @@ else
|
||||
<td>@(offering.PartNumber ?? "-")</td>
|
||||
<td>@(offering.Price.HasValue ? offering.Price.Value.ToString("C") : "-")</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-primary" @onclick="() => EditOffering(offering)">Edit</button>
|
||||
<button class="btn btn-sm btn-outline-danger" @onclick="() => ConfirmDeleteOffering(offering)">Delete</button>
|
||||
<button class="btn btn-sm btn-outline-primary" @onclick="() => EditOffering(offering)" title="Edit"><i class="bi bi-pencil"></i></button>
|
||||
<button class="btn btn-sm btn-outline-danger" @onclick="() => ConfirmDeleteOffering(offering)" title="Delete"><i class="bi bi-trash"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
|
||||
@@ -27,24 +27,28 @@ else
|
||||
<th>Name</th>
|
||||
<th>Contact Info</th>
|
||||
<th>Notes</th>
|
||||
<th style="width: 120px;">Actions</th>
|
||||
<th style="width: 100px;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var supplier in suppliers)
|
||||
@foreach (var supplier in pagedSuppliers)
|
||||
{
|
||||
<tr>
|
||||
<td><a href="suppliers/@supplier.Id">@supplier.Name</a></td>
|
||||
<td>@supplier.ContactInfo</td>
|
||||
<td>@TruncateText(supplier.Notes, 50)</td>
|
||||
<td>
|
||||
<a href="suppliers/@supplier.Id" class="btn btn-sm btn-outline-primary">Edit</a>
|
||||
<button class="btn btn-sm btn-outline-danger" @onclick="() => ConfirmDelete(supplier)">Delete</button>
|
||||
<div class="d-flex gap-1">
|
||||
<a href="suppliers/@supplier.Id" class="btn btn-sm btn-outline-primary" title="Edit"><i class="bi bi-pencil"></i></a>
|
||||
<button class="btn btn-sm btn-outline-danger" @onclick="() => ConfirmDelete(supplier)" title="Delete"><i class="bi bi-trash"></i></button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<Pager TotalCount="suppliers.Count" PageSize="pageSize" CurrentPage="currentPage" CurrentPageChanged="OnPageChanged" />
|
||||
}
|
||||
|
||||
<ConfirmDialog @ref="deleteDialog"
|
||||
@@ -56,10 +60,14 @@ else
|
||||
@code {
|
||||
private List<Supplier> suppliers = new();
|
||||
private bool loading = true;
|
||||
private int currentPage = 1;
|
||||
private int pageSize = 25;
|
||||
private ConfirmDialog deleteDialog = null!;
|
||||
private Supplier? supplierToDelete;
|
||||
private string deleteMessage = "";
|
||||
|
||||
private IEnumerable<Supplier> pagedSuppliers => suppliers.Skip((currentPage - 1) * pageSize).Take(pageSize);
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
suppliers = await SupplierService.GetAllAsync();
|
||||
@@ -79,9 +87,15 @@ else
|
||||
{
|
||||
await SupplierService.DeleteAsync(supplierToDelete.Id);
|
||||
suppliers = await SupplierService.GetAllAsync();
|
||||
|
||||
var totalPages = (int)Math.Ceiling((double)suppliers.Count / pageSize);
|
||||
if (currentPage > totalPages && totalPages > 0)
|
||||
currentPage = totalPages;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPageChanged(int page) => currentPage = page;
|
||||
|
||||
private string? TruncateText(string? text, int maxLength)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text) || text.Length <= maxLength)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@page "/tools"
|
||||
@inject ProjectService ProjectService
|
||||
@inject JobService JobService
|
||||
@using CutList.Core.Formatting
|
||||
|
||||
<PageTitle>Cutting Tools</PageTitle>
|
||||
@@ -74,11 +74,11 @@ else
|
||||
<th>Name</th>
|
||||
<th>Kerf Width</th>
|
||||
<th>Default</th>
|
||||
<th style="width: 140px;">Actions</th>
|
||||
<th style="width: 100px;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var tool in tools)
|
||||
@foreach (var tool in pagedTools)
|
||||
{
|
||||
<tr>
|
||||
<td>@tool.Name</td>
|
||||
@@ -90,13 +90,15 @@ else
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-primary" @onclick="() => Edit(tool)">Edit</button>
|
||||
<button class="btn btn-sm btn-outline-danger" @onclick="() => ConfirmDelete(tool)">Delete</button>
|
||||
<button class="btn btn-sm btn-outline-primary" @onclick="() => Edit(tool)" title="Edit"><i class="bi bi-pencil"></i></button>
|
||||
<button class="btn btn-sm btn-outline-danger" @onclick="() => ConfirmDelete(tool)" title="Delete"><i class="bi bi-trash"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<Pager TotalCount="tools.Count" PageSize="pageSize" CurrentPage="currentPage" CurrentPageChanged="OnPageChanged" />
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,6 +114,10 @@ else
|
||||
private bool showForm;
|
||||
private bool saving;
|
||||
private string? errorMessage;
|
||||
private int currentPage = 1;
|
||||
private int pageSize = 25;
|
||||
|
||||
private IEnumerable<CuttingTool> pagedTools => tools.Skip((currentPage - 1) * pageSize).Take(pageSize);
|
||||
|
||||
private CuttingTool formTool = new();
|
||||
private CuttingTool? editingTool;
|
||||
@@ -122,7 +128,7 @@ else
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
tools = await ProjectService.GetCuttingToolsAsync();
|
||||
tools = await JobService.GetCuttingToolsAsync();
|
||||
loading = false;
|
||||
}
|
||||
|
||||
@@ -177,14 +183,14 @@ else
|
||||
|
||||
if (editingTool == null)
|
||||
{
|
||||
await ProjectService.CreateCuttingToolAsync(formTool);
|
||||
await JobService.CreateCuttingToolAsync(formTool);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ProjectService.UpdateCuttingToolAsync(formTool);
|
||||
await JobService.UpdateCuttingToolAsync(formTool);
|
||||
}
|
||||
|
||||
tools = await ProjectService.GetCuttingToolsAsync();
|
||||
tools = await JobService.GetCuttingToolsAsync();
|
||||
showForm = false;
|
||||
editingTool = null;
|
||||
}
|
||||
@@ -205,11 +211,17 @@ else
|
||||
{
|
||||
if (toolToDelete != null)
|
||||
{
|
||||
await ProjectService.DeleteCuttingToolAsync(toolToDelete.Id);
|
||||
tools = await ProjectService.GetCuttingToolsAsync();
|
||||
await JobService.DeleteCuttingToolAsync(toolToDelete.Id);
|
||||
tools = await JobService.GetCuttingToolsAsync();
|
||||
|
||||
var totalPages = (int)Math.Ceiling((double)tools.Count / pageSize);
|
||||
if (currentPage > totalPages && totalPages > 0)
|
||||
currentPage = totalPages;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPageChanged(int page) => currentPage = page;
|
||||
|
||||
private string FormatKerf(decimal kerf)
|
||||
{
|
||||
// Show as fraction if it's a common value
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
<h1>CUT LIST</h1>
|
||||
<div class="meta-info">
|
||||
<div class="meta-row"><span>Date:</span> @DateTime.Now.ToString("g")</div>
|
||||
<div class="meta-row"><span>Project:</span> @Project.Name</div>
|
||||
@if (!string.IsNullOrWhiteSpace(Project.Customer))
|
||||
<div class="meta-row"><span>Job:</span> @Job.Name</div>
|
||||
@if (!string.IsNullOrWhiteSpace(Job.Customer))
|
||||
{
|
||||
<div class="meta-row"><span>Customer:</span> @Project.Customer</div>
|
||||
<div class="meta-row"><span>Customer:</span> @Job.Customer</div>
|
||||
}
|
||||
@if (Project.CuttingTool != null)
|
||||
@if (Job.CuttingTool != null)
|
||||
{
|
||||
<div class="meta-row"><span>Cut Method:</span> @Project.CuttingTool.Name (kerf: @Project.CuttingTool.KerfInches")</div>
|
||||
<div class="meta-row"><span>Cut Method:</span> @Job.CuttingTool.Name (kerf: @Job.CuttingTool.KerfInches")</div>
|
||||
}
|
||||
<div class="meta-row"><span>Stock Bars:</span> @PackResult.Bins.Count</div>
|
||||
<div class="meta-row"><span>Total Pieces:</span> @TotalPieces</div>
|
||||
@@ -64,18 +64,18 @@
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@if (!string.IsNullOrEmpty(Project.Notes))
|
||||
@if (!string.IsNullOrEmpty(Job.Notes))
|
||||
{
|
||||
<div class="notes-section">
|
||||
<h3>Notes</h3>
|
||||
<p>@Project.Notes</p>
|
||||
<p>@Job.Notes</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter, EditorRequired]
|
||||
public Project Project { get; set; } = null!;
|
||||
public Job Job { get; set; } = null!;
|
||||
|
||||
[Parameter, EditorRequired]
|
||||
public PackResult PackResult { get; set; } = null!;
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
<input type="text"
|
||||
class="form-control @(HasError ? "is-invalid" : "")"
|
||||
value="@DisplayValue"
|
||||
@onchange="OnInputChange"
|
||||
@oninput="OnInputChange"
|
||||
@onblur="OnBlur"
|
||||
placeholder="@Placeholder" />
|
||||
@if (HasError)
|
||||
{
|
||||
@@ -13,24 +14,53 @@
|
||||
</div>
|
||||
|
||||
@code {
|
||||
/// <summary>
|
||||
/// Non-nullable decimal value binding.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public decimal Value { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<decimal> ValueChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nullable decimal value binding (used for optional dimension fields).
|
||||
/// Takes precedence over Value if both ValueChanged and NullableValueChanged are set.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public decimal? NullableValue { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<decimal?> NullableValueChanged { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Placeholder { get; set; } = "e.g., 12' 6\" or 144";
|
||||
|
||||
private string DisplayValue { get; set; } = string.Empty;
|
||||
private bool HasError { get; set; }
|
||||
private string ErrorMessage { get; set; } = string.Empty;
|
||||
private decimal? _lastValue;
|
||||
|
||||
private bool IsNullableMode => NullableValueChanged.HasDelegate;
|
||||
|
||||
private decimal? CurrentValue => IsNullableMode ? NullableValue : Value;
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (Value > 0 && string.IsNullOrEmpty(DisplayValue))
|
||||
// Reset display when Value changes externally (e.g., form reset)
|
||||
if (CurrentValue != _lastValue)
|
||||
{
|
||||
DisplayValue = ArchUnits.FormatFromInches((double)Value);
|
||||
_lastValue = CurrentValue;
|
||||
if (CurrentValue.HasValue && CurrentValue.Value > 0)
|
||||
{
|
||||
DisplayValue = ArchUnits.FormatFromInches((double)CurrentValue.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayValue = string.Empty;
|
||||
HasError = false;
|
||||
ErrorMessage = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +73,15 @@
|
||||
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
await ValueChanged.InvokeAsync(0);
|
||||
_lastValue = null;
|
||||
if (IsNullableMode)
|
||||
{
|
||||
await NullableValueChanged.InvokeAsync(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ValueChanged.InvokeAsync(0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -51,14 +89,32 @@
|
||||
{
|
||||
// Try to parse as architectural units
|
||||
var inches = ArchUnits.ParseToInches(input);
|
||||
await ValueChanged.InvokeAsync((decimal)inches);
|
||||
_lastValue = (decimal)inches;
|
||||
|
||||
if (IsNullableMode)
|
||||
{
|
||||
await NullableValueChanged.InvokeAsync(_lastValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ValueChanged.InvokeAsync(_lastValue.Value);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Try to parse as plain decimal (inches)
|
||||
if (decimal.TryParse(input, out var decimalValue))
|
||||
{
|
||||
await ValueChanged.InvokeAsync(decimalValue);
|
||||
_lastValue = decimalValue;
|
||||
|
||||
if (IsNullableMode)
|
||||
{
|
||||
await NullableValueChanged.InvokeAsync(decimalValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ValueChanged.InvokeAsync(decimalValue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -68,6 +124,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBlur()
|
||||
{
|
||||
// Format the display value nicely on blur if we have a valid value
|
||||
if (!HasError && _lastValue.HasValue && _lastValue.Value > 0)
|
||||
{
|
||||
DisplayValue = ArchUnits.FormatFromInches((double)_lastValue.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public static string FormatLength(decimal inches)
|
||||
{
|
||||
return ArchUnits.FormatFromInches((double)inches);
|
||||
|
||||
79
CutList.Web/Components/Shared/MaterialFilter.razor
Normal file
79
CutList.Web/Components/Shared/MaterialFilter.razor
Normal file
@@ -0,0 +1,79 @@
|
||||
@using CutList.Web.Data.Entities
|
||||
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col-auto">
|
||||
<select class="form-select form-select-sm" value="@Value.Shape" @onchange="OnShapeChanged">
|
||||
<option value="">All Shapes</option>
|
||||
@foreach (var shape in Enum.GetValues<MaterialShape>())
|
||||
{
|
||||
<option value="@shape">@shape.GetDisplayName()</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<select class="form-select form-select-sm" value="@Value.Type" @onchange="OnTypeChanged">
|
||||
<option value="">All Types</option>
|
||||
@foreach (var type in Enum.GetValues<MaterialType>())
|
||||
{
|
||||
<option value="@type">@type</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<select class="form-select form-select-sm" value="@(Value.Grade ?? "")" @onchange="OnGradeChanged">
|
||||
<option value="">All Grades</option>
|
||||
@foreach (var grade in AvailableGrades)
|
||||
{
|
||||
<option value="@grade">@grade</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<input type="text" class="form-control form-control-sm" placeholder="Search..." value="@Value.SearchText" @oninput="OnSearchInput" />
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button class="btn btn-sm btn-outline-secondary" @onclick="OnClear" title="Clear filters">
|
||||
<i class="bi bi-x-lg"></i> Clear
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public IEnumerable<string> AvailableGrades { get; set; } = Enumerable.Empty<string>();
|
||||
[Parameter] public MaterialFilterState Value { get; set; } = new();
|
||||
[Parameter] public EventCallback<MaterialFilterState> ValueChanged { get; set; }
|
||||
|
||||
private async Task OnShapeChanged(ChangeEventArgs e)
|
||||
{
|
||||
Value.Shape = Enum.TryParse<MaterialShape>(e.Value?.ToString(), out var shape) ? shape : null;
|
||||
await ValueChanged.InvokeAsync(Value);
|
||||
}
|
||||
|
||||
private async Task OnTypeChanged(ChangeEventArgs e)
|
||||
{
|
||||
Value.Type = Enum.TryParse<MaterialType>(e.Value?.ToString(), out var type) ? type : null;
|
||||
await ValueChanged.InvokeAsync(Value);
|
||||
}
|
||||
|
||||
private async Task OnGradeChanged(ChangeEventArgs e)
|
||||
{
|
||||
var val = e.Value?.ToString();
|
||||
Value.Grade = string.IsNullOrEmpty(val) ? null : val;
|
||||
await ValueChanged.InvokeAsync(Value);
|
||||
}
|
||||
|
||||
private async Task OnSearchInput(ChangeEventArgs e)
|
||||
{
|
||||
Value.SearchText = e.Value?.ToString();
|
||||
await ValueChanged.InvokeAsync(Value);
|
||||
}
|
||||
|
||||
private async Task OnClear()
|
||||
{
|
||||
Value.Shape = null;
|
||||
Value.Type = null;
|
||||
Value.Grade = null;
|
||||
Value.SearchText = null;
|
||||
await ValueChanged.InvokeAsync(Value);
|
||||
}
|
||||
}
|
||||
11
CutList.Web/Components/Shared/MaterialFilterState.cs
Normal file
11
CutList.Web/Components/Shared/MaterialFilterState.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using CutList.Web.Data.Entities;
|
||||
|
||||
namespace CutList.Web.Components.Shared;
|
||||
|
||||
public class MaterialFilterState
|
||||
{
|
||||
public MaterialShape? Shape { get; set; }
|
||||
public MaterialType? Type { get; set; }
|
||||
public string? Grade { get; set; }
|
||||
public string? SearchText { get; set; }
|
||||
}
|
||||
109
CutList.Web/Components/Shared/Pager.razor
Normal file
109
CutList.Web/Components/Shared/Pager.razor
Normal file
@@ -0,0 +1,109 @@
|
||||
@if (TotalCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@{
|
||||
var totalPages = (int)Math.Ceiling((double)TotalCount / PageSize);
|
||||
var start = (CurrentPage - 1) * PageSize + 1;
|
||||
var end = Math.Min(CurrentPage * PageSize, TotalCount);
|
||||
}
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mt-3">
|
||||
<small class="text-muted">Showing @start–@end of @TotalCount</small>
|
||||
|
||||
@if (totalPages > 1)
|
||||
{
|
||||
<nav>
|
||||
<ul class="pagination pagination-sm mb-0">
|
||||
<li class="page-item @(CurrentPage == 1 ? "disabled" : "")">
|
||||
<button class="page-link" @onclick="() => SetPage(CurrentPage - 1)" disabled="@(CurrentPage == 1)">Previous</button>
|
||||
</li>
|
||||
|
||||
@foreach (var page in GetPageWindow(totalPages))
|
||||
{
|
||||
@if (page == -1)
|
||||
{
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link">…</span>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="page-item @(page == CurrentPage ? "active" : "")">
|
||||
<button class="page-link" @onclick="() => SetPage(page)">@(page)</button>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
|
||||
<li class="page-item @(CurrentPage == totalPages ? "disabled" : "")">
|
||||
<button class="page-link" @onclick="() => SetPage(CurrentPage + 1)" disabled="@(CurrentPage == totalPages)">Next</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter, EditorRequired]
|
||||
public int TotalCount { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public int PageSize { get; set; } = 25;
|
||||
|
||||
[Parameter]
|
||||
public int CurrentPage { get; set; } = 1;
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<int> CurrentPageChanged { get; set; }
|
||||
|
||||
private async Task SetPage(int page)
|
||||
{
|
||||
if (page < 1 || page > (int)Math.Ceiling((double)TotalCount / PageSize))
|
||||
return;
|
||||
if (page == CurrentPage)
|
||||
return;
|
||||
|
||||
CurrentPage = page;
|
||||
await CurrentPageChanged.InvokeAsync(page);
|
||||
}
|
||||
|
||||
private IEnumerable<int> GetPageWindow(int totalPages)
|
||||
{
|
||||
const int maxVisible = 7;
|
||||
|
||||
if (totalPages <= maxVisible)
|
||||
{
|
||||
for (var i = 1; i <= totalPages; i++)
|
||||
yield return i;
|
||||
yield break;
|
||||
}
|
||||
|
||||
// Always show first page
|
||||
yield return 1;
|
||||
|
||||
var windowStart = Math.Max(2, CurrentPage - 2);
|
||||
var windowEnd = Math.Min(totalPages - 1, CurrentPage + 2);
|
||||
|
||||
// Adjust window to show 5 middle pages when possible
|
||||
if (windowEnd - windowStart < 4)
|
||||
{
|
||||
if (windowStart == 2)
|
||||
windowEnd = Math.Min(totalPages - 1, windowStart + 4);
|
||||
else
|
||||
windowStart = Math.Max(2, windowEnd - 4);
|
||||
}
|
||||
|
||||
if (windowStart > 2)
|
||||
yield return -1; // ellipsis
|
||||
|
||||
for (var i = windowStart; i <= windowEnd; i++)
|
||||
yield return i;
|
||||
|
||||
if (windowEnd < totalPages - 1)
|
||||
yield return -1; // ellipsis
|
||||
|
||||
// Always show last page
|
||||
yield return totalPages;
|
||||
}
|
||||
}
|
||||
41
CutList.Web/Controllers/CatalogController.cs
Normal file
41
CutList.Web/Controllers/CatalogController.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using CutList.Web.DTOs;
|
||||
using CutList.Web.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace CutList.Web.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class CatalogController : ControllerBase
|
||||
{
|
||||
private readonly CatalogService _catalogService;
|
||||
|
||||
public CatalogController(CatalogService catalogService)
|
||||
{
|
||||
_catalogService = catalogService;
|
||||
}
|
||||
|
||||
[HttpGet("export")]
|
||||
public async Task<IActionResult> Export()
|
||||
{
|
||||
var data = await _catalogService.ExportAsync();
|
||||
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
};
|
||||
|
||||
return new JsonResult(data, options);
|
||||
}
|
||||
|
||||
[HttpPost("import")]
|
||||
public async Task<ActionResult<ImportResultDto>> Import([FromBody] CatalogData data)
|
||||
{
|
||||
var result = await _catalogService.ImportAsync(data);
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
97
CutList.Web/Controllers/CuttingToolsController.cs
Normal file
97
CutList.Web/Controllers/CuttingToolsController.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using CutList.Web.Data.Entities;
|
||||
using CutList.Web.DTOs;
|
||||
using CutList.Web.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace CutList.Web.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/cutting-tools")]
|
||||
public class CuttingToolsController : ControllerBase
|
||||
{
|
||||
private readonly JobService _jobService;
|
||||
|
||||
public CuttingToolsController(JobService jobService)
|
||||
{
|
||||
_jobService = jobService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<List<CuttingToolDto>>> GetAll([FromQuery] bool includeInactive = false)
|
||||
{
|
||||
var tools = await _jobService.GetCuttingToolsAsync(includeInactive);
|
||||
return Ok(tools.Select(MapToDto).ToList());
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<ActionResult<CuttingToolDto>> GetById(int id)
|
||||
{
|
||||
var tool = await _jobService.GetCuttingToolByIdAsync(id);
|
||||
if (tool == null)
|
||||
return NotFound();
|
||||
|
||||
return Ok(MapToDto(tool));
|
||||
}
|
||||
|
||||
[HttpGet("default")]
|
||||
public async Task<ActionResult<CuttingToolDto>> GetDefault()
|
||||
{
|
||||
var tool = await _jobService.GetDefaultCuttingToolAsync();
|
||||
if (tool == null)
|
||||
return NotFound();
|
||||
|
||||
return Ok(MapToDto(tool));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<CuttingToolDto>> Create(CreateCuttingToolDto dto)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(dto.Name))
|
||||
return BadRequest("Name is required");
|
||||
|
||||
var tool = new CuttingTool
|
||||
{
|
||||
Name = dto.Name,
|
||||
KerfInches = dto.KerfInches,
|
||||
IsDefault = dto.IsDefault
|
||||
};
|
||||
|
||||
await _jobService.CreateCuttingToolAsync(tool);
|
||||
return CreatedAtAction(nameof(GetById), new { id = tool.Id }, MapToDto(tool));
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<ActionResult<CuttingToolDto>> Update(int id, UpdateCuttingToolDto dto)
|
||||
{
|
||||
var tool = await _jobService.GetCuttingToolByIdAsync(id);
|
||||
if (tool == null)
|
||||
return NotFound();
|
||||
|
||||
if (dto.Name != null) tool.Name = dto.Name;
|
||||
if (dto.KerfInches.HasValue) tool.KerfInches = dto.KerfInches.Value;
|
||||
if (dto.IsDefault.HasValue) tool.IsDefault = dto.IsDefault.Value;
|
||||
|
||||
await _jobService.UpdateCuttingToolAsync(tool);
|
||||
return Ok(MapToDto(tool));
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
var tool = await _jobService.GetCuttingToolByIdAsync(id);
|
||||
if (tool == null)
|
||||
return NotFound();
|
||||
|
||||
await _jobService.DeleteCuttingToolAsync(id);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
private static CuttingToolDto MapToDto(CuttingTool tool) => new()
|
||||
{
|
||||
Id = tool.Id,
|
||||
Name = tool.Name,
|
||||
KerfInches = tool.KerfInches,
|
||||
IsDefault = tool.IsDefault,
|
||||
IsActive = tool.IsActive
|
||||
};
|
||||
}
|
||||
492
CutList.Web/Controllers/JobsController.cs
Normal file
492
CutList.Web/Controllers/JobsController.cs
Normal file
@@ -0,0 +1,492 @@
|
||||
using CutList.Core.Formatting;
|
||||
using CutList.Web.Data.Entities;
|
||||
using CutList.Web.DTOs;
|
||||
using CutList.Web.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace CutList.Web.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class JobsController : ControllerBase
|
||||
{
|
||||
private readonly JobService _jobService;
|
||||
private readonly CutListPackingService _packingService;
|
||||
|
||||
public JobsController(JobService jobService, CutListPackingService packingService)
|
||||
{
|
||||
_jobService = jobService;
|
||||
_packingService = packingService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<List<JobDto>>> GetAll()
|
||||
{
|
||||
var jobs = await _jobService.GetAllAsync();
|
||||
return Ok(jobs.Select(MapToDto).ToList());
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<ActionResult<JobDetailDto>> GetById(int id)
|
||||
{
|
||||
var job = await _jobService.GetByIdAsync(id);
|
||||
if (job == null)
|
||||
return NotFound();
|
||||
|
||||
return Ok(MapToDetailDto(job));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<JobDetailDto>> Create(CreateJobDto dto)
|
||||
{
|
||||
var job = new Job
|
||||
{
|
||||
Name = dto.Name,
|
||||
Customer = dto.Customer,
|
||||
CuttingToolId = dto.CuttingToolId,
|
||||
Notes = dto.Notes
|
||||
};
|
||||
|
||||
await _jobService.CreateAsync(job);
|
||||
var created = await _jobService.GetByIdAsync(job.Id);
|
||||
return CreatedAtAction(nameof(GetById), new { id = job.Id }, MapToDetailDto(created!));
|
||||
}
|
||||
|
||||
[HttpPost("quick-create")]
|
||||
public async Task<ActionResult<JobDetailDto>> QuickCreate(QuickCreateJobDto dto)
|
||||
{
|
||||
var job = await _jobService.QuickCreateAsync(dto.Customer);
|
||||
var created = await _jobService.GetByIdAsync(job.Id);
|
||||
return CreatedAtAction(nameof(GetById), new { id = job.Id }, MapToDetailDto(created!));
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<ActionResult<JobDetailDto>> Update(int id, UpdateJobDto dto)
|
||||
{
|
||||
var job = await _jobService.GetByIdAsync(id);
|
||||
if (job == null)
|
||||
return NotFound();
|
||||
|
||||
if (dto.Name != null) job.Name = dto.Name;
|
||||
if (dto.Customer != null) job.Customer = dto.Customer;
|
||||
if (dto.CuttingToolId.HasValue) job.CuttingToolId = dto.CuttingToolId;
|
||||
if (dto.Notes != null) job.Notes = dto.Notes;
|
||||
|
||||
await _jobService.UpdateAsync(job);
|
||||
|
||||
var updated = await _jobService.GetByIdAsync(id);
|
||||
return Ok(MapToDetailDto(updated!));
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
var job = await _jobService.GetByIdAsync(id);
|
||||
if (job == null)
|
||||
return NotFound();
|
||||
|
||||
await _jobService.DeleteAsync(id);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpPost("{id}/duplicate")]
|
||||
public async Task<ActionResult<JobDetailDto>> Duplicate(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var duplicate = await _jobService.DuplicateAsync(id);
|
||||
var loaded = await _jobService.GetByIdAsync(duplicate.Id);
|
||||
return CreatedAtAction(nameof(GetById), new { id = duplicate.Id }, MapToDetailDto(loaded!));
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
// --- Parts ---
|
||||
|
||||
[HttpGet("{id}/parts")]
|
||||
public async Task<ActionResult<List<JobPartDto>>> GetParts(int id)
|
||||
{
|
||||
var job = await _jobService.GetByIdAsync(id);
|
||||
if (job == null)
|
||||
return NotFound();
|
||||
|
||||
return Ok(job.Parts.Select(MapPartToDto).ToList());
|
||||
}
|
||||
|
||||
[HttpPost("{id}/parts")]
|
||||
public async Task<ActionResult<JobPartDto>> AddPart(int id, CreateJobPartDto dto)
|
||||
{
|
||||
var job = await _jobService.GetByIdAsync(id);
|
||||
if (job == null)
|
||||
return NotFound();
|
||||
|
||||
decimal lengthInches;
|
||||
try
|
||||
{
|
||||
lengthInches = (decimal)ArchUnits.ParseToInches(dto.Length);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return BadRequest($"Invalid length format: {dto.Length}");
|
||||
}
|
||||
|
||||
var part = new JobPart
|
||||
{
|
||||
JobId = id,
|
||||
MaterialId = dto.MaterialId,
|
||||
Name = dto.Name,
|
||||
LengthInches = lengthInches,
|
||||
Quantity = dto.Quantity
|
||||
};
|
||||
|
||||
await _jobService.AddPartAsync(part);
|
||||
|
||||
// Reload to get material name
|
||||
var reloadedJob = await _jobService.GetByIdAsync(id);
|
||||
var addedPart = reloadedJob!.Parts.FirstOrDefault(p => p.Id == part.Id);
|
||||
return CreatedAtAction(nameof(GetParts), new { id }, MapPartToDto(addedPart ?? part));
|
||||
}
|
||||
|
||||
[HttpPut("{id}/parts/{partId}")]
|
||||
public async Task<ActionResult<JobPartDto>> UpdatePart(int id, int partId, UpdateJobPartDto dto)
|
||||
{
|
||||
var job = await _jobService.GetByIdAsync(id);
|
||||
if (job == null)
|
||||
return NotFound();
|
||||
|
||||
var part = job.Parts.FirstOrDefault(p => p.Id == partId);
|
||||
if (part == null)
|
||||
return NotFound();
|
||||
|
||||
if (dto.MaterialId.HasValue) part.MaterialId = dto.MaterialId.Value;
|
||||
if (dto.Name != null) part.Name = dto.Name;
|
||||
if (dto.Length != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
part.LengthInches = (decimal)ArchUnits.ParseToInches(dto.Length);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return BadRequest($"Invalid length format: {dto.Length}");
|
||||
}
|
||||
}
|
||||
if (dto.Quantity.HasValue) part.Quantity = dto.Quantity.Value;
|
||||
|
||||
await _jobService.UpdatePartAsync(part);
|
||||
|
||||
var reloadedJob = await _jobService.GetByIdAsync(id);
|
||||
var updatedPart = reloadedJob!.Parts.FirstOrDefault(p => p.Id == partId);
|
||||
return Ok(MapPartToDto(updatedPart ?? part));
|
||||
}
|
||||
|
||||
[HttpDelete("{id}/parts/{partId}")]
|
||||
public async Task<IActionResult> DeletePart(int id, int partId)
|
||||
{
|
||||
var job = await _jobService.GetByIdAsync(id);
|
||||
if (job == null)
|
||||
return NotFound();
|
||||
|
||||
var part = job.Parts.FirstOrDefault(p => p.Id == partId);
|
||||
if (part == null)
|
||||
return NotFound();
|
||||
|
||||
await _jobService.DeletePartAsync(partId);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
// --- Stock ---
|
||||
|
||||
[HttpGet("{id}/stock")]
|
||||
public async Task<ActionResult<List<JobStockDto>>> GetStock(int id)
|
||||
{
|
||||
var job = await _jobService.GetByIdAsync(id);
|
||||
if (job == null)
|
||||
return NotFound();
|
||||
|
||||
return Ok(job.Stock.Select(MapStockToDto).ToList());
|
||||
}
|
||||
|
||||
[HttpPost("{id}/stock")]
|
||||
public async Task<ActionResult<JobStockDto>> AddStock(int id, CreateJobStockDto dto)
|
||||
{
|
||||
var job = await _jobService.GetByIdAsync(id);
|
||||
if (job == null)
|
||||
return NotFound();
|
||||
|
||||
decimal lengthInches;
|
||||
try
|
||||
{
|
||||
lengthInches = (decimal)ArchUnits.ParseToInches(dto.Length);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return BadRequest($"Invalid length format: {dto.Length}");
|
||||
}
|
||||
|
||||
var stock = new JobStock
|
||||
{
|
||||
JobId = id,
|
||||
MaterialId = dto.MaterialId,
|
||||
StockItemId = dto.StockItemId,
|
||||
LengthInches = lengthInches,
|
||||
Quantity = dto.Quantity,
|
||||
IsCustomLength = dto.IsCustomLength,
|
||||
Priority = dto.Priority
|
||||
};
|
||||
|
||||
await _jobService.AddStockAsync(stock);
|
||||
|
||||
var reloadedJob = await _jobService.GetByIdAsync(id);
|
||||
var addedStock = reloadedJob!.Stock.FirstOrDefault(s => s.Id == stock.Id);
|
||||
return CreatedAtAction(nameof(GetStock), new { id }, MapStockToDto(addedStock ?? stock));
|
||||
}
|
||||
|
||||
[HttpPut("{id}/stock/{stockId}")]
|
||||
public async Task<ActionResult<JobStockDto>> UpdateStock(int id, int stockId, UpdateJobStockDto dto)
|
||||
{
|
||||
var job = await _jobService.GetByIdAsync(id);
|
||||
if (job == null)
|
||||
return NotFound();
|
||||
|
||||
var stock = job.Stock.FirstOrDefault(s => s.Id == stockId);
|
||||
if (stock == null)
|
||||
return NotFound();
|
||||
|
||||
if (dto.StockItemId.HasValue) stock.StockItemId = dto.StockItemId;
|
||||
if (dto.Length != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
stock.LengthInches = (decimal)ArchUnits.ParseToInches(dto.Length);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return BadRequest($"Invalid length format: {dto.Length}");
|
||||
}
|
||||
}
|
||||
if (dto.Quantity.HasValue) stock.Quantity = dto.Quantity.Value;
|
||||
if (dto.IsCustomLength.HasValue) stock.IsCustomLength = dto.IsCustomLength.Value;
|
||||
if (dto.Priority.HasValue) stock.Priority = dto.Priority.Value;
|
||||
|
||||
await _jobService.UpdateStockAsync(stock);
|
||||
|
||||
var reloadedJob = await _jobService.GetByIdAsync(id);
|
||||
var updatedStock = reloadedJob!.Stock.FirstOrDefault(s => s.Id == stockId);
|
||||
return Ok(MapStockToDto(updatedStock ?? stock));
|
||||
}
|
||||
|
||||
[HttpDelete("{id}/stock/{stockId}")]
|
||||
public async Task<IActionResult> DeleteStock(int id, int stockId)
|
||||
{
|
||||
var job = await _jobService.GetByIdAsync(id);
|
||||
if (job == null)
|
||||
return NotFound();
|
||||
|
||||
var stock = job.Stock.FirstOrDefault(s => s.Id == stockId);
|
||||
if (stock == null)
|
||||
return NotFound();
|
||||
|
||||
await _jobService.DeleteStockAsync(stockId);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpGet("{id}/available-stock/{materialId}")]
|
||||
public async Task<ActionResult<List<StockItemDto>>> GetAvailableStock(int id, int materialId)
|
||||
{
|
||||
var job = await _jobService.GetByIdAsync(id);
|
||||
if (job == null)
|
||||
return NotFound();
|
||||
|
||||
var items = await _jobService.GetAvailableStockForMaterialAsync(materialId);
|
||||
return Ok(items.Select(s => new StockItemDto
|
||||
{
|
||||
Id = s.Id,
|
||||
MaterialId = s.MaterialId,
|
||||
MaterialName = s.Material?.DisplayName ?? string.Empty,
|
||||
LengthInches = s.LengthInches,
|
||||
LengthFormatted = ArchUnits.FormatFromInches((double)s.LengthInches),
|
||||
Name = s.Name,
|
||||
QuantityOnHand = s.QuantityOnHand,
|
||||
IsActive = s.IsActive
|
||||
}).ToList());
|
||||
}
|
||||
|
||||
// --- Packing ---
|
||||
|
||||
[HttpPost("{id}/pack")]
|
||||
public async Task<ActionResult<PackResponseDto>> Pack(int id, PackJobRequestDto? dto = null)
|
||||
{
|
||||
var job = await _jobService.GetByIdAsync(id);
|
||||
if (job == null)
|
||||
return NotFound();
|
||||
|
||||
if (job.Parts.Count == 0)
|
||||
return BadRequest("Job has no parts to pack");
|
||||
|
||||
// Determine kerf
|
||||
decimal kerf = dto?.KerfOverride
|
||||
?? job.CuttingTool?.KerfInches
|
||||
?? (await _jobService.GetDefaultCuttingToolAsync())?.KerfInches
|
||||
?? 0.125m;
|
||||
|
||||
var result = await _packingService.PackAsync(job.Parts, kerf, job.Stock.Any() ? job.Stock : null);
|
||||
var summary = _packingService.GetSummary(result);
|
||||
|
||||
return Ok(MapPackResult(result, summary));
|
||||
}
|
||||
|
||||
// --- Mapping helpers ---
|
||||
|
||||
private static JobDto MapToDto(Job j) => new()
|
||||
{
|
||||
Id = j.Id,
|
||||
JobNumber = j.JobNumber,
|
||||
Name = j.Name,
|
||||
Customer = j.Customer,
|
||||
CuttingToolId = j.CuttingToolId,
|
||||
CuttingToolName = j.CuttingTool?.Name,
|
||||
Notes = j.Notes,
|
||||
CreatedAt = j.CreatedAt,
|
||||
UpdatedAt = j.UpdatedAt,
|
||||
PartCount = j.Parts?.Count ?? 0,
|
||||
StockCount = j.Stock?.Count ?? 0
|
||||
};
|
||||
|
||||
private static JobDetailDto MapToDetailDto(Job j) => new()
|
||||
{
|
||||
Id = j.Id,
|
||||
JobNumber = j.JobNumber,
|
||||
Name = j.Name,
|
||||
Customer = j.Customer,
|
||||
CuttingToolId = j.CuttingToolId,
|
||||
CuttingToolName = j.CuttingTool?.Name,
|
||||
Notes = j.Notes,
|
||||
CreatedAt = j.CreatedAt,
|
||||
UpdatedAt = j.UpdatedAt,
|
||||
PartCount = j.Parts?.Count ?? 0,
|
||||
StockCount = j.Stock?.Count ?? 0,
|
||||
Parts = j.Parts?.Select(MapPartToDto).ToList() ?? new(),
|
||||
Stock = j.Stock?.Select(MapStockToDto).ToList() ?? new()
|
||||
};
|
||||
|
||||
private static JobPartDto MapPartToDto(JobPart p) => new()
|
||||
{
|
||||
Id = p.Id,
|
||||
JobId = p.JobId,
|
||||
MaterialId = p.MaterialId,
|
||||
MaterialName = p.Material?.DisplayName ?? string.Empty,
|
||||
Name = p.Name,
|
||||
LengthInches = p.LengthInches,
|
||||
LengthFormatted = ArchUnits.FormatFromInches((double)p.LengthInches),
|
||||
Quantity = p.Quantity,
|
||||
SortOrder = p.SortOrder
|
||||
};
|
||||
|
||||
private static JobStockDto MapStockToDto(JobStock s) => new()
|
||||
{
|
||||
Id = s.Id,
|
||||
JobId = s.JobId,
|
||||
MaterialId = s.MaterialId,
|
||||
MaterialName = s.Material?.DisplayName ?? string.Empty,
|
||||
StockItemId = s.StockItemId,
|
||||
LengthInches = s.LengthInches,
|
||||
LengthFormatted = ArchUnits.FormatFromInches((double)s.LengthInches),
|
||||
Quantity = s.Quantity,
|
||||
IsCustomLength = s.IsCustomLength,
|
||||
Priority = s.Priority,
|
||||
SortOrder = s.SortOrder
|
||||
};
|
||||
|
||||
private static PackResponseDto MapPackResult(MultiMaterialPackResult result, MultiMaterialPackingSummary summary)
|
||||
{
|
||||
var response = new PackResponseDto();
|
||||
|
||||
foreach (var mr in result.MaterialResults)
|
||||
{
|
||||
var matResult = new MaterialPackResultDto
|
||||
{
|
||||
MaterialId = mr.Material.Id,
|
||||
MaterialName = mr.Material.DisplayName,
|
||||
InStockBins = mr.InStockBins.Select(MapBinToDto).ToList(),
|
||||
ToBePurchasedBins = mr.ToBePurchasedBins.Select(MapBinToDto).ToList(),
|
||||
ItemsNotPlaced = mr.PackResult.ItemsNotUsed.Select(i => new PackedItemDto
|
||||
{
|
||||
Name = i.Name,
|
||||
LengthInches = i.Length,
|
||||
LengthFormatted = ArchUnits.FormatFromInches(i.Length)
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
var ms = summary.MaterialSummaries.FirstOrDefault(s => s.Material.Id == mr.Material.Id);
|
||||
if (ms != null)
|
||||
{
|
||||
matResult.Summary = new MaterialPackingSummaryDto
|
||||
{
|
||||
MaterialId = ms.Material.Id,
|
||||
MaterialName = ms.Material.DisplayName,
|
||||
InStockBins = ms.InStockBins,
|
||||
ToBePurchasedBins = ms.ToBePurchasedBins,
|
||||
TotalPieces = ms.TotalPieces,
|
||||
TotalMaterialInches = ms.TotalMaterial,
|
||||
TotalUsedInches = ms.TotalUsed,
|
||||
TotalWasteInches = ms.TotalWaste,
|
||||
Efficiency = ms.Efficiency,
|
||||
ItemsNotPlaced = ms.ItemsNotPlaced
|
||||
};
|
||||
}
|
||||
|
||||
response.Materials.Add(matResult);
|
||||
}
|
||||
|
||||
response.Summary = new PackingSummaryDto
|
||||
{
|
||||
TotalInStockBins = summary.TotalInStockBins,
|
||||
TotalToBePurchasedBins = summary.TotalToBePurchasedBins,
|
||||
TotalPieces = summary.TotalPieces,
|
||||
TotalMaterialInches = summary.TotalMaterial,
|
||||
TotalMaterialFormatted = ArchUnits.FormatFromInches(summary.TotalMaterial),
|
||||
TotalUsedInches = summary.TotalUsed,
|
||||
TotalUsedFormatted = ArchUnits.FormatFromInches(summary.TotalUsed),
|
||||
TotalWasteInches = summary.TotalWaste,
|
||||
TotalWasteFormatted = ArchUnits.FormatFromInches(summary.TotalWaste),
|
||||
Efficiency = summary.Efficiency,
|
||||
TotalItemsNotPlaced = summary.TotalItemsNotPlaced,
|
||||
MaterialSummaries = summary.MaterialSummaries.Select(ms => new MaterialPackingSummaryDto
|
||||
{
|
||||
MaterialId = ms.Material.Id,
|
||||
MaterialName = ms.Material.DisplayName,
|
||||
InStockBins = ms.InStockBins,
|
||||
ToBePurchasedBins = ms.ToBePurchasedBins,
|
||||
TotalPieces = ms.TotalPieces,
|
||||
TotalMaterialInches = ms.TotalMaterial,
|
||||
TotalUsedInches = ms.TotalUsed,
|
||||
TotalWasteInches = ms.TotalWaste,
|
||||
Efficiency = ms.Efficiency,
|
||||
ItemsNotPlaced = ms.ItemsNotPlaced
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private static PackedBinDto MapBinToDto(CutList.Core.Bin bin) => new()
|
||||
{
|
||||
LengthInches = bin.Length,
|
||||
LengthFormatted = ArchUnits.FormatFromInches(bin.Length),
|
||||
UsedInches = bin.UsedLength,
|
||||
UsedFormatted = ArchUnits.FormatFromInches(bin.UsedLength),
|
||||
WasteInches = bin.RemainingLength,
|
||||
WasteFormatted = ArchUnits.FormatFromInches(bin.RemainingLength),
|
||||
Efficiency = bin.Length > 0 ? bin.UsedLength / bin.Length * 100 : 0,
|
||||
Items = bin.Items.Select(i => new PackedItemDto
|
||||
{
|
||||
Name = i.Name,
|
||||
LengthInches = i.Length,
|
||||
LengthFormatted = ArchUnits.FormatFromInches(i.Length)
|
||||
}).ToList()
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using CutList.Web.Data;
|
||||
using CutList.Web.Data.Entities;
|
||||
using CutList.Web.DTOs;
|
||||
using CutList.Web.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CutList.Web.Controllers;
|
||||
|
||||
@@ -9,47 +9,55 @@ namespace CutList.Web.Controllers;
|
||||
[Route("api/[controller]")]
|
||||
public class MaterialsController : ControllerBase
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly MaterialService _materialService;
|
||||
|
||||
public MaterialsController(ApplicationDbContext context)
|
||||
public MaterialsController(MaterialService materialService)
|
||||
{
|
||||
_context = context;
|
||||
_materialService = materialService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<IEnumerable<MaterialDto>>> GetMaterials()
|
||||
public async Task<ActionResult<List<MaterialDto>>> GetMaterials(
|
||||
[FromQuery] bool includeInactive = false,
|
||||
[FromQuery] string? shape = null)
|
||||
{
|
||||
var materials = await _context.Materials
|
||||
.Where(m => m.IsActive)
|
||||
.OrderBy(m => m.Shape)
|
||||
.ThenBy(m => m.Size)
|
||||
.Select(m => new MaterialDto
|
||||
{
|
||||
Id = m.Id,
|
||||
Shape = m.Shape,
|
||||
Size = m.Size,
|
||||
Description = m.Description
|
||||
})
|
||||
.ToListAsync();
|
||||
List<Material> materials;
|
||||
|
||||
return Ok(materials);
|
||||
if (!string.IsNullOrWhiteSpace(shape))
|
||||
{
|
||||
var parsedShape = MaterialShapeExtensions.ParseShape(shape);
|
||||
if (!parsedShape.HasValue)
|
||||
return BadRequest($"Unknown shape: {shape}");
|
||||
|
||||
materials = await _materialService.GetByShapeAsync(parsedShape.Value, includeInactive);
|
||||
}
|
||||
else
|
||||
{
|
||||
materials = await _materialService.GetAllAsync(includeInactive);
|
||||
}
|
||||
|
||||
return Ok(materials.Select(MapToDto).ToList());
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<ActionResult<MaterialDto>> GetMaterial(int id)
|
||||
{
|
||||
var material = await _context.Materials.FindAsync(id);
|
||||
|
||||
if (material == null || !material.IsActive)
|
||||
var material = await _materialService.GetByIdAsync(id);
|
||||
if (material == null)
|
||||
return NotFound();
|
||||
|
||||
return Ok(new MaterialDto
|
||||
{
|
||||
Id = material.Id,
|
||||
Shape = material.Shape,
|
||||
Size = material.Size,
|
||||
Description = material.Description
|
||||
});
|
||||
return Ok(MapToDto(material));
|
||||
}
|
||||
|
||||
[HttpGet("by-shape/{shape}")]
|
||||
public async Task<ActionResult<List<MaterialDto>>> GetByShape(string shape, [FromQuery] bool includeInactive = false)
|
||||
{
|
||||
var parsedShape = MaterialShapeExtensions.ParseShape(shape);
|
||||
if (!parsedShape.HasValue)
|
||||
return BadRequest($"Unknown shape: {shape}");
|
||||
|
||||
var materials = await _materialService.GetByShapeAsync(parsedShape.Value, includeInactive);
|
||||
return Ok(materials.Select(MapToDto).ToList());
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
@@ -58,34 +66,53 @@ public class MaterialsController : ControllerBase
|
||||
if (string.IsNullOrWhiteSpace(dto.Shape))
|
||||
return BadRequest("Shape is required");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(dto.Size))
|
||||
return BadRequest("Size is required");
|
||||
var parsedShape = MaterialShapeExtensions.ParseShape(dto.Shape);
|
||||
if (!parsedShape.HasValue)
|
||||
return BadRequest($"Unknown shape: {dto.Shape}");
|
||||
|
||||
// Check for duplicates
|
||||
var exists = await _context.Materials
|
||||
.AnyAsync(m => m.Shape == dto.Shape && m.Size == dto.Size && m.IsActive);
|
||||
|
||||
if (exists)
|
||||
return Conflict($"Material '{dto.Shape} - {dto.Size}' already exists");
|
||||
// Parse material type
|
||||
MaterialType materialType = MaterialType.Steel;
|
||||
if (!string.IsNullOrWhiteSpace(dto.Type))
|
||||
{
|
||||
if (!Enum.TryParse<MaterialType>(dto.Type, true, out materialType))
|
||||
return BadRequest($"Unknown material type: {dto.Type}");
|
||||
}
|
||||
|
||||
var material = new Material
|
||||
{
|
||||
Shape = dto.Shape,
|
||||
Size = dto.Size,
|
||||
Description = dto.Description,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
Shape = parsedShape.Value,
|
||||
Type = materialType,
|
||||
Grade = dto.Grade,
|
||||
Size = dto.Size ?? string.Empty,
|
||||
Description = dto.Description
|
||||
};
|
||||
|
||||
_context.Materials.Add(material);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return CreatedAtAction(nameof(GetMaterial), new { id = material.Id }, new MaterialDto
|
||||
if (dto.Dimensions != null && dto.Dimensions.Count > 0)
|
||||
{
|
||||
Id = material.Id,
|
||||
Shape = material.Shape,
|
||||
Size = material.Size,
|
||||
Description = material.Description
|
||||
});
|
||||
var dimensions = MaterialService.CreateDimensionsForShape(parsedShape.Value);
|
||||
ApplyDimensionValues(dimensions, dto.Dimensions);
|
||||
|
||||
// Check for duplicates using generated size
|
||||
var generatedSize = dimensions.GenerateSizeString();
|
||||
var exists = await _materialService.ExistsAsync(parsedShape.Value, generatedSize);
|
||||
if (exists)
|
||||
return Conflict($"Material '{parsedShape.Value.GetDisplayName()} - {generatedSize}' already exists");
|
||||
|
||||
var created = await _materialService.CreateWithDimensionsAsync(material, dimensions);
|
||||
return CreatedAtAction(nameof(GetMaterial), new { id = created.Id }, MapToDto(created));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(material.Size))
|
||||
return BadRequest("Size is required when dimensions are not provided");
|
||||
|
||||
var exists = await _materialService.ExistsAsync(parsedShape.Value, material.Size);
|
||||
if (exists)
|
||||
return Conflict($"Material '{parsedShape.Value.GetDisplayName()} - {material.Size}' already exists");
|
||||
|
||||
var created = await _materialService.CreateAsync(material);
|
||||
return CreatedAtAction(nameof(GetMaterial), new { id = created.Id }, MapToDto(created));
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("bulk")]
|
||||
@@ -97,33 +124,74 @@ public class MaterialsController : ControllerBase
|
||||
|
||||
foreach (var dto in materials)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(dto.Shape) || string.IsNullOrWhiteSpace(dto.Size))
|
||||
if (string.IsNullOrWhiteSpace(dto.Shape))
|
||||
{
|
||||
errors.Add($"Invalid material: Shape and Size are required");
|
||||
errors.Add("Invalid material: Shape is required");
|
||||
continue;
|
||||
}
|
||||
|
||||
var exists = await _context.Materials
|
||||
.AnyAsync(m => m.Shape == dto.Shape && m.Size == dto.Size && m.IsActive);
|
||||
var parsedShape = MaterialShapeExtensions.ParseShape(dto.Shape);
|
||||
if (!parsedShape.HasValue)
|
||||
{
|
||||
errors.Add($"Unknown shape: {dto.Shape}");
|
||||
continue;
|
||||
}
|
||||
|
||||
var size = dto.Size ?? string.Empty;
|
||||
|
||||
if (dto.Dimensions != null && dto.Dimensions.Count > 0)
|
||||
{
|
||||
var dimensions = MaterialService.CreateDimensionsForShape(parsedShape.Value);
|
||||
ApplyDimensionValues(dimensions, dto.Dimensions);
|
||||
size = dimensions.GenerateSizeString();
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(size))
|
||||
{
|
||||
errors.Add($"Size is required for {dto.Shape}");
|
||||
continue;
|
||||
}
|
||||
|
||||
var exists = await _materialService.ExistsAsync(parsedShape.Value, size);
|
||||
if (exists)
|
||||
{
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
_context.Materials.Add(new Material
|
||||
MaterialType materialType = MaterialType.Steel;
|
||||
if (!string.IsNullOrWhiteSpace(dto.Type))
|
||||
{
|
||||
Shape = dto.Shape,
|
||||
Size = dto.Size,
|
||||
Description = dto.Description,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
if (!Enum.TryParse<MaterialType>(dto.Type, true, out materialType))
|
||||
{
|
||||
errors.Add($"Unknown material type: {dto.Type}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
var material = new Material
|
||||
{
|
||||
Shape = parsedShape.Value,
|
||||
Type = materialType,
|
||||
Grade = dto.Grade,
|
||||
Size = size,
|
||||
Description = dto.Description
|
||||
};
|
||||
|
||||
if (dto.Dimensions != null && dto.Dimensions.Count > 0)
|
||||
{
|
||||
var dimensions = MaterialService.CreateDimensionsForShape(parsedShape.Value);
|
||||
ApplyDimensionValues(dimensions, dto.Dimensions);
|
||||
await _materialService.CreateWithDimensionsAsync(material, dimensions);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _materialService.CreateAsync(material);
|
||||
}
|
||||
|
||||
created++;
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok(new BulkCreateResult
|
||||
{
|
||||
Created = created,
|
||||
@@ -132,39 +200,192 @@ public class MaterialsController : ControllerBase
|
||||
});
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> DeleteMaterial(int id)
|
||||
[HttpPut("{id}")]
|
||||
public async Task<ActionResult<MaterialDto>> UpdateMaterial(int id, UpdateMaterialDto dto)
|
||||
{
|
||||
var material = await _context.Materials.FindAsync(id);
|
||||
|
||||
var material = await _materialService.GetByIdAsync(id);
|
||||
if (material == null)
|
||||
return NotFound();
|
||||
|
||||
material.IsActive = false;
|
||||
await _context.SaveChangesAsync();
|
||||
if (dto.Type != null)
|
||||
{
|
||||
if (!Enum.TryParse<MaterialType>(dto.Type, true, out var materialType))
|
||||
return BadRequest($"Unknown material type: {dto.Type}");
|
||||
material.Type = materialType;
|
||||
}
|
||||
|
||||
if (dto.Grade != null) material.Grade = dto.Grade;
|
||||
if (dto.Size != null) material.Size = dto.Size;
|
||||
if (dto.Description != null) material.Description = dto.Description;
|
||||
|
||||
if (dto.Dimensions != null && dto.Dimensions.Count > 0)
|
||||
{
|
||||
var dimensions = material.Dimensions ?? MaterialService.CreateDimensionsForShape(material.Shape);
|
||||
ApplyDimensionValues(dimensions, dto.Dimensions);
|
||||
await _materialService.UpdateWithDimensionsAsync(material, dimensions, dto.RegenerateSize ?? false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _materialService.UpdateAsync(material);
|
||||
}
|
||||
|
||||
var updated = await _materialService.GetByIdAsync(id);
|
||||
return Ok(MapToDto(updated!));
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> DeleteMaterial(int id)
|
||||
{
|
||||
var material = await _materialService.GetByIdAsync(id);
|
||||
if (material == null)
|
||||
return NotFound();
|
||||
|
||||
await _materialService.DeleteAsync(id);
|
||||
return NoContent();
|
||||
}
|
||||
}
|
||||
|
||||
public class MaterialDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Shape { get; set; } = string.Empty;
|
||||
public string Size { get; set; } = string.Empty;
|
||||
public string? Description { get; set; }
|
||||
}
|
||||
[HttpPost("search")]
|
||||
public async Task<ActionResult<List<MaterialDto>>> SearchMaterials(MaterialSearchDto dto)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(dto.Shape))
|
||||
return BadRequest("Shape is required");
|
||||
|
||||
public class CreateMaterialDto
|
||||
{
|
||||
public string Shape { get; set; } = string.Empty;
|
||||
public string Size { get; set; } = string.Empty;
|
||||
public string? Description { get; set; }
|
||||
}
|
||||
var parsedShape = MaterialShapeExtensions.ParseShape(dto.Shape);
|
||||
if (!parsedShape.HasValue)
|
||||
return BadRequest($"Unknown shape: {dto.Shape}");
|
||||
|
||||
public class BulkCreateResult
|
||||
{
|
||||
public int Created { get; set; }
|
||||
public int Skipped { get; set; }
|
||||
public List<string> Errors { get; set; } = new();
|
||||
var results = parsedShape.Value switch
|
||||
{
|
||||
MaterialShape.RoundBar => await _materialService.SearchRoundBarByDiameterAsync(dto.TargetValue, dto.Tolerance),
|
||||
MaterialShape.RoundTube => await _materialService.SearchRoundTubeByODAsync(dto.TargetValue, dto.Tolerance),
|
||||
MaterialShape.FlatBar => await _materialService.SearchFlatBarByWidthAsync(dto.TargetValue, dto.Tolerance),
|
||||
MaterialShape.SquareBar => await _materialService.SearchSquareBarBySizeAsync(dto.TargetValue, dto.Tolerance),
|
||||
MaterialShape.SquareTube => await _materialService.SearchSquareTubeBySizeAsync(dto.TargetValue, dto.Tolerance),
|
||||
MaterialShape.RectangularTube => await _materialService.SearchRectangularTubeByWidthAsync(dto.TargetValue, dto.Tolerance),
|
||||
MaterialShape.Angle => await _materialService.SearchAngleByLegAsync(dto.TargetValue, dto.Tolerance),
|
||||
MaterialShape.Channel => await _materialService.SearchChannelByHeightAsync(dto.TargetValue, dto.Tolerance),
|
||||
MaterialShape.IBeam => await _materialService.SearchIBeamByHeightAsync(dto.TargetValue, dto.Tolerance),
|
||||
MaterialShape.Pipe => await _materialService.SearchPipeByNominalSizeAsync(dto.TargetValue, dto.Tolerance),
|
||||
_ => new List<Material>()
|
||||
};
|
||||
|
||||
return Ok(results.Select(MapToDto).ToList());
|
||||
}
|
||||
|
||||
private static MaterialDto MapToDto(Material m) => new()
|
||||
{
|
||||
Id = m.Id,
|
||||
Shape = m.Shape.GetDisplayName(),
|
||||
Type = m.Type.ToString(),
|
||||
Grade = m.Grade,
|
||||
Size = m.Size,
|
||||
Description = m.Description,
|
||||
IsActive = m.IsActive,
|
||||
Dimensions = m.Dimensions != null ? MapDimensionsToDto(m.Dimensions) : null
|
||||
};
|
||||
|
||||
private static MaterialDimensionsDto MapDimensionsToDto(MaterialDimensions d)
|
||||
{
|
||||
var dto = new MaterialDimensionsDto
|
||||
{
|
||||
DimensionType = d.GetType().Name.Replace("Dimensions", "")
|
||||
};
|
||||
|
||||
// Extract dimension values based on type
|
||||
switch (d)
|
||||
{
|
||||
case RoundBarDimensions rb:
|
||||
dto.Values["Diameter"] = rb.Diameter;
|
||||
break;
|
||||
case RoundTubeDimensions rt:
|
||||
dto.Values["OuterDiameter"] = rt.OuterDiameter;
|
||||
dto.Values["Wall"] = rt.Wall;
|
||||
break;
|
||||
case FlatBarDimensions fb:
|
||||
dto.Values["Width"] = fb.Width;
|
||||
dto.Values["Thickness"] = fb.Thickness;
|
||||
break;
|
||||
case SquareBarDimensions sb:
|
||||
dto.Values["Size"] = sb.Size;
|
||||
break;
|
||||
case SquareTubeDimensions st:
|
||||
dto.Values["Size"] = st.Size;
|
||||
dto.Values["Wall"] = st.Wall;
|
||||
break;
|
||||
case RectangularTubeDimensions rect:
|
||||
dto.Values["Width"] = rect.Width;
|
||||
dto.Values["Height"] = rect.Height;
|
||||
dto.Values["Wall"] = rect.Wall;
|
||||
break;
|
||||
case AngleDimensions a:
|
||||
dto.Values["Leg1"] = a.Leg1;
|
||||
dto.Values["Leg2"] = a.Leg2;
|
||||
dto.Values["Thickness"] = a.Thickness;
|
||||
break;
|
||||
case ChannelDimensions c:
|
||||
dto.Values["Height"] = c.Height;
|
||||
dto.Values["Flange"] = c.Flange;
|
||||
dto.Values["Web"] = c.Web;
|
||||
break;
|
||||
case IBeamDimensions ib:
|
||||
dto.Values["Height"] = ib.Height;
|
||||
dto.Values["WeightPerFoot"] = ib.WeightPerFoot;
|
||||
break;
|
||||
case PipeDimensions p:
|
||||
dto.Values["NominalSize"] = p.NominalSize;
|
||||
if (p.Wall.HasValue) dto.Values["Wall"] = p.Wall.Value;
|
||||
break;
|
||||
}
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
private static void ApplyDimensionValues(MaterialDimensions dimensions, Dictionary<string, decimal> values)
|
||||
{
|
||||
switch (dimensions)
|
||||
{
|
||||
case RoundBarDimensions rb:
|
||||
if (values.TryGetValue("Diameter", out var diameter)) rb.Diameter = diameter;
|
||||
break;
|
||||
case RoundTubeDimensions rt:
|
||||
if (values.TryGetValue("OuterDiameter", out var od)) rt.OuterDiameter = od;
|
||||
if (values.TryGetValue("Wall", out var rtWall)) rt.Wall = rtWall;
|
||||
break;
|
||||
case FlatBarDimensions fb:
|
||||
if (values.TryGetValue("Width", out var fbWidth)) fb.Width = fbWidth;
|
||||
if (values.TryGetValue("Thickness", out var fbThick)) fb.Thickness = fbThick;
|
||||
break;
|
||||
case SquareBarDimensions sb:
|
||||
if (values.TryGetValue("Size", out var sbSize)) sb.Size = sbSize;
|
||||
break;
|
||||
case SquareTubeDimensions st:
|
||||
if (values.TryGetValue("Size", out var stSize)) st.Size = stSize;
|
||||
if (values.TryGetValue("Wall", out var stWall)) st.Wall = stWall;
|
||||
break;
|
||||
case RectangularTubeDimensions rect:
|
||||
if (values.TryGetValue("Width", out var rectWidth)) rect.Width = rectWidth;
|
||||
if (values.TryGetValue("Height", out var rectHeight)) rect.Height = rectHeight;
|
||||
if (values.TryGetValue("Wall", out var rectWall)) rect.Wall = rectWall;
|
||||
break;
|
||||
case AngleDimensions a:
|
||||
if (values.TryGetValue("Leg1", out var leg1)) a.Leg1 = leg1;
|
||||
if (values.TryGetValue("Leg2", out var leg2)) a.Leg2 = leg2;
|
||||
if (values.TryGetValue("Thickness", out var aThick)) a.Thickness = aThick;
|
||||
break;
|
||||
case ChannelDimensions c:
|
||||
if (values.TryGetValue("Height", out var cHeight)) c.Height = cHeight;
|
||||
if (values.TryGetValue("Flange", out var flange)) c.Flange = flange;
|
||||
if (values.TryGetValue("Web", out var web)) c.Web = web;
|
||||
break;
|
||||
case IBeamDimensions ib:
|
||||
if (values.TryGetValue("Height", out var ibHeight)) ib.Height = ibHeight;
|
||||
if (values.TryGetValue("WeightPerFoot", out var weight)) ib.WeightPerFoot = weight;
|
||||
break;
|
||||
case PipeDimensions p:
|
||||
if (values.TryGetValue("NominalSize", out var nps)) p.NominalSize = nps;
|
||||
if (values.TryGetValue("Wall", out var pWall)) p.Wall = pWall;
|
||||
if (values.TryGetValue("Schedule", out var schedule)) p.Schedule = schedule.ToString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
156
CutList.Web/Controllers/PackingController.cs
Normal file
156
CutList.Web/Controllers/PackingController.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
using CutList.Core;
|
||||
using CutList.Core.Formatting;
|
||||
using CutList.Core.Nesting;
|
||||
using CutList.Web.DTOs;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace CutList.Web.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class PackingController : ControllerBase
|
||||
{
|
||||
[HttpPost("optimize")]
|
||||
public ActionResult<object> Optimize(StandalonePackRequestDto dto)
|
||||
{
|
||||
if (dto.Parts.Count == 0)
|
||||
return BadRequest("At least one part is required");
|
||||
|
||||
if (dto.StockBins.Count == 0)
|
||||
return BadRequest("At least one stock bin is required");
|
||||
|
||||
// Parse parts
|
||||
var items = new List<BinItem>();
|
||||
foreach (var part in dto.Parts)
|
||||
{
|
||||
double length;
|
||||
try
|
||||
{
|
||||
length = ArchUnits.ParseToInches(part.Length);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return BadRequest($"Invalid length format for part '{part.Name}': {part.Length}");
|
||||
}
|
||||
|
||||
for (int i = 0; i < part.Quantity; i++)
|
||||
{
|
||||
items.Add(new BinItem(part.Name, length));
|
||||
}
|
||||
}
|
||||
|
||||
// Parse stock bins
|
||||
var multiBins = new List<MultiBin>();
|
||||
foreach (var bin in dto.StockBins)
|
||||
{
|
||||
double length;
|
||||
try
|
||||
{
|
||||
length = ArchUnits.ParseToInches(bin.Length);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return BadRequest($"Invalid length format for stock bin: {bin.Length}");
|
||||
}
|
||||
|
||||
multiBins.Add(new MultiBin(length, bin.Quantity, bin.Priority));
|
||||
}
|
||||
|
||||
// Select strategy
|
||||
var strategy = dto.Strategy?.ToLowerInvariant() switch
|
||||
{
|
||||
"bestfit" => PackingStrategy.BestFit,
|
||||
"exhaustive" => PackingStrategy.Exhaustive,
|
||||
_ => PackingStrategy.AdvancedFit
|
||||
};
|
||||
|
||||
// Run packing
|
||||
var engine = new MultiBinEngine
|
||||
{
|
||||
Spacing = (double)dto.Kerf,
|
||||
Strategy = strategy
|
||||
};
|
||||
|
||||
engine.SetBins(multiBins);
|
||||
var result = engine.Pack(items);
|
||||
|
||||
// Map result
|
||||
var bins = result.Bins.Select(bin => new PackedBinDto
|
||||
{
|
||||
LengthInches = bin.Length,
|
||||
LengthFormatted = ArchUnits.FormatFromInches(bin.Length),
|
||||
UsedInches = bin.UsedLength,
|
||||
UsedFormatted = ArchUnits.FormatFromInches(bin.UsedLength),
|
||||
WasteInches = bin.RemainingLength,
|
||||
WasteFormatted = ArchUnits.FormatFromInches(bin.RemainingLength),
|
||||
Efficiency = bin.Length > 0 ? bin.UsedLength / bin.Length * 100 : 0,
|
||||
Items = bin.Items.Select(i => new PackedItemDto
|
||||
{
|
||||
Name = i.Name,
|
||||
LengthInches = i.Length,
|
||||
LengthFormatted = ArchUnits.FormatFromInches(i.Length)
|
||||
}).ToList()
|
||||
}).ToList();
|
||||
|
||||
var itemsNotPlaced = result.ItemsNotUsed.Select(i => new PackedItemDto
|
||||
{
|
||||
Name = i.Name,
|
||||
LengthInches = i.Length,
|
||||
LengthFormatted = ArchUnits.FormatFromInches(i.Length)
|
||||
}).ToList();
|
||||
|
||||
var totalMaterial = result.Bins.Sum(b => b.Length);
|
||||
var totalUsed = result.Bins.Sum(b => b.UsedLength);
|
||||
var totalWaste = result.Bins.Sum(b => b.RemainingLength);
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
Bins = bins,
|
||||
ItemsNotPlaced = itemsNotPlaced,
|
||||
Summary = new
|
||||
{
|
||||
TotalBins = result.Bins.Count,
|
||||
TotalPieces = result.Bins.Sum(b => b.Items.Count),
|
||||
TotalMaterialInches = totalMaterial,
|
||||
TotalMaterialFormatted = ArchUnits.FormatFromInches(totalMaterial),
|
||||
TotalUsedInches = totalUsed,
|
||||
TotalUsedFormatted = ArchUnits.FormatFromInches(totalUsed),
|
||||
TotalWasteInches = totalWaste,
|
||||
TotalWasteFormatted = ArchUnits.FormatFromInches(totalWaste),
|
||||
Efficiency = totalMaterial > 0 ? totalUsed / totalMaterial * 100 : 0,
|
||||
ItemsNotPlaced = result.ItemsNotUsed.Count
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("parse-length")]
|
||||
public ActionResult<ParseLengthResponseDto> ParseLength(ParseLengthRequestDto dto)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(dto.Input))
|
||||
return BadRequest("Input is required");
|
||||
|
||||
try
|
||||
{
|
||||
var inches = ArchUnits.ParseToInches(dto.Input);
|
||||
return Ok(new ParseLengthResponseDto
|
||||
{
|
||||
Inches = inches,
|
||||
Formatted = ArchUnits.FormatFromInches(inches)
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest($"Could not parse '{dto.Input}': {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("format-length")]
|
||||
public ActionResult<FormatLengthResponseDto> FormatLength(FormatLengthRequestDto dto)
|
||||
{
|
||||
return Ok(new FormatLengthResponseDto
|
||||
{
|
||||
Inches = dto.Inches,
|
||||
Formatted = ArchUnits.FormatFromInches(dto.Inches)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
using CutList.Web.Data;
|
||||
using CutList.Web.Data.Entities;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace CutList.Web.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class SeedController : ControllerBase
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
public SeedController(ApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
[HttpPost("alro-1018-round")]
|
||||
public async Task<ActionResult> SeedAlro1018Round()
|
||||
{
|
||||
// Add Alro supplier if not exists
|
||||
var alro = await _context.Suppliers.FirstOrDefaultAsync(s => s.Name == "Alro");
|
||||
if (alro == null)
|
||||
{
|
||||
alro = new Supplier
|
||||
{
|
||||
Name = "Alro",
|
||||
ContactInfo = "https://www.alro.com",
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
_context.Suppliers.Add(alro);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// 1018 CF Round bar sizes from the screenshot
|
||||
var sizes = new[]
|
||||
{
|
||||
"1/8\"",
|
||||
"5/32\"",
|
||||
"3/16\"",
|
||||
"7/32\"",
|
||||
".236\"",
|
||||
"1/4\"",
|
||||
"9/32\"",
|
||||
"5/16\"",
|
||||
"11/32\"",
|
||||
"3/8\"",
|
||||
".394\"",
|
||||
"13/32\"",
|
||||
"7/16\"",
|
||||
"15/32\"",
|
||||
".472\"",
|
||||
"1/2\"",
|
||||
"17/32\"",
|
||||
"9/16\"",
|
||||
".593\""
|
||||
};
|
||||
|
||||
var created = 0;
|
||||
var skipped = 0;
|
||||
|
||||
foreach (var size in sizes)
|
||||
{
|
||||
var exists = await _context.Materials
|
||||
.AnyAsync(m => m.Shape == "Round Bar" && m.Size == size && m.IsActive);
|
||||
|
||||
if (exists)
|
||||
{
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
_context.Materials.Add(new Material
|
||||
{
|
||||
Shape = "Round Bar",
|
||||
Size = size,
|
||||
Description = "1018 Cold Finished",
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
created++;
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
Message = "Alro 1018 CF Round materials seeded",
|
||||
SupplierId = alro.Id,
|
||||
MaterialsCreated = created,
|
||||
MaterialsSkipped = skipped
|
||||
});
|
||||
}
|
||||
}
|
||||
272
CutList.Web/Controllers/StockItemsController.cs
Normal file
272
CutList.Web/Controllers/StockItemsController.cs
Normal file
@@ -0,0 +1,272 @@
|
||||
using CutList.Core.Formatting;
|
||||
using CutList.Web.Data.Entities;
|
||||
using CutList.Web.DTOs;
|
||||
using CutList.Web.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace CutList.Web.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/stock-items")]
|
||||
public class StockItemsController : ControllerBase
|
||||
{
|
||||
private readonly StockItemService _stockItemService;
|
||||
private readonly SupplierService _supplierService;
|
||||
|
||||
public StockItemsController(StockItemService stockItemService, SupplierService supplierService)
|
||||
{
|
||||
_stockItemService = stockItemService;
|
||||
_supplierService = supplierService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<List<StockItemDto>>> GetAll(
|
||||
[FromQuery] bool includeInactive = false,
|
||||
[FromQuery] int? materialId = null)
|
||||
{
|
||||
List<StockItem> items;
|
||||
if (materialId.HasValue)
|
||||
items = await _stockItemService.GetByMaterialAsync(materialId.Value, includeInactive);
|
||||
else
|
||||
items = await _stockItemService.GetAllAsync(includeInactive);
|
||||
|
||||
return Ok(items.Select(MapToDto).ToList());
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<ActionResult<StockItemDto>> GetById(int id)
|
||||
{
|
||||
var item = await _stockItemService.GetByIdAsync(id);
|
||||
if (item == null)
|
||||
return NotFound();
|
||||
|
||||
return Ok(MapToDto(item));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<StockItemDto>> Create(CreateStockItemDto dto)
|
||||
{
|
||||
double lengthInches;
|
||||
try
|
||||
{
|
||||
lengthInches = ArchUnits.ParseToInches(dto.Length);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return BadRequest($"Invalid length format: {dto.Length}");
|
||||
}
|
||||
|
||||
var exists = await _stockItemService.ExistsAsync(dto.MaterialId, (decimal)lengthInches);
|
||||
if (exists)
|
||||
return Conflict("A stock item with this material and length already exists");
|
||||
|
||||
var stockItem = new StockItem
|
||||
{
|
||||
MaterialId = dto.MaterialId,
|
||||
LengthInches = (decimal)lengthInches,
|
||||
Name = dto.Name,
|
||||
QuantityOnHand = dto.QuantityOnHand,
|
||||
Notes = dto.Notes
|
||||
};
|
||||
|
||||
await _stockItemService.CreateAsync(stockItem);
|
||||
|
||||
// Reload with includes
|
||||
var created = await _stockItemService.GetByIdAsync(stockItem.Id);
|
||||
return CreatedAtAction(nameof(GetById), new { id = stockItem.Id }, MapToDto(created!));
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<ActionResult<StockItemDto>> Update(int id, UpdateStockItemDto dto)
|
||||
{
|
||||
var item = await _stockItemService.GetByIdAsync(id);
|
||||
if (item == null)
|
||||
return NotFound();
|
||||
|
||||
if (dto.Length != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
item.LengthInches = (decimal)ArchUnits.ParseToInches(dto.Length);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return BadRequest($"Invalid length format: {dto.Length}");
|
||||
}
|
||||
}
|
||||
|
||||
if (dto.Name != null) item.Name = dto.Name;
|
||||
if (dto.Notes != null) item.Notes = dto.Notes;
|
||||
|
||||
await _stockItemService.UpdateAsync(item);
|
||||
return Ok(MapToDto(item));
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
var item = await _stockItemService.GetByIdAsync(id);
|
||||
if (item == null)
|
||||
return NotFound();
|
||||
|
||||
await _stockItemService.DeleteAsync(id);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpGet("by-material/{materialId}")]
|
||||
public async Task<ActionResult<List<StockItemDto>>> GetByMaterial(int materialId)
|
||||
{
|
||||
var items = await _stockItemService.GetByMaterialAsync(materialId);
|
||||
return Ok(items.Select(MapToDto).ToList());
|
||||
}
|
||||
|
||||
[HttpGet("{id}/offerings")]
|
||||
public async Task<ActionResult<List<OfferingDto>>> GetOfferings(int id)
|
||||
{
|
||||
var item = await _stockItemService.GetByIdAsync(id);
|
||||
if (item == null)
|
||||
return NotFound();
|
||||
|
||||
var offerings = await _supplierService.GetOfferingsForStockItemAsync(id);
|
||||
return Ok(offerings.Select(MapOfferingToDto).ToList());
|
||||
}
|
||||
|
||||
[HttpGet("{id}/pricing")]
|
||||
public async Task<ActionResult<StockPricingDto>> GetPricing(int id)
|
||||
{
|
||||
var item = await _stockItemService.GetByIdAsync(id);
|
||||
if (item == null)
|
||||
return NotFound();
|
||||
|
||||
var avgCost = await _stockItemService.GetAverageCostAsync(id);
|
||||
var lastPrice = await _stockItemService.GetLastPurchasePriceAsync(id);
|
||||
|
||||
return Ok(new StockPricingDto
|
||||
{
|
||||
AverageCost = avgCost,
|
||||
LastPurchasePrice = lastPrice
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("{id}/transactions")]
|
||||
public async Task<ActionResult<List<StockTransactionDto>>> GetTransactions(int id, [FromQuery] int? limit = null)
|
||||
{
|
||||
var item = await _stockItemService.GetByIdAsync(id);
|
||||
if (item == null)
|
||||
return NotFound();
|
||||
|
||||
var transactions = await _stockItemService.GetTransactionHistoryAsync(id, limit);
|
||||
return Ok(transactions.Select(MapTransactionToDto).ToList());
|
||||
}
|
||||
|
||||
[HttpPost("{id}/receive")]
|
||||
public async Task<ActionResult<StockTransactionDto>> ReceiveStock(int id, AddStockDto dto)
|
||||
{
|
||||
try
|
||||
{
|
||||
var transaction = await _stockItemService.AddStockAsync(id, dto.Quantity, dto.SupplierId, dto.UnitPrice, dto.Notes);
|
||||
return Ok(MapTransactionToDto(transaction));
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("{id}/use")]
|
||||
public async Task<ActionResult<StockTransactionDto>> UseStock(int id, UseStockDto dto)
|
||||
{
|
||||
try
|
||||
{
|
||||
var transaction = await _stockItemService.UseStockAsync(id, dto.Quantity, dto.JobId, dto.Notes);
|
||||
return Ok(MapTransactionToDto(transaction));
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("{id}/adjust")]
|
||||
public async Task<ActionResult<StockTransactionDto>> AdjustStock(int id, AdjustStockDto dto)
|
||||
{
|
||||
try
|
||||
{
|
||||
var transaction = await _stockItemService.AdjustStockAsync(id, dto.NewQuantity, dto.Notes);
|
||||
return Ok(MapTransactionToDto(transaction));
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("{id}/scrap")]
|
||||
public async Task<ActionResult<StockTransactionDto>> ScrapStock(int id, ScrapStockDto dto)
|
||||
{
|
||||
try
|
||||
{
|
||||
var transaction = await _stockItemService.ScrapStockAsync(id, dto.Quantity, dto.Notes);
|
||||
return Ok(MapTransactionToDto(transaction));
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("{id}/recalculate")]
|
||||
public async Task<ActionResult<object>> RecalculateStock(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var newQuantity = await _stockItemService.RecalculateQuantityAsync(id);
|
||||
return Ok(new { QuantityOnHand = newQuantity });
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
private static StockItemDto MapToDto(StockItem s) => new()
|
||||
{
|
||||
Id = s.Id,
|
||||
MaterialId = s.MaterialId,
|
||||
MaterialName = s.Material?.DisplayName ?? string.Empty,
|
||||
LengthInches = s.LengthInches,
|
||||
LengthFormatted = ArchUnits.FormatFromInches((double)s.LengthInches),
|
||||
Name = s.Name,
|
||||
QuantityOnHand = s.QuantityOnHand,
|
||||
Notes = s.Notes,
|
||||
IsActive = s.IsActive
|
||||
};
|
||||
|
||||
private static StockTransactionDto MapTransactionToDto(StockTransaction t) => new()
|
||||
{
|
||||
Id = t.Id,
|
||||
StockItemId = t.StockItemId,
|
||||
Quantity = t.Quantity,
|
||||
Type = t.Type.ToString(),
|
||||
JobId = t.JobId,
|
||||
JobNumber = t.Job?.JobNumber,
|
||||
SupplierId = t.SupplierId,
|
||||
SupplierName = t.Supplier?.Name,
|
||||
UnitPrice = t.UnitPrice,
|
||||
Notes = t.Notes,
|
||||
CreatedAt = t.CreatedAt
|
||||
};
|
||||
|
||||
private static OfferingDto MapOfferingToDto(SupplierOffering o) => new()
|
||||
{
|
||||
Id = o.Id,
|
||||
SupplierId = o.SupplierId,
|
||||
SupplierName = o.Supplier?.Name,
|
||||
StockItemId = o.StockItemId,
|
||||
PartNumber = o.PartNumber,
|
||||
SupplierDescription = o.SupplierDescription,
|
||||
Price = o.Price,
|
||||
Notes = o.Notes,
|
||||
IsActive = o.IsActive
|
||||
};
|
||||
}
|
||||
172
CutList.Web/Controllers/SuppliersController.cs
Normal file
172
CutList.Web/Controllers/SuppliersController.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
using CutList.Core.Formatting;
|
||||
using CutList.Web.Data.Entities;
|
||||
using CutList.Web.DTOs;
|
||||
using CutList.Web.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace CutList.Web.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class SuppliersController : ControllerBase
|
||||
{
|
||||
private readonly SupplierService _supplierService;
|
||||
|
||||
public SuppliersController(SupplierService supplierService)
|
||||
{
|
||||
_supplierService = supplierService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<List<SupplierDto>>> GetAll([FromQuery] bool includeInactive = false)
|
||||
{
|
||||
var suppliers = await _supplierService.GetAllAsync(includeInactive);
|
||||
return Ok(suppliers.Select(MapToDto).ToList());
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<ActionResult<SupplierDto>> GetById(int id)
|
||||
{
|
||||
var supplier = await _supplierService.GetByIdAsync(id);
|
||||
if (supplier == null)
|
||||
return NotFound();
|
||||
|
||||
return Ok(MapToDto(supplier));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<SupplierDto>> Create(CreateSupplierDto dto)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(dto.Name))
|
||||
return BadRequest("Name is required");
|
||||
|
||||
var supplier = new Supplier
|
||||
{
|
||||
Name = dto.Name,
|
||||
ContactInfo = dto.ContactInfo,
|
||||
Notes = dto.Notes
|
||||
};
|
||||
|
||||
await _supplierService.CreateAsync(supplier);
|
||||
return CreatedAtAction(nameof(GetById), new { id = supplier.Id }, MapToDto(supplier));
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<ActionResult<SupplierDto>> Update(int id, UpdateSupplierDto dto)
|
||||
{
|
||||
var supplier = await _supplierService.GetByIdAsync(id);
|
||||
if (supplier == null)
|
||||
return NotFound();
|
||||
|
||||
if (dto.Name != null) supplier.Name = dto.Name;
|
||||
if (dto.ContactInfo != null) supplier.ContactInfo = dto.ContactInfo;
|
||||
if (dto.Notes != null) supplier.Notes = dto.Notes;
|
||||
|
||||
await _supplierService.UpdateAsync(supplier);
|
||||
return Ok(MapToDto(supplier));
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
var supplier = await _supplierService.GetByIdAsync(id);
|
||||
if (supplier == null)
|
||||
return NotFound();
|
||||
|
||||
await _supplierService.DeleteAsync(id);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
// --- Offerings ---
|
||||
|
||||
[HttpGet("{id}/offerings")]
|
||||
public async Task<ActionResult<List<OfferingDto>>> GetOfferings(int id)
|
||||
{
|
||||
var supplier = await _supplierService.GetByIdAsync(id);
|
||||
if (supplier == null)
|
||||
return NotFound();
|
||||
|
||||
var offerings = await _supplierService.GetOfferingsForSupplierAsync(id);
|
||||
return Ok(offerings.Select(MapOfferingToDto).ToList());
|
||||
}
|
||||
|
||||
[HttpPost("{id}/offerings")]
|
||||
public async Task<ActionResult<OfferingDto>> CreateOffering(int id, CreateOfferingDto dto)
|
||||
{
|
||||
var supplier = await _supplierService.GetByIdAsync(id);
|
||||
if (supplier == null)
|
||||
return NotFound();
|
||||
|
||||
var exists = await _supplierService.OfferingExistsAsync(id, dto.StockItemId);
|
||||
if (exists)
|
||||
return Conflict("An offering for this supplier and stock item already exists");
|
||||
|
||||
var offering = new SupplierOffering
|
||||
{
|
||||
SupplierId = id,
|
||||
StockItemId = dto.StockItemId,
|
||||
PartNumber = dto.PartNumber,
|
||||
SupplierDescription = dto.SupplierDescription,
|
||||
Price = dto.Price,
|
||||
Notes = dto.Notes
|
||||
};
|
||||
|
||||
await _supplierService.AddOfferingAsync(offering);
|
||||
|
||||
// Reload with includes
|
||||
var created = await _supplierService.GetOfferingByIdAsync(offering.Id);
|
||||
return CreatedAtAction(nameof(GetOfferings), new { id }, MapOfferingToDto(created!));
|
||||
}
|
||||
|
||||
[HttpPut("{supplierId}/offerings/{offeringId}")]
|
||||
public async Task<ActionResult<OfferingDto>> UpdateOffering(int supplierId, int offeringId, UpdateOfferingDto dto)
|
||||
{
|
||||
var offering = await _supplierService.GetOfferingByIdAsync(offeringId);
|
||||
if (offering == null || offering.SupplierId != supplierId)
|
||||
return NotFound();
|
||||
|
||||
if (dto.PartNumber != null) offering.PartNumber = dto.PartNumber;
|
||||
if (dto.SupplierDescription != null) offering.SupplierDescription = dto.SupplierDescription;
|
||||
if (dto.Price.HasValue) offering.Price = dto.Price;
|
||||
if (dto.Notes != null) offering.Notes = dto.Notes;
|
||||
|
||||
await _supplierService.UpdateOfferingAsync(offering);
|
||||
return Ok(MapOfferingToDto(offering));
|
||||
}
|
||||
|
||||
[HttpDelete("{supplierId}/offerings/{offeringId}")]
|
||||
public async Task<IActionResult> DeleteOffering(int supplierId, int offeringId)
|
||||
{
|
||||
var offering = await _supplierService.GetOfferingByIdAsync(offeringId);
|
||||
if (offering == null || offering.SupplierId != supplierId)
|
||||
return NotFound();
|
||||
|
||||
await _supplierService.DeleteOfferingAsync(offeringId);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
private static SupplierDto MapToDto(Supplier s) => new()
|
||||
{
|
||||
Id = s.Id,
|
||||
Name = s.Name,
|
||||
ContactInfo = s.ContactInfo,
|
||||
Notes = s.Notes,
|
||||
IsActive = s.IsActive
|
||||
};
|
||||
|
||||
private static OfferingDto MapOfferingToDto(SupplierOffering o) => new()
|
||||
{
|
||||
Id = o.Id,
|
||||
SupplierId = o.SupplierId,
|
||||
SupplierName = o.Supplier?.Name,
|
||||
StockItemId = o.StockItemId,
|
||||
MaterialName = o.StockItem?.Material?.DisplayName,
|
||||
LengthInches = o.StockItem?.LengthInches,
|
||||
LengthFormatted = o.StockItem != null ? ArchUnits.FormatFromInches((double)o.StockItem.LengthInches) : null,
|
||||
PartNumber = o.PartNumber,
|
||||
SupplierDescription = o.SupplierDescription,
|
||||
Price = o.Price,
|
||||
Notes = o.Notes,
|
||||
IsActive = o.IsActive
|
||||
};
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
142
CutList.Web/DTOs/CatalogDtos.cs
Normal file
142
CutList.Web/DTOs/CatalogDtos.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
namespace CutList.Web.DTOs;
|
||||
|
||||
public class CatalogData
|
||||
{
|
||||
public DateTime ExportedAt { get; set; }
|
||||
public List<CatalogSupplierDto> Suppliers { get; set; } = [];
|
||||
public List<CatalogCuttingToolDto> CuttingTools { get; set; } = [];
|
||||
public CatalogMaterialsDto Materials { get; set; } = new();
|
||||
}
|
||||
|
||||
public class CatalogSupplierDto
|
||||
{
|
||||
public string Name { get; set; } = "";
|
||||
public string? ContactInfo { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
}
|
||||
|
||||
public class CatalogCuttingToolDto
|
||||
{
|
||||
public string Name { get; set; } = "";
|
||||
public decimal KerfInches { get; set; }
|
||||
public bool IsDefault { get; set; }
|
||||
}
|
||||
|
||||
public class CatalogMaterialsDto
|
||||
{
|
||||
public List<CatalogAngleDto> Angles { get; set; } = [];
|
||||
public List<CatalogChannelDto> Channels { get; set; } = [];
|
||||
public List<CatalogFlatBarDto> FlatBars { get; set; } = [];
|
||||
public List<CatalogIBeamDto> IBeams { get; set; } = [];
|
||||
public List<CatalogPipeDto> Pipes { get; set; } = [];
|
||||
public List<CatalogRectangularTubeDto> RectangularTubes { get; set; } = [];
|
||||
public List<CatalogRoundBarDto> RoundBars { get; set; } = [];
|
||||
public List<CatalogRoundTubeDto> RoundTubes { get; set; } = [];
|
||||
public List<CatalogSquareBarDto> SquareBars { get; set; } = [];
|
||||
public List<CatalogSquareTubeDto> SquareTubes { get; set; } = [];
|
||||
}
|
||||
|
||||
public abstract class CatalogMaterialBaseDto
|
||||
{
|
||||
public string Type { get; set; } = "";
|
||||
public string? Grade { get; set; }
|
||||
public string Size { get; set; } = "";
|
||||
public string? Description { get; set; }
|
||||
public List<CatalogStockItemDto> StockItems { get; set; } = [];
|
||||
}
|
||||
|
||||
public class CatalogAngleDto : CatalogMaterialBaseDto
|
||||
{
|
||||
public decimal Leg1 { get; set; }
|
||||
public decimal Leg2 { get; set; }
|
||||
public decimal Thickness { get; set; }
|
||||
}
|
||||
|
||||
public class CatalogChannelDto : CatalogMaterialBaseDto
|
||||
{
|
||||
public decimal Height { get; set; }
|
||||
public decimal Flange { get; set; }
|
||||
public decimal Web { get; set; }
|
||||
}
|
||||
|
||||
public class CatalogFlatBarDto : CatalogMaterialBaseDto
|
||||
{
|
||||
public decimal Width { get; set; }
|
||||
public decimal Thickness { get; set; }
|
||||
}
|
||||
|
||||
public class CatalogIBeamDto : CatalogMaterialBaseDto
|
||||
{
|
||||
public decimal Height { get; set; }
|
||||
public decimal WeightPerFoot { get; set; }
|
||||
}
|
||||
|
||||
public class CatalogPipeDto : CatalogMaterialBaseDto
|
||||
{
|
||||
public decimal NominalSize { get; set; }
|
||||
public decimal Wall { get; set; }
|
||||
public string? Schedule { get; set; }
|
||||
}
|
||||
|
||||
public class CatalogRectangularTubeDto : CatalogMaterialBaseDto
|
||||
{
|
||||
public decimal Width { get; set; }
|
||||
public decimal Height { get; set; }
|
||||
public decimal Wall { get; set; }
|
||||
}
|
||||
|
||||
public class CatalogRoundBarDto : CatalogMaterialBaseDto
|
||||
{
|
||||
public decimal Diameter { get; set; }
|
||||
}
|
||||
|
||||
public class CatalogRoundTubeDto : CatalogMaterialBaseDto
|
||||
{
|
||||
public decimal OuterDiameter { get; set; }
|
||||
public decimal Wall { get; set; }
|
||||
}
|
||||
|
||||
public class CatalogSquareBarDto : CatalogMaterialBaseDto
|
||||
{
|
||||
public decimal SideLength { get; set; }
|
||||
}
|
||||
|
||||
public class CatalogSquareTubeDto : CatalogMaterialBaseDto
|
||||
{
|
||||
public decimal SideLength { get; set; }
|
||||
public decimal Wall { get; set; }
|
||||
}
|
||||
|
||||
public class CatalogStockItemDto
|
||||
{
|
||||
public decimal LengthInches { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public int QuantityOnHand { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public List<CatalogSupplierOfferingDto> SupplierOfferings { get; set; } = [];
|
||||
}
|
||||
|
||||
public class CatalogSupplierOfferingDto
|
||||
{
|
||||
public string SupplierName { get; set; } = "";
|
||||
public string? PartNumber { get; set; }
|
||||
public string? SupplierDescription { get; set; }
|
||||
public decimal? Price { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
}
|
||||
|
||||
public class ImportResultDto
|
||||
{
|
||||
public int SuppliersCreated { get; set; }
|
||||
public int SuppliersUpdated { get; set; }
|
||||
public int CuttingToolsCreated { get; set; }
|
||||
public int CuttingToolsUpdated { get; set; }
|
||||
public int MaterialsCreated { get; set; }
|
||||
public int MaterialsUpdated { get; set; }
|
||||
public int StockItemsCreated { get; set; }
|
||||
public int StockItemsUpdated { get; set; }
|
||||
public int OfferingsCreated { get; set; }
|
||||
public int OfferingsUpdated { get; set; }
|
||||
public List<string> Errors { get; set; } = [];
|
||||
public List<string> Warnings { get; set; } = [];
|
||||
}
|
||||
24
CutList.Web/DTOs/CuttingToolDtos.cs
Normal file
24
CutList.Web/DTOs/CuttingToolDtos.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace CutList.Web.DTOs;
|
||||
|
||||
public class CuttingToolDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public decimal KerfInches { get; set; }
|
||||
public bool IsDefault { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
|
||||
public class CreateCuttingToolDto
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public decimal KerfInches { get; set; }
|
||||
public bool IsDefault { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateCuttingToolDto
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public decimal? KerfInches { get; set; }
|
||||
public bool? IsDefault { get; set; }
|
||||
}
|
||||
111
CutList.Web/DTOs/JobDtos.cs
Normal file
111
CutList.Web/DTOs/JobDtos.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
namespace CutList.Web.DTOs;
|
||||
|
||||
public class JobDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string JobNumber { get; set; } = string.Empty;
|
||||
public string? Name { get; set; }
|
||||
public string? Customer { get; set; }
|
||||
public int? CuttingToolId { get; set; }
|
||||
public string? CuttingToolName { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public int PartCount { get; set; }
|
||||
public int StockCount { get; set; }
|
||||
}
|
||||
|
||||
public class JobDetailDto : JobDto
|
||||
{
|
||||
public List<JobPartDto> Parts { get; set; } = new();
|
||||
public List<JobStockDto> Stock { get; set; } = new();
|
||||
}
|
||||
|
||||
public class CreateJobDto
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public string? Customer { get; set; }
|
||||
public int? CuttingToolId { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateJobDto
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public string? Customer { get; set; }
|
||||
public int? CuttingToolId { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
}
|
||||
|
||||
public class JobPartDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int JobId { get; set; }
|
||||
public int MaterialId { get; set; }
|
||||
public string MaterialName { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public decimal LengthInches { get; set; }
|
||||
public string LengthFormatted { get; set; } = string.Empty;
|
||||
public int Quantity { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
}
|
||||
|
||||
public class CreateJobPartDto
|
||||
{
|
||||
public int MaterialId { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Length { get; set; } = string.Empty;
|
||||
public int Quantity { get; set; } = 1;
|
||||
}
|
||||
|
||||
public class UpdateJobPartDto
|
||||
{
|
||||
public int? MaterialId { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Length { get; set; }
|
||||
public int? Quantity { get; set; }
|
||||
}
|
||||
|
||||
public class JobStockDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int JobId { get; set; }
|
||||
public int MaterialId { get; set; }
|
||||
public string MaterialName { get; set; } = string.Empty;
|
||||
public int? StockItemId { get; set; }
|
||||
public decimal LengthInches { get; set; }
|
||||
public string LengthFormatted { get; set; } = string.Empty;
|
||||
public int Quantity { get; set; }
|
||||
public bool IsCustomLength { get; set; }
|
||||
public int Priority { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
}
|
||||
|
||||
public class CreateJobStockDto
|
||||
{
|
||||
public int MaterialId { get; set; }
|
||||
public int? StockItemId { get; set; }
|
||||
public string Length { get; set; } = string.Empty;
|
||||
public int Quantity { get; set; } = 1;
|
||||
public bool IsCustomLength { get; set; }
|
||||
public int Priority { get; set; } = 10;
|
||||
}
|
||||
|
||||
public class UpdateJobStockDto
|
||||
{
|
||||
public int? StockItemId { get; set; }
|
||||
public string? Length { get; set; }
|
||||
public int? Quantity { get; set; }
|
||||
public bool? IsCustomLength { get; set; }
|
||||
public int? Priority { get; set; }
|
||||
}
|
||||
|
||||
public class QuickCreateJobDto
|
||||
{
|
||||
public string? Customer { get; set; }
|
||||
}
|
||||
|
||||
public class PackJobRequestDto
|
||||
{
|
||||
public decimal? KerfOverride { get; set; }
|
||||
}
|
||||
53
CutList.Web/DTOs/MaterialDtos.cs
Normal file
53
CutList.Web/DTOs/MaterialDtos.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
namespace CutList.Web.DTOs;
|
||||
|
||||
public class MaterialDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Shape { get; set; } = string.Empty;
|
||||
public string Type { get; set; } = string.Empty;
|
||||
public string? Grade { get; set; }
|
||||
public string Size { get; set; } = string.Empty;
|
||||
public string? Description { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public MaterialDimensionsDto? Dimensions { get; set; }
|
||||
}
|
||||
|
||||
public class CreateMaterialDto
|
||||
{
|
||||
public string Shape { get; set; } = string.Empty;
|
||||
public string? Type { get; set; }
|
||||
public string? Grade { get; set; }
|
||||
public string? Size { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public Dictionary<string, decimal>? Dimensions { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateMaterialDto
|
||||
{
|
||||
public string? Type { get; set; }
|
||||
public string? Grade { get; set; }
|
||||
public string? Size { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public bool? RegenerateSize { get; set; }
|
||||
public Dictionary<string, decimal>? Dimensions { get; set; }
|
||||
}
|
||||
|
||||
public class BulkCreateResult
|
||||
{
|
||||
public int Created { get; set; }
|
||||
public int Skipped { get; set; }
|
||||
public List<string> Errors { get; set; } = new();
|
||||
}
|
||||
|
||||
public class MaterialDimensionsDto
|
||||
{
|
||||
public string DimensionType { get; set; } = string.Empty;
|
||||
public Dictionary<string, decimal> Values { get; set; } = new();
|
||||
}
|
||||
|
||||
public class MaterialSearchDto
|
||||
{
|
||||
public string Shape { get; set; } = string.Empty;
|
||||
public decimal TargetValue { get; set; }
|
||||
public decimal Tolerance { get; set; } = 0.1m;
|
||||
}
|
||||
66
CutList.Web/DTOs/PackResultDtos.cs
Normal file
66
CutList.Web/DTOs/PackResultDtos.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
namespace CutList.Web.DTOs;
|
||||
|
||||
public class PackResponseDto
|
||||
{
|
||||
public List<MaterialPackResultDto> Materials { get; set; } = new();
|
||||
public PackingSummaryDto Summary { get; set; } = new();
|
||||
}
|
||||
|
||||
public class MaterialPackResultDto
|
||||
{
|
||||
public int MaterialId { get; set; }
|
||||
public string MaterialName { get; set; } = string.Empty;
|
||||
public List<PackedBinDto> InStockBins { get; set; } = new();
|
||||
public List<PackedBinDto> ToBePurchasedBins { get; set; } = new();
|
||||
public List<PackedItemDto> ItemsNotPlaced { get; set; } = new();
|
||||
public MaterialPackingSummaryDto Summary { get; set; } = new();
|
||||
}
|
||||
|
||||
public class PackedBinDto
|
||||
{
|
||||
public double LengthInches { get; set; }
|
||||
public string LengthFormatted { get; set; } = string.Empty;
|
||||
public double UsedInches { get; set; }
|
||||
public string UsedFormatted { get; set; } = string.Empty;
|
||||
public double WasteInches { get; set; }
|
||||
public string WasteFormatted { get; set; } = string.Empty;
|
||||
public double Efficiency { get; set; }
|
||||
public List<PackedItemDto> Items { get; set; } = new();
|
||||
}
|
||||
|
||||
public class PackedItemDto
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public double LengthInches { get; set; }
|
||||
public string LengthFormatted { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class PackingSummaryDto
|
||||
{
|
||||
public int TotalInStockBins { get; set; }
|
||||
public int TotalToBePurchasedBins { get; set; }
|
||||
public int TotalPieces { get; set; }
|
||||
public double TotalMaterialInches { get; set; }
|
||||
public string TotalMaterialFormatted { get; set; } = string.Empty;
|
||||
public double TotalUsedInches { get; set; }
|
||||
public string TotalUsedFormatted { get; set; } = string.Empty;
|
||||
public double TotalWasteInches { get; set; }
|
||||
public string TotalWasteFormatted { get; set; } = string.Empty;
|
||||
public double Efficiency { get; set; }
|
||||
public int TotalItemsNotPlaced { get; set; }
|
||||
public List<MaterialPackingSummaryDto> MaterialSummaries { get; set; } = new();
|
||||
}
|
||||
|
||||
public class MaterialPackingSummaryDto
|
||||
{
|
||||
public int MaterialId { get; set; }
|
||||
public string MaterialName { get; set; } = string.Empty;
|
||||
public int InStockBins { get; set; }
|
||||
public int ToBePurchasedBins { get; set; }
|
||||
public int TotalPieces { get; set; }
|
||||
public double TotalMaterialInches { get; set; }
|
||||
public double TotalUsedInches { get; set; }
|
||||
public double TotalWasteInches { get; set; }
|
||||
public double Efficiency { get; set; }
|
||||
public int ItemsNotPlaced { get; set; }
|
||||
}
|
||||
45
CutList.Web/DTOs/PackingDtos.cs
Normal file
45
CutList.Web/DTOs/PackingDtos.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
namespace CutList.Web.DTOs;
|
||||
|
||||
public class StandalonePackRequestDto
|
||||
{
|
||||
public List<PartInputDto> Parts { get; set; } = new();
|
||||
public List<StockBinInputDto> StockBins { get; set; } = new();
|
||||
public decimal Kerf { get; set; } = 0.125m;
|
||||
public string Strategy { get; set; } = "advanced";
|
||||
}
|
||||
|
||||
public class PartInputDto
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Length { get; set; } = string.Empty;
|
||||
public int Quantity { get; set; } = 1;
|
||||
}
|
||||
|
||||
public class StockBinInputDto
|
||||
{
|
||||
public string Length { get; set; } = string.Empty;
|
||||
public int Quantity { get; set; } = -1;
|
||||
public int Priority { get; set; } = 25;
|
||||
}
|
||||
|
||||
public class ParseLengthRequestDto
|
||||
{
|
||||
public string Input { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class ParseLengthResponseDto
|
||||
{
|
||||
public double Inches { get; set; }
|
||||
public string Formatted { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class FormatLengthRequestDto
|
||||
{
|
||||
public double Inches { get; set; }
|
||||
}
|
||||
|
||||
public class FormatLengthResponseDto
|
||||
{
|
||||
public string Formatted { get; set; } = string.Empty;
|
||||
public double Inches { get; set; }
|
||||
}
|
||||
78
CutList.Web/DTOs/StockItemDtos.cs
Normal file
78
CutList.Web/DTOs/StockItemDtos.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
namespace CutList.Web.DTOs;
|
||||
|
||||
public class StockItemDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int MaterialId { get; set; }
|
||||
public string MaterialName { get; set; } = string.Empty;
|
||||
public decimal LengthInches { get; set; }
|
||||
public string LengthFormatted { get; set; } = string.Empty;
|
||||
public string? Name { get; set; }
|
||||
public int QuantityOnHand { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
|
||||
public class CreateStockItemDto
|
||||
{
|
||||
public int MaterialId { get; set; }
|
||||
public string Length { get; set; } = string.Empty;
|
||||
public string? Name { get; set; }
|
||||
public int QuantityOnHand { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateStockItemDto
|
||||
{
|
||||
public string? Length { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
}
|
||||
|
||||
public class StockTransactionDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int StockItemId { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
public string Type { get; set; } = string.Empty;
|
||||
public int? JobId { get; set; }
|
||||
public string? JobNumber { get; set; }
|
||||
public int? SupplierId { get; set; }
|
||||
public string? SupplierName { get; set; }
|
||||
public decimal? UnitPrice { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
}
|
||||
|
||||
public class AddStockDto
|
||||
{
|
||||
public int Quantity { get; set; }
|
||||
public int? SupplierId { get; set; }
|
||||
public decimal? UnitPrice { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
}
|
||||
|
||||
public class UseStockDto
|
||||
{
|
||||
public int Quantity { get; set; }
|
||||
public int? JobId { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
}
|
||||
|
||||
public class AdjustStockDto
|
||||
{
|
||||
public int NewQuantity { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
}
|
||||
|
||||
public class ScrapStockDto
|
||||
{
|
||||
public int Quantity { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
}
|
||||
|
||||
public class StockPricingDto
|
||||
{
|
||||
public decimal? AverageCost { get; set; }
|
||||
public decimal? LastPurchasePrice { get; set; }
|
||||
}
|
||||
57
CutList.Web/DTOs/SupplierDtos.cs
Normal file
57
CutList.Web/DTOs/SupplierDtos.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
namespace CutList.Web.DTOs;
|
||||
|
||||
public class SupplierDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string? ContactInfo { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
|
||||
public class CreateSupplierDto
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string? ContactInfo { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateSupplierDto
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public string? ContactInfo { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
}
|
||||
|
||||
public class OfferingDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int SupplierId { get; set; }
|
||||
public string? SupplierName { get; set; }
|
||||
public int StockItemId { get; set; }
|
||||
public string? MaterialName { get; set; }
|
||||
public decimal? LengthInches { get; set; }
|
||||
public string? LengthFormatted { get; set; }
|
||||
public string? PartNumber { get; set; }
|
||||
public string? SupplierDescription { get; set; }
|
||||
public decimal? Price { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
|
||||
public class CreateOfferingDto
|
||||
{
|
||||
public int StockItemId { get; set; }
|
||||
public string? PartNumber { get; set; }
|
||||
public string? SupplierDescription { get; set; }
|
||||
public decimal? Price { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateOfferingDto
|
||||
{
|
||||
public string? PartNumber { get; set; }
|
||||
public string? SupplierDescription { get; set; }
|
||||
public decimal? Price { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
}
|
||||
@@ -11,13 +11,16 @@ public class ApplicationDbContext : DbContext
|
||||
}
|
||||
|
||||
public DbSet<Material> Materials => Set<Material>();
|
||||
public DbSet<MaterialStockLength> MaterialStockLengths => Set<MaterialStockLength>();
|
||||
public DbSet<MaterialDimensions> MaterialDimensions => Set<MaterialDimensions>();
|
||||
public DbSet<Supplier> Suppliers => Set<Supplier>();
|
||||
public DbSet<StockItem> StockItems => Set<StockItem>();
|
||||
public DbSet<SupplierOffering> SupplierOfferings => Set<SupplierOffering>();
|
||||
public DbSet<StockTransaction> StockTransactions => Set<StockTransaction>();
|
||||
public DbSet<CuttingTool> CuttingTools => Set<CuttingTool>();
|
||||
public DbSet<Project> Projects => Set<Project>();
|
||||
public DbSet<ProjectPart> ProjectParts => Set<ProjectPart>();
|
||||
public DbSet<Job> Jobs => Set<Job>();
|
||||
public DbSet<JobPart> JobParts => Set<JobPart>();
|
||||
public DbSet<JobStock> JobStocks => Set<JobStock>();
|
||||
public DbSet<PurchaseItem> PurchaseItems => Set<PurchaseItem>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
@@ -27,25 +30,117 @@ public class ApplicationDbContext : DbContext
|
||||
modelBuilder.Entity<Material>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.Shape).HasMaxLength(50).IsRequired();
|
||||
entity.Property(e => e.Shape)
|
||||
.HasMaxLength(50)
|
||||
.IsRequired()
|
||||
.HasConversion(
|
||||
v => v.ToString(), // Enum to string (uses enum name)
|
||||
v => Enum.Parse<MaterialShape>(v)); // String to enum
|
||||
entity.Property(e => e.Size).HasMaxLength(100).IsRequired();
|
||||
entity.Property(e => e.Type)
|
||||
.HasMaxLength(20)
|
||||
.HasConversion(
|
||||
v => v.ToString(),
|
||||
v => Enum.Parse<MaterialType>(v));
|
||||
entity.Property(e => e.Grade).HasMaxLength(50);
|
||||
entity.Property(e => e.Description).HasMaxLength(255);
|
||||
entity.Property(e => e.CreatedAt).HasDefaultValueSql("GETUTCDATE()");
|
||||
});
|
||||
|
||||
// MaterialStockLength
|
||||
modelBuilder.Entity<MaterialStockLength>(entity =>
|
||||
// MaterialDimensions - TPC inheritance (each shape gets its own table, no base table)
|
||||
modelBuilder.Entity<MaterialDimensions>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.LengthInches).HasPrecision(10, 4);
|
||||
entity.Property(e => e.Notes).HasMaxLength(255);
|
||||
entity.UseTpcMappingStrategy();
|
||||
|
||||
// 1:1 relationship with Material
|
||||
entity.HasOne(e => e.Material)
|
||||
.WithMany(m => m.StockLengths)
|
||||
.HasForeignKey(e => e.MaterialId)
|
||||
.WithOne(m => m.Dimensions)
|
||||
.HasForeignKey<MaterialDimensions>(e => e.MaterialId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
entity.HasIndex(e => new { e.MaterialId, e.LengthInches }).IsUnique();
|
||||
// Configure each dimension type's properties
|
||||
modelBuilder.Entity<RoundBarDimensions>(entity =>
|
||||
{
|
||||
entity.ToTable("DimRoundBar");
|
||||
entity.Property(e => e.Diameter).HasPrecision(10, 4);
|
||||
entity.HasIndex(e => e.Diameter);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<RoundTubeDimensions>(entity =>
|
||||
{
|
||||
entity.ToTable("DimRoundTube");
|
||||
entity.Property(e => e.OuterDiameter).HasPrecision(10, 4);
|
||||
entity.Property(e => e.Wall).HasPrecision(10, 4);
|
||||
entity.HasIndex(e => e.OuterDiameter);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<FlatBarDimensions>(entity =>
|
||||
{
|
||||
entity.ToTable("DimFlatBar");
|
||||
entity.Property(e => e.Width).HasPrecision(10, 4);
|
||||
entity.Property(e => e.Thickness).HasPrecision(10, 4);
|
||||
entity.HasIndex(e => e.Width);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<SquareBarDimensions>(entity =>
|
||||
{
|
||||
entity.ToTable("DimSquareBar");
|
||||
entity.Property(e => e.Size).HasPrecision(10, 4);
|
||||
entity.HasIndex(e => e.Size);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<SquareTubeDimensions>(entity =>
|
||||
{
|
||||
entity.ToTable("DimSquareTube");
|
||||
entity.Property(e => e.Size).HasPrecision(10, 4);
|
||||
entity.Property(e => e.Wall).HasPrecision(10, 4);
|
||||
entity.HasIndex(e => e.Size);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<RectangularTubeDimensions>(entity =>
|
||||
{
|
||||
entity.ToTable("DimRectangularTube");
|
||||
entity.Property(e => e.Width).HasPrecision(10, 4);
|
||||
entity.Property(e => e.Height).HasPrecision(10, 4);
|
||||
entity.Property(e => e.Wall).HasPrecision(10, 4);
|
||||
entity.HasIndex(e => e.Width);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<AngleDimensions>(entity =>
|
||||
{
|
||||
entity.ToTable("DimAngle");
|
||||
entity.Property(e => e.Leg1).HasPrecision(10, 4);
|
||||
entity.Property(e => e.Leg2).HasPrecision(10, 4);
|
||||
entity.Property(e => e.Thickness).HasPrecision(10, 4);
|
||||
entity.HasIndex(e => e.Leg1);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<ChannelDimensions>(entity =>
|
||||
{
|
||||
entity.ToTable("DimChannel");
|
||||
entity.Property(e => e.Height).HasPrecision(10, 4);
|
||||
entity.Property(e => e.Flange).HasPrecision(10, 4);
|
||||
entity.Property(e => e.Web).HasPrecision(10, 4);
|
||||
entity.HasIndex(e => e.Height);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<IBeamDimensions>(entity =>
|
||||
{
|
||||
entity.ToTable("DimIBeam");
|
||||
entity.Property(e => e.Height).HasPrecision(10, 4);
|
||||
entity.Property(e => e.WeightPerFoot).HasPrecision(10, 4);
|
||||
entity.HasIndex(e => e.Height);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<PipeDimensions>(entity =>
|
||||
{
|
||||
entity.ToTable("DimPipe");
|
||||
entity.Property(e => e.NominalSize).HasPrecision(10, 4);
|
||||
entity.Property(e => e.Wall).HasPrecision(10, 4);
|
||||
entity.Property(e => e.Schedule).HasMaxLength(20);
|
||||
entity.HasIndex(e => e.NominalSize);
|
||||
});
|
||||
|
||||
// Supplier
|
||||
@@ -63,6 +158,7 @@ public class ApplicationDbContext : DbContext
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.LengthInches).HasPrecision(10, 4);
|
||||
entity.Property(e => e.Name).HasMaxLength(100);
|
||||
entity.Property(e => e.Notes).HasMaxLength(255);
|
||||
entity.Property(e => e.CreatedAt).HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
entity.HasOne(e => e.Material)
|
||||
@@ -73,6 +169,30 @@ public class ApplicationDbContext : DbContext
|
||||
entity.HasIndex(e => new { e.MaterialId, e.LengthInches }).IsUnique();
|
||||
});
|
||||
|
||||
// StockTransaction
|
||||
modelBuilder.Entity<StockTransaction>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.Notes).HasMaxLength(500);
|
||||
entity.Property(e => e.UnitPrice).HasPrecision(10, 2);
|
||||
entity.Property(e => e.CreatedAt).HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
entity.HasOne(e => e.StockItem)
|
||||
.WithMany(s => s.Transactions)
|
||||
.HasForeignKey(e => e.StockItemId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
entity.HasOne(e => e.Job)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.JobId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
entity.HasOne(e => e.Supplier)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.SupplierId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
});
|
||||
|
||||
// SupplierOffering
|
||||
modelBuilder.Entity<SupplierOffering>(entity =>
|
||||
{
|
||||
@@ -103,38 +223,93 @@ public class ApplicationDbContext : DbContext
|
||||
entity.Property(e => e.KerfInches).HasPrecision(6, 4);
|
||||
});
|
||||
|
||||
// Project
|
||||
modelBuilder.Entity<Project>(entity =>
|
||||
// Job
|
||||
modelBuilder.Entity<Job>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.Name).HasMaxLength(100).IsRequired();
|
||||
entity.Property(e => e.JobNumber).HasMaxLength(20).IsRequired();
|
||||
entity.Property(e => e.Name).HasMaxLength(100);
|
||||
entity.Property(e => e.Customer).HasMaxLength(100);
|
||||
entity.Property(e => e.CreatedAt).HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
entity.Property(e => e.OptimizationResultJson).HasColumnType("nvarchar(max)");
|
||||
|
||||
entity.HasIndex(e => e.JobNumber).IsUnique();
|
||||
|
||||
entity.HasOne(e => e.CuttingTool)
|
||||
.WithMany(t => t.Projects)
|
||||
.WithMany(t => t.Jobs)
|
||||
.HasForeignKey(e => e.CuttingToolId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
});
|
||||
|
||||
// ProjectPart
|
||||
modelBuilder.Entity<ProjectPart>(entity =>
|
||||
// JobPart
|
||||
modelBuilder.Entity<JobPart>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.Name).HasMaxLength(100);
|
||||
entity.Property(e => e.LengthInches).HasPrecision(10, 4);
|
||||
|
||||
entity.HasOne(e => e.Project)
|
||||
entity.HasOne(e => e.Job)
|
||||
.WithMany(p => p.Parts)
|
||||
.HasForeignKey(e => e.ProjectId)
|
||||
.HasForeignKey(e => e.JobId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
entity.HasOne(e => e.Material)
|
||||
.WithMany(m => m.ProjectParts)
|
||||
.WithMany(m => m.JobParts)
|
||||
.HasForeignKey(e => e.MaterialId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
});
|
||||
|
||||
// JobStock
|
||||
modelBuilder.Entity<JobStock>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.LengthInches).HasPrecision(10, 4);
|
||||
|
||||
entity.HasOne(e => e.Job)
|
||||
.WithMany(j => j.Stock)
|
||||
.HasForeignKey(e => e.JobId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
entity.HasOne(e => e.Material)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.MaterialId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
entity.HasOne(e => e.StockItem)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.StockItemId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
});
|
||||
|
||||
// PurchaseItem
|
||||
modelBuilder.Entity<PurchaseItem>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id);
|
||||
entity.Property(e => e.Notes).HasMaxLength(500);
|
||||
entity.Property(e => e.Status)
|
||||
.HasMaxLength(20)
|
||||
.HasConversion(
|
||||
v => v.ToString(),
|
||||
v => Enum.Parse<PurchaseItemStatus>(v));
|
||||
entity.Property(e => e.CreatedAt).HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
entity.HasOne(e => e.StockItem)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.StockItemId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
entity.HasOne(e => e.Supplier)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.SupplierId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
entity.HasOne(e => e.Job)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.JobId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
});
|
||||
|
||||
// Seed default cutting tools
|
||||
modelBuilder.Entity<CuttingTool>().HasData(
|
||||
new CuttingTool { Id = 1, Name = "Bandsaw", KerfInches = 0.0625m, IsDefault = true, IsActive = true },
|
||||
|
||||
@@ -8,5 +8,5 @@ public class CuttingTool
|
||||
public bool IsDefault { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
|
||||
public ICollection<Project> Projects { get; set; } = new List<Project>();
|
||||
public ICollection<Job> Jobs { get; set; } = new List<Job>();
|
||||
}
|
||||
|
||||
24
CutList.Web/Data/Entities/Job.cs
Normal file
24
CutList.Web/Data/Entities/Job.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace CutList.Web.Data.Entities;
|
||||
|
||||
public class Job
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string JobNumber { get; set; } = string.Empty;
|
||||
public string? Name { get; set; }
|
||||
public string? Customer { get; set; }
|
||||
public int? CuttingToolId { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
public DateTime? LockedAt { get; set; }
|
||||
public string? OptimizationResultJson { get; set; }
|
||||
public DateTime? OptimizedAt { get; set; }
|
||||
|
||||
public bool IsLocked => LockedAt.HasValue;
|
||||
|
||||
public CuttingTool? CuttingTool { get; set; }
|
||||
public ICollection<JobPart> Parts { get; set; } = new List<JobPart>();
|
||||
public ICollection<JobStock> Stock { get; set; } = new List<JobStock>();
|
||||
|
||||
public string DisplayName => string.IsNullOrWhiteSpace(Name) ? JobNumber : $"{JobNumber} - {Name}";
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
namespace CutList.Web.Data.Entities;
|
||||
|
||||
public class ProjectPart
|
||||
public class JobPart
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int ProjectId { get; set; }
|
||||
public int JobId { get; set; }
|
||||
public int MaterialId { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public decimal LengthInches { get; set; }
|
||||
public int Quantity { get; set; } = 1;
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
public Project Project { get; set; } = null!;
|
||||
public Job Job { get; set; } = null!;
|
||||
public Material Material { get; set; } = null!;
|
||||
}
|
||||
46
CutList.Web/Data/Entities/JobStock.cs
Normal file
46
CutList.Web/Data/Entities/JobStock.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
namespace CutList.Web.Data.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Represents stock allocated to a specific job.
|
||||
/// Can reference an existing StockItem with quantity override,
|
||||
/// or define a custom length just for this job.
|
||||
/// </summary>
|
||||
public class JobStock
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int JobId { get; set; }
|
||||
public int MaterialId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If set, references an existing stock item. Null for custom job-specific lengths.
|
||||
/// </summary>
|
||||
public int? StockItemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Length in inches. For stock items, copied from StockItem.LengthInches.
|
||||
/// For custom lengths, user-specified.
|
||||
/// </summary>
|
||||
public decimal LengthInches { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Quantity to use for this job. Can be less than or equal to available stock.
|
||||
/// For custom lengths, represents unlimited available.
|
||||
/// </summary>
|
||||
public int Quantity { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// True if this is a custom length just for this job (not from inventory).
|
||||
/// </summary>
|
||||
public bool IsCustomLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Priority for bin packing. Lower values are used first.
|
||||
/// </summary>
|
||||
public int Priority { get; set; } = 10;
|
||||
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
public Job Job { get; set; } = null!;
|
||||
public Material Material { get; set; } = null!;
|
||||
public StockItem? StockItem { get; set; }
|
||||
}
|
||||
@@ -3,16 +3,36 @@ namespace CutList.Web.Data.Entities;
|
||||
public class Material
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Shape { get; set; } = string.Empty;
|
||||
public MaterialShape Shape { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Material type (Steel, Aluminum, Stainless, etc.)
|
||||
/// </summary>
|
||||
public MaterialType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Grade or specification (e.g., "A36", "Hot Roll", "304", "6061-T6")
|
||||
/// </summary>
|
||||
public string? Grade { get; set; }
|
||||
|
||||
public string Size { get; set; } = string.Empty;
|
||||
public string? Description { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
|
||||
public ICollection<StockItem> StockItems { get; set; } = new List<StockItem>();
|
||||
public ICollection<MaterialStockLength> StockLengths { get; set; } = new List<MaterialStockLength>();
|
||||
public ICollection<ProjectPart> ProjectParts { get; set; } = new List<ProjectPart>();
|
||||
/// <summary>
|
||||
/// Sort order based on primary dimension (stored as thousandths of an inch for numeric sorting).
|
||||
/// </summary>
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
public string DisplayName => $"{Shape} - {Size}";
|
||||
public ICollection<StockItem> StockItems { get; set; } = new List<StockItem>();
|
||||
public ICollection<JobPart> JobParts { get; set; } = new List<JobPart>();
|
||||
|
||||
/// <summary>
|
||||
/// Optional parsed dimensions for decimal-based searching.
|
||||
/// </summary>
|
||||
public MaterialDimensions? Dimensions { get; set; }
|
||||
|
||||
public string DisplayName => $"{Shape.GetDisplayName()} - {Size}";
|
||||
}
|
||||
|
||||
192
CutList.Web/Data/Entities/MaterialDimensions.cs
Normal file
192
CutList.Web/Data/Entities/MaterialDimensions.cs
Normal file
@@ -0,0 +1,192 @@
|
||||
namespace CutList.Web.Data.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for material dimensions. Each shape has its own derived class with specific properties.
|
||||
/// </summary>
|
||||
public abstract class MaterialDimensions
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int MaterialId { get; set; }
|
||||
public Material Material { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Generates a display string for the size based on the dimensions.
|
||||
/// </summary>
|
||||
public abstract string GenerateSizeString();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the primary dimension value for sorting (in thousandths of an inch).
|
||||
/// </summary>
|
||||
public abstract int GetSortOrder();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dimensions for Round Bar: solid round stock.
|
||||
/// </summary>
|
||||
public class RoundBarDimensions : MaterialDimensions
|
||||
{
|
||||
public decimal Diameter { get; set; }
|
||||
|
||||
public override string GenerateSizeString() =>
|
||||
FormatDimension(Diameter);
|
||||
|
||||
public override int GetSortOrder() => (int)(Diameter * 1000);
|
||||
|
||||
private static string FormatDimension(decimal value) =>
|
||||
CutList.Core.Formatting.ArchUnits.FormatFromInches((double)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dimensions for Round Tube: hollow round stock.
|
||||
/// </summary>
|
||||
public class RoundTubeDimensions : MaterialDimensions
|
||||
{
|
||||
public decimal OuterDiameter { get; set; }
|
||||
public decimal Wall { get; set; }
|
||||
|
||||
public override string GenerateSizeString() =>
|
||||
$"{FormatDimension(OuterDiameter)} OD x {FormatDimension(Wall)} wall";
|
||||
|
||||
public override int GetSortOrder() => (int)(OuterDiameter * 1000);
|
||||
|
||||
private static string FormatDimension(decimal value) =>
|
||||
CutList.Core.Formatting.ArchUnits.FormatFromInches((double)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dimensions for Flat Bar: rectangular solid stock.
|
||||
/// </summary>
|
||||
public class FlatBarDimensions : MaterialDimensions
|
||||
{
|
||||
public decimal Width { get; set; }
|
||||
public decimal Thickness { get; set; }
|
||||
|
||||
public override string GenerateSizeString() =>
|
||||
$"{FormatDimension(Width)} x {FormatDimension(Thickness)}";
|
||||
|
||||
public override int GetSortOrder() => (int)(Width * 1000);
|
||||
|
||||
private static string FormatDimension(decimal value) =>
|
||||
CutList.Core.Formatting.ArchUnits.FormatFromInches((double)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dimensions for Square Bar: solid square stock.
|
||||
/// </summary>
|
||||
public class SquareBarDimensions : MaterialDimensions
|
||||
{
|
||||
public decimal Size { get; set; }
|
||||
|
||||
public override string GenerateSizeString() =>
|
||||
FormatDimension(Size);
|
||||
|
||||
public override int GetSortOrder() => (int)(Size * 1000);
|
||||
|
||||
private static string FormatDimension(decimal value) =>
|
||||
CutList.Core.Formatting.ArchUnits.FormatFromInches((double)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dimensions for Square Tube: hollow square stock.
|
||||
/// </summary>
|
||||
public class SquareTubeDimensions : MaterialDimensions
|
||||
{
|
||||
public decimal Size { get; set; }
|
||||
public decimal Wall { get; set; }
|
||||
|
||||
public override string GenerateSizeString() =>
|
||||
$"{FormatDimension(Size)} x {FormatDimension(Wall)} wall";
|
||||
|
||||
public override int GetSortOrder() => (int)(Size * 1000);
|
||||
|
||||
private static string FormatDimension(decimal value) =>
|
||||
CutList.Core.Formatting.ArchUnits.FormatFromInches((double)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dimensions for Rectangular Tube: hollow rectangular stock.
|
||||
/// </summary>
|
||||
public class RectangularTubeDimensions : MaterialDimensions
|
||||
{
|
||||
public decimal Width { get; set; }
|
||||
public decimal Height { get; set; }
|
||||
public decimal Wall { get; set; }
|
||||
|
||||
public override string GenerateSizeString() =>
|
||||
$"{FormatDimension(Width)} x {FormatDimension(Height)} x {FormatDimension(Wall)} wall";
|
||||
|
||||
public override int GetSortOrder() => (int)(Width * 1000);
|
||||
|
||||
private static string FormatDimension(decimal value) =>
|
||||
CutList.Core.Formatting.ArchUnits.FormatFromInches((double)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dimensions for Angle: L-shaped stock.
|
||||
/// </summary>
|
||||
public class AngleDimensions : MaterialDimensions
|
||||
{
|
||||
public decimal Leg1 { get; set; }
|
||||
public decimal Leg2 { get; set; }
|
||||
public decimal Thickness { get; set; }
|
||||
|
||||
public override string GenerateSizeString() =>
|
||||
$"{FormatDimension(Leg1)} x {FormatDimension(Leg2)} x {FormatDimension(Thickness)}";
|
||||
|
||||
public override int GetSortOrder() => (int)(Leg1 * 1000);
|
||||
|
||||
private static string FormatDimension(decimal value) =>
|
||||
CutList.Core.Formatting.ArchUnits.FormatFromInches((double)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dimensions for Channel: C-shaped stock.
|
||||
/// </summary>
|
||||
public class ChannelDimensions : MaterialDimensions
|
||||
{
|
||||
public decimal Height { get; set; }
|
||||
public decimal Flange { get; set; }
|
||||
public decimal Web { get; set; }
|
||||
|
||||
public override string GenerateSizeString() =>
|
||||
$"{FormatDimension(Height)} x {FormatDimension(Flange)} x {FormatDimension(Web)}";
|
||||
|
||||
public override int GetSortOrder() => (int)(Height * 1000);
|
||||
|
||||
private static string FormatDimension(decimal value) =>
|
||||
CutList.Core.Formatting.ArchUnits.FormatFromInches((double)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dimensions for I-Beam: wide flange beam.
|
||||
/// </summary>
|
||||
public class IBeamDimensions : MaterialDimensions
|
||||
{
|
||||
public decimal Height { get; set; }
|
||||
public decimal WeightPerFoot { get; set; }
|
||||
|
||||
public override string GenerateSizeString() =>
|
||||
$"W{Height:0.##} x {WeightPerFoot:0.##}";
|
||||
|
||||
public override int GetSortOrder() => (int)(Height * 1000);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dimensions for Pipe: nominal pipe size.
|
||||
/// </summary>
|
||||
public class PipeDimensions : MaterialDimensions
|
||||
{
|
||||
public decimal NominalSize { get; set; }
|
||||
public decimal? Wall { get; set; }
|
||||
public string? Schedule { get; set; }
|
||||
|
||||
public override string GenerateSizeString() =>
|
||||
!string.IsNullOrEmpty(Schedule)
|
||||
? $"{FormatDimension(NominalSize)} NPS Sch {Schedule}"
|
||||
: $"{FormatDimension(NominalSize)} NPS x {FormatDimension(Wall ?? 0)} wall";
|
||||
|
||||
public override int GetSortOrder() => (int)(NominalSize * 1000);
|
||||
|
||||
private static string FormatDimension(decimal value) =>
|
||||
CutList.Core.Formatting.ArchUnits.FormatFromInches((double)value);
|
||||
}
|
||||
89
CutList.Web/Data/Entities/MaterialShape.cs
Normal file
89
CutList.Web/Data/Entities/MaterialShape.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
namespace CutList.Web.Data.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Enumeration of supported material shapes.
|
||||
/// </summary>
|
||||
public enum MaterialShape
|
||||
{
|
||||
RoundBar,
|
||||
RoundTube,
|
||||
FlatBar,
|
||||
SquareBar,
|
||||
SquareTube,
|
||||
RectangularTube,
|
||||
Angle,
|
||||
Channel,
|
||||
IBeam,
|
||||
Pipe
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for MaterialShape enum.
|
||||
/// </summary>
|
||||
public static class MaterialShapeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the display name for a material shape.
|
||||
/// </summary>
|
||||
public static string GetDisplayName(this MaterialShape shape) => shape switch
|
||||
{
|
||||
MaterialShape.RoundBar => "Round Bar",
|
||||
MaterialShape.RoundTube => "Round Tube",
|
||||
MaterialShape.FlatBar => "Flat Bar",
|
||||
MaterialShape.SquareBar => "Square Bar",
|
||||
MaterialShape.SquareTube => "Square Tube",
|
||||
MaterialShape.RectangularTube => "Rectangular Tube",
|
||||
MaterialShape.Angle => "Angle",
|
||||
MaterialShape.Channel => "Channel",
|
||||
MaterialShape.IBeam => "I-Beam",
|
||||
MaterialShape.Pipe => "Pipe",
|
||||
_ => shape.ToString()
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Parses a display name or enum value string to a MaterialShape.
|
||||
/// </summary>
|
||||
public static MaterialShape? ParseShape(string? input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
return null;
|
||||
|
||||
// Try exact enum parse first
|
||||
if (Enum.TryParse<MaterialShape>(input, ignoreCase: true, out var result))
|
||||
return result;
|
||||
|
||||
// Try display name matching
|
||||
return input.Trim().ToLowerInvariant() switch
|
||||
{
|
||||
"round bar" => MaterialShape.RoundBar,
|
||||
"round tube" => MaterialShape.RoundTube,
|
||||
"flat bar" => MaterialShape.FlatBar,
|
||||
"square bar" => MaterialShape.SquareBar,
|
||||
"square tube" => MaterialShape.SquareTube,
|
||||
"rectangular tube" or "rect tube" => MaterialShape.RectangularTube,
|
||||
"angle" => MaterialShape.Angle,
|
||||
"channel" => MaterialShape.Channel,
|
||||
"i-beam" or "ibeam" or "i beam" => MaterialShape.IBeam,
|
||||
"pipe" => MaterialShape.Pipe,
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the dimension field names used by a given shape.
|
||||
/// </summary>
|
||||
public static string[] GetDimensionFields(this MaterialShape shape) => shape switch
|
||||
{
|
||||
MaterialShape.RoundBar => new[] { "Diameter" },
|
||||
MaterialShape.RoundTube => new[] { "OuterDiameter", "Wall" },
|
||||
MaterialShape.FlatBar => new[] { "Width", "Thickness" },
|
||||
MaterialShape.SquareBar => new[] { "Size" },
|
||||
MaterialShape.SquareTube => new[] { "Size", "Wall" },
|
||||
MaterialShape.RectangularTube => new[] { "Width", "Height", "Wall" },
|
||||
MaterialShape.Angle => new[] { "Leg1", "Leg2", "Thickness" },
|
||||
MaterialShape.Channel => new[] { "Height", "Flange", "Web" },
|
||||
MaterialShape.IBeam => new[] { "Height", "WeightPerFoot" },
|
||||
MaterialShape.Pipe => new[] { "NominalSize", "Wall", "Schedule" },
|
||||
_ => Array.Empty<string>()
|
||||
};
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
namespace CutList.Web.Data.Entities;
|
||||
|
||||
public class MaterialStockLength
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int MaterialId { get; set; }
|
||||
public decimal LengthInches { get; set; }
|
||||
public int Quantity { get; set; } = 0;
|
||||
public string? Notes { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
|
||||
public Material Material { get; set; } = null!;
|
||||
}
|
||||
13
CutList.Web/Data/Entities/MaterialType.cs
Normal file
13
CutList.Web/Data/Entities/MaterialType.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace CutList.Web.Data.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Type of material (metal).
|
||||
/// </summary>
|
||||
public enum MaterialType
|
||||
{
|
||||
Steel,
|
||||
Aluminum,
|
||||
Stainless,
|
||||
Brass,
|
||||
Copper
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
namespace CutList.Web.Data.Entities;
|
||||
|
||||
public class Project
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string? Customer { get; set; }
|
||||
public int? CuttingToolId { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
|
||||
public CuttingTool? CuttingTool { get; set; }
|
||||
public ICollection<ProjectPart> Parts { get; set; } = new List<ProjectPart>();
|
||||
}
|
||||
25
CutList.Web/Data/Entities/PurchaseItem.cs
Normal file
25
CutList.Web/Data/Entities/PurchaseItem.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
namespace CutList.Web.Data.Entities;
|
||||
|
||||
public enum PurchaseItemStatus
|
||||
{
|
||||
Pending,
|
||||
Ordered,
|
||||
Received
|
||||
}
|
||||
|
||||
public class PurchaseItem
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int StockItemId { get; set; }
|
||||
public int? SupplierId { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
public int? JobId { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public PurchaseItemStatus Status { get; set; } = PurchaseItemStatus.Pending;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
|
||||
public StockItem StockItem { get; set; } = null!;
|
||||
public Supplier? Supplier { get; set; }
|
||||
public Job? Job { get; set; }
|
||||
}
|
||||
@@ -6,10 +6,13 @@ public class StockItem
|
||||
public int MaterialId { get; set; }
|
||||
public decimal LengthInches { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public int QuantityOnHand { get; set; } = 0;
|
||||
public string? Notes { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
|
||||
public Material Material { get; set; } = null!;
|
||||
public ICollection<SupplierOffering> SupplierOfferings { get; set; } = new List<SupplierOffering>();
|
||||
public ICollection<StockTransaction> Transactions { get; set; } = new List<StockTransaction>();
|
||||
}
|
||||
|
||||
27
CutList.Web/Data/Entities/StockTransaction.cs
Normal file
27
CutList.Web/Data/Entities/StockTransaction.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
namespace CutList.Web.Data.Entities;
|
||||
|
||||
public class StockTransaction
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int StockItemId { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
public StockTransactionType Type { get; set; }
|
||||
public int? JobId { get; set; }
|
||||
public int? SupplierId { get; set; }
|
||||
public decimal? UnitPrice { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public StockItem StockItem { get; set; } = null!;
|
||||
public Job? Job { get; set; }
|
||||
public Supplier? Supplier { get; set; }
|
||||
}
|
||||
|
||||
public enum StockTransactionType
|
||||
{
|
||||
Received,
|
||||
Used,
|
||||
Adjustment,
|
||||
Scrapped,
|
||||
Returned
|
||||
}
|
||||
42
CutList.Web/Data/SeedData/alro-catalog.json
Normal file
42
CutList.Web/Data/SeedData/alro-catalog.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"exportedAt": "2026-02-16T17:09:52.843008+00:00",
|
||||
"suppliers": [
|
||||
{
|
||||
"name": "Alro Steel"
|
||||
}
|
||||
],
|
||||
"cuttingTools": [
|
||||
{
|
||||
"name": "Bandsaw",
|
||||
"kerfInches": 0.0625,
|
||||
"isDefault": true
|
||||
},
|
||||
{
|
||||
"name": "Chop Saw",
|
||||
"kerfInches": 0.125,
|
||||
"isDefault": false
|
||||
},
|
||||
{
|
||||
"name": "Cold Cut Saw",
|
||||
"kerfInches": 0.0625,
|
||||
"isDefault": false
|
||||
},
|
||||
{
|
||||
"name": "Hacksaw",
|
||||
"kerfInches": 0.0625,
|
||||
"isDefault": false
|
||||
}
|
||||
],
|
||||
"materials": {
|
||||
"angles": [],
|
||||
"channels": [],
|
||||
"flatBars": [],
|
||||
"iBeams": [],
|
||||
"pipes": [],
|
||||
"rectangularTubes": [],
|
||||
"roundBars": [],
|
||||
"roundTubes": [],
|
||||
"squareBars": [],
|
||||
"squareTubes": []
|
||||
}
|
||||
}
|
||||
14708
CutList.Web/Data/SeedData/oneals-catalog.json
Normal file
14708
CutList.Web/Data/SeedData/oneals-catalog.json
Normal file
File diff suppressed because it is too large
Load Diff
452
CutList.Web/Migrations/20260204214947_RenameProjectToJob.Designer.cs
generated
Normal file
452
CutList.Web/Migrations/20260204214947_RenameProjectToJob.Designer.cs
generated
Normal file
@@ -0,0 +1,452 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CutList.Web.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20260204214947_RenameProjectToJob")]
|
||||
partial class RenameProjectToJob
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsDefault")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("KerfInches")
|
||||
.HasPrecision(6, 4)
|
||||
.HasColumnType("decimal(6,4)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CuttingTools");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
IsActive = true,
|
||||
IsDefault = true,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Bandsaw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.125m,
|
||||
Name = "Chop Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Cold Cut Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Hacksaw"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Customer")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int?>("CuttingToolId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CuttingToolId");
|
||||
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.ToTable("JobParts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Shape")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Size")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Materials");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialStockLength", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId", "LengthInches")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("MaterialStockLengths");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId", "LengthInches")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ContactInfo")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Suppliers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("PartNumber")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<decimal?>("Price")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("SupplierDescription")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId", "StockItemId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("SupplierOfferings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.CuttingTool", "CuttingTool")
|
||||
.WithMany("Jobs")
|
||||
.HasForeignKey("CuttingToolId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("CuttingTool");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Parts")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("JobParts")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialStockLength", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("StockLengths")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("StockItems")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("SupplierOfferings")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany("Offerings")
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Navigation("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Navigation("Parts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Navigation("JobParts");
|
||||
|
||||
b.Navigation("StockItems");
|
||||
|
||||
b.Navigation("StockLengths");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Navigation("SupplierOfferings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Navigation("Offerings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
169
CutList.Web/Migrations/20260204214947_RenameProjectToJob.cs
Normal file
169
CutList.Web/Migrations/20260204214947_RenameProjectToJob.cs
Normal file
@@ -0,0 +1,169 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class RenameProjectToJob : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "ProjectParts");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Projects");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Jobs",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
Name = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
||||
Customer = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
|
||||
CuttingToolId = table.Column<int>(type: "int", nullable: true),
|
||||
Notes = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false, defaultValueSql: "GETUTCDATE()"),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Jobs", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Jobs_CuttingTools_CuttingToolId",
|
||||
column: x => x.CuttingToolId,
|
||||
principalTable: "CuttingTools",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "JobParts",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
JobId = table.Column<int>(type: "int", nullable: false),
|
||||
MaterialId = table.Column<int>(type: "int", nullable: false),
|
||||
Name = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
||||
LengthInches = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false),
|
||||
Quantity = table.Column<int>(type: "int", nullable: false),
|
||||
SortOrder = table.Column<int>(type: "int", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_JobParts", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_JobParts_Jobs_JobId",
|
||||
column: x => x.JobId,
|
||||
principalTable: "Jobs",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_JobParts_Materials_MaterialId",
|
||||
column: x => x.MaterialId,
|
||||
principalTable: "Materials",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_JobParts_JobId",
|
||||
table: "JobParts",
|
||||
column: "JobId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_JobParts_MaterialId",
|
||||
table: "JobParts",
|
||||
column: "MaterialId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Jobs_CuttingToolId",
|
||||
table: "Jobs",
|
||||
column: "CuttingToolId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "JobParts");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Jobs");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Projects",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
CuttingToolId = table.Column<int>(type: "int", nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false, defaultValueSql: "GETUTCDATE()"),
|
||||
Customer = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
|
||||
Name = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
||||
Notes = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Projects", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Projects_CuttingTools_CuttingToolId",
|
||||
column: x => x.CuttingToolId,
|
||||
principalTable: "CuttingTools",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ProjectParts",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
MaterialId = table.Column<int>(type: "int", nullable: false),
|
||||
ProjectId = table.Column<int>(type: "int", nullable: false),
|
||||
LengthInches = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false),
|
||||
Name = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
||||
Quantity = table.Column<int>(type: "int", nullable: false),
|
||||
SortOrder = table.Column<int>(type: "int", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ProjectParts", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ProjectParts_Materials_MaterialId",
|
||||
column: x => x.MaterialId,
|
||||
principalTable: "Materials",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_ProjectParts_Projects_ProjectId",
|
||||
column: x => x.ProjectId,
|
||||
principalTable: "Projects",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ProjectParts_MaterialId",
|
||||
table: "ProjectParts",
|
||||
column: "MaterialId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ProjectParts_ProjectId",
|
||||
table: "ProjectParts",
|
||||
column: "ProjectId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Projects_CuttingToolId",
|
||||
table: "Projects",
|
||||
column: "CuttingToolId");
|
||||
}
|
||||
}
|
||||
}
|
||||
471
CutList.Web/Migrations/20260204220547_MergeStockAndAddTransactions.Designer.cs
generated
Normal file
471
CutList.Web/Migrations/20260204220547_MergeStockAndAddTransactions.Designer.cs
generated
Normal file
@@ -0,0 +1,471 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CutList.Web.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20260204220547_MergeStockAndAddTransactions")]
|
||||
partial class MergeStockAndAddTransactions
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsDefault")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("KerfInches")
|
||||
.HasPrecision(6, 4)
|
||||
.HasColumnType("decimal(6,4)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CuttingTools");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
IsActive = true,
|
||||
IsDefault = true,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Bandsaw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.125m,
|
||||
Name = "Chop Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Cold Cut Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Hacksaw"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Customer")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int?>("CuttingToolId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CuttingToolId");
|
||||
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.ToTable("JobParts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Shape")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Size")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Materials");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("QuantityOnHand")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId", "LengthInches")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<int?>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.ToTable("StockTransactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ContactInfo")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Suppliers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("PartNumber")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<decimal?>("Price")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("SupplierDescription")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId", "StockItemId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("SupplierOfferings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.CuttingTool", "CuttingTool")
|
||||
.WithMany("Jobs")
|
||||
.HasForeignKey("CuttingToolId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("CuttingTool");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Parts")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("JobParts")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("StockItems")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany()
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("Transactions")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("SupplierOfferings")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany("Offerings")
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Navigation("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Navigation("Parts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Navigation("JobParts");
|
||||
|
||||
b.Navigation("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Navigation("SupplierOfferings");
|
||||
|
||||
b.Navigation("Transactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Navigation("Offerings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class MergeStockAndAddTransactions : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// First add the new columns to StockItems
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Notes",
|
||||
table: "StockItems",
|
||||
type: "nvarchar(255)",
|
||||
maxLength: 255,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "QuantityOnHand",
|
||||
table: "StockItems",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
// Migrate data from MaterialStockLengths to StockItems
|
||||
// Update existing StockItems with matching MaterialId + LengthInches
|
||||
migrationBuilder.Sql(@"
|
||||
UPDATE si
|
||||
SET si.QuantityOnHand = msl.Quantity,
|
||||
si.Notes = COALESCE(si.Notes, msl.Notes)
|
||||
FROM StockItems si
|
||||
INNER JOIN MaterialStockLengths msl
|
||||
ON si.MaterialId = msl.MaterialId
|
||||
AND si.LengthInches = msl.LengthInches
|
||||
WHERE msl.IsActive = 1
|
||||
");
|
||||
|
||||
// Insert MaterialStockLengths that don't have a matching StockItem
|
||||
migrationBuilder.Sql(@"
|
||||
INSERT INTO StockItems (MaterialId, LengthInches, QuantityOnHand, Notes, IsActive, CreatedAt)
|
||||
SELECT msl.MaterialId, msl.LengthInches, msl.Quantity, msl.Notes, 1, GETUTCDATE()
|
||||
FROM MaterialStockLengths msl
|
||||
WHERE msl.IsActive = 1
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM StockItems si
|
||||
WHERE si.MaterialId = msl.MaterialId
|
||||
AND si.LengthInches = msl.LengthInches
|
||||
)
|
||||
");
|
||||
|
||||
// Now drop the old table
|
||||
migrationBuilder.DropTable(
|
||||
name: "MaterialStockLengths");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "StockTransactions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
StockItemId = table.Column<int>(type: "int", nullable: false),
|
||||
Quantity = table.Column<int>(type: "int", nullable: false),
|
||||
Type = table.Column<int>(type: "int", nullable: false),
|
||||
JobId = table.Column<int>(type: "int", nullable: true),
|
||||
Notes = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false, defaultValueSql: "GETUTCDATE()")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_StockTransactions", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_StockTransactions_Jobs_JobId",
|
||||
column: x => x.JobId,
|
||||
principalTable: "Jobs",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_StockTransactions_StockItems_StockItemId",
|
||||
column: x => x.StockItemId,
|
||||
principalTable: "StockItems",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_StockTransactions_JobId",
|
||||
table: "StockTransactions",
|
||||
column: "JobId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_StockTransactions_StockItemId",
|
||||
table: "StockTransactions",
|
||||
column: "StockItemId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "StockTransactions");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Notes",
|
||||
table: "StockItems");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "QuantityOnHand",
|
||||
table: "StockItems");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "MaterialStockLengths",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
MaterialId = table.Column<int>(type: "int", nullable: false),
|
||||
IsActive = table.Column<bool>(type: "bit", nullable: false),
|
||||
LengthInches = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false),
|
||||
Notes = table.Column<string>(type: "nvarchar(255)", maxLength: 255, nullable: true),
|
||||
Quantity = table.Column<int>(type: "int", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_MaterialStockLengths", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_MaterialStockLengths_Materials_MaterialId",
|
||||
column: x => x.MaterialId,
|
||||
principalTable: "Materials",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MaterialStockLengths_MaterialId_LengthInches",
|
||||
table: "MaterialStockLengths",
|
||||
columns: new[] { "MaterialId", "LengthInches" },
|
||||
unique: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
487
CutList.Web/Migrations/20260204221152_AddPriceTrackingToTransactions.Designer.cs
generated
Normal file
487
CutList.Web/Migrations/20260204221152_AddPriceTrackingToTransactions.Designer.cs
generated
Normal file
@@ -0,0 +1,487 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CutList.Web.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20260204221152_AddPriceTrackingToTransactions")]
|
||||
partial class AddPriceTrackingToTransactions
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsDefault")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("KerfInches")
|
||||
.HasPrecision(6, 4)
|
||||
.HasColumnType("decimal(6,4)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CuttingTools");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
IsActive = true,
|
||||
IsDefault = true,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Bandsaw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.125m,
|
||||
Name = "Chop Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Cold Cut Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Hacksaw"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Customer")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int?>("CuttingToolId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CuttingToolId");
|
||||
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.ToTable("JobParts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Shape")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Size")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Materials");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("QuantityOnHand")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId", "LengthInches")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<int?>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal?>("UnitPrice")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId");
|
||||
|
||||
b.ToTable("StockTransactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ContactInfo")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Suppliers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("PartNumber")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<decimal?>("Price")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("SupplierDescription")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId", "StockItemId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("SupplierOfferings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.CuttingTool", "CuttingTool")
|
||||
.WithMany("Jobs")
|
||||
.HasForeignKey("CuttingToolId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("CuttingTool");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Parts")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("JobParts")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("StockItems")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany()
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("Transactions")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany()
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("SupplierOfferings")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany("Offerings")
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Navigation("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Navigation("Parts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Navigation("JobParts");
|
||||
|
||||
b.Navigation("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Navigation("SupplierOfferings");
|
||||
|
||||
b.Navigation("Transactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Navigation("Offerings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddPriceTrackingToTransactions : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "SupplierId",
|
||||
table: "StockTransactions",
|
||||
type: "int",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<decimal>(
|
||||
name: "UnitPrice",
|
||||
table: "StockTransactions",
|
||||
type: "decimal(10,2)",
|
||||
precision: 10,
|
||||
scale: 2,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_StockTransactions_SupplierId",
|
||||
table: "StockTransactions",
|
||||
column: "SupplierId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_StockTransactions_Suppliers_SupplierId",
|
||||
table: "StockTransactions",
|
||||
column: "SupplierId",
|
||||
principalTable: "Suppliers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_StockTransactions_Suppliers_SupplierId",
|
||||
table: "StockTransactions");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_StockTransactions_SupplierId",
|
||||
table: "StockTransactions");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SupplierId",
|
||||
table: "StockTransactions");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "UnitPrice",
|
||||
table: "StockTransactions");
|
||||
}
|
||||
}
|
||||
}
|
||||
494
CutList.Web/Migrations/20260204222017_AddJobNumber.Designer.cs
generated
Normal file
494
CutList.Web/Migrations/20260204222017_AddJobNumber.Designer.cs
generated
Normal file
@@ -0,0 +1,494 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CutList.Web.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20260204222017_AddJobNumber")]
|
||||
partial class AddJobNumber
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsDefault")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("KerfInches")
|
||||
.HasPrecision(6, 4)
|
||||
.HasColumnType("decimal(6,4)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CuttingTools");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
IsActive = true,
|
||||
IsDefault = true,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Bandsaw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.125m,
|
||||
Name = "Chop Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Cold Cut Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Hacksaw"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Customer")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int?>("CuttingToolId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("JobNumber")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CuttingToolId");
|
||||
|
||||
b.HasIndex("JobNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.ToTable("JobParts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Shape")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Size")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Materials");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("QuantityOnHand")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId", "LengthInches")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<int?>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal?>("UnitPrice")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId");
|
||||
|
||||
b.ToTable("StockTransactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ContactInfo")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Suppliers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("PartNumber")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<decimal?>("Price")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("SupplierDescription")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId", "StockItemId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("SupplierOfferings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.CuttingTool", "CuttingTool")
|
||||
.WithMany("Jobs")
|
||||
.HasForeignKey("CuttingToolId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("CuttingTool");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Parts")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("JobParts")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("StockItems")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany()
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("Transactions")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany()
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("SupplierOfferings")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany("Offerings")
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Navigation("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Navigation("Parts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Navigation("JobParts");
|
||||
|
||||
b.Navigation("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Navigation("SupplierOfferings");
|
||||
|
||||
b.Navigation("Transactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Navigation("Offerings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
74
CutList.Web/Migrations/20260204222017_AddJobNumber.cs
Normal file
74
CutList.Web/Migrations/20260204222017_AddJobNumber.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddJobNumber : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Name",
|
||||
table: "Jobs",
|
||||
type: "nvarchar(100)",
|
||||
maxLength: 100,
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(100)",
|
||||
oldMaxLength: 100);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "JobNumber",
|
||||
table: "Jobs",
|
||||
type: "nvarchar(20)",
|
||||
maxLength: 20,
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
|
||||
// Generate job numbers for existing jobs
|
||||
migrationBuilder.Sql(@"
|
||||
WITH NumberedJobs AS (
|
||||
SELECT Id, ROW_NUMBER() OVER (ORDER BY Id) AS RowNum
|
||||
FROM Jobs
|
||||
)
|
||||
UPDATE j
|
||||
SET j.JobNumber = 'JOB-' + RIGHT('00000' + CAST(nj.RowNum AS VARCHAR(5)), 5)
|
||||
FROM Jobs j
|
||||
INNER JOIN NumberedJobs nj ON j.Id = nj.Id
|
||||
");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Jobs_JobNumber",
|
||||
table: "Jobs",
|
||||
column: "JobNumber",
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Jobs_JobNumber",
|
||||
table: "Jobs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "JobNumber",
|
||||
table: "Jobs");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Name",
|
||||
table: "Jobs",
|
||||
type: "nvarchar(100)",
|
||||
maxLength: 100,
|
||||
nullable: false,
|
||||
defaultValue: "",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(100)",
|
||||
oldMaxLength: 100,
|
||||
oldNullable: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
566
CutList.Web/Migrations/20260204223202_AddJobStock.Designer.cs
generated
Normal file
566
CutList.Web/Migrations/20260204223202_AddJobStock.Designer.cs
generated
Normal file
@@ -0,0 +1,566 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CutList.Web.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20260204223202_AddJobStock")]
|
||||
partial class AddJobStock
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsDefault")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("KerfInches")
|
||||
.HasPrecision(6, 4)
|
||||
.HasColumnType("decimal(6,4)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CuttingTools");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
IsActive = true,
|
||||
IsDefault = true,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Bandsaw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.125m,
|
||||
Name = "Chop Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Cold Cut Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Hacksaw"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Customer")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int?>("CuttingToolId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("JobNumber")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CuttingToolId");
|
||||
|
||||
b.HasIndex("JobNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.ToTable("JobParts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsCustomLength")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Priority")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.ToTable("JobStocks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Shape")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Size")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Materials");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("QuantityOnHand")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId", "LengthInches")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<int?>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal?>("UnitPrice")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId");
|
||||
|
||||
b.ToTable("StockTransactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ContactInfo")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Suppliers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("PartNumber")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<decimal?>("Price")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("SupplierDescription")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId", "StockItemId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("SupplierOfferings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.CuttingTool", "CuttingTool")
|
||||
.WithMany("Jobs")
|
||||
.HasForeignKey("CuttingToolId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("CuttingTool");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Parts")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("JobParts")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Stock")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany()
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("StockItems")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany()
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("Transactions")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany()
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("SupplierOfferings")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany("Offerings")
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Navigation("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Navigation("Parts");
|
||||
|
||||
b.Navigation("Stock");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Navigation("JobParts");
|
||||
|
||||
b.Navigation("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Navigation("SupplierOfferings");
|
||||
|
||||
b.Navigation("Transactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Navigation("Offerings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
74
CutList.Web/Migrations/20260204223202_AddJobStock.cs
Normal file
74
CutList.Web/Migrations/20260204223202_AddJobStock.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddJobStock : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "JobStocks",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
JobId = table.Column<int>(type: "int", nullable: false),
|
||||
MaterialId = table.Column<int>(type: "int", nullable: false),
|
||||
StockItemId = table.Column<int>(type: "int", nullable: true),
|
||||
LengthInches = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false),
|
||||
Quantity = table.Column<int>(type: "int", nullable: false),
|
||||
IsCustomLength = table.Column<bool>(type: "bit", nullable: false),
|
||||
Priority = table.Column<int>(type: "int", nullable: false),
|
||||
SortOrder = table.Column<int>(type: "int", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_JobStocks", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_JobStocks_Jobs_JobId",
|
||||
column: x => x.JobId,
|
||||
principalTable: "Jobs",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_JobStocks_Materials_MaterialId",
|
||||
column: x => x.MaterialId,
|
||||
principalTable: "Materials",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_JobStocks_StockItems_StockItemId",
|
||||
column: x => x.StockItemId,
|
||||
principalTable: "StockItems",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_JobStocks_JobId",
|
||||
table: "JobStocks",
|
||||
column: "JobId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_JobStocks_MaterialId",
|
||||
table: "JobStocks",
|
||||
column: "MaterialId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_JobStocks_StockItemId",
|
||||
table: "JobStocks",
|
||||
column: "StockItemId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "JobStocks");
|
||||
}
|
||||
}
|
||||
}
|
||||
811
CutList.Web/Migrations/20260205012737_AddMaterialDimensions.Designer.cs
generated
Normal file
811
CutList.Web/Migrations/20260205012737_AddMaterialDimensions.Designer.cs
generated
Normal file
@@ -0,0 +1,811 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CutList.Web.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20260205012737_AddMaterialDimensions")]
|
||||
partial class AddMaterialDimensions
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsDefault")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("KerfInches")
|
||||
.HasPrecision(6, 4)
|
||||
.HasColumnType("decimal(6,4)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CuttingTools");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
IsActive = true,
|
||||
IsDefault = true,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Bandsaw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.125m,
|
||||
Name = "Chop Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Cold Cut Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Hacksaw"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Customer")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int?>("CuttingToolId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("JobNumber")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CuttingToolId");
|
||||
|
||||
b.HasIndex("JobNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.ToTable("JobParts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsCustomLength")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Priority")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.ToTable("JobStocks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Shape")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Size")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Materials");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("DimensionType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(21)
|
||||
.HasColumnType("nvarchar(21)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("MaterialDimensions");
|
||||
|
||||
b.HasDiscriminator<string>("DimensionType").HasValue("MaterialDimensions");
|
||||
|
||||
b.UseTphMappingStrategy();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("QuantityOnHand")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId", "LengthInches")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<int?>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal?>("UnitPrice")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId");
|
||||
|
||||
b.ToTable("StockTransactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ContactInfo")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Suppliers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("PartNumber")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<decimal?>("Price")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("SupplierDescription")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId", "StockItemId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("SupplierOfferings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.AngleDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Leg1")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Leg2")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Thickness");
|
||||
|
||||
b.HasIndex("Leg1");
|
||||
|
||||
b.HasDiscriminator().HasValue("Angle");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.ChannelDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Flange")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("Web")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.HasDiscriminator().HasValue("Channel");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.FlatBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Thickness");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Width");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.HasDiscriminator().HasValue("FlatBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.IBeamDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("WeightPerFoot")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.HasDiscriminator().HasValue("IBeam");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.PipeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("NominalSize")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<string>("Schedule")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<decimal?>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("NominalSize");
|
||||
|
||||
b.HasDiscriminator().HasValue("Pipe");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RectangularTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Width");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.HasDiscriminator().HasValue("RectangularTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Diameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Diameter");
|
||||
|
||||
b.HasDiscriminator().HasValue("RoundBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("OuterDiameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("OuterDiameter");
|
||||
|
||||
b.HasDiscriminator().HasValue("RoundTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Size");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.HasDiscriminator().HasValue("SquareBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Size");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.HasDiscriminator().HasValue("SquareTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.CuttingTool", "CuttingTool")
|
||||
.WithMany("Jobs")
|
||||
.HasForeignKey("CuttingToolId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("CuttingTool");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Parts")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("JobParts")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Stock")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany()
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithOne("Dimensions")
|
||||
.HasForeignKey("CutList.Web.Data.Entities.MaterialDimensions", "MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("StockItems")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany()
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("Transactions")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany()
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("SupplierOfferings")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany("Offerings")
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Navigation("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Navigation("Parts");
|
||||
|
||||
b.Navigation("Stock");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Navigation("Dimensions");
|
||||
|
||||
b.Navigation("JobParts");
|
||||
|
||||
b.Navigation("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Navigation("SupplierOfferings");
|
||||
|
||||
b.Navigation("Transactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Navigation("Offerings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddMaterialDimensions : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "MaterialDimensions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
MaterialId = table.Column<int>(type: "int", nullable: false),
|
||||
DimensionType = table.Column<string>(type: "nvarchar(21)", maxLength: 21, nullable: false),
|
||||
Leg1 = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: true),
|
||||
Leg2 = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: true),
|
||||
Thickness = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: true),
|
||||
Height = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: true),
|
||||
Flange = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: true),
|
||||
Web = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: true),
|
||||
Width = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: true),
|
||||
WeightPerFoot = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: true),
|
||||
NominalSize = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: true),
|
||||
Wall = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: true),
|
||||
Schedule = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true),
|
||||
Diameter = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: true),
|
||||
OuterDiameter = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: true),
|
||||
Size = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_MaterialDimensions", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_MaterialDimensions_Materials_MaterialId",
|
||||
column: x => x.MaterialId,
|
||||
principalTable: "Materials",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MaterialDimensions_Diameter",
|
||||
table: "MaterialDimensions",
|
||||
column: "Diameter");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MaterialDimensions_Height",
|
||||
table: "MaterialDimensions",
|
||||
column: "Height");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MaterialDimensions_Leg1",
|
||||
table: "MaterialDimensions",
|
||||
column: "Leg1");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MaterialDimensions_MaterialId",
|
||||
table: "MaterialDimensions",
|
||||
column: "MaterialId",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MaterialDimensions_NominalSize",
|
||||
table: "MaterialDimensions",
|
||||
column: "NominalSize");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MaterialDimensions_OuterDiameter",
|
||||
table: "MaterialDimensions",
|
||||
column: "OuterDiameter");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MaterialDimensions_Size",
|
||||
table: "MaterialDimensions",
|
||||
column: "Size");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MaterialDimensions_Width",
|
||||
table: "MaterialDimensions",
|
||||
column: "Width");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "MaterialDimensions");
|
||||
}
|
||||
}
|
||||
}
|
||||
811
CutList.Web/Migrations/20260205014058_FixMaterialShapeEnumValues.Designer.cs
generated
Normal file
811
CutList.Web/Migrations/20260205014058_FixMaterialShapeEnumValues.Designer.cs
generated
Normal file
@@ -0,0 +1,811 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CutList.Web.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20260205014058_FixMaterialShapeEnumValues")]
|
||||
partial class FixMaterialShapeEnumValues
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsDefault")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("KerfInches")
|
||||
.HasPrecision(6, 4)
|
||||
.HasColumnType("decimal(6,4)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CuttingTools");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
IsActive = true,
|
||||
IsDefault = true,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Bandsaw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.125m,
|
||||
Name = "Chop Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Cold Cut Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Hacksaw"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Customer")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int?>("CuttingToolId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("JobNumber")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CuttingToolId");
|
||||
|
||||
b.HasIndex("JobNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.ToTable("JobParts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsCustomLength")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Priority")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.ToTable("JobStocks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Shape")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Size")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Materials");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("DimensionType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(21)
|
||||
.HasColumnType("nvarchar(21)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("MaterialDimensions");
|
||||
|
||||
b.HasDiscriminator<string>("DimensionType").HasValue("MaterialDimensions");
|
||||
|
||||
b.UseTphMappingStrategy();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("QuantityOnHand")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId", "LengthInches")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<int?>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal?>("UnitPrice")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId");
|
||||
|
||||
b.ToTable("StockTransactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ContactInfo")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Suppliers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("PartNumber")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<decimal?>("Price")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("SupplierDescription")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId", "StockItemId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("SupplierOfferings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.AngleDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Leg1")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Leg2")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Thickness");
|
||||
|
||||
b.HasIndex("Leg1");
|
||||
|
||||
b.HasDiscriminator().HasValue("Angle");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.ChannelDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Flange")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("Web")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.HasDiscriminator().HasValue("Channel");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.FlatBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Thickness");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Width");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.HasDiscriminator().HasValue("FlatBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.IBeamDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("WeightPerFoot")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.HasDiscriminator().HasValue("IBeam");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.PipeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("NominalSize")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<string>("Schedule")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<decimal?>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("NominalSize");
|
||||
|
||||
b.HasDiscriminator().HasValue("Pipe");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RectangularTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Width");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.HasDiscriminator().HasValue("RectangularTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Diameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Diameter");
|
||||
|
||||
b.HasDiscriminator().HasValue("RoundBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("OuterDiameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("OuterDiameter");
|
||||
|
||||
b.HasDiscriminator().HasValue("RoundTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Size");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.HasDiscriminator().HasValue("SquareBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Size");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.HasDiscriminator().HasValue("SquareTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.CuttingTool", "CuttingTool")
|
||||
.WithMany("Jobs")
|
||||
.HasForeignKey("CuttingToolId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("CuttingTool");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Parts")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("JobParts")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Stock")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany()
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithOne("Dimensions")
|
||||
.HasForeignKey("CutList.Web.Data.Entities.MaterialDimensions", "MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("StockItems")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany()
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("Transactions")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany()
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("SupplierOfferings")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany("Offerings")
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Navigation("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Navigation("Parts");
|
||||
|
||||
b.Navigation("Stock");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Navigation("Dimensions");
|
||||
|
||||
b.Navigation("JobParts");
|
||||
|
||||
b.Navigation("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Navigation("SupplierOfferings");
|
||||
|
||||
b.Navigation("Transactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Navigation("Offerings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class FixMaterialShapeEnumValues : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// Convert display names to enum names
|
||||
migrationBuilder.Sql(@"
|
||||
UPDATE Materials SET Shape = 'RoundBar' WHERE Shape = 'Round Bar';
|
||||
UPDATE Materials SET Shape = 'RoundTube' WHERE Shape = 'Round Tube';
|
||||
UPDATE Materials SET Shape = 'FlatBar' WHERE Shape = 'Flat Bar';
|
||||
UPDATE Materials SET Shape = 'SquareBar' WHERE Shape = 'Square Bar';
|
||||
UPDATE Materials SET Shape = 'SquareTube' WHERE Shape = 'Square Tube';
|
||||
UPDATE Materials SET Shape = 'RectangularTube' WHERE Shape = 'Rectangular Tube';
|
||||
UPDATE Materials SET Shape = 'IBeam' WHERE Shape = 'I-Beam';
|
||||
");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
814
CutList.Web/Migrations/20260205025654_AddMaterialSortOrder.Designer.cs
generated
Normal file
814
CutList.Web/Migrations/20260205025654_AddMaterialSortOrder.Designer.cs
generated
Normal file
@@ -0,0 +1,814 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CutList.Web.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20260205025654_AddMaterialSortOrder")]
|
||||
partial class AddMaterialSortOrder
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsDefault")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("KerfInches")
|
||||
.HasPrecision(6, 4)
|
||||
.HasColumnType("decimal(6,4)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CuttingTools");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
IsActive = true,
|
||||
IsDefault = true,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Bandsaw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.125m,
|
||||
Name = "Chop Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Cold Cut Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Hacksaw"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Customer")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int?>("CuttingToolId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("JobNumber")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CuttingToolId");
|
||||
|
||||
b.HasIndex("JobNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.ToTable("JobParts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsCustomLength")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Priority")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.ToTable("JobStocks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Shape")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Size")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Materials");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("DimensionType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(21)
|
||||
.HasColumnType("nvarchar(21)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("MaterialDimensions");
|
||||
|
||||
b.HasDiscriminator<string>("DimensionType").HasValue("MaterialDimensions");
|
||||
|
||||
b.UseTphMappingStrategy();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("QuantityOnHand")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId", "LengthInches")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<int?>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal?>("UnitPrice")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId");
|
||||
|
||||
b.ToTable("StockTransactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ContactInfo")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Suppliers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("PartNumber")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<decimal?>("Price")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("SupplierDescription")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId", "StockItemId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("SupplierOfferings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.AngleDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Leg1")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Leg2")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Thickness");
|
||||
|
||||
b.HasIndex("Leg1");
|
||||
|
||||
b.HasDiscriminator().HasValue("Angle");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.ChannelDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Flange")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("Web")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.HasDiscriminator().HasValue("Channel");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.FlatBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Thickness");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Width");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.HasDiscriminator().HasValue("FlatBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.IBeamDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("WeightPerFoot")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.HasDiscriminator().HasValue("IBeam");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.PipeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("NominalSize")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<string>("Schedule")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<decimal?>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("NominalSize");
|
||||
|
||||
b.HasDiscriminator().HasValue("Pipe");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RectangularTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Width");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.HasDiscriminator().HasValue("RectangularTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Diameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Diameter");
|
||||
|
||||
b.HasDiscriminator().HasValue("RoundBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("OuterDiameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("OuterDiameter");
|
||||
|
||||
b.HasDiscriminator().HasValue("RoundTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Size");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.HasDiscriminator().HasValue("SquareBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Size");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.HasDiscriminator().HasValue("SquareTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.CuttingTool", "CuttingTool")
|
||||
.WithMany("Jobs")
|
||||
.HasForeignKey("CuttingToolId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("CuttingTool");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Parts")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("JobParts")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Stock")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany()
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithOne("Dimensions")
|
||||
.HasForeignKey("CutList.Web.Data.Entities.MaterialDimensions", "MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("StockItems")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany()
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("Transactions")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany()
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("SupplierOfferings")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany("Offerings")
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Navigation("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Navigation("Parts");
|
||||
|
||||
b.Navigation("Stock");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Navigation("Dimensions");
|
||||
|
||||
b.Navigation("JobParts");
|
||||
|
||||
b.Navigation("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Navigation("SupplierOfferings");
|
||||
|
||||
b.Navigation("Transactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Navigation("Offerings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddMaterialSortOrder : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "SortOrder",
|
||||
table: "Materials",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SortOrder",
|
||||
table: "Materials");
|
||||
}
|
||||
}
|
||||
}
|
||||
814
CutList.Web/Migrations/20260205025716_UpdateExistingSortOrders.Designer.cs
generated
Normal file
814
CutList.Web/Migrations/20260205025716_UpdateExistingSortOrders.Designer.cs
generated
Normal file
@@ -0,0 +1,814 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CutList.Web.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20260205025716_UpdateExistingSortOrders")]
|
||||
partial class UpdateExistingSortOrders
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsDefault")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("KerfInches")
|
||||
.HasPrecision(6, 4)
|
||||
.HasColumnType("decimal(6,4)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CuttingTools");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
IsActive = true,
|
||||
IsDefault = true,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Bandsaw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.125m,
|
||||
Name = "Chop Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Cold Cut Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Hacksaw"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Customer")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int?>("CuttingToolId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("JobNumber")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CuttingToolId");
|
||||
|
||||
b.HasIndex("JobNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.ToTable("JobParts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsCustomLength")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Priority")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.ToTable("JobStocks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Shape")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Size")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Materials");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("DimensionType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(21)
|
||||
.HasColumnType("nvarchar(21)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("MaterialDimensions");
|
||||
|
||||
b.HasDiscriminator<string>("DimensionType").HasValue("MaterialDimensions");
|
||||
|
||||
b.UseTphMappingStrategy();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("QuantityOnHand")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId", "LengthInches")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<int?>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal?>("UnitPrice")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId");
|
||||
|
||||
b.ToTable("StockTransactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ContactInfo")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Suppliers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("PartNumber")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<decimal?>("Price")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("SupplierDescription")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId", "StockItemId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("SupplierOfferings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.AngleDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Leg1")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Leg2")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Thickness");
|
||||
|
||||
b.HasIndex("Leg1");
|
||||
|
||||
b.HasDiscriminator().HasValue("Angle");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.ChannelDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Flange")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("Web")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.HasDiscriminator().HasValue("Channel");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.FlatBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Thickness");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Width");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.HasDiscriminator().HasValue("FlatBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.IBeamDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("WeightPerFoot")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.HasDiscriminator().HasValue("IBeam");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.PipeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("NominalSize")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<string>("Schedule")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<decimal?>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("NominalSize");
|
||||
|
||||
b.HasDiscriminator().HasValue("Pipe");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RectangularTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Width");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.HasDiscriminator().HasValue("RectangularTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Diameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Diameter");
|
||||
|
||||
b.HasDiscriminator().HasValue("RoundBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("OuterDiameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("OuterDiameter");
|
||||
|
||||
b.HasDiscriminator().HasValue("RoundTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Size");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.HasDiscriminator().HasValue("SquareBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Size");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.HasDiscriminator().HasValue("SquareTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.CuttingTool", "CuttingTool")
|
||||
.WithMany("Jobs")
|
||||
.HasForeignKey("CuttingToolId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("CuttingTool");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Parts")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("JobParts")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Stock")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany()
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithOne("Dimensions")
|
||||
.HasForeignKey("CutList.Web.Data.Entities.MaterialDimensions", "MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("StockItems")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany()
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("Transactions")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany()
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("SupplierOfferings")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany("Offerings")
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Navigation("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Navigation("Parts");
|
||||
|
||||
b.Navigation("Stock");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Navigation("Dimensions");
|
||||
|
||||
b.Navigation("JobParts");
|
||||
|
||||
b.Navigation("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Navigation("SupplierOfferings");
|
||||
|
||||
b.Navigation("Transactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Navigation("Offerings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class UpdateExistingSortOrders : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// Update SortOrder for existing materials based on their primary dimension
|
||||
migrationBuilder.Sql(@"
|
||||
UPDATE m
|
||||
SET m.SortOrder = CAST(
|
||||
CASE d.DimensionType
|
||||
WHEN 'RoundBar' THEN d.Diameter * 1000
|
||||
WHEN 'RoundTube' THEN d.OuterDiameter * 1000
|
||||
WHEN 'FlatBar' THEN d.Width * 1000
|
||||
WHEN 'SquareBar' THEN d.Size * 1000
|
||||
WHEN 'SquareTube' THEN d.Size * 1000
|
||||
WHEN 'RectangularTube' THEN d.Width * 1000
|
||||
WHEN 'Angle' THEN d.Leg1 * 1000
|
||||
WHEN 'Channel' THEN d.Height * 1000
|
||||
WHEN 'IBeam' THEN d.Height * 1000
|
||||
WHEN 'Pipe' THEN d.NominalSize * 1000
|
||||
ELSE 0
|
||||
END AS INT)
|
||||
FROM Materials m
|
||||
INNER JOIN MaterialDimensions d ON d.MaterialId = m.Id
|
||||
");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
818
CutList.Web/Migrations/20260205041323_AddMaterialType.Designer.cs
generated
Normal file
818
CutList.Web/Migrations/20260205041323_AddMaterialType.Designer.cs
generated
Normal file
@@ -0,0 +1,818 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CutList.Web.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20260205041323_AddMaterialType")]
|
||||
partial class AddMaterialType
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsDefault")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("KerfInches")
|
||||
.HasPrecision(6, 4)
|
||||
.HasColumnType("decimal(6,4)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CuttingTools");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
IsActive = true,
|
||||
IsDefault = true,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Bandsaw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.125m,
|
||||
Name = "Chop Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Cold Cut Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Hacksaw"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Customer")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int?>("CuttingToolId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("JobNumber")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CuttingToolId");
|
||||
|
||||
b.HasIndex("JobNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.ToTable("JobParts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsCustomLength")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Priority")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.ToTable("JobStocks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("MaterialType")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Shape")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Size")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Materials");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("DimensionType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(21)
|
||||
.HasColumnType("nvarchar(21)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("MaterialDimensions");
|
||||
|
||||
b.HasDiscriminator<string>("DimensionType").HasValue("MaterialDimensions");
|
||||
|
||||
b.UseTphMappingStrategy();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("QuantityOnHand")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId", "LengthInches")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<int?>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal?>("UnitPrice")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId");
|
||||
|
||||
b.ToTable("StockTransactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ContactInfo")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Suppliers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("PartNumber")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<decimal?>("Price")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("SupplierDescription")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId", "StockItemId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("SupplierOfferings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.AngleDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Leg1")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Leg2")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Thickness");
|
||||
|
||||
b.HasIndex("Leg1");
|
||||
|
||||
b.HasDiscriminator().HasValue("Angle");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.ChannelDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Flange")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("Web")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.HasDiscriminator().HasValue("Channel");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.FlatBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Thickness");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Width");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.HasDiscriminator().HasValue("FlatBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.IBeamDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("WeightPerFoot")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.HasDiscriminator().HasValue("IBeam");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.PipeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("NominalSize")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<string>("Schedule")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<decimal?>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("NominalSize");
|
||||
|
||||
b.HasDiscriminator().HasValue("Pipe");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RectangularTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Width");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.HasDiscriminator().HasValue("RectangularTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Diameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Diameter");
|
||||
|
||||
b.HasDiscriminator().HasValue("RoundBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("OuterDiameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("OuterDiameter");
|
||||
|
||||
b.HasDiscriminator().HasValue("RoundTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Size");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.HasDiscriminator().HasValue("SquareBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Size");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.HasDiscriminator().HasValue("SquareTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.CuttingTool", "CuttingTool")
|
||||
.WithMany("Jobs")
|
||||
.HasForeignKey("CuttingToolId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("CuttingTool");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Parts")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("JobParts")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Stock")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany()
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithOne("Dimensions")
|
||||
.HasForeignKey("CutList.Web.Data.Entities.MaterialDimensions", "MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("StockItems")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany()
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("Transactions")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany()
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("SupplierOfferings")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany("Offerings")
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Navigation("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Navigation("Parts");
|
||||
|
||||
b.Navigation("Stock");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Navigation("Dimensions");
|
||||
|
||||
b.Navigation("JobParts");
|
||||
|
||||
b.Navigation("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Navigation("SupplierOfferings");
|
||||
|
||||
b.Navigation("Transactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Navigation("Offerings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
29
CutList.Web/Migrations/20260205041323_AddMaterialType.cs
Normal file
29
CutList.Web/Migrations/20260205041323_AddMaterialType.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddMaterialType : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "MaterialType",
|
||||
table: "Materials",
|
||||
type: "nvarchar(50)",
|
||||
maxLength: 50,
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "MaterialType",
|
||||
table: "Materials");
|
||||
}
|
||||
}
|
||||
}
|
||||
823
CutList.Web/Migrations/20260205041542_AddMaterialTypeEnumAndGrade.Designer.cs
generated
Normal file
823
CutList.Web/Migrations/20260205041542_AddMaterialTypeEnumAndGrade.Designer.cs
generated
Normal file
@@ -0,0 +1,823 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CutList.Web.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20260205041542_AddMaterialTypeEnumAndGrade")]
|
||||
partial class AddMaterialTypeEnumAndGrade
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsDefault")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("KerfInches")
|
||||
.HasPrecision(6, 4)
|
||||
.HasColumnType("decimal(6,4)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CuttingTools");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
IsActive = true,
|
||||
IsDefault = true,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Bandsaw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.125m,
|
||||
Name = "Chop Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Cold Cut Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Hacksaw"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Customer")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int?>("CuttingToolId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("JobNumber")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CuttingToolId");
|
||||
|
||||
b.HasIndex("JobNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.ToTable("JobParts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsCustomLength")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Priority")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.ToTable("JobStocks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("Grade")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Shape")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Size")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Materials");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("DimensionType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(21)
|
||||
.HasColumnType("nvarchar(21)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("MaterialDimensions");
|
||||
|
||||
b.HasDiscriminator<string>("DimensionType").HasValue("MaterialDimensions");
|
||||
|
||||
b.UseTphMappingStrategy();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("QuantityOnHand")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId", "LengthInches")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<int?>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal?>("UnitPrice")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId");
|
||||
|
||||
b.ToTable("StockTransactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ContactInfo")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Suppliers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("PartNumber")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<decimal?>("Price")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("SupplierDescription")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId", "StockItemId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("SupplierOfferings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.AngleDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Leg1")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Leg2")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Thickness");
|
||||
|
||||
b.HasIndex("Leg1");
|
||||
|
||||
b.HasDiscriminator().HasValue("Angle");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.ChannelDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Flange")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("Web")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.HasDiscriminator().HasValue("Channel");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.FlatBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Thickness");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Width");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.HasDiscriminator().HasValue("FlatBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.IBeamDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("WeightPerFoot")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.HasDiscriminator().HasValue("IBeam");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.PipeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("NominalSize")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<string>("Schedule")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<decimal?>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("NominalSize");
|
||||
|
||||
b.HasDiscriminator().HasValue("Pipe");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RectangularTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Width");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.HasDiscriminator().HasValue("RectangularTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Diameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Diameter");
|
||||
|
||||
b.HasDiscriminator().HasValue("RoundBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("OuterDiameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("OuterDiameter");
|
||||
|
||||
b.HasDiscriminator().HasValue("RoundTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Size");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.HasDiscriminator().HasValue("SquareBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Size");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.HasDiscriminator().HasValue("SquareTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.CuttingTool", "CuttingTool")
|
||||
.WithMany("Jobs")
|
||||
.HasForeignKey("CuttingToolId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("CuttingTool");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Parts")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("JobParts")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Stock")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany()
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithOne("Dimensions")
|
||||
.HasForeignKey("CutList.Web.Data.Entities.MaterialDimensions", "MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("StockItems")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany()
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("Transactions")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany()
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("SupplierOfferings")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany("Offerings")
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Navigation("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Navigation("Parts");
|
||||
|
||||
b.Navigation("Stock");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Navigation("Dimensions");
|
||||
|
||||
b.Navigation("JobParts");
|
||||
|
||||
b.Navigation("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Navigation("SupplierOfferings");
|
||||
|
||||
b.Navigation("Transactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Navigation("Offerings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddMaterialTypeEnumAndGrade : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "MaterialType",
|
||||
table: "Materials",
|
||||
newName: "Grade");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Type",
|
||||
table: "Materials",
|
||||
type: "nvarchar(20)",
|
||||
maxLength: 20,
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Type",
|
||||
table: "Materials");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "Grade",
|
||||
table: "Materials",
|
||||
newName: "MaterialType");
|
||||
}
|
||||
}
|
||||
}
|
||||
823
CutList.Web/Migrations/20260205214055_FixDimensionDiscriminatorValues.Designer.cs
generated
Normal file
823
CutList.Web/Migrations/20260205214055_FixDimensionDiscriminatorValues.Designer.cs
generated
Normal file
@@ -0,0 +1,823 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CutList.Web.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20260205214055_FixDimensionDiscriminatorValues")]
|
||||
partial class FixDimensionDiscriminatorValues
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsDefault")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("KerfInches")
|
||||
.HasPrecision(6, 4)
|
||||
.HasColumnType("decimal(6,4)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CuttingTools");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
IsActive = true,
|
||||
IsDefault = true,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Bandsaw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.125m,
|
||||
Name = "Chop Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Cold Cut Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Hacksaw"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Customer")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int?>("CuttingToolId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("JobNumber")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CuttingToolId");
|
||||
|
||||
b.HasIndex("JobNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.ToTable("JobParts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsCustomLength")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Priority")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.ToTable("JobStocks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("Grade")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Shape")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Size")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Materials");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("DimensionType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(21)
|
||||
.HasColumnType("nvarchar(21)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("MaterialDimensions");
|
||||
|
||||
b.HasDiscriminator<string>("DimensionType").HasValue("MaterialDimensions");
|
||||
|
||||
b.UseTphMappingStrategy();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("QuantityOnHand")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId", "LengthInches")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<int?>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal?>("UnitPrice")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId");
|
||||
|
||||
b.ToTable("StockTransactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ContactInfo")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Suppliers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("PartNumber")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<decimal?>("Price")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("SupplierDescription")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId", "StockItemId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("SupplierOfferings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.AngleDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Leg1")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Leg2")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Thickness");
|
||||
|
||||
b.HasIndex("Leg1");
|
||||
|
||||
b.HasDiscriminator().HasValue("Angle");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.ChannelDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Flange")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("Web")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.HasDiscriminator().HasValue("Channel");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.FlatBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Thickness");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Width");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.HasDiscriminator().HasValue("FlatBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.IBeamDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("WeightPerFoot")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.HasDiscriminator().HasValue("IBeam");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.PipeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("NominalSize")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<string>("Schedule")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<decimal?>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("NominalSize");
|
||||
|
||||
b.HasDiscriminator().HasValue("Pipe");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RectangularTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Width");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.HasDiscriminator().HasValue("RectangularTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Diameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Diameter");
|
||||
|
||||
b.HasDiscriminator().HasValue("RoundBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("OuterDiameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("OuterDiameter");
|
||||
|
||||
b.HasDiscriminator().HasValue("RoundTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Size");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.HasDiscriminator().HasValue("SquareBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Size");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.HasDiscriminator().HasValue("SquareTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.CuttingTool", "CuttingTool")
|
||||
.WithMany("Jobs")
|
||||
.HasForeignKey("CuttingToolId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("CuttingTool");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Parts")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("JobParts")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Stock")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany()
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithOne("Dimensions")
|
||||
.HasForeignKey("CutList.Web.Data.Entities.MaterialDimensions", "MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("StockItems")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany()
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("Transactions")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany()
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("SupplierOfferings")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany("Offerings")
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Navigation("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Navigation("Parts");
|
||||
|
||||
b.Navigation("Stock");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Navigation("Dimensions");
|
||||
|
||||
b.Navigation("JobParts");
|
||||
|
||||
b.Navigation("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Navigation("SupplierOfferings");
|
||||
|
||||
b.Navigation("Transactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Navigation("Offerings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class FixDimensionDiscriminatorValues : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// Fix discriminator values: database has class names (e.g. 'AngleDimensions')
|
||||
// but EF Core config expects short names (e.g. 'Angle')
|
||||
migrationBuilder.Sql(@"
|
||||
UPDATE MaterialDimensions SET DimensionType = 'Angle' WHERE DimensionType = 'AngleDimensions';
|
||||
UPDATE MaterialDimensions SET DimensionType = 'Channel' WHERE DimensionType = 'ChannelDimensions';
|
||||
UPDATE MaterialDimensions SET DimensionType = 'FlatBar' WHERE DimensionType = 'FlatBarDimensions';
|
||||
UPDATE MaterialDimensions SET DimensionType = 'IBeam' WHERE DimensionType = 'IBeamDimensions';
|
||||
UPDATE MaterialDimensions SET DimensionType = 'Pipe' WHERE DimensionType = 'PipeDimensions';
|
||||
UPDATE MaterialDimensions SET DimensionType = 'RectangularTube' WHERE DimensionType = 'RectangularTubeDimensions';
|
||||
UPDATE MaterialDimensions SET DimensionType = 'RoundBar' WHERE DimensionType = 'RoundBarDimensions';
|
||||
UPDATE MaterialDimensions SET DimensionType = 'RoundTube' WHERE DimensionType = 'RoundTubeDimensions';
|
||||
UPDATE MaterialDimensions SET DimensionType = 'SquareBar' WHERE DimensionType = 'SquareBarDimensions';
|
||||
UPDATE MaterialDimensions SET DimensionType = 'SquareTube' WHERE DimensionType = 'SquareTubeDimensions';
|
||||
");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// Revert to class name discriminator values
|
||||
migrationBuilder.Sql(@"
|
||||
UPDATE MaterialDimensions SET DimensionType = 'AngleDimensions' WHERE DimensionType = 'Angle';
|
||||
UPDATE MaterialDimensions SET DimensionType = 'ChannelDimensions' WHERE DimensionType = 'Channel';
|
||||
UPDATE MaterialDimensions SET DimensionType = 'FlatBarDimensions' WHERE DimensionType = 'FlatBar';
|
||||
UPDATE MaterialDimensions SET DimensionType = 'IBeamDimensions' WHERE DimensionType = 'IBeam';
|
||||
UPDATE MaterialDimensions SET DimensionType = 'PipeDimensions' WHERE DimensionType = 'Pipe';
|
||||
UPDATE MaterialDimensions SET DimensionType = 'RectangularTubeDimensions' WHERE DimensionType = 'RectangularTube';
|
||||
UPDATE MaterialDimensions SET DimensionType = 'RoundBarDimensions' WHERE DimensionType = 'RoundBar';
|
||||
UPDATE MaterialDimensions SET DimensionType = 'RoundTubeDimensions' WHERE DimensionType = 'RoundTube';
|
||||
UPDATE MaterialDimensions SET DimensionType = 'SquareBarDimensions' WHERE DimensionType = 'SquareBar';
|
||||
UPDATE MaterialDimensions SET DimensionType = 'SquareTubeDimensions' WHERE DimensionType = 'SquareTube';
|
||||
");
|
||||
}
|
||||
}
|
||||
}
|
||||
823
CutList.Web/Migrations/20260205214339_FixEmptyMaterialType.Designer.cs
generated
Normal file
823
CutList.Web/Migrations/20260205214339_FixEmptyMaterialType.Designer.cs
generated
Normal file
@@ -0,0 +1,823 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CutList.Web.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20260205214339_FixEmptyMaterialType")]
|
||||
partial class FixEmptyMaterialType
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsDefault")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("KerfInches")
|
||||
.HasPrecision(6, 4)
|
||||
.HasColumnType("decimal(6,4)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CuttingTools");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
IsActive = true,
|
||||
IsDefault = true,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Bandsaw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.125m,
|
||||
Name = "Chop Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Cold Cut Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Hacksaw"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Customer")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int?>("CuttingToolId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("JobNumber")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CuttingToolId");
|
||||
|
||||
b.HasIndex("JobNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.ToTable("JobParts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsCustomLength")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Priority")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.ToTable("JobStocks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("Grade")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Shape")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Size")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Materials");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("DimensionType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(21)
|
||||
.HasColumnType("nvarchar(21)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("MaterialDimensions");
|
||||
|
||||
b.HasDiscriminator<string>("DimensionType").HasValue("MaterialDimensions");
|
||||
|
||||
b.UseTphMappingStrategy();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("QuantityOnHand")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId", "LengthInches")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<int?>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal?>("UnitPrice")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId");
|
||||
|
||||
b.ToTable("StockTransactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ContactInfo")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Suppliers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("PartNumber")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<decimal?>("Price")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("SupplierDescription")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId", "StockItemId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("SupplierOfferings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.AngleDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Leg1")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Leg2")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Thickness");
|
||||
|
||||
b.HasIndex("Leg1");
|
||||
|
||||
b.HasDiscriminator().HasValue("Angle");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.ChannelDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Flange")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("Web")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.HasDiscriminator().HasValue("Channel");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.FlatBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Thickness");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Width");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.HasDiscriminator().HasValue("FlatBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.IBeamDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("WeightPerFoot")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.HasDiscriminator().HasValue("IBeam");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.PipeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("NominalSize")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<string>("Schedule")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<decimal?>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("NominalSize");
|
||||
|
||||
b.HasDiscriminator().HasValue("Pipe");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RectangularTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Width");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.HasDiscriminator().HasValue("RectangularTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Diameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Diameter");
|
||||
|
||||
b.HasDiscriminator().HasValue("RoundBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("OuterDiameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("OuterDiameter");
|
||||
|
||||
b.HasDiscriminator().HasValue("RoundTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Size");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.HasDiscriminator().HasValue("SquareBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Size");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.HasDiscriminator().HasValue("SquareTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.CuttingTool", "CuttingTool")
|
||||
.WithMany("Jobs")
|
||||
.HasForeignKey("CuttingToolId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("CuttingTool");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Parts")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("JobParts")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Stock")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany()
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithOne("Dimensions")
|
||||
.HasForeignKey("CutList.Web.Data.Entities.MaterialDimensions", "MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("StockItems")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany()
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("Transactions")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany()
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("SupplierOfferings")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany("Offerings")
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Navigation("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Navigation("Parts");
|
||||
|
||||
b.Navigation("Stock");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Navigation("Dimensions");
|
||||
|
||||
b.Navigation("JobParts");
|
||||
|
||||
b.Navigation("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Navigation("SupplierOfferings");
|
||||
|
||||
b.Navigation("Transactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Navigation("Offerings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class FixEmptyMaterialType : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// Fix empty Type values from when the column was added with defaultValue: ""
|
||||
migrationBuilder.Sql("UPDATE Materials SET Type = 'Steel' WHERE Type = '' OR Type IS NULL;");
|
||||
|
||||
// Change the default so new rows also get 'Steel'
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Type",
|
||||
table: "Materials",
|
||||
type: "nvarchar(20)",
|
||||
maxLength: 20,
|
||||
nullable: false,
|
||||
defaultValue: "Steel");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Type",
|
||||
table: "Materials",
|
||||
type: "nvarchar(20)",
|
||||
maxLength: 20,
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
}
|
||||
}
|
||||
}
|
||||
896
CutList.Web/Migrations/20260207195807_AddPurchaseItem.Designer.cs
generated
Normal file
896
CutList.Web/Migrations/20260207195807_AddPurchaseItem.Designer.cs
generated
Normal file
@@ -0,0 +1,896 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CutList.Web.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20260207195807_AddPurchaseItem")]
|
||||
partial class AddPurchaseItem
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsDefault")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("KerfInches")
|
||||
.HasPrecision(6, 4)
|
||||
.HasColumnType("decimal(6,4)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CuttingTools");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
IsActive = true,
|
||||
IsDefault = true,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Bandsaw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.125m,
|
||||
Name = "Chop Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Cold Cut Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Hacksaw"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Customer")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int?>("CuttingToolId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("JobNumber")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CuttingToolId");
|
||||
|
||||
b.HasIndex("JobNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.ToTable("JobParts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsCustomLength")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Priority")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.ToTable("JobStocks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("Grade")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Shape")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Size")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Materials");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("DimensionType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(21)
|
||||
.HasColumnType("nvarchar(21)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("MaterialDimensions");
|
||||
|
||||
b.HasDiscriminator<string>("DimensionType").HasValue("MaterialDimensions");
|
||||
|
||||
b.UseTphMappingStrategy();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.PurchaseItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<int?>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId");
|
||||
|
||||
b.ToTable("PurchaseItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("QuantityOnHand")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId", "LengthInches")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<int?>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal?>("UnitPrice")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId");
|
||||
|
||||
b.ToTable("StockTransactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ContactInfo")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Suppliers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("PartNumber")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<decimal?>("Price")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("SupplierDescription")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId", "StockItemId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("SupplierOfferings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.AngleDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Leg1")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Leg2")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Thickness");
|
||||
|
||||
b.HasIndex("Leg1");
|
||||
|
||||
b.HasDiscriminator().HasValue("Angle");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.ChannelDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Flange")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("Web")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.HasDiscriminator().HasValue("Channel");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.FlatBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Thickness");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Width");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.HasDiscriminator().HasValue("FlatBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.IBeamDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("WeightPerFoot")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.HasDiscriminator().HasValue("IBeam");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.PipeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("NominalSize")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<string>("Schedule")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<decimal?>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("NominalSize");
|
||||
|
||||
b.HasDiscriminator().HasValue("Pipe");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RectangularTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Width");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.HasDiscriminator().HasValue("RectangularTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Diameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Diameter");
|
||||
|
||||
b.HasDiscriminator().HasValue("RoundBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("OuterDiameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("OuterDiameter");
|
||||
|
||||
b.HasDiscriminator().HasValue("RoundTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Size");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.HasDiscriminator().HasValue("SquareBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Size");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.HasDiscriminator().HasValue("SquareTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.CuttingTool", "CuttingTool")
|
||||
.WithMany("Jobs")
|
||||
.HasForeignKey("CuttingToolId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("CuttingTool");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Parts")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("JobParts")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Stock")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany()
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithOne("Dimensions")
|
||||
.HasForeignKey("CutList.Web.Data.Entities.MaterialDimensions", "MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.PurchaseItem", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany()
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany()
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("StockItems")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany()
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("Transactions")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany()
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("SupplierOfferings")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany("Offerings")
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Navigation("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Navigation("Parts");
|
||||
|
||||
b.Navigation("Stock");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Navigation("Dimensions");
|
||||
|
||||
b.Navigation("JobParts");
|
||||
|
||||
b.Navigation("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Navigation("SupplierOfferings");
|
||||
|
||||
b.Navigation("Transactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Navigation("Offerings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
75
CutList.Web/Migrations/20260207195807_AddPurchaseItem.cs
Normal file
75
CutList.Web/Migrations/20260207195807_AddPurchaseItem.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddPurchaseItem : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "PurchaseItems",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
StockItemId = table.Column<int>(type: "int", nullable: false),
|
||||
SupplierId = table.Column<int>(type: "int", nullable: true),
|
||||
Quantity = table.Column<int>(type: "int", nullable: false),
|
||||
JobId = table.Column<int>(type: "int", nullable: true),
|
||||
Notes = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
|
||||
Status = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false, defaultValueSql: "GETUTCDATE()"),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PurchaseItems", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_PurchaseItems_Jobs_JobId",
|
||||
column: x => x.JobId,
|
||||
principalTable: "Jobs",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
table.ForeignKey(
|
||||
name: "FK_PurchaseItems_StockItems_StockItemId",
|
||||
column: x => x.StockItemId,
|
||||
principalTable: "StockItems",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_PurchaseItems_Suppliers_SupplierId",
|
||||
column: x => x.SupplierId,
|
||||
principalTable: "Suppliers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_PurchaseItems_JobId",
|
||||
table: "PurchaseItems",
|
||||
column: "JobId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_PurchaseItems_StockItemId",
|
||||
table: "PurchaseItems",
|
||||
column: "StockItemId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_PurchaseItems_SupplierId",
|
||||
table: "PurchaseItems",
|
||||
column: "SupplierId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "PurchaseItems");
|
||||
}
|
||||
}
|
||||
}
|
||||
899
CutList.Web/Migrations/20260207201007_AddJobLockedAt.Designer.cs
generated
Normal file
899
CutList.Web/Migrations/20260207201007_AddJobLockedAt.Designer.cs
generated
Normal file
@@ -0,0 +1,899 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CutList.Web.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20260207201007_AddJobLockedAt")]
|
||||
partial class AddJobLockedAt
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsDefault")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("KerfInches")
|
||||
.HasPrecision(6, 4)
|
||||
.HasColumnType("decimal(6,4)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CuttingTools");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
IsActive = true,
|
||||
IsDefault = true,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Bandsaw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.125m,
|
||||
Name = "Chop Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Cold Cut Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Hacksaw"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Customer")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int?>("CuttingToolId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("JobNumber")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<DateTime?>("LockedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CuttingToolId");
|
||||
|
||||
b.HasIndex("JobNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.ToTable("JobParts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsCustomLength")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Priority")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.ToTable("JobStocks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("Grade")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Shape")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Size")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Materials");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("DimensionType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(21)
|
||||
.HasColumnType("nvarchar(21)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("MaterialDimensions");
|
||||
|
||||
b.HasDiscriminator<string>("DimensionType").HasValue("MaterialDimensions");
|
||||
|
||||
b.UseTphMappingStrategy();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.PurchaseItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<int?>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId");
|
||||
|
||||
b.ToTable("PurchaseItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("QuantityOnHand")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId", "LengthInches")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<int?>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal?>("UnitPrice")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId");
|
||||
|
||||
b.ToTable("StockTransactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ContactInfo")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Suppliers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("PartNumber")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<decimal?>("Price")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("SupplierDescription")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId", "StockItemId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("SupplierOfferings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.AngleDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Leg1")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Leg2")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Thickness");
|
||||
|
||||
b.HasIndex("Leg1");
|
||||
|
||||
b.HasDiscriminator().HasValue("Angle");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.ChannelDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Flange")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("Web")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.HasDiscriminator().HasValue("Channel");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.FlatBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Thickness");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Width");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.HasDiscriminator().HasValue("FlatBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.IBeamDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("WeightPerFoot")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.HasDiscriminator().HasValue("IBeam");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.PipeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("NominalSize")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<string>("Schedule")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<decimal?>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("NominalSize");
|
||||
|
||||
b.HasDiscriminator().HasValue("Pipe");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RectangularTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Width");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.HasDiscriminator().HasValue("RectangularTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Diameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Diameter");
|
||||
|
||||
b.HasDiscriminator().HasValue("RoundBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("OuterDiameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("OuterDiameter");
|
||||
|
||||
b.HasDiscriminator().HasValue("RoundTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Size");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.HasDiscriminator().HasValue("SquareBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Size");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.HasDiscriminator().HasValue("SquareTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.CuttingTool", "CuttingTool")
|
||||
.WithMany("Jobs")
|
||||
.HasForeignKey("CuttingToolId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("CuttingTool");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Parts")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("JobParts")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Stock")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany()
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithOne("Dimensions")
|
||||
.HasForeignKey("CutList.Web.Data.Entities.MaterialDimensions", "MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.PurchaseItem", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany()
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany()
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("StockItems")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany()
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("Transactions")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany()
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("SupplierOfferings")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany("Offerings")
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Navigation("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Navigation("Parts");
|
||||
|
||||
b.Navigation("Stock");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Navigation("Dimensions");
|
||||
|
||||
b.Navigation("JobParts");
|
||||
|
||||
b.Navigation("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Navigation("SupplierOfferings");
|
||||
|
||||
b.Navigation("Transactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Navigation("Offerings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
29
CutList.Web/Migrations/20260207201007_AddJobLockedAt.cs
Normal file
29
CutList.Web/Migrations/20260207201007_AddJobLockedAt.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddJobLockedAt : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "LockedAt",
|
||||
table: "Jobs",
|
||||
type: "datetime2",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LockedAt",
|
||||
table: "Jobs");
|
||||
}
|
||||
}
|
||||
}
|
||||
905
CutList.Web/Migrations/20260209122312_AddJobOptimizationResult.Designer.cs
generated
Normal file
905
CutList.Web/Migrations/20260209122312_AddJobOptimizationResult.Designer.cs
generated
Normal file
@@ -0,0 +1,905 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CutList.Web.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20260209122312_AddJobOptimizationResult")]
|
||||
partial class AddJobOptimizationResult
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsDefault")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("KerfInches")
|
||||
.HasPrecision(6, 4)
|
||||
.HasColumnType("decimal(6,4)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CuttingTools");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
IsActive = true,
|
||||
IsDefault = true,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Bandsaw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.125m,
|
||||
Name = "Chop Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Cold Cut Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Hacksaw"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Customer")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int?>("CuttingToolId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("JobNumber")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<DateTime?>("LockedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("OptimizationResultJson")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime?>("OptimizedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CuttingToolId");
|
||||
|
||||
b.HasIndex("JobNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.ToTable("JobParts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsCustomLength")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Priority")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.ToTable("JobStocks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("Grade")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Shape")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Size")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Materials");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("DimensionType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(21)
|
||||
.HasColumnType("nvarchar(21)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("MaterialDimensions");
|
||||
|
||||
b.HasDiscriminator<string>("DimensionType").HasValue("MaterialDimensions");
|
||||
|
||||
b.UseTphMappingStrategy();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.PurchaseItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<int?>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId");
|
||||
|
||||
b.ToTable("PurchaseItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("QuantityOnHand")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId", "LengthInches")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<int?>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal?>("UnitPrice")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId");
|
||||
|
||||
b.ToTable("StockTransactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ContactInfo")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Suppliers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("PartNumber")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<decimal?>("Price")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("SupplierDescription")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId", "StockItemId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("SupplierOfferings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.AngleDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Leg1")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Leg2")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Thickness");
|
||||
|
||||
b.HasIndex("Leg1");
|
||||
|
||||
b.HasDiscriminator().HasValue("Angle");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.ChannelDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Flange")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("Web")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.HasDiscriminator().HasValue("Channel");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.FlatBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Thickness");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Width");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.HasDiscriminator().HasValue("FlatBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.IBeamDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("WeightPerFoot")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.HasDiscriminator().HasValue("IBeam");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.PipeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("NominalSize")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<string>("Schedule")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<decimal?>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("NominalSize");
|
||||
|
||||
b.HasDiscriminator().HasValue("Pipe");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RectangularTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Height");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Width");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.HasDiscriminator().HasValue("RectangularTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Diameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Diameter");
|
||||
|
||||
b.HasDiscriminator().HasValue("RoundBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("OuterDiameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("OuterDiameter");
|
||||
|
||||
b.HasDiscriminator().HasValue("RoundTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Size");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.HasDiscriminator().HasValue("SquareBar");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Size");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.ValueGeneratedOnUpdateSometimes()
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)")
|
||||
.HasColumnName("Wall");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.HasDiscriminator().HasValue("SquareTube");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.CuttingTool", "CuttingTool")
|
||||
.WithMany("Jobs")
|
||||
.HasForeignKey("CuttingToolId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("CuttingTool");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Parts")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("JobParts")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Stock")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany()
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithOne("Dimensions")
|
||||
.HasForeignKey("CutList.Web.Data.Entities.MaterialDimensions", "MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.PurchaseItem", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany()
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany()
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("StockItems")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany()
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("Transactions")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany()
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("SupplierOfferings")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany("Offerings")
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Navigation("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Navigation("Parts");
|
||||
|
||||
b.Navigation("Stock");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Navigation("Dimensions");
|
||||
|
||||
b.Navigation("JobParts");
|
||||
|
||||
b.Navigation("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Navigation("SupplierOfferings");
|
||||
|
||||
b.Navigation("Transactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Navigation("Offerings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddJobOptimizationResult : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "OptimizationResultJson",
|
||||
table: "Jobs",
|
||||
type: "nvarchar(max)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "OptimizedAt",
|
||||
table: "Jobs",
|
||||
type: "datetime2",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "OptimizationResultJson",
|
||||
table: "Jobs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "OptimizedAt",
|
||||
table: "Jobs");
|
||||
}
|
||||
}
|
||||
}
|
||||
962
CutList.Web/Migrations/20260216183131_MaterialDimensionsTPHtoTPT.Designer.cs
generated
Normal file
962
CutList.Web/Migrations/20260216183131_MaterialDimensionsTPHtoTPT.Designer.cs
generated
Normal file
@@ -0,0 +1,962 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CutList.Web.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20260216183131_MaterialDimensionsTPHtoTPT")]
|
||||
partial class MaterialDimensionsTPHtoTPT
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<bool>("IsDefault")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("KerfInches")
|
||||
.HasPrecision(6, 4)
|
||||
.HasColumnType("decimal(6,4)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("CuttingTools");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = 1,
|
||||
IsActive = true,
|
||||
IsDefault = true,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Bandsaw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 2,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.125m,
|
||||
Name = "Chop Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 3,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Cold Cut Saw"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = 4,
|
||||
IsActive = true,
|
||||
IsDefault = false,
|
||||
KerfInches = 0.0625m,
|
||||
Name = "Hacksaw"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Customer")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int?>("CuttingToolId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("JobNumber")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<DateTime?>("LockedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("OptimizationResultJson")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<DateTime?>("OptimizedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CuttingToolId");
|
||||
|
||||
b.HasIndex("JobNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.ToTable("JobParts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsCustomLength")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<int>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Priority")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("MaterialId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.ToTable("JobStocks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("Grade")
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Shape")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<string>("Size")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Materials");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("MaterialDimensions");
|
||||
|
||||
b.UseTptMappingStrategy();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.PurchaseItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<int?>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId");
|
||||
|
||||
b.ToTable("PurchaseItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<decimal>("LengthInches")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<int>("MaterialId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("QuantityOnHand")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MaterialId", "LengthInches")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<int?>("JobId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal?>("UnitPrice")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("JobId");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId");
|
||||
|
||||
b.ToTable("StockTransactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("ContactInfo")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime2")
|
||||
.HasDefaultValueSql("GETUTCDATE()");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Suppliers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Notes")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<string>("PartNumber")
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<decimal?>("Price")
|
||||
.HasPrecision(10, 2)
|
||||
.HasColumnType("decimal(10,2)");
|
||||
|
||||
b.Property<int>("StockItemId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("SupplierDescription")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("nvarchar(255)");
|
||||
|
||||
b.Property<int>("SupplierId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StockItemId");
|
||||
|
||||
b.HasIndex("SupplierId", "StockItemId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("SupplierOfferings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.AngleDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Leg1")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Leg2")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Leg1");
|
||||
|
||||
b.ToTable("AngleDimensions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.ChannelDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Flange")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Web")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.ToTable("ChannelDimensions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.FlatBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Thickness")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.ToTable("FlatBarDimensions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.IBeamDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("WeightPerFoot")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Height");
|
||||
|
||||
b.ToTable("IBeamDimensions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.PipeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("NominalSize")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<string>("Schedule")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("nvarchar(20)");
|
||||
|
||||
b.Property<decimal?>("Wall")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("NominalSize");
|
||||
|
||||
b.ToTable("PipeDimensions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RectangularTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Height")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Width")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Width");
|
||||
|
||||
b.ToTable("RectangularTubeDimensions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Diameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Diameter");
|
||||
|
||||
b.ToTable("RoundBarDimensions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("OuterDiameter")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("OuterDiameter");
|
||||
|
||||
b.ToTable("RoundTubeDimensions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareBarDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.ToTable("SquareBarDimensions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareTubeDimensions", b =>
|
||||
{
|
||||
b.HasBaseType("CutList.Web.Data.Entities.MaterialDimensions");
|
||||
|
||||
b.Property<decimal>("Size")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.Property<decimal>("Wall")
|
||||
.HasPrecision(10, 4)
|
||||
.HasColumnType("decimal(10,4)");
|
||||
|
||||
b.HasIndex("Size");
|
||||
|
||||
b.ToTable("SquareTubeDimensions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.CuttingTool", "CuttingTool")
|
||||
.WithMany("Jobs")
|
||||
.HasForeignKey("CuttingToolId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("CuttingTool");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobPart", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Parts")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("JobParts")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.JobStock", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany("Stock")
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany()
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("Material");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.MaterialDimensions", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithOne("Dimensions")
|
||||
.HasForeignKey("CutList.Web.Data.Entities.MaterialDimensions", "MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.PurchaseItem", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany()
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany()
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Material", "Material")
|
||||
.WithMany("StockItems")
|
||||
.HasForeignKey("MaterialId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Material");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockTransaction", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.Job", "Job")
|
||||
.WithMany()
|
||||
.HasForeignKey("JobId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("Transactions")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany()
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Job");
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SupplierOffering", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.StockItem", "StockItem")
|
||||
.WithMany("SupplierOfferings")
|
||||
.HasForeignKey("StockItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("CutList.Web.Data.Entities.Supplier", "Supplier")
|
||||
.WithMany("Offerings")
|
||||
.HasForeignKey("SupplierId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("StockItem");
|
||||
|
||||
b.Navigation("Supplier");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.AngleDimensions", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.MaterialDimensions", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("CutList.Web.Data.Entities.AngleDimensions", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.ChannelDimensions", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.MaterialDimensions", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("CutList.Web.Data.Entities.ChannelDimensions", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.FlatBarDimensions", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.MaterialDimensions", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("CutList.Web.Data.Entities.FlatBarDimensions", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.IBeamDimensions", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.MaterialDimensions", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("CutList.Web.Data.Entities.IBeamDimensions", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.PipeDimensions", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.MaterialDimensions", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("CutList.Web.Data.Entities.PipeDimensions", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RectangularTubeDimensions", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.MaterialDimensions", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("CutList.Web.Data.Entities.RectangularTubeDimensions", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundBarDimensions", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.MaterialDimensions", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("CutList.Web.Data.Entities.RoundBarDimensions", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.RoundTubeDimensions", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.MaterialDimensions", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("CutList.Web.Data.Entities.RoundTubeDimensions", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareBarDimensions", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.MaterialDimensions", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("CutList.Web.Data.Entities.SquareBarDimensions", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.SquareTubeDimensions", b =>
|
||||
{
|
||||
b.HasOne("CutList.Web.Data.Entities.MaterialDimensions", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("CutList.Web.Data.Entities.SquareTubeDimensions", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.CuttingTool", b =>
|
||||
{
|
||||
b.Navigation("Jobs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Job", b =>
|
||||
{
|
||||
b.Navigation("Parts");
|
||||
|
||||
b.Navigation("Stock");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Material", b =>
|
||||
{
|
||||
b.Navigation("Dimensions");
|
||||
|
||||
b.Navigation("JobParts");
|
||||
|
||||
b.Navigation("StockItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.StockItem", b =>
|
||||
{
|
||||
b.Navigation("SupplierOfferings");
|
||||
|
||||
b.Navigation("Transactions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("CutList.Web.Data.Entities.Supplier", b =>
|
||||
{
|
||||
b.Navigation("Offerings");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace CutList.Web.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class MaterialDimensionsTPHtoTPT : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// 1. Create the new TPT tables first (before dropping any columns)
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AngleDimensions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false),
|
||||
Leg1 = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false),
|
||||
Leg2 = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false),
|
||||
Thickness = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AngleDimensions", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AngleDimensions_MaterialDimensions_Id",
|
||||
column: x => x.Id,
|
||||
principalTable: "MaterialDimensions",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ChannelDimensions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false),
|
||||
Height = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false),
|
||||
Flange = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false),
|
||||
Web = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ChannelDimensions", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ChannelDimensions_MaterialDimensions_Id",
|
||||
column: x => x.Id,
|
||||
principalTable: "MaterialDimensions",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "FlatBarDimensions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false),
|
||||
Width = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false),
|
||||
Thickness = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_FlatBarDimensions", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_FlatBarDimensions_MaterialDimensions_Id",
|
||||
column: x => x.Id,
|
||||
principalTable: "MaterialDimensions",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "IBeamDimensions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false),
|
||||
Height = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false),
|
||||
WeightPerFoot = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_IBeamDimensions", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_IBeamDimensions_MaterialDimensions_Id",
|
||||
column: x => x.Id,
|
||||
principalTable: "MaterialDimensions",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "PipeDimensions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false),
|
||||
NominalSize = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false),
|
||||
Wall = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: true),
|
||||
Schedule = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PipeDimensions", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_PipeDimensions_MaterialDimensions_Id",
|
||||
column: x => x.Id,
|
||||
principalTable: "MaterialDimensions",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "RectangularTubeDimensions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false),
|
||||
Width = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false),
|
||||
Height = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false),
|
||||
Wall = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_RectangularTubeDimensions", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_RectangularTubeDimensions_MaterialDimensions_Id",
|
||||
column: x => x.Id,
|
||||
principalTable: "MaterialDimensions",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "RoundBarDimensions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false),
|
||||
Diameter = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_RoundBarDimensions", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_RoundBarDimensions_MaterialDimensions_Id",
|
||||
column: x => x.Id,
|
||||
principalTable: "MaterialDimensions",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "RoundTubeDimensions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false),
|
||||
OuterDiameter = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false),
|
||||
Wall = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_RoundTubeDimensions", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_RoundTubeDimensions_MaterialDimensions_Id",
|
||||
column: x => x.Id,
|
||||
principalTable: "MaterialDimensions",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "SquareBarDimensions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false),
|
||||
Size = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_SquareBarDimensions", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_SquareBarDimensions_MaterialDimensions_Id",
|
||||
column: x => x.Id,
|
||||
principalTable: "MaterialDimensions",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "SquareTubeDimensions",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false),
|
||||
Size = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false),
|
||||
Wall = table.Column<decimal>(type: "decimal(10,4)", precision: 10, scale: 4, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_SquareTubeDimensions", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_SquareTubeDimensions_MaterialDimensions_Id",
|
||||
column: x => x.Id,
|
||||
principalTable: "MaterialDimensions",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
// 2. Migrate existing data from the TPH table into the new TPT tables
|
||||
migrationBuilder.Sql(@"
|
||||
INSERT INTO RoundBarDimensions (Id, Diameter)
|
||||
SELECT Id, ISNULL(Diameter, 0) FROM MaterialDimensions WHERE DimensionType = 'RoundBar';
|
||||
|
||||
INSERT INTO RoundTubeDimensions (Id, OuterDiameter, Wall)
|
||||
SELECT Id, ISNULL(OuterDiameter, 0), ISNULL(Wall, 0) FROM MaterialDimensions WHERE DimensionType = 'RoundTube';
|
||||
|
||||
INSERT INTO FlatBarDimensions (Id, Width, Thickness)
|
||||
SELECT Id, ISNULL(Width, 0), ISNULL(Thickness, 0) FROM MaterialDimensions WHERE DimensionType = 'FlatBar';
|
||||
|
||||
INSERT INTO SquareBarDimensions (Id, Size)
|
||||
SELECT Id, ISNULL(Size, 0) FROM MaterialDimensions WHERE DimensionType = 'SquareBar';
|
||||
|
||||
INSERT INTO SquareTubeDimensions (Id, Size, Wall)
|
||||
SELECT Id, ISNULL(Size, 0), ISNULL(Wall, 0) FROM MaterialDimensions WHERE DimensionType = 'SquareTube';
|
||||
|
||||
INSERT INTO RectangularTubeDimensions (Id, Width, Height, Wall)
|
||||
SELECT Id, ISNULL(Width, 0), ISNULL(Height, 0), ISNULL(Wall, 0) FROM MaterialDimensions WHERE DimensionType = 'RectangularTube';
|
||||
|
||||
INSERT INTO AngleDimensions (Id, Leg1, Leg2, Thickness)
|
||||
SELECT Id, ISNULL(Leg1, 0), ISNULL(Leg2, 0), ISNULL(Thickness, 0) FROM MaterialDimensions WHERE DimensionType = 'Angle';
|
||||
|
||||
INSERT INTO ChannelDimensions (Id, Height, Flange, Web)
|
||||
SELECT Id, ISNULL(Height, 0), ISNULL(Flange, 0), ISNULL(Web, 0) FROM MaterialDimensions WHERE DimensionType = 'Channel';
|
||||
|
||||
INSERT INTO IBeamDimensions (Id, Height, WeightPerFoot)
|
||||
SELECT Id, ISNULL(Height, 0), ISNULL(WeightPerFoot, 0) FROM MaterialDimensions WHERE DimensionType = 'IBeam';
|
||||
|
||||
INSERT INTO PipeDimensions (Id, NominalSize, Wall, Schedule)
|
||||
SELECT Id, ISNULL(NominalSize, 0), Wall, Schedule FROM MaterialDimensions WHERE DimensionType = 'Pipe';
|
||||
");
|
||||
|
||||
// 3. Now drop the old TPH columns and indexes
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_MaterialDimensions_Diameter",
|
||||
table: "MaterialDimensions");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_MaterialDimensions_Height",
|
||||
table: "MaterialDimensions");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_MaterialDimensions_Leg1",
|
||||
table: "MaterialDimensions");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_MaterialDimensions_NominalSize",
|
||||
table: "MaterialDimensions");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_MaterialDimensions_OuterDiameter",
|
||||
table: "MaterialDimensions");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_MaterialDimensions_Size",
|
||||
table: "MaterialDimensions");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_MaterialDimensions_Width",
|
||||
table: "MaterialDimensions");
|
||||
|
||||
migrationBuilder.DropColumn(name: "Diameter", table: "MaterialDimensions");
|
||||
migrationBuilder.DropColumn(name: "DimensionType", table: "MaterialDimensions");
|
||||
migrationBuilder.DropColumn(name: "Flange", table: "MaterialDimensions");
|
||||
migrationBuilder.DropColumn(name: "Height", table: "MaterialDimensions");
|
||||
migrationBuilder.DropColumn(name: "Leg1", table: "MaterialDimensions");
|
||||
migrationBuilder.DropColumn(name: "Leg2", table: "MaterialDimensions");
|
||||
migrationBuilder.DropColumn(name: "NominalSize", table: "MaterialDimensions");
|
||||
migrationBuilder.DropColumn(name: "OuterDiameter", table: "MaterialDimensions");
|
||||
migrationBuilder.DropColumn(name: "Schedule", table: "MaterialDimensions");
|
||||
migrationBuilder.DropColumn(name: "Size", table: "MaterialDimensions");
|
||||
migrationBuilder.DropColumn(name: "Thickness", table: "MaterialDimensions");
|
||||
migrationBuilder.DropColumn(name: "Wall", table: "MaterialDimensions");
|
||||
migrationBuilder.DropColumn(name: "Web", table: "MaterialDimensions");
|
||||
migrationBuilder.DropColumn(name: "WeightPerFoot", table: "MaterialDimensions");
|
||||
migrationBuilder.DropColumn(name: "Width", table: "MaterialDimensions");
|
||||
|
||||
// 4. Create indexes on the new tables
|
||||
migrationBuilder.CreateIndex(name: "IX_AngleDimensions_Leg1", table: "AngleDimensions", column: "Leg1");
|
||||
migrationBuilder.CreateIndex(name: "IX_ChannelDimensions_Height", table: "ChannelDimensions", column: "Height");
|
||||
migrationBuilder.CreateIndex(name: "IX_FlatBarDimensions_Width", table: "FlatBarDimensions", column: "Width");
|
||||
migrationBuilder.CreateIndex(name: "IX_IBeamDimensions_Height", table: "IBeamDimensions", column: "Height");
|
||||
migrationBuilder.CreateIndex(name: "IX_PipeDimensions_NominalSize", table: "PipeDimensions", column: "NominalSize");
|
||||
migrationBuilder.CreateIndex(name: "IX_RectangularTubeDimensions_Width", table: "RectangularTubeDimensions", column: "Width");
|
||||
migrationBuilder.CreateIndex(name: "IX_RoundBarDimensions_Diameter", table: "RoundBarDimensions", column: "Diameter");
|
||||
migrationBuilder.CreateIndex(name: "IX_RoundTubeDimensions_OuterDiameter", table: "RoundTubeDimensions", column: "OuterDiameter");
|
||||
migrationBuilder.CreateIndex(name: "IX_SquareBarDimensions_Size", table: "SquareBarDimensions", column: "Size");
|
||||
migrationBuilder.CreateIndex(name: "IX_SquareTubeDimensions_Size", table: "SquareTubeDimensions", column: "Size");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// Re-add the TPH columns
|
||||
migrationBuilder.AddColumn<string>(name: "DimensionType", table: "MaterialDimensions", type: "nvarchar(21)", maxLength: 21, nullable: false, defaultValue: "");
|
||||
migrationBuilder.AddColumn<decimal>(name: "Diameter", table: "MaterialDimensions", type: "decimal(10,4)", precision: 10, scale: 4, nullable: true);
|
||||
migrationBuilder.AddColumn<decimal>(name: "Flange", table: "MaterialDimensions", type: "decimal(10,4)", precision: 10, scale: 4, nullable: true);
|
||||
migrationBuilder.AddColumn<decimal>(name: "Height", table: "MaterialDimensions", type: "decimal(10,4)", precision: 10, scale: 4, nullable: true);
|
||||
migrationBuilder.AddColumn<decimal>(name: "Leg1", table: "MaterialDimensions", type: "decimal(10,4)", precision: 10, scale: 4, nullable: true);
|
||||
migrationBuilder.AddColumn<decimal>(name: "Leg2", table: "MaterialDimensions", type: "decimal(10,4)", precision: 10, scale: 4, nullable: true);
|
||||
migrationBuilder.AddColumn<decimal>(name: "NominalSize", table: "MaterialDimensions", type: "decimal(10,4)", precision: 10, scale: 4, nullable: true);
|
||||
migrationBuilder.AddColumn<decimal>(name: "OuterDiameter", table: "MaterialDimensions", type: "decimal(10,4)", precision: 10, scale: 4, nullable: true);
|
||||
migrationBuilder.AddColumn<string>(name: "Schedule", table: "MaterialDimensions", type: "nvarchar(20)", maxLength: 20, nullable: true);
|
||||
migrationBuilder.AddColumn<decimal>(name: "Size", table: "MaterialDimensions", type: "decimal(10,4)", precision: 10, scale: 4, nullable: true);
|
||||
migrationBuilder.AddColumn<decimal>(name: "Thickness", table: "MaterialDimensions", type: "decimal(10,4)", precision: 10, scale: 4, nullable: true);
|
||||
migrationBuilder.AddColumn<decimal>(name: "Wall", table: "MaterialDimensions", type: "decimal(10,4)", precision: 10, scale: 4, nullable: true);
|
||||
migrationBuilder.AddColumn<decimal>(name: "Web", table: "MaterialDimensions", type: "decimal(10,4)", precision: 10, scale: 4, nullable: true);
|
||||
migrationBuilder.AddColumn<decimal>(name: "WeightPerFoot", table: "MaterialDimensions", type: "decimal(10,4)", precision: 10, scale: 4, nullable: true);
|
||||
migrationBuilder.AddColumn<decimal>(name: "Width", table: "MaterialDimensions", type: "decimal(10,4)", precision: 10, scale: 4, nullable: true);
|
||||
|
||||
// Migrate data back to TPH
|
||||
migrationBuilder.Sql(@"
|
||||
UPDATE md SET DimensionType = 'RoundBar', Diameter = rb.Diameter FROM MaterialDimensions md INNER JOIN RoundBarDimensions rb ON md.Id = rb.Id;
|
||||
UPDATE md SET DimensionType = 'RoundTube', OuterDiameter = rt.OuterDiameter, Wall = rt.Wall FROM MaterialDimensions md INNER JOIN RoundTubeDimensions rt ON md.Id = rt.Id;
|
||||
UPDATE md SET DimensionType = 'FlatBar', Width = fb.Width, Thickness = fb.Thickness FROM MaterialDimensions md INNER JOIN FlatBarDimensions fb ON md.Id = fb.Id;
|
||||
UPDATE md SET DimensionType = 'SquareBar', Size = sb.Size FROM MaterialDimensions md INNER JOIN SquareBarDimensions sb ON md.Id = sb.Id;
|
||||
UPDATE md SET DimensionType = 'SquareTube', Size = st.Size, Wall = st.Wall FROM MaterialDimensions md INNER JOIN SquareTubeDimensions st ON md.Id = st.Id;
|
||||
UPDATE md SET DimensionType = 'RectangularTube', Width = rt.Width, Height = rt.Height, Wall = rt.Wall FROM MaterialDimensions md INNER JOIN RectangularTubeDimensions rt ON md.Id = rt.Id;
|
||||
UPDATE md SET DimensionType = 'Angle', Leg1 = a.Leg1, Leg2 = a.Leg2, Thickness = a.Thickness FROM MaterialDimensions md INNER JOIN AngleDimensions a ON md.Id = a.Id;
|
||||
UPDATE md SET DimensionType = 'Channel', Height = c.Height, Flange = c.Flange, Web = c.Web FROM MaterialDimensions md INNER JOIN ChannelDimensions c ON md.Id = c.Id;
|
||||
UPDATE md SET DimensionType = 'IBeam', Height = ib.Height, WeightPerFoot = ib.WeightPerFoot FROM MaterialDimensions md INNER JOIN IBeamDimensions ib ON md.Id = ib.Id;
|
||||
UPDATE md SET DimensionType = 'Pipe', NominalSize = p.NominalSize, Wall = p.Wall, Schedule = p.Schedule FROM MaterialDimensions md INNER JOIN PipeDimensions p ON md.Id = p.Id;
|
||||
");
|
||||
|
||||
// Drop TPT tables
|
||||
migrationBuilder.DropTable(name: "AngleDimensions");
|
||||
migrationBuilder.DropTable(name: "ChannelDimensions");
|
||||
migrationBuilder.DropTable(name: "FlatBarDimensions");
|
||||
migrationBuilder.DropTable(name: "IBeamDimensions");
|
||||
migrationBuilder.DropTable(name: "PipeDimensions");
|
||||
migrationBuilder.DropTable(name: "RectangularTubeDimensions");
|
||||
migrationBuilder.DropTable(name: "RoundBarDimensions");
|
||||
migrationBuilder.DropTable(name: "RoundTubeDimensions");
|
||||
migrationBuilder.DropTable(name: "SquareBarDimensions");
|
||||
migrationBuilder.DropTable(name: "SquareTubeDimensions");
|
||||
|
||||
// Re-create TPH indexes
|
||||
migrationBuilder.CreateIndex(name: "IX_MaterialDimensions_Diameter", table: "MaterialDimensions", column: "Diameter");
|
||||
migrationBuilder.CreateIndex(name: "IX_MaterialDimensions_Height", table: "MaterialDimensions", column: "Height");
|
||||
migrationBuilder.CreateIndex(name: "IX_MaterialDimensions_Leg1", table: "MaterialDimensions", column: "Leg1");
|
||||
migrationBuilder.CreateIndex(name: "IX_MaterialDimensions_NominalSize", table: "MaterialDimensions", column: "NominalSize");
|
||||
migrationBuilder.CreateIndex(name: "IX_MaterialDimensions_OuterDiameter", table: "MaterialDimensions", column: "OuterDiameter");
|
||||
migrationBuilder.CreateIndex(name: "IX_MaterialDimensions_Size", table: "MaterialDimensions", column: "Size");
|
||||
migrationBuilder.CreateIndex(name: "IX_MaterialDimensions_Width", table: "MaterialDimensions", column: "Width");
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user