diff --git a/MP.Data/Controllers/MpSpecController.cs b/MP.Data/Controllers/MpSpecController.cs index 105297cf..f5cc5192 100644 --- a/MP.Data/Controllers/MpSpecController.cs +++ b/MP.Data/Controllers/MpSpecController.cs @@ -6,6 +6,7 @@ using NLog; using System; using System.Collections.Generic; using System.Linq; +using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; namespace MP.Data.Controllers @@ -183,7 +184,7 @@ namespace MP.Data.Controllers using (var dbCtx = new MoonProContext(_configuration)) { dbResult = dbCtx - .DbSetArticoli + .DbSetArticoli .FromSqlRaw("EXEC stp_ART_getUsed") .AsNoTracking() .ToList(); @@ -191,6 +192,26 @@ namespace MP.Data.Controllers return dbResult; } + /// + /// Statistiche ODL calcolate (da stored stp_STAT_ODL) + /// + /// + public async Task> StatOdl(int IdxOdl) + { + List dbResult = new List(); + using (var dbCtx = new MoonProContext(_configuration)) + { + var IdxODL = new SqlParameter("@IdxODL", IdxOdl); + + dbResult = await dbCtx + .DbSetStatOdl + .FromSqlRaw("EXEC stp_STAT_ODL @IdxODL", IdxODL) + .AsNoTracking() + .ToListAsync(); + } + return dbResult; + } + /// /// Update Record /// @@ -313,10 +334,11 @@ namespace MP.Data.Controllers /// macchina (ordinato x data registrazione) /// /// * = tutte, altrimenti solo x una data macchina + /// * = tutti, altrimenti solo x un dato articolo /// Data di riferimento (Massima) per estrazioen records /// numero massimo record da restituire /// - public List DossiersGetLastFilt(string IdxMacchina, DateTime DtRef, int MaxRec) + public List DossiersGetLastFilt(string IdxMacchina, string CodArticolo, DateTime DtRef, int MaxRec) { List dbResult = new List(); using (var dbCtx = new MoonProContext(_configuration)) @@ -324,9 +346,10 @@ namespace MP.Data.Controllers dbResult = dbCtx .DbSetDossiers .AsNoTracking() - .Where(x => (IdxMacchina == "*" || x.IdxMacchina == IdxMacchina) && x.DtRif <= DtRef) + .Where(x => (IdxMacchina == "*" || x.IdxMacchina == IdxMacchina) && (CodArticolo == "*" || x.OdlNav.CodArticolo == CodArticolo) && x.DtRif <= DtRef) .Include(m => m.MachineNav) .Include(o => o.OdlNav) + .Include(a => a.OdlNav.ArticoloNav) .OrderByDescending(x => x.DtRif) .Take(MaxRec) .ToList(); @@ -434,19 +457,21 @@ namespace MP.Data.Controllers /// Stato ODL: true=in corso/completato /// Cod articolo /// KeyRich (parziale) da cercare (es cod stato x yacht) + /// Data inizio + /// Data fine /// - public List ListODLFilt(bool inCorso, string codArt, string keyRichPart) + public List ListODLFilt(bool inCorso, string codArt, string keyRichPart, DateTime startDate, DateTime endDate) { List dbResult = new List(); using (var dbCtx = new MoonProContext(_configuration)) { dbResult = dbCtx .DbSetODL - .Where(x => ((inCorso && x.DataFine == null) || (!inCorso && x.DataFine != null)) && (x.KeyRichiesta.Contains(keyRichPart) || keyRichPart == "*") && (codArt == "*" || x.CodArticolo.Contains(codArt))) + .Where(x => ((inCorso && x.DataFine == null) || ((!inCorso && x.DataFine != null) && x.DataInizio >= startDate && x.DataInizio <= endDate)) && (x.KeyRichiesta.Contains(keyRichPart) || keyRichPart == "*") && (codArt == "*" || x.CodArticolo.Contains(codArt))) .AsNoTracking() .Include(m => m.MachineNav) .Include(a => a.ArticoloNav) - .OrderBy(x => x.IdxOdl) + .OrderByDescending(x => x.IdxOdl) .ToList(); } return dbResult; @@ -518,15 +543,34 @@ namespace MP.Data.Controllers /// Elenco id Macchine che abbiano dati FLuxLog /// /// - public List MacchineWithFlux() + public async Task> MacchineWithFlux() + { + List dbResult = new List(); + using (var dbCtx = new MoonProContext(_configuration)) + { + dbResult = await dbCtx + .DbSetFluxLog + .AsNoTracking() + .Select(i => i.IdxMacchina) + .Distinct() + .ToListAsync(); + } + return dbResult; + } + + /// + /// Elenco codice articoli che abbiano dati Dossier + /// + /// + public List ArticleWithDossier() { List dbResult = new List(); using (var dbCtx = new MoonProContext(_configuration)) { dbResult = dbCtx - .DbSetFluxLog + .DbSetDossiers .AsNoTracking() - .Select(i => i.IdxMacchina) + .Select(i => i.OdlNav.CodArticolo) .Distinct() .ToList(); } diff --git a/MP.Data/DatabaseModels/Dossiers.cs b/MP.Data/DatabaseModels/Dossiers.cs index bda35dbc..66cc2fe8 100644 --- a/MP.Data/DatabaseModels/Dossiers.cs +++ b/MP.Data/DatabaseModels/Dossiers.cs @@ -23,11 +23,13 @@ namespace MP.Data.DatabaseModels public DateTime DtRif { get; set; } [MaxLength(50)] - public string IdxMacchina { get; set; } + public string IdxMacchina { get; set; } = ""; + [MaxLength(50)] + public string CodArticolo { get; set; } = ""; - public int IdxODL { get; set; } + public int IdxODL { get; set; } = 0; - public string Valore { get; set; } + public string Valore { get; set; } = ""; diff --git a/MP.Data/DatabaseModels/ODLModel.cs b/MP.Data/DatabaseModels/ODLModel.cs index 7e7c0c15..6b140ff1 100644 --- a/MP.Data/DatabaseModels/ODLModel.cs +++ b/MP.Data/DatabaseModels/ODLModel.cs @@ -33,6 +33,26 @@ namespace MP.Data.DatabaseModels [MaxLength(50)] public string CodCli { get; set; } = ""; + [NotMapped] + public string DurataMinuti + { + get + { + string answ = ""; + DateTime end = DataFine != null ? (DateTime)DataFine : DateTime.Now; + var tsDurata = (end).Subtract((DateTime)DataInizio); + if (tsDurata.TotalDays < 1) + { + answ = $"{tsDurata.Hours:00}h {tsDurata.Minutes:00}'"; + } + else + { + answ = $"{tsDurata.Days}gg {tsDurata.Hours:00}h"; + } + return answ; + } + } + /// /// Navigazione oggetto Machine /// diff --git a/MP.Data/DatabaseModels/StatODLModel.cs b/MP.Data/DatabaseModels/StatODLModel.cs new file mode 100644 index 00000000..2594af04 --- /dev/null +++ b/MP.Data/DatabaseModels/StatODLModel.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + + +#nullable disable +// +// This is here so CodeMaid doesn't reorganize this document +// +namespace MP.Data.DatabaseModels +{ + public partial class StatODLModel + { + #region Public Properties + + [Key] + public int IdxStato { get; set; } + public string Descrizione { get; set; } = ""; + public string Semaforo { get; set; } + public string Css { get; set; } + public double TotDurata { get; set; } + + #endregion Public Properties + } +} \ No newline at end of file diff --git a/MP.Data/MoonProContext.cs b/MP.Data/MoonProContext.cs index 9d62d4f3..e35a351e 100644 --- a/MP.Data/MoonProContext.cs +++ b/MP.Data/MoonProContext.cs @@ -48,6 +48,7 @@ namespace MP.Data public virtual DbSet DbSetPODL { get; set; } public virtual DbSet DbSetFluxLog { get; set; } public virtual DbSet DbSetDossiers { get; set; } + public virtual DbSet DbSetStatOdl { get; set; } #endregion Public Properties diff --git a/MP.Data/Utils.cs b/MP.Data/Utils.cs index b1d5b6d0..6f3ee38d 100644 --- a/MP.Data/Utils.cs +++ b/MP.Data/Utils.cs @@ -57,17 +57,39 @@ namespace MP.Data return answ; } - public static async Task SaveToCsv(List reportData, string path) + /// + /// Effettua salvataggio in file di un generico oggetto in formato CSV + /// + /// + /// + /// + /// Separatore da impiegare + /// + public static async Task SaveToCsv(List reportData, string path, char separator) { var lines = new List(); IEnumerable props = TypeDescriptor.GetProperties(typeof(T)).OfType(); var header = string.Join(";", props.ToList().Select(x => x.Name)); lines.Add(header); - var valueLines = reportData.Select(row => string.Join(";", header.Split(';').Select(a => row.GetType().GetProperty(a).GetValue(row, null)))); + var valueLines = reportData.Select(row => string.Join(separator, header.Split(separator).Select(a => row.GetType().GetProperty(a).GetValue(row, null)))); + //var valueLines = reportData.Select(row => string.Join(";", header.Split(';').Select(a => row.GetType().GetProperty(a).GetValue(row, null)))); lines.AddRange(valueLines); await Task.Run(() => File.WriteAllLines(path, lines.ToArray())); } + /// + /// Inizializzazione con periodo e arrotondamento + /// + /// + /// + public static DateTime InitDatetime(DateTime dtRif, int minRound) + { + TimeSpan DayElapsed = dtRif.Subtract(dtRif.Date); + int minDay = (int)Math.Ceiling((double)(DayElapsed.TotalMinutes / minRound)) * minRound; + DateTime endRounded = DateTime.Today.AddMinutes(minDay); + return endRounded; + } + #endregion Public Methods } } \ No newline at end of file diff --git a/MP.SPEC/Components/Chart/Doughnut.razor b/MP.SPEC/Components/Chart/Doughnut.razor new file mode 100644 index 00000000..7eddca28 --- /dev/null +++ b/MP.SPEC/Components/Chart/Doughnut.razor @@ -0,0 +1 @@ + diff --git a/MP.SPEC/Components/Chart/Doughnut.razor.cs b/MP.SPEC/Components/Chart/Doughnut.razor.cs new file mode 100644 index 00000000..49abb269 --- /dev/null +++ b/MP.SPEC/Components/Chart/Doughnut.razor.cs @@ -0,0 +1,66 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.JSInterop; +using MP.Data; + +namespace MP.SPEC.Components.Chart +{ + public partial class Doughnut + { + [Inject] + private IJSRuntime JSRuntime { get; set; } = null!; + + + public enum ChartType + { + Pie, + Bar, + Doughnut + } + + [Parameter] + public string Id { get; set; } + + [Parameter] + public ChartType Type { get; set; } + + [Parameter] + public double[] Data { get; set; } + + [Parameter] + public string[] BackgroundColor { get; set; } + + [Parameter] + public string[] Labels { get; set; } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + var config = new + { + Type = Type.ToString().ToLower(), + Options = new + { + Responsive = true, + Scales = new + { + YAxes = new[] + { + new { Ticks = new { + BeginAtZero=true + } } + } + } + }, + Data = new + { + Datasets = new[] + { + new { Data = Data, BackgroundColor = BackgroundColor} + }, + Labels = Labels + } + }; + + await JSRuntime.InvokeVoidAsync("setup", Id, config); + } + } +} \ No newline at end of file diff --git a/MP.SPEC/Components/Chart/Line.razor b/MP.SPEC/Components/Chart/Line.razor new file mode 100644 index 00000000..7eddca28 --- /dev/null +++ b/MP.SPEC/Components/Chart/Line.razor @@ -0,0 +1 @@ + diff --git a/MP.SPEC/Components/Chart/Line.razor.cs b/MP.SPEC/Components/Chart/Line.razor.cs new file mode 100644 index 00000000..746912b9 --- /dev/null +++ b/MP.SPEC/Components/Chart/Line.razor.cs @@ -0,0 +1,162 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.JSInterop; +using MP.Data; + +namespace MP.SPEC.Components.Chart +{ + public partial class Line + { + #region Public Properties + + [Parameter] + public double AspRatio { get; set; } = 0; + + [Parameter] + public List backColor { get; set; } = new List(); + + [Parameter] + public string ChartId + { + get + { + return Id; + } + set + { + Id = value; + } + } + + [Parameter] + public List DataTS + { + get + { + return _DataTS; + } + + set + { + _DataTS = value; + //var pUpd = Task.Run(async () => await renderChart()); + //pUpd.Wait(); + } + } + + [Parameter] + public List Labels { get; set; } = new List(); + + [Parameter] + public List lineColor { get; set; } = new List(); + + [Parameter] + public int lTens { get; set; } = 0; + + [Parameter] + public string MaxValue { get; set; } = "0"; + + [Parameter] + public string MinValue { get; set; } = "0"; + + [Parameter] + public List pointColor { get; set; } = new List(); + + [Parameter] + public string Title { get; set; } = "Demo Line"; + + #endregion Public Properties + + #region Protected Properties + + protected string Id { get; set; } = "CurrId"; + + #endregion Protected Properties + + #region Protected Methods + + /// + /// Inizializzazione rendering componente + /// + /// partendo da qui: https://www.williamleme.com/posts/2020/003-chartjs-blazor/ + /// https://www.puresourcecode.com/dotnet/blazor/using-chart-js-with-blazor/ https://www.tutorialsteacher.com/csharp/csharp-anonymous-type + /// + /// + /// + protected override async Task OnAfterRenderAsync(bool firstRender) + { + await renderChart(); + } + + /// + /// Inizializzazione rendering componente + /// + /// partendo da qui: https://www.williamleme.com/posts/2020/003-chartjs-blazor/ + /// https://www.puresourcecode.com/dotnet/blazor/using-chart-js-with-blazor/ https://www.tutorialsteacher.com/csharp/csharp-anonymous-type + /// + /// + /// + protected async Task renderChart() + { + // creazione di un oggetto anonymous type con tutte le opzioni da passare a chart.js + var config = new + { + type = "line", + options = new + { + responsive = true, + scales = new + { + yAxes = new + { + display = true, + position = "right", + ticks = new + { + maxTicksLimit = 10 + }, + suggestedMin = MinValue != MaxValue ? MinValue : "auto", + suggestedMax = MinValue != MaxValue ? MaxValue : "auto" + }, + xAxes = new + { + type = "time", + distribution = "linear", + } + }, + plugins = new + { + legend = new + { + display = false + }, + }, + Animation = false, + AspectRatio = AspRatio == 0 ? "auto" : $"{AspRatio}" + }, + data = new + { + labels = Labels, + datasets = new[]{new + { + data = DataTS, pointBorderColor = backColor, borderColor = lineColor, backgroundColor = backColor, fill = true, PointRadius = 2, BorderWidth = 1, lineTension = lTens, stepped = false, label = Title + } + } + } + } + + ; + await JSRuntime.InvokeVoidAsync("setup", Id, config); + } + + #endregion Protected Methods + + #region Private Properties + + private List _DataTS { get; set; } = null!; + + [Inject] + private IJSRuntime JSRuntime { get; set; } = null!; + + #endregion Private Properties + } +} \ No newline at end of file diff --git a/MP.SPEC/Components/DossiersFilter.razor b/MP.SPEC/Components/DossiersFilter.razor index a57ccf3f..9d549e93 100644 --- a/MP.SPEC/Components/DossiersFilter.razor +++ b/MP.SPEC/Components/DossiersFilter.razor @@ -1,36 +1,54 @@ -
-
- -
- -
-
-
-
- - - - - + + @if (ListArticoli != null) + { + foreach (var item in ListArticoli) { - foreach (var item in ListMacchine) - { - - } + } - - - -
+ } + +
+
+ + +
+
+ + +
+
+ +
diff --git a/MP.SPEC/Components/DossiersFilter.razor.cs b/MP.SPEC/Components/DossiersFilter.razor.cs index 2ae4b100..94174e12 100644 --- a/MP.SPEC/Components/DossiersFilter.razor.cs +++ b/MP.SPEC/Components/DossiersFilter.razor.cs @@ -14,6 +14,8 @@ namespace MP.SPEC.Components [Parameter] public SelectDossierParams SelFilterDossier { get; set; } = null!; + protected bool showParam { get; set; } = false; + #endregion Public Properties #region Protected Properties @@ -58,6 +60,25 @@ namespace MP.SPEC.Components } } + protected string selArticolo + { + get + { + return SelFilterDossier.CodArticolo; + } + set + { + if (!SelFilterDossier.CodArticolo.Equals(value)) + { + SelFilterDossier.CurrPage = 1; + SelFilterDossier.CodArticolo = value; + StateHasChanged(); + Task.Delay(1); + reportChange(); + } + } + } + protected int selMaxRecord { get @@ -83,7 +104,10 @@ namespace MP.SPEC.Components { SelFilterDossier = new SelectDossierParams(); ListMacchine = await MDService.MacchineWithFlux(); - ListDossier = await MDService.DossiersGetLastFilt(selMacchina, selDtRef, selMaxRecord); + ListArticoli = await MDService.ArticleWithDossier(); +#if false + ListDossier = await MDService.DossiersGetLastFilt(selMacchina, selArticolo, selDtRef, selMaxRecord); +#endif await FilterChanged.InvokeAsync(SelFilterDossier); } @@ -96,9 +120,13 @@ namespace MP.SPEC.Components #region Private Fields - private List? ListDossier = null; +#if false + private List? ListDossier = null; +#endif private List? ListMacchine = null; + private List? ListArticoli = null; + #endregion Private Fields #region Private Properties @@ -114,6 +142,11 @@ namespace MP.SPEC.Components FilterChanged.InvokeAsync(SelFilterDossier); } + private void toggleShowParams() + { + showParam = !showParam; + } + #endregion Private Methods } } \ No newline at end of file diff --git a/MP.SPEC/Components/ListDossiers.razor b/MP.SPEC/Components/ListDossiers.razor index 5737bf7d..3fe9e652 100644 --- a/MP.SPEC/Components/ListDossiers.razor +++ b/MP.SPEC/Components/ListDossiers.razor @@ -37,12 +37,14 @@ else @record.OdlNav.CodArticolo +
@record.OdlNav.ArticoloNav.DescArticolo
@tradFase(record.OdlNav.KeyRichiesta) @record.IdxMacchina +
@record.MachineNav.Descrizione
@record.DtRif diff --git a/MP.SPEC/Components/ListDossiers.razor.cs b/MP.SPEC/Components/ListDossiers.razor.cs index 546a164c..fdb5c4e4 100644 --- a/MP.SPEC/Components/ListDossiers.razor.cs +++ b/MP.SPEC/Components/ListDossiers.razor.cs @@ -37,20 +37,6 @@ namespace MP.SPEC.Components return answ; } - public async Task reloadData(bool setChanged) - { - isLoading = true; - SearchRecords = await MDService.DossiersGetLastFilt(SelMacchina, SelDtRef, MaxRecord); - totalCount = SearchRecords.Count; - ListRecords = SearchRecords.Skip(numRecord * (currPage - 1)).Take(numRecord).ToList(); - await Task.Delay(1); - if (setChanged) - { - await InvokeAsync(() => StateHasChanged()); - } - isLoading = false; - } - #endregion Public Methods #region Protected Properties @@ -80,7 +66,7 @@ namespace MP.SPEC.Components await Task.Delay(1); var done = await MDService.DossiersDeleteRecord(selRec); currRecord = null; - await reloadData(); + await reloadData(true); await Task.Delay(1); } @@ -91,9 +77,11 @@ namespace MP.SPEC.Components ListStati = await MDService.AnagStatiComm(); - ListRecords = await MDService.DossiersGetLastFilt(SelMacchina, SelDtRef, MaxRecord); +#if false + ListRecords = await MDService.DossiersGetLastFilt(SelMacchina, SelArticolo, SelDtRef, MaxRecord); +#endif - await reloadData(); + await reloadData(true); } protected async void OnSeachUpdated() @@ -124,9 +112,13 @@ namespace MP.SPEC.Components #region Private Fields private int _totalCount = 0; + private Dossiers? currRecord = null; + private List? ListRecords; + private List? ListStati; + private List? SearchRecords; #endregion Private Fields @@ -154,6 +146,11 @@ namespace MP.SPEC.Components set => MessageService.numRecord = value; } + private string SelArticolo + { + get => SelFilter.CodArticolo; + } + private DateTime SelDtRef { get => SelFilter.DtRef; @@ -185,19 +182,35 @@ namespace MP.SPEC.Components private async void MessageService_EA_PageUpdated() { - await reloadData(); + await reloadData(true); } + private async Task reloadData(bool setChanged) + { + isLoading = true; + SearchRecords = await MDService.DossiersGetLastFilt(SelMacchina, SelArticolo, SelDtRef, MaxRecord); + totalCount = SearchRecords.Count; + ListRecords = SearchRecords.Skip(numRecord * (currPage - 1)).Take(numRecord).ToList(); + await Task.Delay(1); + if (setChanged) + { + await InvokeAsync(() => StateHasChanged()); + } + isLoading = false; + } + +#if false private async Task reloadData() { isLoading = true; - SearchRecords = await MDService.DossiersGetLastFilt(SelMacchina, SelDtRef, MaxRecord); + SearchRecords = await MDService.DossiersGetLastFilt(SelMacchina, SelArticolo, SelDtRef, MaxRecord); totalCount = SearchRecords.Count; ListRecords = SearchRecords.Skip(numRecord * (currPage - 1)).Take(numRecord).ToList(); await Task.Delay(1); await InvokeAsync(() => StateHasChanged()); isLoading = false; } +#endif private async Task toggleTableFlux() { diff --git a/MP.SPEC/Components/ListODL.razor b/MP.SPEC/Components/ListODL.razor index 526a1cd9..08e70a35 100644 --- a/MP.SPEC/Components/ListODL.razor +++ b/MP.SPEC/Components/ListODL.razor @@ -16,52 +16,166 @@ else - - - - - - @**@ - + + + @foreach (var record in ListRecords) { - - + - + + - - @**@ - } diff --git a/MP.SPEC/Components/ListODL.razor.cs b/MP.SPEC/Components/ListODL.razor.cs index 7ccfd34b..f1c3a419 100644 --- a/MP.SPEC/Components/ListODL.razor.cs +++ b/MP.SPEC/Components/ListODL.razor.cs @@ -1,7 +1,10 @@ using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; +using MP.Data; using MP.Data.DatabaseModels; using MP.SPEC.Data; +using Newtonsoft.Json.Linq; +using System.Text; namespace MP.SPEC.Components { @@ -10,20 +13,10 @@ namespace MP.SPEC.Components #region Public Properties [Parameter] - public EventCallback PagerResetReq { get; set; } + public SelectOdlParams currFilter { get; set; } = null!; [Parameter] - public string StatoSel - { - get => _statoSel; - set - { - if (_statoSel != value) - { - _statoSel = value; - } - } - } + public EventCallback PagerResetReq { get; set; } [Parameter] public EventCallback updateRecordCount { get; set; } @@ -32,14 +25,14 @@ namespace MP.SPEC.Components #region Public Methods - public string checkSelect(string CodArticolo) + public string checkSelect(int IdxOdl) { string answ = ""; if (currRecord != null) { try { - answ = (currRecord.CodArticolo == CodArticolo) ? "table-info" : ""; + answ = (currRecord.IdxOdl == IdxOdl) ? "table-info" : ""; } catch { } @@ -49,12 +42,15 @@ namespace MP.SPEC.Components public void Dispose() { +#if false MessageService.EA_PageUpdated -= MessageService_EA_PageUpdated; - MessageService.EA_SearchUpdated -= OnSeachUpdated; + MessageService.EA_SearchUpdated -= OnSeachUpdated; +#endif } #endregion Public Methods + #region Protected Properties [Inject] @@ -73,8 +69,10 @@ namespace MP.SPEC.Components protected override async Task OnInitializedAsync() { ListStati = await MDService.AnagStatiComm(); +#if false MessageService.EA_PageUpdated += MessageService_EA_PageUpdated; - MessageService.EA_SearchUpdated += OnSeachUpdated; + MessageService.EA_SearchUpdated += OnSeachUpdated; +#endif } protected override async Task OnParametersSetAsync() @@ -95,7 +93,10 @@ namespace MP.SPEC.Components protected async Task UpdateData() { - currRecord = null; +#if false + currRecord = null; +#endif + await selectRecord(null); await reloadData(); } @@ -103,11 +104,28 @@ namespace MP.SPEC.Components #region Private Fields - private string _statoSel = "*"; private ODLModel? currRecord = null; + + protected async Task selectRecord(ODLModel? currRec) + { + await Task.Delay(1); + currRecord = currRec; + if (currRec != null) + { + ListOdlStats = await MDService.StatOdl(currRec.IdxOdl); + } + else + { + ListOdlStats = null; + } + } + private List? ListRecords; private List? ListStati; private List? SearchRecords; + private List? ListOdlStats; + private List? ListOdlStatsData = new List(); + private List? ListOdlStatsLabels = new List(); #endregion Private Fields @@ -127,11 +145,6 @@ namespace MP.SPEC.Components set => MessageService.numRecord = value; } - private string SearchVal - { - get => string.IsNullOrEmpty(MessageService.SearchVal) ? "*" : MessageService.SearchVal; - } - private int totalCount { get; set; } = 0; #endregion Private Properties @@ -146,7 +159,7 @@ namespace MP.SPEC.Components private async Task reloadData() { isLoading = true; - SearchRecords = await MDService.ListODLFilt(true, SearchVal, StatoSel); + SearchRecords = await MDService.ListODLFilt(currFilter.IsActive, currFilter.SearchVal, currFilter.CodStato, currFilter.DtStart, currFilter.DtEnd); totalCount = SearchRecords.Count; ListRecords = SearchRecords.Skip(numRecord * (currPage - 1)).Take(numRecord).ToList(); await Task.Delay(1); @@ -168,7 +181,26 @@ namespace MP.SPEC.Components } return answ; } + private double[] addODLData() + { + var eleStats = ListOdlStats.Where(x => x.IdxStato == currRecord.IdxOdl).ToList(); + foreach (var item in eleStats) + { + + ListOdlStatsData.Add(item.TotDurata); + } + return ListOdlStatsData.ToArray(); + } + private string[] addODLLabels() + { + var eleStats = ListOdlStats.Where(x => x.IdxStato == currRecord.IdxOdl).ToList(); + foreach (var item in eleStats) + { + ListOdlStatsLabels.Add(item.Descrizione); + } + return ListOdlStatsLabels.ToArray(); + } #endregion Private Methods } } \ No newline at end of file diff --git a/MP.SPEC/Components/ListPODL.razor b/MP.SPEC/Components/ListPODL.razor index b88df56a..8e79dcad 100644 --- a/MP.SPEC/Components/ListPODL.razor +++ b/MP.SPEC/Components/ListPODL.razor @@ -22,9 +22,8 @@ else - - - + + @**@ @@ -33,7 +32,7 @@ else @foreach (var record in ListRecords) { - @@ -41,18 +40,26 @@ else @record.CodArticolo
@record.ArticoloNav.DescArticolo
- + - - + @**@
- @**@ - Articolo Fase Macchina# pz T.Ciclo Inizio Note Richiesta Info ciclo Periodo Durata
- @**@ -
@record.CodArticolo
@record.ArticoloNav.DescArticolo
@tradFase(record.KeyRichiesta) +
+ @tradFase(record.KeyRichiesta) +
+ @if (record.Note != "") + { +
+ @record.Note +
+ } +
@record.IdxMacchina
@record.MachineNav.Descrizione
- @record.NumPezzi +
N° pezzi: @record.NumPezzi
+
T. Ciclo: @record.Tcassegnato.ToString("N3")
+
- @record.Tcassegnato.ToString("N3") +
+
+
@($"{@record.DataInizio:yyyy/MM/dd}")
+
@($"{@record.DataInizio:ddd HH:mm:ss}")
+
+
+ +
+
+ @if (@record.DataFine != null) + { +
@($"{@record.DataFine:yyyy/MM/dd}")
+
@($"{@record.DataFine:ddd HH:mm:ss}")
+ } + else + { +
+
@($"{DateTime.Now:yyyy/MM/dd}")
+
@($"{DateTime.Now:ddd HH:mm:ss}")
+
+ } +
+
-
@record.DataInizio
-
@record.Note@record.KeyRichiesta - @*@if (ArticoloDelEnabled(record.CodArticolo)) - { - - }*@ +
+ @record.DurataMinuti +
+
+ +
+ +
Articolo Fase Macchina# pz T.Ciclo Note Info ciclo Note Att
+ @tradFase(record.KeyRichiesta) +
+ @tradFase(record.KeyRichiesta) +
+ @if (record.Note != "") + { +
+ Note: @record.Note +
+ } +
@record.IdxMacchina
@record.MachineNav.Descrizione
- @record.NumPezzi +
N° pezzi: @record.NumPezzi
+
T. Ciclo: @record.Tcassegnato.ToString("N3")
- @record.Tcassegnato.ToString("N3") - @record.Note@record.Note @if (@record.Attivabile) { diff --git a/MP.SPEC/Components/ListPODL.razor.cs b/MP.SPEC/Components/ListPODL.razor.cs index 8b69b7a0..80e3371f 100644 --- a/MP.SPEC/Components/ListPODL.razor.cs +++ b/MP.SPEC/Components/ListPODL.razor.cs @@ -2,6 +2,7 @@ using Microsoft.JSInterop; using MP.Data.DatabaseModels; using MP.SPEC.Data; +using MP.SPEC.Services; namespace MP.SPEC.Components { @@ -47,6 +48,9 @@ namespace MP.SPEC.Components [Inject] protected MpDataService MDService { get; set; } = null!; + [Inject] + protected IOApiService MpIoApiCall { get; set; } + [Inject] protected MessageService MsgService { get; set; } = null!; @@ -91,6 +95,7 @@ namespace MP.SPEC.Components return; await Task.Delay(1); var done = await MDService.PODLDeleteRecord(selRec); + await callSyncDb(selRec); currRecord = null; await reloadData(); await Task.Delay(1); @@ -152,6 +157,7 @@ namespace MP.SPEC.Components private List? ListRecords; private List? ListStati; + private List? SearchRecords; #endregion Private Fields @@ -189,6 +195,19 @@ namespace MP.SPEC.Components #region Private Methods + /// + /// Chiama metodo x chiedere sync DB + /// + /// + /// + private async Task callSyncDb(PODLModel selRec) + { + // chiamo aggiunta task SyncDb... + string idxMacc = selRec.IdxMacchina; + string restUrl = $"IOB/addTask2Exe/{idxMacc}?taskName=syncDbData&taskVal="; + var response = await MpIoApiCall.callMpIoUrlGet(restUrl); + } + private async void MessageService_EA_PageUpdated() { await reloadData(); diff --git a/MP.SPEC/Components/ODLPlot.razor b/MP.SPEC/Components/ODLPlot.razor new file mode 100644 index 00000000..9a5234b8 --- /dev/null +++ b/MP.SPEC/Components/ODLPlot.razor @@ -0,0 +1,20 @@ +@if (@SelectedOdl != -1) +{ +
+
+ @SelectedOdl +
+
+
+
+ @if (isLoading) + { + + } + else + { + + } +
+
+} \ No newline at end of file diff --git a/MP.SPEC/Components/ODLPlot.razor.cs b/MP.SPEC/Components/ODLPlot.razor.cs new file mode 100644 index 00000000..e62b9dd2 --- /dev/null +++ b/MP.SPEC/Components/ODLPlot.razor.cs @@ -0,0 +1,72 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.JSInterop; +using MP.Data.DatabaseModels; +using MP.SPEC.Components; +using MP.SPEC.Data; + +namespace MP.SPEC.Components +{ + public partial class ODLPlot + { + #region Public Properties + + public int OdlId + { + get => _selParam; + } + + [Parameter] + public int SelectedOdl + { + get => _selParam; + set => _selParam = value; + } + + #endregion Public Properties + + #region Protected Properties + + //protected DataLogFilter _SelFilter { get; set; } = new DataLogFilter(); + protected int _selParam { get; set; } = -1; + + #endregion Protected Properties + + #region Protected Methods + + private bool isLoading { get; set; } = false; + + protected override async Task OnInitializedAsync() + { + isLoading = true; + await Task.Delay(1); + } + private List? ListRecords = null; + protected override async Task OnParametersSetAsync() + { + await ReloadData(); + await Task.Delay(1); + } + + public List Data = new List(); + public List Labels = new List(); + + protected async Task ReloadData() + { + ListRecords = await MDService.StatOdl(SelectedOdl); + foreach (var record in ListRecords) + { + Data.Add(record.TotDurata); + Labels.Add($"{record.Descrizione} - {record.TotDurata:N1}min"); + } + await Task.Delay(1); + isLoading = false; + } + + + + [Inject] + protected MpDataService MDService { get; set; } = null!; + + #endregion Protected Methods + } +} \ No newline at end of file diff --git a/MP.SPEC/Components/ParamsFilter.razor b/MP.SPEC/Components/ParamsFilter.razor index 1be0c622..06434455 100644 --- a/MP.SPEC/Components/ParamsFilter.razor +++ b/MP.SPEC/Components/ParamsFilter.razor @@ -1,115 +1,126 @@ -
-
-
-
+
+
- @if (!liveUpdate) - { - - } - else - { - - } -
-
- @if (selMacchina != "*") - { - - } -
-
- @if (snapshotDone) - { - - } -
-
-
- -
-
- @if (showEditPar) + @if (!liveUpdate) { -
- - - - - -
+ } else { -
- -
+ } -
-
- - - - - @if (selDtMax == null) +
+
+ @if (selMacchina != "*") + { + + } +
+
+ @if (snapshotDone) + { + + } +
+ +
+ + + +
+
+

FILTRI

+ +
+
+
+ +
+
+ + +
+
+ +
+
+ + - - + foreach (var item in ListFlux) + { + + } } -
+ +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ +
diff --git a/MP.SPEC/Components/ParamsFilter.razor.cs b/MP.SPEC/Components/ParamsFilter.razor.cs index 9930b251..25f497c7 100644 --- a/MP.SPEC/Components/ParamsFilter.razor.cs +++ b/MP.SPEC/Components/ParamsFilter.razor.cs @@ -99,7 +99,8 @@ namespace MP.SPEC.Components } } } - + protected bool showParam { get; set; } = false; + protected bool selDt { get; set; } = false; protected string selMacchina { get => SelFilter.IdxMacchina; @@ -185,7 +186,7 @@ namespace MP.SPEC.Components // copio il filtro var currFilt = SelFilter; // fermo update - currFilt.LiveUpdate = false; + //currFilt.LiveUpdate = false; currFilt.CurrPage = 0; currFilt.lastUpdate = $"{DateTime.Now:yyyy/MM/dd HH:mm:ss}"; currFilt.dtMax = RoundDatetime(5); @@ -193,6 +194,7 @@ namespace MP.SPEC.Components SelFilter = currFilt; } + protected void startTimer() { aTimer = new System.Timers.Timer(5000); diff --git a/MP.SPEC/Components/ToggleMode.razor b/MP.SPEC/Components/ToggleMode.razor new file mode 100644 index 00000000..259a0d14 --- /dev/null +++ b/MP.SPEC/Components/ToggleMode.razor @@ -0,0 +1,12 @@ +
+
+ @leftString +
+ +
+ @rightString +
+
+ + + diff --git a/MP.SPEC/Components/ToggleMode.razor.cs b/MP.SPEC/Components/ToggleMode.razor.cs new file mode 100644 index 00000000..c76ef352 --- /dev/null +++ b/MP.SPEC/Components/ToggleMode.razor.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Components; +using System.Net.Http; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Components.Forms; +using Microsoft.AspNetCore.Components.Routing; +using Microsoft.AspNetCore.Components.Web; +using Microsoft.AspNetCore.Components.Web.Virtualization; +using Microsoft.JSInterop; +using MP.SPEC; +using MP.SPEC.Shared; +using MP.SPEC.Components; +using MP.SPEC.Data; + +namespace MP.SPEC.Components +{ + public partial class ToggleMode + { + [Parameter] + public EventCallback FilterChanged { get; set; } + + [Parameter] + public SelectGlobalToggle SelFilter { get; set; } = new SelectGlobalToggle(); + + protected bool isActive + { + get => SelFilter.isActive; + set + { + if (SelFilter.isActive != value) + { + SelFilter.isActive = value; + reportChange(); + } + } + } + + protected string leftString + { + get => SelFilter.leftString; + set + { + if (SelFilter.leftString != value) + { + SelFilter.leftString = value; + reportChange(); + } + } + } + protected string leftStringCSS + { + get => SelFilter.leftStringCSS; + set + { + if (SelFilter.leftStringCSS != value) + { + SelFilter.leftStringCSS = value; + reportChange(); + } + } + } + protected string rightString + { + get => SelFilter.rightString; + set + { + if (SelFilter.rightString != value) + { + SelFilter.rightString = value; + reportChange(); + } + } + } + protected string rightStringCSS + { + get => SelFilter.rightStringCSS; + set + { + if (SelFilter.rightStringCSS != value) + { + SelFilter.rightStringCSS = value; + reportChange(); + } + } + } + + protected void toggle() + { + var currFilt = SelFilter; + currFilt.isActive = !currFilt.isActive; + SelFilter = currFilt; + if (isActive) + { + rightStringCSS = "fw-bold"; + leftStringCSS = "text-secondary"; + } + else + { + leftStringCSS = "fw-bold"; + rightStringCSS = "text-secondary"; + } + } + protected override async Task OnInitializedAsync() + { + if (isActive) + { + rightStringCSS = "fw-bold"; + leftStringCSS = "text-secondary"; + } + else + { + leftStringCSS = "fw-bold"; + rightStringCSS = "text-secondary"; + } + await FilterChanged.InvokeAsync(SelFilter); + } + + private void reportChange() + { + FilterChanged.InvokeAsync(SelFilter); + } + } +} \ No newline at end of file diff --git a/MP.SPEC/Data/MpDataService.cs b/MP.SPEC/Data/MpDataService.cs index 4f996326..76c82b81 100644 --- a/MP.SPEC/Data/MpDataService.cs +++ b/MP.SPEC/Data/MpDataService.cs @@ -276,10 +276,19 @@ namespace MP.SPEC.Data public List convertToFluxLog(string Valore) { - //string valStriped = Valore.Substring(7, Valore.Length - 8); - //var result = JsonConvert.DeserializeObject>(valStriped); - var result = JsonConvert.DeserializeObject(Valore); - return result.ODL; + List answ = new List(); + DossierFluxLogDTO? result = JsonConvert.DeserializeObject(Valore); + if (result != null) + { + if (result.ODL != null) + { + answ = result + .ODL + .OrderBy(x => x.CodFlux) + .ToList(); + } + } + return answ; } public void Dispose() @@ -317,13 +326,13 @@ namespace MP.SPEC.Data /// Data di riferimento (Massima) per estrazione records /// numero massimo record da restituire /// - public async Task> DossiersGetLastFilt(string IdxMacchina, DateTime DtRef, int MaxRec) + public async Task> DossiersGetLastFilt(string IdxMacchina, string CodArticolo, DateTime DtRef, int MaxRec) { List? result = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); string readType = "DB"; - string currKey = $"{redisDossByMac}:{IdxMacchina}:{DtRef:yyyy/MM/dd HH:mm:ss}:{MaxRec}"; + string currKey = $"{redisDossByMac}:{IdxMacchina}:{CodArticolo}:{DtRef}:{MaxRec}"; // cerco in redis dato valore sel macchina... RedisValue rawData = redisDb.StringGet(currKey); if (rawData.HasValue) @@ -333,7 +342,7 @@ namespace MP.SPEC.Data } else { - result = await Task.FromResult(dbController.DossiersGetLastFilt(IdxMacchina, DtRef, MaxRec)); + result = await Task.FromResult(dbController.DossiersGetLastFilt(IdxMacchina, CodArticolo, DtRef, MaxRec)); // serializzp e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache / 5)); @@ -399,6 +408,16 @@ namespace MP.SPEC.Data return Task.FromResult(dbController.AnagGruppiAziende()); } + + /// + /// Statistiche ODL calcolate (da stored stp_STAT_ODL) + /// + /// + public Task> StatOdl(int IdxOdl) + { + return dbController.StatOdl(IdxOdl); + } + /// /// Restitusice elenco fasi /// @@ -473,9 +492,9 @@ namespace MP.SPEC.Data /// Cod articolo /// KeyRich (parziale) da cercare (es cod stato x yacht) /// - public async Task> ListODLFilt(bool inCorso, string codArt, string keyRichPart) + public async Task> ListODLFilt(bool inCorso, string codArt, string keyRichPart, DateTime startDate, DateTime endDate) { - return await Task.FromResult(dbController.ListODLFilt(inCorso, codArt, keyRichPart)); + return await Task.FromResult(dbController.ListODLFilt(inCorso, codArt, keyRichPart, startDate, endDate)); } /// @@ -544,7 +563,7 @@ namespace MP.SPEC.Data } else { - result = await Task.FromResult(dbController.MacchineWithFlux()); + result = await dbController.MacchineWithFlux(); // serializzp e salvo... rawData = JsonConvert.SerializeObject(result); redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache)); @@ -559,6 +578,42 @@ namespace MP.SPEC.Data return result; } + + /// + /// Elenco Codice articolo con dati dossier gestiti + /// + /// + public async Task> ArticleWithDossier() + { + List? result = new List(); + Stopwatch stopWatch = new Stopwatch(); + stopWatch.Start(); + string readType = "DB"; + string currKey = redisArtByDossier; + // cerco in redis dato valore sel macchina... + RedisValue rawData = redisDb.StringGet(currKey); + if (rawData.HasValue) + { + result = JsonConvert.DeserializeObject>($"{rawData}"); + readType = "REDIS"; + } + else + { + result = await Task.FromResult(dbController.ArticleWithDossier()); + // serializzp e salvo... + rawData = JsonConvert.SerializeObject(result); + redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache)); + } + if (result == null) + { + result = new List(); + } + stopWatch.Stop(); + TimeSpan ts = stopWatch.Elapsed; + Log.Debug($"ArticleWithDossier | Read from {readType}: {ts.TotalMilliseconds}ms"); + return result; + } + /// /// Elenco di tutti i parametri filtrati x macchina /// @@ -650,6 +705,8 @@ namespace MP.SPEC.Data private const string redisMacByFlux = redisBaseAddr + "SPEC:Cache:MacByFlux"; + private const string redisArtByDossier = redisBaseAddr + "SPEC:Cache:ArtByDossier"; + private const string redisMacList = redisBaseAddr + "SPEC:Cache:MacList"; private const string redisStatoCom = redisBaseAddr + "SPEC:Cache:StatoCom"; diff --git a/MP.SPEC/Data/SelectDossierParams.cs b/MP.SPEC/Data/SelectDossierParams.cs index 02cc5991..06489118 100644 --- a/MP.SPEC/Data/SelectDossierParams.cs +++ b/MP.SPEC/Data/SelectDossierParams.cs @@ -17,7 +17,9 @@ public string IdxMacchina { get; set; } = "*"; - public int MaxRecord { get; set; } = 10; + public string CodArticolo { get; set; } = "*"; + + public int MaxRecord { get; set; } = 100; #endregion Public Properties @@ -44,6 +46,9 @@ if (IdxMacchina != item.IdxMacchina) return false; + if (CodArticolo != item.CodArticolo) + return false; + if (MaxRecord != item.MaxRecord) return false; diff --git a/MP.SPEC/Data/SelectGlobalToggle.cs b/MP.SPEC/Data/SelectGlobalToggle.cs new file mode 100644 index 00000000..527bc25a --- /dev/null +++ b/MP.SPEC/Data/SelectGlobalToggle.cs @@ -0,0 +1,74 @@ +namespace MP.SPEC.Data +{ + public class SelectGlobalToggle + { + #region Public Constructors + + public SelectGlobalToggle() + { } + + #endregion Public Constructors + + #region Public Properties + + /// + /// Bool: indica se il toggle è attivo + /// + public bool isActive { get; set; } = true; + + /// + /// string: stringa da mostrare a sinistra (disattivo onInitialize) + /// + public string leftString { get; set; } = ""; + + /// + /// string: stringa da mostrare a destra (attivo onInitialize) + /// + public string rightString { get; set; } = ""; + + /// + /// string: stile stringa da mostrare a sinistra (disattivo onInitialize) + /// + public string leftStringCSS { get; set; } = ""; + + /// + /// string: stile stringa da mostrare a destra (attivo onInitialize) + /// + public string rightStringCSS { get; set; } = ""; + + + #endregion Public Properties + + #region Public Methods + + public override bool Equals(object obj) + { + if (!(obj is SelectGlobalToggle item)) + return false; + + if (isActive != item.isActive) + return false; + + if (leftString != item.leftString) + return false; + + if (rightString != item.rightString) + return false; + + if (leftStringCSS != item.leftStringCSS) + return false; + + if (rightStringCSS != item.rightStringCSS) + return false; + + return true; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + #endregion Public Methods + } +} diff --git a/MP.SPEC/Data/SelectOdlParams.cs b/MP.SPEC/Data/SelectOdlParams.cs new file mode 100644 index 00000000..c2e9de0f --- /dev/null +++ b/MP.SPEC/Data/SelectOdlParams.cs @@ -0,0 +1,68 @@ +using MP.Data; + +namespace MP.SPEC.Data +{ + public class SelectOdlParams + { + #region Public Constructors + + public SelectOdlParams() + { } + + #endregion Public Constructors + + #region Public Properties + + public string CodStato { get; set; } = "*"; + public int CurrPage { get; set; } = 1; + public int NumRec { get; set; } = 10; + public DateTime DtEnd { get; set; } = Utils.InitDatetime(DateTime.Now, 5); + public DateTime DtStart { get; set; } = Utils.InitDatetime(DateTime.Now, 5).AddDays(-7); + public int MaxRecord { get; set; } = 100; + public bool IsActive { get; set; } = true; + public string SearchVal { get; set; } = "*"; + + #endregion Public Properties + + #region Public Methods + + public override bool Equals(object obj) + { + if (!(obj is SelectOdlParams item)) + return false; + + if (IsActive != item.IsActive) + return false; + + if (CodStato != item.CodStato) + return false; + + if (MaxRecord != item.MaxRecord) + return false; + + if (NumRec != item.NumRec) + return false; + + if (DtStart != item.DtStart) + return false; + + if (DtEnd != item.DtEnd) + return false; + + if (CurrPage != item.CurrPage) + return false; + + if (SearchVal != item.SearchVal) + return false; + + return true; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + #endregion Public Methods + } +} \ No newline at end of file diff --git a/MP.SPEC/MP.SPEC.csproj b/MP.SPEC/MP.SPEC.csproj index 539ad3ac..56f475a3 100644 --- a/MP.SPEC/MP.SPEC.csproj +++ b/MP.SPEC/MP.SPEC.csproj @@ -5,7 +5,7 @@ enable enable MP.SPEC - 6.16.2210.511 + 6.16.2210.1016 @@ -14,6 +14,16 @@ + + + + + + + + + + @@ -21,10 +31,6 @@ - - - - diff --git a/MP.SPEC/Pages/DOSS.razor b/MP.SPEC/Pages/DOSS.razor index 94278ea8..9c17c17b 100644 --- a/MP.SPEC/Pages/DOSS.razor +++ b/MP.SPEC/Pages/DOSS.razor @@ -14,6 +14,7 @@ } else { + }
diff --git a/MP.SPEC/Pages/Index.razor b/MP.SPEC/Pages/Index.razor index 2fefa35f..60e3e1f8 100644 --- a/MP.SPEC/Pages/Index.razor +++ b/MP.SPEC/Pages/Index.razor @@ -18,7 +18,7 @@
-
+
@if (ElencoLink == null) @@ -33,6 +33,7 @@ } else { + foreach (var item in ElencoLink) { diff --git a/MP.SPEC/Pages/ODL.razor b/MP.SPEC/Pages/ODL.razor index b66e1d20..0e9fba25 100644 --- a/MP.SPEC/Pages/ODL.razor +++ b/MP.SPEC/Pages/ODL.razor @@ -1,30 +1,82 @@ @page "/ODL"
-
-
-
-

ODL

+
+
+
+
+

ODL

+
-
-
- - + + In Corso +
+
+
+
+ + + +
+
+

FILTRI

+ +
+
+
+
+ Seleziona i filtri per: +
+
+
+ +
+ +
+
+ + +
+
+ @if (!isActive) { - foreach (var item in ListStati) - { - - } +
+ +
+
+ + +
+
+ +
+
+ + +
} - +
- +