Compare commits
	
		
			40 Commits
		
	
	
		
			b184d5661b
			...
			v1.0.1.5
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e9722c790c | |||
| af98c32026 | |||
| 7300b30cf5 | |||
| 428cd6ddc2 | |||
| 2de8af878b | |||
| 34d95eab9d | |||
| 548aeb2ce9 | |||
| 7edd888aa2 | |||
| a0d4f19f30 | |||
| 67ba342c28 | |||
| 1b69fcb16a | |||
| c8a95422af | |||
| 9d02f18bac | |||
| f9ee2cb120 | |||
| b27b89f599 | |||
| bfbd0a6a22 | |||
| e2de7a1f1c | |||
| 3f769eb7d7 | |||
| 8bc053053c | |||
| a0dcaf7b4f | |||
| 844fc5217a | |||
| 542afa5892 | |||
| 9e02b15ff1 | |||
| 463769b549 | |||
| 2c383d0c55 | |||
| 5ba74c8368 | |||
| 3c9b3c2db1 | |||
| 98f8907817 | |||
| 44dcc5e19f | |||
| d7012ebfa1 | |||
| a9b5317e79 | |||
| f02598760f | |||
| 4b8cd2a0d7 | |||
| 104798d4f2 | |||
| 4653a4f129 | |||
| 07f9a0f522 | |||
| 7e21d9e2e4 | |||
| 4e2eca295d | |||
| 1f165055c1 | |||
| 7b953fa73e | 
							
								
								
									
										169
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										169
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -2,6 +2,171 @@
 | 
				
			|||||||
Changelog
 | 
					Changelog
 | 
				
			||||||
