Gestione task EXT non producibili (con transaction bulk x mysql), parziale

This commit is contained in:
Samuele Locatelli
2025-12-24 12:18:30 +01:00
parent eb2fc49e24
commit d0ea040b78
8 changed files with 228 additions and 39 deletions
+202 -26
View File
@@ -21,6 +21,49 @@ namespace EgwCoreLib.Lux.Data.Controllers
{
// manca costruttore parametrico contoller...
#region Public Methods
/// <summary>
/// Esegue assegnazione bulk dei ProdItem ad un unico ProdAssign parent
/// </summary>
/// <param name="ProdAssignId"></param>
/// <param name="itemsToAssign"></param>
/// <returns></returns>
public async Task<int> ProdItemBulkAssignItems(int ProdAssignId, Dictionary<string, double> itemsToAssign)
{
int totalUpdated = 0;
//using (DataLayerContext dbCtx = new DataLayerContext(_config))
using (DataLayerContext dbCtx = new DataLayerContext())
{
using var transaction = await dbCtx.Database.BeginTransactionAsync();
try
{
foreach (var entry in itemsToAssign)
{
// Esegue un UPDATE diretto: UPDATE production_item SET ProdAssignID = x, EstimTime = y WHERE ProdLabel = z
int rowsAffected = await dbCtx.DbSetProdItem
.Where(p => p.ProdLabel == entry.Key)
.ExecuteUpdateAsync(setters => setters
.SetProperty(p => p.ProdAssignID, ProdAssignId)
.SetProperty(p => p.EstimTime, entry.Value)
);
totalUpdated += rowsAffected;
}
await transaction.CommitAsync();
}
catch (Exception exc)
{
await transaction.RollbackAsync();
Log.Error($"Eccezione durante ProdItemBulkAssignItems{Environment.NewLine}{exc}");
//throw;
}
}
return totalUpdated;
}
#endregion Public Methods
#region Internal Methods
internal async Task<List<EnvirParamModel>> ConfEnvirParamGetAllAsync()
@@ -2665,7 +2708,7 @@ namespace EgwCoreLib.Lux.Data.Controllers
catch (Exception exc)
{
#if false
await transaction.RollbackAsync();
await transaction.RollbackAsync();
#endif
Log.Error($"Eccezione durante OrderFromOffer{Environment.NewLine}{exc}");
}
@@ -2832,6 +2875,31 @@ namespace EgwCoreLib.Lux.Data.Controllers
return dbResult;
}
/// <summary>
/// Validazione record:
/// - controllo state vs estimate
/// - segnala il numero dei record aggiornati
/// </summary>
/// <param name="list2chk">Elenco record da verificare</param>
internal async Task<int> OrderRowListValidate(List<OrderRowModel> list2chk)
{
int numDone = 0;
// 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 OrderRowUpsertProdEst(item.OrderRowUID, item.ProdEstimate);
numDone += fatto ? 1 : 0;
}
}
return numDone;
}
/// <summary>
/// Effettua update stato await BOM/PRICE per l'Ordine indicato
/// </summary>
@@ -3072,31 +3140,6 @@ namespace EgwCoreLib.Lux.Data.Controllers
return answ;
}
/// <summary>
/// Validazione record:
/// - controllo state vs estimate
/// - segnala il numero dei record aggiornati
/// </summary>
/// <param name="list2chk">Elenco record da verificare</param>
internal async Task<int> OrderRowListValidate(List<OrderRowModel> list2chk)
{
int numDone = 0;
// 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 OrderRowUpsertProdEst(item.OrderRowUID, item.ProdEstimate);
numDone += fatto ? 1 : 0;
}
}
return numDone;
}
/// <summary>
/// Upsert record
/// </summary>
@@ -3163,6 +3206,139 @@ namespace EgwCoreLib.Lux.Data.Controllers
return dbResult;
}
/// <summary>
/// Elenco record ProductionAssign dato OrderRow
/// </summary>
/// <returns></returns>
internal async Task<List<ProductionAssignModel>> ProdAssignByOrderRow(int OrderRowID)
{
List<ProductionAssignModel> dbResult = new List<ProductionAssignModel>();
//using (DataLayerContext dbCtx = new DataLayerContext(_config))
using (DataLayerContext dbCtx = new DataLayerContext())
{
try
{
dbResult = await dbCtx
.DbSetProdAssign
.Where(x => x.OrderRowID == OrderRowID)
.Include(i => i.ItemsNav)
.ToListAsync();
}
catch (Exception exc)
{
Log.Error($"Eccezione durante ProdAssignByOrderRow{Environment.NewLine}{exc}");
}
}
return dbResult;
}
/// <summary>
/// Verifica esistenza record ProductionAssign per OrderRow dato elenco plant, creando i missing e tornando poi lista
/// </summary>
/// <returns></returns>
internal async Task<List<ProductionAssignModel>> ProdAssignCheckExist(int OrderRowID, List<string> ListPlant)
{
List<ProductionAssignModel> dbResult = new List<ProductionAssignModel>();
// Rimuoviamo i duplicati dalla lista in input a monte per evitare tentativi di inserimento doppi
var uniqueInputPlants = ListPlant.Distinct().ToList();
//using (DataLayerContext dbCtx = new DataLayerContext(_config))
using (DataLayerContext dbCtx = new DataLayerContext())
{
await using var transaction = await dbCtx.Database.BeginTransactionAsync();
try
{
// record esistenti
var existRec = await dbCtx.DbSetProdAssign
.AsNoTracking()
.Where(p => p.OrderRowID == OrderRowID)
.Select(p => p.ProdPlantCod)
.ToListAsync();
// elementi da inserire
var plantsToAdd = uniqueInputPlants.Except(existRec).ToList();
if (plantsToAdd.Any())
{
// 3. Crea i nuovi oggetti
var newAssignments = plantsToAdd.Select(plantCod => new ProductionAssignModel
{
OrderRowID = OrderRowID,
ProdPlantCod = plantCod
});
// 4. Aggiunta massiva e salvataggio
await dbCtx.DbSetProdAssign.AddRangeAsync(newAssignments);
await dbCtx.SaveChangesAsync();
}
// recupero elenco
dbResult = await dbCtx
.DbSetProdAssign
.Where(x => x.OrderRowID == OrderRowID)
.Include(i => i.ItemsNav)
.ToListAsync();
// Conferma la transazione
await transaction.CommitAsync();
}
catch (Exception exc)
{
await transaction.RollbackAsync();
Log.Error($"Eccezione durante ProdAssignCheckExist{Environment.NewLine}{exc}");
}
}
return dbResult;
}
/// <summary>
/// Elimina record ProductionAssign + svincola record item dipendenti
/// </summary>
/// <returns></returns>
internal async Task<bool> ProdAssignDelete(int ProdAssignID)
{
bool removed = false;
//using (DataLayerContext dbCtx = new DataLayerContext(_config))
using (DataLayerContext dbCtx = new DataLayerContext())
{
await using var transaction = await dbCtx.Database.BeginTransactionAsync();
try
{
// 1. Carica il record parent con i relativi figli
var parent = await dbCtx.DbSetProdAssign
.Include(p => p.ItemsNav)
.FirstOrDefaultAsync(p => p.ProdAssignID == ProdAssignID);
if (parent != null)
{
// 2. Disassociazione dei figli
// Iteriamo sui figli e settiamo a null il riferimento al parent
foreach (var item in parent.ItemsNav)
{
// Nota: La proprietà FK nel modello Item deve essere nullable (es. int?)
item.ProdAssignID = null;
}
// 3. Eliminazione del parent
dbCtx.DbSetProdAssign.Remove(parent);
// 4. Salvataggio atomico
await dbCtx.SaveChangesAsync();
}
await transaction.CommitAsync();
removed = true;
}
catch (Exception exc)
{
await transaction.RollbackAsync();
Log.Error($"Eccezione durante ProdAssignDelete{Environment.NewLine}{exc}");
}
}
return removed;
}
/// <summary>
/// Elenco ProdItem dato OrderRow
/// </summary>
@@ -50,9 +50,6 @@ namespace EgwCoreLib.Lux.Data.DbModel.Production
/// <summary>
/// Etichetta dell'item
/// </summary>
#if false
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
#endif
public string ProdLabel { get; set; } = "";
#if false
public string ProdLabel
+1 -1
View File
@@ -4,7 +4,7 @@
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Version>0.9.2512.2317</Version>
<Version>0.9.2512.2411</Version>
</PropertyGroup>
<ItemGroup>
@@ -65,22 +65,28 @@
@if (DetailRecord.NumKo > 0)
{
<li class="list-group-item d-flex justify-content-between">
<div class="px-0">Non producibili: <span class="fw-bold px-1">@DetailRecord.NumKo</span></div>
<div class="px-0"><button class="btn btn-danger">Send EXT <i class="fa-solid fa-triangle-exclamation"></i></button></div>
<div class="col-9">Non producibili: <span class="fw-bold px-1">@DetailRecord.NumKo</span></div>
<div class="col-3">
<button class="btn btn-sm btn-danger w-100">Send EXT <i class="fa-solid fa-triangle-exclamation"></i></button>
</div>
</li>
}
@if (DetailRecord.NumOkVin > 0)
{
<li class="list-group-item d-flex justify-content-between">
<div class="px-0">Vincolati: <span class="fw-bold px-1">@DetailRecord.NumOkVin</span></div>
<div class="px-0"><button class="btn btn-info">Assegna <i class="fa-solid fa-thumbtack"></i></button></div>
<div class="col-9">Vincolati: <span class="fw-bold px-1">@DetailRecord.NumOkVin</span></div>
<div class="col-3">
<button class="btn btn-sm btn-info w-100">Assegna <i class="fa-solid fa-thumbtack"></i></button>
</div>
</li>
}
@if (DetailRecord.NumOk > 0)
{
<li class="list-group-item d-flex justify-content-between">
<div class="px-0">Producibili ovunque: <span class="fw-bold px-1">@DetailRecord.NumOk</span></div>
<div class="px-0"><button class="btn btn-success" @onclick="() => ToggleBalance(DetailRecord.ListWorkable)">Dividi <i class="fa-solid fa-scissors"></i></button></div>
<div class="col-9">Producibili ovunque: <span class="fw-bold px-1">@DetailRecord.NumOk</span></div>
<div class="col-3">
<button class="btn btn-sm btn-success w-100" @onclick="() => ToggleBalance(DetailRecord.ListWorkable)">Dividi <i class="fa-solid fa-scissors"></i></button>
</div>
</li>
}
</ul>
@@ -1,6 +1,7 @@
using EgwCoreLib.Lux.Core.Generic;
using EgwCoreLib.Lux.Core.RestPayload;
using Microsoft.AspNetCore.Components;
using NLog.Targets.Wrappers;
namespace Lux.UI.Components.Compo.WorkLoad
{
@@ -97,6 +98,15 @@ namespace Lux.UI.Components.Compo.WorkLoad
}
}
/// <summary>
/// Assegnazione item unworkable a EXT
/// </summary>
/// <returns></returns>
protected async Task SendUnworkExt()
{
//
}
#endregion Protected Methods
#region Private Fields
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<i>LUX - Web Windows MES</i>
<h4>Versione: 0.9.2512.2318</h4>
<h4>Versione: 0.9.2512.2411</h4>
<br /> Note di rilascio:
<ul>
<li>
+1 -1
View File
@@ -1 +1 @@
0.9.2512.2318
0.9.2512.2411
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>0.9.2512.2318</version>
<version>0.9.2512.2411</version>
<url>http://nexus.steamware.net/repository/SWS/GPW/stable/GPW.UI.zip</url>
<changelog>http://nexus.steamware.net/repository/SWS/GPW/stable/ChangeLog.html</changelog>
<mandatory>false</mandatory>