Browse Source

Moved DynamicItemsListGrid to InABox.DynamicGrid. Added totals and job status filter to JobSummaryDashboard

Kenric Nugteren 2 năm trước cách đây
mục cha
commit
63addbefbc

+ 24 - 8
prs.desktop/Dashboards/Manufacturing/JobManufacturingSummary.xaml

@@ -1,4 +1,5 @@
-<UserControl x:Class="PRSDesktop.Dashboards.Manufacturing.JobManufacturingSummary"
+<UserControl x:Name="UserControl"
+             x:Class="PRSDesktop.Dashboards.Manufacturing.JobManufacturingSummary"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
@@ -94,12 +95,27 @@
     </UserControl.Resources>
     
     <Border BorderThickness="1" BorderBrush="Black" Background="White">
-        <sf:SfDataGrid x:Name="DataGrid"
-                       AutoGenerateColumns="True" AutoGeneratingColumn="DataGrid_AutoGeneratingColumn"
-                       QueryRowHeight="DataGrid_QueryRowHeight"
-                       FrozenColumnCount="1"
-                       AllowSorting="False">
-            
-        </sf:SfDataGrid>
+        <Grid>
+            <Grid.RowDefinitions>
+                <RowDefinition Height="*"/>
+                <RowDefinition Height="Auto"/>
+            </Grid.RowDefinitions>
+            <sf:SfDataGrid x:Name="DataGrid"
+                           Grid.Row="0"
+                           AutoGenerateColumns="True" AutoGeneratingColumn="DataGrid_AutoGeneratingColumn"
+                           QueryRowHeight="DataGrid_QueryRowHeight"
+                           FrozenColumnCount="1"
+                           AllowSorting="False">
+
+            </sf:SfDataGrid>
+            <Label x:Name="Records"
+                   Content="{Binding ElementName=UserControl,Path=NumberOfJobs}"
+                   ContentStringFormat="Showing {0} Jobs"
+                   HorizontalAlignment="Stretch"
+                   HorizontalContentAlignment="Center"
+                   VerticalAlignment="Center"
+                   Padding="5"
+                   Grid.Row="1"/>
+        </Grid>
     </Border>
 </UserControl>

+ 228 - 51
prs.desktop/Dashboards/Manufacturing/JobManufacturingSummary.xaml.cs

@@ -1,11 +1,14 @@
 using Comal.Classes;
 using InABox.Clients;
 using InABox.Core;
+using InABox.WPF;
 using javax.smartcardio;
+using NPOI.SS.Formula.Functions;
 using PRSDesktop.WidgetGroups;
 using Syncfusion.UI.Xaml.Grid;
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.Data;
 using System.Linq;
 using System.Runtime.CompilerServices;
@@ -20,6 +23,7 @@ using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using System.Windows.Navigation;
 using System.Windows.Shapes;
+using DataColumn = System.Data.DataColumn;
 using DataRow = System.Data.DataRow;
 
 namespace PRSDesktop.Dashboards.Manufacturing
@@ -62,6 +66,7 @@ namespace PRSDesktop.Dashboards.Manufacturing
 
     public class JobManufacturingSummaryProperties : IDashboardProperties
     {
+        public Guid JobStatus;
     }
 
     public class JobManufacturingSummaryElement : DashboardElement<JobManufacturingSummary, WidgetGroups.Manufacturing, JobManufacturingSummaryProperties>
@@ -71,7 +76,9 @@ namespace PRSDesktop.Dashboards.Manufacturing
     /// <summary>
     /// Interaction logic for JobManufacturingSummary.xaml
     /// </summary>
