Initial commit: JDE Scoping Tool migration project

Set up repository with legacy .NET Framework 4.8 source (OLD/),
new .NET 10 Blazor solution (NEW/), OpenSpec specifications,
documentation, and project configuration.
This commit is contained in:
Joseph Doherty
2026-01-02 07:43:29 -05:00
commit 26ff8d9b4f
1761 changed files with 596509 additions and 0 deletions
@@ -0,0 +1,124 @@
using ClosedXML.Excel;
using JdeScoping.Core.Interfaces;
using JdeScoping.Core.ViewModels;
namespace JdeScoping.ExcelIO.Parsing;
/// <summary>
/// Service for parsing Excel files uploaded by users.
/// </summary>
public class ExcelParserService : IExcelParserService
{
/// <inheritdoc />
public List<long> ParseWorkOrders(Stream fileStream)
{
using var workbook = new XLWorkbook(fileStream);
var worksheet = workbook.Worksheet(1);
var workOrderNumbers = new List<long>();
var lastRow = worksheet.LastRowUsed()?.RowNumber() ?? 1;
for (var row = 2; row <= lastRow; row++)
{
var cellValue = worksheet.Cell(row, 1).GetString()?.Trim();
if (long.TryParse(cellValue, out var woNumber))
{
workOrderNumbers.Add(woNumber);
}
}
return workOrderNumbers;
}
/// <inheritdoc />
public List<string> ParseItems(Stream fileStream)
{
using var workbook = new XLWorkbook(fileStream);
var worksheet = workbook.Worksheet(1);
var itemNumbers = new List<string>();
var lastRow = worksheet.LastRowUsed()?.RowNumber() ?? 1;
for (var row = 2; row <= lastRow; row++)
{
var cellValue = worksheet.Cell(row, 1).GetString()?.Trim();
if (!string.IsNullOrEmpty(cellValue))
{
itemNumbers.Add(cellValue);
}
}
return itemNumbers;
}
/// <inheritdoc />
public List<LotViewModel> ParseComponentLots(Stream fileStream)
{
using var workbook = new XLWorkbook(fileStream);
var worksheet = workbook.Worksheet(1);
var lotViewModels = new List<LotViewModel>();
var lastRow = worksheet.LastRowUsed()?.RowNumber() ?? 1;
for (var row = 2; row <= lastRow; row++)
{
var lotNumber = worksheet.Cell(row, 1).GetString()?.Trim() ?? string.Empty;
var itemNumber = worksheet.Cell(row, 2).GetString()?.Trim() ?? string.Empty;
if (!string.IsNullOrEmpty(lotNumber))
{
lotViewModels.Add(new LotViewModel
{
LotNumber = lotNumber,
ItemNumber = itemNumber
});
}
}
return lotViewModels;
}
/// <inheritdoc />
public List<PartOperationViewModel> ParsePartOperations(Stream fileStream)
{
using var workbook = new XLWorkbook(fileStream);
var worksheet = workbook.Worksheet(1);
var partOperations = new List<PartOperationViewModel>();
var lastRow = worksheet.LastRowUsed()?.RowNumber() ?? 1;
for (var row = 2; row <= lastRow; row++)
{
try
{
var itemNumber = worksheet.Cell(row, 1).GetString()?.Trim() ?? string.Empty;
var operationNumber = worksheet.Cell(row, 2).GetString()?.Trim() ?? string.Empty;
var misNumber = worksheet.Cell(row, 3).GetString()?.Trim() ?? string.Empty;
var misRevision = worksheet.Cell(row, 4).GetString()?.Trim() ?? string.Empty;
// Remove decimal places from operation number
if (!string.IsNullOrEmpty(operationNumber) && operationNumber.Contains('.'))
{
operationNumber = operationNumber[..operationNumber.IndexOf('.')];
}
if (!string.IsNullOrEmpty(itemNumber) && !string.IsNullOrEmpty(operationNumber))
{
partOperations.Add(new PartOperationViewModel
{
ItemNumber = itemNumber,
OperationNumber = operationNumber,
MisNumber = misNumber,
MisRevision = misRevision
});
}
}
catch
{
// Skip invalid rows
}
}
return partOperations;
}
}