Compare commits

...

49 Commits

Author SHA1 Message Date
lorenz.stechauner 0d8cdd5d6e Bump version to 1.1.0.0
Test / Run tests (push) Successful in 1m54s
Deploy / Build and Deploy (push) Successful in 1m42s
2026-07-09 01:43:38 +02:00
lorenz.stechauner 2350b55cea Tests: Update dependencies
Test / Run tests (push) Successful in 2m6s
2026-07-09 01:01:38 +02:00
lorenz.stechauner e8789aff37 Elwig: Update dependencies 2026-07-09 01:01:38 +02:00
lorenz.stechauner 918555f2b8 [#20] MemberAdminWindow: Add MemberBusinessSharesAdminWindow to manage member shares 2026-07-09 01:01:28 +02:00
lorenz.stechauner 1169ba5101 ChartWindow: Fix warnings and avoid null-forgiving operator
Test / Run tests (push) Successful in 2m10s
2026-07-06 19:11:39 +02:00
lorenz.stechauner 76da392f5f [#20][#80] MemberAdminWindow: Update business share status bar tool tip
Test / Run tests (push) Successful in 2m1s
2026-07-06 18:18:46 +02:00
lorenz.stechauner 000117dc67 CreditNoteData: Add AccountingNr of member
Test / Run tests (push) Successful in 3m6s
2026-07-06 11:03:38 +02:00
lorenz.stechauner 314a0d37a8 [#20] MemberDataSheet: Show members business share history 2026-07-03 12:02:44 +02:00
lorenz.stechauner 765edbdae6 [#20][#80] MemberAdminWindow: Add functionality to edit other business shares 2026-07-03 12:02:44 +02:00
lorenz.stechauner 9a0a422c8a [#20] Entities/MemberHistory: Add DeductYear to simplify billing 2026-07-03 12:02:37 +02:00
lorenz.stechauner a71c41af5c AppDbContext: Add Member.Branch AutoInclude to fix MemberDataSheet
Test / Run tests (push) Successful in 2m52s
2026-07-02 12:17:32 +02:00
lorenz.stechauner 62346a63a8 [#80] Elwig: Add client parameter to switch between simple and red/white business share mode
Test / Run tests (push) Successful in 2m20s
2026-07-01 17:09:55 +02:00
lorenz.stechauner feee6ad1ec [#20][#80] Elwig: Update member_history and add different types of shares
Test / Run tests (push) Successful in 2m0s
2026-07-01 16:22:01 +02:00
lorenz.stechauner 4ebe07f579 AppDbUpdater: Fix handling of foreign key errors
Test / Run tests (push) Successful in 3m4s
2026-07-01 00:36:04 +02:00
lorenz.stechauner beacba6bd9 Services: Add Utils.RunForeground() to factor Task.Run and try-catch-Blocks out
Test / Run tests (push) Successful in 2m2s
2026-06-30 02:32:24 +02:00
lorenz.stechauner 69efca1cc3 Tests: Use Assert.EnterMultipleScope instead of Assert.Multiple
Test / Run tests (push) Successful in 2m6s
2026-06-29 02:33:36 +02:00
lorenz.stechauner fcd0555e4d Elwig: Add InteractionService to centrally manage MessageBox and SaveFileDialogs
Test / Run tests (push) Successful in 2m50s
2026-06-29 02:28:34 +02:00
lorenz.stechauner b93e987685 Bump version to 1.0.5.6
Test / Run tests (push) Successful in 2m24s
Deploy / Build and Deploy (push) Successful in 1m58s
2026-06-25 13:07:10 +02:00
lorenz.stechauner 1ad97a78ff ClientParameters: Add client Seewinkel
Test / Run tests (push) Successful in 3m13s
2026-06-25 12:33:20 +02:00
lorenz.stechauner 1261be001c BaseDataWindow: Remove transactions inside *Save() functions to avoid nesting them 2026-06-25 12:32:46 +02:00
lorenz.stechauner 15de07a4c3 BillingData: Remove quality entry when using –FromGraphEntries
Test / Run tests (push) Successful in 2m34s
2026-06-24 15:51:37 +02:00
lorenz.stechauner 5cbad9a195 Bump version to 1.0.5.5
Test / Run tests (push) Successful in 6m43s
Deploy / Build and Deploy (push) Successful in 2m10s
2026-06-24 14:36:44 +02:00
lorenz.stechauner 4e027a9add Elwig: Update dependencies
Test / Run tests (push) Successful in 4m40s
2026-06-24 14:24:25 +02:00
lorenz.stechauner f32ff945ec BillingData: Preserve all settings when using FromGraphEntries() 2026-06-24 14:24:15 +02:00
lorenz.stechauner ba3f66591e BilllingVariant: Allow for credits with negative value 2026-06-24 13:20:46 +02:00
lorenz.stechauner 3f65b2350b PaymentVariantService: Fix crash in GenerateEbics()
Test / Run tests (push) Successful in 2m48s
2026-06-24 10:36:28 +02:00
lorenz.stechauner 7a6a77419b Bump version to 1.0.5.4
Test / Run tests (push) Successful in 2m17s
Deploy / Build and Deploy (push) Successful in 2m6s
2026-06-22 20:23:46 +02:00
lorenz.stechauner 7a993585c3 Elwig: Update dependencies
Test / Run tests (push) Successful in 2m3s
2026-06-22 20:08:18 +02:00
lorenz.stechauner d0ce377d92 AppDbContext: Fix FetchMembers when using for specific MgNr
Test / Run tests (push) Successful in 2m59s
2026-06-22 20:06:34 +02:00
lorenz.stechauner f8b3147c72 Tests: Update dependencies
Test / Run tests (push) Successful in 2m52s
2026-06-18 12:25:22 +02:00
lorenz.stechauner b4ab8349b4 Elwig: Update dependencies 2026-06-18 12:25:09 +02:00
lorenz.stechauner 5db5876905 Installer: Bundle and ship source code together with binaries
Test / Run tests (push) Successful in 3m14s
2026-05-01 15:10:19 +02:00
lorenz.stechauner 0617726139 Bump version to 1.0.5.3
Test / Run tests (push) Successful in 2m7s
Deploy / Build and Deploy (push) Successful in 2m13s
2026-04-29 11:42:05 +02:00
lorenz.stechauner 9dfe71d6d0 DeliveryConfirmation: Adapt spacing between rows to CreditNote
Test / Run tests (push) Successful in 2m52s
2026-04-29 11:37:09 +02:00
lorenz.stechauner 72155fc54e Documents: Fix error because of modifiers in CreditNote and DeliveryConfirmation 2026-04-29 11:29:16 +02:00
lorenz.stechauner 61bdb2f3b5 Bump version to 1.0.5.2
Test / Run tests (push) Successful in 2m15s
Deploy / Build and Deploy (push) Successful in 2m0s
2026-04-24 10:27:37 +02:00
lorenz.stechauner 2915f08a6b Elwig: Update dependencies
Test / Run tests (push) Successful in 3m7s
2026-04-24 09:49:06 +02:00
lorenz.stechauner 5c2fae1855 DeliveryAdminWindow: Fix attributes and cultivations in DeliveryPartList 2026-04-24 09:48:14 +02:00
lorenz.stechauner 95fabe3633 Bump version to 1.0.5.1
Test / Run tests (push) Successful in 2m25s
Deploy / Build and Deploy (push) Successful in 2m14s
2026-04-20 19:23:01 +02:00
lorenz.stechauner 7861a15e2e Test: Update dependencies
Test / Run tests (push) Successful in 2m20s
2026-04-20 15:12:29 +02:00
lorenz.stechauner 20fd5d5826 Elwig: Update dependencies 2026-04-20 15:12:29 +02:00
lorenz.stechauner 2b3c293730 Database: Adapt to changes for examples in WLNS 2026-04-20 15:12:22 +02:00
lorenz.stechauner a852dbb242 PaymentVariantSummary: Fix price display when null 2026-04-20 15:05:09 +02:00
lorenz.stechauner 7edf395497 CreditNote: Fix padding for Rbl. 2026-04-20 14:21:13 +02:00
lorenz.stechauner ea2b6db1fc Billing: Fix modifiers with absolute amounts 2026-04-20 14:20:43 +02:00
lorenz.stechauner cf48f005e2 DeliveryAdminWindow: Fix missing LostFocus for gradation inputs
Test / Run tests (push) Successful in 2m54s
2026-04-08 17:18:40 +02:00
lorenz.stechauner 4eac8cd629 DeliveryAdminWindow: Fix modifiers for bulk actions 2026-04-08 17:18:08 +02:00
lorenz.stechauner 360856585a Bump version to 1.0.5.0
Test / Run tests (push) Successful in 1m54s
Deploy / Build and Deploy (push) Successful in 2m14s
2026-04-08 00:47:15 +02:00
lorenz.stechauner cc6e31a006 Installer/Setup: Update to WiX 7 2026-04-08 00:31:00 +02:00
103 changed files with 3474 additions and 1656 deletions
+1
View File
@@ -7,3 +7,4 @@ Tests/Resources/Sql/Create.sql
*.exe
!WinziPrint.exe
*.sqlite3
*.zip
+145
View File
@@ -2,6 +2,151 @@
Changelog
=========
[v1.1.0.0][v1.1.0.0] (2026-07-09) {#v1.1.0.0}
---------------------------------------------
### Neue Funktionen {#v1.1.0.0-features}
* Per Opt-In Einstellung in den Stammdaten ist es nun möglich Mitgliederbewegungen und Verläufe von Geschäftsanteilen mit Elwig zu erfassen. ([#20][i20])
* Es wird nun unterstützt verschiedene Typen von Geschäftsanteilen pro Mitglied zu erfassen (normal, rot, weiß, ruhend). ([#80][i80])
### Behobene Fehler {#v1.1.0.0-bugfixes}
* Falls die Datenbank für die momentan verwndete Elwig-Version zu neu war kam es beim Überprüfen auf ein Update zu einem Fehler. (4ebe07f579)
* Im Stammdatenblatt (`MemberDataSheet`) wurde die Stamm-Zweigstelle nicht angeführt. (a71c41af5c)
### Sonstiges {#v1.1.0.0-misc}
* In der Buchungsliste (`CreditNoteData`) wird nun auch das Buchhaltungskonto der Mitglieder angeführt. (000117dc67)
* Viele kleine Verbesserungen der Code-Qualität. (u.a. fcd0555e4d, 69efca1cc3, beacba6bd9, 1169ba5101)
* Abhängigkeiten aktualisiert. (e8789aff37, 2350b55cea)
[v1.1.0.0]: https://git.necronda.net/winzer/elwig/releases/tag/v1.1.0.0
[i20]: https://git.necronda.net/winzer/elwig/issues/20
[i80]: https://git.necronda.net/winzer/elwig/issues/80
[v1.0.5.6][v1.0.5.6] (2026-06-25) {#v1.0.5.6}
---------------------------------------------
### Behobene Fehler {#v1.0.5.6-bugfixes}
* Beim Speichern von Auszahlungsvarianten wurden Einstellungen für Abwertungen nicht immer übernommen. (15de07a4c3)
* Im Stammdaten-Fenster (`BaseDataWindow`) war es nicht möglich Zweigstellen, Attribute, oder Bewirtschaftungsarten zu bearbeiten/erstellen/löschen. (1261be001c)
### Sonstiges {#v1.0.5.6-misc}
* Mandant `Seewinkel` hinzugefügt. (1ad97a78ff)
[v1.0.5.6]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.5.6
[v1.0.5.5][v1.0.5.5] (2026-06-24) {#v1.0.5.5}
---------------------------------------------
### Behobene Fehler {#v1.0.5.5-bugfixes}
* Beim Exportieren der Überweisungsdaten (EBICS) im Auszahlungsvarianten-Fenster (`PaymentVariantsWindow`) kam es zu einem Absturz. (3f65b2350b)
* Einige Einstellungen einer Auszahlungsvariante wurden vom Auszahlungsvariante-Fenster (`ChartWindow`) beim Speichern überschrieben. (f32ff945ec)
### Sonstiges {#v1.0.5.5-misc}
* Traubengutschriften mit negativem Betrag sind nun wieder möglich. (ba3f66591e)
* Abhängigkeiten aktualisiert. (4e027a9add)
[v1.0.5.5]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.5.5
[v1.0.5.4][v1.0.5.4] (2026-06-22) {#v1.0.5.4}
---------------------------------------------
### Behobene Fehler {#v1.0.5.4-bugfixes}
* Seit [v1.0.5.0](#v1.0.5.0) (2026-04-08) war es bei nicht-aktiven Mitgliedern nicht möglich Lieferungen oder Flächenbindungen anzusehen. (d0ce377d92)
### Sonstiges {#v1.0.5.4-misc}
* Bei der Installation wird nun auch der Quellcode mit ausgeliefert. (5db5876905)
* Abhängigkeiten aktualisiert. (b4ab8349b4, f8b3147c72, 7a993585c3)
[v1.0.5.4]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.5.4
[v1.0.5.3][v1.0.5.3] (2026-04-29) {#v1.0.5.3}
---------------------------------------------
### Behobene Fehler {#v1.0.5.3-bugfixes}
* Manche Anlieferungsbestätigungen und Traubengutschriften konnten nicht angezeigt werden. Dies lag an einem internen Fehler, der manchmal bei Zu-/Abschlägen auftrat. (72155fc54e, 9dfe71d6d0)
[v1.0.5.3]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.5.3
[v1.0.5.2][v1.0.5.2] (2026-04-24) {#v1.0.5.2}
---------------------------------------------
### Behobene Fehler {#v1.0.5.2-bugfixes}
* Im Lieferungen-Fenster (`DeliveryAdminWindow`) sind Attribut/Bewirtschaftungsart in der Liste für Teil-Lieferungen nicht angezeigt worden. (5c2fae1855)
### Sonstiges {#v1.0.5.2-misc}
* Abhängigkeiten aktualisiert. (2915f08a6b)
[v1.0.5.2]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.5.2
[v1.0.5.1][v1.0.5.1] (2026-04-20) {#v1.0.5.1}
---------------------------------------------
### Behobene Fehler {#v1.0.5.1-bugfixes}
* Zu-/Abschläge mit absolutem Wert (z.B. `€/kg`) wurden bei der Auszahlung nie mit der Menge multipliziert. (ea2b6db1fc)
* Massenaktionen im Lieferungen-Fenster (`DeliveryAdminWindow`) haben für Zu-/Abschläge nicht funktioniert. (4eac8cd629, cf48f005e2)
### Sonstiges {#v1.0.5.1-misc}
* Behebung von Kleinigkeiten im Layout bei Dokumenten (`CreditNote`, `PaymentVariantSummary`). (7edf395497, a852dbb242)
* Zuordnung von zwei Gemeinden vom Weinbaugebiet Neusiedlersee (`WLNS`) zu Leithaberg (`WLLB`). (2b3c293730)
* Abhängigkeiten aktualisiert. (20fd5d5826, 7861a15e2e)
[v1.0.5.1]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.5.1
[v1.0.5.0][v1.0.5.0] (2026-04-08) {#v1.0.5.0}
---------------------------------------------
### Neue Funktionen {#v1.0.5.0-features}
* Flächenbindungen werden nun in _Verträgen_ zusammengefasst um bessere Übersicht und historische Nachvollziehbarkeit zu gewährleisten. ([#77][i77])
### Sonstiges {#v1.0.5.0-misc}
* Erhebliche Verbesserung der Leistung/Geschwindigkeit. ([#79][i79], e5e5e10cd7, 22fbb0772f)
* Am Stammdatenblatt (`MemberDataSheet`) wird nun als Datum das Datum der letzten Bearbeitung angegeben. (0a9c800116)
* Abhängigkeiten aktualisiert. (07d93dd384, ce1a55df86, cc6e31a006)
[v1.0.5.0]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.5.0
[i77]: https://git.necronda.net/winzer/elwig/issues/77
[i79]: https://git.necronda.net/winzer/elwig/issues/79
[v1.0.4.1][v1.0.4.1] (2026-03-27) {#v1.0.4.1}
---------------------------------------------
+23 -21
View File
@@ -5,6 +5,7 @@ using Elwig.Helpers.Export;
using Elwig.Helpers.Printing;
using Elwig.Helpers.Weighing;
using Elwig.Models.Entities;
using Elwig.Services;
using Elwig.Windows;
using Microsoft.EntityFrameworkCore;
using System;
@@ -91,11 +92,11 @@ namespace Elwig {
try {
await AppDbUpdater.CheckDb();
} catch (Exception e) {
} catch (Exception exc) {
if (Config.UpdateUrl != null && Utils.HasInternetConnectivity()) {
await CheckForUpdates();
}
MessageBox.Show($"Invalid Database:\n\n{e.Message}", "Invalid Database", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Fehlerhafte Datenbank", "Fehlerhafte Datenbank", exc);
Shutdown();
return;
}
@@ -110,8 +111,8 @@ namespace Elwig {
.GetAwaiter().GetResult();
try {
Client = new(ctx);
} catch (Exception e) {
MessageBox.Show($"Fehler beim Laden der Mandantendaten:\n\n{e.Message}", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
} catch (Exception exc) {
InteractionService.ShowException("Fehler", "Fehler beim Laden der Mandantendaten", exc);
Shutdown();
return;
}
@@ -145,18 +146,17 @@ namespace Elwig {
foreach (var s in Config.Scales) {
try {
list.Add(Scale.FromConfig(s));
} catch (Exception e) {
} catch (Exception exc) {
list.Add(new InvalidScale(s.Id));
if (s.Required)
MessageBox.Show($"Verbindung zu Waage {s.Id} konnte nicht hergestellt werden:\n\n{e.Message}", "Waagenfehler",
MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Waagenfehler", $"Verbindung zu Waage {s.Id} konnte nicht hergestellt werden", exc);
}
}
Scales = list;
if (Config.Branch != null) {
if (!branches.ContainsKey(Config.Branch.ToLower())) {
MessageBox.Show("Ungültige Zweigstelle in Konfigurationsdatei!", "Ungültige Zweigstelle", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Ungültige Zweigstelle", "Ungültige Zweigstelle in Konfigurationsdatei!");
Shutdown();
} else {
SetBranch(branches[Config.Branch.ToLower()]);
@@ -164,7 +164,7 @@ namespace Elwig {
} else if (branches.Count == 1) {
SetBranch(branches.First().Value);
} else {
MessageBox.Show("Erkennen der lokalen Zweigstelle nicht möglich!", "Ungültige Zweigstelle", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Ungültige Zweigstelle", "Erkennen der lokalen Zweigstelle nicht möglich!");
Shutdown();
}
@@ -201,6 +201,7 @@ namespace Elwig {
BranchName = entry.Item2;
BranchPlz = entry.Item3;
BranchLocation = entry.Item4?
.Split(",")[0]
.Split(" in ")[0]
.Split(" im ")[0]
.Split(" an ")[0]
@@ -253,14 +254,13 @@ namespace Elwig {
if (Scales[i] is InvalidScale) {
try {
Scales[i] = Scale.FromConfig(s);
MessageBox.Show($"Verbindung zu Waage {s.Id} wieder hergestellt!", $"Waage {s.Id}", MessageBoxButton.OK, MessageBoxImage.Information);
} catch (Exception e) {
InteractionService.ShowInformation($"Waage {s.Id}", $"Verbindung zu Waage {s.Id} wieder hergestellt!");
} catch (Exception exc) {
Scales[i] = new InvalidScale(s.Id);
MessageBox.Show($"Verbindung zu Waage {s.Id} konnte nicht hergestellt werden:\n\n{e.Message}", "Waagenfehler",
MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Waagenfehler", $"Verbindung zu Waage {s.Id} konnte nicht hergestellt werden", exc);
}
} else if (Scales[i] is IEventScale) {
MessageBox.Show($"Verbindung zu Waage {s.Id} wieder hergestellt!", $"Waage {s.Id}", MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation($"Waage {s.Id}", $"Verbindung zu Waage {s.Id} wieder hergestellt!");
}
}
}
@@ -271,7 +271,7 @@ namespace Elwig {
for (var i = 0; i < Config.Scales.Count; i++) {
var s = Config.Scales[i];
if ((s.Connection?.StartsWith($"serial://{name}:") ?? false) && Scales[i] is not InvalidScale) {
MessageBox.Show($"Verbindung zu Waage {s.Id} unterbrochen!", $"Waagen {s.Id}", MessageBoxButton.OK, MessageBoxImage.Warning);
InteractionService.ShowWarning($"Waagen {s.Id}", $"Verbindung zu Waage {s.Id} unterbrochen!");
if (Scales[i] is ICommandScale) {
try {
Scales[i].Dispose();
@@ -306,11 +306,9 @@ namespace Elwig {
});
} else if (showAlert) {
if (latest == null) {
MessageBox.Show("Informationen konnten nicht abgerufen werden!", "Nach Updates suchen",
MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Nach Updates suchen", "Informationen konnten nicht abgerufen werden!");
} else {
MessageBox.Show($"Elwig ist auf dem aktuellsten Stand! (Version: {latest.Value.Version})", "Nach Updates suchen",
MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Nach Updates suchen", $"Elwig ist auf dem aktuellsten Stand! (Version: {latest.Value.Version})");
}
}
}
@@ -320,11 +318,11 @@ namespace Elwig {
await Task.Run(async () => {
await Database.Import(filename);
});
MessageBox.Show("Das Ersetzen war erfolgreich!\n\nBitte starten Sie Elwig neu!", "Datenbank ersetzen", MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Datenbank ersetzen", "Das Ersetzen war erfolgreich!\n\nBitte starten Sie Elwig neu!");
ForceShutdown = true;
Current.Shutdown();
} catch (Exception exc) {
MessageBox.Show("Fehler beim Ersetzen:\n\n" + exc.Message, "Datenbank ersetzen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Datenbank ersetzen", "Fehler beim Ersetzen", exc);
}
}
@@ -346,6 +344,10 @@ namespace Elwig {
return FocusWindow<DeliveryAdminWindow>(() => new(true), w => w.ViewModel.IsReceipt);
}
public static MemberBusinessSharesAdminWindow FocusMemberBusinessSharesWindow(int mgnr) {
return FocusWindow<MemberBusinessSharesAdminWindow>(() => new(mgnr), w => w.ViewModel.FilterMember?.MgNr == mgnr);
}
public static DeliveryAdminWindow FocusMemberDeliveries(int mgnr) {
return FocusWindow<DeliveryAdminWindow>(() => new(mgnr), w => w.ViewModel.FilterMember?.MgNr == mgnr);
}
+1 -1
View File
@@ -122,7 +122,7 @@ namespace Elwig.Controls {
private void OnSelectionChanged(object sender, SelectionChangedEventArgs evt) {
SelectItemsReverse();
var dmp = !string.IsNullOrEmpty(ListDisplayMemberPath) ? ListDisplayMemberPath : !string.IsNullOrEmpty(DisplayMemberPath) ? DisplayMemberPath : null;
var dmp = !string.IsNullOrWhiteSpace(ListDisplayMemberPath) ? ListDisplayMemberPath : !string.IsNullOrWhiteSpace(DisplayMemberPath) ? DisplayMemberPath : null;
if (SelectedItems.Count == ItemsSource.Cast<object>().Count() && AllItemsSelectedContent != null) {
AllItemsSelected = true;
} else if (SelectedItems.Count == 0) {
+2 -1
View File
@@ -1,4 +1,5 @@
using Elwig.Helpers;
using Elwig.Services;
using System;
using System.Diagnostics;
using System.IO;
@@ -51,7 +52,7 @@ namespace Elwig.Dialogs {
File.Delete(fileName);
return;
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
Process.Start(fileName);
DialogResult = true;
+18 -4
View File
@@ -18,6 +18,7 @@ namespace Elwig.Documents {
public class BusinessDocument : Document {
public Member Member;
public MemberHistoryPoint MemberShares;
public string? Location;
public bool IncludeSender = false;
public bool UseBillingAddress = false;
@@ -51,6 +52,7 @@ namespace Elwig.Documents {
public BusinessDocument(string title, Member m, DateOnly? dateFrom, bool includeSender = false) :
base(title) {
Member = m;
MemberShares = new(m.Shares, m.SharesRed, m.SharesWhite, m.SharesDormant);
Location = App.BranchLocation;
IncludeSender = includeSender;
DateFrom = dateFrom;
@@ -263,7 +265,7 @@ namespace Elwig.Documents {
}
protected Table NewBucketTable(
Season season, Dictionary<string, MemberBucket> buckets, int deliveredWeight,
Season season, Dictionary<string, MemberBucket> buckets, int deliveredWeightRed, int deliveredWeightWhite,
bool includeDelivery = true, bool includePayment = false,
bool isTiny = false, IEnumerable<string>? filter = null
) {
@@ -316,9 +318,21 @@ namespace Elwig.Documents {
.Where(b => !fbVars.Contains(b.Key))
.OrderBy(b => b.Value.Name);
tbl.AddCell(NewBucketTh("Gesamtlieferung lt. gez. GA", isTiny: isTiny));
tbl.AddCells(FormatRow(Member.BusinessShares * season.MinKgPerBusinessShare, Member.BusinessShares * season.MaxKgPerBusinessShare,
deliveredWeight, isGa: true, showPayment: includePayment, showArea: !includeDelivery, isTiny: isTiny));
if (MemberShares.Shares != 0 || (MemberShares.SharesRed == 0 && MemberShares.SharesWhite == 0)) {
tbl.AddCell(NewBucketTh("Gesamtlieferung lt. gez. GA", isTiny: isTiny));
tbl.AddCells(FormatRow(MemberShares.Shares * (season.MinKgPerShare ?? 0), MemberShares.Shares * (season.MaxKgPerShare ?? 0),
deliveredWeightRed + deliveredWeightWhite, isGa: true, showPayment: includePayment, showArea: !includeDelivery, isTiny: isTiny));
}
if (MemberShares.SharesRed != 0 || MemberShares.SharesWhite != 0) {
tbl.AddCell(NewBucketTh("Gesamtlieferung lt. gez. GA (rot)", isTiny: isTiny));
tbl.AddCells(FormatRow(MemberShares.SharesRed * (season.MinKgPerShareRed ?? season.MinKgPerShare ?? 0), MemberShares.SharesRed * (season.MaxKgPerShareRed ?? season.MaxKgPerShare ?? 0),
deliveredWeightRed, isGa: true, showPayment: includePayment, showArea: !includeDelivery, isTiny: isTiny));
tbl.AddCell(NewBucketTh("Gesamtlieferung lt. gez. GA (weiß)", isTiny: isTiny));
tbl.AddCells(FormatRow(MemberShares.SharesWhite * (season.MinKgPerShareWhite ?? season.MinKgPerShare ?? 0), MemberShares.SharesWhite * (season.MaxKgPerShareWhite ?? season.MaxKgPerShare ?? 0),
deliveredWeightWhite, isGa: true, showPayment: includePayment, showArea: !includeDelivery, isTiny: isTiny));
}
if (fbs.Any()) {
tbl.AddCell(NewBucketSubHdr("Flächenbindungen" + (vtr.Any() ? " (inkl. Verträge)" : "") + ":", includePayment ? 8 : 7, isTiny: isTiny));
+23 -15
View File
@@ -86,19 +86,24 @@ namespace Elwig.Documents {
}
if (ConsiderTotalPenalty) {
var total = _data.Rows.SelectMany(r => r.Buckets).Sum(b => b.Value);
var totalUnderDelivery = total - Member.BusinessShares * season.MinKgPerBusinessShare;
MemberTotalUnderDelivery = totalUnderDelivery < 0 ? totalUnderDelivery * (season.PenaltyPerKg ?? 0) - (season.PenaltyAmount ?? 0) - (season.PenaltyPerBsAmount * Math.Floor(-(decimal)totalUnderDelivery / season.MinKgPerBusinessShare) ?? 0) : 0;
var weights = _data.Rows.Select(r => new {r.Type, Weight = r.Buckets.Sum(b => b.Value)}).GroupBy(r => r.Type).ToDictionary(r => r.Key, g => g.Sum(r => r.Weight));
var red = weights.GetValueOrDefault("R", 0);
var white = weights.GetValueOrDefault("W", 0);
var total = red + white;
var underDeliveryTotal = total - Member.Shares * (season.MinKgPerShare ?? 0);
var underDeliveryRed = red - Member.SharesRed * (season.MinKgPerShareRed ?? season.MinKgPerShare ?? 0);
var underDeliveryWhite = white - Member.SharesWhite * (season.MinKgPerShareWhite ?? season.MinKgPerShare ?? 0);
MemberTotalUnderDelivery =
(underDeliveryTotal < 0 ? underDeliveryTotal * (season.PenaltyPerKg ?? 0) - (season.PenaltyAmount ?? 0) - (season.PenaltyPerShareAmount * Math.Floor(-(decimal)underDeliveryTotal / (season.MinKgPerShare ?? 0)) ?? 0) : 0) +
(underDeliveryRed < 0 ? underDeliveryRed * (season.PenaltyPerKg ?? 0) - (season.PenaltyAmount ?? 0) - (season.PenaltyPerShareAmount * Math.Floor(-(decimal)underDeliveryRed / (season.MinKgPerShareRed ?? season.MinKgPerShare ?? 0)) ?? 0) : 0) +
(underDeliveryWhite < 0 ? underDeliveryWhite * (season.PenaltyPerKg ?? 0) - (season.PenaltyAmount ?? 0) - (season.PenaltyPerShareAmount * Math.Floor(-(decimal)underDeliveryWhite / (season.MinKgPerShareWhite ?? season.MinKgPerShare ?? 0)) ?? 0) : 0);
if (total == 0)
MemberTotalUnderDelivery -= (season.PenaltyNone ?? 0) + (season.PenaltyPerBsNone * Member.BusinessShares ?? 0);
MemberTotalUnderDelivery -= (season.PenaltyNone ?? 0) + (season.PenaltyPerShareNone * (Member.Shares + Member.SharesRed + Member.SharesWhite) ?? 0);
}
if (ConsiderAutoBusinessShares) {
var fromDate = $"{season.Year}-01-01";
var toDate = $"{season.Year}-12-31";
MemberAutoBusinessShares = await ctx.MemberHistory
.Where(h => h.MgNr == Member.MgNr && h.Type == "auto")
.Where(h => h.DateString.CompareTo(fromDate) >= 0 && h.DateString.CompareTo(toDate) <= 0)
.SumAsync(h => h.BusinessShares);
.Where(h => h.ToMgNr == Member.MgNr && h.DeductYear == season.Year)
.SumAsync(h => h.Shares);
MemberAutoBusinessSharesAmount = MemberAutoBusinessShares * (-season.BusinessShareValue ?? 0);
}
if (ConsiderContractPenalties) {
@@ -197,7 +202,7 @@ namespace Elwig.Documents {
penalty += MemberTotalUnderDelivery;
}
if (MemberAutoBusinessSharesAmount != 0) {
tbl2.AddCells(FormatRow($"Autom. Nachz. von GA ({MemberAutoBusinessShares})", MemberAutoBusinessSharesAmount, add: true));
tbl2.AddCells(FormatRow($"Nachzeichnung von GA ({MemberAutoBusinessShares})", MemberAutoBusinessSharesAmount, add: true));
penalty += MemberAutoBusinessSharesAmount;
}
if (CustomPayment?.Amount != null) {
@@ -296,11 +301,14 @@ namespace Elwig.Documents {
if (p.QualId == "WEI") varibute.Add(Italic("abgew."));
sub.AddCell(NewCell(colspan: 2))
.AddCell(NewTd(varibute, colspan: 3).SetPaddingTop(0));
} else if (i - (rows - p.Modifiers.Length) < p.Modifiers.Length) {
sub.AddCell(NewCell(colspan: 2))
.AddCell(NewTd(p.Modifiers[i - (rows - p.Modifiers.Length)], 8, colspan: 3).SetPaddingTop(0).SetPaddingLeftMM(5));
} else {
sub.AddCell(NewCell(colspan: 5));
var idx = i - (rows - p.Modifiers.Length);
if (idx >= 0 && idx < p.Modifiers.Length) {
sub.AddCell(NewCell(colspan: 2))
.AddCell(NewTd(p.Modifiers[idx], 8, colspan: 3).SetPaddingTop(0).SetPaddingLeftMM(5));
} else {
sub.AddCell(NewCell(colspan: 5));
}
}
if (i < p.Buckets.Length) {
@@ -321,7 +329,7 @@ namespace Elwig.Documents {
var totalMod = p.TotalModifiers ?? 0;
var pad = i == 0 ? 0.5f : 0;
sub.AddCell(NewTd(rebelMod == 0 ? "-" : (Utils.GetSign(rebelMod) + $"{Math.Abs(rebelMod):0.0##}"), 6, center: true)
.SetPaddingTopMM(pad))
.SetPaddingTopMM(pad).SetPaddingLeft(0).SetPaddingRight(0))
.AddCell(NewTd(totalMod == 0 ? "-" : Utils.GetSign(totalMod) + $"{Math.Abs(totalMod):N2}", center: totalMod == 0, right: true)
.SetPaddingTopMM(pad))
.AddCell(NewTd($"{p.Amount:N2}", right: true)
+18 -11
View File
@@ -18,7 +18,8 @@ namespace Elwig.Documents {
private readonly int _year;
public Season? Season;
public int MemberDeliveredWeight;
public int MemberDeliveredWeightRed;
public int MemberDeliveredWeightWhite;
public DeliveryConfirmationDeliveryData? Data;
public string? Text = App.Client.TextDeliveryConfirmation;
public Dictionary<string, MemberBucket> MemberBuckets = [];
@@ -36,10 +37,14 @@ namespace Elwig.Documents {
protected override async Task LoadData(AppDbContext ctx) {
await base.LoadData(ctx);
Season = await ctx.FetchSeasons(_year).SingleOrDefaultAsync() ?? throw new ArgumentException("Invalid season");
MemberDeliveredWeight = await ctx.Deliveries
var weights = await ctx.Deliveries
.Where(d => d.Year == Season.Year && d.MgNr == Member.MgNr)
.SelectMany(d => d.Parts)
.SumAsync(p => p.Weight);
.GroupBy(p => p.Variety.Type)
.ToDictionaryAsync(g => g.Key, g => g.Sum(p => p.Weight));
MemberDeliveredWeightRed = weights.GetValueOrDefault("R", 0);
MemberDeliveredWeightWhite = weights.GetValueOrDefault("W", 0);
MemberShares = await ctx.GetMemberHistory(Season.Year, Member.MgNr);
MemberBuckets = await ctx.GetMemberBuckets(Season.Year, Member.MgNr);
MemberStats = await AppDbContext.GetMemberStats(Season.Year, Member.MgNr);
Data ??= await DeliveryConfirmationDeliveryData.ForMember(ctx.DeliveryParts, Season.Year, Member);
@@ -63,7 +68,7 @@ namespace Elwig.Documents {
doc.Add(NewDeliveryListTable(Data));
doc.Add(NewWeightsTable(MemberStats)
.SetMarginTopMM(10).SetKeepTogether(true));
doc.Add(NewBucketTable(Season, MemberBuckets, MemberDeliveredWeight, includePayment: true)
doc.Add(NewBucketTable(Season, MemberBuckets, MemberDeliveredWeightRed, MemberDeliveredWeightWhite, includePayment: true)
.SetMarginTopMM(10).SetKeepTogether(true));
if (Text != null) {
@@ -114,13 +119,14 @@ namespace Elwig.Documents {
} else if (i == 1 && attr) {
sub.AddCell(NewCell(colspan: 2))
.AddCell(NewTd($"{p.Attribute}{(p.Attribute != null && p.Cultivation != null ? " / " : "")}{p.Cultivation}", 8, colspan: 2)
.SetPaddingsMM(0.125f, 1, 0.125f, 1))
.SetPaddingTop(0))
.AddCell(NewCell(colspan: 2));
} else {
sub.AddCell(NewCell(colspan: 2));
if (i - (rows - p.Modifiers.Length) < p.Modifiers.Length) {
sub.AddCell(NewTd(p.Modifiers[i - (rows - p.Modifiers.Length)], 8, colspan: 2)
.SetPaddingsMM(0.125f, 0, 0.125f, 5));
var idx = i - (rows - p.Modifiers.Length);
if (idx >= 0 && idx < p.Modifiers.Length) {
sub.AddCell(NewTd(p.Modifiers[idx], 8, colspan: 2)
.SetPaddingTop(0).SetPaddingLeftMM(5));
} else {
sub.AddCell(NewCell(colspan: 2));
}
@@ -129,14 +135,15 @@ namespace Elwig.Documents {
if (i < p.Buckets.Length) {
var bucket = p.Buckets[i];
sub.AddCell(NewTd($"{bucket.Name}:", 8).SetHeight(10).SetPaddingsMM(0.125f, 0, 0.125f, 0));
sub.AddCell(NewTd($"{bucket.Value:N0}", right: true));
var pad = i == 0 ? 0.5f : 0;
sub.AddCell(NewTd($"{bucket.Name}:", 8).SetHeight(10).SetPaddingTopMM(pad));
sub.AddCell(NewTd($"{bucket.Value:N0}", right: true).SetPaddingTopMM(pad));
} else {
sub.AddCell(NewCell(colspan: 2));
}
if (i == p.Buckets.Length - 1) {
sub.AddCell(NewTd($"{p.Weight:N0}", right: true));
sub.AddCell(NewTd($"{p.Weight:N0}", right: true).SetPaddingTop(0));
sub.AddCell(NewTd(p.IsNetWeight ? "\u2611" : "\u2610", 7, right: true).SetFont(SF).SetPadding(0));
} else {
sub.AddCell(NewCell(colspan: 2));
+9 -4
View File
@@ -19,7 +19,8 @@ namespace Elwig.Documents {
public Delivery Delivery;
public string? Text;
public int MemberDeliveredWeight;
public int MemberDeliveredWeightRed;
public int MemberDeliveredWeightWhite;
public Dictionary<string, MemberBucket> MemberBuckets = [];
// 0 - none
@@ -59,9 +60,13 @@ namespace Elwig.Documents {
protected override async Task LoadData(AppDbContext ctx) {
await base.LoadData(ctx);
MemberDeliveredWeight = await ctx.DeliveryParts
var weights = await ctx.DeliveryParts
.Where(d => d.Year == Delivery.Year && d.Delivery.MgNr == Member.MgNr)
.SumAsync(p => p.Weight);
.GroupBy(p => p.Variety.Type)
.ToDictionaryAsync(g => g.Key, g => g.Sum(p => p.Weight));
MemberDeliveredWeightRed = weights.GetValueOrDefault("R", 0);
MemberDeliveredWeightWhite = weights.GetValueOrDefault("W", 0);
MemberShares = await ctx.GetMemberHistory(Delivery.Year, Member.MgNr);
MemberBuckets = await ctx.GetMemberBuckets(Delivery.Year, Member.MgNr) ?? [];
}
@@ -81,7 +86,7 @@ namespace Elwig.Documents {
doc.Add(new KernedParagraph($"Anmerkung zur Lieferung: {Delivery.Comment}", 10).SetMarginsMM(5, 0, 0, 0));
}
if (DisplayStats > 0) {
doc.Add(NewBucketTable(Delivery.Season, MemberBuckets, MemberDeliveredWeight, isTiny: true,
doc.Add(NewBucketTable(Delivery.Season, MemberBuckets, MemberDeliveredWeightRed, MemberDeliveredWeightWhite, isTiny: true,
filter: DisplayStats > 2 ? null : DisplayStats == 1 ? [] : Delivery.Parts.Select(p => p.SortId).Distinct().ToList())
.SetKeepTogether(true)
.SetMarginsMM(5, 0, 0, 0));
+170 -11
View File
@@ -1,4 +1,5 @@
using Elwig.Helpers;
using Elwig.Models;
using Elwig.Models.Entities;
using iText.Kernel.Colors;
using iText.Kernel.Pdf;
@@ -18,7 +19,9 @@ namespace Elwig.Documents {
public new static string Name => "Stammdatenblatt";
public Season? Season;
public int MemberDeliveredWeight;
public int MemberDeliveredWeightRed;
public int MemberDeliveredWeightWhite;
public List<MemberHistory> MemberHistory = [];
public Dictionary<string, MemberBucket> MemberBuckets = [];
public List<AreaCom> ActiveAreaCommitments = [];
@@ -30,36 +33,52 @@ namespace Elwig.Documents {
public static async Task<MemberDataSheet> Initialize(int mgnr) {
using var ctx = new AppDbContext();
return new MemberDataSheet(await ctx.FetchMembers(mgnr, true, true).SingleAsync());
return new MemberDataSheet(await ctx.FetchMembers(mgnr, includeContactInfo: true).SingleAsync());
}
protected override async Task LoadData(AppDbContext ctx) {
await base.LoadData(ctx);
Season = await ctx.FetchSeasons().FirstOrDefaultAsync() ?? throw new ArgumentException("Invalid season");
MemberHistory = await ctx.MemberHistory
.Where(h => h.FromMgNr == Member.MgNr || h.ToMgNr == Member.MgNr)
.OrderBy(h => h.DateNoticeString).ThenBy(h => h.HistNr)
.ToListAsync();
MemberBuckets = await ctx.GetMemberBuckets(Utils.CurrentYear, Member.MgNr);
ActiveAreaCommitments = await Member.ActiveAreaCommitments(ctx)
.Include(c => c.Contract).ThenInclude(c => c.Revisions)
.ToListAsync();
MemberDeliveredWeight = await ctx.Deliveries
var weights = await ctx.Deliveries
.Where(d => d.Year == Season.Year && d.MgNr == Member.MgNr)
.SelectMany(d => d.Parts)
.SumAsync(p => p.Weight);
.GroupBy(p => p.Variety.Type)
.ToDictionaryAsync(g => g.Key, g => g.Sum(p => p.Weight));
MemberDeliveredWeightRed = weights.GetValueOrDefault("R", 0);
MemberDeliveredWeightWhite = weights.GetValueOrDefault("W", 0);
}
protected override void RenderBody(iText.Layout.Document doc, PdfDocument pdf) {
if (Season == null) throw new Exception("Call LoadData before RenderBody");
base.RenderBody(doc, pdf);
doc.Add(NewMemberData(Season).SetMarginBottomMM(5));
doc.Add(NewBucketTable(Season, MemberBuckets, MemberDeliveredWeight, includeDelivery: false));
doc.Add(NewBucketTable(Season, MemberBuckets, MemberDeliveredWeightRed, MemberDeliveredWeightWhite, includeDelivery: false));
if (ActiveAreaCommitments.Count != 0) {
bool firstOnPage = false;
if (pdf.GetNumberOfPages() == 1) {
doc.Add(new AreaBreak(AreaBreakType.NEXT_PAGE));
firstOnPage = true;
}
doc.Add(new KernedParagraph(12).Add(Bold($"Flächenbindungen per {Date:dd.MM.yyyy}")).SetMargins(firstOnPage ? 0 : 24, 0, 12, 0));
doc.Add(new KernedParagraph(12).Add(Bold($"Flächenbindungen per {Date:dd.MM.yyyy}")).SetMargins(firstOnPage ? 0 : 24, 0, 6, 0));
doc.Add(NewAreaComTable(ActiveAreaCommitments));
}
if (App.Client.EnableMemberHistory) {
bool firstOnPage = false;
if (pdf.GetNumberOfPages() == 1) {
doc.Add(new AreaBreak(AreaBreakType.NEXT_PAGE));
firstOnPage = true;
}
doc.Add(new KernedParagraph(12).Add(Bold($"Verlauf der Geschäftsanteile per {Date:dd.MM.yyyy}")).SetMargins(firstOnPage ? 0 : 24, 0, 6, 0));
doc.Add(NewMemberBusinessSharesTable(MemberHistory));
}
}
protected Cell NewDataHdr(string title, int colspan) {
@@ -132,6 +151,9 @@ namespace Elwig.Documents {
.AddCell(NewTd(i < subTbl2.Count ? subTbl2[i][1] : "", colspan: 2));
}
var shares = (Member.Shares != 0 || (Member.SharesRed == 0 && Member.SharesWhite == 0) ? $"{Member.Shares:N0}" : "") +
(Member.SharesRed != 0 || Member.SharesWhite != 0 ? (Member.Shares != 0 ? " / " : "") + $"{Member.SharesRed:N0} (rot) / {Member.SharesWhite:N0} (weiß)" : "") +
(Member.SharesDormant != 0 ? $" / {Member.SharesDormant:N0} (ruhend)" : "");
tbl.AddCell(NewDataHdr("Betrieb", colspan: 6))
.AddCell(NewDataTh("Betriebs-Nr.:")).AddCell(NewTd(Member.LfbisNr))
.AddCell(NewDataTh("UID:", colspan: 2)).AddCell(NewTd(Member.UstIdNr, colspan: 2))
@@ -143,7 +165,7 @@ namespace Elwig.Documents {
.AddCell(NewDataHdr("Genossenschaft", colspan: 6))
.AddCell(NewDataTh("Status:")).AddCell(NewTd(new KernedParagraph(Member.IsActive ? "Aktiv " : "Nicht aktiv ", 10)
.Add(Normal("(" + (Member.ExitDate != null ? $"{Member.EntryDate:dd.MM.yyyy}\u2013{Member.ExitDate:dd.MM.yyyy}" : $"seit {Member.EntryDate:dd.MM.yyyy}") + ")", 8))))
.AddCell(NewDataTh("Geschäftsanteile:", colspan: 2)).AddCell(NewTd($"{Member.BusinessShares:N0}", colspan: 2))
.AddCell(NewDataTh("Geschäftsanteile:", colspan: 2)).AddCell(NewTd(shares, colspan: 2))
.AddCell(NewDataTh("Stamm-Zweigstelle:")).AddCell(NewTd(Member.Branch?.Name))
.AddCell(NewDataTh("Volllieferant:", colspan: 2)).AddCell(NewTd(Member.IsVollLieferant ? "Ja" : "Nein", colspan: 2))
.AddCell(NewDataTh("Zusendungen per\u2026")).AddCell(NewTd(new KernedParagraph(10)
@@ -162,10 +184,10 @@ namespace Elwig.Documents {
}).OrderByDescending(a => a.Size).ToList();
var tbl = new Table(ColsMM(40, 30, 35, 15, 25, 20), true)
.SetWidth(UnitValue.CreatePercentValue(100)).SetFixedLayout()
.SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE)
.SetBorder(Border.NO_BORDER)
.SetFontSize(10);
.SetWidth(UnitValue.CreatePercentValue(100)).SetFixedLayout()
.SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE)
.SetBorder(Border.NO_BORDER)
.SetFontSize(10);
tbl.AddHeaderCell(NewTh("Katastralgemeinde", rowspan: 2, left: true))
.AddHeaderCell(NewTh("Ried", rowspan: 2, left: true))
@@ -202,5 +224,142 @@ namespace Elwig.Documents {
return tbl;
}
protected Cell[] NewBusinessSharesCells(MemberHistoryPoint p, bool isDiff = true) {
if (App.Client.HasRedWhite) {
return [
NewTd(isDiff ? (p.SharesRed == 0 ? "" : p.SharesRed < 0 ? $"{-p.SharesRed:N0}" : $"+{p.SharesRed:N0}") : $"{p.SharesRed:N0}",
right: true, bold: !isDiff, borderTop: !isDiff),
NewTd(isDiff ? (p.SharesWhite == 0 ? "" : p.SharesWhite < 0 ? $"{-p.SharesWhite:N0}" : $"+{p.SharesWhite:N0}") : $"{p.SharesWhite:N0}",
right: true, bold: !isDiff, borderTop: !isDiff),
NewTd(isDiff ? (p.SharesDormant == 0 ? "" : p.SharesDormant < 0 ? $"{-p.SharesDormant:N0}" : $"+{p.SharesDormant:N0}") : $"{p.SharesDormant:N0}",
right: true, bold: !isDiff, borderTop: !isDiff),
];
} else {
return [
NewTd(isDiff ? (p.Shares == 0 ? "" : p.Shares < 0 ? $"{-p.Shares:N0}" : $"+{p.Shares:N0}") : $"{p.Shares:N0}",
right: true, bold: !isDiff, borderTop: !isDiff),
NewTd(isDiff ? (p.SharesDormant == 0 ? "" : p.SharesDormant < 0 ? $"{-p.SharesDormant:N0}" : $"+{p.SharesDormant:N0}") : $"{p.SharesDormant:N0}",
right : true, bold: !isDiff, borderTop: !isDiff),
];
}
}
protected Table NewMemberBusinessSharesTable(IEnumerable<MemberHistory> history) {
var tbl = new Table(ColsMM(18, 93, App.Client.HasRedWhite ? 18 : 12, App.Client.HasRedWhite ? 12 : 18, 12, 12), true)
.SetWidth(UnitValue.CreatePercentValue(100)).SetFixedLayout()
.SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE)
.SetBorder(Border.NO_BORDER)
.SetFontSize(10);
var tw = App.Client.HasRedWhite ? 1 : 2;
tbl.AddHeaderCell(NewTh("Meldung am", rowspan: 2))
.AddHeaderCell(NewTh("Text", rowspan: 2, colspan: tw, left: true))
.AddHeaderCell(NewTh("Wirksam ab", rowspan: 2))
.AddHeaderCell(NewTh("Geschäftsanteile", colspan: App.Client.HasRedWhite ? 3 : 2));
if (App.Client.HasRedWhite) {
tbl.AddHeaderCell(NewTh("rot"))
.AddHeaderCell(NewTh("weiß"))
.AddHeaderCell(NewTh("ruhend"));
} else {
tbl.AddHeaderCell(NewTh("normal"))
.AddHeaderCell(NewTh("ruhend"));
}
if (!history.Any() || Member.EntryDate <= history.First().DateNotice) {
tbl.AddCell(NewTd($"{Member.EntryDate:dd.MM.yyyy}"))
.AddCell(NewTd("Eintritt", colspan: tw, bold: true))
.AddCell(NewTd($"{Member.EntryDate:dd.MM.yyyy}"))
.AddCells(NewBusinessSharesCells(new()));
}
var recorded = history.Aggregate(new MemberHistoryPoint(), (s, h) => new(
s.Shares + (h.ToMgNr == Member.MgNr && h.ToType == BusinessShareType.Normal ? h.Shares : 0) - (h.FromMgNr == Member.MgNr && h.FromType == BusinessShareType.Normal ? h.Shares : 0),
s.SharesRed + (h.ToMgNr == Member.MgNr && h.ToType == BusinessShareType.Red ? h.Shares : 0) - (h.FromMgNr == Member.MgNr && h.FromType == BusinessShareType.Red ? h.Shares : 0),
s.SharesWhite + (h.ToMgNr == Member.MgNr && h.ToType == BusinessShareType.White ? h.Shares : 0) - (h.FromMgNr == Member.MgNr && h.FromType == BusinessShareType.White ? h.Shares : 0),
s.SharesDormant + (h.ToMgNr == Member.MgNr && h.ToType == BusinessShareType.Dormant ? h.Shares : 0) - (h.FromMgNr == Member.MgNr && h.FromType == BusinessShareType.Dormant ? h.Shares : 0)));
var cur = new MemberHistoryPoint(Member.Shares - recorded.Shares, Member.SharesRed - recorded.SharesRed, Member.SharesWhite - recorded.SharesWhite, Member.SharesDormant - recorded.SharesDormant);
if (cur.Shares != 0 || cur.SharesRed != 0 || cur.SharesWhite != 0 || cur.SharesDormant != 0) {
tbl.AddCell(NewTd())
.AddCell(NewTd("Nicht in Software erfasst", colspan: tw, italic: true))
.AddCell(NewTd())
.AddCells(NewBusinessSharesCells(cur));
if (history.Any()) {
tbl.AddCell(NewTd(borderTop: true))
.AddCell(NewTd($"Bis Lese {history.First().DateNotice.Year - 1}", borderTop: true, colspan: tw, bold: true))
.AddCell(NewTd(borderTop: true))
.AddCells(NewBusinessSharesCells(cur, isDiff: false));
}
}
DateOnly? lastDate = null;
foreach (var row in history) {
int s = 0, r = 0, w = 0, d = 0;
if (row.FromMgNr == Member.MgNr) {
switch (row.FromType) {
case BusinessShareType.Normal: s -= row.Shares; break;
case BusinessShareType.Red: r -= row.Shares; break;
case BusinessShareType.White: w -= row.Shares; break;
case BusinessShareType.Dormant: d -= row.Shares; break;
}
}
if (row.ToMgNr == Member.MgNr) {
switch (row.ToType) {
case BusinessShareType.Normal: s += row.Shares; break;
case BusinessShareType.Red: r += row.Shares; break;
case BusinessShareType.White: w += row.Shares; break;
case BusinessShareType.Dormant: d += row.Shares; break;
}
}
if (lastDate < Member.EntryDate && row.DateNotice <= Member.EntryDate) {
tbl.AddCell(NewTd($"{Member.EntryDate:dd.MM.yyyy}"))
.AddCell(NewTd("Eintritt", colspan: tw, bold: true))
.AddCell(NewTd($"{Member.EntryDate:dd.MM.yyyy}"))
.AddCells(NewBusinessSharesCells(new()));
}
if (lastDate != null && row.DateNotice.Year != lastDate?.Year) {
tbl.AddCell(NewTd(borderTop: true))
.AddCell(NewTd(lastDate?.Year == row.DateNotice.Year - 1 ? $"Lese {lastDate?.Year}" : $"Lese {lastDate?.Year}{row.DateNotice.Year - 1}", borderTop: true, colspan: tw, bold: true))
.AddCell(NewTd(borderTop: true))
.AddCells(NewBusinessSharesCells(cur, isDiff: false));
}
if (lastDate < Member.ExitDate && row.DateNotice <= Member.ExitDate) {
tbl.AddCell(NewTd($"{Member.ExitDate:dd.MM.yyyy}"))
.AddCell(NewTd("Austritt", colspan: tw, bold: true))
.AddCell(NewTd($"31.12.{Member.ExitDate.Value.Year + 1}")) // FIXME
.AddCells(NewBusinessSharesCells(new()));
}
cur = new(cur.Shares + s, cur.SharesRed + r, cur.SharesWhite + w, cur.SharesDormant + d);
tbl.AddCell(NewTd($"{row.DateNotice:dd.MM.yyyy}"))
.AddCell(NewTd(row.FormatReasonPhrase(Member.MgNr), colspan: tw))
.AddCell(NewTd($"{row.DateEffective:dd.MM.yyyy}"))
.AddCells(NewBusinessSharesCells(new(s, r, w, d)));
lastDate = row.DateNotice;
}
if (Member.EntryDate != null && history.Any() && Member.EntryDate > lastDate) {
tbl.AddCell(NewTd($"{Member.EntryDate:dd.MM.yyyy}"))
.AddCell(NewTd("Eintritt", colspan: tw, bold: true))
.AddCell(NewTd($"{Member.EntryDate:dd.MM.yyyy}"))
.AddCells(NewBusinessSharesCells(new()));
}
if (Member.ExitDate != null && (!history.Any() || Member.ExitDate > lastDate)) {
tbl.AddCell(NewTd($"{Member.ExitDate:dd.MM.yyyy}"))
.AddCell(NewTd("Austritt", colspan: tw, bold: true))
.AddCell(NewTd($"31.12.{Member.ExitDate.Value.Year + 1}")) // FIXME
.AddCells(NewBusinessSharesCells(new()));
}
tbl.AddCell(NewTd(borderTop: true))
.AddCell(NewTd(lastDate == null ? "Aktuell" : $"Ab Lese {lastDate?.Year}", borderTop: true, colspan: tw, bold: true))
.AddCell(NewTd(borderTop: true))
.AddCells(NewBusinessSharesCells(cur, isDiff: false));
return tbl;
}
}
}
+1 -1
View File
@@ -86,7 +86,7 @@ namespace Elwig.Documents {
.AddCell(NewTd($"{m.Plz}", 8))
.AddCell(NewTd(m.Locality, 6))
.AddCell(NewTd(m.LfbisNr ?? "", 8))
.AddCell(NewTd($"{m.BusinessShares:N0}", 8, right: true).SetPaddingLeft(0).SetPaddingRight(0))
.AddCell(NewTd($"{m.SharesActive:N0}", 8, right: true).SetPaddingLeft(0).SetPaddingRight(0))
.AddCell(NewTd(m.DefaultKg ?? "", 6));
if (AreaComFilters.Length > 0) {
foreach (var v in AreaComFilters) {
+4 -4
View File
@@ -139,7 +139,7 @@ namespace Elwig.Documents {
.AddCell(NewSectionTh("Datum/Überw.:", overflow: true))
.AddCell(NewTd($"{Variant.Date:dd.MM.yyyy} / {Variant.TransferDate:dd.MM.yyyy}", colspan: 4, center: true))
.AddCell(NewSectionTh("Automatische Nachzeichnung der GA:", colspan: 2, borderLeft: true))
.AddCell(NewSectionTh("(Automatische) Nachzeichnung von GA:", colspan: 2, borderLeft: true))
.AddCell(NewTd(BillingData.ConsiderAutoBusinessShares ? "Ja" : "Nein", center: true))
.AddCell(NewSectionTh("Berechnung:"))
@@ -183,21 +183,21 @@ namespace Elwig.Documents {
.AddCell(NewTd(CurrencySymbol))
.AddCell(NewTd($"{Math.Abs(payed):N2}", right: true))
.AddCell(NewSectionTh("Preis (abgewertet):", borderTop: true, borderLeft: true))
.AddCell(NewTd((minWei != maxWei ? $"{minWei:N4}\u2013{maxWei:N4}" : $"{minWei:N4}") + $" {CurrencySymbol}/kg", colspan: 2, center: true, borderTop: true))
.AddCell(NewTd(minWei == null && maxWei == null ? "-" : (minWei != maxWei ? $"{minWei:N4}\u2013{maxWei:N4}" : $"{minWei:N4}") + $" {CurrencySymbol}/kg", colspan: 2, center: true, borderTop: true))
.AddCell(NewSectionTh("Nettosumme:", colspan: 2))
.AddCell(NewTd(borderTop: true))
.AddCell(NewTd(CurrencySymbol, borderTop: true))
.AddCell(NewTd($"{netSum:N2}", right: true, borderTop: true))
.AddCell(NewSectionTh("Preis (ungeb., nicht abgew.):", borderLeft: true))
.AddCell(NewTd((minPrice != maxPrice ? $"{minPrice:N4}{maxPrice:N4}" : $"{minPrice:N4}") + $" {CurrencySymbol}/kg", colspan: 2, center: true))
.AddCell(NewTd(minPrice == null && maxPrice == null ? "-" : (minPrice != maxPrice ? $"{minPrice:N4}{maxPrice:N4}" : $"{minPrice:N4}") + $" {CurrencySymbol}/kg", colspan: 2, center: true))
.AddCell(NewSectionTh("Mehrwertsteuer:", colspan: 2))
.AddCell(NewTd(Utils.GetSign(vat), right: true))
.AddCell(NewTd(CurrencySymbol))
.AddCell(NewTd($"{Math.Abs(vat):N2}", right: true))
.AddCell(NewSectionTh("Gebunden-Zuschlag:", borderLeft: true))
.AddCell(NewTd(minGeb != maxGeb ? $"{minGeb:N4}\u2013{maxGeb:N4} {CurrencySymbol}/kg" : minGeb == 0 ? "-" : $"{minGeb:N4} {CurrencySymbol}/kg", colspan: 2, center: true))
.AddCell(NewTd(minGeb == null && maxGeb == null ? "-" : minGeb != maxGeb ? $"{minGeb:N4}\u2013{maxGeb:N4} {CurrencySymbol}/kg" : minGeb == 0 ? "-" : $"{minGeb:N4} {CurrencySymbol}/kg", colspan: 2, center: true))
.AddCell(NewSectionTh("Bruttosumme:", colspan: 2))
.AddCell(NewTd(borderTop: true))
+14 -14
View File
@@ -9,7 +9,7 @@
<UseWindowsForms>true</UseWindowsForms>
<PreserveCompilationContext>true</PreserveCompilationContext>
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
<Version>1.0.4.1</Version>
<Version>1.1.0.0</Version>
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ApplicationManifest>app.manifest</ApplicationManifest>
@@ -23,21 +23,21 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="bblanchon.PDFium.Win32" Version="148.0.7776" />
<PackageReference Include="bblanchon.PDFium.Win32" Version="152.0.7934" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
<PackageReference Include="itext" Version="9.6.0" />
<PackageReference Include="itext.bouncy-castle-adapter" Version="9.6.0" />
<PackageReference Include="itext" Version="9.7.0" />
<PackageReference Include="itext.bouncy-castle-adapter" Version="9.7.0" />
<PackageReference Include="LinqKit" Version="1.3.11" />
<PackageReference Include="MailKit" Version="4.15.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.5" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3856.49" />
<PackageReference Include="NJsonSchema" Version="11.5.2" />
<PackageReference Include="ScottPlot.WPF" Version="5.1.58" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.2" />
<PackageReference Include="System.IO.Hashing" Version="10.0.5" />
<PackageReference Include="System.IO.Ports" Version="10.0.5" />
<PackageReference Include="System.Management" Version="10.0.5" />
<PackageReference Include="MailKit" Version="4.17.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.9" />
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.9" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.4078.44" />
<PackageReference Include="NJsonSchema" Version="11.6.1" />
<PackageReference Include="ScottPlot.WPF" Version="5.1.59" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.3" />
<PackageReference Include="System.IO.Hashing" Version="10.0.9" />
<PackageReference Include="System.IO.Ports" Version="10.0.9" />
<PackageReference Include="System.Management" Version="10.0.9" />
</ItemGroup>
</Project>
+37 -8
View File
@@ -1,10 +1,9 @@
using Elwig.Models.Dtos;
using Elwig.Models.Entities;
using Elwig.Services;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using ScottPlot.TickGenerators.Financial;
using ScottPlot.TickGenerators.TimeUnits;
using System;
using System.Collections.Generic;
using System.Data;
@@ -12,11 +11,10 @@ using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Converters;
namespace Elwig.Helpers {
public record struct MemberHistoryPoint(int Shares, int SharesRed, int SharesWhite, int SharesDormant);
public record struct AreaComBucket(int Area, int Obligation, int Right);
public record struct UnderDelivery(int Weight, int Diff);
public record struct MemberBucket(string Name, int Area, int Obligation, int Right, int Delivery, int DeliveryStrict, int DeliveryTotal, int Payment);
@@ -149,6 +147,7 @@ namespace Elwig.Helpers {
.Include(s => s.Modifiers)
.OrderByDescending(s => s.Year));
private readonly Dictionary<int, Dictionary<int, MemberHistoryPoint>> _memberHistory = [];
private readonly Dictionary<int, Dictionary<int, Dictionary<string, AreaComBucket>>> _memberAreaCommitmentBuckets = [];
private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberDeliveryBuckets = [];
private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberDeliveryBucketsStrict = [];
@@ -162,8 +161,8 @@ namespace Elwig.Helpers {
LogFile = new(file) {
AutoFlush = true
};
} catch (Exception e) {
MessageBox.Show($"Unable to open database log file:\n\n{e.Message}", "Database Log", MessageBoxButton.OK, MessageBoxImage.Error);
} catch (Exception exc) {
InteractionService.ShowException("Database Log", $"Unable to open database log file", exc);
}
}
SavedLastWriteTime = LastWriteTime;
@@ -198,6 +197,7 @@ namespace Elwig.Helpers {
modelBuilder.Entity<PostalDest>().Navigation(p => p.AtPlz).AutoInclude();
modelBuilder.Entity<AT_PlzDest>().Navigation(p => p.AtPlz).AutoInclude();
modelBuilder.Entity<AT_PlzDest>().Navigation(p => p.Ort).AutoInclude();
modelBuilder.Entity<Member>().Navigation(m => m.Branch).AutoInclude();
modelBuilder.Entity<Member>().Navigation(m => m.DefaultWbKg).AutoInclude();
modelBuilder.Entity<Member>().Navigation(m => m.Country).AutoInclude();
modelBuilder.Entity<Member>().Navigation(m => m.PostalDest).AutoInclude();
@@ -233,6 +233,9 @@ namespace Elwig.Helpers {
modelBuilder.Entity<DeliveryAncmt>().Navigation(a => a.Schedule).AutoInclude();
modelBuilder.Entity<DeliveryAncmt>().Navigation(a => a.Variety).AutoInclude();
modelBuilder.Entity<DeliverySchedule>().Navigation(s => s.Branch).AutoInclude();
modelBuilder.Entity<MemberHistory>().Navigation(s => s.FromMember).AutoInclude();
modelBuilder.Entity<MemberHistory>().Navigation(s => s.ToMember).AutoInclude();
modelBuilder.Entity<MemberHistory>().Navigation(s => s.Currency).AutoInclude();
}
public override void Dispose() {
@@ -312,6 +315,10 @@ namespace Elwig.Helpers {
return (await DeliverySchedules.Where(s => s.Year == year).Select(v => (int?)v.DsNr).MaxAsync() ?? 0) + 1;
}
public async Task<int> NextHistNr() {
return (await MemberHistory.Select(c => (int?)c.HistNr).MaxAsync() ?? 0) + 1;
}
public IAsyncEnumerable<Branch> FetchBranches(string? zwstid = null, bool includeWithoutMembers = true) {
return _compiledQueryBranches.Invoke(this, zwstid, includeWithoutMembers);
}
@@ -338,9 +345,9 @@ namespace Elwig.Helpers {
public IAsyncEnumerable<Member> FetchMembers(int? mgnr = null, bool includeNotActive = false, bool includeContactInfo = false) {
if (includeContactInfo) {
return _compiledQueryMembersContactInfo.Invoke(this, mgnr, includeNotActive);
return _compiledQueryMembersContactInfo.Invoke(this, mgnr, mgnr != null || includeNotActive);
} else {
return _compiledQueryMembers.Invoke(this, mgnr, includeNotActive);
return _compiledQueryMembers.Invoke(this, mgnr, mgnr != null || includeNotActive);
}
}
@@ -400,6 +407,22 @@ namespace Elwig.Helpers {
}
}
private async Task FetchMemberHistory(int year, SqliteConnection? cnx = null) {
var ownCnx = cnx == null;
cnx ??= await ConnectAsync();
var history = new Dictionary<int, MemberHistoryPoint>();
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"SELECT mgnr, shares, shares_red, shares_white, shares_dormant FROM v_member_history WHERE year = {year}";
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) {
var mgnr = reader.GetInt32(0);
history[mgnr] = new(reader.GetInt32(1), reader.GetInt32(2), reader.GetInt32(3), reader.GetInt32(4));
}
}
if (ownCnx) await cnx.DisposeAsync();
_memberHistory[year] = history;
}
private async Task FetchMemberAreaCommitmentBuckets(int year, SqliteConnection? cnx = null) {
var ownCnx = cnx == null;
cnx ??= await ConnectAsync();
@@ -490,6 +513,12 @@ namespace Elwig.Helpers {
_memberUnderDelivery[year] = buckets;
}
public async Task<MemberHistoryPoint> GetMemberHistory(int year, int mgnr, SqliteConnection? cnx = null) {
if (!_memberHistory.ContainsKey(year))
await FetchMemberHistory(year, cnx);
return _memberHistory[year].GetValueOrDefault(mgnr, new());
}
public async Task<Dictionary<string, AreaComBucket>> GetMemberAreaCommitmentBuckets(int year, int mgnr, SqliteConnection? cnx = null) {
if (!_memberAreaCommitmentBuckets.ContainsKey(year))
await FetchMemberAreaCommitmentBuckets(year, cnx);
+49 -39
View File
@@ -1,6 +1,6 @@
using Microsoft.Data.Sqlite;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
@@ -9,36 +9,41 @@ namespace Elwig.Helpers {
public static class AppDbUpdater {
// Don't forget to update value in Tests/fetch-resources.bat!
public static readonly int RequiredSchemaVersion = 38;
public static readonly int RequiredSchemaVersion = 40;
private static int VersionOffset = 0;
public static async Task<Version> CheckDb() {
using var cnx = AppDbContext.Connect();
long? applId, schemaVers;
using (var cnx = await AppDbContext.ConnectAsync()) {
applId = (long?)await cnx.ExecuteScalar("PRAGMA application_id") ?? 0;
if (applId != 0x454C5747) throw new Exception($"Invalid application_id in database (0x{applId:X08})");
var applId = (long?)await cnx.ExecuteScalar("PRAGMA application_id") ?? 0;
if (applId != 0x454C5747) throw new Exception($"Invalid application_id in database (0x{applId:X08})");
var schemaVers = (long?)await cnx.ExecuteScalar("PRAGMA schema_version") ?? 0;
VersionOffset = (int)(schemaVers % 100);
if (VersionOffset != 0) {
// schema was modified manually/externally
// TODO issue warning
schemaVers = (long?)await cnx.ExecuteScalar("PRAGMA schema_version") ?? 0;
VersionOffset = (int)(schemaVers % 100);
if (VersionOffset != 0) {
// schema was modified manually/externally
// TODO issue warning
}
}
await UpdateDbSchema(cnx, (int)(schemaVers / 100), RequiredSchemaVersion);
var userVers = (long?)await cnx.ExecuteScalar("PRAGMA user_version") ?? 0;
var v = new Version((int)(userVers >> 24), (int)((userVers >> 16) & 0xFF), (int)((userVers >> 8) & 0xFF), (int)(userVers & 0xFF));
await UpdateDbSchema((int)(schemaVers / 100), RequiredSchemaVersion);
if (App.Version > v) {
long vers = (App.Version.Major << 24) | (App.Version.Minor << 16) | (App.Version.Build << 8) | App.Version.Revision;
await cnx.ExecuteBatch($"PRAGMA user_version = {vers}");
Version v;
using (var cnx = await AppDbContext.ConnectAsync()) {
var userVers = (long?)await cnx.ExecuteScalar("PRAGMA user_version") ?? 0;
v = new Version((int)(userVers >> 24), (int)((userVers >> 16) & 0xFF), (int)((userVers >> 8) & 0xFF), (int)(userVers & 0xFF));
if (App.Version > v) {
long vers = (App.Version.Major << 24) | (App.Version.Minor << 16) | (App.Version.Build << 8) | App.Version.Revision;
await cnx.ExecuteBatch($"PRAGMA user_version = {vers}");
}
}
return v;
}
private static async Task UpdateDbSchema(SqliteConnection cnx, int fromVersion, int toVersion) {
private static async Task UpdateDbSchema(int fromVersion, int toVersion) {
if (fromVersion == toVersion) {
return;
} else if (fromVersion > toVersion) {
@@ -48,43 +53,48 @@ namespace Elwig.Helpers {
}
var asm = Assembly.GetExecutingAssembly();
(int From, int To, string Name)[] scripts = asm.GetManifestResourceNames()
(int From, int To, string Name)[] scripts = [.. asm.GetManifestResourceNames()
.Where(n => n.StartsWith("Elwig.Resources.Sql."))
.Select(n => {
var p = n.Split(".")[^2].Split("-");
return (int.Parse(p[0]), int.Parse(p[1]), n);
})
.OrderBy(s => s.Item1).ThenBy(s => s.Item2)
.ToArray();
.OrderBy(s => s.Item1).ThenBy(s => s.Item2)];
List<string> toExecute = [];
var vers = fromVersion;
while (vers < toVersion) {
var (_, to, name) = scripts.Where(s => s.From == vers).Last();
var (_, to, name) = scripts.Last(s => s.From == vers);
toExecute.Add(name);
vers = to;
}
if (toExecute.Count == 0)
return;
await cnx.ExecuteBatch("""
PRAGMA locking_mode = EXCLUSIVE;
BEGIN EXCLUSIVE;
""");
foreach (var script in toExecute) {
await cnx.ExecuteEmbeddedScript(asm, script);
}
var violations = await cnx.ForeignKeyCheck();
if (violations.Length > 0) {
throw new Exception($"Foreign key violations ({violations.Length}):\n" + string.Join("\n", violations
.Select(v => $"{v.Table} - {v.RowId} - {v.Parent} - {v.FkId}")));
}
var backup = Path.ChangeExtension(App.Config.DatabaseFile, $".v{fromVersion}.sqlite3");
File.Copy(App.Config.DatabaseFile, backup, true);
try {
using var cnx = await AppDbContext.ConnectAsync();
await cnx.ExecuteBatch("PRAGMA locking_mode = EXCLUSIVE");
foreach (var script in toExecute) {
await cnx.ExecuteEmbeddedScript(asm, script);
}
await cnx.ExecuteBatch($"""
COMMIT;
VACUUM;
PRAGMA schema_version = {toVersion * 100 + VersionOffset};
""");
var violations = await cnx.ForeignKeyCheck();
if (violations.Length > 0) {
throw new Exception($"Foreign key violations ({violations.Length}):\n" + string.Join("\n", violations
.Take(50)
.Select(v => $"{v.Table} - {v.RowId} - {v.Parent} - {v.FkId}")));
}
await cnx.ExecuteBatch("VACUUM");
await cnx.ExecuteBatch($"PRAGMA schema_version = {toVersion * 100 + VersionOffset}");
} catch (Exception) {
File.Move(backup, App.Config.DatabaseFile, true);
throw;
} finally {
File.Delete(backup);
}
}
}
}
+12 -28
View File
@@ -50,45 +50,29 @@ namespace Elwig.Helpers.Billing {
""");
}
public async Task AutoAdjustBusinessShares(DateOnly date, int allowanceKg = 0, double allowanceBs = 0, int allowanceKgPerBs = 0, double allowanceRel = 0, int addMinBs = 1) {
if (addMinBs < 1) addMinBs = 1;
public async Task AutoAdjustBusinessShares(DateOnly date, int allowanceKg = 0, double allowanceShares = 0, int allowanceKgPerShare = 0, double allowanceRel = 0, int addMinShares = 1) {
if (addMinShares < 1) addMinShares = 1;
using var cnx = await AppDbContext.ConnectAsync();
await cnx.ExecuteBatch($"""
UPDATE member
SET business_shares = member.business_shares - h.business_shares
FROM member_history h
WHERE h.date = '{Year}-11-30' AND h.type = 'auto' AND h.mgnr = member.mgnr AND member.active;
INSERT INTO member_history (mgnr, date, type, business_shares)
SELECT u.mgnr,
'{date:yyyy-MM-dd}',
'auto',
CEIL((u.diff - {allowanceKg}.0 - {allowanceKgPerBs}.0 * u.business_shares) / s.max_kg_per_bs
- {allowanceBs.ToString(CultureInfo.InvariantCulture)}
- {allowanceRel.ToString(CultureInfo.InvariantCulture)} * u.business_shares) AS bs
DELETE FROM member_history WHERE source = 'elwig' AND reason = '{MemberHistory.REASON_PRESCRIPTION}' AND deduct_year = {Year};
INSERT INTO member_history (histnr, from_mgnr, from_type, to_mgnr, to_type, date_notice, date_effective, reason, source, deduct_year, shares, value_per_share, currency, comment)
SELECT COALESCE((SELECT MAX(histnr) AS histnr FROM member_history), 0) + ROW_NUMBER() OVER(ORDER BY m.mgnr),
NULL, NULL, u.mgnr, 1, '{date:yyyy-MM-dd}', '{date:yyyy-MM-dd}', '{MemberHistory.REASON_PRESCRIPTION}', 'elwig', s.year,
CEIL((u.diff - {allowanceKg}.0 - {allowanceKgPerShare}.0 * u.shares) / s.max_kg_per_share
- {allowanceShares.ToString(CultureInfo.InvariantCulture)}
- {allowanceRel.ToString(CultureInfo.InvariantCulture)} * u.shares) AS adjust_shares,
s.share_value / POW(10, s.precision - 2), s.currency, 'durch automatische Nachzeichnung'
FROM v_total_under_delivery u
JOIN season s ON s.year = u.year
JOIN member m ON m.mgnr = u.mgnr
WHERE s.year = {Year} AND bs >= {addMinBs} AND m.active
ON CONFLICT DO UPDATE
SET business_shares = excluded.business_shares;
UPDATE member
SET business_shares = member.business_shares + h.business_shares
FROM member_history h
WHERE h.date = '{Year}-11-30' AND h.type = 'auto' AND h.mgnr = member.mgnr;
WHERE s.year = {Year} AND adjust_shares >= {addMinShares} AND m.active
""");
}
public async Task UnAdjustBusinessShares() {
using var cnx = await AppDbContext.ConnectAsync();
await cnx.ExecuteBatch($"""
UPDATE member
SET business_shares = member.business_shares - h.business_shares
FROM member_history h
WHERE h.date = '{Year}-11-30' AND h.type = 'auto' AND h.mgnr = member.mgnr AND member.active;
DELETE FROM member_history WHERE date = '{Year}-11-30' AND type = 'auto';
DELETE FROM member_history WHERE source = 'elwig' AND reason = '{MemberHistory.REASON_PRESCRIPTION}' AND deduct_year = {Year};
""");
}
+25 -23
View File
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
@@ -92,6 +93,14 @@ namespace Elwig.Helpers.Billing {
Mode = (mode == "elwig") ? CalculationMode.Elwig : CalculationMode.WgMaster;
}
public BillingData() {
Data = new JsonObject {
["mode"] = "elwig",
["version"] = 1,
};
Mode = CalculationMode.Elwig;
}
protected static JsonObject ParseJson(string json) {
if (Schema == null) throw new InvalidOperationException("Schema has to be initialized first");
try {
@@ -107,6 +116,10 @@ namespace Elwig.Helpers.Billing {
return new(ParseJson(json));
}
public string ToJsonString(JsonSerializerOptions? options = null) {
return Data.ToJsonString(options);
}
protected JsonArray GetCurvesEntry() {
return Data[Mode == CalculationMode.Elwig ? "curves" : "Kurven"]?.AsArray() ?? throw new InvalidOperationException();
}
@@ -412,7 +425,7 @@ namespace Elwig.Helpers.Billing {
}
}
public static JsonObject FromGraphEntries(
public static BillingData FromGraphEntries(
IEnumerable<GraphEntry> graphEntries,
BillingData? origData = null,
IEnumerable<RawVaribute>? vaributes = null,
@@ -446,40 +459,29 @@ namespace Elwig.Helpers.Billing {
}
}
CollapsePaymentData(payment, payment.DeepClone().AsObject(), vaributes ?? payment.Select(e => new RawVaribute(e.Key)).ToList(), useDefaultPayment);
CollapsePaymentData(qualityWei, qualityWei.DeepClone().AsObject(), vaributes ?? qualityWei.Select(e => new RawVaribute(e.Key)).ToList(), useDefaultQuality);
var data = new JsonObject {
["mode"] = "elwig",
["version"] = 1,
};
if (origData?.ConsiderDelieryModifiers == true)
data["consider_delivery_modifiers"] = true;
if (origData?.ConsiderContractPenalties == true)
data["consider_contract_penalties"] = true;
if (origData?.ConsiderTotalPenalty == true)
data["consider_total_penalty"] = true;
if (origData?.ConsiderAutoBusinessShares == true)
data["consider_auto_business_shares"] = true;
CollapsePaymentData(payment, payment.DeepClone().AsObject(), vaributes ?? [.. payment.Select(e => new RawVaribute(e.Key))], useDefaultPayment);
CollapsePaymentData(qualityWei, qualityWei.DeepClone().AsObject(), vaributes ?? [.. qualityWei.Select(e => new RawVaribute(e.Key))], useDefaultQuality);
BillingData data = origData != null && origData.Mode == CalculationMode.Elwig ? new BillingData((JsonObject)origData.Data.DeepClone()) : new BillingData();
if (payment.Count == 0) {
data["payment"] = 0;
data.Data["payment"] = 0;
} else if (payment.Count == 1 && payment.First().Key == "default") {
data["payment"] = payment.Single().Value?.DeepClone();
data.Data["payment"] = payment.Single().Value?.DeepClone();
} else {
data["payment"] = payment;
data.Data["payment"] = payment;
}
if (qualityWei.Count == 1 && qualityWei.First().Key == "default") {
data["quality"] = new JsonObject() {
data.Data["quality"] = new JsonObject() {
["WEI"] = qualityWei.Single().Value?.DeepClone()
};
} else if (qualityWei.Count >= 1) {
data["quality"] = new JsonObject() {
data.Data["quality"] = new JsonObject() {
["WEI"] = qualityWei
};
} else {
data.Data.Remove("quality");
}
data["curves"] = curves;
data.Data["curves"] = curves;
return data;
}
+7 -7
View File
@@ -76,7 +76,7 @@ namespace Elwig.Helpers.Billing {
IIF(m.buchführend, s.vat_normal, s.vat_flatrate) AS vat,
ROUND(IIF({Data.ConsiderTotalPenalty}, COALESCE(b.total_penalty, 0), 0) / POW(10, s.precision - 2)) +
ROUND(IIF({Data.ConsiderContractPenalties}, COALESCE(u.total_penalty, 0), 0) / POW(10, 4 - 2)) +
ROUND(IIF({Data.ConsiderAutoBusinessShares}, -COALESCE(a.total_amount, 0), 0) / POW(10, s.precision - 2)) +
ROUND(IIF({Data.ConsiderAutoBusinessShares}, -COALESCE(a.total_amount, 0), 0)) +
IIF({Data.ConsiderCustomModifiers}, COALESCE(x.amount, 0), 0)
AS modifiers,
IIF(lc.amount < 0, 0, lc.modifiers) AS prev_modifiers
@@ -98,7 +98,7 @@ namespace Elwig.Helpers.Billing {
LEFT JOIN v_penalty_area_commitments u ON (u.year, u.mgnr) = (s.year, m.mgnr)
LEFT JOIN v_auto_business_shares a ON (a.year, a.mgnr) = (s.year, m.mgnr)
LEFT JOIN payment_custom x ON (x.year, x.mgnr) = (s.year, m.mgnr)
WHERE s.year = {Year} AND v.avnr = {AvNr} AND p.amount > COALESCE(lp.amount, 0);
WHERE s.year = {Year} AND v.avnr = {AvNr} AND p.amount != COALESCE(lp.amount, 0);
""");
await cnx.ExecuteBatch($"""
UPDATE payment_variant SET test_variant = FALSE WHERE (year, avnr) = ({Year}, {AvNr});
@@ -137,11 +137,11 @@ namespace Elwig.Helpers.Billing {
await cnx.ExecuteBatch($"""
INSERT INTO payment_member (year, avnr, mgnr, net_amount, mod_abs, mod_rel)
SELECT c.year, {AvNr}, s.mgnr, 0,
ROUND(s.sum * COALESCE(m.abs, 0)),
ROUND(s.weight_total * COALESCE(m.abs, 0)),
COALESCE(m.rel, 0)
FROM (SELECT {Year} AS year, m.mgnr,
ROUND(AVG(COALESCE(a.sum, b.sum)) * {multiplier}) AS baseline,
COUNT(*) = {lastYears} AND MIN(COALESCE(a.sum, b.sum)) > 0 AS allowed
ROUND(AVG(COALESCE(a.weight_total, b.weight_total)) * {multiplier}) AS baseline,
COUNT(*) = {lastYears} AND MIN(COALESCE(a.weight_total, b.weight_total)) > 0 AS allowed
FROM member m
LEFT JOIN v_stat_member a ON a.mgnr = m.mgnr
FULL OUTER JOIN v_stat_member b ON b.mgnr = m.predecessor_mgnr AND b.year = a.year AND {(includePredecessor ? "TRUE" : "FALSE")}
@@ -150,7 +150,7 @@ namespace Elwig.Helpers.Billing {
HAVING allowed) c
JOIN v_stat_member s ON (s.year, s.mgnr) = (c.year, c.mgnr)
LEFT JOIN modifier m ON m.year = c.year AND m.name LIKE '{modName}'
WHERE sum >= baseline
WHERE weight_total >= baseline
ON CONFLICT DO UPDATE
SET mod_abs = mod_abs + excluded.mod_abs,
mod_rel = mod_rel + excluded.mod_rel
@@ -232,7 +232,7 @@ namespace Elwig.Helpers.Billing {
SET mod_rel = mod_rel + excluded.mod_rel;
INSERT INTO payment_delivery_part (year, did, dpnr, avnr, net_amount, mod_abs, mod_rel)
SELECT d.year, d.did, d.dpnr, {AvNr}, 0, COALESCE(m.abs, 0), COALESCE(m.rel, 0)
SELECT d.year, d.did, d.dpnr, {AvNr}, 0, COALESCE(m.abs, 0) * d.weight, COALESCE(m.rel, 0)
FROM delivery_part d
LEFT JOIN delivery_part_modifier p ON (p.year, p.did, p.dpnr) = (d.year, d.did, d.dpnr)
LEFT JOIN modifier m ON (m.year, m.modid) = (d.year, p.modid)
+30 -6
View File
@@ -8,16 +8,18 @@ using System.Threading.Tasks;
namespace Elwig.Helpers {
public class ClientParameters {
public enum Type { Matzen, Winzerkeller, Weinland, Baden };
public enum Type { Matzen, Winzerkeller, Weinland, Baden, Seewinkel };
public bool IsMatzen => Client == Type.Matzen;
public bool IsWinzerkeller => Client == Type.Winzerkeller;
public bool IsWeinland => Client == Type.Weinland;
public bool IsBaden => Client == Type.Baden;
public bool IsSeewinkel => Client == Type.Seewinkel;
public bool IsWolkersdorf => IsWinzerkeller && App.ZwstId == "W";
public bool IsHaugsdorf => IsWinzerkeller && App.ZwstId == "H";
public bool IsSitzendorf => IsWinzerkeller && App.ZwstId == "S";
public bool IsGrInzersdorf => IsWeinland;
public bool IsPamhagen => IsSeewinkel;
public string NameToken;
public string NameShort;
@@ -54,6 +56,10 @@ namespace Elwig.Helpers {
public string? EmailAddress;
public string? Website;
public bool EnableMemberHistory;
public int ModeBusinessShares;
public bool HasRedWhite => ModeBusinessShares == 1;
public int ModeDeliveryNoteStats;
public int ModeWineQualityStatistics;
public int OrderingMemberList;
@@ -73,7 +79,7 @@ namespace Elwig.Helpers {
public int ExportEbicsVersion;
public int ExportEbicsAddress;
public (int? AllowanceKg, double? AllowanceBs, int? AllowanceKgPerBs, double? AllowancePercent, int? MinBs) AutoAdjustBs;
public (int? AllowanceKg, double? AllowanceShares, int? AllowanceKgPerShare, double? AllowancePercent, int? MinShares) AutoAdjustShares;
public ClientParameters(AppDbContext ctx) : this(ctx.ClientParameters.ToDictionary(e => e.Param, e => e.Value)) { }
@@ -93,6 +99,8 @@ namespace Elwig.Helpers {
Client = Type.Weinland; break;
case "Winzergenossenschaft Baden - Bad Vöslau":
Client = Type.Baden; break;
case "Winzerkeller Seewinkel":
Client = Type.Seewinkel; break;
};
Plz = int.Parse(parameters["CLIENT_PLZ"] ?? "");
@@ -108,6 +116,15 @@ namespace Elwig.Helpers {
Iban = parameters.GetValueOrDefault("CLIENT_IBAN");
OrganicAuthority = parameters.GetValueOrDefault("CLIENT_ORGANIC_AUTHORITY");
EnableMemberHistory = (parameters.GetValueOrDefault("ENABLE_MEMBERHISTORY")?.ToUpper()) switch {
"1" or "TRUE" or "YES" or "JA" => true,
_ => false,
};
switch (parameters.GetValueOrDefault("MODE_BUSINESSSHARES", "SIMPLE")?.ToUpper()) {
case "SIMPLE": ModeBusinessShares = 0; break;
case "RED_WHITE": ModeBusinessShares = 1; break;
}
switch (parameters.GetValueOrDefault("MODE_DELIVERYNOTE_STATS", "SHORT")?.ToUpper()) {
case "NONE": ModeDeliveryNoteStats = 0; break;
case "GA_ONLY": ModeDeliveryNoteStats = 1; break;
@@ -172,7 +189,7 @@ namespace Elwig.Helpers {
}
var autoAdjust = (parameters.GetValueOrDefault("AUTOADJUST_BUSINESSSHARES") ?? "").Split(';');
AutoAdjustBs = autoAdjust.Length == 5 ? (
AutoAdjustShares = autoAdjust.Length == 5 ? (
int.TryParse(autoAdjust[0], out var v1) ? v1 : null,
double.TryParse(autoAdjust[1], out var v2) ? v2 : null,
int.TryParse(autoAdjust[2], out var v3) ? v3 : null,
@@ -185,6 +202,11 @@ namespace Elwig.Helpers {
}
private IEnumerable<(string, string?)> GetParamValues() {
string businessShares = "SIMPLE";
switch (ModeBusinessShares) {
case 0: businessShares = "SIMPLE"; break;
case 1: businessShares = "RED_WHITE"; break;
}
string deliveryNoteStats = "SHORT";
switch (ModeDeliveryNoteStats) {
case 0: deliveryNoteStats = "NONE"; break;
@@ -231,9 +253,9 @@ namespace Elwig.Helpers {
case 1: exportEbicsAddress = "LINES"; break;
case 2: exportEbicsAddress = "FULL"; break;
}
string autoAdjust = $"{AutoAdjustBs.AllowanceKg};{AutoAdjustBs.AllowanceBs?.ToString(CultureInfo.InvariantCulture)};" +
$"{AutoAdjustBs.AllowanceKgPerBs};{AutoAdjustBs.AllowancePercent?.ToString(CultureInfo.InvariantCulture)};" +
$"{AutoAdjustBs.MinBs}";
string autoAdjust = $"{AutoAdjustShares.AllowanceKg};{AutoAdjustShares.AllowanceShares?.ToString(CultureInfo.InvariantCulture)};" +
$"{AutoAdjustShares.AllowanceKgPerShare};{AutoAdjustShares.AllowancePercent?.ToString(CultureInfo.InvariantCulture)};" +
$"{AutoAdjustShares.MinShares}";
return [
("CLIENT_NAME_TOKEN", NameToken),
("CLIENT_NAME_SHORT", NameShort),
@@ -252,6 +274,8 @@ namespace Elwig.Helpers {
("CLIENT_BIC", Bic),
("CLIENT_IBAN", Iban),
("CLIENT_ORGANIC_AUTHORITY", OrganicAuthority),
("ENABLE_MEMBERHISTORY", EnableMemberHistory ? "YES" : "NO"),
("MODE_BUSINESSSHARES", businessShares),
("MODE_DELIVERYNOTE_STATS", deliveryNoteStats),
("MODE_WINEQUALITYSTATISTICS", modeWineQualityStatistics),
("ORDERING_MEMBERLIST", orderingMemberList),
+104 -27
View File
@@ -1,4 +1,6 @@
using Elwig.Models;
using Elwig.Models.Entities;
using Elwig.Services;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
@@ -59,6 +61,7 @@ namespace Elwig.Helpers.Export {
List<BillingAddr> BillingAddresses,
List<MemberTelNr> TelephoneNumbers,
List<MemberEmailAddr> EmailAddresses,
List<MemberHistory> MemberHistory,
List<AreaCom> AreaCommitments,
List<AreaComContract> Contracts,
List<WbRd> Riede,
@@ -76,7 +79,7 @@ namespace Elwig.Helpers.Export {
foreach (var filename in filenames) {
try {
data.Add(new([], [], [], [], [], [], [], new([], [], [], [], [], new() {
data.Add(new([], [], [], [], [], [], [], new([], [], [], [], [], [], new() {
["member"] = [],
["area_commitment_contract"] = [],
["area_commitment"] = [],
@@ -142,6 +145,17 @@ namespace Elwig.Helpers.Export {
}
}
var historyJson = zip.GetEntry("member_history.json");
if (historyJson != null) {
using var reader = new StreamReader(historyJson.Open(), Utils.UTF8);
string? line;
while ((line = await reader.ReadLineAsync()) != null) {
var obj = JsonNode.Parse(line)!.AsObject();
var h = obj.ToMemberHistory();
r.MemberHistory.Add(h);
}
}
// legacy area commitments
var areaComsJson = zip.GetEntry("area_commitments.json");
if (areaComsJson != null) {
@@ -208,16 +222,11 @@ namespace Elwig.Helpers.Export {
exc is FileNotFoundException ||
exc is IOException) {
data.RemoveAt(data.Count - 1);
var str = $"Die Elwig-Export-Datei '{Path.GetFileName(filename)}' konnte nicht verarbeitet werden und wird übersprungen.\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Fehler beim Importieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Fehler beim Importieren", $"Die Elwig-Export-Datei '{Path.GetFileName(filename)}' konnte nicht verarbeitet werden und wird übersprungen", exc);
await AddImportedFiles(Path.GetFileName(filename));
} catch (Exception exc) {
data.RemoveAt(data.Count - 1);
var str = $"Die Elwig-Export-Datei '{Path.GetFileName(filename)}' konnte nicht verarbeitet werden. Soll sie in Zukunft übersprungen werden?\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
var r = MessageBox.Show(str, "Fehler beim Importieren", MessageBoxButton.YesNo, MessageBoxImage.Error, MessageBoxResult.No);
if (r == MessageBoxResult.Yes) {
if (InteractionService.AskException("Fehler beim Importieren", $"Die Elwig-Export-Datei '{Path.GetFileName(filename)}' konnte nicht verarbeitet werden. Soll sie in Zukunft übersprungen werden?", exc)) {
await AddImportedFiles(Path.GetFileName(filename));
}
}
@@ -227,11 +236,13 @@ namespace Elwig.Helpers.Export {
var importedAreaComs = new List<(string FileName, string ZwstId, string Device, int New, int Overwritten, int NotImported, string? Filters)>();
var importedDeliveries = new List<(string FileName, string ZwstId, string Device, int New, int Overwritten, int NotImported, string? Filters)>();
foreach (var ((members, billingAddresses, telephoneNumbers, emailAddresses, areaCommitments, contracts, riede, wbKgs, wbGls, deliveries, deliveryParts, modifiers, timestamps), meta) in data.Zip(metaData)) {
foreach (var ((members, billingAddresses, telephoneNumbers, emailAddresses, history, areaCommitments, contracts, riede, wbKgs, wbGls, deliveries, deliveryParts, modifiers, timestamps), meta) in data.Zip(metaData)) {
var branch = branches[meta.ZwstId];
var device = meta.Device;
using var ctx = new AppDbContext();
var tx = await ctx.Database.BeginTransactionAsync();
await ctx.Database.ExecuteSqlAsync($"UPDATE client_parameter SET value = '0' WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS'");
var kgnrs = wbKgs.Select(k => k.KgNr).ToList();
var duplicateKgNrs = await ctx.WbKgs
@@ -239,6 +250,12 @@ namespace Elwig.Helpers.Export {
.Select(k => k.KgNr)
.ToListAsync();
var histNrs = history.Select(h => h.HistNr).ToList();
var duplicateHistNrs = await ctx.MemberHistory
.Where(h => histNrs.Contains(h.HistNr))
.Select(h => h.HistNr)
.ToListAsync();
var mgnrs = members.Select(m => m.MgNr).ToList();
var duplicateMgNrs = await ctx.Members
.Where(m => mgnrs.Contains(m.MgNr))
@@ -328,6 +345,11 @@ namespace Elwig.Helpers.Export {
importedMembers.Add((meta.FileName, meta.ZwstId, meta.Device, n, o, members.Count - n - o, meta.MemberFilters));
}
if (importDuplicateMembers || importNewMembers) {
ctx.UpdateRange(history.Where(h => duplicateHistNrs.Contains(h.HistNr)));
ctx.AddRange(history.Where(h => !duplicateHistNrs.Contains(h.HistNr)));
}
if (importDuplicateContracts) {
ctx.RemoveRange(ctx.AreaCommitments.IgnoreAutoIncludes().Where(c => duplicateFbNrs.Contains(c.FbNr)));
ctx.UpdateRange(contracts.Where(c => duplicateFbNrs.Contains(c.FbNr)));
@@ -385,6 +407,8 @@ namespace Elwig.Helpers.Export {
}
await ctx.SaveChangesAsync();
await ctx.Database.ExecuteSqlAsync($"UPDATE client_parameter SET value = '1' WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS'");
await tx.CommitAsync();
var primaryKeys = new Dictionary<string, string>() {
["member"] = "mgnr, 0, 0",
@@ -411,7 +435,7 @@ namespace Elwig.Helpers.Export {
}
App.HintContextChange();
MessageBox.Show(
InteractionService.ShowInformation("Importieren erfolgreich",
$"Das importieren der Daten war erfolgreich!\n" +
$"Folgendes wurde importiert:\n" +
string.Join("\n", [
@@ -433,37 +457,32 @@ namespace Elwig.Helpers.Export {
$" ({d.New} neu, {d.Overwritten} überschr., {d.NotImported} nicht importiert)\n" +
$" Zwst.: {branches[d.ZwstId].Name} (Gerät {d.Device})\n" +
$" Filter: {d.Filters}")
]),
"Importieren erfolgreich",
MessageBoxButton.OK, MessageBoxImage.Information);
]));
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\nEvtl. muss die Datenbank manuell auf dieses Gerät kopieren werden.\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Fehler beim Importieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Fehler beim Importieren", "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\nEvtl. muss die Datenbank manuell auf dieses Gerät kopieren werden", exc);
}
GC.Collect();
GC.WaitForPendingFinalizers();
}
private static bool ImportQuestion(string branch, string device, string subject, bool duplicate, int number) {
return MessageBox.Show(
return InteractionService.AskQuestion($"{subject} importieren",
$"Sollen {number} {(duplicate ? "" : "neue ")}{subject} durch die Zweigstelle\n" +
$"{branch} (Gerät {device}) {(duplicate ? "überschrieben" : "importiert")} werden?",
$"{subject} importieren",
MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes
) == MessageBoxResult.Yes;
$"{branch} (Gerät {device}) {(duplicate ? "überschrieben" : "importiert")} werden?", true);
}
public static Task Export(string filename, IEnumerable<Member> members, IEnumerable<WbKg> wbKgs, IEnumerable<string> filters) {
public static Task Export(string filename, IEnumerable<Member> members, IEnumerable<MemberHistory> history, IEnumerable<WbKg> wbKgs, IEnumerable<string> filters) {
return new ElwigExport {
Members = (members, filters),
History = (history, ["von exportierten Mitgliedern"]),
WbKgs = (wbKgs, ["von exportierten Mitgliedern"]),
}.Export(filename);
}
public static Task Export(string filename, IEnumerable<Member> members, IEnumerable<AreaComContract> areaComs, IEnumerable<WbKg> wbKgs, IEnumerable<string> filters) {
public static Task Export(string filename, IEnumerable<Member> members, IEnumerable<MemberHistory> history, IEnumerable<AreaComContract> areaComs, IEnumerable<WbKg> wbKgs, IEnumerable<string> filters) {
return new ElwigExport {
Members = (members, filters),
History = (history, ["von exportierten Mitgliedern"]),
AreaComs = (areaComs, ["von exportierten Mitgliedern"]),
WbKgs = (wbKgs, ["von exportierten Mitgliedern und Flächenbindungen"]),
}.Export(filename);
@@ -479,6 +498,7 @@ namespace Elwig.Helpers.Export {
public class ElwigExport {
public (IEnumerable<WbKg> WbKgs, IEnumerable<string> Filters)? WbKgs { get; set; }
public (IEnumerable<Member> Members, IEnumerable<string> Filters)? Members { get; set; }
public (IEnumerable<MemberHistory> History, IEnumerable<string> Filters)? History { get; set; }
public (IEnumerable<AreaComContract> AreaComs, IEnumerable<string> Filters)? AreaComs { get; set; }
public (IEnumerable<Delivery> Deliveries, IEnumerable<string> Filters)? Deliveries { get; set; }
@@ -498,17 +518,21 @@ namespace Elwig.Helpers.Export {
["zwstid"] = App.ZwstId,
["device"] = Environment.MachineName,
};
if (WbKgs != null) {
if (WbKgs != null)
obj["wb_kgs"] = new JsonObject {
["count"] = WbKgs.Value.WbKgs.Count(),
["filters"] = new JsonArray(WbKgs.Value.Filters.Select(f => (JsonNode)f).ToArray()),
};
}
if (Members != null)
obj["members"] = new JsonObject {
["count"] = Members.Value.Members.Count(),
["filters"] = new JsonArray(Members.Value.Filters.Select(f => (JsonNode)f).ToArray()),
};
if (History != null)
obj["member_history"] = new JsonObject {
["count"] = History.Value.History.Count(),
["filters"] = new JsonArray(History.Value.Filters.Select(f => (JsonNode)f).ToArray()),
};
if (AreaComs != null)
obj["area_commitment_contracts"] = new JsonObject {
["count"] = AreaComs.Value.AreaComs.Count(),
@@ -539,6 +563,13 @@ namespace Elwig.Helpers.Export {
await writer.WriteLineAsync(m.ToJson().ToJsonString(Utils.JsonOpts));
}
}
if (History != null) {
var json = zip.CreateEntry("member_history.json", CompressionLevel.SmallestSize);
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
foreach (var h in History.Value.History) {
await writer.WriteLineAsync(h.ToJson().ToJsonString(Utils.JsonOpts));
}
}
if (AreaComs != null) {
var json = zip.CreateEntry("area_commitment_contracts.json", CompressionLevel.SmallestSize);
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
@@ -597,7 +628,10 @@ namespace Elwig.Helpers.Export {
["birthday"] = m.Birthday,
["entry_date"] = m.EntryDate != null ? $"{m.EntryDate:yyyy-MM-dd}" : null,
["exit_date"] = m.ExitDate != null ? $"{m.ExitDate:yyyy-MM-dd}" : null,
["business_shares"] = m.BusinessShares,
["shares"] = m.Shares,
["shares_red"] = m.SharesRed,
["shares_white"] = m.SharesWhite,
["shares_dormant"] = m.SharesDormant,
["accounting_nr"] = m.AccountingNr,
["zwstid"] = m.ZwstId,
["lfbis_nr"] = m.LfbisNr,
@@ -666,7 +700,10 @@ namespace Elwig.Helpers.Export {
Birthday = json["birthday"]?.AsValue().GetValue<string>(),
EntryDateString = json["entry_date"]?.AsValue().GetValue<string>(),
ExitDateString = json["exit_date"]?.AsValue().GetValue<string>(),
BusinessShares = json["business_shares"]?.AsValue().GetValue<int>() ?? 0,
Shares = json["shares"]?.AsValue().GetValue<int>() ?? json["business_shares"]?.AsValue().GetValue<int>() ?? 0,
SharesRed = json["shares_red"]?.AsValue().GetValue<int>() ?? 0,
SharesWhite = json["shares_white"]?.AsValue().GetValue<int>() ?? 0,
SharesDormant = json["shares_dormant"]?.AsValue().GetValue<int>() ?? 0,
AccountingNr = json["accounting_nr"]?.AsValue().GetValue<string>(),
ZwstId = json["zwstid"]?.AsValue().GetValue<string>(),
LfbisNr = json["lfbis_nr"]?.AsValue().GetValue<string>(),
@@ -711,6 +748,46 @@ namespace Elwig.Helpers.Export {
DateTime.ParseExact(modifiedAt, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None)));
}
public static JsonObject ToJson(this MemberHistory h) {
return new JsonObject {
["histnr"] = h.HistNr,
["from_mgnr"] = h.FromMgNr,
["from_type"] = h.FromType?.ToString().ToLower(),
["to_mgnr"] = h.ToMgNr,
["to_type"] = h.ToType?.ToString().ToLower(),
["date_notice"] = h.DateNoticeString,
["date_effective"] = h.DateEffectiveString,
["reason"] = h.Reason,
["source"] = h.Source,
["shares"] = h.Shares,
["signed"] = h.MemberHasSigned,
["value_per_share"] = h.ValuePerShare,
["currency"] = h.CurrencyCode,
["deduct_year"] = h.DeductYear,
["comment"] = h.Comment,
};
}
public static MemberHistory ToMemberHistory(this JsonNode json) {
return new MemberHistory {
HistNr = json["histnr"]!.AsValue().GetValue<int>(),
FromMgNr = json["from_mgnr"]?.AsValue().GetValue<int>(),
FromType = Enum.TryParse<BusinessShareType>(json["from_type"]?.AsValue().GetValue<string>(), true, out var from) ? from : null,
ToMgNr = json["to_mgnr"]?.AsValue().GetValue<int>(),
ToType = Enum.TryParse<BusinessShareType>(json["to_type"]?.AsValue().GetValue<string>(), true, out var to) ? to : null,
DateNoticeString = json["date_notice"]!.AsValue().GetValue<string>(),
DateEffectiveString = json["date_effective"]!.AsValue().GetValue<string>(),
Reason = json["reason"]!.AsValue().GetValue<string>(),
Source = json["source"]!.AsValue().GetValue<string>(),
Shares = json["shares"]!.AsValue().GetValue<int>(),
MemberHasSigned = json["signed"]?.AsValue().GetValue<bool>() ?? false,
ValuePerShare = json["value_per_share"]?.AsValue().GetValue<decimal>(),
CurrencyCode = json["currency"]?.AsValue().GetValue<string>(),
DeductYear = json["deduct_year"]?.AsValue().GetValue<int>(),
Comment = json["comment"]?.AsValue().GetValue<string>(),
};
}
public static JsonObject ToJson(this AreaComContract c) {
return new JsonObject {
["fbnr"] = c.FbNr,
+26
View File
@@ -4,11 +4,14 @@ using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.IO.Hashing;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace Elwig.Helpers {
public static partial class Extensions {
@@ -145,5 +148,28 @@ namespace Elwig.Helpers {
}
return [.. list];
}
public static IEnumerable<T> Join<T>(this IEnumerable<T> src, Func<T> separatorFactory) {
var srcArr = src.ToArray();
for (int i = 0; i < srcArr.Length; i++) {
yield return srcArr[i];
if (i < srcArr.Length - 1) {
yield return separatorFactory();
}
}
}
public static void AddToolTipCell(this Grid grid, string text, int row, int col, int colSpan = 1, bool bold = false, bool alignRight = false, bool alignCenter = false) {
var tb = new TextBlock() {
Text = text,
TextAlignment = alignRight ? TextAlignment.Right : alignCenter ? TextAlignment.Center : TextAlignment.Left,
Margin = new(0, 12 * row, 0, 0),
FontWeight = bold ? FontWeights.Bold : FontWeights.Normal,
};
tb.SetValue(Grid.ColumnProperty, col);
tb.SetValue(Grid.ColumnSpanProperty, colSpan);
grid.Children.Add(tb);
}
}
}
+3 -3
View File
@@ -1,8 +1,8 @@
using Elwig.Services;
using Elwig.Windows;
using System;
using System.Drawing.Printing;
using System.Threading.Tasks;
using System.Windows;
namespace Elwig.Helpers.Printing {
public static class Pdf {
@@ -46,8 +46,8 @@ namespace Elwig.Helpers.Printing {
PrinterSettings = settings,
};
printDoc.Print();
} catch (Exception e) {
MessageBox.Show("Beim Drucken ist ein Fehler aufgetreten:\n\n" + e.Message, "Fehler beim Drucken", MessageBoxButton.OK, MessageBoxImage.Error);
} catch (Exception exc) {
InteractionService.ShowException("Fehler beim Drucken", "Beim Drucken ist ein Fehler aufgetreten", exc);
}
return Task.CompletedTask;
}
+29 -24
View File
@@ -3,12 +3,12 @@ using Elwig.Documents;
using Elwig.Helpers.Billing;
using Elwig.Models;
using Elwig.Models.Entities;
using Elwig.Services;
using iText.Layout.Element;
using LinqKit;
using MailKit.Net.Smtp;
using MailKit.Security;
using Microsoft.EntityFrameworkCore;
using Microsoft.Win32;
using MimeKit;
using System;
using System.Collections;
@@ -31,6 +31,7 @@ using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Markup;
namespace Elwig.Helpers {
@@ -209,16 +210,29 @@ namespace Elwig.Helpers {
return Regex.Replace(iban.Trim(), ".{4}", "$0 ").Trim();
}
public static void RunBackground(string title, Func<Task> a) {
public static void RunBackground(string title, Func<Task> function) {
Task.Run(async () => {
try {
await a();
} catch (Exception e) {
MessageBox.Show(e.ToString(), title, MessageBoxButton.OK, MessageBoxImage.Error);
await function();
} catch (Exception exc) {
InteractionService.ShowException(title, exc);
}
});
}
public static async Task RunForeground(Func<Task> function) {
var isSTA = Thread.CurrentThread.GetApartmentState() == ApartmentState.STA;
if (isSTA) Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
await function();
} catch (Exception exc) {
InteractionService.ShowException(exc);
}
});
if (isSTA) Mouse.OverrideCursor = null;
}
public static void MailTo(string emailAddress) {
MailTo([emailAddress]);
}
@@ -443,7 +457,7 @@ namespace Elwig.Helpers {
Timeout = TimeSpan.FromSeconds(5),
};
client.DefaultRequestHeaders.UserAgent.Clear();
client.DefaultRequestHeaders.UserAgent.ParseAdd($"Elwig/{App.Version} ({App.Client.NameToken}, {App.BranchName}, {Environment.MachineName}, {Environment.OSVersion})");
client.DefaultRequestHeaders.UserAgent.ParseAdd($"Elwig/{App.Version} ({App.Client?.NameToken}, {App.BranchName}, {Environment.MachineName}, {Environment.OSVersion})");
client.DefaultRequestHeaders.Accept.Clear();
if (accept != null)
client.DefaultRequestHeaders.Accept.Add(new(accept));
@@ -537,7 +551,7 @@ namespace Elwig.Helpers {
subject, docs.Select(d => d.Title).ToArray()
)]);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
return false;
} finally {
if (client != null)
@@ -551,8 +565,7 @@ namespace Elwig.Helpers {
public static async Task ExportDocument(Document doc, ExportMode mode, string? filename = null, (Member Member, string Subject, string Text)? emailData = null) {
if (mode == ExportMode.Print && !App.Config.Debug) {
if (doc.IsPreview) {
MessageBox.Show("Dieses Dokument ist als vorläufig markiert und kann daher nicht ausgedruckt werden!",
"Vorläufiges Dokument", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Vorläufiges Dokument", "Dieses Dokument ist als vorläufig markiert und kann daher nicht ausgedruckt werden!");
return;
}
using (var ctx = new AppDbContext()) {
@@ -561,8 +574,7 @@ namespace Elwig.Helpers {
await doc.Print();
} else if (mode == ExportMode.Email && emailData is (Member, string, string) e) {
if (doc.IsPreview) {
MessageBox.Show("Dieses Dokument ist als vorläufig markiert und kann daher nicht verschickt werden!",
"Vorläufiges Dokument", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Vorläufiges Dokument", "Dieses Dokument ist als vorläufig markiert und kann daher nicht verschickt werden!");
return;
}
using (var ctx = new AppDbContext()) {
@@ -570,25 +582,18 @@ namespace Elwig.Helpers {
}
var success = await SendEmail(e.Member, e.Subject, e.Text, [doc]);
if (success)
MessageBox.Show("Die E-Mail wurde erfolgreich verschickt!\n\nEs kann einige Minuten dauern, bis die E-Mail im Posteingang des Empfängers aufscheint.", "E-Mail verschickt",
MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("E-Mail verschickt", "Die E-Mail wurde erfolgreich verschickt!\n\nEs kann einige Minuten dauern, bis die E-Mail im Posteingang des Empfängers aufscheint.");
} else if (mode == ExportMode.SavePdf) {
if (doc.IsPreview) {
MessageBox.Show("Dieses Dokument ist als vorläufig markiert und sollte daher nicht langfristig gespeichert werden!",
"Vorläufiges Dokument", MessageBoxButton.OK, MessageBoxImage.Warning);
InteractionService.ShowWarning("Vorläufiges Dokument", "Dieses Dokument ist als vorläufig markiert und sollte daher nicht langfristig gespeichert werden!");
}
var d = new SaveFileDialog() {
FileName = $"{NormalizeFileName(filename ?? doc.Title)}.pdf",
DefaultExt = "pdf",
Filter = "PDF-Datei (*.pdf)|*.pdf",
Title = $"{doc.Title} speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
filename = InteractionService.SaveFile(doc.Title, NormalizeFileName(filename ?? doc.Title), "pdf");
if (filename != null) {
using (var ctx = new AppDbContext()) {
await doc.Generate(ctx);
}
doc.SaveTo(d.FileName);
Process.Start("explorer.exe", d.FileName);
doc.SaveTo(filename);
if (!App.Config.Debug) Process.Start("explorer.exe", filename);
}
} else {
using (var ctx = new AppDbContext()) {
+8 -14
View File
@@ -136,8 +136,7 @@ namespace Elwig.Helpers {
return new(false, "PLZ zu kurz");
}
using var ctx = new AppDbContext();
int plz = int.Parse(input.Text);
if (ctx.Postleitzahlen.Find(plz) == null) {
if (!int.TryParse(input.Text, out var plz) || ctx.Postleitzahlen.Find(plz) == null) {
return new(false, "Ungültige PLZ");
}
return new(true, null);
@@ -423,8 +422,7 @@ namespace Elwig.Helpers {
}
using var ctx = new AppDbContext();
int nr = int.Parse(input.Text);
if (!ctx.MgNrExists(nr).GetAwaiter().GetResult()) {
if (!int.TryParse(input.Text, out var nr) || !ctx.MgNrExists(nr).GetAwaiter().GetResult()) {
return new(false, "Ungültige Mitgliedsnummer");
}
@@ -440,8 +438,7 @@ namespace Elwig.Helpers {
}
using var ctx = new AppDbContext();
int nr = int.Parse(input.Text);
if (nr != m?.MgNr && ctx.MgNrExists(nr).GetAwaiter().GetResult()) {
if (!int.TryParse(input.Text, out var nr) || nr != m?.MgNr && ctx.MgNrExists(nr).GetAwaiter().GetResult()) {
return new(false, "Mitgliedsnummer wird bereits verwendet");
}
@@ -476,7 +473,7 @@ namespace Elwig.Helpers {
return res;
} else if (!required && input.Text.Length == 0) {
return new(true, null);
} else if (!ctx.MgNrExists(int.Parse(input.Text)).GetAwaiter().GetResult()) {
} else if (!int.TryParse(input.Text, out var nr) ||!ctx.MgNrExists(nr).GetAwaiter().GetResult()) {
return new(false, "Ein Mitglied mit dieser Mitgliedsnummer existiert nicht");
}
@@ -537,7 +534,7 @@ namespace Elwig.Helpers {
return (parts[0]?.Length == 4) ? new(true, null) : new(false, "Datum ist ungültig");
} else if (p == 1) {
// only month and year provided
int m = parts[0] != null && parts[0] != "" ? int.Parse(parts[0] ?? "0") : 0;
int m = parts[0] != null && parts[0] != "" && int.TryParse(parts[0], out var v) ? v : 0;
if (parts[1]?.Length != 4 || parts[0]?.Length != 2 || m < 1 || m > 12)
return new(false, "Datum ist ungültig");
return new(true, null);
@@ -589,8 +586,7 @@ namespace Elwig.Helpers {
}
using var ctx = new AppDbContext();
int nr = int.Parse(input.Text);
if (nr != c?.FbNr && ctx.FbNrExists(nr).GetAwaiter().GetResult()) {
if (!int.TryParse(input.Text, out var nr) || (nr != c?.FbNr && ctx.FbNrExists(nr).GetAwaiter().GetResult())) {
return new(false, "Flächenbindungsnummer wird bereits verwendet");
}
@@ -605,8 +601,7 @@ namespace Elwig.Helpers {
return new(true, null);
}
var oe = double.Parse(input.Text);
if (oe < 10 || oe >= 300) {
if (!double.TryParse(input.Text, out var oe) || oe < 10 || oe >= 300) {
return new(false, "Ungültiger Oechsle-Wert");
}
@@ -621,8 +616,7 @@ namespace Elwig.Helpers {
return new(true, null);
}
var kmw = double.Parse(input.Text);
if (kmw < 5 || kmw >= 50) {
if (!double.TryParse(input.Text, out var kmw) || kmw < 5 || kmw >= 50) {
return new(false, "Ungültiger KMW-Wert");
}
+3 -4
View File
@@ -1,9 +1,9 @@
using Elwig.Services;
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace Elwig.Helpers.Weighing {
public class AveryEventScale : Scale, IEventScale, IDisposable {
@@ -60,9 +60,8 @@ namespace Elwig.Helpers.Weighing {
} catch (TimeoutException) {
await Task.Delay(500);
await Reconnect();
} catch (Exception ex) {
MessageBox.Show($"Beim Wiegen ist ein Fehler Aufgetreten:\n\n{ex.Message} ({ex.GetType().Name})", "Waagenfehler",
MessageBoxButton.OK, MessageBoxImage.Error);
} catch (Exception exc) {
InteractionService.ShowException("Waagenfehler", "Beim Wiegen ist ein Fehler Aufgetreten", exc, showExcType: true);
}
}
}
+3 -4
View File
@@ -1,9 +1,9 @@
using Elwig.Services;
using System;
using System.IO;
using System.IO.Ports;
using System.Net.Sockets;
using System.Text;
using System.Windows;
namespace Elwig.Helpers.Weighing {
public abstract class Scale : IDisposable {
@@ -42,11 +42,10 @@ namespace Elwig.Helpers.Weighing {
if (cnx.StartsWith("serial:")) {
try {
Serial = Utils.OpenSerialConnection(cnx);
} catch (Exception e) {
} catch (Exception exc) {
if (!softFail) throw;
if (!failSilent)
MessageBox.Show($"Verbindung zu Waage konnte nicht hergestellt werden:\n\n{e.Message}", "Waagenfehler",
MessageBoxButton.OK, MessageBoxImage.Warning);
InteractionService.ShowException("Waagenfehler", "Verbindung zu Waage konnte nicht hergestellt werden", exc, isError: false);
}
Stream = Serial?.BaseStream ?? Stream.Null;
} else if (cnx.StartsWith("tcp:")) {
+5
View File
@@ -0,0 +1,5 @@
namespace Elwig.Models {
public enum BusinessShareType {
Normal = 1, Red = 2, White = 3, Dormant = 9,
}
}
+21 -16
View File
@@ -17,6 +17,7 @@ namespace Elwig.Models.Dtos {
("Address", "Adresse", null, 60),
("Plz", "PLZ", null, 10),
("Locality", "Ort", null, 60),
("AccountingNr", "BH-Konto", null, 20),
("Iban", "IBAN", null, 45),
("TgNr", "TG-Nr.", null, 20),
("Sum", "Zwischens.", "€", 20),
@@ -29,7 +30,7 @@ namespace Elwig.Models.Dtos {
("Gross", "Brutto", "€", 20),
("Penalties", "Pönalen FB", "€", 20),
("Penalty", "Unterl. GA", "€", 20),
("AutoBs", "GA Nachz.", "€", 20),
("AutoShares", "GA Nachz.", "€", 20),
("Custom", "Weitere", "€", 20),
("Others", "Sonstige", "€", 20),
("Considered", "Berückstgt.", "€", 20),
@@ -53,12 +54,12 @@ namespace Elwig.Models.Dtos {
SELECT m.mgnr, m.name AS name_1,
COALESCE(m.prefix || ' ', '') || m.given_name ||
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
p.plz, o.name AS ort, m.address, m.iban, c.tgnr, s.year, s.precision,
p.plz, o.name AS ort, m.address, m.accounting_nr, m.iban, c.tgnr, s.year, s.precision,
p.amount - p.net_amount AS surcharge,
c.net_amount, c.prev_net_amount, c.vat, c.vat_amount, c.gross_amount, c.modifiers, c.prev_modifiers, c.amount,
ROUND(b.total_penalty / POW(10, s.precision - 2)) AS bs_penalty,
ROUND(u.total_penalty / POW(10, 4 - 2)) AS fb_penalty,
ROUND(-a.total_amount / POW(10, s.precision - 2)) AS auto_bs,
ROUND(b.total_penalty / POW(10, s.precision - 2)) AS shares_penalty,
ROUND(u.total_penalty / POW(10, 4 - 2)) AS ac_penalty,
-a.total_amount AS auto_shares,
x.amount AS custom_mod
FROM credit c
LEFT JOIN member m ON m.mgnr = c.mgnr
@@ -83,6 +84,7 @@ namespace Elwig.Models.Dtos {
public string Address;
public int Plz;
public string Locality;
public string? AccountingNr;
public string? Iban;
public string TgNr;
public decimal Sum;
@@ -94,7 +96,7 @@ namespace Elwig.Models.Dtos {
public decimal Gross;
public decimal? Penalties;
public decimal? Penalty;
public decimal? AutoBs;
public decimal? AutoShares;
public decimal? Custom;
public decimal? Others;
public decimal? Considered;
@@ -108,6 +110,7 @@ namespace Elwig.Models.Dtos {
Address = row.Address;
Plz = row.Plz;
Locality = row.Locality;
AccountingNr = row.AccountingNr;
Iban = row.Iban != null ? Utils.FormatIban(row.Iban) : null;
TgNr = $"{row.Year}/{row.TgNr}";
Total = Utils.DecFromDb(row.NetAmount, prec1);
@@ -122,14 +125,14 @@ namespace Elwig.Models.Dtos {
}
decimal mod = (row.Modifiers == null) ? 0 : Utils.DecFromDb((long)row.Modifiers, prec1);
if (data.ConsiderContractPenalties)
Penalties = (row.FbPenalty == null) ? null : Utils.DecFromDb((long)row.FbPenalty, prec1);
Penalties = (row.AcPenalty == null) ? null : Utils.DecFromDb((long)row.AcPenalty, prec1);
if (data.ConsiderTotalPenalty)
Penalty = (row.BsPenalty == null) ? null : Utils.DecFromDb((long)row.BsPenalty, prec1);
Penalty = (row.SharesPenalty == null) ? null : Utils.DecFromDb((long)row.SharesPenalty, prec1);
if (data.ConsiderAutoBusinessShares)
AutoBs = (row.AutoBs == null) ? null : Utils.DecFromDb((long)row.AutoBs, prec1);
AutoShares = (row.AutoShares == null) ? null : Utils.DecFromDb((long)row.AutoShares, prec1);
if (data.ConsiderCustomModifiers)
Custom = (row.CustomMod == null) ? null : Utils.DecFromDb((long)row.CustomMod, prec1);
mod -= (Penalties ?? 0) + (Penalty ?? 0) + (AutoBs ?? 0) + (Custom ?? 0);
mod -= (Penalties ?? 0) + (Penalty ?? 0) + (AutoShares ?? 0) + (Custom ?? 0);
Others = (mod == 0) ? null : mod;
Gross = Utils.DecFromDb(row.GrossAmount, prec1);
Considered = (row.PrevModifiers == null || row.PrevModifiers == 0) ? null : -Utils.DecFromDb((long)row.PrevModifiers, prec1);
@@ -153,6 +156,8 @@ namespace Elwig.Models.Dtos {
public required string LocalityFull { get; set; }
[NotMapped]
public string Locality => LocalityFull.Split(",")[0];
[Column("accounting_nr")]
public string? AccountingNr { get; set; }
[Column("iban")]
public string? Iban { get; set; }
[Column("year")]
@@ -179,12 +184,12 @@ namespace Elwig.Models.Dtos {
public long? PrevModifiers { get; set; }
[Column("amount")]
public long Amount { get; set; }
[Column("bs_penalty")]
public long? BsPenalty { get; set; }
[Column("fb_penalty")]
public long? FbPenalty { get; set; }
[Column("auto_bs")]
public long? AutoBs { get; set; }
[Column("shares_penalty")]
public long? SharesPenalty { get; set; }
[Column("ac_penalty")]
public long? AcPenalty { get; set; }
[Column("auto_shares")]
public long? AutoShares { get; set; }
[Column("custom_mod")]
public long? CustomMod { get; set; }
}
+11 -11
View File
@@ -42,13 +42,10 @@ namespace Elwig.Models.Dtos {
}
private static async Task<IEnumerable<CreditNoteDeliveryRowSingle>> FromDbSet(DbSet<CreditNoteDeliveryRowSingle> table, int? year = null, int? avnr = null, int? mgnr = null) {
var y = year?.ToString() ?? "NULL";
var v = avnr?.ToString() ?? "NULL";
var m = mgnr?.ToString() ?? "NULL";
return await table.FromSqlRaw($"""
return await table.FromSql($"""
SELECT d.year, c.tgnr, v.avnr, d.mgnr, d.did, d.lsnr, d.dpnr, d.weight, d.modifiers,
b.bktnr, d.sortid, b.discr, b.value, pb.price, pb.amount, p.net_amount, p.amount AS total_amount,
s.name AS variety, a.name AS attribute, c.name AS cultivation, q.qualid AS qualid, q.name AS quality_level, d.oe, d.kmw, d.net_weight
s.name AS variety, s.type AS type, a.name AS attribute, c.name AS cultivation, q.qualid AS qualid, q.name AS quality_level, d.oe, d.kmw, d.net_weight
FROM v_delivery d
JOIN wine_variety s ON s.sortid = d.sortid
LEFT JOIN wine_attribute a ON a.attrid = d.attrid
@@ -59,7 +56,7 @@ namespace Elwig.Models.Dtos {
LEFT JOIN payment_delivery_part p ON (p.year, p.did, p.dpnr, p.avnr) = (d.year, d.did, d.dpnr, v.avnr)
LEFT JOIN payment_delivery_part_bucket pb ON (pb.year, pb.did, pb.dpnr, pb.bktnr, pb.avnr) = (b.year, b.did, b.dpnr, b.bktnr, v.avnr)
LEFT JOIN credit c ON (c.year, c.avnr, c.mgnr) = (d.year, v.avnr, d.mgnr)
WHERE b.value > 0 AND (d.year = {y} OR {y} IS NULL) AND (v.avnr = {v} OR {v} IS NULL) AND (d.mgnr = {m} OR {m} IS NULL)
WHERE b.value > 0 AND (d.year = {year} OR {year} IS NULL) AND (v.avnr = {avnr} OR {avnr} IS NULL) AND (d.mgnr = {mgnr} OR {mgnr} IS NULL)
ORDER BY d.year, v.avnr, d.mgnr, d.lsnr, d.dpnr
""").ToListAsync();
}
@@ -75,6 +72,7 @@ namespace Elwig.Models.Dtos {
public string LsNr;
public int DPNr;
public string Variety;
public string Type;
public string? Attribute;
public string? Cultivation;
public string[] Modifiers;
@@ -97,24 +95,24 @@ namespace Elwig.Models.Dtos {
LsNr = f.LsNr;
DPNr = f.DPNr;
Variety = f.Variety;
Type = f.Type;
Attribute = f.Attribute;
Cultivation = f.Cultivation;
var modifiers = (IEnumerable<Modifier>)(f.Modifiers ?? "").Split(',')
.Select(m => season?.Modifiers.FirstOrDefault(s => s.ModId == m))
.Where(m => m != null)
.OrderBy(m => m.Ordering)
.OrderBy(m => m!.Ordering)
.ToList();
Modifiers = modifiers.Select(m => m.Name).ToArray();
Modifiers = [.. modifiers.Select(m => m.Name)];
QualId = f.QualId;
QualityLevel = f.QualityLevel;
Gradation = (f.Oe, f.Kmw);
Buckets = rows
Buckets = [.. rows
.Where(b => b.Value > 0)
.OrderByDescending(b => b.BktNr)
.Select(b => (b.Discr == "_" ? "ungeb." : $"geb. {f.SortId}{b.Discr}", b.Value,
b.Price != null ? season?.DecFromDb((long)b.Price) : null,
b.Amount != null ? season?.DecFromDb((long)b.Amount) : null))
.ToArray();
b.Amount != null ? season?.DecFromDb((long)b.Amount) : null))];
WeighingModifier = (varData == null || !varData.ConsiderDelieryModifiers) ? 0 : f.NetWeight ? varData.NetWeightModifier : varData.GrossWeightModifier;
Amount = f.TotalAmount != null ? season?.DecFromDb((long)f.TotalAmount) : null;
var netAmount = f.NetAmount != null ? season?.DecFromDb((long)f.NetAmount) : null;
@@ -161,6 +159,8 @@ namespace Elwig.Models.Dtos {
public long? TotalAmount { get; set; }
[Column("variety")]
public required string Variety { get; set; }
[Column("type")]
public required string Type { get; set; }
[Column("attribute")]
public string? Attribute { get; set; }
[Column("cultivation")]
+1 -1
View File
@@ -8,7 +8,7 @@ namespace Elwig.Models.Dtos {
public class MemberDeliveryData : DataTable<MemberDeliveryRow> {
private static readonly (string, string, string?, int?)[] FieldNames = [
("MgNr", "MgNr.", null, 12),
("MgNr", "MgNr.", null, 12),
("Name1", "Name", null, 40),
("Name2", "Vorname", null, 40),
("Address", "Adresse", null, 60),
+9 -3
View File
@@ -19,7 +19,7 @@ namespace Elwig.Models.Dtos {
("Locality", "Ort", null, 60),
("DefaultKg", "Stammgemeinde", null, 60),
("Branch", "Zweigstelle", null, 40),
("BusinessShares", "GA", null, 10),
("SharesActive", "GA", null, 10),
("BillingName", "Rechnungsname", null, 60),
("BillingAddress", "Rechnungsadresse", null, 60),
("BillingPlz", "PLZ", null, 10),
@@ -66,7 +66,10 @@ namespace Elwig.Models.Dtos {
public string? Name2;
public string? DefaultKg;
public string? Branch;
public int BusinessShares;
public int Shares;
public int SharesRed;
public int SharesWhite;
public int SharesActive;
public string Address;
public int Plz;
public string Locality;
@@ -98,7 +101,10 @@ namespace Elwig.Models.Dtos {
Name2 = m.AdministrativeName2;
DefaultKg = m.DefaultKg?.Name;
Branch = m.Branch?.Name;
BusinessShares = m.BusinessShares;
Shares = m.Shares;
SharesRed = m.SharesRed;
SharesWhite = m.SharesWhite;
SharesActive = Shares + SharesRed + SharesWhite;
Address = m.Address;
Plz = m.PostalDest.AtPlz!.Plz;
Locality = m.PostalDest.AtPlz!.Ort.Name;
+81 -18
View File
@@ -13,35 +13,74 @@ namespace Elwig.Models.Dtos {
("Address", "Adresse", null, 60),
("Plz", "PLZ", null, 10),
("Locality", "Ort", null, 60),
("BusinessShares", "GA", null, 10),
("Shares", "GA", null, 10),
("DeliveryObligation", "Lieferpflicht", "kg", 22),
("DeliveryRight", "Lieferrecht", "kg", 22),
("Weight", "Geliefert", "kg", 22),
("WeightTotal", "Geliefert", "kg", 22),
("OverUnderDelivery", "Über-/Unterliefert", "kg|%", 34),
];
private static readonly (string, string, string?, int)[] FieldNamesRed = [
("MgNr", "MgNr.", null, 12),
("Name1", "Name", null, 40),
("Name2", "Vorname", null, 40),
("Address", "Adresse", null, 60),
("Plz", "PLZ", null, 10),
("Locality", "Ort", null, 60),
("SharesRed", "GA", null, 10),
("DeliveryObligationRed", "Lieferpflicht", "kg", 22),
("DeliveryRightRed", "Lieferrecht", "kg", 22),
("WeightRed", "Geliefert", "kg", 22),
("OverUnderDeliveryRed", "Über-/Unterliefert", "kg|%", 34),
];
private static readonly (string, string, string?, int)[] FieldNamesWhite = [
("MgNr", "MgNr.", null, 12),
("Name1", "Name", null, 40),
("Name2", "Vorname", null, 40),
("Address", "Adresse", null, 60),
("Plz", "PLZ", null, 10),
("Locality", "Ort", null, 60),
("SharesWhite", "GA", null, 10),
("DeliveryObligationWhite", "Lieferpflicht", "kg", 22),
("DeliveryRightWhite", "Lieferrecht", "kg", 22),
("WeightWhite", "Geliefert", "kg", 22),
("OverUnderDeliveryWhite", "Über-/Unterliefert", "kg|%", 34),
];
public OverUnderDeliveryData(IEnumerable<OverUnderDeliveryRow> rows, int year) :
base($"Über-Unterlieferungen", $"Über- und Unterlieferungen laut gezeichneten Geschäftsanteilen {year}", rows, FieldNames) {
}
public static async Task<OverUnderDeliveryData> ForSeason(DbSet<OverUnderDeliveryRow> table, int year) {
public OverUnderDeliveryData(IEnumerable<OverUnderDeliveryRow> rows, int year, string mode) :
base($"Über-Unterlieferungen-{(mode == "R" ? "rot" : "weiß")}", $"Über- und Unterlieferungen laut gezeichneten Geschäftsanteilen {(mode == "R" ? "rot" : "weiß")} {year}", rows,
mode == "R" ? FieldNamesRed : FieldNamesWhite) {
}
public static async Task<(OverUnderDeliveryData Total, OverUnderDeliveryData Red, OverUnderDeliveryData White)> ForSeason(DbSet<OverUnderDeliveryRow> table, int year) {
var rows = await table.FromSql($"""
SELECT m.mgnr, m.name AS name_1,
COALESCE(m.prefix || ' ', '') || m.given_name ||
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
p.plz, o.name AS ort, m.address, m.business_shares,
m.business_shares * s.min_kg_per_bs AS min_kg,
m.business_shares * s.max_kg_per_bs AS max_kg,
COALESCE(SUM(d.weight), 0) AS sum
p.plz, o.name AS ort, m.address, h.shares, h.shares_red, h.shares_white,
h.shares * COALESCE(s.min_kg_per_share, 0) AS min_kg,
h.shares * COALESCE(s.max_kg_per_share, 0) AS max_kg,
h.shares_red * COALESCE(s.min_kg_per_share_red, s.min_kg_per_share, 0) AS min_kg_red,
h.shares_red * COALESCE(s.max_kg_per_share_red, s.max_kg_per_share, 0) AS max_kg_red,
h.shares_white * COALESCE(s.min_kg_per_share_white, s.min_kg_per_share, 0) AS min_kg_white,
h.shares_white * COALESCE(s.max_kg_per_share_white, s.max_kg_per_share, 0) AS max_kg_white,
COALESCE(d.weight_total, 0) AS weight_total,
COALESCE(d.weight_red, 0) AS weight_red,
COALESCE(d.weight_white, 0) AS weight_white
FROM season s, member m
LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
LEFT JOIN AT_ort o ON o.okz = p.okz
LEFT JOIN v_delivery d ON (d.year, d.mgnr) = (s.year, m.mgnr)
WHERE s.year = {year} AND (m.active = TRUE OR d.weight > 0)
GROUP BY s.year, m.mgnr
ORDER BY 100.0 * sum / max_kg, m.mgnr
LEFT JOIN v_member_history h ON (h.year, h.mgnr) = (s.year, m.mgnr)
LEFT JOIN v_stat_member d ON (d.year, d.mgnr) = (s.year, m.mgnr)
WHERE s.year = {year} AND (m.active = TRUE OR d.weight_total > 0)
ORDER BY 100.0 * weight_total / (max_kg + max_kg_red + max_kg_white), m.mgnr
""").ToListAsync();
return new OverUnderDeliveryData(rows, year);
return (new OverUnderDeliveryData(rows, year), new OverUnderDeliveryData(rows, year, "R"), new OverUnderDeliveryData(rows, year, "W"));
}
}
@@ -61,17 +100,41 @@ namespace Elwig.Models.Dtos {
public required string LocalityFull { get; set; }
[NotMapped]
public string Locality => LocalityFull.Split(",")[0];
[Column("business_shares")]
public int BusinessShares { get; set; }
[Column("shares")]
public int Shares { get; set; }
[Column("shares_red")]
public int SharesRed { get; set; }
[Column("shares_white")]
public int SharesWhite { get; set; }
[Column("min_kg")]
public int DeliveryObligation { get; set; }
[Column("max_kg")]
public int DeliveryRight { get; set; }
[Column("sum")]
public int Weight { get; set; }
[NotMapped]
public (int? Kg, double? Percent) OverUnderDelivery =>
Weight < DeliveryObligation ? (Weight - DeliveryObligation, Weight * 100.0 / DeliveryObligation - 100.0) :
Weight > DeliveryRight ? (Weight - DeliveryRight, Weight * 100.0 / DeliveryRight - 100.0) : (null, null);
WeightTotal < DeliveryObligation ? (WeightTotal - DeliveryObligation, WeightTotal * 100.0 / DeliveryObligation - 100.0) :
WeightTotal > DeliveryRight ? (WeightTotal - DeliveryRight, WeightTotal * 100.0 / DeliveryRight - 100.0) : (null, null);
[Column("min_kg_red")]
public int DeliveryObligationRed { get; set; }
[Column("max_kg_red")]
public int DeliveryRightRed { get; set; }
[NotMapped]
public (int? Kg, double? Percent) OverUnderDeliveryRed =>
WeightRed < DeliveryObligationRed ? (WeightRed - DeliveryObligationRed, WeightRed * 100.0 / DeliveryObligationRed - 100.0) :
WeightRed > DeliveryRightRed ? (WeightRed - DeliveryRightRed, WeightRed * 100.0 / DeliveryRightRed - 100.0) : (null, null);
[Column("min_kg_white")]
public int DeliveryObligationWhite { get; set; }
[Column("max_kg_white")]
public int DeliveryRightWhite { get; set; }
[NotMapped]
public (int? Kg, double? Percent) OverUnderDeliveryWhite =>
WeightWhite < DeliveryObligationWhite ? (WeightWhite - DeliveryObligationWhite, WeightWhite * 100.0 / DeliveryObligationWhite - 100.0) :
WeightWhite > DeliveryRightWhite ? (WeightWhite - DeliveryRightWhite, WeightWhite * 100.0 / DeliveryRightWhite - 100.0) : (null, null);
[Column("weight_total")]
public int WeightTotal { get; set; }
[Column("weight_red")]
public int WeightRed { get; set; }
[Column("weight_white")]
public int WeightWhite { get; set; }
}
}
+22 -3
View File
@@ -42,6 +42,8 @@ namespace Elwig.Models.Entities {
[NotMapped]
public string ShortName => (!string.IsNullOrWhiteSpace(GivenName) ? $"{GivenName} " : "") + Name;
[NotMapped]
public string FullAdministrativeName => $"{AdministrativeName} ({MgNr})";
[NotMapped]
public string AdministrativeName => AdministrativeName1 + (!string.IsNullOrWhiteSpace(AdministrativeName2) ? $" {AdministrativeName2}" : "");
[NotMapped]
public string AdministrativeName1 => IsJuridicalPerson ? Name : Name.Replace('ß', 'ẞ').ToUpper();
@@ -67,8 +69,20 @@ namespace Elwig.Models.Entities {
set => ExitDateString = value?.ToString("yyyy-MM-dd");
}
[Column("business_shares")]
public int BusinessShares { get; set; }
[Column("shares")]
public int Shares { get; set; }
[Column("shares_red")]
public int SharesRed { get; set; }
[Column("shares_white")]
public int SharesWhite { get; set; }
[Column("shares_dormant")]
public int SharesDormant { get; set; }
[NotMapped]
public int SharesActive => Shares + SharesRed + SharesWhite;
[Column("accounting_nr")]
public string? AccountingNr { get; set; }
@@ -189,9 +203,14 @@ namespace Elwig.Models.Entities {
[InverseProperty(nameof(BillingAddr.Member))]
public virtual BillingAddr? BillingAddress { get; private set; }
[InverseProperty(nameof(Delivery.Member))]
[InverseProperty(nameof(DeliveryAncmt.Member))]
public virtual ICollection<DeliveryAncmt> Announcements { get; private set; } = null!;
[InverseProperty(nameof(MemberHistory.FromMember))]
public virtual ICollection<MemberHistory> HistoryFrom { get; private set; } = null!;
[InverseProperty(nameof(MemberHistory.ToMember))]
public virtual ICollection<MemberHistory> HistoryTo { get; private set; } = null!;
[InverseProperty(nameof(Delivery.Member))]
public virtual ICollection<Delivery> Deliveries { get; private set; } = null!;
+109 -13
View File
@@ -1,31 +1,127 @@
using Elwig.Helpers;
using Microsoft.EntityFrameworkCore;
using System;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models.Entities {
[Table("member_history"), PrimaryKey("MgNr", "DateString", "Type")]
[Table("member_history"), PrimaryKey("HistNr")]
public class MemberHistory {
[Column("mgnr")]
public int MgNr { get; set; }
[Column("date")]
public required string DateString { get; set; }
public const string REASON_PURCHASE = "purchase"; // Kauf
public const string REASON_CONVERT = "convert"; // Änderung
public const string REASON_TRANSFER = "transfer"; // Übertragung
public const string REASON_PRESCRIPTION = "prescription"; // Vorschreibung
public const string REASON_EXCLUSION = "exclusion"; // Ausschluss
public const string REASON_DECEASED = "deceased"; // Tod
public const string REASON_RETIREMENT = "retirement"; // Kündigung/stilllegen
public const string REASON_PAYOUT = "payout"; // Auszahlung
[Column("histnr")]
public int HistNr { get; set; }
[Column("from_mgnr")]
public int? FromMgNr { get; set; }
[Column("from_type")]
public int? FromTypeValue { get; set; }
[NotMapped]
public DateOnly Date {
get => DateOnly.ParseExact(DateString, "yyyy-MM-dd");
public BusinessShareType? FromType {
get => (BusinessShareType?)FromTypeValue;
set => FromTypeValue = (int?)value;
}
[Column("to_mgnr")]
public int? ToMgNr { get; set; }
[Column("to_type")]
public int? ToTypeValue { get; set; }
[NotMapped]
public BusinessShareType? ToType {
get => (BusinessShareType?)ToTypeValue;
set => ToTypeValue = (int?)value;
}
[Column("date_notice")]
public required string DateNoticeString { get; set; }
[NotMapped]
public DateOnly DateNotice {
get => DateOnly.ParseExact(DateNoticeString, "yyyy-MM-dd");
set => value.ToString("yyyy-MM-dd");
}
[Column("type")]
public required string Type { get; set; }
[Column("date_effective")]
public required string DateEffectiveString { get; set; }
[NotMapped]
public DateOnly DateEffective {
get => DateOnly.ParseExact(DateEffectiveString, "yyyy-MM-dd");
set => value.ToString("yyyy-MM-dd");
}
[Column("business_shares")]
public int BusinessShares { get; set; }
[Column("reason")]
public required string Reason { get; set; }
[NotMapped]
public string ReasonPretty => Reason switch {
REASON_PURCHASE => "Kauf",
REASON_CONVERT => "Änderung",
REASON_TRANSFER => "Übertragung",
REASON_PRESCRIPTION => "Vorschreibung",
REASON_EXCLUSION => "Ausschluss",
REASON_DECEASED => "Tod",
REASON_RETIREMENT => "Kündigung",
REASON_PAYOUT => "Auszahlung",
_ => Reason,
};
[Column("source")]
public required string Source { get; set; }
[Column("shares")]
public int Shares { get; set; }
[Column("signed")]
public bool MemberHasSigned { get; set; }
[Column("value_per_share")]
public long? ValuePerShareValue { get; set; }
[NotMapped]
public decimal? ValuePerShare {
get => ValuePerShareValue != null ? Utils.DecFromDb(ValuePerShareValue.Value, 2) : null;
set => ValuePerShareValue = value != null ? Utils.DecToDb(value.Value, 2) : null;
}
[NotMapped]
public decimal? TotalValue => Shares * ValuePerShare;
[Column("currency")]
public string? CurrencyCode { get; set; }
[Column("deduct_year")]
public int? DeductYear { get; set; }
[Column("comment")]
public string? Comment { get; set; }
[ForeignKey("MgNr")]
public virtual Member Member { get; private set; } = null!;
public string FormatReasonPhrase(int mgnr) {
if (Reason == REASON_TRANSFER) {
if (FromMgNr == mgnr) {
return $"{ReasonPretty} an {ToMember?.FullAdministrativeName ?? "Nachfolger"}" + (Comment != null ? $", {Comment}" : "");
} else if (ToMgNr == mgnr) {
return $"{ReasonPretty} von {FromMember?.FullAdministrativeName ?? "Vorgänger"}" + (Comment != null ? $", {Comment}" : "");
}
}
return ReasonPretty + (Comment != null ? $", {Comment}" : "");
}
[NotMapped]
public int? PoVMgNr { get; set; }
[NotMapped]
public string PoVReasonPhrase => FormatReasonPhrase(PoVMgNr ?? 0);
[ForeignKey("FromMgNr")]
public virtual Member? FromMember { get; private set; } = null!;
[ForeignKey("ToMgNr")]
public virtual Member? ToMember { get; private set; } = null!;
[ForeignKey("CurrencyCode")]
public virtual Currency Currency { get; private set; } = null!;
}
}
+24 -15
View File
@@ -25,11 +25,20 @@ namespace Elwig.Models.Entities {
[Column("vat_flatrate")]
public double VatFlatrate { get; set; }
[Column("min_kg_per_bs")]
public int MinKgPerBusinessShare { get; set; }
[Column("min_kg_per_share")]
public int? MinKgPerShare { get; set; }
[Column("max_kg_per_share")]
public int? MaxKgPerShare { get; set; }
[Column("max_kg_per_bs")]
public int MaxKgPerBusinessShare { get; set; }
[Column("min_kg_per_share_red")]
public int? MinKgPerShareRed { get; set; }
[Column("max_kg_per_share_red")]
public int? MaxKgPerShareRed { get; set; }
[Column("min_kg_per_share_white")]
public int? MinKgPerShareWhite { get; set; }
[Column("max_kg_per_share_white")]
public int? MaxKgPerShareWhite { get; set; }
[Column("penalty_per_kg")]
public long? PenaltyPerKgValue { get; set; }
@@ -55,23 +64,23 @@ namespace Elwig.Models.Entities {
set => PenaltyNoneValue = value != null ? DecToDb(value.Value) : null;
}
[Column("penalty_per_bs_amount")]
public long? PenaltyPerBsAmountValue { get; set; }
[Column("penalty_per_share_amount")]
public long? PenaltyPerShareAmountValue { get; set; }
[NotMapped]
public decimal? PenaltyPerBsAmount {
get => PenaltyPerBsAmountValue != null ? DecFromDb(PenaltyPerBsAmountValue.Value) : null;
set => PenaltyPerBsAmountValue = value != null ? DecToDb(value.Value) : null;
public decimal? PenaltyPerShareAmount {
get => PenaltyPerShareAmountValue != null ? DecFromDb(PenaltyPerShareAmountValue.Value) : null;
set => PenaltyPerShareAmountValue = value != null ? DecToDb(value.Value) : null;
}
[Column("penalty_per_bs_none")]
public long? PenaltyPerBsNoneValue { get; set; }
[Column("penalty_per_share_none")]
public long? PenaltyPerShareNoneValue { get; set; }
[NotMapped]
public decimal? PenaltyPerBsNone {
get => PenaltyPerBsNoneValue != null ? DecFromDb(PenaltyPerBsNoneValue.Value) : null;
set => PenaltyPerBsNoneValue = value != null ? DecToDb(value.Value) : null;
public decimal? PenaltyPerShareNone {
get => PenaltyPerShareNoneValue != null ? DecFromDb(PenaltyPerShareNoneValue.Value) : null;
set => PenaltyPerShareNoneValue = value != null ? DecToDb(value.Value) : null;
}
[Column("bs_value")]
[Column("share_value")]
public long? BusinessShareValueValue { get; set; }
[NotMapped]
public decimal? BusinessShareValue {
+8
View File
@@ -0,0 +1,8 @@
-- schema version 38 to 39
PRAGMA writable_schema = ON;
UPDATE sqlite_schema SET sql = REPLACE(sql, '{2,}', '{2,64}')
WHERE type = 'table' AND name = 'member_email_address';
PRAGMA writable_schema = OFF;
UPDATE wb_gem SET hkid = 'WLLB' WHERE gkz IN (10710, 10723);
+326
View File
@@ -0,0 +1,326 @@
-- schema version 39 to 40
PRAGMA writable_schema = ON;
ALTER TABLE member RENAME COLUMN business_shares TO shares;
ALTER TABLE member ADD COLUMN shares_red INTEGER NOT NULL DEFAULT 0 CHECK (shares_red >= 0);
ALTER TABLE member ADD COLUMN shares_white INTEGER NOT NULL DEFAULT 0 CHECK (shares_white >= 0);
ALTER TABLE member ADD COLUMN shares_dormant INTEGER NOT NULL DEFAULT 0 CHECK (shares_dormant >= 0);
UPDATE sqlite_schema SET sql = REPLACE(sql, 'shares INTEGER NOT NULL DEFAULT 0', 'shares INTEGER NOT NULL DEFAULT 0 CHECK (shares >= 0)')
WHERE type = 'table' AND name = 'member';
UPDATE client_parameter SET value = '0' WHERE param = 'ENABLE_TIME_TRIGGERS';
UPDATE member
SET shares_dormant = shares + shares_red + shares_white,
shares = 0, shares_red = 0, shares_white = 0
WHERE NOT active;
UPDATE client_parameter SET value = '1' WHERE param = 'ENABLE_TIME_TRIGGERS';
CREATE TABLE member_history_new (
histnr INTEGER NOT NULL,
from_mgnr INTEGER,
from_type INTEGER,
to_mgnr INTEGER,
to_type INTEGER,
date_notice TEXT NOT NULL CHECK (date_notice REGEXP '^[1-9][0-9]{3}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$') DEFAULT CURRENT_DATE,
date_effective TEXT NOT NULL CHECK (date_effective REGEXP '^[1-9][0-9]{3}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$') DEFAULT CURRENT_DATE,
reason TEXT NOT NULL CHECK (reason REGEXP '^[a-z_]+$'),
source TEXT NOT NULL CHECK (source REGEXP '^[a-z_]+$'),
shares INTEGER NOT NULL CHECK (shares > 0),
signed INTEGER NOT NULL CHECK (signed IN (TRUE, FALSE)) DEFAULT FALSE,
value_per_share INTEGER DEFAULT NULL,
currency TEXT DEFAULT NULL,
deduct_year INTEGER DEFAULT NULL,
comment TEXT DEFAULT NULL,
CONSTRAINT pk_member_history PRIMARY KEY (histnr),
CONSTRAINT fk_member_history_member_from FOREIGN KEY (from_mgnr) REFERENCES member (mgnr)
ON UPDATE CASCADE
ON DELETE RESTRICT,
CONSTRAINT fk_member_history_member_to FOREIGN KEY (to_mgnr) REFERENCES member (mgnr)
ON UPDATE CASCADE
ON DELETE RESTRICT,
CONSTRAINT fk_member_history_currency FOREIGN KEY (currency) REFERENCES currency (code)
ON UPDATE CASCADE
ON DELETE RESTRICT,
CONSTRAINT c_member_history CHECK ((from_mgnr IS NOT NULL OR to_mgnr IS NOT NULL) AND
((from_mgnr IS NULL AND from_type IS NULL) OR (from_mgnr IS NOT NULL AND from_type IS NOT NULL)) AND
((to_mgnr IS NULL AND to_type IS NULL) OR (to_mgnr IS NOT NULL AND to_type IS NOT NULL)))
) STRICT;
INSERT INTO member_history_new (histnr, from_mgnr, from_type, to_mgnr, to_type, date_notice, date_effective, reason, source, shares, value_per_share, currency, deduct_year, comment)
SELECT ROW_NUMBER() OVER(ORDER BY h.date, h.mgnr), NULL, NULL, h.mgnr, 1, h.date, h.date,
IIF(h.type = 'auto', 'prescription', h.type), 'elwig', h.business_shares, s.bs_value / POW(10, s.precision - 2), s.currency,
IIF(h.type = 'auto', s.year, NULL), 'automatische Nachzeichnung'
FROM member_history h
JOIN season s ON s.year = SUBSTR(h.date, 1, 4);
PRAGMA foreign_keys = OFF;
DROP TABLE member_history;
ALTER TABLE member_history_new RENAME TO member_history;
PRAGMA foreign_keys = ON;
CREATE TRIGGER t_member_history_i_member
AFTER INSERT ON member_history FOR EACH ROW
WHEN (SELECT value FROM client_parameter WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS') = 1
BEGIN
UPDATE member SET shares = shares - NEW.shares WHERE mgnr = NEW.from_mgnr AND NEW.from_type = 1;
UPDATE member SET shares_red = shares_red - NEW.shares WHERE mgnr = NEW.from_mgnr AND NEW.from_type = 2;
UPDATE member SET shares_white = shares_white - NEW.shares WHERE mgnr = NEW.from_mgnr AND NEW.from_type = 3;
UPDATE member SET shares_dormant = shares_dormant - NEW.shares WHERE mgnr = NEW.from_mgnr AND NEW.from_type = 9;
UPDATE member SET shares = shares + NEW.shares WHERE mgnr = NEW.to_mgnr AND NEW.to_type = 1;
UPDATE member SET shares_red = shares_red + NEW.shares WHERE mgnr = NEW.to_mgnr AND NEW.to_type = 2;
UPDATE member SET shares_white = shares_white + NEW.shares WHERE mgnr = NEW.to_mgnr AND NEW.to_type = 3;
UPDATE member SET shares_dormant = shares_dormant + NEW.shares WHERE mgnr = NEW.to_mgnr AND NEW.to_type = 9;
END;
CREATE TRIGGER t_member_history_u_member
AFTER UPDATE OF from_mgnr, from_type, to_mgnr, to_type, shares ON member_history FOR EACH ROW
WHEN (SELECT value FROM client_parameter WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS') = 1
BEGIN
UPDATE member SET shares = shares + OLD.shares WHERE mgnr = OLD.from_mgnr AND OLD.from_type = 1;
UPDATE member SET shares_red = shares_red + OLD.shares WHERE mgnr = OLD.from_mgnr AND OLD.from_type = 2;
UPDATE member SET shares_white = shares_white + OLD.shares WHERE mgnr = OLD.from_mgnr AND OLD.from_type = 3;
UPDATE member SET shares_dormant = shares_dormant + OLD.shares WHERE mgnr = OLD.from_mgnr AND OLD.from_type = 9;
UPDATE member SET shares = shares - OLD.shares WHERE mgnr = OLD.to_mgnr AND OLD.to_type = 1;
UPDATE member SET shares_red = shares_red - OLD.shares WHERE mgnr = OLD.to_mgnr AND OLD.to_type = 2;
UPDATE member SET shares_white = shares_white - OLD.shares WHERE mgnr = OLD.to_mgnr AND OLD.to_type = 3;
UPDATE member SET shares_dormant = shares_dormant - OLD.shares WHERE mgnr = OLD.to_mgnr AND OLD.to_type = 9;
UPDATE member SET shares = shares - NEW.shares WHERE mgnr = NEW.from_mgnr AND NEW.from_type = 1;
UPDATE member SET shares_red = shares_red - NEW.shares WHERE mgnr = NEW.from_mgnr AND NEW.from_type = 2;
UPDATE member SET shares_white = shares_white - NEW.shares WHERE mgnr = NEW.from_mgnr AND NEW.from_type = 3;
UPDATE member SET shares_dormant = shares_dormant - NEW.shares WHERE mgnr = NEW.from_mgnr AND NEW.from_type = 9;
UPDATE member SET shares = shares + NEW.shares WHERE mgnr = NEW.to_mgnr AND NEW.to_type = 1;
UPDATE member SET shares_red = shares_red + NEW.shares WHERE mgnr = NEW.to_mgnr AND NEW.to_type = 2;
UPDATE member SET shares_white = shares_white + NEW.shares WHERE mgnr = NEW.to_mgnr AND NEW.to_type = 3;
UPDATE member SET shares_dormant = shares_dormant + NEW.shares WHERE mgnr = NEW.to_mgnr AND NEW.to_type = 9;
END;
CREATE TRIGGER t_member_history_d_member
AFTER DELETE ON member_history FOR EACH ROW
WHEN (SELECT value FROM client_parameter WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS') = 1
BEGIN
UPDATE member SET shares = shares + OLD.shares WHERE mgnr = OLD.from_mgnr AND OLD.from_type = 1;
UPDATE member SET shares_red = shares_red + OLD.shares WHERE mgnr = OLD.from_mgnr AND OLD.from_type = 2;
UPDATE member SET shares_white = shares_white + OLD.shares WHERE mgnr = OLD.from_mgnr AND OLD.from_type = 3;
UPDATE member SET shares_dormant = shares_dormant + OLD.shares WHERE mgnr = OLD.from_mgnr AND OLD.from_type = 9;
UPDATE member SET shares = shares - OLD.shares WHERE mgnr = OLD.to_mgnr AND OLD.to_type = 1;
UPDATE member SET shares_red = shares_red - OLD.shares WHERE mgnr = OLD.to_mgnr AND OLD.to_type = 2;
UPDATE member SET shares_white = shares_white - OLD.shares WHERE mgnr = OLD.to_mgnr AND OLD.to_type = 3;
UPDATE member SET shares_dormant = shares_dormant - OLD.shares WHERE mgnr = OLD.to_mgnr AND OLD.to_type = 9;
END;
INSERT INTO client_parameter (param, value) VALUES ('ENABLE_MEMBER_HISTORY_TRIGGERS', '1');
CREATE TABLE season_new (
year INTEGER NOT NULL CHECK (year >= 1000 AND year <= 9999),
currency TEXT NOT NULL,
precision INTEGER NOT NULL DEFAULT 4,
max_kg_per_ha INTEGER NOT NULL DEFAULT 10000,
vat_normal REAL NOT NULL DEFAULT 0.10,
vat_flatrate REAL NOT NULL DEFAULT 0.13,
min_kg_per_share INTEGER DEFAULT NULL,
max_kg_per_share INTEGER DEFAULT NULL,
min_kg_per_share_red INTEGER DEFAULT NULL,
max_kg_per_share_red INTEGER DEFAULT NULL,
min_kg_per_share_white INTEGER DEFAULT NULL,
max_kg_per_share_white INTEGER DEFAULT NULL,
penalty_per_kg INTEGER DEFAULT NULL,
penalty_amount INTEGER DEFAULT NULL,
penalty_none INTEGER DEFAULT NULL,
penalty_per_share_amount INTEGER DEFAULT NULL,
penalty_per_share_none INTEGER DEFAULT NULL,
share_value INTEGER,
start_date TEXT CHECK (start_date REGEXP '^[1-9][0-9]{3}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$'),
end_date TEXT CHECK (end_date REGEXP '^[1-9][0-9]{3}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$'),
calc_mode INTEGER NOT NULL DEFAULT 0,
CONSTRAINT pk_season PRIMARY KEY (year),
CONSTRAINT fk_season_currency FOREIGN KEY (currency) REFERENCES currency (code)
ON UPDATE CASCADE
ON DELETE RESTRICT
) STRICT;
INSERT INTO season_new (year, currency, precision, max_kg_per_ha, vat_normal, vat_flatrate, min_kg_per_share, max_kg_per_share, penalty_per_kg, penalty_amount, penalty_none, penalty_per_share_amount, penalty_per_share_none, share_value, start_date, end_date, calc_mode)
SELECT year, currency, precision, max_kg_per_ha, vat_normal, vat_flatrate, min_kg_per_bs, max_kg_per_bs, penalty_per_kg, penalty_amount, penalty_none, penalty_per_bs_amount, penalty_per_bs_none, bs_value, start_date, end_date, calc_mode
FROM season;
PRAGMA foreign_keys = OFF;
DROP TABLE season;
ALTER TABLE season_new RENAME TO season;
PRAGMA foreign_keys = ON;
DROP VIEW v_delivery;
CREATE VIEW v_delivery AS
SELECT p.year, p.did, p.dpnr,
d.date, d.time, d.zwstid, d.lnr, d.lsnr,
m.mgnr, m.name, m.given_name,
v.sortid, v.type, a.attrid, p.cultid,
p.weight, p.kmw, ROUND(p.kmw * (4.54 + 0.022 * p.kmw), 0) AS oe, p.qualid,
p.hkid, p.kgnr, p.rdnr,
p.net_weight, p.gebunden,
p.qualid IN (SELECT l.qualid FROM wine_quality_level l WHERE NOT l.predicate AND (p.kmw >= l.min_kmw OR l.min_kmw IS NULL) ORDER BY l.min_kmw DESC LIMIT 1,100) AS abgewertet,
p.qualid NOT IN ('WEI', 'RSW', 'LDW') AS min_quw,
IIF(a.strict, COALESCE(a.fill_lower, 0), 0) AS attribute_prio,
GROUP_CONCAT(o.modid) AS modifiers,
d.comment, p.comment AS part_comment
FROM delivery_part p
JOIN delivery d ON (d.year, d.did) = (p.year, p.did)
JOIN member m ON m.mgnr = d.mgnr
LEFT JOIN wine_variety v ON v.sortid = p.sortid
LEFT JOIN wine_attribute a ON a.attrid = p.attrid
LEFT JOIN delivery_part_modifier o ON (o.year, o.did, o.dpnr) = (p.year, p.did, p.dpnr)
GROUP BY p.year, p.did, p.dpnr
ORDER BY p.year, p.did, p.dpnr, o.modid;
DROP VIEW v_stat_season;
CREATE VIEW v_stat_season AS
SELECT year,
SUM(weight) AS weight_total,
SUM(IIF(type = 'R', weight, 0)) AS weight_red,
SUM(IIF(type = 'W', weight, 0)) AS weight_white,
ROUND(SUM(kmw * weight) / SUM(weight), 2) AS kmw,
ROUND(SUM(oe * weight) / SUM(weight), 1) AS oe,
COUNT(DISTINCT did) AS lieferungen,
COUNT(DISTINCT mgnr) AS mitglieder
FROM v_delivery
GROUP BY year
ORDER BY year;
DROP VIEW v_stat_sort;
CREATE VIEW v_stat_variety AS
SELECT year, sortid,
SUM(weight) AS weight_total,
SUM(IIF(type = 'R', weight, 0)) AS weight_red,
SUM(IIF(type = 'W', weight, 0)) AS weight_white,
ROUND(SUM(kmw * weight) / SUM(weight), 2) AS kmw,
ROUND(SUM(oe * weight) / SUM(weight), 1) AS oe,
COUNT(DISTINCT did) AS lieferungen,
COUNT(DISTINCT mgnr) AS mitglieder
FROM v_delivery
GROUP BY year, sortid
ORDER BY year, sortid;
DROP VIEW v_stat_attr;
CREATE VIEW v_stat_attr AS
SELECT year, attrid,
SUM(weight) AS weight_total,
SUM(IIF(type = 'R', weight, 0)) AS weight_red,
SUM(IIF(type = 'W', weight, 0)) AS weight_white,
ROUND(SUM(kmw * weight) / SUM(weight), 2) AS kmw,
ROUND(SUM(oe * weight) / SUM(weight), 1) AS oe,
COUNT(DISTINCT did) AS lieferungen,
COUNT(DISTINCT mgnr) AS mitglieder
FROM v_delivery
GROUP BY year, attrid
ORDER BY year, attrid;
DROP VIEW v_stat_sort_attr;
CREATE VIEW v_stat_variety_attr AS
SELECT year, sortid, attrid,
SUM(weight) AS weight_total,
SUM(IIF(type = 'R', weight, 0)) AS weight_red,
SUM(IIF(type = 'W', weight, 0)) AS weight_white,
ROUND(SUM(kmw * weight) / SUM(weight), 2) AS kmw,
ROUND(SUM(oe * weight) / SUM(weight), 1) AS oe,
COUNT(DISTINCT did) AS lieferungen,
COUNT(DISTINCT mgnr) AS mitglieder
FROM v_delivery
GROUP BY year, sortid, attrid
ORDER BY year, sortid, attrid;
DROP VIEW v_stat_member;
CREATE VIEW v_stat_member AS
SELECT year, mgnr,
SUM(weight) AS weight_total,
SUM(IIF(type = 'R', weight, 0)) AS weight_red,
SUM(IIF(type = 'W', weight, 0)) AS weight_white,
ROUND(SUM(kmw * weight) / SUM(weight), 2) AS kmw,
ROUND(SUM(oe * weight) / SUM(weight), 1) AS oe,
COUNT(DISTINCT did) AS lieferungen
FROM v_delivery
GROUP BY year, mgnr
ORDER BY year, mgnr;
CREATE VIEW v_member_history AS
SELECT m.mgnr, s.year,
m.shares + SUM(IIF(h1.from_type = 1, h1.shares, 0)) - SUM(IIF(h2.to_type = 1, h2.shares, 0)) AS shares,
m.shares_red + SUM(IIF(h1.from_type = 2, h1.shares, 0)) - SUM(IIF(h2.to_type = 2, h2.shares, 0)) AS shares_red,
m.shares_white + SUM(IIF(h1.from_type = 3, h1.shares, 0)) - SUM(IIF(h2.to_type = 3, h2.shares, 0)) AS shares_white,
m.shares_dormant + SUM(IIF(h1.from_type = 9, h1.shares, 0)) - SUM(IIF(h2.to_type = 9, h2.shares, 0)) AS shares_dormant
FROM member m, v_virtual_season s
LEFT JOIN member_history h1 ON (SUBSTR(h1.date_notice, 1, 4) + 0) > s.year AND h1.from_mgnr = m.mgnr
LEFT JOIN member_history h2 ON (SUBSTR(h2.date_notice, 1, 4) + 0) > s.year AND h2.to_mgnr = m.mgnr
GROUP BY m.mgnr, s.year
ORDER BY m.mgnr, s.year;
DROP VIEW v_total_under_delivery;
CREATE VIEW v_total_under_delivery AS
SELECT s.year, m.mgnr,
h.shares,
h.shares * COALESCE(s.min_kg_per_share, 0) AS min_kg,
h.shares * COALESCE(s.max_kg_per_share, 0) AS max_kg,
COALESCE(d.weight_total, 0) AS weight_total,
IIF(COALESCE(d.weight_total, 0) < h.shares * COALESCE(s.min_kg_per_share, 0),
COALESCE(d.weight_total, 0) - h.shares * COALESCE(s.min_kg_per_share, 0),
IIF(COALESCE(d.weight_total, 0) > h.shares * COALESCE(s.max_kg_per_share, 0),
COALESCE(d.weight_total, 0) - h.shares * COALESCE(s.max_kg_per_share, 0),
0)) AS diff,
h.shares_red,
h.shares_red * COALESCE(s.min_kg_per_share_red, s.min_kg_per_share, 0) AS min_kg_red,
h.shares_red * COALESCE(s.max_kg_per_share_red, s.max_kg_per_share, 0) AS max_kg_red,
COALESCE(d.weight_red, 0) AS weight_red,
IIF(COALESCE(d.weight_red, 0) < h.shares_red * COALESCE(s.min_kg_per_share_red, s.min_kg_per_share, 0),
COALESCE(d.weight_red, 0) - h.shares_red * COALESCE(s.min_kg_per_share_red, s.min_kg_per_share, 0),
IIF(COALESCE(d.weight_red, 0) > h.shares_red * COALESCE(s.max_kg_per_share_red, s.max_kg_per_share, 0),
COALESCE(d.weight_red, 0) - h.shares_red * COALESCE(s.max_kg_per_share_red, s.max_kg_per_share, 0),
0)) AS diff_red,
h.shares_white,
h.shares_white * COALESCE(s.min_kg_per_share_white, s.min_kg_per_share, 0) AS min_kg_white,
h.shares_white * COALESCE(s.max_kg_per_share_white, s.max_kg_per_share, 0) AS max_kg_white,
COALESCE(d.weight_white, 0) AS weight_white,
IIF(COALESCE(d.weight_white, 0) < h.shares_white * COALESCE(s.min_kg_per_share_white, s.min_kg_per_share, 0),
COALESCE(d.weight_white, 0) - h.shares_white * COALESCE(s.min_kg_per_share_white, s.min_kg_per_share, 0),
IIF(COALESCE(d.weight_white, 0) > h.shares_white * COALESCE(s.max_kg_per_share_white, s.max_kg_per_share, 0),
COALESCE(d.weight_white, 0) - h.shares_white * COALESCE(s.max_kg_per_share_white, s.max_kg_per_share, 0),
0)) AS diff_white
FROM member m, season s
LEFT JOIN v_member_history h ON (h.year, h.mgnr) = (s.year, m.mgnr)
LEFT JOIN v_stat_member d ON (d.year, d.mgnr) = (s.year, m.mgnr)
ORDER BY s.year, m.mgnr;
DROP VIEW v_penalty_business_shares;
CREATE VIEW v_penalty_business_shares AS
SELECT u.year, u.mgnr,
SUM(IIF(u.weight_total = 0, COALESCE(-s.penalty_none, 0) + COALESCE(-(u.shares + u.shares_red + u.shares_white) * s.penalty_per_share_none, 0), 0) +
IIF(u.diff < 0 OR u.diff_red < 0 OR u.diff_white < 0, COALESCE(-s.penalty_amount, 0), 0) +
COALESCE((u.diff + u.diff_red + u.diff_white) * s.penalty_per_kg, 0) +
COALESCE(CEIL(CAST(u.diff AS REAL) / s.min_kg_per_share) * s.penalty_per_share_amount, 0) +
COALESCE(CEIL(CAST(u.diff_red AS REAL) / s.min_kg_per_share_red) * s.penalty_per_share_amount, 0) +
COALESCE(CEIL(CAST(u.diff_white AS REAL) / s.min_kg_per_share_white) * s.penalty_per_share_amount, 0)
) AS total_penalty
FROM v_total_under_delivery u
JOIN season s ON u.year = s.year
JOIN member m ON m.mgnr = u.mgnr
WHERE m.active
GROUP BY u.year, u.mgnr
HAVING total_penalty < 0
ORDER BY u.year, u.mgnr;
DROP VIEW v_auto_business_shares;
CREATE VIEW v_auto_business_shares AS
SELECT (SUBSTR(h.date_notice, 1, 4) + 0) AS year,
h.to_mgnr AS mgnr,
SUM(h.shares) AS shares,
SUM(h.shares * COALESCE(h.value_per_share, 0)) AS total_amount
FROM member_history h
WHERE h.reason = 'prescription' AND h.source = 'elwig'
GROUP BY year, h.to_mgnr
ORDER BY year, h.to_mgnr;
PRAGMA writable_schema = OFF;
+8 -21
View File
@@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows;
namespace Elwig.Services {
public static class AreaComService {
@@ -115,7 +114,7 @@ namespace Elwig.Services {
using var ctx = new AppDbContext();
var c = new AreaComContract {
FbNr = oldFbNr ?? newFbNr,
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
Comment = string.IsNullOrWhiteSpace(vm.Comment) ? null : vm.Comment,
KgNr = vm.Kg!.KgNr,
RdNr = vm.Rd?.RdNr,
};
@@ -158,25 +157,13 @@ namespace Elwig.Services {
});
}
private static void AddToolTipCell(Grid grid, string text, int row, int col, int colSpan = 1, bool bold = false, bool alignRight = false, bool alignCenter = false) {
var tb = new TextBlock() {
Text = text,
TextAlignment = alignRight ? TextAlignment.Right : alignCenter ? TextAlignment.Center : TextAlignment.Left,
Margin = new(0, 12 * row, 0, 0),
FontWeight = bold ? FontWeights.Bold : FontWeights.Normal,
};
tb.SetValue(Grid.ColumnProperty, col);
tb.SetValue(Grid.ColumnSpanProperty, colSpan);
grid.Children.Add(tb);
}
private static void AddToolTipRow(Grid grid, int row, string? h1, string? h2, int area, int? min, int? max) {
var bold = h2 == null;
if (h1 != null) AddToolTipCell(grid, h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold);
if (h2 != null) AddToolTipCell(grid, h2 + ":", row, 1, 1, bold);
AddToolTipCell(grid, $"{area:N0} m²", row, 2, 1, bold, true);
AddToolTipCell(grid, min == null ? "" : $"{min:N0} kg", row, 3, 1, bold, true);
AddToolTipCell(grid, max == null ? "" : $"{max:N0} kg", row, 4, 1, bold, true);
if (h1 != null) grid.AddToolTipCell(h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold);
if (h2 != null) grid.AddToolTipCell(h2 + ":", row, 1, bold: bold);
grid.AddToolTipCell($"{area:N0} m²", row, 2, bold: bold, alignRight: true);
grid.AddToolTipCell(min == null ? "" : $"{min:N0} kg", row, 3, bold: bold, alignRight: true);
grid.AddToolTipCell(max == null ? "" : $"{max:N0} kg", row, 4, bold: bold, alignRight: true);
}
public static async Task<(string, (string?, string?, int, int?, int?)[])> GenerateToolTipData(IQueryable<AreaCom> areaComs, int maxKgPerHa) {
@@ -255,8 +242,8 @@ namespace Elwig.Services {
grid.ColumnDefinitions.Add(new() { Width = new(80) });
grid.ColumnDefinitions.Add(new() { Width = new(80) });
grid.ColumnDefinitions.Add(new() { Width = new(80) });
AddToolTipCell(grid, "Lieferpflicht", 0, 3, 1, false, false, true);
AddToolTipCell(grid, "Lieferrecht", 0, 4, 1, false, false, true);
grid.AddToolTipCell("Lieferpflicht", 0, 3, alignCenter: true);
grid.AddToolTipCell("Lieferrecht", 0, 4, 1, alignCenter: true);
int rowNum = 1;
foreach (var row in data) {
if (rowNum == 2 || (rowNum != 1 && row.Item1 != null)) rowNum++;
+15 -47
View File
@@ -8,9 +8,6 @@ using Microsoft.EntityFrameworkCore;
using Elwig.Documents;
using Elwig.Helpers.Export;
using Elwig.Models.Dtos;
using Microsoft.Win32;
using System.Windows.Input;
using System.Windows;
using System;
using LinqKit;
using System.Windows.Controls;
@@ -261,61 +258,32 @@ namespace Elwig.Services {
.ThenBy(a => a.Member.MgNr);
if (mode == ExportMode.SaveList) {
var d = new SaveFileDialog() {
FileName = $"{DeliveryAncmtList.Name}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"{DeliveryAncmtList.Name} speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var data = await DeliveryAncmtListData.FromQuery(query, filterNames);
using var ods = new OdsFile(d.FileName);
await ods.AddTable(data);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
var filename = InteractionService.SaveFile(DeliveryAncmtList.Name, DeliveryAncmtList.Name, "ods");
if (filename != null) {
await Utils.RunForeground(async () => {
var data = await DeliveryAncmtListData.FromQuery(query, filterNames);
using var ods = new OdsFile(filename);
await ods.AddTable(data);
});
Mouse.OverrideCursor = null;
}
} else {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var data = await DeliveryAncmtListData.FromQuery(query, filterNames);
using var doc = new DeliveryAncmtList(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
await Utils.RunForeground(async () => {
var data = await DeliveryAncmtListData.FromQuery(query, filterNames);
using var doc = new DeliveryAncmtList(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
});
Mouse.OverrideCursor = null;
}
}
private static void AddToolTipCell(Grid grid, string text, int row, int col, int colSpan = 1, bool bold = false, bool alignRight = false, bool alignCenter = false) {
var tb = new TextBlock() {
Text = text,
TextAlignment = alignRight ? TextAlignment.Right : alignCenter ? TextAlignment.Center : TextAlignment.Left,
Margin = new(0, 12 * row, 0, 0),
FontWeight = bold ? FontWeights.Bold : FontWeights.Normal,
};
tb.SetValue(Grid.ColumnProperty, col);
tb.SetValue(Grid.ColumnSpanProperty, colSpan);
grid.Children.Add(tb);
}
private static void AddToolTipRow(Grid grid, int row, string? h1, string? h2, int weight, int? total1, int total2) {
var bold = h2 == null;
if (h1 != null) AddToolTipCell(grid, h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold);
if (h2 != null) AddToolTipCell(grid, h2 + ":", row, 1, 1, bold);
AddToolTipCell(grid, $"{weight:N0} kg", row, 2, 1, bold, true);
if (h1 != null) grid.AddToolTipCell(h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold);
if (h2 != null) grid.AddToolTipCell(h2 + ":", row, 1, 1, bold);
grid.AddToolTipCell($"{weight:N0} kg", row, 2, bold: bold, alignRight: true);
if (total1 != null && total1 != 0)
AddToolTipCell(grid, $"{weight * 100.0 / total1:N1} %", row, 3, 1, bold, true);
grid.AddToolTipCell($"{weight * 100.0 / total1:N1} %", row, 3, bold: bold, alignRight: true);
if (total2 != 0)
AddToolTipCell(grid, $"{weight * 100.0 / total2:N1} %", row, 4, 1, bold, true);
grid.AddToolTipCell($"{weight * 100.0 / total2:N1} %", row, 4, bold: bold, alignRight: true);
}
public static async Task<(string Text, (string?, string?, int, int?, int)[] Grid)> GenerateToolTipData(IQueryable<DeliveryAncmt> deliveryAncmts) {
+97 -212
View File
@@ -3,12 +3,9 @@ using Elwig.Helpers.Export;
using Elwig.Helpers;
using Elwig.Models.Dtos;
using Elwig.Models.Entities;
using Microsoft.Win32;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows;
using System;
using Elwig.ViewModels;
using LinqKit;
@@ -25,15 +22,6 @@ namespace Elwig.Services {
FromFilters, FromToday, FromSeason, FromSeasonAndBranch, Selected,
};
public static async Task<Member?> GetMemberAsync(int mgnr) {
using var ctx = new AppDbContext();
return await ctx.FetchMembers(mgnr).SingleOrDefaultAsync();
}
public static Member? GetMember(int mgnr) {
return GetMemberAsync(mgnr).GetAwaiter().GetResult();
}
public static void ClearInputs(this DeliveryAdminViewModel vm) {
}
@@ -488,7 +476,7 @@ namespace Elwig.Services {
if (partNew && timeIsDefault) {
newTimeString = DateTime.Now.ToString("HH:mm:ss");
} else if (partNew || timeHasChanged) {
newTimeString = string.IsNullOrEmpty(vm.Time) ? null : vm.Time + ":00";
newTimeString = string.IsNullOrWhiteSpace(vm.Time) ? null : vm.Time + ":00";
}
var d = new Delivery {
@@ -500,7 +488,7 @@ namespace Elwig.Services {
ZwstId = vm.Branch!.ZwstId,
LsNr = newLsNr ?? vm.LsNr!,
MgNr = vm.MgNr!.Value,
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
Comment = string.IsNullOrWhiteSpace(vm.Comment) ? null : vm.Comment,
};
p = new DeliveryPart {
@@ -523,7 +511,7 @@ namespace Elwig.Services {
Unloading = vm.Unloading,
Temperature = vm.Temperature,
Acid = vm.Acid,
Comment = string.IsNullOrEmpty(vm.PartComment) ? null : vm.PartComment,
Comment = string.IsNullOrWhiteSpace(vm.PartComment) ? null : vm.PartComment,
Weight = vm.Weight!.Value,
IsManualWeighing = vm.IsManualWeighing,
@@ -727,16 +715,10 @@ namespace Elwig.Services {
}
public static async Task GenerateDeliveryNote(int year, int did, ExportMode mode) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
using var doc = await DeliveryNote.Initialize(year, did);
await Utils.ExportDocument(doc, mode, doc.Delivery.LsNr, (doc.Member, $"{DeliveryNote.Name} Nr. {doc.Delivery.LsNr}", $"Im Anhang finden Sie den {DeliveryNote.Name} Nr. {doc.Delivery.LsNr}"));
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
await Utils.RunForeground(async () => {
using var doc = await DeliveryNote.Initialize(year, did);
await Utils.ExportDocument(doc, mode, doc.Delivery.LsNr, (doc.Member, $"{DeliveryNote.Name} Nr. {doc.Delivery.LsNr}", $"Im Anhang finden Sie den {DeliveryNote.Name} Nr. {doc.Delivery.LsNr}"));
});
Mouse.OverrideCursor = null;
}
public static async Task GenerateDeliveryJournal(this DeliveryAdminViewModel vm, ExportSubject subject, ExportMode mode) {
@@ -773,75 +755,44 @@ namespace Elwig.Services {
.ThenBy(p => p.DPNr);
if (mode == ExportMode.SaveList) {
var d = new SaveFileDialog() {
FileName = $"{DeliveryJournal.Name}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"{DeliveryJournal.Name} speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var data = await DeliveryJournalData.FromQuery(query, filterNames);
using var ods = new OdsFile(d.FileName);
await ods.AddTable(data);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
var filename = InteractionService.SaveFile(DeliveryJournal.Name, DeliveryJournal.Name, "ods");
if (filename != null) {
await Utils.RunForeground(async () => {
var data = await DeliveryJournalData.FromQuery(query, filterNames);
using var ods = new OdsFile(filename);
await ods.AddTable(data);
});
Mouse.OverrideCursor = null;
}
} else if (mode == ExportMode.Export) {
var d = new SaveFileDialog() {
FileName = subject == ExportSubject.Selected ? $"Lieferung_{vm.SelectedDelivery?.LsNr}.elwig.zip" : $"Lieferungen_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip",
DefaultExt = "elwig.zip",
Filter = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip",
Title = $"{DeliveryJournal.Name} speichern unter - Elwig",
AddExtension = false,
};
if (d.ShowDialog() == true) {
if (!d.FileName.EndsWith(".elwig.zip")) d.FileName += ".elwig.zip";
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var list = await query
.Select(p => p.Delivery)
.Distinct()
.Include(d => d.Parts).ThenInclude(p => p.PartModifiers)
.ToListAsync();
var wbKgs = list
.SelectMany(d => d.Parts)
.Where(p => p.Kg != null)
.Select(p => p.Kg!)
.DistinctBy(k => k.KgNr)
.OrderBy(k => k.KgNr)
.ToList();
await ElwigData.Export(d.FileName, list, wbKgs, filterNames);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
var filename = InteractionService.SaveFile(DeliveryJournal.Name, subject == ExportSubject.Selected ? $"Lieferung_{vm.SelectedDelivery?.LsNr}" : $"Lieferungen_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}", "elwig.zip");
if (filename != null) {
if (!filename.EndsWith(".elwig.zip")) filename += ".elwig.zip";
await Utils.RunForeground(async () => {
var list = await query
.Select(p => p.Delivery)
.Distinct()
.Include(d => d.Parts).ThenInclude(p => p.PartModifiers)
.ToListAsync();
var wbKgs = list
.SelectMany(d => d.Parts)
.Where(p => p.Kg != null)
.Select(p => p.Kg!)
.DistinctBy(k => k.KgNr)
.OrderBy(k => k.KgNr)
.ToList();
await ElwigData.Export(filename, list, wbKgs, filterNames);
});
Mouse.OverrideCursor = null;
}
} else if (mode == ExportMode.Upload && App.Config.SyncUrl != null) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
await Utils.RunForeground(async () => {
await SyncService.Upload(App.Config.SyncUrl, App.Config.SyncUrl, App.Config.SyncPassword, query, filterNames);
});
Mouse.OverrideCursor = null;
} else {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var data = await DeliveryJournalData.FromQuery(query, filterNames);
using var doc = new DeliveryJournal(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
await Utils.RunForeground(async () => {
var data = await DeliveryJournalData.FromQuery(query, filterNames);
using var doc = new DeliveryJournal(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
});
Mouse.OverrideCursor = null;
}
}
@@ -862,17 +813,11 @@ namespace Elwig.Services {
throw new ArgumentException("Invalid value for ExportSubject");
}
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var data = await WineQualityStatisticsData.FromQuery(query, App.Client.OrderingMemberList);
using var doc = new WineQualityStatistics(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
await Utils.RunForeground(async () => {
var data = await WineQualityStatisticsData.FromQuery(query, App.Client.OrderingMemberList);
using var doc = new WineQualityStatistics(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
});
Mouse.OverrideCursor = null;
}
public static async Task GenerateLocalityStatistics(this DeliveryAdminViewModel vm, ExportSubject subject) {
@@ -888,24 +833,13 @@ namespace Elwig.Services {
throw new ArgumentException("Invalid value for ExportSubject");
}
var d = new SaveFileDialog() {
FileName = $"Lieferstatistik-{vm.FilterSeason ?? Utils.CurrentLastSeason}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"Lieferstatistik pro Ort speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
using var ods = new OdsFile(d.FileName);
var tbl = await WineLocalityStatisticsData.FromQuery(query, filterNames);
await ods.AddTable(tbl);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
var filename = InteractionService.SaveFile("Lieferstatistik pro Ort", $"Lieferstatistik-{vm.FilterSeason ?? Utils.CurrentLastSeason}", "ods");
if (filename != null) {
await Utils.RunForeground(async () => {
using var ods = new OdsFile(filename);
var tbl = await WineLocalityStatisticsData.FromQuery(query, filterNames);
await ods.AddTable(tbl);
});
Mouse.OverrideCursor = null;
}
}
@@ -936,45 +870,28 @@ namespace Elwig.Services {
filterNames.Remove("abgewertet");
if (mode == ExportMode.SaveList) {
var d = new SaveFileDialog() {
FileName = $"{DeliveryDepreciationList.Name}-{vm.FilterSeason ?? Utils.CurrentLastSeason}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"{DeliveryDepreciationList.Name} speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
using var ods = new OdsFile(d.FileName);
var tblTotal = await DeliveryJournalData.FromQuery(query, filterNames);
tblTotal.FullName = DeliveryDepreciationList.Name;
tblTotal.Name = "Gesamt";
await ods.AddTable(tblTotal);
foreach (var branch in await ctx.FetchBranches().ToListAsync()) {
var tbl = await DeliveryJournalData.FromQuery(query.Where(p => p.Delivery.ZwstId == branch.ZwstId), filterNames);
tbl.FullName = DeliveryDepreciationList.Name;
tbl.Name = branch.Name;
await ods.AddTable(tbl);
}
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
var filename = InteractionService.SaveFile(DeliveryDepreciationList.Name, $"{DeliveryDepreciationList.Name}-{vm.FilterSeason ?? Utils.CurrentLastSeason}", "ods");
if (filename != null) {
await Utils.RunForeground(async () => {
using var ods = new OdsFile(filename);
var tblTotal = await DeliveryJournalData.FromQuery(query, filterNames);
tblTotal.FullName = DeliveryDepreciationList.Name;
tblTotal.Name = "Gesamt";
await ods.AddTable(tblTotal);
foreach (var branch in await ctx.FetchBranches().ToListAsync()) {
var tbl = await DeliveryJournalData.FromQuery(query.Where(p => p.Delivery.ZwstId == branch.ZwstId), filterNames);
tbl.FullName = DeliveryDepreciationList.Name;
tbl.Name = branch.Name;
await ods.AddTable(tbl);
}
});
Mouse.OverrideCursor = null;
}
} else {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var data = await DeliveryJournalData.FromQuery(query, filterNames);
using var doc = new DeliveryDepreciationList(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
await Utils.RunForeground(async () => {
var data = await DeliveryJournalData.FromQuery(query, filterNames);
using var doc = new DeliveryDepreciationList(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
});
Mouse.OverrideCursor = null;
}
}
@@ -1001,59 +918,36 @@ namespace Elwig.Services {
.ThenBy(p => p.AttrId)
.ThenBy(p => p.CultId);
var d = new SaveFileDialog() {
FileName = $"Liefermengen.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"Liefermengen speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
using var ods = new OdsFile(d.FileName);
var tblTotal = await MemberDeliveryData.FromQuery(query, filterNames);
var tbl = await MemberDeliveryPerVarietyData.FromQuery(query, filterNames);
await ods.AddTable(tblTotal);
await ods.AddTable(tbl);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
var filename = InteractionService.SaveFile("Liefermengen", "Liefermengen", "ods");
if (filename != null) {
await Utils.RunForeground(async () => {
using var ods = new OdsFile(filename);
var tblTotal = await MemberDeliveryData.FromQuery(query, filterNames);
var tbl = await MemberDeliveryPerVarietyData.FromQuery(query, filterNames);
await ods.AddTable(tblTotal);
await ods.AddTable(tbl);
});
Mouse.OverrideCursor = null;
}
}
private static void AddToolTipCell(Grid grid, string text, int row, int col, int colSpan = 1, bool bold = false, bool alignRight = false, bool alignCenter = false) {
var tb = new TextBlock() {
Text = text,
TextAlignment = alignRight ? TextAlignment.Right : alignCenter ? TextAlignment.Center : TextAlignment.Left,
Margin = new(0, 12 * row, 0, 0),
FontWeight = bold ? FontWeights.Bold : FontWeights.Normal,
};
tb.SetValue(Grid.ColumnProperty, col);
tb.SetValue(Grid.ColumnSpanProperty, colSpan);
grid.Children.Add(tb);
}
private static void AddWeightToolTipRow(Grid grid, int row, string? h1, string? h2, int weight, int? total1, int total2) {
var bold = h2 == null;
if (h1 != null) AddToolTipCell(grid, h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold);
if (h2 != null) AddToolTipCell(grid, h2 + ":", row, 1, 1, bold);
AddToolTipCell(grid, $"{weight:N0} kg", row, 2, 1, bold, true);
if (h1 != null) grid.AddToolTipCell(h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold);
if (h2 != null) grid.AddToolTipCell(h2 + ":", row, 1, 1, bold);
grid.AddToolTipCell($"{weight:N0} kg", row, 2, bold: bold, alignRight: true);
if (total1 != null && total1 != 0)
AddToolTipCell(grid, $"{weight * 100.0 / total1:N1} %", row, 3, 1, bold, true);
grid.AddToolTipCell($"{weight * 100.0 / total1:N1} %", row, 3, bold: bold, alignRight: true);
if (total2 != 0)
AddToolTipCell(grid, $"{weight * 100.0 / total2:N1} %", row, 4, 1, bold, true);
grid.AddToolTipCell($"{weight * 100.0 / total2:N1} %", row, 4, bold: bold, alignRight: true);
}
private static void AddGradationToolTipRow(Grid grid, int row, string? h1, string? h2, double min, double avg, double max) {
var bold = h2 == null;
if (h1 != null) AddToolTipCell(grid, h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold);
if (h2 != null) AddToolTipCell(grid, h2 + ":", row, 1, 1, bold);
AddToolTipCell(grid, $"{min:N1}°", row, 2, 1, bold, true);
AddToolTipCell(grid, $"{avg:N1}°", row, 3, 1, bold, true);
AddToolTipCell(grid, $"{max:N1}°", row, 4, 1, bold, true);
if (h1 != null) grid.AddToolTipCell(h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold);
if (h2 != null) grid.AddToolTipCell(h2 + ":", row, 1, bold: bold);
grid.AddToolTipCell($"{min:N1}°", row, 2, bold: bold, alignRight: true);
grid.AddToolTipCell($"{avg:N1}°", row, 3, bold: bold, alignRight: true);
grid.AddToolTipCell($"{max:N1}°", row, 4, bold: bold, alignRight: true);
}
public static async Task<(string WeightText, (string?, string?, int, int?, int)[] WeightGrid, string GradationText, (string?, string?, double, double, double)[] GradationGrid)> GenerateToolTipData(IQueryable<DeliveryPart> deliveryParts) {
@@ -1182,9 +1076,9 @@ namespace Elwig.Services {
gGrid.ColumnDefinitions.Add(new() { Width = new(35) });
gGrid.ColumnDefinitions.Add(new() { Width = new(35) });
gGrid.ColumnDefinitions.Add(new() { Width = new(35) });
AddToolTipCell(gGrid, "Min.", 0, 2, 1, false, false, true);
AddToolTipCell(gGrid, "⌀", 0, 3, 1, false, false, true);
AddToolTipCell(gGrid, "Max.", 0, 4, 1, false, false, true);
gGrid.AddToolTipCell("Min.", 0, 2, alignCenter: true);
gGrid.AddToolTipCell("⌀", 0, 3, alignCenter: true);
gGrid.AddToolTipCell("Max.", 0, 4, alignCenter: true);
rowNum = 1;
foreach (var row in gradationData) {
if (rowNum != 1 && row.Item2 == null) rowNum++;
@@ -1219,12 +1113,11 @@ namespace Elwig.Services {
attrid = $"'{attr.AttrId}'";
}
var dids = await vm.GetDidsFromFilters();
var res = MessageBox.Show($"Soll wirklich für {dids.Length:N0} Teillieferung(en) das Attribut\n'{attributeName}' gesetz werden?",
"Massenaktion: Attribut setzen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (res != MessageBoxResult.OK) return;
if (!InteractionService.AskContinue("Massenaktion: Attribut setzen",
$"Soll wirklich für {dids.Length:N0} Teillieferung(en) das Attribut\n'{attributeName}' gesetz werden?"))
return;
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
await Utils.RunForeground(async () => {
using (var cnx = await AppDbContext.ConnectAsync()) {
await cnx.ExecuteBatch($"""
UPDATE delivery_part SET attrid = {attrid}
@@ -1234,9 +1127,7 @@ namespace Elwig.Services {
App.HintContextChange();
});
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
} finally {
Mouse.OverrideCursor = null;
InteractionService.ShowException(exc);
}
}
@@ -1249,12 +1140,11 @@ namespace Elwig.Services {
}
var dids = await vm.GetDidsFromFilters();
var res = MessageBox.Show($"Soll wirklich für {dids.Length:N0} Teillieferung(en) der Zu-/Abschlag\n'{modifierName}' hinzugefügt werden?",
"Massenaktion: Zu-/Abschlag hinzufügen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (res != MessageBoxResult.OK) return;
if (!InteractionService.AskContinue("Massenaktion: Zu-/Abschlag hinzufügen",
$"Soll wirklich für {dids.Length:N0} Teillieferung(en) der Zu-/Abschlag\n'{modifierName}' hinzugefügt werden?"))
return;
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
await Utils.RunForeground(async () => {
using (var cnx = await AppDbContext.ConnectAsync()) {
await cnx.ExecuteBatch($"""
INSERT INTO delivery_part_modifier (year, did, dpnr, modid)
@@ -1265,9 +1155,7 @@ namespace Elwig.Services {
App.HintContextChange();
});
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
} finally {
Mouse.OverrideCursor = null;
InteractionService.ShowException(exc);
}
}
@@ -1280,12 +1168,11 @@ namespace Elwig.Services {
}
var dids = await vm.GetDidsFromFilters();
var res = MessageBox.Show($"Soll wirklich für {dids.Length:N0} Teillieferung(en) der Zu-/Abschlag\n'{modifierName}' entfernt werden?",
"Massenaktion: Zu-/Abschlag entfernen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (res != MessageBoxResult.OK) return;
if (!InteractionService.AskContinue("Massenaktion: Zu-/Abschlag entfernen",
$"Soll wirklich für {dids.Length:N0} Teillieferung(en) der Zu-/Abschlag\n'{modifierName}' entfernt werden?"))
return;
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
await Utils.RunForeground(async () => {
using (var cnx = await AppDbContext.ConnectAsync()) {
await cnx.ExecuteBatch($"""
DELETE FROM delivery_part_modifier
@@ -1295,9 +1182,7 @@ namespace Elwig.Services {
App.HintContextChange();
});
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
} finally {
Mouse.OverrideCursor = null;
InteractionService.ShowException(exc);
}
}
}
+163
View File
@@ -0,0 +1,163 @@
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Windows;
namespace Elwig.Services {
public static class InteractionService {
public static Func<string, string, string, string?>? Override;
public static Action<string, string>? NextInformation;
public static Action<string, string>? NextWarning;
public static Action<string, string>? NextError;
public static Func<string, string, bool>? NextContinue;
public static Func<string, string, bool>? NextConfirmation;
public static Func<string, string, bool>? NextQuestion;
public static Func<string, string, string?>? NextSave;
public static readonly Dictionary<string, string> ExtensionFilters = new() {
["pdf"] = "PDF-Datei (*.pdf)|*.pdf",
["ods"] = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
["vcf"] = "vCard-Datei (*.vcf)|*.vcf",
["xml"] = "EBICS-Datei (*.xml)|*.xml",
["csv"] = "CSV-Datei (*.csv)|*.csv",
["sql.zip"] = "Komprimierte SQL-Datei (*.sql.zip)|*.sql.zip",
["elwig.zip"] = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip",
};
public static void ShowInformation(string title, string text) {
if (NextInformation != null) {
NextInformation(title, text);
NextInformation = null;
} else if (Override != null) {
Override("information", title, text);
} else {
MessageBox.Show(text, title, MessageBoxButton.OK, MessageBoxImage.Information);
}
}
public static bool AskContinue(string title, string text) {
if (NextContinue != null) {
var r = NextContinue(title, text);
NextContinue = null;
return r;
} else if (Override != null) {
return Override("continue", title, text) != null;
} else {
return MessageBox.Show(text, title, MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel) == MessageBoxResult.OK;
}
}
public static bool? AskContinueYesNo(string title, string text) {
if (NextContinue != null) {
var r = NextContinue(title, text);
NextContinue = null;
return r;
} else if (Override != null) {
return Override("continue", title, text) != null;
} else {
var r = MessageBox.Show(text, title, MessageBoxButton.YesNoCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
return r == MessageBoxResult.Yes ? true : r == MessageBoxResult.No ? false : null;
}
}
public static bool AskConfirmation(string title, string text) {
if (NextConfirmation != null) {
var r = NextConfirmation(title, text);
NextConfirmation = null;
return r;
} else if (Override != null) {
return Override("confirm", title, text) != null;
} else {
return MessageBox.Show(text, title, MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No) == MessageBoxResult.Yes;
}
}
public static bool AskQuestion(string title, string text, bool defaultResult) {
if (NextQuestion != null) {
var r = NextQuestion(title, text);
NextQuestion = null;
return r;
} else if (Override != null) {
return Override("question", title, text) != null;
} else {
return MessageBox.Show(text, title, MessageBoxButton.YesNo, MessageBoxImage.Question, defaultResult ? MessageBoxResult.Yes : MessageBoxResult.No) == MessageBoxResult.Yes;
}
}
public static void ShowWarning(string title, string text) {
if (NextWarning != null) {
NextWarning(title, text);
NextWarning = null;
} else if (Override != null) {
Override("warning", title, text);
} else {
MessageBox.Show(text, title, MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
public static void ShowError(string title, string text) {
if (NextError != null) {
NextError(title, text);
NextError = null;
} else if (Override != null) {
Override("error", title, text);
} else {
MessageBox.Show(text, title, MessageBoxButton.OK, MessageBoxImage.Error);
}
}
public static void ShowException(Exception exc, bool showExcType = false, bool isError = true) {
ShowException("Fehler", exc, showExcType, isError);
}
public static void ShowException(string title, Exception exc, bool showExcType = false, bool isError = true) {
ShowException(title, null, exc, showExcType, isError);
}
public static void ShowException(string title, string? text, Exception exc, bool showExcType = false, bool isError = true) {
text = text == null ? "" : text + (text.EndsWith('.') || text.EndsWith('!') || text.EndsWith('?') ? "" : ":") + "\n\n";
text += exc.Message + (showExcType ? $" ({exc.GetType().Name})" : "");
if (exc.InnerException != null) text += "\n\n" + exc.InnerException.Message;
if (isError) {
ShowError(title, text);
} else {
ShowWarning(title, text);
}
}
public static void ShowDbException(string title, Exception exc) {
ShowException(title, "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!", exc);
}
public static bool AskException(string title, string? text, Exception exc) {
text = text == null ? "" : text + (text.EndsWith('.') || text.EndsWith('!') || text.EndsWith('?') ? "" : ":") + "\n\n";
text += exc.Message;
if (exc.InnerException != null) text += "\n\n" + exc.InnerException.Message;
if (Override != null) {
return Override("error", title, text) != null;
} else {
return MessageBox.Show(text, title, MessageBoxButton.YesNo, MessageBoxImage.Error, MessageBoxResult.No) == MessageBoxResult.Yes;
}
}
public static string? SaveFile(string title, string defaultFileName, string extension) {
if (NextSave != null) {
var r = NextSave(title, $"{defaultFileName}.{extension}");
NextSave = null;
return r;
} else if (Override != null) {
return Override("save", title, $"{defaultFileName}.{extension}");
} else {
var d = new SaveFileDialog() {
FileName = $"{defaultFileName}.{extension}",
DefaultExt = extension,
Filter = ExtensionFilters.GetValueOrDefault(extension, ""),
Title = $"{title} speichern unter - Elwig",
AddExtension = !extension.Contains('.'),
};
return d.ShowDialog() == true ? d.FileName : null;
}
}
}
}
@@ -0,0 +1,98 @@
using Elwig.Helpers;
using Elwig.Models;
using Elwig.Models.Entities;
using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;
namespace Elwig.Services {
public static class MemberBusinessSharesService {
public static async Task InitInputs(this MemberBusinessSharesViewModel vm) {
using var ctx = new AppDbContext();
vm.DateNoticeString = $"{Utils.Today:dd.MM.yyyy}";
vm.ValuePerShare = (await ctx.Seasons.OrderBy(s => s.Year).LastOrDefaultAsync())?.BusinessShareValue;
}
public static void ClearInputs(this MemberBusinessSharesViewModel vm) {
}
public static void FillInputs(this MemberBusinessSharesViewModel vm, MemberHistory h) {
var mgnr = vm.FilterMember.MgNr;
vm.DateNotice = h.DateNotice;
vm.DateEffective = h.DateEffective;
vm.Reason = h.Reason + (h.Reason == MemberHistory.REASON_TRANSFER ? $"_{(h.ToMgNr == mgnr ? "from" : "to")}" : "");
vm.Shares = h.Shares;
vm.MemberHasSigned = h.MemberHasSigned;
vm.ValuePerShare = h.ValuePerShare;
vm.OtherMgNr = h.FromMgNr != null && h.FromMgNr != mgnr ? h.FromMgNr : h.ToMgNr != null && h.ToMgNr != mgnr ? h.ToMgNr : null;
vm.DeductYear = h.DeductYear ?? h.DateNotice.Year;
vm.Deduct = h.DeductYear != null;
vm.Comment = h.Comment;
}
public static async Task<int> UpdateMemberHistory(this MemberBusinessSharesViewModel vm, int? oldHistNr, bool enableTriggers) {
return await Task.Run(async () => {
using var ctx = new AppDbContext();
var tx = await ctx.Database.BeginTransactionAsync();
await ctx.Database.ExecuteSqlAsync($"UPDATE client_parameter SET value = {(enableTriggers ? "1" : "0")} WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS'");
var h = new MemberHistory {
HistNr = oldHistNr ?? await ctx.NextHistNr(),
FromMgNr =
vm.Reason == $"{MemberHistory.REASON_TRANSFER}_from" ? vm.OtherMgNr :
vm.Reason == MemberHistory.REASON_PURCHASE || vm.Reason == MemberHistory.REASON_PRESCRIPTION ? null :
vm.FilterMember.MgNr,
FromType =
vm.Reason == MemberHistory.REASON_PAYOUT ? BusinessShareType.Dormant :
vm.Reason == MemberHistory.REASON_PURCHASE || vm.Reason == MemberHistory.REASON_PRESCRIPTION ? null :
BusinessShareType.Normal,
ToMgNr =
vm.Reason == MemberHistory.REASON_PAYOUT || vm.Reason == MemberHistory.REASON_EXCLUSION ? null :
vm.Reason == $"{MemberHistory.REASON_TRANSFER}_to" ? vm.OtherMgNr :
vm.FilterMember.MgNr,
ToType =
vm.Reason == MemberHistory.REASON_PAYOUT || vm.Reason == MemberHistory.REASON_EXCLUSION ? null :
vm.Reason == MemberHistory.REASON_RETIREMENT || vm.Reason == MemberHistory.REASON_DECEASED ? BusinessShareType.Dormant :
BusinessShareType.Normal,
DateNoticeString = $"{vm.DateNotice:yyyy-MM-dd}",
DateEffectiveString = $"{vm.DateEffective:yyyy-MM-dd}",
Reason = vm.Reason?.Split("_")[0] ?? MemberHistory.REASON_CONVERT,
Source = "manual",
Shares = vm.Shares ?? 0,
MemberHasSigned = vm.MemberHasSigned,
ValuePerShare = vm.ValuePerShare,
CurrencyCode = vm.ValuePerShare != null ? "EUR" : null,
DeductYear = vm.Deduct ? vm.DeductYear : null,
Comment = string.IsNullOrWhiteSpace(vm.Comment) ? null : vm.Comment.Trim(),
};
if (oldHistNr != null) {
ctx.Update(h);
} else {
ctx.Add(h);
}
await ctx.SaveChangesAsync();
await ctx.Database.ExecuteSqlAsync($"UPDATE client_parameter SET value = '1' WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS'");
await tx.CommitAsync();
return h.HistNr;
});
}
public static async Task DeleteMemberHistory(int histnr, bool enableTriggers) {
await Task.Run(async () => {
using var ctx = new AppDbContext();
var tx = await ctx.Database.BeginTransactionAsync();
await ctx.Database.ExecuteSqlAsync($"UPDATE client_parameter SET value = {(enableTriggers ? "1" : "0")} WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS'");
await ctx.MemberHistory.Where(h => h.HistNr == histnr).ExecuteDeleteAsync();
await ctx.Database.ExecuteSqlAsync($"UPDATE client_parameter SET value = '1' WHERE param = 'ENABLE_MEMBER_HISTORY_TRIGGERS'");
await tx.CommitAsync();
});
}
}
}
+88 -130
View File
@@ -6,13 +6,10 @@ using Elwig.Models.Dtos;
using Elwig.Models.Entities;
using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace Elwig.Services {
public static class MemberService {
@@ -129,7 +126,15 @@ namespace Elwig.Services {
vm.EntryDate = (m.EntryDateString != null) ? string.Join(".", m.EntryDateString.Split("-").Reverse()) : null;
vm.ExitDate = (m.ExitDateString != null) ? string.Join(".", m.ExitDateString.Split("-").Reverse()) : null;
vm.BusinessShares = m.BusinessShares;
if (App.Client.HasRedWhite) {
vm.BusinessShares1 = m.SharesRed;
vm.BusinessShares2 = m.SharesWhite;
vm.BusinessShares3 = m.SharesDormant + m.Shares;
} else {
vm.BusinessShares1 = m.Shares;
vm.BusinessShares2 = m.SharesDormant + m.SharesRed + m.SharesWhite;
vm.BusinessShares3 = 0;
}
vm.AccountingNr = m.AccountingNr;
vm.Branch = (Branch?)ControlUtils.GetItemFromSourceWithPk(vm.BranchSource, m.ZwstId);
vm.DefaultKg = (AT_Kg?)ControlUtils.GetItemFromSourceWithPk(vm.DefaultKgSource, m.DefaultKgNr);
@@ -392,47 +397,29 @@ namespace Elwig.Services {
}
public static async Task GenerateMemberDataSheet(Member m, ExportMode mode) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
using var doc = new MemberDataSheet(m);
await Utils.ExportDocument(doc, mode, emailData: (m, MemberDataSheet.Name, "Im Anhang finden Sie das aktuelle Stammdatenblatt"));
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
await Utils.RunForeground(async () => {
using var doc = new MemberDataSheet(m);
await Utils.ExportDocument(doc, mode, emailData: (m, MemberDataSheet.Name, "Im Anhang finden Sie das aktuelle Stammdatenblatt"));
});
Mouse.OverrideCursor = null;
}
public static async Task GenerateDeliveryConfirmation(Member m, int year, ExportMode mode) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var b = await Billing.Create(year);
await b.FinishSeason();
await b.CalculateBuckets();
App.HintContextChange();
await Utils.RunForeground(async () => {
var b = await Billing.Create(year);
await b.FinishSeason();
await b.CalculateBuckets();
App.HintContextChange();
using var doc = new DeliveryConfirmation(year, m, null);
await Utils.ExportDocument(doc, mode, emailData: (m, $"{DeliveryConfirmation.Name} {year}", $"Im Anhang finden Sie die Anlieferungsbestätigung {year}"));
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
using var doc = new DeliveryConfirmation(year, m, null);
await Utils.ExportDocument(doc, mode, emailData: (m, $"{DeliveryConfirmation.Name} {year}", $"Im Anhang finden Sie die Anlieferungsbestätigung {year}"));
});
Mouse.OverrideCursor = null;
}
public static async Task GenerateCreditNote(Member m, int year, int avnr, ExportMode mode) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
using var doc = await CreditNote.Initialize(year, avnr, m.MgNr, null);
await Utils.ExportDocument(doc, mode, emailData: (m, $"{CreditNote.Name} {doc.Payment.Variant.Name}", $"Im Anhang finden Sie die Traubengutschrift {doc.Payment.Variant.Name}"));
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
await Utils.RunForeground(async () => {
using var doc = await CreditNote.Initialize(year, avnr, m.MgNr, null);
await Utils.ExportDocument(doc, mode, emailData: (m, $"{CreditNote.Name} {doc.Payment.Variant.Name}", $"Im Anhang finden Sie die Traubengutschrift {doc.Payment.Variant.Name}"));
});
Mouse.OverrideCursor = null;
}
public static async Task GenerateMemberList(this MemberAdminViewModel vm, ExportSubject subject, ExportMode mode) {
@@ -477,104 +464,72 @@ namespace Elwig.Services {
}
if (mode == ExportMode.SaveList) {
var d = new SaveFileDialog() {
FileName = $"{MemberList.Name}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"{MemberList.Name} speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var data = await MemberListData.FromQuery(query, filterNames, filterNames.Where(f => f.StartsWith("Flächenbindung")).Select(f => f.Split(' ')[^1]));
using var ods = new OdsFile(d.FileName);
await ods.AddTable(data);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
var filename = InteractionService.SaveFile(MemberList.Name, MemberList.Name, "ods");
if (filename != null) {
await Utils.RunForeground(async () => {
var data = await MemberListData.FromQuery(query, filterNames, filterNames.Where(f => f.StartsWith("Flächenbindung")).Select(f => f.Split(' ')[^1]));
using var ods = new OdsFile(filename);
await ods.AddTable(data);
});
Mouse.OverrideCursor = null;
}
} else if (mode == ExportMode.Vcf) {
var d = new SaveFileDialog() {
FileName = "Mitglieder.vcf",
DefaultExt = "vcf",
Filter = "vCard-Datei (*.vcf)|*.vcf",
Title = "Kontakte speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var members = await query
.OrderBy(m => m.MgNr)
.Include(m => m.TelephoneNumbers)
.Include(m => m.EmailAddresses)
.ToListAsync();
using var exporter = new VCard(d.FileName);
await exporter.ExportAsync(members);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
var filename = InteractionService.SaveFile("Kontakte", "Mitglieder", "vcf");
if (filename != null) {
await Utils.RunForeground(async () => {
var members = await query
.OrderBy(m => m.MgNr)
.Include(m => m.TelephoneNumbers)
.Include(m => m.EmailAddresses)
.ToListAsync();
using var exporter = new VCard(filename);
await exporter.ExportAsync(members);
});
Mouse.OverrideCursor = null;
}
} else if (mode == ExportMode.Export) {
var d = new SaveFileDialog() {
FileName = subject == ExportSubject.Selected ? $"Mitglied_{vm.SelectedMember?.MgNr}.elwig.zip" : $"Mitglieder_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip",
DefaultExt = "elwig.zip",
Filter = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip",
Title = $"{MemberList.Name} speichern unter - Elwig",
AddExtension = false,
};
if (d.ShowDialog() == true) {
if (!d.FileName.EndsWith(".elwig.zip")) d.FileName += ".elwig.zip";
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var members = await query
.OrderBy(m => m.MgNr)
.Include(m => m.TelephoneNumbers)
.Include(m => m.EmailAddresses)
.ToListAsync();
var areaComs = await query
.SelectMany(m => m.AreaCommitments)
.Select(c => c.Contract).Distinct()
.Include(c => c.Revisions)
.ToListAsync();
var wbKgs = members
.Where(m => m.DefaultWbKg != null)
.Select(m => m.DefaultWbKg!)
.Union(areaComs.Select(c => c.Kg))
.Distinct()
.OrderBy(k => k.KgNr)
.ToList();
await ElwigData.Export(d.FileName, members, areaComs, wbKgs, filterNames);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
var filename = InteractionService.SaveFile(MemberList.Name, subject == ExportSubject.Selected ? $"Mitglied_{vm.SelectedMember?.MgNr}" : $"Mitglieder_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}", "elwig.zip");
if (filename != null) {
if (!filename.EndsWith(".elwig.zip")) filename += ".elwig.zip";
await Utils.RunForeground(async () => {
var members = await query
.OrderBy(m => m.MgNr)
.Include(m => m.TelephoneNumbers)
.Include(m => m.EmailAddresses)
.ToListAsync();
var history1 = await query.IgnoreAutoIncludes()
.SelectMany(m => m.HistoryFrom)
.ToListAsync();
var history2 = await query.IgnoreAutoIncludes()
.SelectMany(m => m.HistoryTo)
.ToListAsync();
var history = history1.Union(history2)
.DistinctBy(h => h.HistNr)
.OrderBy(h => h.DateNotice).ThenBy(h => h.HistNr)
.ToList();
var areaComs = await query
.SelectMany(m => m.AreaCommitments)
.Select(c => c.Contract).Distinct()
.Include(c => c.Revisions)
.ToListAsync();
var wbKgs = members
.Where(m => m.DefaultWbKg != null)
.Select(m => m.DefaultWbKg!)
.Union(areaComs.Select(c => c.Kg))
.Distinct()
.OrderBy(k => k.KgNr)
.ToList();
await ElwigData.Export(filename, members, history, areaComs, wbKgs, filterNames);
});
Mouse.OverrideCursor = null;
}
} else if (mode == ExportMode.Upload && App.Config.SyncUrl != null) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
await Utils.RunForeground(async () => {
await SyncService.Upload(App.Config.SyncUrl, App.Config.SyncUrl, App.Config.SyncPassword, query, filterNames);
});
Mouse.OverrideCursor = null;
} else {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var data = await MemberListData.FromQuery(query, filterNames, filterNames.Where(f => f.StartsWith("Flächenbindung")).Select(f => f.Split(' ')[^1]));
using var doc = new MemberList(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
await Utils.RunForeground(async () => {
var data = await MemberListData.FromQuery(query, filterNames, filterNames.Where(f => f.StartsWith("Flächenbindung")).Select(f => f.Split(' ')[^1]));
using var doc = new MemberList(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
});
Mouse.OverrideCursor = null;
}
}
@@ -589,30 +544,33 @@ namespace Elwig.Services {
Name = vm.Name!,
Suffix = vm.IsJuridicalPerson || string.IsNullOrWhiteSpace(vm.Suffix) ? null : vm.Suffix,
ForTheAttentionOf = !vm.IsJuridicalPerson || string.IsNullOrWhiteSpace(vm.ForTheAttentionOf) ? null : vm.ForTheAttentionOf,
Birthday = string.IsNullOrEmpty(vm.Birthday) ? null : string.Join("-", vm.Birthday!.Split(".").Reverse()),
Birthday = string.IsNullOrWhiteSpace(vm.Birthday) ? null : string.Join("-", vm.Birthday!.Split(".").Reverse()),
IsDeceased = vm.IsDeceased,
CountryNum = 40, // Austria AT AUT
PostalDestId = vm.Ort!.Id,
Address = vm.Address!,
Iban = string.IsNullOrEmpty(vm.Iban) ? null : vm.Iban?.Replace(" ", ""),
Bic = string.IsNullOrEmpty(vm.Bic) ? null : vm.Bic,
Iban = string.IsNullOrWhiteSpace(vm.Iban) ? null : vm.Iban?.Replace(" ", ""),
Bic = string.IsNullOrWhiteSpace(vm.Bic) ? null : vm.Bic,
UstIdNr = string.IsNullOrEmpty(vm.UstIdNr) ? null : vm.UstIdNr,
LfbisNr = string.IsNullOrEmpty(vm.LfbisNr) ? null : vm.LfbisNr,
UstIdNr = string.IsNullOrWhiteSpace(vm.UstIdNr) ? null : vm.UstIdNr,
LfbisNr = string.IsNullOrWhiteSpace(vm.LfbisNr) ? null : vm.LfbisNr,
IsBuchführend = vm.IsBuchführend,
IsOrganic = vm.IsOrganic,
EntryDateString = string.IsNullOrEmpty(vm.EntryDate) ? null : string.Join("-", vm.EntryDate.Split(".").Reverse()),
ExitDateString = string.IsNullOrEmpty(vm.ExitDate) ? null : string.Join("-", vm.ExitDate.Split(".").Reverse()),
BusinessShares = (int)vm.BusinessShares!,
AccountingNr = string.IsNullOrEmpty(vm.AccountingNr) ? null : vm.AccountingNr,
EntryDateString = string.IsNullOrWhiteSpace(vm.EntryDate) ? null : string.Join("-", vm.EntryDate.Split(".").Reverse()),
ExitDateString = string.IsNullOrWhiteSpace(vm.ExitDate) ? null : string.Join("-", vm.ExitDate.Split(".").Reverse()),
Shares = App.Client.HasRedWhite ? 0 : vm.BusinessShares1 ?? 0,
SharesRed = App.Client.HasRedWhite ? vm.BusinessShares1 ?? 0 : 0,
SharesWhite = App.Client.HasRedWhite ? vm.BusinessShares2 ?? 0 : 0,
SharesDormant = App.Client.HasRedWhite ? vm.BusinessShares3 ?? 0 : vm.BusinessShares2 ?? 0,
AccountingNr = string.IsNullOrWhiteSpace(vm.AccountingNr) ? null : vm.AccountingNr,
IsActive = vm.IsActive,
IsVollLieferant = vm.IsVollLieferant,
IsFunktionär = vm.IsFunktionär,
ZwstId = vm.Branch?.ZwstId,
DefaultKgNr = vm.DefaultKg?.KgNr,
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
Comment = string.IsNullOrWhiteSpace(vm.Comment) ? null : vm.Comment,
ContactViaPost = vm.ContactViaPost,
ContactViaEmail = vm.ContactViaEmail,
};
+65 -76
View File
@@ -6,13 +6,12 @@ using Elwig.Models.Dtos;
using Elwig.Models.Entities;
using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace Elwig.Services {
public static class PaymentVariantService {
@@ -142,7 +141,7 @@ namespace Elwig.Services {
});
var credits = ctx.Credits.Where(c => c.Year == v.Year && c.AvNr == v.AvNr);
if (!credits.Any()) {
if (!(await credits.AnyAsync())) {
long lastTotalSum = 0;
decimal vatSum = 0;
var currentPayments = await ctx.MemberPayments
@@ -150,8 +149,7 @@ namespace Elwig.Services {
.ToDictionaryAsync(p => p.MgNr);
var lastV = await ctx.PaymentVariants
.Where(l => l.Year == v.Year && !l.TestVariant)
.OrderByDescending(l => l.TransferDateString ?? l.DateString)
.ThenByDescending(l => l.AvNr)
.OrderByDescending(l => l.TransferDateString ?? l.DateString).ThenByDescending(l => l.AvNr)
.FirstOrDefaultAsync();
if (lastV != null) {
var lastPayments = await ctx.MemberPayments
@@ -160,8 +158,8 @@ namespace Elwig.Services {
lastTotalSum = lastPayments.Sum(e => e.Value.AmountValue);
foreach (int mgnr in currentPayments.Keys) {
var c = currentPayments[mgnr];
var l = lastPayments[mgnr];
vatSum += (c.Amount - l.Amount) * (decimal)(c.Member.IsBuchführend ? v.Season.VatNormal : v.Season.VatFlatrate);
var l = lastPayments.GetValueOrDefault(mgnr);
vatSum += (c.Amount - (l?.Amount ?? 0)) * (decimal)(c.Member.IsBuchführend ? v.Season.VatNormal : v.Season.VatFlatrate);
}
} else {
vatSum = currentPayments.Sum(e => e.Value.Amount * (decimal)(e.Value.Member.IsBuchführend ? v.Season.VatNormal : v.Season.VatFlatrate));
@@ -190,97 +188,58 @@ namespace Elwig.Services {
public static async Task GenerateSummary(PaymentVar v, ExportMode mode) {
if (mode == ExportMode.SaveList) {
var d = new SaveFileDialog() {
FileName = $"Variantendaten-{v.Name.Trim().Replace(' ', '-')}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"Variantendaten {v.Name} speichern unter - Elwig"
};
if (d.ShowDialog() == false)
var filename = InteractionService.SaveFile($"Variantendaten {v.Name}", $"Variantendaten-{v.Name.Trim().Replace(' ', '-')}", "ods");
if (filename == null)
return;
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
using var ctx = new AppDbContext();
var data = await PaymentVariantSummaryData.ForPaymentVariant(v, ctx.PaymentVariantSummaryRows);
using var ods = new OdsFile(d.FileName);
await ods.AddTable(data);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
await Utils.RunForeground(async () => {
using var ctx = new AppDbContext();
var data = await PaymentVariantSummaryData.ForPaymentVariant(v, ctx.PaymentVariantSummaryRows);
using var ods = new OdsFile(filename);
await ods.AddTable(data);
});
Mouse.OverrideCursor = null;
} else {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
using var doc = await PaymentVariantSummary.Initialize(v.Year, v.AvNr);
await Utils.ExportDocument(doc, mode);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
await Utils.RunForeground(async () => {
using var doc = await PaymentVariantSummary.Initialize(v.Year, v.AvNr);
await Utils.ExportDocument(doc, mode);
});
Mouse.OverrideCursor = null;
}
}
public static async Task GenerateEbics(int year, int avnr) {
using var ctx = new AppDbContext();
var v = (await ctx.PaymentVariants.FindAsync(year, avnr))!;
var v = await ctx.PaymentVariants.Include(v => v.Credits).Where(v => v.Year == year && v.AvNr == avnr).SingleAsync();
var withoutIban = v.Credits.Count(c => c.Member.Iban == null);
if (withoutIban > 0) {
var r = MessageBox.Show($"Achtung: Für {withoutIban:N0} Mitglieder ist kein IBAN hinterlegt.\n\nDiese werden NICHT exportiert.",
"Mitglieder ohne IBAN", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (r != MessageBoxResult.OK) return;
if (!InteractionService.AskContinue("Mitglieder ohne IBAN",
$"Achtung: Für {withoutIban:N0} Mitglieder ist kein IBAN hinterlegt.\n\nDiese werden NICHT exportiert."))
return;
}
var withNegAmount = v.Credits.Count(c => c.Amount <= 0);
if (withNegAmount > 0) {
var r = MessageBox.Show($"Achtung: Es gibt {withNegAmount:N0} Traubengutschriften mit negativem Betrag.\n\nDiese werden NICHT exportiert.",
"Traubengutschriften mit negativem Betrag", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.OK);
if (r != MessageBoxResult.OK) return;
if (!InteractionService.AskContinue("Traubengutschriften mit negativem Betrag",
$"Achtung: Es gibt {withNegAmount:N0} Traubengutschriften mit negativem Betrag.\n\nDiese werden NICHT exportiert."))
return;
}
var d = new SaveFileDialog() {
FileName = $"{App.Client.NameToken}-Überweisungsdaten-{v.Year}-{v.Name.Trim().Replace(' ', '-')}.{Ebics.FileExtension}",
DefaultExt = Ebics.FileExtension,
Filter = "EBICS-Datei (*.xml)|*.xml",
Title = $"Überweisungsdaten speichern unter - Elwig",
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
using var e = new Ebics(v, d.FileName, App.Client.ExportEbicsVersion, (Ebics.AddressMode)App.Client.ExportEbicsAddress);
await e.ExportAsync(Transaction.FromPaymentVariant(v));
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
var filename = InteractionService.SaveFile("Überweisungsdaten", $"{App.Client.NameToken}-Überweisungsdaten-{v.Year}-{v.Name.Trim().Replace(' ', '-')}", Ebics.FileExtension);
if (filename != null) {
await Utils.RunForeground(async () => {
using var e = new Ebics(v, filename, App.Client.ExportEbicsVersion, (Ebics.AddressMode)App.Client.ExportEbicsAddress);
await e.ExportAsync(Transaction.FromPaymentVariant(v));
});
Mouse.OverrideCursor = null;
}
}
public static async Task GenerateAccountingList(int year, int avnr, string name) {
var d = new SaveFileDialog() {
FileName = $"{App.Client.NameToken}-Buchungsliste-{year}-{name.Trim().Replace(' ', '-')}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"Buchungsliste speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
using var ctx = new AppDbContext();
var tbl = await CreditNoteData.ForPaymentVariant(ctx, year, avnr);
using var ods = new OdsFile(d.FileName);
await ods.AddTable(tbl);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
var filename = InteractionService.SaveFile("Buchungsliste", $"{App.Client.NameToken}-Buchungsliste-{year}-{name.Trim().Replace(' ', '-')}", "ods");
if (filename != null) {
await Utils.RunForeground(async () => {
using var ctx = new AppDbContext();
var tbl = await CreditNoteData.ForPaymentVariant(ctx, year, avnr);
using var ods = new OdsFile(filename);
await ods.AddTable(tbl);
});
Mouse.OverrideCursor = null;
}
}
@@ -341,7 +300,7 @@ namespace Elwig.Services {
TransferDate = vm.TransferDate,
TestVariant = vm.SelectedPaymentVariant?.TestVariant ?? true,
CalcTimeUnix = vm.SelectedPaymentVariant?.CalcTimeUnix,
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
Comment = string.IsNullOrWhiteSpace(vm.Comment) ? null : vm.Comment,
Data = JsonSerializer.Serialize(d.Data),
};
avnr = v.AvNr;
@@ -369,10 +328,40 @@ namespace Elwig.Services {
}
public static async Task Commit(int year, int avnr) {
List<Member> membersLess = [];
using (var ctx = new AppDbContext()) {
var lastAvNr = await ctx.PaymentVariants
.Where(v => v.Year == year && !v.TestVariant)
.OrderByDescending(v => v.TransferDateString ?? v.DateString).ThenByDescending(v => v.AvNr)
.Select(v => v.AvNr)
.FirstOrDefaultAsync();
if (lastAvNr != 0) {
var cur = await ctx.MemberPayments.Include(p => p.Member).Where(p => p.Year == year && p.AvNr == avnr).ToDictionaryAsync(p => p.Member.MgNr, p => p);
var last = await ctx.MemberPayments.Include(p => p.Member).Where(p => p.Year == year && p.AvNr == lastAvNr).ToDictionaryAsync(p => p.Member.MgNr, p => p);
membersLess = [.. last.Where(p => (cur.TryGetValue(p.Key, out var c) ? c.Amount : 0) < p.Value.Amount).Select(p => p.Value.Member)];
}
}
if (membersLess.Count > 0) {
if (!InteractionService.AskContinue("Kleinerer Gesamtbetrag",
$"Achtung: Bei {membersLess.Count:N0} Mitglied(ern) ist der Soll-Betrag der Auszahlung (\"Gesamtbetrag\") kleiner als bei der letzten Auszahlung. Das sollte nicht vorkommen!\n\n" +
$"{string.Join(", ", membersLess.Select(m => $"{m.AdministrativeName} ({m.MgNr})"))}\n\n" +
"Soll trotzdem forgefahren werden?"))
return;
}
await Task.Run(async () => {
var b = await BillingVariant.Create(year, avnr);
await b.Commit();
});
List<Member> membersNeg = [];
using (var ctx = new AppDbContext()) {
membersNeg = await ctx.Credits.Where(c => c.Year == year && c.AvNr == avnr && c.AmountValue < 0).Select(c => c.Member).ToListAsync();
}
if (membersNeg.Count > 0) {
InteractionService.ShowWarning("Negativer Auszahlungsbetrag", $"Achtung: Bei {membersNeg.Count:N0} Mitglied(ern) ist der Auszahlungsbetrag negativ!\n\n" +
$"{string.Join(", ", membersNeg.Select(m => $"{m.AdministrativeName} ({m.MgNr})"))}");
}
}
public static async Task Revert(int year, int avnr) {
+34 -33
View File
@@ -10,7 +10,6 @@ using System.Linq;
using System.Linq.Expressions;
using System.Net.Http;
using System.Threading.Tasks;
using System.Windows;
namespace Elwig.Services {
public static class SyncService {
@@ -28,6 +27,16 @@ namespace Elwig.Services {
.Include(m => m.TelephoneNumbers)
.Include(m => m.EmailAddresses)
.ToListAsync();
var history1 = await query.IgnoreAutoIncludes()
.SelectMany(m => m.HistoryFrom)
.ToListAsync();
var history2 = await query.IgnoreAutoIncludes()
.SelectMany(m => m.HistoryTo)
.ToListAsync();
var history = history1.Union(history2)
.DistinctBy(h => h.HistNr)
.OrderBy(h => h.DateNotice).ThenBy(h => h.HistNr)
.ToList();
var areaComs = await query
.SelectMany(m => m.AreaCommitments)
.Select(c => c.Contract).Distinct()
@@ -42,22 +51,20 @@ namespace Elwig.Services {
.OrderBy(k => k.KgNr)
.ToList();
if (members.Count == 0) {
MessageBox.Show("Es wurden keine Mitglieder zum Hochladen ausgewählt!", "Mitglieder hochladen",
MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Mitglieder hochladen", "Es wurden keine Mitglieder zum Hochladen ausgewählt!");
} else {
var exportedAt = DateTime.Now;
await ElwigData.Export(path, members, areaComs, wbKgs, filterNames);
await ElwigData.Export(path, members, history, areaComs, wbKgs, filterNames);
await Utils.UploadExportData(path, url, username, password);
await UpdateExportedAt(members, areaComs, [], exportedAt);
MessageBox.Show($"Hochladen von {members.Count:N0} Mitgliedern erfolgreich!", "Mitglieder hochgeladen",
MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Mitglieder hochgeladen", $"Hochladen von {members.Count:N0} Mitgliedern erfolgreich!");
}
} catch (HttpRequestException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Mitglieder hochladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (TaskCanceledException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Mitglieder hochladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Mitglieder hochladen", exc);
}
}
@@ -78,22 +85,20 @@ namespace Elwig.Services {
.OrderBy(k => k.KgNr)
.ToList();
if (list.Count == 0) {
MessageBox.Show("Es wurden keine Lieferungen zum Hochladen ausgewählt!", "Lieferungen hochladen",
MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Lieferungen hochladen", "Es wurden keine Lieferungen zum Hochladen ausgewählt!");
} else {
var exportedAt = DateTime.Now;
await ElwigData.Export(path, list, wbKgs, filterNames);
await Utils.UploadExportData(path, url, username, password);
await UpdateExportedAt([], [], list, exportedAt);
MessageBox.Show($"Hochladen von {list.Count:N0} Lieferungen erfolgreich!", "Lieferungen hochgeladen",
MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Lieferungen hochgeladen", $"Hochladen von {list.Count:N0} Lieferungen erfolgreich!");
}
} catch (HttpRequestException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Lieferungen hochladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (TaskCanceledException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Lieferungen hochladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Lieferungen hochladen", exc);
}
}
@@ -132,8 +137,7 @@ namespace Elwig.Services {
.OrderBy(k => k.KgNr)
.ToList();
if (members.Count == 0 && deliveries.Count == 0) {
MessageBox.Show("Es gibt keine geänderten Mitglieder oder Lieferungen, die hochgeladen werden könnten!", "Mitglieder und Lieferungen hochladen",
MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Mitglieder und Lieferungen hochladen", "Es gibt keine geänderten Mitglieder oder Lieferungen, die hochgeladen werden könnten!");
} else {
var exportedAt = DateTime.Now;
await (new ElwigData.ElwigExport {
@@ -144,15 +148,14 @@ namespace Elwig.Services {
}).Export(path);
await Utils.UploadExportData(path, url, username, password);
await UpdateExportedAt(members, areaComs, deliveries, exportedAt);
MessageBox.Show($"Hochladen von {members.Count:N0} Mitgliedern, {areaComs.Count:N0} Flächenbindungsverträgen, und {deliveries.Count:N0} Lieferungen erfolgreich!", "Mitglieder und Lieferungen hochladen",
MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Mitglieder und Lieferungen hochladen", $"Hochladen von {members.Count:N0} Mitgliedern, {areaComs.Count:N0} Flächenbindungsverträgen, und {deliveries.Count:N0} Lieferungen erfolgreich!");
}
} catch (HttpRequestException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder und Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Mitglieder und Lieferungen hochladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (TaskCanceledException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder und Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Mitglieder und Lieferungen hochladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Mitglieder und Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Mitglieder und Lieferungen hochladen", exc);
}
}
@@ -173,22 +176,20 @@ namespace Elwig.Services {
.DistinctBy(k => k.KgNr)
.ToList();
if (deliveries.Count == 0) {
MessageBox.Show("Es gibt keine Lieferungen, die hochgeladen werden können!", "Lieferungen hochladen",
MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Lieferungen hochladen", "Es gibt keine Lieferungen, die hochgeladen werden können!");
} else {
var exportedAt = DateTime.Now;
await ElwigData.Export(path, deliveries, wbKgs, [$"{year}", $"Zweigstelle {App.BranchName}"]);
await Utils.UploadExportData(path, url, username, password);
await UpdateExportedAt([], [], deliveries, exportedAt);
MessageBox.Show($"Hochladen von {deliveries.Count:N0} Lieferungen erfolgreich!", "Lieferungen hochladen",
MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Lieferungen hochladen", $"Hochladen von {deliveries.Count:N0} Lieferungen erfolgreich!");
}
} catch (HttpRequestException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Lieferungen hochladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (TaskCanceledException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Lieferungen hochladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Lieferungen hochladen", exc);
}
}
@@ -227,11 +228,11 @@ namespace Elwig.Services {
}
await ElwigData.Import(paths, ElwigData.ImportMode.FromBranches);
} catch (HttpRequestException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Daten herunterladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Daten herunterladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (TaskCanceledException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Daten herunterladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Daten herunterladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Daten herunterladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Daten herunterladen", exc);
}
}
+18 -7
View File
@@ -6,6 +6,7 @@ using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
namespace Elwig.ViewModels {
public partial class MemberAdminViewModel : ObservableObject {
@@ -124,10 +125,22 @@ namespace Elwig.ViewModels {
[ObservableProperty]
private string? _exitDate;
[ObservableProperty]
private string? _businessSharesString;
public int? BusinessShares {
get => int.TryParse(BusinessSharesString, out var bs) ? bs : null;
set => BusinessSharesString = $"{value}";
private string? _businessShares1String;
public int? BusinessShares1 {
get => int.TryParse(BusinessShares1String, out var shares) ? shares : null;
set => BusinessShares1String = $"{value}";
}
[ObservableProperty]
private string? _businessShares2String;
public int? BusinessShares2 {
get => int.TryParse(BusinessShares2String, out var shares) ? shares : null;
set => BusinessShares2String = $"{value}";
}
[ObservableProperty]
private string? _businessShares3String;
public int? BusinessShares3 {
get => int.TryParse(BusinessShares3String, out var shares) ? shares : null;
set => BusinessShares3String = $"{value}";
}
[ObservableProperty]
private string? _accountingNr;
@@ -188,9 +201,7 @@ namespace Elwig.ViewModels {
[ObservableProperty]
private string? _statusMembersToolTip;
[ObservableProperty]
private string _statusBusinessShares = "-";
[ObservableProperty]
private string? _statusBusinessSharesToolTip;
private TextBlock? _statusBusinessShares;
[ObservableProperty]
private string _statusDeliveriesLastSeason = "-";
[ObservableProperty]
@@ -0,0 +1,76 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Elwig.Models.Entities;
using System;
using System.Collections.Generic;
using System.Windows;
namespace Elwig.ViewModels {
public partial class MemberBusinessSharesViewModel : ObservableObject {
[ObservableProperty]
private string _title = "Geschäftsanteilbewegungen - Elwig";
public required Member FilterMember { get; set; }
[ObservableProperty]
private MemberHistory? _selectedHistoryEntry;
[ObservableProperty]
private IEnumerable<MemberHistory> _history = [];
[ObservableProperty]
private string? _dateNoticeString;
public DateOnly? DateNotice {
get => DateOnly.TryParseExact(DateNoticeString, "dd.MM.yyyy", out var d) ? d : null;
set => DateNoticeString = $"{value:dd.MM.yyyy}";
}
[ObservableProperty]
private string? _dateEffectiveString;
public DateOnly? DateEffective {
get => DateOnly.TryParseExact(DateEffectiveString, "dd.MM.yyyy", out var d) ? d : null;
set => DateEffectiveString = $"{value:dd.MM.yyyy}";
}
[ObservableProperty]
private string? _reason;
[ObservableProperty]
private string? _sharesString;
public int? Shares {
get => int.TryParse(SharesString, out var shares) ? shares : null;
set => SharesString = $"{value}";
}
[ObservableProperty]
private string? _otherMgNrString;
public int? OtherMgNr {
get => int.TryParse(OtherMgNrString, out var mgnr) ? mgnr : null;
set => OtherMgNrString = $"{value}";
}
[ObservableProperty]
private string? _deductYearString;
public int? DeductYear {
get => int.TryParse(DeductYearString, out var year) ? year : null;
set => DeductYearString = $"{value}";
}
[ObservableProperty]
private bool _deduct;
[ObservableProperty]
private bool _memberHasSigned;
[ObservableProperty]
private string? _valuePerShareString;
public decimal? ValuePerShare {
get => decimal.TryParse(ValuePerShareString, out var v) ? v : null;
set => ValuePerShareString = $"{value:0.00}";
}
[ObservableProperty]
private string? _totalAmount;
[ObservableProperty]
private string? _comment;
[ObservableProperty]
private Visibility _controlButtonsVisibility = Visibility.Visible;
[ObservableProperty]
private Visibility _editingButtonsVisibility = Visibility.Hidden;
[ObservableProperty]
private Visibility _personalNameVisibility = Visibility.Visible;
[ObservableProperty]
private Visibility _juridicalNameVisibility = Visibility.Hidden;
}
}
+1 -1
View File
@@ -15,7 +15,7 @@
<Bold>Website:</Bold> <Hyperlink NavigateUri="https://elwig.at/" RequestNavigate="Hyperlink_RequestNavigate">https://elwig.at/</Hyperlink><LineBreak/>
<Bold>Entwickler:</Bold> Lorenz Stechauner, Thomas Hilscher<LineBreak/>
<Bold>Kontakt:</Bold> <Hyperlink NavigateUri="mailto:lorenz.stechauner@necronda.net" RequestNavigate="Hyperlink_RequestNavigate">lorenz.stechauner@necronda.net</Hyperlink>, <Hyperlink NavigateUri="mailto:thomas.hilscher@gmail.com" RequestNavigate="Hyperlink_RequestNavigate">thomas.hilscher@gmail.com</Hyperlink><LineBreak/>
<Bold>Quellcode:</Bold> <Hyperlink NavigateUri="https://git.necronda.net/winzer/elwig" RequestNavigate="Hyperlink_RequestNavigate">https://git.necronda.net/winzer/elwig</Hyperlink><LineBreak/>
<Bold>Quellcode:</Bold> <Hyperlink NavigateUri="C:\Program Files\Elwig\src" RequestNavigate="Hyperlink_RequestNavigate_Explorer">C:\Program Files\Elwig\src</Hyperlink>, <Hyperlink NavigateUri="https://git.necronda.net/winzer/elwig" RequestNavigate="Hyperlink_RequestNavigate">https://git.necronda.net/winzer/elwig</Hyperlink><LineBreak/>
<Bold>Entwicklungszeitraum:</Bold> 20222026<LineBreak/>
<LineBreak/>
<Bold>Verwendete Technologien:</Bold><LineBreak/>
+9 -1
View File
@@ -11,7 +11,15 @@ namespace Elwig.Windows {
}
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e) {
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) { UseShellExecute = true });
try {
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) { UseShellExecute = true });
} catch { }
}
private void Hyperlink_RequestNavigate_Explorer(object sender, RequestNavigateEventArgs e) {
try {
Process.Start("explorer.exe", e.Uri.AbsoluteUri);
} catch { }
}
}
}
+12 -8
View File
@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Elwig.Services;
namespace Elwig.Windows {
public abstract class AdministrationWindow : ContextWindow {
@@ -109,9 +110,7 @@ namespace Elwig.Windows {
private void OnClosing(object? sender, CancelEventArgs evt) {
if ((IsCreating || IsEditing) && HasChanged) {
var r = MessageBox.Show("Soll das Fenster wirklich geschlossen werden?", "Schließen bestätigen",
MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r != MessageBoxResult.Yes) {
if (!InteractionService.AskConfirmation("Schließen bestätigen", "Soll das Fenster wirklich geschlossen werden?")) {
evt.Cancel = true;
return;
}
@@ -165,10 +164,10 @@ namespace Elwig.Windows {
} else if (input is TextBox tb && tb.Text.Length == 0) {
ControlUtils.SetInputInvalid(input);
Valid[input] = false;
} else if (input is ComboBox cb && cb.SelectedItem == null && cb.ItemsSource != null && cb.ItemsSource.Cast<object>().Any()) {
} else if (input is ComboBox cb && cb.SelectedItem == null && cb.HasItems) {
ControlUtils.SetInputInvalid(input);
Valid[input] = false;
} else if (input is ListBox lb && lb.SelectedItem == null && lb.ItemsSource != null && lb.ItemsSource.Cast<object>().Any()) {
} else if (input is ListBox lb && lb.SelectedItem == null && lb.HasItems) {
ControlUtils.SetInputInvalid(input);
Valid[input] = false;
} else if (input is CheckBox ckb && ((ckb.IsThreeState && ckb.IsChecked == null) || (!ckb.IsThreeState && ckb.IsChecked != true))) {
@@ -286,8 +285,13 @@ namespace Elwig.Windows {
var binding = cb.GetBindingExpression(ComboBox.SelectedItemProperty);
binding?.UpdateSource();
}
foreach (var lb in ListBoxInputs)
lb.SelectedItems.Clear();
foreach (var lb in ListBoxInputs) {
if (lb.SelectionMode == SelectionMode.Single) {
lb.SelectedItem = null;
} else {
lb.SelectedItems.Clear();
}
}
foreach (var cb in CheckBoxInputs) {
cb.IsChecked = cb.IsThreeState ? null : false;
var binding = cb.GetBindingExpression(CheckBox.IsCheckedProperty);
@@ -390,7 +394,7 @@ namespace Elwig.Windows {
protected bool InputLostFocus(TextBox input, ValidationResult res, string? msg = null) {
if (DoShowWarningWindows && !res.IsValid && !IsClosing && (IsEditing || IsCreating))
MessageBox.Show(res.ErrorContent?.ToString(), msg ?? res.ErrorContent?.ToString(), MessageBoxButton.OK, MessageBoxImage.Warning);
InteractionService.ShowWarning(msg ?? res.ErrorContent?.ToString() ?? "", res.ErrorContent?.ToString() ?? "");
return res.IsValid;
}
+2 -6
View File
@@ -246,9 +246,7 @@ namespace Elwig.Windows {
}
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Flächenbindung löschen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Flächenbindung löschen", exc);
}
Mouse.OverrideCursor = null;
}
@@ -298,9 +296,7 @@ namespace Elwig.Windows {
}
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Flächenbindung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Flächenbindung aktualisieren", exc);
SaveButton.IsEnabled = true;
Mouse.OverrideCursor = null;
return;
+20 -5
View File
@@ -453,13 +453,13 @@
HorizontalAlignment="Left"/>
<Label Content="Lieferpflicht/-recht:" Margin="10,10,0,10" Grid.Column="2"/>
<ctrl:UnitTextBox x:Name="SeasonMinKgPerBsInput" Unit="kg/GA" TextChanged="SeasonMinMaxKgInput_TextChanged"
<ctrl:UnitTextBox x:Name="SeasonMinKgPerShareInput" Unit="kg/GA" TextChanged="SeasonMinMaxKgInput_TextChanged"
Grid.Column="3" Width="80" Margin="0,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ctrl:UnitTextBox x:Name="SeasonMaxKgPerBsInput" Unit="kg/GA" TextChanged="SeasonMinMaxKgInput_TextChanged"
<ctrl:UnitTextBox x:Name="SeasonMaxKgPerShareInput" Unit="kg/GA" TextChanged="SeasonMinMaxKgInput_TextChanged"
Grid.Column="3" Width="80" Margin="85,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Label Content="GA-Wert (Nachz.):" Margin="10,40,0,10" Grid.Column="2"/>
<ctrl:UnitTextBox x:Name="SeasonBsValueInput" Unit="€/GA" TextChanged="SeasonPenaltyInput_TextChanged"
<ctrl:UnitTextBox x:Name="SeasonShareValueInput" Unit="€/GA" TextChanged="SeasonPenaltyInput_TextChanged"
Grid.Column="3" Width="85" Margin="0,40,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<GroupBox Header="Strafen bei Unterlieferung lt. GA" Grid.Column="2" Grid.ColumnSpan="2" Margin="0,70,10,0">
@@ -468,13 +468,13 @@
Width="80" Margin="65,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ctrl:UnitTextBox x:Name="SeasonPenaltyInput" Unit="€" TextChanged="SeasonPenaltyInput_TextChanged"
Width="68" Margin="150,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ctrl:UnitTextBox x:Name="SeasonPenaltyPerBsInput" Unit="€/GA" TextChanged="SeasonPenaltyPerBsInput_TextChanged"
<ctrl:UnitTextBox x:Name="SeasonPenaltyPerShareInput" Unit="€/GA" TextChanged="SeasonPenaltyPerShareInput_TextChanged"
Width="100" Margin="222,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Label Content="Zzgl. bei Nicht-Lieferung:" Margin="10,40,0,10"/>
<ctrl:UnitTextBox x:Name="SeasonPenaltyNoneInput" Unit="€" TextChanged="SeasonPenaltyInput_TextChanged"
Width="68" Margin="150,40,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ctrl:UnitTextBox x:Name="SeasonPenaltyPerBsNoneInput" Unit="€/GA" TextChanged="SeasonPenaltyPerBsInput_TextChanged"
<ctrl:UnitTextBox x:Name="SeasonPenaltyPerShareNoneInput" Unit="€/GA" TextChanged="SeasonPenaltyPerShareInput_TextChanged"
Width="100" Margin="222,40,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</Grid>
</GroupBox>
@@ -541,6 +541,21 @@
</TabItem>
<TabItem Header="Parameter">
<StackPanel>
<GroupBox x:Name="ParameterBusinessShare" Header="Geschäftsanteile" Margin="10,10,10,0" VerticalAlignment="Top">
<Grid>
<Label Content="Modus:" Margin="10,10,10,10"/>
<ComboBox x:Name="ParameterBusinessShareModeInput" Margin="160,10,10,10" Width="100"
HorizontalAlignment="Left">
<ComboBoxItem IsSelected="True">Normal</ComboBoxItem>
<ComboBoxItem>Rot/weiß</ComboBoxItem>
</ComboBox>
<Label Content="Mitgliederbewegungen:" Margin="10,40,10,10"/>
<CheckBox x:Name="ParameterBusinessShareHistoryInput" Content="Mit Elwig erfassen" Margin="160,45,10,15"
HorizontalAlignment="Left"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
</Grid>
</GroupBox>
<GroupBox x:Name="ParameterAreaComGroup" Header="Berechnung Flächenbindungen (aktuelle Saison)" Margin="10,10,10,0" VerticalAlignment="Top">
<Grid>
<CheckBox x:Name="ParameterAllowAttrIntoLowerInput" Content="Erlauben Lieferungen auch auf (konfigurierte) &quot;schlechtere&quot; Flächenbindungen aufzuteilen"
@@ -41,7 +41,6 @@ namespace Elwig.Windows {
if (!_branchChanged || _branchList == null || _branches == null || _branchIds == null)
return;
var tx = await ctx.Database.BeginTransactionAsync();
var deleteZwstIds = _branches.Where(b => b.Value == null).Select(b => b.Key).ToList();
await ctx.Branches.Where(b => deleteZwstIds.Contains(b.ZwstId)).ExecuteDeleteAsync();
@@ -62,7 +61,6 @@ namespace Elwig.Windows {
ctx.Add(branch);
}
await ctx.SaveChangesAsync();
await tx.CommitAsync();
}
private void BranchList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
+42 -35
View File
@@ -1,6 +1,7 @@
using Elwig.Dialogs;
using Elwig.Helpers;
using Elwig.Models.Entities;
using Elwig.Services;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
@@ -46,23 +47,23 @@ namespace Elwig.Windows {
SeasonVatFlatrateInput.Text = (s.VatFlatrate * 100).ToString();
SeasonStartInput.Text = $"{s.StartDate:dd.MM.yyyy}";
SeasonEndInput.Text = $"{s.EndDate:dd.MM.yyyy}";
SeasonMinKgPerBsInput.Text = s.MinKgPerBusinessShare.ToString();
SeasonMaxKgPerBsInput.Text = s.MaxKgPerBusinessShare.ToString();
SeasonMinKgPerShareInput.Text = s.MinKgPerShare?.ToString() ?? s.MinKgPerShareRed?.ToString() ?? s.MinKgPerShareWhite?.ToString() ?? "0";
SeasonMaxKgPerShareInput.Text = s.MaxKgPerShare?.ToString() ?? s.MaxKgPerShareRed?.ToString() ?? s.MaxKgPerShareWhite?.ToString() ?? "0";
SeasonPenaltyPerKgInput.Text = s.PenaltyPerKg?.ToString() ?? "";
SeasonPenaltyInput.Text = s.PenaltyAmount?.ToString() ?? "";
SeasonPenaltyNoneInput.Text = s.PenaltyNone?.ToString() ?? "";
SeasonPenaltyPerBsInput.Text = s.PenaltyPerBsAmount?.ToString() ?? "";
SeasonPenaltyPerBsNoneInput.Text = s.PenaltyPerBsNone?.ToString() ?? "";
SeasonBsValueInput.Text = s.BusinessShareValue?.ToString() ?? "";
SeasonPenaltyPerShareInput.Text = s.PenaltyPerShareAmount?.ToString() ?? "";
SeasonPenaltyPerShareNoneInput.Text = s.PenaltyPerShareNone?.ToString() ?? "";
SeasonShareValueInput.Text = s.BusinessShareValue?.ToString() ?? "";
var sym = s.Currency.Symbol ?? s.Currency.Code;
SeasonModifierAbsInput.Unit = $"{sym}/kg";
SeasonPenaltyPerKgInput.Unit = $"{sym}/kg";
SeasonPenaltyInput.Unit = sym;
SeasonPenaltyNoneInput.Unit = sym;
SeasonPenaltyPerBsInput.Unit = $"{sym}/GA";
SeasonPenaltyPerBsNoneInput.Unit = $"{sym}/GA";
SeasonBsValueInput.Unit = $"{sym}/GA";
SeasonPenaltyPerShareInput.Unit = $"{sym}/GA";
SeasonPenaltyPerShareNoneInput.Unit = $"{sym}/GA";
SeasonShareValueInput.Unit = $"{sym}/GA";
AreaCommitmentTypePenaltyPerKgInput.Unit = $"{sym}/kg";
AreaCommitmentTypePenaltyInput.Unit = sym;
AreaCommitmentTypePenaltyNoneInput.Unit = sym;
@@ -73,14 +74,14 @@ namespace Elwig.Windows {
SeasonVatFlatrateInput.Text = "";
SeasonStartInput.Text = "";
SeasonEndInput.Text = "";
SeasonMinKgPerBsInput.Text = "";
SeasonMaxKgPerBsInput.Text = "";
SeasonMinKgPerShareInput.Text = "";
SeasonMaxKgPerShareInput.Text = "";
SeasonPenaltyPerKgInput.Text = "";
SeasonPenaltyInput.Text = "";
SeasonPenaltyNoneInput.Text = "";
SeasonPenaltyPerBsInput.Text = "";
SeasonPenaltyPerBsNoneInput.Text = "";
SeasonBsValueInput.Text = "";
SeasonPenaltyPerShareInput.Text = "";
SeasonPenaltyPerShareNoneInput.Text = "";
SeasonShareValueInput.Text = "";
}
_seasonUpdate = false;
}
@@ -95,16 +96,28 @@ namespace Elwig.Windows {
s.VatNormal = double.Parse(SeasonVatNormalInput.Text) / 100;
if (SeasonVatFlatrateInput.Text.Length > 0)
s.VatFlatrate = double.Parse(SeasonVatFlatrateInput.Text) / 100;
if (SeasonMinKgPerBsInput.Text.Length > 0)
s.MinKgPerBusinessShare = int.Parse(SeasonMinKgPerBsInput.Text);
if (SeasonMaxKgPerBsInput.Text.Length > 0)
s.MaxKgPerBusinessShare = int.Parse(SeasonMaxKgPerBsInput.Text);
if (ParameterBusinessShareModeInput.SelectedIndex == 0) {
s.MinKgPerShare = (SeasonMinKgPerShareInput.Text.Length > 0) ? int.Parse(SeasonMinKgPerShareInput.Text) : null;
s.MaxKgPerShare = (SeasonMaxKgPerShareInput.Text.Length > 0) ? int.Parse(SeasonMaxKgPerShareInput.Text) : null;
s.MinKgPerShareRed = null;
s.MaxKgPerShareRed = null;
s.MinKgPerShareWhite = null;
s.MaxKgPerShareWhite = null;
} else {
s.MinKgPerShare = null;
s.MaxKgPerShare = null;
s.MinKgPerShareRed = (SeasonMinKgPerShareInput.Text.Length > 0) ? int.Parse(SeasonMinKgPerShareInput.Text) : null;
s.MaxKgPerShareRed = (SeasonMaxKgPerShareInput.Text.Length > 0) ? int.Parse(SeasonMaxKgPerShareInput.Text) : null;
s.MinKgPerShareWhite = s.MinKgPerShareRed;
s.MaxKgPerShareWhite = s.MaxKgPerShareRed;
}
s.PenaltyPerKg = (SeasonPenaltyPerKgInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyPerKgInput.Text) : null;
s.PenaltyAmount = (SeasonPenaltyInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyInput.Text) : null;
s.PenaltyNone = (SeasonPenaltyNoneInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyNoneInput.Text) : null;
s.PenaltyPerBsAmount = (SeasonPenaltyPerBsInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyPerBsInput.Text) : null;
s.PenaltyPerBsNone = (SeasonPenaltyPerBsNoneInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyPerBsNoneInput.Text) : null;
s.BusinessShareValue = (SeasonBsValueInput.Text.Length > 0) ? decimal.Parse(SeasonBsValueInput.Text) : null;
s.PenaltyPerShareAmount = (SeasonPenaltyPerShareInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyPerShareInput.Text) : null;
s.PenaltyPerShareNone = (SeasonPenaltyPerShareNoneInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyPerShareNoneInput.Text) : null;
s.BusinessShareValue = (SeasonShareValueInput.Text.Length > 0) ? decimal.Parse(SeasonShareValueInput.Text) : null;
UpdateButtons();
}
@@ -130,7 +143,7 @@ namespace Elwig.Windows {
Season_Changed(sender, evt);
}
private void SeasonPenaltyPerBsInput_TextChanged(object sender, TextChangedEventArgs evt) {
private void SeasonPenaltyPerShareInput_TextChanged(object sender, TextChangedEventArgs evt) {
if (SeasonList.SelectedItem is not Season s) return;
InputTextChanged((TextBox)sender, Validator.CheckDecimal((TextBox)sender, false, 4, s.Precision));
Season_Changed(sender, evt);
@@ -159,13 +172,13 @@ namespace Elwig.Windows {
MaxKgPerHa = s?.MaxKgPerHa ?? 10000,
VatNormal = s?.VatNormal ?? 0.10,
VatFlatrate = s?.VatFlatrate ?? 0.13,
MinKgPerBusinessShare = s?.MinKgPerBusinessShare ?? 500,
MaxKgPerBusinessShare = s?.MaxKgPerBusinessShare ?? 1000,
MinKgPerShare = s?.MinKgPerShare,
MaxKgPerShare = s?.MaxKgPerShare,
PenaltyPerKgValue = s?.PenaltyPerKgValue,
PenaltyAmoutValue = s?.PenaltyAmoutValue,
PenaltyNoneValue = s?.PenaltyNoneValue,
PenaltyPerBsAmountValue = s?.PenaltyPerBsAmountValue,
PenaltyPerBsNoneValue = s?.PenaltyPerBsNoneValue,
PenaltyPerShareAmountValue = s?.PenaltyPerShareAmountValue,
PenaltyPerShareNoneValue = s?.PenaltyPerShareNoneValue,
BusinessShareValueValue = s?.BusinessShareValueValue,
CalcMode = s?.CalcMode ?? 0,
});
@@ -180,13 +193,12 @@ namespace Elwig.Windows {
AbsValue = m.AbsValue * mult / div,
RelValue = m.RelValue,
IsActive = m.IsActive,
IsRedacted = m.IsRedacted,
}));
}
await ctx.SaveChangesAsync();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Saison anlegen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Saison anlegen", exc);
}
App.HintContextChange();
Mouse.OverrideCursor = null;
@@ -197,19 +209,14 @@ namespace Elwig.Windows {
private async void SeasonRemoveButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonList.SelectedItem is not Season s)
return;
var r = MessageBox.Show(
$"Soll die Saison {s.Year} wirklich unwiderruflich gelöscht werden?",
"Saison löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (r == MessageBoxResult.OK) {
if (InteractionService.AskContinue("Saison löschen", $"Soll die Saison {s.Year} wirklich unwiderruflich gelöscht werden?")) {
Mouse.OverrideCursor = Cursors.Wait;
try {
using var ctx = new AppDbContext();
ctx.Remove(s);
await ctx.SaveChangesAsync();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Saison löschen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Saison löschen", exc);
}
App.HintContextChange();
Mouse.OverrideCursor = null;
@@ -41,7 +41,6 @@ namespace Elwig.Windows {
if (!_attrChanged || _attrList == null || _attrs == null || _attrIds == null)
return;
using var tx = await ctx.Database.BeginTransactionAsync();
var deleteAttrIds = _attrs.Where(a => a.Value == null).Select(a => a.Key).ToList();
await ctx.WineAttributes.Where(a => deleteAttrIds.Contains(a.AttrId)).ExecuteDeleteAsync();
foreach (var (attr, old) in _attrIds) {
@@ -63,7 +62,6 @@ namespace Elwig.Windows {
ctx.Add(attr);
}
await ctx.SaveChangesAsync();
await tx.CommitAsync();
}
private void WineAttributeList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
@@ -41,7 +41,6 @@ namespace Elwig.Windows {
if (!_cultChanged || _cultList == null || _cults == null || _cultIds == null)
return;
using var tx = await ctx.Database.BeginTransactionAsync();
var deleteCultIds = _cults.Where(c => c.Value == null).Select(c => c.Key).ToList();
await ctx.WineCultivations.Where(c => deleteCultIds.Contains(c.CultId)).ExecuteDeleteAsync();
foreach (var (cult, old) in _cultIds) {
@@ -62,7 +61,6 @@ namespace Elwig.Windows {
ctx.Add(cult);
}
await ctx.SaveChangesAsync();
await tx.CommitAsync();
}
private void WineCultivationList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
+32 -40
View File
@@ -1,5 +1,6 @@
using Elwig.Helpers;
using Elwig.Models.Entities;
using Elwig.Services;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
@@ -31,14 +32,15 @@ namespace Elwig.Windows {
AreaCommitmentTypeMinKgPerHaInput, AreaCommitmentTypePenaltyPerKgInput,
AreaCommitmentTypePenaltyInput, AreaCommitmentTypePenaltyNoneInput,
SeasonMaxKgPerHaInput, SeasonVatNormalInput, SeasonVatFlatrateInput, SeasonStartInput, SeasonEndInput,
SeasonMinKgPerBsInput, SeasonMaxKgPerBsInput, SeasonBsValueInput,
SeasonMinKgPerShareInput, SeasonMaxKgPerShareInput, SeasonShareValueInput,
SeasonPenaltyPerKgInput, SeasonPenaltyInput, SeasonPenaltyNoneInput,
SeasonPenaltyPerBsInput, SeasonPenaltyPerBsNoneInput,
SeasonPenaltyPerShareInput, SeasonPenaltyPerShareNoneInput,
SeasonModifierIdInput, SeasonModifierNameInput, SeasonModifierRelInput, SeasonModifierAbsInput,
];
WineAttributeFillLowerInput.Visibility = Visibility.Hidden;
WineAttributeFillLowerLabel.Visibility = Visibility.Hidden;
ParameterAreaComGroup.Header = ParameterAreaComGroup.Header.ToString()!.Split('(')[0] + $"({Utils.CurrentLastSeason})";
LockInputs();
}
protected override void ShortcutNew() { }
@@ -78,25 +80,19 @@ namespace Elwig.Windows {
SeasonMaxKgPerHaInput.IsReadOnly = true;
SeasonVatNormalInput.IsReadOnly = true;
SeasonVatFlatrateInput.IsReadOnly = true;
SeasonMinKgPerBsInput.IsReadOnly = true;
SeasonMaxKgPerBsInput.IsReadOnly = true;
SeasonMinKgPerShareInput.IsReadOnly = true;
SeasonMaxKgPerShareInput.IsReadOnly = true;
SeasonPenaltyPerKgInput.IsReadOnly = true;
SeasonPenaltyInput.IsReadOnly = true;
SeasonPenaltyNoneInput.IsReadOnly = true;
SeasonPenaltyPerBsInput.IsReadOnly = true;
SeasonPenaltyPerBsNoneInput.IsReadOnly = true;
SeasonBsValueInput.IsReadOnly = true;
SeasonPenaltyPerShareInput.IsReadOnly = true;
SeasonPenaltyPerShareNoneInput.IsReadOnly = true;
SeasonShareValueInput.IsReadOnly = true;
SeasonModifierIdInput.IsReadOnly = true;
SeasonModifierNameInput.IsReadOnly = true;
SeasonModifierRelInput.IsReadOnly = true;
SeasonModifierAbsInput.IsReadOnly = true;
ParameterAllowAttrIntoLowerInput.IsEnabled = false;
ParameterAvoidUnderDeliveriesInput.IsEnabled = false;
ParameterHonorGebundenInput.IsEnabled = false;
ParameterExportEbicsVersion.IsEnabled = false;
ParameterExportEbicsAddress.IsEnabled = false;
}
new protected void UnlockInputs() {
@@ -132,25 +128,19 @@ namespace Elwig.Windows {
SeasonMaxKgPerHaInput.IsReadOnly = false;
SeasonVatNormalInput.IsReadOnly = false;
SeasonVatFlatrateInput.IsReadOnly = false;
SeasonMinKgPerBsInput.IsReadOnly = false;
SeasonMaxKgPerBsInput.IsReadOnly = false;
SeasonMinKgPerShareInput.IsReadOnly = false;
SeasonMaxKgPerShareInput.IsReadOnly = false;
SeasonPenaltyPerKgInput.IsReadOnly = false;
SeasonPenaltyInput.IsReadOnly = false;
SeasonPenaltyNoneInput.IsReadOnly = false;
SeasonPenaltyPerBsInput.IsReadOnly = false;
SeasonPenaltyPerBsNoneInput.IsReadOnly = false;
SeasonBsValueInput.IsReadOnly = false;
SeasonPenaltyPerShareInput.IsReadOnly = false;
SeasonPenaltyPerShareNoneInput.IsReadOnly = false;
SeasonShareValueInput.IsReadOnly = false;
SeasonModifierIdInput.IsReadOnly = false;
SeasonModifierNameInput.IsReadOnly = false;
SeasonModifierRelInput.IsReadOnly = false;
SeasonModifierAbsInput.IsReadOnly = false;
ParameterAllowAttrIntoLowerInput.IsEnabled = true;
ParameterAvoidUnderDeliveriesInput.IsEnabled = true;
ParameterHonorGebundenInput.IsEnabled = true;
ParameterExportEbicsVersion.IsEnabled = true;
ParameterExportEbicsAddress.IsEnabled = true;
}
protected override async Task OnRenewContext(AppDbContext ctx) {
@@ -300,9 +290,7 @@ namespace Elwig.Windows {
try {
await Save();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Stammdaten aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Stammdaten aktualisieren", exc);
SaveButton.IsEnabled = true;
Mouse.OverrideCursor = null;
return;
@@ -360,6 +348,8 @@ namespace Elwig.Windows {
TextElementDeliveryConfirmation.Text = p.TextDeliveryConfirmation;
TextElementCreditNote.Text = p.TextCreditNote;
ParameterBusinessShareModeInput.SelectedIndex = p.ModeBusinessShares;
ParameterBusinessShareHistoryInput.IsChecked = p.EnableMemberHistory;
ParameterAllowAttrIntoLowerInput.IsChecked = s?.Billing_AllowAttrsIntoLower ?? false;
ParameterAvoidUnderDeliveriesInput.IsChecked = s?.Billing_AvoidUnderDeliveries ?? false;
ParameterHonorGebundenInput.IsChecked = s?.Billing_HonorGebunden ?? false;
@@ -371,27 +361,29 @@ namespace Elwig.Windows {
private async Task UpdateClientParameters(ClientParameters p) {
p.Name = ClientNameInput.Text;
p.NameSuffix = string.IsNullOrEmpty(ClientNameSuffixInput.Text) ? null : ClientNameSuffixInput.Text;
p.NameSuffix = string.IsNullOrWhiteSpace(ClientNameSuffixInput.Text) ? null : ClientNameSuffixInput.Text;
p.NameType = ClientNameTypeInput.Text;
p.NameToken = ClientNameTokenInput.Text;
p.NameShort = ClientNameShortInput.Text;
p.Address = ClientAddressInput.Text;
p.Plz = int.Parse(ClientPlzInput.Text);
p.Ort = ClientOrtInput.Text;
p.Iban = string.IsNullOrEmpty(ClientIbanInput.Text) ? null : ClientIbanInput.Text;
p.Bic = string.IsNullOrEmpty(ClientBicInput.Text) ? null : ClientBicInput.Text;
p.UstIdNr = string.IsNullOrEmpty(ClientUstIdNrInput.Text) ? null : ClientUstIdNrInput.Text;
p.LfbisNr = string.IsNullOrEmpty(ClientLfbisNrInput.Text) ? null : ClientLfbisNrInput.Text;
p.OrganicAuthority = string.IsNullOrEmpty(ClientOrganicAuthorityInput.Text) ? null : ClientOrganicAuthorityInput.Text;
p.PhoneNr = string.IsNullOrEmpty(ClientPhoneNrInput.Text) ? null : ClientPhoneNrInput.Text;
p.FaxNr = string.IsNullOrEmpty(ClientFaxNrInput.Text) ? null : ClientFaxNrInput.Text;
p.EmailAddress = string.IsNullOrEmpty(ClientEmailAddressInput.Text) ? null : ClientEmailAddressInput.Text;
p.Website = string.IsNullOrEmpty(ClientWebsiteInput.Text) ? null : ClientWebsiteInput.Text;
p.Iban = string.IsNullOrWhiteSpace(ClientIbanInput.Text) ? null : ClientIbanInput.Text;
p.Bic = string.IsNullOrWhiteSpace(ClientBicInput.Text) ? null : ClientBicInput.Text;
p.UstIdNr = string.IsNullOrWhiteSpace(ClientUstIdNrInput.Text) ? null : ClientUstIdNrInput.Text;
p.LfbisNr = string.IsNullOrWhiteSpace(ClientLfbisNrInput.Text) ? null : ClientLfbisNrInput.Text;
p.OrganicAuthority = string.IsNullOrWhiteSpace(ClientOrganicAuthorityInput.Text) ? null : ClientOrganicAuthorityInput.Text;
p.PhoneNr = string.IsNullOrWhiteSpace(ClientPhoneNrInput.Text) ? null : ClientPhoneNrInput.Text;
p.FaxNr = string.IsNullOrWhiteSpace(ClientFaxNrInput.Text) ? null : ClientFaxNrInput.Text;
p.EmailAddress = string.IsNullOrWhiteSpace(ClientEmailAddressInput.Text) ? null : ClientEmailAddressInput.Text;
p.Website = string.IsNullOrWhiteSpace(ClientWebsiteInput.Text) ? null : ClientWebsiteInput.Text;
p.TextDeliveryNote = string.IsNullOrEmpty(TextElementDeliveryNote.Text) ? null : TextElementDeliveryNote.Text;
p.ModeBusinessShares = ParameterBusinessShareModeInput.SelectedIndex;
p.EnableMemberHistory = ParameterBusinessShareHistoryInput.IsChecked ?? false;
p.TextDeliveryNote = string.IsNullOrWhiteSpace(TextElementDeliveryNote.Text) ? null : TextElementDeliveryNote.Text;
p.ModeDeliveryNoteStats = (ModeDeliveryNoteNone.IsChecked == true) ? 0 : (ModeDeliveryNoteGaOnly.IsChecked == true) ? 1 : (ModeDeliveryNoteShort.IsChecked == true) ? 2 : (ModeDeliveryNoteFull.IsChecked == true) ? 3 : 2;
p.TextDeliveryConfirmation = string.IsNullOrEmpty(TextElementDeliveryConfirmation.Text) ? null : TextElementDeliveryConfirmation.Text;
p.TextCreditNote = string.IsNullOrEmpty(TextElementCreditNote.Text) ? null : TextElementCreditNote.Text;
p.TextDeliveryConfirmation = string.IsNullOrWhiteSpace(TextElementDeliveryConfirmation.Text) ? null : TextElementDeliveryConfirmation.Text;
p.TextCreditNote = string.IsNullOrWhiteSpace(TextElementCreditNote.Text) ? null : TextElementCreditNote.Text;
p.ExportEbicsVersion = ParameterExportEbicsVersion.SelectedIndex + 3;
p.ExportEbicsAddress = ParameterExportEbicsAddress.SelectedIndex;
+65 -62
View File
@@ -10,6 +10,7 @@ using Elwig.Helpers.Billing;
using Elwig.Models.Entities;
using ScottPlot.Plottables;
using ScottPlot;
using Elwig.Services;
namespace Elwig.Windows {
public partial class ChartWindow : ContextWindow {
@@ -25,12 +26,12 @@ namespace Elwig.Windows {
private bool HasChanged = false;
private bool Updating = false;
private Scatter DataPlot;
private Scatter? DataPlot;
private Scatter? GebundenPlot;
private Marker HighlightedPointPlot;
private Marker PrimaryMarkedPointPlot;
private Marker SecondaryMarkedPointPlot;
private Text TooltipPlot;
private Marker? HighlightedPointPlot;
private Marker? PrimaryMarkedPointPlot;
private Marker? SecondaryMarkedPointPlot;
private Text? TooltipPlot;
private static readonly LegendItem
UngebundenLegend = new() {
@@ -76,15 +77,13 @@ namespace Elwig.Windows {
PriceInput.Unit = $"{CurrencySymbol}/kg";
GebundenFlatBonus.Unit = $"{CurrencySymbol}/kg";
PaymentVar = ctx.PaymentVariants.Find(year, avnr) ?? throw new ArgumentException("PaymentVar not found");
Title = $"{PaymentVar?.Name} - Lese {year} - Elwig";
Title = $"{PaymentVar.Name} - Lese {year} - Elwig";
LockContext = true;
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
if (HasChanged) {
var r = MessageBox.Show("Soll das Fenster wirklich geschlossen werden? Nicht gespeicherte Änderungen werden NICHT übernommen!", "Schließen bestätigen",
MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r != MessageBoxResult.Yes) {
if (!InteractionService.AskConfirmation("Schließen bestätigen", "Soll das Fenster wirklich geschlossen werden? Nicht gespeicherte Änderungen werden NICHT übernommen!")) {
e.Cancel = true;
return;
}
@@ -105,23 +104,22 @@ namespace Elwig.Windows {
try {
var data = EditBillingData.FromJson(PaymentVar.Data, await Utils.GetVaributes(ctx, Year));
var paymentEntries = await data.GetPaymentGraphEntries(ctx, Season);
var paymentEntries = (await data.GetPaymentGraphEntries(ctx, Season)).ToList();
GraphEntries = [
..paymentEntries,
..await data.GetQualityGraphEntries(ctx, Season, paymentEntries.Any() ? paymentEntries.Max(e => e.Id) : 0)
..await data.GetQualityGraphEntries(ctx, Season, paymentEntries.Count != 0 ? paymentEntries.Max(e => e.Id) : 0)
];
} catch (KeyNotFoundException ex) {
var key = ex.Message.Split('\'')[1].Split('\'')[0];
MessageBox.Show($"Fehler beim Laden der Auszahlungsvariante:\n\n" +
$"Mit unbekanntem Attribut '{key}' kann nicht umgegangen werden.", "Fehler",
MessageBoxButton.OK, MessageBoxImage.Error);
} catch (KeyNotFoundException exc) {
var key = exc.Message.Split('\'')[1].Split('\'')[0];
InteractionService.ShowError("Fehler",
$"Fehler beim Laden der Auszahlungsvariante:\n\n" +
$"Mit unbekanntem Attribut '{key}' kann nicht umgegangen werden.");
} catch (ArgumentException) {
MessageBox.Show($"Fehler beim Laden der Auszahlungsvariante:\n\n" +
$"Die Daten der Auszahlungsvariante entsprechen nicht dem benötigtem Format.", "Fehler",
MessageBoxButton.OK, MessageBoxImage.Error);
} catch (Exception ex) {
MessageBox.Show("Fehler beim Laden der Auszahlungsvariante:\n\n" + ex.Message, "Fehler",
MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Fehler",
$"Fehler beim Laden der Auszahlungsvariante:\n\n" +
$"Die Daten der Auszahlungsvariante entsprechen nicht dem benötigtem Format.");
} catch (Exception exc) {
InteractionService.ShowException("Fehler", "Fehler beim Laden der Auszahlungsvariante", exc);
}
Vaributes = await Utils.GetVaributeList(ctx, Year);
GraphEntries.ForEach(e => {
@@ -217,10 +215,12 @@ namespace Elwig.Windows {
GebundenPlot.MarkerStyle = new MarkerStyle(MarkerShape.FilledCircle, 9, ColorGebunden);
}
DataPlot = OechslePricePlot.Plot.Add.Scatter(SelectedGraphEntry!.DataGraph.DataX, SelectedGraphEntry!.DataGraph.DataY);
DataPlot.LineStyle.Color = ColorUngebunden;
DataPlot.Color = ColorUngebunden;
DataPlot.MarkerStyle = new MarkerStyle(MarkerShape.FilledCircle, 9, ColorUngebunden);
if (SelectedGraphEntry != null) {
DataPlot = OechslePricePlot.Plot.Add.Scatter(SelectedGraphEntry.DataGraph.DataX, SelectedGraphEntry.DataGraph.DataY);
DataPlot.LineStyle.Color = ColorUngebunden;
DataPlot.Color = ColorUngebunden;
DataPlot.MarkerStyle = new MarkerStyle(MarkerShape.FilledCircle, 9, ColorUngebunden);
}
OechslePricePlot.Plot.Axes.SetLimits(Math.Min(GraphEntry.MinX, GraphEntry.MinXGeb) - 1, GraphEntry.MaxX + 1, -0.1, 1.5);
@@ -246,7 +246,10 @@ namespace Elwig.Windows {
SecondaryMarkedPoint = -1;
ChangeActiveGraph(null);
HideGradationLines();
OechslePricePlot.Plot.Remove(DataPlot);
if (DataPlot != null) {
OechslePricePlot.Plot.Remove(DataPlot);
DataPlot = null;
}
if (GebundenPlot != null) {
OechslePricePlot.Plot.Remove(GebundenPlot);
GebundenPlot = null;
@@ -256,9 +259,9 @@ namespace Elwig.Windows {
OechslePricePlot.Refresh();
}
private void ChangeMarker(Marker point, bool visible, double x = 0, double y = 0) {
point.Location = new Coordinates(x, y);
point.IsVisible = visible;
private static void ChangeMarker(Marker? point, bool visible, double x = 0, double y = 0) {
point?.Location = new Coordinates(x, y);
point?.IsVisible = visible;
}
private void EnableActionButtons() {
@@ -418,7 +421,7 @@ namespace Elwig.Windows {
var res = Validator.CheckDecimal(PriceInput, true, 2, Season.Precision);
if (res.IsValid && double.TryParse(PriceInput.Text, out double price)) {
ActiveGraph.SetPriceAt(PrimaryMarkedPoint, price);
PrimaryMarkedPointPlot.Location = new Coordinates(PrimaryMarkedPointPlot.Location.X, price);
PrimaryMarkedPointPlot?.Location = new Coordinates(PrimaryMarkedPointPlot.Location.X, price);
SetHasChanged();
OechslePricePlot.Refresh();
CheckGebundenTypeFixed();
@@ -494,17 +497,19 @@ namespace Elwig.Windows {
ChangeMarker(PrimaryMarkedPointPlot, true, ActiveGraph.GetOechsleAt(PrimaryMarkedPoint), ActiveGraph.GetPriceAt(PrimaryMarkedPoint));
Updating = true;
OechsleInput.Text = Highlighted.Graph!.GetOechsleAt(Highlighted.Index).ToString();
GebInput.IsChecked = Highlighted.Graph == SelectedGraphEntry?.GebundenGraph;
PriceInput.Text = Math.Round(Highlighted.Graph.GetPriceAt(Highlighted.Index), Season.Precision).ToString();
Updating = false;
if (Highlighted.Graph != null) {
Updating = true;
OechsleInput.Text = Highlighted.Graph.GetOechsleAt(Highlighted.Index).ToString();
GebInput.IsChecked = Highlighted.Graph == SelectedGraphEntry?.GebundenGraph;
PriceInput.Text = Math.Round(Highlighted.Graph.GetPriceAt(Highlighted.Index), Season.Precision).ToString();
Updating = false;
}
EnableActionButtons();
} else {
PrimaryMarkedPoint = -1;
SecondaryMarkedPoint = -1;
if (SelectedGraphEntry!.GebundenGraph != null) {
if (SelectedGraphEntry?.GebundenGraph != null) {
ChangeActiveGraph(null);
}
ChangeMarker(PrimaryMarkedPointPlot, false);
@@ -550,21 +555,26 @@ namespace Elwig.Windows {
if (mouseOnData != null) {
ChangeMarker(HighlightedPointPlot, true, mouseOnData.Value.X, mouseOnData.Value.Y);
HighlightedPointPlot.IsVisible = true;
HighlightedPointPlot?.IsVisible = true;
HoverChanged = true ^ HoverActive;
HoverActive = true;
HandleTooltip(mouseOnData.Value.X, mouseOnData.Value.Y, mouseOnData.Value.Index, SelectedGraphEntry!.DataGraph, e.GetPosition(OechslePricePlot), e is MouseWheelEventArgs);
if (SelectedGraphEntry?.DataGraph is Graph g)
HandleTooltip(mouseOnData.Value.X, mouseOnData.Value.Y, mouseOnData.Value.Index, g, e.GetPosition(OechslePricePlot), e is MouseWheelEventArgs);
} else if (mouseOnGebunden != null) {
ChangeMarker(HighlightedPointPlot, true, mouseOnGebunden.Value.X, mouseOnGebunden.Value.Y);
HighlightedPointPlot.IsVisible = true;
HighlightedPointPlot?.IsVisible = true;
HoverChanged = true ^ HoverActive;
HoverActive = true;
HandleTooltip(mouseOnGebunden.Value.X, mouseOnGebunden.Value.Y, mouseOnGebunden.Value.Index, SelectedGraphEntry!.GebundenGraph!, e.GetPosition(OechslePricePlot), e is MouseWheelEventArgs);
if (SelectedGraphEntry?.GebundenGraph is Graph g)
HandleTooltip(mouseOnGebunden.Value.X, mouseOnGebunden.Value.Y, mouseOnGebunden.Value.Index, g, e.GetPosition(OechslePricePlot), e is MouseWheelEventArgs);
} else {
ChangeMarker(HighlightedPointPlot, false);
HoverChanged = false ^ HoverActive;
HoverActive = false;
OechslePricePlot.Plot.PlottableList.Remove(TooltipPlot);
if (TooltipPlot != null) {
OechslePricePlot.Plot.PlottableList.Remove(TooltipPlot);
TooltipPlot = null;
}
OechslePricePlot.Refresh();
}
}
@@ -579,7 +589,10 @@ namespace Elwig.Windows {
private void HandleTooltip(double pointX, double pointY, int pointIndex, Graph g, Point p, bool force) {
if (force || LastHighlighted != Highlighted || HoverChanged) {
OechslePricePlot.Plot.PlottableList.Remove(TooltipPlot);
if (TooltipPlot != null) {
OechslePricePlot.Plot.PlottableList.Remove(TooltipPlot);
TooltipPlot = null;
}
if (TooltipInput.IsChecked == true) {
Coordinates mouseLocation = GetMouseCoordinates(new(p.X, p.Y - 30));
TooltipPlot = OechslePricePlot.Plot.Add.Text($"Oechsle: {pointX:N2}, Preis: {Math.Round(pointY, Season.Precision)} {CurrencySymbol}/kg", mouseLocation.X, mouseLocation.Y);
@@ -622,11 +635,7 @@ namespace Elwig.Windows {
private void DeleteButton_Click(object sender, RoutedEventArgs e) {
if (SelectedGraphEntry == null) return;
var r = MessageBox.Show(
$"Soll die Kurve {SelectedGraphEntry.Id} (verwendet in folgenden Verträgen: {SelectedGraphEntry.VaributeStringSimple}) wirklich gelöscht werden?",
"Kurve löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r == MessageBoxResult.Yes) {
if (InteractionService.AskConfirmation("Kurve löschen", $"Soll die Kurve {SelectedGraphEntry.Id} (verwendet in folgenden Verträgen: {SelectedGraphEntry.VaributeStringSimple}) wirklich gelöscht werden?")) {
GraphEntries.Remove(SelectedGraphEntry);
SetHasChanged();
GraphList.Items.Refresh();
@@ -651,9 +660,7 @@ namespace Elwig.Windows {
});
} catch (Exception exc) {
Mouse.OverrideCursor = null;
var str = "Der Eintrag konnte nicht in der Datenbank gespeichert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Auszahlungsvariante speichern", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Auszahlungsvariante speichern", exc);
SaveButton.IsEnabled = true;
return;
}
@@ -664,9 +671,9 @@ namespace Elwig.Windows {
await b.Calculate(false);
});
} catch (KeyNotFoundException exc) {
MessageBox.Show(exc.Message, "Noch nicht alle Preise festgelegt", MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Noch nicht alle Preise festgelegt", exc.Message);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Berechnungsfehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Berechnungsfehler", exc);
}
LockContext = false;
@@ -684,7 +691,7 @@ namespace Elwig.Windows {
}
}
private void DisableTextBox(TextBox u) {
private static void DisableTextBox(TextBox u) {
u.IsEnabled = false;
u.IsReadOnly = true;
}
@@ -715,10 +722,8 @@ namespace Elwig.Windows {
ActiveGraph = g;
}
private void ChangeLineWidth(Scatter? p, double lineWidth) {
if (p != null) {
p.LineWidth = (float)lineWidth;
}
private static void ChangeLineWidth(Scatter? p, double lineWidth) {
p?.LineWidth = (float)lineWidth;
}
private void GraphList_SelectionChanged(object sender, SelectionChangedEventArgs e) {
@@ -760,7 +765,7 @@ namespace Elwig.Windows {
}
}
SelectedGraphEntry!.Vaributes = VaributeInput.SelectedItems.Cast<Varibute>().ToList();
SelectedGraphEntry?.Vaributes = [.. VaributeInput.SelectedItems.Cast<Varibute>()];
SetHasChanged();
GraphList.Items.Refresh();
VaributeInput.Items.Refresh();
@@ -773,9 +778,7 @@ namespace Elwig.Windows {
if (ge != SelectedGraphEntry && ge.Abgewertet == SelectedGraphEntry?.Abgewertet) {
var toRemove = ge.Vaributes.Where(c => c.Listing.Equals(varibute)).ToList();
if (toRemove.Count == 0) continue;
var r = MessageBox.Show($"Achtung: {string.Join(", ", toRemove)} ist bereits in Kurve {ge.Id} in Verwendung!\nSoll die Zuweisung dort entfernt werden?", "Entfernen bestätigen",
MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r != MessageBoxResult.Yes) {
if (!InteractionService.AskConfirmation("Entfernen bestätigen", $"Achtung: {string.Join(", ", toRemove)} ist bereits in Kurve {ge.Id} in Verwendung!\nSoll die Zuweisung dort entfernt werden?")) {
return false;
}
ge.Vaributes.RemoveAll(c => c.Listing.Equals(varibute));
+2 -2
View File
@@ -529,12 +529,12 @@
</Grid.ColumnDefinitions>
<Label Content="Gradation:" Margin="10,10,10,10"/>
<ctrl:UnitTextBox x:Name="GradationOeInput" Unit="°Oe" Text="{Binding GradationOeString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TextChanged="GradationOeInput_TextChanged" KeyUp="Input_KeyUp"
TextChanged="GradationOeInput_TextChanged" LostFocus="GradationOeInput_LostFocus" KeyUp="Input_KeyUp"
Grid.Column="1" Width="54" Margin="0,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Label Content="=" Margin="60,10,10,10" Grid.Column="1"/>
<ctrl:UnitTextBox x:Name="GradationKmwInput" Unit="°KMW" Text="{Binding GradationKmwString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TextChanged="GradationKmwInput_TextChanged" KeyUp="Input_KeyUp"
TextChanged="GradationKmwInput_TextChanged" LostFocus="GradationKmwInput_LostFocus" KeyUp="Input_KeyUp"
Grid.Column="1" Width="68" Margin="78,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Label Content="Qualitätsstufe:" Margin="10,40,10,10"/>
+26 -46
View File
@@ -6,7 +6,6 @@ using Elwig.Models.Entities;
using Elwig.Services;
using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -125,7 +124,8 @@ namespace Elwig.Windows {
public DeliveryAdminWindow(int mgnr) :
this() {
ViewModel.FilterMember = DeliveryService.GetMember(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
using var ctx = new AppDbContext();
ViewModel.FilterMember = ctx.Members.Find(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
ViewModel.Title = $"Lieferungen - {ViewModel.FilterMember.AdministrativeName} - Elwig";
ViewModel.EnableAllSeasons = true;
}
@@ -137,8 +137,9 @@ namespace Elwig.Windows {
if (ViewModel.IsReceipt) {
NewDeliveryButton_Click(null, null);
if (await ctx.FetchSeasons(Utils.CurrentYear).SingleOrDefaultAsync() == null) {
MessageBox.Show("Die Saison für das aktuelle Jahr wurde noch nicht erstellt. Neue Lieferungen können nicht abgespeichert werden.\n\n(Stammdaten -> Saisons -> Neu anlegen...)",
"Saison noch nicht erstellt", MessageBoxButton.OK, MessageBoxImage.Warning);
InteractionService.ShowWarning("Saison noch nicht erstellt",
"Die Saison für das aktuelle Jahr wurde noch nicht erstellt. Neue Lieferungen können nicht abgespeichert werden.\n\n" +
"(Stammdaten -> Saisons -> Neu anlegen...)");
}
}
}
@@ -164,9 +165,7 @@ namespace Elwig.Windows {
private async void Menu_DeliveryNote_Email_Click(object sender, RoutedEventArgs evt) {
if (DeliveryList.SelectedItem is not Delivery d)
return;
var res = MessageBox.Show("Soll eine E-Mail verschickt werden?", "Lieferschein verschicken",
MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
if (res != MessageBoxResult.Yes)
if (!InteractionService.AskQuestion("Lieferschein verschicken", "Soll eine E-Mail verschickt werden?", true))
return;
await DeliveryService.GenerateDeliveryNote(d.Year, d.DId, ExportMode.Email);
}
@@ -174,16 +173,11 @@ namespace Elwig.Windows {
private async void Menu_Bki_SaveList_Click(object sender, RoutedEventArgs evt) {
if (sender is not MenuItem m) return;
var year = int.Parse(m.Header.ToString()?.Split(" ")[^1] ?? Utils.CurrentLastSeason.ToString());
var d = new SaveFileDialog() {
FileName = $"{App.Client.NameToken}-Traubentransportscheinliste-{year}.{Bki.FileExtension}",
DefaultExt = Bki.FileExtension,
Filter = "CSV-Datei (*.csv)|*.csv",
Title = $"Traubentransportscheinliste (BKI) speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
var filename = InteractionService.SaveFile("Traubentransportscheinliste (BKI)", $"{App.Client.NameToken}-Traubentransportscheinliste-{year}", Bki.FileExtension);
if (filename != null) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
using var file = new Bki(d.FileName);
using var file = new Bki(filename);
await file.ExportAsync(year);
});
Mouse.OverrideCursor = null;
@@ -431,6 +425,8 @@ namespace Elwig.Windows {
var deliveries = await deliveryQuery
.Include(d => d.Parts).ThenInclude(p => p.PartModifiers).ThenInclude(m => m.Modifier)
.Include(d => d.Parts).ThenInclude(p => p.Variety)
.Include(d => d.Parts).ThenInclude(p => p.Attribute)
.Include(d => d.Parts).ThenInclude(p => p.Cultivation)
.IgnoreAutoIncludes()
.AsSplitQuery()
.ToListAsync();
@@ -487,7 +483,7 @@ namespace Elwig.Windows {
await base.OnRenewContext(ctx);
if (ViewModel.FilterMember != null) {
if (await DeliveryService.GetMemberAsync(ViewModel.FilterMember.MgNr) is not Member m) {
if (await ctx.FetchMembers(ViewModel.FilterMember.MgNr).SingleOrDefaultAsync() is not Member m) {
Close();
return;
}
@@ -527,7 +523,7 @@ namespace Elwig.Windows {
Menu_BulkAction_AddModifier.Items.Clear();
Menu_BulkAction_RemoveModifier.Items.Clear();
foreach (var mod in Modifiers.GetValueOrDefault(ViewModel.SelectedDelivery?.Year ?? 0, []).Where(m => m.IsActive || !IsCreating)) {
foreach (var mod in Modifiers.GetValueOrDefault(ViewModel.SelectedDelivery?.Year ?? year, [])) {
var i1 = new MenuItem {
Header = mod.Name,
};
@@ -639,11 +635,10 @@ namespace Elwig.Windows {
try {
var res = await s.Weigh();
OnWeighingResult(s, res);
} catch (Exception ex) {
ViewModel.LastScaleError = ex.Message.Split(": ")[^1];
} catch (Exception exc) {
ViewModel.LastScaleError = exc.Message.Split(": ")[^1];
OnWeighingResult(s, new());
MessageBox.Show($"Beim Wiegen ist ein Fehler aufgetreten:\n\n{ex.Message}", "Waagenfehler",
MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Waagenfehler", "Beim Wiegen ist ein Fehler aufgetreten", exc);
}
ViewModel.ManualWeighingReason = null;
ManualWeighingInput.IsChecked = false;
@@ -799,9 +794,7 @@ namespace Elwig.Windows {
);
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Lieferung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Lieferung aktualisieren", exc);
FinishButton.IsEnabled = true;
SaveButton.IsEnabled = true;
Mouse.OverrideCursor = null;
@@ -837,9 +830,7 @@ namespace Elwig.Windows {
);
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Lieferung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Lieferung aktualisieren", exc);
FinishButton.IsEnabled = true;
SaveButton.IsEnabled = true;
Mouse.OverrideCursor = null;
@@ -869,9 +860,8 @@ namespace Elwig.Windows {
private async void CancelCreatingButton_Click(object sender, RoutedEventArgs evt) {
if (IsCreating && HasChanged) {
var r = MessageBox.Show("Soll der Vorgang wirklich abgebrochen werden?", "Abbrechen bestätigen",
MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r != MessageBoxResult.Yes) return;
if (!InteractionService.AskConfirmation("Abbrechen bestätigen", "Soll der Vorgang wirklich abgebrochen werden?"))
return;
}
using var ctx = new AppDbContext();
@@ -952,9 +942,7 @@ namespace Elwig.Windows {
}
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Lieferung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Lieferung aktualisieren", exc);
}
Mouse.OverrideCursor = null;
}
@@ -1001,18 +989,13 @@ namespace Elwig.Windows {
if (DeliveryList.SelectedItem is not Delivery d)
return;
var r = MessageBox.Show(
$"Soll die Lieferung {d.LsNr} ({d.Member.AdministrativeName}, MgNr. {d.Member.MgNr}) wirklich unwiderruflich gelöscht werden?",
"Lieferung löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (r == MessageBoxResult.OK) {
if (InteractionService.AskContinue("Lieferung löschen", $"Soll die Lieferung {d.LsNr} ({d.Member.AdministrativeName}, MgNr. {d.Member.MgNr}) wirklich unwiderruflich gelöscht werden?")) {
Mouse.OverrideCursor = Cursors.Wait;
try {
await DeliveryService.DeleteDelivery(d.LsNr);
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Lieferung löschen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Lieferung löschen", exc);
}
Mouse.OverrideCursor = null;
}
@@ -1040,9 +1023,7 @@ namespace Elwig.Windows {
);
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Lieferung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Lieferung aktualisieren", exc);
SaveButton.IsEnabled = true;
Mouse.OverrideCursor = null;
return;
@@ -1220,7 +1201,7 @@ namespace Elwig.Windows {
}
private async Task UpdateLsNr() {
if (string.IsNullOrEmpty(ViewModel.Date) || ViewModel.Branch == null) {
if (string.IsNullOrWhiteSpace(ViewModel.Date) || ViewModel.Branch == null) {
ViewModel.LsNr = "";
} else {
try {
@@ -1279,8 +1260,7 @@ namespace Elwig.Windows {
UpdateWineQualityLevels();
if (!ViewModel.WineVar?.IsQuw ?? false) {
App.MainDispatcher.BeginInvoke(() => {
MessageBox.Show("Die eingegebene Sorte darf nicht als Qualitätswein\nübernommen werden!", "Kein Qualitätswein",
MessageBoxButton.OK, MessageBoxImage.Warning);
InteractionService.ShowWarning("Kein Qualitätswein", "Die eingegebene Sorte darf nicht als Qualitätswein\nübernommen werden!");
});
}
}
+3 -10
View File
@@ -309,10 +309,7 @@ namespace Elwig.Windows {
private async void DeleteDeliveryAncmtButton_Click(object? sender, RoutedEventArgs? evt) {
if (ViewModel.SelectedDeliveryAncmt is not DeliveryAncmt a)
return;
var r = MessageBox.Show(
$"Soll die Anmeldung wirklich unwiderruflich gelöscht werden?",
"Anmeldung löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (r == MessageBoxResult.OK) {
if (InteractionService.AskContinue("Anmeldung löschen", "Soll die Anmeldung wirklich unwiderruflich gelöscht werden?")) {
Mouse.OverrideCursor = Cursors.Wait;
try {
await Task.Run(async () => {
@@ -322,9 +319,7 @@ namespace Elwig.Windows {
});
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Anmeldung löschen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Anmeldung löschen", exc);
}
Mouse.OverrideCursor = null;
}
@@ -347,9 +342,7 @@ namespace Elwig.Windows {
(year, dsnr, mgnr, sortid) = await ViewModel.UpdateDeliveryAncmt(s?.Year, s?.DsNr, s?.MgNr, s?.SortId, s?.Type);
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Anmeldung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Anmeldung aktualisieren", exc);
SaveButton.IsEnabled = true;
return;
} finally {
@@ -177,10 +177,7 @@ namespace Elwig.Windows {
private async void DeleteDeliveryScheduleButton_Click(object? sender, RoutedEventArgs? evt) {
if (ViewModel.SelectedDeliverySchedule is not DeliverySchedule s)
return;
var r = MessageBox.Show(
$"Soll der Leseplan \"{s.Description}\" vom {s.Date:dd.MM.yyyy} wirklich unwiderruflich gelöscht werden?",
"Leseplan löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (r == MessageBoxResult.OK) {
if (InteractionService.AskContinue("Leseplan löschen", $"Soll der Leseplan \"{s.Description}\" vom {s.Date:dd.MM.yyyy} wirklich unwiderruflich gelöscht werden?")) {
Mouse.OverrideCursor = Cursors.Wait;
try {
await Task.Run(async () => {
@@ -190,9 +187,7 @@ namespace Elwig.Windows {
});
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Leseplan löschen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Leseplan löschen", exc);
}
Mouse.OverrideCursor = null;
}
@@ -212,9 +207,7 @@ namespace Elwig.Windows {
await ViewModel.UpdateDeliverySchedule(ViewModel.SelectedDeliverySchedule?.Year, ViewModel.SelectedDeliverySchedule?.DsNr);
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Leseplan aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Leseplan aktualisieren", exc);
SaveButton.IsEnabled = true;
return;
} finally {
+10 -15
View File
@@ -3,6 +3,7 @@ using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Elwig.Models.Dtos;
using Elwig.Models.Entities;
using Elwig.Services;
using MailKit.Net.Smtp;
using Microsoft.EntityFrameworkCore;
using Microsoft.Win32;
@@ -690,7 +691,7 @@ namespace Elwig.Windows {
PrintButton.IsEnabled = PrintDocument != null && !hasPreviewDocs;
EmailButton.IsEnabled = EmailDocuments != null && !hasPreviewDocs && App.Config.Smtp != null;
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
} finally {
UnlockInputs();
GenerateButton.IsEnabled = true;
@@ -774,7 +775,7 @@ namespace Elwig.Windows {
throw new NotImplementedException("Invalid DocType");
}
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
return [];
} finally {
App.MainDispatcher.Invoke(() => {
@@ -787,9 +788,9 @@ namespace Elwig.Windows {
var hasPreviewDocs = memberDocs.Any(m => m.Docs.Any(d => d.Doc.IsPreview));
if (hasPreviewDocs) {
var res = MessageBox.Show("Einige der ausgewählten Dokumente sind nur als vorläufig zu betrachten und können daher nicht verschickt/ausgedruckt werden!\n\nDies könnte an einer noch nicht festgesetzen Auszahlungsvariante liegen oder daran, dass nicht alle Adressaten/Empfänger eine Traubengutschrift erhalten\n(\"Empfänger von Gutschriften\").\n\nSoll mit dem Generieren fortgefahren werden?",
"Vorläufige Dokumente", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (res != MessageBoxResult.OK) {
if (!InteractionService.AskContinue("Vorläufige Dokumente",
"Einige der ausgewählten Dokumente sind nur als vorläufig zu betrachten und können daher nicht verschickt/ausgedruckt werden!\n\n" +
"Dies könnte an einer noch nicht festgesetzen Auszahlungsvariante liegen oder daran, dass nicht alle Adressaten/Empfänger eine Traubengutschrift erhalten\n(\"Empfänger von Gutschriften\").\n\nSoll mit dem Generieren fortgefahren werden?")) {
throw new OperationCanceledException("Dokumentenerzeugung abgebrochen!");
}
}
@@ -892,9 +893,7 @@ namespace Elwig.Windows {
GenerateButton.IsEnabled = false;
LockInputs();
var res = MessageBox.Show($"Sollen {PrintDocument.Pages} Blätter ({PrintDocument.TotalPages} Seiten) gedruckt werden?",
"Rundschreiben drucken", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No);
if (res == MessageBoxResult.Yes) {
if (InteractionService.AskQuestion("Rundschreiben drucken", $"Sollen {PrintDocument.Pages} Blätter ({PrintDocument.TotalPages} Seiten) gedruckt werden?", false)) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
if (App.Config.Debug) {
@@ -932,11 +931,8 @@ namespace Elwig.Windows {
client = await Task.Run(Utils.GetSmtpClient);
Mouse.OverrideCursor = null;
var res = MessageBox.Show($"Sollen {EmailDocuments.Count:N0} E-Mails verschickt werden?",
"Rundschreiben verschicken", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No);
if (res != MessageBoxResult.Yes) {
if (!InteractionService.AskQuestion("Rundschreiben verschicken", $"Sollen {EmailDocuments.Count:N0} E-Mails verschickt werden?", false))
return;
}
Mouse.OverrideCursor = Cursors.Wait;
var subject = EmailSubjectInput.Text;
@@ -965,10 +961,9 @@ namespace Elwig.Windows {
}
});
MessageBox.Show("Erfolgreich alle E-Mails verschickt!\n\nEs kann einige Minuten dauern, bis die E-Mails in den Posteingängen der Empfänger aufscheinen.", "Rundschreiben verschicken",
MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Rundschreiben verschicken", "Erfolgreich alle E-Mails verschickt!\n\nEs kann einige Minuten dauern, bis die E-Mails in den Posteingängen der Empfänger aufscheinen.");
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
} finally {
if (client != null)
await client.DisconnectAsync(true);
+53 -81
View File
@@ -59,9 +59,7 @@ namespace Elwig.Windows {
}
Thread.Sleep(100);
if (App.NumWindows > 1 && !App.Current.Windows.Cast<Window>().Any(w => ((w as AdministrationWindow)?.IsEditing ?? false) || ((w as AdministrationWindow)?.IsCreating ?? false))) {
var res = MessageBox.Show("Es sind noch weitere Fenster geöffnet.\nSollen alle Fenster geschlossen werden?",
"Elwig beenden", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (res != MessageBoxResult.Yes) {
if (!InteractionService.AskConfirmation("Elwig beenden", "Es sind noch weitere Fenster geöffnet.\nSollen alle Fenster geschlossen werden?")) {
evt.Cancel = true;
} else {
Application.Current.Shutdown();
@@ -84,9 +82,9 @@ namespace Elwig.Windows {
try {
using var client = await Utils.GetSmtpClient();
await client!.DisconnectAsync(true);
MessageBox.Show("E-Mail-Einstellungen erfolgreich überprüft!", "Erfolg", MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Erfolg", "E -Mail-Einstellungen erfolgreich überprüft!");
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
Mouse.OverrideCursor = null;
}
@@ -115,17 +113,17 @@ namespace Elwig.Windows {
private async void Menu_Scale_SetDateTime_Click(object sender, RoutedEventArgs evt) {
if (App.CommandScales.Count == 0) {
MessageBox.Show("Es sind keine geeigneten Waagen verfügbar!", "Datum und Uhrzeit setzen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Datum und Uhrzeit setzen", "Es sind keine geeigneten Waagen verfügbar!");
return;
}
foreach (var s in App.CommandScales) {
try {
await s.SetDateAndTime(DateTime.Now);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
}
MessageBox.Show("Datum und Uhrzeit auf entsprechenden Waagen gesetzt!", "Datum und Uhrzeit setzen", MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Datum und Uhrzeit setzen", "Datum und Uhrzeit auf entsprechenden Waagen gesetzt!");
}
private void Menu_Database_Query_Click(object sender, RoutedEventArgs evt) {
@@ -157,29 +155,23 @@ namespace Elwig.Windows {
});
}
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
Mouse.OverrideCursor = null;
}
private async void Menu_Database_Backup_Click(object sender, RoutedEventArgs evt) {
try {
var d = new SaveFileDialog() {
Title = "Datenbank sichern - Elwig",
FileName = $"database_{Utils.Today:yyyy-MM-dd}.sql.zip",
DefaultExt = "sql.zip",
Filter = "Komprimierte SQL-Datei (*.sql.zip)|*.sql.zip",
AddExtension = false,
};
if (d.ShowDialog() == true) {
if (!d.FileName.EndsWith(".sql.zip")) d.FileName += ".sql.zip";
var filename = InteractionService.SaveFile("Datenbank-Sicherung", $"database_{Utils.Today:yyyy-MM-dd}", "sql.zip");
if (filename != null) {
if (!filename.EndsWith(".sql.zip")) filename += ".sql.zip";
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
await Database.ExportSql(d.FileName, true);
await Database.ExportSql(filename, true);
});
}
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
Mouse.OverrideCursor = null;
}
@@ -192,16 +184,13 @@ namespace Elwig.Windows {
Filter = "SQLite-Datenbank (*.sqlite3, *.sqlite3.zip, *.sql, *.sql.zip)|*.sqlite3;*.sqlite3.zip;*.sql;*.sql.zip",
};
if (d.ShowDialog() == true) {
var res = MessageBox.Show("Soll die Datenbank wirklich unwiederruflich durch die wiederhergestellte Version ersetzt werden?", "Datenbank wiederherstellen",
MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (res != MessageBoxResult.OK)
if (!InteractionService.AskContinue("Datenbank wiederherstellen", "Soll die Datenbank wirklich unwiederruflich durch die wiederhergestellte Version ersetzt werden?"))
return;
Mouse.OverrideCursor = Cursors.Wait;
await App.ReplaceDatabase(d.FileName);
}
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
Mouse.OverrideCursor = null;
}
@@ -266,15 +255,15 @@ namespace Elwig.Windows {
.ToList();
if (files.Count == 0) {
MessageBox.Show("Die Datenbank wurde noch nicht vom Hauptgerät hochgeladen!", "Datenbank herunterladen",
MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Datenbank herunterladen", "Die Datenbank wurde noch nicht vom Hauptgerät hochgeladen!");
return;
}
var file = files[0];
var res = MessageBox.Show($"Es wurde eine komprimierte Datenbank (ca. {file.Size / 1024 / 1024} MB) vom {file.Timestamp:dd.MM.yyyy, HH:mm} gefunden.\n\nWollen Sie wirklich die aktuelle Datenbank unwiederruflich\nlöschen und durch die gefundene ersetzen?\n\nDas kann zu Datenverlust führen!", "Datenbank herunterladen",
MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (res != MessageBoxResult.OK)
if (!InteractionService.AskContinue("Datenbank herunterladen",
$"Es wurde eine komprimierte Datenbank (ca. {file.Size / 1024 / 1024} MB) vom {file.Timestamp:dd.MM.yyyy, HH:mm} gefunden.\n\n" +
$"Wollen Sie wirklich die aktuelle Datenbank unwiederruflich\nlöschen und durch die gefundene ersetzen?\n\n" +
$"Das kann zu Datenverlust führen!"))
return;
var filename = Path.Combine(App.TempPath, file.Name);
@@ -283,20 +272,21 @@ namespace Elwig.Windows {
await client.DownloadAsync(file.Url, stream);
}
res = MessageBox.Show("Die Datenbank wurde erfolgreich heruntergeladen!\n\nSoll die Datenbank wirklich unwiederruflich ersetzt werden?\n\nWenn Sie unsicher sind sprechen Sie sich mit dem Benutzer des Hauptgerätes ab!", "Datenbank herunterladen",
MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (res != MessageBoxResult.OK)
if (!InteractionService.AskContinue("Datenbank herunterladen",
"Die Datenbank wurde erfolgreich heruntergeladen!\n\n" +
"Soll die Datenbank wirklich unwiederruflich ersetzt werden?\n\n" +
"Wenn Sie unsicher sind sprechen Sie sich mit dem Benutzer des Hauptgerätes ab!"))
return;
await App.MainDispatcher.BeginInvoke(async () => {
await App.ReplaceDatabase(filename);
});
} catch (HttpRequestException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Datenbank herunterladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Datenbank herunterladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (TaskCanceledException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Datenbank herunterladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Datenbank herunterladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Datenbank herunterladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Datenbank herunterladen", exc);
}
});
Mouse.OverrideCursor = null;
@@ -306,9 +296,7 @@ namespace Elwig.Windows {
if (App.Config.SyncUrl == null)
return;
var res = MessageBox.Show("Sind Sie wirklich sicher, dass Sie die Datenbank dieses\nGerätes hochladen möchten? Das sollte nur vom Hauptgerät aus passieren!", "Datenbank hochladen",
MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (res != MessageBoxResult.OK)
if (!InteractionService.AskContinue("Datenbank hochladen", "Sind Sie wirklich sicher, dass Sie die Datenbank dieses\nGerätes hochladen möchten? Das sollte nur vom Hauptgerät aus passieren!"))
return;
Mouse.OverrideCursor = Cursors.Wait;
@@ -317,14 +305,13 @@ namespace Elwig.Windows {
var path = Path.Combine(App.TempPath, "database.sql.zip");
await Database.ExportSql(path, true);
await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
MessageBox.Show($"Hochladen der gesamten Datenbank erfolgreich!", "Datenbank hochladen",
MessageBoxButton.OK, MessageBoxImage.Information);
InteractionService.ShowInformation("Datenbank hochladen", $"Hochladen der gesamten Datenbank erfolgreich!");
} catch (HttpRequestException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Datenbank hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Datenbank hochladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (TaskCanceledException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Datenbank hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Datenbank hochladen", "Eventuell Internetverbindung prüfen!", exc);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Datenbank hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Datenbank hochladen", exc);
}
});
Mouse.OverrideCursor = null;
@@ -457,13 +444,8 @@ namespace Elwig.Windows {
private async void OverUnderDeliveryButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year)
return;
var d = new SaveFileDialog() {
FileName = $"Über-Unterlieferungen-{year}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"Über-/Unterlieferungen {year} speichern unter - Elwig"
};
if (d.ShowDialog() == false)
var filename = InteractionService.SaveFile($"Über-/Unterlieferungen {year}", $"Über-Unterlieferungen-{year}", "ods");
if (filename == null)
return;
Mouse.OverrideCursor = Cursors.Wait;
@@ -477,11 +459,16 @@ namespace Elwig.Windows {
using var ctx = new AppDbContext();
var tbl1 = await OverUnderDeliveryData.ForSeason(ctx.OverUnderDeliveryRows, year);
var tbl2 = await AreaComUnderDeliveryData.ForSeason(ctx.AreaComUnderDeliveryRows, year);
using var ods = new OdsFile(d.FileName);
await ods.AddTable(tbl1);
using var ods = new OdsFile(filename);
if (App.Client.HasRedWhite) {
await ods.AddTable(tbl1.Red);
await ods.AddTable(tbl1.White);
} else {
await ods.AddTable(tbl1.Total);
}
await ods.AddTable(tbl2);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
});
Mouse.OverrideCursor = null;
@@ -490,13 +477,8 @@ namespace Elwig.Windows {
private async void BreakdownButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year)
return;
var d = new SaveFileDialog() {
FileName = $"Aufschlüsselung-{year}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"Sorten-/Qualitätsaufschlüsselung {year} speichern unter - Elwig"
};
if (d.ShowDialog() == false)
var filename = InteractionService.SaveFile($"Sorten-/Qualitätsaufschlüsselung {year}", $"Aufschlüsselung-{year}", "ods");
if (filename == null)
return;
Mouse.OverrideCursor = Cursors.Wait;
@@ -508,7 +490,7 @@ namespace Elwig.Windows {
App.HintContextChange();
using var ctx = new AppDbContext();
using var ods = new OdsFile(d.FileName);
using var ods = new OdsFile(filename);
var tblTotal = await WeightBreakdownData.ForSeason(ctx.WeightBreakDownRows, year);
await ods.AddTable(tblTotal);
foreach (var branch in await ctx.FetchBranches().ToListAsync()) {
@@ -516,7 +498,7 @@ namespace Elwig.Windows {
await ods.AddTable(tbl);
}
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
});
Mouse.OverrideCursor = null;
@@ -525,13 +507,8 @@ namespace Elwig.Windows {
private async void AreaCommitmentsButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year)
return;
var d = new SaveFileDialog() {
FileName = $"Flächenbindungen-{year}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"Flächenbindungen {year} speichern unter - Elwig"
};
if (d.ShowDialog() == false)
var filename = InteractionService.SaveFile($"Flächenbindungen {year}", $"Flächenbindungen-{year}", "ods");
if (filename == null)
return;
Mouse.OverrideCursor = Cursors.Wait;
@@ -544,10 +521,10 @@ namespace Elwig.Windows {
using var ctx = new AppDbContext();
var tbl = await MemberAreaComsData.ForSeason(ctx.MemberAreaComsRows, year);
using var ods = new OdsFile(d.FileName);
using var ods = new OdsFile(filename);
await ods.AddTable(tbl);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
});
Mouse.OverrideCursor = null;
@@ -556,13 +533,8 @@ namespace Elwig.Windows {
private async void BreakdownMemberVarietyButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year)
return;
var d = new SaveFileDialog() {
FileName = $"Liefermengen-Ertrag-{year}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"Liefermengen/Ertrag {year} speichern unter - Elwig"
};
if (d.ShowDialog() == false)
var filename = InteractionService.SaveFile($"Liefermengen/Ertrag {year}", $"Liefermengen-Ertrag-{year}", "ods");
if (filename == null)
return;
Mouse.OverrideCursor = Cursors.Wait;
@@ -575,10 +547,10 @@ namespace Elwig.Windows {
using var ctx = new AppDbContext();
var tbl = await MemberDeliveryYieldsPerVarietyData.ForSeason(ctx.MemberDeliveryPerVariantRows, year);
using var ods = new OdsFile(d.FileName);
using var ods = new OdsFile(filename);
await ods.AddTable(tbl);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
});
Mouse.OverrideCursor = null;
+22 -15
View File
@@ -233,7 +233,7 @@
</DataGridTextColumn>
<DataGridTextColumn Header="Nachname" Binding="{Binding Name}" Width="140"/>
<DataGridTextColumn Header="Vorname" Binding="{Binding GivenName}" Width="140"/>
<DataGridTextColumn Header="GA" Binding="{Binding BusinessShares, StringFormat='{}{0} '}" Width="40">
<DataGridTextColumn Header="GA" Binding="{Binding SharesActive, StringFormat='{}{0} '}" Width="40">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
@@ -619,16 +619,27 @@
Margin="0,40,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="DateInput_TextChanged" LostFocus="DateInput_LostFocus"/>
<Label Content="Geschäftsanteile:" Margin="10,70,0,0" Grid.Column="0"/>
<TextBox x:Name="BusinessSharesInput" Text="{Binding BusinessSharesString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,70,10,0" Width="48" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="IntegerInput_TextChanged"/>
<Label Content="BH-Konto:" Margin="10,100,0,0" Grid.Column="0"/>
<Label Content="BH-Konto:" Margin="10,70,0,0" Grid.Column="0"/>
<TextBox x:Name="AccountingNrInput" Text="{Binding AccountingNr, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,100,10,0" Grid.Column="1"
Margin="0,70,10,0" Width="63" Grid.Column="1" HorizontalAlignment="Left"
TextChanged="TextBox_TextChanged"/>
<Label Content="Geschäftsanteile:" Margin="10,95,0,0" Grid.Column="0"/>
<Label x:Name="BusinessSharesLabel" Content="(rot/weiß/ruhend)" Margin="10,108,0,0" Grid.Column="0" FontSize="10"/>
<TextBox x:Name="BusinessShares1Input" Text="{Binding BusinessShares1String, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,100,10,0" Width="32" Grid.Column="1" Grid.ColumnSpan="2" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="IntegerInput_TextChanged"/>
<TextBox x:Name="BusinessShares2Input" Text="{Binding BusinessShares2String, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="37,100,10,0" Width="32" Grid.Column="1" Grid.ColumnSpan="2" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="IntegerInput_TextChanged"/>
<TextBox x:Name="BusinessShares3Input" Text="{Binding BusinessShares3String, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="74,100,10,0" Width="32" Grid.Column="1" Grid.ColumnSpan="2" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="IntegerInput_TextChanged"/>
<Button x:Name="BusinessSharesButton" FontFamily="Segoe MDL2 Assets" Content="&#xEB05;" FontSize="16" Padding="0.5,0,0,0" IsEnabled="{Binding IsMemberSelected}"
Margin="111,100,10,0" Width="25" Height="25" Grid.Column="1" Grid.ColumnSpan="2" HorizontalAlignment="Left" VerticalAlignment="Top"
ToolTip="Geschäftsanteile bearbeiten"
Click="BusinessSharesButton_Click"/>
<CheckBox x:Name="ActiveInput" Content="Aktiv" IsChecked="{Binding IsActive, Mode=TwoWay}" IsEnabled="False"
Checked="ActiveInput_Changed" Unchecked="ActiveInput_Changed"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,15,0,0" VerticalAlignment="Top"/>
@@ -656,7 +667,7 @@
<Label Content="Anmerkung:" Margin="10,190,0,0" Grid.Column="0"/>
<TextBox x:Name="CommentInput" Text="{Binding Comment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,190,10,70" Grid.Column="1" Grid.ColumnSpan="2"
Margin="0,190,10,70" Grid.Column="1" Grid.ColumnSpan="2" FontSize="12"
TextChanged="TextBox_TextChanged"
VerticalAlignment="Stretch" Height="auto" AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible"/>
@@ -712,7 +723,7 @@
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="Auto"/>
@@ -729,11 +740,7 @@
</TextBlock>
</StatusBarItem>
<Separator Grid.Column="1"/>
<StatusBarItem Grid.Column="2">
<TextBlock ToolTip="{Binding StatusBusinessSharesToolTip}">
Geschäftsanteile: <Run Text="{Binding StatusBusinessShares}"/>
</TextBlock>
</StatusBarItem>
<StatusBarItem Grid.Column="2" Content="{Binding StatusBusinessShares}"/>
<Separator Grid.Column="3"/>
<StatusBarItem Grid.Column="4">
<TextBlock ToolTip="{Binding StatusAreaCommitmentToolTip}">
+183 -53
View File
@@ -1,18 +1,21 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Elwig.Dialogs;
using Elwig.Documents;
using Elwig.Helpers;
using Elwig.Models.Entities;
using System.Threading.Tasks;
using Elwig.Documents;
using System.Diagnostics;
using Elwig.Dialogs;
using Elwig.Services;
using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
namespace Elwig.Windows {
public partial class MemberAdminWindow : AdministrationWindow {
@@ -37,13 +40,20 @@ namespace Elwig.Windows {
CommandBindings.Add(new CommandBinding(CtrlO, Menu_List_ShowActive_Click));
CommandBindings.Add(new CommandBinding(CtrlShiftP, Menu_MemberDataSheet_Print_Click));
CommandBindings.Add(new CommandBinding(CtrlShiftO, Menu_List_PrintActive_Click));
ExemptInputs = [
List<Control> exempt = [
SearchInput, ActiveMemberInput, MemberList,
];
if (App.Client.EnableMemberHistory) {
exempt.AddRange([BusinessShares1Input, BusinessShares2Input, BusinessShares3Input]);
BusinessShares1Input.IsReadOnly = true;
BusinessShares2Input.IsReadOnly = true;
BusinessShares3Input.IsReadOnly = true;
}
ExemptInputs = [.. exempt];
RequiredInputs = [
MgNrInput, GivenNameInput, NameInput,
AddressInput, PlzInput, OrtInput, BillingOrtInput,
BusinessSharesInput, BranchInput, DefaultKgInput
BusinessShares1Input, BusinessShares2Input, BusinessShares3Input, BranchInput, DefaultKgInput
];
EmailAddressInputs = [
(EmailAddress1Label, EmailAddress1Input),
@@ -84,6 +94,29 @@ namespace Elwig.Windows {
Menu_Export_UploadAll.IsEnabled = App.Config.SyncUrl != null;
ViewModel.ShowOnlyActiveMembers = true;
if (App.Client.HasRedWhite) {
BusinessSharesLabel.Content = "(rot/weiß/ruhend)";
//BusinessShares1Input.Background = Brushes.MistyRose;
BusinessShares1Input.Foreground = Brushes.DarkRed;
BusinessShares1Input.ToolTip = "Geschäftsanteile (rot)";
//BusinessShares2Input.Background = Brushes.MintCream;
BusinessShares2Input.Foreground = Brushes.DarkGreen;
BusinessShares2Input.ToolTip = "Geschäftsanteile (weiß)";
BusinessShares3Input.Background = Brushes.WhiteSmoke;
BusinessShares3Input.ToolTip = "Geschäftsanteile (ruhend)";
} else {
BusinessSharesLabel.Content = "(normal/ruhend)";
BusinessShares1Input.ToolTip = "Geschäftsanteile (normal)";
BusinessShares2Input.Background = Brushes.WhiteSmoke;
BusinessShares2Input.ToolTip = "Geschäftsanteile (ruhend)";
BusinessShares3Input.Visibility = Visibility.Hidden;
BusinessSharesButton.Margin = new(74, 100, 10, 0);
}
if (!App.Client.EnableMemberHistory) {
BusinessSharesButton.Visibility = Visibility.Hidden;
}
UpdateContactInfoVisibility();
}
@@ -114,7 +147,7 @@ namespace Elwig.Windows {
var cursor = Mouse.OverrideCursor != null;
if (!cursor) Mouse.OverrideCursor = Cursors.Wait;
var query = (vm.SearchQuery, vm.ShowOnlyActiveMembers);
var (members, totalMemberCount, totalBusinessShares) = await Task.Run(async () => {
var (members, stat) = await Task.Run(async () => {
using var ctx = new AppDbContext();
var (_, memberQuery, filter) = await vm.GetFilters(ctx);
var members = await memberQuery
@@ -141,10 +174,17 @@ namespace Elwig.Windows {
.ThenBy(m => m.MgNr)];
}
var totalMemberCount = await ctx.Members.CountAsync();
var totalBusinessShares = await ctx.Members.SumAsync(m => m.BusinessShares);
var stat = (await ctx.Members.GroupBy(m => 1).Select(g => new {
Count = g.Count(),
Shares = g.Sum(m => m.Shares),
SharesRed = g.Sum(m => m.SharesRed),
SharesWhite = g.Sum(m => m.SharesWhite),
SharesDormant = g.Sum(m => m.SharesDormant),
SharesTotal = g.Sum(m => m.Shares + m.SharesRed + m.SharesWhite + m.SharesDormant),
SharesActive = g.Sum(m => m.Shares + m.SharesRed + m.SharesWhite),
}).ToListAsync()).Select(g => (g.Count, g.Shares, g.SharesRed, g.SharesWhite, g.SharesDormant, g.SharesTotal, g.SharesActive)).ToList();
return (members, totalMemberCount, totalBusinessShares);
return (members, stat[0]);
});
if (!cursor) Mouse.OverrideCursor = null;
if (query != (ViewModel.SearchQuery, ViewModel.ShowOnlyActiveMembers)) return;
@@ -154,8 +194,37 @@ namespace Elwig.Windows {
if (updateSort && MemberList.SelectedItem != null)
MemberList.ScrollIntoView(MemberList.SelectedItem);
ViewModel.StatusMembers = $"{members.Count:N0} ({totalMemberCount:N0})";
ViewModel.StatusBusinessShares = $"{members.Sum(m => m.BusinessShares):N0} ({totalBusinessShares:N0})";
ViewModel.StatusMembers = $"{members.Count:N0} ({stat.Count:N0})";
var sA = members.Sum(m => m.Shares);
var rA = members.Sum(m => m.SharesRed);
var wA = members.Sum(m => m.SharesWhite);
var dA = members.Sum(m => m.SharesDormant);
List<(int Count, Brush? Color)> a = [];
if (sA > 0) a.Add((sA, null));
if (rA > 0) a.Add((rA, Brushes.DarkRed));
if (wA > 0) a.Add((wA, Brushes.DarkGreen));
if (a.Count == 0) a.Add((0, null));
List<(int Count, Brush? Color)> b = [];
if (stat.Shares > 0) b.Add((stat.Shares, null));
if (stat.SharesRed > 0) b.Add((stat.SharesRed, Brushes.DarkRed));
if (stat.SharesWhite > 0) b.Add((stat.SharesWhite, Brushes.DarkGreen));
if (a.Count == 0) b.Add((0, null));
Run[] inlines = [
new("Geschäftsanteile: "),
.. a.Select(i => new Run($"{i.Count:N0}") { Foreground = i.Color ?? Brushes.Black }).Join(() => new("+")),
new(" ("),
.. b.Select(i => new Run($"{i.Count:N0}") { Foreground = i.Color ?? Brushes.Black }).Join(() => new("+")),
new(")")
];
if (ViewModel.StatusBusinessShares == null) {
ViewModel.StatusBusinessShares = new();
} else {
ViewModel.StatusBusinessShares.Inlines.Clear();
}
ViewModel.StatusBusinessShares.Inlines.AddRange(inlines);
}
private void RefreshInputs(bool validate = false) {
@@ -179,6 +248,17 @@ namespace Elwig.Windows {
ClearOriginalValues();
ClearDefaultValues();
await ViewModel.InitInputs();
if (App.Client.EnableMemberHistory) {
BusinessShares1Input.Text = "0";
BusinessShares2Input.Text = "0";
BusinessShares3Input.Text = "0";
} else if (App.Client.HasRedWhite) {
BusinessShares3Input.Text = "0";
SetDefaultValue(BusinessShares3Input);
} else {
BusinessShares2Input.Text = "0";
SetDefaultValue(BusinessShares2Input);
}
SetDefaultValue(MgNrInput);
SetDefaultValue(EntryDateInput);
SetDefaultValue(ActiveInput);
@@ -190,7 +270,7 @@ namespace Elwig.Windows {
ControlUtils.RenewItemsSource(BranchInput, await ctx.FetchBranches().ToListAsync());
ControlUtils.RenewItemsSource(DefaultKgInput, await ctx.WbKgs.Select(k => k.AtKg).OrderBy(k => k.Name).ToListAsync());
var font = new System.Windows.Media.FontFamily("Segoe MDL2 Assets");
var font = new FontFamily("Segoe MDL2 Assets");
MenuItem? temp = null;
var seasons = await ctx.Seasons.Include(s => s.PaymentVariants).OrderByDescending(s => s.Year).ToListAsync();
Menu_DeliveryConfirmation.Items.Clear();
@@ -267,10 +347,21 @@ namespace Elwig.Windows {
await RefreshList();
var stat = await ctx.Members.GroupBy(m => m.IsActive).Select(g => new {
IsActive = g.Key,
Count = g.Count(),
Shares = g.Sum(m => m.Shares),
SharesRed = g.Sum(m => m.SharesRed),
SharesWhite = g.Sum(m => m.SharesWhite),
SharesDormant = g.Sum(m => m.SharesDormant),
SharesTotal = g.Sum(m => m.Shares + m.SharesRed + m.SharesWhite + m.SharesDormant),
SharesActive = g.Sum(m => m.Shares + m.SharesRed + m.SharesWhite),
}).ToDictionaryAsync(g => g.IsActive);
var (s1, s2) = ('\u2007', '\u202f');
var mA = $"{await ctx.Members.CountAsync(m => m.IsActive):N0}";
var mI = $"{await ctx.Members.CountAsync(m => !m.IsActive):N0}";
var mT = $"{await ctx.Members.CountAsync():N0}";
var mA = $"{stat.GetValueOrDefault(true)?.Count ?? 0:N0}";
var mI = $"{stat.GetValueOrDefault(false)?.Count ?? 0:N0}";
var mT = $"{stat.Sum(g => g.Value.Count):N0}";
var mM = Math.Max(mA.Length, Math.Max(mI.Length, mT.Length));
var mS = mM > 3;
if (mS) mM--;
@@ -279,16 +370,53 @@ namespace Elwig.Windows {
$"{new string(s1, Math.Max(0, mM - mI.Length))}{(mS && mI.Length < 4 ? s2 : "")}{mI} nicht aktive Mitglieder\n" +
$"{new string(s1, Math.Max(0, mM - mT.Length))}{(mS && mT.Length < 4 ? s2 : "")}{mT} Mitglieder gesamt";
var bA = $"{await ctx.Members.Where(m => m.IsActive).SumAsync(m => m.BusinessShares):N0}";
var bI = $"{await ctx.Members.Where(m => !m.IsActive).SumAsync(m => m.BusinessShares):N0}";
var bT = $"{await ctx.Members.SumAsync(m => m.BusinessShares):N0}";
var bM = Math.Max(bA.Length, Math.Max(bI.Length, bT.Length));
var bS = bM > 3;
if (bS) bM--;
ViewModel.StatusBusinessSharesToolTip =
$"{new string(s1, Math.Max(0, bM - bA.Length))}{(bS && bA.Length < 4 ? s2 : "")}{bA} Geschäftsanteile von aktiven Mitgliedern\n" +
$"{new string(s1, Math.Max(0, bM - bI.Length))}{(bS && bI.Length < 4 ? s2 : "")}{bI} Geschäftsanteile von nicht aktiven Mitgliedern\n" +
$"{new string(s1, Math.Max(0, bM - bT.Length))}{(bS && bT.Length < 4 ? s2 : "")}{bT} Geschäftsanteile gesamt";
var grid = new Grid();
grid.ColumnDefinitions.Add(new() { Width = new(100) });
grid.AddToolTipCell("Geschäftsanteile:", 0, 0, bold: true);
grid.AddToolTipCell("Aktive Mitglieder", 1, 0);
grid.AddToolTipCell("Nicht aktive Mg.", 2, 0);
grid.AddToolTipCell("Alle Mitglieder", 3, 0, bold: true);
if (App.Client.HasRedWhite) {
grid.ColumnDefinitions.Add(new() { Width = new(50) });
grid.ColumnDefinitions.Add(new() { Width = new(50) });
grid.ColumnDefinitions.Add(new() { Width = new(50) });
grid.ColumnDefinitions.Add(new() { Width = new(50) });
grid.AddToolTipCell("rot", 0, 1, alignRight: true);
grid.AddToolTipCell("weiß", 0, 2, alignRight: true);
grid.AddToolTipCell("ruhend", 0, 3, alignRight: true);
grid.AddToolTipCell("gesamt", 0, 4, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(true)?.SharesRed ?? 0:N0}", 1, 1, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(true)?.SharesWhite ?? 0:N0}", 1, 2, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(true)?.SharesDormant ?? 0:N0}", 1, 3, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(true)?.SharesTotal ?? 0:N0}", 1, 4, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(false)?.SharesRed ?? 0:N0}", 2, 1, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(false)?.SharesWhite ?? 0:N0}", 2, 2, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(false)?.SharesDormant ?? 0:N0}", 2, 3, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(false)?.SharesTotal ?? 0:N0}", 2, 4, alignRight: true);
grid.AddToolTipCell($"{stat.Sum(g => g.Value.SharesRed):N0}", 3, 1, alignRight: true, bold: true);
grid.AddToolTipCell($"{stat.Sum(g => g.Value.SharesWhite):N0}", 3, 2, alignRight: true, bold: true);
grid.AddToolTipCell($"{stat.Sum(g => g.Value.SharesDormant):N0}", 3, 3, alignRight: true, bold: true);
grid.AddToolTipCell($"{stat.Sum(g => g.Value.SharesTotal):N0}", 3, 4, alignRight: true, bold: true);
} else {
grid.ColumnDefinitions.Add(new() { Width = new(50) });
grid.ColumnDefinitions.Add(new() { Width = new(50) });
grid.ColumnDefinitions.Add(new() { Width = new(50) });
grid.AddToolTipCell("normal", 0, 1, alignRight: true);
grid.AddToolTipCell("ruhend", 0, 2, alignRight: true);
grid.AddToolTipCell("gesamt", 0, 3, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(true)?.Shares ?? 0:N0}", 1, 1, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(true)?.SharesDormant ?? 0:N0}", 1, 2, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(true)?.SharesTotal ?? 0:N0}", 1, 3, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(false)?.Shares ?? 0:N0}", 2, 1, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(false)?.SharesDormant ?? 0:N0}", 2, 2, alignRight: true);
grid.AddToolTipCell($"{stat.GetValueOrDefault(false)?.SharesTotal ?? 0:N0}", 2, 3, alignRight: true);
grid.AddToolTipCell($"{stat.Sum(g => g.Value.Shares):N0}", 3, 1, alignRight: true, bold: true);
grid.AddToolTipCell($"{stat.Sum(g => g.Value.SharesDormant):N0}", 3, 2, alignRight: true, bold: true);
grid.AddToolTipCell($"{stat.Sum(g => g.Value.SharesTotal):N0}", 3, 3, alignRight: true, bold: true);
}
ViewModel.StatusBusinessShares?.ToolTip = grid;
}
private void SetPhoneNrInputVisible(int nr, bool visible, int? position = null) {
@@ -399,9 +527,7 @@ namespace Elwig.Windows {
await MemberService.DeleteMember(m.MgNr, d.DeletePaymentData, d.DeleteDeliveries, d.DeleteAreaComs);
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Mitglied löschen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Mitglied löschen", exc);
}
Mouse.OverrideCursor = null;
}
@@ -422,9 +548,7 @@ namespace Elwig.Windows {
mgnr = await ViewModel.UpdateMember(ViewModel.SelectedMember?.MgNr);
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Mitglied aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Mitglied aktualisieren", exc);
SaveButton.IsEnabled = true;
Mouse.OverrideCursor = null;
return;
@@ -478,6 +602,10 @@ namespace Elwig.Windows {
ViewModel.EnableSearchInputs = true;
}
private void BusinessSharesButton_Click(object sender, RoutedEventArgs evt) {
App.FocusMemberBusinessSharesWindow(ViewModel.SelectedMember!.MgNr);
}
private void AreaCommitmentButton_Click(object sender, RoutedEventArgs evt) {
App.FocusMemberAreaComs(ViewModel.SelectedMember!.MgNr);
}
@@ -512,7 +640,7 @@ namespace Elwig.Windows {
}
});
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
Mouse.OverrideCursor = null;
}
@@ -534,9 +662,7 @@ namespace Elwig.Windows {
private async void Menu_MemberDataSheet_Email_Click(object sender, RoutedEventArgs evt) {
if (ViewModel.SelectedMember is not Member m) return;
var res = MessageBox.Show("Soll eine E-Mail verschickt werden?", "Stammdatenblatt verschicken",
MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
if (res != MessageBoxResult.Yes)
if (!InteractionService.AskQuestion("Stammdatenblatt verschicken", "Soll eine E-Mail verschickt werden?", true))
return;
await MemberService.GenerateMemberDataSheet(m, ExportMode.Email);
}
@@ -566,9 +692,7 @@ namespace Elwig.Windows {
var year = (int?)((sender as MenuItem)?.Parent as MenuItem)?.Tag;
if (ViewModel.SelectedMember is not Member m || year == null)
return;
var res = MessageBox.Show("Soll eine E-Mail verschickt werden?", "Anlieferungsbestätigung verschicken",
MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
if (res != MessageBoxResult.Yes)
if (!InteractionService.AskQuestion("Anlieferungsbestätigung verschicken", "Soll eine E-Mail verschickt werden?", true))
return;
await MemberService.GenerateDeliveryConfirmation(m, (int)year, ExportMode.Email);
}
@@ -602,9 +726,7 @@ namespace Elwig.Windows {
var avnr = (int?)((sender as MenuItem)?.Parent as MenuItem)?.Tag;
if (ViewModel.SelectedMember is not Member m || year == null || avnr == null)
return;
var res = MessageBox.Show("Soll eine E-Mail verschickt werden?", "Traubengutschrift verschicken",
MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
if (res != MessageBoxResult.Yes)
if (!InteractionService.AskQuestion("Traubengutschrift verschicken", "Soll eine E-Mail verschickt werden?", true))
return;
await MemberService.GenerateCreditNote(m, (int)year, (int)avnr, ExportMode.Email);
}
@@ -726,7 +848,7 @@ namespace Elwig.Windows {
lastVisible = true;
for (int i = 0; i < EmailAddressInputs.Length; i++) {
var input = EmailAddressInputs[i];
var vis = !string.IsNullOrEmpty(input.Address.Text) || (m?.EmailAddresses.Any(a => a.Nr - 1 == i) ?? false);
var vis = !string.IsNullOrWhiteSpace(input.Address.Text) || (m?.EmailAddresses.Any(a => a.Nr - 1 == i) ?? false);
var cVis = vis || (extra && lastVisible);
SetEmailAddressInputVisible(i, cVis, cVis ? num++ : null);
lastVisible = vis;
@@ -734,7 +856,7 @@ namespace Elwig.Windows {
lastVisible = true;
for (int i = 0; i < PhoneNrInputs.Length; i++) {
var input = PhoneNrInputs[i];
var vis = !string.IsNullOrEmpty(input.Number.Text) || (m?.TelephoneNumbers.Any(n => n.Nr - 1 == i) ?? false);
var vis = !string.IsNullOrWhiteSpace(input.Number.Text) || (m?.TelephoneNumbers.Any(n => n.Nr - 1 == i) ?? false);
var cVis = vis || (extra && lastVisible);
SetPhoneNrInputVisible(i, cVis, cVis ? num++ : null);
lastVisible = vis;
@@ -751,6 +873,14 @@ namespace Elwig.Windows {
new protected void ClearInputs(bool validate = false) {
ViewModel.ClearInputs();
if (App.Client.EnableMemberHistory) {
BusinessShares1Input.Text = "";
BusinessShares2Input.Text = "";
BusinessShares3Input.Text = "";
ControlUtils.ClearInputState(BusinessShares1Input);
ControlUtils.ClearInputState(BusinessShares2Input);
ControlUtils.ClearInputState(BusinessShares3Input);
}
base.ClearInputs(validate);
}
@@ -795,9 +925,9 @@ namespace Elwig.Windows {
var oldMember = await ctx.FetchMembers(mgnr).SingleAsync();
var newName = $"{ViewModel.Name?.Replace('ß', 'ẞ').ToUpper()} " +
$"{ViewModel.Prefix}{(!string.IsNullOrEmpty(ViewModel.Prefix) ? " " : "")}" +
$"{ViewModel.GivenName}{(!string.IsNullOrEmpty(ViewModel.GivenName) ? " " : "")}" +
$"{ViewModel.Suffix}{(!string.IsNullOrEmpty(ViewModel.Suffix) ? " " : "")}";
$"{ViewModel.Prefix}{(!string.IsNullOrWhiteSpace(ViewModel.Prefix) ? " " : "")}" +
$"{ViewModel.GivenName}{(!string.IsNullOrWhiteSpace(ViewModel.GivenName) ? " " : "")}" +
$"{ViewModel.Suffix}{(!string.IsNullOrWhiteSpace(ViewModel.Suffix) ? " " : "")}";
var d = new AreaComTransferDialog(oldMember.AdministrativeName, newName, areaComs.Count, areaComs.Sum(c => c.Area));
if (d.ShowDialog() != true)
return;
@@ -0,0 +1,214 @@
<local:AdministrationWindow
x:Class="Elwig.Windows.MemberBusinessSharesAdminWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Elwig.Windows"
xmlns:ctrl="clr-namespace:Elwig.Controls"
xmlns:vm="clr-namespace:Elwig.ViewModels"
Title="{Binding Title}" Height="450" Width="800" MinHeight="400" MinWidth="700">
<Window.DataContext>
<vm:MemberBusinessSharesViewModel/>
</Window.DataContext>
<Window.Resources>
<Style TargetType="Label">
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="Padding" Value="2,4,2,4"/>
<Setter Property="Height" Value="25"/>
</Style>
<Style TargetType="TextBox">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="Height" Value="25"/>
<Setter Property="TextWrapping" Value="NoWrap"/>
</Style>
<Style TargetType="ctrl:UnitTextBox">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="Height" Value="25"/>
<Setter Property="TextWrapping" Value="NoWrap"/>
</Style>
<Style TargetType="ComboBox">
<Setter Property="Height" Value="25"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Top"/>
</Style>
<Style TargetType="Button">
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="9,3"/>
<Setter Property="Height" Value="27"/>
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" MinWidth="300"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="1*" MinWidth="350"/>
</Grid.ColumnDefinitions>
<Grid Margin="5,0,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="42"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListBox x:Name="MemberHistoryList" Margin="5,10,5,0" Grid.ColumnSpan="3"
ItemsSource="{Binding History, Mode=TwoWay}" SelectedItem="{Binding SelectedHistoryEntry, Mode=TwoWay}"
SelectionChanged="MemberHistoryList_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding DateNotice, StringFormat='{}{0:dd.MM.yy}'}" Width="55" TextAlignment="Left" Margin="0,0,0,0"/>
<TextBlock Text="{Binding DateEffective, StringFormat='{}{0:dd.MM.yy}'}" Width="55" TextAlignment="Left" Margin="0,0,0,0"/>
<TextBlock Text="{Binding Shares, StringFormat='{}{0:N0}'}" Width="15" TextAlignment="Right" Margin="0,0,10,0"/>
<TextBlock Text="{Binding PoVReasonPhrase}" TextAlignment="Left" Margin="0,0,0,0"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button x:Name="NewHistoryButton" Content="Neu" Visibility="{Binding ControlButtonsVisibility}"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2"
Click="NewHistoryButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Alt+Einfg</TextBlock>
</Button.ToolTip>
</Button>
<Button x:Name="EditHistoryButton" Content="Bearbeiten" IsEnabled="False" Visibility="{Binding ControlButtonsVisibility}"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2"
Click="EditHistoryButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Strg+B</TextBlock>
</Button.ToolTip>
</Button>
<Button x:Name="DeleteHistoryButton" Content="Löschen" IsEnabled="False" Visibility="{Binding ControlButtonsVisibility}"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2"
Click="DeleteHistoryButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Alt+Entf</TextBlock>
</Button.ToolTip>
</Button>
<Button x:Name="SaveButton" Content="Speichern" IsEnabled="False" Visibility="{Binding EditingButtonsVisibility}"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2"
Click="SaveButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Strg+S</TextBlock>
</Button.ToolTip>
</Button>
<Button x:Name="ResetButton" Content="Zurücksetzen" IsEnabled="False" Visibility="{Binding EditingButtonsVisibility}"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2"
Click="ResetButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Strg+Z</TextBlock>
</Button.ToolTip>
</Button>
<Button x:Name="CancelButton" Content="Abbrechen" IsEnabled="False" Visibility="{Binding EditingButtonsVisibility}" IsCancel="True"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2"
Click="CancelButton_Click">
<Button.ToolTip>
<TextBlock FontWeight="Bold">Esc</TextBlock>
</Button.ToolTip>
</Button>
</Grid>
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<Grid Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="95"/>
</Grid.RowDefinitions>
<GroupBox Header="Geschäftsanteilbewegung" Margin="5,10,5,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="110"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="Grund:" Margin="10,10,0,0" Grid.Column="0"/>
<ComboBox x:Name="ReasonInput" SelectedValue="{Binding Reason, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,10,10,0" Grid.Column="1"
SelectedValuePath="Tag"
SelectionChanged="ReasonInput_SelectionChanged">
<ComboBoxItem Tag="purchase">Kauf</ComboBoxItem>
<ComboBoxItem Tag="transfer_to">Übertragung an...</ComboBoxItem>
<ComboBoxItem Tag="transfer_from">Übertragung von...</ComboBoxItem>
<ComboBoxItem Tag="prescription">Vorschreibung</ComboBoxItem>
<ComboBoxItem Tag="exclusion">Ausschluss</ComboBoxItem>
<ComboBoxItem Tag="deceased">Tod</ComboBoxItem>
<ComboBoxItem Tag="retirement">Kündigung</ComboBoxItem>
<ComboBoxItem Tag="payout">Auszahlung</ComboBoxItem>
</ComboBox>
<Label Content="Anderes Mitgl.:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="OtherMgNrInput" Text="{Binding OtherMgNrString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Width="48" Grid.Column="1" Margin="0,40,0,0" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="OtherMgNrInput_TextChanged" LostFocus="OtherMgNrInput_LostFocus"/>
<ComboBox x:Name="OtherMemberInput"
Grid.Column="1" Margin="53,40,40,10" IsEditable="True"
ItemTemplate="{StaticResource MemberAdminNameTemplate}" TextSearch.TextPath="AdministrativeName"
SelectionChanged="OtherMemberInput_SelectionChanged"/>
<Button x:Name="OtherMemberReferenceButton" Grid.Column="1" Height="25" Width="25" FontFamily="Segoe MDL2 Assets" Content="&#xEE35;" Padding="0,0,0,0"
Margin="10,40,10,10" VerticalAlignment="Top" HorizontalAlignment="Right" ToolTip="Zu Mitglied springen"
Click="OtherMemberReferenceButton_Click"/>
<Label Content="Anmerkung:" Margin="10,70,0,0" Grid.Column="0"/>
<TextBox x:Name="CommentInput" Text="{Binding Comment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,70,10,0" Grid.Column="1"/>
<Label Content="Meldung:" Margin="10,100,0,0" Grid.Column="0"/>
<TextBox x:Name="DateNoticeInput" Text="{Binding DateNoticeString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,100,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="DateNoticeInput_TextChanged" LostFocus="DateInput_LostFocus"/>
<CheckBox x:Name="HasMemberSignedInput" IsChecked="{Binding MemberHasSigned, Mode=TwoWay}" Content="Von Mg. unterfertigt"
Margin="88,105,10,0" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="TOp"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
<Label Content="Wirksam:" Margin="10,130,0,0" Grid.Column="0"/>
<TextBox x:Name="DateEffeciveInput" Text="{Binding DateEffectiveString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,130,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="DateInput_TextChanged" LostFocus="DateInput_LostFocus"/>
<Label Content="Geschäftsanteile:" Margin="10,160,0,0" Grid.Column="0"/>
<TextBox x:Name="SharesInput" Text="{Binding SharesString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,160,10,0" Width="32" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="SharesInput_TextChanged"/>
<CheckBox x:Name="DeductInput" IsChecked="{Binding Deduct, Mode=TwoWay}"
Margin="42,165,10,0" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="TOp"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed">
<TextBlock>
bei Auszahlung <Run Text="{Binding DeductYearString}"/> abziehen
</TextBlock>
</CheckBox>
<Label Content="Betrag:" Margin="10,190,0,10"/>
<ctrl:UnitTextBox x:Name="ValuePerShareInput" Text="{Binding ValuePerShareString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Unit="€/GA" TextChanged="AmountPerShareInput_TextChanged"
Grid.Column="1" Width="76" Margin="0,190,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ctrl:UnitTextBox x:Name="TotalAmountInput" Text="{Binding TotalAmount}" Unit="€" IsEnabled="False"
Grid.Column="1" Width="77" Margin="81,190,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</Grid>
</GroupBox>
<GroupBox Header="Mitglied" Grid.Row="1" Margin="5,5,5,10">
<Grid>
<TextBlock x:Name="MemberInfo" Text="Name (1234)"
FontSize="14" Margin="10,10,10,10"/>
</Grid>
</GroupBox>
</Grid>
</Grid>
</local:AdministrationWindow>
@@ -0,0 +1,347 @@
using Elwig.Helpers;
using Elwig.Models.Entities;
using Elwig.Services;
using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace Elwig.Windows {
public partial class MemberBusinessSharesAdminWindow : AdministrationWindow {
public MemberBusinessSharesViewModel ViewModel => (MemberBusinessSharesViewModel)DataContext;
public MemberBusinessSharesAdminWindow(int mgnr) {
InitializeComponent();
using var ctx = new AppDbContext();
ViewModel.FilterMember = ctx.Members.Find(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
ViewModel.Title = $"Geschäftsanteilbewegungen - {ViewModel.FilterMember.AdministrativeName} - Elwig";
ExemptInputs = [
MemberHistoryList, TotalAmountInput
];
RequiredInputs = [
ReasonInput, DateNoticeInput, DateEffeciveInput, SharesInput
];
}
public void FocusMemberHistory(int histnr) {
if (IsEditing || IsCreating) return;
var item = ViewModel.History.FirstOrDefault(h => h.HistNr == histnr);
if (item != null) {
ControlUtils.SelectItem(MemberHistoryList, item);
}
}
private async Task RefreshList() {
var vm = ViewModel;
var cursor = Mouse.OverrideCursor != null;
if (!cursor) Mouse.OverrideCursor = Cursors.Wait;
var history = await Task.Run(async () => {
using var ctx = new AppDbContext();
return await ctx.MemberHistory
.Where(h => h.FromMgNr == vm.FilterMember.MgNr || h.ToMgNr == vm.FilterMember.MgNr)
.OrderBy(h => h.DateNoticeString).ThenBy(h => h.HistNr)
.ToListAsync();
});
if (!cursor) Mouse.OverrideCursor = null;
history.ForEach(h => h.PoVMgNr = vm.FilterMember.MgNr);
ControlUtils.RenewItemsSource(MemberHistoryList, history,
MemberHistoryList_SelectionChanged, ControlUtils.RenewSourceDefault.None);
RefreshInputs();
}
private void RefreshInputs(bool validate = false) {
ClearInputStates();
if (ViewModel.SelectedHistoryEntry is MemberHistory h) {
EditHistoryButton.IsEnabled = true;
DeleteHistoryButton.IsEnabled = true;
FillInputs(h);
} else {
EditHistoryButton.IsEnabled = false;
DeleteHistoryButton.IsEnabled = false;
ClearOriginalValues();
ClearDefaultValues();
ClearInputs(validate);
ClearInputStates();
}
GC.Collect();
}
private void FillInputs(MemberHistory h) {
ClearOriginalValues();
ClearDefaultValues();
ViewModel.FillInputs(h);
ReasonInput.SelectedValue = h.Reason + (h.Reason == MemberHistory.REASON_TRANSFER ? $"_{(h.FromMgNr == ViewModel.FilterMember.MgNr ? "to" : "from")}" : "");
FinishInputFilling();
}
private async Task InitInputs() {
ClearOriginalValues();
ClearDefaultValues();
await ViewModel.InitInputs();
SetDefaultValue(DateNoticeInput);
if (ViewModel.ValuePerShare != null)
SetDefaultValue(ValuePerShareInput);
ValidateRequiredInputs();
}
new protected void ClearInputs(bool validate = false) {
ViewModel.ClearInputs();
base.ClearInputs(validate);
}
override protected void UpdateButtons() {
if (!IsEditing && !IsCreating) return;
bool ch = HasChanged, v = IsValid;
ResetButton.IsEnabled = ch;
SaveButton.IsEnabled = v && ch;
}
protected override async Task OnRenewContext(AppDbContext ctx) {
await base.OnRenewContext(ctx);
if (await ctx.FetchMembers(ViewModel.FilterMember.MgNr).SingleOrDefaultAsync() is not Member m) {
Close();
return;
}
ViewModel.FilterMember = m;
ViewModel.Title = $"Geschäftsanteilbewegungen - {ViewModel.FilterMember.AdministrativeName} - Elwig";
ControlUtils.RenewItemsSource(OtherMemberInput, await ctx.FetchMembers(includeNotActive: true).ToListAsync());
MemberInfo.Text = $"{m.FullAdministrativeName}\nGA: {m.Shares:N0} (normal) / {m.SharesRed:N0} (rot) / {m.SharesWhite:N0} (weiß) / {m.SharesDormant:N0} (ruhend)";
await RefreshList();
}
private void MemberHistoryList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
RefreshInputs();
}
protected override void ShortcutNew() {
if (!NewHistoryButton.IsEnabled || NewHistoryButton.Visibility != Visibility.Visible)
return;
NewHistoryButton_Click(null, null);
}
private async void NewHistoryButton_Click(object? sender, RoutedEventArgs? evt) {
IsCreating = true;
MemberHistoryList.IsEnabled = false;
ViewModel.SelectedHistoryEntry = null;
HideNewEditDeleteButtons();
ShowSaveResetCancelButtons();
UnlockInputs();
await InitInputs();
DeductInput.IsEnabled = false;
OtherMgNrInput.IsEnabled = false;
OtherMemberInput.IsEnabled = false;
}
protected override void ShortcutEdit() {
if (!EditHistoryButton.IsEnabled || EditHistoryButton.Visibility != Visibility.Visible)
return;
EditHistoryButton_Click(null, null);
}
private void EditHistoryButton_Click(object? sender, RoutedEventArgs? evt) {
if (ViewModel.SelectedHistoryEntry == null) return;
IsEditing = true;
MemberHistoryList.IsEnabled = false;
HideNewEditDeleteButtons();
ShowSaveResetCancelButtons();
UnlockInputs();
DeductInput.IsEnabled = ViewModel.Reason == MemberHistory.REASON_PURCHASE || ViewModel.Reason == MemberHistory.REASON_PRESCRIPTION;
OtherMgNrInput.IsEnabled = ViewModel.Reason?.StartsWith(MemberHistory.REASON_TRANSFER) ?? false;
OtherMemberInput.IsEnabled = OtherMgNrInput.IsEnabled;
}
protected override void ShortcutDelete() {
if (!DeleteHistoryButton.IsEnabled || DeleteHistoryButton.Visibility != Visibility.Visible)
return;
DeleteHistoryButton_Click(null, null);
}
private async void DeleteHistoryButton_Click(object? sender, RoutedEventArgs? evt) {
if (ViewModel.SelectedHistoryEntry is not MemberHistory h)
return;
if (InteractionService.AskContinueYesNo("Geschäftsanteilbewegung löschen", $"Soll die Geschäftsanteilbewegung wirklich unwiderruflich\ngelöscht werden?\n\nFalls ja:\nSoll die Änderung der aktuellen GA wieder rückgängig gemacht werden?") is bool enableTriggers) {
Mouse.OverrideCursor = Cursors.Wait;
try {
await MemberBusinessSharesService.DeleteMemberHistory(h.HistNr, enableTriggers);
App.HintContextChange();
} catch (Exception exc) {
InteractionService.ShowDbException("Geschäftsanteilbewegung löschen", exc);
}
Mouse.OverrideCursor = null;
}
}
protected override void ShortcutSave() {
if (!SaveButton.IsEnabled || SaveButton.Visibility != Visibility.Visible)
return;
SaveButton_Click(null, null);
}
private async void SaveButton_Click(object? sender, RoutedEventArgs? evt) {
Mouse.OverrideCursor = Cursors.Wait;
SaveButton.IsEnabled = false;
int mgnr;
try {
mgnr = await ViewModel.UpdateMemberHistory(ViewModel.SelectedHistoryEntry?.HistNr, IsEditing || ViewModel.DateNotice >= DateOnly.FromDateTime(Utils.Today).AddDays(-14));
App.HintContextChange();
} catch (Exception exc) {
InteractionService.ShowDbException("Geschäftsanteilbewegung aktualisieren", exc);
SaveButton.IsEnabled = true;
Mouse.OverrideCursor = null;
return;
}
IsEditing = false;
IsCreating = false;
MemberHistoryList.IsEnabled = true;
OtherMgNrInput.IsEnabled = true;
HideSaveResetCancelButtons();
ShowNewEditDeleteButtons();
LockInputs();
FinishInputFilling();
await EnsureContextRenewed();
Mouse.OverrideCursor = null;
if (mgnr is int m)
FocusMemberHistory(m);
}
protected override void ShortcutReset() {
if (!ResetButton.IsEnabled || ResetButton.Visibility != Visibility.Visible)
return;
ResetButton_Click(null, null);
}
private async void ResetButton_Click(object? sender, RoutedEventArgs? evt) {
if (IsEditing) {
RefreshInputs();
} else if (IsCreating) {
ClearInputs();
await InitInputs();
}
UpdateButtons();
}
private void CancelButton_Click(object sender, RoutedEventArgs evt) {
IsEditing = false;
IsCreating = false;
MemberHistoryList.IsEnabled = true;
OtherMgNrInput.IsEnabled = true;
HideSaveResetCancelButtons();
ShowNewEditDeleteButtons();
RefreshInputs();
LockInputs();
}
private void ShowSaveResetCancelButtons() {
SaveButton.IsEnabled = false;
ResetButton.IsEnabled = false;
CancelButton.IsEnabled = true;
ViewModel.EditingButtonsVisibility = Visibility.Visible;
}
private void HideSaveResetCancelButtons() {
SaveButton.IsEnabled = false;
ResetButton.IsEnabled = false;
CancelButton.IsEnabled = false;
ViewModel.EditingButtonsVisibility = Visibility.Hidden;
}
private void ShowNewEditDeleteButtons() {
NewHistoryButton.IsEnabled = true;
EditHistoryButton.IsEnabled = ViewModel.SelectedHistoryEntry != null;
DeleteHistoryButton.IsEnabled = ViewModel.SelectedHistoryEntry != null;
ViewModel.ControlButtonsVisibility = Visibility.Visible;
}
private void HideNewEditDeleteButtons() {
NewHistoryButton.IsEnabled = false;
EditHistoryButton.IsEnabled = false;
DeleteHistoryButton.IsEnabled = false;
ViewModel.ControlButtonsVisibility = Visibility.Hidden;
}
private void OtherMgNrInput_TextChanged(object sender, TextChangedEventArgs evt) {
var valid = InputTextChanged((TextBox)sender, Validator.CheckMgNr);
var text = OtherMgNrInput.Text;
var caret = OtherMgNrInput.CaretIndex;
ControlUtils.SelectItemWithPk(OtherMemberInput, valid ? ViewModel.OtherMgNr : null);
OtherMgNrInput.Text = text;
OtherMgNrInput.CaretIndex = caret;
}
private void OtherMgNrInput_LostFocus(object sender, RoutedEventArgs evt) {
var valid = InputLostFocus((TextBox)sender, Validator.CheckMgNr);
ControlUtils.SelectItemWithPk(OtherMemberInput, valid ? ViewModel.OtherMgNr : null);
}
private void OtherMemberInput_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
var m = OtherMemberInput.SelectedItem as Member;
ViewModel.OtherMgNr = m?.MgNr;
}
private void OtherMemberReferenceButton_Click(object sender, RoutedEventArgs evt) {
if (OtherMemberInput.SelectedItem is not Member m) return;
App.FocusMember(m.MgNr);
}
private void ReasonInput_SelectionChanged(object sender, RoutedEventArgs evt) {
ComboBox_SelectionChanged(sender, evt);
if (!IsEditing && !IsCreating) return;
if (ViewModel.DateNotice is not DateOnly d) return;
if (ViewModel.Reason == MemberHistory.REASON_EXCLUSION || ViewModel.Reason == MemberHistory.REASON_DECEASED || ViewModel.Reason == MemberHistory.REASON_RETIREMENT) {
ViewModel.DateEffectiveString = $"31.12.{d.Year + 1}";
} else {
ViewModel.DateEffectiveString = ViewModel.DateNoticeString;
}
DeductInput.IsEnabled = ViewModel.Reason == MemberHistory.REASON_PURCHASE || ViewModel.Reason == MemberHistory.REASON_PRESCRIPTION;
if (!DeductInput.IsEnabled) DeductInput.IsChecked = false;
OtherMgNrInput.IsEnabled = ViewModel.Reason?.StartsWith(MemberHistory.REASON_TRANSFER) ?? false;
OtherMemberInput.IsEnabled = OtherMgNrInput.IsEnabled;
if (!OtherMgNrInput.IsEnabled) OtherMgNrInput.Text = "";
}
private void DateNoticeInput_TextChanged(object sender, TextChangedEventArgs evt) {
DateInput_TextChanged(sender, evt);
if (!IsEditing && !IsCreating) return;
if (ViewModel.DateNotice is not DateOnly d) return;
ViewModel.DeductYear = d.Year;
if (ViewModel.DateEffective == null && ViewModel.Reason != null) {
if (ViewModel.Reason == MemberHistory.REASON_EXCLUSION || ViewModel.Reason == MemberHistory.REASON_DECEASED || ViewModel.Reason == MemberHistory.REASON_RETIREMENT) {
ViewModel.DateEffectiveString = $"31.12.{d.Year + 1}";
} else {
ViewModel.DateEffectiveString = ViewModel.DateNoticeString;
}
}
}
private void UpdateAmount() {
ViewModel.TotalAmount = $"{ViewModel.Shares * ViewModel.ValuePerShare:N2}";
}
private void SharesInput_TextChanged(object sender, TextChangedEventArgs evt) {
IntegerInput_TextChanged(sender, evt);
UpdateAmount();
}
private void AmountPerShareInput_TextChanged(object sender, TextChangedEventArgs evt) {
InputTextChanged((TextBox)sender, Validator.CheckDecimal((TextBox)sender, false, 3, 2));
UpdateAmount();
}
}
}
+5 -10
View File
@@ -1,5 +1,6 @@
using Elwig.Helpers;
using Elwig.Models.Entities;
using Elwig.Services;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
@@ -192,18 +193,14 @@ namespace Elwig.Windows {
App.HintContextChange();
ControlUtils.SelectItemWithPk(WbGlKgs, k.KgNr);
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Katastralgemeinde aktivieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Katastralgemeinde aktivieren", exc);
}
}
private async void DeactivateKgButton_Click(object sender, RoutedEventArgs e) {
if (WbGlKgs.SelectedItem is not AT_Kg k || k.WbKg == null) return;
var r = MessageBox.Show(
$"Sollen alle Riede und Stammgemeinden-Einträge von der KG {k.Name} wirklich unwiderruflich gelöscht werden?",
"Katastralgemeinde deaktivieren", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r != MessageBoxResult.Yes) return;
if (!InteractionService.AskConfirmation("Katastralgemeinde deaktivieren", $"Sollen alle Riede und Stammgemeinden-Einträge von der KG {k.Name} wirklich unwiderruflich gelöscht werden?"))
return;
try {
using (var ctx = new AppDbContext()) {
ctx.Remove(k.WbKg);
@@ -213,9 +210,7 @@ namespace Elwig.Windows {
ControlUtils.SelectItemWithPk(WbKgs, k.KgNr);
} catch (Exception exc) {
await ForceContextReload();
var str = "Der Eintrag konnte nicht aus der Datenbank gelöscht werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Katastralgemeinde deaktivieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Katastralgemeinde deaktivieren", exc);
}
}
+11 -11
View File
@@ -76,7 +76,7 @@
</DataGridTextColumn>
<DataGridTextColumn Header="Nachname" Binding="{Binding Name}" Width="100"/>
<DataGridTextColumn Header="Vorname" Binding="{Binding GivenName}" Width="90"/>
<DataGridTextColumn Header="GA" Binding="{Binding BusinessShares, StringFormat='{}{0:N0} '}" Width="35">
<DataGridTextColumn Header="GA" Binding="{Binding SharesActive, StringFormat='{}{0:N0} '}" Width="35">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
@@ -104,7 +104,7 @@
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Strafe GA" Binding="{Binding PenaltyBs, Converter={StaticResource CurrencyConverter}, StringFormat='{}{0} '}" Width="65">
<DataGridTextColumn Header="Strafe GA" Binding="{Binding PenaltyShares, Converter={StaticResource CurrencyConverter}, StringFormat='{}{0} '}" Width="65">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
@@ -194,7 +194,7 @@
</Grid>
</GroupBox>
<GroupBox Header="Automatische Nachzeichnung der Geschäftsanteile" Margin="5,10,10,10" Height="180" Width="365"
<GroupBox Header="Automatische Nachzeichnung von Geschäftsanteilen" Margin="5,10,10,10" Height="180" Width="365"
VerticalAlignment="Bottom" HorizontalAlignment="Left">
<Grid>
<Grid.ColumnDefinitions>
@@ -204,28 +204,28 @@
<Label Content="Absoluter Freibetrag:" Margin="10,10,0,0" Grid.ColumnSpan="2"/>
<ctrl:UnitTextBox x:Name="AllowanceKgInput" Unit="kg" Margin="128,10,0,0" Width="70"
TextChanged="KgInput_TextChanged" Grid.Column="1"/>
<ctrl:UnitTextBox x:Name="AllowanceBsInput" Unit="GA" Margin="203,10,0,0" Width="60"
<ctrl:UnitTextBox x:Name="AllowanceShareInput" Unit="GA" Margin="203,10,0,0" Width="60"
TextChanged="PercentInput_TextChanged" Grid.Column="1"/>
<Label Content="Relativer Freibetrag:" Margin="10,40,0,0" Grid.ColumnSpan="2"/>
<ctrl:UnitTextBox x:Name="AllowanceKgPerBsInput" Unit="kg/GA" Margin="128,40,0,0" Width="87"
<ctrl:UnitTextBox x:Name="AllowanceKgPerShareInput" Unit="kg/GA" Margin="128,40,0,0" Width="87"
TextChanged="KgInput_TextChanged" Grid.Column="1"/>
<ctrl:UnitTextBox x:Name="AllowancePercentInput" Unit="%" Margin="220,40,0,0" Width="60"
TextChanged="PercentInput_TextChanged" Grid.Column="1"/>
<Label Content="Nur mind. nachz.:" Margin="10,70,0,0" Grid.ColumnSpan="2"/>
<ctrl:UnitTextBox x:Name="MinBsInput" Unit="GA" Margin="128,70,0,0" Width="50"
TextChanged="BsInput_TextChanged" Grid.Column="1"/>
<ctrl:UnitTextBox x:Name="MinSharesInput" Unit="GA" Margin="128,70,0,0" Width="50"
TextChanged="SharesInput_TextChanged" Grid.Column="1"/>
<Button x:Name="SeasonButton" Content="GA-Wert" Margin="0,0,10,42" Width="120"
HorizontalAlignment="Right" VerticalAlignment="Bottom"
Click="SeasonButton_Click" Grid.Column="1"/>
<Button x:Name="AutoAdjustBsButton" Content="Nachzeichnen" Margin="0,0,135,10" Width="120"
<Button x:Name="AutoAdjustSharesButton" Content="Nachzeichnen" Margin="0,0,135,10" Width="120"
HorizontalAlignment="Right" VerticalAlignment="Bottom"
Click="AutoAdjustBsButton_Click" Grid.Column="1"/>
<Button x:Name="UnAdjustBsButton" Content="Rückgängig" Margin="0,0,10,10" Width="120"
Click="AutoAdjustSharesButton_Click" Grid.Column="1"/>
<Button x:Name="UnAdjustSharesButton" Content="Rückgängig" Margin="0,0,10,10" Width="120"
HorizontalAlignment="Right" VerticalAlignment="Bottom"
Click="UnAdjustBsButton_Click" Grid.Column="1"/>
Click="UnAdjustSharesButton_Click" Grid.Column="1"/>
</Grid>
</GroupBox>
</Grid>
+48 -53
View File
@@ -2,6 +2,7 @@ using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Elwig.Models.Dtos;
using Elwig.Models.Entities;
using Elwig.Services;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
@@ -25,18 +26,18 @@ namespace Elwig.Windows {
Year = year;
// using (var ctx = new AppDbContext()) { SeasonLocked = ctx.Seasons.Find(Year + 1) != null; }
Title = $"Auszahlung anpassen - Lese {Year} - Elwig";
AutoAdjustBsButton.IsEnabled = !SeasonLocked;
UnAdjustBsButton.IsEnabled = !SeasonLocked;
AutoAdjustSharesButton.IsEnabled = !SeasonLocked;
UnAdjustSharesButton.IsEnabled = !SeasonLocked;
SaveCustomButton.IsEnabled = !SeasonLocked;
RemoveCustomButton.IsEnabled = !SeasonLocked;
CustomAmountInput.IsEnabled = !SeasonLocked;
CustomCommentInput.IsEnabled = !SeasonLocked;
AllowanceKgInput.Text = $"{App.Client.AutoAdjustBs.AllowanceKg}";
AllowanceBsInput.Text = $"{App.Client.AutoAdjustBs.AllowanceBs}";
AllowanceKgPerBsInput.Text = $"{App.Client.AutoAdjustBs.AllowanceKgPerBs}";
AllowancePercentInput.Text = $"{App.Client.AutoAdjustBs.AllowancePercent}";
MinBsInput.Text = $"{App.Client.AutoAdjustBs.MinBs}";
AllowanceKgInput.Text = $"{App.Client.AutoAdjustShares.AllowanceKg}";
AllowanceShareInput.Text = $"{App.Client.AutoAdjustShares.AllowanceShares}";
AllowanceKgPerShareInput.Text = $"{App.Client.AutoAdjustShares.AllowanceKgPerShare}";
AllowancePercentInput.Text = $"{App.Client.AutoAdjustShares.AllowancePercent}";
MinSharesInput.Text = $"{App.Client.AutoAdjustShares.MinShares}";
}
protected override async Task OnRenewContext(AppDbContext ctx) {
@@ -45,36 +46,36 @@ namespace Elwig.Windows {
m.MgNr,
m.Name,
m.GivenName,
m.BusinessShares,
m.SharesActive,
m.IsActive,
})
.ToListAsync();
var season = await ctx.FetchSeasons(Year).SingleAsync();
var contracts = await ctx.AreaCommitmentTypes.ToDictionaryAsync(t => t.VtrgId, t => t);
var tbl1 = await OverUnderDeliveryData.ForSeason(ctx.OverUnderDeliveryRows, Year);
var (tbl1, _, _) = await OverUnderDeliveryData.ForSeason(ctx.OverUnderDeliveryRows, Year);
var tbl2 = await AreaComUnderDeliveryData.ForSeason(ctx.AreaComUnderDeliveryRows, Year);
var weight = tbl1.Rows.ToDictionary(r => r.MgNr, r => r.Weight);
var weight = tbl1.Rows.ToDictionary(r => r.MgNr, r => r.WeightTotal);
var areaComs = tbl2.Rows.ToDictionary(r => r.MgNr, r => r.VtrgIds.Zip(r.UnderDeliveries).ToDictionary(r => r.First, r => r.Second));
CustomPayments = await ctx.CustomPayments.Where(p => p.Year == Year).ToDictionaryAsync(p => p.MgNr, p => p);
var history = await ctx.MemberHistory
.Where(h => h.DateString.CompareTo($"{Year}-01-01") >= 0 && h.DateString.CompareTo($"{Year}-12-31") <= 0 && h.Type == "auto" && h.BusinessShares > 0)
.GroupBy(h => h.Member)
.ToDictionaryAsync(h => h.Key.MgNr, h => h.Sum(g => g.BusinessShares));
.Where(h => h.DeductYear == Year)
.GroupBy(h => h.ToMember)
.ToDictionaryAsync(h => h.Key.MgNr, h => h.Sum(g => g.Shares));
var list = members
.Select(m => new {
m.MgNr, m.Name, m.GivenName,
m.IsActive,
BusinessShares = m.BusinessShares - history.GetValueOrDefault(m.MgNr, 0),
DeliveryObligation = (m.BusinessShares - history.GetValueOrDefault(m.MgNr, 0)) * season.MinKgPerBusinessShare,
DeliveryRight = (m.BusinessShares - history.GetValueOrDefault(m.MgNr, 0)) * season.MaxKgPerBusinessShare,
SharesActive = m.SharesActive - history.GetValueOrDefault(m.MgNr, 0),
DeliveryObligation = (m.SharesActive - history.GetValueOrDefault(m.MgNr, 0)) * season.MinKgPerShare,
DeliveryRight = (m.SharesActive - history.GetValueOrDefault(m.MgNr, 0)) * season.MaxKgPerShare,
Adjust = history.TryGetValue(m.MgNr, out int v2) ? (int?)v2 : null,
})
.Select(m => new {
m.MgNr, m.Name, m.GivenName,
m.BusinessShares,
m.SharesActive,
Weight = weight.GetValueOrDefault(m.MgNr, 0),
OverUnder = weight.TryGetValue(m.MgNr, out int v1) ?
(v1 < m.DeliveryObligation ? (int?)v1 - m.DeliveryObligation :
@@ -85,12 +86,12 @@ namespace Elwig.Windows {
})
.Select(m => new {
m.MgNr, m.Name, m.GivenName,
m.BusinessShares, m.Weight, m.OverUnder,
PenaltyBs = m.OverUnder != null && m.OverUnder < 0 ?
m.SharesActive, m.Weight, m.OverUnder,
PenaltyShares = m.OverUnder != null && m.OverUnder < 0 ?
(season.PenaltyPerKg * m.OverUnder ?? 0) +
(-season.PenaltyAmount ?? 0) +
(season.PenaltyPerBsAmount * Math.Floor(m.OverUnder / season.MinKgPerBusinessShare ?? 0m) ?? 0) +
(m.Weight == 0 ? (-season.PenaltyNone ?? 0) + (-season.PenaltyPerBsNone * m.BusinessShares ?? 0) : 0)
(season.PenaltyPerShareAmount * Math.Floor(m.OverUnder / season.MinKgPerShare ?? 0m) ?? 0) +
(m.Weight == 0 ? (-season.PenaltyNone ?? 0) + (-season.PenaltyPerShareNone * m.SharesActive ?? 0) : 0)
: (decimal?)null,
PenaltyAc = areaComs.TryGetValue(m.MgNr, out var c) ? c.Select(r => {
var con = contracts[r.Key];
@@ -102,30 +103,30 @@ namespace Elwig.Windows {
})
.Select(m => new {
m.MgNr, m.Name, m.GivenName,
m.BusinessShares, m.Weight, m.OverUnder,
PenaltyBs = m.PenaltyBs == null || m.PenaltyBs == 0 ? (decimal?)null : Math.Round((decimal)m.PenaltyBs, 2),
m.SharesActive, m.Weight, m.OverUnder,
PenaltyShares = m.PenaltyShares == null || m.PenaltyShares == 0 ? (decimal?)null : Math.Round((decimal)m.PenaltyShares, 2),
PenaltyAc = m.PenaltyAc == null ? (decimal?)null : Math.Round((decimal)m.PenaltyAc, 2),
m.Adjust,
AdjustAmount = m.AdjustAmount == null ? (decimal?)null : Math.Round((decimal)m.AdjustAmount, 2),
CustomAmount = m.Custom?.Amount,
ModAbs = m.Custom?.ModAbs,
ModRel = m.Custom?.ModRel,
m.Custom?.ModAbs,
m.Custom?.ModRel,
})
.Select(m => new {
m.MgNr, m.Name, m.GivenName,
m.BusinessShares, m.Weight, m.OverUnder,
m.PenaltyBs, m.PenaltyAc, m.Adjust, m.AdjustAmount, m.CustomAmount, m.ModAbs, m.ModRel,
Total = (m.PenaltyBs ?? 0) + (m.PenaltyAc ?? 0) + (m.AdjustAmount ?? 0) + (m.CustomAmount ?? 0),
m.SharesActive, m.Weight, m.OverUnder,
m.PenaltyShares, m.PenaltyAc, m.Adjust, m.AdjustAmount, m.CustomAmount, m.ModAbs, m.ModRel,
Total = (m.PenaltyShares ?? 0) + (m.PenaltyAc ?? 0) + (m.AdjustAmount ?? 0) + (m.CustomAmount ?? 0),
})
.Select(m => new {
m.MgNr, m.Name, m.GivenName,
m.BusinessShares, m.Weight, m.OverUnder,
m.PenaltyBs, m.PenaltyAc, m.Adjust, m.AdjustAmount, m.CustomAmount, m.ModAbs, m.ModRel,
m.SharesActive, m.Weight, m.OverUnder,
m.PenaltyShares, m.PenaltyAc, m.Adjust, m.AdjustAmount, m.CustomAmount, m.ModAbs, m.ModRel,
m.Total,
Background = m.Weight == 0 ? Brushes.Orange : m.Weight / 2 < -m.Total ? Brushes.Red : Brushes.White,
Foreground = m.Total == 0 ? Brushes.Gray : Brushes.Black,
})
.Where(m => m.OverUnder != null || m.Adjust != null || m.PenaltyBs != null || m.PenaltyAc != null || m.CustomAmount != null || m.ModAbs != null || m.ModRel != null)
.Where(m => m.OverUnder != null || m.Adjust != null || m.PenaltyShares != null || m.PenaltyAc != null || m.CustomAmount != null || m.ModAbs != null || m.ModRel != null)
.OrderByDescending(m => m.OverUnder ?? 0)
.ThenBy(m => m.Name)
.ThenBy(m => m.GivenName)
@@ -135,7 +136,7 @@ namespace Elwig.Windows {
MemberList.ItemsSource = list;
var sym = season.Currency.Symbol ?? season.Currency.Code;
PenaltyBusinessShares.Text = $"{list.Count(r => r.PenaltyBs != null && r.PenaltyBs != 0)} Mg. / {list.Sum(r => r.PenaltyBs):N2} {sym}";
PenaltyBusinessShares.Text = $"{list.Count(r => r.PenaltyShares != null && r.PenaltyShares != 0)} Mg. / {list.Sum(r => r.PenaltyShares):N2} {sym}";
PenaltyAreaCommitments.Text = $"{list.Count(r => r.PenaltyAc != null && r.PenaltyAc != 0)} Mg. / {list.Sum(r => r.PenaltyAc):N2} {sym}";
AutoBusinessShareAdjustment.Text = $"{list.Count(r => r.Adjust > 0)} Mg. / {list.Sum(r => r.Adjust)} GA / {list.Sum(r => r.AdjustAmount):N2} {sym}";
CustomModifiers.Text = $"{list.Count(r => r.CustomAmount != null)} Mg. / {list.Sum(r => r.CustomAmount):N2} {sym}";
@@ -146,36 +147,34 @@ namespace Elwig.Windows {
CustomAmountInput.Unit = sym;
}
private async void AutoAdjustBsButton_Click(object sender, RoutedEventArgs evt) {
private async void AutoAdjustSharesButton_Click(object sender, RoutedEventArgs evt) {
Mouse.OverrideCursor = Cursors.Wait;
try {
int? kg = AllowanceKgInput.Text == "" ? null : int.Parse(AllowanceKgInput.Text);
double? bs = AllowanceBsInput.Text == "" ? null : double.Parse(AllowanceBsInput.Text);
int? kgPerBs = AllowanceKgPerBsInput.Text == "" ? null : int.Parse(AllowanceKgPerBsInput.Text);
double? shares = AllowanceShareInput.Text == "" ? null : double.Parse(AllowanceShareInput.Text);
int? kgPerShare = AllowanceKgPerShareInput.Text == "" ? null : int.Parse(AllowanceKgPerShareInput.Text);
double? percent = AllowancePercentInput.Text == "" ? null : double.Parse(AllowancePercentInput.Text);
int? minBs = MinBsInput.Text == "" ? null : int.Parse(MinBsInput.Text);
int? minShares = MinSharesInput.Text == "" ? null : int.Parse(MinSharesInput.Text);
App.Client.AutoAdjustBs.AllowanceKg = kg;
App.Client.AutoAdjustBs.AllowanceBs = bs;
App.Client.AutoAdjustBs.AllowanceKgPerBs = kgPerBs;
App.Client.AutoAdjustBs.AllowancePercent = percent;
App.Client.AutoAdjustBs.MinBs = minBs;
App.Client.AutoAdjustShares.AllowanceKg = kg;
App.Client.AutoAdjustShares.AllowanceShares = shares;
App.Client.AutoAdjustShares.AllowanceKgPerShare = kgPerShare;
App.Client.AutoAdjustShares.AllowancePercent = percent;
App.Client.AutoAdjustShares.MinShares = minShares;
await Task.Run(async () => {
await App.Client.UpdateValues();
var b = await Billing.Create(Year);
await b.AutoAdjustBusinessShares(new DateOnly(Year, 11, 30), kg ?? default, bs ?? default, kgPerBs ?? default, percent / 100.0 ?? default, minBs ?? default);
await b.AutoAdjustBusinessShares(new DateOnly(Year, 11, 30), kg ?? default, shares ?? default, kgPerShare ?? default, percent / 100.0 ?? default, minShares ?? default);
});
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "GA Nachzeichnen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("GA Nachzeichnen", exc);
}
Mouse.OverrideCursor = null;
}
private async void UnAdjustBsButton_Click(object sender, RoutedEventArgs evt) {
private async void UnAdjustSharesButton_Click(object sender, RoutedEventArgs evt) {
Mouse.OverrideCursor = Cursors.Wait;
try {
await Task.Run(async () => {
@@ -184,9 +183,7 @@ namespace Elwig.Windows {
});
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "GA Nachzeichnen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("GA Nachzeichnen", exc);
}
Mouse.OverrideCursor = null;
}
@@ -199,7 +196,7 @@ namespace Elwig.Windows {
Validator.CheckInteger((TextBox)sender, false, 6);
}
private void BsInput_TextChanged(object sender, TextChangedEventArgs evt) {
private void SharesInput_TextChanged(object sender, TextChangedEventArgs evt) {
Validator.CheckInteger((TextBox)sender, false, 3);
}
@@ -303,9 +300,7 @@ namespace Elwig.Windows {
});
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Benutzerdefinierten Zu-/Abschlag speichern", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Benutzerdefinierten Zu-/Abschlag speichern", exc);
}
Mouse.OverrideCursor = null;
}
+1 -1
View File
@@ -188,7 +188,7 @@
Margin="110,135,10,10" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top"
Checked="ConsiderPenaltyInput_Changed" Unchecked="ConsiderPenaltyInput_Changed"/>
<CheckBox x:Name="ConsiderAutoInput" IsChecked="{Binding ConsiderAuto, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsEnabled="{Binding IsEnabled}"
Content="Automatische Nachzeichnungen der GA"
Content="(Automatische) Nachzeichnungen von GA"
Margin="110,155,10,10" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top"
Checked="ConsiderAutoInput_Changed" Unchecked="ConsiderAutoInput_Changed"/>
<CheckBox x:Name="ConsiderCustomInput" IsChecked="{Binding ConsiderCustom, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsEnabled="{Binding IsEnabled}"
+11 -24
View File
@@ -90,9 +90,7 @@ namespace Elwig.Windows {
App.HintContextChange();
ControlUtils.SelectItem(PaymentVariantList, v);
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Auszahlungsvariante erstellen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Auszahlungsvariante erstellen", exc);
}
Mouse.OverrideCursor = null;
}
@@ -106,9 +104,7 @@ namespace Elwig.Windows {
App.HintContextChange();
ControlUtils.SelectItem(PaymentVariantList, n);
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Auszahlungsvariante kopieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Auszahlungsvariante kopieren", exc);
}
Mouse.OverrideCursor = null;
}
@@ -116,19 +112,14 @@ namespace Elwig.Windows {
private async void DeleteButton_Click(object sender, RoutedEventArgs evt) {
if (PaymentVariantList.SelectedItem is not PaymentVar v || !v.TestVariant)
return;
var res = MessageBox.Show(
$"Soll die Auszahlungsvariante \"{v.Name}\" wirklich unwiderruflich gelöscht werden?",
"Auszahlungsvariante löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (res != MessageBoxResult.OK)
if (!InteractionService.AskContinue("Auszahlungsvariante löschen", $"Soll die Auszahlungsvariante \"{v.Name}\" wirklich unwiderruflich gelöscht werden?"))
return;
Mouse.OverrideCursor = Cursors.Wait;
try {
await PaymentVariantService.DeletePaymentVariant(v.Year, v.AvNr);
App.HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Auszahlungsvariante löschen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Auszahlungsvariante löschen", exc);
}
Mouse.OverrideCursor = null;
}
@@ -142,7 +133,7 @@ namespace Elwig.Windows {
await PaymentVariantService.Calculate(v.Year, v.AvNr);
App.HintContextChange();
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Berechnungsfehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Berechnungsfehler", exc);
}
Mouse.OverrideCursor = null;
ViewModel.CalculateIsEnabled = true;
@@ -204,7 +195,7 @@ namespace Elwig.Windows {
await PaymentVariantService.Commit(v.Year, v.AvNr);
App.HintContextChange();
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
Mouse.OverrideCursor = null;
ViewModel.RevertIsEnabled = true;
@@ -213,11 +204,9 @@ namespace Elwig.Windows {
private async void RevertButton_Click(object sender, RoutedEventArgs evt) {
if (PaymentVariantList.SelectedItem is not PaymentVar v)
return;
var res = MessageBox.Show(
if (!InteractionService.AskConfirmation("Traubengutschriften löschen",
"Sollen wirklich alle festgesetzten Traubengutschriften der ausgewählten Auszahlungsvariante unwiderruflich gelöscht werden?\n\n" +
"Dies ist im Allgemeinen nie empfohlen. Handelt es sich um die aktuellste Auszahlungsvariante könnte das eine Ausnahme sein.",
"Traubengutschriften löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (res != MessageBoxResult.Yes)
"Dies ist im Allgemeinen nie empfohlen. Handelt es sich um die aktuellste Auszahlungsvariante könnte das eine Ausnahme sein."))
return;
ViewModel.RevertIsEnabled = false;
Mouse.OverrideCursor = Cursors.Wait;
@@ -225,7 +214,7 @@ namespace Elwig.Windows {
await PaymentVariantService.Revert(v.Year, v.AvNr);
App.HintContextChange();
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException(exc);
}
Mouse.OverrideCursor = null;
ViewModel.CommitIsEnabled = true;
@@ -235,7 +224,7 @@ namespace Elwig.Windows {
if (PaymentVariantList.SelectedItem is not PaymentVar v) {
return;
} else if (v.TransferDate == null) {
MessageBox.Show("Überweisungsdatum muss gesetzt sein!", "Exportieren nicht möglich", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowError("Exportieren nicht möglich", "Überweisungsdatum muss gesetzt sein!");
return;
}
await PaymentVariantService.GenerateEbics(v.Year, v.AvNr);
@@ -256,9 +245,7 @@ namespace Elwig.Windows {
App.HintContextChange();
} catch (Exception exc) {
await ForceContextReload();
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Auszahlungsvariante aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowDbException("Auszahlungsvariante aktualisieren", exc);
}
Mouse.OverrideCursor = null;
await EnsureContextRenewed();
+7 -12
View File
@@ -1,6 +1,6 @@
using Elwig.Helpers;
using Elwig.Helpers.Export;
using Microsoft.Win32;
using Elwig.Services;
using System;
using System.Collections.Generic;
using System.Data.Common;
@@ -42,9 +42,9 @@ namespace Elwig.Windows {
Mouse.OverrideCursor = Cursors.Wait;
await DisplayQuery(QueryInput.Text);
Mouse.OverrideCursor = null;
} catch (Exception e) {
} catch (Exception exc) {
Mouse.OverrideCursor = null;
MessageBox.Show(e.Message, "Fehler beim Ausführen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Fehler beim Ausführen", exc);
}
}
@@ -85,21 +85,16 @@ namespace Elwig.Windows {
}
private static async Task SaveQuery(string sqlQuery) {
var d = new SaveFileDialog() {
FileName = $"Abfrage.csv",
DefaultExt = "csv",
Filter = "CSV-Datei (*.csv)|*.csv",
Title = $"Datenbank Abfrage speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
var filename = InteractionService.SaveFile("Datenbank Abfrage", "Abfrage", "csv");
if (filename != null) {
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {
var (header, rows) = await ExecuteQuery(sqlQuery);
using var csv = new CsvSimple(d.FileName, ';', Utils.UTF8BOM);
using var csv = new CsvSimple(filename, ';', Utils.UTF8BOM);
await csv.ExportAsync(rows.Prepend([.. header.Select(h => h.ColumnName)]));
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler beim Ausführen", MessageBoxButton.OK, MessageBoxImage.Error);
InteractionService.ShowException("Fehler beim Ausführen", exc);
}
});
Mouse.OverrideCursor = null;
+17
View File
@@ -0,0 +1,17 @@
Elwig
=====
Source code
C:\Program Files\Elwig\src\
https://git.necronda.net/winzer/elwig
Installation folder
C:\Program Files\Elwig\
Data and configuration folder
C:\ProgramData\Elwig\
- config.ini : main configuration file
- database.sqlite3 : stores all data
- imported.txt : list of all imported *.elwig.zip files to not automatically import them again
- mails\ : sent/outgoing email log
+3 -1
View File
@@ -3,7 +3,9 @@
<Fragment>
<!-- C:\Program Files (x86)\Elwig or C:\Program Files\Elwig -->
<StandardDirectory Id="ProgramFiles64Folder">
<Directory Id="InstallFolder" Name="!(bind.Property.ProductName)" />
<Directory Id="InstallFolder" Name="!(bind.Property.ProductName)">
<Directory Id="SourceFolder" Name="src"/>
</Directory>
</StandardDirectory>
<!-- C:\ProgramData\Elwig -->
+6 -1
View File
@@ -1,4 +1,4 @@
<Project Sdk="WixToolset.Sdk/6">
<Project Sdk="WixToolset.Sdk/7">
<PropertyGroup>
<HarvestFileSuppressUniqueIds>false</HarvestFileSuppressUniqueIds>
<HarvestFileGenerateGuidsNow>true</HarvestFileGenerateGuidsNow>
@@ -11,6 +11,7 @@
<BuildProjectReferences>False</BuildProjectReferences>
<OutputName>Elwig</OutputName>
<Cultures>de-AT</Cultures>
<AcceptEula>wix7</AcceptEula>
</PropertyGroup>
<UsingTask TaskName="GetFileVersion" TaskFactory="RoslynCodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
@@ -33,7 +34,11 @@
<DefineConstants>ProductVersion=$(ElwigFileVersion);BuildPath=..\Elwig\bin\Publish;ElwigProjectDir=..\Elwig</DefineConstants>
</PropertyGroup>
</Target>
<Target Name="CreateSourceArchive" BeforeTargets="BeforeBuild">
<Exec Command="git -C .. archive -o $(ProjectDir)\Files\elwig.zip HEAD" />
</Target>
<ItemGroup>
<None Include="Files\config.ini" />
<None Include="Files\README.txt" />
</ItemGroup>
</Project>
+6
View File
@@ -4,9 +4,15 @@
<Component Directory="InstallFolder">
<File Source="$(var.ElwigProjectDir)\bin\Publish\Elwig.exe" Id="Elwig.exe"/>
</Component>
<Component Directory="SourceFolder">
<File Source="$(ProjectDir)\Files\elwig.zip" Id="elwig.zip"/>
</Component>
<Component Directory="ConfigFolder" Permanent="true" NeverOverwrite="true">
<File Source="$(ProjectDir)\Files\config.ini" Id="config.ini"/>
</Component>
<Component Directory="ConfigFolder">
<File Source="$(ProjectDir)\Files\README.txt" Id="README.txt"/>
</Component>
</ComponentGroup>
</Fragment>
</Wix>
+2 -2
View File
@@ -13,7 +13,7 @@ About
**Product:** Elwig
**Description:** Electronic Management for Vintners' Cooperatives
**Type:** ERP system
**Version:** 1.0.4.1 ([Changelog](./CHANGELOG.md))
**Version:** 1.1.0.0 ([Changelog](./CHANGELOG.md))
**License:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
**Website:** https://elwig.at/
**Source code:** https://git.necronda.net/winzer/elwig
@@ -33,7 +33,7 @@ Packaging: [WiX Toolset](https://www.firegiant.com/wixtoolset/)
**Produkt:** Elwig
**Beschreibung:** Elektronische Winzergenossenschaftsverwaltung
**Typ:** Warenwirtschaftssystem (ERP-System)
**Version:** 1.0.4.1 ([Änderungsprotokoll](./CHANGELOG.md))
**Version:** 1.1.0.0 ([Änderungsprotokoll](./CHANGELOG.md))
**Lizenz:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
**Website:** https://elwig.at/
**Quellcode:** https://git.necronda.net/winzer/elwig
+4 -3
View File
@@ -1,8 +1,9 @@
<Project Sdk="WixToolset.Sdk/6">
<Project Sdk="WixToolset.Sdk/7">
<PropertyGroup>
<OutputType>Bundle</OutputType>
<OutputName>Elwig</OutputName>
<Cultures>de-AT</Cultures>
<AcceptEula>wix7</AcceptEula>
</PropertyGroup>
<Target Name="CustomBeforeBuild" BeforeTargets="BeforeBuild">
<Exec Command="curl --fail -s -L &quot;https://go.microsoft.com/fwlink/p/?LinkId=2124703&quot; -z &quot;$(ProjectDir)\Files\MicrosoftEdgeWebview2Setup.exe&quot; -o &quot;$(ProjectDir)\Files\MicrosoftEdgeWebview2Setup.exe&quot;" />
@@ -13,7 +14,7 @@
</Target>
<ItemGroup>
<ProjectReference Include="..\Installer\Installer.wixproj" />
<PackageReference Include="WixToolset.Bal.wixext" Version="6.0.2" />
<PackageReference Include="WixToolset.Util.wixext" Version="6.0.2" />
<PackageReference Include="WixToolset.Bal.wixext" Version="7.0.0" />
<PackageReference Include="WixToolset.Util.wixext" Version="7.0.0" />
</ItemGroup>
</Project>
+3 -3
View File
@@ -32,9 +32,9 @@ INSERT INTO area_commitment (fbnr, revnr, mgnr, vtrgid, cultid, area, gstnr, yea
( 2, 1, 101, 'GV', 'KIP', 10000, '123/5', 2025, 2030),
( 3, 1, 101, 'GV', 'KIP', 10000, '123/6', 2021, 2031);
INSERT INTO season (year, currency, min_kg_per_bs, max_kg_per_bs, penalty_per_kg, penalty_amount, penalty_none, start_date, end_date) VALUES
(2020, 'EUR', 1000, 2000, NULL, NULL, NULL, NULL, NULL),
(2021, 'EUR', 2000, 4000, NULL, NULL, NULL, NULL, NULL);
INSERT INTO season (year, currency, min_kg_per_share, max_kg_per_share, start_date, end_date) VALUES
(2020, 'EUR', 1000, 2000, NULL, NULL),
(2021, 'EUR', 2000, 4000, NULL, NULL);
INSERT INTO modifier (year, modid, ordering, name, abs, rel, active) VALUES
(2020, 'S', 0, 'Geschädigte Trauben', NULL, -0.1, TRUE),
+2 -2
View File
@@ -6,8 +6,8 @@ INSERT INTO wine_cultivation (cultid, name, description) VALUES
INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lower) VALUES
('K', 'Kabinett', TRUE, NULL, FALSE, 0);
INSERT INTO season (year, currency, min_kg_per_bs, max_kg_per_bs, penalty_per_kg, penalty_amount, penalty_none, start_date, end_date) VALUES
(2020, 'EUR', 1000, 2000, NULL, NULL, NULL, NULL, NULL);
INSERT INTO season (year, currency, min_kg_per_share, max_kg_per_share, start_date, end_date) VALUES
(2020, 'EUR', 1000, 2000, NULL, NULL);
INSERT INTO delivery_schedule (year, dsnr, date, zwstid, description, max_weight, ancmt_from, ancmt_to) VALUES
(2020, 1, '2020-10-01', 'X', 'GV Kabinettaktion', 100000, NULL, NULL);
+4 -4
View File
@@ -42,10 +42,10 @@ INSERT INTO area_commitment (fbnr, revnr, mgnr, vtrgid, cultid, area, gstnr, yea
( 4, 1, 204, 'GV', NULL, 10000, '123/3', 2021, 2031);
INSERT INTO season (year, currency, min_kg_per_bs, max_kg_per_bs, penalty_per_kg, penalty_amount, penalty_none, start_date, end_date) VALUES
(2021, 'EUR', 2000, 4000, NULL, NULL, NULL, NULL, NULL),
(2022, 'EUR', 2000, 4000, NULL, NULL, NULL, NULL, NULL),
(2023, 'EUR', 2000, 4000, NULL, NULL, NULL, NULL, NULL);
INSERT INTO season (year, currency, min_kg_per_share, max_kg_per_share, start_date, end_date) VALUES
(2021, 'EUR', 2000, 4000, NULL, NULL),
(2022, 'EUR', 2000, 4000, NULL, NULL),
(2023, 'EUR', 2000, 4000, NULL, NULL);
INSERT INTO modifier (year, modid, ordering, name, abs, rel, active) VALUES
(2021, 'S', 0, 'Geschädigte Trauben', NULL, -0.1, TRUE),
+4 -4
View File
@@ -19,16 +19,16 @@
</Target>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.4.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.7.0" />
<PackageReference Include="Appium.WebDriver" Version="4.4.5" />
<PackageReference Include="NReco.PdfRenderer" Version="1.6.0" />
<PackageReference Include="NUnit" Version="4.5.1" />
<PackageReference Include="NUnit" Version="4.6.1" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.12.0">
<PackageReference Include="NUnit.Analyzers" Version="4.14.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="8.0.1">
<PackageReference Include="coverlet.collector" Version="10.0.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
@@ -8,7 +8,7 @@ namespace Tests.UnitTests.DocumentTests {
public async Task Test_01_VirtualCreditNote() {
using var doc = await CreditNote.Initialize(2020, 1, 101, null);
var text = await Utils.GeneratePdfText(doc);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(text, Contains.Substring("""
MUSTERMANN Max
Winzerstraße 1
@@ -32,7 +32,7 @@ namespace Tests.UnitTests.DocumentTests {
"""));
Assert.That(text, Contains.Substring("Gesamtbetrag: € 1 000,00"));
Assert.That(text, Contains.Substring("Auszahlungsbetrag: € 1 000,00"));
});
}
}
}
}
@@ -14,7 +14,7 @@ namespace Tests.UnitTests.DocumentTests {
using var doc = new DeliveryAncmtList(filter, data);
var text = await Utils.GeneratePdfText(doc, true);
var table = Utils.ExtractTable(text);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(text, Contains.Substring("Anmeldeliste"));
Assert.That(text, Contains.Substring("01.10.2020 Matzen GV Kabinettaktion"));
Assert.That(table, Is.EqualTo(new string[][] {
@@ -24,7 +24,7 @@ namespace Tests.UnitTests.DocumentTests {
["01.10.2020", "104 WINZER Waltraud", "Wolkersdorf", "Grüner Veltliner", "-", "2 000"],
["Gesamt:", "Anmeldungen: 4", "25 000"],
}));
});
}
}
}
}
@@ -13,7 +13,7 @@ namespace Tests.UnitTests.DocumentTests {
var data = await DeliveryConfirmationDeliveryData.ForMember(ctx.DeliveryParts, 2020, m!);
using var doc = new DeliveryConfirmation(2020, m!, null, data);
var text = await Utils.GeneratePdfText(doc);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(text, Contains.Substring("""
MUSTERMANN Max
Winzerstraße 1
@@ -39,7 +39,7 @@ namespace Tests.UnitTests.DocumentTests {
Welschriesling 5 382 - 5 382
12 442 3 129 15 571
"""));
});
}
}
}
}
@@ -13,7 +13,7 @@ namespace Tests.UnitTests.DocumentTests {
using var doc = new DeliveryDepreciationList("Saison 2020", data);
var text = await Utils.GeneratePdfText(doc, true);
var table = Utils.ExtractTable(text);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(text, Contains.Substring("Abwertungsliste"));
Assert.That(text, Contains.Substring("Saison 2020"));
Assert.That(table, Is.EqualTo(new string[][] {
@@ -26,7 +26,7 @@ namespace Tests.UnitTests.DocumentTests {
["20201002X002 1 02.10.2020 09:28 Grüner Veltliner", "Bio", "78", "16,0", "2 901"],
["Gesamt:", "(Teil-)Lieferungen: 3 (5)", "80", "16,3", "13 069"],
}));
});
}
}
}
}
@@ -13,7 +13,7 @@ namespace Tests.UnitTests.DocumentTests {
using var doc = new DeliveryJournal("Saison 2020", data);
var text = await Utils.GeneratePdfText(doc, true);
var table = Utils.ExtractTable(text);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(text, Contains.Substring("Lieferjournal"));
Assert.That(text, Contains.Substring("Saison 2020"));
Assert.That(table, Is.EqualTo(new string[][] {
@@ -42,7 +42,7 @@ namespace Tests.UnitTests.DocumentTests {
["20201003X003", "2", "03.10.2020 15:15", "104 WINZER Waltraud", "Blauer Portugieser", "89", "18,1", "2 313"],
["Gesamt:", "(Teil-)Lieferungen: 12 (23)", "82", "16,6", "58 886"],
}));
});
}
}
}
}
@@ -135,7 +135,7 @@ namespace Tests.UnitTests.DocumentTests {
public async Task Test_05_DeliveryPartsWithModifier() {
using var doc = await DeliveryNote.Initialize(2020, 2);
var text = await Utils.GeneratePdfText(doc);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(text, Contains.Substring("""
W&B Weinbauer GesbR
WEINBAUER Wernhardt
@@ -160,7 +160,7 @@ namespace Tests.UnitTests.DocumentTests {
Waage: ?, ID: ? (gerebelt gewogen)
Gesamt: 87 17,6 4 860
"""));
});
}
}
}
}
@@ -11,14 +11,14 @@ namespace Tests.UnitTests.DocumentTests {
var m = await ctx.FetchMembers(104).SingleAsync();
using var doc = new Letterhead(m!);
var text = await Utils.GeneratePdfText(doc);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(text, Contains.Substring("WG Test | Genossenschaftsstraße 1 | 2120 Wolkersdorf"));
Assert.That(text, Contains.Substring("""
WINZER Waltraud
Wiener Straße 15
2120 Wolkersdorf im Weinviertel
"""));
});
}
}
}
}
@@ -8,7 +8,7 @@ namespace Tests.UnitTests.DocumentTests {
public async Task Test_01_SimpleMember() {
using var doc = await MemberDataSheet.Initialize(104);
var text = await Utils.GeneratePdfText(doc);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(text, Contains.Substring("""
WINZER Waltraud
Wiener Straße 15
@@ -32,7 +32,7 @@ namespace Tests.UnitTests.DocumentTests {
Assert.That(text, Contains.Substring("IBAN: AT97 1234 5678 9012 3460"));
Assert.That(text, Contains.Substring("Betriebs-Nr.: 0123498"));
Assert.That(text, Contains.Substring("Stammgemeinde: Wolkersdorf"));
});
}
}
}
}
@@ -13,7 +13,7 @@ namespace Tests.UnitTests.DocumentTests {
using var doc = new MemberList("Alle Mitglieder", data);
var text = await Utils.GeneratePdfText(doc, true);
var table = Utils.ExtractTable(text);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(text, Contains.Substring("Mitgliederliste"));
Assert.That(text, Contains.Substring("Alle Mitglieder"));
Assert.That(table.Take(3), Is.EqualTo(new string[][] {
@@ -21,7 +21,7 @@ namespace Tests.UnitTests.DocumentTests {
["102 WEINBAUER Wernhardt", "Winzerstraße 2", "2223", "Hohenruppersdorf", "0123471", "0 Hohenruppersdorf"],
[ "W&B Weinbauer GesbR", "Winzerstraße 2", "2223", "Hohenruppersdorf"],
}));
});
}
}
}
}
@@ -9,7 +9,7 @@ namespace Tests.UnitTests.DocumentTests {
using var doc = await PaymentVariantSummary.Initialize(2020, 1);
var text = await Utils.GeneratePdfText(doc, true);
var table = Utils.ExtractTable(text);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(text, Contains.Substring("Auszahlungsvariante"));
Assert.That(text, Contains.Substring(doc.Variant.Name));
Assert.That(table.Skip(19).ToArray(), Is.EqualTo(new string[][] {
@@ -18,7 +18,7 @@ namespace Tests.UnitTests.DocumentTests {
["Grüner Veltliner", "3 219", "0", "0", "1 609,50"],
["Qualitätswein", "73", "3 219", "0,5000", "-", "-", "-", "-", "1 609,50"]
}));
});
}
}
}
}
+7 -5
View File
@@ -4,10 +4,13 @@ using NReco.PdfRenderer;
using System.Text.RegularExpressions;
namespace Tests.UnitTests.DocumentTests {
public static class Utils {
public static partial class Utils {
private static readonly string FileName = Path.Combine(Path.GetTempPath(), "test_document.pdf");
[GeneratedRegex(@"\s{2,}")]
private static partial Regex WideSpaces();
public static async Task<string> GeneratePdfText(Document doc, bool preserveLayout = false) {
using (var ctx = new AppDbContext()) {
await doc.Generate(ctx);
@@ -22,11 +25,10 @@ namespace Tests.UnitTests.DocumentTests {
}
public static string[][] ExtractTable(string text) {
return text.Split('\n')
.Select(row => Regex.Split(row, @"\s{2,}").Select(c => c.Trim()).Where(c => c.Length > 0).ToArray())
return [.. text.Split('\n')
.Select(row => WideSpaces().Split(row).Select(c => c.Trim()).Where(c => c.Length > 0).ToArray())
.Where(row => row.Length >= 3)
.Skip(1)
.ToArray();
.Skip(1)];
}
}
}
@@ -12,7 +12,7 @@ namespace Tests.UnitTests.DocumentTests {
var data = await WineQualityStatisticsData.FromQuery(ctx.Deliveries.Where(d => d.Year == 2020).SelectMany(d => d.Parts));
using var doc = new WineQualityStatistics("Saison 2020", data);
var text = await Utils.GeneratePdfText(doc);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(text, Contains.Substring("Qualitätsstatistik"));
Assert.That(text, Contains.Substring("Saison 2020"));
Assert.That(text, Contains.Substring("""
@@ -44,7 +44,7 @@ namespace Tests.UnitTests.DocumentTests {
"- 0 0 " +
"77 5 " + "11 568 " +
"85 6 " + "17 561"));
});
}
}
}
}
+25 -26
View File
@@ -51,10 +51,10 @@ namespace Tests.UnitTests.HelperTests {
"curves": []
}
""", Vaributes);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
TestCalcOe(data, "GV", 73, 0.5m);
TestCalcOe(data, "WRS", 74, 0.5m);
});
}
}
[Test]
@@ -76,7 +76,7 @@ namespace Tests.UnitTests.HelperTests {
}]
}
""", Vaributes);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
TestCalcOe(data, "GV", 70, 0.25m);
TestCalcOe(data, "GV", 72, 0.25m);
TestCalcOe(data, "GV", 73, 0.50m);
@@ -89,7 +89,7 @@ namespace Tests.UnitTests.HelperTests {
TestCalcOe(data, "GV", 80, 0.95m, geb: true);
TestCalcOe(data, "GV", 83, 1.10m, geb: true);
TestCalcOe(data, "GV", 90, 1.10m, geb: true);
});
}
}
[Test]
@@ -113,7 +113,7 @@ namespace Tests.UnitTests.HelperTests {
}]
}
""", Vaributes);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
TestCalcKmw(data, "GV", 13.00, 0.10m);
TestCalcKmw(data, "GV", 13.50, 0.10m);
TestCalcKmw(data, "GV", 13.99, 0.10m);
@@ -128,7 +128,7 @@ namespace Tests.UnitTests.HelperTests {
TestCalcKmw(data, "GV", 17.50, 1.25m);
TestCalcKmw(data, "GV", 18.00, 1.25m);
TestCalcKmw(data, "GV", 18.50, 1.25m);
});
}
}
[Test]
@@ -147,7 +147,7 @@ namespace Tests.UnitTests.HelperTests {
"curves": []
}
""", Vaributes);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
TestCalcOe(data, "WR", 73, 0.10m);
TestCalcOe(data, "WRS", 73, 0.15m);
TestCalcOe(data, "GV", 73, 0.20m);
@@ -158,7 +158,7 @@ namespace Tests.UnitTests.HelperTests {
TestCalcOe(data, "ZW", 73, 0.25m);
TestCalcOe(data, "ZWS", 73, 0.15m);
TestCalcOe(data, "ZWZ", 73, 0.25m);
});
}
}
[Test]
@@ -175,7 +175,7 @@ namespace Tests.UnitTests.HelperTests {
"curves": []
}
""", Vaributes);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
TestCalcOe(data, "WR", 73, 0.10m);
TestCalcOe(data, "WR-B", 73, 0.20m);
TestCalcOe(data, "WRS", 73, 0.30m);
@@ -191,7 +191,7 @@ namespace Tests.UnitTests.HelperTests {
TestCalcOe(data, "ZW-B", 73, 0.20m);
TestCalcOe(data, "ZWS", 73, 0.30m);
TestCalcOe(data, "ZWZ", 73, 0.10m);
});
}
}
[Test]
@@ -211,7 +211,7 @@ namespace Tests.UnitTests.HelperTests {
"curves": []
}
""", Vaributes);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
TestCalcOe(data, "GV", 75, 0.30m, qualid: "WEI");
TestCalcOe(data, "ZW", 76, 0.25m, qualid: "WEI");
TestCalcOe(data, "GVS", 75, 0.20m, qualid: "WEI");
@@ -222,7 +222,7 @@ namespace Tests.UnitTests.HelperTests {
TestCalcOe(data, "GV", 73, 0.5m);
TestCalcOe(data, "ZWS", 74, 0.5m);
TestCalcOe(data, "GVK", 80, 0.5m);
});
}
}
[Test]
@@ -253,7 +253,7 @@ namespace Tests.UnitTests.HelperTests {
}]
}
""", Vaributes);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
TestCalcKmw(data, "GV", 15.0, 2.0m);
TestCalcKmw(data, "GV", 15.5, 2.272727m);
TestCalcKmw(data, "GV", 16.0, 2.454545m);
@@ -264,7 +264,7 @@ namespace Tests.UnitTests.HelperTests {
TestCalcKmw(data, "ZW", 16.0, 2.50m);
TestCalcKmw(data, "ZW", 16.5, 2.75m);
TestCalcKmw(data, "ZW", 17.0, 3.0m);
});
}
}
[Test]
@@ -313,7 +313,7 @@ namespace Tests.UnitTests.HelperTests {
}]
}
""", Vaributes);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
TestCalcKmw(data, "GV", 15.0, 0.75m);
TestCalcKmw(data, "GVS", 15.0, 0.50m);
TestCalcKmw(data, "GVS", 16.0, 0.55m);
@@ -333,7 +333,7 @@ namespace Tests.UnitTests.HelperTests {
TestCalcKmw(data, "WRS", 15.0, 0.80m, geb: true);
TestCalcKmw(data, "WRS", 16.0, 0.875m, geb: true);
TestCalcKmw(data, "WRS", 17.0, 0.95m, geb: true);
});
}
}
[Test]
@@ -372,12 +372,12 @@ namespace Tests.UnitTests.HelperTests {
}]
}
""", Vaributes);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
TestCalcOe(data, "GVK", 73, 0.032m);
TestCalcOe(data, "ZWS", 74, 0.033m);
TestCalcOe(data, "GV", 75, 0.005m, qualid: "WEI");
TestCalcOe(data, "GVK", 115, 0.065m);
});
}
}
[Test]
@@ -395,17 +395,17 @@ namespace Tests.UnitTests.HelperTests {
"curves": []
}
""", Vaributes);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
TestCalcOe(data, "GV", 73, 0.10m);
TestCalcOe(data, "GVS", 73, 0.20m);
TestCalcOe(data, "GV-B", 73, 0.30m);
TestCalcOe(data, "GVS-B", 73, 0.40m);
TestCalcOe(data, "ZWS-B", 73, 0.20m);
});
}
}
private static List<Varibute> GetSelection(IEnumerable<string> attVars) {
return attVars.Select(s => new Varibute(new RawVaribute(s))).ToList();
return [.. attVars.Select(s => new Varibute(new RawVaribute(s)))];
}
[Test]
@@ -1002,11 +1002,10 @@ namespace Tests.UnitTests.HelperTests {
if (!TestedCurves.Add(str))
return;
var vaributes = curves.SelectMany(v => v).ToList();
List<GraphEntry> entries = curves
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();
}, null), GetSelection(l.Select(v => v.ToString()))))];
var data = BillingData.FromGraphEntries(entries);
var test = PaymentBillingData.FromJson(data.ToJsonString(), vaributes);
for (int i = 0; i < curves.Count; i++) {
@@ -1027,12 +1026,12 @@ namespace Tests.UnitTests.HelperTests {
[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(() => {
using (Assert.EnterMultipleScope()) {
foreach (var config in configurations) {
foreach (var c in GetCurves(config))
TestCollapse(c);
}
});
}
}
}
}
+22 -22
View File
@@ -128,24 +128,24 @@ namespace Tests.UnitTests.HelperTests {
var areaCom = await GetMemberAreaCommitmentBuckets(year, mgnr);
Assert.That(areaCom, Is.Empty);
var delivery = await GetMemberDeliveryBuckets(year, mgnr);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(delivery, Has.Count.EqualTo(3));
Assert.That(delivery["GV"], Is.EqualTo(16_000));
Assert.That(delivery["GV_"], Is.EqualTo( 1_000));
Assert.That(delivery["GVK"], Is.EqualTo( 4_000));
});
}
var b = await BillingVariant.Create(year, 1);
await b.CalculateBuckets(false, false, false);
var payment = await GetMemberPaymentBuckets(year, mgnr);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(payment, Has.Count.EqualTo(1));
Assert.That(payment["GV_"], Is.EqualTo(17_000));
});
}
await b.Calculate();
var prices = await GetMemberDeliveryPrices(year, mgnr);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(prices, Has.Count.EqualTo(6));
// Kabinett
Assert.That(prices[("20201001X001/1", "GV_")], Is.EqualTo((4_000, GV_ungeb)));
@@ -159,7 +159,7 @@ namespace Tests.UnitTests.HelperTests {
Assert.That(prices[("20201001X003/1", "GV_")], Is.EqualTo(( 500, WEI)));
// ohne Attribut
Assert.That(prices[("20201001X003/2", "GV_")], Is.EqualTo(( 500, GV_ungeb)));
});
}
}
[Test]
@@ -167,30 +167,30 @@ namespace Tests.UnitTests.HelperTests {
int mgnr = MgNr1, year = Year2;
var areaCom = await GetMemberAreaCommitmentBuckets(year, mgnr);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(areaCom, Has.Count.EqualTo(1));
Assert.That(areaCom["GV"], Is.EqualTo(new AreaComBucket(10_000, 5_000, 10_000)));
});
}
var delivery = await GetMemberDeliveryBuckets(year, mgnr);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(delivery, Has.Count.EqualTo(3));
Assert.That(delivery["GV"], Is.EqualTo(16_000));
Assert.That(delivery["GV_"], Is.EqualTo( 1_000));
Assert.That(delivery["GVK"], Is.EqualTo( 4_000));
});
}
var b = await BillingVariant.Create(year, 1);
await b.CalculateBuckets(false, false, false, Connection);
var payment = await GetMemberPaymentBuckets(year, mgnr);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(payment, Has.Count.EqualTo(2));
Assert.That(payment["GV_"], Is.EqualTo( 7_000));
Assert.That(payment["GV"], Is.EqualTo(10_000));
});
}
await b.Calculate(true, false, false, false);
var prices = await GetMemberDeliveryPrices(year, mgnr);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(prices, Has.Count.EqualTo(7));
// Kabinett
Assert.That(prices[("20211001X001/1", "GV")] , Is.EqualTo((4_000, GV_geb)));
@@ -205,7 +205,7 @@ namespace Tests.UnitTests.HelperTests {
Assert.That(prices[("20211001X003/1", "GV_")], Is.EqualTo(( 500, WEI)));
// ohne Attribut
Assert.That(prices[("20211001X003/2", "GV_")], Is.EqualTo(( 500, GV_ungeb)));
});
}
}
[Test]
@@ -213,30 +213,30 @@ namespace Tests.UnitTests.HelperTests {
int mgnr = MgNr1, year = Year2;
var areaCom = await GetMemberAreaCommitmentBuckets(year, mgnr);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(areaCom, Has.Count.EqualTo(1));
Assert.That(areaCom["GV"], Is.EqualTo(new AreaComBucket(10_000, 5_000, 10_000)));
});
}
var delivery = await GetMemberDeliveryBuckets(year, mgnr);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(delivery, Has.Count.EqualTo(3));
Assert.That(delivery["GV"], Is.EqualTo(16_000));
Assert.That(delivery["GV_"], Is.EqualTo( 1_000));
Assert.That(delivery["GVK"], Is.EqualTo( 4_000));
});
}
var b = await BillingVariant.Create(year, 1);
await b.CalculateBuckets(true, false, false, Connection);
var payment = await GetMemberPaymentBuckets(year, mgnr);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(payment, Has.Count.EqualTo(2));
Assert.That(payment["GV_"], Is.EqualTo(9_000));
Assert.That(payment["GV"], Is.EqualTo(8_000));
});
}
await b.Calculate(true, true, false, false);
var prices = await GetMemberDeliveryPrices(year, mgnr);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(prices, Has.Count.EqualTo(6));
// Kabinett
Assert.That(prices[("20211001X001/1", "GV")], Is.EqualTo((4_000, GV_geb)));
@@ -250,7 +250,7 @@ namespace Tests.UnitTests.HelperTests {
Assert.That(prices[("20211001X003/1", "GV_")], Is.EqualTo(( 500, WEI)));
// ohne Attribut
Assert.That(prices[("20211001X003/2", "GV_")], Is.EqualTo(( 500, GV_ungeb)));
});
}
}
[Test]
+16 -16
View File
@@ -17,45 +17,45 @@ namespace Tests.UnitTests.HelperTests {
[Test]
public void Test_KmwToOe() {
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
for (int i = 0; i < Gradation.GetLength(0); i++) {
Assert.That(Utils.KmwToOe(Gradation[i, 0]), Is.EqualTo(Gradation[i, 1]));
}
});
}
}
[Test]
public void Test_OeToKmw() {
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
for (int i = 0; i < Gradation.GetLength(0); i++) {
Assert.That(Utils.OeToKmw(Gradation[i, 1]), Is.EqualTo(Gradation[i, 0]));
}
});
}
}
[Test]
public void Test_DecFromDb() {
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(Utils.DecFromDb(10670, 3), Is.EqualTo(10.67M));
Assert.That(Utils.DecFromDb(-1009999, 4), Is.EqualTo(-100.9999M));
Assert.That(Utils.DecFromDb(1, 2), Is.EqualTo(0.01M));
});
}
}
[Test]
public void Test_DecToDb() {
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(Utils.DecToDb(219.48M, 2), Is.EqualTo(21948));
Assert.That(Utils.DecToDb(-1.2345M, 4), Is.EqualTo(-12345));
Assert.That(Utils.DecToDb(99190, 0), Is.EqualTo(99190));
Assert.That(Utils.DecToDb(817.9099M, 3), Is.EqualTo(817910));
Assert.That(Utils.DecToDb(-5618.944M, 2), Is.EqualTo(-561894));
});
}
}
[Test]
public void Test_Modulo() {
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(Utils.Modulo("1", 2), Is.EqualTo(1));
Assert.That(Utils.Modulo("12", 11), Is.EqualTo(1));
Assert.That(Utils.Modulo("65", 16), Is.EqualTo(1));
@@ -66,12 +66,12 @@ namespace Tests.UnitTests.HelperTests {
Assert.Throws<ArgumentException>(() => Utils.Modulo("123", 1));
Assert.Throws<ArgumentException>(() => Utils.Modulo("456", 0));
Assert.Throws<ArgumentException>(() => Utils.Modulo("789", -1));
});
}
}
[Test]
public void Test_SplitAddress() {
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(Utils.SplitAddress("Winzerstraße 1"), Is.EqualTo(("Winzerstraße", "1")));
Assert.That(Utils.SplitAddress("Auf dem Feld 12"), Is.EqualTo(("Auf dem Feld", "12")));
Assert.That(Utils.SplitAddress("Winzerstraße 5a"), Is.EqualTo(("Winzerstraße", "5a")));
@@ -80,12 +80,12 @@ namespace Tests.UnitTests.HelperTests {
Assert.That(Utils.SplitAddress("Winzerstraße 7/2/4/77"), Is.EqualTo(("Winzerstraße", "7/2/4/77")));
Assert.That(Utils.SplitAddress("Winzerstraße 95b"), Is.EqualTo(("Winzerstraße", "95b")));
Assert.That(Utils.SplitAddress("Winzerstraße 1, TOP 3"), Is.EqualTo(("Winzerstraße", "1, TOP 3")));
});
}
}
[Test]
public void Test_SplitName() {
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(Utils.SplitName("Max Bauer", "Bauer"), Is.EqualTo(("Bauer", "Max")));
Assert.That(Utils.SplitName("Bauer Max", "Bauer"), Is.EqualTo(("Bauer", "Max")));
Assert.That(Utils.SplitName("Max und Moritz Bauer", "Bauer"), Is.EqualTo(("Bauer", "Max und Moritz")));
@@ -98,12 +98,12 @@ namespace Tests.UnitTests.HelperTests {
Assert.That(Utils.SplitName("Max und Moritz Bauer und Mustermann", "Bauer"), Is.EqualTo(("Bauer und Mustermann", "Max und Moritz")));
Assert.That(Utils.SplitName("Bauer und Mustermann Max und Moritz", "Bauer"), Is.EqualTo(("Bauer und Mustermann", "Max und Moritz")));
Assert.That(Utils.SplitName("ABC GesbR", "Bauer"), Is.EqualTo(((string, string?))("ABC GesbR", null)));
});
}
}
[Test]
public void Test_CalcCrc16Modbus() {
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(Utils.CalcCrc16Modbus(""), Is.EqualTo(0xFFFF));
Assert.That(Utils.CalcCrc16Modbus("abcd"), Is.EqualTo(0x1D97));
Assert.That(Utils.CalcCrc16Modbus("ABCD"), Is.EqualTo(0x0F85));
@@ -145,7 +145,7 @@ namespace Tests.UnitTests.HelperTests {
Assert.That(Utils.CalcCrc16Modbus("000019.02.2410:50 91 40 0 40kg 001"), Is.EqualTo(60047));
Assert.That(Utils.CalcCrc16Modbus("000019.02.2410:50 101 40 0 40kg 001"), Is.EqualTo(60785));
Assert.That(Utils.CalcCrc16Modbus("000019.02.2410:50 111 45 0 45kg 001"), Is.EqualTo(35918));
});
}
}
}
}
+18 -18
View File
@@ -15,69 +15,69 @@ namespace Tests.UnitTests.HelperTests {
[Test]
public void Test_CheckInteger_Simple() {
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(Validator.CheckInteger(TB(""), true).IsValid, Is.False);
Assert.That(Validator.CheckInteger(TB(""), false).IsValid, Is.True);
Assert.That(Validator.CheckInteger(TB("123"), true).IsValid, Is.True);
Assert.That(Validator.CheckInteger(TB("456"), false).IsValid, Is.True);
Assert.That(Validator.CheckInteger(TB("1234"), false, 4).IsValid, Is.True);
Assert.That(Validator.CheckInteger(TB("4567"), false, 3).IsValid, Is.True);
});
}
}
[Test]
public void Test_CheckInteger_Caret() {
var tb = TB("1a2b3c", 2);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(Validator.CheckInteger(tb, true).IsValid, Is.True);
Assert.That(tb.Text, Is.EqualTo("123"));
Assert.That(tb.CaretIndex, Is.EqualTo(1));
});
}
}
[Test]
public void Test_CheckInteger_MaxLen() {
var tb = TB("1a2b3c4d5e", 4);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(Validator.CheckInteger(tb, true, 3).IsValid, Is.True);
Assert.That(tb.Text, Is.EqualTo("123"));
Assert.That(tb.CaretIndex, Is.EqualTo(2));
});
}
}
[Test]
public void Test_CheckPhoneNumber_Simple() {
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(Validator.CheckPhoneNumber(TB(""), true).IsValid, Is.False);
Assert.That(Validator.CheckPhoneNumber(TB("+43"), false).IsValid, Is.False);
Assert.That(Validator.CheckPhoneNumber(TB("066412345678"), true).IsValid, Is.True);
Assert.That(Validator.CheckPhoneNumber(TB("0ab66412cd345678"), true).IsValid, Is.True);
});
}
}
[Test]
public void Test_CheckPhoneNumber_Format_1() {
var tb = TB("066412345678", 5);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(Validator.CheckPhoneNumber(tb, true).IsValid, Is.True);
Assert.That(tb.Text, Is.EqualTo("+43 664 12345678"));
Assert.That(tb.CaretIndex, Is.EqualTo(9));
});
}
}
[Test]
public void Test_CheckPhoneNumber_Format_2() {
var tb = TB("0a2574b1c2d34..", 7);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(Validator.CheckPhoneNumber(tb, true).IsValid, Is.True);
Assert.That(tb.Text, Is.EqualTo("+43 2574 1234"));
Assert.That(tb.CaretIndex, Is.EqualTo(8));
});
}
}
[Test]
public void Test_CheckEmailAddress_Simple() {
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(Validator.CheckEmailAddress(TB(""), true).IsValid, Is.False);
Assert.That(Validator.CheckEmailAddress(TB("name"), false).IsValid, Is.False);
Assert.That(Validator.CheckEmailAddress(TB("@"), false).IsValid, Is.False);
@@ -87,27 +87,27 @@ namespace Tests.UnitTests.HelperTests {
Assert.That(Validator.CheckEmailAddress(TB("name@a.com.a"), true).IsValid, Is.False);
Assert.That(Validator.CheckEmailAddress(TB("name@a.com"), true).IsValid, Is.True);
Assert.That(Validator.CheckEmailAddress(TB("my.name@hello#.com"), true).IsValid, Is.True);
});
}
}
[Test]
public void Test_CheckEmailAddress_Format_1() {
var tb = TB("my . name . is @heinz#.com", 17);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(Validator.CheckEmailAddress(tb, true).IsValid, Is.True);
Assert.That(tb.Text, Is.EqualTo("my.name.is@heinz.com"));
Assert.That(tb.CaretIndex, Is.EqualTo(12));
});
}
}
[Test]
public void Test_CheckEmailAddress_Format_2() {
var tb = TB("sabine.müsterfrau@heinz#.com.b", 30);
Assert.Multiple(() => {
using (Assert.EnterMultipleScope()) {
Assert.That(Validator.CheckEmailAddress(tb, true).IsValid, Is.False);
Assert.That(tb.Text, Is.EqualTo("sabine.müsterfrau@heinz.com.b"));
Assert.That(tb.CaretIndex, Is.EqualTo(29));
});
}
}
}
}

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