diff --git a/Elwig/Services/DeliveryAncmtService.cs b/Elwig/Services/DeliveryAncmtService.cs index 4175a1e..65608bb 100644 --- a/Elwig/Services/DeliveryAncmtService.cs +++ b/Elwig/Services/DeliveryAncmtService.cs @@ -12,6 +12,8 @@ using Microsoft.Win32; using System.Windows.Input; using System.Windows; using System; +using LinqKit; +using System.Windows.Controls; namespace Elwig.Services { public static class DeliveryAncmtService { @@ -53,11 +55,21 @@ namespace Elwig.Services { var filterVar = new List(); var filterNotVar = new List(); var filterMgNr = new List(); + var filterZwst = new List(); + var filterAttr = new List(); + var filterNotAttr = new List(); + var filterCult = new List(); + var filterNotCult = new List(); + var filterDate = new List<(string?, string?)>(); + int filterWeightGt = 0, filterWeightLt = 0; var filter = vm.TextFilter; if (filter.Count > 0) { var var = await ctx.WineVarieties.ToDictionaryAsync(v => v.SortId, v => v); var mgnr = await ctx.Members.ToDictionaryAsync(m => m.MgNr.ToString(), m => m); + var zwst = await ctx.Branches.ToDictionaryAsync(b => b.Name.ToLower().Split(' ')[0], b => b); + var attr = await ctx.WineAttributes.ToDictionaryAsync(a => a.Name.ToLower().Split(' ')[0], a => a); + var cult = await ctx.WineCultivations.ToDictionaryAsync(c => c.Name.ToLower().Split(' ')[0], c => c); for (int i = 0; i < filter.Count; i++) { var e = filter[i]; @@ -81,6 +93,82 @@ namespace Elwig.Services { filterMgNr.Add(int.Parse(e)); filter.RemoveAt(i--); filterNames.Add(member.AdministrativeName); + } else if (attr.ContainsKey(e.ToLower())) { + var a = attr[e.ToLower()]; + filterAttr.Add(a.AttrId); + filter.RemoveAt(i--); + filterNames.Add($"Attribut {a.Name}"); + } else if (e[0] == '!' && attr.ContainsKey(e[1..].ToLower())) { + var a = attr[e[1..].ToLower()]; + filterNotAttr.Add(a.AttrId); + filter.RemoveAt(i--); + filterNames.Add($"ohne Attribut {a.Name}"); + } else if (cult.ContainsKey(e.ToLower())) { + var c = cult[e.ToLower()]; + filterCult.Add(c.CultId); + filter.RemoveAt(i--); + filterNames.Add($"Bewirtschaftung {c.Name}"); + } else if (e[0] == '!' && cult.ContainsKey(e[1..].ToLower())) { + var c = cult[e[1..].ToLower()]; + filterNotCult.Add(c.CultId); + filter.RemoveAt(i--); + filterNames.Add($"ohne Bewirtschaftung {c.Name}"); + } else if (zwst.ContainsKey(e.ToLower())) { + var b = zwst[e.ToLower()]; + filterZwst.Add(b.ZwstId); + filter.RemoveAt(i--); + filterNames.Add($"Zweigstelle {b.Name}"); + } else if ((e.StartsWith('>') || e.StartsWith('<')) && e.EndsWith("kg")) { + if (int.TryParse(e[1..^2], out var num)) { + switch (e[0]) { + case '>': filterWeightGt = num; break; + case '<': filterWeightLt = num; break; + } + filter.RemoveAt(i--); + } + if (e.Length == 3) filter.RemoveAt(i--); + } else if (DateOnly.TryParse(e, out var date)) { + var str = date.ToString("yyyy-MM-dd"); + filterDate.Add((str, str)); + filter.RemoveAt(i--); + if (filterNames.Contains($"{vm.FilterSeason}") && vm.FilterSeason == date.Year) + filterNames.Remove($"{vm.FilterSeason}"); + filterNames.Add(date.ToString("dd.MM.yyyy")); + } else if (Utils.DateFromToRegex.IsMatch(e)) { + var parts = e.Split("-"); + if (parts.Length == 1) { + // single date + var dParts = parts[0].Split('.'); + var str = $"{dParts[2]}-{dParts[1].PadLeft(2, '0')}-{dParts[0].PadLeft(2, '0')}"; + filterDate.Add((str, str)); + filter.RemoveAt(i--); + var n = string.Join('.', str.Split('-').Reverse()); + if (dParts[2] == "") { + filterNames.Remove($"{vm.FilterSeason}"); + filterNames.Add(n + $"{vm.FilterSeason}"); + } else { + if ($"{vm.FilterSeason}" == dParts[2]) + filterNames.Remove($"{vm.FilterSeason}"); + filterNames.Add(n); + } + } else if (parts.Length == 2) { + // from/to date + var d1Parts = parts[0].Split('.'); + var d2Parts = parts[1].Split('.'); + var s1 = d1Parts.Length < 2 ? null : $"{d1Parts.ElementAtOrDefault(2)}-{d1Parts[1].PadLeft(2, '0')}-{d1Parts[0].PadLeft(2, '0')}"; + var s2 = d2Parts.Length < 2 ? null : $"{d2Parts.ElementAtOrDefault(2)}-{d2Parts[1].PadLeft(2, '0')}-{d2Parts[0].PadLeft(2, '0')}"; + filterDate.Add((s1, s2)); + filter.RemoveAt(i--); + var n1 = s1 == null ? null : string.Join('.', s1.Split('-').Reverse()); + var n2 = s2 == null ? null : string.Join('.', s2.Split('-').Reverse()); + if (n1 != null && n2 != null) { + filterNames.Add($"{n1}–{n2}"); + } else if (n1 != null) { + filterNames.Add($"ab dem {n1}"); + } else if (n2 != null) { + filterNames.Add($"bis zum {n2}"); + } + } } else if (e.Length > 2 && e.StartsWith('"') && e.EndsWith('"')) { filter[i] = e[1..^1]; } else if (e.Length <= 2) { @@ -88,9 +176,30 @@ namespace Elwig.Services { } } + if (filterWeightGt > 0) deliveryAncmtQuery = deliveryAncmtQuery.Where(a => a.Weight >= filterWeightGt); + if (filterWeightLt > 0) deliveryAncmtQuery = deliveryAncmtQuery.Where(a => a.Weight <= filterWeightLt); if (filterMgNr.Count > 0) deliveryAncmtQuery = deliveryAncmtQuery.Where(a => filterMgNr.Contains(a.MgNr)); + if (filterDate.Count > 0) { + var pr = PredicateBuilder.New(false); + foreach (var (d1, d2) in filterDate) + pr.Or(a => (d1 == null || d1.CompareTo(a.Schedule.DateString.Substring(10 - d1.Length)) <= 0) && (d2 == null || d2.CompareTo(a.Schedule.DateString.Substring(10 - d2.Length)) >= 0)); + deliveryAncmtQuery = deliveryAncmtQuery.Where(pr); + } if (filterVar.Count > 0) deliveryAncmtQuery = deliveryAncmtQuery.Where(a => filterVar.Contains(a.SortId)); if (filterNotVar.Count > 0) deliveryAncmtQuery = deliveryAncmtQuery.Where(a => !filterNotVar.Contains(a.SortId)); + if (filterZwst.Count > 0) deliveryAncmtQuery = deliveryAncmtQuery.Where(a => filterZwst.Contains(a.Schedule.ZwstId)); + if (filterAttr.Count > 0) deliveryAncmtQuery = deliveryAncmtQuery.Where(a => a.Schedule.AttrId != null && filterAttr.Contains(a.Schedule.AttrId)); + if (filterNotAttr.Count > 0) deliveryAncmtQuery = deliveryAncmtQuery.Where(a => a.Schedule.AttrId == null || !filterNotAttr.Contains(a.Schedule.AttrId)); + if (filterCult.Count > 0) deliveryAncmtQuery = deliveryAncmtQuery.Where(a => a.Schedule.CultId != null && filterCult.Contains(a.Schedule.CultId)); + if (filterNotCult.Count > 0) deliveryAncmtQuery = deliveryAncmtQuery.Where(a => a.Schedule.CultId == null || !filterNotCult.Contains(a.Schedule.CultId)); + + if (filterWeightGt > 0 && filterWeightLt > 0) { + filterNames.Add($"{filterWeightGt:N0}–{filterWeightLt:N0} kg"); + } else if (filterWeightGt > 0) { + filterNames.Add($"ab {filterWeightGt:N0} kg"); + } else if (filterWeightLt > 0) { + filterNames.Add($"bis {filterWeightLt:N0} kg"); + } } return (filterNames, deliveryAncmtQuery, filter); @@ -182,5 +291,108 @@ namespace Elwig.Services { Mouse.OverrideCursor = null; } } + + private static void AddToolTipCell(Grid grid, string text, int row, int col, int colSpan = 1, bool bold = false, bool alignRight = false, bool alignCenter = false) { + var tb = new TextBlock() { + Text = text, + TextAlignment = alignRight ? TextAlignment.Right : alignCenter ? TextAlignment.Center : TextAlignment.Left, + Margin = new(0, 12 * row, 0, 0), + FontWeight = bold ? FontWeights.Bold : FontWeights.Normal, + }; + tb.SetValue(Grid.ColumnProperty, col); + tb.SetValue(Grid.ColumnSpanProperty, colSpan); + grid.Children.Add(tb); + } + + private static void AddToolTipRow(Grid grid, int row, string? h1, string? h2, int weight, int? total1, int total2) { + var bold = h2 == null; + if (h1 != null) AddToolTipCell(grid, h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold); + if (h2 != null) AddToolTipCell(grid, h2 + ":", row, 1, 1, bold); + AddToolTipCell(grid, $"{weight:N0} kg", row, 2, 1, bold, true); + if (total1 != null && total1 != 0) + AddToolTipCell(grid, $"{weight * 100.0 / total1:N1} %", row, 3, 1, bold, true); + if (total2 != 0) + AddToolTipCell(grid, $"{weight * 100.0 / total2:N1} %", row, 4, 1, bold, true); + } + + public static async Task<(string, Grid)> GenerateToolTip(IQueryable deliveryAncmts) { + var grid = new Grid(); + grid.ColumnDefinitions.Add(new() { Width = new(10) }); + grid.ColumnDefinitions.Add(new() { Width = new(60) }); + grid.ColumnDefinitions.Add(new() { Width = new(80) }); + grid.ColumnDefinitions.Add(new() { Width = new(50) }); + grid.ColumnDefinitions.Add(new() { Width = new(50) }); + var text = "-"; + + var weight = await deliveryAncmts.SumAsync(p => p.Weight); + text = $"{weight:N0} kg"; + AddToolTipRow(grid, 0, "Gewicht", null, weight, null, weight); + + if (await deliveryAncmts.AnyAsync()) { + var attrGroups = await deliveryAncmts + .GroupBy(a => new { Attr = a.Schedule.Attribute!.Name, Cult = a.Schedule.Cultivation!.Name }) + .Select(g => new { + g.Key.Attr, + g.Key.Cult, + Weight = g.Sum(a => a.Weight) + }) + .OrderByDescending(g => g.Weight) + .ThenBy(g => g.Attr) + .ThenBy(g => g.Cult) + .ToListAsync(); + var sortGroups = await deliveryAncmts + .GroupBy(a => a.SortId) + .Select(g => new { + SortId = g.Key, + Weight = g.Sum(a => a.Weight) + }) + .OrderByDescending(g => g.Weight) + .ThenBy(g => g.SortId) + .ToListAsync(); + var groups = await deliveryAncmts + .GroupBy(a => new { + Attr = a.Schedule.Attribute!.Name, + Cult = a.Schedule.Cultivation!.Name, + a.SortId, + }) + .Select(g => new { + g.Key.Attr, + g.Key.Cult, + g.Key.SortId, + Weight = g.Sum(p => p.Weight) + }) + .OrderByDescending(g => g.Weight) + .ThenBy(g => g.Attr) + .ThenBy(g => g.Cult) + .ThenBy(g => g.SortId) + .ToListAsync(); + + int rowNum = 1; + foreach (var attrG in attrGroups) { + rowNum++; + var name = attrG.Attr == null && attrG.Cult == null ? null : attrG.Attr + (attrG.Attr != null && attrG.Cult != null ? " / " : "") + attrG.Cult; + AddToolTipRow(grid, rowNum++, name, null, attrG.Weight, attrG.Weight, weight); + foreach (var g in groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult).OrderByDescending(g => g.Weight).ThenBy(g => g.SortId)) { + AddToolTipRow(grid, rowNum++, null, g.SortId, g.Weight, attrG.Weight, weight); + } + } + + if (attrGroups.Count == 1) { + var g = attrGroups.First(); + var name = g.Attr == null && g.Cult == null ? null : g.Attr + (g.Attr != null && g.Cult != null ? " / " : "") + g.Cult; + if (name != null) { + text += $" [{name}]"; + } + if (sortGroups.Count > 1 && sortGroups.Count <= 4) { + text += $" = {string.Join(" + ", sortGroups.Select(g => $"{g.Weight:N0} kg ({(double)g.Weight / weight:0%})" + (g.SortId == null ? "" : $" [{g.SortId}]")))}"; + + } + } else if (attrGroups.Count <= 4) { + text += $" = {string.Join(" + ", attrGroups.Select(g => $"{g.Weight:N0} kg ({(double)g.Weight / weight:0%})" + (g.Attr == null && g.Cult == null ? "" : $" [{g.Attr}{(g.Attr != null && g.Cult != null ? " / " : "")}{g.Cult}]")))}"; + } + } + + return (text, grid); + } } } diff --git a/Elwig/ViewModels/DeliveryAncmtAdminViewModel.cs b/Elwig/ViewModels/DeliveryAncmtAdminViewModel.cs index c9ea9b0..24beed6 100644 --- a/Elwig/ViewModels/DeliveryAncmtAdminViewModel.cs +++ b/Elwig/ViewModels/DeliveryAncmtAdminViewModel.cs @@ -3,6 +3,7 @@ using Elwig.Models.Entities; using System.Collections.Generic; using System.Linq; using System.Windows; +using System.Windows.Controls; namespace Elwig.ViewModels { public partial class DeliveryAncmtAdminViewModel : ObservableObject { @@ -73,6 +74,9 @@ namespace Elwig.ViewModels { [ObservableProperty] private string _statusAncmtModified = "-"; + [ObservableProperty] + private Grid? _statusWeightToolTip; + [ObservableProperty] private Visibility _controlButtonsVisibility = Visibility.Visible; [ObservableProperty] diff --git a/Elwig/Windows/DeliveryAncmtAdminWindow.xaml b/Elwig/Windows/DeliveryAncmtAdminWindow.xaml index b6f47bf..d63f205 100644 --- a/Elwig/Windows/DeliveryAncmtAdminWindow.xaml +++ b/Elwig/Windows/DeliveryAncmtAdminWindow.xaml @@ -105,7 +105,12 @@ Filtern nach: Sorte: z.B. GV, ZW, rr, sa, !gv (ausgenommen GV), ... Rot/Weiß: z.B. r, Rot, w, weiß, ... - Mitglied: z.B. 1234, 987, ... + Mitglied: z.B. 1234, 987, ... + Zweigstelle: z.B. musterort, ... + Attribut: z.B. kabinett, !kabinett (alle außer kabinett), ... + Bewirtschaftung: z.B. bio, !kip (alle außer KIP), ... + Gewicht: z.B. <500kg, >6000kg, ... + Datum: z.B. 1.9., 15.9.-10.10., -15.10.2020, ... @@ -324,11 +329,11 @@ - + - + - + @@ -342,7 +347,7 @@ - + Gewicht: diff --git a/Elwig/Windows/DeliveryAncmtAdminWindow.xaml.cs b/Elwig/Windows/DeliveryAncmtAdminWindow.xaml.cs index 2e0b8dc..676bd85 100644 --- a/Elwig/Windows/DeliveryAncmtAdminWindow.xaml.cs +++ b/Elwig/Windows/DeliveryAncmtAdminWindow.xaml.cs @@ -134,7 +134,14 @@ namespace Elwig.Windows { DeliveryAncmtList.ScrollIntoView(DeliveryAncmtList.SelectedItem); ViewModel.StatusAncmts = $"{deliveryAncmts.Count:N0}"; - ViewModel.StatusWeight = $"{deliveryAncmts.Sum(a => a.Weight):N0} kg"; + if (filter.Count == 0) { + var (text, grid) = await DeliveryAncmtService.GenerateToolTip(deliveryAncmtQuery); + ViewModel.StatusWeight = text; + ViewModel.StatusWeightToolTip = grid; + } else { + ViewModel.StatusWeight = $"{deliveryAncmts.Sum(a => a.Weight):N0} kg"; + ViewModel.StatusWeightToolTip = null; + } } private async Task RefreshInputs(bool validate = false) {