diff --git a/EgwCoreLib.Lux.Data/Controllers/LuxController.cs b/EgwCoreLib.Lux.Data/Controllers/LuxController.cs
index 373a4513..72afc345 100644
--- a/EgwCoreLib.Lux.Data/Controllers/LuxController.cs
+++ b/EgwCoreLib.Lux.Data/Controllers/LuxController.cs
@@ -294,6 +294,7 @@ namespace EgwCoreLib.Lux.Data.Controllers
return dbResult;
}
+#if false
///
/// Esegue il cloning completo di un offerta e di TUTTE le relative righe di offerta...
///
@@ -456,6 +457,130 @@ namespace EgwCoreLib.Lux.Data.Controllers
return dbResult;
}
+ ///
+ /// 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
+ BomCalculator.Validate(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;
+ }
+
+ ///
+ /// Add 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;
+ }
+#endif
+
///
/// Recupera da DB riga offerta dato Primary ID
///
@@ -928,128 +1053,7 @@ namespace EgwCoreLib.Lux.Data.Controllers
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
- BomCalculator.Validate(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;
- }
-
- ///
- /// Add 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
diff --git a/EgwCoreLib.Lux.Data/Repository/Sales/IOfferRepository.cs b/EgwCoreLib.Lux.Data/Repository/Sales/IOfferRepository.cs
new file mode 100644
index 00000000..0dcf1a4a
--- /dev/null
+++ b/EgwCoreLib.Lux.Data/Repository/Sales/IOfferRepository.cs
@@ -0,0 +1,36 @@
+using EgwCoreLib.Lux.Data.DbModel.Items;
+using EgwCoreLib.Lux.Data.DbModel.Sales;
+
+namespace EgwCoreLib.Lux.Data.Repository.Sales
+{
+ public interface IOfferRepository : IBaseRepository
+ {
+ #region Public Methods
+
+ Task AddAsync(OfferModel entity);
+
+ Task CloneAsync(OfferModel rec2clone);
+
+ Task DeleteAsync(OfferModel entity);
+
+ Task> GetAllAsync();
+
+ Task> GetBomItemsAsync();
+
+ Task GetByIdAsync(int recId);
+
+ Task> GetFiltAsync(DateTime inizio, DateTime fine);
+
+ Task> GetItemGroupsAsync();
+
+ Task> GetRowsAsync(int recId);
+
+ Task SaveRowsAsync(List rows);
+
+ Task UpdateAsync(OfferModel entity);
+
+ Task UpdateCostAsync(int OfferID);
+
+ #endregion Public Methods
+ }
+}
\ No newline at end of file
diff --git a/EgwCoreLib.Lux.Data/Repository/Sales/IOrderRepository.cs b/EgwCoreLib.Lux.Data/Repository/Sales/IOrderRepository.cs
new file mode 100644
index 00000000..2ec401ae
--- /dev/null
+++ b/EgwCoreLib.Lux.Data/Repository/Sales/IOrderRepository.cs
@@ -0,0 +1,21 @@
+using EgwCoreLib.Lux.Data.DbModel.Sales;
+
+namespace EgwCoreLib.Lux.Data.Repository.Sales
+{
+ public interface IOrderRepository : IBaseRepository
+ {
+ #region Public Methods
+
+ Task AddAsync(OrderModel entity);
+
+ Task DeleteAsync(OrderModel entity);
+
+ Task> GetAllAsync();
+
+ Task GetByIdAsync(int recId);
+
+ Task UpdateAsync(OrderModel entity);
+
+ #endregion Public Methods
+ }
+}
\ No newline at end of file
diff --git a/EgwCoreLib.Lux.Data/Repository/Sales/OfferRepository.cs b/EgwCoreLib.Lux.Data/Repository/Sales/OfferRepository.cs
new file mode 100644
index 00000000..d36cb5fd
--- /dev/null
+++ b/EgwCoreLib.Lux.Data/Repository/Sales/OfferRepository.cs
@@ -0,0 +1,270 @@
+using EgwCoreLib.Lux.Core.RestPayload;
+using EgwCoreLib.Lux.Data.DbModel.Items;
+using EgwCoreLib.Lux.Data.DbModel.Sales;
+using EgwCoreLib.Lux.Data.Domains;
+using Microsoft.EntityFrameworkCore;
+using Newtonsoft.Json;
+using static EgwCoreLib.Lux.Core.Enums;
+
+namespace EgwCoreLib.Lux.Data.Repository.Sales
+{
+ public class OfferRepository : BaseRepository, IOfferRepository
+ {
+ #region Public Constructors
+
+ public OfferRepository(IDbContextFactory ctxFactory) : base(ctxFactory)
+ {
+ }
+
+ #endregion Public Constructors
+
+ #region Public Methods
+
+ public async Task AddAsync(OfferModel entity)
+ {
+ await using var dbCtx = await CreateContextAsync();
+ await dbCtx.DbSetOffer.AddAsync(entity);
+ return await dbCtx.SaveChangesAsync() > 0;
+ }
+
+ ///
+ /// Esegue il cloning completo di un Offerta e di TUTTE le relative righe...
+ ///
+ ///
+ ///
+ public async Task CloneAsync(OfferModel rec2clone)
+ {
+ await using var dbCtx = await CreateContextAsync();
+ DateTime now = DateTime.Now;
+
+ var currRec = await dbCtx.DbSetOffer
+ .Include(x => x.OfferRowNav)
+ .FirstOrDefaultAsync(x => x.OfferID == rec2clone.OfferID);
+
+ if (currRec == null)
+ return false;
+
+ DateTime adesso = DateTime.Now;
+ var lastRec = dbCtx
+ .DbSetOffer
+ .Where(x => x.RefYear == adesso.Year)
+ .OrderByDescending(x => x.RefNum)
+ .FirstOrDefault();
+ int newRefNum = lastRec != null ? lastRec.RefNum + 1 : 1;
+
+ // 2. Creo il nuovo parent
+ var 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
+ };
+
+ // 3. Clono i child
+ // 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();
+
+ // 4. Aggiungo il nuovo parent (EF aggiunge anche i child)
+ dbCtx.DbSetOffer.Add(newRec);
+
+ // 5. Salvo tutto
+ return await dbCtx.SaveChangesAsync() > 0;
+ }
+
+ public async Task DeleteAsync(OfferModel entity)
+ {
+ await using var dbCtx = await CreateContextAsync();
+ dbCtx.DbSetOffer.Remove(entity);
+ return await dbCtx.SaveChangesAsync() > 0;
+ }
+
+ public async Task> GetAllAsync()
+ {
+ await using var dbCtx = await CreateContextAsync();
+ return await dbCtx.DbSetOffer
+ .Include(c => c.CustomerNav)
+ .Include(d => d.DealerNav)
+ .Include(o => o.OfferRowNav)
+ .AsNoTracking()
+ .ToListAsync();
+ }
+
+ public async Task> GetBomItemsAsync()
+ {
+ await using var dbCtx = await CreateContextAsync();
+ return await dbCtx.DbSetItem
+ .Where(x => x.ItemType == ItemClassType.Bom || x.ItemType == ItemClassType.BomAlt)
+ .ToListAsync();
+ }
+
+ public async Task GetByIdAsync(int recId)
+ {
+ await using var dbCtx = await CreateContextAsync();
+ return await dbCtx.DbSetOffer.FirstOrDefaultAsync(x => x.OfferID == recId);
+ }
+
+ public async Task> GetFiltAsync(DateTime inizio, DateTime fine)
+ {
+ await using var dbCtx = await CreateContextAsync();
+ return await dbCtx.DbSetOffer
+ .Where(x => x.Inserted >= inizio && x.Inserted <= fine)
+ .Include(c => c.CustomerNav)
+ .Include(d => d.DealerNav)
+ .Include(o => o.OfferRowNav)
+ .AsNoTracking()
+ .ToListAsync();
+ }
+
+ public async Task> GetItemGroupsAsync()
+ {
+ await using var dbCtx = await CreateContextAsync();
+ return await dbCtx.DbSetItemGroup.ToListAsync();
+ }
+
+ public async Task> GetRowsAsync(int recId)
+ {
+ await using var dbCtx = await CreateContextAsync();
+ return await dbCtx.DbSetOfferRow
+ .Where(x => x.OfferID == recId)
+ .ToListAsync();
+ }
+
+ public async Task SaveRowsAsync(List rows)
+ {
+ await using var dbCtx = await CreateContextAsync();
+ foreach (var row in rows)
+ dbCtx.Entry(row).State = EntityState.Modified;
+
+ return await dbCtx.SaveChangesAsync() > 0;
+ }
+
+ public async Task UpdateAsync(OfferModel entity)
+ {
+ await using var dbCtx = await CreateContextAsync();
+ // Recuperiamo l'entità tracciata dal context
+ var trackedEntity = dbCtx.DbSetOffer.Local.FirstOrDefault(x => x.OfferID == entity.OfferID);
+
+ if (trackedEntity != null)
+ {
+ // verifico eventuale riapertura SE fosse expired ma la data è valida...
+ if (trackedEntity.OffertState == OfferStates.Expired && entity.ValidUntil > DateTime.Today)
+ {
+ entity.OffertState = OfferStates.Open;
+ }
+ // Aggiorna i valori dell'entità tracciata con quelli della nuova
+ dbCtx.Entry(trackedEntity).CurrentValues.SetValues(entity);
+ }
+ else
+ {
+ dbCtx.DbSetOffer.Update(entity);
+ }
+ return await dbCtx.SaveChangesAsync() > 0;
+ }
+
+ public async Task UpdateCostAsync(int OfferID)
+ {
+ await using var dbCtx = await CreateContextAsync();
+
+ // recupero righe offerta...
+ var offRowList = await dbCtx
+ .DbSetOfferRow
+ .Where(x => x.OfferID == OfferID)
+ .ToListAsync();
+
+ // recupero l'elenco degli itemGroup gestiti
+ var itemGroupList = await dbCtx
+ .DbSetItemGroup
+ .ToListAsync();
+
+ // recupero il subset item da BOM / BomAlt...
+ var bomGenList = await dbCtx
+ .DbSetItem
+ .Where(x => (x.ItemType == Core.Enums.ItemClassType.Bom || x.ItemType == Core.Enums.ItemClassType.BomAlt))
+ .ToListAsync();
+
+ // 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
+ BomCalculator.Validate(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;
+ }
+ }
+ }
+
+ return await dbCtx.SaveChangesAsync() > 0;
+ }
+
+ #endregion Public Methods
+ }
+}
\ No newline at end of file
diff --git a/EgwCoreLib.Lux.Data/Services/DataLayerServices.cs b/EgwCoreLib.Lux.Data/Services/DataLayerServices.cs
index af9a5312..bb19d526 100644
--- a/EgwCoreLib.Lux.Data/Services/DataLayerServices.cs
+++ b/EgwCoreLib.Lux.Data/Services/DataLayerServices.cs
@@ -316,6 +316,7 @@ namespace EgwCoreLib.Lux.Data.Services
return result;
}
+#if false
///
/// Esegue clone completo dell'offerta indicata e svuota la cache
///
@@ -404,6 +405,65 @@ namespace EgwCoreLib.Lux.Data.Services
return result;
}
+ ///
+ /// Effettua update dei costi di tutte le righe dell'offerta indicata
+ ///
+ /// Key
+ ///
+ public async Task OffertUpdateCost(int OfferID)
+ {
+ using var activity = StartActivity();
+ string source = "DB+REDIS";
+ // calcolo
+ bool fatto = await dbController.OffertUpdateCost(OfferID);
+ // svuoto cache...
+ await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:Offers:*");
+ await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:OfferRows:*");
+ activity?.SetTag("data.source", source);
+ LogTrace($"{source} | trace: {activity?.TraceId} | {activity?.Duration.TotalMilliseconds}ms");
+ return fatto;
+ }
+
+ ///
+ /// Effettua Add complessivo record offerta
+ ///
+ /// Key
+ ///
+ public async Task OffertUpsert(OfferModel updRec)
+ {
+ using var activity = StartActivity();
+ string source = "DB+REDIS";
+ // calcolo
+ bool fatto = await dbController.OffertUpsert(updRec);
+ // svuoto cache...
+ await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:Offers:*");
+ await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:OfferRows:*");
+ activity?.SetTag("data.source", source);
+ LogTrace($"{source} | trace: {activity?.TraceId} | {activity?.Duration.TotalMilliseconds}ms");
+ return fatto;
+ }
+#endif
+ ///
+ /// Effettua update dei costi di tutte le righe dell'ordine indicata
+ ///
+ /// Key
+ ///
+ public async Task OrderUpdateCost(int OrderID)
+ {
+ using var activity = StartActivity();
+ bool fatto = false;
+ string source = "DB+REDIS";
+ // calcolo
+#if false
+ fatto = await dbController.OrderUpdateCost(OrderID);
+#endif
+ // svuoto cache...
+ await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:Offers:*");
+ await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:OfferRows:*");
+ activity?.SetTag("data.source", source);
+ LogTrace($"{source} | trace: {activity?.TraceId} | {activity?.Duration.TotalMilliseconds}ms");
+ return fatto;
+ }
///
/// Elenco righe offerta specificata
///
@@ -738,43 +798,6 @@ namespace EgwCoreLib.Lux.Data.Services
return dbResult;
}
- ///
- /// Effettua update dei costi di tutte le righe dell'offerta indicata
- ///
- /// Key
- ///
- public async Task OffertUpdateCost(int OfferID)
- {
- using var activity = StartActivity();
- string source = "DB+REDIS";
- // calcolo
- bool fatto = await dbController.OffertUpdateCost(OfferID);
- // svuoto cache...
- await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:Offers:*");
- await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:OfferRows:*");
- activity?.SetTag("data.source", source);
- LogTrace($"{source} | trace: {activity?.TraceId} | {activity?.Duration.TotalMilliseconds}ms");
- return fatto;
- }
-
- ///
- /// Effettua Add complessivo record offerta
- ///
- /// Key
- ///
- public async Task OffertUpsert(OfferModel updRec)
- {
- using var activity = StartActivity();
- string source = "DB+REDIS";
- // calcolo
- bool fatto = await dbController.OffertUpsert(updRec);
- // svuoto cache...
- await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:Offers:*");
- await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:OfferRows:*");
- activity?.SetTag("data.source", source);
- LogTrace($"{source} | trace: {activity?.TraceId} | {activity?.Duration.TotalMilliseconds}ms");
- return fatto;
- }
///
/// Restituisce record ordine + righe Ordine dato ID
diff --git a/EgwCoreLib.Lux.Data/Services/Sales/IOfferService.cs b/EgwCoreLib.Lux.Data/Services/Sales/IOfferService.cs
new file mode 100644
index 00000000..967f7235
--- /dev/null
+++ b/EgwCoreLib.Lux.Data/Services/Sales/IOfferService.cs
@@ -0,0 +1,21 @@
+using EgwCoreLib.Lux.Data.DbModel.Sales;
+
+namespace EgwCoreLib.Lux.Data.Services.Sales
+{
+ public interface IOfferService
+ {
+ #region Public Methods
+
+ Task CloneAsync(OfferModel rec2clone);
+
+ Task> GetAllAsync();
+
+ Task> GetFiltAsync(DateTime inizio, DateTime fine);
+
+ Task UpsertAsync(OfferModel upsRec);
+
+ Task UpdateCostAsync(int OfferId);
+
+ #endregion Public Methods
+ }
+}
diff --git a/EgwCoreLib.Lux.Data/Services/Sales/OfferService.cs b/EgwCoreLib.Lux.Data/Services/Sales/OfferService.cs
new file mode 100644
index 00000000..c33e14bf
--- /dev/null
+++ b/EgwCoreLib.Lux.Data/Services/Sales/OfferService.cs
@@ -0,0 +1,193 @@
+using EgwCoreLib.Lux.Core.RestPayload;
+using EgwCoreLib.Lux.Data.DbModel.Sales;
+using EgwCoreLib.Lux.Data.Domains;
+using EgwCoreLib.Lux.Data.Repository.Sales;
+using Microsoft.Extensions.Configuration;
+using Newtonsoft.Json;
+using StackExchange.Redis;
+
+namespace EgwCoreLib.Lux.Data.Services.Sales
+{
+ public class OfferService : BaseServ, IOfferService
+ {
+ #region Public Constructors
+
+ public OfferService(
+ IConfiguration config,
+ IConnectionMultiplexer redis,
+ IOfferRepository repo) : base(config, redis)
+ {
+ _className = "Offer";
+ _repo = repo;
+ }
+
+ #endregion Public Constructors
+
+ #region Public Methods
+
+ ///
+ /// Esegue il cloning completo di un Offer e di TUTTE le relative righe...
+ ///
+ ///
+ ///
+ public async Task CloneAsync(OfferModel rec2clone)
+ {
+ return await TraceAsync($"{_className}.Clone", async (activity) =>
+ {
+ // eseguo clone
+ var result = await _repo.CloneAsync(rec2clone);
+
+ // se eseguito, pulisco la cache correlata
+ if (result)
+ {
+ // Invalido sia la lista classi che eventuali dettagli correlati
+ await ClearCacheAsync($"{_redisBaseKey}:{_className}:*");
+ await ClearCacheAsync($"{_redisBaseKey}:OfferRows:*");
+ }
+ return result;
+ });
+ }
+
+ ///
+ /// Elenco completo Offer da DB
+ ///
+ ///
+ public async Task> GetAllAsync()
+ {
+ // Uso helper TraceAsync che gestisce automaticamente StartActivity, Log e Exception tracking
+ return await TraceAsync($"{_className}.GetAll", async (activity) =>
+ {
+ return await GetOrSetCacheAsync(
+ $"{_redisBaseKey}:{_className}:ALL",
+ async () => await _repo.GetAllAsync(),
+ LongCache
+ );
+ });
+ }
+
+ ///
+ /// Elenco completo Offer da DB
+ ///
+ ///
+ public async Task> GetFiltAsync(DateTime inizio, DateTime fine)
+ {
+ // Uso helper TraceAsync che gestisce automaticamente StartActivity, Log e Exception tracking
+ return await TraceAsync($"{_className}.GetFilt", async (activity) =>
+ {
+ return await GetOrSetCacheAsync(
+ $"{_redisBaseKey}:{_className}:Filt:{inizio:yyMMdd-HHmm}:{fine:yyMMdd-HHmm}",
+ async () => await _repo.GetFiltAsync(inizio, fine),
+ LongCache
+ );
+ });
+ }
+
+ ///
+ /// Effettua update dei costi di tutte le righe del Offer indicato
+ ///
+ ///
+ ///
+ public async Task UpdateCostAsync(int OfferId)
+ {
+ return await TraceAsync($"{_className}.Upsert", async (activity) =>
+ {
+ // 1. Recupero dati
+ var rows = await _repo.GetRowsAsync(OfferId);
+ var itemGroups = await _repo.GetItemGroupsAsync();
+ var bomItems = await _repo.GetBomItemsAsync();
+
+ // 2. Calcolo costi BOM
+ foreach (var row in rows)
+ {
+ if (!string.IsNullOrEmpty(row.ItemBOM) && row.ItemBOM.Length > 2)
+ {
+ var bomList = JsonConvert.DeserializeObject>(row.ItemBOM);
+ if (bomList != null)
+ {
+ double totCost = 0;
+ double totPrice = 0;
+ int totItemQty = 0;
+ int numGroupOk = 0;
+ int numItemOk = 0;
+
+ BomCalculator.Validate(
+ itemGroups,
+ bomItems,
+ ref bomList,
+ null,
+ ref totCost,
+ ref totPrice,
+ ref totItemQty,
+ ref numGroupOk,
+ ref numItemOk
+ );
+
+ row.ItemBOM = JsonConvert.SerializeObject(bomList);
+ row.BomCost = Math.Round(totCost, 3);
+ row.BomPrice = Math.Round(totPrice, 3);
+ row.BomOk = bomList.Count == numGroupOk;
+ row.ItemOk = bomList.Count == numItemOk;
+ row.ProdItemQty = totItemQty;
+ }
+ }
+ }
+
+ // 3. Salvo
+ var result = await _repo.SaveRowsAsync(rows);
+
+ if (result)
+ {
+ // 4. Invalido cache
+ await ClearCacheAsync($"{_redisBaseKey}:{_className}:*");
+ await ClearCacheAsync($"{_redisBaseKey}:OfferRows:*");
+ }
+
+ return result;
+ });
+ }
+
+ ///
+ /// Upsert record Offer
+ ///
+ ///
+ ///
+ public async Task UpsertAsync(OfferModel upsRec)
+ {
+ return await TraceAsync($"{_className}.Upsert", async (activity) =>
+ {
+ var currRec = await _repo.GetByIdAsync(upsRec.OfferID);
+
+ string operation = "UPDATE";
+ bool success = false;
+ if (currRec != null)
+ {
+ success = await _repo.UpdateAsync(upsRec);
+ }
+ else
+ {
+ operation = "INSERT";
+ success = await _repo.AddAsync(upsRec);
+ }
+
+ activity?.SetTag("db.operation", operation);
+
+ if (success)
+ {
+ await ClearCacheAsync($"{_redisBaseKey}:{_className}:*");
+ await ClearCacheAsync($"{_redisBaseKey}:OfferRows:*");
+ }
+
+ return success;
+ });
+ }
+
+ #endregion Public Methods
+
+ #region Private Fields
+
+ private readonly string _className;
+ private readonly IOfferRepository _repo;
+
+ #endregion Private Fields
+ }
+}
\ No newline at end of file
diff --git a/Lux.API/Lux.API.csproj b/Lux.API/Lux.API.csproj
index 0a3d51e5..25f5c9ae 100644
--- a/Lux.API/Lux.API.csproj
+++ b/Lux.API/Lux.API.csproj
@@ -4,7 +4,7 @@
net8.0
enable
enable
- 1.1.2603.1715
+ 1.1.2603.1716
diff --git a/Lux.API/Program.cs b/Lux.API/Program.cs
index 805d4189..32878354 100644
--- a/Lux.API/Program.cs
+++ b/Lux.API/Program.cs
@@ -3,12 +3,14 @@ using EgwCoreLib.Lux.Data.Repository.Config;
using EgwCoreLib.Lux.Data.Repository.Cost;
using EgwCoreLib.Lux.Data.Repository.Items;
using EgwCoreLib.Lux.Data.Repository.Job;
+using EgwCoreLib.Lux.Data.Repository.Sales;
using EgwCoreLib.Lux.Data.Repository.Utils;
using EgwCoreLib.Lux.Data.Services;
using EgwCoreLib.Lux.Data.Services.Config;
using EgwCoreLib.Lux.Data.Services.Cost;
using EgwCoreLib.Lux.Data.Services.Items;
using EgwCoreLib.Lux.Data.Services.Job;
+using EgwCoreLib.Lux.Data.Services.Sales;
using EgwCoreLib.Lux.Data.Services.Utils;
using Lux.API.Services;
using Microsoft.EntityFrameworkCore;
@@ -180,6 +182,7 @@ builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
+builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
@@ -195,6 +198,7 @@ builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
+builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
diff --git a/Lux.UI/Components/Compo/OfferRowMan.razor.cs b/Lux.UI/Components/Compo/OfferRowMan.razor.cs
index 47cf2a38..3a99ae39 100644
--- a/Lux.UI/Components/Compo/OfferRowMan.razor.cs
+++ b/Lux.UI/Components/Compo/OfferRowMan.razor.cs
@@ -7,6 +7,7 @@ using EgwCoreLib.Lux.Data.DbModel.Sales;
using EgwCoreLib.Lux.Data.DbModel.Utils;
using EgwCoreLib.Lux.Data.Services;
using EgwCoreLib.Lux.Data.Services.Config;
+using EgwCoreLib.Lux.Data.Services.Sales;
using EgwCoreLib.Lux.Data.Services.Utils;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
@@ -303,6 +304,9 @@ namespace Lux.UI.Components.Compo
[Inject]
private IEnvirParamService EPService { get; set; } = null!;
+ [Inject]
+ private IOfferService OService { get; set; } = default!;
+
[Inject]
private ITemplateService TService { get; set; } = default!;
@@ -881,7 +885,7 @@ namespace Lux.UI.Components.Compo
await InvokeAsync(StateHasChanged);
await Task.Delay(300);
}
- await DLService.OffertUpdateCost(OfferID);
+ await OService.UpdateCostAsync(OfferID);
if (forceResetCalc)
{
await Task.Delay(300);
diff --git a/Lux.UI/Components/Compo/OrderRowMan.razor.cs b/Lux.UI/Components/Compo/OrderRowMan.razor.cs
index 30852b84..59db4788 100644
--- a/Lux.UI/Components/Compo/OrderRowMan.razor.cs
+++ b/Lux.UI/Components/Compo/OrderRowMan.razor.cs
@@ -820,7 +820,7 @@ namespace Lux.UI.Components.Compo
await InvokeAsync(StateHasChanged);
await Task.Delay(300);
}
- await DLService.OffertUpdateCost(OrderID);
+ await DLService.OrderUpdateCost(OrderID);
if (forceResetCalc)
{
await Task.Delay(300);
diff --git a/Lux.UI/Components/Compo/Templates/TemplateRowList.razor.cs b/Lux.UI/Components/Compo/Templates/TemplateRowList.razor.cs
index e9568fec..3fa00eff 100644
--- a/Lux.UI/Components/Compo/Templates/TemplateRowList.razor.cs
+++ b/Lux.UI/Components/Compo/Templates/TemplateRowList.razor.cs
@@ -311,6 +311,9 @@ namespace Lux.UI.Components.Compo.Templates
[Inject]
private ITemplateRowService TRService { get; set; } = default!;
+ [Inject]
+ private ITemplateService TplService { get; set; } = default!;
+
#endregion Private Properties
#region Private Methods
@@ -612,7 +615,7 @@ namespace Lux.UI.Components.Compo.Templates
await InvokeAsync(StateHasChanged);
await Task.Delay(300);
}
- await DLService.OffertUpdateCost(TemplateID);
+ await TplService.UpdateCostAsync(TemplateID);
if (forceResetCalc)
{
await Task.Delay(300);
diff --git a/Lux.UI/Components/Pages/Offers.razor.cs b/Lux.UI/Components/Pages/Offers.razor.cs
index 70b36fb4..34ad3b56 100644
--- a/Lux.UI/Components/Pages/Offers.razor.cs
+++ b/Lux.UI/Components/Pages/Offers.razor.cs
@@ -5,6 +5,7 @@ using EgwCoreLib.Lux.Data.DbModel.Config;
using EgwCoreLib.Lux.Data.DbModel.Sales;
using EgwCoreLib.Lux.Data.Services;
using EgwCoreLib.Lux.Data.Services.Config;
+using EgwCoreLib.Lux.Data.Services.Sales;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using static EgwCoreLib.Lux.Core.Enums;
@@ -69,6 +70,10 @@ namespace Lux.UI.Components.Pages
[Inject]
protected IJSRuntime JSRuntime { get; set; } = null!;
+ [Inject]
+ private IOfferService OService { get; set; } = default!;
+
+
[Inject]
protected ProdService PService { get; set; } = null!;
@@ -106,7 +111,7 @@ namespace Lux.UI.Components.Pages
if (!await JSRuntime.InvokeAsync("confirm", $"Confermi di voler duplicare interamente l'offerta selezionata?{Environment.NewLine}---------------------------{Environment.NewLine}Env: {rec2clone.Envir} | {rec2clone.OfferCode}{Environment.NewLine}{rec2clone.Description}{Environment.NewLine}Articoli: {rec2clone.NumItems}{Environment.NewLine}---------------------------{Environment.NewLine}Importo: {rec2clone.TotalPrice:C2}"))
return;
// clona intera offerta + tutte le righe...
- await DLService.OfferClone(rec2clone);
+ await OService.CloneAsync(rec2clone);
await ReloadData();
UpdateTable();
}
@@ -124,7 +129,7 @@ namespace Lux.UI.Components.Pages
if (!await JSRuntime.InvokeAsync("confirm", $"Vuoi uscire salvando?"))
return;
// salvo record
- await DLService.OffertUpsert(updRec);
+ await OService.UpsertAsync(updRec);
}
protected void DoSelect(OfferModel curRec)
@@ -277,7 +282,7 @@ namespace Lux.UI.Components.Pages
}
currRec.OffertState = newStatus;
- await DLService.OffertUpsert(currRec);
+ await OService.UpsertAsync(currRec);
await ReloadData();
UpdateTable();
}
@@ -392,7 +397,7 @@ namespace Lux.UI.Components.Pages
private async Task DoSave(OfferModel updRec)
{
// salvo record
- await DLService.OffertUpsert(updRec);
+ await OService.UpsertAsync(updRec);
// cambio step
CompileStep nextStep = currStep < CompileStep.FinalCheck ? currStep + 1 : currStep;
AdvStep(nextStep);
@@ -443,8 +448,7 @@ namespace Lux.UI.Components.Pages
private async Task ReloadData()
{
await DLService.OffersCheckExpired();
- //AllRecordsRedis = await GVService.OfferGetAll();
- AllRecords = await DLService.OfferGetFilt(PeriodoSel.Inizio, PeriodoSel.Fine);
+ AllRecords = await OService.GetFiltAsync(PeriodoSel.Inizio, PeriodoSel.Fine);
DoFilter();
}
diff --git a/Lux.UI/Lux.UI.csproj b/Lux.UI/Lux.UI.csproj
index 82971e4b..9bef2b45 100644
--- a/Lux.UI/Lux.UI.csproj
+++ b/Lux.UI/Lux.UI.csproj
@@ -5,7 +5,7 @@
enable
enable
aspnet-Lux.UI-a758c101-a2f4-4e38-977d-1c4887dbbd50
- 1.1.2603.1715
+ 1.1.2603.1716
diff --git a/Lux.UI/Program.cs b/Lux.UI/Program.cs
index cd5cfffd..ef7c5ffe 100644
--- a/Lux.UI/Program.cs
+++ b/Lux.UI/Program.cs
@@ -3,12 +3,14 @@ using EgwCoreLib.Lux.Data.Repository.Config;
using EgwCoreLib.Lux.Data.Repository.Cost;
using EgwCoreLib.Lux.Data.Repository.Items;
using EgwCoreLib.Lux.Data.Repository.Job;
+using EgwCoreLib.Lux.Data.Repository.Sales;
using EgwCoreLib.Lux.Data.Repository.Utils;
using EgwCoreLib.Lux.Data.Services;
using EgwCoreLib.Lux.Data.Services.Config;
using EgwCoreLib.Lux.Data.Services.Cost;
using EgwCoreLib.Lux.Data.Services.Items;
using EgwCoreLib.Lux.Data.Services.Job;
+using EgwCoreLib.Lux.Data.Services.Sales;
using EgwCoreLib.Lux.Data.Services.Utils;
using Lux.UI.Components;
using Lux.UI.Components.Account;
@@ -218,6 +220,7 @@ builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
+builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
@@ -233,6 +236,7 @@ builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
+builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
diff --git a/Resources/ChangeLog.html b/Resources/ChangeLog.html
index b8581f91..9e391ab0 100644
--- a/Resources/ChangeLog.html
+++ b/Resources/ChangeLog.html
@@ -1,6 +1,6 @@
LUX - Web Windows MES
- Versione: 1.1.2603.1715
+ Versione: 1.1.2603.1716
Note di rilascio:
-
diff --git a/Resources/VersNum.txt b/Resources/VersNum.txt
index 9222caa1..a8f1f93c 100644
--- a/Resources/VersNum.txt
+++ b/Resources/VersNum.txt
@@ -1 +1 @@
-1.1.2603.1715
+1.1.2603.1716
diff --git a/Resources/manifest.xml b/Resources/manifest.xml
index 23f5bd52..720d6002 100644
--- a/Resources/manifest.xml
+++ b/Resources/manifest.xml
@@ -1,6 +1,6 @@
-
- 1.1.2603.1715
+ 1.1.2603.1716
http://nexus.steamware.net/repository/SWS/GPW/stable/GPW.UI.zip
http://nexus.steamware.net/repository/SWS/GPW/stable/ChangeLog.html
false