From 626724fe87b9b285004f000ed68fc9372541dbec Mon Sep 17 00:00:00 2001
From: Lorenz Stechauner <lorenz.stechauner@necronda.net>
Date: Fri, 19 Jan 2024 13:35:53 +0100
Subject: [PATCH] [#29] DeliveryAdminWindow: Only show sums of filtered parts
 when filtering

---
 Elwig/Models/Entities/Delivery.cs         | 15 +++++-
 Elwig/Windows/DeliveryAdminWindow.xaml    |  6 +--
 Elwig/Windows/DeliveryAdminWindow.xaml.cs | 65 +++++++++++++----------
 3 files changed, 53 insertions(+), 33 deletions(-)

diff --git a/Elwig/Models/Entities/Delivery.cs b/Elwig/Models/Entities/Delivery.cs
index f7721a5..c05f079 100644
--- a/Elwig/Models/Entities/Delivery.cs
+++ b/Elwig/Models/Entities/Delivery.cs
@@ -68,19 +68,32 @@ namespace Elwig.Models.Entities {
 
         [InverseProperty("Delivery")]
         public virtual ISet<DeliveryPart> Parts { get; private set; }
+        [NotMapped]
+        public IEnumerable<DeliveryPart> FilteredParts => PartFilter == null ? Parts : Parts.Where(p => PartFilter(p));
+
+        [NotMapped]
+        public Predicate<DeliveryPart>? PartFilter { get; set; }
 
         public int Weight => Parts.Select(p => p.Weight).Sum();
+        public int FilteredWeight => FilteredParts.Select(p => p.Weight).Sum();
 
         public IEnumerable<string> SortIds => Parts
             .GroupBy(p => p.SortId)
             .OrderByDescending(g => g.Select(p => p.Weight).Sum())
-            .Select(g => g.Select(p => p.SortId).First());
+            .Select(g => g.Key);
+        public IEnumerable<string> FilteredSortIds => FilteredParts
+            .GroupBy(p => p.SortId)
+            .OrderByDescending(g => g.Select(p => p.Weight).Sum())
+            .Select(g => g.Key);
 
         public string SortIdString => string.Join(", ", SortIds);
+        public string FilteredSortIdString => string.Join(", ", FilteredSortIds);
 
         public double Kmw => Utils.AggregateDeliveryPartsKmw(Parts);
+        public double FilteredKmw => Utils.AggregateDeliveryPartsKmw(FilteredParts);
 
         public double Oe => Utils.KmwToOe(Kmw);
+        public double FilteredOe => Utils.KmwToOe(FilteredKmw);
 
         public int SearchScore(IEnumerable<string> keywords) {
             var list = new string?[] {
diff --git a/Elwig/Windows/DeliveryAdminWindow.xaml b/Elwig/Windows/DeliveryAdminWindow.xaml
index a1ac8c5..001149e 100644
--- a/Elwig/Windows/DeliveryAdminWindow.xaml
+++ b/Elwig/Windows/DeliveryAdminWindow.xaml
@@ -148,21 +148,21 @@
                             </Style>
                         </DataGridTextColumn.CellStyle>
                     </DataGridTextColumn>
-                    <DataGridTextColumn Header="Sorte" Binding="{Binding SortIdString}" Width="50">
+                    <DataGridTextColumn Header="Sorte" Binding="{Binding FilteredSortIdString}" Width="50">
                         <DataGridTextColumn.CellStyle>
                             <Style>
                                 <Setter Property="TextBlock.TextAlignment" Value="Center"/>
                             </Style>
                         </DataGridTextColumn.CellStyle>
                     </DataGridTextColumn>
-                    <DataGridTextColumn Header="Gewicht" Binding="{Binding Weight, StringFormat='{}{0:N0} kg '}" Width="75">
+                    <DataGridTextColumn Header="Gewicht" Binding="{Binding FilteredWeight, StringFormat='{}{0:N0} kg '}" Width="75">
                         <DataGridTextColumn.CellStyle>
                             <Style>
                                 <Setter Property="TextBlock.TextAlignment" Value="Right"/>
                             </Style>
                         </DataGridTextColumn.CellStyle>
                     </DataGridTextColumn>
-                    <DataGridTextColumn Header="Gradation" Binding="{Binding Kmw, StringFormat='{}{0:N1}° '}" Width="50">
+                    <DataGridTextColumn Header="Gradation" Binding="{Binding FilteredKmw, StringFormat='{}{0:N1}° '}" Width="50">
                         <DataGridTextColumn.CellStyle>
                             <Style>
                                 <Setter Property="TextBlock.TextAlignment" Value="Right"/>
diff --git a/Elwig/Windows/DeliveryAdminWindow.xaml.cs b/Elwig/Windows/DeliveryAdminWindow.xaml.cs
index 110fb9f..8201564 100644
--- a/Elwig/Windows/DeliveryAdminWindow.xaml.cs
+++ b/Elwig/Windows/DeliveryAdminWindow.xaml.cs
@@ -10,6 +10,7 @@ using System;
 using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
+using System.Linq.Expressions;
 using System.Threading.Tasks;
 using System.Windows;
 using System.Windows.Controls;
@@ -168,7 +169,7 @@ namespace Elwig.Windows {
 
         private async void Menu_Print_DeliveryJournal_ShowFilter_Click(object sender, RoutedEventArgs evt) {
             Mouse.OverrideCursor = Cursors.AppStarting;
-            var (f, _, d, _) = await GetFilters();
+            var (f, _, d, _, _) = await GetFilters();
             var doc = new DeliveryJournal(string.Join(" / ", f), d);
             await doc.Generate();
             Mouse.OverrideCursor = null;
@@ -177,7 +178,7 @@ namespace Elwig.Windows {
 
         private async void Menu_Print_DeliveryJournal_PrintFilter_Click(object sender, RoutedEventArgs evt) {
             Mouse.OverrideCursor = Cursors.AppStarting;
-            var (f, _, d, _) = await GetFilters();
+            var (f, _, d, _, _) = await GetFilters();
             var doc = new DeliveryJournal(string.Join(" / ", f), d);
             await doc.Generate();
             Mouse.OverrideCursor = null;
@@ -296,7 +297,7 @@ namespace Elwig.Windows {
             await RefreshDeliveryListQuery();
         }
 
-        private async Task<(List<string>, IQueryable<Delivery>, IQueryable<DeliveryPart>, List<string>)> GetFilters() {
+        private async Task<(List<string>, IQueryable<Delivery>, IQueryable<DeliveryPart>, Predicate<DeliveryPart>, List<string>)> GetFilters() {
             List<string> filterNames = [];
             IQueryable<Delivery> deliveryQuery = Context.Deliveries;
             if (IsReceipt && App.BranchNum > 1) {
@@ -316,12 +317,8 @@ namespace Elwig.Windows {
                 deliveryQuery = deliveryQuery.Where(d => d.Year == SeasonInput.Value);
                 filterNames.Add(SeasonInput.Value.ToString() ?? "");
             }
-            IQueryable<DeliveryPart> dpq = deliveryQuery
-                .SelectMany(d => d.Parts)
-                .OrderBy(p => p.Delivery.DateString)
-                .ThenBy(p => p.Delivery.TimeString)
-                .ThenBy(p => p.Delivery.LsNr)
-                .ThenBy(p => p.DPNr);
+
+            Expression<Func<DeliveryPart, bool>> prd = p => true;
 
             var filterVar = new List<string>();
             var filterNotVar = new List<string>();
@@ -487,32 +484,32 @@ namespace Elwig.Windows {
                     }
                 }
 
-                if (filterYearGt > 0) dpq = dpq.Where(p => p.Year >= filterYearGt);
-                if (filterYearLt > 0) dpq = dpq.Where(p => p.Year < filterYearLt);
-                if (filterMgNr.Count > 0) dpq = dpq.Where(p => filterMgNr.Contains(p.Delivery.MgNr));
+                if (filterYearGt > 0) prd = prd.And(p => p.Year >= filterYearGt);
+                if (filterYearLt > 0) prd = prd.And(p => p.Year < filterYearLt);
+                if (filterMgNr.Count > 0) prd = prd.And(p => filterMgNr.Contains(p.Delivery.MgNr));
                 if (filterDate.Count > 0) {
                     var pr = PredicateBuilder.New<DeliveryPart>(false);
                     foreach (var (d1, d2) in filterDate)
                         pr.Or(p => (d1 == null || d1.CompareTo(p.Delivery.DateString.Substring(10 - d1.Length)) <= 0) && (d2 == null || d2.CompareTo(p.Delivery.DateString.Substring(10 - d2.Length)) >= 0));
-                    dpq = dpq.Where(pr);
+                    prd = prd.And(pr);
                 }
                 if (filterTime.Count > 0) {
                     var pr = PredicateBuilder.New<DeliveryPart>(false);
                     foreach (var (t1, t2) in filterTime)
                         pr.Or(p => (t1 == null || t1.CompareTo(p.Delivery.TimeString) <= 0) && (t2 == null || t2.CompareTo(p.Delivery.TimeString) > 0));
-                    dpq = dpq.Where(p => p.Delivery.TimeString != null).Where(pr);
+                    prd = prd.And(p => p.Delivery.TimeString != null).And(pr);
                 }
-                if (filterVar.Count > 0) dpq = dpq.Where(p => filterVar.Contains(p.SortId));
-                if (filterNotVar.Count > 0) dpq = dpq.Where(p => !filterNotVar.Contains(p.SortId));
-                if (filterQual.Count > 0) dpq = dpq.Where(p => filterQual.Contains(p.QualId));
-                if (filterNotQual.Count > 0) dpq = dpq.Where(p => !filterNotQual.Contains(p.QualId));
-                if (filterZwst.Count > 0) dpq = dpq.Where(p => filterZwst.Contains(p.Delivery.ZwstId));
-                if (filterAttr.Count > 0) dpq = dpq.Where(p => p.AttrId != null && filterAttr.Contains(p.AttrId));
-                if (filterNotAttr.Count > 0) dpq = dpq.Where(p => p.AttrId == null || !filterNotAttr.Contains(p.AttrId));
-                if (filterKmwGt > 0) dpq = dpq.Where(p => p.Kmw >= filterKmwGt);
-                if (filterKmwLt > 0) dpq = dpq.Where(p => p.Kmw < filterKmwLt);
-                if (filterOeGt > 0) dpq = dpq.Where(p => p.Kmw * (4.54 + 0.022 * p.Kmw) >= filterOeGt);
-                if (filterOeLt > 0) dpq = dpq.Where(p => p.Kmw * (4.54 + 0.022 * p.Kmw) < filterOeLt);
+                if (filterVar.Count > 0) prd = prd.And(p => filterVar.Contains(p.SortId));
+                if (filterNotVar.Count > 0) prd = prd.And(p => !filterNotVar.Contains(p.SortId));
+                if (filterQual.Count > 0) prd = prd.And(p => filterQual.Contains(p.QualId));
+                if (filterNotQual.Count > 0) prd = prd.And(p => !filterNotQual.Contains(p.QualId));
+                if (filterZwst.Count > 0) prd = prd.And(p => filterZwst.Contains(p.Delivery.ZwstId));
+                if (filterAttr.Count > 0) prd = prd.And(p => p.AttrId != null && filterAttr.Contains(p.AttrId));
+                if (filterNotAttr.Count > 0) prd = prd.And(p => p.AttrId == null || !filterNotAttr.Contains(p.AttrId));
+                if (filterKmwGt > 0) prd = prd.And(p => p.Kmw >= filterKmwGt);
+                if (filterKmwLt > 0) prd = prd.And(p => p.Kmw < filterKmwLt);
+                if (filterOeGt > 0) prd = prd.And(p => p.Kmw * (4.54 + 0.022 * p.Kmw) >= filterOeGt);
+                if (filterOeLt > 0) prd = prd.And(p => p.Kmw * (4.54 + 0.022 * p.Kmw) < filterOeLt);
 
                 if (filterYearGt > 0 && filterYearLt > 0) {
                     filterNames.Insert(0, $"{filterYearGt}–{filterYearLt - 1}");
@@ -537,7 +534,15 @@ namespace Elwig.Windows {
                 }
             }
 
-            return (filterNames, dpq.Select(p => p.Delivery).Distinct().OrderBy(d => d.DateString).ThenBy(d => d.TimeString), dpq, filter);
+            IQueryable<DeliveryPart> dpq = deliveryQuery
+                .SelectMany(d => d.Parts)
+                .Where(prd)
+                .OrderBy(p => p.Delivery.DateString)
+                .ThenBy(p => p.Delivery.TimeString)
+                .ThenBy(p => p.Delivery.LsNr)
+                .ThenBy(p => p.DPNr);
+
+            return (filterNames, dpq.Select(p => p.Delivery).Distinct().OrderBy(d => d.DateString).ThenBy(d => d.TimeString), dpq, prd.Invoke, filter);
         }
 
         private static void AddToolTipCell(Grid grid, string text, int row, int col, int colSpan = 1, bool bold = false, bool alignRight = false, bool alignCenter = false) {
@@ -573,7 +578,7 @@ namespace Elwig.Windows {
         }
 
         private async Task RefreshDeliveryListQuery(bool updateSort = false) {
-            var (_, deliveryQuery, deliveryPartsQuery, filter) = await GetFilters();
+            var (_, deliveryQuery, deliveryPartsQuery, predicate, filter) = await GetFilters();
             var deliveries = await deliveryQuery.ToListAsync();
             deliveries.Reverse();
 
@@ -589,8 +594,10 @@ namespace Elwig.Windows {
                     .ToList();
             }
 
+            deliveries.ForEach(d => { d.PartFilter = predicate; });
             ControlUtils.RenewItemsSource(DeliveryList, deliveries, d => ((d as Delivery)?.Year, (d as Delivery)?.DId),
                 DeliveryList_SelectionChanged, filter.Count > 0 ? ControlUtils.RenewSourceDefault.IfOnly : ControlUtils.RenewSourceDefault.None, !updateSort);
+            await RefreshDeliveryParts();
 
             var members = deliveries.Select(d => d.Member).DistinctBy(m => m.MgNr).ToList();
             StatusMembers.Text = $"Mitglieder: {members.Count}" + (members.Count > 0 && members.Count <= 4 ? $" ({string.Join(", ", members.Select(m => m.AdministrativeName))})" : "");
@@ -760,7 +767,7 @@ namespace Elwig.Windows {
         private async Task RefreshDeliveryParts() {
             if (DeliveryList.SelectedItem is Delivery d) {
                 ControlUtils.RenewItemsSource(ModifiersInput, await Context.Modifiers.Where(m => m.Year == d.Year).OrderBy(m => m.Ordering).ToListAsync(), i => (i as Modifier)?.ModId);
-                ControlUtils.RenewItemsSource(DeliveryPartList, d.Parts.OrderBy(p => p.DPNr).ToList(), i => ((i as DeliveryPart)?.Year, (i as DeliveryPart)?.DId, (i as DeliveryPart)?.DPNr), DeliveryPartList_SelectionChanged, ControlUtils.RenewSourceDefault.First);
+                ControlUtils.RenewItemsSource(DeliveryPartList, d.FilteredParts.OrderBy(p => p.DPNr).ToList(), i => ((i as DeliveryPart)?.Year, (i as DeliveryPart)?.DId, (i as DeliveryPart)?.DPNr), DeliveryPartList_SelectionChanged, ControlUtils.RenewSourceDefault.First);
             } else {
                 ControlUtils.RenewItemsSource(ModifiersInput, await Context.Modifiers.Where(m => m.Year == Utils.CurrentLastSeason).OrderBy(m => m.Ordering).ToListAsync(), i => (i as Modifier)?.ModId);
                 DeliveryPartList.ItemsSource = null;
@@ -1134,7 +1141,7 @@ namespace Elwig.Windows {
             } else {
                 // switch to last delivery part
                 DeliveryPartList.IsEnabled = true;
-                DeliveryPartList.SelectedItem = d.Parts.Last();
+                DeliveryPartList.SelectedItem = d.FilteredParts.Last();
             }
         }