mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-27 11:31:07 -04:00
Auto Nest on MaterialLedger stock now records which inventory item each
physical sheet uses, so ML can hold exactly those items instead of matching
by size.
- Core: Plate.StockItemCodes (entry i = sheet i of Quantity; empty = uncoded).
Core invalidates it: a resize clears it (turning the sheet keeps it), a lower
Quantity truncates it, a nest material/grade/thickness change clears every
plate's, ClearAll clears it and new plates never copy it.
- Desktop Auto Nest: after a kept proposal is committed, LedgerStockAssignment
deals each ledger row's codes to its plates in Nest.Plates order (bundles
repeat their code; clamp-removal variants use their base row). Rejected,
discarded or cancelled proposals never reach it. NestPipelineCommit.PairWithStock
pairs committed plates with their solved stock; the engine stays ledger-free.
- Plate list tooltip: "Stock: CODE (location)"; locations are display-only.
- nest.json: optional plate stockItemCodes, omitted when empty, read leniently.
- /api/nests/{id}/sheets: new plates[] { index, width, length, quantity,
stockItemCodes }; sheets[] unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
137 lines
4.2 KiB
C#
137 lines
4.2 KiB
C#
using OpenNest.Geometry;
|
|
using OpenNest.Shapes;
|
|
|
|
namespace OpenNest.Tests;
|
|
|
|
/// <summary>
|
|
/// <see cref="Plate.StockItemCodes"/> name the inventory item of each physical sheet, and Core
|
|
/// drops them as soon as they stop describing the sheet.
|
|
/// </summary>
|
|
public class PlateStockItemCodesTests
|
|
{
|
|
private static Plate Coded(int quantity, params string[] codes)
|
|
{
|
|
var plate = new Plate(30, 52.5) { Quantity = quantity };
|
|
plate.StockItemCodes = codes;
|
|
return plate;
|
|
}
|
|
|
|
[Fact]
|
|
public void NewPlate_HasNoCodes()
|
|
{
|
|
Assert.Empty(new Plate().StockItemCodes);
|
|
Assert.Empty(new Nest().CreatePlate().StockItemCodes);
|
|
}
|
|
|
|
[Fact]
|
|
public void Resize_ClearsCodes_ButTurningTheSheetKeepsThem()
|
|
{
|
|
var plate = Coded(1, "DROP-1182");
|
|
|
|
plate.Size = new Size(52.5, 30);
|
|
Assert.Equal(["DROP-1182"], plate.StockItemCodes);
|
|
plate.Rotate90(RotationType.CW);
|
|
Assert.Equal(["DROP-1182"], plate.StockItemCodes);
|
|
|
|
plate.Size = new Size(30, 60);
|
|
Assert.Empty(plate.StockItemCodes);
|
|
}
|
|
|
|
[Fact]
|
|
public void LowerQuantity_TruncatesCodes_AndHigherQuantityLeavesTheExtraSheetsUncoded()
|
|
{
|
|
var plate = Coded(3, "SHEET-456", "SHEET-456", "SHEET-789");
|
|
|
|
plate.Quantity = 2;
|
|
Assert.Equal(["SHEET-456", "SHEET-456"], plate.StockItemCodes);
|
|
|
|
plate.Quantity = 5;
|
|
Assert.Equal(["SHEET-456", "SHEET-456"], plate.StockItemCodes);
|
|
|
|
plate.Quantity = 0;
|
|
Assert.Empty(plate.StockItemCodes);
|
|
}
|
|
|
|
[Fact]
|
|
public void Codes_BeyondQuantityAreDropped_AndBlankCodesAreRefused()
|
|
{
|
|
Assert.Equal(["A"], Coded(1, "A", "B").StockItemCodes);
|
|
Assert.Throws<ArgumentException>(() => Coded(2, "A", " "));
|
|
Assert.Throws<ArgumentNullException>(() => new Plate().StockItemCodes = null);
|
|
}
|
|
|
|
[Fact]
|
|
public void NestMaterialOrThicknessChange_ClearsEveryPlatesCodes()
|
|
{
|
|
var nest = new Nest { Material = new Material("Mild Steel", "A36"), Thickness = 0.25 };
|
|
var first = Coded(1, "DROP-1");
|
|
var second = Coded(2, "SHEET-2", "SHEET-2");
|
|
nest.Plates.Add(first);
|
|
nest.Plates.Add(second);
|
|
|
|
// Same material written differently, and the same thickness, are the same stock.
|
|
nest.Material = new Material(" mild steel ", "a36", 0.284);
|
|
nest.Thickness = 0.25;
|
|
Assert.Equal(["DROP-1"], first.StockItemCodes);
|
|
Assert.Equal(["SHEET-2", "SHEET-2"], second.StockItemCodes);
|
|
|
|
nest.Material = new Material("Mild Steel", "A572");
|
|
Assert.Empty(first.StockItemCodes);
|
|
Assert.Empty(second.StockItemCodes);
|
|
|
|
first.StockItemCodes = ["DROP-1"];
|
|
nest.Thickness = 0.1875;
|
|
Assert.Empty(first.StockItemCodes);
|
|
|
|
first.StockItemCodes = ["DROP-1"];
|
|
nest.Material = new Material("Stainless");
|
|
Assert.Empty(first.StockItemCodes);
|
|
}
|
|
|
|
[Fact]
|
|
public void PlateFromAnExistingPlate_CopiesNoCodes()
|
|
{
|
|
var nest = new Nest();
|
|
var coded = Coded(1, "DROP-1182");
|
|
nest.Plates.Add(coded);
|
|
|
|
nest.PlateDefaults.SetFromExisting(coded);
|
|
var copy = nest.CreatePlate();
|
|
|
|
Assert.Equal(coded.Size, copy.Size);
|
|
Assert.Empty(copy.StockItemCodes);
|
|
}
|
|
|
|
[Fact]
|
|
public void ClearAll_DropsTheRemainingPlatesCodes()
|
|
{
|
|
var nest = new Nest();
|
|
var drawing = new RectangleShape { Name = "SYN", Length = 4, Width = 2 }.GetDrawing();
|
|
nest.Drawings.Add(drawing);
|
|
var plate = Coded(2, "SHEET-1", "SHEET-1");
|
|
plate.Parts.Add(new Part(drawing));
|
|
nest.Plates.Add(plate);
|
|
using var manager = new PlateManager(nest);
|
|
|
|
manager.ClearAll();
|
|
|
|
Assert.Empty(nest.Plates[0].StockItemCodes);
|
|
}
|
|
|
|
[Fact]
|
|
public void Codes_AreNotQuantityAccounting()
|
|
{
|
|
var nest = new Nest();
|
|
var drawing = new RectangleShape { Name = "SYN", Length = 4, Width = 2 }.GetDrawing();
|
|
nest.Drawings.Add(drawing);
|
|
var plate = new Plate(48, 96) { Quantity = 2 };
|
|
nest.Plates.Add(plate);
|
|
plate.Parts.Add(new Part(drawing));
|
|
|
|
plate.StockItemCodes = ["SHEET-1", "SHEET-1"];
|
|
plate.StockItemCodes = [];
|
|
|
|
Assert.Equal(2, drawing.Quantity.Nested);
|
|
}
|
|
}
|