Files
lux/EgwCoreLib.Lux.Data/Repository/Sales/OrderRowRepository.cs
T
2026-06-10 16:15:51 +02:00

407 lines
17 KiB
C#

namespace EgwCoreLib.Lux.Data.Repository.Sales
{
public class OrderRowRepository : BaseRepository, IOrderRowRepository
{
#region Public Constructors
public OrderRowRepository(IDbContextFactory<DataLayerContext> ctxFactory) : base(ctxFactory)
{
}
#endregion Public Constructors
#region Public Methods
/// <inheritdoc />
public async Task<bool> AddAsync(OrderRowModel entity)
{
await using var dbCtx = await CreateContextAsync();
await dbCtx.DbSetOrderRow.AddAsync(entity);
return await dbCtx.SaveChangesAsync() > 0;
}
/// <inheritdoc />
public async Task<bool> DeleteAsync(OrderRowModel entity)
{
await using var dbCtx = await CreateContextAsync();
// Wrap in transaction for atomicity (multi-row update + delete)
await using var tx = await dbCtx.Database.BeginTransactionAsync();
try
{
// 1. Recupero il record da eliminare
var dbResult = await dbCtx.DbSetOrderRow
.FirstOrDefaultAsync(x => x.OrderRowID == entity.OrderRowID);
if (dbResult == null)
return false;
// 2. Recupero i record successivi da shiftare
var list2Move = await dbCtx.DbSetOrderRow
.Where(x => x.OrderID == entity.OrderID && x.RowNum > dbResult.RowNum)
.ToListAsync();
foreach (var item in list2Move)
{
item.RowNum--;
dbCtx.Entry(item).State = EntityState.Modified;
}
// 3. Rimuovo il record
dbCtx.DbSetOrderRow.Remove(dbResult);
// 4. Salvo tutto
bool done = await dbCtx.SaveChangesAsync() > 0;
if (done)
await tx.CommitAsync();
return done;
}
catch
{
await tx.RollbackAsync();
throw;
}
}
/// <inheritdoc />
public async Task<List<ItemModel>> GetBomItemsAsync()
{
await using var dbCtx = await CreateContextAsync();
return await dbCtx.DbSetItem
.Where(x => x.ItemType == ItemClassType.Bom || x.ItemType == ItemClassType.BomAlt)
.ToListAsync();
}
/// <inheritdoc />
public async Task<OrderRowModel?> GetByIdAsync(int OrderRowId)
{
await using var dbCtx = await CreateContextAsync();
return await dbCtx.DbSetOrderRow
.Include(r => r.SellingItemNav)
.FirstOrDefaultAsync(x => x.OrderRowID == OrderRowId);
}
/// <inheritdoc />
public async Task<List<OrderRowModel>> GetByParentAsync(int orderId)
{
await using var dbCtx = await CreateContextAsync();
return await dbCtx.DbSetOrderRow
.Where(x => x.OrderID == orderId)
.Include(r => r.SellingItemNav)
.AsNoTracking()
.ToListAsync();
}
/// <inheritdoc />
public async Task<List<OrderRowModel>> GetByStateAsync(OrderStates reqState, DateTime dtStart, DateTime dtEnd)
{
await using var dbCtx = await CreateContextAsync();
return await dbCtx.DbSetOrderRow
.Where(x => x.OrderRowState == reqState && x.Inserted >= dtStart && x.Inserted <= dtEnd)
.Include(r => r.SellingItemNav)
.AsNoTracking()
.ToListAsync();
}
/// <inheritdoc />
public async Task<List<OrderRowModel>> GetByStateMinAsync(OrderStates reqState, DateTime dtStart, DateTime dtEnd)
{
await using var dbCtx = await CreateContextAsync();
return await dbCtx.DbSetOrderRow
.Where(x => x.OrderRowState >= reqState && x.Inserted >= dtStart && x.Inserted <= dtEnd)
.Include(r => r.SellingItemNav)
.AsNoTracking()
.ToListAsync();
}
/// <inheritdoc />
public async Task<OrderRowModel?> GetByUidAsync(string OrderRowUid)
{
await using var dbCtx = await CreateContextAsync();
return await dbCtx.DbSetOrderRow
.Include(r => r.SellingItemNav)
.FirstOrDefaultAsync(x => x.OrderRowUID == OrderRowUid);
}
/// <inheritdoc />
public async Task<List<ItemGroupModel>> GetItemGroupsAsync()
{
await using var dbCtx = await CreateContextAsync();
return await dbCtx.DbSetItemGroup.ToListAsync();
}
/// <inheritdoc />
public async Task<bool> SaveRowsAsync(List<OrderRowModel> rows)
{
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(OrderRowModel entity)
{
await using var dbCtx = await CreateContextAsync();
// Recuperiamo l'entità tracciata dal context
var trackedEntity = await dbCtx.DbSetOrderRow.FirstOrDefaultAsync(x => x.OrderRowID == entity.OrderRowID);
if (trackedEntity != null)
{
// Aggiorna i valori dell'entità tracciata con quelli della nuova
dbCtx.Entry(trackedEntity).CurrentValues.SetValues(entity);
}
else
{
dbCtx.DbSetOrderRow.Update(entity);
}
return await dbCtx.SaveChangesAsync() > 0;
}
public async Task<bool> SaveProdEstAsync(string uID, string prodEstim)
{
// Add validation for null or empty list
if (string.IsNullOrWhiteSpace(uID) || string.IsNullOrWhiteSpace(prodEstim)) 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
{
// recupero offerta...
var currRec = dbCtx
.DbSetOrderRow
.Where(x => x.OrderRowUID == uID)
.FirstOrDefault();
// se trovo aggiorno
if (currRec != null)
{
// genero WLD x controllo
var currWLD = new WorkLoadDetailDTO(currRec.OrderRowUID, currRec.ProdEstimate);
// faccio update in cascata dei record collegati x macchine e items
var listMachDb = dbCtx
.DbSetProdPlant
.Select(x => x.ProdPlantCod)
.ToList();
List<string> listMaccCurr = currWLD.MachineCalcResults.Select(x => x.Name).OrderBy(x => x).ToList() ?? new List<string>();
if (listMaccCurr != null && listMaccCurr.Any())
{
var listDiff = listMaccCurr.Except(listMachDb).ToList();
if (listDiff.Any())
{
foreach (var macch in listDiff)
{
dbCtx
.DbSetProdPlant
.Add(new ProductionPlantModel() { ProdPlantCod = macch, ProdPlantDescript = macch });
}
}
}
// resetta assegnazioni prodGroup agli items...
var listItem2upd = await dbCtx
.DbSetProdItem
.Where(x => x.OrderRowID == currRec.OrderRowID && x.ProdGroupID != null)
.ToListAsync();
if (listItem2upd != null && listItem2upd.Count > 0)
{
// li aggiorna tutti resettando ProdGroupID
listItem2upd.ForEach(x => x.ProdGroupID = null);
}
// elimina eventuali oggetti ProductionGroup precedenti
List<ProductionGroupModel> listProdGroup2Rem = await dbCtx
.DbSetProdGroup
.Where(x => x.OrderRowID == currRec.OrderRowID)
.ToListAsync();
// rimuovo...
if (listProdGroup2Rem != null && listProdGroup2Rem.Count > 0)
{
dbCtx.DbSetProdGroup.RemoveRange(listProdGroup2Rem);
}
/*----------------------------------
* Generazione ProdGroup
* FixMe ToDo !!!
*
* rifare considerando le REALI combinazioni scaturite x questo specifico caso e
* - ENUMERARE le combinazioni
* - ogni combinazione sarà un caso specifico tra 0...N dove N è il totale delle macchine gestite
* - i successivi calcoli di balance/stima saranno fatti x questo SPECIFICO ID GROUP così da fare prima... a sto punto GroupIP potrebbe essere un int 0...n oppure l'id del record... forse meglio il counter 0..n
*
* */
int grpIdx = 1;
// preparo x add nuovi ProductionGroup da analisi ritorno stime
List<ProductionGroupModel> listProdGroup2Add = new List<ProductionGroupModel>();
// 1: non lavorabili...
if (currWLD.ListUnWorkable.Count > 0)
{
// calcolo il dizionario degli elementi...
Dictionary<string, ProdMachineDetailDto> newWorkGroupList = new();
ProdMachineDetailDto detProd = new ProdMachineDetailDto()
{
TagList = currWLD.ListUnWorkable
};
newWorkGroupList.Add("", detProd);
string rawWGL = JsonConvert.SerializeObject(newWorkGroupList);
ProductionGroupModel newRec = new ProductionGroupModel()
{
OrderRowID = currRec.OrderRowID,
GrpIdx = grpIdx++,
WorkGroupListRaw = rawWGL
};
listProdGroup2Add.Add(newRec);
}
// dizionario x macchina delle parts LAVORABILI su impianto..
var machineTags = currWLD.MachineCalcResults
.ToDictionary(
m => m.Name,
m => m.PartList
.Where(p => p.CalcResult == EgwCoreLib.Lux.Core.Enums.PartVerificationResult.MACHINABLE)
.ToList()
);
// ciclo x tutte le combinazioni di gruppi lavorabilità...
foreach (var item in currWLD.LoadDetail)
{
// calcolo il dizionario degli elementi...
Dictionary<string, ProdMachineDetailDto> newWorkGroupList = new();
foreach (var machineName in item.Machines)
{
decimal effectiveTime = 0;
if (currRec.Envir == Constants.EXECENVIRONMENTS.WINDOW)
{
// Creiamo un set dei tag del gruppo per una ricerca veloce O(1)
var groupTagsSet = item.Tags.ToHashSet();
effectiveTime = currWLD.TotMaxTime;
}
else
{
// Recuperiamo i dati della macchina dal dizionario
if (machineTags.TryGetValue(machineName, out var machineParts))
{
// Creiamo un set dei tag del gruppo per una ricerca veloce O(1)
var groupTagsSet = item.Tags.ToHashSet();
// Sommiamo il tempo solo per i pezzi che appartengono a questo gruppo
effectiveTime = machineParts
.Where(p => groupTagsSet.Contains(p.Tag))
.Sum(p => p.Time);
}
}
ProdMachineDetailDto detProd = new ProdMachineDetailDto()
{
TagList = item.Tags,
Time = effectiveTime // Tempo reale specifico per questa macchina/gruppo
};
newWorkGroupList.Add(machineName, detProd);
}
string rawWGL = JsonConvert.SerializeObject(newWorkGroupList);
ProductionGroupModel newRec = new ProductionGroupModel()
{
OrderRowID = currRec.OrderRowID,
GrpIdx = grpIdx++,
WorkGroupListRaw = rawWGL
};
listProdGroup2Add.Add(newRec);
}
// aggiungo i record...
dbCtx.DbSetProdGroup.AddRange(listProdGroup2Add);
// aggiorno info Estimation, tempi e stato
currRec.ProdEstimate = prodEstim;
if (!string.IsNullOrEmpty(prodEstim))
{
currRec.OrderRowState = OrderStates.Estimated;
}
var totEstim = listProdGroup2Add.Sum(x => x.TotalEstimTime);
currRec.ProdEstimTime = totEstim;
dbCtx.Entry(currRec).State = EntityState.Modified;
}
// salvo TUTTI i cambiamenti...
bool done = await dbCtx.SaveChangesAsync() > 0;
if (done)
await tx.CommitAsync();
return done;
}
catch
{
await tx.RollbackAsync();
throw;
}
}
/// <inheritdoc />
public async Task<int> ValidateAsync(List<OrderRowModel> list2chk)
{
int numDone = 0;
// Add validation for null or empty list
if (list2chk.Count == 0)
return numDone;
// context
await using var dbCtx = await CreateContextAsync();
// Wrap in transaction for atomicity (batch update multiple rows)
await using var tx = await dbCtx.Database.BeginTransactionAsync();
try
{
// verifica preliminare: serve SSE stato e estimate non corrispondono...
var list2fix = list2chk
.Where(x => x.OrderRowState == OrderStates.Created && !string.IsNullOrEmpty(x.ProdEstimate))
.ToList();
if (list2fix.Any())
{
// per ogni record processo intera validazione
foreach (var item in list2fix)
{
bool fatto = await SaveProdEstAsync(item.OrderRowUID, item.ProdEstimate);
numDone += fatto ? 1 : 0;
}
}
// salvo TUTTI i cambiamenti...
bool done = await dbCtx.SaveChangesAsync() > 0;
if (done)
await tx.CommitAsync();
return numDone;
}
catch
{
await tx.RollbackAsync();
throw;
}
}
#endregion Public Methods
}
}