mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-06 18:05:44 -05:00
Auto Nest's Stock Options gets a Load from ledger button that reads the nest
material's free sheets and drops from MaterialLedger's API-key-protected
GET /api/stock/opennest. Ledger rows are request-local: the engine sees them as
ordinary NestPlateStock rows with ledger:{n} ids and finite quantities, and they
are never written to Nest.PlateOptions.
- OpenNest.Data/MaterialLedger: settings file, read-only client (X-Api-Key,
5 s timeout, closed LedgerStockResult set) and LedgerStockMapper (bundles and
sheets one row each, same-size drops grouped with ordered item codes, unknown
shapes and too-small items skipped, inch/mm conversion).
- Options: MaterialLedger URL and API key (DPAPI, CurrentUser) with Test.
- Auto Nest grid: Use, Qty and Source columns for every row; PlateOption gains
request-local Quantity and Id, which NestStockBuilder passes to the engine.
Ledger rows score by area; hand-typed costs stay saved.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
84 lines
2.9 KiB
C#
84 lines
2.9 KiB
C#
using OpenNest.Engine.Jobs;
|
|
using OpenNest.Geometry;
|
|
|
|
namespace OpenNest.Engine.Tests.Jobs;
|
|
|
|
public class NestStockBuilderTests
|
|
{
|
|
private static Plate Template() => new(new Size(48, 96))
|
|
{
|
|
Quantity = 7,
|
|
PartSpacing = 0.25,
|
|
EdgeSpacing = new Spacing(1, 2, 3, 4),
|
|
Quadrant = 3,
|
|
};
|
|
|
|
[Fact]
|
|
public void TemplateWithoutOptionsUsesUnlimitedStockAndSnapshotsSettings()
|
|
{
|
|
var template = Template();
|
|
var stock = Assert.Single(NestStockBuilder.FromTemplate(template, null));
|
|
Assert.Equal("plate", stock.Id);
|
|
Assert.Null(stock.Quantity);
|
|
Assert.Equal(template.Size, stock.Size);
|
|
Assert.Equal(template.PartSpacing, stock.PartSpacing);
|
|
Assert.Equal(template.EdgeSpacing, stock.EdgeSpacing);
|
|
Assert.Equal(template.Quadrant, stock.Quadrant);
|
|
template.Size = new Size(60, 120);
|
|
template.EdgeSpacing = default;
|
|
Assert.Equal(new Size(48, 96), stock.Size);
|
|
Assert.Equal(new Spacing(1, 2, 3, 4), stock.EdgeSpacing);
|
|
}
|
|
|
|
[Fact]
|
|
public void OptionsHaveStableDistinctIdsAndCopyTemplateSettings()
|
|
{
|
|
var template = Template();
|
|
var options = new[]
|
|
{
|
|
new PlateOption { Width = 60, Length = 120 },
|
|
new PlateOption { Width = 72, Length = 144 },
|
|
};
|
|
var stock = NestStockBuilder.FromTemplate(template, options, quantityPerOption: 2);
|
|
Assert.Equal(new[] { "option-0", "option-1" }, stock.Select(s => s.Id));
|
|
Assert.Equal(new[] { new Size(60, 120), new Size(72, 144) }, stock.Select(s => s.Size));
|
|
Assert.All(stock, s =>
|
|
{
|
|
Assert.Equal(2, s.Quantity);
|
|
Assert.Equal(template.EdgeSpacing, s.EdgeSpacing);
|
|
Assert.Equal(template.PartSpacing, s.PartSpacing);
|
|
Assert.Equal(template.Quadrant, s.Quadrant);
|
|
});
|
|
Assert.Equal(new Size(48, 96), template.Size);
|
|
}
|
|
|
|
[Fact]
|
|
public void SinglePlateOffersExactlyOneSheetNotPlateRepeatQuantity()
|
|
{
|
|
var stock = Assert.Single(NestStockBuilder.SinglePlate(Template()));
|
|
Assert.Equal(1, stock.Quantity);
|
|
}
|
|
|
|
[Fact]
|
|
public void MissingTemplateIsRejected()
|
|
{
|
|
Assert.Throws<ArgumentNullException>(() => NestStockBuilder.FromTemplate(null, null));
|
|
Assert.Throws<ArgumentNullException>(() => NestStockBuilder.SinglePlate(null));
|
|
}
|
|
|
|
[Fact]
|
|
public void OptionQuantityAndIdWinOverTheDefaults()
|
|
{
|
|
var options = new[]
|
|
{
|
|
new PlateOption { Width = 48, Length = 96, Quantity = 2, Id = "ledger:1" },
|
|
new PlateOption { Width = 60, Length = 120 },
|
|
};
|
|
var stock = NestStockBuilder.FromTemplate(Template(), options, quantityPerOption: 5);
|
|
Assert.Equal("ledger:1", stock[0].Id);
|
|
Assert.Equal(2, stock[0].Quantity);
|
|
Assert.Equal("option-1", stock[1].Id);
|
|
Assert.Equal(5, stock[1].Quantity);
|
|
}
|
|
}
|