diff --git a/EgwCoreLib.Lux.Data/Controllers/LuxController.cs b/EgwCoreLib.Lux.Data/Controllers/LuxController.cs
index fdb8f0b3..40a70368 100644
--- a/EgwCoreLib.Lux.Data/Controllers/LuxController.cs
+++ b/EgwCoreLib.Lux.Data/Controllers/LuxController.cs
@@ -4,7 +4,6 @@ using EgwCoreLib.Lux.Data.DbModel.Config;
using EgwCoreLib.Lux.Data.DbModel.Items;
using EgwCoreLib.Lux.Data.DbModel.Job;
using EgwCoreLib.Lux.Data.DbModel.Production;
-using EgwCoreLib.Lux.Data.DbModel.Sales;
using EgwCoreLib.Lux.Data.DbModel.Stats;
using EgwCoreLib.Lux.Data.Domains;
using EgwMultiEngineManager.Data;
@@ -228,6 +227,8 @@ namespace EgwCoreLib.Lux.Data.Controllers
///
///
///
+#if false
+
internal async Task OrderRowUpsertProdEst(string uID, string prodEstim)
{
bool answ = false;
@@ -293,7 +294,7 @@ namespace EgwCoreLib.Lux.Data.Controllers
* Generazione ProdGroup
* FixMe ToDo !!!
*
- * rifare onsiderando le REALI combinazioni scaturite x questo specifico caso e
+ * 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
@@ -417,6 +418,7 @@ namespace EgwCoreLib.Lux.Data.Controllers
}
return numDone;
}
+#endif
///
/// Elenco record Fasi da DB
@@ -602,118 +604,6 @@ namespace EgwCoreLib.Lux.Data.Controllers
}
#endif
-#if false
- ///
- /// Aggiorna record ProdOdl (se trovato) con BOM (raw) ricevuta
- ///
- ///
- ///
- ///
- internal async Task ProdOdlUpdateBomAsync(string uID, string bomRaw)
- {
- bool answ = false;
- //using (DataLayerContext dbCtx = new DataLayerContext(_config))
- using (DataLayerContext dbCtx = new DataLayerContext())
- {
- try
- {
- var currRec = dbCtx
- .DbSetProdODL
- .Where(x => x.OdlTag == uID)
- .FirstOrDefault();
- // se trovato --> salvo BOM e calcolo costi
- if (currRec != null)
- {
- currRec.RawBoM = bomRaw;
- dbCtx.Entry(currRec).State = EntityState.Modified;
- }
-
- // salvo...
- var result = dbCtx.SaveChanges();
- answ = result > 0;
- }
- catch (Exception exc)
- {
- Log.Error($"Eccezione durante ProdOdlUpdateBomAsync{Environment.NewLine}{exc}");
- }
- }
- return answ;
- }
-
- ///
- /// Aggiorna record ProdOdl (se trovato) con ItemListRaw (raw) inviata x calcolo PROD
- ///
- ///
- ///
- ///
- internal async Task ProdOdlUpdateItemRawAsync(string uID, string itemListRaw)
- {
- bool answ = false;
- //using (DataLayerContext dbCtx = new DataLayerContext(_config))
- using (DataLayerContext dbCtx = new DataLayerContext())
- {
- try
- {
- var currRec = dbCtx
- .DbSetProdODL
- .Where(x => x.OdlTag == uID)
- .FirstOrDefault();
- // se trovato --> salvo BOM e calcolo costi
- if (currRec != null)
- {
- currRec.RawItemRawList = itemListRaw;
- dbCtx.Entry(currRec).State = EntityState.Modified;
- }
-
- // salvo...
- var result = dbCtx.SaveChanges();
- answ = result > 0;
- }
- catch (Exception exc)
- {
- Log.Error($"Eccezione durante ProdOdlUpdateItemRawAsync{Environment.NewLine}{exc}");
- }
- }
- return answ;
- }
-
- ///
- /// Aggiorna record ProdOdl (se trovato) con RawMaterialList (raw) ricevuta da calcolo PROD
- ///
- ///
- ///
- ///
- internal async Task ProdOdlUpdateRawMaterialAsync(string uID, string materialListRaw)
- {
- bool answ = false;
- //using (DataLayerContext dbCtx = new DataLayerContext(_config))
- using (DataLayerContext dbCtx = new DataLayerContext())
- {
- try
- {
- var currRec = dbCtx
- .DbSetProdODL
- .Where(x => x.OdlTag == uID)
- .FirstOrDefault();
- // se trovato --> salvo BOM e calcolo costi
- if (currRec != null)
- {
- currRec.RawMaterials = materialListRaw;
- dbCtx.Entry(currRec).State = EntityState.Modified;
- }
-
- // salvo...
- var result = dbCtx.SaveChanges();
- answ = result > 0;
- }
- catch (Exception exc)
- {
- Log.Error($"Eccezione durante ProdOdlUpdateRawMaterialAsync{Environment.NewLine}{exc}");
- }
- }
- return answ;
- }
-#endif
///
/// Esegue merge dei dati nella tab profili del DB con le info accessorie...
diff --git a/EgwCoreLib.Lux.Data/Repository/Sales/IOrderRowRepository.cs b/EgwCoreLib.Lux.Data/Repository/Sales/IOrderRowRepository.cs
index a5fe46c0..141c7c0b 100644
--- a/EgwCoreLib.Lux.Data/Repository/Sales/IOrderRowRepository.cs
+++ b/EgwCoreLib.Lux.Data/Repository/Sales/IOrderRowRepository.cs
@@ -26,10 +26,14 @@ namespace EgwCoreLib.Lux.Data.Repository.Sales
Task> GetItemGroupsAsync();
+ Task SaveProdEstAsync(string uID, string prodEstim);
+
Task SaveRowsAsync(List rows);
Task UpdateAsync(OrderRowModel entity);
+ Task ValidateAsync(List list2chk);
+
#endregion Public Methods
}
}
\ No newline at end of file
diff --git a/EgwCoreLib.Lux.Data/Repository/Sales/OrderRowRepository.cs b/EgwCoreLib.Lux.Data/Repository/Sales/OrderRowRepository.cs
index 9e668842..77b99722 100644
--- a/EgwCoreLib.Lux.Data/Repository/Sales/OrderRowRepository.cs
+++ b/EgwCoreLib.Lux.Data/Repository/Sales/OrderRowRepository.cs
@@ -1,6 +1,10 @@
-using EgwCoreLib.Lux.Data.DbModel.Items;
+using EgwCoreLib.Lux.Core.Generic;
+using EgwCoreLib.Lux.Core.RestPayload;
+using EgwCoreLib.Lux.Data.DbModel.Items;
+using EgwCoreLib.Lux.Data.DbModel.Production;
using EgwCoreLib.Lux.Data.DbModel.Sales;
using Microsoft.EntityFrameworkCore;
+using Newtonsoft.Json;
using static EgwCoreLib.Lux.Core.Enums;
namespace EgwCoreLib.Lux.Data.Repository.Sales
@@ -171,6 +175,221 @@ namespace EgwCoreLib.Lux.Data.Repository.Sales
return await dbCtx.SaveChangesAsync() > 0;
}
+ public async Task 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 = dbCtx.Database.BeginTransaction();
+ 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 listMaccCurr = currWLD.MachineCalcResults.Select(x => x.Name).OrderBy(x => x).ToList() ?? new List();
+ 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...
+ List 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 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 listProdGroup2Add = new List();
+ // 1: non lavorabili...
+ if (currWLD.ListUnWorkable.Count > 0)
+ {
+ // calcolo il dizionario degli elementi...
+ Dictionary 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 newWorkGroupList = new();
+ foreach (var machineName in item.Machines)
+ {
+ decimal effectiveTime = 0;
+ // 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)
+ tx.Commit();
+
+ return done;
+ }
+ catch
+ {
+ tx.Rollback();
+ throw;
+ }
+ }
+
+ public async Task ValidateAsync(List 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 = dbCtx.Database.BeginTransaction();
+ 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)
+ tx.Commit();
+
+ return numDone;
+ }
+ catch
+ {
+ tx.Rollback();
+ throw;
+ }
+ }
+
#endregion Public Methods
}
}
\ No newline at end of file
diff --git a/EgwCoreLib.Lux.Data/Services/DataLayerServices.cs b/EgwCoreLib.Lux.Data/Services/DataLayerServices.cs
index 70b9c022..0f888882 100644
--- a/EgwCoreLib.Lux.Data/Services/DataLayerServices.cs
+++ b/EgwCoreLib.Lux.Data/Services/DataLayerServices.cs
@@ -21,8 +21,6 @@ namespace EgwCoreLib.Lux.Data.Services
{
public class DataLayerServices : BaseServ
{
- private readonly IServiceProvider _serviceProvider;
-
#region Public Constructors
public DataLayerServices(
@@ -50,15 +48,15 @@ namespace EgwCoreLib.Lux.Data.Services
}
}
+ #endregion Public Constructors
- #region Lazy InitServices
+ #region Public Properties
public IOfferRowService OfferRowServ => _serviceProvider.GetRequiredService();
+ public IOrderRowService OrderRowServ => _serviceProvider.GetRequiredService();
public IProductionOdlService PrOdlServ => _serviceProvider.GetRequiredService();
- #endregion Lazy InitServices
-
- #endregion Public Constructors
+ #endregion Public Properties
#region Public Methods
@@ -181,27 +179,6 @@ namespace EgwCoreLib.Lux.Data.Services
return answ;
}
- ///
- /// Validazione record:
- /// - controllo state vs estimate
- /// - segnala il numero dei record aggiornati
- ///
- /// Elenco record da verificare
- public async Task OrderRowListValidate(List list2chk)
- {
- using var activity = StartActivity();
- string source = "DB+REDIS";
- // calcolo
- int numDone = await dbController.OrderRowListValidate(list2chk);
- // svuoto cache...
- await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:Orders:*");
- await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:OrderRows:*");
- await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:OrderRowsByState:*");
- activity?.SetTag("data.source", source);
- LogTrace($"{source} | trace: {activity?.TraceId} | {activity?.Duration.TotalMilliseconds}ms");
- return numDone;
- }
-
///
/// Elenco completo Fasi
///
@@ -289,25 +266,6 @@ namespace EgwCoreLib.Lux.Data.Services
LogTrace($"{source} | trace: {activity?.TraceId} | {activity?.Duration.TotalMilliseconds}ms");
return fatto;
}
-#if false
-
- ///
- /// Aggiorna record ProdOdl (se trovato) con ItemListRaw (raw) inviata x calcolo PROD
- ///
- ///
- ///
- ///
- public async Task ProdOdlUpdateItemRawAsync(string uID, string itemListRaw)
- {
- using var activity = StartActivity();
- string source = "DB+REDIS";
- bool result = await dbController.ProdOdlUpdateItemRawAsync(uID, itemListRaw);
- await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:ProdOdl:*");
- activity?.SetTag("data.source", source);
- LogTrace($"{source} | trace: {activity?.TraceId} | {activity?.Duration.TotalMilliseconds}ms");
- return result;
- }
-#endif
///
/// Esegue salvataggio BOM sul DB
@@ -439,8 +397,11 @@ namespace EgwCoreLib.Lux.Data.Services
/// Environment dell'item
/// Stima ProdEstimate serializzata
///
- public async Task SaveProdEstimateAsync(string uID, Constants.EXECENVIRONMENTS execEnvironment, string prodEstim)
+ public async Task SaveProdEstimateAsync(string uID, Constants.EXECENVIRONMENTS execEnvironment, string prodEstim)
{
+ var result = await OrderRowServ.SaveProdEstAsync(uID, prodEstim);
+ return result;
+#if false
using var activity = StartActivity();
string source = "DB";
// salvo sul DB il risultato della BOM
@@ -451,6 +412,7 @@ namespace EgwCoreLib.Lux.Data.Services
}
activity?.SetTag("data.source", source);
LogTrace($"{source} | trace: {activity?.TraceId} | {activity?.Duration.TotalMilliseconds}ms");
+#endif
}
///
@@ -730,6 +692,30 @@ namespace EgwCoreLib.Lux.Data.Services
#region Private Fields
private new static Logger Log = LogManager.GetCurrentClassLogger();
+ private readonly IServiceProvider _serviceProvider;
+#if false
+
+ ///
+ /// Validazione record:
+ /// - controllo state vs estimate
+ /// - segnala il numero dei record aggiornati
+ ///
+ /// Elenco record da verificare
+ public async Task OrderRowListValidate(List list2chk)
+ {
+ using var activity = StartActivity();
+ string source = "DB+REDIS";
+ // calcolo
+ int numDone = await dbController.OrderRowListValidate(list2chk);
+ // svuoto cache...
+ await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:Orders:*");
+ await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:OrderRows:*");
+ await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:OrderRowsByState:*");
+ activity?.SetTag("data.source", source);
+ LogTrace($"{source} | trace: {activity?.TraceId} | {activity?.Duration.TotalMilliseconds}ms");
+ return numDone;
+ }
+#endif
private string redisBaseKey = "Lux:Cache";
#endregion Private Fields
diff --git a/EgwCoreLib.Lux.Data/Services/Sales/IOrderRowService.cs b/EgwCoreLib.Lux.Data/Services/Sales/IOrderRowService.cs
index 621d7ea8..b644c1ea 100644
--- a/EgwCoreLib.Lux.Data/Services/Sales/IOrderRowService.cs
+++ b/EgwCoreLib.Lux.Data/Services/Sales/IOrderRowService.cs
@@ -36,6 +36,10 @@ namespace EgwCoreLib.Lux.Data.Services.Sales
Task UpsertAsync(OrderRowModel upsRec);
+ Task SaveProdEstAsync(string uID, string prodEstim);
+
+ Task ValidateAsync(List list2chk);
+
#endregion Public Methods
}
}
\ No newline at end of file
diff --git a/EgwCoreLib.Lux.Data/Services/Sales/OrderRowService.cs b/EgwCoreLib.Lux.Data/Services/Sales/OrderRowService.cs
index 10856e87..3fe2899d 100644
--- a/EgwCoreLib.Lux.Data/Services/Sales/OrderRowService.cs
+++ b/EgwCoreLib.Lux.Data/Services/Sales/OrderRowService.cs
@@ -392,6 +392,30 @@ namespace EgwCoreLib.Lux.Data.Services.Sales
});
}
+
+ ///
+ /// Validazione record:
+ /// - controllo state vs estimate
+ /// - segnala il numero dei record aggiornati
+ ///
+ /// Elenco record da verificare
+ public async Task ValidateAsync(List list2chk)
+ {
+ return await TraceAsync($"{_className}.Validate", async (activity) =>
+ {
+ activity?.SetTag("db.operation", "Validate");
+
+ var success = await _repo.ValidateAsync(list2chk);
+
+ await ClearCacheAsync($"{_redisBaseKey}:{_className}:*");
+ await ClearCacheAsync($"{_redisBaseKey}:Order:*");
+ await ClearCacheAsync($"{_redisBaseKey}:Orders:*");
+ await ClearCacheAsync($"{_redisBaseKey}:OrderRowsByState:*");
+
+ return success;
+ });
+ }
+
///
/// Upsert record OrderRow
///
@@ -436,6 +460,27 @@ namespace EgwCoreLib.Lux.Data.Services.Sales
});
}
+ ///
+ /// Esegue salvataggio ProdEstimate (per riga ordine) sul DB
+ ///
+ /// UID dell'item offerta di cui si è ricevuto la ProdEstim
+ /// Environment dell'item
+ /// Stima ProdEstimate serializzata
+ ///
+ public async Task SaveProdEstAsync(string uID, string prodEstim)
+ {
+ return await TraceAsync($"{_className}.UpsertProdEst", async (activity) =>
+ {
+ activity?.SetTag("db.operation", "UpsertProdEst");
+
+ var success = await _repo.SaveProdEstAsync(uID, prodEstim);
+
+ await ClearCacheAsync($"{_redisBaseKey}:{_className}:*");
+
+ return success;
+ });
+ }
+
#endregion Public Methods
#region Private Fields
diff --git a/Lux.UI/Components/Compo/OrderRowMan.razor.cs b/Lux.UI/Components/Compo/OrderRowMan.razor.cs
index 3ca4d58a..36bb04a5 100644
--- a/Lux.UI/Components/Compo/OrderRowMan.razor.cs
+++ b/Lux.UI/Components/Compo/OrderRowMan.razor.cs
@@ -1450,7 +1450,7 @@ namespace Lux.UI.Components.Compo
AllRecords = await OrdRService.GetByParentAsync(OrderID);
totalCount = AllRecords.Count();
// faccio un controllo/fix dei record
- int numFix = await DLService.OrderRowListValidate(AllRecords);
+ int numFix = await OrdRService.ValidateAsync(AllRecords);
}
}