Compare commits

..

53 Commits

Author SHA1 Message Date
623f55f5b0 App: Rename ExePath to InstallPath 2025-05-27 11:43:34 +02:00
b580e1bf79 App: Use environment variables for paths 2025-05-26 15:18:32 +02:00
b08ca63bed [] Documents: Move Documents install folder from ProgramData to Program Files 2025-05-26 15:12:49 +02:00
6a5f4cefa9 [] PaymentVariantSummary: Add 'attributlos gebunden' columns 2025-05-23 15:43:39 +02:00
0f3ce39f35 Elwig: Use 'Menge' instead of 'Gewicht' where applicable 2025-05-20 15:55:11 +02:00
336aef5c70 CreditNoteDeliveryData: Include WeighingModifier only if delivery modifiers are considered 2025-05-20 13:09:29 +02:00
0dff3986b7 PaymentVariantsWindow: Add note, that modifiers include Rebelzuschlag 2025-05-20 12:07:28 +02:00
318fb5dc7b Bump version to 0.13.9 2025-05-05 10:41:18 +02:00
41c5288fc5 Elwig: Update dependencies 2025-05-05 10:36:30 +02:00
e50e7337e6 Helpers/Utils: Automatically change URL to sync.elwig.at when applicable 2025-05-05 10:36:30 +02:00
ffe85d471c README: Add text 2025-04-24 16:05:41 +02:00
d1c07ee92a Installer/Setup: Update to WiX 6 2025-04-24 14:35:40 +02:00
4af2fa256e Tests: Update dependencies 2025-04-24 14:31:36 +02:00
bf0db37872 Elwig: Update dependencies 2025-04-24 14:31:27 +02:00
3161351a30 Bump version to 0.13.8 2025-02-21 12:05:34 +01:00
aa98909c0a DeliveryAdminWindow: Fix Handlese/Gerebelt Gewogen input behaviour 2025-02-20 17:06:40 +01:00
775bb08e95 Elwig: Update dependencies 2025-02-20 16:08:52 +01:00
fe0a7dab2a Tests: Update dependencies 2025-02-20 16:08:42 +01:00
138dae715e DeliveryAdminWindow: Allow Gr.Inzersdorf to select LesewagenInput 2025-02-20 15:56:26 +01:00
28af7f8dd3 CHANGELOG: Fix unser anchor names 2025-01-22 16:19:24 +01:00
7cc2f75e7c Bump version to 0.13.7 2025-01-21 11:59:54 +01:00
c7a2f2241d Billing: Handle negative credit amount in following credits 2025-01-21 11:58:08 +01:00
bd4ebb8c35 PaymentVariantsWindow: Allow user to change date 2025-01-21 11:03:54 +01:00
6d88c5645c PaymentVariantsWindow: Warn user about negative credit exports 2025-01-21 11:01:56 +01:00
5d017cc8ea MailLogWindow: Fix crash when opening 2025-01-19 17:00:31 +01:00
0b8a1b321f BillingData: Fix collapsing tested with permutations 2025-01-18 12:19:33 +01:00
95927c3f1a ChartWindow: Remove old commented-out code 2025-01-16 09:09:28 +01:00
f7297d313a Bump version to 0.13.6 2025-01-14 23:43:20 +01:00
80fec4473a Elwig: Update dependencies 2025-01-14 22:43:53 +01:00
95ccb2627c Tests: Update dependencies 2025-01-14 22:43:04 +01:00
20e3e2a76b BillingData: Fix collapsing with cultivations/defaults 2025-01-14 22:05:48 +01:00
b83df45e8f Bump version to 0.13.5 2025-01-02 17:41:51 +01:00
c24b1ca2b9 DeliveryAdminWindow: Add WineLocalityStatistics 2025-01-02 17:25:46 +01:00
5e53d864b1 MemberAdminWindow: Add filters for active and non-active members 2025-01-02 14:08:46 +01:00
633b560a67 Tests: Update dependencies 2025-01-02 14:04:50 +01:00
c9e483ba9d Elwig: Update dependencies 2025-01-02 14:04:40 +01:00
c484d27520 Bump version to 0.13.4 2024-11-25 19:23:20 +01:00
6c7f10cb26 Tests: Update dependencies 2024-11-25 19:19:31 +01:00
4a10e94d71 Elwig: Update dependencies 2024-11-25 19:19:28 +01:00
338f9fe092 AreaComUnderDeliveryData: Also include inactive members with active area commitments 2024-11-25 12:38:04 +01:00
3b97c2243a AreaComUnderDeliveryData: Fix file name 2024-11-25 12:34:43 +01:00
6ba2aa7143 OverUnderDeliveryData: Fix absence of non-deliverers in list
Bug was introduced by commit 9930e6173c772fe433ff69397205872ad30f6a08
and shipped with v0.10.6 (2024-08-30)
2024-11-25 12:17:23 +01:00
a99a23fd08 Tests: Update dependencies 2024-11-17 16:58:09 +01:00
70e01849be Setup: Update dependencies 2024-11-17 16:57:53 +01:00
c3a2f983d5 Elwig: Update dependencies 2024-11-17 16:57:40 +01:00
ebf0c20a90 Bump version to 0.13.3 2024-11-13 23:35:29 +01:00
2ee0d56dcc Windows: Add MailLogWindow 2024-11-13 18:37:50 +01:00
0a9731af09 MailWindow: Fix small bugs and persist all settings 2024-11-13 18:07:03 +01:00
6718ad4c8d AreaComAdminWindow: Add filters for season 2024-11-10 16:35:44 +01:00
a1d84dd988 MemberAdminWindow: Add more filters for AreaComs 2024-11-10 16:35:03 +01:00
f4fa549130 Windows: Add icons on Buttons and MenuItems 2024-11-10 10:58:44 +01:00
c5453c2fe6 MainWindow: Add 'Flächenbindungen' and 'Liefermenge/Ertrag' button 2024-11-10 10:58:35 +01:00
54deccf021 MainWindow: Add statistics table in Leseabschluss 2024-11-09 09:49:19 +01:00
73 changed files with 2212 additions and 668 deletions
CHANGELOG.md
Elwig
Installer
README.md
Setup
Tests