-    public partial class JobManufacturingSummary : UserControl, IDashboardWidget<WidgetGroups.Manufacturing, JobManufacturingSummaryProperties>
+    public partial class JobManufacturingSummary : UserControl, IDashboardWidget<WidgetGroups.Manufacturing, JobManufacturingSummaryProperties>,
+        IHeaderDashboard,
+        INotifyPropertyChanged
     {
         private class SectionColumn
         {
@@ -80,15 +87,39 @@ namespace PRSDesktop.Dashboards.Manufacturing
 
             public SolidColorBrush Background { get; set; }
         }
+        private class ManufacturingTotal
+        {
+            public int NPackets { get; set; } = 0;
+
+            public double NHours { get; set; } = 0;
+        }
 
 
         private readonly Dictionary<Guid, SectionColumn> SectionColumns = new();
-        private List<Job> Jobs { get; set; } = new();
+        private readonly Dictionary<Guid, SectionColumn> FactoryColumns = new();
+
+        public event PropertyChangedEventHandler? PropertyChanged;
+
+        private List<Job> _jobs = new();
+        private List<Job> Jobs
+        {
+            get => _jobs;
+            set
+            {
+                _jobs = value;
+                OnPropertyChanged(nameof(NumberOfJobs));
+            }
+        }
+
         private List<ManufacturingFactory> Factories { get; set; } = new();
         private Dictionary<Guid, List<ManufacturingSection>> Sections { get; set; } = new();
         private List<FrameworkElement> Cards { get; set; } = new();
         private DataTable Data { get; set; }
 
+        private Dictionary<Guid, StackedColumn> FactoryColumnHeaders = new();
+
+        public int NumberOfJobs => Jobs.Count;
+
         public JobManufacturingSummary()
         {
             InitializeComponent();
@@ -96,11 +127,13 @@ namespace PRSDesktop.Dashboards.Manufacturing
 
         public JobManufacturingSummaryProperties Properties { get; set; }
 
+        public DashboardHeader Header { get; } = new();
+
         public void Setup()
         {
             Jobs = new Client<Job>()
                 .Query(
-                    LookupFactory.DefineFilter<Job>(),
+                    new Filter<Job>().None(),
                     new Columns<Job>(x => x.ID)
                         .Add(x => x.JobNumber)
                         .Add(x => x.Name)
@@ -121,9 +154,59 @@ namespace PRSDesktop.Dashboards.Manufacturing
                 .ToObjects<ManufacturingSection>()
                 .GroupBy(x => x.Factory.ID).ToDictionary(x => x.Key, x => x.ToList());
 
+            SetupHeader();
             SetupGrid();
+
+            RefreshJobs();
+        }
+
+        #region Header
+
+        private ComboBox JobStatusBox;
+
+        private JobStatus? SelectedStatus => JobStatusBox.SelectedValue as JobStatus;
+
+        private void SetupHeader()
+        {
+            JobStatusBox = new ComboBox
+            {
+                Padding = new Thickness(5)
+            };
+            var statuses = new Client<JobStatus>().Load();
+            JobStatusBox.ItemsSource = statuses.Select(x => new Tuple<string, JobStatus>($"{x.Code}: {x.Description}", x));
+            JobStatusBox.DisplayMemberPath = "Item1";
+            JobStatusBox.SelectedValuePath = "Item2";
+
+            JobStatusBox.SelectedValue = (Properties.JobStatus != Guid.Empty
+                ? statuses.FirstOrDefault(x => x.ID == Properties.JobStatus)
+                : null) ?? statuses.FirstOrDefault(x => x.Default) ?? statuses.FirstOrDefault();
+            JobStatusBox.SelectionChanged += StatusBox_SelectionChanged;
+
+            Header.Add(JobStatusBox);
+        }
+
+        private void RefreshJobs()
+        {
+            Jobs = new Client<Job>()
+                .Query(
+                    new Filter<Job>(x => x.JobStatus.ID).IsEqualTo(Properties.JobStatus),
+                    new Columns<Job>(x => x.ID)
+                        .Add(x => x.JobNumber)
+                        .Add(x => x.Name)
+                        .Add(x => x.Color))
+                .ToList<Job>();
+
+            Refresh();
+        }
+
+        private void StatusBox_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
+        {
+            Properties.JobStatus = SelectedStatus?.ID ?? Guid.Empty;
+            RefreshJobs();
         }
 
+        #endregion
+
         private void SetupGrid()
         {
             DataGrid.HeaderTemplate = Resources["HeaderTemplate"] as DataTemplate;
@@ -133,30 +216,55 @@ namespace PRSDesktop.Dashboards.Manufacturing
             Data.Columns.Add("Job", typeof(object));
 
             var stackedHeaderRow = new StackedHeaderRow();
+            var stackedDetailRow = new StackedHeaderRow();
             var factoryIdx = 0;
             foreach (var factory in Factories)
             {
-                if (!Sections.TryGetValue(factory.ID, out var sections)) continue;
+                var columns = new List<string>();
 
-                foreach (var section in sections)
+                var toBeIssued = factory.ID.ToString();
+                Data.Columns.Add(toBeIssued, typeof(object));
+                columns.Add(toBeIssued);
+                FactoryColumns.Add(factory.ID, new SectionColumn
                 {
-                    var columnName = section.ID.ToString();
-                    SectionColumns.Add(section.ID, new SectionColumn
+                    ColumnName = toBeIssued,
+                    DisplayName = "To Be Issued",
+                    Background = factoryIdx % 2 == 0 ? new SolidColorBrush(Colors.WhiteSmoke) : new SolidColorBrush(Colors.LightGray)
+                });
+
+                if (Sections.TryGetValue(factory.ID, out var sections))
+                {
+                    foreach (var section in sections)
                     {
-                        ColumnName = columnName,
-                        DisplayName = section.Name,
-                        Background = factoryIdx % 2 == 0 ? new SolidColorBrush(Colors.WhiteSmoke) : new SolidColorBrush(Colors.LightGray)
-                    });
-                    Data.Columns.Add(columnName, typeof(object));
+                        var columnName = section.ID.ToString();
+                        SectionColumns.Add(section.ID, new SectionColumn
+                        {
+                            ColumnName = columnName,
+                            DisplayName = section.Name,
+                            Background = factoryIdx % 2 == 0 ? new SolidColorBrush(Colors.WhiteSmoke) : new SolidColorBrush(Colors.LightGray)
+                        });
+                        Data.Columns.Add(columnName, typeof(object));
+                        columns.Add(columnName);
+                    }
                 }
+
+                var column = new StackedColumn
+                {
+                    ChildColumns = string.Join(',', columns),
+                    HeaderText = ""
+                };
+                FactoryColumnHeaders.Add(factory.ID, column);
+                stackedDetailRow.StackedColumns.Add(column);
+
                 stackedHeaderRow.StackedColumns.Add(new StackedColumn
                 {
-                    ChildColumns = string.Join(',', sections.Select(x => x.ID)),
+                    ChildColumns = string.Join(',', columns),
                     HeaderText = factory.Name
                 });
                 ++factoryIdx;
             }
             DataGrid.StackedHeaderRows.Add(stackedHeaderRow);
+            DataGrid.StackedHeaderRows.Add(stackedDetailRow);
         }
 
         private void SetPosition(FrameworkElement element, int row, int rowSpan, int column, int columnSpan)
@@ -167,21 +275,12 @@ namespace PRSDesktop.Dashboards.Manufacturing
             element.SetValue(Grid.ColumnSpanProperty, columnSpan);
         }
 
-        public void Refresh()
+        private void LoadPackets(
+            IEnumerable<IGrouping<Guid, ManufacturingPacket>> packets,
+            Dictionary<Guid, SectionColumn> columns,
+            Dictionary<Guid, DataRow> jobRows)
         {
-            Data.Rows.Clear();
-
-            var packets = new Client<ManufacturingPacket>()
-                .Query(
-                    new Filter<ManufacturingPacket>(x => x.Archived).IsEqualTo(DateTime.MinValue)
-                        .And(x => x.Completed).IsEqualTo(DateTime.MinValue)
-                        .And(x => x.SetoutLink.JobLink.ID).InList(Jobs.Select(x => x.ID).ToArray()),
-                    new Columns<ManufacturingPacket>(x => x.ID)
-                        .Add(x => x.SetoutLink.JobLink.ID)
-                        .Add(x => x.StageLink.SectionID)
-                        .Add(x => x.TimeRemaining))
-                .ToObjects<ManufacturingPacket>()
-                .GroupBy(x => x.StageLink.SectionID)
+            var groupPackets = packets
                 .ToDictionary(
                     x => x.Key,
                     x => x.GroupBy(x => x.SetoutLink.JobLink.ID)
@@ -191,7 +290,28 @@ namespace PRSDesktop.Dashboards.Manufacturing
                         ++c.NPackets;
                         return c;
                     })));