=========
 | 
					=========
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[v1.0.1.5][v1.0.1.5] (2025-10-29) {#v1.0.1.5}
 | 
				
			||||||
 | 
					---------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Behobene Fehler {#v1.0.1.5-bugfixes}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Im Rundschreiben-Fenster (`MailWindow`) kam es zu einem Absturz, wenn man das Fenster über den "Anlieferungsbestätigung"-Knopf im Leseabschluss-Abschnitt geöffnet hat. (af98c32026)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[v1.0.1.5]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.1.5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[v1.0.1.4][v1.0.1.4] (2025-10-28) {#v1.0.1.4}
 | 
				
			||||||
 | 
					---------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Behobene Fehler {#v1.0.1.4-bugfixes}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Im Rundschreiben-Fenster (`MailWindow`) kam es zu einem Absturz, wenn man die Zustelloptionen "Post zusenden an Mitglieder, die keine E-Mail erhalten würden" und "E-Mail zusenden an niemanden" kombiniert hat. (2de8af878b)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Sonstiges {#v1.0.1.4-misc}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Im Auszahlungsvariante-Fenster (`ChartWindow`) gibt es keine Fehlermeldung mehr wenn nicht für alle Sorten ein Preis definiert ist, nur noch eine Warnung. (428cd6ddc2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[v1.0.1.4]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.1.4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[v1.0.1.3][v1.0.1.3] (2025-10-13) {#v1.0.1.3}
 | 
				
			||||||
 | 
					---------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Neue Funktionen {#v1.0.1.3-features}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* In der Liste des Lieferungen-Fenster (`DeliveryAdminWindow`) werden
 | 
				
			||||||
 | 
						* statt ausschließlich der Sorte auch Attribut und Bewirtschaftungsart angezeigt. (a0d4f19f30)
 | 
				
			||||||
 | 
						* Kommentare der Lieferungen (und Teillieferungen) angezeigt. (548aeb2ce9)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Sonstiges {#v1.0.1.3-misc}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Verzögerung der Überprüfung auf automatische Updates auf einige Sekunden verlängert. (67ba342c28)
 | 
				
			||||||
 | 
					* Verbesserung der Ladezeiten im Anmeldungen-Fenster (`DeliveryAncmtAdminWindow`). (7edd888aa2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[v1.0.1.3]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.1.3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[v1.0.1.2][v1.0.1.2] (2025-09-25) {#v1.0.1.2}
 | 
				
			||||||
 | 
					---------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Behobene Fehler {#v1.0.1.2-bugfixes}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Beim automatischen Importieren/Synchronisieren wird bei einem Fehlerfall der Benutzer verständigt, aber der Vorgang nicht abgebrochen. (9d02f18bac)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Sonstiges {#v1.0.1.2-misc}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Beim Sichern der Datenbank werden Meta-Informationen in der ZIP-Datei gespeichert. (c8a95422af)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[v1.0.1.2]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.1.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[v1.0.1.1][v1.0.1.1] (2025-09-21) {#v1.0.1.1}
 | 
				
			||||||
 | 
					---------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Sonstiges {#v1.0.1.1-misc}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Eingabe von Sorten und Qualitätsstufen im Übernahme-Fenster (`DeliveryAdminWindows`) verbessert. (e2de7a1f1c, b27b89f599)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[v1.0.1.1]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.1.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[v1.0.1.0][v1.0.1.0] (2025-09-18) {#v1.0.1.0}
 | 
				
			||||||
 | 
					---------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Neue Funktionen {#v1.0.1.0-features}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Neue Weinsorten gemäß Kürzelliste der Bundeskellereiinspektion hinzugefügt. (a0dcaf7b4f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Sonstiges {#v1.0.1.0-misc}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* HTTP-Anfragen haben jetzt das Feld `User-Agent` gesetzt. (844fc5217a)
 | 
				
			||||||
 | 
					* Auto-Update-Funktion wird auch beim Erlangen von Netzwerkverbindung ausgeführt. (8bc053053c)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[v1.0.1.0]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.1.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[v1.0.0.6][v1.0.0.6] (2025-09-17) {#v1.0.0.6}
 | 
				
			||||||
 | 
					---------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Behobene Fehler {#v1.0.0.6-bugfixes}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Die automatische Erkennung des Wiege-Modus hat im WKW nicht funktioniert. (463769b549)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Sonstiges {#v1.0.0.6-misc}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Tippfehler im Über-Fenster (`AboutWindow`) behoben. (2c383d0c55)
 | 
				
			||||||
 | 
					* `SaveFileDialog` mit Multi-File-Extensions verbessert. (9e02b15ff1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[v1.0.0.6]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.0.6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[v1.0.0.5][v1.0.0.5] (2025-09-15) {#v1.0.0.5}
 | 
				
			||||||
 | 
					---------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Neue Funktionen {#v1.0.0.5-features}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Die Datenbank kann im Haupt-Fenster (`MainWindow`) gesichert und wiederhergestellt werden. (f02598760f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Sonstiges {#v1.0.0.5-misc}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* In der WGM ist eine Auswahl eines Zu/-Abschlags im Übernahme-Fenster (`DeliveryAdminWindow`) nun erforderlich. (a9b5317e79)
 | 
				
			||||||
 | 
					* In der Konfigurationsdatei kann im `[general]` Block `weighing = gross`, `weighing = net`, oder `weighing = box` angegeben werden. (d7012ebfa1)
 | 
				
			||||||
 | 
					* Über-Fenser (`AboutWindow`) hinzugefügt. (3c9b3c2db1)
 | 
				
			||||||
 | 
					* Abhängigkeiten aktualisiert. (44dcc5e19f, 98f8907817)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[v1.0.0.5]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.0.5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[v1.0.0.4][v1.0.0.4] (2025-09-01) {#v1.0.0.4}
 | 
				
			||||||
 | 
					---------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Behobene Fehler {#v1.0.0.4-bugfixes}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Absturz beim Verschicken von einzelnen E-Mails außerhalb des Rundschreiben-Fensters (`MailWindow`) behoben. (07f9a0f522)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Sonstiges {#v1.0.0.4-misc}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Ladezeit des Fehler-Protokoll-Fensters (`LogWindow`) verbessert. (4653a4f129)
 | 
				
			||||||
 | 
					* Im Übernahme-Fenster (`DeliveryAdminWindow`) ist es nun möglich die Qualitätsstufe zu verändern. (104798d4f2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[v1.0.0.4]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.0.4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[v1.0.0.3][v1.0.0.3] (2025-08-11) {#v1.0.0.3}
 | 
				
			||||||
 | 
					---------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Neue Funktionen {#v1.0.0.3-features}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Im Haupt-Fenster (`MainWindow`) ist es nun möglich die gesamte Datenbank zu hochzuladen bzw. herunterzuladen. ([#69][i69])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Sonstiges {#v1.0.0.3-misc}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Die Intigrität von Elwig-Export-Dateien (`.elwig.zip`) und anderen `.zip` Dateien wir nun überprüft. (d3157e4d48)
 | 
				
			||||||
 | 
					* Die Herkunftshierarchie wird nun auch automatisch synchronisiert (benötigt für Stamm-KG bei Mitglied, Ried/KG bei Flächenbindung, und Herkunft bei Lieferung). ([#70][i70], b6c03892b1)
 | 
				
			||||||
 | 
					* Abhängigkeiten aktualisiert. (1f165055c1, 4e2eca295d)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[v1.0.0.3]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.0.3
 | 
				
			||||||
 | 
					[i69]: https://git.necronda.net/winzer/elwig/issues/69
 | 
				
			||||||
 | 
					[i70]: https://git.necronda.net/winzer/elwig/issues/70
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[v1.0.0.2][v1.0.0.2] (2025-08-05) {#v1.0.0.2}
 | 
					[v1.0.0.2][v1.0.0.2] (2025-08-05) {#v1.0.0.2}
 | 
				
			||||||
---------------------------------------------
 | 
					---------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -9,6 +174,8 @@ Changelog
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
* Explizit native SQLite-Bibliothek hinzugefügt. (77c3f388e7)
 | 
					* Explizit native SQLite-Bibliothek hinzugefügt. (77c3f388e7)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[v1.0.0.2]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.0.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -20,6 +187,8 @@ Changelog
 | 
				
			|||||||
* Abhängigkeiten aktualisiert. (466c8a322c)
 | 
					* Abhängigkeiten aktualisiert. (466c8a322c)
 | 
				
			||||||
* Angepasste Fehlermeldung, wenn Importieren fehlschlägt. (ab7c7404e2)
 | 
					* Angepasste Fehlermeldung, wenn Importieren fehlschlägt. (ab7c7404e2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[v1.0.0.1]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.0.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,7 +35,7 @@
 | 
				
			|||||||
        </DataTemplate>
 | 
					        </DataTemplate>
 | 
				
			||||||
        <DataTemplate x:Key="WineVarietyTemplateExpanded">
 | 
					        <DataTemplate x:Key="WineVarietyTemplateExpanded">
 | 
				
			||||||
            <StackPanel Orientation="Horizontal">
 | 
					            <StackPanel Orientation="Horizontal">
 | 
				
			||||||
                <TextBlock Text="{Binding SortId}" Foreground="{Binding Color}" MinWidth="36" Margin="0,0,10,0"/>
 | 
					                <TextBlock Text="{Binding SortIdFormat}" Foreground="{Binding Color}" MinWidth="36" Margin="0,0,10,0"/>
 | 
				
			||||||
                <TextBlock Text="{Binding Name}" Foreground="{Binding Color}"/>
 | 
					                <TextBlock Text="{Binding Name}" Foreground="{Binding Color}"/>
 | 
				
			||||||
                <TextBlock Text="{Binding CommentFormat}" FontSize="10" VerticalAlignment="Bottom" Margin="0,0,0,2"/>
 | 
					                <TextBlock Text="{Binding CommentFormat}" FontSize="10" VerticalAlignment="Bottom" Margin="0,0,0,2"/>
 | 
				
			||||||
            </StackPanel>
 | 
					            </StackPanel>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@ using System.Collections.Generic;
 | 
				
			|||||||
using System.Data;
 | 
					using System.Data;
 | 
				
			||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Net.NetworkInformation;
 | 
				
			||||||
using System.Reflection;
 | 
					using System.Reflection;
 | 
				
			||||||
using System.Text;
 | 
					using System.Text;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
@@ -127,10 +128,11 @@ namespace Elwig {
 | 
				
			|||||||
            if (Config.UpdateAuto && Config.UpdateUrl != null) {
 | 
					            if (Config.UpdateAuto && Config.UpdateUrl != null) {
 | 
				
			||||||
                if (Utils.HasInternetConnectivity()) {
 | 
					                if (Utils.HasInternetConnectivity()) {
 | 
				
			||||||
                    Utils.RunBackground("Auto Updater", async () => {
 | 
					                    Utils.RunBackground("Auto Updater", async () => {
 | 
				
			||||||
                        await Task.Delay(500);
 | 
					                        await Task.Delay(1000);
 | 
				
			||||||
                        await CheckForUpdates();
 | 
					                        await CheckForUpdates();
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged;
 | 
				
			||||||
                _autoUpdateTimer.Tick += new EventHandler(OnAutoUpdateTimer);
 | 
					                _autoUpdateTimer.Tick += new EventHandler(OnAutoUpdateTimer);
 | 
				
			||||||
                _autoUpdateTimer.Start();
 | 
					                _autoUpdateTimer.Start();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -162,6 +164,16 @@ namespace Elwig {
 | 
				
			|||||||
                Shutdown();
 | 
					                Shutdown();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (Config.WeighingMode == null) {
 | 
				
			||||||
 | 
					                if (Client.IsMatzen || Client.IsWolkersdorf) {
 | 
				
			||||||
 | 
					                    Config.WeighingMode = WeighingMode.Net;
 | 
				
			||||||
 | 
					                } else if (Client.IsHaugsdorf || Client.IsSitzendorf) {
 | 
				
			||||||
 | 
					                    Config.WeighingMode = WeighingMode.Box;
 | 
				
			||||||
 | 
					                } else if (Client.IsBaden || Client.IsGrInzersdorf) {
 | 
				
			||||||
 | 
					                    Config.WeighingMode = WeighingMode.Gross;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            base.OnStartup(evt);
 | 
					            base.OnStartup(evt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var window = new MainWindow();
 | 
					            var window = new MainWindow();
 | 
				
			||||||
@@ -218,6 +230,16 @@ namespace Elwig {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void OnNetworkAvailabilityChanged(object? sender, NetworkAvailabilityEventArgs evt) {
 | 
				
			||||||
 | 
					            if (!evt.IsAvailable) return;
 | 
				
			||||||
 | 
					            if (Utils.HasInternetConnectivity()) {
 | 
				
			||||||
 | 
					                Utils.RunBackground("Auto Updater", async () => {
 | 
				
			||||||
 | 
					                    await Task.Delay(2000);
 | 
				
			||||||
 | 
					                    await CheckForUpdates();
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static async Task CheckForUpdates(bool showAlert = false) {
 | 
					        public static async Task CheckForUpdates(bool showAlert = false) {
 | 
				
			||||||
            if (Config.UpdateUrl == null) return;
 | 
					            if (Config.UpdateUrl == null) return;
 | 
				
			||||||
            var latest = await Utils.GetLatestInstallerUrl(Config.UpdateUrl);
 | 
					            var latest = await Utils.GetLatestInstallerUrl(Config.UpdateUrl);
 | 
				
			||||||
@@ -242,7 +264,9 @@ namespace Elwig {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public static async Task ReplaceDatabase(string filename) {
 | 
					        public static async Task ReplaceDatabase(string filename) {
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
                await ElwigData.ImportDatabase(filename);
 | 
					                await Task.Run(async () => {
 | 
				
			||||||
 | 
					                    await Database.Import(filename);
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
                MessageBox.Show("Das Ersetzen war erfolgreich!\n\nBitte starten Sie Elwig neu!", "Datenbank ersetzen", MessageBoxButton.OK, MessageBoxImage.Information);
 | 
					                MessageBox.Show("Das Ersetzen war erfolgreich!\n\nBitte starten Sie Elwig neu!", "Datenbank ersetzen", MessageBoxButton.OK, MessageBoxImage.Information);
 | 
				
			||||||
                ForceShutdown = true;
 | 
					                ForceShutdown = true;
 | 
				
			||||||
                Current.Shutdown();
 | 
					                Current.Shutdown();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@
 | 
				
			|||||||
    <UseWPF>true</UseWPF>
 | 
					    <UseWPF>true</UseWPF>
 | 
				
			||||||
    <PreserveCompilationContext>true</PreserveCompilationContext>
 | 
					    <PreserveCompilationContext>true</PreserveCompilationContext>
 | 
				
			||||||
    <ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
 | 
					    <ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
 | 
				
			||||||
    <Version>1.0.0.2</Version>
 | 
					    <Version>1.0.1.5</Version>
 | 
				
			||||||
    <SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
 | 
					    <SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
 | 
				
			||||||
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
 | 
					    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
 | 
				
			||||||
    <ApplicationManifest>app.manifest</ApplicationManifest>
 | 
					    <ApplicationManifest>app.manifest</ApplicationManifest>
 | 
				
			||||||
@@ -25,19 +25,19 @@
 | 
				
			|||||||
    <PackageReference Include="LinqKit" Version="1.3.8" />
 | 
					    <PackageReference Include="LinqKit" Version="1.3.8" />
 | 
				
			||||||
    <PackageReference Include="MailKit" Version="4.13.0" />
 | 
					    <PackageReference Include="MailKit" Version="4.13.0" />
 | 
				
			||||||
    <PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.36" />
 | 
					    <PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.36" />
 | 
				
			||||||
    <PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.8" />
 | 
					    <PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.9" />
 | 
				
			||||||
    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.8" />
 | 
					    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.9" />
 | 
				
			||||||
    <PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="9.0.8" />
 | 
					    <PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="9.0.9" />
 | 
				
			||||||
    <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3351.48" />
 | 
					    <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3485.44" />
 | 
				
			||||||
    <PackageReference Include="NJsonSchema" Version="11.4.0" />
 | 
					    <PackageReference Include="NJsonSchema" Version="11.4.0" />
 | 
				
			||||||
    <PackageReference Include="PdfiumViewer" Version="2.13.0" />
 | 
					    <PackageReference Include="PdfiumViewer" Version="2.13.0" />
 | 
				
			||||||
    <PackageReference Include="PdfiumViewer.Native.x86_64.no_v8-no_xfa" Version="2018.4.8.256" />
 | 
					    <PackageReference Include="PdfiumViewer.Native.x86_64.no_v8-no_xfa" Version="2018.4.8.256" />
 | 
				
			||||||
    <PackageReference Include="RazorLight" Version="2.3.1" />
 | 
					    <PackageReference Include="RazorLight" Version="2.3.1" />
 | 
				
			||||||
    <PackageReference Include="ScottPlot.WPF" Version="5.0.55" />
 | 
					    <PackageReference Include="ScottPlot.WPF" Version="5.0.56" />
 | 
				
			||||||
    <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.0" />
 | 
					    <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.2" />
 | 
				
			||||||
    <PackageReference Include="System.IO.Hashing" Version="9.0.8" />
 | 
					    <PackageReference Include="System.IO.Hashing" Version="9.0.9" />
 | 
				
			||||||
    <PackageReference Include="System.IO.Ports" Version="9.0.8" />
 | 
					    <PackageReference Include="System.IO.Ports" Version="9.0.9" />
 | 
				
			||||||
    <PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.8" />
 | 
					    <PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.9" />
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</Project>
 | 
					</Project>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -248,13 +248,6 @@ namespace Elwig.Helpers {
 | 
				
			|||||||
            return c + 1;
 | 
					            return c + 1;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task<WineQualLevel> GetWineQualityLevel(double kmw) {
 | 
					 | 
				
			||||||
            return await WineQualityLevels
 | 
					 | 
				
			||||||
                .Where(q => !q.IsPredicate && (q.MinKmw == null || q.MinKmw <= kmw))
 | 
					 | 
				
			||||||
                .OrderBy(q => q.MinKmw)
 | 
					 | 
				
			||||||
                .LastAsync();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public void UpdateDeliveryPartModifiers(DeliveryPart part, IEnumerable<Modifier> oldModifiers, IEnumerable<Modifier> newModifiers) {
 | 
					        public void UpdateDeliveryPartModifiers(DeliveryPart part, IEnumerable<Modifier> oldModifiers, IEnumerable<Modifier> newModifiers) {
 | 
				
			||||||
            foreach (var m in Modifiers.Where(m => m.Year == part.Year)) {
 | 
					            foreach (var m in Modifiers.Where(m => m.Year == part.Year)) {
 | 
				
			||||||
                var mod = new DeliveryPartModifier {
 | 
					                var mod = new DeliveryPartModifier {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@ namespace Elwig.Helpers {
 | 
				
			|||||||
    public static class AppDbUpdater {
 | 
					    public static class AppDbUpdater {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Don't forget to update value in Tests/fetch-resources.bat!
 | 
					        // Don't forget to update value in Tests/fetch-resources.bat!
 | 
				
			||||||
        public static readonly int RequiredSchemaVersion = 32;
 | 
					        public static readonly int RequiredSchemaVersion = 33;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static int VersionOffset = 0;
 | 
					        private static int VersionOffset = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,13 +20,19 @@ namespace Elwig.Helpers.Billing {
 | 
				
			|||||||
            Data = PaymentBillingData.FromJson(PaymentVariant.Data, Utils.GetVaributes(ctx, Year, onlyDelivered: false));
 | 
					            Data = PaymentBillingData.FromJson(PaymentVariant.Data, Utils.GetVaributes(ctx, Year, onlyDelivered: false));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task Calculate(bool? honorGebunden = null, bool? allowAttrsIntoLower = null, bool? avoidUnderDeliveries = null) {
 | 
					        public async Task Calculate(bool strictPrices = true, bool? honorGebunden = null, bool? allowAttrsIntoLower = null, bool? avoidUnderDeliveries = null) {
 | 
				
			||||||
            using var cnx = await AppDbContext.ConnectAsync();
 | 
					            using var cnx = await AppDbContext.ConnectAsync();
 | 
				
			||||||
            using var tx = await cnx.BeginTransactionAsync();
 | 
					            using var tx = await cnx.BeginTransactionAsync();
 | 
				
			||||||
            await CalculateBuckets(honorGebunden, allowAttrsIntoLower, avoidUnderDeliveries, cnx);
 | 
					            await CalculateBuckets(honorGebunden, allowAttrsIntoLower, avoidUnderDeliveries, cnx);
 | 
				
			||||||
            await DeleteInDb(cnx);
 | 
					            await DeleteInDb(cnx);
 | 
				
			||||||
            await SetCalcTime(cnx);
 | 
					            await SetCalcTime(cnx);
 | 
				
			||||||
            await CalculatePrices(cnx);
 | 
					            KeyNotFoundException? exception = null;
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                await CalculatePrices(cnx, strictPrices);
 | 
				
			||||||
 | 
					            } catch (KeyNotFoundException e) {
 | 
				
			||||||
 | 
					                if (strictPrices) throw;
 | 
				
			||||||
 | 
					                exception = e;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            if (Data.ConsiderDelieryModifiers) {
 | 
					            if (Data.ConsiderDelieryModifiers) {
 | 
				
			||||||
                await CalculateDeliveryModifiers(cnx);
 | 
					                await CalculateDeliveryModifiers(cnx);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -34,6 +40,8 @@ namespace Elwig.Helpers.Billing {
 | 
				
			|||||||
                await CalculateMemberModifiers(cnx);
 | 
					                await CalculateMemberModifiers(cnx);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            await tx.CommitAsync();
 | 
					            await tx.CommitAsync();
 | 
				
			||||||
 | 
					            if (exception != null)
 | 
				
			||||||
 | 
					                throw exception;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task Commit() {
 | 
					        public async Task Commit() {
 | 
				
			||||||
@@ -142,7 +150,8 @@ namespace Elwig.Helpers.Billing {
 | 
				
			|||||||
                """);
 | 
					                """);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected async Task CalculatePrices(SqliteConnection cnx) {
 | 
					        protected async Task CalculatePrices(SqliteConnection cnx, bool strict = true) {
 | 
				
			||||||
 | 
					            var invalid = new HashSet<string>();
 | 
				
			||||||
            var parts = new List<(int Year, int DId, int DPNr, int BktNr, string SortId, string? AttrId, string? CultId, string Discr, int Value, double Oe, double Kmw, string QualId, bool AttrAreaCom)>();
 | 
					            var parts = new List<(int Year, int DId, int DPNr, int BktNr, string SortId, string? AttrId, string? CultId, string Discr, int Value, double Oe, double Kmw, string QualId, bool AttrAreaCom)>();
 | 
				
			||||||
            using (var cmd = cnx.CreateCommand()) {
 | 
					            using (var cmd = cnx.CreateCommand()) {
 | 
				
			||||||
                cmd.CommandText = $"""
 | 
					                cmd.CommandText = $"""
 | 
				
			||||||
@@ -172,15 +181,25 @@ namespace Elwig.Helpers.Billing {
 | 
				
			|||||||
                var payAttrId = (part.Discr is "" or "_") ? null : part.Discr;
 | 
					                var payAttrId = (part.Discr is "" or "_") ? null : part.Discr;
 | 
				
			||||||
                var attrId = part.AttrAreaCom ? payAttrId : part.AttrId;
 | 
					                var attrId = part.AttrAreaCom ? payAttrId : part.AttrId;
 | 
				
			||||||
                var geb = !ungeb && (payAttrId == attrId || !part.AttrAreaCom);
 | 
					                var geb = !ungeb && (payAttrId == attrId || !part.AttrAreaCom);
 | 
				
			||||||
                var price = Data.CalculatePrice(part.SortId, attrId, part.CultId, part.QualId, geb, part.Oe, part.Kmw);
 | 
					                decimal price = 0;
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    price = Data.CalculatePrice(part.SortId, attrId, part.CultId, part.QualId, geb, part.Oe, part.Kmw);
 | 
				
			||||||
 | 
					                } catch (KeyNotFoundException e) {
 | 
				
			||||||
 | 
					                    invalid.Add(e.Message.Split('\'')[1]);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                var priceL = PaymentVariant.Season.DecToDb(price);
 | 
					                var priceL = PaymentVariant.Season.DecToDb(price);
 | 
				
			||||||
                inserts.Add((part.Year, part.DId, part.DPNr, part.BktNr, priceL, priceL * part.Value));
 | 
					                inserts.Add((part.Year, part.DId, part.DPNr, part.BktNr, priceL, priceL * part.Value));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var msg = invalid.Count == 0 ? null : "Für folgende Sorten wurde noch keine Preiskurve festgelegt: " + string.Join(", ", invalid);
 | 
				
			||||||
 | 
					            if (msg != null && strict)
 | 
				
			||||||
 | 
					                throw new KeyNotFoundException(msg);
 | 
				
			||||||
            await AppDbContext.ExecuteBatch(cnx, $"""
 | 
					            await AppDbContext.ExecuteBatch(cnx, $"""
 | 
				
			||||||
                INSERT INTO payment_delivery_part_bucket (year, did, dpnr, bktnr, avnr, price, amount)
 | 
					                INSERT INTO payment_delivery_part_bucket (year, did, dpnr, bktnr, avnr, price, amount)
 | 
				
			||||||
                VALUES {string.Join(",\n       ", inserts.Select(i => $"({i.Year}, {i.DId}, {i.DPNr}, {i.BktNr}, {AvNr}, {i.Price}, {i.Amount})"))};
 | 
					                VALUES {string.Join(",\n       ", inserts.Select(i => $"({i.Year}, {i.DId}, {i.DPNr}, {i.BktNr}, {AvNr}, {i.Price}, {i.Amount})"))};
 | 
				
			||||||
                """);
 | 
					                """);
 | 
				
			||||||
 | 
					            if (msg != null)
 | 
				
			||||||
 | 
					                throw new KeyNotFoundException(msg);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected async Task CalculateDeliveryModifiers(SqliteConnection cnx) {
 | 
					        protected async Task CalculateDeliveryModifiers(SqliteConnection cnx) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,14 +19,6 @@ namespace Elwig.Helpers {
 | 
				
			|||||||
        public bool IsSitzendorf => IsWinzerkeller && App.ZwstId == "S";
 | 
					        public bool IsSitzendorf => IsWinzerkeller && App.ZwstId == "S";
 | 
				
			||||||
        public bool IsGrInzersdorf => IsWeinland;
 | 
					        public bool IsGrInzersdorf => IsWeinland;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public bool HasNetWeighing(string? zwstId) => IsMatzen || (IsWinzerkeller && zwstId == "W");
 | 
					 | 
				
			||||||
        public bool HasNetWeighing(Branch? b) => HasNetWeighing(b?.ZwstId);
 | 
					 | 
				
			||||||
        public bool HasNetWeighing() => HasNetWeighing(App.ZwstId);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public bool HasBoxWeighing(string? zwstId) => IsWinzerkeller && (zwstId != "W");
 | 
					 | 
				
			||||||
        public bool HasBoxWeighing(Branch? b) => HasBoxWeighing(b?.ZwstId);
 | 
					 | 
				
			||||||
        public bool HasBoxWeighing() => HasBoxWeighing(App.ZwstId);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public string NameToken;
 | 
					        public string NameToken;
 | 
				
			||||||
        public string NameShort;
 | 
					        public string NameShort;
 | 
				
			||||||
        public string Name;
 | 
					        public string Name;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,4 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
@@ -5,6 +6,8 @@ using Microsoft.Extensions.Configuration;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Elwig.Helpers {
 | 
					namespace Elwig.Helpers {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public enum WeighingMode { Gross, Net, Box }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public record struct ScaleConfig {
 | 
					    public record struct ScaleConfig {
 | 
				
			||||||
        public string Id;
 | 
					        public string Id;
 | 
				
			||||||
        public string? Type;
 | 
					        public string? Type;
 | 
				
			||||||
@@ -41,6 +44,7 @@ namespace Elwig.Helpers {
 | 
				
			|||||||
        public string DatabaseFile = App.DataPath + "database.sqlite3";
 | 
					        public string DatabaseFile = App.DataPath + "database.sqlite3";
 | 
				
			||||||
        public string? DatabaseLog = null;
 | 
					        public string? DatabaseLog = null;
 | 
				
			||||||
        public string? Branch = null;
 | 
					        public string? Branch = null;
 | 
				
			||||||
 | 
					        public WeighingMode? WeighingMode;
 | 
				
			||||||
        public string? UpdateUrl = null;
 | 
					        public string? UpdateUrl = null;
 | 
				
			||||||
        public bool UpdateAuto = false;
 | 
					        public bool UpdateAuto = false;
 | 
				
			||||||
        public string? SyncUrl = null;
 | 
					        public string? SyncUrl = null;
 | 
				
			||||||
@@ -74,6 +78,8 @@ namespace Elwig.Helpers {
 | 
				
			|||||||
            DatabaseLog = log != null ? Path.Combine(Path.GetDirectoryName(FileName) ?? App.DataPath, log) : null;
 | 
					            DatabaseLog = log != null ? Path.Combine(Path.GetDirectoryName(FileName) ?? App.DataPath, log) : null;
 | 
				
			||||||
            Branch = config["general:branch"];
 | 
					            Branch = config["general:branch"];
 | 
				
			||||||
            Debug = TrueValues.Contains(config["general:debug"]?.ToLower());
 | 
					            Debug = TrueValues.Contains(config["general:debug"]?.ToLower());
 | 
				
			||||||
 | 
					            var weighing = config["general:weighing"];
 | 
				
			||||||
 | 
					            WeighingMode = weighing != null && Enum.TryParse<WeighingMode>(weighing, true, out var w) ? w : null;
 | 
				
			||||||
            UpdateUrl = config["update:url"];
 | 
					            UpdateUrl = config["update:url"];
 | 
				
			||||||
            UpdateAuto = TrueValues.Contains(config["update:auto"]?.ToLower());
 | 
					            UpdateAuto = TrueValues.Contains(config["update:auto"]?.ToLower());
 | 
				
			||||||
            SyncUrl = config["sync:url"];
 | 
					            SyncUrl = config["sync:url"];
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										243
									
								
								Elwig/Helpers/Export/Database.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								Elwig/Helpers/Export/Database.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,243 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
 | 
					using System.IO.Compression;
 | 
				
			||||||
 | 
					using System.Text.Json.Nodes;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Elwig.Helpers.Export {
 | 
				
			||||||
 | 
					    public static class Database {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static async Task<(long? ApplicationId, string? UserVersion, long? SchemaVersion, long FileSize)> GetMeta() {
 | 
				
			||||||
 | 
					            long size = new FileInfo(App.Config.DatabaseFile).Length;
 | 
				
			||||||
 | 
					            using var cnx = await AppDbContext.ConnectAsync();
 | 
				
			||||||
 | 
					            var applId = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA application_id");
 | 
				
			||||||
 | 
					            var userVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA user_version");
 | 
				
			||||||
 | 
					            var schemaVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA schema_version");
 | 
				
			||||||
 | 
					            return (applId, userVers != null ? $"{userVers >> 24}.{(userVers >> 16) & 0xFF}.{(userVers >> 8) & 0xFF}.{userVers & 0xFF}" : null, schemaVers, size);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static async Task ExportSqlite(string filename, bool zipFile) {
 | 
				
			||||||
 | 
					            if (zipFile) {
 | 
				
			||||||
 | 
					                File.Delete(filename);
 | 
				
			||||||
 | 
					                using var zip = ZipFile.Open(filename, ZipArchiveMode.Create);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                var version = zip.CreateEntry("version", CompressionLevel.NoCompression);
 | 
				
			||||||
 | 
					                using (var writer = new StreamWriter(version.Open(), Utils.UTF8)) {
 | 
				
			||||||
 | 
					                    await writer.WriteAsync("elwig:1");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                var (applId, userVers, schemaVers, size) = await GetMeta();
 | 
				
			||||||
 | 
					                var meta = zip.CreateEntry("meta.json", CompressionLevel.NoCompression);
 | 
				
			||||||
 | 
					                using (var writer = new StreamWriter(meta.Open(), Utils.UTF8)) {
 | 
				
			||||||
 | 
					                    var obj = new JsonObject {
 | 
				
			||||||
 | 
					                        ["timestamp"] = $"{DateTime.UtcNow:yyyy-MM-ddTHH:mm:ssZ}",
 | 
				
			||||||
 | 
					                        ["zwstid"] = App.ZwstId,
 | 
				
			||||||
 | 
					                        ["device"] = Environment.MachineName,
 | 
				
			||||||
 | 
					                        ["database"] = new JsonObject {
 | 
				
			||||||
 | 
					                            ["application_id"] = applId,
 | 
				
			||||||
 | 
					                            ["user_version"] = userVers,
 | 
				
			||||||
 | 
					                            ["schema_version"] = schemaVers,
 | 
				
			||||||
 | 
					                            ["file_size"] = size,
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					                    await writer.WriteAsync(obj.ToJsonString(Utils.JsonOpts));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                var db = zip.CreateEntryFromFile(App.Config.DatabaseFile, "database.sqlite3", CompressionLevel.SmallestSize);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                File.Copy(App.Config.DatabaseFile, filename, true);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static async Task ExportSql(string filename, bool zipFile) {
 | 
				
			||||||
 | 
					            if (zipFile) {
 | 
				
			||||||
 | 
					                File.Delete(filename);
 | 
				
			||||||
 | 
					                using var zip = ZipFile.Open(filename, ZipArchiveMode.Create);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                var version = zip.CreateEntry("version", CompressionLevel.NoCompression);
 | 
				
			||||||
 | 
					                using (var writer = new StreamWriter(version.Open(), Utils.UTF8)) {
 | 
				
			||||||
 | 
					                    await writer.WriteAsync("elwig:1");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                var (applId, userVers, schemaVers, size) = await GetMeta();
 | 
				
			||||||
 | 
					                var meta = zip.CreateEntry("meta.json", CompressionLevel.NoCompression);
 | 
				
			||||||
 | 
					                using (var writer = new StreamWriter(meta.Open(), Utils.UTF8)) {
 | 
				
			||||||
 | 
					                    var obj = new JsonObject {
 | 
				
			||||||
 | 
					                        ["timestamp"] = $"{DateTime.UtcNow:yyyy-MM-ddTHH:mm:ssZ}",
 | 
				
			||||||
 | 
					                        ["zwstid"] = App.ZwstId,
 | 
				
			||||||
 | 
					                        ["device"] = Environment.MachineName,
 | 
				
			||||||
 | 
					                        ["database"] = new JsonObject {
 | 
				
			||||||
 | 
					                            ["application_id"] = applId,
 | 
				
			||||||
 | 
					                            ["user_version"] = userVers,
 | 
				
			||||||
 | 
					                            ["schema_version"] = schemaVers,
 | 
				
			||||||
 | 
					                            ["file_size"] = size,
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					                    await writer.WriteAsync(obj.ToJsonString(Utils.JsonOpts));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                var sql = zip.CreateEntry("database.sql", CompressionLevel.SmallestSize);
 | 
				
			||||||
 | 
					                using (var writer = new StreamWriter(sql.Open(), Utils.UTF8)) {
 | 
				
			||||||
 | 
					                    await ExportSql(writer);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                using var stream = File.OpenWrite(filename);
 | 
				
			||||||
 | 
					                using var writer = new StreamWriter(stream, Utils.UTF8);
 | 
				
			||||||
 | 
					                await ExportSql(writer);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static async Task ExportSql(StreamWriter writer) {
 | 
				
			||||||
 | 
					            using var cnx = await AppDbContext.ConnectAsync();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var tables = new List<(string Name, string Sql)>();
 | 
				
			||||||
 | 
					            using (var cmd = cnx.CreateCommand()) {
 | 
				
			||||||
 | 
					                cmd.CommandText = "SELECT name, sql FROM sqlite_schema WHERE type = 'table'";
 | 
				
			||||||
 | 
					                using var reader = await cmd.ExecuteReaderAsync();
 | 
				
			||||||
 | 
					                while (await reader.ReadAsync()) {
 | 
				
			||||||
 | 
					                    tables.Add((reader.GetString(0), reader.GetString(1)));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var applId = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA application_id") ?? 0;
 | 
				
			||||||
 | 
					            var userVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA user_version") ?? 0;
 | 
				
			||||||
 | 
					            var schemaVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA schema_version") ?? 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await writer.WriteLineAsync($"-- Elwig database dump, {DateTime.Now:yyyy-MM-dd, HH:mm:ss}");
 | 
				
			||||||
 | 
					            await writer.WriteLineAsync($"-- {Environment.MachineName}, Zwst. {App.BranchName}, {App.Client.Name}");
 | 
				
			||||||
 | 
					            await writer.WriteLineAsync("BEGIN TRANSACTION;");
 | 
				
			||||||
 | 
					            await writer.WriteLineAsync("PRAGMA foreign_keys=OFF;");
 | 
				
			||||||
 | 
					            await writer.WriteLineAsync($"PRAGMA application_id=0x{applId:X8};");
 | 
				
			||||||
 | 
					            await writer.WriteLineAsync($"PRAGMA user_version=0x{userVers:X8};");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach (var t in tables) {
 | 
				
			||||||
 | 
					                await writer.WriteAsync(t.Sql);
 | 
				
			||||||
 | 
					                await writer.WriteLineAsync(";");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                var columnNames = new List<string>();
 | 
				
			||||||
 | 
					                using (var cmd = cnx.CreateCommand()) {
 | 
				
			||||||
 | 
					                    cmd.CommandText = $"PRAGMA table_info({t.Name})";
 | 
				
			||||||
 | 
					                    using var reader = await cmd.ExecuteReaderAsync();
 | 
				
			||||||
 | 
					                    while (await reader.ReadAsync()) {
 | 
				
			||||||
 | 
					                        columnNames.Add(reader.GetString(1));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                using (var cmd = cnx.CreateCommand()) {
 | 
				
			||||||
 | 
					                    cmd.CommandText = $"SELECT {string.Join(',', columnNames)} FROM {t.Name}";
 | 
				
			||||||
 | 
					                    using var reader = await cmd.ExecuteReaderAsync();
 | 
				
			||||||
 | 
					                    var columns = await reader.GetColumnSchemaAsync();
 | 
				
			||||||
 | 
					                    var values = new object[reader.FieldCount];
 | 
				
			||||||
 | 
					                    while (await reader.ReadAsync()) {
 | 
				
			||||||
 | 
					                        await writer.WriteAsync($"INSERT INTO {t.Name} VALUES (");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        reader.GetValues(values);
 | 
				
			||||||
 | 
					                        for (int i = 0; i < columns.Count; i++) {
 | 
				
			||||||
 | 
					                            var c = columns[i];
 | 
				
			||||||
 | 
					                            var v = values[i];
 | 
				
			||||||
 | 
					                            if (i > 0) await writer.WriteAsync(",");
 | 
				
			||||||
 | 
					                            if (v == null || v is DBNull) {
 | 
				
			||||||
 | 
					                                await writer.WriteAsync("NULL");
 | 
				
			||||||
 | 
					                            } else if (c.DataTypeName == "TEXT") {
 | 
				
			||||||
 | 
					                                await writer.WriteAsync($"'{v.ToString()?.Replace("'", "''")}'");
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                await writer.WriteAsync(v.ToString()?.Replace(',', '.'));
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        await writer.WriteLineAsync(");");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            using (var cmd = cnx.CreateCommand()) {
 | 
				
			||||||
 | 
					                cmd.CommandText = "SELECT sql FROM sqlite_schema WHERE type != 'table' AND sql IS NOT NULL";
 | 
				
			||||||
 | 
					                using var reader = await cmd.ExecuteReaderAsync();
 | 
				
			||||||
 | 
					                while (await reader.ReadAsync()) {
 | 
				
			||||||
 | 
					                    await writer.WriteAsync(reader.GetString(0));
 | 
				
			||||||
 | 
					                    await writer.WriteLineAsync(";");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await writer.WriteLineAsync($"PRAGMA schema_version={schemaVers};");
 | 
				
			||||||
 | 
					            await writer.WriteLineAsync("PRAGMA foreign_keys=ON;");
 | 
				
			||||||
 | 
					            await writer.WriteLineAsync("COMMIT;");
 | 
				
			||||||
 | 
					            await writer.WriteLineAsync("VACUUM;");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static async Task Import(string filename) {
 | 
				
			||||||
 | 
					            if (filename.EndsWith(".sql")) {
 | 
				
			||||||
 | 
					                await ImportSql(filename, false);
 | 
				
			||||||
 | 
					            } else if (filename.EndsWith(".sql.zip")) {
 | 
				
			||||||
 | 
					                await ImportSql(filename, true);
 | 
				
			||||||
 | 
					            } else if (filename.EndsWith(".sqlite3")) {
 | 
				
			||||||
 | 
					                await ImportSqlite(filename, false);
 | 
				
			||||||
 | 
					            } else if (filename.EndsWith(".sqlite3.zip")) {
 | 
				
			||||||
 | 
					                await ImportSqlite(filename, true);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                throw new ArgumentException($"Unknown file extension for importing: '{filename}'");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static async Task ImportSql(string filename, bool zipFile = false) {
 | 
				
			||||||
 | 
					            if (zipFile) {
 | 
				
			||||||
 | 
					                using var zip = ZipFile.Open(filename, ZipArchiveMode.Read);
 | 
				
			||||||
 | 
					                await zip.CheckIntegrity();
 | 
				
			||||||
 | 
					                foreach (var entry in zip.Entries) {
 | 
				
			||||||
 | 
					                    if (entry.Name.EndsWith(".sql")) {
 | 
				
			||||||
 | 
					                        using var stream = entry.Open();
 | 
				
			||||||
 | 
					                        using var reader = new StreamReader(stream, Utils.UTF8);
 | 
				
			||||||
 | 
					                        await ImportSql(reader);
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                throw new FileFormatException("ZIP archive has to contain at least one .sql file");
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                using var stream = File.Open(filename, FileMode.Open);
 | 
				
			||||||
 | 
					                using var reader = new StreamReader(stream, Utils.UTF8);
 | 
				
			||||||
 | 
					                await ImportSql(reader);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static async Task ImportSqlite(string filename, bool zipFile = false) {
 | 
				
			||||||
 | 
					            if (zipFile) {
 | 
				
			||||||
 | 
					                var newName = Path.ChangeExtension(App.Config.DatabaseFile, ".new.sqlite3");
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    using var zip = ZipFile.Open(filename, ZipArchiveMode.Read);
 | 
				
			||||||
 | 
					                    await zip.CheckIntegrity();
 | 
				
			||||||
 | 
					                    foreach (var entry in zip.Entries) {
 | 
				
			||||||
 | 
					                        if (entry.Name.EndsWith(".sqlite3")) {
 | 
				
			||||||
 | 
					                            entry.ExtractToFile(newName);
 | 
				
			||||||
 | 
					                            await ImportSqlite(newName);
 | 
				
			||||||
 | 
					                            return;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    throw new FileFormatException("ZIP archive has to contain at least one .sqlite3 file");
 | 
				
			||||||
 | 
					                } finally {
 | 
				
			||||||
 | 
					                    if (File.Exists(newName)) File.Delete(newName);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var oldName = Path.ChangeExtension(App.Config.DatabaseFile, ".old.sqlite3");
 | 
				
			||||||
 | 
					            File.Move(App.Config.DatabaseFile, oldName, true);
 | 
				
			||||||
 | 
					            File.Move(filename, App.Config.DatabaseFile, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            using var cnx = await AppDbContext.ConnectAsync();
 | 
				
			||||||
 | 
					            await AppDbContext.ExecuteBatch(cnx, "VACUUM");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static async Task ImportSql(StreamReader reader) {
 | 
				
			||||||
 | 
					            var newName = Path.ChangeExtension(App.Config.DatabaseFile, ".new.sqlite3");
 | 
				
			||||||
 | 
					            File.Delete(newName);
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                using (var cnx = await AppDbContext.ConnectAsync($"Data Source=\"{newName}\"; Mode=ReadWriteCreate; Foreign Keys=False; Cache=Default; Pooling=False")) {
 | 
				
			||||||
 | 
					                    await AppDbContext.ExecuteBatch(cnx, await reader.ReadToEndAsync());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                await ImportSqlite(newName);
 | 
				
			||||||
 | 
					            } finally {
 | 
				
			||||||
 | 
					                if (File.Exists(newName)) File.Delete(newName);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -6,7 +6,6 @@ using System.Globalization;
 | 
				
			|||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
using System.IO.Compression;
 | 
					using System.IO.Compression;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Text.Json;
 | 
					 | 
				
			||||||
using System.Text.Json.Nodes;
 | 
					using System.Text.Json.Nodes;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using System.Windows;
 | 
					using System.Windows;
 | 
				
			||||||
@@ -18,8 +17,6 @@ namespace Elwig.Helpers.Export {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public static readonly string ImportedTxt = Path.Combine(App.DataPath, "imported.txt");
 | 
					        public static readonly string ImportedTxt = Path.Combine(App.DataPath, "imported.txt");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static readonly JsonSerializerOptions JsonOpts = new() { Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static async Task<string[]> GetImportedFiles() {
 | 
					        public static async Task<string[]> GetImportedFiles() {
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
                return await File.ReadAllLinesAsync(ImportedTxt, Utils.UTF8);
 | 
					                return await File.ReadAllLinesAsync(ImportedTxt, Utils.UTF8);
 | 
				
			||||||
@@ -41,6 +38,7 @@ namespace Elwig.Helpers.Export {
 | 
				
			|||||||
                Dictionary<string, int> currentLsNrs;
 | 
					                Dictionary<string, int> currentLsNrs;
 | 
				
			||||||
                Dictionary<int, List<WbRd>> currentWbRde;
 | 
					                Dictionary<int, List<WbRd>> currentWbRde;
 | 
				
			||||||
                Dictionary<int, AT_Kg> kgs;
 | 
					                Dictionary<int, AT_Kg> kgs;
 | 
				
			||||||
 | 
					                List<WbGl> currentWbGls;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                using (var ctx = new AppDbContext()) {
 | 
					                using (var ctx = new AppDbContext()) {
 | 
				
			||||||
                    branches = await ctx.Branches.ToDictionaryAsync(b => b.ZwstId);
 | 
					                    branches = await ctx.Branches.ToDictionaryAsync(b => b.ZwstId);
 | 
				
			||||||
@@ -52,6 +50,7 @@ namespace Elwig.Helpers.Export {
 | 
				
			|||||||
                    currentWbRde = await ctx.WbRde
 | 
					                    currentWbRde = await ctx.WbRde
 | 
				
			||||||
                        .GroupBy(r => r.KgNr)
 | 
					                        .GroupBy(r => r.KgNr)
 | 
				
			||||||
                        .ToDictionaryAsync(g => g.Key, g => g.ToList());
 | 
					                        .ToDictionaryAsync(g => g.Key, g => g.ToList());
 | 
				
			||||||
 | 
					                    currentWbGls = await ctx.WbGls.ToListAsync();
 | 
				
			||||||
                    kgs = await ctx.Katastralgemeinden.Include(k => k.WbKg).ToDictionaryAsync(k => k.KgNr);
 | 
					                    kgs = await ctx.Katastralgemeinden.Include(k => k.WbKg).ToDictionaryAsync(k => k.KgNr);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -62,6 +61,8 @@ namespace Elwig.Helpers.Export {
 | 
				
			|||||||
                    List<MemberEmailAddr> EmailAddresses,
 | 
					                    List<MemberEmailAddr> EmailAddresses,
 | 
				
			||||||
                    List<AreaCom> AreaCommitments,
 | 
					                    List<AreaCom> AreaCommitments,
 | 
				
			||||||
                    List<WbRd> Riede,
 | 
					                    List<WbRd> Riede,
 | 
				
			||||||
 | 
					                    List<WbKg> WbKgs,
 | 
				
			||||||
 | 
					                    List<WbGl> WbGls,
 | 
				
			||||||
                    List<Delivery> Deliveries,
 | 
					                    List<Delivery> Deliveries,
 | 
				
			||||||
                    List<DeliveryPart> DeliveryParts,
 | 
					                    List<DeliveryPart> DeliveryParts,
 | 
				
			||||||
                    List<DeliveryPartModifier> Modifiers,
 | 
					                    List<DeliveryPartModifier> Modifiers,
 | 
				
			||||||
@@ -73,82 +74,117 @@ namespace Elwig.Helpers.Export {
 | 
				
			|||||||
                    int? DeliveryNum, string? DeliveryFilters)>();
 | 
					                    int? DeliveryNum, string? DeliveryFilters)>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                foreach (var filename in filenames) {
 | 
					                foreach (var filename in filenames) {
 | 
				
			||||||
                    // TODO read encrypted files
 | 
					                    try {
 | 
				
			||||||
                    using var zip = ZipFile.Open(filename, ZipArchiveMode.Read);
 | 
					                        data.Add(new([], [], [], [], [], [], [], new([], [], [], [], new() {
 | 
				
			||||||
                    await zip.CheckIntegrity();
 | 
					                            ["member"] = [],
 | 
				
			||||||
 | 
					                            ["area_commitment"] = [],
 | 
				
			||||||
 | 
					                            ["delivery"] = [],
 | 
				
			||||||
 | 
					                        })));
 | 
				
			||||||
 | 
					                        var r = data[^1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var version = zip.GetEntry("version");
 | 
					                        // TODO read encrypted files
 | 
				
			||||||
                    using (var reader = new StreamReader(version!.Open(), Utils.UTF8)) {
 | 
					                        using var zip = ZipFile.Open(filename, ZipArchiveMode.Read);
 | 
				
			||||||
                        if (await reader.ReadToEndAsync() != "elwig:1")
 | 
					                        await zip.CheckIntegrity();
 | 
				
			||||||
                            throw new FileFormatException($"Ungültige Export-Datei ({filename})");
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var metaJson = zip.GetEntry("meta.json");
 | 
					                        var version = zip.GetEntry("version");
 | 
				
			||||||
                    var meta = await JsonNode.ParseAsync(metaJson!.Open());
 | 
					                        using (var reader = new StreamReader(version!.Open(), Utils.UTF8)) {
 | 
				
			||||||
                    var memberCount = meta!["members"]?["count"]?.AsValue().GetValue<int>();
 | 
					                            if (await reader.ReadToEndAsync() != "elwig:1")
 | 
				
			||||||
                    var memberFilters = meta!["members"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
 | 
					                                throw new FileFormatException($"Ungültige Elwig-Export-Datei ({filename})");
 | 
				
			||||||
                    var areaComCount = meta!["area_commitments"]?["count"]?.AsValue().GetValue<int>();
 | 
					 | 
				
			||||||
                    var areaComFilters = meta!["area_commitments"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
 | 
					 | 
				
			||||||
                    var deliveryCount = meta!["deliveries"]?["count"]?.AsValue().GetValue<int>();
 | 
					 | 
				
			||||||
                    var deliveryFilters = meta!["deliveries"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
 | 
					 | 
				
			||||||
                    metaData.Add((Path.GetFileName(filename),
 | 
					 | 
				
			||||||
                        meta["zwstid"]!.AsValue().GetValue<string>(), meta["device"]!.AsValue().GetValue<string>(),
 | 
					 | 
				
			||||||
                        memberCount, memberFilters != null ? string.Join(" / ", memberFilters) : null,
 | 
					 | 
				
			||||||
                        areaComCount, areaComFilters != null ? string.Join(" / ", areaComFilters) : null,
 | 
					 | 
				
			||||||
                        deliveryCount, deliveryFilters != null ? string.Join(" / ", deliveryFilters) : null));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    data.Add(new([], [], [], [], [], [], [], new([], [], new() {
 | 
					 | 
				
			||||||
                        ["member"] = [],
 | 
					 | 
				
			||||||
                        ["area_commitment"] = [],
 | 
					 | 
				
			||||||
                        ["delivery"] = [],
 | 
					 | 
				
			||||||
                    })));
 | 
					 | 
				
			||||||
                    var r = data[^1];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    var membersJson = zip.GetEntry("members.json");
 | 
					 | 
				
			||||||
                    if (membersJson != null) {
 | 
					 | 
				
			||||||
                        using var reader = new StreamReader(membersJson.Open(), Utils.UTF8);
 | 
					 | 
				
			||||||
                        string? line;
 | 
					 | 
				
			||||||
                        while ((line = await reader.ReadLineAsync()) != null) {
 | 
					 | 
				
			||||||
                            var obj = JsonNode.Parse(line)!.AsObject();
 | 
					 | 
				
			||||||
                            var (m, b, telNrs, emailAddrs, timestamps) = obj.ToMember(kgs);
 | 
					 | 
				
			||||||
                            r.Members.Add(m);
 | 
					 | 
				
			||||||
                            if (b != null) r.BillingAddresses.Add(b);
 | 
					 | 
				
			||||||
                            r.TelephoneNumbers.AddRange(telNrs);
 | 
					 | 
				
			||||||
                            r.EmailAddresses.AddRange(emailAddrs);
 | 
					 | 
				
			||||||
                            if (timestamps.HasValue)
 | 
					 | 
				
			||||||
                                r.Timestamps["member"].Add((m.MgNr, 0, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt));
 | 
					 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var areaComsJson = zip.GetEntry("area_commitments.json");
 | 
					                        var metaJson = zip.GetEntry("meta.json");
 | 
				
			||||||
                    if (areaComsJson != null) {
 | 
					                        var meta = await JsonNode.ParseAsync(metaJson!.Open());
 | 
				
			||||||
                        using var reader = new StreamReader(areaComsJson.Open(), Utils.UTF8);
 | 
					                        var memberCount = meta!["members"]?["count"]?.AsValue().GetValue<int>();
 | 
				
			||||||
                        string? line;
 | 
					                        var memberFilters = meta!["members"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
 | 
				
			||||||
                        while ((line = await reader.ReadLineAsync()) != null) {
 | 
					                        var areaComCount = meta!["area_commitments"]?["count"]?.AsValue().GetValue<int>();
 | 
				
			||||||
                            var obj = JsonNode.Parse(line)!.AsObject();
 | 
					                        var areaComFilters = meta!["area_commitments"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
 | 
				
			||||||
                            var (areaCom, wbrd, timestamps) = obj.ToAreaCom(kgs, currentWbRde);
 | 
					                        var deliveryCount = meta!["deliveries"]?["count"]?.AsValue().GetValue<int>();
 | 
				
			||||||
                            r.AreaCommitments.Add(areaCom);
 | 
					                        var deliveryFilters = meta!["deliveries"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
 | 
				
			||||||
                            if (wbrd != null) {
 | 
					                        metaData.Add((Path.GetFileName(filename),
 | 
				
			||||||
                                currentWbRde[wbrd.KgNr].Add(wbrd);
 | 
					                            meta["zwstid"]!.AsValue().GetValue<string>(), meta["device"]!.AsValue().GetValue<string>(),
 | 
				
			||||||
                                r.Riede.Add(wbrd);
 | 
					                            memberCount, memberFilters != null ? string.Join(" / ", memberFilters) : null,
 | 
				
			||||||
 | 
					                            areaComCount, areaComFilters != null ? string.Join(" / ", areaComFilters) : null,
 | 
				
			||||||
 | 
					                            deliveryCount, deliveryFilters != null ? string.Join(" / ", deliveryFilters) : null));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        var wbKgsJson = zip.GetEntry("wb_kgs.json");
 | 
				
			||||||
 | 
					                        if (wbKgsJson != null) {
 | 
				
			||||||
 | 
					                            using var reader = new StreamReader(wbKgsJson.Open(), Utils.UTF8);
 | 
				
			||||||
 | 
					                            string? line;
 | 
				
			||||||
 | 
					                            while ((line = await reader.ReadLineAsync()) != null) {
 | 
				
			||||||
 | 
					                                var obj = JsonNode.Parse(line)!.AsObject();
 | 
				
			||||||
 | 
					                                var (k, g) = obj.ToWbKg(currentWbGls);
 | 
				
			||||||
 | 
					                                r.WbKgs.Add(k);
 | 
				
			||||||
 | 
					                                if (g != null) {
 | 
				
			||||||
 | 
					                                    currentWbGls[g.GlNr] = g;
 | 
				
			||||||
 | 
					                                    r.WbGls.Add(g);
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            if (timestamps.HasValue)
 | 
					 | 
				
			||||||
                                r.Timestamps["area_commitment"].Add((areaCom.FbNr, 0, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt));
 | 
					 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var deliveriesJson = zip.GetEntry("deliveries.json");
 | 
					                        var membersJson = zip.GetEntry("members.json");
 | 
				
			||||||
                    if (deliveriesJson != null) {
 | 
					                        if (membersJson != null) {
 | 
				
			||||||
                        using var reader = new StreamReader(deliveriesJson.Open(), Utils.UTF8);
 | 
					                            using var reader = new StreamReader(membersJson.Open(), Utils.UTF8);
 | 
				
			||||||
                        string? line;
 | 
					                            string? line;
 | 
				
			||||||
                        while ((line = await reader.ReadLineAsync()) != null) {
 | 
					                            while ((line = await reader.ReadLineAsync()) != null) {
 | 
				
			||||||
                            var obj = JsonNode.Parse(line)!.AsObject();
 | 
					                                var obj = JsonNode.Parse(line)!.AsObject();
 | 
				
			||||||
                            var (d, parts, mods, timestamps) = obj.ToDelivery(currentLsNrs, currentDids);
 | 
					                                var (m, b, telNrs, emailAddrs, timestamps) = obj.ToMember(kgs);
 | 
				
			||||||
                            r.Deliveries.Add(d);
 | 
					                                r.Members.Add(m);
 | 
				
			||||||
                            r.DeliveryParts.AddRange(parts);
 | 
					                                if (b != null) r.BillingAddresses.Add(b);
 | 
				
			||||||
                            r.Modifiers.AddRange(mods);
 | 
					                                r.TelephoneNumbers.AddRange(telNrs);
 | 
				
			||||||
                            if (timestamps.HasValue)
 | 
					                                r.EmailAddresses.AddRange(emailAddrs);
 | 
				
			||||||
                                r.Timestamps["delivery"].Add((d.Year, d.DId, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt));
 | 
					                                if (timestamps.HasValue)
 | 
				
			||||||
 | 
					                                    r.Timestamps["member"].Add((m.MgNr, 0, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt));
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        var areaComsJson = zip.GetEntry("area_commitments.json");
 | 
				
			||||||
 | 
					                        if (areaComsJson != null) {
 | 
				
			||||||
 | 
					                            using var reader = new StreamReader(areaComsJson.Open(), Utils.UTF8);
 | 
				
			||||||
 | 
					                            string? line;
 | 
				
			||||||
 | 
					                            while ((line = await reader.ReadLineAsync()) != null) {
 | 
				
			||||||
 | 
					                                var obj = JsonNode.Parse(line)!.AsObject();
 | 
				
			||||||
 | 
					                                var (areaCom, wbrd, timestamps) = obj.ToAreaCom(currentWbRde);
 | 
				
			||||||
 | 
					                                r.AreaCommitments.Add(areaCom);
 | 
				
			||||||
 | 
					                                if (wbrd != null) {
 | 
				
			||||||
 | 
					                                    r.Riede.Add(wbrd);
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                if (timestamps.HasValue)
 | 
				
			||||||
 | 
					                                    r.Timestamps["area_commitment"].Add((areaCom.FbNr, 0, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt));
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        var deliveriesJson = zip.GetEntry("deliveries.json");
 | 
				
			||||||
 | 
					                        if (deliveriesJson != null) {
 | 
				
			||||||
 | 
					                            using var reader = new StreamReader(deliveriesJson.Open(), Utils.UTF8);
 | 
				
			||||||
 | 
					                            string? line;
 | 
				
			||||||
 | 
					                            while ((line = await reader.ReadLineAsync()) != null) {
 | 
				
			||||||
 | 
					                                var obj = JsonNode.Parse(line)!.AsObject();
 | 
				
			||||||
 | 
					                                var (d, parts, mods, rde, timestamps) = obj.ToDelivery(currentLsNrs, currentDids, kgs, currentWbRde);
 | 
				
			||||||
 | 
					                                r.Deliveries.Add(d);
 | 
				
			||||||
 | 
					                                r.DeliveryParts.AddRange(parts);
 | 
				
			||||||
 | 
					                                r.Modifiers.AddRange(mods);
 | 
				
			||||||
 | 
					                                r.Riede.AddRange(rde);
 | 
				
			||||||
 | 
					                                if (timestamps.HasValue)
 | 
				
			||||||
 | 
					                                    r.Timestamps["delivery"].Add((d.Year, d.DId, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt));
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } catch (Exception exc) when (
 | 
				
			||||||
 | 
					                            exc is InvalidDataException ||
 | 
				
			||||||
 | 
					                            exc is FileFormatException ||
 | 
				
			||||||
 | 
					                            exc is FileNotFoundException ||
 | 
				
			||||||
 | 
					                            exc is IOException) {
 | 
				
			||||||
 | 
					                        data.RemoveAt(data.Count - 1);
 | 
				
			||||||
 | 
					                        var str = $"Die Elwig-Export-Datei '{Path.GetFileName(filename)}' konnte nicht verarbeitet werden und wird übersprungen.\n\n" + exc.Message;
 | 
				
			||||||
 | 
					                        if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
 | 
				
			||||||
 | 
					                        MessageBox.Show(str, "Fehler beim Importieren", MessageBoxButton.OK, MessageBoxImage.Error);
 | 
				
			||||||
 | 
					                        await AddImportedFiles(Path.GetFileName(filename));
 | 
				
			||||||
 | 
					                    } catch (Exception exc) {
 | 
				
			||||||
 | 
					                        data.RemoveAt(data.Count - 1);
 | 
				
			||||||
 | 
					                        var str = $"Die Elwig-Export-Datei '{Path.GetFileName(filename)}' konnte nicht verarbeitet werden. Soll sie in Zukunft übersprungen werden?\n\n" + exc.Message;
 | 
				
			||||||
 | 
					                        if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
 | 
				
			||||||
 | 
					                        var r = MessageBox.Show(str, "Fehler beim Importieren", MessageBoxButton.YesNo, MessageBoxImage.Error, MessageBoxResult.No);
 | 
				
			||||||
 | 
					                        if (r == MessageBoxResult.Yes) {
 | 
				
			||||||
 | 
					                            await AddImportedFiles(Path.GetFileName(filename));
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -157,12 +193,18 @@ namespace Elwig.Helpers.Export {
 | 
				
			|||||||
                var importedAreaComs = new List<(string FileName, string ZwstId, string Device, int Imported, int NotImported, string Filters)>();
 | 
					                var importedAreaComs = new List<(string FileName, string ZwstId, string Device, int Imported, int NotImported, string Filters)>();
 | 
				
			||||||
                var importedDeliveries = new List<(string FileName, string ZwstId, string Device, int New, int Overwritten, int NotImported, string Filters)>();
 | 
					                var importedDeliveries = new List<(string FileName, string ZwstId, string Device, int New, int Overwritten, int NotImported, string Filters)>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                foreach (var ((members, billingAddresses, telephoneNumbers, emailAddresses, areaCommitments, riede, deliveries, deliveryParts, modifiers, timestamps), meta) in data.Zip(metaData)) {
 | 
					                foreach (var ((members, billingAddresses, telephoneNumbers, emailAddresses, areaCommitments, riede, wbKgs, wbGls, deliveries, deliveryParts, modifiers, timestamps), meta) in data.Zip(metaData)) {
 | 
				
			||||||
                    var branch = branches[meta.ZwstId];
 | 
					                    var branch = branches[meta.ZwstId];
 | 
				
			||||||
                    var device = meta.Device;
 | 
					                    var device = meta.Device;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    using var ctx = new AppDbContext();
 | 
					                    using var ctx = new AppDbContext();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    var kgnrs = wbKgs.Select(k => k.KgNr).ToList();
 | 
				
			||||||
 | 
					                    var duplicateKgNrs = await ctx.WbKgs
 | 
				
			||||||
 | 
					                        .Where(k => kgnrs.Contains(k.KgNr))
 | 
				
			||||||
 | 
					                        .Select(k => k.KgNr)
 | 
				
			||||||
 | 
					                        .ToListAsync();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    var mgnrs = members.Select(m => m.MgNr).ToList();
 | 
					                    var mgnrs = members.Select(m => m.MgNr).ToList();
 | 
				
			||||||
                    var duplicateMgNrs = await ctx.Members
 | 
					                    var duplicateMgNrs = await ctx.Members
 | 
				
			||||||
                        .Where(m => mgnrs.Contains(m.MgNr))
 | 
					                        .Where(m => mgnrs.Contains(m.MgNr))
 | 
				
			||||||
@@ -216,6 +258,12 @@ namespace Elwig.Helpers.Export {
 | 
				
			|||||||
                            importDuplicateDeliveries = ImportQuestion(branch.Name, device, "Lieferungen", true, duplicateLsNrs.Count);
 | 
					                            importDuplicateDeliveries = ImportQuestion(branch.Name, device, "Lieferungen", true, duplicateLsNrs.Count);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (importDuplicateMembers || importNewMembers || importDuplicateDeliveries || importNewDeliveries) {
 | 
				
			||||||
 | 
					                        ctx.AddRange(wbGls);
 | 
				
			||||||
 | 
					                        ctx.UpdateRange(wbKgs.Where(k => duplicateKgNrs.Contains(k.KgNr)));
 | 
				
			||||||
 | 
					                        ctx.AddRange(wbKgs.Where(k => !duplicateKgNrs.Contains(k.KgNr)));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (importDuplicateMembers) {
 | 
					                    if (importDuplicateMembers) {
 | 
				
			||||||
                        ctx.RemoveRange(ctx.BillingAddresses.Where(a => duplicateMgNrs.Contains(a.MgNr)));
 | 
					                        ctx.RemoveRange(ctx.BillingAddresses.Where(a => duplicateMgNrs.Contains(a.MgNr)));
 | 
				
			||||||
                        ctx.RemoveRange(ctx.MemberTelephoneNrs.Where(n => duplicateMgNrs.Contains(n.MgNr)));
 | 
					                        ctx.RemoveRange(ctx.MemberTelephoneNrs.Where(n => duplicateMgNrs.Contains(n.MgNr)));
 | 
				
			||||||
@@ -352,49 +400,30 @@ namespace Elwig.Helpers.Export {
 | 
				
			|||||||
            ) == MessageBoxResult.Yes;
 | 
					            ) == MessageBoxResult.Yes;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static Task Export(string filename, IEnumerable<Member> members, IEnumerable<string> filters) {
 | 
					        public static Task Export(string filename, IEnumerable<Member> members, IEnumerable<WbKg> wbKgs, IEnumerable<string> filters) {
 | 
				
			||||||
            return new ElwigExport {
 | 
					            return new ElwigExport {
 | 
				
			||||||
                Members = (members, filters)
 | 
					                Members = (members, filters),
 | 
				
			||||||
 | 
					                WbKgs = (wbKgs, ["von exportierten Mitgliedern"]),
 | 
				
			||||||
            }.Export(filename);
 | 
					            }.Export(filename);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static Task Export(string filename, IEnumerable<Member> members, IEnumerable<AreaCom> areaComs, IEnumerable<string> filters) {
 | 
					        public static Task Export(string filename, IEnumerable<Member> members, IEnumerable<AreaCom> areaComs, IEnumerable<WbKg> wbKgs, IEnumerable<string> filters) {
 | 
				
			||||||
            return new ElwigExport {
 | 
					            return new ElwigExport {
 | 
				
			||||||
                Members = (members, filters),
 | 
					                Members = (members, filters),
 | 
				
			||||||
                AreaComs = (areaComs, ["von exportierten Mitgliedern"]),
 | 
					                AreaComs = (areaComs, ["von exportierten Mitgliedern"]),
 | 
				
			||||||
 | 
					                WbKgs = (wbKgs, ["von exportierten Mitgliedern und Flächenbindungen"]),
 | 
				
			||||||
            }.Export(filename);
 | 
					            }.Export(filename);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static Task Export(string filename, IEnumerable<Delivery> deliveries, IEnumerable<string> filters) {
 | 
					        public static Task Export(string filename, IEnumerable<Delivery> deliveries, IEnumerable<WbKg> wbKgs, IEnumerable<string> filters) {
 | 
				
			||||||
            return new ElwigExport {
 | 
					            return new ElwigExport {
 | 
				
			||||||
                Deliveries = (deliveries, filters)
 | 
					                Deliveries = (deliveries, filters),
 | 
				
			||||||
 | 
					                WbKgs = (wbKgs, ["von exportierten Lieferungen"]),
 | 
				
			||||||
            }.Export(filename);
 | 
					            }.Export(filename);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static async Task ImportDatabase(string filename) {
 | 
					 | 
				
			||||||
            var oldName = Path.ChangeExtension(App.Config.DatabaseFile, ".old.sqlite3");
 | 
					 | 
				
			||||||
            var newName = Path.ChangeExtension(App.Config.DatabaseFile, ".new.sqlite3");
 | 
					 | 
				
			||||||
            try {
 | 
					 | 
				
			||||||
                using (var zip = ZipFile.Open(filename, ZipArchiveMode.Read)) {
 | 
					 | 
				
			||||||
                    await zip.CheckIntegrity();
 | 
					 | 
				
			||||||
                    var db = zip.GetEntry("database.sqlite3")!;
 | 
					 | 
				
			||||||
                    db.ExtractToFile(newName, true);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                File.Move(App.Config.DatabaseFile, oldName, true);
 | 
					 | 
				
			||||||
                File.Move(newName, App.Config.DatabaseFile, false);
 | 
					 | 
				
			||||||
            } finally {
 | 
					 | 
				
			||||||
                if (File.Exists(newName))
 | 
					 | 
				
			||||||
                    File.Delete(newName);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public static void ExportDatabase(string filename) {
 | 
					 | 
				
			||||||
            File.Delete(filename);
 | 
					 | 
				
			||||||
            using var zip = ZipFile.Open(filename, ZipArchiveMode.Create);
 | 
					 | 
				
			||||||
            var db = zip.CreateEntryFromFile(App.Config.DatabaseFile, "database.sqlite3", CompressionLevel.SmallestSize);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public class ElwigExport {
 | 
					        public class ElwigExport {
 | 
				
			||||||
 | 
					            public (IEnumerable<WbKg> WbKgs, IEnumerable<string> Filters)? WbKgs { get; set; }
 | 
				
			||||||
            public (IEnumerable<Member> Members, IEnumerable<string> Filters)? Members { get; set; }
 | 
					            public (IEnumerable<Member> Members, IEnumerable<string> Filters)? Members { get; set; }
 | 
				
			||||||
            public (IEnumerable<AreaCom> AreaComs, IEnumerable<string> Filters)? AreaComs { get; set; }
 | 
					            public (IEnumerable<AreaCom> AreaComs, IEnumerable<string> Filters)? AreaComs { get; set; }
 | 
				
			||||||
            public (IEnumerable<Delivery> Deliveries, IEnumerable<string> Filters)? Deliveries { get; set; }
 | 
					            public (IEnumerable<Delivery> Deliveries, IEnumerable<string> Filters)? Deliveries { get; set; }
 | 
				
			||||||
@@ -415,6 +444,12 @@ namespace Elwig.Helpers.Export {
 | 
				
			|||||||
                        ["zwstid"] = App.ZwstId,
 | 
					                        ["zwstid"] = App.ZwstId,
 | 
				
			||||||
                        ["device"] = Environment.MachineName,
 | 
					                        ["device"] = Environment.MachineName,
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
 | 
					                    if (WbKgs != null) {
 | 
				
			||||||
 | 
					                        obj["wb_kgs"] = new JsonObject {
 | 
				
			||||||
 | 
					                            ["count"] = WbKgs.Value.WbKgs.Count(),
 | 
				
			||||||
 | 
					                            ["filters"] = new JsonArray(WbKgs.Value.Filters.Select(f => (JsonNode)f).ToArray()),
 | 
				
			||||||
 | 
					                        };
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                    if (Members != null)
 | 
					                    if (Members != null)
 | 
				
			||||||
                        obj["members"] = new JsonObject {
 | 
					                        obj["members"] = new JsonObject {
 | 
				
			||||||
                            ["count"] = Members.Value.Members.Count(),
 | 
					                            ["count"] = Members.Value.Members.Count(),
 | 
				
			||||||
@@ -431,34 +466,69 @@ namespace Elwig.Helpers.Export {
 | 
				
			|||||||
                            ["parts"] = Deliveries.Value.Deliveries.Sum(d => d.Parts.Count),
 | 
					                            ["parts"] = Deliveries.Value.Deliveries.Sum(d => d.Parts.Count),
 | 
				
			||||||
                            ["filters"] = new JsonArray(Deliveries.Value.Filters.Select(f => (JsonNode)f).ToArray()),
 | 
					                            ["filters"] = new JsonArray(Deliveries.Value.Filters.Select(f => (JsonNode)f).ToArray()),
 | 
				
			||||||
                        };
 | 
					                        };
 | 
				
			||||||
                    await writer.WriteAsync(obj.ToJsonString(JsonOpts));
 | 
					                    await writer.WriteAsync(obj.ToJsonString(Utils.JsonOpts));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // TODO encrypt files
 | 
					                // TODO encrypt files
 | 
				
			||||||
 | 
					                if (WbKgs != null) {
 | 
				
			||||||
 | 
					                    var json = zip.CreateEntry("wb_kgs.json", CompressionLevel.SmallestSize);
 | 
				
			||||||
 | 
					                    using var writer = new StreamWriter(json.Open(), Utils.UTF8);
 | 
				
			||||||
 | 
					                    foreach (var k in WbKgs.Value.WbKgs) {
 | 
				
			||||||
 | 
					                        await writer.WriteLineAsync(k.ToJson().ToJsonString(Utils.JsonOpts));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                if (Members != null) {
 | 
					                if (Members != null) {
 | 
				
			||||||
                    var json = zip.CreateEntry("members.json", CompressionLevel.SmallestSize);
 | 
					                    var json = zip.CreateEntry("members.json", CompressionLevel.SmallestSize);
 | 
				
			||||||
                    using var writer = new StreamWriter(json.Open(), Utils.UTF8);
 | 
					                    using var writer = new StreamWriter(json.Open(), Utils.UTF8);
 | 
				
			||||||
                    foreach (var m in Members.Value.Members) {
 | 
					                    foreach (var m in Members.Value.Members) {
 | 
				
			||||||
                        await writer.WriteLineAsync(m.ToJson().ToJsonString(JsonOpts));
 | 
					                        await writer.WriteLineAsync(m.ToJson().ToJsonString(Utils.JsonOpts));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (AreaComs != null) {
 | 
					                if (AreaComs != null) {
 | 
				
			||||||
                    var json = zip.CreateEntry("area_commitments.json", CompressionLevel.SmallestSize);
 | 
					                    var json = zip.CreateEntry("area_commitments.json", CompressionLevel.SmallestSize);
 | 
				
			||||||
                    using var writer = new StreamWriter(json.Open(), Utils.UTF8);
 | 
					                    using var writer = new StreamWriter(json.Open(), Utils.UTF8);
 | 
				
			||||||
                    foreach (var c in AreaComs.Value.AreaComs) {
 | 
					                    foreach (var c in AreaComs.Value.AreaComs) {
 | 
				
			||||||
                        await writer.WriteLineAsync(c.ToJson().ToJsonString(JsonOpts));
 | 
					                        await writer.WriteLineAsync(c.ToJson().ToJsonString(Utils.JsonOpts));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (Deliveries != null) {
 | 
					                if (Deliveries != null) {
 | 
				
			||||||
                    var json = zip.CreateEntry("deliveries.json", CompressionLevel.SmallestSize);
 | 
					                    var json = zip.CreateEntry("deliveries.json", CompressionLevel.SmallestSize);
 | 
				
			||||||
                    using var writer = new StreamWriter(json.Open(), Utils.UTF8);
 | 
					                    using var writer = new StreamWriter(json.Open(), Utils.UTF8);
 | 
				
			||||||
                    foreach (var d in Deliveries.Value.Deliveries) {
 | 
					                    foreach (var d in Deliveries.Value.Deliveries) {
 | 
				
			||||||
                        await writer.WriteLineAsync(d.ToJson().ToJsonString(JsonOpts));
 | 
					                        await writer.WriteLineAsync(d.ToJson().ToJsonString(Utils.JsonOpts));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static JsonObject ToJson(this WbKg k) {
 | 
				
			||||||
 | 
					            return new JsonObject {
 | 
				
			||||||
 | 
					                ["kgnr"] = k.KgNr,
 | 
				
			||||||
 | 
					                ["großlage"] = k.Gl?.Name,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static (WbKg, WbGl?) ToWbKg(this JsonNode json, List<WbGl> gls) {
 | 
				
			||||||
 | 
					            var grosslage = json["großlage"]?.AsValue().GetValue<string>();
 | 
				
			||||||
 | 
					            WbGl? gl = null;
 | 
				
			||||||
 | 
					            bool newGl = false;
 | 
				
			||||||
 | 
					            if (grosslage != null) {
 | 
				
			||||||
 | 
					                gl = gls.FirstOrDefault(g => g.Name == grosslage);
 | 
				
			||||||
 | 
					                if (gl == null) {
 | 
				
			||||||
 | 
					                    newGl = true;
 | 
				
			||||||
 | 
					                    gl = new WbGl {
 | 
				
			||||||
 | 
					                        GlNr = (gls.Count == 0 ? 1 : gls.Max(g => g.GlNr)) + 1,
 | 
				
			||||||
 | 
					                        Name = grosslage,
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					                    gls[gl.GlNr] = gl;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return (new WbKg {
 | 
				
			||||||
 | 
					                KgNr = json["kgnr"]!.AsValue().GetValue<int>(),
 | 
				
			||||||
 | 
					                GlNr = gl?.GlNr,
 | 
				
			||||||
 | 
					            }, newGl ? gl : null);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static JsonObject ToJson(this Member m) {
 | 
					        public static JsonObject ToJson(this Member m) {
 | 
				
			||||||
            return new JsonObject {
 | 
					            return new JsonObject {
 | 
				
			||||||
                ["mgnr"] = m.MgNr,
 | 
					                ["mgnr"] = m.MgNr,
 | 
				
			||||||
@@ -604,23 +674,23 @@ namespace Elwig.Helpers.Export {
 | 
				
			|||||||
            };
 | 
					            };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static (AreaCom, WbRd?, (DateTime CreatedAt, DateTime ModifiedAt)?) ToAreaCom(this JsonNode json, Dictionary<int, AT_Kg> kgs, Dictionary<int, List<WbRd>> riede) {
 | 
					        public static (AreaCom, WbRd?, (DateTime CreatedAt, DateTime ModifiedAt)?) ToAreaCom(this JsonNode json, Dictionary<int, List<WbRd>> riede) {
 | 
				
			||||||
            var kgnr = json["kgnr"]!.AsValue().GetValue<int>();
 | 
					            var kgnr = json["kgnr"]!.AsValue().GetValue<int>();
 | 
				
			||||||
            var ried = json["ried"]?.AsValue().GetValue<string>();
 | 
					            var ried = json["ried"]?.AsValue().GetValue<string>();
 | 
				
			||||||
            WbRd? rd = null;
 | 
					            WbRd? rd = null;
 | 
				
			||||||
            bool newRd = false;
 | 
					            bool newRd = false;
 | 
				
			||||||
            if (ried != null) {
 | 
					            if (ried != null) {
 | 
				
			||||||
                if (!riede.TryGetValue(kgnr, out var rde))
 | 
					                var rde = riede.GetValueOrDefault(kgnr, []);
 | 
				
			||||||
                    throw new ArgumentException($"Für KG {(kgs.TryGetValue(kgnr, out var k) ? k.Name : "?")} ({kgnr:00000}) ist noch keine Großlage festgelegt!\n(Stammdaten → Herkunftshierarchie)");
 | 
					 | 
				
			||||||
                rd = rde.FirstOrDefault(r => r.Name == ried);
 | 
					                rd = rde.FirstOrDefault(r => r.Name == ried);
 | 
				
			||||||
                if (rd == null) {
 | 
					                if (rd == null) {
 | 
				
			||||||
                    newRd = true;
 | 
					                    newRd = true;
 | 
				
			||||||
                    rd = new WbRd {
 | 
					                    rd = new WbRd {
 | 
				
			||||||
                        KgNr = kgnr,
 | 
					                        KgNr = kgnr,
 | 
				
			||||||
                        RdNr = (rde.Count == 0 ? 0 : rde.Max(r => r.RdNr)) + 1,
 | 
					                        RdNr = (rde.Count == 0 ? 1 : rde.Max(r => r.RdNr)) + 1,
 | 
				
			||||||
                        Name = ried,
 | 
					                        Name = ried,
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
                    rde.Add(rd);
 | 
					                    rde.Add(rd);
 | 
				
			||||||
 | 
					                    riede[rd.KgNr] = rde;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            var createdAt = json["created_at"]?.AsValue().GetValue<string>();
 | 
					            var createdAt = json["created_at"]?.AsValue().GetValue<string>();
 | 
				
			||||||
@@ -633,7 +703,7 @@ namespace Elwig.Helpers.Export {
 | 
				
			|||||||
                Area = json["area"]!.AsValue().GetValue<int>(),
 | 
					                Area = json["area"]!.AsValue().GetValue<int>(),
 | 
				
			||||||
                KgNr = kgnr,
 | 
					                KgNr = kgnr,
 | 
				
			||||||
                GstNr = json["gstnr"]?.AsValue().GetValue<string>() ?? "-",
 | 
					                GstNr = json["gstnr"]?.AsValue().GetValue<string>() ?? "-",
 | 
				
			||||||
                RdNr = rd?.RdNr,
 | 
					                RdNr = rd?.RdNr ?? json["rdnr"]?.AsValue().GetValue<int>(),
 | 
				
			||||||
                YearFrom = json["year_from"]?.AsValue().GetValue<int>(),
 | 
					                YearFrom = json["year_from"]?.AsValue().GetValue<int>(),
 | 
				
			||||||
                YearTo = json["year_to"]?.AsValue().GetValue<int>(),
 | 
					                YearTo = json["year_to"]?.AsValue().GetValue<int>(),
 | 
				
			||||||
                Comment = json["comment"]?.AsValue().GetValue<string>(),
 | 
					                Comment = json["comment"]?.AsValue().GetValue<string>(),
 | 
				
			||||||
@@ -664,7 +734,7 @@ namespace Elwig.Helpers.Export {
 | 
				
			|||||||
                        ["qualid"] = p.QualId,
 | 
					                        ["qualid"] = p.QualId,
 | 
				
			||||||
                        ["hkid"] = p.HkId,
 | 
					                        ["hkid"] = p.HkId,
 | 
				
			||||||
                        ["kgnr"] = p.KgNr,
 | 
					                        ["kgnr"] = p.KgNr,
 | 
				
			||||||
                        ["rdnr"] = p.RdNr,
 | 
					                        ["ried"] = p.Rd?.Name,
 | 
				
			||||||
                        ["net_weight"] = p.IsNetWeight,
 | 
					                        ["net_weight"] = p.IsNetWeight,
 | 
				
			||||||
                        ["manual_weighing"] = p.IsManualWeighing,
 | 
					                        ["manual_weighing"] = p.IsManualWeighing,
 | 
				
			||||||
                        ["modids"] = new JsonArray(p.Modifiers.Select(m => (JsonNode)m.ModId).ToArray()),
 | 
					                        ["modids"] = new JsonArray(p.Modifiers.Select(m => (JsonNode)m.ModId).ToArray()),
 | 
				
			||||||
@@ -689,7 +759,7 @@ namespace Elwig.Helpers.Export {
 | 
				
			|||||||
            };
 | 
					            };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static (Delivery, List<DeliveryPart>, List<DeliveryPartModifier>, (DateTime CreatedAt, DateTime ModifiedAt)?) ToDelivery(this JsonNode json, Dictionary<string, int> currentLsNrs, Dictionary<int, int> currentDids) {
 | 
					        public static (Delivery, List<DeliveryPart>, List<DeliveryPartModifier>, List<WbRd>, (DateTime CreatedAt, DateTime ModifiedAt)?) ToDelivery(this JsonNode json, Dictionary<string, int> currentLsNrs, Dictionary<int, int> currentDids, Dictionary<int, AT_Kg> kgs, Dictionary<int, List<WbRd>> riede) {
 | 
				
			||||||
            var year = json["year"]!.AsValue().GetValue<int>();
 | 
					            var year = json["year"]!.AsValue().GetValue<int>();
 | 
				
			||||||
            var lsnr = json["lsnr"]!.AsValue().GetValue<string>();
 | 
					            var lsnr = json["lsnr"]!.AsValue().GetValue<string>();
 | 
				
			||||||
            var did = currentLsNrs.GetValueOrDefault(lsnr, -1);
 | 
					            var did = currentLsNrs.GetValueOrDefault(lsnr, -1);
 | 
				
			||||||
@@ -700,6 +770,7 @@ namespace Elwig.Helpers.Export {
 | 
				
			|||||||
            currentLsNrs[lsnr] = did;
 | 
					            currentLsNrs[lsnr] = did;
 | 
				
			||||||
            var createdAt = json["created_at"]?.AsValue().GetValue<string>();
 | 
					            var createdAt = json["created_at"]?.AsValue().GetValue<string>();
 | 
				
			||||||
            var modifiedAt = json["modified_at"]?.AsValue().GetValue<string>();
 | 
					            var modifiedAt = json["modified_at"]?.AsValue().GetValue<string>();
 | 
				
			||||||
 | 
					            var wbRde = new List<WbRd>();
 | 
				
			||||||
            return (new Delivery {
 | 
					            return (new Delivery {
 | 
				
			||||||
                Year = year,
 | 
					                Year = year,
 | 
				
			||||||
                DId = did,
 | 
					                DId = did,
 | 
				
			||||||
@@ -711,37 +782,57 @@ namespace Elwig.Helpers.Export {
 | 
				
			|||||||
                MgNr = json["mgnr"]!.AsValue().GetValue<int>(),
 | 
					                MgNr = json["mgnr"]!.AsValue().GetValue<int>(),
 | 
				
			||||||
                Comment = json["comment"]?.AsValue().GetValue<string>(),
 | 
					                Comment = json["comment"]?.AsValue().GetValue<string>(),
 | 
				
			||||||
                ImportedAt = DateTime.Now,
 | 
					                ImportedAt = DateTime.Now,
 | 
				
			||||||
            }, json["parts"]!.AsArray().Select(p => p!.AsObject()).Select(p => new DeliveryPart {
 | 
					            }, json["parts"]!.AsArray().Select(p => p!.AsObject()).Select(p => {
 | 
				
			||||||
                Year = year,
 | 
					                var kgnr = p["kgnr"]!.AsValue().GetValue<int>();
 | 
				
			||||||
                DId = did,
 | 
					                var ried = p["ried"]?.AsValue().GetValue<string>();
 | 
				
			||||||
                DPNr = p["dpnr"]!.AsValue().GetValue<int>(),
 | 
					                WbRd? rd = null;
 | 
				
			||||||
                SortId = p["sortid"]!.AsValue().GetValue<string>(),
 | 
					                if (ried != null) {
 | 
				
			||||||
                AttrId = p["attrid"]?.AsValue().GetValue<string>(),
 | 
					                    var rde = riede.GetValueOrDefault(kgnr, []);
 | 
				
			||||||
                CultId = p["cultid"]?.AsValue().GetValue<string>(),
 | 
					                    rd = rde.FirstOrDefault(r => r.Name == ried);
 | 
				
			||||||
                Weight = p["weight"]!.AsValue().GetValue<int>(),
 | 
					                    if (rd == null) {
 | 
				
			||||||
                Kmw = p["kmw"]!.AsValue().GetValue<double>(),
 | 
					                        rd = new WbRd {
 | 
				
			||||||
                QualId = p["qualid"]!.AsValue().GetValue<string>(),
 | 
					                            KgNr = kgnr,
 | 
				
			||||||
                HkId = p["hkid"]!.AsValue().GetValue<string>(),
 | 
					                            RdNr = (rde.Count == 0 ? 1 : rde.Max(r => r.RdNr)) + 1,
 | 
				
			||||||
                KgNr = p["kgnr"]?.AsValue().GetValue<int>(),
 | 
					                            Name = ried,
 | 
				
			||||||
                RdNr = p["rdnr"]?.AsValue().GetValue<int>(),
 | 
					                        };
 | 
				
			||||||
                IsNetWeight = p["net_weight"]!.AsValue().GetValue<bool>(),
 | 
					                        rde.Add(rd);
 | 
				
			||||||
                IsManualWeighing = p["manual_weighing"]!.AsValue().GetValue<bool>(),
 | 
					                        riede[rd.KgNr] = rde;
 | 
				
			||||||
                Comment = p["comment"]?.AsValue().GetValue<string>(),
 | 
					                        wbRde.Add(rd);
 | 
				
			||||||
                IsSplCheck = p["spl_check"]?.AsValue().GetValue<bool>() ?? false,
 | 
					                    }
 | 
				
			||||||
                IsHandPicked = p["hand_picked"]?.AsValue().GetValue<bool>(),
 | 
					                }
 | 
				
			||||||
                IsLesewagen = p["lesewagen"]?.AsValue().GetValue<bool>(),
 | 
					                return new DeliveryPart {
 | 
				
			||||||
                IsGebunden = p["gebunden"]?.AsValue().GetValue<bool>(),
 | 
					                    Year = year,
 | 
				
			||||||
                Temperature = p["temperature"]?.AsValue().GetValue<double>(),
 | 
					                    DId = did,
 | 
				
			||||||
                Acid = p["acid"]?.AsValue().GetValue<double>(),
 | 
					                    DPNr = p["dpnr"]!.AsValue().GetValue<int>(),
 | 
				
			||||||
                ScaleId = p["scale_id"]?.AsValue().GetValue<string>(),
 | 
					                    SortId = p["sortid"]!.AsValue().GetValue<string>(),
 | 
				
			||||||
                WeighingData = p["weighing_data"]?.AsObject().ToJsonString(JsonOpts),
 | 
					                    AttrId = p["attrid"]?.AsValue().GetValue<string>(),
 | 
				
			||||||
                WeighingReason = p["weighing_reason"]?.AsValue().GetValue<string>(),
 | 
					                    CultId = p["cultid"]?.AsValue().GetValue<string>(),
 | 
				
			||||||
 | 
					                    Weight = p["weight"]!.AsValue().GetValue<int>(),
 | 
				
			||||||
 | 
					                    Kmw = p["kmw"]!.AsValue().GetValue<double>(),
 | 
				
			||||||
 | 
					                    QualId = p["qualid"]!.AsValue().GetValue<string>(),
 | 
				
			||||||
 | 
					                    HkId = p["hkid"]!.AsValue().GetValue<string>(),
 | 
				
			||||||
 | 
					                    KgNr = p["kgnr"]?.AsValue().GetValue<int>(),
 | 
				
			||||||
 | 
					                    RdNr = rd?.RdNr ?? p["rdnr"]?.AsValue().GetValue<int>(),
 | 
				
			||||||
 | 
					                    IsNetWeight = p["net_weight"]!.AsValue().GetValue<bool>(),
 | 
				
			||||||
 | 
					                    IsManualWeighing = p["manual_weighing"]!.AsValue().GetValue<bool>(),
 | 
				
			||||||
 | 
					                    Comment = p["comment"]?.AsValue().GetValue<string>(),
 | 
				
			||||||
 | 
					                    IsSplCheck = p["spl_check"]?.AsValue().GetValue<bool>() ?? false,
 | 
				
			||||||
 | 
					                    IsHandPicked = p["hand_picked"]?.AsValue().GetValue<bool>(),
 | 
				
			||||||
 | 
					                    IsLesewagen = p["lesewagen"]?.AsValue().GetValue<bool>(),
 | 
				
			||||||
 | 
					                    IsGebunden = p["gebunden"]?.AsValue().GetValue<bool>(),
 | 
				
			||||||
 | 
					                    Temperature = p["temperature"]?.AsValue().GetValue<double>(),
 | 
				
			||||||
 | 
					                    Acid = p["acid"]?.AsValue().GetValue<double>(),
 | 
				
			||||||
 | 
					                    ScaleId = p["scale_id"]?.AsValue().GetValue<string>(),
 | 
				
			||||||
 | 
					                    WeighingData = p["weighing_data"]?.AsObject().ToJsonString(Utils.JsonOpts),
 | 
				
			||||||
 | 
					                    WeighingReason = p["weighing_reason"]?.AsValue().GetValue<string>(),
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
            }).ToList(), json["parts"]!.AsArray().SelectMany(p => p!["modids"]!.AsArray().Select(m => new DeliveryPartModifier {
 | 
					            }).ToList(), json["parts"]!.AsArray().SelectMany(p => p!["modids"]!.AsArray().Select(m => new DeliveryPartModifier {
 | 
				
			||||||
                Year = year,
 | 
					                Year = year,
 | 
				
			||||||
                DId = did,
 | 
					                DId = did,
 | 
				
			||||||
                DPNr = p["dpnr"]!.AsValue().GetValue<int>(),
 | 
					                DPNr = p["dpnr"]!.AsValue().GetValue<int>(),
 | 
				
			||||||
                ModId = m!.AsValue().GetValue<string>(),
 | 
					                ModId = m!.AsValue().GetValue<string>(),
 | 
				
			||||||
            })).ToList(),
 | 
					            })).ToList(),
 | 
				
			||||||
 | 
					            wbRde,
 | 
				
			||||||
            createdAt == null || modifiedAt == null ? null :
 | 
					            createdAt == null || modifiedAt == null ? null :
 | 
				
			||||||
            (DateTime.ParseExact(createdAt, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None),
 | 
					            (DateTime.ParseExact(createdAt, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None),
 | 
				
			||||||
             DateTime.ParseExact(modifiedAt, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None)));
 | 
					             DateTime.ParseExact(modifiedAt, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None)));
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,41 +1,42 @@
 | 
				
			|||||||
using System;
 | 
					 | 
				
			||||||
using System.Collections.Generic;
 | 
					 | 
				
			||||||
using System.Linq;
 | 
					 | 
				
			||||||
using System.Threading.Tasks;
 | 
					 | 
				
			||||||
using System.Windows;
 | 
					 | 
				
			||||||
using System.Diagnostics;
 | 
					 | 
				
			||||||
using System.Text.RegularExpressions;
 | 
					 | 
				
			||||||
using System.IO.Ports;
 | 
					 | 
				
			||||||
using System.Net.Sockets;
 | 
					 | 
				
			||||||
using Elwig.Dialogs;
 | 
					using Elwig.Dialogs;
 | 
				
			||||||
using System.Text;
 | 
					using Elwig.Documents;
 | 
				
			||||||
using System.Numerics;
 | 
					 | 
				
			||||||
using Elwig.Models.Entities;
 | 
					 | 
				
			||||||
using Elwig.Helpers.Billing;
 | 
					using Elwig.Helpers.Billing;
 | 
				
			||||||
using System.Runtime.InteropServices;
 | 
					using Elwig.Models;
 | 
				
			||||||
using System.Net.Http;
 | 
					using Elwig.Models.Entities;
 | 
				
			||||||
using System.Text.Json.Nodes;
 | 
					using LinqKit;
 | 
				
			||||||
using System.IO;
 | 
					 | 
				
			||||||
using MailKit.Net.Smtp;
 | 
					using MailKit.Net.Smtp;
 | 
				
			||||||
using MailKit.Security;
 | 
					using MailKit.Security;
 | 
				
			||||||
using Microsoft.EntityFrameworkCore;
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
using System.Reflection;
 | 
					 | 
				
			||||||
using System.Collections;
 | 
					 | 
				
			||||||
using Elwig.Documents;
 | 
					 | 
				
			||||||
using MimeKit;
 | 
					 | 
				
			||||||
using System.Windows.Input;
 | 
					 | 
				
			||||||
using LinqKit;
 | 
					 | 
				
			||||||
using System.Linq.Expressions;
 | 
					 | 
				
			||||||
using Elwig.Models;
 | 
					 | 
				
			||||||
using Microsoft.Win32;
 | 
					using Microsoft.Win32;
 | 
				
			||||||
 | 
					using MimeKit;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Diagnostics;
 | 
				
			||||||
using System.Globalization;
 | 
					using System.Globalization;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
 | 
					using System.IO.Ports;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Linq.Expressions;
 | 
				
			||||||
 | 
					using System.Net.Http;
 | 
				
			||||||
 | 
					using System.Net.Sockets;
 | 
				
			||||||
 | 
					using System.Numerics;
 | 
				
			||||||
 | 
					using System.Reflection;
 | 
				
			||||||
 | 
					using System.Runtime.InteropServices;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Text.Json;
 | 
				
			||||||
 | 
					using System.Text.Json.Nodes;
 | 
				
			||||||
 | 
					using System.Text.RegularExpressions;
 | 
				
			||||||
using System.Threading;
 | 
					using System.Threading;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using System.Windows;
 | 
				
			||||||
using System.Windows.Markup;
 | 
					using System.Windows.Markup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Elwig.Helpers {
 | 
					namespace Elwig.Helpers {
 | 
				
			||||||
    public static partial class Utils {
 | 
					    public static partial class Utils {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static readonly Encoding UTF8 = new UTF8Encoding(false, true);
 | 
					        public static readonly Encoding UTF8 = new UTF8Encoding(false, true);
 | 
				
			||||||
 | 
					        public static readonly JsonSerializerOptions JsonOpts = new() { Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static int CurrentYear => DateTime.Now.Year;
 | 
					        public static int CurrentYear => DateTime.Now.Year;
 | 
				
			||||||
        public static int CurrentNextSeason => DateTime.Now.Year - (DateTime.Now.Month <= 3 ? 1 : 0);
 | 
					        public static int CurrentNextSeason => DateTime.Now.Year - (DateTime.Now.Month <= 3 ? 1 : 0);
 | 
				
			||||||
@@ -430,6 +431,8 @@ namespace Elwig.Helpers {
 | 
				
			|||||||
            var client = new HttpClient() {
 | 
					            var client = new HttpClient() {
 | 
				
			||||||
                Timeout = TimeSpan.FromSeconds(5),
 | 
					                Timeout = TimeSpan.FromSeconds(5),
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					            client.DefaultRequestHeaders.UserAgent.Clear();
 | 
				
			||||||
 | 
					            client.DefaultRequestHeaders.UserAgent.ParseAdd($"Elwig/{App.Version} ({App.Client.NameToken}, {App.BranchName}, {Environment.MachineName}, {Environment.OSVersion})");
 | 
				
			||||||
            client.DefaultRequestHeaders.Accept.Clear();
 | 
					            client.DefaultRequestHeaders.Accept.Clear();
 | 
				
			||||||
            if (accept != null)
 | 
					            if (accept != null)
 | 
				
			||||||
                client.DefaultRequestHeaders.Accept.Add(new(accept));
 | 
					                client.DefaultRequestHeaders.Accept.Add(new(accept));
 | 
				
			||||||
@@ -498,10 +501,7 @@ namespace Elwig.Helpers {
 | 
				
			|||||||
        public static async Task<bool> SendEmail(Member member, string subject, string text, IEnumerable<Document> docs) {
 | 
					        public static async Task<bool> SendEmail(Member member, string subject, string text, IEnumerable<Document> docs) {
 | 
				
			||||||
            if (App.Config.Smtp == null)
 | 
					            if (App.Config.Smtp == null)
 | 
				
			||||||
                return false;
 | 
					                return false;
 | 
				
			||||||
 | 
					            return await Task.Run(async () => {
 | 
				
			||||||
            Mouse.OverrideCursor = Cursors.Wait;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var success = await Task.Run(async () => {
 | 
					 | 
				
			||||||
                SmtpClient? client = null;
 | 
					                SmtpClient? client = null;
 | 
				
			||||||
                try {
 | 
					                try {
 | 
				
			||||||
                    client = await GetSmtpClient();
 | 
					                    client = await GetSmtpClient();
 | 
				
			||||||
@@ -529,18 +529,15 @@ namespace Elwig.Helpers {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                return true;
 | 
					                return true;
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					 | 
				
			||||||
            Mouse.OverrideCursor = null;
 | 
					 | 
				
			||||||
            return success;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static async Task ExportDocument(Document doc, ExportMode mode, string? filename = null, (Member, string, string)? emailData = null) {
 | 
					        public static async Task ExportDocument(Document doc, ExportMode mode, string? filename = null, (Member Member, string Subject, string Text)? emailData = null) {
 | 
				
			||||||
            if (mode == ExportMode.Print && !App.Config.Debug) {
 | 
					            if (mode == ExportMode.Print && !App.Config.Debug) {
 | 
				
			||||||
                await doc.Generate();
 | 
					                await doc.Generate();
 | 
				
			||||||
                await doc.Print();
 | 
					                await doc.Print();
 | 
				
			||||||
            } else if (mode == ExportMode.Email && emailData is (Member, string, string) e) {
 | 
					            } else if (mode == ExportMode.Email && emailData is (Member, string, string) e) {
 | 
				
			||||||
                await doc.Generate();
 | 
					                await doc.Generate();
 | 
				
			||||||
                var success = await SendEmail(e.Item1, e.Item2, e.Item3, [doc]);
 | 
					                var success = await SendEmail(e.Member, e.Subject, e.Text, [doc]);
 | 
				
			||||||
                if (success)
 | 
					                if (success)
 | 
				
			||||||
                    MessageBox.Show("Die E-Mail wurde erfolgreich verschickt!", "E-Mail verschickt",
 | 
					                    MessageBox.Show("Die E-Mail wurde erfolgreich verschickt!", "E-Mail verschickt",
 | 
				
			||||||
                        MessageBoxButton.OK, MessageBoxImage.Information);
 | 
					                        MessageBoxButton.OK, MessageBoxImage.Information);
 | 
				
			||||||
@@ -567,9 +564,7 @@ namespace Elwig.Helpers {
 | 
				
			|||||||
                Log = "Application",
 | 
					                Log = "Application",
 | 
				
			||||||
                Source = ".NET Runtime",
 | 
					                Source = ".NET Runtime",
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
            return log.Entries.Cast<EventLogEntry>()
 | 
					            return [.. log.Entries.OfType<EventLogEntry>().Where(e => e.InstanceId == 1026).Where(e => e.Message.StartsWith("Application: Elwig.exe"))];
 | 
				
			||||||
                .Where(e => e.Message.StartsWith("Application: Elwig.exe"))
 | 
					 | 
				
			||||||
                .ToList();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public static int GetEntityIdetifierForPk(params object?[] primaryKey) {
 | 
					        public static int GetEntityIdetifierForPk(params object?[] primaryKey) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
using Elwig.Helpers;
 | 
					using Elwig.Helpers;
 | 
				
			||||||
 | 
					using Elwig.Helpers.Billing;
 | 
				
			||||||
using Microsoft.EntityFrameworkCore;
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
@@ -62,6 +63,11 @@ namespace Elwig.Models.Entities {
 | 
				
			|||||||
        [Column("comment")]
 | 
					        [Column("comment")]
 | 
				
			||||||
        public string? Comment { get; set; }
 | 
					        public string? Comment { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [NotMapped]
 | 
				
			||||||
 | 
					        public string[] Comments => [.. Parts.Select(p => p.Comment).Prepend(Comment).Where(c => c != null).Cast<string>()];
 | 
				
			||||||
 | 
					        [NotMapped]
 | 
				
			||||||
 | 
					        public string CommentsString => string.Join(" / ", Comments);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
 | 
					        [Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
 | 
				
			||||||
        public long CTime { get; set; }
 | 
					        public long CTime { get; set; }
 | 
				
			||||||
        [NotMapped]
 | 
					        [NotMapped]
 | 
				
			||||||
@@ -108,16 +114,16 @@ namespace Elwig.Models.Entities {
 | 
				
			|||||||
        public int Weight => Parts.Select(p => p.Weight).Sum();
 | 
					        public int Weight => Parts.Select(p => p.Weight).Sum();
 | 
				
			||||||
        public int FilteredWeight => FilteredParts.Select(p => p.Weight).Sum();
 | 
					        public int FilteredWeight => FilteredParts.Select(p => p.Weight).Sum();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public IEnumerable<string> SortIds => Parts
 | 
					        public IEnumerable<RawVaribute> Vaributes => Parts
 | 
				
			||||||
            .GroupBy(p => p.SortId)
 | 
					            .GroupBy(p => (p.SortId, p.AttrId, p.CultId))
 | 
				
			||||||
            .OrderByDescending(g => g.Select(p => p.Weight).Sum())
 | 
					            .OrderByDescending(g => g.Select(p => p.Weight).Sum())
 | 
				
			||||||
            .Select(g => g.Key);
 | 
					            .Select(g => new RawVaribute(g.Key.SortId, g.Key.AttrId, g.Key.CultId));
 | 
				
			||||||
        public IEnumerable<string> FilteredSortIds => FilteredParts
 | 
					        public IEnumerable<RawVaribute> FilteredVaributes => FilteredParts
 | 
				
			||||||
            .GroupBy(p => p.SortId)
 | 
					            .GroupBy(p => (p.SortId, p.AttrId, p.CultId))
 | 
				
			||||||
            .OrderByDescending(g => g.Select(p => p.Weight).Sum())
 | 
					            .OrderByDescending(g => g.Select(p => p.Weight).Sum())
 | 
				
			||||||
            .Select(g => g.Key);
 | 
					            .Select(g => new RawVaribute(g.Key.SortId, g.Key.AttrId, g.Key.CultId));
 | 
				
			||||||
        public string SortIdString => string.Join(", ", SortIds);
 | 
					        public string VaributeString => string.Join(", ", Vaributes);
 | 
				
			||||||
        public string FilteredSortIdString => string.Join(", ", FilteredSortIds);
 | 
					        public string FilteredVaributeString => string.Join(", ", FilteredVaributes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Brush? Color => Parts.Select(p => p.Variety.Color).Distinct().SingleOrDefault();
 | 
					        public Brush? Color => Parts.Select(p => p.Variety.Color).Distinct().SingleOrDefault();
 | 
				
			||||||
        public Brush? FilteredColor => FilteredParts.Select(p => p.Variety.Color).Distinct().SingleOrDefault();
 | 
					        public Brush? FilteredColor => FilteredParts.Select(p => p.Variety.Color).Distinct().SingleOrDefault();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,7 +42,9 @@ namespace Elwig.Models.Entities {
 | 
				
			|||||||
        [Column("max_weight")]
 | 
					        [Column("max_weight")]
 | 
				
			||||||
        public int? MaxWeight { get; set; }
 | 
					        public int? MaxWeight { get; set; }
 | 
				
			||||||
        [NotMapped]
 | 
					        [NotMapped]
 | 
				
			||||||
        public int AnnouncedWeight => Announcements.Sum(a => a.Weight);
 | 
					        public int AnnouncedWeight => AnnouncedWeightOverride ?? Announcements.Sum(a => a.Weight);
 | 
				
			||||||
 | 
					        [NotMapped]
 | 
				
			||||||
 | 
					        public int? AnnouncedWeightOverride { get; set; }
 | 
				
			||||||
        [NotMapped]
 | 
					        [NotMapped]
 | 
				
			||||||
        public double? Percent => (double)AnnouncedWeight / MaxWeight * 100;
 | 
					        public double? Percent => (double)AnnouncedWeight / MaxWeight * 100;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,10 +6,10 @@ namespace Elwig.Models.Entities {
 | 
				
			|||||||
    [Table("wb_gl"), PrimaryKey("GlNr")]
 | 
					    [Table("wb_gl"), PrimaryKey("GlNr")]
 | 
				
			||||||
    public class WbGl {
 | 
					    public class WbGl {
 | 
				
			||||||
        [Column("glnr")]
 | 
					        [Column("glnr")]
 | 
				
			||||||
        public int GlNr { get; private set; }
 | 
					        public int GlNr { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Column("name")]
 | 
					        [Column("name")]
 | 
				
			||||||
        public string Name { get; private set; } = null!;
 | 
					        public string Name { get; set; } = null!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [InverseProperty(nameof(WbKg.Gl))]
 | 
					        [InverseProperty(nameof(WbKg.Gl))]
 | 
				
			||||||
        public virtual ICollection<WbKg> Kgs { get; private set; } = null!;
 | 
					        public virtual ICollection<WbKg> Kgs { get; private set; } = null!;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,7 @@ namespace Elwig.Models.Entities {
 | 
				
			|||||||
        public virtual AT_Kg AtKg { get; private set; } = null!;
 | 
					        public virtual AT_Kg AtKg { get; private set; } = null!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [ForeignKey("GlNr")]
 | 
					        [ForeignKey("GlNr")]
 | 
				
			||||||
        public virtual WbGl Gl { get; private set; } = null!;
 | 
					        public virtual WbGl? Gl { get; private set; } = null!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [InverseProperty(nameof(WbRd.Kg))]
 | 
					        [InverseProperty(nameof(WbRd.Kg))]
 | 
				
			||||||
        public virtual ICollection<WbRd> Rds { get; private set; } = null!;
 | 
					        public virtual ICollection<WbRd> Rds { get; private set; } = null!;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,16 +11,24 @@ namespace Elwig.Models.Entities {
 | 
				
			|||||||
        [Column("type")]
 | 
					        [Column("type")]
 | 
				
			||||||
        public string Type { get; private set; } = null!;
 | 
					        public string Type { get; private set; } = null!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Column("max_qualid")]
 | 
				
			||||||
 | 
					        public string MaxQualId { get; private set; } = null!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [ForeignKey("MaxQualId")]
 | 
				
			||||||
 | 
					        public virtual WineQualLevel MaxQualityLevel { get; private set; } = null!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Column("name")]
 | 
					        [Column("name")]
 | 
				
			||||||
        public string Name { get; private set; } = null!;
 | 
					        public string Name { get; private set; } = null!;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Column("comment")]
 | 
					        [Column("comment")]
 | 
				
			||||||
        public string? Comment { get; private set; }
 | 
					        public string? Comment { get; private set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string SortIdFormat => IsQuw ? SortId : $"({SortId})";
 | 
				
			||||||
        public string CommentFormat => (Comment != null) ? $" ({Comment})" : "";
 | 
					        public string CommentFormat => (Comment != null) ? $" ({Comment})" : "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public bool IsRed => Type == "R";
 | 
					        public bool IsRed => Type == "R";
 | 
				
			||||||
        public bool IsWhite => Type == "W";
 | 
					        public bool IsWhite => Type == "W";
 | 
				
			||||||
 | 
					        public bool IsQuw => MaxQualId == "QUW";
 | 
				
			||||||
        public Brush? Color => IsWhite ? Brushes.DarkGreen : IsRed ? Brushes.DarkRed : null;
 | 
					        public Brush? Color => IsWhite ? Brushes.DarkGreen : IsRed ? Brushes.DarkRed : null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public WineVar() { }
 | 
					        public WineVar() { }
 | 
				
			||||||
@@ -28,6 +36,7 @@ namespace Elwig.Models.Entities {
 | 
				
			|||||||
        public WineVar(string sortId, string name) {
 | 
					        public WineVar(string sortId, string name) {
 | 
				
			||||||
            SortId = sortId;
 | 
					            SortId = sortId;
 | 
				
			||||||
            Name = name;
 | 
					            Name = name;
 | 
				
			||||||
 | 
					            MaxQualId = "QUW";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public override string ToString() {
 | 
					        public override string ToString() {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										17
									
								
								Elwig/Resources/Sql/32-33.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								Elwig/Resources/Sql/32-33.sql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					-- schema version 32 to 33
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ALTER TABLE wine_variety ADD COLUMN max_qualid TEXT NOT NULL DEFAULT 'QUW';
 | 
				
			||||||
 | 
					UPDATE wine_quality_level SET qualid = 'ALW' WHERE qualid = 'AUL';
 | 
				
			||||||
 | 
					UPDATE wine_variety SET comment = 'Muscato' WHERE sortid = 'MO';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INSERT INTO wine_variety (sortid, type, max_qualid, name, comment) VALUES
 | 
				
			||||||
 | 
					('DR', 'W', 'QUW', 'Donauriesling', NULL),
 | 
				
			||||||
 | 
					('DV', 'W', 'QUW', 'Donauveltliner', NULL),
 | 
				
			||||||
 | 
					('BN', 'W', 'RSW', 'Bronner', NULL),
 | 
				
			||||||
 | 
					('CB', 'W', 'RSW', 'Cabernet Blanc', NULL),
 | 
				
			||||||
 | 
					('CJ', 'R', 'RSW', 'Cabernet Jura', NULL),
 | 
				
			||||||
 | 
					('JO', 'W', 'RSW', 'Johanniter', NULL),
 | 
				
			||||||
 | 
					('OR', 'W', 'RSW', 'Orangetraube', NULL),
 | 
				
			||||||
 | 
					('PI', 'R', 'RSW', 'Pinot Nova', NULL),
 | 
				
			||||||
 | 
					('RE', 'R', 'RSW', 'Regent', NULL),
 | 
				
			||||||
 | 
					('SI', 'W', 'RSW', 'Solaris', NULL);
 | 
				
			||||||
@@ -721,19 +721,30 @@ namespace Elwig.Services {
 | 
				
			|||||||
                    FileName = subject == ExportSubject.Selected ? $"Lieferung_{vm.SelectedDelivery?.LsNr}.elwig.zip" : $"Lieferungen_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip",
 | 
					                    FileName = subject == ExportSubject.Selected ? $"Lieferung_{vm.SelectedDelivery?.LsNr}.elwig.zip" : $"Lieferungen_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip",
 | 
				
			||||||
                    DefaultExt = "elwig.zip",
 | 
					                    DefaultExt = "elwig.zip",
 | 
				
			||||||
                    Filter = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip",
 | 
					                    Filter = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip",
 | 
				
			||||||
                    Title = $"{DeliveryJournal.Name} speichern unter - Elwig"
 | 
					                    Title = $"{DeliveryJournal.Name} speichern unter - Elwig",
 | 
				
			||||||
 | 
					                    AddExtension = false,
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
                if (d.ShowDialog() == true) {
 | 
					                if (d.ShowDialog() == true) {
 | 
				
			||||||
 | 
					                    if (!d.FileName.EndsWith(".elwig.zip")) d.FileName += ".elwig.zip";
 | 
				
			||||||
                    Mouse.OverrideCursor = Cursors.Wait;
 | 
					                    Mouse.OverrideCursor = Cursors.Wait;
 | 
				
			||||||
                    await Task.Run(async () => {
 | 
					                    await Task.Run(async () => {
 | 
				
			||||||
                        try {
 | 
					                        try {
 | 
				
			||||||
                            await ElwigData.Export(d.FileName, await query
 | 
					                            var list = await query
 | 
				
			||||||
                                .Select(p => p.Delivery)
 | 
					                                .Select(p => p.Delivery)
 | 
				
			||||||
                                .Distinct()
 | 
					                                .Distinct()
 | 
				
			||||||
                                .Include(d => d.Parts)
 | 
					                                .Include(d => d.Parts).ThenInclude(p => p.PartModifiers)
 | 
				
			||||||
                                .ThenInclude(p => p.PartModifiers)
 | 
					                                .Include(d => d.Parts).ThenInclude(p => p.Rd)
 | 
				
			||||||
 | 
					                                .Include(d => d.Parts).ThenInclude(p => p.Kg!.Gl)
 | 
				
			||||||
                                .AsSplitQuery()
 | 
					                                .AsSplitQuery()
 | 
				
			||||||
                                .ToListAsync(), filterNames);
 | 
					                                .ToListAsync();
 | 
				
			||||||
 | 
					                            var wbKgs = list
 | 
				
			||||||
 | 
					                                .SelectMany(d => d.Parts)
 | 
				
			||||||
 | 
					                                .Where(p => p.Kg != null)
 | 
				
			||||||
 | 
					                                .Select(p => p.Kg!)
 | 
				
			||||||
 | 
					                                .DistinctBy(k => k.KgNr)
 | 
				
			||||||
 | 
					                                .OrderBy(k => k.KgNr)
 | 
				
			||||||
 | 
					                                .ToList();
 | 
				
			||||||
 | 
					                            await ElwigData.Export(d.FileName, list, wbKgs, filterNames);
 | 
				
			||||||
                        } catch (Exception exc) {
 | 
					                        } catch (Exception exc) {
 | 
				
			||||||
                            MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
 | 
					                            MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
@@ -749,15 +760,23 @@ namespace Elwig.Services {
 | 
				
			|||||||
                        var list = await query
 | 
					                        var list = await query
 | 
				
			||||||
                            .Select(p => p.Delivery)
 | 
					                            .Select(p => p.Delivery)
 | 
				
			||||||
                            .Distinct()
 | 
					                            .Distinct()
 | 
				
			||||||
                            .Include(d => d.Parts)
 | 
					                            .Include(d => d.Parts).ThenInclude(p => p.PartModifiers)
 | 
				
			||||||
                            .ThenInclude(p => p.PartModifiers)
 | 
					                            .Include(d => d.Parts).ThenInclude(p => p.Rd)
 | 
				
			||||||
 | 
					                            .Include(d => d.Parts).ThenInclude(p => p.Kg!.Gl)
 | 
				
			||||||
                            .AsSplitQuery()
 | 
					                            .AsSplitQuery()
 | 
				
			||||||
                            .ToListAsync();
 | 
					                            .ToListAsync();
 | 
				
			||||||
 | 
					                        var wbKgs = list
 | 
				
			||||||
 | 
					                            .SelectMany(d => d.Parts)
 | 
				
			||||||
 | 
					                            .Where(p => p.Kg != null)
 | 
				
			||||||
 | 
					                            .Select(p => p.Kg!)
 | 
				
			||||||
 | 
					                            .DistinctBy(k => k.KgNr)
 | 
				
			||||||
 | 
					                            .OrderBy(k => k.KgNr)
 | 
				
			||||||
 | 
					                            .ToList();
 | 
				
			||||||
                        if (list.Count == 0) {
 | 
					                        if (list.Count == 0) {
 | 
				
			||||||
                            MessageBox.Show("Es wurden keine Lieferungen zum Hochladen ausgewählt!", "Lieferungen hochladen",
 | 
					                            MessageBox.Show("Es wurden keine Lieferungen zum Hochladen ausgewählt!", "Lieferungen hochladen",
 | 
				
			||||||
                                MessageBoxButton.OK, MessageBoxImage.Error);
 | 
					                                MessageBoxButton.OK, MessageBoxImage.Error);
 | 
				
			||||||
                        } else {
 | 
					                        } else {
 | 
				
			||||||
                            await ElwigData.Export(path, list, filterNames);
 | 
					                            await ElwigData.Export(path, list, wbKgs, filterNames);
 | 
				
			||||||
                            await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
 | 
					                            await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
 | 
				
			||||||
                            MessageBox.Show($"Hochladen von {list.Count:N0} Lieferungen erfolgreich!", "Lieferungen hochgeladen",
 | 
					                            MessageBox.Show($"Hochladen von {list.Count:N0} Lieferungen erfolgreich!", "Lieferungen hochgeladen",
 | 
				
			||||||
                                MessageBoxButton.OK, MessageBoxImage.Information);
 | 
					                                MessageBoxButton.OK, MessageBoxImage.Information);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -496,11 +496,13 @@ namespace Elwig.Services {
 | 
				
			|||||||
            } else if (mode == ExportMode.Export) {
 | 
					            } else if (mode == ExportMode.Export) {
 | 
				
			||||||
                var d = new SaveFileDialog() {
 | 
					                var d = new SaveFileDialog() {
 | 
				
			||||||
                    FileName = subject == ExportSubject.Selected ? $"Mitglied_{vm.SelectedMember?.MgNr}.elwig.zip" : $"Mitglieder_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip",
 | 
					                    FileName = subject == ExportSubject.Selected ? $"Mitglied_{vm.SelectedMember?.MgNr}.elwig.zip" : $"Mitglieder_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip",
 | 
				
			||||||
                    DefaultExt = ".elwig.zip",
 | 
					                    DefaultExt = "elwig.zip",
 | 
				
			||||||
                    Filter = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip",
 | 
					                    Filter = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip",
 | 
				
			||||||
                    Title = $"{MemberList.Name} speichern unter - Elwig"
 | 
					                    Title = $"{MemberList.Name} speichern unter - Elwig",
 | 
				
			||||||
 | 
					                    AddExtension = false,
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
                if (d.ShowDialog() == true) {
 | 
					                if (d.ShowDialog() == true) {
 | 
				
			||||||
 | 
					                    if (!d.FileName.EndsWith(".elwig.zip")) d.FileName += ".elwig.zip";
 | 
				
			||||||
                    Mouse.OverrideCursor = Cursors.Wait;
 | 
					                    Mouse.OverrideCursor = Cursors.Wait;
 | 
				
			||||||
                    await Task.Run(async () => {
 | 
					                    await Task.Run(async () => {
 | 
				
			||||||
                        try {
 | 
					                        try {
 | 
				
			||||||
@@ -509,13 +511,22 @@ namespace Elwig.Services {
 | 
				
			|||||||
                                .Include(m => m.BillingAddress)
 | 
					                                .Include(m => m.BillingAddress)
 | 
				
			||||||
                                .Include(m => m.TelephoneNumbers)
 | 
					                                .Include(m => m.TelephoneNumbers)
 | 
				
			||||||
                                .Include(m => m.EmailAddresses)
 | 
					                                .Include(m => m.EmailAddresses)
 | 
				
			||||||
 | 
					                                .Include(m => m.DefaultWbKg!.Gl)
 | 
				
			||||||
                                .AsSplitQuery()
 | 
					                                .AsSplitQuery()
 | 
				
			||||||
                                .ToListAsync();
 | 
					                                .ToListAsync();
 | 
				
			||||||
                            var areaComs = await query
 | 
					                            var areaComs = await query
 | 
				
			||||||
                                .SelectMany(m => m.AreaCommitments)
 | 
					                                .SelectMany(m => m.AreaCommitments)
 | 
				
			||||||
                                .Include(c => c.Rd)
 | 
					                                .Include(c => c.Rd)
 | 
				
			||||||
 | 
					                                .Include(c => c.Kg.Gl)
 | 
				
			||||||
                                .ToListAsync();
 | 
					                                .ToListAsync();
 | 
				
			||||||
                            await ElwigData.Export(d.FileName, members, areaComs, filterNames);
 | 
					                            var wbKgs = members
 | 
				
			||||||
 | 
					                                .Where(m => m.DefaultWbKg != null)
 | 
				
			||||||
 | 
					                                .Select(m => m.DefaultWbKg!)
 | 
				
			||||||
 | 
					                                .Union(areaComs.Select(c => c.Kg))
 | 
				
			||||||
 | 
					                                .Distinct()
 | 
				
			||||||
 | 
					                                .OrderBy(k => k.KgNr)
 | 
				
			||||||
 | 
					                                .ToList();
 | 
				
			||||||
 | 
					                            await ElwigData.Export(d.FileName, members, areaComs, wbKgs, filterNames);
 | 
				
			||||||
                        } catch (Exception exc) {
 | 
					                        } catch (Exception exc) {
 | 
				
			||||||
                            MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
 | 
					                            MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
@@ -533,17 +544,27 @@ namespace Elwig.Services {
 | 
				
			|||||||
                            .Include(m => m.BillingAddress)
 | 
					                            .Include(m => m.BillingAddress)
 | 
				
			||||||
                            .Include(m => m.TelephoneNumbers)
 | 
					                            .Include(m => m.TelephoneNumbers)
 | 
				
			||||||
                            .Include(m => m.EmailAddresses)
 | 
					                            .Include(m => m.EmailAddresses)
 | 
				
			||||||
 | 
					                            .Include(m => m.DefaultWbKg!.Gl)
 | 
				
			||||||
                            .AsSplitQuery()
 | 
					                            .AsSplitQuery()
 | 
				
			||||||
                            .ToListAsync();
 | 
					                            .ToListAsync();
 | 
				
			||||||
                        var areaComs = await query
 | 
					                        var areaComs = await query
 | 
				
			||||||
                            .SelectMany(m => m.AreaCommitments)
 | 
					                            .SelectMany(m => m.AreaCommitments)
 | 
				
			||||||
 | 
					                            .OrderBy(c => c.MgNr).ThenBy(c => c.FbNr)
 | 
				
			||||||
                            .Include(c => c.Rd)
 | 
					                            .Include(c => c.Rd)
 | 
				
			||||||
 | 
					                            .Include(c => c.Kg.Gl)
 | 
				
			||||||
                            .ToListAsync();
 | 
					                            .ToListAsync();
 | 
				
			||||||
 | 
					                        var wbKgs = members
 | 
				
			||||||
 | 
					                            .Where(m => m.DefaultWbKg != null)
 | 
				
			||||||
 | 
					                            .Select(m => m.DefaultWbKg!)
 | 
				
			||||||
 | 
					                            .Union(areaComs.Select(c => c.Kg))
 | 
				
			||||||
 | 
					                            .Distinct()
 | 
				
			||||||
 | 
					                            .OrderBy(k => k.KgNr)
 | 
				
			||||||
 | 
					                            .ToList();
 | 
				
			||||||
                        if (members.Count == 0) {
 | 
					                        if (members.Count == 0) {
 | 
				
			||||||
                            MessageBox.Show("Es wurden keine Mitglieder zum Hochladen ausgewählt!", "Mitglieder hochladen",
 | 
					                            MessageBox.Show("Es wurden keine Mitglieder zum Hochladen ausgewählt!", "Mitglieder hochladen",
 | 
				
			||||||
                                MessageBoxButton.OK, MessageBoxImage.Error);
 | 
					                                MessageBoxButton.OK, MessageBoxImage.Error);
 | 
				
			||||||
                        } else {
 | 
					                        } else {
 | 
				
			||||||
                            await ElwigData.Export(path, members, areaComs, filterNames);
 | 
					                            await ElwigData.Export(path, members, areaComs, wbKgs, filterNames);
 | 
				
			||||||
                            await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
 | 
					                            await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
 | 
				
			||||||
                            MessageBox.Show($"Hochladen von {members.Count:N0} Mitgliedern erfolgreich!", "Mitglieder hochgeladen",
 | 
					                            MessageBox.Show($"Hochladen von {members.Count:N0} Mitgliedern erfolgreich!", "Mitglieder hochgeladen",
 | 
				
			||||||
                                MessageBoxButton.OK, MessageBoxImage.Information);
 | 
					                                MessageBoxButton.OK, MessageBoxImage.Information);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										32
									
								
								Elwig/Windows/AboutWindow.xaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								Elwig/Windows/AboutWindow.xaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					<Window x:Class="Elwig.Windows.AboutWindow"
 | 
				
			||||||
 | 
					        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 | 
				
			||||||
 | 
					        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 | 
				
			||||||
 | 
					        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 | 
				
			||||||
 | 
					        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 | 
				
			||||||
 | 
					        xmlns:local="clr-namespace:Elwig.Windows"
 | 
				
			||||||
 | 
					        Title="Über - Elwig" Height="340" Width="460" ResizeMode="NoResize">
 | 
				
			||||||
 | 
					    <Grid>
 | 
				
			||||||
 | 
					        <TextBlock Margin="20,10" FontSize="12">
 | 
				
			||||||
 | 
					            <Bold>Produkt:</Bold> Elwig<LineBreak/>
 | 
				
			||||||
 | 
					            <Bold>Beschreibung:</Bold> Elektronische Winzergenossenschaftsverwaltung<LineBreak/>
 | 
				
			||||||
 | 
					            <Bold>Typ:</Bold> Warenwirtschaftssystem (ERP-System)<LineBreak/>
 | 
				
			||||||
 | 
					            <Bold>Version:</Bold> <Run x:Name="Version">0.0.0.0</Run> (<Hyperlink NavigateUri="https://elwig.at/changelog" RequestNavigate="Hyperlink_RequestNavigate">Änderungsprotokoll</Hyperlink>)<LineBreak/>
 | 
				
			||||||
 | 
					            <Bold>Lizenz:</Bold> <Hyperlink NavigateUri="https://www.gnu.org/licenses/gpl-3.0.html" RequestNavigate="Hyperlink_RequestNavigate">GNU General Public License 3.0 (GPLv3)</Hyperlink><LineBreak/>
 | 
				
			||||||
 | 
					            <Bold>Website:</Bold> <Hyperlink NavigateUri="https://elwig.at/" RequestNavigate="Hyperlink_RequestNavigate">https://elwig.at/</Hyperlink><LineBreak/>
 | 
				
			||||||
 | 
					            <Bold>Entwickler:</Bold> Lorenz Stechauner, Thomas Hilscher<LineBreak/>
 | 
				
			||||||
 | 
					            <Bold>Kontakt:</Bold> <Hyperlink NavigateUri="mailto:lorenz.stechauner@necronda.net" RequestNavigate="Hyperlink_RequestNavigate">lorenz.stechauner@necronda.net</Hyperlink>, <Hyperlink NavigateUri="mailto:thomas.hilscher@gmail.com" RequestNavigate="Hyperlink_RequestNavigate">thomas.hilscher@gmail.com</Hyperlink><LineBreak/>
 | 
				
			||||||
 | 
					            <Bold>Quellcode:</Bold> <Hyperlink NavigateUri="https://git.necronda.net/winzer/elwig" RequestNavigate="Hyperlink_RequestNavigate">https://git.necronda.net/winzer/elwig</Hyperlink><LineBreak/>
 | 
				
			||||||
 | 
					            <Bold>Entwicklungszeitraum:</Bold> 2022–2025<LineBreak/>
 | 
				
			||||||
 | 
					            <LineBreak/>
 | 
				
			||||||
 | 
					            <Bold>Verwendete Technologien:</Bold><LineBreak/>
 | 
				
			||||||
 | 
					            Programmiersprache: C#<LineBreak/>
 | 
				
			||||||
 | 
					            Framework: Windows Presentation Framework (WPF)<LineBreak/>
 | 
				
			||||||
 | 
					            Datenbank: <Hyperlink NavigateUri="https://sqlite.org/" RequestNavigate="Hyperlink_RequestNavigate">SQLite</Hyperlink><LineBreak/>
 | 
				
			||||||
 | 
					            PDF-Erstellung: <Hyperlink NavigateUri="https://weasyprint.org/" RequestNavigate="Hyperlink_RequestNavigate">WeasyPrint</Hyperlink>, <Hyperlink NavigateUri="https://github.com/toddams/RazorLight" RequestNavigate="Hyperlink_RequestNavigate">RazorLight</Hyperlink>, <Hyperlink NavigateUri="https://github.com/pvginkel/PdfiumViewer" RequestNavigate="Hyperlink_RequestNavigate">PdfiumViewer</Hyperlink><LineBreak/>
 | 
				
			||||||
 | 
					            Paketierung: <Hyperlink NavigateUri="https://www.firegiant.com/wixtoolset/" RequestNavigate="Hyperlink_RequestNavigate">WiX Toolset</Hyperlink>
 | 
				
			||||||
 | 
					        </TextBlock>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <Image Source="\Resources\Images\Elwig.png" RenderOptions.BitmapScalingMode="HighQuality" Height="64"
 | 
				
			||||||
 | 
					               HorizontalAlignment="Right" Margin="10" VerticalAlignment="Top"/>
 | 
				
			||||||
 | 
					    </Grid>
 | 
				
			||||||
 | 
					</Window>
 | 
				
			||||||
							
								
								
									
										17
									
								
								Elwig/Windows/AboutWindow.xaml.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								Elwig/Windows/AboutWindow.xaml.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					using System.Diagnostics;
 | 
				
			||||||
 | 
					using System.Windows;
 | 
				
			||||||
 | 
					using System.Windows.Navigation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Elwig.Windows {
 | 
				
			||||||
 | 
					    public partial class AboutWindow : Window {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public AboutWindow() {
 | 
				
			||||||
 | 
					            InitializeComponent();
 | 
				
			||||||
 | 
					            Version.Text = App.Version.ToString();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e) {
 | 
				
			||||||
 | 
					            Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) { UseShellExecute = true });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -13,8 +13,8 @@ using System.Windows.Input;
 | 
				
			|||||||
namespace Elwig.Windows {
 | 
					namespace Elwig.Windows {
 | 
				
			||||||
    public abstract class AdministrationWindow : ContextWindow {
 | 
					    public abstract class AdministrationWindow : ContextWindow {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        protected Control[] ExemptInputs { private get; set; }
 | 
					        protected Control[] ExemptInputs { get; set; }
 | 
				
			||||||
        protected Control[] RequiredInputs { private get; set; }
 | 
					        protected Control[] RequiredInputs { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private bool _isEditing;
 | 
					        private bool _isEditing;
 | 
				
			||||||
        private bool _isCreating;
 | 
					        private bool _isCreating;
 | 
				
			||||||
@@ -166,8 +166,10 @@ namespace Elwig.Windows {
 | 
				
			|||||||
                    Valid[input] = false;
 | 
					                    Valid[input] = false;
 | 
				
			||||||
                } else if (input is ComboBox cb && cb.SelectedItem == null && cb.ItemsSource != null && cb.ItemsSource.Cast<object>().Any()) {
 | 
					                } else if (input is ComboBox cb && cb.SelectedItem == null && cb.ItemsSource != null && cb.ItemsSource.Cast<object>().Any()) {
 | 
				
			||||||
                    ControlUtils.SetInputInvalid(input);
 | 
					                    ControlUtils.SetInputInvalid(input);
 | 
				
			||||||
 | 
					                    Valid[input] = false;
 | 
				
			||||||
                } else if (input is ListBox lb && lb.SelectedItem == null && lb.ItemsSource != null && lb.ItemsSource.Cast<object>().Any()) {
 | 
					                } else if (input is ListBox lb && lb.SelectedItem == null && lb.ItemsSource != null && lb.ItemsSource.Cast<object>().Any()) {
 | 
				
			||||||
                    ControlUtils.SetInputInvalid(input);
 | 
					                    ControlUtils.SetInputInvalid(input);
 | 
				
			||||||
 | 
					                    Valid[input] = false;
 | 
				
			||||||
                } else if (input is CheckBox ckb && ((ckb.IsThreeState && ckb.IsChecked == null) || (!ckb.IsThreeState && ckb.IsChecked != true))) {
 | 
					                } else if (input is CheckBox ckb && ((ckb.IsThreeState && ckb.IsChecked == null) || (!ckb.IsThreeState && ckb.IsChecked != true))) {
 | 
				
			||||||
                    ControlUtils.SetInputInvalid(input);
 | 
					                    ControlUtils.SetInputInvalid(input);
 | 
				
			||||||
                    Valid[input] = false;
 | 
					                    Valid[input] = false;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -664,8 +664,10 @@ namespace Elwig.Windows {
 | 
				
			|||||||
            try {
 | 
					            try {
 | 
				
			||||||
                await Task.Run(async () => {
 | 
					                await Task.Run(async () => {
 | 
				
			||||||
                    var b = new BillingVariant(PaymentVar.Year, PaymentVar.AvNr);
 | 
					                    var b = new BillingVariant(PaymentVar.Year, PaymentVar.AvNr);
 | 
				
			||||||
                    await b.Calculate();
 | 
					                    await b.Calculate(false);
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					            } catch (KeyNotFoundException exc) {
 | 
				
			||||||
 | 
					                MessageBox.Show(exc.Message, "Noch nicht alle Preise festgelegt", MessageBoxButton.OK, MessageBoxImage.Information);
 | 
				
			||||||
            } catch (Exception exc) {
 | 
					            } catch (Exception exc) {
 | 
				
			||||||
                MessageBox.Show(exc.Message, "Berechnungsfehler", MessageBoxButton.OK, MessageBoxImage.Error);
 | 
					                MessageBox.Show(exc.Message, "Berechnungsfehler", MessageBoxButton.OK, MessageBoxImage.Error);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -265,13 +265,18 @@
 | 
				
			|||||||
                            </Style>
 | 
					                            </Style>
 | 
				
			||||||
                        </DataGridTextColumn.CellStyle>
 | 
					                        </DataGridTextColumn.CellStyle>
 | 
				
			||||||
                    </DataGridTextColumn>
 | 
					                    </DataGridTextColumn>
 | 
				
			||||||
                    <DataGridTextColumn Header="Sorte" Binding="{Binding FilteredSortIdString}" Width="50">
 | 
					                    <DataGridTextColumn Header="Sorte" Binding="{Binding FilteredVaributeString}" Width="60">
 | 
				
			||||||
                        <DataGridTextColumn.CellStyle>
 | 
					                        <DataGridTextColumn.CellStyle>
 | 
				
			||||||
                            <Style>
 | 
					                            <Style>
 | 
				
			||||||
                                <Setter Property="TextBlock.Foreground" Value="{Binding FilteredColor}"/>
 | 
					                                <Setter Property="TextBlock.Foreground" Value="{Binding FilteredColor}"/>
 | 
				
			||||||
                                <Setter Property="TextBlock.TextAlignment" Value="Center"/>
 | 
					                                <Setter Property="TextBlock.TextAlignment" Value="Center"/>
 | 
				
			||||||
                            </Style>
 | 
					                            </Style>
 | 
				
			||||||
                        </DataGridTextColumn.CellStyle>
 | 
					                        </DataGridTextColumn.CellStyle>
 | 
				
			||||||
 | 
					                        <DataGridTextColumn.ElementStyle>
 | 
				
			||||||
 | 
					                            <Style TargetType="TextBlock">
 | 
				
			||||||
 | 
					                                <Setter Property="TextTrimming" Value="CharacterEllipsis"/>
 | 
				
			||||||
 | 
					                            </Style>
 | 
				
			||||||
 | 
					                        </DataGridTextColumn.ElementStyle>
 | 
				
			||||||
                    </DataGridTextColumn>
 | 
					                    </DataGridTextColumn>
 | 
				
			||||||
                    <DataGridTextColumn Header="Menge" Binding="{Binding FilteredWeight, StringFormat='{}{0:N0} kg '}" Width="75">
 | 
					                    <DataGridTextColumn Header="Menge" Binding="{Binding FilteredWeight, StringFormat='{}{0:N0} kg '}" Width="75">
 | 
				
			||||||
                        <DataGridTextColumn.CellStyle>
 | 
					                        <DataGridTextColumn.CellStyle>
 | 
				
			||||||
@@ -290,6 +295,7 @@
 | 
				
			|||||||
                    <DataGridTextColumn Header="LsNr." Binding="{Binding LsNr}" Width="120"/>
 | 
					                    <DataGridTextColumn Header="LsNr." Binding="{Binding LsNr}" Width="120"/>
 | 
				
			||||||
                    <DataGridTextColumn Header="Mitglied" Binding="{Binding Member.AdministrativeName}" Width="180"/>
 | 
					                    <DataGridTextColumn Header="Mitglied" Binding="{Binding Member.AdministrativeName}" Width="180"/>
 | 
				
			||||||
                    <DataGridTextColumn Header="Zu-/Abschläge" Binding="{Binding FilteredModifiersString}" Width="150"/>
 | 
					                    <DataGridTextColumn Header="Zu-/Abschläge" Binding="{Binding FilteredModifiersString}" Width="150"/>
 | 
				
			||||||
 | 
					                    <DataGridTextColumn Header="Kommentar" Binding="{Binding CommentsString}" Width="150"/>
 | 
				
			||||||
                </DataGrid.Columns>
 | 
					                </DataGrid.Columns>
 | 
				
			||||||
            </DataGrid>
 | 
					            </DataGrid>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,7 @@ using Elwig.ViewModels;
 | 
				
			|||||||
using Microsoft.EntityFrameworkCore;
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
using Microsoft.Win32;
 | 
					using Microsoft.Win32;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using System.Windows;
 | 
					using System.Windows;
 | 
				
			||||||
@@ -34,6 +35,8 @@ namespace Elwig.Windows {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private readonly Button[] WeighingButtons;
 | 
					        private readonly Button[] WeighingButtons;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private List<WineQualLevel> WineQualityLevels = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public DeliveryAdminWindow(bool receipt = false) {
 | 
					        public DeliveryAdminWindow(bool receipt = false) {
 | 
				
			||||||
            InitializeComponent();
 | 
					            InitializeComponent();
 | 
				
			||||||
            CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput));
 | 
					            CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput));
 | 
				
			||||||
@@ -89,6 +92,9 @@ namespace Elwig.Windows {
 | 
				
			|||||||
                foreach (var s in App.EventScales) {
 | 
					                foreach (var s in App.EventScales) {
 | 
				
			||||||
                    s.WeighingEvent += Scale_Weighing;
 | 
					                    s.WeighingEvent += Scale_Weighing;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                if (App.Client.IsMatzen) {
 | 
				
			||||||
 | 
					                    RequiredInputs = [.. RequiredInputs, ModifiersInput];
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                WeighingManualButton.Visibility = Visibility.Hidden;
 | 
					                WeighingManualButton.Visibility = Visibility.Hidden;
 | 
				
			||||||
                WeighingAButton.Visibility = Visibility.Hidden;
 | 
					                WeighingAButton.Visibility = Visibility.Hidden;
 | 
				
			||||||
@@ -278,6 +284,7 @@ namespace Elwig.Windows {
 | 
				
			|||||||
                DateInput.IsReadOnly = false;
 | 
					                DateInput.IsReadOnly = false;
 | 
				
			||||||
                TimeInput.IsReadOnly = false;
 | 
					                TimeInput.IsReadOnly = false;
 | 
				
			||||||
                BranchInput.IsEnabled = true;
 | 
					                BranchInput.IsEnabled = true;
 | 
				
			||||||
 | 
					                GerebeltGewogenInput.IsEnabled = true;
 | 
				
			||||||
                if (IsCreating) ViewModel.Time = "";
 | 
					                if (IsCreating) ViewModel.Time = "";
 | 
				
			||||||
                OnSecondPassed(null, null);
 | 
					                OnSecondPassed(null, null);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -287,6 +294,7 @@ namespace Elwig.Windows {
 | 
				
			|||||||
            DateInput.IsReadOnly = true;
 | 
					            DateInput.IsReadOnly = true;
 | 
				
			||||||
            TimeInput.IsReadOnly = true;
 | 
					            TimeInput.IsReadOnly = true;
 | 
				
			||||||
            BranchInput.IsEnabled = false;
 | 
					            BranchInput.IsEnabled = false;
 | 
				
			||||||
 | 
					            GerebeltGewogenInput.IsEnabled = App.Config.WeighingMode != WeighingMode.Net;
 | 
				
			||||||
            OnSecondPassed(null, null);
 | 
					            OnSecondPassed(null, null);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -301,7 +309,7 @@ namespace Elwig.Windows {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void InitialDefaultInputs() {
 | 
					        private void InitialDefaultInputs() {
 | 
				
			||||||
            if (App.Client.HasNetWeighing(ViewModel.Branch)) {
 | 
					            if (App.Config.WeighingMode == WeighingMode.Net) {
 | 
				
			||||||
                GerebeltGewogenInput.IsEnabled = false;
 | 
					                GerebeltGewogenInput.IsEnabled = false;
 | 
				
			||||||
                SetDefaultValue(GerebeltGewogenInput, true);
 | 
					                SetDefaultValue(GerebeltGewogenInput, true);
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
@@ -310,7 +318,7 @@ namespace Elwig.Windows {
 | 
				
			|||||||
                UnsetDefaultValue(GerebeltGewogenInput);
 | 
					                UnsetDefaultValue(GerebeltGewogenInput);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (App.Client.HasBoxWeighing(ViewModel.Branch)) {
 | 
					            if (App.Config.WeighingMode == WeighingMode.Box) {
 | 
				
			||||||
                LesewagenInput.IsEnabled = false;
 | 
					                LesewagenInput.IsEnabled = false;
 | 
				
			||||||
                SetDefaultValue(LesewagenInput, false);
 | 
					                SetDefaultValue(LesewagenInput, false);
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
@@ -318,7 +326,7 @@ namespace Elwig.Windows {
 | 
				
			|||||||
                UnsetDefaultValue(LesewagenInput);
 | 
					                UnsetDefaultValue(LesewagenInput);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!App.Client.HasNetWeighing(ViewModel.Branch)) {
 | 
					            if (App.Config.WeighingMode != WeighingMode.Net) {
 | 
				
			||||||
                HandPickedInput.IsThreeState = false;
 | 
					                HandPickedInput.IsThreeState = false;
 | 
				
			||||||
                UnsetDefaultValue(HandPickedInput);
 | 
					                UnsetDefaultValue(HandPickedInput);
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
@@ -344,13 +352,13 @@ namespace Elwig.Windows {
 | 
				
			|||||||
            ClearOriginalValues();
 | 
					            ClearOriginalValues();
 | 
				
			||||||
            ClearDefaultValues();
 | 
					            ClearDefaultValues();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ViewModel.IsNetWeight = App.Client.HasNetWeighing(ViewModel.Branch);
 | 
					            ViewModel.IsNetWeight = App.Config.WeighingMode == WeighingMode.Net;
 | 
				
			||||||
            ViewModel.IsLesewagen = false;
 | 
					            ViewModel.IsLesewagen = false;
 | 
				
			||||||
            ViewModel.IsHandPicked = !App.Client.HasNetWeighing(ViewModel.Branch) ? true : null;
 | 
					            ViewModel.IsHandPicked = App.Config.WeighingMode != WeighingMode.Net ? true : null;
 | 
				
			||||||
            ViewModel.IsGebunden = null;
 | 
					            ViewModel.IsGebunden = null;
 | 
				
			||||||
            InitialDefaultInputs();
 | 
					            InitialDefaultInputs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            WineQualityLevelInput.IsEnabled = false;
 | 
					            //WineQualityLevelInput.IsEnabled = false;  // disable wine quality level input in Übernahme
 | 
				
			||||||
            ValidateRequiredInputs();
 | 
					            ValidateRequiredInputs();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -478,7 +486,8 @@ namespace Elwig.Windows {
 | 
				
			|||||||
            var cultList = await ctx.WineCultivations.OrderBy(a => a.Name).Cast<object>().ToListAsync();
 | 
					            var cultList = await ctx.WineCultivations.OrderBy(a => a.Name).Cast<object>().ToListAsync();
 | 
				
			||||||
            cultList.Insert(0, new NullItem(""));
 | 
					            cultList.Insert(0, new NullItem(""));
 | 
				
			||||||
            ControlUtils.RenewItemsSource(CultivationInput, cultList, null, ControlUtils.RenewSourceDefault.First);
 | 
					            ControlUtils.RenewItemsSource(CultivationInput, cultList, null, ControlUtils.RenewSourceDefault.First);
 | 
				
			||||||
            ControlUtils.RenewItemsSource(WineQualityLevelInput, await ctx.WineQualityLevels.ToListAsync());
 | 
					            WineQualityLevels = await ctx.WineQualityLevels.ToListAsync();
 | 
				
			||||||
 | 
					            ControlUtils.RenewItemsSource(WineQualityLevelInput, WineQualityLevels);
 | 
				
			||||||
            ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
 | 
					            ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
 | 
				
			||||||
                .Where(m => m.Year == y && (!IsCreating || m.IsActive))
 | 
					                .Where(m => m.Year == y && (!IsCreating || m.IsActive))
 | 
				
			||||||
                .OrderBy(m => m.Ordering)
 | 
					                .OrderBy(m => m.Ordering)
 | 
				
			||||||
@@ -1139,6 +1148,7 @@ namespace Elwig.Windows {
 | 
				
			|||||||
            DateInput.IsReadOnly = !Menu_Settings_EnableFreeEditing.IsChecked;
 | 
					            DateInput.IsReadOnly = !Menu_Settings_EnableFreeEditing.IsChecked;
 | 
				
			||||||
            TimeInput.IsReadOnly = !Menu_Settings_EnableFreeEditing.IsChecked;
 | 
					            TimeInput.IsReadOnly = !Menu_Settings_EnableFreeEditing.IsChecked;
 | 
				
			||||||
            BranchInput.IsEnabled = Menu_Settings_EnableFreeEditing.IsChecked;
 | 
					            BranchInput.IsEnabled = Menu_Settings_EnableFreeEditing.IsChecked;
 | 
				
			||||||
 | 
					            GerebeltGewogenInput.IsEnabled = App.Config.WeighingMode == WeighingMode.Net || Menu_Settings_EnableFreeEditing.IsChecked;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void DisableWeighingButtons() {
 | 
					        private void DisableWeighingButtons() {
 | 
				
			||||||
@@ -1201,6 +1211,7 @@ namespace Elwig.Windows {
 | 
				
			|||||||
                AttributeInput.SelectedIndex = 0;
 | 
					                AttributeInput.SelectedIndex = 0;
 | 
				
			||||||
                CultivationInput.SelectedIndex = 0;
 | 
					                CultivationInput.SelectedIndex = 0;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            UpdateWineQualityLevels();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void SortIdInput_TextChanged(object sender, TextChangedEventArgs evt) {
 | 
					        private void SortIdInput_TextChanged(object sender, TextChangedEventArgs evt) {
 | 
				
			||||||
@@ -1214,19 +1225,39 @@ namespace Elwig.Windows {
 | 
				
			|||||||
        private void WineVarietyInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
 | 
					        private void WineVarietyInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
 | 
				
			||||||
            if (WineVarietyInput.SelectedItem is WineVar s)
 | 
					            if (WineVarietyInput.SelectedItem is WineVar s)
 | 
				
			||||||
                ViewModel.SortId = s.SortId;
 | 
					                ViewModel.SortId = s.SortId;
 | 
				
			||||||
 | 
					            UpdateWineQualityLevels();
 | 
				
			||||||
 | 
					            if (!ViewModel.WineVar?.IsQuw ?? false) {
 | 
				
			||||||
 | 
					                App.MainDispatcher.BeginInvoke(() => {
 | 
				
			||||||
 | 
					                    MessageBox.Show("Die eingegebene Sorte darf nicht als Qualitätswein\nübernommen werden!", "Kein Qualitätswein",
 | 
				
			||||||
 | 
					                        MessageBoxButton.OK, MessageBoxImage.Warning);
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private WineQualLevel GetWineQualityLevel(double kmw, string? maxQualId = null) {
 | 
				
			||||||
 | 
					            return WineQualityLevels
 | 
				
			||||||
 | 
					                .Where(q => !q.IsPredicate && (q.MinKmw == null || q.MinKmw <= kmw))
 | 
				
			||||||
 | 
					                .Where(q => maxQualId == null || q.QualId == "WEI" || q.QualId == maxQualId)
 | 
				
			||||||
 | 
					                .OrderBy(q => q.MinKmw)
 | 
				
			||||||
 | 
					                .Last();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void UpdateWineQualityLevels() {
 | 
					        private void UpdateWineQualityLevels() {
 | 
				
			||||||
            using var ctx = new AppDbContext();
 | 
					 | 
				
			||||||
            if (!GetInputValid(GradationKmwInput)) {
 | 
					            if (!GetInputValid(GradationKmwInput)) {
 | 
				
			||||||
                UnsetDefaultValue(WineQualityLevelInput);
 | 
					                UnsetDefaultValue(WineQualityLevelInput);
 | 
				
			||||||
                ComboBox_SelectionChanged(WineQualityLevelInput, null);
 | 
					                ComboBox_SelectionChanged(WineQualityLevelInput, null);
 | 
				
			||||||
                WineQualityLevelInput.ItemsSource = ctx.WineQualityLevels.Where(q => q.QualId == "WEI").ToList();
 | 
					                WineQualityLevelInput.ItemsSource = WineQualityLevels;
 | 
				
			||||||
 | 
					                WineQualityLevelInput.SelectedItem = null;
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            var kmw = (double)ViewModel.GradationKmw!;
 | 
					            var kmw = (double)ViewModel.GradationKmw!;
 | 
				
			||||||
            WineQualityLevelInput.ItemsSource = ctx.WineQualityLevels.Where(q => q.MinKmw == null || q.MinKmw <= kmw).ToList();
 | 
					            var max = ViewModel.WineVar?.MaxQualId;
 | 
				
			||||||
            var qual = ctx.GetWineQualityLevel(kmw).GetAwaiter().GetResult();
 | 
					            var quw = ViewModel.WineVar?.IsQuw ?? true;
 | 
				
			||||||
 | 
					            WineQualityLevelInput.ItemsSource = WineQualityLevels
 | 
				
			||||||
 | 
					                .Where(q => q.MinKmw == null || q.MinKmw <= kmw)
 | 
				
			||||||
 | 
					                .Where(q => quw || q.QualId == "WEI" || q.QualId == max)
 | 
				
			||||||
 | 
					                .ToList();
 | 
				
			||||||
 | 
					            var qual = GetWineQualityLevel(kmw, !quw ? max : null);
 | 
				
			||||||
            SetDefaultValue(WineQualityLevelInput, qual);
 | 
					            SetDefaultValue(WineQualityLevelInput, qual);
 | 
				
			||||||
            if (WineQualityLevelInput.SelectedItem == null || (WineQualityLevelInput.SelectedItem is WineQualLevel selected && !selected.IsPredicate)) {
 | 
					            if (WineQualityLevelInput.SelectedItem == null || (WineQualityLevelInput.SelectedItem is WineQualLevel selected && !selected.IsPredicate)) {
 | 
				
			||||||
                ControlUtils.SelectItem(WineQualityLevelInput, qual);
 | 
					                ControlUtils.SelectItem(WineQualityLevelInput, qual);
 | 
				
			||||||
@@ -1366,8 +1397,7 @@ namespace Elwig.Windows {
 | 
				
			|||||||
                AbgewertetInput.IsChecked = false;
 | 
					                AbgewertetInput.IsChecked = false;
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            using var ctx = new AppDbContext();
 | 
					            var defQual = GetWineQualityLevel(ViewModel.GradationKmw!.Value, !(ViewModel.WineVar?.IsQuw ?? true) ? ViewModel.WineVar?.MaxQualId : null);
 | 
				
			||||||
            var defQual = ctx.GetWineQualityLevel(double.Parse(GradationKmwInput.Text)).GetAwaiter().GetResult();
 | 
					 | 
				
			||||||
            AbgewertetInput.IsChecked = !qual.IsPredicate && defQual != qual;
 | 
					            AbgewertetInput.IsChecked = !qual.IsPredicate && defQual != qual;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1389,17 +1419,17 @@ namespace Elwig.Windows {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void GerebeltGewogenInput_Changed(object sender, RoutedEventArgs evt) {
 | 
					        private void GerebeltGewogenInput_Changed(object sender, RoutedEventArgs evt) {
 | 
				
			||||||
            if ((IsEditing || IsCreating) && !App.Client.HasNetWeighing(ViewModel.Branch)) {
 | 
					            if ((IsEditing || IsCreating) && App.Config.WeighingMode != WeighingMode.Net) {
 | 
				
			||||||
                HandPickedInput.IsChecked = !GerebeltGewogenInput.IsChecked;
 | 
					                HandPickedInput.IsChecked = !GerebeltGewogenInput.IsChecked;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (!ViewModel.IsReceipt || App.Client.HasNetWeighing(ViewModel.Branch)) {
 | 
					            if (!ViewModel.IsReceipt || App.Config.WeighingMode == WeighingMode.Net) {
 | 
				
			||||||
                GerebeltGewogenInput.IsChecked ??= false;
 | 
					                GerebeltGewogenInput.IsChecked ??= false;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            CheckBox_Changed(sender, evt);
 | 
					            CheckBox_Changed(sender, evt);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void HandPickedInput_Changed(object sender, RoutedEventArgs evt) {
 | 
					        private void HandPickedInput_Changed(object sender, RoutedEventArgs evt) {
 | 
				
			||||||
            if ((IsEditing || IsCreating) && !App.Client.HasNetWeighing(ViewModel.Branch)) {
 | 
					            if ((IsEditing || IsCreating) && App.Config.WeighingMode != WeighingMode.Net) {
 | 
				
			||||||
                GerebeltGewogenInput.IsChecked = !HandPickedInput.IsChecked;
 | 
					                GerebeltGewogenInput.IsChecked = !HandPickedInput.IsChecked;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            CheckBox_Changed(sender, evt);
 | 
					            CheckBox_Changed(sender, evt);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -83,14 +83,19 @@ namespace Elwig.Windows {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private async Task RefreshDeliveryScheduleList() {
 | 
					        private async Task RefreshDeliveryScheduleList() {
 | 
				
			||||||
            using var ctx = new AppDbContext();
 | 
					            using var ctx = new AppDbContext();
 | 
				
			||||||
            var deliverySchedules = await ctx.DeliverySchedules
 | 
					            var list = await ctx.DeliverySchedules
 | 
				
			||||||
                .Where(s => s.Year == ViewModel.FilterSeason)
 | 
					                .Where(s => s.Year == ViewModel.FilterSeason)
 | 
				
			||||||
                .Include(s => s.Branch)
 | 
					                .Include(s => s.Branch)
 | 
				
			||||||
                .Include(s => s.Announcements)
 | 
					 | 
				
			||||||
                .OrderBy(s => s.DateString)
 | 
					                .OrderBy(s => s.DateString)
 | 
				
			||||||
                .ThenBy(s => s.Branch.Name)
 | 
					                .ThenBy(s => s.Branch.Name)
 | 
				
			||||||
                .ThenBy(s => s.Description)
 | 
					                .ThenBy(s => s.Description)
 | 
				
			||||||
 | 
					                .Select(s => new {
 | 
				
			||||||
 | 
					                    Schedule = s,
 | 
				
			||||||
 | 
					                    AnnouncedWeight = s.Announcements.Sum(a => a.Weight)
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
                .ToListAsync();
 | 
					                .ToListAsync();
 | 
				
			||||||
 | 
					            list.ForEach(v => v.Schedule.AnnouncedWeightOverride = v.AnnouncedWeight);
 | 
				
			||||||
 | 
					            var deliverySchedules = list.Select(v => v.Schedule).ToList();
 | 
				
			||||||
            ControlUtils.RenewItemsSource(DeliveryScheduleList, deliverySchedules
 | 
					            ControlUtils.RenewItemsSource(DeliveryScheduleList, deliverySchedules
 | 
				
			||||||
                .Where(s => !ViewModel.FilterOnlyUpcoming || s.DateString.CompareTo(Utils.Today.ToString("yyyy-MM-dd")) >= 0)
 | 
					                .Where(s => !ViewModel.FilterOnlyUpcoming || s.DateString.CompareTo(Utils.Today.ToString("yyyy-MM-dd")) >= 0)
 | 
				
			||||||
                .ToList(), DeliveryScheduleList_SelectionChanged, ViewModel.FilterFromAllSchedules ? ControlUtils.RenewSourceDefault.None : ControlUtils.RenewSourceDefault.First);
 | 
					                .ToList(), DeliveryScheduleList_SelectionChanged, ViewModel.FilterFromAllSchedules ? ControlUtils.RenewSourceDefault.None : ControlUtils.RenewSourceDefault.First);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,9 @@
 | 
				
			|||||||
using Elwig.Helpers;
 | 
					using Elwig.Helpers;
 | 
				
			||||||
using System.Diagnostics;
 | 
					using System.Diagnostics;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using System.Windows;
 | 
					using System.Windows;
 | 
				
			||||||
 | 
					using System.Windows.Input;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Elwig.Windows {
 | 
					namespace Elwig.Windows {
 | 
				
			||||||
    public partial class LogWindow : Window {
 | 
					    public partial class LogWindow : Window {
 | 
				
			||||||
@@ -11,28 +13,34 @@ namespace Elwig.Windows {
 | 
				
			|||||||
            WindowState = WindowState.Maximized;
 | 
					            WindowState = WindowState.Maximized;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void Window_Loaded(object sender, RoutedEventArgs evt) {
 | 
					        private async void Window_Loaded(object sender, RoutedEventArgs evt) {
 | 
				
			||||||
            var log = Utils.GetLogEntries();
 | 
					            Mouse.OverrideCursor = Cursors.Wait;
 | 
				
			||||||
            EventList.ItemsSource = log
 | 
					            await Task.Run(async () => {
 | 
				
			||||||
                .Select(e => new {
 | 
					                var list = Utils.GetLogEntries()
 | 
				
			||||||
                    Event = e,
 | 
					                  .Select(e => new {
 | 
				
			||||||
                    Lines = e.Message.Split('\n').ToArray(),
 | 
					                      Event = e,
 | 
				
			||||||
                })
 | 
					                      Lines = e.Message.Split('\n').ToArray(),
 | 
				
			||||||
                .Select(e => new {
 | 
					                  })
 | 
				
			||||||
                    e.Event,
 | 
					                  .Select(e => new {
 | 
				
			||||||
                    Exception = e.Lines.FirstOrDefault(l => l.StartsWith("Exception Info: "))?[16..].Trim().Split(':', 2),
 | 
					                      e.Event,
 | 
				
			||||||
                    Location = e.Lines.FirstOrDefault(l => l.StartsWith("   at Elwig."))?[5..].Trim(),
 | 
					                      Exception = e.Lines.FirstOrDefault(l => l.StartsWith("Exception Info: "))?[16..].Trim().Split(':', 2),
 | 
				
			||||||
                })
 | 
					                      Location = e.Lines.FirstOrDefault(l => l.StartsWith("   at Elwig."))?[5..].Trim(),
 | 
				
			||||||
                .Select(e => new {
 | 
					                  })
 | 
				
			||||||
                    e.Event,
 | 
					                  .Select(e => new {
 | 
				
			||||||
                    e.Exception,
 | 
					                      e.Event,
 | 
				
			||||||
                    ExceptionName = e.Exception?[0].Trim(),
 | 
					                      e.Exception,
 | 
				
			||||||
                    ExceptionMessage = e.Exception?.Length >= 2 ? e.Exception?[1].Trim() : null,
 | 
					                      ExceptionName = e.Exception?[0].Trim(),
 | 
				
			||||||
                    e.Location,
 | 
					                      ExceptionMessage = e.Exception?.Length >= 2 ? e.Exception?[1].Trim() : null,
 | 
				
			||||||
                })
 | 
					                      e.Location,
 | 
				
			||||||
                .OrderByDescending(e => e.Event.TimeGenerated)
 | 
					                  })
 | 
				
			||||||
                .ToList();
 | 
					                  .OrderByDescending(e => e.Event.TimeGenerated)
 | 
				
			||||||
            EventList.SelectedIndex = 0;
 | 
					                  .ToList();
 | 
				
			||||||
 | 
					                await App.MainDispatcher.BeginInvoke(() => {
 | 
				
			||||||
 | 
					                    EventList.ItemsSource = list;
 | 
				
			||||||
 | 
					                    EventList.SelectedIndex = 0;
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            Mouse.OverrideCursor = null;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void EventList_SelectionChanged(object sender, RoutedEventArgs evt) {
 | 
					        private void EventList_SelectionChanged(object sender, RoutedEventArgs evt) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -738,7 +738,7 @@ namespace Elwig.Windows {
 | 
				
			|||||||
                PostalNoEmailInput.IsChecked == true ? 1 : 0;
 | 
					                PostalNoEmailInput.IsChecked == true ? 1 : 0;
 | 
				
			||||||
            var emailMode = EmailAllInput.IsChecked == true ? 2 : EmailWishInput.IsChecked == true ? 1 : 0;
 | 
					            var emailMode = EmailAllInput.IsChecked == true ? 2 : EmailWishInput.IsChecked == true ? 1 : 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            double printNum = printMode == 3 ? PostalAllCount : printMode == 2 ? PostalWishCount : printMode == 2 ? PostalNoEmailCount : 0;
 | 
					            double printNum = printMode == 3 ? PostalAllCount : printMode == 2 ? PostalWishCount : printMode == 1 ? PostalNoEmailCount : 0;
 | 
				
			||||||
            double emailNum = emailMode == 2 ? EmailAllCount : emailMode == 1 ? EmailWishCount : 0;
 | 
					            double emailNum = emailMode == 2 ? EmailAllCount : emailMode == 1 ? EmailWishCount : 0;
 | 
				
			||||||
            double totalNum = printNum + emailNum;
 | 
					            double totalNum = printNum + emailNum;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -954,7 +954,7 @@ namespace Elwig.Windows {
 | 
				
			|||||||
            AvaiableDocumentsList.SelectedIndex = 1;
 | 
					            AvaiableDocumentsList.SelectedIndex = 1;
 | 
				
			||||||
            if (AvaiableDocumentsList.SelectedItem is not string s || SelectedDocs.Any(d => d.Type == DocType.DeliveryConfirmation))
 | 
					            if (AvaiableDocumentsList.SelectedItem is not string s || SelectedDocs.Any(d => d.Type == DocType.DeliveryConfirmation))
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            SelectedDocs.Add(new(DocType.DeliveryConfirmation, s, (Year, DocumentNonDeliverersInput.IsChecked == true)));
 | 
					            SelectedDocs.Add(new(DocType.DeliveryConfirmation, s, Year));
 | 
				
			||||||
            SelectedDocumentsList.SelectedIndex = SelectedDocs.Count - 1;
 | 
					            SelectedDocumentsList.SelectedIndex = SelectedDocs.Count - 1;
 | 
				
			||||||
            RecipientsDeliveryMembersInput.IsChecked = true;
 | 
					            RecipientsDeliveryMembersInput.IsChecked = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,6 +31,17 @@
 | 
				
			|||||||
                    </MenuItem.Icon>
 | 
					                    </MenuItem.Icon>
 | 
				
			||||||
                </MenuItem>
 | 
					                </MenuItem>
 | 
				
			||||||
                <Separator/>
 | 
					                <Separator/>
 | 
				
			||||||
 | 
					                <MenuItem Header="Datenbank sichern..." Click="Menu_Database_Backup_Click">
 | 
				
			||||||
 | 
					                    <MenuItem.Icon>
 | 
				
			||||||
 | 
					                        <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text=""/>
 | 
				
			||||||
 | 
					                    </MenuItem.Icon>
 | 
				
			||||||
 | 
					                </MenuItem>
 | 
				
			||||||
 | 
					                <MenuItem Header="Datenbank wiederherstellen..." Click="Menu_Database_Restore_Click">
 | 
				
			||||||
 | 
					                    <MenuItem.Icon>
 | 
				
			||||||
 | 
					                        <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text=""/>
 | 
				
			||||||
 | 
					                    </MenuItem.Icon>
 | 
				
			||||||
 | 
					                </MenuItem>
 | 
				
			||||||
 | 
					                <Separator/>
 | 
				
			||||||
                <MenuItem Header="Abfragen stellen" Click="Menu_Database_Query_Click">
 | 
					                <MenuItem Header="Abfragen stellen" Click="Menu_Database_Query_Click">
 | 
				
			||||||
                    <MenuItem.Icon>
 | 
					                    <MenuItem.Icon>
 | 
				
			||||||
                        <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text=""/>
 | 
					                        <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text=""/>
 | 
				
			||||||
@@ -61,7 +72,7 @@
 | 
				
			|||||||
                </MenuItem>
 | 
					                </MenuItem>
 | 
				
			||||||
            </MenuItem>
 | 
					            </MenuItem>
 | 
				
			||||||
            <MenuItem x:Name="HelpMenu" Header="Hilfe">
 | 
					            <MenuItem x:Name="HelpMenu" Header="Hilfe">
 | 
				
			||||||
                <MenuItem Header="Über">
 | 
					                <MenuItem Header="Über" Click="Menu_Help_About_Click">
 | 
				
			||||||
                    <MenuItem.Icon>
 | 
					                    <MenuItem.Icon>
 | 
				
			||||||
                        <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text=""/>
 | 
					                        <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text=""/>
 | 
				
			||||||
                    </MenuItem.Icon>
 | 
					                    </MenuItem.Icon>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -59,6 +59,11 @@ namespace Elwig.Windows {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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) {
 | 
					        private async void Menu_Help_Update_Click(object sender, RoutedEventArgs evt) {
 | 
				
			||||||
            await App.CheckForUpdates(true);
 | 
					            await App.CheckForUpdates(true);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -146,6 +151,50 @@ namespace Elwig.Windows {
 | 
				
			|||||||
            Mouse.OverrideCursor = null;
 | 
					            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 DownloadButton_Click(object sender, RoutedEventArgs evt) {
 | 
					        private async void DownloadButton_Click(object sender, RoutedEventArgs evt) {
 | 
				
			||||||
            if (App.Config.SyncUrl == null)
 | 
					            if (App.Config.SyncUrl == null)
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
@@ -202,16 +251,25 @@ namespace Elwig.Windows {
 | 
				
			|||||||
                        .Where(d => d.Year == Utils.CurrentLastSeason && d.ZwstId == App.ZwstId)
 | 
					                        .Where(d => d.Year == Utils.CurrentLastSeason && d.ZwstId == App.ZwstId)
 | 
				
			||||||
                        .Include(d => d.Parts)
 | 
					                        .Include(d => d.Parts)
 | 
				
			||||||
                        .ThenInclude(p => p.PartModifiers)
 | 
					                        .ThenInclude(p => p.PartModifiers)
 | 
				
			||||||
 | 
					                        .Include(d => d.Parts)
 | 
				
			||||||
 | 
					                        .ThenInclude(p => p.Kg)
 | 
				
			||||||
 | 
					                        .ThenInclude(k => k!.Gl)
 | 
				
			||||||
                        .OrderBy(d => d.DateString)
 | 
					                        .OrderBy(d => d.DateString)
 | 
				
			||||||
                        .ThenBy(d => d.TimeString)
 | 
					                        .ThenBy(d => d.TimeString)
 | 
				
			||||||
                        .ThenBy(d => d.LsNr)
 | 
					                        .ThenBy(d => d.LsNr)
 | 
				
			||||||
                        .AsSplitQuery()
 | 
					                        .AsSplitQuery()
 | 
				
			||||||
                        .ToListAsync();
 | 
					                        .ToListAsync();
 | 
				
			||||||
 | 
					                    var wbKgs = deliveries
 | 
				
			||||||
 | 
					                        .SelectMany(d => d.Parts)
 | 
				
			||||||
 | 
					                        .Where(p => p.Kg != null)
 | 
				
			||||||
 | 
					                        .Select(p => p.Kg!)
 | 
				
			||||||
 | 
					                        .DistinctBy(k => k.KgNr)
 | 
				
			||||||
 | 
					                        .ToList();
 | 
				
			||||||
                    if (deliveries.Count == 0) {
 | 
					                    if (deliveries.Count == 0) {
 | 
				
			||||||
                        MessageBox.Show("Es gibt keine Lieferungen, die hochgeladen werden können!", "Lieferungen hochladen",
 | 
					                        MessageBox.Show("Es gibt keine Lieferungen, die hochgeladen werden können!", "Lieferungen hochladen",
 | 
				
			||||||
                            MessageBoxButton.OK, MessageBoxImage.Error);
 | 
					                            MessageBoxButton.OK, MessageBoxImage.Error);
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        await ElwigData.Export(path, deliveries, [$"{Utils.CurrentLastSeason}", $"Zweigstelle {App.BranchName}"]);
 | 
					                        await ElwigData.Export(path, deliveries, wbKgs, [$"{Utils.CurrentLastSeason}", $"Zweigstelle {App.BranchName}"]);
 | 
				
			||||||
                        await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
 | 
					                        await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
 | 
				
			||||||
                        MessageBox.Show($"Hochladen von {deliveries.Count:N0} Lieferungen erfolgreich!", "Lieferungen hochladen",
 | 
					                        MessageBox.Show($"Hochladen von {deliveries.Count:N0} Lieferungen erfolgreich!", "Lieferungen hochladen",
 | 
				
			||||||
                            MessageBoxButton.OK, MessageBoxImage.Information);
 | 
					                            MessageBoxButton.OK, MessageBoxImage.Information);
 | 
				
			||||||
@@ -234,21 +292,23 @@ namespace Elwig.Windows {
 | 
				
			|||||||
            await Task.Run(async () => {
 | 
					            await Task.Run(async () => {
 | 
				
			||||||
                try {
 | 
					                try {
 | 
				
			||||||
                    var data = await Utils.GetExportMetaData(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
 | 
					                    var data = await Utils.GetExportMetaData(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
 | 
				
			||||||
                    var file = data
 | 
					                    var files = data
 | 
				
			||||||
                        .Select(f => new {
 | 
					                        .Select(f => new {
 | 
				
			||||||
                            Name = f!["name"]!.AsValue().GetValue<string>(),
 | 
					                            Name = f!["name"]!.AsValue().GetValue<string>(),
 | 
				
			||||||
                            Timestamp = f!["modified"] != null && DateTime.TryParseExact(f!["modified"]!.AsValue().GetValue<string>(), "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt) ? dt : (DateTime?)null,
 | 
					                            Timestamp = f!["modified"] != null && DateTime.TryParseExact(f!["modified"]!.AsValue().GetValue<string>(), "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt) ? dt : (DateTime?)null,
 | 
				
			||||||
                            Url = f!["url"]!.AsValue().GetValue<string>(),
 | 
					                            Url = f!["url"]!.AsValue().GetValue<string>(),
 | 
				
			||||||
                            Size = f!["size"]!.AsValue().GetValue<long>(),
 | 
					                            Size = f!["size"]!.AsValue().GetValue<long>(),
 | 
				
			||||||
                        })
 | 
					                        })
 | 
				
			||||||
                        .Where(f => f.Name == "database.sqlite3.zip")
 | 
					                        .Where(f => f.Name.StartsWith("database.") && f.Name.EndsWith(".zip"))
 | 
				
			||||||
                        .FirstOrDefault();
 | 
					                        .OrderBy(f => f.Size)
 | 
				
			||||||
 | 
					                        .ToList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (file == null) {
 | 
					                    if (files.Count == 0) {
 | 
				
			||||||
                        MessageBox.Show("Die Datenbank wurde noch nicht vom Hauptgerät hochgeladen!", "Datenbank herunterladen",
 | 
					                        MessageBox.Show("Die Datenbank wurde noch nicht vom Hauptgerät hochgeladen!", "Datenbank herunterladen",
 | 
				
			||||||
                            MessageBoxButton.OK, MessageBoxImage.Error);
 | 
					                            MessageBoxButton.OK, MessageBoxImage.Error);
 | 
				
			||||||
                        return;
 | 
					                        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",
 | 
					                    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);
 | 
					                        MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
 | 
				
			||||||
@@ -292,8 +352,8 @@ namespace Elwig.Windows {
 | 
				
			|||||||
            Mouse.OverrideCursor = Cursors.Wait;
 | 
					            Mouse.OverrideCursor = Cursors.Wait;
 | 
				
			||||||
            await Task.Run(async () => {
 | 
					            await Task.Run(async () => {
 | 
				
			||||||
                try {
 | 
					                try {
 | 
				
			||||||
                    var path = Path.Combine(App.TempPath, "database.sqlite3.zip");
 | 
					                    var path = Path.Combine(App.TempPath, "database.sql.zip");
 | 
				
			||||||
                    ElwigData.ExportDatabase(path);
 | 
					                    await Database.ExportSql(path, true);
 | 
				
			||||||
                    await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
 | 
					                    await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
 | 
				
			||||||
                    MessageBox.Show($"Hochladen der gesamten Datenbank erfolgreich!", "Datenbank hochladen",
 | 
					                    MessageBox.Show($"Hochladen der gesamten Datenbank erfolgreich!", "Datenbank hochladen",
 | 
				
			||||||
                        MessageBoxButton.OK, MessageBoxImage.Information);
 | 
					                        MessageBoxButton.OK, MessageBoxImage.Information);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,7 +38,7 @@ namespace Elwig.Windows {
 | 
				
			|||||||
            IList<DbColumn> header;
 | 
					            IList<DbColumn> header;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            using (var cnx = await AppDbContext.ConnectAsync()) {
 | 
					            using (var cnx = await AppDbContext.ConnectAsync()) {
 | 
				
			||||||
                var cmd = cnx.CreateCommand();
 | 
					                using var cmd = cnx.CreateCommand();
 | 
				
			||||||
                cmd.CommandText = sqlQuery;
 | 
					                cmd.CommandText = sqlQuery;
 | 
				
			||||||
                using var reader = await cmd.ExecuteReaderAsync();
 | 
					                using var reader = await cmd.ExecuteReaderAsync();
 | 
				
			||||||
                header = await reader.GetColumnSchemaAsync();
 | 
					                header = await reader.GetColumnSchemaAsync();
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										40
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								README.md
									
									
									
									
									
								
							@@ -5,3 +5,43 @@ Elwig
 | 
				
			|||||||
**El**ektronische **Wi**nzer**g**enossenschaftsverwaltung (Electronic Management for Vintners' Cooperatives).
 | 
					**El**ektronische **Wi**nzer**g**enossenschaftsverwaltung (Electronic Management for Vintners' Cooperatives).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Further information may be found on [the website](https://elwig.at).
 | 
					Further information may be found on [the website](https://elwig.at).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					About
 | 
				
			||||||
 | 
					=====
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Product:** Elwig  
 | 
				
			||||||
 | 
					**Description:** Electronic Management for Vintners' Cooperatives  
 | 
				
			||||||
 | 
					**Type:** ERP system  
 | 
				
			||||||
 | 
					**Version:** 1.0.1.5 ([Changelog](./CHANGELOG.md))  
 | 
				
			||||||
 | 
					**License:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)  
 | 
				
			||||||
 | 
					**Website:** https://elwig.at/  
 | 
				
			||||||
 | 
					**Source code:** https://git.necronda.net/winzer/elwig  
 | 
				
			||||||
 | 
					**Developement period:** 2022–2025
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Technology Stack:**  
 | 
				
			||||||
 | 
					Language: C#  
 | 
				
			||||||
 | 
					Framework: Windows Presentation Framework (WPF)  
 | 
				
			||||||
 | 
					Database: [SQLite](https://sqlite.org/)  
 | 
				
			||||||
 | 
					PDF creation: [WeasyPrint](https://weasyprint.org/), [RazorLight](https://github.com/toddams/RazorLight), [PdfiumViewer](https://github.com/pvginkel/PdfiumViewer)  
 | 
				
			||||||
 | 
					Packaging: [WiX Toolset](https://www.firegiant.com/wixtoolset/)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Über
 | 
				
			||||||
 | 
					====
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Produkt:** Elwig  
 | 
				
			||||||
 | 
					**Beschreibung:** Elektronische Winzergenossenschaftsverwaltung  
 | 
				
			||||||
 | 
					**Typ:** Warenwirtschaftssystem (ERP-System)  
 | 
				
			||||||
 | 
					**Version:** 1.0.1.5 ([Änderungsprotokoll](./CHANGELOG.md))  
 | 
				
			||||||
 | 
					**Lizenz:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)  
 | 
				
			||||||
 | 
					**Website:** https://elwig.at/  
 | 
				
			||||||
 | 
					**Quellcode:** https://git.necronda.net/winzer/elwig  
 | 
				
			||||||
 | 
					**Entwicklungszeitraum:** 2022–2025
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Verwendete Technologien:**  
 | 
				
			||||||
 | 
					Programmiersprache: C#  
 | 
				
			||||||
 | 
					Framework: Windows Presentation Framework (WPF)  
 | 
				
			||||||
 | 
					Datenbank: [SQLite](https://sqlite.org/)  
 | 
				
			||||||
 | 
					PDF-Erstellung: [WeasyPrint](https://weasyprint.org/), [RazorLight](https://github.com/toddams/RazorLight), [PdfiumViewer](https://github.com/pvginkel/PdfiumViewer)  
 | 
				
			||||||
 | 
					Paketierung: [WiX Toolset](https://www.firegiant.com/wixtoolset/)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@
 | 
				
			|||||||
  </Target>
 | 
					  </Target>
 | 
				
			||||||
  <ItemGroup>
 | 
					  <ItemGroup>
 | 
				
			||||||
    <ProjectReference Include="..\Installer\Installer.wixproj" />
 | 
					    <ProjectReference Include="..\Installer\Installer.wixproj" />
 | 
				
			||||||
    <PackageReference Include="WixToolset.Bal.wixext" Version="6.0.1" />
 | 
					    <PackageReference Include="WixToolset.Bal.wixext" Version="6.0.2" />
 | 
				
			||||||
    <PackageReference Include="WixToolset.Util.wixext" Version="6.0.1" />
 | 
					    <PackageReference Include="WixToolset.Util.wixext" Version="6.0.2" />
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
</Project>
 | 
					</Project>
 | 
				
			||||||
@@ -22,9 +22,9 @@
 | 
				
			|||||||
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
 | 
					    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
 | 
				
			||||||
    <PackageReference Include="Appium.WebDriver" Version="4.4.5" />
 | 
					    <PackageReference Include="Appium.WebDriver" Version="4.4.5" />
 | 
				
			||||||
    <PackageReference Include="NReco.PdfRenderer" Version="1.6.0" />
 | 
					    <PackageReference Include="NReco.PdfRenderer" Version="1.6.0" />
 | 
				
			||||||
    <PackageReference Include="NUnit" Version="4.3.2" />
 | 
					    <PackageReference Include="NUnit" Version="4.4.0" />
 | 
				
			||||||
    <PackageReference Include="NUnit3TestAdapter" Version="5.0.0" />
 | 
					    <PackageReference Include="NUnit3TestAdapter" Version="5.1.0" />
 | 
				
			||||||
    <PackageReference Include="NUnit.Analyzers" Version="4.9.2">
 | 
					    <PackageReference Include="NUnit.Analyzers" Version="4.10.0">
 | 
				
			||||||
      <PrivateAssets>all</PrivateAssets>
 | 
					      <PrivateAssets>all</PrivateAssets>
 | 
				
			||||||
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
 | 
					      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
 | 
				
			||||||
    </PackageReference>
 | 
					    </PackageReference>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -188,7 +188,7 @@ namespace Tests.UnitTests.HelperTests {
 | 
				
			|||||||
                Assert.That(payment["GV"],  Is.EqualTo(10_000));
 | 
					                Assert.That(payment["GV"],  Is.EqualTo(10_000));
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await b.Calculate(false, false, false);
 | 
					            await b.Calculate(true, false, false, false);
 | 
				
			||||||
            var prices = await GetMemberDeliveryPrices(year, mgnr);
 | 
					            var prices = await GetMemberDeliveryPrices(year, mgnr);
 | 
				
			||||||
            Assert.Multiple(() => {
 | 
					            Assert.Multiple(() => {
 | 
				
			||||||
                Assert.That(prices, Has.Count.EqualTo(7));
 | 
					                Assert.That(prices, Has.Count.EqualTo(7));
 | 
				
			||||||
@@ -234,7 +234,7 @@ namespace Tests.UnitTests.HelperTests {
 | 
				
			|||||||
                Assert.That(payment["GV"],  Is.EqualTo(8_000));
 | 
					                Assert.That(payment["GV"],  Is.EqualTo(8_000));
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await b.Calculate(true, false, false);
 | 
					            await b.Calculate(true, true, false, false);
 | 
				
			||||||
            var prices = await GetMemberDeliveryPrices(year, mgnr);
 | 
					            var prices = await GetMemberDeliveryPrices(year, mgnr);
 | 
				
			||||||
            Assert.Multiple(() => {
 | 
					            Assert.Multiple(() => {
 | 
				
			||||||
                Assert.That(prices, Has.Count.EqualTo(6));
 | 
					                Assert.That(prices, Has.Count.EqualTo(6));
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1 +1 @@
 | 
				
			|||||||
curl --fail -s -L "https://elwig.at/files/create.sql?v=32" -u "elwig:ganzGeheim123!" -o "Resources\Sql\Create.sql"
 | 
					curl --fail -s -L "https://elwig.at/files/create.sql?v=33" -u "elwig:ganzGeheim123!" -o "Resources\Sql\Create.sql"
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user