diff --git a/OpenNest.Data/MaterialLedger/LedgerHoldSummary.cs b/OpenNest.Data/MaterialLedger/LedgerHoldSummary.cs
index c77ea34b..74496c91 100644
--- a/OpenNest.Data/MaterialLedger/LedgerHoldSummary.cs
+++ b/OpenNest.Data/MaterialLedger/LedgerHoldSummary.cs
@@ -50,7 +50,7 @@ public sealed class LedgerHoldSummary
public int OpenHolds { get; }
- /// Holds in any other status: closed, cancelled, exception or unknown.
+ /// Holds in any other status: released, closed, cancelled, exception or unknown.
public int ClosedHolds { get; }
/// Sheets without an open hold.
diff --git a/OpenNest.Data/MaterialLedger/LedgerHolds.cs b/OpenNest.Data/MaterialLedger/LedgerHolds.cs
index 13a1f1ae..fe761171 100644
--- a/OpenNest.Data/MaterialLedger/LedgerHolds.cs
+++ b/OpenNest.Data/MaterialLedger/LedgerHolds.cs
@@ -30,13 +30,13 @@ public static class LedgerHoldStatuses
public const string Exception = "Exception";
///
- /// True for holds that still keep a sheet for the nest and need closing out. Closed,
- /// cancelled, exception and unknown statuses are not open.
+ /// True for holds that still keep a sheet for the nest and need closing out: the statuses ML
+ /// counts against free stock. Released means the hold was let go, not sent to cutting, so it
+ /// is not open; nor are closed, cancelled, exception and unknown statuses.
///
public static bool IsOpen(string? status) =>
string.Equals(status, Proposed, StringComparison.OrdinalIgnoreCase)
|| string.Equals(status, Approved, StringComparison.OrdinalIgnoreCase)
- || string.Equals(status, Released, StringComparison.OrdinalIgnoreCase)
|| string.Equals(status, AwaitingCloseout, StringComparison.OrdinalIgnoreCase);
}
diff --git a/OpenNest.Tests/Data/LedgerHoldSummaryTests.cs b/OpenNest.Tests/Data/LedgerHoldSummaryTests.cs
index b1e75d14..1081af9c 100644
--- a/OpenNest.Tests/Data/LedgerHoldSummaryTests.cs
+++ b/OpenNest.Tests/Data/LedgerHoldSummaryTests.cs
@@ -23,19 +23,20 @@ public class LedgerHoldSummaryTests
[Fact]
public void AllSheetsHeld()
{
- var summary = Summarize(4, Hold("Approved"), Hold("Released"), Hold("Proposed"), Hold("AwaitingCloseout"));
+ var summary = Summarize(4, Hold("Approved"), Hold("Approved"), Hold("Proposed"), Hold("AwaitingCloseout"));
Assert.Equal(LedgerHoldState.Held, summary.State);
Assert.Equal("Ledger: 4 of 4 sheets held", summary.Text);
Assert.Equal(0, summary.ShortSheets);
}
+ // ML's Release lets a hold go: the sheet is free stock again, so the nest is short of it.
[Fact]
- public void ClosedAndUnknownHoldsDoNotCountAsHeld()
+ public void ReleasedClosedAndUnknownHoldsDoNotCountAsHeld()
{
- var summary = Summarize(4, Hold("Approved"), Hold("Released"), Hold("ClosedConsumed"),
+ var summary = Summarize(4, Hold("Approved"), Hold("Proposed"), Hold("Released"), Hold("ClosedConsumed"),
Hold("Cancelled"), Hold("Exception"), Hold("OnTruck"));
Assert.Equal(LedgerHoldState.Short, summary.State);
- Assert.Equal((2, 4, 2), (summary.OpenHolds, summary.ClosedHolds, summary.ShortSheets));
+ Assert.Equal((2, 5, 2), (summary.OpenHolds, summary.ClosedHolds, summary.ShortSheets));
Assert.Equal("Ledger: 2 of 4 held, 2 short", summary.Text);
}
diff --git a/OpenNest.WinForms.Tests/Forms/NestHoldStatusTests.cs b/OpenNest.WinForms.Tests/Forms/NestHoldStatusTests.cs
index c3513473..d9ec6dc5 100644
--- a/OpenNest.WinForms.Tests/Forms/NestHoldStatusTests.cs
+++ b/OpenNest.WinForms.Tests/Forms/NestHoldStatusTests.cs
@@ -47,7 +47,7 @@ public class NestHoldStatusTests
var document = Bound(sheets: 4);
var changes = 0;
HttpRequestMessage seen = null;
- var status = Status(document, databaseMode: true, request => { seen = request; return Holds("Approved", "Released"); });
+ var status = Status(document, databaseMode: true, request => { seen = request; return Holds("Approved", "Proposed", "Released"); });
status.Changed += (_, _) => changes++;
await status.RefreshAfterSaveAsync();
@@ -56,7 +56,7 @@ public class NestHoldStatusTests
Assert.False(status.IsRefreshing);
Assert.Equal("Ledger: 2 of 4 held, 2 short", status.Summary.Text);
Assert.Contains(document.RemoteId.ToString("D"), seen.RequestUri!.Query);
- Assert.Equal($"http://ledger:5000/Nests/{document.RemoteId:D}", status.LedgerNestUrl);
+ Assert.Equal($"http://ledger:5000/Nests/Details/{document.RemoteId:D}", status.LedgerNestUrl);
Assert.Equal(2, changes); // Refreshing, then the answer.
// An on-demand refresh counts the sheets of the last save, not unsaved edits.
diff --git a/OpenNest/Forms/NestHoldStatus.cs b/OpenNest/Forms/NestHoldStatus.cs
index 4e772a63..70b467f3 100644
--- a/OpenNest/Forms/NestHoldStatus.cs
+++ b/OpenNest/Forms/NestHoldStatus.cs
@@ -60,7 +60,7 @@ namespace OpenNest.Forms
public bool IsVisible => IsApplicable && (Summary != null || IsRefreshing);
/// The nest's page in MaterialLedger, where its holds are closed out.
- public string LedgerNestUrl => $"{ledgerSettings().BaseUrl.TrimEnd('/')}/Nests/{document.RemoteId:D}";
+ public string LedgerNestUrl => $"{ledgerSettings().BaseUrl.TrimEnd('/')}/Nests/Details/{document.RemoteId:D}";
///
/// Rereads the holds after the nest was opened or saved, counting the sheets of the nest
diff --git a/docs/nest-storage.md b/docs/nest-storage.md
index ca165409..6c50e274 100644
--- a/docs/nest-storage.md
+++ b/docs/nest-storage.md
@@ -172,9 +172,11 @@ Each read uses `MaterialLedgerClient.GetHoldsAsync` (`GET /api/stock/holds?openN
**Summary.** `LedgerHoldSummary` (`OpenNest.Data/MaterialLedger/`) compares the
open holds with the sheets of the last open or save: the sum of plate quantities,
-skipping the trailing empty plate as `/sheets` does. Proposed, Approved, Released
-and AwaitingCloseout holds are open. Closed, cancelled, exception and unknown
-statuses are listed but not counted; unknown status strings are kept as sent.
+skipping the trailing empty plate as `/sheets` does. Proposed, Approved and
+AwaitingCloseout holds are open, the statuses ML counts against free stock. Released
+means the hold was let go, not sent to cutting, so it is listed but not counted, as
+are closed, cancelled, exception and unknown statuses; unknown status strings are kept
+as sent.
| Holds | Label |
|-------|-------|
@@ -189,7 +191,7 @@ nest's material label beside it.
**Has Been Cut.** When Nest Info changes the status to Has Been Cut, the desktop
rereads the holds. If any are still open, it shows a non-modal note, "Close out N holds
-in MaterialLedger", with a link to `{ledgerUrl}/Nests/{id}`. Closeout happens there.
+in MaterialLedger", with a link to `{ledgerUrl}/Nests/Details/{id}`. Closeout happens there.
The note never blocks the dialog, save or post, and posting is not gated on holds.
### Generated nest names