+            foreach (var (id, column) in columns)
+            {
+                var packetList = groupPackets.GetValueOrDefault(id);
+                foreach (var job in Jobs)
+                {
+                    if (!jobRows.TryGetValue(job.ID, out var row))
+                    {
+                        continue;
+                    }
+
+                    if (packetList is null || !packetList.TryGetValue(job.ID, out var model))
+                    {
+                        model = new CardModel();
+                    }
+                    row[column.ColumnName] = model;
+                }
+            }
+        }
 
+        public void Refresh()
+        {
+            Data.Rows.Clear();
             var rows = new Dictionary<Guid, DataRow>();
             foreach (var job in Jobs)
             {
@@ -201,23 +321,73 @@ namespace PRSDesktop.Dashboards.Manufacturing
                 rows[job.ID] = row;
             }
 
-            foreach (var (sectionID, column) in SectionColumns)
+            LoadPackets(
+                new Client<ManufacturingPacket>()
+                    .Query(
+                        new Filter<ManufacturingPacket>(x => x.Archived).IsEqualTo(DateTime.MinValue)
+                            .And(x => x.StageLink.SectionID).IsNotEqualTo(Guid.Empty)
+                            .And(x => x.Completed).IsEqualTo(DateTime.MinValue)
+                            .And(x => x.SetoutLink.JobLink.ID).InList(Jobs.Select(x => x.ID).ToArray()),
+                        new Columns<ManufacturingPacket>(x => x.ID)
+                            .Add(x => x.SetoutLink.JobLink.ID)
+                            .Add(x => x.StageLink.SectionID)
+                            .Add(x => x.TimeRemaining))
+                    .ToObjects<ManufacturingPacket>()
+                    .GroupBy(x => x.StageLink.SectionID),
+                SectionColumns,
+                rows);
+            LoadPackets(
+                new Client<ManufacturingPacket>()
+                    .Query(
+                        new Filter<ManufacturingPacket>(x => x.Archived).IsEqualTo(DateTime.MinValue)
+                            .And(x => x.StageLink.SectionID).IsEqualTo(Guid.Empty)
+                            .And(x => x.Completed).IsEqualTo(DateTime.MinValue)
+                            .And(x => x.SetoutLink.JobLink.ID).InList(Jobs.Select(x => x.ID).ToArray()),
+                        new Columns<ManufacturingPacket>(x => x.ID)
+                            .Add(x => x.SetoutLink.JobLink.ID)
+                            .Add(x => x.TimeRemaining)
+                            .Add(x => x.ManufacturingTemplateLink.Factory.ID))
+                    .ToObjects<ManufacturingPacket>()
+                    .GroupBy(x => x.ManufacturingTemplateLink.Factory.ID),
+                FactoryColumns,
+                rows);
+
+            var totals = new Dictionary<Guid, ManufacturingTotal>();
+            foreach(var column in Data.Columns)
             {
-                var sectionPackets = packets.GetValueOrDefault(sectionID);
-                foreach (var job in Jobs)
+                if (column is not DataColumn dataColumn) continue;
+
+                Guid factoryID;
+                var sectionColumn = SectionColumns.FirstOrDefault(x => x.Value.ColumnName == dataColumn.ColumnName);
+                if(sectionColumn.Key != Guid.Empty)
                 {
-                    if (!rows.TryGetValue(job.ID, out var row))
-                    {
-                        continue;
-                    }
+                    factoryID = Sections.FirstOrDefault(x => x.Value.Any(x => x.ID == sectionColumn.Key)).Key;
+                }
+                else
+                {
+                    var factoryColumn = FactoryColumns.FirstOrDefault(x => x.Value.ColumnName == dataColumn.ColumnName);
+                    factoryID = factoryColumn.Key;
+                }
+                if (factoryID == Guid.Empty) continue;
 
-                    if (sectionPackets is null || !sectionPackets.TryGetValue(job.ID, out var model))
-                    {
-                        model = new CardModel();
-                    }
-                    row[column.ColumnName] = model;
+                if(!totals.TryGetValue(factoryID, out var total))
+                {
+                    total = new();
+                    totals[factoryID] = total;
+                }
+                foreach(var row in rows.Values)
+                {
+                    if (row[dataColumn] is not CardModel model) continue;
+
+                    total.NHours += model.NHours;
+                    total.NPackets += model.NPackets;
                 }
             }
+            foreach(var (factoryID, total) in totals)
+            {
+                var header = FactoryColumnHeaders[factoryID];
+                header.HeaderText = $"Packets: {total.NPackets}, Hours: {total.NHours:f2}";
+            }
 
             DataGrid.ItemsSource = Data;
         }
