feat: drop supplier import/export from the catalog format
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,18 +3,10 @@ namespace CutList.Web.DTOs;
|
||||
public class CatalogData
|
||||
{
|
||||
public DateTime ExportedAt { get; set; }
|
||||
public List<CatalogSupplierDto> Suppliers { get; set; } = [];
|
||||
public List<CatalogCuttingToolDto> CuttingTools { get; set; } = [];
|
||||
public CatalogMaterialsDto Materials { get; set; } = new();
|
||||
}
|
||||
|
||||
public class CatalogSupplierDto
|
||||
{
|
||||
public string Name { get; set; } = "";
|
||||
public string? ContactInfo { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
}
|
||||
|
||||
public class CatalogCuttingToolDto
|
||||
{
|
||||
public string Name { get; set; } = "";
|
||||
@@ -113,30 +105,16 @@ public class CatalogStockItemDto
|
||||
public string? Name { get; set; }
|
||||
public int QuantityOnHand { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public List<CatalogSupplierOfferingDto> SupplierOfferings { get; set; } = [];
|
||||
}
|
||||
|
||||
public class CatalogSupplierOfferingDto
|
||||
{
|
||||
public string SupplierName { get; set; } = "";
|
||||
public string? PartNumber { get; set; }
|
||||
public string? SupplierDescription { get; set; }
|
||||
public decimal? Price { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
}
|
||||
|
||||
public class ImportResultDto
|
||||
{
|
||||
public int SuppliersCreated { get; set; }
|
||||
public int SuppliersUpdated { get; set; }
|
||||
public int CuttingToolsCreated { get; set; }
|
||||
public int CuttingToolsUpdated { get; set; }
|
||||
public int MaterialsCreated { get; set; }
|
||||
public int MaterialsUpdated { get; set; }
|
||||
public int StockItemsCreated { get; set; }
|
||||
public int StockItemsUpdated { get; set; }
|
||||
public int OfferingsCreated { get; set; }
|
||||
public int OfferingsUpdated { get; set; }
|
||||
public List<string> Errors { get; set; } = [];
|
||||
public List<string> Warnings { get; set; } = [];
|
||||
}
|
||||
|
||||
@@ -20,12 +20,6 @@ public class CatalogService
|
||||
{
|
||||
await using var context = _factory.CreateDbContext();
|
||||
|
||||
var suppliers = await context.Suppliers
|
||||
.Where(s => s.IsActive)
|
||||
.OrderBy(s => s.Name)
|
||||
.AsNoTracking()
|
||||
.ToListAsync();
|
||||
|
||||
var cuttingTools = await context.CuttingTools
|
||||
.Where(t => t.IsActive)
|
||||
.OrderBy(t => t.Name)
|
||||
@@ -35,7 +29,6 @@ public class CatalogService
|
||||
var materials = await context.Materials
|
||||
.Include(m => m.Dimensions)
|
||||
.Include(m => m.StockItems.Where(s => s.IsActive))
|
||||
.ThenInclude(s => s.SupplierOfferings.Where(o => o.IsActive))
|
||||
.Where(m => m.IsActive)
|
||||
.OrderBy(m => m.Shape).ThenBy(m => m.SortOrder)
|
||||
.AsNoTracking()
|
||||
@@ -48,7 +41,7 @@ public class CatalogService
|
||||
{
|
||||
foreach (var m in group)
|
||||
{
|
||||
var stockItems = MapStockItems(m, suppliers);
|
||||
var stockItems = MapStockItems(m);
|
||||
|
||||
switch (m.Shape)
|
||||
{
|
||||
@@ -139,12 +132,6 @@ public class CatalogService
|
||||
return new CatalogData
|
||||
{
|
||||
ExportedAt = DateTime.UtcNow,
|
||||
Suppliers = suppliers.Select(s => new CatalogSupplierDto
|
||||
{
|
||||
Name = s.Name,
|
||||
ContactInfo = s.ContactInfo,
|
||||
Notes = s.Notes
|
||||
}).ToList(),
|
||||
CuttingTools = cuttingTools.Select(t => new CatalogCuttingToolDto
|
||||
{
|
||||
Name = t.Name,
|
||||
@@ -164,14 +151,11 @@ public class CatalogService
|
||||
|
||||
try
|
||||
{
|
||||
// 1. Suppliers - upsert by name
|
||||
var supplierMap = await ImportSuppliersAsync(context, data.Suppliers, result);
|
||||
|
||||
// 2. Cutting tools - upsert by name
|
||||
// 1. Cutting tools - upsert by name
|
||||
await ImportCuttingToolsAsync(context, data.CuttingTools, result);
|
||||
|
||||
// 3. Materials + stock items + offerings
|
||||
await ImportAllMaterialsAsync(context, data.Materials, supplierMap, result);
|
||||
// 2. Materials + stock items
|
||||
await ImportAllMaterialsAsync(context, data.Materials, result);
|
||||
|
||||
await transaction.CommitAsync();
|
||||
}
|
||||
@@ -184,54 +168,6 @@ public class CatalogService
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<Dictionary<string, int>> ImportSuppliersAsync(
|
||||
ApplicationDbContext context, List<CatalogSupplierDto> suppliers, ImportResultDto result)
|
||||
{
|
||||
var map = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var existingSuppliers = await context.Suppliers.ToListAsync();
|
||||
|
||||
foreach (var dto in suppliers)
|
||||
{
|
||||
try
|
||||
{
|
||||
var existing = existingSuppliers.FirstOrDefault(
|
||||
s => s.Name.Equals(dto.Name, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (existing != null)
|
||||
{
|
||||
existing.ContactInfo = dto.ContactInfo ?? existing.ContactInfo;
|
||||
existing.Notes = dto.Notes ?? existing.Notes;
|
||||
existing.IsActive = true;
|
||||
map[dto.Name] = existing.Id;
|
||||
result.SuppliersUpdated++;
|
||||
}
|
||||
else
|
||||
{
|
||||
var supplier = new Supplier
|
||||
{
|
||||
Name = dto.Name,
|
||||
ContactInfo = dto.ContactInfo,
|
||||
Notes = dto.Notes,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
context.Suppliers.Add(supplier);
|
||||
await context.SaveChangesAsync();
|
||||
existingSuppliers.Add(supplier);
|
||||
map[dto.Name] = supplier.Id;
|
||||
result.SuppliersCreated++;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Errors.Add($"Supplier '{dto.Name}': {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
return map;
|
||||
}
|
||||
|
||||
private async Task ImportCuttingToolsAsync(
|
||||
ApplicationDbContext context, List<CatalogCuttingToolDto> tools, ImportResultDto result)
|
||||
{
|
||||
@@ -273,68 +209,67 @@ public class CatalogService
|
||||
}
|
||||
|
||||
private async Task ImportAllMaterialsAsync(
|
||||
ApplicationDbContext context, CatalogMaterialsDto materials, Dictionary<string, int> supplierMap, ImportResultDto result)
|
||||
ApplicationDbContext context, CatalogMaterialsDto materials, ImportResultDto result)
|
||||
{
|
||||
var existingMaterials = await context.Materials
|
||||
.Include(m => m.Dimensions)
|
||||
.Include(m => m.StockItems)
|
||||
.ThenInclude(s => s.SupplierOfferings)
|
||||
.ToListAsync();
|
||||
|
||||
foreach (var dto in materials.Angles)
|
||||
await ImportMaterialAsync(context, dto, MaterialShape.Angle, existingMaterials, supplierMap, result,
|
||||
await ImportMaterialAsync(context, dto, MaterialShape.Angle, existingMaterials, result,
|
||||
() => new AngleDimensions { Leg1 = dto.Leg1, Leg2 = dto.Leg2, Thickness = dto.Thickness },
|
||||
dim => { var d = (AngleDimensions)dim; d.Leg1 = dto.Leg1; d.Leg2 = dto.Leg2; d.Thickness = dto.Thickness; });
|
||||
|
||||
foreach (var dto in materials.Channels)
|
||||
await ImportMaterialAsync(context, dto, MaterialShape.Channel, existingMaterials, supplierMap, result,
|
||||
await ImportMaterialAsync(context, dto, MaterialShape.Channel, existingMaterials, result,
|
||||
() => new ChannelDimensions { Height = dto.Height, Flange = dto.Flange, Web = dto.Web },
|
||||
dim => { var d = (ChannelDimensions)dim; d.Height = dto.Height; d.Flange = dto.Flange; d.Web = dto.Web; });
|
||||
|
||||
foreach (var dto in materials.FlatBars)
|
||||
await ImportMaterialAsync(context, dto, MaterialShape.FlatBar, existingMaterials, supplierMap, result,
|
||||
await ImportMaterialAsync(context, dto, MaterialShape.FlatBar, existingMaterials, result,
|
||||
() => new FlatBarDimensions { Width = dto.Width, Thickness = dto.Thickness },
|
||||
dim => { var d = (FlatBarDimensions)dim; d.Width = dto.Width; d.Thickness = dto.Thickness; });
|
||||
|
||||
foreach (var dto in materials.IBeams)
|
||||
await ImportMaterialAsync(context, dto, MaterialShape.IBeam, existingMaterials, supplierMap, result,
|
||||
await ImportMaterialAsync(context, dto, MaterialShape.IBeam, existingMaterials, result,
|
||||
() => new IBeamDimensions { Height = dto.Height, WeightPerFoot = dto.WeightPerFoot },
|
||||
dim => { var d = (IBeamDimensions)dim; d.Height = dto.Height; d.WeightPerFoot = dto.WeightPerFoot; });
|
||||
|
||||
foreach (var dto in materials.Pipes)
|
||||
await ImportMaterialAsync(context, dto, MaterialShape.Pipe, existingMaterials, supplierMap, result,
|
||||
await ImportMaterialAsync(context, dto, MaterialShape.Pipe, existingMaterials, result,
|
||||
() => new PipeDimensions { NominalSize = dto.NominalSize, Wall = dto.Wall, Schedule = dto.Schedule },
|
||||
dim => { var d = (PipeDimensions)dim; d.NominalSize = dto.NominalSize; d.Wall = (decimal?)dto.Wall; d.Schedule = dto.Schedule; });
|
||||
|
||||
foreach (var dto in materials.RectangularTubes)
|
||||
await ImportMaterialAsync(context, dto, MaterialShape.RectangularTube, existingMaterials, supplierMap, result,
|
||||
await ImportMaterialAsync(context, dto, MaterialShape.RectangularTube, existingMaterials, result,
|
||||
() => new RectangularTubeDimensions { Width = dto.Width, Height = dto.Height, Wall = dto.Wall },
|
||||
dim => { var d = (RectangularTubeDimensions)dim; d.Width = dto.Width; d.Height = dto.Height; d.Wall = dto.Wall; });
|
||||
|
||||
foreach (var dto in materials.RoundBars)
|
||||
await ImportMaterialAsync(context, dto, MaterialShape.RoundBar, existingMaterials, supplierMap, result,
|
||||
await ImportMaterialAsync(context, dto, MaterialShape.RoundBar, existingMaterials, result,
|
||||
() => new RoundBarDimensions { Diameter = dto.Diameter },
|
||||
dim => { var d = (RoundBarDimensions)dim; d.Diameter = dto.Diameter; });
|
||||
|
||||
foreach (var dto in materials.RoundTubes)
|
||||
await ImportMaterialAsync(context, dto, MaterialShape.RoundTube, existingMaterials, supplierMap, result,
|
||||
await ImportMaterialAsync(context, dto, MaterialShape.RoundTube, existingMaterials, result,
|
||||
() => new RoundTubeDimensions { OuterDiameter = dto.OuterDiameter, Wall = dto.Wall },
|
||||
dim => { var d = (RoundTubeDimensions)dim; d.OuterDiameter = dto.OuterDiameter; d.Wall = dto.Wall; });
|
||||
|
||||
foreach (var dto in materials.SquareBars)
|
||||
await ImportMaterialAsync(context, dto, MaterialShape.SquareBar, existingMaterials, supplierMap, result,
|
||||
await ImportMaterialAsync(context, dto, MaterialShape.SquareBar, existingMaterials, result,
|
||||
() => new SquareBarDimensions { Size = dto.SideLength },
|
||||
dim => { var d = (SquareBarDimensions)dim; d.Size = dto.SideLength; });
|
||||
|
||||
foreach (var dto in materials.SquareTubes)
|
||||
await ImportMaterialAsync(context, dto, MaterialShape.SquareTube, existingMaterials, supplierMap, result,
|
||||
await ImportMaterialAsync(context, dto, MaterialShape.SquareTube, existingMaterials, result,
|
||||
() => new SquareTubeDimensions { Size = dto.SideLength, Wall = dto.Wall },
|
||||
dim => { var d = (SquareTubeDimensions)dim; d.Size = dto.SideLength; d.Wall = dto.Wall; });
|
||||
}
|
||||
|
||||
private async Task ImportMaterialAsync(
|
||||
ApplicationDbContext context, CatalogMaterialBaseDto dto, MaterialShape shape,
|
||||
List<Material> existingMaterials, Dictionary<string, int> supplierMap,
|
||||
List<Material> existingMaterials,
|
||||
ImportResultDto result,
|
||||
Func<MaterialDimensions> createDimensions,
|
||||
Action<MaterialDimensions> updateDimensions)
|
||||
@@ -389,7 +324,7 @@ public class CatalogService
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
await ImportStockItemsAsync(context, material, dto.StockItems, supplierMap, result);
|
||||
await ImportStockItemsAsync(context, material, dto.StockItems, result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -399,10 +334,9 @@ public class CatalogService
|
||||
|
||||
private async Task ImportStockItemsAsync(
|
||||
ApplicationDbContext context, Material material, List<CatalogStockItemDto> stockItems,
|
||||
Dictionary<string, int> supplierMap, ImportResultDto result)
|
||||
ImportResultDto result)
|
||||
{
|
||||
var existingStockItems = await context.StockItems
|
||||
.Include(s => s.SupplierOfferings)
|
||||
.Where(s => s.MaterialId == material.Id)
|
||||
.ToListAsync();
|
||||
|
||||
@@ -440,56 +374,6 @@ public class CatalogService
|
||||
existingStockItems.Add(stockItem);
|
||||
result.StockItemsCreated++;
|
||||
}
|
||||
|
||||
foreach (var offeringDto in dto.SupplierOfferings)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!supplierMap.TryGetValue(offeringDto.SupplierName, out var supplierId))
|
||||
{
|
||||
result.Warnings.Add(
|
||||
$"Offering for stock '{material.DisplayName} @ {dto.LengthInches}\"': " +
|
||||
$"Unknown supplier '{offeringDto.SupplierName}', skipped");
|
||||
continue;
|
||||
}
|
||||
|
||||
var existingOffering = stockItem.SupplierOfferings.FirstOrDefault(
|
||||
o => o.SupplierId == supplierId);
|
||||
|
||||
if (existingOffering != null)
|
||||
{
|
||||
existingOffering.PartNumber = offeringDto.PartNumber ?? existingOffering.PartNumber;
|
||||
existingOffering.SupplierDescription = offeringDto.SupplierDescription ?? existingOffering.SupplierDescription;
|
||||
existingOffering.Price = offeringDto.Price ?? existingOffering.Price;
|
||||
existingOffering.Notes = offeringDto.Notes ?? existingOffering.Notes;
|
||||
existingOffering.IsActive = true;
|
||||
result.OfferingsUpdated++;
|
||||
}
|
||||
else
|
||||
{
|
||||
var offering = new SupplierOffering
|
||||
{
|
||||
StockItemId = stockItem.Id,
|
||||
SupplierId = supplierId,
|
||||
PartNumber = offeringDto.PartNumber,
|
||||
SupplierDescription = offeringDto.SupplierDescription,
|
||||
Price = offeringDto.Price,
|
||||
Notes = offeringDto.Notes
|
||||
};
|
||||
context.SupplierOfferings.Add(offering);
|
||||
stockItem.SupplierOfferings.Add(offering);
|
||||
result.OfferingsCreated++;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Errors.Add(
|
||||
$"Offering for '{material.DisplayName} @ {dto.LengthInches}\"' " +
|
||||
$"from '{offeringDto.SupplierName}': {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -499,22 +383,14 @@ public class CatalogService
|
||||
}
|
||||
}
|
||||
|
||||
private static List<CatalogStockItemDto> MapStockItems(Material m, List<Supplier> suppliers)
|
||||
private static List<CatalogStockItemDto> MapStockItems(Material m)
|
||||
{
|
||||
return m.StockItems.OrderBy(s => s.LengthInches).Select(s => new CatalogStockItemDto
|
||||
{
|
||||
LengthInches = s.LengthInches,
|
||||
Name = s.Name,
|
||||
QuantityOnHand = s.QuantityOnHand,
|
||||
Notes = s.Notes,
|
||||
SupplierOfferings = s.SupplierOfferings.Select(o => new CatalogSupplierOfferingDto
|
||||
{
|
||||
SupplierName = suppliers.FirstOrDefault(sup => sup.Id == o.SupplierId)?.Name ?? "Unknown",
|
||||
PartNumber = o.PartNumber,
|
||||
SupplierDescription = o.SupplierDescription,
|
||||
Price = o.Price,
|
||||
Notes = o.Notes
|
||||
}).ToList()
|
||||
Notes = s.Notes
|
||||
}).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user