refactor: migrate to IDbContextFactory and Windows Service hosting
Replace AddDbContext with AddDbContextFactory for Blazor Server circuit safety — each service method now creates a short-lived DbContext via the factory. Configure Windows Service hosting with ContentRootPath and remove HSTS/HTTPS redirect for reverse-proxy deployment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,13 @@ using CutList.Web.Data;
|
|||||||
using CutList.Web.Services;
|
using CutList.Web.Services;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
|
||||||
|
{
|
||||||
|
Args = args,
|
||||||
|
ContentRootPath = AppContext.BaseDirectory
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Host.UseWindowsService();
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
@@ -13,8 +19,8 @@ builder.Services.AddRazorComponents()
|
|||||||
builder.Services.AddEndpointsApiExplorer();
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
builder.Services.AddSwaggerGen();
|
builder.Services.AddSwaggerGen();
|
||||||
|
|
||||||
// Add Entity Framework
|
// Add Entity Framework (factory pattern for Blazor Server circuit safety)
|
||||||
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
builder.Services.AddDbContextFactory<ApplicationDbContext>(options =>
|
||||||
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
|
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
|
||||||
|
|
||||||
// Add application services
|
// Add application services
|
||||||
@@ -38,10 +44,8 @@ if (app.Environment.IsDevelopment())
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
||||||
app.UseHsts();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
|
||||||
app.UseStaticFiles();
|
app.UseStaticFiles();
|
||||||
app.UseAntiforgery();
|
app.UseAntiforgery();
|
||||||
|
|
||||||
|
|||||||
@@ -7,30 +7,32 @@ namespace CutList.Web.Services;
|
|||||||
|
|
||||||
public class CatalogService
|
public class CatalogService
|
||||||
{
|
{
|
||||||
private readonly ApplicationDbContext _context;
|
private readonly IDbContextFactory<ApplicationDbContext> _factory;
|
||||||
private readonly MaterialService _materialService;
|
private readonly MaterialService _materialService;
|
||||||
|
|
||||||
public CatalogService(ApplicationDbContext context, MaterialService materialService)
|
public CatalogService(IDbContextFactory<ApplicationDbContext> factory, MaterialService materialService)
|
||||||
{
|
{
|
||||||
_context = context;
|
_factory = factory;
|
||||||
_materialService = materialService;
|
_materialService = materialService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<CatalogData> ExportAsync()
|
public async Task<CatalogData> ExportAsync()
|
||||||
{
|
{
|
||||||
var suppliers = await _context.Suppliers
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
|
var suppliers = await context.Suppliers
|
||||||
.Where(s => s.IsActive)
|
.Where(s => s.IsActive)
|
||||||
.OrderBy(s => s.Name)
|
.OrderBy(s => s.Name)
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var cuttingTools = await _context.CuttingTools
|
var cuttingTools = await context.CuttingTools
|
||||||
.Where(t => t.IsActive)
|
.Where(t => t.IsActive)
|
||||||
.OrderBy(t => t.Name)
|
.OrderBy(t => t.Name)
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var materials = await _context.Materials
|
var materials = await context.Materials
|
||||||
.Include(m => m.Dimensions)
|
.Include(m => m.Dimensions)
|
||||||
.Include(m => m.StockItems.Where(s => s.IsActive))
|
.Include(m => m.StockItems.Where(s => s.IsActive))
|
||||||
.ThenInclude(s => s.SupplierOfferings.Where(o => o.IsActive))
|
.ThenInclude(s => s.SupplierOfferings.Where(o => o.IsActive))
|
||||||
@@ -157,18 +159,19 @@ public class CatalogService
|
|||||||
{
|
{
|
||||||
var result = new ImportResultDto();
|
var result = new ImportResultDto();
|
||||||
|
|
||||||
await using var transaction = await _context.Database.BeginTransactionAsync();
|
await using var context = _factory.CreateDbContext();
|
||||||
|
await using var transaction = await context.Database.BeginTransactionAsync();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 1. Suppliers - upsert by name
|
// 1. Suppliers - upsert by name
|
||||||
var supplierMap = await ImportSuppliersAsync(data.Suppliers, result);
|
var supplierMap = await ImportSuppliersAsync(context, data.Suppliers, result);
|
||||||
|
|
||||||
// 2. Cutting tools - upsert by name
|
// 2. Cutting tools - upsert by name
|
||||||
await ImportCuttingToolsAsync(data.CuttingTools, result);
|
await ImportCuttingToolsAsync(context, data.CuttingTools, result);
|
||||||
|
|
||||||
// 3. Materials + stock items + offerings
|
// 3. Materials + stock items + offerings
|
||||||
await ImportAllMaterialsAsync(data.Materials, supplierMap, result);
|
await ImportAllMaterialsAsync(context, data.Materials, supplierMap, result);
|
||||||
|
|
||||||
await transaction.CommitAsync();
|
await transaction.CommitAsync();
|
||||||
}
|
}
|
||||||
@@ -182,11 +185,11 @@ public class CatalogService
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Dictionary<string, int>> ImportSuppliersAsync(
|
private async Task<Dictionary<string, int>> ImportSuppliersAsync(
|
||||||
List<CatalogSupplierDto> suppliers, ImportResultDto result)
|
ApplicationDbContext context, List<CatalogSupplierDto> suppliers, ImportResultDto result)
|
||||||
{
|
{
|
||||||
var map = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
var map = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
var existingSuppliers = await _context.Suppliers.ToListAsync();
|
var existingSuppliers = await context.Suppliers.ToListAsync();
|
||||||
|
|
||||||
foreach (var dto in suppliers)
|
foreach (var dto in suppliers)
|
||||||
{
|
{
|
||||||
@@ -212,8 +215,8 @@ public class CatalogService
|
|||||||
Notes = dto.Notes,
|
Notes = dto.Notes,
|
||||||
CreatedAt = DateTime.UtcNow
|
CreatedAt = DateTime.UtcNow
|
||||||
};
|
};
|
||||||
_context.Suppliers.Add(supplier);
|
context.Suppliers.Add(supplier);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
existingSuppliers.Add(supplier);
|
existingSuppliers.Add(supplier);
|
||||||
map[dto.Name] = supplier.Id;
|
map[dto.Name] = supplier.Id;
|
||||||
result.SuppliersCreated++;
|
result.SuppliersCreated++;
|
||||||
@@ -225,14 +228,14 @@ public class CatalogService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ImportCuttingToolsAsync(
|
private async Task ImportCuttingToolsAsync(
|
||||||
List<CatalogCuttingToolDto> tools, ImportResultDto result)
|
ApplicationDbContext context, List<CatalogCuttingToolDto> tools, ImportResultDto result)
|
||||||
{
|
{
|
||||||
var existingTools = await _context.CuttingTools.ToListAsync();
|
var existingTools = await context.CuttingTools.ToListAsync();
|
||||||
|
|
||||||
foreach (var dto in tools)
|
foreach (var dto in tools)
|
||||||
{
|
{
|
||||||
@@ -255,7 +258,7 @@ public class CatalogService
|
|||||||
KerfInches = dto.KerfInches,
|
KerfInches = dto.KerfInches,
|
||||||
IsDefault = false
|
IsDefault = false
|
||||||
};
|
};
|
||||||
_context.CuttingTools.Add(tool);
|
context.CuttingTools.Add(tool);
|
||||||
existingTools.Add(tool);
|
existingTools.Add(tool);
|
||||||
result.CuttingToolsCreated++;
|
result.CuttingToolsCreated++;
|
||||||
}
|
}
|
||||||
@@ -266,71 +269,71 @@ public class CatalogService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ImportAllMaterialsAsync(
|
private async Task ImportAllMaterialsAsync(
|
||||||
CatalogMaterialsDto materials, Dictionary<string, int> supplierMap, ImportResultDto result)
|
ApplicationDbContext context, CatalogMaterialsDto materials, Dictionary<string, int> supplierMap, ImportResultDto result)
|
||||||
{
|
{
|
||||||
var existingMaterials = await _context.Materials
|
var existingMaterials = await context.Materials
|
||||||
.Include(m => m.Dimensions)
|
.Include(m => m.Dimensions)
|
||||||
.Include(m => m.StockItems)
|
.Include(m => m.StockItems)
|
||||||
.ThenInclude(s => s.SupplierOfferings)
|
.ThenInclude(s => s.SupplierOfferings)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
foreach (var dto in materials.Angles)
|
foreach (var dto in materials.Angles)
|
||||||
await ImportMaterialAsync(dto, MaterialShape.Angle, existingMaterials, supplierMap, result,
|
await ImportMaterialAsync(context, dto, MaterialShape.Angle, existingMaterials, supplierMap, result,
|
||||||
() => new AngleDimensions { Leg1 = dto.Leg1, Leg2 = dto.Leg2, Thickness = dto.Thickness },
|
() => 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; });
|
dim => { var d = (AngleDimensions)dim; d.Leg1 = dto.Leg1; d.Leg2 = dto.Leg2; d.Thickness = dto.Thickness; });
|
||||||
|
|
||||||
foreach (var dto in materials.Channels)
|
foreach (var dto in materials.Channels)
|
||||||
await ImportMaterialAsync(dto, MaterialShape.Channel, existingMaterials, supplierMap, result,
|
await ImportMaterialAsync(context, dto, MaterialShape.Channel, existingMaterials, supplierMap, result,
|
||||||
() => new ChannelDimensions { Height = dto.Height, Flange = dto.Flange, Web = dto.Web },
|
() => 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; });
|
dim => { var d = (ChannelDimensions)dim; d.Height = dto.Height; d.Flange = dto.Flange; d.Web = dto.Web; });
|
||||||
|
|
||||||
foreach (var dto in materials.FlatBars)
|
foreach (var dto in materials.FlatBars)
|
||||||
await ImportMaterialAsync(dto, MaterialShape.FlatBar, existingMaterials, supplierMap, result,
|
await ImportMaterialAsync(context, dto, MaterialShape.FlatBar, existingMaterials, supplierMap, result,
|
||||||
() => new FlatBarDimensions { Width = dto.Width, Thickness = dto.Thickness },
|
() => new FlatBarDimensions { Width = dto.Width, Thickness = dto.Thickness },
|
||||||
dim => { var d = (FlatBarDimensions)dim; d.Width = dto.Width; d.Thickness = dto.Thickness; });
|
dim => { var d = (FlatBarDimensions)dim; d.Width = dto.Width; d.Thickness = dto.Thickness; });
|
||||||
|
|
||||||
foreach (var dto in materials.IBeams)
|
foreach (var dto in materials.IBeams)
|
||||||
await ImportMaterialAsync(dto, MaterialShape.IBeam, existingMaterials, supplierMap, result,
|
await ImportMaterialAsync(context, dto, MaterialShape.IBeam, existingMaterials, supplierMap, result,
|
||||||
() => new IBeamDimensions { Height = dto.Height, WeightPerFoot = dto.WeightPerFoot },
|
() => new IBeamDimensions { Height = dto.Height, WeightPerFoot = dto.WeightPerFoot },
|
||||||
dim => { var d = (IBeamDimensions)dim; d.Height = dto.Height; d.WeightPerFoot = dto.WeightPerFoot; });
|
dim => { var d = (IBeamDimensions)dim; d.Height = dto.Height; d.WeightPerFoot = dto.WeightPerFoot; });
|
||||||
|
|
||||||
foreach (var dto in materials.Pipes)
|
foreach (var dto in materials.Pipes)
|
||||||
await ImportMaterialAsync(dto, MaterialShape.Pipe, existingMaterials, supplierMap, result,
|
await ImportMaterialAsync(context, dto, MaterialShape.Pipe, existingMaterials, supplierMap, result,
|
||||||
() => new PipeDimensions { NominalSize = dto.NominalSize, Wall = dto.Wall, Schedule = dto.Schedule },
|
() => 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; });
|
dim => { var d = (PipeDimensions)dim; d.NominalSize = dto.NominalSize; d.Wall = (decimal?)dto.Wall; d.Schedule = dto.Schedule; });
|
||||||
|
|
||||||
foreach (var dto in materials.RectangularTubes)
|
foreach (var dto in materials.RectangularTubes)
|
||||||
await ImportMaterialAsync(dto, MaterialShape.RectangularTube, existingMaterials, supplierMap, result,
|
await ImportMaterialAsync(context, dto, MaterialShape.RectangularTube, existingMaterials, supplierMap, result,
|
||||||
() => new RectangularTubeDimensions { Width = dto.Width, Height = dto.Height, Wall = dto.Wall },
|
() => 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; });
|
dim => { var d = (RectangularTubeDimensions)dim; d.Width = dto.Width; d.Height = dto.Height; d.Wall = dto.Wall; });
|
||||||
|
|
||||||
foreach (var dto in materials.RoundBars)
|
foreach (var dto in materials.RoundBars)
|
||||||
await ImportMaterialAsync(dto, MaterialShape.RoundBar, existingMaterials, supplierMap, result,
|
await ImportMaterialAsync(context, dto, MaterialShape.RoundBar, existingMaterials, supplierMap, result,
|
||||||
() => new RoundBarDimensions { Diameter = dto.Diameter },
|
() => new RoundBarDimensions { Diameter = dto.Diameter },
|
||||||
dim => { var d = (RoundBarDimensions)dim; d.Diameter = dto.Diameter; });
|
dim => { var d = (RoundBarDimensions)dim; d.Diameter = dto.Diameter; });
|
||||||
|
|
||||||
foreach (var dto in materials.RoundTubes)
|
foreach (var dto in materials.RoundTubes)
|
||||||
await ImportMaterialAsync(dto, MaterialShape.RoundTube, existingMaterials, supplierMap, result,
|
await ImportMaterialAsync(context, dto, MaterialShape.RoundTube, existingMaterials, supplierMap, result,
|
||||||
() => new RoundTubeDimensions { OuterDiameter = dto.OuterDiameter, Wall = dto.Wall },
|
() => new RoundTubeDimensions { OuterDiameter = dto.OuterDiameter, Wall = dto.Wall },
|
||||||
dim => { var d = (RoundTubeDimensions)dim; d.OuterDiameter = dto.OuterDiameter; d.Wall = dto.Wall; });
|
dim => { var d = (RoundTubeDimensions)dim; d.OuterDiameter = dto.OuterDiameter; d.Wall = dto.Wall; });
|
||||||
|
|
||||||
foreach (var dto in materials.SquareBars)
|
foreach (var dto in materials.SquareBars)
|
||||||
await ImportMaterialAsync(dto, MaterialShape.SquareBar, existingMaterials, supplierMap, result,
|
await ImportMaterialAsync(context, dto, MaterialShape.SquareBar, existingMaterials, supplierMap, result,
|
||||||
() => new SquareBarDimensions { Size = dto.SideLength },
|
() => new SquareBarDimensions { Size = dto.SideLength },
|
||||||
dim => { var d = (SquareBarDimensions)dim; d.Size = dto.SideLength; });
|
dim => { var d = (SquareBarDimensions)dim; d.Size = dto.SideLength; });
|
||||||
|
|
||||||
foreach (var dto in materials.SquareTubes)
|
foreach (var dto in materials.SquareTubes)
|
||||||
await ImportMaterialAsync(dto, MaterialShape.SquareTube, existingMaterials, supplierMap, result,
|
await ImportMaterialAsync(context, dto, MaterialShape.SquareTube, existingMaterials, supplierMap, result,
|
||||||
() => new SquareTubeDimensions { Size = dto.SideLength, Wall = dto.Wall },
|
() => new SquareTubeDimensions { Size = dto.SideLength, Wall = dto.Wall },
|
||||||
dim => { var d = (SquareTubeDimensions)dim; d.Size = dto.SideLength; d.Wall = dto.Wall; });
|
dim => { var d = (SquareTubeDimensions)dim; d.Size = dto.SideLength; d.Wall = dto.Wall; });
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ImportMaterialAsync(
|
private async Task ImportMaterialAsync(
|
||||||
CatalogMaterialBaseDto dto, MaterialShape shape,
|
ApplicationDbContext context, CatalogMaterialBaseDto dto, MaterialShape shape,
|
||||||
List<Material> existingMaterials, Dictionary<string, int> supplierMap,
|
List<Material> existingMaterials, Dictionary<string, int> supplierMap,
|
||||||
ImportResultDto result,
|
ImportResultDto result,
|
||||||
Func<MaterialDimensions> createDimensions,
|
Func<MaterialDimensions> createDimensions,
|
||||||
@@ -384,9 +387,9 @@ public class CatalogService
|
|||||||
result.MaterialsCreated++;
|
result.MaterialsCreated++;
|
||||||
}
|
}
|
||||||
|
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
await ImportStockItemsAsync(material, dto.StockItems, supplierMap, result);
|
await ImportStockItemsAsync(context, material, dto.StockItems, supplierMap, result);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -395,10 +398,10 @@ public class CatalogService
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task ImportStockItemsAsync(
|
private async Task ImportStockItemsAsync(
|
||||||
Material material, List<CatalogStockItemDto> stockItems,
|
ApplicationDbContext context, Material material, List<CatalogStockItemDto> stockItems,
|
||||||
Dictionary<string, int> supplierMap, ImportResultDto result)
|
Dictionary<string, int> supplierMap, ImportResultDto result)
|
||||||
{
|
{
|
||||||
var existingStockItems = await _context.StockItems
|
var existingStockItems = await context.StockItems
|
||||||
.Include(s => s.SupplierOfferings)
|
.Include(s => s.SupplierOfferings)
|
||||||
.Where(s => s.MaterialId == material.Id)
|
.Where(s => s.MaterialId == material.Id)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
@@ -432,8 +435,8 @@ public class CatalogService
|
|||||||
Notes = dto.Notes,
|
Notes = dto.Notes,
|
||||||
CreatedAt = DateTime.UtcNow
|
CreatedAt = DateTime.UtcNow
|
||||||
};
|
};
|
||||||
_context.StockItems.Add(stockItem);
|
context.StockItems.Add(stockItem);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
existingStockItems.Add(stockItem);
|
existingStockItems.Add(stockItem);
|
||||||
result.StockItemsCreated++;
|
result.StockItemsCreated++;
|
||||||
}
|
}
|
||||||
@@ -473,7 +476,7 @@ public class CatalogService
|
|||||||
Price = offeringDto.Price,
|
Price = offeringDto.Price,
|
||||||
Notes = offeringDto.Notes
|
Notes = offeringDto.Notes
|
||||||
};
|
};
|
||||||
_context.SupplierOfferings.Add(offering);
|
context.SupplierOfferings.Add(offering);
|
||||||
stockItem.SupplierOfferings.Add(offering);
|
stockItem.SupplierOfferings.Add(offering);
|
||||||
result.OfferingsCreated++;
|
result.OfferingsCreated++;
|
||||||
}
|
}
|
||||||
@@ -486,7 +489,7 @@ public class CatalogService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ namespace CutList.Web.Services;
|
|||||||
|
|
||||||
public class CutListPackingService
|
public class CutListPackingService
|
||||||
{
|
{
|
||||||
private readonly ApplicationDbContext _context;
|
private readonly IDbContextFactory<ApplicationDbContext> _factory;
|
||||||
|
|
||||||
public CutListPackingService(ApplicationDbContext context)
|
public CutListPackingService(IDbContextFactory<ApplicationDbContext> factory)
|
||||||
{
|
{
|
||||||
_context = context;
|
_factory = factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<MultiMaterialPackResult> PackAsync(IEnumerable<JobPart> parts, decimal kerfInches)
|
public async Task<MultiMaterialPackResult> PackAsync(IEnumerable<JobPart> parts, decimal kerfInches)
|
||||||
@@ -22,6 +22,7 @@ public class CutListPackingService
|
|||||||
|
|
||||||
public async Task<MultiMaterialPackResult> PackAsync(IEnumerable<JobPart> parts, decimal kerfInches, IEnumerable<JobStock>? jobStock)
|
public async Task<MultiMaterialPackResult> PackAsync(IEnumerable<JobPart> parts, decimal kerfInches, IEnumerable<JobStock>? jobStock)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
var result = new MultiMaterialPackResult();
|
var result = new MultiMaterialPackResult();
|
||||||
|
|
||||||
// Group parts by material
|
// Group parts by material
|
||||||
@@ -37,7 +38,7 @@ public class CutListPackingService
|
|||||||
var materialParts = group.ToList();
|
var materialParts = group.ToList();
|
||||||
|
|
||||||
// Get the material
|
// Get the material
|
||||||
var material = await _context.Materials
|
var material = await context.Materials
|
||||||
.FirstOrDefaultAsync(m => m.Id == materialId);
|
.FirstOrDefaultAsync(m => m.Id == materialId);
|
||||||
|
|
||||||
if (material == null) continue;
|
if (material == null) continue;
|
||||||
@@ -63,7 +64,7 @@ public class CutListPackingService
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// No job-specific stock - use all available stock items for this material
|
// No job-specific stock - use all available stock items for this material
|
||||||
var stockItems = await _context.StockItems
|
var stockItems = await context.StockItems
|
||||||
.Where(s => s.MaterialId == materialId && s.IsActive)
|
.Where(s => s.MaterialId == materialId && s.IsActive)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
@@ -172,10 +173,12 @@ public class CutListPackingService
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MultiMaterialPackResult? LoadSavedResult(string json)
|
public async Task<MultiMaterialPackResult?> LoadSavedResultAsync(string json)
|
||||||
{
|
{
|
||||||
var saved = System.Text.Json.JsonSerializer.Deserialize<SavedOptimizationResult>(json);
|
var saved = System.Text.Json.JsonSerializer.Deserialize<SavedOptimizationResult>(json);
|
||||||
return saved?.ToPackResult(_context);
|
if (saved == null) return null;
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
|
return await saved.ToPackResultAsync(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SerializeResult(MultiMaterialPackResult result)
|
public string SerializeResult(MultiMaterialPackResult result)
|
||||||
@@ -344,13 +347,13 @@ public class SavedOptimizationResult
|
|||||||
return saved;
|
return saved;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MultiMaterialPackResult ToPackResult(ApplicationDbContext context)
|
public async Task<MultiMaterialPackResult> ToPackResultAsync(ApplicationDbContext context)
|
||||||
{
|
{
|
||||||
var result = new MultiMaterialPackResult();
|
var result = new MultiMaterialPackResult();
|
||||||
|
|
||||||
foreach (var savedMr in MaterialResults)
|
foreach (var savedMr in MaterialResults)
|
||||||
{
|
{
|
||||||
var material = context.Materials.Find(savedMr.MaterialId);
|
var material = await context.Materials.FindAsync(savedMr.MaterialId);
|
||||||
if (material == null) continue;
|
if (material == null) continue;
|
||||||
|
|
||||||
var packResult = new PackResult();
|
var packResult = new PackResult();
|
||||||
|
|||||||
@@ -6,16 +6,17 @@ namespace CutList.Web.Services;
|
|||||||
|
|
||||||
public class JobService
|
public class JobService
|
||||||
{
|
{
|
||||||
private readonly ApplicationDbContext _context;
|
private readonly IDbContextFactory<ApplicationDbContext> _factory;
|
||||||
|
|
||||||
public JobService(ApplicationDbContext context)
|
public JobService(IDbContextFactory<ApplicationDbContext> factory)
|
||||||
{
|
{
|
||||||
_context = context;
|
_factory = factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<Job>> GetAllAsync()
|
public async Task<List<Job>> GetAllAsync()
|
||||||
{
|
{
|
||||||
return await _context.Jobs
|
await using var context = _factory.CreateDbContext();
|
||||||
|
return await context.Jobs
|
||||||
.Include(p => p.CuttingTool)
|
.Include(p => p.CuttingTool)
|
||||||
.Include(p => p.Parts)
|
.Include(p => p.Parts)
|
||||||
.ThenInclude(pt => pt.Material)
|
.ThenInclude(pt => pt.Material)
|
||||||
@@ -25,7 +26,8 @@ public class JobService
|
|||||||
|
|
||||||
public async Task<Job?> GetByIdAsync(int id)
|
public async Task<Job?> GetByIdAsync(int id)
|
||||||
{
|
{
|
||||||
return await _context.Jobs
|
await using var context = _factory.CreateDbContext();
|
||||||
|
return await context.Jobs
|
||||||
.Include(p => p.CuttingTool)
|
.Include(p => p.CuttingTool)
|
||||||
.Include(p => p.Parts.OrderBy(pt => pt.SortOrder))
|
.Include(p => p.Parts.OrderBy(pt => pt.SortOrder))
|
||||||
.ThenInclude(pt => pt.Material)
|
.ThenInclude(pt => pt.Material)
|
||||||
@@ -38,17 +40,24 @@ public class JobService
|
|||||||
|
|
||||||
public async Task<Job> CreateAsync(Job? job = null)
|
public async Task<Job> CreateAsync(Job? job = null)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
job ??= new Job();
|
job ??= new Job();
|
||||||
job.JobNumber = await GenerateJobNumberAsync();
|
job.JobNumber = await GenerateJobNumberAsync(context);
|
||||||
job.CreatedAt = DateTime.UtcNow;
|
job.CreatedAt = DateTime.UtcNow;
|
||||||
_context.Jobs.Add(job);
|
context.Jobs.Add(job);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
return job;
|
return job;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> GenerateJobNumberAsync()
|
public async Task<string> GenerateJobNumberAsync()
|
||||||
{
|
{
|
||||||
var maxNumber = await _context.Jobs
|
await using var context = _factory.CreateDbContext();
|
||||||
|
return await GenerateJobNumberAsync(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<string> GenerateJobNumberAsync(ApplicationDbContext context)
|
||||||
|
{
|
||||||
|
var maxNumber = await context.Jobs
|
||||||
.Where(j => j.JobNumber.StartsWith("JOB-"))
|
.Where(j => j.JobNumber.StartsWith("JOB-"))
|
||||||
.Select(j => j.JobNumber)
|
.Select(j => j.JobNumber)
|
||||||
.MaxAsync() as string;
|
.MaxAsync() as string;
|
||||||
@@ -71,46 +80,61 @@ public class JobService
|
|||||||
|
|
||||||
public async Task UpdateAsync(Job job)
|
public async Task UpdateAsync(Job job)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
job.UpdatedAt = DateTime.UtcNow;
|
job.UpdatedAt = DateTime.UtcNow;
|
||||||
job.OptimizationResultJson = null;
|
job.OptimizationResultJson = null;
|
||||||
job.OptimizedAt = null;
|
job.OptimizedAt = null;
|
||||||
_context.Jobs.Update(job);
|
context.Jobs.Update(job);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LockAsync(int id)
|
public async Task LockAsync(int id)
|
||||||
{
|
{
|
||||||
var job = await _context.Jobs.FindAsync(id);
|
await using var context = _factory.CreateDbContext();
|
||||||
|
var job = await context.Jobs.FindAsync(id);
|
||||||
if (job != null)
|
if (job != null)
|
||||||
{
|
{
|
||||||
job.LockedAt = DateTime.UtcNow;
|
job.LockedAt = DateTime.UtcNow;
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UnlockAsync(int id)
|
public async Task UnlockAsync(int id)
|
||||||
{
|
{
|
||||||
var job = await _context.Jobs.FindAsync(id);
|
await using var context = _factory.CreateDbContext();
|
||||||
|
var job = await context.Jobs.FindAsync(id);
|
||||||
if (job != null)
|
if (job != null)
|
||||||
{
|
{
|
||||||
job.LockedAt = null;
|
job.LockedAt = null;
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteAsync(int id)
|
public async Task DeleteAsync(int id)
|
||||||
{
|
{
|
||||||
var job = await _context.Jobs.FindAsync(id);
|
await using var context = _factory.CreateDbContext();
|
||||||
|
var job = await context.Jobs.FindAsync(id);
|
||||||
if (job != null)
|
if (job != null)
|
||||||
{
|
{
|
||||||
_context.Jobs.Remove(job);
|
context.Jobs.Remove(job);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Job> DuplicateAsync(int id)
|
public async Task<Job> DuplicateAsync(int id)
|
||||||
{
|
{
|
||||||
var original = await GetByIdAsync(id);
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
|
var original = await context.Jobs
|
||||||
|
.Include(p => p.CuttingTool)
|
||||||
|
.Include(p => p.Parts.OrderBy(pt => pt.SortOrder))
|
||||||
|
.ThenInclude(pt => pt.Material)
|
||||||
|
.Include(p => p.Stock.OrderBy(s => s.SortOrder))
|
||||||
|
.ThenInclude(s => s.Material)
|
||||||
|
.Include(p => p.Stock)
|
||||||
|
.ThenInclude(s => s.StockItem)
|
||||||
|
.FirstOrDefaultAsync(p => p.Id == id);
|
||||||
|
|
||||||
if (original == null)
|
if (original == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Job not found", nameof(id));
|
throw new ArgumentException("Job not found", nameof(id));
|
||||||
@@ -118,7 +142,7 @@ public class JobService
|
|||||||
|
|
||||||
var duplicate = new Job
|
var duplicate = new Job
|
||||||
{
|
{
|
||||||
JobNumber = await GenerateJobNumberAsync(),
|
JobNumber = await GenerateJobNumberAsync(context),
|
||||||
Name = string.IsNullOrWhiteSpace(original.Name) ? null : $"{original.Name} (Copy)",
|
Name = string.IsNullOrWhiteSpace(original.Name) ? null : $"{original.Name} (Copy)",
|
||||||
Customer = original.Customer,
|
Customer = original.Customer,
|
||||||
CuttingToolId = original.CuttingToolId,
|
CuttingToolId = original.CuttingToolId,
|
||||||
@@ -126,13 +150,13 @@ public class JobService
|
|||||||
CreatedAt = DateTime.UtcNow
|
CreatedAt = DateTime.UtcNow
|
||||||
};
|
};
|
||||||
|
|
||||||
_context.Jobs.Add(duplicate);
|
context.Jobs.Add(duplicate);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
// Copy parts
|
// Copy parts
|
||||||
foreach (var part in original.Parts)
|
foreach (var part in original.Parts)
|
||||||
{
|
{
|
||||||
_context.JobParts.Add(new JobPart
|
context.JobParts.Add(new JobPart
|
||||||
{
|
{
|
||||||
JobId = duplicate.Id,
|
JobId = duplicate.Id,
|
||||||
MaterialId = part.MaterialId,
|
MaterialId = part.MaterialId,
|
||||||
@@ -146,7 +170,7 @@ public class JobService
|
|||||||
// Copy stock selections
|
// Copy stock selections
|
||||||
foreach (var stock in original.Stock)
|
foreach (var stock in original.Stock)
|
||||||
{
|
{
|
||||||
_context.JobStocks.Add(new JobStock
|
context.JobStocks.Add(new JobStock
|
||||||
{
|
{
|
||||||
JobId = duplicate.Id,
|
JobId = duplicate.Id,
|
||||||
MaterialId = stock.MaterialId,
|
MaterialId = stock.MaterialId,
|
||||||
@@ -159,52 +183,55 @@ public class JobService
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
return duplicate;
|
return duplicate;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optimization result persistence
|
// Optimization result persistence
|
||||||
public async Task SaveOptimizationResultAsync(int jobId, string resultJson, DateTime optimizedAt)
|
public async Task SaveOptimizationResultAsync(int jobId, string resultJson, DateTime optimizedAt)
|
||||||
{
|
{
|
||||||
var job = await _context.Jobs.FindAsync(jobId);
|
await using var context = _factory.CreateDbContext();
|
||||||
|
var job = await context.Jobs.FindAsync(jobId);
|
||||||
if (job != null)
|
if (job != null)
|
||||||
{
|
{
|
||||||
job.OptimizationResultJson = resultJson;
|
job.OptimizationResultJson = resultJson;
|
||||||
job.OptimizedAt = optimizedAt;
|
job.OptimizedAt = optimizedAt;
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ClearOptimizationResultAsync(int jobId)
|
public async Task ClearOptimizationResultAsync(int jobId)
|
||||||
{
|
{
|
||||||
var job = await _context.Jobs.FindAsync(jobId);
|
await using var context = _factory.CreateDbContext();
|
||||||
|
var job = await context.Jobs.FindAsync(jobId);
|
||||||
if (job != null && job.OptimizationResultJson != null)
|
if (job != null && job.OptimizationResultJson != null)
|
||||||
{
|
{
|
||||||
job.OptimizationResultJson = null;
|
job.OptimizationResultJson = null;
|
||||||
job.OptimizedAt = null;
|
job.OptimizedAt = null;
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parts management
|
// Parts management
|
||||||
public async Task<JobPart> AddPartAsync(JobPart part)
|
public async Task<JobPart> AddPartAsync(JobPart part)
|
||||||
{
|
{
|
||||||
var maxOrder = await _context.JobParts
|
await using var context = _factory.CreateDbContext();
|
||||||
|
var maxOrder = await context.JobParts
|
||||||
.Where(p => p.JobId == part.JobId)
|
.Where(p => p.JobId == part.JobId)
|
||||||
.MaxAsync(p => (int?)p.SortOrder) ?? -1;
|
.MaxAsync(p => (int?)p.SortOrder) ?? -1;
|
||||||
part.SortOrder = maxOrder + 1;
|
part.SortOrder = maxOrder + 1;
|
||||||
|
|
||||||
_context.JobParts.Add(part);
|
context.JobParts.Add(part);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
// Update job timestamp and clear stale results
|
// Update job timestamp and clear stale results
|
||||||
var job = await _context.Jobs.FindAsync(part.JobId);
|
var job = await context.Jobs.FindAsync(part.JobId);
|
||||||
if (job != null)
|
if (job != null)
|
||||||
{
|
{
|
||||||
job.UpdatedAt = DateTime.UtcNow;
|
job.UpdatedAt = DateTime.UtcNow;
|
||||||
job.OptimizationResultJson = null;
|
job.OptimizationResultJson = null;
|
||||||
job.OptimizedAt = null;
|
job.OptimizedAt = null;
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
return part;
|
return part;
|
||||||
@@ -212,35 +239,37 @@ public class JobService
|
|||||||
|
|
||||||
public async Task UpdatePartAsync(JobPart part)
|
public async Task UpdatePartAsync(JobPart part)
|
||||||
{
|
{
|
||||||
_context.JobParts.Update(part);
|
await using var context = _factory.CreateDbContext();
|
||||||
await _context.SaveChangesAsync();
|
context.JobParts.Update(part);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
var job = await _context.Jobs.FindAsync(part.JobId);
|
var job = await context.Jobs.FindAsync(part.JobId);
|
||||||
if (job != null)
|
if (job != null)
|
||||||
{
|
{
|
||||||
job.UpdatedAt = DateTime.UtcNow;
|
job.UpdatedAt = DateTime.UtcNow;
|
||||||
job.OptimizationResultJson = null;
|
job.OptimizationResultJson = null;
|
||||||
job.OptimizedAt = null;
|
job.OptimizedAt = null;
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeletePartAsync(int id)
|
public async Task DeletePartAsync(int id)
|
||||||
{
|
{
|
||||||
var part = await _context.JobParts.FindAsync(id);
|
await using var context = _factory.CreateDbContext();
|
||||||
|
var part = await context.JobParts.FindAsync(id);
|
||||||
if (part != null)
|
if (part != null)
|
||||||
{
|
{
|
||||||
var jobId = part.JobId;
|
var jobId = part.JobId;
|
||||||
_context.JobParts.Remove(part);
|
context.JobParts.Remove(part);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
var job = await _context.Jobs.FindAsync(jobId);
|
var job = await context.Jobs.FindAsync(jobId);
|
||||||
if (job != null)
|
if (job != null)
|
||||||
{
|
{
|
||||||
job.UpdatedAt = DateTime.UtcNow;
|
job.UpdatedAt = DateTime.UtcNow;
|
||||||
job.OptimizationResultJson = null;
|
job.OptimizationResultJson = null;
|
||||||
job.OptimizedAt = null;
|
job.OptimizedAt = null;
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -248,21 +277,22 @@ public class JobService
|
|||||||
// Stock management
|
// Stock management
|
||||||
public async Task<JobStock> AddStockAsync(JobStock stock)
|
public async Task<JobStock> AddStockAsync(JobStock stock)
|
||||||
{
|
{
|
||||||
var maxOrder = await _context.JobStocks
|
await using var context = _factory.CreateDbContext();
|
||||||
|
var maxOrder = await context.JobStocks
|
||||||
.Where(s => s.JobId == stock.JobId)
|
.Where(s => s.JobId == stock.JobId)
|
||||||
.MaxAsync(s => (int?)s.SortOrder) ?? -1;
|
.MaxAsync(s => (int?)s.SortOrder) ?? -1;
|
||||||
stock.SortOrder = maxOrder + 1;
|
stock.SortOrder = maxOrder + 1;
|
||||||
|
|
||||||
_context.JobStocks.Add(stock);
|
context.JobStocks.Add(stock);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
var job = await _context.Jobs.FindAsync(stock.JobId);
|
var job = await context.Jobs.FindAsync(stock.JobId);
|
||||||
if (job != null)
|
if (job != null)
|
||||||
{
|
{
|
||||||
job.UpdatedAt = DateTime.UtcNow;
|
job.UpdatedAt = DateTime.UtcNow;
|
||||||
job.OptimizationResultJson = null;
|
job.OptimizationResultJson = null;
|
||||||
job.OptimizedAt = null;
|
job.OptimizedAt = null;
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
return stock;
|
return stock;
|
||||||
@@ -270,42 +300,45 @@ public class JobService
|
|||||||
|
|
||||||
public async Task UpdateStockAsync(JobStock stock)
|
public async Task UpdateStockAsync(JobStock stock)
|
||||||
{
|
{
|
||||||
_context.JobStocks.Update(stock);
|
await using var context = _factory.CreateDbContext();
|
||||||
await _context.SaveChangesAsync();
|
context.JobStocks.Update(stock);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
var job = await _context.Jobs.FindAsync(stock.JobId);
|
var job = await context.Jobs.FindAsync(stock.JobId);
|
||||||
if (job != null)
|
if (job != null)
|
||||||
{
|
{
|
||||||
job.UpdatedAt = DateTime.UtcNow;
|
job.UpdatedAt = DateTime.UtcNow;
|
||||||
job.OptimizationResultJson = null;
|
job.OptimizationResultJson = null;
|
||||||
job.OptimizedAt = null;
|
job.OptimizedAt = null;
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteStockAsync(int id)
|
public async Task DeleteStockAsync(int id)
|
||||||
{
|
{
|
||||||
var stock = await _context.JobStocks.FindAsync(id);
|
await using var context = _factory.CreateDbContext();
|
||||||
|
var stock = await context.JobStocks.FindAsync(id);
|
||||||
if (stock != null)
|
if (stock != null)
|
||||||
{
|
{
|
||||||
var jobId = stock.JobId;
|
var jobId = stock.JobId;
|
||||||
_context.JobStocks.Remove(stock);
|
context.JobStocks.Remove(stock);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
var job = await _context.Jobs.FindAsync(jobId);
|
var job = await context.Jobs.FindAsync(jobId);
|
||||||
if (job != null)
|
if (job != null)
|
||||||
{
|
{
|
||||||
job.UpdatedAt = DateTime.UtcNow;
|
job.UpdatedAt = DateTime.UtcNow;
|
||||||
job.OptimizationResultJson = null;
|
job.OptimizationResultJson = null;
|
||||||
job.OptimizedAt = null;
|
job.OptimizedAt = null;
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<StockItem>> GetAvailableStockForMaterialAsync(int materialId)
|
public async Task<List<StockItem>> GetAvailableStockForMaterialAsync(int materialId)
|
||||||
{
|
{
|
||||||
return await _context.StockItems
|
await using var context = _factory.CreateDbContext();
|
||||||
|
return await context.StockItems
|
||||||
.Include(s => s.Material)
|
.Include(s => s.Material)
|
||||||
.Where(s => s.MaterialId == materialId && s.IsActive)
|
.Where(s => s.MaterialId == materialId && s.IsActive)
|
||||||
.OrderBy(s => s.LengthInches)
|
.OrderBy(s => s.LengthInches)
|
||||||
@@ -315,7 +348,8 @@ public class JobService
|
|||||||
// Cutting tools
|
// Cutting tools
|
||||||
public async Task<List<CuttingTool>> GetCuttingToolsAsync(bool includeInactive = false)
|
public async Task<List<CuttingTool>> GetCuttingToolsAsync(bool includeInactive = false)
|
||||||
{
|
{
|
||||||
var query = _context.CuttingTools.AsQueryable();
|
await using var context = _factory.CreateDbContext();
|
||||||
|
var query = context.CuttingTools.AsQueryable();
|
||||||
if (!includeInactive)
|
if (!includeInactive)
|
||||||
{
|
{
|
||||||
query = query.Where(t => t.IsActive);
|
query = query.Where(t => t.IsActive);
|
||||||
@@ -325,53 +359,58 @@ public class JobService
|
|||||||
|
|
||||||
public async Task<CuttingTool?> GetCuttingToolByIdAsync(int id)
|
public async Task<CuttingTool?> GetCuttingToolByIdAsync(int id)
|
||||||
{
|
{
|
||||||
return await _context.CuttingTools.FindAsync(id);
|
await using var context = _factory.CreateDbContext();
|
||||||
|
return await context.CuttingTools.FindAsync(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<CuttingTool?> GetDefaultCuttingToolAsync()
|
public async Task<CuttingTool?> GetDefaultCuttingToolAsync()
|
||||||
{
|
{
|
||||||
return await _context.CuttingTools.FirstOrDefaultAsync(t => t.IsDefault && t.IsActive);
|
await using var context = _factory.CreateDbContext();
|
||||||
|
return await context.CuttingTools.FirstOrDefaultAsync(t => t.IsDefault && t.IsActive);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<CuttingTool> CreateCuttingToolAsync(CuttingTool tool)
|
public async Task<CuttingTool> CreateCuttingToolAsync(CuttingTool tool)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
if (tool.IsDefault)
|
if (tool.IsDefault)
|
||||||
{
|
{
|
||||||
// Clear other defaults
|
// Clear other defaults
|
||||||
var others = await _context.CuttingTools.Where(t => t.IsDefault).ToListAsync();
|
var others = await context.CuttingTools.Where(t => t.IsDefault).ToListAsync();
|
||||||
foreach (var other in others)
|
foreach (var other in others)
|
||||||
{
|
{
|
||||||
other.IsDefault = false;
|
other.IsDefault = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.CuttingTools.Add(tool);
|
context.CuttingTools.Add(tool);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
return tool;
|
return tool;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateCuttingToolAsync(CuttingTool tool)
|
public async Task UpdateCuttingToolAsync(CuttingTool tool)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
if (tool.IsDefault)
|
if (tool.IsDefault)
|
||||||
{
|
{
|
||||||
var others = await _context.CuttingTools.Where(t => t.IsDefault && t.Id != tool.Id).ToListAsync();
|
var others = await context.CuttingTools.Where(t => t.IsDefault && t.Id != tool.Id).ToListAsync();
|
||||||
foreach (var other in others)
|
foreach (var other in others)
|
||||||
{
|
{
|
||||||
other.IsDefault = false;
|
other.IsDefault = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.CuttingTools.Update(tool);
|
context.CuttingTools.Update(tool);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteCuttingToolAsync(int id)
|
public async Task DeleteCuttingToolAsync(int id)
|
||||||
{
|
{
|
||||||
var tool = await _context.CuttingTools.FindAsync(id);
|
await using var context = _factory.CreateDbContext();
|
||||||
|
var tool = await context.CuttingTools.FindAsync(id);
|
||||||
if (tool != null)
|
if (tool != null)
|
||||||
{
|
{
|
||||||
tool.IsActive = false;
|
tool.IsActive = false;
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,16 +6,17 @@ namespace CutList.Web.Services;
|
|||||||
|
|
||||||
public class MaterialService
|
public class MaterialService
|
||||||
{
|
{
|
||||||
private readonly ApplicationDbContext _context;
|
private readonly IDbContextFactory<ApplicationDbContext> _factory;
|
||||||
|
|
||||||
public MaterialService(ApplicationDbContext context)
|
public MaterialService(IDbContextFactory<ApplicationDbContext> factory)
|
||||||
{
|
{
|
||||||
_context = context;
|
_factory = factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<Material>> GetAllAsync(bool includeInactive = false)
|
public async Task<List<Material>> GetAllAsync(bool includeInactive = false)
|
||||||
{
|
{
|
||||||
var query = _context.Materials
|
await using var context = _factory.CreateDbContext();
|
||||||
|
var query = context.Materials
|
||||||
.Include(m => m.Dimensions)
|
.Include(m => m.Dimensions)
|
||||||
.AsQueryable();
|
.AsQueryable();
|
||||||
if (!includeInactive)
|
if (!includeInactive)
|
||||||
@@ -27,16 +28,18 @@ public class MaterialService
|
|||||||
|
|
||||||
public async Task<Material?> GetByIdAsync(int id)
|
public async Task<Material?> GetByIdAsync(int id)
|
||||||
{
|
{
|
||||||
return await _context.Materials
|
await using var context = _factory.CreateDbContext();
|
||||||
|
return await context.Materials
|
||||||
.Include(m => m.Dimensions)
|
.Include(m => m.Dimensions)
|
||||||
.FirstOrDefaultAsync(m => m.Id == id);
|
.FirstOrDefaultAsync(m => m.Id == id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Material> CreateAsync(Material material)
|
public async Task<Material> CreateAsync(Material material)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
material.CreatedAt = DateTime.UtcNow;
|
material.CreatedAt = DateTime.UtcNow;
|
||||||
_context.Materials.Add(material);
|
context.Materials.Add(material);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
return material;
|
return material;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,6 +48,7 @@ public class MaterialService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<Material> CreateWithDimensionsAsync(Material material, MaterialDimensions dimensions)
|
public async Task<Material> CreateWithDimensionsAsync(Material material, MaterialDimensions dimensions)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
material.CreatedAt = DateTime.UtcNow;
|
material.CreatedAt = DateTime.UtcNow;
|
||||||
|
|
||||||
// Auto-generate Size string from dimensions if not provided
|
// Auto-generate Size string from dimensions if not provided
|
||||||
@@ -56,13 +60,13 @@ public class MaterialService
|
|||||||
// Set sort order from primary dimension
|
// Set sort order from primary dimension
|
||||||
material.SortOrder = dimensions.GetSortOrder();
|
material.SortOrder = dimensions.GetSortOrder();
|
||||||
|
|
||||||
_context.Materials.Add(material);
|
context.Materials.Add(material);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
// Link dimensions to the created material
|
// Link dimensions to the created material
|
||||||
dimensions.MaterialId = material.Id;
|
dimensions.MaterialId = material.Id;
|
||||||
_context.MaterialDimensions.Add(dimensions);
|
context.MaterialDimensions.Add(dimensions);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
material.Dimensions = dimensions;
|
material.Dimensions = dimensions;
|
||||||
return material;
|
return material;
|
||||||
@@ -70,9 +74,10 @@ public class MaterialService
|
|||||||
|
|
||||||
public async Task UpdateAsync(Material material)
|
public async Task UpdateAsync(Material material)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
material.UpdatedAt = DateTime.UtcNow;
|
material.UpdatedAt = DateTime.UtcNow;
|
||||||
_context.Materials.Update(material);
|
context.Materials.Update(material);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -80,6 +85,7 @@ public class MaterialService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task UpdateWithDimensionsAsync(Material material, MaterialDimensions dimensions, bool regenerateSize = false)
|
public async Task UpdateWithDimensionsAsync(Material material, MaterialDimensions dimensions, bool regenerateSize = false)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
material.UpdatedAt = DateTime.UtcNow;
|
material.UpdatedAt = DateTime.UtcNow;
|
||||||
|
|
||||||
if (regenerateSize)
|
if (regenerateSize)
|
||||||
@@ -98,14 +104,14 @@ public class MaterialService
|
|||||||
if (dimensions.Id > 0)
|
if (dimensions.Id > 0)
|
||||||
{
|
{
|
||||||
// Already tracked, just save
|
// Already tracked, just save
|
||||||
_context.Entry(material).State = EntityState.Modified;
|
context.Entry(material).State = EntityState.Modified;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_context.Materials.Update(material);
|
context.Materials.Update(material);
|
||||||
|
|
||||||
// Check if dimensions already exist for this material
|
// Check if dimensions already exist for this material
|
||||||
var existingDimensions = await _context.MaterialDimensions
|
var existingDimensions = await context.MaterialDimensions
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.FirstOrDefaultAsync(d => d.MaterialId == material.Id);
|
.FirstOrDefaultAsync(d => d.MaterialId == material.Id);
|
||||||
|
|
||||||
@@ -113,30 +119,32 @@ public class MaterialService
|
|||||||
{
|
{
|
||||||
// Copy the existing Id to update in place
|
// Copy the existing Id to update in place
|
||||||
dimensions.Id = existingDimensions.Id;
|
dimensions.Id = existingDimensions.Id;
|
||||||
_context.MaterialDimensions.Update(dimensions);
|
context.MaterialDimensions.Update(dimensions);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_context.MaterialDimensions.Add(dimensions);
|
context.MaterialDimensions.Add(dimensions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteAsync(int id)
|
public async Task DeleteAsync(int id)
|
||||||
{
|
{
|
||||||
var material = await _context.Materials.FindAsync(id);
|
await using var context = _factory.CreateDbContext();
|
||||||
|
var material = await context.Materials.FindAsync(id);
|
||||||
if (material != null)
|
if (material != null)
|
||||||
{
|
{
|
||||||
material.IsActive = false;
|
material.IsActive = false;
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> ExistsAsync(MaterialShape shape, string size, int? excludeId = null)
|
public async Task<bool> ExistsAsync(MaterialShape shape, string size, int? excludeId = null)
|
||||||
{
|
{
|
||||||
var query = _context.Materials.Where(m => m.Shape == shape && m.Size == size && m.IsActive);
|
await using var context = _factory.CreateDbContext();
|
||||||
|
var query = context.Materials.Where(m => m.Shape == shape && m.Size == size && m.IsActive);
|
||||||
if (excludeId.HasValue)
|
if (excludeId.HasValue)
|
||||||
{
|
{
|
||||||
query = query.Where(m => m.Id != excludeId.Value);
|
query = query.Where(m => m.Id != excludeId.Value);
|
||||||
@@ -149,10 +157,11 @@ public class MaterialService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<List<Material>> SearchRoundBarByDiameterAsync(decimal targetDiameter, decimal tolerance)
|
public async Task<List<Material>> SearchRoundBarByDiameterAsync(decimal targetDiameter, decimal tolerance)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
var minValue = targetDiameter - tolerance;
|
var minValue = targetDiameter - tolerance;
|
||||||
var maxValue = targetDiameter + tolerance;
|
var maxValue = targetDiameter + tolerance;
|
||||||
|
|
||||||
return await _context.Set<RoundBarDimensions>()
|
return await context.Set<RoundBarDimensions>()
|
||||||
.Include(d => d.Material)
|
.Include(d => d.Material)
|
||||||
.Where(d => d.Material.IsActive)
|
.Where(d => d.Material.IsActive)
|
||||||
.Where(d => d.Diameter >= minValue && d.Diameter <= maxValue)
|
.Where(d => d.Diameter >= minValue && d.Diameter <= maxValue)
|
||||||
@@ -166,10 +175,11 @@ public class MaterialService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<List<Material>> SearchRoundTubeByODAsync(decimal targetOD, decimal tolerance)
|
public async Task<List<Material>> SearchRoundTubeByODAsync(decimal targetOD, decimal tolerance)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
var minValue = targetOD - tolerance;
|
var minValue = targetOD - tolerance;
|
||||||
var maxValue = targetOD + tolerance;
|
var maxValue = targetOD + tolerance;
|
||||||
|
|
||||||
return await _context.Set<RoundTubeDimensions>()
|
return await context.Set<RoundTubeDimensions>()
|
||||||
.Include(d => d.Material)
|
.Include(d => d.Material)
|
||||||
.Where(d => d.Material.IsActive)
|
.Where(d => d.Material.IsActive)
|
||||||
.Where(d => d.OuterDiameter >= minValue && d.OuterDiameter <= maxValue)
|
.Where(d => d.OuterDiameter >= minValue && d.OuterDiameter <= maxValue)
|
||||||
@@ -183,10 +193,11 @@ public class MaterialService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<List<Material>> SearchFlatBarByWidthAsync(decimal targetWidth, decimal tolerance)
|
public async Task<List<Material>> SearchFlatBarByWidthAsync(decimal targetWidth, decimal tolerance)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
var minValue = targetWidth - tolerance;
|
var minValue = targetWidth - tolerance;
|
||||||
var maxValue = targetWidth + tolerance;
|
var maxValue = targetWidth + tolerance;
|
||||||
|
|
||||||
return await _context.Set<FlatBarDimensions>()
|
return await context.Set<FlatBarDimensions>()
|
||||||
.Include(d => d.Material)
|
.Include(d => d.Material)
|
||||||
.Where(d => d.Material.IsActive)
|
.Where(d => d.Material.IsActive)
|
||||||
.Where(d => d.Width >= minValue && d.Width <= maxValue)
|
.Where(d => d.Width >= minValue && d.Width <= maxValue)
|
||||||
@@ -200,10 +211,11 @@ public class MaterialService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<List<Material>> SearchSquareBarBySizeAsync(decimal targetSize, decimal tolerance)
|
public async Task<List<Material>> SearchSquareBarBySizeAsync(decimal targetSize, decimal tolerance)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
var minValue = targetSize - tolerance;
|
var minValue = targetSize - tolerance;
|
||||||
var maxValue = targetSize + tolerance;
|
var maxValue = targetSize + tolerance;
|
||||||
|
|
||||||
return await _context.Set<SquareBarDimensions>()
|
return await context.Set<SquareBarDimensions>()
|
||||||
.Include(d => d.Material)
|
.Include(d => d.Material)
|
||||||
.Where(d => d.Material.IsActive)
|
.Where(d => d.Material.IsActive)
|
||||||
.Where(d => d.Size >= minValue && d.Size <= maxValue)
|
.Where(d => d.Size >= minValue && d.Size <= maxValue)
|
||||||
@@ -217,10 +229,11 @@ public class MaterialService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<List<Material>> SearchSquareTubeBySizeAsync(decimal targetSize, decimal tolerance)
|
public async Task<List<Material>> SearchSquareTubeBySizeAsync(decimal targetSize, decimal tolerance)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
var minValue = targetSize - tolerance;
|
var minValue = targetSize - tolerance;
|
||||||
var maxValue = targetSize + tolerance;
|
var maxValue = targetSize + tolerance;
|
||||||
|
|
||||||
return await _context.Set<SquareTubeDimensions>()
|
return await context.Set<SquareTubeDimensions>()
|
||||||
.Include(d => d.Material)
|
.Include(d => d.Material)
|
||||||
.Where(d => d.Material.IsActive)
|
.Where(d => d.Material.IsActive)
|
||||||
.Where(d => d.Size >= minValue && d.Size <= maxValue)
|
.Where(d => d.Size >= minValue && d.Size <= maxValue)
|
||||||
@@ -234,10 +247,11 @@ public class MaterialService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<List<Material>> SearchRectangularTubeByWidthAsync(decimal targetWidth, decimal tolerance)
|
public async Task<List<Material>> SearchRectangularTubeByWidthAsync(decimal targetWidth, decimal tolerance)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
var minValue = targetWidth - tolerance;
|
var minValue = targetWidth - tolerance;
|
||||||
var maxValue = targetWidth + tolerance;
|
var maxValue = targetWidth + tolerance;
|
||||||
|
|
||||||
return await _context.Set<RectangularTubeDimensions>()
|
return await context.Set<RectangularTubeDimensions>()
|
||||||
.Include(d => d.Material)
|
.Include(d => d.Material)
|
||||||
.Where(d => d.Material.IsActive)
|
.Where(d => d.Material.IsActive)
|
||||||
.Where(d => d.Width >= minValue && d.Width <= maxValue)
|
.Where(d => d.Width >= minValue && d.Width <= maxValue)
|
||||||
@@ -251,10 +265,11 @@ public class MaterialService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<List<Material>> SearchAngleByLegAsync(decimal targetLeg, decimal tolerance)
|
public async Task<List<Material>> SearchAngleByLegAsync(decimal targetLeg, decimal tolerance)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
var minValue = targetLeg - tolerance;
|
var minValue = targetLeg - tolerance;
|
||||||
var maxValue = targetLeg + tolerance;
|
var maxValue = targetLeg + tolerance;
|
||||||
|
|
||||||
return await _context.Set<AngleDimensions>()
|
return await context.Set<AngleDimensions>()
|
||||||
.Include(d => d.Material)
|
.Include(d => d.Material)
|
||||||
.Where(d => d.Material.IsActive)
|
.Where(d => d.Material.IsActive)
|
||||||
.Where(d => d.Leg1 >= minValue && d.Leg1 <= maxValue)
|
.Where(d => d.Leg1 >= minValue && d.Leg1 <= maxValue)
|
||||||
@@ -268,10 +283,11 @@ public class MaterialService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<List<Material>> SearchChannelByHeightAsync(decimal targetHeight, decimal tolerance)
|
public async Task<List<Material>> SearchChannelByHeightAsync(decimal targetHeight, decimal tolerance)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
var minValue = targetHeight - tolerance;
|
var minValue = targetHeight - tolerance;
|
||||||
var maxValue = targetHeight + tolerance;
|
var maxValue = targetHeight + tolerance;
|
||||||
|
|
||||||
return await _context.Set<ChannelDimensions>()
|
return await context.Set<ChannelDimensions>()
|
||||||
.Include(d => d.Material)
|
.Include(d => d.Material)
|
||||||
.Where(d => d.Material.IsActive)
|
.Where(d => d.Material.IsActive)
|
||||||
.Where(d => d.Height >= minValue && d.Height <= maxValue)
|
.Where(d => d.Height >= minValue && d.Height <= maxValue)
|
||||||
@@ -285,10 +301,11 @@ public class MaterialService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<List<Material>> SearchIBeamByHeightAsync(decimal targetHeight, decimal tolerance)
|
public async Task<List<Material>> SearchIBeamByHeightAsync(decimal targetHeight, decimal tolerance)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
var minValue = targetHeight - tolerance;
|
var minValue = targetHeight - tolerance;
|
||||||
var maxValue = targetHeight + tolerance;
|
var maxValue = targetHeight + tolerance;
|
||||||
|
|
||||||
return await _context.Set<IBeamDimensions>()
|
return await context.Set<IBeamDimensions>()
|
||||||
.Include(d => d.Material)
|
.Include(d => d.Material)
|
||||||
.Where(d => d.Material.IsActive)
|
.Where(d => d.Material.IsActive)
|
||||||
.Where(d => d.Height >= minValue && d.Height <= maxValue)
|
.Where(d => d.Height >= minValue && d.Height <= maxValue)
|
||||||
@@ -302,10 +319,11 @@ public class MaterialService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<List<Material>> SearchPipeByNominalSizeAsync(decimal targetNPS, decimal tolerance)
|
public async Task<List<Material>> SearchPipeByNominalSizeAsync(decimal targetNPS, decimal tolerance)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
var minValue = targetNPS - tolerance;
|
var minValue = targetNPS - tolerance;
|
||||||
var maxValue = targetNPS + tolerance;
|
var maxValue = targetNPS + tolerance;
|
||||||
|
|
||||||
return await _context.Set<PipeDimensions>()
|
return await context.Set<PipeDimensions>()
|
||||||
.Include(d => d.Material)
|
.Include(d => d.Material)
|
||||||
.Where(d => d.Material.IsActive)
|
.Where(d => d.Material.IsActive)
|
||||||
.Where(d => d.NominalSize >= minValue && d.NominalSize <= maxValue)
|
.Where(d => d.NominalSize >= minValue && d.NominalSize <= maxValue)
|
||||||
@@ -319,7 +337,8 @@ public class MaterialService
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<List<Material>> GetByShapeAsync(MaterialShape shape, bool includeInactive = false)
|
public async Task<List<Material>> GetByShapeAsync(MaterialShape shape, bool includeInactive = false)
|
||||||
{
|
{
|
||||||
var query = _context.Materials
|
await using var context = _factory.CreateDbContext();
|
||||||
|
var query = context.Materials
|
||||||
.Include(m => m.Dimensions)
|
.Include(m => m.Dimensions)
|
||||||
.Where(m => m.Shape == shape);
|
.Where(m => m.Shape == shape);
|
||||||
|
|
||||||
|
|||||||
@@ -6,16 +6,18 @@ namespace CutList.Web.Services;
|
|||||||
|
|
||||||
public class PurchaseItemService
|
public class PurchaseItemService
|
||||||
{
|
{
|
||||||
private readonly ApplicationDbContext _context;
|
private readonly IDbContextFactory<ApplicationDbContext> _factory;
|
||||||
|
|
||||||
public PurchaseItemService(ApplicationDbContext context)
|
public PurchaseItemService(IDbContextFactory<ApplicationDbContext> factory)
|
||||||
{
|
{
|
||||||
_context = context;
|
_factory = factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<PurchaseItem>> GetAllAsync(PurchaseItemStatus? status = null)
|
public async Task<List<PurchaseItem>> GetAllAsync(PurchaseItemStatus? status = null)
|
||||||
{
|
{
|
||||||
var query = _context.PurchaseItems
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
|
var query = context.PurchaseItems
|
||||||
.Include(p => p.StockItem)
|
.Include(p => p.StockItem)
|
||||||
.ThenInclude(s => s.Material)
|
.ThenInclude(s => s.Material)
|
||||||
.Include(p => p.Supplier)
|
.Include(p => p.Supplier)
|
||||||
@@ -35,7 +37,9 @@ public class PurchaseItemService
|
|||||||
|
|
||||||
public async Task<PurchaseItem?> GetByIdAsync(int id)
|
public async Task<PurchaseItem?> GetByIdAsync(int id)
|
||||||
{
|
{
|
||||||
return await _context.PurchaseItems
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
|
return await context.PurchaseItems
|
||||||
.Include(p => p.StockItem)
|
.Include(p => p.StockItem)
|
||||||
.ThenInclude(s => s.Material)
|
.ThenInclude(s => s.Material)
|
||||||
.Include(p => p.Supplier)
|
.Include(p => p.Supplier)
|
||||||
@@ -45,59 +49,71 @@ public class PurchaseItemService
|
|||||||
|
|
||||||
public async Task<PurchaseItem> CreateAsync(PurchaseItem item)
|
public async Task<PurchaseItem> CreateAsync(PurchaseItem item)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
item.CreatedAt = DateTime.UtcNow;
|
item.CreatedAt = DateTime.UtcNow;
|
||||||
_context.PurchaseItems.Add(item);
|
context.PurchaseItems.Add(item);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CreateBulkAsync(List<PurchaseItem> items)
|
public async Task CreateBulkAsync(List<PurchaseItem> items)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
var now = DateTime.UtcNow;
|
var now = DateTime.UtcNow;
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
{
|
{
|
||||||
item.CreatedAt = now;
|
item.CreatedAt = now;
|
||||||
}
|
}
|
||||||
_context.PurchaseItems.AddRange(items);
|
context.PurchaseItems.AddRange(items);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateAsync(PurchaseItem item)
|
public async Task UpdateAsync(PurchaseItem item)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
item.UpdatedAt = DateTime.UtcNow;
|
item.UpdatedAt = DateTime.UtcNow;
|
||||||
_context.PurchaseItems.Update(item);
|
context.PurchaseItems.Update(item);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateStatusAsync(int id, PurchaseItemStatus status)
|
public async Task UpdateStatusAsync(int id, PurchaseItemStatus status)
|
||||||
{
|
{
|
||||||
var item = await _context.PurchaseItems.FindAsync(id);
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
|
var item = await context.PurchaseItems.FindAsync(id);
|
||||||
if (item != null)
|
if (item != null)
|
||||||
{
|
{
|
||||||
item.Status = status;
|
item.Status = status;
|
||||||
item.UpdatedAt = DateTime.UtcNow;
|
item.UpdatedAt = DateTime.UtcNow;
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateSupplierAsync(int id, int? supplierId)
|
public async Task UpdateSupplierAsync(int id, int? supplierId)
|
||||||
{
|
{
|
||||||
var item = await _context.PurchaseItems.FindAsync(id);
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
|
var item = await context.PurchaseItems.FindAsync(id);
|
||||||
if (item != null)
|
if (item != null)
|
||||||
{
|
{
|
||||||
item.SupplierId = supplierId;
|
item.SupplierId = supplierId;
|
||||||
item.UpdatedAt = DateTime.UtcNow;
|
item.UpdatedAt = DateTime.UtcNow;
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteAsync(int id)
|
public async Task DeleteAsync(int id)
|
||||||
{
|
{
|
||||||
var item = await _context.PurchaseItems.FindAsync(id);
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
|
var item = await context.PurchaseItems.FindAsync(id);
|
||||||
if (item != null)
|
if (item != null)
|
||||||
{
|
{
|
||||||
_context.PurchaseItems.Remove(item);
|
context.PurchaseItems.Remove(item);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,16 +6,18 @@ namespace CutList.Web.Services;
|
|||||||
|
|
||||||
public class StockItemService
|
public class StockItemService
|
||||||
{
|
{
|
||||||
private readonly ApplicationDbContext _context;
|
private readonly IDbContextFactory<ApplicationDbContext> _factory;
|
||||||
|
|
||||||
public StockItemService(ApplicationDbContext context)
|
public StockItemService(IDbContextFactory<ApplicationDbContext> factory)
|
||||||
{
|
{
|
||||||
_context = context;
|
_factory = factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<StockItem>> GetAllAsync(bool includeInactive = false)
|
public async Task<List<StockItem>> GetAllAsync(bool includeInactive = false)
|
||||||
{
|
{
|
||||||
var query = _context.StockItems
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
|
var query = context.StockItems
|
||||||
.Include(s => s.Material)
|
.Include(s => s.Material)
|
||||||
.AsQueryable();
|
.AsQueryable();
|
||||||
|
|
||||||
@@ -33,7 +35,9 @@ public class StockItemService
|
|||||||
|
|
||||||
public async Task<List<StockItem>> GetByMaterialAsync(int materialId, bool includeInactive = false)
|
public async Task<List<StockItem>> GetByMaterialAsync(int materialId, bool includeInactive = false)
|
||||||
{
|
{
|
||||||
var query = _context.StockItems
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
|
var query = context.StockItems
|
||||||
.Include(s => s.Material)
|
.Include(s => s.Material)
|
||||||
.Where(s => s.MaterialId == materialId);
|
.Where(s => s.MaterialId == materialId);
|
||||||
|
|
||||||
@@ -49,7 +53,9 @@ public class StockItemService
|
|||||||
|
|
||||||
public async Task<StockItem?> GetByIdAsync(int id)
|
public async Task<StockItem?> GetByIdAsync(int id)
|
||||||
{
|
{
|
||||||
return await _context.StockItems
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
|
return await context.StockItems
|
||||||
.Include(s => s.Material)
|
.Include(s => s.Material)
|
||||||
.Include(s => s.SupplierOfferings)
|
.Include(s => s.SupplierOfferings)
|
||||||
.ThenInclude(o => o.Supplier)
|
.ThenInclude(o => o.Supplier)
|
||||||
@@ -58,33 +64,41 @@ public class StockItemService
|
|||||||
|
|
||||||
public async Task<StockItem> CreateAsync(StockItem stockItem)
|
public async Task<StockItem> CreateAsync(StockItem stockItem)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
stockItem.CreatedAt = DateTime.UtcNow;
|
stockItem.CreatedAt = DateTime.UtcNow;
|
||||||
_context.StockItems.Add(stockItem);
|
context.StockItems.Add(stockItem);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
return stockItem;
|
return stockItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateAsync(StockItem stockItem)
|
public async Task UpdateAsync(StockItem stockItem)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
stockItem.UpdatedAt = DateTime.UtcNow;
|
stockItem.UpdatedAt = DateTime.UtcNow;
|
||||||
_context.StockItems.Update(stockItem);
|
context.StockItems.Update(stockItem);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteAsync(int id)
|
public async Task DeleteAsync(int id)
|
||||||
{
|
{
|
||||||
var stockItem = await _context.StockItems.FindAsync(id);
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
|
var stockItem = await context.StockItems.FindAsync(id);
|
||||||
if (stockItem != null)
|
if (stockItem != null)
|
||||||
{
|
{
|
||||||
stockItem.IsActive = false;
|
stockItem.IsActive = false;
|
||||||
stockItem.UpdatedAt = DateTime.UtcNow;
|
stockItem.UpdatedAt = DateTime.UtcNow;
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> ExistsAsync(int materialId, decimal lengthInches, int? excludeId = null)
|
public async Task<bool> ExistsAsync(int materialId, decimal lengthInches, int? excludeId = null)
|
||||||
{
|
{
|
||||||
var query = _context.StockItems.Where(s =>
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
|
var query = context.StockItems.Where(s =>
|
||||||
s.MaterialId == materialId &&
|
s.MaterialId == materialId &&
|
||||||
s.LengthInches == lengthInches &&
|
s.LengthInches == lengthInches &&
|
||||||
s.IsActive);
|
s.IsActive);
|
||||||
@@ -100,7 +114,9 @@ public class StockItemService
|
|||||||
// Stock transaction methods
|
// Stock transaction methods
|
||||||
public async Task<StockTransaction> AddStockAsync(int stockItemId, int quantity, int? supplierId = null, decimal? unitPrice = null, string? notes = null)
|
public async Task<StockTransaction> AddStockAsync(int stockItemId, int quantity, int? supplierId = null, decimal? unitPrice = null, string? notes = null)
|
||||||
{
|
{
|
||||||
var stockItem = await _context.StockItems.FindAsync(stockItemId)
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
|
var stockItem = await context.StockItems.FindAsync(stockItemId)
|
||||||
?? throw new InvalidOperationException($"Stock item {stockItemId} not found");
|
?? throw new InvalidOperationException($"Stock item {stockItemId} not found");
|
||||||
|
|
||||||
var transaction = new StockTransaction
|
var transaction = new StockTransaction
|
||||||
@@ -117,15 +133,17 @@ public class StockItemService
|
|||||||
stockItem.QuantityOnHand += quantity;
|
stockItem.QuantityOnHand += quantity;
|
||||||
stockItem.UpdatedAt = DateTime.UtcNow;
|
stockItem.UpdatedAt = DateTime.UtcNow;
|
||||||
|
|
||||||
_context.StockTransactions.Add(transaction);
|
context.StockTransactions.Add(transaction);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
return transaction;
|
return transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<decimal?> GetAverageCostAsync(int stockItemId)
|
public async Task<decimal?> GetAverageCostAsync(int stockItemId)
|
||||||
{
|
{
|
||||||
var transactions = await _context.StockTransactions
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
|
var transactions = await context.StockTransactions
|
||||||
.Where(t => t.StockItemId == stockItemId && t.Type == StockTransactionType.Received && t.UnitPrice.HasValue)
|
.Where(t => t.StockItemId == stockItemId && t.Type == StockTransactionType.Received && t.UnitPrice.HasValue)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
@@ -140,7 +158,9 @@ public class StockItemService
|
|||||||
|
|
||||||
public async Task<decimal?> GetLastPurchasePriceAsync(int stockItemId)
|
public async Task<decimal?> GetLastPurchasePriceAsync(int stockItemId)
|
||||||
{
|
{
|
||||||
return await _context.StockTransactions
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
|
return await context.StockTransactions
|
||||||
.Where(t => t.StockItemId == stockItemId && t.Type == StockTransactionType.Received && t.UnitPrice.HasValue)
|
.Where(t => t.StockItemId == stockItemId && t.Type == StockTransactionType.Received && t.UnitPrice.HasValue)
|
||||||
.OrderByDescending(t => t.CreatedAt)
|
.OrderByDescending(t => t.CreatedAt)
|
||||||
.Select(t => t.UnitPrice)
|
.Select(t => t.UnitPrice)
|
||||||
@@ -149,7 +169,9 @@ public class StockItemService
|
|||||||
|
|
||||||
public async Task<StockTransaction> UseStockAsync(int stockItemId, int quantity, int? jobId = null, string? notes = null)
|
public async Task<StockTransaction> UseStockAsync(int stockItemId, int quantity, int? jobId = null, string? notes = null)
|
||||||
{
|
{
|
||||||
var stockItem = await _context.StockItems.FindAsync(stockItemId)
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
|
var stockItem = await context.StockItems.FindAsync(stockItemId)
|
||||||
?? throw new InvalidOperationException($"Stock item {stockItemId} not found");
|
?? throw new InvalidOperationException($"Stock item {stockItemId} not found");
|
||||||
|
|
||||||
var transaction = new StockTransaction
|
var transaction = new StockTransaction
|
||||||
@@ -165,15 +187,17 @@ public class StockItemService
|
|||||||
stockItem.QuantityOnHand -= quantity;
|
stockItem.QuantityOnHand -= quantity;
|
||||||
stockItem.UpdatedAt = DateTime.UtcNow;
|
stockItem.UpdatedAt = DateTime.UtcNow;
|
||||||
|
|
||||||
_context.StockTransactions.Add(transaction);
|
context.StockTransactions.Add(transaction);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
return transaction;
|
return transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<StockTransaction> AdjustStockAsync(int stockItemId, int newQuantity, string? notes = null)
|
public async Task<StockTransaction> AdjustStockAsync(int stockItemId, int newQuantity, string? notes = null)
|
||||||
{
|
{
|
||||||
var stockItem = await _context.StockItems.FindAsync(stockItemId)
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
|
var stockItem = await context.StockItems.FindAsync(stockItemId)
|
||||||
?? throw new InvalidOperationException($"Stock item {stockItemId} not found");
|
?? throw new InvalidOperationException($"Stock item {stockItemId} not found");
|
||||||
|
|
||||||
var difference = newQuantity - stockItem.QuantityOnHand;
|
var difference = newQuantity - stockItem.QuantityOnHand;
|
||||||
@@ -190,15 +214,17 @@ public class StockItemService
|
|||||||
stockItem.QuantityOnHand = newQuantity;
|
stockItem.QuantityOnHand = newQuantity;
|
||||||
stockItem.UpdatedAt = DateTime.UtcNow;
|
stockItem.UpdatedAt = DateTime.UtcNow;
|
||||||
|
|
||||||
_context.StockTransactions.Add(transaction);
|
context.StockTransactions.Add(transaction);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
return transaction;
|
return transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<StockTransaction> ScrapStockAsync(int stockItemId, int quantity, string? notes = null)
|
public async Task<StockTransaction> ScrapStockAsync(int stockItemId, int quantity, string? notes = null)
|
||||||
{
|
{
|
||||||
var stockItem = await _context.StockItems.FindAsync(stockItemId)
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
|
var stockItem = await context.StockItems.FindAsync(stockItemId)
|
||||||
?? throw new InvalidOperationException($"Stock item {stockItemId} not found");
|
?? throw new InvalidOperationException($"Stock item {stockItemId} not found");
|
||||||
|
|
||||||
var transaction = new StockTransaction
|
var transaction = new StockTransaction
|
||||||
@@ -213,15 +239,17 @@ public class StockItemService
|
|||||||
stockItem.QuantityOnHand -= quantity;
|
stockItem.QuantityOnHand -= quantity;
|
||||||
stockItem.UpdatedAt = DateTime.UtcNow;
|
stockItem.UpdatedAt = DateTime.UtcNow;
|
||||||
|
|
||||||
_context.StockTransactions.Add(transaction);
|
context.StockTransactions.Add(transaction);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
return transaction;
|
return transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<StockTransaction>> GetTransactionHistoryAsync(int stockItemId, int? limit = null)
|
public async Task<List<StockTransaction>> GetTransactionHistoryAsync(int stockItemId, int? limit = null)
|
||||||
{
|
{
|
||||||
var query = _context.StockTransactions
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
|
var query = context.StockTransactions
|
||||||
.Include(t => t.Job)
|
.Include(t => t.Job)
|
||||||
.Include(t => t.Supplier)
|
.Include(t => t.Supplier)
|
||||||
.Where(t => t.StockItemId == stockItemId)
|
.Where(t => t.StockItemId == stockItemId)
|
||||||
@@ -238,10 +266,12 @@ public class StockItemService
|
|||||||
|
|
||||||
public async Task<int> RecalculateQuantityAsync(int stockItemId)
|
public async Task<int> RecalculateQuantityAsync(int stockItemId)
|
||||||
{
|
{
|
||||||
var stockItem = await _context.StockItems.FindAsync(stockItemId)
|
await using var context = _factory.CreateDbContext();
|
||||||
|
|
||||||
|
var stockItem = await context.StockItems.FindAsync(stockItemId)
|
||||||
?? throw new InvalidOperationException($"Stock item {stockItemId} not found");
|
?? throw new InvalidOperationException($"Stock item {stockItemId} not found");
|
||||||
|
|
||||||
var calculatedQuantity = await _context.StockTransactions
|
var calculatedQuantity = await context.StockTransactions
|
||||||
.Where(t => t.StockItemId == stockItemId)
|
.Where(t => t.StockItemId == stockItemId)
|
||||||
.SumAsync(t => t.Quantity);
|
.SumAsync(t => t.Quantity);
|
||||||
|
|
||||||
@@ -249,7 +279,7 @@ public class StockItemService
|
|||||||
{
|
{
|
||||||
stockItem.QuantityOnHand = calculatedQuantity;
|
stockItem.QuantityOnHand = calculatedQuantity;
|
||||||
stockItem.UpdatedAt = DateTime.UtcNow;
|
stockItem.UpdatedAt = DateTime.UtcNow;
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
return calculatedQuantity;
|
return calculatedQuantity;
|
||||||
|
|||||||
@@ -6,16 +6,17 @@ namespace CutList.Web.Services;
|
|||||||
|
|
||||||
public class SupplierService
|
public class SupplierService
|
||||||
{
|
{
|
||||||
private readonly ApplicationDbContext _context;
|
private readonly IDbContextFactory<ApplicationDbContext> _factory;
|
||||||
|
|
||||||
public SupplierService(ApplicationDbContext context)
|
public SupplierService(IDbContextFactory<ApplicationDbContext> factory)
|
||||||
{
|
{
|
||||||
_context = context;
|
_factory = factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<Supplier>> GetAllAsync(bool includeInactive = false)
|
public async Task<List<Supplier>> GetAllAsync(bool includeInactive = false)
|
||||||
{
|
{
|
||||||
var query = _context.Suppliers.AsQueryable();
|
await using var context = _factory.CreateDbContext();
|
||||||
|
var query = context.Suppliers.AsQueryable();
|
||||||
if (!includeInactive)
|
if (!includeInactive)
|
||||||
{
|
{
|
||||||
query = query.Where(s => s.IsActive);
|
query = query.Where(s => s.IsActive);
|
||||||
@@ -25,7 +26,8 @@ public class SupplierService
|
|||||||
|
|
||||||
public async Task<Supplier?> GetByIdAsync(int id)
|
public async Task<Supplier?> GetByIdAsync(int id)
|
||||||
{
|
{
|
||||||
return await _context.Suppliers
|
await using var context = _factory.CreateDbContext();
|
||||||
|
return await context.Suppliers
|
||||||
.Include(s => s.Offerings)
|
.Include(s => s.Offerings)
|
||||||
.ThenInclude(o => o.StockItem)
|
.ThenInclude(o => o.StockItem)
|
||||||
.ThenInclude(si => si.Material)
|
.ThenInclude(si => si.Material)
|
||||||
@@ -34,32 +36,36 @@ public class SupplierService
|
|||||||
|
|
||||||
public async Task<Supplier> CreateAsync(Supplier supplier)
|
public async Task<Supplier> CreateAsync(Supplier supplier)
|
||||||
{
|
{
|
||||||
|
await using var context = _factory.CreateDbContext();
|
||||||
supplier.CreatedAt = DateTime.UtcNow;
|
supplier.CreatedAt = DateTime.UtcNow;
|
||||||
_context.Suppliers.Add(supplier);
|
context.Suppliers.Add(supplier);
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
return supplier;
|
return supplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateAsync(Supplier supplier)
|
public async Task UpdateAsync(Supplier supplier)
|
||||||
{
|
{
|
||||||
_context.Suppliers.Update(supplier);
|
await using var context = _factory.CreateDbContext();
|
||||||
await _context.SaveChangesAsync();
|
context.Suppliers.Update(supplier);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteAsync(int id)
|
public async Task DeleteAsync(int id)
|
||||||
{
|
{
|
||||||
var supplier = await _context.Suppliers.FindAsync(id);
|
await using var context = _factory.CreateDbContext();
|
||||||
|
var supplier = await context.Suppliers.FindAsync(id);
|
||||||
if (supplier != null)
|
if (supplier != null)
|
||||||
{
|
{
|
||||||
supplier.IsActive = false;
|
supplier.IsActive = false;
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Offering management
|
// Offering management
|
||||||
public async Task<List<SupplierOffering>> GetOfferingsForSupplierAsync(int supplierId)
|
public async Task<List<SupplierOffering>> GetOfferingsForSupplierAsync(int supplierId)
|
||||||
{
|
{
|
||||||
return await _context.SupplierOfferings
|
await using var context = _factory.CreateDbContext();
|
||||||
|
return await context.SupplierOfferings
|
||||||
.Include(o => o.StockItem)
|
.Include(o => o.StockItem)
|
||||||
.ThenInclude(si => si.Material)
|
.ThenInclude(si => si.Material)
|
||||||
.Where(o => o.SupplierId == supplierId && o.IsActive)
|
.Where(o => o.SupplierId == supplierId && o.IsActive)
|
||||||
@@ -71,7 +77,8 @@ public class SupplierService
|
|||||||
|
|
||||||
public async Task<List<SupplierOffering>> GetOfferingsForStockItemAsync(int stockItemId)
|
public async Task<List<SupplierOffering>> GetOfferingsForStockItemAsync(int stockItemId)
|
||||||
{
|
{
|
||||||
return await _context.SupplierOfferings
|
await using var context = _factory.CreateDbContext();
|
||||||
|
return await context.SupplierOfferings
|
||||||
.Include(o => o.Supplier)
|
.Include(o => o.Supplier)
|
||||||
.Where(o => o.StockItemId == stockItemId && o.IsActive && o.Supplier.IsActive)
|
.Where(o => o.StockItemId == stockItemId && o.IsActive && o.Supplier.IsActive)
|
||||||
.OrderBy(o => o.Supplier.Name)
|
.OrderBy(o => o.Supplier.Name)
|
||||||
@@ -80,7 +87,8 @@ public class SupplierService
|
|||||||
|
|
||||||
public async Task<SupplierOffering?> GetOfferingByIdAsync(int id)
|
public async Task<SupplierOffering?> GetOfferingByIdAsync(int id)
|
||||||
{
|
{
|
||||||
return await _context.SupplierOfferings
|
await using var context = _factory.CreateDbContext();
|
||||||
|
return await context.SupplierOfferings
|
||||||
.Include(o => o.StockItem)
|
.Include(o => o.StockItem)
|
||||||
.ThenInclude(si => si.Material)
|
.ThenInclude(si => si.Material)
|
||||||
.Include(o => o.Supplier)
|
.Include(o => o.Supplier)
|
||||||
@@ -89,30 +97,34 @@ public class SupplierService
|
|||||||
|
|
||||||
public async Task<SupplierOffering> AddOfferingAsync(SupplierOffering offering)
|
public async Task<SupplierOffering> AddOfferingAsync(SupplierOffering offering)
|
||||||
{
|
{
|
||||||
_context.SupplierOfferings.Add(offering);
|
await using var context = _factory.CreateDbContext();
|
||||||
await _context.SaveChangesAsync();
|
context.SupplierOfferings.Add(offering);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
return offering;
|
return offering;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateOfferingAsync(SupplierOffering offering)
|
public async Task UpdateOfferingAsync(SupplierOffering offering)
|
||||||
{
|
{
|
||||||
_context.SupplierOfferings.Update(offering);
|
await using var context = _factory.CreateDbContext();
|
||||||
await _context.SaveChangesAsync();
|
context.SupplierOfferings.Update(offering);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteOfferingAsync(int id)
|
public async Task DeleteOfferingAsync(int id)
|
||||||
{
|
{
|
||||||
var offering = await _context.SupplierOfferings.FindAsync(id);
|
await using var context = _factory.CreateDbContext();
|
||||||
|
var offering = await context.SupplierOfferings.FindAsync(id);
|
||||||
if (offering != null)
|
if (offering != null)
|
||||||
{
|
{
|
||||||
offering.IsActive = false;
|
offering.IsActive = false;
|
||||||
await _context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> OfferingExistsAsync(int supplierId, int stockItemId, int? excludeId = null)
|
public async Task<bool> OfferingExistsAsync(int supplierId, int stockItemId, int? excludeId = null)
|
||||||
{
|
{
|
||||||
var query = _context.SupplierOfferings.Where(o =>
|
await using var context = _factory.CreateDbContext();
|
||||||
|
var query = context.SupplierOfferings.Where(o =>
|
||||||
o.SupplierId == supplierId &&
|
o.SupplierId == supplierId &&
|
||||||
o.StockItemId == stockItemId &&
|
o.StockItemId == stockItemId &&
|
||||||
o.IsActive);
|
o.IsActive);
|
||||||
|
|||||||
Reference in New Issue
Block a user