@@ -230,19 +400,21 @@ namespace PRSDesktop.Dashboards.Manufacturing
         {
             if(Guid.TryParse(e.Column.MappingName, out var id))
             {
-                var column = SectionColumns[id];
-                var style = new Style();
-                style.Setters.Add(new Setter(GridCell.BackgroundProperty, column.Background));
-
-                e.Column = new GridTemplateColumn
+                if(SectionColumns.TryGetValue(id, out var column) || FactoryColumns.TryGetValue(id, out column))
                 {
-                    HeaderText = column.DisplayName,
-                    CellStyle = style,
-                    MappingName = e.Column.MappingName,
-                    CellTemplate = Resources["CardTemplate"] as DataTemplate,
-                    SetCellBoundValue = true,
-                    ColumnSizer = GridLengthUnitType.Auto
-                };
+                    var style = new Style();
+                    style.Setters.Add(new Setter(GridCell.BackgroundProperty, column.Background));
+
+                    e.Column = new GridTemplateColumn
+                    {
+                        HeaderText = column.DisplayName,
+                        CellStyle = style,
+                        MappingName = e.Column.MappingName,
+                        CellTemplate = Resources["CardTemplate"] as DataTemplate,
+                        SetCellBoundValue = true,
+                        ColumnSizer = GridLengthUnitType.Auto
+                    };
+                }
             }
             else
             {
@@ -265,5 +437,10 @@ namespace PRSDesktop.Dashboards.Manufacturing
                 e.Handled = true;
             }
         }
