Compare commits

..
62 Commits
Author SHA1 Message Date
lorenz.stechauner b29afe8817 [WIP] certs
Test / Run tests (push) Successful in 2m4s
2026-09-23 19:30:51 +02:00
lorenz.stechauner 824cfc5636 BusinessDocument: Add OrganicAuthorityCode and OrganicOperatorId to member Aside
Test / Run tests (push) Successful in 2m3s
2026-09-23 19:28:55 +02:00
lorenz.stechauner 7b8117f9ac Entities/MemberHistory: Add CompanyHasSigned 2026-09-23 19:28:55 +02:00
lorenz.stechauner 3c81b432c0 ClientParameters: Add ClientOrganicOperatorId 2026-09-23 19:28:51 +02:00
lorenz.stechauner e23b5e07dd Entities/WineCult: Add IsOrganic 2026-09-23 19:28:03 +02:00
lorenz.stechauner 0ed793d9be Entities/Member: Add OrganicOperatorId 2026-09-23 19:27:57 +02:00
lorenz.stechauner 569913fe8f Entities/Member: Add OrganicAuthorityCode 2026-09-22 11:21:05 +02:00
lorenz.stechauner d592fef352 BusinessDocument: Use more columns in Aside table
Test / Run tests (push) Successful in 2m5s
2026-09-22 11:19:41 +02:00
lorenz.stechauner f2b84053e6 DeliveryNote: Remove right padding for weight cells
Test / Run tests (push) Successful in 1m51s
2026-09-22 10:54:23 +02:00
lorenz.stechauner 7fef895491 DeliveryService: Add red/white distinction to tool tips
Test / Run tests (push) Successful in 1m53s
2026-09-16 17:28:38 +02:00
lorenz.stechauner 0e9121c462 TareCorrectionDialog: Allow actual tare to be null 2026-09-16 17:23:12 +02:00
lorenz.stechauner 15e191d74e Export/Database: Do not delete import source files 2026-09-16 17:12:52 +02:00
lorenz.stechauner abf06c558f Bump version to 1.1.2.2
Test / Run tests (push) Successful in 2m8s
Deploy / Build and Deploy (push) Successful in 2m55s
2026-09-14 17:10:23 +02:00
lorenz.stechauner 6436762e8d DeliveryAdminWindow: Show CorrectTareButton also for unloading type box
Test / Run tests (push) Successful in 2m11s
2026-09-14 16:40:16 +02:00
lorenz.stechauner 6441fa4f50 MemberAdminWindow: Add details in header and tool tips
Test / Run tests (push) Successful in 2m3s
2026-09-14 15:20:34 +02:00
lorenz.stechauner 6bed370eba Tests: Update dependencies
Test / Run tests (push) Successful in 2m13s
2026-09-14 14:53:39 +02:00
lorenz.stechauner 4cc33b56c8 Elwig: Update dependencies 2026-09-14 14:53:26 +02:00
lorenz.stechauner f4cc443521 DeliveryNote: Fix page breaks in delivery table
Test / Run tests (push) Successful in 2m12s
2026-09-14 14:45:39 +02:00
lorenz.stechauner 8ba051198f DeliveryAdminWindow: Add button to correct tare 2026-09-14 14:43:24 +02:00
lorenz.stechauner fea779bd95 Windows: Add filters to Titles in some AdministrationWindows
Test / Run tests (push) Successful in 2m21s
2026-09-12 22:59:57 +02:00
lorenz.stechauner e66780ccd0 DeliveryNote: Fix missing ':' for Comment and Acid 2026-09-12 22:48:28 +02:00
lorenz.stechauner cc01f92005 InteractionService: Add more detailed messages for SqliteException
Test / Run tests (push) Successful in 2m44s
2026-08-29 11:42:09 +02:00
lorenz.stechauner 0a93167ea7 Bump version to 1.1.2.1
Test / Run tests (push) Successful in 1m53s
Deploy / Build and Deploy (push) Successful in 1m51s
2026-08-27 11:33:48 +02:00
lorenz.stechauner 848a6d1ce0 MailWindow: Add Date header to email
Test / Run tests (push) Successful in 1m55s
2026-08-27 11:28:27 +02:00
lorenz.stechauner f0745eae4e Elwig: Fix LINQ queries containing DateTime.Today
Test / Run tests (push) Successful in 2m18s
2026-08-27 11:20:22 +02:00
lorenz.stechauner 8471ac9319 Bump version to 1.1.2.0
Test / Run tests (push) Successful in 2m34s
Deploy / Build and Deploy (push) Successful in 2m11s
2026-08-26 23:05:34 +02:00
lorenz.stechauner 7add96dfd5 Elwig: Update dependencies
Test / Run tests (push) Successful in 2m18s
2026-08-26 16:28:29 +02:00
lorenz.stechauner 2360402124 Tests: Update dependencies 2026-08-26 16:28:14 +02:00
lorenz.stechauner cadac4302d App: Add option to restore database when a DB error occurs at startup
Test / Run tests (push) Successful in 2m12s
2026-08-26 12:01:03 +02:00
lorenz.stechauner c1690bf104 DeliveryService: Fix GetFilterToday and add tests
Test / Run tests (push) Successful in 2m16s
2026-08-26 10:57:02 +02:00
lorenz.stechauner ca5b719630 Elwig: Add BackupService
Test / Run tests (push) Successful in 2m48s
2026-08-26 10:39:50 +02:00
lorenz.stechauner 163fe0fac8 Bump version to 1.1.1.2
Test / Run tests (push) Successful in 2m41s
Deploy / Build and Deploy (push) Successful in 1m52s
2026-08-25 20:36:03 +02:00
lorenz.stechauner edf3678154 Export/ElwigData: Fix detection of KGs without Großlage
Test / Run tests (push) Successful in 1m54s
2026-08-25 18:19:41 +02:00
lorenz.stechauner f450dc35b1 Weighing: Add option to opt-out of automatic datetime sync at startup 2026-08-25 18:19:31 +02:00
lorenz.stechauner 0c4611024f App: Add default weighing mode for Pamhagen
Test / Run tests (push) Successful in 1m57s
2026-08-25 16:31:33 +02:00
lorenz.stechauner 46e596ae23 Utils: Replace Utils.Today with DateTime.Today and adapt DeliveryService filters
Test / Run tests (push) Successful in 2m51s
2026-08-25 16:30:38 +02:00
lorenz.stechauner 3366cc25eb DeliveryAdminWindow: Change modifier handling for Matzen
Test / Run tests (push) Successful in 2m12s
2026-08-24 22:11:52 +02:00
lorenz.stechauner ff71e9fb7f DeliveryNote: Fix alignment of weights 2026-08-24 21:49:10 +02:00
lorenz.stechauner 7d5d7d9307 Bump version to 1.1.1.1
Test / Run tests (push) Successful in 2m20s
Deploy / Build and Deploy (push) Successful in 2m0s
2026-08-24 00:51:00 +02:00
lorenz.stechauner b468bbce31 MainWindow: Add individual commitments to Area Commitment export
Test / Run tests (push) Successful in 2m37s
2026-08-24 00:39:30 +02:00
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
104 changed files with 2560 additions and 865 deletions
+1 -1
View File
@@ -2,7 +2,7 @@ name: Test
on: on:
push: push:
branches: ["**"] branches: ["**"]
paths: ["Elwig/**", "Tests/**", "Installer/Files/*.exe", ".gitea/workflows/test.yaml"] paths: ["Elwig/**", "Tests/**", ".gitea/workflows/test.yaml"]
jobs: jobs:
test: test:
name: Run tests name: Run tests
+161 -1
View File
@@ -2,6 +2,166 @@
Changelog Changelog
========= =========
[v1.1.2.2][v1.1.2.2] (2026-09-14) {#v1.1.2.2}
---------------------------------------------
### Neue Funktionen {#v1.1.2.2-features}
* Bei einigen Fenstern werden in den Titeln nun die aktiven Filter der Suche angezeigt. (fea779bd95)
* Im Lieferungen-Fenster (`DeliveryAdminWindow`) ist es nun möglich die Tara nachträglich zu korrigieren. (8ba051198f, 6436762e8d)
### Behobene Fehler {#v1.1.2.2-bugfixes}
* Bei Lieferscheinen (`DeliveryNote`) hat bei Anmerkungen zu Teil-Lfrg. ein `:` gefehlt.
Außerdem wurden Seitenumbrüche so platziert, dass sie nicht mehr innerhalb einer Teil-Lfrg. liegen. (e66780ccd0, f4cc443521)
### Sonstiges {#v1.1.2.2-misc}
* Bei Datenbank-Fehler sind die Fehlermeldungen nun genauer bzw. auf deutsch übersetzt. (cc01f92005)
* Im Mitglieder-Fenster (`MemberAdminWindow`) wurden Überschriften und Tooltips leicht angepasst. (6441fa4f50)
* Abhängigkeiten aktualisiert. (4cc33b56c8, 6bed370eba)
[v1.1.2.2]: https://git.necronda.net/winzer/elwig/releases/tag/v1.1.2.2
[v1.1.2.1][v1.1.2.1] (2026-08-27) {#v1.1.2.1}
---------------------------------------------
### Behobene Fehler {#v1.1.2.1-bugfixes}
* Im Anmeldungen-Fenster (`DeliveryAncmtAdminWindow`) und im Leseplanung-Fenster (`DeliveryScheduleAdminWindow`) kam es seit [v1.1.1.2](#v1.1.1.2) (2026-08-25) zu einem Absturz. (f0745eae4e)
### Sonstiges {#v1.1.2.1-misc}
* Bei E-Mails wird ab jetzt auch Datum und Uhrzeit gesetzt. (848a6d1ce0)
[v1.1.2.1]: https://git.necronda.net/winzer/elwig/releases/tag/v1.1.2.1
[v1.1.2.0][v1.1.2.0] (2026-08-26) {#v1.1.2.0}
---------------------------------------------
### Neue Funktionen {#v1.1.2.0-features}
* Eine Automatische Backup-/Sicherungs-Funktion für die Datenbank wurde hinzugefügt. (ca5b719630, cadac4302d)
### Behobene Fehler {#v1.1.2.0-bugfixes}
* Wenn am heutigen Tag noch keine Lieferung übernommen wurde, wurden im Lieferungen-/Übernahme-Fenster (`DeliveryAdminWindow`) mit dem Filter "Nur heute" auch Lieferungen des vorhergehenden Tages angezeigt. (c1690bf104)
### Sonstiges {#v1.1.2.0-misc}
* Abhängigkeiten aktualisiert. (2360402124, 7add96dfd5)
[v1.1.2.0]: https://git.necronda.net/winzer/elwig/releases/tag/v1.1.2.0
[v1.1.1.2][v1.1.1.2] (2026-08-25) {#v1.1.1.2}
---------------------------------------------
### Behobene Fehler {#v1.1.1.2-bugfixes}
* Auf Lieferscheinen (`DeliveryNote`) waren die Mengenangaben nicht rechtsbündig. (ff71e9fb7f)
* Beim Importieren von Mitgliedern mit Stamm-KG ohne Großlage kam es zu einem Fehler, obwohl die Großlage automatisch hätte zugewiesen werden müssen. (edf3678154)
### Sonstiges {#v1.1.1.2-misc}
* In Matzen werden für Planenw./Kipper und Lesewagen die Klassen automatisch gesetzt. (3366cc25eb)
* Im Lieferungen-Fenster (`DeliveryAdminWindow`) wird für "Nur heute" nicht mehr nur das Datum, sondern die letzten zusammenhängenden Lieferungen verwendet. (46e596ae23)
* Für Pamhagen wird als standard Wiege-Methode "brutto" verwendet. (0c4611024f)
* Für Waagen kann das automatische setzen von Datum und Uhrzeit nun per Opt-out ausgeschalten werden (Konfigurationsdatei -> `[scale.X]` -> `synctime = false`). (f450dc35b1)
[v1.1.1.2]: https://git.necronda.net/winzer/elwig/releases/tag/v1.1.1.2
[v1.1.1.1][v1.1.1.1] (2026-08-24) {#v1.1.1.1}
---------------------------------------------
### Sonstiges {#v1.1.1.1-misc}
* Beim Export der Flächenbindungen im Haupt-Fenster (`MainWindow`) werden neben der zusammengefassten gebundenen Fläche nun auch die jeweiligen Flächenindungen exportiert. (b468bbce31)
[v1.1.1.1]: https://git.necronda.net/winzer/elwig/releases/tag/v1.1.1.1
[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} [v1.1.0.0][v1.1.0.0] (2026-07-09) {#v1.1.0.0}
--------------------------------------------- ---------------------------------------------
@@ -290,7 +450,7 @@ Changelog
[v1.0.3.0]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.3.0 [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 [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 [i71]: https://git.necronda.net/winzer/elwig/issues/71
[i73]: https://git.necronda.net/winzer/elwig/issues/73 [i73]: https://git.necronda.net/winzer/elwig/issues/73
+1 -8
View File
@@ -16,14 +16,7 @@
<dependency> <dependency>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" />
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly> </dependentAssembly>
</dependency> </dependency>
</assembly> </assembly>
+1 -2
View File
@@ -2,8 +2,7 @@
x:Class="Elwig.App" x:Class="Elwig.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ctrl="clr-namespace:Elwig.Controls" xmlns:ctrl="clr-namespace:Elwig.Controls">
Exit="Application_Exit">
<Application.Resources> <Application.Resources>
<ctrl:BoolToStringConverter x:Key="BoolToStarConverter" FalseValue="" TrueValue="*"/> <ctrl:BoolToStringConverter x:Key="BoolToStarConverter" FalseValue="" TrueValue="*"/>
<ctrl:WidthToMarginConverter x:Key="WidthToMarginConverter"/> <ctrl:WidthToMarginConverter x:Key="WidthToMarginConverter"/>
+47 -12
View File
@@ -25,9 +25,10 @@ namespace Elwig {
protected static App CurrentApp; protected static App CurrentApp;
public static int NumWindows => CurrentApp.Windows.Count; public static int NumWindows => CurrentApp.Windows.Count;
public static bool ForceShutdown { get; private set; } = false; public static bool ForceShutdown { get; set; } = false;
private readonly DispatcherTimer _autoUpdateTimer = new() { Interval = TimeSpan.FromHours(1) }; private readonly DispatcherTimer _autoUpdateTimer = new() { Interval = TimeSpan.FromHours(1) };
private readonly DispatcherTimer _autoBackupTimer = new() { Interval = TimeSpan.FromMinutes(10) };
public readonly SerialPortWatcher SerialPortWatcher = new(); public readonly SerialPortWatcher SerialPortWatcher = new();
public static readonly string DataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Elwig"); public static readonly string DataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Elwig");
@@ -74,7 +75,7 @@ namespace Elwig {
Config = new(Path.GetFullPath(args[1])); Config = new(Path.GetFullPath(args[1]));
} }
ContextTimer.Tick += (object? sender, EventArgs evt) => { ContextTimer.Tick += (sender, evt) => {
var ch = CurrentLastWrite; var ch = CurrentLastWrite;
if (ch > LastChanged) { if (ch > LastChanged) {
LastChanged = ch; LastChanged = ch;
@@ -92,11 +93,15 @@ namespace Elwig {
try { try {
await AppDbUpdater.CheckDb(); await AppDbUpdater.CheckDb();
} catch (FileNotFoundException exc) {
InteractionService.ShowException("Datenbank nicht gefunden", "Die Datenbank konnte nicht gefunden werden", exc);
} catch (Exception exc) { } catch (Exception exc) {
if (Config.UpdateUrl != null && Utils.HasInternetConnectivity()) { if (Config.UpdateUrl != null && Utils.HasInternetConnectivity()) {
await CheckForUpdates(); await CheckForUpdates();
} }
InteractionService.ShowException("Fehlerhafte Datenbank", "Fehlerhafte Datenbank", exc); InteractionService.ShowException("Fehlerhafte Datenbank", "Schwerwiegender Fehler in der Datenbank, unbedingt den Programmierern melden!", exc);
if (InteractionService.AskQuestion("Backup wiederherstellen", "Soll die Datenbank aus einem Backup wiederhergestellt werden?\n\nAchtung: Das kann zu Datenverlust führen!", false))
await BackupService.RestoreDatabase();
Shutdown(); Shutdown();
return; return;
} }
@@ -106,13 +111,14 @@ namespace Elwig {
Dictionary<string, (string, string, int?, string?, string?, string?, string?, string?)> branches = []; Dictionary<string, (string, string, int?, string?, string?, string?, string?, string?)> branches = [];
using (var ctx = new AppDbContext()) { using (var ctx = new AppDbContext()) {
branches = ctx.FetchBranches() branches = await ctx.FetchBranches()
.ToDictionaryAsync(b => b.Name.ToLower(), b => (b.ZwstId, b.Name, b.PostalDest?.AtPlz?.Plz, b.PostalDest?.AtPlz?.Ort.Name, b.Address, b.PhoneNr, b.FaxNr, b.MobileNr)) .ToDictionaryAsync(b => b.Name.ToLower(), b => (b.ZwstId, b.Name, b.PostalDest?.AtPlz?.Plz, b.PostalDest?.AtPlz?.Ort.Name, b.Address, b.PhoneNr, b.FaxNr, b.MobileNr));
.GetAwaiter().GetResult();
try { try {
Client = new(ctx); Client = new(ctx);
} catch (Exception exc) { } catch (Exception exc) {
InteractionService.ShowException("Fehler", "Fehler beim Laden der Mandantendaten", exc); InteractionService.ShowException("Fehler", "Fehler beim Laden der Mandantendaten, unbedingt den Programmierern melden!", exc);
if (InteractionService.AskQuestion("Backup wiederherstellen", "Soll die Datenbank aus einem Backup wiederhergestellt werden?\n\nAchtung: Das kann zu Datenverlust führen!", false))
await BackupService.RestoreDatabase();
Shutdown(); Shutdown();
return; return;
} }
@@ -124,9 +130,23 @@ namespace Elwig {
return Task.CompletedTask; return Task.CompletedTask;
}); });
Utils.RunBackground("PDF Initialization", () => Pdf.Init()); Utils.RunBackground("PDF Initialization", async () => Pdf.Init());
Utils.RunBackground("JSON Schema Initialization", BillingData.Init); Utils.RunBackground("JSON Schema Initialization", BillingData.Init);
if (Config.BackupAuto) {
Utils.RunBackground("Automatisches Backup", async () => {
Directory.CreateDirectory(Config.BackupPath);
await Task.Delay(500);
await BackupService.TryDatabaseBackup();
});
_autoBackupTimer.Tick += new EventHandler((sender, evt) => {
Utils.RunBackground("Automatisches Backup", async () => {
await BackupService.TryDatabaseBackup();
});
});
_autoBackupTimer.Start();
}
if (Config.UpdateAuto && Config.UpdateUrl != null) { if (Config.UpdateAuto && Config.UpdateUrl != null) {
if (Utils.HasInternetConnectivity()) { if (Utils.HasInternetConnectivity()) {
Utils.RunBackground("Auto Updater", async () => { Utils.RunBackground("Auto Updater", async () => {
@@ -145,7 +165,13 @@ namespace Elwig {
var list = new List<IScale>(); var list = new List<IScale>();
foreach (var s in Config.Scales) { foreach (var s in Config.Scales) {
try { try {
list.Add(Scale.FromConfig(s)); var scale = Scale.FromConfig(s);
if (s.SyncTime && scale is ICommandScale cmd) {
try {
await cmd.SetDateAndTime(DateTime.Now);
} catch { }
}
list.Add(scale);
} catch (Exception exc) { } catch (Exception exc) {
list.Add(new InvalidScale(s.Id)); list.Add(new InvalidScale(s.Id));
if (s.Required) if (s.Required)
@@ -173,7 +199,7 @@ namespace Elwig {
Config.WeighingMode = WeighingMode.Net; Config.WeighingMode = WeighingMode.Net;
} else if (Client.IsHaugsdorf || Client.IsSitzendorf) { } else if (Client.IsHaugsdorf || Client.IsSitzendorf) {
Config.WeighingMode = WeighingMode.Box; Config.WeighingMode = WeighingMode.Box;
} else if (Client.IsBaden || Client.IsGrInzersdorf) { } else if (Client.IsBaden || Client.IsGrInzersdorf || Client.IsPamhagen) {
Config.WeighingMode = WeighingMode.Gross; Config.WeighingMode = WeighingMode.Gross;
} }
} }
@@ -184,12 +210,21 @@ namespace Elwig {
window.Show(); window.Show();
} }
private async void Application_Exit(object sender, ExitEventArgs evt) { public async Task OnClosing() {
try {
await BackupService.TryDatabaseBackup();
} catch (Exception exc) {
InteractionService.ShowException("Automatisches Backup", exc);
}
}
protected override void OnExit(ExitEventArgs evt) {
SerialPortWatcher.Dispose(); SerialPortWatcher.Dispose();
foreach (var s in EventScales) { foreach (var s in EventScales) {
s.Dispose(); s.Dispose();
} }
await Pdf.Cleanup(); Pdf.Cleanup();
base.OnExit(evt);
} }
public static void SetBranch(Branch b) { public static void SetBranch(Branch b) {
+4 -5
View File
@@ -4,6 +4,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Dialogs" xmlns:local="clr-namespace:Elwig.Dialogs"
xmlns:ctrl="clr-namespace:Elwig.Controls"
ResizeMode="NoResize" ShowInTaskbar="False" Topmost="True" ResizeMode="NoResize" ShowInTaskbar="False" Topmost="True"
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"
FocusManager.FocusedElement="{Binding ElementName=WeightInput}" FocusManager.FocusedElement="{Binding ElementName=WeightInput}"
@@ -37,11 +38,9 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Label Content="Gewicht:" Margin="10,20,10,10"/> <Label Content="Gewicht:" Margin="10,20,10,10"/>
<Grid Grid.Column="1" Width="70" Height="25" Margin="0,20,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"> <ctrl:UnitTextBox x:Name="WeightInput" Grid.Column="1" Width="62" Height="25" Unit="kg"
<TextBox x:Name="WeightInput" TextAlignment="Right" Padding="2,2,17,2" Margin="0,20,10,10" HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="14" Padding="2"
TextChanged="WeightInput_TextChanged"/> TextChanged="WeightInput_TextChanged"/>
<Label Content="kg" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10" Padding="2,4,2,4"/>
</Grid>
<Label Content="Grund:" Margin="10,50,10,10"/> <Label Content="Grund:" Margin="10,50,10,10"/>
<TextBox x:Name="ReasonInput" Grid.Column="1" Margin="0,50,10,10" <TextBox x:Name="ReasonInput" Grid.Column="1" Margin="0,50,10,10"
+2 -2
View File
@@ -6,8 +6,8 @@ using System.Windows.Controls;
namespace Elwig.Dialogs { namespace Elwig.Dialogs {
public partial class ManualWeighingDialog : Window { public partial class ManualWeighingDialog : Window {
public int Weight = 0; public int Weight { get; private set; }
public string? Reason = null; public string? Reason { get; private set; }
public ManualWeighingDialog(string? reason = null) { public ManualWeighingDialog(string? reason = null) {
InitializeComponent(); InitializeComponent();
+55
View File
@@ -0,0 +1,55 @@
<Window x:Class="Elwig.Dialogs.TareCorrectionDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Dialogs"
xmlns:ctrl="clr-namespace:Elwig.Controls"
ResizeMode="NoResize" ShowInTaskbar="False" Topmost="True"
WindowStartupLocation="CenterOwner"
FocusManager.FocusedElement="{Binding ElementName=TargetTareInput}"
Title="Tara korrigieren" Height="170" Width="300">
<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="Button">
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="25"/>
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<Label Content="Ist-Tara:" Margin="10,20,0,10" Grid.Column="1"/>
<ctrl:UnitTextBox x:Name="ActualTareInput" Grid.Column="2" Width="56" Height="25" Unit="kg" IsReadOnly="True"
Margin="0,20,10,10" HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="14" Padding="2"
TextChanged="TareInput_TextChanged"/>
<Label Content="Soll-Tara:" Margin="10,50,0,10" Grid.Column="1"/>
<ctrl:UnitTextBox x:Name="TargetTareInput" Grid.Column="2" Width="56" Height="25" Unit="kg"
Margin="0,50,10,10" HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="14" Padding="2"
TextChanged="TareInput_TextChanged"/>
<Button x:Name="ConfirmButton" Content="Bestätigen" Margin="10,10,115,10" Grid.ColumnSpan="3" IsEnabled="False" IsDefault="True"
Click="ConfirmButton_Click"/>
<Button x:Name="CancelButton" Content="Abbrechen" Margin="10,10,10,10" Grid.ColumnSpan="3" IsCancel="True"/>
</Grid>
</Window>
@@ -0,0 +1,38 @@
using Elwig.Helpers;
using System.Windows;
using System.Windows.Controls;
namespace Elwig.Dialogs {
public partial class TareCorrectionDialog : Window {
public int ActualTare { get; private set; }
public int TargetTare { get; private set; }
public TareCorrectionDialog(int? actualTare, int? targetTare = null) {
InitializeComponent();
ActualTare = actualTare ?? 0;
TargetTare = targetTare ?? actualTare ?? 0;
ActualTareInput.IsReadOnly = actualTare != null;
ActualTareInput.Text = ActualTare.ToString();
TargetTareInput.Text = TargetTare.ToString();
TargetTareInput.SelectAll();
}
private void ConfirmButton_Click(object sender, RoutedEventArgs evt) {
DialogResult = true;
ActualTare = int.Parse(ActualTareInput.Text);
TargetTare = int.Parse(TargetTareInput.Text);
Close();
}
private void UpdateButtons() {
ConfirmButton.IsEnabled = ActualTareInput.Text.Length > 0 && TargetTareInput.Text.Length > 0;
}
private void TareInput_TextChanged(object sender, TextChangedEventArgs evt) {
if (sender is not TextBox b) return;
Validator.CheckInteger(b, true, 4);
UpdateButtons();
}
}
}
+29 -14
View File
@@ -34,19 +34,23 @@ namespace Elwig.Documents {
} }
} }
protected Cell NewAsideCell(Paragraph text, int colspan = 1, bool isName = false) { protected Cell NewAsideCell(Paragraph text, int colspan = 1, bool isName = false, bool right = false, bool center = false) {
var cell = NewCell(text, colspan: colspan).SetPaddingsMM(0.25f, 0.5f, 0.25f, isName ? 1 : 0); var cell = NewCell(text, colspan: colspan).SetPaddingsMM(0.25f, center ? 0 : right ? 1 : 0.5f, 0.25f, isName ? 1 : 0);
if (colspan > 1) { if (colspan == 26) {
cell.SetTextAlignment(TextAlignment.CENTER).SetFont(BF) cell.SetTextAlignment(TextAlignment.CENTER).SetFont(BF)
.SetBackgroundColor(new DeviceRgb(0xe0, 0xe0, 0xe0)) .SetBackgroundColor(new DeviceRgb(0xe0, 0xe0, 0xe0))
.SetBorderTop(new SolidBorder(new DeviceRgb(0x80, 0x80, 0x80), BorderThickness)) .SetBorderTop(new SolidBorder(new DeviceRgb(0x80, 0x80, 0x80), BorderThickness))
.SetPaddingsMM(0.5f, 1, 0.5f, 1); .SetPaddingsMM(0.5f, 1, 0.5f, 1);
} else if (isName) {
cell.SetBorderLeft(new SolidBorder(new DeviceRgb(0x80, 0x80, 0x80), BorderThickness));
} }
if (right) cell.SetTextAlignment(TextAlignment.RIGHT);
if (center) cell.SetTextAlignment(TextAlignment.CENTER);
return cell; return cell;
} }
protected Cell NewAsideCell(string text, int colspan = 1, bool isName = false) { protected Cell NewAsideCell(string text, int colspan = 1, bool isName = false, bool right = false, bool center = false) {
return NewAsideCell(new KernedParagraph(text, 10), colspan, isName); return NewAsideCell(new KernedParagraph(text, 10), colspan, isName, right, center);
} }
public BusinessDocument(string title, Member m, DateOnly? dateFrom, bool includeSender = false) : public BusinessDocument(string title, Member m, DateOnly? dateFrom, bool includeSender = false) :
@@ -60,16 +64,27 @@ namespace Elwig.Documents {
protected override void BeforeRenderBody(iText.Layout.Document doc, PdfDocument pdf) { protected override void BeforeRenderBody(iText.Layout.Document doc, PdfDocument pdf) {
base.BeforeRenderBody(doc, pdf); base.BeforeRenderBody(doc, pdf);
var uid = new KernedParagraph(Member.UstIdNr ?? "-", 10); Aside = new Table(ColsMM(2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5))
if (!Member.IsBuchführend) uid.Add(Normal(" ")).Add(Italic("(pauschaliert)")); .SetWidth(UnitValue.CreatePointValue(65 * PtInMM)).SetFixedLayout()
Aside = new Table(ColsMM(22.5, 42.5))
.SetFont(NF).SetFontSize(10)
.SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE) .SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE)
.SetFont(NF).SetFontSize(10)
.SetBorder(new SolidBorder(new DeviceRgb(0x80, 0x80, 0x80), BorderThickness)) .SetBorder(new SolidBorder(new DeviceRgb(0x80, 0x80, 0x80), BorderThickness))
.AddCell(NewAsideCell("Mitglied", 2)) .AddCell(NewAsideCell("Mitglied", 26));
.AddCell(NewAsideCell("Mitglieds-Nr.:", isName: true)).AddCell(NewAsideCell($"{Member.MgNr}")) if (Member.IsOrganic) {
.AddCell(NewAsideCell("Betriebs-Nr.:", isName: true)).AddCell(NewAsideCell(Member.LfbisNr ?? "")) Aside
.AddCell(NewAsideCell("UID:", isName: true)).AddCell(NewAsideCell(uid)); .AddCell(NewAsideCell("Mitgl.-Nr.:", 8, isName: true)).AddCell(NewAsideCell($"{Member.MgNr}", 4, right: true))
.AddCell(NewAsideCell("Bio-KSt.:", 6, isName: true)).AddCell(NewAsideCell(Member.OrganicAuthorityCode ?? "-", 8, right: true, center: Member.OrganicAuthorityCode == null))
.AddCell(NewAsideCell("Betr.-Nr.:", 6, isName: true)).AddCell(NewAsideCell(Member.LfbisNr ?? "-", 6, right: true, center: Member.LfbisNr == null))
.AddCell(NewAsideCell("OOC-ID:", 6, isName: true)).AddCell(NewAsideCell(Member.OrganicOperatorId ?? "-", 8, right: true, center: Member.OrganicOperatorId == null))
.AddCell(NewAsideCell("UID:", 6, isName: true)).AddCell(NewAsideCell(Member.UstIdNr ?? "-", 10, right: true, center: Member.UstIdNr == null))
.AddCell(NewAsideCell(new KernedParagraph("", 10).Add(Italic(Member.IsBuchführend ? "(buchführend)" : "(pauschaliert)")), 10, center: true));
} else {
Aside
.AddCell(NewAsideCell("Mitglieds-Nr.:", 12, isName: true)).AddCell(NewAsideCell($"{Member.MgNr}", 4, right: true)).AddCell(NewAsideCell("", 10))
.AddCell(NewAsideCell("Betriebs-Nr.:", 10, isName: true)).AddCell(NewAsideCell(Member.LfbisNr ?? "-", 6, right: true, center: Member.LfbisNr == null)).AddCell(NewAsideCell("", 10))
.AddCell(NewAsideCell("UID:", 6, isName: true)).AddCell(NewAsideCell(Member.UstIdNr ?? "-", 10, right: true, center: Member.UstIdNr == null))
.AddCell(NewAsideCell(new KernedParagraph("", 10).Add(Italic(Member.IsBuchführend ? "(buchführend)" : "(pauschaliert)")), 10, center: true));
}
} }
protected void RenderAddress(Canvas canvas, Rectangle pageSize) { protected void RenderAddress(Canvas canvas, Rectangle pageSize) {
@@ -125,7 +140,7 @@ namespace Elwig.Documents {
.Item(c.Address).Item($"{c.Plz} {c.Ort}").Item("Österreich").Item("Tel.", c.PhoneNr).Item("Fax", c.FaxNr).NextLine() .Item(c.Address).Item($"{c.Plz} {c.Ort}").Item("Österreich").Item("Tel.", c.PhoneNr).Item("Fax", c.FaxNr).NextLine()
.Item(c.EmailAddress != null ? link1 : null) .Item(c.EmailAddress != null ? link1 : null)
.Item(c.Website != null ? link2 : null) .Item(c.Website != null ? link2 : null)
.Item("Betriebs-Nr.", c.LfbisNr).Item("Bio-KSt.", c.OrganicAuthority).NextLine() .Item("Betriebs-Nr.", c.LfbisNr).Item("Bio-KSt.", c.OrganicAuthorityCode).NextLine()
.Item("UID", c.UstIdNr).Item("BIC", c.Bic).Item("IBAN", c.Iban) .Item("UID", c.UstIdNr).Item("BIC", c.Bic).Item("IBAN", c.Iban)
.ToLeafElements()); .ToLeafElements());
} }
+4 -4
View File
@@ -125,10 +125,10 @@ namespace Elwig.Documents {
protected override void BeforeRenderBody(iText.Layout.Document doc, PdfDocument pdf) { protected override void BeforeRenderBody(iText.Layout.Document doc, PdfDocument pdf) {
base.BeforeRenderBody(doc, pdf); base.BeforeRenderBody(doc, pdf);
Aside?.AddCell(NewAsideCell("Gutschrift", 2)) Aside?.AddCell(NewAsideCell("Gutschrift", 26))
.AddCell(NewAsideCell("TG-Nr.:", isName: true)).AddCell(NewAsideCell(Payment?.Credit != null ? $"{Payment.Credit.Year}/{Payment.Credit.TgNr:000}" : "-")) .AddCell(NewAsideCell("Tr.-Gutschr.-Nr.:", 12, isName: true)).AddCell(NewAsideCell(Payment?.Credit != null ? $"{Payment.Credit.Year}/{Payment.Credit.TgNr:000}" : "-", 14))
.AddCell(NewAsideCell("Datum:", isName: true)).AddCell(NewAsideCell($"{Payment?.Variant.Date:dd.MM.yyyy}")) .AddCell(NewAsideCell("Datum:", 12, isName: true)).AddCell(NewAsideCell($"{Payment?.Variant.Date:dd.MM.yyyy}", 14))
.AddCell(NewAsideCell("Überw. am:", isName: true)).AddCell(NewAsideCell($"{Payment?.Variant.TransferDate:dd.MM.yyyy}")); .AddCell(NewAsideCell("Überwiesen am:", 12, isName: true)).AddCell(NewAsideCell($"{Payment?.Variant.TransferDate:dd.MM.yyyy}", 14));
} }
protected override void RenderBody(iText.Layout.Document doc, PdfDocument pdf) { protected override void RenderBody(iText.Layout.Document doc, PdfDocument pdf) {
+5 -5
View File
@@ -30,7 +30,7 @@ namespace Elwig.Documents {
_year = year; _year = year;
ShowDateAndLocation = true; ShowDateAndLocation = true;
UseBillingAddress = true; UseBillingAddress = true;
DocumentId = $"Anl.-Best. {_year}/{m.MgNr}"; DocumentId = $"Anlfrg.-Bstng. {_year}/{m.MgNr}";
Data = data; Data = data;
} }
@@ -55,10 +55,10 @@ namespace Elwig.Documents {
base.BeforeRenderBody(doc, pdf); base.BeforeRenderBody(doc, pdf);
var firstDay = Data.Rows.MinBy(r => r.Date)?.Date; var firstDay = Data.Rows.MinBy(r => r.Date)?.Date;
var lastDay = Data.Rows.MaxBy(r => r.Date)?.Date; var lastDay = Data.Rows.MaxBy(r => r.Date)?.Date;
Aside?.AddCell(NewAsideCell("Saison", 2)) Aside?.AddCell(NewAsideCell("Saison", 26))
.AddCell(NewAsideCell("Lieferungen:", isName: true)).AddCell(NewAsideCell($"{Data.Rows.DistinctBy(r => r.LsNr).Count():N0} (Teil-Lfrg.: {Data.RowNum:N0})")) .AddCell(NewAsideCell("Lieferungen:", 12, isName: true)).AddCell(NewAsideCell($"{Data.Rows.DistinctBy(r => r.LsNr).Count():N0} (Teil-Lfrg.: {Data.RowNum:N0})", 14))
.AddCell(NewAsideCell("Zeitraum:", isName: true)).AddCell(NewAsideCell(firstDay == null || lastDay == null ? "-" : firstDay == lastDay ? $"{firstDay:dd.MM.} (1 Tag)" : $"{firstDay:dd.MM.}\u2013{lastDay:dd.MM.} ({lastDay?.DayNumber - firstDay?.DayNumber + 1:N0} Tage)")) .AddCell(NewAsideCell("Zeitraum:", 8, isName: true)).AddCell(NewAsideCell(firstDay == null || lastDay == null ? "-" : firstDay == lastDay ? $"{firstDay:dd.MM.} (1 Tag)" : $"{firstDay:dd.MM.}\u2013{lastDay:dd.MM.} ({lastDay?.DayNumber - firstDay?.DayNumber + 1:N0} Tage)", 18, center: true))
.AddCell(NewAsideCell("Menge:", isName: true)).AddCell(NewAsideCell($"{MemberStats.Sum(s => s.Weight):N0} kg")); .AddCell(NewAsideCell("Menge:", 12, isName: true)).AddCell(NewAsideCell($"{MemberStats.Sum(s => s.Weight):N0} kg", 14));
} }
protected override void RenderBody(iText.Layout.Document doc, PdfDocument pdf) { protected override void RenderBody(iText.Layout.Document doc, PdfDocument pdf) {
+30 -18
View File
@@ -72,10 +72,10 @@ namespace Elwig.Documents {
protected override void BeforeRenderBody(iText.Layout.Document doc, PdfDocument pdf) { protected override void BeforeRenderBody(iText.Layout.Document doc, PdfDocument pdf) {
base.BeforeRenderBody(doc, pdf); base.BeforeRenderBody(doc, pdf);
Aside?.AddCell(NewAsideCell("Lieferung", 2)) Aside?.AddCell(NewAsideCell("Lieferung", 26))
.AddCell(NewAsideCell("LS-Nr.:", isName: true)).AddCell(NewAsideCell(Delivery.LsNr)) .AddCell(NewAsideCell("Lieferschein-Nr.:", 12, isName: true)).AddCell(NewAsideCell(Delivery.LsNr, 14))
.AddCell(NewAsideCell("Datum/Zeit:", isName: true)).AddCell(NewAsideCell($"{Delivery.Date:dd.MM.yyyy} / {Delivery.Time:HH:mm}")) .AddCell(NewAsideCell("Datum/Zeit:", 12, isName: true)).AddCell(NewAsideCell($"{Delivery.Date:dd.MM.yyyy} / {Delivery.Time:HH:mm}", 14))
.AddCell(NewAsideCell("Zweigstelle:", isName: true)).AddCell(NewAsideCell(Delivery.Branch.Name)); .AddCell(NewAsideCell("Zweigstelle:", 12, isName: true)).AddCell(NewAsideCell(Delivery.Branch.Name, 14));
} }
protected override void RenderBody(iText.Layout.Document doc, PdfDocument pdf) { protected override void RenderBody(iText.Layout.Document doc, PdfDocument pdf) {
@@ -121,7 +121,7 @@ namespace Elwig.Documents {
} }
protected Table NewDeliveryTable() { protected Table NewDeliveryTable() {
var tbl = new Table(ColsMM(10, 21, 25, 19.5, 19.5, 30, 12.5, 12.5, 15), true) var tbl = new Table(ColsMM(10, 21, 25, 20, 20, 30, 12.5, 12.5, 14), true)
.SetWidth(UnitValue.CreatePercentValue(100)).SetFixedLayout() .SetWidth(UnitValue.CreatePercentValue(100)).SetFixedLayout()
.SetBorder(Border.NO_BORDER).SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE); .SetBorder(Border.NO_BORDER).SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE);
@@ -130,15 +130,16 @@ namespace Elwig.Documents {
.AddHeaderCell(NewTh("Attribut", colspan: 2, rowspan: 2, left: true)) .AddHeaderCell(NewTh("Attribut", colspan: 2, rowspan: 2, left: true))
.AddHeaderCell(NewTh("Qualitätsstufe", rowspan: 2, left: true)) .AddHeaderCell(NewTh("Qualitätsstufe", rowspan: 2, left: true))
.AddHeaderCell(NewTh("Gradation", colspan: 2)) .AddHeaderCell(NewTh("Gradation", colspan: 2))
.AddHeaderCell(NewTh("Menge")) .AddHeaderCell(NewTh("Menge").SetPaddingRight(0))
.AddHeaderCell(NewTh("[°Oe]", 8)) .AddHeaderCell(NewTh("[°Oe]", 8))
.AddHeaderCell(NewTh("[°KMW]", 8)) .AddHeaderCell(NewTh("[°KMW]", 8))
.AddHeaderCell(NewTh("[kg]", 8)); .AddHeaderCell(NewTh("[kg]", 8).SetPaddingRight(0));
foreach (var part in Delivery.Parts.OrderBy(p => p.DPNr)) { foreach (var part in Delivery.Parts.OrderBy(p => p.DPNr)) {
var sub = new Table(ColsMM(10, 21, 25, 19.5, 19.5, 30, 12.5, 12.5, 15), true) var sub = new Table(ColsMM(10, 21, 25, 20, 20, 30, 12.5, 12.5, 14), true)
.SetWidth(UnitValue.CreatePercentValue(100)).SetFixedLayout() .SetWidth(UnitValue.CreatePercentValue(100)).SetFixedLayout()
.SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE); .SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE)
.SetKeepTogether(true);
int rowspan = 1 + (part.Cultivation != null ? 1 : 0) + 1 + part.Modifiers.Count() + 1 + (part.Comment != null ? 1 : 0) + (part.Temperature != null || part.Acid != null ? 1 : 0); int rowspan = 1 + (part.Cultivation != null ? 1 : 0) + 1 + part.Modifiers.Count() + 1 + (part.Comment != null ? 1 : 0) + (part.Temperature != null || part.Acid != null ? 1 : 0);
sub.AddCell(NewDeliveryMainTd($"{part.DPNr:N0}", center: true)) sub.AddCell(NewDeliveryMainTd($"{part.DPNr:N0}", center: true))
@@ -147,11 +148,14 @@ namespace Elwig.Documents {
.AddCell(NewDeliveryMainTd(part.Quality.Name)) .AddCell(NewDeliveryMainTd(part.Quality.Name))
.AddCell(NewDeliveryMainTd($"{part.Oe:N0}", center: true)) .AddCell(NewDeliveryMainTd($"{part.Oe:N0}", center: true))
.AddCell(NewDeliveryMainTd($"{part.Kmw:N1}", center: true)) .AddCell(NewDeliveryMainTd($"{part.Kmw:N1}", center: true))
.AddCell(NewDeliveryMainTd($"{part.Weight:N0}", center: true)); .AddCell(NewDeliveryMainTd($"{part.Weight:N0}", right: true).SetPaddingRight(0));
if (part.Cultivation != null) { if (part.Cultivation != null) {
var cult = new KernedParagraph(8); var cult = new KernedParagraph(8);
cult.Add(Italic("Bewirtschaftung:")).Add(Normal(" " + part.Cultivation.Name + (part.Cultivation.Description != null ? $" ({part.Cultivation.Description})" : ""))); cult.Add(Italic("Bewirtschaftung:")).Add(Normal(" " + part.Cultivation.Name + (part.Cultivation.Description != null ? $" ({part.Cultivation.Description})" : "")));
if (part.Cultivation.IsOrganic) {
cult.Add(" \u2013 ").Add(Italic("Kontrollstelle:")).Add(Normal($" {Member.OrganicAuthorityCode ?? "?"}"));
}
sub.AddCell(NewTd()) sub.AddCell(NewTd())
.AddCell(NewTd(cult, colspan: 5)) .AddCell(NewTd(cult, colspan: 5))
.AddCell(NewTd(colspan: 3)); .AddCell(NewTd(colspan: 3));
@@ -181,8 +185,8 @@ namespace Elwig.Documents {
} }
} else { } else {
var info = part.WeighingInfo; var info = part.WeighingInfo;
weighing.Add(Italic("Waage:")) weighing.Add(Italic("Waage/Terminal:"))
.Add(Normal(" " + (part.ScaleId ?? "?") + ", ")) .Add(Normal(" " + (info.ScaleId ?? "?") + "/" + (part.ScaleTerminalId ?? "?") + ", "))
.Add(Italic("ID:")) .Add(Italic("ID:"))
.Add(Normal(" " + (info.Id ?? "?"))); .Add(Normal(" " + (info.Id ?? "?")));
if (info.Date != null || info.Time != null) if (info.Date != null || info.Time != null)
@@ -196,9 +200,17 @@ namespace Elwig.Documents {
.Add(Italic("Brutto:")) .Add(Italic("Brutto:"))
.Add(Normal($" {info.Gross:N0} kg \u2013 ")) .Add(Normal($" {info.Gross:N0} kg \u2013 "))
.Add(Italic("Tara:")) .Add(Italic("Tara:"))
.Add(Normal($" {info.Tare:N0} kg \u2013 ")) .Add(Normal($" {info.Tare:N0} kg "));
if (info.TareCorrection != null && info.TareCorrection != 0)
weighing.Add(Bold($"(Korr.: {Utils.GetSign(info.TareCorrection.Value)}{Math.Abs(info.TareCorrection.Value)} kg)")).Add(" ");
weighing
.Add(Normal("\u2013 "))
.Add(Italic("Netto:")) .Add(Italic("Netto:"))
.Add(Normal($" {info.Net:N0} kg \u2013 ")) .Add(Normal($" {info.Net:N0} kg "));
if (info.TareCorrection != null && info.TareCorrection != 0)
weighing.Add(Normal($"({Utils.GetSign(-info.TareCorrection.Value)}{Math.Abs(info.TareCorrection.Value)} kg) "));
weighing
.Add(Normal("\u2013 "))
.Add(Italic(part.IsNetWeight ? "gerebelt gewogen" : "nicht gerebelt gewogen")); .Add(Italic(part.IsNetWeight ? "gerebelt gewogen" : "nicht gerebelt gewogen"));
} else { } else {
weighing.Add(" ") weighing.Add(" ")
@@ -210,7 +222,7 @@ namespace Elwig.Documents {
.AddCell(NewCell(colspan: 3)); .AddCell(NewCell(colspan: 3));
if (part.Comment != null) { if (part.Comment != null) {
sub.AddCell(NewCell()) sub.AddCell(NewCell())
.AddCell(NewCell(new KernedParagraph(8).Add(Italic("Anmerkung")).Add(Normal(" " + part.Comment)), colspan: 5)) .AddCell(NewCell(new KernedParagraph(8).Add(Italic("Anmerkung:")).Add(Normal(" " + part.Comment)), colspan: 5))
.AddCell(NewCell(colspan: 3)); .AddCell(NewCell(colspan: 3));
} }
if (part.Temperature != null || part.Acid != null) { if (part.Temperature != null || part.Acid != null) {
@@ -220,7 +232,7 @@ namespace Elwig.Documents {
if (part.Temperature != null && part.Acid != null) if (part.Temperature != null && part.Acid != null)
p.Add(Normal(", ")); p.Add(Normal(", "));
if (part.Acid != null) if (part.Acid != null)
p.Add(Italic("Säure")).Add(Normal($" {part.Acid:N1} g/l")); p.Add(Italic("Säure:")).Add(Normal($" {part.Acid:N1} g/l"));
sub.AddCell(NewCell()) sub.AddCell(NewCell())
.AddCell(NewCell(p, colspan: 5)) .AddCell(NewCell(p, colspan: 5))
.AddCell(NewCell(colspan: 3)); .AddCell(NewCell(colspan: 3));
@@ -229,10 +241,10 @@ namespace Elwig.Documents {
} }
if (Delivery.Parts.Count > 1) { if (Delivery.Parts.Count > 1) {
tbl.AddCell(NewTd("Gesamt:", 12, bold: true, borderTop: true, colspan: 6).SetPaddingsMM(1, 1, 1, 1)) tbl.AddCell(NewTd("Gesamt:", 12, bold: true, borderTop: true, colspan: 6).SetPaddingsMM(1, 1, 1, 0))
.AddCell(NewTd($"{Delivery.Oe:N0}", 12, bold: true, center: true, borderTop: true).SetPaddingsMM(1, 1, 1, 1)) .AddCell(NewTd($"{Delivery.Oe:N0}", 12, bold: true, center: true, borderTop: true).SetPaddingsMM(1, 1, 1, 1))
.AddCell(NewTd($"{Delivery.Kmw:N1}", 12, bold: true, center: true, borderTop: true).SetPaddingsMM(1, 1, 1, 1)) .AddCell(NewTd($"{Delivery.Kmw:N1}", 12, bold: true, center: true, borderTop: true).SetPaddingsMM(1, 1, 1, 1))
.AddCell(NewTd($"{Delivery.Weight:N0}", 12, bold: true, right: true, borderTop: true).SetPaddingsMM(1, 1, 1, 1)); .AddCell(NewTd($"{Delivery.Weight:N0}", 12, bold: true, right: true, borderTop: true).SetPaddingsMM(1, 0, 1, 1));
} }
return tbl; return tbl;
+2 -2
View File
@@ -57,7 +57,7 @@ namespace Elwig.Documents {
public Document(string title) { public Document(string title) {
Title = title; Title = title;
Author = App.Client.NameFull; Author = App.Client.NameFull;
Date = DateOnly.FromDateTime(Utils.Today); Date = DateOnly.FromDateTime(DateTime.Today);
} }
~Document() { ~Document() {
@@ -252,7 +252,7 @@ namespace Elwig.Documents {
public async Task Print(int copies = 1) { public async Task Print(int copies = 1) {
if (PdfPath == null) throw new InvalidOperationException("Pdf file has not been generated yet"); if (PdfPath == null) throw new InvalidOperationException("Pdf file has not been generated yet");
await Pdf.Print(PdfPath, copies, IsDoublePaged); Pdf.Print(PdfPath, copies, IsDoublePaged);
} }
public void Show() { public void Show() {
+2 -1
View File
@@ -161,7 +161,8 @@ namespace Elwig.Documents {
.AddCell(NewDataTh("Buchführend:", colspan: 2)).AddCell(NewTd(new KernedParagraph(Member.IsBuchführend ? "Ja " : "Nein ", 10) .AddCell(NewDataTh("Buchführend:", colspan: 2)).AddCell(NewTd(new KernedParagraph(Member.IsBuchführend ? "Ja " : "Nein ", 10)
.Add(Normal($"({(Member.IsBuchführend ? season.VatNormal : season.VatFlatrate) * 100:N0}% USt.)", 8)), colspan: 2)) .Add(Normal($"({(Member.IsBuchführend ? season.VatNormal : season.VatFlatrate) * 100:N0}% USt.)", 8)), colspan: 2))
.AddCell(NewDataTh("(Katastralgemeinde mit dem größten Anteil an Weinbauflächen)", 8, colspan: 2)) .AddCell(NewDataTh("(Katastralgemeinde mit dem größten Anteil an Weinbauflächen)", 8, colspan: 2))
.AddCell(NewDataTh("Bio:", colspan: 2)).AddCell(NewTd(Member.IsOrganic ? "Ja" : "Nein", colspan: 2)) .AddCell(NewDataTh("Bio:", colspan: 2)).AddCell(NewTd(new KernedParagraph(Member.IsOrganic ? "Ja" : "Nein", 10)
.Add(Normal(Member.OrganicAuthorityCode != null || Member.OrganicOperatorId != null ? $" ({Member.OrganicAuthorityCode}{(Member.OrganicAuthorityCode != null && Member.OrganicOperatorId != null ? ", " : "")}{Member.OrganicOperatorId})" : "", 8)), colspan: 2))
.AddCell(NewDataHdr("Genossenschaft", colspan: 6)) .AddCell(NewDataHdr("Genossenschaft", colspan: 6))
.AddCell(NewDataTh("Status:")).AddCell(NewTd(new KernedParagraph(Member.IsActive ? "Aktiv " : "Nicht aktiv ", 10) .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)))) .Add(Normal("(" + (Member.ExitDate != null ? $"{Member.EntryDate:dd.MM.yyyy}\u2013{Member.ExitDate:dd.MM.yyyy}" : $"seit {Member.EntryDate:dd.MM.yyyy}") + ")", 8))))
+11 -11
View File
@@ -9,10 +9,10 @@
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<PreserveCompilationContext>true</PreserveCompilationContext> <PreserveCompilationContext>true</PreserveCompilationContext>
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon> <ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
<Version>1.1.0.0</Version> <Version>1.1.2.2</Version>
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages> <SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ApplicationManifest>app.manifest</ApplicationManifest> <ApplicationManifest>App.manifest</ApplicationManifest>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -23,21 +23,21 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="bblanchon.PDFium.Win32" Version="152.0.7934" /> <PackageReference Include="bblanchon.PDFium.Win32" Version="155.0.8044" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
<PackageReference Include="itext" Version="9.7.0" /> <PackageReference Include="itext" Version="9.7.0" />
<PackageReference Include="itext.bouncy-castle-adapter" Version="9.7.0" /> <PackageReference Include="itext.bouncy-castle-adapter" Version="9.7.0" />
<PackageReference Include="LinqKit" Version="1.3.11" /> <PackageReference Include="LinqKit" Version="1.3.11" />
<PackageReference Include="MailKit" Version="4.17.0" /> <PackageReference Include="MailKit" Version="4.18.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.9" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.12" />
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.9" /> <PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.12" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.4078.44" /> <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.4191.47" />
<PackageReference Include="NJsonSchema" Version="11.6.1" /> <PackageReference Include="NJsonSchema" Version="11.6.1" />
<PackageReference Include="ScottPlot.WPF" Version="5.1.59" /> <PackageReference Include="ScottPlot.WPF" Version="5.1.59" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.3" /> <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.5" />
<PackageReference Include="System.IO.Hashing" Version="10.0.9" /> <PackageReference Include="System.IO.Hashing" Version="10.0.12" />
<PackageReference Include="System.IO.Ports" Version="10.0.9" /> <PackageReference Include="System.IO.Ports" Version="10.0.12" />
<PackageReference Include="System.Management" Version="10.0.9" /> <PackageReference Include="System.Management" Version="10.0.12" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+2 -1
View File
@@ -69,7 +69,8 @@ namespace Elwig.Helpers {
public DbSet<OverUnderDeliveryRow> OverUnderDeliveryRows { get; private set; } public DbSet<OverUnderDeliveryRow> OverUnderDeliveryRows { get; private set; }
public DbSet<AreaComUnderDeliveryRowSingle> AreaComUnderDeliveryRows { get; private set; } public DbSet<AreaComUnderDeliveryRowSingle> AreaComUnderDeliveryRows { get; private set; }
public DbSet<MemberDeliveryPerVarietyRowSingle> MemberDeliveryPerVariantRows { get; private set; } public DbSet<MemberDeliveryPerVarietyRowSingle> MemberDeliveryPerVariantRows { get; private set; }
public DbSet<MemberAreaComsRowSingle> MemberAreaComsRows { get; private set; } public DbSet<MemberAreaComTotalsRowSingle> MemberAreaComTotalsRows { get; private set; }
public DbSet<MemberAreaComsRow> MemberAreaComsRows { get; private set; }
public DbSet<CreditNoteDeliveryRowSingle> CreditNoteDeliveryRows { get; private set; } public DbSet<CreditNoteDeliveryRowSingle> CreditNoteDeliveryRows { get; private set; }
public DbSet<CreditNoteRowSingle> CreditNoteRows { get; private set; } public DbSet<CreditNoteRowSingle> CreditNoteRows { get; private set; }
public DbSet<WeightBreakdownRow> WeightBreakDownRows { get; private set; } public DbSet<WeightBreakdownRow> WeightBreakDownRows { get; private set; }
+70 -11
View File
@@ -1,3 +1,5 @@
using Elwig.Services;
using Microsoft.Data.Sqlite;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@@ -9,15 +11,22 @@ namespace Elwig.Helpers {
public static class AppDbUpdater { public static class AppDbUpdater {
// Don't forget to update value in Tests/fetch-resources.bat! // Don't forget to update value in Tests/fetch-resources.bat!
public static readonly int RequiredSchemaVersion = 40; public static readonly int RequiredSchemaVersion = 42;
private static int VersionOffset = 0; private static int VersionOffset = 0;
public static async Task<Version> CheckDb() { public static async Task<Version> CheckDb() {
if (!File.Exists(App.Config.DatabaseFile))
throw new FileNotFoundException($"Die Datei \"{App.Config.DatabaseFile}\" exisitiert nicht");
long? applId, schemaVers; long? applId, schemaVers;
using (var cnx = await AppDbContext.ConnectAsync()) { using (var cnx = await AppDbContext.ConnectAsync()) {
await cnx.QuickCheck();
applId = (long?)await cnx.ExecuteScalar("PRAGMA application_id") ?? 0; applId = (long?)await cnx.ExecuteScalar("PRAGMA application_id") ?? 0;
if (applId != 0x454C5747) throw new Exception($"Invalid application_id in database (0x{applId:X08})"); if (applId != 0x454C5747)
throw new FileFormatException($"Invalid application_id in database (0x{applId:X08})");
schemaVers = (long?)await cnx.ExecuteScalar("PRAGMA schema_version") ?? 0; schemaVers = (long?)await cnx.ExecuteScalar("PRAGMA schema_version") ?? 0;
VersionOffset = (int)(schemaVers % 100); VersionOffset = (int)(schemaVers % 100);
@@ -61,11 +70,11 @@ namespace Elwig.Helpers {
}) })
.OrderBy(s => s.Item1).ThenBy(s => s.Item2)]; .OrderBy(s => s.Item1).ThenBy(s => s.Item2)];
List<string> toExecute = []; List<(int ToVersion, string File)> toExecute = [];
var vers = fromVersion; var vers = fromVersion;
while (vers < toVersion) { while (vers < toVersion) {
var (_, to, name) = scripts.Last(s => s.From == vers); var (_, to, name) = scripts.Last(s => s.From == vers);
toExecute.Add(name); toExecute.Add((to, name));
vers = to; vers = to;
} }
if (toExecute.Count == 0) if (toExecute.Count == 0)
@@ -76,16 +85,19 @@ namespace Elwig.Helpers {
try { try {
using var cnx = await AppDbContext.ConnectAsync(); using var cnx = await AppDbContext.ConnectAsync();
await cnx.ExecuteBatch("PRAGMA locking_mode = EXCLUSIVE"); await cnx.ExecuteBatch("PRAGMA locking_mode = EXCLUSIVE");
foreach (var script in toExecute) {
await cnx.IntegrityCheck();
await cnx.ForeignKeyCheck();
foreach (var (to, script) in toExecute) {
await cnx.ExecuteEmbeddedScript(asm, script); await cnx.ExecuteEmbeddedScript(asm, script);
if (to == 42) {
await UpdateDbSchema_41_To_42(cnx);
}
} }
var violations = await cnx.ForeignKeyCheck(); await cnx.IntegrityCheck();
if (violations.Length > 0) { await cnx.ForeignKeyCheck();
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("VACUUM");
await cnx.ExecuteBatch($"PRAGMA schema_version = {toVersion * 100 + VersionOffset}"); await cnx.ExecuteBatch($"PRAGMA schema_version = {toVersion * 100 + VersionOffset}");
@@ -96,5 +108,52 @@ namespace Elwig.Helpers {
File.Delete(backup); File.Delete(backup);
} }
} }
private static async Task UpdateDbSchema_41_To_42(SqliteConnection cnx) {
List<(int MgNr, string LfbisNr, string Name, string Address, int Plz)> members = [];
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = """
SELECT m.mgnr, m.lfbis_nr,
COALESCE(a.name, (COALESCE(m.prefix || ' ', '') || COALESCE(m.given_name || ' ', '') || m.name || COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, ''))),
COALESCE(a.address, m.address),
COALESCE(aplz.plz, mplz.plz)
FROM member m
JOIN postal_dest mp ON (mp.country, mp.id) = (m.country, m.postal_dest)
JOIN AT_plz_dest ma ON (ma.country, ma.id) = (mp.country, mp.id)
JOIN AT_plz mplz ON mplz.plz = ma.plz
LEFT JOIN member_billing_address a ON a.mgnr = m.mgnr
LEFT JOIN postal_dest ap ON (ap.country, ap.id) = (a.country, a.postal_dest)
LEFT JOIN AT_plz_dest aa ON (aa.country, aa.id) = (ap.country, mp.id)
LEFT JOIN AT_plz aplz ON aplz.plz = aa.plz
WHERE organic
""";
using var reader = await cmd.ExecuteReaderAsync();
var header = await reader.GetColumnSchemaAsync();
while (await reader.ReadAsync()) {
members.Add((reader.GetInt32(0), reader.GetString(1), reader.GetString(2), reader.GetString(3), reader.GetInt32(4)));
}
}
if (members.Count > 0)
InteractionService.ShowInformation("Datenbank-Update", "Es wird versucht, die Bio-Kontrollstellen-Informationen der Mitglieder automatisch zu laden. Dies könnte einen kurzen Moment in Anspruch nehmen.");
foreach (var m in members) {
string? oocId = null;
string? authCode = null;
var certs = await OrganicService.FetchTracesCertificates(null, m.LfbisNr, (m.Name, m.Address, m.Plz.ToString()));
if (certs.Length > 0) {
oocId = certs[0].OperatorId;
authCode = certs[0].AuthorityCode;
}
if (oocId != null || authCode != null) {
oocId = oocId == null ? "NULL" : $"'{oocId}'";
authCode = authCode == null ? "NULL" : $"'{authCode}'";
await cnx.ExecuteBatch($"UPDATE member SET ooc_id = {oocId}, org_auth_code = {authCode} WHERE mgnr = {m.MgNr}");
}
}
}
} }
} }
+6 -3
View File
@@ -49,7 +49,8 @@ namespace Elwig.Helpers {
public string? Bic; public string? Bic;
public string? UstIdNr; public string? UstIdNr;
public string? LfbisNr; public string? LfbisNr;
public string? OrganicAuthority; public string? OrganicOperatorId;
public string? OrganicAuthorityCode;
public string? PhoneNr; public string? PhoneNr;
public string? FaxNr; public string? FaxNr;
@@ -114,7 +115,8 @@ namespace Elwig.Helpers {
UstIdNr = parameters.GetValueOrDefault("CLIENT_USTIDNR"); UstIdNr = parameters.GetValueOrDefault("CLIENT_USTIDNR");
Bic = parameters.GetValueOrDefault("CLIENT_BIC"); Bic = parameters.GetValueOrDefault("CLIENT_BIC");
Iban = parameters.GetValueOrDefault("CLIENT_IBAN"); Iban = parameters.GetValueOrDefault("CLIENT_IBAN");
OrganicAuthority = parameters.GetValueOrDefault("CLIENT_ORGANIC_AUTHORITY"); OrganicOperatorId = parameters.GetValueOrDefault("CLIENT_ORGANIC_OPERATORID");
OrganicAuthorityCode = parameters.GetValueOrDefault("CLIENT_ORGANIC_AUTHORITYCODE");
EnableMemberHistory = (parameters.GetValueOrDefault("ENABLE_MEMBERHISTORY")?.ToUpper()) switch { EnableMemberHistory = (parameters.GetValueOrDefault("ENABLE_MEMBERHISTORY")?.ToUpper()) switch {
"1" or "TRUE" or "YES" or "JA" => true, "1" or "TRUE" or "YES" or "JA" => true,
@@ -273,7 +275,8 @@ namespace Elwig.Helpers {
("CLIENT_USTIDNR", UstIdNr), ("CLIENT_USTIDNR", UstIdNr),
("CLIENT_BIC", Bic), ("CLIENT_BIC", Bic),
("CLIENT_IBAN", Iban), ("CLIENT_IBAN", Iban),
("CLIENT_ORGANIC_AUTHORITY", OrganicAuthority), ("CLIENT_ORGANIC_OPERATORID", OrganicOperatorId),
("CLIENT_ORGANIC_AUTHORITYCODE", OrganicAuthorityCode),
("ENABLE_MEMBERHISTORY", EnableMemberHistory ? "YES" : "NO"), ("ENABLE_MEMBERHISTORY", EnableMemberHistory ? "YES" : "NO"),
("MODE_BUSINESSSHARES", businessShares), ("MODE_BUSINESSSHARES", businessShares),
("MODE_DELIVERYNOTE_STATS", deliveryNoteStats), ("MODE_DELIVERYNOTE_STATS", deliveryNoteStats),
+30 -4
View File
@@ -17,10 +17,11 @@ namespace Elwig.Helpers {
public string? Filling; public string? Filling;
public string? Limit; public string? Limit;
public bool Required; public bool Required;
public bool SyncTime;
public string? Log; public string? Log;
public string? _Log; public string? _Log;
public ScaleConfig(string id, string? type, string? model, string? cnx, string? empty, string? filling, string? limit, bool? required, string? log) { public ScaleConfig(string id, string? type, string? model, string? cnx, string? empty, string? filling, string? limit, bool? required, bool? syncTime, string? log) {
Id = id; Id = id;
Type = type; Type = type;
Model = model; Model = model;
@@ -29,6 +30,7 @@ namespace Elwig.Helpers {
Filling = filling; Filling = filling;
Limit = limit; Limit = limit;
Required = required ?? true; Required = required ?? true;
SyncTime = syncTime ?? true;
_Log = log; _Log = log;
Log = log != null ? Path.Combine(App.DataPath, log) : null; Log = log != null ? Path.Combine(App.DataPath, log) : null;
} }
@@ -37,19 +39,30 @@ namespace Elwig.Helpers {
public class Config { public class Config {
private static readonly string[] TrueValues = ["1", "true", "yes", "on"]; private static readonly string[] TrueValues = ["1", "true", "yes", "on"];
private static readonly string[] FalseValues = ["0", "false", "no", "off"];
private readonly string FileName; private readonly string FileName;
public bool Debug; public bool Debug;
public string DatabaseFile = App.DataPath + "database.sqlite3"; public string DatabaseFile = Path.Combine(App.DataPath, "database.sqlite3");
public string? DatabaseLog = null; public string? DatabaseLog = null;
public string? Branch = null; public string? Branch = null;
public WeighingMode? WeighingMode; public WeighingMode? WeighingMode;
public bool BackupAuto = true;
public string BackupPath = Path.Combine(App.DataPath, "backups");
public int BackupRetainHours = 36;
public int BackupRetainDays = 8;
public int BackupRetainWeeks = 6;
public int BackupRetainMonths = 18;
public string? UpdateUrl = null; public string? UpdateUrl = null;
public bool UpdateAuto = false; public bool UpdateAuto = false;
public string? SyncUrl = null; public string? SyncUrl = null;
public string SyncUsername = ""; public string SyncUsername = "";
public string SyncPassword = ""; public string SyncPassword = "";
public bool SyncMain = false;
public string? SmtpHost = null; public string? SmtpHost = null;
public int? SmtpPort = null; public int? SmtpPort = null;
@@ -57,9 +70,10 @@ namespace Elwig.Helpers {
public string? SmtpUsername = null; public string? SmtpUsername = null;
public string? SmtpPassword = null; public string? SmtpPassword = null;
public string? SmtpFrom = 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 ? 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; public IList<ScaleConfig> Scales;
private readonly List<ScaleConfig> ScaleList = []; private readonly List<ScaleConfig> ScaleList = [];
@@ -80,11 +94,21 @@ namespace Elwig.Helpers {
Debug = TrueValues.Contains(config["general:debug"]?.ToLower()); Debug = TrueValues.Contains(config["general:debug"]?.ToLower());
var weighing = config["general:weighing"]; var weighing = config["general:weighing"];
WeighingMode = weighing != null && Enum.TryParse<WeighingMode>(weighing, true, out var w) ? w : null; WeighingMode = weighing != null && Enum.TryParse<WeighingMode>(weighing, true, out var w) ? w : null;
BackupAuto = !FalseValues.Contains(config["backup:auto"]?.ToLower());
BackupPath = Path.Combine(Path.GetDirectoryName(DatabaseFile) ?? App.DataPath, config["backup:path"] ?? "backups");
BackupRetainHours = int.TryParse(config["backup:retain_hours"], out var hours) ? hours : 36;
BackupRetainDays = int.TryParse(config["backup:retain_days"], out var days) ? days : 8;
BackupRetainWeeks = int.TryParse(config["backup:retain_weeks"], out var weeks) ? weeks : 6;
BackupRetainMonths = int.TryParse(config["backup:retain_months"], out var months) ? months : 18;
UpdateUrl = config["update:url"]; UpdateUrl = config["update:url"];
UpdateAuto = TrueValues.Contains(config["update:auto"]?.ToLower()); UpdateAuto = TrueValues.Contains(config["update:auto"]?.ToLower());
SyncUrl = config["sync:url"]; SyncUrl = config["sync:url"];
SyncUsername = config["sync:username"] ?? ""; SyncUsername = config["sync:username"] ?? "";
SyncPassword = config["sync:password"] ?? ""; SyncPassword = config["sync:password"] ?? "";
SyncMain = TrueValues.Contains(config["sync:main"]?.ToLower());
SmtpHost = config["smtp:host"]; SmtpHost = config["smtp:host"];
SmtpPort = config["smtp:port"]?.All(char.IsAsciiDigit) == true && config["smtp:port"]?.Length > 0 ? int.Parse(config["smtp:port"]!) : null; SmtpPort = config["smtp:port"]?.All(char.IsAsciiDigit) == true && config["smtp:port"]?.Length > 0 ? int.Parse(config["smtp:port"]!) : null;
@@ -92,6 +116,7 @@ namespace Elwig.Helpers {
SmtpUsername = config["smtp:username"]; SmtpUsername = config["smtp:username"];
SmtpPassword = config["smtp:password"]; SmtpPassword = config["smtp:password"];
SmtpFrom = config["smtp:from"]; 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(); var scales = config.AsEnumerable().Where(i => i.Key.StartsWith("scale.")).GroupBy(i => i.Key.Split(':')[0][6..]).Select(i => i.Key).Order();
ScaleList.Clear(); ScaleList.Clear();
@@ -101,6 +126,7 @@ namespace Elwig.Helpers {
s, config[$"scale.{s}:type"], config[$"scale.{s}:model"], config[$"scale.{s}:connection"], s, config[$"scale.{s}:type"], config[$"scale.{s}:model"], config[$"scale.{s}:connection"],
config[$"scale.{s}:empty"], config[$"scale.{s}:filling"], config[$"scale.{s}:limit"], config[$"scale.{s}:empty"], config[$"scale.{s}:filling"], config[$"scale.{s}:limit"],
config[$"scale.{s}:required"] != null ? TrueValues.Contains(config[$"scale.{s}:required"]?.ToLower()) : null, config[$"scale.{s}:required"] != null ? TrueValues.Contains(config[$"scale.{s}:required"]?.ToLower()) : null,
config[$"scale.{s}:synctime"] != null ? !FalseValues.Contains(config[$"scale.{s}:synctime"]?.ToLower()) : null,
config[$"scale.{s}:log"] config[$"scale.{s}:log"]
)); ));
} }
+6 -6
View File
@@ -6,7 +6,7 @@ using System;
namespace Elwig.Helpers.Export { 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> { public class Bki : Csv<Row> {
@@ -29,7 +29,7 @@ namespace Elwig.Helpers.Export {
using var cnx = await AppDbContext.ConnectAsync(); using var cnx = await AppDbContext.ConnectAsync();
using var cmd = cnx.CreateCommand(); using var cmd = cnx.CreateCommand();
cmd.CommandText = $""" 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 date, weight, type, sortid, qualid, year, hkid, kmw, oe
FROM v_bki_delivery FROM v_bki_delivery
WHERE year = {year} WHERE year = {year}
@@ -38,8 +38,8 @@ namespace Elwig.Helpers.Export {
List<Row> rows = []; List<Row> rows = [];
while (await r.ReadAsync()) { while (await r.ReadAsync()) {
rows.Add(new( 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.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(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.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) { public override string FormatRow(Row row) {
var (member, delivery) = 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 (date, weight, type, sortid, qualid, year, hkid, kmw, oe) = delivery;
var (n1, n2) = billingName == null ? (familyName, name) : Utils.SplitName(billingName, familyName); var (n1, n2) = billingName == null ? (familyName, name) : Utils.SplitName(billingName, familyName);
var (a1, a2) = Utils.SplitAddress(address); var (a1, a2) = Utils.SplitAddress(address);
var memberData = $"{lfBisNr};{n1};{n2};{a1};\t{a2};{plz};{ort}"; 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 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}"; return $"{_clientData};{memberData};{deliveryData};{vollData}";
} }
+76 -60
View File
@@ -18,73 +18,85 @@ namespace Elwig.Helpers.Export {
} }
public static async Task ExportSqlite(string filename, bool zipFile) { public static async Task ExportSqlite(string filename, bool zipFile) {
if (zipFile) { var tmp = filename + ".tmp";
File.Delete(filename); try {
using var zip = ZipFile.Open(filename, ZipArchiveMode.Create); File.Delete(tmp);
if (zipFile) {
using var zip = ZipFile.Open(tmp, ZipArchiveMode.Create);
var version = zip.CreateEntry("version", CompressionLevel.NoCompression); var version = zip.CreateEntry("version", CompressionLevel.NoCompression);
using (var writer = new StreamWriter(version.Open(), Utils.UTF8)) { using (var writer = new StreamWriter(version.Open(), Utils.UTF8)) {
await writer.WriteAsync("elwig:1"); await writer.WriteAsync("elwig:1");
}
var (applId, userVers, schemaVers, size) = await GetMeta();
var meta = zip.CreateEntry("meta.json", CompressionLevel.NoCompression);
using (var writer = new StreamWriter(meta.Open(), Utils.UTF8)) {
var obj = new JsonObject {
["timestamp"] = $"{DateTime.UtcNow:yyyy-MM-ddTHH:mm:ssZ}",
["zwstid"] = App.ZwstId,
["device"] = Environment.MachineName,
["database"] = new JsonObject {
["application_id"] = applId,
["user_version"] = userVers,
["schema_version"] = schemaVers,
["file_size"] = size,
},
};
await writer.WriteAsync(obj.ToJsonString(Utils.JsonOpts));
}
var db = zip.CreateEntryFromFile(App.Config.DatabaseFile, "database.sqlite3", CompressionLevel.SmallestSize);
} else {
File.Copy(App.Config.DatabaseFile, tmp);
} }
File.Move(tmp, filename, true);
var (applId, userVers, schemaVers, size) = await GetMeta(); } finally {
var meta = zip.CreateEntry("meta.json", CompressionLevel.NoCompression); File.Delete(tmp);
using (var writer = new StreamWriter(meta.Open(), Utils.UTF8)) {
var obj = new JsonObject {
["timestamp"] = $"{DateTime.UtcNow:yyyy-MM-ddTHH:mm:ssZ}",
["zwstid"] = App.ZwstId,
["device"] = Environment.MachineName,
["database"] = new JsonObject {
["application_id"] = applId,
["user_version"] = userVers,
["schema_version"] = schemaVers,
["file_size"] = size,
},
};
await writer.WriteAsync(obj.ToJsonString(Utils.JsonOpts));
}
var db = zip.CreateEntryFromFile(App.Config.DatabaseFile, "database.sqlite3", CompressionLevel.SmallestSize);
} else {
File.Copy(App.Config.DatabaseFile, filename, true);
} }
} }
public static async Task ExportSql(string filename, bool zipFile) { public static async Task ExportSql(string filename, bool zipFile) {
if (zipFile) { var tmp = filename + ".tmp";
File.Delete(filename); try {
using var zip = ZipFile.Open(filename, ZipArchiveMode.Create); File.Delete(tmp);
if (zipFile) {
using var zip = ZipFile.Open(tmp, ZipArchiveMode.Create);
var version = zip.CreateEntry("version", CompressionLevel.NoCompression); var version = zip.CreateEntry("version", CompressionLevel.NoCompression);
using (var writer = new StreamWriter(version.Open(), Utils.UTF8)) { using (var writer = new StreamWriter(version.Open(), Utils.UTF8)) {
await writer.WriteAsync("elwig:1"); await writer.WriteAsync("elwig:1");
} }
var (applId, userVers, schemaVers, size) = await GetMeta(); var (applId, userVers, schemaVers, size) = await GetMeta();
var meta = zip.CreateEntry("meta.json", CompressionLevel.NoCompression); var meta = zip.CreateEntry("meta.json", CompressionLevel.NoCompression);
using (var writer = new StreamWriter(meta.Open(), Utils.UTF8)) { using (var writer = new StreamWriter(meta.Open(), Utils.UTF8)) {
var obj = new JsonObject { var obj = new JsonObject {
["timestamp"] = $"{DateTime.UtcNow:yyyy-MM-ddTHH:mm:ssZ}", ["timestamp"] = $"{DateTime.UtcNow:yyyy-MM-ddTHH:mm:ssZ}",
["zwstid"] = App.ZwstId, ["zwstid"] = App.ZwstId,
["device"] = Environment.MachineName, ["device"] = Environment.MachineName,
["database"] = new JsonObject { ["database"] = new JsonObject {
["application_id"] = applId, ["application_id"] = applId,
["user_version"] = userVers, ["user_version"] = userVers,
["schema_version"] = schemaVers, ["schema_version"] = schemaVers,
["file_size"] = size, ["file_size"] = size,
}, },
}; };
await writer.WriteAsync(obj.ToJsonString(Utils.JsonOpts)); await writer.WriteAsync(obj.ToJsonString(Utils.JsonOpts));
} }
var sql = zip.CreateEntry("database.sql", CompressionLevel.SmallestSize); var sql = zip.CreateEntry("database.sql", CompressionLevel.SmallestSize);
using (var writer = new StreamWriter(sql.Open(), Utils.UTF8)) { using (var writer = new StreamWriter(sql.Open(), Utils.UTF8)) {
await ExportSql(writer);
}
} else {
using var stream = File.OpenWrite(tmp);
using var writer = new StreamWriter(stream, Utils.UTF8);
await ExportSql(writer); await ExportSql(writer);
} }
} else { File.Move(tmp, filename, true);
using var stream = File.OpenWrite(filename); } finally {
using var writer = new StreamWriter(stream, Utils.UTF8); File.Delete(tmp);
await ExportSql(writer);
} }
} }
@@ -200,7 +212,7 @@ namespace Elwig.Helpers.Export {
} }
} }
public static async Task ImportSqlite(string filename, bool zipFile = false) { public static async Task ImportSqlite(string filename, bool zipFile = false, bool keepSource = true) {
if (zipFile) { if (zipFile) {
var newName = Path.ChangeExtension(App.Config.DatabaseFile, ".new.sqlite3"); var newName = Path.ChangeExtension(App.Config.DatabaseFile, ".new.sqlite3");
try { try {
@@ -209,7 +221,7 @@ namespace Elwig.Helpers.Export {
foreach (var entry in zip.Entries) { foreach (var entry in zip.Entries) {
if (entry.Name.EndsWith(".sqlite3")) { if (entry.Name.EndsWith(".sqlite3")) {
entry.ExtractToFile(newName); entry.ExtractToFile(newName);
await ImportSqlite(newName); await ImportSqlite(newName, keepSource: false);
return; return;
} }
} }
@@ -221,7 +233,11 @@ namespace Elwig.Helpers.Export {
var oldName = Path.ChangeExtension(App.Config.DatabaseFile, ".old.sqlite3"); var oldName = Path.ChangeExtension(App.Config.DatabaseFile, ".old.sqlite3");
File.Move(App.Config.DatabaseFile, oldName, true); File.Move(App.Config.DatabaseFile, oldName, true);
File.Move(filename, App.Config.DatabaseFile, false); if (keepSource) {
File.Copy(filename, App.Config.DatabaseFile, false);
} else {
File.Move(filename, App.Config.DatabaseFile, false);
}
using var cnx = await AppDbContext.ConnectAsync(); using var cnx = await AppDbContext.ConnectAsync();
await cnx.ExecuteBatch("VACUUM"); await cnx.ExecuteBatch("VACUUM");
@@ -234,7 +250,7 @@ namespace Elwig.Helpers.Export {
using (var cnx = await AppDbContext.ConnectAsync($"Data Source=\"{newName}\"; Mode=ReadWriteCreate; Foreign Keys=False; Cache=Default; Pooling=False")) { using (var cnx = await AppDbContext.ConnectAsync($"Data Source=\"{newName}\"; Mode=ReadWriteCreate; Foreign Keys=False; Cache=Default; Pooling=False")) {
await cnx.ExecuteBatch(await reader.ReadToEndAsync()); await cnx.ExecuteBatch(await reader.ReadToEndAsync());
} }
await ImportSqlite(newName); await ImportSqlite(newName, keepSource: false);
} finally { } finally {
if (File.Exists(newName)) File.Delete(newName); if (File.Exists(newName)) File.Delete(newName);
} }
+34 -17
View File
@@ -10,7 +10,6 @@ using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Text.Json.Nodes; using System.Text.Json.Nodes;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows;
namespace Elwig.Helpers.Export { namespace Elwig.Helpers.Export {
public static class ElwigData { public static class ElwigData {
@@ -36,14 +35,16 @@ namespace Elwig.Helpers.Export {
public static async Task Import(IEnumerable<string> filenames, ImportMode mode) { public static async Task Import(IEnumerable<string> filenames, ImportMode mode) {
try { try {
Dictionary<string, Branch> branches; Dictionary<string, Branch> branches;
Dictionary<int, AT_Kg> kgs;
Dictionary<int, int> currentDids; Dictionary<int, int> currentDids;
Dictionary<string, int> currentLsNrs; Dictionary<string, int> currentLsNrs;
Dictionary<int, List<WbRd>> currentWbRde; Dictionary<int, List<WbRd>> currentWbRde;
Dictionary<int, AT_Kg> kgs; Dictionary<int, WbKg> currentWbKgs;
List<WbGl> currentWbGls; List<WbGl> currentWbGls;
using (var ctx = new AppDbContext()) { using (var ctx = new AppDbContext()) {
branches = await ctx.FetchBranches().ToDictionaryAsync(b => b.ZwstId); branches = await ctx.FetchBranches().ToDictionaryAsync(b => b.ZwstId);
kgs = await ctx.Katastralgemeinden.ToDictionaryAsync(k => k.KgNr);
currentDids = await ctx.Deliveries currentDids = await ctx.Deliveries
.GroupBy(d => d.Year) .GroupBy(d => d.Year)
.ToDictionaryAsync(g => g.Key, g => g.Max(d => d.DId)); .ToDictionaryAsync(g => g.Key, g => g.Max(d => d.DId));
@@ -52,8 +53,8 @@ namespace Elwig.Helpers.Export {
currentWbRde = await ctx.WbRde currentWbRde = await ctx.WbRde
.GroupBy(r => r.KgNr) .GroupBy(r => r.KgNr)
.ToDictionaryAsync(g => g.Key, g => g.ToList()); .ToDictionaryAsync(g => g.Key, g => g.ToList());
currentWbKgs = await ctx.WbKgs.ToDictionaryAsync(k => k.KgNr);
currentWbGls = await ctx.WbGls.ToListAsync(); currentWbGls = await ctx.WbGls.ToListAsync();
kgs = await ctx.Katastralgemeinden.Include(k => k.WbKg).ToDictionaryAsync(k => k.KgNr);
} }
var data = new List<( var data = new List<(
@@ -122,6 +123,7 @@ namespace Elwig.Helpers.Export {
var obj = JsonNode.Parse(line)!.AsObject(); var obj = JsonNode.Parse(line)!.AsObject();
var (k, g) = obj.ToWbKg(currentWbGls); var (k, g) = obj.ToWbKg(currentWbGls);
r.WbKgs.Add(k); r.WbKgs.Add(k);
currentWbKgs[k.KgNr] = k;
if (g != null) { if (g != null) {
currentWbGls[g.GlNr] = g; currentWbGls[g.GlNr] = g;
r.WbGls.Add(g); r.WbGls.Add(g);
@@ -135,7 +137,7 @@ namespace Elwig.Helpers.Export {
string? line; string? line;
while ((line = await reader.ReadLineAsync()) != null) { while ((line = await reader.ReadLineAsync()) != null) {
var obj = JsonNode.Parse(line)!.AsObject(); var obj = JsonNode.Parse(line)!.AsObject();
var (m, b, telNrs, emailAddrs, timestamps) = obj.ToMember(kgs); var (m, b, telNrs, emailAddrs, timestamps) = obj.ToMember(kgs, currentWbKgs);
r.Members.Add(m); r.Members.Add(m);
if (b != null) r.BillingAddresses.Add(b); if (b != null) r.BillingAddresses.Add(b);
r.TelephoneNumbers.AddRange(telNrs); r.TelephoneNumbers.AddRange(telNrs);
@@ -163,7 +165,7 @@ namespace Elwig.Helpers.Export {
string? line; string? line;
while ((line = await reader.ReadLineAsync()) != null) { while ((line = await reader.ReadLineAsync()) != null) {
var obj = JsonNode.Parse(line)!.AsObject(); var obj = JsonNode.Parse(line)!.AsObject();
var (contract, areaCom, wbrd, timestamps) = obj.ToAreaCom(currentWbRde); var (contract, areaCom, wbrd, timestamps) = obj.ToAreaCom(kgs, currentWbKgs, currentWbRde);
r.Contracts.Add(contract); r.Contracts.Add(contract);
r.AreaCommitments.Add(areaCom); r.AreaCommitments.Add(areaCom);
if (wbrd != null) { if (wbrd != null) {
@@ -182,7 +184,7 @@ namespace Elwig.Helpers.Export {
string? line; string? line;
while ((line = await reader.ReadLineAsync()) != null) { while ((line = await reader.ReadLineAsync()) != null) {
var obj = JsonNode.Parse(line)!.AsObject(); var obj = JsonNode.Parse(line)!.AsObject();
var (contract, areaComs, wbrd, timestamps) = obj.ToAreaComContract(currentWbRde); var (contract, areaComs, wbrd, timestamps) = obj.ToAreaComContract(kgs, currentWbKgs, currentWbRde);
r.Contracts.Add(contract); r.Contracts.Add(contract);
r.AreaCommitments.AddRange(areaComs.Select(v => v.Item1)); r.AreaCommitments.AddRange(areaComs.Select(v => v.Item1));
if (wbrd != null) { if (wbrd != null) {
@@ -203,7 +205,7 @@ namespace Elwig.Helpers.Export {
string? line; string? line;
while ((line = await reader.ReadLineAsync()) != null) { while ((line = await reader.ReadLineAsync()) != null) {
var obj = JsonNode.Parse(line)!.AsObject(); var obj = JsonNode.Parse(line)!.AsObject();
var (d, parts, mods, rde, timestamps) = obj.ToDelivery(currentLsNrs, currentDids, kgs, currentWbRde); var (d, parts, mods, rde, timestamps) = obj.ToDelivery(currentLsNrs, currentDids, kgs, currentWbKgs, currentWbRde);
r.Deliveries.Add(d); r.Deliveries.Add(d);
r.DeliveryParts.AddRange(parts.Select(p => p.Item1)); r.DeliveryParts.AddRange(parts.Select(p => p.Item1));
r.Modifiers.AddRange(mods); r.Modifiers.AddRange(mods);
@@ -636,6 +638,8 @@ namespace Elwig.Helpers.Export {
["zwstid"] = m.ZwstId, ["zwstid"] = m.ZwstId,
["lfbis_nr"] = m.LfbisNr, ["lfbis_nr"] = m.LfbisNr,
["ustid_nr"] = m.UstIdNr, ["ustid_nr"] = m.UstIdNr,
["ooc_id"] = m.OrganicOperatorId,
["org_auth_code"] = m.OrganicAuthorityCode,
["juridical_pers"] = m.IsJuridicalPerson, ["juridical_pers"] = m.IsJuridicalPerson,
["volllieferant"] = m.IsVollLieferant, ["volllieferant"] = m.IsVollLieferant,
["buchführend"] = m.IsBuchführend, ["buchführend"] = m.IsBuchführend,
@@ -680,11 +684,11 @@ namespace Elwig.Helpers.Export {
}; };
} }
public static (Member, BillingAddr?, List<MemberTelNr>, List<MemberEmailAddr>, (DateTime CreatedAt, DateTime ModifiedAt)?) ToMember(this JsonNode json, Dictionary<int, AT_Kg> kgs) { public static (Member, BillingAddr?, List<MemberTelNr>, List<MemberEmailAddr>, (DateTime CreatedAt, DateTime ModifiedAt)?) ToMember(this JsonNode json, Dictionary<int, AT_Kg> kgs, Dictionary<int, WbKg> currentWbKgs) {
var mgnr = json["mgnr"]!.AsValue().GetValue<int>(); var mgnr = json["mgnr"]!.AsValue().GetValue<int>();
var kgnr = json["default_kgnr"]?.AsValue().GetValue<int>(); var kgnr = json["default_kgnr"]?.AsValue().GetValue<int>();
if (kgnr != null && !kgs.Values.Any(k => k.WbKg?.KgNr == kgnr)) { if (kgnr != null && !currentWbKgs.Values.Any(k => k.KgNr == kgnr)) {
throw new ArgumentException($"Für KG {(kgs.TryGetValue(kgnr.Value, out var k) ? k.Name : "?")} ({kgnr:00000}) ist noch keine Großlage festgelegt!\n(Stammdaten Herkunftshierarchie)"); throw new KeyNotFoundException($"Für KG {(kgs.TryGetValue(kgnr.Value, out var k) ? k.Name : "?")} ({kgnr:00000}) ist noch keine Großlage festgelegt!\n(Stammdaten \u2192 Herkunftshierarchie)");
} }
var createdAt = json["created_at"]?.AsValue().GetValue<string>(); var createdAt = json["created_at"]?.AsValue().GetValue<string>();
var modifiedAt = json["modified_at"]?.AsValue().GetValue<string>(); var modifiedAt = json["modified_at"]?.AsValue().GetValue<string>();
@@ -708,6 +712,8 @@ namespace Elwig.Helpers.Export {
ZwstId = json["zwstid"]?.AsValue().GetValue<string>(), ZwstId = json["zwstid"]?.AsValue().GetValue<string>(),
LfbisNr = json["lfbis_nr"]?.AsValue().GetValue<string>(), LfbisNr = json["lfbis_nr"]?.AsValue().GetValue<string>(),
UstIdNr = json["ustid_nr"]?.AsValue().GetValue<string>(), UstIdNr = json["ustid_nr"]?.AsValue().GetValue<string>(),
OrganicOperatorId = json["ooc_id"]?.AsValue().GetValue<string>(),
OrganicAuthorityCode = json["org_auth_code"]?.AsValue().GetValue<string>(),
IsJuridicalPerson = json["juridical_pers"]?.AsValue().GetValue<bool>() ?? false, IsJuridicalPerson = json["juridical_pers"]?.AsValue().GetValue<bool>() ?? false,
IsVollLieferant = json["volllieferant"]?.AsValue().GetValue<bool>() ?? false, IsVollLieferant = json["volllieferant"]?.AsValue().GetValue<bool>() ?? false,
IsBuchführend = json["buchführend"]?.AsValue().GetValue<bool>() ?? false, IsBuchführend = json["buchführend"]?.AsValue().GetValue<bool>() ?? false,
@@ -760,7 +766,8 @@ namespace Elwig.Helpers.Export {
["reason"] = h.Reason, ["reason"] = h.Reason,
["source"] = h.Source, ["source"] = h.Source,
["shares"] = h.Shares, ["shares"] = h.Shares,
["signed"] = h.MemberHasSigned, ["signed_company"] = h.CompanyHasSigned,
["signed_member"] = h.MemberHasSigned,
["value_per_share"] = h.ValuePerShare, ["value_per_share"] = h.ValuePerShare,
["currency"] = h.CurrencyCode, ["currency"] = h.CurrencyCode,
["deduct_year"] = h.DeductYear, ["deduct_year"] = h.DeductYear,
@@ -780,7 +787,8 @@ namespace Elwig.Helpers.Export {
Reason = json["reason"]!.AsValue().GetValue<string>(), Reason = json["reason"]!.AsValue().GetValue<string>(),
Source = json["source"]!.AsValue().GetValue<string>(), Source = json["source"]!.AsValue().GetValue<string>(),
Shares = json["shares"]!.AsValue().GetValue<int>(), Shares = json["shares"]!.AsValue().GetValue<int>(),
MemberHasSigned = json["signed"]?.AsValue().GetValue<bool>() ?? false, CompanyHasSigned = json["signed_company"]?.AsValue().GetValue<bool>() ?? false,
MemberHasSigned = json["signed_member"]?.AsValue().GetValue<bool>() ?? false,
ValuePerShare = json["value_per_share"]?.AsValue().GetValue<decimal>(), ValuePerShare = json["value_per_share"]?.AsValue().GetValue<decimal>(),
CurrencyCode = json["currency"]?.AsValue().GetValue<string>(), CurrencyCode = json["currency"]?.AsValue().GetValue<string>(),
DeductYear = json["deduct_year"]?.AsValue().GetValue<int>(), DeductYear = json["deduct_year"]?.AsValue().GetValue<int>(),
@@ -815,10 +823,13 @@ namespace Elwig.Helpers.Export {
}; };
} }
public static (AreaComContract, List<(AreaCom, (DateTime CreatedAt, DateTime ModifiedAt)?)>, WbRd?, (DateTime CreatedAt, DateTime ModifiedAt)?) ToAreaComContract(this JsonNode json, Dictionary<int, List<WbRd>> riede) { public static (AreaComContract, List<(AreaCom, (DateTime CreatedAt, DateTime ModifiedAt)?)>, WbRd?, (DateTime CreatedAt, DateTime ModifiedAt)?) ToAreaComContract(this JsonNode json, Dictionary<int, AT_Kg> kgs, Dictionary<int, WbKg> currentWbKgs, Dictionary<int, List<WbRd>> riede) {
var kgnr = json["kgnr"]!.AsValue().GetValue<int>(); var kgnr = json["kgnr"]!.AsValue().GetValue<int>();
var ried = json["ried"]?.AsValue().GetValue<string>(); var ried = json["ried"]?.AsValue().GetValue<string>();
var fbnr = json["fbnr"]!.AsValue()!.GetValue<int>(); var fbnr = json["fbnr"]!.AsValue()!.GetValue<int>();
if (!currentWbKgs.Values.Any(k => k.KgNr == kgnr)) {
throw new KeyNotFoundException($"Für KG {(kgs.TryGetValue(kgnr, out var k) ? k.Name : "?")} ({kgnr:00000}) ist noch keine Großlage festgelegt!\n(Stammdaten \u2192 Herkunftshierarchie)");
}
WbRd? rd = null; WbRd? rd = null;
bool newRd = false; bool newRd = false;
if (ried != null) { if (ried != null) {
@@ -866,9 +877,12 @@ namespace Elwig.Helpers.Export {
DateTime.ParseExact(modifiedAt, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None))); DateTime.ParseExact(modifiedAt, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None)));
} }
public static (AreaComContract, AreaCom, WbRd?, (DateTime CreatedAt, DateTime ModifiedAt)?) ToAreaCom(this JsonNode json, Dictionary<int, List<WbRd>> riede) { public static (AreaComContract, AreaCom, WbRd?, (DateTime CreatedAt, DateTime ModifiedAt)?) ToAreaCom(this JsonNode json, Dictionary<int, AT_Kg> kgs, Dictionary<int, WbKg> currentWbKgs, Dictionary<int, List<WbRd>> riede) {
var kgnr = json["kgnr"]!.AsValue().GetValue<int>(); var kgnr = json["kgnr"]!.AsValue().GetValue<int>();
var ried = json["ried"]?.AsValue().GetValue<string>(); var ried = json["ried"]?.AsValue().GetValue<string>();
if (!currentWbKgs.Values.Any(k => k.KgNr == kgnr)) {
throw new KeyNotFoundException($"Für KG {(kgs.TryGetValue(kgnr, out var k) ? k.Name : "?")} ({kgnr:00000}) ist noch keine Großlage festgelegt!\n(Stammdaten \u2192 Herkunftshierarchie)");
}
WbRd? rd = null; WbRd? rd = null;
bool newRd = false; bool newRd = false;
if (ried != null) { if (ried != null) {
@@ -951,13 +965,13 @@ namespace Elwig.Helpers.Export {
if (p.Unloading != null) obj["unloading"] = p.Unloading; if (p.Unloading != null) obj["unloading"] = p.Unloading;
if (p.Temperature != null) obj["temperature"] = p.Temperature; if (p.Temperature != null) obj["temperature"] = p.Temperature;
if (p.Acid != null) obj["acid"] = p.Acid; 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.WeighingData != null) obj["weighing_data"] = JsonNode.Parse(p.WeighingData);
if (p.WeighingReason != null) obj["weighing_reason"] = p.WeighingReason; if (p.WeighingReason != null) obj["weighing_reason"] = p.WeighingReason;
return obj; return obj;
} }
public static (Delivery, List<(DeliveryPart, (DateTime CreatedAt, DateTime ModifiedAt)?)>, List<DeliveryPartModifier>, List<WbRd>, (DateTime CreatedAt, DateTime ModifiedAt)?) ToDelivery(this JsonNode json, Dictionary<string, int> currentLsNrs, Dictionary<int, int> currentDids, Dictionary<int, AT_Kg> kgs, Dictionary<int, List<WbRd>> riede) { public static (Delivery, List<(DeliveryPart, (DateTime CreatedAt, DateTime ModifiedAt)?)>, List<DeliveryPartModifier>, List<WbRd>, (DateTime CreatedAt, DateTime ModifiedAt)?) ToDelivery(this JsonNode json, Dictionary<string, int> currentLsNrs, Dictionary<int, int> currentDids, Dictionary<int, AT_Kg> kgs, Dictionary<int, WbKg> currentWbKgs, Dictionary<int, List<WbRd>> riede) {
var year = json["year"]!.AsValue().GetValue<int>(); var year = json["year"]!.AsValue().GetValue<int>();
var lsnr = json["lsnr"]!.AsValue().GetValue<string>(); var lsnr = json["lsnr"]!.AsValue().GetValue<string>();
var did = currentLsNrs.GetValueOrDefault(lsnr, -1); var did = currentLsNrs.GetValueOrDefault(lsnr, -1);
@@ -983,6 +997,9 @@ namespace Elwig.Helpers.Export {
}, [.. json["parts"]!.AsArray().Select(p => p!.AsObject()).Select<JsonObject, (DeliveryPart, (DateTime, DateTime)?)>(p => { }, [.. json["parts"]!.AsArray().Select(p => p!.AsObject()).Select<JsonObject, (DeliveryPart, (DateTime, DateTime)?)>(p => {
var kgnr = p["kgnr"]?.AsValue().GetValue<int>(); var kgnr = p["kgnr"]?.AsValue().GetValue<int>();
var ried = p["ried"]?.AsValue().GetValue<string>(); var ried = p["ried"]?.AsValue().GetValue<string>();
if (kgnr != null && !currentWbKgs.Values.Any(k => k.KgNr == kgnr)) {
throw new KeyNotFoundException($"Für KG {(kgs.TryGetValue(kgnr.Value, out var k) ? k.Name : "?")} ({kgnr:00000}) ist noch keine Großlage festgelegt!\n(Stammdaten \u2192 Herkunftshierarchie)");
}
WbRd? rd = null; WbRd? rd = null;
if (ried != null && kgnr != null) { if (ried != null && kgnr != null) {
var rde = riede.GetValueOrDefault(kgnr.Value, []); var rde = riede.GetValueOrDefault(kgnr.Value, []);
@@ -1022,7 +1039,7 @@ namespace Elwig.Helpers.Export {
Unloading = p["unloading"]?.AsValue().GetValue<string>() ?? ((p["lesewagen"]?.AsValue().GetValue<bool>() ?? false) ? DeliveryPart.Pumped : null), Unloading = p["unloading"]?.AsValue().GetValue<string>() ?? ((p["lesewagen"]?.AsValue().GetValue<bool>() ?? false) ? DeliveryPart.Pumped : null),
Temperature = p["temperature"]?.AsValue().GetValue<double>(), Temperature = p["temperature"]?.AsValue().GetValue<double>(),
Acid = p["acid"]?.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), WeighingData = p["weighing_data"]?.AsObject().ToJsonString(Utils.JsonOpts),
WeighingReason = p["weighing_reason"]?.AsValue().GetValue<string>(), WeighingReason = p["weighing_reason"]?.AsValue().GetValue<string>(),
}, createdAt == null || modifiedAt == null ? null : }, createdAt == null || modifiedAt == null ? null :
+46 -4
View File
@@ -1,6 +1,7 @@
using Microsoft.Data.Sqlite; using Microsoft.Data.Sqlite;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.IO.Hashing; using System.IO.Hashing;
@@ -12,6 +13,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media;
namespace Elwig.Helpers { namespace Elwig.Helpers {
public static partial class Extensions { public static partial class Extensions {
@@ -134,11 +136,39 @@ namespace Elwig.Helpers {
return await cmd.ExecuteScalarAsync(); return await cmd.ExecuteScalarAsync();
} }
public static async Task<(string Table, long RowId, string Parent, long FkId)[]> ForeignKeyCheck(this SqliteConnection cnx) { public static async Task QuickCheck(this SqliteConnection cnx) {
using var cmd = cnx.CreateCommand();
cmd.CommandText = "PRAGMA quick_check";
using var reader = await cmd.ExecuteReaderAsync();
var list = new List<string>();
while (await reader.ReadAsync()) {
list.Add(reader.GetString(0));
}
if (list.Count != 1 || list[0] != "ok") {
throw new FileFormatException($"Integrity problems ({list.Count}):\n" + string.Join("\n", list.Take(50)));
}
}
public static async Task IntegrityCheck(this SqliteConnection cnx) {
using var cmd = cnx.CreateCommand();
cmd.CommandText = "PRAGMA integrity_check";
using var reader = await cmd.ExecuteReaderAsync();
var list = new List<string>();
while (await reader.ReadAsync()) {
list.Add(reader.GetString(0));
}
if (list.Count != 1 || list[0] != "ok") {
throw new FileFormatException($"Integrity problems ({list.Count}):\n" + string.Join("\n", list.Take(50)));
}
}
public static async Task ForeignKeyCheck(this SqliteConnection cnx) {
using var cmd = cnx.CreateCommand(); using var cmd = cnx.CreateCommand();
cmd.CommandText = "PRAGMA foreign_key_check"; cmd.CommandText = "PRAGMA foreign_key_check";
using var reader = await cmd.ExecuteReaderAsync(); using var reader = await cmd.ExecuteReaderAsync();
var list = new List<(string, long, string, long)>(); var list = new List<(string Table, long RowId, string Parent, long FkId)>();
while (await reader.ReadAsync()) { while (await reader.ReadAsync()) {
var table = reader.GetString(0); var table = reader.GetString(0);
var rowid = reader.GetInt64(1); var rowid = reader.GetInt64(1);
@@ -146,7 +176,11 @@ namespace Elwig.Helpers {
var fkid = reader.GetInt64(3); var fkid = reader.GetInt64(3);
list.Add((table, rowid, parent, fkid)); list.Add((table, rowid, parent, fkid));
} }
return [.. list];
if (list.Count > 0) {
throw new InvalidDataException($"Foreign key violations ({list.Count}):\n" + string.Join("\n", list.Take(50)
.Select(v => $"{v.Table} - {v.RowId} - {v.Parent} - {v.FkId}")));
}
} }
public static IEnumerable<T> Join<T>(this IEnumerable<T> src, Func<T> separatorFactory) { public static IEnumerable<T> Join<T>(this IEnumerable<T> src, Func<T> separatorFactory) {
@@ -159,17 +193,25 @@ namespace Elwig.Helpers {
} }
} }
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) { 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, Brush? color = null) {
var tb = new TextBlock() { var tb = new TextBlock() {
Text = text, Text = text,
TextAlignment = alignRight ? TextAlignment.Right : alignCenter ? TextAlignment.Center : TextAlignment.Left, TextAlignment = alignRight ? TextAlignment.Right : alignCenter ? TextAlignment.Center : TextAlignment.Left,
Margin = new(0, 12 * row, 0, 0), Margin = new(0, 12 * row, 0, 0),
FontWeight = bold ? FontWeights.Bold : FontWeights.Normal, FontWeight = bold ? FontWeights.Bold : FontWeights.Normal,
}; };
if (color != null) tb.Foreground = color;
tb.SetValue(Grid.ColumnProperty, col); tb.SetValue(Grid.ColumnProperty, col);
tb.SetValue(Grid.ColumnSpanProperty, colSpan); tb.SetValue(Grid.ColumnSpanProperty, colSpan);
grid.Children.Add(tb); grid.Children.Add(tb);
} }
public static (int Year, int Week) GetYearAndWeek(this DateTime date) {
return (ISOWeek.GetYear(date), ISOWeek.GetWeekOfYear(date));
}
public static (int Year, int Tertial) GetYearAndTertial(this DateTime date) {
return (date.Month <= 7 ? date.Year - 1 : date.Year, date.Month == 12 || date.Month <= 3 ? 2 : date.Month <= 7 ? 3 : 1);
}
} }
} }
+5 -20
View File
@@ -1,21 +1,17 @@
using Elwig.Services; using Elwig.Services;
using Elwig.Windows; using Elwig.Windows;
using System; using System;
using System.Drawing.Printing;
using System.Threading.Tasks;
namespace Elwig.Helpers.Printing { namespace Elwig.Helpers.Printing {
public static class Pdf { public static class Pdf {
public static Task Init(Action? evtHandler = null) { public static void Init(Action? evtHandler = null) {
PdfiumNative.FPDF_InitLibrary(); PdfiumNative.FPDF_InitLibrary();
evtHandler?.Invoke(); evtHandler?.Invoke();
return Task.CompletedTask;
} }
public static Task Cleanup() { public static void Cleanup() {
PdfiumNative.FPDF_DestroyLibrary(); PdfiumNative.FPDF_DestroyLibrary();
return Task.CompletedTask;
} }
public static void Show(TempFile file, string title) { public static void Show(TempFile file, string title) {
@@ -32,24 +28,13 @@ namespace Elwig.Helpers.Printing {
}); });
} }
public static async Task Print(string path, int copies = 1, bool doublePaged = false) { public static void 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) {
try { try {
using var printDoc = new PdfPrintDocument(path) { var printer = new PdfPrinter();
PrinterSettings = settings, printer.Print(path, copies, doublePaged);
};
printDoc.Print();
} catch (Exception exc) { } catch (Exception exc) {
InteractionService.ShowException("Fehler beim Drucken", "Beim Drucken ist ein Fehler aufgetreten", 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);
}
}
}
}
+16 -6
View File
@@ -45,7 +45,6 @@ namespace Elwig.Helpers {
public static int CurrentNextSeason => DateTime.Now.Year - (DateTime.Now.Month <= 3 ? 1 : 0); public static int CurrentNextSeason => DateTime.Now.Year - (DateTime.Now.Month <= 3 ? 1 : 0);
public static int CurrentLastSeason => DateTime.Now.Year - (DateTime.Now.Month <= 6 ? 1 : 0); public static int CurrentLastSeason => DateTime.Now.Year - (DateTime.Now.Month <= 6 ? 1 : 0);
public static int FollowingSeason => DateTime.Now.Year + (DateTime.Now.Month >= 11 ? 1 : 0); public static int FollowingSeason => DateTime.Now.Year + (DateTime.Now.Month >= 11 ? 1 : 0);
public static DateTime Today => (DateTime.Now.Hour >= 3) ? DateTime.Today : DateTime.Today.AddDays(-1);
[GeneratedRegex("^serial://([A-Za-z0-9]+):([0-9]+)(,([5-9]),([NOEMSnoems]),(0|1|1\\.5|2|))?$", RegexOptions.Compiled)] [GeneratedRegex("^serial://([A-Za-z0-9]+):([0-9]+)(,([5-9]),([NOEMSnoems]),(0|1|1\\.5|2|))?$", RegexOptions.Compiled)]
private static partial Regex GeneratedSerialRegex(); private static partial Regex GeneratedSerialRegex();
@@ -304,6 +303,11 @@ namespace Elwig.Helpers {
return d.ShowDialog() == true ? (d.Weight, d.Reason) : null; return d.ShowDialog() == true ? (d.Weight, d.Reason) : null;
} }
public static int? ShowTareCorrectionDialog(int? actualTare, int? targetTare = null) {
var d = new TareCorrectionDialog(actualTare, targetTare);
return d.ShowDialog() == true ? d.TargetTare - d.ActualTare : null;
}
public static (string?, int[])? ShowDeliverySplittingDialog(Delivery delivery) { public static (string?, int[])? ShowDeliverySplittingDialog(Delivery delivery) {
var d = new DeliverySplittingDialog(delivery); var d = new DeliverySplittingDialog(delivery);
return d.ShowDialog() == true ? (d.MgNr?.ToString() ?? d.LsNr, d.Weights ?? []) : null; return d.ShowDialog() == true ? (d.MgNr?.ToString() ?? d.LsNr, d.Weights ?? []) : null;
@@ -516,7 +520,7 @@ namespace Elwig.Helpers {
public static async Task<SmtpClient?> GetSmtpClient() { public static async Task<SmtpClient?> GetSmtpClient() {
if (App.Config.Smtp == null) if (App.Config.Smtp == null)
return 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(); var client = new SmtpClient();
await client.ConnectAsync(host, port, mode == "starttls" ? SecureSocketOptions.StartTls : SecureSocketOptions.None); await client.ConnectAsync(host, port, mode == "starttls" ? SecureSocketOptions.StartTls : SecureSocketOptions.None);
await client.AuthenticateAsync(username, password); await client.AuthenticateAsync(username, password);
@@ -536,6 +540,7 @@ namespace Elwig.Helpers {
msg.From.Add(new MailboxAddress(App.Client.NameFull, App.Config.Smtp.Value.From)); msg.From.Add(new MailboxAddress(App.Client.NameFull, App.Config.Smtp.Value.From));
msg.To.AddRange(member.EmailAddresses.OrderBy(a => a.Nr).Select(a => new MailboxAddress(member.AdministrativeName, a.Address))); msg.To.AddRange(member.EmailAddresses.OrderBy(a => a.Nr).Select(a => new MailboxAddress(member.AdministrativeName, a.Address)));
msg.Subject = subject; msg.Subject = subject;
msg.Date = DateTime.Now;
var body = new Multipart("mixed") { var body = new Multipart("mixed") {
new TextPart("plain") { Text = text } new TextPart("plain") { Text = text }
}; };
@@ -546,10 +551,10 @@ namespace Elwig.Helpers {
msg.Body = body; msg.Body = body;
await client!.SendAsync(msg); await client!.SendAsync(msg);
await AddSentMails([( await AddSentMails([(
"email", member.MgNr, member.AdministrativeName, "email", member.MgNr, member.AdministrativeName,
member.EmailAddresses.OrderBy(a => a.Nr).Select(a => a.Address).ToArray(), member.EmailAddresses.OrderBy(a => a.Nr).Select(a => a.Address).ToArray(),
subject, docs.Select(d => d.Title).ToArray() subject, docs.Select(d => d.Title).ToArray()
)]); )]);
} catch (Exception exc) { } catch (Exception exc) {
InteractionService.ShowException(exc); InteractionService.ShowException(exc);
return false; return false;
@@ -712,5 +717,10 @@ namespace Elwig.Helpers {
var filename = Path.Combine(App.MailsPath, $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{NormalizeFileName(subject)}.txt"); var filename = Path.Combine(App.MailsPath, $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{NormalizeFileName(subject)}.txt");
await File.WriteAllTextAsync(filename, $"# {subject}\r\n# Vorgesehene Empfänger: {recipients}\r\n\r\n" + body, Utils.UTF8); await File.WriteAllTextAsync(filename, $"# {subject}\r\n# Vorgesehene Empfänger: {recipients}\r\n\r\n" + body, Utils.UTF8);
} }
public static string GetTitle(string window, IEnumerable<string> filterNames, IEnumerable<string> filter) {
var f = string.Join(" / ", filterNames.Union(filter.Select(f => $"\"{f}\"")));
return window + (f.Length == 0 ? "" : ": " + f) + " - Elwig";
}
} }
} }
+46 -7
View File
@@ -623,6 +623,37 @@ namespace Elwig.Helpers {
return new(true, null); return new(true, null);
} }
public static ValidationResult CheckOrganicOperatorId(TextBox input, bool required) {
string text = "";
int pos = input.CaretIndex;
for (int i = 0, v = 0; i < input.Text.Length; i++) {
char ch = input.Text[i];
if (char.IsAsciiDigit(ch)) {
if (v == 3 && text[^1] != '-')
text += "-";
v++;
text += ch;
}
if (i == input.CaretIndex - 1) {
pos = text.Length;
} else if (v >= 10) {
break;
}
}
input.Text = text;
input.CaretIndex = pos;
if (text.Length == 0) {
return required ? new(false, "OOC-ID ist nicht optional") : new(true, null);
} else if (!text.StartsWith("040-") || text.Length != 11) {
return new(false, "OOC-ID ist ungültig");
}
return new(true, null);
}
public static ValidationResult CheckOrganicAuthorityCode(TextBox input, bool required) { public static ValidationResult CheckOrganicAuthorityCode(TextBox input, bool required) {
string text = ""; string text = "";
int pos = input.CaretIndex; int pos = input.CaretIndex;
@@ -631,15 +662,18 @@ namespace Elwig.Helpers {
if (v < 2 && char.IsAsciiLetter(ch)) { if (v < 2 && char.IsAsciiLetter(ch)) {
v++; v++;
text += ch; text += ch;
} else if ((v == 2 || v == 6) && ch == '-') { } else if ((v == 2 || v == 5) && ch == '-' && text[^1] != '-') {
v++;
text += ch; text += ch;
} else if (v >= 2 && char.IsLetterOrDigit(ch)) { } else if (v >= 2 && v <= 4 && char.IsLetter(ch)) {
if (v == 2 && text[^1] != '-')
text += "-";
if (text.StartsWith("AT")) { if (text.StartsWith("AT")) {
if (v == 3 && ch == 'B' || v == 4 && ch == 'I' || v == 5 && ch == 'O') { if (v == 2 && ch == 'B' || v == 3 && ch == 'I' || v == 4 && ch == 'O') {
v++; v++;
text += ch; text += ch;
} else if (v > 6 && char.IsAsciiDigit(ch)) { }
} else if (text.StartsWith("DE")) {
if (v == 2 && ch == 'Ö' || v == 3 && ch == 'K' || v == 4 && ch == 'O') {
v++; v++;
text += ch; text += ch;
} }
@@ -647,11 +681,16 @@ namespace Elwig.Helpers {
v++; v++;
text += ch; text += ch;
} }
} else if (v >= 5 && v <= 7 && char.IsAsciiDigit(ch)) {
if (v == 5 && text[^1] != '-')
text += "-";
v++;
text += ch;
} }
if (i == input.CaretIndex - 1) { if (i == input.CaretIndex - 1) {
pos = text.Length; pos = text.Length;
} else if (text.StartsWith("AT") && v >= 10) { } else if ((text.StartsWith("AT") || text.StartsWith("DE")) && v >= 10) {
break; break;
} }
} }
@@ -662,7 +701,7 @@ namespace Elwig.Helpers {
if (text.Length == 0) if (text.Length == 0)
return required ? new(false, "Bio-Kontrollstellen-Code ist nicht optional") : new(true, null); return required ? new(false, "Bio-Kontrollstellen-Code ist nicht optional") : new(true, null);
if (text.StartsWith("AT")) { if (text.StartsWith("AT") || text.StartsWith("DE")) {
if (text.Length != 10) { if (text.Length != 10) {
return new(false, "Bio-Kontrollstellen-Code ist ungültig"); return new(false, "Bio-Kontrollstellen-Code ist ungültig");
} }
+2 -5
View File
@@ -9,9 +9,8 @@ namespace Elwig.Helpers.Weighing {
public class AveryEventScale : Scale, IEventScale, IDisposable { public class AveryEventScale : Scale, IEventScale, IDisposable {
public string Manufacturer => "Avery"; public string Manufacturer => "Avery";
public int InternalScaleNr => 1;
public string Model { get; private set; } 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 IsReady { get; private set; }
public bool HasFillingClearance { get; private set; } public bool HasFillingClearance { get; private set; }
@@ -19,13 +18,11 @@ namespace Elwig.Helpers.Weighing {
private bool IsRunning = true; private bool IsRunning = true;
private readonly Thread BackgroundThread; private readonly Thread BackgroundThread;
private readonly string Connection;
public AveryEventScale(string id, string model, string cnx, string? log = null, bool required = true) : public AveryEventScale(string id, string model, string cnx, string? log = null, bool required = true) :
base(cnx, null, null, null, log, true, !required) { base(cnx, null, null, null, log, true, !required) {
ScaleId = id; ScaleTerminalId = id;
Model = model; Model = model;
Connection = cnx;
IsReady = true; IsReady = true;
HasFillingClearance = false; HasFillingClearance = false;
Stream.WriteTimeout = -1; Stream.WriteTimeout = -1;
@@ -5,18 +5,17 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Elwig.Helpers.Weighing { namespace Elwig.Helpers.Weighing {
public class GassnerScale : Scale, ICommandScale { public class GassnerEnqScale : Scale, ICommandScale {
public string Manufacturer => "Gassner"; public string Manufacturer => "Gassner";
public int InternalScaleNr => 1;
public string Model { get; private set; } 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 IsReady { get; private set; }
public bool HasFillingClearance { 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) { base(cnx, empty, filling, limit, log) {
ScaleId = id; ScaleTerminalId = id;
Model = model; Model = model;
IsReady = true; IsReady = true;
HasFillingClearance = false; HasFillingClearance = false;
@@ -24,10 +23,9 @@ namespace Elwig.Helpers.Weighing {
Stream.ReadTimeout = 11000; 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) { protected async Task SendCommand(byte[] cmd) {
byte[] cmd = [command];
await Stream.WriteAsync(cmd); await Stream.WriteAsync(cmd);
if (LogPath != null) await File.AppendAllTextAsync(LogPath, Encoding.ASCII.GetString(cmd)); if (LogPath != null) await File.AppendAllTextAsync(LogPath, Encoding.ASCII.GetString(cmd));
} }
@@ -53,15 +51,15 @@ namespace Elwig.Helpers.Weighing {
return line[1..^1]; 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; string record;
try { try {
await SendCommand(incIdentNr ? '\x05' : '?'); await SendCommand(incIdentNr ? "\x05" : "?");
record = await ReceiveResponse(); record = await ReceiveResponse();
} catch (IOException) { } catch (IOException) {
if (!retry || Tcp == null) throw; if (!retry || Tcp == null) throw;
ReconnectTcp(); ReconnectTcp();
return await Weigh(incIdentNr, false); return await Weigh(internalScaleNr, incIdentNr, false);
} }
if (record.Length != 45) if (record.Length != 45)
throw new FormatException("Invalid response from scale: Received record has invalid size"); throw new FormatException("Invalid response from scale: Received record has invalid size");
@@ -80,6 +78,7 @@ namespace Elwig.Helpers.Weighing {
GrossWeight = int.Parse(brutto), GrossWeight = int.Parse(brutto),
TareWeight = int.Parse(tara), TareWeight = int.Parse(tara),
NetWeight = int.Parse(netto), NetWeight = int.Parse(netto),
ScaleId = scaleNr,
WeighingId = identNr, WeighingId = identNr,
FullWeighingId = identNr, FullWeighingId = identNr,
Date = DateOnly.TryParseExact(date, "yyyyMMdd", out var d) ? d : null, Date = DateOnly.TryParseExact(date, "yyyyMMdd", out var d) ? d : null,
@@ -87,12 +86,12 @@ namespace Elwig.Helpers.Weighing {
}; };
} }
public async Task<WeighingResult> Weigh() { public async Task<WeighingResult> Weigh(int? scaleNr = null) {
return await Weigh(true); return await Weigh(scaleNr, true);
} }
public async Task<WeighingResult> GetCurrentWeight() { public async Task<WeighingResult> GetCurrentWeight(int? scaleNr = null) {
return await Weigh(false); return await Weigh(scaleNr, false);
} }
public async Task Empty() { public async Task Empty() {
@@ -126,8 +125,9 @@ namespace Elwig.Helpers.Weighing {
await SetFillingClearance(false); await SetFillingClearance(false);
} }
public Task SetDateAndTime(DateTime dateTime) { public async Task SetDateAndTime(DateTime dateTime) {
throw new NotImplementedException("Für Waagen vom Typ 'Gassner' ist diese Funktion noch nicht implementiert"); 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 /// Get the current weight on the scale without performing a weighing process
/// </summary> /// </summary>
/// <returns>Result of the weighing process (probably without a weighing id)</returns> /// <returns>Result of the weighing process (probably without a weighing id)</returns>
Task<WeighingResult> GetCurrentWeight(); Task<WeighingResult> GetCurrentWeight(int? scaleNr = null);
/// <summary> /// <summary>
/// Perform a weighing process /// Perform a weighing process
/// </summary> /// </summary>
/// <returns>Result of the weighing process (including a weighing id)</returns> /// <returns>Result of the weighing process (including a weighing id)</returns>
Task<WeighingResult> Weigh(); Task<WeighingResult> Weigh(int? scaleNr = null);
/// <summary> /// <summary>
/// Empty the scale container or grant clearance to do so /// Empty the scale container or grant clearance to do so
+1 -6
View File
@@ -18,12 +18,7 @@ namespace Elwig.Helpers.Weighing {
/// <summary> /// <summary>
/// Unique identificator of the scale /// Unique identificator of the scale
/// </summary> /// </summary>
string ScaleId { get; } string ScaleTerminalId { get; }
/// <summary>
/// Internal identifying number of the scale in its system
/// </summary>
int InternalScaleNr { get; }
/// <summary> /// <summary>
/// Indicates if the scale is currently processing a request or not /// 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 Manufacturer => "NONE";
public string Model => "NONE"; public string Model => "NONE";
public string ScaleId => id; public string ScaleTerminalId => id;
public int InternalScaleNr => 0;
public bool IsReady => false; public bool IsReady => false;
public bool HasFillingClearance => false; public bool HasFillingClearance => false;
public int? WeightLimit => null; public int? WeightLimit => null;
+3 -3
View File
@@ -28,10 +28,10 @@ namespace Elwig.Helpers.Weighing {
int? limit = config.Limit != null ? int.Parse(config.Limit) : null; int? limit = config.Limit != null ? int.Parse(config.Limit) : null;
if (config.Type == "SysTec-IT") { if (config.Type == "SysTec-IT") {
return new SysTecITScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log); 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); 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 { } else {
throw new ArgumentException($"Invalid scale type: \"{config.Type}\""); throw new ArgumentException($"Invalid scale type: \"{config.Type}\"");
} }
+10 -10
View File
@@ -8,15 +8,14 @@ namespace Elwig.Helpers.Weighing {
public class SysTecITScale : Scale, ICommandScale { public class SysTecITScale : Scale, ICommandScale {
public string Manufacturer => "SysTec"; public string Manufacturer => "SysTec";
public int InternalScaleNr => 1;
public string Model { get; private set; } 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 IsReady { get; private set; }
public bool HasFillingClearance { 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) : 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) { base(cnx, empty, filling, limit, log) {
ScaleId = id; ScaleTerminalId = id;
Model = model; Model = model;
IsReady = true; IsReady = true;
HasFillingClearance = false; HasFillingClearance = false;
@@ -74,15 +73,15 @@ namespace Elwig.Helpers.Weighing {
return line[1..^3]; 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; string record;
try { try {
await SendCommand(incIdentNr ? $"RN{InternalScaleNr}" : $"RM{InternalScaleNr}"); await SendCommand(incIdentNr ? $"RN{internalScalNr}" : $"RM{internalScalNr}");
record = await ReceiveResponse(); record = await ReceiveResponse();
} catch (IOException) { } catch (IOException) {
if (!retry || Tcp == null) throw; if (!retry || Tcp == null) throw;
ReconnectTcp(); ReconnectTcp();
return await Weigh(incIdentNr, false); return await Weigh(internalScalNr, incIdentNr, false);
} }
if (record.Length != 62) if (record.Length != 62)
throw new FormatException("Invalid response from scale: Received record has invalid size"); throw new FormatException("Invalid response from scale: Received record has invalid size");
@@ -114,6 +113,7 @@ namespace Elwig.Helpers.Weighing {
GrossWeight = int.Parse(brutto), GrossWeight = int.Parse(brutto),
TareWeight = int.Parse(tara), TareWeight = int.Parse(tara),
NetWeight = int.Parse(netto), NetWeight = int.Parse(netto),
ScaleId = scaleNr,
WeighingId = identNr, WeighingId = identNr,
FullWeighingId = identNr != null ? $"{parsedDate:yyyy-MM-dd}/{identNr}" : null, FullWeighingId = identNr != null ? $"{parsedDate:yyyy-MM-dd}/{identNr}" : null,
Date = parsedDate, Date = parsedDate,
@@ -121,12 +121,12 @@ namespace Elwig.Helpers.Weighing {
}; };
} }
public async Task<WeighingResult> Weigh() { public async Task<WeighingResult> Weigh(int? scaleNr = 1) {
return await Weigh(true); return await Weigh(scaleNr ?? 1, true);
} }
public async Task<WeighingResult> GetCurrentWeight() { public async Task<WeighingResult> GetCurrentWeight(int? scaleNr = 1) {
return await Weigh(false); return await Weigh(scaleNr ?? 1, false);
} }
public async Task Empty() { public async Task Empty() {
+7 -1
View File
@@ -21,6 +21,11 @@ namespace Elwig.Helpers.Weighing {
/// </summary> /// </summary>
public int? NetWeight; public int? NetWeight;
/// <summary>
/// Reported id of the scale
/// </summary>
public string? ScaleId;
/// <summary> /// <summary>
/// Weighing id (or IdentNr) provided by the scale /// Weighing id (or IdentNr) provided by the scale
/// </summary> /// </summary>
@@ -44,11 +49,12 @@ namespace Elwig.Helpers.Weighing {
/// <returns>&lt;[GrossWeight-TaraWeight=]NetWeight/WeighingId/Date/Time&gt;</returns> /// <returns>&lt;[GrossWeight-TaraWeight=]NetWeight/WeighingId/Date/Time&gt;</returns>
public override readonly string ToString() { public override readonly string ToString() {
var w = NetWeight != null ? (GrossWeight != null && TareWeight != null ? $"{GrossWeight}-{TareWeight}=" : "") + $"{NetWeight}kg" : ""; 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() { public readonly JsonObject ToJson() {
var obj = new JsonObject(); var obj = new JsonObject();
if (ScaleId != null) obj["scale"] = ScaleId;
if (FullWeighingId != null) obj["id"] = FullWeighingId; if (FullWeighingId != null) obj["id"] = FullWeighingId;
if (WeighingId != null) obj["nr"] = int.Parse(WeighingId); if (WeighingId != null) obj["nr"] = int.Parse(WeighingId);
if (Date != null) obj["date"] = $"{Date:yyyy-MM-dd}"; if (Date != null) obj["date"] = $"{Date:yyyy-MM-dd}";
@@ -0,0 +1,105 @@
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
namespace Elwig.Models.Dtos {
class MemberAreaComTotalsData : DataTable<MemberAreaComTotalsRow> {
private static readonly (string, string, string?, int)[] FieldNames = [
("MgNr", "MgNr.", null, 12),
("Name1", "Name", null, 40),
("Name2", "Vorname", null, 40),
("Address", "Adresse", null, 60),
("Plz", "PLZ", null, 10),
("Locality", "Ort", null, 60),
("SortIds", "Sorte", null, 12),
("AttrIds", "Attribut", null, 16),
("Areas", "Fläche", "m²", 22),
("DeliveryObligations", "Lieferpflicht", "kg", 22),
("DeliveryRights", "Lieferrecht", "kg", 22),
];
public MemberAreaComTotalsData(IEnumerable<MemberAreaComTotalsRow> rows, int year) :
base($"Gebundene Fläche", $"Gebundene Fläche pro Mitglied {year}", rows, FieldNames) {
}
public static async Task<MemberAreaComTotalsData> ForSeason(DbSet<MemberAreaComTotalsRowSingle> table, int year) {
return new MemberAreaComTotalsData(
(await FromDbSet(table, year)).GroupBy(
r => r.MgNr,
(k, g) => new MemberAreaComTotalsRow(g)
), year);
}
private static async Task<IEnumerable<MemberAreaComTotalsRowSingle>> FromDbSet(DbSet<MemberAreaComTotalsRowSingle> table, int year) {
return 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,
c.bucket, c.area, c.min_kg, c.max_kg
FROM v_area_commitment_bucket_strict c
LEFT JOIN member m ON m.mgnr = c.mgnr
LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
LEFT JOIN AT_ort o ON o.okz = p.okz
WHERE c.year = {year}
ORDER BY m.mgnr, c.bucket
""").ToListAsync();
}
}
public class MemberAreaComTotalsRow {
public int MgNr;
public string Name1;
public string? Name2;
public string Address;
public int Plz;
public string Locality;
public string[] SortIds;
public string[] AttrIds;
public int[] Areas;
public int[] DeliveryObligations;
public int[] DeliveryRights;
public MemberAreaComTotalsRow(IEnumerable<MemberAreaComTotalsRowSingle> rows) {
var f = rows.First();
MgNr = f.MgNr;
Name1 = f.Name1;
Name2 = f.Name2;
Address = f.Address;
Plz = f.Plz;
Locality = f.Locality.Split(",")[0];
SortIds = [.. rows.Select(r => r.VtrgId[..2])];
AttrIds = [.. rows.Select(r => r.VtrgId[2..])];
Areas = [.. rows.Select(r => r.Area)];
DeliveryObligations = [.. rows.Select(r => r.MinKg)];
DeliveryRights = [.. rows.Select(r => r.MaxKg)];
}
}
[Keyless]
public class MemberAreaComTotalsRowSingle {
[Column("mgnr")]
public int MgNr { get; set; }
[Column("name_1")]
public required string Name1 { get; set; }
[Column("name_2")]
public string? Name2 { get; set; }
[Column("address")]
public required string Address { get; set; }
[Column("plz")]
public int Plz { get; set; }
[Column("ort")]
public required string Locality { get; set; }
[Column("bucket")]
public required string VtrgId { get; set; }
[Column("area")]
public int Area { get; set; }
[Column("min_kg")]
public int MinKg { get; set; }
[Column("max_kg")]
public int MaxKg { get; set; }
}
}
+30 -48
View File
@@ -14,73 +14,49 @@ namespace Elwig.Models.Dtos {
("Address", "Adresse", null, 60), ("Address", "Adresse", null, 60),
("Plz", "PLZ", null, 10), ("Plz", "PLZ", null, 10),
("Locality", "Ort", null, 60), ("Locality", "Ort", null, 60),
("SortIds", "Sorte", null, 12), ("SortId", "Sorte", null, 12),
("AttrIds", "Attribut", null, 16), ("AttrId", "Attribut", null, 16),
("Areas", "Fläche", "m²", 22), ("CultId", "Bewirt.", null, 16),
("DeliveryObligations", "Lieferpflicht", "kg", 22), ("GstNr", "Parzelle(n)", null, 25),
("DeliveryRights", "Lieferrecht", "kg", 22), ("Area", "Fläche", "", 22),
("MinKg", "Lieferpflicht", "kg", 22),
("MaxKg", "Lieferrecht", "kg", 22),
]; ];
public MemberAreaComsData(IEnumerable<MemberAreaComsRow> rows, int year) : public MemberAreaComsData(IEnumerable<MemberAreaComsRow> rows, int year) :
base($"Flächenbindungen", $"Flächenbindungen pro Mitglied {year}", rows, FieldNames) { base($"Flächenbindungen", $"Flächenbindungen pro Mitglied {year}", rows, FieldNames) {
} }
public static async Task<MemberAreaComsData> ForSeason(DbSet<MemberAreaComsRowSingle> table, int year) { public static async Task<MemberAreaComsData> ForSeason(DbSet<MemberAreaComsRow> table, int year) {
return new MemberAreaComsData( return new MemberAreaComsData(await FromDbSet(table, year), year);
(await FromDbSet(table, year)).GroupBy(
r => r.MgNr,
(k, g) => new MemberAreaComsRow(g)
), year);
} }
private static async Task<IEnumerable<MemberAreaComsRowSingle>> FromDbSet(DbSet<MemberAreaComsRowSingle> table, int year) { private static async Task<IEnumerable<MemberAreaComsRow>> FromDbSet(DbSet<MemberAreaComsRow> table, int year) {
return await table.FromSql($""" return await table.FromSql($"""
SELECT m.mgnr, m.name AS name_1, SELECT m.mgnr, m.name AS name_1,
COALESCE(m.prefix || ' ', '') || m.given_name || COALESCE(m.prefix || ' ', '') || m.given_name ||
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2, COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
p.plz, o.name AS ort, m.address, p.plz, o.name AS ort, m.address,
c.bucket, c.area, c.min_kg, c.max_kg t.sortid, t.attrid, c.cultid,
FROM v_area_commitment_bucket_strict c c.gstnr, c.area,
CAST(ROUND(area * COALESCE(t.min_kg_per_ha, 0) / 10000.0, 0) AS INTEGER) AS min_kg,
CAST(ROUND(area * MIN(COALESCE(a.max_kg_per_ha, s.max_kg_per_ha), s.max_kg_per_ha) / 10000.0, 0) AS INTEGER) AS max_kg
FROM v_virtual_season s, area_commitment c
JOIN area_commitment_type t ON t.vtrgid = c.vtrgid
LEFT JOIN wine_attribute a ON a.attrid = t.attrid
LEFT JOIN member m ON m.mgnr = c.mgnr LEFT JOIN member m ON m.mgnr = c.mgnr
LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
LEFT JOIN AT_ort o ON o.okz = p.okz LEFT JOIN AT_ort o ON o.okz = p.okz
WHERE c.year = {year} WHERE s.year = {year} AND
ORDER BY m.mgnr, c.bucket (year_from IS NULL OR year_from <= s.year) AND
(year_to IS NULL OR year_to >= s.year)
ORDER BY c.mgnr, t.sortid, t.attrid, c.cultid, c.gstnr;
""").ToListAsync(); """).ToListAsync();
} }
} }
public class MemberAreaComsRow {
public int MgNr;
public string Name1;
public string? Name2;
public string Address;
public int Plz;
public string Locality;
public string[] SortIds;
public string[] AttrIds;
public int[] Areas;
public int[] DeliveryObligations;
public int[] DeliveryRights;
public MemberAreaComsRow(IEnumerable<MemberAreaComsRowSingle> rows) {
var f = rows.First();
MgNr = f.MgNr;
Name1 = f.Name1;
Name2 = f.Name2;
Address = f.Address;
Plz = f.Plz;
Locality = f.Locality.Split(",")[0];
SortIds = rows.Select(r => r.VtrgId[..2]).ToArray();
AttrIds = rows.Select(r => r.VtrgId[2..]).ToArray();
Areas = rows.Select(r => r.Area).ToArray();
DeliveryObligations = rows.Select(r => r.MinKg).ToArray();
DeliveryRights = rows.Select(r => r.MaxKg).ToArray();
}
}
[Keyless] [Keyless]
public class MemberAreaComsRowSingle { public class MemberAreaComsRow {
[Column("mgnr")] [Column("mgnr")]
public int MgNr { get; set; } public int MgNr { get; set; }
[Column("name_1")] [Column("name_1")]
@@ -93,8 +69,14 @@ namespace Elwig.Models.Dtos {
public int Plz { get; set; } public int Plz { get; set; }
[Column("ort")] [Column("ort")]
public required string Locality { get; set; } public required string Locality { get; set; }
[Column("bucket")] [Column("sortid")]
public required string VtrgId { get; set; } public required string SortId { get; set; }
[Column("attrid")]
public string? AttrId { get; set; }
[Column("cultid")]
public string? CultId { get; set; }
[Column("gstnr")]
public required string GstNr { get; set; }
[Column("area")] [Column("area")]
public int Area { get; set; } public int Area { get; set; }
[Column("min_kg")] [Column("min_kg")]
+6 -4
View File
@@ -118,26 +118,28 @@ namespace Elwig.Models.Entities {
[Column("acid")] [Column("acid")]
public double? Acid { get; set; } public double? Acid { get; set; }
[Column("scale_id")] [Column("scale_terminal")]
public string? ScaleId { get; set; } public string? ScaleTerminalId { get; set; }
[Column("weighing_data")] [Column("weighing_data")]
public string? WeighingData { get; set; } public string? WeighingData { get; set; }
[NotMapped] [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? TareCorrection, int? Net, DateOnly? Date, TimeOnly? Time) WeighingInfo {
get { get {
try { try {
var obj = JsonNode.Parse(WeighingData!)!.AsObject(); var obj = JsonNode.Parse(WeighingData!)!.AsObject();
return ( return (
obj["scale"]?.AsValue().GetValue<string>(),
obj["id"]?.AsValue().GetValue<string>(), obj["id"]?.AsValue().GetValue<string>(),
obj["gross_weight"]?.AsValue().GetValue<int>(), obj["gross_weight"]?.AsValue().GetValue<int>(),
obj["tare_weight"]?.AsValue().GetValue<int>(), obj["tare_weight"]?.AsValue().GetValue<int>(),
obj["tare_correction"]?.AsValue().GetValue<int>(),
obj["net_weight"]?.AsValue().GetValue<int>(), obj["net_weight"]?.AsValue().GetValue<int>(),
DateOnly.TryParseExact(obj["date"]?.AsValue().GetValue<string>(), "yyyy-MM-dd", out var d) ? d : null, DateOnly.TryParseExact(obj["date"]?.AsValue().GetValue<string>(), "yyyy-MM-dd", out var d) ? d : null,
TimeOnly.TryParseExact(obj["time"]?.AsValue().GetValue<string>(), ["HH:mm:ss", "HH:mm"], out var t) ? t : null TimeOnly.TryParseExact(obj["time"]?.AsValue().GetValue<string>(), ["HH:mm:ss", "HH:mm"], out var t) ? t : null
); );
} catch { } catch {
return (null, null, null, null, null, null); return (null, null, null, null, null, null, null, null);
} }
} }
} }
+6
View File
@@ -96,6 +96,12 @@ namespace Elwig.Models.Entities {
[Column("ustid_nr")] [Column("ustid_nr")]
public string? UstIdNr { get; set; } public string? UstIdNr { get; set; }
[Column("ooc_id")]
public string? OrganicOperatorId { get; set; }
[Column("org_auth_code")]
public string? OrganicAuthorityCode { get; set; }
[Column("juridical_pers")] [Column("juridical_pers")]
public bool IsJuridicalPerson { get; set; } public bool IsJuridicalPerson { get; set; }
+4 -1
View File
@@ -76,7 +76,10 @@ namespace Elwig.Models.Entities {
[Column("shares")] [Column("shares")]
public int Shares { get; set; } public int Shares { get; set; }
[Column("signed")] [Column("signed_company")]
public bool CompanyHasSigned { get; set; }
[Column("signed_member")]
public bool MemberHasSigned { get; set; } public bool MemberHasSigned { get; set; }
[Column("value_per_share")] [Column("value_per_share")]
+3
View File
@@ -11,6 +11,9 @@ namespace Elwig.Models.Entities {
[Column("name")] [Column("name")]
public required string Name { get; set; } public required string Name { get; set; }
[Column("organic")]
public bool IsOrganic { get; set; }
[Column("description")] [Column("description")]
public string? Description { get; set; } public string? Description { get; set; }
+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;
+22
View File
@@ -0,0 +1,22 @@
-- schema version 41 to 42
ALTER TABLE member ADD COLUMN ooc_id TEXT CHECK (ooc_id REGEXP '^[0-9]{3}-[0-9]{7}$') DEFAULT NULL;
ALTER TABLE member ADD COLUMN org_auth_code TEXT CHECK (org_auth_code REGEXP '^[A-Z]{2}-[A-ZÖØ]{3}-[0-9]{2,3}(-[A-Z]{2})?$') DEFAULT NULL;
ALTER TABLE wine_cultivation ADD COLUMN organic INTEGER NOT NULL CHECK (organic IN (TRUE, FALSE)) DEFAULT FALSE;
ALTER TABLE member_history RENAME COLUMN signed TO signed_member;
ALTER TABLE member_history ADD COLUMN signed_company INTEGER NOT NULL CHECK (signed_company IN (TRUE, FALSE)) DEFAULT FALSE;
UPDATE member SET org_auth_code = UPPER(SUBSTR(a.name, -11, 10)) FROM member m JOIN member_billing_address a ON a.mgnr = m.mgnr WHERE a.name LIKE '%AT-BIO-___)';
UPDATE member SET org_auth_code = UPPER(SUBSTR(a.name, -10, 10)) FROM member m JOIN member_billing_address a ON a.mgnr = m.mgnr WHERE a.name LIKE '%AT-BIO-___';
UPDATE member SET org_auth_code = UPPER(suffix) WHERE suffix LIKE 'AT-BIO-___';
UPDATE wine_cultivation SET organic = TRUE WHERE name LIKE '%bio%' OR name LIKE '%org%' OR description LIKE '%AT-BIO-%';
UPDATE wine_cultivation SET description = NULL WHERE description LIKE '%AT-BIO-%';
UPDATE wine_cultivation SET description = 'Biologische Produktion' WHERE organic;
UPDATE client_parameter SET param = 'CLIENT_ORGANIC_AUTHORITYCODE' WHERE param = 'CLIENT_ORGANIC_AUTHORITY';
UPDATE member_telephone_number SET comment = NULL WHERE comment = '';
UPDATE member_email_address SET comment = NULL WHERE comment = '';
+2
View File
@@ -43,6 +43,8 @@ namespace Elwig.Services {
public static async Task<(List<string>, IQueryable<AreaComContract>, IQueryable<AreaCom>, List<string>)> GetFilters(this AreaComAdminViewModel vm, AppDbContext ctx) { public static async Task<(List<string>, IQueryable<AreaComContract>, IQueryable<AreaCom>, List<string>)> GetFilters(this AreaComAdminViewModel vm, AppDbContext ctx) {
List<string> filterNames = []; List<string> filterNames = [];
IQueryable<AreaCom> areaComQuery = ctx.AreaCommitments.Where(a => a.MgNr == vm.FilterMember.MgNr).OrderBy(a => a.FbNr); IQueryable<AreaCom> areaComQuery = ctx.AreaCommitments.Where(a => a.MgNr == vm.FilterMember.MgNr).OrderBy(a => a.FbNr);
filterNames.Add(vm.FilterMember.AdministrativeName);
if (vm.FilterSeason is int season) { if (vm.FilterSeason is int season) {
areaComQuery = Utils.ActiveAreaCommitments(areaComQuery, season); areaComQuery = Utils.ActiveAreaCommitments(areaComQuery, season);
filterNames.Add($"laufend {season}"); filterNames.Add($"laufend {season}");
+133
View File
@@ -0,0 +1,133 @@
using Elwig.Helpers;
using Elwig.Helpers.Export;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Elwig.Services {
public static class BackupService {
public static bool IsAnyBackupUpToDate {
get {
var lastDbWrite = File.GetLastWriteTime(App.Config.DatabaseFile);
lastDbWrite += new TimeSpan(0, 0, -1);
return Directory.GetFiles(App.Config.BackupPath, "????-??-??_??-??-??.*.zip")
.Any(n => DateTime.ParseExact(Path.GetFileName(n)[..19], "yyyy-MM-dd_HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.None) >= lastDbWrite);
}
}
public static async Task TryDatabaseBackup() {
if (IsAnyBackupUpToDate) return;
await BackupDatabase();
PruneBackups();
}
public static async Task BackupDatabase() {
await Database.ExportSql(Path.Combine(App.Config.BackupPath, $"{File.GetLastWriteTime(App.Config.DatabaseFile):yyyy-MM-dd_HH-mm-ss}.sql.zip"), true);
}
public static void PruneBackups() {
var retain = new HashSet<string>();
var backups = Directory.GetFiles(App.Config.BackupPath, "????-??-??_??-??-??.*.zip")
.Select(n => (FileName: n, Timestamp: DateTime.ParseExact(Path.GetFileName(n)[..19], "yyyy-MM-dd_HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.None)))
.OrderByDescending(t => t.Timestamp).ToArray();
DateTime now;
// retain hourly backups
now = DateTime.Now;
for (int i = 0; i < App.Config.BackupRetainHours; i++, now = now.AddHours(-1)) {
foreach (var b in backups) {
if (b.Timestamp.Date == now.Date && b.Timestamp.Hour == now.Hour) {
retain.Add(b.FileName);
break;
}
}
}
// retain daily backups
now = DateTime.Now;
for (int i = 0; i < App.Config.BackupRetainDays; i++, now = now.AddDays(-1)) {
foreach (var b in backups) {
if (b.Timestamp.Date == now.Date) {
retain.Add(b.FileName);
break;
}
}
}
// retain weekly backups
now = DateTime.Now;
for (int i = 0; i < App.Config.BackupRetainWeeks; i++, now = now.AddDays(-7)) {
foreach (var b in backups) {
if (b.Timestamp.GetYearAndWeek() == now.GetYearAndWeek()) {
retain.Add(b.FileName);
break;
}
}
}
// retain monthly backups
now = DateTime.Now;
for (int i = 0; i < App.Config.BackupRetainMonths; i++, now = now.AddMonths(-1)) {
foreach (var b in backups) {
if (b.Timestamp.Year == now.Year && b.Timestamp.Month == now.Month) {
retain.Add(b.FileName);
break;
}
}
}
// retain tertial backups
now = new DateTime(DateTime.Now.Year + 1, 3, 1);
for (int i = 0; i < 600; i++, now = now.AddMonths(-4)) {
foreach (var b in backups) {
if (b.Timestamp.GetYearAndTertial() == now.GetYearAndTertial()) {
retain.Add(b.FileName);
break;
}
}
}
// delete non-retained files
foreach (var b in backups) {
if (retain.Contains(b.FileName)) continue;
try {
File.Delete(b.FileName);
} catch { }
}
// delete temp files
foreach (var f in Directory.GetFiles(App.Config.BackupPath, "*.tmp")) {
try {
File.Delete(f);
} catch { }
}
}
public static async Task RestoreDatabase() {
try {
var d = new OpenFileDialog() {
Title = "Datenbank wiederherstellen - Elwig",
DefaultDirectory = App.Config.BackupPath,
InitialDirectory = App.Config.BackupPath,
DefaultExt = "sql.zip",
Filter = "SQLite-Datenbank (*.sqlite3, *.sqlite3.zip, *.sql, *.sql.zip)|*.sqlite3;*.sqlite3.zip;*.sql;*.sql.zip",
};
if (d.ShowDialog() == true) {
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) {
InteractionService.ShowException(exc);
}
Mouse.OverrideCursor = null;
}
}
}
+2 -1
View File
@@ -45,7 +45,8 @@ namespace Elwig.Services {
deliveryAncmtQuery = deliveryAncmtQuery.Where(a => a.Year == s.Year && a.DsNr == s.DsNr); deliveryAncmtQuery = deliveryAncmtQuery.Where(a => a.Year == s.Year && a.DsNr == s.DsNr);
filterNames.Add($"{s.Date:dd.MM.yyyy} {s.Branch.Name} {s.Description}"); filterNames.Add($"{s.Date:dd.MM.yyyy} {s.Branch.Name} {s.Description}");
} else { } else {
deliveryAncmtQuery = deliveryAncmtQuery.Where(a => a.Year == vm.FilterSeason && (!vm.FilterOnlyUpcoming || a.Schedule.DateString.CompareTo(Utils.Today.ToString("yyyy-MM-dd")) >= 0)); var today = DateTime.Today;
deliveryAncmtQuery = deliveryAncmtQuery.Where(a => a.Year == vm.FilterSeason && (!vm.FilterOnlyUpcoming || a.Schedule.DateString.CompareTo(today.ToString("yyyy-MM-dd")) >= 0));
filterNames.Add($"{vm.FilterSeason}"); filterNames.Add($"{vm.FilterSeason}");
} }
+3 -2
View File
@@ -49,8 +49,9 @@ namespace Elwig.Services {
filterNames.Add($"{vm.FilterSeason}"); filterNames.Add($"{vm.FilterSeason}");
} }
if (vm.FilterOnlyUpcoming) { if (vm.FilterOnlyUpcoming) {
deliveryScheduleQuery = deliveryScheduleQuery.Where(s => s.DateString.CompareTo(Utils.Today.ToString("yyyy-MM-dd")) >= 0); var today = DateTime.Today;
filterNames.Add($"ab {Utils.Today:dd.MM.yyyy}"); deliveryScheduleQuery = deliveryScheduleQuery.Where(s => s.DateString.CompareTo(today.ToString("yyyy-MM-dd")) >= 0);
filterNames.Add($"ab {today:dd.MM.yyyy}");
} }
var filterVar = new List<string>(); var filterVar = new List<string>();
+164 -66
View File
@@ -1,19 +1,21 @@
using Elwig.Documents; using Elwig.Documents;
using Elwig.Helpers.Export;
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Helpers.Export;
using Elwig.Models.Dtos; using Elwig.Models.Dtos;
using Elwig.Models.Entities; using Elwig.Models.Entities;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System;
using Elwig.ViewModels; using Elwig.ViewModels;
using iText.Commons.Internal.Runtime;
using LinqKit; using LinqKit;
using System.Globalization;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.ChangeTracking;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media;
namespace Elwig.Services { namespace Elwig.Services {
public static class DeliveryService { public static class DeliveryService {
@@ -35,8 +37,13 @@ namespace Elwig.Services {
vm.SortId = ""; vm.SortId = "";
vm.GradationKmwString = ""; vm.GradationKmwString = "";
vm.WeightString = "";
vm.Weight = null;
vm.ActualTare = null;
vm.TargetTare = null;
vm.Tare = null;
vm.IsManualWeighing = false; vm.IsManualWeighing = false;
vm.PartComment = ""; vm.PartComment = "";
vm.TemperatureString = ""; vm.TemperatureString = "";
vm.AcidString = ""; vm.AcidString = "";
@@ -51,7 +58,11 @@ namespace Elwig.Services {
vm.WineKg = ControlUtils.GetItemFromSourceWithPk(vm.WineKgSource, p.KgNr) as AT_Kg; vm.WineKg = ControlUtils.GetItemFromSourceWithPk(vm.WineKgSource, p.KgNr) as AT_Kg;
vm.WineRd = ControlUtils.GetItemFromSourceWithPk(vm.WineRdSource, p.KgNr, p.RdNr) as WbRd; vm.WineRd = ControlUtils.GetItemFromSourceWithPk(vm.WineRdSource, p.KgNr, p.RdNr) as WbRd;
vm.WineOrigin = ControlUtils.GetItemFromSourceWithPk(vm.WineOriginSource, p.HkId) as WineOrigin; vm.WineOrigin = ControlUtils.GetItemFromSourceWithPk(vm.WineOriginSource, p.HkId) as WineOrigin;
vm.WeightString = $"{p.Weight:N0}";
vm.Weight = p.Weight;
vm.ActualTare = p.WeighingInfo.Tare;
vm.TargetTare = vm.ActualTare + p.WeighingInfo.TareCorrection;
vm.Tare = vm.TargetTare ?? (vm.ActualTare is int t && t != 0 ? t : null);
vm.IsManualWeighing = p.IsManualWeighing; vm.IsManualWeighing = p.IsManualWeighing;
vm.IsNetWeight = p.IsNetWeight; vm.IsNetWeight = p.IsNetWeight;
@@ -67,11 +78,50 @@ namespace Elwig.Services {
vm.IsGebunden = p.IsGebunden; vm.IsGebunden = p.IsGebunden;
vm.Unloading = p.Unloading; vm.Unloading = p.Unloading;
vm.ScaleId = p.ScaleId; vm.ScaleTerminalId = p.ScaleTerminalId;
vm.WeighingData = p.WeighingData; vm.WeighingData = p.WeighingData;
vm.ManualWeighingReason = p.WeighingReason; vm.ManualWeighingReason = p.WeighingReason;
} }
public static (DateTime From, DateTime To)? GetFilterToday(DateTime today, IEnumerable<DateTime> timestamps) {
var filtered = timestamps.Where(t => t >= today.AddDays(-1) && t < today.AddDays(1)).Order().ToArray();
if (filtered.Length == 0) return null;
var latest = filtered.Last();
var since = filtered.Cast<DateTime>().Reverse().Aggregate(latest, (s, c) => s.Subtract(c).TotalHours <= 6 ? c : s);
latest += new TimeSpan(0, 1, -latest.Minute, -latest.Second, -latest.Millisecond, -latest.Microsecond);
since += new TimeSpan(0, 0, -since.Minute, -since.Second, -since.Millisecond, -since.Microsecond);
if (since.Subtract(today) <= new TimeSpan(-6, 0, 0) && latest.Subtract(today) < new TimeSpan(0, 0, 0))
return null;
return (since, latest);
}
private static async Task<(Expression<Func<Delivery, bool>>, string)> GetFilterToday(AppDbContext ctx) {
var today = DateTime.Today;
var timestamps = (await ctx.Deliveries
.Where(d => d.DateString.CompareTo(today.AddDays(-1).ToString("yyyy-MM-dd")) >= 0)
.OrderBy(d => d.DateString).ThenBy(d => d.TimeString)
.Select(d => d.DateString + " " + d.TimeString)
.ToListAsync())
.Select(s => DateTime.TryParseExact(s, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt) ? (DateTime?)dt : null)
.Where(t => t.HasValue)
.Cast<DateTime>()
.ToArray();
var res = GetFilterToday(today, timestamps);
if (!res.HasValue) {
return (_ => false, today.ToString("dd.MM.yyyy"));
} else {
var from = res.Value.From;
var to = res.Value.To;
return (d => (d.DateString == from.ToString("yyyy-MM-dd") && (d.TimeString == null || d.TimeString.CompareTo(from.ToString("HH:mm:ss")) >= 0)) ||
(d.DateString.CompareTo(from.ToString("yyyy-MM-dd")) > 0),
from.Date == to.Date ? from.ToString("dd.MM.yyyy") : $"{from:dd.MM.yyyy \\a\\b HH:mm} / {to:dd.MM.yyyy \\b\\i\\s HH:mm}");
}
}
public static async Task<(List<string>, IQueryable<Delivery>, IQueryable<DeliveryPart>, Predicate<DeliveryPart>, List<string>)> GetFilters(this DeliveryAdminViewModel vm, AppDbContext ctx) { public static async Task<(List<string>, IQueryable<Delivery>, IQueryable<DeliveryPart>, Predicate<DeliveryPart>, List<string>)> GetFilters(this DeliveryAdminViewModel vm, AppDbContext ctx) {
List<string> filterNames = []; List<string> filterNames = [];
IQueryable<Delivery> deliveryQuery = ctx.Deliveries; IQueryable<Delivery> deliveryQuery = ctx.Deliveries;
@@ -84,10 +134,9 @@ namespace Elwig.Services {
filterNames.Add(vm.FilterMember.AdministrativeName); filterNames.Add(vm.FilterMember.AdministrativeName);
} }
if (vm.FilterTodayOnly) { if (vm.FilterTodayOnly) {
deliveryQuery = deliveryQuery var (pred, name) = await GetFilterToday(ctx);
.Where(d => (d.DateString == Utils.Today.ToString("yyyy-MM-dd") && (d.TimeString == null || d.TimeString.CompareTo("03:00:00") > 0)) || deliveryQuery = deliveryQuery.Where(pred);
(d.DateString == Utils.Today.AddDays(1).ToString("yyyy-MM-dd") && (d.TimeString == null || d.TimeString.CompareTo("03:00:00") <= 0))); filterNames.Add(name);
filterNames.Add(Utils.Today.ToString("dd.MM.yyyy"));
} else if (!vm.FilterAllSeasons) { } else if (!vm.FilterAllSeasons) {
deliveryQuery = deliveryQuery.Where(d => d.Year == vm.FilterSeason); deliveryQuery = deliveryQuery.Where(d => d.Year == vm.FilterSeason);
filterNames.Add($"{vm.FilterSeason}"); filterNames.Add($"{vm.FilterSeason}");
@@ -515,7 +564,7 @@ namespace Elwig.Services {
Weight = vm.Weight!.Value, Weight = vm.Weight!.Value,
IsManualWeighing = vm.IsManualWeighing, IsManualWeighing = vm.IsManualWeighing,
ScaleId = vm.ScaleId, ScaleTerminalId = vm.ScaleTerminalId,
WeighingData = vm.WeighingData, WeighingData = vm.WeighingData,
WeighingReason = vm.ManualWeighingReason, WeighingReason = vm.ManualWeighingReason,
}; };
@@ -730,10 +779,9 @@ namespace Elwig.Services {
query = q; query = q;
filterNames.AddRange(f); filterNames.AddRange(f);
} else if (subject == ExportSubject.FromToday) { } else if (subject == ExportSubject.FromToday) {
var date = $"{Utils.Today:yyyy-MM-dd}"; var (pred, name) = await GetFilterToday(ctx);
query = ctx.DeliveryParts query = ctx.Deliveries.Where(pred).SelectMany(d => d.Parts);
.Where(p => p.Delivery.DateString == date); filterNames.Add(name);
filterNames.Add($"{Utils.Today:dd.MM.yyyy}");
} else if (subject == ExportSubject.FromSeasonAndBranch) { } else if (subject == ExportSubject.FromSeasonAndBranch) {
query = ctx.DeliveryParts query = ctx.DeliveryParts
.Where(p => p.Year == Utils.CurrentLastSeason && p.Delivery.ZwstId == App.ZwstId); .Where(p => p.Year == Utils.CurrentLastSeason && p.Delivery.ZwstId == App.ZwstId);
@@ -805,10 +853,9 @@ namespace Elwig.Services {
query = q; query = q;
filterNames.AddRange(f); filterNames.AddRange(f);
} else if (subject == ExportSubject.FromToday) { } else if (subject == ExportSubject.FromToday) {
var date = $"{Utils.Today:yyyy-MM-dd}"; var (pred, name) = await GetFilterToday(ctx);
query = ctx.DeliveryParts query = ctx.Deliveries.Where(pred).SelectMany(d => d.Parts);
.Where(p => p.Delivery.DateString == date); filterNames.Add(name);
filterNames.Add($"{Utils.Today:dd.MM.yyyy}");
} else { } else {
throw new ArgumentException("Invalid value for ExportSubject"); throw new ArgumentException("Invalid value for ExportSubject");
} }
@@ -930,30 +977,40 @@ namespace Elwig.Services {
} }
} }
private static void AddWeightToolTipRow(Grid grid, int row, string? h1, string? h2, int weight, int? total1, int total2) { private static void AddWeightToolTipRow(Grid grid, int row, string? h1, string? h2, string? h3, int weight, int? total1, int? total2, int total3, string? type) {
var bold = h2 == null; var bold = h3 == null;
if (h1 != null) grid.AddToolTipCell(h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold); var color = type == "R" ? Brushes.Firebrick : type == "W" ? Brushes.ForestGreen : null;
if (h2 != null) grid.AddToolTipCell(h2 + ":", row, 1, 1, bold); if (h1 != null) grid.AddToolTipCell(h1 + ":", row, 0, (h2 == null) ? (h3 == null) ? 3 : 2 : 1, bold: bold, color: color);
grid.AddToolTipCell($"{weight:N0} kg", row, 2, bold: bold, alignRight: true); if (h2 != null) grid.AddToolTipCell(h2 + ":", row, 1, (h3 == null) ? 2 : 1, bold: bold, color: color);
if (h3 != null) grid.AddToolTipCell(h3 + ":", row, 2, 1, bold: bold, color: color);
grid.AddToolTipCell($"{weight:N0} kg", row, 3, bold: bold, alignRight: true, color: color);
if (total1 != null && total1 != 0) if (total1 != null && total1 != 0)
grid.AddToolTipCell($"{weight * 100.0 / total1:N1} %", row, 3, bold: bold, alignRight: true); grid.AddToolTipCell($"{weight * 100.0 / total1:N1} %", row, 4, bold: bold, alignRight: true, color: color);
if (total2 != 0) if (total2 != null && total2 != 0)
grid.AddToolTipCell($"{weight * 100.0 / total2:N1} %", row, 4, bold: bold, alignRight: true); grid.AddToolTipCell($"{weight * 100.0 / total2:N1} %", row, 5, bold: bold, alignRight: true, color: color);
if (total3 != 0)
grid.AddToolTipCell($"{weight * 100.0 / total3:N1} %", row, 6, bold: bold, alignRight: true, color: color);
} }
private static void AddGradationToolTipRow(Grid grid, int row, string? h1, string? h2, double min, double avg, double max) { private static void AddGradationToolTipRow(Grid grid, int row, string? h1, string? h2, string? h3, double min, double avg, double max, string? type) {
var bold = h2 == null; var bold = h3 == null;
if (h1 != null) grid.AddToolTipCell(h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold); var color = type == "R" ? Brushes.Firebrick : type == "W" ? Brushes.ForestGreen : null;
if (h2 != null) grid.AddToolTipCell(h2 + ":", row, 1, bold: bold); if (h1 != null) grid.AddToolTipCell(h1 + ":", row, 0, (h2 == null) ? (h3 == null) ? 3 : 2 : 1, bold: bold, color: color);
grid.AddToolTipCell($"{min:N1}°", row, 2, bold: bold, alignRight: true); if (h2 != null) grid.AddToolTipCell(h2 + ":", row, 1, (h3 == null) ? 2 : 1, bold: bold, color: color);
grid.AddToolTipCell($"{avg:N1}°", row, 3, bold: bold, alignRight: true); if (h3 != null) grid.AddToolTipCell(h3 + ":", row, 2, bold: bold, color: color);
grid.AddToolTipCell($"{max:N1}°", row, 4, bold: bold, alignRight: true); grid.AddToolTipCell($"{min:N1}°", row, 3, bold: bold, alignRight: true, color: color);
grid.AddToolTipCell($"{avg:N1}°", row, 4, bold: bold, alignRight: true, color: color);
grid.AddToolTipCell($"{max:N1}°", row, 5, bold: bold, alignRight: true, color: color);
} }
public static async Task<(string WeightText, (string?, string?, int, int?, int)[] WeightGrid, string GradationText, (string?, string?, double, double, double)[] GradationGrid)> GenerateToolTipData(IQueryable<DeliveryPart> deliveryParts) { private static string? GetGroupName(params string?[] parts) {
var wGrid = new List<(string?, string?, int, int?, int)>(); return parts.All(p => p == null) ? null : string.Join(" / ", parts.Where(p => p != null));
}
public static async Task<(string WeightText, (string?, string?, string?, int, int?, int?, int, string?)[] WeightGrid, string GradationText, (string?, string?, string?, double, double, double, string?)[] GradationGrid)> GenerateToolTipData(IQueryable<DeliveryPart> deliveryParts) {
var wGrid = new List<(string?, string?, string?, int, int?, int?, int, string?)>();
var wText = "-"; var wText = "-";
var gGrid = new List<(string?, string?, double, double, double)>(); var gGrid = new List<(string?, string?, string?, double, double, double, string?)>();
var gText = "-"; var gText = "-";
var stat = (await deliveryParts.GroupBy(p => 0) var stat = (await deliveryParts.GroupBy(p => 0)
@@ -968,11 +1025,11 @@ namespace Elwig.Services {
.Single(); .Single();
wText = $"{stat.Weight:N0} kg"; wText = $"{stat.Weight:N0} kg";
wGrid.Add(("Menge", null, stat.Weight, null, stat.Weight)); wGrid.Add(("Menge", null, null, stat.Weight, null, null, stat.Weight, null));
if (stat.Min != null && stat.Max != null) { if (stat.Min != null && stat.Max != null) {
gText = $"{stat.Min:N1}° / {stat.Avg:N1}° / {stat.Max:N1}°"; gText = $"{stat.Min:N1}° / {stat.Avg:N1}° / {stat.Max:N1}°";
gGrid.Add(("Gradation", null, stat.Min.Value, stat.Avg, stat.Max.Value)); gGrid.Add(("Gradation", null, null, stat.Min.Value, stat.Avg, stat.Max.Value, null));
var attrGroups = await deliveryParts var attrGroups = await deliveryParts
.GroupBy(p => new { Attr = p.Attribute!.Name, Cult = p.Cultivation!.Name }) .GroupBy(p => new { Attr = p.Attribute!.Name, Cult = p.Cultivation!.Name })
@@ -989,9 +1046,10 @@ namespace Elwig.Services {
.ThenBy(g => g.Cult) .ThenBy(g => g.Cult)
.ToListAsync(); .ToListAsync();
var sortGroups = await deliveryParts var sortGroups = await deliveryParts
.GroupBy(p => p.SortId) .GroupBy(p => new { p.SortId, p.Variety.Type })
.Select(g => new { .Select(g => new {
SortId = g.Key, g.Key.SortId,
g.Key.Type,
Weight = g.Sum(p => p.Weight), Weight = g.Sum(p => p.Weight),
Min = g.Min(p => p.Kmw), Min = g.Min(p => p.Kmw),
Avg = g.Sum(p => p.Kmw * p.Weight) / g.Sum(p => p.Weight), Avg = g.Sum(p => p.Kmw * p.Weight) / g.Sum(p => p.Weight),
@@ -1004,11 +1062,13 @@ namespace Elwig.Services {
.GroupBy(p => new { .GroupBy(p => new {
Attr = p.Attribute!.Name, Attr = p.Attribute!.Name,
Cult = p.Cultivation!.Name, Cult = p.Cultivation!.Name,
p.Variety.Type,
p.SortId, p.SortId,
}) })
.Select(g => new { .Select(g => new {
g.Key.Attr, g.Key.Attr,
g.Key.Cult, g.Key.Cult,
g.Key.Type,
g.Key.SortId, g.Key.SortId,
Weight = g.Sum(p => p.Weight), Weight = g.Sum(p => p.Weight),
Min = g.Min(p => p.Kmw), Min = g.Min(p => p.Kmw),
@@ -1018,27 +1078,62 @@ namespace Elwig.Services {
.OrderByDescending(g => g.Weight) .OrderByDescending(g => g.Weight)
.ThenBy(g => g.Attr) .ThenBy(g => g.Attr)
.ThenBy(g => g.Cult) .ThenBy(g => g.Cult)
.ThenBy(g => g.Type)
.ThenBy(g => g.SortId) .ThenBy(g => g.SortId)
.ToListAsync(); .ToListAsync();
foreach (var attrG in attrGroups) { foreach (var attrG in attrGroups) {
var name = attrG.Attr == null && attrG.Cult == null ? null : attrG.Attr + (attrG.Attr != null && attrG.Cult != null ? " / " : "") + attrG.Cult; var name = GetGroupName(attrG.Attr, attrG.Cult);
wGrid.Add((name, null, attrG.Weight, attrG.Weight, stat.Weight)); var typeGroups = groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult)
foreach (var g in groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult).OrderByDescending(g => g.Weight).ThenBy(g => g.SortId)) { .GroupBy(g => g.Type)
wGrid.Add((null, g.SortId, g.Weight, attrG.Weight, stat.Weight)); .Select(g => new { Type = g.Key, Weight = g.Sum(p => p.Weight) })
.OrderByDescending(g => g.Weight).ThenBy(g => g.Type)
.ToList();
wGrid.Add((name, null, null, attrG.Weight, null, attrG.Weight, stat.Weight, typeGroups.Count == 1 ? typeGroups.First().Type : null));
if (typeGroups.Count == 1) {
foreach (var g in groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult).OrderByDescending(g => g.Weight).ThenBy(g => g.SortId)) {
wGrid.Add((null, null, g.SortId, g.Weight, null, attrG.Weight, stat.Weight, null));
}
} else {
foreach (var t in typeGroups) {
wGrid.Add((null, t.Type == "R" ? "Rot" : "Weiß", null, t.Weight, t.Weight, attrG.Weight, stat.Weight, t.Type));
foreach (var g in groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult && g.Type == t.Type).OrderByDescending(g => g.Weight).ThenBy(g => g.SortId)) {
wGrid.Add((null, null, g.SortId, g.Weight, t.Weight, attrG.Weight, stat.Weight, null));
}
}
} }
} }
foreach (var attrG in attrGroups) { foreach (var attrG in attrGroups) {
var name = attrG.Attr == null && attrG.Cult == null ? null : attrG.Attr + (attrG.Attr != null && attrG.Cult != null ? " / " : "") + attrG.Cult; var name = GetGroupName(attrG.Attr, attrG.Cult);
gGrid.Add((name, null, attrG.Min, attrG.Avg, attrG.Max)); var typeGroups = groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult)
foreach (var g in groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult).OrderByDescending(g => g.Avg).ThenBy(g => g.SortId)) { .GroupBy(g => g.Type)
gGrid.Add((null, g.SortId, g.Min, g.Avg, g.Max)); .Select(g => new {
Type = g.Key,
Weight = g.Sum(p => p.Weight),
Min = g.Min(p => p.Min),
Avg = g.Sum(p => p.Avg * p.Weight) / g.Sum(p => p.Weight),
Max = g.Max(p => p.Max),
})
.OrderByDescending(g => g.Weight).ThenBy(g => g.Type)
.ToList();
gGrid.Add((name, null, null, attrG.Min, attrG.Avg, attrG.Max, typeGroups.Count == 1 ? typeGroups.First().Type : null));
if (typeGroups.Count == 1) {
foreach (var g in groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult).OrderByDescending(g => g.Avg).ThenBy(g => g.SortId)) {
gGrid.Add((null, null, g.SortId, g.Min, g.Avg, g.Max, null));
}
} else {
foreach (var t in typeGroups) {
gGrid.Add((null, t.Type == "R" ? "Rot" : "Weiß", null, t.Min, t.Avg, t.Max, t.Type));
foreach (var g in groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult && g.Type == t.Type).OrderByDescending(g => g.Avg).ThenBy(g => g.SortId)) {
gGrid.Add((null, null, g.SortId, g.Min, g.Avg, g.Max, null));
}
}
} }
} }
if (attrGroups.Count == 1) { if (attrGroups.Count == 1) {
var g = attrGroups.First(); var g = attrGroups.First();
var name = g.Attr == null && g.Cult == null ? null : g.Attr + (g.Attr != null && g.Cult != null ? " / " : "") + g.Cult; var name = GetGroupName(g.Attr, g.Cult);
if (name != null) { if (name != null) {
wText += $" [{name}]"; wText += $" [{name}]";
gText += $" [{name}]"; gText += $" [{name}]";
@@ -1049,40 +1144,43 @@ namespace Elwig.Services {
} }
} else if (attrGroups.Count <= 4) { } else if (attrGroups.Count <= 4) {
wText += $" = {string.Join(" + ", attrGroups.Select(g => $"{g.Weight:N0} kg ({(double)g.Weight / stat.Weight:0%})" + (g.Attr == null && g.Cult == null ? "" : $" [{g.Attr}{(g.Attr != null && g.Cult != null ? " / " : "")}{g.Cult}]")))}"; wText += $" = {string.Join(" + ", attrGroups.Select(g => $"{g.Weight:N0} kg ({(double)g.Weight / stat.Weight:0%})" + (GetGroupName(g.Attr, g.Cult) is string s ? $" [{s}]" : "")))}";
gText += $" = {string.Join(" + ", attrGroups.Select(g => $"{g.Min:N1}/{g.Avg:N1}/{g.Max:N1}" + (g.Attr == null && g.Cult == null ? "" : $" [{g.Attr}{(g.Attr != null && g.Cult != null ? " / " : "")}{g.Cult}]")))}"; gText += $" = {string.Join(" + ", attrGroups.Select(g => $"{g.Min:N1}/{g.Avg:N1}/{g.Max:N1}" + (GetGroupName(g.Attr, g.Cult) is string s ? $" [{s}]" : "")))}";
} }
} }
return (wText, wGrid.ToArray(), gText, gGrid.ToArray()); return (wText, wGrid.ToArray(), gText, gGrid.ToArray());
} }
public static (Grid WeightGrid, Grid GradationGrid) GenerateToolTip((string?, string?, int, int?, int)[] weightData, (string?, string?, double, double, double)[] gradationData) { public static (Grid WeightGrid, Grid GradationGrid) GenerateToolTip((string?, string?, string?, int, int?, int?, int, string?)[] weightData, (string?, string?, string?, double, double, double, string?)[] gradationData) {
var wGrid = new Grid(); var wGrid = new Grid();
wGrid.ColumnDefinitions.Add(new() { Width = new(10) }); wGrid.ColumnDefinitions.Add(new() { Width = new(10) });
wGrid.ColumnDefinitions.Add(new() { Width = new(60) }); wGrid.ColumnDefinitions.Add(new() { Width = new(10) });
wGrid.ColumnDefinitions.Add(new() { Width = new(80) }); wGrid.ColumnDefinitions.Add(new() { Width = new(80) });
wGrid.ColumnDefinitions.Add(new() { Width = new(80) });
wGrid.ColumnDefinitions.Add(new() { Width = new(50) });
wGrid.ColumnDefinitions.Add(new() { Width = new(50) }); wGrid.ColumnDefinitions.Add(new() { Width = new(50) });
wGrid.ColumnDefinitions.Add(new() { Width = new(50) }); wGrid.ColumnDefinitions.Add(new() { Width = new(50) });
int rowNum = 0; int rowNum = 0;
foreach (var row in weightData) { foreach (var row in weightData) {
if (rowNum != 0 && row.Item2 == null) rowNum++; if (rowNum != 0 && row.Item2 == null && row.Item3 == null) rowNum++;
AddWeightToolTipRow(wGrid, rowNum++, row.Item1, row.Item2, row.Item3, row.Item4, row.Item5); AddWeightToolTipRow(wGrid, rowNum++, row.Item1, row.Item2, row.Item3, row.Item4, row.Item5, row.Item6, row.Item7, row.Item8);
} }
var gGrid = new Grid(); var gGrid = new Grid();
gGrid.ColumnDefinitions.Add(new() { Width = new(10) }); gGrid.ColumnDefinitions.Add(new() { Width = new(10) });
gGrid.ColumnDefinitions.Add(new() { Width = new(60) }); gGrid.ColumnDefinitions.Add(new() { Width = new(10) });
gGrid.ColumnDefinitions.Add(new() { Width = new(80) });
gGrid.ColumnDefinitions.Add(new() { Width = new(35) }); gGrid.ColumnDefinitions.Add(new() { Width = new(35) });
gGrid.ColumnDefinitions.Add(new() { Width = new(35) }); gGrid.ColumnDefinitions.Add(new() { Width = new(35) });
gGrid.ColumnDefinitions.Add(new() { Width = new(35) }); gGrid.ColumnDefinitions.Add(new() { Width = new(35) });
gGrid.AddToolTipCell("Min.", 0, 2, alignCenter: true); gGrid.AddToolTipCell("Min.", 0, 3, alignCenter: true);
gGrid.AddToolTipCell("⌀", 0, 3, alignCenter: true); gGrid.AddToolTipCell("⌀", 0, 4, alignCenter: true);
gGrid.AddToolTipCell("Max.", 0, 4, alignCenter: true); gGrid.AddToolTipCell("Max.", 0, 5, alignCenter: true);
rowNum = 1; rowNum = 1;
foreach (var row in gradationData) { foreach (var row in gradationData) {
if (rowNum != 1 && row.Item2 == null) rowNum++; if (rowNum != 1 && row.Item2 == null && row.Item3 == null) rowNum++;
AddGradationToolTipRow(gGrid, rowNum++, row.Item1, row.Item2, row.Item3, row.Item4, row.Item5); AddGradationToolTipRow(gGrid, rowNum++, row.Item1, row.Item2, row.Item3, row.Item4, row.Item5, row.Item6, row.Item7);
} }
return (wGrid, gGrid); return (wGrid, gGrid);
+30 -2
View File
@@ -1,3 +1,4 @@
using Microsoft.Data.Sqlite;
using Microsoft.Win32; using Microsoft.Win32;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -117,8 +118,35 @@ namespace Elwig.Services {
public static void ShowException(string title, string? text, Exception exc, bool showExcType = false, bool isError = true) { 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 = text == null ? "" : text + (text.EndsWith('.') || text.EndsWith('!') || text.EndsWith('?') ? "" : ":") + "\n\n";
text += exc.Message + (showExcType ? $" ({exc.GetType().Name})" : ""); if ((exc.InnerException ?? exc) is SqliteException sql) {
if (exc.InnerException != null) text += "\n\n" + exc.InnerException.Message; if (sql.SqliteErrorCode == 5 || sql.SqliteErrorCode == 6) {
// SQLITE_BUSY / SQLITE_LOCKED
text += "Die Datenbank wurde gerade von einem anderen Gerät/Prozess verwendet. Bitte noch einmal versuchen!\n\n" + sql.Message;
} else if (sql.SqliteErrorCode == 19) {
// SQLITE_CONSTRAINT
switch (sql.SqliteExtendedErrorCode) {
case 1555: // SQLITE_CONSTRAINT_PRIMARYKEY - indicating that a PRIMARY KEY constraint failed
case 2067: // SQLITE_CONSTRAINT_UNIQUE - indicating that a UNIQUE constraint failed
case 2579: // SQLITE_CONSTRAINT_ROWID - indicating that a rowid is not unique
text += "Dieser Datensatz existiert bereits!\n\n";
break;
case 787: // SQLITE_CONSTRAINT_FOREIGNKEY - indicating that a foreign key constraint failed
case 1299: // SQLITE_CONSTRAINT_NOTNULL - indicating that a NOT NULL constraint failed
case 275: // SQLITE_CONSTRAINT_CHECK - indicating that a CHECK constraint failed
case 1811: // SQLITE_CONSTRAINT_TRIGGER - indicating that a RAISE function within a trigger fired, causing the SQL statement to abort
case 531: // SQLITE_CONSTRAINT_COMMITHOOK - indicating that a commit hook callback returned non-zero that thus caused the SQL statement to be rolled back
text += "Der Datensatz ist inkonsistent!\n\n";
break;
}
text += sql.Message;
} else {
text += exc.Message + (showExcType ? $" ({exc.GetType().Name})" : "");
if (exc.InnerException != null) text += "\n\n" + exc.InnerException.Message;
}
} else {
text += exc.Message + (showExcType ? $" ({exc.GetType().Name})" : "");
if (exc.InnerException != null) text += "\n\n" + exc.InnerException.Message;
}
if (isError) { if (isError) {
ShowError(title, text); ShowError(title, text);
} else { } else {
@@ -3,6 +3,7 @@ using Elwig.Models;
using Elwig.Models.Entities; using Elwig.Models.Entities;
using Elwig.ViewModels; using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -11,7 +12,7 @@ namespace Elwig.Services {
public static async Task InitInputs(this MemberBusinessSharesViewModel vm) { public static async Task InitInputs(this MemberBusinessSharesViewModel vm) {
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
vm.DateNoticeString = $"{Utils.Today:dd.MM.yyyy}"; vm.DateNoticeString = $"{DateTime.Today:dd.MM.yyyy}";
vm.ValuePerShare = (await ctx.Seasons.OrderBy(s => s.Year).LastOrDefaultAsync())?.BusinessShareValue; vm.ValuePerShare = (await ctx.Seasons.OrderBy(s => s.Year).LastOrDefaultAsync())?.BusinessShareValue;
} }
@@ -25,6 +26,7 @@ namespace Elwig.Services {
vm.DateEffective = h.DateEffective; vm.DateEffective = h.DateEffective;
vm.Reason = h.Reason + (h.Reason == MemberHistory.REASON_TRANSFER ? $"_{(h.ToMgNr == mgnr ? "from" : "to")}" : ""); vm.Reason = h.Reason + (h.Reason == MemberHistory.REASON_TRANSFER ? $"_{(h.ToMgNr == mgnr ? "from" : "to")}" : "");
vm.Shares = h.Shares; vm.Shares = h.Shares;
vm.CompanyHasSigned = h.CompanyHasSigned;
vm.MemberHasSigned = h.MemberHasSigned; vm.MemberHasSigned = h.MemberHasSigned;
vm.ValuePerShare = h.ValuePerShare; vm.ValuePerShare = h.ValuePerShare;
vm.OtherMgNr = h.FromMgNr != null && h.FromMgNr != mgnr ? h.FromMgNr : h.ToMgNr != null && h.ToMgNr != mgnr ? h.ToMgNr : null; vm.OtherMgNr = h.FromMgNr != null && h.FromMgNr != mgnr ? h.FromMgNr : h.ToMgNr != null && h.ToMgNr != mgnr ? h.ToMgNr : null;
@@ -62,6 +64,7 @@ namespace Elwig.Services {
Reason = vm.Reason?.Split("_")[0] ?? MemberHistory.REASON_CONVERT, Reason = vm.Reason?.Split("_")[0] ?? MemberHistory.REASON_CONVERT,
Source = "manual", Source = "manual",
Shares = vm.Shares ?? 0, Shares = vm.Shares ?? 0,
CompanyHasSigned = vm.CompanyHasSigned,
MemberHasSigned = vm.MemberHasSigned, MemberHasSigned = vm.MemberHasSigned,
ValuePerShare = vm.ValuePerShare, ValuePerShare = vm.ValuePerShare,
CurrencyCode = vm.ValuePerShare != null ? "EUR" : null, CurrencyCode = vm.ValuePerShare != null ? "EUR" : null,
+61 -5
View File
@@ -10,6 +10,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Media;
namespace Elwig.Services { namespace Elwig.Services {
public static class MemberService { public static class MemberService {
@@ -106,6 +107,8 @@ namespace Elwig.Services {
vm.UstIdNr = m.UstIdNr; vm.UstIdNr = m.UstIdNr;
vm.LfbisNr = m.LfbisNr; vm.LfbisNr = m.LfbisNr;
vm.OrganicOperatorId = m.OrganicOperatorId;
vm.OrganicAuthorityCode = m.OrganicAuthorityCode;
vm.IsBuchführend = m.IsBuchführend; vm.IsBuchführend = m.IsBuchführend;
vm.IsOrganic = m.IsOrganic; vm.IsOrganic = m.IsOrganic;
@@ -187,10 +190,9 @@ namespace Elwig.Services {
.GroupBy(d => d.Year) .GroupBy(d => d.Year)
.ToDictionaryAsync(g => g.Key, g => g.Any()); .ToDictionaryAsync(g => g.Key, g => g.Any());
if (m.MgNr != vm.MgNr)
return;
await App.MainDispatcher.BeginInvoke(() => { await App.MainDispatcher.BeginInvoke(() => {
if (m.MgNr != vm.MgNr)
return;
var (d1Grid, _) = DeliveryService.GenerateToolTip(d1GridData, []); var (d1Grid, _) = DeliveryService.GenerateToolTip(d1GridData, []);
var (d2Grid, _) = DeliveryService.GenerateToolTip(d2GridData, []); var (d2Grid, _) = DeliveryService.GenerateToolTip(d2GridData, []);
var grid = AreaComService.GenerateToolTip(gridData); var grid = AreaComService.GenerateToolTip(gridData);
@@ -203,13 +205,65 @@ namespace Elwig.Services {
vm.StatusAreaCommitmentInfo = $"{Utils.CurrentLastSeason}"; vm.StatusAreaCommitmentInfo = $"{Utils.CurrentLastSeason}";
vm.StatusAreaCommitment = text; vm.StatusAreaCommitment = text;
vm.StatusAreaCommitmentToolTip = grid; vm.StatusAreaCommitmentToolTip = grid;
vm.MemberHasDeliveries = Enumerable.Range(0, 9999).Select(i => deliveries.GetValueOrDefault(i, false)).ToList(); vm.MemberHasDeliveries = [.. Enumerable.Range(0, 9999).Select(i => deliveries.GetValueOrDefault(i, false))];
});
});
vm.OrganicTest1 = null;
vm.OrganicTest2 = "...";
vm.OrganicTest3 = Brushes.Black;
vm.OrganicTest4 = null;
Utils.RunBackground("Bio-Zertifikatsdaten laden", async () => {
if (App.MainDispatcher == null || !m.IsOrganic)
return;
var certs = await OrganicService.GetTracesCertificates(m);
// TODO button for TRACES search url
// TODO button for authority search url
await App.MainDispatcher.BeginInvoke(() => {
if (m.MgNr != vm.MgNr)
return;
vm.OrganicTest2 = null;
if (certs.Length == 0) {
vm.OrganicTest1 = "Nicht gefunden";
vm.OrganicTest3 = Brushes.DarkRed;
} else {
var cert = certs[0];
vm.OrganicTest4 = $"Zertifikat-Nr.: {cert.Id}\nBio-KSt.: {cert.AuthorityCode}\nEU Operator-ID: {cert.OperatorId}\nOperator: {cert.OperatorName}";
if (cert.IsValidForWineProduction) {
vm.OrganicTest1 = "Gültig";
vm.OrganicTest2 = $" (bis {cert.ExpiresOn:dd.MM.yyyy})";
vm.OrganicTest3 = Brushes.DarkGreen;
} else if (cert.IsValid) {
vm.OrganicTest1 = "Teilw. gültig";
vm.OrganicTest2 = $" (bis {cert.ExpiresOn:dd.MM.yyyy})";
vm.OrganicTest3 = Brushes.OrangeRed;
} else {
vm.OrganicTest3 = Brushes.DarkRed;
switch (cert.Status) {
case OrganicService.TracesStatus.ISSUED:
vm.OrganicTest1 = "Ausgestellt";
vm.OrganicTest2 = $" (am {cert.LastStatusUpdateOn:dd.MM.yyyy})"; break;
case OrganicService.TracesStatus.SUSPENDED:
vm.OrganicTest1 = "Gesperrt";
vm.OrganicTest2 = $" (am {cert.LastStatusUpdateOn:dd.MM.yyyy})"; break;
case OrganicService.TracesStatus.EXPIRED:
vm.OrganicTest1 = "Ausgelaufen";
vm.OrganicTest2 = $" (am {cert.LastStatusUpdateOn:dd.MM.yyyy})"; break;
case OrganicService.TracesStatus.WITHDRAWN:
vm.OrganicTest1 = "Widerrufen";
vm.OrganicTest2 = $" (am {cert.LastStatusUpdateOn:dd.MM.yyyy})"; break;
}
}
}
}); });
}); });
vm.MemberHasEmail = m.EmailAddresses.Count > 0; vm.MemberHasEmail = m.EmailAddresses.Count > 0;
vm.MemberCanSendEmail = App.Config.Smtp != null && m.EmailAddresses.Count > 0; vm.MemberCanSendEmail = App.Config.Smtp != null && m.EmailAddresses.Count > 0;
vm.MemberHasDeliveries = Enumerable.Range(0, 9999).Select(i => false).ToList(); vm.MemberHasDeliveries = [.. Enumerable.Range(0, 9999).Select(i => false)];
} }
public static async Task<(List<string>, IQueryable<Member>, List<string>)> GetFilters(this MemberAdminViewModel vm, AppDbContext ctx) { public static async Task<(List<string>, IQueryable<Member>, List<string>)> GetFilters(this MemberAdminViewModel vm, AppDbContext ctx) {
@@ -555,6 +609,8 @@ namespace Elwig.Services {
UstIdNr = string.IsNullOrWhiteSpace(vm.UstIdNr) ? null : vm.UstIdNr, UstIdNr = string.IsNullOrWhiteSpace(vm.UstIdNr) ? null : vm.UstIdNr,
LfbisNr = string.IsNullOrWhiteSpace(vm.LfbisNr) ? null : vm.LfbisNr, LfbisNr = string.IsNullOrWhiteSpace(vm.LfbisNr) ? null : vm.LfbisNr,
OrganicOperatorId = string.IsNullOrWhiteSpace(vm.OrganicOperatorId) ? null : vm.OrganicOperatorId,
OrganicAuthorityCode = string.IsNullOrWhiteSpace(vm.OrganicAuthorityCode) ? null : vm.OrganicAuthorityCode,
IsBuchführend = vm.IsBuchführend, IsBuchführend = vm.IsBuchführend,
IsOrganic = vm.IsOrganic, IsOrganic = vm.IsOrganic,
+143
View File
@@ -0,0 +1,143 @@
using Elwig.Helpers;
using Elwig.Models.Entities;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
using System.Web;
namespace Elwig.Services {
public static class OrganicService {
public const string TRACES_API_URL = "https://webgate.ec.europa.eu/tracesnt/directory/publication/organic-operator/for/query";
public const string TRACES_PDF_URL_PREFIX = "https://webgate.ec.europa.eu/tracesnt/directory/publication/organic-operator/digitally-signed/";
public enum TracesStatus {
ISSUED, SUSPENDED, WITHDRAWN, EXPIRED
}
public enum TracesActivity {
PREPARATION, EXPORT, IMPORT, STORING, PRODUCTION, DISTRIBUTION, DISTRIBUTION_PLACING_ON_THE_MARKET
}
public enum TracesCategoryOfProduct {
UNPROCESSED_PLANT_PRODUCTS_INCLUDING_SEEDS, LIVESTOCK_AND_UNPROCESSED_LIVESTOCK_PRODUCTS, ALGAE_AND_UNPROCESSED_AQUACULTURE_PRODUCTS,
PROCESSED_AGRICULTURAL_PRODUCTS_INCLUDING_AQUACULTURE_FOR_USE_AS_FOOD, FEED, WINE, OTHER_PRODUCTS_NOT_COVERED_BY_PREVIOUS_CATEGORIES
}
public record TracesCertificate {
public required string Id { get; init; }
public required string AuthorityCode { get; init; }
public required string OperatorId { get; init; }
public required string OperatorName { get; init; }
public required TracesActivity[] Activities { get; init; }
public required TracesCategoryOfProduct[] CategoriesOfProduct { get; init; }
public required DateOnly IssuedOn { get; init; }
public required DateOnly ExpiresOn { get; init; }
public required DateOnly LastStatusUpdateOn { get; init; }
public required TracesStatus Status { get; init; }
public string PdfUrl => TRACES_PDF_URL_PREFIX + Id + ".pdf";
public bool IsValid => Status == TracesStatus.ISSUED && IssuedOn.ToDateTime(new()) <= DateTime.Today && ExpiresOn.ToDateTime(new()) >= DateTime.Today;
public bool IsValidForWineProduction => IsValid && Activities.Contains(TracesActivity.PRODUCTION) &&
(CategoriesOfProduct.Contains(TracesCategoryOfProduct.UNPROCESSED_PLANT_PRODUCTS_INCLUDING_SEEDS) || CategoriesOfProduct.Contains(TracesCategoryOfProduct.WINE));
}
public async static Task<TracesCertificate[]> GetTracesCertificates(Member m, bool? tryNameAndAddress = null) {
// TODO cache
return await FetchTracesCertificates(m, tryNameAndAddress);
}
private async static Task<JsonArray> TryFetchTracesCertificates(Dictionary<string, string> query) {
var q = HttpUtility.ParseQueryString("");
q.Add("sort", "-issuedOn");
q.Add("countryCode", "AT");
foreach (var (k, v) in query) {
q.Add(k, v);
}
using var client = Utils.GetHttpClient();
using var res = await client.GetAsync($"{TRACES_API_URL}?{q}");
res.EnsureSuccessStatusCode();
var resJson = JsonNode.Parse(await res.Content.ReadAsStringAsync());
var certs = resJson?.AsArray() ?? throw new Exception();
if (certs.Count == 0)
throw new Exception();
return certs;
}
public static Task<TracesCertificate[]> FetchTracesCertificates(Member m, bool? tryNameAndAddress = null) {
return FetchTracesCertificates(m.OrganicOperatorId, m.LfbisNr,
(tryNameAndAddress ?? m.IsOrganic) ? m.BillingAddress is BillingAddr a ?
(a.FullName, a.Address, $"{a.PostalDest.AtPlz?.Plz}") :
(m.FullName, m.Address, $"{m.PostalDest.AtPlz?.Plz}") :
null);
}
public async static Task<TracesCertificate[]> FetchTracesCertificates(string? oocId, string? lfbisNr, (string Name, string Address, string PostalCode)? address) {
bool searchedByName = false;
JsonArray? jsonCerts = null;
if (oocId != null) {
try {
jsonCerts = await TryFetchTracesCertificates(new Dictionary<string, string> {
{ "operatorIdentifierType", "ooc_identifier" },
{ "operatorIdentifierSearchOperator", "STRICT" },
{ "operatorIdentifier", oocId },
});
} catch {
jsonCerts = null;
}
}
if (jsonCerts == null && lfbisNr != null) {
try {
jsonCerts = await TryFetchTracesCertificates(new Dictionary<string, string> {
{ "operatorIdentifierType", "comp_reg" },
{ "operatorIdentifierSearchOperator", "STRICT" },
{ "operatorIdentifier", lfbisNr.TrimStart('0') },
});
} catch {
jsonCerts = null;
}
}
if (jsonCerts == null && address != null) {
try {
jsonCerts = await TryFetchTracesCertificates(new Dictionary<string, string> {
{ "operatorPostalCode", address.Value.PostalCode },
{ "query", $"{address.Value.Name} {address.Value.Address}" },
});
searchedByName = true;
} catch {
jsonCerts = null;
}
}
if (jsonCerts == null)
return [];
TracesCertificate[] certs = [];
try {
certs = [.. jsonCerts.Select(j => new TracesCertificate {
Id = j!["reference"]?.GetValue<string>() ?? throw new Exception(),
AuthorityCode = j["issuingBody"]?["code"]?.GetValue<string>() ?? throw new Exception(),
OperatorId = j["operatorIdentifier"]?.GetValue<string>() ?? throw new Exception(),
OperatorName = j["operator"]?["name"]?.GetValue<string>() ?? throw new Exception(),
Activities = [.. j["activities"]?.AsArray().Select(a => Enum.Parse<TracesActivity>(a?["id"]?.GetValue<string>().ToUpper().Replace("_IMPORT", "IMPORT") ?? throw new Exception())) ?? []],
CategoriesOfProduct = [.. j["categoriesOfProduct"]?.AsArray().Select(c => Enum.Parse<TracesCategoryOfProduct>(c?["id"]?.GetValue<string>() ?? throw new Exception())) ?? []],
IssuedOn = DateOnly.FromDateTime(DateTime.ParseExact(j["issuedOn"]?.GetValue<string>() ?? throw new Exception(), "yyyy-MM-ddTHH:mm:ss.fffK", CultureInfo.InvariantCulture, DateTimeStyles.None)),
ExpiresOn = DateOnly.ParseExact(j["expiresOn"]?.GetValue<string>() ?? throw new Exception(), "yyyy-MM-dd"),
LastStatusUpdateOn = DateOnly.FromDateTime(DateTime.ParseExact(j["lastStatusUpdateDateTime"]?.GetValue<string>() ?? throw new Exception(), "yyyy-MM-ddTHH:mm:ss.fffK", CultureInfo.InvariantCulture, DateTimeStyles.None)),
Status = Enum.Parse<TracesStatus>(j["status"]?["id"]?.GetValue<string>() ?? throw new Exception()),
})];
} catch {
return [];
}
return (!searchedByName || certs.Where(c => c.Status == TracesStatus.ISSUED).Select(c => c.OperatorId).ToHashSet().Count == 1) ? certs : [];
}
}
}
+11 -2
View File
@@ -214,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 { try {
var import = await GetFilesToImport(url, username, password); var import = await GetFilesToImport(url, username, password);
var paths = new List<string>(); var paths = new List<string>();
@@ -226,7 +226,7 @@ namespace Elwig.Services {
paths.Add(filename); paths.Add(filename);
} }
} }
await ElwigData.Import(paths, ElwigData.ImportMode.FromBranches); await ElwigData.Import(paths, confirmImports ? ElwigData.ImportMode.Interactively : ElwigData.ImportMode.FromBranches);
} catch (HttpRequestException exc) { } catch (HttpRequestException exc) {
InteractionService.ShowException("Daten herunterladen", "Eventuell Internetverbindung prüfen!", exc); InteractionService.ShowException("Daten herunterladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (TaskCanceledException exc) { } catch (TaskCanceledException exc) {
@@ -264,5 +264,14 @@ namespace Elwig.Services {
return false; 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;
}
}
} }
} }
+9 -1
View File
@@ -25,7 +25,7 @@ namespace Elwig.ViewModels {
[ObservableProperty] [ObservableProperty]
private string? _manualWeighingReason; private string? _manualWeighingReason;
[ObservableProperty] [ObservableProperty]
private string? _scaleId; private string? _scaleTerminalId;
[ObservableProperty] [ObservableProperty]
private string? _weighingData; private string? _weighingData;
@@ -120,6 +120,14 @@ namespace Elwig.ViewModels {
set => WeightString = $"{value:N0}"; set => WeightString = $"{value:N0}";
} }
[ObservableProperty] [ObservableProperty]
private string? _tareString = "-";
public int? Tare {
get => int.TryParse(TareString?.Replace(Utils.GroupSeparator, "").Replace(" kg", ""), out var w) ? w : null;
set => TareString = value == null ? "-" : $"{value:N0} kg{(TargetTare != null ? "*" : "")}";
}
public int? ActualTare;
public int? TargetTare;
[ObservableProperty]
private bool _isManualWeighing; private bool _isManualWeighing;
[ObservableProperty] [ObservableProperty]
private bool? _isNetWeightValue = false; private bool? _isNetWeightValue = false;
@@ -8,6 +8,9 @@ using System.Windows.Controls;
namespace Elwig.ViewModels { namespace Elwig.ViewModels {
public partial class DeliveryAncmtAdminViewModel : ObservableObject { public partial class DeliveryAncmtAdminViewModel : ObservableObject {
[ObservableProperty]
private string _title = "Anmeldungen - Elwig";
[ObservableProperty] [ObservableProperty]
private string? _searchQuery = ""; private string? _searchQuery = "";
public List<string> TextFilter => [.. SearchQuery?.ToLower().Split(' ').ToList().FindAll(e => e.Length > 0) ?? []]; public List<string> TextFilter => [.. SearchQuery?.ToLower().Split(' ').ToList().FindAll(e => e.Length > 0) ?? []];
+17 -1
View File
@@ -6,11 +6,14 @@ using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Documents; using System.Windows.Media;
namespace Elwig.ViewModels { namespace Elwig.ViewModels {
public partial class MemberAdminViewModel : ObservableObject { public partial class MemberAdminViewModel : ObservableObject {
[ObservableProperty]
private string _title = "Mitglieder - Elwig";
public int? TransferPredecessorAreaComs = null; public int? TransferPredecessorAreaComs = null;
public int? CancelAreaComs = null; public int? CancelAreaComs = null;
@@ -116,10 +119,23 @@ namespace Elwig.ViewModels {
[ObservableProperty] [ObservableProperty]
private string? _lfbisNr; private string? _lfbisNr;
[ObservableProperty] [ObservableProperty]
private string? _organicOperatorId;
[ObservableProperty]
private string? _organicAuthorityCode;
[ObservableProperty]
private bool _isBuchführend; private bool _isBuchführend;
[ObservableProperty] [ObservableProperty]
private bool _isOrganic; private bool _isOrganic;
[ObservableProperty]
private string? _organicTest1;
[ObservableProperty]
private string? _organicTest2;
[ObservableProperty]
private Brush? _organicTest3;
[ObservableProperty]
private string? _organicTest4;
[ObservableProperty] [ObservableProperty]
private string? _entryDate; private string? _entryDate;
[ObservableProperty] [ObservableProperty]
@@ -52,6 +52,8 @@ namespace Elwig.ViewModels {
[ObservableProperty] [ObservableProperty]
private bool _deduct; private bool _deduct;
[ObservableProperty] [ObservableProperty]
private bool _companyHasSigned;
[ObservableProperty]
private bool _memberHasSigned; private bool _memberHasSigned;
[ObservableProperty] [ObservableProperty]
private string? _valuePerShareString; private string? _valuePerShareString;
+8
View File
@@ -576,6 +576,14 @@ namespace Elwig.Windows {
InputLostFocus((TextBox)sender, Validator.CheckLfbisNr); InputLostFocus((TextBox)sender, Validator.CheckLfbisNr);
} }
protected void OrganicOperatorIdInput_TextChanged(object sender, TextChangedEventArgs? evt) {
InputTextChanged((TextBox)sender, Validator.CheckOrganicOperatorId);
}
protected void OrganicOperatorIdInput_LostFocus(object sender, RoutedEventArgs? evt) {
InputLostFocus((TextBox)sender, Validator.CheckOrganicOperatorId);
}
protected void OrganicAuthorityCodeInput_TextChanged(object sender, TextChangedEventArgs? evt) { protected void OrganicAuthorityCodeInput_TextChanged(object sender, TextChangedEventArgs? evt) {
InputTextChanged((TextBox)sender, Validator.CheckOrganicAuthorityCode); InputTextChanged((TextBox)sender, Validator.CheckOrganicAuthorityCode);
} }
+4 -5
View File
@@ -23,7 +23,6 @@ namespace Elwig.Windows {
CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput)); CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput));
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
ViewModel.FilterMember = ctx.Members.Find(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value"); ViewModel.FilterMember = ctx.Members.Find(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
ViewModel.Title = $"Flächenbindungen - {ViewModel.FilterMember.AdministrativeName} - Elwig";
ExemptInputs = [ ExemptInputs = [
AreaCommitmentList, RevisionList, AreaCommitmentList, RevisionList,
NewAreaCommitmentButton, EditAreaCommitmentButton, DeleteAreaCommitmentButton, SaveButton, NewAreaCommitmentButton, EditAreaCommitmentButton, DeleteAreaCommitmentButton, SaveButton,
@@ -51,9 +50,9 @@ namespace Elwig.Windows {
var cursor = Mouse.OverrideCursor != null; var cursor = Mouse.OverrideCursor != null;
if (!cursor) Mouse.OverrideCursor = Cursors.Wait; if (!cursor) Mouse.OverrideCursor = Cursors.Wait;
var query = (vm.SearchQuery, vm.FilterSeason); var query = (vm.SearchQuery, vm.FilterSeason);
var (filter, contracts, areaComs, areaComCount, stat) = await Task.Run(async () => { var (filterNames, filter, contracts, areaComs, areaComCount, stat) = await Task.Run(async () => {
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
var (_, contractQuery, areaComQuery, filter) = await vm.GetFilters(ctx); var (filterNames, contractQuery, areaComQuery, filter) = await vm.GetFilters(ctx);
var contracts = await contractQuery var contracts = await contractQuery
.Include(c => c.Revisions).ThenInclude(a => a.Member) .Include(c => c.Revisions).ThenInclude(a => a.Member)
.ToListAsync(); .ToListAsync();
@@ -73,11 +72,12 @@ namespace Elwig.Windows {
var season = await ctx.FetchSeasons().FirstOrDefaultAsync(); var season = await ctx.FetchSeasons().FirstOrDefaultAsync();
var stat = await AreaComService.GenerateToolTipData(areaComQuery, season?.MaxKgPerHa ?? 10_000); var stat = await AreaComService.GenerateToolTipData(areaComQuery, season?.MaxKgPerHa ?? 10_000);
return (filter, contracts, areaComs, areaComCount, stat); return (filterNames, filter, contracts, areaComs, areaComCount, stat);
}); });
if (!cursor) Mouse.OverrideCursor = null; if (!cursor) Mouse.OverrideCursor = null;
if (query != (ViewModel.SearchQuery, ViewModel.FilterSeason)) return; if (query != (ViewModel.SearchQuery, ViewModel.FilterSeason)) return;
ViewModel.Title = Utils.GetTitle("Flächenbindungen", filterNames, filter);
ControlUtils.RenewItemsSource(AreaCommitmentList, contracts, ControlUtils.RenewItemsSource(AreaCommitmentList, contracts,
AreaCommitmentList_SelectionChanged, ViewModel.TextFilter.Count > 0 ? ControlUtils.RenewSourceDefault.IfOnly : ControlUtils.RenewSourceDefault.None, !updateSort); AreaCommitmentList_SelectionChanged, ViewModel.TextFilter.Count > 0 ? ControlUtils.RenewSourceDefault.IfOnly : ControlUtils.RenewSourceDefault.None, !updateSort);
RefreshInputs(); RefreshInputs();
@@ -164,7 +164,6 @@ namespace Elwig.Windows {
return; return;
} }
ViewModel.FilterMember = m; ViewModel.FilterMember = m;
ViewModel.Title = $"Flächenbindungen - {ViewModel.FilterMember.AdministrativeName} - Elwig";
ControlUtils.RenewItemsSource(KgInput, await ctx.WbKgs ControlUtils.RenewItemsSource(KgInput, await ctx.WbKgs
.Include(k => k.AtKg.WbKg!.Rds) .Include(k => k.AtKg.WbKg!.Rds)
+14 -4
View File
@@ -121,12 +121,18 @@
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="3" Margin="0,130,10,10" Width="64" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="3" Margin="0,130,10,10" Width="64"
TextChanged="LfbisNrInput_TextChanged" LostFocus="LfbisNrInput_LostFocus"/> TextChanged="LfbisNrInput_TextChanged" LostFocus="LfbisNrInput_LostFocus"/>
<Label Content="Bio-Kontrollstelle:" <Label Content="Bio-KSt.:" ToolTip="Bio-Konstrollstelle"
VerticalAlignment="Top" HorizontalAlignment="Right" Grid.Column="3" Margin="10,130,110,10"/> VerticalAlignment="Top" HorizontalAlignment="Right" Grid.Column="3" Margin="10,100,110,10"/>
<TextBox x:Name="ClientOrganicAuthorityInput" <TextBox x:Name="ClientOrganicAuthorityCodeInput"
VerticalAlignment="Top" HorizontalAlignment="Right" Grid.Column="3" Margin="10,130,10,10" Width="90" VerticalAlignment="Top" HorizontalAlignment="Right" Grid.Column="3" Margin="10,100,10,10" Width="91"
TextChanged="OrganicAuthorityCodeInput_TextChanged" LostFocus="OrganicAuthorityCodeInput_LostFocus"/> TextChanged="OrganicAuthorityCodeInput_TextChanged" LostFocus="OrganicAuthorityCodeInput_LostFocus"/>
<Label Content="OOC-ID:" ToolTip="Bio-EU-Operator-ID / Organic Operator Certificate ID"
VerticalAlignment="Top" HorizontalAlignment="Right" Grid.Column="3" Margin="10,130,110,10"/>
<TextBox x:Name="ClientOrganicOperatorIdInput"
VerticalAlignment="Top" HorizontalAlignment="Right" Grid.Column="3" Margin="10,130,10,10" Width="91"
TextChanged="OrganicOperatorIdInput_TextChanged" LostFocus="OrganicOperatorIdInput_LostFocus"/>
<Label Content="Telefon-Nr.:" <Label Content="Telefon-Nr.:"
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="2" Margin="10,160,0,10"/> VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="2" Margin="10,160,0,10"/>
<TextBox x:Name="ClientPhoneNrInput" Margin="0,160,10,10" Grid.Column="3" <TextBox x:Name="ClientPhoneNrInput" Margin="0,160,10,10" Grid.Column="3"
@@ -320,6 +326,10 @@
<Label Content="Beschreibung:" Margin="10,70,0,10"/> <Label Content="Beschreibung:" Margin="10,70,0,10"/>
<TextBox x:Name="WineCultivationDescriptionInput" Grid.Column="1" Margin="0,70,10,10" <TextBox x:Name="WineCultivationDescriptionInput" Grid.Column="1" Margin="0,70,10,10"
TextChanged="WineCultivation_Changed"/> TextChanged="WineCultivation_Changed"/>
<CheckBox x:Name="WineCultivationOrganicInput" Content="Bio" Grid.Column="1" Margin="0,105,10,10"
Checked="WineCultivation_Changed" Unchecked="WineCultivation_Changed"
HorizontalAlignment="Left" VerticalAlignment="Top"/>
</Grid> </Grid>
</Grid> </Grid>
</TabItem> </TabItem>
+17 -17
View File
@@ -47,26 +47,26 @@ namespace Elwig.Windows {
if (!_actChanged || _actList == null || _acts == null || _actIds == null) if (!_actChanged || _actList == null || _acts == null || _actIds == null)
return; return;
foreach (var (vtrgid, _) in _acts.Where(a => a.Value == null)) { // delete
ctx.Remove(ctx.AreaCommitmentTypes.Find(vtrgid)!); 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 (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();
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.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))) { // insert
if (type.VtrgId == null) continue; ctx.AddRange(_actList
ctx.Add(type); .Where(t => !_actIds.ContainsKey(t) && t.VtrgId != null));
}
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
} }
@@ -125,7 +125,7 @@ namespace Elwig.Windows {
var id = AreaCommitmentTypeIdInput.Text; var id = AreaCommitmentTypeIdInput.Text;
if (old != null) _acts[old] = id; if (old != null) _acts[old] = id;
attr.VtrgId = id; attr.VtrgId = id;
attr.SortId = v?.SortId; attr.SortId = v?.SortId ?? "";
attr.AttrId = a?.AttrId; attr.AttrId = a?.AttrId;
attr.MinKgPerHa = AreaCommitmentTypeMinKgPerHaInput.Text.Length > 0 ? int.Parse(AreaCommitmentTypeMinKgPerHaInput.Text) : null; attr.MinKgPerHa = AreaCommitmentTypeMinKgPerHaInput.Text.Length > 0 ? int.Parse(AreaCommitmentTypeMinKgPerHaInput.Text) : null;
attr.PenaltyPerKg = AreaCommitmentTypePenaltyPerKgInput.Text.Length > 0 ? decimal.Parse(AreaCommitmentTypePenaltyPerKgInput.Text) : null; attr.PenaltyPerKg = AreaCommitmentTypePenaltyPerKgInput.Text.Length > 0 ? decimal.Parse(AreaCommitmentTypePenaltyPerKgInput.Text) : null;
+13 -12
View File
@@ -41,25 +41,26 @@ namespace Elwig.Windows {
if (!_branchChanged || _branchList == null || _branches == null || _branchIds == null) if (!_branchChanged || _branchList == null || _branches == null || _branchIds == null)
return; return;
// delete
var deleteZwstIds = _branches.Where(b => b.Value == null).Select(b => b.Key).ToList(); var deleteZwstIds = _branches.Where(b => b.Value == null).Select(b => b.Key).ToList();
await ctx.Branches.Where(b => deleteZwstIds.Contains(b.ZwstId)).ExecuteDeleteAsync(); await ctx.Branches.Where(b => deleteZwstIds.Contains(b.ZwstId)).ExecuteDeleteAsync();
foreach (var (branch, old) in _branchIds) { // update
branch.ZwstId = old; ctx.UpdateRange(_branchList
} .Where(_branchIds.ContainsKey)
foreach (var (old, zwstid) in _branches.Where(b => b.Value != null)) { .Select(b => {
ctx.Update(ctx.Branches.Find(old)!); b.ZwstId = _branchIds[b];
} ctx.Entry(b).State = EntityState.Added;
return b;
}));
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
foreach (var (old, zwstid) in _branches.Where(b => b.Value != null && b.Key != b.Value)) {
foreach (var (old, zwstid) in _branches.Where(b => b.Value != null)) {
await ctx.Database.ExecuteSqlAsync($"UPDATE branch SET zwstid = {zwstid} WHERE zwstid = {old}"); await ctx.Database.ExecuteSqlAsync($"UPDATE branch SET zwstid = {zwstid} WHERE zwstid = {old}");
} }
foreach (var branch in _branchList.Where(b => !_branchIds.ContainsKey(b))) { // insert
if (branch.ZwstId == null) continue; ctx.AddRange(_branchList
ctx.Add(branch); .Where(b => !_branchIds.ContainsKey(b) && b.ZwstId != null));
}
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
} }
+16 -16
View File
@@ -48,30 +48,30 @@ namespace Elwig.Windows {
if (!_modChanged || _modList == null || _mods == null || _modIds == null) if (!_modChanged || _modList == null || _mods == null || _modIds == null)
return; return;
var year = (SeasonList.SelectedItem as Season)?.Year;
int i = 0; int i = 0;
foreach (var mod in _modList) mod.Ordering = ++i; foreach (var mod in _modList) mod.Ordering = ++i;
var year = (SeasonList.SelectedItem as Season)?.Year; // delete
foreach (var (modid, _) in _mods.Where(m => m.Value == null)) { var deleteModIds = _mods.Where(b => b.Value == null).Select(b => b.Key).ToList();
ctx.Remove(ctx.Modifiers.Find(year, modid)!); await ctx.Modifiers.Where(b => b.Year == year && deleteModIds.Contains(b.ModId)).ExecuteDeleteAsync();
}
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();
// 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)) { 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.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))) { // insert
if (mod.ModId == null) continue; ctx.AddRange(_modList
await ctx.AddAsync(mod); .Where(m => !_modIds.ContainsKey(m) && m.ModId != null));
}
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
} }
+14 -12
View File
@@ -41,26 +41,28 @@ namespace Elwig.Windows {
if (!_attrChanged || _attrList == null || _attrs == null || _attrIds == null) if (!_attrChanged || _attrList == null || _attrs == null || _attrIds == null)
return; return;
// delete
var deleteAttrIds = _attrs.Where(a => a.Value == null).Select(a => a.Key).ToList(); var deleteAttrIds = _attrs.Where(a => a.Value == null).Select(a => a.Key).ToList();
await ctx.WineAttributes.Where(a => deleteAttrIds.Contains(a.AttrId)).ExecuteDeleteAsync(); 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)) { 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 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.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))) { // insert
if (attr.AttrId == null) continue; ctx.AddRange(_attrList
ctx.Add(attr); .Where(a => !_attrIds.ContainsKey(a) && a.AttrId != null));
}
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
} }
+20 -14
View File
@@ -41,25 +41,27 @@ namespace Elwig.Windows {
if (!_cultChanged || _cultList == null || _cults == null || _cultIds == null) if (!_cultChanged || _cultList == null || _cults == null || _cultIds == null)
return; return;
// delete
var deleteCultIds = _cults.Where(c => c.Value == null).Select(c => c.Key).ToList(); var deleteCultIds = _cults.Where(c => c.Value == null).Select(c => c.Key).ToList();
await ctx.WineCultivations.Where(c => deleteCultIds.Contains(c.CultId)).ExecuteDeleteAsync(); 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)) { 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.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))) { // insert
if (cult.CultId == null) continue; ctx.AddRange(_cultList
ctx.Add(cult); .Where(c => !_cultIds.ContainsKey(c) && c.CultId != null));
}
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
} }
@@ -70,10 +72,12 @@ namespace Elwig.Windows {
WineCultivationIdInput.Text = ""; WineCultivationIdInput.Text = "";
WineCultivationNameInput.Text = ""; WineCultivationNameInput.Text = "";
WineCultivationDescriptionInput.Text = ""; WineCultivationDescriptionInput.Text = "";
WineCultivationOrganicInput.IsChecked = false;
} else { } else {
WineCultivationIdInput.Text = cult.CultId; WineCultivationIdInput.Text = cult.CultId;
WineCultivationNameInput.Text = cult.Name; WineCultivationNameInput.Text = cult.Name;
WineCultivationDescriptionInput.Text = cult.Description; WineCultivationDescriptionInput.Text = cult.Description;
WineCultivationOrganicInput.IsChecked = cult.IsOrganic;
} }
_cultUpdate = false; _cultUpdate = false;
} }
@@ -103,7 +107,8 @@ namespace Elwig.Windows {
_cultChanged = _cultChanged || _cultChanged = _cultChanged ||
WineCultivationIdInput.Text != cult.CultId || WineCultivationIdInput.Text != cult.CultId ||
WineCultivationNameInput.Text != cult.Name || WineCultivationNameInput.Text != cult.Name ||
WineCultivationDescriptionInput.Text != (cult.Description ?? ""); WineCultivationDescriptionInput.Text != (cult.Description ?? "") ||
WineCultivationOrganicInput.IsChecked != cult.IsOrganic;
var old = _cultIds.GetValueOrDefault(cult); var old = _cultIds.GetValueOrDefault(cult);
var id = WineCultivationIdInput.Text ?? ""; var id = WineCultivationIdInput.Text ?? "";
@@ -111,7 +116,8 @@ namespace Elwig.Windows {
cult.CultId = id; cult.CultId = id;
cult.Name = WineCultivationNameInput.Text ?? ""; cult.Name = WineCultivationNameInput.Text ?? "";
cult.Description = WineCultivationDescriptionInput.Text ?? ""; cult.Description = WineCultivationDescriptionInput.Text ?? "";
if (cult.Description.Length == 0) cult.Description = null; cult.IsOrganic = WineCultivationOrganicInput.IsChecked ?? false;
if (string.IsNullOrWhiteSpace(cult.Description)) cult.Description = null;
CollectionViewSource.GetDefaultView(_cultList).Refresh(); CollectionViewSource.GetDefaultView(_cultList).Refresh();
UpdateButtons(); UpdateButtons();
+22 -21
View File
@@ -12,8 +12,6 @@ using System.Windows.Input;
namespace Elwig.Windows { namespace Elwig.Windows {
public partial class BaseDataWindow : AdministrationWindow { public partial class BaseDataWindow : AdministrationWindow {
protected AppDbContext? EditContext;
public BaseDataWindow() { public BaseDataWindow() {
InitializeComponent(); InitializeComponent();
RequiredInputs = [ RequiredInputs = [
@@ -27,7 +25,7 @@ namespace Elwig.Windows {
BranchAddressInput, BranchPhoneNrInput, BranchFaxNrInput, BranchMobileNrInput, BranchAddressInput, BranchPhoneNrInput, BranchFaxNrInput, BranchMobileNrInput,
WineAttributeIdInput, WineAttributeNameInput, WineAttributeActiveInput, WineAttributeIdInput, WineAttributeNameInput, WineAttributeActiveInput,
WineAttributeMaxKgPerHaInput, WineAttributeStrictInput, WineAttributeFillLowerInput, WineAttributeMaxKgPerHaInput, WineAttributeStrictInput, WineAttributeFillLowerInput,
WineCultivationIdInput, WineCultivationNameInput, WineCultivationDescriptionInput, WineCultivationIdInput, WineCultivationNameInput, WineCultivationDescriptionInput, WineCultivationOrganicInput,
AreaCommitmentTypeIdInput, AreaCommitmentTypeWineVariantInput, AreaCommitmentTypeWineAttributeInput, AreaCommitmentTypeIdInput, AreaCommitmentTypeWineVariantInput, AreaCommitmentTypeWineAttributeInput,
AreaCommitmentTypeMinKgPerHaInput, AreaCommitmentTypePenaltyPerKgInput, AreaCommitmentTypeMinKgPerHaInput, AreaCommitmentTypePenaltyPerKgInput,
AreaCommitmentTypePenaltyInput, AreaCommitmentTypePenaltyNoneInput, AreaCommitmentTypePenaltyInput, AreaCommitmentTypePenaltyNoneInput,
@@ -69,6 +67,7 @@ namespace Elwig.Windows {
WineCultivationIdInput.IsReadOnly = true; WineCultivationIdInput.IsReadOnly = true;
WineCultivationNameInput.IsReadOnly = true; WineCultivationNameInput.IsReadOnly = true;
WineCultivationDescriptionInput.IsReadOnly = true; WineCultivationDescriptionInput.IsReadOnly = true;
WineCultivationOrganicInput.IsEnabled = false;
AreaCommitmentTypeWineVariantInput.IsEnabled = false; AreaCommitmentTypeWineVariantInput.IsEnabled = false;
AreaCommitmentTypeWineAttributeInput.IsEnabled = false; AreaCommitmentTypeWineAttributeInput.IsEnabled = false;
@@ -117,6 +116,7 @@ namespace Elwig.Windows {
WineCultivationIdInput.IsReadOnly = false; WineCultivationIdInput.IsReadOnly = false;
WineCultivationNameInput.IsReadOnly = false; WineCultivationNameInput.IsReadOnly = false;
WineCultivationDescriptionInput.IsReadOnly = false; WineCultivationDescriptionInput.IsReadOnly = false;
WineCultivationOrganicInput.IsEnabled = true;
AreaCommitmentTypeWineVariantInput.IsEnabled = true; AreaCommitmentTypeWineVariantInput.IsEnabled = true;
AreaCommitmentTypeWineAttributeInput.IsEnabled = true; AreaCommitmentTypeWineAttributeInput.IsEnabled = true;
@@ -189,31 +189,30 @@ namespace Elwig.Windows {
} }
private async Task InitEditing() { private async Task InitEditing() {
EditContext = new AppDbContext(); using var ctx = new AppDbContext();
await BranchesInitEditing(EditContext); await BranchesInitEditing(ctx);
await WineAttributesInitEditing(EditContext); await WineAttributesInitEditing(ctx);
await WineCultivationsInitEditing(EditContext); await WineCultivationsInitEditing(ctx);
await AreaCommitmentTypesInitEditing(EditContext); await AreaCommitmentTypesInitEditing(ctx);
await SeasonsInitEditing(EditContext); await SeasonsInitEditing(ctx);
await ModifiersInitEditing(EditContext); await ModifiersInitEditing(ctx);
} }
private async Task Save() { private async Task Save() {
await UpdateClientParameters(App.Client); await UpdateClientParameters(App.Client);
await UpdateParameters(Utils.CurrentLastSeason); await UpdateParameters(Utils.CurrentLastSeason);
using var tx = await EditContext!.Database.BeginTransactionAsync(); using var ctx = new AppDbContext();
await BranchesSave(EditContext!); using var tx = await ctx.Database.BeginTransactionAsync();
await WineAttributesSave(EditContext!); await BranchesSave(ctx);
await WineCultivationsSave(EditContext!); await WineAttributesSave(ctx);
await AreaCommitmentTypesSave(EditContext!); await WineCultivationsSave(ctx);
await SeasonsSave(EditContext!); await AreaCommitmentTypesSave(ctx);
await ModifiersSave(EditContext!); await SeasonsSave(ctx);
await ModifiersSave(ctx);
await tx.CommitAsync(); await tx.CommitAsync();
} }
private async Task FinishEditing() { private async Task FinishEditing() {
EditContext?.Dispose();
EditContext = null;
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
await BranchesFinishEditing(ctx); await BranchesFinishEditing(ctx);
await WineAttributesFinishEditing(ctx); await WineAttributesFinishEditing(ctx);
@@ -332,7 +331,8 @@ namespace Elwig.Windows {
ClientBicInput.Text = p.Bic; ClientBicInput.Text = p.Bic;
ClientUstIdNrInput.Text = p.UstIdNr; ClientUstIdNrInput.Text = p.UstIdNr;
ClientLfbisNrInput.Text = p.LfbisNr; ClientLfbisNrInput.Text = p.LfbisNr;
ClientOrganicAuthorityInput.Text = p.OrganicAuthority; ClientOrganicOperatorIdInput.Text = p.OrganicOperatorId;
ClientOrganicAuthorityCodeInput.Text = p.OrganicAuthorityCode;
ClientPhoneNrInput.Text = p.PhoneNr; ClientPhoneNrInput.Text = p.PhoneNr;
ClientFaxNrInput.Text = p.FaxNr; ClientFaxNrInput.Text = p.FaxNr;
ClientEmailAddressInput.Text = p.EmailAddress; ClientEmailAddressInput.Text = p.EmailAddress;
@@ -372,7 +372,8 @@ namespace Elwig.Windows {
p.Bic = string.IsNullOrWhiteSpace(ClientBicInput.Text) ? null : ClientBicInput.Text; p.Bic = string.IsNullOrWhiteSpace(ClientBicInput.Text) ? null : ClientBicInput.Text;
p.UstIdNr = string.IsNullOrWhiteSpace(ClientUstIdNrInput.Text) ? null : ClientUstIdNrInput.Text; p.UstIdNr = string.IsNullOrWhiteSpace(ClientUstIdNrInput.Text) ? null : ClientUstIdNrInput.Text;
p.LfbisNr = string.IsNullOrWhiteSpace(ClientLfbisNrInput.Text) ? null : ClientLfbisNrInput.Text; p.LfbisNr = string.IsNullOrWhiteSpace(ClientLfbisNrInput.Text) ? null : ClientLfbisNrInput.Text;
p.OrganicAuthority = string.IsNullOrWhiteSpace(ClientOrganicAuthorityInput.Text) ? null : ClientOrganicAuthorityInput.Text; p.OrganicOperatorId = string.IsNullOrWhiteSpace(ClientOrganicOperatorIdInput.Text) ? null : ClientOrganicOperatorIdInput.Text;
p.OrganicAuthorityCode = string.IsNullOrWhiteSpace(ClientOrganicAuthorityCodeInput.Text) ? null : ClientOrganicAuthorityCodeInput.Text;
p.PhoneNr = string.IsNullOrWhiteSpace(ClientPhoneNrInput.Text) ? null : ClientPhoneNrInput.Text; p.PhoneNr = string.IsNullOrWhiteSpace(ClientPhoneNrInput.Text) ? null : ClientPhoneNrInput.Text;
p.FaxNr = string.IsNullOrWhiteSpace(ClientFaxNrInput.Text) ? null : ClientFaxNrInput.Text; p.FaxNr = string.IsNullOrWhiteSpace(ClientFaxNrInput.Text) ? null : ClientFaxNrInput.Text;
p.EmailAddress = string.IsNullOrWhiteSpace(ClientEmailAddressInput.Text) ? null : ClientEmailAddressInput.Text; p.EmailAddress = string.IsNullOrWhiteSpace(ClientEmailAddressInput.Text) ? null : ClientEmailAddressInput.Text;
+15 -7
View File
@@ -398,7 +398,8 @@
<Grid Grid.Row="1" Grid.Column="2"> <Grid Grid.Row="1" Grid.Column="2">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="0.625*"/> <RowDefinition Height="0.625*"/>
<RowDefinition Height="0.875*"/> <RowDefinition Height="0.8125*"/>
<RowDefinition Height="0.0625*"/>
<RowDefinition Height="0.875*"/> <RowDefinition Height="0.875*"/>
<RowDefinition Height="0.25*"/> <RowDefinition Height="0.25*"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
@@ -434,7 +435,7 @@
</Grid> </Grid>
</GroupBox> </GroupBox>
<GroupBox Header="Lieferung" Grid.Column="0" Grid.Row="1" Grid.RowSpan="3" Margin="5,5,5,5"> <GroupBox Header="Lieferung" Grid.Column="0" Grid.Row="1" Grid.RowSpan="4" Margin="5,5,5,5">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/> <ColumnDefinition Width="100"/>
@@ -553,7 +554,7 @@
</Grid> </Grid>
</GroupBox> </GroupBox>
<GroupBox Header="Menge" Grid.Column="1" Grid.Row="2" Margin="5,5,5,5"> <GroupBox Header="Menge" Grid.Column="1" Grid.Row="2" Grid.RowSpan="2" Margin="5,5,5,5">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/> <ColumnDefinition Width="70"/>
@@ -565,16 +566,23 @@
TextChanged="WeightInput_TextChanged" TextChanged="WeightInput_TextChanged"
Grid.Column="1" Width="70" Margin="0,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/> Grid.Column="1" Width="70" Margin="0,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Label Content="Tara:" Margin="10,40,10,10"/>
<TextBlock x:Name="Tare" Text="{Binding TareString}" Width="40"
Grid.Column="1" Margin="30,44,10,10" TextAlignment="Center" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<CheckBox x:Name="ManualWeighingInput" IsChecked="{Binding IsManualWeighing, Mode=TwoWay}" <CheckBox x:Name="ManualWeighingInput" IsChecked="{Binding IsManualWeighing, Mode=TwoWay}"
Content="Handwiegung" IsEnabled="False" Content="Handwiegung" IsEnabled="False"
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,45,10,10" Grid.Column="0" Grid.ColumnSpan="2" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,75,10,10" Grid.Column="0" Grid.ColumnSpan="2"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/> Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
<CheckBox x:Name="GerebeltGewogenInput" IsChecked="{Binding IsNetWeightValue, Mode=TwoWay}" <CheckBox x:Name="GerebeltGewogenInput" IsChecked="{Binding IsNetWeightValue, Mode=TwoWay}"
Content="Gerebelt gewogen" IsThreeState="True" Content="Gerebelt gewogen" IsThreeState="True"
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,75,10,10" Grid.Column="0" Grid.ColumnSpan="2" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,100,10,10" Grid.Column="0" Grid.ColumnSpan="2"
Checked="GerebeltGewogenInput_Changed" Unchecked="GerebeltGewogenInput_Changed" Indeterminate="GerebeltGewogenInput_Changed"/> Checked="GerebeltGewogenInput_Changed" Unchecked="GerebeltGewogenInput_Changed" Indeterminate="GerebeltGewogenInput_Changed"/>
<Button x:Name="CorrectTareButton" Content="Tara korrigieren" Width="120" Visibility="Hidden"
Click="CorrectTareButton_Click"
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,10,10,10" Grid.Column="2"/>
<Button x:Name="WeighingAButton" Content="Wiegen A" Width="120" <Button x:Name="WeighingAButton" Content="Wiegen A" Width="120"
Click="WeighingButton_Click" Click="WeighingButton_Click"
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,10,10,10" Grid.Column="2"/> VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,10,10,10" Grid.Column="2"/>
@@ -593,7 +601,7 @@
</Grid> </Grid>
</GroupBox> </GroupBox>
<GroupBox Header="Sonstiges" Grid.Column="1" Grid.Row="3" Grid.RowSpan="2" Margin="5,5,5,10"> <GroupBox Header="Sonstiges" Grid.Column="1" Grid.Row="4" Grid.RowSpan="2" Margin="5,5,5,10">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/> <ColumnDefinition Width="100"/>
@@ -646,7 +654,7 @@
</Grid> </Grid>
</GroupBox> </GroupBox>
<GroupBox Header="Herkunft" Grid.Column="0" Grid.Row="4" Margin="5,5,5,10"> <GroupBox Header="Herkunft" Grid.Column="0" Grid.Row="5" Margin="5,5,5,10">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/> <ColumnDefinition Width="100"/>
+55 -17
View File
@@ -9,6 +9,7 @@ using Microsoft.EntityFrameworkCore;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.Json.Nodes;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
@@ -85,10 +86,10 @@ namespace Elwig.Windows {
if (n < 3) WeighingCButton.Visibility = Visibility.Hidden; if (n < 3) WeighingCButton.Visibility = Visibility.Hidden;
if (n < 4) WeighingDButton.Visibility = Visibility.Hidden; if (n < 4) WeighingDButton.Visibility = Visibility.Hidden;
if (n == 1) WeighingAButton.Content = "Wiegen"; if (n == 1) WeighingAButton.Content = "Wiegen";
if (n > 1) WeighingAButton.Content = $"Wiegen {App.CommandScales[0].ScaleId}"; if (n > 1) WeighingAButton.Content = $"Wiegen {App.CommandScales[0].ScaleTerminalId}";
if (n >= 2) WeighingBButton.Content = $"Wiegen {App.CommandScales[1].ScaleId}"; if (n >= 2) WeighingBButton.Content = $"Wiegen {App.CommandScales[1].ScaleTerminalId}";
if (n >= 3) WeighingCButton.Content = $"Wiegen {App.CommandScales[2].ScaleId}"; if (n >= 3) WeighingCButton.Content = $"Wiegen {App.CommandScales[2].ScaleTerminalId}";
if (n >= 4) WeighingDButton.Content = $"Wiegen {App.CommandScales[3].ScaleId}"; if (n >= 4) WeighingDButton.Content = $"Wiegen {App.CommandScales[3].ScaleTerminalId}";
WeighingManualButton.Margin = new Thickness(10, 10 + n * 32, 10, 10); WeighingManualButton.Margin = new Thickness(10, 10 + n * 32, 10, 10);
foreach (var s in App.EventScales) { foreach (var s in App.EventScales) {
s.WeighingEvent += Scale_Weighing; s.WeighingEvent += Scale_Weighing;
@@ -103,6 +104,7 @@ namespace Elwig.Windows {
WeighingBButton.Visibility = Visibility.Hidden; WeighingBButton.Visibility = Visibility.Hidden;
WeighingCButton.Visibility = Visibility.Hidden; WeighingCButton.Visibility = Visibility.Hidden;
WeighingDButton.Visibility = Visibility.Hidden; WeighingDButton.Visibility = Visibility.Hidden;
DisableWeighingButtons();
} }
Menu_Statistics_WineQuality_ModeOe.IsChecked = false; Menu_Statistics_WineQuality_ModeOe.IsChecked = false;
@@ -126,7 +128,6 @@ namespace Elwig.Windows {
this() { this() {
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
ViewModel.FilterMember = ctx.Members.Find(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value"); ViewModel.FilterMember = ctx.Members.Find(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
ViewModel.Title = $"Lieferungen - {ViewModel.FilterMember.AdministrativeName} - Elwig";
ViewModel.EnableAllSeasons = true; ViewModel.EnableAllSeasons = true;
} }
@@ -139,7 +140,7 @@ namespace Elwig.Windows {
if (await ctx.FetchSeasons(Utils.CurrentYear).SingleOrDefaultAsync() == null) { if (await ctx.FetchSeasons(Utils.CurrentYear).SingleOrDefaultAsync() == null) {
InteractionService.ShowWarning("Saison noch nicht erstellt", 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" + "Die Saison für das aktuelle Jahr wurde noch nicht erstellt. Neue Lieferungen können nicht abgespeichert werden.\n\n" +
"(Stammdaten -> Saisons -> Neu anlegen...)"); "(Stammdaten \u2192 Saisons \u2192 Neu anlegen...)");
} }
} }
} }
@@ -366,7 +367,7 @@ namespace Elwig.Windows {
private void InitialInputs() { private void InitialInputs() {
ViewModel.LastScaleError = null; ViewModel.LastScaleError = null;
ViewModel.WeighingData = null; ViewModel.WeighingData = null;
ViewModel.ScaleId = null; ViewModel.ScaleTerminalId = null;
ViewModel.ManualWeighingReason = null; ViewModel.ManualWeighingReason = null;
ClearOriginalValues(); ClearOriginalValues();
@@ -419,9 +420,9 @@ namespace Elwig.Windows {
var cursor = Mouse.OverrideCursor != null; var cursor = Mouse.OverrideCursor != null;
if (!cursor) Mouse.OverrideCursor = Cursors.Wait; if (!cursor) Mouse.OverrideCursor = Cursors.Wait;
var query = (vm.SearchQuery, vm.FilterSeason, vm.FilterAllSeasons, vm.FilterTodayOnly); var query = (vm.SearchQuery, vm.FilterSeason, vm.FilterAllSeasons, vm.FilterTodayOnly);
var (filter, deliveries, deliveryPartsNum, varieties, members, stat) = await Task.Run(async () => { var (filterNames, filter, deliveries, deliveryPartsNum, varieties, members, stat) = await Task.Run(async () => {
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
var (_, deliveryQuery, deliveryPartsQuery, predicate, filter) = await vm.GetFilters(ctx); var (filterNames, deliveryQuery, deliveryPartsQuery, predicate, filter) = await vm.GetFilters(ctx);
var deliveries = await deliveryQuery var deliveries = await deliveryQuery
.Include(d => d.Parts).ThenInclude(p => p.PartModifiers).ThenInclude(m => m.Modifier) .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.Variety)
@@ -450,11 +451,13 @@ namespace Elwig.Windows {
var members = await deliveryQuery.Select(d => d.Member).Distinct().IgnoreAutoIncludes().ToListAsync(); var members = await deliveryQuery.Select(d => d.Member).Distinct().IgnoreAutoIncludes().ToListAsync();
var stat = await DeliveryService.GenerateToolTipData(deliveryPartsQuery); var stat = await DeliveryService.GenerateToolTipData(deliveryPartsQuery);
return (filter, deliveries, deliveryPartsNum, varieties, members, stat); return (filterNames, filter, deliveries, deliveryPartsNum, varieties, members, stat);
}); });
if (!cursor) Mouse.OverrideCursor = null; if (!cursor) Mouse.OverrideCursor = null;
if (query != (ViewModel.SearchQuery, ViewModel.FilterSeason, ViewModel.FilterAllSeasons, ViewModel.FilterTodayOnly)) return; if (query != (ViewModel.SearchQuery, ViewModel.FilterSeason, ViewModel.FilterAllSeasons, ViewModel.FilterTodayOnly)) return;
if (!ViewModel.IsReceipt)
ViewModel.Title = Utils.GetTitle("Lieferungen", filterNames, filter);
ControlUtils.RenewItemsSource(DeliveryList, deliveries, ControlUtils.RenewItemsSource(DeliveryList, deliveries,
DeliveryList_SelectionChanged, ViewModel.TextFilter.Count > 0 ? ControlUtils.RenewSourceDefault.IfOnly : ControlUtils.RenewSourceDefault.None, !updateSort); DeliveryList_SelectionChanged, ViewModel.TextFilter.Count > 0 ? ControlUtils.RenewSourceDefault.IfOnly : ControlUtils.RenewSourceDefault.None, !updateSort);
await RefreshDeliveryParts(); await RefreshDeliveryParts();
@@ -488,7 +491,6 @@ namespace Elwig.Windows {
return; return;
} }
ViewModel.FilterMember = m; ViewModel.FilterMember = m;
ViewModel.Title = $"Lieferungen - {ViewModel.FilterMember.AdministrativeName} - Elwig";
} }
int year = 0; int year = 0;
@@ -612,14 +614,34 @@ namespace Elwig.Windows {
ClearOriginalValues(); ClearOriginalValues();
ClearDefaultValues(); ClearDefaultValues();
ViewModel.FillInputs(p); ViewModel.FillInputs(p);
CorrectTareButton.Visibility = !ViewModel.IsReceipt && ((ViewModel.ActualTare is int t && t != 0) || ViewModel.IsUnloadingBox) ? Visibility.Visible : Visibility.Hidden;
FinishInputFilling(); FinishInputFilling();
} }
new protected void ClearInputs(bool validate = false) { new protected void ClearInputs(bool validate = false) {
ViewModel.ClearInputs(); ViewModel.ClearInputs();
CorrectTareButton.Visibility = Visibility.Hidden;
base.ClearInputs(validate); base.ClearInputs(validate);
} }
private void CorrectTareButton_Click(object sender, RoutedEventArgs evt) {
var lastCorrection = (ViewModel.TargetTare ?? ViewModel.ActualTare ?? 0) - (ViewModel.ActualTare ?? 0);
var correction = Utils.ShowTareCorrectionDialog(ViewModel.ActualTare, ViewModel.TargetTare);
if (correction == null) return;
ViewModel.Weight += lastCorrection - correction;
if (ViewModel.WeighingData is string s) {
ViewModel.TargetTare = correction == 0 ? null : (ViewModel.ActualTare ?? 0) + correction;
ViewModel.Tare = ViewModel.TargetTare ?? ViewModel.ActualTare;
var obj = JsonNode.Parse(s)!.AsObject();
if (correction == 0) {
obj.Remove("tare_correction");
} else {
obj["tare_correction"] = correction;
}
ViewModel.WeighingData = obj.ToJsonString(Utils.JsonOpts);
}
}
private void WeighingButton_Click(object sender, RoutedEventArgs evt) { private void WeighingButton_Click(object sender, RoutedEventArgs evt) {
int index = Array.IndexOf(WeighingButtons, sender as Button); int index = Array.IndexOf(WeighingButtons, sender as Button);
if (index >= 0) WeighingButton_Click(index); if (index >= 0) WeighingButton_Click(index);
@@ -651,13 +673,19 @@ namespace Elwig.Windows {
private void OnWeighingResult(IScale scale, WeighingResult res) { private void OnWeighingResult(IScale scale, WeighingResult res) {
if ((res.NetWeight ?? 0) > 0 && res.FullWeighingId != null) { if ((res.NetWeight ?? 0) > 0 && res.FullWeighingId != null) {
ViewModel.Weight = res.NetWeight; ViewModel.Weight = res.NetWeight;
ViewModel.ScaleId = scale.ScaleId; ViewModel.ActualTare = res.TareWeight;
ViewModel.TargetTare = null;
ViewModel.Tare = ViewModel.ActualTare is int t && t != 0 ? t : null;
ViewModel.ScaleTerminalId = scale.ScaleTerminalId;
ViewModel.WeighingData = res.ToJson().ToJsonString(); ViewModel.WeighingData = res.ToJson().ToJsonString();
ViewModel.ManualWeighingReason = null; ViewModel.ManualWeighingReason = null;
ManualWeighingInput.IsChecked = false; ManualWeighingInput.IsChecked = false;
} else { } else {
ViewModel.Weight = null; ViewModel.Weight = null;
ViewModel.ScaleId = null; ViewModel.ActualTare = null;
ViewModel.TargetTare = null;
ViewModel.Tare = null;
ViewModel.ScaleTerminalId = null;
ViewModel.WeighingData = null; ViewModel.WeighingData = null;
} }
ViewModel.LastScaleError = null; ViewModel.LastScaleError = null;
@@ -685,7 +713,7 @@ namespace Elwig.Windows {
private async void TodayOnlyInput_Changed(object sender, RoutedEventArgs evt) { private async void TodayOnlyInput_Changed(object sender, RoutedEventArgs evt) {
if (!HasContextLoaded) return; if (!HasContextLoaded) return;
if (TodayOnlyInput.IsChecked == true && AllSeasonsInput.IsChecked == false) { if (TodayOnlyInput.IsChecked == true && AllSeasonsInput.IsChecked == false) {
ViewModel.FilterSeason = Utils.Today.Year; ViewModel.FilterSeason = DateTime.Today.Year;
ViewModel.FilterTodayOnly = true; ViewModel.FilterTodayOnly = true;
} }
await RefreshList(); await RefreshList();
@@ -771,7 +799,7 @@ namespace Elwig.Windows {
} }
private void EmptyScale() { 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; if (scale is not ICommandScale cs) return;
cs.Empty(); cs.Empty();
} }
@@ -953,7 +981,7 @@ namespace Elwig.Windows {
ViewModel.Weight = res?.Item1; ViewModel.Weight = res?.Item1;
ManualWeighingInput.IsChecked = true; ManualWeighingInput.IsChecked = true;
ViewModel.ManualWeighingReason = res?.Item2; ViewModel.ManualWeighingReason = res?.Item2;
ViewModel.ScaleId = null; ViewModel.ScaleTerminalId = null;
ViewModel.WeighingData = null; ViewModel.WeighingData = null;
} }
@@ -971,6 +999,8 @@ namespace Elwig.Windows {
DeliveryList.IsEnabled = false; DeliveryList.IsEnabled = false;
DeliveryPartList.IsEnabled = false; DeliveryPartList.IsEnabled = false;
if (!ViewModel.IsReceipt)
EnableWeighingButtons();
HideNewEditDeleteButtons(); HideNewEditDeleteButtons();
ShowSaveResetCancelButtons(); ShowSaveResetCancelButtons();
UnlockInputs(); UnlockInputs();
@@ -1034,6 +1064,8 @@ namespace Elwig.Windows {
DeliveryList.IsEnabled = true; DeliveryList.IsEnabled = true;
DeliveryPartList.IsEnabled = true; DeliveryPartList.IsEnabled = true;
if (!ViewModel.IsReceipt)
DisableWeighingButtons();
HideSaveResetCancelButtons(); HideSaveResetCancelButtons();
ShowNewEditDeleteButtons(); ShowNewEditDeleteButtons();
LockInputs(); LockInputs();
@@ -1067,6 +1099,8 @@ namespace Elwig.Windows {
DeliveryList.IsEnabled = true; DeliveryList.IsEnabled = true;
DeliveryPartList.IsEnabled = true; DeliveryPartList.IsEnabled = true;
if (!ViewModel.IsReceipt)
DisableWeighingButtons();
HideSaveResetCancelButtons(); HideSaveResetCancelButtons();
ShowNewEditDeleteButtons(); ShowNewEditDeleteButtons();
RefreshInputs(); RefreshInputs();
@@ -1171,6 +1205,7 @@ namespace Elwig.Windows {
} }
private void DisableWeighingButtons() { private void DisableWeighingButtons() {
CorrectTareButton.IsEnabled = false;
WeighingManualButton.IsEnabled = false; WeighingManualButton.IsEnabled = false;
WeighingAButton.IsEnabled = false; WeighingAButton.IsEnabled = false;
WeighingBButton.IsEnabled = false; WeighingBButton.IsEnabled = false;
@@ -1179,6 +1214,7 @@ namespace Elwig.Windows {
} }
private void EnableWeighingButtons() { private void EnableWeighingButtons() {
CorrectTareButton.IsEnabled = true;
WeighingManualButton.IsEnabled = true; WeighingManualButton.IsEnabled = true;
var n = App.CommandScales.Count; var n = App.CommandScales.Count;
WeighingAButton.IsEnabled = n > 0 && App.CommandScales[0].IsReady; WeighingAButton.IsEnabled = n > 0 && App.CommandScales[0].IsReady;
@@ -1373,13 +1409,15 @@ namespace Elwig.Windows {
private void UnloadingInput_Checked(object sender, RoutedEventArgs evt) { private void UnloadingInput_Checked(object sender, RoutedEventArgs evt) {
if (!IsEditing && !IsCreating) return; if (!IsEditing && !IsCreating) return;
if (!ViewModel.IsReceipt)
CorrectTareButton.Visibility = (ViewModel.ActualTare is int t && t != 0) || ViewModel.IsUnloadingBox ? Visibility.Visible : Visibility.Hidden;
var mod = ViewModel.Modifiers.ToList(); var mod = ViewModel.Modifiers.ToList();
var source = ViewModel.ModifiersSource.ToList(); var source = ViewModel.ModifiersSource.ToList();
if (App.Client.IsMatzen) { if (App.Client.IsMatzen) {
var kl = mod.Where(m => m.Name.StartsWith("Klasse ")).Select(m => m.ModId).LastOrDefault("_")[0]; var kl = mod.Where(m => m.Name.StartsWith("Klasse ")).Select(m => m.ModId).LastOrDefault("_")[0];
if (ViewModel.IsUnloadingPumped && (kl == 'A' || kl == '_')) { if (ViewModel.IsUnloadingPumped && (kl == 'A' || kl == '_')) {
kl = 'B'; kl = 'B';
} else if (ViewModel.IsUnloadingDumper && kl == '_') { } else if (ViewModel.IsUnloadingDumper && (kl == 'B' || kl == '_')) {
kl = 'A'; kl = 'A';
} else { } else {
kl = '_'; kl = '_';
+1 -1
View File
@@ -8,7 +8,7 @@
xmlns:local="clr-namespace:Elwig.Windows" xmlns:local="clr-namespace:Elwig.Windows"
xmlns:vm="clr-namespace:Elwig.ViewModels" xmlns:vm="clr-namespace:Elwig.ViewModels"
xmlns:ctrl="clr-namespace:Elwig.Controls" xmlns:ctrl="clr-namespace:Elwig.Controls"
Title="Anmeldungen - Elwig" Height="700" Width="980" MinWidth="600" MinHeight="400"> Title="{Binding Title}" Height="700" Width="980" MinWidth="600" MinHeight="400">
<Window.DataContext> <Window.DataContext>
<vm:DeliveryAncmtAdminViewModel/> <vm:DeliveryAncmtAdminViewModel/>
</Window.DataContext> </Window.DataContext>
@@ -91,8 +91,9 @@ namespace Elwig.Windows {
.ToListAsync(); .ToListAsync();
list.ForEach(v => v.Schedule.AnnouncedWeightOverride = v.AnnouncedWeight); list.ForEach(v => v.Schedule.AnnouncedWeightOverride = v.AnnouncedWeight);
var deliverySchedules = list.Select(v => v.Schedule).ToList(); var deliverySchedules = list.Select(v => v.Schedule).ToList();
var today = DateTime.Today;
ControlUtils.RenewItemsSource(DeliveryScheduleList, deliverySchedules ControlUtils.RenewItemsSource(DeliveryScheduleList, deliverySchedules
.Where(s => !ViewModel.FilterOnlyUpcoming || s.DateString.CompareTo(Utils.Today.ToString("yyyy-MM-dd")) >= 0) .Where(s => !ViewModel.FilterOnlyUpcoming || s.DateString.CompareTo(today.ToString("yyyy-MM-dd")) >= 0)
.ToList(), DeliveryScheduleList_SelectionChanged, ViewModel.FilterFromAllSchedules ? ControlUtils.RenewSourceDefault.None : ControlUtils.RenewSourceDefault.First); .ToList(), DeliveryScheduleList_SelectionChanged, ViewModel.FilterFromAllSchedules ? ControlUtils.RenewSourceDefault.None : ControlUtils.RenewSourceDefault.First);
ControlUtils.RenewItemsSource(DeliveryScheduleInput, deliverySchedules, DeliveryScheduleInput_SelectionChanged); ControlUtils.RenewItemsSource(DeliveryScheduleInput, deliverySchedules, DeliveryScheduleInput_SelectionChanged);
} }
@@ -102,9 +103,9 @@ namespace Elwig.Windows {
var cursor = Mouse.OverrideCursor != null; var cursor = Mouse.OverrideCursor != null;
if (!cursor) Mouse.OverrideCursor = Cursors.Wait; if (!cursor) Mouse.OverrideCursor = Cursors.Wait;
var query = (vm.SearchQuery, vm.FilterSeason, vm.FilterOnlyUpcoming, vm.FilterOnlyUpcoming); var query = (vm.SearchQuery, vm.FilterSeason, vm.FilterOnlyUpcoming, vm.FilterOnlyUpcoming);
var (filter, deliveryAncmts, stat) = await Task.Run(async () => { var (filterNames, filter, deliveryAncmts, stat) = await Task.Run(async () => {
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
var (_, deliveryAncmtQuery, filter) = await vm.GetFilters(ctx); var (filterNames, deliveryAncmtQuery, filter) = await vm.GetFilters(ctx);
var deliveryAncmts = await deliveryAncmtQuery.ToListAsync(); var deliveryAncmts = await deliveryAncmtQuery.ToListAsync();
if (filter.Count > 0 && deliveryAncmts.Count > 0) { if (filter.Count > 0 && deliveryAncmts.Count > 0) {
@@ -129,11 +130,12 @@ namespace Elwig.Windows {
var stat = await DeliveryAncmtService.GenerateToolTipData(deliveryAncmtQuery); var stat = await DeliveryAncmtService.GenerateToolTipData(deliveryAncmtQuery);
return (filter, deliveryAncmts, stat); return (filterNames, filter, deliveryAncmts, stat);
}); });
if (!cursor) Mouse.OverrideCursor = null; if (!cursor) Mouse.OverrideCursor = null;
if (query != (ViewModel.SearchQuery, ViewModel.FilterSeason, ViewModel.FilterOnlyUpcoming, ViewModel.FilterOnlyUpcoming)) return; if (query != (ViewModel.SearchQuery, ViewModel.FilterSeason, ViewModel.FilterOnlyUpcoming, ViewModel.FilterOnlyUpcoming)) return;
ViewModel.Title = Utils.GetTitle("Anmeldungen", filterNames, filter);
ControlUtils.RenewItemsSource(DeliveryAncmtList, deliveryAncmts, ControlUtils.RenewItemsSource(DeliveryAncmtList, deliveryAncmts,
DeliveryAncmtList_SelectionChanged, ViewModel.TextFilter.Count > 0 ? ControlUtils.RenewSourceDefault.IfOnly : ControlUtils.RenewSourceDefault.None, !updateSort); DeliveryAncmtList_SelectionChanged, ViewModel.TextFilter.Count > 0 ? ControlUtils.RenewSourceDefault.IfOnly : ControlUtils.RenewSourceDefault.None, !updateSort);
if (updateSort && DeliveryAncmtList.SelectedItem != null) if (updateSort && DeliveryAncmtList.SelectedItem != null)
+6 -3
View File
@@ -108,7 +108,7 @@ namespace Elwig.Windows {
public MailWindow(int? year = null) { public MailWindow(int? year = null) {
InitializeComponent(); InitializeComponent();
using (var ctx = new AppDbContext()) { using (var ctx = new AppDbContext()) {
Year = year ?? ctx.Seasons.OrderByDescending(s => s.Year).FirstOrDefault()?.Year ?? Utils.Today.Year; Year = year ?? ctx.Seasons.OrderByDescending(s => s.Year).FirstOrDefault()?.Year ?? DateTime.Today.Year;
Title = $"Rundschreiben - Lese {Year} - Elwig"; Title = $"Rundschreiben - Lese {Year} - Elwig";
} }
@@ -150,7 +150,7 @@ namespace Elwig.Windows {
PostalSender1.Text = App.Client.Sender1; PostalSender1.Text = App.Client.Sender1;
PostalSender2.Text = App.Client.Sender2; PostalSender2.Text = App.Client.Sender2;
PostalLocation.Text = App.BranchLocation; PostalLocation.Text = App.BranchLocation;
PostalDate.Text = $"{Utils.Today:dd.MM.yyyy}"; PostalDate.Text = $"{DateTime.Today:dd.MM.yyyy}";
EmailSubjectInput.Text = App.Client.TextEmailSubject ?? "Rundschreiben"; EmailSubjectInput.Text = App.Client.TextEmailSubject ?? "Rundschreiben";
EmailBodyInput.Text = App.Client.TextEmailBody ?? "Sehr geehrtes Mitglied,\n\nim Anhang finden Sie das aktuelle Rundschreiben.\n\nIhre Winzergenossenschaft\n"; EmailBodyInput.Text = App.Client.TextEmailBody ?? "Sehr geehrtes Mitglied,\n\nim Anhang finden Sie das aktuelle Rundschreiben.\n\nIhre Winzergenossenschaft\n";
} }
@@ -438,7 +438,7 @@ namespace Elwig.Windows {
private void Date_LostFocus(object sender, RoutedEventArgs evt) { private void Date_LostFocus(object sender, RoutedEventArgs evt) {
var res = Validator.CheckDate((TextBox)sender, true); var res = Validator.CheckDate((TextBox)sender, true);
if (!res.IsValid) ((TextBox)sender).Text = $"{Utils.Today:dd.MM.yyyy}"; if (!res.IsValid) ((TextBox)sender).Text = $"{DateTime.Today:dd.MM.yyyy}";
} }
private async Task UpdateRecipients(AppDbContext ctx) { private async Task UpdateRecipients(AppDbContext ctx) {
@@ -937,6 +937,7 @@ namespace Elwig.Windows {
Mouse.OverrideCursor = Cursors.Wait; Mouse.OverrideCursor = Cursors.Wait;
var subject = EmailSubjectInput.Text; var subject = EmailSubjectInput.Text;
var text = EmailBodyInput.Text; var text = EmailBodyInput.Text;
var date = DateTime.Now;
await Task.Run(async () => { await Task.Run(async () => {
await Utils.AddSentMailBody(subject, text, EmailDocuments.Count); await Utils.AddSentMailBody(subject, text, EmailDocuments.Count);
foreach (var (m, docs) in EmailDocuments) { foreach (var (m, docs) in EmailDocuments) {
@@ -944,6 +945,7 @@ namespace Elwig.Windows {
msg.From.Add(new MailboxAddress(App.Client.NameFull, App.Config.Smtp.Value.From)); msg.From.Add(new MailboxAddress(App.Client.NameFull, App.Config.Smtp.Value.From));
msg.To.AddRange(m.EmailAddresses.OrderBy(a => a.Nr).Select(a => new MailboxAddress(m.AdministrativeName, a.Address))); msg.To.AddRange(m.EmailAddresses.OrderBy(a => a.Nr).Select(a => new MailboxAddress(m.AdministrativeName, a.Address)));
msg.Subject = subject; msg.Subject = subject;
msg.Date = date;
var body = new Multipart("mixed") { var body = new Multipart("mixed") {
new TextPart("plain") { Text = text } new TextPart("plain") { Text = text }
}; };
@@ -958,6 +960,7 @@ namespace Elwig.Windows {
m.EmailAddresses.OrderBy(a => a.Nr).Select(a => a.Address).ToArray(), m.EmailAddresses.OrderBy(a => a.Nr).Select(a => a.Address).ToArray(),
subject, docs.Select(d => d.Title).ToArray() subject, docs.Select(d => d.Title).ToArray()
)]); )]);
await Task.Delay((int)(App.Config.SmtpWait * 1000));
} }
}); });
+39 -29
View File
@@ -50,22 +50,27 @@ namespace Elwig.Windows {
_syncTimer.Start(); _syncTimer.Start();
} }
private void Window_Closing(object sender, CancelEventArgs evt) { private async void Window_Closing(object sender, CancelEventArgs evt) {
evt.Cancel = !App.ForceShutdown;
if (App.NumWindows > 1 && !App.ForceShutdown) { if (App.NumWindows > 1 && !App.ForceShutdown) {
foreach (var w in App.Current.Windows.Cast<Window>().Where(w => ((w as AdministrationWindow)?.IsEditing ?? false) || ((w as AdministrationWindow)?.IsCreating ?? false))) { foreach (var w in Application.Current.Windows.Cast<Window>().Where(w => ((w as AdministrationWindow)?.IsEditing ?? false) || ((w as AdministrationWindow)?.IsCreating ?? false))) {
try { try {
w.Close(); w.Close();
} catch { } } catch { }
} }
Thread.Sleep(100); Thread.Sleep(100);
if (App.NumWindows > 1 && !App.Current.Windows.Cast<Window>().Any(w => ((w as AdministrationWindow)?.IsEditing ?? false) || ((w as AdministrationWindow)?.IsCreating ?? false))) { if (App.NumWindows > 1 && !Application.Current.Windows.Cast<Window>().Any(w => ((w as AdministrationWindow)?.IsEditing ?? false) || ((w as AdministrationWindow)?.IsCreating ?? false))) {
if (!InteractionService.AskConfirmation("Elwig beenden", "Es sind noch weitere Fenster geöffnet.\nSollen alle Fenster geschlossen werden?")) { if (!InteractionService.AskConfirmation("Elwig beenden", "Es sind noch weitere Fenster geöffnet.\nSollen alle Fenster geschlossen werden?")) {
evt.Cancel = true; return;
} else {
Application.Current.Shutdown();
} }
App.ForceShutdown = true;
} }
} }
Mouse.OverrideCursor = Cursors.AppStarting;
await (Application.Current as App)!.OnClosing();
Application.Current.Shutdown();
} }
private void Menu_Help_About_Click(object sender, RoutedEventArgs evt) { private void Menu_Help_About_Click(object sender, RoutedEventArgs evt) {
@@ -162,7 +167,7 @@ namespace Elwig.Windows {
private async void Menu_Database_Backup_Click(object sender, RoutedEventArgs evt) { private async void Menu_Database_Backup_Click(object sender, RoutedEventArgs evt) {
try { try {
var filename = InteractionService.SaveFile("Datenbank-Sicherung", $"database_{Utils.Today:yyyy-MM-dd}", "sql.zip"); var filename = InteractionService.SaveFile("Datenbank-Sicherung", $"database_{DateTime.Today:yyyy-MM-dd}", "sql.zip");
if (filename != null) { if (filename != null) {
if (!filename.EndsWith(".sql.zip")) filename += ".sql.zip"; if (!filename.EndsWith(".sql.zip")) filename += ".sql.zip";
Mouse.OverrideCursor = Cursors.Wait; Mouse.OverrideCursor = Cursors.Wait;
@@ -177,22 +182,7 @@ namespace Elwig.Windows {
} }
private async void Menu_Database_Restore_Click(object sender, RoutedEventArgs evt) { private async void Menu_Database_Restore_Click(object sender, RoutedEventArgs evt) {
try { await BackupService.RestoreDatabase();
var d = new OpenFileDialog() {
Title = "Datenbank wiederherstellen - Elwig",
DefaultExt = "sql.zip",
Filter = "SQLite-Datenbank (*.sqlite3, *.sqlite3.zip, *.sql, *.sql.zip)|*.sqlite3;*.sqlite3.zip;*.sql;*.sql.zip",
};
if (d.ShowDialog() == true) {
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) {
InteractionService.ShowException(exc);
}
Mouse.OverrideCursor = null;
} }
private async void SyncButton_Click(object sender, RoutedEventArgs evt) { private async void SyncButton_Click(object sender, RoutedEventArgs evt) {
@@ -200,8 +190,13 @@ namespace Elwig.Windows {
return; return;
Mouse.OverrideCursor = Cursors.Wait; Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => { await Task.Run(async () => {
await SyncService.Download(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword); if (App.Config.SyncMain) {
await SyncService.UploadModified(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword); 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; Mouse.OverrideCursor = null;
} }
@@ -211,7 +206,7 @@ namespace Elwig.Windows {
return; return;
Mouse.OverrideCursor = Cursors.Wait; Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => { 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; Mouse.OverrideCursor = null;
} }
@@ -229,6 +224,10 @@ namespace Elwig.Windows {
private async void Menu_Sync_UploadModified_Click(object sender, RoutedEventArgs evt) { private async void Menu_Sync_UploadModified_Click(object sender, RoutedEventArgs evt) {
if (App.Config.SyncUrl == null) if (App.Config.SyncUrl == null)
return; 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; Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => { await Task.Run(async () => {
await SyncService.UploadModified(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword); await SyncService.UploadModified(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
@@ -296,6 +295,11 @@ namespace Elwig.Windows {
if (App.Config.SyncUrl == null) if (App.Config.SyncUrl == null)
return; return;
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!")) 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; return;
@@ -367,7 +371,11 @@ namespace Elwig.Windows {
await Task.Delay(delay); await Task.Delay(delay);
var ch = false; var ch = false;
using (var ctx = new AppDbContext()) { 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(() => { await App.MainDispatcher.BeginInvoke(() => {
if (ch) { if (ch) {
@@ -520,9 +528,11 @@ namespace Elwig.Windows {
App.HintContextChange(); App.HintContextChange();
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
var tbl = await MemberAreaComsData.ForSeason(ctx.MemberAreaComsRows, year); var tbl1 = await MemberAreaComTotalsData.ForSeason(ctx.MemberAreaComTotalsRows, year);
var tbl2 = await MemberAreaComsData.ForSeason(ctx.MemberAreaComsRows, year);
using var ods = new OdsFile(filename); using var ods = new OdsFile(filename);
await ods.AddTable(tbl); await ods.AddTable(tbl1);
await ods.AddTable(tbl2);
} catch (Exception exc) { } catch (Exception exc) {
InteractionService.ShowException(exc); InteractionService.ShowException(exc);
} }
+56 -26
View File
@@ -5,7 +5,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Elwig.Windows" xmlns:local="clr-namespace:Elwig.Windows"
xmlns:vm="clr-namespace:Elwig.ViewModels" xmlns:vm="clr-namespace:Elwig.ViewModels"
Title="Mitglieder - Elwig" Height="700" Width="1250" MinHeight="650" MinWidth="1150"> Title="{Binding Title}" Height="700" Width="1250" MinHeight="650" MinWidth="1150">
<Window.DataContext> <Window.DataContext>
<vm:MemberAdminViewModel/> <vm:MemberAdminViewModel/>
</Window.DataContext> </Window.DataContext>
@@ -547,35 +547,48 @@
<GroupBox Header="Betrieb" Grid.Column="1" Grid.Row="0" Grid.RowSpan="1" Margin="5,5,5,5"> <GroupBox Header="Betrieb" Grid.Column="1" Grid.Row="0" Grid.RowSpan="1" Margin="5,5,5,5">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="90"/> <ColumnDefinition Width="85"/>
<ColumnDefinition Width="150"/> <ColumnDefinition Width="69"/>
<ColumnDefinition/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="55"/>
<ColumnDefinition Width="101"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Label Content="UID:" Margin="10,10,0,0" Grid.Column="0"/> <Label Content="Betriebs-Nr.:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="UstIdNrInput" Text="{Binding UstIdNr, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,10,10,0" Grid.Column="1" Width="96" HorizontalAlignment="Left"
TextChanged="UstIdNrInput_TextChanged" LostFocus="UstIdNrInput_LostFocus"/>
<Label Content="Betriebs-Nr.:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="LfbisNrInput" Text="{Binding LfbisNr, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" <TextBox x:Name="LfbisNrInput" Text="{Binding LfbisNr, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,40,10,0" Grid.Column="1" Width="64" HorizontalAlignment="Left" TextAlignment="Right" Margin="0,10,5,0" Grid.Column="1" Width="64" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="LfbisNrInput_TextChanged" LostFocus="LfbisNrInput_LostFocus"/> TextChanged="LfbisNrInput_TextChanged" LostFocus="LfbisNrInput_LostFocus"/>
<CheckBox x:Name="BuchführendInput" Content="Buchführend" IsChecked="{Binding IsBuchführend, Mode=TwoWay}" IsEnabled="False" <Label Content="UID:" Margin="10,40,0,0" Grid.Column="0" ToolTip="USt-IdNr. / Umsatzsteuer-Identifikationsnummer"/>
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed" <TextBox x:Name="UstIdNrInput" Text="{Binding UstIdNr, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,15,0,0" VerticalAlignment="Top"/> Margin="0,40,5,0" Grid.Column="0" Grid.ColumnSpan="2" Width="96" HorizontalAlignment="Right"
TextChanged="UstIdNrInput_TextChanged" LostFocus="UstIdNrInput_LostFocus"/>
<CheckBox x:Name="OrganicInput" Content="Bio" IsChecked="{Binding IsOrganic, Mode=TwoWay}" IsEnabled="False" <CheckBox x:Name="OrganicInput" Content="Bio" IsChecked="{Binding IsOrganic, Mode=TwoWay}" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed" Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,45,0,0" VerticalAlignment="Top"/> Grid.Column="2" HorizontalAlignment="Left" Margin="5,15,0,0" VerticalAlignment="Top"/>
<Button x:Name="OrganicButton" Content="easy-cert.com" IsEnabled="{Binding IsMemberSelected}"
Height="25" FontSize="12" <CheckBox x:Name="BuchführendInput" Content="Buchführend" IsChecked="{Binding IsBuchführend, Mode=TwoWay}" IsEnabled="False"
Click="OrganicButton_Click" Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="60,40,0,0"/> Grid.Column="2" HorizontalAlignment="Left" Margin="5,45,0,0" VerticalAlignment="Top"/>
<Label Content="Kontrollstelle:" Margin="0,10,5,0" Grid.Column="2" Grid.ColumnSpan="2" HorizontalAlignment="Right" ToolTip="Bio-Kontrollstelle"/>
<TextBox x:Name="OrganicAuthorityCodeInput" Text="{Binding OrganicAuthorityCode, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,10,10,0" Grid.Column="4" Width="91" HorizontalAlignment="Left"
TextChanged="OrganicAuthorityCodeInput_TextChanged" LostFocus="OrganicAuthorityCodeInput_LostFocus"/>
<Label Content="OOC-ID:" Margin="0,40,0,0" Grid.Column="3" HorizontalAlignment="Left" ToolTip="Bio-EU-Operator-ID / Organic Operator Certificate ID"/>
<TextBox x:Name="OrganicOperatorIdInput" Text="{Binding OrganicOperatorId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,40,10,0" Grid.Column="4" Width="91" HorizontalAlignment="Left"
TextChanged="OrganicOperatorIdInput_TextChanged" LostFocus="OrganicOperatorIdInput_LostFocus"/>
</Grid> </Grid>
</GroupBox> </GroupBox>
<GroupBox Header="Rechnungsadresse (optional)" Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" Margin="5,5,5,5"> <GroupBox Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" Margin="5,5,5,5">
<GroupBox.Header>
<TextBlock>Rechnungsadresse (optional)
<Run FontSize="10">für Lieferschein, Anlfrg.-Bstng., Tr.-Gutschr.</Run>
</TextBlock>
</GroupBox.Header>
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="65"/> <ColumnDefinition Width="65"/>
@@ -605,7 +618,7 @@
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/> <ColumnDefinition Width="120"/>
<ColumnDefinition Width="120"/> <ColumnDefinition Width="135"/>
<ColumnDefinition/> <ColumnDefinition/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
@@ -619,7 +632,7 @@
Margin="0,40,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right" Margin="0,40,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="DateInput_TextChanged" LostFocus="DateInput_LostFocus"/> TextChanged="DateInput_TextChanged" LostFocus="DateInput_LostFocus"/>
<Label Content="BH-Konto:" Margin="10,70,0,0" Grid.Column="0"/> <Label Content="BH-Konto:" Margin="10,70,0,0" Grid.Column="0" ToolTip="Buchhaltungskonto"/>
<TextBox x:Name="AccountingNrInput" Text="{Binding AccountingNr, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" <TextBox x:Name="AccountingNrInput" Text="{Binding AccountingNr, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,70,10,0" Width="63" Grid.Column="1" HorizontalAlignment="Left" Margin="0,70,10,0" Width="63" Grid.Column="1" HorizontalAlignment="Left"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
@@ -652,6 +665,11 @@
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed" Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,75,0,0" VerticalAlignment="Top"/> Grid.Column="2" HorizontalAlignment="Left" Margin="10,75,0,0" VerticalAlignment="Top"/>
<Label Content="Bio-Zert.:" Margin="10,100,0,0" Grid.Column="2" HorizontalAlignment="Left"/>
<TextBlock FontSize="14" Margin="70,102,10,0" Grid.Column="2" HorizontalAlignment="Stretch" VerticalAlignment="Top" ToolTip="{Binding OrganicTest4}">
<Run Text="{Binding OrganicTest1}" FontWeight="Bold" Foreground="{Binding OrganicTest3}"/><Run FontSize="12" Text="{Binding OrganicTest2}"/>
</TextBlock>
<Label Content="Stamm-Zwst.:" Margin="10,130,0,0" Grid.Column="0"/> <Label Content="Stamm-Zwst.:" Margin="10,130,0,0" Grid.Column="0"/>
<ComboBox x:Name="BranchInput" SelectedItem="{Binding Branch, Mode=TwoWay}" ItemsSource="{Binding BranchSource, Mode=TwoWay}" <ComboBox x:Name="BranchInput" SelectedItem="{Binding Branch, Mode=TwoWay}" ItemsSource="{Binding BranchSource, Mode=TwoWay}"
DisplayMemberPath="Name" TextSearch.TextPath="Name" DisplayMemberPath="Name" TextSearch.TextPath="Name"
@@ -706,12 +724,24 @@
</TextBlock.ToolTip> </TextBlock.ToolTip>
</TextBlock> </TextBlock>
<Button x:Name="DeliveryButton" Content="Lieferungen" IsEnabled="{Binding IsMemberSelected}" <Button x:Name="DeliveryButton" IsEnabled="{Binding IsMemberSelected}"
Click="DeliveryButton_Click" Click="DeliveryButton_Click"
HorizontalAlignment="Right" Margin="10,00,10,37" Width="150" VerticalAlignment="Bottom" Grid.ColumnSpan="3"/> HorizontalAlignment="Right" Margin="10,00,10,37" Width="160" VerticalAlignment="Bottom" Grid.ColumnSpan="3" HorizontalContentAlignment="Stretch">
<Button x:Name="AreaCommitmentButton" Content="Flächenbindungen" IsEnabled="{Binding IsMemberSelected}" <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" 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> </Grid>
</GroupBox> </GroupBox>
</Grid> </Grid>
+4 -15
View File
@@ -7,7 +7,6 @@ using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
@@ -147,9 +146,9 @@ namespace Elwig.Windows {
var cursor = Mouse.OverrideCursor != null; var cursor = Mouse.OverrideCursor != null;
if (!cursor) Mouse.OverrideCursor = Cursors.Wait; if (!cursor) Mouse.OverrideCursor = Cursors.Wait;
var query = (vm.SearchQuery, vm.ShowOnlyActiveMembers); var query = (vm.SearchQuery, vm.ShowOnlyActiveMembers);
var (members, stat) = await Task.Run(async () => { var (filterNames, members, stat, filter) = await Task.Run(async () => {
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
var (_, memberQuery, filter) = await vm.GetFilters(ctx); var (filterNames, memberQuery, filter) = await vm.GetFilters(ctx);
var members = await memberQuery var members = await memberQuery
.Include(m => m.EmailAddresses) .Include(m => m.EmailAddresses)
.Include(m => m.TelephoneNumbers) .Include(m => m.TelephoneNumbers)
@@ -184,11 +183,12 @@ namespace Elwig.Windows {
SharesActive = g.Sum(m => m.Shares + m.SharesRed + m.SharesWhite), 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(); }).ToListAsync()).Select(g => (g.Count, g.Shares, g.SharesRed, g.SharesWhite, g.SharesDormant, g.SharesTotal, g.SharesActive)).ToList();
return (members, stat[0]); return (filterNames, members, stat[0], filter);
}); });
if (!cursor) Mouse.OverrideCursor = null; if (!cursor) Mouse.OverrideCursor = null;
if (query != (ViewModel.SearchQuery, ViewModel.ShowOnlyActiveMembers)) return; if (query != (ViewModel.SearchQuery, ViewModel.ShowOnlyActiveMembers)) return;
ViewModel.Title = Utils.GetTitle("Mitglieder", filterNames, filter);
ControlUtils.RenewItemsSource(MemberList, members, ControlUtils.RenewItemsSource(MemberList, members,
MemberList_SelectionChanged, ViewModel.TextFilter.Count > 0 ? ControlUtils.RenewSourceDefault.IfOnly : ControlUtils.RenewSourceDefault.None, !updateSort); MemberList_SelectionChanged, ViewModel.TextFilter.Count > 0 ? ControlUtils.RenewSourceDefault.IfOnly : ControlUtils.RenewSourceDefault.None, !updateSort);
if (updateSort && MemberList.SelectedItem != null) if (updateSort && MemberList.SelectedItem != null)
@@ -965,17 +965,6 @@ namespace Elwig.Windows {
} }
} }
private void OrganicButton_Click(object sender, RoutedEventArgs evt) {
if (ViewModel.SelectedMember is not Member m) return;
var url = "https://www.easy-cert.com/htm/suchergebnis.htm?" +
//$"CustomerNumber={m.LfbisNr}&" +
$"Name={(m.BillingAddress?.FullName ?? m.FullName).Replace(' ', '+')}&" +
$"PostalCode={(m.BillingAddress?.PostalDest ?? m.PostalDest).AtPlz?.Plz}";
Process.Start(new ProcessStartInfo(url) {
UseShellExecute = true,
});
}
private void MemberReferenceButton_Click(object sender, RoutedEventArgs evt) { private void MemberReferenceButton_Click(object sender, RoutedEventArgs evt) {
if (ViewModel.SelectedMember is not Member m || m.PredecessorMgNr == null) return; if (ViewModel.SelectedMember is not Member m || m.PredecessorMgNr == null) return;
FocusMember((int)m.PredecessorMgNr); FocusMember((int)m.PredecessorMgNr);
@@ -173,8 +173,11 @@
Margin="0,100,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right" Margin="0,100,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="DateNoticeInput_TextChanged" LostFocus="DateInput_LostFocus"/> TextChanged="DateNoticeInput_TextChanged" LostFocus="DateInput_LostFocus"/>
<CheckBox x:Name="HasCompanySignedInput" IsChecked="{Binding CompanyHasSigned, Mode=TwoWay}" Content="Von WG unterfertigt"
Margin="88,105,10,0" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
<CheckBox x:Name="HasMemberSignedInput" IsChecked="{Binding MemberHasSigned, Mode=TwoWay}" Content="Von Mg. unterfertigt" <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" Margin="88,135,10,0" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/> Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
<Label Content="Wirksam:" Margin="10,130,0,0" Grid.Column="0"/> <Label Content="Wirksam:" Margin="10,130,0,0" Grid.Column="0"/>
@@ -198,7 +198,7 @@ namespace Elwig.Windows {
int mgnr; int mgnr;
try { try {
mgnr = await ViewModel.UpdateMemberHistory(ViewModel.SelectedHistoryEntry?.HistNr, IsEditing || ViewModel.DateNotice >= DateOnly.FromDateTime(Utils.Today).AddDays(-14)); mgnr = await ViewModel.UpdateMemberHistory(ViewModel.SelectedHistoryEntry?.HistNr, IsEditing || ViewModel.DateNotice >= DateOnly.FromDateTime(DateTime.Today).AddDays(-14));
App.HintContextChange(); App.HintContextChange();
} catch (Exception exc) { } catch (Exception exc) {
InteractionService.ShowDbException("Geschäftsanteilbewegung aktualisieren", exc); InteractionService.ShowDbException("Geschäftsanteilbewegung aktualisieren", exc);
+28 -11
View File
@@ -7,7 +7,7 @@
xmlns:local="clr-namespace:Elwig.Windows" xmlns:local="clr-namespace:Elwig.Windows"
xmlns:vm="clr-namespace:Elwig.ViewModels" xmlns:vm="clr-namespace:Elwig.ViewModels"
xmlns:ctrl="clr-namespace:Elwig.Controls" 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> <Window.DataContext>
<vm:PaymentVariantsViewModel/> <vm:PaymentVariantsViewModel/>
</Window.DataContext> </Window.DataContext>
@@ -195,11 +195,10 @@
Content="Benutzerdefinierte Zu-/Abschläge pro Mitglied" Content="Benutzerdefinierte Zu-/Abschläge pro Mitglied"
Margin="110,175,10,10" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="110,175,10,10" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top"
Checked="ConsiderCustomInput_Changed" Unchecked="ConsiderCustomInput_Changed"/> 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 Grid.Column="0" Grid.ColumnSpan="2" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="50,200,10,10">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="110"/> <ColumnDefinition Width="130"/>
<ColumnDefinition Width="27"/> <ColumnDefinition Width="27"/>
<ColumnDefinition Width="110"/> <ColumnDefinition Width="110"/>
<ColumnDefinition Width="27"/> <ColumnDefinition Width="27"/>
@@ -207,7 +206,7 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="27"/> <RowDefinition Height="27"/>
<RowDefinition Height="5"/> <RowDefinition Height="37"/>
<RowDefinition Height="27"/> <RowDefinition Height="27"/>
<RowDefinition Height="5"/> <RowDefinition Height="5"/>
<RowDefinition Height="27"/> <RowDefinition Height="27"/>
@@ -227,11 +226,23 @@
</Grid.Resources> </Grid.Resources>
<Button x:Name="EditButton" IsEnabled="{Binding IsPaymentVariantSelected}" <Button x:Name="EditButton" IsEnabled="{Binding IsPaymentVariantSelected}"
Content="{Binding EditText}" Grid.Column="0" Grid.Row="2" Grid.Column="0" Grid.Row="2" HorizontalContentAlignment="Stretch"
Click="EditButton_Click"/> 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"/> <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" <Button x:Name="ModifierButton" Grid.Column="0" Grid.Row="4" HorizontalContentAlignment="Stretch"
Click="ModifierButton_Click"/> 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 Content="&#xF0AF;" FontFamily="Segoe MDL2 Assets" FontSize="16" Grid.Row="4" Grid.Column="1" RenderTransformOrigin="0.5,0.5" >
<Label.RenderTransform> <Label.RenderTransform>
<TransformGroup> <TransformGroup>
@@ -244,8 +255,14 @@
Content="Berechnen" Grid.Column="2" Grid.Row="2" Content="Berechnen" Grid.Column="2" Grid.Row="2"
Click="CalculateButton_Click"/> Click="CalculateButton_Click"/>
<Label Content="{Binding Arrow}" FontFamily="Segoe MDL2 Assets" FontSize="16" Grid.Row="2" Grid.Column="3"/> <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" <Button x:Name="PaymentAdjustmentButton" Grid.Column="2" Grid.Row="4" HorizontalContentAlignment="Stretch"
Click="PaymentAdjustmentButton_Click"/> 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 Content="{Binding Arrow}" FontFamily="Segoe MDL2 Assets" FontSize="16" Grid.Row="4" Grid.Column="3" RenderTransformOrigin="0.5,0.5" >
<Label.RenderTransform> <Label.RenderTransform>
<TransformGroup> <TransformGroup>
@@ -261,7 +278,7 @@
Content="Freigeben" Grid.Column="4" Grid.Row="2" Content="Freigeben" Grid.Column="4" Grid.Row="2"
Click="RevertButton_Click"/> Click="RevertButton_Click"/>
<Button x:Name="SaveButton" IsEnabled="{Binding SaveIsEnabled}" <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"/> Click="SaveButton_Click"/>
</Grid> </Grid>
+14 -1
View File
@@ -5,6 +5,7 @@ using Elwig.Services;
using Elwig.ViewModels; using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -239,15 +240,27 @@ namespace Elwig.Windows {
private async void SaveButton_Click(object sender, RoutedEventArgs evt) { private async void SaveButton_Click(object sender, RoutedEventArgs evt) {
if (PaymentVariantList.SelectedItem is not PaymentVar v || ViewModel.BillingData == null) if (PaymentVariantList.SelectedItem is not PaymentVar v || ViewModel.BillingData == null)
return; return;
Mouse.OverrideCursor = Cursors.Wait; Mouse.OverrideCursor = Cursors.Wait;
try { try {
await ViewModel.UpdatePaymentVariant(v.Year, v.AvNr); await ViewModel.UpdatePaymentVariant(v.Year, v.AvNr);
App.HintContextChange();
} catch (Exception exc) { } catch (Exception exc) {
await ForceContextReload(); await ForceContextReload();
InteractionService.ShowDbException("Auszahlungsvariante aktualisieren", exc); 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; Mouse.OverrideCursor = null;
await EnsureContextRenewed(); await EnsureContextRenewed();
CommentInput_TextChanged(null, null); CommentInput_TextChanged(null, null);
DateInput_TextChanged(null, null); DateInput_TextChanged(null, null);
+1
View File
@@ -13,5 +13,6 @@ Data and configuration folder
C:\ProgramData\Elwig\ C:\ProgramData\Elwig\
- config.ini : main configuration file - config.ini : main configuration file
- database.sqlite3 : stores all data - database.sqlite3 : stores all data
- backups\ : (automatic) backups of the database
- imported.txt : list of all imported *.elwig.zip files to not automatically import them again - imported.txt : list of all imported *.elwig.zip files to not automatically import them again
- mails\ : sent/outgoing email log - mails\ : sent/outgoing email log
+12
View File
@@ -3,6 +3,7 @@
; Only needed, if more than one branch is stored in database ; Only needed, if more than one branch is stored in database
;branch = Zweigstelle ;branch = Zweigstelle
;debug = true ;debug = true
;weighing = gross ; gross/net/box
[database] [database]
; Relative or absolute path to database file ; Relative or absolute path to database file
@@ -10,6 +11,14 @@ file = database.sqlite3
; Enables database logging ; Enables database logging
;log = db.log ;log = db.log
[backup]
;auto = false
;path = backups
;retain_hours = 36
;retain_days = 8
;retain_weeks = 6
;retain_months = 18
[update] [update]
url = https://elwig.at/files/elwig/latest url = https://elwig.at/files/elwig/latest
auto = true auto = true
@@ -26,6 +35,7 @@ auto = true
;username = "" ;username = ""
;password = "" ;password = ""
;from = mail@wg.at ;from = mail@wg.at
;wait = 0.5
;[scale.1] ;[scale.1]
;type = SysTec-IT ;type = SysTec-IT
@@ -37,6 +47,8 @@ auto = true
;limit = 3500 ;limit = 3500
; Enables scale logging ; Enables scale logging
;log = waage.log ;log = waage.log
; Opt-out of auto time synchronization at startup
;synctime = false
;[scale.B] ;[scale.B]
;type = Avery-Async ;type = Avery-Async
+2 -2
View File
@@ -13,7 +13,7 @@ About
**Product:** Elwig **Product:** Elwig
**Description:** Electronic Management for Vintners' Cooperatives **Description:** Electronic Management for Vintners' Cooperatives
**Type:** ERP system **Type:** ERP system
**Version:** 1.1.0.0 ([Changelog](./CHANGELOG.md)) **Version:** 1.1.2.2 ([Changelog](./CHANGELOG.md))
**License:** [GNU General Public License 3.0 (GPLv3)](./LICENSE) **License:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
**Website:** https://elwig.at/ **Website:** https://elwig.at/
**Source code:** https://git.necronda.net/winzer/elwig **Source code:** https://git.necronda.net/winzer/elwig
@@ -33,7 +33,7 @@ Packaging: [WiX Toolset](https://www.firegiant.com/wixtoolset/)
**Produkt:** Elwig **Produkt:** Elwig
**Beschreibung:** Elektronische Winzergenossenschaftsverwaltung **Beschreibung:** Elektronische Winzergenossenschaftsverwaltung
**Typ:** Warenwirtschaftssystem (ERP-System) **Typ:** Warenwirtschaftssystem (ERP-System)
**Version:** 1.1.0.0 ([Änderungsprotokoll](./CHANGELOG.md)) **Version:** 1.1.2.2 ([Änderungsprotokoll](./CHANGELOG.md))
**Lizenz:** [GNU General Public License 3.0 (GPLv3)](./LICENSE) **Lizenz:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
**Website:** https://elwig.at/ **Website:** https://elwig.at/
**Quellcode:** https://git.necronda.net/winzer/elwig **Quellcode:** https://git.necronda.net/winzer/elwig
+1 -1
View File
@@ -53,7 +53,7 @@ namespace Tests.E2ETests {
Session.App.FindElement(By.Name("Rundschreiben")).Click(); Session.App.FindElement(By.Name("Rundschreiben")).Click();
Thread.Sleep(Utils.WINDOW_OPEN_SLEEP); Thread.Sleep(Utils.WINDOW_OPEN_SLEEP);
var window = Session.CreateWindowDriver("MailWindow"); var window = Session.CreateWindowDriver("MailWindow");
Assert.That(window.Title, Is.EqualTo($"Rundschreiben - Lese {Elwig.Helpers.Utils.Today.Year} - Elwig")); Assert.That(window.Title, Is.EqualTo($"Rundschreiben - Lese {DateTime.Today.Year} - Elwig"));
window.Close(); window.Close();
}); });
} }
+4 -4
View File
@@ -84,12 +84,12 @@ namespace Tests.E2ETests {
Window.FindElement(By.WpfId("SearchInput")).SendKeys("9999"); Window.FindElement(By.WpfId("SearchInput")).SendKeys("9999");
Thread.Sleep(500); Thread.Sleep(500);
var memberListRow = Window.FindElement(By.WpfId("MemberList")).FindElement(By.ClassName("DataGridRow")); var memberListRow = Window.FindElement(By.WpfId("MemberList")).FindElement(By.ClassName("DataGridRow"));
Assert.Multiple(() => { using (Assert.EnterMultipleScope()) {
Assert.That(memberListRow, Is.Not.Null); Assert.That(memberListRow, Is.Not.Null);
Assert.That(memberListRow.FindElement(By.Name("9999 ")), Is.Not.Null); Assert.That(memberListRow.FindElement(By.Name("9999 ")), Is.Not.Null);
Assert.That(memberListRow.FindElement(By.Name("Norbert")), Is.Not.Null); Assert.That(memberListRow.FindElement(By.Name("Norbert")), Is.Not.Null);
Assert.That(memberListRow.FindElement(By.Name("Neuling")), Is.Not.Null); Assert.That(memberListRow.FindElement(By.Name("Neuling")), Is.Not.Null);
}); }
} }
[Test] [Test]
@@ -128,12 +128,12 @@ namespace Tests.E2ETests {
Assert.That(memberListRows, Has.Count.EqualTo(1)); Assert.That(memberListRows, Has.Count.EqualTo(1));
var memberListRow = memberListRows.First(); var memberListRow = memberListRows.First();
Assert.Multiple(() => { using (Assert.EnterMultipleScope()) {
Assert.That(memberListRow, Is.Not.Null); Assert.That(memberListRow, Is.Not.Null);
Assert.That(memberListRow.FindElement(By.Name("9999 ")), Is.Not.Null); Assert.That(memberListRow.FindElement(By.Name("9999 ")), Is.Not.Null);
Assert.That(memberListRow.FindElement(By.Name("Norbert")), Is.Not.Null); Assert.That(memberListRow.FindElement(By.Name("Norbert")), Is.Not.Null);
Assert.That(memberListRow.FindElement(By.Name("Neuling")), Is.Not.Null); Assert.That(memberListRow.FindElement(By.Name("Neuling")), Is.Not.Null);
}); }
Window.FindElement(By.WpfId("DeleteMemberButton")).Click(); Window.FindElement(By.WpfId("DeleteMemberButton")).Click();
var dialog = Session.CreateWindowDriver("DeleteMemberDialog"); var dialog = Session.CreateWindowDriver("DeleteMemberDialog");
+5 -5
View File
@@ -1,8 +1,8 @@
-- inserts for HelperTests.BillingTest -- inserts for HelperTests.BillingTest
INSERT INTO wine_cultivation (cultid, name, description) VALUES INSERT INTO wine_cultivation (cultid, name, description, organic) VALUES
('KIP', 'KIP', 'Kontrollierte Integrierte Produktion'), ('KIP', 'KIP', 'Kontrollierte Integrierte Produktion', FALSE),
('B', 'Bio', 'AT-BIO-302'); ('B', 'Bio', 'Biologische Produktion', TRUE);
INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lower) VALUES INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lower) VALUES
('K', 'Kabinett', TRUE, NULL, FALSE, 0), ('K', 'Kabinett', TRUE, NULL, FALSE, 0),
@@ -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, 1, '2020-10-01', NULL, 'X', 1),
(101, 2020, 2, '2020-10-01', NULL, 'X', 2), (101, 2020, 2, '2020-10-01', NULL, 'X', 2),
(101, 2020, 3, '2020-10-01', NULL, 'X', 3); (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, 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, 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), (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, 1, '2021-10-01', NULL, 'X', 1),
(101, 2021, 2, '2021-10-01', NULL, 'X', 2), (101, 2021, 2, '2021-10-01', NULL, 'X', 2),
(101, 2021, 3, '2021-10-01', NULL, 'X', 3); (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, 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, 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), (2021, 2, 1, 'GV', NULL, 'B', 4000, 15, 'QUW', 'WLNO', 06109, TRUE, TRUE, FALSE, FALSE, NULL, NULL, NULL),
+3 -3
View File
@@ -1,7 +1,7 @@
-- inserts for DocumentTests -- inserts for DocumentTests
INSERT INTO wine_cultivation (cultid, name, description) VALUES INSERT INTO wine_cultivation (cultid, name, description, organic) VALUES
('B', 'Bio', 'AT-BIO-302'); ('B', 'Bio', 'Biologische Produktion', TRUE);
INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lower) VALUES INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lower) VALUES
('K', 'Kabinett', TRUE, NULL, FALSE, 0); ('K', 'Kabinett', TRUE, NULL, FALSE, 0);
@@ -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, 11, '2020-10-03', '14:39:22', 'X', 2),
(104, 2020, 12, '2020-10-03', '15:15:41', 'X', 3); (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, 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, 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), (2020, 2, 2, 'GV', 'K', NULL, 1873, 17.7, 'KAB', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
+3 -3
View File
@@ -1,8 +1,8 @@
-- inserts for E2ETests -- inserts for E2ETests
INSERT INTO wine_cultivation (cultid, name, description) VALUES INSERT INTO wine_cultivation (cultid, name, description, organic) VALUES
('KIP', 'KIP', 'Kontrollierte Integrierte Produktion'), ('KIP', 'KIP', 'Kontrollierte Integrierte Produktion', FALSE),
('B', 'Bio', 'AT-BIO-302'); ('B', 'Bio', 'Biologische Produktion', TRUE);
INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lower) VALUES INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lower) VALUES
('K', 'Kabinett', TRUE, NULL, FALSE, 0), ('K', 'Kabinett', TRUE, NULL, FALSE, 0),
+5 -5
View File
@@ -66,11 +66,11 @@ INSERT INTO wb_kg (kgnr, glnr) VALUES
(15216, 2), (15216, 2),
(15224, 2); (15224, 2);
INSERT INTO member (mgnr, given_name, name, zwstid, volllieferant, buchführend, country, postal_dest, address, default_kgnr, iban, lfbis_nr, ustid_nr) VALUES INSERT INTO member (mgnr, given_name, name, zwstid, volllieferant, buchführend, country, postal_dest, address, default_kgnr, iban, lfbis_nr, ustid_nr, org_auth_code) VALUES
(101, 'Max', 'Mustermann', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 1', 06109, 'AT811234567890123457', '0123463', NULL ), (101, 'Max', 'Mustermann', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 1', 06109, 'AT811234567890123457', '0123463', NULL , NULL ),
(102, 'Wernhardt', 'Weinbauer', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 2', 06109, 'AT541234567890123458', '0123471', 'ATU12345684'), (102, 'Wernhardt', 'Weinbauer', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 2', 06109, 'AT541234567890123458', '0123471', 'ATU12345684', NULL ),
(103, 'Matthäus', 'Musterbauer', 'X', FALSE, FALSE, 40, 212005138, 'Brünner Straße 10', 15224, 'AT271234567890123459', '0123480', NULL ), (103, 'Matthäus', 'Musterbauer', 'X', FALSE, FALSE, 40, 212005138, 'Brünner Straße 10', 15224, 'AT271234567890123459', '0123480', NULL , 'AT-BIO-302'),
(104, 'Waltraud', 'Winzer', 'X', FALSE, TRUE , 40, 212005138, 'Wiener Straße 15', 15224, 'AT971234567890123460', '0123498', 'ATU12345693'); (104, 'Waltraud', 'Winzer', 'X', FALSE, TRUE , 40, 212005138, 'Wiener Straße 15', 15224, 'AT971234567890123460', '0123498', 'ATU12345693', NULL );
INSERT INTO member_billing_address (mgnr, name, country, postal_dest, address) VALUES INSERT INTO member_billing_address (mgnr, name, country, postal_dest, address) VALUES
(102, 'W&B Weinbauer GesbR', 40, 222303524, 'Winzerstraße 2'), (102, 'W&B Weinbauer GesbR', 40, 222303524, 'Winzerstraße 2'),
+3 -4
View File
@@ -1,8 +1,7 @@
-- inserts for ServiceTests -- inserts for ServiceTests
INSERT INTO wine_cultivation (cultid, name, description, organic) VALUES
INSERT INTO wine_cultivation (cultid, name, description) VALUES ('B', 'Bio', 'Biologische Produktion', TRUE);
('B', 'Bio', 'AT-BIO-302');
INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lower) VALUES INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lower) VALUES
('K', 'Kabinett', TRUE, NULL, FALSE, 0); ('K', 'Kabinett', TRUE, NULL, FALSE, 0);
@@ -76,7 +75,7 @@ INSERT INTO delivery (mgnr, year, did, date, time, zwstid, lnr) VALUES
(101, 2023, 18, '2023-10-10', NULL, 'X', 2), (101, 2023, 18, '2023-10-10', NULL, 'X', 2),
(101, 2023, 19, '2023-10-10', NULL, 'X', 3); (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, 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, 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), (2021, 2, 2, 'GV', NULL, NULL, 4000, 16.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
+3 -3
View File
@@ -19,12 +19,12 @@
</Target> </Target>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.7.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.10.0" />
<PackageReference Include="Appium.WebDriver" Version="4.4.5" /> <PackageReference Include="Appium.WebDriver" Version="4.4.5" />
<PackageReference Include="NReco.PdfRenderer" Version="1.6.0" /> <PackageReference Include="NReco.PdfRenderer" Version="1.6.0" />
<PackageReference Include="NUnit" Version="4.6.1" /> <PackageReference Include="NUnit" Version="4.6.1" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" /> <PackageReference Include="NUnit3TestAdapter" Version="6.3.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.14.0"> <PackageReference Include="NUnit.Analyzers" Version="4.15.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
@@ -16,7 +16,7 @@ namespace Tests.UnitTests.DocumentTests {
""")); """));
Assert.That(text, Contains.Substring("0123463")); // Betriebsnummer Assert.That(text, Contains.Substring("0123463")); // Betriebsnummer
Assert.That(text, Contains.Substring("pauschaliert")); Assert.That(text, Contains.Substring("pauschaliert"));
Assert.That(text, Contains.Substring($"Wolkersdorf, am {Elwig.Helpers.Utils.Today:dd.MM.yyyy}")); Assert.That(text, Contains.Substring($"Wolkersdorf, am {DateTime.Today:dd.MM.yyyy}"));
Assert.That(text, Contains.Substring("Traubengutschrift Max Mustermann Probevariante")); Assert.That(text, Contains.Substring("Traubengutschrift Max Mustermann Probevariante"));
Assert.That(text, Contains.Substring("AT81 1234 5678 9012 3457")); Assert.That(text, Contains.Substring("AT81 1234 5678 9012 3457"));
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
@@ -21,7 +21,7 @@ namespace Tests.UnitTests.DocumentTests {
""")); """));
Assert.That(text, Contains.Substring("0123463")); // Betriebsnummer Assert.That(text, Contains.Substring("0123463")); // Betriebsnummer
Assert.That(text, Contains.Substring("pauschaliert")); Assert.That(text, Contains.Substring("pauschaliert"));
Assert.That(text, Contains.Substring($"Wolkersdorf, am {Elwig.Helpers.Utils.Today:dd.MM.yyyy}")); Assert.That(text, Contains.Substring($"Wolkersdorf, am {DateTime.Today:dd.MM.yyyy}"));
Assert.That(text, Contains.Substring("Anlieferungsbestätigung 2020")); Assert.That(text, Contains.Substring("Anlieferungsbestätigung 2020"));
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
20201001X001 1 Grüner Veltliner QUW 73 15,0 ungeb.: 3 219 3 219 20201001X001 1 Grüner Veltliner QUW 73 15,0 ungeb.: 3 219 3 219
@@ -8,7 +8,7 @@ namespace Tests.UnitTests.DocumentTests {
public async Task Test_01_OneDeliveryPart() { public async Task Test_01_OneDeliveryPart() {
using var doc = await DeliveryNote.Initialize(2020, 1); using var doc = await DeliveryNote.Initialize(2020, 1);
var text = await Utils.GeneratePdfText(doc); var text = await Utils.GeneratePdfText(doc);
Assert.Multiple(() => { using (Assert.EnterMultipleScope()) {
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
MUSTERMANN Max MUSTERMANN Max
Winzerstraße 1 Winzerstraße 1
@@ -23,17 +23,17 @@ namespace Tests.UnitTests.DocumentTests {
1 Grüner Veltliner Qualitätswein 73 15,0 3 219 1 Grüner Veltliner Qualitätswein 73 15,0 3 219
Herkunft: Österreich / Weinland / Niederösterreich Herkunft: Österreich / Weinland / Niederösterreich
/ Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf / Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf
Waage: 1, ID: 321 09:02, 01.10.2020 Waage/Terminal: ?/1, ID: 321 09:02, 01.10.2020
Brutto: 3 219 kg Tara: 0 kg Netto: 3 219 kg gerebelt gewogen Brutto: 3 219 kg Tara: 0 kg Netto: 3 219 kg gerebelt gewogen
""")); """));
}); }
} }
[Test] [Test]
public async Task Test_02_TwoDeliveryParts() { public async Task Test_02_TwoDeliveryParts() {
using var doc = await DeliveryNote.Initialize(2020, 4); using var doc = await DeliveryNote.Initialize(2020, 4);
var text = await Utils.GeneratePdfText(doc); var text = await Utils.GeneratePdfText(doc);
Assert.Multiple(() => { using (Assert.EnterMultipleScope()) {
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
W&B Weinbauer GesbR W&B Weinbauer GesbR
WEINBAUER Wernhardt WEINBAUER Wernhardt
@@ -49,23 +49,23 @@ namespace Tests.UnitTests.DocumentTests {
1 Grüner Veltliner Qualitätswein 82 16,7 4 002 1 Grüner Veltliner Qualitätswein 82 16,7 4 002
Herkunft: Österreich / Weinland / Niederösterreich Herkunft: Österreich / Weinland / Niederösterreich
/ Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf / Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf
Waage: ?, ID: ? (gerebelt gewogen) Waage/Terminal: ?/?, ID: ? (gerebelt gewogen)
""")); """));
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
2 Grüner Veltliner Qualitätswein 75 15,3 481 2 Grüner Veltliner Qualitätswein 75 15,3 481
Herkunft: Österreich / Weinland / Niederösterreich Herkunft: Österreich / Weinland / Niederösterreich
/ Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf / Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf
Waage: ?, ID: ? (gerebelt gewogen) Waage/Terminal: ?/?, ID: ? (gerebelt gewogen)
""")); """));
Assert.That(text, Contains.Substring("Gesamt: 81 16,5 4 483")); Assert.That(text, Contains.Substring("Gesamt: 81 16,5 4 483"));
}); }
} }
[Test] [Test]
public async Task Test_03_DeliveryPartsWithAttribute() { public async Task Test_03_DeliveryPartsWithAttribute() {
using var doc = await DeliveryNote.Initialize(2020, 3); using var doc = await DeliveryNote.Initialize(2020, 3);
var text = await Utils.GeneratePdfText(doc); var text = await Utils.GeneratePdfText(doc);
Assert.Multiple(() => { using (Assert.EnterMultipleScope()) {
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
MUSTERMANN Max MUSTERMANN Max
Winzerstraße 1 Winzerstraße 1
@@ -80,29 +80,29 @@ namespace Tests.UnitTests.DocumentTests {
1 Grüner Veltliner Wein 75 15,4 2 561 1 Grüner Veltliner Wein 75 15,4 2 561
Herkunft: Österreich Herkunft: Österreich
/ Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf / Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf
Waage: ?, ID: ? (gerebelt gewogen) Waage/Terminal: ?/?, ID: ? (gerebelt gewogen)
""")); """));
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
2 Grüner Veltliner Kabinett Wein 87 17,6 3 129 2 Grüner Veltliner Kabinett Wein 87 17,6 3 129
Herkunft: Österreich Herkunft: Österreich
/ Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf / Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf
Waage: ?, ID: ? (gerebelt gewogen) Waage/Terminal: ?/?, ID: ? (gerebelt gewogen)
""")); """));
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
3 Grüner Veltliner Wein 79 16,1 1 280 3 Grüner Veltliner Wein 79 16,1 1 280
Herkunft: Österreich Herkunft: Österreich
/ Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf / Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf
Waage: ?, ID: ? (gerebelt gewogen) Waage/Terminal: ?/?, ID: ? (gerebelt gewogen)
""")); """));
Assert.That(text, Contains.Substring("Gesamt: 81 16,5 6 970")); Assert.That(text, Contains.Substring("Gesamt: 81 16,5 6 970"));
}); }
} }
[Test] [Test]
public async Task Test_04_DeliveryPartsWithCultivation() { public async Task Test_04_DeliveryPartsWithCultivation() {
using var doc = await DeliveryNote.Initialize(2020, 7); using var doc = await DeliveryNote.Initialize(2020, 7);
var text = await Utils.GeneratePdfText(doc); var text = await Utils.GeneratePdfText(doc);
Assert.Multiple(() => { using (Assert.EnterMultipleScope()) {
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
MUSTERBAUER Matthäus MUSTERBAUER Matthäus
Brünner Straße 10 Brünner Straße 10
@@ -115,20 +115,20 @@ namespace Tests.UnitTests.DocumentTests {
Assert.That(text, Contains.Substring("Das Mitglied erklärt, dass die gelieferte Ware dem österreichischen Weingesetz entspricht")); Assert.That(text, Contains.Substring("Das Mitglied erklärt, dass die gelieferte Ware dem österreichischen Weingesetz entspricht"));
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
1 Grüner Veltliner Wein 80 16,3 3 198 1 Grüner Veltliner Wein 80 16,3 3 198
Bewirtschaftung: Bio (AT-BIO-302) Bewirtschaftung: Bio (Biologische Produktion) Kontrollstelle: AT-BIO-302
Herkunft: Österreich Herkunft: Österreich
/ Wolkersdorfer Hochleithen / Wolkersdorf im Weinviertel / KG Wolkersdorf / Wolkersdorfer Hochleithen / Wolkersdorf im Weinviertel / KG Wolkersdorf
Waage: ?, ID: ? (gerebelt gewogen) Waage/Terminal: ?/?, ID: ? (gerebelt gewogen)
""")); """));
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
2 Grüner Veltliner Qualitätswein 75 15,4 2 134 2 Grüner Veltliner Qualitätswein 75 15,4 2 134
Bewirtschaftung: Bio (AT-BIO-302) Bewirtschaftung: Bio (Biologische Produktion) Kontrollstelle: AT-BIO-302
Herkunft: Österreich / Weinland / Niederösterreich Herkunft: Österreich / Weinland / Niederösterreich
/ Wolkersdorfer Hochleithen / Wolkersdorf im Weinviertel / KG Wolkersdorf / Wolkersdorfer Hochleithen / Wolkersdorf im Weinviertel / KG Wolkersdorf
Waage: ?, ID: ? (gerebelt gewogen) Waage/Terminal: ?/?, ID: ? (gerebelt gewogen)
""")); """));
Assert.That(text, Contains.Substring("Gesamt: 78 15,9 5 332")); Assert.That(text, Contains.Substring("Gesamt: 78 15,9 5 332"));
}); }
} }
[Test] [Test]
@@ -152,12 +152,12 @@ namespace Tests.UnitTests.DocumentTests {
Herkunft: Österreich / Weinland / Niederösterreich Herkunft: Österreich / Weinland / Niederösterreich
/ Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf / Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf
Zu-/Abschläge: Geschädigte Trauben 10,00 % Zu-/Abschläge: Geschädigte Trauben 10,00 %
Waage: ?, ID: ? (gerebelt gewogen) Waage/Terminal: ?/?, ID: ? (gerebelt gewogen)
2 Grüner Veltliner Kabinett Kabinett 87 17,7 1 873 2 Grüner Veltliner Kabinett Kabinett 87 17,7 1 873
Herkunft: Österreich / Weinland / Niederösterreich Herkunft: Österreich / Weinland / Niederösterreich
/ Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf / Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf
Zu-/Abschläge: Keine Voranmeldung 0,1000 /kg Zu-/Abschläge: Keine Voranmeldung 0,1000 /kg
Waage: ?, ID: ? (gerebelt gewogen) Waage/Terminal: ?/?, ID: ? (gerebelt gewogen)
Gesamt: 87 17,6 4 860 Gesamt: 87 17,6 4 860
""")); """));
} }
+4 -4
View File
@@ -24,13 +24,13 @@ namespace Tests.UnitTests.DocumentTests {
} }
[OneTimeSetUp] [OneTimeSetUp]
public async Task SetupPrinting() { public void SetupPrinting() {
await Pdf.Init(); Pdf.Init();
} }
[OneTimeTearDown] [OneTimeTearDown]
public async Task TeardownPrinting() { public void TeardownPrinting() {
await Pdf.Cleanup(); Pdf.Cleanup();
} }
} }
} }
@@ -842,5 +842,151 @@ namespace Tests.UnitTests.ServiceTests {
Assert.That(await ctx.Deliveries.FindAsync(2022, 2), Is.Null); Assert.That(await ctx.Deliveries.FindAsync(2022, 2), Is.Null);
} }
} }
private static readonly DateTime[] DeliveryDates = [
new(2020, 10, 1, 17, 45, 0),
new(2020, 10, 1, 18, 30, 0),
new(2020, 10, 2, 8, 34, 0),
new(2020, 10, 2, 9, 45, 0),
new(2020, 10, 2, 10, 19, 0),
new(2020, 10, 2, 11, 22, 0),
new(2020, 10, 2, 13, 39, 0),
new(2020, 10, 2, 14, 2, 0),
new(2020, 10, 2, 15, 55, 0),
new(2020, 10, 2, 16, 28, 0),
new(2020, 10, 2, 17, 13, 0),
new(2020, 10, 2, 18, 10, 0),
new(2020, 10, 3, 8, 13, 0),
new(2020, 10, 3, 9, 39, 0),
new(2020, 10, 3, 10, 20, 0),
new(2020, 10, 3, 11, 46, 0),
new(2020, 10, 3, 13, 56, 0),
new(2020, 10, 3, 14, 4, 0),
new(2020, 10, 3, 15, 33, 0),
new(2020, 10, 3, 16, 23, 0),
new(2020, 10, 3, 17, 18, 0),
new(2020, 10, 3, 18, 19, 0),
new(2020, 10, 3, 19, 10, 0),
new(2020, 10, 3, 20, 25, 0),
new(2020, 10, 4, 5, 57, 0),
new(2020, 10, 4, 6, 13, 0),
new(2020, 10, 4, 7, 42, 0),
new(2020, 10, 4, 8, 34, 0),
new(2020, 10, 4, 9, 45, 0),
new(2020, 10, 4, 10, 19, 0),
new(2020, 10, 4, 11, 22, 0),
new(2020, 10, 4, 13, 39, 0),
new(2020, 10, 4, 14, 2, 0),
new(2020, 10, 4, 15, 55, 0),
new(2020, 10, 4, 16, 28, 0),
new(2020, 10, 4, 17, 13, 0),
new(2020, 10, 4, 18, 10, 0),
new(2020, 10, 5, 5, 57, 0),
new(2020, 10, 5, 6, 13, 0),
new(2020, 10, 5, 7, 42, 0),
new(2020, 10, 5, 8, 34, 0),
new(2020, 10, 5, 9, 45, 0),
new(2020, 10, 5, 10, 19, 0),
new(2020, 10, 5, 11, 22, 0),
new(2020, 10, 5, 13, 39, 0),
new(2020, 10, 5, 14, 2, 0),
new(2020, 10, 5, 15, 55, 0),
new(2020, 10, 5, 16, 28, 0),
new(2020, 10, 5, 17, 13, 0),
new(2020, 10, 5, 18, 10, 0),
new(2020, 10, 5, 19, 10, 0),
new(2020, 10, 5, 20, 25, 0),
new(2020, 10, 6, 16, 23, 0),
new(2020, 10, 6, 22, 24, 0),
new(2020, 10, 6, 23, 45, 0),
new(2020, 10, 7, 0, 32, 0),
new(2020, 10, 7, 1, 12, 0),
new(2020, 10, 7, 2, 16, 0),
new(2020, 10, 7, 3, 24, 0),
new(2020, 10, 7, 4, 9, 0),
new(2020, 10, 7, 5, 57, 0),
new(2020, 10, 7, 6, 41, 0),
new(2020, 10, 7, 7, 10, 0),
new(2020, 10, 7, 17, 23, 0),
new(2020, 10, 7, 18, 16, 0),
new(2020, 10, 7, 19, 39, 0),
new(2020, 10, 7, 20, 31, 0),
new(2020, 10, 7, 21, 23, 0),
new(2020, 10, 7, 22, 24, 0),
new(2020, 10, 7, 23, 45, 0),
new(2020, 10, 8, 0, 32, 0),
new(2020, 10, 8, 1, 12, 0),
new(2020, 10, 8, 2, 16, 0),
new(2020, 10, 8, 3, 24, 0),
new(2020, 10, 8, 4, 9, 0),
new(2020, 10, 8, 5, 57, 0),
new(2020, 10, 8, 6, 41, 0),
new(2020, 10, 8, 7, 10, 0),
new(2020, 10, 9, 7, 53, 0),
new(2020, 10, 9, 12, 36, 0),
new(2020, 10, 9, 16, 26, 0),
new(2020, 10, 9, 20, 19, 0),
];
[Test]
public void Test_GetFilterToday_01_Empty() {
Assert.That(DeliveryService.GetFilterToday(new(2020, 10, 10), DeliveryDates),
Is.Null);
}
[Test]
public void Test_GetFilterToday_02_Normal() {
Assert.That(DeliveryService.GetFilterToday(new(2020, 10, 2), DeliveryDates),
Is.EqualTo((new DateTime(2020, 10, 2, 8, 0, 0), new DateTime(2020, 10, 2, 19, 0, 0))));
}
[Test]
public void Test_GetFilterToday_03_Overtime() {
Assert.That(DeliveryService.GetFilterToday(new(2020, 10, 3), DeliveryDates),
Is.EqualTo((new DateTime(2020, 10, 3, 8, 0, 0), new DateTime(2020, 10, 3, 21, 0, 0))));
}
[Test]
public void Test_GetFilterToday_04_EarlyStart() {
Assert.That(DeliveryService.GetFilterToday(new(2020, 10, 4), DeliveryDates),
Is.EqualTo((new DateTime(2020, 10, 4, 5, 0, 0), new DateTime(2020, 10, 4, 19, 0, 0))));
}
[Test]
public void Test_GetFilterToday_05_EarlyStartAndOvertime() {
Assert.That(DeliveryService.GetFilterToday(new(2020, 10, 5), DeliveryDates),
Is.EqualTo((new DateTime(2020, 10, 5, 5, 0, 0), new DateTime(2020, 10, 5, 21, 0, 0))));
}
[Test]
public void Test_GetFilterToday_06_NightShift() {
using (Assert.EnterMultipleScope()) {
Assert.That(DeliveryService.GetFilterToday(new(2020, 10, 6), DeliveryDates),
Is.EqualTo((new DateTime(2020, 10, 6, 22, 0, 0), new DateTime(2020, 10, 7, 0, 0, 0))));
Assert.That(DeliveryService.GetFilterToday(new(2020, 10, 7), DeliveryDates.Where(d => d <= new DateTime(2020, 10, 7, 12, 0, 0))),
Is.EqualTo((new DateTime(2020, 10, 6, 22, 0, 0), new DateTime(2020, 10, 7, 8, 0, 0))));
Assert.That(DeliveryService.GetFilterToday(new(2020, 10, 7), DeliveryDates.Where(d => d <= new DateTime(2020, 10, 7, 12, 0, 0)).Append(new(2020, 10, 7, 15, 10, 0))),
Is.EqualTo((new DateTime(2020, 10, 7, 15, 0, 0), new DateTime(2020, 10, 7, 16, 0, 0))));
}
}
[Test]
public void Test_GetFilterToday_07_EarlyStartNightShift() {
using (Assert.EnterMultipleScope()) {
Assert.That(DeliveryService.GetFilterToday(new(2020, 10, 7), DeliveryDates),
Is.EqualTo((new DateTime(2020, 10, 7, 17, 0, 0), new DateTime(2020, 10, 8, 0, 0, 0))));
Assert.That(DeliveryService.GetFilterToday(new(2020, 10, 8), DeliveryDates),
Is.EqualTo((new DateTime(2020, 10, 7, 17, 0, 0), new DateTime(2020, 10, 8, 8, 0, 0))));
Assert.That(DeliveryService.GetFilterToday(new(2020, 10, 8), DeliveryDates.Append(new(2020, 10, 8, 15, 10, 0))),
Is.EqualTo((new DateTime(2020, 10, 8, 15, 0, 0), new DateTime(2020, 10, 8, 16, 0, 0))));
}
}
} }
} }
@@ -31,16 +31,19 @@ namespace Tests.UnitTests.WeighingTests {
Mock.Weight = 1235; Mock.Weight = 1235;
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult { Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
GrossWeight = 1235, TareWeight = 0, NetWeight = 1235, GrossWeight = 1235, TareWeight = 0, NetWeight = 1235,
ScaleId = "1",
Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23), Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23),
})); }));
Mock.Weight = 1240; Mock.Weight = 1240;
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult { Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
GrossWeight = 1240, TareWeight = 0, NetWeight = 1240, GrossWeight = 1240, TareWeight = 0, NetWeight = 1240,
ScaleId = "1",
Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23), Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23),
})); }));
Mock.Weight = 1245; Mock.Weight = 1245;
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult { Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
GrossWeight = 1245, TareWeight = 0, NetWeight = 1245, GrossWeight = 1245, TareWeight = 0, NetWeight = 1245,
ScaleId = "1",
Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23), Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23),
})); }));
} }
@@ -50,19 +53,19 @@ namespace Tests.UnitTests.WeighingTests {
Mock.Weight = 1235; Mock.Weight = 1235;
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult { Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
GrossWeight = 1235, TareWeight = 0, NetWeight = 1235, GrossWeight = 1235, TareWeight = 0, NetWeight = 1235,
WeighingId = "1", FullWeighingId = $"2020-10-17/1", ScaleId = "1", WeighingId = "1", FullWeighingId = $"2020-10-17/1",
Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23), Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23),
})); }));
Mock.Weight = 3335; Mock.Weight = 3335;
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult { Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
GrossWeight = 3335, TareWeight = 0, NetWeight = 3335, GrossWeight = 3335, TareWeight = 0, NetWeight = 3335,
WeighingId = "2", FullWeighingId = $"2020-10-17/2", ScaleId = "1", WeighingId = "2", FullWeighingId = $"2020-10-17/2",
Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23), Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23),
})); }));
Mock.Weight = 6420; Mock.Weight = 6420;
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult { Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
GrossWeight = 6420, TareWeight = 0, NetWeight = 6420, GrossWeight = 6420, TareWeight = 0, NetWeight = 6420,
WeighingId = "3", FullWeighingId = $"2020-10-17/3", ScaleId = "1", WeighingId = "3", FullWeighingId = $"2020-10-17/3",
Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23), Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23),
})); }));
} }
@@ -5,7 +5,7 @@ namespace Tests.UnitTests.WeighingTests {
public class ScaleTestHaugsdorfDMA02 { public class ScaleTestHaugsdorfDMA02 {
private MockScale Mock; private MockScale Mock;
private GassnerScale Scale; private GassnerEnqScale Scale;
[OneTimeSetUp] [OneTimeSetUp]
public void SetupScale() { public void SetupScale() {
@@ -32,29 +32,23 @@ namespace Tests.UnitTests.WeighingTests {
Mock.Weight = 1234; Mock.Weight = 1234;
Mock.Tare = 0; Mock.Tare = 0;
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult { Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
GrossWeight = 1234, GrossWeight = 1234, TareWeight = 0, NetWeight = 1234,
TareWeight = 0, ScaleId = "1",
NetWeight = 1234, Date = new DateOnly(2020, 10, 15), Time = new TimeOnly(12, 34),
Date = new DateOnly(2020, 10, 15),
Time = new TimeOnly(12, 34),
})); }));
Mock.Weight = 1235; Mock.Weight = 1235;
Mock.Tare = 46; Mock.Tare = 46;
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult { Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
GrossWeight = 1281, GrossWeight = 1281, TareWeight = 46, NetWeight = 1235,
TareWeight = 46, ScaleId = "1",
NetWeight = 1235, Date = new DateOnly(2020, 10, 15), Time = new TimeOnly(12, 34),
Date = new DateOnly(2020, 10, 15),
Time = new TimeOnly(12, 34),
})); }));
Mock.Weight = 1236; Mock.Weight = 1236;
Mock.Tare = 92; Mock.Tare = 92;
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult { Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
GrossWeight = 1328, GrossWeight = 1328, TareWeight = 92, NetWeight = 1236,
TareWeight = 92, ScaleId = "1",
NetWeight = 1236, Date = new DateOnly(2020, 10, 15), Time = new TimeOnly(12, 34),
Date = new DateOnly(2020, 10, 15),
Time = new TimeOnly(12, 34),
})); }));
} }
@@ -63,35 +57,23 @@ namespace Tests.UnitTests.WeighingTests {
Mock.Weight = 1234; Mock.Weight = 1234;
Mock.Tare = 92; Mock.Tare = 92;
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult { Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
GrossWeight = 1326, GrossWeight = 1326, TareWeight = 92, NetWeight = 1234,
TareWeight = 92, ScaleId = "1", WeighingId = "1", FullWeighingId = "1",
NetWeight = 1234, Date = new DateOnly(2020, 10, 15), Time = new TimeOnly(12, 34),
WeighingId = "1",
FullWeighingId = "1",
Date = new DateOnly(2020, 10, 15),
Time = new TimeOnly(12, 34),
})); }));
Mock.Weight = 3333; Mock.Weight = 3333;
Mock.Tare = 41; Mock.Tare = 41;
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult { Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
GrossWeight = 3374, GrossWeight = 3374, TareWeight = 41, NetWeight = 3333,
TareWeight = 41, ScaleId = "1", WeighingId = "2", FullWeighingId = "2",
NetWeight = 3333, Date = new DateOnly(2020, 10, 15), Time = new TimeOnly(12, 34),
WeighingId = "2",
FullWeighingId = "2",
Date = new DateOnly(2020, 10, 15),
Time = new TimeOnly(12, 34),
})); }));
Mock.Weight = 4321; Mock.Weight = 4321;
Mock.Tare = 0; Mock.Tare = 0;
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult { Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
GrossWeight = 4321, GrossWeight = 4321, TareWeight = 0, NetWeight = 4321,
TareWeight = 0, ScaleId = "1", WeighingId = "3", FullWeighingId = "3",
NetWeight = 4321, Date = new DateOnly(2020, 10, 15), Time = new TimeOnly(12, 34),
WeighingId = "3",
FullWeighingId = "3",
Date = new DateOnly(2020, 10, 15),
Time = new TimeOnly(12, 34),
})); }));
} }

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