Compare commits
	
		
			17 Commits
		
	
	
		
			ce3da46dd6
			...
			v1.0.0.4
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 4b8cd2a0d7 | |||
| 104798d4f2 | |||
| 4653a4f129 | |||
| 07f9a0f522 | |||
| 7e21d9e2e4 | |||
| 4e2eca295d | |||
| 1f165055c1 | |||
| 7b953fa73e | |||
| d3157e4d48 | |||
| 73fe4531cc | |||
| b6c03892b1 | |||
| 91950ad9fd | |||
| 77c3f388e7 | |||
| 6f3e1a8905 | |||
| 466c8a322c | |||
| ab7c7404e2 | |||
| ea3ea5ca83 | 
| @@ -1,7 +1,7 @@ | |||||||
| name: Deploy | name: Deploy | ||||||
| on: | on: | ||||||
|   push: |   push: | ||||||
|     tags: ["v[0-9]+.[0-9]+.[0-9]+"] |     tags: ["v[0-9]+.[0-9]+.[0-9]+.[0-9]+"] | ||||||
| jobs: | jobs: | ||||||
|   deploy: |   deploy: | ||||||
|     name: Build and Deploy |     name: Build and Deploy | ||||||
|   | |||||||
							
								
								
									
										102
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -2,6 +2,108 @@ | |||||||
| Changelog | Changelog | ||||||
| ========= | ========= | ||||||
|  |  | ||||||
|  | [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} | ||||||
|  | --------------------------------------------- | ||||||
|  |  | ||||||
|  | ### Sonstiges {#v1.0.0.2-misc} | ||||||
|  |  | ||||||
|  | * Explizit native SQLite-Bibliothek hinzugefügt. (77c3f388e7) | ||||||
|  |  | ||||||
|  | [v1.0.0.2]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.0.2 | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | [v1.0.0.1][v1.0.0.1] (2025-08-05) {#v1.0.0.1} | ||||||
|  | --------------------------------------------- | ||||||
|  |  | ||||||
|  | ### Sonstiges {#v1.0.0.1-misc} | ||||||
|  |  | ||||||
|  | * Abhängigkeiten aktualisiert. (466c8a322c) | ||||||
|  | * Angepasste Fehlermeldung, wenn Importieren fehlschlägt. (ab7c7404e2) | ||||||
|  |  | ||||||
|  | [v1.0.0.1]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.0.1 | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | [v1.0.0.0][v1.0.0.0] (2025-07-30) {#v1.0.0.0} | ||||||
|  | --------------------------------------------- | ||||||
|  |  | ||||||
|  | ### Neue Funktionen {#v1.0.0.0-features} | ||||||
|  |  | ||||||
|  | * Es wird nun gespeichert, wann Mitglieder/Flächenbindungen/Lieferungen importiert bzw. exporiert wurden. (4234c7f994) | ||||||
|  | * Im Auszahlungsvarianten-Fenster (`PaymentVariantsWindow`) wird der Auszahlungsbetrag für nicht-festgesetzte Varianten nun überschlagsmäßig angezeigt. (913129f155) | ||||||
|  | * Wenn eine Variante im Auszahlungsvariante-Fenster (`ChartWindow`) gespeichert wird, wird sie nun automatisch auch berechnet. (c6c8fd9b68) | ||||||
|  | * Überall wo Weinsorten vorkommen werden diese jetzt rot oder grün eingefärbt angezeigt. ([#61][i61]) | ||||||
|  | * Das Flächenbindungs-Fenster (`AreaComAdminWindow`) wurde überarbeitet. (53d82604a1, 267aa3d47c, [#64][i64]) | ||||||
|  | * Auf allen Dokumenten wird der Bio-Kontrollstellen-Code in der Fußzeile angeführt (sofern dieser in den Stammdaten gesetzt wurde). (fad1e28c06) | ||||||
|  |  | ||||||
|  | ### Behobene Fehler {#v1.0.0.0-bugfixes} | ||||||
|  |  | ||||||
|  | * Bei Traubengutschriften (`CreditNote`) wurde der Rebelzuschlag immer angeführt, auch wenn dieser in der zugrundeliegenden Berechnung nicht berücksichtigt wurde. (336aef5c70) | ||||||
|  | * In den Variantendaten einer Auszahlungsvariante (`PaymentVariantSummary`) wurde neben den Spalten _gebunden_ und _ungebunden_ noch _attributlos gebunden_ hinzugefügt. Ohne diese neue Spalte wären die Werte der anderen beiden falsch. ([#58][i58]) | ||||||
|  | * Das erste Laden des Ausgangs-Protokoll-Fensters (`MailLogWindow`) hat nicht funktioniert. ([#65][i65]) | ||||||
|  | * Im Lieferungen-Fenster (`DeliveryAdminWindow`) und im Mitglieder-Fenster (`MemberAdminWindow`) wird der Tool-Tip für Gewicht/Gradation mit korrektem Layout angezeigt. (e9f389b885) | ||||||
|  | * Bei Traubengutschriften (`CreditNote`) werden längere Freitexte vollständig angezeigt statt abgeschnitten. ([#62][i62]) | ||||||
|  |  | ||||||
|  | ### Sonstiges {#v1.0.0.0-misc} | ||||||
|  |  | ||||||
|  | * Im Auszahlungsvarianten-Fenster (`PaymentVariantsWindow`) wird angeführt, dass "Zu- und Abschläge bei Lieferungen" auch den Rebelzuschlag umfassen. (0dff3986b7) | ||||||
|  | * Im gesamten Program wird an passenden Stellen statt "Gewicht" nun "Menge" verwendet. (0f3ce39f35) | ||||||
|  | * Verbesserungen der Code-Qualität und der Leistung. ([#59][i59], b580e1bf79, 623f55f5b0, 3493ff6df1, 267797b55d, 41811925be, [#57][i57], 38d0ff969d, e6746f76b1, 7e9a27c75d) | ||||||
|  | * Im Mitglieder-Fenster (`MemberAdminWindow`) werden die Flächenbindungen jetzt für die momentane Saison angezeigt statt für das aktuelle Jahr. (953532cae4) | ||||||
|  | * Abhängigkeiten aktualisiert. (4b27ebf81b, 7153bfab6f, 9b48242f0e) | ||||||
|  | * GPLv3 Lizenz hinzugefügt. (93e920f61a) | ||||||
|  |  | ||||||
|  | [v1.0.0.0]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.0.0 | ||||||
|  | [i57]: https://git.necronda.net/winzer/elwig/issues/57 | ||||||
|  | [i58]: https://git.necronda.net/winzer/elwig/issues/58 | ||||||
|  | [i59]: https://git.necronda.net/winzer/elwig/issues/59 | ||||||
|  | [i61]: https://git.necronda.net/winzer/elwig/issues/61 | ||||||
|  | [i62]: https://git.necronda.net/winzer/elwig/issues/62 | ||||||
|  | [i64]: https://git.necronda.net/winzer/elwig/issues/64 | ||||||
|  | [i65]: https://git.necronda.net/winzer/elwig/issues/65 | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| [v0.13.9][v0.13.9] (2025-05-05) {#v0.13.9} | [v0.13.9][v0.13.9] (2025-05-05) {#v0.13.9} | ||||||
| ------------------------------------------ | ------------------------------------------ | ||||||
|   | |||||||
| @@ -1,20 +1,21 @@ | |||||||
| using System; |  | ||||||
| using System.Data; |  | ||||||
| using System.Linq; |  | ||||||
| using System.Windows; |  | ||||||
| using System.IO; |  | ||||||
| using Elwig.Helpers; |  | ||||||
| using Elwig.Helpers.Weighing; |  | ||||||
| using System.Collections.Generic; |  | ||||||
| using System.Windows.Threading; |  | ||||||
| using System.Reflection; |  | ||||||
| using Elwig.Helpers.Printing; |  | ||||||
| using Elwig.Windows; |  | ||||||
| using Elwig.Dialogs; | using Elwig.Dialogs; | ||||||
| using System.Threading.Tasks; | using Elwig.Helpers; | ||||||
| using Elwig.Helpers.Billing; | using Elwig.Helpers.Billing; | ||||||
|  | using Elwig.Helpers.Export; | ||||||
|  | using Elwig.Helpers.Printing; | ||||||
|  | using Elwig.Helpers.Weighing; | ||||||
| using Elwig.Models.Entities; | using Elwig.Models.Entities; | ||||||
|  | using Elwig.Windows; | ||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Data; | ||||||
|  | using System.IO; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Reflection; | ||||||
| using System.Text; | using System.Text; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  | using System.Windows; | ||||||
|  | using System.Windows.Threading; | ||||||
|  |  | ||||||
| namespace Elwig { | namespace Elwig { | ||||||
|     public partial class App : Application { |     public partial class App : Application { | ||||||
| @@ -85,7 +86,7 @@ namespace Elwig { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         protected override async void OnStartup(StartupEventArgs evt) { |         protected override async void OnStartup(StartupEventArgs evt) { | ||||||
|             Version = new Version(typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion.Split('+')[0] ?? "0.0.0"); |             Version = new Version(typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion.Split('+')[0] ?? "0.0.0.0"); | ||||||
|  |  | ||||||
|             try { |             try { | ||||||
|                 await AppDbUpdater.CheckDb(); |                 await AppDbUpdater.CheckDb(); | ||||||
| @@ -239,6 +240,17 @@ namespace Elwig { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         public static async Task ReplaceDatabase(string filename) { | ||||||
|  |             try { | ||||||
|  |                 await ElwigData.ImportDatabase(filename); | ||||||
|  |                 MessageBox.Show("Das Ersetzen war erfolgreich!\n\nBitte starten Sie Elwig neu!", "Datenbank ersetzen", MessageBoxButton.OK, MessageBoxImage.Information); | ||||||
|  |                 ForceShutdown = true; | ||||||
|  |                 Current.Shutdown(); | ||||||
|  |             } catch (Exception exc) { | ||||||
|  |                 MessageBox.Show("Fehler beim Ersetzen:\n\n" + exc.Message, "Datenbank ersetzen", MessageBoxButton.OK, MessageBoxImage.Error); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|         private static T FocusWindow<T>(Func<T> constructor, Predicate<T>? selector = null) where T : Window { |         private static T FocusWindow<T>(Func<T> constructor, Predicate<T>? selector = null) where T : Window { | ||||||
|             foreach (Window w in CurrentApp.Windows) { |             foreach (Window w in CurrentApp.Windows) { | ||||||
|                 if (w is T t && (selector == null || selector(t))) { |                 if (w is T t && (selector == null || selector(t))) { | ||||||
|   | |||||||
| @@ -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>0.13.9</Version> |     <Version>1.0.0.4</Version> | ||||||
|     <SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages> |     <SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages> | ||||||
|     <AllowUnsafeBlocks>true</AllowUnsafeBlocks> |     <AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||||||
|     <ApplicationManifest>app.manifest</ApplicationManifest> |     <ApplicationManifest>app.manifest</ApplicationManifest> | ||||||
| @@ -25,17 +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.7" /> |     <PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.8" /> | ||||||
|     <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.7" /> |     <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.8" /> | ||||||
|     <PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="9.0.7" /> |     <PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="9.0.8" /> | ||||||
|     <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3351.48" /> |     <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3405.78" /> | ||||||
|     <PackageReference Include="NJsonSchema" Version="11.3.2" /> |     <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.55" /> | ||||||
|     <PackageReference Include="System.IO.Ports" Version="9.0.7" /> |     <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.1" /> | ||||||
|     <PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.7" /> |     <PackageReference Include="System.IO.Hashing" Version="9.0.8" /> | ||||||
|  |     <PackageReference Include="System.IO.Ports" Version="9.0.8" /> | ||||||
|  |     <PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.8" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|  |  | ||||||
| </Project> | </Project> | ||||||
|   | |||||||
| @@ -28,10 +28,10 @@ namespace Elwig.Helpers { | |||||||
|             await UpdateDbSchema(cnx, (int)(schemaVers / 100), RequiredSchemaVersion); |             await UpdateDbSchema(cnx, (int)(schemaVers / 100), RequiredSchemaVersion); | ||||||
|  |  | ||||||
|             var userVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA user_version") ?? 0; |             var userVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA user_version") ?? 0; | ||||||
|             var v = new Version((int)(userVers >> 24), (int)((userVers >> 16) & 0xFF), (int)(userVers & 0xFFFF)); |             var v = new Version((int)(userVers >> 24), (int)((userVers >> 16) & 0xFF), (int)((userVers >> 8) & 0xFF), (int)(userVers & 0xFF)); | ||||||
|  |  | ||||||
|             if (App.Version > v) { |             if (App.Version > v) { | ||||||
|                 long vers = (App.Version.Major << 24) | (App.Version.Minor << 16) | App.Version.Build; |                 long vers = (App.Version.Major << 24) | (App.Version.Minor << 16) | (App.Version.Build << 8) | App.Version.Revision; | ||||||
|                 await AppDbContext.ExecuteBatch(cnx, $"PRAGMA user_version = {vers}"); |                 await AppDbContext.ExecuteBatch(cnx, $"PRAGMA user_version = {vers}"); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -41,6 +41,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 +53,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 +64,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, | ||||||
| @@ -75,6 +79,7 @@ namespace Elwig.Helpers.Export { | |||||||
|                 foreach (var filename in filenames) { |                 foreach (var filename in filenames) { | ||||||
|                     // TODO read encrypted files |                     // TODO read encrypted files | ||||||
|                     using var zip = ZipFile.Open(filename, ZipArchiveMode.Read); |                     using var zip = ZipFile.Open(filename, ZipArchiveMode.Read); | ||||||
|  |                     await zip.CheckIntegrity(); | ||||||
|  |  | ||||||
|                     var version = zip.GetEntry("version"); |                     var version = zip.GetEntry("version"); | ||||||
|                     using (var reader = new StreamReader(version!.Open(), Utils.UTF8)) { |                     using (var reader = new StreamReader(version!.Open(), Utils.UTF8)) { | ||||||
| @@ -96,13 +101,28 @@ namespace Elwig.Helpers.Export { | |||||||
|                         areaComCount, areaComFilters != null ? string.Join(" / ", areaComFilters) : null, |                         areaComCount, areaComFilters != null ? string.Join(" / ", areaComFilters) : null, | ||||||
|                         deliveryCount, deliveryFilters != null ? string.Join(" / ", deliveryFilters) : null)); |                         deliveryCount, deliveryFilters != null ? string.Join(" / ", deliveryFilters) : null)); | ||||||
|  |  | ||||||
|                     data.Add(new([], [], [], [], [], [], [], new([], [], new() { |                     data.Add(new([], [], [], [], [], [], [], new([], [], [], [], new() { | ||||||
|                         ["member"] = [], |                         ["member"] = [], | ||||||
|                         ["area_commitment"] = [], |                         ["area_commitment"] = [], | ||||||
|                         ["delivery"] = [], |                         ["delivery"] = [], | ||||||
|                     }))); |                     }))); | ||||||
|                     var r = data[^1]; |                     var r = data[^1]; | ||||||
|  |  | ||||||
|  |                     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); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |  | ||||||
|                     var membersJson = zip.GetEntry("members.json"); |                     var membersJson = zip.GetEntry("members.json"); | ||||||
|                     if (membersJson != null) { |                     if (membersJson != null) { | ||||||
|                         using var reader = new StreamReader(membersJson.Open(), Utils.UTF8); |                         using var reader = new StreamReader(membersJson.Open(), Utils.UTF8); | ||||||
| @@ -125,10 +145,9 @@ namespace Elwig.Helpers.Export { | |||||||
|                         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 (areaCom, wbrd, timestamps) = obj.ToAreaCom(kgs, currentWbRde); |                             var (areaCom, wbrd, timestamps) = obj.ToAreaCom(currentWbRde); | ||||||
|                             r.AreaCommitments.Add(areaCom); |                             r.AreaCommitments.Add(areaCom); | ||||||
|                             if (wbrd != null) { |                             if (wbrd != null) { | ||||||
|                                 currentWbRde[wbrd.KgNr].Add(wbrd); |  | ||||||
|                                 r.Riede.Add(wbrd); |                                 r.Riede.Add(wbrd); | ||||||
|                             } |                             } | ||||||
|                             if (timestamps.HasValue) |                             if (timestamps.HasValue) | ||||||
| @@ -142,10 +161,11 @@ namespace Elwig.Helpers.Export { | |||||||
|                         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 (d, parts, mods, rde, timestamps) = obj.ToDelivery(currentLsNrs, currentDids, kgs, currentWbRde); | ||||||
|                             r.Deliveries.Add(d); |                             r.Deliveries.Add(d); | ||||||
|                             r.DeliveryParts.AddRange(parts); |                             r.DeliveryParts.AddRange(parts); | ||||||
|                             r.Modifiers.AddRange(mods); |                             r.Modifiers.AddRange(mods); | ||||||
|  |                             r.Riede.AddRange(rde); | ||||||
|                             if (timestamps.HasValue) |                             if (timestamps.HasValue) | ||||||
|                                 r.Timestamps["delivery"].Add((d.Year, d.DId, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt)); |                                 r.Timestamps["delivery"].Add((d.Year, d.DId, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt)); | ||||||
|                         } |                         } | ||||||
| @@ -156,12 +176,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)) | ||||||
| @@ -215,6 +241,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))); | ||||||
| @@ -334,9 +366,9 @@ namespace Elwig.Helpers.Export { | |||||||
|                     "Importieren erfolgreich", |                     "Importieren erfolgreich", | ||||||
|                     MessageBoxButton.OK, MessageBoxImage.Information); |                     MessageBoxButton.OK, MessageBoxImage.Information); | ||||||
|             } catch (Exception exc) { |             } catch (Exception exc) { | ||||||
|                 var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message; |                 var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\nEvtl. muss die Datenbank manuell auf dieses Gerät kopieren werden.\n\n" + exc.Message; | ||||||
|                 if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message; |                 if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message; | ||||||
|                 MessageBox.Show(str, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); |                 MessageBox.Show(str, "Fehler beim Importieren", MessageBoxButton.OK, MessageBoxImage.Error); | ||||||
|             } |             } | ||||||
|             GC.Collect(); |             GC.Collect(); | ||||||
|             GC.WaitForPendingFinalizers(); |             GC.WaitForPendingFinalizers(); | ||||||
| @@ -351,26 +383,53 @@ 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; } | ||||||
| @@ -391,6 +450,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(), | ||||||
| @@ -411,6 +476,13 @@ namespace Elwig.Helpers.Export { | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 // 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(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); | ||||||
| @@ -435,6 +507,34 @@ namespace Elwig.Helpers.Export { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         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, | ||||||
| @@ -580,22 +680,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) { | ||||||
|                 var rde = riede[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)"); |                 var rde = riede.GetValueOrDefault(kgnr, []); | ||||||
|                 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>(); | ||||||
| @@ -608,7 +709,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>(), | ||||||
| @@ -639,7 +740,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()), | ||||||
| @@ -664,7 +765,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); | ||||||
| @@ -675,6 +776,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, | ||||||
| @@ -686,7 +788,25 @@ 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 => { | ||||||
|  |                 var kgnr = p["kgnr"]!.AsValue().GetValue<int>(); | ||||||
|  |                 var ried = p["ried"]?.AsValue().GetValue<string>(); | ||||||
|  |                 WbRd? rd = null; | ||||||
|  |                 if (ried != null) { | ||||||
|  |                     var rde = riede.GetValueOrDefault(kgnr, []); | ||||||
|  |                     rd = rde.FirstOrDefault(r => r.Name == ried); | ||||||
|  |                     if (rd == null) { | ||||||
|  |                         rd = new WbRd { | ||||||
|  |                             KgNr = kgnr, | ||||||
|  |                             RdNr = (rde.Count == 0 ? 1 : rde.Max(r => r.RdNr)) + 1, | ||||||
|  |                             Name = ried, | ||||||
|  |                         }; | ||||||
|  |                         rde.Add(rd); | ||||||
|  |                         riede[rd.KgNr] = rde; | ||||||
|  |                         wbRde.Add(rd); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 return new DeliveryPart { | ||||||
|                     Year = year, |                     Year = year, | ||||||
|                     DId = did, |                     DId = did, | ||||||
|                     DPNr = p["dpnr"]!.AsValue().GetValue<int>(), |                     DPNr = p["dpnr"]!.AsValue().GetValue<int>(), | ||||||
| @@ -698,7 +818,7 @@ namespace Elwig.Helpers.Export { | |||||||
|                     QualId = p["qualid"]!.AsValue().GetValue<string>(), |                     QualId = p["qualid"]!.AsValue().GetValue<string>(), | ||||||
|                     HkId = p["hkid"]!.AsValue().GetValue<string>(), |                     HkId = p["hkid"]!.AsValue().GetValue<string>(), | ||||||
|                     KgNr = p["kgnr"]?.AsValue().GetValue<int>(), |                     KgNr = p["kgnr"]?.AsValue().GetValue<int>(), | ||||||
|                 RdNr = p["rdnr"]?.AsValue().GetValue<int>(), |                     RdNr = rd?.RdNr ?? p["rdnr"]?.AsValue().GetValue<int>(), | ||||||
|                     IsNetWeight = p["net_weight"]!.AsValue().GetValue<bool>(), |                     IsNetWeight = p["net_weight"]!.AsValue().GetValue<bool>(), | ||||||
|                     IsManualWeighing = p["manual_weighing"]!.AsValue().GetValue<bool>(), |                     IsManualWeighing = p["manual_weighing"]!.AsValue().GetValue<bool>(), | ||||||
|                     Comment = p["comment"]?.AsValue().GetValue<string>(), |                     Comment = p["comment"]?.AsValue().GetValue<string>(), | ||||||
| @@ -711,12 +831,14 @@ namespace Elwig.Helpers.Export { | |||||||
|                     ScaleId = p["scale_id"]?.AsValue().GetValue<string>(), |                     ScaleId = p["scale_id"]?.AsValue().GetValue<string>(), | ||||||
|                     WeighingData = p["weighing_data"]?.AsObject().ToJsonString(JsonOpts), |                     WeighingData = p["weighing_data"]?.AsObject().ToJsonString(JsonOpts), | ||||||
|                     WeighingReason = p["weighing_reason"]?.AsValue().GetValue<string>(), |                     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,5 +1,7 @@ | |||||||
| using System; | using System; | ||||||
| using System.IO; | using System.IO; | ||||||
|  | using System.IO.Compression; | ||||||
|  | using System.IO.Hashing; | ||||||
| using System.Net.Http; | using System.Net.Http; | ||||||
| using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||||||
| using System.Threading; | using System.Threading; | ||||||
| @@ -95,5 +97,16 @@ namespace Elwig.Helpers { | |||||||
|             await download.CopyToAsync(destination, 81920, relativeProgress, cancellationToken); |             await download.CopyToAsync(destination, 81920, relativeProgress, cancellationToken); | ||||||
|             progress.Report(100.0); |             progress.Report(100.0); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         public static async Task CheckIntegrity(this ZipArchive zip) { | ||||||
|  |             var crc = new Crc32(); | ||||||
|  |             foreach (var entry in zip.Entries) { | ||||||
|  |                 crc.Reset(); | ||||||
|  |                 using var stream = entry.Open(); | ||||||
|  |                 await crc.AppendAsync(stream); | ||||||
|  |                 if (crc.GetCurrentHashAsUInt32() != entry.Crc32) | ||||||
|  |                     throw new InvalidDataException($"CRC-32 mismatch in '{entry.FullName}'"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -457,7 +457,7 @@ namespace Elwig.Helpers { | |||||||
|                 url = "https://sync.elwig.at/" + url[25..]; |                 url = "https://sync.elwig.at/" + url[25..]; | ||||||
|             if (!url.EndsWith('/')) url += "/"; |             if (!url.EndsWith('/')) url += "/"; | ||||||
|             using var client = GetHttpClient(username, password, accept: "application/json"); |             using var client = GetHttpClient(username, password, accept: "application/json"); | ||||||
|             var content = new StreamContent(new FileStream(zip, FileMode.Open, FileAccess.Read)); |             using var content = new StreamContent(new FileStream(zip, FileMode.Open, FileAccess.Read)); | ||||||
|             content.Headers.ContentType = new("application/zip"); |             content.Headers.ContentType = new("application/zip"); | ||||||
|             using var res = await client.PutAsync(url + Path.GetFileName(zip), content); |             using var res = await client.PutAsync(url + Path.GetFileName(zip), content); | ||||||
|             res.EnsureSuccessStatusCode(); |             res.EnsureSuccessStatusCode(); | ||||||
| @@ -498,10 +498,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 +526,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 +561,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) { | ||||||
|   | |||||||
| @@ -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!; | ||||||
|   | |||||||
| @@ -727,13 +727,22 @@ namespace Elwig.Services { | |||||||
|                     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 +758,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); | ||||||
|   | |||||||
| @@ -509,13 +509,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 +542,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); | ||||||
|   | |||||||
| @@ -350,7 +350,7 @@ namespace Elwig.Windows { | |||||||
|             ViewModel.IsGebunden = null; |             ViewModel.IsGebunden = null; | ||||||
|             InitialDefaultInputs(); |             InitialDefaultInputs(); | ||||||
|  |  | ||||||
|             WineQualityLevelInput.IsEnabled = false; |             //WineQualityLevelInput.IsEnabled = false;  // disable wine quality level input in Übernahme | ||||||
|             ValidateRequiredInputs(); |             ValidateRequiredInputs(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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,9 +13,10 @@ 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 () => { | ||||||
|  |                 var list = Utils.GetLogEntries() | ||||||
|                   .Select(e => new { |                   .Select(e => new { | ||||||
|                       Event = e, |                       Event = e, | ||||||
|                       Lines = e.Message.Split('\n').ToArray(), |                       Lines = e.Message.Split('\n').ToArray(), | ||||||
| @@ -32,7 +35,12 @@ namespace Elwig.Windows { | |||||||
|                   }) |                   }) | ||||||
|                   .OrderByDescending(e => e.Event.TimeGenerated) |                   .OrderByDescending(e => e.Event.TimeGenerated) | ||||||
|                   .ToList(); |                   .ToList(); | ||||||
|  |                 await App.MainDispatcher.BeginInvoke(() => { | ||||||
|  |                     EventList.ItemsSource = list; | ||||||
|                     EventList.SelectedIndex = 0; |                     EventList.SelectedIndex = 0; | ||||||
|  |                 }); | ||||||
|  |             }); | ||||||
|  |             Mouse.OverrideCursor = null; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private void EventList_SelectionChanged(object sender, RoutedEventArgs evt) { |         private void EventList_SelectionChanged(object sender, RoutedEventArgs evt) { | ||||||
|   | |||||||
| @@ -41,6 +41,17 @@ | |||||||
|                         <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text=""/> |                         <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text=""/> | ||||||
|                     </MenuItem.Icon> |                     </MenuItem.Icon> | ||||||
|                 </MenuItem> |                 </MenuItem> | ||||||
|  |                 <Separator/> | ||||||
|  |                 <MenuItem x:Name="Menu_Database_Upload" Header="Datenbank hochladen..." Click="Menu_Database_Upload_Click"> | ||||||
|  |                     <MenuItem.Icon> | ||||||
|  |                         <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text=""/> | ||||||
|  |                     </MenuItem.Icon> | ||||||
|  |                 </MenuItem> | ||||||
|  |                 <MenuItem x:Name="Menu_Database_Download" Header="Datenbank herunterladen..." Click="Menu_Database_Download_Click" IsEnabled="False"> | ||||||
|  |                     <MenuItem.Icon> | ||||||
|  |                         <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text=""/> | ||||||
|  |                     </MenuItem.Icon> | ||||||
|  |                 </MenuItem> | ||||||
|             </MenuItem> |             </MenuItem> | ||||||
|             <MenuItem Header="Waage"> |             <MenuItem Header="Waage"> | ||||||
|                 <MenuItem Header="Datum und Uhrzeit setzen" Click="Menu_Scale_SetDateTime_Click"> |                 <MenuItem Header="Datum und Uhrzeit setzen" Click="Menu_Scale_SetDateTime_Click"> | ||||||
|   | |||||||
| @@ -25,12 +25,14 @@ namespace Elwig.Windows { | |||||||
|         public MainWindow() { |         public MainWindow() { | ||||||
|             InitializeComponent(); |             InitializeComponent(); | ||||||
|             var v = Assembly.GetExecutingAssembly().GetName().Version; |             var v = Assembly.GetExecutingAssembly().GetName().Version; | ||||||
|             VersionField.Text = "Version: " + (v == null ? "?" : $"{v.Major}.{v.Minor}.{v.Build}") + $" – {App.BranchName}"; |             VersionField.Text = $"Version: {v?.ToString() ?? "?"} – {App.BranchName}"; | ||||||
|             if (App.Client.Client == null) VersionField.Text += " (Unbekannt)"; |             if (App.Client.Client == null) VersionField.Text += " (Unbekannt)"; | ||||||
|             Menu_Help_Update.IsEnabled = App.Config.UpdateUrl != null; |             Menu_Help_Update.IsEnabled = App.Config.UpdateUrl != null; | ||||||
|             Menu_Help_Smtp.IsEnabled = App.Config.Smtp != null; |             Menu_Help_Smtp.IsEnabled = App.Config.Smtp != null; | ||||||
|             DownloadButton.Visibility = App.Config.SyncUrl != null ? Visibility.Visible : Visibility.Hidden; |             DownloadButton.Visibility = App.Config.SyncUrl != null ? Visibility.Visible : Visibility.Hidden; | ||||||
|             UploadButton.Visibility = App.Config.SyncUrl != null ? Visibility.Visible : Visibility.Hidden; |             UploadButton.Visibility = App.Config.SyncUrl != null ? Visibility.Visible : Visibility.Hidden; | ||||||
|  |             Menu_Database_Upload.IsEnabled = App.Config.SyncUrl != null; | ||||||
|  |             Menu_Database_Download.IsEnabled = App.Config.SyncUrl != null; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private void Window_Loaded(object sender, RoutedEventArgs evt) { |         private void Window_Loaded(object sender, RoutedEventArgs evt) { | ||||||
| @@ -156,7 +158,7 @@ namespace Elwig.Windows { | |||||||
|                             Name = f!["name"]!.AsValue().GetValue<string>(), |                             Name = f!["name"]!.AsValue().GetValue<string>(), | ||||||
|                             Timestamp = f!["timestamp"] != null && DateTime.TryParseExact(f!["timestamp"]!.AsValue().GetValue<string>(), "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt) ? dt : (DateTime?)null, |                             Timestamp = f!["timestamp"] != null && DateTime.TryParseExact(f!["timestamp"]!.AsValue().GetValue<string>(), "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt) ? dt : (DateTime?)null, | ||||||
|                             ZwstId = f!["meta"]?["zwstid"]?.AsValue().GetValue<string>() ?? f!["zwstid"]?.AsValue().GetValue<string>(), |                             ZwstId = f!["meta"]?["zwstid"]?.AsValue().GetValue<string>() ?? f!["zwstid"]?.AsValue().GetValue<string>(), | ||||||
|                             Device = f!["meta"]?["device"]!.AsValue().GetValue<string>(), |                             Device = f!["meta"]?["device"]?.AsValue().GetValue<string>(), | ||||||
|                             Url = f!["url"]!.AsValue().GetValue<string>(), |                             Url = f!["url"]!.AsValue().GetValue<string>(), | ||||||
|                             Size = f!["size"]!.AsValue().GetValue<long>(), |                             Size = f!["size"]!.AsValue().GetValue<long>(), | ||||||
|                         }) |                         }) | ||||||
| @@ -178,11 +180,11 @@ namespace Elwig.Windows { | |||||||
|                     } |                     } | ||||||
|                     await ElwigData.Import(paths, ElwigData.ImportMode.FromBranches); |                     await ElwigData.Import(paths, ElwigData.ImportMode.FromBranches); | ||||||
|                 } catch (HttpRequestException exc) { |                 } catch (HttpRequestException exc) { | ||||||
|                     MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); |                     MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Daten herunterladen", MessageBoxButton.OK, MessageBoxImage.Error); | ||||||
|                 } catch (TaskCanceledException exc) { |                 } catch (TaskCanceledException exc) { | ||||||
|                     MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); |                     MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Daten herunterladen", MessageBoxButton.OK, MessageBoxImage.Error); | ||||||
|                 } catch (Exception exc) { |                 } catch (Exception exc) { | ||||||
|                     MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); |                     MessageBox.Show(exc.Message, "Daten herunterladen", MessageBoxButton.OK, MessageBoxImage.Error); | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|             Mouse.OverrideCursor = null; |             Mouse.OverrideCursor = null; | ||||||
| @@ -200,16 +202,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); | ||||||
| @@ -225,6 +236,87 @@ namespace Elwig.Windows { | |||||||
|             Mouse.OverrideCursor = null; |             Mouse.OverrideCursor = null; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         private async void Menu_Database_Download_Click(object sender, RoutedEventArgs evt) { | ||||||
|  |             if (App.Config.SyncUrl == null) | ||||||
|  |                 return; | ||||||
|  |             Mouse.OverrideCursor = Cursors.Wait; | ||||||
|  |             await Task.Run(async () => { | ||||||
|  |                 try { | ||||||
|  |                     var data = await Utils.GetExportMetaData(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword); | ||||||
|  |                     var file = data | ||||||
|  |                         .Select(f => new { | ||||||
|  |                             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, | ||||||
|  |                             Url = f!["url"]!.AsValue().GetValue<string>(), | ||||||
|  |                             Size = f!["size"]!.AsValue().GetValue<long>(), | ||||||
|  |                         }) | ||||||
|  |                         .Where(f => f.Name == "database.sqlite3.zip") | ||||||
|  |                         .FirstOrDefault(); | ||||||
|  |  | ||||||
|  |                     if (file == null) { | ||||||
|  |                         MessageBox.Show("Die Datenbank wurde noch nicht vom Hauptgerät hochgeladen!", "Datenbank herunterladen", | ||||||
|  |                             MessageBoxButton.OK, MessageBoxImage.Error); | ||||||
|  |                         return; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     var res = MessageBox.Show($"Es wurde eine komprimierte Datenbank (ca. {file.Size / 1024 / 1024} MB) vom {file.Timestamp:dd.MM.yyyy, HH:mm} gefunden.\n\nWollen Sie wirklich die aktuelle Datenbank unwiederruflich\nlöschen und durch die gefundene ersetzen?\n\nDas kann zu Datenverlust führen!", "Datenbank herunterladen", | ||||||
|  |                         MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel); | ||||||
|  |                     if (res != MessageBoxResult.OK) | ||||||
|  |                         return; | ||||||
|  |  | ||||||
|  |                     var filename = Path.Combine(App.TempPath, file.Name); | ||||||
|  |                     using (var client = Utils.GetHttpClient(App.Config.SyncUsername, App.Config.SyncPassword)) { | ||||||
|  |                         using var stream = new FileStream(filename, FileMode.Create); | ||||||
|  |                         await client.DownloadAsync(file.Url, stream); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     res = MessageBox.Show("Die Datenbank wurde erfolgreich heruntergeladen!\n\nSoll die Datenbank wirklich unwiederruflich ersetzt werden?\n\nWenn Sie unsicher sind sprechen Sie sich mit dem Benutzer des Hauptgerätes ab!", "Datenbank herunterladen", | ||||||
|  |                         MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel); | ||||||
|  |                     if (res != MessageBoxResult.OK) | ||||||
|  |                         return; | ||||||
|  |  | ||||||
|  |                     await App.MainDispatcher.BeginInvoke(async () => { | ||||||
|  |                         await App.ReplaceDatabase(filename); | ||||||
|  |                     }); | ||||||
|  |                 } catch (HttpRequestException exc) { | ||||||
|  |                     MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Datenbank herunterladen", MessageBoxButton.OK, MessageBoxImage.Error); | ||||||
|  |                 } catch (TaskCanceledException exc) { | ||||||
|  |                     MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Datenbank herunterladen", MessageBoxButton.OK, MessageBoxImage.Error); | ||||||
|  |                 } catch (Exception exc) { | ||||||
|  |                     MessageBox.Show(exc.Message, "Datenbank herunterladen", MessageBoxButton.OK, MessageBoxImage.Error); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |             Mouse.OverrideCursor = null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private async void Menu_Database_Upload_Click(object sender, RoutedEventArgs evt) { | ||||||
|  |             if (App.Config.SyncUrl == null) | ||||||
|  |                 return; | ||||||
|  |  | ||||||
|  |             var res = MessageBox.Show("Sind Sie wirklich sicher, dass Sie die Datenbank dieses\nGerätes hochladen möchten? Das sollte nur vom Hauptgerät aus passieren!", "Datenbank hochladen", | ||||||
|  |                 MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel); | ||||||
|  |             if (res != MessageBoxResult.OK) | ||||||
|  |                 return; | ||||||
|  |  | ||||||
|  |             Mouse.OverrideCursor = Cursors.Wait; | ||||||
|  |             await Task.Run(async () => { | ||||||
|  |                 try { | ||||||
|  |                     var path = Path.Combine(App.TempPath, "database.sqlite3.zip"); | ||||||
|  |                     ElwigData.ExportDatabase(path); | ||||||
|  |                     await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword); | ||||||
|  |                     MessageBox.Show($"Hochladen der gesamten Datenbank erfolgreich!", "Datenbank hochladen", | ||||||
|  |                         MessageBoxButton.OK, MessageBoxImage.Information); | ||||||
|  |                 } catch (HttpRequestException exc) { | ||||||
|  |                     MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Datenbank hochladen", MessageBoxButton.OK, MessageBoxImage.Error); | ||||||
|  |                 } catch (TaskCanceledException exc) { | ||||||
|  |                     MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Datenbank hochladen", MessageBoxButton.OK, MessageBoxImage.Error); | ||||||
|  |                 } catch (Exception exc) { | ||||||
|  |                     MessageBox.Show(exc.Message, "Datenbank hochladen", MessageBoxButton.OK, MessageBoxImage.Error); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |             Mouse.OverrideCursor = null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         private void MemberAdminButton_Click(object sender, RoutedEventArgs evt) { |         private void MemberAdminButton_Click(object sender, RoutedEventArgs evt) { | ||||||
|             var w = new MemberAdminWindow(); |             var w = new MemberAdminWindow(); | ||||||
|             w.Show(); |             w.Show(); | ||||||
|   | |||||||
| @@ -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> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user