diff --git a/EgwCoreLib.Lux.Core/EgwCoreLib.Lux.Core.csproj b/EgwCoreLib.Lux.Core/EgwCoreLib.Lux.Core.csproj index b17d3b4e..51a79149 100644 --- a/EgwCoreLib.Lux.Core/EgwCoreLib.Lux.Core.csproj +++ b/EgwCoreLib.Lux.Core/EgwCoreLib.Lux.Core.csproj @@ -21,8 +21,8 @@ - - + + diff --git a/EgwCoreLib.Lux.Core/MachineCalc/Utils.cs b/EgwCoreLib.Lux.Core/MachineCalc/Utils.cs new file mode 100644 index 00000000..c50a8f89 --- /dev/null +++ b/EgwCoreLib.Lux.Core/MachineCalc/Utils.cs @@ -0,0 +1,117 @@ +using EgwCoreLib.Lux.Core.RestPayload; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EgwCoreLib.Lux.Core.MachineCalc +{ + public class Utils + { + + /// + /// Helper calcolo Intersezione lista macchine + /// + /// + /// + /// + public static IEnumerable IntersectTags(List machines, Func predicate) + { + return machines + .Select(m => m.PartList.Where(predicate).Select(p => p.Tag).ToHashSet()) + .Aggregate((set1, set2) => { set1.IntersectWith(set2); return set1; }); + } + + /// + /// Calcolo delle intersezioni Macchine/Tags(Parts) + /// + /// + /// + public static List CalculateIntersections(List machineResult) + { + // Step 1: extract workable tags per machine + var machineTags = machineResult + .ToDictionary( + m => m.Name, + m => m.PartList + .Where(p => p.CalcResult == Enums.PartVerificationResult.MACHINABLE) + .ToList() + ); + + var machineNames = machineTags.Keys.ToList(); + var results = new List(); + + // Step 2: generate all combinations of machines + for (int size = 1; size <= machineNames.Count; size++) + { + foreach (var combo in GetCombinations(machineNames, size)) + { + var comboList = combo.ToList(); + + // Intersection of tags across all machines in this combo + var intersectionTags = comboList + .Select(name => machineTags[name].Select(p => p.Tag).ToHashSet()) + .Aggregate((set1, set2) => { set1.IntersectWith(set2); return set1; }); + + // Compute per-machine sums of Time for these tags + var sums = comboList.Select(name => + machineTags[name] + .Where(p => intersectionTags.Contains(p.Tag)) + .Sum(p => p.Time) + ).ToList(); + + results.Add(new MachineTagDTO + { + Machines = comboList, + Tags = intersectionTags.ToList(), + MinTime = sums.Any() ? sums.Min() : 0, + MaxTime = sums.Any() ? sums.Max() : 0 + }); + } + } + + // Step 3: add unique (non-intersecting) tags per machine + foreach (var name in machineNames) + { + var uniqueTags = machineTags[name] + .Select(p => p.Tag) + .Except(machineNames.Where(n => n != name) + .SelectMany(n => machineTags[n].Select(p => p.Tag))) + .ToList(); + + var sum = machineTags[name] + .Where(p => uniqueTags.Contains(p.Tag)) + .Sum(p => p.Time); + + results.Add(new MachineTagDTO + { + Machines = new List { name }, + Tags = uniqueTags, + MinTime = sum, + MaxTime = sum + }); + } + + return results; + } + + /// + /// Helper generazione combinazioni di items per una data length + /// + /// + /// + /// + /// + public static IEnumerable> GetCombinations(IEnumerable items, int length) + { + if (length == 1) + return items.Select(i => new T[] { i }); + + return GetCombinations(items, length - 1) + .SelectMany(c => items.Where(i => !c.Contains(i)), + (c, i) => c.Concat(new T[] { i })); + } + + } +} diff --git a/EgwCoreLib.Lux.Core/RestPayload/MachineCalcResultDTO.cs b/EgwCoreLib.Lux.Core/RestPayload/MachineCalcResultDTO.cs index 76951f93..5fb8e956 100644 --- a/EgwCoreLib.Lux.Core/RestPayload/MachineCalcResultDTO.cs +++ b/EgwCoreLib.Lux.Core/RestPayload/MachineCalcResultDTO.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection.PortableExecutable; using System.Text; using System.Threading.Tasks; @@ -11,14 +12,90 @@ namespace EgwCoreLib.Lux.Core.RestPayload /// public class MachineCalcResultDTO { + #region Public Properties + /// /// Nome macchina /// public string Name { get; set; } = ""; + /// + /// Numero di parts totali + /// + public int NumParts + { + get => PartList.Count; + } + + /// + /// Numero di Parts con errori di calcolo + /// + public int NumPartsCalcFail + { + get => PartListCalcFail.Count(); + } + + /// + /// Numero di Parts NON OK (errori calcolo o non lavorabili) + /// + public int NumPartsKo + { + get => PartListKo.Count(); + } + + /// + /// Numero di Parts NON lavorabili + /// + public int NumPartsNotMach + { + get => PartListNotMach.Count(); + } + + /// + /// Numero di Parts lavorabili dalla macchina + /// + public int NumPartsOk + { + get => PartListOk.Count(); + } + /// /// Elenco delle parts ed esito stima lavorabilità /// public List PartList { get; set; } = new List(); + + /// + /// Elenco parts con errori calcolo + /// + public List PartListCalcFail + { + get => PartList.Where(x => x.CalcResult == Enums.PartVerificationResult.CALCULATIONFAILED).ToList(); + } + + /// + /// Elenco delle parts KO / non "healthy" (non lavorabili o errore calcolo) + /// + public List PartListKo + { + get => PartList.Where(x => x.CalcResult != Enums.PartVerificationResult.MACHINABLE).ToList(); + } + + /// + /// Elenco parts NON lavorabili + /// + public List PartListNotMach + { + get => PartList.Where(x => x.CalcResult == Enums.PartVerificationResult.NOTMACHINABLE).ToList(); + } + + /// + /// Elenco parts lavorabili + /// + public List PartListOk + { + get => PartList.Where(x => x.CalcResult == Enums.PartVerificationResult.MACHINABLE).ToList(); + } + + #endregion Public Properties } -} +} \ No newline at end of file diff --git a/EgwCoreLib.Lux.Core/RestPayload/MachineTagDTO.cs b/EgwCoreLib.Lux.Core/RestPayload/MachineTagDTO.cs new file mode 100644 index 00000000..9e3bd795 --- /dev/null +++ b/EgwCoreLib.Lux.Core/RestPayload/MachineTagDTO.cs @@ -0,0 +1,10 @@ +namespace EgwCoreLib.Lux.Core.RestPayload +{ + public class MachineTagDTO + { + public List Machines { get; set; } = new List(); + public List Tags { get; set; } = new List(); + public decimal MinTime { get; set; } + public decimal MaxTime { get; set; } + } +} diff --git a/EgwCoreLib.Lux.Core/RestPayload/WorkLoadDetailDTO.cs b/EgwCoreLib.Lux.Core/RestPayload/WorkLoadDetailDTO.cs new file mode 100644 index 00000000..234a6d10 --- /dev/null +++ b/EgwCoreLib.Lux.Core/RestPayload/WorkLoadDetailDTO.cs @@ -0,0 +1,99 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection.PortableExecutable; +using System.Text; +using System.Threading.Tasks; + +namespace EgwCoreLib.Lux.Core.RestPayload +{ + /// + /// Classe per definizione WorkLoad in dettaglio x un dato task (POR tipicamente) + /// + public class WorkLoadDetailDTO + { + #region Public Constructors + + /// + /// Init classe aprtendo dal valore serializzato di una stima di lavorabilità di un item d'ordine minimo (POR) + /// + /// + /// + public WorkLoadDetailDTO(string UID, string rawData) + { + uID = UID; + // deserializzo risultati calcolo + machineCalcResults = JsonConvert.DeserializeObject>(rawData) ?? new List(); + // calcolo "esito sinteico" della lavorabilità (NESSUNA part UnHealthy su ogni macchina) + workable = machineCalcResults.Sum(x => x.NumPartsKo) == 0; + // se almeno 1 verifico SE ci siano part unhealty su OGNI macchina (altrimenti è cmq lavorabile + if (!workable) + { + var listKo = new List(); + // cerco l'insieme dei pezzi DAVVERO non lavorabili + var listUnWorkable = MachineCalc.Utils.IntersectTags(machineCalcResults, p => p.CalcResult != Enums.PartVerificationResult.MACHINABLE); + workable = !listUnWorkable.Any(); + numKo = listUnWorkable.Count(); + } + + LoadDetail = MachineCalc.Utils.CalculateIntersections(machineCalcResults); + + } + + /// + /// Dettaglio combinazioni carico di lavoro + /// + public List LoadDetail { get; set; } = new List(); + + /// + /// Tempo minimo complessivo + /// + public decimal TotMinTime + { + get => LoadDetail.Sum(x => x.MinTime); + } + + /// + /// Tempo massimo complessivo + /// + public decimal TotMaxTime + { + get => LoadDetail.Sum(x => x.MaxTime); + } + + #endregion Public Constructors + + #region Public Properties + + public List MachineCalcResults + { + get => machineCalcResults; + } + + public string UID + { + get => uID; + } + + public bool Workable + { + get => workable; + } + public int NumKo + { + get => numKo; + } + + #endregion Public Properties + + #region Private Fields + + private List machineCalcResults = new List(); + private string uID = ""; + private int numKo = 0; + private bool workable = false; + + #endregion Private Fields + } +} \ No newline at end of file diff --git a/EgwCoreLib.Lux.Data/EgwCoreLib.Lux.Data.csproj b/EgwCoreLib.Lux.Data/EgwCoreLib.Lux.Data.csproj index 232a1d19..2184629e 100644 --- a/EgwCoreLib.Lux.Data/EgwCoreLib.Lux.Data.csproj +++ b/EgwCoreLib.Lux.Data/EgwCoreLib.Lux.Data.csproj @@ -27,7 +27,7 @@ - + diff --git a/Lux.API/Lux.API.csproj b/Lux.API/Lux.API.csproj index 4b424721..10c81d37 100644 --- a/Lux.API/Lux.API.csproj +++ b/Lux.API/Lux.API.csproj @@ -4,7 +4,7 @@ net8.0 enable enable - 0.9.2512.0418 + 0.9.2512.0518 diff --git a/Lux.API/Services/ExternalMessageProcessor.cs b/Lux.API/Services/ExternalMessageProcessor.cs index d0db07a4..458eaac0 100644 --- a/Lux.API/Services/ExternalMessageProcessor.cs +++ b/Lux.API/Services/ExternalMessageProcessor.cs @@ -154,7 +154,9 @@ namespace Lux.API.Services // deserializzo try { - machineEstimList = JsonConvert.DeserializeObject>(rawAnsw); +#if false + machineEstimList = JsonConvert.DeserializeObject>(rawAnsw); +#endif await dbService.SaveProdEstimateAsync(UID, retData.ExecEnvironment, rawAnsw); } catch (Exception exc) diff --git a/Lux.UI.Client/Lux.UI.Client.csproj b/Lux.UI.Client/Lux.UI.Client.csproj index 3043de06..3cf0dfd9 100644 --- a/Lux.UI.Client/Lux.UI.Client.csproj +++ b/Lux.UI.Client/Lux.UI.Client.csproj @@ -9,7 +9,7 @@ - + diff --git a/Lux.UI/Components/Compo/OfferRowMan.razor.cs b/Lux.UI/Components/Compo/OfferRowMan.razor.cs index fa0e9660..af50c4c7 100644 --- a/Lux.UI/Components/Compo/OfferRowMan.razor.cs +++ b/Lux.UI/Components/Compo/OfferRowMan.razor.cs @@ -1277,18 +1277,18 @@ namespace Lux.UI.Components.Compo /// Esecuzione azione richiesta /// /// Azione richiesta - private void DoAction(TableComp.DataAction actReq) + private void DoAction(LayoutConst.DataAction actReq) { switch (actReq) { - case TableComp.DataAction.None: + case LayoutConst.DataAction.None: break; - case TableComp.DataAction.ResetDictShape: + case LayoutConst.DataAction.ResetDictShape: CurrData.DictShape = new Dictionary(); break; - case TableComp.DataAction.ResetHwOpt: + case LayoutConst.DataAction.ResetHwOpt: CurrData.DictOptionsXml = new Dictionary(); break; diff --git a/Lux.UI/Components/Compo/OrderRowMan.razor b/Lux.UI/Components/Compo/OrderRowMan.razor index 9d9250e4..94be2d1c 100644 --- a/Lux.UI/Components/Compo/OrderRowMan.razor +++ b/Lux.UI/Components/Compo/OrderRowMan.razor @@ -83,15 +83,18 @@ else Codice Descrizione Qty - Importo + @* Importo *@ + # Prod @if (DisplayMode == EgwCoreLib.Lux.Core.Enums.DisplayMode.Edit) { Mat. Lav. } - Totale - Marg. + @* Totale + Marg. *@ + # KO + Timing @if (DisplayMode == EgwCoreLib.Lux.Core.Enums.DisplayMode.Edit) { @@ -102,6 +105,7 @@ else @foreach (var item in ListRecords) { bool isNote = item.SellingItemID == null; + var itemWLD = WorkLoadDetail(item.OrderRowUID, item.ProdEstimate); @@ -214,7 +218,7 @@ else @item.Qty } - + @*
@if (!(item.BomOk && item.ItemOk)) { @@ -227,6 +231,19 @@ else @($"{item.UnitPrice:C2}")
(@item.UnitCost.ToString("C2"))
+ *@ + +
+ @($"{item.ProdItemQtyTot:N0}") +
+ @if (@itemWLD.Workable) + { + + } + else + { + + } @if (DisplayMode == EgwCoreLib.Lux.Core.Enums.DisplayMode.Edit) { @@ -251,7 +268,7 @@ else @item.ProdItemQtyTot } - + @*
@if (item.AwaitPrice) { @@ -263,6 +280,14 @@ else @item.MaxDiscount.ToString("P2") + *@ + +
+ @($"{itemWLD.NumKo}") +
+ + + @($"{itemWLD.TotMinTime} - {itemWLD.TotMaxTime}") } @if (DisplayMode == EgwCoreLib.Lux.Core.Enums.DisplayMode.Edit) diff --git a/Lux.UI/Components/Compo/OrderRowMan.razor.cs b/Lux.UI/Components/Compo/OrderRowMan.razor.cs index b96e7f0c..7d27d74c 100644 --- a/Lux.UI/Components/Compo/OrderRowMan.razor.cs +++ b/Lux.UI/Components/Compo/OrderRowMan.razor.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Components.Forms; using Microsoft.JSInterop; using Newtonsoft.Json; using NLog; +using System.Xml; using WebWindowComplex; using WebWindowComplex.DTO; using static EgwCoreLib.Lux.Core.Enums; @@ -226,7 +227,7 @@ namespace Lux.UI.Components.Compo await DLService.OffertRowUpsert(newNote); #endif await ReloadData(); - UpdateTable(); + UpdateTable(); } /// @@ -1277,18 +1278,18 @@ namespace Lux.UI.Components.Compo /// Esecuzione azione richiesta /// /// Azione richiesta - private void DoAction(TableComp.DataAction actReq) + private void DoAction(LayoutConst.DataAction actReq) { switch (actReq) { - case TableComp.DataAction.None: + case LayoutConst.DataAction.None: break; - case TableComp.DataAction.ResetDictShape: + case LayoutConst.DataAction.ResetDictShape: CurrData.DictShape = new Dictionary(); break; - case TableComp.DataAction.ResetHwOpt: + case LayoutConst.DataAction.ResetHwOpt: CurrData.DictOptionsXml = new Dictionary(); break; @@ -1297,6 +1298,18 @@ namespace Lux.UI.Components.Compo } } + /// + /// Restituisce struttura dettaglio WorkLoad x item + /// + /// + /// + /// + protected WorkLoadDetailDTO WorkLoadDetail(string UID, string rawData) + { + var currWLD = new WorkLoadDetailDTO(UID, rawData); + return currWLD; + } + /// /// Salvataggio del JWD aggiornato nella mia riga di offerta /// diff --git a/Lux.UI/Components/Pages/Orders.razor b/Lux.UI/Components/Pages/Orders.razor index c6caa784..f08cb61f 100644 --- a/Lux.UI/Components/Pages/Orders.razor +++ b/Lux.UI/Components/Pages/Orders.razor @@ -261,8 +261,11 @@ else ID Date Stato - Codice - Agente/Riv + Codice + @if (SelRecord == null) + { + Agente/Riv + } Cliente @if (SelRecord == null) { @@ -271,8 +274,11 @@ else - Importo - Marg. + @if (SelRecord == null) + { + Importo + Marg. + } @@ -295,13 +301,16 @@ else @item.OrderCode
@item.Envir
- - @if (item.DealerNav != null) - { -
@item.DealerNav.FirstName @item.DealerNav.LastName
-
@item.DealerNav.VAT
- } - + @if (SelRecord == null) + { + + @if (item.DealerNav != null) + { +
@item.DealerNav.FirstName @item.DealerNav.LastName
+
@item.DealerNav.VAT
+ } + + } @if (item.CustomerNav != null) { @@ -321,7 +330,8 @@ else @item.NumProdItems - + @if (SelRecord == null) + {
@item.TotalPrice.ToString("C2")
(@item.TotalCost.ToString("C2"))
@@ -329,6 +339,7 @@ else @item.MaxDiscount.ToString("P2") + } } diff --git a/Lux.UI/Lux.UI.csproj b/Lux.UI/Lux.UI.csproj index 461ebdf1..b559b34e 100644 --- a/Lux.UI/Lux.UI.csproj +++ b/Lux.UI/Lux.UI.csproj @@ -5,7 +5,7 @@ enable enable aspnet-Lux.UI-a758c101-a2f4-4e38-977d-1c4887dbbd50 - 0.9.2512.0418 + 0.9.2512.0518 @@ -17,7 +17,7 @@ - + diff --git a/Resources/ChangeLog.html b/Resources/ChangeLog.html index e51fc680..0f8bdbad 100644 --- a/Resources/ChangeLog.html +++ b/Resources/ChangeLog.html @@ -1,6 +1,6 @@ LUX - Web Windows MES -

Versione: 0.9.2512.0418

+

Versione: 0.9.2512.0518


Note di rilascio:
  • diff --git a/Resources/VersNum.txt b/Resources/VersNum.txt index a27b7f6e..d7dc2b74 100644 --- a/Resources/VersNum.txt +++ b/Resources/VersNum.txt @@ -1 +1 @@ -0.9.2512.0418 +0.9.2512.0518 diff --git a/Resources/manifest.xml b/Resources/manifest.xml index 0b9f91fa..170a0dbb 100644 --- a/Resources/manifest.xml +++ b/Resources/manifest.xml @@ -1,6 +1,6 @@ - 0.9.2512.0418 + 0.9.2512.0518 http://nexus.steamware.net/repository/SWS/GPW/stable/GPW.UI.zip http://nexus.steamware.net/repository/SWS/GPW/stable/ChangeLog.html false