using EgwCoreLib.Lux.Core.Generic; using EgwCoreLib.Lux.Core.RestPayload; using EgwCoreLib.Lux.Data.DbModel.Config; using EgwCoreLib.Lux.Data.DbModel.Cost; using EgwCoreLib.Lux.Data.DbModel.Items; using EgwCoreLib.Lux.Data.DbModel.Production; using EgwCoreLib.Lux.Data.DbModel.Sales; using EgwCoreLib.Lux.Data.DbModel.Stats; using EgwCoreLib.Lux.Data.DbModel.Task; using EgwCoreLib.Lux.Data.DbModel.Utils; using EgwMultiEngineManager.Data; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using MySqlConnector; using Newtonsoft.Json; using NLog; using StackExchange.Redis; using System.Data; using System.Globalization; using static EgwCoreLib.Lux.Core.Enums; namespace EgwCoreLib.Lux.Data.Controllers { internal class LuxController { // manca costruttore parametrico contoller... #region Internal Methods internal async Task> ConfEnvirParamGetAllAsync() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetEnvirPar .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ConfEnvirParamGetAllAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Esegue eliminazione /// /// /// internal async Task ConfGlassDeleteAsync(GlassModel rec2del) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var dbResult = dbCtx .DbSetConfGlass .Where(x => x.GlassID == rec2del.GlassID) .FirstOrDefault(); // se trovato --> elimino e sposto i rimanenti... if (dbResult != null) { // elimino dbCtx.DbSetConfGlass.Remove(dbResult); // salvo tutto await dbCtx.SaveChangesAsync(); } } catch (Exception exc) { Log.Error($"Eccezione durante ConfGlassDeleteAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Elenco completo Config Glass /// /// internal async Task> ConfGlassGetAllAsync() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetConfGlass .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ConfGlassGetAllAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Upsert record ConfGlass /// /// /// internal async Task ConfGlassUpsertAsync(GlassModel upsRec) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currRec = dbCtx .DbSetConfGlass .Where(x => upsRec.GlassID > 0 && x.GlassID == upsRec.GlassID) .FirstOrDefault(); // se trovato --> aggiorno if (currRec != null) { currRec.Code = string.IsNullOrEmpty(upsRec.Code) ? $"{upsRec.GlassID:0000}" : upsRec.Code; currRec.Description = upsRec.Description; currRec.Thickness = upsRec.Thickness; dbCtx.Entry(currRec).State = EntityState.Modified; } // se mancasse --> aggiungo else { dbCtx.DbSetConfGlass.Add(upsRec); } // salvo... int numAct = await dbCtx.SaveChangesAsync(); answ = numAct > 0; } catch (Exception exc) { Log.Error($"Eccezione durante ConfGlassUpsertAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Esegue eliminazione /// /// /// internal async Task ConfProfileDeleteAsync(ProfileModel rec2del) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var dbResult = dbCtx .DbSetConfProfile .Where(x => x.ProfileID == rec2del.ProfileID) .FirstOrDefault(); // se trovato --> elimino e sposto i rimanenti... if (dbResult != null) { // elimino dbCtx.DbSetConfProfile.Remove(dbResult); // salvo tutto await dbCtx.SaveChangesAsync(); } } catch (Exception exc) { Log.Error($"Eccezione durante ConfProfileDeleteAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Elenco completo Config Profile /// /// internal async Task> ConfProfileGetAllAsync() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetConfProfile .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ConfProfileGetAllAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Upsert record ConfProfile /// /// /// internal async Task ConfProfileUpsertAsync(ProfileModel upsRec) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currRec = dbCtx .DbSetConfProfile .Where(x => upsRec.ProfileID > 0 && x.ProfileID == upsRec.ProfileID) .FirstOrDefault(); // se trovato --> aggiorno if (currRec != null) { currRec.Code = string.IsNullOrEmpty(upsRec.Code) ? $"{upsRec.ProfileID:0000}" : upsRec.Code; currRec.Description = upsRec.Description; currRec.Thickness = upsRec.Thickness; dbCtx.Entry(currRec).State = EntityState.Modified; } // se mancasse --> aggiungo else { dbCtx.DbSetConfProfile.Add(upsRec); } // salvo... int numAct = await dbCtx.SaveChangesAsync(); answ = numAct > 0; } catch (Exception exc) { Log.Error($"Eccezione durante ConfProfileUpsertAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Esegue eliminazione /// /// /// internal async Task ConfWoodDeleteAsync(WoodModel rec2del) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var dbResult = dbCtx .DbSetConfWood .Where(x => x.WoodID == rec2del.WoodID) .FirstOrDefault(); // se trovato --> elimino e sposto i rimanenti... if (dbResult != null) { // elimino dbCtx.DbSetConfWood.Remove(dbResult); // salvo tutto await dbCtx.SaveChangesAsync(); } } catch (Exception exc) { Log.Error($"Eccezione durante ConfWoodDeleteAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Elenco completo Config Wood /// /// internal async Task> ConfWoodGetAllAsync() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetConfWood .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ConfWoodGetAllAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Upsert record ConfWood /// /// /// internal async Task ConfWoodUpsertAsync(WoodModel upsRec) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currRec = dbCtx .DbSetConfWood .Where(x => upsRec.WoodID > 0 && x.WoodID == upsRec.WoodID) .FirstOrDefault(); // se trovato --> aggiorno if (currRec != null) { currRec.Code = string.IsNullOrEmpty(upsRec.Code) ? $"{upsRec.WoodID:0000}" : upsRec.Code; currRec.Description = upsRec.Description; currRec.Type = upsRec.Type; dbCtx.Entry(currRec).State = EntityState.Modified; } // se mancasse --> aggiungo else { dbCtx.DbSetConfWood.Add(upsRec); } // salvo... int numAct = await dbCtx.SaveChangesAsync(); answ = numAct > 0; } catch (Exception exc) { Log.Error($"Eccezione durante ConfWoodUpsertAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Elenco CostDrivers /// /// internal async Task> CostDriverGetAllAsync() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetCostDriver .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante CostDriverGetAllAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco contatori /// /// Anno riferimento, se null da tutti /// internal async Task> CountersGetAllAsync(int? yearRef = null) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetCounters .Where(x => yearRef == null || x.RefYear == yearRef) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante CountersGetAllAsync | yearRef: {yearRef}{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Recupera un nuovo valore del contatore per tipo ed anno richiesto, se mancasse crea /// /// Anno riferimento /// Counter richiesto /// internal async Task CountersGetNext(int yearRef, string countName) { int newCount = 0; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { var outParam = new MySqlParameter("@pValue", MySqlDbType.Int32) { Direction = ParameterDirection.Output }; await dbCtx.Database.ExecuteSqlRawAsync( "CALL GetNextCounter(@pYear, @pName, @pValue);", new MySqlParameter("@pYear", yearRef), new MySqlParameter("@pName", countName), outParam ); if (outParam != null) { int.TryParse($"{outParam.Value}", out newCount); } #if false try { var currRec = await dbCtx .DbSetCounters .Where(x => x.RefYear == yearRef && x.CountName == countName) .FirstOrDefaultAsync(); // se manca aggiungo if (currRec == null) { newCount = 1; CounterModel newRec = new CounterModel() { RefYear = yearRef, CountName = countName, Counter = newCount }; dbCtx.DbSetCounters.Add(newRec); } //altrimenti aggiorno else { newCount = currRec.Counter + 1; currRec.Counter = newCount; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo! int numAct = await dbCtx.SaveChangesAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante CountersGetNext | yearRef: {yearRef}{Environment.NewLine}{exc}"); } #endif } return newCount; } /// /// Elenco completo Customers da DB /// /// internal List CustomersGetAll() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetCustomer .ToList(); } catch (Exception exc) { Log.Error($"Eccezione durante CustomersGetAll{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco completo Dealers da DB /// /// internal List DealersGetAll() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetDealer .ToList(); } catch (Exception exc) { Log.Error($"Eccezione durante DealersGetAll{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Esegue eliminazione /// /// /// internal async Task GenClassDeleteAsync(GenClassModel rec2del) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var dbResult = dbCtx .DbSetGenClass .Where(x => x.ClassCod == rec2del.ClassCod) .FirstOrDefault(); var numChild = dbCtx .DbSetGenVal .Count(x => x.ClassCod == rec2del.ClassCod); // se trovato e NON HA record child --> elimino if (dbResult != null && numChild == 0) { dbCtx.DbSetGenClass.Remove(dbResult); await dbCtx.SaveChangesAsync(); } } catch (Exception exc) { Log.Error($"Eccezione durante GenClassDeleteAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Elenco completo GenClass /// /// internal async Task> GenClassGetAllAsync() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetGenClass .Include(o => o.GenValNav) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante GenClassGetAllAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Upsert record GenClass /// /// /// internal async Task GenClassUpsertAsync(GenClassModel upsRec) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currRec = dbCtx .DbSetGenClass .Where(x => x.ClassCod == upsRec.ClassCod) .FirstOrDefault(); // se trovato --> aggiorno if (currRec != null) { currRec.Description = upsRec.Description; dbCtx.Entry(currRec).State = EntityState.Modified; } // se mancasse --> aggiungo else { dbCtx.DbSetGenClass.Add(upsRec); } // salvo... int numAct = await dbCtx.SaveChangesAsync(); answ = numAct > 0; } catch (Exception exc) { Log.Error($"Eccezione durante GenClassUpsertAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Esegue eliminazione /// /// /// internal async Task GenValDeleteAsync(GenValueModel rec2del) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var dbResult = dbCtx .DbSetGenVal .Where(x => x.GenValID == rec2del.GenValID) .FirstOrDefault(); // se trovato --> elimino e sposto i rimanenti... if (dbResult != null) { // modifico record successivi... var list2Move = dbCtx .DbSetGenVal .Where(x => x.ClassCod == rec2del.ClassCod && x.Index > dbResult.Index) .ToList(); foreach (var item in list2Move) { item.Index--; dbCtx.Entry(item).State = EntityState.Modified; } // elimino dbCtx.DbSetGenVal.Remove(dbResult); // salvo tutto await dbCtx.SaveChangesAsync(); } } catch (Exception exc) { Log.Error($"Eccezione durante GenValDeleteAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Elenco valori x classe richiesta /// /// /// internal async Task> GenValGetFiltAsync(string codClass) { List dbResult = new List(); if (!string.IsNullOrEmpty(codClass)) { //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetGenVal .Where(x => x.ClassCod == codClass) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante GenValGetFiltAsync{Environment.NewLine}{exc}"); } } } return dbResult; } /// /// Esegue spostamento nell'ordinamento relativo alla classe /// NB: verifica spostamento sia ammissibile: se primo rec non "sale", se ultimo non "scende"... /// /// /// /// internal async Task GenValMoveAsync(GenValueModel selRec, bool moveUp) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currRec = dbCtx .DbSetGenVal .Where(x => x.GenValID == selRec.GenValID) .FirstOrDefault(); if (currRec != null) { // recupero info del num rec del suo gruppo int numRec = dbCtx .DbSetGenVal .Count(x => x.ClassCod == selRec.ClassCod); // verifico NON sia primo/ultimo... bool canMove = false; int newPos = moveUp ? currRec.Index - 1 : currRec.Index + 1; if (moveUp) { canMove = newPos > 0; } else { canMove = newPos <= numRec; } // se abilitato --> aggiorno i 2 record... if (canMove) { var otherRec = dbCtx .DbSetGenVal .Where(x => x.ClassCod == selRec.ClassCod && x.Index == newPos) .FirstOrDefault(); if (otherRec != null) { otherRec.Index = currRec.Index; dbCtx.Entry(otherRec).State = EntityState.Modified; currRec.Index = newPos; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo... int numAct = await dbCtx.SaveChangesAsync(); answ = numAct > 0; } } } catch (Exception exc) { Log.Error($"Eccezione durante GenValMoveAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Esegue Upsert del record ricevuto /// /// /// internal async Task GenValUpsertAsync(GenValueModel upsRec) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currRec = dbCtx .DbSetGenVal .Where(x => x.GenValID == upsRec.GenValID) .FirstOrDefault(); // se trovato --> aggiorno if (currRec != null) { currRec.ValString = upsRec.ValString; dbCtx.Entry(currRec).State = EntityState.Modified; } // se mancasse --> aggiungo else { dbCtx.DbSetGenVal.Add(upsRec); } // salvo... int numAct = await dbCtx.SaveChangesAsync(); answ = numAct > 0; } catch (Exception exc) { Log.Error($"Eccezione durante GenValUpsertAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Eliminazione record item /// /// /// internal async Task ItemDeleteAsync(ItemModel rec2del) { bool result = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var dbResult = await dbCtx .DbSetItem .Where(x => x.ItemID == rec2del.ItemID) .FirstOrDefaultAsync(); if (dbResult != null) { dbCtx.DbSetItem.Remove(dbResult); await dbCtx.SaveChangesAsync(); } } catch (Exception exc) { Log.Error($"Eccezione durante ItemDeleteAsync{Environment.NewLine}{exc}"); } } return result; } /// /// Elenco completo Items /// /// internal List ItemGetAll() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetItem .ToList(); } catch (Exception exc) { Log.Error($"Eccezione durante ItemGetAll{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco item alternativi da ID di un record (per sostituzione) /// /// ID item corrente (valido quindi >0) /// internal List ItemGetAlt(int ItemId) { List dbResult = new List(); if (ItemId > 0) { //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // cerco singolo record x partire... var currRec = dbCtx .DbSetItem .Where(x => x.ItemID == ItemId) .FirstOrDefault(); if ((currRec != null)) { // se è un record che ha parentId > 0 --> cerco da quello record analoghi + parent if (currRec.ItemIDParent > 0) { dbResult = dbCtx .DbSetItem .Where(x => x.ItemID == currRec.ItemIDParent || x.ItemIDParent == currRec.ItemIDParent) .ToList(); } // altrimenti cerco child collegati else { dbResult = dbCtx .DbSetItem .Where(x => x.ItemID == currRec.ItemID || x.ItemIDParent == currRec.ItemID) .ToList(); } } } catch (Exception exc) { Log.Error($"Eccezione durante ItemGetAlt{Environment.NewLine}{exc}"); } } } return dbResult; } /// /// Elenco item da ricerca filtro x gruppo/tipo /// /// /// /// internal List ItemGetFilt(string CodGroup, ItemClassType ItemType) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetItem .Where(x => (string.IsNullOrEmpty(CodGroup) || x.CodGroup == CodGroup) && (ItemType == ItemClassType.ND || x.ItemType == ItemType)) .ToList(); } catch (Exception exc) { Log.Error($"Eccezione durante ItemGetFilt{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco item da ricerca completa /// /// /// /// /// internal List ItemGetFilt(string CodGroup, ItemClassType ItemType, string SearchVal) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetItem .Where(x => (string.IsNullOrEmpty(CodGroup) || x.CodGroup == CodGroup) && (ItemType == ItemClassType.ND || x.ItemType == ItemType) && (string.IsNullOrEmpty(SearchVal) || x.Description.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase) || x.ExtItemCode.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase) || x.SupplCode.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase)) ) .ToList(); } catch (Exception exc) { Log.Error($"Eccezione durante ItemGetFilt{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco item da ricerca filtro x gruppo/tipo Async /// /// /// /// /// internal async Task> ItemGetFiltAsync(string CodGroup, ItemClassType ItemType) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetItem .Where(x => (string.IsNullOrEmpty(CodGroup) || x.CodGroup == CodGroup) && (ItemType == ItemClassType.ND || x.ItemType == ItemType)) .Include(g => g.ItemGroupNav) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ItemGetFiltAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco item da ricerca completa Async /// /// /// /// /// internal async Task> ItemGetFiltAsync(string CodGroup, ItemClassType ItemType, string SearchVal) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetItem .Where(x => (string.IsNullOrEmpty(CodGroup) || x.CodGroup == CodGroup) && (ItemType == ItemClassType.ND || x.ItemType == ItemType) && (string.IsNullOrEmpty(SearchVal) || x.Description.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase) || x.ExtItemCode.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase) || x.SupplCode.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase)) ) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ItemGetFiltAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco item da ricerca /// /// /// internal List ItemGetSearch(string SearchVal) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetItem .Where(x => x.Description.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase) || x.ExtItemCode.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase) || x.SupplCode.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase)) .ToList(); } catch (Exception exc) { Log.Error($"Eccezione durante ItemGetSearch{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco item da ricerca async /// /// /// internal async Task> ItemGetSearchAsync(string SearchVal) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetItem .Where(x => x.Description.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase) || x.ExtItemCode.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase) || x.SupplCode.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase)) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ItemGetSearchAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco completo ItemGroup gestiti /// /// internal List ItemGroupGetAll() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetItemGroup .ToList(); } catch (Exception exc) { Log.Error($"Eccezione durante ItemGroupGetAllAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco completo ItemGroup gestiti async /// /// internal async Task> ItemGroupGetAllAsync() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetItemGroup .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ItemGroupGetAllAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Esegue un mass update dei valori di margine, qtyMax, costo di un set di dati ricevuto /// /// Elenco items da aggiornare /// Costo standard (al volume m3) /// Margine da impostare per tutti /// Valore qty max da impostare per tutti /// Valore UM da impostare per tutti /// Valore di arrotondamento richiesto (0 = non arrotondo) /// Valore di scala da unità in ingresso x unità di costo (mm x mm x m --> m3) /// /// internal async Task ItemMassUpdate(List list2upd, double setCost, double defMargin, double defQtyMax, string defUM, int roundVal, double scaleFactor) { bool answ = false; if (list2upd == null || !list2upd.Any()) return answ; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // Validate input if (setCost <= 0) throw new ArgumentException("setCost must be greater than 0."); // Step 1: Extract width and height from ExtItemCode var itemUpdates = new List(); foreach (var item in list2upd) { if (string.IsNullOrWhiteSpace(item.ItemCode)) continue; // Try to parse ExtItemCode: "Pine-200.0x360.0" if (!item.ItemCode.Contains("-") || !item.ItemCode.Contains("x")) { // Skip invalid format continue; } // Split by "-" to get prefix and number part var parts = item.ItemCode.Split('-', 2); if (parts.Length < 2) { continue; } string numberPart = parts[1]; // e.g. "200.0x360.0" // Split by "x" to get width and height var widthHeight = numberPart.Split('x', 2); if (widthHeight.Length < 2) { continue; } if (!double.TryParse(widthHeight[0], NumberStyles.Any, CultureInfo.InvariantCulture, out double width) || !double.TryParse(widthHeight[1], NumberStyles.Any, CultureInfo.InvariantCulture, out double height)) { continue; } // Step 2: Calculate Cost using formula: // Cost = setCost / 1,000,000 * width * height double calculatedCost = (setCost / scaleFactor) * width * height; // if requested do round ceiling if (roundVal > 0) { calculatedCost = Math.Ceiling(calculatedCost / roundVal) * roundVal; } // Optional: you can also apply margin to cost if needed, but you said "Cost = ..." // So we're just computing it based on width/height // Step 4: Create a new ItemModel with updated values var updatedItem = new ItemModel { ItemID = item.ItemID, ExtItemCode = item.ItemCode, // keep original Cost = calculatedCost, Margin = defMargin, QtyMax = defQtyMax, UM = defUM }; itemUpdates.Add(updatedItem); } // Step 5: Update database using EF Core (EF Core doesn't support "update" on entire list directly) // We'll use .UpdateRange() or a raw update via .Where() and .SetProperty() // ✅ Use EF Core's Update method (for bulk update) if (itemUpdates.Any()) { // Update only the ones that exist in the DB var existingItems = await dbCtx.DbSetItem .Where(i => itemUpdates.Select(ui => ui.ItemID).Contains(i.ItemID)) .ToListAsync(); if (existingItems.Any()) { // Update existing records using EF Core's Update method foreach (var updatedItem in itemUpdates) { var existing = existingItems.FirstOrDefault(i => i.ItemID == updatedItem.ItemID); if (existing != null) { // Update only the fields we're setting existing.Cost = updatedItem.Cost; existing.Margin = updatedItem.Margin; existing.QtyMax = updatedItem.QtyMax; } } await dbCtx.SaveChangesAsync(); } } } catch (Exception exc) { Log.Error($"Eccezione durante ItemMassUpdate{Environment.NewLine}{exc}"); } } return answ; } internal bool ItemUpsert(ItemModel newRec) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currRec = dbCtx .DbSetItem .Where(x => x.ItemID == newRec.ItemID) .FirstOrDefault(); // se trovato --> aggiorno if (currRec != null) { currRec.Description = newRec.Description; currRec.SupplCode = newRec.SupplCode; currRec.ItemCode = newRec.ItemCode; currRec.Cost = newRec.Cost; currRec.Description = newRec.Description; currRec.IsService = newRec.IsService; currRec.Margin = newRec.Margin; dbCtx.Entry(currRec).State = EntityState.Modified; } // se mancasse --> aggiungo else { dbCtx.DbSetItem.Add(newRec); } // salvo... dbCtx.SaveChanges(); } catch (Exception exc) { Log.Error($"Eccezione durante ItemGetSearch{Environment.NewLine}{exc}"); } } return answ; } /// /// Inserisce o aggiorna il record /// /// /// internal async Task ItemUpsertAsync(ItemModel currRec) { bool result = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var dbResult = await dbCtx .DbSetItem .Where(x => x.ItemID == currRec.ItemID) .FirstOrDefaultAsync(); if (dbResult != null) { //dbCtx.DbSetItem.Remove(newCount); dbResult.CodGroup = currRec.CodGroup; dbResult.ItemType = currRec.ItemType; dbResult.IsService = currRec.IsService; dbResult.ItemCode = currRec.ItemCode; dbResult.ExtItemCode = currRec.ExtItemCode; dbResult.Cost = currRec.Cost; dbResult.Margin = currRec.Margin; dbResult.Description = currRec.Description; dbResult.QtyMin = currRec.QtyMin; dbResult.QtyMax = currRec.QtyMax; dbResult.UM = currRec.UM; dbCtx.Entry(dbResult).State = EntityState.Modified; } else { dbCtx.DbSetItem.Add(currRec); } await dbCtx.SaveChangesAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ItemUpsertAsync{Environment.NewLine}{exc}"); } } return result; } /// /// Upsert item ricevuti da BOM calcolata /// /// /// internal bool ItemUpsertFromBom(List bomList) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // Controllo ed inserisco eventuali gruppi mancanti UpdateCodGroup(bomList); // prendo solo elementi a prezzo 0 da salvare sul DB var item2save = bomList .Where(x => x.Price == 0) .ToList(); List listInserted = new List(); // ciclo x ogni elemento della BOM, cercando x gruppo e ExtItemCode foreach (var item in item2save) { var currRec = dbCtx .DbSetItem .Where(x => x.CodGroup == item.ClassCode && x.ExtItemCode == item.ItemCode) .FirstOrDefault(); // se nullo --> verifico x inserire!!! if (currRec == null) { // verifico NON sia tra gli list2upd già in fase di inserimento if (!listInserted.Any(x => x.CodGroup == item.ClassCode && x.ExtItemCode == item.ItemCode)) { ItemModel newRec = new ItemModel() { CodGroup = item.ClassCode, ItemType = Core.Enums.ItemClassType.Bom, IsService = false, // da calcolare meglio x gruppo ItemCode = 0, ExtItemCode = item.ItemCode, SupplCode = "BOM ITEM", Description = $"BOM | {item.ClassCode} | {item.ItemCode}", Cost = 0, Margin = 0, QtyMin = 0, QtyMax = 0, UM = "#" }; dbCtx.DbSetItem.Add(newRec); listInserted.Add(newRec); } } } // salvo... dbCtx.SaveChanges(); } catch (Exception exc) { Log.Error($"Eccezione durante ItemUpsertFromBom{Environment.NewLine}{exc}"); } } return answ; } /// /// Eliminazione record richiesto /// /// /// internal async Task JobStepDeleteAsync(JobStepModel rec2del) { bool result = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var dbResult = await dbCtx .DbSetJobStep .Where(x => x.JobStepID == rec2del.JobStepID) .FirstOrDefaultAsync(); if (dbResult != null) { // cerco righe successive var list2move = dbCtx .DbSetJobStep .Where(x => x.JobID == rec2del.JobID && x.Index > dbResult.Index) .ToList(); // se ci sono aggiorno! if (list2move != null && list2move.Count > 0) { foreach (var item in list2move) { item.Index--; dbCtx.Entry(item).State = EntityState.Modified; } } // elimino record dbCtx.DbSetJobStep.Remove(dbResult); await dbCtx.SaveChangesAsync(); } } catch (Exception exc) { Log.Error($"Eccezione durante JobStepDeleteAsync{Environment.NewLine}{exc}"); } } return result; } /// /// Elenco JobStep dato JobID parent /// /// /// internal async Task> JobStepGetAsync(int jobID) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetJobStep .Where(x => x.JobID == jobID) .Include(c => c.JobNav) .Include(c => c.PhaseNav) .Include(c => c.ResourceNav) .Include(c => c.TagNav) .Include(c => c.ResourceNav.DriverNav) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante JobStepGetAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Esegue spostamento nell'ordinamento /// NB: verifica spostamento sia ammissibile: se primo rec non "sale", se ultimo non "scende"... /// /// /// /// internal async Task JobStepMoveAsync(JobStepModel selRec, bool moveUp) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currRec = dbCtx .DbSetJobStep .Where(x => x.JobStepID == selRec.JobStepID) .FirstOrDefault(); if (currRec != null) { // recupero info del num rec del suo gruppo int numRec = dbCtx .DbSetJobStep .Where(x => x.JobID == selRec.JobID) .Count(); // verifico NON sia primo/ultimo... bool canMove = false; int newPos = moveUp ? currRec.Index - 1 : currRec.Index + 1; if (moveUp) { canMove = newPos > 0; } else { canMove = newPos <= numRec; } // se abilitato --> aggiorno i 2 record... if (canMove) { var otherRec = dbCtx .DbSetJobStep .Where(x => x.Index == newPos) .FirstOrDefault(); if (otherRec != null) { otherRec.Index = currRec.Index; dbCtx.Entry(otherRec).State = EntityState.Modified; currRec.Index = newPos; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo... int numAct = await dbCtx.SaveChangesAsync(); answ = numAct > 0; } } } catch (Exception exc) { Log.Error($"Eccezione durante JobStepMoveAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Upsert record /// /// /// internal async Task JobStepUpsertAsync(JobStepModel upsRec) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currRec = dbCtx .DbSetJobStep .Where(x => x.JobStepID == upsRec.JobStepID && upsRec.JobStepID > 0) .FirstOrDefault(); // se trovato --> aggiorno if (currRec != null) { dbCtx.Entry(currRec).CurrentValues.SetValues(upsRec); } // se mancasse --> aggiungo else { dbCtx.DbSetJobStep.Add(upsRec); } // salvo... int numAct = await dbCtx.SaveChangesAsync(); answ = numAct > 0; } catch (Exception exc) { Log.Error($"Eccezione durante JobStepUpsertAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Upsert dell'elenco dei tags associati /// /// ID Job richiesto /// Elenco Tags richiesto /// internal async Task JobTask2TagsUpsertAsync(int JobID, List reqTagList) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currRec = dbCtx .DbSetJobTask .Where(x => x.JobID == JobID) .Include(t => t.TagNav) .FirstOrDefault(); // se trovato --> aggiorno if (currRec != null) { var currentTags = currRec.TagNav.Select(t => t.CodTag).ToList(); // calcolo modifiche var toAdd = reqTagList.Except(currentTags).ToList(); var toRemove = currentTags.Except(reqTagList).ToList(); // aggiunte foreach (var tag in toAdd) { currRec.TagNav.Add(new JobTaskTagModel { JobID = JobID, CodTag = tag }); } // rimozioni foreach (var tag in toRemove) { var entity = currRec.TagNav.FirstOrDefault(t => t.CodTag == tag); if (entity != null) dbCtx.Remove(entity); } dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo... int numAct = await dbCtx.SaveChangesAsync(); answ = numAct > 0; } catch (Exception exc) { Log.Error($"Eccezione durante JobTask2TagsUpsertAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Eliminazione record richiesto /// /// /// internal async Task JobTaskDeleteAsync(JobTaskModel rec2del) { bool result = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var dbResult = await dbCtx .DbSetJobTask .Where(x => x.JobID == rec2del.JobID) .FirstOrDefaultAsync(); if (dbResult != null) { // cerco righe successive var list2move = dbCtx .DbSetJobTask .Where(x => x.Index > dbResult.Index) .ToList(); // se ci sono aggiorno! if (list2move != null && list2move.Count > 0) { foreach (var item in list2move) { item.Index--; dbCtx.Entry(item).State = EntityState.Modified; } } dbCtx.DbSetJobTask.Remove(dbResult); await dbCtx.SaveChangesAsync(); } } catch (Exception exc) { Log.Error($"Eccezione durante JobTaskDeleteAsync{Environment.NewLine}{exc}"); } } return result; } /// /// Recupera elenco Jobs (conf) da DB /// /// internal async Task> JobTaskGetAllAsync() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetJobTask .Include(c => c.TagNav) .Include(c => c.JobStepNav) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante JobTaskGetAllAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Esegue spostamento nell'ordinamento /// NB: verifica spostamento sia ammissibile: se primo rec non "sale", se ultimo non "scende"... /// /// /// /// internal async Task JobTaskMoveAsync(JobTaskModel selRec, bool moveUp) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currRec = dbCtx .DbSetJobTask .Where(x => x.JobID == selRec.JobID) .FirstOrDefault(); if (currRec != null) { // recupero info del num rec del suo gruppo int numRec = dbCtx .DbSetJobTask .Count(); // verifico NON sia primo/ultimo... bool canMove = false; int newPos = moveUp ? currRec.Index - 1 : currRec.Index + 1; if (moveUp) { canMove = newPos > 0; } else { canMove = newPos <= numRec; } // se abilitato --> aggiorno i 2 record... if (canMove) { var otherRec = dbCtx .DbSetJobTask .Where(x => x.Index == newPos) .FirstOrDefault(); if (otherRec != null) { otherRec.Index = currRec.Index; dbCtx.Entry(otherRec).State = EntityState.Modified; currRec.Index = newPos; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo... int numAct = await dbCtx.SaveChangesAsync(); answ = numAct > 0; } } } catch (Exception exc) { Log.Error($"Eccezione durante JobTaskMoveAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Upsert record /// /// /// internal async Task JobTaskUpsertAsync(JobTaskModel upsRec) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currRec = dbCtx .DbSetJobTask .Where(x => x.JobID == upsRec.JobID) .FirstOrDefault(); // se trovato --> aggiorno if (currRec != null) { dbCtx.Entry(currRec).CurrentValues.SetValues(upsRec); } // se mancasse --> aggiungo else { dbCtx.DbSetJobTask.Add(upsRec); } // salvo... int numAct = await dbCtx.SaveChangesAsync(); answ = numAct > 0; } catch (Exception exc) { Log.Error($"Eccezione durante JobTaskUpsertAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Esegue il cloning completo di un offerta e di TUTTE le relative righe di offerta... /// /// /// internal async Task OfferClone(OfferModel rec2clone) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { DateTime adesso = DateTime.Now; // recupero offerta... var currRec = dbCtx .DbSetOffer .Where(x => x.OfferID == rec2clone.OfferID) .Include(x => x.OfferRowNav) .FirstOrDefault(); // ultimo record OFFERTA x calcolo refNum var lastRec = dbCtx .DbSetOffer .Where(x => x.RefYear == adesso.Year) .OrderByDescending(x => x.RefNum) .FirstOrDefault(); int newRefNum = lastRec != null ? lastRec.RefNum + 1 : 1; // se trovo --> duplico! if (currRec != null) { // recupero ultimo num offerta dell'anno corrente... OfferModel newRec = new OfferModel() { ConsNote = rec2clone.ConsNote, CustomerID = rec2clone.CustomerID, DealerID = rec2clone.DealerID, Description = rec2clone.Description, DictPresel = rec2clone.DictPresel, Discount = rec2clone.Discount, DueDateProm = rec2clone.DueDateProm, DueDateReq = rec2clone.DueDateReq, Envir = rec2clone.Envir, Inserted = adesso, Modified = adesso, OffertState = OfferStates.Open, RefNum = newRefNum, RefRev = 1, RefYear = adesso.Year, ValidUntil = currRec.ValidUntil }; // sistemo child... newRec.OfferRowNav = currRec.OfferRowNav .Select(c => new OfferRowModel() { AwaitBom = c.AwaitBom, AwaitPrice = c.AwaitPrice, BomCost = c.BomCost, BomOk = c.BomOk, BomPrice = c.BomPrice, Envir = c.Envir, FileName = c.FileName, FileResource = c.FileResource, FileSize = c.FileSize, Inserted = adesso, ItemBOM = c.ItemBOM, ItemJCD = c.ItemJCD, ItemOk = c.ItemOk, ItemSteps = c.ItemSteps, ItemTags = c.ItemTags, JobID = c.JobID, Modified = c.Modified, Note = c.Note, ProdItemQty = c.ProdItemQty, Qty = c.Qty, RowNum = c.RowNum, SellingItemID = c.SellingItemID, SerStruct = c.SerStruct, StepCost = c.StepCost, StepFlowTime = c.StepFlowTime, StepLeadTime = c.StepLeadTime, StepPrice = c.StepPrice, //OrderID = dbRec.OrderID, OfferRowUID = c.OfferRowDtx }) .ToList(); // infine aggiungo riga ordine e relativi child dbCtx.DbSetOffer.Add(newRec); } // salvo TUTTI i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OfferClone{Environment.NewLine}{exc}"); } } return answ; } /// /// Elenco completo offerte da DB /// /// internal async Task> OfferGetAll() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetOffer .Include(c => c.CustomerNav) .Include(d => d.DealerNav) .Include(o => o.OfferRowNav) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante OfferGetAll{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco offerte da DB filtrate x periodo e stato /// /// /// /// internal async Task> OfferGetFilt(DateTime inizio, DateTime fine) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetOffer .Where(x => x.Inserted >= inizio && x.Inserted <= fine) .Include(c => c.CustomerNav) .Include(d => d.DealerNav) .Include(o => o.OfferRowNav) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante OfferGetFilt{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco righe offerta specificata /// /// /// internal List OfferRowGetByOffer(int OfferID) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetOfferRow .Where(x => x.OfferID == OfferID) .Include(s => s.SellingItemNav) //.Include(d => d.DealerNav) .ToList(); } catch (Exception exc) { Log.Error($"Eccezione durante OfferRowGetByOffer{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Recupera da DB riga offerta dato UID /// /// /// internal OfferRowModel? OfferRowGetByUID(string OfferRowUID) { OfferRowModel? dbResult = null; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetOfferRow .Include(s => s.SellingItemNav) .FirstOrDefault(x => x.OfferRowUID == OfferRowUID); } catch (Exception exc) { Log.Error($"Eccezione durante OfferRowGetByUID{Environment.NewLine}{exc}"); } } return dbResult; } internal async Task OffersCheckExpired() { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { DateTime adesso = DateTime.Now; // recupero offerta... var listExpired = dbCtx .DbSetOffer .Where(x => x.ValidUntil < adesso && x.OffertState == OfferStates.Open) .ToList(); // se trovo le aggiorno come stato if (listExpired != null) { foreach (var item in listExpired) { item.OffertState = OfferStates.Expired; dbCtx.Entry(item).State = EntityState.Modified; } // salvo TUTTI i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } } catch (Exception exc) { Log.Error($"Eccezione durante OffersCheckExpired{Environment.NewLine}{exc}"); } } return answ; } /// Elimina riga e sposta eventuali righe successive... /// /// /// internal async Task OffertRowDelete(OfferRowModel rec2Del) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero offerta... var currRec = dbCtx .DbSetOfferRow .Where(x => x.OfferRowID == rec2Del.OfferRowID) .FirstOrDefault(); // se non trovo aggiungo if (currRec != null) { // recupero indice attuale... int currRowNum = rec2Del.RowNum; // cerco righe successive var list2move = dbCtx .DbSetOfferRow .Where(x => x.OfferID == rec2Del.OfferID && x.RowNum > currRowNum) .ToList(); // se ci sono aggiorno! if (list2move != null && list2move.Count > 0) { foreach (var item in list2move) { item.RowNum--; dbCtx.Entry(item).State = EntityState.Modified; } } // infine rimuovo riga dbCtx.DbSetOfferRow.Remove(currRec); } // salvo TUTTI i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OffertRowDelete{Environment.NewLine}{exc}"); } } return answ; } /// /// Aggiornamento valore UID non calcolato + ritorno elenco UID da aggiornare /// /// /// internal List OffertRowFixUid(int offertID) { List answ = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currList = dbCtx .DbSetOfferRow .Where(x => x.OfferID == offertID) .ToList(); // se trovato --> verifico valori differenti, aggiorno e restituisco da calcolare if (currList != null) { var list2fix = currList.Where(x => string.IsNullOrEmpty(x.OfferRowUID) || x.OfferRowUID != x.OfferRowDtx).ToList(); if (list2fix != null && list2fix.Count > 0) { // salvo elenco answ = list2fix.Select(x => x.OfferRowDtx).ToList(); // sistemo UID foreach (var item in list2fix) { item.OfferRowUID = item.OfferRowDtx; dbCtx.Entry(item).State = EntityState.Modified; } // salvo... var result = dbCtx.SaveChanges(); } } } catch (Exception exc) { Log.Error($"Eccezione durante OffertRowFixUid{Environment.NewLine}{exc}"); } } return answ; } /// /// Effettua update stato await BOM/PRICE per l'offerta indicata /// /// ID singola riga offerta /// Se non nullo è il nuovo stato await BOM /// Se non nullo è stato await Price /// internal async Task OffertRowUpdateAwaitStateAsync(int offerRowID, bool? awaitBom, bool? awaitPrice) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero righe offerta... var currRec = dbCtx .DbSetOfferRow .Where(x => x.OfferRowID == offerRowID) .FirstOrDefault(); // aggiorno parametri (se inviati) if (currRec != null) { currRec.AwaitBom = awaitBom ?? currRec.AwaitBom; currRec.AwaitPrice = awaitPrice ?? currRec.AwaitPrice; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OffertRowUpdateAwaitStateAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Effettua update dei costi di tutte le righe dell'offerta indicata /// /// ID riga offerta da aggiornare /// Bom aggiornata da salvare /// internal async Task OffertRowUpdateBom(int OfferRowID, List newBomList) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero riga offerta da aggiornare... var currRec = dbCtx .DbSetOfferRow .Where(x => x.OfferRowID == OfferRowID) .FirstOrDefault(); // se è valida --> procedo! if (currRec != null) { // recupero l'elenco degli itemGroup gestiti var itemGroupList = dbCtx .DbSetItemGroup .ToList(); // recupero il subset item da BOM / BomAlt... var bomGenList = dbCtx .DbSetItem .Where(x => (x.ItemType == Core.Enums.ItemClassType.Bom || x.ItemType == Core.Enums.ItemClassType.BomAlt)) .ToList(); // calcolo il NUOVO costo e lo aggiorno... double totCost = 0; double totPrice = 0; int totItemQty = 0; int numGroupOk = 0; int numItemOk = 0; int numElems = newBomList.Count; // validazione e completamento BOM validateBom(itemGroupList, bomGenList, ref newBomList, null, ref totCost, ref totPrice, ref totItemQty, ref numGroupOk, ref numItemOk); // salvo BOM... string itemBom = JsonConvert.SerializeObject(newBomList); currRec.ItemBOM = itemBom; // salvo arrotondato alla 3° decimale currRec.BomCost = Math.Round(totCost, 3); currRec.BomPrice = Math.Round(totPrice, 3); currRec.BomOk = numElems == numGroupOk; currRec.ItemOk = numElems == numItemOk; currRec.ProdItemQty = totItemQty; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo modifiche... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OffertRowUpdateBom{Environment.NewLine}{exc}"); } } return answ; } /// /// Aggiorno sul DB i dati del file associato /// /// /// internal async Task OffertRowUpdateFileData(OfferRowModel updRec) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero righe offerta... var currRec = dbCtx .DbSetOfferRow .Where(x => x.OfferRowID == updRec.OfferRowID) .FirstOrDefault(); // aggiorno parametri (se inviati) if (currRec != null) { currRec.FileName = updRec.FileName; currRec.FileResource = updRec.FileResource; currRec.FileSize = updRec.FileSize; currRec.SerStruct = updRec.SerStruct; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OffertRowUpdateFileData{Environment.NewLine}{exc}"); } } return answ; } /// /// Effettua update serStruct per l'offerta indicata /// /// ID singola riga offerta /// Valore serializzatoe /// internal async Task OffertRowUpdateSerStruct(int offerRowID, string serStruct) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero righe offerta... var currRec = dbCtx .DbSetOfferRow .Where(x => x.OfferRowID == offerRowID) .FirstOrDefault(); // aggiorno parametri (se inviati) if (currRec != null) { currRec.SerStruct = serStruct; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OffertRowUpdateSerStruct{Environment.NewLine}{exc}"); } } return answ; } /// /// Upsert record riga offerta /// /// /// internal async Task OffertRowUpsert(OfferRowModel updRec) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero offerta... var currRec = dbCtx .DbSetOfferRow .Where(x => x.OfferRowID == updRec.OfferRowID) .FirstOrDefault(); // se non trovo aggiungo if (currRec == null) { dbCtx.DbSetOfferRow.Add(updRec); // se ci sono record successivi li devo spostare... var list2move = dbCtx .DbSetOfferRow .Where(x => x.OfferID == updRec.OfferID && x.RowNum >= updRec.RowNum) .ToList(); if (list2move != null && list2move.Count > 0) { foreach (var item2move in list2move) { item2move.RowNum++; dbCtx.Entry(item2move).State = EntityState.Modified; } } } // altrimenti aggiorno else { dbCtx.Entry(currRec).CurrentValues.SetValues(updRec); } // salvo TUTTI i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OffertRowUpsert{Environment.NewLine}{exc}"); } } return answ; } /// /// Effettua update dei costi di tutte le righe dell'offerta indicata /// /// /// internal async Task OffertUpdateCost(int OfferID) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero righe offerta... var offRowList = dbCtx .DbSetOfferRow .Where(x => x.OfferID == OfferID) .ToList(); // recupero l'elenco degli itemGroup gestiti var itemGroupList = dbCtx .DbSetItemGroup .ToList(); // recupero il subset item da BOM / BomAlt... var bomGenList = dbCtx .DbSetItem .Where(x => (x.ItemType == Core.Enums.ItemClassType.Bom || x.ItemType == Core.Enums.ItemClassType.BomAlt)) .ToList(); // ciclo! foreach (var currRec in offRowList) { // se contiene qualcosa x BOM... if (!string.IsNullOrEmpty(currRec.ItemBOM) && currRec.ItemBOM.Length > 2) { // deserializzo var bomList = JsonConvert.DeserializeObject>(currRec.ItemBOM); // se ho trovato elementi... if (bomList != null) { // calcolo il NUOVO costo e lo aggiorno... double totCost = 0; double totPrice = 0; int totItemQty = 0; int numGroupOk = 0; int numItemOk = 0; int numElems = bomList.Count; // validazione e completamento BOM validateBom(itemGroupList, bomGenList, ref bomList, null, ref totCost, ref totPrice, ref totItemQty, ref numGroupOk, ref numItemOk); // salvo BOM... string itemBom = JsonConvert.SerializeObject(bomList); currRec.ItemBOM = itemBom; // salvo arrotondato alla 3° decimale currRec.BomCost = Math.Round(totCost, 3); currRec.BomPrice = Math.Round(totPrice, 3); currRec.BomOk = numElems == numGroupOk; currRec.ItemOk = numElems == numItemOk; currRec.ProdItemQty = totItemQty; dbCtx.Entry(currRec).State = EntityState.Modified; } } } // salvo TUTTI i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OffertUpdateCost{Environment.NewLine}{exc}"); } } return answ; } /// /// Upsert record offerta /// /// /// internal async Task OffertUpsert(OfferModel updRec) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero offerta... var currRec = dbCtx .DbSetOffer .Where(x => x.OfferID == updRec.OfferID) .FirstOrDefault(); // se non trovo aggiungo if (currRec == null) { dbCtx.DbSetOffer.Add(updRec); } // altrimenti aggiorno else { // verifico eventuale riapertura SE fosse expired ma la data è valida... if (currRec.OffertState == OfferStates.Expired && updRec.ValidUntil > DateTime.Today) { updRec.OffertState = OfferStates.Open; } dbCtx.Entry(currRec).CurrentValues.SetValues(updRec); } // salvo TUTTI i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OffertUpsert{Environment.NewLine}{exc}"); } } return answ; } /// /// Esegue upsert del record offerta data la BOM ricevuta /// /// /// internal bool OfferUpsertFromBom(string uID, List bomList) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currRec = dbCtx .DbSetOfferRow .Where(x => x.OfferRowUID == uID) .FirstOrDefault(); // se trovato --> salvo BOM e calcolo costi if (currRec != null) { // recupero l'elenco degli itemGroup gestiti var itemGroupList = dbCtx .DbSetItemGroup .ToList(); // recupero il subset item da BOM... var bomGenList = dbCtx .DbSetItem //.Where(x => x.sourceType == Core.Enums.ItemClassType.Bom) .Where(x => (x.ItemType == Core.Enums.ItemClassType.Bom || x.ItemType == Core.Enums.ItemClassType.BomAlt)) .ToList(); // recupero la BOM list precedente var bomListPrev = JsonConvert.DeserializeObject>(currRec.ItemBOM); // calcolo il NUOVO costo e lo aggiorno... double totCost = 0; double totPrice = 0; int totItemQty = 0; int numGroupOk = 0; int numItemOk = 0; int numElems = bomList.Count; // validazione e completamento BOM validateBom(itemGroupList, bomGenList, ref bomList, bomListPrev, ref totCost, ref totPrice, ref totItemQty, ref numGroupOk, ref numItemOk); // salvo BOM... string itemBom = JsonConvert.SerializeObject(bomList); currRec.ItemBOM = itemBom; // salvo arrotondato alla 3° decimale currRec.BomCost = Math.Round(totCost, 3); currRec.BomPrice = Math.Round(totPrice, 3); currRec.BomOk = numElems == numGroupOk; currRec.ItemOk = numElems == numItemOk; // setto ok await di BOM e Price currRec.AwaitBom = false; currRec.AwaitPrice = false; currRec.ProdItemQty = totItemQty; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo... var result = dbCtx.SaveChanges(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OfferUpsertFromBom{Environment.NewLine}{exc}"); } } return answ; } /// /// Restituisce record ordine + righe Ordine dato ID /// /// /// internal async Task OrderById(int orderId) { OrderModel? dbRec = null; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { DateTime adesso = DateTime.Now; // recupero ordine... dbRec = dbCtx .DbSetOrder .Where(x => x.OrderID == orderId) .Include(x => x.OrderRowNav) .FirstOrDefault(); } catch (Exception exc) { Log.Error($"Eccezione durante OrderById{Environment.NewLine}{exc}"); } } return dbRec; } /// /// Genera un nuovo record ordine come cloning completo di un offerta e di TUTTE le relative righe di offerta + rif offerta /// /// /// internal async Task OrderFromOffer(OfferModel rec2clone) { OrderModel? newRec = null; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { using var transaction = await dbCtx.Database.BeginTransactionAsync(); try { DateTime adesso = DateTime.Now; // recupero offerta... var currRec = dbCtx .DbSetOffer .Where(x => x.OfferID == rec2clone.OfferID) .Include(x => x.OfferRowNav) .FirstOrDefault(); // ultimo record ORDINE x calcolo refNum var lastRec = dbCtx .DbSetOrder .Where(x => x.RefYear == adesso.Year) .OrderByDescending(x => x.RefNum) .FirstOrDefault(); int newRefNum = lastRec != null ? lastRec.RefNum + 1 : 1; // se trovo --> duplico! if (currRec != null) { // recupero ultimo num offerta dell'anno corrente... newRec = new OrderModel() { ConsNote = rec2clone.ConsNote, CustomerID = rec2clone.CustomerID, DealerID = rec2clone.DealerID, Description = rec2clone.Description, DictPresel = rec2clone.DictPresel, Discount = rec2clone.Discount, DueDateProm = rec2clone.DueDateProm, DueDateReq = rec2clone.DueDateReq, Envir = rec2clone.Envir, Inserted = adesso, Modified = adesso, OfferID = rec2clone.OfferID, OrderState = OrderStates.Created, RefNum = newRefNum, RefRev = 1, RefYear = adesso.Year, ValidUntil = currRec.ValidUntil }; // sistemo child offer... newRec.OrderRowNav = currRec.OfferRowNav .Select(c => new OrderRowModel() { AwaitBom = c.AwaitBom, AwaitPrice = c.AwaitPrice, BomCost = c.BomCost, BomOk = c.BomOk, BomPrice = c.BomPrice, Envir = c.Envir, FileName = c.FileName, FileResource = c.FileResource, FileSize = c.FileSize, Inserted = adesso, ItemBOM = c.ItemBOM, ItemJCD = c.ItemJCD, ItemOk = c.ItemOk, ItemSteps = c.ItemSteps, ItemTags = c.ItemTags, JobID = c.JobID, Modified = adesso, Note = c.Note, ProdItemQty = c.ProdItemQty, Qty = c.Qty, RowNum = c.RowNum, SellingItemID = c.SellingItemID, SerStruct = c.SerStruct, StepCost = c.StepCost, StepFlowTime = c.StepFlowTime, StepLeadTime = c.StepLeadTime, StepPrice = c.StepPrice }) .ToList(); // infine aggiungo riga ordine e relativi child dbCtx.DbSetOrder.Add(newRec); } // salvo TUTTI i cambiamenti... var numSave = await dbCtx.SaveChangesAsync(); // se ok sistemo UID... if (numSave > 0 && newRec != null) { // sistemo UID... foreach (var item in newRec.OrderRowNav) { item.OrderRowUID = item.OrderRowCode; // alternativa da valutare.. if (false) { // genero tanti record collegati alla riga d'ordine... for (int i = 0; i < item.ProdItemQtyTot; i++) { var child = new ProductionItemModel { OrderRowID = item.OrderRowID, //OrderRowNav = item, ItemCode = i + 1, ExtItemCode = $"{item.OrderRowCode}-{i + 1:000}", ProdBatchID = null }; // aggiungo record item.ProdItemNav.Add(child); } } else { item.ProdItemNav = Enumerable.Range(1, (int)item.ProdItemQtyTot) .Select(i => new ProductionItemModel { //OrderRowID = item.OrderRowID, OrderRowNav = item, ItemCode = i, ExtItemCode = $"{item.OrderRowCode}-{i:000}", ProdBatchID = null, ProdItemTag = null //nullo e POI verrà sistemato }) .ToList(); } dbCtx.Entry(item).State = EntityState.Modified; } // salvo ulteriori variazioni await dbCtx.SaveChangesAsync(); // tutti gli ordini e anno corrente... await dbCtx.Database.ExecuteSqlRawAsync("CALL stp_ProdItem_UpdateProdItemTag(0,0);"); // committo in un unica transazione (da provare!!!) await transaction.CommitAsync(); } } catch (Exception exc) { await transaction.RollbackAsync(); Log.Error($"Eccezione durante OrderFromOffer{Environment.NewLine}{exc}"); } } return newRec; } /// /// Ordini da DB filtrati x periodo /// /// /// /// internal async Task> OrderGetFilt(DateTime inizio, DateTime fine) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetOrder .Where(x => x.Inserted >= inizio && x.Inserted <= fine) .Include(c => c.CustomerNav) .Include(d => d.DealerNav) .Include(o => o.OrderRowNav) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante OrderGetFilt{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elimina riga e sposta eventuali righe successive... /// /// /// internal async Task OrderRowDelete(OrderRowModel rec2Del) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero offerta... var currRec = dbCtx .DbSetOrderRow .Where(x => x.OrderRowID == rec2Del.OrderRowID) .FirstOrDefault(); // se trovo procedo if (currRec != null) { // recupero indice attuale... int currRowNum = rec2Del.RowNum; // cerco righe successive var list2move = dbCtx .DbSetOrderRow .Where(x => x.OrderID == rec2Del.OrderID && x.RowNum > currRowNum) .ToList(); // se ci sono aggiorno! if (list2move != null && list2move.Count > 0) { foreach (var item in list2move) { item.RowNum--; dbCtx.Entry(item).State = EntityState.Modified; } } // infine rimuovo riga dbCtx.DbSetOrderRow.Remove(currRec); } // salvo TUTTI i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowDelete{Environment.NewLine}{exc}"); } } return answ; } /// /// Aggiornamento valore UID non calcolato + ritorno elenco UID da aggiornare /// /// /// internal List OrderRowFixUid(int orderID) { List answ = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currList = dbCtx .DbSetOrderRow .Where(x => x.OrderID == orderID) .ToList(); // se trovato --> verifico valori differenti, aggiorno e restituisco da calcolare if (currList != null) { var list2fix = currList.Where(x => string.IsNullOrEmpty(x.OrderRowUID) || x.OrderRowUID != x.OrderRowCode).ToList(); if (list2fix != null && list2fix.Count > 0) { // salvo elenco answ = list2fix.Select(x => x.OrderRowCode).ToList(); // sistemo UID foreach (var item in list2fix) { item.OrderRowUID = item.OrderRowCode; dbCtx.Entry(item).State = EntityState.Modified; } // salvo... var result = dbCtx.SaveChanges(); } } } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowFixUid{Environment.NewLine}{exc}"); } } return answ; } /// /// Elenco righe Ordine specificato /// /// /// internal List OrderRowGetByOffer(int OrderID) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetOrderRow .Where(x => x.OrderID == OrderID) .Include(s => s.SellingItemNav) //.Include(d => d.DealerNav) .ToList(); } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowGetByOffer{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco righe ordine dato stato richiesto /// /// /// internal List OrderRowGetByState(OrderStates reqState, DateTime dtStart, DateTime dtEnd) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetOrderRow .Where(x => x.OrderRowState == reqState && x.Inserted >= dtStart && x.Inserted <= dtEnd) .Include(s => s.SellingItemNav) //.Include(d => d.DealerNav) .ToList(); } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowGetByState{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco righe ordine dato periodo + stato richiesto MINIMO /// /// /// internal List OrderRowGetByStateMin(OrderStates reqState, DateTime dtStart, DateTime dtEnd) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetOrderRow .Where(x => x.OrderRowState >= reqState && x.Inserted >= dtStart && x.Inserted <= dtEnd) .Include(s => s.SellingItemNav) //.Include(d => d.DealerNav) .ToList(); } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowGetByStateMin{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Riga Ordine dato suo UID /// /// /// internal OrderRowModel OrderRowGetByUID(string OrderRowUID) { OrderRowModel? dbResult = null; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetOrderRow .Include(s => s.SellingItemNav) .FirstOrDefault(x => x.OrderRowUID == OrderRowUID); } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowGetByUID{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Validazione record: /// - controllo state vs estimate /// - segnala il numero dei record aggiornati /// /// Elenco record da verificare internal async Task OrderRowListValidate(List 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; } /// /// Effettua update stato await BOM/PRICE per l'Ordine indicato /// /// ID singola riga ordine /// Se non nullo è il nuovo stato await BOM /// Se non nullo è stato await Price /// internal async Task OrderRowUpdateAwaitState(int orderRowID, bool? awaitBom, bool? awaitPrice) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero righe ordine... var currRec = dbCtx .DbSetOrderRow .Where(x => x.OrderRowID == orderRowID) .FirstOrDefault(); // aggiorno parametri (se inviati) if (currRec != null) { currRec.AwaitBom = awaitBom ?? currRec.AwaitBom; currRec.AwaitPrice = awaitPrice ?? currRec.AwaitPrice; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowUpdateAwaitState{Environment.NewLine}{exc}"); } } return answ; } /// /// Aggiorno sul DB i dati del file associato /// /// /// internal async Task OrderRowUpdateFileData(OrderRowModel updRec) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero righe offerta... var currRec = dbCtx .DbSetOrderRow .Where(x => x.OrderRowID == updRec.OrderRowID) .FirstOrDefault(); // aggiorno parametri (se inviati) if (currRec != null) { currRec.FileName = updRec.FileName; currRec.FileResource = updRec.FileResource; currRec.FileSize = updRec.FileSize; currRec.SerStruct = updRec.SerStruct; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowUpdateFileData{Environment.NewLine}{exc}"); } } return answ; } /// /// Effettua update serStruct per l'Ordine indicato /// /// ID singola riga Ordine /// Valore serializzatoe /// internal async Task OrderRowUpdateSerStruct(int orderRowID, string serStruct) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero righe offerta... var currRec = dbCtx .DbSetOrderRow .Where(x => x.OrderRowID == orderRowID) .FirstOrDefault(); // aggiorno parametri (se inviati) if (currRec != null) { currRec.SerStruct = serStruct; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowUpdateSerStruct{Environment.NewLine}{exc}"); } } return answ; } /// /// Aggiorna stato riga ordine /// /// /// /// internal async Task OrderRowUpdateState(int orderRowID, OrderStates reqState) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero righe offerta... var currRec = dbCtx .DbSetOrderRow .Where(x => x.OrderRowID == orderRowID) .FirstOrDefault(); // aggiorno parametri (se inviati) if (currRec != null) { currRec.OrderRowState = reqState; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowUpdateState{Environment.NewLine}{exc}"); } } return answ; } /// /// Upsert riga ordine /// /// /// internal async Task OrderRowUpsert(OrderRowModel updRec) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero offerta... var currRec = dbCtx .DbSetOrderRow .Where(x => x.OrderRowID == updRec.OrderRowID) .FirstOrDefault(); // se non trovo aggiungo if (currRec == null) { dbCtx.DbSetOrderRow.Add(updRec); // se ci sono record successivi li devo spostare... var list2move = dbCtx .DbSetOrderRow .Where(x => x.OrderID == updRec.OrderID && x.RowNum >= updRec.RowNum) .ToList(); if (list2move != null && list2move.Count > 0) { foreach (var item2move in list2move) { item2move.RowNum++; dbCtx.Entry(item2move).State = EntityState.Modified; } } } // altrimenti aggiorno else { dbCtx.Entry(currRec).CurrentValues.SetValues(updRec); } // salvo TUTTI i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowUpsert{Environment.NewLine}{exc}"); } } return answ; } /// /// Upsert record del solo ProdEstimate /// /// /// internal async Task OrderRowUpsertProdEst(string uID, string prodEstim) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { 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 onsiderando 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... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowUpsertProdEst{Environment.NewLine}{exc}"); } } return answ; } /// /// Upsert record /// /// /// internal async Task OrderUpsert(OrderModel updRec) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero offerta... var currRec = dbCtx .DbSetOrder .Where(x => x.OrderID == updRec.OrderID) .FirstOrDefault(); // se non trovo aggiungo if (currRec == null) { dbCtx.DbSetOrder.Add(updRec); } // altrimenti aggiorno else { dbCtx.Entry(currRec).CurrentValues.SetValues(updRec); } // salvo TUTTI i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OrderUpsert{Environment.NewLine}{exc}"); } } return answ; } /// /// Elenco record Fasi da DB /// /// internal async Task> PhasesGetAllAsync() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetPhase .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante PhasesGetAllAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco record ProductionGroup dato OrderRow /// /// internal async Task> ProdGroupByOrderRow(int OrderRowID) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetProdGroup .Where(x => x.OrderRowID == OrderRowID) .Include(i => i.ItemsNav) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ProdGroupByOrderRow{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco record ProductionGroup dato Stato dell'OrderRow /// /// internal async Task> ProdGroupByOrderState(OrderStates reqState) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetProdGroup .Include(o => o.OrderRowNav) .Where(x => x.OrderRowNav.OrderRowState == reqState) .Include(i => i.ItemsNav) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ProdGroupByOrderState{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Upsert record di un singolo ProdGroup da fase Balance /// /// UID dell'item offerta di cui si è ricevuto l'oggetto Balance' /// Prod Group di riferimento /// internal async Task ProdGroupUpsertBalance(string uID, string rGroup, string rawBalance) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // Tentativo di deserializzazione var data = JsonConvert.DeserializeObject>(rawBalance); // proseguo solo se è valida la deserializzazione... if (data != null) { // Togliamo la 'G' e convertiamo in int (gestisce automaticamente "01" -> 1) int grpIdx = int.Parse(rGroup.TrimStart('G')); // recupero ord row (parent)... var ordRowRec = dbCtx .DbSetOrderRow .Where(x => x.OrderRowUID == uID) .FirstOrDefault(); if (ordRowRec != null) { // recupero record specifico var currRec = dbCtx .DbSetProdGroup .Where(x => x.OrderRowID == ordRowRec.OrderRowID && x.GrpIdx == grpIdx) .FirstOrDefault(); // se trovato aggiorno if (currRec != null) { currRec.WorkGroupListRaw = rawBalance; dbCtx.Entry(currRec).State = EntityState.Modified; } // altrimenti aggiungo else { ProductionGroupModel newRec = new ProductionGroupModel() { OrderRowID = ordRowRec.OrderRowID, GrpIdx = grpIdx, WorkGroupListRaw = rawBalance }; dbCtx .DbSetProdGroup .Add(newRec); } // segno ordine come Assigned se non lo fosse... if (ordRowRec.OrderRowState != OrderStates.Assigned) { ordRowRec.OrderRowState = OrderStates.Assigned; dbCtx.Entry(ordRowRec).State = EntityState.Modified; } // salvo TUTTI i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } } } catch (Exception exc) { Log.Error($"Eccezione durante ProdGroupUpsertBalance{Environment.NewLine}{exc}"); } } return answ; } /// /// Assegnazione in blocco degli item agli ODL corrispondenti /// /// /// /// internal async Task ProdItem2ODL_AssignAsync(List dbList, Dictionary<(int phaseId, int resId, string machine, int index), List> dictParts) { int totalCreated = 0; using (DataLayerContext dbCtx = new DataLayerContext()) { // 1. Recuperiamo tutti i ProdBatchID coinvolti per fare una sola query List batchIds = dbList.Select(o => o.ProdBatchID).Distinct().ToList(); if (batchIds != null && batchIds.Count > 0) { // 2. Carichiamo in memoria i ProdItem necessari (solo ID e Tag per risparmiare RAM) var itemsList = await dbCtx.DbSetProdItem .Where(x => batchIds.Contains(x.ProdBatchID ?? 0) && x.ProdItemTag != null && x.ProdItemTag != "") .Select(x => new { x.ProdItemID, x.ProdItemTag }) .ToListAsync(); // 1. Usiamo il "!" (null-forgiving operator) dopo x.ProdItemTag // perché il filtro .Where sopra garantisce che non sia null. var itemLookup = itemsList .GroupBy(x => x.ProdItemTag!) .ToDictionary( g => g.Key, g => g.First().ProdItemID, StringComparer.OrdinalIgnoreCase ); using var transaction = await dbCtx.Database.BeginTransactionAsync(); try { var relationsToInsert = new List(); foreach (var odl in dbList) { var key = (odl.PhaseID ?? 0, odl.ResourceID ?? 0, odl.ProdPlantCod, odl.Index); if (dictParts.TryGetValue(key, out List tagList)) { foreach (var tag in tagList) { // 3. Cerchiamo l'ID corrispondente al tag nel nostro lookup locale if (itemLookup.TryGetValue(tag, out int realItemId)) { relationsToInsert.Add(new ProductionItem2ODLModel { ProdODLID = odl.ProdODLID, ProdItemID = realItemId, DtAssign = DateTime.Now }); } else { //Log.Warning($"Tag {tag} non trovato nel database per i batch selezionati."); } } } } if (relationsToInsert.Any()) { await dbCtx.DbSetProdItem2ODL.AddRangeAsync(relationsToInsert); totalCreated = relationsToInsert.Count; await dbCtx.SaveChangesAsync(); } await transaction.CommitAsync(); } catch (Exception exc) { await transaction.RollbackAsync(); Log.Error($"Errore nel salvataggio relazioni ODL-Parts: {exc.Message}"); throw; } } } return totalCreated; } /// /// Esegue assegnazione bulk dei ProdItem ad un unico ProdBatch parent (per ora totale x RigaOrd) /// /// /// /// internal async Task ProdItemBulkAssignProdBatch(int OrderRowId, int ProdBatchId) { int totalUpdated = 0; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { using var transaction = await dbCtx.Database.BeginTransactionAsync(); try { int rowsAffected = await dbCtx.DbSetProdItem .Where(p => p.OrderRowID == OrderRowId) .ExecuteUpdateAsync(setters => setters .SetProperty(p => p.ProdBatchID, ProdBatchId) ); totalUpdated += rowsAffected; await transaction.CommitAsync(); } catch (Exception exc) { await transaction.RollbackAsync(); Log.Error($"Eccezione durante ProdItemBulkAssignProdBatch{Environment.NewLine}{exc}"); //throw; } } return totalUpdated; } /// /// Esegue assegnazione bulk dei ProdItem ad un unico ProdGroup parent /// /// /// /// /// internal async Task ProdItemBulkAssignProdGroup(int OrderRowId, int ProdGroupId, Dictionary 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 ProdItemTag = z int rowsAffected = await dbCtx.DbSetProdItem .Where(p => p.OrderRowID == OrderRowId && p.ProdItemTag == entry.Key) .ExecuteUpdateAsync(setters => setters .SetProperty(p => p.ProdGroupID, ProdGroupId) .SetProperty(p => p.EstimTime, entry.Value) ); totalUpdated += rowsAffected; } await transaction.CommitAsync(); } catch (Exception exc) { await transaction.RollbackAsync(); Log.Error($"Eccezione durante ProdItemBulkAssignProdGroup{Environment.NewLine}{exc}"); //throw; } } return totalUpdated; } /// /// Elenco ProdItem dato OrderRow /// /// /// internal async Task> ProdItemByOrderRow(int orderRowId) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetProdItem .Where(x => x.OrderRowID == orderRowId) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ProdItemByOrderRow{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Reset impostazione ProdItem x ripartire senza setting ProdGroup /// /// /// internal async Task ProdItemResetProdGroup(int orderRowID) { int numItem = 0; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { numItem = await dbCtx.DbSetProdItem .Where(p => p.OrderRowID == orderRowID) .ExecuteUpdateAsync(setters => setters .SetProperty(p => p.ProdGroupID, (int?)null) .SetProperty(p => p.EstimTime, 0) ); } catch (Exception exc) { Log.Error($"Eccezione durante ProdItemResetProdGroup{Environment.NewLine}{exc}"); } } return numItem; } /// /// Elenco PODL non assegnati con struttura DTO appiattita /// /// internal async Task?> ProdOdlAssignGetAsync() { List? dbResults = null; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResults = await dbCtx.DbSetProdODL .Where(x => !x.DateAssign.HasValue) .Select(odl => new OdlAssignDto { Envir = odl.ProdBatchNav.Envir, ProdODLID = odl.ProdODLID, Description = odl.Description, EstimTime = odl.EstimTime, Index = odl.Index, OdlTag = odl.OdlTag, PhaseID = odl.PhaseID, ProdBatchID = odl.ProdBatchID, ProdPlantCod = odl.ProdPlantCod, Qty = odl.Qty, ResourceID = odl.ResourceID, ItemList = odl.Item2OdlNav.Select(i => new ItemAssignDto { ProdItemID = i.ProdItemID, OrderID = i.ProductionItemNav.OrderRowNav.OrderNav.OrderID, OrderRowID = i.ProductionItemNav.OrderRowNav.OrderRowID, OrderTag = i.ProductionItemNav.OrderRowNav.OrderNav.OrderCode, OrderRowTag = i.ProductionItemNav.OrderRowNav.OrderRowCode, ProdItemTag = i.ProductionItemNav.ProdItemTag ?? "***" }).ToList() }) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ProductionOdlUnassignAsync{Environment.NewLine}{exc}"); } } return dbResults; } /// /// Recupero record ProdOdl dato Tag/uID /// /// /// internal async Task ProdOdlGetByUidAsync(string uID) { ProductionODLModel? answ = null; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { answ = await dbCtx .DbSetProdODL .Where(x => x.OdlTag == uID) .FirstOrDefaultAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ProdOdlGetByUidAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// 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; } internal async Task> ProdPlantGetAllAsync() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetProdPlant .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ProdPlantGetAllAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Creazione di un Batch con relativo Tag e info x creazione ODL correlati /// /// /// internal async Task ProductionBatchCreateAsync(ProductionBatchModel newRec) { ProductionBatchModel dbResult = null; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // Definiamo i parametri di input var pDesc = new MySqlParameter("@pDescription", newRec.Description ?? (object)DBNull.Value); var pDate = new MySqlParameter("@pDueDate", newRec.DueDate); var pPref = new MySqlParameter("@pPrefix", "BC."); var pYear = new MySqlParameter("@pYear", newRec.DueDate.Year); var pEnv = new MySqlParameter("@pEnv", newRec.Envir); // Eseguiamo la procedura e mappiamo il risultato direttamente sul modello // Nota: DbSetProdBatch deve essere configurato nel DbContext var result = await dbCtx .DbSetProdBatch .FromSqlRaw("CALL stp_ProdBatch_insert(@pDescription, @pDueDate, @pPrefix, @pYear, @pEnv)", pDesc, pDate, pPref, pYear, pEnv) .ToListAsync(); dbResult = result.FirstOrDefault() ?? new ProductionBatchModel(); } catch (Exception ex) { throw new Exception("Errore durante ProductionBatchCreateAsync", ex); } return dbResult; } } /// /// Insert sul DB di un elenco ODL con calcolo della relativa KEY a cui poter, successivamente, collegare i record child (items) /// /// /// /// internal async Task> ProductionOdlCreateAsync(List listOdl2ins) { //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { // avvio transazione using var transaction = await dbCtx.Database.BeginTransactionAsync(); try { int cYear = DateTime.Today.Year; // insert in blocco dbCtx.DbSetProdODL.AddRange(listOdl2ins); // 3. Salvataggio massivo // EF Core 8 ottimizzerà gli insert in batch dove possibile await dbCtx.SaveChangesAsync(); // stored update Tags var pProdBatchID = new MySqlParameter("@pDueDate", listOdl2ins.FirstOrDefault()?.ProdBatchID ?? 0); var pPref = new MySqlParameter("@pPrefix", "ODL."); var pYear = new MySqlParameter("@pYear", cYear); await dbCtx.Database.ExecuteSqlRawAsync("CALL stp_ProdOdl_UpdateTag(@pProdBatchID, @pPrefix, @pYear)", pProdBatchID, pPref, pYear); // 4. Conferma transazione await transaction.CommitAsync(); // A questo punto, ogni oggetto in 'listOdl2ins' ha il ProdODLID aggiornato dal DB return listOdl2ins; } catch (Exception ex) { await transaction.RollbackAsync(); // Logga l'errore secondo le tue necessità throw new Exception("Errore durante la creazione massiva degli ODL", ex); } } } /// /// Elenco PODL non assegnati /// /// internal async Task?> ProductionOdlUnassignAsync() { List? dbRestults = null; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbRestults = await dbCtx .DbSetProdODL .Where(x => !x.DateAssign.HasValue) .Include(x => x.Item2OdlNav) //.ThenInclude(i => i.ProductionItemNav) //.ThenInclude(o => o.OrderRowNav) //.ThenInclude(o => o.OrderNav) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ProductionOdlUnassignAsync{Environment.NewLine}{exc}"); } } return dbRestults; } /// /// Eliminazione record /// /// /// internal async Task ResourcesDeleteAsync(ResourceModel rec2del) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currRec = dbCtx .DbSetResource .Where(x => rec2del.ResourceID > 0 && x.ResourceID == rec2del.ResourceID) .FirstOrDefault(); // se trovato --> elimino if (currRec != null) { dbCtx.DbSetResource.Remove(rec2del); } // salvo... int numAct = await dbCtx.SaveChangesAsync(); answ = numAct > 0; } catch (Exception exc) { Log.Error($"Eccezione durante ResourcesDeleteAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Elenco record risorse da DB /// /// internal async Task> ResourcesGetAllAsync() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetResource .Include(d => d.DriverNav) .Include(j => j.JobStepNav) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ResourcesGetAllAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Upsert record /// /// /// internal async Task ResourcesUpsertAsync(ResourceModel upsRec) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currRec = dbCtx .DbSetResource .Where(x => upsRec.ResourceID > 0 && x.ResourceID == upsRec.ResourceID) .FirstOrDefault(); // se trovato --> aggiorno if (currRec != null) { dbCtx.Entry(currRec).CurrentValues.SetValues(upsRec); } // se mancasse --> aggiungo else { dbCtx.DbSetResource.Add(upsRec); } // salvo... int numAct = await dbCtx.SaveChangesAsync(); answ = numAct > 0; } catch (Exception exc) { Log.Error($"Eccezione durante ResourcesUpsertAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Esegue merge dei dati nella tab profili del DB con le info accessorie... /// /// /// /// /// internal async Task SaveProfileListAsync(string uID, Constants.EXECENVIRONMENTS execEnvironment, string rawContent) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { // solo se ho qualcosa da controllare... if (!string.IsNullOrEmpty(rawContent)) { // in primis recupero profili attuali var dbList = await dbCtx .DbSetConfProfile .ToListAsync(); // ciclo sul contenuto ricevuto, se mancasse aggiungo! List list2check = JsonConvert.DeserializeObject>(rawContent) ?? new List(); List rec2ins = new List(); foreach (var item in list2check) { if (!dbList.Any(x => x.Code == item)) { rec2ins.Add(new ProfileModel() { Code = item, Description = $"{item} - NEW" }); } } // se ho dati li inserisco... if (rec2ins.Count > 0) { dbCtx.DbSetConfProfile.AddRange(rec2ins); // salvo... int numDone = await dbCtx.SaveChangesAsync(); answ = numDone > 0; } } } return answ; } /// /// Salvataggio info serializzate x soglie e dati opzionali sul DB /// /// /// /// /// /// /// internal async Task SaveProfileThreshAsync(string uID, Constants.EXECENVIRONMENTS execEnvironment, string rawThreshold, string rawData) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { // solo se ho qualcosa da controllare... if (!string.IsNullOrEmpty(rawThreshold) || !string.IsNullOrEmpty(rawData)) { // in primis recupero profilo da aggiornare var dbRec = await dbCtx .DbSetConfProfile .Where(x => x.Code == uID) .FirstOrDefaultAsync(); // se ho record aggiorno... if (dbRec != null) { dbRec.ProfDataRaw = rawData; dbRec.ThreshDataRaw = rawThreshold; dbCtx.Entry(dbRec).State = EntityState.Modified; } // alrimenti creo + aggiorno else { dbCtx.DbSetConfProfile.Add(new ProfileModel() { Code = uID, Description = $"{uID} - NEW PROFILE", ProfDataRaw = rawData, ThreshDataRaw = rawThreshold }); } // salvo... int numDone = await dbCtx.SaveChangesAsync(); answ = numDone > 0; } } return answ; } /// /// Elenco item da ricerca filtro x gruppo/tipo Async /// /// /// /// internal async Task> SellingItemGetFiltAsync(EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS envir, ItemSourceType sourceType) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetSellItem .Where(x => (x.Envir == envir || envir == Constants.EXECENVIRONMENTS.NULL) && (sourceType == ItemSourceType.ND || x.SourceType == sourceType)) //.Include(j => j.JobNav) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante SellingItemGetFiltAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Restituisce elenco SellingItem dato sEnvir /// /// /// internal async Task> SellingItemsByEnvir(EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS envir) { List dbRec = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { DateTime adesso = DateTime.Now; // recupero offerta... dbRec = await dbCtx .DbSetSellItem .Where(x => x.Envir == envir) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante SellingItemsByEnvirAsync{Environment.NewLine}{exc}"); } } return dbRec; } /// /// Inserisce o aggiorna il record /// /// /// internal async Task SellingItemUpsertAsync(SellingItemModel currRec) { bool result = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { //try //{ var dbResult = await dbCtx .DbSetSellItem .Where(x => x.SellingItemID == currRec.SellingItemID) .FirstOrDefaultAsync(); if (dbResult != null) { //dbCtx.DbSetItem.Remove(newCount); dbResult.Cost = currRec.Cost; dbResult.Description = currRec.Description; dbResult.Envir = currRec.Envir; dbResult.ExtItemCode = currRec.ExtItemCode; dbResult.IsService = currRec.IsService; dbResult.ItemCode = currRec.ItemCode; dbResult.ItemSteps = currRec.ItemSteps; dbResult.JobID = currRec.JobID; dbResult.Margin = currRec.Margin; dbResult.SerStruct = currRec.SerStruct; dbResult.SourceType = currRec.SourceType; dbResult.SupplCode = currRec.SupplCode; dbResult.UM = currRec.UM; dbCtx.Entry(dbResult).State = EntityState.Modified; } else { dbCtx.DbSetSellItem.Add(currRec); } await dbCtx.SaveChangesAsync(); //} //catch (Exception exc) //{ // Log.Error($"Eccezione durante ItemUpsertAsync{Environment.NewLine}{exc}"); //} } return result; } #if false /// /// Ritorna direttamente 1 riga offerta /// /// /// internal OfferRowModel? OfferRowGetByOfferRowID(int offerRowID) { OfferRowModel? dbResult = null; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetOfferRow .Where(x => x.OfferRowID == offerRowID) .Include(s => s.SellingItemNav) .FirstOrDefault(); } catch (Exception exc) { Log.Error($"Eccezione durante OfferRowGetByOfferRowID{Environment.NewLine}{exc}"); } } return dbResult; } #endif /// /// Elenco da DB delel stats aggregate dato periodo inizio/fine /// /// /// /// internal async Task> StatsAggrGetAsync(DateTime dtStart, DateTime dtEnd) { List answ = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { // recupero ed ordino per data-ora answ = await dbCtx .DbSetStatsAggr .Where(x => x.Hour >= dtStart && x.Hour <= dtEnd) .AsNoTracking() .OrderBy(x => x.Hour) .ToListAsync(); } return answ; } /// /// Range periodo per chiamate aggregate /// /// internal async Task StatsAggrRangeAsync() { Utils.DtUtils.Periodo answ = new Utils.DtUtils.Periodo(Utils.DtUtils.PeriodSet.Today); using (DataLayerContext dbCtx = new DataLayerContext()) { var query = dbCtx.DbSetStatsAggr.AsQueryable(); var minHour = await query.MinAsync(x => x.Hour); var maxHour = await query.MaxAsync(x => x.Hour); answ.Inizio = minHour; answ.Fine = maxHour; return answ; } } /// /// Esegue insert statistiche aggregate sul DB /// /// Elenco dei record da inserire /// Se true preventivamente elimina record nel periodo richiesto /// internal async Task StatsAggrUpsertAsync(List listRecords, bool removeOld) { int answ = 0; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { // in primis se richiesto calcolo range periodo e svuoto... if (removeOld) { var firstRec = listRecords.OrderBy(x => x.Hour).FirstOrDefault(); var lastRec = listRecords.OrderByDescending(x => x.Hour).FirstOrDefault(); if (firstRec != null && lastRec != null) { DateTime startDate = firstRec.Hour; DateTime endDate = lastRec.Hour; // uso direttamente ExecuteDelete await dbCtx .DbSetStatsAggr .Where(x => x.Hour >= startDate && x.Hour <= endDate) .ExecuteDeleteAsync(); } } // ora preparo inserimento massivo await dbCtx .DbSetStatsAggr .AddRangeAsync(listRecords); // salvo! answ = await dbCtx.SaveChangesAsync(); // libero memoria del changeTracker dbCtx.ChangeTracker.Clear(); } return answ; } /// /// Recupera dati stats di dettaglio dato filtro envir/tipo (opzionali) e periodo /// /// /// /// /// /// internal async Task> StatsDetailModelGetAsync(DateTime dtStart, DateTime dtEnd, string sEnvir = "", string sType = "") { List answ = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { // recupero ed ordino per data-ora var query = dbCtx.DbSetStatsDet .Where(x => x.Hour >= dtStart && x.Hour <= dtEnd); if (!string.IsNullOrEmpty(sEnvir)) query = query.Where(x => x.Environment == sEnvir); if (!string.IsNullOrEmpty(sType)) query = query.Where(x => x.Type == sType); answ = await query .AsNoTracking() .OrderBy(x => x.Hour) .ThenBy(x => x.Environment) .ThenBy(x => x.Type) .ToListAsync(); } return answ; } /// /// Range periodo x chiamate detail eventualmente filtrate /// /// /// /// internal async Task StatsDetailModelRangeAsync(string sEnvir, string sType) { Utils.DtUtils.Periodo answ = new Utils.DtUtils.Periodo(Utils.DtUtils.PeriodSet.Today); using (DataLayerContext dbCtx = new DataLayerContext()) { var query = dbCtx.DbSetStatsDet.AsQueryable(); if (!string.IsNullOrEmpty(sEnvir)) query = query.Where(x => x.Environment == sEnvir); if (!string.IsNullOrEmpty(sType)) query = query.Where(x => x.Type == sType); var minHour = await query.MinAsync(x => x.Hour); var maxHour = await query.MaxAsync(x => x.Hour); answ.Inizio = minHour; answ.Fine = maxHour; return answ; } } /// /// Esegue insert statistiche di dettaglio sul DB /// /// Elenco dei record da inserire /// Se true preventivamente elimina record nel periodo richiesto /// internal async Task StatsDetailModelUpsertAsync(List listRecords, bool removeOld) { int answ = 0; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { // in primis se richiesto calcolo range periodo e svuoto... if (removeOld) { var firstRec = listRecords.OrderBy(x => x.Hour).FirstOrDefault(); var lastRec = listRecords.OrderByDescending(x => x.Hour).FirstOrDefault(); if (firstRec != null && lastRec != null) { DateTime startDate = firstRec.Hour; DateTime endDate = lastRec.Hour; // uso direttamente ExecuteDelete await dbCtx .DbSetStatsDet .Where(x => x.Hour >= startDate && x.Hour <= endDate) .ExecuteDeleteAsync(); } } // ora preparo inserimento massivo await dbCtx .DbSetStatsDet .AddRangeAsync(listRecords); // salvo! answ = await dbCtx.SaveChangesAsync(); // libero memoria del changeTracker dbCtx.ChangeTracker.Clear(); } return answ; } /// /// Elenco completo Tags /// /// internal async Task> TagsGetAllAsync() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetTags .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante TagsGetAllAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Esegue il cloning completo di un Template e di TUTTE le relative righe... /// /// /// internal async Task TemplateCloneAsync(TemplateModel rec2clone) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { DateTime adesso = DateTime.Now; // recupero offerta... var currRec = dbCtx .DbSetTemplate .Where(x => x.TemplateID == rec2clone.TemplateID) .Include(x => x.TemplateRowNav) .FirstOrDefault(); // se trovo --> duplico! if (currRec != null) { // recupero ultimo num offerta dell'anno corrente... TemplateModel newRec = new TemplateModel() { Name = rec2clone.Name, Envir = rec2clone.Envir, Description = rec2clone.Description, SourceType = rec2clone.SourceType, }; // sistemo child... newRec.TemplateRowNav = currRec.TemplateRowNav .Select(c => new TemplateRowModel() { AwaitBom = c.AwaitBom, AwaitPrice = c.AwaitPrice, BomCost = c.BomCost, BomOk = c.BomOk, BomPrice = c.BomPrice, Envir = c.Envir, FileName = c.FileName, FileResource = c.FileResource, FileSize = c.FileSize, Inserted = adesso, ItemBOM = c.ItemBOM, ItemJCD = c.ItemJCD, ItemOk = c.ItemOk, ItemSteps = c.ItemSteps, ItemTags = c.ItemTags, JobID = c.JobID, Note = c.Note, ProdItemQty = c.ProdItemQty, Qty = c.Qty, RowNum = c.RowNum, SellingItemID = c.SellingItemID, SerStruct = c.SerStruct, StepCost = c.StepCost, StepFlowTime = c.StepFlowTime, StepLeadTime = c.StepLeadTime, StepPrice = c.StepPrice, Name = c.Name, TemplateRowUID = c.TemplateRowDtx }) .ToList(); // infine aggiungo riga ordine e relativi child dbCtx.DbSetTemplate.Add(newRec); } // salvo TUTTI i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante TemplateCloneAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Eliminazione record /// /// /// internal async Task TemplateDeleteAsync(TemplateModel rec2del) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currRec = dbCtx .DbSetTemplate .Where(x => rec2del.TemplateID == rec2del.TemplateID) .FirstOrDefault(); // se trovato --> elimino if (currRec != null) { dbCtx.DbSetTemplate.Remove(rec2del); } // salvo... int numAct = await dbCtx.SaveChangesAsync(); answ = numAct > 0; } catch (Exception exc) { Log.Error($"Eccezione durante TemplateDeleteAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Elenco completo Template da DB /// /// internal async Task> TemplateGetAllAsync() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetTemplate .Include(r => r.TemplateRowNav) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante TemplateGetAllAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco filtrato (parent) TemplateRow da DB /// /// /// internal async Task> TemplateRowByParentAsync(int templateID) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetTemplateRow .Where(x => x.TemplateID == templateID) .Include(r => r.SellingItemNav) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante TemplateRowByParentAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Esegue il cloning completo di un TemplateRow e di TUTTE le relative righe... /// /// /// internal async Task TemplateRowCloneAsync(TemplateRowModel rec2clone) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { DateTime adesso = DateTime.Now; // recupero offerta... var currRec = await dbCtx .DbSetTemplateRow .Where(x => x.TemplateRowID == rec2clone.TemplateRowID) .Include(x => x.SellingItemNav) .FirstOrDefaultAsync(); // se trovo --> duplico! if (currRec != null) { // cerco ultimo rec... var lastRec = await dbCtx .DbSetTemplateRow .Where(x => x.TemplateID == rec2clone.TemplateID) .OrderByDescending(x => x.RowNum) .FirstOrDefaultAsync(); int rowNum = lastRec != null ? lastRec.RowNum + 1 : 1; TemplateRowModel newRec = new TemplateRowModel() { Name = rec2clone.Name, Envir = rec2clone.Envir, AwaitBom = rec2clone.AwaitBom, AwaitPrice = rec2clone.AwaitPrice, BomCost = rec2clone.BomCost, BomOk = rec2clone.BomOk, BomPrice = rec2clone.BomPrice, FileName = rec2clone.FileName, FileResource = rec2clone.FileResource, FileSize = rec2clone.FileSize, Inserted = rec2clone.Inserted, ItemBOM = rec2clone.ItemBOM, ItemJCD = rec2clone.ItemJCD, ItemOk = rec2clone.ItemOk, ItemSteps = rec2clone.ItemSteps, ItemTags = rec2clone.ItemTags, JobID = rec2clone.JobID, Note = rec2clone.Note, ProdItemQty = rec2clone.ProdItemQty, Qty = rec2clone.Qty, RowNum = rowNum, SellingItemID = rec2clone.SellingItemID, SellingItemNav = rec2clone.SellingItemNav, SerStruct = rec2clone.SerStruct, StepCost = rec2clone.StepCost, StepFlowTime = rec2clone.StepFlowTime, StepLeadTime = rec2clone.StepLeadTime, StepPrice = rec2clone.StepPrice, TemplateID = rec2clone.TemplateID, TemplateRowUID = rec2clone.TemplateRowDtx }; //newRec.TemplateRowUID = newRec.TemplateRowDtx; // infine aggiungo riga ordine e relativi child dbCtx.DbSetTemplateRow.Add(newRec); } // salvo TUTTI i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante TemplateRowCloneAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Eliminazione record /// /// /// internal async Task TemplateRowDeleteAsync(TemplateRowModel rec2del) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currRec = dbCtx .DbSetTemplateRow .Where(x => rec2del.TemplateRowID == rec2del.TemplateRowID) .FirstOrDefault(); // se trovato --> elimino if (currRec != null) { dbCtx.DbSetTemplateRow.Remove(rec2del); } // salvo... int numAct = await dbCtx.SaveChangesAsync(); answ = numAct > 0; } catch (Exception exc) { Log.Error($"Eccezione durante TemplateRowDeleteAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Aggiornamento valore UID non calcolato + ritorno elenco UID da aggiornare /// /// /// internal List TemplateRowFixUid(int TemplateID) { List answ = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currList = dbCtx .DbSetTemplateRow .Where(x => x.TemplateID == TemplateID) .ToList(); // se trovato --> verifico valori differenti, aggiorno e restituisco da calcolare if (currList != null) { var list2fix = currList.Where(x => string.IsNullOrEmpty(x.TemplateRowUID) || x.TemplateRowUID != x.TemplateRowDtx).ToList(); if (list2fix != null && list2fix.Count > 0) { // salvo elenco answ = list2fix.Select(x => x.TemplateRowDtx).ToList(); // sistemo UID foreach (var item in list2fix) { item.TemplateRowUID = item.TemplateRowDtx; dbCtx.Entry(item).State = EntityState.Modified; } // salvo... var result = dbCtx.SaveChanges(); } } } catch (Exception exc) { Log.Error($"Eccezione durante TemplateRowFixUid{Environment.NewLine}{exc}"); } } return answ; } /// /// Elenco completo TemplateRow da DB /// /// internal async Task> TemplateRowGetAllAsync() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetTemplateRow .Include(r => r.SellingItemNav) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante TemplateRowGetAllAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Effettua update stato await BOM/PRICE per l template indicato /// /// ID singola riga offerta /// Se non nullo è il nuovo stato await BOM /// Se non nullo è stato await Price /// internal async Task TemplateRowUpdateAwaitStateAsync(int TemplateRowID, bool? awaitBom, bool? awaitPrice) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero righe offerta... var currRec = dbCtx .DbSetTemplateRow .Where(x => x.TemplateRowID == TemplateRowID) .FirstOrDefault(); // aggiorno parametri (se inviati) if (currRec != null) { currRec.AwaitBom = awaitBom ?? currRec.AwaitBom; currRec.AwaitPrice = awaitPrice ?? currRec.AwaitPrice; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante TemplateRowUpdateAwaitStateAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Aggiorno sul DB i dati del file associato /// /// /// internal async Task TemplateRowUpdateFileDataAsync(TemplateRowModel updRec) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero righe Template... var currRec = dbCtx .DbSetTemplateRow .Where(x => x.TemplateRowID == updRec.TemplateRowID) .FirstOrDefault(); // aggiorno parametri (se inviati) if (currRec != null) { currRec.FileName = updRec.FileName; currRec.FileResource = updRec.FileResource; currRec.FileSize = updRec.FileSize; currRec.SerStruct = updRec.SerStruct; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante TemplateRowUpdateFileDataAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Effettua update serStruct per il Template indicato /// /// ID singola Riga Template /// Valore serializzatoe /// internal async Task TemplateRowUpdateSerStructAsync(int TemplateRowID, string serStruct) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero righe offerta... var currRec = dbCtx .DbSetTemplateRow .Where(x => x.TemplateRowID == TemplateRowID) .FirstOrDefault(); // aggiorno parametri (se inviati) if (currRec != null) { currRec.SerStruct = serStruct; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante TemplateRowUpdateSerStructAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Upsert record TemplateRow /// /// /// internal async Task TemplateRowUpsertAsync(TemplateRowModel updRec) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero offerta... var currRec = dbCtx .DbSetTemplateRow .Where(x => x.TemplateRowID == updRec.TemplateRowID) .FirstOrDefault(); // se non trovo aggiungo if (currRec == null) { dbCtx.DbSetTemplateRow.Add(updRec); } // altrimenti aggiorno else { dbCtx.Entry(currRec).CurrentValues.SetValues(updRec); } // salvo TUTTI i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante TemplateRowUpsertAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Effettua update dei costi di tutte le righe del template indicato /// /// /// internal async Task TemplateUpdateCostAsync(int TemplateID) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero righe template... var offRowList = dbCtx .DbSetTemplateRow .Where(x => x.TemplateID == TemplateID) .ToList(); // recupero l'elenco degli itemGroup gestiti var itemGroupList = dbCtx .DbSetItemGroup .ToList(); // recupero il subset item da BOM / BomAlt... var bomGenList = dbCtx .DbSetItem .Where(x => (x.ItemType == Core.Enums.ItemClassType.Bom || x.ItemType == Core.Enums.ItemClassType.BomAlt)) .ToList(); // ciclo! foreach (var currRec in offRowList) { // se contiene qualcosa x BOM... if (!string.IsNullOrEmpty(currRec.ItemBOM) && currRec.ItemBOM.Length > 2) { // deserializzo var bomList = JsonConvert.DeserializeObject>(currRec.ItemBOM); // se ho trovato elementi... if (bomList != null) { // calcolo il NUOVO costo e lo aggiorno... double totCost = 0; double totPrice = 0; int totItemQty = 0; int numGroupOk = 0; int numItemOk = 0; int numElems = bomList.Count; // validazione e completamento BOM validateBom(itemGroupList, bomGenList, ref bomList, null, ref totCost, ref totPrice, ref totItemQty, ref numGroupOk, ref numItemOk); // salvo BOM... string itemBom = JsonConvert.SerializeObject(bomList); currRec.ItemBOM = itemBom; // salvo arrotondato alla 3° decimale currRec.BomCost = Math.Round(totCost, 3); currRec.BomPrice = Math.Round(totPrice, 3); currRec.BomOk = numElems == numGroupOk; currRec.ItemOk = numElems == numItemOk; currRec.ProdItemQty = totItemQty; dbCtx.Entry(currRec).State = EntityState.Modified; } } } // salvo TUTTI i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante TemplateUpdateCostAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Upsert record Template /// /// /// internal async Task TemplateUpsertAsync(TemplateModel updRec) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero offerta... var currRec = dbCtx .DbSetTemplate .Where(x => x.TemplateID == updRec.TemplateID) .FirstOrDefault(); // se non trovo aggiungo if (currRec == null) { dbCtx.DbSetTemplate.Add(updRec); } // altrimenti aggiorno else { dbCtx.Entry(currRec).CurrentValues.SetValues(updRec); } // salvo TUTTI i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante TemplateUpsertAsync{Environment.NewLine}{exc}"); } } return answ; } internal bool UpdateCodGroup(List bomList) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { // in primis calcolo i distinct dei CodGroup x eventuale insert preventivo List distCodGroups = bomList .Select(i => i.ClassCode) .Distinct() .Where(c => !string.IsNullOrWhiteSpace(c)) .ToList(); // recupero l'elenco degli itemGroup gestiti var itemGroupList = dbCtx .DbSetItemGroup .ToList(); // elenco da inserire... var codGroupsToInsert = distCodGroups .Where(x => !itemGroupList.Any(i => i.CodGroup == x)) .Select(x => new ItemGroupModel() { CodGroup = x, Description = x }) .ToList(); // se ci sono inserisco! if (codGroupsToInsert != null && codGroupsToInsert.Count > 0) { dbCtx .DbSetItemGroup .AddRange(codGroupsToInsert); // salvo... dbCtx.SaveChanges(); } } return answ; } #endregion Internal Methods #if false /// /// Elenco item Child da ID Parent (per sostituzione) /// /// ID parent (valido quindi >0) /// internal List ItemGetChild(int ItemIdParent) { List dbResult = new List(); if (ItemIdParent > 0) { //using (DataLayerContext dbCtx = new DataLayerContext(configuration)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetItem .Where(x => x.ItemID == ItemIdParent || x.ItemIDParent == ItemIdParent) .ToList(); } catch (Exception exc) { Log.Error($"Eccezione durante ItemGetChild{Environment.NewLine}{exc}"); } } } return dbResult; } #endif #region Private Fields private static IConfiguration _configuration; private static Logger Log = LogManager.GetCurrentClassLogger(); #endregion Private Fields #region Private Methods /// /// Esegue completamento e la validazione dei dati BOM da lista articoli + gruppi, /// validando i dati stessi /// /// Elenco ItemGroup da considerare /// Item di tipo BOM/BomAlt AMMESSI /// Lista BOM ricevuta da validare /// Lista BOM precedente da confrontare x scelta alternativi /// Costo netto componenti BOM calcolato /// Prezzo complessivo calcolato (con aggiunta marginalità) /// Numero toale di item dello step BOM /// Controllo coerenza calcoli sui gruppi list2upd /// Controllo coerenza calcoli su num list2upd private static void validateBom(List itemGroupList, List bomGenList, ref List bomList, List? bomListPrev, ref double totCost, ref double totPrice, ref int totItemQty, ref int numGroupOk, ref int numItemOk) { double margin = 0; // ciclo x ogni elemento della BOM, cercando x gruppo e ExtItemCode foreach (var item in bomList) { // init del margine margin = 0; // verifico item group esistente... if (itemGroupList.Where(x => x.CodGroup == item.ClassCode).Count() > 0) { numGroupOk++; } // 2025.09.16: se il prezzo arriva dalla BOM calcolata uso quello... if (item.Price > 0) { // resetto ItemID ma NON il prezzo item.ItemID = 0; // conto l'item numItemOk++; item.PriceEff = item.Price; // dovrei recuperare margine da BOM... da rivedere, x ora cablato 20%... margin = 0.2; } else { /************************************************* * Ricerca costo item: * - se ho un itemID --> cerco record ESATTO e sostituisco descrizioni & co... * - se non ho itemID --> cerco dati di selezione + qtyRange *************************************************/ ItemModel? recCost = null; bool selExact = item.ItemID > 0; if (selExact) { recCost = bomGenList .Where(x => x.ItemID == item.ItemID) .FirstOrDefault(); } else { recCost = bomGenList .Where(x => x.CodGroup == item.ClassCode && x.ItemIDParent == 0 // voglio NON sia un record CHILD && x.ExtItemCode == item.ItemCode && item.Qty >= x.QtyMin && item.Qty < x.QtyMax) .OrderByDescending(x => x.Cost) .FirstOrDefault(); // 2025.09.24: se ho un elenco item della BOM precedente if (bomListPrev != null && bomListPrev.Count > 0 && recCost != null) { // ...cerco item trovato come PARENT di altri item var listAlt = bomGenList.Where(x => x.ItemIDParent == recCost.ItemID).ToList(); // che cerco nella nella BOM precedente... var result = listAlt .Join( bomListPrev, l1 => l1.ItemID, l2 => l2.ItemID, (l1, l2) => l1) .ToList(); // nel caso ne trovassi solo 1 uso quello al posto del recCost... if (result.Count == 1) { recCost = result.FirstOrDefault(); } } } // se trovato valorizzo! if (recCost != null) { numItemOk++; item.ItemID = recCost.ItemID; //item.PriceEff = recCost.BomCost * (1 + recCost.Margin); item.PriceEff = recCost.Cost; // se selezione esatta sovrascrivo altri valori if (selExact) { item.ItemCode = recCost.ExtItemCode; //item.DescriptionCode = recCost.Name; } margin = recCost.Margin; } else { item.ItemID = 0; item.PriceEff = 0; } } // ...e aggiorno totale totCost += item.TotalCost; // e prezzo totale compreso margine totPrice += item.TotalCost * (1 + margin); totItemQty += item.ItemQty; } } #endregion Private Methods } }