diff --git a/OpenNest.Reporting/NestPdfWriter.cs b/OpenNest.Reporting/NestPdfWriter.cs index 890833fc..4b3ba2c8 100644 --- a/OpenNest.Reporting/NestPdfWriter.cs +++ b/OpenNest.Reporting/NestPdfWriter.cs @@ -131,7 +131,7 @@ public static class NestPdfWriter else { var plates = AddTable(section, 60, 200, 80, 100, 100); - AddHeader(plates, "Plate", "Stock size", "Quantity", "Parts/sheet", "Utilization"); + AddHeader(plates, "Plate", "Stock size", "Quantity", "Parts per plate", "Utilization"); foreach (var plate in snapshot.Plates) AddRow(plates, $"Plate {plate.Number}", false, plate.Number.ToString(Invariant), SheetSize(snapshot, plate), plate.Copies.ToString(Invariant), plate.Parts.Length.ToString(Invariant), Percent(plate.Utilization)); @@ -172,13 +172,13 @@ public static class NestPdfWriter title.Format.Font.Size = 13; title.Format.Font.Bold = true; - var info = AddTable(section, 80, 280, 90, 270); + var info = AddTable(section, 100, 260, 90, 270); info.Borders.Visible = false; AddInfoRow(info, "Nest:", snapshot.Name, "Sheet size:", SheetSize(snapshot, plate)); AddInfoRow(info, "Material:", Material(snapshot), "Quantity:", plate.Copies.ToString(Invariant)); AddInfoRow(info, "Thickness:", Thickness(snapshot), "Part spacing:", $"{Number(plate.PartSpacing)} {snapshot.Units}"); - AddInfoRow(info, "Parts/sheet:", plate.Parts.Length.ToString(Invariant), "Utilization:", + AddInfoRow(info, "Parts per plate:", plate.Parts.Length.ToString(Invariant), "Utilization:", Percent(plate.Utilization) + " (net part area / full sheet)"); var overview = AddReservedArea(section, null, plan.Overview.Size.Height); @@ -204,7 +204,7 @@ public static class NestPdfWriter var perSheet = plate.Parts.GroupBy(part => part.ReportId, StringComparer.Ordinal) .ToDictionary(group => group.Key, group => (long)group.Count(), StringComparer.Ordinal); var parts = AddTable(section, 60, 400, 110, 150); - AddHeader(parts, "ID", "Drawing", "Qty/sheet", $"Total ({plate.Copies} plates)"); + AddHeader(parts, "ID", "Drawing", "Parts per plate", $"Total ({plate.Copies} plates)"); foreach (var drawing in snapshot.Drawings.Where(drawing => perSheet.ContainsKey(drawing.Id))) { var count = perSheet[drawing.Id]; diff --git a/OpenNest.Tests/Reporting/NestPdfLayoutTests.cs b/OpenNest.Tests/Reporting/NestPdfLayoutTests.cs index ef228308..9a7e37a3 100644 --- a/OpenNest.Tests/Reporting/NestPdfLayoutTests.cs +++ b/OpenNest.Tests/Reporting/NestPdfLayoutTests.cs @@ -85,7 +85,7 @@ public sealed class NestPdfLayoutTests : IDisposable // Every page carrying plate-table rows repeats the table heading. var tablePages = platePages.Where(page => Regex.IsMatch(page, @"(?m)^R\d{3}\s+Demand \d{2}\s+1\s+2$")).ToList(); Assert.True(tablePages.Count >= 2, $"plate table pages: {tablePages.Count}"); - Assert.All(tablePages, page => Assert.Matches(@"ID\s+Drawing\s+Qty/sheet\s+Total \(2 plates\)", page)); + Assert.All(tablePages, page => Assert.Matches(@"ID\s+Drawing\s+Parts per plate\s+Total \(2 plates\)", page)); var plateText = string.Join("\n", platePages); for (var i = 0; i < 40; i++) Assert.Matches($@"R{i + 4:D3}\s+Demand {i:D2}\s+1\s+2", plateText); @@ -114,7 +114,7 @@ public sealed class NestPdfLayoutTests : IDisposable var platePage = Array.FindIndex(pages, page => page.Contains("Plate 1 of 4")); Assert.True(summaryPage >= 0 && platePage > summaryPage); Assert.Equal(longName.Replace(" ", ""), ColumnText(path, summaryPage + 1, "Drawing", "Required")); - Assert.Equal(longName.Replace(" ", ""), ColumnText(path, platePage + 1, "Drawing", "Qty/sheet")); + Assert.Equal(longName.Replace(" ", ""), ColumnText(path, platePage + 1, "Drawing", "Parts")); // Column headings stay on one line. Assert.Matches(@"ID\s+Part\s+Drawing\s+Required\s+Nested\s+Shortage\s+Extra\s+Plates", pages[summaryPage]); } diff --git a/OpenNest.Tests/Reporting/NestPdfWriterTests.cs b/OpenNest.Tests/Reporting/NestPdfWriterTests.cs index 76fa7d11..b19971db 100644 --- a/OpenNest.Tests/Reporting/NestPdfWriterTests.cs +++ b/OpenNest.Tests/Reporting/NestPdfWriterTests.cs @@ -53,6 +53,8 @@ public sealed class NestPdfWriterTests : IDisposable Assert.Matches(@"R003\s+Bracket\s+7\s+2\s+5\s+0\s+1", summary); Assert.Matches(@"R004\s+Unplaced\s+3\s+0\s+3\s+0\s+-", summary); Assert.Contains("Page 1 of 2", summary); + Assert.Matches(@"Stock size\s+Quantity\s+Parts per plate", summary); + Assert.DoesNotContain("Parts/sheet", summary + plate); Assert.DoesNotContain("Copies", summary + plate); Assert.Contains("Plate 1", plate); diff --git a/docs/nest-reports.md b/docs/nest-reports.md index e8febc49..f666264a 100644 --- a/docs/nest-reports.md +++ b/docs/nest-reports.md @@ -54,7 +54,9 @@ each plate's part table across pages with the heading row repeated, and notes flow as an ordinary paragraph. No row, table or note text is ever dropped or truncated to fit a page. -Stock sizes read **width x length** in job units. Plate counts use **Quantity**. +Stock sizes read **width x length** in job units. Plate tables use **Quantity** +and **Parts per plate**; the plate quantity multiplies per-plate part counts for +the totals. Each part ID is centered on its part's pole of inaccessibility (`PolyLabel`, the same method `PlateView`'s `LayoutPart` uses), computed on a