mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-06 10:42:12 -04:00
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
This commit is contained in:
1 parent
f4d1d45c81
commit
b83aeb0e41
12 files changed
+315
-122
No files matched your search
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenNest.IO.Bom
|
||||
@@ -31,23 +29,8 @@ namespace OpenNest.IO.Bom
|
||||
{
|
||||
var result = new BomAnalysis();
|
||||
|
||||
// Build a case-insensitive lookup of DXF files in the folder (if it exists)
|
||||
var folderExists = Directory.Exists(dxfFolder);
|
||||
var dxfFiles = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
if (folderExists)
|
||||
{
|
||||
foreach (var file in Directory.GetFiles(dxfFolder, "*.dxf"))
|
||||
{
|
||||
var nameWithoutExt = Path.GetFileNameWithoutExtension(file);
|
||||
dxfFiles[nameWithoutExt] = file;
|
||||
}
|
||||
foreach (var file in Directory.GetFiles(dxfFolder, "*.dwg"))
|
||||
{
|
||||
var nameWithoutExt = Path.GetFileNameWithoutExtension(file);
|
||||
dxfFiles.TryAdd(nameWithoutExt, file);
|
||||
}
|
||||
}
|
||||
// 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>();
|
||||
@@ -60,20 +43,12 @@ namespace OpenNest.IO.Bom
|
||||
continue;
|
||||
}
|
||||
|
||||
var lookupName = item.FileName;
|
||||
|
||||
if (
|
||||
lookupName.EndsWith(".dxf", StringComparison.OrdinalIgnoreCase)
|
||||
|| lookupName.EndsWith(".dwg", StringComparison.OrdinalIgnoreCase)
|
||||
)
|
||||
lookupName = Path.GetFileNameWithoutExtension(lookupName);
|
||||
|
||||
if (!folderExists)
|
||||
if (!drawings.FolderExists)
|
||||
{
|
||||
// No folder to search — group items without a DXF path
|
||||
matched.Add(new MatchedPart { Item = item, DxfPath = null });
|
||||
}
|
||||
else if (dxfFiles.TryGetValue(lookupName, out var dxfPath))
|
||||
else if (drawings.Find(item.FileName) is { } dxfPath)
|
||||
{
|
||||
matched.Add(new MatchedPart { Item = item, DxfPath = dxfPath });
|
||||
}
|
||||
|
||||
@@ -35,18 +35,13 @@ public sealed class BomImportGroup
|
||||
public static class BomImportGroups
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the groups of editable rows that have a drawing, a material
|
||||
/// and a thickness. Material is compared case-insensitively. Groups are
|
||||
/// ordered by material, then thickness.
|
||||
/// Returns the groups of <see cref="BomRowStatus.Ready"/> rows. Material
|
||||
/// is compared case-insensitively. Groups are ordered by material, then
|
||||
/// thickness.
|
||||
/// </summary>
|
||||
public static List<BomImportGroup> Build(IEnumerable<BomPartRow> rows)
|
||||
{
|
||||
return rows.Where(p =>
|
||||
p.IsEditable
|
||||
&& !string.IsNullOrWhiteSpace(p.Material)
|
||||
&& p.Thickness.HasValue
|
||||
&& !string.IsNullOrWhiteSpace(p.DxfPath)
|
||||
)
|
||||
return rows.Where(p => p.Status == BomRowStatus.Ready)
|
||||
.GroupBy(p => new
|
||||
{
|
||||
Material = p.Material.ToUpperInvariant(),
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace OpenNest.IO.Bom;
|
||||
|
||||
@@ -11,71 +9,33 @@ namespace OpenNest.IO.Bom;
|
||||
public static class BomImportRows
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns one row per BOM item, in BOM order, with the drawing file it
|
||||
/// matched in <paramref name="dxfFolder"/> and its import status.
|
||||
/// Returns one row per BOM item, in BOM order. Every row with a file
|
||||
/// name gets the drawing it matches in <paramref name="dxfFolder"/>,
|
||||
/// whether or not its material and thickness are filled in.
|
||||
/// </summary>
|
||||
public static List<BomPartRow> Build(List<BomItem> items, string dxfFolder)
|
||||
{
|
||||
var analysis = BomAnalyzer.Analyze(items, dxfFolder);
|
||||
var matchedPaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var group in analysis.Groups)
|
||||
foreach (var part in group.Parts)
|
||||
if (part.DxfPath != null)
|
||||
matchedPaths[LookupName(part.Item.FileName)] = part.DxfPath;
|
||||
|
||||
var index = DrawingFileIndex.Load(dxfFolder);
|
||||
var rows = new List<BomPartRow>();
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
var row = new BomPartRow
|
||||
{
|
||||
ItemNum = item.ItemNum,
|
||||
FileName = item.FileName,
|
||||
Qty = item.Qty,
|
||||
Description = item.Description,
|
||||
Material = item.Material,
|
||||
Thickness = item.Thickness,
|
||||
};
|
||||
|
||||
if (string.IsNullOrWhiteSpace(item.FileName))
|
||||
{
|
||||
row.Status = "Skipped";
|
||||
row.IsEditable = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (matchedPaths.TryGetValue(LookupName(item.FileName), out var dxfPath))
|
||||
rows.Add(
|
||||
new BomPartRow
|
||||
{
|
||||
row.DxfPath = dxfPath;
|
||||
row.Status = "Matched";
|
||||
row.IsEditable = true;
|
||||
ItemNum = item.ItemNum,
|
||||
FileName = item.FileName,
|
||||
Qty = item.Qty,
|
||||
Description = item.Description,
|
||||
Material = item.Material,
|
||||
Thickness = item.Thickness,
|
||||
DxfPath = string.IsNullOrWhiteSpace(item.FileName)
|
||||
? null
|
||||
: index.Find(item.FileName),
|
||||
}
|
||||
else
|
||||
{
|
||||
row.Status = "No DXF";
|
||||
row.IsEditable = false;
|
||||
}
|
||||
}
|
||||
|
||||
rows.Add(row);
|
||||
);
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The drawing name a BOM file name refers to: the name without a
|
||||
/// .dxf or .dwg extension, as <see cref="BomAnalyzer"/> matches it.
|
||||
/// </summary>
|
||||
private static string LookupName(string fileName)
|
||||
{
|
||||
fileName ??= "";
|
||||
if (
|
||||
fileName.EndsWith(".dxf", StringComparison.OrdinalIgnoreCase)
|
||||
|| fileName.EndsWith(".dwg", StringComparison.OrdinalIgnoreCase)
|
||||
)
|
||||
return Path.GetFileNameWithoutExtension(fileName);
|
||||
return fileName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenNest.IO.Bom;
|
||||
|
||||
/// <summary>The import dialog's one-line summary of the part rows.</summary>
|
||||
public static class BomImportSummary
|
||||
{
|
||||
/// <summary>
|
||||
/// For example "12 ready, 2 need a thickness, 1 no drawing found".
|
||||
/// </summary>
|
||||
public static string Describe(IEnumerable<BomPartRow> rows)
|
||||
{
|
||||
var counts = rows.GroupBy(r => r.Status).ToDictionary(g => g.Key, g => g.Count());
|
||||
int Count(BomRowStatus status) => counts.TryGetValue(status, out var n) ? n : 0;
|
||||
|
||||
var parts = new List<string> { $"{Count(BomRowStatus.Ready)} ready" };
|
||||
Add(parts, Count(BomRowStatus.NeedsMaterial), "needs a material", "need a material");
|
||||
Add(parts, Count(BomRowStatus.NeedsThickness), "needs a thickness", "need a thickness");
|
||||
Add(parts, Count(BomRowStatus.NoDrawing), "no drawing found", "no drawing found");
|
||||
Add(parts, Count(BomRowStatus.NoFileName), "no file name", "no file name");
|
||||
return string.Join(", ", parts);
|
||||
}
|
||||
|
||||
private static void Add(List<string> parts, int count, string one, string many)
|
||||
{
|
||||
if (count > 0)
|
||||
parts.Add($"{count} {(count == 1 ? one : many)}");
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace OpenNest.IO.Bom;
|
||||
|
||||
/// <summary>Whether a BOM row can be imported, and if not, why.</summary>
|
||||
public enum BomRowStatus
|
||||
{
|
||||
/// <summary>Drawing found, material and thickness set: the row is imported.</summary>
|
||||
Ready,
|
||||
|
||||
/// <summary>Drawing found but the material is blank.</summary>
|
||||
NeedsMaterial,
|
||||
|
||||
/// <summary>Drawing found but the thickness is blank, zero or negative.</summary>
|
||||
NeedsThickness,
|
||||
|
||||
/// <summary>No drawing file matches the row's file name.</summary>
|
||||
NoDrawing,
|
||||
|
||||
/// <summary>The BOM row has no file name.</summary>
|
||||
NoFileName,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// One BOM line as the import dialog shows and edits it: the values read
|
||||
/// from the BOM, the drawing file it resolved to and whether it can be
|
||||
@@ -19,9 +40,41 @@ public class BomPartRow
|
||||
|
||||
public double? Thickness { get; set; }
|
||||
|
||||
/// <summary>The matched drawing file, or null when none was found.</summary>
|
||||
public string DxfPath { get; set; }
|
||||
|
||||
public string Status { get; set; }
|
||||
public BomRowStatus Status
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(FileName))
|
||||
return BomRowStatus.NoFileName;
|
||||
if (string.IsNullOrWhiteSpace(DxfPath))
|
||||
return BomRowStatus.NoDrawing;
|
||||
if (string.IsNullOrWhiteSpace(Material))
|
||||
return BomRowStatus.NeedsMaterial;
|
||||
if (Thickness is not double thickness || !double.IsFinite(thickness) || thickness <= 0)
|
||||
return BomRowStatus.NeedsThickness;
|
||||
return BomRowStatus.Ready;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEditable { get; set; }
|
||||
public string StatusText => Describe(Status);
|
||||
|
||||
/// <summary>
|
||||
/// True when the row has a drawing, so the operator can complete its
|
||||
/// values; rows without one can never be imported.
|
||||
/// </summary>
|
||||
public bool IsEditable => Status is not (BomRowStatus.NoFileName or BomRowStatus.NoDrawing);
|
||||
|
||||
public static string Describe(BomRowStatus status) =>
|
||||
status switch
|
||||
{
|
||||
BomRowStatus.Ready => "Ready",
|
||||
BomRowStatus.NeedsMaterial => "Needs material",
|
||||
BomRowStatus.NeedsThickness => "Needs thickness",
|
||||
BomRowStatus.NoDrawing => "No drawing found",
|
||||
BomRowStatus.NoFileName => "No file name",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(status), status, null),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace OpenNest.IO.Bom;
|
||||
|
||||
/// <summary>
|
||||
/// The .dxf and .dwg files in one folder, looked up by the name a BOM uses.
|
||||
/// A .dxf wins over a .dwg with the same name.
|
||||
/// </summary>
|
||||
internal sealed class DrawingFileIndex
|
||||
{
|
||||
private readonly Dictionary<string, string> files = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
private DrawingFileIndex(string folder)
|
||||
{
|
||||
FolderExists = Directory.Exists(folder);
|
||||
if (!FolderExists)
|
||||
return;
|
||||
|
||||
foreach (var file in Directory.GetFiles(folder, "*.dxf"))
|
||||
files[Path.GetFileNameWithoutExtension(file)] = file;
|
||||
foreach (var file in Directory.GetFiles(folder, "*.dwg"))
|
||||
files.TryAdd(Path.GetFileNameWithoutExtension(file), file);
|
||||
}
|
||||
|
||||
public bool FolderExists { get; }
|
||||
|
||||
public static DrawingFileIndex Load(string folder) => new(folder);
|
||||
|
||||
/// <summary>
|
||||
/// The drawing file for a BOM file name, ignoring case and a .dxf or
|
||||
/// .dwg extension; null when there is none.
|
||||
/// </summary>
|
||||
public string Find(string bomFileName) =>
|
||||
files.TryGetValue(LookupName(bomFileName), out var path) ? path : null;
|
||||
|
||||
/// <summary>The BOM file name without a .dxf or .dwg extension.</summary>
|
||||
public static string LookupName(string fileName)
|
||||
{
|
||||
fileName ??= "";
|
||||
if (
|
||||
fileName.EndsWith(".dxf", StringComparison.OrdinalIgnoreCase)
|
||||
|| fileName.EndsWith(".dwg", StringComparison.OrdinalIgnoreCase)
|
||||
)
|
||||
return Path.GetFileNameWithoutExtension(fileName);
|
||||
return fileName;
|
||||
}
|
||||
}
|
||||
@@ -31,17 +31,18 @@ public class BomImportGroupsTests
|
||||
[Fact]
|
||||
public void Build_LeavesOutRowsThatCannotBeImported()
|
||||
{
|
||||
var locked = Row("PT02", "Stainless", 0.25);
|
||||
locked.IsEditable = false;
|
||||
var noFileName = Row("PT02", "Stainless", 0.25);
|
||||
noFileName.FileName = " ";
|
||||
var noDrawing = Row("PT05", "Stainless", 0.25);
|
||||
noDrawing.DxfPath = null;
|
||||
var rows = new List<BomPartRow>
|
||||
{
|
||||
Row("PT01", "Stainless", 0.25),
|
||||
locked,
|
||||
noFileName,
|
||||
Row("PT03", " ", 0.25),
|
||||
Row("PT04", "Stainless", null),
|
||||
noDrawing,
|
||||
Row("PT06", "Stainless", 0),
|
||||
};
|
||||
|
||||
var groups = BomImportGroups.Build(rows);
|
||||
@@ -65,7 +66,5 @@ public class BomImportGroupsTests
|
||||
Thickness = thickness,
|
||||
Qty = qty,
|
||||
DxfPath = $"/drawings/{fileName}.dxf",
|
||||
Status = "Matched",
|
||||
IsEditable = true,
|
||||
};
|
||||
}
|
||||
@@ -36,7 +36,16 @@ public sealed class BomImportRowsTests : IDisposable
|
||||
var rows = BomImportRows.Build(items, folder);
|
||||
|
||||
Assert.Equal(new[] { 1, 2, 3, 4 }, rows.Select(r => r.ItemNum ?? 0));
|
||||
Assert.Equal(new[] { "Matched", "No DXF", "Skipped", "Matched" }, rows.Select(r => r.Status));
|
||||
Assert.Equal(
|
||||
new[]
|
||||
{
|
||||
BomRowStatus.Ready,
|
||||
BomRowStatus.NoDrawing,
|
||||
BomRowStatus.NoFileName,
|
||||
BomRowStatus.Ready,
|
||||
},
|
||||
rows.Select(r => r.Status)
|
||||
);
|
||||
Assert.Equal(new[] { true, false, false, true }, rows.Select(r => r.IsEditable));
|
||||
Assert.Equal(Path.Combine(folder, "PT01.dxf"), rows[0].DxfPath);
|
||||
Assert.Null(rows[1].DxfPath);
|
||||
@@ -57,7 +66,7 @@ public sealed class BomImportRowsTests : IDisposable
|
||||
|
||||
var rows = BomImportRows.Build(new List<BomItem> { Item("pt01") }, folder);
|
||||
|
||||
Assert.Equal("Matched", rows[0].Status);
|
||||
Assert.Equal(BomRowStatus.Ready, rows[0].Status);
|
||||
Assert.Equal(Path.Combine(folder, "PT01.dxf"), rows[0].DxfPath);
|
||||
}
|
||||
|
||||
@@ -77,6 +86,33 @@ public sealed class BomImportRowsTests : IDisposable
|
||||
Assert.Equal(Path.Combine(folder, "PT02.dwg"), rows[1].DxfPath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_RowWithoutThickness_StillFindsItsDrawing()
|
||||
{
|
||||
Touch("PT01.dxf");
|
||||
var item = Item("PT01");
|
||||
item.Thickness = null;
|
||||
|
||||
var rows = BomImportRows.Build(new List<BomItem> { item }, folder);
|
||||
|
||||
Assert.Equal(Path.Combine(folder, "PT01.dxf"), rows[0].DxfPath);
|
||||
Assert.True(rows[0].IsEditable);
|
||||
Assert.Equal(BomRowStatus.NeedsThickness, rows[0].Status);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_RowWithoutMaterial_NeedsAMaterial()
|
||||
{
|
||||
Touch("PT01.dxf");
|
||||
var item = Item("PT01");
|
||||
item.Material = " ";
|
||||
|
||||
var rows = BomImportRows.Build(new List<BomItem> { item }, folder);
|
||||
|
||||
Assert.True(rows[0].IsEditable);
|
||||
Assert.Equal(BomRowStatus.NeedsMaterial, rows[0].Status);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_WithMissingFolder_FindsNoDrawings()
|
||||
{
|
||||
@@ -84,7 +120,7 @@ public sealed class BomImportRowsTests : IDisposable
|
||||
|
||||
var rows = BomImportRows.Build(new List<BomItem> { Item("PT01") }, missing);
|
||||
|
||||
Assert.Equal("No DXF", rows[0].Status);
|
||||
Assert.Equal(BomRowStatus.NoDrawing, rows[0].Status);
|
||||
Assert.False(rows[0].IsEditable);
|
||||
Assert.Null(rows[0].DxfPath);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
using OpenNest.IO.Bom;
|
||||
|
||||
namespace OpenNest.Tests.Bom;
|
||||
|
||||
public class BomImportSummaryTests
|
||||
{
|
||||
[Fact]
|
||||
public void Describe_CountsReadyRowsAndEachProblem()
|
||||
{
|
||||
var rows = new List<BomPartRow>
|
||||
{
|
||||
Row("PT01", "Steel", 0.25),
|
||||
Row("PT02", "Steel", 0.25),
|
||||
Row("PT03", null, 0.25),
|
||||
Row("PT04", "Steel", null),
|
||||
Row("PT05", "Steel", null),
|
||||
Row("PT06", "Steel", 0.25, dxfPath: null),
|
||||
Row(null, "Steel", 0.25),
|
||||
};
|
||||
|
||||
Assert.Equal(
|
||||
"2 ready, 1 needs a material, 2 need a thickness, 1 no drawing found, 1 no file name",
|
||||
BomImportSummary.Describe(rows)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Describe_WithEveryRowReady_ListsOnlyTheReadyCount()
|
||||
{
|
||||
Assert.Equal("1 ready", BomImportSummary.Describe(new[] { Row("PT01", "Steel", 0.25) }));
|
||||
Assert.Equal("0 ready", BomImportSummary.Describe(Array.Empty<BomPartRow>()));
|
||||
}
|
||||
|
||||
private static BomPartRow Row(
|
||||
string? fileName,
|
||||
string? material,
|
||||
double? thickness,
|
||||
string? dxfPath = "/d/part.dxf"
|
||||
) =>
|
||||
new()
|
||||
{
|
||||
FileName = fileName,
|
||||
Material = material,
|
||||
Thickness = thickness,
|
||||
DxfPath = dxfPath,
|
||||
};
|
||||
}
|
||||
@@ -94,8 +94,6 @@ public sealed class BomNestBuilderTests : IDisposable
|
||||
Material = "Stainless",
|
||||
Thickness = 0.25,
|
||||
DxfPath = WriteSquare(name),
|
||||
Status = "Matched",
|
||||
IsEditable = true,
|
||||
};
|
||||
|
||||
private static BomImportGroup Group(params BomPartRow[] rows) => new("Stainless", 0.25, rows);
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
using OpenNest.IO.Bom;
|
||||
|
||||
namespace OpenNest.Tests.Bom;
|
||||
|
||||
public class BomPartRowTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(null, "/d/PT01.dxf", "Steel", 0.25, BomRowStatus.NoFileName)]
|
||||
[InlineData(" ", "/d/PT01.dxf", "Steel", 0.25, BomRowStatus.NoFileName)]
|
||||
[InlineData("PT01", null, "Steel", 0.25, BomRowStatus.NoDrawing)]
|
||||
[InlineData("PT01", "/d/PT01.dxf", null, 0.25, BomRowStatus.NeedsMaterial)]
|
||||
[InlineData("PT01", "/d/PT01.dxf", "\t", 0.25, BomRowStatus.NeedsMaterial)]
|
||||
[InlineData("PT01", "/d/PT01.dxf", "Steel", null, BomRowStatus.NeedsThickness)]
|
||||
[InlineData("PT01", "/d/PT01.dxf", "Steel", 0.0, BomRowStatus.NeedsThickness)]
|
||||
[InlineData("PT01", "/d/PT01.dxf", "Steel", -0.25, BomRowStatus.NeedsThickness)]
|
||||
[InlineData("PT01", "/d/PT01.dxf", "Steel", double.NaN, BomRowStatus.NeedsThickness)]
|
||||
[InlineData("PT01", "/d/PT01.dxf", "Steel", double.PositiveInfinity, BomRowStatus.NeedsThickness)]
|
||||
[InlineData("PT01", "/d/PT01.dxf", "Steel", 0.25, BomRowStatus.Ready)]
|
||||
public void Status_FollowsTheFirstMissingValue(
|
||||
string? fileName,
|
||||
string? dxfPath,
|
||||
string? material,
|
||||
double? thickness,
|
||||
BomRowStatus expected
|
||||
)
|
||||
{
|
||||
var row = new BomPartRow
|
||||
{
|
||||
FileName = fileName,
|
||||
DxfPath = dxfPath,
|
||||
Material = material,
|
||||
Thickness = thickness,
|
||||
};
|
||||
|
||||
Assert.Equal(expected, row.Status);
|
||||
Assert.Equal(expected is not (BomRowStatus.NoFileName or BomRowStatus.NoDrawing), row.IsEditable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Status_ChangesWhenTheOperatorFillsInAValue()
|
||||
{
|
||||
var row = new BomPartRow { FileName = "PT01", DxfPath = "/d/PT01.dxf" };
|
||||
Assert.Equal(BomRowStatus.NeedsMaterial, row.Status);
|
||||
|
||||
row.Material = "Steel";
|
||||
Assert.Equal(BomRowStatus.NeedsThickness, row.Status);
|
||||
|
||||
row.Thickness = 0.25;
|
||||
Assert.Equal(BomRowStatus.Ready, row.Status);
|
||||
Assert.Equal("Ready", row.StatusText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Describe_NamesEveryStatus()
|
||||
{
|
||||
Assert.Equal(
|
||||
new[] { "Ready", "Needs material", "Needs thickness", "No drawing found", "No file name" },
|
||||
Enum.GetValues<BomRowStatus>().Select(BomPartRow.Describe)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using OpenNest.Data;
|
||||
using OpenNest.IO;
|
||||
@@ -145,7 +144,7 @@ namespace OpenNest.Forms
|
||||
part.Description ?? "",
|
||||
part.Material ?? "",
|
||||
part.Thickness?.ToString("0.####") ?? "",
|
||||
part.Status
|
||||
part.StatusText
|
||||
);
|
||||
}
|
||||
|
||||
@@ -196,6 +195,10 @@ namespace OpenNest.Forms
|
||||
part.Thickness = double.TryParse(text, out var t) ? t : (double?)null;
|
||||
}
|
||||
|
||||
_suppressRegroup = true;
|
||||
dgvParts.Rows[e.RowIndex].Cells["Status"].Value = part.StatusText;
|
||||
_suppressRegroup = false;
|
||||
|
||||
RebuildGroups();
|
||||
UpdateSummary();
|
||||
}
|
||||
@@ -307,20 +310,7 @@ namespace OpenNest.Forms
|
||||
|
||||
private void UpdateSummary()
|
||||
{
|
||||
var skipped = _parts.Count(p => p.Status == "Skipped");
|
||||
var noDxf = _parts.Count(p => p.Status == "No DXF");
|
||||
var matched = _parts.Count(p => p.Status == "Matched");
|
||||
|
||||
var summaryParts = new List<string>();
|
||||
if (skipped > 0)
|
||||
summaryParts.Add($"{skipped} skipped (no file name)");
|
||||
if (noDxf > 0)
|
||||
summaryParts.Add($"{noDxf} no DXF found");
|
||||
|
||||
lblSummary.Text =
|
||||
summaryParts.Count > 0
|
||||
? string.Join(", ", summaryParts)
|
||||
: $"{matched} parts matched";
|
||||
lblSummary.Text = BomImportSummary.Describe(_parts);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
Reference in new issue
Block a user