From c24b1ca2b98e601c9acfdad6f44c9b179b79e0db Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Thu, 2 Jan 2025 16:50:32 +0100 Subject: [PATCH] DeliveryAdminWindow: Add WineLocalityStatistics --- Elwig/Helpers/Export/Ods.cs | 1 + .../Dtos/MemberDeliveryPerVarietyData.cs | 1 - .../Models/Dtos/WineLocalityStatisticsData.cs | 60 ++++++++++++++++++ Elwig/Services/DeliveryService.cs | 32 ++++++++++ Elwig/Windows/DeliveryAdminWindow.xaml | 58 +++++++++-------- Elwig/Windows/DeliveryAdminWindow.xaml.cs | 62 ++++++++++--------- 6 files changed, 158 insertions(+), 56 deletions(-) create mode 100644 Elwig/Models/Dtos/WineLocalityStatisticsData.cs diff --git a/Elwig/Helpers/Export/Ods.cs b/Elwig/Helpers/Export/Ods.cs index b673fdc..7e1c892 100644 --- a/Elwig/Helpers/Export/Ods.cs +++ b/Elwig/Helpers/Export/Ods.cs @@ -301,6 +301,7 @@ namespace Elwig.Helpers.Export { if (units != null && units.Length > 0) { int n = -1; switch (units[0]) { + case "#": n = 0; data = $"{v:N0}"; break; case "%": n = 1; data = $"{v:N1}"; break; case "€": n = 2; data = $"{v:N2}"; break; case "€/kg": n = 4; data = $"{v:N4}"; break; diff --git a/Elwig/Models/Dtos/MemberDeliveryPerVarietyData.cs b/Elwig/Models/Dtos/MemberDeliveryPerVarietyData.cs index 5575f66..dea124b 100644 --- a/Elwig/Models/Dtos/MemberDeliveryPerVarietyData.cs +++ b/Elwig/Models/Dtos/MemberDeliveryPerVarietyData.cs @@ -1,5 +1,4 @@ using Microsoft.EntityFrameworkCore; -using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; diff --git a/Elwig/Models/Dtos/WineLocalityStatisticsData.cs b/Elwig/Models/Dtos/WineLocalityStatisticsData.cs new file mode 100644 index 0000000..edd8e74 --- /dev/null +++ b/Elwig/Models/Dtos/WineLocalityStatisticsData.cs @@ -0,0 +1,60 @@ +using Elwig.Helpers; +using Elwig.Models.Entities; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using static Elwig.Models.Dtos.WineLocalityStatisticsData; + +namespace Elwig.Models.Dtos { + class WineLocalityStatisticsData : DataTable { + + private static readonly (string, string, string?, int?)[] FieldNames = [ + ("Branch", "Zwst.", null, 30), + ("KgNr", "KgNr.", null, 15), + ("Name", "Katastralgemeinde", null, 50), + ("Members", "Mitgl.", "#", 15), + ("Deliveries", "Lfrg.", "#", 15), + ("Parts", "Teill.", "#", 15), + ("Weight", "Gewicht", "kg", 20), + ("Gradation", "Gradation", "°Oe|°KMW", 30), + ]; + + public record struct StatisticsRow( + string Branch, + int? KgNr, + string? Name, + int Members, + int Deliveries, + int Parts, + int Weight, + (double Oe, double Kmw) Gradation + ); + + public WineLocalityStatisticsData(IEnumerable rows, List filterNames) : + base("Lieferstatistik pro Ort", "Lieferstatistik pro Ort", string.Join(" / ", filterNames), rows, FieldNames) { + } + + public static async Task FromQuery(IQueryable query, List filterNames) { + return new((await query + .GroupBy(p => new { + Branch = p.Delivery.Branch.Name, + p.Kg!.KgNr, + Kg = p.Kg!.AtKg.Name, + }, (k, g) => new { + k.Branch, + KgNr = (int?)k.KgNr, + Kg = (string?)k.Kg, + Members = g.Select(p => p.Delivery.Member).Distinct().Count(), + Deliveries = g.Select(p => p.Delivery).Distinct().Count(), + Parts = g.Count(), + Weight = g.Sum(p => p.Weight), + Kmw = g.Sum(p => p.Kmw * p.Weight) / g.Sum(p => p.Weight), + }) + .OrderByDescending(g => g.Weight) + .ThenBy(g => g.KgNr) + .ToListAsync()).Select(g => new StatisticsRow(g.Branch, g.KgNr, g.Kg, g.Members, g.Deliveries, g.Parts, g.Weight, (Utils.KmwToOe(g.Kmw), Math.Round(g.Kmw, 1)))), filterNames); + } + } +} diff --git a/Elwig/Services/DeliveryService.cs b/Elwig/Services/DeliveryService.cs index 6725d88..1425314 100644 --- a/Elwig/Services/DeliveryService.cs +++ b/Elwig/Services/DeliveryService.cs @@ -809,6 +809,38 @@ namespace Elwig.Services { Mouse.OverrideCursor = null; } + public static async Task GenerateLocalityStatistics(this DeliveryAdminViewModel vm, ExportSubject subject) { + using var ctx = new AppDbContext(); + IQueryable query; + List filterNames = []; + if (subject == ExportSubject.FromFilters) { + var (f, _, q, _, _) = await vm.GetFilters(ctx); + query = q; + filterNames.AddRange(f); + + } else { + throw new ArgumentException("Invalid value for ExportSubject"); + } + + var d = new SaveFileDialog() { + FileName = $"Lieferstatistik-{vm.FilterSeason ?? Utils.CurrentLastSeason}.ods", + DefaultExt = "ods", + Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods", + Title = $"Lieferstatistik pro Ort speichern unter - Elwig" + }; + if (d.ShowDialog() == true) { + Mouse.OverrideCursor = Cursors.AppStarting; + try { + using var ods = new OdsFile(d.FileName); + var tbl = await WineLocalityStatisticsData.FromQuery(query, filterNames); + await ods.AddTable(tbl); + } catch (Exception exc) { + MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); + } + Mouse.OverrideCursor = null; + } + } + public static async Task GenerateDeliveryDepreciationList(this DeliveryAdminViewModel vm, ExportSubject subject, ExportMode mode) { using var ctx = new AppDbContext(); IQueryable query; diff --git a/Elwig/Windows/DeliveryAdminWindow.xaml b/Elwig/Windows/DeliveryAdminWindow.xaml index d768101..024fd3b 100644 --- a/Elwig/Windows/DeliveryAdminWindow.xaml +++ b/Elwig/Windows/DeliveryAdminWindow.xaml @@ -110,32 +110,6 @@ - - - - - - - - - - - - - - - @@ -155,6 +129,38 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/Elwig/Windows/DeliveryAdminWindow.xaml.cs b/Elwig/Windows/DeliveryAdminWindow.xaml.cs index 073504a..67464d4 100644 --- a/Elwig/Windows/DeliveryAdminWindow.xaml.cs +++ b/Elwig/Windows/DeliveryAdminWindow.xaml.cs @@ -41,7 +41,7 @@ namespace Elwig.Windows { CommandBindings.Add(new CommandBinding(CtrlP, Menu_DeliveryNote_Show_Click)); CommandBindings.Add(new CommandBinding(CtrlO, Menu_DeliveryJournal_ShowFilters_Click)); CommandBindings.Add(new CommandBinding(CtrlJ, Menu_DeliveryJournal_PrintToday_Click)); - CommandBindings.Add(new CommandBinding(CtrlQ, Menu_WineQualityStatistics_PrintToday_Click)); + CommandBindings.Add(new CommandBinding(CtrlQ, Menu_Statistics_WineQuality_PrintToday_Click)); CommandBindings.Add(new CommandBinding(CtrlShiftP, Menu_DeliveryNote_Print_Click)); CommandBindings.Add(new CommandBinding(CtrlShiftO, Menu_DeliveryJournal_PrintFilters_Click)); RequiredInputs = [ @@ -97,17 +97,17 @@ namespace Elwig.Windows { WeighingDButton.Visibility = Visibility.Hidden; } - Menu_WineQualityStatistics_ModeOe.IsChecked = false; - Menu_WineQualityStatistics_ModeKmw1.IsChecked = false; - Menu_WineQualityStatistics_ModeKmw2.IsChecked = false; - Menu_WineQualityStatistics_ModeKmw5.IsChecked = false; - Menu_WineQualityStatistics_ModeKmw10.IsChecked = false; + Menu_Statistics_WineQuality_ModeOe.IsChecked = false; + Menu_Statistics_WineQuality_ModeKmw1.IsChecked = false; + Menu_Statistics_WineQuality_ModeKmw2.IsChecked = false; + Menu_Statistics_WineQuality_ModeKmw5.IsChecked = false; + Menu_Statistics_WineQuality_ModeKmw10.IsChecked = false; switch (App.Client.OrderingMemberList) { - case 0: Menu_WineQualityStatistics_ModeOe.IsChecked = true; break; - case 1: Menu_WineQualityStatistics_ModeKmw1.IsChecked = true; break; - case 2: Menu_WineQualityStatistics_ModeKmw2.IsChecked = true; break; - case 3: Menu_WineQualityStatistics_ModeKmw5.IsChecked = true; break; - case 4: Menu_WineQualityStatistics_ModeKmw10.IsChecked = true; break; + case 0: Menu_Statistics_WineQuality_ModeOe.IsChecked = true; break; + case 1: Menu_Statistics_WineQuality_ModeKmw1.IsChecked = true; break; + case 2: Menu_Statistics_WineQuality_ModeKmw2.IsChecked = true; break; + case 3: Menu_Statistics_WineQuality_ModeKmw5.IsChecked = true; break; + case 4: Menu_Statistics_WineQuality_ModeKmw10.IsChecked = true; break; } Menu_Export_UploadFilters.IsEnabled = App.Config.SyncUrl != null; @@ -218,40 +218,44 @@ namespace Elwig.Windows { await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromSeasonAndBranch, ExportMode.Upload); } - private async void Menu_WineQualityStatistics_ShowToday_Click(object sender, RoutedEventArgs evt) => + private async void Menu_Statistics_WineQuality_ShowToday_Click(object sender, RoutedEventArgs evt) => await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromToday, ExportMode.Show); - private async void Menu_WineQualityStatistics_SavePdfToday_Click(object sender, RoutedEventArgs evt) => + private async void Menu_Statistics_WineQuality_SavePdfToday_Click(object sender, RoutedEventArgs evt) => await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromToday, ExportMode.SavePdf); - private async void Menu_WineQualityStatistics_PrintToday_Click(object sender, RoutedEventArgs evt) => + private async void Menu_Statistics_WineQuality_PrintToday_Click(object sender, RoutedEventArgs evt) => await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromToday, ExportMode.Print); - private async void Menu_WineQualityStatistics_ShowFilters_Click(object sender, RoutedEventArgs evt) => + private async void Menu_Statistics_WineQuality_ShowFilters_Click(object sender, RoutedEventArgs evt) => await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromFilters, ExportMode.Show); - private async void Menu_WineQualityStatistics_SavePdfFilters_Click(object sender, RoutedEventArgs evt) => + private async void Menu_Statistics_WineQuality_SavePdfFilters_Click(object sender, RoutedEventArgs evt) => await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromFilters, ExportMode.SavePdf); - private async void Menu_WineQualityStatistics_PrintFilters_Click(object sender, RoutedEventArgs evt) => + private async void Menu_Statistics_WineQuality_PrintFilters_Click(object sender, RoutedEventArgs evt) => await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromFilters, ExportMode.Print); - private async void Menu_WineQualityStatistics_Mode_Click(object sender, RoutedEventArgs evt) { - Menu_WineQualityStatistics.IsSubmenuOpen = true; - if (sender == Menu_WineQualityStatistics_ModeOe) { + private async void Menu_Statistics_WineQuality_Mode_Click(object sender, RoutedEventArgs evt) { + Menu_Statistics.IsSubmenuOpen = true; + Menu_Statistics_WineQuality.IsSubmenuOpen = true; + if (sender == Menu_Statistics_WineQuality_ModeOe) { App.Client.OrderingMemberList = 0; - } else if (sender == Menu_WineQualityStatistics_ModeKmw1) { + } else if (sender == Menu_Statistics_WineQuality_ModeKmw1) { App.Client.OrderingMemberList = 1; - } else if (sender == Menu_WineQualityStatistics_ModeKmw2) { + } else if (sender == Menu_Statistics_WineQuality_ModeKmw2) { App.Client.OrderingMemberList = 2; - } else if (sender == Menu_WineQualityStatistics_ModeKmw5) { + } else if (sender == Menu_Statistics_WineQuality_ModeKmw5) { App.Client.OrderingMemberList = 3; - } else if (sender == Menu_WineQualityStatistics_ModeKmw10) { + } else if (sender == Menu_Statistics_WineQuality_ModeKmw10) { App.Client.OrderingMemberList = 4; } - Menu_WineQualityStatistics_ModeOe.IsChecked = App.Client.OrderingMemberList == 0; - Menu_WineQualityStatistics_ModeKmw1.IsChecked = App.Client.OrderingMemberList == 1; - Menu_WineQualityStatistics_ModeKmw2.IsChecked = App.Client.OrderingMemberList == 2; - Menu_WineQualityStatistics_ModeKmw5.IsChecked = App.Client.OrderingMemberList == 3; - Menu_WineQualityStatistics_ModeKmw10.IsChecked = App.Client.OrderingMemberList == 4; + Menu_Statistics_WineQuality_ModeOe.IsChecked = App.Client.OrderingMemberList == 0; + Menu_Statistics_WineQuality_ModeKmw1.IsChecked = App.Client.OrderingMemberList == 1; + Menu_Statistics_WineQuality_ModeKmw2.IsChecked = App.Client.OrderingMemberList == 2; + Menu_Statistics_WineQuality_ModeKmw5.IsChecked = App.Client.OrderingMemberList == 3; + Menu_Statistics_WineQuality_ModeKmw10.IsChecked = App.Client.OrderingMemberList == 4; await App.Client.UpdateValues(); } + private async void Menu_Statistic_Locality_SaveFilters_Click(object sender, RoutedEventArgs evt)=> + await ViewModel.GenerateLocalityStatistics(DeliveryService.ExportSubject.FromFilters); + private async void Menu_DeliveryDepreciationList_SaveFilters_Click(object sender, RoutedEventArgs evt) => await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromFilters, ExportMode.SaveList); private async void Menu_DeliveryDepreciationList_ShowFilters_Click(object sender, RoutedEventArgs evt) =>