Compare commits

...

46 Commits

Author SHA1 Message Date
318fb5dc7b Bump version to 0.13.9
All checks were successful
Test / Run tests (push) Successful in 2m7s
Deploy / Build and Deploy (push) Successful in 1m55s
2025-05-05 10:41:18 +02:00
41c5288fc5 Elwig: Update dependencies
Some checks failed
Test / Run tests (push) Failing after 14s
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
All checks were successful
Test / Run tests (push) Successful in 2m43s
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
All checks were successful
Test / Run tests (push) Successful in 2m19s
Deploy / Build and Deploy (push) Successful in 2m1s
2025-02-21 12:05:34 +01:00
aa98909c0a DeliveryAdminWindow: Fix Handlese/Gerebelt Gewogen input behaviour
All checks were successful
Test / Run tests (push) Successful in 1m51s
2025-02-20 17:06:40 +01:00
775bb08e95 Elwig: Update dependencies
All checks were successful
Test / Run tests (push) Successful in 2m29s
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
All checks were successful
Test / Run tests (push) Successful in 2m15s
Deploy / Build and Deploy (push) Successful in 2m23s
2025-01-21 11:59:54 +01:00
c7a2f2241d Billing: Handle negative credit amount in following credits
Some checks failed
Test / Run tests (push) Has been cancelled
2025-01-21 11:58:08 +01:00
bd4ebb8c35 PaymentVariantsWindow: Allow user to change date
All checks were successful
Test / Run tests (push) Successful in 2m35s
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
All checks were successful
Test / Run tests (push) Successful in 2m52s
2025-01-19 17:00:31 +01:00
0b8a1b321f BillingData: Fix collapsing tested with permutations
All checks were successful
Test / Run tests (push) Successful in 3m7s
2025-01-18 12:19:33 +01:00
95927c3f1a ChartWindow: Remove old commented-out code
All checks were successful
Test / Run tests (push) Successful in 2m42s
2025-01-16 09:09:28 +01:00
f7297d313a Bump version to 0.13.6
All checks were successful
Deploy / Build and Deploy (push) Successful in 2m20s
2025-01-14 23:43:20 +01:00
80fec4473a Elwig: Update dependencies
All checks were successful
Test / Run tests (push) Successful in 2m3s
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
All checks were successful
Test / Run tests (push) Successful in 1m58s
2025-01-14 22:05:48 +01:00
b83df45e8f Bump version to 0.13.5
All checks were successful
Test / Run tests (push) Successful in 2m9s
Deploy / Build and Deploy (push) Successful in 2m22s
2025-01-02 17:41:51 +01:00
c24b1ca2b9 DeliveryAdminWindow: Add WineLocalityStatistics
All checks were successful
Test / Run tests (push) Successful in 2m10s
2025-01-02 17:25:46 +01:00
5e53d864b1 MemberAdminWindow: Add filters for active and non-active members
All checks were successful
Test / Run tests (push) Successful in 2m20s
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
All checks were successful
Test / Run tests (push) Successful in 1m54s
Deploy / Build and Deploy (push) Successful in 2m10s
2024-11-25 19:23:20 +01:00
6c7f10cb26 Tests: Update dependencies
All checks were successful
Test / Run tests (push) Successful in 1m55s
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
All checks were successful
Test / Run tests (push) Successful in 2m9s
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
All checks were successful
Test / Run tests (push) Successful in 2m52s
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
All checks were successful
Test / Run tests (push) Successful in 2m26s
Deploy / Build and Deploy (push) Successful in 2m21s
2024-11-13 23:35:29 +01:00
2ee0d56dcc Windows: Add MailLogWindow
All checks were successful
Test / Run tests (push) Successful in 1m54s
2024-11-13 18:37:50 +01:00
0a9731af09 MailWindow: Fix small bugs and persist all settings
All checks were successful
Test / Run tests (push) Successful in 1m54s
2024-11-13 18:07:03 +01:00
6718ad4c8d AreaComAdminWindow: Add filters for season
All checks were successful
Test / Run tests (push) Successful in 2m26s
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
All checks were successful
Test / Run tests (push) Successful in 2m15s
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
45 changed files with 2064 additions and 544 deletions

View File

@ -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}
------------------------------------------
### 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)
* 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)
### Sonstiges {#0.13.2-misc}
### Sonstiges {#v0.13.2-misc}
* Weitere automatisierte Tests hinzugefügt. ([#11][i11])
* 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}
------------------------------------------
### 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)
### 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)
### Sonstiges {#0.13.1-misc}
### Sonstiges {#v0.13.1-misc}
* Abhängigkeiten aktualisiert. (b6ae1f5675)
@ -55,7 +178,7 @@ Changelog
> [!NOTE]
> 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)
@ -132,7 +255,7 @@ Changelog
### Neue Funktionen {#v0.11.2-features}
* 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}
@ -239,7 +362,7 @@ Changelog
### 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])
* Versuch: Fehler bei automatischer Daten-Erneuerung bei längerer Benutzung. (8c8c0a8c2b)
@ -556,7 +679,7 @@ Changelog
### 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}

View File

