Compare commits

...
61 Commits
Author SHA1 Message Date
lorenz.stechauner a79631c586 Bump version to 1.1.1.0
Test / Run tests (push) Successful in 3m10s
Deploy / Build and Deploy (push) Successful in 1m52s
2026-08-18 23:58:31 +02:00
lorenz.stechauner 20b6e11b7f Export/Bki: Honor vollablieferer flag for member
Test / Run tests (push) Successful in 2m44s
2026-08-18 16:26:29 +02:00
lorenz.stechauner 77e812f5e8 Bump version 1.1.0.3
Deploy / Build and Deploy (push) Successful in 2m1s
2026-08-17 23:53:32 +02:00
lorenz.stechauner 58bfb4f56d Tests: Update dependencies
Test / Run tests (push) Successful in 2m4s
2026-08-17 23:38:46 +02:00
lorenz.stechauner e6e140dc08 Elwig: Update dependencies 2026-08-17 23:38:43 +02:00
lorenz.stechauner eaddf0c3cb SyncService: Add distinction main/non-main device
Test / Run tests (push) Successful in 2m26s
2026-08-17 23:29:31 +02:00
lorenz.stechauner fc5ad82686 Printing: Print all copies as one single print job 2026-08-17 17:50:33 +02:00
lorenz.stechauner b76367f73a Bump version to 1.1.0.2
Test / Run tests (push) Successful in 1m56s
Deploy / Build and Deploy (push) Successful in 1m51s
2026-08-11 18:27:22 +02:00
lorenz.stechauner 76ea3513ac MainWindow: Delay email sending
Test / Run tests (push) Successful in 2m4s
2026-08-11 18:25:16 +02:00
lorenz.stechauner 07b197958d Printing: Try to duplex print with manual dialog
Test / Run tests (push) Successful in 2m12s
2026-08-11 18:09:51 +02:00
lorenz.stechauner 470c6e09e5 Printing: Change fit-to-printable-are to scale 1:1
Test / Run tests (push) Successful in 2m40s
2026-08-11 17:25:49 +02:00
lorenz.stechauner 20b19775b1 Printing: Use –FPDF_RenderPage instead of FPDF_RenderPageBitmap to allow vector graphics
Test / Run tests (push) Successful in 2m38s
2026-08-10 18:35:49 +02:00
lorenz.stechauner f13a836ebd Printing: Fix slight translocation to the bottom right
Test / Run tests (push) Successful in 3m2s
2026-08-10 13:10:10 +02:00
lorenz.stechauner 2bf926c84c Bump version to 1.1.0.1
Test / Run tests (push) Successful in 2m15s
Deploy / Build and Deploy (push) Successful in 1m59s
2026-08-05 17:17:27 +02:00
lorenz.stechauner a8cc3c9cb9 Elwig: Update dependencies
Test / Run tests (push) Successful in 1m52s
2026-08-05 10:33:47 +02:00
lorenz.stechauner 88616b2c0b MemberAdminWindow: Add icons to deliveries and area commitments buttons
Test / Run tests (push) Successful in 1m58s
2026-08-03 14:20:24 +02:00
lorenz.stechauner e6712732cd [#82] BaseDataWindow: Fixed error handling after first error to allow saving changes
Test / Run tests (push) Successful in 2m8s
2026-08-03 14:18:20 +02:00
lorenz.stechauner cfabf99c99 [#83] PaymentVariantsWindow: Automatically calculate when saving variant
Test / Run tests (push) Successful in 1m57s
2026-08-03 11:26:28 +02:00
lorenz.stechauner 668030fbff Tests: Update dependencies 2026-08-03 11:26:28 +02:00
lorenz.stechauner b445928c4d Elwig: Update dependencies 2026-08-03 11:26:28 +02:00
lorenz.stechauner 2a3999f300 Weighing: Set Date/Time of scales at startup 2026-08-03 11:26:23 +02:00
lorenz.stechauner 309f118739 Weighing: Differentiate between scale and terminal 2026-08-03 10:56:36 +02:00
lorenz.stechauner 0d8cdd5d6e Bump version to 1.1.0.0
Test / Run tests (push) Successful in 1m54s
Deploy / Build and Deploy (push) Successful in 1m42s
2026-07-09 01:43:38 +02:00
lorenz.stechauner 2350b55cea Tests: Update dependencies
Test / Run tests (push) Successful in 2m6s
2026-07-09 01:01:38 +02:00
lorenz.stechauner e8789aff37 Elwig: Update dependencies 2026-07-09 01:01:38 +02:00
lorenz.stechauner 918555f2b8 [#20] MemberAdminWindow: Add MemberBusinessSharesAdminWindow to manage member shares 2026-07-09 01:01:28 +02:00
lorenz.stechauner 1169ba5101 ChartWindow: Fix warnings and avoid null-forgiving operator
Test / Run tests (push) Successful in 2m10s
2026-07-06 19:11:39 +02:00
lorenz.stechauner 76da392f5f [#20][#80] MemberAdminWindow: Update business share status bar tool tip
Test / Run tests (push) Successful in 2m1s
2026-07-06 18:18:46 +02:00
lorenz.stechauner 000117dc67 CreditNoteData: Add AccountingNr of member
Test / Run tests (push) Successful in 3m6s
2026-07-06 11:03:38 +02:00
lorenz.stechauner 314a0d37a8 [#20] MemberDataSheet: Show members business share history 2026-07-03 12:02:44 +02:00
lorenz.stechauner 765edbdae6 [#20][#80] MemberAdminWindow: Add functionality to edit other business shares 2026-07-03 12:02:44 +02:00
lorenz.stechauner 9a0a422c8a [#20] Entities/MemberHistory: Add DeductYear to simplify billing 2026-07-03 12:02:37 +02:00
lorenz.stechauner a71c41af5c AppDbContext: Add Member.Branch AutoInclude to fix MemberDataSheet
Test / Run tests (push) Successful in 2m52s
2026-07-02 12:17:32 +02:00
lorenz.stechauner 62346a63a8 [#80] Elwig: Add client parameter to switch between simple and red/white business share mode
Test / Run tests (push) Successful in 2m20s
2026-07-01 17:09:55 +02:00
lorenz.stechauner feee6ad1ec [#20][#80] Elwig: Update member_history and add different types of shares
Test / Run tests (push) Successful in 2m0s
2026-07-01 16:22:01 +02:00
lorenz.stechauner 4ebe07f579 AppDbUpdater: Fix handling of foreign key errors
Test / Run tests (push) Successful in 3m4s
2026-07-01 00:36:04 +02:00
lorenz.stechauner beacba6bd9 Services: Add Utils.RunForeground() to factor Task.Run and try-catch-Blocks out
Test / Run tests (push) Successful in 2m2s
2026-06-30 02:32:24 +02:00
lorenz.stechauner 69efca1cc3 Tests: Use Assert.EnterMultipleScope instead of Assert.Multiple
Test / Run tests (push) Successful in 2m6s
2026-06-29 02:33:36 +02:00
lorenz.stechauner fcd0555e4d Elwig: Add InteractionService to centrally manage MessageBox and SaveFileDialogs
Test / Run tests (push) Successful in 2m50s
2026-06-29 02:28:34 +02:00
lorenz.stechauner b93e987685 Bump version to 1.0.5.6
Test / Run tests (push) Successful in 2m24s
Deploy / Build and Deploy (push) Successful in 1m58s
2026-06-25 13:07:10 +02:00
lorenz.stechauner 1ad97a78ff ClientParameters: Add client Seewinkel
Test / Run tests (push) Successful in 3m13s
2026-06-25 12:33:20 +02:00
lorenz.stechauner 1261be001c BaseDataWindow: Remove transactions inside *Save() functions to avoid nesting them 2026-06-25 12:32:46 +02:00
lorenz.stechauner 15de07a4c3 BillingData: Remove quality entry when using –FromGraphEntries
Test / Run tests (push) Successful in 2m34s
2026-06-24 15:51:37 +02:00
lorenz.stechauner 5cbad9a195 Bump version to 1.0.5.5
Test / Run tests (push) Successful in 6m43s
Deploy / Build and Deploy (push) Successful in 2m10s
2026-06-24 14:36:44 +02:00
lorenz.stechauner 4e027a9add Elwig: Update dependencies
Test / Run tests (push) Successful in 4m40s
2026-06-24 14:24:25 +02:00
lorenz.stechauner f32ff945ec BillingData: Preserve all settings when using FromGraphEntries() 2026-06-24 14:24:15 +02:00
lorenz.stechauner ba3f66591e BilllingVariant: Allow for credits with negative value 2026-06-24 13:20:46 +02:00
lorenz.stechauner 3f65b2350b PaymentVariantService: Fix crash in GenerateEbics()
Test / Run tests (push) Successful in 2m48s
2026-06-24 10:36:28 +02:00
lorenz.stechauner 7a6a77419b Bump version to 1.0.5.4
Test / Run tests (push) Successful in 2m17s
Deploy / Build and Deploy (push) Successful in 2m6s
2026-06-22 20:23:46 +02:00
lorenz.stechauner 7a993585c3 Elwig: Update dependencies
Test / Run tests (push) Successful in 2m3s
2026-06-22 20:08:18 +02:00
lorenz.stechauner d0ce377d92 AppDbContext: Fix FetchMembers when using for specific MgNr
Test / Run tests (push) Successful in 2m59s
2026-06-22 20:06:34 +02:00
lorenz.stechauner f8b3147c72 Tests: Update dependencies
Test / Run tests (push) Successful in 2m52s
2026-06-18 12:25:22 +02:00
lorenz.stechauner b4ab8349b4 Elwig: Update dependencies 2026-06-18 12:25:09 +02:00
lorenz.stechauner 5db5876905 Installer: Bundle and ship source code together with binaries
Test / Run tests (push) Successful in 3m14s
2026-05-01 15:10:19 +02:00
lorenz.stechauner 0617726139 Bump version to 1.0.5.3
Test / Run tests (push) Successful in 2m7s
Deploy / Build and Deploy (push) Successful in 2m13s
2026-04-29 11:42:05 +02:00
lorenz.stechauner 9dfe71d6d0 DeliveryConfirmation: Adapt spacing between rows to CreditNote
Test / Run tests (push) Successful in 2m52s
2026-04-29 11:37:09 +02:00
lorenz.stechauner 72155fc54e Documents: Fix error because of modifiers in CreditNote and DeliveryConfirmation 2026-04-29 11:29:16 +02:00
lorenz.stechauner 61bdb2f3b5 Bump version to 1.0.5.2
Test / Run tests (push) Successful in 2m15s
Deploy / Build and Deploy (push) Successful in 2m0s
2026-04-24 10:27:37 +02:00
lorenz.stechauner 2915f08a6b Elwig: Update dependencies
Test / Run tests (push) Successful in 3m7s
2026-04-24 09:49:06 +02:00
lorenz.stechauner 5c2fae1855 DeliveryAdminWindow: Fix attributes and cultivations in DeliveryPartList 2026-04-24 09:48:14 +02:00
lorenz.stechauner 95fabe3633 Bump version to 1.0.5.1
Test / Run tests (push) Successful in 2m25s
Deploy / Build and Deploy (push) Successful in 2m14s
2026-04-20 19:23:01 +02:00
125 changed files with 4242 additions and 2055 deletions
+1 -1
View File
@@ -2,7 +2,7 @@ name: Test
on:
push:
branches: ["**"]
paths: ["Elwig/**", "Tests/**", "Installer/Files/*.exe", ".gitea/workflows/test.yaml"]
paths: ["Elwig/**", "Tests/**", ".gitea/workflows/test.yaml"]
jobs:
test:
name: Run tests
+1
View File
@@ -7,3 +7,4 @@ Tests/Resources/Sql/Create.sql
*.exe
!WinziPrint.exe
*.sqlite3
*.zip
+195 -2
View File
@@ -2,6 +2,199 @@
Changelog
=========
[v1.1.1.0][v1.1.1.0] (2026-08-19) {#v1.1.1.0}
---------------------------------------------
### Sonstiges {#v1.1.1.0-misc}
* Bei der BKI-Traubentransportscheinliste werden Volllieferanten/Vollablieferer nun auch als solche markiert. (20b6e11b7f)
[v1.1.1.0]: https://git.necronda.net/winzer/elwig/releases/tag/v1.1.1.0
[v1.1.0.3][v1.1.0.3] (2026-08-17) {#v1.1.0.3}
---------------------------------------------
### Sonstiges {#v1.1.0.3-misc}
* Beim Drucken mehrerer Kopien einer PDF-Datei wird nun nur mehr ein einzelner Druck-Auftrag verwendet. (fc5ad82686)
* Beim Synchronisieren können Geräte als Hauptgerät festgelegt werden. (eaddf0c3cb)
* Abhängigkeiten aktualisiert. (e6e140dc08, 58bfb4f56d)
[v1.1.0.3]: https://git.necronda.net/winzer/elwig/releases/tag/v1.1.0.3
[v1.1.0.2][v1.1.0.2] (2026-08-11) {#v1.1.0.2}
---------------------------------------------
### Neue Funktionen {#v1.1.0.2-features}
* Es ist nun möglich zwischen dem Senden von E-Mail einige Sekunden zu warten (Konfigurationsdatei -> `[smtp]` -> `wait = 0.5`). (76ea3513ac)
### Behobene Fehler {#v1.1.0.2-bugfixes}
* Beim Drucken von Dokumenten waren die Seitenränder nicht korrekt. (f13a836ebd, 20b19775b1, 470c6e09e5, 07b197958d)
[v1.1.0.2]: https://git.necronda.net/winzer/elwig/releases/tag/v1.1.0.2
[v1.1.0.1][v1.1.0.1] (2026-08-05) {#v1.1.0.1}
---------------------------------------------
### Neue Funktionen {#v1.1.0.1-features}
* Auf Lieferscheinen sind nun Waage und Terminal angegeben. (309f118739)
* Beim Starten von Elwig wird Datum/Uhrzeit auf allen angeschlossenen Waagen gesetzt. (2a3999f300)
### Behobene Fehler {#v1.1.0.1-bugfixes}
* Im Stammdaten-Fenster (`BaseDataWindow`) war es nach einem Fehler beim Bearbeiten bis zum Schließen des Fensters nicht mehr möglich zu speichern. ([#82][i82])
### Sonstiges {#v1.1.0.1-misc}
* Im Auszahlungsvarianten-Fenster (`PaymentVariantsWindow`)...
* wurde auf 3 Knöpfen das Symbol "in neuem Fenster öffnen" hinzugefügt. (88616b2c0b)
* wird beim "Speichern" die Variante automatisch auch berechnet um Fehler/Missverständnisse zu vermeiden. ([#83][i83])
* Abhängigkeiten aktualisiert. (b445928c4d, 668030fbff, a8cc3c9cb9)
[v1.1.0.1]: https://git.necronda.net/winzer/elwig/releases/tag/v1.1.0.1
[i82]: https://git.necronda.net/winzer/elwig/issues/82
[i83]: https://git.necronda.net/winzer/elwig/issues/83
[v1.1.0.0][v1.1.0.0] (2026-07-09) {#v1.1.0.0}
---------------------------------------------
### Neue Funktionen {#v1.1.0.0-features}
* Per Opt-In Einstellung in den Stammdaten ist es nun möglich Mitgliederbewegungen und Verläufe von Geschäftsanteilen mit Elwig zu erfassen. ([#20][i20])
* Es wird nun unterstützt verschiedene Typen von Geschäftsanteilen pro Mitglied zu erfassen (normal, rot, weiß, ruhend). ([#80][i80])
### Behobene Fehler {#v1.1.0.0-bugfixes}
* Falls die Datenbank für die momentan verwndete Elwig-Version zu neu war kam es beim Überprüfen auf ein Update zu einem Fehler. (4ebe07f579)
* Im Stammdatenblatt (`MemberDataSheet`) wurde die Stamm-Zweigstelle nicht angeführt. (a71c41af5c)
### Sonstiges {#v1.1.0.0-misc}
* In der Buchungsliste (`CreditNoteData`) wird nun auch das Buchhaltungskonto der Mitglieder angeführt. (000117dc67)
* Viele kleine Verbesserungen der Code-Qualität. (u.a. fcd0555e4d, 69efca1cc3, beacba6bd9, 1169ba5101)
* Abhängigkeiten aktualisiert. (e8789aff37, 2350b55cea)
[v1.1.0.0]: https://git.necronda.net/winzer/elwig/releases/tag/v1.1.0.0
[i20]: https://git.necronda.net/winzer/elwig/issues/20
[i80]: https://git.necronda.net/winzer/elwig/issues/80
[v1.0.5.6][v1.0.5.6] (2026-06-25) {#v1.0.5.6}
---------------------------------------------
### Behobene Fehler {#v1.0.5.6-bugfixes}
* Beim Speichern von Auszahlungsvarianten wurden Einstellungen für Abwertungen nicht immer übernommen. (15de07a4c3)
* Im Stammdaten-Fenster (`BaseDataWindow`) war es nicht möglich Zweigstellen, Attribute, oder Bewirtschaftungsarten zu bearbeiten/erstellen/löschen. (1261be001c)
### Sonstiges {#v1.0.5.6-misc}
* Mandant `Seewinkel` hinzugefügt. (1ad97a78ff)
[v1.0.5.6]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.5.6
[v1.0.5.5][v1.0.5.5] (2026-06-24) {#v1.0.5.5}
---------------------------------------------
### Behobene Fehler {#v1.0.5.5-bugfixes}
* Beim Exportieren der Überweisungsdaten (EBICS) im Auszahlungsvarianten-Fenster (`PaymentVariantsWindow`) kam es zu einem Absturz. (3f65b2350b)
* Einige Einstellungen einer Auszahlungsvariante wurden vom Auszahlungsvariante-Fenster (`ChartWindow`) beim Speichern überschrieben. (f32ff945ec)
### Sonstiges {#v1.0.5.5-misc}
* Traubengutschriften mit negativem Betrag sind nun wieder möglich. (ba3f66591e)
* Abhängigkeiten aktualisiert. (4e027a9add)
[v1.0.5.5]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.5.5
[v1.0.5.4][v1.0.5.4] (2026-06-22) {#v1.0.5.4}
---------------------------------------------
### Behobene Fehler {#v1.0.5.4-bugfixes}
* Seit [v1.0.5.0](#v1.0.5.0) (2026-04-08) war es bei nicht-aktiven Mitgliedern nicht möglich Lieferungen oder Flächenbindungen anzusehen. (d0ce377d92)
### Sonstiges {#v1.0.5.4-misc}
* Bei der Installation wird nun auch der Quellcode mit ausgeliefert. (5db5876905)
* Abhängigkeiten aktualisiert. (b4ab8349b4, f8b3147c72, 7a993585c3)
[v1.0.5.4]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.5.4
[v1.0.5.3][v1.0.5.3] (2026-04-29) {#v1.0.5.3}
---------------------------------------------
### Behobene Fehler {#v1.0.5.3-bugfixes}
* Manche Anlieferungsbestätigungen und Traubengutschriften konnten nicht angezeigt werden. Dies lag an einem internen Fehler, der manchmal bei Zu-/Abschlägen auftrat. (72155fc54e, 9dfe71d6d0)
[v1.0.5.3]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.5.3
[v1.0.5.2][v1.0.5.2] (2026-04-24) {#v1.0.5.2}
---------------------------------------------
### Behobene Fehler {#v1.0.5.2-bugfixes}
* Im Lieferungen-Fenster (`DeliveryAdminWindow`) sind Attribut/Bewirtschaftungsart in der Liste für Teil-Lieferungen nicht angezeigt worden. (5c2fae1855)
### Sonstiges {#v1.0.5.2-misc}
* Abhängigkeiten aktualisiert. (2915f08a6b)
[v1.0.5.2]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.5.2
[v1.0.5.1][v1.0.5.1] (2026-04-20) {#v1.0.5.1}
---------------------------------------------
### Behobene Fehler {#v1.0.5.1-bugfixes}
* Zu-/Abschläge mit absolutem Wert (z.B. `€/kg`) wurden bei der Auszahlung nie mit der Menge multipliziert. (ea2b6db1fc)
* Massenaktionen im Lieferungen-Fenster (`DeliveryAdminWindow`) haben für Zu-/Abschläge nicht funktioniert. (4eac8cd629, cf48f005e2)
### Sonstiges {#v1.0.5.1-misc}
* Behebung von Kleinigkeiten im Layout bei Dokumenten (`CreditNote`, `PaymentVariantSummary`). (7edf395497, a852dbb242)
* Zuordnung von zwei Gemeinden vom Weinbaugebiet Neusiedlersee (`WLNS`) zu Leithaberg (`WLLB`). (2b3c293730)
* Abhängigkeiten aktualisiert. (20fd5d5826, 7861a15e2e)
[v1.0.5.1]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.5.1
[v1.0.5.0][v1.0.5.0] (2026-04-08) {#v1.0.5.0}
---------------------------------------------
@@ -12,7 +205,7 @@ Changelog
### Sonstiges {#v1.0.5.0-misc}
* Erhebliche Verbesserung der Leistung/Geschwindigkeit. ([#79][i79], e5e5e10cd7, 22fbb0772f)
* Am Stammdatenblatt (`MemberDataSheet`) wird nun als Datum das Datum der letzten Bearbeitung angegeben. (e5e5e10cd7)
* Am Stammdatenblatt (`MemberDataSheet`) wird nun als Datum das Datum der letzten Bearbeitung angegeben. (0a9c800116)
* Abhängigkeiten aktualisiert. (07d93dd384, ce1a55df86, cc6e31a006)
[v1.0.5.0]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.5.0
@@ -165,7 +358,7 @@ Changelog
[v1.0.3.0]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.3.0
[i50]: https://git.necronda.net/winzer/elwig/issues/50
[i66]: https://git.necronda.net/winzer/elwig/issues/60
[i66]: https://git.necronda.net/winzer/elwig/issues/66
[i71]: https://git.necronda.net/winzer/elwig/issues/71
[i73]: https://git.necronda.net/winzer/elwig/issues/73
+1 -8
View File
@@ -16,14 +16,7 @@
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" />
</dependentAssembly>
</dependency>
</assembly>
+29 -22
View File
@@ -5,6 +5,7 @@ using Elwig.Helpers.Export;
using Elwig.Helpers.Printing;
using Elwig.Helpers.Weighing;
using Elwig.Models.Entities;
using Elwig.Services;
using Elwig.Windows;
using Microsoft.EntityFrameworkCore;
using System;
@@ -91,11 +92,11 @@ namespace Elwig {
try {
await AppDbUpdater.CheckDb();
} catch (Exception e) {
} catch (Exception exc) {
if (Config.UpdateUrl != null && Utils.HasInternetConnectivity()) {
await CheckForUpdates();
}
MessageBox.Show($"Invalid Database:\n\n{e.Message}", "Invalid Database", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Fehlerhafte Datenbank", "Fehlerhafte Datenbank", exc);
Shutdown();
return;
}
@@ -110,8 +111,8 @@ namespace Elwig {
.GetAwaiter().GetResult();
try {
Client = new(ctx);
} catch (Exception e) {
MessageBox.Show($"Fehler beim Laden der Mandantendaten:\n\n{e.Message}", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
} catch (Exception exc) {
InteractionService.ShowException("Fehler", "Fehler beim Laden der Mandantendaten", exc);
Shutdown();
return;
}
@@ -144,19 +145,24 @@ namespace Elwig {
var list = new List<IScale>();
foreach (var s in Config.Scales) {
try {
list.Add(Scale.FromConfig(s));
} catch (Exception e) {
var scale = Scale.FromConfig(s);
if (scale is ICommandScale cmd) {
try {
await cmd.SetDateAndTime(DateTime.Now);
} catch { }
}
list.Add(scale);
} catch (Exception exc) {
list.Add(new InvalidScale(s.Id));
if (s.Required)
MessageBox.Show($"Verbindung zu Waage {s.Id} konnte nicht hergestellt werden:\n\n{e.Message}", "Waagenfehler",
MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Waagenfehler", $"Verbindung zu Waage {s.Id} konnte nicht hergestellt werden", exc);
}
}
Scales = list;
if (Config.Branch != null) {
if (!branches.ContainsKey(Config.Branch.ToLower())) {
MessageBox.Show("Ungültige Zweigstelle in Konfigurationsdatei!", "Ungültige Zweigstelle", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Ungültige Zweigstelle", "Ungültige Zweigstelle in Konfigurationsdatei!");
Shutdown();
} else {
SetBranch(branches[Config.Branch.ToLower()]);
@@ -164,7 +170,7 @@ namespace Elwig {
} else if (branches.Count == 1) {
SetBranch(branches.First().Value);
} else {
MessageBox.Show("Erkennen der lokalen Zweigstelle nicht möglich!", "Ungültige Zweigstelle", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Ungültige Zweigstelle", "Erkennen der lokalen Zweigstelle nicht möglich!");
Shutdown();
}
@@ -254,14 +260,13 @@ namespace Elwig {
if (Scales[i] is InvalidScale) {
try {
Scales[i] = Scale.FromConfig(s);
MessageBox.Show($"Verbindung zu Waage {s.Id} wieder hergestellt!", $"Waage {s.Id}", MessageBoxButton.OK, MessageBoxImage.Information);
} catch (Exception e) {
InteractionService.ShowInformation($"Waage {s.Id}", $"Verbindung zu Waage {s.Id} wieder hergestellt!");
} catch (Exception exc) {
Scales[i] = new InvalidScale(s.Id);
MessageBox.Show($"Verbindung zu Waage {s.Id} konnte nicht hergestellt werden:\n\n{e.Message}", "Waagenfehler",
MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Waagenfehler", $"Verbindung zu Waage {s.Id} konnte nicht hergestellt werden", exc);
}
} else if (Scales[i] is IEventScale) {
MessageBox.Show($"Verbindung zu Waage {s.Id} wieder hergestellt!", $"Waage {s.Id}", MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation($"Waage {s.Id}", $"Verbindung zu Waage {s.Id} wieder hergestellt!");
}
}
}
@@ -272,7 +277,7 @@ namespace Elwig {
for (var i = 0; i < Config.Scales.Count; i++) {
var s = Config.Scales[i];
if ((s.Connection?.StartsWith($"serial://{name}:") ?? false) && Scales[i] is not InvalidScale) {
MessageBox.Show($"Verbindung zu Waage {s.Id} unterbrochen!", $"Waagen {s.Id}", MessageBoxButton.OK, MessageBoxImage.Warning);
InteractionService.ShowWarning($"Waagen {s.Id}", $"Verbindung zu Waage {s.Id} unterbrochen!");
if (Scales[i] is ICommandScale) {
try {
Scales[i].Dispose();
@@ -307,11 +312,9 @@ namespace Elwig {
});
} else if (showAlert) {
if (latest == null) {
MessageBox.Show("Informationen konnten nicht abgerufen werden!", "Nach Updates suchen",
MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Nach Updates suchen", "Informationen konnten nicht abgerufen werden!");
} else {
MessageBox.Show($"Elwig ist auf dem aktuellsten Stand! (Version: {latest.Value.Version})", "Nach Updates suchen",
MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Nach Updates suchen", $"Elwig ist auf dem aktuellsten Stand! (Version: {latest.Value.Version})");
}
}
}
@@ -321,11 +324,11 @@ namespace Elwig {
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);
InteractionService.ShowInformation("Datenbank ersetzen", "Das Ersetzen war erfolgreich!\n\nBitte starten Sie Elwig neu!");
ForceShutdown = true;
Current.Shutdown();
} catch (Exception exc) {
MessageBox.Show("Fehler beim Ersetzen:\n\n" + exc.Message, "Datenbank ersetzen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Datenbank ersetzen", "Fehler beim Ersetzen", exc);
}
}
@@ -347,6 +350,10 @@ namespace Elwig {
return FocusWindow<DeliveryAdminWindow>(() => new(true), w => w.ViewModel.IsReceipt);
}
public static MemberBusinessSharesAdminWindow FocusMemberBusinessSharesWindow(int mgnr) {
return FocusWindow<MemberBusinessSharesAdminWindow>(() => new(mgnr), w => w.ViewModel.FilterMember?.MgNr == mgnr);
}
public static DeliveryAdminWindow FocusMemberDeliveries(int mgnr) {
return FocusWindow<DeliveryAdminWindow>(() => new(mgnr), w => w.ViewModel.FilterMember?.MgNr == mgnr);
}
+1 -1
View File
@@ -122,7 +122,7 @@ namespace Elwig.Controls {
private void OnSelectionChanged(object sender, SelectionChangedEventArgs evt) {
SelectItemsReverse();
var dmp = !string.IsNullOrEmpty(ListDisplayMemberPath) ? ListDisplayMemberPath : !string.IsNullOrEmpty(DisplayMemberPath) ? DisplayMemberPath : null;
var dmp = !string.IsNullOrWhiteSpace(ListDisplayMemberPath) ? ListDisplayMemberPath : !string.IsNullOrWhiteSpace(DisplayMemberPath) ? DisplayMemberPath : null;
if (SelectedItems.Count == ItemsSource.Cast<object>().Count() && AllItemsSelectedContent != null) {
AllItemsSelected = true;
} else if (SelectedItems.Count == 0) {
+2 -1
View File
@@ -1,4 +1,5 @@
using Elwig.Helpers;
using Elwig.Services;
using System;
using System.Diagnostics;
using System.IO;
@@ -51,7 +52,7 @@ namespace Elwig.Dialogs {
File.Delete(fileName);
return;
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
Process.Start(fileName);
DialogResult = true;
+18 -4
View File
@@ -18,6 +18,7 @@ namespace Elwig.Documents {
public class BusinessDocument : Document {
public Member Member;
public MemberHistoryPoint MemberShares;
public string? Location;
public bool IncludeSender = false;
public bool UseBillingAddress = false;
@@ -51,6 +52,7 @@ namespace Elwig.Documents {
public BusinessDocument(string title, Member m, DateOnly? dateFrom, bool includeSender = false) :
base(title) {
Member = m;
MemberShares = new(m.Shares, m.SharesRed, m.SharesWhite, m.SharesDormant);
Location = App.BranchLocation;
IncludeSender = includeSender;
DateFrom = dateFrom;
@@ -263,7 +265,7 @@ namespace Elwig.Documents {
}
protected Table NewBucketTable(
Season season, Dictionary<string, MemberBucket> buckets, int deliveredWeight,
Season season, Dictionary<string, MemberBucket> buckets, int deliveredWeightRed, int deliveredWeightWhite,
bool includeDelivery = true, bool includePayment = false,
bool isTiny = false, IEnumerable<string>? filter = null
) {
@@ -316,9 +318,21 @@ namespace Elwig.Documents {
.Where(b => !fbVars.Contains(b.Key))
.OrderBy(b => b.Value.Name);
tbl.AddCell(NewBucketTh("Gesamtlieferung lt. gez. GA", isTiny: isTiny));
tbl.AddCells(FormatRow(Member.BusinessShares * season.MinKgPerBusinessShare, Member.BusinessShares * season.MaxKgPerBusinessShare,
deliveredWeight, isGa: true, showPayment: includePayment, showArea: !includeDelivery, isTiny: isTiny));
if (MemberShares.Shares != 0 || (MemberShares.SharesRed == 0 && MemberShares.SharesWhite == 0)) {
tbl.AddCell(NewBucketTh("Gesamtlieferung lt. gez. GA", isTiny: isTiny));
tbl.AddCells(FormatRow(MemberShares.Shares * (season.MinKgPerShare ?? 0), MemberShares.Shares * (season.MaxKgPerShare ?? 0),
deliveredWeightRed + deliveredWeightWhite, isGa: true, showPayment: includePayment, showArea: !includeDelivery, isTiny: isTiny));
}
if (MemberShares.SharesRed != 0 || MemberShares.SharesWhite != 0) {
tbl.AddCell(NewBucketTh("Gesamtlieferung lt. gez. GA (rot)", isTiny: isTiny));
tbl.AddCells(FormatRow(MemberShares.SharesRed * (season.MinKgPerShareRed ?? season.MinKgPerShare ?? 0), MemberShares.SharesRed * (season.MaxKgPerShareRed ?? season.MaxKgPerShare ?? 0),
deliveredWeightRed, isGa: true, showPayment: includePayment, showArea: !includeDelivery, isTiny: isTiny));
tbl.AddCell(NewBucketTh("Gesamtlieferung lt. gez. GA (weiß)", isTiny: isTiny));
tbl.AddCells(FormatRow(MemberShares.SharesWhite * (season.MinKgPerShareWhite ?? season.MinKgPerShare ?? 0), MemberShares.SharesWhite * (season.MaxKgPerShareWhite ?? season.MaxKgPerShare ?? 0),
deliveredWeightWhite, isGa: true, showPayment: includePayment, showArea: !includeDelivery, isTiny: isTiny));
}
if (fbs.Any()) {
tbl.AddCell(NewBucketSubHdr("Flächenbindungen" + (vtr.Any() ? " (inkl. Verträge)" : "") + ":", includePayment ? 8 : 7, isTiny: isTiny));
+22 -14
View File
@@ -86,19 +86,24 @@ namespace Elwig.Documents {
}
if (ConsiderTotalPenalty) {
var total = _data.Rows.SelectMany(r => r.Buckets).Sum(b => b.Value);
var totalUnderDelivery = total - Member.BusinessShares * season.MinKgPerBusinessShare;
MemberTotalUnderDelivery = totalUnderDelivery < 0 ? totalUnderDelivery * (season.PenaltyPerKg ?? 0) - (season.PenaltyAmount ?? 0) - (season.PenaltyPerBsAmount * Math.Floor(-(decimal)totalUnderDelivery / season.MinKgPerBusinessShare) ?? 0) : 0;
var weights = _data.Rows.Select(r => new {r.Type, Weight = r.Buckets.Sum(b => b.Value)}).GroupBy(r => r.Type).ToDictionary(r => r.Key, g => g.Sum(r => r.Weight));
var red = weights.GetValueOrDefault("R", 0);
var white = weights.GetValueOrDefault("W", 0);
var total = red + white;
var underDeliveryTotal = total - Member.Shares * (season.MinKgPerShare ?? 0);
var underDeliveryRed = red - Member.SharesRed * (season.MinKgPerShareRed ?? season.MinKgPerShare ?? 0);
var underDeliveryWhite = white - Member.SharesWhite * (season.MinKgPerShareWhite ?? season.MinKgPerShare ?? 0);
MemberTotalUnderDelivery =
(underDeliveryTotal < 0 ? underDeliveryTotal * (season.PenaltyPerKg ?? 0) - (season.PenaltyAmount ?? 0) - (season.PenaltyPerShareAmount * Math.Floor(-(decimal)underDeliveryTotal / (season.MinKgPerShare ?? 0)) ?? 0) : 0) +
(underDeliveryRed < 0 ? underDeliveryRed * (season.PenaltyPerKg ?? 0) - (season.PenaltyAmount ?? 0) - (season.PenaltyPerShareAmount * Math.Floor(-(decimal)underDeliveryRed / (season.MinKgPerShareRed ?? season.MinKgPerShare ?? 0)) ?? 0) : 0) +
(underDeliveryWhite < 0 ? underDeliveryWhite * (season.PenaltyPerKg ?? 0) - (season.PenaltyAmount ?? 0) - (season.PenaltyPerShareAmount * Math.Floor(-(decimal)underDeliveryWhite / (season.MinKgPerShareWhite ?? season.MinKgPerShare ?? 0)) ?? 0) : 0);
if (total == 0)
MemberTotalUnderDelivery -= (season.PenaltyNone ?? 0) + (season.PenaltyPerBsNone * Member.BusinessShares ?? 0);
MemberTotalUnderDelivery -= (season.PenaltyNone ?? 0) + (season.PenaltyPerShareNone * (Member.Shares + Member.SharesRed + Member.SharesWhite) ?? 0);
}
if (ConsiderAutoBusinessShares) {
var fromDate = $"{season.Year}-01-01";
var toDate = $"{season.Year}-12-31";
MemberAutoBusinessShares = await ctx.MemberHistory
.Where(h => h.MgNr == Member.MgNr && h.Type == "auto")
.Where(h => h.DateString.CompareTo(fromDate) >= 0 && h.DateString.CompareTo(toDate) <= 0)
.SumAsync(h => h.BusinessShares);
.Where(h => h.ToMgNr == Member.MgNr && h.DeductYear == season.Year)
.SumAsync(h => h.Shares);
MemberAutoBusinessSharesAmount = MemberAutoBusinessShares * (-season.BusinessShareValue ?? 0);
}
if (ConsiderContractPenalties) {
@@ -197,7 +202,7 @@ namespace Elwig.Documents {
penalty += MemberTotalUnderDelivery;
}
if (MemberAutoBusinessSharesAmount != 0) {
tbl2.AddCells(FormatRow($"Autom. Nachz. von GA ({MemberAutoBusinessShares})", MemberAutoBusinessSharesAmount, add: true));
tbl2.AddCells(FormatRow($"Nachzeichnung von GA ({MemberAutoBusinessShares})", MemberAutoBusinessSharesAmount, add: true));
penalty += MemberAutoBusinessSharesAmount;
}
if (CustomPayment?.Amount != null) {
@@ -296,11 +301,14 @@ namespace Elwig.Documents {
if (p.QualId == "WEI") varibute.Add(Italic("abgew."));
sub.AddCell(NewCell(colspan: 2))
.AddCell(NewTd(varibute, colspan: 3).SetPaddingTop(0));
} else if (i - (rows - p.Modifiers.Length) < p.Modifiers.Length) {
sub.AddCell(NewCell(colspan: 2))
.AddCell(NewTd(p.Modifiers[i - (rows - p.Modifiers.Length)], 8, colspan: 3).SetPaddingTop(0).SetPaddingLeftMM(5));
} else {
sub.AddCell(NewCell(colspan: 5));
var idx = i - (rows - p.Modifiers.Length);
if (idx >= 0 && idx < p.Modifiers.Length) {
sub.AddCell(NewCell(colspan: 2))
.AddCell(NewTd(p.Modifiers[idx], 8, colspan: 3).SetPaddingTop(0).SetPaddingLeftMM(5));
} else {
sub.AddCell(NewCell(colspan: 5));
}
}
if (i < p.Buckets.Length) {
+18 -11
View File
@@ -18,7 +18,8 @@ namespace Elwig.Documents {
private readonly int _year;
public Season? Season;
public int MemberDeliveredWeight;
public int MemberDeliveredWeightRed;
public int MemberDeliveredWeightWhite;
public DeliveryConfirmationDeliveryData? Data;
public string? Text = App.Client.TextDeliveryConfirmation;
public Dictionary<string, MemberBucket> MemberBuckets = [];
@@ -36,10 +37,14 @@ namespace Elwig.Documents {
protected override async Task LoadData(AppDbContext ctx) {
await base.LoadData(ctx);
Season = await ctx.FetchSeasons(_year).SingleOrDefaultAsync() ?? throw new ArgumentException("Invalid season");
MemberDeliveredWeight = await ctx.Deliveries
var weights = await ctx.Deliveries
.Where(d => d.Year == Season.Year && d.MgNr == Member.MgNr)
.SelectMany(d => d.Parts)
.SumAsync(p => p.Weight);
.GroupBy(p => p.Variety.Type)
.ToDictionaryAsync(g => g.Key, g => g.Sum(p => p.Weight));
MemberDeliveredWeightRed = weights.GetValueOrDefault("R", 0);
MemberDeliveredWeightWhite = weights.GetValueOrDefault("W", 0);
MemberShares = await ctx.GetMemberHistory(Season.Year, Member.MgNr);
MemberBuckets = await ctx.GetMemberBuckets(Season.Year, Member.MgNr);
MemberStats = await AppDbContext.GetMemberStats(Season.Year, Member.MgNr);
Data ??= await DeliveryConfirmationDeliveryData.ForMember(ctx.DeliveryParts, Season.Year, Member);
@@ -63,7 +68,7 @@ namespace Elwig.Documents {
doc.Add(NewDeliveryListTable(Data));
doc.Add(NewWeightsTable(MemberStats)
.SetMarginTopMM(10).SetKeepTogether(true));
doc.Add(NewBucketTable(Season, MemberBuckets, MemberDeliveredWeight, includePayment: true)
doc.Add(NewBucketTable(Season, MemberBuckets, MemberDeliveredWeightRed, MemberDeliveredWeightWhite, includePayment: true)
.SetMarginTopMM(10).SetKeepTogether(true));
if (Text != null) {
@@ -114,13 +119,14 @@ namespace Elwig.Documents {
} else if (i == 1 && attr) {
sub.AddCell(NewCell(colspan: 2))
.AddCell(NewTd($"{p.Attribute}{(p.Attribute != null && p.Cultivation != null ? " / " : "")}{p.Cultivation}", 8, colspan: 2)
.SetPaddingsMM(0.125f, 1, 0.125f, 1))
.SetPaddingTop(0))
.AddCell(NewCell(colspan: 2));
} else {
sub.AddCell(NewCell(colspan: 2));
if (i - (rows - p.Modifiers.Length) < p.Modifiers.Length) {
sub.AddCell(NewTd(p.Modifiers[i - (rows - p.Modifiers.Length)], 8, colspan: 2)
.SetPaddingsMM(0.125f, 0, 0.125f, 5));
var idx = i - (rows - p.Modifiers.Length);
if (idx >= 0 && idx < p.Modifiers.Length) {
sub.AddCell(NewTd(p.Modifiers[idx], 8, colspan: 2)
.SetPaddingTop(0).SetPaddingLeftMM(5));
} else {
sub.AddCell(NewCell(colspan: 2));
}
@@ -129,14 +135,15 @@ namespace Elwig.Documents {
if (i < p.Buckets.Length) {
var bucket = p.Buckets[i];
sub.AddCell(NewTd($"{bucket.Name}:", 8).SetHeight(10).SetPaddingsMM(0.125f, 0, 0.125f, 0));
sub.AddCell(NewTd($"{bucket.Value:N0}", right: true));
var pad = i == 0 ? 0.5f : 0;
sub.AddCell(NewTd($"{bucket.Name}:", 8).SetHeight(10).SetPaddingTopMM(pad));
sub.AddCell(NewTd($"{bucket.Value:N0}", right: true).SetPaddingTopMM(pad));
} else {
sub.AddCell(NewCell(colspan: 2));
}
if (i == p.Buckets.Length - 1) {
sub.AddCell(NewTd($"{p.Weight:N0}", right: true));
sub.AddCell(NewTd($"{p.Weight:N0}", right: true).SetPaddingTop(0));
sub.AddCell(NewTd(p.IsNetWeight ? "\u2611" : "\u2610", 7, right: true).SetFont(SF).SetPadding(0));
} else {
sub.AddCell(NewCell(colspan: 2));
+11 -6
View File
@@ -19,7 +19,8 @@ namespace Elwig.Documents {
public Delivery Delivery;
public string? Text;
public int MemberDeliveredWeight;
public int MemberDeliveredWeightRed;
public int MemberDeliveredWeightWhite;
public Dictionary<string, MemberBucket> MemberBuckets = [];
// 0 - none
@@ -59,9 +60,13 @@ namespace Elwig.Documents {
protected override async Task LoadData(AppDbContext ctx) {
await base.LoadData(ctx);
MemberDeliveredWeight = await ctx.DeliveryParts
var weights = await ctx.DeliveryParts
.Where(d => d.Year == Delivery.Year && d.Delivery.MgNr == Member.MgNr)
.SumAsync(p => p.Weight);
.GroupBy(p => p.Variety.Type)
.ToDictionaryAsync(g => g.Key, g => g.Sum(p => p.Weight));
MemberDeliveredWeightRed = weights.GetValueOrDefault("R", 0);
MemberDeliveredWeightWhite = weights.GetValueOrDefault("W", 0);
MemberShares = await ctx.GetMemberHistory(Delivery.Year, Member.MgNr);
MemberBuckets = await ctx.GetMemberBuckets(Delivery.Year, Member.MgNr) ?? [];
}
@@ -81,7 +86,7 @@ namespace Elwig.Documents {
doc.Add(new KernedParagraph($"Anmerkung zur Lieferung: {Delivery.Comment}", 10).SetMarginsMM(5, 0, 0, 0));
}
if (DisplayStats > 0) {
doc.Add(NewBucketTable(Delivery.Season, MemberBuckets, MemberDeliveredWeight, isTiny: true,
doc.Add(NewBucketTable(Delivery.Season, MemberBuckets, MemberDeliveredWeightRed, MemberDeliveredWeightWhite, isTiny: true,
filter: DisplayStats > 2 ? null : DisplayStats == 1 ? [] : Delivery.Parts.Select(p => p.SortId).Distinct().ToList())
.SetKeepTogether(true)
.SetMarginsMM(5, 0, 0, 0));
@@ -176,8 +181,8 @@ namespace Elwig.Documents {
}
} else {
var info = part.WeighingInfo;
weighing.Add(Italic("Waage:"))
.Add(Normal(" " + (part.ScaleId ?? "?") + ", "))
weighing.Add(Italic("Waage/Terminal:"))
.Add(Normal(" " + (info.ScaleId ?? "?") + "/" + (part.ScaleTerminalId ?? "?") + ", "))
.Add(Italic("ID:"))
.Add(Normal(" " + (info.Id ?? "?")));
if (info.Date != null || info.Time != null)
+170 -11
View File
@@ -1,4 +1,5 @@
using Elwig.Helpers;
using Elwig.Models;
using Elwig.Models.Entities;
using iText.Kernel.Colors;
using iText.Kernel.Pdf;
@@ -18,7 +19,9 @@ namespace Elwig.Documents {
public new static string Name => "Stammdatenblatt";
public Season? Season;
public int MemberDeliveredWeight;
public int MemberDeliveredWeightRed;
public int MemberDeliveredWeightWhite;
public List<MemberHistory> MemberHistory = [];
public Dictionary<string, MemberBucket> MemberBuckets = [];
public List<AreaCom> ActiveAreaCommitments = [];
@@ -30,36 +33,52 @@ namespace Elwig.Documents {
public static async Task<MemberDataSheet> Initialize(int mgnr) {
using var ctx = new AppDbContext();
return new MemberDataSheet(await ctx.FetchMembers(mgnr, true, true).SingleAsync());
return new MemberDataSheet(await ctx.FetchMembers(mgnr, includeContactInfo: true).SingleAsync());
}
protected override async Task LoadData(AppDbContext ctx) {
await base.LoadData(ctx);
Season = await ctx.FetchSeasons().FirstOrDefaultAsync() ?? throw new ArgumentException("Invalid season");
MemberHistory = await ctx.MemberHistory
.Where(h => h.FromMgNr == Member.MgNr || h.ToMgNr == Member.MgNr)
.OrderBy(h => h.DateNoticeString).ThenBy(h => h.HistNr)
.ToListAsync();
MemberBuckets = await ctx.GetMemberBuckets(Utils.CurrentYear, Member.MgNr);
ActiveAreaCommitments = await Member.ActiveAreaCommitments(ctx)
.Include(c => c.Contract).ThenInclude(c => c.Revisions)
.ToListAsync();
MemberDeliveredWeight = await ctx.Deliveries
var weights = await ctx.Deliveries
.Where(d => d.Year == Season.Year && d.MgNr == Member.MgNr)
.SelectMany(d => d.Parts)
.SumAsync(p => p.Weight);
.GroupBy(p => p.Variety.Type)
.ToDictionaryAsync(g => g.Key, g => g.Sum(p => p.Weight));
MemberDeliveredWeightRed = weights.GetValueOrDefault("R", 0);
MemberDeliveredWeightWhite = weights.GetValueOrDefault("W", 0);
}
protected override void RenderBody(iText.Layout.Document doc, PdfDocument pdf) {
if (Season == null) throw new Exception("Call LoadData before RenderBody");
base.RenderBody(doc, pdf);
doc.Add(NewMemberData(Season).SetMarginBottomMM(5));
doc.Add(NewBucketTable(Season, MemberBuckets, MemberDeliveredWeight, includeDelivery: false));
doc.Add(NewBucketTable(Season, MemberBuckets, MemberDeliveredWeightRed, MemberDeliveredWeightWhite, includeDelivery: false));
if (ActiveAreaCommitments.Count != 0) {
bool firstOnPage = false;
if (pdf.GetNumberOfPages() == 1) {
doc.Add(new AreaBreak(AreaBreakType.NEXT_PAGE));
firstOnPage = true;
}
doc.Add(new KernedParagraph(12).Add(Bold($"Flächenbindungen per {Date:dd.MM.yyyy}")).SetMargins(firstOnPage ? 0 : 24, 0, 12, 0));
doc.Add(new KernedParagraph(12).Add(Bold($"Flächenbindungen per {Date:dd.MM.yyyy}")).SetMargins(firstOnPage ? 0 : 24, 0, 6, 0));
doc.Add(NewAreaComTable(ActiveAreaCommitments));
}
if (App.Client.EnableMemberHistory) {
bool firstOnPage = false;
if (pdf.GetNumberOfPages() == 1) {
doc.Add(new AreaBreak(AreaBreakType.NEXT_PAGE));
firstOnPage = true;
}
doc.Add(new KernedParagraph(12).Add(Bold($"Verlauf der Geschäftsanteile per {Date:dd.MM.yyyy}")).SetMargins(firstOnPage ? 0 : 24, 0, 6, 0));
doc.Add(NewMemberBusinessSharesTable(MemberHistory));
}
}
protected Cell NewDataHdr(string title, int colspan) {
@@ -132,6 +151,9 @@ namespace Elwig.Documents {
.AddCell(NewTd(i < subTbl2.Count ? subTbl2[i][1] : "", colspan: 2));
}
var shares = (Member.Shares != 0 || (Member.SharesRed == 0 && Member.SharesWhite == 0) ? $"{Member.Shares:N0}" : "") +
(Member.SharesRed != 0 || Member.SharesWhite != 0 ? (Member.Shares != 0 ? " / " : "") + $"{Member.SharesRed:N0} (rot) / {Member.SharesWhite:N0} (weiß)" : "") +
(Member.SharesDormant != 0 ? $" / {Member.SharesDormant:N0} (ruhend)" : "");
tbl.AddCell(NewDataHdr("Betrieb", colspan: 6))
.AddCell(NewDataTh("Betriebs-Nr.:")).AddCell(NewTd(Member.LfbisNr))
.AddCell(NewDataTh("UID:", colspan: 2)).AddCell(NewTd(Member.UstIdNr, colspan: 2))
@@ -143,7 +165,7 @@ namespace Elwig.Documents {
.AddCell(NewDataHdr("Genossenschaft", colspan: 6))
.AddCell(NewDataTh("Status:")).AddCell(NewTd(new KernedParagraph(Member.IsActive ? "Aktiv " : "Nicht aktiv ", 10)
.Add(Normal("(" + (Member.ExitDate != null ? $"{Member.EntryDate:dd.MM.yyyy}\u2013{Member.ExitDate:dd.MM.yyyy}" : $"seit {Member.EntryDate:dd.MM.yyyy}") + ")", 8))))
.AddCell(NewDataTh("Geschäftsanteile:", colspan: 2)).AddCell(NewTd($"{Member.BusinessShares:N0}", colspan: 2))
.AddCell(NewDataTh("Geschäftsanteile:", colspan: 2)).AddCell(NewTd(shares, colspan: 2))
.AddCell(NewDataTh("Stamm-Zweigstelle:")).AddCell(NewTd(Member.Branch?.Name))
.AddCell(NewDataTh("Volllieferant:", colspan: 2)).AddCell(NewTd(Member.IsVollLieferant ? "Ja" : "Nein", colspan: 2))
.AddCell(NewDataTh("Zusendungen per\u2026")).AddCell(NewTd(new KernedParagraph(10)
@@ -162,10 +184,10 @@ namespace Elwig.Documents {
}).OrderByDescending(a => a.Size).ToList();
var tbl = new Table(ColsMM(40, 30, 35, 15, 25, 20), true)
.SetWidth(UnitValue.CreatePercentValue(100)).SetFixedLayout()
.SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE)
.SetBorder(Border.NO_BORDER)
.SetFontSize(10);
.SetWidth(UnitValue.CreatePercentValue(100)).SetFixedLayout()
.SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE)
.SetBorder(Border.NO_BORDER)
.SetFontSize(10);
tbl.AddHeaderCell(NewTh("Katastralgemeinde", rowspan: 2, left: true))
.AddHeaderCell(NewTh("Ried", rowspan: 2, left: true))
@@ -202,5 +224,142 @@ namespace Elwig.Documents {
return tbl;
}
protected Cell[] NewBusinessSharesCells(MemberHistoryPoint p, bool isDiff = true) {
if (App.Client.HasRedWhite) {
return [
NewTd(isDiff ? (p.SharesRed == 0 ? "" : p.SharesRed < 0 ? $"{-p.SharesRed:N0}" : $"+{p.SharesRed:N0}") : $"{p.SharesRed:N0}",
right: true, bold: !isDiff, borderTop: !isDiff),
NewTd(isDiff ? (p.SharesWhite == 0 ? "" : p.SharesWhite < 0 ? $"{-p.SharesWhite:N0}" : $"+{p.SharesWhite:N0}") : $"{p.SharesWhite:N0}",
right: true, bold: !isDiff, borderTop: !isDiff),
NewTd(isDiff ? (p.SharesDormant == 0 ? "" : p.SharesDormant < 0 ? $"{-p.SharesDormant:N0}" : $"+{p.SharesDormant:N0}") : $"{p.SharesDormant:N0}",
right: true, bold: !isDiff, borderTop: !isDiff),
];
} else {
return [
NewTd(isDiff ? (p.Shares == 0 ? "" : p.Shares < 0 ? $"{-p.Shares:N0}" : $"+{p.Shares:N0}") : $"{p.Shares:N0}",
right: true, bold: !isDiff, borderTop: !isDiff),
NewTd(isDiff ? (p.SharesDormant == 0 ? "" : p.SharesDormant < 0 ? $"{-p.SharesDormant:N0}" : $"+{p.SharesDormant:N0}") : $"{p.SharesDormant:N0}",
right : true, bold: !isDiff, borderTop: !isDiff),
];
}
}
protected Table NewMemberBusinessSharesTable(IEnumerable<MemberHistory> history) {
var tbl = new Table(ColsMM(18, 93, App.Client.HasRedWhite ? 18 : 12, App.Client.HasRedWhite ? 12 : 18, 12, 12), true)
.SetWidth(UnitValue.CreatePercentValue(100)).SetFixedLayout()
.SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE)
.SetBorder(Border.NO_BORDER)
.SetFontSize(10);
var tw = App.Client.HasRedWhite ? 1 : 2;
tbl.AddHeaderCell(NewTh("Meldung am", rowspan: 2))
.AddHeaderCell(NewTh("Text", rowspan: 2, colspan: tw, left: true))
.AddHeaderCell(NewTh("Wirksam ab", rowspan: 2))
.AddHeaderCell(NewTh("Geschäftsanteile", colspan: App.Client.HasRedWhite ? 3 : 2));
if (App.Client.HasRedWhite) {
tbl.AddHeaderCell(NewTh("rot"))
.AddHeaderCell(NewTh("weiß"))
.AddHeaderCell(NewTh("ruhend"));
} else {
tbl.AddHeaderCell(NewTh("normal"))
.AddHeaderCell(NewTh("ruhend"));
}
if (!history.Any() || Member.EntryDate <= history.First().DateNotice) {
tbl.AddCell(NewTd($"{Member.EntryDate:dd.MM.yyyy}"))
.AddCell(NewTd("Eintritt", colspan: tw, bold: true))
.AddCell(NewTd($"{Member.EntryDate:dd.MM.yyyy}"))
.AddCells(NewBusinessSharesCells(new()));
}
var recorded = history.Aggregate(new MemberHistoryPoint(), (s, h) => new(
s.Shares + (h.ToMgNr == Member.MgNr && h.ToType == BusinessShareType.Normal ? h.Shares : 0) - (h.FromMgNr == Member.MgNr && h.FromType == BusinessShareType.Normal ? h.Shares : 0),
s.SharesRed + (h.ToMgNr == Member.MgNr && h.ToType == BusinessShareType.Red ? h.Shares : 0) - (h.FromMgNr == Member.MgNr && h.FromType == BusinessShareType.Red ? h.Shares : 0),
s.SharesWhite + (h.ToMgNr == Member.MgNr && h.ToType == BusinessShareType.White ? h.Shares : 0) - (h.FromMgNr == Member.MgNr && h.FromType == BusinessShareType.White ? h.Shares : 0),
s.SharesDormant + (h.ToMgNr == Member.MgNr && h.ToType == BusinessShareType.Dormant ? h.Shares : 0) - (h.FromMgNr == Member.MgNr && h.FromType == BusinessShareType.Dormant ? h.Shares : 0)));
var cur = new MemberHistoryPoint(Member.Shares - recorded.Shares, Member.SharesRed - recorded.SharesRed, Member.SharesWhite - recorded.SharesWhite, Member.SharesDormant - recorded.SharesDormant);
if (cur.Shares != 0 || cur.SharesRed != 0 || cur.SharesWhite != 0 || cur.SharesDormant != 0) {
tbl.AddCell(NewTd())
.AddCell(NewTd("Nicht in Software erfasst", colspan: tw, italic: true))
.AddCell(NewTd())
.AddCells(NewBusinessSharesCells(cur));
if (history.Any()) {
tbl.AddCell(NewTd(borderTop: true))
.AddCell(NewTd($"Bis Lese {history.First().DateNotice.Year - 1}", borderTop: true, colspan: tw, bold: true))
.AddCell(NewTd(borderTop: true))
.AddCells(NewBusinessSharesCells(cur, isDiff: false));
}
}
DateOnly? lastDate = null;
foreach (var row in history) {
int s = 0, r = 0, w = 0, d = 0;
if (row.FromMgNr == Member.MgNr) {
switch (row.FromType) {
case BusinessShareType.Normal: s -= row.Shares; break;
case BusinessShareType.Red: r -= row.Shares; break;
case BusinessShareType.White: w -= row.Shares; break;
case BusinessShareType.Dormant: d -= row.Shares; break;
}
}
if (row.ToMgNr == Member.MgNr) {
switch (row.ToType) {
case BusinessShareType.Normal: s += row.Shares; break;
case BusinessShareType.Red: r += row.Shares; break;
case BusinessShareType.White: w += row.Shares; break;
case BusinessShareType.Dormant: d += row.Shares; break;
}
}
if (lastDate < Member.EntryDate && row.DateNotice <= Member.EntryDate) {
tbl.AddCell(NewTd($"{Member.EntryDate:dd.MM.yyyy}"))
.AddCell(NewTd("Eintritt", colspan: tw, bold: true))
.AddCell(NewTd($"{Member.EntryDate:dd.MM.yyyy}"))
.AddCells(NewBusinessSharesCells(new()));
}
if (lastDate != null && row.DateNotice.Year != lastDate?.Year) {
tbl.AddCell(NewTd(borderTop: true))
.AddCell(NewTd(lastDate?.Year == row.DateNotice.Year - 1 ? $"Lese {lastDate?.Year}" : $"Lese {lastDate?.Year}{row.DateNotice.Year - 1}", borderTop: true, colspan: tw, bold: true))
.AddCell(NewTd(borderTop: true))
.AddCells(NewBusinessSharesCells(cur, isDiff: false));
}
if (lastDate < Member.ExitDate && row.DateNotice <= Member.ExitDate) {
tbl.AddCell(NewTd($"{Member.ExitDate:dd.MM.yyyy}"))
.AddCell(NewTd("Austritt", colspan: tw, bold: true))
.AddCell(NewTd($"31.12.{Member.ExitDate.Value.Year + 1}")) // FIXME
.AddCells(NewBusinessSharesCells(new()));
}
cur = new(cur.Shares + s, cur.SharesRed + r, cur.SharesWhite + w, cur.SharesDormant + d);
tbl.AddCell(NewTd($"{row.DateNotice:dd.MM.yyyy}"))
.AddCell(NewTd(row.FormatReasonPhrase(Member.MgNr), colspan: tw))
.AddCell(NewTd($"{row.DateEffective:dd.MM.yyyy}"))
.AddCells(NewBusinessSharesCells(new(s, r, w, d)));
lastDate = row.DateNotice;
}
if (Member.EntryDate != null && history.Any() && Member.EntryDate > lastDate) {
tbl.AddCell(NewTd($"{Member.EntryDate:dd.MM.yyyy}"))
.AddCell(NewTd("Eintritt", colspan: tw, bold: true))
.AddCell(NewTd($"{Member.EntryDate:dd.MM.yyyy}"))
.AddCells(NewBusinessSharesCells(new()));
}
if (Member.ExitDate != null && (!history.Any() || Member.ExitDate > lastDate)) {
tbl.AddCell(NewTd($"{Member.ExitDate:dd.MM.yyyy}"))
.AddCell(NewTd("Austritt", colspan: tw, bold: true))
.AddCell(NewTd($"31.12.{Member.ExitDate.Value.Year + 1}")) // FIXME
.AddCells(NewBusinessSharesCells(new()));
}
tbl.AddCell(NewTd(borderTop: true))
.AddCell(NewTd(lastDate == null ? "Aktuell" : $"Ab Lese {lastDate?.Year}", borderTop: true, colspan: tw, bold: true))
.AddCell(NewTd(borderTop: true))
.AddCells(NewBusinessSharesCells(cur, isDiff: false));
return tbl;
}
}
}
+1 -1
View File
@@ -86,7 +86,7 @@ namespace Elwig.Documents {
.AddCell(NewTd($"{m.Plz}", 8))
.AddCell(NewTd(m.Locality, 6))
.AddCell(NewTd(m.LfbisNr ?? "", 8))
.AddCell(NewTd($"{m.BusinessShares:N0}", 8, right: true).SetPaddingLeft(0).SetPaddingRight(0))
.AddCell(NewTd($"{m.SharesActive:N0}", 8, right: true).SetPaddingLeft(0).SetPaddingRight(0))
.AddCell(NewTd(m.DefaultKg ?? "", 6));
if (AreaComFilters.Length > 0) {
foreach (var v in AreaComFilters) {
+1 -1
View File
@@ -139,7 +139,7 @@ namespace Elwig.Documents {
.AddCell(NewSectionTh("Datum/Überw.:", overflow: true))
.AddCell(NewTd($"{Variant.Date:dd.MM.yyyy} / {Variant.TransferDate:dd.MM.yyyy}", colspan: 4, center: true))
.AddCell(NewSectionTh("Automatische Nachzeichnung der GA:", colspan: 2, borderLeft: true))
.AddCell(NewSectionTh("(Automatische) Nachzeichnung von GA:", colspan: 2, borderLeft: true))
.AddCell(NewTd(BillingData.ConsiderAutoBusinessShares ? "Ja" : "Nein", center: true))
.AddCell(NewSectionTh("Berechnung:"))
+15 -15
View File
@@ -9,10 +9,10 @@
<UseWindowsForms>true</UseWindowsForms>
<PreserveCompilationContext>true</PreserveCompilationContext>
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
<Version>1.0.5.0</Version>
<Version>1.1.1.0</Version>
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ApplicationManifest>app.manifest</ApplicationManifest>
<ApplicationManifest>App.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
@@ -23,21 +23,21 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="bblanchon.PDFium.Win32" Version="149.0.7802" />
<PackageReference Include="bblanchon.PDFium.Win32" Version="153.0.8009" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
<PackageReference Include="itext" Version="9.6.0" />
<PackageReference Include="itext.bouncy-castle-adapter" Version="9.6.0" />
<PackageReference Include="itext" Version="9.7.0" />
<PackageReference Include="itext.bouncy-castle-adapter" Version="9.7.0" />
<PackageReference Include="LinqKit" Version="1.3.11" />
<PackageReference Include="MailKit" Version="4.16.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.6" />
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.6" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3912.50" />
<PackageReference Include="NJsonSchema" Version="11.6.0" />
<PackageReference Include="ScottPlot.WPF" Version="5.1.58" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.2" />
<PackageReference Include="System.IO.Hashing" Version="10.0.6" />
<PackageReference Include="System.IO.Ports" Version="10.0.6" />
<PackageReference Include="System.Management" Version="10.0.6" />
<PackageReference Include="MailKit" Version="4.17.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.11" />
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.11" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.4129.50" />
<PackageReference Include="NJsonSchema" Version="11.6.1" />
<PackageReference Include="ScottPlot.WPF" Version="5.1.59" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.5" />
<PackageReference Include="System.IO.Hashing" Version="10.0.11" />
<PackageReference Include="System.IO.Ports" Version="10.0.11" />
<PackageReference Include="System.Management" Version="10.0.11" />
</ItemGroup>
</Project>
+37 -8
View File
@@ -1,10 +1,9 @@
using Elwig.Models.Dtos;
using Elwig.Models.Entities;
using Elwig.Services;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using ScottPlot.TickGenerators.Financial;
using ScottPlot.TickGenerators.TimeUnits;
using System;
using System.Collections.Generic;
using System.Data;
@@ -12,11 +11,10 @@ using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Converters;
namespace Elwig.Helpers {
public record struct MemberHistoryPoint(int Shares, int SharesRed, int SharesWhite, int SharesDormant);
public record struct AreaComBucket(int Area, int Obligation, int Right);
public record struct UnderDelivery(int Weight, int Diff);
public record struct MemberBucket(string Name, int Area, int Obligation, int Right, int Delivery, int DeliveryStrict, int DeliveryTotal, int Payment);
@@ -149,6 +147,7 @@ namespace Elwig.Helpers {
.Include(s => s.Modifiers)
.OrderByDescending(s => s.Year));
private readonly Dictionary<int, Dictionary<int, MemberHistoryPoint>> _memberHistory = [];
private readonly Dictionary<int, Dictionary<int, Dictionary<string, AreaComBucket>>> _memberAreaCommitmentBuckets = [];
private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberDeliveryBuckets = [];
private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberDeliveryBucketsStrict = [];
@@ -162,8 +161,8 @@ namespace Elwig.Helpers {
LogFile = new(file) {
AutoFlush = true
};
} catch (Exception e) {
MessageBox.Show($"Unable to open database log file:\n\n{e.Message}", "Database Log", MessageBoxButton.OK, MessageBoxImage.Error);
} catch (Exception exc) {
InteractionService.ShowException("Database Log", $"Unable to open database log file", exc);
}
}
SavedLastWriteTime = LastWriteTime;
@@ -198,6 +197,7 @@ namespace Elwig.Helpers {
modelBuilder.Entity<PostalDest>().Navigation(p => p.AtPlz).AutoInclude();
modelBuilder.Entity<AT_PlzDest>().Navigation(p => p.AtPlz).AutoInclude();
modelBuilder.Entity<AT_PlzDest>().Navigation(p => p.Ort).AutoInclude();
modelBuilder.Entity<Member>().Navigation(m => m.Branch).AutoInclude();
modelBuilder.Entity<Member>().Navigation(m => m.DefaultWbKg).AutoInclude();
modelBuilder.Entity<Member>().Navigation(m => m.Country).AutoInclude();
modelBuilder.Entity<Member>().Navigation(m => m.PostalDest).AutoInclude();
@@ -233,6 +233,9 @@ namespace Elwig.Helpers {
modelBuilder.Entity<DeliveryAncmt>().Navigation(a => a.Schedule).AutoInclude();
modelBuilder.Entity<DeliveryAncmt>().Navigation(a => a.Variety).AutoInclude();
modelBuilder.Entity<DeliverySchedule>().Navigation(s => s.Branch).AutoInclude();
modelBuilder.Entity<MemberHistory>().Navigation(s => s.FromMember).AutoInclude();
modelBuilder.Entity<MemberHistory>().Navigation(s => s.ToMember).AutoInclude();
modelBuilder.Entity<MemberHistory>().Navigation(s => s.Currency).AutoInclude();
}
public override void Dispose() {
@@ -312,6 +315,10 @@ namespace Elwig.Helpers {
return (await DeliverySchedules.Where(s => s.Year == year).Select(v => (int?)v.DsNr).MaxAsync() ?? 0) + 1;
}
public async Task<int> NextHistNr() {
return (await MemberHistory.Select(c => (int?)c.HistNr).MaxAsync() ?? 0) + 1;
}
public IAsyncEnumerable<Branch> FetchBranches(string? zwstid = null, bool includeWithoutMembers = true) {
return _compiledQueryBranches.Invoke(this, zwstid, includeWithoutMembers);
}
@@ -338,9 +345,9 @@ namespace Elwig.Helpers {
public IAsyncEnumerable<Member> FetchMembers(int? mgnr = null, bool includeNotActive = false, bool includeContactInfo = false) {
if (includeContactInfo) {
return _compiledQueryMembersContactInfo.Invoke(this, mgnr, includeNotActive);
return _compiledQueryMembersContactInfo.Invoke(this, mgnr, mgnr != null || includeNotActive);
} else {
return _compiledQueryMembers.Invoke(this, mgnr, includeNotActive);
return _compiledQueryMembers.Invoke(this, mgnr, mgnr != null || includeNotActive);
}
}
@@ -400,6 +407,22 @@ namespace Elwig.Helpers {
}
}
private async Task FetchMemberHistory(int year, SqliteConnection? cnx = null) {
var ownCnx = cnx == null;
cnx ??= await ConnectAsync();
var history = new Dictionary<int, MemberHistoryPoint>();
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"SELECT mgnr, shares, shares_red, shares_white, shares_dormant FROM v_member_history WHERE year = {year}";
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) {
var mgnr = reader.GetInt32(0);
history[mgnr] = new(reader.GetInt32(1), reader.GetInt32(2), reader.GetInt32(3), reader.GetInt32(4));
}
}
if (ownCnx) await cnx.DisposeAsync();
_memberHistory[year] = history;
}
private async Task FetchMemberAreaCommitmentBuckets(int year, SqliteConnection? cnx = null) {
var ownCnx = cnx == null;
cnx ??= await ConnectAsync();
@@ -490,6 +513,12 @@ namespace Elwig.Helpers {
_memberUnderDelivery[year] = buckets;
}
public async Task<MemberHistoryPoint> GetMemberHistory(int year, int mgnr, SqliteConnection? cnx = null) {
if (!_memberHistory.ContainsKey(year))
await FetchMemberHistory(year, cnx);
return _memberHistory[year].GetValueOrDefault(mgnr, new());
}
public async Task<Dictionary<string, AreaComBucket>> GetMemberAreaCommitmentBuckets(int year, int mgnr, SqliteConnection? cnx = null) {
if (!_memberAreaCommitmentBuckets.ContainsKey(year))
await FetchMemberAreaCommitmentBuckets(year, cnx);
+49 -39
View File
@@ -1,6 +1,6 @@
using Microsoft.Data.Sqlite;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
@@ -9,36 +9,41 @@ namespace Elwig.Helpers {
public static class AppDbUpdater {
// Don't forget to update value in Tests/fetch-resources.bat!
public static readonly int RequiredSchemaVersion = 39;
public static readonly int RequiredSchemaVersion = 41;
private static int VersionOffset = 0;
public static async Task<Version> CheckDb() {
using var cnx = AppDbContext.Connect();
long? applId, schemaVers;
using (var cnx = await AppDbContext.ConnectAsync()) {
applId = (long?)await cnx.ExecuteScalar("PRAGMA application_id") ?? 0;
if (applId != 0x454C5747) throw new Exception($"Invalid application_id in database (0x{applId:X08})");
var applId = (long?)await cnx.ExecuteScalar("PRAGMA application_id") ?? 0;
if (applId != 0x454C5747) throw new Exception($"Invalid application_id in database (0x{applId:X08})");
var schemaVers = (long?)await cnx.ExecuteScalar("PRAGMA schema_version") ?? 0;
VersionOffset = (int)(schemaVers % 100);
if (VersionOffset != 0) {
// schema was modified manually/externally
// TODO issue warning
schemaVers = (long?)await cnx.ExecuteScalar("PRAGMA schema_version") ?? 0;
VersionOffset = (int)(schemaVers % 100);
if (VersionOffset != 0) {
// schema was modified manually/externally
// TODO issue warning
}
}
await UpdateDbSchema(cnx, (int)(schemaVers / 100), RequiredSchemaVersion);
var userVers = (long?)await cnx.ExecuteScalar("PRAGMA user_version") ?? 0;
var v = new Version((int)(userVers >> 24), (int)((userVers >> 16) & 0xFF), (int)((userVers >> 8) & 0xFF), (int)(userVers & 0xFF));
await UpdateDbSchema((int)(schemaVers / 100), RequiredSchemaVersion);
if (App.Version > v) {
long vers = (App.Version.Major << 24) | (App.Version.Minor << 16) | (App.Version.Build << 8) | App.Version.Revision;
await cnx.ExecuteBatch($"PRAGMA user_version = {vers}");
Version v;
using (var cnx = await AppDbContext.ConnectAsync()) {
var userVers = (long?)await cnx.ExecuteScalar("PRAGMA user_version") ?? 0;
v = new Version((int)(userVers >> 24), (int)((userVers >> 16) & 0xFF), (int)((userVers >> 8) & 0xFF), (int)(userVers & 0xFF));
if (App.Version > v) {
long vers = (App.Version.Major << 24) | (App.Version.Minor << 16) | (App.Version.Build << 8) | App.Version.Revision;
await cnx.ExecuteBatch($"PRAGMA user_version = {vers}");
}
}
return v;
}
private static async Task UpdateDbSchema(SqliteConnection cnx, int fromVersion, int toVersion) {
private static async Task UpdateDbSchema(int fromVersion, int toVersion) {
if (fromVersion == toVersion) {
return;
} else if (fromVersion > toVersion) {
@@ -48,43 +53,48 @@ namespace Elwig.Helpers {
}
var asm = Assembly.GetExecutingAssembly();
(int From, int To, string Name)[] scripts = asm.GetManifestResourceNames()
(int From, int To, string Name)[] scripts = [.. asm.GetManifestResourceNames()
.Where(n => n.StartsWith("Elwig.Resources.Sql."))
.Select(n => {
var p = n.Split(".")[^2].Split("-");
return (int.Parse(p[0]), int.Parse(p[1]), n);
})
.OrderBy(s => s.Item1).ThenBy(s => s.Item2)
.ToArray();
.OrderBy(s => s.Item1).ThenBy(s => s.Item2)];
List<string> toExecute = [];
var vers = fromVersion;
while (vers < toVersion) {
var (_, to, name) = scripts.Where(s => s.From == vers).Last();
var (_, to, name) = scripts.Last(s => s.From == vers);
toExecute.Add(name);
vers = to;
}
if (toExecute.Count == 0)
return;
await cnx.ExecuteBatch("""
PRAGMA locking_mode = EXCLUSIVE;
BEGIN EXCLUSIVE;
""");
foreach (var script in toExecute) {
await cnx.ExecuteEmbeddedScript(asm, script);
}
var violations = await cnx.ForeignKeyCheck();
if (violations.Length > 0) {
throw new Exception($"Foreign key violations ({violations.Length}):\n" + string.Join("\n", violations
.Select(v => $"{v.Table} - {v.RowId} - {v.Parent} - {v.FkId}")));
}
var backup = Path.ChangeExtension(App.Config.DatabaseFile, $".v{fromVersion}.sqlite3");
File.Copy(App.Config.DatabaseFile, backup, true);
try {
using var cnx = await AppDbContext.ConnectAsync();
await cnx.ExecuteBatch("PRAGMA locking_mode = EXCLUSIVE");
foreach (var script in toExecute) {
await cnx.ExecuteEmbeddedScript(asm, script);
}
await cnx.ExecuteBatch($"""
COMMIT;
VACUUM;
PRAGMA schema_version = {toVersion * 100 + VersionOffset};
""");
var violations = await cnx.ForeignKeyCheck();
if (violations.Length > 0) {
throw new Exception($"Foreign key violations ({violations.Length}):\n" + string.Join("\n", violations
.Take(50)
.Select(v => $"{v.Table} - {v.RowId} - {v.Parent} - {v.FkId}")));
}
await cnx.ExecuteBatch("VACUUM");
await cnx.ExecuteBatch($"PRAGMA schema_version = {toVersion * 100 + VersionOffset}");
} catch (Exception) {
File.Move(backup, App.Config.DatabaseFile, true);
throw;
} finally {
File.Delete(backup);
}
}
}
}
+12 -28
View File
@@ -50,45 +50,29 @@ namespace Elwig.Helpers.Billing {
""");
}
public async Task AutoAdjustBusinessShares(DateOnly date, int allowanceKg = 0, double allowanceBs = 0, int allowanceKgPerBs = 0, double allowanceRel = 0, int addMinBs = 1) {
if (addMinBs < 1) addMinBs = 1;
public async Task AutoAdjustBusinessShares(DateOnly date, int allowanceKg = 0, double allowanceShares = 0, int allowanceKgPerShare = 0, double allowanceRel = 0, int addMinShares = 1) {
if (addMinShares < 1) addMinShares = 1;
using var cnx = await AppDbContext.ConnectAsync();
await cnx.ExecuteBatch($"""
UPDATE member
SET business_shares = member.business_shares - h.business_shares
FROM member_history h
WHERE h.date = '{Year}-11-30' AND h.type = 'auto' AND h.mgnr = member.mgnr AND member.active;
INSERT INTO member_history (mgnr, date, type, business_shares)
SELECT u.mgnr,
'{date:yyyy-MM-dd}',
'auto',
CEIL((u.diff - {allowanceKg}.0 - {allowanceKgPerBs}.0 * u.business_shares) / s.max_kg_per_bs
- {allowanceBs.ToString(CultureInfo.InvariantCulture)}
- {allowanceRel.ToString(CultureInfo.InvariantCulture)} * u.business_shares) AS bs
DELETE FROM member_history WHERE source = 'elwig' AND reason = '{MemberHistory.REASON_PRESCRIPTION}' AND deduct_year = {Year};
INSERT INTO member_history (histnr, from_mgnr, from_type, to_mgnr, to_type, date_notice, date_effective, reason, source, deduct_year, shares, value_per_share, currency, comment)
SELECT COALESCE((SELECT MAX(histnr) AS histnr FROM member_history), 0) + ROW_NUMBER() OVER(ORDER BY m.mgnr),
NULL, NULL, u.mgnr, 1, '{date:yyyy-MM-dd}', '{date:yyyy-MM-dd}', '{MemberHistory.REASON_PRESCRIPTION}', 'elwig', s.year,
CEIL((u.diff - {allowanceKg}.0 - {allowanceKgPerShare}.0 * u.shares) / s.max_kg_per_share
- {allowanceShares.ToString(CultureInfo.InvariantCulture)}
- {allowanceRel.ToString(CultureInfo.InvariantCulture)} * u.shares) AS adjust_shares,
s.share_value / POW(10, s.precision - 2), s.currency, 'durch automatische Nachzeichnung'
FROM v_total_under_delivery u
JOIN season s ON s.year = u.year
JOIN member m ON m.mgnr = u.mgnr
WHERE s.year = {Year} AND bs >= {addMinBs} AND m.active
ON CONFLICT DO UPDATE
SET business_shares = excluded.business_shares;
UPDATE member
SET business_shares = member.business_shares + h.business_shares
FROM member_history h
WHERE h.date = '{Year}-11-30' AND h.type = 'auto' AND h.mgnr = member.mgnr;
WHERE s.year = {Year} AND adjust_shares >= {addMinShares} AND m.active
""");
}
public async Task UnAdjustBusinessShares() {
using var cnx = await AppDbContext.ConnectAsync();
await cnx.ExecuteBatch($"""
UPDATE member
SET business_shares = member.business_shares - h.business_shares
FROM member_history h
WHERE h.date = '{Year}-11-30' AND h.type = 'auto' AND h.mgnr = member.mgnr AND member.active;
DELETE FROM member_history WHERE date = '{Year}-11-30' AND type = 'auto';
DELETE FROM member_history WHERE source = 'elwig' AND reason = '{MemberHistory.REASON_PRESCRIPTION}' AND deduct_year = {Year};
""");
}
+25 -23
View File
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
@@ -92,6 +93,14 @@ namespace Elwig.Helpers.Billing {
Mode = (mode == "elwig") ? CalculationMode.Elwig : CalculationMode.WgMaster;
}
public BillingData() {
Data = new JsonObject {
["mode"] = "elwig",
["version"] = 1,
};
Mode = CalculationMode.Elwig;
}
protected static JsonObject ParseJson(string json) {
if (Schema == null) throw new InvalidOperationException("Schema has to be initialized first");
try {
@@ -107,6 +116,10 @@ namespace Elwig.Helpers.Billing {
return new(ParseJson(json));
}
public string ToJsonString(JsonSerializerOptions? options = null) {
return Data.ToJsonString(options);
}
protected JsonArray GetCurvesEntry() {
return Data[Mode == CalculationMode.Elwig ? "curves" : "Kurven"]?.AsArray() ?? throw new InvalidOperationException();
}
@@ -412,7 +425,7 @@ namespace Elwig.Helpers.Billing {
}
}
public static JsonObject FromGraphEntries(
public static BillingData FromGraphEntries(
IEnumerable<GraphEntry> graphEntries,
BillingData? origData = null,
IEnumerable<RawVaribute>? vaributes = null,
@@ -446,40 +459,29 @@ namespace Elwig.Helpers.Billing {
}
}
CollapsePaymentData(payment, payment.DeepClone().AsObject(), vaributes ?? payment.Select(e => new RawVaribute(e.Key)).ToList(), useDefaultPayment);
CollapsePaymentData(qualityWei, qualityWei.DeepClone().AsObject(), vaributes ?? qualityWei.Select(e => new RawVaribute(e.Key)).ToList(), useDefaultQuality);
var data = new JsonObject {
["mode"] = "elwig",
["version"] = 1,
};
if (origData?.ConsiderDelieryModifiers == true)
data["consider_delivery_modifiers"] = true;
if (origData?.ConsiderContractPenalties == true)
data["consider_contract_penalties"] = true;
if (origData?.ConsiderTotalPenalty == true)
data["consider_total_penalty"] = true;
if (origData?.ConsiderAutoBusinessShares == true)
data["consider_auto_business_shares"] = true;
CollapsePaymentData(payment, payment.DeepClone().AsObject(), vaributes ?? [.. payment.Select(e => new RawVaribute(e.Key))], useDefaultPayment);
CollapsePaymentData(qualityWei, qualityWei.DeepClone().AsObject(), vaributes ?? [.. qualityWei.Select(e => new RawVaribute(e.Key))], useDefaultQuality);
BillingData data = origData != null && origData.Mode == CalculationMode.Elwig ? new BillingData((JsonObject)origData.Data.DeepClone()) : new BillingData();
if (payment.Count == 0) {
data["payment"] = 0;
data.Data["payment"] = 0;
} else if (payment.Count == 1 && payment.First().Key == "default") {
data["payment"] = payment.Single().Value?.DeepClone();
data.Data["payment"] = payment.Single().Value?.DeepClone();
} else {
data["payment"] = payment;
data.Data["payment"] = payment;
}
if (qualityWei.Count == 1 && qualityWei.First().Key == "default") {
data["quality"] = new JsonObject() {
data.Data["quality"] = new JsonObject() {
["WEI"] = qualityWei.Single().Value?.DeepClone()
};
} else if (qualityWei.Count >= 1) {
data["quality"] = new JsonObject() {
data.Data["quality"] = new JsonObject() {
["WEI"] = qualityWei
};
} else {
data.Data.Remove("quality");
}
data["curves"] = curves;
data.Data["curves"] = curves;
return data;
}
+6 -6
View File
@@ -76,7 +76,7 @@ namespace Elwig.Helpers.Billing {
IIF(m.buchführend, s.vat_normal, s.vat_flatrate) AS vat,
ROUND(IIF({Data.ConsiderTotalPenalty}, COALESCE(b.total_penalty, 0), 0) / POW(10, s.precision - 2)) +
ROUND(IIF({Data.ConsiderContractPenalties}, COALESCE(u.total_penalty, 0), 0) / POW(10, 4 - 2)) +
ROUND(IIF({Data.ConsiderAutoBusinessShares}, -COALESCE(a.total_amount, 0), 0) / POW(10, s.precision - 2)) +
ROUND(IIF({Data.ConsiderAutoBusinessShares}, -COALESCE(a.total_amount, 0), 0)) +
IIF({Data.ConsiderCustomModifiers}, COALESCE(x.amount, 0), 0)
AS modifiers,
IIF(lc.amount < 0, 0, lc.modifiers) AS prev_modifiers
@@ -98,7 +98,7 @@ namespace Elwig.Helpers.Billing {
LEFT JOIN v_penalty_area_commitments u ON (u.year, u.mgnr) = (s.year, m.mgnr)
LEFT JOIN v_auto_business_shares a ON (a.year, a.mgnr) = (s.year, m.mgnr)
LEFT JOIN payment_custom x ON (x.year, x.mgnr) = (s.year, m.mgnr)
WHERE s.year = {Year} AND v.avnr = {AvNr} AND p.amount > COALESCE(lp.amount, 0);
WHERE s.year = {Year} AND v.avnr = {AvNr} AND p.amount != COALESCE(lp.amount, 0);
""");
await cnx.ExecuteBatch($"""
UPDATE payment_variant SET test_variant = FALSE WHERE (year, avnr) = ({Year}, {AvNr});
@@ -137,11 +137,11 @@ namespace Elwig.Helpers.Billing {
await cnx.ExecuteBatch($"""
INSERT INTO payment_member (year, avnr, mgnr, net_amount, mod_abs, mod_rel)
SELECT c.year, {AvNr}, s.mgnr, 0,
ROUND(s.sum * COALESCE(m.abs, 0)),
ROUND(s.weight_total * COALESCE(m.abs, 0)),
COALESCE(m.rel, 0)
FROM (SELECT {Year} AS year, m.mgnr,
ROUND(AVG(COALESCE(a.sum, b.sum)) * {multiplier}) AS baseline,
COUNT(*) = {lastYears} AND MIN(COALESCE(a.sum, b.sum)) > 0 AS allowed
ROUND(AVG(COALESCE(a.weight_total, b.weight_total)) * {multiplier}) AS baseline,
COUNT(*) = {lastYears} AND MIN(COALESCE(a.weight_total, b.weight_total)) > 0 AS allowed
FROM member m
LEFT JOIN v_stat_member a ON a.mgnr = m.mgnr
FULL OUTER JOIN v_stat_member b ON b.mgnr = m.predecessor_mgnr AND b.year = a.year AND {(includePredecessor ? "TRUE" : "FALSE")}
@@ -150,7 +150,7 @@ namespace Elwig.Helpers.Billing {
HAVING allowed) c
JOIN v_stat_member s ON (s.year, s.mgnr) = (c.year, c.mgnr)
LEFT JOIN modifier m ON m.year = c.year AND m.name LIKE '{modName}'
WHERE sum >= baseline
WHERE weight_total >= baseline
ON CONFLICT DO UPDATE
SET mod_abs = mod_abs + excluded.mod_abs,
mod_rel = mod_rel + excluded.mod_rel
+30 -6
View File
@@ -8,16 +8,18 @@ using System.Threading.Tasks;
namespace Elwig.Helpers {
public class ClientParameters {
public enum Type { Matzen, Winzerkeller, Weinland, Baden };
public enum Type { Matzen, Winzerkeller, Weinland, Baden, Seewinkel };
public bool IsMatzen => Client == Type.Matzen;
public bool IsWinzerkeller => Client == Type.Winzerkeller;
public bool IsWeinland => Client == Type.Weinland;
public bool IsBaden => Client == Type.Baden;
public bool IsSeewinkel => Client == Type.Seewinkel;
public bool IsWolkersdorf => IsWinzerkeller && App.ZwstId == "W";
public bool IsHaugsdorf => IsWinzerkeller && App.ZwstId == "H";
public bool IsSitzendorf => IsWinzerkeller && App.ZwstId == "S";
public bool IsGrInzersdorf => IsWeinland;
public bool IsPamhagen => IsSeewinkel;
public string NameToken;
public string NameShort;
@@ -54,6 +56,10 @@ namespace Elwig.Helpers {
public string? EmailAddress;
public string? Website;
public bool EnableMemberHistory;
public int ModeBusinessShares;
public bool HasRedWhite => ModeBusinessShares == 1;
public int ModeDeliveryNoteStats;
public int ModeWineQualityStatistics;
public int OrderingMemberList;
@@ -73,7 +79,7 @@ namespace Elwig.Helpers {
public int ExportEbicsVersion;
public int ExportEbicsAddress;
public (int? AllowanceKg, double? AllowanceBs, int? AllowanceKgPerBs, double? AllowancePercent, int? MinBs) AutoAdjustBs;
public (int? AllowanceKg, double? AllowanceShares, int? AllowanceKgPerShare, double? AllowancePercent, int? MinShares) AutoAdjustShares;
public ClientParameters(AppDbContext ctx) : this(ctx.ClientParameters.ToDictionary(e => e.Param, e => e.Value)) { }
@@ -93,6 +99,8 @@ namespace Elwig.Helpers {
Client = Type.Weinland; break;
case "Winzergenossenschaft Baden - Bad Vöslau":
Client = Type.Baden; break;
case "Winzerkeller Seewinkel":
Client = Type.Seewinkel; break;
};
Plz = int.Parse(parameters["CLIENT_PLZ"] ?? "");
@@ -108,6 +116,15 @@ namespace Elwig.Helpers {
Iban = parameters.GetValueOrDefault("CLIENT_IBAN");
OrganicAuthority = parameters.GetValueOrDefault("CLIENT_ORGANIC_AUTHORITY");
EnableMemberHistory = (parameters.GetValueOrDefault("ENABLE_MEMBERHISTORY")?.ToUpper()) switch {
"1" or "TRUE" or "YES" or "JA" => true,
_ => false,
};
switch (parameters.GetValueOrDefault("MODE_BUSINESSSHARES", "SIMPLE")?.ToUpper()) {
case "SIMPLE": ModeBusinessShares = 0; break;
case "RED_WHITE": ModeBusinessShares = 1; break;
}
switch (parameters.GetValueOrDefault("MODE_DELIVERYNOTE_STATS", "SHORT")?.ToUpper()) {
case "NONE": ModeDeliveryNoteStats = 0; break;
case "GA_ONLY": ModeDeliveryNoteStats = 1; break;
@@ -172,7 +189,7 @@ namespace Elwig.Helpers {
}
var autoAdjust = (parameters.GetValueOrDefault("AUTOADJUST_BUSINESSSHARES") ?? "").Split(';');
AutoAdjustBs = autoAdjust.Length == 5 ? (
AutoAdjustShares = autoAdjust.Length == 5 ? (
int.TryParse(autoAdjust[0], out var v1) ? v1 : null,
double.TryParse(autoAdjust[1], out var v2) ? v2 : null,
int.TryParse(autoAdjust[2], out var v3) ? v3 : null,
@@ -185,6 +202,11 @@ namespace Elwig.Helpers {
}
private IEnumerable<(string, string?)> GetParamValues() {
string businessShares = "SIMPLE";
switch (ModeBusinessShares) {
case 0: businessShares = "SIMPLE"; break;
case 1: businessShares = "RED_WHITE"; break;
}
string deliveryNoteStats = "SHORT";
switch (ModeDeliveryNoteStats) {
case 0: deliveryNoteStats = "NONE"; break;
@@ -231,9 +253,9 @@ namespace Elwig.Helpers {
case 1: exportEbicsAddress = "LINES"; break;
case 2: exportEbicsAddress = "FULL"; break;
}
string autoAdjust = $"{AutoAdjustBs.AllowanceKg};{AutoAdjustBs.AllowanceBs?.ToString(CultureInfo.InvariantCulture)};" +
$"{AutoAdjustBs.AllowanceKgPerBs};{AutoAdjustBs.AllowancePercent?.ToString(CultureInfo.InvariantCulture)};" +
$"{AutoAdjustBs.MinBs}";
string autoAdjust = $"{AutoAdjustShares.AllowanceKg};{AutoAdjustShares.AllowanceShares?.ToString(CultureInfo.InvariantCulture)};" +
$"{AutoAdjustShares.AllowanceKgPerShare};{AutoAdjustShares.AllowancePercent?.ToString(CultureInfo.InvariantCulture)};" +
$"{AutoAdjustShares.MinShares}";
return [
("CLIENT_NAME_TOKEN", NameToken),
("CLIENT_NAME_SHORT", NameShort),
@@ -252,6 +274,8 @@ namespace Elwig.Helpers {
("CLIENT_BIC", Bic),
("CLIENT_IBAN", Iban),
("CLIENT_ORGANIC_AUTHORITY", OrganicAuthority),
("ENABLE_MEMBERHISTORY", EnableMemberHistory ? "YES" : "NO"),
("MODE_BUSINESSSHARES", businessShares),
("MODE_DELIVERYNOTE_STATS", deliveryNoteStats),
("MODE_WINEQUALITYSTATISTICS", modeWineQualityStatistics),
("ORDERING_MEMBERLIST", orderingMemberList),
+6 -2
View File
@@ -50,6 +50,7 @@ namespace Elwig.Helpers {
public string? SyncUrl = null;
public string SyncUsername = "";
public string SyncPassword = "";
public bool SyncMain = false;
public string? SmtpHost = null;
public int? SmtpPort = null;
@@ -57,9 +58,10 @@ namespace Elwig.Helpers {
public string? SmtpUsername = null;
public string? SmtpPassword = null;
public string? SmtpFrom = null;
public (string Host, int Port, string Mode, string Username, string Password, string From)? Smtp =>
public double SmtpWait = 0;
public (string Host, int Port, string Mode, string Username, string Password, string From, double Wait)? Smtp =>
SmtpHost == null || SmtpPort == null || SmtpMode == null || SmtpUsername == null || SmtpPassword == null || SmtpFrom == null ?
null : (SmtpHost, (int)SmtpPort, SmtpMode, SmtpUsername, SmtpPassword, SmtpFrom);
null : (SmtpHost, (int)SmtpPort, SmtpMode, SmtpUsername, SmtpPassword, SmtpFrom, SmtpWait);
public IList<ScaleConfig> Scales;
private readonly List<ScaleConfig> ScaleList = [];
@@ -85,6 +87,7 @@ namespace Elwig.Helpers {
SyncUrl = config["sync:url"];
SyncUsername = config["sync:username"] ?? "";
SyncPassword = config["sync:password"] ?? "";
SyncMain = TrueValues.Contains(config["sync:main"]?.ToLower());
SmtpHost = config["smtp:host"];
SmtpPort = config["smtp:port"]?.All(char.IsAsciiDigit) == true && config["smtp:port"]?.Length > 0 ? int.Parse(config["smtp:port"]!) : null;
@@ -92,6 +95,7 @@ namespace Elwig.Helpers {
SmtpUsername = config["smtp:username"];
SmtpPassword = config["smtp:password"];
SmtpFrom = config["smtp:from"];
SmtpWait = double.TryParse(config["smtp:wait"], out var res) ? res : 0;
var scales = config.AsEnumerable().Where(i => i.Key.StartsWith("scale.")).GroupBy(i => i.Key.Split(':')[0][6..]).Select(i => i.Key).Order();
ScaleList.Clear();
+6 -6
View File
@@ -6,7 +6,7 @@ using System;
namespace Elwig.Helpers.Export {
using Row = Tuple<(string?, string?, string?, string?, string, int, string, int), (string, int, string, string, string, int, string, double, double)>;
using Row = Tuple<(string?, string?, string?, string?, string, int, string, bool, int), (string, int, string, string, string, int, string, double, double)>;
public class Bki : Csv<Row> {
@@ -29,7 +29,7 @@ namespace Elwig.Helpers.Export {
using var cnx = await AppDbContext.ConnectAsync();
using var cmd = cnx.CreateCommand();
cmd.CommandText = $"""
SELECT lfbis_nr, name, other_names, billing_name, address, plz, ort, area,
SELECT lfbis_nr, name, other_names, billing_name, address, plz, ort, vollablieferer, area,
date, weight, type, sortid, qualid, year, hkid, kmw, oe
FROM v_bki_delivery
WHERE year = {year}
@@ -38,8 +38,8 @@ namespace Elwig.Helpers.Export {
List<Row> rows = [];
while (await r.ReadAsync()) {
rows.Add(new(
(r.IsDBNull(0) ? null : r.GetString(0), r.IsDBNull(1) ? null : r.GetString(1), r.IsDBNull(2) ? null : r.GetString(2), r.IsDBNull(3) ? null : r.GetString(3), r.GetString(4), r.GetInt32(5), r.GetString(6), r.GetInt32(7)),
(r.GetString(8), r.GetInt32(9), r.GetString(10), r.GetString(11), r.GetString(12), r.GetInt32(13), r.GetString(14), r.GetDouble(15), r.GetDouble(16))
(r.IsDBNull(0) ? null : r.GetString(0), r.IsDBNull(1) ? null : r.GetString(1), r.IsDBNull(2) ? null : r.GetString(2), r.IsDBNull(3) ? null : r.GetString(3), r.GetString(4), r.GetInt32(5), r.GetString(6), r.GetBoolean(7), r.GetInt32(8)),
(r.GetString(9), r.GetInt32(10), r.GetString(11), r.GetString(12), r.GetString(13), r.GetInt32(14), r.GetString(15), r.GetDouble(16), r.GetDouble(17))
));
}
@@ -52,14 +52,14 @@ namespace Elwig.Helpers.Export {
public override string FormatRow(Row row) {
var (member, delivery) = row;
var (lfBisNr, familyName, name, billingName, address, plz, ort, area) = member;
var (lfBisNr, familyName, name, billingName, address, plz, ort, vollablieferer, area) = member;
var (date, weight, type, sortid, qualid, year, hkid, kmw, oe) = delivery;
var (n1, n2) = billingName == null ? (familyName, name) : Utils.SplitName(billingName, familyName);
var (a1, a2) = Utils.SplitAddress(address);
var memberData = $"{lfBisNr};{n1};{n2};{a1};\t{a2};{plz};{ort}";
var deliveryData = $"{string.Join(".", date.Split("-").Reverse())};{weight};TB;{(type == "W" ? "J" : "")};{(type == "R" ? "J" : "")};{sortid};;;{qualid};{year};{hkid};{kmw:0.0};{oe:0}";
var vollData = $"N;;;{area / 10_000.0}";
var vollData = $"{(vollablieferer ? "J" : "N")};{(vollablieferer ? area / 10_000.0 : null):N4};{(vollablieferer ? area / 10_000.0 : null):N4};{area / 10_000.0:N4}";
return $"{_clientData};{memberData};{deliveryData};{vollData}";
}
+106 -29
View File
@@ -1,4 +1,6 @@
using Elwig.Models;
using Elwig.Models.Entities;
using Elwig.Services;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
@@ -59,6 +61,7 @@ namespace Elwig.Helpers.Export {
List<BillingAddr> BillingAddresses,
List<MemberTelNr> TelephoneNumbers,
List<MemberEmailAddr> EmailAddresses,
List<MemberHistory> MemberHistory,
List<AreaCom> AreaCommitments,
List<AreaComContract> Contracts,
List<WbRd> Riede,
@@ -76,7 +79,7 @@ namespace Elwig.Helpers.Export {
foreach (var filename in filenames) {
try {
data.Add(new([], [], [], [], [], [], [], new([], [], [], [], [], new() {
data.Add(new([], [], [], [], [], [], [], new([], [], [], [], [], [], new() {
["member"] = [],
["area_commitment_contract"] = [],
["area_commitment"] = [],
@@ -142,6 +145,17 @@ namespace Elwig.Helpers.Export {
}
}
var historyJson = zip.GetEntry("member_history.json");
if (historyJson != null) {
using var reader = new StreamReader(historyJson.Open(), Utils.UTF8);
string? line;
while ((line = await reader.ReadLineAsync()) != null) {
var obj = JsonNode.Parse(line)!.AsObject();
var h = obj.ToMemberHistory();
r.MemberHistory.Add(h);
}
}
// legacy area commitments
var areaComsJson = zip.GetEntry("area_commitments.json");
if (areaComsJson != null) {
@@ -208,16 +222,11 @@ namespace Elwig.Helpers.Export {
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);
InteractionService.ShowException("Fehler beim Importieren", $"Die Elwig-Export-Datei '{Path.GetFileName(filename)}' konnte nicht verarbeitet werden und wird übersprungen", exc);
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) {
if (InteractionService.AskException("Fehler beim Importieren", $"Die Elwig-Export-Datei '{Path.GetFileName(filename)}' konnte nicht verarbeitet werden. Soll sie in Zukunft übersprungen werden?", exc)) {
await AddImportedFiles(Path.GetFileName(filename));
}
}
@@ -227,11 +236,13 @@ namespace Elwig.Helpers.Export {
var importedAreaComs = 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, contracts, riede, wbKgs, wbGls, deliveries, deliveryParts, modifiers, timestamps), meta) in data.Zip(metaData)) {
foreach (var ((members, billingAddresses, telephoneNumbers, emailAddresses, history, areaCommitments, contracts, riede, wbKgs, wbGls, deliveries, deliveryParts, modifiers, timestamps), meta) in data.Zip(metaData)) {
var branch = branches[meta.ZwstId];
var device = meta.Device;
using var ctx = new AppDbContext();
var tx = await ctx.Database.BeginTransactionAsync();
await ctx.Database.ExecuteSqlAsync($"UPDATE client_parameter SET value = '0' WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS'");
var kgnrs = wbKgs.Select(k => k.KgNr).ToList();
var duplicateKgNrs = await ctx.WbKgs
@@ -239,6 +250,12 @@ namespace Elwig.Helpers.Export {
.Select(k => k.KgNr)
.ToListAsync();
var histNrs = history.Select(h => h.HistNr).ToList();
var duplicateHistNrs = await ctx.MemberHistory
.Where(h => histNrs.Contains(h.HistNr))
.Select(h => h.HistNr)
.ToListAsync();
var mgnrs = members.Select(m => m.MgNr).ToList();
var duplicateMgNrs = await ctx.Members
.Where(m => mgnrs.Contains(m.MgNr))
@@ -328,6 +345,11 @@ namespace Elwig.Helpers.Export {
importedMembers.Add((meta.FileName, meta.ZwstId, meta.Device, n, o, members.Count - n - o, meta.MemberFilters));
}
if (importDuplicateMembers || importNewMembers) {
ctx.UpdateRange(history.Where(h => duplicateHistNrs.Contains(h.HistNr)));
ctx.AddRange(history.Where(h => !duplicateHistNrs.Contains(h.HistNr)));
}
if (importDuplicateContracts) {
ctx.RemoveRange(ctx.AreaCommitments.IgnoreAutoIncludes().Where(c => duplicateFbNrs.Contains(c.FbNr)));
ctx.UpdateRange(contracts.Where(c => duplicateFbNrs.Contains(c.FbNr)));
@@ -385,6 +407,8 @@ namespace Elwig.Helpers.Export {
}
await ctx.SaveChangesAsync();
await ctx.Database.ExecuteSqlAsync($"UPDATE client_parameter SET value = '1' WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS'");
await tx.CommitAsync();
var primaryKeys = new Dictionary<string, string>() {
["member"] = "mgnr, 0, 0",
@@ -411,7 +435,7 @@ namespace Elwig.Helpers.Export {
}
App.HintContextChange();
MessageBox.Show(
InteractionService.ShowInformation("Importieren erfolgreich",
$"Das importieren der Daten war erfolgreich!\n" +
$"Folgendes wurde importiert:\n" +
string.Join("\n", [
@@ -433,37 +457,32 @@ namespace Elwig.Helpers.Export {
$" ({d.New} neu, {d.Overwritten} überschr., {d.NotImported} nicht importiert)\n" +
$" Zwst.: {branches[d.ZwstId].Name} (Gerät {d.Device})\n" +
$" Filter: {d.Filters}")
]),
"Importieren erfolgreich",
MessageBoxButton.OK, MessageBoxImage.Information);
]));
} catch (Exception exc) {
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;
MessageBox.Show(str, "Fehler beim Importieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Fehler beim Importieren", "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\nEvtl. muss die Datenbank manuell auf dieses Gerät kopieren werden", exc);
}
GC.Collect();
GC.WaitForPendingFinalizers();
}
private static bool ImportQuestion(string branch, string device, string subject, bool duplicate, int number) {
return MessageBox.Show(
return InteractionService.AskQuestion($"{subject} importieren",
$"Sollen {number} {(duplicate ? "" : "neue ")}{subject} durch die Zweigstelle\n" +
$"{branch} (Gerät {device}) {(duplicate ? "überschrieben" : "importiert")} werden?",
$"{subject} importieren",
MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes
) == MessageBoxResult.Yes;
$"{branch} (Gerät {device}) {(duplicate ? "überschrieben" : "importiert")} werden?", true);
}
public static Task Export(string filename, IEnumerable<Member> members, IEnumerable<WbKg> wbKgs, IEnumerable<string> filters) {
public static Task Export(string filename, IEnumerable<Member> members, IEnumerable<MemberHistory> history, IEnumerable<WbKg> wbKgs, IEnumerable<string> filters) {
return new ElwigExport {
Members = (members, filters),
History = (history, ["von exportierten Mitgliedern"]),
WbKgs = (wbKgs, ["von exportierten Mitgliedern"]),
}.Export(filename);
}
public static Task Export(string filename, IEnumerable<Member> members, IEnumerable<AreaComContract> areaComs, IEnumerable<WbKg> wbKgs, IEnumerable<string> filters) {
public static Task Export(string filename, IEnumerable<Member> members, IEnumerable<MemberHistory> history, IEnumerable<AreaComContract> areaComs, IEnumerable<WbKg> wbKgs, IEnumerable<string> filters) {
return new ElwigExport {
Members = (members, filters),
History = (history, ["von exportierten Mitgliedern"]),
AreaComs = (areaComs, ["von exportierten Mitgliedern"]),
WbKgs = (wbKgs, ["von exportierten Mitgliedern und Flächenbindungen"]),
}.Export(filename);
@@ -479,6 +498,7 @@ namespace Elwig.Helpers.Export {
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<MemberHistory> History, IEnumerable<string> Filters)? History { get; set; }
public (IEnumerable<AreaComContract> AreaComs, IEnumerable<string> Filters)? AreaComs { get; set; }
public (IEnumerable<Delivery> Deliveries, IEnumerable<string> Filters)? Deliveries { get; set; }
@@ -498,17 +518,21 @@ namespace Elwig.Helpers.Export {
["zwstid"] = App.ZwstId,
["device"] = Environment.MachineName,
};
if (WbKgs != null) {
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)
obj["members"] = new JsonObject {
["count"] = Members.Value.Members.Count(),
["filters"] = new JsonArray(Members.Value.Filters.Select(f => (JsonNode)f).ToArray()),
};
if (History != null)
obj["member_history"] = new JsonObject {
["count"] = History.Value.History.Count(),
["filters"] = new JsonArray(History.Value.Filters.Select(f => (JsonNode)f).ToArray()),
};
if (AreaComs != null)
obj["area_commitment_contracts"] = new JsonObject {
["count"] = AreaComs.Value.AreaComs.Count(),
@@ -539,6 +563,13 @@ namespace Elwig.Helpers.Export {
await writer.WriteLineAsync(m.ToJson().ToJsonString(Utils.JsonOpts));
}
}
if (History != null) {
var json = zip.CreateEntry("member_history.json", CompressionLevel.SmallestSize);
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
foreach (var h in History.Value.History) {
await writer.WriteLineAsync(h.ToJson().ToJsonString(Utils.JsonOpts));
}
}
if (AreaComs != null) {
var json = zip.CreateEntry("area_commitment_contracts.json", CompressionLevel.SmallestSize);
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
@@ -597,7 +628,10 @@ namespace Elwig.Helpers.Export {
["birthday"] = m.Birthday,
["entry_date"] = m.EntryDate != null ? $"{m.EntryDate:yyyy-MM-dd}" : null,
["exit_date"] = m.ExitDate != null ? $"{m.ExitDate:yyyy-MM-dd}" : null,
["business_shares"] = m.BusinessShares,
["shares"] = m.Shares,
["shares_red"] = m.SharesRed,
["shares_white"] = m.SharesWhite,
["shares_dormant"] = m.SharesDormant,
["accounting_nr"] = m.AccountingNr,
["zwstid"] = m.ZwstId,
["lfbis_nr"] = m.LfbisNr,
@@ -666,7 +700,10 @@ namespace Elwig.Helpers.Export {
Birthday = json["birthday"]?.AsValue().GetValue<string>(),
EntryDateString = json["entry_date"]?.AsValue().GetValue<string>(),
ExitDateString = json["exit_date"]?.AsValue().GetValue<string>(),
BusinessShares = json["business_shares"]?.AsValue().GetValue<int>() ?? 0,
Shares = json["shares"]?.AsValue().GetValue<int>() ?? json["business_shares"]?.AsValue().GetValue<int>() ?? 0,
SharesRed = json["shares_red"]?.AsValue().GetValue<int>() ?? 0,
SharesWhite = json["shares_white"]?.AsValue().GetValue<int>() ?? 0,
SharesDormant = json["shares_dormant"]?.AsValue().GetValue<int>() ?? 0,
AccountingNr = json["accounting_nr"]?.AsValue().GetValue<string>(),
ZwstId = json["zwstid"]?.AsValue().GetValue<string>(),
LfbisNr = json["lfbis_nr"]?.AsValue().GetValue<string>(),
@@ -711,6 +748,46 @@ namespace Elwig.Helpers.Export {
DateTime.ParseExact(modifiedAt, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None)));
}
public static JsonObject ToJson(this MemberHistory h) {
return new JsonObject {
["histnr"] = h.HistNr,
["from_mgnr"] = h.FromMgNr,
["from_type"] = h.FromType?.ToString().ToLower(),
["to_mgnr"] = h.ToMgNr,
["to_type"] = h.ToType?.ToString().ToLower(),
["date_notice"] = h.DateNoticeString,
["date_effective"] = h.DateEffectiveString,
["reason"] = h.Reason,
["source"] = h.Source,
["shares"] = h.Shares,
["signed"] = h.MemberHasSigned,
["value_per_share"] = h.ValuePerShare,
["currency"] = h.CurrencyCode,
["deduct_year"] = h.DeductYear,
["comment"] = h.Comment,
};
}
public static MemberHistory ToMemberHistory(this JsonNode json) {
return new MemberHistory {
HistNr = json["histnr"]!.AsValue().GetValue<int>(),
FromMgNr = json["from_mgnr"]?.AsValue().GetValue<int>(),
FromType = Enum.TryParse<BusinessShareType>(json["from_type"]?.AsValue().GetValue<string>(), true, out var from) ? from : null,
ToMgNr = json["to_mgnr"]?.AsValue().GetValue<int>(),
ToType = Enum.TryParse<BusinessShareType>(json["to_type"]?.AsValue().GetValue<string>(), true, out var to) ? to : null,
DateNoticeString = json["date_notice"]!.AsValue().GetValue<string>(),
DateEffectiveString = json["date_effective"]!.AsValue().GetValue<string>(),
Reason = json["reason"]!.AsValue().GetValue<string>(),
Source = json["source"]!.AsValue().GetValue<string>(),
Shares = json["shares"]!.AsValue().GetValue<int>(),
MemberHasSigned = json["signed"]?.AsValue().GetValue<bool>() ?? false,
ValuePerShare = json["value_per_share"]?.AsValue().GetValue<decimal>(),
CurrencyCode = json["currency"]?.AsValue().GetValue<string>(),
DeductYear = json["deduct_year"]?.AsValue().GetValue<int>(),
Comment = json["comment"]?.AsValue().GetValue<string>(),
};
}
public static JsonObject ToJson(this AreaComContract c) {
return new JsonObject {
["fbnr"] = c.FbNr,
@@ -874,7 +951,7 @@ namespace Elwig.Helpers.Export {
if (p.Unloading != null) obj["unloading"] = p.Unloading;
if (p.Temperature != null) obj["temperature"] = p.Temperature;
if (p.Acid != null) obj["acid"] = p.Acid;
if (p.ScaleId != null) obj["scale_id"] = p.ScaleId;
if (p.ScaleTerminalId != null) obj["scale_terminal"] = p.ScaleTerminalId;
if (p.WeighingData != null) obj["weighing_data"] = JsonNode.Parse(p.WeighingData);
if (p.WeighingReason != null) obj["weighing_reason"] = p.WeighingReason;
return obj;
@@ -945,7 +1022,7 @@ namespace Elwig.Helpers.Export {
Unloading = p["unloading"]?.AsValue().GetValue<string>() ?? ((p["lesewagen"]?.AsValue().GetValue<bool>() ?? false) ? DeliveryPart.Pumped : null),
Temperature = p["temperature"]?.AsValue().GetValue<double>(),
Acid = p["acid"]?.AsValue().GetValue<double>(),
ScaleId = p["scale_id"]?.AsValue().GetValue<string>(),
ScaleTerminalId = p["scale_terminal"]?.AsValue().GetValue<string>() ?? p["scale_id"]?.AsValue().GetValue<string>(),
WeighingData = p["weighing_data"]?.AsObject().ToJsonString(Utils.JsonOpts),
WeighingReason = p["weighing_reason"]?.AsValue().GetValue<string>(),
}, createdAt == null || modifiedAt == null ? null :
+26
View File
@@ -4,11 +4,14 @@ using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.IO.Hashing;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace Elwig.Helpers {
public static partial class Extensions {
@@ -145,5 +148,28 @@ namespace Elwig.Helpers {
}
return [.. list];
}
public static IEnumerable<T> Join<T>(this IEnumerable<T> src, Func<T> separatorFactory) {
var srcArr = src.ToArray();
for (int i = 0; i < srcArr.Length; i++) {
yield return srcArr[i];
if (i < srcArr.Length - 1) {
yield return separatorFactory();
}
}
}
public static void AddToolTipCell(this Grid grid, string text, int row, int col, int colSpan = 1, bool bold = false, bool alignRight = false, bool alignCenter = false) {
var tb = new TextBlock() {
Text = text,
TextAlignment = alignRight ? TextAlignment.Right : alignCenter ? TextAlignment.Center : TextAlignment.Left,
Margin = new(0, 12 * row, 0, 0),
FontWeight = bold ? FontWeights.Bold : FontWeights.Normal,
};
tb.SetValue(Grid.ColumnProperty, col);
tb.SetValue(Grid.ColumnSpanProperty, colSpan);
grid.Children.Add(tb);
}
}
}
+6 -17
View File
@@ -1,8 +1,7 @@
using Elwig.Services;
using Elwig.Windows;
using System;
using System.Drawing.Printing;
using System.Threading.Tasks;
using System.Windows;
namespace Elwig.Helpers.Printing {
public static class Pdf {
@@ -32,22 +31,12 @@ namespace Elwig.Helpers.Printing {
});
}
public static async Task Print(string path, int copies = 1, bool doublePaged = false) {
await Print(path, new() {
Copies = (short)copies,
Collate = true,
Duplex = doublePaged ? Duplex.Vertical : Duplex.Simplex,
});
}
public static Task Print(string path, PrinterSettings settings) {
public static Task Print(string path, int copies = 1, bool doublePaged = false) {
try {
using var printDoc = new PdfPrintDocument(path) {
PrinterSettings = settings,
};
printDoc.Print();
} catch (Exception e) {
MessageBox.Show("Beim Drucken ist ein Fehler aufgetreten:\n\n" + e.Message, "Fehler beim Drucken", MessageBoxButton.OK, MessageBoxImage.Error);
var printer = new PdfPrinter();
printer.Print(path, copies, doublePaged);
} catch (Exception exc) {
InteractionService.ShowException("Fehler beim Drucken", "Beim Drucken ist ein Fehler aufgetreten", exc);
}
return Task.CompletedTask;
}
-102
View File
@@ -1,102 +0,0 @@
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Printing;
using System.Runtime.InteropServices;
namespace Elwig.Helpers.Printing {
public class PdfPrintDocument : PrintDocument {
private readonly IntPtr _handle;
private readonly int _pageCount;
private readonly double _dpi;
private int _currentPage;
public PdfPrintDocument(string path, string? password = null, double dpi = 300.0) :
base() {
_handle = PdfiumNative.FPDF_LoadDocument(path, password);
_pageCount = PdfiumNative.FPDF_GetPageCount(_handle);
_dpi = dpi;
}
protected override void Dispose(bool disposing) {
PdfiumNative.FPDF_CloseDocument(_handle);
base.Dispose(disposing);
}
protected override void OnBeginPrint(PrintEventArgs evt) {
_currentPage = (PrinterSettings.FromPage != 0) ? (PrinterSettings.FromPage - 1) : 0;
base.OnBeginPrint(evt);
}
protected override void OnPrintPage(PrintPageEventArgs evt) {
if (_currentPage < _pageCount) {
IntPtr page = PdfiumNative.FPDF_LoadPage(_handle, _currentPage);
double width = PdfiumNative.FPDF_GetPageWidth(page);
double height = PdfiumNative.FPDF_GetPageHeight(page);
int pixelWidth = (int)(width / 72.0 * _dpi);
int pixelHeight = (int)(height / 72.0 * _dpi);
IntPtr bitmap = PdfiumNative.FPDFBitmap_Create(pixelWidth, pixelHeight, 1);
PdfiumNative.FPDF_RenderPageBitmap(bitmap, page, 0, 0, pixelWidth, pixelHeight, 0, 0);
IntPtr buffer = PdfiumNative.FPDFBitmap_GetBuffer(bitmap);
int stride = PdfiumNative.FPDFBitmap_GetStride(bitmap);
using (var bmp = new Bitmap(pixelWidth, pixelHeight, stride, PixelFormat.Format32bppArgb, buffer)) {
evt.Graphics?.DrawImage(bmp, evt.PageBounds);
}
_currentPage++;
PdfiumNative.FPDFBitmap_Destroy(bitmap);
PdfiumNative.FPDF_ClosePage(page);
}
evt.HasMorePages = _currentPage < ((PrinterSettings.ToPage == 0) ? _pageCount : Math.Min(PrinterSettings.ToPage, _pageCount));
base.OnPrintPage(evt);
}
}
internal partial class PdfiumNative {
[LibraryImport("pdfium.dll")]
public static partial void FPDF_InitLibrary();
[LibraryImport("pdfium.dll")]
public static partial void FPDF_DestroyLibrary();
[LibraryImport("pdfium.dll", StringMarshalling = StringMarshalling.Utf8)]
public static partial IntPtr FPDF_LoadDocument(string filePath, string? password);
[LibraryImport("pdfium.dll")]
public static partial void FPDF_CloseDocument(IntPtr document);
[LibraryImport("pdfium.dll")]
public static partial int FPDF_GetPageCount(IntPtr document);
[LibraryImport("pdfium.dll")]
public static partial IntPtr FPDF_LoadPage(IntPtr document, int pageIndex);
[LibraryImport("pdfium.dll")]
public static partial void FPDF_ClosePage(IntPtr page);
[LibraryImport("pdfium.dll")]
public static partial double FPDF_GetPageWidth(IntPtr page);
[LibraryImport("pdfium.dll")]
public static partial double FPDF_GetPageHeight(IntPtr page);
[LibraryImport("pdfium.dll")]
public static partial IntPtr FPDFBitmap_Create(int width, int height, int alpha);
[LibraryImport("pdfium.dll")]
public static partial void FPDFBitmap_Destroy(IntPtr bitmap);
[LibraryImport("pdfium.dll")]
public static partial IntPtr FPDFBitmap_GetBuffer(IntPtr bitmap);
[LibraryImport("pdfium.dll")]
public static partial int FPDFBitmap_GetStride(IntPtr bitmap);
[LibraryImport("pdfium.dll")]
public static partial void FPDF_RenderPageBitmap(IntPtr bitmap, IntPtr page, int start_x, int start_y, int size_x, int size_y, int rotate, int flags);
}
}
+83
View File
@@ -0,0 +1,83 @@
using System;
using System.IO;
using System.Printing;
namespace Elwig.Helpers.Printing {
public class PdfPrinter {
public string PrinterName { get; init; }
public PdfPrinter(string? printerName = null) {
PrinterName = printerName ?? GetDefaultPrinterName() ?? throw new InvalidOperationException("No default Windows printer is configured.");
}
public void Print(string pdfPath, int copies = 1, bool doublePaged = false) {
if (!File.Exists(pdfPath)) throw new FileNotFoundException("PDF file not found.", pdfPath);
IntPtr document = PdfiumNative.FPDF_LoadDocument(pdfPath, null);
if (document == IntPtr.Zero) throw new InvalidOperationException("PDFium could not open the PDF.");
try {
int pageCount = PdfiumNative.FPDF_GetPageCount(document);
if (pageCount <= 0)
return;
using var printer = new Win32Printer.Printer(PrinterName);
printer.TrySetDuplex(doublePaged ? Duplexing.TwoSidedLongEdge : Duplexing.OneSided);
printer.StartDocument(Path.GetFileName(pdfPath));
bool documentStarted = true;
try {
for (int i = 0; i < copies; i++) {
for (int j = 0; j < pageCount; j++) {
PrintPage(document, printer, j);
}
}
printer.EndDocument();
documentStarted = false;
} finally {
if (documentStarted) printer.AbortDocument();
}
} finally {
PdfiumNative.FPDF_CloseDocument(document);
}
}
private static void PrintPage(IntPtr document, Win32Printer.Printer printer, int pageIndex) {
if (!PdfiumNative.FPDF_GetPageSizeByIndex(document, pageIndex, out double pageWidthPt, out double pageHeightPt)) {
throw new InvalidOperationException($"Unable to obtain PDF page {pageIndex} size.");
}
IntPtr page = PdfiumNative.FPDF_LoadPage(document, pageIndex);
if (page == IntPtr.Zero)
throw new InvalidOperationException($"Unable to load PDF page {pageIndex}.");
try {
int renderWidth = (int)Math.Round(pageWidthPt / 72.0 * printer.DpiX);
int renderHeight = (int)Math.Round(pageHeightPt / 72.0 * printer.DpiY);
int offsetX = (printer.PrintableWidth - renderWidth) / 2;
int offsetY = (printer.PrintableHeight - renderHeight) / 2;
printer.StartPage();
try {
PdfiumNative.FPDF_RenderPage(printer.Hdc, page, offsetX, offsetY, renderWidth, renderHeight, 0, PdfiumNative.FPDF_PRINTING | PdfiumNative.FPDF_ANNOT);
} finally {
printer.EndPage();
}
} finally {
PdfiumNative.FPDF_ClosePage(page);
}
}
private static string? GetDefaultPrinterName() {
using var key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows NT\CurrentVersion\Windows");
string? printer = key?.GetValue("Device") as string;
if (string.IsNullOrWhiteSpace(printer))
return null;
// Device has the form:
// Printer Name,winspool,PORT
int comma = printer.IndexOf(',');
return comma >= 0 ? printer[..comma] : printer;
}
}
}
+38
View File
@@ -0,0 +1,38 @@
using System;
using System.Runtime.InteropServices;
namespace Elwig.Helpers.Printing {
internal partial class PdfiumNative {
public const int FPDF_ANNOT = 0x01;
public const int FPDF_PRINTING = 0x800;
[LibraryImport("pdfium.dll")]
public static partial void FPDF_InitLibrary();
[LibraryImport("pdfium.dll")]
public static partial void FPDF_DestroyLibrary();
[LibraryImport("pdfium.dll", StringMarshalling = StringMarshalling.Utf8)]
public static partial IntPtr FPDF_LoadDocument(string filePath, string? password);
[LibraryImport("pdfium.dll")]
public static partial void FPDF_CloseDocument(IntPtr document);
[LibraryImport("pdfium.dll")]
public static partial int FPDF_GetPageCount(IntPtr document);
[LibraryImport("pdfium.dll")]
public static partial IntPtr FPDF_LoadPage(IntPtr document, int pageIndex);
[LibraryImport("pdfium.dll")]
public static partial void FPDF_ClosePage(IntPtr page);
[LibraryImport("pdfium.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static partial bool FPDF_GetPageSizeByIndex(IntPtr document, int pageIndex, out double width, out double height);
[LibraryImport("pdfium.dll")]
public static partial void FPDF_RenderPage(IntPtr hdc, IntPtr page, int startX, int startY, int sizeX, int sizeY, int rotate, int flags);
}
}
+239
View File
@@ -0,0 +1,239 @@
using System;
using System.ComponentModel;
using System.Printing;
using System.Runtime.InteropServices;
namespace Elwig.Helpers.Printing {
public partial class Win32Printer {
private const int DM_OUT_BUFFER = 2;
private const int HORZRES = 8;
private const int VERTRES = 10;
private const int LOGPIXELSX = 88;
private const int LOGPIXELSY = 90;
private const int PHYSICALWIDTH = 110;
private const int PHYSICALHEIGHT = 111;
private const int PHYSICALOFFSETX = 112;
private const int PHYSICALOFFSETY = 113;
private const int DM_DUPLEX = 0x1000;
private const short DMDUP_SIMPLEX = 1;
private const short DMDUP_VERTICAL = 2;
private const short DMDUP_HORIZONTAL = 3;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct DOCINFO {
public int cbSize;
[MarshalAs(UnmanagedType.LPWStr)]
public string? lpszDocName;
[MarshalAs(UnmanagedType.LPWStr)]
public string? lpszOutput;
[MarshalAs(UnmanagedType.LPWStr)]
public string? lpszDatatype;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct DEVMODE {
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string dmDeviceName;
public short dmSpecVersion;
public short dmDriverVersion;
public short dmSize;
public short dmDriverExtra;
public int dmFields;
public short dmOrientation;
public short dmPaperSize;
public short dmPaperLength;
public short dmPaperWidth;
public short dmScale;
public short dmCopies;
public short dmDefaultSource;
public short dmPrintQuality;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string dmFormName;
public short dmLogPixels;
public int dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
public int dmICMMethod;
public int dmICMIntent;
public int dmMediaType;
public int dmDitherType;
public int dmReserved1;
public int dmReserved2;
public int dmPanningWidth;
public int dmPanningHeight;
}
[DllImport("winspool.drv", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool OpenPrinter(string pPrinterName, out IntPtr phPrinter, IntPtr pDefault);
[DllImport("winspool.drv", SetLastError = true)]
private static extern bool ClosePrinter(IntPtr hPrinter);
[DllImport("winspool.drv", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter, string pDeviceName, IntPtr pDevModeOutput, IntPtr pDevModeInput, int fMode);
[DllImport("gdi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr CreateDC(string? pDriver, string? pDevice, string? pOutput, IntPtr pInitData);
[DllImport("gdi32.dll", SetLastError = true)]
private static extern bool DeleteDC(IntPtr hdc);
[DllImport("gdi32.dll", SetLastError = true)]
private static extern int GetDeviceCaps(IntPtr hdc, int index);
[DllImport("gdi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern int StartDoc(IntPtr hdc, ref DOCINFO lpdi);
[DllImport("gdi32.dll", SetLastError = true)]
private static extern int EndDoc(IntPtr hdc);
[DllImport("gdi32.dll", SetLastError = true)]
private static extern int AbortDoc(IntPtr hdc);
[DllImport("gdi32.dll", SetLastError = true)]
private static extern int StartPage(IntPtr hdc);
[DllImport("gdi32.dll", SetLastError = true)]
private static extern int EndPage(IntPtr hdc);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GlobalAlloc(uint uFlags, UIntPtr dwBytes);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GlobalFree(IntPtr hMem);
[DllImport("kernel32.dll")]
private static extern IntPtr GlobalLock(IntPtr hMem);
[DllImport("kernel32.dll")]
private static extern bool GlobalUnlock(IntPtr hMem);
public class Printer : IDisposable {
private readonly string _printerName;
private readonly IntPtr _printerHandle;
private readonly IntPtr _devMode;
private readonly IntPtr _hdc;
public IntPtr Hdc => _hdc;
public int PrintableWidth => GetDeviceCaps(_hdc, HORZRES);
public int PrintableHeight => GetDeviceCaps(_hdc, VERTRES);
public int DpiX => GetDeviceCaps(_hdc, LOGPIXELSX);
public int DpiY => GetDeviceCaps(_hdc, LOGPIXELSY);
public int PhysicalWidth => GetDeviceCaps(_hdc, PHYSICALWIDTH);
public int PhysicalHeight => GetDeviceCaps(_hdc, PHYSICALHEIGHT);
public int PhysicalOffsetX => GetDeviceCaps(_hdc, PHYSICALOFFSETX);
public int PhysicalOffsetY => GetDeviceCaps(_hdc, PHYSICALOFFSETY);
public Printer(string printerName) {
_printerName = printerName;
if (!OpenPrinter(_printerName, out _printerHandle, IntPtr.Zero)) {
throw new Win32Exception(Marshal.GetLastWin32Error(), $"Unable to open printer '{printerName}'.");
}
try {
_devMode = CreateDevMode();
_hdc = CreateDC(null, _printerName, null, _devMode);
if (_hdc == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error(), $"Unable to create printer DC for '{_printerName}'.");
} catch {
if (_devMode != IntPtr.Zero) Marshal.FreeHGlobal(_devMode);
ClosePrinter(_printerHandle);
throw;
}
}
public bool TrySetDuplex(Duplexing duplex) {
var devMode = Marshal.PtrToStructure<DEVMODE>(_devMode);
switch (duplex) {
case Duplexing.OneSided: devMode.dmDuplex = DMDUP_SIMPLEX; break;
case Duplexing.TwoSidedShortEdge: devMode.dmDuplex = DMDUP_HORIZONTAL; break;
case Duplexing.TwoSidedLongEdge: devMode.dmDuplex = DMDUP_VERTICAL; break;
}
if ((devMode.dmFields & DM_DUPLEX) != 0) {
Marshal.StructureToPtr(devMode, _devMode, false);
return true;
} else {
devMode.dmFields |= DM_DUPLEX;
Marshal.StructureToPtr(devMode, _devMode, false);
return false;
}
}
private IntPtr CreateDevMode() {
int size = DocumentProperties(IntPtr.Zero, _printerHandle, _printerName, IntPtr.Zero, IntPtr.Zero, 0);
if (size <= 0)
throw new Win32Exception(Marshal.GetLastWin32Error(), "DocumentProperties failed.");
IntPtr devMode = Marshal.AllocHGlobal(size);
int result = DocumentProperties(IntPtr.Zero, _printerHandle, _printerName, devMode, IntPtr.Zero, DM_OUT_BUFFER);
if (result < 0) {
Marshal.FreeHGlobal(devMode);
throw new Win32Exception(Marshal.GetLastWin32Error(), "Unable to obtain printer DEVMODE.");
}
return devMode;
}
public void StartDocument(string documentName) {
var docInfo = new DOCINFO {
cbSize = Marshal.SizeOf<DOCINFO>(),
lpszDocName = documentName,
lpszDatatype = null,
lpszOutput = null
};
if (StartDoc(_hdc, ref docInfo) <= 0)
throw new Win32Exception(Marshal.GetLastWin32Error(), "StartDoc failed.");
}
public void StartPage() {
if (Win32Printer.StartPage(_hdc) <= 0)
throw new Win32Exception(Marshal.GetLastWin32Error(), "StartPage failed.");
}
public void EndPage() {
if (Win32Printer.EndPage(_hdc) <= 0)
throw new Win32Exception(Marshal.GetLastWin32Error(), "EndPage failed.");
}
public void EndDocument() {
if (EndDoc(_hdc) <= 0)
throw new Win32Exception( Marshal.GetLastWin32Error(), "EndDoc failed.");
}
public void AbortDocument() {
AbortDoc(_hdc);
}
public void Dispose() {
if (_hdc != IntPtr.Zero)
DeleteDC(_hdc);
if (_devMode != IntPtr.Zero)
Marshal.FreeHGlobal(_devMode);
if (_printerHandle != IntPtr.Zero)
ClosePrinter(_printerHandle);
}
}
}
}
+30 -25
View File
@@ -3,12 +3,12 @@ using Elwig.Documents;
using Elwig.Helpers.Billing;
using Elwig.Models;
using Elwig.Models.Entities;
using Elwig.Services;
using iText.Layout.Element;
using LinqKit;
using MailKit.Net.Smtp;
using MailKit.Security;
using Microsoft.EntityFrameworkCore;
using Microsoft.Win32;
using MimeKit;
using System;
using System.Collections;
@@ -31,6 +31,7 @@ using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Markup;
namespace Elwig.Helpers {
@@ -209,16 +210,29 @@ namespace Elwig.Helpers {
return Regex.Replace(iban.Trim(), ".{4}", "$0 ").Trim();
}
public static void RunBackground(string title, Func<Task> a) {
public static void RunBackground(string title, Func<Task> function) {
Task.Run(async () => {
try {
await a();
} catch (Exception e) {
MessageBox.Show(e.ToString(), title, MessageBoxButton.OK, MessageBoxImage.Error);
await function();
} catch (Exception exc) {
InteractionService.ShowException(title, exc);
}
});
}
public static async Task RunForeground(Func<Task> function) {
var isSTA = Thread.CurrentThread.GetApartmentState() == ApartmentState.STA;
if (isSTA) Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
await function();
} catch (Exception exc) {
InteractionService.ShowException(exc);
}
});
if (isSTA) Mouse.OverrideCursor = null;
}
public static void MailTo(string emailAddress) {
MailTo([emailAddress]);
}
@@ -443,7 +457,7 @@ namespace Elwig.Helpers {
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.UserAgent.ParseAdd($"Elwig/{App.Version} ({App.Client?.NameToken}, {App.BranchName}, {Environment.MachineName}, {Environment.OSVersion})");
client.DefaultRequestHeaders.Accept.Clear();
if (accept != null)
client.DefaultRequestHeaders.Accept.Add(new(accept));
@@ -502,7 +516,7 @@ namespace Elwig.Helpers {
public static async Task<SmtpClient?> GetSmtpClient() {
if (App.Config.Smtp == null)
return null;
var (host, port, mode, username, password, _) = App.Config.Smtp.Value;
var (host, port, mode, username, password, _, _) = App.Config.Smtp.Value;
var client = new SmtpClient();
await client.ConnectAsync(host, port, mode == "starttls" ? SecureSocketOptions.StartTls : SecureSocketOptions.None);
await client.AuthenticateAsync(username, password);
@@ -537,7 +551,7 @@ namespace Elwig.Helpers {
subject, docs.Select(d => d.Title).ToArray()
)]);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
return false;
} finally {
if (client != null)
@@ -551,8 +565,7 @@ namespace Elwig.Helpers {
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 (doc.IsPreview) {
MessageBox.Show("Dieses Dokument ist als vorläufig markiert und kann daher nicht ausgedruckt werden!",
"Vorläufiges Dokument", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Vorläufiges Dokument", "Dieses Dokument ist als vorläufig markiert und kann daher nicht ausgedruckt werden!");
return;
}
using (var ctx = new AppDbContext()) {
@@ -561,8 +574,7 @@ namespace Elwig.Helpers {
await doc.Print();
} else if (mode == ExportMode.Email && emailData is (Member, string, string) e) {
if (doc.IsPreview) {
MessageBox.Show("Dieses Dokument ist als vorläufig markiert und kann daher nicht verschickt werden!",
"Vorläufiges Dokument", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Vorläufiges Dokument", "Dieses Dokument ist als vorläufig markiert und kann daher nicht verschickt werden!");
return;
}
using (var ctx = new AppDbContext()) {
@@ -570,25 +582,18 @@ namespace Elwig.Helpers {
}
var success = await SendEmail(e.Member, e.Subject, e.Text, [doc]);
if (success)
MessageBox.Show("Die E-Mail wurde erfolgreich verschickt!\n\nEs kann einige Minuten dauern, bis die E-Mail im Posteingang des Empfängers aufscheint.", "E-Mail verschickt",
MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("E-Mail verschickt", "Die E-Mail wurde erfolgreich verschickt!\n\nEs kann einige Minuten dauern, bis die E-Mail im Posteingang des Empfängers aufscheint.");
} else if (mode == ExportMode.SavePdf) {
if (doc.IsPreview) {
MessageBox.Show("Dieses Dokument ist als vorläufig markiert und sollte daher nicht langfristig gespeichert werden!",
"Vorläufiges Dokument", MessageBoxButton.OK, MessageBoxImage.Warning);
InteractionService.ShowWarning("Vorläufiges Dokument", "Dieses Dokument ist als vorläufig markiert und sollte daher nicht langfristig gespeichert werden!");
}
var d = new SaveFileDialog() {
FileName = $"{NormalizeFileName(filename ?? doc.Title)}.pdf",
DefaultExt = "pdf",
Filter = "PDF-Datei (*.pdf)|*.pdf",
Title = $"{doc.Title} speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
filename = InteractionService.SaveFile(doc.Title, NormalizeFileName(filename ?? doc.Title), "pdf");
if (filename != null) {
using (var ctx = new AppDbContext()) {
await doc.Generate(ctx);
}
doc.SaveTo(d.FileName);
Process.Start("explorer.exe", d.FileName);
doc.SaveTo(filename);
if (!App.Config.Debug) Process.Start("explorer.exe", filename);
}
} else {
using (var ctx = new AppDbContext()) {
+8 -14
View File
@@ -136,8 +136,7 @@ namespace Elwig.Helpers {
return new(false, "PLZ zu kurz");
}
using var ctx = new AppDbContext();
int plz = int.Parse(input.Text);
if (ctx.Postleitzahlen.Find(plz) == null) {
if (!int.TryParse(input.Text, out var plz) || ctx.Postleitzahlen.Find(plz) == null) {
return new(false, "Ungültige PLZ");
}
return new(true, null);
@@ -423,8 +422,7 @@ namespace Elwig.Helpers {
}
using var ctx = new AppDbContext();
int nr = int.Parse(input.Text);
if (!ctx.MgNrExists(nr).GetAwaiter().GetResult()) {
if (!int.TryParse(input.Text, out var nr) || !ctx.MgNrExists(nr).GetAwaiter().GetResult()) {
return new(false, "Ungültige Mitgliedsnummer");
}
@@ -440,8 +438,7 @@ namespace Elwig.Helpers {
}
using var ctx = new AppDbContext();
int nr = int.Parse(input.Text);
if (nr != m?.MgNr && ctx.MgNrExists(nr).GetAwaiter().GetResult()) {
if (!int.TryParse(input.Text, out var nr) || nr != m?.MgNr && ctx.MgNrExists(nr).GetAwaiter().GetResult()) {
return new(false, "Mitgliedsnummer wird bereits verwendet");
}
@@ -476,7 +473,7 @@ namespace Elwig.Helpers {
return res;
} else if (!required && input.Text.Length == 0) {
return new(true, null);
} else if (!ctx.MgNrExists(int.Parse(input.Text)).GetAwaiter().GetResult()) {
} else if (!int.TryParse(input.Text, out var nr) ||!ctx.MgNrExists(nr).GetAwaiter().GetResult()) {
return new(false, "Ein Mitglied mit dieser Mitgliedsnummer existiert nicht");
}
@@ -537,7 +534,7 @@ namespace Elwig.Helpers {
return (parts[0]?.Length == 4) ? new(true, null) : new(false, "Datum ist ungültig");
} else if (p == 1) {
// only month and year provided
int m = parts[0] != null && parts[0] != "" ? int.Parse(parts[0] ?? "0") : 0;
int m = parts[0] != null && parts[0] != "" && int.TryParse(parts[0], out var v) ? v : 0;
if (parts[1]?.Length != 4 || parts[0]?.Length != 2 || m < 1 || m > 12)
return new(false, "Datum ist ungültig");
return new(true, null);
@@ -589,8 +586,7 @@ namespace Elwig.Helpers {
}
using var ctx = new AppDbContext();
int nr = int.Parse(input.Text);
if (nr != c?.FbNr && ctx.FbNrExists(nr).GetAwaiter().GetResult()) {
if (!int.TryParse(input.Text, out var nr) || (nr != c?.FbNr && ctx.FbNrExists(nr).GetAwaiter().GetResult())) {
return new(false, "Flächenbindungsnummer wird bereits verwendet");
}
@@ -605,8 +601,7 @@ namespace Elwig.Helpers {
return new(true, null);
}
var oe = double.Parse(input.Text);
if (oe < 10 || oe >= 300) {
if (!double.TryParse(input.Text, out var oe) || oe < 10 || oe >= 300) {
return new(false, "Ungültiger Oechsle-Wert");
}
@@ -621,8 +616,7 @@ namespace Elwig.Helpers {
return new(true, null);
}
var kmw = double.Parse(input.Text);
if (kmw < 5 || kmw >= 50) {
if (!double.TryParse(input.Text, out var kmw) || kmw < 5 || kmw >= 50) {
return new(false, "Ungültiger KMW-Wert");
}
+5 -9
View File
@@ -1,17 +1,16 @@
using Elwig.Services;
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace Elwig.Helpers.Weighing {
public class AveryEventScale : Scale, IEventScale, IDisposable {
public string Manufacturer => "Avery";
public int InternalScaleNr => 1;
public string Model { get; private set; }
public string ScaleId { get; private set; }
public string ScaleTerminalId { get; private set; }
public bool IsReady { get; private set; }
public bool HasFillingClearance { get; private set; }
@@ -19,13 +18,11 @@ namespace Elwig.Helpers.Weighing {
private bool IsRunning = true;
private readonly Thread BackgroundThread;
private readonly string Connection;
public AveryEventScale(string id, string model, string cnx, string? log = null, bool required = true) :
base(cnx, null, null, null, log, true, !required) {
ScaleId = id;
ScaleTerminalId = id;
Model = model;
Connection = cnx;
IsReady = true;
HasFillingClearance = false;
Stream.WriteTimeout = -1;
@@ -60,9 +57,8 @@ namespace Elwig.Helpers.Weighing {
} catch (TimeoutException) {
await Task.Delay(500);
await Reconnect();
} catch (Exception ex) {
MessageBox.Show($"Beim Wiegen ist ein Fehler Aufgetreten:\n\n{ex.Message} ({ex.GetType().Name})", "Waagenfehler",
MessageBoxButton.OK, MessageBoxImage.Error);
} catch (Exception exc) {
InteractionService.ShowException("Waagenfehler", "Beim Wiegen ist ein Fehler Aufgetreten", exc, showExcType: true);
}
}
}
@@ -5,18 +5,17 @@ using System.Text;
using System.Threading.Tasks;
namespace Elwig.Helpers.Weighing {
public class GassnerScale : Scale, ICommandScale {
public class GassnerEnqScale : Scale, ICommandScale {
public string Manufacturer => "Gassner";
public int InternalScaleNr => 1;
public string Model { get; private set; }
public string ScaleId { get; private set; }
public string ScaleTerminalId { get; private set; }
public bool IsReady { get; private set; }
public bool HasFillingClearance { get; private set; }
public GassnerScale(string id, string model, string cnx, string? empty = null, string? filling = null, int? limit = null, string? log = null) :
public GassnerEnqScale(string id, string model, string cnx, string? empty = null, string? filling = null, int? limit = null, string? log = null) :
base(cnx, empty, filling, limit, log) {
ScaleId = id;
ScaleTerminalId = id;
Model = model;
IsReady = true;
HasFillingClearance = false;
@@ -24,10 +23,9 @@ namespace Elwig.Helpers.Weighing {
Stream.ReadTimeout = 11000;
}
protected Task SendCommand(char command) => SendCommand(Convert.ToByte(command));
protected Task SendCommand(string command) => SendCommand(Encoding.ASCII.GetBytes(command));
protected async Task SendCommand(byte command) {
byte[] cmd = [command];
protected async Task SendCommand(byte[] cmd) {
await Stream.WriteAsync(cmd);
if (LogPath != null) await File.AppendAllTextAsync(LogPath, Encoding.ASCII.GetString(cmd));
}
@@ -53,15 +51,15 @@ namespace Elwig.Helpers.Weighing {
return line[1..^1];
}
protected async Task<WeighingResult> Weigh(bool incIdentNr, bool retry = true) {
protected async Task<WeighingResult> Weigh(int? internalScaleNr, bool incIdentNr, bool retry = true) {
string record;
try {
await SendCommand(incIdentNr ? '\x05' : '?');
await SendCommand(incIdentNr ? "\x05" : "?");
record = await ReceiveResponse();
} catch (IOException) {
if (!retry || Tcp == null) throw;
ReconnectTcp();
return await Weigh(incIdentNr, false);
return await Weigh(internalScaleNr, incIdentNr, false);
}
if (record.Length != 45)
throw new FormatException("Invalid response from scale: Received record has invalid size");
@@ -80,6 +78,7 @@ namespace Elwig.Helpers.Weighing {
GrossWeight = int.Parse(brutto),
TareWeight = int.Parse(tara),
NetWeight = int.Parse(netto),
ScaleId = scaleNr,
WeighingId = identNr,
FullWeighingId = identNr,
Date = DateOnly.TryParseExact(date, "yyyyMMdd", out var d) ? d : null,
@@ -87,12 +86,12 @@ namespace Elwig.Helpers.Weighing {
};
}
public async Task<WeighingResult> Weigh() {
return await Weigh(true);
public async Task<WeighingResult> Weigh(int? scaleNr = null) {
return await Weigh(scaleNr, true);
}
public async Task<WeighingResult> GetCurrentWeight() {
return await Weigh(false);
public async Task<WeighingResult> GetCurrentWeight(int? scaleNr = null) {
return await Weigh(scaleNr, false);
}
public async Task Empty() {
@@ -126,8 +125,9 @@ namespace Elwig.Helpers.Weighing {
await SetFillingClearance(false);
}
public Task SetDateAndTime(DateTime dateTime) {
throw new NotImplementedException("Für Waagen vom Typ 'Gassner' ist diese Funktion noch nicht implementiert");
public async Task SetDateAndTime(DateTime dateTime) {
await SendCommand($"C{dateTime:yyyyMMddHHmmss}\r");
await ReceiveResponse();
}
}
}
+2 -2
View File
@@ -10,13 +10,13 @@ namespace Elwig.Helpers.Weighing {
/// Get the current weight on the scale without performing a weighing process
/// </summary>
/// <returns>Result of the weighing process (probably without a weighing id)</returns>
Task<WeighingResult> GetCurrentWeight();
Task<WeighingResult> GetCurrentWeight(int? scaleNr = null);
/// <summary>
/// Perform a weighing process
/// </summary>
/// <returns>Result of the weighing process (including a weighing id)</returns>
Task<WeighingResult> Weigh();
Task<WeighingResult> Weigh(int? scaleNr = null);
/// <summary>
/// Empty the scale container or grant clearance to do so
+1 -6
View File
@@ -18,12 +18,7 @@ namespace Elwig.Helpers.Weighing {
/// <summary>
/// Unique identificator of the scale
/// </summary>
string ScaleId { get; }
/// <summary>
/// Internal identifying number of the scale in its system
/// </summary>
int InternalScaleNr { get; }
string ScaleTerminalId { get; }
/// <summary>
/// Indicates if the scale is currently processing a request or not
+1 -2
View File
@@ -5,8 +5,7 @@ namespace Elwig.Helpers.Weighing {
public string Manufacturer => "NONE";
public string Model => "NONE";
public string ScaleId => id;
public int InternalScaleNr => 0;
public string ScaleTerminalId => id;
public bool IsReady => false;
public bool HasFillingClearance => false;
public int? WeightLimit => null;
+6 -7
View File
@@ -1,9 +1,9 @@
using Elwig.Services;
using System;
using System.IO;
using System.IO.Ports;
using System.Net.Sockets;
using System.Text;
using System.Windows;
namespace Elwig.Helpers.Weighing {
public abstract class Scale : IDisposable {
@@ -28,10 +28,10 @@ namespace Elwig.Helpers.Weighing {
int? limit = config.Limit != null ? int.Parse(config.Limit) : null;
if (config.Type == "SysTec-IT") {
return new SysTecITScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
} else if (config.Type == "Avery-Async") {
} else if (config.Type == "Gassner-Enq" || config.Type == "Gassner") {
return new GassnerEnqScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
} else if (config.Type == "Avery-Event" || config.Type == "Avery-Async") {
return new AveryEventScale(config.Id, config.Model!, config.Connection!, config.Log, config.Required);
} else if (config.Type == "Gassner") {
return new GassnerScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
} else {
throw new ArgumentException($"Invalid scale type: \"{config.Type}\"");
}
@@ -42,11 +42,10 @@ namespace Elwig.Helpers.Weighing {
if (cnx.StartsWith("serial:")) {
try {
Serial = Utils.OpenSerialConnection(cnx);
} catch (Exception e) {
} catch (Exception exc) {
if (!softFail) throw;
if (!failSilent)
MessageBox.Show($"Verbindung zu Waage konnte nicht hergestellt werden:\n\n{e.Message}", "Waagenfehler",
MessageBoxButton.OK, MessageBoxImage.Warning);
InteractionService.ShowException("Waagenfehler", "Verbindung zu Waage konnte nicht hergestellt werden", exc, isError: false);
}
Stream = Serial?.BaseStream ?? Stream.Null;
} else if (cnx.StartsWith("tcp:")) {
+10 -10
View File
@@ -8,15 +8,14 @@ namespace Elwig.Helpers.Weighing {
public class SysTecITScale : Scale, ICommandScale {
public string Manufacturer => "SysTec";
public int InternalScaleNr => 1;
public string Model { get; private set; }
public string ScaleId { get; private set; }
public string ScaleTerminalId { get; private set; }
public bool IsReady { get; private set; }
public bool HasFillingClearance { get; private set; }
public SysTecITScale(string id, string model, string cnx, string? empty = null, string? filling = null, int? limit = null, string? log = null) :
base(cnx, empty, filling, limit, log) {
ScaleId = id;
ScaleTerminalId = id;
Model = model;
IsReady = true;
HasFillingClearance = false;
@@ -74,15 +73,15 @@ namespace Elwig.Helpers.Weighing {
return line[1..^3];
}
protected async Task<WeighingResult> Weigh(bool incIdentNr, bool retry = true) {
protected async Task<WeighingResult> Weigh(int internalScalNr, bool incIdentNr, bool retry = true) {
string record;
try {
await SendCommand(incIdentNr ? $"RN{InternalScaleNr}" : $"RM{InternalScaleNr}");
await SendCommand(incIdentNr ? $"RN{internalScalNr}" : $"RM{internalScalNr}");
record = await ReceiveResponse();
} catch (IOException) {
if (!retry || Tcp == null) throw;
ReconnectTcp();
return await Weigh(incIdentNr, false);
return await Weigh(internalScalNr, incIdentNr, false);
}
if (record.Length != 62)
throw new FormatException("Invalid response from scale: Received record has invalid size");
@@ -114,6 +113,7 @@ namespace Elwig.Helpers.Weighing {
GrossWeight = int.Parse(brutto),
TareWeight = int.Parse(tara),
NetWeight = int.Parse(netto),
ScaleId = scaleNr,
WeighingId = identNr,
FullWeighingId = identNr != null ? $"{parsedDate:yyyy-MM-dd}/{identNr}" : null,
Date = parsedDate,
@@ -121,12 +121,12 @@ namespace Elwig.Helpers.Weighing {
};
}
public async Task<WeighingResult> Weigh() {
return await Weigh(true);
public async Task<WeighingResult> Weigh(int? scaleNr = 1) {
return await Weigh(scaleNr ?? 1, true);
}
public async Task<WeighingResult> GetCurrentWeight() {
return await Weigh(false);
public async Task<WeighingResult> GetCurrentWeight(int? scaleNr = 1) {
return await Weigh(scaleNr ?? 1, false);
}
public async Task Empty() {
+7 -1
View File
@@ -21,6 +21,11 @@ namespace Elwig.Helpers.Weighing {
/// </summary>
public int? NetWeight;
/// <summary>
/// Reported id of the scale
/// </summary>
public string? ScaleId;
/// <summary>
/// Weighing id (or IdentNr) provided by the scale
/// </summary>
@@ -44,11 +49,12 @@ namespace Elwig.Helpers.Weighing {
/// <returns>&lt;[GrossWeight-TaraWeight=]NetWeight/WeighingId/Date/Time&gt;</returns>
public override readonly string ToString() {
var w = NetWeight != null ? (GrossWeight != null && TareWeight != null ? $"{GrossWeight}-{TareWeight}=" : "") + $"{NetWeight}kg" : "";
return $"<{w}/{WeighingId}/{Date:yyyy-MM-dd}/{Time:HH:mm}>";
return $"<{w}/{WeighingId}/{Date:yyyy-MM-dd}/{Time:HH:mm:ss}>";
}
public readonly JsonObject ToJson() {
var obj = new JsonObject();
if (ScaleId != null) obj["scale"] = ScaleId;
if (FullWeighingId != null) obj["id"] = FullWeighingId;
if (WeighingId != null) obj["nr"] = int.Parse(WeighingId);
if (Date != null) obj["date"] = $"{Date:yyyy-MM-dd}";
+5
View File
@@ -0,0 +1,5 @@
namespace Elwig.Models {
public enum BusinessShareType {
Normal = 1, Red = 2, White = 3, Dormant = 9,
}
}
+21 -16
View File
@@ -17,6 +17,7 @@ namespace Elwig.Models.Dtos {
("Address", "Adresse", null, 60),
("Plz", "PLZ", null, 10),
("Locality", "Ort", null, 60),
("AccountingNr", "BH-Konto", null, 20),
("Iban", "IBAN", null, 45),
("TgNr", "TG-Nr.", null, 20),
("Sum", "Zwischens.", "€", 20),
@@ -29,7 +30,7 @@ namespace Elwig.Models.Dtos {
("Gross", "Brutto", "€", 20),
("Penalties", "Pönalen FB", "€", 20),
("Penalty", "Unterl. GA", "€", 20),
("AutoBs", "GA Nachz.", "€", 20),
("AutoShares", "GA Nachz.", "€", 20),
("Custom", "Weitere", "€", 20),
("Others", "Sonstige", "€", 20),
("Considered", "Berückstgt.", "€", 20),
@@ -53,12 +54,12 @@ namespace Elwig.Models.Dtos {
SELECT m.mgnr, m.name AS name_1,
COALESCE(m.prefix || ' ', '') || m.given_name ||
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
p.plz, o.name AS ort, m.address, m.iban, c.tgnr, s.year, s.precision,
p.plz, o.name AS ort, m.address, m.accounting_nr, m.iban, c.tgnr, s.year, s.precision,
p.amount - p.net_amount AS surcharge,
c.net_amount, c.prev_net_amount, c.vat, c.vat_amount, c.gross_amount, c.modifiers, c.prev_modifiers, c.amount,
ROUND(b.total_penalty / POW(10, s.precision - 2)) AS bs_penalty,
ROUND(u.total_penalty / POW(10, 4 - 2)) AS fb_penalty,
ROUND(-a.total_amount / POW(10, s.precision - 2)) AS auto_bs,
ROUND(b.total_penalty / POW(10, s.precision - 2)) AS shares_penalty,
ROUND(u.total_penalty / POW(10, 4 - 2)) AS ac_penalty,
-a.total_amount AS auto_shares,
x.amount AS custom_mod
FROM credit c
LEFT JOIN member m ON m.mgnr = c.mgnr
@@ -83,6 +84,7 @@ namespace Elwig.Models.Dtos {
public string Address;
public int Plz;
public string Locality;
public string? AccountingNr;
public string? Iban;
public string TgNr;
public decimal Sum;
@@ -94,7 +96,7 @@ namespace Elwig.Models.Dtos {
public decimal Gross;
public decimal? Penalties;
public decimal? Penalty;
public decimal? AutoBs;
public decimal? AutoShares;
public decimal? Custom;
public decimal? Others;
public decimal? Considered;
@@ -108,6 +110,7 @@ namespace Elwig.Models.Dtos {
Address = row.Address;
Plz = row.Plz;
Locality = row.Locality;
AccountingNr = row.AccountingNr;
Iban = row.Iban != null ? Utils.FormatIban(row.Iban) : null;
TgNr = $"{row.Year}/{row.TgNr}";
Total = Utils.DecFromDb(row.NetAmount, prec1);
@@ -122,14 +125,14 @@ namespace Elwig.Models.Dtos {
}
decimal mod = (row.Modifiers == null) ? 0 : Utils.DecFromDb((long)row.Modifiers, prec1);
if (data.ConsiderContractPenalties)
Penalties = (row.FbPenalty == null) ? null : Utils.DecFromDb((long)row.FbPenalty, prec1);
Penalties = (row.AcPenalty == null) ? null : Utils.DecFromDb((long)row.AcPenalty, prec1);
if (data.ConsiderTotalPenalty)
Penalty = (row.BsPenalty == null) ? null : Utils.DecFromDb((long)row.BsPenalty, prec1);
Penalty = (row.SharesPenalty == null) ? null : Utils.DecFromDb((long)row.SharesPenalty, prec1);
if (data.ConsiderAutoBusinessShares)
AutoBs = (row.AutoBs == null) ? null : Utils.DecFromDb((long)row.AutoBs, prec1);
AutoShares = (row.AutoShares == null) ? null : Utils.DecFromDb((long)row.AutoShares, prec1);
if (data.ConsiderCustomModifiers)
Custom = (row.CustomMod == null) ? null : Utils.DecFromDb((long)row.CustomMod, prec1);
mod -= (Penalties ?? 0) + (Penalty ?? 0) + (AutoBs ?? 0) + (Custom ?? 0);
mod -= (Penalties ?? 0) + (Penalty ?? 0) + (AutoShares ?? 0) + (Custom ?? 0);
Others = (mod == 0) ? null : mod;
Gross = Utils.DecFromDb(row.GrossAmount, prec1);
Considered = (row.PrevModifiers == null || row.PrevModifiers == 0) ? null : -Utils.DecFromDb((long)row.PrevModifiers, prec1);
@@ -153,6 +156,8 @@ namespace Elwig.Models.Dtos {
public required string LocalityFull { get; set; }
[NotMapped]
public string Locality => LocalityFull.Split(",")[0];
[Column("accounting_nr")]
public string? AccountingNr { get; set; }
[Column("iban")]
public string? Iban { get; set; }
[Column("year")]
@@ -179,12 +184,12 @@ namespace Elwig.Models.Dtos {
public long? PrevModifiers { get; set; }
[Column("amount")]
public long Amount { get; set; }
[Column("bs_penalty")]
public long? BsPenalty { get; set; }
[Column("fb_penalty")]
public long? FbPenalty { get; set; }
[Column("auto_bs")]
public long? AutoBs { get; set; }
[Column("shares_penalty")]
public long? SharesPenalty { get; set; }
[Column("ac_penalty")]
public long? AcPenalty { get; set; }
[Column("auto_shares")]
public long? AutoShares { get; set; }
[Column("custom_mod")]
public long? CustomMod { get; set; }
}
+11 -11
View File
@@ -42,13 +42,10 @@ namespace Elwig.Models.Dtos {
}
private static async Task<IEnumerable<CreditNoteDeliveryRowSingle>> FromDbSet(DbSet<CreditNoteDeliveryRowSingle> table, int? year = null, int? avnr = null, int? mgnr = null) {
var y = year?.ToString() ?? "NULL";
var v = avnr?.ToString() ?? "NULL";
var m = mgnr?.ToString() ?? "NULL";
return await table.FromSqlRaw($"""
return await table.FromSql($"""
SELECT d.year, c.tgnr, v.avnr, d.mgnr, d.did, d.lsnr, d.dpnr, d.weight, d.modifiers,
b.bktnr, d.sortid, b.discr, b.value, pb.price, pb.amount, p.net_amount, p.amount AS total_amount,
s.name AS variety, a.name AS attribute, c.name AS cultivation, q.qualid AS qualid, q.name AS quality_level, d.oe, d.kmw, d.net_weight
s.name AS variety, s.type AS type, a.name AS attribute, c.name AS cultivation, q.qualid AS qualid, q.name AS quality_level, d.oe, d.kmw, d.net_weight
FROM v_delivery d
JOIN wine_variety s ON s.sortid = d.sortid
LEFT JOIN wine_attribute a ON a.attrid = d.attrid
@@ -59,7 +56,7 @@ namespace Elwig.Models.Dtos {
LEFT JOIN payment_delivery_part p ON (p.year, p.did, p.dpnr, p.avnr) = (d.year, d.did, d.dpnr, v.avnr)
LEFT JOIN payment_delivery_part_bucket pb ON (pb.year, pb.did, pb.dpnr, pb.bktnr, pb.avnr) = (b.year, b.did, b.dpnr, b.bktnr, v.avnr)
LEFT JOIN credit c ON (c.year, c.avnr, c.mgnr) = (d.year, v.avnr, d.mgnr)
WHERE b.value > 0 AND (d.year = {y} OR {y} IS NULL) AND (v.avnr = {v} OR {v} IS NULL) AND (d.mgnr = {m} OR {m} IS NULL)
WHERE b.value > 0 AND (d.year = {year} OR {year} IS NULL) AND (v.avnr = {avnr} OR {avnr} IS NULL) AND (d.mgnr = {mgnr} OR {mgnr} IS NULL)
ORDER BY d.year, v.avnr, d.mgnr, d.lsnr, d.dpnr
""").ToListAsync();
}
@@ -75,6 +72,7 @@ namespace Elwig.Models.Dtos {
public string LsNr;
public int DPNr;
public string Variety;
public string Type;
public string? Attribute;
public string? Cultivation;
public string[] Modifiers;
@@ -97,24 +95,24 @@ namespace Elwig.Models.Dtos {
LsNr = f.LsNr;
DPNr = f.DPNr;
Variety = f.Variety;
Type = f.Type;
Attribute = f.Attribute;
Cultivation = f.Cultivation;
var modifiers = (IEnumerable<Modifier>)(f.Modifiers ?? "").Split(',')
.Select(m => season?.Modifiers.FirstOrDefault(s => s.ModId == m))
.Where(m => m != null)
.OrderBy(m => m.Ordering)
.OrderBy(m => m!.Ordering)
.ToList();
Modifiers = modifiers.Select(m => m.Name).ToArray();
Modifiers = [.. modifiers.Select(m => m.Name)];
QualId = f.QualId;
QualityLevel = f.QualityLevel;
Gradation = (f.Oe, f.Kmw);
Buckets = rows
Buckets = [.. rows
.Where(b => b.Value > 0)
.OrderByDescending(b => b.BktNr)
.Select(b => (b.Discr == "_" ? "ungeb." : $"geb. {f.SortId}{b.Discr}", b.Value,
b.Price != null ? season?.DecFromDb((long)b.Price) : null,
b.Amount != null ? season?.DecFromDb((long)b.Amount) : null))
.ToArray();
b.Amount != null ? season?.DecFromDb((long)b.Amount) : null))];
WeighingModifier = (varData == null || !varData.ConsiderDelieryModifiers) ? 0 : f.NetWeight ? varData.NetWeightModifier : varData.GrossWeightModifier;
Amount = f.TotalAmount != null ? season?.DecFromDb((long)f.TotalAmount) : null;
var netAmount = f.NetAmount != null ? season?.DecFromDb((long)f.NetAmount) : null;
@@ -161,6 +159,8 @@ namespace Elwig.Models.Dtos {
public long? TotalAmount { get; set; }
[Column("variety")]
public required string Variety { get; set; }
[Column("type")]
public required string Type { get; set; }
[Column("attribute")]
public string? Attribute { get; set; }
[Column("cultivation")]
+1 -1
View File
@@ -8,7 +8,7 @@ namespace Elwig.Models.Dtos {
public class MemberDeliveryData : DataTable<MemberDeliveryRow> {
private static readonly (string, string, string?, int?)[] FieldNames = [
("MgNr", "MgNr.", null, 12),
("MgNr", "MgNr.", null, 12),
("Name1", "Name", null, 40),
("Name2", "Vorname", null, 40),
("Address", "Adresse", null, 60),
+9 -3
View File
@@ -19,7 +19,7 @@ namespace Elwig.Models.Dtos {
("Locality", "Ort", null, 60),
("DefaultKg", "Stammgemeinde", null, 60),
("Branch", "Zweigstelle", null, 40),
("BusinessShares", "GA", null, 10),
("SharesActive", "GA", null, 10),
("BillingName", "Rechnungsname", null, 60),
("BillingAddress", "Rechnungsadresse", null, 60),
("BillingPlz", "PLZ", null, 10),
@@ -66,7 +66,10 @@ namespace Elwig.Models.Dtos {
public string? Name2;
public string? DefaultKg;
public string? Branch;
public int BusinessShares;
public int Shares;
public int SharesRed;
public int SharesWhite;
public int SharesActive;
public string Address;
public int Plz;
public string Locality;
@@ -98,7 +101,10 @@ namespace Elwig.Models.Dtos {
Name2 = m.AdministrativeName2;
DefaultKg = m.DefaultKg?.Name;
Branch = m.Branch?.Name;
BusinessShares = m.BusinessShares;
Shares = m.Shares;
SharesRed = m.SharesRed;
SharesWhite = m.SharesWhite;
SharesActive = Shares + SharesRed + SharesWhite;
Address = m.Address;
Plz = m.PostalDest.AtPlz!.Plz;
Locality = m.PostalDest.AtPlz!.Ort.Name;
+81 -18
View File
@@ -13,35 +13,74 @@ namespace Elwig.Models.Dtos {
("Address", "Adresse", null, 60),
("Plz", "PLZ", null, 10),
("Locality", "Ort", null, 60),
("BusinessShares", "GA", null, 10),
("Shares", "GA", null, 10),
("DeliveryObligation", "Lieferpflicht", "kg", 22),
("DeliveryRight", "Lieferrecht", "kg", 22),
("Weight", "Geliefert", "kg", 22),
("WeightTotal", "Geliefert", "kg", 22),
("OverUnderDelivery", "Über-/Unterliefert", "kg|%", 34),
];
private static readonly (string, string, string?, int)[] FieldNamesRed = [
("MgNr", "MgNr.", null, 12),
("Name1", "Name", null, 40),
("Name2", "Vorname", null, 40),
("Address", "Adresse", null, 60),
("Plz", "PLZ", null, 10),
("Locality", "Ort", null, 60),
("SharesRed", "GA", null, 10),
("DeliveryObligationRed", "Lieferpflicht", "kg", 22),
("DeliveryRightRed", "Lieferrecht", "kg", 22),
("WeightRed", "Geliefert", "kg", 22),
("OverUnderDeliveryRed", "Über-/Unterliefert", "kg|%", 34),
];
private static readonly (string, string, string?, int)[] FieldNamesWhite = [
("MgNr", "MgNr.", null, 12),
("Name1", "Name", null, 40),
("Name2", "Vorname", null, 40),
("Address", "Adresse", null, 60),
("Plz", "PLZ", null, 10),
("Locality", "Ort", null, 60),
("SharesWhite", "GA", null, 10),
("DeliveryObligationWhite", "Lieferpflicht", "kg", 22),
("DeliveryRightWhite", "Lieferrecht", "kg", 22),
("WeightWhite", "Geliefert", "kg", 22),
("OverUnderDeliveryWhite", "Über-/Unterliefert", "kg|%", 34),
];
public OverUnderDeliveryData(IEnumerable<OverUnderDeliveryRow> rows, int year) :
base($"Über-Unterlieferungen", $"Über- und Unterlieferungen laut gezeichneten Geschäftsanteilen {year}", rows, FieldNames) {
}
public static async Task<OverUnderDeliveryData> ForSeason(DbSet<OverUnderDeliveryRow> table, int year) {
public OverUnderDeliveryData(IEnumerable<OverUnderDeliveryRow> rows, int year, string mode) :
base($"Über-Unterlieferungen-{(mode == "R" ? "rot" : "weiß")}", $"Über- und Unterlieferungen laut gezeichneten Geschäftsanteilen {(mode == "R" ? "rot" : "weiß")} {year}", rows,
mode == "R" ? FieldNamesRed : FieldNamesWhite) {
}
public static async Task<(OverUnderDeliveryData Total, OverUnderDeliveryData Red, OverUnderDeliveryData White)> ForSeason(DbSet<OverUnderDeliveryRow> table, int year) {
var rows = await table.FromSql($"""
SELECT m.mgnr, m.name AS name_1,
COALESCE(m.prefix || ' ', '') || m.given_name ||
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
p.plz, o.name AS ort, m.address, m.business_shares,
m.business_shares * s.min_kg_per_bs AS min_kg,
m.business_shares * s.max_kg_per_bs AS max_kg,
COALESCE(SUM(d.weight), 0) AS sum
p.plz, o.name AS ort, m.address, h.shares, h.shares_red, h.shares_white,
h.shares * COALESCE(s.min_kg_per_share, 0) AS min_kg,
h.shares * COALESCE(s.max_kg_per_share, 0) AS max_kg,
h.shares_red * COALESCE(s.min_kg_per_share_red, s.min_kg_per_share, 0) AS min_kg_red,
h.shares_red * COALESCE(s.max_kg_per_share_red, s.max_kg_per_share, 0) AS max_kg_red,
h.shares_white * COALESCE(s.min_kg_per_share_white, s.min_kg_per_share, 0) AS min_kg_white,
h.shares_white * COALESCE(s.max_kg_per_share_white, s.max_kg_per_share, 0) AS max_kg_white,
COALESCE(d.weight_total, 0) AS weight_total,
COALESCE(d.weight_red, 0) AS weight_red,
COALESCE(d.weight_white, 0) AS weight_white
FROM season s, member m
LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
LEFT JOIN AT_ort o ON o.okz = p.okz
LEFT JOIN v_delivery d ON (d.year, d.mgnr) = (s.year, m.mgnr)
WHERE s.year = {year} AND (m.active = TRUE OR d.weight > 0)
GROUP BY s.year, m.mgnr
ORDER BY 100.0 * sum / max_kg, m.mgnr
LEFT JOIN v_member_history h ON (h.year, h.mgnr) = (s.year, m.mgnr)
LEFT JOIN v_stat_member d ON (d.year, d.mgnr) = (s.year, m.mgnr)
WHERE s.year = {year} AND (m.active = TRUE OR d.weight_total > 0)
ORDER BY 100.0 * weight_total / (max_kg + max_kg_red + max_kg_white), m.mgnr
""").ToListAsync();
return new OverUnderDeliveryData(rows, year);
return (new OverUnderDeliveryData(rows, year), new OverUnderDeliveryData(rows, year, "R"), new OverUnderDeliveryData(rows, year, "W"));
}
}
@@ -61,17 +100,41 @@ namespace Elwig.Models.Dtos {
public required string LocalityFull { get; set; }
[NotMapped]
public string Locality => LocalityFull.Split(",")[0];
[Column("business_shares")]
public int BusinessShares { get; set; }
[Column("shares")]
public int Shares { get; set; }
[Column("shares_red")]
public int SharesRed { get; set; }
[Column("shares_white")]
public int SharesWhite { get; set; }
[Column("min_kg")]
public int DeliveryObligation { get; set; }
[Column("max_kg")]
public int DeliveryRight { get; set; }
[Column("sum")]
public int Weight { get; set; }
[NotMapped]
public (int? Kg, double? Percent) OverUnderDelivery =>
Weight < DeliveryObligation ? (Weight - DeliveryObligation, Weight * 100.0 / DeliveryObligation - 100.0) :
Weight > DeliveryRight ? (Weight - DeliveryRight, Weight * 100.0 / DeliveryRight - 100.0) : (null, null);
WeightTotal < DeliveryObligation ? (WeightTotal - DeliveryObligation, WeightTotal * 100.0 / DeliveryObligation - 100.0) :
WeightTotal > DeliveryRight ? (WeightTotal - DeliveryRight, WeightTotal * 100.0 / DeliveryRight - 100.0) : (null, null);
[Column("min_kg_red")]
public int DeliveryObligationRed { get; set; }
[Column("max_kg_red")]
public int DeliveryRightRed { get; set; }
[NotMapped]
public (int? Kg, double? Percent) OverUnderDeliveryRed =>
WeightRed < DeliveryObligationRed ? (WeightRed - DeliveryObligationRed, WeightRed * 100.0 / DeliveryObligationRed - 100.0) :
WeightRed > DeliveryRightRed ? (WeightRed - DeliveryRightRed, WeightRed * 100.0 / DeliveryRightRed - 100.0) : (null, null);
[Column("min_kg_white")]
public int DeliveryObligationWhite { get; set; }
[Column("max_kg_white")]
public int DeliveryRightWhite { get; set; }
[NotMapped]
public (int? Kg, double? Percent) OverUnderDeliveryWhite =>
WeightWhite < DeliveryObligationWhite ? (WeightWhite - DeliveryObligationWhite, WeightWhite * 100.0 / DeliveryObligationWhite - 100.0) :
WeightWhite > DeliveryRightWhite ? (WeightWhite - DeliveryRightWhite, WeightWhite * 100.0 / DeliveryRightWhite - 100.0) : (null, null);
[Column("weight_total")]
public int WeightTotal { get; set; }
[Column("weight_red")]
public int WeightRed { get; set; }
[Column("weight_white")]
public int WeightWhite { get; set; }
}
}
+5 -4
View File
@@ -118,17 +118,18 @@ namespace Elwig.Models.Entities {
[Column("acid")]
public double? Acid { get; set; }
[Column("scale_id")]
public string? ScaleId { get; set; }
[Column("scale_terminal")]
public string? ScaleTerminalId { get; set; }
[Column("weighing_data")]
public string? WeighingData { get; set; }
[NotMapped]
public (string? Id, int? Gross, int? Tare, int? Net, DateOnly? Date, TimeOnly? Time) WeighingInfo {
public (string? ScaleId, string? Id, int? Gross, int? Tare, int? Net, DateOnly? Date, TimeOnly? Time) WeighingInfo {
get {
try {
var obj = JsonNode.Parse(WeighingData!)!.AsObject();
return (
obj["scale"]?.AsValue().GetValue<string>(),
obj["id"]?.AsValue().GetValue<string>(),
obj["gross_weight"]?.AsValue().GetValue<int>(),
obj["tare_weight"]?.AsValue().GetValue<int>(),
@@ -137,7 +138,7 @@ namespace Elwig.Models.Entities {
TimeOnly.TryParseExact(obj["time"]?.AsValue().GetValue<string>(), ["HH:mm:ss", "HH:mm"], out var t) ? t : null
);
} catch {
return (null, null, null, null, null, null);
return (null, null, null, null, null, null, null);
}
}
}
+22 -3
View File
@@ -42,6 +42,8 @@ namespace Elwig.Models.Entities {
[NotMapped]
public string ShortName => (!string.IsNullOrWhiteSpace(GivenName) ? $"{GivenName} " : "") + Name;
[NotMapped]
public string FullAdministrativeName => $"{AdministrativeName} ({MgNr})";
[NotMapped]
public string AdministrativeName => AdministrativeName1 + (!string.IsNullOrWhiteSpace(AdministrativeName2) ? $" {AdministrativeName2}" : "");
[NotMapped]
public string AdministrativeName1 => IsJuridicalPerson ? Name : Name.Replace('ß', 'ẞ').ToUpper();
@@ -67,8 +69,20 @@ namespace Elwig.Models.Entities {
set => ExitDateString = value?.ToString("yyyy-MM-dd");
}
[Column("business_shares")]
public int BusinessShares { get; set; }
[Column("shares")]
public int Shares { get; set; }
[Column("shares_red")]
public int SharesRed { get; set; }
[Column("shares_white")]
public int SharesWhite { get; set; }
[Column("shares_dormant")]
public int SharesDormant { get; set; }
[NotMapped]
public int SharesActive => Shares + SharesRed + SharesWhite;
[Column("accounting_nr")]
public string? AccountingNr { get; set; }
@@ -189,9 +203,14 @@ namespace Elwig.Models.Entities {
[InverseProperty(nameof(BillingAddr.Member))]
public virtual BillingAddr? BillingAddress { get; private set; }
[InverseProperty(nameof(Delivery.Member))]
[InverseProperty(nameof(DeliveryAncmt.Member))]
public virtual ICollection<DeliveryAncmt> Announcements { get; private set; } = null!;
[InverseProperty(nameof(MemberHistory.FromMember))]
public virtual ICollection<MemberHistory> HistoryFrom { get; private set; } = null!;
[InverseProperty(nameof(MemberHistory.ToMember))]
public virtual ICollection<MemberHistory> HistoryTo { get; private set; } = null!;
[InverseProperty(nameof(Delivery.Member))]
public virtual ICollection<Delivery> Deliveries { get; private set; } = null!;
+109 -13
View File
@@ -1,31 +1,127 @@
using Elwig.Helpers;
using Microsoft.EntityFrameworkCore;
using System;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models.Entities {
[Table("member_history"), PrimaryKey("MgNr", "DateString", "Type")]
[Table("member_history"), PrimaryKey("HistNr")]
public class MemberHistory {
[Column("mgnr")]
public int MgNr { get; set; }
[Column("date")]
public required string DateString { get; set; }
public const string REASON_PURCHASE = "purchase"; // Kauf
public const string REASON_CONVERT = "convert"; // Änderung
public const string REASON_TRANSFER = "transfer"; // Übertragung
public const string REASON_PRESCRIPTION = "prescription"; // Vorschreibung
public const string REASON_EXCLUSION = "exclusion"; // Ausschluss
public const string REASON_DECEASED = "deceased"; // Tod
public const string REASON_RETIREMENT = "retirement"; // Kündigung/stilllegen
public const string REASON_PAYOUT = "payout"; // Auszahlung
[Column("histnr")]
public int HistNr { get; set; }
[Column("from_mgnr")]
public int? FromMgNr { get; set; }
[Column("from_type")]
public int? FromTypeValue { get; set; }
[NotMapped]
public DateOnly Date {
get => DateOnly.ParseExact(DateString, "yyyy-MM-dd");
public BusinessShareType? FromType {
get => (BusinessShareType?)FromTypeValue;
set => FromTypeValue = (int?)value;
}
[Column("to_mgnr")]
public int? ToMgNr { get; set; }
[Column("to_type")]
public int? ToTypeValue { get; set; }
[NotMapped]
public BusinessShareType? ToType {
get => (BusinessShareType?)ToTypeValue;
set => ToTypeValue = (int?)value;
}
[Column("date_notice")]
public required string DateNoticeString { get; set; }
[NotMapped]
public DateOnly DateNotice {
get => DateOnly.ParseExact(DateNoticeString, "yyyy-MM-dd");
set => value.ToString("yyyy-MM-dd");
}
[Column("type")]
public required string Type { get; set; }
[Column("date_effective")]
public required string DateEffectiveString { get; set; }
[NotMapped]
public DateOnly DateEffective {
get => DateOnly.ParseExact(DateEffectiveString, "yyyy-MM-dd");
set => value.ToString("yyyy-MM-dd");
}
[Column("business_shares")]
public int BusinessShares { get; set; }
[Column("reason")]
public required string Reason { get; set; }
[NotMapped]
public string ReasonPretty => Reason switch {
REASON_PURCHASE => "Kauf",
REASON_CONVERT => "Änderung",
REASON_TRANSFER => "Übertragung",
REASON_PRESCRIPTION => "Vorschreibung",
REASON_EXCLUSION => "Ausschluss",
REASON_DECEASED => "Tod",
REASON_RETIREMENT => "Kündigung",
REASON_PAYOUT => "Auszahlung",
_ => Reason,
};
[Column("source")]
public required string Source { get; set; }
[Column("shares")]
public int Shares { get; set; }
[Column("signed")]
public bool MemberHasSigned { get; set; }
[Column("value_per_share")]
public long? ValuePerShareValue { get; set; }
[NotMapped]
public decimal? ValuePerShare {
get => ValuePerShareValue != null ? Utils.DecFromDb(ValuePerShareValue.Value, 2) : null;
set => ValuePerShareValue = value != null ? Utils.DecToDb(value.Value, 2) : null;
}
[NotMapped]
public decimal? TotalValue => Shares * ValuePerShare;
[Column("currency")]
public string? CurrencyCode { get; set; }
[Column("deduct_year")]
public int? DeductYear { get; set; }
[Column("comment")]
public string? Comment { get; set; }
[ForeignKey("MgNr")]
public virtual Member Member { get; private set; } = null!;
public string FormatReasonPhrase(int mgnr) {
if (Reason == REASON_TRANSFER) {
if (FromMgNr == mgnr) {
return $"{ReasonPretty} an {ToMember?.FullAdministrativeName ?? "Nachfolger"}" + (Comment != null ? $", {Comment}" : "");
} else if (ToMgNr == mgnr) {
return $"{ReasonPretty} von {FromMember?.FullAdministrativeName ?? "Vorgänger"}" + (Comment != null ? $", {Comment}" : "");
}
}
return ReasonPretty + (Comment != null ? $", {Comment}" : "");
}
[NotMapped]
public int? PoVMgNr { get; set; }
[NotMapped]
public string PoVReasonPhrase => FormatReasonPhrase(PoVMgNr ?? 0);
[ForeignKey("FromMgNr")]
public virtual Member? FromMember { get; private set; } = null!;
[ForeignKey("ToMgNr")]
public virtual Member? ToMember { get; private set; } = null!;
[ForeignKey("CurrencyCode")]
public virtual Currency Currency { get; private set; } = null!;
}
}
+24 -15
View File
@@ -25,11 +25,20 @@ namespace Elwig.Models.Entities {
[Column("vat_flatrate")]
public double VatFlatrate { get; set; }
[Column("min_kg_per_bs")]
public int MinKgPerBusinessShare { get; set; }
[Column("min_kg_per_share")]
public int? MinKgPerShare { get; set; }
[Column("max_kg_per_share")]
public int? MaxKgPerShare { get; set; }
[Column("max_kg_per_bs")]
public int MaxKgPerBusinessShare { get; set; }
[Column("min_kg_per_share_red")]
public int? MinKgPerShareRed { get; set; }
[Column("max_kg_per_share_red")]
public int? MaxKgPerShareRed { get; set; }
[Column("min_kg_per_share_white")]
public int? MinKgPerShareWhite { get; set; }
[Column("max_kg_per_share_white")]
public int? MaxKgPerShareWhite { get; set; }
[Column("penalty_per_kg")]
public long? PenaltyPerKgValue { get; set; }
@@ -55,23 +64,23 @@ namespace Elwig.Models.Entities {
set => PenaltyNoneValue = value != null ? DecToDb(value.Value) : null;
}
[Column("penalty_per_bs_amount")]
public long? PenaltyPerBsAmountValue { get; set; }
[Column("penalty_per_share_amount")]
public long? PenaltyPerShareAmountValue { get; set; }
[NotMapped]
public decimal? PenaltyPerBsAmount {
get => PenaltyPerBsAmountValue != null ? DecFromDb(PenaltyPerBsAmountValue.Value) : null;
set => PenaltyPerBsAmountValue = value != null ? DecToDb(value.Value) : null;
public decimal? PenaltyPerShareAmount {
get => PenaltyPerShareAmountValue != null ? DecFromDb(PenaltyPerShareAmountValue.Value) : null;
set => PenaltyPerShareAmountValue = value != null ? DecToDb(value.Value) : null;
}
[Column("penalty_per_bs_none")]
public long? PenaltyPerBsNoneValue { get; set; }
[Column("penalty_per_share_none")]
public long? PenaltyPerShareNoneValue { get; set; }
[NotMapped]
public decimal? PenaltyPerBsNone {
get => PenaltyPerBsNoneValue != null ? DecFromDb(PenaltyPerBsNoneValue.Value) : null;
set => PenaltyPerBsNoneValue = value != null ? DecToDb(value.Value) : null;
public decimal? PenaltyPerShareNone {
get => PenaltyPerShareNoneValue != null ? DecFromDb(PenaltyPerShareNoneValue.Value) : null;
set => PenaltyPerShareNoneValue = value != null ? DecToDb(value.Value) : null;
}
[Column("bs_value")]
[Column("share_value")]
public long? BusinessShareValueValue { get; set; }
[NotMapped]
public decimal? BusinessShareValue {
+326
View File
@@ -0,0 +1,326 @@
-- schema version 39 to 40
PRAGMA writable_schema = ON;
ALTER TABLE member RENAME COLUMN business_shares TO shares;
ALTER TABLE member ADD COLUMN shares_red INTEGER NOT NULL DEFAULT 0 CHECK (shares_red >= 0);
ALTER TABLE member ADD COLUMN shares_white INTEGER NOT NULL DEFAULT 0 CHECK (shares_white >= 0);
ALTER TABLE member ADD COLUMN shares_dormant INTEGER NOT NULL DEFAULT 0 CHECK (shares_dormant >= 0);
UPDATE sqlite_schema SET sql = REPLACE(sql, 'shares INTEGER NOT NULL DEFAULT 0', 'shares INTEGER NOT NULL DEFAULT 0 CHECK (shares >= 0)')
WHERE type = 'table' AND name = 'member';
UPDATE client_parameter SET value = '0' WHERE param = 'ENABLE_TIME_TRIGGERS';
UPDATE member
SET shares_dormant = shares + shares_red + shares_white,
shares = 0, shares_red = 0, shares_white = 0
WHERE NOT active;
UPDATE client_parameter SET value = '1' WHERE param = 'ENABLE_TIME_TRIGGERS';
CREATE TABLE member_history_new (
histnr INTEGER NOT NULL,
from_mgnr INTEGER,
from_type INTEGER,
to_mgnr INTEGER,
to_type INTEGER,
date_notice TEXT NOT NULL CHECK (date_notice REGEXP '^[1-9][0-9]{3}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$') DEFAULT CURRENT_DATE,
date_effective TEXT NOT NULL CHECK (date_effective REGEXP '^[1-9][0-9]{3}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$') DEFAULT CURRENT_DATE,
reason TEXT NOT NULL CHECK (reason REGEXP '^[a-z_]+$'),
source TEXT NOT NULL CHECK (source REGEXP '^[a-z_]+$'),
shares INTEGER NOT NULL CHECK (shares > 0),
signed INTEGER NOT NULL CHECK (signed IN (TRUE, FALSE)) DEFAULT FALSE,
value_per_share INTEGER DEFAULT NULL,
currency TEXT DEFAULT NULL,
deduct_year INTEGER DEFAULT NULL,
comment TEXT DEFAULT NULL,
CONSTRAINT pk_member_history PRIMARY KEY (histnr),
CONSTRAINT fk_member_history_member_from FOREIGN KEY (from_mgnr) REFERENCES member (mgnr)
ON UPDATE CASCADE
ON DELETE RESTRICT,
CONSTRAINT fk_member_history_member_to FOREIGN KEY (to_mgnr) REFERENCES member (mgnr)
ON UPDATE CASCADE
ON DELETE RESTRICT,
CONSTRAINT fk_member_history_currency FOREIGN KEY (currency) REFERENCES currency (code)
ON UPDATE CASCADE
ON DELETE RESTRICT,
CONSTRAINT c_member_history CHECK ((from_mgnr IS NOT NULL OR to_mgnr IS NOT NULL) AND
((from_mgnr IS NULL AND from_type IS NULL) OR (from_mgnr IS NOT NULL AND from_type IS NOT NULL)) AND
((to_mgnr IS NULL AND to_type IS NULL) OR (to_mgnr IS NOT NULL AND to_type IS NOT NULL)))
) STRICT;
INSERT INTO member_history_new (histnr, from_mgnr, from_type, to_mgnr, to_type, date_notice, date_effective, reason, source, shares, value_per_share, currency, deduct_year, comment)
SELECT ROW_NUMBER() OVER(ORDER BY h.date, h.mgnr), NULL, NULL, h.mgnr, 1, h.date, h.date,
IIF(h.type = 'auto', 'prescription', h.type), 'elwig', h.business_shares, s.bs_value / POW(10, s.precision - 2), s.currency,
IIF(h.type = 'auto', s.year, NULL), 'automatische Nachzeichnung'
FROM member_history h
JOIN season s ON s.year = SUBSTR(h.date, 1, 4);
PRAGMA foreign_keys = OFF;
DROP TABLE member_history;
ALTER TABLE member_history_new RENAME TO member_history;
PRAGMA foreign_keys = ON;
CREATE TRIGGER t_member_history_i_member
AFTER INSERT ON member_history FOR EACH ROW
WHEN (SELECT value FROM client_parameter WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS') = 1
BEGIN
UPDATE member SET shares = shares - NEW.shares WHERE mgnr = NEW.from_mgnr AND NEW.from_type = 1;
UPDATE member SET shares_red = shares_red - NEW.shares WHERE mgnr = NEW.from_mgnr AND NEW.from_type = 2;
UPDATE member SET shares_white = shares_white - NEW.shares WHERE mgnr = NEW.from_mgnr AND NEW.from_type = 3;
UPDATE member SET shares_dormant = shares_dormant - NEW.shares WHERE mgnr = NEW.from_mgnr AND NEW.from_type = 9;
UPDATE member SET shares = shares + NEW.shares WHERE mgnr = NEW.to_mgnr AND NEW.to_type = 1;
UPDATE member SET shares_red = shares_red + NEW.shares WHERE mgnr = NEW.to_mgnr AND NEW.to_type = 2;
UPDATE member SET shares_white = shares_white + NEW.shares WHERE mgnr = NEW.to_mgnr AND NEW.to_type = 3;
UPDATE member SET shares_dormant = shares_dormant + NEW.shares WHERE mgnr = NEW.to_mgnr AND NEW.to_type = 9;
END;
CREATE TRIGGER t_member_history_u_member
AFTER UPDATE OF from_mgnr, from_type, to_mgnr, to_type, shares ON member_history FOR EACH ROW
WHEN (SELECT value FROM client_parameter WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS') = 1
BEGIN
UPDATE member SET shares = shares + OLD.shares WHERE mgnr = OLD.from_mgnr AND OLD.from_type = 1;
UPDATE member SET shares_red = shares_red + OLD.shares WHERE mgnr = OLD.from_mgnr AND OLD.from_type = 2;
UPDATE member SET shares_white = shares_white + OLD.shares WHERE mgnr = OLD.from_mgnr AND OLD.from_type = 3;
UPDATE member SET shares_dormant = shares_dormant + OLD.shares WHERE mgnr = OLD.from_mgnr AND OLD.from_type = 9;
UPDATE member SET shares = shares - OLD.shares WHERE mgnr = OLD.to_mgnr AND OLD.to_type = 1;
UPDATE member SET shares_red = shares_red - OLD.shares WHERE mgnr = OLD.to_mgnr AND OLD.to_type = 2;
UPDATE member SET shares_white = shares_white - OLD.shares WHERE mgnr = OLD.to_mgnr AND OLD.to_type = 3;
UPDATE member SET shares_dormant = shares_dormant - OLD.shares WHERE mgnr = OLD.to_mgnr AND OLD.to_type = 9;
UPDATE member SET shares = shares - NEW.shares WHERE mgnr = NEW.from_mgnr AND NEW.from_type = 1;
UPDATE member SET shares_red = shares_red - NEW.shares WHERE mgnr = NEW.from_mgnr AND NEW.from_type = 2;
UPDATE member SET shares_white = shares_white - NEW.shares WHERE mgnr = NEW.from_mgnr AND NEW.from_type = 3;
UPDATE member SET shares_dormant = shares_dormant - NEW.shares WHERE mgnr = NEW.from_mgnr AND NEW.from_type = 9;
UPDATE member SET shares = shares + NEW.shares WHERE mgnr = NEW.to_mgnr AND NEW.to_type = 1;
UPDATE member SET shares_red = shares_red + NEW.shares WHERE mgnr = NEW.to_mgnr AND NEW.to_type = 2;
UPDATE member SET shares_white = shares_white + NEW.shares WHERE mgnr = NEW.to_mgnr AND NEW.to_type = 3;
UPDATE member SET shares_dormant = shares_dormant + NEW.shares WHERE mgnr = NEW.to_mgnr AND NEW.to_type = 9;
END;
CREATE TRIGGER t_member_history_d_member
AFTER DELETE ON member_history FOR EACH ROW
WHEN (SELECT value FROM client_parameter WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS') = 1
BEGIN
UPDATE member SET shares = shares + OLD.shares WHERE mgnr = OLD.from_mgnr AND OLD.from_type = 1;
UPDATE member SET shares_red = shares_red + OLD.shares WHERE mgnr = OLD.from_mgnr AND OLD.from_type = 2;
UPDATE member SET shares_white = shares_white + OLD.shares WHERE mgnr = OLD.from_mgnr AND OLD.from_type = 3;
UPDATE member SET shares_dormant = shares_dormant + OLD.shares WHERE mgnr = OLD.from_mgnr AND OLD.from_type = 9;
UPDATE member SET shares = shares - OLD.shares WHERE mgnr = OLD.to_mgnr AND OLD.to_type = 1;
UPDATE member SET shares_red = shares_red - OLD.shares WHERE mgnr = OLD.to_mgnr AND OLD.to_type = 2;
UPDATE member SET shares_white = shares_white - OLD.shares WHERE mgnr = OLD.to_mgnr AND OLD.to_type = 3;
UPDATE member SET shares_dormant = shares_dormant - OLD.shares WHERE mgnr = OLD.to_mgnr AND OLD.to_type = 9;
END;
INSERT INTO client_parameter (param, value) VALUES ('ENABLE_MEMBER_HISTORY_TRIGGERS', '1');
CREATE TABLE season_new (
year INTEGER NOT NULL CHECK (year >= 1000 AND year <= 9999),
currency TEXT NOT NULL,
precision INTEGER NOT NULL DEFAULT 4,
max_kg_per_ha INTEGER NOT NULL DEFAULT 10000,
vat_normal REAL NOT NULL DEFAULT 0.10,
vat_flatrate REAL NOT NULL DEFAULT 0.13,
min_kg_per_share INTEGER DEFAULT NULL,
max_kg_per_share INTEGER DEFAULT NULL,
min_kg_per_share_red INTEGER DEFAULT NULL,
max_kg_per_share_red INTEGER DEFAULT NULL,
min_kg_per_share_white INTEGER DEFAULT NULL,
max_kg_per_share_white INTEGER DEFAULT NULL,
penalty_per_kg INTEGER DEFAULT NULL,
penalty_amount INTEGER DEFAULT NULL,
penalty_none INTEGER DEFAULT NULL,
penalty_per_share_amount INTEGER DEFAULT NULL,
penalty_per_share_none INTEGER DEFAULT NULL,
share_value INTEGER,
start_date TEXT CHECK (start_date REGEXP '^[1-9][0-9]{3}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$'),
end_date TEXT CHECK (end_date REGEXP '^[1-9][0-9]{3}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$'),
calc_mode INTEGER NOT NULL DEFAULT 0,
CONSTRAINT pk_season PRIMARY KEY (year),
CONSTRAINT fk_season_currency FOREIGN KEY (currency) REFERENCES currency (code)
ON UPDATE CASCADE
ON DELETE RESTRICT
) STRICT;
INSERT INTO season_new (year, currency, precision, max_kg_per_ha, vat_normal, vat_flatrate, min_kg_per_share, max_kg_per_share, penalty_per_kg, penalty_amount, penalty_none, penalty_per_share_amount, penalty_per_share_none, share_value, start_date, end_date, calc_mode)
SELECT year, currency, precision, max_kg_per_ha, vat_normal, vat_flatrate, min_kg_per_bs, max_kg_per_bs, penalty_per_kg, penalty_amount, penalty_none, penalty_per_bs_amount, penalty_per_bs_none, bs_value, start_date, end_date, calc_mode
FROM season;
PRAGMA foreign_keys = OFF;
DROP TABLE season;
ALTER TABLE season_new RENAME TO season;
PRAGMA foreign_keys = ON;
DROP VIEW v_delivery;
CREATE VIEW v_delivery AS
SELECT p.year, p.did, p.dpnr,
d.date, d.time, d.zwstid, d.lnr, d.lsnr,
m.mgnr, m.name, m.given_name,
v.sortid, v.type, a.attrid, p.cultid,
p.weight, p.kmw, ROUND(p.kmw * (4.54 + 0.022 * p.kmw), 0) AS oe, p.qualid,
p.hkid, p.kgnr, p.rdnr,
p.net_weight, p.gebunden,
p.qualid IN (SELECT l.qualid FROM wine_quality_level l WHERE NOT l.predicate AND (p.kmw >= l.min_kmw OR l.min_kmw IS NULL) ORDER BY l.min_kmw DESC LIMIT 1,100) AS abgewertet,
p.qualid NOT IN ('WEI', 'RSW', 'LDW') AS min_quw,
IIF(a.strict, COALESCE(a.fill_lower, 0), 0) AS attribute_prio,
GROUP_CONCAT(o.modid) AS modifiers,
d.comment, p.comment AS part_comment
FROM delivery_part p
JOIN delivery d ON (d.year, d.did) = (p.year, p.did)
JOIN member m ON m.mgnr = d.mgnr
LEFT JOIN wine_variety v ON v.sortid = p.sortid
LEFT JOIN wine_attribute a ON a.attrid = p.attrid
LEFT JOIN delivery_part_modifier o ON (o.year, o.did, o.dpnr) = (p.year, p.did, p.dpnr)
GROUP BY p.year, p.did, p.dpnr
ORDER BY p.year, p.did, p.dpnr, o.modid;
DROP VIEW v_stat_season;
CREATE VIEW v_stat_season AS
SELECT year,
SUM(weight) AS weight_total,
SUM(IIF(type = 'R', weight, 0)) AS weight_red,
SUM(IIF(type = 'W', weight, 0)) AS weight_white,
ROUND(SUM(kmw * weight) / SUM(weight), 2) AS kmw,
ROUND(SUM(oe * weight) / SUM(weight), 1) AS oe,
COUNT(DISTINCT did) AS lieferungen,
COUNT(DISTINCT mgnr) AS mitglieder
FROM v_delivery
GROUP BY year
ORDER BY year;
DROP VIEW v_stat_sort;
CREATE VIEW v_stat_variety AS
SELECT year, sortid,
SUM(weight) AS weight_total,
SUM(IIF(type = 'R', weight, 0)) AS weight_red,
SUM(IIF(type = 'W', weight, 0)) AS weight_white,
ROUND(SUM(kmw * weight) / SUM(weight), 2) AS kmw,
ROUND(SUM(oe * weight) / SUM(weight), 1) AS oe,
COUNT(DISTINCT did) AS lieferungen,
COUNT(DISTINCT mgnr) AS mitglieder
FROM v_delivery
GROUP BY year, sortid
ORDER BY year, sortid;
DROP VIEW v_stat_attr;
CREATE VIEW v_stat_attr AS
SELECT year, attrid,
SUM(weight) AS weight_total,
SUM(IIF(type = 'R', weight, 0)) AS weight_red,
SUM(IIF(type = 'W', weight, 0)) AS weight_white,
ROUND(SUM(kmw * weight) / SUM(weight), 2) AS kmw,
ROUND(SUM(oe * weight) / SUM(weight), 1) AS oe,
COUNT(DISTINCT did) AS lieferungen,
COUNT(DISTINCT mgnr) AS mitglieder
FROM v_delivery
GROUP BY year, attrid
ORDER BY year, attrid;
DROP VIEW v_stat_sort_attr;
CREATE VIEW v_stat_variety_attr AS
SELECT year, sortid, attrid,
SUM(weight) AS weight_total,
SUM(IIF(type = 'R', weight, 0)) AS weight_red,
SUM(IIF(type = 'W', weight, 0)) AS weight_white,
ROUND(SUM(kmw * weight) / SUM(weight), 2) AS kmw,
ROUND(SUM(oe * weight) / SUM(weight), 1) AS oe,
COUNT(DISTINCT did) AS lieferungen,
COUNT(DISTINCT mgnr) AS mitglieder
FROM v_delivery
GROUP BY year, sortid, attrid
ORDER BY year, sortid, attrid;
DROP VIEW v_stat_member;
CREATE VIEW v_stat_member AS
SELECT year, mgnr,
SUM(weight) AS weight_total,
SUM(IIF(type = 'R', weight, 0)) AS weight_red,
SUM(IIF(type = 'W', weight, 0)) AS weight_white,
ROUND(SUM(kmw * weight) / SUM(weight), 2) AS kmw,
ROUND(SUM(oe * weight) / SUM(weight), 1) AS oe,
COUNT(DISTINCT did) AS lieferungen
FROM v_delivery
GROUP BY year, mgnr
ORDER BY year, mgnr;
CREATE VIEW v_member_history AS
SELECT m.mgnr, s.year,
m.shares + SUM(IIF(h1.from_type = 1, h1.shares, 0)) - SUM(IIF(h2.to_type = 1, h2.shares, 0)) AS shares,
m.shares_red + SUM(IIF(h1.from_type = 2, h1.shares, 0)) - SUM(IIF(h2.to_type = 2, h2.shares, 0)) AS shares_red,
m.shares_white + SUM(IIF(h1.from_type = 3, h1.shares, 0)) - SUM(IIF(h2.to_type = 3, h2.shares, 0)) AS shares_white,
m.shares_dormant + SUM(IIF(h1.from_type = 9, h1.shares, 0)) - SUM(IIF(h2.to_type = 9, h2.shares, 0)) AS shares_dormant
FROM member m, v_virtual_season s
LEFT JOIN member_history h1 ON (SUBSTR(h1.date_notice, 1, 4) + 0) > s.year AND h1.from_mgnr = m.mgnr
LEFT JOIN member_history h2 ON (SUBSTR(h2.date_notice, 1, 4) + 0) > s.year AND h2.to_mgnr = m.mgnr
GROUP BY m.mgnr, s.year
ORDER BY m.mgnr, s.year;
DROP VIEW v_total_under_delivery;
CREATE VIEW v_total_under_delivery AS
SELECT s.year, m.mgnr,
h.shares,
h.shares * COALESCE(s.min_kg_per_share, 0) AS min_kg,
h.shares * COALESCE(s.max_kg_per_share, 0) AS max_kg,
COALESCE(d.weight_total, 0) AS weight_total,
IIF(COALESCE(d.weight_total, 0) < h.shares * COALESCE(s.min_kg_per_share, 0),
COALESCE(d.weight_total, 0) - h.shares * COALESCE(s.min_kg_per_share, 0),
IIF(COALESCE(d.weight_total, 0) > h.shares * COALESCE(s.max_kg_per_share, 0),
COALESCE(d.weight_total, 0) - h.shares * COALESCE(s.max_kg_per_share, 0),
0)) AS diff,
h.shares_red,
h.shares_red * COALESCE(s.min_kg_per_share_red, s.min_kg_per_share, 0) AS min_kg_red,
h.shares_red * COALESCE(s.max_kg_per_share_red, s.max_kg_per_share, 0) AS max_kg_red,
COALESCE(d.weight_red, 0) AS weight_red,
IIF(COALESCE(d.weight_red, 0) < h.shares_red * COALESCE(s.min_kg_per_share_red, s.min_kg_per_share, 0),
COALESCE(d.weight_red, 0) - h.shares_red * COALESCE(s.min_kg_per_share_red, s.min_kg_per_share, 0),
IIF(COALESCE(d.weight_red, 0) > h.shares_red * COALESCE(s.max_kg_per_share_red, s.max_kg_per_share, 0),
COALESCE(d.weight_red, 0) - h.shares_red * COALESCE(s.max_kg_per_share_red, s.max_kg_per_share, 0),
0)) AS diff_red,
h.shares_white,
h.shares_white * COALESCE(s.min_kg_per_share_white, s.min_kg_per_share, 0) AS min_kg_white,
h.shares_white * COALESCE(s.max_kg_per_share_white, s.max_kg_per_share, 0) AS max_kg_white,
COALESCE(d.weight_white, 0) AS weight_white,
IIF(COALESCE(d.weight_white, 0) < h.shares_white * COALESCE(s.min_kg_per_share_white, s.min_kg_per_share, 0),
COALESCE(d.weight_white, 0) - h.shares_white * COALESCE(s.min_kg_per_share_white, s.min_kg_per_share, 0),
IIF(COALESCE(d.weight_white, 0) > h.shares_white * COALESCE(s.max_kg_per_share_white, s.max_kg_per_share, 0),
COALESCE(d.weight_white, 0) - h.shares_white * COALESCE(s.max_kg_per_share_white, s.max_kg_per_share, 0),
0)) AS diff_white
FROM member m, season s
LEFT JOIN v_member_history h ON (h.year, h.mgnr) = (s.year, m.mgnr)
LEFT JOIN v_stat_member d ON (d.year, d.mgnr) = (s.year, m.mgnr)
ORDER BY s.year, m.mgnr;
DROP VIEW v_penalty_business_shares;
CREATE VIEW v_penalty_business_shares AS
SELECT u.year, u.mgnr,
SUM(IIF(u.weight_total = 0, COALESCE(-s.penalty_none, 0) + COALESCE(-(u.shares + u.shares_red + u.shares_white) * s.penalty_per_share_none, 0), 0) +
IIF(u.diff < 0 OR u.diff_red < 0 OR u.diff_white < 0, COALESCE(-s.penalty_amount, 0), 0) +
COALESCE((u.diff + u.diff_red + u.diff_white) * s.penalty_per_kg, 0) +
COALESCE(CEIL(CAST(u.diff AS REAL) / s.min_kg_per_share) * s.penalty_per_share_amount, 0) +
COALESCE(CEIL(CAST(u.diff_red AS REAL) / s.min_kg_per_share_red) * s.penalty_per_share_amount, 0) +
COALESCE(CEIL(CAST(u.diff_white AS REAL) / s.min_kg_per_share_white) * s.penalty_per_share_amount, 0)
) AS total_penalty
FROM v_total_under_delivery u
JOIN season s ON u.year = s.year
JOIN member m ON m.mgnr = u.mgnr
WHERE m.active
GROUP BY u.year, u.mgnr
HAVING total_penalty < 0
ORDER BY u.year, u.mgnr;
DROP VIEW v_auto_business_shares;
CREATE VIEW v_auto_business_shares AS
SELECT (SUBSTR(h.date_notice, 1, 4) + 0) AS year,
h.to_mgnr AS mgnr,
SUM(h.shares) AS shares,
SUM(h.shares * COALESCE(h.value_per_share, 0)) AS total_amount
FROM member_history h
WHERE h.reason = 'prescription' AND h.source = 'elwig'
GROUP BY year, h.to_mgnr
ORDER BY year, h.to_mgnr;
PRAGMA writable_schema = OFF;
+37
View File
@@ -0,0 +1,37 @@
-- schema version 40 to 41
PRAGMA writable_schema = ON;
UPDATE sqlite_schema SET sql = REPLACE(sql, 'scale_id ', 'scale_terminal')
WHERE type = 'table' AND name = 'delivery_part';
DROP VIEW v_bki_delivery;
DROP VIEW v_bki_member;
CREATE VIEW v_bki_member AS
SELECT s.year, m.mgnr, m.lfbis_nr, m.name,
(COALESCE(m.prefix || ' ', '') || m.given_name || COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '')) AS other_names,
a.name AS billing_name, COALESCE(a.address, m.address) AS address,
COALESCE(a.country, m.country) AS country, COALESCE(a.postal_dest, m.postal_dest) AS postal_dest,
m.volllieferant AS vollablieferer,
SUM(COALESCE(IIF((c.year_from IS NULL OR c.year_from <= s.year) AND (c.year_to IS NULL OR c.year_to >= s.year), c.area, NULL), 0)) AS area
FROM season s, member m
LEFT JOIN member_billing_address a ON a.mgnr = m.mgnr
LEFT JOIN area_commitment c ON c.mgnr = m.mgnr
GROUP BY s.year, m.mgnr;
CREATE VIEW v_bki_delivery AS
SELECT m.lfbis_nr, m.name, m.other_names, m.billing_name,
m.address, plz.plz, IIF(INSTR(o.name, ',') = 0, o.name, SUBSTR(o.name, 1, INSTR(o.name, ',') - 1)) AS ort,
d.date, d.weight, v.type, v.sortid, d.qualid, d.year, d.hkid, d.kmw, d.oe,
m.vollablieferer, m.area
FROM v_delivery d
JOIN v_bki_member m ON (m.year, m.mgnr) = (d.year, d.mgnr)
JOIN postal_dest pd ON (pd.country, pd.id) = (m.country, m.postal_dest)
LEFT JOIN AT_plz_dest ap ON (ap.country, ap.id) = (pd.country, pd.id)
LEFT JOIN AT_plz plz ON plz.plz = ap.plz
LEFT JOIN AT_ort o ON o.okz = ap.okz
JOIN wine_variety v ON v.sortid = d.sortid
ORDER BY d.date, d.time;
PRAGMA writable_schema = OFF;
+8 -21
View File
@@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows;
namespace Elwig.Services {
public static class AreaComService {
@@ -115,7 +114,7 @@ namespace Elwig.Services {
using var ctx = new AppDbContext();
var c = new AreaComContract {
FbNr = oldFbNr ?? newFbNr,
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
Comment = string.IsNullOrWhiteSpace(vm.Comment) ? null : vm.Comment,
KgNr = vm.Kg!.KgNr,
RdNr = vm.Rd?.RdNr,
};
@@ -158,25 +157,13 @@ namespace Elwig.Services {
});
}
private static void AddToolTipCell(Grid grid, string text, int row, int col, int colSpan = 1, bool bold = false, bool alignRight = false, bool alignCenter = false) {
var tb = new TextBlock() {
Text = text,
TextAlignment = alignRight ? TextAlignment.Right : alignCenter ? TextAlignment.Center : TextAlignment.Left,
Margin = new(0, 12 * row, 0, 0),
FontWeight = bold ? FontWeights.Bold : FontWeights.Normal,
};
tb.SetValue(Grid.ColumnProperty, col);
tb.SetValue(Grid.ColumnSpanProperty, colSpan);
grid.Children.Add(tb);
}
private static void AddToolTipRow(Grid grid, int row, string? h1, string? h2, int area, int? min, int? max) {
var bold = h2 == null;
if (h1 != null) AddToolTipCell(grid, h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold);
if (h2 != null) AddToolTipCell(grid, h2 + ":", row, 1, 1, bold);
AddToolTipCell(grid, $"{area:N0} m²", row, 2, 1, bold, true);
AddToolTipCell(grid, min == null ? "" : $"{min:N0} kg", row, 3, 1, bold, true);
AddToolTipCell(grid, max == null ? "" : $"{max:N0} kg", row, 4, 1, bold, true);
if (h1 != null) grid.AddToolTipCell(h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold);
if (h2 != null) grid.AddToolTipCell(h2 + ":", row, 1, bold: bold);
grid.AddToolTipCell($"{area:N0} m²", row, 2, bold: bold, alignRight: true);
grid.AddToolTipCell(min == null ? "" : $"{min:N0} kg", row, 3, bold: bold, alignRight: true);
grid.AddToolTipCell(max == null ? "" : $"{max:N0} kg", row, 4, bold: bold, alignRight: true);
}
public static async Task<(string, (string?, string?, int, int?, int?)[])> GenerateToolTipData(IQueryable<AreaCom> areaComs, int maxKgPerHa) {
@@ -255,8 +242,8 @@ namespace Elwig.Services {
grid.ColumnDefinitions.Add(new() { Width = new(80) });
grid.ColumnDefinitions.Add(new() { Width = new(80) });
grid.ColumnDefinitions.Add(new() { Width = new(80) });
AddToolTipCell(grid, "Lieferpflicht", 0, 3, 1, false, false, true);
AddToolTipCell(grid, "Lieferrecht", 0, 4, 1, false, false, true);
grid.AddToolTipCell("Lieferpflicht", 0, 3, alignCenter: true);
grid.AddToolTipCell("Lieferrecht", 0, 4, 1, alignCenter: true);
int rowNum = 1;
foreach (var row in data) {
if (rowNum == 2 || (rowNum != 1 && row.Item1 != null)) rowNum++;
+15 -47
View File
@@ -8,9 +8,6 @@ using Microsoft.EntityFrameworkCore;
using Elwig.Documents;
using Elwig.Helpers.Export;
using Elwig.Models.Dtos;
using Microsoft.Win32;
using System.Windows.Input;
using System.Windows;
using System;
using LinqKit;
using System.Windows.Controls;
@@ -261,61 +258,32 @@ namespace Elwig.Services {
.ThenBy(a => a.Member.MgNr);
if (mode == ExportMode.SaveList) {
var d = new SaveFileDialog() {
FileName = $"{DeliveryAncmtList.Name}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"{DeliveryAncmtList.Name} speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var data = await DeliveryAncmtListData.FromQuery(query, filterNames);
using var ods = new OdsFile(d.FileName);
await ods.AddTable(data);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
var filename = InteractionService.SaveFile(DeliveryAncmtList.Name, DeliveryAncmtList.Name, "ods");
if (filename != null) {
await Utils.RunForeground(async () => {
var data = await DeliveryAncmtListData.FromQuery(query, filterNames);
using var ods = new OdsFile(filename);
await ods.AddTable(data);
});
Mouse.OverrideCursor = null;
}
} else {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var data = await DeliveryAncmtListData.FromQuery(query, filterNames);
using var doc = new DeliveryAncmtList(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
await Utils.RunForeground(async () => {
var data = await DeliveryAncmtListData.FromQuery(query, filterNames);
using var doc = new DeliveryAncmtList(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
});
Mouse.OverrideCursor = null;
}
}
private static void AddToolTipCell(Grid grid, string text, int row, int col, int colSpan = 1, bool bold = false, bool alignRight = false, bool alignCenter = false) {
var tb = new TextBlock() {
Text = text,
TextAlignment = alignRight ? TextAlignment.Right : alignCenter ? TextAlignment.Center : TextAlignment.Left,
Margin = new(0, 12 * row, 0, 0),
FontWeight = bold ? FontWeights.Bold : FontWeights.Normal,
};
tb.SetValue(Grid.ColumnProperty, col);
tb.SetValue(Grid.ColumnSpanProperty, colSpan);
grid.Children.Add(tb);
}
private static void AddToolTipRow(Grid grid, int row, string? h1, string? h2, int weight, int? total1, int total2) {
var bold = h2 == null;
if (h1 != null) AddToolTipCell(grid, h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold);
if (h2 != null) AddToolTipCell(grid, h2 + ":", row, 1, 1, bold);
AddToolTipCell(grid, $"{weight:N0} kg", row, 2, 1, bold, true);
if (h1 != null) grid.AddToolTipCell(h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold);
if (h2 != null) grid.AddToolTipCell(h2 + ":", row, 1, 1, bold);
grid.AddToolTipCell($"{weight:N0} kg", row, 2, bold: bold, alignRight: true);
if (total1 != null && total1 != 0)
AddToolTipCell(grid, $"{weight * 100.0 / total1:N1} %", row, 3, 1, bold, true);
grid.AddToolTipCell($"{weight * 100.0 / total1:N1} %", row, 3, bold: bold, alignRight: true);
if (total2 != 0)
AddToolTipCell(grid, $"{weight * 100.0 / total2:N1} %", row, 4, 1, bold, true);
grid.AddToolTipCell($"{weight * 100.0 / total2:N1} %", row, 4, bold: bold, alignRight: true);
}
public static async Task<(string Text, (string?, string?, int, int?, int)[] Grid)> GenerateToolTipData(IQueryable<DeliveryAncmt> deliveryAncmts) {
+99 -214
View File
@@ -3,12 +3,9 @@ using Elwig.Helpers.Export;
using Elwig.Helpers;
using Elwig.Models.Dtos;
using Elwig.Models.Entities;
using Microsoft.Win32;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows;
using System;
using Elwig.ViewModels;
using LinqKit;
@@ -25,15 +22,6 @@ namespace Elwig.Services {
FromFilters, FromToday, FromSeason, FromSeasonAndBranch, Selected,
};
public static async Task<Member?> GetMemberAsync(int mgnr) {
using var ctx = new AppDbContext();
return await ctx.FetchMembers(mgnr).SingleOrDefaultAsync();
}
public static Member? GetMember(int mgnr) {
return GetMemberAsync(mgnr).GetAwaiter().GetResult();
}
public static void ClearInputs(this DeliveryAdminViewModel vm) {
}
@@ -79,7 +67,7 @@ namespace Elwig.Services {
vm.IsGebunden = p.IsGebunden;
vm.Unloading = p.Unloading;
vm.ScaleId = p.ScaleId;
vm.ScaleTerminalId = p.ScaleTerminalId;
vm.WeighingData = p.WeighingData;
vm.ManualWeighingReason = p.WeighingReason;
}
@@ -488,7 +476,7 @@ namespace Elwig.Services {
if (partNew && timeIsDefault) {
newTimeString = DateTime.Now.ToString("HH:mm:ss");
} else if (partNew || timeHasChanged) {
newTimeString = string.IsNullOrEmpty(vm.Time) ? null : vm.Time + ":00";
newTimeString = string.IsNullOrWhiteSpace(vm.Time) ? null : vm.Time + ":00";
}
var d = new Delivery {
@@ -500,7 +488,7 @@ namespace Elwig.Services {
ZwstId = vm.Branch!.ZwstId,
LsNr = newLsNr ?? vm.LsNr!,
MgNr = vm.MgNr!.Value,
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
Comment = string.IsNullOrWhiteSpace(vm.Comment) ? null : vm.Comment,
};
p = new DeliveryPart {
@@ -523,11 +511,11 @@ namespace Elwig.Services {
Unloading = vm.Unloading,
Temperature = vm.Temperature,
Acid = vm.Acid,
Comment = string.IsNullOrEmpty(vm.PartComment) ? null : vm.PartComment,
Comment = string.IsNullOrWhiteSpace(vm.PartComment) ? null : vm.PartComment,
Weight = vm.Weight!.Value,
IsManualWeighing = vm.IsManualWeighing,
ScaleId = vm.ScaleId,
ScaleTerminalId = vm.ScaleTerminalId,
WeighingData = vm.WeighingData,
WeighingReason = vm.ManualWeighingReason,
};
@@ -727,16 +715,10 @@ namespace Elwig.Services {
}
public static async Task GenerateDeliveryNote(int year, int did, ExportMode mode) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
using var doc = await DeliveryNote.Initialize(year, did);
await Utils.ExportDocument(doc, mode, doc.Delivery.LsNr, (doc.Member, $"{DeliveryNote.Name} Nr. {doc.Delivery.LsNr}", $"Im Anhang finden Sie den {DeliveryNote.Name} Nr. {doc.Delivery.LsNr}"));
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
await Utils.RunForeground(async () => {
using var doc = await DeliveryNote.Initialize(year, did);
await Utils.ExportDocument(doc, mode, doc.Delivery.LsNr, (doc.Member, $"{DeliveryNote.Name} Nr. {doc.Delivery.LsNr}", $"Im Anhang finden Sie den {DeliveryNote.Name} Nr. {doc.Delivery.LsNr}"));
});
Mouse.OverrideCursor = null;
}
public static async Task GenerateDeliveryJournal(this DeliveryAdminViewModel vm, ExportSubject subject, ExportMode mode) {
@@ -773,75 +755,44 @@ namespace Elwig.Services {
.ThenBy(p => p.DPNr);
if (mode == ExportMode.SaveList) {
var d = new SaveFileDialog() {
FileName = $"{DeliveryJournal.Name}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"{DeliveryJournal.Name} speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var data = await DeliveryJournalData.FromQuery(query, filterNames);
using var ods = new OdsFile(d.FileName);
await ods.AddTable(data);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
var filename = InteractionService.SaveFile(DeliveryJournal.Name, DeliveryJournal.Name, "ods");
if (filename != null) {
await Utils.RunForeground(async () => {
var data = await DeliveryJournalData.FromQuery(query, filterNames);
using var ods = new OdsFile(filename);
await ods.AddTable(data);
});
Mouse.OverrideCursor = null;
}
} else if (mode == ExportMode.Export) {
var d = new SaveFileDialog() {
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",
Filter = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip",
Title = $"{DeliveryJournal.Name} speichern unter - Elwig",
AddExtension = false,
};
if (d.ShowDialog() == true) {
if (!d.FileName.EndsWith(".elwig.zip")) d.FileName += ".elwig.zip";
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var list = await query
.Select(p => p.Delivery)
.Distinct()
.Include(d => d.Parts).ThenInclude(p => p.PartModifiers)
.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) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
var filename = InteractionService.SaveFile(DeliveryJournal.Name, subject == ExportSubject.Selected ? $"Lieferung_{vm.SelectedDelivery?.LsNr}" : $"Lieferungen_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}", "elwig.zip");
if (filename != null) {
if (!filename.EndsWith(".elwig.zip")) filename += ".elwig.zip";
await Utils.RunForeground(async () => {
var list = await query
.Select(p => p.Delivery)
.Distinct()
.Include(d => d.Parts).ThenInclude(p => p.PartModifiers)
.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(filename, list, wbKgs, filterNames);
});
Mouse.OverrideCursor = null;
}
} else if (mode == ExportMode.Upload && App.Config.SyncUrl != null) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
await Utils.RunForeground(async () => {
await SyncService.Upload(App.Config.SyncUrl, App.Config.SyncUrl, App.Config.SyncPassword, query, filterNames);
});
Mouse.OverrideCursor = null;
} else {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var data = await DeliveryJournalData.FromQuery(query, filterNames);
using var doc = new DeliveryJournal(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
await Utils.RunForeground(async () => {
var data = await DeliveryJournalData.FromQuery(query, filterNames);
using var doc = new DeliveryJournal(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
});
Mouse.OverrideCursor = null;
}
}
@@ -862,17 +813,11 @@ namespace Elwig.Services {
throw new ArgumentException("Invalid value for ExportSubject");
}
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var data = await WineQualityStatisticsData.FromQuery(query, App.Client.OrderingMemberList);
using var doc = new WineQualityStatistics(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
await Utils.RunForeground(async () => {
var data = await WineQualityStatisticsData.FromQuery(query, App.Client.OrderingMemberList);
using var doc = new WineQualityStatistics(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
});
Mouse.OverrideCursor = null;
}
public static async Task GenerateLocalityStatistics(this DeliveryAdminViewModel vm, ExportSubject subject) {
@@ -888,24 +833,13 @@ namespace Elwig.Services {
throw new ArgumentException("Invalid value for ExportSubject");
}
var d = new SaveFileDialog() {
FileName = $"Lieferstatistik-{vm.FilterSeason ?? Utils.CurrentLastSeason}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"Lieferstatistik pro Ort speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
using var ods = new OdsFile(d.FileName);
var tbl = await WineLocalityStatisticsData.FromQuery(query, filterNames);
await ods.AddTable(tbl);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
var filename = InteractionService.SaveFile("Lieferstatistik pro Ort", $"Lieferstatistik-{vm.FilterSeason ?? Utils.CurrentLastSeason}", "ods");
if (filename != null) {
await Utils.RunForeground(async () => {
using var ods = new OdsFile(filename);
var tbl = await WineLocalityStatisticsData.FromQuery(query, filterNames);
await ods.AddTable(tbl);
});
Mouse.OverrideCursor = null;
}
}
@@ -936,45 +870,28 @@ namespace Elwig.Services {
filterNames.Remove("abgewertet");
if (mode == ExportMode.SaveList) {
var d = new SaveFileDialog() {
FileName = $"{DeliveryDepreciationList.Name}-{vm.FilterSeason ?? Utils.CurrentLastSeason}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"{DeliveryDepreciationList.Name} speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
using var ods = new OdsFile(d.FileName);
var tblTotal = await DeliveryJournalData.FromQuery(query, filterNames);
tblTotal.FullName = DeliveryDepreciationList.Name;
tblTotal.Name = "Gesamt";
await ods.AddTable(tblTotal);
foreach (var branch in await ctx.FetchBranches().ToListAsync()) {
var tbl = await DeliveryJournalData.FromQuery(query.Where(p => p.Delivery.ZwstId == branch.ZwstId), filterNames);
tbl.FullName = DeliveryDepreciationList.Name;
tbl.Name = branch.Name;
await ods.AddTable(tbl);
}
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
var filename = InteractionService.SaveFile(DeliveryDepreciationList.Name, $"{DeliveryDepreciationList.Name}-{vm.FilterSeason ?? Utils.CurrentLastSeason}", "ods");
if (filename != null) {
await Utils.RunForeground(async () => {
using var ods = new OdsFile(filename);
var tblTotal = await DeliveryJournalData.FromQuery(query, filterNames);
tblTotal.FullName = DeliveryDepreciationList.Name;
tblTotal.Name = "Gesamt";
await ods.AddTable(tblTotal);
foreach (var branch in await ctx.FetchBranches().ToListAsync()) {
var tbl = await DeliveryJournalData.FromQuery(query.Where(p => p.Delivery.ZwstId == branch.ZwstId), filterNames);
tbl.FullName = DeliveryDepreciationList.Name;
tbl.Name = branch.Name;
await ods.AddTable(tbl);
}
});
Mouse.OverrideCursor = null;
}
} else {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var data = await DeliveryJournalData.FromQuery(query, filterNames);
using var doc = new DeliveryDepreciationList(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
await Utils.RunForeground(async () => {
var data = await DeliveryJournalData.FromQuery(query, filterNames);
using var doc = new DeliveryDepreciationList(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
});
Mouse.OverrideCursor = null;
}
}
@@ -1001,59 +918,36 @@ namespace Elwig.Services {
.ThenBy(p => p.AttrId)
.ThenBy(p => p.CultId);
var d = new SaveFileDialog() {
FileName = $"Liefermengen.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"Liefermengen speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
using var ods = new OdsFile(d.FileName);
var tblTotal = await MemberDeliveryData.FromQuery(query, filterNames);
var tbl = await MemberDeliveryPerVarietyData.FromQuery(query, filterNames);
await ods.AddTable(tblTotal);
await ods.AddTable(tbl);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
var filename = InteractionService.SaveFile("Liefermengen", "Liefermengen", "ods");
if (filename != null) {
await Utils.RunForeground(async () => {
using var ods = new OdsFile(filename);
var tblTotal = await MemberDeliveryData.FromQuery(query, filterNames);
var tbl = await MemberDeliveryPerVarietyData.FromQuery(query, filterNames);
await ods.AddTable(tblTotal);
await ods.AddTable(tbl);
});
Mouse.OverrideCursor = null;
}
}
private static void AddToolTipCell(Grid grid, string text, int row, int col, int colSpan = 1, bool bold = false, bool alignRight = false, bool alignCenter = false) {
var tb = new TextBlock() {
Text = text,
TextAlignment = alignRight ? TextAlignment.Right : alignCenter ? TextAlignment.Center : TextAlignment.Left,
Margin = new(0, 12 * row, 0, 0),
FontWeight = bold ? FontWeights.Bold : FontWeights.Normal,
};
tb.SetValue(Grid.ColumnProperty, col);
tb.SetValue(Grid.ColumnSpanProperty, colSpan);
grid.Children.Add(tb);
}
private static void AddWeightToolTipRow(Grid grid, int row, string? h1, string? h2, int weight, int? total1, int total2) {
var bold = h2 == null;
if (h1 != null) AddToolTipCell(grid, h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold);
if (h2 != null) AddToolTipCell(grid, h2 + ":", row, 1, 1, bold);
AddToolTipCell(grid, $"{weight:N0} kg", row, 2, 1, bold, true);
if (h1 != null) grid.AddToolTipCell(h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold);
if (h2 != null) grid.AddToolTipCell(h2 + ":", row, 1, 1, bold);
grid.AddToolTipCell($"{weight:N0} kg", row, 2, bold: bold, alignRight: true);
if (total1 != null && total1 != 0)
AddToolTipCell(grid, $"{weight * 100.0 / total1:N1} %", row, 3, 1, bold, true);
grid.AddToolTipCell($"{weight * 100.0 / total1:N1} %", row, 3, bold: bold, alignRight: true);
if (total2 != 0)
AddToolTipCell(grid, $"{weight * 100.0 / total2:N1} %", row, 4, 1, bold, true);
grid.AddToolTipCell($"{weight * 100.0 / total2:N1} %", row, 4, bold: bold, alignRight: true);
}
private static void AddGradationToolTipRow(Grid grid, int row, string? h1, string? h2, double min, double avg, double max) {
var bold = h2 == null;
if (h1 != null) AddToolTipCell(grid, h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold);
if (h2 != null) AddToolTipCell(grid, h2 + ":", row, 1, 1, bold);
AddToolTipCell(grid, $"{min:N1}°", row, 2, 1, bold, true);
AddToolTipCell(grid, $"{avg:N1}°", row, 3, 1, bold, true);
AddToolTipCell(grid, $"{max:N1}°", row, 4, 1, bold, true);
if (h1 != null) grid.AddToolTipCell(h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold);
if (h2 != null) grid.AddToolTipCell(h2 + ":", row, 1, bold: bold);
grid.AddToolTipCell($"{min:N1}°", row, 2, bold: bold, alignRight: true);
grid.AddToolTipCell($"{avg:N1}°", row, 3, bold: bold, alignRight: true);
grid.AddToolTipCell($"{max:N1}°", row, 4, bold: bold, alignRight: true);
}
public static async Task<(string WeightText, (string?, string?, int, int?, int)[] WeightGrid, string GradationText, (string?, string?, double, double, double)[] GradationGrid)> GenerateToolTipData(IQueryable<DeliveryPart> deliveryParts) {
@@ -1182,9 +1076,9 @@ namespace Elwig.Services {
gGrid.ColumnDefinitions.Add(new() { Width = new(35) });
gGrid.ColumnDefinitions.Add(new() { Width = new(35) });
gGrid.ColumnDefinitions.Add(new() { Width = new(35) });
AddToolTipCell(gGrid, "Min.", 0, 2, 1, false, false, true);
AddToolTipCell(gGrid, "⌀", 0, 3, 1, false, false, true);
AddToolTipCell(gGrid, "Max.", 0, 4, 1, false, false, true);
gGrid.AddToolTipCell("Min.", 0, 2, alignCenter: true);
gGrid.AddToolTipCell("⌀", 0, 3, alignCenter: true);
gGrid.AddToolTipCell("Max.", 0, 4, alignCenter: true);
rowNum = 1;
foreach (var row in gradationData) {
if (rowNum != 1 && row.Item2 == null) rowNum++;
@@ -1219,12 +1113,11 @@ namespace Elwig.Services {
attrid = $"'{attr.AttrId}'";
}
var dids = await vm.GetDidsFromFilters();
var res = MessageBox.Show($"Soll wirklich für {dids.Length:N0} Teillieferung(en) das Attribut\n'{attributeName}' gesetz werden?",
"Massenaktion: Attribut setzen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (res != MessageBoxResult.OK) return;
if (!InteractionService.AskContinue("Massenaktion: Attribut setzen",
$"Soll wirklich für {dids.Length:N0} Teillieferung(en) das Attribut\n'{attributeName}' gesetz werden?"))
return;
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
await Utils.RunForeground(async () => {
using (var cnx = await AppDbContext.ConnectAsync()) {
await cnx.ExecuteBatch($"""
UPDATE delivery_part SET attrid = {attrid}
@@ -1234,9 +1127,7 @@ namespace Elwig.Services {
App.HintContextChange();
});
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
} finally {
Mouse.OverrideCursor = null;
InteractionService.ShowException(exc);
}
}
@@ -1249,12 +1140,11 @@ namespace Elwig.Services {
}
var dids = await vm.GetDidsFromFilters();
var res = MessageBox.Show($"Soll wirklich für {dids.Length:N0} Teillieferung(en) der Zu-/Abschlag\n'{modifierName}' hinzugefügt werden?",
"Massenaktion: Zu-/Abschlag hinzufügen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (res != MessageBoxResult.OK) return;
if (!InteractionService.AskContinue("Massenaktion: Zu-/Abschlag hinzufügen",
$"Soll wirklich für {dids.Length:N0} Teillieferung(en) der Zu-/Abschlag\n'{modifierName}' hinzugefügt werden?"))
return;
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
await Utils.RunForeground(async () => {
using (var cnx = await AppDbContext.ConnectAsync()) {
await cnx.ExecuteBatch($"""
INSERT INTO delivery_part_modifier (year, did, dpnr, modid)
@@ -1265,9 +1155,7 @@ namespace Elwig.Services {
App.HintContextChange();
});
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
} finally {
Mouse.OverrideCursor = null;
InteractionService.ShowException(exc);
}
}
@@ -1280,12 +1168,11 @@ namespace Elwig.Services {
}
var dids = await vm.GetDidsFromFilters();
var res = MessageBox.Show($"Soll wirklich für {dids.Length:N0} Teillieferung(en) der Zu-/Abschlag\n'{modifierName}' entfernt werden?",
"Massenaktion: Zu-/Abschlag entfernen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (res != MessageBoxResult.OK) return;
if (!InteractionService.AskContinue("Massenaktion: Zu-/Abschlag entfernen",
$"Soll wirklich für {dids.Length:N0} Teillieferung(en) der Zu-/Abschlag\n'{modifierName}' entfernt werden?"))
return;
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
await Utils.RunForeground(async () => {
using (var cnx = await AppDbContext.ConnectAsync()) {
await cnx.ExecuteBatch($"""
DELETE FROM delivery_part_modifier
@@ -1295,9 +1182,7 @@ namespace Elwig.Services {
App.HintContextChange();
});
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
} finally {
Mouse.OverrideCursor = null;
InteractionService.ShowException(exc);
}
}
}
+163
View File
@@ -0,0 +1,163 @@
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Windows;
namespace Elwig.Services {
public static class InteractionService {
public static Func<string, string, string, string?>? Override;
public static Action<string, string>? NextInformation;
public static Action<string, string>? NextWarning;
public static Action<string, string>? NextError;
public static Func<string, string, bool>? NextContinue;
public static Func<string, string, bool>? NextConfirmation;
public static Func<string, string, bool>? NextQuestion;
public static Func<string, string, string?>? NextSave;
public static readonly Dictionary<string, string> ExtensionFilters = new() {
["pdf"] = "PDF-Datei (*.pdf)|*.pdf",
["ods"] = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
["vcf"] = "vCard-Datei (*.vcf)|*.vcf",
["xml"] = "EBICS-Datei (*.xml)|*.xml",
["csv"] = "CSV-Datei (*.csv)|*.csv",
["sql.zip"] = "Komprimierte SQL-Datei (*.sql.zip)|*.sql.zip",
["elwig.zip"] = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip",
};
public static void ShowInformation(string title, string text) {
if (NextInformation != null) {
NextInformation(title, text);
NextInformation = null;
} else if (Override != null) {
Override("information", title, text);
} else {
MessageBox.Show(text, title, MessageBoxButton.OK, MessageBoxImage.Information);
}
}
public static bool AskContinue(string title, string text) {
if (NextContinue != null) {
var r = NextContinue(title, text);
NextContinue = null;
return r;
} else if (Override != null) {
return Override("continue", title, text) != null;
} else {
return MessageBox.Show(text, title, MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel) == MessageBoxResult.OK;
}
}
public static bool? AskContinueYesNo(string title, string text) {
if (NextContinue != null) {
var r = NextContinue(title, text);
NextContinue = null;
return r;
} else if (Override != null) {
return Override("continue", title, text) != null;
} else {
var r = MessageBox.Show(text, title, MessageBoxButton.YesNoCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
return r == MessageBoxResult.Yes ? true : r == MessageBoxResult.No ? false : null;
}
}
public static bool AskConfirmation(string title, string text) {
if (NextConfirmation != null) {
var r = NextConfirmation(title, text);
NextConfirmation = null;
return r;
} else if (Override != null) {
return Override("confirm", title, text) != null;
} else {
return MessageBox.Show(text, title, MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No) == MessageBoxResult.Yes;
}
}
public static bool AskQuestion(string title, string text, bool defaultResult) {
if (NextQuestion != null) {
var r = NextQuestion(title, text);
NextQuestion = null;
return r;
} else if (Override != null) {
return Override("question", title, text) != null;
} else {
return MessageBox.Show(text, title, MessageBoxButton.YesNo, MessageBoxImage.Question, defaultResult ? MessageBoxResult.Yes : MessageBoxResult.No) == MessageBoxResult.Yes;
}
}
public static void ShowWarning(string title, string text) {
if (NextWarning != null) {
NextWarning(title, text);
NextWarning = null;
} else if (Override != null) {
Override("warning", title, text);
} else {
MessageBox.Show(text, title, MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
public static void ShowError(string title, string text) {
if (NextError != null) {
NextError(title, text);
NextError = null;
} else if (Override != null) {
Override("error", title, text);
} else {
MessageBox.Show(text, title, MessageBoxButton.OK, MessageBoxImage.Error);
}
}
public static void ShowException(Exception exc, bool showExcType = false, bool isError = true) {
ShowException("Fehler", exc, showExcType, isError);
}
public static void ShowException(string title, Exception exc, bool showExcType = false, bool isError = true) {
ShowException(title, null, exc, showExcType, isError);
}
public static void ShowException(string title, string? text, Exception exc, bool showExcType = false, bool isError = true) {
text = text == null ? "" : text + (text.EndsWith('.') || text.EndsWith('!') || text.EndsWith('?') ? "" : ":") + "\n\n";
text += exc.Message + (showExcType ? $" ({exc.GetType().Name})" : "");
if (exc.InnerException != null) text += "\n\n" + exc.InnerException.Message;
if (isError) {
ShowError(title, text);
} else {
ShowWarning(title, text);
}
}
public static void ShowDbException(string title, Exception exc) {
ShowException(title, "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!", exc);
}
public static bool AskException(string title, string? text, Exception exc) {
text = text == null ? "" : text + (text.EndsWith('.') || text.EndsWith('!') || text.EndsWith('?') ? "" : ":") + "\n\n";
text += exc.Message;
if (exc.InnerException != null) text += "\n\n" + exc.InnerException.Message;
if (Override != null) {
return Override("error", title, text) != null;
} else {
return MessageBox.Show(text, title, MessageBoxButton.YesNo, MessageBoxImage.Error, MessageBoxResult.No) == MessageBoxResult.Yes;
}
}
public static string? SaveFile(string title, string defaultFileName, string extension) {
if (NextSave != null) {
var r = NextSave(title, $"{defaultFileName}.{extension}");
NextSave = null;
return r;
} else if (Override != null) {
return Override("save", title, $"{defaultFileName}.{extension}");
} else {
var d = new SaveFileDialog() {
FileName = $"{defaultFileName}.{extension}",
DefaultExt = extension,
Filter = ExtensionFilters.GetValueOrDefault(extension, ""),
Title = $"{title} speichern unter - Elwig",
AddExtension = !extension.Contains('.'),
};
return d.ShowDialog() == true ? d.FileName : null;
}
}
}
}
@@ -0,0 +1,98 @@
using Elwig.Helpers;
using Elwig.Models;
using Elwig.Models.Entities;
using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;
namespace Elwig.Services {
public static class MemberBusinessSharesService {
public static async Task InitInputs(this MemberBusinessSharesViewModel vm) {
using var ctx = new AppDbContext();
vm.DateNoticeString = $"{Utils.Today:dd.MM.yyyy}";
vm.ValuePerShare = (await ctx.Seasons.OrderBy(s => s.Year).LastOrDefaultAsync())?.BusinessShareValue;
}
public static void ClearInputs(this MemberBusinessSharesViewModel vm) {
}
public static void FillInputs(this MemberBusinessSharesViewModel vm, MemberHistory h) {
var mgnr = vm.FilterMember.MgNr;
vm.DateNotice = h.DateNotice;
vm.DateEffective = h.DateEffective;
vm.Reason = h.Reason + (h.Reason == MemberHistory.REASON_TRANSFER ? $"_{(h.ToMgNr == mgnr ? "from" : "to")}" : "");
vm.Shares = h.Shares;
vm.MemberHasSigned = h.MemberHasSigned;
vm.ValuePerShare = h.ValuePerShare;
vm.OtherMgNr = h.FromMgNr != null && h.FromMgNr != mgnr ? h.FromMgNr : h.ToMgNr != null && h.ToMgNr != mgnr ? h.ToMgNr : null;
vm.DeductYear = h.DeductYear ?? h.DateNotice.Year;
vm.Deduct = h.DeductYear != null;
vm.Comment = h.Comment;
}
public static async Task<int> UpdateMemberHistory(this MemberBusinessSharesViewModel vm, int? oldHistNr, bool enableTriggers) {
return await Task.Run(async () => {
using var ctx = new AppDbContext();
var tx = await ctx.Database.BeginTransactionAsync();
await ctx.Database.ExecuteSqlAsync($"UPDATE client_parameter SET value = {(enableTriggers ? "1" : "0")} WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS'");
var h = new MemberHistory {
HistNr = oldHistNr ?? await ctx.NextHistNr(),
FromMgNr =
vm.Reason == $"{MemberHistory.REASON_TRANSFER}_from" ? vm.OtherMgNr :
vm.Reason == MemberHistory.REASON_PURCHASE || vm.Reason == MemberHistory.REASON_PRESCRIPTION ? null :
vm.FilterMember.MgNr,
FromType =
vm.Reason == MemberHistory.REASON_PAYOUT ? BusinessShareType.Dormant :
vm.Reason == MemberHistory.REASON_PURCHASE || vm.Reason == MemberHistory.REASON_PRESCRIPTION ? null :
BusinessShareType.Normal,
ToMgNr =
vm.Reason == MemberHistory.REASON_PAYOUT || vm.Reason == MemberHistory.REASON_EXCLUSION ? null :
vm.Reason == $"{MemberHistory.REASON_TRANSFER}_to" ? vm.OtherMgNr :
vm.FilterMember.MgNr,
ToType =
vm.Reason == MemberHistory.REASON_PAYOUT || vm.Reason == MemberHistory.REASON_EXCLUSION ? null :
vm.Reason == MemberHistory.REASON_RETIREMENT || vm.Reason == MemberHistory.REASON_DECEASED ? BusinessShareType.Dormant :
BusinessShareType.Normal,
DateNoticeString = $"{vm.DateNotice:yyyy-MM-dd}",
DateEffectiveString = $"{vm.DateEffective:yyyy-MM-dd}",
Reason = vm.Reason?.Split("_")[0] ?? MemberHistory.REASON_CONVERT,
Source = "manual",
Shares = vm.Shares ?? 0,
MemberHasSigned = vm.MemberHasSigned,
ValuePerShare = vm.ValuePerShare,
CurrencyCode = vm.ValuePerShare != null ? "EUR" : null,
DeductYear = vm.Deduct ? vm.DeductYear : null,
Comment = string.IsNullOrWhiteSpace(vm.Comment) ? null : vm.Comment.Trim(),
};
if (oldHistNr != null) {
ctx.Update(h);
} else {
ctx.Add(h);
}
await ctx.SaveChangesAsync();
await ctx.Database.ExecuteSqlAsync($"UPDATE client_parameter SET value = '1' WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS'");
await tx.CommitAsync();
return h.HistNr;
});
}
public static async Task DeleteMemberHistory(int histnr, bool enableTriggers) {
await Task.Run(async () => {
using var ctx = new AppDbContext();
var tx = await ctx.Database.BeginTransactionAsync();
await ctx.Database.ExecuteSqlAsync($"UPDATE client_parameter SET value = {(enableTriggers ? "1" : "0")} WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS'");
await ctx.MemberHistory.Where(h => h.HistNr == histnr).ExecuteDeleteAsync();
await ctx.Database.ExecuteSqlAsync($"UPDATE client_parameter SET value = '1' WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS'");
await tx.CommitAsync();
});
}
}
}
+88 -130
View File
@@ -6,13 +6,10 @@ using Elwig.Models.Dtos;
using Elwig.Models.Entities;
using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace Elwig.Services {
public static class MemberService {
@@ -129,7 +126,15 @@ namespace Elwig.Services {
vm.EntryDate = (m.EntryDateString != null) ? string.Join(".", m.EntryDateString.Split("-").Reverse()) : null;
vm.ExitDate = (m.ExitDateString != null) ? string.Join(".", m.ExitDateString.Split("-").Reverse()) : null;
vm.BusinessShares = m.BusinessShares;
if (App.Client.HasRedWhite) {
vm.BusinessShares1 = m.SharesRed;
vm.BusinessShares2 = m.SharesWhite;
vm.BusinessShares3 = m.SharesDormant + m.Shares;
} else {
vm.BusinessShares1 = m.Shares;
vm.BusinessShares2 = m.SharesDormant + m.SharesRed + m.SharesWhite;
vm.BusinessShares3 = 0;
}
vm.AccountingNr = m.AccountingNr;
vm.Branch = (Branch?)ControlUtils.GetItemFromSourceWithPk(vm.BranchSource, m.ZwstId);
vm.DefaultKg = (AT_Kg?)ControlUtils.GetItemFromSourceWithPk(vm.DefaultKgSource, m.DefaultKgNr);
@@ -392,47 +397,29 @@ namespace Elwig.Services {
}
public static async Task GenerateMemberDataSheet(Member m, ExportMode mode) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
using var doc = new MemberDataSheet(m);
await Utils.ExportDocument(doc, mode, emailData: (m, MemberDataSheet.Name, "Im Anhang finden Sie das aktuelle Stammdatenblatt"));
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
await Utils.RunForeground(async () => {
using var doc = new MemberDataSheet(m);
await Utils.ExportDocument(doc, mode, emailData: (m, MemberDataSheet.Name, "Im Anhang finden Sie das aktuelle Stammdatenblatt"));
});
Mouse.OverrideCursor = null;
}
public static async Task GenerateDeliveryConfirmation(Member m, int year, ExportMode mode) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var b = await Billing.Create(year);
await b.FinishSeason();
await b.CalculateBuckets();
App.HintContextChange();
await Utils.RunForeground(async () => {
var b = await Billing.Create(year);
await b.FinishSeason();
await b.CalculateBuckets();
App.HintContextChange();
using var doc = new DeliveryConfirmation(year, m, null);
await Utils.ExportDocument(doc, mode, emailData: (m, $"{DeliveryConfirmation.Name} {year}", $"Im Anhang finden Sie die Anlieferungsbestätigung {year}"));
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
using var doc = new DeliveryConfirmation(year, m, null);
await Utils.ExportDocument(doc, mode, emailData: (m, $"{DeliveryConfirmation.Name} {year}", $"Im Anhang finden Sie die Anlieferungsbestätigung {year}"));
});
Mouse.OverrideCursor = null;
}
public static async Task GenerateCreditNote(Member m, int year, int avnr, ExportMode mode) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
using var doc = await CreditNote.Initialize(year, avnr, m.MgNr, null);
await Utils.ExportDocument(doc, mode, emailData: (m, $"{CreditNote.Name} {doc.Payment.Variant.Name}", $"Im Anhang finden Sie die Traubengutschrift {doc.Payment.Variant.Name}"));
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
await Utils.RunForeground(async () => {
using var doc = await CreditNote.Initialize(year, avnr, m.MgNr, null);
await Utils.ExportDocument(doc, mode, emailData: (m, $"{CreditNote.Name} {doc.Payment.Variant.Name}", $"Im Anhang finden Sie die Traubengutschrift {doc.Payment.Variant.Name}"));
});
Mouse.OverrideCursor = null;
}
public static async Task GenerateMemberList(this MemberAdminViewModel vm, ExportSubject subject, ExportMode mode) {
@@ -477,104 +464,72 @@ namespace Elwig.Services {
}
if (mode == ExportMode.SaveList) {
var d = new SaveFileDialog() {
FileName = $"{MemberList.Name}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"{MemberList.Name} speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var data = await MemberListData.FromQuery(query, filterNames, filterNames.Where(f => f.StartsWith("Flächenbindung")).Select(f => f.Split(' ')[^1]));
using var ods = new OdsFile(d.FileName);
await ods.AddTable(data);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
var filename = InteractionService.SaveFile(MemberList.Name, MemberList.Name, "ods");
if (filename != null) {
await Utils.RunForeground(async () => {
var data = await MemberListData.FromQuery(query, filterNames, filterNames.Where(f => f.StartsWith("Flächenbindung")).Select(f => f.Split(' ')[^1]));
using var ods = new OdsFile(filename);
await ods.AddTable(data);
});
Mouse.OverrideCursor = null;
}
} else if (mode == ExportMode.Vcf) {
var d = new SaveFileDialog() {
FileName = "Mitglieder.vcf",
DefaultExt = "vcf",
Filter = "vCard-Datei (*.vcf)|*.vcf",
Title = "Kontakte speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var members = await query
.OrderBy(m => m.MgNr)
.Include(m => m.TelephoneNumbers)
.Include(m => m.EmailAddresses)
.ToListAsync();
using var exporter = new VCard(d.FileName);
await exporter.ExportAsync(members);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
var filename = InteractionService.SaveFile("Kontakte", "Mitglieder", "vcf");
if (filename != null) {
await Utils.RunForeground(async () => {
var members = await query
.OrderBy(m => m.MgNr)
.Include(m => m.TelephoneNumbers)
.Include(m => m.EmailAddresses)
.ToListAsync();
using var exporter = new VCard(filename);
await exporter.ExportAsync(members);
});
Mouse.OverrideCursor = null;
}
} else if (mode == ExportMode.Export) {
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",
DefaultExt = "elwig.zip",
Filter = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip",
Title = $"{MemberList.Name} speichern unter - Elwig",
AddExtension = false,
};
if (d.ShowDialog() == true) {
if (!d.FileName.EndsWith(".elwig.zip")) d.FileName += ".elwig.zip";
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var members = await query
.OrderBy(m => m.MgNr)
.Include(m => m.TelephoneNumbers)
.Include(m => m.EmailAddresses)
.ToListAsync();
var areaComs = await query
.SelectMany(m => m.AreaCommitments)
.Select(c => c.Contract).Distinct()
.Include(c => c.Revisions)
.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();
await ElwigData.Export(d.FileName, members, areaComs, wbKgs, filterNames);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
var filename = InteractionService.SaveFile(MemberList.Name, subject == ExportSubject.Selected ? $"Mitglied_{vm.SelectedMember?.MgNr}" : $"Mitglieder_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}", "elwig.zip");
if (filename != null) {
if (!filename.EndsWith(".elwig.zip")) filename += ".elwig.zip";
await Utils.RunForeground(async () => {
var members = await query
.OrderBy(m => m.MgNr)
.Include(m => m.TelephoneNumbers)
.Include(m => m.EmailAddresses)
.ToListAsync();
var history1 = await query.IgnoreAutoIncludes()
.SelectMany(m => m.HistoryFrom)
.ToListAsync();
var history2 = await query.IgnoreAutoIncludes()
.SelectMany(m => m.HistoryTo)
.ToListAsync();
var history = history1.Union(history2)
.DistinctBy(h => h.HistNr)
.OrderBy(h => h.DateNotice).ThenBy(h => h.HistNr)
.ToList();
var areaComs = await query
.SelectMany(m => m.AreaCommitments)
.Select(c => c.Contract).Distinct()
.Include(c => c.Revisions)
.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();
await ElwigData.Export(filename, members, history, areaComs, wbKgs, filterNames);
});
Mouse.OverrideCursor = null;
}
} else if (mode == ExportMode.Upload && App.Config.SyncUrl != null) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
await Utils.RunForeground(async () => {
await SyncService.Upload(App.Config.SyncUrl, App.Config.SyncUrl, App.Config.SyncPassword, query, filterNames);
});
Mouse.OverrideCursor = null;
} else {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var data = await MemberListData.FromQuery(query, filterNames, filterNames.Where(f => f.StartsWith("Flächenbindung")).Select(f => f.Split(' ')[^1]));
using var doc = new MemberList(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
await Utils.RunForeground(async () => {
var data = await MemberListData.FromQuery(query, filterNames, filterNames.Where(f => f.StartsWith("Flächenbindung")).Select(f => f.Split(' ')[^1]));
using var doc = new MemberList(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
});
Mouse.OverrideCursor = null;
}
}
@@ -589,30 +544,33 @@ namespace Elwig.Services {
Name = vm.Name!,
Suffix = vm.IsJuridicalPerson || string.IsNullOrWhiteSpace(vm.Suffix) ? null : vm.Suffix,
ForTheAttentionOf = !vm.IsJuridicalPerson || string.IsNullOrWhiteSpace(vm.ForTheAttentionOf) ? null : vm.ForTheAttentionOf,
Birthday = string.IsNullOrEmpty(vm.Birthday) ? null : string.Join("-", vm.Birthday!.Split(".").Reverse()),
Birthday = string.IsNullOrWhiteSpace(vm.Birthday) ? null : string.Join("-", vm.Birthday!.Split(".").Reverse()),
IsDeceased = vm.IsDeceased,
CountryNum = 40, // Austria AT AUT
PostalDestId = vm.Ort!.Id,
Address = vm.Address!,
Iban = string.IsNullOrEmpty(vm.Iban) ? null : vm.Iban?.Replace(" ", ""),
Bic = string.IsNullOrEmpty(vm.Bic) ? null : vm.Bic,
Iban = string.IsNullOrWhiteSpace(vm.Iban) ? null : vm.Iban?.Replace(" ", ""),
Bic = string.IsNullOrWhiteSpace(vm.Bic) ? null : vm.Bic,
UstIdNr = string.IsNullOrEmpty(vm.UstIdNr) ? null : vm.UstIdNr,
LfbisNr = string.IsNullOrEmpty(vm.LfbisNr) ? null : vm.LfbisNr,
UstIdNr = string.IsNullOrWhiteSpace(vm.UstIdNr) ? null : vm.UstIdNr,
LfbisNr = string.IsNullOrWhiteSpace(vm.LfbisNr) ? null : vm.LfbisNr,
IsBuchführend = vm.IsBuchführend,
IsOrganic = vm.IsOrganic,
EntryDateString = string.IsNullOrEmpty(vm.EntryDate) ? null : string.Join("-", vm.EntryDate.Split(".").Reverse()),
ExitDateString = string.IsNullOrEmpty(vm.ExitDate) ? null : string.Join("-", vm.ExitDate.Split(".").Reverse()),
BusinessShares = (int)vm.BusinessShares!,
AccountingNr = string.IsNullOrEmpty(vm.AccountingNr) ? null : vm.AccountingNr,
EntryDateString = string.IsNullOrWhiteSpace(vm.EntryDate) ? null : string.Join("-", vm.EntryDate.Split(".").Reverse()),
ExitDateString = string.IsNullOrWhiteSpace(vm.ExitDate) ? null : string.Join("-", vm.ExitDate.Split(".").Reverse()),
Shares = App.Client.HasRedWhite ? 0 : vm.BusinessShares1 ?? 0,
SharesRed = App.Client.HasRedWhite ? vm.BusinessShares1 ?? 0 : 0,
SharesWhite = App.Client.HasRedWhite ? vm.BusinessShares2 ?? 0 : 0,
SharesDormant = App.Client.HasRedWhite ? vm.BusinessShares3 ?? 0 : vm.BusinessShares2 ?? 0,
AccountingNr = string.IsNullOrWhiteSpace(vm.AccountingNr) ? null : vm.AccountingNr,
IsActive = vm.IsActive,
IsVollLieferant = vm.IsVollLieferant,
IsFunktionär = vm.IsFunktionär,
ZwstId = vm.Branch?.ZwstId,
DefaultKgNr = vm.DefaultKg?.KgNr,
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
Comment = string.IsNullOrWhiteSpace(vm.Comment) ? null : vm.Comment,
ContactViaPost = vm.ContactViaPost,
ContactViaEmail = vm.ContactViaEmail,
};
+65 -76
View File
@@ -6,13 +6,12 @@ using Elwig.Models.Dtos;
using Elwig.Models.Entities;
using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace Elwig.Services {
public static class PaymentVariantService {
@@ -142,7 +141,7 @@ namespace Elwig.Services {
});
var credits = ctx.Credits.Where(c => c.Year == v.Year && c.AvNr == v.AvNr);
if (!credits.Any()) {
if (!(await credits.AnyAsync())) {
long lastTotalSum = 0;
decimal vatSum = 0;
var currentPayments = await ctx.MemberPayments
@@ -150,8 +149,7 @@ namespace Elwig.Services {
.ToDictionaryAsync(p => p.MgNr);
var lastV = await ctx.PaymentVariants
.Where(l => l.Year == v.Year && !l.TestVariant)
.OrderByDescending(l => l.TransferDateString ?? l.DateString)
.ThenByDescending(l => l.AvNr)
.OrderByDescending(l => l.TransferDateString ?? l.DateString).ThenByDescending(l => l.AvNr)
.FirstOrDefaultAsync();
if (lastV != null) {
var lastPayments = await ctx.MemberPayments
@@ -160,8 +158,8 @@ namespace Elwig.Services {
lastTotalSum = lastPayments.Sum(e => e.Value.AmountValue);
foreach (int mgnr in currentPayments.Keys) {
var c = currentPayments[mgnr];
var l = lastPayments[mgnr];
vatSum += (c.Amount - l.Amount) * (decimal)(c.Member.IsBuchführend ? v.Season.VatNormal : v.Season.VatFlatrate);
var l = lastPayments.GetValueOrDefault(mgnr);
vatSum += (c.Amount - (l?.Amount ?? 0)) * (decimal)(c.Member.IsBuchführend ? v.Season.VatNormal : v.Season.VatFlatrate);
}
} else {
vatSum = currentPayments.Sum(e => e.Value.Amount * (decimal)(e.Value.Member.IsBuchführend ? v.Season.VatNormal : v.Season.VatFlatrate));
@@ -190,97 +188,58 @@ namespace Elwig.Services {
public static async Task GenerateSummary(PaymentVar v, ExportMode mode) {
if (mode == ExportMode.SaveList) {
var d = new SaveFileDialog() {
FileName = $"Variantendaten-{v.Name.Trim().Replace(' ', '-')}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"Variantendaten {v.Name} speichern unter - Elwig"
};
if (d.ShowDialog() == false)
var filename = InteractionService.SaveFile($"Variantendaten {v.Name}", $"Variantendaten-{v.Name.Trim().Replace(' ', '-')}", "ods");
if (filename == null)
return;
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
using var ctx = new AppDbContext();
var data = await PaymentVariantSummaryData.ForPaymentVariant(v, ctx.PaymentVariantSummaryRows);
using var ods = new OdsFile(d.FileName);
await ods.AddTable(data);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
await Utils.RunForeground(async () => {
using var ctx = new AppDbContext();
var data = await PaymentVariantSummaryData.ForPaymentVariant(v, ctx.PaymentVariantSummaryRows);
using var ods = new OdsFile(filename);
await ods.AddTable(data);
});
Mouse.OverrideCursor = null;
} else {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
using var doc = await PaymentVariantSummary.Initialize(v.Year, v.AvNr);
await Utils.ExportDocument(doc, mode);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
await Utils.RunForeground(async () => {
using var doc = await PaymentVariantSummary.Initialize(v.Year, v.AvNr);
await Utils.ExportDocument(doc, mode);
});
Mouse.OverrideCursor = null;
}
}
public static async Task GenerateEbics(int year, int avnr) {
using var ctx = new AppDbContext();
var v = (await ctx.PaymentVariants.FindAsync(year, avnr))!;
var v = await ctx.PaymentVariants.Include(v => v.Credits).Where(v => v.Year == year && v.AvNr == avnr).SingleAsync();
var withoutIban = v.Credits.Count(c => c.Member.Iban == null);
if (withoutIban > 0) {
var r = MessageBox.Show($"Achtung: Für {withoutIban:N0} Mitglieder ist kein IBAN hinterlegt.\n\nDiese werden NICHT exportiert.",
"Mitglieder ohne IBAN", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (r != MessageBoxResult.OK) return;
if (!InteractionService.AskContinue("Mitglieder ohne IBAN",
$"Achtung: Für {withoutIban:N0} Mitglieder ist kein IBAN hinterlegt.\n\nDiese werden NICHT exportiert."))
return;
}
var withNegAmount = v.Credits.Count(c => c.Amount <= 0);
if (withNegAmount > 0) {
var r = MessageBox.Show($"Achtung: Es gibt {withNegAmount:N0} Traubengutschriften mit negativem Betrag.\n\nDiese werden NICHT exportiert.",
"Traubengutschriften mit negativem Betrag", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.OK);
if (r != MessageBoxResult.OK) return;
if (!InteractionService.AskContinue("Traubengutschriften mit negativem Betrag",
$"Achtung: Es gibt {withNegAmount:N0} Traubengutschriften mit negativem Betrag.\n\nDiese werden NICHT exportiert."))
return;
}
var d = new SaveFileDialog() {
FileName = $"{App.Client.NameToken}-Überweisungsdaten-{v.Year}-{v.Name.Trim().Replace(' ', '-')}.{Ebics.FileExtension}",
DefaultExt = Ebics.FileExtension,
Filter = "EBICS-Datei (*.xml)|*.xml",
Title = $"Überweisungsdaten speichern unter - Elwig",
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
using var e = new Ebics(v, d.FileName, App.Client.ExportEbicsVersion, (Ebics.AddressMode)App.Client.ExportEbicsAddress);
await e.ExportAsync(Transaction.FromPaymentVariant(v));
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
var filename = InteractionService.SaveFile("Überweisungsdaten", $"{App.Client.NameToken}-Überweisungsdaten-{v.Year}-{v.Name.Trim().Replace(' ', '-')}", Ebics.FileExtension);
if (filename != null) {
await Utils.RunForeground(async () => {
using var e = new Ebics(v, filename, App.Client.ExportEbicsVersion, (Ebics.AddressMode)App.Client.ExportEbicsAddress);
await e.ExportAsync(Transaction.FromPaymentVariant(v));
});
Mouse.OverrideCursor = null;
}
}
public static async Task GenerateAccountingList(int year, int avnr, string name) {
var d = new SaveFileDialog() {
FileName = $"{App.Client.NameToken}-Buchungsliste-{year}-{name.Trim().Replace(' ', '-')}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"Buchungsliste speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
using var ctx = new AppDbContext();
var tbl = await CreditNoteData.ForPaymentVariant(ctx, year, avnr);
using var ods = new OdsFile(d.FileName);
await ods.AddTable(tbl);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
var filename = InteractionService.SaveFile("Buchungsliste", $"{App.Client.NameToken}-Buchungsliste-{year}-{name.Trim().Replace(' ', '-')}", "ods");
if (filename != null) {
await Utils.RunForeground(async () => {
using var ctx = new AppDbContext();
var tbl = await CreditNoteData.ForPaymentVariant(ctx, year, avnr);
using var ods = new OdsFile(filename);
await ods.AddTable(tbl);
});
Mouse.OverrideCursor = null;
}
}
@@ -341,7 +300,7 @@ namespace Elwig.Services {
TransferDate = vm.TransferDate,
TestVariant = vm.SelectedPaymentVariant?.TestVariant ?? true,
CalcTimeUnix = vm.SelectedPaymentVariant?.CalcTimeUnix,
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
Comment = string.IsNullOrWhiteSpace(vm.Comment) ? null : vm.Comment,
Data = JsonSerializer.Serialize(d.Data),
};
avnr = v.AvNr;
@@ -369,10 +328,40 @@ namespace Elwig.Services {
}
public static async Task Commit(int year, int avnr) {
List<Member> membersLess = [];
using (var ctx = new AppDbContext()) {
var lastAvNr = await ctx.PaymentVariants
.Where(v => v.Year == year && !v.TestVariant)
.OrderByDescending(v => v.TransferDateString ?? v.DateString).ThenByDescending(v => v.AvNr)
.Select(v => v.AvNr)
.FirstOrDefaultAsync();
if (lastAvNr != 0) {
var cur = await ctx.MemberPayments.Include(p => p.Member).Where(p => p.Year == year && p.AvNr == avnr).ToDictionaryAsync(p => p.Member.MgNr, p => p);
var last = await ctx.MemberPayments.Include(p => p.Member).Where(p => p.Year == year && p.AvNr == lastAvNr).ToDictionaryAsync(p => p.Member.MgNr, p => p);
membersLess = [.. last.Where(p => (cur.TryGetValue(p.Key, out var c) ? c.Amount : 0) < p.Value.Amount).Select(p => p.Value.Member)];
}
}
if (membersLess.Count > 0) {
if (!InteractionService.AskContinue("Kleinerer Gesamtbetrag",
$"Achtung: Bei {membersLess.Count:N0} Mitglied(ern) ist der Soll-Betrag der Auszahlung (\"Gesamtbetrag\") kleiner als bei der letzten Auszahlung. Das sollte nicht vorkommen!\n\n" +
$"{string.Join(", ", membersLess.Select(m => $"{m.AdministrativeName} ({m.MgNr})"))}\n\n" +
"Soll trotzdem forgefahren werden?"))
return;
}
await Task.Run(async () => {
var b = await BillingVariant.Create(year, avnr);
await b.Commit();
});
List<Member> membersNeg = [];
using (var ctx = new AppDbContext()) {
membersNeg = await ctx.Credits.Where(c => c.Year == year && c.AvNr == avnr && c.AmountValue < 0).Select(c => c.Member).ToListAsync();
}
if (membersNeg.Count > 0) {
InteractionService.ShowWarning("Negativer Auszahlungsbetrag", $"Achtung: Bei {membersNeg.Count:N0} Mitglied(ern) ist der Auszahlungsbetrag negativ!\n\n" +
$"{string.Join(", ", membersNeg.Select(m => $"{m.AdministrativeName} ({m.MgNr})"))}");
}
}
public static async Task Revert(int year, int avnr) {
+45 -35
View File
@@ -10,7 +10,6 @@ using System.Linq;
using System.Linq.Expressions;
using System.Net.Http;
using System.Threading.Tasks;
using System.Windows;
namespace Elwig.Services {
public static class SyncService {
@@ -28,6 +27,16 @@ namespace Elwig.Services {
.Include(m => m.TelephoneNumbers)
.Include(m => m.EmailAddresses)
.ToListAsync();
var history1 = await query.IgnoreAutoIncludes()
.SelectMany(m => m.HistoryFrom)
.ToListAsync();
var history2 = await query.IgnoreAutoIncludes()
.SelectMany(m => m.HistoryTo)
.ToListAsync();
var history = history1.Union(history2)
.DistinctBy(h => h.HistNr)
.OrderBy(h => h.DateNotice).ThenBy(h => h.HistNr)
.ToList();
var areaComs = await query
.SelectMany(m => m.AreaCommitments)
.Select(c => c.Contract).Distinct()
@@ -42,22 +51,20 @@ namespace Elwig.Services {
.OrderBy(k => k.KgNr)
.ToList();
if (members.Count == 0) {
MessageBox.Show("Es wurden keine Mitglieder zum Hochladen ausgewählt!", "Mitglieder hochladen",
MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Mitglieder hochladen", "Es wurden keine Mitglieder zum Hochladen ausgewählt!");
} else {
var exportedAt = DateTime.Now;
await ElwigData.Export(path, members, areaComs, wbKgs, filterNames);
await ElwigData.Export(path, members, history, areaComs, wbKgs, filterNames);
await Utils.UploadExportData(path, url, username, password);
await UpdateExportedAt(members, areaComs, [], exportedAt);
MessageBox.Show($"Hochladen von {members.Count:N0} Mitgliedern erfolgreich!", "Mitglieder hochgeladen",
MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Mitglieder hochgeladen", $"Hochladen von {members.Count:N0} Mitgliedern erfolgreich!");
}
} catch (HttpRequestException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Mitglieder hochladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (TaskCanceledException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Mitglieder hochladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Mitglieder hochladen", exc);
}
}
@@ -78,22 +85,20 @@ namespace Elwig.Services {
.OrderBy(k => k.KgNr)
.ToList();
if (list.Count == 0) {
MessageBox.Show("Es wurden keine Lieferungen zum Hochladen ausgewählt!", "Lieferungen hochladen",
MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Lieferungen hochladen", "Es wurden keine Lieferungen zum Hochladen ausgewählt!");
} else {
var exportedAt = DateTime.Now;
await ElwigData.Export(path, list, wbKgs, filterNames);
await Utils.UploadExportData(path, url, username, password);
await UpdateExportedAt([], [], list, exportedAt);
MessageBox.Show($"Hochladen von {list.Count:N0} Lieferungen erfolgreich!", "Lieferungen hochgeladen",
MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Lieferungen hochgeladen", $"Hochladen von {list.Count:N0} Lieferungen erfolgreich!");
}
} catch (HttpRequestException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Lieferungen hochladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (TaskCanceledException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Lieferungen hochladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Lieferungen hochladen", exc);
}
}
@@ -132,8 +137,7 @@ namespace Elwig.Services {
.OrderBy(k => k.KgNr)
.ToList();
if (members.Count == 0 && deliveries.Count == 0) {
MessageBox.Show("Es gibt keine geänderten Mitglieder oder Lieferungen, die hochgeladen werden könnten!", "Mitglieder und Lieferungen hochladen",
MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Mitglieder und Lieferungen hochladen", "Es gibt keine geänderten Mitglieder oder Lieferungen, die hochgeladen werden könnten!");
} else {
var exportedAt = DateTime.Now;
await (new ElwigData.ElwigExport {
@@ -144,15 +148,14 @@ namespace Elwig.Services {
}).Export(path);
await Utils.UploadExportData(path, url, username, password);
await UpdateExportedAt(members, areaComs, deliveries, exportedAt);
MessageBox.Show($"Hochladen von {members.Count:N0} Mitgliedern, {areaComs.Count:N0} Flächenbindungsverträgen, und {deliveries.Count:N0} Lieferungen erfolgreich!", "Mitglieder und Lieferungen hochladen",
MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Mitglieder und Lieferungen hochladen", $"Hochladen von {members.Count:N0} Mitgliedern, {areaComs.Count:N0} Flächenbindungsverträgen, und {deliveries.Count:N0} Lieferungen erfolgreich!");
}
} catch (HttpRequestException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder und Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Mitglieder und Lieferungen hochladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (TaskCanceledException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder und Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Mitglieder und Lieferungen hochladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Mitglieder und Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Mitglieder und Lieferungen hochladen", exc);
}
}
@@ -173,22 +176,20 @@ namespace Elwig.Services {
.DistinctBy(k => k.KgNr)
.ToList();
if (deliveries.Count == 0) {
MessageBox.Show("Es gibt keine Lieferungen, die hochgeladen werden können!", "Lieferungen hochladen",
MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Lieferungen hochladen", "Es gibt keine Lieferungen, die hochgeladen werden können!");
} else {
var exportedAt = DateTime.Now;
await ElwigData.Export(path, deliveries, wbKgs, [$"{year}", $"Zweigstelle {App.BranchName}"]);
await Utils.UploadExportData(path, url, username, password);
await UpdateExportedAt([], [], deliveries, exportedAt);
MessageBox.Show($"Hochladen von {deliveries.Count:N0} Lieferungen erfolgreich!", "Lieferungen hochladen",
MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Lieferungen hochladen", $"Hochladen von {deliveries.Count:N0} Lieferungen erfolgreich!");
}
} catch (HttpRequestException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Lieferungen hochladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (TaskCanceledException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Lieferungen hochladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Lieferungen hochladen", exc);
}
}
@@ -213,7 +214,7 @@ namespace Elwig.Services {
];
}
public static async Task Download(string url, string username, string password) {
public static async Task Download(string url, string username, string password, bool confirmImports) {
try {
var import = await GetFilesToImport(url, username, password);
var paths = new List<string>();
@@ -225,13 +226,13 @@ namespace Elwig.Services {
paths.Add(filename);
}
}
await ElwigData.Import(paths, ElwigData.ImportMode.FromBranches);
await ElwigData.Import(paths, confirmImports ? ElwigData.ImportMode.Interactively : ElwigData.ImportMode.FromBranches);
} catch (HttpRequestException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Daten herunterladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Daten herunterladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (TaskCanceledException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Daten herunterladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Daten herunterladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Daten herunterladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Daten herunterladen", exc);
}
}
@@ -263,5 +264,14 @@ namespace Elwig.Services {
return false;
}
}
public static async Task<bool> ChangesAvailable(AppDbContext ctx, string url, string username, string password, int? year = null) {
try {
year ??= Utils.CurrentLastSeason;
return await ctx.Deliveries.Where(d => d.ZwstId == App.ZwstId && d.Year == year).Where(ChangedDeliveries).AnyAsync() || (Utils.HasInternetConnectivity() && (await GetFilesToImport(url, username, password)).Count > 0);
} catch {
return false;
}
}
}
}
+1 -1
View File
@@ -25,7 +25,7 @@ namespace Elwig.ViewModels {
[ObservableProperty]
private string? _manualWeighingReason;
[ObservableProperty]
private string? _scaleId;
private string? _scaleTerminalId;
[ObservableProperty]
private string? _weighingData;
+18 -7
View File
@@ -6,6 +6,7 @@ using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
namespace Elwig.ViewModels {
public partial class MemberAdminViewModel : ObservableObject {
@@ -124,10 +125,22 @@ namespace Elwig.ViewModels {
[ObservableProperty]
private string? _exitDate;
[ObservableProperty]
private string? _businessSharesString;
public int? BusinessShares {
get => int.TryParse(BusinessSharesString, out var bs) ? bs : null;
set => BusinessSharesString = $"{value}";
private string? _businessShares1String;
public int? BusinessShares1 {
get => int.TryParse(BusinessShares1String, out var shares) ? shares : null;
set => BusinessShares1String = $"{value}";
}
[ObservableProperty]
private string? _businessShares2String;
public int? BusinessShares2 {
get => int.TryParse(BusinessShares2String, out var shares) ? shares : null;
set => BusinessShares2String = $"{value}";
}
[ObservableProperty]
private string? _businessShares3String;
public int? BusinessShares3 {
get => int.TryParse(BusinessShares3String, out var shares) ? shares : null;
set => BusinessShares3String = $"{value}";
}
[ObservableProperty]
private string? _accountingNr;
@@ -188,9 +201,7 @@ namespace Elwig.ViewModels {
[ObservableProperty]
private string? _statusMembersToolTip;
[ObservableProperty]
private string _statusBusinessShares = "-";
[ObservableProperty]
private string? _statusBusinessSharesToolTip;
private TextBlock? _statusBusinessShares;
[ObservableProperty]
private string _statusDeliveriesLastSeason = "-";
[ObservableProperty]
@@ -0,0 +1,76 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Elwig.Models.Entities;
using System;
using System.Collections.Generic;
using System.Windows;
namespace Elwig.ViewModels {
public partial class MemberBusinessSharesViewModel : ObservableObject {
[ObservableProperty]
private string _title = "Geschäftsanteilbewegungen - Elwig";
public required Member FilterMember { get; set; }
[ObservableProperty]
private MemberHistory? _selectedHistoryEntry;
[ObservableProperty]
private IEnumerable<MemberHistory> _history = [];
[ObservableProperty]
private string? _dateNoticeString;
public DateOnly? DateNotice {
get => DateOnly.TryParseExact(DateNoticeString, "dd.MM.yyyy", out var d) ? d : null;
set => DateNoticeString = $"{value:dd.MM.yyyy}";
}
[ObservableProperty]
private string? _dateEffectiveString;
public DateOnly? DateEffective {
get => DateOnly.TryParseExact(DateEffectiveString, "dd.MM.yyyy", out var d) ? d : null;
set => DateEffectiveString = $"{value:dd.MM.yyyy}";
}
[ObservableProperty]
private string? _reason;
[ObservableProperty]
private string? _sharesString;
public int? Shares {
get => int.TryParse(SharesString, out var shares) ? shares : null;
set => SharesString = $"{value}";
}
[ObservableProperty]
private string? _otherMgNrString;
public int? OtherMgNr {
get => int.TryParse(OtherMgNrString, out var mgnr) ? mgnr : null;
set => OtherMgNrString = $"{value}";
}
[ObservableProperty]
private string? _deductYearString;
public int? DeductYear {
get => int.TryParse(DeductYearString, out var year) ? year : null;
set => DeductYearString = $"{value}";
}
[ObservableProperty]
private bool _deduct;
[ObservableProperty]
private bool _memberHasSigned;
[ObservableProperty]
private string? _valuePerShareString;
public decimal? ValuePerShare {
get => decimal.TryParse(ValuePerShareString, out var v) ? v : null;
set => ValuePerShareString = $"{value:0.00}";
}
[ObservableProperty]
private string? _totalAmount;
[ObservableProperty]
private string? _comment;
[ObservableProperty]
private Visibility _controlButtonsVisibility = Visibility.Visible;
[ObservableProperty]
private Visibility _editingButtonsVisibility = Visibility.Hidden;
[ObservableProperty]
private Visibility _personalNameVisibility = Visibility.Visible;
[ObservableProperty]
private Visibility _juridicalNameVisibility = Visibility.Hidden;
}
}
+1 -1
View File
@@ -15,7 +15,7 @@
<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>Quellcode:</Bold> <Hyperlink NavigateUri="C:\Program Files\Elwig\src" RequestNavigate="Hyperlink_RequestNavigate_Explorer">C:\Program Files\Elwig\src</Hyperlink>, <Hyperlink NavigateUri="https://git.necronda.net/winzer/elwig" RequestNavigate="Hyperlink_RequestNavigate">https://git.necronda.net/winzer/elwig</Hyperlink><LineBreak/>
<Bold>Entwicklungszeitraum:</Bold> 20222026<LineBreak/>
<LineBreak/>
<Bold>Verwendete Technologien:</Bold><LineBreak/>
+9 -1
View File
@@ -11,7 +11,15 @@ namespace Elwig.Windows {
}
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e) {
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) { UseShellExecute = true });
try {
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) { UseShellExecute = true });
} catch { }
}
private void Hyperlink_RequestNavigate_Explorer(object sender, RequestNavigateEventArgs e) {
try {
Process.Start("explorer.exe", e.Uri.AbsoluteUri);
} catch { }
}
}
}
+12 -8
View File
@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Elwig.Services;
namespace Elwig.Windows {
public abstract class AdministrationWindow : ContextWindow {
@@ -109,9 +110,7 @@ namespace Elwig.Windows {
private void OnClosing(object? sender, CancelEventArgs evt) {
if ((IsCreating || IsEditing) && HasChanged) {
var r = MessageBox.Show("Soll das Fenster wirklich geschlossen werden?", "Schließen bestätigen",
MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r != MessageBoxResult.Yes) {
if (!InteractionService.AskConfirmation("Schließen bestätigen", "Soll das Fenster wirklich geschlossen werden?")) {
evt.Cancel = true;
return;
}
@@ -165,10 +164,10 @@ namespace Elwig.Windows {
} else if (input is TextBox tb && tb.Text.Length == 0) {
ControlUtils.SetInputInvalid(input);
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.HasItems) {
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.HasItems) {
ControlUtils.SetInputInvalid(input);
Valid[input] = false;
} else if (input is CheckBox ckb && ((ckb.IsThreeState && ckb.IsChecked == null) || (!ckb.IsThreeState && ckb.IsChecked != true))) {
@@ -286,8 +285,13 @@ namespace Elwig.Windows {
var binding = cb.GetBindingExpression(ComboBox.SelectedItemProperty);
binding?.UpdateSource();
}
foreach (var lb in ListBoxInputs)
lb.SelectedItems.Clear();
foreach (var lb in ListBoxInputs) {
if (lb.SelectionMode == SelectionMode.Single) {
lb.SelectedItem = null;
} else {
lb.SelectedItems.Clear();
}
}
foreach (var cb in CheckBoxInputs) {
cb.IsChecked = cb.IsThreeState ? null : false;
var binding = cb.GetBindingExpression(CheckBox.IsCheckedProperty);
@@ -390,7 +394,7 @@ namespace Elwig.Windows {
protected bool InputLostFocus(TextBox input, ValidationResult res, string? msg = null) {
if (DoShowWarningWindows && !res.IsValid && !IsClosing && (IsEditing || IsCreating))
MessageBox.Show(res.ErrorContent?.ToString(), msg ?? res.ErrorContent?.ToString(), MessageBoxButton.OK, MessageBoxImage.Warning);
InteractionService.ShowWarning(msg ?? res.ErrorContent?.ToString() ?? "", res.ErrorContent?.ToString() ?? "");
return res.IsValid;
}
+2 -6
View File
@@ -246,9 +246,7 @@ namespace Elwig.Windows {
}
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Flächenbindung löschen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Flächenbindung löschen", exc);
}
Mouse.OverrideCursor = null;
}
@@ -298,9 +296,7 @@ namespace Elwig.Windows {
}
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Flächenbindung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Flächenbindung aktualisieren", exc);
SaveButton.IsEnabled = true;
Mouse.OverrideCursor = null;
return;
+20 -5
View File
@@ -453,13 +453,13 @@
HorizontalAlignment="Left"/>
<Label Content="Lieferpflicht/-recht:" Margin="10,10,0,10" Grid.Column="2"/>
<ctrl:UnitTextBox x:Name="SeasonMinKgPerBsInput" Unit="kg/GA" TextChanged="SeasonMinMaxKgInput_TextChanged"
<ctrl:UnitTextBox x:Name="SeasonMinKgPerShareInput" Unit="kg/GA" TextChanged="SeasonMinMaxKgInput_TextChanged"
Grid.Column="3" Width="80" Margin="0,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ctrl:UnitTextBox x:Name="SeasonMaxKgPerBsInput" Unit="kg/GA" TextChanged="SeasonMinMaxKgInput_TextChanged"
<ctrl:UnitTextBox x:Name="SeasonMaxKgPerShareInput" Unit="kg/GA" TextChanged="SeasonMinMaxKgInput_TextChanged"
Grid.Column="3" Width="80" Margin="85,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Label Content="GA-Wert (Nachz.):" Margin="10,40,0,10" Grid.Column="2"/>
<ctrl:UnitTextBox x:Name="SeasonBsValueInput" Unit="€/GA" TextChanged="SeasonPenaltyInput_TextChanged"
<ctrl:UnitTextBox x:Name="SeasonShareValueInput" Unit="€/GA" TextChanged="SeasonPenaltyInput_TextChanged"
Grid.Column="3" Width="85" Margin="0,40,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<GroupBox Header="Strafen bei Unterlieferung lt. GA" Grid.Column="2" Grid.ColumnSpan="2" Margin="0,70,10,0">
@@ -468,13 +468,13 @@
Width="80" Margin="65,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ctrl:UnitTextBox x:Name="SeasonPenaltyInput" Unit="€" TextChanged="SeasonPenaltyInput_TextChanged"
Width="68" Margin="150,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ctrl:UnitTextBox x:Name="SeasonPenaltyPerBsInput" Unit="€/GA" TextChanged="SeasonPenaltyPerBsInput_TextChanged"
<ctrl:UnitTextBox x:Name="SeasonPenaltyPerShareInput" Unit="€/GA" TextChanged="SeasonPenaltyPerShareInput_TextChanged"
Width="100" Margin="222,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Label Content="Zzgl. bei Nicht-Lieferung:" Margin="10,40,0,10"/>
<ctrl:UnitTextBox x:Name="SeasonPenaltyNoneInput" Unit="€" TextChanged="SeasonPenaltyInput_TextChanged"
Width="68" Margin="150,40,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ctrl:UnitTextBox x:Name="SeasonPenaltyPerBsNoneInput" Unit="€/GA" TextChanged="SeasonPenaltyPerBsInput_TextChanged"
<ctrl:UnitTextBox x:Name="SeasonPenaltyPerShareNoneInput" Unit="€/GA" TextChanged="SeasonPenaltyPerShareInput_TextChanged"
Width="100" Margin="222,40,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</Grid>
</GroupBox>
@@ -541,6 +541,21 @@
</TabItem>
<TabItem Header="Parameter">
<StackPanel>
<GroupBox x:Name="ParameterBusinessShare" Header="Geschäftsanteile" Margin="10,10,10,0" VerticalAlignment="Top">
<Grid>
<Label Content="Modus:" Margin="10,10,10,10"/>
<ComboBox x:Name="ParameterBusinessShareModeInput" Margin="160,10,10,10" Width="100"
HorizontalAlignment="Left">
<ComboBoxItem IsSelected="True">Normal</ComboBoxItem>
<ComboBoxItem>Rot/weiß</ComboBoxItem>
</ComboBox>
<Label Content="Mitgliederbewegungen:" Margin="10,40,10,10"/>
<CheckBox x:Name="ParameterBusinessShareHistoryInput" Content="Mit Elwig erfassen" Margin="160,45,10,15"
HorizontalAlignment="Left"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
</Grid>
</GroupBox>
<GroupBox x:Name="ParameterAreaComGroup" Header="Berechnung Flächenbindungen (aktuelle Saison)" Margin="10,10,10,0" VerticalAlignment="Top">
<Grid>
<CheckBox x:Name="ParameterAllowAttrIntoLowerInput" Content="Erlauben Lieferungen auch auf (konfigurierte) &quot;schlechtere&quot; Flächenbindungen aufzuteilen"
+17 -17
View File
@@ -47,26 +47,26 @@ namespace Elwig.Windows {
if (!_actChanged || _actList == null || _acts == null || _actIds == null)
return;
foreach (var (vtrgid, _) in _acts.Where(a => a.Value == null)) {
ctx.Remove(ctx.AreaCommitmentTypes.Find(vtrgid)!);
}
foreach (var (attr, old) in _actIds) {
attr.VtrgId = old;
}
foreach (var (old, vtrgid) in _acts.Where(a => a.Value != null)) {
ctx.Update(ctx.AreaCommitmentTypes.Find(old)!);
}
await ctx.SaveChangesAsync();
// delete
var delete = _acts.Where(a => a.Value == null).Select(a => a.Key).ToList();
await ctx.AreaCommitmentTypes.Where(v => delete.Contains(v.VtrgId)).ExecuteDeleteAsync();
foreach (var (old, vtrgid) in _acts.Where(a => a.Value != null)) {
// update
ctx.UpdateRange(_actList
.Where(_actIds.ContainsKey)
.Select(t => {
t.VtrgId = _actIds[t];
ctx.Entry(t).State = EntityState.Added;
return t;
}));
await ctx.SaveChangesAsync();
foreach (var (old, vtrgid) in _acts.Where(a => a.Value != null && a.Key != a.Value)) {
await ctx.Database.ExecuteSqlAsync($"UPDATE area_commitment_type SET vtrgid = {vtrgid} WHERE vtrgid = {old}");
}
await ctx.SaveChangesAsync();
foreach (var type in _actList.Where(a => !_actIds.ContainsKey(a))) {
if (type.VtrgId == null) continue;
ctx.Add(type);
}
// insert
ctx.AddRange(_actList
.Where(t => !_actIds.ContainsKey(t) && t.VtrgId != null));
await ctx.SaveChangesAsync();
}
@@ -125,7 +125,7 @@ namespace Elwig.Windows {
var id = AreaCommitmentTypeIdInput.Text;
if (old != null) _acts[old] = id;
attr.VtrgId = id;
attr.SortId = v?.SortId;
attr.SortId = v?.SortId ?? "";
attr.AttrId = a?.AttrId;
attr.MinKgPerHa = AreaCommitmentTypeMinKgPerHaInput.Text.Length > 0 ? int.Parse(AreaCommitmentTypeMinKgPerHaInput.Text) : null;
attr.PenaltyPerKg = AreaCommitmentTypePenaltyPerKgInput.Text.Length > 0 ? decimal.Parse(AreaCommitmentTypePenaltyPerKgInput.Text) : null;
+13 -14
View File
@@ -41,28 +41,27 @@ namespace Elwig.Windows {
if (!_branchChanged || _branchList == null || _branches == null || _branchIds == null)
return;
var tx = await ctx.Database.BeginTransactionAsync();
// delete
var deleteZwstIds = _branches.Where(b => b.Value == null).Select(b => b.Key).ToList();
await ctx.Branches.Where(b => deleteZwstIds.Contains(b.ZwstId)).ExecuteDeleteAsync();
foreach (var (branch, old) in _branchIds) {
branch.ZwstId = old;
}
foreach (var (old, zwstid) in _branches.Where(b => b.Value != null)) {
ctx.Update(ctx.Branches.Find(old)!);
}
// update
ctx.UpdateRange(_branchList
.Where(_branchIds.ContainsKey)
.Select(b => {
b.ZwstId = _branchIds[b];
ctx.Entry(b).State = EntityState.Added;
return b;
}));
await ctx.SaveChangesAsync();
foreach (var (old, zwstid) in _branches.Where(b => b.Value != null)) {
foreach (var (old, zwstid) in _branches.Where(b => b.Value != null && b.Key != b.Value)) {
await ctx.Database.ExecuteSqlAsync($"UPDATE branch SET zwstid = {zwstid} WHERE zwstid = {old}");
}
foreach (var branch in _branchList.Where(b => !_branchIds.ContainsKey(b))) {
if (branch.ZwstId == null) continue;
ctx.Add(branch);
}
// insert
ctx.AddRange(_branchList
.Where(b => !_branchIds.ContainsKey(b) && b.ZwstId != null));
await ctx.SaveChangesAsync();
await tx.CommitAsync();
}
private void BranchList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
+16 -16
View File
@@ -48,30 +48,30 @@ namespace Elwig.Windows {
if (!_modChanged || _modList == null || _mods == null || _modIds == null)
return;
var year = (SeasonList.SelectedItem as Season)?.Year;
int i = 0;
foreach (var mod in _modList) mod.Ordering = ++i;
var year = (SeasonList.SelectedItem as Season)?.Year;
foreach (var (modid, _) in _mods.Where(m => m.Value == null)) {
ctx.Remove(ctx.Modifiers.Find(year, modid)!);
}
foreach (var (mod, old) in _modIds) {
mod.ModId = old;
}
foreach (var (old, modid) in _mods.Where(m => m.Value != null)) {
ctx.Update(ctx.Modifiers.Find(year, old)!);
}
await ctx.SaveChangesAsync();
// delete
var deleteModIds = _mods.Where(b => b.Value == null).Select(b => b.Key).ToList();
await ctx.Modifiers.Where(b => b.Year == year && deleteModIds.Contains(b.ModId)).ExecuteDeleteAsync();
// update
ctx.UpdateRange(_modList
.Where(_modIds.ContainsKey)
.Select(m => {
m.ModId = _modIds[m];
ctx.Entry(m).State = EntityState.Added;
return m;
}));
await ctx.SaveChangesAsync();
foreach (var (old, modid) in _mods.Where(m => m.Value != null)) {
await ctx.Database.ExecuteSqlAsync($"UPDATE modifier SET modid = {modid} WHERE (year, modid) = ({year}, {old})");
}
await ctx.SaveChangesAsync();
foreach (var mod in _modList.Where(m => !_modIds.ContainsKey(m))) {
if (mod.ModId == null) continue;
await ctx.AddAsync(mod);
}
// insert
ctx.AddRange(_modList
.Where(m => !_modIds.ContainsKey(m) && m.ModId != null));
await ctx.SaveChangesAsync();
}
+42 -35
View File
@@ -1,6 +1,7 @@
using Elwig.Dialogs;
using Elwig.Helpers;
using Elwig.Models.Entities;
using Elwig.Services;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
@@ -46,23 +47,23 @@ namespace Elwig.Windows {
SeasonVatFlatrateInput.Text = (s.VatFlatrate * 100).ToString();
SeasonStartInput.Text = $"{s.StartDate:dd.MM.yyyy}";
SeasonEndInput.Text = $"{s.EndDate:dd.MM.yyyy}";
SeasonMinKgPerBsInput.Text = s.MinKgPerBusinessShare.ToString();
SeasonMaxKgPerBsInput.Text = s.MaxKgPerBusinessShare.ToString();
SeasonMinKgPerShareInput.Text = s.MinKgPerShare?.ToString() ?? s.MinKgPerShareRed?.ToString() ?? s.MinKgPerShareWhite?.ToString() ?? "0";
SeasonMaxKgPerShareInput.Text = s.MaxKgPerShare?.ToString() ?? s.MaxKgPerShareRed?.ToString() ?? s.MaxKgPerShareWhite?.ToString() ?? "0";
SeasonPenaltyPerKgInput.Text = s.PenaltyPerKg?.ToString() ?? "";
SeasonPenaltyInput.Text = s.PenaltyAmount?.ToString() ?? "";
SeasonPenaltyNoneInput.Text = s.PenaltyNone?.ToString() ?? "";
SeasonPenaltyPerBsInput.Text = s.PenaltyPerBsAmount?.ToString() ?? "";
SeasonPenaltyPerBsNoneInput.Text = s.PenaltyPerBsNone?.ToString() ?? "";
SeasonBsValueInput.Text = s.BusinessShareValue?.ToString() ?? "";
SeasonPenaltyPerShareInput.Text = s.PenaltyPerShareAmount?.ToString() ?? "";
SeasonPenaltyPerShareNoneInput.Text = s.PenaltyPerShareNone?.ToString() ?? "";
SeasonShareValueInput.Text = s.BusinessShareValue?.ToString() ?? "";
var sym = s.Currency.Symbol ?? s.Currency.Code;
SeasonModifierAbsInput.Unit = $"{sym}/kg";
SeasonPenaltyPerKgInput.Unit = $"{sym}/kg";
SeasonPenaltyInput.Unit = sym;
SeasonPenaltyNoneInput.Unit = sym;
SeasonPenaltyPerBsInput.Unit = $"{sym}/GA";
SeasonPenaltyPerBsNoneInput.Unit = $"{sym}/GA";
SeasonBsValueInput.Unit = $"{sym}/GA";
SeasonPenaltyPerShareInput.Unit = $"{sym}/GA";
SeasonPenaltyPerShareNoneInput.Unit = $"{sym}/GA";
SeasonShareValueInput.Unit = $"{sym}/GA";
AreaCommitmentTypePenaltyPerKgInput.Unit = $"{sym}/kg";
AreaCommitmentTypePenaltyInput.Unit = sym;
AreaCommitmentTypePenaltyNoneInput.Unit = sym;
@@ -73,14 +74,14 @@ namespace Elwig.Windows {
SeasonVatFlatrateInput.Text = "";
SeasonStartInput.Text = "";
SeasonEndInput.Text = "";
SeasonMinKgPerBsInput.Text = "";
SeasonMaxKgPerBsInput.Text = "";
SeasonMinKgPerShareInput.Text = "";
SeasonMaxKgPerShareInput.Text = "";
SeasonPenaltyPerKgInput.Text = "";
SeasonPenaltyInput.Text = "";
SeasonPenaltyNoneInput.Text = "";
SeasonPenaltyPerBsInput.Text = "";
SeasonPenaltyPerBsNoneInput.Text = "";
SeasonBsValueInput.Text = "";
SeasonPenaltyPerShareInput.Text = "";
SeasonPenaltyPerShareNoneInput.Text = "";
SeasonShareValueInput.Text = "";
}
_seasonUpdate = false;
}
@@ -95,16 +96,28 @@ namespace Elwig.Windows {
s.VatNormal = double.Parse(SeasonVatNormalInput.Text) / 100;
if (SeasonVatFlatrateInput.Text.Length > 0)
s.VatFlatrate = double.Parse(SeasonVatFlatrateInput.Text) / 100;
if (SeasonMinKgPerBsInput.Text.Length > 0)
s.MinKgPerBusinessShare = int.Parse(SeasonMinKgPerBsInput.Text);
if (SeasonMaxKgPerBsInput.Text.Length > 0)
s.MaxKgPerBusinessShare = int.Parse(SeasonMaxKgPerBsInput.Text);
if (ParameterBusinessShareModeInput.SelectedIndex == 0) {
s.MinKgPerShare = (SeasonMinKgPerShareInput.Text.Length > 0) ? int.Parse(SeasonMinKgPerShareInput.Text) : null;
s.MaxKgPerShare = (SeasonMaxKgPerShareInput.Text.Length > 0) ? int.Parse(SeasonMaxKgPerShareInput.Text) : null;
s.MinKgPerShareRed = null;
s.MaxKgPerShareRed = null;
s.MinKgPerShareWhite = null;
s.MaxKgPerShareWhite = null;
} else {
s.MinKgPerShare = null;
s.MaxKgPerShare = null;
s.MinKgPerShareRed = (SeasonMinKgPerShareInput.Text.Length > 0) ? int.Parse(SeasonMinKgPerShareInput.Text) : null;
s.MaxKgPerShareRed = (SeasonMaxKgPerShareInput.Text.Length > 0) ? int.Parse(SeasonMaxKgPerShareInput.Text) : null;
s.MinKgPerShareWhite = s.MinKgPerShareRed;
s.MaxKgPerShareWhite = s.MaxKgPerShareRed;
}
s.PenaltyPerKg = (SeasonPenaltyPerKgInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyPerKgInput.Text) : null;
s.PenaltyAmount = (SeasonPenaltyInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyInput.Text) : null;
s.PenaltyNone = (SeasonPenaltyNoneInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyNoneInput.Text) : null;
s.PenaltyPerBsAmount = (SeasonPenaltyPerBsInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyPerBsInput.Text) : null;
s.PenaltyPerBsNone = (SeasonPenaltyPerBsNoneInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyPerBsNoneInput.Text) : null;
s.BusinessShareValue = (SeasonBsValueInput.Text.Length > 0) ? decimal.Parse(SeasonBsValueInput.Text) : null;
s.PenaltyPerShareAmount = (SeasonPenaltyPerShareInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyPerShareInput.Text) : null;
s.PenaltyPerShareNone = (SeasonPenaltyPerShareNoneInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyPerShareNoneInput.Text) : null;
s.BusinessShareValue = (SeasonShareValueInput.Text.Length > 0) ? decimal.Parse(SeasonShareValueInput.Text) : null;
UpdateButtons();
}
@@ -130,7 +143,7 @@ namespace Elwig.Windows {
Season_Changed(sender, evt);
}
private void SeasonPenaltyPerBsInput_TextChanged(object sender, TextChangedEventArgs evt) {
private void SeasonPenaltyPerShareInput_TextChanged(object sender, TextChangedEventArgs evt) {
if (SeasonList.SelectedItem is not Season s) return;
InputTextChanged((TextBox)sender, Validator.CheckDecimal((TextBox)sender, false, 4, s.Precision));
Season_Changed(sender, evt);
@@ -159,13 +172,13 @@ namespace Elwig.Windows {
MaxKgPerHa = s?.MaxKgPerHa ?? 10000,
VatNormal = s?.VatNormal ?? 0.10,
VatFlatrate = s?.VatFlatrate ?? 0.13,
MinKgPerBusinessShare = s?.MinKgPerBusinessShare ?? 500,
MaxKgPerBusinessShare = s?.MaxKgPerBusinessShare ?? 1000,
MinKgPerShare = s?.MinKgPerShare,
MaxKgPerShare = s?.MaxKgPerShare,
PenaltyPerKgValue = s?.PenaltyPerKgValue,
PenaltyAmoutValue = s?.PenaltyAmoutValue,
PenaltyNoneValue = s?.PenaltyNoneValue,
PenaltyPerBsAmountValue = s?.PenaltyPerBsAmountValue,
PenaltyPerBsNoneValue = s?.PenaltyPerBsNoneValue,
PenaltyPerShareAmountValue = s?.PenaltyPerShareAmountValue,
PenaltyPerShareNoneValue = s?.PenaltyPerShareNoneValue,
BusinessShareValueValue = s?.BusinessShareValueValue,
CalcMode = s?.CalcMode ?? 0,
});
@@ -180,13 +193,12 @@ namespace Elwig.Windows {
AbsValue = m.AbsValue * mult / div,
RelValue = m.RelValue,
IsActive = m.IsActive,
IsRedacted = m.IsRedacted,
}));
}
await ctx.SaveChangesAsync();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Saison anlegen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Saison anlegen", exc);
}
App.HintContextChange();
Mouse.OverrideCursor = null;
@@ -197,19 +209,14 @@ namespace Elwig.Windows {
private async void SeasonRemoveButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonList.SelectedItem is not Season s)
return;
var r = MessageBox.Show(
$"Soll die Saison {s.Year} wirklich unwiderruflich gelöscht werden?",
"Saison löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (r == MessageBoxResult.OK) {
if (InteractionService.AskContinue("Saison löschen", $"Soll die Saison {s.Year} wirklich unwiderruflich gelöscht werden?")) {
Mouse.OverrideCursor = Cursors.Wait;
try {
using var ctx = new AppDbContext();
ctx.Remove(s);
await ctx.SaveChangesAsync();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Saison löschen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Saison löschen", exc);
}
App.HintContextChange();
Mouse.OverrideCursor = null;
+14 -14
View File
@@ -41,29 +41,29 @@ namespace Elwig.Windows {
if (!_attrChanged || _attrList == null || _attrs == null || _attrIds == null)
return;
using var tx = await ctx.Database.BeginTransactionAsync();
// delete
var deleteAttrIds = _attrs.Where(a => a.Value == null).Select(a => a.Key).ToList();
await ctx.WineAttributes.Where(a => deleteAttrIds.Contains(a.AttrId)).ExecuteDeleteAsync();
foreach (var (attr, old) in _attrIds) {
attr.AttrId = old;
}
foreach (var (old, attrid) in _attrs.Where(a => a.Value != null)) {
ctx.Update(ctx.WineAttributes.Find(old)!);
}
await ctx.SaveChangesAsync();
// update
ctx.UpdateRange(_attrList
.Where(_attrIds.ContainsKey)
.Select(a => {
a.AttrId = _attrIds[a];
ctx.Entry(a).State = EntityState.Added;
return a;
}));
await ctx.SaveChangesAsync();
foreach (var (old, attrid) in _attrs.Where(a => a.Value != null)) {
await ctx.Database.ExecuteSqlAsync($"UPDATE wine_attribute SET attrid = {attrid} WHERE attrid = {old}");
await ctx.Database.ExecuteSqlAsync($"UPDATE area_commitment_type SET vtrgid = (sortid || COALESCE(attrid, '') || COALESCE(disc, '')) WHERE attrid = {attrid}");
await ctx.Database.ExecuteSqlRawAsync($"UPDATE payment_variant SET data = REPLACE(REPLACE(data, '/{old}\"', '/{attrid}\"'), '/{old}-', '/{attrid}-')");
await ctx.Database.ExecuteSqlAsync($"UPDATE payment_variant SET data = REPLACE(REPLACE(data, {$"/{old}\""}, {$"/{attrid}\""}), {$"/{old}-"}, {$"/{attrid}-"})");
}
foreach (var attr in _attrList.Where(a => !_attrIds.ContainsKey(a))) {
if (attr.AttrId == null) continue;
ctx.Add(attr);
}
// insert
ctx.AddRange(_attrList
.Where(a => !_attrIds.ContainsKey(a) && a.AttrId != null));
await ctx.SaveChangesAsync();
await tx.CommitAsync();
}
private void WineAttributeList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
+14 -14
View File
@@ -41,28 +41,28 @@ namespace Elwig.Windows {
if (!_cultChanged || _cultList == null || _cults == null || _cultIds == null)
return;
using var tx = await ctx.Database.BeginTransactionAsync();
// delete
var deleteCultIds = _cults.Where(c => c.Value == null).Select(c => c.Key).ToList();
await ctx.WineCultivations.Where(c => deleteCultIds.Contains(c.CultId)).ExecuteDeleteAsync();
foreach (var (cult, old) in _cultIds) {
cult.CultId = old;
}
foreach (var (old, cultid) in _cults.Where(c => c.Value != null)) {
ctx.Update(ctx.WineCultivations.Find(old)!);
}
await ctx.SaveChangesAsync();
// update
ctx.UpdateRange(_cultList
.Where(_cultIds.ContainsKey)
.Select(c => {
c.CultId = _cultIds[c];
ctx.Entry(c).State = EntityState.Added;
return c;
}));
await ctx.SaveChangesAsync();
foreach (var (old, cultid) in _cults.Where(c => c.Value != null)) {
await ctx.Database.ExecuteSqlAsync($"UPDATE wine_cultivation SET cultid = {cultid} WHERE cultid = {old}");
await ctx.Database.ExecuteSqlRawAsync($"UPDATE payment_variant SET data = REPLACE(data, '-{old}\"', '-{cultid}\"')");
await ctx.Database.ExecuteSqlAsync($"UPDATE payment_variant SET data = REPLACE(data, {$"-{old}\""}, {$"-{cultid}\""})");
}
foreach (var cult in _cultList.Where(c => !_cultIds.ContainsKey(c))) {
if (cult.CultId == null) continue;
ctx.Add(cult);
}
// insert
ctx.AddRange(_cultList
.Where(c => !_cultIds.ContainsKey(c) && c.CultId != null));
await ctx.SaveChangesAsync();
await tx.CommitAsync();
}
private void WineCultivationList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
+47 -58
View File
@@ -1,5 +1,6 @@
using Elwig.Helpers;
using Elwig.Models.Entities;
using Elwig.Services;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
@@ -11,8 +12,6 @@ using System.Windows.Input;
namespace Elwig.Windows {
public partial class BaseDataWindow : AdministrationWindow {
protected AppDbContext? EditContext;
public BaseDataWindow() {
InitializeComponent();
RequiredInputs = [
@@ -31,14 +30,15 @@ namespace Elwig.Windows {
AreaCommitmentTypeMinKgPerHaInput, AreaCommitmentTypePenaltyPerKgInput,
AreaCommitmentTypePenaltyInput, AreaCommitmentTypePenaltyNoneInput,
SeasonMaxKgPerHaInput, SeasonVatNormalInput, SeasonVatFlatrateInput, SeasonStartInput, SeasonEndInput,
SeasonMinKgPerBsInput, SeasonMaxKgPerBsInput, SeasonBsValueInput,
SeasonMinKgPerShareInput, SeasonMaxKgPerShareInput, SeasonShareValueInput,
SeasonPenaltyPerKgInput, SeasonPenaltyInput, SeasonPenaltyNoneInput,
SeasonPenaltyPerBsInput, SeasonPenaltyPerBsNoneInput,
SeasonPenaltyPerShareInput, SeasonPenaltyPerShareNoneInput,
SeasonModifierIdInput, SeasonModifierNameInput, SeasonModifierRelInput, SeasonModifierAbsInput,
];
WineAttributeFillLowerInput.Visibility = Visibility.Hidden;
WineAttributeFillLowerLabel.Visibility = Visibility.Hidden;
ParameterAreaComGroup.Header = ParameterAreaComGroup.Header.ToString()!.Split('(')[0] + $"({Utils.CurrentLastSeason})";
LockInputs();
}
protected override void ShortcutNew() { }
@@ -78,25 +78,19 @@ namespace Elwig.Windows {
SeasonMaxKgPerHaInput.IsReadOnly = true;
SeasonVatNormalInput.IsReadOnly = true;
SeasonVatFlatrateInput.IsReadOnly = true;
SeasonMinKgPerBsInput.IsReadOnly = true;
SeasonMaxKgPerBsInput.IsReadOnly = true;
SeasonMinKgPerShareInput.IsReadOnly = true;
SeasonMaxKgPerShareInput.IsReadOnly = true;
SeasonPenaltyPerKgInput.IsReadOnly = true;
SeasonPenaltyInput.IsReadOnly = true;
SeasonPenaltyNoneInput.IsReadOnly = true;
SeasonPenaltyPerBsInput.IsReadOnly = true;
SeasonPenaltyPerBsNoneInput.IsReadOnly = true;
SeasonBsValueInput.IsReadOnly = true;
SeasonPenaltyPerShareInput.IsReadOnly = true;
SeasonPenaltyPerShareNoneInput.IsReadOnly = true;
SeasonShareValueInput.IsReadOnly = true;
SeasonModifierIdInput.IsReadOnly = true;
SeasonModifierNameInput.IsReadOnly = true;
SeasonModifierRelInput.IsReadOnly = true;
SeasonModifierAbsInput.IsReadOnly = true;
ParameterAllowAttrIntoLowerInput.IsEnabled = false;
ParameterAvoidUnderDeliveriesInput.IsEnabled = false;
ParameterHonorGebundenInput.IsEnabled = false;
ParameterExportEbicsVersion.IsEnabled = false;
ParameterExportEbicsAddress.IsEnabled = false;
}
new protected void UnlockInputs() {
@@ -132,25 +126,19 @@ namespace Elwig.Windows {
SeasonMaxKgPerHaInput.IsReadOnly = false;
SeasonVatNormalInput.IsReadOnly = false;
SeasonVatFlatrateInput.IsReadOnly = false;
SeasonMinKgPerBsInput.IsReadOnly = false;
SeasonMaxKgPerBsInput.IsReadOnly = false;
SeasonMinKgPerShareInput.IsReadOnly = false;
SeasonMaxKgPerShareInput.IsReadOnly = false;
SeasonPenaltyPerKgInput.IsReadOnly = false;
SeasonPenaltyInput.IsReadOnly = false;
SeasonPenaltyNoneInput.IsReadOnly = false;
SeasonPenaltyPerBsInput.IsReadOnly = false;
SeasonPenaltyPerBsNoneInput.IsReadOnly = false;
SeasonBsValueInput.IsReadOnly = false;
SeasonPenaltyPerShareInput.IsReadOnly = false;
SeasonPenaltyPerShareNoneInput.IsReadOnly = false;
SeasonShareValueInput.IsReadOnly = false;
SeasonModifierIdInput.IsReadOnly = false;
SeasonModifierNameInput.IsReadOnly = false;
SeasonModifierRelInput.IsReadOnly = false;
SeasonModifierAbsInput.IsReadOnly = false;
ParameterAllowAttrIntoLowerInput.IsEnabled = true;
ParameterAvoidUnderDeliveriesInput.IsEnabled = true;
ParameterHonorGebundenInput.IsEnabled = true;
ParameterExportEbicsVersion.IsEnabled = true;
ParameterExportEbicsAddress.IsEnabled = true;
}
protected override async Task OnRenewContext(AppDbContext ctx) {
@@ -199,31 +187,30 @@ namespace Elwig.Windows {
}
private async Task InitEditing() {
EditContext = new AppDbContext();
await BranchesInitEditing(EditContext);
await WineAttributesInitEditing(EditContext);
await WineCultivationsInitEditing(EditContext);
await AreaCommitmentTypesInitEditing(EditContext);
await SeasonsInitEditing(EditContext);
await ModifiersInitEditing(EditContext);
using var ctx = new AppDbContext();
await BranchesInitEditing(ctx);
await WineAttributesInitEditing(ctx);
await WineCultivationsInitEditing(ctx);
await AreaCommitmentTypesInitEditing(ctx);
await SeasonsInitEditing(ctx);
await ModifiersInitEditing(ctx);
}
private async Task Save() {
await UpdateClientParameters(App.Client);
await UpdateParameters(Utils.CurrentLastSeason);
using var tx = await EditContext!.Database.BeginTransactionAsync();
await BranchesSave(EditContext!);
await WineAttributesSave(EditContext!);
await WineCultivationsSave(EditContext!);
await AreaCommitmentTypesSave(EditContext!);
await SeasonsSave(EditContext!);
await ModifiersSave(EditContext!);
using var ctx = new AppDbContext();
using var tx = await ctx.Database.BeginTransactionAsync();
await BranchesSave(ctx);
await WineAttributesSave(ctx);
await WineCultivationsSave(ctx);
await AreaCommitmentTypesSave(ctx);
await SeasonsSave(ctx);
await ModifiersSave(ctx);
await tx.CommitAsync();
}
private async Task FinishEditing() {
EditContext?.Dispose();
EditContext = null;
using var ctx = new AppDbContext();
await BranchesFinishEditing(ctx);
await WineAttributesFinishEditing(ctx);
@@ -300,9 +287,7 @@ namespace Elwig.Windows {
try {
await Save();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Stammdaten aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Stammdaten aktualisieren", exc);
SaveButton.IsEnabled = true;
Mouse.OverrideCursor = null;
return;
@@ -360,6 +345,8 @@ namespace Elwig.Windows {
TextElementDeliveryConfirmation.Text = p.TextDeliveryConfirmation;
TextElementCreditNote.Text = p.TextCreditNote;
ParameterBusinessShareModeInput.SelectedIndex = p.ModeBusinessShares;
ParameterBusinessShareHistoryInput.IsChecked = p.EnableMemberHistory;
ParameterAllowAttrIntoLowerInput.IsChecked = s?.Billing_AllowAttrsIntoLower ?? false;
ParameterAvoidUnderDeliveriesInput.IsChecked = s?.Billing_AvoidUnderDeliveries ?? false;
ParameterHonorGebundenInput.IsChecked = s?.Billing_HonorGebunden ?? false;
@@ -371,27 +358,29 @@ namespace Elwig.Windows {
private async Task UpdateClientParameters(ClientParameters p) {
p.Name = ClientNameInput.Text;
p.NameSuffix = string.IsNullOrEmpty(ClientNameSuffixInput.Text) ? null : ClientNameSuffixInput.Text;
p.NameSuffix = string.IsNullOrWhiteSpace(ClientNameSuffixInput.Text) ? null : ClientNameSuffixInput.Text;
p.NameType = ClientNameTypeInput.Text;
p.NameToken = ClientNameTokenInput.Text;
p.NameShort = ClientNameShortInput.Text;
p.Address = ClientAddressInput.Text;
p.Plz = int.Parse(ClientPlzInput.Text);
p.Ort = ClientOrtInput.Text;
p.Iban = string.IsNullOrEmpty(ClientIbanInput.Text) ? null : ClientIbanInput.Text;
p.Bic = string.IsNullOrEmpty(ClientBicInput.Text) ? null : ClientBicInput.Text;
p.UstIdNr = string.IsNullOrEmpty(ClientUstIdNrInput.Text) ? null : ClientUstIdNrInput.Text;
p.LfbisNr = string.IsNullOrEmpty(ClientLfbisNrInput.Text) ? null : ClientLfbisNrInput.Text;
p.OrganicAuthority = string.IsNullOrEmpty(ClientOrganicAuthorityInput.Text) ? null : ClientOrganicAuthorityInput.Text;
p.PhoneNr = string.IsNullOrEmpty(ClientPhoneNrInput.Text) ? null : ClientPhoneNrInput.Text;
p.FaxNr = string.IsNullOrEmpty(ClientFaxNrInput.Text) ? null : ClientFaxNrInput.Text;
p.EmailAddress = string.IsNullOrEmpty(ClientEmailAddressInput.Text) ? null : ClientEmailAddressInput.Text;
p.Website = string.IsNullOrEmpty(ClientWebsiteInput.Text) ? null : ClientWebsiteInput.Text;
p.Iban = string.IsNullOrWhiteSpace(ClientIbanInput.Text) ? null : ClientIbanInput.Text;
p.Bic = string.IsNullOrWhiteSpace(ClientBicInput.Text) ? null : ClientBicInput.Text;
p.UstIdNr = string.IsNullOrWhiteSpace(ClientUstIdNrInput.Text) ? null : ClientUstIdNrInput.Text;
p.LfbisNr = string.IsNullOrWhiteSpace(ClientLfbisNrInput.Text) ? null : ClientLfbisNrInput.Text;
p.OrganicAuthority = string.IsNullOrWhiteSpace(ClientOrganicAuthorityInput.Text) ? null : ClientOrganicAuthorityInput.Text;
p.PhoneNr = string.IsNullOrWhiteSpace(ClientPhoneNrInput.Text) ? null : ClientPhoneNrInput.Text;
p.FaxNr = string.IsNullOrWhiteSpace(ClientFaxNrInput.Text) ? null : ClientFaxNrInput.Text;
p.EmailAddress = string.IsNullOrWhiteSpace(ClientEmailAddressInput.Text) ? null : ClientEmailAddressInput.Text;
p.Website = string.IsNullOrWhiteSpace(ClientWebsiteInput.Text) ? null : ClientWebsiteInput.Text;
p.TextDeliveryNote = string.IsNullOrEmpty(TextElementDeliveryNote.Text) ? null : TextElementDeliveryNote.Text;
p.ModeBusinessShares = ParameterBusinessShareModeInput.SelectedIndex;
p.EnableMemberHistory = ParameterBusinessShareHistoryInput.IsChecked ?? false;
p.TextDeliveryNote = string.IsNullOrWhiteSpace(TextElementDeliveryNote.Text) ? null : TextElementDeliveryNote.Text;
p.ModeDeliveryNoteStats = (ModeDeliveryNoteNone.IsChecked == true) ? 0 : (ModeDeliveryNoteGaOnly.IsChecked == true) ? 1 : (ModeDeliveryNoteShort.IsChecked == true) ? 2 : (ModeDeliveryNoteFull.IsChecked == true) ? 3 : 2;
p.TextDeliveryConfirmation = string.IsNullOrEmpty(TextElementDeliveryConfirmation.Text) ? null : TextElementDeliveryConfirmation.Text;
p.TextCreditNote = string.IsNullOrEmpty(TextElementCreditNote.Text) ? null : TextElementCreditNote.Text;
p.TextDeliveryConfirmation = string.IsNullOrWhiteSpace(TextElementDeliveryConfirmation.Text) ? null : TextElementDeliveryConfirmation.Text;
p.TextCreditNote = string.IsNullOrWhiteSpace(TextElementCreditNote.Text) ? null : TextElementCreditNote.Text;
p.ExportEbicsVersion = ParameterExportEbicsVersion.SelectedIndex + 3;
p.ExportEbicsAddress = ParameterExportEbicsAddress.SelectedIndex;
+65 -62
View File
@@ -10,6 +10,7 @@ using Elwig.Helpers.Billing;
using Elwig.Models.Entities;
using ScottPlot.Plottables;
using ScottPlot;
using Elwig.Services;
namespace Elwig.Windows {
public partial class ChartWindow : ContextWindow {
@@ -25,12 +26,12 @@ namespace Elwig.Windows {
private bool HasChanged = false;
private bool Updating = false;
private Scatter DataPlot;
private Scatter? DataPlot;
private Scatter? GebundenPlot;
private Marker HighlightedPointPlot;
private Marker PrimaryMarkedPointPlot;
private Marker SecondaryMarkedPointPlot;
private Text TooltipPlot;
private Marker? HighlightedPointPlot;
private Marker? PrimaryMarkedPointPlot;
private Marker? SecondaryMarkedPointPlot;
private Text? TooltipPlot;
private static readonly LegendItem
UngebundenLegend = new() {
@@ -76,15 +77,13 @@ namespace Elwig.Windows {
PriceInput.Unit = $"{CurrencySymbol}/kg";
GebundenFlatBonus.Unit = $"{CurrencySymbol}/kg";
PaymentVar = ctx.PaymentVariants.Find(year, avnr) ?? throw new ArgumentException("PaymentVar not found");
Title = $"{PaymentVar?.Name} - Lese {year} - Elwig";
Title = $"{PaymentVar.Name} - Lese {year} - Elwig";
LockContext = true;
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
if (HasChanged) {
var r = MessageBox.Show("Soll das Fenster wirklich geschlossen werden? Nicht gespeicherte Änderungen werden NICHT übernommen!", "Schließen bestätigen",
MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r != MessageBoxResult.Yes) {
if (!InteractionService.AskConfirmation("Schließen bestätigen", "Soll das Fenster wirklich geschlossen werden? Nicht gespeicherte Änderungen werden NICHT übernommen!")) {
e.Cancel = true;
return;
}
@@ -105,23 +104,22 @@ namespace Elwig.Windows {
try {
var data = EditBillingData.FromJson(PaymentVar.Data, await Utils.GetVaributes(ctx, Year));
var paymentEntries = await data.GetPaymentGraphEntries(ctx, Season);
var paymentEntries = (await data.GetPaymentGraphEntries(ctx, Season)).ToList();
GraphEntries = [
..paymentEntries,
..await data.GetQualityGraphEntries(ctx, Season, paymentEntries.Any() ? paymentEntries.Max(e => e.Id) : 0)
..await data.GetQualityGraphEntries(ctx, Season, paymentEntries.Count != 0 ? paymentEntries.Max(e => e.Id) : 0)
];
} catch (KeyNotFoundException ex) {
var key = ex.Message.Split('\'')[1].Split('\'')[0];
MessageBox.Show($"Fehler beim Laden der Auszahlungsvariante:\n\n" +
$"Mit unbekanntem Attribut '{key}' kann nicht umgegangen werden.", "Fehler",
MessageBoxButton.OK, MessageBoxImage.Error);
} catch (KeyNotFoundException exc) {
var key = exc.Message.Split('\'')[1].Split('\'')[0];
InteractionService.ShowError("Fehler",
$"Fehler beim Laden der Auszahlungsvariante:\n\n" +
$"Mit unbekanntem Attribut '{key}' kann nicht umgegangen werden.");
} catch (ArgumentException) {
MessageBox.Show($"Fehler beim Laden der Auszahlungsvariante:\n\n" +
$"Die Daten der Auszahlungsvariante entsprechen nicht dem benötigtem Format.", "Fehler",
MessageBoxButton.OK, MessageBoxImage.Error);
} catch (Exception ex) {
MessageBox.Show("Fehler beim Laden der Auszahlungsvariante:\n\n" + ex.Message, "Fehler",
MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Fehler",
$"Fehler beim Laden der Auszahlungsvariante:\n\n" +
$"Die Daten der Auszahlungsvariante entsprechen nicht dem benötigtem Format.");
} catch (Exception exc) {
InteractionService.ShowException("Fehler", "Fehler beim Laden der Auszahlungsvariante", exc);
}
Vaributes = await Utils.GetVaributeList(ctx, Year);
GraphEntries.ForEach(e => {
@@ -217,10 +215,12 @@ namespace Elwig.Windows {
GebundenPlot.MarkerStyle = new MarkerStyle(MarkerShape.FilledCircle, 9, ColorGebunden);
}
DataPlot = OechslePricePlot.Plot.Add.Scatter(SelectedGraphEntry!.DataGraph.DataX, SelectedGraphEntry!.DataGraph.DataY);
DataPlot.LineStyle.Color = ColorUngebunden;
DataPlot.Color = ColorUngebunden;
DataPlot.MarkerStyle = new MarkerStyle(MarkerShape.FilledCircle, 9, ColorUngebunden);
if (SelectedGraphEntry != null) {
DataPlot = OechslePricePlot.Plot.Add.Scatter(SelectedGraphEntry.DataGraph.DataX, SelectedGraphEntry.DataGraph.DataY);
DataPlot.LineStyle.Color = ColorUngebunden;
DataPlot.Color = ColorUngebunden;
DataPlot.MarkerStyle = new MarkerStyle(MarkerShape.FilledCircle, 9, ColorUngebunden);
}
OechslePricePlot.Plot.Axes.SetLimits(Math.Min(GraphEntry.MinX, GraphEntry.MinXGeb) - 1, GraphEntry.MaxX + 1, -0.1, 1.5);
@@ -246,7 +246,10 @@ namespace Elwig.Windows {
SecondaryMarkedPoint = -1;
ChangeActiveGraph(null);
HideGradationLines();
OechslePricePlot.Plot.Remove(DataPlot);
if (DataPlot != null) {
OechslePricePlot.Plot.Remove(DataPlot);
DataPlot = null;
}
if (GebundenPlot != null) {
OechslePricePlot.Plot.Remove(GebundenPlot);
GebundenPlot = null;
@@ -256,9 +259,9 @@ namespace Elwig.Windows {
OechslePricePlot.Refresh();
}
private void ChangeMarker(Marker point, bool visible, double x = 0, double y = 0) {
point.Location = new Coordinates(x, y);
point.IsVisible = visible;
private static void ChangeMarker(Marker? point, bool visible, double x = 0, double y = 0) {
point?.Location = new Coordinates(x, y);
point?.IsVisible = visible;
}
private void EnableActionButtons() {
@@ -418,7 +421,7 @@ namespace Elwig.Windows {
var res = Validator.CheckDecimal(PriceInput, true, 2, Season.Precision);
if (res.IsValid && double.TryParse(PriceInput.Text, out double price)) {
ActiveGraph.SetPriceAt(PrimaryMarkedPoint, price);
PrimaryMarkedPointPlot.Location = new Coordinates(PrimaryMarkedPointPlot.Location.X, price);
PrimaryMarkedPointPlot?.Location = new Coordinates(PrimaryMarkedPointPlot.Location.X, price);
SetHasChanged();
OechslePricePlot.Refresh();
CheckGebundenTypeFixed();
@@ -494,17 +497,19 @@ namespace Elwig.Windows {
ChangeMarker(PrimaryMarkedPointPlot, true, ActiveGraph.GetOechsleAt(PrimaryMarkedPoint), ActiveGraph.GetPriceAt(PrimaryMarkedPoint));
Updating = true;
OechsleInput.Text = Highlighted.Graph!.GetOechsleAt(Highlighted.Index).ToString();
GebInput.IsChecked = Highlighted.Graph == SelectedGraphEntry?.GebundenGraph;
PriceInput.Text = Math.Round(Highlighted.Graph.GetPriceAt(Highlighted.Index), Season.Precision).ToString();
Updating = false;
if (Highlighted.Graph != null) {
Updating = true;
OechsleInput.Text = Highlighted.Graph.GetOechsleAt(Highlighted.Index).ToString();
GebInput.IsChecked = Highlighted.Graph == SelectedGraphEntry?.GebundenGraph;
PriceInput.Text = Math.Round(Highlighted.Graph.GetPriceAt(Highlighted.Index), Season.Precision).ToString();
Updating = false;
}
EnableActionButtons();
} else {
PrimaryMarkedPoint = -1;
SecondaryMarkedPoint = -1;
if (SelectedGraphEntry!.GebundenGraph != null) {
if (SelectedGraphEntry?.GebundenGraph != null) {
ChangeActiveGraph(null);
}
ChangeMarker(PrimaryMarkedPointPlot, false);
@@ -550,21 +555,26 @@ namespace Elwig.Windows {
if (mouseOnData != null) {
ChangeMarker(HighlightedPointPlot, true, mouseOnData.Value.X, mouseOnData.Value.Y);
HighlightedPointPlot.IsVisible = true;
HighlightedPointPlot?.IsVisible = true;
HoverChanged = true ^ HoverActive;
HoverActive = true;
HandleTooltip(mouseOnData.Value.X, mouseOnData.Value.Y, mouseOnData.Value.Index, SelectedGraphEntry!.DataGraph, e.GetPosition(OechslePricePlot), e is MouseWheelEventArgs);
if (SelectedGraphEntry?.DataGraph is Graph g)
HandleTooltip(mouseOnData.Value.X, mouseOnData.Value.Y, mouseOnData.Value.Index, g, e.GetPosition(OechslePricePlot), e is MouseWheelEventArgs);
} else if (mouseOnGebunden != null) {
ChangeMarker(HighlightedPointPlot, true, mouseOnGebunden.Value.X, mouseOnGebunden.Value.Y);
HighlightedPointPlot.IsVisible = true;
HighlightedPointPlot?.IsVisible = true;
HoverChanged = true ^ HoverActive;
HoverActive = true;
HandleTooltip(mouseOnGebunden.Value.X, mouseOnGebunden.Value.Y, mouseOnGebunden.Value.Index, SelectedGraphEntry!.GebundenGraph!, e.GetPosition(OechslePricePlot), e is MouseWheelEventArgs);
if (SelectedGraphEntry?.GebundenGraph is Graph g)
HandleTooltip(mouseOnGebunden.Value.X, mouseOnGebunden.Value.Y, mouseOnGebunden.Value.Index, g, e.GetPosition(OechslePricePlot), e is MouseWheelEventArgs);
} else {
ChangeMarker(HighlightedPointPlot, false);
HoverChanged = false ^ HoverActive;
HoverActive = false;
OechslePricePlot.Plot.PlottableList.Remove(TooltipPlot);
if (TooltipPlot != null) {
OechslePricePlot.Plot.PlottableList.Remove(TooltipPlot);
TooltipPlot = null;
}
OechslePricePlot.Refresh();
}
}
@@ -579,7 +589,10 @@ namespace Elwig.Windows {
private void HandleTooltip(double pointX, double pointY, int pointIndex, Graph g, Point p, bool force) {
if (force || LastHighlighted != Highlighted || HoverChanged) {
OechslePricePlot.Plot.PlottableList.Remove(TooltipPlot);
if (TooltipPlot != null) {
OechslePricePlot.Plot.PlottableList.Remove(TooltipPlot);
TooltipPlot = null;
}
if (TooltipInput.IsChecked == true) {
Coordinates mouseLocation = GetMouseCoordinates(new(p.X, p.Y - 30));
TooltipPlot = OechslePricePlot.Plot.Add.Text($"Oechsle: {pointX:N2}, Preis: {Math.Round(pointY, Season.Precision)} {CurrencySymbol}/kg", mouseLocation.X, mouseLocation.Y);
@@ -622,11 +635,7 @@ namespace Elwig.Windows {
private void DeleteButton_Click(object sender, RoutedEventArgs e) {
if (SelectedGraphEntry == null) return;
var r = MessageBox.Show(
$"Soll die Kurve {SelectedGraphEntry.Id} (verwendet in folgenden Verträgen: {SelectedGraphEntry.VaributeStringSimple}) wirklich gelöscht werden?",
"Kurve löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r == MessageBoxResult.Yes) {
if (InteractionService.AskConfirmation("Kurve löschen", $"Soll die Kurve {SelectedGraphEntry.Id} (verwendet in folgenden Verträgen: {SelectedGraphEntry.VaributeStringSimple}) wirklich gelöscht werden?")) {
GraphEntries.Remove(SelectedGraphEntry);
SetHasChanged();
GraphList.Items.Refresh();
@@ -651,9 +660,7 @@ namespace Elwig.Windows {
});
} catch (Exception exc) {
Mouse.OverrideCursor = null;
var str = "Der Eintrag konnte nicht in der Datenbank gespeichert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Auszahlungsvariante speichern", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Auszahlungsvariante speichern", exc);
SaveButton.IsEnabled = true;
return;
}
@@ -664,9 +671,9 @@ namespace Elwig.Windows {
await b.Calculate(false);
});
} catch (KeyNotFoundException exc) {
MessageBox.Show(exc.Message, "Noch nicht alle Preise festgelegt", MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Noch nicht alle Preise festgelegt", exc.Message);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Berechnungsfehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Berechnungsfehler", exc);
}
LockContext = false;
@@ -684,7 +691,7 @@ namespace Elwig.Windows {
}
}
private void DisableTextBox(TextBox u) {
private static void DisableTextBox(TextBox u) {
u.IsEnabled = false;
u.IsReadOnly = true;
}
@@ -715,10 +722,8 @@ namespace Elwig.Windows {
ActiveGraph = g;
}
private void ChangeLineWidth(Scatter? p, double lineWidth) {
if (p != null) {
p.LineWidth = (float)lineWidth;
}
private static void ChangeLineWidth(Scatter? p, double lineWidth) {
p?.LineWidth = (float)lineWidth;
}
private void GraphList_SelectionChanged(object sender, SelectionChangedEventArgs e) {
@@ -760,7 +765,7 @@ namespace Elwig.Windows {
}
}
SelectedGraphEntry!.Vaributes = VaributeInput.SelectedItems.Cast<Varibute>().ToList();
SelectedGraphEntry?.Vaributes = [.. VaributeInput.SelectedItems.Cast<Varibute>()];
SetHasChanged();
GraphList.Items.Refresh();
VaributeInput.Items.Refresh();
@@ -773,9 +778,7 @@ namespace Elwig.Windows {
if (ge != SelectedGraphEntry && ge.Abgewertet == SelectedGraphEntry?.Abgewertet) {
var toRemove = ge.Vaributes.Where(c => c.Listing.Equals(varibute)).ToList();
if (toRemove.Count == 0) continue;
var r = MessageBox.Show($"Achtung: {string.Join(", ", toRemove)} ist bereits in Kurve {ge.Id} in Verwendung!\nSoll die Zuweisung dort entfernt werden?", "Entfernen bestätigen",
MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r != MessageBoxResult.Yes) {
if (!InteractionService.AskConfirmation("Entfernen bestätigen", $"Achtung: {string.Join(", ", toRemove)} ist bereits in Kurve {ge.Id} in Verwendung!\nSoll die Zuweisung dort entfernt werden?")) {
return false;
}
ge.Vaributes.RemoveAll(c => c.Listing.Equals(varibute));
+34 -54
View File
@@ -6,7 +6,6 @@ using Elwig.Models.Entities;
using Elwig.Services;
using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -86,10 +85,10 @@ namespace Elwig.Windows {
if (n < 3) WeighingCButton.Visibility = Visibility.Hidden;
if (n < 4) WeighingDButton.Visibility = Visibility.Hidden;
if (n == 1) WeighingAButton.Content = "Wiegen";
if (n > 1) WeighingAButton.Content = $"Wiegen {App.CommandScales[0].ScaleId}";
if (n >= 2) WeighingBButton.Content = $"Wiegen {App.CommandScales[1].ScaleId}";
if (n >= 3) WeighingCButton.Content = $"Wiegen {App.CommandScales[2].ScaleId}";
if (n >= 4) WeighingDButton.Content = $"Wiegen {App.CommandScales[3].ScaleId}";
if (n > 1) WeighingAButton.Content = $"Wiegen {App.CommandScales[0].ScaleTerminalId}";
if (n >= 2) WeighingBButton.Content = $"Wiegen {App.CommandScales[1].ScaleTerminalId}";
if (n >= 3) WeighingCButton.Content = $"Wiegen {App.CommandScales[2].ScaleTerminalId}";
if (n >= 4) WeighingDButton.Content = $"Wiegen {App.CommandScales[3].ScaleTerminalId}";
WeighingManualButton.Margin = new Thickness(10, 10 + n * 32, 10, 10);
foreach (var s in App.EventScales) {
s.WeighingEvent += Scale_Weighing;
@@ -125,7 +124,8 @@ namespace Elwig.Windows {
public DeliveryAdminWindow(int mgnr) :
this() {
ViewModel.FilterMember = DeliveryService.GetMember(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
using var ctx = new AppDbContext();
ViewModel.FilterMember = ctx.Members.Find(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
ViewModel.Title = $"Lieferungen - {ViewModel.FilterMember.AdministrativeName} - Elwig";
ViewModel.EnableAllSeasons = true;
}
@@ -137,8 +137,9 @@ namespace Elwig.Windows {
if (ViewModel.IsReceipt) {
NewDeliveryButton_Click(null, null);
if (await ctx.FetchSeasons(Utils.CurrentYear).SingleOrDefaultAsync() == null) {
MessageBox.Show("Die Saison für das aktuelle Jahr wurde noch nicht erstellt. Neue Lieferungen können nicht abgespeichert werden.\n\n(Stammdaten -> Saisons -> Neu anlegen...)",
"Saison noch nicht erstellt", MessageBoxButton.OK, MessageBoxImage.Warning);
InteractionService.ShowWarning("Saison noch nicht erstellt",
"Die Saison für das aktuelle Jahr wurde noch nicht erstellt. Neue Lieferungen können nicht abgespeichert werden.\n\n" +
"(Stammdaten -> Saisons -> Neu anlegen...)");
}
}
}
@@ -164,9 +165,7 @@ namespace Elwig.Windows {
private async void Menu_DeliveryNote_Email_Click(object sender, RoutedEventArgs evt) {
if (DeliveryList.SelectedItem is not Delivery d)
return;
var res = MessageBox.Show("Soll eine E-Mail verschickt werden?", "Lieferschein verschicken",
MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
if (res != MessageBoxResult.Yes)
if (!InteractionService.AskQuestion("Lieferschein verschicken", "Soll eine E-Mail verschickt werden?", true))
return;
await DeliveryService.GenerateDeliveryNote(d.Year, d.DId, ExportMode.Email);
}
@@ -174,16 +173,11 @@ namespace Elwig.Windows {
private async void Menu_Bki_SaveList_Click(object sender, RoutedEventArgs evt) {
if (sender is not MenuItem m) return;
var year = int.Parse(m.Header.ToString()?.Split(" ")[^1] ?? Utils.CurrentLastSeason.ToString());
var d = new SaveFileDialog() {
FileName = $"{App.Client.NameToken}-Traubentransportscheinliste-{year}.{Bki.FileExtension}",
DefaultExt = Bki.FileExtension,
Filter = "CSV-Datei (*.csv)|*.csv",
Title = $"Traubentransportscheinliste (BKI) speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
var filename = InteractionService.SaveFile("Traubentransportscheinliste (BKI)", $"{App.Client.NameToken}-Traubentransportscheinliste-{year}", Bki.FileExtension);
if (filename != null) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
using var file = new Bki(d.FileName);
using var file = new Bki(filename);
await file.ExportAsync(year);
});
Mouse.OverrideCursor = null;
@@ -372,7 +366,7 @@ namespace Elwig.Windows {
private void InitialInputs() {
ViewModel.LastScaleError = null;
ViewModel.WeighingData = null;
ViewModel.ScaleId = null;
ViewModel.ScaleTerminalId = null;
ViewModel.ManualWeighingReason = null;
ClearOriginalValues();
@@ -431,6 +425,8 @@ namespace Elwig.Windows {
var deliveries = await deliveryQuery
.Include(d => d.Parts).ThenInclude(p => p.PartModifiers).ThenInclude(m => m.Modifier)
.Include(d => d.Parts).ThenInclude(p => p.Variety)
.Include(d => d.Parts).ThenInclude(p => p.Attribute)
.Include(d => d.Parts).ThenInclude(p => p.Cultivation)
.IgnoreAutoIncludes()
.AsSplitQuery()
.ToListAsync();
@@ -487,7 +483,7 @@ namespace Elwig.Windows {
await base.OnRenewContext(ctx);
if (ViewModel.FilterMember != null) {
if (await DeliveryService.GetMemberAsync(ViewModel.FilterMember.MgNr) is not Member m) {
if (await ctx.FetchMembers(ViewModel.FilterMember.MgNr).SingleOrDefaultAsync() is not Member m) {
Close();
return;
}
@@ -639,11 +635,10 @@ namespace Elwig.Windows {
try {
var res = await s.Weigh();
OnWeighingResult(s, res);
} catch (Exception ex) {
ViewModel.LastScaleError = ex.Message.Split(": ")[^1];
} catch (Exception exc) {
ViewModel.LastScaleError = exc.Message.Split(": ")[^1];
OnWeighingResult(s, new());
MessageBox.Show($"Beim Wiegen ist ein Fehler aufgetreten:\n\n{ex.Message}", "Waagenfehler",
MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Waagenfehler", "Beim Wiegen ist ein Fehler aufgetreten", exc);
}
ViewModel.ManualWeighingReason = null;
ManualWeighingInput.IsChecked = false;
@@ -656,13 +651,13 @@ namespace Elwig.Windows {
private void OnWeighingResult(IScale scale, WeighingResult res) {
if ((res.NetWeight ?? 0) > 0 && res.FullWeighingId != null) {
ViewModel.Weight = res.NetWeight;
ViewModel.ScaleId = scale.ScaleId;
ViewModel.ScaleTerminalId = scale.ScaleTerminalId;
ViewModel.WeighingData = res.ToJson().ToJsonString();
ViewModel.ManualWeighingReason = null;
ManualWeighingInput.IsChecked = false;
} else {
ViewModel.Weight = null;
ViewModel.ScaleId = null;
ViewModel.ScaleTerminalId = null;
ViewModel.WeighingData = null;
}
ViewModel.LastScaleError = null;
@@ -776,7 +771,7 @@ namespace Elwig.Windows {
}
private void EmptyScale() {
var scale = App.Scales.Where(s => s.ScaleId == ViewModel.ScaleId).FirstOrDefault();
var scale = App.Scales.Where(s => s.ScaleTerminalId == ViewModel.ScaleTerminalId).FirstOrDefault();
if (scale is not ICommandScale cs) return;
cs.Empty();
}
@@ -799,9 +794,7 @@ namespace Elwig.Windows {
);
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Lieferung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Lieferung aktualisieren", exc);
FinishButton.IsEnabled = true;
SaveButton.IsEnabled = true;
Mouse.OverrideCursor = null;
@@ -837,9 +830,7 @@ namespace Elwig.Windows {
);
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Lieferung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Lieferung aktualisieren", exc);
FinishButton.IsEnabled = true;
SaveButton.IsEnabled = true;
Mouse.OverrideCursor = null;
@@ -869,9 +860,8 @@ namespace Elwig.Windows {
private async void CancelCreatingButton_Click(object sender, RoutedEventArgs evt) {
if (IsCreating && HasChanged) {
var r = MessageBox.Show("Soll der Vorgang wirklich abgebrochen werden?", "Abbrechen bestätigen",
MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r != MessageBoxResult.Yes) return;
if (!InteractionService.AskConfirmation("Abbrechen bestätigen", "Soll der Vorgang wirklich abgebrochen werden?"))
return;
}
using var ctx = new AppDbContext();
@@ -952,9 +942,7 @@ namespace Elwig.Windows {
}
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Lieferung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Lieferung aktualisieren", exc);
}
Mouse.OverrideCursor = null;
}
@@ -965,7 +953,7 @@ namespace Elwig.Windows {
ViewModel.Weight = res?.Item1;
ManualWeighingInput.IsChecked = true;
ViewModel.ManualWeighingReason = res?.Item2;
ViewModel.ScaleId = null;
ViewModel.ScaleTerminalId = null;
ViewModel.WeighingData = null;
}
@@ -1001,18 +989,13 @@ namespace Elwig.Windows {
if (DeliveryList.SelectedItem is not Delivery d)
return;
var r = MessageBox.Show(
$"Soll die Lieferung {d.LsNr} ({d.Member.AdministrativeName}, MgNr. {d.Member.MgNr}) wirklich unwiderruflich gelöscht werden?",
"Lieferung löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (r == MessageBoxResult.OK) {
if (InteractionService.AskContinue("Lieferung löschen", $"Soll die Lieferung {d.LsNr} ({d.Member.AdministrativeName}, MgNr. {d.Member.MgNr}) wirklich unwiderruflich gelöscht werden?")) {
Mouse.OverrideCursor = Cursors.Wait;
try {
await DeliveryService.DeleteDelivery(d.LsNr);
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Lieferung löschen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Lieferung löschen", exc);
}
Mouse.OverrideCursor = null;
}
@@ -1040,9 +1023,7 @@ namespace Elwig.Windows {
);
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Lieferung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Lieferung aktualisieren", exc);
SaveButton.IsEnabled = true;
Mouse.OverrideCursor = null;
return;
@@ -1220,7 +1201,7 @@ namespace Elwig.Windows {
}
private async Task UpdateLsNr() {
if (string.IsNullOrEmpty(ViewModel.Date) || ViewModel.Branch == null) {
if (string.IsNullOrWhiteSpace(ViewModel.Date) || ViewModel.Branch == null) {
ViewModel.LsNr = "";
} else {
try {
@@ -1279,8 +1260,7 @@ namespace Elwig.Windows {
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);
InteractionService.ShowWarning("Kein Qualitätswein", "Die eingegebene Sorte darf nicht als Qualitätswein\nübernommen werden!");
});
}
}
+3 -10
View File
@@ -309,10 +309,7 @@ namespace Elwig.Windows {
private async void DeleteDeliveryAncmtButton_Click(object? sender, RoutedEventArgs? evt) {
if (ViewModel.SelectedDeliveryAncmt is not DeliveryAncmt a)
return;
var r = MessageBox.Show(
$"Soll die Anmeldung wirklich unwiderruflich gelöscht werden?",
"Anmeldung löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (r == MessageBoxResult.OK) {
if (InteractionService.AskContinue("Anmeldung löschen", "Soll die Anmeldung wirklich unwiderruflich gelöscht werden?")) {
Mouse.OverrideCursor = Cursors.Wait;
try {
await Task.Run(async () => {
@@ -322,9 +319,7 @@ namespace Elwig.Windows {
});
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Anmeldung löschen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Anmeldung löschen", exc);
}
Mouse.OverrideCursor = null;
}
@@ -347,9 +342,7 @@ namespace Elwig.Windows {
(year, dsnr, mgnr, sortid) = await ViewModel.UpdateDeliveryAncmt(s?.Year, s?.DsNr, s?.MgNr, s?.SortId, s?.Type);
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Anmeldung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Anmeldung aktualisieren", exc);
SaveButton.IsEnabled = true;
return;
} finally {
@@ -177,10 +177,7 @@ namespace Elwig.Windows {
private async void DeleteDeliveryScheduleButton_Click(object? sender, RoutedEventArgs? evt) {
if (ViewModel.SelectedDeliverySchedule is not DeliverySchedule s)
return;
var r = MessageBox.Show(
$"Soll der Leseplan \"{s.Description}\" vom {s.Date:dd.MM.yyyy} wirklich unwiderruflich gelöscht werden?",
"Leseplan löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (r == MessageBoxResult.OK) {
if (InteractionService.AskContinue("Leseplan löschen", $"Soll der Leseplan \"{s.Description}\" vom {s.Date:dd.MM.yyyy} wirklich unwiderruflich gelöscht werden?")) {
Mouse.OverrideCursor = Cursors.Wait;
try {
await Task.Run(async () => {
@@ -190,9 +187,7 @@ namespace Elwig.Windows {
});
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Leseplan löschen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Leseplan löschen", exc);
}
Mouse.OverrideCursor = null;
}
@@ -212,9 +207,7 @@ namespace Elwig.Windows {
await ViewModel.UpdateDeliverySchedule(ViewModel.SelectedDeliverySchedule?.Year, ViewModel.SelectedDeliverySchedule?.DsNr);
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Leseplan aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Leseplan aktualisieren", exc);
SaveButton.IsEnabled = true;
return;
} finally {
+11 -15
View File
@@ -3,6 +3,7 @@ using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Elwig.Models.Dtos;
using Elwig.Models.Entities;
using Elwig.Services;
using MailKit.Net.Smtp;
using Microsoft.EntityFrameworkCore;
using Microsoft.Win32;
@@ -690,7 +691,7 @@ namespace Elwig.Windows {
PrintButton.IsEnabled = PrintDocument != null && !hasPreviewDocs;
EmailButton.IsEnabled = EmailDocuments != null && !hasPreviewDocs && App.Config.Smtp != null;
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
} finally {
UnlockInputs();
GenerateButton.IsEnabled = true;
@@ -774,7 +775,7 @@ namespace Elwig.Windows {
throw new NotImplementedException("Invalid DocType");
}
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
return [];
} finally {
App.MainDispatcher.Invoke(() => {
@@ -787,9 +788,9 @@ namespace Elwig.Windows {
var hasPreviewDocs = memberDocs.Any(m => m.Docs.Any(d => d.Doc.IsPreview));
if (hasPreviewDocs) {
var res = MessageBox.Show("Einige der ausgewählten Dokumente sind nur als vorläufig zu betrachten und können daher nicht verschickt/ausgedruckt werden!\n\nDies könnte an einer noch nicht festgesetzen Auszahlungsvariante liegen oder daran, dass nicht alle Adressaten/Empfänger eine Traubengutschrift erhalten\n(\"Empfänger von Gutschriften\").\n\nSoll mit dem Generieren fortgefahren werden?",
"Vorläufige Dokumente", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (res != MessageBoxResult.OK) {
if (!InteractionService.AskContinue("Vorläufige Dokumente",
"Einige der ausgewählten Dokumente sind nur als vorläufig zu betrachten und können daher nicht verschickt/ausgedruckt werden!\n\n" +
"Dies könnte an einer noch nicht festgesetzen Auszahlungsvariante liegen oder daran, dass nicht alle Adressaten/Empfänger eine Traubengutschrift erhalten\n(\"Empfänger von Gutschriften\").\n\nSoll mit dem Generieren fortgefahren werden?")) {
throw new OperationCanceledException("Dokumentenerzeugung abgebrochen!");
}
}
@@ -892,9 +893,7 @@ namespace Elwig.Windows {
GenerateButton.IsEnabled = false;
LockInputs();
var res = MessageBox.Show($"Sollen {PrintDocument.Pages} Blätter ({PrintDocument.TotalPages} Seiten) gedruckt werden?",
"Rundschreiben drucken", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No);
if (res == MessageBoxResult.Yes) {
if (InteractionService.AskQuestion("Rundschreiben drucken", $"Sollen {PrintDocument.Pages} Blätter ({PrintDocument.TotalPages} Seiten) gedruckt werden?", false)) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
if (App.Config.Debug) {
@@ -932,11 +931,8 @@ namespace Elwig.Windows {
client = await Task.Run(Utils.GetSmtpClient);
Mouse.OverrideCursor = null;
var res = MessageBox.Show($"Sollen {EmailDocuments.Count:N0} E-Mails verschickt werden?",
"Rundschreiben verschicken", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No);
if (res != MessageBoxResult.Yes) {
if (!InteractionService.AskQuestion("Rundschreiben verschicken", $"Sollen {EmailDocuments.Count:N0} E-Mails verschickt werden?", false))
return;
}
Mouse.OverrideCursor = Cursors.Wait;
var subject = EmailSubjectInput.Text;
@@ -962,13 +958,13 @@ namespace Elwig.Windows {
m.EmailAddresses.OrderBy(a => a.Nr).Select(a => a.Address).ToArray(),
subject, docs.Select(d => d.Title).ToArray()
)]);
await Task.Delay((int)(App.Config.SmtpWait * 1000));
}
});
MessageBox.Show("Erfolgreich alle E-Mails verschickt!\n\nEs kann einige Minuten dauern, bis die E-Mails in den Posteingängen der Empfänger aufscheinen.", "Rundschreiben verschicken",
MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Rundschreiben verschicken", "Erfolgreich alle E-Mails verschickt!\n\nEs kann einige Minuten dauern, bis die E-Mails in den Posteingängen der Empfänger aufscheinen.");
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
} finally {
if (client != null)
await client.DisconnectAsync(true);
+75 -85
View File
@@ -59,9 +59,7 @@ namespace Elwig.Windows {
}
Thread.Sleep(100);
if (App.NumWindows > 1 && !App.Current.Windows.Cast<Window>().Any(w => ((w as AdministrationWindow)?.IsEditing ?? false) || ((w as AdministrationWindow)?.IsCreating ?? false))) {
var res = MessageBox.Show("Es sind noch weitere Fenster geöffnet.\nSollen alle Fenster geschlossen werden?",
"Elwig beenden", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (res != MessageBoxResult.Yes) {
if (!InteractionService.AskConfirmation("Elwig beenden", "Es sind noch weitere Fenster geöffnet.\nSollen alle Fenster geschlossen werden?")) {
evt.Cancel = true;
} else {
Application.Current.Shutdown();
@@ -84,9 +82,9 @@ namespace Elwig.Windows {
try {
using var client = await Utils.GetSmtpClient();
await client!.DisconnectAsync(true);
MessageBox.Show("E-Mail-Einstellungen erfolgreich überprüft!", "Erfolg", MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Erfolg", "E -Mail-Einstellungen erfolgreich überprüft!");
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
Mouse.OverrideCursor = null;
}
@@ -115,17 +113,17 @@ namespace Elwig.Windows {
private async void Menu_Scale_SetDateTime_Click(object sender, RoutedEventArgs evt) {
if (App.CommandScales.Count == 0) {
MessageBox.Show("Es sind keine geeigneten Waagen verfügbar!", "Datum und Uhrzeit setzen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Datum und Uhrzeit setzen", "Es sind keine geeigneten Waagen verfügbar!");
return;
}
foreach (var s in App.CommandScales) {
try {
await s.SetDateAndTime(DateTime.Now);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
}
MessageBox.Show("Datum und Uhrzeit auf entsprechenden Waagen gesetzt!", "Datum und Uhrzeit setzen", MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Datum und Uhrzeit setzen", "Datum und Uhrzeit auf entsprechenden Waagen gesetzt!");
}
private void Menu_Database_Query_Click(object sender, RoutedEventArgs evt) {
@@ -157,29 +155,23 @@ namespace Elwig.Windows {
});
}
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
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";
var filename = InteractionService.SaveFile("Datenbank-Sicherung", $"database_{Utils.Today:yyyy-MM-dd}", "sql.zip");
if (filename != null) {
if (!filename.EndsWith(".sql.zip")) filename += ".sql.zip";
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
await Database.ExportSql(d.FileName, true);
await Database.ExportSql(filename, true);
});
}
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
Mouse.OverrideCursor = null;
}
@@ -192,16 +184,13 @@ namespace Elwig.Windows {
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)
if (!InteractionService.AskContinue("Datenbank wiederherstellen", "Soll die Datenbank wirklich unwiederruflich durch die wiederhergestellte Version ersetzt werden?"))
return;
Mouse.OverrideCursor = Cursors.Wait;
await App.ReplaceDatabase(d.FileName);
}
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
Mouse.OverrideCursor = null;
}
@@ -211,8 +200,13 @@ namespace Elwig.Windows {
return;
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
await SyncService.Download(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
await SyncService.UploadModified(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
if (App.Config.SyncMain) {
await SyncService.Download(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword, true);
await SyncService.UploadModified(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
} else {
await SyncService.Download(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword, false);
await SyncService.UploadBranchDeliveries(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
}
});
Mouse.OverrideCursor = null;
}
@@ -222,7 +216,7 @@ namespace Elwig.Windows {
return;
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
await SyncService.Download(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
await SyncService.Download(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword, App.Config.SyncMain);
});
Mouse.OverrideCursor = null;
}
@@ -240,6 +234,10 @@ namespace Elwig.Windows {
private async void Menu_Sync_UploadModified_Click(object sender, RoutedEventArgs evt) {
if (App.Config.SyncUrl == null)
return;
if (!App.Config.SyncMain) {
InteractionService.ShowInformation("Mitglieder und Lieferungen hochladen", "Hochladen nicht möglich: Dieses Gerät ist nicht das Hauptgerät!");
return;
}
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
await SyncService.UploadModified(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
@@ -266,15 +264,15 @@ namespace Elwig.Windows {
.ToList();
if (files.Count == 0) {
MessageBox.Show("Die Datenbank wurde noch nicht vom Hauptgerät hochgeladen!", "Datenbank herunterladen",
MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Datenbank herunterladen", "Die Datenbank wurde noch nicht vom Hauptgerät hochgeladen!");
return;
}
var file = files[0];
var res = MessageBox.Show($"Es wurde eine komprimierte Datenbank (ca. {file.Size / 1024 / 1024} MB) vom {file.Timestamp:dd.MM.yyyy, HH:mm} gefunden.\n\nWollen Sie wirklich die aktuelle Datenbank unwiederruflich\nlöschen und durch die gefundene ersetzen?\n\nDas kann zu Datenverlust führen!", "Datenbank herunterladen",
MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (res != MessageBoxResult.OK)
if (!InteractionService.AskContinue("Datenbank herunterladen",
$"Es wurde eine komprimierte Datenbank (ca. {file.Size / 1024 / 1024} MB) vom {file.Timestamp:dd.MM.yyyy, HH:mm} gefunden.\n\n" +
$"Wollen Sie wirklich die aktuelle Datenbank unwiederruflich\nlöschen und durch die gefundene ersetzen?\n\n" +
$"Das kann zu Datenverlust führen!"))
return;
var filename = Path.Combine(App.TempPath, file.Name);
@@ -283,20 +281,21 @@ namespace Elwig.Windows {
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)
if (!InteractionService.AskContinue("Datenbank herunterladen",
"Die Datenbank wurde erfolgreich heruntergeladen!\n\n" +
"Soll die Datenbank wirklich unwiederruflich ersetzt werden?\n\n" +
"Wenn Sie unsicher sind sprechen Sie sich mit dem Benutzer des Hauptgerätes ab!"))
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);
InteractionService.ShowException("Datenbank herunterladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (TaskCanceledException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Datenbank herunterladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Datenbank herunterladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Datenbank herunterladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Datenbank herunterladen", exc);
}
});
Mouse.OverrideCursor = null;
@@ -306,9 +305,12 @@ namespace Elwig.Windows {
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)
if (!App.Config.SyncMain) {
InteractionService.ShowInformation("Datenbank hochladen", "Hochladen nicht möglich: Dieses Gerät ist nicht das Hauptgerät!");
return;
}
if (!InteractionService.AskContinue("Datenbank hochladen", "Sind Sie wirklich sicher, dass Sie die Datenbank dieses\nGerätes hochladen möchten? Das sollte nur vom Hauptgerät aus passieren!"))
return;
Mouse.OverrideCursor = Cursors.Wait;
@@ -317,14 +319,13 @@ namespace Elwig.Windows {
var path = Path.Combine(App.TempPath, "database.sql.zip");
await Database.ExportSql(path, true);
await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
MessageBox.Show($"Hochladen der gesamten Datenbank erfolgreich!", "Datenbank hochladen",
MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Datenbank hochladen", $"Hochladen der gesamten Datenbank erfolgreich!");
} catch (HttpRequestException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Datenbank hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Datenbank hochladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (TaskCanceledException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Datenbank hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Datenbank hochladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Datenbank hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Datenbank hochladen", exc);
}
});
Mouse.OverrideCursor = null;
@@ -380,7 +381,11 @@ namespace Elwig.Windows {
await Task.Delay(delay);
var ch = false;
using (var ctx = new AppDbContext()) {
ch = await SyncService.ChangesAvailable(ctx, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
if (App.Config.SyncMain) {
ch = await SyncService.ChangesAvailable(ctx, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
} else {
ch = await SyncService.ChangesAvailable(ctx, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword, default);
}
}
await App.MainDispatcher.BeginInvoke(() => {
if (ch) {
@@ -457,13 +462,8 @@ namespace Elwig.Windows {
private async void OverUnderDeliveryButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year)
return;
var d = new SaveFileDialog() {
FileName = $"Über-Unterlieferungen-{year}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"Über-/Unterlieferungen {year} speichern unter - Elwig"
};
if (d.ShowDialog() == false)
var filename = InteractionService.SaveFile($"Über-/Unterlieferungen {year}", $"Über-Unterlieferungen-{year}", "ods");
if (filename == null)
return;
Mouse.OverrideCursor = Cursors.Wait;
@@ -477,11 +477,16 @@ namespace Elwig.Windows {
using var ctx = new AppDbContext();
var tbl1 = await OverUnderDeliveryData.ForSeason(ctx.OverUnderDeliveryRows, year);
var tbl2 = await AreaComUnderDeliveryData.ForSeason(ctx.AreaComUnderDeliveryRows, year);
using var ods = new OdsFile(d.FileName);
await ods.AddTable(tbl1);
using var ods = new OdsFile(filename);
if (App.Client.HasRedWhite) {
await ods.AddTable(tbl1.Red);
await ods.AddTable(tbl1.White);
} else {
await ods.AddTable(tbl1.Total);
}
await ods.AddTable(tbl2);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
});
Mouse.OverrideCursor = null;
@@ -490,13 +495,8 @@ namespace Elwig.Windows {
private async void BreakdownButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year)
return;
var d = new SaveFileDialog() {
FileName = $"Aufschlüsselung-{year}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"Sorten-/Qualitätsaufschlüsselung {year} speichern unter - Elwig"
};
if (d.ShowDialog() == false)
var filename = InteractionService.SaveFile($"Sorten-/Qualitätsaufschlüsselung {year}", $"Aufschlüsselung-{year}", "ods");
if (filename == null)
return;
Mouse.OverrideCursor = Cursors.Wait;
@@ -508,7 +508,7 @@ namespace Elwig.Windows {
App.HintContextChange();
using var ctx = new AppDbContext();
using var ods = new OdsFile(d.FileName);
using var ods = new OdsFile(filename);
var tblTotal = await WeightBreakdownData.ForSeason(ctx.WeightBreakDownRows, year);
await ods.AddTable(tblTotal);
foreach (var branch in await ctx.FetchBranches().ToListAsync()) {
@@ -516,7 +516,7 @@ namespace Elwig.Windows {
await ods.AddTable(tbl);
}
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
});
Mouse.OverrideCursor = null;
@@ -525,13 +525,8 @@ namespace Elwig.Windows {
private async void AreaCommitmentsButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year)
return;
var d = new SaveFileDialog() {
FileName = $"Flächenbindungen-{year}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"Flächenbindungen {year} speichern unter - Elwig"
};
if (d.ShowDialog() == false)
var filename = InteractionService.SaveFile($"Flächenbindungen {year}", $"Flächenbindungen-{year}", "ods");
if (filename == null)
return;
Mouse.OverrideCursor = Cursors.Wait;
@@ -544,10 +539,10 @@ namespace Elwig.Windows {
using var ctx = new AppDbContext();
var tbl = await MemberAreaComsData.ForSeason(ctx.MemberAreaComsRows, year);
using var ods = new OdsFile(d.FileName);
using var ods = new OdsFile(filename);
await ods.AddTable(tbl);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
});
Mouse.OverrideCursor = null;
@@ -556,13 +551,8 @@ namespace Elwig.Windows {
private async void BreakdownMemberVarietyButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year)
return;
var d = new SaveFileDialog() {
FileName = $"Liefermengen-Ertrag-{year}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"Liefermengen/Ertrag {year} speichern unter - Elwig"
};
if (d.ShowDialog() == false)
var filename = InteractionService.SaveFile($"Liefermengen/Ertrag {year}", $"Liefermengen-Ertrag-{year}", "ods");
if (filename == null)
return;
Mouse.OverrideCursor = Cursors.Wait;
@@ -575,10 +565,10 @@ namespace Elwig.Windows {
using var ctx = new AppDbContext();
var tbl = await MemberDeliveryYieldsPerVarietyData.ForSeason(ctx.MemberDeliveryPerVariantRows, year);
using var ods = new OdsFile(d.FileName);
using var ods = new OdsFile(filename);
await ods.AddTable(tbl);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
});
Mouse.OverrideCursor = null;
+38 -19
View File
@@ -233,7 +233,7 @@
</DataGridTextColumn>
<DataGridTextColumn Header="Nachname" Binding="{Binding Name}" Width="140"/>
<DataGridTextColumn Header="Vorname" Binding="{Binding GivenName}" Width="140"/>
<DataGridTextColumn Header="GA" Binding="{Binding BusinessShares, StringFormat='{}{0} '}" Width="40">
<DataGridTextColumn Header="GA" Binding="{Binding SharesActive, StringFormat='{}{0} '}" Width="40">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
@@ -619,16 +619,27 @@
Margin="0,40,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="DateInput_TextChanged" LostFocus="DateInput_LostFocus"/>
<Label Content="Geschäftsanteile:" Margin="10,70,0,0" Grid.Column="0"/>
<TextBox x:Name="BusinessSharesInput" Text="{Binding BusinessSharesString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,70,10,0" Width="48" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="IntegerInput_TextChanged"/>
<Label Content="BH-Konto:" Margin="10,100,0,0" Grid.Column="0"/>
<Label Content="BH-Konto:" Margin="10,70,0,0" Grid.Column="0"/>
<TextBox x:Name="AccountingNrInput" Text="{Binding AccountingNr, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,100,10,0" Grid.Column="1"
Margin="0,70,10,0" Width="63" Grid.Column="1" HorizontalAlignment="Left"
TextChanged="TextBox_TextChanged"/>
<Label Content="Geschäftsanteile:" Margin="10,95,0,0" Grid.Column="0"/>
<Label x:Name="BusinessSharesLabel" Content="(rot/weiß/ruhend)" Margin="10,108,0,0" Grid.Column="0" FontSize="10"/>
<TextBox x:Name="BusinessShares1Input" Text="{Binding BusinessShares1String, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,100,10,0" Width="32" Grid.Column="1" Grid.ColumnSpan="2" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="IntegerInput_TextChanged"/>
<TextBox x:Name="BusinessShares2Input" Text="{Binding BusinessShares2String, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="37,100,10,0" Width="32" Grid.Column="1" Grid.ColumnSpan="2" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="IntegerInput_TextChanged"/>
<TextBox x:Name="BusinessShares3Input" Text="{Binding BusinessShares3String, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="74,100,10,0" Width="32" Grid.Column="1" Grid.ColumnSpan="2" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="IntegerInput_TextChanged"/>
<Button x:Name="BusinessSharesButton" FontFamily="Segoe MDL2 Assets" Content="&#xEB05;" FontSize="16" Padding="0.5,0,0,0" IsEnabled="{Binding IsMemberSelected}"
Margin="111,100,10,0" Width="25" Height="25" Grid.Column="1" Grid.ColumnSpan="2" HorizontalAlignment="Left" VerticalAlignment="Top"
ToolTip="Geschäftsanteile bearbeiten"
Click="BusinessSharesButton_Click"/>
<CheckBox x:Name="ActiveInput" Content="Aktiv" IsChecked="{Binding IsActive, Mode=TwoWay}" IsEnabled="False"
Checked="ActiveInput_Changed" Unchecked="ActiveInput_Changed"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,15,0,0" VerticalAlignment="Top"/>
@@ -656,7 +667,7 @@
<Label Content="Anmerkung:" Margin="10,190,0,0" Grid.Column="0"/>
<TextBox x:Name="CommentInput" Text="{Binding Comment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,190,10,70" Grid.Column="1" Grid.ColumnSpan="2"
Margin="0,190,10,70" Grid.Column="1" Grid.ColumnSpan="2" FontSize="12"
TextChanged="TextBox_TextChanged"
VerticalAlignment="Stretch" Height="auto" AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible"/>
@@ -695,12 +706,24 @@
</TextBlock.ToolTip>
</TextBlock>
<Button x:Name="DeliveryButton" Content="Lieferungen" IsEnabled="{Binding IsMemberSelected}"
<Button x:Name="DeliveryButton" IsEnabled="{Binding IsMemberSelected}"
Click="DeliveryButton_Click"
HorizontalAlignment="Right" Margin="10,00,10,37" Width="150" VerticalAlignment="Bottom" Grid.ColumnSpan="3"/>
<Button x:Name="AreaCommitmentButton" Content="Flächenbindungen" IsEnabled="{Binding IsMemberSelected}"
HorizontalAlignment="Right" Margin="10,00,10,37" Width="160" VerticalAlignment="Bottom" Grid.ColumnSpan="3" HorizontalContentAlignment="Stretch">
<Grid>
<TextBlock Margin="0,0,10,0" TextAlignment="Center">Lieferungen</TextBlock>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE8A7;"
TextAlignment="Left" HorizontalAlignment="Right" Padding="0,1.5,0,0" Margin="0,0,0,0"/>
</Grid>
</Button>
<Button x:Name="AreaCommitmentButton" IsEnabled="{Binding IsMemberSelected}"
Click="AreaCommitmentButton_Click"
HorizontalAlignment="Right" Margin="10,10,10,5" Width="150" VerticalAlignment="Bottom" Grid.ColumnSpan="3"/>
HorizontalAlignment="Right" Margin="10,10,10,5" Width="160" VerticalAlignment="Bottom" Grid.ColumnSpan="3" HorizontalContentAlignment="Stretch">
<Grid>
<TextBlock Margin="0,0,15,0" TextAlignment="Center">Flächenbindungen</TextBlock>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE8A7;"
TextAlignment="Left" HorizontalAlignment="Right" Padding="0,1.5,0,0" Margin="0,0,0,0"/>
</Grid>
</Button>
</Grid>
</GroupBox>
</Grid>
@@ -712,7 +735,7 @@
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="Auto"/>
@@ -729,11 +752,7 @@
</TextBlock>
</StatusBarItem>
<Separator Grid.Column="1"/>
<StatusBarItem Grid.Column="2">
<TextBlock ToolTip="{Binding StatusBusinessSharesToolTip}">
Geschäftsanteile: <Run Text="{Binding StatusBusinessShares}"/>
</TextBlock>
</StatusBarItem>
<StatusBarItem Grid.Column="2" Content="{Binding StatusBusinessShares}"/>
<Separator Grid.Column="3"/>
<StatusBarItem Grid.Column="4">
<TextBlock ToolTip="{Binding StatusAreaCommitmentToolTip}">
+183 -53
View File
@@ -1,18 +1,21 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Elwig.Dialogs;
using Elwig.Documents;
using Elwig.Helpers;
using Elwig.Models.Entities;
using System.Threading.Tasks;
using Elwig.Documents;
using System.Diagnostics;
using Elwig.Dialogs;
using Elwig.Services;
using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
namespace Elwig.Windows {
public partial class MemberAdminWindow : AdministrationWindow {
@@ -37,13 +40,20 @@ namespace Elwig.Windows {
CommandBindings.Add(new CommandBinding(CtrlO, Menu_List_ShowActive_Click));
CommandBindings.Add(new CommandBinding(CtrlShiftP, Menu_MemberDataSheet_Print_Click));
CommandBindings.Add(new CommandBinding(CtrlShiftO, Menu_List_PrintActive_Click));
ExemptInputs = [
List<Control> exempt = [
SearchInput, ActiveMemberInput, MemberList,
];
if (App.Client.EnableMemberHistory) {
exempt.AddRange([BusinessShares1Input, BusinessShares2Input, BusinessShares3Input]);
BusinessShares1Input.IsReadOnly = true;
BusinessShares2Input.IsReadOnly = true;
BusinessShares3Input.IsReadOnly = true;
}
ExemptInputs = [.. exempt];
RequiredInputs = [
MgNrInput, GivenNameInput, NameInput,
AddressInput, PlzInput, OrtInput, BillingOrtInput,
BusinessSharesInput, BranchInput, DefaultKgInput
BusinessShares1Input, BusinessShares2Input, BusinessShares3Input, BranchInput, DefaultKgInput
];
EmailAddressInputs = [
(EmailAddress1Label, EmailAddress1Input),
@@ -84,6 +94,29 @@ namespace Elwig.Windows {
Menu_Export_UploadAll.IsEnabled = App.Config.SyncUrl != null;
ViewModel.ShowOnlyActiveMembers = true;
if (App.Client.HasRedWhite) {
BusinessSharesLabel.Content = "(rot/weiß/ruhend)";
//BusinessShares1Input.Background = Brushes.MistyRose;
BusinessShares1Input.Foreground = Brushes.DarkRed;
BusinessShares1Input.ToolTip = "Geschäftsanteile (rot)";
//BusinessShares2Input.Background = Brushes.MintCream;
BusinessShares2Input.Foreground = Brushes.DarkGreen;
BusinessShares2Input.ToolTip = "Geschäftsanteile (weiß)";
BusinessShares3Input.Background = Brushes.WhiteSmoke;
BusinessShares3Input.ToolTip = "Geschäftsanteile (ruhend)";
} else {
BusinessSharesLabel.Content = "(normal/ruhend)";
BusinessShares1Input.ToolTip = "Geschäftsanteile (normal)";
BusinessShares2Input.Background = Brushes.WhiteSmoke;
BusinessShares2Input.ToolTip = "Geschäftsanteile (ruhend)";
BusinessShares3Input.Visibility = Visibility.Hidden;
BusinessSharesButton.Margin = new(74, 100, 10, 0);
}
if (!App.Client.EnableMemberHistory) {
BusinessSharesButton.Visibility = Visibility.Hidden;
}
UpdateContactInfoVisibility();
}
@@ -114,7 +147,7 @@ namespace Elwig.Windows {
var cursor = Mouse.OverrideCursor != null;
if (!cursor) Mouse.OverrideCursor = Cursors.Wait;
var query = (vm.SearchQuery, vm.ShowOnlyActiveMembers);
var (members, totalMemberCount, totalBusinessShares) = await Task.Run(async () => {
var (members, stat) = await Task.Run(async () => {
using var ctx = new AppDbContext();
var (_, memberQuery, filter) = await vm.GetFilters(ctx);
var members = await memberQuery
@@ -141,10 +174,17 @@ namespace Elwig.Windows {
.ThenBy(m => m.MgNr)];
}
var totalMemberCount = await ctx.Members.CountAsync();
var totalBusinessShares = await ctx.Members.SumAsync(m => m.BusinessShares);
var stat = (await ctx.Members.GroupBy(m => 1).Select(g => new {
Count = g.Count(),
Shares = g.Sum(m => m.Shares),
SharesRed = g.Sum(m => m.SharesRed),
SharesWhite = g.Sum(m => m.SharesWhite),
SharesDormant = g.Sum(m => m.SharesDormant),
SharesTotal = g.Sum(m => m.Shares + m.SharesRed + m.SharesWhite + m.SharesDormant),
SharesActive = g.Sum(m => m.Shares + m.SharesRed + m.SharesWhite),
}).ToListAsync()).Select(g => (g.Count, g.Shares, g.SharesRed, g.SharesWhite, g.SharesDormant, g.SharesTotal, g.SharesActive)).ToList();
return (members, totalMemberCount, totalBusinessShares);
return (members, stat[0]);
});
if (!cursor) Mouse.OverrideCursor = null;
if (query != (ViewModel.SearchQuery, ViewModel.ShowOnlyActiveMembers)) return;
@@ -154,8 +194,37 @@ namespace Elwig.Windows {
if (updateSort && MemberList.SelectedItem != null)
MemberList.ScrollIntoView(MemberList.SelectedItem);
ViewModel.StatusMembers = $"{members.Count:N0} ({totalMemberCount:N0})";
ViewModel.StatusBusinessShares = $"{members.Sum(m => m.BusinessShares):N0} ({totalBusinessShares:N0})";
ViewModel.StatusMembers = $"{members.Count:N0} ({stat.Count:N0})";
var sA = members.Sum(m => m.Shares);
var rA = members.Sum(m => m.SharesRed);
var wA = members.Sum(m => m.SharesWhite);
var dA = members.Sum(m => m.SharesDormant);
List<(int Count, Brush? Color)> a = [];
if (sA > 0) a.Add((sA, null));
if (rA > 0) a.Add((rA, Brushes.DarkRed));
if (wA > 0) a.Add((wA, Brushes.DarkGreen));
if (a.Count == 0) a.Add((0, null));
List<(int Count, Brush? Color)> b = [];
if (stat.Shares > 0) b.Add((stat.Shares, null));
if (stat.SharesRed > 0) b.Add((stat.SharesRed, Brushes.DarkRed));
if (stat.SharesWhite > 0) b.Add((stat.SharesWhite, Brushes.DarkGreen));
if (a.Count == 0) b.Add((0, null));
Run[] inlines = [
new("Geschäftsanteile: "),
.. a.Select(i => new Run($"{i.Count:N0}") { Foreground = i.Color ?? Brushes.Black }).Join(() => new("+")),
new(" ("),
.. b.Select(i => new Run($"{i.Count:N0}") { Foreground = i.Color ?? Brushes.Black }).Join(() => new("+")),
new(")")
];
if (ViewModel.StatusBusinessShares == null) {
ViewModel.StatusBusinessShares = new();
} else {
ViewModel.StatusBusinessShares.Inlines.Clear();
}
ViewModel.StatusBusinessShares.Inlines.AddRange(inlines);
}
private void RefreshInputs(bool validate = false) {
@@ -179,6 +248,17 @@ namespace Elwig.Windows {
ClearOriginalValues();
ClearDefaultValues();
await ViewModel.InitInputs();
if (App.Client.EnableMemberHistory) {
BusinessShares1Input.Text = "0";
BusinessShares2Input.Text = "0";
BusinessShares3Input.Text = "0";
} else if (App.Client.HasRedWhite) {
BusinessShares3Input.Text = "0";
SetDefaultValue(BusinessShares3Input);
} else {
BusinessShares2Input.Text = "0";
SetDefaultValue(BusinessShares2Input);
}
SetDefaultValue(MgNrInput);
SetDefaultValue(EntryDateInput);
SetDefaultValue(ActiveInput);
@@ -190,7 +270,7 @@ namespace Elwig.Windows {
ControlUtils.RenewItemsSource(BranchInput, await ctx.FetchBranches().ToListAsync());
ControlUtils.RenewItemsSource(DefaultKgInput, await ctx.WbKgs.Select(k => k.AtKg).OrderBy(k => k.Name).ToListAsync());
var font = new System.Windows.Media.FontFamily("Segoe MDL2 Assets");
var font = new FontFamily("Segoe MDL2 Assets");
MenuItem? temp = null;
var seasons = await ctx.Seasons.Include(s => s.PaymentVariants).OrderByDescending(s => s.Year).ToListAsync();
Menu_DeliveryConfirmation.Items.Clear();
@@ -267,10 +347,21 @@ namespace Elwig.Windows {
await RefreshList();
var stat = await ctx.Members.GroupBy(m => m.IsActive).Select(g => new {
IsActive = g.Key,
Count = g.Count(),
Shares = g.Sum(m => m.Shares),
SharesRed = g.Sum(m => m.SharesRed),
SharesWhite = g.Sum(m => m.SharesWhite),
SharesDormant = g.Sum(m => m.SharesDormant),
SharesTotal = g.Sum(m => m.Shares + m.SharesRed + m.SharesWhite + m.SharesDormant),
SharesActive = g.Sum(m => m.Shares + m.SharesRed + m.SharesWhite),
}).ToDictionaryAsync(g => g.IsActive);
var (s1, s2) = ('\u2007', '\u202f');
var mA = $"{await ctx.Members.CountAsync(m => m.IsActive):N0}";
var mI = $"{await ctx.Members.CountAsync(m => !m.IsActive):N0}";
var mT = $"{await ctx.Members.CountAsync():N0}";
var mA = $"{stat.GetValueOrDefault(true)?.Count ?? 0:N0}";
var mI = $"{stat.GetValueOrDefault(false)?.Count ?? 0:N0}";
var mT = $"{stat.Sum(g => g.Value.Count):N0}";
var mM = Math.Max(mA.Length, Math.Max(mI.Length, mT.Length));
var mS = mM > 3;
if (mS) mM--;
@@ -279,16 +370,53 @@ namespace Elwig.Windows {
$"{new string(s1, Math.Max(0, mM - mI.Length))}{(mS && mI.Length < 4 ? s2 : "")}{mI} nicht aktive Mitglieder\n" +
$"{new string(s1, Math.Max(0, mM - mT.Length))}{(mS && mT.Length < 4 ? s2 : "")}{mT} Mitglieder gesamt";
var bA = $"{await ctx.Members.Where(m => m.IsActive).SumAsync(m => m.BusinessShares):N0}";
var bI = $"{await ctx.Members.Where(m => !m.IsActive).SumAsync(m => m.BusinessShares):N0}";
var bT = $"{await ctx.Members.SumAsync(m => m.BusinessShares):N0}";
var bM = Math.Max(bA.Length, Math.Max(bI.Length, bT.Length));
var bS = bM > 3;
if (bS) bM--;
ViewModel.StatusBusinessSharesToolTip =
$"{new string(s1, Math.Max(0, bM - bA.Length))}{(bS && bA.Length < 4 ? s2 : "")}{bA} Geschäftsanteile von aktiven Mitgliedern\n" +
$"{new string(s1, Math.Max(0, bM - bI.Length))}{(bS && bI.Length < 4 ? s2 : "")}{bI} Geschäftsanteile von nicht aktiven Mitgliedern\n" +
$"{new string(s1, Math.Max(0, bM - bT.Length))}{(bS && bT.Length < 4 ? s2 : "")}{bT} Geschäftsanteile gesamt";
var grid = new Grid();
grid.ColumnDefinitions.Add(new() { Width = new(100) });
grid.AddToolTipCell("Geschäftsanteile:", 0, 0, bold: true);
grid.AddToolTipCell("Aktive Mitglieder", 1, 0);
grid.AddToolTipCell("Nicht aktive Mg.", 2, 0);
grid.AddToolTipCell("Alle Mitglieder", 3, 0, bold: true);
if (App.Client.HasRedWhite) {
grid.ColumnDefinitions.Add(new() { Width = new(50) });
grid.ColumnDefinitions.Add(new() { Width = new(50) });
grid.ColumnDefinitions.Add(new() { Width = new(50) });
grid.ColumnDefinitions.Add(new() { Width = new(50) });
grid.AddToolTipCell("rot", 0, 1, alignRight: true);
grid.AddToolTipCell("weiß", 0, 2, alignRight: true);
grid.AddToolTipCell("ruhend", 0, 3, alignRight: true);
grid.AddToolTipCell("gesamt", 0, 4, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(true)?.SharesRed ?? 0:N0}", 1, 1, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(true)?.SharesWhite ?? 0:N0}", 1, 2, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(true)?.SharesDormant ?? 0:N0}", 1, 3, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(true)?.SharesTotal ?? 0:N0}", 1, 4, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(false)?.SharesRed ?? 0:N0}", 2, 1, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(false)?.SharesWhite ?? 0:N0}", 2, 2, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(false)?.SharesDormant ?? 0:N0}", 2, 3, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(false)?.SharesTotal ?? 0:N0}", 2, 4, alignRight: true);
grid.AddToolTipCell($"{stat.Sum(g => g.Value.SharesRed):N0}", 3, 1, alignRight: true, bold: true);
grid.AddToolTipCell($"{stat.Sum(g => g.Value.SharesWhite):N0}", 3, 2, alignRight: true, bold: true);
grid.AddToolTipCell($"{stat.Sum(g => g.Value.SharesDormant):N0}", 3, 3, alignRight: true, bold: true);
grid.AddToolTipCell($"{stat.Sum(g => g.Value.SharesTotal):N0}", 3, 4, alignRight: true, bold: true);
} else {
grid.ColumnDefinitions.Add(new() { Width = new(50) });
grid.ColumnDefinitions.Add(new() { Width = new(50) });
grid.ColumnDefinitions.Add(new() { Width = new(50) });
grid.AddToolTipCell("normal", 0, 1, alignRight: true);
grid.AddToolTipCell("ruhend", 0, 2, alignRight: true);
grid.AddToolTipCell("gesamt", 0, 3, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(true)?.Shares ?? 0:N0}", 1, 1, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(true)?.SharesDormant ?? 0:N0}", 1, 2, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(true)?.SharesTotal ?? 0:N0}", 1, 3, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(false)?.Shares ?? 0:N0}", 2, 1, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(false)?.SharesDormant ?? 0:N0}", 2, 2, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(false)?.SharesTotal ?? 0:N0}", 2, 3, alignRight: true);
grid.AddToolTipCell($"{stat.Sum(g => g.Value.Shares):N0}", 3, 1, alignRight: true, bold: true);
grid.AddToolTipCell($"{stat.Sum(g => g.Value.SharesDormant):N0}", 3, 2, alignRight: true, bold: true);
grid.AddToolTipCell($"{stat.Sum(g => g.Value.SharesTotal):N0}", 3, 3, alignRight: true, bold: true);
}
ViewModel.StatusBusinessShares?.ToolTip = grid;
}
private void SetPhoneNrInputVisible(int nr, bool visible, int? position = null) {
@@ -399,9 +527,7 @@ namespace Elwig.Windows {
await MemberService.DeleteMember(m.MgNr, d.DeletePaymentData, d.DeleteDeliveries, d.DeleteAreaComs);
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Mitglied löschen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Mitglied löschen", exc);
}
Mouse.OverrideCursor = null;
}
@@ -422,9 +548,7 @@ namespace Elwig.Windows {
mgnr = await ViewModel.UpdateMember(ViewModel.SelectedMember?.MgNr);
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Mitglied aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Mitglied aktualisieren", exc);
SaveButton.IsEnabled = true;
Mouse.OverrideCursor = null;
return;
@@ -478,6 +602,10 @@ namespace Elwig.Windows {
ViewModel.EnableSearchInputs = true;
}
private void BusinessSharesButton_Click(object sender, RoutedEventArgs evt) {
App.FocusMemberBusinessSharesWindow(ViewModel.SelectedMember!.MgNr);
}
private void AreaCommitmentButton_Click(object sender, RoutedEventArgs evt) {
App.FocusMemberAreaComs(ViewModel.SelectedMember!.MgNr);
}
@@ -512,7 +640,7 @@ namespace Elwig.Windows {
}
});
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
Mouse.OverrideCursor = null;
}
@@ -534,9 +662,7 @@ namespace Elwig.Windows {
private async void Menu_MemberDataSheet_Email_Click(object sender, RoutedEventArgs evt) {
if (ViewModel.SelectedMember is not Member m) return;
var res = MessageBox.Show("Soll eine E-Mail verschickt werden?", "Stammdatenblatt verschicken",
MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
if (res != MessageBoxResult.Yes)
if (!InteractionService.AskQuestion("Stammdatenblatt verschicken", "Soll eine E-Mail verschickt werden?", true))
return;
await MemberService.GenerateMemberDataSheet(m, ExportMode.Email);
}
@@ -566,9 +692,7 @@ namespace Elwig.Windows {
var year = (int?)((sender as MenuItem)?.Parent as MenuItem)?.Tag;
if (ViewModel.SelectedMember is not Member m || year == null)
return;
var res = MessageBox.Show("Soll eine E-Mail verschickt werden?", "Anlieferungsbestätigung verschicken",
MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
if (res != MessageBoxResult.Yes)
if (!InteractionService.AskQuestion("Anlieferungsbestätigung verschicken", "Soll eine E-Mail verschickt werden?", true))
return;
await MemberService.GenerateDeliveryConfirmation(m, (int)year, ExportMode.Email);
}
@@ -602,9 +726,7 @@ namespace Elwig.Windows {
var avnr = (int?)((sender as MenuItem)?.Parent as MenuItem)?.Tag;
if (ViewModel.SelectedMember is not Member m || year == null || avnr == null)
return;
var res = MessageBox.Show("Soll eine E-Mail verschickt werden?", "Traubengutschrift verschicken",
MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
if (res != MessageBoxResult.Yes)
if (!InteractionService.AskQuestion("Traubengutschrift verschicken", "Soll eine E-Mail verschickt werden?", true))
return;
await MemberService.GenerateCreditNote(m, (int)year, (int)avnr, ExportMode.Email);
}
@@ -726,7 +848,7 @@ namespace Elwig.Windows {
lastVisible = true;
for (int i = 0; i < EmailAddressInputs.Length; i++) {
var input = EmailAddressInputs[i];
var vis = !string.IsNullOrEmpty(input.Address.Text) || (m?.EmailAddresses.Any(a => a.Nr - 1 == i) ?? false);
var vis = !string.IsNullOrWhiteSpace(input.Address.Text) || (m?.EmailAddresses.Any(a => a.Nr - 1 == i) ?? false);
var cVis = vis || (extra && lastVisible);
SetEmailAddressInputVisible(i, cVis, cVis ? num++ : null);
lastVisible = vis;
@@ -734,7 +856,7 @@ namespace Elwig.Windows {
lastVisible = true;
for (int i = 0; i < PhoneNrInputs.Length; i++) {
var input = PhoneNrInputs[i];
var vis = !string.IsNullOrEmpty(input.Number.Text) || (m?.TelephoneNumbers.Any(n => n.Nr - 1 == i) ?? false);
var vis = !string.IsNullOrWhiteSpace(input.Number.Text) || (m?.TelephoneNumbers.Any(n => n.Nr - 1 == i) ?? false);
var cVis = vis || (extra && lastVisible);
SetPhoneNrInputVisible(i, cVis, cVis ? num++ : null);
lastVisible = vis;
@@ -751,6 +873,14 @@ namespace Elwig.Windows {
new protected void ClearInputs(bool validate = false) {
ViewModel.ClearInputs();
if (App.Client.EnableMemberHistory) {
BusinessShares1Input.Text = "";
BusinessShares2Input.Text = "";
BusinessShares3Input.Text = "";
ControlUtils.ClearInputState(BusinessShares1Input);
ControlUtils.ClearInputState(BusinessShares2Input);
ControlUtils.ClearInputState(BusinessShares3Input);
}
base.ClearInputs(validate);
}
@@ -795,9 +925,9 @@ namespace Elwig.Windows {
var oldMember = await ctx.FetchMembers(mgnr).SingleAsync();
var newName = $"{ViewModel.Name?.Replace('ß', 'ẞ').ToUpper()} " +
$"{ViewModel.Prefix}{(!string.IsNullOrEmpty(ViewModel.Prefix) ? " " : "")}" +
$"{ViewModel.GivenName}{(!string.IsNullOrEmpty(ViewModel.GivenName) ? " " : "")}" +
$"{ViewModel.Suffix}{(!string.IsNullOrEmpty(ViewModel.Suffix) ? " " : "")}";
$"{ViewModel.Prefix}{(!string.IsNullOrWhiteSpace(ViewModel.Prefix) ? " " : "")}" +
$"{ViewModel.GivenName}{(!string.IsNullOrWhiteSpace(ViewModel.GivenName) ? " " : "")}" +
$"{ViewModel.Suffix}{(!string.IsNullOrWhiteSpace(ViewModel.Suffix) ? " " : "")}";
var d = new AreaComTransferDialog(oldMember.AdministrativeName, newName, areaComs.Count, areaComs.Sum(c => c.Area));
if (d.ShowDialog() != true)
return;
@@ -0,0 +1,214 @@
<local:AdministrationWindow
x:Class="Elwig.Windows.MemberBusinessSharesAdminWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Elwig.Windows"
xmlns:ctrl="clr-namespace:Elwig.Controls"
xmlns:vm="clr-namespace:Elwig.ViewModels"
Title="{Binding Title}" Height="450" Width="800" MinHeight="400" MinWidth="700">
<Window.DataContext>
<vm:MemberBusinessSharesViewModel/>
</Window.DataContext>
<Window.Resources>
<Style TargetType="Label">
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="Padding" Value="2,4,2,4"/>
<Setter Property="Height" Value="25"/>
</Style>
<Style TargetType="TextBox">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="Height" Value="25"/>
<Setter Property="TextWrapping" Value="NoWrap"/>
</Style>
<Style TargetType="ctrl:UnitTextBox">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="Height" Value="25"/>
<Setter Property="TextWrapping" Value="NoWrap"/>
</Style>
<Style TargetType="ComboBox">
<Setter Property="Height" Value="25"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Top"/>
</Style>
<Style TargetType="Button">
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="9,3"/>
<Setter Property="Height" Value="27"/>
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" MinWidth="300"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="1*" MinWidth="350"/>
</Grid.ColumnDefinitions>
<Grid Margin="5,0,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="42"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListBox x:Name="MemberHistoryList" Margin="5,10,5,0" Grid.ColumnSpan="3"
ItemsSource="{Binding History, Mode=TwoWay}" SelectedItem="{Binding SelectedHistoryEntry, Mode=TwoWay}"
SelectionChanged="MemberHistoryList_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding DateNotice, StringFormat='{}{0:dd.MM.yy}'}" Width="55" TextAlignment="Left" Margin="0,0,0,0"/>
<TextBlock Text="{Binding DateEffective, StringFormat='{}{0:dd.MM.yy}'}" Width="55" TextAlignment="Left" Margin="0,0,0,0"/>
<TextBlock Text="{Binding Shares, StringFormat='{}{0:N0}'}" Width="15" TextAlignment="Right" Margin="0,0,10,0"/>
<TextBlock Text="{Binding PoVReasonPhrase}" TextAlignment="Left" Margin="0,0,0,0"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button x:Name="NewHistoryButton" Content="Neu" Visibility="{Binding ControlButtonsVisibility}"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2"
Click="NewHistoryButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Alt+Einfg</TextBlock>
</Button.ToolTip>
</Button>
<Button x:Name="EditHistoryButton" Content="Bearbeiten" IsEnabled="False" Visibility="{Binding ControlButtonsVisibility}"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2"
Click="EditHistoryButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Strg+B</TextBlock>
</Button.ToolTip>
</Button>
<Button x:Name="DeleteHistoryButton" Content="Löschen" IsEnabled="False" Visibility="{Binding ControlButtonsVisibility}"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2"
Click="DeleteHistoryButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Alt+Entf</TextBlock>
</Button.ToolTip>
</Button>
<Button x:Name="SaveButton" Content="Speichern" IsEnabled="False" Visibility="{Binding EditingButtonsVisibility}"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2"
Click="SaveButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Strg+S</TextBlock>
</Button.ToolTip>
</Button>
<Button x:Name="ResetButton" Content="Zurücksetzen" IsEnabled="False" Visibility="{Binding EditingButtonsVisibility}"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2"
Click="ResetButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Strg+Z</TextBlock>
</Button.ToolTip>
</Button>
<Button x:Name="CancelButton" Content="Abbrechen" IsEnabled="False" Visibility="{Binding EditingButtonsVisibility}" IsCancel="True"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2"
Click="CancelButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Esc</TextBlock>
</Button.ToolTip>
</Button>
</Grid>
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<Grid Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="95"/>
</Grid.RowDefinitions>
<GroupBox Header="Geschäftsanteilbewegung" Margin="5,10,5,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="110"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="Grund:" Margin="10,10,0,0" Grid.Column="0"/>
<ComboBox x:Name="ReasonInput" SelectedValue="{Binding Reason, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,10,10,0" Grid.Column="1"
SelectedValuePath="Tag"
SelectionChanged="ReasonInput_SelectionChanged">
<ComboBoxItem Tag="purchase">Kauf</ComboBoxItem>
<ComboBoxItem Tag="transfer_to">Übertragung an...</ComboBoxItem>
<ComboBoxItem Tag="transfer_from">Übertragung von...</ComboBoxItem>
<ComboBoxItem Tag="prescription">Vorschreibung</ComboBoxItem>
<ComboBoxItem Tag="exclusion">Ausschluss</ComboBoxItem>
<ComboBoxItem Tag="deceased">Tod</ComboBoxItem>
<ComboBoxItem Tag="retirement">Kündigung</ComboBoxItem>
<ComboBoxItem Tag="payout">Auszahlung</ComboBoxItem>
</ComboBox>
<Label Content="Anderes Mitgl.:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="OtherMgNrInput" Text="{Binding OtherMgNrString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Width="48" Grid.Column="1" Margin="0,40,0,0" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="OtherMgNrInput_TextChanged" LostFocus="OtherMgNrInput_LostFocus"/>
<ComboBox x:Name="OtherMemberInput"
Grid.Column="1" Margin="53,40,40,10" IsEditable="True"
ItemTemplate="{StaticResource MemberAdminNameTemplate}" TextSearch.TextPath="AdministrativeName"
SelectionChanged="OtherMemberInput_SelectionChanged"/>
<Button x:Name="OtherMemberReferenceButton" Grid.Column="1" Height="25" Width="25" FontFamily="Segoe MDL2 Assets" Content="&#xEE35;" Padding="0,0,0,0"
Margin="10,40,10,10" VerticalAlignment="Top" HorizontalAlignment="Right" ToolTip="Zu Mitglied springen"
Click="OtherMemberReferenceButton_Click"/>
<Label Content="Anmerkung:" Margin="10,70,0,0" Grid.Column="0"/>
<TextBox x:Name="CommentInput" Text="{Binding Comment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,70,10,0" Grid.Column="1"/>
<Label Content="Meldung:" Margin="10,100,0,0" Grid.Column="0"/>
<TextBox x:Name="DateNoticeInput" Text="{Binding DateNoticeString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,100,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="DateNoticeInput_TextChanged" LostFocus="DateInput_LostFocus"/>
<CheckBox x:Name="HasMemberSignedInput" IsChecked="{Binding MemberHasSigned, Mode=TwoWay}" Content="Von Mg. unterfertigt"
Margin="88,105,10,0" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="TOp"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
<Label Content="Wirksam:" Margin="10,130,0,0" Grid.Column="0"/>
<TextBox x:Name="DateEffeciveInput" Text="{Binding DateEffectiveString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,130,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="DateInput_TextChanged" LostFocus="DateInput_LostFocus"/>
<Label Content="Geschäftsanteile:" Margin="10,160,0,0" Grid.Column="0"/>
<TextBox x:Name="SharesInput" Text="{Binding SharesString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,160,10,0" Width="32" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="SharesInput_TextChanged"/>
<CheckBox x:Name="DeductInput" IsChecked="{Binding Deduct, Mode=TwoWay}"
Margin="42,165,10,0" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="TOp"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed">
<TextBlock>
bei Auszahlung <Run Text="{Binding DeductYearString}"/> abziehen
</TextBlock>
</CheckBox>
<Label Content="Betrag:" Margin="10,190,0,10"/>
<ctrl:UnitTextBox x:Name="ValuePerShareInput" Text="{Binding ValuePerShareString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Unit="€/GA" TextChanged="AmountPerShareInput_TextChanged"
Grid.Column="1" Width="76" Margin="0,190,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ctrl:UnitTextBox x:Name="TotalAmountInput" Text="{Binding TotalAmount}" Unit="€" IsEnabled="False"
Grid.Column="1" Width="77" Margin="81,190,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</Grid>
</GroupBox>
<GroupBox Header="Mitglied" Grid.Row="1" Margin="5,5,5,10">
<Grid>
<TextBlock x:Name="MemberInfo" Text="Name (1234)"
FontSize="14" Margin="10,10,10,10"/>
</Grid>
</GroupBox>
</Grid>
</Grid>
</local:AdministrationWindow>
@@ -0,0 +1,347 @@
using Elwig.Helpers;
using Elwig.Models.Entities;
using Elwig.Services;
using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace Elwig.Windows {
public partial class MemberBusinessSharesAdminWindow : AdministrationWindow {
public MemberBusinessSharesViewModel ViewModel => (MemberBusinessSharesViewModel)DataContext;
public MemberBusinessSharesAdminWindow(int mgnr) {
InitializeComponent();
using var ctx = new AppDbContext();
ViewModel.FilterMember = ctx.Members.Find(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
ViewModel.Title = $"Geschäftsanteilbewegungen - {ViewModel.FilterMember.AdministrativeName} - Elwig";
ExemptInputs = [
MemberHistoryList, TotalAmountInput
];
RequiredInputs = [
ReasonInput, DateNoticeInput, DateEffeciveInput, SharesInput
];
}
public void FocusMemberHistory(int histnr) {
if (IsEditing || IsCreating) return;
var item = ViewModel.History.FirstOrDefault(h => h.HistNr == histnr);
if (item != null) {
ControlUtils.SelectItem(MemberHistoryList, item);
}
}
private async Task RefreshList() {
var vm = ViewModel;
var cursor = Mouse.OverrideCursor != null;
if (!cursor) Mouse.OverrideCursor = Cursors.Wait;
var history = await Task.Run(async () => {
using var ctx = new AppDbContext();
return await ctx.MemberHistory
.Where(h => h.FromMgNr == vm.FilterMember.MgNr || h.ToMgNr == vm.FilterMember.MgNr)
.OrderBy(h => h.DateNoticeString).ThenBy(h => h.HistNr)
.ToListAsync();
});
if (!cursor) Mouse.OverrideCursor = null;
history.ForEach(h => h.PoVMgNr = vm.FilterMember.MgNr);
ControlUtils.RenewItemsSource(MemberHistoryList, history,
MemberHistoryList_SelectionChanged, ControlUtils.RenewSourceDefault.None);
RefreshInputs();
}
private void RefreshInputs(bool validate = false) {
ClearInputStates();
if (ViewModel.SelectedHistoryEntry is MemberHistory h) {
EditHistoryButton.IsEnabled = true;
DeleteHistoryButton.IsEnabled = true;
FillInputs(h);
} else {
EditHistoryButton.IsEnabled = false;
DeleteHistoryButton.IsEnabled = false;
ClearOriginalValues();
ClearDefaultValues();
ClearInputs(validate);
ClearInputStates();
}
GC.Collect();
}
private void FillInputs(MemberHistory h) {
ClearOriginalValues();
ClearDefaultValues();
ViewModel.FillInputs(h);
ReasonInput.SelectedValue = h.Reason + (h.Reason == MemberHistory.REASON_TRANSFER ? $"_{(h.FromMgNr == ViewModel.FilterMember.MgNr ? "to" : "from")}" : "");
FinishInputFilling();
}
private async Task InitInputs() {
ClearOriginalValues();
ClearDefaultValues();
await ViewModel.InitInputs();
SetDefaultValue(DateNoticeInput);
if (ViewModel.ValuePerShare != null)
SetDefaultValue(ValuePerShareInput);
ValidateRequiredInputs();
}
new protected void ClearInputs(bool validate = false) {
ViewModel.ClearInputs();
base.ClearInputs(validate);
}
override protected void UpdateButtons() {
if (!IsEditing && !IsCreating) return;
bool ch = HasChanged, v = IsValid;
ResetButton.IsEnabled = ch;
SaveButton.IsEnabled = v && ch;
}
protected override async Task OnRenewContext(AppDbContext ctx) {
await base.OnRenewContext(ctx);
if (await ctx.FetchMembers(ViewModel.FilterMember.MgNr).SingleOrDefaultAsync() is not Member m) {
Close();
return;
}
ViewModel.FilterMember = m;
ViewModel.Title = $"Geschäftsanteilbewegungen - {ViewModel.FilterMember.AdministrativeName} - Elwig";
ControlUtils.RenewItemsSource(OtherMemberInput, await ctx.FetchMembers(includeNotActive: true).ToListAsync());
MemberInfo.Text = $"{m.FullAdministrativeName}\nGA: {m.Shares:N0} (normal) / {m.SharesRed:N0} (rot) / {m.SharesWhite:N0} (weiß) / {m.SharesDormant:N0} (ruhend)";
await RefreshList();
}
private void MemberHistoryList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
RefreshInputs();
}
protected override void ShortcutNew() {
if (!NewHistoryButton.IsEnabled || NewHistoryButton.Visibility != Visibility.Visible)
return;
NewHistoryButton_Click(null, null);
}
private async void NewHistoryButton_Click(object? sender, RoutedEventArgs? evt) {
IsCreating = true;
MemberHistoryList.IsEnabled = false;
ViewModel.SelectedHistoryEntry = null;
HideNewEditDeleteButtons();
ShowSaveResetCancelButtons();
UnlockInputs();
await InitInputs();
DeductInput.IsEnabled = false;
OtherMgNrInput.IsEnabled = false;
OtherMemberInput.IsEnabled = false;
}
protected override void ShortcutEdit() {
if (!EditHistoryButton.IsEnabled || EditHistoryButton.Visibility != Visibility.Visible)
return;
EditHistoryButton_Click(null, null);
}
private void EditHistoryButton_Click(object? sender, RoutedEventArgs? evt) {
if (ViewModel.SelectedHistoryEntry == null) return;
IsEditing = true;
MemberHistoryList.IsEnabled = false;
HideNewEditDeleteButtons();
ShowSaveResetCancelButtons();
UnlockInputs();
DeductInput.IsEnabled = ViewModel.Reason == MemberHistory.REASON_PURCHASE || ViewModel.Reason == MemberHistory.REASON_PRESCRIPTION;
OtherMgNrInput.IsEnabled = ViewModel.Reason?.StartsWith(MemberHistory.REASON_TRANSFER) ?? false;
OtherMemberInput.IsEnabled = OtherMgNrInput.IsEnabled;
}
protected override void ShortcutDelete() {
if (!DeleteHistoryButton.IsEnabled || DeleteHistoryButton.Visibility != Visibility.Visible)
return;
DeleteHistoryButton_Click(null, null);
}
private async void DeleteHistoryButton_Click(object? sender, RoutedEventArgs? evt) {
if (ViewModel.SelectedHistoryEntry is not MemberHistory h)
return;
if (InteractionService.AskContinueYesNo("Geschäftsanteilbewegung löschen", $"Soll die Geschäftsanteilbewegung wirklich unwiderruflich\ngelöscht werden?\n\nFalls ja:\nSoll die Änderung der aktuellen GA wieder rückgängig gemacht werden?") is bool enableTriggers) {
Mouse.OverrideCursor = Cursors.Wait;
try {
await MemberBusinessSharesService.DeleteMemberHistory(h.HistNr, enableTriggers);
App.HintContextChange();
} catch (Exception exc) {
InteractionService.ShowDbException("Geschäftsanteilbewegung löschen", exc);
}
Mouse.OverrideCursor = null;
}
}
protected override void ShortcutSave() {
if (!SaveButton.IsEnabled || SaveButton.Visibility != Visibility.Visible)
return;
SaveButton_Click(null, null);
}
private async void SaveButton_Click(object? sender, RoutedEventArgs? evt) {
Mouse.OverrideCursor = Cursors.Wait;
SaveButton.IsEnabled = false;
int mgnr;
try {
mgnr = await ViewModel.UpdateMemberHistory(ViewModel.SelectedHistoryEntry?.HistNr, IsEditing || ViewModel.DateNotice >= DateOnly.FromDateTime(Utils.Today).AddDays(-14));
App.HintContextChange();
} catch (Exception exc) {
InteractionService.ShowDbException("Geschäftsanteilbewegung aktualisieren", exc);
SaveButton.IsEnabled = true;
Mouse.OverrideCursor = null;
return;
}
IsEditing = false;
IsCreating = false;
MemberHistoryList.IsEnabled = true;
OtherMgNrInput.IsEnabled = true;
HideSaveResetCancelButtons();
ShowNewEditDeleteButtons();
LockInputs();
FinishInputFilling();
await EnsureContextRenewed();
Mouse.OverrideCursor = null;
if (mgnr is int m)
FocusMemberHistory(m);
}
protected override void ShortcutReset() {
if (!ResetButton.IsEnabled || ResetButton.Visibility != Visibility.Visible)
return;
ResetButton_Click(null, null);
}
private async void ResetButton_Click(object? sender, RoutedEventArgs? evt) {
if (IsEditing) {
RefreshInputs();
} else if (IsCreating) {
ClearInputs();
await InitInputs();
}
UpdateButtons();
}
private void CancelButton_Click(object sender, RoutedEventArgs evt) {
IsEditing = false;
IsCreating = false;
MemberHistoryList.IsEnabled = true;
OtherMgNrInput.IsEnabled = true;
HideSaveResetCancelButtons();
ShowNewEditDeleteButtons();
RefreshInputs();
LockInputs();
}
private void ShowSaveResetCancelButtons() {
SaveButton.IsEnabled = false;
ResetButton.IsEnabled = false;
CancelButton.IsEnabled = true;
ViewModel.EditingButtonsVisibility = Visibility.Visible;
}
private void HideSaveResetCancelButtons() {
SaveButton.IsEnabled = false;
ResetButton.IsEnabled = false;
CancelButton.IsEnabled = false;
ViewModel.EditingButtonsVisibility = Visibility.Hidden;
}
private void ShowNewEditDeleteButtons() {
NewHistoryButton.IsEnabled = true;
EditHistoryButton.IsEnabled = ViewModel.SelectedHistoryEntry != null;
DeleteHistoryButton.IsEnabled = ViewModel.SelectedHistoryEntry != null;
ViewModel.ControlButtonsVisibility = Visibility.Visible;
}
private void HideNewEditDeleteButtons() {
NewHistoryButton.IsEnabled = false;
EditHistoryButton.IsEnabled = false;
DeleteHistoryButton.IsEnabled = false;
ViewModel.ControlButtonsVisibility = Visibility.Hidden;
}
private void OtherMgNrInput_TextChanged(object sender, TextChangedEventArgs evt) {
var valid = InputTextChanged((TextBox)sender, Validator.CheckMgNr);
var text = OtherMgNrInput.Text;
var caret = OtherMgNrInput.CaretIndex;
ControlUtils.SelectItemWithPk(OtherMemberInput, valid ? ViewModel.OtherMgNr : null);
OtherMgNrInput.Text = text;
OtherMgNrInput.CaretIndex = caret;
}
private void OtherMgNrInput_LostFocus(object sender, RoutedEventArgs evt) {
var valid = InputLostFocus((TextBox)sender, Validator.CheckMgNr);
ControlUtils.SelectItemWithPk(OtherMemberInput, valid ? ViewModel.OtherMgNr : null);
}
private void OtherMemberInput_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
var m = OtherMemberInput.SelectedItem as Member;
ViewModel.OtherMgNr = m?.MgNr;
}
private void OtherMemberReferenceButton_Click(object sender, RoutedEventArgs evt) {
if (OtherMemberInput.SelectedItem is not Member m) return;
App.FocusMember(m.MgNr);
}
private void ReasonInput_SelectionChanged(object sender, RoutedEventArgs evt) {
ComboBox_SelectionChanged(sender, evt);
if (!IsEditing && !IsCreating) return;
if (ViewModel.DateNotice is not DateOnly d) return;
if (ViewModel.Reason == MemberHistory.REASON_EXCLUSION || ViewModel.Reason == MemberHistory.REASON_DECEASED || ViewModel.Reason == MemberHistory.REASON_RETIREMENT) {
ViewModel.DateEffectiveString = $"31.12.{d.Year + 1}";
} else {
ViewModel.DateEffectiveString = ViewModel.DateNoticeString;
}
DeductInput.IsEnabled = ViewModel.Reason == MemberHistory.REASON_PURCHASE || ViewModel.Reason == MemberHistory.REASON_PRESCRIPTION;
if (!DeductInput.IsEnabled) DeductInput.IsChecked = false;
OtherMgNrInput.IsEnabled = ViewModel.Reason?.StartsWith(MemberHistory.REASON_TRANSFER) ?? false;
OtherMemberInput.IsEnabled = OtherMgNrInput.IsEnabled;
if (!OtherMgNrInput.IsEnabled) OtherMgNrInput.Text = "";
}
private void DateNoticeInput_TextChanged(object sender, TextChangedEventArgs evt) {
DateInput_TextChanged(sender, evt);
if (!IsEditing && !IsCreating) return;
if (ViewModel.DateNotice is not DateOnly d) return;
ViewModel.DeductYear = d.Year;
if (ViewModel.DateEffective == null && ViewModel.Reason != null) {
if (ViewModel.Reason == MemberHistory.REASON_EXCLUSION || ViewModel.Reason == MemberHistory.REASON_DECEASED || ViewModel.Reason == MemberHistory.REASON_RETIREMENT) {
ViewModel.DateEffectiveString = $"31.12.{d.Year + 1}";
} else {
ViewModel.DateEffectiveString = ViewModel.DateNoticeString;
}
}
}
private void UpdateAmount() {
ViewModel.TotalAmount = $"{ViewModel.Shares * ViewModel.ValuePerShare:N2}";
}
private void SharesInput_TextChanged(object sender, TextChangedEventArgs evt) {
IntegerInput_TextChanged(sender, evt);
UpdateAmount();
}
private void AmountPerShareInput_TextChanged(object sender, TextChangedEventArgs evt) {
InputTextChanged((TextBox)sender, Validator.CheckDecimal((TextBox)sender, false, 3, 2));
UpdateAmount();
}
}
}
+5 -10
View File
@@ -1,5 +1,6 @@
using Elwig.Helpers;
using Elwig.Models.Entities;
using Elwig.Services;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
@@ -192,18 +193,14 @@ namespace Elwig.Windows {
App.HintContextChange();
ControlUtils.SelectItemWithPk(WbGlKgs, k.KgNr);
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Katastralgemeinde aktivieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Katastralgemeinde aktivieren", exc);
}
}
private async void DeactivateKgButton_Click(object sender, RoutedEventArgs e) {
if (WbGlKgs.SelectedItem is not AT_Kg k || k.WbKg == null) return;
var r = MessageBox.Show(
$"Sollen alle Riede und Stammgemeinden-Einträge von der KG {k.Name} wirklich unwiderruflich gelöscht werden?",
"Katastralgemeinde deaktivieren", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r != MessageBoxResult.Yes) return;
if (!InteractionService.AskConfirmation("Katastralgemeinde deaktivieren", $"Sollen alle Riede und Stammgemeinden-Einträge von der KG {k.Name} wirklich unwiderruflich gelöscht werden?"))
return;
try {
using (var ctx = new AppDbContext()) {
ctx.Remove(k.WbKg);
@@ -213,9 +210,7 @@ namespace Elwig.Windows {
ControlUtils.SelectItemWithPk(WbKgs, k.KgNr);
} catch (Exception exc) {
await ForceContextReload();
var str = "Der Eintrag konnte nicht aus der Datenbank gelöscht werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Katastralgemeinde deaktivieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Katastralgemeinde deaktivieren", exc);
}
}
+11 -11
View File
@@ -76,7 +76,7 @@
</DataGridTextColumn>
<DataGridTextColumn Header="Nachname" Binding="{Binding Name}" Width="100"/>
<DataGridTextColumn Header="Vorname" Binding="{Binding GivenName}" Width="90"/>
<DataGridTextColumn Header="GA" Binding="{Binding BusinessShares, StringFormat='{}{0:N0} '}" Width="35">
<DataGridTextColumn Header="GA" Binding="{Binding SharesActive, StringFormat='{}{0:N0} '}" Width="35">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
@@ -104,7 +104,7 @@
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Strafe GA" Binding="{Binding PenaltyBs, Converter={StaticResource CurrencyConverter}, StringFormat='{}{0} '}" Width="65">
<DataGridTextColumn Header="Strafe GA" Binding="{Binding PenaltyShares, Converter={StaticResource CurrencyConverter}, StringFormat='{}{0} '}" Width="65">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
@@ -194,7 +194,7 @@
</Grid>
</GroupBox>
<GroupBox Header="Automatische Nachzeichnung der Geschäftsanteile" Margin="5,10,10,10" Height="180" Width="365"
<GroupBox Header="Automatische Nachzeichnung von Geschäftsanteilen" Margin="5,10,10,10" Height="180" Width="365"
VerticalAlignment="Bottom" HorizontalAlignment="Left">
<Grid>
<Grid.ColumnDefinitions>
@@ -204,28 +204,28 @@
<Label Content="Absoluter Freibetrag:" Margin="10,10,0,0" Grid.ColumnSpan="2"/>
<ctrl:UnitTextBox x:Name="AllowanceKgInput" Unit="kg" Margin="128,10,0,0" Width="70"
TextChanged="KgInput_TextChanged" Grid.Column="1"/>
<ctrl:UnitTextBox x:Name="AllowanceBsInput" Unit="GA" Margin="203,10,0,0" Width="60"
<ctrl:UnitTextBox x:Name="AllowanceShareInput" Unit="GA" Margin="203,10,0,0" Width="60"
TextChanged="PercentInput_TextChanged" Grid.Column="1"/>
<Label Content="Relativer Freibetrag:" Margin="10,40,0,0" Grid.ColumnSpan="2"/>
<ctrl:UnitTextBox x:Name="AllowanceKgPerBsInput" Unit="kg/GA" Margin="128,40,0,0" Width="87"
<ctrl:UnitTextBox x:Name="AllowanceKgPerShareInput" Unit="kg/GA" Margin="128,40,0,0" Width="87"
TextChanged="KgInput_TextChanged" Grid.Column="1"/>
<ctrl:UnitTextBox x:Name="AllowancePercentInput" Unit="%" Margin="220,40,0,0" Width="60"
TextChanged="PercentInput_TextChanged" Grid.Column="1"/>
<Label Content="Nur mind. nachz.:" Margin="10,70,0,0" Grid.ColumnSpan="2"/>
<ctrl:UnitTextBox x:Name="MinBsInput" Unit="GA" Margin="128,70,0,0" Width="50"
TextChanged="BsInput_TextChanged" Grid.Column="1"/>
<ctrl:UnitTextBox x:Name="MinSharesInput" Unit="GA" Margin="128,70,0,0" Width="50"
TextChanged="SharesInput_TextChanged" Grid.Column="1"/>
<Button x:Name="SeasonButton" Content="GA-Wert" Margin="0,0,10,42" Width="120"
HorizontalAlignment="Right" VerticalAlignment="Bottom"
Click="SeasonButton_Click" Grid.Column="1"/>
<Button x:Name="AutoAdjustBsButton" Content="Nachzeichnen" Margin="0,0,135,10" Width="120"
<Button x:Name="AutoAdjustSharesButton" Content="Nachzeichnen" Margin="0,0,135,10" Width="120"
HorizontalAlignment="Right" VerticalAlignment="Bottom"
Click="AutoAdjustBsButton_Click" Grid.Column="1"/>
<Button x:Name="UnAdjustBsButton" Content="Rückgängig" Margin="0,0,10,10" Width="120"
Click="AutoAdjustSharesButton_Click" Grid.Column="1"/>
<Button x:Name="UnAdjustSharesButton" Content="Rückgängig" Margin="0,0,10,10" Width="120"
HorizontalAlignment="Right" VerticalAlignment="Bottom"
Click="UnAdjustBsButton_Click" Grid.Column="1"/>
Click="UnAdjustSharesButton_Click" Grid.Column="1"/>
</Grid>
</GroupBox>
</Grid>
+48 -53
View File
@@ -2,6 +2,7 @@ using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Elwig.Models.Dtos;
using Elwig.Models.Entities;
using Elwig.Services;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
@@ -25,18 +26,18 @@ namespace Elwig.Windows {
Year = year;
// using (var ctx = new AppDbContext()) { SeasonLocked = ctx.Seasons.Find(Year + 1) != null; }
Title = $"Auszahlung anpassen - Lese {Year} - Elwig";
AutoAdjustBsButton.IsEnabled = !SeasonLocked;
UnAdjustBsButton.IsEnabled = !SeasonLocked;
AutoAdjustSharesButton.IsEnabled = !SeasonLocked;
UnAdjustSharesButton.IsEnabled = !SeasonLocked;
SaveCustomButton.IsEnabled = !SeasonLocked;
RemoveCustomButton.IsEnabled = !SeasonLocked;
CustomAmountInput.IsEnabled = !SeasonLocked;
CustomCommentInput.IsEnabled = !SeasonLocked;
AllowanceKgInput.Text = $"{App.Client.AutoAdjustBs.AllowanceKg}";
AllowanceBsInput.Text = $"{App.Client.AutoAdjustBs.AllowanceBs}";
AllowanceKgPerBsInput.Text = $"{App.Client.AutoAdjustBs.AllowanceKgPerBs}";
AllowancePercentInput.Text = $"{App.Client.AutoAdjustBs.AllowancePercent}";
MinBsInput.Text = $"{App.Client.AutoAdjustBs.MinBs}";
AllowanceKgInput.Text = $"{App.Client.AutoAdjustShares.AllowanceKg}";
AllowanceShareInput.Text = $"{App.Client.AutoAdjustShares.AllowanceShares}";
AllowanceKgPerShareInput.Text = $"{App.Client.AutoAdjustShares.AllowanceKgPerShare}";
AllowancePercentInput.Text = $"{App.Client.AutoAdjustShares.AllowancePercent}";
MinSharesInput.Text = $"{App.Client.AutoAdjustShares.MinShares}";
}
protected override async Task OnRenewContext(AppDbContext ctx) {
@@ -45,36 +46,36 @@ namespace Elwig.Windows {
m.MgNr,
m.Name,
m.GivenName,
m.BusinessShares,
m.SharesActive,
m.IsActive,
})
.ToListAsync();
var season = await ctx.FetchSeasons(Year).SingleAsync();
var contracts = await ctx.AreaCommitmentTypes.ToDictionaryAsync(t => t.VtrgId, t => t);
var tbl1 = await OverUnderDeliveryData.ForSeason(ctx.OverUnderDeliveryRows, Year);
var (tbl1, _, _) = await OverUnderDeliveryData.ForSeason(ctx.OverUnderDeliveryRows, Year);
var tbl2 = await AreaComUnderDeliveryData.ForSeason(ctx.AreaComUnderDeliveryRows, Year);
var weight = tbl1.Rows.ToDictionary(r => r.MgNr, r => r.Weight);
var weight = tbl1.Rows.ToDictionary(r => r.MgNr, r => r.WeightTotal);
var areaComs = tbl2.Rows.ToDictionary(r => r.MgNr, r => r.VtrgIds.Zip(r.UnderDeliveries).ToDictionary(r => r.First, r => r.Second));
CustomPayments = await ctx.CustomPayments.Where(p => p.Year == Year).ToDictionaryAsync(p => p.MgNr, p => p);
var history = await ctx.MemberHistory
.Where(h => h.DateString.CompareTo($"{Year}-01-01") >= 0 && h.DateString.CompareTo($"{Year}-12-31") <= 0 && h.Type == "auto" && h.BusinessShares > 0)
.GroupBy(h => h.Member)
.ToDictionaryAsync(h => h.Key.MgNr, h => h.Sum(g => g.BusinessShares));
.Where(h => h.DeductYear == Year)
.GroupBy(h => h.ToMember)
.ToDictionaryAsync(h => h.Key.MgNr, h => h.Sum(g => g.Shares));
var list = members
.Select(m => new {
m.MgNr, m.Name, m.GivenName,
m.IsActive,
BusinessShares = m.BusinessShares - history.GetValueOrDefault(m.MgNr, 0),
DeliveryObligation = (m.BusinessShares - history.GetValueOrDefault(m.MgNr, 0)) * season.MinKgPerBusinessShare,
DeliveryRight = (m.BusinessShares - history.GetValueOrDefault(m.MgNr, 0)) * season.MaxKgPerBusinessShare,
SharesActive = m.SharesActive - history.GetValueOrDefault(m.MgNr, 0),
DeliveryObligation = (m.SharesActive - history.GetValueOrDefault(m.MgNr, 0)) * season.MinKgPerShare,
DeliveryRight = (m.SharesActive - history.GetValueOrDefault(m.MgNr, 0)) * season.MaxKgPerShare,
Adjust = history.TryGetValue(m.MgNr, out int v2) ? (int?)v2 : null,
})
.Select(m => new {
m.MgNr, m.Name, m.GivenName,
m.BusinessShares,
m.SharesActive,
Weight = weight.GetValueOrDefault(m.MgNr, 0),
OverUnder = weight.TryGetValue(m.MgNr, out int v1) ?
(v1 < m.DeliveryObligation ? (int?)v1 - m.DeliveryObligation :
@@ -85,12 +86,12 @@ namespace Elwig.Windows {
})
.Select(m => new {
m.MgNr, m.Name, m.GivenName,
m.BusinessShares, m.Weight, m.OverUnder,
PenaltyBs = m.OverUnder != null && m.OverUnder < 0 ?
m.SharesActive, m.Weight, m.OverUnder,
PenaltyShares = m.OverUnder != null && m.OverUnder < 0 ?
(season.PenaltyPerKg * m.OverUnder ?? 0) +
(-season.PenaltyAmount ?? 0) +
(season.PenaltyPerBsAmount * Math.Floor(m.OverUnder / season.MinKgPerBusinessShare ?? 0m) ?? 0) +
(m.Weight == 0 ? (-season.PenaltyNone ?? 0) + (-season.PenaltyPerBsNone * m.BusinessShares ?? 0) : 0)
(season.PenaltyPerShareAmount * Math.Floor(m.OverUnder / season.MinKgPerShare ?? 0m) ?? 0) +
(m.Weight == 0 ? (-season.PenaltyNone ?? 0) + (-season.PenaltyPerShareNone * m.SharesActive ?? 0) : 0)
: (decimal?)null,
PenaltyAc = areaComs.TryGetValue(m.MgNr, out var c) ? c.Select(r => {
var con = contracts[r.Key];
@@ -102,30 +103,30 @@ namespace Elwig.Windows {
})
.Select(m => new {
m.MgNr, m.Name, m.GivenName,
m.BusinessShares, m.Weight, m.OverUnder,
PenaltyBs = m.PenaltyBs == null || m.PenaltyBs == 0 ? (decimal?)null : Math.Round((decimal)m.PenaltyBs, 2),
m.SharesActive, m.Weight, m.OverUnder,
PenaltyShares = m.PenaltyShares == null || m.PenaltyShares == 0 ? (decimal?)null : Math.Round((decimal)m.PenaltyShares, 2),
PenaltyAc = m.PenaltyAc == null ? (decimal?)null : Math.Round((decimal)m.PenaltyAc, 2),
m.Adjust,
AdjustAmount = m.AdjustAmount == null ? (decimal?)null : Math.Round((decimal)m.AdjustAmount, 2),
CustomAmount = m.Custom?.Amount,
ModAbs = m.Custom?.ModAbs,
ModRel = m.Custom?.ModRel,
m.Custom?.ModAbs,
m.Custom?.ModRel,
})
.Select(m => new {
m.MgNr, m.Name, m.GivenName,
m.BusinessShares, m.Weight, m.OverUnder,
m.PenaltyBs, m.PenaltyAc, m.Adjust, m.AdjustAmount, m.CustomAmount, m.ModAbs, m.ModRel,
Total = (m.PenaltyBs ?? 0) + (m.PenaltyAc ?? 0) + (m.AdjustAmount ?? 0) + (m.CustomAmount ?? 0),
m.SharesActive, m.Weight, m.OverUnder,
m.PenaltyShares, m.PenaltyAc, m.Adjust, m.AdjustAmount, m.CustomAmount, m.ModAbs, m.ModRel,
Total = (m.PenaltyShares ?? 0) + (m.PenaltyAc ?? 0) + (m.AdjustAmount ?? 0) + (m.CustomAmount ?? 0),
})
.Select(m => new {
m.MgNr, m.Name, m.GivenName,
m.BusinessShares, m.Weight, m.OverUnder,
m.PenaltyBs, m.PenaltyAc, m.Adjust, m.AdjustAmount, m.CustomAmount, m.ModAbs, m.ModRel,
m.SharesActive, m.Weight, m.OverUnder,
m.PenaltyShares, m.PenaltyAc, m.Adjust, m.AdjustAmount, m.CustomAmount, m.ModAbs, m.ModRel,
m.Total,
Background = m.Weight == 0 ? Brushes.Orange : m.Weight / 2 < -m.Total ? Brushes.Red : Brushes.White,
Foreground = m.Total == 0 ? Brushes.Gray : Brushes.Black,
})
.Where(m => m.OverUnder != null || m.Adjust != null || m.PenaltyBs != null || m.PenaltyAc != null || m.CustomAmount != null || m.ModAbs != null || m.ModRel != null)
.Where(m => m.OverUnder != null || m.Adjust != null || m.PenaltyShares != null || m.PenaltyAc != null || m.CustomAmount != null || m.ModAbs != null || m.ModRel != null)
.OrderByDescending(m => m.OverUnder ?? 0)
.ThenBy(m => m.Name)
.ThenBy(m => m.GivenName)
@@ -135,7 +136,7 @@ namespace Elwig.Windows {
MemberList.ItemsSource = list;
var sym = season.Currency.Symbol ?? season.Currency.Code;
PenaltyBusinessShares.Text = $"{list.Count(r => r.PenaltyBs != null && r.PenaltyBs != 0)} Mg. / {list.Sum(r => r.PenaltyBs):N2} {sym}";
PenaltyBusinessShares.Text = $"{list.Count(r => r.PenaltyShares != null && r.PenaltyShares != 0)} Mg. / {list.Sum(r => r.PenaltyShares):N2} {sym}";
PenaltyAreaCommitments.Text = $"{list.Count(r => r.PenaltyAc != null && r.PenaltyAc != 0)} Mg. / {list.Sum(r => r.PenaltyAc):N2} {sym}";
AutoBusinessShareAdjustment.Text = $"{list.Count(r => r.Adjust > 0)} Mg. / {list.Sum(r => r.Adjust)} GA / {list.Sum(r => r.AdjustAmount):N2} {sym}";
CustomModifiers.Text = $"{list.Count(r => r.CustomAmount != null)} Mg. / {list.Sum(r => r.CustomAmount):N2} {sym}";
@@ -146,36 +147,34 @@ namespace Elwig.Windows {
CustomAmountInput.Unit = sym;
}
private async void AutoAdjustBsButton_Click(object sender, RoutedEventArgs evt) {
private async void AutoAdjustSharesButton_Click(object sender, RoutedEventArgs evt) {
Mouse.OverrideCursor = Cursors.Wait;
try {
int? kg = AllowanceKgInput.Text == "" ? null : int.Parse(AllowanceKgInput.Text);
double? bs = AllowanceBsInput.Text == "" ? null : double.Parse(AllowanceBsInput.Text);
int? kgPerBs = AllowanceKgPerBsInput.Text == "" ? null : int.Parse(AllowanceKgPerBsInput.Text);
double? shares = AllowanceShareInput.Text == "" ? null : double.Parse(AllowanceShareInput.Text);
int? kgPerShare = AllowanceKgPerShareInput.Text == "" ? null : int.Parse(AllowanceKgPerShareInput.Text);
double? percent = AllowancePercentInput.Text == "" ? null : double.Parse(AllowancePercentInput.Text);
int? minBs = MinBsInput.Text == "" ? null : int.Parse(MinBsInput.Text);
int? minShares = MinSharesInput.Text == "" ? null : int.Parse(MinSharesInput.Text);
App.Client.AutoAdjustBs.AllowanceKg = kg;
App.Client.AutoAdjustBs.AllowanceBs = bs;
App.Client.AutoAdjustBs.AllowanceKgPerBs = kgPerBs;
App.Client.AutoAdjustBs.AllowancePercent = percent;
App.Client.AutoAdjustBs.MinBs = minBs;
App.Client.AutoAdjustShares.AllowanceKg = kg;
App.Client.AutoAdjustShares.AllowanceShares = shares;
App.Client.AutoAdjustShares.AllowanceKgPerShare = kgPerShare;
App.Client.AutoAdjustShares.AllowancePercent = percent;
App.Client.AutoAdjustShares.MinShares = minShares;
await Task.Run(async () => {
await App.Client.UpdateValues();
var b = await Billing.Create(Year);
await b.AutoAdjustBusinessShares(new DateOnly(Year, 11, 30), kg ?? default, bs ?? default, kgPerBs ?? default, percent / 100.0 ?? default, minBs ?? default);
await b.AutoAdjustBusinessShares(new DateOnly(Year, 11, 30), kg ?? default, shares ?? default, kgPerShare ?? default, percent / 100.0 ?? default, minShares ?? default);
});
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "GA Nachzeichnen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("GA Nachzeichnen", exc);
}
Mouse.OverrideCursor = null;
}
private async void UnAdjustBsButton_Click(object sender, RoutedEventArgs evt) {
private async void UnAdjustSharesButton_Click(object sender, RoutedEventArgs evt) {
Mouse.OverrideCursor = Cursors.Wait;
try {
await Task.Run(async () => {
@@ -184,9 +183,7 @@ namespace Elwig.Windows {
});
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "GA Nachzeichnen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("GA Nachzeichnen", exc);
}
Mouse.OverrideCursor = null;
}
@@ -199,7 +196,7 @@ namespace Elwig.Windows {
Validator.CheckInteger((TextBox)sender, false, 6);
}
private void BsInput_TextChanged(object sender, TextChangedEventArgs evt) {
private void SharesInput_TextChanged(object sender, TextChangedEventArgs evt) {
Validator.CheckInteger((TextBox)sender, false, 3);
}
@@ -303,9 +300,7 @@ namespace Elwig.Windows {
});
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Benutzerdefinierten Zu-/Abschlag speichern", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Benutzerdefinierten Zu-/Abschlag speichern", exc);
}
Mouse.OverrideCursor = null;
}
+29 -12
View File
@@ -7,7 +7,7 @@
xmlns:local="clr-namespace:Elwig.Windows"
xmlns:vm="clr-namespace:Elwig.ViewModels"
xmlns:ctrl="clr-namespace:Elwig.Controls"
Title="Auszahlungsvarianten - Elwig" Height="480" Width="850" MinHeight="400" MinWidth="850">
Title="Auszahlungsvarianten - Elwig" Height="480" Width="850" MinHeight="430" MinWidth="850">
<Window.DataContext>
<vm:PaymentVariantsViewModel/>
</Window.DataContext>
@@ -188,18 +188,17 @@
Margin="110,135,10,10" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top"
Checked="ConsiderPenaltyInput_Changed" Unchecked="ConsiderPenaltyInput_Changed"/>
<CheckBox x:Name="ConsiderAutoInput" IsChecked="{Binding ConsiderAuto, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsEnabled="{Binding IsEnabled}"
Content="Automatische Nachzeichnungen der GA"
Content="(Automatische) Nachzeichnungen von GA"
Margin="110,155,10,10" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top"
Checked="ConsiderAutoInput_Changed" Unchecked="ConsiderAutoInput_Changed"/>
<CheckBox x:Name="ConsiderCustomInput" IsChecked="{Binding ConsiderCustom, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsEnabled="{Binding IsEnabled}"
Content="Benutzerdefinierte Zu-/Abschläge pro Mitglied"
Margin="110,175,10,10" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top"
Checked="ConsiderCustomInput_Changed" Unchecked="ConsiderCustomInput_Changed"/>
<Label Content="&#xF0AE;" FontFamily="Segoe MDL2 Assets" FontSize="16" Grid.Row="0" Grid.Column="1" Margin="108,195,10,10"/>
<Grid Grid.Column="0" Grid.ColumnSpan="2" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="50,200,10,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="110"/>
<ColumnDefinition Width="130"/>
<ColumnDefinition Width="27"/>
<ColumnDefinition Width="110"/>
<ColumnDefinition Width="27"/>
@@ -207,7 +206,7 @@
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="27"/>
<RowDefinition Height="5"/>
<RowDefinition Height="37"/>
<RowDefinition Height="27"/>
<RowDefinition Height="5"/>
<RowDefinition Height="27"/>
@@ -227,11 +226,23 @@
</Grid.Resources>
<Button x:Name="EditButton" IsEnabled="{Binding IsPaymentVariantSelected}"
Content="{Binding EditText}" Grid.Column="0" Grid.Row="2"
Click="EditButton_Click"/>
Grid.Column="0" Grid.Row="2" HorizontalContentAlignment="Stretch"
Click="EditButton_Click">
<Grid>
<TextBlock Margin="0,0,13,0" TextAlignment="Center" Text="{Binding EditText}"/>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE8A7;"
TextAlignment="Left" HorizontalAlignment="Right" Padding="0,1.5,0,0" Margin="0,0,5,0"/>
</Grid>
</Button>
<Label Content="&#xF0AF;" FontFamily="Segoe MDL2 Assets" FontSize="16" Grid.Row="2" Grid.Column="1"/>
<Button x:Name="ModifierButton" Content="Zu-/Abschläge" Grid.Column="0" Grid.Row="4"
Click="ModifierButton_Click"/>
<Button x:Name="ModifierButton" Grid.Column="0" Grid.Row="4" HorizontalContentAlignment="Stretch"
Click="ModifierButton_Click">
<Grid>
<TextBlock Margin="0,0,15,0" TextAlignment="Center">Zu-/Abschläge</TextBlock>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE8A7;"
TextAlignment="Left" HorizontalAlignment="Right" Padding="0,1.5,0,0" Margin="0,0,5,0"/>
</Grid>
</Button>
<Label Content="&#xF0AF;" FontFamily="Segoe MDL2 Assets" FontSize="16" Grid.Row="4" Grid.Column="1" RenderTransformOrigin="0.5,0.5" >
<Label.RenderTransform>
<TransformGroup>
@@ -244,8 +255,14 @@
Content="Berechnen" Grid.Column="2" Grid.Row="2"
Click="CalculateButton_Click"/>
<Label Content="{Binding Arrow}" FontFamily="Segoe MDL2 Assets" FontSize="16" Grid.Row="2" Grid.Column="3"/>
<Button x:Name="PaymentAdjustmentButton" Content="Anpassen" Grid.Column="2" Grid.Row="4"
Click="PaymentAdjustmentButton_Click"/>
<Button x:Name="PaymentAdjustmentButton" Grid.Column="2" Grid.Row="4" HorizontalContentAlignment="Stretch"
Click="PaymentAdjustmentButton_Click">
<Grid>
<TextBlock Margin="0,0,13,0" TextAlignment="Center">Anpassen</TextBlock>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE8A7;"
TextAlignment="Left" HorizontalAlignment="Right" Padding="0,1.5,0,0" Margin="0,0,5,0"/>
</Grid>
</Button>
<Label Content="{Binding Arrow}" FontFamily="Segoe MDL2 Assets" FontSize="16" Grid.Row="4" Grid.Column="3" RenderTransformOrigin="0.5,0.5" >
<Label.RenderTransform>
<TransformGroup>
@@ -261,7 +278,7 @@
Content="Freigeben" Grid.Column="4" Grid.Row="2"
Click="RevertButton_Click"/>
<Button x:Name="SaveButton" IsEnabled="{Binding SaveIsEnabled}"
Content="Speichern" Grid.Column="4" Grid.Row="0"
Content="Speichern" Grid.Column="2" Grid.Row="0"
Click="SaveButton_Click"/>
</Grid>
+25 -25
View File
@@ -5,6 +5,7 @@ using Elwig.Services;
using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
@@ -90,9 +91,7 @@ namespace Elwig.Windows {
App.HintContextChange();
ControlUtils.SelectItem(PaymentVariantList, v);
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Auszahlungsvariante erstellen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Auszahlungsvariante erstellen", exc);
}
Mouse.OverrideCursor = null;
}
@@ -106,9 +105,7 @@ namespace Elwig.Windows {
App.HintContextChange();
ControlUtils.SelectItem(PaymentVariantList, n);
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Auszahlungsvariante kopieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Auszahlungsvariante kopieren", exc);
}
Mouse.OverrideCursor = null;
}
@@ -116,19 +113,14 @@ namespace Elwig.Windows {
private async void DeleteButton_Click(object sender, RoutedEventArgs evt) {
if (PaymentVariantList.SelectedItem is not PaymentVar v || !v.TestVariant)
return;
var res = MessageBox.Show(
$"Soll die Auszahlungsvariante \"{v.Name}\" wirklich unwiderruflich gelöscht werden?",
"Auszahlungsvariante löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (res != MessageBoxResult.OK)
if (!InteractionService.AskContinue("Auszahlungsvariante löschen", $"Soll die Auszahlungsvariante \"{v.Name}\" wirklich unwiderruflich gelöscht werden?"))
return;
Mouse.OverrideCursor = Cursors.Wait;
try {
await PaymentVariantService.DeletePaymentVariant(v.Year, v.AvNr);
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Auszahlungsvariante löschen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Auszahlungsvariante löschen", exc);
}
Mouse.OverrideCursor = null;
}
@@ -142,7 +134,7 @@ namespace Elwig.Windows {
await PaymentVariantService.Calculate(v.Year, v.AvNr);
App.HintContextChange();
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Berechnungsfehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Berechnungsfehler", exc);
}
Mouse.OverrideCursor = null;
ViewModel.CalculateIsEnabled = true;
@@ -204,7 +196,7 @@ namespace Elwig.Windows {
await PaymentVariantService.Commit(v.Year, v.AvNr);
App.HintContextChange();
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
Mouse.OverrideCursor = null;
ViewModel.RevertIsEnabled = true;
@@ -213,11 +205,9 @@ namespace Elwig.Windows {
private async void RevertButton_Click(object sender, RoutedEventArgs evt) {
if (PaymentVariantList.SelectedItem is not PaymentVar v)
return;
var res = MessageBox.Show(
if (!InteractionService.AskConfirmation("Traubengutschriften löschen",
"Sollen wirklich alle festgesetzten Traubengutschriften der ausgewählten Auszahlungsvariante unwiderruflich gelöscht werden?\n\n" +
"Dies ist im Allgemeinen nie empfohlen. Handelt es sich um die aktuellste Auszahlungsvariante könnte das eine Ausnahme sein.",
"Traubengutschriften löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (res != MessageBoxResult.Yes)
"Dies ist im Allgemeinen nie empfohlen. Handelt es sich um die aktuellste Auszahlungsvariante könnte das eine Ausnahme sein."))
return;
ViewModel.RevertIsEnabled = false;
Mouse.OverrideCursor = Cursors.Wait;
@@ -225,7 +215,7 @@ namespace Elwig.Windows {
await PaymentVariantService.Revert(v.Year, v.AvNr);
App.HintContextChange();
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
Mouse.OverrideCursor = null;
ViewModel.CommitIsEnabled = true;
@@ -235,7 +225,7 @@ namespace Elwig.Windows {
if (PaymentVariantList.SelectedItem is not PaymentVar v) {
return;
} else if (v.TransferDate == null) {
MessageBox.Show("Überweisungsdatum muss gesetzt sein!", "Exportieren nicht möglich", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Exportieren nicht möglich", "Überweisungsdatum muss gesetzt sein!");
return;
}
await PaymentVariantService.GenerateEbics(v.Year, v.AvNr);
@@ -250,17 +240,27 @@ namespace Elwig.Windows {
private async void SaveButton_Click(object sender, RoutedEventArgs evt) {
if (PaymentVariantList.SelectedItem is not PaymentVar v || ViewModel.BillingData == null)
return;
Mouse.OverrideCursor = Cursors.Wait;
try {
await ViewModel.UpdatePaymentVariant(v.Year, v.AvNr);
App.HintContextChange();
} catch (Exception exc) {
await ForceContextReload();
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Auszahlungsvariante aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Auszahlungsvariante aktualisieren", exc);
}
try {
await Task.Run(async () => {
var b = await BillingVariant.Create(v.Year, v.AvNr);
await b.Calculate(false);
});
} catch (KeyNotFoundException exc) {
InteractionService.ShowInformation("Noch nicht alle Preise festgelegt", exc.Message);
} catch (Exception exc) {
InteractionService.ShowException("Berechnungsfehler", exc);
}
App.HintContextChange();
Mouse.OverrideCursor = null;
await EnsureContextRenewed();
CommentInput_TextChanged(null, null);
DateInput_TextChanged(null, null);
+7 -12
View File
@@ -1,6 +1,6 @@
using Elwig.Helpers;
using Elwig.Helpers.Export;
using Microsoft.Win32;
using Elwig.Services;
using System;
using System.Collections.Generic;
using System.Data.Common;
@@ -42,9 +42,9 @@ namespace Elwig.Windows {
Mouse.OverrideCursor = Cursors.Wait;
await DisplayQuery(QueryInput.Text);
Mouse.OverrideCursor = null;
} catch (Exception e) {
} catch (Exception exc) {
Mouse.OverrideCursor = null;
MessageBox.Show(e.Message, "Fehler beim Ausführen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Fehler beim Ausführen", exc);
}
}
@@ -85,21 +85,16 @@ namespace Elwig.Windows {
}
private static async Task SaveQuery(string sqlQuery) {
var d = new SaveFileDialog() {
FileName = $"Abfrage.csv",
DefaultExt = "csv",
Filter = "CSV-Datei (*.csv)|*.csv",
Title = $"Datenbank Abfrage speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
var filename = InteractionService.SaveFile("Datenbank Abfrage", "Abfrage", "csv");
if (filename != null) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var (header, rows) = await ExecuteQuery(sqlQuery);
using var csv = new CsvSimple(d.FileName, ';', Utils.UTF8BOM);
using var csv = new CsvSimple(filename, ';', Utils.UTF8BOM);
await csv.ExportAsync(rows.Prepend([.. header.Select(h => h.ColumnName)]));
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler beim Ausführen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Fehler beim Ausführen", exc);
}
});
Mouse.OverrideCursor = null;
+17
View File
@@ -0,0 +1,17 @@
Elwig
=====
Source code
C:\Program Files\Elwig\src\
https://git.necronda.net/winzer/elwig
Installation folder
C:\Program Files\Elwig\
Data and configuration folder
C:\ProgramData\Elwig\
- config.ini : main configuration file
- database.sqlite3 : stores all data
- imported.txt : list of all imported *.elwig.zip files to not automatically import them again
- mails\ : sent/outgoing email log
+1
View File
@@ -26,6 +26,7 @@ auto = true
;username = ""
;password = ""
;from = mail@wg.at
;wait = 0.5
;[scale.1]
;type = SysTec-IT
+3 -1
View File
@@ -3,7 +3,9 @@
<Fragment>
<!-- C:\Program Files (x86)\Elwig or C:\Program Files\Elwig -->
<StandardDirectory Id="ProgramFiles64Folder">
<Directory Id="InstallFolder" Name="!(bind.Property.ProductName)" />
<Directory Id="InstallFolder" Name="!(bind.Property.ProductName)">
<Directory Id="SourceFolder" Name="src"/>
</Directory>
</StandardDirectory>
<!-- C:\ProgramData\Elwig -->
+4
View File
@@ -34,7 +34,11 @@
<DefineConstants>ProductVersion=$(ElwigFileVersion);BuildPath=..\Elwig\bin\Publish;ElwigProjectDir=..\Elwig</DefineConstants>
</PropertyGroup>
</Target>
<Target Name="CreateSourceArchive" BeforeTargets="BeforeBuild">
<Exec Command="git -C .. archive -o $(ProjectDir)\Files\elwig.zip HEAD" />
</Target>
<ItemGroup>
<None Include="Files\config.ini" />
<None Include="Files\README.txt" />
</ItemGroup>
</Project>
+6
View File
@@ -4,9 +4,15 @@
<Component Directory="InstallFolder">
<File Source="$(var.ElwigProjectDir)\bin\Publish\Elwig.exe" Id="Elwig.exe"/>
</Component>
<Component Directory="SourceFolder">
<File Source="$(ProjectDir)\Files\elwig.zip" Id="elwig.zip"/>
</Component>
<Component Directory="ConfigFolder" Permanent="true" NeverOverwrite="true">
<File Source="$(ProjectDir)\Files\config.ini" Id="config.ini"/>
</Component>
<Component Directory="ConfigFolder">
<File Source="$(ProjectDir)\Files\README.txt" Id="README.txt"/>
</Component>
</ComponentGroup>
</Fragment>
</Wix>
+2 -2
View File
@@ -13,7 +13,7 @@ About
**Product:** Elwig
**Description:** Electronic Management for Vintners' Cooperatives
**Type:** ERP system
**Version:** 1.0.5.0 ([Changelog](./CHANGELOG.md))
**Version:** 1.1.1.0 ([Changelog](./CHANGELOG.md))
**License:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
**Website:** https://elwig.at/
**Source code:** https://git.necronda.net/winzer/elwig
@@ -33,7 +33,7 @@ Packaging: [WiX Toolset](https://www.firegiant.com/wixtoolset/)
**Produkt:** Elwig
**Beschreibung:** Elektronische Winzergenossenschaftsverwaltung
**Typ:** Warenwirtschaftssystem (ERP-System)
**Version:** 1.0.5.0 ([Änderungsprotokoll](./CHANGELOG.md))
**Version:** 1.1.1.0 ([Änderungsprotokoll](./CHANGELOG.md))
**Lizenz:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
**Website:** https://elwig.at/
**Quellcode:** https://git.necronda.net/winzer/elwig
+5 -5
View File
@@ -32,9 +32,9 @@ INSERT INTO area_commitment (fbnr, revnr, mgnr, vtrgid, cultid, area, gstnr, yea
( 2, 1, 101, 'GV', 'KIP', 10000, '123/5', 2025, 2030),
( 3, 1, 101, 'GV', 'KIP', 10000, '123/6', 2021, 2031);
INSERT INTO season (year, currency, min_kg_per_bs, max_kg_per_bs, penalty_per_kg, penalty_amount, penalty_none, start_date, end_date) VALUES
(2020, 'EUR', 1000, 2000, NULL, NULL, NULL, NULL, NULL),
(2021, 'EUR', 2000, 4000, NULL, NULL, NULL, NULL, NULL);
INSERT INTO season (year, currency, min_kg_per_share, max_kg_per_share, start_date, end_date) VALUES
(2020, 'EUR', 1000, 2000, NULL, NULL),
(2021, 'EUR', 2000, 4000, NULL, NULL);
INSERT INTO modifier (year, modid, ordering, name, abs, rel, active) VALUES
(2020, 'S', 0, 'Geschädigte Trauben', NULL, -0.1, TRUE),
@@ -45,7 +45,7 @@ INSERT INTO delivery (mgnr, year, did, date, time, zwstid, lnr) VALUES
(101, 2020, 1, '2020-10-01', NULL, 'X', 1),
(101, 2020, 2, '2020-10-01', NULL, 'X', 2),
(101, 2020, 3, '2020-10-01', NULL, 'X', 3);
INSERT INTO delivery_part (year, did, dpnr, sortid, attrid, cultid, weight, kmw, qualid, hkid, kgnr, net_weight, manual_weighing, spl_check, scale_id, weighing_data, weighing_reason) VALUES
INSERT INTO delivery_part (year, did, dpnr, sortid, attrid, cultid, weight, kmw, qualid, hkid, kgnr, net_weight, manual_weighing, spl_check, scale_terminal, weighing_data, weighing_reason) VALUES
(2020, 1, 1, 'GV', 'K', NULL, 4000, 17, 'KAB', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 1, 2, 'GV', NULL, NULL, 4000, 16, 'QUW', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 2, 1, 'GV', NULL, 'B', 4000, 15, 'QUW', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
@@ -60,7 +60,7 @@ INSERT INTO delivery (mgnr, year, did, date, time, zwstid, lnr) VALUES
(101, 2021, 1, '2021-10-01', NULL, 'X', 1),
(101, 2021, 2, '2021-10-01', NULL, 'X', 2),
(101, 2021, 3, '2021-10-01', NULL, 'X', 3);
INSERT INTO delivery_part (year, did, dpnr, sortid, attrid, cultid, weight, kmw, qualid, hkid, kgnr, gebunden, net_weight, manual_weighing, spl_check, scale_id, weighing_data, weighing_reason) VALUES
INSERT INTO delivery_part (year, did, dpnr, sortid, attrid, cultid, weight, kmw, qualid, hkid, kgnr, gebunden, net_weight, manual_weighing, spl_check, scale_terminal, weighing_data, weighing_reason) VALUES
(2021, 1, 1, 'GV', 'K', NULL, 4000, 17, 'KAB', 'WLNO', 06109, TRUE, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2021, 1, 2, 'GV', NULL, NULL, 4000, 16, 'QUW', 'WLNO', 06109, FALSE, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2021, 2, 1, 'GV', NULL, 'B', 4000, 15, 'QUW', 'WLNO', 06109, TRUE, TRUE, FALSE, FALSE, NULL, NULL, NULL),
+3 -3
View File
@@ -6,8 +6,8 @@ INSERT INTO wine_cultivation (cultid, name, description) VALUES
INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lower) VALUES
('K', 'Kabinett', TRUE, NULL, FALSE, 0);
INSERT INTO season (year, currency, min_kg_per_bs, max_kg_per_bs, penalty_per_kg, penalty_amount, penalty_none, start_date, end_date) VALUES
(2020, 'EUR', 1000, 2000, NULL, NULL, NULL, NULL, NULL);
INSERT INTO season (year, currency, min_kg_per_share, max_kg_per_share, start_date, end_date) VALUES
(2020, 'EUR', 1000, 2000, NULL, NULL);
INSERT INTO delivery_schedule (year, dsnr, date, zwstid, description, max_weight, ancmt_from, ancmt_to) VALUES
(2020, 1, '2020-10-01', 'X', 'GV Kabinettaktion', 100000, NULL, NULL);
@@ -39,7 +39,7 @@ INSERT INTO delivery (mgnr, year, did, date, time, zwstid, lnr) VALUES
(104, 2020, 11, '2020-10-03', '14:39:22', 'X', 2),
(104, 2020, 12, '2020-10-03', '15:15:41', 'X', 3);
INSERT INTO delivery_part (year, did, dpnr, sortid, attrid, cultid, weight, kmw, qualid, hkid, kgnr, net_weight, manual_weighing, spl_check, scale_id, weighing_data, weighing_reason) VALUES
INSERT INTO delivery_part (year, did, dpnr, sortid, attrid, cultid, weight, kmw, qualid, hkid, kgnr, net_weight, manual_weighing, spl_check, scale_terminal, weighing_data, weighing_reason) VALUES
(2020, 1, 1, 'GV', NULL, NULL, 3219, 15.0, 'QUW', 'WLNO', 06109, TRUE, FALSE, FALSE, '1', '{"id":"321","nr":321,"gross_weight":3219,"tare_weight":0,"net_weight":3219,"date":"2020-10-01","time":"09:02:46"}', NULL),
(2020, 2, 1, 'GV', 'K', NULL, 2987, 17.5, 'KAB', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 2, 2, 'GV', 'K', NULL, 1873, 17.7, 'KAB', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
+5 -5
View File
@@ -42,10 +42,10 @@ INSERT INTO area_commitment (fbnr, revnr, mgnr, vtrgid, cultid, area, gstnr, yea
( 4, 1, 204, 'GV', NULL, 10000, '123/3', 2021, 2031);
INSERT INTO season (year, currency, min_kg_per_bs, max_kg_per_bs, penalty_per_kg, penalty_amount, penalty_none, start_date, end_date) VALUES
(2021, 'EUR', 2000, 4000, NULL, NULL, NULL, NULL, NULL),
(2022, 'EUR', 2000, 4000, NULL, NULL, NULL, NULL, NULL),
(2023, 'EUR', 2000, 4000, NULL, NULL, NULL, NULL, NULL);
INSERT INTO season (year, currency, min_kg_per_share, max_kg_per_share, start_date, end_date) VALUES
(2021, 'EUR', 2000, 4000, NULL, NULL),
(2022, 'EUR', 2000, 4000, NULL, NULL),
(2023, 'EUR', 2000, 4000, NULL, NULL);
INSERT INTO modifier (year, modid, ordering, name, abs, rel, active) VALUES
(2021, 'S', 0, 'Geschädigte Trauben', NULL, -0.1, TRUE),
@@ -76,7 +76,7 @@ INSERT INTO delivery (mgnr, year, did, date, time, zwstid, lnr) VALUES
(101, 2023, 18, '2023-10-10', NULL, 'X', 2),
(101, 2023, 19, '2023-10-10', NULL, 'X', 3);
INSERT INTO delivery_part (year, did, dpnr, sortid, attrid, cultid, weight, kmw, qualid, hkid, kgnr, net_weight, manual_weighing, spl_check, scale_id, weighing_data, weighing_reason) VALUES
INSERT INTO delivery_part (year, did, dpnr, sortid, attrid, cultid, weight, kmw, qualid, hkid, kgnr, net_weight, manual_weighing, spl_check, scale_terminal, weighing_data, weighing_reason) VALUES
(2021, 1, 1, 'GV', NULL, NULL, 3500, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2021, 2, 1, 'GV', NULL, NULL, 4000, 17.0, 'KAB', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2021, 2, 2, 'GV', NULL, NULL, 4000, 16.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),

Some files were not shown because too many files have changed in this diff Show More