@ -3,20 +3,143 @@ Changelog
========= =========
[v0.13.9][v0.13.9] (2025-05-05) {#v0.13.9}
------------------------------------------
### Sonstiges {#v0.13.9-misc}
* Abhängigkeiten aktualisiert. (bf0db37872, 4af2fa256e, d1c07ee92a, 41c5288fc5)
* Automatisches Aktualisieren der Synchroisations-URL (`https://elwig.at/clients/` -> `https://sync.elwig.at/`). (e50e7337e6)
[v0.13.9]: https://git.necronda.net/winzer/elwig/releases/tag/v0.13.9
[v0.13.8][v0.13.8] (2025-02-21) {#v0.13.8}
------------------------------------------
### Behobene Fehler {#v0.13.8-bugfixes}
* Details im Lieferungen-Fenster (`DeliveryAdminWindow`) für die WG Weinland richtiggestellt (Lesweagen, Verhalten bei _Gerebelt gewogen_). (138dae715e, aa98909c0a)
### Sonstiges {#v0.13.8-misc}
* Abhängigkeiten aktualisiert. (fe0a7dab2a, 775bb08e95)
[v0.13.8]: https://git.necronda.net/winzer/elwig/releases/tag/v0.13.8
[v0.13.7][v0.13.7] (2025-01-21) {#v0.13.7}
------------------------------------------
### Behobene Fehler {#v0.13.7-bugfixes}
* In seltenen Fällen konnten im Auszahlungsvariante-Fenster (`ChartWindow`) manche (Sorten-/Attribut-/Bewirtschaftungsart-)Zuordnungen zu Kurven nicht richtig gespeichert werden. (0b8a1b321f)
* Beim Öffnen des Ausgangs-Protokoll-Fensters (`MailLogWindow`) kam es zu einem Absturz. (5d017cc8ea)
* Im Auszahlungsvarianten-Fenster (`PaymentVariantsWindow`) war es nicht möglich die Überweisungsdaten zu exportieren, sofern mindestens eine Traubengutschrift einen negativen Betrag aufwies.
Jetzt wird der Benutzer nur gewarnt und es ist möglich alle anderen Gutschriften zu exportieren. (6d88c5645c, c7a2f2241d)
### Sonstiges {#v0.13.7-misc}
* Im Auszahlungsvarianten-Fenster (`PaymentVariantsWindow`) ist es nun möglich das Datum einer Auszahlungsvariante zu ändern. (bd4ebb8c35)
[v0.13.7]: https://git.necronda.net/winzer/elwig/releases/tag/v0.13.7
[v0.13.6][v0.13.6] (2025-01-14) {#v0.13.6}
------------------------------------------
### Behobene Fehler {#v0.13.6-bugfixes}
* In seltenen Fällen konnten im Auszahlungsvariante-Fenster (`ChartWindow`) manche (Sorten-/Attribut-/Bewirtschaftungsart-)Zuordnungen zu Kurven nicht richtig gespeichert werden.
Berechnungen basierend auf diesen (evtl. falschen) Zuordnungen wurden immer richtig ausgeführt, eine nachträgliche Überprüfung ist daher möglich. (20e3e2a76b)
### Sonstiges {#v0.13.6-misc}
* Abhängigkeiten aktualisiert. (95ccb2627c, 80fec4473a)
[v0.13.6]: https://git.necronda.net/winzer/elwig/releases/tag/v0.13.6
[v0.13.5][v0.13.5] (2025-01-02) {#v0.13.5}
------------------------------------------
### Neue Funktionen {#v0.13.5-features}
* Im Mitglieder-Fenster (`MemberAdminWindow`) Filter `aktiv` und `!aktiv` hinzugefügt. (5e53d864b1)
* Im Lieferungen Fenster (`DeliveryAdminWindow`) Menüpunkt _Statistik_ mitsamt _Qualitätsstatistik_ und _Lieferstatistik pro Ort_ hinzugefügt. (c24b1ca2b9)
### Sonstiges {#v0.13.5-misc}
* Abhängigkeiten aktualisiert. (c9e483ba9d, 633b560a67)
[v0.13.5]: https://git.necronda.net/winzer/elwig/releases/tag/v0.13.5
[v0.13.4][v0.13.4] (2024-11-25) {#v0.13.4}
------------------------------------------
### Behobene Fehler {#v0.13.4-bugfixes}
* Bei _Unter-/Überlieferungen lt. gez. GA_ waren seit [v0.10.6](#v0.10.6) (2023-08-30) Nicht-Lieferanten nicht aufgeführt. (6ba2aa7143)
* Bei _Unterlieferungen laut Flächenbindungen_ wurden nur Mitglieder berücksichtigt, die zum Zeitpunkt des Exports aktiv waren. (3b97c2243a, 338f9fe092)
### Sonstiges {#v0.13.4-misc}
* Abhängigkeiten aktualisiert. (c3a2f983d5, 70e01849be, a99a23fd08, 4a10e94d71, 6c7f10cb26)
[v0.13.4]: https://git.necronda.net/winzer/elwig/releases/tag/v0.13.4
[v0.13.3][v0.13.3] (2024-11-13) {#v0.13.3}
------------------------------------------
### Neue Funktionen {#v0.13.3-features}
* Im Haupt-Fenster (`MainWindow`) unter _Leseabschluss_ eine Statistik-Tabelle (Gebunden/Ungeb., Mitglieder/Gewicht/Fläche) hinzugefügt. (54deccf021)
* Im Haupt-Fenster (`MainWindow`) unter _Leseabschluss_ zwei Exportmöglichkeiten (_Flächenbindungen_, _Liefermenge/Ertrag_) hinzugefügt. (c5453c2fe6)
* Ausgangs-Protokoll-Fenster (`MailLogWindow`) zum Ansehen aller ausgehenden E-Mails oder ausgedruckten Rundschreiben hinzugefügt (_Rundschreiben_ -> _Hilfe_). (2ee0d56dcc)
### Behobene Fehler {#v0.13.3-bugfixes}
* Im Rundschreiben-Fenster (`MailWindow`) kleinere Fehler behoben und alle Einstellungen werden nun gespeichert. (0a9731af09)
### Sonstiges {#v0.13.3-misc}
* In allen Fenstern an passenden Stellen Symbole/Icons hinzugefügt. (f4fa549130)
* Im Mitglieder-Fenster (`MemberAdminWindow`) Filter `Flächenbindung` (Mitglieder mit irgendeiner aktiven Flächenbindung) hinzugefügt. (a1d84dd988)
* Im Flächenbindungen-Fenster (`AreaComAdminWindow`) Filter für explizite Saisons hinzugefügt. (6718ad4c8d)
[v0.13.3]: https://git.necronda.net/winzer/elwig/releases/tag/v0.13.3
[v0.13.2][v0.13.2] (2024-10-13) {#v0.13.2} [v0.13.2][v0.13.2] (2024-10-13) {#v0.13.2}
------------------------------------------ ------------------------------------------
### Neue Funktionen {#0.13.2-features} ### Neue Funktionen {#v0.13.2-features}
* Im Lieferungen-Fenster den Menüpunkt _Abwerteliste_ (`DeliveryDepreciationList`) hinzugefügt. (3cbffdbf27) * Im Lieferungen-Fenster den Menüpunkt _Abwertungsliste_ (`DeliveryDepreciationList`) hinzugefügt. (3cbffdbf27)
### Behobene Fehler {#0.13.2-bugfixes} ### Behobene Fehler {#v0.13.2-bugfixes}
* Fehler im Waagenprotokoll `Avery-Async` (L320) behoben. (8680e51052) * Fehler im Waagenprotokoll `Avery-Async` (L320) behoben. (8680e51052)
* Hausnummern in der BKI Traubentransportscheinliste werden in Excel richtig angezeigt. (86f7f693a0) * Hausnummern in der BKI Traubentransportscheinliste werden in Excel richtig angezeigt. (86f7f693a0)
* Beim Ändern der Identifikatoren von Attributen/Bewirtschaftungsarten wurden diese in Auszahlungsvarianten nicht aktualisiert. (d1f67dc57d) * Beim Ändern der Identifikatoren von Attributen/Bewirtschaftungsarten wurden diese in Auszahlungsvarianten nicht aktualisiert. (d1f67dc57d)
### Sonstiges {#0.13.2-misc} ### Sonstiges {#v0.13.2-misc}
* Weitere automatisierte Tests hinzugefügt. ([#11][i11]) * Weitere automatisierte Tests hinzugefügt. ([#11][i11])
* Namenszusätze bei Gemeinden (z.B. an, bei, im, am) genauer angegeben. (65498dd18f) * Namenszusätze bei Gemeinden (z.B. an, bei, im, am) genauer angegeben. (65498dd18f)
@ -32,15 +155,15 @@ Changelog
[v0.13.1][v0.13.1] (2024-09-29) {#v0.13.1} [v0.13.1][v0.13.1] (2024-09-29) {#v0.13.1}
------------------------------------------ ------------------------------------------
### Neue Funktionen {#0.13.1-features} ### Neue Funktionen {#v0.13.1-features}
* Das Extrahieren/Abwerten/Aufteilen von (Teil-)Lieferungen wurde grundlegend überarbeitet und funktioniert ab jetzt in einem einzigen, übersichtlicheren Dialog. (c62947dacd, c185437b9a) * Das Extrahieren/Abwerten/Aufteilen von (Teil-)Lieferungen wurde grundlegend überarbeitet und funktioniert ab jetzt in einem einzigen, übersichtlicheren Dialog. (c62947dacd, c185437b9a)
### Behobene Fehler {#0.13.1-bugfixes} ### Behobene Fehler {#v0.13.1-bugfixes}
* Im Mitglieder-Fenster (`MemberAdminWinodw`) wurden bei `Anlieferungsbestätigung -> speichern (PDF)` und `Traubengutschrift -> speichern (PDF)` E-Mails verschickt, anstatt ein PDF gespeichert. (6ba1973087, a2315e84bd) * Im Mitglieder-Fenster (`MemberAdminWinodw`) wurden bei `Anlieferungsbestätigung -> speichern (PDF)` und `Traubengutschrift -> speichern (PDF)` E-Mails verschickt, anstatt ein PDF gespeichert. (6ba1973087, a2315e84bd)
### Sonstiges {#0.13.1-misc} ### Sonstiges {#v0.13.1-misc}
* Abhängigkeiten aktualisiert. (b6ae1f5675) * Abhängigkeiten aktualisiert. (b6ae1f5675)
@ -55,7 +178,7 @@ Changelog
> [!NOTE] > [!NOTE]
> Ab dieser Version verhält sich die Berechnung der Unterlieferungen bei Flächenbindungen anders. > Ab dieser Version verhält sich die Berechnung der Unterlieferungen bei Flächenbindungen anders.
### Behobene Fehler {#0.13.0-bugfixes} ### Behobene Fehler {#v0.13.0-bugfixes}
* Im Lieferungen-Fenster (`DeliveryAdminWindow`) war das Extrahieren in eine neue Lieferung seit ca. 6 Monaten (98688168b8) nicht mehr möglich. (8a61747538) * Im Lieferungen-Fenster (`DeliveryAdminWindow`) war das Extrahieren in eine neue Lieferung seit ca. 6 Monaten (98688168b8) nicht mehr möglich. (8a61747538)
@ -132,7 +255,7 @@ Changelog
### Neue Funktionen {#v0.11.2-features} ### Neue Funktionen {#v0.11.2-features}
* In der Anmeldeliste (`DeliveryAncmtList`) wird die Stamm-KG des Mitgliedes angegeben. (165770fa37) * In der Anmeldeliste (`DeliveryAncmtList`) wird die Stamm-KG des Mitgliedes angegeben. (165770fa37)
* Im Hauptmenü wurde im Menüpunkt _Hilfe_ ein Fehler-Protokoll-Fenster (`LogWindow`) hinzugefügt. (526e951029) * Im Haupt-Fenster (`MainWindow`) wurde im Menüpunkt _Hilfe_ ein Fehler-Protokoll-Fenster (`LogWindow`) hinzugefügt. (526e951029)
### Behobene Fehler {#v0.11.2-bugfixes} ### Behobene Fehler {#v0.11.2-bugfixes}
@ -239,7 +362,7 @@ Changelog
### Behobene Fehler {#v0.10.6-bugfixes} ### Behobene Fehler {#v0.10.6-bugfixes}
* Der Titel des Flächenbindungs-Fensters (`AreaComAdminWindow`) ist jetzt _Flächenbindungen_, nicht mehr _Lieferungen_. (ee1315929c) * Der Titel des Flächenbindungen-Fensters (`AreaComAdminWindow`) ist jetzt _Flächenbindungen_, nicht mehr _Lieferungen_. (ee1315929c)
* Im Auszahlungsvariante-Fenster (`ChartWindow`) einen Skalierungs-Fehler behoben. ([#33][i33]) * Im Auszahlungsvariante-Fenster (`ChartWindow`) einen Skalierungs-Fehler behoben. ([#33][i33])
* Versuch: Fehler bei automatischer Daten-Erneuerung bei längerer Benutzung. (8c8c0a8c2b) * Versuch: Fehler bei automatischer Daten-Erneuerung bei längerer Benutzung. (8c8c0a8c2b)
@ -556,7 +679,7 @@ Changelog
### Behobene Fehler {#v0.8.4-bugfixes} ### Behobene Fehler {#v0.8.4-bugfixes}
* Falls beim Schließen des Hauptmenüs ein anderes Fenster im Bearbeiten-/Erstellen-Modus war führte das zu einem Absturz. (70f8276808) * Falls beim Schließen des Haupt-Fensters (`MainWindow`) ein anderes Fenster im Bearbeiten-/Erstellen-Modus war führte das zu einem Absturz. (70f8276808)
### Sonstiges {#v0.8.4-misc} ### Sonstiges {#v0.8.4-misc}

@ -25,9 +25,13 @@ namespace Elwig {
private readonly DispatcherTimer _autoUpdateTimer = new() { Interval = TimeSpan.FromHours(1) }; private readonly DispatcherTimer _autoUpdateTimer = new() { Interval = TimeSpan.FromHours(1) };
public static readonly string DataPath = @"C:\ProgramData\Elwig\"; public static readonly string DataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Elwig");
public static readonly string MailsPath = Path.Combine(DataPath, "mails");
public static readonly string ConfigPath = Path.Combine(DataPath, "config.ini"); public static readonly string ConfigPath = Path.Combine(DataPath, "config.ini");
public static readonly string ExePath = @"C:\Program Files\Elwig\"; public static readonly string InstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Elwig");
public static readonly string DocumentsPath = (Assembly.GetEntryAssembly()?.Location.Contains(@"\bin\") ?? false) ?
Path.Combine(Assembly.GetEntryAssembly()!.Location.Split(@"\bin\")[0], "../Elwig/Documents") :
Path.Combine(InstallPath, "resources/Documents");
public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig"); public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig");
public static Config Config { get; private set; } = new(ConfigPath); public static Config Config { get; private set; } = new(ConfigPath);
@ -56,6 +60,7 @@ namespace Elwig {
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Directory.CreateDirectory(TempPath); Directory.CreateDirectory(TempPath);
Directory.CreateDirectory(DataPath); Directory.CreateDirectory(DataPath);
Directory.CreateDirectory(MailsPath);
MainDispatcher = Dispatcher; MainDispatcher = Dispatcher;
Scales = []; Scales = [];
CurrentApp = this; CurrentApp = this;

@ -2,7 +2,7 @@
@inherits TemplatePage<Elwig.Documents.BusinessDocument> @inherits TemplatePage<Elwig.Documents.BusinessDocument>
@model Elwig.Documents.BusinessDocument @model Elwig.Documents.BusinessDocument
@{ Layout = "Document"; } @{ Layout = "Document"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\BusinessDocument.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\BusinessDocument.css"/>
<div class="info-wrapper"> <div class="info-wrapper">
<div class="address-wrapper"> <div class="address-wrapper">
<div class="sender"> <div class="sender">

@ -3,7 +3,7 @@
@inherits TemplatePage<Elwig.Documents.CreditNote> @inherits TemplatePage<Elwig.Documents.CreditNote>
@model Elwig.Documents.CreditNote @model Elwig.Documents.CreditNote
@{ Layout = "BusinessDocument"; } @{ Layout = "BusinessDocument"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\CreditNote.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\CreditNote.css" />
<main> <main>
<h1>@Model.Title</h1> <h1>@Model.Title</h1>
<table class="credit"> <table class="credit">

@ -2,7 +2,7 @@
@inherits TemplatePage<Elwig.Documents.DeliveryAncmtList> @inherits TemplatePage<Elwig.Documents.DeliveryAncmtList>
@model Elwig.Documents.DeliveryAncmtList @model Elwig.Documents.DeliveryAncmtList
@{ Layout = "Document"; } @{ Layout = "Document"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\DeliveryAncmtList.css" /> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\DeliveryAncmtList.css" />
<main> <main>
<h1>Anmeldeliste</h1> <h1>Anmeldeliste</h1>
<h2>@Model.Filter</h2> <h2>@Model.Filter</h2>
@ -24,7 +24,7 @@
<th rowspan="2" style="text-align: left;">Ort</th> <th rowspan="2" style="text-align: left;">Ort</th>
<th rowspan="2" style="text-align: left;">Sorte</th> <th rowspan="2" style="text-align: left;">Sorte</th>
<th rowspan="2">Anmldg.</th> <th rowspan="2">Anmldg.</th>
<th>Gewicht</th> <th>Menge</th>
</tr> </tr>
<tr> <tr>
<th class="unit">[kg]</th> <th class="unit">[kg]</th>

@ -3,7 +3,7 @@
@inherits TemplatePage<Elwig.Documents.DeliveryConfirmation> @inherits TemplatePage<Elwig.Documents.DeliveryConfirmation>
@model Elwig.Documents.DeliveryConfirmation @model Elwig.Documents.DeliveryConfirmation
@{ Layout = "BusinessDocument"; } @{ Layout = "BusinessDocument"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\DeliveryConfirmation.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\DeliveryConfirmation.css" />
<main> <main>
<h1>@Model.Title</h1> <h1>@Model.Title</h1>
<table class="delivery-confirmation"> <table class="delivery-confirmation">
@ -30,7 +30,7 @@
<th rowspan="2" style="text-align: left;">Qualitätsstufe</th> <th rowspan="2" style="text-align: left;">Qualitätsstufe</th>
<th colspan="2">Gradation</th> <th colspan="2">Gradation</th>
<th colspan="2">Flächenbindung</th> <th colspan="2">Flächenbindung</th>
<th>Gewicht</th> <th>Menge</th>
<th rowspan="3" style="padding: 0;"> <th rowspan="3" style="padding: 0;">
<svg width="10" height="40" xmlns="http://www.w3.org/2000/svg"> <svg width="10" height="40" xmlns="http://www.w3.org/2000/svg">
<text x="-40" y="4" transform="rotate(270)" font-size="8pt" font-style="italic" font-family="Times New Roman" <text x="-40" y="4" transform="rotate(270)" font-size="8pt" font-style="italic" font-family="Times New Roman"

@ -2,7 +2,7 @@
@inherits TemplatePage<Elwig.Documents.DeliveryDepreciationList> @inherits TemplatePage<Elwig.Documents.DeliveryDepreciationList>
@model Elwig.Documents.DeliveryDepreciationList @model Elwig.Documents.DeliveryDepreciationList
@{ Layout = "Document"; } @{ Layout = "Document"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\DeliveryDepreciationList.css" /> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\DeliveryDepreciationList.css" />
<main> <main>
<h1>Abwertungsliste</h1> <h1>Abwertungsliste</h1>
<h2>@Model.Filter</h2> <h2>@Model.Filter</h2>
@ -27,7 +27,7 @@
<th rowspan="2" style="text-align: left;">Sorte</th> <th rowspan="2" style="text-align: left;">Sorte</th>
<th rowspan="2" style="text-align: left;">Attr./Bewirt.</th> <th rowspan="2" style="text-align: left;">Attr./Bewirt.</th>
<th colspan="2">Gradation</th> <th colspan="2">Gradation</th>
<th>Gewicht</th> <th>Menge</th>
</tr> </tr>
<tr> <tr>
<th class="unit">[°Oe]</th> <th class="unit">[°Oe]</th>

@ -2,7 +2,7 @@
@inherits TemplatePage<Elwig.Documents.DeliveryJournal> @inherits TemplatePage<Elwig.Documents.DeliveryJournal>
@model Elwig.Documents.DeliveryJournal @model Elwig.Documents.DeliveryJournal
@{ Layout = "Document"; } @{ Layout = "Document"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\DeliveryJournal.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\DeliveryJournal.css"/>
<main> <main>
<h1>Lieferjournal</h1> <h1>Lieferjournal</h1>
<h2>@Model.Filter</h2> <h2>@Model.Filter</h2>
@ -29,7 +29,7 @@
<th rowspan="2" style="text-align: left;">Mitglied</th> <th rowspan="2" style="text-align: left;">Mitglied</th>
<th rowspan="2" style="text-align: left;">Sorte</th> <th rowspan="2" style="text-align: left;">Sorte</th>
<th colspan="2">Gradation</th> <th colspan="2">Gradation</th>
<th>Gewicht</th> <th>Menge</th>
</tr> </tr>
<tr> <tr>
<th class="unit">[°Oe]</th> <th class="unit">[°Oe]</th>

@ -2,7 +2,7 @@
@inherits TemplatePage<Elwig.Documents.DeliveryNote> @inherits TemplatePage<Elwig.Documents.DeliveryNote>
@model Elwig.Documents.DeliveryNote @model Elwig.Documents.DeliveryNote
@{ Layout = "BusinessDocument"; } @{ Layout = "BusinessDocument"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\DeliveryNote.css" /> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\DeliveryNote.css" />
<main> <main>
<h1>@Model.Title</h1> <h1>@Model.Title</h1>
<table class="delivery large"> <table class="delivery large">
@ -24,7 +24,7 @@
<th class="main" rowspan="2" colspan="2">Attribut</th> <th class="main" rowspan="2" colspan="2">Attribut</th>
<th class="main" rowspan="2">Qualitätsstufe</th> <th class="main" rowspan="2">Qualitätsstufe</th>
<th colspan="2">Gradation</th> <th colspan="2">Gradation</th>
<th>Gewicht</th> <th>Menge</th>
</tr> </tr>
<tr> <tr>
<th class="unit">[°Oe]</th> <th class="unit">[°Oe]</th>

@ -23,7 +23,7 @@ namespace Elwig.Documents {
public bool ShowFoldMarks = App.Config.Debug; public bool ShowFoldMarks = App.Config.Debug;
public bool DoublePaged = false; public bool DoublePaged = false;
public string DataPath; public string DocumentsPath;
public int CurrentNextSeason; public int CurrentNextSeason;
public string? DocumentId; public string? DocumentId;
public string Title; public string Title;
@ -34,7 +34,7 @@ namespace Elwig.Documents {
public Document(string title) { public Document(string title) {
var c = App.Client; var c = App.Client;
DataPath = App.DataPath; DocumentsPath = App.DocumentsPath;
CurrentNextSeason = Utils.CurrentNextSeason; CurrentNextSeason = Utils.CurrentNextSeason;
Title = title; Title = title;
Author = c.NameFull; Author = c.NameFull;

@ -7,9 +7,9 @@
<title>@Model.Title</title> <title>@Model.Title</title>
<meta name="author" value="@Model.Author"/> <meta name="author" value="@Model.Author"/>
<meta charset="UTF-8"/> <meta charset="UTF-8"/>
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\Document.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\Document.css" />
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\Document.Page.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\Document.Page.css" />
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\Document.Table.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\Document.Table.css" />
@if (Model.DoublePaged) { @if (Model.DoublePaged) {
<style> <style>
@@page :left { @@page :left {

@ -3,7 +3,7 @@
@inherits TemplatePage<Elwig.Documents.MemberDataSheet> @inherits TemplatePage<Elwig.Documents.MemberDataSheet>
@model Elwig.Documents.MemberDataSheet @model Elwig.Documents.MemberDataSheet
@{ Layout = "BusinessDocument"; } @{ Layout = "BusinessDocument"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\MemberDataSheet.css" /> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\MemberDataSheet.css" />
<main> <main>
<h1>@Model.Title</h1> <h1>@Model.Title</h1>
<table class="member border"> <table class="member border">

@ -2,7 +2,7 @@
@inherits TemplatePage<Elwig.Documents.MemberList> @inherits TemplatePage<Elwig.Documents.MemberList>
@model Elwig.Documents.MemberList @model Elwig.Documents.MemberList
@{ Layout = "Document"; } @{ Layout = "Document"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\MemberList.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\MemberList.css" />
<main> <main>
<h1>Mitgliederliste</h1> <h1>Mitgliederliste</h1>
<h2>@Model.Filter</h2> <h2>@Model.Filter</h2>

@ -3,7 +3,7 @@
@inherits TemplatePage<Elwig.Documents.PaymentVariantSummary> @inherits TemplatePage<Elwig.Documents.PaymentVariantSummary>
@model Elwig.Documents.PaymentVariantSummary @model Elwig.Documents.PaymentVariantSummary
@{ Layout = "Document"; } @{ Layout = "Document"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\PaymentVariantSummary.css" /> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\PaymentVariantSummary.css" />
<main> <main>
<h1>Auszahlungsvariante Lese @Model.Variant.Year</h1> <h1>Auszahlungsvariante Lese @Model.Variant.Year</h1>
<h2>@Model.Variant.Name</h2> <h2>@Model.Variant.Name</h2>
@ -111,8 +111,8 @@
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(payed):N2}")</td> <td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(payed):N2}")</td>
@{ @{
var weiRows = Model.Data.Rows.Where(r => r.QualityLevel == "Wein"); var weiRows = Model.Data.Rows.Where(r => r.QualityLevel == "Wein");
var minWei = weiRows.Min(r => r.Ungeb.Price); var minWei = weiRows.Min(r => r.Ungeb.MinPrice);
var maxWei = weiRows.Max(r => r.Ungeb.Price); var maxWei = weiRows.Max(r => r.Ungeb.MaxPrice);
} }
<th class="lborder tborder">Preis (abgewertet):</th> <th class="lborder tborder">Preis (abgewertet):</th>
<td colspan="2" class="center tborder">@(minWei != maxWei ? $"{minWei:N4}{maxWei:N4}" : $"{minWei:N4}") @Model.CurrencySymbol/kg</td> <td colspan="2" class="center tborder">@(minWei != maxWei ? $"{minWei:N4}{maxWei:N4}" : $"{minWei:N4}") @Model.CurrencySymbol/kg</td>
@ -123,8 +123,8 @@
<td class="number tborder"><span class="fleft">@Model.CurrencySymbol</span>@($"{netSum:N2}")</td> <td class="number tborder"><span class="fleft">@Model.CurrencySymbol</span>@($"{netSum:N2}")</td>
@{ @{
var quwRows = Model.Data.Rows.Where(r => r.QualityLevel != "Wein"); var quwRows = Model.Data.Rows.Where(r => r.QualityLevel != "Wein");
var minPrice = quwRows.Min(r => r.Ungeb.Price); var minPrice = quwRows.Min(r => r.Ungeb.MinPrice);
var maxPrice = quwRows.Max(r => r.Ungeb.Price); var maxPrice = quwRows.Max(r => r.Ungeb.MaxPrice);
} }
<th class="lborder">Preis (ungeb., nicht abgew.):</th> <th class="lborder">Preis (ungeb., nicht abgew.):</th>
<td colspan="2" class="center">@(minPrice != maxPrice ? $"{minPrice:N4}{maxPrice:N4}" : $"{minPrice:N4}") @Model.CurrencySymbol/kg</td> <td colspan="2" class="center">@(minPrice != maxPrice ? $"{minPrice:N4}{maxPrice:N4}" : $"{minPrice:N4}") @Model.CurrencySymbol/kg</td>
@ -135,8 +135,8 @@
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(vat):N2}")</td> <td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(vat):N2}")</td>
@{ @{
var gebRows = Model.Data.Rows var gebRows = Model.Data.Rows
.Where(r => r.Geb.Price != null && r.Ungeb.Price != null) .Where(r => r.Geb.MaxPrice != null && r.Ungeb.MinPrice != null)
.Select(r => r.Geb.Price - r.Ungeb.Price); .Select(r => r.Geb.MaxPrice - r.Ungeb.MinPrice);
var minGeb = gebRows.Min(); var minGeb = gebRows.Min();
var maxGeb = gebRows.Max(); var maxGeb = gebRows.Max();
} }
@ -219,19 +219,22 @@
</table> </table>
<table class="payment-variant-data"> <table class="payment-variant-data">
<colgroup> <colgroup>
<col style="width: 30mm;"/>
<col style="width: 20mm;"/>
<col style="width: 25mm;"/>
<col style="width: 20mm;"/>
<col style="width: 25mm;"/>
<col style="width: 20mm;"/>
<col style="width: 25mm;"/> <col style="width: 25mm;"/>
<col style="width: 19mm;"/>
<col style="width: 18mm;"/>
<col style="width: 15mm;"/>
<col style="width: 18mm;"/>
<col style="width: 15mm;"/>
<col style="width: 18mm;"/>
<col style="width: 15mm;"/>
<col style="width: 22mm;"/>
</colgroup> </colgroup>
<thead> <thead>
<tr> <tr>
<th rowspan="2" style="text-align: left;">Qualitätsstufe</th> <th rowspan="2" style="text-align: left;">Qualitätsstufe</th>
<th>Gradation</th> <th>Gradation</th>
<th colspan="2">ungebunden</th> <th colspan="2">ungebunden</th>
<th colspan="2">attributlos gebunden</th>
<th colspan="2">gebunden</th> <th colspan="2">gebunden</th>
<th>Gesamt</th> <th>Gesamt</th>
</tr> </tr>
@ -241,6 +244,8 @@
<th>[@(Model.CurrencySymbol)/kg]</th> <th>[@(Model.CurrencySymbol)/kg]</th>
<th>[kg]</th> <th>[kg]</th>
<th>[@(Model.CurrencySymbol)/kg]</th> <th>[@(Model.CurrencySymbol)/kg]</th>
<th>[kg]</th>
<th>[@(Model.CurrencySymbol)/kg]</th>
<th>[@(Model.CurrencySymbol)]</th> <th>[@(Model.CurrencySymbol)]</th>
</tr> </tr>
</thead> </thead>
@ -258,6 +263,8 @@
<th colspan="2">@hdr</th> <th colspan="2">@hdr</th>
<td class="number">@($"{rows.Sum(r => r.Ungeb.Weight):N0}")</td> <td class="number">@($"{rows.Sum(r => r.Ungeb.Weight):N0}")</td>
<td></td> <td></td>
<td class="number">@($"{rows.Sum(r => r.LowGeb.Weight):N0}")</td>
<td></td>
<td class="number">@($"{rows.Sum(r => r.Geb.Weight):N0}")</td> <td class="number">@($"{rows.Sum(r => r.Geb.Weight):N0}")</td>
<td></td> <td></td>
<td class="number">@($"{rows.Sum(r => r.Amount):N2}")</td> <td class="number">@($"{rows.Sum(r => r.Amount):N2}")</td>
@ -267,9 +274,11 @@
<td>@(row.QualityLevel)</td> <td>@(row.QualityLevel)</td>
<td class="center">@($"{row.Oe:N0}")</td> <td class="center">@($"{row.Oe:N0}")</td>
<td class="number">@(row.Ungeb.Weight != 0 ? $"{row.Ungeb.Weight:N0}" : "-")</td> <td class="number">@(row.Ungeb.Weight != 0 ? $"{row.Ungeb.Weight:N0}" : "-")</td>
<td class="number">@(row.Ungeb.Price != null ? $"{row.Ungeb.Price:N4}" : "-")</td> <td class="number">@(row.Ungeb.MaxPrice != null ? $"{row.Ungeb.MaxPrice:N4}" : "-")</td>
<td class="number">@(row.LowGeb.Weight != 0 ? $"{row.LowGeb.Weight:N0}" : "-")</td>
<td class="number">@(row.LowGeb.MaxPrice != null ? $"{row.LowGeb.MaxPrice:N4}" : "-")</td>
<td class="number">@(row.Geb.Weight != 0 ? $"{row.Geb.Weight:N0}" : "-")</td> <td class="number">@(row.Geb.Weight != 0 ? $"{row.Geb.Weight:N0}" : "-")</td>
<td class="number">@(row.Geb.Price != null ? $"{row.Geb.Price:N4}" : "-")</td> <td class="number">@(row.Geb.MaxPrice != null ? $"{row.Geb.MaxPrice:N4}" : "-")</td>
<td class="number">@($"{row.Amount:N2}")</td> <td class="number">@($"{row.Amount:N2}")</td>
</tr> </tr>
lastHdr = hdr; lastHdr = hdr;

@ -3,7 +3,7 @@
@inherits TemplatePage<Elwig.Documents.WineQualityStatistics> @inherits TemplatePage<Elwig.Documents.WineQualityStatistics>
@model Elwig.Documents.WineQualityStatistics @model Elwig.Documents.WineQualityStatistics
@{ Layout = "Document"; } @{ Layout = "Document"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\WineQualityStatistics.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\WineQualityStatistics.css" />
<main> <main>
<h1>Qualitätsstatistik</h1> <h1>Qualitätsstatistik</h1>
<h2>@Model.Filter</h2> <h2>@Model.Filter</h2>

@ -7,7 +7,7 @@
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<PreserveCompilationContext>true</PreserveCompilationContext> <PreserveCompilationContext>true</PreserveCompilationContext>
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon> <ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
<Version>0.13.2</Version> <Version>0.13.9</Version>
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages> <SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ApplicationManifest>app.manifest</ApplicationManifest> <ApplicationManifest>app.manifest</ApplicationManifest>
@ -20,26 +20,22 @@
<EmbeddedResource Include="Resources\Sql\*" /> <EmbeddedResource Include="Resources\Sql\*" />
</ItemGroup> </ItemGroup>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="call fetch-resources.bat" />
</Target>
<ItemGroup> <ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="LinqKit" Version="1.3.0" /> <PackageReference Include="LinqKit" Version="1.3.8" />
<PackageReference Include="MailKit" Version="4.8.0" /> <PackageReference Include="MailKit" Version="4.12.0" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.33" /> <PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.36" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.8" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.8" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="9.0.4" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2792.45" /> <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3179.45" />
<PackageReference Include="NJsonSchema" Version="11.0.2" /> <PackageReference Include="NJsonSchema" Version="11.3.2" />
<PackageReference Include="PdfiumViewer" Version="2.13.0" /> <PackageReference Include="PdfiumViewer" Version="2.13.0" />
<PackageReference Include="PdfiumViewer.Native.x86_64.no_v8-no_xfa" Version="2018.4.8.256" /> <PackageReference Include="PdfiumViewer.Native.x86_64.no_v8-no_xfa" Version="2018.4.8.256" />
<PackageReference Include="RazorLight" Version="2.3.1" /> <PackageReference Include="RazorLight" Version="2.3.1" />
<PackageReference Include="ScottPlot.WPF" Version="5.0.39" /> <PackageReference Include="ScottPlot.WPF" Version="5.0.55" />
<PackageReference Include="System.IO.Ports" Version="8.0.0" /> <PackageReference Include="System.IO.Ports" Version="9.0.4" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" /> <PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.4" />
</ItemGroup> </ItemGroup>
</Project> </Project>

@ -63,10 +63,12 @@ namespace Elwig.Helpers {
public DbSet<PaymentDeliveryPart> PaymentDeliveryParts { get; private set; } public DbSet<PaymentDeliveryPart> PaymentDeliveryParts { get; private set; }
public DbSet<PaymentCustom> CustomPayments { get; private set; } public DbSet<PaymentCustom> CustomPayments { get; private set; }
public DbSet<Credit> Credits { get; private set; } public DbSet<Credit> Credits { get; private set; }
public DbSet<DeliveryPartBucket> DeliveryPartBuckets { get; private set; }
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<MemberDeliveryPerVariantRowSingle> MemberDeliveryPerVariantRows { get; private set; } public DbSet<MemberDeliveryPerVariantRowSingle> MemberDeliveryPerVariantRows { get; private set; }
public DbSet<MemberAreaComsRowSingle> 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; }

@ -299,22 +299,30 @@ namespace Elwig.Helpers.Billing {
return (rev1, rev2); return (rev1, rev2);
} }
protected static void CollapsePaymentData(JsonObject data, IEnumerable<RawVaribute> vaributes, bool useDefault = true) { protected static void CollapsePaymentData(JsonObject data, JsonObject originalData, IEnumerable<RawVaribute> vaributes, bool useDefault = true) {
var (rev1, rev2) = GetReverseKeys(data); var (rev1, rev2) = GetReverseKeys(data);
if (!data.ContainsKey("default")) { if (!data.ContainsKey("default")) {
foreach (var (v, ks) in rev1) { foreach (var (v, ks) in rev1) {
if ((ks.Count >= vaributes.Count() * 0.5 && useDefault) || ks.Count == vaributes.Count()) { if ((ks.Count > vaributes.Count() * 0.5 && useDefault) || ks.Count == vaributes.Count()) {
foreach (var k in ks) data.Remove(k); foreach (var k in ks) {
if (!(originalData[$"{k[..2]}/"]?.AsValue().TryGetValue<string>(out var o) ?? false) || o == v)
if (!(originalData[k.Split('-')[0]]?.AsValue().TryGetValue<string>(out var o2) ?? false) || o2 == v)
data.Remove(k);
}
data["default"] = v; data["default"] = v;
CollapsePaymentData(data, vaributes, useDefault); CollapsePaymentData(data, originalData, vaributes, useDefault);
return; return;
} }
} }
foreach (var (v, ks) in rev2) { foreach (var (v, ks) in rev2) {
if ((ks.Count >= vaributes.Count() * 0.5 && useDefault) || ks.Count == vaributes.Count()) { if ((ks.Count > vaributes.Count() * 0.5 && useDefault) || ks.Count == vaributes.Count()) {
foreach (var k in ks) data.Remove(k); foreach (var k in ks) {
if (!(originalData[$"{k[..2]}/"]?.AsValue().TryGetValue<decimal>(out var o) ?? false) || o == v)
if (!(originalData[k.Split('-')[0]]?.AsValue().TryGetValue<decimal>(out var o2) ?? false) || o2 == v)
data.Remove(k);
}
data["default"] = v; data["default"] = v;
CollapsePaymentData(data, vaributes, useDefault); CollapsePaymentData(data, originalData, vaributes, useDefault);
return; return;
} }
} }
@ -330,16 +338,26 @@ namespace Elwig.Helpers.Billing {
var len = vaributes.Count(e => $"{e.AttrId}{(e.CultId != null && e.CultId != "" ? "-" : "")}{e.CultId}" == idx); var len = vaributes.Count(e => $"{e.AttrId}{(e.CultId != null && e.CultId != "" ? "-" : "")}{e.CultId}" == idx);
foreach (var (v, ks) in rev1) { foreach (var (v, ks) in rev1) {
var myKs = ks.Where(k => k.EndsWith($"/{idx}")).ToList(); var myKs = ks.Where(k => k.EndsWith($"/{idx}")).ToList();
if (myKs.Count > 1 && ((myKs.Count >= len * 0.5 && useDefault) || myKs.Count == len)) { if (myKs.Count > 1 && ((myKs.Count > len * 0.5 && useDefault) || myKs.Count == len)) {
foreach (var k in myKs) data.Remove(k); foreach (var k in myKs) data.Remove(k);
data[(idx.StartsWith('-') && !useDefault ? "" : "/") + idx] = v; var discr = (idx.StartsWith('-') && !useDefault ? "" : "/") + idx;
data[discr] = v;
foreach (var (k, o) in originalData) {
if (o!.AsValue().TryGetValue<string>(out var o2) && o2 != v && k.Contains(discr))
data[k] = o2;
}
} }
} }
foreach (var (v, ks) in rev2) { foreach (var (v, ks) in rev2) {
var myKs = ks.Where(k => k.EndsWith($"/{idx}")).ToList(); var myKs = ks.Where(k => k.EndsWith($"/{idx}")).ToList();
if (myKs.Count > 1 && ((myKs.Count >= len * 0.5 && useDefault) || myKs.Count == len)) { if (myKs.Count > 1 && ((myKs.Count > len * 0.5 && useDefault) || myKs.Count == len)) {
foreach (var k in myKs) data.Remove(k); foreach (var k in myKs) data.Remove(k);
data[(idx.StartsWith('-') && !useDefault ? "" : "/") + idx] = v; var discr = (idx.StartsWith('-') && !useDefault ? "" : "/") + idx;
data[discr] = v;
foreach (var (k, o) in originalData) {
if (o!.AsValue().TryGetValue<decimal>(out var o2) && o2 != v && k.Contains(discr))
data[k] = o2;
}
} }
} }
} }
@ -355,13 +373,22 @@ namespace Elwig.Helpers.Billing {
} else if (k.Contains("/-")) { } else if (k.Contains("/-")) {
data.Remove(k, out var val); data.Remove(k, out var val);
data.Add(k.Replace("/-", "-"), val); data.Add(k.Replace("/-", "-"), val);
if (k[0] == '/' || k.Contains('-')) {
foreach (var (k2, o) in originalData) {
if (!data.ContainsKey(k2) && k2.Contains('-') && k2.Contains("-" + k.Split('-')[1]) && !k2.Contains("/-")
&& (!k2.Contains('/') || k2.Length <= 4 || !data.ContainsKey(k2[2..])))
{
data[k2] = o?.DeepClone();
}
}
}
} }
} }
(rev1, rev2) = GetReverseKeys(data, false); (rev1, rev2) = GetReverseKeys(data, false);
var keyVaributes = data var keyVaributes = data
.Select(e => e.Key.Split('-')[0]) .Select(e => e.Key)
.Where(e => e.Length > 0 && e != "default") .Where(e => e.Length > 0 && !e.Contains('-') && e != "default")
.Distinct() .Distinct()
.ToList(); .ToList();
foreach (var idx in keyVaributes) { foreach (var idx in keyVaributes) {
@ -419,8 +446,8 @@ namespace Elwig.Helpers.Billing {
} }
} }
CollapsePaymentData(payment, vaributes ?? payment.Select(e => new RawVaribute(e.Key)).ToList(), useDefaultPayment); CollapsePaymentData(payment, payment.DeepClone().AsObject(), vaributes ?? payment.Select(e => new RawVaribute(e.Key)).ToList(), useDefaultPayment);
CollapsePaymentData(qualityWei, vaributes ?? qualityWei.Select(e => new RawVaribute(e.Key)).ToList(), useDefaultQuality); CollapsePaymentData(qualityWei, qualityWei.DeepClone().AsObject(), vaributes ?? qualityWei.Select(e => new RawVaribute(e.Key)).ToList(), useDefaultQuality);
var data = new JsonObject { var data = new JsonObject {
["mode"] = "elwig", ["mode"] = "elwig",

@ -46,14 +46,14 @@ namespace Elwig.Helpers.Billing {
m.mgnr, m.mgnr,
v.avnr, v.avnr,
ROUND(p.amount / POW(10, s.precision - 2)) AS net_amount, ROUND(p.amount / POW(10, s.precision - 2)) AS net_amount,
ROUND(lp.amount / POW(10, s.precision - 2)) AS prev_amount, IIF(lc.amount >= 0, ROUND(lp.amount / POW(10, s.precision - 2)), 0) AS prev_net_amount,
IIF(m.buchführend, s.vat_normal, s.vat_flatrate) AS vat, IIF(m.buchführend, s.vat_normal, s.vat_flatrate) AS vat,
ROUND(IIF({Data.ConsiderTotalPenalty}, COALESCE(b.total_penalty, 0), 0) / POW(10, s.precision - 2)) + ROUND(IIF({Data.ConsiderTotalPenalty}, COALESCE(b.total_penalty, 0), 0) / POW(10, s.precision - 2)) +
ROUND(IIF({Data.ConsiderContractPenalties}, COALESCE(u.total_penalty, 0), 0) / POW(10, 4 - 2)) + ROUND(IIF({Data.ConsiderContractPenalties}, COALESCE(u.total_penalty, 0), 0) / POW(10, 4 - 2)) +
ROUND(IIF({Data.ConsiderAutoBusinessShares}, -COALESCE(a.total_amount, 0), 0) / POW(10, s.precision - 2)) + ROUND(IIF({Data.ConsiderAutoBusinessShares}, -COALESCE(a.total_amount, 0), 0) / POW(10, s.precision - 2)) +
IIF({Data.ConsiderCustomModifiers}, COALESCE(x.amount, 0), 0) IIF({Data.ConsiderCustomModifiers}, COALESCE(x.amount, 0), 0)
AS modifiers, AS modifiers,
lc.modifiers AS prev_modifiers IIF(lc.amount >= 0, lc.modifiers, 0) AS prev_modifiers
FROM season s FROM season s
JOIN payment_variant v ON v.year = s.year JOIN payment_variant v ON v.year = s.year
LEFT JOIN payment_variant l ON l.year = s.year LEFT JOIN payment_variant l ON l.year = s.year

@ -23,6 +23,10 @@ namespace Elwig.Helpers {
public bool HasNetWeighing(Branch? b) => HasNetWeighing(b?.ZwstId); public bool HasNetWeighing(Branch? b) => HasNetWeighing(b?.ZwstId);
public bool HasNetWeighing() => HasNetWeighing(App.ZwstId); public bool HasNetWeighing() => HasNetWeighing(App.ZwstId);
public bool HasBoxWeighing(string? zwstId) => IsWinzerkeller && (zwstId != "W");
public bool HasBoxWeighing(Branch? b) => HasBoxWeighing(b?.ZwstId);
public bool HasBoxWeighing() => HasBoxWeighing(App.ZwstId);
public string NameToken; public string NameToken;
public string NameShort; public string NameShort;
public string Name; public string Name;
@ -67,6 +71,12 @@ namespace Elwig.Helpers {
public string? TextEmailSubject; public string? TextEmailSubject;
public string? TextEmailBody; public string? TextEmailBody;
public bool MailIncludeNonDeliverers;
public bool MailDoublePaged;
public int MailSendPostal;
public int MailSendEmail;
public int MailOrdering;
public int ExportEbicsVersion; public int ExportEbicsVersion;
public int ExportEbicsAddress; public int ExportEbicsAddress;
@ -117,7 +127,7 @@ namespace Elwig.Helpers {
case "KMW/5": ModeWineQualityStatistics = 3; break; case "KMW/5": ModeWineQualityStatistics = 3; break;
case "KMW/10": ModeWineQualityStatistics = 4; break; case "KMW/10": ModeWineQualityStatistics = 4; break;
} }
switch (parameters.GetValueOrDefault("ORDERING_MEMBERLIST", "")?.ToUpper()) { switch (parameters.GetValueOrDefault("ORDERING_MEMBERLIST", "MGNR")?.ToUpper()) {
case "MGNR": OrderingMemberList = 0; break; case "MGNR": OrderingMemberList = 0; break;
case "NAME": OrderingMemberList = 1; break; case "NAME": OrderingMemberList = 1; break;
case "KG": OrderingMemberList = 2; break; case "KG": OrderingMemberList = 2; break;
@ -135,6 +145,31 @@ namespace Elwig.Helpers {
TextEmailBody = parameters.GetValueOrDefault("TEXT_EMAIL_BODY"); TextEmailBody = parameters.GetValueOrDefault("TEXT_EMAIL_BODY");
if (TextEmailBody == "") TextEmailBody = null; if (TextEmailBody == "") TextEmailBody = null;
MailIncludeNonDeliverers = (parameters.GetValueOrDefault("MAIL_INCLUDE_NON_DELIVERERS")?.ToUpper()) switch {
"1" or "TRUE" or "YES" or "JA" => true,
_ => false,
};
MailDoublePaged = (parameters.GetValueOrDefault("MAIL_DOUBLE_PAGED")?.ToUpper()) switch {
"1" or "TRUE" or "YES" or "JA" => true,
_ => false,
};
switch (parameters.GetValueOrDefault("MAIL_SEND_POSTAL", "WISH")?.ToUpper()) {
case "ALL": MailSendPostal = 3; break;
case "WISH": MailSendPostal = 2; break;
case "NO_EMAIL": MailSendPostal = 1; break;
case "NONE": MailSendPostal = 0; break;
}
switch (parameters.GetValueOrDefault("MAIL_SEND_EMAIL", "WISH")?.ToUpper()) {
case "ALL": MailSendEmail = 2; break;
case "WISH": MailSendEmail = 1; break;
case "NONE": MailSendEmail = 0; break;
}
switch (parameters.GetValueOrDefault("MAIL_ORDERING", "MGNR")?.ToUpper()) {
case "MGNR": MailOrdering = 0; break;
case "NAME": MailOrdering = 1; break;
case "PLZ": MailOrdering = 2; break;
}
ExportEbicsVersion = int.TryParse(parameters.GetValueOrDefault("EXPORT_EBICS_VERSION"), out var v) ? v : 9; ExportEbicsVersion = int.TryParse(parameters.GetValueOrDefault("EXPORT_EBICS_VERSION"), out var v) ? v : 9;
switch (parameters.GetValueOrDefault("EXPORT_EBICS_ADDRESS", "FULL")?.ToUpper()) { switch (parameters.GetValueOrDefault("EXPORT_EBICS_ADDRESS", "FULL")?.ToUpper()) {
case "OMIT": ExportEbicsAddress = 0; break; case "OMIT": ExportEbicsAddress = 0; break;
@ -177,6 +212,25 @@ namespace Elwig.Helpers {
case 1: orderingMemberList = "NAME"; break; case 1: orderingMemberList = "NAME"; break;
case 2: orderingMemberList = "KG"; break; case 2: orderingMemberList = "KG"; break;
} }
string mailSendPostal = "MGNR";
switch (MailOrdering) {
case 0: mailSendPostal = "NONE"; break;
case 1: mailSendPostal = "NO_EMAIL"; break;
case 2: mailSendPostal = "WISH"; break;
case 3: mailSendPostal = "ALL"; break;
}
string mailSendEmail = "MGNR";
switch (MailOrdering) {
case 0: mailSendEmail = "NONE"; break;
case 1: mailSendEmail = "WISH"; break;
case 2: mailSendEmail = "ALL"; break;
}
string mailOrdering = "MGNR";
switch (MailOrdering) {
case 0: mailOrdering = "MGNR"; break;
case 1: mailOrdering = "NAME"; break;
case 2: mailOrdering = "PLZ"; break;
}
string exportEbicsAddress = "FULL"; string exportEbicsAddress = "FULL";
switch (ExportEbicsAddress) { switch (ExportEbicsAddress) {
case 0: exportEbicsAddress = "OMIT"; break; case 0: exportEbicsAddress = "OMIT"; break;
@ -212,6 +266,11 @@ namespace Elwig.Helpers {
("TEXT_CREDITNOTE", TextCreditNote), ("TEXT_CREDITNOTE", TextCreditNote),
("TEXT_EMAIL_SUBJECT", TextEmailSubject), ("TEXT_EMAIL_SUBJECT", TextEmailSubject),
("TEXT_EMAIL_BODY", TextEmailBody), ("TEXT_EMAIL_BODY", TextEmailBody),
("MAIL_INCLUDE_NON_DELIVERERS", MailIncludeNonDeliverers ? "YES" : "NO"),
("MAIL_DOUBLE_PAGED", MailDoublePaged ? "YES" : "NO"),
("MAIL_SEND_POSTAL", mailSendPostal),
("MAIL_SEND_EMAIL", mailSendEmail),
("MAIL_ORDERING", mailOrdering),
("EXPORT_EBICS_VERSION", ExportEbicsVersion.ToString()), ("EXPORT_EBICS_VERSION", ExportEbicsVersion.ToString()),
("EXPORT_EBICS_ADDRESS", exportEbicsAddress), ("EXPORT_EBICS_ADDRESS", exportEbicsAddress),
("AUTOADJUST_BUSINESSSHARES", autoAdjust), ("AUTOADJUST_BUSINESSSHARES", autoAdjust),

@ -5,6 +5,7 @@ using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Controls.Primitives; using System.Windows.Controls.Primitives;
using System.Windows.Threading;
using Brush = System.Windows.Media.Brush; using Brush = System.Windows.Media.Brush;
using Brushes = System.Windows.Media.Brushes; using Brushes = System.Windows.Media.Brushes;
@ -234,5 +235,21 @@ namespace Elwig.Helpers {
return null; return null;
} }
} }
public static void InitializeDelayTimer(TextBox tb, Action<object, TextChangedEventArgs> handler) {
var timer = new DispatcherTimer {
Interval = TimeSpan.FromMilliseconds(250)
};
timer.Tick += (object? sender, EventArgs evt) => {
timer.Stop();
var (oSender, oEvent) = ((object, TextChangedEventArgs))timer.Tag;
handler(oSender, oEvent);
};
tb.TextChanged += (object sender, TextChangedEventArgs evt) => {
timer.Stop();
timer.Tag = (sender, evt);
timer.Start();
};
}
} }
} }

@ -301,6 +301,7 @@ namespace Elwig.Helpers.Export {
if (units != null && units.Length > 0) { if (units != null && units.Length > 0) {
int n = -1; int n = -1;
switch (units[0]) { switch (units[0]) {
case "#": n = 0; data = $"{v:N0}"; break;
case "%": n = 1; data = $"{v:N1}"; break; case "%": n = 1; data = $"{v:N1}"; break;
case "€": n = 2; data = $"{v:N2}"; break; case "€": n = 2; data = $"{v:N2}"; break;
case "€/kg": n = 4; data = $"{v:N4}"; break; case "€/kg": n = 4; data = $"{v:N4}"; break;
@ -316,7 +317,7 @@ namespace Elwig.Helpers.Export {
c = $"<{ct} office:value-type=\"string\" calcext:value-type=\"string\"{add}><text:p>{SecurityElement.Escape(data.ToString())}</text:p></{ct}>"; c = $"<{ct} office:value-type=\"string\" calcext:value-type=\"string\"{add}><text:p>{SecurityElement.Escape(data.ToString())}</text:p></{ct}>";
} }
return $" {c}\r\n" + (colSpan > 1 ? $" <table:covered-table-cell table:number-rows-repeated=\"{colSpan - 1}\"/>\r\n" : ""); return $" {c}\r\n" + (colSpan > 1 ? $" <table:covered-table-cell table:number-columns-repeated=\"{colSpan - 1}\"/>\r\n" : "");
} }
} }
} }

@ -10,7 +10,7 @@ namespace Elwig.Helpers.Printing {
public static async Task Init(Action? evtHandler = null) { public static async Task Init(Action? evtHandler = null) {
var e = new RazorLightEngineBuilder() var e = new RazorLightEngineBuilder()
.UseFileSystemProject(App.DataPath + "resources") .UseFileSystemProject(App.DocumentsPath)
.UseMemoryCachingProvider() .UseMemoryCachingProvider()
.Build(); .Build();

@ -14,7 +14,7 @@ using System.Drawing.Printing;
namespace Elwig.Helpers.Printing { namespace Elwig.Helpers.Printing {
public static class Pdf { public static class Pdf {
private static readonly string WinziPrint = new string[] { App.ExePath } private static readonly string WinziPrint = new string[] { App.InstallPath }
.Union(Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? []) .Union(Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? [])
.Select(x => Path.Combine(x, "WinziPrint.exe")) .Select(x => Path.Combine(x, "WinziPrint.exe"))
.Where(File.Exists) .Where(File.Exists)

@ -434,8 +434,7 @@ namespace Elwig.Helpers {
if (accept != null) if (accept != null)
client.DefaultRequestHeaders.Accept.Add(new(accept)); client.DefaultRequestHeaders.Accept.Add(new(accept));
if (username != null || password != null) if (username != null || password != null)
client.DefaultRequestHeaders.Authorization = new("Basic", Convert.ToBase64String( client.DefaultRequestHeaders.Authorization = new("Basic", Convert.ToBase64String(Utils.UTF8.GetBytes($"{username}:{password}")));
Utils.UTF8.GetBytes($"{username}:{password}")));
return client; return client;
} }
@ -454,6 +453,8 @@ namespace Elwig.Helpers {
} }
public static async Task UploadExportData(string zip, string url, string username, string password) { public static async Task UploadExportData(string zip, string url, string username, string password) {
if (url.StartsWith("https://elwig.at/clients/"))
url = "https://sync.elwig.at/" + url[25..];
if (!url.EndsWith('/')) url += "/"; if (!url.EndsWith('/')) url += "/";
using var client = GetHttpClient(username, password, accept: "application/json"); using var client = GetHttpClient(username, password, accept: "application/json");
var content = new StreamContent(new FileStream(zip, FileMode.Open, FileAccess.Read)); var content = new StreamContent(new FileStream(zip, FileMode.Open, FileAccess.Read));
@ -463,6 +464,8 @@ namespace Elwig.Helpers {
} }
public static async Task<JsonArray> GetExportMetaData(string url, string username, string password) { public static async Task<JsonArray> GetExportMetaData(string url, string username, string password) {
if (url.StartsWith("https://elwig.at/clients/"))
url = "https://sync.elwig.at/" + url[25..];
using var client = GetHttpClient(username, password, accept: "application/json"); using var client = GetHttpClient(username, password, accept: "application/json");
using var res = await client.GetAsync(url); using var res = await client.GetAsync(url);
res.EnsureSuccessStatusCode(); res.EnsureSuccessStatusCode();
@ -594,5 +597,76 @@ namespace Elwig.Helpers {
public static IEnumerable<AreaCom> ActiveAreaCommitments(IEnumerable<AreaCom> query) => ActiveAreaCommitments(query, CurrentYear); public static IEnumerable<AreaCom> ActiveAreaCommitments(IEnumerable<AreaCom> query) => ActiveAreaCommitments(query, CurrentYear);
public static IEnumerable<AreaCom> ActiveAreaCommitments(IEnumerable<AreaCom> query, int year) => public static IEnumerable<AreaCom> ActiveAreaCommitments(IEnumerable<AreaCom> query, int year) =>
query.Where(c => ActiveAreaCommitments(year).Invoke(c)); query.Where(c => ActiveAreaCommitments(year).Invoke(c));
public static async Task<(DateTime DateTime, string Type, int MgNr, string Name, string[] Addresses, string Subject, string[] Attachments)[]> GetSentMails(DateOnly? fromDate = null, DateOnly? toDate = null) {
var f = $"{fromDate:yyyy-MM-dd}";
var t = $"{toDate:yyyy-MM-dd}";
try {
var rows = new List<(DateTime, string, int, string, string[], string, string[])>();
var filenames = Directory.GetFiles(App.MailsPath, "????.csv")
.Where(n => fromDate == null || Path.GetFileName(n).CompareTo($"{fromDate.Value.Year}.csv") >= 0)
.Where(n => toDate == null || Path.GetFileName(n).CompareTo($"{toDate.Value.Year}.csv") <= 0)
.Order();
foreach (var filename in filenames) {
using var reader = new StreamReader(filename, Utils.UTF8);
string? line;
while ((line = await reader.ReadLineAsync()) != null) {
try {
if (line.Length < 20 || line[10] != ';' || line[19] != ';')
continue;
var date = line[..10];
if (fromDate != null && date.CompareTo(f) < 0) {
continue;
} else if (toDate != null && date.CompareTo(t) > 0) {
break;
}
var p = line.Split(';');
rows.Add((
DateOnly.ParseExact(p[0], "yyyy-MM-dd").ToDateTime(TimeOnly.ParseExact(p[1], "HH:mm:ss")),
p[2],
int.Parse(p[3]),
p[4],
p[5].Split(',').Select(a => a.Replace(" | ", "\n")).ToArray(),
p[6],
p[7].Split(',')
));
} catch {
continue;
}
}
}
return [.. rows];
} catch {
return [];
}
}
public static async Task AddSentMails(IEnumerable<(string Type, int MgNr, string Name, string[] Addresses, string Subject, string[] Attachments)> data) {
var now = DateTime.Now;
var filename = Path.Combine(App.MailsPath, $"{now.Year}.csv");
await File.AppendAllLinesAsync(filename, data.Select(d =>
$"{now:yyyy-MM-dd;HH:mm:ss};{d.Type};" +
$"{d.MgNr};{d.Name.Replace(';', ' ')};" +
$"{string.Join(',', d.Addresses.Select(a => a.Replace(';', ' ').Replace(',', ' ').Replace("\n", " | ")))};" +
$"{d.Subject.Replace(';', ' ')};" +
$"{string.Join(',', d.Attachments.Select(a => a.Replace(';', ' ').Replace(',', ' ')))}"
), Utils.UTF8);
}
public static async Task<string?> FindSentMailBody(DateTime target) {
var dt = $"{target:yyyy-MM-dd_HH-mm-ss}_";
var filename = Directory.GetFiles(App.MailsPath, "????-??-??_??-??-??_*.txt")
.Where(n => Path.GetFileName(n).CompareTo(dt) <= 0)
.Order()
.LastOrDefault();
if (filename == null)
return null;
return await File.ReadAllTextAsync(filename, Utils.UTF8);
}
public static async Task AddSentMailBody(string subject, string body, int recipients) {
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);
}
} }
} }

@ -45,7 +45,7 @@ namespace Elwig.Models.Dtos {
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
JOIN v_under_delivery u ON (u.mgnr, u.bucket, u.year) = (m.mgnr, c.bucket, c.year) JOIN v_under_delivery u ON (u.mgnr, u.bucket, u.year) = (m.mgnr, c.bucket, c.year)
WHERE c.year = {year} AND m.active = 1 WHERE c.year = {year}
ORDER BY m.mgnr, c.bucket ORDER BY m.mgnr, c.bucket
""").ToListAsync(); """).ToListAsync();
} }

@ -27,14 +27,14 @@ namespace Elwig.Models.Dtos {
MgNr = mgnr; MgNr = mgnr;
} }
public static async Task<IDictionary<int, CreditNoteDeliveryData>> ForPaymentVariant(DbSet<CreditNoteDeliveryRowSingle> table, DbSet<Season> seasons, int year, int avnr) { public static async Task<IDictionary<int, CreditNoteDeliveryData>> ForPaymentVariant(DbSet<CreditNoteDeliveryRowSingle> table, DbSet<PaymentVar> paymentVariants, int year, int avnr) {
var variant = (await seasons.FindAsync(year))?.PaymentVariants.Where(v => v.AvNr == avnr).SingleOrDefault(); var variant = await paymentVariants.FindAsync(year, avnr);
BillingData? varData = null; BillingData? varData = null;
try { varData = variant != null ? BillingData.FromJson(variant.Data) : null; } catch { } try { varData = variant != null ? BillingData.FromJson(variant.Data) : null; } catch { }
return (await FromDbSet(table, year, avnr)) return (await FromDbSet(table, year, avnr))
.GroupBy( .GroupBy(
r => new { r.Year, r.AvNr, r.MgNr, r.TgNr, r.DId, r.DPNr }, r => new { r.Year, r.AvNr, r.MgNr, r.TgNr, r.DId, r.DPNr },
(k, g) => new CreditNoteDeliveryRow(g, seasons, varData?.NetWeightModifier ?? 0.0, varData?.GrossWeightModifier ?? 0.0)) (k, g) => new CreditNoteDeliveryRow(g, variant, varData))
.GroupBy( .GroupBy(
r => new { r.Year, r.AvNr, r.MgNr, r.TgNr }, r => new { r.Year, r.AvNr, r.MgNr, r.TgNr },
(k, g) => new CreditNoteDeliveryData(g, k.Year, k.TgNr, mgnr: k.MgNr)) (k, g) => new CreditNoteDeliveryData(g, k.Year, k.TgNr, mgnr: k.MgNr))
@ -86,13 +86,13 @@ namespace Elwig.Models.Dtos {
public decimal? Amount; public decimal? Amount;
public double WeighingModifier; public double WeighingModifier;
public CreditNoteDeliveryRow(IEnumerable<CreditNoteDeliveryRowSingle> rows, DbSet<Season> seasons, double netWeightModifier, double grossWeightModifier) { public CreditNoteDeliveryRow(IEnumerable<CreditNoteDeliveryRowSingle> rows, PaymentVar paymentVariant, BillingData? varData) {
var f = rows.First(); var f = rows.First();
Year = f.Year; Year = f.Year;
TgNr = f.TgNr; TgNr = f.TgNr;
AvNr = f.AvNr; AvNr = f.AvNr;
MgNr = f.MgNr; MgNr = f.MgNr;
var season = seasons.Find(Year); var season = paymentVariant.Season;
LsNr = f.LsNr; LsNr = f.LsNr;
DPNr = f.DPNr; DPNr = f.DPNr;
@ -115,7 +115,7 @@ namespace Elwig.Models.Dtos {
b.Price != null ? season?.DecFromDb((long)b.Price) : null, b.Price != null ? season?.DecFromDb((long)b.Price) : null,
b.Amount != null ? season?.DecFromDb((long)b.Amount) : null)) b.Amount != null ? season?.DecFromDb((long)b.Amount) : null))
.ToArray(); .ToArray();
WeighingModifier = f.NetWeight ? netWeightModifier : grossWeightModifier; WeighingModifier = (varData == null || !varData.ConsiderDelieryModifiers) ? 0 : f.NetWeight ? varData.NetWeightModifier : varData.GrossWeightModifier;
Amount = f.TotalAmount != null ? season?.DecFromDb((long)f.TotalAmount) : null; Amount = f.TotalAmount != null ? season?.DecFromDb((long)f.TotalAmount) : null;
var netAmount = f.NetAmount != null ? season?.DecFromDb((long)f.NetAmount) : null; var netAmount = f.NetAmount != null ? season?.DecFromDb((long)f.NetAmount) : null;
var amt = netAmount * (decimal)(1.0 + WeighingModifier); var amt = netAmount * (decimal)(1.0 + WeighingModifier);

@ -17,7 +17,7 @@ namespace Elwig.Models.Dtos {
("Name2", "Vorname", null, 40), ("Name2", "Vorname", null, 40),
("DefaultKg", "Ort", null, 40), ("DefaultKg", "Ort", null, 40),
("SortId", "Sorte", null, 10), ("SortId", "Sorte", null, 10),
("Weight", "Gewicht", "kg", 20), ("Weight", "Menge", "kg", 20),
("CreatedTimestamp", "Angemeldet", null, 35), ("CreatedTimestamp", "Angemeldet", null, 35),
("ModifiedTimestamp", "Geändert", null, 35), ("ModifiedTimestamp", "Geändert", null, 35),
("Status", "Status", null, 20), ("Status", "Status", null, 20),

@ -16,7 +16,7 @@ namespace Elwig.Models.Dtos {
("QualityLevel", "Qualitätsstufe", null, 25), ("QualityLevel", "Qualitätsstufe", null, 25),
("Gradation", "Gradation", "°Oe|°KMW", 32), ("Gradation", "Gradation", "°Oe|°KMW", 32),
("Buckets", "Flächenbindung", "|kg", 36), ("Buckets", "Flächenbindung", "|kg", 36),
("Weight", "Gewicht", "kg", 16), ("Weight", "Menge", "kg", 16),
]; ];
private readonly int MgNr; private readonly int MgNr;

@ -24,7 +24,7 @@ namespace Elwig.Models.Dtos {
("CultId", "Bewirt.", null, 15), ("CultId", "Bewirt.", null, 15),
("QualId", "Qualität", null, 15), ("QualId", "Qualität", null, 15),
("Gradation", "Gradation", "°Oe|°KMW", 40), ("Gradation", "Gradation", "°Oe|°KMW", 40),
("Weight", "Gewicht", "kg", 20), ("Weight", "Menge", "kg", 20),
("IsNetWeight", "Gerebelt", null, 20), ("IsNetWeight", "Gerebelt", null, 20),
("HkId", "Herkunft", null, 20), ("HkId", "Herkunft", null, 20),
("Modifiers", "Zu-/Abschläge", null, 40), ("Modifiers", "Zu-/Abschläge", null, 40),

@ -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 MemberAreaComsData : DataTable<MemberAreaComsRow> {
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 MemberAreaComsData(IEnumerable<MemberAreaComsRow> rows, int year) :
base($"Flächenbindungen", $"Flächenbindungen pro Mitglied {year}", rows, FieldNames) {
}
public static async Task<MemberAreaComsData> ForSeason(DbSet<MemberAreaComsRowSingle> table, int year) {
return new MemberAreaComsData(
(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) {
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 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]
public class MemberAreaComsRowSingle {
[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; }
}
}

@ -1,12 +1,11 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Elwig.Models.Dtos { namespace Elwig.Models.Dtos {
public class MemberDeliveryPerVariantData : DataTable<MemberDeliveryPerVariantRow> { public class MemberDeliveryPerVarietyData : DataTable<MemberDeliveryPerVariantRow> {
private static readonly (string, string, string?, int)[] FieldNames = [ private static readonly (string, string, string?, int)[] FieldNames = [
("MgNr", "MgNr.", null, 12), ("MgNr", "MgNr.", null, 12),
@ -22,13 +21,12 @@ namespace Elwig.Models.Dtos {
("Yields", "Ertrag", "kg/ha", 22), ("Yields", "Ertrag", "kg/ha", 22),
]; ];
public MemberDeliveryPerVarietyData(IEnumerable<MemberDeliveryPerVariantRow> rows, int year) :
public MemberDeliveryPerVariantData(IEnumerable<MemberDeliveryPerVariantRow> rows, int year) :
base($"Liefermengen", $"Liefermengen pro Mitglied, Sorte und Attribut {year}", rows, FieldNames) { base($"Liefermengen", $"Liefermengen pro Mitglied, Sorte und Attribut {year}", rows, FieldNames) {
} }
public static async Task<MemberDeliveryPerVariantData> ForSeason(DbSet<MemberDeliveryPerVariantRowSingle> table, int year) { public static async Task<MemberDeliveryPerVarietyData> ForSeason(DbSet<MemberDeliveryPerVariantRowSingle> table, int year) {
return new MemberDeliveryPerVariantData( return new MemberDeliveryPerVarietyData(
(await FromDbSet(table, year)).GroupBy( (await FromDbSet(table, year)).GroupBy(
r => r.MgNr, r => r.MgNr,
(k, g) => new MemberDeliveryPerVariantRow(g) (k, g) => new MemberDeliveryPerVariantRow(g)

@ -33,14 +33,13 @@ namespace Elwig.Models.Dtos {
m.business_shares * s.min_kg_per_bs AS min_kg, m.business_shares * s.min_kg_per_bs AS min_kg,
m.business_shares * s.max_kg_per_bs AS max_kg, m.business_shares * s.max_kg_per_bs AS max_kg,
COALESCE(SUM(d.weight), 0) AS sum COALESCE(SUM(d.weight), 0) AS sum
FROM v_delivery d FROM season s, member m
LEFT JOIN member m ON m.mgnr = d.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
LEFT JOIN season s ON s.year = d.year LEFT JOIN v_delivery d ON (d.year, d.mgnr) = (s.year, m.mgnr)
WHERE s.year = {year} AND (m.active = TRUE OR d.weight > 0) WHERE s.year = {year} AND (m.active = TRUE OR d.weight > 0)
GROUP BY d.year, m.mgnr GROUP BY s.year, m.mgnr
ORDER BY 100.0 * sum / max_kg, m.mgnr; ORDER BY 100.0 * sum / max_kg, m.mgnr
""").ToListAsync(); """).ToListAsync();
return new OverUnderDeliveryData(rows, year); return new OverUnderDeliveryData(rows, year);
} }

@ -18,8 +18,9 @@ namespace Elwig.Models.Dtos {
("Cultivation", "Bewirt.", null, 20), ("Cultivation", "Bewirt.", null, 20),
("QualityLevel", "Qualitätsstufe", null, 30), ("QualityLevel", "Qualitätsstufe", null, 30),
("Oe", "Gradation", "°Oe", 20), ("Oe", "Gradation", "°Oe", 20),
("Ungeb", "ungebunden", "kg|€/kg", 40), ("Ungeb", "ungebunden", "kg|€/kg|€/kg", 60),
("Geb", "gebunden", "kg|€/kg", 40), ("LowGeb", "attributlos gebunden", "kg|€/kg|€/kg", 60),
("Geb", "gebunden", "kg|€/kg|€/kg", 60),
("Amount", "Gesamt", "€", 25), ("Amount", "Gesamt", "€", 25),
]; ];
@ -30,8 +31,9 @@ namespace Elwig.Models.Dtos {
string? Cultivation, string? Cultivation,
string QualityLevel, string QualityLevel,
double Oe, double Oe,
(int Weight, decimal? Price) Ungeb, (int Weight, decimal? MinPrice, decimal? MaxPrice) Ungeb,
(int Weight, decimal? Price) Geb, (int Weight, decimal? MinPrice, decimal? MaxPrice) LowGeb,
(int Weight, decimal? MinPrice, decimal? MaxPrice) Geb,
decimal Amount decimal Amount
); );
@ -42,8 +44,9 @@ namespace Elwig.Models.Dtos {
public static async Task<PaymentVariantSummaryData> ForPaymentVariant(PaymentVar v, DbSet<PaymentVariantSummaryRow> table) { public static async Task<PaymentVariantSummaryData> ForPaymentVariant(PaymentVar v, DbSet<PaymentVariantSummaryRow> table) {
return new(v, (await FromDbSet(table, v.Year, v.AvNr)) return new(v, (await FromDbSet(table, v.Year, v.AvNr))
.Select(r => new PaymentRow(r.Type, r.Variety, r.Attribute, r.Cultivation, r.QualityLevel, r.Oe, .Select(r => new PaymentRow(r.Type, r.Variety, r.Attribute, r.Cultivation, r.QualityLevel, r.Oe,
(r.WeightUngeb, r.PriceUngeb != null ? Utils.DecFromDb(r.PriceUngeb.Value, v.Season.Precision) : null), (r.WeightUngeb, r.MinPriceUngeb != null ? Utils.DecFromDb(r.MinPriceUngeb.Value, v.Season.Precision) : null, r.MaxPriceUngeb != null ? Utils.DecFromDb(r.MaxPriceUngeb.Value, v.Season.Precision) : null),
(r.WeightGeb, r.PriceGeb != null ? Utils.DecFromDb(r.PriceGeb.Value, v.Season.Precision) : null), (r.WeightLowGeb, r.MinPriceLowGeb != null ? Utils.DecFromDb(r.MinPriceLowGeb.Value, v.Season.Precision) : null, r.MaxPriceLowGeb != null ? Utils.DecFromDb(r.MaxPriceLowGeb.Value, v.Season.Precision) : null),
(r.WeightGeb, r.MinPriceGeb != null ? Utils.DecFromDb(r.MinPriceGeb.Value, v.Season.Precision) : null, r.MaxPriceGeb != null ? Utils.DecFromDb(r.MaxPriceGeb.Value, v.Season.Precision) : null),
Utils.DecFromDb(r.Amount, v.Season.Precision))) Utils.DecFromDb(r.Amount, v.Season.Precision)))
.ToArray()); .ToArray());
} }
@ -57,19 +60,23 @@ namespace Elwig.Models.Dtos {
q.name AS quality_level, q.name AS quality_level,
ROUND(kmw * (4.54 + 0.022 * kmw)) AS oe, ROUND(kmw * (4.54 + 0.022 * kmw)) AS oe,
SUM(IIF(w.discr = '_', w.value, 0)) AS weight_ungeb, SUM(IIF(w.discr = '_', w.value, 0)) AS weight_ungeb,
MAX(IIF(w.discr = '_', b.price, NULL)) AS price_ungeb, MIN(IIF(w.discr = '_', b.price, NULL)) AS min_price_ungeb,
SUM(IIF(w.discr != '_', w.value, 0)) AS weight_geb, MAX(IIF(w.discr = '_', b.price, NULL)) AS max_price_ungeb,
MAX(IIF(w.discr != '_', b.price, NULL)) AS price_geb, SUM(IIF(w.discr NOT IN (COALESCE(p.attrid, ''), '_'), w.value, 0)) AS weight_lowgeb,
MIN(IIF(w.discr NOT IN (COALESCE(p.attrid, ''), '_'), b.price, NULL)) AS min_price_lowgeb,
MAX(IIF(w.discr NOT IN (COALESCE(p.attrid, ''), '_'), b.price, NULL)) AS max_price_lowgeb,
SUM(IIF(w.discr = COALESCE(p.attrid, ''), w.value, 0)) AS weight_geb,
MIN(IIF(w.discr = COALESCE(p.attrid, ''), b.price, NULL)) AS min_price_geb,
MAX(IIF(w.discr = COALESCE(p.attrid, ''), b.price, NULL)) AS max_price_geb,
SUM(b.amount) AS amount SUM(b.amount) AS amount
FROM payment_delivery_part_bucket b FROM payment_delivery_part_bucket b
LEFT JOIN delivery_part_bucket w ON (w.year, w.did, w.dpnr, w.bktnr) = (b.year, b.did, b.dpnr, b.bktnr) LEFT JOIN delivery_part_bucket w ON (w.year, w.did, w.dpnr, w.bktnr) = (b.year, b.did, b.dpnr, b.bktnr)
LEFT JOIN delivery_part p ON (p.year, p.did, p.dpnr) = (b.year, b.did, b.dpnr) LEFT JOIN delivery_part p ON (p.year, p.did, p.dpnr) = (b.year, b.did, b.dpnr)
LEFT JOIN delivery d ON (d.year, d.did) = (p.year, p.did)
LEFT JOIN wine_variety v ON v.sortid = p.sortid LEFT JOIN wine_variety v ON v.sortid = p.sortid
LEFT JOIN wine_attribute a ON a.attrid = p.attrid LEFT JOIN wine_attribute a ON a.attrid = p.attrid
LEFT JOIN wine_cultivation c ON c.cultid = p.cultid LEFT JOIN wine_cultivation c ON c.cultid = p.cultid
LEFT JOIN wine_quality_level q ON q.qualid = p.qualid LEFT JOIN wine_quality_level q ON q.qualid = p.qualid
WHERE d.year = {year} AND b.avnr = {avnr} WHERE p.year = {year} AND b.avnr = {avnr}
GROUP BY variety, attribute, cultivation, q.min_kmw, oe GROUP BY variety, attribute, cultivation, q.min_kmw, oe
ORDER BY variety, attribute, cultivation, q.min_kmw, oe ORDER BY variety, attribute, cultivation, q.min_kmw, oe
""").ToListAsync(); """).ToListAsync();
@ -92,12 +99,22 @@ namespace Elwig.Models.Dtos {
public double Oe { get; set; } public double Oe { get; set; }
[Column("weight_ungeb")] [Column("weight_ungeb")]
public int WeightUngeb { get; set; } public int WeightUngeb { get; set; }
[Column("price_ungeb")] [Column("min_price_ungeb")]
public long? PriceUngeb { get; set; } public long? MinPriceUngeb { get; set; }
[Column("max_price_ungeb")]
public long? MaxPriceUngeb { get; set; }
[Column("weight_lowgeb")]
public int WeightLowGeb { get; set; }
[Column("min_price_lowgeb")]
public long? MinPriceLowGeb { get; set; }
[Column("max_price_lowgeb")]
public long? MaxPriceLowGeb { get; set; }
[Column("weight_geb")] [Column("weight_geb")]
public int WeightGeb { get; set; } public int WeightGeb { get; set; }
[Column("price_geb")] [Column("min_price_geb")]
public long? PriceGeb { get; set; } public long? MinPriceGeb { get; set; }
[Column("max_price_geb")]
public long? MaxPriceGeb { get; set; }
[Column("amount")] [Column("amount")]
public long Amount { get; set; } public long Amount { get; set; }
} }

@ -15,7 +15,7 @@ namespace Elwig.Models.Dtos {
public static IEnumerable<Transaction> FromPaymentVariant(PaymentVar variant) { public static IEnumerable<Transaction> FromPaymentVariant(PaymentVar variant) {
return variant.Credits return variant.Credits
.Where(c => c.Member.Iban != null) .Where(c => c.Member.Iban != null && c.Amount > 0)
.OrderBy(c => c.TgNr) .OrderBy(c => c.TgNr)
.Select(c => new Transaction(c)) .Select(c => new Transaction(c))
.ToList(); .ToList();

@ -14,7 +14,7 @@ namespace Elwig.Models.Dtos {
("CultId", "Bewirt.", null, 15), ("CultId", "Bewirt.", null, 15),
("QualId", "Qual.", null, 15), ("QualId", "Qual.", null, 15),
("Geb", "gebunden", null, 20), ("Geb", "gebunden", null, 20),
("Weight", "Gewicht", "kg", 20), ("Weight", "Menge", "kg", 20),
]; ];
public WeightBreakdownData(IEnumerable<WeightBreakdownRow> rows, int year, string name) : public WeightBreakdownData(IEnumerable<WeightBreakdownRow> rows, int year, string name) :

@ -0,0 +1,60 @@
using Elwig.Helpers;
using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using static Elwig.Models.Dtos.WineLocalityStatisticsData;
namespace Elwig.Models.Dtos {
class WineLocalityStatisticsData : DataTable<StatisticsRow> {
private static readonly (string, string, string?, int?)[] FieldNames = [
("Branch", "Zwst.", null, 30),
("KgNr", "KgNr.", null, 15),
("Name", "Katastralgemeinde", null, 50),
("Members", "Mitgl.", "#", 15),
("Deliveries", "Lfrg.", "#", 15),
("Parts", "Teill.", "#", 15),
("Weight", "Menge", "kg", 20),
("Gradation", "Gradation", "°Oe|°KMW", 30),
];
public record struct StatisticsRow(
string Branch,
int? KgNr,
string? Name,
int Members,
int Deliveries,
int Parts,
int Weight,
(double Oe, double Kmw) Gradation
);
public WineLocalityStatisticsData(IEnumerable<StatisticsRow> rows, List<string> filterNames) :
base("Lieferstatistik pro Ort", "Lieferstatistik pro Ort", string.Join(" / ", filterNames), rows, FieldNames) {
}
public static async Task<WineLocalityStatisticsData> FromQuery(IQueryable<DeliveryPart> query, List<string> filterNames) {
return new((await query
.GroupBy(p => new {
Branch = p.Delivery.Branch.Name,
p.Kg!.KgNr,
Kg = p.Kg!.AtKg.Name,
}, (k, g) => new {
k.Branch,
KgNr = (int?)k.KgNr,
Kg = (string?)k.Kg,
Members = g.Select(p => p.Delivery.Member).Distinct().Count(),
Deliveries = g.Select(p => p.Delivery).Distinct().Count(),
Parts = g.Count(),
Weight = g.Sum(p => p.Weight),
Kmw = g.Sum(p => p.Kmw * p.Weight) / g.Sum(p => p.Weight),
})
.OrderByDescending(g => g.Weight)
.ThenBy(g => g.KgNr)
.ToListAsync()).Select(g => new StatisticsRow(g.Branch, g.KgNr, g.Kg, g.Members, g.Deliveries, g.Parts, g.Weight, (Utils.KmwToOe(g.Kmw), Math.Round(g.Kmw, 1)))), filterNames);
}
}
}

@ -48,6 +48,7 @@ namespace Elwig.Services {
var filterNotVar = new List<string>(); var filterNotVar = new List<string>();
var filterAttr = new List<string>(); var filterAttr = new List<string>();
var filterNotAttr = new List<string>(); var filterNotAttr = new List<string>();
var filterSeasons = new List<int>();
var filter = vm.TextFilter; var filter = vm.TextFilter;
if (filter.Count > 0) { if (filter.Count > 0) {
@ -87,6 +88,10 @@ namespace Elwig.Services {
filter.RemoveAt(i--); filter.RemoveAt(i--);
filterNames.Add($"ohne {var[e[1..3].ToUpper()].Name}"); filterNames.Add($"ohne {var[e[1..3].ToUpper()].Name}");
filterNames.Add($"ohne Attribut {attrId[e[3..].ToUpper()].Name}"); filterNames.Add($"ohne Attribut {attrId[e[3..].ToUpper()].Name}");
} else if (e.Length == 4 && int.TryParse(e, out var year)) {
filterSeasons.Add(year);
filter.RemoveAt(i--);
filterNames.Add($"laufend {e}");
} }
} }
@ -94,6 +99,7 @@ namespace Elwig.Services {
if (filterNotVar.Count > 0) areaComQuery = areaComQuery.Where(a => !filterNotVar.Contains(a.AreaComType.WineVar.SortId)); if (filterNotVar.Count > 0) areaComQuery = areaComQuery.Where(a => !filterNotVar.Contains(a.AreaComType.WineVar.SortId));
if (filterAttr.Count > 0) areaComQuery = areaComQuery.Where(a => a.AreaComType.WineAttr!.AttrId != null && filterAttr.Contains(a.AreaComType.WineAttr.AttrId)); if (filterAttr.Count > 0) areaComQuery = areaComQuery.Where(a => a.AreaComType.WineAttr!.AttrId != null && filterAttr.Contains(a.AreaComType.WineAttr.AttrId));
if (filterNotAttr.Count > 0) areaComQuery = areaComQuery.Where(a => a.AreaComType.WineAttr!.AttrId == null || !filterNotAttr.Contains(a.AreaComType.WineAttr.AttrId)); if (filterNotAttr.Count > 0) areaComQuery = areaComQuery.Where(a => a.AreaComType.WineAttr!.AttrId == null || !filterNotAttr.Contains(a.AreaComType.WineAttr.AttrId));
foreach (var year in filterSeasons) areaComQuery = Utils.ActiveAreaCommitments(areaComQuery, year);
} }
return (filterNames, areaComQuery, filter); return (filterNames, areaComQuery, filter);

@ -326,7 +326,7 @@ namespace Elwig.Services {
var weight = await deliveryAncmts.SumAsync(p => p.Weight); var weight = await deliveryAncmts.SumAsync(p => p.Weight);
text = $"{weight:N0} kg"; text = $"{weight:N0} kg";
AddToolTipRow(grid, 0, "Gewicht", null, weight, null, weight); AddToolTipRow(grid, 0, "Menge", null, weight, null, weight);
if (await deliveryAncmts.AnyAsync()) { if (await deliveryAncmts.AnyAsync()) {
var attrGroups = await deliveryAncmts var attrGroups = await deliveryAncmts

@ -809,6 +809,38 @@ namespace Elwig.Services {
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
} }
public static async Task GenerateLocalityStatistics(this DeliveryAdminViewModel vm, ExportSubject subject) {
using var ctx = new AppDbContext();
IQueryable<DeliveryPart> query;
List<string> filterNames = [];
if (subject == ExportSubject.FromFilters) {
var (f, _, q, _, _) = await vm.GetFilters(ctx);
query = q;
filterNames.AddRange(f);
} else {
throw new ArgumentException("Invalid value for ExportSubject");
}
var d = new SaveFileDialog() {
FileName = $"Lieferstatistik-{vm.FilterSeason ?? Utils.CurrentLastSeason}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"Lieferstatistik pro Ort speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.AppStarting;
try {
using var ods = new OdsFile(d.FileName);
var tbl = await WineLocalityStatisticsData.FromQuery(query, filterNames);
await ods.AddTable(tbl);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
}
public static async Task GenerateDeliveryDepreciationList(this DeliveryAdminViewModel vm, ExportSubject subject, ExportMode mode) { public static async Task GenerateDeliveryDepreciationList(this DeliveryAdminViewModel vm, ExportSubject subject, ExportMode mode) {
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
IQueryable<DeliveryPart> query; IQueryable<DeliveryPart> query;
@ -914,7 +946,7 @@ namespace Elwig.Services {
var weight = await deliveryParts.SumAsync(p => p.Weight); var weight = await deliveryParts.SumAsync(p => p.Weight);
wText = $"{weight:N0} kg"; wText = $"{weight:N0} kg";
wGrid.Add(("Gewicht", null, weight, null, weight)); wGrid.Add(("Menge", null, weight, null, weight));
if (await deliveryParts.AnyAsync()) { if (await deliveryParts.AnyAsync()) {
var kmwMin = await deliveryParts.MinAsync(p => p.Kmw); var kmwMin = await deliveryParts.MinAsync(p => p.Kmw);

@ -212,6 +212,7 @@ namespace Elwig.Services {
var filterLfbisNr = new List<string>(); var filterLfbisNr = new List<string>();
var filterUstIdNr = new List<string>(); var filterUstIdNr = new List<string>();
var filterAreaCom = new List<string>(); var filterAreaCom = new List<string>();
var filterNotAreaCom = new List<string>();
var filter = vm.TextFilter; var filter = vm.TextFilter;
if (filter.Count > 0) { if (filter.Count > 0) {
@ -223,7 +224,15 @@ namespace Elwig.Services {
for (int i = 0; i < filter.Count; i++) { for (int i = 0; i < filter.Count; i++) {
var e = filter[i]; var e = filter[i];
if (e.Length >= 5 && e.Length <= 10 && "funktionär".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) { if (e.Equals("aktiv", StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => m.IsActive);
filter.RemoveAt(i--);
filterNames.Add("aktive Mitglieder");
} else if (e.Equals("!aktiv", StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => !m.IsActive);
filter.RemoveAt(i--);
filterNames.Add("inaktive Mitglieder");
} else if (e.Length >= 5 && e.Length <= 10 && "funktionär".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => m.IsFunktionär); memberQuery = memberQuery.Where(m => m.IsFunktionär);
filter.RemoveAt(i--); filter.RemoveAt(i--);
filterNames.Add("Funktionäre"); filterNames.Add("Funktionäre");
@ -314,10 +323,22 @@ namespace Elwig.Services {
memberQuery = memberQuery.Where(m => m.TelephoneNumbers.Any(t => t.Number.Replace(" ", "").StartsWith(e))); memberQuery = memberQuery.Where(m => m.TelephoneNumbers.Any(t => t.Number.Replace(" ", "").StartsWith(e)));
filter.RemoveAt(i--); filter.RemoveAt(i--);
filterNames.Add($"Tel.-Nr. {e}"); filterNames.Add($"Tel.-Nr. {e}");
} else if (e.Length >= 5 && e.Length <= 14 && "flächenbindung".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
filterAreaCom.AddRange(areaComs.Keys);
filter.RemoveAt(i--);
filterNames.Add($"Flächenbindung");
} else if (e.Length >= 6 && e.Length <= 15 && "!flächenbindung".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
filterNotAreaCom.AddRange(areaComs.Keys);
filter.RemoveAt(i--);
filterNames.Add($"keine Flächenbindung");
} else if (areaComs.ContainsKey(e.ToUpper())) { } else if (areaComs.ContainsKey(e.ToUpper())) {
filterAreaCom.Add(e.ToUpper()); filterAreaCom.Add(e.ToUpper());
filter.RemoveAt(i--); filter.RemoveAt(i--);
filterNames.Add($"Flächenbindung {e.ToUpper()}"); filterNames.Add($"Flächenbindung {e.ToUpper()}");
} else if (e.Length >= 3 && e[0] == '!' && areaComs.ContainsKey(e[1..].ToUpper())) {
filterNotAreaCom.Add(e[1..].ToUpper());
filter.RemoveAt(i--);
filterNames.Add($"ohne Flächenbindung {e[1..].ToUpper()}");
} else if (Validator.CheckLfbisNr(e)) { } else if (Validator.CheckLfbisNr(e)) {
filterLfbisNr.Add(e); filterLfbisNr.Add(e);
filter.RemoveAt(i--); filter.RemoveAt(i--);
@ -337,6 +358,7 @@ namespace Elwig.Services {
if (filterKgNr.Count > 0) memberQuery = memberQuery.Where(m => m.DefaultKgNr != null && filterKgNr.Contains((int)m.DefaultKgNr)); if (filterKgNr.Count > 0) memberQuery = memberQuery.Where(m => m.DefaultKgNr != null && filterKgNr.Contains((int)m.DefaultKgNr));
if (filterZwst.Count > 0) memberQuery = memberQuery.Where(m => m.ZwstId != null && filterZwst.Contains(m.ZwstId)); if (filterZwst.Count > 0) memberQuery = memberQuery.Where(m => m.ZwstId != null && filterZwst.Contains(m.ZwstId));
if (filterAreaCom.Count > 0) memberQuery = memberQuery.Where(m => m.AreaCommitments.AsQueryable().Where(Utils.ActiveAreaCommitments()).Any(c => filterAreaCom.Contains(c.VtrgId))); if (filterAreaCom.Count > 0) memberQuery = memberQuery.Where(m => m.AreaCommitments.AsQueryable().Where(Utils.ActiveAreaCommitments()).Any(c => filterAreaCom.Contains(c.VtrgId)));
if (filterNotAreaCom.Count > 0) memberQuery = memberQuery.Where(m => !m.AreaCommitments.AsQueryable().Where(Utils.ActiveAreaCommitments()).All(c => filterNotAreaCom.Contains(c.VtrgId)));
if (filterLfbisNr.Count > 0) memberQuery = memberQuery.Where(m => m.LfbisNr != null && filterLfbisNr.Contains(m.LfbisNr)); if (filterLfbisNr.Count > 0) memberQuery = memberQuery.Where(m => m.LfbisNr != null && filterLfbisNr.Contains(m.LfbisNr));
if (filterUstIdNr.Count > 0) memberQuery = memberQuery.Where(m => m.UstIdNr != null && filterUstIdNr.Contains(m.UstIdNr)); if (filterUstIdNr.Count > 0) memberQuery = memberQuery.Where(m => m.UstIdNr != null && filterUstIdNr.Contains(m.UstIdNr));
} }
@ -379,7 +401,7 @@ namespace Elwig.Services {
try { try {
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
var v = (await ctx.PaymentVariants.FindAsync(year, avnr))!; var v = (await ctx.PaymentVariants.FindAsync(year, avnr))!;
var data = await CreditNoteDeliveryData.ForPaymentVariant(ctx.CreditNoteDeliveryRows, ctx.Seasons, year, avnr); var data = await CreditNoteDeliveryData.ForPaymentVariant(ctx.CreditNoteDeliveryRows, ctx.PaymentVariants, year, avnr);
var p = (await ctx.MemberPayments.FindAsync(year, avnr, m.MgNr))!; var p = (await ctx.MemberPayments.FindAsync(year, avnr, m.MgNr))!;
var b = BillingData.FromJson((await ctx.PaymentVariants.FindAsync(year, avnr))!.Data); var b = BillingData.FromJson((await ctx.PaymentVariants.FindAsync(year, avnr))!.Data);

@ -8,7 +8,6 @@ using System.Linq;
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.Threading;
using System.Windows.Input; using System.Windows.Input;
namespace Elwig.Windows { namespace Elwig.Windows {
@ -358,22 +357,6 @@ namespace Elwig.Windows {
UpdateComboBox(ortInput); UpdateComboBox(ortInput);
} }
protected static void InitializeDelayTimer(TextBox tb, Action<object, TextChangedEventArgs> handler) {
var timer = new DispatcherTimer {
Interval = TimeSpan.FromMilliseconds(250)
};
timer.Tick += (object? sender, EventArgs evt) => {
timer.Stop();
var (oSender, oEvent) = ((object, TextChangedEventArgs))timer.Tag;
handler(oSender, oEvent);
};
tb.TextChanged += (object sender, TextChangedEventArgs evt) => {
timer.Stop();
timer.Tag = (sender, evt);
timer.Start();
};
}
protected bool InputTextChanged(TextBox input) { protected bool InputTextChanged(TextBox input) {
return InputTextChanged(input, new ValidationResult(true, null)); return InputTextChanged(input, new ValidationResult(true, null));
} }

@ -88,8 +88,9 @@
<LineBreak/> <LineBreak/>
Filtern nach:<LineBreak/> Filtern nach:<LineBreak/>
<Bold>Sorte</Bold>: z.B. GV, zw, RR, ...<LineBreak/> <Bold>Sorte</Bold>: z.B. GV, zw, RR, ...<LineBreak/>
<Bold>Attribut</Bold>: z.B. Kabinett, dac, ... <LineBreak/> <Bold>Attribut</Bold>: z.B. Kabinett, dac, ...<LineBreak/>
<Bold>Flächenbindung</Bold>: z.B. GVK, GVD, ... <Bold>Flächenbindung</Bold>: z.B. GVK, GVD, ...<LineBreak/>
<Bold>Saison</Bold>: z.B. 2020, 2019... (in dieser Saison aktiv)
</TextBlock> </TextBlock>
</TextBox.ToolTip> </TextBox.ToolTip>
</TextBox> </TextBox>

@ -33,7 +33,7 @@ namespace Elwig.Windows {
GstNrInput, AreaInput, AreaComTypeInput, WineCultivationInput GstNrInput, AreaInput, AreaComTypeInput, WineCultivationInput
]; ];
InitializeDelayTimer(SearchInput, SearchInput_TextChanged); ControlUtils.InitializeDelayTimer(SearchInput, SearchInput_TextChanged);
SearchInput.TextChanged -= SearchInput_TextChanged; SearchInput.TextChanged -= SearchInput_TextChanged;
ActiveAreaCommitmentInput.Content = ((string)ActiveAreaCommitmentInput.Content).Replace("2020", $"{Utils.CurrentLastSeason}"); ActiveAreaCommitmentInput.Content = ((string)ActiveAreaCommitmentInput.Content).Replace("2020", $"{Utils.CurrentLastSeason}");
} }

@ -10,7 +10,6 @@ using Elwig.Helpers.Billing;
using Elwig.Models.Entities; using Elwig.Models.Entities;
using ScottPlot.Plottables; using ScottPlot.Plottables;
using ScottPlot; using ScottPlot;
using ScottPlot.Control;
namespace Elwig.Windows { namespace Elwig.Windows {
public partial class ChartWindow : ContextWindow { public partial class ChartWindow : ContextWindow {
@ -226,30 +225,8 @@ namespace Elwig.Windows {
DataPlot.Color = ColorUngebunden; DataPlot.Color = ColorUngebunden;
DataPlot.MarkerStyle = new MarkerStyle(MarkerShape.FilledCircle, 9, ColorUngebunden); DataPlot.MarkerStyle = new MarkerStyle(MarkerShape.FilledCircle, 9, ColorUngebunden);
OechslePricePlot.Interaction.Enable(new PlotActions() {
ZoomIn = StandardActions.ZoomIn,
ZoomOut = StandardActions.ZoomOut,
PanUp = StandardActions.PanUp,
PanDown = StandardActions.PanDown,
PanLeft = StandardActions.PanLeft,
PanRight = StandardActions.PanRight,
DragPan = StandardActions.DragPan,
DragZoom = StandardActions.DragZoom,
DragZoomRectangle = StandardActions.DragZoomRectangle,
ZoomRectangleClear = StandardActions.ZoomRectangleClear,
ZoomRectangleApply = StandardActions.ZoomRectangleApply,
AutoScale = StandardActions.AutoScale,
});
//OechslePricePlot.Plot.XAxis.ManualTickSpacing(1);
//OechslePricePlot.Plot.YAxis.ManualTickSpacing(0.1);
OechslePricePlot.Plot.Axes.SetLimits(Math.Min(GraphEntry.MinX, GraphEntry.MinXGeb) - 1, GraphEntry.MaxX + 1, -0.1, 1.5); OechslePricePlot.Plot.Axes.SetLimits(Math.Min(GraphEntry.MinX, GraphEntry.MinXGeb) - 1, GraphEntry.MaxX + 1, -0.1, 1.5);
//OechslePricePlot.Plot.Layout(padding: 0);
//OechslePricePlot.Plot.XAxis2.Layout(padding: 0);
//OechslePricePlot.Plot.YAxis.Layout(padding: 0);
//OechslePricePlot.Plot.YAxis2.Layout(padding: 0);
HighlightedPointPlot = OechslePricePlot.Plot.Add.Marker(0, 0, MarkerShape.OpenCircle, 10, Colors.Red); HighlightedPointPlot = OechslePricePlot.Plot.Add.Marker(0, 0, MarkerShape.OpenCircle, 10, Colors.Red);
HighlightedPointPlot.IsVisible = false; HighlightedPointPlot.IsVisible = false;

@ -67,13 +67,29 @@
<Menu Grid.ColumnSpan="3" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White"> <Menu Grid.ColumnSpan="3" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
<MenuItem Header="Lieferschein"> <MenuItem Header="Lieferschein">
<MenuItem x:Name="Menu_DeliveryNote_Show" Header="...anzeigen (PDF)" IsEnabled="False" <MenuItem x:Name="Menu_DeliveryNote_Show" Header="...anzeigen (PDF)" IsEnabled="False"
Click="Menu_DeliveryNote_Show_Click" InputGestureText="Strg+P"/> Click="Menu_DeliveryNote_Show_Click" InputGestureText="Strg+P">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE8FF;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_DeliveryNote_SavePdf" Header="...speichern... (PDF)" IsEnabled="False" <MenuItem x:Name="Menu_DeliveryNote_SavePdf" Header="...speichern... (PDF)" IsEnabled="False"
Click="Menu_DeliveryNote_SavePdf_Click"/> Click="Menu_DeliveryNote_SavePdf_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xEA90;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_DeliveryNote_Print" Header="...drucken" IsEnabled="False" <MenuItem x:Name="Menu_DeliveryNote_Print" Header="...drucken" IsEnabled="False"
Click="Menu_DeliveryNote_Print_Click" InputGestureText="Strg+Shift+P"/> Click="Menu_DeliveryNote_Print_Click" InputGestureText="Strg+Shift+P">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE749;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_DeliveryNote_Email" Header="...per E-Mail schicken" IsEnabled="False" <MenuItem x:Name="Menu_DeliveryNote_Email" Header="...per E-Mail schicken" IsEnabled="False"
Click="Menu_DeliveryNote_Email_Click"/> Click="Menu_DeliveryNote_Email_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE89C;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="Lieferjournal"> <MenuItem Header="Lieferjournal">
<MenuItem x:Name="Menu_DeliveryJournal_SaveFilters" Header="...aus Filtern speichern... (Excel)" <MenuItem x:Name="Menu_DeliveryJournal_SaveFilters" Header="...aus Filtern speichern... (Excel)"
@ -94,32 +110,6 @@
<MenuItem x:Name="Menu_DeliveryJournal_PrintToday" Header="...von heute drucken" <MenuItem x:Name="Menu_DeliveryJournal_PrintToday" Header="...von heute drucken"
Click="Menu_DeliveryJournal_PrintToday_Click" InputGestureText="Strg+J"/> Click="Menu_DeliveryJournal_PrintToday_Click" InputGestureText="Strg+J"/>
</MenuItem> </MenuItem>
<MenuItem Header="Qualitätsstatistik" x:Name="Menu_WineQualityStatistics">
<MenuItem x:Name="Menu_WineQualityStatistics_ShowFilters" Header="...aus Filtern anzeigen (PDF)"
Click="Menu_WineQualityStatistics_ShowFilters_Click"/>
<MenuItem x:Name="Menu_WineQualityStatistics_SavePdfFilters" Header="...aus Filtern speichern... (PDF)"
Click="Menu_WineQualityStatistics_SavePdfFilters_Click"/>
<MenuItem x:Name="Menu_WineQualityStatistics_PrintFilters" Header="...aus Filtern drucken"
Click="Menu_WineQualityStatistics_PrintFilters_Click"/>
<Separator/>
<MenuItem x:Name="Menu_WineQualityStatistics_ShowToday" Header="...von heute anzeigen (PDF)"
Click="Menu_WineQualityStatistics_ShowToday_Click"/>
<MenuItem x:Name="Menu_WineQualityStatistics_SavePdfToday" Header="...von heute speichern... (PDF)"
Click="Menu_WineQualityStatistics_SavePdfToday_Click"/>
<MenuItem x:Name="Menu_WineQualityStatistics_PrintToday" Header="...von heute drucken"
Click="Menu_WineQualityStatistics_PrintToday_Click" InputGestureText="Strg+Q"/>
<Separator/>
<MenuItem x:Name="Menu_WineQualityStatistics_ModeOe" Header="...nach °Oe aufschlüsseln" IsCheckable="True" IsChecked="True"
Click="Menu_WineQualityStatistics_Mode_Click"/>
<MenuItem x:Name="Menu_WineQualityStatistics_ModeKmw10" Header="...nach °KMW aufschlüsseln (&#x2152;)" IsCheckable="True"
Click="Menu_WineQualityStatistics_Mode_Click"/>
<MenuItem x:Name="Menu_WineQualityStatistics_ModeKmw5" Header="...nach °KMW aufschlüsseln (&#x2155;)" IsCheckable="True"
Click="Menu_WineQualityStatistics_Mode_Click"/>
<MenuItem x:Name="Menu_WineQualityStatistics_ModeKmw2" Header="...nach °KMW aufschlüsseln (&#x00BD;)" IsCheckable="True"
Click="Menu_WineQualityStatistics_Mode_Click"/>
<MenuItem x:Name="Menu_WineQualityStatistics_ModeKmw1" Header="...nach °KMW aufschlüsseln (ganze)" IsCheckable="True"
Click="Menu_WineQualityStatistics_Mode_Click"/>
</MenuItem>
<MenuItem Header="Abwertungsliste" x:Name="Menu_DeliveryDepreciationList"> <MenuItem Header="Abwertungsliste" x:Name="Menu_DeliveryDepreciationList">
<MenuItem x:Name="Menu_DeliveryDepreciationList_SaveFilters" Header="...aus Filtern speichern... (Excel)" <MenuItem x:Name="Menu_DeliveryDepreciationList_SaveFilters" Header="...aus Filtern speichern... (Excel)"
Click="Menu_DeliveryDepreciationList_SaveFilters_Click"/> Click="Menu_DeliveryDepreciationList_SaveFilters_Click"/>
@ -139,6 +129,38 @@
<MenuItem x:Name="Menu_DeliveryDepreciationList_PrintSeason" Header="...von Saison drucken" <MenuItem x:Name="Menu_DeliveryDepreciationList_PrintSeason" Header="...von Saison drucken"
Click="Menu_DeliveryDepreciationList_PrintSeason_Click"/> Click="Menu_DeliveryDepreciationList_PrintSeason_Click"/>
</MenuItem> </MenuItem>
<MenuItem Header="Statistik" x:Name="Menu_Statistics">
<MenuItem Header="Qualitätsstatistik..." x:Name="Menu_Statistics_WineQuality">
<MenuItem x:Name="Menu_Statistics_WineQuality_ShowFilters" Header="...aus Filtern anzeigen (PDF)"
Click="Menu_Statistics_WineQuality_ShowFilters_Click"/>
<MenuItem x:Name="Menu_Statistics_WineQuality_SavePdfFilters" Header="...aus Filtern speichern... (PDF)"
Click="Menu_Statistics_WineQuality_SavePdfFilters_Click"/>
<MenuItem x:Name="Menu_Statistics_WineQuality_PrintFilters" Header="...aus Filtern drucken"
Click="Menu_Statistics_WineQuality_PrintFilters_Click"/>
<Separator/>
<MenuItem x:Name="Menu_Statistics_WineQuality_ShowToday" Header="...von heute anzeigen (PDF)"
Click="Menu_Statistics_WineQuality_ShowToday_Click"/>
<MenuItem x:Name="Menu_Statistics_WineQuality_SavePdfToday" Header="...von heute speichern... (PDF)"
Click="Menu_Statistics_WineQuality_SavePdfToday_Click"/>
<MenuItem x:Name="Menu_Statistics_WineQuality_PrintToday" Header="...von heute drucken"
Click="Menu_Statistics_WineQuality_PrintToday_Click" InputGestureText="Strg+Q"/>
<Separator/>
<MenuItem x:Name="Menu_Statistics_WineQuality_ModeOe" Header="...nach °Oe aufschlüsseln" IsCheckable="True" IsChecked="True"
Click="Menu_Statistics_WineQuality_Mode_Click"/>
<MenuItem x:Name="Menu_Statistics_WineQuality_ModeKmw10" Header="...nach °KMW aufschlüsseln (&#x2152;)" IsCheckable="True"
Click="Menu_Statistics_WineQuality_Mode_Click"/>
<MenuItem x:Name="Menu_Statistics_WineQuality_ModeKmw5" Header="...nach °KMW aufschlüsseln (&#x2155;)" IsCheckable="True"
Click="Menu_Statistics_WineQuality_Mode_Click"/>
<MenuItem x:Name="Menu_Statistics_WineQuality_ModeKmw2" Header="...nach °KMW aufschlüsseln (&#x00BD;)" IsCheckable="True"
Click="Menu_Statistics_WineQuality_Mode_Click"/>
<MenuItem x:Name="Menu_Statistics_WineQuality_ModeKmw1" Header="...nach °KMW aufschlüsseln (ganze)" IsCheckable="True"
Click="Menu_Statistics_WineQuality_Mode_Click"/>
</MenuItem>
<MenuItem x:Name="Menu_Statistics_Locality" Header="Lieferstatistik pro Ort...">
<MenuItem x:Name="Menu_Statistic_Locality_SaveFilters" Header="...aus Filtern speichern... (Excel)"
Click="Menu_Statistic_Locality_SaveFilters_Click"/>
</MenuItem>
</MenuItem>
<MenuItem Header="BKI"> <MenuItem Header="BKI">
<MenuItem x:Name="Menu_Bki_SaveList" Header="Traubentransportscheinliste speichern..."/> <MenuItem x:Name="Menu_Bki_SaveList" Header="Traubentransportscheinliste speichern..."/>
</MenuItem> </MenuItem>
@ -195,7 +217,7 @@
<Bold>Zweigstelle</Bold>: z.B. musterort, ...<LineBreak/> <Bold>Zweigstelle</Bold>: z.B. musterort, ...<LineBreak/>
<Bold>Attribut</Bold>: z.B. kabinett, !kabinett (alle außer kabinett), ...<LineBreak/> <Bold>Attribut</Bold>: z.B. kabinett, !kabinett (alle außer kabinett), ...<LineBreak/>
<Bold>Bewirtschaftung</Bold>: z.B. bio, !kip (alle außer KIP), ...<LineBreak/> <Bold>Bewirtschaftung</Bold>: z.B. bio, !kip (alle außer KIP), ...<LineBreak/>
<Bold>Gewicht</Bold>: z.B. &lt;500kg, &gt;6000kg, ... (gilt für Gewicht der gesamten Lieferung)<LineBreak/> <Bold>Gewicht</Bold>: z.B. &lt;500kg, &gt;6000kg, ... (gilt für Menge der gesamten Lieferung)<LineBreak/>
<Bold>Datum</Bold>: z.B. 1.9., 15.9.-10.10., -15.10.2020, ...<LineBreak/> <Bold>Datum</Bold>: z.B. 1.9., 15.9.-10.10., -15.10.2020, ...<LineBreak/>
<Bold>Uhrzeit</Bold>: z.B. 06:00-08:00, 18:00-, ...<LineBreak/> <Bold>Uhrzeit</Bold>: z.B. 06:00-08:00, 18:00-, ...<LineBreak/>
<Bold>Handwiegung</Bold>: handw[iegung], !Handw[iegung] (alle ohne Handwiegung)<LineBreak/> <Bold>Handwiegung</Bold>: handw[iegung], !Handw[iegung] (alle ohne Handwiegung)<LineBreak/>
@ -250,7 +272,7 @@
</Style> </Style>
</DataGridTextColumn.CellStyle> </DataGridTextColumn.CellStyle>
</DataGridTextColumn> </DataGridTextColumn>
<DataGridTextColumn Header="Gewicht" Binding="{Binding FilteredWeight, StringFormat='{}{0:N0} kg '}" Width="75"> <DataGridTextColumn Header="Menge" Binding="{Binding FilteredWeight, StringFormat='{}{0:N0} kg '}" Width="75">
<DataGridTextColumn.CellStyle> <DataGridTextColumn.CellStyle>
<Style> <Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/> <Setter Property="TextBlock.TextAlignment" Value="Right"/>
@ -494,7 +516,7 @@
</Grid> </Grid>
</GroupBox> </GroupBox>
<GroupBox Header="Gewicht" Grid.Column="1" Grid.Row="2" Margin="5,5,5,5"> <GroupBox Header="Menge" Grid.Column="1" Grid.Row="2" Margin="5,5,5,5">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/> <ColumnDefinition Width="70"/>
@ -647,7 +669,7 @@
<Separator Grid.Column="5"/> <Separator Grid.Column="5"/>
<StatusBarItem Grid.Column="6"> <StatusBarItem Grid.Column="6">
<TextBlock ToolTip="{Binding StatusWeightToolTip}"> <TextBlock ToolTip="{Binding StatusWeightToolTip}">
Gewicht: <Run Text="{Binding StatusWeight}"/> Menge: <Run Text="{Binding StatusWeight}"/>
</TextBlock> </TextBlock>
</StatusBarItem> </StatusBarItem>
<Separator Grid.Column="7"/> <Separator Grid.Column="7"/>

@ -41,7 +41,7 @@ namespace Elwig.Windows {
CommandBindings.Add(new CommandBinding(CtrlP, Menu_DeliveryNote_Show_Click)); CommandBindings.Add(new CommandBinding(CtrlP, Menu_DeliveryNote_Show_Click));
CommandBindings.Add(new CommandBinding(CtrlO, Menu_DeliveryJournal_ShowFilters_Click)); CommandBindings.Add(new CommandBinding(CtrlO, Menu_DeliveryJournal_ShowFilters_Click));
CommandBindings.Add(new CommandBinding(CtrlJ, Menu_DeliveryJournal_PrintToday_Click)); CommandBindings.Add(new CommandBinding(CtrlJ, Menu_DeliveryJournal_PrintToday_Click));
CommandBindings.Add(new CommandBinding(CtrlQ, Menu_WineQualityStatistics_PrintToday_Click)); CommandBindings.Add(new CommandBinding(CtrlQ, Menu_Statistics_WineQuality_PrintToday_Click));
CommandBindings.Add(new CommandBinding(CtrlShiftP, Menu_DeliveryNote_Print_Click)); CommandBindings.Add(new CommandBinding(CtrlShiftP, Menu_DeliveryNote_Print_Click));
CommandBindings.Add(new CommandBinding(CtrlShiftO, Menu_DeliveryJournal_PrintFilters_Click)); CommandBindings.Add(new CommandBinding(CtrlShiftO, Menu_DeliveryJournal_PrintFilters_Click));
RequiredInputs = [ RequiredInputs = [
@ -66,7 +66,7 @@ namespace Elwig.Windows {
SecondsTimer.Tick += new EventHandler(OnSecondPassed); SecondsTimer.Tick += new EventHandler(OnSecondPassed);
SecondsTimer.Interval = new TimeSpan(0, 0, 1); SecondsTimer.Interval = new TimeSpan(0, 0, 1);
InitializeDelayTimer(SearchInput, SearchInput_TextChanged); ControlUtils.InitializeDelayTimer(SearchInput, SearchInput_TextChanged);
SearchInput.TextChanged -= SearchInput_TextChanged; SearchInput.TextChanged -= SearchInput_TextChanged;
ViewModel.FilterSeason = Utils.CurrentLastSeason; ViewModel.FilterSeason = Utils.CurrentLastSeason;
@ -97,17 +97,17 @@ namespace Elwig.Windows {
WeighingDButton.Visibility = Visibility.Hidden; WeighingDButton.Visibility = Visibility.Hidden;
} }
Menu_WineQualityStatistics_ModeOe.IsChecked = false; Menu_Statistics_WineQuality_ModeOe.IsChecked = false;
Menu_WineQualityStatistics_ModeKmw1.IsChecked = false; Menu_Statistics_WineQuality_ModeKmw1.IsChecked = false;
Menu_WineQualityStatistics_ModeKmw2.IsChecked = false; Menu_Statistics_WineQuality_ModeKmw2.IsChecked = false;
Menu_WineQualityStatistics_ModeKmw5.IsChecked = false; Menu_Statistics_WineQuality_ModeKmw5.IsChecked = false;
Menu_WineQualityStatistics_ModeKmw10.IsChecked = false; Menu_Statistics_WineQuality_ModeKmw10.IsChecked = false;
switch (App.Client.OrderingMemberList) { switch (App.Client.OrderingMemberList) {
case 0: Menu_WineQualityStatistics_ModeOe.IsChecked = true; break; case 0: Menu_Statistics_WineQuality_ModeOe.IsChecked = true; break;
case 1: Menu_WineQualityStatistics_ModeKmw1.IsChecked = true; break; case 1: Menu_Statistics_WineQuality_ModeKmw1.IsChecked = true; break;
case 2: Menu_WineQualityStatistics_ModeKmw2.IsChecked = true; break; case 2: Menu_Statistics_WineQuality_ModeKmw2.IsChecked = true; break;
case 3: Menu_WineQualityStatistics_ModeKmw5.IsChecked = true; break; case 3: Menu_Statistics_WineQuality_ModeKmw5.IsChecked = true; break;
case 4: Menu_WineQualityStatistics_ModeKmw10.IsChecked = true; break; case 4: Menu_Statistics_WineQuality_ModeKmw10.IsChecked = true; break;
} }
Menu_Export_UploadFilters.IsEnabled = App.Config.SyncUrl != null; Menu_Export_UploadFilters.IsEnabled = App.Config.SyncUrl != null;
@ -218,40 +218,44 @@ namespace Elwig.Windows {
await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromSeasonAndBranch, ExportMode.Upload); await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromSeasonAndBranch, ExportMode.Upload);
} }
private async void Menu_WineQualityStatistics_ShowToday_Click(object sender, RoutedEventArgs evt) => private async void Menu_Statistics_WineQuality_ShowToday_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromToday, ExportMode.Show); await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromToday, ExportMode.Show);
private async void Menu_WineQualityStatistics_SavePdfToday_Click(object sender, RoutedEventArgs evt) => private async void Menu_Statistics_WineQuality_SavePdfToday_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromToday, ExportMode.SavePdf); await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromToday, ExportMode.SavePdf);
private async void Menu_WineQualityStatistics_PrintToday_Click(object sender, RoutedEventArgs evt) => private async void Menu_Statistics_WineQuality_PrintToday_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromToday, ExportMode.Print); await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromToday, ExportMode.Print);
private async void Menu_WineQualityStatistics_ShowFilters_Click(object sender, RoutedEventArgs evt) => private async void Menu_Statistics_WineQuality_ShowFilters_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromFilters, ExportMode.Show); await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromFilters, ExportMode.Show);
private async void Menu_WineQualityStatistics_SavePdfFilters_Click(object sender, RoutedEventArgs evt) => private async void Menu_Statistics_WineQuality_SavePdfFilters_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromFilters, ExportMode.SavePdf); await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromFilters, ExportMode.SavePdf);
private async void Menu_WineQualityStatistics_PrintFilters_Click(object sender, RoutedEventArgs evt) => private async void Menu_Statistics_WineQuality_PrintFilters_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromFilters, ExportMode.Print); await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromFilters, ExportMode.Print);
private async void Menu_WineQualityStatistics_Mode_Click(object sender, RoutedEventArgs evt) { private async void Menu_Statistics_WineQuality_Mode_Click(object sender, RoutedEventArgs evt) {
Menu_WineQualityStatistics.IsSubmenuOpen = true; Menu_Statistics.IsSubmenuOpen = true;
if (sender == Menu_WineQualityStatistics_ModeOe) { Menu_Statistics_WineQuality.IsSubmenuOpen = true;
if (sender == Menu_Statistics_WineQuality_ModeOe) {
App.Client.OrderingMemberList = 0; App.Client.OrderingMemberList = 0;
} else if (sender == Menu_WineQualityStatistics_ModeKmw1) { } else if (sender == Menu_Statistics_WineQuality_ModeKmw1) {
App.Client.OrderingMemberList = 1; App.Client.OrderingMemberList = 1;
} else if (sender == Menu_WineQualityStatistics_ModeKmw2) { } else if (sender == Menu_Statistics_WineQuality_ModeKmw2) {
App.Client.OrderingMemberList = 2; App.Client.OrderingMemberList = 2;
} else if (sender == Menu_WineQualityStatistics_ModeKmw5) { } else if (sender == Menu_Statistics_WineQuality_ModeKmw5) {
App.Client.OrderingMemberList = 3; App.Client.OrderingMemberList = 3;
} else if (sender == Menu_WineQualityStatistics_ModeKmw10) { } else if (sender == Menu_Statistics_WineQuality_ModeKmw10) {
App.Client.OrderingMemberList = 4; App.Client.OrderingMemberList = 4;
} }
Menu_WineQualityStatistics_ModeOe.IsChecked = App.Client.OrderingMemberList == 0; Menu_Statistics_WineQuality_ModeOe.IsChecked = App.Client.OrderingMemberList == 0;
Menu_WineQualityStatistics_ModeKmw1.IsChecked = App.Client.OrderingMemberList == 1; Menu_Statistics_WineQuality_ModeKmw1.IsChecked = App.Client.OrderingMemberList == 1;
Menu_WineQualityStatistics_ModeKmw2.IsChecked = App.Client.OrderingMemberList == 2; Menu_Statistics_WineQuality_ModeKmw2.IsChecked = App.Client.OrderingMemberList == 2;
Menu_WineQualityStatistics_ModeKmw5.IsChecked = App.Client.OrderingMemberList == 3; Menu_Statistics_WineQuality_ModeKmw5.IsChecked = App.Client.OrderingMemberList == 3;
Menu_WineQualityStatistics_ModeKmw10.IsChecked = App.Client.OrderingMemberList == 4; Menu_Statistics_WineQuality_ModeKmw10.IsChecked = App.Client.OrderingMemberList == 4;
await App.Client.UpdateValues(); await App.Client.UpdateValues();
} }
private async void Menu_Statistic_Locality_SaveFilters_Click(object sender, RoutedEventArgs evt)=>
await ViewModel.GenerateLocalityStatistics(DeliveryService.ExportSubject.FromFilters);
private async void Menu_DeliveryDepreciationList_SaveFilters_Click(object sender, RoutedEventArgs evt) => private async void Menu_DeliveryDepreciationList_SaveFilters_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromFilters, ExportMode.SaveList); await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromFilters, ExportMode.SaveList);
private async void Menu_DeliveryDepreciationList_ShowFilters_Click(object sender, RoutedEventArgs evt) => private async void Menu_DeliveryDepreciationList_ShowFilters_Click(object sender, RoutedEventArgs evt) =>
@ -306,19 +310,23 @@ namespace Elwig.Windows {
UnsetDefaultValue(GerebeltGewogenInput); UnsetDefaultValue(GerebeltGewogenInput);
} }
if (!App.Client.HasNetWeighing(ViewModel.Branch)) { if (App.Client.HasBoxWeighing(ViewModel.Branch)) {
LesewagenInput.IsEnabled = false; LesewagenInput.IsEnabled = false;
SetDefaultValue(LesewagenInput, false); SetDefaultValue(LesewagenInput, false);
HandPickedInput.IsThreeState = false;
UnsetDefaultValue(HandPickedInput);
} else { } else {
LesewagenInput.IsEnabled = true; LesewagenInput.IsEnabled = true;
UnsetDefaultValue(LesewagenInput); UnsetDefaultValue(LesewagenInput);
}
if (!App.Client.HasNetWeighing(ViewModel.Branch)) {
HandPickedInput.IsThreeState = false;
UnsetDefaultValue(HandPickedInput);
} else {
HandPickedInput.IsThreeState = true; HandPickedInput.IsThreeState = true;
SetDefaultValue(HandPickedInput, null); SetDefaultValue(HandPickedInput, null);
} }
if (App.Client.IsMatzen || App.Client.IsWinzerkeller) { if (App.Client.IsMatzen || App.Client.IsWinzerkeller || App.Client.IsBaden || App.Client.IsWeinland) {
GebundenInput.IsEnabled = false; GebundenInput.IsEnabled = false;
SetDefaultValue(GebundenInput, null); SetDefaultValue(GebundenInput, null);
} else { } else {
@ -1376,7 +1384,7 @@ namespace Elwig.Windows {
} }
private void GerebeltGewogenInput_Changed(object sender, RoutedEventArgs evt) { private void GerebeltGewogenInput_Changed(object sender, RoutedEventArgs evt) {
if (!App.Client.HasNetWeighing(ViewModel.Branch)) { if ((IsEditing || IsCreating) && !App.Client.HasNetWeighing(ViewModel.Branch)) {
HandPickedInput.IsChecked = !GerebeltGewogenInput.IsChecked; HandPickedInput.IsChecked = !GerebeltGewogenInput.IsChecked;
} }
if (!ViewModel.IsReceipt || App.Client.HasNetWeighing(ViewModel.Branch)) { if (!ViewModel.IsReceipt || App.Client.HasNetWeighing(ViewModel.Branch)) {
@ -1386,7 +1394,7 @@ namespace Elwig.Windows {
} }
private void HandPickedInput_Changed(object sender, RoutedEventArgs evt) { private void HandPickedInput_Changed(object sender, RoutedEventArgs evt) {
if (!App.Client.HasNetWeighing(ViewModel.Branch)) { if ((IsEditing || IsCreating) && !App.Client.HasNetWeighing(ViewModel.Branch)) {
GerebeltGewogenInput.IsChecked = !HandPickedInput.IsChecked; GerebeltGewogenInput.IsChecked = !HandPickedInput.IsChecked;
} }
CheckBox_Changed(sender, evt); CheckBox_Changed(sender, evt);

@ -69,13 +69,29 @@
<Menu Grid.ColumnSpan="3" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White"> <Menu Grid.ColumnSpan="3" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
<MenuItem Header="Anmeldeliste"> <MenuItem Header="Anmeldeliste">
<MenuItem x:Name="Menu_DeliveryAncmtList_SaveSelected" Header="...von ausgewähltem Leseplan speichern... (Excel)" <MenuItem x:Name="Menu_DeliveryAncmtList_SaveSelected" Header="...von ausgewähltem Leseplan speichern... (Excel)"
Click="Menu_DeliveryAncmtList_SaveSelected_Click"/> Click="Menu_DeliveryAncmtList_SaveSelected_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE9F9;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_DeliveryAncmtList_ShowSelected" Header="...von ausgewähltem Leseplan anzeigen (PDF)" <MenuItem x:Name="Menu_DeliveryAncmtList_ShowSelected" Header="...von ausgewähltem Leseplan anzeigen (PDF)"
Click="Menu_DeliveryAncmtList_ShowSelected_Click" InputGestureText="Strg+P"/> Click="Menu_DeliveryAncmtList_ShowSelected_Click" InputGestureText="Strg+P">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE8FF;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_DeliveryAncmtList_SavePdfSelected" Header="...von ausgewähltem Leseplan speichern... (PDF)" <MenuItem x:Name="Menu_DeliveryAncmtList_SavePdfSelected" Header="...von ausgewähltem Leseplan speichern... (PDF)"
Click="Menu_DeliveryAncmtList_SavePdfSelected_Click"/> Click="Menu_DeliveryAncmtList_SavePdfSelected_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xEA90;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_DeliveryAncmtList_PrintSelected" Header="...von ausgewähltem Leseplan drucken" <MenuItem x:Name="Menu_DeliveryAncmtList_PrintSelected" Header="...von ausgewähltem Leseplan drucken"
Click="Menu_DeliveryAncmtList_PrintSelected_Click" InputGestureText="Strg+Shift+P"/> Click="Menu_DeliveryAncmtList_PrintSelected_Click" InputGestureText="Strg+Shift+P">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE749;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
</Menu> </Menu>
@ -109,7 +125,7 @@
<Bold>Zweigstelle</Bold>: z.B. musterort, ...<LineBreak/> <Bold>Zweigstelle</Bold>: z.B. musterort, ...<LineBreak/>
<Bold>Attribut</Bold>: z.B. kabinett, !kabinett (alle außer kabinett), ...<LineBreak/> <Bold>Attribut</Bold>: z.B. kabinett, !kabinett (alle außer kabinett), ...<LineBreak/>
<Bold>Bewirtschaftung</Bold>: z.B. bio, !kip (alle außer KIP), ...<LineBreak/> <Bold>Bewirtschaftung</Bold>: z.B. bio, !kip (alle außer KIP), ...<LineBreak/>
<Bold>Gewicht</Bold>: z.B. &lt;500kg, &gt;6000kg, ...<LineBreak/> <Bold>Menge</Bold>: z.B. &lt;500kg, &gt;6000kg, ...<LineBreak/>
<Bold>Datum</Bold>: z.B. 1.9., 15.9.-10.10., -15.10.2020, ... <Bold>Datum</Bold>: z.B. 1.9., 15.9.-10.10., -15.10.2020, ...
</TextBlock> </TextBlock>
</TextBox.ToolTip> </TextBox.ToolTip>
@ -175,7 +191,7 @@
</Style> </Style>
</DataGridTextColumn.CellStyle> </DataGridTextColumn.CellStyle>
</DataGridTextColumn> </DataGridTextColumn>
<DataGridTextColumn Header="Gewicht" Binding="{Binding Weight, StringFormat='{}{0:N0} kg'}" Width="75"> <DataGridTextColumn Header="Menge" Binding="{Binding Weight, StringFormat='{}{0:N0} kg'}" Width="75">
<DataGridTextColumn.CellStyle> <DataGridTextColumn.CellStyle>
<Style> <Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/> <Setter Property="TextBlock.TextAlignment" Value="Right"/>
@ -312,7 +328,7 @@
</ComboBox.ItemTemplateSelector> </ComboBox.ItemTemplateSelector>
</ComboBox> </ComboBox>
<Label Content="Gewicht:" Margin="10,70,0,0" Grid.Column="0"/> <Label Content="Menge:" Margin="10,70,0,0" Grid.Column="0"/>
<ctrl:UnitTextBox x:Name="WeightInput" Unit="kg" Text="{Binding WeightString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" <ctrl:UnitTextBox x:Name="WeightInput" Unit="kg" Text="{Binding WeightString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="1" Margin="0,70,10,10" Width="61" HorizontalAlignment="Left" Grid.Column="1" Margin="0,70,10,10" Width="61" HorizontalAlignment="Left"
TextChanged="WeightInput_TextChanged" KeyUp="Input_KeyUp"/> TextChanged="WeightInput_TextChanged" KeyUp="Input_KeyUp"/>
@ -348,7 +364,7 @@
<Separator Grid.Column="1"/> <Separator Grid.Column="1"/>
<StatusBarItem Grid.Column="2"> <StatusBarItem Grid.Column="2">
<TextBlock ToolTip="{Binding StatusWeightToolTip}"> <TextBlock ToolTip="{Binding StatusWeightToolTip}">
Gewicht: <Run Text="{Binding StatusWeight}"/> Menge: <Run Text="{Binding StatusWeight}"/>
</TextBlock> </TextBlock>
</StatusBarItem> </StatusBarItem>
<Separator Grid.Column="3"/> <Separator Grid.Column="3"/>

@ -33,7 +33,7 @@ namespace Elwig.Windows {
DoShowWarningWindows = false; DoShowWarningWindows = false;
InitializeDelayTimer(SearchInput, SearchInput_TextChanged); ControlUtils.InitializeDelayTimer(SearchInput, SearchInput_TextChanged);
SearchInput.TextChanged -= SearchInput_TextChanged; SearchInput.TextChanged -= SearchInput_TextChanged;
ViewModel.FilterSeason = Utils.CurrentLastSeason; ViewModel.FilterSeason = Utils.CurrentLastSeason;
} }

@ -127,7 +127,7 @@
</DataGridTextColumn.CellStyle> </DataGridTextColumn.CellStyle>
</DataGridTextColumn> </DataGridTextColumn>
<DataGridTextColumn Header="Beschreibung" Binding="{Binding Description}" Width="200"/> <DataGridTextColumn Header="Beschreibung" Binding="{Binding Description}" Width="200"/>
<DataGridTextColumn Header="Max. Gew." Binding="{Binding MaxWeight, StringFormat='{}{0:N0} kg'}" Width="80"> <DataGridTextColumn Header="Max. Mg." Binding="{Binding MaxWeight, StringFormat='{}{0:N0} kg'}" Width="80">
<DataGridTextColumn.CellStyle> <DataGridTextColumn.CellStyle>
<Style> <Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/> <Setter Property="TextBlock.TextAlignment" Value="Right"/>
@ -211,7 +211,7 @@
Margin="0,40,10,10" Grid.Column="1" Grid.ColumnSpan="2" Margin="0,40,10,10" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<Label Content="Max. Gewicht:" Margin="10,70,0,10"/> <Label Content="Max. Menge:" Margin="10,70,0,10"/>
<ctrl:UnitTextBox x:Name="MaxWeightInput" Unit="kg" Text="{Binding MaxWeightString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" <ctrl:UnitTextBox x:Name="MaxWeightInput" Unit="kg" Text="{Binding MaxWeightString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,70,10,10" Grid.Column="1" Grid.ColumnSpan="2" Width="68" HorizontalAlignment="Left" Margin="0,70,10,10" Grid.Column="1" Grid.ColumnSpan="2" Width="68" HorizontalAlignment="Left"
TextChanged="MaxWeightInput_TextChanged"/> TextChanged="MaxWeightInput_TextChanged"/>

@ -27,7 +27,7 @@ namespace Elwig.Windows {
DateInput, BranchInput, DescriptionInput, MainWineVarietiesInput, DateInput, BranchInput, DescriptionInput, MainWineVarietiesInput,
]; ];
InitializeDelayTimer(SearchInput, SearchInput_TextChanged); ControlUtils.InitializeDelayTimer(SearchInput, SearchInput_TextChanged);
SearchInput.TextChanged -= SearchInput_TextChanged; SearchInput.TextChanged -= SearchInput_TextChanged;
ViewModel.FilterSeason = Utils.CurrentLastSeason; ViewModel.FilterSeason = Utils.CurrentLastSeason;
} }

@ -36,9 +36,10 @@ namespace Elwig.Windows {
} }
private void EventList_SelectionChanged(object sender, RoutedEventArgs evt) { private void EventList_SelectionChanged(object sender, RoutedEventArgs evt) {
var t = EventList.SelectedItem.GetType(); var item = EventList.SelectedItem;
var t = item.GetType();
var p = t.GetProperty("Event")!; var p = t.GetProperty("Event")!;
EventData.Text = ((EventLogEntry)p.GetValue(EventList.SelectedItem)!).Message; EventData.Text = ((EventLogEntry)p.GetValue(item)!).Message;
} }
} }
} }

@ -0,0 +1,54 @@
<Window x:Class="Elwig.Windows.MailLogWindow"
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:local="clr-namespace:Elwig.Windows"
Title="Ausgangs-Protokoll - Rundschreiben - Elwig" Height="600" Width="1000">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="FilterInput" Margin="10,10,300,10" Height="25" FontSize="14" Padding="2" TextWrapping="NoWrap"
HorizontalAlignment="Stretch" VerticalAlignment="Top"
TextChanged="FilterInput_TextChanged"/>
<ComboBox x:Name="TypeInput" Margin="10,10,165,10" Width="130" Height="25" FontSize="14"
HorizontalAlignment="Right" VerticalAlignment="Top"
SelectionChanged="TypeInput_SelectionChanged">
<ComboBoxItem Content="Post &amp; E-Mail" IsSelected="True"/>
<ComboBoxItem Content="Nur Post"/>
<ComboBoxItem Content="Nur E-Mail"/>
</ComboBox>
<ComboBox x:Name="TimeSpanInput" Margin="10,10,10,10" Width="150" Height="25" FontSize="14"
HorizontalAlignment="Right" VerticalAlignment="Top"
SelectionChanged="TimeSpanInput_SelectionChanged">
<ComboBoxItem Content="letzten 7 Tage" IsSelected="True"/>
<ComboBoxItem Content="letzten 6 Monate"/>
<ComboBoxItem Content="Immer"/>
</ComboBox>
<DataGrid x:Name="MailList" AutoGenerateColumns="False" HeadersVisibility="Column" IsReadOnly="True" GridLinesVisibility="None" SelectionMode="Single"
CanUserDeleteRows="False" CanUserResizeRows="False" CanUserAddRows="False"
SelectionChanged="MailList_SelectionChanged"
Margin="10,40,5,10">
<DataGrid.Columns>
<DataGridTextColumn Header="Zeitpunkt" Binding="{Binding DateTime, StringFormat='{}{0:dd.MM.yyyy HH:mm:ss}'}" Width="120"/>
<DataGridTextColumn Header="Typ" Binding="{Binding Type}" Width="50"/>
<DataGridTextColumn Header="MgNr." Binding="{Binding MgNr}" Width="50"/>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="200"/>
<DataGridTextColumn Header="Adressen" Binding="{Binding Addresses}" Width="200"/>
<DataGridTextColumn Header="Betreff" Binding="{Binding Subject}" Width="250"/>
<DataGridTextColumn Header="Anhänge" Binding="{Binding Attachments}" Width="200"/>
</DataGrid.Columns>
</DataGrid>
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<TextBox x:Name="MailData" Grid.Column="2" Margin="5,10,10,10" IsReadOnly="True"
HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible"/>
</Grid>
</Window>

@ -0,0 +1,85 @@
using Elwig.Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
namespace Elwig.Windows {
public partial class MailLogWindow : Window {
record struct Row (DateTime DateTime, string Type, int MgNr, string Name, string Addresses, string Subject, string Attachments);
private List<Row> Data = [];
public MailLogWindow() {
InitializeComponent();
WindowState = WindowState.Maximized;
ControlUtils.InitializeDelayTimer(FilterInput, FilterInput_TextChanged);
FilterInput.TextChanged -= FilterInput_TextChanged;
}
private async void TimeSpanInput_SelectionChanged(object sender, RoutedEventArgs evt) {
if (!IsLoaded) return;
DateTime? fromDate = DateTime.Now;
if (TimeSpanInput.SelectedIndex == 0) {
fromDate = fromDate.Value.AddDays(-7);
} else if (TimeSpanInput.SelectedIndex == 1) {
fromDate = fromDate.Value.AddMonths(-6);
} else {
fromDate = null;
}
var mails = await Utils.GetSentMails(fromDate: fromDate == null ? null : DateOnly.FromDateTime(fromDate.Value));
Data = mails.Reverse().Select(m => new Row(
m.DateTime,
m.Type == "email" ? "E-Mail" : m.Type == "postal" ? "Post" : "?",
m.MgNr,
m.Name,
string.Join("\n", m.Addresses),
m.Subject,
string.Join("\n", m.Attachments)
)).ToList();
MailList.ItemsSource = Data;
ApplyFilters();
}
private void FilterInput_TextChanged(object sender, RoutedEventArgs evt) {
ApplyFilters();
}
private void TypeInput_SelectionChanged(object sender, RoutedEventArgs evt) {
ApplyFilters();
}
private void ApplyFilters() {
if (!IsLoaded) return;
var filters = FilterInput.Text.Split(' ');
IEnumerable<Row> data = Data;
switch (TypeInput.SelectedIndex) {
case 1: data = data.Where(d => d.Type == "Post"); break;
case 2: data = data.Where(d => d.Type == "E-Mail"); break;
}
foreach (var filter in filters) {
if (int.TryParse(filter, out var mgnr)) {
data = data.Where(d => d.MgNr == mgnr);
} else {
var f = filter.ToLower();
data = data.Where(d => d.Name.Contains(f, StringComparison.CurrentCultureIgnoreCase) ||
d.Addresses.Contains(f, StringComparison.CurrentCultureIgnoreCase) ||
d.Subject.Contains(f, StringComparison.CurrentCultureIgnoreCase) ||
d.Attachments.Contains(f, StringComparison.CurrentCultureIgnoreCase));
}
}
if (IsLoaded)
MailList.ItemsSource = data.ToList();
}
private async void MailList_SelectionChanged(object sender, RoutedEventArgs evt) {
if (MailList.SelectedItem is not Row item) return;
if (item.Type == "E-Mail") {
MailData.Text = await Utils.FindSentMailBody(item.DateTime);
} else {
MailData.Text = "";
}
}
}
}

@ -27,281 +27,334 @@
<Setter Property="TextWrapping" Value="NoWrap"/> <Setter Property="TextWrapping" Value="NoWrap"/>
</Style> </Style>
</Window.Resources> </Window.Resources>
<TabControl x:Name="TabControl" BorderThickness="0" PreviewDragOver="Document_PreviwDragOver" AllowDrop="True" Drop="Document_Drop"> <Grid>
<TabItem Header="Dokumente" Visibility="Collapsed"> <Grid.RowDefinitions>
<Grid> <RowDefinition Height="19"/>
<Grid.ColumnDefinitions> <RowDefinition Height="1*"/>
<ColumnDefinition Width="*"/> <RowDefinition Height="24"/>
<ColumnDefinition Width="320"/> </Grid.RowDefinitions>
</Grid.ColumnDefinitions>
<Grid Height="200" VerticalAlignment="Top" HorizontalAlignment="Stretch"> <Menu BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
<MenuItem Header="Hilfe">
<MenuItem x:Name="Menu_Help_Log" Header="Ausgangs-Protokoll anzeigen"
Click="Menu_Help_Log_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xF168;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem>
</Menu>
<TabControl x:Name="TabControl" BorderThickness="0" Grid.Row="1"
PreviewDragOver="Document_PreviwDragOver" AllowDrop="True" Drop="Document_Drop">
<TabItem Header="Dokumente" Visibility="Collapsed">
<Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="25"/> <ColumnDefinition Width="320"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<Label Content="Verfügbare Dokumente" <Grid Height="200" VerticalAlignment="Top" HorizontalAlignment="Stretch">
Grid.Column="0" Margin="10,8,10,10"/>
<ListBox x:Name="AvaiableDocumentsList"
Grid.Column="0" Margin="10,30,10,10"
SelectionChanged="AvaiableDocumentsList_SelectionChanged"/>
<Button x:Name="DocumentAddButton" Content="&#xF0AF;" FontFamily="Segoe MDL2 Assets" FontSize="14"
Grid.Column="1" Margin="0,0,0,30" VerticalAlignment="Center" Height="25" IsEnabled="False"
Click="DocumentAddButton_Click"/>
<Button x:Name="DocumentRemoveButton" Content="&#xE74D;" FontFamily="Segoe MDL2 Assets" FontSize="16" Padding="1.5,0,0,0"
Grid.Column="1" Margin="0,30,0,0" VerticalAlignment="Center" Height="25" IsEnabled="False"
Click="DocumentRemoveButton_Click"/>
<Label Content="Ausgewählte Dokumente"
Grid.Column="2" Margin="10,8,10,10"/>
<ListBox x:Name="SelectedDocumentsList" DisplayMemberPath="Name"
Grid.Column="2" Margin="10,30,10,37"
SelectionChanged="SelectedDocumentsList_SelectionChanged">
<ListBox.InputBindings>
<KeyBinding Key="Delete" Command="{Binding Path=DeleteCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>
</ListBox.InputBindings>
</ListBox>
<Button x:Name="SelectDocumentButton" Content="Durchsuchen..."
Grid.Column="2" VerticalAlignment="Bottom" Margin="10,10,10,10" Height="22"
Click="SelectDocumentButton_Click"/>
</Grid>
<GroupBox x:Name="DocumentBox" Header="Dokument" Margin="10,170,10,47" HorizontalAlignment="Stretch">
<Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<CheckBox x:Name="DocumentNonDeliverersInput" Content="Auch Nicht-Lieferanten miteinbeziehen" <Label Content="Verfügbare Dokumente"
Margin="10,10,10,10" Grid.Column="1"/> Grid.Column="0" Margin="10,8,10,10"/>
<ListBox x:Name="AvaiableDocumentsList"
Grid.Column="0" Margin="10,30,10,10"
SelectionChanged="AvaiableDocumentsList_SelectionChanged"/>
<Label x:Name="DocumentFooterLabel" Content="Fußtext:" Margin="10,40,0,10"/> <Button x:Name="DocumentAddButton" Content="&#xF0AF;" FontFamily="Segoe MDL2 Assets" FontSize="14"
<TextBox x:Name="DeliveryConfirmationFooterInput" Grid.Column="1" Grid.Column="1" Margin="0,0,0,30" VerticalAlignment="Center" Height="25" IsEnabled="False"
Margin="0,40,10,10" Height="Auto" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Click="DocumentAddButton_Click"/>
AcceptsReturn="True" VerticalScrollBarVisibility="Visible" TextWrapping="Wrap" <Button x:Name="DocumentRemoveButton" Content="&#xE74D;" FontFamily="Segoe MDL2 Assets" FontSize="16" Padding="1.5,0,0,0"
TextChanged="DocumentInput_TextChanged"/> Grid.Column="1" Margin="0,30,0,0" VerticalAlignment="Center" Height="25" IsEnabled="False"
<TextBox x:Name="CreditNoteFooterInput" Grid.Column="1" Click="DocumentRemoveButton_Click"/>
Margin="0,10,10,10" Height="Auto" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
AcceptsReturn="True" VerticalScrollBarVisibility="Visible" TextWrapping="Wrap" <Label Content="Ausgewählte Dokumente"
TextChanged="DocumentInput_TextChanged"/> Grid.Column="2" Margin="10,8,10,10"/>
<ListBox x:Name="SelectedDocumentsList" DisplayMemberPath="Name"
Grid.Column="2" Margin="10,30,10,37"
SelectionChanged="SelectedDocumentsList_SelectionChanged">
<ListBox.InputBindings>
<KeyBinding Key="Delete" Command="{Binding Path=DeleteCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>
</ListBox.InputBindings>
</ListBox>
<Button x:Name="SelectDocumentButton" Content="Durchsuchen..."
Grid.Column="2" VerticalAlignment="Bottom" Margin="10,10,10,10" Height="22"
Click="SelectDocumentButton_Click"/>
</Grid> </Grid>
</GroupBox>
<TextBox x:Name="PostalLocation" Grid.Column="1" TextChanged="PostalLocation_TextChanged" <GroupBox x:Name="DocumentBox" Header="Dokument" Margin="10,170,10,47" HorizontalAlignment="Stretch">
Margin="10,30,10,10" Width="120" HorizontalAlignment="Left"/> <Grid>
<Label Content=", am" Margin="130,30,10,10" FontSize="14" Grid.Column="1"/> <Grid.ColumnDefinitions>
<TextBox x:Name="PostalDate" Grid.Column="1" Text="01.01.2020" <ColumnDefinition Width="70"/>
Margin="162,30,10,10" Width="78" HorizontalAlignment="Left" <ColumnDefinition Width="*"/>
TextChanged="Date_TextChanged" LostFocus="Date_LostFocus"/> </Grid.ColumnDefinitions>
<GroupBox Header="Adressaten" Margin="10,70,10,47" Grid.Column="1"> <CheckBox x:Name="DocumentNonDeliverersInput" Content="Auch Nicht-Lieferanten miteinbeziehen"
<Grid> Margin="10,10,10,10" Grid.Column="1"/>
<RadioButton GroupName="Recipients" x:Name="RecipientsActiveMembersInput" Content="aktive Mitglieder"
Margin="10,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<RadioButton GroupName="Recipients" x:Name="RecipientsAreaComMembersInput" Content="Mitglieder mit Flächenbindung"
Margin="10,30,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<RadioButton GroupName="Recipients" x:Name="RecipientsDeliveryAncmtMembersInput" Content="Mitglieder mit Anmeldung"
Margin="10,50,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<RadioButton GroupName="Recipients" x:Name="RecipientsDeliveryMembersInput" Content="Lieferanten der Saison"
Margin="10,70,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<RadioButton GroupName="Recipients" x:Name="RecipientsNonDeliveryMembersInput" Content="Nicht-Lieferanten der Saison"
Margin="10,90,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<RadioButton GroupName="Recipients" x:Name="RecipientsCustomInput" Content="Benutzerdefiniert"
Margin="10,110,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<Label Content="Zwst.:" x:Name="MemberBranchLabel" Margin="10,140,0,10"/> <Label x:Name="DocumentFooterLabel" Content="Fußtext:" Margin="10,40,0,10"/>
<ctrl:CheckComboBox x:Name="MemberBranchInput" AllItemsSelectedContent="Alle Stammzweigstellen" Delimiter=", " DisplayMemberPath="Name" <TextBox x:Name="DeliveryConfirmationFooterInput" Grid.Column="1"
Margin="50,140,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25" Margin="0,40,10,10" Height="Auto" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
SelectionChanged="MemberInput_SelectionChanged"/> AcceptsReturn="True" VerticalScrollBarVisibility="Visible" TextWrapping="Wrap"
TextChanged="DocumentInput_TextChanged"/>
<TextBox x:Name="CreditNoteFooterInput" Grid.Column="1"
Margin="0,10,10,10" Height="Auto" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
AcceptsReturn="True" VerticalScrollBarVisibility="Visible" TextWrapping="Wrap"
TextChanged="DocumentInput_TextChanged"/>
</Grid>
</GroupBox>
<Label Content="Gem.:" x:Name="MemberKgLabel" Margin="10,170,0,10"/> <TextBox x:Name="PostalLocation" Grid.Column="1" TextChanged="PostalLocation_TextChanged"
<ctrl:CheckComboBox x:Name="MemberKgInput" AllItemsSelectedContent="Alle Stammgemeinden" Delimiter=", " DisplayMemberPath="Name" Margin="10,30,10,10" Width="120" HorizontalAlignment="Left"/>
IsSelectAllActive="True" SelectAllContent="Alle Stammgemeinden" <Label Content=", am" Margin="130,30,10,10" FontSize="14" Grid.Column="1"/>
Margin="50,170,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25" <TextBox x:Name="PostalDate" Grid.Column="1" Text="01.01.2020"
SelectionChanged="MemberInput_SelectionChanged"/> Margin="162,30,10,10" Width="78" HorizontalAlignment="Left"
TextChanged="Date_TextChanged" LostFocus="Date_LostFocus"/>
<Label Content="Bio-Betrieb:" x:Name="MemberOrganicLabel" Margin="10,200,0,10"/> <GroupBox Header="Adressaten" Margin="10,70,10,47" Grid.Column="1">
<RadioButton x:Name="MemberOrganicYesInput" Content="Ja" GroupName="MemberOrganic" <Grid>
Margin="80,205,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" <RadioButton GroupName="Recipients" x:Name="RecipientsActiveMembersInput" Content="aktive Mitglieder"
Checked="MemberInput_Checked"/> Margin="10,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
<RadioButton x:Name="MemberOrganicNoInput" Content="Nein" GroupName="MemberOrganic" Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
Margin="125,205,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" <RadioButton GroupName="Recipients" x:Name="RecipientsAreaComMembersInput" Content="Mitglieder mit Flächenbindung"
Checked="MemberInput_Checked"/> Margin="10,30,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
<RadioButton x:Name="MemberOrganicIndifferentInput" Content="Egal" GroupName="MemberOrganic" Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
Margin="180,205,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" <RadioButton GroupName="Recipients" x:Name="RecipientsDeliveryAncmtMembersInput" Content="Mitglieder mit Anmeldung"
Checked="MemberInput_Checked"/> Margin="10,50,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<RadioButton GroupName="Recipients" x:Name="RecipientsDeliveryMembersInput" Content="Lieferanten der Saison"
Margin="10,70,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<RadioButton GroupName="Recipients" x:Name="RecipientsNonDeliveryMembersInput" Content="Nicht-Lieferanten der Saison"
Margin="10,90,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<RadioButton GroupName="Recipients" x:Name="RecipientsCustomInput" Content="Benutzerdefiniert"
Margin="10,110,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<Label Content="Funktionär:" x:Name="MemberFunktionärLabel" Margin="10,230,0,10"/> <Label Content="Zwst.:" x:Name="MemberBranchLabel" Margin="10,140,0,10"/>
<RadioButton x:Name="MemberFunktionärYesInput" Content="Ja" GroupName="MemberFunktionär" <ctrl:CheckComboBox x:Name="MemberBranchInput" AllItemsSelectedContent="Alle Stammzweigstellen" Delimiter=", " DisplayMemberPath="Name"
Margin="80,235,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="50,140,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25"
Checked="MemberInput_Checked"/> SelectionChanged="MemberInput_SelectionChanged"/>
<RadioButton x:Name="MemberFunktionärNoInput" Content="Nein" GroupName="MemberFunktionär"
Margin="125,235,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="MemberInput_Checked"/>
<RadioButton x:Name="MemberFunktionärIndifferentInput" Content="Egal" GroupName="MemberFunktionär"
Margin="180,235,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="MemberInput_Checked"/>
<Label Content="Vtrg.:" x:Name="MemberAreaComLabel" Margin="10,260,0,10"/> <Label Content="Gem.:" x:Name="MemberKgLabel" Margin="10,170,0,10"/>
<ctrl:CheckComboBox x:Name="MemberAreaComInput" AllItemsSelectedContent="Alle Vertragsarten" Delimiter=", " DisplayMemberPath="VtrgId" <ctrl:CheckComboBox x:Name="MemberKgInput" AllItemsSelectedContent="Alle Stammgemeinden" Delimiter=", " DisplayMemberPath="Name"
IsSelectAllActive="True" SelectAllContent="Alle Vertragsarten" IsSelectAllActive="True" SelectAllContent="Alle Stammgemeinden"
Margin="50,260,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25" Margin="50,170,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25"
SelectionChanged="MemberInput_SelectionChanged"/> SelectionChanged="MemberInput_SelectionChanged"/>
<Label Content="Tag:" x:Name="MemberDeliveryAncmtLabel" Margin="10,260,0,10"/> <Label Content="Bio-Betrieb:" x:Name="MemberOrganicLabel" Margin="10,200,0,10"/>
<ctrl:CheckComboBox x:Name="MemberDeliveryAncmtInput" AllItemsSelectedContent="Alle Lesepläne" Delimiter=", " DisplayMemberPath="Identifier" <RadioButton x:Name="MemberOrganicYesInput" Content="Ja" GroupName="MemberOrganic"
IsSelectAllActive="True" SelectAllContent="Alle Lesepläne" Margin="80,205,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Margin="50,260,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25" Checked="MemberInput_Checked"/>
SelectionChanged="MemberInput_SelectionChanged"/> <RadioButton x:Name="MemberOrganicNoInput" Content="Nein" GroupName="MemberOrganic"
Margin="125,205,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="MemberInput_Checked"/>
<RadioButton x:Name="MemberOrganicIndifferentInput" Content="Egal" GroupName="MemberOrganic"
Margin="180,205,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="MemberInput_Checked"/>
<ctrl:CheckComboBox x:Name="MemberCustomInput" AllItemsSelectedContent="Alle Mitglieder" Delimiter=", " DisplayMemberPath="AdministrativeName" <Label Content="Funktionär:" x:Name="MemberFunktionärLabel" Margin="10,230,0,10"/>
IsSelectAllActive="True" SelectAllContent="Alle Mitglieder" <RadioButton x:Name="MemberFunktionärYesInput" Content="Ja" GroupName="MemberFunktionär"
Margin="10,140,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25" Margin="80,235,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
SelectionChanged="MemberInput_SelectionChanged"/> Checked="MemberInput_Checked"/>
</Grid> <RadioButton x:Name="MemberFunktionärNoInput" Content="Nein" GroupName="MemberFunktionär"
</GroupBox> Margin="125,235,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="MemberInput_Checked"/>
<RadioButton x:Name="MemberFunktionärIndifferentInput" Content="Egal" GroupName="MemberFunktionär"
Margin="180,235,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="MemberInput_Checked"/>
<Button x:Name="ContinueButton" Content="Weiter" Grid.Column="1" <Label Content="Vtrg.:" x:Name="MemberAreaComLabel" Margin="10,260,0,10"/>
Margin="10,10,10,10" Height="27" Width="100" Padding="9,3" FontSize="14" <ctrl:CheckComboBox x:Name="MemberAreaComInput" AllItemsSelectedContent="Alle Vertragsarten" Delimiter=", " DisplayMemberPath="VtrgId"
VerticalAlignment="Bottom" HorizontalAlignment="Right" IsSelectAllActive="True" SelectAllContent="Alle Vertragsarten"
Click="ContinueButton_Click"/> Margin="50,260,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25"
</Grid> SelectionChanged="MemberInput_SelectionChanged"/>
</TabItem>
<TabItem Header="Absenden" Visibility="Collapsed"> <Label Content="Tag:" x:Name="MemberDeliveryAncmtLabel" Margin="10,260,0,10"/>
<Grid> <ctrl:CheckComboBox x:Name="MemberDeliveryAncmtInput" AllItemsSelectedContent="Alle Lesepläne" Delimiter=", " DisplayMemberPath="Identifier"
<Grid.ColumnDefinitions> IsSelectAllActive="True" SelectAllContent="Alle Lesepläne"
<ColumnDefinition Width="*"/> Margin="50,260,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25"
<ColumnDefinition Width="1.5*"/> SelectionChanged="MemberInput_SelectionChanged"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="80"/>
</Grid.RowDefinitions>
<GroupBox Header="Post" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10,10,5,10" Grid.Column="0"> <ctrl:CheckComboBox x:Name="MemberCustomInput" AllItemsSelectedContent="Alle Mitglieder" Delimiter=", " DisplayMemberPath="AdministrativeName"
<Grid> IsSelectAllActive="True" SelectAllContent="Alle Mitglieder"
<GroupBox Header="Zusenden an..." Margin="10,10,10,10" Height="150" Width="220" VerticalAlignment="Top" HorizontalAlignment="Left"> Margin="10,140,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25"
<StackPanel> SelectionChanged="MemberInput_SelectionChanged"/>
<RadioButton x:Name="PostalAllInput" Margin="10,10,10,2.5" Click="PostalInput_Changed"> </Grid>
<TextBlock> </GroupBox>
...alle (<Run Text="{Binding Path=PostalAllCount, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>)
</TextBlock>
</RadioButton>
<RadioButton x:Name="PostalWishInput" Margin="10,2.5,10,2.5" Click="PostalInput_Changed" IsChecked="True">
<TextBlock>
...Mitglieder, die Zusendung<LineBreak/>
per Post wünschen (<Run Text="{Binding Path=PostalWishCount, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>)
</TextBlock>
</RadioButton>
<RadioButton x:Name="PostalNoEmailInput" Margin="10,2.5,10,2.5" Click="PostalInput_Changed">
<TextBlock>
...Mitglieder, die keine<LineBreak/>
E-Mail erhalten würden (<Run Text="{Binding Path=PostalNoEmailCount, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>)
</TextBlock>
</RadioButton>
<RadioButton x:Name="PostalNobodyInput" Margin="10,2.5,10,10" Content="...niemanden (0)" Click="PostalInput_Changed"/>
</StackPanel>
</GroupBox>
<GroupBox Header="Sortieren nach" Margin="10,180,10,10" Width="180" Height="80" VerticalAlignment="Top" HorizontalAlignment="Left"> <Button x:Name="ContinueButton" Content="Weiter" Grid.Column="1"
<StackPanel Margin="5,5,0,5"> Margin="10,10,10,10" Height="27" Width="100" Padding="9,3" FontSize="14"
<RadioButton GroupName="Order" x:Name="OrderMgNrInput" Content="Mitgliedsnummer" Click="OrderInput_Changed" IsChecked="True"/> VerticalAlignment="Bottom" HorizontalAlignment="Right"
<RadioButton GroupName="Order" x:Name="OrderNameInput" Content="Name" Click="OrderInput_Changed"/> Click="ContinueButton_Click"/>
<RadioButton GroupName="Order" x:Name="OrderPlzInput" Content="PLZ, Ort, Name" Click="OrderInput_Changed"/> </Grid>
</StackPanel> </TabItem>
</GroupBox>
<CheckBox x:Name="DoublePagedInput" Margin="20,270,10,10" Content="Doppelseitig drucken" <TabItem Header="Absenden" Visibility="Collapsed">
VerticalAlignment="Top" HorizontalAlignment="Left" <Grid>
Checked="DoublePagedInput_Changed" Unchecked="DoublePagedInput_Changed"/>
<TextBox x:Name="PostalSender1" TextChanged="PostalSender_TextChanged" IsEnabled="False"
Margin="10,300,10,10"/>
<TextBox x:Name="PostalSender2" TextChanged="PostalSender_TextChanged"
Margin="10,330,10,10"/>
</Grid>
</GroupBox>
<GroupBox Header="E-Mail" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5,10,10,10" Grid.Column="1">
<Grid>
<GroupBox Header="Zusenden an..." Margin="80,10,10,10" Width="220" Height="110" VerticalAlignment="Top" HorizontalAlignment="Left">
<StackPanel>
<RadioButton x:Name="EmailAllInput" Margin="10,10,10,2.5" Checked="EmailInput_Changed">
<TextBlock>
...alle mit E-Mail-Adressen (<Run Text="{Binding Path=EmailAllCount, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>)
</TextBlock>
</RadioButton>
<RadioButton x:Name="EmailWishInput" Margin="10,2.5,10,2.5" IsChecked="True" Checked="EmailInput_Changed">
<TextBlock>
...Mitglieder, die Zusendung<LineBreak/>
per E-Mail wünschen (<Run Text="{Binding Path=EmailWishCount, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>)
</TextBlock>
</RadioButton>
<RadioButton x:Name="EmailNobodyInput" Margin="10,2.5,10,10" Content="...niemanden (0)" Checked="EmailInput_Changed"/>
</StackPanel>
</GroupBox>
<Label Content="Betreff:" Margin="10,130,10,10"/>
<TextBox x:Name="EmailSubjectInput" Margin="80,130,10,10"/>
<Label Content="Nachricht:" Margin="10,160,10,10"/>
<TextBox x:Name="EmailBodyInput"
Margin="80,160,10,10" VerticalAlignment="Stretch" Height="Auto"
TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" AcceptsReturn="True"/>
</Grid>
</GroupBox>
<Grid Grid.Row="1" Grid.ColumnSpan="2" Width="400" Height="59">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="0.7*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="10"/> <ColumnDefinition Width="1.5*"/>
<ColumnDefinition Width="0.4*"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="0.6*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="5"/> <RowDefinition Height="80"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Button x:Name="GenerateButton" Content="Generieren" <GroupBox Header="Post" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10,10,5,10" Grid.Column="0">
Grid.Row="0" Grid.Column="0" FontSize="14" <Grid>
Click="GenerateButton_Click"/> <GroupBox Header="Zusenden an..." Margin="10,10,10,10" Height="150" Width="220" VerticalAlignment="Top" HorizontalAlignment="Left">
<ProgressBar x:Name="ProgressBar" <StackPanel>
Grid.Row="2" Grid.Column="0" SnapsToDevicePixels="True"/> <RadioButton x:Name="PostalAllInput" Margin="10,10,10,2.5" Click="PostalInput_Changed">
<TextBlock>
...alle (<Run Text="{Binding Path=PostalAllCount, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>)
</TextBlock>
</RadioButton>
<RadioButton x:Name="PostalWishInput" Margin="10,2.5,10,2.5" Click="PostalInput_Changed" IsChecked="True">
<TextBlock>
...Mitglieder, die Zusendung<LineBreak/>
per Post wünschen (<Run Text="{Binding Path=PostalWishCount, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>)
</TextBlock>
</RadioButton>
<RadioButton x:Name="PostalNoEmailInput" Margin="10,2.5,10,2.5" Click="PostalInput_Changed">
<TextBlock>
...Mitglieder, die keine<LineBreak/>
E-Mail erhalten würden (<Run Text="{Binding Path=PostalNoEmailCount, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>)
</TextBlock>
</RadioButton>
<RadioButton x:Name="PostalNobodyInput" Margin="10,2.5,10,10" Content="...niemanden (0)" Click="PostalInput_Changed"/>
</StackPanel>
</GroupBox>
<Button x:Name="PreviewButton" Content="Vorschau" IsEnabled="False" <GroupBox Header="Sortieren nach" Margin="10,180,10,10" Width="180" Height="80" VerticalAlignment="Top" HorizontalAlignment="Left">
Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="3" FontSize="14" <StackPanel Margin="5,5,0,5">
Click="PreviewButton_Click"/> <RadioButton GroupName="Order" x:Name="OrderMgNrInput" Content="Mitgliedsnummer" Click="OrderInput_Changed" IsChecked="True"/>
<Button x:Name="PrintButton" Content="Drucken" IsEnabled="False" <RadioButton GroupName="Order" x:Name="OrderNameInput" Content="Name" Click="OrderInput_Changed"/>
Grid.Row="2" Grid.Column="2" FontSize="14" <RadioButton GroupName="Order" x:Name="OrderPlzInput" Content="PLZ, Ort, Name" Click="OrderInput_Changed"/>
Click="PrintButton_Click"/> </StackPanel>
<Button x:Name="EmailButton" Content="E-Mails verschicken" IsEnabled="False" </GroupBox>
Grid.Row="2" Grid.Column="4" FontSize="14"
Click="EmailButton_Click"/> <CheckBox x:Name="DoublePagedInput" Margin="20,270,10,10" Content="Doppelseitig drucken"
VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="DoublePagedInput_Changed" Unchecked="DoublePagedInput_Changed"/>
<TextBox x:Name="PostalSender1" TextChanged="PostalSender_TextChanged" IsEnabled="False"
Margin="10,300,10,10"/>
<TextBox x:Name="PostalSender2" TextChanged="PostalSender_TextChanged"
Margin="10,330,10,10"/>
</Grid>
</GroupBox>
<GroupBox Header="E-Mail" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5,10,10,10" Grid.Column="1">
<Grid>
<GroupBox Header="Zusenden an..." Margin="80,10,10,10" Width="220" Height="110" VerticalAlignment="Top" HorizontalAlignment="Left">
<StackPanel>
<RadioButton x:Name="EmailAllInput" Margin="10,10,10,2.5" Checked="EmailInput_Changed">
<TextBlock>
...alle mit E-Mail-Adressen (<Run Text="{Binding Path=EmailAllCount, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>)
</TextBlock>
</RadioButton>
<RadioButton x:Name="EmailWishInput" Margin="10,2.5,10,2.5" IsChecked="True" Checked="EmailInput_Changed">
<TextBlock>
...Mitglieder, die Zusendung<LineBreak/>
per E-Mail wünschen (<Run Text="{Binding Path=EmailWishCount, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>)
</TextBlock>
</RadioButton>
<RadioButton x:Name="EmailNobodyInput" Margin="10,2.5,10,10" Content="...niemanden (0)" Checked="EmailInput_Changed"/>
</StackPanel>
</GroupBox>
<Label Content="Betreff:" Margin="10,130,10,10"/>
<TextBox x:Name="EmailSubjectInput" Margin="80,130,10,10"/>
<Label Content="Nachricht:" Margin="10,160,10,10"/>
<TextBox x:Name="EmailBodyInput"
Margin="80,160,10,10" VerticalAlignment="Stretch" Height="Auto"
TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" AcceptsReturn="True"/>
</Grid>
</GroupBox>
<Grid Grid.Row="1" Grid.ColumnSpan="2" Width="400" Height="59">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.7*"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="0.4*"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="0.6*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="5"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button x:Name="GenerateButton" Content="Generieren"
Grid.Row="0" Grid.Column="0" FontSize="14"
Click="GenerateButton_Click"/>
<ProgressBar x:Name="ProgressBar"
Grid.Row="2" Grid.Column="0" SnapsToDevicePixels="True"/>
<Button x:Name="PreviewButton" Content="Vorschau" IsEnabled="False"
Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="3" FontSize="14"
Click="PreviewButton_Click"/>
<Button x:Name="PrintButton" Content="Drucken" IsEnabled="False"
Grid.Row="2" Grid.Column="2" FontSize="14"
Click="PrintButton_Click"/>
<Button x:Name="EmailButton" Content="E-Mails verschicken" IsEnabled="False"
Grid.Row="2" Grid.Column="4" FontSize="14"
Click="EmailButton_Click"/>
</Grid>
<Button x:Name="BackButton" Content="Zurück" Grid.Row="1"
Margin="10,10,10,10" Height="27" Width="100" Padding="9,3" FontSize="14"
VerticalAlignment="Bottom" HorizontalAlignment="Left"
Click="BackButton_Click"/>
</Grid> </Grid>
</TabItem>
</TabControl>
<Button x:Name="BackButton" Content="Zurück" Grid.Row="1" <StatusBar Grid.Row="2" BorderThickness="0,1,0,0" BorderBrush="Gray">
Margin="10,10,10,10" Height="27" Width="100" Padding="9,3" FontSize="14" <StatusBar.ItemsPanel>
VerticalAlignment="Bottom" HorizontalAlignment="Left" <ItemsPanelTemplate>
Click="BackButton_Click"/> <Grid>
</Grid> <Grid.ColumnDefinitions>
</TabItem> <ColumnDefinition Width="1*"/>
</TabControl> <ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem>
<TextBlock>
Adressaten: <Run x:Name="StatusRecipients" Text="0"/>
</TextBlock>
</StatusBarItem>
<Separator Grid.Column="1"/>
<StatusBarItem Grid.Column="2">
<TextBlock>
Adressaten (Post): <Run x:Name="StatusPostalRecipients" Text="0"/>
</TextBlock>
</StatusBarItem>
<Separator Grid.Column="3"/>
<StatusBarItem Grid.Column="4">
<TextBlock>
Adressaten (E-Mail): <Run x:Name="StatusEmailRecipients" Text="0"/>
</TextBlock>
</StatusBarItem>
</StatusBar>
</Grid>
</local:ContextWindow> </local:ContextWindow>

@ -58,6 +58,7 @@ namespace Elwig.Windows {
public IEnumerable<Member> Recipients = []; public IEnumerable<Member> Recipients = [];
protected Document? PrintDocument; protected Document? PrintDocument;
protected Dictionary<Member, List<Document>>? PrintMemberDocuments;
protected Dictionary<Member, List<Document>>? EmailDocuments; protected Dictionary<Member, List<Document>>? EmailDocuments;
public static readonly DependencyProperty PostalAllCountProperty = DependencyProperty.Register(nameof(PostalAllCount), typeof(int), typeof(MailWindow)); public static readonly DependencyProperty PostalAllCountProperty = DependencyProperty.Register(nameof(PostalAllCount), typeof(int), typeof(MailWindow));
@ -122,6 +123,26 @@ namespace Elwig.Windows {
DeliveryConfirmationFooterInput.Text = App.Client.TextDeliveryConfirmation; DeliveryConfirmationFooterInput.Text = App.Client.TextDeliveryConfirmation;
CreditNoteFooterInput.Text = App.Client.TextCreditNote; CreditNoteFooterInput.Text = App.Client.TextCreditNote;
DocumentNonDeliverersInput.IsChecked = App.Client.MailIncludeNonDeliverers;
DoublePagedInput.IsChecked = App.Client.MailDoublePaged;
switch (App.Client.MailSendPostal) {
case 0: PostalNobodyInput.IsChecked = true; break;
case 1: PostalNoEmailInput.IsChecked = true; break;
case 2: PostalWishInput.IsChecked = true; break;
case 3: PostalAllInput.IsChecked = true; break;
}
switch (App.Client.MailSendEmail) {
case 0: EmailNobodyInput.IsChecked = true; break;
case 1: EmailWishInput.IsChecked = true; break;
case 2: EmailAllInput.IsChecked = true; break;
}
switch (App.Client.MailOrdering) {
case 0: OrderMgNrInput.IsChecked = true; break;
case 1: OrderNameInput.IsChecked = true; break;
case 2: OrderPlzInput.IsChecked = true; break;
}
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;
@ -242,6 +263,11 @@ namespace Elwig.Windows {
rb.IsEnabled = true; rb.IsEnabled = true;
} }
private void Menu_Help_Log_Click(object sender, RoutedEventArgs evt) {
var w = new MailLogWindow();
w.Show();
}
private void ContinueButton_Click(object sender, RoutedEventArgs evt) { private void ContinueButton_Click(object sender, RoutedEventArgs evt) {
TabControl.SelectedIndex = 1; TabControl.SelectedIndex = 1;
TabControl.AllowDrop = false; TabControl.AllowDrop = false;
@ -312,7 +338,7 @@ namespace Elwig.Windows {
if (idx == 0) { if (idx == 0) {
SelectedDocs.Add(new(DocType.MemberDataSheet, s, null)); SelectedDocs.Add(new(DocType.MemberDataSheet, s, null));
} else if (idx == 1) { } else if (idx == 1) {
SelectedDocs.Add(new(DocType.DeliveryConfirmation, s, (Year, DocumentNonDeliverersInput.IsChecked == true))); SelectedDocs.Add(new(DocType.DeliveryConfirmation, s, Year));
RecipientsDeliveryMembersInput.IsChecked = true; RecipientsDeliveryMembersInput.IsChecked = true;
} else if (idx >= 2) { } else if (idx >= 2) {
var name = s.Split(" ")[^1]; var name = s.Split(" ")[^1];
@ -449,18 +475,32 @@ namespace Elwig.Windows {
} }
private void UpdatePostalEmailRecipients() { private void UpdatePostalEmailRecipients() {
var modeEmail = EmailAllInput.IsChecked == true ? 2 : EmailWishInput.IsChecked == true ? 1 : 0;
var modePostal = PostalAllInput.IsChecked == true ? 3 : PostalWishInput.IsChecked == true ? 2 : PostalNoEmailInput.IsChecked == true ? 1 : 0;
EmailAllCount = Recipients.Count(m => m.EmailAddresses.Count > 0); EmailAllCount = Recipients.Count(m => m.EmailAddresses.Count > 0);
EmailWishCount = Recipients.Count(m => m.EmailAddresses.Count > 0 && m.ContactViaEmail); EmailWishCount = Recipients.Count(m => m.EmailAddresses.Count > 0 && m.ContactViaEmail);
PostalAllCount = Recipients.Count(); PostalAllCount = Recipients.Count();
PostalWishCount = Recipients.Count(m => m.ContactViaPost); PostalWishCount = Recipients.Count(m => m.ContactViaPost);
var m = EmailAllInput.IsChecked == true ? 3 : EmailWishInput.IsChecked == true ? 2 : 1; var countEmail = (modeEmail == 2 ? EmailAllCount : modeEmail == 1 ? EmailWishCount : 0);
PostalNoEmailCount = PostalAllCount - (m == 3 ? EmailAllCount : m == 2 ? EmailWishCount : 0); PostalNoEmailCount = PostalAllCount - countEmail;
var countPostal = (modePostal == 3 ? PostalAllCount : modePostal == 2 ? PostalWishCount : modePostal == 1 ? PostalNoEmailCount : 0);
if (IsLoaded) {
StatusRecipients.Text = $"{Recipients.Count():N0}";
StatusPostalRecipients.Text = $"{countPostal:N0}";
StatusEmailRecipients.Text = $"{countEmail:N0}";
}
ResetDocuments(); ResetDocuments();
} }
private async Task UpdateTextParameters() { private async Task UpdateClientParameters() {
var changed = false; var changed = false;
var dcInclude = DocumentNonDeliverersInput.IsChecked ?? false;
if (dcInclude != App.Client.MailIncludeNonDeliverers) {
App.Client.MailIncludeNonDeliverers = dcInclude;
changed = true;
}
var dcText = DeliveryConfirmationFooterInput.Text.Trim(); var dcText = DeliveryConfirmationFooterInput.Text.Trim();
if (dcText.Length == 0) dcText = null; if (dcText.Length == 0) dcText = null;
if (dcText != App.Client.TextDeliveryConfirmation) { if (dcText != App.Client.TextDeliveryConfirmation) {
@ -475,6 +515,53 @@ namespace Elwig.Windows {
changed = true; changed = true;
} }
var sendPostal = 0;
if (PostalAllInput.IsChecked ?? false) {
sendPostal = 3;
} else if (PostalWishInput.IsChecked ?? false) {
sendPostal = 2;
} else if (PostalNoEmailInput.IsChecked ?? false) {
sendPostal = 1;
}
if (sendPostal != App.Client.MailSendPostal) {
App.Client.MailSendPostal = sendPostal;
changed = true;
}
var sendEmail = 0;
if (EmailAllInput.IsChecked ?? false) {
sendEmail = 2;
} else if (EmailWishInput.IsChecked ?? false) {
sendEmail = 1;
}
if (sendEmail != App.Client.MailSendEmail) {
App.Client.MailSendEmail = sendEmail;
changed = true;
}
var ordering = 0;
if (OrderNameInput.IsChecked ?? false) {
ordering = 1;
} else if (OrderPlzInput.IsChecked ?? false) {
ordering = 2;
}
if (ordering != App.Client.MailOrdering) {
App.Client.MailOrdering = ordering;
changed = true;
}
var mailDoublePaged = DoublePagedInput.IsChecked ?? false;
if (mailDoublePaged != App.Client.MailDoublePaged) {
App.Client.MailDoublePaged = mailDoublePaged;
changed = true;
}
var sender2 = PostalSender2.Text.Trim();
if (sender2 != App.Client.Sender2) {
App.Client.Sender2 = sender2;
changed = true;
}
var emailSubject = EmailSubjectInput.Text.Trim(); var emailSubject = EmailSubjectInput.Text.Trim();
if (emailSubject.Length == 0) emailSubject = null; if (emailSubject.Length == 0) emailSubject = null;
if (emailSubject != App.Client.TextEmailSubject) { if (emailSubject != App.Client.TextEmailSubject) {
@ -519,7 +606,7 @@ namespace Elwig.Windows {
GenerateButton.IsEnabled = false; GenerateButton.IsEnabled = false;
DisposeDocs(); DisposeDocs();
await UpdateTextParameters(); await UpdateClientParameters();
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
@ -562,8 +649,7 @@ namespace Elwig.Windows {
Dictionary<(int, int), (IDictionary<int, CreditNoteDeliveryData>, IDictionary<int, PaymentMember>, BillingData)> cnData = []; Dictionary<(int, int), (IDictionary<int, CreditNoteDeliveryData>, IDictionary<int, PaymentMember>, BillingData)> cnData = [];
foreach (var doc in docs) { foreach (var doc in docs) {
if (doc.Type == DocType.DeliveryConfirmation) { if (doc.Type == DocType.DeliveryConfirmation) {
var details = ((int, bool))doc.Details!; var year = (int)doc.Details!;
var year = details.Item1;
try { try {
var b = new Billing(year); var b = new Billing(year);
@ -585,7 +671,7 @@ namespace Elwig.Windows {
var avnr = details.Item2; var avnr = details.Item2;
try { try {
cnData[(year, avnr)] = ( cnData[(year, avnr)] = (
await CreditNoteDeliveryData.ForPaymentVariant(ctx.CreditNoteDeliveryRows, ctx.Seasons, year, avnr), await CreditNoteDeliveryData.ForPaymentVariant(ctx.CreditNoteDeliveryRows, ctx.PaymentVariants, year, avnr),
await ctx.MemberPayments.Where(p => p.Year == year && p.AvNr == avnr).ToDictionaryAsync(c => c.MgNr), await ctx.MemberPayments.Where(p => p.Year == year && p.AvNr == avnr).ToDictionaryAsync(c => c.MgNr),
BillingData.FromJson((await ctx.PaymentVariants.FindAsync(year, avnr))!.Data) BillingData.FromJson((await ctx.PaymentVariants.FindAsync(year, avnr))!.Data)
); );
@ -610,13 +696,11 @@ namespace Elwig.Windows {
} else if (doc.Type == DocType.MemberDataSheet) { } else if (doc.Type == DocType.MemberDataSheet) {
return [new GeneratedDoc(new MemberDataSheet(m, ctx) { Date = postalDate })]; return [new GeneratedDoc(new MemberDataSheet(m, ctx) { Date = postalDate })];
} else if (doc.Type == DocType.DeliveryConfirmation) { } else if (doc.Type == DocType.DeliveryConfirmation) {
var details = ((int, bool))doc.Details!; var year = (int)doc.Details!;
var year = details.Item1;
var include = details.Item2;
DeliveryConfirmationDeliveryData data; DeliveryConfirmationDeliveryData data;
if (dcData[year].TryGetValue(m.MgNr, out var d)) { if (dcData[year].TryGetValue(m.MgNr, out var d)) {
data = d; data = d;
} else if (include) { } else if (App.Client.MailIncludeNonDeliverers) {
data = DeliveryConfirmationDeliveryData.CreateEmpty(year, m); data = DeliveryConfirmationDeliveryData.CreateEmpty(year, m);
} else { } else {
return []; return [];
@ -691,11 +775,13 @@ namespace Elwig.Windows {
EmailDocuments = email; EmailDocuments = email;
} }
var printDocs = memberDocs var printMemberDocs = memberDocs
.Where(d => .Where(d =>
printMode == 3 || printMode == 3 ||
(printMode == 2 && d.Member.ContactViaPost) || (printMode == 2 && d.Member.ContactViaPost) ||
(printMode == 1 && !emailRecipients.Contains(d.Member.MgNr))) (printMode == 1 && !emailRecipients.Contains(d.Member.MgNr)))
.ToList();
var printDocs = printMemberDocs
.SelectMany(m => { .SelectMany(m => {
var docs = m.Docs.Select(d => d.Doc).ToList(); var docs = m.Docs.Select(d => d.Doc).ToList();
if (docs.Count == 0 || m.Docs[0].Type == DocType.Custom) { if (docs.Count == 0 || m.Docs[0].Type == DocType.Custom) {
@ -720,6 +806,7 @@ namespace Elwig.Windows {
ProgressBar.Value = 100.0 * emailNum / totalNum + v * printNum / totalNum; ProgressBar.Value = 100.0 * emailNum / totalNum + v * printNum / totalNum;
})); }));
PrintDocument = print; PrintDocument = print;
PrintMemberDocuments = printMemberDocs.ToDictionary(m => m.Member, m => m.Docs.Select(d => d.Doc).ToList());
} catch (Exception exc) { } catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
UnlockInputs(); UnlockInputs();
@ -727,6 +814,9 @@ namespace Elwig.Windows {
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
return; return;
} }
} else {
PrintDocument = null;
PrintMemberDocuments = null;
} }
ProgressBar.Value = 100.0; ProgressBar.Value = 100.0;
@ -763,7 +853,7 @@ namespace Elwig.Windows {
} }
private async void PrintButton_Click(object sender, RoutedEventArgs evt) { private async void PrintButton_Click(object sender, RoutedEventArgs evt) {
if (PrintDocument == null) return; if (PrintDocument == null || PrintMemberDocuments == null) return;
PrintButton.IsEnabled = false; PrintButton.IsEnabled = false;
GenerateButton.IsEnabled = false; GenerateButton.IsEnabled = false;
@ -777,6 +867,14 @@ namespace Elwig.Windows {
PrintDocument.Show(); PrintDocument.Show();
} else { } else {
await PrintDocument.Print(); await PrintDocument.Print();
await Utils.AddSentMails(
PrintMemberDocuments.Select(d => (
"postal", d.Key.MgNr, d.Key.AdministrativeName,
new string[] { d.Value.Select(d => (d as BusinessDocument)?.Address).FirstOrDefault(a => a != null) ?? d.Key.FullAddress },
d.Value.Select(d => d.Title).FirstOrDefault("Briefkopf"),
d.Value.Select(d => d.Title).ToArray()
))
);
} }
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
} }
@ -808,6 +906,7 @@ namespace Elwig.Windows {
Mouse.OverrideCursor = Cursors.AppStarting; Mouse.OverrideCursor = Cursors.AppStarting;
var subject = EmailSubjectInput.Text; var subject = EmailSubjectInput.Text;
var text = EmailBodyInput.Text; var text = EmailBodyInput.Text;
await Utils.AddSentMailBody(subject, text, EmailDocuments.Count);
foreach (var (m, docs) in EmailDocuments) { foreach (var (m, docs) in EmailDocuments) {
using var msg = new MimeMessage(); using var msg = new MimeMessage();
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));
@ -822,6 +921,12 @@ namespace Elwig.Windows {
} }
msg.Body = body; msg.Body = body;
await client!.SendAsync(msg); await client!.SendAsync(msg);
await Utils.AddSentMails([(
"email", m.MgNr, m.AdministrativeName,
m.EmailAddresses.OrderBy(a => a.Nr).Select(a => a.Address).ToArray(),
subject,
docs.Select(d => d.Title).ToArray()
)]);
} }
MessageBox.Show("Erfolgreich alle E-Mails verschickt!", "Rundschreiben verschicken", MessageBoxButton.OK, MessageBoxImage.Information); MessageBox.Show("Erfolgreich alle E-Mails verschickt!", "Rundschreiben verschicken", MessageBoxButton.OK, MessageBoxImage.Information);
@ -870,7 +975,7 @@ namespace Elwig.Windows {
} }
private void PostalInput_Changed(object sender, RoutedEventArgs evt) { private void PostalInput_Changed(object sender, RoutedEventArgs evt) {
ResetDocuments(); UpdatePostalEmailRecipients();
} }
private void OrderInput_Changed(object sender, RoutedEventArgs evt) { private void OrderInput_Changed(object sender, RoutedEventArgs evt) {

@ -10,6 +10,7 @@
<Style TargetType="Button"> <Style TargetType="Button">
<Setter Property="VerticalAlignment" Value="Top"/> <Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="HorizontalAlignment" Value="Center"/> <Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="FontSize" Value="14"/> <Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="9,3"/> <Setter Property="Padding" Value="9,3"/>
<Setter Property="Height" Value="35"/> <Setter Property="Height" Value="35"/>
@ -19,23 +20,67 @@
<Grid> <Grid>
<Menu BorderThickness="0,0,0,1" VerticalAlignment="Top" Height="19" BorderBrush="LightGray" Background="White"> <Menu BorderThickness="0,0,0,1" VerticalAlignment="Top" Height="19" BorderBrush="LightGray" Background="White">
<MenuItem Header="Datenbank"> <MenuItem Header="Datenbank">
<MenuItem Header="Daten exportieren..." Click="Menu_Database_Export_Click" IsEnabled="False"/> <MenuItem Header="Daten exportieren..." Click="Menu_Database_Export_Click" IsEnabled="False">
<MenuItem Header="Daten importieren..." Click="Menu_Database_Import_Click"/> <MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xEDE1;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Daten importieren..." Click="Menu_Database_Import_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE8B5;"/>
</MenuItem.Icon>
</MenuItem>
<Separator/> <Separator/>
<MenuItem Header="Abfragen stellen" Click="Menu_Database_Query_Click"/> <MenuItem Header="Abfragen stellen" Click="Menu_Database_Query_Click">
<MenuItem Header="Speicherort öffnen..." Click="Menu_Database_Open_Click"/> <MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE756;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Speicherort öffnen..." Click="Menu_Database_Open_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xED25;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="Waage"> <MenuItem Header="Waage">
<MenuItem Header="Datum und Uhrzeit setzen" Click="Menu_Scale_SetDateTime_Click"/> <MenuItem Header="Datum und Uhrzeit setzen" Click="Menu_Scale_SetDateTime_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xEC92;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
<MenuItem x:Name="HelpMenu" Header="Hilfe"> <MenuItem x:Name="HelpMenu" Header="Hilfe">
<MenuItem Header="Über"/> <MenuItem Header="Über">
<MenuItem x:Name="Menu_Help_Update" Header="Nach Updates suchen" Click="Menu_Help_Update_Click"/> <MenuItem.Icon>
<MenuItem x:Name="Menu_Help_Smtp" Header="E-Mail-Einstellungen testen" Click="Menu_Help_Smtp_Click"/> <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE946;"/>
<MenuItem x:Name="Menu_Help_Log" Header="Fehler-Protokoll anzeigen" Click="Menu_Help_Log_Click"/> </MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_Help_Update" Header="Nach Updates suchen" Click="Menu_Help_Update_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE895;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_Help_Smtp" Header="E-Mail-Einstellungen testen" Click="Menu_Help_Smtp_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE715;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_Help_Log" Header="Fehler-Protokoll anzeigen" Click="Menu_Help_Log_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xF168;"/>
</MenuItem.Icon>
</MenuItem>
<Separator/> <Separator/>
<MenuItem x:Name="Menu_Help_Config" Header="Konfigurationsdatei öffnen..." Click="Menu_Help_Config_Click"/> <MenuItem x:Name="Menu_Help_Config" Header="Konfigurationsdatei öffnen..." Click="Menu_Help_Config_Click">
<MenuItem x:Name="Menu_Help_Directory" Header="Konfigurationsspeicherort öffnen..." Click="Menu_Help_Directory_Click"/> <MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xEC7A;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_Help_Directory" Header="Konfigurationsspeicherort öffnen..." Click="Menu_Help_Directory_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xED25;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
</Menu> </Menu>
@ -58,26 +103,64 @@
</TextBlock> </TextBlock>
</Grid> </Grid>
<Button x:Name="MemberAdminButton" Content="Mitglieder" Click="MemberAdminButton_Click" <Button x:Name="MemberAdminButton" Click="MemberAdminButton_Click"
Margin="0,170,205,0"/> Margin="0,170,205,0">
<Button x:Name="MailButton" Content="Rundschreiben" Click="MailButton_Click" <Grid>
Margin="205,170,0,0"/> <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE77B;"
<Button x:Name="DeliveryAdminButton" Content="Lieferungen" Click="DeliveryAdminButton_Click" TextAlignment="Left" HorizontalAlignment="Left" Padding="0.5,1.5,0,0"/>
Margin="0,210,205,0"/> <TextBlock TextAlignment="Center">Mitglieder</TextBlock>
<Button x:Name="ReceiptButton" Content="Übernahme" Click="ReceiptButton_Click" </Grid>
Margin="205,210,0,0"/> </Button>
<Button x:Name="BaseDataButton" Content="Stammdaten" Click="BaseDataButton_Click" <Button x:Name="MailButton" Click="MailButton_Click"
Margin="0,250,205,0"/> Margin="205,170,0,0">
<Button x:Name="DeliveryAncmtButton" Content="Anmeldungen" Click="DeliveryAncmtButton_Click" <Grid>
Margin="205,250,0,0"/> <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE715;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="0.5,1.5,0,0"/>
<TextBlock TextAlignment="Center">Rundschreiben</TextBlock>
</Grid>
</Button>
<Button x:Name="DeliveryAdminButton" Click="DeliveryAdminButton_Click"
Margin="0,210,205,0">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE736;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="0.5,1.5,0,0"/>
<TextBlock TextAlignment="Center">Lieferungen</TextBlock>
</Grid>
</Button>
<Button x:Name="ReceiptButton" Click="ReceiptButton_Click"
Margin="205,210,0,0">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xEC5B;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="0.5,1.5,0,0"/>
<TextBlock TextAlignment="Center">Übernahme</TextBlock>
</Grid>
</Button>
<Button x:Name="BaseDataButton" Click="BaseDataButton_Click"
Margin="0,250,205,0">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE713;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="0.5,1.5,0,0"/>
<TextBlock TextAlignment="Center">Stammdaten</TextBlock>
</Grid>
</Button>
<Button x:Name="DeliveryAncmtButton" Click="DeliveryAncmtButton_Click"
Margin="205,250,0,0">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xF0E4;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="0.5,1.5,0,0"/>
<TextBlock TextAlignment="Center">Anmeldungen</TextBlock>
</Grid>
</Button>
<Button x:Name="DownloadButton" Click="DownloadButton_Click" <Button x:Name="DownloadButton" Click="DownloadButton_Click"
Margin="310,135,0,0" Padding="1.5,0,0,0" Height="30" Width="30" Margin="310,135,0,0" Padding="0.375,0.5,0,0" Height="30" Width="30"
Content="&#xE896;" FontFamily="Segoe MDL2 Assets" FontSize="16" Content="&#xE896;" FontFamily="Segoe MDL2 Assets" FontSize="16"
HorizontalContentAlignment="Center"
ToolTip="Lieferungen und Mitgliederdaten anderer Zweigstellen herunterladen"/> ToolTip="Lieferungen und Mitgliederdaten anderer Zweigstellen herunterladen"/>
<Button x:Name="UploadButton" Click="UploadButton_Click" <Button x:Name="UploadButton" Click="UploadButton_Click"
Margin="375,135,0,0" Padding="1.5,0,0,0" Height="30" Width="30" Margin="375,135,0,0" Padding="1.0,0.5,0,0" Height="30" Width="30"
Content="&#xE898;" FontFamily="Segoe MDL2 Assets" FontSize="16" Content="&#xE898;" FontFamily="Segoe MDL2 Assets" FontSize="16"
HorizontalContentAlignment="Center"
ToolTip="Lieferungen dieser Zweigstelle hochladen"/> ToolTip="Lieferungen dieser Zweigstelle hochladen"/>
<Expander x:Name="SeasonFinish" Header="Leseabschluss" SnapsToDevicePixels="True" <Expander x:Name="SeasonFinish" Header="Leseabschluss" SnapsToDevicePixels="True"
@ -90,21 +173,104 @@
Margin="0,13,0,0" VerticalAlignment="Top" HorizontalAlignment="Center" Margin="0,13,0,0" VerticalAlignment="Top" HorizontalAlignment="Center"
TextChanged="SeasonInput_TextChanged"/> TextChanged="SeasonInput_TextChanged"/>
<Button x:Name="OverUnderDeliveryButton" Content="Über-/Unterlieferungen" <Button x:Name="DeliveryConfirmationButton"
Click="OverUnderDeliveryButton_Click"
Margin="0,50,195,10" Width="190"/>
<Button x:Name="DeliveryConfirmationButton" Content="Anlieferungsbestätigung"
Click="DeliveryConfirmationButton_Click" Click="DeliveryConfirmationButton_Click"
Margin="195,50,0,10" Width="190"/> Margin="0,50,195,10" Width="190">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE715;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="0.5,0.5,0,0"/>
<TextBlock FontSize="12" Margin="18,1,0,0" TextAlignment="Center">Anlieferungsbestätigung</TextBlock>
</Grid>
</Button>
<Button x:Name="BreakdownButton" Content="Sorten-/Qual.aufteilung" <Button x:Name="PaymentButton"
Click="BreakdownButton_Click"
Margin="0,90,195,10" Width="190"/>
<Button x:Name="PaymentButton" Content="Auszahlung"
Click="PaymentButton_Click" Click="PaymentButton_Click"
Margin="195,90,0,10" Width="190"/> Margin="195,50,0,10" Width="190">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE8EF;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="0.5,1.5,0,0"/>
<TextBlock TextAlignment="Center">Auszahlung</TextBlock>
</Grid>
</Button>
<Button x:Name="OverUnderDeliveryButton"
Click="OverUnderDeliveryButton_Click"
Margin="0,90,195,10" Width="190" Padding="3,5,5,5"
ToolTip="Über-/Unterlieferungen laut gezeichneten Geschäftsanteilen und Unterlieferungen nach Flächenbindungen">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE9F9;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="6.5,0.5,0,0"/>
<TextBlock FontSize="12" Margin="18,1,0,0" TextAlignment="Center">Über-/Unterlieferungen</TextBlock>
</Grid>
</Button>
<Button x:Name="BreakdownButton"
Click="BreakdownButton_Click"
Margin="195,90,0,10" Width="190" Padding="3,5,5,5"
ToolTip="Aufschlüsselung der Menge nach Zweigstelle, Mitglied, Sorte, Attribut/Bewirt., Qualitätsstufe, gebunden/ungebunden">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE9F9;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="6.5,0.5,0,0"/>
<TextBlock FontSize="12" Margin="18,1,0,0" TextAlignment="Center">Sorten-/Qual.aufschlüssel.</TextBlock>
</Grid>
</Button>
<Button x:Name="AreaCommitmentsButton"
Click="AreaCommitmentsButton_Click"
Margin="0,130,195,10" Width="190" Padding="3,5,5,5"
ToolTip="Aktive Flächenbindungen der Saison pro Mitglied und Sorte/Attribut">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE9F9;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="6.5,1.5,0,0"/>
<TextBlock Margin="18,0,0,0" TextAlignment="Center">Flächenbindungen</TextBlock>
</Grid>
</Button>
<Button x:Name="BreakdownMemberVarietyButton"
Click="BreakdownMemberVarietyButton_Click"
Margin="195,130,0,10" Width="190" Padding="3,5,5,5"
ToolTip="Liefermengen und Ertrag (kg/ha) pro Mitglied">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE9F9;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="6.5,1.5,0,0"/>
<TextBlock Margin="18,0,0,0" TextAlignment="Center">Liefermengen/Ertrag</TextBlock>
</Grid>
</Button>
<Grid VerticalAlignment="Bottom" Margin="50,175,50,15">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="1" TextAlignment="Right"><Bold>Mitglieder</Bold></TextBlock>
<TextBlock Grid.Row="0" Grid.Column="2" TextAlignment="Right"><Bold>Menge</Bold></TextBlock>
<TextBlock Grid.Row="0" Grid.Column="3" TextAlignment="Right"><Bold>Fläche</Bold></TextBlock>
<TextBlock Grid.Row="1" Grid.Column="0">Gesamt:</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="0">Gebunden:</TextBlock>
<TextBlock Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2">Ungebunden:</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="1" TextAlignment="Right" x:Name="SeasonStatMembersTotal">-</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="1" TextAlignment="Right" x:Name="SeasonStatMembersGeb">-</TextBlock>
<TextBlock Grid.Row="3" Grid.Column="1" TextAlignment="Right">-</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="2" TextAlignment="Right" x:Name="SeasonStatWeightTotal">-</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="2" TextAlignment="Right" x:Name="SeasonStatWeightGeb">-</TextBlock>
<TextBlock Grid.Row="3" Grid.Column="2" TextAlignment="Right" x:Name="SeasonStatWeightUngeb">-</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="3" TextAlignment="Right">-</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="3" TextAlignment="Right" x:Name="SeasonStatArea">-</TextBlock>
<TextBlock Grid.Row="3" Grid.Column="3" TextAlignment="Right">-</TextBlock>
</Grid>
</Grid> </Grid>
</Expander> </Expander>
</Grid> </Grid>

@ -251,7 +251,7 @@ namespace Elwig.Windows {
} }
private void SeasonFinish_Expanded(object sender, RoutedEventArgs evt) { private void SeasonFinish_Expanded(object sender, RoutedEventArgs evt) {
Height = 530; Height = 660;
} }
private void SeasonFinish_Collapsed(object sender, RoutedEventArgs evt) { private void SeasonFinish_Collapsed(object sender, RoutedEventArgs evt) {
@ -260,12 +260,34 @@ namespace Elwig.Windows {
private async void SeasonInput_TextChanged(object? sender, TextChangedEventArgs? evt) { private async void SeasonInput_TextChanged(object? sender, TextChangedEventArgs? evt) {
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
var s0 = await ctx.Seasons.FindAsync(SeasonInput.Value); var year = SeasonInput.Value;
var s0 = await ctx.Seasons.FindAsync(year);
var valid = (s0 != null); var valid = (s0 != null);
DeliveryConfirmationButton.IsEnabled = valid; DeliveryConfirmationButton.IsEnabled = valid;
OverUnderDeliveryButton.IsEnabled = valid;
PaymentButton.IsEnabled = valid; PaymentButton.IsEnabled = valid;
OverUnderDeliveryButton.IsEnabled = valid;
BreakdownButton.IsEnabled = valid; BreakdownButton.IsEnabled = valid;
AreaCommitmentsButton.IsEnabled = valid;
BreakdownMemberVarietyButton.IsEnabled = valid;
if (valid) {
var areaComs = Utils.ActiveAreaCommitments(ctx.AreaCommitments, year!.Value);
var weightTotal = await ctx.DeliveryParts.Where(p => p.Year == year).SumAsync(p => p.Weight);
var gebWeight = await ctx.DeliveryPartBuckets.Where(b => b.Year == year && b.Discr != "_").SumAsync(b => b.Value);
SeasonStatMembersTotal.Text = $"{await ctx.Deliveries.Where(d => d.Year == year).Select(d => d.Member).Distinct().CountAsync():N0}";
SeasonStatMembersGeb.Text = $"{await areaComs.Select(c => c.Member).Distinct().CountAsync():N0}";
SeasonStatWeightTotal.Text = $"{weightTotal:N0} kg";
SeasonStatWeightGeb.Text = $"{gebWeight:N0} kg";
SeasonStatWeightUngeb.Text = $"{weightTotal - gebWeight:N0} kg";
SeasonStatArea.Text = $"{await areaComs.SumAsync(c => c.Area):N0} m²";
} else {
SeasonStatMembersTotal.Text = "-";
SeasonStatMembersGeb.Text = "-";
SeasonStatWeightTotal.Text = "-";
SeasonStatWeightGeb.Text = "-";
SeasonStatWeightUngeb.Text = "-";
SeasonStatArea.Text = "-";
}
} }
private void DeliveryConfirmationButton_Click(object sender, RoutedEventArgs evt) { private void DeliveryConfirmationButton_Click(object sender, RoutedEventArgs evt) {
@ -275,6 +297,12 @@ namespace Elwig.Windows {
w.AddDeliveryConfirmation(); w.AddDeliveryConfirmation();
} }
private void PaymentButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year)
return;
App.FocusPaymentVariants(year);
}
private async void OverUnderDeliveryButton_Click(object sender, RoutedEventArgs evt) { private async void OverUnderDeliveryButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year) if (SeasonInput.Value is not int year)
return; return;
@ -297,23 +325,15 @@ namespace Elwig.Windows {
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
var tbl1 = await OverUnderDeliveryData.ForSeason(ctx.OverUnderDeliveryRows, year); var tbl1 = await OverUnderDeliveryData.ForSeason(ctx.OverUnderDeliveryRows, year);
var tbl2 = await AreaComUnderDeliveryData.ForSeason(ctx.AreaComUnderDeliveryRows, year); var tbl2 = await AreaComUnderDeliveryData.ForSeason(ctx.AreaComUnderDeliveryRows, year);
var tbl3 = await MemberDeliveryPerVariantData.ForSeason(ctx.MemberDeliveryPerVariantRows, year);
using var ods = new OdsFile(d.FileName); using var ods = new OdsFile(d.FileName);
await ods.AddTable(tbl1); await ods.AddTable(tbl1);
await ods.AddTable(tbl2); await ods.AddTable(tbl2);
await ods.AddTable(tbl3);
} catch (Exception exc) { } catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
} }
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
} }
private void PaymentButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year)
return;
App.FocusPaymentVariants(year);
}
private async void BreakdownButton_Click(object sender, RoutedEventArgs evt) { private async void BreakdownButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year) if (SeasonInput.Value is not int year)
return; return;
@ -346,5 +366,63 @@ namespace Elwig.Windows {
} }
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
} }
private async void AreaCommitmentsButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year)
return;
var d = new SaveFileDialog() {
FileName = $"Flächenbindungen-{year}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"Flächenbindungen {year} speichern unter - Elwig"
};
if (d.ShowDialog() == false)
return;
Mouse.OverrideCursor = Cursors.AppStarting;
try {
var b = new Billing(year);
await b.FinishSeason();
await b.CalculateBuckets();
App.HintContextChange();
using var ctx = new AppDbContext();
var tbl = await MemberAreaComsData.ForSeason(ctx.MemberAreaComsRows, year);
using var ods = new OdsFile(d.FileName);
await ods.AddTable(tbl);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
private async void BreakdownMemberVarietyButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year)
return;
var d = new SaveFileDialog() {
FileName = $"Liefermengen-Ertrag-{year}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"Liefermengen/Ertrag {year} speichern unter - Elwig"
};
if (d.ShowDialog() == false)
return;
Mouse.OverrideCursor = Cursors.AppStarting;
try {
var b = new Billing(year);
await b.FinishSeason();
await b.CalculateBuckets();
App.HintContextChange();
using var ctx = new AppDbContext();
var tbl = await MemberDeliveryPerVarietyData.ForSeason(ctx.MemberDeliveryPerVariantRows, year);
using var ods = new OdsFile(d.FileName);
await ods.AddTable(tbl);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
} }
} }

@ -52,19 +52,43 @@
<Menu Grid.ColumnSpan="3" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White"> <Menu Grid.ColumnSpan="3" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
<MenuItem Header="Kontaktieren"> <MenuItem Header="Kontaktieren">
<MenuItem x:Name="Menu_Contact_Email" Header="E-Mail senden..." IsEnabled="{Binding MemberHasEmail}" <MenuItem x:Name="Menu_Contact_Email" Header="E-Mail senden..." IsEnabled="{Binding MemberHasEmail}"
Click="Menu_Contact_Email_Click"/> Click="Menu_Contact_Email_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE715;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_Contact_Letterhead" Header="Briefkopf drucken" IsEnabled="{Binding IsMemberSelected}" <MenuItem x:Name="Menu_Contact_Letterhead" Header="Briefkopf drucken" IsEnabled="{Binding IsMemberSelected}"
Click="Menu_Contact_Letterhead_Click"/> Click="Menu_Contact_Letterhead_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE749;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="Stammdatenblatt"> <MenuItem Header="Stammdatenblatt">
<MenuItem x:Name="Menu_MemberDataSheet_Show" Header="...anzeigen (PDF)" IsEnabled="{Binding IsMemberSelected}" <MenuItem x:Name="Menu_MemberDataSheet_Show" Header="...anzeigen (PDF)" IsEnabled="{Binding IsMemberSelected}"
Click="Menu_MemberDataSheet_Show_Click" InputGestureText="Strg+P"/> Click="Menu_MemberDataSheet_Show_Click" InputGestureText="Strg+P">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE8FF;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_MemberDataSheet_SavePdf" Header="...speichern... (PDF)" IsEnabled="{Binding IsMemberSelected}" <MenuItem x:Name="Menu_MemberDataSheet_SavePdf" Header="...speichern... (PDF)" IsEnabled="{Binding IsMemberSelected}"
Click="Menu_MemberDataSheet_SavePdf_Click"/> Click="Menu_MemberDataSheet_SavePdf_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xEA90;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_MemberDataSheet_Print" Header="...drucken" IsEnabled="{Binding IsMemberSelected}" <MenuItem x:Name="Menu_MemberDataSheet_Print" Header="...drucken" IsEnabled="{Binding IsMemberSelected}"
Click="Menu_MemberDataSheet_Print_Click" InputGestureText="Strg+Shift+P"/> Click="Menu_MemberDataSheet_Print_Click" InputGestureText="Strg+Shift+P">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE749;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_MemberDataSheet_Email" Header="...per E-Mail schicken" IsEnabled="{Binding MemberCanSendEmail}" <MenuItem x:Name="Menu_MemberDataSheet_Email" Header="...per E-Mail schicken" IsEnabled="{Binding MemberCanSendEmail}"
Click="Menu_MemberDataSheet_Email_Click"/> Click="Menu_MemberDataSheet_Email_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE89C;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="Anlieferungsbestätigung" x:Name="Menu_DeliveryConfirmation"> <MenuItem Header="Anlieferungsbestätigung" x:Name="Menu_DeliveryConfirmation">
<MenuItem x:Name="Menu_DeliveryConfirmation_Show" Header="...anzeigen (PDF)" IsEnabled="False" <MenuItem x:Name="Menu_DeliveryConfirmation_Show" Header="...anzeigen (PDF)" IsEnabled="False"
@ -170,6 +194,7 @@
<Bold>Stammgemeinde</Bold>: z.B. matzen, Wolkersdorf, ...<LineBreak/> <Bold>Stammgemeinde</Bold>: z.B. matzen, Wolkersdorf, ...<LineBreak/>
<Bold>UID</Bold>: z.B. ATU12345678, ...<LineBreak/> <Bold>UID</Bold>: z.B. ATU12345678, ...<LineBreak/>
<Bold>Betriebs-Nr.</Bold>: z.B. 0123456, ...<LineBreak/> <Bold>Betriebs-Nr.</Bold>: z.B. 0123456, ...<LineBreak/>
<Bold>Aktiv</Bold>: aktiv, !Aktiv (nicht-aktiv)<LineBreak/>
<Bold>Bio-Betrieb</Bold>: BIO, !bio (ausgenommen Bio)<LineBreak/> <Bold>Bio-Betrieb</Bold>: BIO, !bio (ausgenommen Bio)<LineBreak/>
<Bold>Buchführend</Bold>: buchf[ührend], Pauschal[iert], !buchf[ührend]<LineBreak/> <Bold>Buchführend</Bold>: buchf[ührend], Pauschal[iert], !buchf[ührend]<LineBreak/>
<Bold>Volllieferant</Bold>: voll[lieferant], !Voll[lieferant] (nicht-Volllieferant)<LineBreak/> <Bold>Volllieferant</Bold>: voll[lieferant], !Voll[lieferant] (nicht-Volllieferant)<LineBreak/>
@ -177,7 +202,7 @@
<Bold>Telefon-Nr.</Bold>: z.B. +436641234, ....<LineBreak/> <Bold>Telefon-Nr.</Bold>: z.B. +436641234, ....<LineBreak/>
<Bold>Kontaktdaten</Bold>: email (mind. 1 E-Mail-Adr.), telnr (mind. 1 Tel.-Nr.), !email (keine E-Mail-Adr.), !telnr (keine Tel.-Nr.)<LineBreak/> <Bold>Kontaktdaten</Bold>: email (mind. 1 E-Mail-Adr.), telnr (mind. 1 Tel.-Nr.), !email (keine E-Mail-Adr.), !telnr (keine Tel.-Nr.)<LineBreak/>
<Bold>Kontaktart</Bold>: kontakt:email, kontakt:post, !kontakt:email, !kontakt:post<LineBreak/> <Bold>Kontaktart</Bold>: kontakt:email, kontakt:post, !kontakt:email, !kontakt:post<LineBreak/>
<Bold>Flächenbindungen</Bold>: z.B. zw, GVK, WRB, ... (Mitglieder mit aktiven Flächenbindungen)<LineBreak/> <Bold>Flächenbindungen</Bold>: z.B. zw, GVK, WRB, Fläch[enbindung], !GVK, ... (Mitglieder mit aktiven Flächenbindungen)<LineBreak/>
<Bold>Freitext</Bold>: z.B. Rechnungsaddresse, Anmerkung, "matzen" (sucht nach dem Text "matzen") <Bold>Freitext</Bold>: z.B. Rechnungsaddresse, Anmerkung, "matzen" (sucht nach dem Text "matzen")
</TextBlock> </TextBlock>
</TextBox.ToolTip> </TextBox.ToolTip>

@ -68,7 +68,7 @@ namespace Elwig.Windows {
(PhoneNr9TypeInput, PhoneNr9Input, PhoneNr9CommentInput), (PhoneNr9TypeInput, PhoneNr9Input, PhoneNr9CommentInput),
]; ];
InitializeDelayTimer(SearchInput, SearchInput_TextChanged); ControlUtils.InitializeDelayTimer(SearchInput, SearchInput_TextChanged);
SearchInput.TextChanged -= SearchInput_TextChanged; SearchInput.TextChanged -= SearchInput_TextChanged;
ViewModel.MemberListOrderByMgNr = false; ViewModel.MemberListOrderByMgNr = false;
@ -187,49 +187,80 @@ namespace Elwig.Windows {
ControlUtils.RenewItemsSource(BranchInput, await ctx.Branches.OrderBy(b => b.Name).ToListAsync()); ControlUtils.RenewItemsSource(BranchInput, await ctx.Branches.OrderBy(b => b.Name).ToListAsync());
ControlUtils.RenewItemsSource(DefaultKgInput, await ctx.WbKgs.Select(k => k.AtKg).OrderBy(k => k.Name).ToListAsync()); ControlUtils.RenewItemsSource(DefaultKgInput, await ctx.WbKgs.Select(k => k.AtKg).OrderBy(k => k.Name).ToListAsync());
var font = new System.Windows.Media.FontFamily("Segoe MDL2 Assets");
MenuItem? temp = null;
var seasons = await ctx.Seasons.OrderByDescending(s => s.Year).ToListAsync(); var seasons = await ctx.Seasons.OrderByDescending(s => s.Year).ToListAsync();
Menu_DeliveryConfirmation.Items.Clear(); Menu_DeliveryConfirmation.Items.Clear();
foreach (var s in seasons) { foreach (var s in seasons) {
var i = new MenuItem { Header = $"Saison {s.Year}...", Tag = s.Year }; var i = new MenuItem {
Header = $"Saison {s.Year}...",
Tag = s.Year,
Icon = s.Year == seasons[0].Year ? new TextBlock { FontSize = 16, Text = "\uE734", FontFamily = font } : null,
};
i.SetBinding(IsEnabledProperty, new Binding() { Path = new("IsMemberSelected") }); i.SetBinding(IsEnabledProperty, new Binding() { Path = new("IsMemberSelected") });
var show = new MenuItem { Header = "...anzeigen (PDF)" }; var show = new MenuItem { Header = "...anzeigen (PDF)", Icon = new TextBlock { FontSize = 16, Text = "\uE8FF", FontFamily = font } };
show.Click += Menu_DeliveryConfirmation_Show_Click; show.Click += Menu_DeliveryConfirmation_Show_Click;
i.Items.Add(show); i.Items.Add(show);
var pdf = new MenuItem { Header = "...speichern... (PDF)" }; var pdf = new MenuItem { Header = "...speichern... (PDF)", Icon = new TextBlock { FontSize = 16, Text = "\uEA90", FontFamily = font } };
pdf.Click += Menu_DeliveryConfirmation_SavePdf_Click; pdf.Click += Menu_DeliveryConfirmation_SavePdf_Click;
i.Items.Add(pdf); i.Items.Add(pdf);
var print = new MenuItem { Header = "...drucken" }; var print = new MenuItem { Header = "...drucken", Icon = new TextBlock { FontSize = 16, Text = "\uE749", FontFamily = font } };
print.Click += Menu_DeliveryConfirmation_Print_Click; print.Click += Menu_DeliveryConfirmation_Print_Click;
i.Items.Add(print); i.Items.Add(print);
var email = new MenuItem { Header = "...per E-Mail schicken" }; var email = new MenuItem { Header = "...per E-Mail schicken", Icon = new TextBlock { FontSize = 16, Text = "\uE89C", FontFamily = font } };
email.Click += Menu_DeliveryConfirmation_Email_Click; email.Click += Menu_DeliveryConfirmation_Email_Click;
email.SetBinding(IsEnabledProperty, new Binding() { Path = new("MemberCanSendEmail") }); email.SetBinding(IsEnabledProperty, new Binding() { Path = new("MemberCanSendEmail") });
i.Items.Add(email); i.Items.Add(email);
Menu_DeliveryConfirmation.Items.Add(i); var decade = s.Year / 10;
if (seasons[0].Year / 10 != decade) {
if (temp == null || !temp.Header.ToString()!.Contains($"{decade}0er")) {
temp = new MenuItem { Header = $"Saisons {decade}0er..." };
Menu_DeliveryConfirmation.Items.Add(temp);
}
temp?.Items.Add(i);
} else {
Menu_DeliveryConfirmation.Items.Add(i);
}
} }
temp = null;
Menu_CreditNote.Items.Clear(); Menu_CreditNote.Items.Clear();
foreach (var s in seasons) { foreach (var s in seasons) {
var i1 = new MenuItem { Header = $"Saison {s.Year}...", Tag = s.Year, IsEnabled = MemberList.SelectedItem != null }; var i1 = new MenuItem {
Header = $"Saison {s.Year}...",
Tag = s.Year,
IsEnabled = MemberList.SelectedItem != null,
Icon = s.Year == seasons[0].Year ? new TextBlock { FontSize = 16, Text = "\uE734", FontFamily = font } : null,
};
i1.SetBinding(IsEnabledProperty, new Binding() { Path = new($"MemberHasDeliveries[{s.Year}]") }); i1.SetBinding(IsEnabledProperty, new Binding() { Path = new($"MemberHasDeliveries[{s.Year}]") });
foreach (var v in s.PaymentVariants.OrderByDescending(v => v.AvNr)) { foreach (var v in s.PaymentVariants.OrderByDescending(v => v.AvNr)) {
var i2 = new MenuItem { Header = $"...{v.Name}...", Tag = v.AvNr }; var i2 = new MenuItem { Header = $"...{v.Name}...", Tag = v.AvNr };
var show = new MenuItem { Header = "...anzeigen (PDF)" }; var show = new MenuItem { Header = "...anzeigen (PDF)", Icon = new TextBlock { FontSize = 16, Text = "\uE8FF", FontFamily = font } };
show.Click += Menu_CreditNote_Show_Click; show.Click += Menu_CreditNote_Show_Click;
i2.Items.Add(show); i2.Items.Add(show);
var pdf = new MenuItem { Header = "...speichern... (PDF)" }; var pdf = new MenuItem { Header = "...speichern... (PDF)", Icon = new TextBlock { FontSize = 16, Text = "\uEA90", FontFamily = font } };
pdf.Click += Menu_CreditNote_SavePdf_Click; pdf.Click += Menu_CreditNote_SavePdf_Click;
i2.Items.Add(pdf); i2.Items.Add(pdf);
var print = new MenuItem { Header = "...drucken" }; var print = new MenuItem { Header = "...drucken", Icon = new TextBlock { FontSize = 16, Text = "\uE749", FontFamily = font } };
print.Click += Menu_CreditNote_Print_Click; print.Click += Menu_CreditNote_Print_Click;
i2.Items.Add(print); i2.Items.Add(print);
var email = new MenuItem { Header = "...per E-Mail schicken" }; var email = new MenuItem { Header = "...per E-Mail schicken", Icon = new TextBlock { FontSize = 16, Text = "\uE89C", FontFamily = font } };
email.SetBinding(IsEnabledProperty, new Binding { Path = new("MemberCanSendEmail") }); email.SetBinding(IsEnabledProperty, new Binding { Path = new("MemberCanSendEmail") });
email.Click += Menu_CreditNote_Email_Click; email.Click += Menu_CreditNote_Email_Click;
i2.Items.Add(email); i2.Items.Add(email);
i1.Items.Add(i2); i1.Items.Add(i2);
} }
Menu_CreditNote.Items.Add(i1); var decade = s.Year / 10;
if (seasons[0].Year / 10 != decade) {
if (temp == null || !temp.Header.ToString()!.Contains($"{decade}0er")) {
temp = new MenuItem { Header = $"Saisons {decade}0er..." };
Menu_CreditNote.Items.Add(temp);
}
temp?.Items.Add(i1);
} else {
Menu_CreditNote.Items.Add(i1);
}
} }
temp = null;
await RefreshList(); await RefreshList();

@ -6,7 +6,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows" xmlns:local="clr-namespace:Elwig.Windows"
xmlns:ctrl="clr-namespace:Elwig.Controls" xmlns:ctrl="clr-namespace:Elwig.Controls"
Title="Auszahlungsvarianten - Elwig" Height="480" Width="850" MinHeight="400" MinWidth="830"> Title="Auszahlungsvarianten - Elwig" Height="480" Width="850" MinHeight="400" MinWidth="850">
<Window.Resources> <Window.Resources>
<Style TargetType="Label"> <Style TargetType="Label">
<Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="HorizontalAlignment" Value="Left"/>
@ -56,21 +56,45 @@
<Menu Grid.ColumnSpan="2" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White"> <Menu Grid.ColumnSpan="2" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
<MenuItem Header="Variantendaten"> <MenuItem Header="Variantendaten">
<MenuItem x:Name="Menu_SummaryShow" Header="...anzeigen (PDF)" IsEnabled="False" <MenuItem x:Name="Menu_SummaryShow" Header="...anzeigen (PDF)" IsEnabled="False"
Click="Menu_SummaryShow_Click" InputGestureText="Strg+P"/> Click="Menu_SummaryShow_Click" InputGestureText="Strg+P">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE8FF;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_SummarySave" Header="...speichern... (PDF)" IsEnabled="False" <MenuItem x:Name="Menu_SummarySave" Header="...speichern... (PDF)" IsEnabled="False"
Click="Menu_SummarySave_Click"/> Click="Menu_SummarySave_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xEA90;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_SummaryExport" Header="...speichern... (Excel)" IsEnabled="False" <MenuItem x:Name="Menu_SummaryExport" Header="...speichern... (Excel)" IsEnabled="False"
Click="Menu_SummaryExport_Click"/> Click="Menu_SummaryExport_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE9F9;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_SummaryPrint" Header="...drucken" IsEnabled="False" <MenuItem x:Name="Menu_SummaryPrint" Header="...drucken" IsEnabled="False"
Click="Menu_SummaryPrint_Click" InputGestureText="Strg+Shift+P"/> Click="Menu_SummaryPrint_Click" InputGestureText="Strg+Shift+P">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE749;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="Buchungsliste"> <MenuItem Header="Buchungsliste">
<MenuItem x:Name="Menu_ExportSave" Header="...speichern... (Excel)" IsEnabled="False" <MenuItem x:Name="Menu_ExportSave" Header="...speichern... (Excel)" IsEnabled="False"
Click="Menu_ExportSave_Click" InputGestureText="Strg+L"/> Click="Menu_ExportSave_Click" InputGestureText="Strg+L">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE9F9;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="Überweisungsdaten"> <MenuItem Header="Überweisungsdaten">
<MenuItem x:Name="Menu_EbicsSave" Header="...exportieren... (EBICS)" IsEnabled="False" <MenuItem x:Name="Menu_EbicsSave" Header="...exportieren... (EBICS)" IsEnabled="False"
Click="Menu_EbicsSave_Click" InputGestureText="Strg+Ü"/> Click="Menu_EbicsSave_Click" InputGestureText="Strg+Ü">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE792;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
</Menu> </Menu>
@ -116,8 +140,9 @@
<TextBox x:Name="CommentInput" Grid.Column="1" HorizontalAlignment="Stretch" Margin="0,40,10,0" <TextBox x:Name="CommentInput" Grid.Column="1" HorizontalAlignment="Stretch" Margin="0,40,10,0"
TextChanged="CommentInput_TextChanged"/> TextChanged="CommentInput_TextChanged"/>
<Label Content="Erstellt am:" Margin="10,70,0,0" Grid.Column="0"/> <Label Content="Datum:" Margin="10,70,0,0" Grid.Column="0"/>
<TextBox x:Name="DateInput" Grid.Column="1" Width="77" HorizontalAlignment="Left" Margin="0,70,10,0" IsReadOnly="True"/> <TextBox x:Name="DateInput" Grid.Column="1" Width="77" HorizontalAlignment="Left" Margin="0,70,10,0"
TextChanged="DateInput_TextChanged"/>
<Label Content="Überwiesen am:" Margin="10,100,0,0" Grid.Column="0"/> <Label Content="Überwiesen am:" Margin="10,100,0,0" Grid.Column="0"/>
<TextBox x:Name="TransferDateInput" Grid.Column="1" Width="77" HorizontalAlignment="Left" Margin="0,100,10,0" <TextBox x:Name="TransferDateInput" Grid.Column="1" Width="77" HorizontalAlignment="Left" Margin="0,100,10,0"
@ -135,7 +160,7 @@
</TextBlock> </TextBlock>
<Label Content="Berücksichtigen:" Margin="90,70,10,10" Grid.Column="1"/> <Label Content="Berücksichtigen:" Margin="90,70,10,10" Grid.Column="1"/>
<CheckBox x:Name="ConsiderModifiersInput" Content="Zu-/Abschläge bei Lieferungen" <CheckBox x:Name="ConsiderModifiersInput" Content="Zu-/Abschläge bei Lieferungen (inkl. Rebelzuschl.)"
Margin="110,95,10,10" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="110,95,10,10" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top"
Checked="ConsiderModifiersInput_Changed" Unchecked="ConsiderModifiersInput_Changed"/> Checked="ConsiderModifiersInput_Changed" Unchecked="ConsiderModifiersInput_Changed"/>
<CheckBox x:Name="ConsiderPenaltiesInput" Content="Pönalen bei Unterlieferungen (FB)" <CheckBox x:Name="ConsiderPenaltiesInput" Content="Pönalen bei Unterlieferungen (FB)"
@ -215,10 +240,16 @@
Click="SaveButton_Click"/> Click="SaveButton_Click"/>
</Grid> </Grid>
<Button x:Name="MailButton" Content="Traubengutschriften" <Button x:Name="MailButton"
FontSize="14" Width="160" Margin="10,10,10,10" Height="27" IsEnabled="False" FontSize="14" Width="180" Margin="10,10,10,10" Height="30" IsEnabled="False"
Click="MailButton_Click" Click="MailButton_Click"
VerticalAlignment="Bottom" HorizontalAlignment="Right" Grid.Column="1"/> VerticalAlignment="Bottom" HorizontalAlignment="Right" HorizontalContentAlignment="Stretch" Grid.Column="1">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE715;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="0,1.5,0,0" Margin="0,0,10,0"/>
<TextBlock Margin="18,0,0,0" TextAlignment="Center">Traubengutschriften</TextBlock>
</Grid>
</Button>
</Grid> </Grid>
<StatusBar Grid.Row="2" Grid.ColumnSpan="2" BorderThickness="0,1,0,0" BorderBrush="Gray"> <StatusBar Grid.Row="2" Grid.ColumnSpan="2" BorderThickness="0,1,0,0" BorderBrush="Gray">

@ -19,7 +19,7 @@ namespace Elwig.Windows {
public readonly int Year; public readonly int Year;
public readonly bool SeasonLocked; public readonly bool SeasonLocked;
private bool DataValid, DataChanged, NameChanged, CommentChanged, TransferDateValid, TransferDateChanged; private bool DataValid, DataChanged, NameChanged, CommentChanged, DateValid, DateChanged, TransferDateValid, TransferDateChanged;
private BillingData? BillingData; private BillingData? BillingData;
private bool WeightModifierChanged = false; private bool WeightModifierChanged = false;
@ -175,6 +175,7 @@ namespace Elwig.Windows {
private void UpdateSaveButton() { private void UpdateSaveButton() {
SaveButton.IsEnabled = PaymentVariantList.SelectedItem != null && SaveButton.IsEnabled = PaymentVariantList.SelectedItem != null &&
((DataChanged && DataValid) || NameChanged || CommentChanged || ((DataChanged && DataValid) || NameChanged || CommentChanged ||
(DateChanged && DateValid) ||
(TransferDateChanged && TransferDateValid) || (TransferDateChanged && TransferDateValid) ||
(ConsiderModifiersInput.IsChecked != BillingData?.ConsiderDelieryModifiers) || (ConsiderModifiersInput.IsChecked != BillingData?.ConsiderDelieryModifiers) ||
(ConsiderPenaltiesInput.IsChecked != BillingData?.ConsiderContractPenalties) || (ConsiderPenaltiesInput.IsChecked != BillingData?.ConsiderContractPenalties) ||
@ -433,10 +434,16 @@ namespace Elwig.Windows {
var withoutIban = v.Credits.Count(c => c.Member.Iban == null); var withoutIban = v.Credits.Count(c => c.Member.Iban == null);
if (withoutIban > 0) { if (withoutIban > 0) {
var r = MessageBox.Show($"Achtung: Für {withoutIban} Mitglieder ist kein IBAN hinterlegt.\nDiese werden NICHT exportiert.", var r = MessageBox.Show($"Achtung: Für {withoutIban:N0} Mitglieder ist kein IBAN hinterlegt.\n\nDiese werden NICHT exportiert.",
"Mitglieder ohne IBAN", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel); "Mitglieder ohne IBAN", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (r != MessageBoxResult.OK) return; if (r != MessageBoxResult.OK) return;
} }
var withNegAmount = v.Credits.Count(c => c.Amount <= 0);
if (withNegAmount > 0) {
var r = MessageBox.Show($"Achtung: Es gibt {withNegAmount:N0} Traubengutschriften mit negativem Betrag.\n\nDiese werden NICHT exportiert.",
"Traubengutschriften mit negativem Betrag", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.OK);
if (r != MessageBoxResult.OK) return;
}
var d = new SaveFileDialog() { var d = new SaveFileDialog() {
FileName = $"{App.Client.NameToken}-Überweisungsdaten-{v.Year}-{v.Name.Trim().Replace(' ', '-')}.{Ebics.FileExtension}", FileName = $"{App.Client.NameToken}-Überweisungsdaten-{v.Year}-{v.Name.Trim().Replace(' ', '-')}.{Ebics.FileExtension}",
@ -489,6 +496,7 @@ namespace Elwig.Windows {
try { try {
v.Name = NameInput.Text; v.Name = NameInput.Text;
v.Comment = (CommentInput.Text != "") ? CommentInput.Text : null; v.Comment = (CommentInput.Text != "") ? CommentInput.Text : null;
v.DateString = string.Join("-", DateInput.Text.Split(".").Reverse());
v.TransferDateString = (TransferDateInput.Text != "") ? string.Join("-", TransferDateInput.Text.Split(".").Reverse()) : null; v.TransferDateString = (TransferDateInput.Text != "") ? string.Join("-", TransferDateInput.Text.Split(".").Reverse()) : null;
var d = App.Config.Debug ? BillingData.FromJson(DataInput.Text) : BillingData; var d = App.Config.Debug ? BillingData.FromJson(DataInput.Text) : BillingData;
d.ConsiderDelieryModifiers = ConsiderModifiersInput.IsChecked ?? false; d.ConsiderDelieryModifiers = ConsiderModifiersInput.IsChecked ?? false;
@ -557,6 +565,27 @@ namespace Elwig.Windows {
UpdateSaveButton(); UpdateSaveButton();
} }
private void DateInput_TextChanged(object sender, TextChangedEventArgs evt) {
if (PaymentVariantList.SelectedItem is not PaymentVar v) {
ControlUtils.ClearInputState(DateInput);
return;
}
var res = Validator.CheckDate(DateInput, true);
if (!res.IsValid) {
ControlUtils.SetInputInvalid(DateInput);
DateValid = false;
} else if (DateInput.Text != $"{v.Date:dd.MM.yyyy}") {
ControlUtils.SetInputChanged(DateInput);
DateValid = true;
DateChanged = true;
} else {
ControlUtils.ClearInputState(DateInput);
DateValid = true;
DateChanged = false;
}
UpdateSaveButton();
}
private void TransferDateInput_TextChanged(object sender, TextChangedEventArgs evt) { private void TransferDateInput_TextChanged(object sender, TextChangedEventArgs evt) {
if (PaymentVariantList.SelectedItem is not PaymentVar v) { if (PaymentVariantList.SelectedItem is not PaymentVar v) {
ControlUtils.ClearInputState(TransferDateInput); ControlUtils.ClearInputState(TransferDateInput);

@ -1,5 +0,0 @@
::mkdir "C:\Program Files\Elwig"
mkdir "C:\ProgramData\Elwig\resources"
copy /b /y Documents\*.css "C:\ProgramData\Elwig\resources"
copy /b /y Documents\*.cshtml "C:\ProgramData\Elwig\resources"
::copy /b /y ..\Installer\Files\*.exe "C:\Program Files\Elwig\"

@ -1,23 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Fragment> <Fragment>
<!-- C:\Program Files (x86)\Elwig oder C:\Program Files\Elwig --> <!-- C:\Program Files (x86)\Elwig oder C:\Program Files\Elwig -->
<StandardDirectory Id="ProgramFiles64Folder"> <StandardDirectory Id="ProgramFiles64Folder">
<Directory Id="InstallFolder" Name="!(bind.Property.ProductName)" /> <Directory Id="InstallFolder" Name="!(bind.Property.ProductName)">
<Directory Id="InstallFolderResources" Name="resources">
<Directory Id="InstallFolderDocuments" Name="Documents" />
</Directory>
</Directory>
</StandardDirectory> </StandardDirectory>
<!-- C:\ProgramData\Elwig --> <!-- C:\ProgramData\Elwig -->
<StandardDirectory Id="CommonAppDataFolder"> <StandardDirectory Id="CommonAppDataFolder">
<Directory Id="ConfigFolder" Name="!(bind.Property.ProductName)"> <Directory Id="ConfigFolder" Name="!(bind.Property.ProductName)" />
<Directory Id="ConfigFolderResources" Name="resources" /> </StandardDirectory>
</Directory>
</StandardDirectory> <!-- C:\ProgramData\Microsoft\Windows\Start Menu\Programs -->
<StandardDirectory Id="ProgramMenuFolder">
<!-- C:\ProgramData\Microsoft\Windows\Start Menu\Programs --> <Directory Id="StartMenuProgramsFolder" Name="!(bind.Property.ProductName)" />
<StandardDirectory Id="ProgramMenuFolder"> </StandardDirectory>
<Directory Id="StartMenuProgramsFolder" Name="!(bind.Property.ProductName)" />
</StandardDirectory> <!-- C:\Users\{USERNAME}\Desktop -->
<StandardDirectory Id="DesktopFolder" />
<!-- C:\Users{USERNAME}\Desktop -->
<StandardDirectory Id="DesktopFolder" />
</Fragment> </Fragment>
</Wix> </Wix>

@ -1,14 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Fragment> <Fragment>
<ComponentGroup Id="HeatComponents"> <ComponentGroup Id="HeatComponents">
<Files Directory="InstallFolder" Include="..\Elwig\bin\Publish\**"> <Files Directory="InstallFolder" Include="..\Elwig\bin\Publish\**">
<Exclude Files="..\Elwig\bin\Publish\**.exe" /> <Exclude Files="..\Elwig\bin\Publish\**.exe" />
<Exclude Files="..\Elwig\bin\Publish\**.pdb" /> <Exclude Files="..\Elwig\bin\Publish\**.pdb" />
</Files> </Files>
<Files Directory="ConfigFolderResources" Include="..\Elwig\Documents\**"> <Files Directory="InstallFolderDocuments" Include="..\Elwig\Documents\**">
<Exclude Files="..\Elwig\Documents\**.cs" /> <Exclude Files="..\Elwig\Documents\**.cs" />
</Files> </Files>
</ComponentGroup> </ComponentGroup>
</Fragment> </Fragment>
</Wix> </Wix>

@ -1,4 +1,4 @@
<Project Sdk="WixToolset.Sdk/5.0.0"> <Project Sdk="WixToolset.Sdk/6">
<PropertyGroup> <PropertyGroup>
<HarvestFileSuppressUniqueIds>false</HarvestFileSuppressUniqueIds> <HarvestFileSuppressUniqueIds>false</HarvestFileSuppressUniqueIds>
<HarvestFileGenerateGuidsNow>true</HarvestFileGenerateGuidsNow> <HarvestFileGenerateGuidsNow>true</HarvestFileGenerateGuidsNow>
@ -37,4 +37,4 @@
<None Include="Files\config.ini" /> <None Include="Files\config.ini" />
<None Include="Files\WinziPrint.exe" /> <None Include="Files\WinziPrint.exe" />
</ItemGroup> </ItemGroup>
</Project> </Project>

@ -2,3 +2,6 @@
Elwig Elwig
===== =====
**El**ektronische **Wi**nzer**g**enossenschaftsverwaltung (Electronic Management for Vintners' Cooperatives).
Further information may be found on [the website](https://elwig.at).

@ -1,4 +1,6 @@
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util"> <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal"
xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
<Bundle Name="Elwig" Manufacturer="Elwig" Version="!(bind.packageVersion.ElwigMsi)" UpgradeCode="f3c8fcab-c37c-43aa-9ab8-e42f4bb518b7" <Bundle Name="Elwig" Manufacturer="Elwig" Version="!(bind.packageVersion.ElwigMsi)" UpgradeCode="f3c8fcab-c37c-43aa-9ab8-e42f4bb518b7"
IconSourceFile="$(var.ElwigProjectDir)\Resources\Images\Elwig.ico"> IconSourceFile="$(var.ElwigProjectDir)\Resources\Images\Elwig.ico">
<BootstrapperApplication> <BootstrapperApplication>

@ -1,4 +1,4 @@
<Project Sdk="WixToolset.Sdk/5.0.0"> <Project Sdk="WixToolset.Sdk/6">
<PropertyGroup> <PropertyGroup>
<OutputType>Bundle</OutputType> <OutputType>Bundle</OutputType>
<OutputName>Elwig</OutputName> <OutputName>Elwig</OutputName>
@ -13,7 +13,7 @@
</Target> </Target>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Installer\Installer.wixproj" /> <ProjectReference Include="..\Installer\Installer.wixproj" />
<PackageReference Include="WixToolset.Bal.wixext" Version="5.0.1" /> <PackageReference Include="WixToolset.Bal.wixext" Version="6.0.0" />
<PackageReference Include="WixToolset.Util.wixext" Version="5.0.1" /> <PackageReference Include="WixToolset.Util.wixext" Version="6.0.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

@ -12,7 +12,7 @@ namespace Tests.DocumentTests {
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
var m = await ctx.Members.FindAsync(101); var m = await ctx.Members.FindAsync(101);
var p = await ctx.MemberPayments.Where(p => p.Year == 2020 && p.AvNr == 1).SingleAsync(); var p = await ctx.MemberPayments.Where(p => p.Year == 2020 && p.AvNr == 1).SingleAsync();
var data = await CreditNoteDeliveryData.ForPaymentVariant(ctx.CreditNoteDeliveryRows, ctx.Seasons, 2020, 1); var data = await CreditNoteDeliveryData.ForPaymentVariant(ctx.CreditNoteDeliveryRows, ctx.PaymentVariants, 2020, 1);
using var doc = new CreditNote(ctx, p, data[m!.MgNr], false, false, false, false, using var doc = new CreditNote(ctx, p, data[m!.MgNr], false, false, false, false,
ctx.GetMemberUnderDelivery(2020, m!.MgNr).GetAwaiter().GetResult()); ctx.GetMemberUnderDelivery(2020, m!.MgNr).GetAwaiter().GetResult());
var text = await Utils.GeneratePdfText(doc); var text = await Utils.GeneratePdfText(doc);

@ -12,16 +12,17 @@ namespace Tests.DocumentTests {
var v = (await ctx.PaymentVariants.FindAsync(2020, 1))!; var v = (await ctx.PaymentVariants.FindAsync(2020, 1))!;
var data = await PaymentVariantSummaryData.ForPaymentVariant(v, ctx.PaymentVariantSummaryRows); var data = await PaymentVariantSummaryData.ForPaymentVariant(v, ctx.PaymentVariantSummaryRows);
using var doc = new PaymentVariantSummary(v, data); using var doc = new PaymentVariantSummary(v, data);
var text = await Utils.GeneratePdfText(doc); var text = await Utils.GeneratePdfText(doc, true);
var table = Utils.ExtractTable(text);
Assert.Multiple(() => { Assert.Multiple(() => {
Assert.That(text, Contains.Substring("Auszahlungsvariante")); Assert.That(text, Contains.Substring("Auszahlungsvariante"));
Assert.That(text, Contains.Substring(v.Name)); Assert.That(text, Contains.Substring(v.Name));
Assert.That(text, Contains.Substring(""" Assert.That(table.Skip(17).ToArray(), Is.EqualTo(new string[][] {
Gradation ungebunden gebunden Gesamt [ "Gradation", "ungebunden", "attributlos gebunden", "gebunden", "Gesamt" ],
[°Oe] [kg] [/kg] [kg] [/kg] [] [ "[°Oe]", "[kg]", "[/kg]", "[kg]", "[/kg]", "[kg]", "[/kg]", "[]" ],
Grüner Veltliner 3 219 0 1 609,50 ["Grüner Veltliner", "3 219", "0", "0", "1 609,50"],
Qualitätswein 73 3 219 0,5000 - - 1 609,50 ["Qualitätswein", "73", "3 219", "0,5000", "-", "-", "-", "-", "1 609,50"]
""")); }));
}); });
} }
} }

@ -1,6 +1,5 @@
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Helpers.Billing; using Elwig.Helpers.Billing;
using Elwig.Models.Entities;
using System.Text.Json; using System.Text.Json;
namespace Tests.HelperTests { namespace Tests.HelperTests {
@ -452,7 +451,7 @@ namespace Tests.HelperTests {
[73] = 0.5m, [73] = 0.5m,
[83] = 1.0m [83] = 1.0m
}, null), GetSelection(["GV/-"])) }, null), GetSelection(["GV/-"]))
]; ];
var data = BillingData.FromGraphEntries(entries); var data = BillingData.FromGraphEntries(entries);
Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo(""" Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo("""
{ {
@ -476,14 +475,14 @@ namespace Tests.HelperTests {
[Test] [Test]
public void TestWrite_04_Simple() { public void TestWrite_04_Simple() {
List<GraphEntry> entries = [ List<GraphEntry> entries = [
new GraphEntry(0, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() { new GraphEntry(0, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.5m, [73] = 0.5m,
[84] = 1.0m [84] = 1.0m
}, null), GetSelection(["GV/-", "ZW/-"])), }, null), GetSelection(["GV/-", "ZW/-"])),
new GraphEntry(10, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() { new GraphEntry(10, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.75m, [73] = 0.75m,
}, null), GetSelection(["WR/-"])) }, null), GetSelection(["WR/-"]))
]; ];
var data = BillingData.FromGraphEntries(entries); var data = BillingData.FromGraphEntries(entries);
Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo(""" Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo("""
{ {
@ -510,18 +509,18 @@ namespace Tests.HelperTests {
[Test] [Test]
public void TestWrite_05_Attribute() { public void TestWrite_05_Attribute() {
List<GraphEntry> entries = [ List<GraphEntry> entries = [
new GraphEntry(0, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() { new GraphEntry(0, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.5m, [73] = 0.5m,
[84] = 1.0m [84] = 1.0m
}, null), GetSelection(["GV/K-", "ZW/K-"])), }, null), GetSelection(["GV/K-", "ZW/K-"])),
new GraphEntry(2, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() { new GraphEntry(2, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.75m, [73] = 0.75m,
}, null), GetSelection(["WR/-", "BL/-", "RR/-", "FV/-"])), }, null), GetSelection(["WR/-", "BL/-", "RR/-", "FV/-", "GV/-"])),
new GraphEntry(4, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() { new GraphEntry(4, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.65m, [73] = 0.65m,
[84] = 1.2m [84] = 1.2m
}, null), GetSelection(["BP/-", "SA/-"])) }, null), GetSelection(["BP/-", "SA/-"]))
]; ];
var data = BillingData.FromGraphEntries(entries); var data = BillingData.FromGraphEntries(entries);
Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo(""" Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo("""
{ {
@ -564,12 +563,12 @@ namespace Tests.HelperTests {
}, null), GetSelection(["GV/-B", "ZW/-B"])), }, null), GetSelection(["GV/-B", "ZW/-B"])),
new GraphEntry(2, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() { new GraphEntry(2, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.75m, [73] = 0.75m,
}, null), GetSelection(["WR/-", "BL/-", "RR/-", "FV/-"])), }, null), GetSelection(["WR/-", "BL/-", "RR/-", "FV/-", "GV/-"])),
new GraphEntry(4, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() { new GraphEntry(4, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.65m, [73] = 0.65m,
[84] = 1.2m [84] = 1.2m
}, null), GetSelection(["BP/-", "SA/-"])) }, null), GetSelection(["BP/-", "SA/-"]))
]; ];
var data = BillingData.FromGraphEntries(entries); var data = BillingData.FromGraphEntries(entries);
Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo(""" Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo("""
{ {
@ -616,7 +615,7 @@ namespace Tests.HelperTests {
new GraphEntry(2, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() { new GraphEntry(2, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.8m, [73] = 0.8m,
}, null), GetSelection(["BP/-", "BP/-B"])), }, null), GetSelection(["BP/-", "BP/-B"])),
]; ];
var data = BillingData.FromGraphEntries(entries); var data = BillingData.FromGraphEntries(entries);
Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo(""" Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo("""
{ {
@ -653,8 +652,8 @@ namespace Tests.HelperTests {
}, null), GetSelection(["BP/-B", "ZW/-B", "FV/-B"])), }, null), GetSelection(["BP/-B", "ZW/-B", "FV/-B"])),
new GraphEntry(2, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() { new GraphEntry(2, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.8m, [73] = 0.8m,
}, null), GetSelection(["BP/-", "ZW/-", "FV/-", "WR/-", "BL/-"])), }, null), GetSelection(["BP/-", "ZW/-", "FV/-", "WR/-", "BL/-", "RR/-"])),
]; ];
var data = BillingData.FromGraphEntries(entries); var data = BillingData.FromGraphEntries(entries);
Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo(""" Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo("""
{ {
@ -711,5 +710,329 @@ namespace Tests.HelperTests {
} }
""")); """));
} }
[Test]
public void TestWrite_10_QualityLevel() {
List<GraphEntry> entries = [
new GraphEntry(0, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.1m,
}, null), GetSelection(["GV/-"])),
new GraphEntry(1, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.2m,
}, null), GetSelection(["ZW/-"]))
];
entries[0].Abgewertet = true;
var data = BillingData.FromGraphEntries(entries);
Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo("""
{
"mode": "elwig",
"version": 1,
"payment": 0.2,
"quality": {
"WEI": 0.1
},
"curves": []
}
"""));
}
[Test]
public void TestWrite_11_MixedCultivation_1() {
List<GraphEntry> entries = [
new GraphEntry(0, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.1m,
}, null), GetSelection(["GV/-", "WR/-"])),
new GraphEntry(1, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.2m,
}, null), GetSelection(["GV/-B"]))
];
var data = BillingData.FromGraphEntries(entries);
Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo("""
{
"mode": "elwig",
"version": 1,
"payment": {
"default": 0.1,
"GV-B": 0.2
},
"curves": []
}
"""));
}
[Test]
public void TestWrite_12_MixedCultivation_2() {
List<GraphEntry> entries = [
new GraphEntry(0, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.1m,
}, null), GetSelection(["GV/-", "ZW/-", "WR/-", "FV/-", "RR/-"])),
new GraphEntry(1, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.2m,
}, null), GetSelection(["GV/-B", "FV/-B"])),
new GraphEntry(2, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.3m,
}, null), GetSelection(["ZW/-B", "WR/-B"]))
];
var data = BillingData.FromGraphEntries(entries);
Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo("""
{
"mode": "elwig",
"version": 1,
"payment": {
"default": 0.1,
"GV-B": 0.2,
"FV-B": 0.2,
"ZW-B": 0.3,
"WR-B": 0.3
},
"curves": []
}
"""));
}
[Test]
public void TestWrite_13_DefaultCultivation_1() {
List<GraphEntry> entries = [
new GraphEntry(0, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.1m,
}, null), GetSelection(["GV/-", "GV/-B"])),
new GraphEntry(1, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.2m,
}, null), GetSelection(["WR/-B"]))
];
var data = BillingData.FromGraphEntries(entries);
Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo("""
{
"mode": "elwig",
"version": 1,
"payment": {
"default": 0.1,
"WR-B": 0.2
},
"curves": []
}
"""));
}
[Test]
public void TestWrite_14_DefaultCultivation_2() {
List<GraphEntry> entries = [
new GraphEntry(0, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.1m,
}, null), GetSelection(["GV/-", "GV/-B", "ZW/-"])),
new GraphEntry(1, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.2m,
}, null), GetSelection(["WR/-B", "ZW/-B"]))
];
var data = BillingData.FromGraphEntries(entries);
Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo("""
{
"mode": "elwig",
"version": 1,
"payment": {
"default": 0.1,
"-B": 0.2,
"GV-B": 0.1
},
"curves": []
}
"""));
}
[Test]
public void TestWrite_15_DefaultCultivation_3() {
List<GraphEntry> entries = [
new GraphEntry(0, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.1m,
}, null), GetSelection(["GV/-", "GV/-B", "ZW/-"])),
new GraphEntry(1, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.2m,
}, null), GetSelection(["GV/S-", "GV/S-B", "ZW/S-"])),
new GraphEntry(2, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.3m,
}, null), GetSelection(["WR/S-B", "ZW/S-B"]))
];
var data = BillingData.FromGraphEntries(entries);
Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo("""
{
"mode": "elwig",
"version": 1,
"payment": {
"GV/S-B": 0.2,
"/S": 0.2,
"/S-B": 0.3,
"GV": 0.1,
"ZW": 0.1
},
"curves": []
}
"""));
}
[Test]
public void TestWrite_16_DefaultCultivation_4() {
List<GraphEntry> entries = [
new GraphEntry(0, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.1m,
}, null), GetSelection(["GV/-", "RR/-B", "ZW/-"])),
new GraphEntry(1, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.2m,
}, null), GetSelection(["RR/-"]))
];
var data = BillingData.FromGraphEntries(entries);
Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo("""
{
"mode": "elwig",
"version": 1,
"payment": {
"default": 0.1,
"RR-B": 0.1,
"RR": 0.2
},
"curves": []
}
"""));
}
[Test]
public void TestWrite_17_DefaultCultivation_5() {
List<GraphEntry> entries = [
new GraphEntry(0, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.1m,
}, null), GetSelection(["GV/-", "RR/-B", "ZW/-", "SW/-", "SO/-"])),
new GraphEntry(1, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.2m,
}, null), GetSelection(["GV/K-", "RR/K-B", "ZW/K-"])),
new GraphEntry(2, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.3m,
}, null), GetSelection(["RR/K-"]))
];
var data = BillingData.FromGraphEntries(entries);
Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo("""
{
"mode": "elwig",
"version": 1,
"payment": {
"RR/K-B": 0.2,
"RR/K": 0.3,
"default": 0.1,
"/K": 0.2
},
"curves": []
}
"""));
}
[Test]
public void TestWrite_18_DefaultAttribute_1() {
List<GraphEntry> entries = [
new GraphEntry(0, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.1m,
}, null), GetSelection(["GV/-", "GV/S-"])),
new GraphEntry(1, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.2m,
}, null), GetSelection(["WR/S-"]))
];
var data = BillingData.FromGraphEntries(entries);
Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo("""
{
"mode": "elwig",
"version": 1,
"payment": {
"WR/S": 0.2,
"default": 0.1
},
"curves": []
}
"""));
}
[Test]
public void TestWrite_19_DefaultAttribute_2() {
List<GraphEntry> entries = [
new GraphEntry(0, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.1m,
}, null), GetSelection(["GV/-", "GV/S-", "ZW/-"])),
new GraphEntry(1, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.2m,
}, null), GetSelection(["WR/S-", "ZW/S-"]))
];
var data = BillingData.FromGraphEntries(entries);
Assert.That(data.ToJsonString(JsonOpts), Is.EqualTo("""
{
"mode": "elwig",
"version": 1,
"payment": {
"default": 0.1,
"/S": 0.2,
"GV/S": 0.1
},
"curves": []
}
"""));
}
private static IEnumerable<List<T>> GetPermutations<T>(T[] input) {
if (input.Length <= 1)
yield return [..input];
for (int i = 0; i < input.Length; i++) {
yield return [input[i]];
foreach (var p in GetPermutations(input.Where((_, j) => j != i).ToArray())) {
p.Insert(0, input[i]);
yield return p;
}
}
}
private static IEnumerable<List<HashSet<RawVaribute>>> GetCurves(RawVaribute[] vaributes, int minNumCurves = 1, int maxNumCurves = 10) {
foreach (var p in GetPermutations(vaributes)) {
for (int nCurves = minNumCurves; nCurves <= Math.Min(maxNumCurves, p.Count); nCurves++) {
yield return Enumerable.Range(0, nCurves)
.Select(n => p.Where((v, idx) => idx % nCurves == n).ToHashSet())
.ToList();
}
}
}
private readonly HashSet<string> TestedCurves = [];
private void TestCollapse(List<HashSet<RawVaribute>> curves) {
var str = string.Join("\n", curves.Select(c => string.Join(" ", c.Select(v => v.ToString().PadRight(6)).Order())).Order().Select((c, n) => $"{n + 1}: [ " + c + " ]"));
if (!TestedCurves.Add(str))
return;
var vaributes = curves.SelectMany(v => v).ToList();
List<GraphEntry> entries = curves
.Select((l, n) => new GraphEntry(n, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = n + 1,
}, null), GetSelection(l.Select(v => v.ToString()))))
.ToList();
var data = BillingData.FromGraphEntries(entries);
var test = PaymentBillingData.FromJson(data.ToJsonString(), vaributes);
for (int i = 0; i < curves.Count; i++) {
foreach (var v in curves[i]) {
var val = test.CalculatePrice(v.SortId!, v.AttrId, v.CultId, "QUW", false, 73.0, 15.0);
var actualCurve = (int)val;
Assert.That(actualCurve, Is.EqualTo(i + 1), $"Invalid: {v} (Curve {i + 1} -> {actualCurve})\n\n{str}\n\n{data.ToJsonString(JsonOpts)}\n");
}
}
}
[Test]
public void TestCollapse_01_Permutations() {
RawVaribute[][] configurations = [
[new("GV/-"), new("WR/-"), new("ZW/-"), new("GV/K-"), new("WR/K-"), new("ZW/K-")],
[new("GV/-"), new("WR/-"), new("GV/K-"), new("WR/K-"), new("GV/S-"), new("WR/S-")],
[new("GV/-"), new("WR/-"), new("ZW/-"), new("GV/-B"), new("WR/-B"), new("ZW/-B")],
[new("GV/-"), new("WR/-"), new("GV/-B"), new("WR/-B"), new("GV/-KIP"), new("WR/-KIP")],
[new("GV/-"), new("GV/K-"), new("ZW/-"), new("ZW/K-"), new("GV/-B"), new("GV/K-B"), new("ZW/-B"), new("ZW/K-B")],
];
Assert.Multiple(() => {
foreach (var config in configurations) {
foreach (var c in GetCurves(config))
TestCollapse(c);
}
});
}
} }
} }

@ -19,16 +19,16 @@
</Target> </Target>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="Appium.WebDriver" Version="4.4.5" /> <PackageReference Include="Appium.WebDriver" Version="4.4.5" />
<PackageReference Include="NReco.PdfRenderer" Version="1.5.4" /> <PackageReference Include="NReco.PdfRenderer" Version="1.6.0" />
<PackageReference Include="NUnit" Version="4.2.2" /> <PackageReference Include="NUnit" Version="4.3.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" /> <PackageReference Include="NUnit3TestAdapter" Version="5.0.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.3.0"> <PackageReference Include="NUnit.Analyzers" Version="4.7.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>
<PackageReference Include="coverlet.collector" Version="6.0.2"> <PackageReference Include="coverlet.collector" Version="6.0.4">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>