+
+        private void OnPropertyChanged([CallerMemberName] string name = "")
+        {
+            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
+        }
     }
 }

+ 6 - 46
prs.desktop/Dashboards/UtilityDashboard.xaml.cs

@@ -30,48 +30,6 @@ namespace PRSDesktop
         public string Layout { get; set; }
     }
 
-    public class DynamicItemsListGrid<T> : DynamicGrid<T>
-        where T : BaseObject, new()
-    {
-        public List<T> Items { get; set; }
-
-        public DynamicItemsListGrid() : this(new()) { }
-
-        public DynamicItemsListGrid(List<T> items) : base()
-        {
-            Items = items;
-        }
-
-        protected override void DeleteItems(params CoreRow[] rows)
-        {
-            foreach (var row in rows.OrderByDescending(x => x.Index))
-            {
-                Items.RemoveAt(row.Index);
-            }
-        }
-
-        protected override T LoadItem(CoreRow row)
-        {
-            return Items[row.Index];
-        }
-
-        protected override void Reload(Filters<T> criteria, Columns<T> columns, ref SortOrder<T>? sort, Action<CoreTable?, Exception?> action)
-        {
-            var result = new CoreTable();
-            result.LoadColumns(typeof(T));
-            result.LoadRows(Items);
-            action.Invoke(result, null);
-        }
-
-        public override void SaveItem(T item)
-        {
-            if (!Items.Contains(item))
-            {
-                Items.Add(item);
-            }
-        }
-    }
-
     public class GlobalUtilityDashboardSettings : GlobalConfigurationSettings
     {
         public List<DashboardFavourite> Favourites { get; set; }
@@ -213,7 +171,7 @@ namespace PRSDesktop
 
         #region Favourites
 
-        private IEnumerable<DashboardFavourite> GetFavourites()
+        private IEnumerable<DashboardFavourite> GetFavourites(bool includeGlobal)
         {
             foreach(var favourite in _settings.Favourites)
             {
@@ -228,7 +186,9 @@ namespace PRSDesktop
 
         private void ManageFavourites_Click()
         {
-            var favourites = GetFavourites().ToList();
+            var canSetGlobal = Security.IsAllowed<CanSetGlobalDashboardFavourites>();
+
+            var favourites = GetFavourites(canSetGlobal).ToList();
             var grid = new DynamicItemsListGrid<DashboardFavourite>(favourites);
             grid.Options.AddRange(DynamicGridOption.DeleteRows, DynamicGridOption.EditRows, DynamicGridOption.MultiSelect);
             grid.OnCustomiseEditor += FavouritesGrid_OnCustomiseEditor;
@@ -237,7 +197,7 @@ namespace PRSDesktop
             _settings.Favourites = favourites.Where(x => !x.IsGlobal).ToList();
             SaveSettings();
 
-            if (Security.IsAllowed<CanSetGlobalDashboardFavourites>())
+            if (canSetGlobal)
             {
                 var config = new GlobalConfiguration<GlobalUtilityDashboardSettings>();
                 var global = config.Load();
@@ -311,7 +271,7 @@ namespace PRSDesktop
             });
 
             var index = 0;
-            var favourites = GetFavourites().ToList();
+            var favourites = GetFavourites(true).ToList();
             if (favourites.Any())
             {
                 foreach (var favourite in favourites)