407 lines
17 KiB
C#
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
|
|
}
|
|
} |