Merge branch 'release/UpdateImgDisplay'

This commit is contained in:
Samuele Locatelli
2025-10-20 12:19:25 +02:00
118 changed files with 11699 additions and 6041 deletions
@@ -21,6 +21,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="EgwMultiEngineManager.Data" Version="2.7.8.5" />
<PackageReference Include="Egw.Lux.WebWindowComplex" Version="2.7.10.1710" />
<PackageReference Include="Egw.Window.Data" Version="2.7.10.1012" />
<PackageReference Include="EgwMultiEngineManager.Data" Version="2.7.10.1" />
</ItemGroup>
</Project>
+89
View File
@@ -0,0 +1,89 @@
using Newtonsoft.Json;
namespace EgwCoreLib.Lux.Core
{
/// <summary>
/// Generico dizionario parametri con funzione ricerca valore (SE presente)
/// </summary>
public class ParamDict
{
#region Public Constructors
/// <summary>
/// init classe dal valore serializzato del dizionario
/// </summary>
/// <param name="rawVal"></param>
public ParamDict(string rawVal)
{
DictVals = JsonConvert.DeserializeObject<Dictionary<string, string>>(rawVal) ?? new Dictionary<string, string>();
}
/// <summary>
/// init classe da dizionario
/// </summary>
/// <param name="newDict"></param>
public ParamDict(Dictionary<string, string> newDict)
{
DictVals = newDict;
}
#endregion Public Constructors
#region Public Properties
/// <summary>
/// Versione serializzata del dizionario
/// </summary>
public string Serialized
{
get => JsonConvert.SerializeObject(DictVals);
}
#endregion Public Properties
#region Public Methods
/// <summary>
/// Ricerca (se disponibile) il valore della chiave richiesta
/// </summary>
/// <param name="reqKey"></param>
/// <returns></returns>
public string GetVal(string reqKey)
{
string answ = "";
if (DictVals.ContainsKey(reqKey))
{
answ = DictVals[reqKey];
}
return answ;
}
/// <summary>
/// Imposta valore (aggiungendo se mancasse)
/// </summary>
/// <param name="Key"></param>
/// <param name="Val"></param>
public void SetVal(string Key, string Val)
{
if (DictVals.ContainsKey(Key))
{
DictVals[Key] = Val;
}
else
{
DictVals.Add(Key, Val);
}
}
#endregion Public Methods
#region Private Properties
/// <summary>
/// Dizionario interno valori
/// </summary>
private Dictionary<string, string> DictVals { get; set; } = new Dictionary<string, string>();
#endregion Private Properties
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
using EgwCoreLib.Lux.Data.DbModel;
using EgwCoreLib.Lux.Data.Data.DbModel.Admin;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using System;
File diff suppressed because it is too large Load Diff
+39 -6
View File
@@ -1,4 +1,11 @@
using EgwCoreLib.Lux.Data.DbModel;
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.Stock;
using EgwCoreLib.Lux.Data.DbModel.Task;
using EgwCoreLib.Lux.Data.DbModel.Utils;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using NLog;
@@ -39,12 +46,16 @@ namespace EgwCoreLib.Lux.Data
}
}
public virtual DbSet<CounterModel> DbSetCounters { get; set; }
public virtual DbSet<GlassModel> DbSetConfGlass { get; set; }
public virtual DbSet<ProfileModel> DbSetConfProfile { get; set; }
public virtual DbSet<WoodModel> DbSetConfWood { get; set; }
public virtual DbSet<EnvirParamModel> DbSetEnvirPar { get; set; }
public virtual DbSet<ItemGroupModel> DbSetItemGroup { get; set; }
public virtual DbSet<ItemModel> DbSetItem { get; set; }
public virtual DbSet<SellingItemModel> DbSetSellItem { get; set; }
public virtual DbSet<TagsModel> DbSetRole { get; set; }
public virtual DbSet<TagsModel> DbSetTags { get; set; }
public virtual DbSet<CustomerModel> DbSetCustomer { get; set; }
public virtual DbSet<DealerModel> DbSetDealer { get; set; }
public virtual DbSet<SupplierModel> DbSetSupplier { get; set; }
@@ -55,14 +66,18 @@ namespace EgwCoreLib.Lux.Data
public virtual DbSet<ResourceModel> DbSetResource { get; set; }
public virtual DbSet<PhaseModel> DbSetPhase { get; set; }
public virtual DbSet<JobModel> DbSetJob { get; set; }
public virtual DbSet<JobRowModel> DbSetJobRow { get; set; }
public virtual DbSet<JobRowItemModel> DbSetJobRowItem { get; set; }
public virtual DbSet<JobStepModel> DbSetJobRow { get; set; }
public virtual DbSet<JobStepItemModel> DbSetJobRowItem { get; set; }
public virtual DbSet<ProductionBatchModel> DbSetProdBatch { get; set; }
public virtual DbSet<ProductionItemModel> DbSetProdItem { get; set; }
public virtual DbSet<ProductionItemRowModel> DbSetProdItemRow { get; set; }
public virtual DbSet<ProductionItemStepModel> DbSetProdItemRow { get; set; }
public virtual DbSet<StockStatusModel> DbSetStockStatus { get; set; }
public virtual DbSet<MovTypeModel> DbSetMovType { get; set; }
public virtual DbSet<StockMovModel> DbSetStockMov { get; set; }
public virtual DbSet<GenClassModel> DbSetGenClass { get; set; }
public virtual DbSet<GenValueModel> DbSetGenVal { get; set; }
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
@@ -73,12 +88,23 @@ namespace EgwCoreLib.Lux.Data
string connString = DbConfig.CONNECTION_STRING;
if (string.IsNullOrEmpty(connString))
{
#if DEBUG
connString = "Server=mdb.ufficio;port=3306;database=Lux_000;uid=lux_user;pwd=Egal_pwd!;sslmode=None;";
//connString = "Server=mdb.ufficio;port=3306;database=Lux_000_dev;uid=lux_user;pwd=Egal_pwd!;sslmode=None;";
#else
connString = "Server=mdb.ufficio;port=3306;database=Lux_000;uid=lux_user;pwd=Egal_pwd!;sslmode=None;";
#endif
}
if (!optionsBuilder.IsConfigured)
{
var serverVersion = ServerVersion.AutoDetect(connString);
optionsBuilder.UseMySql(connString, serverVersion);
// verificare setup componente
#if false
optionsBuilder
.UseMySql(connString, serverVersion)
.UseSnakeCaseNamingConvention(); // via EFCore.NamingConventions
#endif
}
}
@@ -93,6 +119,13 @@ namespace EgwCoreLib.Lux.Data
modelBuilder.Entity<CounterModel>()
.HasKey(c => new { c.RefYear, c.CountName});
modelBuilder.Entity<GlassModel>()
.HasKey(c => new { c.GlassID });
modelBuilder.Entity<ProfileModel>()
.HasKey(c => new { c.ProfileID });
modelBuilder.Entity<WoodModel>()
.HasKey(c => new { c.WoodID });
// fix valori timestamp
modelBuilder.Entity<StockMovModel>(entity =>
{
+5 -1
View File
@@ -74,8 +74,12 @@ namespace EgwCoreLib.Lux.Data
DATABASE_NAME = $"Lux_{nKey}";
DATABASE_USER = $"user_{nKey}";
DATABASE_PWD = $"pwd_{sKey}";
CONNECTION_STRING = $"server={DATABASE_SERV};port=3306;database={DATABASE_NAME};uid={DATABASE_USER};pwd={DATABASE_PWD};sslmode=None";
// stringa admin con utente root egalware...
#if DEBUG
CONNECTION_STRING = $"server={DATABASE_SERV};port=3306;database={DATABASE_NAME}_dev;uid={DATABASE_USER};pwd={DATABASE_PWD};sslmode=None";
#else
CONNECTION_STRING = $"server={DATABASE_SERV};port=3306;database={DATABASE_NAME};uid={DATABASE_USER};pwd={DATABASE_PWD};sslmode=None";
#endif
ADMIN_CONNECTION_STRING = $"server={DATABASE_SERV};port=3306;database=mysql;uid=root;pwd=Egalware_24068!;sslmode=None";
}
@@ -5,7 +5,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EgwCoreLib.Lux.Data.DbModel
namespace EgwCoreLib.Lux.Data.Data.DbModel.Admin
{
/// <summary>
/// Tabella dei USER di MySql
@@ -0,0 +1,26 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
namespace EgwCoreLib.Lux.Data.DbModel.Config
{
/// <summary>
/// Risorsa tipo di Glass x EF
/// </summary>
[Table("conf_envir")]
public class EnvirParamModel
{
/// <summary>
/// ID / Environment definito
/// </summary>
[Key]
public EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS EnvirID { get; set; } = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW;
/// <summary>
/// Chiave da impiegare nel dizionario x inviare la struttura serializzata da calcolare
/// </summary>
public string SerStrucKey { get; set; } = "SerStr";
}
}
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
namespace EgwCoreLib.Lux.Data.DbModel.Config
{
/// <summary>
/// Risorsa tipo di Glass x EF
/// </summary>
[Table("conf_glass")]
public class GlassModel : Egw.Window.Data.Glass
{
}
}
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
namespace EgwCoreLib.Lux.Data.DbModel.Config
{
/// <summary>
/// Risorsa tipo di Hardware x EF
/// </summary>
[Table("conf_Hardware")]
public class HardwareModel : Egw.Window.Data.Hardware
{
public HardwareModel(string Id, string FamilyName, string Description, Egw.Window.Data.Enums.OpeningTypes OpeningType, string Shape, int SashQty, int SashPosition) : base(Id, FamilyName, Description, OpeningType, Shape, SashQty, SashPosition)
{
}
}
}
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
namespace EgwCoreLib.Lux.Data.DbModel.Config
{
/// <summary>
/// Risorsa tipo di Wood x EF
/// </summary>
[Table("conf_profile")]
public class ProfileModel : Egw.Window.Data.Profile
{
}
}
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
namespace EgwCoreLib.Lux.Data.DbModel.Config
{
/// <summary>
/// Risorsa tipo di Wood x EF
/// </summary>
[Table("conf_wood")]
public class WoodModel : Egw.Window.Data.Wood
{
}
}
@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
namespace EgwCoreLib.Lux.Data.DbModel.Cost
{
/// <summary>
/// Rappresenta la definizione di un Driver Risorsa da convertire in WorkOur (driver di ResourceModel)
/// </summary>
[Table("cost_driver")]
public class CostDriverModel
{
[Key]
public int CostDriverID { get; set; }
/// <summary>
/// Nome driver
/// e.g. "WorkHour"
/// </summary>
public string Name { get; set; } = "";
/// <summary>
/// Nome dell'UM
/// e.g. "hour", "meter"
/// </summary>
public string Unit { get; set; } = "";
/// <summary>
/// Descrizione driver
/// e.g. "WorkHour"
/// </summary>
public string Descript { get; set; } = "";
}
}
@@ -0,0 +1,149 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
namespace EgwCoreLib.Lux.Data.DbModel.Cost
{
/// <summary>
/// Risorsa / Centro di Costo, con costi / Hourly
/// </summary>
[Table("cost_resource")]
public class ResourceModel
{
/// <summary>
/// ID del record
/// </summary>
[Key]
public int ResourceID { get; set; }
/// <summary>
/// Nome/Descrizione
/// </summary>
public string Name { get; set; } = string.Empty;
/// <summary>
/// ID del driver di costo impiegato, tipicamente 1 = WorkHour
/// </summary>
public int CostDriverID { get; set; }
/// <summary>
/// Valore di riferimento del CostDriver per il calcolo dell'importo unitario della risorsa
/// Se è basato su WorkHour diventa il budget annuale della risorsa disponibile
/// Tipicamente le Ore di impiego a buget risorsa, es 220gg x 8h
/// </summary>
public decimal CostDriverBudget { get; set; } = 1;
/// <summary>
/// Costo totale (rif al CostDriverBudget) componente FIXED (macchinari) della risorsa (ove applicabile), comprendendo
/// - ammortamenti
/// - costi di manutenzione ordinaria
/// - costi di manutenzione straordinaria (se stimabili, in periodo post ammortamento)
/// </summary>
public decimal FixedCost { get; set; } = 0;
/// <summary>
/// Costo totale (rif al CostDriverBudget) componente variabile (tipicamente Energia)
/// nb: stima basata sul (costo medio energia aziendale) * consumo effettivo (se disponibile), altrimenti (stima potenza media impiegata) * (ore di impiego stimate)
/// </summary>
public decimal VariableCost { get; set; } = 0;
/// <summary>
/// Costo della componente HR sulla gestione impianto (rif al CostDriverBudget)
/// </summary>
public decimal LaborCost { get; set; } = 0;
/// <summary>
/// Costi di OverHead (rif al CostDriverBudget) da ribaltare su risorsa (tipicamente struttura)
/// </summary>
public decimal OverHeadCost { get; set; } = 0;
/// <summary>
/// Costo di overhead (come % on top del resto)
/// </summary>
public decimal OverHeadPerc { get; set; } = 0.15M;
/// <summary>
/// EBT ovvero marginalità minima garantita on top del resto dei costi e OH
/// </summary>
public decimal EBTPerc { get; set; } = 0.1M;
/// <summary>
/// Margine sul prezzo ovvero valore da potersi eventualmente scontare
/// </summary>
public decimal PriceMargin { get; set; } = 0.2M;
/// <summary>
/// Costo Netto la risorsa su base CostDriver
/// </summary>
[NotMapped]
private decimal BaseNetCost
{
get => CostDriverBudget == 0 ? 0 : (FixedCost + VariableCost + OverHeadCost) / CostDriverBudget;
}
/// <summary>
/// Costo RockBottom Risorsa su base CostDriver
/// </summary>
[NotMapped]
public decimal BaseRockBottomCost
{
get => BaseNetCost * (1 + OverHeadPerc) * (1 + EBTPerc);
}
/// <summary>
/// Prezzo comprensivo di margine di ricarico massimo scontabile (sul RockBottom)
/// </summary>
[NotMapped]
public decimal BasePrice
{
get => BaseRockBottomCost * (1 + PriceMargin);
}
/// <summary>
/// Navigazione Driver costo
/// </summary>
[ForeignKey("CostDriverID")]
public virtual CostDriverModel DriverNav { get; set; } = null!;
#if false
/// <summary>
/// Indica gli asset/cespiti
/// </summary>
public bool IsAsset { get; set; } = false;
/// <summary>
/// Indica che è un operatore umano
/// </summary>
public bool IsHuman { get; set; } = false;
/// <summary>
/// Costo fisso risorsa per UM tipo ammortamento
/// </summary>
public double UnitCostFix { get; set; } = 0;
/// <summary>
/// Unità di misura della risorsa tipo fisso/ammortamento
/// </summary>
public string UmFix { get; set; } = "";
/// <summary>
/// Costo unitario risorsa per UM tipo consumabili
/// </summary>
public double UnitCostProp { get; set; } = 0;
/// <summary>
/// Unità di misura della risorsa
/// </summary>
public string UmProp { get; set; } = string.Empty;
#endif
}
}
@@ -6,12 +6,12 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EgwCoreLib.Lux.Data.DbModel
namespace EgwCoreLib.Lux.Data.DbModel.Items
{ // <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
[Table("RegItemGroup")]
[Table("item_group")]
public class ItemGroupModel
{
@@ -2,13 +2,13 @@
using System.ComponentModel.DataAnnotations.Schema;
using static EgwCoreLib.Lux.Core.Enums;
namespace EgwCoreLib.Lux.Data.DbModel
namespace EgwCoreLib.Lux.Data.DbModel.Items
{
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
[Table("RegItem")]
[Table("item_item")]
public class ItemModel
{
/// <summary>
@@ -85,7 +85,7 @@ namespace EgwCoreLib.Lux.Data.DbModel
[NotMapped]
public bool IsOkQty
{
get => QtyMax > 0 && (QtyMax - QtyMin) > 0;
get => QtyMax > 0 && QtyMax - QtyMin > 0;
}
/// <summary>
@@ -117,7 +117,7 @@ namespace EgwCoreLib.Lux.Data.DbModel
[NotMapped]
public bool IsProtected
{
get => this.ItemType == EgwCoreLib.Lux.Core.Enums.ItemClassType.Bom;
get => ItemType == ItemClassType.Bom;
}
/// <summary>
@@ -1,13 +1,14 @@
using System.ComponentModel.DataAnnotations;
using EgwCoreLib.Lux.Data.DbModel.Task;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EgwCoreLib.Lux.Data.DbModel
namespace EgwCoreLib.Lux.Data.DbModel.Items
{
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
[Table("SellingItem")]
[Table("item_selling_item")]
public class SellingItemModel
{
/// <summary>
@@ -16,6 +17,11 @@ namespace EgwCoreLib.Lux.Data.DbModel
[Key]
public int SellingItemID { get; set; }
/// <summary>
/// Environment del selling item
/// </summary>
public EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS Envir { get; set; } = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW;
/// <summary>
/// Definisce l'articolo come servizio vs concreto=materiale
/// </summary>
@@ -67,9 +73,10 @@ namespace EgwCoreLib.Lux.Data.DbModel
public string SerStruct { get; set; } = "";
/// <summary>
/// Json contenente la serializzazione delle fasi previste per la stima dei tempi e costi in formato SerializedPhasePreview; potrebbe contenere anche altre info accessorie x definire dati logistico/gestionali
/// Elenco StepDTO (Fasi) per la stima tempi / costi
/// potrebbe contenere anche altre info accessorie x definire dati logistico/gestionali
/// </summary>
public string ItemSPP { get; set; } = "";
public string ItemSteps { get; set; } = "";
/// <summary>
/// Navigazione Job/Cicli
@@ -6,13 +6,13 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EgwCoreLib.Lux.Data.DbModel
namespace EgwCoreLib.Lux.Data.DbModel.Items
{
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
[Table("RegSupplier")]
[Table("item_supplier")]
public class SupplierModel
{
/// <summary>
-26
View File
@@ -1,26 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EgwCoreLib.Lux.Data.DbModel
{
[Table("JobList")]
public class JobModel
{
/// <summary>
/// ID del record
/// </summary>
[Key]
public int JobID { get; set; }
/// <summary>
/// Descrizione del ciclo
/// </summary>
public string Description { get; set; } = "";
}
}
@@ -1,71 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
namespace EgwCoreLib.Lux.Data.DbModel
{
[Table("JobRowList")]
public class JobRowModel
{
/// <summary>
/// ID del record
/// </summary>
[Key]
public int JobRowID { get; set; }
/// <summary>
/// Ciclo di appartenenza
/// </summary>
public int JobID { get; set; }
/// <summary>
/// Indice della fase all'interno del Job
/// </summary>
public int Index { get; set; } = 0;
/// <summary>
/// ID della fase realizzata
/// </summary>
public int PhaseID { get; set; }
/// <summary>
/// ID dellaa risorsa impiegata
/// </summary>
public int ResourceID { get; set; }
/// <summary>
/// Descrizione della fase del Job
/// </summary>
public string Description { get; set; } = "";
/// <summary>
/// Margine percentuale standard
/// </summary>
public double Qty { get; set; } = 1;
/// <summary>
/// Navigazione Job/Cicli
/// </summary>
[ForeignKey("JobID")]
public virtual JobModel JobNav { get; set; } = null!;
/// <summary>
/// Navigazione Job/Cicli
/// </summary>
[ForeignKey("PhaseID")]
public virtual PhaseModel PhaseNav { get; set; } = null!;
/// <summary>
/// Navigazione Job/Cicli
/// </summary>
[ForeignKey("ResourceID")]
public virtual ResourceModel ResourceNav { get; set; } = null!;
}
}
@@ -1,136 +0,0 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EgwCoreLib.Lux.Data.DbModel
{
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
[Table("OfferRowList")]
public class OfferRowModel
{
/// <summary>
/// ID del record
/// </summary>
[Key]
public int OfferRowID { get; set; }
/// <summary>
/// Riferimento offerta
/// </summary>
public int OfferID { get; set; }
/// <summary>
/// Riga Offerta (per ordinamento)
/// </summary>
public int RowNum { get; set; } = 0;
/// <summary>
/// Campo salvato dell'Environment
/// </summary>
public string Environment { get; set; } = "WINDOW";
/// <summary>
/// Campo salvato dell'UID da codice DataMatrix calcolato
/// </summary>
public string OfferRowUID { get; set; } = "OFF00001230AZ";
/// <summary>
/// Codice calcolato offerta ANNO.ID_RIGA_OFFERTA (0...Z come Dtx)
/// </summary>
[NotMapped]
public string OfferRowDtx
{
get => $"OFF{Inserted.Year:yy}{OfferRowID:000000000}";
}
/// <summary>
/// ID dell'articolo di vendita offerto
/// </summary>
public int SellingItemID { get; set; }
/// <summary>
/// Costo (standard / senza listini)
/// </summary>
public double Cost { get; set; } = 0;
/// <summary>
/// Margine percentuale standard
/// </summary>
public double Qty { get; set; } = 1;
[NotMapped]
public double TotalCost
{
get => Qty * Cost;
}
/// <summary>
/// Valore serializzato della composizione articolo (in formato JWD x finestra)
/// </summary>
public string SerStruct { get; set; } = "";
/// <summary>
/// Json contenente la serializzazione delle fasi previste per la stima dei tempi e costi in formato SerializedPhasePreview; potrebbe contenere anche altre info accessorie x definire dati logistico/gestionali
/// </summary>
public string ItemSPP { get; set; } = "";
/// <summary>
/// BOM serializzata per la produzione dell'item
/// </summary>
public string ItemBOM { get; set; } = "";
/// <summary>
/// Validazione dati BOM (Inteso come gruppi tutti trovati/esistenti)
/// </summary>
public bool BomOk { get; set; } = false;
/// <summary>
/// Validazione livello item per Costo e range dimensione
/// </summary>
public bool ItemOk { get; set; } = false;
/// <summary>
/// Note libere
/// </summary>
public string Note { get; set; } = "";
#if false
/// <summary>
/// Stack degli ultimi item serializzati per Uno/Redo actions
/// </summary>
[NotMapped]
public List<string> UndoRedoSerStruct { get; set; } = new List<string>();
#endif
/// <summary>
/// DataOra inserimento
/// </summary>
public DateTime Inserted { get; set; } = DateTime.Now;
/// <summary>
/// DataOra ultima modifica
/// </summary>
public DateTime Modified { get; set; } = DateTime.Now;
/// <summary>
/// Navigazione Offer
/// </summary>
[ForeignKey("OfferID")]
public virtual OfferModel OfferNav { get; set; } = null!;
/// <summary>
/// Navigazione Item
/// </summary>
[ForeignKey("SellingItemID")]
public virtual SellingItemModel SellingItemNav { get; set; } = null!;
}
}
@@ -1,13 +1,13 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EgwCoreLib.Lux.Data.DbModel
namespace EgwCoreLib.Lux.Data.DbModel.Production
{
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
[Table("ProductionBatch")]
[Table("production_batch")]
public class ProductionBatchModel
{
/// <summary>
@@ -1,13 +1,14 @@
using System.ComponentModel.DataAnnotations;
using EgwCoreLib.Lux.Data.DbModel.Sales;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EgwCoreLib.Lux.Data.DbModel
namespace EgwCoreLib.Lux.Data.DbModel.Production
{
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
[Table("ProductionItem")]
[Table("production_item")]
public class ProductionItemModel
{
/// <summary>
@@ -1,4 +1,6 @@
using System;
using EgwCoreLib.Lux.Data.DbModel.Cost;
using EgwCoreLib.Lux.Data.DbModel.Task;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
@@ -9,19 +11,19 @@ using System.Threading.Tasks;
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
namespace EgwCoreLib.Lux.Data.DbModel
namespace EgwCoreLib.Lux.Data.DbModel.Production
{
/// <summary>
/// Tabella delel effettiva righe della fgasi di lavorazione x ogni item da produrre
/// Tabella delle fasi di lavorazione x ogni item da produrre
/// </summary>
[Table("ProductionItemRowList")]
public class ProductionItemRowModel
[Table("production_item_step")]
public class ProductionItemStepModel
{
/// <summary>
/// ID del record
/// </summary>
[Key]
public int ProdItemRowID { get; set; }
public int ProdItemStepID { get; set; }
/// <summary>
/// Item di appartenenza
@@ -1,59 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
namespace EgwCoreLib.Lux.Data.DbModel
{
[Table("RegResource")]
public class ResourceModel
{
/// <summary>
/// ID del record
/// </summary>
[Key]
public int ResourceID { get; set; }
/// <summary>
/// Descrizione
/// </summary>
public string Description { get; set; } = "";
/// <summary>
/// Indica gli assec/cespiti
/// </summary>
public bool IsAsset { get; set; } = false;
/// <summary>
/// Indica che è un operatore umano
/// </summary>
public bool IsHuman { get; set; } = false;
/// <summary>
/// Costo fisso risorsa per UM tipo ammortamento
/// </summary>
public double UnitCostFix { get; set; } = 0;
/// <summary>
/// Unità di misura della risorsa tipo fisso/ammortamento
/// </summary>
public string UmFix { get; set; } = "";
/// <summary>
/// Costo unitario risorsa per UM tipo consumabili
/// </summary>
public double UnitCostProp { get; set; } = 0;
/// <summary>
/// Unità di misura della risorsa
/// </summary>
public string UmProp { get; set; } = "";
}
}
@@ -6,13 +6,13 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EgwCoreLib.Lux.Data.DbModel
namespace EgwCoreLib.Lux.Data.DbModel.Sales
{
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
[Table("RegCustomer")]
[Table("sales_customer")]
public class CustomerModel
{
/// <summary>
@@ -6,13 +6,13 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EgwCoreLib.Lux.Data.DbModel
namespace EgwCoreLib.Lux.Data.DbModel.Sales
{
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
[Table("RegDealer")]
[Table("sales_dealer")]
public class DealerModel
{
/// <summary>
@@ -1,20 +1,14 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static EgwCoreLib.Lux.Core.Enums;
namespace EgwCoreLib.Lux.Data.DbModel
namespace EgwCoreLib.Lux.Data.DbModel.Sales
{
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
[Table("Offer")]
[Table("sales_offer")]
public class OfferModel
{
/// <summary>
@@ -23,6 +17,11 @@ namespace EgwCoreLib.Lux.Data.DbModel
[Key]
public int OfferID { get; set; }
/// <summary>
/// Environment della richiesta
/// </summary>
public EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS Envir { get; set; } = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW;
/// <summary>
/// Anno rif offerta
/// </summary>
@@ -40,11 +39,12 @@ namespace EgwCoreLib.Lux.Data.DbModel
/// <summary>
/// Codice calcolato offerta ANNO.NUMERO.REV
/// inizia per SO = SalesOffer
/// </summary>
[NotMapped]
public string OfferCode
{
get => $"{RefYear:00}.{RefNum:00000}.{RefRev:00}";
get => $"SO.{RefYear:00}.{RefNum:000000}.{RefRev:000}";
}
/// <summary>
@@ -62,6 +62,16 @@ namespace EgwCoreLib.Lux.Data.DbModel
/// </summary>
public int DealerID { get; set; }
/// <summary>
/// Dizionario serializzato delle preselezioni (opzionale)
/// </summary>
public string DictPresel { get; set; } = "";
/// <summary>
/// note di consegna (opzionali)
/// </summary>
public string ConsNote { get; set; } = "";
/// <summary>
/// Validità offerta
/// </summary>
@@ -77,30 +87,61 @@ namespace EgwCoreLib.Lux.Data.DbModel
/// </summary>
public DateTime Modified { get; set; } = DateTime.Now;
/// <summary>
/// Enum stato offerta
/// </summary>
public OfferStates OffertState { get; set; } = OfferStates.Open;
/// <summary>
/// Sconto applicato (deve essere < del MAX)
/// </summary>
public double Discount { get; set; } = 0;
/// <summary>
/// Numero Item compresi
/// </summary>
[NotMapped]
public int NumItems
public double NumItems
{
get => OfferRowNav?.Sum(x => x.Qty) ?? 0;
}
/// <summary>
/// Numero Item compresi
/// </summary>
[NotMapped]
public int NumRows
{
get => OfferRowNav?.Count ?? 0;
}
/// <summary>
/// Imposto totale offerta
/// Costo totale offerta (rock bottom)
/// </summary>
[NotMapped]
public double TotalCost
{
get => OfferRowNav?.Sum(x => x.Cost) ?? 0;
get => OfferRowNav?.Sum(x => x.TotalCost) ?? 0;
}
/// <summary>
/// Prezzo totale offerta (compreso di amrginalità)
/// </summary>
[NotMapped]
public double TotalPrice
{
get => OfferRowNav?.Sum(x => x.TotalPrice) ?? 0;
}
/// <summary>
/// Sconto massimo applicabile
/// </summary>
[NotMapped]
public double MaxDiscount
{
get => (TotalCost > 0 && TotalPrice > TotalCost) ? (TotalPrice - TotalCost) / TotalPrice : 0;
}
#if false
/// <summary>
/// ID Ordine (nullo se non c'è ordine)
@@ -126,6 +167,7 @@ namespace EgwCoreLib.Lux.Data.DbModel
[ForeignKey("DealerID")]
public virtual DealerModel DealerNav { get; set; } = null!;
/// <summary>
/// Navigazione alle righe offerta
/// </summary>
@@ -0,0 +1,214 @@
using EgwCoreLib.Lux.Data.DbModel.Items;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
namespace EgwCoreLib.Lux.Data.DbModel.Sales
{
[Table("sales_offer_row")]
public class OfferRowModel
{
/// <summary>
/// ID del record
/// </summary>
[Key]
public int OfferRowID { get; set; }
/// <summary>
/// Riferimento offerta
/// </summary>
public int OfferID { get; set; }
/// <summary>
/// Riga Offerta (per ordinamento)
/// </summary>
public int RowNum { get; set; } = 0;
/// <summary>
/// Environment della richiesta
/// </summary>
public EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS Envir { get; set; } = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW;
/// <summary>
/// Campo salvato dell'UID da codice DataMatrix calcolato
/// inizia per SOR = SalesOfferRow
/// </summary>
public string OfferRowUID { get; set; } = "SOR.25.0123ABCD";
/// <summary>
/// Codice calcolato offerta ANNO.ID_RIGA_OFFERTA in HEX (0xFFFFFFFF)
/// </summary>
[NotMapped]
public string OfferRowDtx
{
get => $"SOR.{Inserted:yy}.{OfferRowID:X8}";
}
/// <summary>
/// ID dell'articolo di vendita offerto
/// </summary>
public int SellingItemID { get; set; }
/// <summary>
/// Quantità della risorsa
/// </summary>
public double Qty { get; set; } = 1;
/// <summary>
/// Costo dei componeti BOM (RockBottom)
/// </summary>
public double BomCost { get; set; } = 0;
/// <summary>
/// Prezzo dei componeti BOM (scontabile)
/// </summary>
public double BomPrice { get; set; } = 0;
/// <summary>
/// Costo produzione Fase/Step (RockBottom)
/// </summary>
public double StepCost { get; set; } = 0;
/// <summary>
/// Prezzo produzione Fase/Step (scontabile)
/// </summary>
public double StepPrice { get; set; } = 0;
/// <summary>
/// Costo Totale Risorsa (BOM + Fase)
/// </summary>
[NotMapped]
public double UnitCost
{
get => BomCost + StepCost;
}
/// <summary>
/// Costo Totale Risorsa (BOM + Fase)
/// </summary>
[NotMapped]
public double UnitPrice
{
get => BomPrice + StepPrice;
}
/// <summary>
/// Sconto massimo applicabile
/// </summary>
[NotMapped]
public double MaxDiscount
{
get => (UnitCost > 0 && UnitPrice > UnitCost) ? (UnitPrice - UnitCost) / UnitPrice : 0;
}
/// <summary>
/// Costo Totale risorsa
/// </summary>
[NotMapped]
public double TotalCost
{
get => UnitCost * Qty;
}
/// <summary>
/// Costo Totale risorsa
/// </summary>
[NotMapped]
public double TotalPrice
{
get => UnitPrice * Qty;
}
/// <summary>
/// Valore serializzato della composizione articolo (in formato JWD x finestra)
/// </summary>
public string SerStruct { get; set; } = "";
/// <summary>
/// Nomi risorsa file associato alla riga offerta (es per BTL)
/// URI come risorsa dentro folder offerta/riga-offerta/guid
/// </summary>
public string FileResource { get; set; } = "";
/// <summary>
/// Nomi file originale associato alla riga offerta (es per BTL)
/// </summary>
public string FileName { get; set; } = "";
/// <summary>
/// Dimensione del file (per visualizzazione rapida)
/// </summary>
public long FileSize { get; set; } = 0;
/// <summary>
/// Elenco StepDTO (Fasi) per la stima tempi / costi
/// potrebbe contenere anche altre info accessorie x definire dati logistico/gestionali
/// </summary>
public string ItemSteps { get; set; } = "";
/// <summary>
/// BOM serializzata per la produzione dell'item
/// </summary>
public string ItemBOM { get; set; } = "";
/// <summary>
/// Validazione dati BOM (Inteso come gruppi tutti trovati/esistenti)
/// </summary>
public bool BomOk { get; set; } = false;
/// <summary>
/// Validazione livello item per Costo e range dimensione
/// </summary>
public bool ItemOk { get; set; } = false;
/// <summary>
/// Note libere
/// </summary>
public string Note { get; set; } = "";
#if false
/// <summary>
/// Stack degli ultimi item serializzati per Uno/Redo actions
/// </summary>
[NotMapped]
public List<string> UndoRedoSerStruct { get; set; } = new List<string>();
#endif
/// <summary>
/// DataOra inserimento
/// </summary>
public DateTime Inserted { get; set; } = DateTime.Now;
/// <summary>
/// DataOra ultima modifica
/// </summary>
public DateTime Modified { get; set; } = DateTime.Now;
/// <summary>
/// Indica che è in attesa aggiornamento BOM
/// </summary>
public bool AwaitBom { get; set; } = false;
/// <summary>
/// Indica che è in attesa aggiornamento Price
/// </summary>
public bool AwaitPrice { get; set; } = false;
/// <summary>
/// Navigazione Offer
/// </summary>
[ForeignKey("OfferID")]
public virtual OfferModel OfferNav { get; set; } = null!;
/// <summary>
/// Navigazione Item
/// </summary>
[ForeignKey("SellingItemID")]
public virtual SellingItemModel SellingItemNav { get; set; } = null!;
}
}
@@ -8,7 +8,7 @@ using System.Text;
using System.Threading.Tasks;
using static EgwCoreLib.Lux.Core.Enums;
namespace EgwCoreLib.Lux.Data.DbModel
namespace EgwCoreLib.Lux.Data.DbModel.Sales
{
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
@@ -17,7 +17,7 @@ namespace EgwCoreLib.Lux.Data.DbModel
/// <summary>
/// Classe degli ordini commerciali
/// </summary>
[Table("Order")]
[Table("sales_order")]
public class OrderModel
{
/// <summary>
@@ -70,6 +70,16 @@ namespace EgwCoreLib.Lux.Data.DbModel
/// </summary>
public int DealerID { get; set; }
/// <summary>
/// note di consegna (opzionali)
/// </summary>
public string ConsNote { get; set; } = "";
/// <summary>
/// Dizionario serializzato delle preselezioni (opzionale)
/// </summary>
public string DictPresel { get; set; } = "";
/// <summary>
/// Validità Ordine
/// </summary>
@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
using EgwCoreLib.Lux.Data.DbModel.Items;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
@@ -7,13 +8,13 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EgwCoreLib.Lux.Data.DbModel
namespace EgwCoreLib.Lux.Data.DbModel.Sales
{
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
[Table("OrderRowList")]
[Table("sales_order_row")]
public class OrderRowModel
{
/// <summary>
@@ -33,12 +34,13 @@ namespace EgwCoreLib.Lux.Data.DbModel
public int RowNum { get; set; } = 0;
/// <summary>
/// Codice calcolato Ordine ANNO.NUMERO.REV
/// Codice calcolato Ordine ANNO.NUMERO
/// inizia per PO = PurchaseOrder
/// </summary>
[NotMapped]
public string OrderRowCode
{
get => $"{OrderRowID:0000}.{RowNum:000}";
get => $"PO{OrderRowID:000000}.{RowNum:000}";
}
/// <summary>
@@ -47,19 +49,74 @@ namespace EgwCoreLib.Lux.Data.DbModel
public int SellingItemID { get; set; }
/// <summary>
/// Costo (standard / senza listini)
/// Costo dei componeti BOM (RockBottom)
/// </summary>
public double Cost { get; set; } = 0;
public double BomCost { get; set; } = 0;
/// <summary>
/// Margine percentuale standard
/// </summary>
public double Qty { get; set; } = 1;
/// <summary>
/// Prezzo dei componeti BOM (scontabile)
/// </summary>
public double BomPrice { get; set; } = 0;
/// <summary>
/// Costo produzione Fase/Step (RockBottom)
/// </summary>
public double StepCost { get; set; } = 0;
/// <summary>
/// Prezzo produzione Fase/Step (scontabile)
/// </summary>
public double StepPrice { get; set; } = 0;
/// <summary>
/// Costo Totale Risorsa (BOM + Fase)
/// </summary>
[NotMapped]
public double UnitCost
{
get => BomCost + StepCost;
}
/// <summary>
/// Costo Totale Risorsa (BOM + Fase)
/// </summary>
[NotMapped]
public double UnitPrice
{
get => BomPrice + StepPrice;
}
/// <summary>
/// Sconto massimo applicabile
/// </summary>
[NotMapped]
public double MaxDiscount
{
get => (UnitCost > 0 && UnitPrice > UnitCost) ? (UnitPrice - UnitCost) / UnitPrice : 0;
}
/// <summary>
/// Costo Totale risorsa
/// </summary>
[NotMapped]
public double TotalCost
{
get => Qty * Cost;
get => UnitCost * Qty;
}
/// <summary>
/// Costo Totale risorsa
/// </summary>
[NotMapped]
public double TotalPrice
{
get => UnitPrice * Qty;
}
/// <summary>
@@ -68,9 +125,10 @@ namespace EgwCoreLib.Lux.Data.DbModel
public string SerStruct { get; set; } = "";
/// <summary>
/// Json contenente la serializzazione delle fasi previste per la stima dei tempi e costi in formato SerializedPhasePreview; potrebbe contenere anche altre info accessorie x definire dati logistico/gestionali
/// Elenco StepDTO (Fasi) per la stima tempi / costi
/// potrebbe contenere anche altre info accessorie x definire dati logistico/gestionali
/// </summary>
public string ItemSPP { get; set; } = "";
public string ItemSteps { get; set; } = "";
/// <summary>
/// Note libere
@@ -1,4 +1,5 @@
using System;
using EgwCoreLib.Lux.Data.DbModel.Utils;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
@@ -6,7 +7,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EgwCoreLib.Lux.Data.DbModel
namespace EgwCoreLib.Lux.Data.DbModel.Stock
{
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
@@ -14,7 +15,7 @@ namespace EgwCoreLib.Lux.Data.DbModel
/// <summary>
/// Tabella dei movimenti degli item in giacenza
/// </summary>
[Table("StockMov")]
[Table("stock_mov")]
public class StockMovModel
{
/// <summary>
@@ -39,7 +40,7 @@ namespace EgwCoreLib.Lux.Data.DbModel
public DateTime DtMod { get; set; } = DateTime.Now;
/// <summary>
/// Qty movimento registrato (delta +/-)
/// ProductivityRate movimento registrato (delta +/-)
/// </summary>
public double QtyRec { get; set; } = 0;
@@ -1,4 +1,5 @@
using System;
using EgwCoreLib.Lux.Data.DbModel.Items;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
@@ -6,7 +7,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EgwCoreLib.Lux.Data.DbModel
namespace EgwCoreLib.Lux.Data.DbModel.Stock
{
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
@@ -15,7 +16,7 @@ namespace EgwCoreLib.Lux.Data.DbModel
/// Tabelal delle giacenze di magazzino
/// </summary>
[Table("StockStatus")]
[Table("stock_status")]
public class StockStatusModel
{
/// <summary>
@@ -30,7 +31,7 @@ namespace EgwCoreLib.Lux.Data.DbModel
public int ItemID { get; set; } = 0;
/// <summary>
/// Qty in giacenza
/// ProductivityRate in giacenza
/// </summary>
public double QtyAvail { get; set; } = 0;
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
namespace EgwCoreLib.Lux.Data.DbModel.Task
{
/// <summary>
/// Definizione macro dei Cicli di Lavoro / Job
/// </summary>
[Table("task_job")]
public class JobModel
{
/// <summary>
/// ID del record
/// </summary>
[Key]
public int JobID { get; set; }
/// <summary>
/// Descrizione del ciclo di lavoro
/// </summary>
public string Description { get; set; } = "";
/// <summary>
/// Navigation verso JobStep
/// </summary>
public virtual ICollection<JobStepModel> JobStepNav { get; set; } = new List<JobStepModel>();
}
}
@@ -1,29 +1,25 @@
using System;
using System.Collections.Generic;
using EgwCoreLib.Lux.Data.DbModel.Items;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
namespace EgwCoreLib.Lux.Data.DbModel
namespace EgwCoreLib.Lux.Data.DbModel.Task
{
[Table("JobRowItemList")]
public class JobRowItemModel
[Table("task_job_step_item")]
public class JobStepItemModel
{
/// <summary>
/// ID del record
/// </summary>
[Key]
public int JobRowItemID { get; set; }
public int JobStepItemID { get; set; }
/// <summary>
/// Fase del Ciclo di appartenenza
/// </summary>
public int JobRowID { get; set; }
public int JobStepID { get; set; }
/// <summary>
/// Indice della fase all'interno del Job
@@ -48,8 +44,8 @@ namespace EgwCoreLib.Lux.Data.DbModel
/// <summary>
/// Navigazione su fasi ciclo
/// </summary>
[ForeignKey("JobRowID")]
public virtual JobRowModel JobRowNav { get; set; } = null!;
[ForeignKey("JobStepID")]
public virtual JobStepModel JobStepNav { get; set; } = null!;
/// <summary>
/// Navigazione su Items
@@ -0,0 +1,116 @@
using EgwCoreLib.Lux.Data.DbModel.Cost;
using EgwCoreLib.Lux.Data.DbModel.Task;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
namespace EgwCoreLib.Lux.Data.DbModel.Task
{
/// <summary>
/// Routing dei cicli di lavoro, con riferimento a risorse e fasi
/// </summary>
[Table("task_job_step")]
public class JobStepModel
{
/// <summary>
/// ID del record
/// </summary>
[Key]
public int JobStepID { get; set; }
/// <summary>
/// Ciclo di appartenenza
/// </summary>
public int JobID { get; set; }
/// <summary>
/// Indice della fase all'interno del Job
/// </summary>
public int Index { get; set; } = 0;
/// <summary>
/// ID del driver di costo impiegato
/// </summary>
public int CostDriverID { get; set; }
/// <summary>
/// ID della fase realizzata
/// </summary>
public int PhaseID { get; set; }
/// <summary>
/// ID della risorsa impiegata
/// </summary>
public int ResourceID { get; set; }
/// <summary>
/// Descrizione della fase del Job
/// </summary>
public string Description { get; set; } = "";
/// <summary>
/// Rapporto produttività tra CostDriver Step e CostDriver Risorsa (es: m/h)
/// </summary>
public decimal ProductivityRate { get; set; } = 1;
/// <summary>
/// Costo RockBottom Fase (step) di produzione
/// sulla base del costo della risorsa, della produttività e della quantità impiegata
/// </summary>
/// <param name="quantity">Quantità risorsa impiegata</param>
/// <returns></returns>
public decimal RockBottomCost(decimal quantity)
{
decimal stepCost = 0;
var unitRequired = quantity / ProductivityRate;
if (ResourceNav != null)
{
stepCost = ResourceNav.BaseRockBottomCost * quantity;
}
return stepCost;
}
/// <summary>
/// Prezzo calcolato per Fase (step) di produzione
/// sulla base del costo RockBottom + marginalità standard
/// </summary>
/// <param name="quantity">Quantità risorsa impiegata</param>
/// <returns></returns>
public decimal RockBottomPrice(decimal quantity)
{
decimal stepCost = 0;
var unitRequired = quantity / ProductivityRate;
if (ResourceNav != null)
{
stepCost = ResourceNav.BasePrice * quantity;
}
return stepCost;
}
/// <summary>
/// Navigazione Job/Cicli
/// </summary>
[ForeignKey("JobID")]
public virtual JobModel JobNav { get; set; } = null!;
/// <summary>
/// Navigazione Driver costo
/// </summary>
[ForeignKey("CostDriverID")]
public virtual CostDriverModel DriverNav { get; set; } = null!;
/// <summary>
/// Navigazione Fasi
/// </summary>
[ForeignKey("PhaseID")]
public virtual PhaseModel PhaseNav { get; set; } = null!;
/// <summary>
/// Navigazione Risorse
/// </summary>
[ForeignKey("ResourceID")]
public virtual ResourceModel ResourceNav { get; set; } = null!;
}
}
@@ -9,10 +9,12 @@ using System.Threading.Tasks;
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
namespace EgwCoreLib.Lux.Data.DbModel
namespace EgwCoreLib.Lux.Data.DbModel.Task
{
[Table("RegPhase")]
/// <summary>
/// Fase di lavorazione / ProductionStage
/// </summary>
[Table("task_phase")]
public class PhaseModel
{
/// <summary>
@@ -25,7 +27,5 @@ namespace EgwCoreLib.Lux.Data.DbModel
/// Descrizione
/// </summary>
public string Description { get; set; } = "";
}
}
@@ -6,13 +6,13 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EgwCoreLib.Lux.Data.DbModel
namespace EgwCoreLib.Lux.Data.DbModel.Utils
{
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
[Table("Counter")]
[Table("utils_counter")]
public class CounterModel
{
/// <summary>
@@ -0,0 +1,38 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
namespace EgwCoreLib.Lux.Data.DbModel.Utils
{
[Table("utils_gen_class")]
public class GenClassModel
{
/// <summary>
/// Cod della classe
/// </summary>
[Key]
public string ClassCod { get; set; } = "";
/// <summary>
/// Descrizione
/// </summary>
public string Description { get; set; } = "";
/// <summary>
/// Numero Item compresi
/// </summary>
[NotMapped]
public int NumChild
{
get => GenValNav?.Count ?? 0;
}
/// <summary>
/// Navigazione alle righe dei valori associati
/// </summary>
public virtual ICollection<GenValueModel> GenValNav { get; set; } = new List<GenValueModel>();
}
}
@@ -0,0 +1,40 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
namespace EgwCoreLib.Lux.Data.DbModel.Utils
{
[Table("utils_gen_value")]
public class GenValueModel
{
/// <summary>
/// ID del record
/// </summary>
[Key]
public int GenValID { get; set; }
/// <summary>
/// Riferimento Classe di appartenenza
/// </summary>
public string ClassCod { get; set; } = "";
/// <summary>
/// Valore ordinale (entro classe)
/// </summary>
public int Ordinal { get; set; } = 0;
/// <summary>
/// Valore String
/// </summary>
public string ValString { get; set; } = "";
/// <summary>
/// Navigazione GenClass
/// </summary>
[ForeignKey("ClassCod")]
public virtual GenClassModel GenClassNav { get; set; } = null!;
}
}
@@ -6,12 +6,12 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EgwCoreLib.Lux.Data.DbModel
namespace EgwCoreLib.Lux.Data.DbModel.Utils
{ // <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
[Table("RegMovType")]
[Table("utils_mov_type")]
public class MovTypeModel
{
@@ -6,13 +6,13 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EgwCoreLib.Lux.Data.DbModel
namespace EgwCoreLib.Lux.Data.DbModel.Utils
{
// <Auto-Generated>
// This is here so CodeMaid doesn't reorganize this document
// </Auto-Generated>
[Table("RegTags")]
[Table("utils_tags")]
public class TagsModel
{
/// <summary>
@@ -17,6 +17,7 @@
</PropertyGroup>
<ItemGroup>
<Compile Remove="DbModel\Cost\Enums.cs" />
<Compile Remove="Services\ExternalMessageProcessor.cs" />
<Compile Remove="Services\RedisSubscriberServiceOld.cs" />
</ItemGroup>
@@ -26,6 +27,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Egw.Lux.WebWindow.Base" Version="2.7.10.1710" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.17" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="8.0.17" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Analyzers" Version="8.0.17" />
@@ -40,7 +42,7 @@
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.7" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="NLog" Version="6.0.1" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.3" />
<PackageReference Include="RestSharp" Version="112.1.0" />
@@ -48,6 +50,7 @@
</ItemGroup>
<ItemGroup>
<Folder Include="DbModel\Engine\" />
<Folder Include="Migrations\" />
</ItemGroup>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,310 +0,0 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace EgwCoreLib.Lux.Data.Migrations
{
/// <inheritdoc />
public partial class AddItemParentRel : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "ItemIDParent",
table: "RegItem",
type: "int",
nullable: false,
defaultValue: 0);
migrationBuilder.UpdateData(
table: "Offer",
keyColumn: "OfferID",
keyValue: 1,
columns: new[] { "Inserted", "Modified", "ValidUntil" },
values: new object[] { new DateTime(2025, 9, 16, 17, 23, 0, 861, DateTimeKind.Local).AddTicks(9069), new DateTime(2025, 9, 16, 17, 23, 0, 861, DateTimeKind.Local).AddTicks(9071), new DateTime(2025, 10, 16, 17, 23, 0, 861, DateTimeKind.Local).AddTicks(9064) });
migrationBuilder.UpdateData(
table: "OfferRowList",
keyColumn: "OfferRowID",
keyValue: 1,
columns: new[] { "Inserted", "Modified" },
values: new object[] { new DateTime(2025, 9, 16, 17, 23, 0, 861, DateTimeKind.Local).AddTicks(9104), new DateTime(2025, 9, 16, 17, 23, 0, 861, DateTimeKind.Local).AddTicks(9106) });
migrationBuilder.UpdateData(
table: "OfferRowList",
keyColumn: "OfferRowID",
keyValue: 2,
columns: new[] { "Inserted", "Modified" },
values: new object[] { new DateTime(2025, 9, 16, 17, 23, 0, 861, DateTimeKind.Local).AddTicks(9114), new DateTime(2025, 9, 16, 17, 23, 0, 861, DateTimeKind.Local).AddTicks(9116) });
migrationBuilder.UpdateData(
table: "OfferRowList",
keyColumn: "OfferRowID",
keyValue: 3,
columns: new[] { "Inserted", "Modified" },
values: new object[] { new DateTime(2025, 9, 16, 17, 23, 0, 861, DateTimeKind.Local).AddTicks(9123), new DateTime(2025, 9, 16, 17, 23, 0, 861, DateTimeKind.Local).AddTicks(9125) });
migrationBuilder.UpdateData(
table: "RegItem",
keyColumn: "ItemID",
keyValue: 1,
column: "ItemIDParent",
value: 0);
migrationBuilder.UpdateData(
table: "RegItem",
keyColumn: "ItemID",
keyValue: 2,
column: "ItemIDParent",
value: 0);
migrationBuilder.UpdateData(
table: "RegItem",
keyColumn: "ItemID",
keyValue: 3,
column: "ItemIDParent",
value: 0);
migrationBuilder.UpdateData(
table: "RegItem",
keyColumn: "ItemID",
keyValue: 4,
column: "ItemIDParent",
value: 0);
migrationBuilder.UpdateData(
table: "RegItem",
keyColumn: "ItemID",
keyValue: 5,
column: "ItemIDParent",
value: 0);
migrationBuilder.UpdateData(
table: "RegItem",
keyColumn: "ItemID",
keyValue: 6,
column: "ItemIDParent",
value: 0);
migrationBuilder.UpdateData(
table: "RegItem",
keyColumn: "ItemID",
keyValue: 7,
column: "ItemIDParent",
value: 0);
migrationBuilder.UpdateData(
table: "RegItem",
keyColumn: "ItemID",
keyValue: 8,
column: "ItemIDParent",
value: 0);
migrationBuilder.UpdateData(
table: "RegItem",
keyColumn: "ItemID",
keyValue: 9,
column: "ItemIDParent",
value: 0);
migrationBuilder.UpdateData(
table: "RegItem",
keyColumn: "ItemID",
keyValue: 10,
column: "ItemIDParent",
value: 0);
migrationBuilder.UpdateData(
table: "RegItem",
keyColumn: "ItemID",
keyValue: 11,
column: "ItemIDParent",
value: 0);
migrationBuilder.UpdateData(
table: "RegItem",
keyColumn: "ItemID",
keyValue: 12,
column: "ItemIDParent",
value: 0);
migrationBuilder.UpdateData(
table: "StockMov",
keyColumn: "StockMovID",
keyValue: 1,
column: "DtCreate",
value: new DateTime(2025, 9, 16, 17, 23, 0, 861, DateTimeKind.Local).AddTicks(8693));
migrationBuilder.UpdateData(
table: "StockMov",
keyColumn: "StockMovID",
keyValue: 2,
column: "DtCreate",
value: new DateTime(2025, 9, 16, 17, 23, 0, 861, DateTimeKind.Local).AddTicks(8785));
migrationBuilder.UpdateData(
table: "StockMov",
keyColumn: "StockMovID",
keyValue: 3,
column: "DtCreate",
value: new DateTime(2025, 9, 16, 17, 23, 0, 861, DateTimeKind.Local).AddTicks(8790));
migrationBuilder.UpdateData(
table: "StockMov",
keyColumn: "StockMovID",
keyValue: 4,
column: "DtCreate",
value: new DateTime(2025, 9, 16, 17, 23, 0, 861, DateTimeKind.Local).AddTicks(8794));
migrationBuilder.UpdateData(
table: "StockMov",
keyColumn: "StockMovID",
keyValue: 5,
column: "DtCreate",
value: new DateTime(2025, 9, 16, 17, 23, 0, 861, DateTimeKind.Local).AddTicks(8799));
migrationBuilder.UpdateData(
table: "StockMov",
keyColumn: "StockMovID",
keyValue: 6,
column: "DtCreate",
value: new DateTime(2025, 9, 16, 17, 23, 0, 861, DateTimeKind.Local).AddTicks(8803));
migrationBuilder.UpdateData(
table: "StockMov",
keyColumn: "StockMovID",
keyValue: 7,
column: "DtCreate",
value: new DateTime(2025, 9, 16, 17, 23, 0, 861, DateTimeKind.Local).AddTicks(8808));
migrationBuilder.UpdateData(
table: "StockMov",
keyColumn: "StockMovID",
keyValue: 8,
column: "DtCreate",
value: new DateTime(2025, 9, 16, 17, 23, 0, 861, DateTimeKind.Local).AddTicks(8812));
migrationBuilder.UpdateData(
table: "StockMov",
keyColumn: "StockMovID",
keyValue: 9,
column: "DtCreate",
value: new DateTime(2025, 9, 16, 17, 23, 0, 861, DateTimeKind.Local).AddTicks(8817));
migrationBuilder.UpdateData(
table: "StockMov",
keyColumn: "StockMovID",
keyValue: 10,
column: "DtCreate",
value: new DateTime(2025, 9, 16, 17, 23, 0, 861, DateTimeKind.Local).AddTicks(8821));
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ItemIDParent",
table: "RegItem");
migrationBuilder.UpdateData(
table: "Offer",
keyColumn: "OfferID",
keyValue: 1,
columns: new[] { "Inserted", "Modified", "ValidUntil" },
values: new object[] { new DateTime(2025, 8, 8, 16, 36, 28, 420, DateTimeKind.Local).AddTicks(4215), new DateTime(2025, 8, 8, 16, 36, 28, 420, DateTimeKind.Local).AddTicks(4216), new DateTime(2025, 9, 8, 16, 36, 28, 420, DateTimeKind.Local).AddTicks(4212) });
migrationBuilder.UpdateData(
table: "OfferRowList",
keyColumn: "OfferRowID",
keyValue: 1,
columns: new[] { "Inserted", "Modified" },
values: new object[] { new DateTime(2025, 8, 8, 16, 36, 28, 420, DateTimeKind.Local).AddTicks(4243), new DateTime(2025, 8, 8, 16, 36, 28, 420, DateTimeKind.Local).AddTicks(4245) });
migrationBuilder.UpdateData(
table: "OfferRowList",
keyColumn: "OfferRowID",
keyValue: 2,
columns: new[] { "Inserted", "Modified" },
values: new object[] { new DateTime(2025, 8, 8, 16, 36, 28, 420, DateTimeKind.Local).AddTicks(4252), new DateTime(2025, 8, 8, 16, 36, 28, 420, DateTimeKind.Local).AddTicks(4253) });
migrationBuilder.UpdateData(
table: "OfferRowList",
keyColumn: "OfferRowID",
keyValue: 3,
columns: new[] { "Inserted", "Modified" },
values: new object[] { new DateTime(2025, 8, 8, 16, 36, 28, 420, DateTimeKind.Local).AddTicks(4259), new DateTime(2025, 8, 8, 16, 36, 28, 420, DateTimeKind.Local).AddTicks(4261) });
migrationBuilder.UpdateData(
table: "StockMov",
keyColumn: "StockMovID",
keyValue: 1,
column: "DtCreate",
value: new DateTime(2025, 8, 8, 16, 36, 28, 420, DateTimeKind.Local).AddTicks(3939));
migrationBuilder.UpdateData(
table: "StockMov",
keyColumn: "StockMovID",
keyValue: 2,
column: "DtCreate",
value: new DateTime(2025, 8, 8, 16, 36, 28, 420, DateTimeKind.Local).AddTicks(3989));
migrationBuilder.UpdateData(
table: "StockMov",
keyColumn: "StockMovID",
keyValue: 3,
column: "DtCreate",
value: new DateTime(2025, 8, 8, 16, 36, 28, 420, DateTimeKind.Local).AddTicks(3993));
migrationBuilder.UpdateData(
table: "StockMov",
keyColumn: "StockMovID",
keyValue: 4,
column: "DtCreate",
value: new DateTime(2025, 8, 8, 16, 36, 28, 420, DateTimeKind.Local).AddTicks(3997));
migrationBuilder.UpdateData(
table: "StockMov",
keyColumn: "StockMovID",
keyValue: 5,
column: "DtCreate",
value: new DateTime(2025, 8, 8, 16, 36, 28, 420, DateTimeKind.Local).AddTicks(4000));
migrationBuilder.UpdateData(
table: "StockMov",
keyColumn: "StockMovID",
keyValue: 6,
column: "DtCreate",
value: new DateTime(2025, 8, 8, 16, 36, 28, 420, DateTimeKind.Local).AddTicks(4004));
migrationBuilder.UpdateData(
table: "StockMov",
keyColumn: "StockMovID",
keyValue: 7,
column: "DtCreate",
value: new DateTime(2025, 8, 8, 16, 36, 28, 420, DateTimeKind.Local).AddTicks(4008));
migrationBuilder.UpdateData(
table: "StockMov",
keyColumn: "StockMovID",
keyValue: 8,
column: "DtCreate",
value: new DateTime(2025, 8, 8, 16, 36, 28, 420, DateTimeKind.Local).AddTicks(4011));
migrationBuilder.UpdateData(
table: "StockMov",
keyColumn: "StockMovID",
keyValue: 9,
column: "DtCreate",
value: new DateTime(2025, 8, 8, 16, 36, 28, 420, DateTimeKind.Local).AddTicks(4015));
migrationBuilder.UpdateData(
table: "StockMov",
keyColumn: "StockMovID",
keyValue: 10,
column: "DtCreate",
value: new DateTime(2025, 8, 8, 16, 36, 28, 420, DateTimeKind.Local).AddTicks(4019));
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+138 -39
View File
@@ -1,10 +1,11 @@
using EgwCoreLib.Lux.Data.DbModel;
using EgwCoreLib.Lux.Data.DbModel.Config;
using EgwCoreLib.Lux.Data.DbModel.Cost;
using EgwCoreLib.Lux.Data.DbModel.Items;
using EgwCoreLib.Lux.Data.DbModel.Sales;
using EgwCoreLib.Lux.Data.DbModel.Stock;
using EgwCoreLib.Lux.Data.DbModel.Task;
using EgwCoreLib.Lux.Data.DbModel.Utils;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EgwCoreLib.Lux.Data
{
@@ -18,7 +19,8 @@ namespace EgwCoreLib.Lux.Data
/// <param name="modelBuilder"></param>
public static void Seed(this ModelBuilder modelBuilder)
{
// inizializzazione dei valori di default x Ruoli
// inizializzazione dei valori di default x Ruoli/Tags
modelBuilder.Entity<TagsModel>().HasData(
new TagsModel { TagID = 1, Description = "Tag 01" },
new TagsModel { TagID = 2, Description = "Tag 02" },
@@ -27,6 +29,60 @@ namespace EgwCoreLib.Lux.Data
new TagsModel { TagID = 5, Description = "Tag 05" }
);
// init classi generiche x gestione liste
modelBuilder.Entity<GenClassModel>().HasData(
new GenClassModel { ClassCod = "ShapeList", Description = "Elenco Shape Gestite" },
new GenClassModel { ClassCod = "WoodCol", Description = "Elenco Colori Legno" }
);
// init dati x invio serializzazioni da environment
modelBuilder.Entity<EnvirParamModel>().HasData(
new EnvirParamModel { EnvirID = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW, SerStrucKey = "Jwd" },
new EnvirParamModel { EnvirID = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.BEAM, SerStrucKey = "Btl" },
new EnvirParamModel { EnvirID = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.CABINET, SerStrucKey = "Btl" },
new EnvirParamModel { EnvirID = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WALL, SerStrucKey = "Btl" }
);
modelBuilder.Entity<GenValueModel>().HasData(
new GenValueModel { GenValID = 1, Ordinal = 1, ClassCod = "WoodCol", ValString = "Blue" },
new GenValueModel { GenValID = 2, Ordinal = 2, ClassCod = "WoodCol", ValString = "White" },
new GenValueModel { GenValID = 3, Ordinal = 3, ClassCod = "WoodCol", ValString = "Red" },
new GenValueModel { GenValID = 4, Ordinal = 4, ClassCod = "WoodCol", ValString = "Black" },
new GenValueModel { GenValID = 5, Ordinal = 1, ClassCod = "ShapeList", ValString = "Rectangular" },
new GenValueModel { GenValID = 6, Ordinal = 2, ClassCod = "ShapeList", ValString = "Trapezoidal" },
new GenValueModel { GenValID = 7, Ordinal = 3, ClassCod = "ShapeList", ValString = "Triangular" },
new GenValueModel { GenValID = 8, Ordinal = 4, ClassCod = "ShapeList", ValString = "Arc" },
new GenValueModel { GenValID = 9, Ordinal = 5, ClassCod = "ShapeList", ValString = "FullArc" },
new GenValueModel { GenValID = 10, Ordinal = 6, ClassCod = "ShapeList", ValString = "SemiFullArc" },
new GenValueModel { GenValID = 11, Ordinal = 7, ClassCod = "ShapeList", ValString = "SemiArc" },
new GenValueModel { GenValID = 12, Ordinal = 8, ClassCod = "ShapeList", ValString = "Circular" }
);
modelBuilder.Entity<GlassModel>().HasData(
new GlassModel { GlassID = 1, Code = "0001", Description = "Vetro BE 2S 4/12/4", Thickness = 20 },
new GlassModel { GlassID = 2, Code = "0002", Description = "Vetro BE 2S 4/16/4", Thickness = 24 },
new GlassModel { GlassID = 3, Code = "0003", Description = "Vetro BE 3S 4/12/4/12/4", Thickness = 36 },
new GlassModel { GlassID = 4, Code = "0004", Description = "Vetro BE 3S 4/16/4/16/4", Thickness = 44 },
new GlassModel { GlassID = 5, Code = "0005", Description = "Vetro BE 2S 4T/12/4T", Thickness = 20 },
new GlassModel { GlassID = 6, Code = "0006", Description = "Vetro BE 2S 4T/16/4T", Thickness = 24 },
new GlassModel { GlassID = 7, Code = "0007", Description = "Vetro BE 3S 4T/12/4T/12/4T", Thickness = 36 },
new GlassModel { GlassID = 8, Code = "0008", Description = "Vetro BE 3S 4T/16/4T/16/4T", Thickness = 44 }
);
modelBuilder.Entity<ProfileModel>().HasData(
new ProfileModel { ProfileID = 1, Code = "0001", Description = "Profilo60", Thickness = 60 },
new ProfileModel { ProfileID = 2, Code = "0002", Description = "Profilo78", Thickness = 78 },
new ProfileModel { ProfileID = 3, Code = "0003", Description = "Profilo90", Thickness = 90 }
);
modelBuilder.Entity<WoodModel>().HasData(
new WoodModel { WoodID = 1, Code = "0001", Description = "Abete", Type = 1 },
new WoodModel { WoodID = 2, Code = "0002", Description = "Acero", Type = 1 },
new WoodModel { WoodID = 3, Code = "0003", Description = "Pino", Type = 2 },
new WoodModel { WoodID = 4, Code = "0004", Description = "Tek", Type = 3 }
);
// valori base classi generiche
// inizializzazione dei valori di default x Customer
modelBuilder.Entity<CustomerModel>().HasData(
new CustomerModel { CustomerID = 1, FirstName = "Customer A", LastName = "Egalware", VAT = "1234567890123456" },
@@ -131,15 +187,23 @@ namespace EgwCoreLib.Lux.Data
new StockMovModel { StockMovID = 10, StockStatusId = 10, QtyRec = 1, MovCod = "CAR", UserId = "samuele.locatelli@egalware.com", Note = "DEMO" }
);
// init cost drivers
modelBuilder.Entity<CostDriverModel>().HasData(
// Risorsa principale di calcolo produttività/costi
new CostDriverModel() { CostDriverID = 1, Name = "WorkHour", Unit = "h", Descript = "Ore lavorate per step/fase" },
new CostDriverModel() { CostDriverID = 2, Name = "Meter", Unit = "m", Descript = "Metri prodotti per step/fase" },
new CostDriverModel() { CostDriverID = 3, Name = "Unit", Unit = "#", Descript = "Numero unità prodotte (lavorate) per step/fase" }
);
// inizializzazione risorse
modelBuilder.Entity<ResourceModel>().HasData(
new ResourceModel { ResourceID = 1, Description = "Sezionatrice", IsAsset = true, IsHuman = false, UnitCostFix = 15, UmFix = "€/h" },
new ResourceModel { ResourceID = 2, Description = "Linea SAOMAD WoodPecker Just 3500", IsAsset = true, IsHuman = false, UnitCostFix = 240, UmFix = "€/h", UnitCostProp = 1.9, UmProp = "€/m" },
new ResourceModel { ResourceID = 3, Description = "Linea Pantografo", IsAsset = true, IsHuman = false, UnitCostFix = 90, UmFix = "€/h", UnitCostProp = 5.9, UmProp = "€/m" },
new ResourceModel { ResourceID = 4, Description = "Stazione Verniciatura", IsAsset = true, IsHuman = false, UnitCostFix = 40, UmFix = "€/h", UnitCostProp = 1.9, UmProp = "€/m" },
new ResourceModel { ResourceID = 5, Description = "Verniciatura Manuale", IsAsset = false, IsHuman = true, UnitCostFix = 10, UmFix = "€/h", UnitCostProp = 1.9, UmProp = "€/m" },
new ResourceModel { ResourceID = 6, Description = "Montaggio Manuale", IsAsset = false, IsHuman = true, UnitCostProp = 40, UmProp = "€/h" },
new ResourceModel { ResourceID = 7, Description = "Installatore", IsAsset = false, IsHuman = true, UnitCostProp = 40, UmProp = "€/h" }
new ResourceModel { ResourceID = 1, Name = "Sezionatrice", FixedCost = 12000, VariableCost = 6000, OverHeadCost = 5000, CostDriverID = 1, CostDriverBudget = 220 * 4, LaborCost = 30, OverHeadPerc = 0.15M, EBTPerc = 0.15M },
new ResourceModel { ResourceID = 2, Name = "Linea SAOMAD WoodPecker Just 3500", FixedCost = 100000, VariableCost = 30000, OverHeadCost = 15000, CostDriverID = 1, CostDriverBudget = 220 * 8, LaborCost = 40, OverHeadPerc = 0.15M, EBTPerc = 0.15M },
new ResourceModel { ResourceID = 3, Name = "Linea Pantografo", FixedCost = 24000, VariableCost = 6000, OverHeadCost = 5000, CostDriverID = 1, CostDriverBudget = 220 * 8, LaborCost = 35, OverHeadPerc = 0.15M, EBTPerc = 0.15M },
new ResourceModel { ResourceID = 4, Name = "Stazione Verniciatura", FixedCost = 24000, VariableCost = 6000, OverHeadCost = 3000, CostDriverID = 1, CostDriverBudget = 220 * 4, LaborCost = 30, OverHeadPerc = 0.15M, EBTPerc = 0.15M },
new ResourceModel { ResourceID = 5, Name = "Verniciatura Manuale", FixedCost = 6000, VariableCost = 2000, OverHeadCost = 3000, CostDriverID = 1, CostDriverBudget = 220 * 1, LaborCost = 30, OverHeadPerc = 0.15M, EBTPerc = 0.15M },
new ResourceModel { ResourceID = 6, Name = "Montaggio Manuale", FixedCost = 500, VariableCost = 500, OverHeadCost = 500, CostDriverID = 1, CostDriverBudget = 220 * 8 * 2, LaborCost = 30, OverHeadPerc = 0.15M, EBTPerc = 0.15M },
new ResourceModel { ResourceID = 7, Name = "Installatore", FixedCost = 0, VariableCost = 3000, OverHeadCost = 0, CostDriverID = 1, CostDriverBudget = 220 * 8 * 2, LaborCost = 40, OverHeadPerc = 0.15M, EBTPerc = 0.15M }
);
// inizializzazione fasi
@@ -156,57 +220,92 @@ namespace EgwCoreLib.Lux.Data
// inizializzazione cicli di lavoro
modelBuilder.Entity<JobModel>().HasData(
new JobModel { JobID = 1, Description = "Rivendita / servizi" },
new JobModel { JobID = 2, Description = "Serramento Completo Legno su linea Saomad e installatore interno" }//,
//new JobModel { JobID = 2, Description = "Serramento Completo Legno/Alluminio" },
//new JobModel { JobID = 3, Description = "Persiana Legno" },
//new JobModel { JobID = 4, Description = "restauro Persiana Esistente" }
new JobModel { JobID = 2, Description = "Serramento Completo Legno su linea Saomad e installatore interno" },
new JobModel { JobID = 3, Description = "Realizzazione Trave" },
new JobModel { JobID = 4, Description = "Realizzazione Cabinet" },
new JobModel { JobID = 5, Description = "Realizzazione Parete" }
);
// init righe ciclo (fasi di ciclo)
modelBuilder.Entity<JobRowModel>().HasData(
modelBuilder.Entity<JobStepModel>().HasData(
// per fare 1 finestra singola/semplice taglio 4 tronchetti telaio + 4 tronchetti finestra, considero 1 perché mi arriva dal sistema preventivo/motore
new JobRowModel { JobRowID = 1, JobID = 2, Index = 1, PhaseID = 1, Qty = 1, ResourceID = 1 },
new JobStepModel { JobStepID = 1, JobID = 2, Index = 1, CostDriverID = 3, PhaseID = 1, ProductivityRate = 1, ResourceID = 1 },
// taglio profilo su linea saomad, considero 1 perché mi arriva dal sistema preventivo/motore
new JobRowModel { JobRowID = 2, JobID = 2, Index = 2, PhaseID = 2, Qty = 1, ResourceID = 2 },
new JobStepModel { JobStepID = 2, JobID = 2, Index = 2, CostDriverID = 2, PhaseID = 2, ProductivityRate = 1, ResourceID = 2 },
// verniciatura automatica, considero 1 perché mi arriva dal sistema preventivo/motore
new JobRowModel { JobRowID = 3, JobID = 2, Index = 3, PhaseID = 3, Qty = 1, ResourceID = 4 },
new JobStepModel { JobStepID = 3, JobID = 2, Index = 3, CostDriverID = 3, PhaseID = 3, ProductivityRate = 1, ResourceID = 4 },
// assemblaggio
new JobRowModel { JobRowID = 4, JobID = 2, Index = 4, PhaseID = 4, Qty = 1, ResourceID = 6 },
new JobStepModel { JobStepID = 4, JobID = 2, Index = 4, CostDriverID = 3, PhaseID = 4, ProductivityRate = 1, ResourceID = 6 },
// installazione
new JobRowModel { JobRowID = 5, JobID = 2, Index = 5, PhaseID = 6, Qty = 1, ResourceID = 7 }
new JobStepModel { JobStepID = 5, JobID = 2, Index = 5, CostDriverID = 3, PhaseID = 6, ProductivityRate = 1, ResourceID = 7 }
);
// init item righe ciclo (articoli + prodotti delle fasi del ciclo)
modelBuilder.Entity<JobRowItemModel>().HasData(
modelBuilder.Entity<JobStepItemModel>().HasData(
// eventuale scarto del materiale tra grezzo e finito--> porta ad un numero >1 (QUI NON USATO, ho il valore calcolato dal motore)
new JobRowItemModel { JobRowItemID = 1, JobRowID = 1, Index = 1, ItemID = 1, Qty = 1, Description = "Grezzo legno abete" },
new JobStepItemModel { JobStepItemID = 1, JobStepID = 1, Index = 1, ItemID = 1, Qty = 1, Description = "Grezzo legno abete" },
// 1/10 litro di vernice per metro lineare prodotto
new JobRowItemModel { JobRowItemID = 2, JobRowID = 3, Index = 2, ItemID = 8, Qty = 0.1, Description = "Vernice trasparente standard 1L" },
new JobStepItemModel { JobStepItemID = 2, JobStepID = 3, Index = 2, ItemID = 8, Qty = 0.1, Description = "Vernice trasparente standard 1L" },
// uso un KIT intero (vs specifico n prodotti singoli) x questo modello; se dal preventivo arrivano n pezzi spcifici, qui li esplodiamo
new JobRowItemModel { JobRowItemID = 3, JobRowID = 4, Index = 3, ItemID = 9, Qty = 1, Description = "Ferramenta AGB - rif. AGFD.00000.00000" }
new JobStepItemModel { JobStepItemID = 3, JobStepID = 4, Index = 3, ItemID = 9, Qty = 1, Description = "Ferramenta AGB - rif. AGFD.00000.00000" }
);
// JWD finestra cVetro Fisso / Cieca
string jwdVetroFisso = "{\"ProfilePath\":\"Profilo78\",\"Material\":\"Abete\",\"ColorMaterial\":\"White\",\"Glass\":\"Vetro BE 2S 4/12/4\",\"AreaList\":[{\"Shape\":\"RECTANGLE\",\"DimensionList\":[{\"nIndex\":1,\"sName\":\"Width\",\"dValue\":800.0},{\"nIndex\":2,\"sName\":\"Height\",\"dValue\":1200.0}],\"JointList\":[{\"nIndex\":1,\"JointType\":\"FULL_H\"},{\"nIndex\":2,\"JointType\":\"FULL_H\"},{\"nIndex\":3,\"JointType\":\"FULL_H\"},{\"nIndex\":4,\"JointType\":\"FULL_H\"}],\"BottomRail\":false,\"BottomRailQty\":0,\"IdGroup\":1,\"AreaList\":[{\"FillType\":\"GLASS\",\"IdGroup\":4,\"AreaList\":[],\"AreaType\":\"FILL\"}],\"AreaType\":\"FRAME\"}]}";
// JWD finestra 1 Anta 800x1200
string jwdAntaSingola = "{\"ProfilePath\":\"Profilo78\",\"Material\":\"Abete\",\"ColorMaterial\":\"White\",\"Glass\":\"Vetro BE 2S 4/12/4\",\"AreaList\":[{\"Shape\":\"RECTANGLE\",\"DimensionList\":[{\"nIndex\":1,\"sName\":\"Width\",\"dValue\":800.0},{\"nIndex\":2,\"sName\":\"Height\",\"dValue\":1200.0}],\"JointList\":[{\"nIndex\":1,\"JointType\":\"FULL_H\"},{\"nIndex\":2,\"JointType\":\"FULL_H\"},{\"nIndex\":3,\"JointType\":\"FULL_H\"},{\"nIndex\":4,\"JointType\":\"FULL_H\"}],\"BottomRail\":false,\"BottomRailQty\":0,\"IdGroup\":1,\"AreaList\":[{\"bIsSashVertical\":true,\"SashList\":[{\"nSashId\":1,\"OpeningType\":\"TILTTURN_LEFT\",\"bHasHandle\":true,\"dDimension\":100.0}],\"SashType\":\"NULL\",\"JointList\":[{\"nIndex\":1,\"JointType\":\"FULL_H\"},{\"nIndex\":2,\"JointType\":\"FULL_H\"},{\"nIndex\":3,\"JointType\":\"FULL_H\"},{\"nIndex\":4,\"JointType\":\"FULL_H\"}],\"BottomRail\":false,\"BottomRailQty\":0,\"Hardware\":\"000558\",\"IdGroup\":2,\"AreaList\":[{\"FillType\":\"GLASS\",\"IdGroup\":3,\"AreaList\":[],\"AreaType\":\"FILL\"}],\"AreaType\":\"SASH\"}],\"AreaType\":\"FRAME\"}]}";
// inizializzazione dei valori di default x SellingItem
modelBuilder.Entity<SellingItemModel>().HasData(
new SellingItemModel { SellingItemID = 1, IsService = false, Description = "Finestra anta Singola", Cost = 820, Margin = 0.2, JobID = 2 },
new SellingItemModel { SellingItemID = 2, IsService = false, Description = "Persiana anta singola", Cost = 150, Margin = 0.1, JobID = 1 },
new SellingItemModel { SellingItemID = 3, IsService = true, Description = "Installazione", Cost = 200, Margin = 0.3, JobID = 1 }
new SellingItemModel { SellingItemID = 1, IsService = false, Description = "Finestra Anta Singola", Cost = 500, Margin = 0.2, JobID = 2, SerStruct = jwdAntaSingola, Envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW },
new SellingItemModel { SellingItemID = 2, IsService = false, Description = "Finestra Vetro Fisso ", Cost = 300, Margin = 0.2, JobID = 2, SerStruct = jwdVetroFisso, Envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW },
new SellingItemModel { SellingItemID = 3, IsService = false, Description = "Persiana anta singola", Cost = 150, Margin = 0.1, JobID = 1, Envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW },
new SellingItemModel { SellingItemID = 4, IsService = true, Description = "Installazione", Cost = 200, Margin = 0.3, JobID = 1, Envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW },
new SellingItemModel { SellingItemID = 5, IsService = false, Description = "Trave lamellare", Cost = 1000, Margin = 0.3, JobID = 3, Envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.BEAM },
new SellingItemModel { SellingItemID = 6, IsService = false, Description = "Cabinet", Cost = 500, Margin = 0.3, JobID = 4, Envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.CABINET },
new SellingItemModel { SellingItemID = 7, IsService = false, Description = "Parete", Cost = 2000, Margin = 0.3, JobID = 5, Envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WALL }
);
// inizializzazione dei valori di default x Offer
modelBuilder.Entity<OfferModel>().HasData(
new OfferModel { OfferID = 1, RefYear = 2024, RefNum = 1, RefRev = 1, Description = "Offerta per tre serramenti", CustomerID = 2, DealerID = 2 }
//new OfferModel { OfferID = 2, RefYear = 2025, RefNum = 2, RefRev = 1, Description = "Offerta per un serramento + installazione", CustomerID = 1, DealerID = 1 },
//new OfferModel { OfferID = 3, RefYear = 2025, RefNum = 3, RefRev = 1, Description = "Offerta per tre serramenti", CustomerID = 2, DealerID = 1 },
//new OfferModel { OfferID = 5, RefYear = 2025, RefNum = 4, RefRev = 2, Description = "Offerta per cinque serramenti + installazione", CustomerID = 3, DealerID = 2 }
new OfferModel { OfferID = 1, RefYear = 2024, RefNum = 1, RefRev = 1, Description = "Offerta per tre serramenti", CustomerID = 2, DealerID = 2, Envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW },
new OfferModel { OfferID = 2, RefYear = 2024, RefNum = 2, RefRev = 1, Description = "Offerta BEAM", CustomerID = 2, DealerID = 2, Envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.BEAM },
new OfferModel { OfferID = 3, RefYear = 2024, RefNum = 3, RefRev = 1, Description = "Offerta Cabinet", CustomerID = 2, DealerID = 2, Envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.CABINET },
new OfferModel { OfferID = 4, RefYear = 2024, RefNum = 4, RefRev = 1, Description = "Offerta Wall", CustomerID = 2, DealerID = 2, Envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WALL }
//new OfferModel { OfferID = 2, RefYear = 2025, RefNum = 2, RefRev = 1, Name = "Offerta per un serramento + installazione", CustomerID = 1, DealerID = 1 },
//new OfferModel { OfferID = 3, RefYear = 2025, RefNum = 3, RefRev = 1, Name = "Offerta per tre serramenti", CustomerID = 2, DealerID = 1 },
//new OfferModel { OfferID = 5, RefYear = 2025, RefNum = 4, RefRev = 2, Name = "Offerta per cinque serramenti + installazione", CustomerID = 3, DealerID = 2 }
);
// inizializzazione dei valori di default x OfferRow
// inizializzazione dei valori di default x Offerta 1 = WINDOW
modelBuilder.Entity<OfferRowModel>().HasData(
new OfferRowModel { OfferRowID = 1, OfferID = 1, OfferRowUID = "OFF0000000001", Inserted = DateTime.Now, Modified = DateTime.Now, Cost = 950, SellingItemID = 1, Qty = 3, RowNum = 1, SerStruct = "{}", Note = "Finestra anta singola 2025", ItemSPP = "{}", BomOk = true, ItemOk = true },
new OfferRowModel { OfferRowID = 2, OfferID = 1, OfferRowUID = "OFF0000000002", Inserted = DateTime.Now, Modified = DateTime.Now, Cost = 160, SellingItemID = 2, Qty = 3, RowNum = 2, SerStruct = "{}", Note = "Persiana per Finestra anta singola 2025", ItemSPP = "{}", BomOk = true, ItemOk = true },
new OfferRowModel { OfferRowID = 3, OfferID = 1, OfferRowUID = "OFF0000000003", Inserted = DateTime.Now, Modified = DateTime.Now, Cost = 200, SellingItemID = 3, Qty = 3, RowNum = 3, SerStruct = "{}", Note = "Installazione serramento", ItemSPP = "{}", BomOk = true, ItemOk = true }
new OfferRowModel { OfferRowID = 1, OfferID = 1, Envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW, OfferRowUID = $"SOR.{DateTime.Today:yy}.{1:X8}", Inserted = DateTime.Now, Modified = DateTime.Now, BomCost = 900, BomPrice = 950, SellingItemID = 2, Qty = 3, RowNum = 1, SerStruct = jwdVetroFisso, Note = "Finestra Vetro Fisso 2025", ItemSteps = "{}", BomOk = true, ItemOk = true },
new OfferRowModel { OfferRowID = 2, OfferID = 1, Envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW, OfferRowUID = $"SOR.{DateTime.Today:yy}.{2:X8}", Inserted = DateTime.Now, Modified = DateTime.Now, BomCost = 900, BomPrice = 950, SellingItemID = 1, Qty = 3, RowNum = 1, SerStruct = jwdAntaSingola, Note = "Finestra Anta Singola 2025", ItemSteps = "{}", BomOk = true, ItemOk = true },
new OfferRowModel { OfferRowID = 3, OfferID = 1, Envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW, OfferRowUID = $"SOR.{DateTime.Today:yy}.{3:X8}", Inserted = DateTime.Now, Modified = DateTime.Now, BomCost = 160, BomPrice = 200, SellingItemID = 2, Qty = 3, RowNum = 2, SerStruct = "{}", Note = "Persiana per Finestra anta singola 2025", ItemSteps = "{}", BomOk = true, ItemOk = true },
new OfferRowModel { OfferRowID = 4, OfferID = 1, Envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW, OfferRowUID = $"SOR.{DateTime.Today:yy}.{4:X8}", Inserted = DateTime.Now, Modified = DateTime.Now, BomCost = 200, BomPrice = 250, SellingItemID = 3, Qty = 3, RowNum = 3, SerStruct = "{}", Note = "Installazione serramento", ItemSteps = "{}", BomOk = true, ItemOk = true }
);
// inizializzazione dei valori di default x Offerta 2 = BEAM
modelBuilder.Entity<OfferRowModel>().HasData(
new OfferRowModel { OfferRowID = 5, OfferID = 2, Envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.BEAM, OfferRowUID = $"SOR.{DateTime.Today:yy}.{5:X8}", Inserted = DateTime.Now, Modified = DateTime.Now, BomCost = 800, BomPrice = 1150, SellingItemID = 4, Qty = 10, RowNum = 1, SerStruct = "", Note = "Demo file 01", ItemSteps = "{}", BomOk = true, ItemOk = true },
new OfferRowModel { OfferRowID = 6, OfferID = 2, Envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.BEAM, OfferRowUID = $"SOR.{DateTime.Today:yy}.{6:X8}", Inserted = DateTime.Now, Modified = DateTime.Now, BomCost = 600, BomPrice = 950, SellingItemID = 4, Qty = 4, RowNum = 1, SerStruct = "", Note = "Demo file 02", ItemSteps = "{}", BomOk = true, ItemOk = true }
);
// inizializzazione dei valori di default x Offerta 3 = CABINET
modelBuilder.Entity<OfferRowModel>().HasData(
new OfferRowModel { OfferRowID = 7, OfferID = 3, Envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WALL, OfferRowUID = $"SOR.{DateTime.Today:yy}.{7:X8}", Inserted = DateTime.Now, Modified = DateTime.Now, BomCost = 200, BomPrice = 250, SellingItemID = 5, Qty = 4, RowNum = 1, SerStruct = "", Note = "Demo file 01", ItemSteps = "{}", BomOk = true, ItemOk = true },
new OfferRowModel { OfferRowID = 8, OfferID = 3, Envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WALL, OfferRowUID = $"SOR.{DateTime.Today:yy}.{8:X8}", Inserted = DateTime.Now, Modified = DateTime.Now, BomCost = 50, BomPrice = 80, SellingItemID = 5, Qty = 12, RowNum = 1, SerStruct = "", Note = "Demo file 02", ItemSteps = "{}", BomOk = true, ItemOk = true }
);
// inizializzazione dei valori di default x Offerta 4 = WALL
modelBuilder.Entity<OfferRowModel>().HasData(
new OfferRowModel { OfferRowID = 9, OfferID = 4, Envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.CABINET, OfferRowUID = $"SOR.{DateTime.Today:yy}.{9:X8}", Inserted = DateTime.Now, Modified = DateTime.Now, BomCost = 800, BomPrice = 1150, SellingItemID = 6, Qty = 6, RowNum = 1, SerStruct = "", Note = "Demo file 01", ItemSteps = "{}", BomOk = true, ItemOk = true },
new OfferRowModel { OfferRowID = 10, OfferID = 4, Envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.CABINET, OfferRowUID = $"SOR.{DateTime.Today:yy}.{10:X8}", Inserted = DateTime.Now, Modified = DateTime.Now, BomCost = 600, BomPrice = 950, SellingItemID = 6, Qty = 4, RowNum = 1, SerStruct = "", Note = "Demo file 02", ItemSteps = "{}", BomOk = true, ItemOk = true }
);
}
#endregion Public Methods
+99 -26
View File
@@ -13,58 +13,105 @@ using System.Threading.Tasks;
namespace EgwCoreLib.Lux.Data.Services
{
/// <summary>
/// Classe base per i servizi che fornisce funzionalità comuni come
/// - connessione a Redis
/// - configurazione
/// - gestione dei messaggi
/// - strategie di caching.
///
/// Questa classe agisce come modello per altri servizi derivati.
/// </summary>
public class BaseServ
{
#region Public Constructors
/// <summary>
/// Inizializza una nuova istanza della classe BaseServ.
/// Configura la connessione Redis, carica le impostazioni di configurazione e inizializza il serializzatore JSON.
/// </summary>
/// <param name="Configuration">Oggetto di configurazione per recuperare le impostazioni dell'applicazione.</param>
/// <param name="RedisConn">Multiplexer di connessione Redis per operazioni sul database.</param>
public BaseServ(IConfiguration Configuration, IConnectionMultiplexer RedisConn)
{
configuration = Configuration;
// setup componenti REDIS
#if false
redisConn = ConnectionMultiplexer.Connect(_configuration.GetConnectionString("Redis") ?? "localhost");
#endif
_config = Configuration;
redisConn = RedisConn;
redisDb = redisConn.GetDatabase();
svgChannel = configuration.GetValue<string>("ServerConf:SvgChannel") ?? "svg:img";
// aggiungo ricerca generica ":*" al channel...
// channel name setup
svgChannel = _config.GetValue<string>("ServerConf:SvgChannel") ?? "Egw-svg:img";
bomChannel = _config.GetValue<string>("ServerConf:BomChannel") ?? "Egw-bom";
updateChannel = _config.GetValue<string>("ServerConf:UpdateChannel") ?? "Egw-update";
// Appends ":*" to the channels to enable wildcard subscription for dynamic events
if (!svgChannel.EndsWith(":*"))
{
svgChannel += ":*";
}
}
if (!bomChannel.EndsWith(":*"))
{
bomChannel += ":*";
}
if (!updateChannel.EndsWith(":*"))
{
updateChannel += ":*";
}
// json serializer... FIX errore loop circolare https://www.ryadel.com/en/jsonserializationexception-self-referencing-loop-detected-error-fix-entity-framework-asp-net-core/
// Configurazione serializzatore JSON per risolvere errore di loop circolare
JSSettings = new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
// conf message pipe
// Configurazione pipe dei messaggi
CalcDonePipe = new MessagePipe(redisConn, svgChannel);
BomPipe = new MessagePipe(RedisConn, bomChannel);
UpdatePipe = new MessagePipe(RedisConn, updateChannel);
}
#endregion Public Constructors
#region Protected Fields
private string svgChannel = "";
protected static IConfiguration configuration = null!;
protected JsonSerializerSettings? JSSettings;
#region Public Properties
/// <summary>
/// Message pipe esecuzione elaborazione EgwCalc >> UI
/// Pipe dei messaggi per la comunicazione riguardo update calcolo BOM
/// </summary>
public MessagePipe BomPipe { get; set; } = null!;
/// <summary>
/// Pipe dei messaggi per la comunicazione tra servizi di calcolo e interfaccia utente.
/// I messaggi vengono inviati sul canale Redis definito da svgChannel.
/// </summary>
public MessagePipe CalcDonePipe { get; set; } = null!;
/// <summary>
/// Oggetto per connessione a REDIS
/// Pipe dei messaggi per la comunicazione riguardo update generico UI
/// </summary>
public MessagePipe UpdatePipe { get; set; } = null!;
#endregion Public Properties
#region Protected Fields
/// <summary>
/// Oggetto di configurazione statico per accedere alle impostazioni dell'applicazione (es. stringhe di connessione).
/// Condiviso tra tutte le istanze di BaseServ.
/// </summary>
protected static IConfiguration _config = null!;
/// <summary>
/// Impostazioni del serializzatore JSON utilizzato per gestire oggetti con riferimenti circolari
/// (es. oggetti che si fanno riferimento reciprocamente).
/// </summary>
protected JsonSerializerSettings? JSSettings;
/// <summary>
/// Oggetto per la connessione a Redis utilizzato per operazioni di lettura/scrittura.
/// </summary>
protected IConnectionMultiplexer redisConn = null!;
//ISubscriber sub = redis.GetSubscriber();
/// <summary>
/// Oggetto DB redis da impiegare x chiamate R/W
/// Database Redis utilizzato per le operazioni di lettura/scrittura
/// nb: ottenuto tramite redisConn.GetDatabase()
/// </summary>
protected IDatabase redisDb = null!;
@@ -73,7 +120,7 @@ namespace EgwCoreLib.Lux.Data.Services
#region Protected Properties
/// <summary>
/// Durata cache breve (1 min circa + perturbazione percentuale +/-10%)
/// Durata della cache breve (circa 1 minuto + variazione del +/-10%)
/// </summary>
protected TimeSpan FastCache
{
@@ -81,7 +128,7 @@ namespace EgwCoreLib.Lux.Data.Services
}
/// <summary>
/// Durata cache lunga (+ perturbazione percentuale +/-10%)
/// Durata della cache lunga (+ variazione del +/-10%)
/// </summary>
protected TimeSpan LongCache
{
@@ -89,7 +136,7 @@ namespace EgwCoreLib.Lux.Data.Services
}
/// <summary>
/// Durata cache MOLTO breve (10 sec circa + perturbazione percentuale +/-10%)
/// Durata della cache molto breve (circa 10 secondi + variazione del +/-10%)
/// </summary>
protected TimeSpan UltraFastCache
{
@@ -97,7 +144,7 @@ namespace EgwCoreLib.Lux.Data.Services
}
/// <summary>
/// Durata cache MOLTO lunga (+ perturbazione percentuale +/-10%)
/// Durata della cache molto lunga (+ variazione del +/-10%)
/// </summary>
protected TimeSpan UltraLongCache
{
@@ -108,20 +155,46 @@ namespace EgwCoreLib.Lux.Data.Services
#region Private Fields
/// <summary>
/// Oggetto logger utilizzato per registrare eventi e errori a livello di classe.
/// Utile per il monitoraggio del comportamento dell'applicazione e la risoluzione di problemi.
/// </summary>
private static Logger Log = LogManager.GetCurrentClassLogger();
/// <summary>
/// Durata cache lunga IN SECONDI
/// Redis channel for BOM related info
/// </summary>
private string bomChannel = "";
/// <summary>
/// Durata della cache lunga in secondi (predefinito: 5 minuti)
/// Utilizzato nella proprietà LongCache per definire quanto a lungo i dati devono essere memorizzati in cache.
/// </summary>
private int cacheTtlLong = 60 * 5;
/// <summary>
/// Durata cache breve IN SECONDI
/// Durata della cache breve in secondi (predefinito: 1 minuto)
/// Utilizzato nelle proprietà FastCache e UltraFastCache per definire la durata della cache breve.
/// </summary>
private int cacheTtlShort = 60 * 1;
/// <summary>
/// Generatore di numeri casuali utilizzato per introdurre variabilità dinamica nelle durate della cache
/// (simula variazioni reali nella freschezza dei dati e nei tempi di scadenza).
/// </summary>
private Random rnd = new Random();
/// <summary>
/// Nome del canale Redis utilizzato per l'invio/ricezione di messaggi relativi a img svg.
/// Predefinito a "svg:img" con suffisso ":*".
/// </summary>
private string svgChannel = "";
/// <summary>
/// Nome del canale Redis utilizzato per l'invio/ricezione di messaggi di update
/// </summary>
private string updateChannel = "";
#endregion Private Fields
}
}
@@ -0,0 +1,83 @@
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using NLog;
using RestSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EgwCoreLib.Lux.Data.Services
{
public class CalcRequestService
{
#region Public Constructors
public CalcRequestService(IConfiguration config, IRedisService redisService)
{
_config = config;
_redisService = redisService;
bomChannel = _config.GetValue<string>("ServerConf:BomChannel") ?? "bom";
// verifico la url base
apiUrl = _config.GetValue<string>("ServerConf:Prog.ApiUrl") ?? "https://iis01.egalware.com/lux/srv/api";
routeBasePath = _config.GetValue<string>("ServerConf:RouteBaseUrl") ?? "window";
// fix opzioni base del RestClient
restOptStd = new RestClientOptions
{
Timeout = TimeSpan.FromSeconds(60),
BaseUrl = new Uri($"{apiUrl}/{routeBasePath}")
};
Log.Info("ImageCache Service Started");
}
#endregion Public Constructors
#region Public Methods
/// <summary>
/// Effettua una generica chiamata ApiRest
/// </summary>
/// <param name="ApiUrl">URL Api di base</param>
/// <param name="ApiRequest">Richiesta completa</param>
/// <param name="rawBody">Corpo Json trasmesso con richiesta</param>
/// <returns></returns>
public async Task<RestResponse> CallRestPost(string ApiUrl, string ApiRequest, object rawBody)
{
RestResponse response;
// cerco online
using (RestClient client = new RestClient(ApiUrl))
{
var request = new RestRequest(ApiRequest, Method.Post);
string rawData = JsonConvert.SerializeObject(rawBody);
request.AddJsonBody(rawData);
response = await client.ExecutePostAsync(request);
}
return response;
}
#endregion Public Methods
#region Private Fields
private static Logger Log = LogManager.GetCurrentClassLogger();
private readonly IRedisService _redisService;
private readonly string apiUrl = "";
private readonly string bomChannel = "bomdev";
private readonly string routeBasePath = "";
private IConfiguration _config;
#endregion Private Fields
#region Private Properties
/// <summary>
/// Conf client RestSharp standard:
/// - base URI al sito
/// - timeout 1 min
/// </summary>
private RestClientOptions restOptStd { get; set; } = new RestClientOptions();
#endregion Private Properties
}
}
@@ -0,0 +1,53 @@
using EgwCoreLib.Lux.Core.RestPayload;
using EgwCoreLib.Lux.Data.Controllers;
using EgwCoreLib.Lux.Data.DbModel.Config;
using EgwMultiEngineManager.Data;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using NLog;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EgwCoreLib.Lux.Data.Services
{
public class ConfigDataService : BaseServ
{
public ConfigDataService(IConfiguration configuration, IConnectionMultiplexer RedisConn) : base(configuration, RedisConn)
{
// init implicito
Log.Info($"ConfigDataService | Init OK");
}
/// <summary>
/// restituisce l'elenco dell'HW valido in memoria
/// </summary>
/// <returns></returns>
public List<HardwareModel> ElencoHw(Constants.EXECENVIRONMENTS execEnvironment, string HwReq = "HW.AGB")
{
List<HardwareModel> result = new List<HardwareModel>();
// leggo obj da redis cache
var currKey = $"{redisBaseKey}:{execEnvironment}:HML:{HwReq}";
RedisValue rawData = redisDb.StringGet(currKey);
// prendo solo i valori validi (Description <> FamilyName)
if (rawData.HasValue)
{
var currList = JsonConvert.DeserializeObject<List<HardwareModel>>($"{rawData}");
result = currList ?? new List<HardwareModel>();
//var currList = JsonConvert.DeserializeObject<HwListDTO>($"{rawData}");
//if (currList != null && currList.ListItem != null)
//{
// result = currList.ListItem;
//}
}
return result;
}
private static Logger Log = LogManager.GetCurrentClassLogger();
private string redisBaseKey = "Lux";
}
}
+569 -49
View File
@@ -1,6 +1,7 @@
using EgwCoreLib.Lux.Core.RestPayload;
using EgwCoreLib.Lux.Data.Controllers;
using EgwCoreLib.Lux.Data.DbModel;
using EgwCoreLib.Lux.Data.DbModel.Utils;
using EgwCoreLib.Lux.Data.DbModel.Sales;
using EgwMultiEngineManager.Data;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
@@ -15,6 +16,8 @@ using System.Text;
using System.Threading.Tasks;
using static EgwCoreLib.Lux.Core.Enums;
using static System.Runtime.InteropServices.JavaScript.JSType;
using EgwCoreLib.Lux.Data.DbModel.Items;
using EgwCoreLib.Lux.Data.DbModel.Config;
namespace EgwCoreLib.Lux.Data.Services
{
@@ -25,15 +28,15 @@ namespace EgwCoreLib.Lux.Data.Services
public DataLayerServices(IConfiguration configuration, IConnectionMultiplexer RedisConn) : base(configuration, RedisConn)
{
// conf DB
string connStr = BaseServ.configuration.GetConnectionString("Lux.All") ?? "";
string connStr = BaseServ._config.GetConnectionString("Lux.All") ?? "";
if (string.IsNullOrEmpty(connStr))
{
Log.Error("ConnString empty!");
}
else
{
//dbController = new Controllers.LuxController(_config);
dbController = new LuxController();
//dbController = new Controllers.LuxController(configuration);
StringBuilder sb = new StringBuilder();
sb.AppendLine($"DataLayerServices | LuxController OK");
Log.Info(sb.ToString());
@@ -42,14 +45,216 @@ namespace EgwCoreLib.Lux.Data.Services
#endregion Public Constructors
#region Public Properties
public static LuxController dbController { get; set; } = null!;
#endregion Public Properties
#region Public Methods
/// <summary>
/// Elenco completo Config Envir
/// </summary>
/// <returns></returns>
public async Task<List<EnvirParamModel>> ConfEnvirParamGetAllAsync()
{
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
List<EnvirParamModel>? result = new List<EnvirParamModel>();
// cerco in redis...
string currKey = $"{redisBaseKey}:ConfEnvir";
RedisValue rawData = await redisDb.StringGetAsync(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<EnvirParamModel>>($"{rawData}");
source = "REDIS";
}
else
{
result = await dbController.ConfEnvirParamGetAllAsync();
// serializzo e salvo con config x evitare loop...
rawData = JsonConvert.SerializeObject(result, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, UltraLongCache);
}
if (result == null)
{
result = new List<EnvirParamModel>();
}
sw.Stop();
Log.Debug($"ConfEnvirParamGetAllAsync | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// Esegue eliminazione + refresh cache
/// </summary>
/// <param name="selRec"></param>
/// <returns></returns>
public async Task<bool> ConfGlassDeleteAsync(GlassModel selRec)
{
bool result = await dbController.ConfGlassDeleteAsync(selRec);
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:ConfGlass");
return result;
}
/// <summary>
/// Elenco completo Config Glass
/// </summary>
/// <returns></returns>
public async Task<List<GlassModel>> ConfGlassGetAllAsync()
{
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
List<GlassModel>? result = new List<GlassModel>();
// cerco in redis...
string currKey = $"{redisBaseKey}:ConfGlass";
RedisValue rawData = await redisDb.StringGetAsync(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<GlassModel>>($"{rawData}");
source = "REDIS";
}
else
{
result = await dbController.ConfGlassGetAllAsync();
// serializzo e salvo con config x evitare loop...
rawData = JsonConvert.SerializeObject(result, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, UltraLongCache);
}
if (result == null)
{
result = new List<GlassModel>();
}
sw.Stop();
Log.Debug($"ConfGlassGetAllAsync | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// Esegue Upsert del record ricevuto
/// </summary>
/// <param name="upsRec"></param>
/// <returns></returns>
public async Task<bool> ConfGlassUpsertAsync(GlassModel upsRec)
{
bool result = await dbController.ConfGlassUpsertAsync(upsRec);
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:ConfGlass");
return result;
}
/// <summary>
/// Esegue eliminazione + refresh cache
/// </summary>
/// <param name="selRec"></param>
/// <returns></returns>
public async Task<bool> ConfProfileDeleteAsync(ProfileModel selRec)
{
bool result = await dbController.ConfProfileDeleteAsync(selRec);
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:ConfProfile");
return result;
}
/// <summary>
/// Elenco completo Config Profile
/// </summary>
/// <returns></returns>
public async Task<List<ProfileModel>> ConfProfileGetAllAsync()
{
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
List<ProfileModel>? result = new List<ProfileModel>();
// cerco in redis...
string currKey = $"{redisBaseKey}:ConfProfile";
RedisValue rawData = await redisDb.StringGetAsync(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<ProfileModel>>($"{rawData}");
source = "REDIS";
}
else
{
result = await dbController.ConfProfileGetAllAsync();
// serializzo e salvo con config x evitare loop...
rawData = JsonConvert.SerializeObject(result, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, UltraLongCache);
}
if (result == null)
{
result = new List<ProfileModel>();
}
sw.Stop();
Log.Debug($"ConfProfileGetAllAsync | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// Esegue Upsert del record ricevuto
/// </summary>
/// <param name="upsRec"></param>
/// <returns></returns>
public async Task<bool> ConfProfileUpsertAsync(ProfileModel upsRec)
{
bool result = await dbController.ConfProfileUpsertAsync(upsRec);
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:ConfProfile");
return result;
}
/// <summary>
/// Esegue eliminazione + refresh cache
/// </summary>
/// <param name="selRec"></param>
/// <returns></returns>
public async Task<bool> ConfWoodDeleteAsync(WoodModel selRec)
{
bool result = await dbController.ConfWoodDeleteAsync(selRec);
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:ConfWood");
return result;
}
/// <summary>
/// Elenco completo Config Wood
/// </summary>
/// <returns></returns>
public async Task<List<WoodModel>> ConfWoodGetAllAsync()
{
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
List<WoodModel>? result = new List<WoodModel>();
// cerco in redis...
string currKey = $"{redisBaseKey}:ConfWood";
RedisValue rawData = await redisDb.StringGetAsync(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<WoodModel>>($"{rawData}");
source = "REDIS";
}
else
{
result = await dbController.ConfWoodGetAllAsync();
// serializzo e salvo con config x evitare loop...
rawData = JsonConvert.SerializeObject(result, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, UltraLongCache);
}
if (result == null)
{
result = new List<WoodModel>();
}
sw.Stop();
Log.Debug($"ConfWoodGetAllAsync | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// Esegue Upsert del record ricevuto
/// </summary>
/// <param name="upsRec"></param>
/// <returns></returns>
public async Task<bool> ConfWoodUpsertAsync(WoodModel upsRec)
{
bool result = await dbController.ConfWoodUpsertAsync(upsRec);
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:ConfWood");
return result;
}
/// <summary>
/// Elenco completo Customers
/// </summary>
@@ -73,7 +278,7 @@ namespace EgwCoreLib.Lux.Data.Services
result = dbController.CustomersGetAll();
// serializzo e salvo...
rawData = JsonConvert.SerializeObject(result);
redisDb.StringSet(currKey, rawData, LongCache);
redisDb.StringSet(currKey, rawData, UltraLongCache);
}
if (result == null)
{
@@ -148,6 +353,171 @@ namespace EgwCoreLib.Lux.Data.Services
return answ;
}
/// <summary>
/// Reset cache sistema x Offerte modalità async
/// </summary>
public async Task<bool> FlushCacheOffersAsync()
{
bool answ = false;
Stopwatch sw = new Stopwatch();
sw.Start();
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:Offers:*");
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:OfferRows:*");
sw.Stop();
Log.Debug($"FlushCacheOffersAsync in {sw.Elapsed.TotalMilliseconds} ms");
return answ;
}
/// <summary>
/// Reset cache sistema x Ordini modalità async
/// </summary>
public async Task<bool> FlushCacheOrdersAsync()
{
bool answ = false;
Stopwatch sw = new Stopwatch();
sw.Start();
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:Orders:*");
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:OrderRows:*");
sw.Stop();
Log.Debug($"FlushCacheOrdersAsync in {sw.Elapsed.TotalMilliseconds} ms");
return answ;
}
/// <summary>
/// Esegue eliminazione + refresh cache
/// </summary>
/// <param name="selRec"></param>
/// <returns></returns>
public async Task<bool> GenClassDeleteAsync(GenClassModel selRec)
{
bool result = await dbController.GenClassDeleteAsync(selRec);
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:GenClass");
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:GenVal:*");
return result;
}
/// <summary>
/// Elenco completo GenClass
/// </summary>
/// <returns></returns>
public async Task<List<GenClassModel>> GenClassGetAllAsync()
{
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
List<GenClassModel>? result = new List<GenClassModel>();
// cerco in redis...
string currKey = $"{redisBaseKey}:GenClass";
RedisValue rawData = await redisDb.StringGetAsync(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<GenClassModel>>($"{rawData}");
source = "REDIS";
}
else
{
result = await dbController.GenClassGetAllAsync();
// serializzo e salvo con config x evitare loop...
rawData = JsonConvert.SerializeObject(result, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, LongCache);
}
if (result == null)
{
result = new List<GenClassModel>();
}
sw.Stop();
Log.Debug($"GenClassGetAllAsync | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// Esegue Upsert del record ricevuto
/// </summary>
/// <param name="upsRec"></param>
/// <returns></returns>
public async Task<bool> GenClassUpsertAsync(GenClassModel upsRec)
{
bool result = await dbController.GenClassUpsertAsync(upsRec);
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:GenClass");
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:GenVal:*");
return result;
}
/// <summary>
/// Esegue eliminazione + refresh cache
/// </summary>
/// <param name="selRec"></param>
/// <returns></returns>
public async Task<bool> GenValDeleteAsync(GenValueModel selRec)
{
bool result = await dbController.GenValDeleteAsync(selRec);
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:GenClass");
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:GenVal:*");
return result;
}
/// <summary>
/// Elenco valori x classe richiesta
/// </summary>
/// <param name="codClass"></param>
/// <returns></returns>
public async Task<List<GenValueModel>> GenValGetFiltAsync(string codClass)
{
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
List<GenValueModel>? result = new List<GenValueModel>();
// cerco in redis...
string currKey = $"{redisBaseKey}:GenVal:{codClass}";
RedisValue rawData = await redisDb.StringGetAsync(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<GenValueModel>>($"{rawData}");
source = "REDIS";
}
else
{
result = await dbController.GenValGetFiltAsync(codClass);
// serializzo e salvo con config x evitare loop...
rawData = JsonConvert.SerializeObject(result, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, LongCache);
}
if (result == null)
{
result = new List<GenValueModel>();
}
sw.Stop();
Log.Debug($"GenValGetFiltAsync | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// Esegue spostamento nell'ordinamento relativo alla classe + refresh cache
/// </summary>
/// <param name="selRec"></param>
/// <param name="moveUp"></param>
/// <returns></returns>
public async Task<bool> GenValMoveAsync(GenValueModel selRec, bool moveUp)
{
bool result = await dbController.GenValMoveAsync(selRec, moveUp);
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:GenClass");
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:GenVal:*");
return result;
}
/// <summary>
/// Esegue Upsert del record ricevuto
/// </summary>
/// <param name="upsRec"></param>
/// <returns></returns>
public async Task<bool> GenValUpsertAsync(GenValueModel upsRec)
{
bool result = await dbController.GenValUpsertAsync(upsRec);
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:GenClass");
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:GenVal:*");
return result;
}
/// <summary>
/// Eliminazione record item
/// </summary>
@@ -197,43 +567,6 @@ namespace EgwCoreLib.Lux.Data.Services
return result;
}
#if false
/// <summary>
/// Elenco item Child da ParentId (per sostituzione) async
/// </summary>
/// <param name="ItemId">ID corrente di cui cercare parent + fratelli</param>
/// <returns></returns>
public List<ItemModel> ItemGetChild(int ItemId)
{
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
List<ItemModel>? result = new List<ItemModel>();
// cerco in redis...
string currKey = $"{redisBaseKey}:Item:ListChild:{ItemId}";
RedisValue rawData = redisDb.StringGet(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<ItemModel>>($"{rawData}");
source = "REDIS";
}
else
{
result = dbController.ItemGetChild(ItemId);
// serializzo e salvo con config x evitare loop...
rawData = JsonConvert.SerializeObject(result, JSSettings);
redisDb.StringSet(currKey, rawData, FastCache);
}
if (result == null)
{
result = new List<ItemModel>();
}
sw.Stop();
Log.Debug($"ItemGetChild | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
#endif
/// <summary>
/// Elenco item da ricerca completa Async
/// </summary>
@@ -442,7 +775,27 @@ namespace EgwCoreLib.Lux.Data.Services
}
/// <summary>
/// Effettua update dei costi di tutte le righe dell'offerta indicata
/// Effettua fix UID righe child dell'offerta indicata e restituisce elenco UID da chiamare x refresh
/// </summary>
/// <param name="updRec">Key</param>
/// <returns></returns>
public async Task<List<string>> OffertRowFixUid(int OffertID)
{
List<string> answ = new List<string>();
Stopwatch sw = new Stopwatch();
sw.Start();
// calcolo
answ = dbController.OffertRowFixUid(OffertID);
// svuoto cache...
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:Offers:*");
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:OfferRows:*");
sw.Stop();
Log.Debug($"OffertRowFixUid in {sw.Elapsed.TotalMilliseconds} ms");
return answ;
}
/// <summary>
/// Effettua update della BOM (e dei costi) di tutte le righe dell'offerta indicata
/// </summary>
/// <param name="OfferRowID">ID riga offerta da aggiornare</param>
/// <param name="newBomList">Bom aggiornata da salvare</param>
@@ -461,6 +814,111 @@ namespace EgwCoreLib.Lux.Data.Services
return fatto;
}
/// <summary>
/// Effettua update delle info legate al file per la riga offerta
/// </summary>
/// <param name="updRec">IRiga offerta coin dati FILE da aggiornare</param>
/// <returns></returns>
public async Task<bool> OffertRowUpdateFileData(OfferRowModel updRec)
{
Stopwatch sw = new Stopwatch();
sw.Start();
// calcolo
bool fatto = await dbController.OffertRowUpdateFileData(updRec);
// svuoto cache...
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:Offers:*");
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:OfferRows:*");
sw.Stop();
Log.Debug($"OffertRowUpdateFileData in {sw.Elapsed.TotalMilliseconds} ms");
return fatto;
}
/// <summary>
/// Effettua update del valore serializzato della riga offerta
/// </summary>
/// <param name="OfferRowID">ID riga offerta da aggiornare</param>
/// <param name="serStruct">Serializzazione oggetto (es JWD)</param>
/// <returns></returns>
public async Task<bool> OffertRowUpdateSerStruct(int OfferRowID, string serStruct)
{
Stopwatch sw = new Stopwatch();
sw.Start();
// calcolo
bool fatto = await dbController.OffertRowUpdateSerStruct(OfferRowID, serStruct);
// svuoto cache...
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:Offers:*");
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:OfferRows:*");
sw.Stop();
Log.Debug($"OffertRowUpdateSerStruct in {sw.Elapsed.TotalMilliseconds} ms");
return fatto;
}
/// <summary>
/// Effettua eliminazione della riga offerta
/// </summary>
/// <param name="updRec">IRiga offerta coin dati FILE da aggiornare</param>
/// <returns></returns>
public async Task<bool> OffertRowDelete(OfferRowModel updRec)
{
Stopwatch sw = new Stopwatch();
sw.Start();
// calcolo
bool fatto = await dbController.OffertRowDelete(updRec);
// svuoto cache...
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:Offers:*");
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:OfferRows:*");
sw.Stop();
Log.Debug($"OffertRowDelete in {sw.Elapsed.TotalMilliseconds} ms");
return fatto;
}
/// <summary>
/// Effettua Upsert della riga offerta
/// </summary>
/// <param name="updRec">IRiga offerta coin dati FILE da aggiornare</param>
/// <returns></returns>
public async Task<bool> OffertRowUpsert(OfferRowModel updRec)
{
Stopwatch sw = new Stopwatch();
sw.Start();
// calcolo
bool fatto = await dbController.OffertRowUpsert(updRec);
// svuoto cache...
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:Offers:*");
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:OfferRows:*");
sw.Stop();
Log.Debug($"OffertRowUpsert in {sw.Elapsed.TotalMilliseconds} ms");
return fatto;
}
/// <summary>
/// Effettua update stato await BOM/PRICE per l'offerta indicata
/// </summary>
/// <param name="offerRowID">ID singola riga offerta</param>
/// <param name="awaitBom">Se non nullo è il nuovo stato await BOM</param>
/// <param name="awaitPrice">Se non nullo è stato await Price</param>
/// <param name="flushCache">Indica se svuotare la cache</param>
/// <returns></returns>
public async Task<bool> OffertUpdateAwaitState(int offerRowID, bool? awaitBom, bool? awaitPrice, bool flushCache = false)
{
Stopwatch sw = new Stopwatch();
sw.Start();
// aggiorno
bool fatto = await dbController.OffertUpdateAwaitState(offerRowID, awaitBom, awaitPrice);
if (flushCache)
{
// svuoto cache...
#if false
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:Offers:*");
#endif
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:OfferRows:*");
}
sw.Stop();
Log.Debug($"OffertUpdateAwaitState in {sw.Elapsed.TotalMilliseconds} ms");
return fatto;
}
/// <summary>
/// Effettua update dei costi di tutte le righe dell'offerta indicata
/// </summary>
@@ -480,6 +938,25 @@ namespace EgwCoreLib.Lux.Data.Services
return fatto;
}
/// <summary>
/// Effettua Upsert complessivo record offerta
/// </summary>
/// <param name="updRec">Key</param>
/// <returns></returns>
public async Task<bool> OffertUpsert(OfferModel updRec)
{
Stopwatch sw = new Stopwatch();
sw.Start();
// calcolo
bool fatto = await dbController.OffertUpsert(updRec);
// svuoto cache...
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:Offers:*");
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:OfferRows:*");
sw.Stop();
Log.Debug($"OffertUpsert in {sw.Elapsed.TotalMilliseconds} ms");
return fatto;
}
/// <summary>
/// Esegue salvataggio BOM sul DB
/// </summary>
@@ -498,7 +975,7 @@ namespace EgwCoreLib.Lux.Data.Services
var bomList = JsonConvert.DeserializeObject<List<BomItemDTO>>(bomContent);
if (bomList != null)
{
// verifico 1:1 gli item ricevuti dalla BOM sul DB con eventuale insert in anagrafica
// verifico 1:1 gli item ricevuti dalla BOM sul DB con eventuale insert in anagrafica dei nuovi
dbController.ItemUpsertFromBom(bomList);
// salvo la BOM nel record del DB relativo all'oggetto richiesto
dbController.OfferUpsertFromBom(uID, bomList);
@@ -732,5 +1209,48 @@ namespace EgwCoreLib.Lux.Data.Services
private string redisBaseKey = "Lux:Cache";
#endregion Private Fields
#region Private Properties
private static LuxController dbController { get; set; } = null!;
#endregion Private Properties
#if false
/// <summary>
/// Elenco item Child da ParentId (per sostituzione) async
/// </summary>
/// <param name="ItemId">ID corrente di cui cercare parent + fratelli</param>
/// <returns></returns>
public List<ItemModel> ItemGetChild(int ItemId)
{
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
List<ItemModel>? result = new List<ItemModel>();
// cerco in redis...
string currKey = $"{redisBaseKey}:Item:ListChild:{ItemId}";
RedisValue rawData = redisDb.StringGet(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<ItemModel>>($"{rawData}");
source = "REDIS";
}
else
{
result = dbController.ItemGetChild(ItemId);
// serializzo e salvo con config x evitare loop...
rawData = JsonConvert.SerializeObject(result, JSSettings);
redisDb.StringSet(currKey, rawData, FastCache);
}
if (result == null)
{
result = new List<ItemModel>();
}
sw.Stop();
Log.Debug($"ItemGetChild | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
#endif
}
}
@@ -12,10 +12,6 @@ namespace EgwCoreLib.Lux.Data.Services
/// </summary>
public interface IRedisService
{
#if false
IDatabase GetDatabase();
void Publish(string channel, string message);
#endif
long Publish(string channel, string message);
Task<long> PublishAsync(string channel, string message);
@@ -25,6 +25,8 @@ namespace EgwCoreLib.Lux.Data.Services
liveTag = _config.GetValue<string>("ServerConf:ImageLiveTag") ?? "svg";
cacheTag = _config.GetValue<string>("ServerConf:ImageFileTag") ?? "svgfile";
calcTag = _config.GetValue<string>("ServerConf:ImageCalcTag") ?? "svg-preview";
bomChannel = _config.GetValue<string>("ServerConf:BomChannel") ?? "Egw-bom";
updateChannel = _config.GetValue<string>("ServerConf:UpdateChannel") ?? "Egw-update";
// verifico la url base
apiUrl = _config.GetValue<string>("ServerConf:Prog.ApiUrl") ?? "https://iis01.egalware.com/lux/srv/api";
imgBasePath = _config.GetValue<string>("ServerConf:ImageBaseUrl") ?? "window";
@@ -114,11 +116,12 @@ namespace EgwCoreLib.Lux.Data.Services
public string ImageUrl(string baseUrl, bool isLive, string imgUID, EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW)
{
string tag = isLive ? liveTag : cacheTag;
if (!imgUID.EndsWith(".svg"))
if (imgUID.EndsWith(".svg"))
{
imgUID += ".svg";
imgUID.Replace(".svg","");
}
string fullUrl = $"{baseUrl}/{tag}/{imgUID}?envir={envir}".Replace("////", "//");
string rndImg = $"{DateTime.Now:HHmmssfff}";
string fullUrl = $"{baseUrl}/{tag}/{imgUID}-{rndImg}.svg?envir={envir}".Replace("////", "//");
return fullUrl;
}
@@ -168,13 +171,14 @@ namespace EgwCoreLib.Lux.Data.Services
}
/// <summary>
/// Salva e invia su channel img redis SVG per il doc richiesto
/// Salva e invia su channel BOM redis il doc richiesto
/// </summary>
/// <param name="uid"></param>
/// <param name="env"></param>
/// <param name="bomContent"></param>
public async Task<bool> SaveBomAsync(string uid, EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS env, string bomContent)
public async Task<bool> SaveBomAsync(string uid, Constants.EXECENVIRONMENTS env, string bomContent)
{
// salvo img in cache
// salvo in cache
string currKey = $"{redisBaseKey}:{env}:BOM:{uid.Replace("/", ":")}";
var done = await _redisService.SetAsync(currKey, bomContent);
// invio notifica nuova svg generata sul canale... viene inviato solo svg con ID nel canale
@@ -183,12 +187,29 @@ namespace EgwCoreLib.Lux.Data.Services
return done;
}
/// <summary>
/// Salva e invia su channel richiesto un update x UID
/// è attesao un refresh per chi sottoscrive il canale
/// </summary>
/// <param name="env"></param>
/// <param name="uid"></param>
public async Task<bool> PublishUpdateAsync(string uid, Constants.EXECENVIRONMENTS env)
{
bool done = false;
// invio notifica sul canale di update generale...
string notifyChannel = $"{updateChannel}:{env}";
// contenuto è UID
long numSent = await _redisService.PublishAsync(notifyChannel, $"{uid}");
done = numSent > 0;
return done;
}
/// <summary>
/// Salva e invia su channel img redis SVG per il doc richiesto
/// </summary>
/// <param name="uid"></param>
/// <param name="rawData"></param>
public async Task<bool> SaveHmlAsync(string uid, EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS env, string rawData)
public async Task<bool> SaveHmlAsync(string uid, Constants.EXECENVIRONMENTS env, string rawData)
{
// salvo img in cache
string currKey = $"{redisBaseKey}:{env}:HML:{uid.Replace("/", ":")}";
@@ -244,6 +265,7 @@ namespace EgwCoreLib.Lux.Data.Services
private readonly string apiUrl = "";
private readonly string bomChannel = "bom:item";
private readonly string updateChannel = "ui:update";
private readonly string cacheTag = "svgfile";
private readonly string calcTag = "svgpreview";
private readonly string hmlChannel = "hml:item";
+7
View File
@@ -7,6 +7,13 @@ Lbreria salvata come nuget SDK per accesso dati per EgalWare's LUX
Necessaria rpesenza configurazione DB/Redis in appsettings.json:
esempio dev ufficio:
```json
"ConnectionStrings": {
"DefaultConnection": "Server=mdb.ufficio;port=3306;database=Lux_000_dev_;uid=db_user;pwd=secure_password;sslmode=None;",
"Redis": "redis.ufficio:26379, serviceName=devel, DefaultDatabase=6, keepAlive=180, connectTimeout=15000, syncTimeout=15000, asyncTimeout=15000, abortConnect=false, ssl=false, allowAdmin=true"
},
```
esempio prod ufficio:
```json
"ConnectionStrings": {
"DefaultConnection": "Server=mdb.ufficio;port=3306;database=Lux_000;uid=db_user;pwd=secure_password;sslmode=None;",
+40 -9
View File
@@ -36,13 +36,7 @@ namespace Lux.API.Controllers
{
Stopwatch sw = new Stopwatch();
sw.Start();
string svgContent = "";
// se contiene ".svg" lo levo...
if (id.EndsWith(".svg"))
{
id = id.Replace(".svg", "");
}
string retVal = "";
// ...se ricevo percorso --> leggo jwd/svg cablato
if (currReq != null)
@@ -62,11 +56,48 @@ namespace Lux.API.Controllers
QuestionDTO currArgs = new QuestionDTO(nId, currReq.EnvType, DictExec);
await _redisService.PublishAsync(pubChannel, currArgs.sProcessArgs);
svgContent = "DONE";
retVal = "DONE";
}
sw.Stop();
Log.Info($"calcSvg | {sw.Elapsed.TotalMilliseconds:N3} ms");
return Ok(svgContent);
return Ok(retVal);
}
/// <summary>
/// Chiamata POST: riceve Json in formato JWD serializzato, invia richiesta calcolo modo 2 (BOM)
/// PUT: api/window/bom/00000000-0000-0000-0000-000000000000
/// </summary>
/// <param name="id">id oggetto</param>
/// <returns></returns>
[HttpPost("bom/{id}")]
public async Task<ActionResult<string>> getBom(string id, [FromBody] string currSer)
{
Stopwatch sw = new Stopwatch();
sw.Start();
string retVal = "";
// se messaggio vuoto --> uso default!
currSer = string.IsNullOrEmpty(currSer) ? "" : currSer;
// ...se ricevo percorso --> leggo jwd/svg cablato
if (!string.IsNullOrEmpty(currSer))
{
Dictionary<string, string> DictExec = new Dictionary<string, string>();
// cablata la BOM
DictExec.Add("Mode", "2");
// UID cablato x ora...
DictExec.Add("UID", id);
DictExec.Add("Jwd", currSer);
int nId = 1;
// da modificare con tipo richiesta...
QuestionDTO currArgs = new QuestionDTO(nId, EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW, DictExec);
await _redisService.PublishAsync(pubChannel, currArgs.sProcessArgs);
retVal = "DONE";
}
sw.Stop();
Log.Info($"getBom | {sw.Elapsed.TotalMilliseconds:N3} ms");
return Ok(retVal);
}
#endregion Public Methods
+48 -4
View File
@@ -1,4 +1,5 @@
using EgwCoreLib.Lux.Core;
using EgwCoreLib.Lux.Data.DbModel.Config;
using EgwCoreLib.Lux.Data.Services;
using EgwMultiEngineManager.Data;
using Microsoft.AspNetCore.Mvc;
@@ -13,11 +14,12 @@ namespace Lux.API.Controllers
{
#region Public Constructors
public WindowController(IConfiguration config, IRedisService redisService, ImageCacheService imgServ)
public WindowController(IConfiguration config, IRedisService redisService, ImageCacheService imgServ, ConfigDataService confServ)
{
_config = config;
_redisService = redisService;
_imgService = imgServ;
_confService = confServ;
pubChannel = _config.GetValue<string>("ServerConf:PubChannel") ?? "";
}
@@ -75,7 +77,7 @@ namespace Lux.API.Controllers
}
/// <summary>
/// Chiamata GET: riceve Json in formato serializzato, invia richiesta modo 3 (HardwareModelList)
/// Chiamata GET: invia richiesta modo 3 (HardwareModelList) che sarà poi salvata
/// GET: api/window/hwlist
/// </summary>
/// <param name="id">id oggetto</param>
@@ -86,6 +88,19 @@ namespace Lux.API.Controllers
Stopwatch sw = new Stopwatch();
sw.Start();
string hwContent = "";
await sendHwReq();
hwContent = "DONE";
sw.Stop();
Log.Info($"getHardwareList | {sw.Elapsed.TotalMilliseconds:N3} ms");
return Ok(hwContent);
}
/// <summary>
/// Esegue invio effettivo richiesta elenco HW list
/// </summary>
/// <returns></returns>
private async Task sendHwReq()
{
Dictionary<string, string> DictExec = new Dictionary<string, string>();
DictExec.Add("Mode", $"{(int)Enums.EngineQueryType.HardwareModelList}");
// da rivedere?
@@ -96,10 +111,38 @@ namespace Lux.API.Controllers
// da modificare con tipo richiesta...
QuestionDTO currArgs = new QuestionDTO(nId, EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW, DictExec);
await _redisService.PublishAsync(pubChannel, currArgs.sProcessArgs);
hwContent = "DONE";
}
/// <summary>
/// Chiamata GET:
/// - se trova in cache risponde con elenco hw già ricevuto
/// - se non trova invia richiesta modo 3 (HardwareModelList) che sarà poi salvata
/// GET: api/window/hwlist/nome_produttore
/// </summary>
/// <param name="id">nome del produttore, es HW.AGB (default)</param>
/// <returns></returns>
[HttpGet("hwlist/{id}")]
public async Task<ActionResult<List<HardwareModel>>> getHardwareList(string id)
{
Stopwatch sw = new Stopwatch();
sw.Start();
string hwReq = id ?? "HW.AGB";
var answ = _confService.ElencoHw(EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW, hwReq);
if (answ == null || answ.Count == 0)
{
await sendHwReq();
answ = new List<HardwareModel>();
}
// filtro quelli default/non significativi (famili=descript)
else
{
answ = answ
.Where(x => !x.FamilyName.Equals(x.Description, StringComparison.OrdinalIgnoreCase))
.ToList();
}
sw.Stop();
Log.Info($"getHardwareList | {sw.Elapsed.TotalMilliseconds:N3} ms");
return Ok(hwContent);
return Ok(answ);
}
/// <summary>
@@ -224,6 +267,7 @@ namespace Lux.API.Controllers
#region Private Properties
private ImageCacheService _imgService { get; set; }
private ConfigDataService _confService { get; set; }
#endregion Private Properties
}
+1 -3
View File
@@ -4,7 +4,7 @@
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Version>0.9.2509.1807</Version>
<Version>0.9.2510.2012</Version>
</PropertyGroup>
<ItemGroup>
@@ -65,8 +65,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Egw.Lux.WebWindow.Base" Version="2.7.9-beta.1313" />
<PackageReference Include="EgwMultiEngineManager.Data" Version="2.7.9-beta.1" />
<PackageReference Include="NLog" Version="6.0.1" />
<PackageReference Include="NLog.Web.AspNetCore" Version="6.0.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
+1
View File
@@ -48,6 +48,7 @@ builder.Services.AddSingleton<ImageCacheService>();
builder.Services.AddSingleton<DataLayerServices>();
builder.Services.AddSingleton<ExternalMessageProcessor>();
builder.Services.AddHostedService<RedisSubscriberService>();
builder.Services.AddSingleton<ConfigDataService>();
var app = builder.Build();
+4 -1
View File
@@ -59,6 +59,9 @@ namespace Lux.API.Services
await cacheService.SaveBomAsync(UID, retData.ExecEnvironment, newBom);
// salvo la BOM completa con i dati recuperati dal DB
await dbService.SaveBomAsync(UID, retData.ExecEnvironment, newBom);
// pubblico refresh info così da segnalare richeista di update conseguente
await cacheService.PublishUpdateAsync(UID, retData.ExecEnvironment);
}
if (retData.Args.ContainsKey("HardwareModelList"))
{
@@ -90,9 +93,9 @@ namespace Lux.API.Services
#region Private Fields
private static Logger Log = LogManager.GetCurrentClassLogger();
private readonly ImageCacheService cacheService;
private readonly DataLayerServices dbService;
private static Logger Log = LogManager.GetCurrentClassLogger();
#endregion Private Fields
+3 -2
View File
@@ -7,7 +7,8 @@
},
"ServerConf": {
"BaseUrl": "/lux/srv/",
"PubChannel": "EgwEngineInput",
"SubChannel": "EgwEngineOutput"
"PubChannel": "EgwDevEngineInput",
"SubChannel": "EgwDevEngineOutput",
"ImageBaseUrl": "https://iis01.egalware.com/lux/srv/api/window/"
}
}
+2 -1
View File
@@ -8,6 +8,7 @@
"ServerConf": {
"BaseUrl": "/lux/srv/",
"PubChannel": "EgwEngineInput",
"SubChannel": "EgwEngineOutput"
"SubChannel": "EgwEngineOutput",
"ImageBaseUrl": "https://office.egalware.com/lux/srv/api/window/"
}
}
+2 -4
View File
@@ -7,9 +7,7 @@
},
"ServerConf": {
"BaseUrl": "/lux/srv/",
//"PubChannel": "EgwEngineInput",
//"SubChannel": "EgwEngineOutput",
"PubChannel": "EgwDevEngineInput",
"SubChannel": "EgwDevEngineOutput"
"PubChannel": "EgwEngineInput",
"SubChannel": "EgwEngineOutput"
}
}
+4 -2
View File
@@ -50,15 +50,17 @@
},
"AllowedHosts": "*",
"ConnectionStrings": {
//"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-Lux.UI-a758c101-a2f4-4e38-977d-1c4887dbbd50;Trusted_Connection=True;MultipleActiveResultSets=true",
"DefaultConnection": "Server=mdb.ufficio;port=3306;database=Lux_000;uid=lux_user;pwd=Egal_pwd!;sslmode=None;",
"Lux.All": "Server=SQL2016DEV;Database=MoonPro_STATS; User ID=sa;Password=keyhammer16; integrated security=False; MultipleActiveResultSets=True; App=MP.Land;",
"Redis": "redis.ufficio:26379, serviceName=devel, DefaultDatabase=6, keepAlive=180, connectTimeout=15000, syncTimeout=15000, asyncTimeout=15000, abortConnect=false, ssl=false, allowAdmin=true"
},
"ServerConf": {
"CalcTag": "calc",
"PubChannel": "EgwEngineInput",
"SubChannel": "EgwEngineOutput",
"SvgChannel": "svg:img",
"SvgChannel": "Egw:svg:img",
"BomChannel": "Egw:bom",
"UpdateChannel": "Egw:update",
"BaseUrl": "/lux/srv/",
"ImageBaseUrl": "https://iis01.egalware.com/lux/srv/api/window/",
"ImageLiveTag": "svg",
-6
View File
@@ -7,8 +7,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EgwCoreLib.Lux.Core", "EgwC
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EgwCoreLib.Lux.Data", "EgwCoreLib.Lux.Data\EgwCoreLib.Lux.Data.csproj", "{9BA9FBAC-0D76-41CA-9970-A8DB982CC5F0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApp", "TestApp\TestApp.csproj", "{E94496AD-22BB-4443-9A81-11D19AFBE0A3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lux.UI", "Lux.UI\Lux.UI.csproj", "{A3B8FAE0-3BAD-4402-B4D9-26DD9CB777E5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lux.UI.Client", "Lux.UI.Client\Lux.UI.Client.csproj", "{ECA4B6A4-692B-41EB-8238-C33C5FB71942}"
@@ -29,10 +27,6 @@ Global
{9BA9FBAC-0D76-41CA-9970-A8DB982CC5F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9BA9FBAC-0D76-41CA-9970-A8DB982CC5F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9BA9FBAC-0D76-41CA-9970-A8DB982CC5F0}.Release|Any CPU.Build.0 = Release|Any CPU
{E94496AD-22BB-4443-9A81-11D19AFBE0A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E94496AD-22BB-4443-9A81-11D19AFBE0A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E94496AD-22BB-4443-9A81-11D19AFBE0A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E94496AD-22BB-4443-9A81-11D19AFBE0A3}.Release|Any CPU.Build.0 = Release|Any CPU
{A3B8FAE0-3BAD-4402-B4D9-26DD9CB777E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A3B8FAE0-3BAD-4402-B4D9-26DD9CB777E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A3B8FAE0-3BAD-4402-B4D9-26DD9CB777E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+1 -1
View File
@@ -9,7 +9,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Egw.Lux.WebWindow.Base" Version="2.7.9-beta.1313" />
<PackageReference Include="Egw.Lux.WebWindowComplex" Version="2.7.10.1710" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.17" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.17" />
</ItemGroup>
@@ -0,0 +1,81 @@
<div class="card shadow">
<div class="card-header">
<div class="d-flex justify-content-between">
<div class="px-0">
<h5>Conf. Vetro</h5>
</div>
<div class="px-0">
<div class="px-0 d-flex justify-content-between">
<div class="px-1">
<div class="input-group input-group-sm" title="ricerca">
<span class="input-group-text"><i class="fas fa-search"></i></span>
<input type="text" class="form-control" @bind="@SearchVal">
<button class="btn btn-outline-secondary" @onclick="ResetSearch"><i class="fas fa-ban"></i></button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card-body p-1">
@if (isLoading || ListRecords == null)
{
<LoadingData></LoadingData>
}
else if (totalCount == 0)
{
<div class="alert alert-info text-center display-6">Nessun record trovato</div>
}
else
{
<table class="table table-sm table-striped">
<thead>
<tr>
<th class="text-nowrap text-center">
<button class="btn btn-sm btn-primary" title="Reset selezione" @onclick="DoReset"><i class="fa-solid fa-arrow-rotate-right"></i></button>
</th>
@* <th>ID</th> *@
<th class="text-nowrap">Cod.</th>
<th class="w-100">Descrizione</th>
<th class="text-nowrap text-end">Size mm</th>
<th class="text-nowrap text-end">
<button class="btn btn-sm btn-success" @onclick="DoAdd"><i class="fa-solid fa-plus"></i></button>
</th>
</tr>
</thead>
<tbody>
@foreach (var item in ListRecords)
{
<tr>
<td class="text-center">@item.GlassID</td>
<td class="">@item.Code</td>
<td class="w-100">@item.Description</td>
<td class="text-end">@($"{item.Thickness:N2}")</td>
<td class="text-nowrap text-end">
@if (false)
{
<button class="btn btn-sm btn-danger" @onclick="() => DoDelete(item)"><i class="fa-solid fa-trash-can"></i></button>
}
else
{
<button class="btn btn-sm btn-secondary" disabled><i class="fa-solid fa-trash-can"></i></button>
}
</td>
</tr>
}
</tbody>
@if (totalCount >= numRecord)
{
<tfoot>
<tr>
<td colspan="15">
<EgwCoreLib.Razor.DataPager currPage="@currPage" PageSize="@numRecord" totalCount="@totalCount" numPageChanged="SavePage" numRecordChanged="SaveNumRec"></EgwCoreLib.Razor.DataPager>
</td>
</tr>
</tfoot>
}
</table>
}
</div>
</div>
@@ -0,0 +1,199 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using EgwCoreLib.Lux.Data.DbModel.Config;
using EgwCoreLib.Lux.Data.Services;
namespace Lux.UI.Components.Compo.Config
{
public partial class GlassMan
{
#region Protected Properties
[Inject]
protected DataLayerServices DLService { get; set; } = null!;
[Inject]
protected IJSRuntime JSRuntime { get; set; } = null!;
protected string SearchVal
{
get => searchVal;
set
{
if (searchVal != value)
{
searchVal = value;
_ = FullUpdate();
}
}
}
#endregion Protected Properties
#region Protected Methods
/// <summary>
/// impossta record x eliminazione
/// </summary>
/// <param name="selRec"></param>
protected async Task DoDelete(GlassModel selRec)
{
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Sicuro di voler eliminare il record? Dettagli: {selRec.GlassID} | {selRec.Description} | {selRec.Thickness}"))
return;
// esegue eliminazione del record...
await DLService.ConfGlassDeleteAsync(selRec);
EditRecord = null;
SelRecord = null;
await ReloadData();
UpdateTable();
}
/// <summary>
/// Edit articolo selezionato
/// </summary>
/// <param name="curRec"></param>
protected void DoEdit(GlassModel curRec)
{
EditRecord = curRec;
}
/// <summary>
/// Reset selezione
/// </summary>
protected void DoReset()
{
EditRecord = null;
}
/// <summary>
/// Selezione articolo x display info
/// </summary>
/// <param name="curRec"></param>
protected void DoSelect(GlassModel curRec)
{
SelRecord = curRec;
}
protected override async Task OnParametersSetAsync()
{
await ReloadData();
UpdateTable();
}
protected void ResetSearch()
{
SearchVal = "";
}
protected void SaveNumRec(int newNum)
{
numRecord = newNum;
UpdateTable();
}
protected void SavePage(int newNum)
{
currPage = newNum;
UpdateTable();
}
#endregion Protected Methods
#region Private Fields
private List<GlassModel> AllRecords = new();
private int currPage = 1;
private GlassModel? EditRecord = null;
private bool isLoading = false;
private List<GlassModel> ListRecords = new();
private int numRecord = 5;
private GlassModel? SelRecord = null;
private int totalCount = 0;
#endregion Private Fields
#region Private Properties
private string searchVal { get; set; } = string.Empty;
#endregion Private Properties
#region Private Methods
private async Task DoAdd()
{
// aggiungo un nuovo record in coda...
EditRecord = new GlassModel()
{
Description = "New Glass",
Code = "",
Thickness = 30
};
await DoSave(EditRecord);
}
private async Task DoSave(GlassModel currRec)
{
// salvo
await DLService.ConfGlassUpsertAsync(currRec);
await ResetEdit();
UpdateTable();
EditRecord = null;
SelRecord = null;
}
private async Task FullUpdate()
{
await ReloadData();
UpdateTable();
await InvokeAsync(StateHasChanged);
}
private async Task ReloadData()
{
isLoading = true;
AllRecords = await DLService.ConfGlassGetAllAsync();
// se ho ricerca testuale faccio filtro ulteriore...
if (string.IsNullOrEmpty(SearchVal))
{
AllRecords = AllRecords
.OrderBy(x => x.Description)
.ToList();
}
else
{
AllRecords = AllRecords
.Where(x =>
x.Description.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase) ||
x.Code.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase))
.OrderBy(x => x.Description)
.ToList();
}
totalCount = AllRecords.Count;
}
private async Task ResetEdit()
{
// reset edit
EditRecord = null;
await ReloadData();
}
/// <summary>
/// Filtro e paginazione
/// </summary>
private void UpdateTable()
{
// fix paginazione
ListRecords = AllRecords
.Skip(numRecord * (currPage - 1))
.Take(numRecord)
.ToList();
isLoading = false;
}
#endregion Private Methods
}
}
@@ -0,0 +1,74 @@
<div class="card shadow">
<div class="card-header">
<div class="d-flex justify-content-between">
<div class="px-0">
<h5>Conf. Hardware</h5>
</div>
<div class="px-0">
<div class="px-0 d-flex justify-content-between">
<div class="px-1">
<div class="input-group input-group-sm" title="ricerca">
<span class="input-group-text"><i class="fas fa-search"></i></span>
<input type="text" class="form-control" @bind="@SearchVal">
<button class="btn btn-outline-secondary" @onclick="ResetSearch"><i class="fas fa-ban"></i></button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card-body p-1">
@if (isLoading || ListRecords == null)
{
<LoadingData></LoadingData>
}
else if (totalCount == 0)
{
<div class="alert alert-info text-center display-6">Nessun record trovato</div>
}
else
{
<table class="table table-sm table-striped">
<thead>
<tr>
<th class="text-nowrap text-center">
<button class="btn btn-sm btn-primary" title="Reset selezione" @onclick="DoReset"><i class="fa-solid fa-arrow-rotate-right"></i></button>
</th>
@* <th>ID</th> *@
<th class="text-nowrap">Fam.</th>
<th class="w-100">Descrizione</th>
<th class="text-end">Shape</th>
@* <th class="text-nowrap text-end">
<button class="btn btn-sm btn-success" @onclick="DoAdd"><i class="fa-solid fa-plus"></i></button>
</th> *@
</tr>
</thead>
<tbody>
@foreach (var item in ListRecords)
{
<tr>
<td class="text-center">@item.Id</td>
<td class="text-nowrap">@item.FamilyName</td>
<td class="w-100">@item.Description</td>
<td class="text-end">@item.Shape</td>
</tr>
}
</tbody>
@if (totalCount >= numRecord)
{
<tfoot>
<tr>
<td colspan="15">
<EgwCoreLib.Razor.DataPager currPage="@currPage" PageSize="@numRecord" totalCount="@totalCount" numPageChanged="SavePage" numRecordChanged="SaveNumRec"></EgwCoreLib.Razor.DataPager>
</td>
</tr>
</tfoot>
}
</table>
}
</div>
</div>
@@ -0,0 +1,199 @@
using EgwCoreLib.Lux.Data.DbModel.Config;
using EgwCoreLib.Lux.Data.Services;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace Lux.UI.Components.Compo.Config
{
public partial class HardwareMan
{
#region Protected Properties
[Inject]
protected ConfigDataService CDService { get; set; } = null!;
[Inject]
protected IJSRuntime JSRuntime { get; set; } = null!;
protected string SearchVal
{
get => searchVal;
set
{
if (searchVal != value)
{
searchVal = value;
ReloadData();
UpdateTable();
}
}
}
#endregion Protected Properties
#region Protected Methods
/// <summary>
/// Reset selezione
/// </summary>
protected void DoReset()
{
EditRecord = null;
SelRecord = null;
}
/// <summary>
/// Selezione articolo x display info
/// </summary>
/// <param name="curRec"></param>
protected void DoSelect(HardwareModel curRec)
{
SelRecord = curRec;
}
protected override void OnParametersSet()
{
ReloadData();
UpdateTable();
}
protected void ResetSearch()
{
SearchVal = "";
}
protected void SaveNumRec(int newNum)
{
numRecord = newNum;
UpdateTable();
}
protected void SavePage(int newNum)
{
currPage = newNum;
UpdateTable();
}
#endregion Protected Methods
#region Private Fields
private List<HardwareModel> AllRecords = new();
private int currPage = 1;
private HardwareModel? EditRecord = null;
private bool isLoading = false;
private List<HardwareModel> ListRecords = new();
private int numRecord = 5;
private HardwareModel? SelRecord = null;
private int totalCount = 0;
#endregion Private Fields
#region Private Properties
private string searchVal { get; set; } = string.Empty;
#endregion Private Properties
#if false
/// <summary>
/// impossta record x eliminazione
/// </summary>
/// <param name="selRec"></param>
protected async Task DoDelete(HardwareModel selRec)
{
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Sicuro di voler eliminare il record? Dettagli: {selRec.HardwareID} | {selRec.Description} | {selRec.Thickness}"))
return;
// esegue eliminazione del record...
await CDService.ConfHardwareDeleteAsync(selRec);
EditRecord = null;
SelRecord = null;
await ReloadData();
UpdateTable();
}
/// <summary>
/// Edit articolo selezionato
/// </summary>
/// <param name="curRec"></param>
protected void DoEdit(HardwareModel curRec)
{
EditRecord = curRec;
}
#endif
#if false
private async Task DoAdd()
{
// aggiungo un nuovo record in coda...
EditRecord = new HardwareModel()
{
Description = "New Glass",
Code = "",
Thickness = 30
};
await DoSave(EditRecord);
}
private async Task DoSave(HardwareModel currRec)
{
// salvo
await CDService.ConfHardwareUpsertAsync(currRec);
await ResetEdit();
UpdateTable();
EditRecord = null;
SelRecord = null;
}
#endif
#region Private Methods
private void ReloadData()
{
isLoading = true;
AllRecords = CDService.ElencoHw(EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW, "HW.AGB");
// se ho ricerca testuale faccio filtro ulteriore...
if (string.IsNullOrEmpty(SearchVal))
{
AllRecords = AllRecords
.OrderBy(x => x.Description)
.ToList();
}
else
{
AllRecords = AllRecords
.Where(x =>
x.Description.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase) ||
x.FamilyName.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase))
.OrderBy(x => x.Description)
.ToList();
}
totalCount = AllRecords.Count;
}
#if false
private void ResetEdit()
{
// reset edit
EditRecord = null;
ReloadData();
}
#endif
/// <summary>
/// Filtro e paginazione
/// </summary>
private void UpdateTable()
{
// fix paginazione
ListRecords = AllRecords
.Skip(numRecord * (currPage - 1))
.Take(numRecord)
.ToList();
isLoading = false;
}
#endregion Private Methods
}
}
@@ -0,0 +1,81 @@
<div class="card shadow">
<div class="card-header">
<div class="d-flex justify-content-between">
<div class="px-0">
<h5>Conf. Profilo</h5>
</div>
<div class="px-0">
<div class="px-0 d-flex justify-content-between">
<div class="px-1">
<div class="input-group input-group-sm" title="ricerca">
<span class="input-group-text"><i class="fas fa-search"></i></span>
<input type="text" class="form-control" @bind="@SearchVal">
<button class="btn btn-outline-secondary" @onclick="ResetSearch"><i class="fas fa-ban"></i></button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card-body p-1">
@if (isLoading || ListRecords == null)
{
<LoadingData></LoadingData>
}
else if (totalCount == 0)
{
<div class="alert alert-info text-center display-6">Nessun record trovato</div>
}
else
{
<table class="table table-sm table-striped">
<thead>
<tr>
<th class="text-nowrap text-center">
<button class="btn btn-sm btn-primary" title="Reset selezione" @onclick="DoReset"><i class="fa-solid fa-arrow-rotate-right"></i></button>
</th>
@* <th>ID</th> *@
<th class="text-nowrap">Cod.</th>
<th class="w-100">Descrizione</th>
<th class="text-nowrap text-end">Size mm</th>
<th class="text-nowrap text-end">
<button class="btn btn-sm btn-success" @onclick="DoAdd"><i class="fa-solid fa-plus"></i></button>
</th>
</tr>
</thead>
<tbody>
@foreach (var item in ListRecords)
{
<tr>
<td class="text-center">@item.ProfileID</td>
<td class="">@item.Code</td>
<td class="w-100">@item.Description</td>
<td class="text-end">@($"{item.Thickness:N2}")</td>
<td class="text-nowrap text-end">
@if (false)
{
<button class="btn btn-sm btn-danger" @onclick="() => DoDelete(item)"><i class="fa-solid fa-trash-can"></i></button>
}
else
{
<button class="btn btn-sm btn-secondary" disabled><i class="fa-solid fa-trash-can"></i></button>
}
</td>
</tr>
}
</tbody>
@if (totalCount >= numRecord)
{
<tfoot>
<tr>
<td colspan="15">
<EgwCoreLib.Razor.DataPager currPage="@currPage" PageSize="@numRecord" totalCount="@totalCount" numPageChanged="SavePage" numRecordChanged="SaveNumRec"></EgwCoreLib.Razor.DataPager>
</td>
</tr>
</tfoot>
}
</table>
}
</div>
</div>
@@ -0,0 +1,199 @@
using EgwCoreLib.Lux.Data.DbModel.Config;
using EgwCoreLib.Lux.Data.Services;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace Lux.UI.Components.Compo.Config
{
public partial class ProfileMan
{
#region Protected Properties
[Inject]
protected DataLayerServices DLService { get; set; } = null!;
[Inject]
protected IJSRuntime JSRuntime { get; set; } = null!;
protected string SearchVal
{
get => searchVal;
set
{
if (searchVal != value)
{
searchVal = value;
_ = FullUpdate();
}
}
}
#endregion Protected Properties
#region Protected Methods
/// <summary>
/// impossta record x eliminazione
/// </summary>
/// <param name="selRec"></param>
protected async Task DoDelete(ProfileModel selRec)
{
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Sicuro di voler eliminare il record? Dettagli: {selRec.ProfileID} | {selRec.Description} | {selRec.Thickness}"))
return;
// esegue eliminazione del record...
await DLService.ConfProfileDeleteAsync(selRec);
EditRecord = null;
SelRecord = null;
await ReloadData();
UpdateTable();
}
/// <summary>
/// Edit articolo selezionato
/// </summary>
/// <param name="curRec"></param>
protected void DoEdit(ProfileModel curRec)
{
EditRecord = curRec;
}
/// <summary>
/// Reset selezione
/// </summary>
protected void DoReset()
{
EditRecord = null;
}
/// <summary>
/// Selezione articolo x display info
/// </summary>
/// <param name="curRec"></param>
protected void DoSelect(ProfileModel curRec)
{
SelRecord = curRec;
}
protected override async Task OnParametersSetAsync()
{
await ReloadData();
UpdateTable();
}
protected void ResetSearch()
{
SearchVal = "";
}
protected void SaveNumRec(int newNum)
{
numRecord = newNum;
UpdateTable();
}
protected void SavePage(int newNum)
{
currPage = newNum;
UpdateTable();
}
#endregion Protected Methods
#region Private Fields
private List<ProfileModel> AllRecords = new();
private int currPage = 1;
private ProfileModel? EditRecord = null;
private bool isLoading = false;
private List<ProfileModel> ListRecords = new();
private int numRecord = 5;
private ProfileModel? SelRecord = null;
private int totalCount = 0;
#endregion Private Fields
#region Private Properties
private string searchVal { get; set; } = string.Empty;
#endregion Private Properties
#region Private Methods
private async Task DoAdd()
{
// aggiungo un nuovo record in coda...
EditRecord = new ProfileModel()
{
Description = "New Glass",
Code = "",
Thickness = 30
};
await DoSave(EditRecord);
}
private async Task DoSave(ProfileModel currRec)
{
// salvo
await DLService.ConfProfileUpsertAsync(currRec);
await ResetEdit();
UpdateTable();
EditRecord = null;
SelRecord = null;
}
private async Task FullUpdate()
{
await ReloadData();
UpdateTable();
await InvokeAsync(StateHasChanged);
}
private async Task ReloadData()
{
isLoading = true;
AllRecords = await DLService.ConfProfileGetAllAsync();
// se ho ricerca testuale faccio filtro ulteriore...
if (string.IsNullOrEmpty(SearchVal))
{
AllRecords = AllRecords
.OrderBy(x => x.Description)
.ToList();
}
else
{
AllRecords = AllRecords
.Where(x =>
x.Description.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase) ||
x.Code.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase))
.OrderBy(x => x.Description)
.ToList();
}
totalCount = AllRecords.Count;
}
private async Task ResetEdit()
{
// reset edit
EditRecord = null;
await ReloadData();
}
/// <summary>
/// Filtro e paginazione
/// </summary>
private void UpdateTable()
{
// fix paginazione
ListRecords = AllRecords
.Skip(numRecord * (currPage - 1))
.Take(numRecord)
.ToList();
isLoading = false;
}
#endregion Private Methods
}
}
@@ -0,0 +1,81 @@
<div class="card shadow">
<div class="card-header">
<div class="d-flex justify-content-between">
<div class="px-0">
<h5>Conf. Legno</h5>
</div>
<div class="px-0">
<div class="px-0 d-flex justify-content-between">
<div class="px-1">
<div class="input-group input-group-sm" title="ricerca">
<span class="input-group-text"><i class="fas fa-search"></i></span>
<input type="text" class="form-control" @bind="@SearchVal">
<button class="btn btn-outline-secondary" @onclick="ResetSearch"><i class="fas fa-ban"></i></button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card-body p-1">
@if (isLoading || ListRecords == null)
{
<LoadingData></LoadingData>
}
else if (totalCount == 0)
{
<div class="alert alert-info text-center display-6">Nessun record trovato</div>
}
else
{
<table class="table table-sm table-striped">
<thead>
<tr>
<th class="text-nowrap text-center">
<button class="btn btn-sm btn-primary" title="Reset selezione" @onclick="DoReset"><i class="fa-solid fa-arrow-rotate-right"></i></button>
</th>
@* <th>ID</th> *@
<th class="text-nowrap">Cod.</th>
<th class="w-100">Descrizione</th>
<th class="text-nowrap text-end">Tipo</th>
<th class="text-nowrap text-end">
<button class="btn btn-sm btn-success" @onclick="DoAdd"><i class="fa-solid fa-plus"></i></button>
</th>
</tr>
</thead>
<tbody>
@foreach (var item in ListRecords)
{
<tr>
<td class="text-center">@item.WoodID</td>
<td class="">@item.Code</td>
<td class="w-100">@item.Description</td>
<td class="text-end">@item.Type</td>
<td class="text-nowrap text-end">
@if (false)
{
<button class="btn btn-sm btn-danger" @onclick="() => DoDelete(item)"><i class="fa-solid fa-trash-can"></i></button>
}
else
{
<button class="btn btn-sm btn-secondary" disabled><i class="fa-solid fa-trash-can"></i></button>
}
</td>
</tr>
}
</tbody>
@if (totalCount >= numRecord)
{
<tfoot>
<tr>
<td colspan="15">
<EgwCoreLib.Razor.DataPager currPage="@currPage" PageSize="@numRecord" totalCount="@totalCount" numPageChanged="SavePage" numRecordChanged="SaveNumRec"></EgwCoreLib.Razor.DataPager>
</td>
</tr>
</tfoot>
}
</table>
}
</div>
</div>
@@ -0,0 +1,199 @@
using EgwCoreLib.Lux.Data.DbModel.Config;
using EgwCoreLib.Lux.Data.Services;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace Lux.UI.Components.Compo.Config
{
public partial class WoodMan
{
#region Protected Properties
[Inject]
protected DataLayerServices DLService { get; set; } = null!;
[Inject]
protected IJSRuntime JSRuntime { get; set; } = null!;
protected string SearchVal
{
get => searchVal;
set
{
if (searchVal != value)
{
searchVal = value;
_ = FullUpdate();
}
}
}
#endregion Protected Properties
#region Protected Methods
/// <summary>
/// impossta record x eliminazione
/// </summary>
/// <param name="selRec"></param>
protected async Task DoDelete(WoodModel selRec)
{
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Sicuro di voler eliminare il record? Dettagli: {selRec.WoodID} | {selRec.Description} | Tipo: {selRec.Type}"))
return;
// esegue eliminazione del record...
await DLService.ConfWoodDeleteAsync(selRec);
EditRecord = null;
SelRecord = null;
await ReloadData();
UpdateTable();
}
/// <summary>
/// Edit articolo selezionato
/// </summary>
/// <param name="curRec"></param>
protected void DoEdit(WoodModel curRec)
{
EditRecord = curRec;
}
/// <summary>
/// Reset selezione
/// </summary>
protected void DoReset()
{
EditRecord = null;
}
/// <summary>
/// Selezione articolo x display info
/// </summary>
/// <param name="curRec"></param>
protected void DoSelect(WoodModel curRec)
{
SelRecord = curRec;
}
protected override async Task OnParametersSetAsync()
{
await ReloadData();
UpdateTable();
}
protected void ResetSearch()
{
SearchVal = "";
}
protected void SaveNumRec(int newNum)
{
numRecord = newNum;
UpdateTable();
}
protected void SavePage(int newNum)
{
currPage = newNum;
UpdateTable();
}
#endregion Protected Methods
#region Private Fields
private List<WoodModel> AllRecords = new();
private int currPage = 1;
private WoodModel? EditRecord = null;
private bool isLoading = false;
private List<WoodModel> ListRecords = new();
private int numRecord = 5;
private WoodModel? SelRecord = null;
private int totalCount = 0;
#endregion Private Fields
#region Private Properties
private string searchVal { get; set; } = string.Empty;
#endregion Private Properties
#region Private Methods
private async Task DoAdd()
{
// aggiungo un nuovo record in coda...
EditRecord = new WoodModel()
{
Description = "New Wood",
Code = "",
Type = 1
};
await DoSave(EditRecord);
}
private async Task DoSave(WoodModel currRec)
{
// salvo
await DLService.ConfWoodUpsertAsync(currRec);
await ResetEdit();
UpdateTable();
EditRecord = null;
SelRecord = null;
}
private async Task FullUpdate()
{
await ReloadData();
UpdateTable();
await InvokeAsync(StateHasChanged);
}
private async Task ReloadData()
{
isLoading = true;
AllRecords = await DLService.ConfWoodGetAllAsync();
// se ho ricerca testuale faccio filtro ulteriore...
if (string.IsNullOrEmpty(SearchVal))
{
AllRecords = AllRecords
.OrderBy(x => x.Description)
.ToList();
}
else
{
AllRecords = AllRecords
.Where(x =>
x.Description.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase) ||
x.Code.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase))
.OrderBy(x => x.Description)
.ToList();
}
totalCount = AllRecords.Count;
}
private async Task ResetEdit()
{
// reset edit
EditRecord = null;
await ReloadData();
}
/// <summary>
/// Filtro e paginazione
/// </summary>
private void UpdateTable()
{
// fix paginazione
ListRecords = AllRecords
.Skip(numRecord * (currPage - 1))
.Take(numRecord)
.ToList();
isLoading = false;
}
#endregion Private Methods
}
}
+1 -1
View File
@@ -15,7 +15,7 @@
<tbody>
@foreach (var item in bomDict)
{
@if (item.Key == currIdx && EditRecord != null)
@if (EditRecord != null && item.Key == currIdx)
{
<tr class="table-info">
<td>@(item.Key + 1)</td>
+1 -1
View File
@@ -1,5 +1,5 @@
using EgwCoreLib.Lux.Core.RestPayload;
using EgwCoreLib.Lux.Data.DbModel;
using EgwCoreLib.Lux.Data.DbModel.Items;
using EgwCoreLib.Lux.Data.Services;
using EgwMultiEngineManager.Data;
using Microsoft.AspNetCore.Components;
+112
View File
@@ -0,0 +1,112 @@
@if (AddVisible)
{
<div class="modal" tabindex="-1" style="display:block; background-color: rgba(10,10,10,.6);" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<div class="modal-title fs-4">Aggiunta Classe Anagrafica</div>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="ToggleAdd">
</button>
</div>
<div class="modal-body">
@if (NewRecord != null)
{
<div class="row">
<div class="input-group mb-2">
<span class="input-group-text" id="basic-addon1">Codice</span>
<input type="text" class="form-control text-end" @bind="@NewRecord.ClassCod" />
</div>
</div>
<div class="row">
<div class="input-group mb-2">
<span class="input-group-text" id="basic-addon1">Descrizione</span>
<input type="text" class="form-control text-end" @bind="@NewRecord.Description" />
</div>
</div>
}
@if (!string.IsNullOrEmpty(ErrorMsg))
{
<div class="alert alert-danger my-3">
@ErrorMsg
</div>
}
<div class="row">
<div class="col">
<button class="btn btn-success w-100" @onclick="DoAdd"><i class="fa-solid fa-save"></i> Save</button>
</div>
<div class="col">
<button class="btn btn-warning w-100" @onclick="ToggleAdd">Cancel <i class="fa-solid fa-ban"></i></button>
</div>
</div>
</div>
</div>
</div>
</div>
}
<table class="table table-sm table-striped">
<thead>
<tr>
<th>
<button class="btn btn-sm btn-primary" title="Reset selezione" @onclick="DoReset"><i class="fa-solid fa-arrow-rotate-right"></i></button>
</th>
<th>Cod</th>
<th>Descrizione</th>
<th class="text-end"># Val</th>
<th class="text-end">
<button class="btn btn-sm btn-success" @onclick="ToggleAdd"><i class="fa-solid fa-plus"></i></button>
</th>
</tr>
</thead>
<tbody>
@foreach (var item in ListRecords)
{
<tr class="@checkSel(item)">
@if (EditRecord != null && item.ClassCod == EditRecord.ClassCod)
{
<td class="text-start text-nowrap">
<button class="btn btn-sm btn-success" @onclick="() => DoSave(item)"><i class="fa-solid fa-save"></i></button>
<button class="btn btn-sm btn-secondary opacity-50"><i class="fa-solid fa-pencil"></i></button>
</td>
<td>@item.ClassCod</td>
<td><input class="form-control form-control-sm border-2 border-info py-1" type="text" style="width: 12rem;" @bind="@item.Description" /></td>
<td class="text-end">@item.NumChild</td>
<td class="text-end">
<button class="btn btn-sm btn-warning" @onclick="DoReset"><i class="fa-solid fa-ban"></i></button>
</td>
}
else
{
<td class="text-start text-nowrap">
<button class="btn btn-sm btn-primary" @onclick="() => DoSelect(item)"><i class="fa-solid fa-magnifying-glass"></i></button>
<button class="btn btn-sm btn-info" @onclick="() => DoEdit(item)"><i class="fa-solid fa-pencil"></i></button>
@* <button class="btn btn-sm btn-success" @onclick="() => DoClone(item)"><i class="fa-solid fa-clone"></i></button> *@
</td>
<td>@item.ClassCod</td>
<td>@item.Description</td>
<td class="text-end">@item.NumChild</td>
<td class="text-end">
@if (item.NumChild > 0)
{
<button class="btn btn-sm btn-secondary opacity-50" disabled title="Record Child correlati: cancellazione non permessa"><i class="fa-solid fa-trash-can"></i></button>
}
else
{
<button class="btn btn-sm btn-danger" @onclick="() => DoDelete(item)"><i class="fa-solid fa-trash-can"></i></button>
}
</td>
}
</tr>
}
</tbody>
@if (totalCount >= numRecord)
{
<tfoot>
<tr>
<td colspan="5">
<EgwCoreLib.Razor.DataPager currPage="@currPage" PageSize="@numRecord" totalCount="@totalCount" numPageChanged="SavePage" numRecordChanged="SaveNumRec"></EgwCoreLib.Razor.DataPager>
</td>
</tr>
</tfoot>
}
</table>
@@ -0,0 +1,244 @@
using EgwCoreLib.Lux.Data.DbModel.Utils;
using EgwCoreLib.Lux.Data.Services;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.JSInterop;
using NLog.LayoutRenderers;
using System.Threading.Tasks;
namespace Lux.UI.Components.Compo
{
public partial class GenClassMan
{
#region Public Properties
[Parameter]
public EventCallback<string> EC_Selected { get; set; }
[Parameter]
public EventCallback<bool> EC_Updated { get; set; }
[Parameter]
public List<GenClassModel> ListGenClass { get; set; } = null!;
[Parameter]
public string SearchVal { get; set; } = "";
#endregion Public Properties
#region Protected Fields
protected List<GenClassModel> AllRecords = new List<GenClassModel>();
protected List<GenClassModel> ListRecords = new List<GenClassModel>();
#endregion Protected Fields
#region Protected Properties
[Inject]
protected DataLayerServices DLService { get; set; } = null!;
[Inject]
protected IJSRuntime JSRuntime { get; set; } = null!;
#endregion Protected Properties
#region Protected Methods
/// <summary>
/// Genera nuovo record e lo salva
/// </summary>
/// <exception cref="NotImplementedException"></exception>
protected async Task DoAdd()
{
if (NewRecord != null)
{
// verifico non sia duplicato...
var recExist = ListRecords.Count(x => x.ClassCod == NewRecord.ClassCod);
if (recExist > 0)
{
ErrorMsg = $"Errore: record già esistente con codice {NewRecord.ClassCod}";
}
else
{
ErrorMsg = "";
// faccio upsert record...
await DLService.GenClassUpsertAsync(NewRecord);
// segnalo update x reload
await EC_Updated.InvokeAsync(true);
NewRecord = null;
}
}
}
protected void DoCancel()
{
ResetEdit();
UpdateTable();
}
/// <summary>
/// Clona record
/// </summary>
/// <param name="curRec"></param>
protected void DoClone(GenClassModel curRec)
{
}
/// <summary>
/// Eliminazione record
/// </summary>
/// <param name="selRec"></param>
protected async Task DoDelete(GenClassModel selRec)
{
// controlo che NON abbia child obj... altrimenti esco
if (selRec.NumChild > 0)
return;
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Sicuro di voler eliminare il record? Dettagli: {selRec.ClassCod} | {selRec.Description}"))
return;
// esegue eliminazione del record...
await DLService.GenClassDeleteAsync(selRec);
// segnalo update x reload
await EC_Updated.InvokeAsync(true);
}
/// <summary>
/// Edit articolo selezionato
/// </summary>
/// <param name="curRec"></param>
protected async void DoEdit(GenClassModel curRec)
{
EditRecord = curRec;
SelRecord = curRec;
await EC_Selected.InvokeAsync(curRec.ClassCod);
}
/// <summary>
/// Reset selezione
/// </summary>
protected async void DoReset()
{
EditRecord = null;
SelRecord = null;
await EC_Selected.InvokeAsync("");
}
protected async Task DoSave(GenClassModel currRec)
{
// faccio upsert record...
await DLService.GenClassUpsertAsync(currRec);
// segnalo update x reload
await EC_Updated.InvokeAsync(true);
}
/// <summary>
/// Selezione articolo x display info
/// </summary>
/// <param name="curRec"></param>
protected async void DoSelect(GenClassModel curRec)
{
SelRecord = curRec;
await EC_Selected.InvokeAsync(curRec.ClassCod);
}
protected override void OnParametersSet()
{
ReloadData();
UpdateTable();
}
protected void SaveNumRec(int newNum)
{
numRecord = newNum;
UpdateTable();
}
protected void SavePage(int newNum)
{
currPage = newNum;
UpdateTable();
}
protected void ToggleAdd()
{
AddVisible = !AddVisible;
NewRecord = new GenClassModel()
{
ClassCod = "NewClass",
Description = $"Descrizione-{DateTime.Now:yyyy.MM.dd-HH.mm.ss}"
};
}
#endregion Protected Methods
#region Private Fields
private bool AddVisible = false;
private int currPage = 1;
private GenClassModel? EditRecord = null;
private string ErrorMsg = "";
private bool isLoading = false;
private GenClassModel? NewRecord = null;
private int numRecord = 10;
private GenClassModel? SelRecord = null;
private int totalCount = 0;
#endregion Private Fields
#region Private Methods
private string checkSel(GenClassModel curRec)
{
string answ = "";
if (SelRecord != null)
{
answ = curRec.ClassCod == SelRecord.ClassCod ? "table-info" : "";
}
return answ;
}
private void ReloadData()
{
isLoading = true;
AllRecords = ListGenClass;
// se ho ricerca testuale faccio filtro ulteriore...
if (!string.IsNullOrEmpty(SearchVal))
{
AllRecords = AllRecords
.Where(x =>
x.ClassCod.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase) ||
x.Description.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase))
.ToList();
}
totalCount = AllRecords.Count;
}
private void ResetEdit()
{
// reset edit
EditRecord = null;
ReloadData();
}
/// <summary>
/// Filtro e paginazione
/// </summary>
private void UpdateTable()
{
// fix paginazione
ListRecords = AllRecords
.OrderBy(x => x.ClassCod)
.Skip(numRecord * (currPage - 1))
.Take(numRecord)
.ToList();
isLoading = false;
}
#endregion Private Methods
}
}
+81
View File
@@ -0,0 +1,81 @@
@if (isLoading || ListRecords == null)
{
<LoadingData></LoadingData>
}
else if (totalCount == 0)
{
<div class="alert alert-info text-center display-4">Nessun record trovato</div>
}
else
{
<table class="table table-sm table-striped">
<thead>
<tr>
<th class="text-nowrap">
<button class="btn btn-sm btn-primary" title="Reset selezione" @onclick="DoReset"><i class="fa-solid fa-arrow-rotate-right"></i></button>
</th>
@* <th>ID</th> *@
<th class="text-nowrap text-center mx-2">Ord.</th>
<th class="w-100">Valore</th>
<th class="text-nowrap text-end">
<button class="btn btn-sm btn-success" @onclick="DoAdd"><i class="fa-solid fa-plus"></i></button>
</th>
</tr>
</thead>
<tbody>
@foreach (var item in ListRecords)
{
string cssBtnUp = EditRecord == null && item.Ordinal > 1 ? "btn-outline-primary" : "btn-outline-secondary opacity-50 disabled";
string cssBtnDown = EditRecord == null && item.Ordinal < totalCount ? "btn-outline-primary" : "btn-outline-secondary opacity-50 disabled";
<tr>
@if (EditRecord != null && item.GenValID == EditRecord.GenValID)
{
<td class="text-start text-nowrap">
<button class="btn btn-sm btn-success" @onclick="() => DoSave(item)"><i class="fa-solid fa-save"></i></button>
<button class="btn btn-sm btn-secondary opacity-50"><i class="fa-solid fa-pencil" title="Edit @item.GenValID"></i></button>
@* <button class="btn btn-sm btn-success" @onclick="() => DoClone(item)" title="Duplicate @item.GenValID"><i class="fa-solid fa-clone"></i></button> *@
</td>
@* <td>@item.GenValID</td> *@
<td class="text-nowrap text-center mx-2">
<button class="btn @cssBtnUp btn-sm fa-solid fa-caret-up"></button>
@item.Ordinal
<button class="btn @cssBtnDown btn-sm fa-solid fa-caret-down"></button>
</td>
<td class="w-100"><input class="form-control form-control-sm border-2 border-info py-1" type="text" @bind="@item.ValString" /></td>
<td class="text-nowrap text-end">
<button class="btn btn-sm btn-warning" @onclick="DoReset"><i class="fa-solid fa-ban"></i></button>
</td>
}
else
{
<td class="text-start text-nowrap">
<button class="btn btn-sm btn-primary" @onclick="() => DoSelect(item)" title="Select @item.GenValID"><i class="fa-solid fa-magnifying-glass"></i></button>
<button class="btn btn-sm btn-info" @onclick="() => DoEdit(item)"><i class="fa-solid fa-pencil" title="Edit @item.GenValID"></i></button>
@* <button class="btn btn-sm btn-success" @onclick="() => DoClone(item)" title="Duplicate @item.GenValID"><i class="fa-solid fa-clone"></i></button> *@
</td>
@* <td>@item.GenValID</td> *@
<td class="text-nowrap text-center mx-2">
<button class="btn @cssBtnUp btn-sm fa-solid fa-caret-up" @onclick="() => MoveRec(item, true)"></button>
@item.Ordinal
<button class="btn @cssBtnDown btn-sm fa-solid fa-caret-down" @onclick="() => MoveRec(item, false)"></button>
</td>
<td class="w-100">@item.ValString</td>
<td class="text-nowrap text-end">
<button class="btn btn-sm btn-danger" @onclick="() => DoDelete(item)"><i class="fa-solid fa-trash-can"></i></button>
</td>
}
</tr>
}
</tbody>
@if (totalCount >= numRecord)
{
<tfoot>
<tr>
<td colspan="15">
<EgwCoreLib.Razor.DataPager currPage="@currPage" PageSize="@numRecord" totalCount="@totalCount" numPageChanged="SavePage" numRecordChanged="SaveNumRec"></EgwCoreLib.Razor.DataPager>
</td>
</tr>
</tfoot>
}
</table>
}
+285
View File
@@ -0,0 +1,285 @@
using EgwCoreLib.Lux.Core;
using EgwCoreLib.Lux.Data.DbModel.Utils;
using EgwCoreLib.Lux.Data.Services;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace Lux.UI.Components.Compo
{
public partial class GenValMan
{
#region Public Properties
[Parameter]
public List<GenClassModel> ListGenClass { get; set; } = null!;
[Parameter]
public FiltSelect SelFilt { get; set; } = null!;
#endregion Public Properties
#region Public Classes
/// <summary>
/// Filtro selezione items
/// </summary>
public class FiltSelect
{
#region Public Properties
public string SearchVal { get; set; } = "";
public string SelCodGroup { get; set; } = "";
#endregion Public Properties
#region Public Methods
public override bool Equals(object? obj)
{
if (obj == null)
return false;
if (!(obj is FiltSelect item))
return false;
if (SelCodGroup != item.SelCodGroup)
return false;
if (SearchVal != item.SearchVal)
return false;
return true;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
#endregion Public Methods
}
#endregion Public Classes
#region Protected Fields
protected List<GenValueModel> AllRecords = new List<GenValueModel>();
protected List<GenValueModel> ListRecords = new List<GenValueModel>();
#endregion Protected Fields
#region Protected Properties
[Inject]
protected DataLayerServices DLService { get; set; } = null!;
[Inject]
protected IJSRuntime JSRuntime { get; set; } = null!;
#endregion Protected Properties
#region Protected Methods
/// <summary>
/// Clona record
/// </summary>
/// <param name="curRec"></param>
protected void DoClone(GenValueModel curRec)
{
#if false
editRecord = new ItemModel()
{
ItemIDParent = curRec.ItemType == Enums.ItemClassType.Bom ? curRec.ItemID : curRec.ItemIDParent,
CodGroup = curRec.CodGroup,
ItemType = curRec.ItemType == Enums.ItemClassType.Bom ? Enums.ItemClassType.BomAlt : curRec.ItemType,
IsService = curRec.IsService,
ItemCode = curRec.ItemCode,
ExtItemCode = $"{curRec.ExtItemCode} - COPY",
SupplCode = curRec.ItemType == Enums.ItemClassType.Bom ? $"{curRec.SupplCode} ALT" : curRec.SupplCode,
Description = $"{curRec.Description} - COPY",
Cost = curRec.Cost,
Margin = curRec.Margin,
QtyMin = curRec.QtyMin,
QtyMax = curRec.QtyMax,
UM = curRec.UM
};
#endif
}
/// <summary>
/// impossta record x eliminazione
/// </summary>
/// <param name="selRec"></param>
protected async Task DoDelete(GenValueModel selRec)
{
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Sicuro di voler eliminare il record? Dettagli: {selRec.ClassCod} | {selRec.ValString}"))
return;
// esegue eliminazione del record...
await DLService.GenValDeleteAsync(selRec);
EditRecord = null;
SelRecord = null;
await ReloadData();
UpdateTable();
#if false
// segnalo update x reload
await EC_Updated.InvokeAsync(true);
#endif
}
/// <summary>
/// Edit articolo selezionato
/// </summary>
/// <param name="curRec"></param>
protected void DoEdit(GenValueModel curRec)
{
EditRecord = curRec;
}
/// <summary>
/// Reset selezione
/// </summary>
protected void DoReset()
{
EditRecord = null;
}
/// <summary>
/// Selezione articolo x display info
/// </summary>
/// <param name="curRec"></param>
protected void DoSelect(GenValueModel curRec)
{
SelRecord = curRec;
}
protected override async Task OnParametersSetAsync()
{
if (!SelFilt.Equals(actFilt) || true)
{
actFilt = SelFilt;
await ReloadData();
UpdateTable();
}
}
protected void SaveNumRec(int newNum)
{
numRecord = newNum;
UpdateTable();
}
protected void SavePage(int newNum)
{
currPage = newNum;
UpdateTable();
}
#endregion Protected Methods
#region Private Fields
private FiltSelect actFilt = new FiltSelect();
private int currPage = 1;
private GenValueModel? EditRecord = null;
private bool isLoading = false;
private int numRecord = 10;
private GenValueModel? SelRecord = null;
private int totalCount = 0;
#endregion Private Fields
#region Private Methods
private async Task DoAdd()
{
// aggiungo un nuovo record in coda...
EditRecord = new GenValueModel()
{
ClassCod = actFilt.SelCodGroup,
ValString = $"Nuovo-{DateTime.Now:yy.MM.ss HH.mm.ss}",
Ordinal = totalCount + 1
};
await DoSave(EditRecord);
}
/// <summary>
/// Esegue spostamento
/// </summary>
/// <param name="curRec"></param>
/// <param name="moveUp"></param>
/// <returns></returns>
private async Task MoveRec(GenValueModel curRec, bool moveUp)
{
await DLService.GenValMoveAsync(curRec, moveUp);
await DoCancel();
}
private async Task DoCancel()
{
await ResetEdit();
UpdateTable();
}
private async Task DoSave(GenValueModel currRec)
{
// salvo
await DLService.GenValUpsertAsync(currRec);
await ResetEdit();
UpdateTable();
EditRecord = null;
SelRecord = null;
}
private async Task ReloadData()
{
isLoading = true;
AllRecords = await DLService.GenValGetFiltAsync(actFilt.SelCodGroup);
// se ho ricerca testuale faccio filtro ulteriore...
if (string.IsNullOrEmpty(actFilt.SearchVal))
{
AllRecords = AllRecords
.OrderBy(x => x.Ordinal)
.ToList();
}
else
{
AllRecords = AllRecords
.Where(x =>
x.ClassCod.Contains(actFilt.SearchVal, StringComparison.InvariantCultureIgnoreCase) ||
x.ValString.Contains(actFilt.SearchVal, StringComparison.InvariantCultureIgnoreCase))
.OrderBy(x => x.Ordinal)
.ToList();
}
totalCount = AllRecords.Count;
}
private async Task ResetEdit()
{
// reset edit
EditRecord = null;
await ReloadData();
}
/// <summary>
/// Filtro e paginazione
/// </summary>
private void UpdateTable()
{
// fix paginazione
ListRecords = AllRecords
.Skip(numRecord * (currPage - 1))
.Take(numRecord)
.ToList();
isLoading = false;
}
#endregion Private Methods
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
using EgwCoreLib.Lux.Data.DbModel;
using EgwCoreLib.Lux.Data.DbModel.Items;
using Microsoft.AspNetCore.Components;
namespace Lux.UI.Components.Compo
-2
View File
@@ -99,5 +99,3 @@ else
</tfoot>
</table>
}
+2 -5
View File
@@ -1,5 +1,5 @@
using EgwCoreLib.Lux.Core;
using EgwCoreLib.Lux.Data.DbModel;
using EgwCoreLib.Lux.Data.DbModel.Items;
using EgwCoreLib.Lux.Data.Services;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
@@ -68,9 +68,6 @@ namespace Lux.UI.Components.Compo
#region Protected Fields
protected List<ItemModel> AllRecords = new List<ItemModel>();
#if false
protected List<ItemGroupModel> ListItemGroup = new List<ItemGroupModel>();
#endif
protected List<ItemModel> ListRecords = new List<ItemModel>();
#endregion Protected Fields
@@ -120,7 +117,7 @@ namespace Lux.UI.Components.Compo
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Sicuro di voler eliminare il record? Dettagli: {selRec.ItemID} | {selRec.CodGroup} | {selRec.ItemType} | {selRec.ExtItemCode}"))
return;
//// esegue eliminazione del record...
//await DLService.ItemDeleteAsync(selRec);
//await CDService.ItemDeleteAsync(selRec);
editRecord = null;
selRecord = null;
+1
View File
@@ -1,4 +1,5 @@
@using EgwCoreLib.Lux.Data.DbModel
@using EgwCoreLib.Lux.Data.DbModel.Sales
<div class="alert alert-warning text-center display-4">
display check finali
<button class="btn btn-sm btn-primary" title="Reset selezione" @onclick="DoCancel"><i class="fa-solid fa-arrow-rotate-right"></i></button>
@@ -0,0 +1,65 @@
<div class="row">
@if (isLoading)
{
<LoadingData></LoadingData>
}
else
{
<div class="col-3">
<div class="form-floating mb-3">
<select @bind="@SelProfile" class="form-select">
<option value="">--- Nessuna Selezione ---</option>
@foreach (var item in ListProfiles)
{
<option value="@item">@item</option>
}
</select>
<label class="small">Profilo</label>
</div>
</div>
<div class="col-3">
<div class="form-floating mb-3">
<select @bind="@SelGlass" class="form-select">
<option value="">--- Nessuna Selezione ---</option>
@foreach (var item in ListGlass)
{
<option value="@item">@item</option>
}
</select>
<label class="small">Vetro</label>
</div>
</div>
<div class="col-3">
<div class="form-floating mb-3">
<select @bind="@SelWood" class="form-select">
<option value="">--- Nessuna Selezione ---</option>
@foreach (var item in ListWood)
{
<option value="@item">@item</option>
}
</select>
<label class="small">Legno</label>
</div>
</div>
<div class="col-3">
<div class="form-floating mb-3">
<select @bind="@SelColor" class="form-select">
<option value="">--- Nessuna Selezione ---</option>
@foreach (var item in ListColors)
{
<option value="@item">@item</option>
}
</select>
<label class="small">Colore</label>
</div>
</div>
<div class="col-6">
<button class="btn btn-lg btn-success w-100" @onclick="DoSave"><i class="fa-solid fa-floppy-disk"></i> Save</button>
</div>
<div class="col-6">
<button class="btn btn-lg btn-warning w-100" @onclick="DoCancel"><i class="fa-solid fa-ban"></i> Cancel</button>
</div>
}
</div>
@@ -0,0 +1,129 @@
using EgwCoreLib.Lux.Core;
using EgwCoreLib.Lux.Data.DbModel.Sales;
using EgwCoreLib.Lux.Data.Services;
using Microsoft.AspNetCore.Components;
namespace Lux.UI.Components.Compo
{
public partial class OfferCommonPar
{
#region Public Properties
[Parameter]
public OfferModel CurrRecord { get; set; } = null!;
[Parameter]
public EventCallback<bool> EC_Close { get; set; }
[Parameter]
public EventCallback<OfferModel> EC_Updated { get; set; }
#endregion Public Properties
#region Protected Properties
[Inject]
protected DataLayerServices DLService { get; set; } = null!;
/// <summary>
/// Gestione selezione Colore
/// </summary>
protected string SelColor
{
get => CurrSel.GetVal("Color");
set => CurrSel.SetVal("Color", value);
}
/// <summary>
/// Gestione selezione Glass
/// </summary>
protected string SelGlass
{
get => CurrSel.GetVal("Glass");
set => CurrSel.SetVal("Glass", value);
}
/// <summary>
/// Gestione selezione Profile
/// </summary>
protected string SelProfile
{
get => CurrSel.GetVal("Profile");
set => CurrSel.SetVal("Profile", value);
}
/// <summary>
/// Gestione selezione Wood
/// </summary>
protected string SelWood
{
get => CurrSel.GetVal("Wood");
set => CurrSel.SetVal("Wood", value);
}
#endregion Protected Properties
#region Protected Methods
protected override async Task OnParametersSetAsync()
{
isLoading = true;
// deserializzo se possibile dal record...
CurrSel = new ParamDict(CurrRecord.DictPresel);
// leggo dati di base
var AllColors = await DLService.GenValGetFiltAsync("WoodCol");
var AllConfGlass = await DLService.ConfGlassGetAllAsync();
var AllConfProfile = await DLService.ConfProfileGetAllAsync();
var AllConfWood = await DLService.ConfWoodGetAllAsync();
// conversione tipi
ListColors = AllColors
.Select(x => x.ValString)
.ToList();
ListGlass = AllConfGlass
.Select(x => x.Description)
.ToList();
ListProfiles = AllConfProfile
.Select(x => x.Description)
.ToList();
ListWood = AllConfWood
.Select(x => x.Description)
.ToList();
isLoading = false;
}
#endregion Protected Methods
#region Private Fields
private ParamDict CurrSel = new ParamDict("");
private bool isLoading = false;
private List<string> ListColors = new List<string>();
private List<string> ListGlass = new List<string>();
private List<string> ListProfiles = new List<string>();
private List<string> ListWood = new List<string>();
#endregion Private Fields
#region Private Methods
private async Task DoCancel()
{
await EC_Close.InvokeAsync(true);
}
private async Task DoSave()
{
// aggiorno valore serializzato...
CurrRecord.DictPresel = CurrSel.Serialized;
// richiesta update con salvataggio record
await EC_Updated.InvokeAsync(CurrRecord);
}
#endregion Private Methods
}
}
+57 -142
View File
@@ -1,146 +1,61 @@
@using EgwCoreLib.Lux.Core
@* <div class="card shadow">
<div class="card-dialog card-xl">
<div class="card-content">
<div class="card-header">
<div class="card-title fs-4">Edit Offerta <b>@CurrRecord.OfferCode</b></div>
<button type="button" class="btn-close" data-bs-dismiss="card" aria-label="Close" @onclick="ClosePopup">
</button>
</div>
<div class="card-body"> *@
<div class="row">
<div class="col-3">
<div class="form-floating mb-3">
<select @bind="@CurrRecord.CustomerID" class="form-select">
@foreach (var item in CustomersList)
{
<option value="@item.CustomerID">@item.FirstName @item.LastName (@item.VAT)</option>
}
</select>
<label class="small">Cliente</label>
</div>
</div>
<div class="col-3">
<div class="form-floating mb-3">
<select @bind="@CurrRecord.DealerID" class="form-select">
@foreach (var item in DealersList)
{
<option value="@item.DealerID">@item.CompanyName | @item.FirstName @item.LastName</option>
}
</select>
<label class="small">Rivenditore</label>
</div>
</div>
<div class="col-2">
<div class="form-floating mb-3">
<select @bind="@CurrRecord.OffertState" class="form-select">
@foreach (var item in System.Enum.GetValues(typeof(EgwCoreLib.Lux.Core.Enums.OfferStates)))
{
<option value="@item">@item</option>
}
</select>
<label class="small">Stato</label>
</div>
</div>
<div class="col-2">
<div class="form-floating mb-3">
<input @bind="@CurrRecord.Inserted" class="form-control" type="date">
<label class="small">Data Ins.</label>
</div>
</div>
<div class="col-2">
<div class="form-floating mb-3">
<input @bind="@CurrRecord.ValidUntil" class="form-control" type="date">
<label class="small">Validit&agrave;</label>
</div>
</div>
<div class="col-12">
<div class="form-floating mb-3">
<textarea @bind="@CurrRecord.Description" class="form-control" type="text"></textarea>
<label class="small">Descrizione</label>
</div>
</div>
<div class="col-6">
<button class="btn btn-lg btn-success w-100" @onclick="DoSave"><i class="fa-solid fa-floppy-disk"></i> Save</button>
</div>
<div class="col-6">
<button class="btn btn-lg btn-warning w-100" @onclick="DoCancel"><i class="fa-solid fa-ban"></i> Cancel</button>
</div>
</div>
@* </div>
</div>
</div>
</div> *@
@* <div class="modal" tabindex="-1" style="display:block; background-color: rgba(10,10,10,.6);" role="dialog">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<div class="modal-title fs-4">Edit Offerta <b>@CurrRecord.OfferCode</b></div>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="ClosePopup">
</button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-3">
<div class="form-floating mb-3">
<select @bind="@CurrRecord.CustomerID" class="form-select">
@foreach (var item in CustomersList)
{
<option value="@item.CustomerID">@item.FirstName @item.LastName (@item.VAT)</option>
}
</select>
<label class="small">Cliente</label>
</div>
</div>
<div class="col-3">
<div class="form-floating mb-3">
<select @bind="@CurrRecord.DealerID" class="form-select">
@foreach (var item in DealersList)
{
<option value="@item.DealerID">@item.CompanyName | @item.FirstName @item.LastName</option>
}
</select>
<label class="small">Rivenditore</label>
</div>
</div>
<div class="col-2">
<div class="form-floating mb-3">
<select @bind="@CurrRecord.OffertState" class="form-select">
@foreach (var item in Enum.GetValues(typeof(Core.Enum.OfferStates)))
{
<option value="@item">@item</option>
}
</select>
<label class="small">Stato</label>
</div>
</div>
<div class="col-2">
<div class="form-floating mb-3">
<input @bind="@CurrRecord.Inserted" class="form-control" type="date">
<label class="small">Data Ins.</label>
</div>
</div>
<div class="col-2">
<div class="form-floating mb-3">
<input @bind="@CurrRecord.ValidUntil" class="form-control" type="date">
<label class="small">Validit&agrave;</label>
</div>
</div>
<div class="col-12">
<div class="form-floating mb-3">
<textarea @bind="@CurrRecord.Description" class="form-control" type="text"></textarea>
<label class="small">Descrizione</label>
</div>
</div>
<div class="col-6">
<button class="btn btn-lg btn-success w-100"><i class="fa-solid fa-floppy-disk"></i> Save</button>
</div>
<div class="col-6">
<button class="btn btn-lg btn-warning w-100"><i class="fa-solid fa-ban"></i> Cancel</button>
</div>
</div>
</div>
<div class="row">
<div class="col-3">
<div class="form-floating mb-3">
<select @bind="@CurrRecord.CustomerID" class="form-select">
@foreach (var item in CustomersList)
{
<option value="@item.CustomerID">@item.FirstName @item.LastName (@item.VAT)</option>
}
</select>
<label class="small">Cliente</label>
</div>
</div>
</div> *@
<div class="col-3">
<div class="form-floating mb-3">
<select @bind="@CurrRecord.DealerID" class="form-select">
@foreach (var item in DealersList)
{
<option value="@item.DealerID">@item.CompanyName | @item.FirstName @item.LastName</option>
}
</select>
<label class="small">Rivenditore</label>
</div>
</div>
<div class="col-2">
<div class="form-floating mb-3">
<select @bind="@CurrRecord.OffertState" class="form-select">
@foreach (var item in System.Enum.GetValues(typeof(EgwCoreLib.Lux.Core.Enums.OfferStates)))
{
<option value="@item">@item</option>
}
</select>
<label class="small">Stato</label>
</div>
</div>
<div class="col-2">
<div class="form-floating mb-3">
<input @bind="@CurrRecord.Inserted" class="form-control" type="date">
<label class="small">Data Ins.</label>
</div>
</div>
<div class="col-2">
<div class="form-floating mb-3">
<input @bind="@CurrRecord.ValidUntil" class="form-control" type="date">
<label class="small">Validit&agrave;</label>
</div>
</div>
<div class="col-12">
<div class="form-floating mb-3">
<textarea @bind="@CurrRecord.Description" class="form-control" type="text"></textarea>
<label class="small">Descrizione</label>
</div>
</div>
<div class="col-6">
<button class="btn btn-lg btn-success w-100" @onclick="DoSave"><i class="fa-solid fa-floppy-disk"></i> Save</button>
</div>
<div class="col-6">
<button class="btn btn-lg btn-warning w-100" @onclick="DoCancel"><i class="fa-solid fa-ban"></i> Cancel</button>
</div>
</div>
+2 -15
View File
@@ -1,4 +1,4 @@
using EgwCoreLib.Lux.Data.DbModel;
using EgwCoreLib.Lux.Data.DbModel.Sales;
using EgwCoreLib.Lux.Data.Services;
using Microsoft.AspNetCore.Components;
using System.Threading.Tasks;
@@ -46,17 +46,6 @@ namespace Lux.UI.Components.Compo
#region Private Methods
private async Task ClosePopup()
{
await EC_Close.InvokeAsync(true);
#if false
// Close the Popup
ShowPopup = false;
// Refresh Users
await GetUsers();
#endif
}
private async Task DoCancel()
{
await EC_Close.InvokeAsync(true);
@@ -64,9 +53,7 @@ namespace Lux.UI.Components.Compo
private async Task DoSave()
{
// fixme todo !!!
// effettuare salvataggio record...
// richiesta update con salvataggio record
await EC_Updated.InvokeAsync(CurrRecord);
}
+176 -33
View File
@@ -1,23 +1,30 @@
@if (EditRecord != null)
@if (EditRecord != null && CurrEditMode == EditMode.SerStruc)
{
<h1>EDITING</h1>
<h3>@EditRecord.OfferRowUID</h3>
@* <WebWindowConfigurator.WebWindowMaker IN_TemplateDTOList="@AvailTemplateList"
IN_SelTemplate="@SelTemplate"
EC_OnUpdate="SaveJWD"
EC_OnSelectedTemplate="SetTemplate"
LiveSVG="@currSvg">
</WebWindowConfigurator.WebWindowMaker> *@
<WebWindowComplex.TableComp ListPayload="SetupList"
LiveData="CurrData"
EC_OnUpdate="SaveJWD"
EC_OnClose="CloseEdit">
</WebWindowComplex.TableComp>
}
else
{
<div class="card shadow">
<div class="card-header bg-info bg-opacity-50 bg-gradient d-flex justify-content-between">
<div class="px-0">Dettaglio Offerta</div>
<div class="px-0">
<button class="btn btn-sm btn-primary" @onclick="() => RecalcOffer()">Ricalcola <i class="fa-solid fa-calculator"></i></button>
<div class="px-0 fs-4">Dettaglio Offerta</div>
<div class="px-0 d-flex justify-content-end">
@if (DisplayMode == EgwCoreLib.Lux.Core.Enums.DisplayMode.Edit)
{
<div class="px-1">
<button class="btn btn-sm btn-warning" @onclick="() => OfferForceParameters()">Forza Parametri <i class="fa-solid fa-hammer"></i></button>
</div>
<div class="px-1">
<button class="btn btn-sm btn-primary" @onclick="() => OfferUpdatePrices()">Aggiorna Prezzi <i class="fa-solid fa-calculator"></i></button>
</div>
<div class="px-1">
<button class="btn btn-sm btn-primary" @onclick="() => OfferUpdateAllCosting()">Ricalcolo Completo <i class="fa-solid fa-calculator"></i></button>
</div>
}
</div>
</div>
<div class="cad-body px-2">
@if (isLoading || ListRecords == null)
@@ -35,8 +42,10 @@ else
<tr>
@if (DisplayMode == EgwCoreLib.Lux.Core.Enums.DisplayMode.Edit)
{
<th>Def</th>
<th>img</th>
<th>
<button class="btn btn-sm btn-primary" style="min-width: 6rem;" @onclick="ForceReloadData"><i class="fa-solid fa-rotate-right"></i></button>
</th>
<th>Preview</th>
}
else
{
@@ -45,22 +54,41 @@ else
<th>Codice</th>
<th>Descrizione</th>
<th class="text-end">Qty</th>
<th class="text-end">Importo </th>
<th class="text-end">Tot</th>
<th class="text-end">Importo</th>
@if (DisplayMode == EgwCoreLib.Lux.Core.Enums.DisplayMode.Edit)
{
<th class="text-end" title="Cambio Materiali">Mat.</th>
}
<th class="text-end">Totale</th>
<th class="text-end">Marg.</th>
@if (DisplayMode == EgwCoreLib.Lux.Core.Enums.DisplayMode.Edit)
{
<th></th>
}
</tr>
</thead>
<tbody>
@foreach (var item in ListRecords)
{
<tr>
<td>
<tr class="@RowClass(item)">
<td class="text-nowrap">
<span class="px-1">
@item.RowNum
</span>
@if (DisplayMode == EgwCoreLib.Lux.Core.Enums.DisplayMode.Edit)
{
<button class="btn btn-sm btn-info" @onclick="() => DoEdit(item)"><i class="fa-solid fa-pencil"></i></button>
if (CurrEditMode == EditMode.None)
{
<button class="btn btn-sm btn-info" @onclick="() => DoEdit(item)"><i class="fa-solid fa-pencil"></i></button>
<button class="btn btn-sm btn-warning" @onclick="() => DoClone(item)"><i class="fa-solid fa-clone"></i></button>
}
else
{
if (EditRecord != null && EditRecord.OfferRowID == item.OfferRowID)
{
<button class="btn btn-sm btn-success" title="Annulla Modifiche" @onclick="() => DoSave(item)"><i class="fa-solid fa-floppy-disk"></i></button>
}
}
}
else
{
@@ -70,44 +98,159 @@ else
@if (DisplayMode == EgwCoreLib.Lux.Core.Enums.DisplayMode.Edit)
{
<td>
<img class="img-fluid" src="@(imgUrl(item.OfferRowUID, item.Environment))" width="48" />
@if (string.IsNullOrEmpty(item.SerStruct) || item.SerStruct.Length <= 2)
{
<img class="img-fluid" src="@(imgUrl(item.OfferRowUID, $"{item.Envir}"))" width="48" />
}
else
{
<img class="img-fluid" src="@(imgUrl(item.OfferRowUID, $"{item.Envir}"))" width="48" @onclick="() => DoEditJwd(item)" title="Edit Item" />
}
</td>
}
<td>@item.OfferRowUID</td>
<td>@item.Note</td>
<td class="text-end">@item.Qty</td>
<td class="text-end">
@if (!(item.BomOk && item.ItemOk))
<td class="small">
<div>@item.OfferRowUID</div>
@if (DisplayMode == EgwCoreLib.Lux.Core.Enums.DisplayMode.Edit && !string.IsNullOrEmpty(item.SerStruct) && item.SerStruct.Length > 2)
{
<span class="text-danger me-2" title=""><i class="fa-solid fa-triangle-exclamation"></i></span>
<button class="btn btn-sm btn-primary" @onclick="() => RequestBom(item)" title="Richiesta ricalcolo BOM">
BOM <i class="fa-solid fa-arrow-right-arrow-left pe-2"></i>
@if (item.AwaitBom)
{
<span class="text-warning spinner-grow spinner-grow-sm" aria-hidden="true"></span>
}
</button>
}
@if (item.Envir != EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW || !string.IsNullOrEmpty(item.FileName))
{
<div class="input-group input-group-sm">
@if (EditRecord != null && EditRecord.OfferRowID == item.OfferRowID)
{
<span>
<InputFile class="form-control" OnChange="UploadFile" style="width: 24rem;" />
</span>
<button class="btn btn-sm btn-info" @onclick="() => ToggleFileEdit(null)"><i class="fa-solid fa-floppy-disk"></i></button>
}
else
{
<span class="input-group-text small"><b>@item.FileName</b> | @fSize(item.FileSize)</span>
<button class="btn btn-sm btn-primary" @onclick="() => ToggleFileEdit(item)"><i class="fa-solid fa-floppy-disk"></i></button>
}
</div>
}
@($"{item.Cost:C2}")
</td>
<td class="text-end fw-bold">@($"{item.TotalCost:C2}")</td>
@if (CurrEditMode == EditMode.RecData && EditRecord != null && EditRecord.OfferRowID == item.OfferRowID)
{
<td>
<div class="input-group">
<input class="form-input w-100" style="width: 16rem;" type="text" @bind="@item.Note" />
</div>
</td>
<td class="text-end">
<input class="text-end" style="width: 4rem;" type="number" @bind="@item.Qty" />
</td>
}
else
{
<td>@item.Note</td>
<td class="text-end">
<b>@item.Qty</b>
</td>
}
<td class="text-end text-nowrap">
<div class="fw-bold" title="Prezzo Finito">
@if (!(item.BomOk && item.ItemOk))
{
<span class="text-danger me-2" title=""><i class="fa-solid fa-triangle-exclamation"></i></span>
}
@if (item.AwaitPrice)
{
<span class="text-warning spinner-grow spinner-grow-sm" aria-hidden="true"></span>
}
@($"{item.UnitPrice:C2}")
</div>
<div class="small text-secondary" title="RockBottom Price">(@item.UnitCost.ToString("C2"))</div>
</td>
@if (DisplayMode == EgwCoreLib.Lux.Core.Enums.DisplayMode.Edit)
{
<td class="text-end">
@if(!string.IsNullOrEmpty(item.ItemBOM))
@if (!string.IsNullOrEmpty(item.ItemBOM))
{
<button class="btn btn-sm btn-info" title="cambio materiali assegnati" @onclick="() => DoSwapMat(item)"><i class="fa-solid fa-arrow-right-arrow-left"></i></button>
<button class="btn btn-sm btn-info" title="Cambio materiali assegnati" @onclick="() => DoSwapMat(item)"><i class="fa-solid fa-arrow-right-arrow-left"></i></button>
}
</td>
}
<td class="text-end text-nowrap">
<div class="fw-bold" title="Prezzo Finito">
@if (item.AwaitPrice)
{
<span class="text-warning spinner-grow spinner-grow-sm" aria-hidden="true"></span>
}
@($"{item.TotalPrice:C2}")
</div>
<div class="small text-secondary" title="RockBottom Price">(@item.TotalCost.ToString("C2"))</div>
</td>
<td class="text-end text-nowrap" title="Margine / Sconto MAX applicabile">
@item.MaxDiscount.ToString("P2")
</td>
@if (DisplayMode == EgwCoreLib.Lux.Core.Enums.DisplayMode.Edit)
{
<td>
@if (CurrEditMode == EditMode.RecData && EditRecord != null && EditRecord.OfferRowID == item.OfferRowID)
{
<button class="btn btn-sm btn-warning" title="Annulla Modifiche" @onclick="DoCancel"><i class="fa-solid fa-ban"></i></button>
}
else
{
<button class="btn btn-sm btn-danger" title="Eliminazione riga" @onclick="() => DoDelete(item)"><i class="fa-solid fa-trash"></i></button>
}
</td>
}
</tr>
}
</tbody>
<tfoot>
<tr class="table-success">
@if (DisplayMode == EgwCoreLib.Lux.Core.Enums.DisplayMode.Edit)
{
<th colspan="4"></th>
}
else
{
<th colspan="3"></th>
}
<th class="text-end">@($"{GrandTotQty:N0}")</th>
@if (DisplayMode == EgwCoreLib.Lux.Core.Enums.DisplayMode.Edit)
{
<th colspan="2"></th>
}
else
{
<th colspan="1"></th>
}
<th class="text-end">
@($"{GrandTotPrice:C2}")
<div class="small text-secondary" title="RockBottom Price">(@($"{GrandTotCost:C2}"))</div>
</th>
<th class="text-end">@($"{GrandTotMargin:P2}")</th>
@if (DisplayMode == EgwCoreLib.Lux.Core.Enums.DisplayMode.Edit)
{
<th></th>
}
</tr>
</tfoot>
</table>
}
</div>
</div>
}
@if (showChangeMat && EditBomRecord != null)
@if (EditRecord != null && CurrEditMode == EditMode.BOM)
{
<div class="modal" tabindex="-1" style="display:block; background-color: rgba(10,10,10,.6);" role="dialog">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<div class="modal-title fs-4">Cambio Materiali offerta <b>@EditBomRecord.OfferRowUID</b></div>
<div class="modal-title fs-4">Cambio Materiali offerta <b>@EditRecord.OfferRowUID</b></div>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="ClosePopup">
</button>
</div>
+842 -19
View File
@@ -1,61 +1,348 @@
using EgwCoreLib.Lux.Core;
using EgwCoreLib.Lux.Core.RestPayload;
using EgwCoreLib.Lux.Data;
using EgwCoreLib.Lux.Data.DbModel;
using EgwCoreLib.Lux.Data.DbModel.Config;
using EgwCoreLib.Lux.Data.DbModel.Sales;
using EgwCoreLib.Lux.Data.DbModel.Utils;
using EgwCoreLib.Lux.Data.Services;
using EgwMultiEngineManager.Data;
using Lux.UI.Components.Compo.Config;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.EntityFrameworkCore.Storage.Json;
using Microsoft.JSInterop;
using NLog.LayoutRenderers;
using System.Text;
using WebWindowComplex;
using WebWindowComplex.DTO;
using static EgwCoreLib.Lux.Core.Enums;
namespace Lux.UI.Components.Compo
{
public partial class OfferRowMan
public partial class OfferRowMan : IDisposable
{
#region Public Enums
/// <summary>
/// modalità modifica riga offerta
/// </summary>
public enum EditMode
{
None = 0,
/// <summary>
/// Dati generici del record
/// </summary>
RecData,
/// <summary>
/// Struttura serializzata (es JWD)
/// </summary>
SerStruc,
/// <summary>
/// BOM editing
/// </summary>
BOM,
/// <summary>
/// File editing (es BTL)
/// </summary>
File
}
#endregion Public Enums
#region Public Properties
[Parameter]
public OfferModel CurrRecord { get; set; } = null!;
[Parameter]
public DisplayMode DisplayMode { get; set; } = DisplayMode.Standard;
[Parameter]
public int OfferID { get; set; } = 0;
public EventCallback<bool> EC_Updated { get; set; }
#endregion Public Properties
#region Public Methods
/// <summary>
/// Dispose sottoscrizione canale
/// </summary>
public void Dispose()
{
DLService.UpdatePipe.EA_NewMessage -= UpdatePipe_EA_NewMessage;
DLService.CalcDonePipe.EA_NewMessage -= CalcDonePipe_EA_NewMessage;
}
#endregion Public Methods
#region Protected Fields
/// <summary>
/// Predisposizione valori live SVG/JWD
/// </summary>
protected LivePayload CurrData = new LivePayload();
/// <summary>
/// Configurazione elenchi anagrafiche
/// </summary>
protected BaseListPayload SetupList = new BaseListPayload();
#endregion Protected Fields
#region Protected Properties
[Inject]
protected ConfigDataService CDService { get; set; } = null!;
[Inject]
protected IConfiguration Config { get; set; } = null!;
[Inject]
protected CalcRequestService CService { get; set; } = null!;
[Inject]
protected DataLayerServices DLService { get; set; } = null!;
/// <summary>
/// Costo totale calcolato x offerta
/// </summary>
protected double GrandTotCost
{
get => AllRecords != null && AllRecords.Count > 0 ? AllRecords.Sum(x => x.TotalCost) : 0;
}
/// <summary>
/// Margine medio calcolato x offerta
/// </summary>
protected double GrandTotMargin
{
get
{
double answ = 0;
if (AllRecords != null && AllRecords.Count > 0)
{
double totPrice = AllRecords.Sum(x => x.TotalPrice);
double totCost = AllRecords.Sum(x => x.TotalCost);
if (totPrice > 0)
{
answ = (totPrice - totCost) / totPrice;
}
}
return answ;
}
}
/// <summary>
/// Importo totale calcolato x offerta
/// </summary>
protected double GrandTotPrice
{
get => AllRecords != null && AllRecords.Count > 0 ? AllRecords.Sum(x => x.TotalPrice) : 0;
}
/// <summary>
/// Num totale obj calcolato x offerta
/// </summary>
protected double GrandTotQty
{
get => AllRecords != null && AllRecords.Count > 0 ? AllRecords.Sum(x => x.Qty) : 0;
}
[Inject]
protected IWebHostEnvironment HostEnv { get; set; } = null!;
[Inject]
protected ImageCacheService ICService { get; set; } = null!;
[Inject]
protected IJSRuntime JSRuntime { get; set; } = null!;
/// <summary>
/// ID Offerta corrente
/// </summary>
protected int OfferID
{
get => CurrRecord.OfferID;
}
#endregion Protected Properties
#region Protected Methods
protected void ClosePopup()
{
showChangeMat = false;
EditBomRecord = null;
CurrEditMode = EditMode.None;
EditRecord = null;
}
/// <summary>
/// Annullamento modifica
/// </summary>
/// <param name="curRec"></param>
/// <returns></returns>
protected async Task DoCancel()
{
isLoading = true;
CurrEditMode = EditMode.None;
EditRecord = null;
await Task.Delay(20);
await DLService.FlushCacheOffersAsync();
await Task.Delay(20);
await ReloadData();
UpdateTable();
isLoading = false;
}
/// <summary>
/// Clona riga richiesta
/// </summary>
/// <param name="rec2clone"></param>
protected async Task DoClone(OfferRowModel rec2clone)
{
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Confermi di voler duplicare la riga corrente?"))
return;
// calcolo indice riga...
int numRow = totalCount + 1;
OfferRowModel newRec = new OfferRowModel()
{
AwaitBom = true,
AwaitPrice = true,
Envir = rec2clone.Envir,
FileName = rec2clone.FileName,
FileResource = rec2clone.FileResource,
FileSize = rec2clone.FileSize,
Inserted = DateTime.Now,
ItemBOM = rec2clone.ItemBOM,
ItemSteps = rec2clone.ItemSteps,
Modified = DateTime.Now,
Note = rec2clone.Note,
OfferID = OfferID,
OfferRowUID = "",
Qty = rec2clone.Qty,
RowNum = numRow,
SellingItemID = rec2clone.SellingItemID,
SerStruct = rec2clone.SerStruct,
StepCost = rec2clone.StepCost,
StepPrice = rec2clone.StepPrice,
};
// salvo sul DB
await DLService.OffertRowUpsert(newRec);
// chiamo update record che non hanno UID x questo ordine
var list2fix = await DLService.OffertRowFixUid(OfferID);
if (list2fix != null && list2fix.Count > 0)
{
// rileggo i miei record...
await ReloadData();
var listCalc = SorListCalc();
foreach (var item in listCalc)
{
// se UID è tra quelli da ricalcolare...
if (list2fix.Contains(item.OfferRowUID))
{
// chiedo BOM e immagine
await reqBomUpdate(item);
}
}
}
await ReloadData();
UpdateTable();
}
/// <summary>
/// Eliminazione riga offerta
/// </summary>
/// <param name="rec2del"></param>
/// <returns></returns>
protected async Task DoDelete(OfferRowModel rec2del)
{
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Confermi di voler eliminare la riga corrente?<br/>Codice: {rec2del.OfferRowUID} | {rec2del.Note} | importo tot: {rec2del.TotalPrice}"))
return;
await DLService.OffertRowDelete(rec2del);
await ReloadData();
UpdateTable();
}
/// <summary>
/// Va in edit della riga richiesta
/// </summary>
/// <param name="curRec"></param>
protected void DoEdit(OfferRowModel curRec)
{
#if false
// preparazione dati da record corrente
PrepareWindowData(curRec.SerStruct);
// reset prev
prevJwd = "";
#endif
// imposto edit record
EditRecord = curRec;
/// modalitàedit: gestione valori campi record
CurrEditMode = EditMode.RecData;
}
/// <summary>
/// Apre editor finestre del record richiesto
/// </summary>
/// <param name="curRec"></param>
protected void DoEditJwd(OfferRowModel curRec)
{
EditRecord = curRec;
/// modalitàedit: gestione JWD
CurrEditMode = EditMode.SerStruc;
// preparazione dati da record corrente
PrepareWindowData(EditRecord.SerStruct);
// reset prev
prevJwd = "";
}
/// <summary>
/// Salvataggio edit record + reload
/// </summary>
/// <param name="curRec"></param>
/// <returns></returns>
protected async Task DoSave(OfferRowModel curRec)
{
isLoading = true;
// salvo record modificato...
await DLService.OffertRowUpsert(curRec);
// reset
CurrEditMode = EditMode.None;
EditRecord = null;
await DLService.FlushCacheOffersAsync();
await ReloadData();
UpdateTable();
isLoading = false;
}
protected void DoSwapMat(OfferRowModel currRow)
{
showChangeMat = true;
EditBomRecord = currRow;
CurrBomList = DLService.OffertGetBomList(EditBomRecord);
CurrEditMode = EditMode.BOM;
EditRecord = currRow;
CurrBomList = DLService.OffertGetBomList(EditRecord);
}
/// <summary>
/// Display fileSize scalato
/// </summary>
/// <param name="size"></param>
/// <returns></returns>
protected string fSize(long size)
{
return EgwCoreLib.Utils.FileHelpers.SizeSuffix(size, 1);
}
/// <summary>
/// Calcolo URL immagine
/// </summary>
/// <param name="imgUid"></param>
/// <param name="env"></param>
/// <returns></returns>
protected string imgUrl(string imgUid, string env)
{
// cast string su env..
@@ -64,10 +351,110 @@ namespace Lux.UI.Components.Compo
return ICService.ImageUrl($"{apiUrl}/{imgBasePath}", false, imgUid, envir);
}
protected override void OnInitialized()
/// <summary>
/// Forza parametri general selezionati nell'offerta
/// </summary>
/// <returns></returns>
protected async Task OfferForceParameters()
{
apiUrl = Config.GetValue<string>("ServerConf:Prog.ApiUrl") ?? "";
imgBasePath = Config.GetValue<string>("ServerConf:ImageBaseUrl") ?? "";
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Confermi di voler impostare i parametri selezionati per l'offerta?"))
return;
// recupero obj dizionario x i parametri compresi...
ParamDict CurrSel = new ParamDict(CurrRecord.DictPresel);
// metto a waiting tutte le righe con bom...
var listCalc = SorListCalc();
foreach (var item in listCalc)
{
await DLService.OffertUpdateAwaitState(item.OfferRowID, true, true);
// poiché non è gestito evento ritorno update window interno si "scassa" --> try catch/ if FALSE
try
{
string rColor = CurrSel.GetVal("Color");
string rGlass = CurrSel.GetVal("Glass");
string rProfile = CurrSel.GetVal("Profile");
string rWood = CurrSel.GetVal("Wood");
var newSerStruct = SerialMan.MassUpdate((string)item.SerStruct, null, null, rColor, rWood, rGlass, rProfile);
await DLService.OffertRowUpdateSerStruct(item.OfferRowID, newSerStruct);
}
catch
{ }
}
await InvokeAsync(StateHasChanged);
// verifica preliminare UID
await DLService.OffertRowFixUid(OfferID);
// ricalcolo di tutte le BOM e relativi prezzi...
foreach (var item in listCalc)
{
// chiedo BOM e immagine
await reqBomUpdate(item);
}
//await DoRecalcOffer();
}
/// <summary>
/// Aggiornamento costing completo:
/// - verifica UID
/// - ricalcolo BOM
/// - update prezzi
/// </summary>
/// <returns></returns>
protected async Task OfferUpdateAllCosting()
{
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Confermi di voler ricalcolare/validare in toto l'offerta?"))
return;
// metto a waiting tutte le righe con bom...
var listCalc = SorListCalc();
foreach (var item in listCalc)
{
await DLService.OffertUpdateAwaitState(item.OfferRowID, true, true);
}
await InvokeAsync(StateHasChanged);
// verifica preliminare UID
await DLService.OffertRowFixUid(OfferID);
// fixme todo da riverificare con calcolo BOM funzionante
#if false
// rileggo i record...
await ReloadData();
#endif
// ricalcolo di tutte le BOM e relativi prezzi...
foreach (var item in listCalc)
{
// chiedo BOM e immagine
await reqBomUpdate(item);
}
#if false
await DoRecalcOffer();
#endif
}
/// <summary>
/// Verifica e ricalcolo dei prezzi degli items nell'offerta
/// </summary>
/// <returns></returns>
protected async Task OfferUpdatePrices()
{
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Confermi di voler ricalcolare a costi correnti offerta?"))
return;
await DoRecalcOffer();
}
/// <summary>
/// init obj
/// </summary>
protected override async Task OnInitializedAsync()
{
ConfInit();
prevJwd = "";
await ReloadBaseList();
DLService.UpdatePipe.EA_NewMessage += UpdatePipe_EA_NewMessage;
DLService.CalcDonePipe.EA_NewMessage += CalcDonePipe_EA_NewMessage;
}
protected override async Task OnParametersSetAsync()
@@ -76,30 +463,82 @@ namespace Lux.UI.Components.Compo
UpdateTable();
}
protected async Task RecalcOffer()
/// <summary>
/// Lancia la richiesta di ricaolo della BOM dal JWD (o equivalente)
/// </summary>
/// <returns></returns>
protected async Task RequestBom(OfferRowModel currRec)
{
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Confermi di voler ricalcolare importi offerta?"))
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Confermi di voler ricalcolare la BOM?"))
return;
await DoRecalcOffer();
await reqBomUpdate(currRec);
}
/// <summary>
/// Css di verifica riga selezionata
/// </summary>
/// <param name="selRow"></param>
/// <returns></returns>
protected string RowClass(OfferRowModel selRow)
{
return EditRecord != null && EditRecord.OfferRowID == selRow.OfferRowID ? "table-info" : "";
}
#endregion Protected Methods
#region Private Fields
private List<GenValueModel> AllColors = new();
private List<EnvirParamModel> AllConfEnvir = new();
private List<GlassModel> AllConfGlass = new();
private List<HardwareModel> AllConfHardware = new();
private List<ProfileModel> AllConfProfile = new();
private List<WoodModel> AllConfWood = new();
private List<OfferRowModel> AllRecords = new List<OfferRowModel>();
private string apiUrl = "";
private List<string> AvailColorMaterialList = new List<string>();
private List<string> AvailFamilyHardwareList = new List<string>();
private List<string> AvailGlassList = new List<string>();
private List<Egw.Window.Data.Hardware> AvailHardwareList = new();
private List<string> AvailMaterialList = new List<string>();
private List<string> AvailProfileList = new List<string>();
private string calcTag = "calc";
private List<BomItemDTO>? CurrBomList = null;
/// <summary>
/// Modalità editint attiva
/// </summary>
private EditMode CurrEditMode = EditMode.None;
private int currPage = 1;
private OfferRowModel? EditBomRecord = null;
private string currSvg = "";
/// <summary>
/// Record in Edit corrente
/// </summary>
private OfferRowModel? EditRecord = null;
private string genBasePath = "generic";
private string GenericBasePath = "";
private string imgBasePath = "";
private bool isLoading = false;
@@ -108,7 +547,22 @@ namespace Lux.UI.Components.Compo
private int numRecord = 10;
private bool showChangeMat = false;
/// <summary>
/// Versione originale (pre edit)
/// </summary>
private string origJwd = "";
/// <summary>
/// Versione precedente JWD x test e confronto
/// </summary>
private string prevJwd = "";
/// <summary>
/// Dizionario richieste
/// </summary>
private Dictionary<string, string> reqDict = new Dictionary<string, string>();
private string subChannel = "";
private int totalCount = 0;
@@ -116,14 +570,184 @@ namespace Lux.UI.Components.Compo
#region Private Methods
/// <summary>
/// Ricevuto SVG, se è il mio lo aggiorno...
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void CalcDonePipe_EA_NewMessage(object? sender, EventArgs e)
{
// vale SOLO SE sono in editing...
if (EditRecord != null)
{
// aggiorno visualizzazione
PubSubEventArgs currArgs = (PubSubEventArgs)e;
// conversione on-the-fly SVG da mostrare
if (!string.IsNullOrEmpty(currArgs.newMessage))
{
if (currArgs.msgUid.Equals($"{subChannel}:{EditRecord.OfferRowUID}"))
{
currSvg = currArgs.newMessage;
// salvo in live data...
CurrData.SvgPreview = currSvg;
}
await InvokeAsync(StateHasChanged);
}
}
}
/// <summary>
/// Chiude edit andando eventualmente a salvare
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
private async Task CloseEdit(bool doSave)
{
// ...se ho editing
if (EditRecord != null)
{
bool updateBom = false;
// SE richiesto salvataggio...
if (doSave)
{
// salvo su DB!
await DLService.OffertRowUpdateSerStruct(EditRecord.OfferRowID, prevJwd);
updateBom = true;
}
else
// altrimenti ricalcolo valore salvato
{
prevJwd = EditRecord.SerStruct;
CurrData.CurrJwd = EditRecord.SerStruct;
}
if (updateBom)
{
await reqBomUpdate(EditRecord);
}
// aggiorno nel dizionari
if (reqDict.ContainsKey("Jwd"))
{
reqDict["Jwd"] = prevJwd;
}
if (reqDict != null && reqDict.Count > 0)
{
// chiamo richiesta update
CalcRequestDTO calcRequestDTO = new CalcRequestDTO();
calcRequestDTO.EnvType = EditRecord.Envir;
calcRequestDTO.DictExec = reqDict;
// chiamo la chiamata POST alla API, che manda la richiesta via REDIS
await ICService.CallRestPost($"{apiUrl}/{GenericBasePath}", $"{calcTag}/{EditRecord.OfferRowUID}", calcRequestDTO);
}
EditRecord = null;
CurrEditMode = EditMode.None;
}
}
private void ConfInit()
{
apiUrl = Config.GetValue<string>("ServerConf:Prog.ApiUrl") ?? "";
imgBasePath = Config.GetValue<string>("ServerConf:ImageBaseUrl") ?? "";
GenericBasePath = Config.GetValue<string>("ServerConf:GenericBaseUrl") ?? "";
calcTag = Config.GetValue<string>("ServerConf:CalcTag") ?? "calc";
subChannel = Config.GetValue<string>("ServerConf:SvgChannel") ?? "";
}
private async Task DoRecalcOffer()
{
isLoading = true;
// ciclo sulle righe...
await setAwaitPrice(true, false);
await Task.Delay(50);
await DLService.OffertUpdateCost(OfferID);
await Task.Delay(50);
await setAwaitPrice(false, true);
await ReloadData();
UpdateTable();
isLoading = false;
await EC_Updated.InvokeAsync(true);
}
/// <summary>
/// Svuota cache corrente + rilegge dati
/// </summary>
private async Task ForceReloadData()
{
isLoading = true;
CurrEditMode = EditMode.None;
EditRecord = null;
await Task.Delay(20);
await DLService.FlushCacheOffersAsync();
await Task.Delay(20);
await ReloadData();
UpdateTable();
isLoading = false;
}
/// <summary>
/// Preparazione dati x componente edit JWD
/// </summary>
/// <param name="currJwd"></param>
private void PrepareWindowData(string currJwd)
{
// preparo conf oggetti x controllo
SetupList = SetupList = new BaseListPayload()
{
ColorMaterial = AvailColorMaterialList,
FamilyHardware = AvailFamilyHardwareList,
Glass = AvailGlassList,
Hardware = AvailHardwareList,
Material = AvailMaterialList,
Profile = AvailProfileList,
TemplateDTO = null
};
CurrData = new LivePayload()
{
CurrJwd = currJwd,
SvgPreview = currSvg
};
}
/// <summary>
/// init classi configurazione
/// </summary>
/// <returns></returns>
private async Task ReloadBaseList()
{
// lettura config setup varie da DB/Cache Redis
AllConfEnvir = await DLService.ConfEnvirParamGetAllAsync();
AllConfGlass = await DLService.ConfGlassGetAllAsync();
var rawHw = CDService.ElencoHw(EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW, "HW.AGB");
// hw filtro solo validi...
AllConfHardware = rawHw
.Where(x => !x.FamilyName.Equals(x.Description, StringComparison.OrdinalIgnoreCase))
.ToList();
AllConfProfile = await DLService.ConfProfileGetAllAsync();
AllConfWood = await DLService.ConfWoodGetAllAsync();
AllColors = await DLService.GenValGetFiltAsync("WoodCol");
// conversione tipi
AvailGlassList = AllConfGlass
.Select(x => x.Description)
.ToList();
AvailProfileList = AllConfProfile
.Select(x => x.Description)
.ToList();
AvailFamilyHardwareList = AllConfHardware
.DistinctBy(x => x.FamilyName)
.OrderBy(x => x.FamilyName)
.Select(x => x.FamilyName)
.ToList();
AvailHardwareList = AllConfHardware
.Select(x => new Egw.Window.Data.Hardware(x.Id, x.FamilyName, x.Description, x.OpeningType, x.Shape, x.SashQty, x.SashPosition))
.ToList();
AvailMaterialList = AllConfWood
.Select(x => x.Description)
.ToList();
// FixMe Todo: aggiunta profili (manca anche nel costruttore...)
AvailColorMaterialList = AllColors
.OrderBy(x => x.Ordinal)
.Select(x => x.ValString)
.ToList();
}
/// <summary>
@@ -139,22 +763,174 @@ namespace Lux.UI.Components.Compo
}
/// <summary>
/// salva nel record corrente la BOM aggiornata e poi ricalcola importo...
/// Effettua vera richiesta della BOM
/// </summary>
/// <param name="currRec"></param>
/// <returns></returns>
private async Task reqBomUpdate(OfferRowModel currRec)
{
// salvo richiesta BOM su record
currRec.AwaitBom = true;
currRec.AwaitPrice = true;
await DLService.OffertUpdateAwaitState(currRec.OfferRowID, true, true);
// preparo la domanda serializzata
Dictionary<string, string> DictExec = new Dictionary<string, string>();
// verifico parametri da conf envir...
var envRec = AllConfEnvir.FirstOrDefault(x => x.EnvirID == currRec.Envir);
string serKey = envRec != null ? envRec.SerStrucKey : "Jwd";
// cablata la BOM
DictExec.Add("Mode", $"{(int)Egw.Window.Data.Enums.QuestionModes.BOM}");
//DictExec.Add("Mode", "2");
// UID cablato x ora...
DictExec.Add("UID", currRec.OfferRowUID);
DictExec.Add(serKey, currRec.SerStruct);
CalcRequestDTO req = new CalcRequestDTO()
{
EnvType = currRec.Envir,
//EnvType = currEnv,
DictExec = DictExec
};
await InvokeAsync(StateHasChanged);
// chiamo la chiamata POST alla API, che manda la richiesta via REDIS
await CService.CallRestPost($"{apiUrl}/{genBasePath}", $"{calcTag}/{currRec.OfferRowUID}", req);
}
/// <summary>
/// Esegue salvataggio del file ricevuto
/// </summary>
/// <param name="secureName">Nome secure da impiegare</param>
/// <param name="content">Contenuto file</param>
private bool saveFileContent(string folderPath, string secureName, string content)
{
bool answ = false;
if (!string.IsNullOrEmpty(folderPath))
{
// calcolo path file...
string filePath = Path.Combine("unsafe_uploads", folderPath, secureName);
File.WriteAllText(filePath, content);
}
return answ;
}
/// <summary>
/// Salvataggio del JWD aggiornato nella mia riga di offerta
/// </summary>
/// <param name="args"></param>
private async void SaveJWD(Dictionary<string, string> args)
{
// ...se ho editing
if (EditRecord != null)
{
// SE contiene il mio Jwd...
if (args.ContainsKey("Jwd"))
{
reqDict = args;
string serStruct = reqDict["Jwd"];
// controllo SE variato...
if (!prevJwd.Equals(serStruct))
{
// aggiorno val prev
prevJwd = serStruct;
// aggiorno live data
CurrData.CurrJwd = serStruct;
// chiamo richiesta update
CalcRequestDTO calcRequestDTO = new CalcRequestDTO();
calcRequestDTO.EnvType = EditRecord.Envir;
calcRequestDTO.DictExec = reqDict;
// chiamo la chiamata POST alla API, che manda la richiesta via REDIS
await ICService.CallRestPost($"{apiUrl}/{GenericBasePath}", $"{calcTag}/{EditRecord.OfferRowUID}", calcRequestDTO);
#if false
// salvo su DB!
await DLService.OffertRowUpdateSerStruct(EditRecord.OfferRowID, serStruct);
#endif
}
}
}
}
private async Task setAwaitPrice(bool awaitPrice, bool flushCache)
{
foreach (var item in AllRecords)
{
await DLService.OffertUpdateAwaitState(item.OfferRowID, false, awaitPrice, flushCache);
}
}
/// <summary>
/// Elenco SalesOfferRows calcolabili:
/// - contengono serializzazione come JWD
/// - contengono file come BTL
/// </summary>
private List<OfferRowModel> SorListCalc()
{
var rawList = AllRecords
.Where(x => (!string.IsNullOrEmpty(x.SerStruct) && x.SerStruct.Length > 2) || (!string.IsNullOrEmpty(x.FileName) && !string.IsNullOrEmpty(x.FileResource)))
.ToList();
return rawList ?? new List<OfferRowModel>();
}
/// <summary>
/// Toggle visibilità modifica file indicando ID della OfferRow corrente (o zero se deselect)
/// </summary>
private void ToggleFileEdit(OfferRowModel? currRec)
{
CurrEditMode = currRec == null ? EditMode.None : EditMode.File;
EditRecord = currRec;
}
/// <summary>
/// Salva nel record corrente la BOM aggiornata e poi ricalcola importo...
/// </summary>
/// <param name="newBomList"></param>
/// <exception cref="NotImplementedException"></exception>
private async void UpdateBom(List<BomItemDTO> newBomList)
{
if (EditBomRecord != null)
if (EditRecord != null)
{
// salvo BOM nel record corrente...
bool fatto = await DLService.OffertRowUpdateBom(EditBomRecord.OfferRowID, newBomList);
bool fatto = await DLService.OffertRowUpdateBom(EditRecord.OfferRowID, newBomList);
// ricalcolo offerta completa
await ReloadData();
UpdateTable();
}
}
/// <summary>
/// Task verifica update ricevuti
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <exception cref="NotImplementedException"></exception>
private async void UpdatePipe_EA_NewMessage(object? sender, EventArgs e)
{
// aggiorno visualizzazione
PubSubEventArgs currArgs = (PubSubEventArgs)e;
// conversione on-the-fly SVG da mostrare
if (!string.IsNullOrEmpty(currArgs.newMessage))
{
// cerco se faccia aprte dei record correnti...
var recFound = AllRecords.Any(x => x.OfferRowUID == currArgs.newMessage);
if (recFound)
{
isLoading = true;
await Task.Delay(10);
#if false
if (currArgs.msgUid.Equals($"{subChannel}:{windowUid}"))
{
}
#endif
// se si tratta dell'UID corrente --> fa update
await DoRecalcOffer();
await InvokeAsync(StateHasChanged);
}
}
await Task.Delay(1);
}
/// <summary>
/// Filtro e paginazione
/// </summary>
@@ -162,12 +938,59 @@ namespace Lux.UI.Components.Compo
{
// fix paginazione
ListRecords = AllRecords
.OrderBy(x => x.RowNum)
.Skip(numRecord * (currPage - 1))
.Take(numRecord)
.ToList();
isLoading = false;
}
/// <summary>
/// Esegue lettura file + invio richiesta specifica
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
private async Task UploadFile(InputFileChangeEventArgs e)
{
if (EditRecord != null)
{
// init dizionari arg richiesta update
Dictionary<string, string> fileArgs = new Dictionary<string, string>();
// leggo il contenuto del PRIMO (singolo) file
IBrowserFile file = e.File;
// limite file size (al momento 10 MB)
var maxAllowedSize = 10 * 1024 * 1024;
using var stream = file.OpenReadStream(maxAllowedSize);
using var reader = new StreamReader(stream);
string rawContent = await reader.ReadToEndAsync();
// calcolo il nome del file trusted...
string trustedFileName = Path.GetRandomFileName();
EditRecord.FileResource = trustedFileName;
EditRecord.FileName = file.Name;
EditRecord.FileSize = rawContent.LongCount();
// salvo sul DB i dati (nome, nome sicuro, size...)
await DLService.OffertRowUpdateFileData(EditRecord);
// parametri richiesta
fileArgs.Add("Mode", $"{(int)Egw.Window.Data.Enums.QuestionModes.PREVIEW}");
fileArgs.Add("Btl", rawContent);
// invio!
CalcRequestDTO calcRequestDTO = new CalcRequestDTO();
calcRequestDTO.EnvType = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.BEAM;
calcRequestDTO.DictExec = fileArgs;
await ICService.CallRestPost($"{apiUrl}/{GenericBasePath}", $"{calcTag}/{EditRecord.OfferRowUID}", calcRequestDTO);
#if false
// salvo in locale il file: SISTEMARE PERMESSI
saveFileContent(EditFileRecord.OfferRowUID, trustedFileName, rawContent);
#endif
}
}
#endregion Private Methods
}
}
+17 -1
View File
@@ -39,19 +39,35 @@
<span class="fa-solid fa-book px-2 fs-4" aria-hidden="true"></span> <span class="@hideText">Articoli</span>
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="JobRoute">
<span class="fa-solid fa-route px-2 fs-4" aria-hidden="true"></span> <span class="@hideText">Cicli</span>
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="Offers">
<span class="fa-solid fa-cart-shopping px-2 fs-4" aria-hidden="true"></span> <span class="@hideText">Offerte</span>
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="GenList">
<span class="fa-solid fa-list-check px-2 fs-4" aria-hidden="true"></span> <span class="@hideText">Anagrafiche</span>
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="ConfList">
<span class="fa-solid fa-list-check px-2 fs-4" aria-hidden="true"></span> <span class="@hideText">Configurazioni</span>
</NavLink>
</div>
@* <div class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="bi bi-list-ol px-2 fs-4" aria-hidden="true"></span> <span class="@hideText">Counter</span>
</NavLink>
</div> *@
<div class="nav-item px-3">
<NavLink class="nav-link" href="scratch">
<span class="bi bi-cloud-moon px-2 fs-4" aria-hidden="true"></span> <span class="@hideText">Scratch</span>
+41
View File
@@ -0,0 +1,41 @@
@page "/ConfList"
<div class="card shadow">
<div class="card-header">
<div class=" d-flex justify-content-between">
<div class="px-0 fs-3">
<b>Configurazioni</b>
</div>
@* <div class="px-0 d-flex justify-content-between">
<div class="px-1">
<div class="input-group input-group-sm" title="ricerca">
<span class="input-group-text"><i class="fas fa-search"></i></span>
<input type="text" class="form-control" @bind="@SearchVal">
<button class="btn btn-outline-secondary" @onclick="ResetSearch"><i class="fas fa-ban"></i></button>
</div>
</div>
</div> *@
</div>
</div>
<div class="card-body py-1">
<div class="row">
<div class="col-12 col-md-6 g-2" style="min-height: 24rem;">
<ProfileMan></ProfileMan>
</div>
<div class="col-12 col-md-6 g-2" style="min-height: 24rem;">
<WoodMan></WoodMan>
</div>
<div class="col-12 col-md-6 g-2" style="min-height: 24rem;">
<GlassMan></GlassMan>
</div>
<div class="col-12 col-md-6 g-2" style="min-height: 24rem;">
<HardwareMan></HardwareMan>
</div>
</div>
</div>
<div class="card-footer">
<div class="small">NB: le configurazioni possono essere RW o Read-Only (ricevute dal sistema CAD/CAM di calcolo)</div>
</div>
</div>
+36
View File
@@ -0,0 +1,36 @@
namespace Lux.UI.Components.Pages
{
public partial class ConfList
{
#region Protected Properties
protected string SearchVal
{
get => searchVal;
set
{
isLoading = true;
searchVal = value;
isLoading = false;
}
}
#endregion Protected Properties
#region Protected Methods
protected void ResetSearch()
{
SearchVal = "";
}
#endregion Protected Methods
#region Private Fields
private bool isLoading = false;
private string searchVal = "";
#endregion Private Fields
}
}
+41
View File
@@ -0,0 +1,41 @@
@page "/GenList"
<div class="card shadow">
<div class="card-header">
<div class=" d-flex justify-content-between">
<div class="px-0 fs-3">
<b>Anagrafiche Generiche</b>
</div>
<div class="px-0 d-flex justify-content-between">
<div class="px-1">
<div class="input-group input-group-sm" title="ricerca">
<span class="input-group-text"><i class="fas fa-search"></i></span>
<input type="text" class="form-control" @bind="@SearchVal">
<button class="btn btn-outline-secondary" @onclick="ResetSearch"><i class="fas fa-ban"></i></button>
</div>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="row">
@if (isLoading)
{
<LoadingData></LoadingData>
}
else
{
<div class="col-6">
<GenClassMan ListGenClass="ListGenClass" EC_Selected="SaveSel" EC_Updated="() => ForceReloadAsync()"></GenClassMan>
</div>
<div class="col-6">
@if (!string.IsNullOrEmpty(CurrFilt.SelCodGroup))
{
<GenValMan SelFilt="CurrFilt" ListGenClass="ListGenClass"></GenValMan>
}
</div>
}
</div>
</div>
</div>
+95
View File
@@ -0,0 +1,95 @@
using EgwCoreLib.Lux.Data.DbModel.Items;
using EgwCoreLib.Lux.Data.DbModel.Utils;
using EgwCoreLib.Lux.Data.Services;
using Lux.UI.Components.Compo;
using Microsoft.AspNetCore.Components;
using System.Threading.Tasks;
namespace Lux.UI.Components.Pages
{
public partial class GenList
{
#region Protected Fields
protected List<GenClassModel> ListGenClass = new List<GenClassModel>();
#endregion Protected Fields
#region Protected Properties
protected GenValMan.FiltSelect CurrFilt { get; set; } = new GenValMan.FiltSelect();
[Inject]
protected DataLayerServices DLService { get; set; } = null!;
protected string SearchVal
{
get => CurrFilt.SearchVal;
set
{
isLoading = true;
CurrFilt.SearchVal = value;
isLoading = false;
}
}
protected string SelCodGroup
{
get => CurrFilt.SelCodGroup;
set
{
if (CurrFilt.SelCodGroup != value)
{
isLoading = true;
CurrFilt.SelCodGroup = value;
isLoading = false;
}
}
}
#endregion Protected Properties
#region Protected Methods
protected override async Task OnInitializedAsync()
{
isLoading = true;
await ReloadBaseData();
}
protected void ResetSearch()
{
SearchVal = "";
}
#endregion Protected Methods
#region Private Fields
private ItemModel? editRecord = null;
private bool isLoading = false;
#endregion Private Fields
#region Private Methods
private async Task ForceReloadAsync()
{
await ReloadBaseData();
}
private async Task ReloadBaseData()
{
isLoading = true;
ListGenClass = await DLService.GenClassGetAllAsync();
isLoading = false;
}
private void SaveSel(string codGroup)
{
SelCodGroup = codGroup;
}
#endregion Private Methods
}
}
+34 -4
View File
@@ -1,8 +1,38 @@
@page "/"
@page "/Home"
<PageTitle>Home</PageTitle>
<PageTitle>LUX</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<div class="alert alert-secondary shadow">
<h1>LUX Wood MES</h1>
Development Version
</div>
<div class="row my-3">
<div class="col-4">
<ul class="list-group mb-2 shadow">
<li class="list-group-item bg-primary text-light">Sezioni Attive</li>
<li class="list-group-item">Anagrafiche generiche</li>
<li class="list-group-item">Articoli</li>
<li class="list-group-item">Offerte e ricalcoli</li>
</ul>
</div>
<div class="col-4">
<ul class="list-group mb-2 shadow">
<li class="list-group-item bg-primary bg-opacity-50">Sezioni in sviluppo</li>
<li class="list-group-item">Anagrafiche Configurazioni (RO)</li>
<li class="list-group-item">Configuratore</li>
<li class="list-group-item">Cicli</li>
<li class="list-group-item">Ordini</li>
<li class="list-group-item">Sistema Costing</li>
</ul>
</div>
<div class="col-4">
<ul class="list-group mb-2 shadow">
<li class="list-group-item bg-primary bg-opacity-25">Sezioni TBD</li>
<li class="list-group-item">Pianificazione Produzione</li>
<li class="list-group-item">Rilievo produzione</li>
<li class="list-group-item">Consuntivazione</li>
<li class="list-group-item">Stampe</li>
</ul>
</div>
</div>
+1 -4
View File
@@ -1,4 +1,4 @@
using EgwCoreLib.Lux.Data.DbModel;
using EgwCoreLib.Lux.Data.DbModel.Items;
using EgwCoreLib.Lux.Data.Services;
using Lux.UI.Components.Compo;
using Microsoft.AspNetCore.Components;
@@ -29,9 +29,6 @@ namespace Lux.UI.Components.Pages
isLoading = true;
CurrFilt.SearchVal = value;
isLoading = false;
if (CurrFilt.SearchVal != value)
{
}
}
}

Some files were not shown because too many files have changed in this diff Show More