@ -26,6 +26,7 @@ namespace Elwig {
private readonly DispatcherTimer _autoUpdateTimer = new() { Interval = TimeSpan.FromHours(1) };
public static readonly string DataPath = @"C:\ProgramData\Elwig\";
public static readonly string MailsPath = Path.Combine(DataPath, "mails");
public static readonly string ConfigPath = Path.Combine(DataPath, "config.ini");
public static readonly string ExePath = @"C:\Program Files\Elwig\";
public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig");
@ -56,6 +57,7 @@ namespace Elwig {
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Directory.CreateDirectory(TempPath);
Directory.CreateDirectory(DataPath);
Directory.CreateDirectory(MailsPath);
MainDispatcher = Dispatcher;
Scales = [];
CurrentApp = this;

View File

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

View File

@ -63,10 +63,12 @@ namespace Elwig.Helpers {
public DbSet<PaymentDeliveryPart> PaymentDeliveryParts { get; private set; }
public DbSet<PaymentCustom> CustomPayments { 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<AreaComUnderDeliveryRowSingle> AreaComUnderDeliveryRows { 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<CreditNoteRowSingle> CreditNoteRows { get; private set; }
public DbSet<WeightBreakdownRow> WeightBreakDownRows { get; private set; }

View File

@ -299,22 +299,30 @@ namespace Elwig.Helpers.Billing {
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);
if (!data.ContainsKey("default")) {
foreach (var (v, ks) in rev1) {
if ((ks.Count >= vaributes.Count() * 0.5 && useDefault) || ks.Count == vaributes.Count()) {
foreach (var k in ks) data.Remove(k);
if ((ks.Count > vaributes.Count() * 0.5 && useDefault) || ks.Count == vaributes.Count()) {
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;
CollapsePaymentData(data, vaributes, useDefault);
CollapsePaymentData(data, originalData, vaributes, useDefault);
return;
}
}
foreach (var (v, ks) in rev2) {
if ((ks.Count >= vaributes.Count() * 0.5 && useDefault) || ks.Count == vaributes.Count()) {
foreach (var k in ks) data.Remove(k);
if ((ks.Count > vaributes.Count() * 0.5 && useDefault) || ks.Count == vaributes.Count()) {
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;
CollapsePaymentData(data, vaributes, useDefault);
CollapsePaymentData(data, originalData, vaributes, useDefault);
return;
}
}
@ -330,16 +338,26 @@ namespace Elwig.Helpers.Billing {
var len = vaributes.Count(e => $"{e.AttrId}{(e.CultId != null && e.CultId != "" ? "-" : "")}{e.CultId}" == idx);
foreach (var (v, ks) in rev1) {
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);
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) {
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);
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("/-")) {
data.Remove(k, out var 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);
var keyVaributes = data
.Select(e => e.Key.Split('-')[0])
.Where(e => e.Length > 0 && e != "default")
.Select(e => e.Key)
.Where(e => e.Length > 0 && !e.Contains('-') && e != "default")
.Distinct()
.ToList();
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(qualityWei, vaributes ?? qualityWei.Select(e => new RawVaribute(e.Key)).ToList(), useDefaultQuality);
CollapsePaymentData(payment, payment.DeepClone().AsObject(), vaributes ?? payment.Select(e => new RawVaribute(e.Key)).ToList(), useDefaultPayment);
CollapsePaymentData(qualityWei, qualityWei.DeepClone().AsObject(), vaributes ?? qualityWei.Select(e => new RawVaribute(e.Key)).ToList(), useDefaultQuality);
var data = new JsonObject {
["mode"] = "elwig",

View File

@ -46,14 +46,14 @@ namespace Elwig.Helpers.Billing {
m.mgnr,
v.avnr,
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,
ROUND(IIF({Data.ConsiderTotalPenalty}, COALESCE(b.total_penalty, 0), 0) / POW(10, s.precision - 2)) +
ROUND(IIF({Data.ConsiderContractPenalties}, COALESCE(u.total_penalty, 0), 0) / POW(10, 4 - 2)) +
ROUND(IIF({Data.ConsiderAutoBusinessShares}, -COALESCE(a.total_amount, 0), 0) / POW(10, s.precision - 2)) +
IIF({Data.ConsiderCustomModifiers}, COALESCE(x.amount, 0), 0)
AS modifiers,
lc.modifiers AS prev_modifiers
IIF(lc.amount >= 0, lc.modifiers, 0) AS prev_modifiers
FROM season s
JOIN payment_variant v ON v.year = s.year
LEFT JOIN payment_variant l ON l.year = s.year

View File

@ -23,6 +23,10 @@ namespace Elwig.Helpers {
public bool HasNetWeighing(Branch? b) => HasNetWeighing(b?.ZwstId);
public bool HasNetWeighing() => HasNetWeighing(App.ZwstId);
public bool HasBoxWeighing(string? zwstId) => IsWinzerkeller && (zwstId != "W");
public bool HasBoxWeighing(Branch? b) => HasBoxWeighing(b?.ZwstId);
public bool HasBoxWeighing() => HasBoxWeighing(App.ZwstId);
public string NameToken;
public string NameShort;
public string Name;
@ -67,6 +71,12 @@ namespace Elwig.Helpers {
public string? TextEmailSubject;
public string? TextEmailBody;
public bool MailIncludeNonDeliverers;
public bool MailDoublePaged;
public int MailSendPostal;
public int MailSendEmail;
public int MailOrdering;
public int ExportEbicsVersion;
public int ExportEbicsAddress;
@ -117,7 +127,7 @@ namespace Elwig.Helpers {
case "KMW/5": ModeWineQualityStatistics = 3; 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 "NAME": OrderingMemberList = 1; break;
case "KG": OrderingMemberList = 2; break;
@ -135,6 +145,31 @@ namespace Elwig.Helpers {
TextEmailBody = parameters.GetValueOrDefault("TEXT_EMAIL_BODY");
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;
switch (parameters.GetValueOrDefault("EXPORT_EBICS_ADDRESS", "FULL")?.ToUpper()) {
case "OMIT": ExportEbicsAddress = 0; break;
@ -177,6 +212,25 @@ namespace Elwig.Helpers {
case 1: orderingMemberList = "NAME"; 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";
switch (ExportEbicsAddress) {
case 0: exportEbicsAddress = "OMIT"; break;
@ -212,6 +266,11 @@ namespace Elwig.Helpers {
("TEXT_CREDITNOTE", TextCreditNote),
("TEXT_EMAIL_SUBJECT", TextEmailSubject),
("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_ADDRESS", exportEbicsAddress),
("AUTOADJUST_BUSINESSSHARES", autoAdjust),

View File

@ -5,6 +5,7 @@ using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Threading;
using Brush = System.Windows.Media.Brush;
using Brushes = System.Windows.Media.Brushes;
@ -234,5 +235,21 @@ namespace Elwig.Helpers {
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();
};
}
}
}

View File

@ -301,6 +301,7 @@ namespace Elwig.Helpers.Export {
if (units != null && units.Length > 0) {
int n = -1;
switch (units[0]) {
case "#": n = 0; data = $"{v:N0}"; break;
case "%": n = 1; data = $"{v:N1}"; break;
case "€": n = 2; data = $"{v:N2}"; break;
case "€/kg": n = 4; data = $"{v:N4}"; break;

View File

@ -434,8 +434,7 @@ namespace Elwig.Helpers {
if (accept != null)
client.DefaultRequestHeaders.Accept.Add(new(accept));
if (username != null || password != null)
client.DefaultRequestHeaders.Authorization = new("Basic", Convert.ToBase64String(
Utils.UTF8.GetBytes($"{username}:{password}")));
client.DefaultRequestHeaders.Authorization = new("Basic", Convert.ToBase64String(Utils.UTF8.GetBytes($"{username}:{password}")));
return client;
}
@ -454,6 +453,8 @@ namespace Elwig.Helpers {
}
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 += "/";
using var client = GetHttpClient(username, password, accept: "application/json");
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) {
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 res = await client.GetAsync(url);
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, int year) =>
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);
}
}
}

View File

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

View File

@ -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; }
}
}

View File

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

View File

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

View File

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

View File

@ -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", "Gewicht", "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);
}
}
}

View File

@ -48,6 +48,7 @@ namespace Elwig.Services {
var filterNotVar = new List<string>();
var filterAttr = new List<string>();
var filterNotAttr = new List<string>();
var filterSeasons = new List<int>();
var filter = vm.TextFilter;
if (filter.Count > 0) {
@ -87,6 +88,10 @@ namespace Elwig.Services {
filter.RemoveAt(i--);
filterNames.Add($"ohne {var[e[1..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 (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));
foreach (var year in filterSeasons) areaComQuery = Utils.ActiveAreaCommitments(areaComQuery, year);
}
return (filterNames, areaComQuery, filter);

View File

@ -809,6 +809,38 @@ namespace Elwig.Services {
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) {
using var ctx = new AppDbContext();
IQueryable<DeliveryPart> query;

View File

@ -212,6 +212,7 @@ namespace Elwig.Services {
var filterLfbisNr = new List<string>();
var filterUstIdNr = new List<string>();
var filterAreaCom = new List<string>();
var filterNotAreaCom = new List<string>();
var filter = vm.TextFilter;
if (filter.Count > 0) {
@ -223,7 +224,15 @@ namespace Elwig.Services {
for (int i = 0; i < filter.Count; 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);
filter.RemoveAt(i--);
filterNames.Add("Funktionäre");
@ -314,10 +323,22 @@ namespace Elwig.Services {
memberQuery = memberQuery.Where(m => m.TelephoneNumbers.Any(t => t.Number.Replace(" ", "").StartsWith(e)));
filter.RemoveAt(i--);
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())) {
filterAreaCom.Add(e.ToUpper());
filter.RemoveAt(i--);
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)) {
filterLfbisNr.Add(e);
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 (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 (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 (filterUstIdNr.Count > 0) memberQuery = memberQuery.Where(m => m.UstIdNr != null && filterUstIdNr.Contains(m.UstIdNr));
}

View File

@ -8,7 +8,6 @@ using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using System.Windows.Input;
namespace Elwig.Windows {
@ -358,22 +357,6 @@ namespace Elwig.Windows {
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) {
return InputTextChanged(input, new ValidationResult(true, null));
}

View File

@ -89,7 +89,8 @@
Filtern nach:<LineBreak/>
<Bold>Sorte</Bold>: z.B. GV, zw, RR, ...<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>
</TextBox.ToolTip>
</TextBox>

View File

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

View File

@ -10,7 +10,6 @@ using Elwig.Helpers.Billing;
using Elwig.Models.Entities;
using ScottPlot.Plottables;
using ScottPlot;
using ScottPlot.Control;
namespace Elwig.Windows {
public partial class ChartWindow : ContextWindow {
@ -226,30 +225,8 @@ namespace Elwig.Windows {
DataPlot.Color = 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.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.IsVisible = false;

View File

@ -67,13 +67,29 @@
<Menu Grid.ColumnSpan="3" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
<MenuItem Header="Lieferschein">
<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"
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"
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"
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 Header="Lieferjournal">
<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"
Click="Menu_DeliveryJournal_PrintToday_Click" InputGestureText="Strg+J"/>
</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 x:Name="Menu_DeliveryDepreciationList_SaveFilters" Header="...aus Filtern speichern... (Excel)"
Click="Menu_DeliveryDepreciationList_SaveFilters_Click"/>
@ -139,6 +129,38 @@
<MenuItem x:Name="Menu_DeliveryDepreciationList_PrintSeason" Header="...von Saison drucken"
Click="Menu_DeliveryDepreciationList_PrintSeason_Click"/>
</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 x:Name="Menu_Bki_SaveList" Header="Traubentransportscheinliste speichern..."/>
</MenuItem>

View File

@ -41,7 +41,7 @@ namespace Elwig.Windows {
CommandBindings.Add(new CommandBinding(CtrlP, Menu_DeliveryNote_Show_Click));
CommandBindings.Add(new CommandBinding(CtrlO, Menu_DeliveryJournal_ShowFilters_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(CtrlShiftO, Menu_DeliveryJournal_PrintFilters_Click));
RequiredInputs = [
@ -66,7 +66,7 @@ namespace Elwig.Windows {
SecondsTimer.Tick += new EventHandler(OnSecondPassed);
SecondsTimer.Interval = new TimeSpan(0, 0, 1);
InitializeDelayTimer(SearchInput, SearchInput_TextChanged);
ControlUtils.InitializeDelayTimer(SearchInput, SearchInput_TextChanged);
SearchInput.TextChanged -= SearchInput_TextChanged;
ViewModel.FilterSeason = Utils.CurrentLastSeason;
@ -97,17 +97,17 @@ namespace Elwig.Windows {
WeighingDButton.Visibility = Visibility.Hidden;
}
Menu_WineQualityStatistics_ModeOe.IsChecked = false;
Menu_WineQualityStatistics_ModeKmw1.IsChecked = false;
Menu_WineQualityStatistics_ModeKmw2.IsChecked = false;
Menu_WineQualityStatistics_ModeKmw5.IsChecked = false;
Menu_WineQualityStatistics_ModeKmw10.IsChecked = false;
Menu_Statistics_WineQuality_ModeOe.IsChecked = false;
Menu_Statistics_WineQuality_ModeKmw1.IsChecked = false;
Menu_Statistics_WineQuality_ModeKmw2.IsChecked = false;
Menu_Statistics_WineQuality_ModeKmw5.IsChecked = false;
Menu_Statistics_WineQuality_ModeKmw10.IsChecked = false;
switch (App.Client.OrderingMemberList) {
case 0: Menu_WineQualityStatistics_ModeOe.IsChecked = true; break;
case 1: Menu_WineQualityStatistics_ModeKmw1.IsChecked = true; break;
case 2: Menu_WineQualityStatistics_ModeKmw2.IsChecked = true; break;
case 3: Menu_WineQualityStatistics_ModeKmw5.IsChecked = true; break;
case 4: Menu_WineQualityStatistics_ModeKmw10.IsChecked = true; break;
case 0: Menu_Statistics_WineQuality_ModeOe.IsChecked = true; break;
case 1: Menu_Statistics_WineQuality_ModeKmw1.IsChecked = true; break;
case 2: Menu_Statistics_WineQuality_ModeKmw2.IsChecked = true; break;
case 3: Menu_Statistics_WineQuality_ModeKmw5.IsChecked = true; break;
case 4: Menu_Statistics_WineQuality_ModeKmw10.IsChecked = true; break;
}
Menu_Export_UploadFilters.IsEnabled = App.Config.SyncUrl != null;
@ -218,40 +218,44 @@ namespace Elwig.Windows {
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);
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);
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);
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);
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);
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);
private async void Menu_WineQualityStatistics_Mode_Click(object sender, RoutedEventArgs evt) {
Menu_WineQualityStatistics.IsSubmenuOpen = true;
if (sender == Menu_WineQualityStatistics_ModeOe) {
private async void Menu_Statistics_WineQuality_Mode_Click(object sender, RoutedEventArgs evt) {
Menu_Statistics.IsSubmenuOpen = true;
Menu_Statistics_WineQuality.IsSubmenuOpen = true;
if (sender == Menu_Statistics_WineQuality_ModeOe) {
App.Client.OrderingMemberList = 0;
} else if (sender == Menu_WineQualityStatistics_ModeKmw1) {
} else if (sender == Menu_Statistics_WineQuality_ModeKmw1) {
App.Client.OrderingMemberList = 1;
} else if (sender == Menu_WineQualityStatistics_ModeKmw2) {
} else if (sender == Menu_Statistics_WineQuality_ModeKmw2) {
App.Client.OrderingMemberList = 2;
} else if (sender == Menu_WineQualityStatistics_ModeKmw5) {
} else if (sender == Menu_Statistics_WineQuality_ModeKmw5) {
App.Client.OrderingMemberList = 3;
} else if (sender == Menu_WineQualityStatistics_ModeKmw10) {
} else if (sender == Menu_Statistics_WineQuality_ModeKmw10) {
App.Client.OrderingMemberList = 4;
}
Menu_WineQualityStatistics_ModeOe.IsChecked = App.Client.OrderingMemberList == 0;
Menu_WineQualityStatistics_ModeKmw1.IsChecked = App.Client.OrderingMemberList == 1;
Menu_WineQualityStatistics_ModeKmw2.IsChecked = App.Client.OrderingMemberList == 2;
Menu_WineQualityStatistics_ModeKmw5.IsChecked = App.Client.OrderingMemberList == 3;
Menu_WineQualityStatistics_ModeKmw10.IsChecked = App.Client.OrderingMemberList == 4;
Menu_Statistics_WineQuality_ModeOe.IsChecked = App.Client.OrderingMemberList == 0;
Menu_Statistics_WineQuality_ModeKmw1.IsChecked = App.Client.OrderingMemberList == 1;
Menu_Statistics_WineQuality_ModeKmw2.IsChecked = App.Client.OrderingMemberList == 2;
Menu_Statistics_WineQuality_ModeKmw5.IsChecked = App.Client.OrderingMemberList == 3;
Menu_Statistics_WineQuality_ModeKmw10.IsChecked = App.Client.OrderingMemberList == 4;
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) =>
await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromFilters, ExportMode.SaveList);
private async void Menu_DeliveryDepreciationList_ShowFilters_Click(object sender, RoutedEventArgs evt) =>
@ -306,19 +310,23 @@ namespace Elwig.Windows {
UnsetDefaultValue(GerebeltGewogenInput);
}
if (!App.Client.HasNetWeighing(ViewModel.Branch)) {
if (App.Client.HasBoxWeighing(ViewModel.Branch)) {
LesewagenInput.IsEnabled = false;
SetDefaultValue(LesewagenInput, false);
HandPickedInput.IsThreeState = false;
UnsetDefaultValue(HandPickedInput);
} else {
LesewagenInput.IsEnabled = true;
UnsetDefaultValue(LesewagenInput);
}
if (!App.Client.HasNetWeighing(ViewModel.Branch)) {
HandPickedInput.IsThreeState = false;
UnsetDefaultValue(HandPickedInput);
} else {
HandPickedInput.IsThreeState = true;
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;
SetDefaultValue(GebundenInput, null);
} else {
@ -1376,7 +1384,7 @@ namespace Elwig.Windows {
}
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;
}
if (!ViewModel.IsReceipt || App.Client.HasNetWeighing(ViewModel.Branch)) {
@ -1386,7 +1394,7 @@ namespace Elwig.Windows {
}
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;
}
CheckBox_Changed(sender, evt);

View File

@ -69,13 +69,29 @@
<Menu Grid.ColumnSpan="3" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
<MenuItem Header="Anmeldeliste">
<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)"
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)"
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"
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>
</Menu>

View File

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

View File

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

View File

@ -36,9 +36,10 @@ namespace Elwig.Windows {
}
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")!;
EventData.Text = ((EventLogEntry)p.GetValue(EventList.SelectedItem)!).Message;
EventData.Text = ((EventLogEntry)p.GetValue(item)!).Message;
}
}
}

View File

@ -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>

View File

@ -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 = "";
}
}
}
}

View File

@ -27,7 +27,26 @@
<Setter Property="TextWrapping" Value="NoWrap"/>
</Style>
</Window.Resources>
<TabControl x:Name="TabControl" BorderThickness="0" PreviewDragOver="Document_PreviwDragOver" AllowDrop="True" Drop="Document_Drop">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="19"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="24"/>
</Grid.RowDefinitions>
<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>
@ -304,4 +323,38 @@
</Grid>
</TabItem>
</TabControl>
<StatusBar Grid.Row="2" BorderThickness="0,1,0,0" BorderBrush="Gray">
<StatusBar.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<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>

View File

@ -58,6 +58,7 @@ namespace Elwig.Windows {
public IEnumerable<Member> Recipients = [];
protected Document? PrintDocument;
protected Dictionary<Member, List<Document>>? PrintMemberDocuments;
protected Dictionary<Member, List<Document>>? EmailDocuments;
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;
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;
PostalSender2.Text = App.Client.Sender2;
PostalLocation.Text = App.BranchLocation;
@ -242,6 +263,11 @@ namespace Elwig.Windows {
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) {
TabControl.SelectedIndex = 1;
TabControl.AllowDrop = false;
@ -312,7 +338,7 @@ namespace Elwig.Windows {
if (idx == 0) {
SelectedDocs.Add(new(DocType.MemberDataSheet, s, null));
} else if (idx == 1) {
SelectedDocs.Add(new(DocType.DeliveryConfirmation, s, (Year, DocumentNonDeliverersInput.IsChecked == true)));
SelectedDocs.Add(new(DocType.DeliveryConfirmation, s, Year));
RecipientsDeliveryMembersInput.IsChecked = true;
} else if (idx >= 2) {
var name = s.Split(" ")[^1];
@ -449,18 +475,32 @@ namespace Elwig.Windows {
}
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);
EmailWishCount = Recipients.Count(m => m.EmailAddresses.Count > 0 && m.ContactViaEmail);
PostalAllCount = Recipients.Count();
PostalWishCount = Recipients.Count(m => m.ContactViaPost);
var m = EmailAllInput.IsChecked == true ? 3 : EmailWishInput.IsChecked == true ? 2 : 1;
PostalNoEmailCount = PostalAllCount - (m == 3 ? EmailAllCount : m == 2 ? EmailWishCount : 0);
var countEmail = (modeEmail == 2 ? EmailAllCount : modeEmail == 1 ? 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();
}
private async Task UpdateTextParameters() {
private async Task UpdateClientParameters() {
var changed = false;
var dcInclude = DocumentNonDeliverersInput.IsChecked ?? false;
if (dcInclude != App.Client.MailIncludeNonDeliverers) {
App.Client.MailIncludeNonDeliverers = dcInclude;
changed = true;
}
var dcText = DeliveryConfirmationFooterInput.Text.Trim();
if (dcText.Length == 0) dcText = null;
if (dcText != App.Client.TextDeliveryConfirmation) {
@ -475,6 +515,53 @@ namespace Elwig.Windows {
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();
if (emailSubject.Length == 0) emailSubject = null;
if (emailSubject != App.Client.TextEmailSubject) {
@ -519,7 +606,7 @@ namespace Elwig.Windows {
GenerateButton.IsEnabled = false;
DisposeDocs();
await UpdateTextParameters();
await UpdateClientParameters();
using var ctx = new AppDbContext();
@ -562,8 +649,7 @@ namespace Elwig.Windows {
Dictionary<(int, int), (IDictionary<int, CreditNoteDeliveryData>, IDictionary<int, PaymentMember>, BillingData)> cnData = [];
foreach (var doc in docs) {
if (doc.Type == DocType.DeliveryConfirmation) {
var details = ((int, bool))doc.Details!;
var year = details.Item1;
var year = (int)doc.Details!;
try {
var b = new Billing(year);
@ -610,13 +696,11 @@ namespace Elwig.Windows {
} else if (doc.Type == DocType.MemberDataSheet) {
return [new GeneratedDoc(new MemberDataSheet(m, ctx) { Date = postalDate })];
} else if (doc.Type == DocType.DeliveryConfirmation) {
var details = ((int, bool))doc.Details!;
var year = details.Item1;
var include = details.Item2;
var year = (int)doc.Details!;
DeliveryConfirmationDeliveryData data;
if (dcData[year].TryGetValue(m.MgNr, out var d)) {
data = d;
} else if (include) {
} else if (App.Client.MailIncludeNonDeliverers) {
data = DeliveryConfirmationDeliveryData.CreateEmpty(year, m);
} else {
return [];
@ -691,11 +775,13 @@ namespace Elwig.Windows {
EmailDocuments = email;
}
var printDocs = memberDocs
var printMemberDocs = memberDocs
.Where(d =>
printMode == 3 ||
(printMode == 2 && d.Member.ContactViaPost) ||
(printMode == 1 && !emailRecipients.Contains(d.Member.MgNr)))
.ToList();
var printDocs = printMemberDocs
.SelectMany(m => {
var docs = m.Docs.Select(d => d.Doc).ToList();
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;
}));
PrintDocument = print;
PrintMemberDocuments = printMemberDocs.ToDictionary(m => m.Member, m => m.Docs.Select(d => d.Doc).ToList());
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
UnlockInputs();
@ -727,6 +814,9 @@ namespace Elwig.Windows {
Mouse.OverrideCursor = null;
return;
}
} else {
PrintDocument = null;
PrintMemberDocuments = null;
}
ProgressBar.Value = 100.0;
@ -763,7 +853,7 @@ namespace Elwig.Windows {
}
private async void PrintButton_Click(object sender, RoutedEventArgs evt) {
if (PrintDocument == null) return;
if (PrintDocument == null || PrintMemberDocuments == null) return;
PrintButton.IsEnabled = false;
GenerateButton.IsEnabled = false;
@ -777,6 +867,14 @@ namespace Elwig.Windows {
PrintDocument.Show();
} else {
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;
}
@ -808,6 +906,7 @@ namespace Elwig.Windows {
Mouse.OverrideCursor = Cursors.AppStarting;
var subject = EmailSubjectInput.Text;
var text = EmailBodyInput.Text;
await Utils.AddSentMailBody(subject, text, EmailDocuments.Count);
foreach (var (m, docs) in EmailDocuments) {
using var msg = new MimeMessage();
msg.From.Add(new MailboxAddress(App.Client.NameFull, App.Config.Smtp.Value.From));
@ -822,6 +921,12 @@ namespace Elwig.Windows {
}
msg.Body = body;
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);
@ -870,7 +975,7 @@ namespace Elwig.Windows {
}
private void PostalInput_Changed(object sender, RoutedEventArgs evt) {
ResetDocuments();
UpdatePostalEmailRecipients();
}
private void OrderInput_Changed(object sender, RoutedEventArgs evt) {

View File

@ -10,6 +10,7 @@
<Style TargetType="Button">
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="9,3"/>
<Setter Property="Height" Value="35"/>
@ -19,23 +20,67 @@
<Grid>
<Menu BorderThickness="0,0,0,1" VerticalAlignment="Top" Height="19" BorderBrush="LightGray" Background="White">
<MenuItem Header="Datenbank">
<MenuItem Header="Daten exportieren..." Click="Menu_Database_Export_Click" IsEnabled="False"/>
<MenuItem Header="Daten importieren..." Click="Menu_Database_Import_Click"/>
<MenuItem Header="Daten exportieren..." Click="Menu_Database_Export_Click" IsEnabled="False">
<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/>
<MenuItem Header="Abfragen stellen" Click="Menu_Database_Query_Click"/>
<MenuItem Header="Speicherort öffnen..." Click="Menu_Database_Open_Click"/>
<MenuItem Header="Abfragen stellen" Click="Menu_Database_Query_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 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 x:Name="HelpMenu" Header="Hilfe">
<MenuItem Header="Über"/>
<MenuItem x:Name="Menu_Help_Update" Header="Nach Updates suchen" Click="Menu_Help_Update_Click"/>
<MenuItem x:Name="Menu_Help_Smtp" Header="E-Mail-Einstellungen testen" Click="Menu_Help_Smtp_Click"/>
<MenuItem x:Name="Menu_Help_Log" Header="Fehler-Protokoll anzeigen" Click="Menu_Help_Log_Click"/>
<MenuItem Header="Über">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE946;"/>
</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/>
<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 x:Name="Menu_Help_Config" Header="Konfigurationsdatei öffnen..." Click="Menu_Help_Config_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>
</Menu>
@ -58,26 +103,64 @@
</TextBlock>
</Grid>
<Button x:Name="MemberAdminButton" Content="Mitglieder" Click="MemberAdminButton_Click"
Margin="0,170,205,0"/>
<Button x:Name="MailButton" Content="Rundschreiben" Click="MailButton_Click"
Margin="205,170,0,0"/>
<Button x:Name="DeliveryAdminButton" Content="Lieferungen" Click="DeliveryAdminButton_Click"
Margin="0,210,205,0"/>
<Button x:Name="ReceiptButton" Content="Übernahme" Click="ReceiptButton_Click"
Margin="205,210,0,0"/>
<Button x:Name="BaseDataButton" Content="Stammdaten" Click="BaseDataButton_Click"
Margin="0,250,205,0"/>
<Button x:Name="DeliveryAncmtButton" Content="Anmeldungen" Click="DeliveryAncmtButton_Click"
Margin="205,250,0,0"/>
<Button x:Name="MemberAdminButton" Click="MemberAdminButton_Click"
Margin="0,170,205,0">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE77B;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="0.5,1.5,0,0"/>
<TextBlock TextAlignment="Center">Mitglieder</TextBlock>
</Grid>
</Button>
<Button x:Name="MailButton" Click="MailButton_Click"
Margin="205,170,0,0">
<Grid>
<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"
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"
HorizontalContentAlignment="Center"
ToolTip="Lieferungen und Mitgliederdaten anderer Zweigstellen herunterladen"/>
<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"
HorizontalContentAlignment="Center"
ToolTip="Lieferungen dieser Zweigstelle hochladen"/>
<Expander x:Name="SeasonFinish" Header="Leseabschluss" SnapsToDevicePixels="True"
@ -90,21 +173,104 @@
Margin="0,13,0,0" VerticalAlignment="Top" HorizontalAlignment="Center"
TextChanged="SeasonInput_TextChanged"/>
<Button x:Name="OverUnderDeliveryButton" Content="Über-/Unterlieferungen"
Click="OverUnderDeliveryButton_Click"
Margin="0,50,195,10" Width="190"/>
<Button x:Name="DeliveryConfirmationButton" Content="Anlieferungsbestätigung"
<Button x:Name="DeliveryConfirmationButton"
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"
Click="BreakdownButton_Click"
Margin="0,90,195,10" Width="190"/>
<Button x:Name="PaymentButton" Content="Auszahlung"
<Button x:Name="PaymentButton"
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 des Gewichts 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>Gewicht</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>
</Expander>
</Grid>

View File

@ -251,7 +251,7 @@ namespace Elwig.Windows {
}
private void SeasonFinish_Expanded(object sender, RoutedEventArgs evt) {
Height = 530;
Height = 660;
}
private void SeasonFinish_Collapsed(object sender, RoutedEventArgs evt) {
@ -260,12 +260,34 @@ namespace Elwig.Windows {
private async void SeasonInput_TextChanged(object? sender, TextChangedEventArgs? evt) {
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);
DeliveryConfirmationButton.IsEnabled = valid;
OverUnderDeliveryButton.IsEnabled = valid;
PaymentButton.IsEnabled = valid;
OverUnderDeliveryButton.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) {
@ -275,6 +297,12 @@ namespace Elwig.Windows {
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) {
if (SeasonInput.Value is not int year)
return;
@ -297,23 +325,15 @@ namespace Elwig.Windows {
using var ctx = new AppDbContext();
var tbl1 = await OverUnderDeliveryData.ForSeason(ctx.OverUnderDeliveryRows, year);
var tbl2 = await AreaComUnderDeliveryData.ForSeason(ctx.AreaComUnderDeliveryRows, year);
var tbl3 = await MemberDeliveryPerVariantData.ForSeason(ctx.MemberDeliveryPerVariantRows, year);
using var ods = new OdsFile(d.FileName);
await ods.AddTable(tbl1);
await ods.AddTable(tbl2);
await ods.AddTable(tbl3);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
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) {
if (SeasonInput.Value is not int year)
return;
@ -346,5 +366,63 @@ namespace Elwig.Windows {
}
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;
}
}
}

View File

@ -52,19 +52,43 @@
<Menu Grid.ColumnSpan="3" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
<MenuItem Header="Kontaktieren">
<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}"
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 Header="Stammdatenblatt">
<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}"
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}"
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}"
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 Header="Anlieferungsbestätigung" x:Name="Menu_DeliveryConfirmation">
<MenuItem x:Name="Menu_DeliveryConfirmation_Show" Header="...anzeigen (PDF)" IsEnabled="False"
@ -170,6 +194,7 @@
<Bold>Stammgemeinde</Bold>: z.B. matzen, Wolkersdorf, ...<LineBreak/>
<Bold>UID</Bold>: z.B. ATU12345678, ...<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>Buchführend</Bold>: buchf[ührend], Pauschal[iert], !buchf[ührend]<LineBreak/>
<Bold>Volllieferant</Bold>: voll[lieferant], !Voll[lieferant] (nicht-Volllieferant)<LineBreak/>
@ -177,7 +202,7 @@
<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>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")
</TextBlock>
</TextBox.ToolTip>

View File

@ -68,7 +68,7 @@ namespace Elwig.Windows {
(PhoneNr9TypeInput, PhoneNr9Input, PhoneNr9CommentInput),
];
InitializeDelayTimer(SearchInput, SearchInput_TextChanged);
ControlUtils.InitializeDelayTimer(SearchInput, SearchInput_TextChanged);
SearchInput.TextChanged -= SearchInput_TextChanged;
ViewModel.MemberListOrderByMgNr = false;
@ -187,49 +187,80 @@ namespace Elwig.Windows {
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());
var font = new System.Windows.Media.FontFamily("Segoe MDL2 Assets");
MenuItem? temp = null;
var seasons = await ctx.Seasons.OrderByDescending(s => s.Year).ToListAsync();
Menu_DeliveryConfirmation.Items.Clear();
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") });
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;
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;
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;
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.SetBinding(IsEnabledProperty, new Binding() { Path = new("MemberCanSendEmail") });
i.Items.Add(email);
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();
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}]") });
foreach (var v in s.PaymentVariants.OrderByDescending(v => 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;
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;
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;
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.Click += Menu_CreditNote_Email_Click;
i2.Items.Add(email);
i1.Items.Add(i2);
}
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();

View File

@ -56,21 +56,45 @@
<Menu Grid.ColumnSpan="2" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
<MenuItem Header="Variantendaten">
<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"
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"
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"
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 Header="Buchungsliste">
<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 Header="Überweisungsdaten">
<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>
</Menu>
@ -116,8 +140,9 @@
<TextBox x:Name="CommentInput" Grid.Column="1" HorizontalAlignment="Stretch" Margin="0,40,10,0"
TextChanged="CommentInput_TextChanged"/>
<Label Content="Erstellt am:" 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"/>
<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"
TextChanged="DateInput_TextChanged"/>
<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"
@ -215,10 +240,16 @@
Click="SaveButton_Click"/>
</Grid>
<Button x:Name="MailButton" Content="Traubengutschriften"
FontSize="14" Width="160" Margin="10,10,10,10" Height="27" IsEnabled="False"
<Button x:Name="MailButton"
FontSize="14" Width="180" Margin="10,10,10,10" Height="30" IsEnabled="False"
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>
<StatusBar Grid.Row="2" Grid.ColumnSpan="2" BorderThickness="0,1,0,0" BorderBrush="Gray">

View File

@ -19,7 +19,7 @@ namespace Elwig.Windows {
public readonly int Year;
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 bool WeightModifierChanged = false;
@ -175,6 +175,7 @@ namespace Elwig.Windows {
private void UpdateSaveButton() {
SaveButton.IsEnabled = PaymentVariantList.SelectedItem != null &&
((DataChanged && DataValid) || NameChanged || CommentChanged ||
(DateChanged && DateValid) ||
(TransferDateChanged && TransferDateValid) ||
(ConsiderModifiersInput.IsChecked != BillingData?.ConsiderDelieryModifiers) ||
(ConsiderPenaltiesInput.IsChecked != BillingData?.ConsiderContractPenalties) ||
@ -433,10 +434,16 @@ namespace Elwig.Windows {
var withoutIban = v.Credits.Count(c => c.Member.Iban == null);
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);
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() {
FileName = $"{App.Client.NameToken}-Überweisungsdaten-{v.Year}-{v.Name.Trim().Replace(' ', '-')}.{Ebics.FileExtension}",
@ -489,6 +496,7 @@ namespace Elwig.Windows {
try {
v.Name = NameInput.Text;
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;
var d = App.Config.Debug ? BillingData.FromJson(DataInput.Text) : BillingData;
d.ConsiderDelieryModifiers = ConsiderModifiersInput.IsChecked ?? false;
@ -557,6 +565,27 @@ namespace Elwig.Windows {
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) {
if (PaymentVariantList.SelectedItem is not PaymentVar v) {
ControlUtils.ClearInputState(TransferDateInput);

View File

@ -1,4 +1,4 @@
<Project Sdk="WixToolset.Sdk/5.0.0">
<Project Sdk="WixToolset.Sdk/6">
<PropertyGroup>
<HarvestFileSuppressUniqueIds>false</HarvestFileSuppressUniqueIds>
<HarvestFileGenerateGuidsNow>true</HarvestFileGenerateGuidsNow>

View File

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

View File

@ -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"
IconSourceFile="$(var.ElwigProjectDir)\Resources\Images\Elwig.ico">
<BootstrapperApplication>

View File

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

View File

@ -1,6 +1,5 @@
using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Elwig.Models.Entities;
using System.Text.Json;
namespace Tests.HelperTests {
@ -516,7 +515,7 @@ namespace Tests.HelperTests {
}, null), GetSelection(["GV/K-", "ZW/K-"])),
new GraphEntry(2, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[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() {
[73] = 0.65m,
[84] = 1.2m
@ -564,7 +563,7 @@ namespace Tests.HelperTests {
}, null), GetSelection(["GV/-B", "ZW/-B"])),
new GraphEntry(2, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[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() {
[73] = 0.65m,
[84] = 1.2m
@ -653,7 +652,7 @@ namespace Tests.HelperTests {
}, null), GetSelection(["BP/-B", "ZW/-B", "FV/-B"])),
new GraphEntry(2, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() {
[73] = 0.8m,
}, null), GetSelection(["BP/-", "ZW/-", "FV/-", "WR/-", "BL/-"])),
}, null), GetSelection(["BP/-", "ZW/-", "FV/-", "WR/-", "BL/-", "RR/-"])),
];
var data = BillingData.FromGraphEntries(entries);
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);
}
});
}
}
}

View File

@ -19,16 +19,16 @@
</Target>
<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="NReco.PdfRenderer" Version="1.5.4" />
<PackageReference Include="NUnit" Version="4.2.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.3.0">
<PackageReference Include="NReco.PdfRenderer" Version="1.6.0" />
<PackageReference Include="NUnit" Version="4.3.2" />
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.7.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.2">
<PackageReference Include="coverlet.collector" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>