Files
OpenNest/OpenNest.IO/Bom/BomAnalyzer.cs
T
aj b83aeb0e41 fix(bom): find the drawing for rows that still need a thickness
A BOM row with a file name and a matching drawing but a blank thickness
was reported "No DXF" and locked, so its thickness could not be entered
and the part was dropped: BomAnalyzer skips such items before looking
for a drawing, and the form only knew drawings the analyzer matched.
A row with a blank material read "Matched" but was silently left out
of every group.

Rows now resolve their drawing through a DrawingFileIndex shared with
BomAnalyzer (whose behavior and tests are unchanged), and a row's status
is computed from its values:

  Ready | Needs material | Needs thickness | No drawing found | No file name

Rows with a drawing stay editable, and an edit updates the status cell.
Groups take only Ready rows (a zero, negative or non-finite thickness is
not Ready). The summary line counts ready rows and each problem.

Build_RowWithoutThickness_StillFindsItsDrawing failed at its DxfPath
assertion against the previous commit. Mutation reds: removing the
material check, accepting zero or non-finite thickness, skipping the
drawing for rows without thickness, and grouping without the status
each fail the Bom tests.

Project Memory: b8e3a978-2cfe-4fe7-b2a5-eb01c6c54679
2026-10-05 17:50:54 -04:00

83 lines
2.6 KiB
C#

using System.Collections.Generic;
using System.Linq;
namespace OpenNest.IO.Bom
{
public class BomAnalysis
{
public List<MaterialGroup> Groups { get; set; } = new List<MaterialGroup>();
public List<BomItem> Skipped { get; set; } = new List<BomItem>();
public List<BomItem> Unmatched { get; set; } = new List<BomItem>();
}
public class MaterialGroup
{
public string Material { get; set; }
public double Thickness { get; set; }
public List<MatchedPart> Parts { get; set; } = new List<MatchedPart>();
}
public class MatchedPart
{
public BomItem Item { get; set; }
public string DxfPath { get; set; }
}
public static class BomAnalyzer
{
public static BomAnalysis Analyze(List<BomItem> items, string dxfFolder)
{
var result = new BomAnalysis();
// Case-insensitive lookup of the drawing files in the folder (if it exists)
var drawings = DrawingFileIndex.Load(dxfFolder);
// Partition items into: skipped, unmatched, or matched (grouped)
var matched = new List<MatchedPart>();
foreach (var item in items)
{
if (string.IsNullOrWhiteSpace(item.FileName) || !item.Thickness.HasValue)
{
result.Skipped.Add(item);
continue;
}
if (!drawings.FolderExists)
{
// No folder to search — group items without a DXF path
matched.Add(new MatchedPart { Item = item, DxfPath = null });
}
else if (drawings.Find(item.FileName) is { } dxfPath)
{
matched.Add(new MatchedPart { Item = item, DxfPath = dxfPath });
}
else
{
result.Unmatched.Add(item);
}
}
// Group matched parts by material + thickness
var groups = matched
.GroupBy(p => new
{
Material = (p.Item.Material ?? "").ToUpperInvariant(),
Thickness = p.Item.Thickness.Value,
})
.Select(g => new MaterialGroup
{
Material = g.First().Item.Material ?? "",
Thickness = g.Key.Thickness,
Parts = g.ToList(),
})
.OrderBy(g => g.Material)
.ThenBy(g => g.Thickness)
.ToList();
result.Groups = groups;
return result;
}
}
}