using Elwig.Helpers; using Elwig.Helpers.Billing; using Elwig.Helpers.Export; using Elwig.Models.Dtos; using Elwig.Services; using Microsoft.EntityFrameworkCore; using Microsoft.Win32; using System; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; using System.Net.NetworkInformation; using System.Reflection; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Threading; namespace Elwig.Windows { public partial class MainWindow : ContextWindow { private readonly DispatcherTimer _syncTimer = new() { Interval = TimeSpan.FromHours(1) }; public MainWindow() { InitializeComponent(); var v = Assembly.GetExecutingAssembly().GetName().Version; VersionField.Text = $"Version: {v?.ToString() ?? "?"} – {App.BranchName}"; if (App.Client.Client == null) VersionField.Text += " (Unbekannt)"; Menu_Help_Update.IsEnabled = App.Config.UpdateUrl != null; Menu_Help_Smtp.IsEnabled = App.Config.Smtp != null; Menu_Sync.IsEnabled = App.Config.SyncUrl != null; SyncButton.Visibility = App.Config.SyncUrl != null ? Visibility.Visible : Visibility.Hidden; Menu_Database_Upload.IsEnabled = App.Config.SyncUrl != null; Menu_Database_Download.IsEnabled = App.Config.SyncUrl != null; } private void Window_Loaded(object sender, RoutedEventArgs evt) { SeasonInput.Value = Utils.CurrentLastSeason; if (Utils.HasInternetConnectivity()) { CheckSync(200); } NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged; _syncTimer.Tick += new EventHandler(OnSyncTimer); _syncTimer.Start(); } private void Window_Closing(object sender, CancelEventArgs evt) { if (App.NumWindows > 1 && !App.ForceShutdown) { foreach (var w in App.Current.Windows.Cast().Where(w => ((w as AdministrationWindow)?.IsEditing ?? false) || ((w as AdministrationWindow)?.IsCreating ?? false))) { try { w.Close(); } catch { } } Thread.Sleep(100); if (App.NumWindows > 1 && !App.Current.Windows.Cast().Any(w => ((w as AdministrationWindow)?.IsEditing ?? false) || ((w as AdministrationWindow)?.IsCreating ?? false))) { var res = MessageBox.Show("Es sind noch weitere Fenster geöffnet.\nSollen alle Fenster geschlossen werden?", "Elwig beenden", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No); if (res != MessageBoxResult.Yes) { evt.Cancel = true; } else { Application.Current.Shutdown(); } } } } private void Menu_Help_About_Click(object sender, RoutedEventArgs evt) { var w = new AboutWindow(); w.Show(); } private async void Menu_Help_Update_Click(object sender, RoutedEventArgs evt) { await App.CheckForUpdates(true); } private async void Menu_Help_Smtp_Click(object sender, RoutedEventArgs evt) { Mouse.OverrideCursor = Cursors.Wait; try { using var client = await Utils.GetSmtpClient(); await client!.DisconnectAsync(true); MessageBox.Show("E-Mail-Einstellungen erfolgreich überprüft!", "Erfolg", MessageBoxButton.OK, MessageBoxImage.Information); } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } Mouse.OverrideCursor = null; } private void Menu_Help_Directory_Click(object sender, RoutedEventArgs evt) { try { Process.Start("explorer.exe", App.DataPath); } catch { } } private void Menu_Help_Config_Click(object sender, RoutedEventArgs evt) { try { Process.Start(new ProcessStartInfo { FileName = "notepad.exe", Arguments = App.ConfigPath, Verb = "runas", UseShellExecute = true, }); } catch { } } private void Menu_Help_Log_Click(object sender, RoutedEventArgs evt) { var w = new LogWindow(); w.Show(); } private async void Menu_Scale_SetDateTime_Click(object sender, RoutedEventArgs evt) { if (App.CommandScales.Count == 0) { MessageBox.Show("Es sind keine geeigneten Waagen verfügbar!", "Datum und Uhrzeit setzen", MessageBoxButton.OK, MessageBoxImage.Error); return; } foreach (var s in App.CommandScales) { try { await s.SetDateAndTime(DateTime.Now); } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } } MessageBox.Show("Datum und Uhrzeit auf entsprechenden Waagen gesetzt!", "Datum und Uhrzeit setzen", MessageBoxButton.OK, MessageBoxImage.Information); } private void Menu_Database_Query_Click(object sender, RoutedEventArgs evt) { var w = new QueryWindow(); w.Show(); } private void Menu_Database_Open_Click(object sender, RoutedEventArgs evt) { if (Path.GetDirectoryName(App.Config.DatabaseFile) is string path) Process.Start("explorer.exe", path); } private void Menu_Database_Export_Click(object sender, RoutedEventArgs evt) { // TODO Menu_Database_Export_Click } private async void Menu_Database_Import_Click(object sender, RoutedEventArgs evt) { try { var d = new OpenFileDialog() { Title = "Export-Datei auswählen - Elwig", DefaultExt = "elwig.zip", Filter = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip", Multiselect = true, }; if (d.ShowDialog() == true) { Mouse.OverrideCursor = Cursors.Wait; await Task.Run(async () => { await ElwigData.Import(d.FileNames, ElwigData.ImportMode.Interactively); }); } } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } Mouse.OverrideCursor = null; } private async void Menu_Database_Backup_Click(object sender, RoutedEventArgs evt) { try { var d = new SaveFileDialog() { Title = "Datenbank sichern - Elwig", FileName = $"database_{Utils.Today:yyyy-MM-dd}.sql.zip", DefaultExt = "sql.zip", Filter = "Komprimierte SQL-Datei (*.sql.zip)|*.sql.zip", AddExtension = false, }; if (d.ShowDialog() == true) { if (!d.FileName.EndsWith(".sql.zip")) d.FileName += ".sql.zip"; Mouse.OverrideCursor = Cursors.Wait; await Task.Run(async () => { await Database.ExportSql(d.FileName, true); }); } } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } Mouse.OverrideCursor = null; } private async void Menu_Database_Restore_Click(object sender, RoutedEventArgs evt) { try { var d = new OpenFileDialog() { Title = "Datenbank wiederherstellen - Elwig", DefaultExt = "sql.zip", Filter = "SQLite-Datenbank (*.sqlite3, *.sqlite3.zip, *.sql, *.sql.zip)|*.sqlite3;*.sqlite3.zip;*.sql;*.sql.zip", }; if (d.ShowDialog() == true) { var res = MessageBox.Show("Soll die Datenbank wirklich unwiederruflich durch die wiederhergestellte Version ersetzt werden?", "Datenbank wiederherstellen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel); if (res != MessageBoxResult.OK) return; Mouse.OverrideCursor = Cursors.Wait; await App.ReplaceDatabase(d.FileName); } } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } Mouse.OverrideCursor = null; } private async void SyncButton_Click(object sender, RoutedEventArgs evt) { if (App.Config.SyncUrl == null) return; Mouse.OverrideCursor = Cursors.Wait; await Task.Run(async () => { await SyncService.Download(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword); await SyncService.UploadModified(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword); }); Mouse.OverrideCursor = null; } private async void Menu_Sync_Download_Click(object sender, RoutedEventArgs evt) { if (App.Config.SyncUrl == null) return; Mouse.OverrideCursor = Cursors.Wait; await Task.Run(async () => { await SyncService.Download(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword); }); Mouse.OverrideCursor = null; } private async void Menu_Sync_UploadBranchDeliveries_Click(object sender, RoutedEventArgs evt) { if (App.Config.SyncUrl == null) return; Mouse.OverrideCursor = Cursors.Wait; await Task.Run(async () => { await SyncService.UploadBranchDeliveries(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword); }); Mouse.OverrideCursor = null; } private async void Menu_Sync_UploadModified_Click(object sender, RoutedEventArgs evt) { if (App.Config.SyncUrl == null) return; Mouse.OverrideCursor = Cursors.Wait; await Task.Run(async () => { await SyncService.UploadModified(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword); }); Mouse.OverrideCursor = null; } private async void Menu_Database_Download_Click(object sender, RoutedEventArgs evt) { if (App.Config.SyncUrl == null) return; Mouse.OverrideCursor = Cursors.Wait; await Task.Run(async () => { try { var data = await Utils.GetExportMetaData(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword); var files = data .Select(f => new { Name = f!["name"]!.AsValue().GetValue(), Timestamp = f!["modified"] != null && DateTime.TryParseExact(f!["modified"]!.AsValue().GetValue(), "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt) ? dt : (DateTime?)null, Url = f!["url"]!.AsValue().GetValue(), Size = f!["size"]!.AsValue().GetValue(), }) .Where(f => f.Name.StartsWith("database.") && f.Name.EndsWith(".zip")) .OrderBy(f => f.Size) .ToList(); if (files.Count == 0) { MessageBox.Show("Die Datenbank wurde noch nicht vom Hauptgerät hochgeladen!", "Datenbank herunterladen", MessageBoxButton.OK, MessageBoxImage.Error); return; } var file = files[0]; var res = MessageBox.Show($"Es wurde eine komprimierte Datenbank (ca. {file.Size / 1024 / 1024} MB) vom {file.Timestamp:dd.MM.yyyy, HH:mm} gefunden.\n\nWollen Sie wirklich die aktuelle Datenbank unwiederruflich\nlöschen und durch die gefundene ersetzen?\n\nDas kann zu Datenverlust führen!", "Datenbank herunterladen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel); if (res != MessageBoxResult.OK) return; var filename = Path.Combine(App.TempPath, file.Name); using (var client = Utils.GetHttpClient(App.Config.SyncUsername, App.Config.SyncPassword)) { using var stream = new FileStream(filename, FileMode.Create); await client.DownloadAsync(file.Url, stream); } res = MessageBox.Show("Die Datenbank wurde erfolgreich heruntergeladen!\n\nSoll die Datenbank wirklich unwiederruflich ersetzt werden?\n\nWenn Sie unsicher sind sprechen Sie sich mit dem Benutzer des Hauptgerätes ab!", "Datenbank herunterladen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel); if (res != MessageBoxResult.OK) return; await App.MainDispatcher.BeginInvoke(async () => { await App.ReplaceDatabase(filename); }); } catch (HttpRequestException exc) { MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Datenbank herunterladen", MessageBoxButton.OK, MessageBoxImage.Error); } catch (TaskCanceledException exc) { MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Datenbank herunterladen", MessageBoxButton.OK, MessageBoxImage.Error); } catch (Exception exc) { MessageBox.Show(exc.Message, "Datenbank herunterladen", MessageBoxButton.OK, MessageBoxImage.Error); } }); Mouse.OverrideCursor = null; } private async void Menu_Database_Upload_Click(object sender, RoutedEventArgs evt) { if (App.Config.SyncUrl == null) return; var res = MessageBox.Show("Sind Sie wirklich sicher, dass Sie die Datenbank dieses\nGerätes hochladen möchten? Das sollte nur vom Hauptgerät aus passieren!", "Datenbank hochladen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel); if (res != MessageBoxResult.OK) return; Mouse.OverrideCursor = Cursors.Wait; await Task.Run(async () => { try { var path = Path.Combine(App.TempPath, "database.sql.zip"); await Database.ExportSql(path, true); await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword); MessageBox.Show($"Hochladen der gesamten Datenbank erfolgreich!", "Datenbank hochladen", MessageBoxButton.OK, MessageBoxImage.Information); } catch (HttpRequestException exc) { MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Datenbank hochladen", MessageBoxButton.OK, MessageBoxImage.Error); } catch (TaskCanceledException exc) { MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Datenbank hochladen", MessageBoxButton.OK, MessageBoxImage.Error); } catch (Exception exc) { MessageBox.Show(exc.Message, "Datenbank hochladen", MessageBoxButton.OK, MessageBoxImage.Error); } }); Mouse.OverrideCursor = null; } private void MemberAdminButton_Click(object sender, RoutedEventArgs evt) { var w = new MemberAdminWindow(); w.Show(); } private void ReceiptButton_Click(object sender, RoutedEventArgs evt) { App.FocusReceipt(); } private void DeliveryAdminButton_Click(object sender, RoutedEventArgs evt) { var w = new DeliveryAdminWindow(); w.Show(); } private void DeliveryAncmtButton_Click(object sender, RoutedEventArgs evt) { App.FocusDeliveryAncmt(); } private void BaseDataButton_Click(object sender, RoutedEventArgs evt) { App.FocusBaseData(); } private void MailButton_Click(object sender, RoutedEventArgs evt) { App.FocusMailWindow(); } protected async override Task OnRenewContext(AppDbContext ctx) { SeasonInput_TextChanged(null, null); CheckSync(); } private void OnSyncTimer(object? sender, EventArgs? evt) { if (Utils.HasInternetConnectivity()) { CheckSync(); } } private void OnNetworkAvailabilityChanged(object? sender, NetworkAvailabilityEventArgs evt) { if (!evt.IsAvailable) return; if (Utils.HasInternetConnectivity()) { CheckSync(1000); } } private async void CheckSync(int delay = 0) { if (App.Config.SyncUrl == null) return; Utils.RunBackground("Daten Synchronisieren", async () => { await Task.Delay(delay); var ch = false; using (var ctx = new AppDbContext()) { ch = await SyncService.ChangesAvailable(ctx, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword); } await App.MainDispatcher.BeginInvoke(() => { if (ch) { SyncButton_1.Text = "\uEA6A"; SyncButton_2.Text = "\uEA81"; } else { SyncButton_1.Text = "\uE895"; SyncButton_2.Text = ""; } }); }); } private void SeasonFinish_Expanded(object sender, RoutedEventArgs evt) { Height = 660; } private void SeasonFinish_Collapsed(object sender, RoutedEventArgs evt) { Height = 390; } private async void SeasonInput_TextChanged(object? sender, TextChangedEventArgs? evt) { using var ctx = new AppDbContext(); var year = SeasonInput.Value; var s0 = await ctx.Seasons.FindAsync(year); var valid = (s0 != null); DeliveryConfirmationButton.IsEnabled = valid; PaymentButton.IsEnabled = valid; OverUnderDeliveryButton.IsEnabled = valid; BreakdownButton.IsEnabled = valid; AreaCommitmentsButton.IsEnabled = valid; BreakdownMemberVarietyButton.IsEnabled = valid; SeasonStatMembersTotal.Text = "-"; SeasonStatMembersGeb.Text = "-"; SeasonStatWeightTotal.Text = "-"; SeasonStatWeightGeb.Text = "-"; SeasonStatWeightUngeb.Text = "-"; SeasonStatArea.Text = "-"; if (valid) { await Task.Run(async () => { var membersTotal = await ctx.Deliveries.Where(d => d.Year == year).Select(d => d.Member).Distinct().CountAsync(); var areaComs = Utils.ActiveAreaCommitments(ctx.AreaCommitments, year!.Value); var membersGeb = await areaComs.Select(c => c.Member).Distinct().CountAsync(); var weightTotal = await ctx.DeliveryParts.Where(p => p.Year == year).SumAsync(p => p.Weight); var gebWeight = await ctx.DeliveryPartBuckets.Where(b => b.Year == year && b.Discr != "_").SumAsync(b => b.Value); var area = await areaComs.SumAsync(c => c.Area); await App.MainDispatcher.BeginInvoke(() => { if (year != SeasonInput.Value) return; SeasonStatMembersTotal.Text = $"{membersTotal:N0}"; SeasonStatMembersGeb.Text = $"{membersGeb:N0}"; SeasonStatWeightTotal.Text = $"{weightTotal:N0} kg"; SeasonStatWeightGeb.Text = $"{gebWeight:N0} kg"; SeasonStatWeightUngeb.Text = $"{weightTotal - gebWeight:N0} kg"; SeasonStatArea.Text = $"{area:N0} m²"; }); }); }; } private void DeliveryConfirmationButton_Click(object sender, RoutedEventArgs evt) { if (SeasonInput.Value is not int year) return; var w = App.FocusMailWindow(year); w.AddDeliveryConfirmation(); } private void PaymentButton_Click(object sender, RoutedEventArgs evt) { if (SeasonInput.Value is not int year) return; App.FocusPaymentVariants(year); } private async void OverUnderDeliveryButton_Click(object sender, RoutedEventArgs evt) { if (SeasonInput.Value is not int year) return; var d = new SaveFileDialog() { FileName = $"Über-Unterlieferungen-{year}.ods", DefaultExt = "ods", Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods", Title = $"Über-/Unterlieferungen {year} speichern unter - Elwig" }; if (d.ShowDialog() == false) return; Mouse.OverrideCursor = Cursors.Wait; await Task.Run(async () => { try { var b = new Billing(year); await b.FinishSeason(); await b.CalculateBuckets(); App.HintContextChange(); using var ctx = new AppDbContext(); var tbl1 = await OverUnderDeliveryData.ForSeason(ctx.OverUnderDeliveryRows, year); var tbl2 = await AreaComUnderDeliveryData.ForSeason(ctx.AreaComUnderDeliveryRows, year); using var ods = new OdsFile(d.FileName); await ods.AddTable(tbl1); await ods.AddTable(tbl2); } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } }); Mouse.OverrideCursor = null; } private async void BreakdownButton_Click(object sender, RoutedEventArgs evt) { if (SeasonInput.Value is not int year) return; var d = new SaveFileDialog() { FileName = $"Aufschlüsselung-{year}.ods", DefaultExt = "ods", Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods", Title = $"Sorten-/Qualitätsaufschlüsselung {year} speichern unter - Elwig" }; if (d.ShowDialog() == false) return; Mouse.OverrideCursor = Cursors.Wait; await Task.Run(async () => { try { var b = new Billing(year); await b.FinishSeason(); await b.CalculateBuckets(); App.HintContextChange(); using var ctx = new AppDbContext(); using var ods = new OdsFile(d.FileName); var tblTotal = await WeightBreakdownData.ForSeason(ctx.WeightBreakDownRows, year); await ods.AddTable(tblTotal); foreach (var branch in await ctx.Branches.OrderBy(b => b.Name).ToListAsync()) { var tbl = await WeightBreakdownData.ForSeason(ctx.WeightBreakDownRows, year, branch); await ods.AddTable(tbl); } } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } }); Mouse.OverrideCursor = null; } private async void AreaCommitmentsButton_Click(object sender, RoutedEventArgs evt) { if (SeasonInput.Value is not int year) return; var d = new SaveFileDialog() { FileName = $"Flächenbindungen-{year}.ods", DefaultExt = "ods", Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods", Title = $"Flächenbindungen {year} speichern unter - Elwig" }; if (d.ShowDialog() == false) return; Mouse.OverrideCursor = Cursors.Wait; await Task.Run(async () => { try { var b = new Billing(year); await b.FinishSeason(); await b.CalculateBuckets(); App.HintContextChange(); using var ctx = new AppDbContext(); var tbl = await MemberAreaComsData.ForSeason(ctx.MemberAreaComsRows, year); using var ods = new OdsFile(d.FileName); await ods.AddTable(tbl); } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } }); Mouse.OverrideCursor = null; } private async void BreakdownMemberVarietyButton_Click(object sender, RoutedEventArgs evt) { if (SeasonInput.Value is not int year) return; var d = new SaveFileDialog() { FileName = $"Liefermengen-Ertrag-{year}.ods", DefaultExt = "ods", Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods", Title = $"Liefermengen/Ertrag {year} speichern unter - Elwig" }; if (d.ShowDialog() == false) return; Mouse.OverrideCursor = Cursors.Wait; await Task.Run(async () => { try { var b = new Billing(year); await b.FinishSeason(); await b.CalculateBuckets(); App.HintContextChange(); using var ctx = new AppDbContext(); var tbl = await MemberDeliveryYieldsPerVarietyData.ForSeason(ctx.MemberDeliveryPerVariantRows, year); using var ods = new OdsFile(d.FileName); await ods.AddTable(tbl); } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } }); Mouse.OverrideCursor = null; } } }