Files
lux/EgwCoreLib.Lux.Data/Repository/Supplier/BuyOrderRepository.cs
T
2026-04-22 10:40:40 +02:00

233 lines
9.0 KiB
C#

namespace EgwCoreLib.Lux.Data.Repository.Supplier
{
public class BuyOrderRepository : BaseRepository, IBuyOrderRepository
{
#region Public Constructors
public BuyOrderRepository(IDbContextFactory<DataLayerContext> ctxFactory) : base(ctxFactory)
{
}
#endregion Public Constructors
#region Public Methods
/// <inheritdoc />
public async Task<bool> AddAsync(BuyOrderModel entity)
{
await using var dbCtx = await CreateContextAsync();
await dbCtx.DbSetBuyOrder.AddAsync(entity);
return await dbCtx.SaveChangesAsync() > 0;
}
/// <inheritdoc />
public async Task<bool> DeleteAsync(BuyOrderModel entity)
{
await using var dbCtx = await CreateContextAsync();
dbCtx.DbSetBuyOrder.Remove(entity);
return await dbCtx.SaveChangesAsync() > 0;
}
/// <inheritdoc />
public async Task<List<BuyOrderModel>> GetAllAsync()
{
await using var dbCtx = await CreateContextAsync();
return await dbCtx.DbSetBuyOrder
.Include(c => c.SupplierNav)
.Include(o => o.BuyOrderRowNav)
.ThenInclude(s => s.ItemNav)
.Include(m => m.BuyOrderRowNav)
.ThenInclude(s => s.MatReqNav)
.AsNoTracking()
.ToListAsync();
}
/// <inheritdoc />
public async Task<BuyOrderModel> GenerateFromSelectionAsync(Dictionary<string, List<int>> currSelDict)
{
if (currSelDict == null) throw new ArgumentNullException(nameof(currSelDict));
if (currSelDict.Values.Any(l => l == null || l.Count == 0))
throw new ArgumentException("La selezione contiene valori null o vuoti.");
await using var dbCtx = await CreateContextAsync();
// 1. Avvia transazione per garantire atomicità
await using var tx = await dbCtx.Database.BeginTransactionAsync();
try
{
// 2. Calcola il prossimo RefNum per l'anno corrente
int nextRefNum = await GetNextRefNumAsync(DateTime.Today.Year);
// 3. Crea il nuovo ordine padre
var newOrder = new BuyOrderModel
{
RefYear = DateTime.Today.Year,
RefNum = nextRefNum,
RefRev = 1,
Description = $"Acquisto raggruppato - {DateTime.Now:yyyy-MM-dd HH:mm}",
BuyOrderRowNav = new List<BuyOrderRowModel>()
};
dbCtx.DbSetBuyOrder.Add(newOrder);
await dbCtx.SaveChangesAsync(); // Necessario per generare BuyOrderID
// 4. Recupera in BATCH tutti i MatReq selezionati (Anti-N+1)
var allSelectedIds = currSelDict.Values.SelectMany(ids => ids).Distinct().ToList();
var matReqLookup = await dbCtx.DbSetMaterialReq
.Where(m => allSelectedIds.Contains(m.MatReqID))
.ToListAsync();
//.Select(m => new { m.MatReqID, m.ItemID, m.TotQty, m.CodGroup })
//.ToDictionaryAsync(m => m.MatReqID);
// 5. Costruisci righe e join table
int rowCounter = 1;
foreach (var matReqIds in currSelDict.Values)
{
foreach (var matReqId in matReqIds)
{
var matReqInfo = matReqLookup.FirstOrDefault(x => x.MatReqID == matReqId);
if (matReqInfo == null) continue;
var row = new BuyOrderRowModel
{
BuyOrderID = newOrder.BuyOrderID,
ItemID = matReqInfo.ItemID,
RowNum = rowCounter++,
ClassCode = matReqInfo.CodGroup,
ItemCode = matReqInfo.ItemCode,
DescriptionCode = matReqInfo.Description
};
var join = new BuyOrderRow2MatReqModel
{
MatReqID = matReqId,
Qty = matReqInfo.TotQty,
// BuyOrderRowID verrà popolato automaticamente da EF Core al SaveChanges
};
// segno processato
matReqInfo.Processed = true;
// EF Core gestisce le relazioni attraverso le collection
row.MatReqNav.Add(join);
newOrder.BuyOrderRowNav.Add(row);
}
}
await dbCtx.SaveChangesAsync();
await tx.CommitAsync();
return newOrder;
}
catch
{
await tx.RollbackAsync();
throw;
}
}
/// <summary>
/// Helper calcolo del nuovo refNum dell'ordine
/// </summary>
/// <param name="year"></param>
/// <returns></returns>
private async Task<int> GetNextRefNumAsync(int year)
{
await using var dbCtx = await CreateContextAsync();
var max = await dbCtx.DbSetBuyOrder
.Where(o => o.RefYear == year)
.MaxAsync(o => (int?)o.RefNum);
return (max ?? 0) + 1;
}
/// <inheritdoc />
public async Task<BuyOrderModel?> GetByIdAsync(int recId)
{
await using var dbCtx = await CreateContextAsync();
return await dbCtx.DbSetBuyOrder
.Where(x => x.BuyOrderID == recId)
.Include(c => c.SupplierNav)
.Include(o => o.BuyOrderRowNav)
.ThenInclude(s => s.ItemNav)
.Include(m => m.BuyOrderRowNav)
.ThenInclude(s => s.MatReqNav)
.FirstOrDefaultAsync();
}
/// <inheritdoc />
public async Task<List<BuyOrderModel>> GetFiltAsync(DateTime inizio, DateTime fine)
{
await using var dbCtx = await CreateContextAsync();
return await dbCtx.DbSetBuyOrder
.Where(x => x.Inserted >= inizio && x.Inserted <= fine)
.Include(c => c.SupplierNav)
.Include(o => o.BuyOrderRowNav)
.ThenInclude(s => s.ItemNav)
.Include(m => m.BuyOrderRowNav)
.ThenInclude(s => s.MatReqNav)
.AsNoTracking()
.ToListAsync();
}
/// <inheritdoc />
public async Task<List<BuyOrderRowModel>> GetRowsAsync(int recId)
{
await using var dbCtx = await CreateContextAsync();
return await dbCtx.DbSetBuyOrderRow
.Where(x => x.BuyOrderID == recId)
.ToListAsync();
}
/// <inheritdoc />
public async Task<bool> SaveRowsAsync(List<BuyOrderRowModel> rows)
{
// Add validation for null or empty list
if (rows == null || rows.Count == 0) return false;
await using var dbCtx = await CreateContextAsync();
// Wrap in transaction for atomicity (batch update multiple rows)
await using var tx = await dbCtx.Database.BeginTransactionAsync();
try
{
foreach (var row in rows)
dbCtx.Entry(row).State = EntityState.Modified;
bool done = await dbCtx.SaveChangesAsync() > 0;
if (done)
await tx.CommitAsync();
return done;
}
catch
{
await tx.RollbackAsync();
throw;
}
}
/// <inheritdoc />
public async Task<bool> UpdateAsync(BuyOrderModel entity)
{
await using var dbCtx = await CreateContextAsync();
// Recuperiamo l'entità tracciata dal context
var trackedEntity = await dbCtx.DbSetBuyOrder.FirstOrDefaultAsync(x => x.BuyOrderID == entity.BuyOrderID);
if (trackedEntity != null)
{
// Aggiorna i valori dell'entità tracciata con quelli della nuova
dbCtx.Entry(trackedEntity).CurrentValues.SetValues(entity);
}
else
{
dbCtx.DbSetBuyOrder.Update(entity);
}
return await dbCtx.SaveChangesAsync() > 0;
}
#endregion Public Methods
}
}