Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e9e90b1e98 | |||
| 640dbf705e | |||
| 42121fe7da | |||
| d45c3f867f | |||
| a90be2644d | |||
| 01739ba42e | |||
| 7bea4d9ee0 | |||
| b6497fd422 | |||
| f141485537 | |||
| b31603554a | |||
| 90def81cc5 | |||
| 87903227b5 | |||
| 6d6776f0f9 | |||
| bf3cc2ea1e | |||
| c1697dc4f3 | |||
| 3b335a568e | |||
| beacdab54f | |||
| da05a49e10 | |||
| 5cec5b3556 | |||
| 9af498287d | |||
| 452f246f24 | |||
| e97c29db43 | |||
| 3419113dec | |||
| bf6297f63b | |||
| f228ba3019 | |||
| 495aa8a691 | |||
| 811916a8b9 | |||
| 3b6333a6a2 | |||
| 9b37330362 | |||
| 889a17b21c | |||
| ac6d559e5d | |||
| 6d80cca241 | |||
| 9dc225d3e4 | |||
| 0d513f7bff | |||
| b10c744bf9 | |||
| e6367da286 | |||
| 01f4480a08 | |||
| e9722c790c | |||
| af98c32026 | |||
| 7300b30cf5 | |||
| 428cd6ddc2 | |||
| 2de8af878b |
86
CHANGELOG.md
86
CHANGELOG.md
@@ -2,6 +2,92 @@
|
|||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
[v1.0.3.0][v1.0.3.0] (2026-01-16) {#v1.0.3.0}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Neue Funktionen {#v1.0.3.0-features}
|
||||||
|
|
||||||
|
* Bei Zu-/Abschlägen ist es nun möglich den konkreten Wert dieser auf Lieferscheinen nicht anzuzeigen (Stammdaten -> Saisons). (495aa8a691)
|
||||||
|
* Bei Teillieferungen kann nun die Art der Anlieferung angegeben werden: Planenwagen/Kipper, Lesewagen, Kiste(n). (bf6297f63b, 3419113dec, 6d6776f0f9)
|
||||||
|
* Im Lieferungen-Fenster (`DeliveryAdminWindow`) können nun die Gesamtmengen der gefilterten Lieferungen nach Mitglied und Sorte als Excel-Liste exportiert werden ("Liefermengen"). ([#73][i73], e97c29db43)
|
||||||
|
* Im Datenbankabfragen-Fenster (`QueryWindow`) gibt es die Möglichkeit die Ergebnisse als CSV-Datei zu exportieren. (9af498287d, 5cec5b3556)
|
||||||
|
* PDF-Dokumente wurden überarbeitet:
|
||||||
|
* Traubengutschriften können nur noch ausgedruckt bzw. verschickt werden, wenn die zugehörige Auszahlungsvariante festgesetzt ist. (b31603554a)
|
||||||
|
* Auf Dokumenten für interne Verwendung (alle außer an Mitglieder adressierte) wurden Kopf- und Fußzeile auf ein Minimum beschränkt. (f141485537)
|
||||||
|
* Das Synchronisieren zwischen Zweigstellen ist für den Benutzer nun wesentlich vereinfacht. Geänderte Daten von Mitgliedern, Flächenbindungen oder Lieferungen werden beim Synchronisieren automatsich hochgeladen bzw. heruntergeladen. ([#66][i66], 3b335a568e)
|
||||||
|
* Im Rundschreiben-Fenster (`MailWindow`) wurde ein Knopf zum Abbrechen des Generierens hinzugefügt. ([#50][i50])
|
||||||
|
|
||||||
|
### Behobene Fehler {#v1.0.3.0-bugfixes}
|
||||||
|
|
||||||
|
* Einzelne (nicht über das Rundschreiben-Fenster) verschickte E-Mails werde korrekt im Ausgangsprotokoll angeführt. (ac6d559e5d)
|
||||||
|
* Bei automatischen Datenbank-Updates sind auftretende Fehler ignoriert worden. (f228ba3019)
|
||||||
|
* Beim Aufteilen von Lieferungen sind Zu-/Abschläge nicht auf neu erstellen Teillieferungen übernommen worden. (da05a49e10)
|
||||||
|
* Das Verbinden und Trennen von Waagen mittels Serial-/COM-Port ist nun bei laufendem Programm möglich. ([#71][i71])
|
||||||
|
* In den Auszahlungsvarianten-Daten (`PaymentVariantSummary`) ist die statistische Summe der gebundenen und ungebundenen Menge ohne die Spalte _attributlos gebunden_ berechnet worden. (90def81cc5)
|
||||||
|
* Seit [v0.13.7](#v0.13.7) (2025-01-21) wurde auch bei der ersten Auszahlung der Saison ein "Bisher berücksichtigt: € 0,00" auf den Traubengutschriften angeführt. (7bea4d9ee0)
|
||||||
|
* Das Speichern der Parameter `MAIL_SEND_POSTAL` und `MAIL_SEND_EMAIL` war fehlerhaft. (01739ba42e)
|
||||||
|
* Bessere Isolation der automatisierten Dokumenten-Tests. (640dbf705e)
|
||||||
|
|
||||||
|
### Sonstiges {#v1.0.3.0-misc}
|
||||||
|
|
||||||
|
* Im Lieferungen-Fenster (`DeliveryAdminWindow`) wird bei Zu-/Abschlägen nur "Alle" angezeigt, wenn mehr als 1 ausgewählt sind. (811916a8b9)
|
||||||
|
* Im Auszahlungsvarianten-Fenster (`PaymentVariantsWindow`) wird die letzte Variante standardmäßig ausgewählt und beim Löschen wird um Bestätigung gebeten. (b6497fd422)
|
||||||
|
* Abhängigkeiten aktualisiert. (889a17b21c, 9b37330362, 3b6333a6a2, d45c3f867f, 42121fe7da)
|
||||||
|
|
||||||
|
[v1.0.3.0]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.3.0
|
||||||
|
[i50]: https://git.necronda.net/winzer/elwig/issues/50
|
||||||
|
[i66]: https://git.necronda.net/winzer/elwig/issues/60
|
||||||
|
[i71]: https://git.necronda.net/winzer/elwig/issues/71
|
||||||
|
[i73]: https://git.necronda.net/winzer/elwig/issues/73
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v1.0.2.0][v1.0.2.0] (2025-11-10) {#v1.0.2.0}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Neue Funktionen {#v1.0.2.0-features}
|
||||||
|
|
||||||
|
* Im Mitglieder-Fenster (`MemberAdminWindow`) können Kontaktdaten der Mitglieder als .vcf-Datei exportiert werden. (01f4480a08, 9dc225d3e4)
|
||||||
|
|
||||||
|
### Sonstiges {#v1.0.2.0-misc}
|
||||||
|
|
||||||
|
* Wenn ein Serial-/COM-Port-USB-Adapter an- oder abgesteckt wird, wird das nun automatisch erkannt. (e6367da286)
|
||||||
|
* Abhängigkeiten aktualisiert. (b10c744bf9, 0d513f7bff)
|
||||||
|
|
||||||
|
[v1.0.2.0]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.2.0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v1.0.1.5][v1.0.1.5] (2025-10-29) {#v1.0.1.5}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Behobene Fehler {#v1.0.1.5-bugfixes}
|
||||||
|
|
||||||
|
* Im Rundschreiben-Fenster (`MailWindow`) kam es zu einem Absturz, wenn man das Fenster über den "Anlieferungsbestätigung"-Knopf im Leseabschluss-Abschnitt geöffnet hat. (af98c32026)
|
||||||
|
|
||||||
|
[v1.0.1.5]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.1.5
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v1.0.1.4][v1.0.1.4] (2025-10-28) {#v1.0.1.4}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Behobene Fehler {#v1.0.1.4-bugfixes}
|
||||||
|
|
||||||
|
* Im Rundschreiben-Fenster (`MailWindow`) kam es zu einem Absturz, wenn man die Zustelloptionen "Post zusenden an Mitglieder, die keine E-Mail erhalten würden" und "E-Mail zusenden an niemanden" kombiniert hat. (2de8af878b)
|
||||||
|
|
||||||
|
### Sonstiges {#v1.0.1.4-misc}
|
||||||
|
|
||||||
|
* Im Auszahlungsvariante-Fenster (`ChartWindow`) gibt es keine Fehlermeldung mehr wenn nicht für alle Sorten ein Preis definiert ist, nur noch eine Warnung. (428cd6ddc2)
|
||||||
|
|
||||||
|
[v1.0.1.4]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.1.4
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[v1.0.1.3][v1.0.1.3] (2025-10-13) {#v1.0.1.3}
|
[v1.0.1.3][v1.0.1.3] (2025-10-13) {#v1.0.1.3}
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,12 @@
|
|||||||
<TextBlock Text="{Binding ValueStr}"/>
|
<TextBlock Text="{Binding ValueStr}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
<DataTemplate x:Key="PublicModifierTemplate">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Text="{Binding Name}" MinWidth="250" Margin="0,0,10,0"/>
|
||||||
|
<TextBlock Text="{Binding PublicValueStr}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
<DataTemplate x:Key="WineAttributeTemplate">
|
<DataTemplate x:Key="WineAttributeTemplate">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ namespace Elwig {
|
|||||||
public static bool ForceShutdown { get; private set; } = false;
|
public static bool ForceShutdown { get; private set; } = false;
|
||||||
|
|
||||||
private readonly DispatcherTimer _autoUpdateTimer = new() { Interval = TimeSpan.FromHours(1) };
|
private readonly DispatcherTimer _autoUpdateTimer = new() { Interval = TimeSpan.FromHours(1) };
|
||||||
|
public readonly SerialPortWatcher SerialPortWatcher = new();
|
||||||
|
|
||||||
public static readonly string DataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Elwig");
|
public static readonly string DataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Elwig");
|
||||||
public static readonly string MailsPath = Path.Combine(DataPath, "mails");
|
public static readonly string MailsPath = Path.Combine(DataPath, "mails");
|
||||||
@@ -48,9 +49,10 @@ namespace Elwig {
|
|||||||
public static string? BranchPhoneNr { get; private set; }
|
public static string? BranchPhoneNr { get; private set; }
|
||||||
public static string? BranchFaxNr { get; private set; }
|
public static string? BranchFaxNr { get; private set; }
|
||||||
public static string? BranchMobileNr { get; private set; }
|
public static string? BranchMobileNr { get; private set; }
|
||||||
public static IList<IScale> Scales { get; private set; }
|
|
||||||
public static IList<ICommandScale> CommandScales => Scales.Where(s => s is ICommandScale).Cast<ICommandScale>().ToList();
|
public static IList<IScale> Scales { get; private set; } = [];
|
||||||
public static IList<IEventScale> EventScales => Scales.Where(s => s is IEventScale).Cast<IEventScale>().ToList();
|
public static IList<ICommandScale> CommandScales => [.. Scales.Where(s => s is ICommandScale).Cast<ICommandScale>()];
|
||||||
|
public static IList<IEventScale> EventScales => [.. Scales.Where(s => s is IEventScale).Cast<IEventScale>()];
|
||||||
public static ClientParameters Client { get; set; }
|
public static ClientParameters Client { get; set; }
|
||||||
|
|
||||||
public static Dispatcher MainDispatcher { get; private set; }
|
public static Dispatcher MainDispatcher { get; private set; }
|
||||||
@@ -137,6 +139,9 @@ namespace Elwig {
|
|||||||
_autoUpdateTimer.Start();
|
_autoUpdateTimer.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SerialPortWatcher.SerialPortConnected += OnSerialPortConnected;
|
||||||
|
SerialPortWatcher.SerialPortDisconnected += OnSerialPortDisconnected;
|
||||||
|
|
||||||
var list = new List<IScale>();
|
var list = new List<IScale>();
|
||||||
foreach (var s in Config.Scales) {
|
foreach (var s in Config.Scales) {
|
||||||
try {
|
try {
|
||||||
@@ -144,7 +149,7 @@ namespace Elwig {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
list.Add(new InvalidScale(s.Id));
|
list.Add(new InvalidScale(s.Id));
|
||||||
if (s.Required)
|
if (s.Required)
|
||||||
MessageBox.Show($"Unable to create scale {s.Id}:\n\n{e.Message}", "Scale Error",
|
MessageBox.Show($"Verbindung zu Waage {s.Id} konnte nicht hergestellt werden:\n\n{e.Message}", "Waagenfehler",
|
||||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152,7 +157,7 @@ namespace Elwig {
|
|||||||
|
|
||||||
if (Config.Branch != null) {
|
if (Config.Branch != null) {
|
||||||
if (!branches.ContainsKey(Config.Branch.ToLower())) {
|
if (!branches.ContainsKey(Config.Branch.ToLower())) {
|
||||||
MessageBox.Show("Invalid branch name in config!", "Invalid Branch Config", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show("Ungültige Zweigstelle in Konfigurationsdatei!", "Ungültige Zweigstelle", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
Shutdown();
|
Shutdown();
|
||||||
} else {
|
} else {
|
||||||
SetBranch(branches[Config.Branch.ToLower()]);
|
SetBranch(branches[Config.Branch.ToLower()]);
|
||||||
@@ -160,7 +165,7 @@ namespace Elwig {
|
|||||||
} else if (branches.Count == 1) {
|
} else if (branches.Count == 1) {
|
||||||
SetBranch(branches.First().Value);
|
SetBranch(branches.First().Value);
|
||||||
} else {
|
} else {
|
||||||
MessageBox.Show("Unable to determine local branch!", "Invalid Branch Config", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show("Erkennen der lokalen Zweigstelle nicht möglich!", "Ungültige Zweigstelle", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
Shutdown();
|
Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,6 +186,7 @@ namespace Elwig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async void Application_Exit(object sender, ExitEventArgs evt) {
|
private async void Application_Exit(object sender, ExitEventArgs evt) {
|
||||||
|
SerialPortWatcher.Dispose();
|
||||||
foreach (var s in EventScales) {
|
foreach (var s in EventScales) {
|
||||||
s.Dispose();
|
s.Dispose();
|
||||||
}
|
}
|
||||||
@@ -240,6 +246,53 @@ namespace Elwig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnSerialPortConnected(object? sender, string name) {
|
||||||
|
for (var i = 0; i < Config.Scales.Count; i++) {
|
||||||
|
var s = Config.Scales[i];
|
||||||
|
if (s.Connection?.StartsWith($"serial://{name}:") ?? false) {
|
||||||
|
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) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
} else if (Scales[i] is IEventScale) {
|
||||||
|
MessageBox.Show($"Verbindung zu Waage {s.Id} wieder hergestellt!", $"Waage {s.Id}", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UpdateScales();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSerialPortDisconnected(object? sender, string name) {
|
||||||
|
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);
|
||||||
|
if (Scales[i] is ICommandScale) {
|
||||||
|
try {
|
||||||
|
Scales[i].Dispose();
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
Scales[i] = new InvalidScale(s.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UpdateScales();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void UpdateScales() {
|
||||||
|
foreach (Window w in CurrentApp.Windows) {
|
||||||
|
if (w is DeliveryAdminWindow t && t.ViewModel.IsReceipt) {
|
||||||
|
t.UpdateScales();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static async Task CheckForUpdates(bool showAlert = false) {
|
public static async Task CheckForUpdates(bool showAlert = false) {
|
||||||
if (Config.UpdateUrl == null) return;
|
if (Config.UpdateUrl == null) return;
|
||||||
var latest = await Utils.GetLatestInstallerUrl(Config.UpdateUrl);
|
var latest = await Utils.GetLatestInstallerUrl(Config.UpdateUrl);
|
||||||
|
|||||||
@@ -124,19 +124,24 @@ namespace Elwig.Controls {
|
|||||||
SelectItemsReverse();
|
SelectItemsReverse();
|
||||||
var dmp = !string.IsNullOrEmpty(ListDisplayMemberPath) ? ListDisplayMemberPath : !string.IsNullOrEmpty(DisplayMemberPath) ? DisplayMemberPath : null;
|
var dmp = !string.IsNullOrEmpty(ListDisplayMemberPath) ? ListDisplayMemberPath : !string.IsNullOrEmpty(DisplayMemberPath) ? DisplayMemberPath : null;
|
||||||
if (SelectedItems.Count == ItemsSource.Cast<object>().Count() && AllItemsSelectedContent != null) {
|
if (SelectedItems.Count == ItemsSource.Cast<object>().Count() && AllItemsSelectedContent != null) {
|
||||||
_textBox.Text = AllItemsSelectedContent;
|
|
||||||
AllItemsSelected = true;
|
AllItemsSelected = true;
|
||||||
} else if (SelectedItems.Count == 0) {
|
} else if (SelectedItems.Count == 0) {
|
||||||
_textBox.Text = "";
|
|
||||||
AllItemsSelected = false;
|
AllItemsSelected = false;
|
||||||
|
} else {
|
||||||
|
AllItemsSelected = null;
|
||||||
|
}
|
||||||
|
if (SelectedItems.Count > 1 && SelectedItems.Count == ItemsSource.Cast<object>().Count() && AllItemsSelectedContent != null) {
|
||||||
|
_textBox.Text = AllItemsSelectedContent;
|
||||||
|
} else if (SelectedItems.Count == 0) {
|
||||||
|
_textBox.Text = "";
|
||||||
} else {
|
} else {
|
||||||
_textBox.Text = string.Join(Delimiter,
|
_textBox.Text = string.Join(Delimiter,
|
||||||
dmp == null ? SelectedItems.Cast<object>() :
|
dmp == null ? SelectedItems.Cast<object>() :
|
||||||
SelectedItems.Cast<object>()
|
SelectedItems.Cast<object>()
|
||||||
.Select(i => i.GetType().GetProperty(dmp)?.GetValue(i))
|
.Select(i => i.GetType().GetProperty(dmp)?.GetValue(i))
|
||||||
);
|
);
|
||||||
AllItemsSelected = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RaiseEvent(new SelectionChangedEventArgs(SelectionChangedEvent, evt.RemovedItems, evt.AddedItems));
|
RaiseEvent(new SelectionChangedEventArgs(SelectionChangedEvent, evt.RemovedItems, evt.AddedItems));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,16 @@ namespace Elwig.Documents {
|
|||||||
Member = m;
|
Member = m;
|
||||||
Location = App.BranchLocation;
|
Location = App.BranchLocation;
|
||||||
IncludeSender = includeSender;
|
IncludeSender = includeSender;
|
||||||
|
var c = App.Client;
|
||||||
|
Header = $"<div class='name'>{c.Name}</div><div class='suffix'>{c.NameSuffix}</div><div class='type'>{c.NameTypeFull}</div>";
|
||||||
|
Footer = Utils.GenerateFooter("<br/>", " \u00b7 ")
|
||||||
|
.Item(c.NameFull).NextLine()
|
||||||
|
.Item(c.Address).Item($"{c.Plz} {c.Ort}").Item("Österreich").Item("Tel.", c.PhoneNr).Item("Fax", c.FaxNr).NextLine()
|
||||||
|
.Item(c.EmailAddress != null ? $"<a href=\"mailto:{c.Name} {c.NameSuffix} <{c.EmailAddress}>\">{c.EmailAddress}</a>" : null)
|
||||||
|
.Item(c.Website != null ? $"<a href=\"http://{c.Website}/\">{c.Website}</a>" : null)
|
||||||
|
.Item("Betriebs-Nr.", c.LfbisNr).Item("Bio-KSt.", c.OrganicAuthority).NextLine()
|
||||||
|
.Item("UID", c.UstIdNr).Item("BIC", c.Bic).Item("IBAN", c.Iban)
|
||||||
|
.ToString();
|
||||||
var uid = (m.UstIdNr ?? "-") + (m.IsBuchführend ? "" : " <i>(pauschaliert)</i>");
|
var uid = (m.UstIdNr ?? "-") + (m.IsBuchführend ? "" : " <i>(pauschaliert)</i>");
|
||||||
Aside = $"<table><colgroup><col span='1' style='width: 22.5mm;'/><col span='1' style='width: 42.5mm;'/></colgroup>" +
|
Aside = $"<table><colgroup><col span='1' style='width: 22.5mm;'/><col span='1' style='width: 42.5mm;'/></colgroup>" +
|
||||||
$"<thead><tr><th colspan='2'>Mitglied</th></tr></thead><tbody>" +
|
$"<thead><tr><th colspan='2'>Mitglied</th></tr></thead><tbody>" +
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ namespace Elwig.Documents {
|
|||||||
Data = data;
|
Data = data;
|
||||||
Payment = p;
|
Payment = p;
|
||||||
Credit = p.Credit;
|
Credit = p.Credit;
|
||||||
|
IsPreview = Payment == null || Credit == null;
|
||||||
var season = p.Variant.Season;
|
var season = p.Variant.Season;
|
||||||
if (considerCustomModifiers) {
|
if (considerCustomModifiers) {
|
||||||
CustomPayment = ctx.CustomPayments.Find(p.Year, p.MgNr);
|
CustomPayment = ctx.CustomPayments.Find(p.Year, p.MgNr);
|
||||||
@@ -48,7 +49,7 @@ namespace Elwig.Documents {
|
|||||||
if (CustomPayment?.ModComment != null) {
|
if (CustomPayment?.ModComment != null) {
|
||||||
MemberModifier = CustomPayment.ModComment;
|
MemberModifier = CustomPayment.ModComment;
|
||||||
} else if (mod != null) {
|
} else if (mod != null) {
|
||||||
MemberModifier = $"{mod.Name} ({mod.ValueStr})";
|
MemberModifier = $"{mod.Name} ({mod.PublicValueStr})";
|
||||||
} else {
|
} else {
|
||||||
MemberModifier = "Sonstige Zu-/Abschläge";
|
MemberModifier = "Sonstige Zu-/Abschläge";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 24pt;
|
font-size: 24pt;
|
||||||
margin-top: 10mm;
|
margin-top: 0;
|
||||||
margin-bottom: 2mm;
|
margin-bottom: 2mm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 24pt;
|
font-size: 24pt;
|
||||||
margin-top: 10mm;
|
margin-top: 0;
|
||||||
margin-bottom: 2mm;
|
margin-bottom: 2mm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 24pt;
|
font-size: 24pt;
|
||||||
margin-top: 10mm;
|
margin-top: 0;
|
||||||
margin-bottom: 2mm;
|
margin-bottom: 2mm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
@if (part.Modifiers.Count() > 0) {
|
@if (part.Modifiers.Count() > 0) {
|
||||||
var first = true;
|
var first = true;
|
||||||
foreach (var mod in part.Modifiers) {
|
foreach (var mod in part.Modifiers) {
|
||||||
<tr class="tight @(first ? "first" : "")"><td></td><td>@Raw(first ? "<i>Zu-/Abschläge:</i>" : "")</td><td colspan="3"><b>@mod.Name</b></td><td style="white-space: pre;">@mod.ValueStr</td></tr>
|
<tr class="tight @(first ? "first" : "")"><td></td><td>@Raw(first ? "<i>Zu-/Abschläge:</i>" : "")</td><td colspan="3"><b>@mod.Name</b></td><td style="white-space: pre;">@mod.PublicValueStr</td></tr>
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Elwig.Helpers.Printing;
|
using Elwig.Helpers.Printing;
|
||||||
using MimeKit;
|
using MimeKit;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Elwig.Documents {
|
namespace Elwig.Documents {
|
||||||
public abstract partial class Document : IDisposable {
|
public abstract partial class Document : IDisposable {
|
||||||
@@ -18,10 +19,11 @@ namespace Elwig.Documents {
|
|||||||
protected string? _pdfPath;
|
protected string? _pdfPath;
|
||||||
protected string? PdfPath => _pdfPath ?? _pdfFile?.FilePath;
|
protected string? PdfPath => _pdfPath ?? _pdfFile?.FilePath;
|
||||||
public int? TotalPages { get; private set; }
|
public int? TotalPages { get; private set; }
|
||||||
public int? Pages => TotalPages / (DoublePaged ? 2 : 1);
|
public int? Pages => TotalPages / (IsDoublePaged ? 2 : 1);
|
||||||
|
|
||||||
public bool ShowFoldMarks = App.Config.Debug;
|
public bool ShowFoldMarks = App.Config.Debug;
|
||||||
public bool DoublePaged = false;
|
public bool IsDoublePaged = false;
|
||||||
|
public bool IsPreview = false;
|
||||||
|
|
||||||
public string DocumentsPath;
|
public string DocumentsPath;
|
||||||
public int CurrentNextSeason;
|
public int CurrentNextSeason;
|
||||||
@@ -38,15 +40,8 @@ namespace Elwig.Documents {
|
|||||||
CurrentNextSeason = Utils.CurrentNextSeason;
|
CurrentNextSeason = Utils.CurrentNextSeason;
|
||||||
Title = title;
|
Title = title;
|
||||||
Author = c.NameFull;
|
Author = c.NameFull;
|
||||||
Header = $"<div class='name'>{c.Name}</div><div class='suffix'>{c.NameSuffix}</div><div class='type'>{c.NameTypeFull}</div>";
|
Header = "";
|
||||||
Footer = Utils.GenerateFooter("<br/>", " \u00b7 ")
|
Footer = Utils.GenerateFooter("<br/>", " \u00b7 ").Item(c.NameFull).ToString();
|
||||||
.Item(c.NameFull).NextLine()
|
|
||||||
.Item(c.Address).Item($"{c.Plz} {c.Ort}").Item("Österreich").Item("Tel.", c.PhoneNr).Item("Fax", c.FaxNr).NextLine()
|
|
||||||
.Item(c.EmailAddress != null ? $"<a href=\"mailto:{c.Name} {c.NameSuffix} <{c.EmailAddress}>\">{c.EmailAddress}</a>" : null)
|
|
||||||
.Item(c.Website != null ? $"<a href=\"http://{c.Website}/\">{c.Website}</a>" : null)
|
|
||||||
.Item("Betriebs-Nr.", c.LfbisNr).Item("Bio-KSt.", c.OrganicAuthority).NextLine()
|
|
||||||
.Item("UID", c.UstIdNr).Item("BIC", c.Bic).Item("IBAN", c.Iban)
|
|
||||||
.ToString();
|
|
||||||
Date = DateOnly.FromDateTime(Utils.Today);
|
Date = DateOnly.FromDateTime(Utils.Today);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,7 +99,7 @@ namespace Elwig.Documents {
|
|||||||
return await Html.CompileRenderAsync(name, this); ;
|
return await Html.CompileRenderAsync(name, this); ;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Generate(IProgress<double>? progress = null) {
|
public async Task Generate(CancellationToken? cancelToken = null, IProgress<double>? progress = null) {
|
||||||
if (_pdfFile != null)
|
if (_pdfFile != null)
|
||||||
return;
|
return;
|
||||||
progress?.Report(0.0);
|
progress?.Report(0.0);
|
||||||
@@ -114,6 +109,7 @@ namespace Elwig.Documents {
|
|||||||
var pdf = new TempFile("pdf");
|
var pdf = new TempFile("pdf");
|
||||||
var tmpHtmls = new List<TempFile>();
|
var tmpHtmls = new List<TempFile>();
|
||||||
var tmpFiles = new List<string>();
|
var tmpFiles = new List<string>();
|
||||||
|
try {
|
||||||
var n = m.Documents.Count();
|
var n = m.Documents.Count();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
foreach (var doc in m.Documents) {
|
foreach (var doc in m.Documents) {
|
||||||
@@ -121,6 +117,8 @@ namespace Elwig.Documents {
|
|||||||
tmpFiles.Add(doc.PdfPath!);
|
tmpFiles.Add(doc.PdfPath!);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (cancelToken?.IsCancellationRequested ?? false)
|
||||||
|
throw new OperationCanceledException("Dokumentenerzeugung abgebrochen!");
|
||||||
var tmpHtml = new TempFile("html");
|
var tmpHtml = new TempFile("html");
|
||||||
await File.WriteAllTextAsync(tmpHtml.FilePath, await doc.Render(), Utils.UTF8);
|
await File.WriteAllTextAsync(tmpHtml.FilePath, await doc.Render(), Utils.UTF8);
|
||||||
tmpHtmls.Add(tmpHtml);
|
tmpHtmls.Add(tmpHtml);
|
||||||
@@ -129,21 +127,32 @@ namespace Elwig.Documents {
|
|||||||
progress?.Report(GenerationProportion * 100 * i / n);
|
progress?.Report(GenerationProportion * 100 * i / n);
|
||||||
}
|
}
|
||||||
progress?.Report(GenerationProportion * 100);
|
progress?.Report(GenerationProportion * 100);
|
||||||
var pages = await Pdf.Convert(tmpFiles, pdf.FileName, DoublePaged, new Progress<double>(v => progress?.Report(GenerationProportion * 100 + v * (1 - GenerationProportion))));
|
var pages = await Pdf.Convert(tmpFiles, pdf.FileName, IsDoublePaged, cancelToken, new Progress<double>(v => progress?.Report(GenerationProportion * 100 + v * (1 - GenerationProportion))));
|
||||||
TotalPages = pages.Pages;
|
TotalPages = pages.Pages;
|
||||||
|
_pdfFile = pdf;
|
||||||
|
} catch {
|
||||||
|
pdf.Dispose();
|
||||||
|
throw;
|
||||||
|
} finally {
|
||||||
foreach (var tmp in tmpHtmls) {
|
foreach (var tmp in tmpHtmls) {
|
||||||
tmp.Dispose();
|
tmp.Dispose();
|
||||||
}
|
}
|
||||||
_pdfFile = pdf;
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (cancelToken?.IsCancellationRequested ?? false)
|
||||||
|
throw new OperationCanceledException("Dokumentenerzeugung abgebrochen!");
|
||||||
var pdf = new TempFile("pdf");
|
var pdf = new TempFile("pdf");
|
||||||
using (var tmpHtml = new TempFile("html")) {
|
try {
|
||||||
|
using var tmpHtml = new TempFile("html");
|
||||||
await File.WriteAllTextAsync(tmpHtml.FilePath, await Render(), Utils.UTF8);
|
await File.WriteAllTextAsync(tmpHtml.FilePath, await Render(), Utils.UTF8);
|
||||||
progress?.Report(50.0);
|
progress?.Report(50.0);
|
||||||
var pages = await Pdf.Convert(tmpHtml.FilePath, pdf.FilePath, DoublePaged);
|
var pages = await Pdf.Convert(tmpHtml.FilePath, pdf.FilePath, IsDoublePaged, cancelToken);
|
||||||
TotalPages = pages.Pages;
|
TotalPages = pages.Pages;
|
||||||
}
|
|
||||||
_pdfFile = pdf;
|
_pdfFile = pdf;
|
||||||
|
} catch {
|
||||||
|
pdf.Dispose();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
progress?.Report(100.0);
|
progress?.Report(100.0);
|
||||||
}
|
}
|
||||||
@@ -155,7 +164,7 @@ namespace Elwig.Documents {
|
|||||||
|
|
||||||
public async Task Print(int copies = 1) {
|
public async Task Print(int copies = 1) {
|
||||||
if (PdfPath == null) throw new InvalidOperationException("Pdf file has not been generated yet");
|
if (PdfPath == null) throw new InvalidOperationException("Pdf file has not been generated yet");
|
||||||
await Pdf.Print(PdfPath, copies, DoublePaged);
|
await Pdf.Print(PdfPath, copies, IsDoublePaged);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Show() {
|
public void Show() {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\Document.css" />
|
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\Document.css" />
|
||||||
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\Document.Page.css" />
|
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\Document.Page.css" />
|
||||||
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\Document.Table.css" />
|
<link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\Document.Table.css" />
|
||||||
@if (Model.DoublePaged) {
|
@if (Model.IsDoublePaged) {
|
||||||
<style>
|
<style>
|
||||||
@@page :left {
|
@@page :left {
|
||||||
margin: 25mm 25mm 35mm 20mm;
|
margin: 25mm 25mm 35mm 20mm;
|
||||||
@@ -34,14 +34,14 @@
|
|||||||
<div class="pre-footer">
|
<div class="pre-footer">
|
||||||
<span class="date">@($"{Model.Date:dddd, d. MMMM yyyy}")</span>
|
<span class="date">@($"{Model.Date:dddd, d. MMMM yyyy}")</span>
|
||||||
<span class="doc-id">@Model.DocumentId</span>
|
<span class="doc-id">@Model.DocumentId</span>
|
||||||
<span class="page"></span>
|
<span><span class="page"></span>@Raw(Model.IsPreview ? " <b>(vorläufig)</b>" : "")</span>
|
||||||
</div>
|
</div>
|
||||||
<footer>@Raw(Model.Footer)</footer>
|
<footer>@Raw(Model.Footer)</footer>
|
||||||
</div>
|
</div>
|
||||||
@if (Model.DoublePaged) {
|
@if (Model.IsDoublePaged) {
|
||||||
<div class="footer-wrapper left">
|
<div class="footer-wrapper left">
|
||||||
<div class="pre-footer">
|
<div class="pre-footer">
|
||||||
<span class="page"></span>
|
<span>@Raw(Model.IsPreview ? "<b>(vorläufig)</b> " : "")<span class="page"></span></span>
|
||||||
<span class="doc-id">@Model.DocumentId</span>
|
<span class="doc-id">@Model.DocumentId</span>
|
||||||
<span class="date">@($"{Model.Date:dddd, d. MMMM yyyy}")</span>
|
<span class="date">@($"{Model.Date:dddd, d. MMMM yyyy}")</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 24pt;
|
font-size: 24pt;
|
||||||
margin-top: 10mm;
|
margin-top: 0;
|
||||||
margin-bottom: 2mm;
|
margin-bottom: 2mm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ namespace Elwig.Documents {
|
|||||||
Data = data;
|
Data = data;
|
||||||
CurrencySymbol = v.Season.Currency.Symbol ?? v.Season.Currency.Code;
|
CurrencySymbol = v.Season.Currency.Symbol ?? v.Season.Currency.Code;
|
||||||
MemberNum = v.Credits.Count;
|
MemberNum = v.Credits.Count;
|
||||||
|
IsPreview = MemberNum == 0;
|
||||||
DeliveryNum = v.DeliveryPartPayments.DistinctBy(p => p.DeliveryPart.Delivery).Count();
|
DeliveryNum = v.DeliveryPartPayments.DistinctBy(p => p.DeliveryPart.Delivery).Count();
|
||||||
DeliveryPartNum = v.DeliveryPartPayments.Count;
|
DeliveryPartNum = v.DeliveryPartPayments.Count;
|
||||||
ModifierStat = AppDbContext.GetModifierStats(v.Year, v.AvNr).GetAwaiter().GetResult();
|
ModifierStat = AppDbContext.GetModifierStats(v.Year, v.AvNr).GetAwaiter().GetResult();
|
||||||
|
|||||||
@@ -164,14 +164,14 @@
|
|||||||
<td class="number">@Utils.GetSign(considered)</td>
|
<td class="number">@Utils.GetSign(considered)</td>
|
||||||
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(considered):N2}")</td>
|
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(considered):N2}")</td>
|
||||||
<th class="lborder">Menge (gebunden):</th>
|
<th class="lborder">Menge (gebunden):</th>
|
||||||
<td colspan="2" class="number">@($"{Model.Data.Rows.Sum(r => r.Geb.Weight):N0}") kg</td>
|
<td colspan="2" class="number">@($"{Model.Data.Rows.Sum(r => r.Geb.Weight + r.LowGeb.Weight):N0}") kg</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="2">Auszahlungsbetrag:</th>
|
<th colspan="2">Auszahlungsbetrag:</th>
|
||||||
<td class="number tborder"></td>
|
<td class="number tborder"></td>
|
||||||
<td class="number tborder"><span class="fleft">@Model.CurrencySymbol</span>@($"{totalSum:N2}")</td>
|
<td class="number tborder"><span class="fleft">@Model.CurrencySymbol</span>@($"{totalSum:N2}")</td>
|
||||||
<th class="lborder">Gesamtmenge:</th>
|
<th class="lborder">Gesamtmenge:</th>
|
||||||
<td colspan="2" class="number tborder">@($"{Model.Data.Rows.Sum(r => r.Ungeb.Weight + r.Geb.Weight):N0}") kg</td>
|
<td colspan="2" class="number tborder">@($"{Model.Data.Rows.Sum(r => r.Ungeb.Weight + r.LowGeb.Weight + r.Geb.Weight):N0}") kg</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 24pt;
|
font-size: 24pt;
|
||||||
margin-top: 10mm;
|
margin-top: 0;
|
||||||
margin-bottom: 2mm;
|
margin-bottom: 2mm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 24pt;
|
font-size: 24pt;
|
||||||
margin-top: 10mm;
|
margin-top: 0;
|
||||||
margin-bottom: 2mm;
|
margin-bottom: 2mm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net8.0-windows</TargetFramework>
|
<TargetFramework>net10.0-windows</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<UseWPF>true</UseWPF>
|
<UseWPF>true</UseWPF>
|
||||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||||
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
|
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
|
||||||
<Version>1.0.1.3</Version>
|
<Version>1.0.3.0</Version>
|
||||||
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
|
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
@@ -22,22 +22,23 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||||
<PackageReference Include="LinqKit" Version="1.3.8" />
|
<PackageReference Include="LinqKit" Version="1.3.9" />
|
||||||
<PackageReference Include="MailKit" Version="4.13.0" />
|
<PackageReference Include="MailKit" Version="4.14.1" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.36" />
|
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.36" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.9" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="10.0.2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.9" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.2" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="9.0.9" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.2" />
|
||||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3485.44" />
|
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3650.58" />
|
||||||
<PackageReference Include="NJsonSchema" Version="11.4.0" />
|
<PackageReference Include="NJsonSchema" Version="11.5.2" />
|
||||||
<PackageReference Include="PdfiumViewer" Version="2.13.0" />
|
<PackageReference Include="PdfiumViewer" Version="2.13.0" />
|
||||||
<PackageReference Include="PdfiumViewer.Native.x86_64.no_v8-no_xfa" Version="2018.4.8.256" />
|
<PackageReference Include="PdfiumViewer.Native.x86_64.no_v8-no_xfa" Version="2018.4.8.256" />
|
||||||
<PackageReference Include="RazorLight" Version="2.3.1" />
|
<PackageReference Include="RazorLight" Version="2.3.1" />
|
||||||
<PackageReference Include="ScottPlot.WPF" Version="5.0.56" />
|
<PackageReference Include="ScottPlot.WPF" Version="5.1.57" />
|
||||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.2" />
|
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.2" />
|
||||||
<PackageReference Include="System.IO.Hashing" Version="9.0.9" />
|
<PackageReference Include="System.IO.Hashing" Version="10.0.2" />
|
||||||
<PackageReference Include="System.IO.Ports" Version="9.0.9" />
|
<PackageReference Include="System.IO.Ports" Version="10.0.2" />
|
||||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.9" />
|
<PackageReference Include="System.Management" Version="10.0.2" />
|
||||||
|
<PackageReference Include="System.Text.Encoding.CodePages" Version="10.0.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ using Microsoft.Data.Sqlite;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Elwig.Models.Dtos;
|
using Elwig.Models.Dtos;
|
||||||
using System.Reflection;
|
|
||||||
using System.Data;
|
using System.Data;
|
||||||
|
|
||||||
namespace Elwig.Helpers {
|
namespace Elwig.Helpers {
|
||||||
@@ -67,7 +66,7 @@ namespace Elwig.Helpers {
|
|||||||
|
|
||||||
public DbSet<OverUnderDeliveryRow> OverUnderDeliveryRows { get; private set; }
|
public DbSet<OverUnderDeliveryRow> OverUnderDeliveryRows { get; private set; }
|
||||||
public DbSet<AreaComUnderDeliveryRowSingle> AreaComUnderDeliveryRows { get; private set; }
|
public DbSet<AreaComUnderDeliveryRowSingle> AreaComUnderDeliveryRows { get; private set; }
|
||||||
public DbSet<MemberDeliveryPerVariantRowSingle> MemberDeliveryPerVariantRows { get; private set; }
|
public DbSet<MemberDeliveryPerVarietyRowSingle> MemberDeliveryPerVariantRows { get; private set; }
|
||||||
public DbSet<MemberAreaComsRowSingle> MemberAreaComsRows { get; private set; }
|
public DbSet<MemberAreaComsRowSingle> MemberAreaComsRows { get; private set; }
|
||||||
public DbSet<CreditNoteDeliveryRowSingle> CreditNoteDeliveryRows { get; private set; }
|
public DbSet<CreditNoteDeliveryRowSingle> CreditNoteDeliveryRows { get; private set; }
|
||||||
public DbSet<CreditNoteRowSingle> CreditNoteRows { get; private set; }
|
public DbSet<CreditNoteRowSingle> CreditNoteRows { get; private set; }
|
||||||
@@ -117,39 +116,6 @@ namespace Elwig.Helpers {
|
|||||||
return cnx;
|
return cnx;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task ExecuteBatch(SqliteConnection cnx, string sql) {
|
|
||||||
using var cmd = cnx.CreateCommand();
|
|
||||||
cmd.CommandText = sql;
|
|
||||||
await (await cmd.ExecuteReaderAsync()).CloseAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task ExecuteEmbeddedScript(SqliteConnection cnx, Assembly asm, string name) {
|
|
||||||
using var stream = asm.GetManifestResourceStream(name) ?? throw new FileNotFoundException("Unable to load embedded resource");
|
|
||||||
using var reader = new StreamReader(stream);
|
|
||||||
await ExecuteBatch(cnx, await reader.ReadToEndAsync());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<object?> ExecuteScalar(SqliteConnection cnx, string sql) {
|
|
||||||
using var cmd = cnx.CreateCommand();
|
|
||||||
cmd.CommandText = sql;
|
|
||||||
return await cmd.ExecuteScalarAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<(string Table, long RowId, string Parent, long FkId)[]> ForeignKeyCheck(SqliteConnection cnx) {
|
|
||||||
using var cmd = cnx.CreateCommand();
|
|
||||||
cmd.CommandText = "PRAGMA foreign_key_check";
|
|
||||||
using var reader = await cmd.ExecuteReaderAsync();
|
|
||||||
var list = new List<(string, long, string, long)>();
|
|
||||||
while (await reader.ReadAsync()) {
|
|
||||||
var table = reader.GetString(0);
|
|
||||||
var rowid = reader.GetInt64(1);
|
|
||||||
var parent = reader.GetString(2);
|
|
||||||
var fkid = reader.GetInt64(3);
|
|
||||||
list.Add((table, rowid, parent, fkid));
|
|
||||||
}
|
|
||||||
return [.. list];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
|
||||||
optionsBuilder.UseSqlite(ConnectionString);
|
optionsBuilder.UseSqlite(ConnectionString);
|
||||||
optionsBuilder.UseLazyLoadingProxies();
|
optionsBuilder.UseLazyLoadingProxies();
|
||||||
|
|||||||
@@ -9,17 +9,17 @@ namespace Elwig.Helpers {
|
|||||||
public static class AppDbUpdater {
|
public static class AppDbUpdater {
|
||||||
|
|
||||||
// Don't forget to update value in Tests/fetch-resources.bat!
|
// Don't forget to update value in Tests/fetch-resources.bat!
|
||||||
public static readonly int RequiredSchemaVersion = 33;
|
public static readonly int RequiredSchemaVersion = 36;
|
||||||
|
|
||||||
private static int VersionOffset = 0;
|
private static int VersionOffset = 0;
|
||||||
|
|
||||||
public static async Task<Version> CheckDb() {
|
public static async Task<Version> CheckDb() {
|
||||||
using var cnx = AppDbContext.Connect();
|
using var cnx = AppDbContext.Connect();
|
||||||
|
|
||||||
var applId = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA application_id") ?? 0;
|
var applId = (long?)await cnx.ExecuteScalar("PRAGMA application_id") ?? 0;
|
||||||
if (applId != 0x454C5747) throw new Exception($"Invalid application_id in database (0x{applId:X08})");
|
if (applId != 0x454C5747) throw new Exception($"Invalid application_id in database (0x{applId:X08})");
|
||||||
|
|
||||||
var schemaVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA schema_version") ?? 0;
|
var schemaVers = (long?)await cnx.ExecuteScalar("PRAGMA schema_version") ?? 0;
|
||||||
VersionOffset = (int)(schemaVers % 100);
|
VersionOffset = (int)(schemaVers % 100);
|
||||||
if (VersionOffset != 0) {
|
if (VersionOffset != 0) {
|
||||||
// schema was modified manually/externally
|
// schema was modified manually/externally
|
||||||
@@ -27,12 +27,12 @@ namespace Elwig.Helpers {
|
|||||||
}
|
}
|
||||||
await UpdateDbSchema(cnx, (int)(schemaVers / 100), RequiredSchemaVersion);
|
await UpdateDbSchema(cnx, (int)(schemaVers / 100), RequiredSchemaVersion);
|
||||||
|
|
||||||
var userVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA user_version") ?? 0;
|
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));
|
var v = new Version((int)(userVers >> 24), (int)((userVers >> 16) & 0xFF), (int)((userVers >> 8) & 0xFF), (int)(userVers & 0xFF));
|
||||||
|
|
||||||
if (App.Version > v) {
|
if (App.Version > v) {
|
||||||
long vers = (App.Version.Major << 24) | (App.Version.Minor << 16) | (App.Version.Build << 8) | App.Version.Revision;
|
long vers = (App.Version.Major << 24) | (App.Version.Minor << 16) | (App.Version.Build << 8) | App.Version.Revision;
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"PRAGMA user_version = {vers}");
|
await cnx.ExecuteBatch($"PRAGMA user_version = {vers}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
@@ -67,20 +67,20 @@ namespace Elwig.Helpers {
|
|||||||
if (toExecute.Count == 0)
|
if (toExecute.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await AppDbContext.ExecuteBatch(cnx, """
|
await cnx.ExecuteBatch("""
|
||||||
PRAGMA locking_mode = EXCLUSIVE;
|
PRAGMA locking_mode = EXCLUSIVE;
|
||||||
BEGIN EXCLUSIVE;
|
BEGIN EXCLUSIVE;
|
||||||
""");
|
""");
|
||||||
foreach (var script in toExecute) {
|
foreach (var script in toExecute) {
|
||||||
await AppDbContext.ExecuteEmbeddedScript(cnx, asm, script);
|
await cnx.ExecuteEmbeddedScript(asm, script);
|
||||||
}
|
}
|
||||||
var violations = await AppDbContext.ForeignKeyCheck(cnx);
|
var violations = await cnx.ForeignKeyCheck();
|
||||||
if (violations.Length > 0) {
|
if (violations.Length > 0) {
|
||||||
throw new Exception($"Foreign key violations ({violations.Length}):\n" + string.Join("\n", violations
|
throw new Exception($"Foreign key violations ({violations.Length}):\n" + string.Join("\n", violations
|
||||||
.Select(v => $"{v.Table} - {v.RowId} - {v.Parent} - {v.FkId}")));
|
.Select(v => $"{v.Table} - {v.RowId} - {v.Parent} - {v.FkId}")));
|
||||||
}
|
}
|
||||||
|
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
COMMIT;
|
COMMIT;
|
||||||
VACUUM;
|
VACUUM;
|
||||||
PRAGMA schema_version = {toVersion * 100 + VersionOffset};
|
PRAGMA schema_version = {toVersion * 100 + VersionOffset};
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace Elwig.Helpers.Billing {
|
|||||||
|
|
||||||
public async Task FinishSeason() {
|
public async Task FinishSeason() {
|
||||||
using var cnx = await AppDbContext.ConnectAsync();
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
UPDATE season
|
UPDATE season
|
||||||
SET (start_date, end_date) = (SELECT MIN(date), MAX(date) FROM delivery WHERE year = {Year})
|
SET (start_date, end_date) = (SELECT MIN(date), MAX(date) FROM delivery WHERE year = {Year})
|
||||||
WHERE year = {Year};
|
WHERE year = {Year};
|
||||||
@@ -37,7 +37,7 @@ 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) {
|
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;
|
if (addMinBs < 1) addMinBs = 1;
|
||||||
using var cnx = await AppDbContext.ConnectAsync();
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
UPDATE member
|
UPDATE member
|
||||||
SET business_shares = member.business_shares - h.business_shares
|
SET business_shares = member.business_shares - h.business_shares
|
||||||
FROM member_history h
|
FROM member_history h
|
||||||
@@ -66,7 +66,7 @@ namespace Elwig.Helpers.Billing {
|
|||||||
|
|
||||||
public async Task UnAdjustBusinessShares() {
|
public async Task UnAdjustBusinessShares() {
|
||||||
using var cnx = await AppDbContext.ConnectAsync();
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
UPDATE member
|
UPDATE member
|
||||||
SET business_shares = member.business_shares - h.business_shares
|
SET business_shares = member.business_shares - h.business_shares
|
||||||
FROM member_history h
|
FROM member_history h
|
||||||
@@ -157,9 +157,9 @@ namespace Elwig.Helpers.Billing {
|
|||||||
lastMgNr = mgnr;
|
lastMgNr = mgnr;
|
||||||
}
|
}
|
||||||
|
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"UPDATE delivery_part_bucket SET value = 0 WHERE year = {Year}");
|
await cnx.ExecuteBatch($"UPDATE delivery_part_bucket SET value = 0 WHERE year = {Year}");
|
||||||
if (inserts.Count > 0) {
|
if (inserts.Count > 0) {
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value)
|
INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value)
|
||||||
VALUES {string.Join(",\n ", inserts.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '{i.Item4}', {i.Item5})"))}
|
VALUES {string.Join(",\n ", inserts.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '{i.Item4}', {i.Item5})"))}
|
||||||
ON CONFLICT DO UPDATE
|
ON CONFLICT DO UPDATE
|
||||||
@@ -237,7 +237,7 @@ namespace Elwig.Helpers.Billing {
|
|||||||
if (needed == 0) break;
|
if (needed == 0) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value)
|
INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value)
|
||||||
VALUES {string.Join(",\n ", posChanges.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '', {i.Item4})"))}
|
VALUES {string.Join(",\n ", posChanges.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '', {i.Item4})"))}
|
||||||
ON CONFLICT DO UPDATE
|
ON CONFLICT DO UPDATE
|
||||||
|
|||||||
@@ -20,13 +20,19 @@ namespace Elwig.Helpers.Billing {
|
|||||||
Data = PaymentBillingData.FromJson(PaymentVariant.Data, Utils.GetVaributes(ctx, Year, onlyDelivered: false));
|
Data = PaymentBillingData.FromJson(PaymentVariant.Data, Utils.GetVaributes(ctx, Year, onlyDelivered: false));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Calculate(bool? honorGebunden = null, bool? allowAttrsIntoLower = null, bool? avoidUnderDeliveries = null) {
|
public async Task Calculate(bool strictPrices = true, bool? honorGebunden = null, bool? allowAttrsIntoLower = null, bool? avoidUnderDeliveries = null) {
|
||||||
using var cnx = await AppDbContext.ConnectAsync();
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
using var tx = await cnx.BeginTransactionAsync();
|
using var tx = await cnx.BeginTransactionAsync();
|
||||||
await CalculateBuckets(honorGebunden, allowAttrsIntoLower, avoidUnderDeliveries, cnx);
|
await CalculateBuckets(honorGebunden, allowAttrsIntoLower, avoidUnderDeliveries, cnx);
|
||||||
await DeleteInDb(cnx);
|
await DeleteInDb(cnx);
|
||||||
await SetCalcTime(cnx);
|
await SetCalcTime(cnx);
|
||||||
await CalculatePrices(cnx);
|
KeyNotFoundException? exception = null;
|
||||||
|
try {
|
||||||
|
await CalculatePrices(cnx, strictPrices);
|
||||||
|
} catch (KeyNotFoundException e) {
|
||||||
|
if (strictPrices) throw;
|
||||||
|
exception = e;
|
||||||
|
}
|
||||||
if (Data.ConsiderDelieryModifiers) {
|
if (Data.ConsiderDelieryModifiers) {
|
||||||
await CalculateDeliveryModifiers(cnx);
|
await CalculateDeliveryModifiers(cnx);
|
||||||
}
|
}
|
||||||
@@ -34,26 +40,28 @@ namespace Elwig.Helpers.Billing {
|
|||||||
await CalculateMemberModifiers(cnx);
|
await CalculateMemberModifiers(cnx);
|
||||||
}
|
}
|
||||||
await tx.CommitAsync();
|
await tx.CommitAsync();
|
||||||
|
if (exception != null)
|
||||||
|
throw exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Commit() {
|
public async Task Commit() {
|
||||||
await Revert();
|
await Revert();
|
||||||
using var cnx = await AppDbContext.ConnectAsync();
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
INSERT INTO credit (year, tgnr, mgnr, avnr, net_amount, prev_net_amount, vat, modifiers, prev_modifiers)
|
INSERT INTO credit (year, tgnr, mgnr, avnr, net_amount, prev_net_amount, vat, modifiers, prev_modifiers)
|
||||||
SELECT s.year,
|
SELECT s.year,
|
||||||
COALESCE(t.tgnr, 0) + ROW_NUMBER() OVER(ORDER BY m.mgnr) AS tgnr,
|
COALESCE(t.tgnr, 0) + ROW_NUMBER() OVER(ORDER BY m.mgnr) AS tgnr,
|
||||||
m.mgnr,
|
m.mgnr,
|
||||||
v.avnr,
|
v.avnr,
|
||||||
ROUND(p.amount / POW(10, s.precision - 2)) AS net_amount,
|
ROUND(p.amount / POW(10, s.precision - 2)) AS net_amount,
|
||||||
IIF(lc.amount >= 0, ROUND(lp.amount / POW(10, s.precision - 2)), 0) AS prev_net_amount,
|
IIF(lc.amount < 0, 0, ROUND(lp.amount / POW(10, s.precision - 2))) AS prev_net_amount,
|
||||||
IIF(m.buchführend, s.vat_normal, s.vat_flatrate) AS vat,
|
IIF(m.buchführend, s.vat_normal, s.vat_flatrate) AS vat,
|
||||||
ROUND(IIF({Data.ConsiderTotalPenalty}, COALESCE(b.total_penalty, 0), 0) / POW(10, s.precision - 2)) +
|
ROUND(IIF({Data.ConsiderTotalPenalty}, COALESCE(b.total_penalty, 0), 0) / POW(10, s.precision - 2)) +
|
||||||
ROUND(IIF({Data.ConsiderContractPenalties}, COALESCE(u.total_penalty, 0), 0) / POW(10, 4 - 2)) +
|
ROUND(IIF({Data.ConsiderContractPenalties}, COALESCE(u.total_penalty, 0), 0) / POW(10, 4 - 2)) +
|
||||||
ROUND(IIF({Data.ConsiderAutoBusinessShares}, -COALESCE(a.total_amount, 0), 0) / POW(10, s.precision - 2)) +
|
ROUND(IIF({Data.ConsiderAutoBusinessShares}, -COALESCE(a.total_amount, 0), 0) / POW(10, s.precision - 2)) +
|
||||||
IIF({Data.ConsiderCustomModifiers}, COALESCE(x.amount, 0), 0)
|
IIF({Data.ConsiderCustomModifiers}, COALESCE(x.amount, 0), 0)
|
||||||
AS modifiers,
|
AS modifiers,
|
||||||
IIF(lc.amount >= 0, lc.modifiers, 0) AS prev_modifiers
|
IIF(lc.amount < 0, 0, lc.modifiers) AS prev_modifiers
|
||||||
FROM season s
|
FROM season s
|
||||||
JOIN payment_variant v ON v.year = s.year
|
JOIN payment_variant v ON v.year = s.year
|
||||||
LEFT JOIN payment_variant l ON l.year = s.year
|
LEFT JOIN payment_variant l ON l.year = s.year
|
||||||
@@ -74,27 +82,27 @@ namespace Elwig.Helpers.Billing {
|
|||||||
LEFT JOIN payment_custom x ON (x.year, x.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};
|
WHERE s.year = {Year} AND v.avnr = {AvNr};
|
||||||
""");
|
""");
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
UPDATE payment_variant SET test_variant = FALSE WHERE (year, avnr) = ({Year}, {AvNr});
|
UPDATE payment_variant SET test_variant = FALSE WHERE (year, avnr) = ({Year}, {AvNr});
|
||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Revert() {
|
public async Task Revert() {
|
||||||
using var cnx = await AppDbContext.ConnectAsync();
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
DELETE FROM credit WHERE (year, avnr) = ({Year}, {AvNr});
|
DELETE FROM credit WHERE (year, avnr) = ({Year}, {AvNr});
|
||||||
UPDATE payment_variant SET test_variant = TRUE WHERE (year, avnr) = ({Year}, {AvNr});
|
UPDATE payment_variant SET test_variant = TRUE WHERE (year, avnr) = ({Year}, {AvNr});
|
||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task SetCalcTime(SqliteConnection cnx) {
|
protected async Task SetCalcTime(SqliteConnection cnx) {
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
UPDATE payment_variant SET calc_time = UNIXEPOCH() WHERE (year, avnr) = ({Year}, {AvNr})
|
UPDATE payment_variant SET calc_time = UNIXEPOCH() WHERE (year, avnr) = ({Year}, {AvNr})
|
||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task DeleteInDb(SqliteConnection cnx) {
|
protected async Task DeleteInDb(SqliteConnection cnx) {
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
DELETE FROM payment_delivery_part_bucket WHERE (year, avnr) = ({Year}, {AvNr});
|
DELETE FROM payment_delivery_part_bucket WHERE (year, avnr) = ({Year}, {AvNr});
|
||||||
DELETE FROM payment_delivery_part WHERE (year, avnr) = ({Year}, {AvNr});
|
DELETE FROM payment_delivery_part WHERE (year, avnr) = ({Year}, {AvNr});
|
||||||
DELETE FROM payment_member WHERE (year, avnr) = ({Year}, {AvNr});
|
DELETE FROM payment_member WHERE (year, avnr) = ({Year}, {AvNr});
|
||||||
@@ -108,7 +116,7 @@ namespace Elwig.Helpers.Billing {
|
|||||||
var multiplier = 0.50;
|
var multiplier = 0.50;
|
||||||
var includePredecessor = true;
|
var includePredecessor = true;
|
||||||
var modName = "Treue%";
|
var modName = "Treue%";
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
INSERT INTO payment_member (year, avnr, mgnr, net_amount, mod_abs, mod_rel)
|
INSERT INTO payment_member (year, avnr, mgnr, net_amount, mod_abs, mod_rel)
|
||||||
SELECT c.year, {AvNr}, s.mgnr, 0,
|
SELECT c.year, {AvNr}, s.mgnr, 0,
|
||||||
ROUND(s.sum * COALESCE(m.abs, 0)),
|
ROUND(s.sum * COALESCE(m.abs, 0)),
|
||||||
@@ -130,7 +138,7 @@ namespace Elwig.Helpers.Billing {
|
|||||||
mod_rel = mod_rel + excluded.mod_rel
|
mod_rel = mod_rel + excluded.mod_rel
|
||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
INSERT INTO payment_member (year, avnr, mgnr, net_amount, mod_abs, mod_rel)
|
INSERT INTO payment_member (year, avnr, mgnr, net_amount, mod_abs, mod_rel)
|
||||||
SELECT x.year, {AvNr}, x.mgnr, 0, COALESCE(x.mod_abs * POW(10, s.precision - 2), 0), COALESCE(x.mod_rel, 0)
|
SELECT x.year, {AvNr}, x.mgnr, 0, COALESCE(x.mod_abs * POW(10, s.precision - 2), 0), COALESCE(x.mod_rel, 0)
|
||||||
FROM payment_custom x
|
FROM payment_custom x
|
||||||
@@ -142,7 +150,8 @@ namespace Elwig.Helpers.Billing {
|
|||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task CalculatePrices(SqliteConnection cnx) {
|
protected async Task CalculatePrices(SqliteConnection cnx, bool strict = true) {
|
||||||
|
var invalid = new HashSet<string>();
|
||||||
var parts = new List<(int Year, int DId, int DPNr, int BktNr, string SortId, string? AttrId, string? CultId, string Discr, int Value, double Oe, double Kmw, string QualId, bool AttrAreaCom)>();
|
var parts = new List<(int Year, int DId, int DPNr, int BktNr, string SortId, string? AttrId, string? CultId, string Discr, int Value, double Oe, double Kmw, string QualId, bool AttrAreaCom)>();
|
||||||
using (var cmd = cnx.CreateCommand()) {
|
using (var cmd = cnx.CreateCommand()) {
|
||||||
cmd.CommandText = $"""
|
cmd.CommandText = $"""
|
||||||
@@ -172,21 +181,31 @@ namespace Elwig.Helpers.Billing {
|
|||||||
var payAttrId = (part.Discr is "" or "_") ? null : part.Discr;
|
var payAttrId = (part.Discr is "" or "_") ? null : part.Discr;
|
||||||
var attrId = part.AttrAreaCom ? payAttrId : part.AttrId;
|
var attrId = part.AttrAreaCom ? payAttrId : part.AttrId;
|
||||||
var geb = !ungeb && (payAttrId == attrId || !part.AttrAreaCom);
|
var geb = !ungeb && (payAttrId == attrId || !part.AttrAreaCom);
|
||||||
var price = Data.CalculatePrice(part.SortId, attrId, part.CultId, part.QualId, geb, part.Oe, part.Kmw);
|
decimal price = 0;
|
||||||
|
try {
|
||||||
|
price = Data.CalculatePrice(part.SortId, attrId, part.CultId, part.QualId, geb, part.Oe, part.Kmw);
|
||||||
|
} catch (KeyNotFoundException e) {
|
||||||
|
invalid.Add(e.Message.Split('\'')[1]);
|
||||||
|
}
|
||||||
var priceL = PaymentVariant.Season.DecToDb(price);
|
var priceL = PaymentVariant.Season.DecToDb(price);
|
||||||
inserts.Add((part.Year, part.DId, part.DPNr, part.BktNr, priceL, priceL * part.Value));
|
inserts.Add((part.Year, part.DId, part.DPNr, part.BktNr, priceL, priceL * part.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
var msg = invalid.Count == 0 ? null : "Für folgende Sorten wurde noch keine Preiskurve festgelegt: " + string.Join(", ", invalid);
|
||||||
|
if (msg != null && strict)
|
||||||
|
throw new KeyNotFoundException(msg);
|
||||||
|
await cnx.ExecuteBatch($"""
|
||||||
INSERT INTO payment_delivery_part_bucket (year, did, dpnr, bktnr, avnr, price, amount)
|
INSERT INTO payment_delivery_part_bucket (year, did, dpnr, bktnr, avnr, price, amount)
|
||||||
VALUES {string.Join(",\n ", inserts.Select(i => $"({i.Year}, {i.DId}, {i.DPNr}, {i.BktNr}, {AvNr}, {i.Price}, {i.Amount})"))};
|
VALUES {string.Join(",\n ", inserts.Select(i => $"({i.Year}, {i.DId}, {i.DPNr}, {i.BktNr}, {AvNr}, {i.Price}, {i.Amount})"))};
|
||||||
""");
|
""");
|
||||||
|
if (msg != null)
|
||||||
|
throw new KeyNotFoundException(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task CalculateDeliveryModifiers(SqliteConnection cnx) {
|
protected async Task CalculateDeliveryModifiers(SqliteConnection cnx) {
|
||||||
var netMod = Data.NetWeightModifier.ToString().Replace(',', '.');
|
var netMod = Data.NetWeightModifier.ToString().Replace(',', '.');
|
||||||
var grossMod = Data.GrossWeightModifier.ToString().Replace(',', '.');
|
var grossMod = Data.GrossWeightModifier.ToString().Replace(',', '.');
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
INSERT INTO payment_delivery_part (year, did, dpnr, avnr, net_amount, mod_abs, 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, 0, IIF(d.net_weight, {netMod}, {grossMod})
|
SELECT d.year, d.did, d.dpnr, {AvNr}, 0, 0, IIF(d.net_weight, {netMod}, {grossMod})
|
||||||
FROM delivery_part d
|
FROM delivery_part d
|
||||||
|
|||||||
@@ -206,15 +206,15 @@ namespace Elwig.Helpers {
|
|||||||
case 1: orderingMemberList = "NAME"; break;
|
case 1: orderingMemberList = "NAME"; break;
|
||||||
case 2: orderingMemberList = "KG"; break;
|
case 2: orderingMemberList = "KG"; break;
|
||||||
}
|
}
|
||||||
string mailSendPostal = "MGNR";
|
string mailSendPostal = "WISH";
|
||||||
switch (MailOrdering) {
|
switch (MailSendPostal) {
|
||||||
case 0: mailSendPostal = "NONE"; break;
|
case 0: mailSendPostal = "NONE"; break;
|
||||||
case 1: mailSendPostal = "NO_EMAIL"; break;
|
case 1: mailSendPostal = "NO_EMAIL"; break;
|
||||||
case 2: mailSendPostal = "WISH"; break;
|
case 2: mailSendPostal = "WISH"; break;
|
||||||
case 3: mailSendPostal = "ALL"; break;
|
case 3: mailSendPostal = "ALL"; break;
|
||||||
}
|
}
|
||||||
string mailSendEmail = "MGNR";
|
string mailSendEmail = "WISH";
|
||||||
switch (MailOrdering) {
|
switch (MailSendEmail) {
|
||||||
case 0: mailSendEmail = "NONE"; break;
|
case 0: mailSendEmail = "NONE"; break;
|
||||||
case 1: mailSendEmail = "WISH"; break;
|
case 1: mailSendEmail = "WISH"; break;
|
||||||
case 2: mailSendEmail = "ALL"; break;
|
case 2: mailSendEmail = "ALL"; break;
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ namespace Elwig.Helpers.Export {
|
|||||||
protected readonly char Separator;
|
protected readonly char Separator;
|
||||||
protected string? Header;
|
protected string? Header;
|
||||||
|
|
||||||
public Csv(string filename, char separator = ';') : this(filename, separator, Utils.UTF8) { }
|
public Csv(string filename, char separator = ';') :
|
||||||
|
this(filename, separator, Utils.UTF8) {
|
||||||
|
}
|
||||||
|
|
||||||
public Csv(string filename, char separator, Encoding encoding) {
|
public Csv(string filename, char separator, Encoding encoding) {
|
||||||
_writer = new StreamWriter(filename, false, encoding);
|
_writer = new StreamWriter(filename, false, encoding);
|
||||||
@@ -58,4 +60,22 @@ namespace Elwig.Helpers.Export {
|
|||||||
|
|
||||||
public abstract string FormatRow(T row);
|
public abstract string FormatRow(T row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class CsvSimple : Csv<IEnumerable<object?>> {
|
||||||
|
|
||||||
|
public CsvSimple(string filename, char separator, Encoding encoding) :
|
||||||
|
base(filename, separator, encoding) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public CsvSimple(string filename, char separator = ';') :
|
||||||
|
base(filename, separator) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string FormatRow(IEnumerable<object?> row) {
|
||||||
|
return string.Join(Separator, row.Select(i => {
|
||||||
|
var str = $"{i}";
|
||||||
|
return str.Contains(Separator) || str.Contains('\n') ? $"\"{str.Replace("\"", "\"\"")}\"" : str;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ namespace Elwig.Helpers.Export {
|
|||||||
private static async Task<(long? ApplicationId, string? UserVersion, long? SchemaVersion, long FileSize)> GetMeta() {
|
private static async Task<(long? ApplicationId, string? UserVersion, long? SchemaVersion, long FileSize)> GetMeta() {
|
||||||
long size = new FileInfo(App.Config.DatabaseFile).Length;
|
long size = new FileInfo(App.Config.DatabaseFile).Length;
|
||||||
using var cnx = await AppDbContext.ConnectAsync();
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
var applId = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA application_id");
|
var applId = (long?)await cnx.ExecuteScalar("PRAGMA application_id");
|
||||||
var userVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA user_version");
|
var userVers = (long?)await cnx.ExecuteScalar("PRAGMA user_version");
|
||||||
var schemaVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA schema_version");
|
var schemaVers = (long?)await cnx.ExecuteScalar("PRAGMA schema_version");
|
||||||
return (applId, userVers != null ? $"{userVers >> 24}.{(userVers >> 16) & 0xFF}.{(userVers >> 8) & 0xFF}.{userVers & 0xFF}" : null, schemaVers, size);
|
return (applId, userVers != null ? $"{userVers >> 24}.{(userVers >> 16) & 0xFF}.{(userVers >> 8) & 0xFF}.{userVers & 0xFF}" : null, schemaVers, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,9 +100,9 @@ namespace Elwig.Helpers.Export {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var applId = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA application_id") ?? 0;
|
var applId = (long?)await cnx.ExecuteScalar("PRAGMA application_id") ?? 0;
|
||||||
var userVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA user_version") ?? 0;
|
var userVers = (long?)await cnx.ExecuteScalar("PRAGMA user_version") ?? 0;
|
||||||
var schemaVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA schema_version") ?? 0;
|
var schemaVers = (long?)await cnx.ExecuteScalar("PRAGMA schema_version") ?? 0;
|
||||||
|
|
||||||
await writer.WriteLineAsync($"-- Elwig database dump, {DateTime.Now:yyyy-MM-dd, HH:mm:ss}");
|
await writer.WriteLineAsync($"-- Elwig database dump, {DateTime.Now:yyyy-MM-dd, HH:mm:ss}");
|
||||||
await writer.WriteLineAsync($"-- {Environment.MachineName}, Zwst. {App.BranchName}, {App.Client.Name}");
|
await writer.WriteLineAsync($"-- {Environment.MachineName}, Zwst. {App.BranchName}, {App.Client.Name}");
|
||||||
@@ -224,7 +224,7 @@ namespace Elwig.Helpers.Export {
|
|||||||
File.Move(filename, App.Config.DatabaseFile, false);
|
File.Move(filename, App.Config.DatabaseFile, false);
|
||||||
|
|
||||||
using var cnx = await AppDbContext.ConnectAsync();
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
await AppDbContext.ExecuteBatch(cnx, "VACUUM");
|
await cnx.ExecuteBatch("VACUUM");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task ImportSql(StreamReader reader) {
|
public static async Task ImportSql(StreamReader reader) {
|
||||||
@@ -232,7 +232,7 @@ namespace Elwig.Helpers.Export {
|
|||||||
File.Delete(newName);
|
File.Delete(newName);
|
||||||
try {
|
try {
|
||||||
using (var cnx = await AppDbContext.ConnectAsync($"Data Source=\"{newName}\"; Mode=ReadWriteCreate; Foreign Keys=False; Cache=Default; Pooling=False")) {
|
using (var cnx = await AppDbContext.ConnectAsync($"Data Source=\"{newName}\"; Mode=ReadWriteCreate; Foreign Keys=False; Cache=Default; Pooling=False")) {
|
||||||
await AppDbContext.ExecuteBatch(cnx, await reader.ReadToEndAsync());
|
await cnx.ExecuteBatch(await reader.ReadToEndAsync());
|
||||||
}
|
}
|
||||||
await ImportSqlite(newName);
|
await ImportSqlite(newName);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -345,7 +345,7 @@ namespace Elwig.Helpers.Export {
|
|||||||
$"mtime = {((DateTimeOffset)m.ModifiedAt.ToUniversalTime()).ToUnixTimeSeconds()} " +
|
$"mtime = {((DateTimeOffset)m.ModifiedAt.ToUniversalTime()).ToUnixTimeSeconds()} " +
|
||||||
$"WHERE ({primaryKeys[e.Key]}) = ({m.Id1}, {m.Id2});"));
|
$"WHERE ({primaryKeys[e.Key]}) = ({m.Id1}, {m.Id2});"));
|
||||||
using var cnx = AppDbContext.Connect();
|
using var cnx = AppDbContext.Connect();
|
||||||
await AppDbContext.ExecuteBatch(cnx, $"""
|
await cnx.ExecuteBatch($"""
|
||||||
BEGIN;
|
BEGIN;
|
||||||
UPDATE client_parameter SET value = '0' WHERE param = 'ENABLE_TIME_TRIGGERS';
|
UPDATE client_parameter SET value = '0' WHERE param = 'ENABLE_TIME_TRIGGERS';
|
||||||
{string.Join("\n", updateStmts)}
|
{string.Join("\n", updateStmts)}
|
||||||
@@ -744,8 +744,8 @@ namespace Elwig.Helpers.Export {
|
|||||||
};
|
};
|
||||||
if (p.IsSplCheck) obj["spl_check"] = p.IsSplCheck;
|
if (p.IsSplCheck) obj["spl_check"] = p.IsSplCheck;
|
||||||
if (p.IsHandPicked != null) obj["hand_picked"] = p.IsHandPicked;
|
if (p.IsHandPicked != null) obj["hand_picked"] = p.IsHandPicked;
|
||||||
if (p.IsLesewagen != null) obj["lesewagen"] = p.IsLesewagen;
|
|
||||||
if (p.IsGebunden != null) obj["gebunden"] = p.IsGebunden;
|
if (p.IsGebunden != null) obj["gebunden"] = p.IsGebunden;
|
||||||
|
if (p.Unloading != null) obj["unloading"] = p.Unloading;
|
||||||
if (p.Temperature != null) obj["temperature"] = p.Temperature;
|
if (p.Temperature != null) obj["temperature"] = p.Temperature;
|
||||||
if (p.Acid != null) obj["acid"] = p.Acid;
|
if (p.Acid != null) obj["acid"] = p.Acid;
|
||||||
if (p.ScaleId != null) obj["scale_id"] = p.ScaleId;
|
if (p.ScaleId != null) obj["scale_id"] = p.ScaleId;
|
||||||
@@ -818,8 +818,8 @@ namespace Elwig.Helpers.Export {
|
|||||||
Comment = p["comment"]?.AsValue().GetValue<string>(),
|
Comment = p["comment"]?.AsValue().GetValue<string>(),
|
||||||
IsSplCheck = p["spl_check"]?.AsValue().GetValue<bool>() ?? false,
|
IsSplCheck = p["spl_check"]?.AsValue().GetValue<bool>() ?? false,
|
||||||
IsHandPicked = p["hand_picked"]?.AsValue().GetValue<bool>(),
|
IsHandPicked = p["hand_picked"]?.AsValue().GetValue<bool>(),
|
||||||
IsLesewagen = p["lesewagen"]?.AsValue().GetValue<bool>(),
|
|
||||||
IsGebunden = p["gebunden"]?.AsValue().GetValue<bool>(),
|
IsGebunden = p["gebunden"]?.AsValue().GetValue<bool>(),
|
||||||
|
Unloading = p["unloading"]?.AsValue().GetValue<string>() ?? ((p["lesewagen"]?.AsValue().GetValue<bool>() ?? false) ? DeliveryPart.Pumped : null),
|
||||||
Temperature = p["temperature"]?.AsValue().GetValue<double>(),
|
Temperature = p["temperature"]?.AsValue().GetValue<double>(),
|
||||||
Acid = p["acid"]?.AsValue().GetValue<double>(),
|
Acid = p["acid"]?.AsValue().GetValue<double>(),
|
||||||
ScaleId = p["scale_id"]?.AsValue().GetValue<string>(),
|
ScaleId = p["scale_id"]?.AsValue().GetValue<string>(),
|
||||||
|
|||||||
69
Elwig/Helpers/Export/VCard.cs
Normal file
69
Elwig/Helpers/Export/VCard.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
using Elwig.Models.Entities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Elwig.Helpers.Export {
|
||||||
|
public class VCard : IExporter<Member> {
|
||||||
|
|
||||||
|
public static string FileExtension => "vcf";
|
||||||
|
|
||||||
|
private readonly StreamWriter _writer;
|
||||||
|
|
||||||
|
public VCard(string filename) : this(filename, Utils.UTF8) { }
|
||||||
|
|
||||||
|
public VCard(string filename, Encoding encoding) {
|
||||||
|
_writer = new StreamWriter(filename, false, encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() {
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
_writer.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueTask DisposeAsync() {
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
return _writer.DisposeAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ExportAsync(IEnumerable<Member> data, IProgress<double>? progress = null) {
|
||||||
|
progress?.Report(0.0);
|
||||||
|
int count = data.Count() + 1, i = 0;
|
||||||
|
|
||||||
|
foreach (var row in data) {
|
||||||
|
var billingAddr = row.BillingAddress != null ? $"ADR;TYPE=work;LANGUAGE=de;LABEL=\"{Escape(row.BillingAddress.FullName)}\\n{Escape(row.BillingAddress.Address)}\\n{row.BillingAddress.PostalDest.AtPlz?.Plz} {Escape(row.BillingAddress.PostalDest.AtPlz?.Ort.Name)}\\nÖsterreich\":;;{Escape(row.BillingAddress.Address)};{Escape(row.BillingAddress.PostalDest.AtPlz?.Ort.Name)};;{row.BillingAddress.PostalDest.AtPlz?.Plz};Österreich\r\n" : null;
|
||||||
|
var tel = string.Join("", row.TelephoneNumbers
|
||||||
|
.Where(n => n.Type != "fax")
|
||||||
|
.Select(n => $"TEL;TYPE={(n.Type == "mobile" ? "cell" : "voice")}:{Escape(n.Number)}\r\n"));
|
||||||
|
var email = string.Join("", row.EmailAddresses.Select(a => $"EMAIL:{Escape(a.Address)}\r\n"));
|
||||||
|
await _writer.WriteLineAsync($"""
|
||||||
|
BEGIN:VCARD
|
||||||
|
VERSION:4.0
|
||||||
|
UID:mg{row.MgNr}@{App.Client.NameToken.ToLower()}.elwig.at
|
||||||
|
NOTE:MgNr. {row.MgNr}
|
||||||
|
FN:{Escape(row.AdministrativeName)}
|
||||||
|
N:{Escape(row.Name)};{Escape(row.GivenName)};{Escape(row.MiddleName)};{Escape(row.Prefix)};{Escape(row.Suffix)}
|
||||||
|
KIND:{(row.IsJuridicalPerson ? "org" : "individual")}
|
||||||
|
ADR{(billingAddr == null ? "" : ";TYPE=home")};LANGUAGE=de;LABEL="{Escape(row.Address)}\n{row.PostalDest.AtPlz?.Plz} {Escape(row.PostalDest.AtPlz?.Ort.Name)}\nÖsterreich":;;{Escape(row.Address)};{Escape(row.PostalDest.AtPlz?.Ort.Name)};;{row.PostalDest.AtPlz?.Plz};Österreich
|
||||||
|
{billingAddr}{tel}{email}REV:{row.ModifiedAt.ToUniversalTime():yyyyMMdd\THHmmss\Z}
|
||||||
|
END:VCARD
|
||||||
|
""");
|
||||||
|
progress?.Report(100.0 * ++i / count);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _writer.FlushAsync();
|
||||||
|
progress?.Report(100.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Export(IEnumerable<Member> data, IProgress<double>? progress = null) {
|
||||||
|
ExportAsync(data, progress).GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? Escape(string? text) {
|
||||||
|
return text?.Replace("\\", "\\\\").Replace(",", "\\,").Replace(";", "\\;").Replace("\n", "\\n").Replace("\r", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
namespace Elwig.Helpers {
|
namespace Elwig.Helpers {
|
||||||
public enum ExportMode {
|
public enum ExportMode {
|
||||||
Show, SaveList, SavePdf, Print, Email, Export, Upload
|
Show, SaveList, SavePdf, Print, Email, Vcf, Export, Upload
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
|
using Microsoft.Data.Sqlite;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.IO.Hashing;
|
using System.IO.Hashing;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Elwig.Helpers {
|
namespace Elwig.Helpers {
|
||||||
static partial class Extensions {
|
public static partial class Extensions {
|
||||||
|
|
||||||
[LibraryImport("msvcrt.dll")]
|
[LibraryImport("msvcrt.dll")]
|
||||||
[UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
|
[UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
|
||||||
@@ -108,5 +111,39 @@ namespace Elwig.Helpers {
|
|||||||
throw new InvalidDataException($"CRC-32 mismatch in '{entry.FullName}'");
|
throw new InvalidDataException($"CRC-32 mismatch in '{entry.FullName}'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task ExecuteBatch(this SqliteConnection cnx, string sql) {
|
||||||
|
using var cmd = cnx.CreateCommand();
|
||||||
|
cmd.CommandText = sql;
|
||||||
|
using var reader = await cmd.ExecuteReaderAsync();
|
||||||
|
while (await reader.NextResultAsync()) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task ExecuteEmbeddedScript(this SqliteConnection cnx, Assembly asm, string name) {
|
||||||
|
using var stream = asm.GetManifestResourceStream(name) ?? throw new FileNotFoundException("Unable to load embedded resource");
|
||||||
|
using var reader = new StreamReader(stream);
|
||||||
|
await ExecuteBatch(cnx, await reader.ReadToEndAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<object?> ExecuteScalar(this SqliteConnection cnx, string sql) {
|
||||||
|
using var cmd = cnx.CreateCommand();
|
||||||
|
cmd.CommandText = sql;
|
||||||
|
return await cmd.ExecuteScalarAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<(string Table, long RowId, string Parent, long FkId)[]> ForeignKeyCheck(this SqliteConnection cnx) {
|
||||||
|
using var cmd = cnx.CreateCommand();
|
||||||
|
cmd.CommandText = "PRAGMA foreign_key_check";
|
||||||
|
using var reader = await cmd.ExecuteReaderAsync();
|
||||||
|
var list = new List<(string, long, string, long)>();
|
||||||
|
while (await reader.ReadAsync()) {
|
||||||
|
var table = reader.GetString(0);
|
||||||
|
var rowid = reader.GetInt64(1);
|
||||||
|
var parent = reader.GetString(2);
|
||||||
|
var fkid = reader.GetInt64(3);
|
||||||
|
list.Add((table, rowid, parent, fkid));
|
||||||
|
}
|
||||||
|
return [.. list];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,24 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
using Elwig.Windows;
|
using Elwig.Windows;
|
||||||
using System.Diagnostics;
|
using PdfiumViewer;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Windows;
|
using System.Diagnostics;
|
||||||
using System.Text.RegularExpressions;
|
using System.Drawing.Printing;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using PdfiumViewer;
|
using System.Reflection;
|
||||||
using System.Drawing.Printing;
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
namespace Elwig.Helpers.Printing {
|
namespace Elwig.Helpers.Printing {
|
||||||
public static class Pdf {
|
public static class Pdf {
|
||||||
|
|
||||||
private static readonly string WinziPrint = new string[] { App.InstallPath }
|
private static readonly string WinziPrint = (Assembly.GetEntryAssembly()?.Location.Contains(@"\bin\") ?? false) ?
|
||||||
|
Path.Combine(Assembly.GetEntryAssembly()!.Location.Split(@"\bin\")[0], "../Installer/Files/WinziPrint.exe") :
|
||||||
|
new string[] { App.InstallPath }
|
||||||
.Union(Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? [])
|
.Union(Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? [])
|
||||||
.Select(x => Path.Combine(x, "WinziPrint.exe"))
|
.Select(x => Path.Combine(x, "WinziPrint.exe"))
|
||||||
.Where(File.Exists)
|
.Where(File.Exists)
|
||||||
@@ -46,24 +50,45 @@ namespace Elwig.Helpers.Printing {
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<(int Pages, IEnumerable<int> PerDoc)> Convert(string htmlPath, string pdfPath, bool doublePaged = false, IProgress<double>? progress = null) {
|
public static async Task<(int Pages, IEnumerable<int> PerDoc)> Convert(string htmlPath, string pdfPath, bool doublePaged = false, CancellationToken? cancelToken = null, IProgress<double>? progress = null) {
|
||||||
return await Convert([htmlPath], pdfPath, doublePaged, progress);
|
return await Convert([htmlPath], pdfPath, doublePaged, cancelToken, progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<(int Pages, IEnumerable<int> PerDoc)> Convert(IEnumerable<string> htmlPath, string pdfPath, bool doublePaged = false, IProgress<double>? progress = null) {
|
public static async Task<(int Pages, IEnumerable<int> PerDoc)> Convert(IEnumerable<string> htmlPath, string pdfPath, bool doublePaged = false, CancellationToken? cancelToken = null, IProgress<double>? progress = null) {
|
||||||
if (WinziPrintProc == null) throw new InvalidOperationException("The WinziPrint process has not been initialized yet");
|
if (WinziPrintProc == null) throw new InvalidOperationException("The WinziPrint process has not been initialized yet");
|
||||||
progress?.Report(0.0);
|
progress?.Report(0.0);
|
||||||
using var client = new TcpClient("127.0.0.1", 30983);
|
using var client = new TcpClient("127.0.0.1", 30983);
|
||||||
using var stream = client.GetStream();
|
using var stream = client.GetStream();
|
||||||
|
string cnxId;
|
||||||
|
using var reader = new StreamReader(stream);
|
||||||
|
var first = await reader.ReadLineAsync() ?? throw new IOException("Invalid response from WinziPrint");
|
||||||
|
if (first.StartsWith("id:")) {
|
||||||
|
cnxId = first[3..].Trim();
|
||||||
|
} else {
|
||||||
|
throw new IOException("Invalid response from WinziPrint");
|
||||||
|
}
|
||||||
await stream.WriteAsync(Utils.UTF8.GetBytes(
|
await stream.WriteAsync(Utils.UTF8.GetBytes(
|
||||||
"-e utf-8;-p;" + (doublePaged ? "-2;" : "") +
|
"-e utf-8;-p;" + (doublePaged ? "-2;" : "") +
|
||||||
$"{string.Join(';', htmlPath)};{pdfPath}" +
|
$"{string.Join(';', htmlPath)};{pdfPath}" +
|
||||||
"\r\n"));
|
"\r\n"));
|
||||||
using var reader = new StreamReader(stream);
|
bool cancelled = false;
|
||||||
while (true) {
|
while (true) {
|
||||||
|
if (!cancelled && (cancelToken?.IsCancellationRequested ?? false)) {
|
||||||
|
try {
|
||||||
|
using var cancelClient = new TcpClient("127.0.0.1", 30983);
|
||||||
|
using var cancelStream = cancelClient.GetStream();
|
||||||
|
using var cancelReader = new StreamReader(cancelStream);
|
||||||
|
await cancelReader.ReadLineAsync();
|
||||||
|
await cancelStream.WriteAsync(Utils.UTF8.GetBytes($"cancel;{cnxId}\r\n"));
|
||||||
|
} catch { }
|
||||||
|
cancelled = true;
|
||||||
|
}
|
||||||
var line = await reader.ReadLineAsync() ?? throw new IOException("Invalid response from WinziPrint");
|
var line = await reader.ReadLineAsync() ?? throw new IOException("Invalid response from WinziPrint");
|
||||||
if (line.StartsWith("error:")) {
|
if (line.StartsWith("error:")) {
|
||||||
throw new IOException($"WinziPrint: {line[6..].Trim()}");
|
var msg = line[6..].Trim();
|
||||||
|
if (msg == "aborted")
|
||||||
|
throw new OperationCanceledException("Dokumentenerzeugung abgebrochen!");
|
||||||
|
throw new IOException($"WinziPrint: {msg}");
|
||||||
} else if (line.StartsWith("progress:")) {
|
} else if (line.StartsWith("progress:")) {
|
||||||
var parts = line[9..].Trim().Split('/').Select(int.Parse).ToArray();
|
var parts = line[9..].Trim().Split('/').Select(int.Parse).ToArray();
|
||||||
progress?.Report(100.0 * parts[0] / parts[1]);
|
progress?.Report(100.0 * parts[0] / parts[1]);
|
||||||
|
|||||||
60
Elwig/Helpers/SerialPortWatcher.cs
Normal file
60
Elwig/Helpers/SerialPortWatcher.cs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO.Ports;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Management;
|
||||||
|
|
||||||
|
namespace Elwig.Helpers {
|
||||||
|
public sealed class SerialPortWatcher : IDisposable {
|
||||||
|
|
||||||
|
private readonly ManagementEventWatcher _deviceArrivalWatcher;
|
||||||
|
private readonly ManagementEventWatcher _deviceRemovalWatcher;
|
||||||
|
|
||||||
|
private string[] _knownPorts;
|
||||||
|
|
||||||
|
public event EventHandler<string>? SerialPortConnected;
|
||||||
|
public event EventHandler<string>? SerialPortDisconnected;
|
||||||
|
|
||||||
|
public SerialPortWatcher() {
|
||||||
|
_knownPorts = SerialPort.GetPortNames();
|
||||||
|
_deviceArrivalWatcher = new ManagementEventWatcher("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2");
|
||||||
|
_deviceArrivalWatcher.EventArrived += OnDeviceArrived;
|
||||||
|
_deviceRemovalWatcher = new ManagementEventWatcher("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 3");
|
||||||
|
_deviceRemovalWatcher.EventArrived += OnDeviceRemoved;
|
||||||
|
_deviceArrivalWatcher.Start();
|
||||||
|
_deviceRemovalWatcher.Start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDeviceArrived(object sender, EventArrivedEventArgs evt) {
|
||||||
|
App.MainDispatcher.Invoke(() => {
|
||||||
|
// "synchronized"
|
||||||
|
string[] currentPorts = SerialPort.GetPortNames();
|
||||||
|
var newPorts = currentPorts.Except(_knownPorts).ToArray();
|
||||||
|
foreach (var port in newPorts)
|
||||||
|
SerialPortConnected?.Invoke(this, port);
|
||||||
|
_knownPorts = currentPorts;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDeviceRemoved(object sender, EventArrivedEventArgs evt) {
|
||||||
|
App.MainDispatcher.Invoke(() => {
|
||||||
|
// "synchronized"
|
||||||
|
string[] currentPorts = SerialPort.GetPortNames();
|
||||||
|
var removedPorts = _knownPorts.Except(currentPorts).ToArray();
|
||||||
|
foreach (var port in removedPorts)
|
||||||
|
SerialPortDisconnected?.Invoke(this, port);
|
||||||
|
_knownPorts = currentPorts;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() {
|
||||||
|
try {
|
||||||
|
_deviceArrivalWatcher?.Stop();
|
||||||
|
_deviceRemovalWatcher?.Stop();
|
||||||
|
} finally {
|
||||||
|
_deviceArrivalWatcher?.Dispose();
|
||||||
|
_deviceRemovalWatcher?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,6 +36,7 @@ namespace Elwig.Helpers {
|
|||||||
public static partial class Utils {
|
public static partial class Utils {
|
||||||
|
|
||||||
public static readonly Encoding UTF8 = new UTF8Encoding(false, true);
|
public static readonly Encoding UTF8 = new UTF8Encoding(false, true);
|
||||||
|
public static readonly Encoding UTF8BOM = new UTF8Encoding(true, true);
|
||||||
public static readonly JsonSerializerOptions JsonOpts = new() { Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping };
|
public static readonly JsonSerializerOptions JsonOpts = new() { Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping };
|
||||||
|
|
||||||
public static int CurrentYear => DateTime.Now.Year;
|
public static int CurrentYear => DateTime.Now.Year;
|
||||||
@@ -502,6 +503,7 @@ namespace Elwig.Helpers {
|
|||||||
if (App.Config.Smtp == null)
|
if (App.Config.Smtp == null)
|
||||||
return false;
|
return false;
|
||||||
return await Task.Run(async () => {
|
return await Task.Run(async () => {
|
||||||
|
await AddSentMailBody(subject, text, 1);
|
||||||
SmtpClient? client = null;
|
SmtpClient? client = null;
|
||||||
try {
|
try {
|
||||||
client = await GetSmtpClient();
|
client = await GetSmtpClient();
|
||||||
@@ -519,6 +521,11 @@ namespace Elwig.Helpers {
|
|||||||
}
|
}
|
||||||
msg.Body = body;
|
msg.Body = body;
|
||||||
await client!.SendAsync(msg);
|
await client!.SendAsync(msg);
|
||||||
|
await AddSentMails([(
|
||||||
|
"email", member.MgNr, member.AdministrativeName,
|
||||||
|
member.EmailAddresses.OrderBy(a => a.Nr).Select(a => a.Address).ToArray(),
|
||||||
|
subject, docs.Select(d => d.Title).ToArray()
|
||||||
|
)]);
|
||||||
} catch (Exception exc) {
|
} catch (Exception exc) {
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
return false;
|
return false;
|
||||||
@@ -533,15 +540,29 @@ namespace Elwig.Helpers {
|
|||||||
|
|
||||||
public static async Task ExportDocument(Document doc, ExportMode mode, string? filename = null, (Member Member, string Subject, string Text)? emailData = null) {
|
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 (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);
|
||||||
|
return;
|
||||||
|
}
|
||||||
await doc.Generate();
|
await doc.Generate();
|
||||||
await doc.Print();
|
await doc.Print();
|
||||||
} else if (mode == ExportMode.Email && emailData is (Member, string, string) e) {
|
} 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);
|
||||||
|
return;
|
||||||
|
}
|
||||||
await doc.Generate();
|
await doc.Generate();
|
||||||
var success = await SendEmail(e.Member, e.Subject, e.Text, [doc]);
|
var success = await SendEmail(e.Member, e.Subject, e.Text, [doc]);
|
||||||
if (success)
|
if (success)
|
||||||
MessageBox.Show("Die E-Mail wurde erfolgreich verschickt!", "E-Mail verschickt",
|
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);
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
} else if (mode == ExportMode.SavePdf) {
|
} 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);
|
||||||
|
}
|
||||||
var d = new SaveFileDialog() {
|
var d = new SaveFileDialog() {
|
||||||
FileName = $"{NormalizeFileName(filename ?? doc.Title)}.pdf",
|
FileName = $"{NormalizeFileName(filename ?? doc.Title)}.pdf",
|
||||||
DefaultExt = "pdf",
|
DefaultExt = "pdf",
|
||||||
@@ -654,9 +675,9 @@ namespace Elwig.Helpers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<string?> FindSentMailBody(DateTime target) {
|
public static async Task<string?> FindSentMailBody(DateTime target) {
|
||||||
var dt = $"{target:yyyy-MM-dd_HH-mm-ss}_";
|
var dt = $"{target:yyyy-MM-dd_HH-mm-ss}";
|
||||||
var filename = Directory.GetFiles(App.MailsPath, "????-??-??_??-??-??_*.txt")
|
var filename = Directory.GetFiles(App.MailsPath, "????-??-??_??-??-??_*.txt")
|
||||||
.Where(n => Path.GetFileName(n).CompareTo(dt) <= 0)
|
.Where(n => Path.GetFileName(n)[..19].CompareTo(dt) <= 0)
|
||||||
.Order()
|
.Order()
|
||||||
.LastOrDefault();
|
.LastOrDefault();
|
||||||
if (filename == null)
|
if (filename == null)
|
||||||
|
|||||||
@@ -15,15 +15,17 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
public bool IsReady { get; private set; }
|
public bool IsReady { get; private set; }
|
||||||
public bool HasFillingClearance { get; private set; }
|
public bool HasFillingClearance { get; private set; }
|
||||||
|
|
||||||
public event IEventScale.EventHandler<WeighingEventArgs> WeighingEvent;
|
public event IEventScale.EventHandler<WeighingEventArgs>? WeighingEvent;
|
||||||
|
|
||||||
private bool IsRunning = true;
|
private bool IsRunning = true;
|
||||||
private readonly Thread BackgroundThread;
|
private readonly Thread BackgroundThread;
|
||||||
|
private readonly string Connection;
|
||||||
|
|
||||||
public AveryEventScale(string id, string model, string cnx, string? empty = null, string? filling = null, int? limit = null, string? log = null) :
|
public AveryEventScale(string id, string model, string cnx, string? log = null, bool required = true) :
|
||||||
base(cnx, empty, filling, limit, log) {
|
base(cnx, null, null, null, log, true, !required) {
|
||||||
ScaleId = id;
|
ScaleId = id;
|
||||||
Model = model;
|
Model = model;
|
||||||
|
Connection = cnx;
|
||||||
IsReady = true;
|
IsReady = true;
|
||||||
HasFillingClearance = false;
|
HasFillingClearance = false;
|
||||||
Stream.WriteTimeout = -1;
|
Stream.WriteTimeout = -1;
|
||||||
@@ -50,19 +52,49 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
var data = await Receive();
|
var data = await Receive();
|
||||||
if (data != null)
|
if (data != null)
|
||||||
RaiseWeighingEvent(new WeighingEventArgs(data.Value));
|
RaiseWeighingEvent(new WeighingEventArgs(data.Value));
|
||||||
|
} catch (ThreadInterruptedException) {
|
||||||
|
// ignore
|
||||||
|
} catch (IOException) {
|
||||||
|
await Task.Delay(500);
|
||||||
|
await Reconnect();
|
||||||
|
} catch (TimeoutException) {
|
||||||
|
await Task.Delay(500);
|
||||||
|
await Reconnect();
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
MessageBox.Show($"Beim Wiegen ist ein Fehler Aufgetreten:\n\n{ex.Message}", "Waagenfehler",
|
MessageBox.Show($"Beim Wiegen ist ein Fehler Aufgetreten:\n\n{ex.Message} ({ex.GetType().Name})", "Waagenfehler",
|
||||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected async Task Reconnect() {
|
||||||
|
try { Reader.Close(); } catch { }
|
||||||
|
try { Stream.Close(); } catch { }
|
||||||
|
try { Serial?.Close(); } catch { }
|
||||||
|
while (IsRunning) {
|
||||||
|
try {
|
||||||
|
if (Connection.StartsWith("serial:")) {
|
||||||
|
Serial = Utils.OpenSerialConnection(Connection);
|
||||||
|
Stream = Serial.BaseStream;
|
||||||
|
} else if (Connection.StartsWith("tcp:")) {
|
||||||
|
Tcp = Utils.OpenTcpConnection(Connection);
|
||||||
|
Stream = Tcp.GetStream();
|
||||||
|
}
|
||||||
|
Reader = new(Stream, Encoding.ASCII, false, 512);
|
||||||
|
break;
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
await Task.Delay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected async Task<WeighingResult?> Receive() {
|
protected async Task<WeighingResult?> Receive() {
|
||||||
var line = "";
|
var line = "";
|
||||||
while (line.Length < 33) {
|
while (line.Length < 33) {
|
||||||
var ch = Reader.Read();
|
var ch = Reader.Read();
|
||||||
if (ch == -1) {
|
if (ch == -1) {
|
||||||
return null;
|
throw new IOException("Connection closed");
|
||||||
} else if (line.Length > 0 || ch == ' ') {
|
} else if (line.Length > 0 || ch == ' ') {
|
||||||
line += char.ToString((char)ch);
|
line += char.ToString((char)ch);
|
||||||
}
|
}
|
||||||
@@ -71,7 +103,7 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
if (line == null || line == "") {
|
if (line == null || line == "") {
|
||||||
return null;
|
return null;
|
||||||
} else if (line.Length != 33 || line[0] != ' ' || line[9] != ' ' || line[15] != ' ' || line[20] != ' ' || line[32] != ' ') {
|
} else if (line.Length != 33 || line[0] != ' ' || line[9] != ' ' || line[15] != ' ' || line[20] != ' ' || line[32] != ' ') {
|
||||||
throw new IOException($"Invalid event from scale: '{line}'");
|
throw new FormatException($"Invalid event from scale: '{line}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
var date = line[ 1.. 9];
|
var date = line[ 1.. 9];
|
||||||
@@ -81,7 +113,7 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
var unit = line[30..32];
|
var unit = line[30..32];
|
||||||
|
|
||||||
if (unit != "kg") {
|
if (unit != "kg") {
|
||||||
throw new IOException($"Unsupported unit in weighing event: '{unit}'");
|
throw new WeighingException($"Unsupported unit in weighing event: '{unit}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
identNr = identNr.Length > 0 && identNr != "0" ? identNr : null;
|
identNr = identNr.Length > 0 && identNr != "0" ? identNr : null;
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
var line = await Reader.ReadUntilAsync('\x03');
|
var line = await Reader.ReadUntilAsync('\x03');
|
||||||
if (LogPath != null) await File.AppendAllTextAsync(LogPath, $"{line}\r\n");
|
if (LogPath != null) await File.AppendAllTextAsync(LogPath, $"{line}\r\n");
|
||||||
if (line == null || line.Length < 4 || !line.StartsWith('\x02')) {
|
if (line == null || line.Length < 4 || !line.StartsWith('\x02')) {
|
||||||
throw new IOException("Invalid response from scale");
|
throw new FormatException("Invalid response from scale");
|
||||||
}
|
}
|
||||||
|
|
||||||
var status = line[1..3];
|
var status = line[1..3];
|
||||||
@@ -45,9 +45,9 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
switch (status[1]) {
|
switch (status[1]) {
|
||||||
case 'M': msg = "Waage in Bewegung"; break;
|
case 'M': msg = "Waage in Bewegung"; break;
|
||||||
}
|
}
|
||||||
throw new IOException($"Waagenfehler {status}: {msg}");
|
throw new WeighingException($"Waagenfehler {status}: {msg}");
|
||||||
} else if (status[0] != ' ') {
|
} else if (status[0] != ' ') {
|
||||||
throw new IOException($"Invalid response from scale (error code {status})");
|
throw new WeighingException($"Invalid response from scale (error code {status})");
|
||||||
}
|
}
|
||||||
|
|
||||||
return line[1..^1];
|
return line[1..^1];
|
||||||
@@ -57,7 +57,7 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
await SendCommand(incIdentNr ? '\x05' : '?');
|
await SendCommand(incIdentNr ? '\x05' : '?');
|
||||||
string record = await ReceiveResponse();
|
string record = await ReceiveResponse();
|
||||||
if (record.Length != 45)
|
if (record.Length != 45)
|
||||||
throw new IOException("Invalid response from scale: Received record has invalid size");
|
throw new FormatException("Invalid response from scale: Received record has invalid size");
|
||||||
var line = record[2..];
|
var line = record[2..];
|
||||||
|
|
||||||
var brutto = line[ 0.. 7].Trim();
|
var brutto = line[ 0.. 7].Trim();
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
using System.IO.Ports;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Ports;
|
||||||
|
using System.Net.Sockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
namespace Elwig.Helpers.Weighing {
|
namespace Elwig.Helpers.Weighing {
|
||||||
public abstract class Scale : IDisposable {
|
public abstract class Scale : IDisposable {
|
||||||
@@ -27,7 +28,7 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
if (config.Type == "SysTec-IT") {
|
if (config.Type == "SysTec-IT") {
|
||||||
return new SysTecITScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
|
return new SysTecITScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
|
||||||
} else if (config.Type == "Avery-Async") {
|
} else if (config.Type == "Avery-Async") {
|
||||||
return new AveryEventScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
|
return new AveryEventScale(config.Id, config.Model!, config.Connection!, config.Log, config.Required);
|
||||||
} else if (config.Type == "Gassner") {
|
} else if (config.Type == "Gassner") {
|
||||||
return new GassnerScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
|
return new GassnerScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
|
||||||
} else {
|
} else {
|
||||||
@@ -35,10 +36,17 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Scale(string cnx, string? empty, string? filling, int? limit, string? log) {
|
protected Scale(string cnx, string? empty, string? filling, int? limit, string? log, bool softFail = false, bool failSilent = false) {
|
||||||
if (cnx.StartsWith("serial:")) {
|
if (cnx.StartsWith("serial:")) {
|
||||||
|
try {
|
||||||
Serial = Utils.OpenSerialConnection(cnx);
|
Serial = Utils.OpenSerialConnection(cnx);
|
||||||
Stream = Serial.BaseStream;
|
} catch (Exception e) {
|
||||||
|
if (!softFail) throw;
|
||||||
|
if (!failSilent)
|
||||||
|
MessageBox.Show($"Verbindung zu Waage konnte nicht hergestellt werden:\n\n{e.Message}", "Waagenfehler",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
|
}
|
||||||
|
Stream = Serial?.BaseStream ?? Stream.Null;
|
||||||
} else if (cnx.StartsWith("tcp:")) {
|
} else if (cnx.StartsWith("tcp:")) {
|
||||||
Tcp = Utils.OpenTcpConnection(cnx);
|
Tcp = Utils.OpenTcpConnection(cnx);
|
||||||
Stream = Tcp.GetStream();
|
Stream = Tcp.GetStream();
|
||||||
|
|||||||
@@ -34,14 +34,14 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
var line = await Reader.ReadUntilAsync("\r\n");
|
var line = await Reader.ReadUntilAsync("\r\n");
|
||||||
if (LogPath != null) await File.AppendAllTextAsync(LogPath, line);
|
if (LogPath != null) await File.AppendAllTextAsync(LogPath, line);
|
||||||
if (line == null || line.Length < 4 || !line.StartsWith('<') || !line.EndsWith(">\r\n")) {
|
if (line == null || line.Length < 4 || !line.StartsWith('<') || !line.EndsWith(">\r\n")) {
|
||||||
throw new IOException("Invalid response from scale");
|
throw new FormatException("Invalid response from scale");
|
||||||
}
|
}
|
||||||
|
|
||||||
var error = line[1..3];
|
var error = line[1..3];
|
||||||
string msg = $"Unbekannter Fehler (Fehler code {error})";
|
string msg = $"Unbekannter Fehler (Fehler code {error})";
|
||||||
if (error[0] == '0') {
|
if (error[0] == '0') {
|
||||||
if (error[1] != '0') {
|
if (error[1] != '0') {
|
||||||
throw new IOException($"Invalid response from scale (error code {error})");
|
throw new WeighingException($"Invalid response from scale (error code {error})");
|
||||||
}
|
}
|
||||||
} else if (error[0] == '1') {
|
} else if (error[0] == '1') {
|
||||||
switch (error[1]) {
|
switch (error[1]) {
|
||||||
@@ -52,21 +52,21 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
case '6': msg = "Drucker nicht bereit"; break;
|
case '6': msg = "Drucker nicht bereit"; break;
|
||||||
case '7': msg = "Druckmuster enthält ungültiges Kommando"; break;
|
case '7': msg = "Druckmuster enthält ungültiges Kommando"; break;
|
||||||
}
|
}
|
||||||
throw new IOException($"Waagenfehler {error}: {msg}");
|
throw new WeighingException($"Waagenfehler {error}: {msg}");
|
||||||
} else if (error[0] == '2') {
|
} else if (error[0] == '2') {
|
||||||
switch (error[1]) {
|
switch (error[1]) {
|
||||||
case '0': msg = "Brutto negativ"; break;
|
case '0': msg = "Brutto negativ"; break;
|
||||||
}
|
}
|
||||||
throw new IOException($"Fehler {error}: {msg}");
|
throw new WeighingException($"Fehler {error}: {msg}");
|
||||||
} else if (error[0] == '3') {
|
} else if (error[0] == '3') {
|
||||||
switch (error[1]) {
|
switch (error[1]) {
|
||||||
case '1': msg = "Übertragunsfehler"; break;
|
case '1': msg = "Übertragunsfehler"; break;
|
||||||
case '2': msg = "Ungültiger Befehl"; break;
|
case '2': msg = "Ungültiger Befehl"; break;
|
||||||
case '3': msg = "Ungültiger Parameter"; break;
|
case '3': msg = "Ungültiger Parameter"; break;
|
||||||
}
|
}
|
||||||
throw new IOException($"Kommunikationsfehler {error}: {msg}");
|
throw new WeighingException($"Kommunikationsfehler {error}: {msg}");
|
||||||
} else {
|
} else {
|
||||||
throw new IOException($"Invalid response from scale (error code {error})");
|
throw new WeighingException($"Invalid response from scale (error code {error})");
|
||||||
}
|
}
|
||||||
|
|
||||||
return line[1..^3];
|
return line[1..^3];
|
||||||
@@ -76,7 +76,7 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
await SendCommand(incIdentNr ? $"RN{InternalScaleNr}" : $"RM{InternalScaleNr}");
|
await SendCommand(incIdentNr ? $"RN{InternalScaleNr}" : $"RM{InternalScaleNr}");
|
||||||
string record = await ReceiveResponse();
|
string record = await ReceiveResponse();
|
||||||
if (record.Length != 62)
|
if (record.Length != 62)
|
||||||
throw new IOException("Invalid response from scale: Received record has invalid size");
|
throw new FormatException("Invalid response from scale: Received record has invalid size");
|
||||||
var line = record[2..];
|
var line = record[2..];
|
||||||
|
|
||||||
var status = line[ 0.. 2];
|
var status = line[ 0.. 2];
|
||||||
@@ -94,9 +94,9 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
var crc16 = line[52..60].Trim();
|
var crc16 = line[52..60].Trim();
|
||||||
|
|
||||||
if (Utils.CalcCrc16Modbus(record[..54]) != ushort.Parse(crc16)) {
|
if (Utils.CalcCrc16Modbus(record[..54]) != ushort.Parse(crc16)) {
|
||||||
throw new IOException($"Invalid response from scale: Invalid CRC16 checksum ({crc16} != {Utils.CalcCrc16Modbus(record[..54])})");
|
throw new WeighingException($"Invalid response from scale: Invalid CRC16 checksum ({crc16} != {Utils.CalcCrc16Modbus(record[..54])})");
|
||||||
} else if (unit != "kg") {
|
} else if (unit != "kg") {
|
||||||
throw new IOException($"Unsupported unit in weighing response: '{unit}'");
|
throw new WeighingException($"Unsupported unit in weighing response: '{unit}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
identNr = identNr.Length > 0 && identNr != "0" ? identNr : null;
|
identNr = identNr.Length > 0 && identNr != "0" ? identNr : null;
|
||||||
|
|||||||
6
Elwig/Helpers/Weighing/WeighingException.cs
Normal file
6
Elwig/Helpers/Weighing/WeighingException.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Elwig.Helpers.Weighing {
|
||||||
|
public class WeighingException(string? message = null) : Exception(message) {
|
||||||
|
}
|
||||||
|
}
|
||||||
59
Elwig/Models/Dtos/MemberDeliveryData.cs
Normal file
59
Elwig/Models/Dtos/MemberDeliveryData.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
using Elwig.Models.Entities;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Elwig.Models.Dtos {
|
||||||
|
public class MemberDeliveryData : DataTable<MemberDeliveryRow> {
|
||||||
|
|
||||||
|
private static readonly (string, string, string?, int?)[] FieldNames = [
|
||||||
|
("MgNr", "MgNr.", null, 12),
|
||||||
|
("Name1", "Name", null, 40),
|
||||||
|
("Name2", "Vorname", null, 40),
|
||||||
|
("Address", "Adresse", null, 60),
|
||||||
|
("Plz", "PLZ", null, 10),
|
||||||
|
("Locality", "Ort", null, 60),
|
||||||
|
("Weight", "Geliefert", "kg", 22),
|
||||||
|
];
|
||||||
|
|
||||||
|
public MemberDeliveryData(IEnumerable<MemberDeliveryRow> rows, List<string> filterNames) :
|
||||||
|
base("Liefermengen Gesamt", "Liefermengen pro Mitglied", string.Join(" / ", filterNames), rows, FieldNames) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<MemberDeliveryData> FromQuery(IQueryable<DeliveryPart> query, List<string> filterNames) {
|
||||||
|
return new((await query
|
||||||
|
.GroupBy(p => new {
|
||||||
|
p.Delivery.MgNr,
|
||||||
|
p.Delivery.Member.Name,
|
||||||
|
p.Delivery.Member.GivenName,
|
||||||
|
p.Delivery.Member.Address,
|
||||||
|
p.Delivery.Member.PostalDest.AtPlz!.Plz,
|
||||||
|
Ort = p.Delivery.Member.PostalDest.AtPlz!.Ort.Name,
|
||||||
|
})
|
||||||
|
.Select(g => new {
|
||||||
|
g.Key,
|
||||||
|
Weight = g.Sum(p => p.Weight),
|
||||||
|
}).ToListAsync())
|
||||||
|
.Select(g => new MemberDeliveryRow {
|
||||||
|
MgNr = g.Key.MgNr,
|
||||||
|
Name1 = g.Key.Name,
|
||||||
|
Name2 = g.Key.GivenName,
|
||||||
|
Address = g.Key.Address,
|
||||||
|
Plz = g.Key.Plz,
|
||||||
|
Locality = g.Key.Ort,
|
||||||
|
Weight = g.Weight,
|
||||||
|
}), filterNames);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MemberDeliveryRow {
|
||||||
|
public required int MgNr;
|
||||||
|
public required string Name1;
|
||||||
|
public required string? Name2;
|
||||||
|
public required string Address;
|
||||||
|
public required int Plz;
|
||||||
|
public required string Locality;
|
||||||
|
public required int Weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
|
using Elwig.Models.Entities;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Elwig.Models.Dtos {
|
namespace Elwig.Models.Dtos {
|
||||||
public class MemberDeliveryPerVarietyData : DataTable<MemberDeliveryPerVariantRow> {
|
public class MemberDeliveryPerVarietyData : DataTable<MemberDeliveryPerVarietyRow> {
|
||||||
|
|
||||||
private static readonly (string, string, string?, int)[] FieldNames = [
|
private static readonly (string, string, string?, int?)[] FieldNames = [
|
||||||
("MgNr", "MgNr.", null, 12),
|
("MgNr", "MgNr.", null, 12),
|
||||||
("Name1", "Name", null, 40),
|
("Name1", "Name", null, 40),
|
||||||
("Name2", "Vorname", null, 40),
|
("Name2", "Vorname", null, 40),
|
||||||
@@ -16,104 +16,63 @@ namespace Elwig.Models.Dtos {
|
|||||||
("Locality", "Ort", null, 60),
|
("Locality", "Ort", null, 60),
|
||||||
("SortIds", "Sorte", null, 12),
|
("SortIds", "Sorte", null, 12),
|
||||||
("AttrIds", "Attribut", null, 16),
|
("AttrIds", "Attribut", null, 16),
|
||||||
|
("CultIds", "Bewirt.", null, 16),
|
||||||
("Weights", "Geliefert", "kg", 22),
|
("Weights", "Geliefert", "kg", 22),
|
||||||
("Areas", "Fläche", "m²", 22),
|
|
||||||
("Yields", "Ertrag", "kg/ha", 22),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
public MemberDeliveryPerVarietyData(IEnumerable<MemberDeliveryPerVariantRow> rows, int year) :
|
public MemberDeliveryPerVarietyData(IEnumerable<MemberDeliveryPerVarietyRow> rows, List<string> filterNames) :
|
||||||
base($"Liefermengen", $"Liefermengen pro Mitglied, Sorte und Attribut {year}", rows, FieldNames) {
|
base("Liefermengen pro Sorte", "Liefermengen pro Mitglied, Sorte, Attribut und Bewirtschaftungsart", string.Join(" / ", filterNames), rows, FieldNames) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<MemberDeliveryPerVarietyData> ForSeason(DbSet<MemberDeliveryPerVariantRowSingle> table, int year) {
|
public static async Task<MemberDeliveryPerVarietyData> FromQuery(IQueryable<DeliveryPart> query, List<string> filterNames) {
|
||||||
return new MemberDeliveryPerVarietyData(
|
return new((await query
|
||||||
(await FromDbSet(table, year)).GroupBy(
|
.GroupBy(p => new {
|
||||||
r => r.MgNr,
|
p.Delivery.MgNr,
|
||||||
(k, g) => new MemberDeliveryPerVariantRow(g)
|
p.Delivery.Member.Name,
|
||||||
), year);
|
p.Delivery.Member.GivenName,
|
||||||
}
|
p.Delivery.Member.Address,
|
||||||
|
p.Delivery.Member.PostalDest.AtPlz!.Plz,
|
||||||
private static async Task<IEnumerable<MemberDeliveryPerVariantRowSingle>> FromDbSet(DbSet<MemberDeliveryPerVariantRowSingle> table, int year) {
|
Ort = p.Delivery.Member.PostalDest.AtPlz!.Ort.Name,
|
||||||
return await table.FromSql($"""
|
p.SortId,
|
||||||
SELECT m.mgnr, m.name AS name_1,
|
p.AttrId,
|
||||||
COALESCE(m.prefix || ' ', '') || m.given_name ||
|
p.CultId,
|
||||||
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
|
})
|
||||||
p.plz, o.name AS ort, m.address,
|
.Select(g => new {
|
||||||
v.bucket, v.weight, v.area
|
g.Key,
|
||||||
FROM (
|
Weight = g.Sum(p => p.Weight),
|
||||||
SELECT c.year AS year,
|
})
|
||||||
c.mgnr AS mgnr,
|
.ToListAsync()).GroupBy(g => new {
|
||||||
c.bucket AS bucket,
|
g.Key.MgNr,
|
||||||
COALESCE(d.weight, 0) AS weight,
|
g.Key.Name,
|
||||||
COALESCE(c.area, 0) AS area
|
g.Key.GivenName,
|
||||||
FROM v_area_commitment_bucket_strict c
|
g.Key.Address,
|
||||||
LEFT JOIN v_delivery_bucket_strict d ON (d.year, d.mgnr, d.bucket) = (c.year, c.mgnr, c.bucket)
|
g.Key.Plz,
|
||||||
WHERE c.year = {year}
|
g.Key.Ort,
|
||||||
UNION
|
}).Select(g => new MemberDeliveryPerVarietyRow {
|
||||||
SELECT d.year,
|
MgNr = g.Key.MgNr,
|
||||||
d.mgnr,
|
Name1 = g.Key.Name,
|
||||||
d.bucket,
|
Name2 = g.Key.GivenName,
|
||||||
COALESCE(d.weight, 0),
|
Address = g.Key.Address,
|
||||||
COALESCE(c.area, 0)
|
Plz = g.Key.Plz,
|
||||||
FROM v_delivery_bucket_strict d
|
Locality = g.Key.Ort,
|
||||||
LEFT JOIN v_area_commitment_bucket_strict c ON (c.year, c.mgnr, c.bucket) = (d.year, d.mgnr, d.bucket)
|
SortIds = [.. g.Select(d => d.Key.SortId)],
|
||||||
WHERE d.year = {year}
|
AttrIds = [.. g.Select(d => d.Key.AttrId)],
|
||||||
) v
|
CultIds = [.. g.Select(d => d.Key.CultId)],
|
||||||
LEFT JOIN member m ON m.mgnr = v.mgnr
|
Weights = [.. g.Select(d => d.Weight)],
|
||||||
LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
|
}), filterNames);
|
||||||
LEFT JOIN AT_ort o ON o.okz = p.okz
|
|
||||||
ORDER BY m.mgnr, v.bucket
|
|
||||||
""").ToListAsync();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MemberDeliveryPerVariantRow {
|
public class MemberDeliveryPerVarietyRow {
|
||||||
public int MgNr;
|
public required int MgNr;
|
||||||
public string Name1;
|
public required string Name1;
|
||||||
public string? Name2;
|
public required string? Name2;
|
||||||
public string Address;
|
public required string Address;
|
||||||
public int Plz;
|
public required int Plz;
|
||||||
public string Locality;
|
public required string Locality;
|
||||||
public string[] SortIds;
|
public required string[] SortIds;
|
||||||
public string[] AttrIds;
|
public required string?[] AttrIds;
|
||||||
public int[] Areas;
|
public required string?[] CultIds;
|
||||||
public int[] Weights;
|
public required int[] Weights;
|
||||||
public int?[] Yields => Weights.Zip(Areas).Select(i => i.Second > 0 ? (int?)i.First * 10_000 / i.Second : null).ToArray();
|
|
||||||
|
|
||||||
public MemberDeliveryPerVariantRow(IEnumerable<MemberDeliveryPerVariantRowSingle> rows) {
|
|
||||||
var f = rows.First();
|
|
||||||
MgNr = f.MgNr;
|
|
||||||
Name1 = f.Name1;
|
|
||||||
Name2 = f.Name2;
|
|
||||||
Address = f.Address;
|
|
||||||
Plz = f.Plz;
|
|
||||||
Locality = f.Locality.Split(",")[0];
|
|
||||||
SortIds = rows.Select(r => r.VtrgId[..2]).ToArray();
|
|
||||||
AttrIds = rows.Select(r => r.VtrgId[2..]).ToArray();
|
|
||||||
Areas = rows.Select(r => r.Area).ToArray();
|
|
||||||
Weights = rows.Select(r => r.Weight).ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Keyless]
|
|
||||||
public class MemberDeliveryPerVariantRowSingle {
|
|
||||||
[Column("mgnr")]
|
|
||||||
public int MgNr { get; set; }
|
|
||||||
[Column("name_1")]
|
|
||||||
public required string Name1 { get; set; }
|
|
||||||
[Column("name_2")]
|
|
||||||
public string? Name2 { get; set; }
|
|
||||||
[Column("address")]
|
|
||||||
public required string Address { get; set; }
|
|
||||||
[Column("plz")]
|
|
||||||
public int Plz { get; set; }
|
|
||||||
[Column("ort")]
|
|
||||||
public required string Locality { get; set; }
|
|
||||||
[Column("bucket")]
|
|
||||||
public required string VtrgId { get; set; }
|
|
||||||
[Column("area")]
|
|
||||||
public int Area { get; set; }
|
|
||||||
[Column("weight")]
|
|
||||||
public int Weight { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
119
Elwig/Models/Dtos/MemberDeliveryYieldsPerVarietyData.cs
Normal file
119
Elwig/Models/Dtos/MemberDeliveryYieldsPerVarietyData.cs
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Elwig.Models.Dtos {
|
||||||
|
public class MemberDeliveryYieldsPerVarietyData : DataTable<MemberDeliveryYieldsPerVarietyRow> {
|
||||||
|
|
||||||
|
private static readonly (string, string, string?, int)[] FieldNames = [
|
||||||
|
("MgNr", "MgNr.", null, 12),
|
||||||
|
("Name1", "Name", null, 40),
|
||||||
|
("Name2", "Vorname", null, 40),
|
||||||
|
("Address", "Adresse", null, 60),
|
||||||
|
("Plz", "PLZ", null, 10),
|
||||||
|
("Locality", "Ort", null, 60),
|
||||||
|
("SortIds", "Sorte", null, 12),
|
||||||
|
("AttrIds", "Attribut", null, 16),
|
||||||
|
("Weights", "Geliefert", "kg", 22),
|
||||||
|
("Areas", "Fläche", "m²", 22),
|
||||||
|
("Yields", "Ertrag", "kg/ha", 22),
|
||||||
|
];
|
||||||
|
|
||||||
|
public MemberDeliveryYieldsPerVarietyData(IEnumerable<MemberDeliveryYieldsPerVarietyRow> rows, int year) :
|
||||||
|
base($"Liefermengen", $"Liefermengen pro Mitglied, Sorte und Attribut {year}", rows, FieldNames) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<MemberDeliveryYieldsPerVarietyData> ForSeason(DbSet<MemberDeliveryPerVarietyRowSingle> table, int year) {
|
||||||
|
return new MemberDeliveryYieldsPerVarietyData(
|
||||||
|
(await FromDbSet(table, year)).GroupBy(
|
||||||
|
r => r.MgNr,
|
||||||
|
(k, g) => new MemberDeliveryYieldsPerVarietyRow(g)
|
||||||
|
), year);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IEnumerable<MemberDeliveryPerVarietyRowSingle>> FromDbSet(DbSet<MemberDeliveryPerVarietyRowSingle> table, int year) {
|
||||||
|
return await table.FromSql($"""
|
||||||
|
SELECT m.mgnr, m.name AS name_1,
|
||||||
|
COALESCE(m.prefix || ' ', '') || m.given_name ||
|
||||||
|
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
|
||||||
|
p.plz, o.name AS ort, m.address,
|
||||||
|
v.bucket, v.weight, v.area
|
||||||
|
FROM (
|
||||||
|
SELECT c.year AS year,
|
||||||
|
c.mgnr AS mgnr,
|
||||||
|
c.bucket AS bucket,
|
||||||
|
COALESCE(d.weight, 0) AS weight,
|
||||||
|
COALESCE(c.area, 0) AS area
|
||||||
|
FROM v_area_commitment_bucket_strict c
|
||||||
|
LEFT JOIN v_delivery_bucket_strict d ON (d.year, d.mgnr, d.bucket) = (c.year, c.mgnr, c.bucket)
|
||||||
|
WHERE c.year = {year}
|
||||||
|
UNION
|
||||||
|
SELECT d.year,
|
||||||
|
d.mgnr,
|
||||||
|
d.bucket,
|
||||||
|
COALESCE(d.weight, 0),
|
||||||
|
COALESCE(c.area, 0)
|
||||||
|
FROM v_delivery_bucket_strict d
|
||||||
|
LEFT JOIN v_area_commitment_bucket_strict c ON (c.year, c.mgnr, c.bucket) = (d.year, d.mgnr, d.bucket)
|
||||||
|
WHERE d.year = {year}
|
||||||
|
) v
|
||||||
|
LEFT JOIN member m ON m.mgnr = v.mgnr
|
||||||
|
LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
|
||||||
|
LEFT JOIN AT_ort o ON o.okz = p.okz
|
||||||
|
ORDER BY m.mgnr, v.bucket
|
||||||
|
""").ToListAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MemberDeliveryYieldsPerVarietyRow {
|
||||||
|
public int MgNr;
|
||||||
|
public string Name1;
|
||||||
|
public string? Name2;
|
||||||
|
public string Address;
|
||||||
|
public int Plz;
|
||||||
|
public string Locality;
|
||||||
|
public string[] SortIds;
|
||||||
|
public string[] AttrIds;
|
||||||
|
public int[] Areas;
|
||||||
|
public int[] Weights;
|
||||||
|
public int?[] Yields => Weights.Zip(Areas).Select(i => i.Second > 0 ? (int?)i.First * 10_000 / i.Second : null).ToArray();
|
||||||
|
|
||||||
|
public MemberDeliveryYieldsPerVarietyRow(IEnumerable<MemberDeliveryPerVarietyRowSingle> rows) {
|
||||||
|
var f = rows.First();
|
||||||
|
MgNr = f.MgNr;
|
||||||
|
Name1 = f.Name1;
|
||||||
|
Name2 = f.Name2;
|
||||||
|
Address = f.Address;
|
||||||
|
Plz = f.Plz;
|
||||||
|
Locality = f.Locality.Split(",")[0];
|
||||||
|
SortIds = rows.Select(r => r.VtrgId[..2]).ToArray();
|
||||||
|
AttrIds = rows.Select(r => r.VtrgId[2..]).ToArray();
|
||||||
|
Areas = rows.Select(r => r.Area).ToArray();
|
||||||
|
Weights = rows.Select(r => r.Weight).ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Keyless]
|
||||||
|
public class MemberDeliveryPerVarietyRowSingle {
|
||||||
|
[Column("mgnr")]
|
||||||
|
public int MgNr { get; set; }
|
||||||
|
[Column("name_1")]
|
||||||
|
public required string Name1 { get; set; }
|
||||||
|
[Column("name_2")]
|
||||||
|
public string? Name2 { get; set; }
|
||||||
|
[Column("address")]
|
||||||
|
public required string Address { get; set; }
|
||||||
|
[Column("plz")]
|
||||||
|
public int Plz { get; set; }
|
||||||
|
[Column("ort")]
|
||||||
|
public required string Locality { get; set; }
|
||||||
|
[Column("bucket")]
|
||||||
|
public required string VtrgId { get; set; }
|
||||||
|
[Column("area")]
|
||||||
|
public int Area { get; set; }
|
||||||
|
[Column("weight")]
|
||||||
|
public int Weight { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,11 @@ using System.Text.Json.Nodes;
|
|||||||
namespace Elwig.Models.Entities {
|
namespace Elwig.Models.Entities {
|
||||||
[Table("delivery_part"), PrimaryKey("Year", "DId", "DPNr")]
|
[Table("delivery_part"), PrimaryKey("Year", "DId", "DPNr")]
|
||||||
public class DeliveryPart : IDelivery {
|
public class DeliveryPart : IDelivery {
|
||||||
|
|
||||||
|
public const string Dumper = "dumper";
|
||||||
|
public const string Pumped = "pumped";
|
||||||
|
public const string Box = "box";
|
||||||
|
|
||||||
[Column("year")]
|
[Column("year")]
|
||||||
public int Year { get; set; }
|
public int Year { get; set; }
|
||||||
|
|
||||||
@@ -86,12 +91,27 @@ namespace Elwig.Models.Entities {
|
|||||||
[Column("hand_picked")]
|
[Column("hand_picked")]
|
||||||
public bool? IsHandPicked { get; set; }
|
public bool? IsHandPicked { get; set; }
|
||||||
|
|
||||||
[Column("lesewagen")]
|
|
||||||
public bool? IsLesewagen { get; set; }
|
|
||||||
|
|
||||||
[Column("gebunden")]
|
[Column("gebunden")]
|
||||||
public bool? IsGebunden { get; set; }
|
public bool? IsGebunden { get; set; }
|
||||||
|
|
||||||
|
[Column("unloading")]
|
||||||
|
public string? Unloading { get; set; }
|
||||||
|
[NotMapped]
|
||||||
|
public bool IsDumper {
|
||||||
|
get => Unloading == Dumper;
|
||||||
|
set => Unloading = value ? Dumper : Unloading;
|
||||||
|
}
|
||||||
|
[NotMapped]
|
||||||
|
public bool IsPumped {
|
||||||
|
get => Unloading == Pumped;
|
||||||
|
set => Unloading = value ? Pumped : Unloading;
|
||||||
|
}
|
||||||
|
[NotMapped]
|
||||||
|
public bool IsBox {
|
||||||
|
get => Unloading == Box;
|
||||||
|
set => Unloading = value ? Box : Unloading;
|
||||||
|
}
|
||||||
|
|
||||||
[Column("temperature")]
|
[Column("temperature")]
|
||||||
public double? Temperature { get; set; }
|
public double? Temperature { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ namespace Elwig.Models.Entities {
|
|||||||
[Column("active")]
|
[Column("active")]
|
||||||
public bool IsActive { get; set; }
|
public bool IsActive { get; set; }
|
||||||
|
|
||||||
|
[Column("redacted")]
|
||||||
|
public bool IsRedacted { get; set; }
|
||||||
|
|
||||||
[Column("ordering")]
|
[Column("ordering")]
|
||||||
public int Ordering { get; set; }
|
public int Ordering { get; set; }
|
||||||
|
|
||||||
@@ -46,6 +49,8 @@ namespace Elwig.Models.Entities {
|
|||||||
(Rel != null) ? $"{Utils.GetSign(Rel.Value)}{(Math.Abs(Rel.Value) < 0.1m ? "\u2007" : "")}{Math.Abs(Rel.Value):0.00##\u00a0%}" :
|
(Rel != null) ? $"{Utils.GetSign(Rel.Value)}{(Math.Abs(Rel.Value) < 0.1m ? "\u2007" : "")}{Math.Abs(Rel.Value):0.00##\u00a0%}" :
|
||||||
"";
|
"";
|
||||||
|
|
||||||
|
public string? PublicValueStr => IsRedacted ? null : ValueStr;
|
||||||
|
|
||||||
public override string ToString() {
|
public override string ToString() {
|
||||||
return Name;
|
return Name;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
|||||||
<PublishDir>bin\Publish</PublishDir>
|
<PublishDir>bin\Publish</PublishDir>
|
||||||
<PublishProtocol>FileSystem</PublishProtocol>
|
<PublishProtocol>FileSystem</PublishProtocol>
|
||||||
<_TargetId>Folder</_TargetId>
|
<_TargetId>Folder</_TargetId>
|
||||||
<TargetFramework>net8.0-windows</TargetFramework>
|
<TargetFramework>net10.0-windows</TargetFramework>
|
||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
<SelfContained>true</SelfContained>
|
<SelfContained>true</SelfContained>
|
||||||
<PublishSingleFile>false</PublishSingleFile>
|
<PublishSingleFile>false</PublishSingleFile>
|
||||||
|
|||||||
3
Elwig/Resources/Sql/33-34.sql
Normal file
3
Elwig/Resources/Sql/33-34.sql
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
-- schema version 33 to 34
|
||||||
|
|
||||||
|
ALTER TABLE modifier ADD COLUMN redacted INTEGER NOT NULL CHECK (redacted IN (TRUE, FALSE)) DEFAULT FALSE;
|
||||||
6
Elwig/Resources/Sql/34-35.sql
Normal file
6
Elwig/Resources/Sql/34-35.sql
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
-- schema version 34 to 35
|
||||||
|
|
||||||
|
ALTER TABLE delivery_part ADD COLUMN unloading TEXT DEFAULT NULL;
|
||||||
|
UPDATE delivery_part SET unloading = 'pumped' WHERE lesewagen;
|
||||||
|
UPDATE delivery_part SET unloading = 'box' WHERE (SELECT zwstid IN ('H','S') FROM delivery d WHERE (d.year, d.did) = (delivery_part.year, delivery_part.did));
|
||||||
|
ALTER TABLE delivery_part DROP COLUMN lesewagen;
|
||||||
19
Elwig/Resources/Sql/35-36.sql
Normal file
19
Elwig/Resources/Sql/35-36.sql
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
-- schema version 35 to 36
|
||||||
|
|
||||||
|
CREATE VIEW v_member AS
|
||||||
|
SELECT m.mgnr, m.name,
|
||||||
|
(COALESCE(m.prefix || ' ', '') || m.given_name || COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '')) AS other_names,
|
||||||
|
m.address, p.plz, o.name AS locality,
|
||||||
|
a.name AS billing_name, a.address AS billing_address, p2.plz AS billing_plz, o2.name AS billing_locality,
|
||||||
|
k.name AS default_kg,
|
||||||
|
GROUP_CONCAT(e.address, ', ') AS email_addresses
|
||||||
|
FROM 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 member_billing_address a ON a.mgnr = m.mgnr
|
||||||
|
LEFT JOIN AT_plz_dest p2 ON p2.id = a.postal_dest
|
||||||
|
LEFT JOIN AT_ort o2 ON o2.okz = p2.okz
|
||||||
|
LEFT JOIN AT_kg k ON k.kgnr = m.default_kgnr
|
||||||
|
LEFT JOIN member_email_address e ON e.mgnr = m.mgnr
|
||||||
|
GROUP BY m.mgnr
|
||||||
|
ORDER BY m.mgnr;
|
||||||
@@ -15,10 +15,8 @@ using LinqKit;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.IO;
|
|
||||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Net.Http;
|
|
||||||
|
|
||||||
namespace Elwig.Services {
|
namespace Elwig.Services {
|
||||||
public static class DeliveryService {
|
public static class DeliveryService {
|
||||||
@@ -80,9 +78,9 @@ namespace Elwig.Services {
|
|||||||
vm.PartComment = p.Comment ?? "";
|
vm.PartComment = p.Comment ?? "";
|
||||||
vm.TemperatureString = (p.Temperature != null) ? $"{p.Temperature:N1}" : "";
|
vm.TemperatureString = (p.Temperature != null) ? $"{p.Temperature:N1}" : "";
|
||||||
vm.AcidString = (p.Acid != null) ? $"{p.Acid:N1}" : "";
|
vm.AcidString = (p.Acid != null) ? $"{p.Acid:N1}" : "";
|
||||||
vm.IsLesewagen = p.IsLesewagen ?? false;
|
|
||||||
vm.IsHandPicked = p.IsHandPicked;
|
vm.IsHandPicked = p.IsHandPicked;
|
||||||
vm.IsGebunden = p.IsGebunden;
|
vm.IsGebunden = p.IsGebunden;
|
||||||
|
vm.Unloading = p.Unloading;
|
||||||
|
|
||||||
vm.ScaleId = p.ScaleId;
|
vm.ScaleId = p.ScaleId;
|
||||||
vm.WeighingData = p.WeighingData;
|
vm.WeighingData = p.WeighingData;
|
||||||
@@ -188,14 +186,54 @@ namespace Elwig.Services {
|
|||||||
prd = prd.And(p => p.IsNetWeight == true);
|
prd = prd.And(p => p.IsNetWeight == true);
|
||||||
filter.RemoveAt(i--);
|
filter.RemoveAt(i--);
|
||||||
filterNames.Add("gerebelt gewogen");
|
filterNames.Add("gerebelt gewogen");
|
||||||
|
} else if (e.Length >= 5 && e.Length <= 11 && "planenwagen".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
prd = prd.And(p => p.Unloading == DeliveryPart.Dumper);
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("Planenw./Kipper");
|
||||||
|
} else if (e.Length >= 6 && e.Length <= 12 && "!planenwagen".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
prd = prd.And(p => p.Unloading != DeliveryPart.Dumper);
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("kein Planenw./Kipper");
|
||||||
|
} else if (e.Length >= 4 && e.Length <= 6 && "kipper".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
prd = prd.And(p => p.Unloading == DeliveryPart.Dumper);
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("Planenw./Kipper");
|
||||||
|
} else if (e.Length >= 5 && e.Length <= 7 && "!kipper".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
prd = prd.And(p => p.Unloading != DeliveryPart.Dumper);
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("kein Planenw./Kipper");
|
||||||
} else if (e.Length >= 5 && e.Length <= 9 && "lesewagen".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
} else if (e.Length >= 5 && e.Length <= 9 && "lesewagen".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
prd = prd.And(p => p.IsLesewagen == true);
|
prd = prd.And(p => p.Unloading == DeliveryPart.Pumped);
|
||||||
filter.RemoveAt(i--);
|
filter.RemoveAt(i--);
|
||||||
filterNames.Add("Lesewagen");
|
filterNames.Add("Lesewagen");
|
||||||
} else if (e.Length >= 6 && e.Length <= 10 && "!lesewagen".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
} else if (e.Length >= 6 && e.Length <= 10 && "!lesewagen".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
prd = prd.And(p => p.IsLesewagen == false);
|
prd = prd.And(p => p.Unloading != DeliveryPart.Pumped);
|
||||||
filter.RemoveAt(i--);
|
filter.RemoveAt(i--);
|
||||||
filterNames.Add("kein Lesewagen");
|
filterNames.Add("kein Lesewagen");
|
||||||
|
} else if (e.Length >= 5 && e.Length <= 6 && "kisten".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
prd = prd.And(p => p.Unloading == DeliveryPart.Box);
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("Kisten");
|
||||||
|
} else if (e.Length >= 6 && e.Length <= 7 && "!kisten".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
prd = prd.And(p => p.Unloading != DeliveryPart.Box);
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("keine Kisten");
|
||||||
|
} else if ("upload".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
prd = prd.And(p => (p.Delivery.XTime == null || p.Delivery.MTime > p.Delivery.XTime) && (p.Delivery.ITime == null || p.Delivery.MTime > p.Delivery.ITime));
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("geändert seit letztem Export");
|
||||||
|
} else if ("!upload".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
prd = prd.And(p => !((p.Delivery.XTime == null || p.Delivery.MTime > p.Delivery.XTime) && (p.Delivery.ITime == null || p.Delivery.MTime > p.Delivery.ITime)));
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("unverändert seit letztem Export");
|
||||||
|
} else if (">import".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
prd = prd.And(p => p.ITime != null && p.Delivery.MTime > p.Delivery.ITime);
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("geändert seit letztem Import");
|
||||||
|
} else if ("<import".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
prd = prd.And(p => p.Delivery.MTime <= p.Delivery.ITime);
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("unverändert seit letztem Import");
|
||||||
} else if (e.Length == 2 && var.ContainsKey(e.ToUpper())) {
|
} else if (e.Length == 2 && var.ContainsKey(e.ToUpper())) {
|
||||||
filterVar.Add(e.ToUpper());
|
filterVar.Add(e.ToUpper());
|
||||||
filter.RemoveAt(i--);
|
filter.RemoveAt(i--);
|
||||||
@@ -483,8 +521,8 @@ namespace Elwig.Services {
|
|||||||
|
|
||||||
IsNetWeight = vm.IsNetWeight,
|
IsNetWeight = vm.IsNetWeight,
|
||||||
IsHandPicked = vm.IsHandPicked,
|
IsHandPicked = vm.IsHandPicked,
|
||||||
IsLesewagen = vm.IsLesewagen,
|
|
||||||
IsGebunden = vm.IsGebunden,
|
IsGebunden = vm.IsGebunden,
|
||||||
|
Unloading = vm.Unloading,
|
||||||
Temperature = vm.Temperature,
|
Temperature = vm.Temperature,
|
||||||
Acid = vm.Acid,
|
Acid = vm.Acid,
|
||||||
Comment = string.IsNullOrEmpty(vm.PartComment) ? null : vm.PartComment,
|
Comment = string.IsNullOrEmpty(vm.PartComment) ? null : vm.PartComment,
|
||||||
@@ -571,6 +609,13 @@ namespace Elwig.Services {
|
|||||||
s.DPNr = dpnr++;
|
s.DPNr = dpnr++;
|
||||||
s.Weight = w;
|
s.Weight = w;
|
||||||
ctx.Add(s);
|
ctx.Add(s);
|
||||||
|
|
||||||
|
ctx.AddRange(p.PartModifiers.Select(m => new DeliveryPartModifier() {
|
||||||
|
Year = s.Year,
|
||||||
|
DId = s.DId,
|
||||||
|
DPNr = s.DPNr,
|
||||||
|
ModId = m.ModId,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -608,6 +653,13 @@ namespace Elwig.Services {
|
|||||||
s.DPNr = dpnr++;
|
s.DPNr = dpnr++;
|
||||||
s.Weight = w;
|
s.Weight = w;
|
||||||
ctx.Add(s);
|
ctx.Add(s);
|
||||||
|
|
||||||
|
ctx.AddRange(p.PartModifiers.Select(m => new DeliveryPartModifier() {
|
||||||
|
Year = s.Year,
|
||||||
|
DId = s.DId,
|
||||||
|
DPNr = s.DPNr,
|
||||||
|
ModId = m.ModId,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -642,6 +694,13 @@ namespace Elwig.Services {
|
|||||||
n.QualId = "WEI";
|
n.QualId = "WEI";
|
||||||
n.HkId = "OEST";
|
n.HkId = "OEST";
|
||||||
ctx.Add(n);
|
ctx.Add(n);
|
||||||
|
|
||||||
|
ctx.AddRange(p.PartModifiers.Select(m => new DeliveryPartModifier() {
|
||||||
|
Year = n.Year,
|
||||||
|
DId = n.DId,
|
||||||
|
DPNr = n.DPNr,
|
||||||
|
ModId = m.ModId,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await ctx.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
@@ -754,40 +813,7 @@ namespace Elwig.Services {
|
|||||||
} else if (mode == ExportMode.Upload && App.Config.SyncUrl != null) {
|
} else if (mode == ExportMode.Upload && App.Config.SyncUrl != null) {
|
||||||
Mouse.OverrideCursor = Cursors.Wait;
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
await Task.Run(async () => {
|
await Task.Run(async () => {
|
||||||
try {
|
await SyncService.Upload(App.Config.SyncUrl, App.Config.SyncUrl, App.Config.SyncPassword, query, filterNames);
|
||||||
var filename = $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip";
|
|
||||||
var path = Path.Combine(App.TempPath, filename);
|
|
||||||
var list = await query
|
|
||||||
.Select(p => p.Delivery)
|
|
||||||
.Distinct()
|
|
||||||
.Include(d => d.Parts).ThenInclude(p => p.PartModifiers)
|
|
||||||
.Include(d => d.Parts).ThenInclude(p => p.Rd)
|
|
||||||
.Include(d => d.Parts).ThenInclude(p => p.Kg!.Gl)
|
|
||||||
.AsSplitQuery()
|
|
||||||
.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();
|
|
||||||
if (list.Count == 0) {
|
|
||||||
MessageBox.Show("Es wurden keine Lieferungen zum Hochladen ausgewählt!", "Lieferungen hochladen",
|
|
||||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
} else {
|
|
||||||
await ElwigData.Export(path, list, wbKgs, filterNames);
|
|
||||||
await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
|
|
||||||
MessageBox.Show($"Hochladen von {list.Count:N0} Lieferungen erfolgreich!", "Lieferungen hochgeladen",
|
|
||||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
|
||||||
}
|
|
||||||
} catch (HttpRequestException exc) {
|
|
||||||
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
} catch (TaskCanceledException exc) {
|
|
||||||
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
} catch (Exception exc) {
|
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
} else {
|
} else {
|
||||||
@@ -938,6 +964,52 @@ namespace Elwig.Services {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task GenerateDeliveryDataList(this DeliveryAdminViewModel vm, ExportSubject subject, ExportMode mode) {
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
|
IQueryable<DeliveryPart> query;
|
||||||
|
List<string> filterNames = [];
|
||||||
|
if (subject == ExportSubject.FromFilters) {
|
||||||
|
var (f, _, q, _, _) = await vm.GetFilters(ctx);
|
||||||
|
query = q;
|
||||||
|
filterNames.AddRange(f);
|
||||||
|
} else if (subject == ExportSubject.FromSeason) {
|
||||||
|
var year = vm.FilterSeason ?? Utils.CurrentLastSeason;
|
||||||
|
query = ctx.DeliveryParts
|
||||||
|
.Where(p => p.Year == year);
|
||||||
|
filterNames.Add($"{year}");
|
||||||
|
} else {
|
||||||
|
throw new ArgumentException("Invalid value for ExportSubject");
|
||||||
|
}
|
||||||
|
|
||||||
|
query = query
|
||||||
|
.OrderBy(p => p.Delivery.MgNr)
|
||||||
|
.ThenBy(p => p.SortId)
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
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) {
|
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() {
|
var tb = new TextBlock() {
|
||||||
Text = text,
|
Text = text,
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
@@ -315,6 +313,22 @@ namespace Elwig.Services {
|
|||||||
memberQuery = memberQuery.Where(m => !m.ContactViaPost);
|
memberQuery = memberQuery.Where(m => !m.ContactViaPost);
|
||||||
filter.RemoveAt(i--);
|
filter.RemoveAt(i--);
|
||||||
filterNames.Add("nicht Kontaktart Post");
|
filterNames.Add("nicht Kontaktart Post");
|
||||||
|
} else if ("upload".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
memberQuery = memberQuery.Where(p => (p.XTime == null || p.MTime > p.XTime) && (p.ITime == null || p.MTime > p.ITime));
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("geändert seit letztem Export");
|
||||||
|
} else if ("!upload".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
memberQuery = memberQuery.Where(p => !((p.XTime == null || p.MTime > p.XTime) && (p.ITime == null || p.MTime > p.ITime)));
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("unverändert seit letztem Export");
|
||||||
|
} else if (">import".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
memberQuery = memberQuery.Where(p => p.MTime > p.ITime);
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("geändert seit letztem Import");
|
||||||
|
} else if ("<import".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
|
||||||
|
memberQuery = memberQuery.Where(p => p.MTime <= p.ITime);
|
||||||
|
filter.RemoveAt(i--);
|
||||||
|
filterNames.Add("unverändert seit letztem Import");
|
||||||
} else if (e.All(char.IsAsciiDigit) && mgnr.ContainsKey(e)) {
|
} else if (e.All(char.IsAsciiDigit) && mgnr.ContainsKey(e)) {
|
||||||
filterMgNr.Add(int.Parse(e));
|
filterMgNr.Add(int.Parse(e));
|
||||||
filter.RemoveAt(i--);
|
filter.RemoveAt(i--);
|
||||||
@@ -493,6 +507,32 @@ namespace Elwig.Services {
|
|||||||
});
|
});
|
||||||
Mouse.OverrideCursor = null;
|
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.BillingAddress)
|
||||||
|
.Include(m => m.TelephoneNumbers)
|
||||||
|
.Include(m => m.EmailAddresses)
|
||||||
|
.AsSplitQuery()
|
||||||
|
.ToListAsync();
|
||||||
|
using var exporter = new VCard(d.FileName);
|
||||||
|
await exporter.ExportAsync(members);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Mouse.OverrideCursor = null;
|
||||||
|
}
|
||||||
} else if (mode == ExportMode.Export) {
|
} else if (mode == ExportMode.Export) {
|
||||||
var d = new SaveFileDialog() {
|
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",
|
FileName = subject == ExportSubject.Selected ? $"Mitglied_{vm.SelectedMember?.MgNr}.elwig.zip" : $"Mitglieder_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip",
|
||||||
@@ -536,46 +576,7 @@ namespace Elwig.Services {
|
|||||||
} else if (mode == ExportMode.Upload && App.Config.SyncUrl != null) {
|
} else if (mode == ExportMode.Upload && App.Config.SyncUrl != null) {
|
||||||
Mouse.OverrideCursor = Cursors.Wait;
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
await Task.Run(async () => {
|
await Task.Run(async () => {
|
||||||
try {
|
await SyncService.Upload(App.Config.SyncUrl, App.Config.SyncUrl, App.Config.SyncPassword, query, filterNames);
|
||||||
var filename = $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip";
|
|
||||||
var path = Path.Combine(App.TempPath, filename);
|
|
||||||
var members = await query
|
|
||||||
.OrderBy(m => m.MgNr)
|
|
||||||
.Include(m => m.BillingAddress)
|
|
||||||
.Include(m => m.TelephoneNumbers)
|
|
||||||
.Include(m => m.EmailAddresses)
|
|
||||||
.Include(m => m.DefaultWbKg!.Gl)
|
|
||||||
.AsSplitQuery()
|
|
||||||
.ToListAsync();
|
|
||||||
var areaComs = await query
|
|
||||||
.SelectMany(m => m.AreaCommitments)
|
|
||||||
.OrderBy(c => c.MgNr).ThenBy(c => c.FbNr)
|
|
||||||
.Include(c => c.Rd)
|
|
||||||
.Include(c => c.Kg.Gl)
|
|
||||||
.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();
|
|
||||||
if (members.Count == 0) {
|
|
||||||
MessageBox.Show("Es wurden keine Mitglieder zum Hochladen ausgewählt!", "Mitglieder hochladen",
|
|
||||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
} else {
|
|
||||||
await ElwigData.Export(path, members, areaComs, wbKgs, filterNames);
|
|
||||||
await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
|
|
||||||
MessageBox.Show($"Hochladen von {members.Count:N0} Mitgliedern erfolgreich!", "Mitglieder hochgeladen",
|
|
||||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
|
||||||
}
|
|
||||||
} catch (HttpRequestException exc) {
|
|
||||||
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
} catch (TaskCanceledException exc) {
|
|
||||||
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
} catch (Exception exc) {
|
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
277
Elwig/Services/SyncService.cs
Normal file
277
Elwig/Services/SyncService.cs
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
using Elwig.Helpers;
|
||||||
|
using Elwig.Helpers.Export;
|
||||||
|
using Elwig.Models.Entities;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
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 {
|
||||||
|
|
||||||
|
public static readonly Expression<Func<Member, bool>> ChangedMembers = (m) => ((m.XTime == null && m.MTime > 1751328000) || m.MTime > m.XTime) && (m.ITime == null || m.MTime > m.ITime);
|
||||||
|
public static readonly Expression<Func<Delivery, bool>> ChangedDeliveries = (d) => ((d.XTime == null && d.MTime > 1751328000) || d.MTime > d.XTime) && (d.ITime == null || d.MTime > d.ITime);
|
||||||
|
|
||||||
|
public static async Task Upload(string url, string username, string password, IQueryable<Member> query, IEnumerable<string> filterNames) {
|
||||||
|
try {
|
||||||
|
var filename = $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip";
|
||||||
|
var path = Path.Combine(App.TempPath, filename);
|
||||||
|
var members = await query
|
||||||
|
.OrderBy(m => m.MgNr)
|
||||||
|
.Include(m => m.BillingAddress)
|
||||||
|
.Include(m => m.TelephoneNumbers)
|
||||||
|
.Include(m => m.EmailAddresses)
|
||||||
|
.Include(m => m.DefaultWbKg!.Gl)
|
||||||
|
.AsSplitQuery()
|
||||||
|
.ToListAsync();
|
||||||
|
var areaComs = await query
|
||||||
|
.SelectMany(m => m.AreaCommitments)
|
||||||
|
.OrderBy(c => c.MgNr).ThenBy(c => c.FbNr)
|
||||||
|
.Include(c => c.Rd)
|
||||||
|
.Include(c => c.Kg.Gl)
|
||||||
|
.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();
|
||||||
|
if (members.Count == 0) {
|
||||||
|
MessageBox.Show("Es wurden keine Mitglieder zum Hochladen ausgewählt!", "Mitglieder hochladen",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} else {
|
||||||
|
var exportedAt = DateTime.Now;
|
||||||
|
await ElwigData.Export(path, members, areaComs, wbKgs, filterNames);
|
||||||
|
await Utils.UploadExportData(path, url, username, password);
|
||||||
|
await UpdateExportedAt(members, [], exportedAt);
|
||||||
|
MessageBox.Show($"Hochladen von {members.Count:N0} Mitgliedern erfolgreich!", "Mitglieder hochgeladen",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
} catch (HttpRequestException exc) {
|
||||||
|
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} catch (TaskCanceledException exc) {
|
||||||
|
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task Upload(string url, string username, string password, IQueryable<DeliveryPart> query, IEnumerable<string> filterNames) {
|
||||||
|
try {
|
||||||
|
var filename = $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip";
|
||||||
|
var path = Path.Combine(App.TempPath, filename);
|
||||||
|
var list = await query
|
||||||
|
.Select(p => p.Delivery)
|
||||||
|
.Distinct()
|
||||||
|
.Include(d => d.Parts).ThenInclude(p => p.PartModifiers).ThenInclude(m => m.Modifier)
|
||||||
|
.Include(d => d.Parts).ThenInclude(p => p.Rd)
|
||||||
|
.Include(d => d.Parts).ThenInclude(p => p.Kg!.Gl)
|
||||||
|
.AsSplitQuery()
|
||||||
|
.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();
|
||||||
|
if (list.Count == 0) {
|
||||||
|
MessageBox.Show("Es wurden keine Lieferungen zum Hochladen ausgewählt!", "Lieferungen hochladen",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
} catch (HttpRequestException exc) {
|
||||||
|
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} catch (TaskCanceledException exc) {
|
||||||
|
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task UploadModified(string url, string username, string password) {
|
||||||
|
try {
|
||||||
|
var path = Path.Combine(App.TempPath, $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip");
|
||||||
|
List<Member> members;
|
||||||
|
List<AreaCom> areaComs;
|
||||||
|
List<Delivery> deliveries;
|
||||||
|
using (var ctx = new AppDbContext()) {
|
||||||
|
members = await ctx.Members
|
||||||
|
.Where(ChangedMembers)
|
||||||
|
.Include(m => m.BillingAddress)
|
||||||
|
.Include(m => m.TelephoneNumbers)
|
||||||
|
.Include(m => m.EmailAddresses)
|
||||||
|
.Include(m => m.DefaultWbKg!.Gl)
|
||||||
|
.OrderBy(m => m.MgNr)
|
||||||
|
.AsSplitQuery()
|
||||||
|
.ToListAsync();
|
||||||
|
areaComs = await ctx.Members
|
||||||
|
.Where(ChangedMembers)
|
||||||
|
.SelectMany(m => m.AreaCommitments)
|
||||||
|
.Include(c => c.Rd)
|
||||||
|
.Include(c => c.Kg.Gl)
|
||||||
|
.OrderBy(c => c.MgNr).ThenBy(c => c.FbNr)
|
||||||
|
.ToListAsync();
|
||||||
|
deliveries = await ctx.Deliveries
|
||||||
|
.Where(ChangedDeliveries)
|
||||||
|
.Include(d => d.Parts).ThenInclude(p => p.PartModifiers).ThenInclude(m => m.Modifier)
|
||||||
|
.Include(d => d.Parts).ThenInclude(p => p.Rd)
|
||||||
|
.Include(d => d.Parts).ThenInclude(p => p.Kg).ThenInclude(k => k!.Gl)
|
||||||
|
.OrderBy(d => d.DateString).ThenBy(d => d.TimeString).ThenBy(d => d.LsNr)
|
||||||
|
.AsSplitQuery()
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
var wbKgs = members
|
||||||
|
.Where(m => m.DefaultWbKg != null)
|
||||||
|
.Select(m => m.DefaultWbKg!)
|
||||||
|
.Union(areaComs.Select(c => c.Kg))
|
||||||
|
.Union(deliveries.SelectMany(d => d.Parts)
|
||||||
|
.Where(p => p.Kg != null)
|
||||||
|
.Select(p => p.Kg!))
|
||||||
|
.DistinctBy(k => k.KgNr)
|
||||||
|
.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);
|
||||||
|
} else {
|
||||||
|
var exportedAt = DateTime.Now;
|
||||||
|
await (new ElwigData.ElwigExport {
|
||||||
|
Members = (members, ["geändert seit letztem Export"]),
|
||||||
|
AreaComs = (areaComs, ["von exportierten Mitgliedern"]),
|
||||||
|
Deliveries = (deliveries, ["geändert seit letzem Export"]),
|
||||||
|
WbKgs = (wbKgs, ["von exportierten Mitgliedern, Flächenbindungen und Lieferungen"]),
|
||||||
|
}).Export(path);
|
||||||
|
await Utils.UploadExportData(path, url, username, password);
|
||||||
|
await UpdateExportedAt(members, deliveries, exportedAt);
|
||||||
|
MessageBox.Show($"Hochladen von {members.Count:N0} Mitgliedern und {deliveries.Count:N0} Lieferungen erfolgreich!", "Mitglieder und Lieferungen hochladen",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
} catch (HttpRequestException exc) {
|
||||||
|
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder und Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} catch (TaskCanceledException exc) {
|
||||||
|
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder und Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Mitglieder und Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task UploadBranchDeliveries(string url, string username, string password, int? year = null) {
|
||||||
|
try {
|
||||||
|
year ??= Utils.CurrentLastSeason;
|
||||||
|
var path = Path.Combine(App.TempPath, $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip");
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
|
var deliveries = await ctx.Deliveries
|
||||||
|
.Where(d => d.Year == year && d.ZwstId == App.ZwstId)
|
||||||
|
.Include(d => d.Parts).ThenInclude(p => p.PartModifiers).ThenInclude(m => m.Modifier)
|
||||||
|
.Include(d => d.Parts).ThenInclude(p => p.Rd)
|
||||||
|
.Include(d => d.Parts).ThenInclude(p => p.Kg).ThenInclude(k => k!.Gl)
|
||||||
|
.OrderBy(d => d.DateString).ThenBy(d => d.TimeString).ThenBy(d => d.LsNr)
|
||||||
|
.AsSplitQuery()
|
||||||
|
.ToListAsync();
|
||||||
|
var wbKgs = deliveries
|
||||||
|
.SelectMany(d => d.Parts)
|
||||||
|
.Where(p => p.Kg != null)
|
||||||
|
.Select(p => p.Kg!)
|
||||||
|
.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);
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
} catch (HttpRequestException exc) {
|
||||||
|
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} catch (TaskCanceledException exc) {
|
||||||
|
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<List<(string Name, string Url)>> GetFilesToImport(string url, string username, string password) {
|
||||||
|
var data = await Utils.GetExportMetaData(url, username, password);
|
||||||
|
var files = data
|
||||||
|
.Select(f => new {
|
||||||
|
Name = f!["name"]!.AsValue().GetValue<string>(),
|
||||||
|
Timestamp = f!["timestamp"] != null && DateTime.TryParseExact(f!["timestamp"]!.AsValue().GetValue<string>(), "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt) ? dt : (DateTime?)null,
|
||||||
|
ZwstId = f!["meta"]?["zwstid"]?.AsValue().GetValue<string>() ?? f!["zwstid"]?.AsValue().GetValue<string>(),
|
||||||
|
Device = f!["meta"]?["device"]?.AsValue().GetValue<string>(),
|
||||||
|
Url = f!["url"]!.AsValue().GetValue<string>(),
|
||||||
|
Size = f!["size"]!.AsValue().GetValue<long>(),
|
||||||
|
})
|
||||||
|
.Where(f => f.Timestamp >= new DateTime(Utils.CurrentLastSeason, 7, 1))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var imported = await ElwigData.GetImportedFiles();
|
||||||
|
return [.. files
|
||||||
|
.Where(f => f.Device != Environment.MachineName && !imported.Contains(f.Name))
|
||||||
|
.Select(f => (f.Name, f.Url))
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task Download(string url, string username, string password) {
|
||||||
|
try {
|
||||||
|
var import = await GetFilesToImport(url, username, password);
|
||||||
|
var paths = new List<string>();
|
||||||
|
using (var client = Utils.GetHttpClient(username, password)) {
|
||||||
|
foreach (var f in import) {
|
||||||
|
var filename = Path.Combine(App.TempPath, f.Name);
|
||||||
|
using var stream = new FileStream(filename, FileMode.Create);
|
||||||
|
await client.DownloadAsync(f.Url, stream);
|
||||||
|
paths.Add(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
} catch (TaskCanceledException exc) {
|
||||||
|
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Daten herunterladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Daten herunterladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task UpdateExportedAt(IEnumerable<Member> member, IEnumerable<Delivery> deliveries, DateTime dateTime) {
|
||||||
|
var timestamp = ((DateTimeOffset)dateTime.ToUniversalTime()).ToUnixTimeSeconds();
|
||||||
|
var mgnrs = string.Join(",", member.Select(m => $"{m.MgNr}").Append("0"));
|
||||||
|
var dids = string.Join(",", deliveries.Select(d => $"({d.Year},{d.DId})").Append("(0,0)"));
|
||||||
|
using (var cnx = await AppDbContext.ConnectAsync()) {
|
||||||
|
await cnx.ExecuteBatch($"""
|
||||||
|
BEGIN;
|
||||||
|
UPDATE client_parameter SET value = '0' WHERE param = 'ENABLE_TIME_TRIGGERS';
|
||||||
|
UPDATE member SET xtime = {timestamp} WHERE mgnr IN ({mgnrs});
|
||||||
|
UPDATE area_commitment SET xtime = {timestamp} WHERE mgnr IN ({mgnrs});
|
||||||
|
UPDATE delivery SET xtime = {timestamp} WHERE (year, did) IN ({dids});
|
||||||
|
UPDATE delivery_part SET xtime = {timestamp} WHERE (year, did) IN ({dids});
|
||||||
|
UPDATE client_parameter SET value = '1' WHERE param = 'ENABLE_TIME_TRIGGERS';
|
||||||
|
COMMIT;
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
App.HintContextChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<bool> ChangesAvailable(AppDbContext ctx, string url, string username, string password) {
|
||||||
|
return await ctx.Members.AnyAsync(ChangedMembers) || await ctx.Deliveries.AnyAsync(ChangedDeliveries) || (Utils.HasInternetConnectivity() && (await GetFilesToImport(url, username, password)).Count > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -169,10 +169,28 @@ namespace Elwig.ViewModels {
|
|||||||
set => AcidString = $"{value:0.0}";
|
set => AcidString = $"{value:0.0}";
|
||||||
}
|
}
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _isLesewagen;
|
|
||||||
[ObservableProperty]
|
|
||||||
private bool? _isHandPicked;
|
private bool? _isHandPicked;
|
||||||
|
|
||||||
|
public string? Unloading {
|
||||||
|
get => IsUnloadingDumper ? DeliveryPart.Dumper : IsUnloadingPumped ? DeliveryPart.Pumped : IsUnloadingBox ? DeliveryPart.Box : null;
|
||||||
|
set {
|
||||||
|
switch (value) {
|
||||||
|
case DeliveryPart.Dumper: IsUnloadingDumper = true; break;
|
||||||
|
case DeliveryPart.Pumped: IsUnloadingPumped = true; break;
|
||||||
|
case DeliveryPart.Box: IsUnloadingBox = true; break;
|
||||||
|
default: IsUnloadingOther = true; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _isUnloadingDumper;
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _isUnloadingPumped;
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _isUnloadingBox;
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _isUnloadingOther;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _statusMembers = "-";
|
private string _statusMembers = "-";
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
xmlns:local="clr-namespace:Elwig.Windows"
|
xmlns:local="clr-namespace:Elwig.Windows"
|
||||||
xmlns:ctrl="clr-namespace:Elwig.Controls"
|
xmlns:ctrl="clr-namespace:Elwig.Controls"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Title="Stammdaten - Elwig" Height="500" MinHeight="400" Width="850" MinWidth="810"
|
Title="Stammdaten - Elwig" Height="520" MinHeight="400" Width="860" MinWidth="810"
|
||||||
Loaded="Window_Loaded">
|
Loaded="Window_Loaded">
|
||||||
<Window.Resources>
|
<Window.Resources>
|
||||||
<Style TargetType="Label">
|
<Style TargetType="Label">
|
||||||
@@ -532,6 +532,10 @@
|
|||||||
<CheckBox x:Name="SeasonModifierActiveInput" Content="In Übernahme-Fenster anzeigen"
|
<CheckBox x:Name="SeasonModifierActiveInput" Content="In Übernahme-Fenster anzeigen"
|
||||||
Grid.Column="1" Grid.ColumnSpan="2" Margin="10,134,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"
|
Grid.Column="1" Grid.ColumnSpan="2" Margin="10,134,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"
|
||||||
Checked="SeasonModifier_Changed" Unchecked="SeasonModifier_Changed"/>
|
Checked="SeasonModifier_Changed" Unchecked="SeasonModifier_Changed"/>
|
||||||
|
|
||||||
|
<CheckBox x:Name="SeasonModifierRedactedInput" Content="Wert auf Lieferschein verbergen"
|
||||||
|
Grid.Column="1" Grid.ColumnSpan="2" Margin="10,154,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"
|
||||||
|
Checked="SeasonModifier_Changed" Unchecked="SeasonModifier_Changed"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</GroupBox>
|
</GroupBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -135,12 +135,14 @@ namespace Elwig.Windows {
|
|||||||
SeasonModifierRelInput.Text = "";
|
SeasonModifierRelInput.Text = "";
|
||||||
SeasonModifierAbsInput.Text = "";
|
SeasonModifierAbsInput.Text = "";
|
||||||
SeasonModifierActiveInput.IsChecked = false;
|
SeasonModifierActiveInput.IsChecked = false;
|
||||||
|
SeasonModifierRedactedInput.IsChecked = false;
|
||||||
} else {
|
} else {
|
||||||
SeasonModifierIdInput.Text = mod.ModId;
|
SeasonModifierIdInput.Text = mod.ModId;
|
||||||
SeasonModifierNameInput.Text = mod.Name;
|
SeasonModifierNameInput.Text = mod.Name;
|
||||||
SeasonModifierRelInput.Text = (mod.Rel * 100)?.ToString() ?? "";
|
SeasonModifierRelInput.Text = (mod.Rel * 100)?.ToString() ?? "";
|
||||||
SeasonModifierAbsInput.Text = mod.Abs?.ToString() ?? "";
|
SeasonModifierAbsInput.Text = mod.Abs?.ToString() ?? "";
|
||||||
SeasonModifierActiveInput.IsChecked = mod.IsActive;
|
SeasonModifierActiveInput.IsChecked = mod.IsActive;
|
||||||
|
SeasonModifierRedactedInput.IsChecked = mod.IsRedacted;
|
||||||
}
|
}
|
||||||
_modUpdate = false;
|
_modUpdate = false;
|
||||||
}
|
}
|
||||||
@@ -157,6 +159,7 @@ namespace Elwig.Windows {
|
|||||||
mod.Rel = decimal.TryParse(SeasonModifierRelInput.Text, out var vRel) ? vRel / 100 : null;
|
mod.Rel = decimal.TryParse(SeasonModifierRelInput.Text, out var vRel) ? vRel / 100 : null;
|
||||||
mod.AbsValue = decimal.TryParse(SeasonModifierAbsInput.Text, out var vAbs) ? Utils.DecToDb(vAbs, s.Precision) : null;
|
mod.AbsValue = decimal.TryParse(SeasonModifierAbsInput.Text, out var vAbs) ? Utils.DecToDb(vAbs, s.Precision) : null;
|
||||||
mod.IsActive = SeasonModifierActiveInput.IsChecked ?? false;
|
mod.IsActive = SeasonModifierActiveInput.IsChecked ?? false;
|
||||||
|
mod.IsRedacted = SeasonModifierRedactedInput.IsChecked ?? false;
|
||||||
|
|
||||||
CollectionViewSource.GetDefaultView(_modList).Refresh();
|
CollectionViewSource.GetDefaultView(_modList).Refresh();
|
||||||
UpdateButtons();
|
UpdateButtons();
|
||||||
|
|||||||
@@ -664,8 +664,10 @@ namespace Elwig.Windows {
|
|||||||
try {
|
try {
|
||||||
await Task.Run(async () => {
|
await Task.Run(async () => {
|
||||||
var b = new BillingVariant(PaymentVar.Year, PaymentVar.AvNr);
|
var b = new BillingVariant(PaymentVar.Year, PaymentVar.AvNr);
|
||||||
await b.Calculate();
|
await b.Calculate(false);
|
||||||
});
|
});
|
||||||
|
} catch (KeyNotFoundException exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Noch nicht alle Preise festgelegt", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
} catch (Exception exc) {
|
} catch (Exception exc) {
|
||||||
MessageBox.Show(exc.Message, "Berechnungsfehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show(exc.Message, "Berechnungsfehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,6 +129,10 @@
|
|||||||
<MenuItem x:Name="Menu_DeliveryDepreciationList_PrintSeason" Header="...von Saison drucken"
|
<MenuItem x:Name="Menu_DeliveryDepreciationList_PrintSeason" Header="...von Saison drucken"
|
||||||
Click="Menu_DeliveryDepreciationList_PrintSeason_Click"/>
|
Click="Menu_DeliveryDepreciationList_PrintSeason_Click"/>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
<MenuItem Header="Liefermengen" x:Name="Menu_DeliveryDataList">
|
||||||
|
<MenuItem x:Name="Menu_DeliveryDataList_SaveFilters" Header="...aus Filtern speichern... (Excel)"
|
||||||
|
Click="Menu_DeliveryDataList_SaveFilters_Click"/>
|
||||||
|
</MenuItem>
|
||||||
<MenuItem Header="Statistik" x:Name="Menu_Statistics">
|
<MenuItem Header="Statistik" x:Name="Menu_Statistics">
|
||||||
<MenuItem Header="Qualitätsstatistik..." x:Name="Menu_Statistics_WineQuality">
|
<MenuItem Header="Qualitätsstatistik..." x:Name="Menu_Statistics_WineQuality">
|
||||||
<MenuItem x:Name="Menu_Statistics_WineQuality_ShowFilters" Header="...aus Filtern anzeigen (PDF)"
|
<MenuItem x:Name="Menu_Statistics_WineQuality_ShowFilters" Header="...aus Filtern anzeigen (PDF)"
|
||||||
@@ -222,6 +226,7 @@
|
|||||||
<Bold>Uhrzeit</Bold>: z.B. 06:00-08:00, 18:00-, ...<LineBreak/>
|
<Bold>Uhrzeit</Bold>: z.B. 06:00-08:00, 18:00-, ...<LineBreak/>
|
||||||
<Bold>Handwiegung</Bold>: handw[iegung], !Handw[iegung] (alle ohne Handwiegung)<LineBreak/>
|
<Bold>Handwiegung</Bold>: handw[iegung], !Handw[iegung] (alle ohne Handwiegung)<LineBreak/>
|
||||||
<Bold>Handlese</Bold>: Handl[ese], !handl[ese] (alle ohne Handlese)<LineBreak/>
|
<Bold>Handlese</Bold>: Handl[ese], !handl[ese] (alle ohne Handlese)<LineBreak/>
|
||||||
|
<Bold>Anlieferung</Bold>: Plane[nwagen]/Kipp[er], !plane[nwagen]/!kipp[er], Lesew[agen], !lesew[agen], kiste[n], !kiste[n]<LineBreak/>
|
||||||
<Bold>Gebunden</Bold>: geb[unden], ungeb[unden], !geb[unden], !ungeb[unden]<LineBreak/>
|
<Bold>Gebunden</Bold>: geb[unden], ungeb[unden], !geb[unden], !ungeb[unden]<LineBreak/>
|
||||||
<Bold>Gerebelt</Bold>: gerebelt, !Gerebelt (nicht gerebelt gewogen)<LineBreak/>
|
<Bold>Gerebelt</Bold>: gerebelt, !Gerebelt (nicht gerebelt gewogen)<LineBreak/>
|
||||||
<Bold>Freitext</Bold>: z.B. Lieferscheinnummern, Anmerkung, "quw" (sucht nach dem Text "quw")
|
<Bold>Freitext</Bold>: z.B. Lieferscheinnummern, Anmerkung, "quw" (sucht nach dem Text "quw")
|
||||||
@@ -369,8 +374,9 @@
|
|||||||
<Grid Grid.Row="1" Grid.Column="2">
|
<Grid Grid.Row="1" Grid.Column="2">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="0.625*"/>
|
<RowDefinition Height="0.625*"/>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="0.875*"/>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="0.875*"/>
|
||||||
|
<RowDefinition Height="0.25*"/>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="*"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
@@ -404,7 +410,7 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</GroupBox>
|
</GroupBox>
|
||||||
|
|
||||||
<GroupBox Header="Lieferung" Grid.Column="0" Grid.Row="1" Grid.RowSpan="2" Margin="5,5,5,5">
|
<GroupBox Header="Lieferung" Grid.Column="0" Grid.Row="1" Grid.RowSpan="3" Margin="5,5,5,5">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="100"/>
|
<ColumnDefinition Width="100"/>
|
||||||
@@ -563,7 +569,7 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</GroupBox>
|
</GroupBox>
|
||||||
|
|
||||||
<GroupBox Header="Sonstiges" Grid.Column="1" Grid.Row="3" Margin="5,5,5,10">
|
<GroupBox Header="Sonstiges" Grid.Column="1" Grid.Row="3" Grid.RowSpan="2" Margin="5,5,5,10">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="100"/>
|
<ColumnDefinition Width="100"/>
|
||||||
@@ -592,18 +598,31 @@
|
|||||||
TextChanged="TemperatureAcidInput_TextChanged" LostFocus="TemperatureAcidInput_LostFocus"
|
TextChanged="TemperatureAcidInput_TextChanged" LostFocus="TemperatureAcidInput_LostFocus"
|
||||||
Grid.Column="1" Margin="0,100,10,10" VerticalAlignment="Top"/>
|
Grid.Column="1" Margin="0,100,10,10" VerticalAlignment="Top"/>
|
||||||
|
|
||||||
<CheckBox x:Name="LesewagenInput" IsChecked="{Binding IsLesewagen, Mode=TwoWay}"
|
|
||||||
Content="Lesewagen" Margin="10,75,0,0" Grid.Column="2"
|
|
||||||
VerticalAlignment="Top" HorizontalAlignment="Left"
|
|
||||||
Checked="LesewagenInput_Changed" Unchecked="LesewagenInput_Changed"/>
|
|
||||||
<CheckBox x:Name="HandPickedInput" IsChecked="{Binding IsHandPicked, Mode=TwoWay}"
|
<CheckBox x:Name="HandPickedInput" IsChecked="{Binding IsHandPicked, Mode=TwoWay}"
|
||||||
Content="Handlese" Margin="10,105,0,0" Grid.Column="2" IsThreeState="True"
|
Content="Handlese" Margin="10,135,10,0" Grid.Column="0" Grid.ColumnSpan="2" IsThreeState="True"
|
||||||
VerticalAlignment="Top" HorizontalAlignment="Left"
|
VerticalAlignment="Top" HorizontalAlignment="Left"
|
||||||
Checked="HandPickedInput_Changed" Unchecked="HandPickedInput_Changed" Indeterminate="HandPickedInput_Changed"/>
|
Checked="HandPickedInput_Changed" Unchecked="HandPickedInput_Changed" Indeterminate="HandPickedInput_Changed"/>
|
||||||
|
|
||||||
|
<RadioButton x:Name="UnloadingDumperInput" GroupName="Unloading" IsChecked="{Binding IsUnloadingDumper, Mode=TwoWay}"
|
||||||
|
Content="Planenw./Kipper" Margin="10,75,0,0" Grid.Column="2"
|
||||||
|
VerticalAlignment="Top" HorizontalAlignment="Left"
|
||||||
|
Checked="UnloadingInput_Checked" Unchecked="UnloadingInput_Unchecked"/>
|
||||||
|
<RadioButton x:Name="UnloadingPumpedInput" GroupName="Unloading" IsChecked="{Binding IsUnloadingPumped, Mode=TwoWay}"
|
||||||
|
Content="Lesewagen" Margin="10,95,0,0" Grid.Column="2"
|
||||||
|
VerticalAlignment="Top" HorizontalAlignment="Left"
|
||||||
|
Checked="UnloadingInput_Checked" Unchecked="UnloadingInput_Unchecked"/>
|
||||||
|
<RadioButton x:Name="UnloadingBoxInput" GroupName="Unloading" IsChecked="{Binding IsUnloadingBox, Mode=TwoWay}"
|
||||||
|
Content="Kiste(n)" Margin="10,115,0,0" Grid.Column="2"
|
||||||
|
VerticalAlignment="Top" HorizontalAlignment="Left"
|
||||||
|
Checked="UnloadingInput_Checked" Unchecked="UnloadingInput_Unchecked"/>
|
||||||
|
<RadioButton x:Name="UnloadingOtherInput" GroupName="Unloading" IsChecked="{Binding IsUnloadingOther, Mode=TwoWay}"
|
||||||
|
Content="Andere/Unbek." Margin="10,135,0,0" Grid.Column="2"
|
||||||
|
VerticalAlignment="Top" HorizontalAlignment="Left"
|
||||||
|
Checked="UnloadingInput_Checked" Unchecked="UnloadingInput_Unchecked"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</GroupBox>
|
</GroupBox>
|
||||||
|
|
||||||
<GroupBox Header="Herkunft" Grid.Column="0" Grid.Row="3" Margin="5,5,5,10">
|
<GroupBox Header="Herkunft" Grid.Column="0" Grid.Row="4" Margin="5,5,5,10">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="100"/>
|
<ColumnDefinition Width="100"/>
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ namespace Elwig.Windows {
|
|||||||
if (App.Client.IsMatzen) {
|
if (App.Client.IsMatzen) {
|
||||||
RequiredInputs = [.. RequiredInputs, ModifiersInput];
|
RequiredInputs = [.. RequiredInputs, ModifiersInput];
|
||||||
}
|
}
|
||||||
|
ModifiersInput.ItemTemplate = (DataTemplate)ModifiersInput.FindResource("PublicModifierTemplate");
|
||||||
} else {
|
} else {
|
||||||
WeighingManualButton.Visibility = Visibility.Hidden;
|
WeighingManualButton.Visibility = Visibility.Hidden;
|
||||||
WeighingAButton.Visibility = Visibility.Hidden;
|
WeighingAButton.Visibility = Visibility.Hidden;
|
||||||
@@ -134,7 +135,7 @@ namespace Elwig.Windows {
|
|||||||
NewDeliveryButton_Click(null, null);
|
NewDeliveryButton_Click(null, null);
|
||||||
using var ctx = new AppDbContext();
|
using var ctx = new AppDbContext();
|
||||||
if (ctx.Seasons.Find(Utils.CurrentYear) == null) {
|
if (ctx.Seasons.Find(Utils.CurrentYear) == null) {
|
||||||
MessageBox.Show("Die Saison für das aktuelle Jahr wurde noch nicht erstellt. Neue Lieferungen können nicht abgespeichert werden.",
|
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);
|
"Saison noch nicht erstellt", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -259,7 +260,7 @@ namespace Elwig.Windows {
|
|||||||
await App.Client.UpdateValues();
|
await App.Client.UpdateValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void Menu_Statistic_Locality_SaveFilters_Click(object sender, RoutedEventArgs evt)=>
|
private async void Menu_Statistic_Locality_SaveFilters_Click(object sender, RoutedEventArgs evt) =>
|
||||||
await ViewModel.GenerateLocalityStatistics(DeliveryService.ExportSubject.FromFilters);
|
await ViewModel.GenerateLocalityStatistics(DeliveryService.ExportSubject.FromFilters);
|
||||||
|
|
||||||
private async void Menu_DeliveryDepreciationList_SaveFilters_Click(object sender, RoutedEventArgs evt) =>
|
private async void Menu_DeliveryDepreciationList_SaveFilters_Click(object sender, RoutedEventArgs evt) =>
|
||||||
@@ -279,6 +280,9 @@ namespace Elwig.Windows {
|
|||||||
private async void Menu_DeliveryDepreciationList_PrintSeason_Click(object sender, RoutedEventArgs evt) =>
|
private async void Menu_DeliveryDepreciationList_PrintSeason_Click(object sender, RoutedEventArgs evt) =>
|
||||||
await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromSeason, ExportMode.Print);
|
await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromSeason, ExportMode.Print);
|
||||||
|
|
||||||
|
private async void Menu_DeliveryDataList_SaveFilters_Click(object sender, RoutedEventArgs evt) =>
|
||||||
|
await ViewModel.GenerateDeliveryDataList(DeliveryService.ExportSubject.FromFilters, ExportMode.SaveList);
|
||||||
|
|
||||||
private void Menu_Settings_EnableFreeEditing_Checked(object sender, RoutedEventArgs evt) {
|
private void Menu_Settings_EnableFreeEditing_Checked(object sender, RoutedEventArgs evt) {
|
||||||
if (IsEditing || IsCreating) {
|
if (IsEditing || IsCreating) {
|
||||||
DateInput.IsReadOnly = false;
|
DateInput.IsReadOnly = false;
|
||||||
@@ -319,11 +323,16 @@ namespace Elwig.Windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (App.Config.WeighingMode == WeighingMode.Box) {
|
if (App.Config.WeighingMode == WeighingMode.Box) {
|
||||||
LesewagenInput.IsEnabled = false;
|
SetDefaultValue(UnloadingDumperInput, false);
|
||||||
SetDefaultValue(LesewagenInput, false);
|
SetDefaultValue(UnloadingPumpedInput, false);
|
||||||
|
SetDefaultValue(UnloadingBoxInput, true);
|
||||||
|
SetDefaultValue(UnloadingOtherInput, false);
|
||||||
|
UnloadingBoxInput.IsChecked = true;
|
||||||
} else {
|
} else {
|
||||||
LesewagenInput.IsEnabled = true;
|
UnsetDefaultValue(UnloadingDumperInput);
|
||||||
UnsetDefaultValue(LesewagenInput);
|
UnsetDefaultValue(UnloadingPumpedInput);
|
||||||
|
UnsetDefaultValue(UnloadingBoxInput);
|
||||||
|
UnsetDefaultValue(UnloadingOtherInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (App.Config.WeighingMode != WeighingMode.Net) {
|
if (App.Config.WeighingMode != WeighingMode.Net) {
|
||||||
@@ -353,8 +362,8 @@ namespace Elwig.Windows {
|
|||||||
ClearDefaultValues();
|
ClearDefaultValues();
|
||||||
|
|
||||||
ViewModel.IsNetWeight = App.Config.WeighingMode == WeighingMode.Net;
|
ViewModel.IsNetWeight = App.Config.WeighingMode == WeighingMode.Net;
|
||||||
ViewModel.IsLesewagen = false;
|
|
||||||
ViewModel.IsHandPicked = App.Config.WeighingMode != WeighingMode.Net ? true : null;
|
ViewModel.IsHandPicked = App.Config.WeighingMode != WeighingMode.Net ? true : null;
|
||||||
|
ViewModel.Unloading = null;
|
||||||
ViewModel.IsGebunden = null;
|
ViewModel.IsGebunden = null;
|
||||||
InitialDefaultInputs();
|
InitialDefaultInputs();
|
||||||
|
|
||||||
@@ -1168,6 +1177,19 @@ namespace Elwig.Windows {
|
|||||||
WeighingDButton.IsEnabled = n > 3 && App.CommandScales[3].IsReady;
|
WeighingDButton.IsEnabled = n > 3 && App.CommandScales[3].IsReady;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateScales() {
|
||||||
|
if (!ViewModel.IsReceipt) return;
|
||||||
|
foreach (var s in App.EventScales) {
|
||||||
|
s.WeighingEvent -= Scale_Weighing;
|
||||||
|
s.WeighingEvent += Scale_Weighing;
|
||||||
|
}
|
||||||
|
if (WeighingManualButton.IsEnabled) {
|
||||||
|
EnableWeighingButtons();
|
||||||
|
} else {
|
||||||
|
DisableWeighingButtons();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task UpdateLsNr() {
|
private async Task UpdateLsNr() {
|
||||||
if (string.IsNullOrEmpty(ViewModel.Date) || ViewModel.Branch == null) {
|
if (string.IsNullOrEmpty(ViewModel.Date) || ViewModel.Branch == null) {
|
||||||
ViewModel.LsNr = "";
|
ViewModel.LsNr = "";
|
||||||
@@ -1311,42 +1333,73 @@ namespace Elwig.Windows {
|
|||||||
|
|
||||||
private void ModifiersInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
|
private void ModifiersInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
|
||||||
if (!IsEditing && !IsCreating) return;
|
if (!IsEditing && !IsCreating) return;
|
||||||
var mod = ModifiersInput.SelectedItems.Cast<Modifier>().ToList();
|
var mod = ViewModel.Modifiers.ToList();
|
||||||
|
var source = ViewModel.ModifiersSource.ToList();
|
||||||
if (App.Client.IsMatzen) {
|
if (App.Client.IsMatzen) {
|
||||||
var kl = mod.Where(m => m.Name.StartsWith("Klasse "));
|
var kl = mod.Where(m => m.Name.StartsWith("Klasse "));
|
||||||
if (kl.Count() > 1) {
|
if (kl.Count() > 1) {
|
||||||
App.MainDispatcher.BeginInvoke(() => {
|
App.MainDispatcher.BeginInvoke(() => {
|
||||||
foreach (var r in kl.Take(kl.Count() - 1))
|
foreach (var r in kl.Take(kl.Count() - 1))
|
||||||
ModifiersInput.SelectedItems.Remove(r);
|
ViewModel.Modifiers.Remove(r);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (App.Client.IsWinzerkeller) {
|
} else if (App.Client.IsWinzerkeller) {
|
||||||
|
if (source.Any(m => m.Name.Contains("Lesewagen"))) {
|
||||||
if (mod.Any(m => m.Name.Contains("Lesewagen"))) {
|
if (mod.Any(m => m.Name.Contains("Lesewagen"))) {
|
||||||
ViewModel.IsLesewagen = true;
|
ViewModel.IsUnloadingPumped = true;
|
||||||
} else {
|
} else {
|
||||||
ViewModel.IsLesewagen = false;
|
ViewModel.IsUnloadingPumped = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (App.Client.IsBaden) {
|
||||||
|
if (source.Any(m => m.Name.Contains("Kiste"))) {
|
||||||
|
if (mod.Any(m => m.Name.Contains("Kiste"))) {
|
||||||
|
ViewModel.IsUnloadingBox = true;
|
||||||
|
} else {
|
||||||
|
ViewModel.IsUnloadingBox = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LesewagenInput_Changed(object sender, RoutedEventArgs evt) {
|
private void UnloadingInput_Checked(object sender, RoutedEventArgs evt) {
|
||||||
if (!IsEditing && !IsCreating) return;
|
if (!IsEditing && !IsCreating) return;
|
||||||
var mod = ModifiersInput.SelectedItems.Cast<Modifier>().ToList();
|
var mod = ViewModel.Modifiers.ToList();
|
||||||
var source = ModifiersInput.ItemsSource.Cast<Modifier>().ToList();
|
var source = ViewModel.ModifiersSource.ToList();
|
||||||
var lw = LesewagenInput.IsChecked == true;
|
|
||||||
if (App.Client.IsMatzen) {
|
if (App.Client.IsMatzen) {
|
||||||
var kl = mod.Where(m => m.Name.StartsWith("Klasse ")).Select(m => m.ModId).LastOrDefault("A")[0];
|
var kl = mod.Where(m => m.Name.StartsWith("Klasse ")).Select(m => m.ModId).LastOrDefault("_")[0];
|
||||||
if (lw) kl++; else kl--;
|
if (ViewModel.IsUnloadingPumped && (kl == 'A' || kl == '_')) {
|
||||||
var newKl = source.FirstOrDefault(m => m.ModId == kl.ToString());
|
kl = 'B';
|
||||||
if (newKl != null) ModifiersInput.SelectedItems.Add(newKl);
|
} else if (ViewModel.IsUnloadingDumper && kl == '_') {
|
||||||
|
kl = 'A';
|
||||||
|
} else {
|
||||||
|
kl = '_';
|
||||||
|
}
|
||||||
|
var newKl = source.FirstOrDefault(m => m?.ModId == kl.ToString(), null);
|
||||||
|
if (newKl != null) ViewModel.Modifiers.Add(newKl);
|
||||||
} else if (App.Client.IsWinzerkeller) {
|
} else if (App.Client.IsWinzerkeller) {
|
||||||
if (lw && !mod.Any(m => m.Name.Contains("Lesewagen"))) {
|
if (source.Any(m => m.Name.Contains("Lesewagen"))) {
|
||||||
ModifiersInput.SelectedItems.Add(source.First(m => m.Name.Contains("Lesewagen")));
|
if (ViewModel.IsUnloadingPumped && !mod.Any(m => m.Name.Contains("Lesewagen"))) {
|
||||||
} else if (!lw && mod.Any(m => m.Name.Contains("Lesewagen"))) {
|
ViewModel.Modifiers.Add(source.First(m => m.Name.Contains("Lesewagen")));
|
||||||
ModifiersInput.SelectedItems.Remove(mod.First(m => m.Name.Contains("Lesewagen")));
|
} else if (!ViewModel.IsUnloadingPumped && mod.Any(m => m.Name.Contains("Lesewagen"))) {
|
||||||
|
ViewModel.Modifiers.Remove(mod.First(m => m.Name.Contains("Lesewagen")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CheckBox_Changed(sender, evt);
|
} else if (App.Client.IsBaden) {
|
||||||
|
if (source.Any(m => m.Name.Contains("Kiste"))) {
|
||||||
|
if (ViewModel.IsUnloadingBox && !mod.Any(m => m.Name.Contains("Kiste"))) {
|
||||||
|
ViewModel.Modifiers.Add(source.First(m => m.Name.Contains("Kiste")));
|
||||||
|
} else if (!ViewModel.IsUnloadingBox && mod.Any(m => m.Name.Contains("Kiste"))) {
|
||||||
|
ViewModel.Modifiers.Remove(mod.First(m => m.Name.Contains("Kiste")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RadioButton_Changed(sender, evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnloadingInput_Unchecked(object sender, RoutedEventArgs evt) {
|
||||||
|
if (!IsEditing && !IsCreating) return;
|
||||||
|
RadioButton_Changed(sender, evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateWineOrigin() {
|
private void UpdateWineOrigin() {
|
||||||
|
|||||||
@@ -302,6 +302,9 @@
|
|||||||
<Button x:Name="GenerateButton" Content="Generieren"
|
<Button x:Name="GenerateButton" Content="Generieren"
|
||||||
Grid.Row="0" Grid.Column="0" FontSize="14"
|
Grid.Row="0" Grid.Column="0" FontSize="14"
|
||||||
Click="GenerateButton_Click"/>
|
Click="GenerateButton_Click"/>
|
||||||
|
<Button x:Name="AbortButton" Content="Abbrechen" Visibility="Hidden" IsEnabled="False"
|
||||||
|
Grid.Row="0" Grid.Column="0" FontSize="14"
|
||||||
|
Click="AbortButton_Click"/>
|
||||||
<ProgressBar x:Name="ProgressBar"
|
<ProgressBar x:Name="ProgressBar"
|
||||||
Grid.Row="2" Grid.Column="0" SnapsToDevicePixels="True"/>
|
Grid.Row="2" Grid.Column="0" SnapsToDevicePixels="True"/>
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using System.Collections.ObjectModel;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
@@ -61,6 +62,8 @@ namespace Elwig.Windows {
|
|||||||
protected Dictionary<Member, List<Document>>? PrintMemberDocuments;
|
protected Dictionary<Member, List<Document>>? PrintMemberDocuments;
|
||||||
protected Dictionary<Member, List<Document>>? EmailDocuments;
|
protected Dictionary<Member, List<Document>>? EmailDocuments;
|
||||||
|
|
||||||
|
private CancellationTokenSource? CancelGeneration;
|
||||||
|
|
||||||
public static readonly DependencyProperty PostalAllCountProperty = DependencyProperty.Register(nameof(PostalAllCount), typeof(int), typeof(MailWindow));
|
public static readonly DependencyProperty PostalAllCountProperty = DependencyProperty.Register(nameof(PostalAllCount), typeof(int), typeof(MailWindow));
|
||||||
public int PostalAllCount {
|
public int PostalAllCount {
|
||||||
get => (int)GetValue(PostalAllCountProperty);
|
get => (int)GetValue(PostalAllCountProperty);
|
||||||
@@ -594,20 +597,32 @@ namespace Elwig.Windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void Window_Closed(object sender, EventArgs evt) {
|
private void Window_Closed(object sender, EventArgs evt) {
|
||||||
|
CancelGeneration?.Dispose();
|
||||||
DisposeDocs();
|
DisposeDocs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void AbortButton_Click(object sender, RoutedEventArgs evt) {
|
||||||
|
AbortButton.IsEnabled = false;
|
||||||
|
CancelGeneration?.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
private async void GenerateButton_Click(object sender, RoutedEventArgs evt) {
|
private async void GenerateButton_Click(object sender, RoutedEventArgs evt) {
|
||||||
LockInputs();
|
LockInputs();
|
||||||
PreviewButton.IsEnabled = false;
|
PreviewButton.IsEnabled = false;
|
||||||
PrintButton.IsEnabled = false;
|
PrintButton.IsEnabled = false;
|
||||||
EmailButton.IsEnabled = false;
|
EmailButton.IsEnabled = false;
|
||||||
Mouse.OverrideCursor = Cursors.Wait;
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
|
AbortButton.IsEnabled = true;
|
||||||
|
AbortButton.Visibility = Visibility.Visible;
|
||||||
GenerateButton.IsEnabled = false;
|
GenerateButton.IsEnabled = false;
|
||||||
|
GenerateButton.Visibility = Visibility.Hidden;
|
||||||
|
|
||||||
DisposeDocs();
|
DisposeDocs();
|
||||||
await UpdateClientParameters();
|
await UpdateClientParameters();
|
||||||
|
|
||||||
|
CancelGeneration?.Dispose();
|
||||||
|
CancelGeneration = new();
|
||||||
|
|
||||||
using var ctx = new AppDbContext();
|
using var ctx = new AppDbContext();
|
||||||
|
|
||||||
var doublePaged = DoublePagedInput.IsChecked == true;
|
var doublePaged = DoublePagedInput.IsChecked == true;
|
||||||
@@ -662,6 +677,9 @@ namespace Elwig.Windows {
|
|||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
UnlockInputs();
|
UnlockInputs();
|
||||||
GenerateButton.IsEnabled = true;
|
GenerateButton.IsEnabled = true;
|
||||||
|
GenerateButton.Visibility = Visibility.Visible;
|
||||||
|
AbortButton.IsEnabled = false;
|
||||||
|
AbortButton.Visibility = Visibility.Hidden;
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -679,6 +697,9 @@ namespace Elwig.Windows {
|
|||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
UnlockInputs();
|
UnlockInputs();
|
||||||
GenerateButton.IsEnabled = true;
|
GenerateButton.IsEnabled = true;
|
||||||
|
GenerateButton.Visibility = Visibility.Visible;
|
||||||
|
AbortButton.IsEnabled = false;
|
||||||
|
AbortButton.Visibility = Visibility.Hidden;
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -733,12 +754,27 @@ namespace Elwig.Windows {
|
|||||||
}).ToList()
|
}).ToList()
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
||||||
|
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.\n\nSoll mit dem Generieren fortgefahren werden?",
|
||||||
|
"Vorläufige Dokumente", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
|
||||||
|
if (res != MessageBoxResult.OK) {
|
||||||
|
UnlockInputs();
|
||||||
|
GenerateButton.IsEnabled = true;
|
||||||
|
GenerateButton.Visibility = Visibility.Visible;
|
||||||
|
AbortButton.IsEnabled = false;
|
||||||
|
AbortButton.Visibility = Visibility.Hidden;
|
||||||
|
Mouse.OverrideCursor = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var printMode = PostalAllInput.IsChecked == true ? 3 :
|
var printMode = PostalAllInput.IsChecked == true ? 3 :
|
||||||
PostalWishInput.IsChecked == true ? 2 :
|
PostalWishInput.IsChecked == true ? 2 :
|
||||||
PostalNoEmailInput.IsChecked == true ? 1 : 0;
|
PostalNoEmailInput.IsChecked == true ? 1 : 0;
|
||||||
var emailMode = EmailAllInput.IsChecked == true ? 2 : EmailWishInput.IsChecked == true ? 1 : 0;
|
var emailMode = EmailAllInput.IsChecked == true ? 2 : EmailWishInput.IsChecked == true ? 1 : 0;
|
||||||
|
|
||||||
double printNum = printMode == 3 ? PostalAllCount : printMode == 2 ? PostalWishCount : printMode == 2 ? PostalNoEmailCount : 0;
|
double printNum = printMode == 3 ? PostalAllCount : printMode == 2 ? PostalWishCount : printMode == 1 ? PostalNoEmailCount : 0;
|
||||||
double emailNum = emailMode == 2 ? EmailAllCount : emailMode == 1 ? EmailWishCount : 0;
|
double emailNum = emailMode == 2 ? EmailAllCount : emailMode == 1 ? EmailWishCount : 0;
|
||||||
double totalNum = printNum + emailNum;
|
double totalNum = printNum + emailNum;
|
||||||
|
|
||||||
@@ -747,7 +783,7 @@ namespace Elwig.Windows {
|
|||||||
.ToDictionary(d => d.Member, m => {
|
.ToDictionary(d => d.Member, m => {
|
||||||
var docs = m.Docs.Select(d => d.Doc).ToList();
|
var docs = m.Docs.Select(d => d.Doc).ToList();
|
||||||
foreach (var doc in docs) {
|
foreach (var doc in docs) {
|
||||||
doc!.DoublePaged = false;
|
doc!.IsDoublePaged = false;
|
||||||
if (doc is BusinessDocument b) {
|
if (doc is BusinessDocument b) {
|
||||||
b.IncludeSender = false;
|
b.IncludeSender = false;
|
||||||
b.Location = location;
|
b.Location = location;
|
||||||
@@ -759,7 +795,7 @@ namespace Elwig.Windows {
|
|||||||
try {
|
try {
|
||||||
foreach (var item1 in email.Select((e, i) => new { Index = i, e.Key, e.Value })) {
|
foreach (var item1 in email.Select((e, i) => new { Index = i, e.Key, e.Value })) {
|
||||||
foreach (var item2 in item1.Value.Select((d, i) => new { Index = i, Doc = d })) {
|
foreach (var item2 in item1.Value.Select((d, i) => new { Index = i, Doc = d })) {
|
||||||
await item2.Doc.Generate(new Progress<double>(v => {
|
await item2.Doc.Generate(CancelGeneration.Token, new Progress<double>(v => {
|
||||||
ProgressBar.Value = v * (item2.Index + 1) / item1.Value.Count / totalNum + 100.0 * item1.Index / totalNum;
|
ProgressBar.Value = v * (item2.Index + 1) / item1.Value.Count / totalNum + 100.0 * item1.Index / totalNum;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -768,6 +804,9 @@ namespace Elwig.Windows {
|
|||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
UnlockInputs();
|
UnlockInputs();
|
||||||
GenerateButton.IsEnabled = true;
|
GenerateButton.IsEnabled = true;
|
||||||
|
GenerateButton.Visibility = Visibility.Visible;
|
||||||
|
AbortButton.IsEnabled = false;
|
||||||
|
AbortButton.Visibility = Visibility.Hidden;
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -788,7 +827,7 @@ namespace Elwig.Windows {
|
|||||||
docs.Insert(0, new Letterhead(m.Member));
|
docs.Insert(0, new Letterhead(m.Member));
|
||||||
}
|
}
|
||||||
docs.ForEach(doc => {
|
docs.ForEach(doc => {
|
||||||
doc.DoublePaged = doublePaged;
|
doc.IsDoublePaged = doublePaged;
|
||||||
if (doc is BusinessDocument b)
|
if (doc is BusinessDocument b)
|
||||||
b.Location = location;
|
b.Location = location;
|
||||||
});
|
});
|
||||||
@@ -801,8 +840,8 @@ namespace Elwig.Windows {
|
|||||||
if (printDocs.Count > 0) {
|
if (printDocs.Count > 0) {
|
||||||
try {
|
try {
|
||||||
var print = Document.Merge(printDocs);
|
var print = Document.Merge(printDocs);
|
||||||
print.DoublePaged = doublePaged;
|
print.IsDoublePaged = doublePaged;
|
||||||
await print.Generate(new Progress<double>(v => {
|
await print.Generate(CancelGeneration.Token, new Progress<double>(v => {
|
||||||
ProgressBar.Value = 100.0 * emailNum / totalNum + v * printNum / totalNum;
|
ProgressBar.Value = 100.0 * emailNum / totalNum + v * printNum / totalNum;
|
||||||
}));
|
}));
|
||||||
PrintDocument = print;
|
PrintDocument = print;
|
||||||
@@ -811,6 +850,9 @@ namespace Elwig.Windows {
|
|||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
UnlockInputs();
|
UnlockInputs();
|
||||||
GenerateButton.IsEnabled = true;
|
GenerateButton.IsEnabled = true;
|
||||||
|
GenerateButton.Visibility = Visibility.Visible;
|
||||||
|
AbortButton.IsEnabled = false;
|
||||||
|
AbortButton.Visibility = Visibility.Hidden;
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -822,10 +864,13 @@ namespace Elwig.Windows {
|
|||||||
|
|
||||||
UnlockInputs();
|
UnlockInputs();
|
||||||
GenerateButton.IsEnabled = true;
|
GenerateButton.IsEnabled = true;
|
||||||
|
GenerateButton.Visibility = Visibility.Visible;
|
||||||
|
AbortButton.IsEnabled = false;
|
||||||
|
AbortButton.Visibility = Visibility.Hidden;
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
PreviewButton.IsEnabled = true;
|
PreviewButton.IsEnabled = true;
|
||||||
PrintButton.IsEnabled = PrintDocument != null;
|
PrintButton.IsEnabled = PrintDocument != null && !hasPreviewDocs;
|
||||||
EmailButton.IsEnabled = EmailDocuments != null && App.Config.Smtp != null;
|
EmailButton.IsEnabled = EmailDocuments != null && !hasPreviewDocs && App.Config.Smtp != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void PreviewButton_Click(object sender, RoutedEventArgs evt) {
|
private async void PreviewButton_Click(object sender, RoutedEventArgs evt) {
|
||||||
@@ -929,13 +974,13 @@ namespace Elwig.Windows {
|
|||||||
await Utils.AddSentMails([(
|
await Utils.AddSentMails([(
|
||||||
"email", m.MgNr, m.AdministrativeName,
|
"email", m.MgNr, m.AdministrativeName,
|
||||||
m.EmailAddresses.OrderBy(a => a.Nr).Select(a => a.Address).ToArray(),
|
m.EmailAddresses.OrderBy(a => a.Nr).Select(a => a.Address).ToArray(),
|
||||||
subject,
|
subject, docs.Select(d => d.Title).ToArray()
|
||||||
docs.Select(d => d.Title).ToArray()
|
|
||||||
)]);
|
)]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
MessageBox.Show("Erfolgreich alle E-Mails verschickt!", "Rundschreiben verschicken", MessageBoxButton.OK, MessageBoxImage.Information);
|
MessageBox.Show("Erfolgreich alle E-Mails verschickt!\n\nEs kann einige Minuten dauern, bis die E-Mails in den Posteingängen der Empfänger aufscheinen.", "Rundschreiben verschicken",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
} catch (Exception exc) {
|
} catch (Exception exc) {
|
||||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -954,7 +999,7 @@ namespace Elwig.Windows {
|
|||||||
AvaiableDocumentsList.SelectedIndex = 1;
|
AvaiableDocumentsList.SelectedIndex = 1;
|
||||||
if (AvaiableDocumentsList.SelectedItem is not string s || SelectedDocs.Any(d => d.Type == DocType.DeliveryConfirmation))
|
if (AvaiableDocumentsList.SelectedItem is not string s || SelectedDocs.Any(d => d.Type == DocType.DeliveryConfirmation))
|
||||||
return;
|
return;
|
||||||
SelectedDocs.Add(new(DocType.DeliveryConfirmation, s, (Year, DocumentNonDeliverersInput.IsChecked == true)));
|
SelectedDocs.Add(new(DocType.DeliveryConfirmation, s, Year));
|
||||||
SelectedDocumentsList.SelectedIndex = SelectedDocs.Count - 1;
|
SelectedDocumentsList.SelectedIndex = SelectedDocs.Count - 1;
|
||||||
RecipientsDeliveryMembersInput.IsChecked = true;
|
RecipientsDeliveryMembersInput.IsChecked = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,23 @@
|
|||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
<MenuItem Header="Synchronisieren" x:Name="Menu_Sync" IsEnabled="false">
|
||||||
|
<MenuItem x:Name="Menu_Sync_Download" Header="Mitgliederdaten und Lieferungen herunterladen" Click="Menu_Sync_Download_Click">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text=""/>
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem x:Name="Menu_Sync_UploadBranchDeliveries" Header="Lieferungen dieser Saison/Zweigstelle hochladen" Click="Menu_Sync_UploadBranchDeliveries_Click">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text=""/>
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem x:Name="Menu_Sync_UploadModified" Header="Geänderte Mitglieder und Lieferungen hochladen" Click="Menu_Sync_UploadModified_Click">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text=""/>
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
</MenuItem>
|
||||||
<MenuItem Header="Waage">
|
<MenuItem Header="Waage">
|
||||||
<MenuItem Header="Datum und Uhrzeit setzen" Click="Menu_Scale_SetDateTime_Click">
|
<MenuItem Header="Datum und Uhrzeit setzen" Click="Menu_Scale_SetDateTime_Click">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
@@ -174,16 +191,18 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button x:Name="DownloadButton" Click="DownloadButton_Click"
|
<Button x:Name="SyncButton" Click="SyncButton_Click"
|
||||||
Margin="310,135,0,0" Padding="0.375,0.5,0,0" Height="30" Width="30"
|
|
||||||
Content="" FontFamily="Segoe MDL2 Assets" FontSize="16"
|
|
||||||
HorizontalContentAlignment="Center"
|
|
||||||
ToolTip="Lieferungen und Mitgliederdaten anderer Zweigstellen herunterladen"/>
|
|
||||||
<Button x:Name="UploadButton" Click="UploadButton_Click"
|
|
||||||
Margin="375,135,0,0" Padding="1.0,0.5,0,0" Height="30" Width="30"
|
Margin="375,135,0,0" Padding="1.0,0.5,0,0" Height="30" Width="30"
|
||||||
Content="" FontFamily="Segoe MDL2 Assets" FontSize="16"
|
FontFamily="Segoe MDL2 Assets" FontSize="16"
|
||||||
HorizontalContentAlignment="Center"
|
HorizontalContentAlignment="Center"
|
||||||
ToolTip="Lieferungen dieser Zweigstelle hochladen"/>
|
ToolTip="Geänderte Mitgliederdaten und Lieferungen synchronisieren">
|
||||||
|
<Button.Content>
|
||||||
|
<Grid TextElement.FontFamily="Segoe MDL2 Assets">
|
||||||
|
<TextBlock x:Name="SyncButton_1" Text=""/>
|
||||||
|
<TextBlock x:Name="SyncButton_2" Text="" Foreground="DarkOrange"/>
|
||||||
|
</Grid>
|
||||||
|
</Button.Content>
|
||||||
|
</Button>
|
||||||
|
|
||||||
<Expander x:Name="SeasonFinish" Header="Leseabschluss" SnapsToDevicePixels="True"
|
<Expander x:Name="SeasonFinish" Header="Leseabschluss" SnapsToDevicePixels="True"
|
||||||
Expanded="SeasonFinish_Expanded" Collapsed="SeasonFinish_Collapsed"
|
Expanded="SeasonFinish_Expanded" Collapsed="SeasonFinish_Collapsed"
|
||||||
|
|||||||
@@ -2,26 +2,30 @@ using Elwig.Helpers;
|
|||||||
using Elwig.Helpers.Billing;
|
using Elwig.Helpers.Billing;
|
||||||
using Elwig.Helpers.Export;
|
using Elwig.Helpers.Export;
|
||||||
using Elwig.Models.Dtos;
|
using Elwig.Models.Dtos;
|
||||||
|
using Elwig.Services;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Threading;
|
||||||
|
|
||||||
namespace Elwig.Windows {
|
namespace Elwig.Windows {
|
||||||
public partial class MainWindow : ContextWindow {
|
public partial class MainWindow : ContextWindow {
|
||||||
|
|
||||||
|
private readonly DispatcherTimer _syncTimer = new() { Interval = TimeSpan.FromHours(1) };
|
||||||
|
|
||||||
public MainWindow() {
|
public MainWindow() {
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
var v = Assembly.GetExecutingAssembly().GetName().Version;
|
var v = Assembly.GetExecutingAssembly().GetName().Version;
|
||||||
@@ -29,14 +33,21 @@ namespace Elwig.Windows {
|
|||||||
if (App.Client.Client == null) VersionField.Text += " (Unbekannt)";
|
if (App.Client.Client == null) VersionField.Text += " (Unbekannt)";
|
||||||
Menu_Help_Update.IsEnabled = App.Config.UpdateUrl != null;
|
Menu_Help_Update.IsEnabled = App.Config.UpdateUrl != null;
|
||||||
Menu_Help_Smtp.IsEnabled = App.Config.Smtp != null;
|
Menu_Help_Smtp.IsEnabled = App.Config.Smtp != null;
|
||||||
DownloadButton.Visibility = App.Config.SyncUrl != null ? Visibility.Visible : Visibility.Hidden;
|
Menu_Sync.IsEnabled = App.Config.SyncUrl != null;
|
||||||
UploadButton.Visibility = App.Config.SyncUrl != null ? Visibility.Visible : Visibility.Hidden;
|
SyncButton.Visibility = App.Config.SyncUrl != null ? Visibility.Visible : Visibility.Hidden;
|
||||||
Menu_Database_Upload.IsEnabled = App.Config.SyncUrl != null;
|
Menu_Database_Upload.IsEnabled = App.Config.SyncUrl != null;
|
||||||
Menu_Database_Download.IsEnabled = App.Config.SyncUrl != null;
|
Menu_Database_Download.IsEnabled = App.Config.SyncUrl != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Window_Loaded(object sender, RoutedEventArgs evt) {
|
private void Window_Loaded(object sender, RoutedEventArgs evt) {
|
||||||
SeasonInput.Value = Utils.CurrentLastSeason;
|
SeasonInput.Value = Utils.CurrentLastSeason;
|
||||||
|
|
||||||
|
if (Utils.HasInternetConnectivity()) {
|
||||||
|
CheckSync(200);
|
||||||
|
}
|
||||||
|
NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged;
|
||||||
|
_syncTimer.Tick += new EventHandler(OnSyncTimer);
|
||||||
|
_syncTimer.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Window_Closing(object sender, CancelEventArgs evt) {
|
private void Window_Closing(object sender, CancelEventArgs evt) {
|
||||||
@@ -195,92 +206,43 @@ namespace Elwig.Windows {
|
|||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void DownloadButton_Click(object sender, RoutedEventArgs evt) {
|
private async void SyncButton_Click(object sender, RoutedEventArgs evt) {
|
||||||
if (App.Config.SyncUrl == null)
|
if (App.Config.SyncUrl == null)
|
||||||
return;
|
return;
|
||||||
Mouse.OverrideCursor = Cursors.Wait;
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
await Task.Run(async () => {
|
await Task.Run(async () => {
|
||||||
try {
|
await SyncService.Download(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
|
||||||
var data = await Utils.GetExportMetaData(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
|
await SyncService.UploadModified(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
|
||||||
var files = data
|
|
||||||
.Select(f => new {
|
|
||||||
Name = f!["name"]!.AsValue().GetValue<string>(),
|
|
||||||
Timestamp = f!["timestamp"] != null && DateTime.TryParseExact(f!["timestamp"]!.AsValue().GetValue<string>(), "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt) ? dt : (DateTime?)null,
|
|
||||||
ZwstId = f!["meta"]?["zwstid"]?.AsValue().GetValue<string>() ?? f!["zwstid"]?.AsValue().GetValue<string>(),
|
|
||||||
Device = f!["meta"]?["device"]?.AsValue().GetValue<string>(),
|
|
||||||
Url = f!["url"]!.AsValue().GetValue<string>(),
|
|
||||||
Size = f!["size"]!.AsValue().GetValue<long>(),
|
|
||||||
})
|
|
||||||
.Where(f => f.Timestamp >= new DateTime(Utils.CurrentLastSeason, 7, 1))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
var imported = await ElwigData.GetImportedFiles();
|
|
||||||
var import = files
|
|
||||||
.Where(f => f.Device != Environment.MachineName && !imported.Contains(f.Name))
|
|
||||||
.ToList();
|
|
||||||
var paths = new List<string>();
|
|
||||||
using (var client = Utils.GetHttpClient(App.Config.SyncUsername, App.Config.SyncPassword)) {
|
|
||||||
foreach (var f in import) {
|
|
||||||
var filename = Path.Combine(App.TempPath, f.Name);
|
|
||||||
using var stream = new FileStream(filename, FileMode.Create);
|
|
||||||
await client.DownloadAsync(f.Url, stream);
|
|
||||||
paths.Add(filename);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
} catch (TaskCanceledException exc) {
|
|
||||||
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Daten herunterladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
} catch (Exception exc) {
|
|
||||||
MessageBox.Show(exc.Message, "Daten herunterladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void UploadButton_Click(object sender, RoutedEventArgs evt) {
|
private async void Menu_Sync_Download_Click(object sender, RoutedEventArgs evt) {
|
||||||
if (App.Config.SyncUrl == null)
|
if (App.Config.SyncUrl == null)
|
||||||
return;
|
return;
|
||||||
Mouse.OverrideCursor = Cursors.Wait;
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
await Task.Run(async () => {
|
await Task.Run(async () => {
|
||||||
try {
|
await SyncService.Download(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
|
||||||
var path = Path.Combine(App.TempPath, $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip");
|
});
|
||||||
using var ctx = new AppDbContext();
|
Mouse.OverrideCursor = null;
|
||||||
var deliveries = await ctx.Deliveries
|
|
||||||
.Where(d => d.Year == Utils.CurrentLastSeason && d.ZwstId == App.ZwstId)
|
|
||||||
.Include(d => d.Parts)
|
|
||||||
.ThenInclude(p => p.PartModifiers)
|
|
||||||
.Include(d => d.Parts)
|
|
||||||
.ThenInclude(p => p.Kg)
|
|
||||||
.ThenInclude(k => k!.Gl)
|
|
||||||
.OrderBy(d => d.DateString)
|
|
||||||
.ThenBy(d => d.TimeString)
|
|
||||||
.ThenBy(d => d.LsNr)
|
|
||||||
.AsSplitQuery()
|
|
||||||
.ToListAsync();
|
|
||||||
var wbKgs = deliveries
|
|
||||||
.SelectMany(d => d.Parts)
|
|
||||||
.Where(p => p.Kg != null)
|
|
||||||
.Select(p => p.Kg!)
|
|
||||||
.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);
|
|
||||||
} else {
|
|
||||||
await ElwigData.Export(path, deliveries, wbKgs, [$"{Utils.CurrentLastSeason}", $"Zweigstelle {App.BranchName}"]);
|
|
||||||
await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
|
|
||||||
MessageBox.Show($"Hochladen von {deliveries.Count:N0} Lieferungen erfolgreich!", "Lieferungen hochladen",
|
|
||||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
|
||||||
}
|
}
|
||||||
} catch (HttpRequestException exc) {
|
|
||||||
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
private async void Menu_Sync_UploadBranchDeliveries_Click(object sender, RoutedEventArgs evt) {
|
||||||
} catch (TaskCanceledException exc) {
|
if (App.Config.SyncUrl == null)
|
||||||
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
return;
|
||||||
} catch (Exception exc) {
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
MessageBox.Show(exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
|
await Task.Run(async () => {
|
||||||
|
await SyncService.UploadBranchDeliveries(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
|
||||||
|
});
|
||||||
|
Mouse.OverrideCursor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void Menu_Sync_UploadModified_Click(object sender, RoutedEventArgs evt) {
|
||||||
|
if (App.Config.SyncUrl == null)
|
||||||
|
return;
|
||||||
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
|
await Task.Run(async () => {
|
||||||
|
await SyncService.UploadModified(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
|
||||||
});
|
});
|
||||||
Mouse.OverrideCursor = null;
|
Mouse.OverrideCursor = null;
|
||||||
}
|
}
|
||||||
@@ -394,9 +356,42 @@ namespace Elwig.Windows {
|
|||||||
App.FocusMailWindow();
|
App.FocusMailWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Task OnRenewContext(AppDbContext ctx) {
|
protected async override Task OnRenewContext(AppDbContext ctx) {
|
||||||
SeasonInput_TextChanged(null, null);
|
SeasonInput_TextChanged(null, null);
|
||||||
return Task.CompletedTask;
|
CheckSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSyncTimer(object? sender, EventArgs? evt) {
|
||||||
|
if (Utils.HasInternetConnectivity()) {
|
||||||
|
CheckSync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnNetworkAvailabilityChanged(object? sender, NetworkAvailabilityEventArgs evt) {
|
||||||
|
if (!evt.IsAvailable) return;
|
||||||
|
if (Utils.HasInternetConnectivity()) {
|
||||||
|
CheckSync(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void CheckSync(int delay = 0) {
|
||||||
|
if (App.Config.SyncUrl == null) return;
|
||||||
|
Utils.RunBackground("Daten Synchronisieren", async () => {
|
||||||
|
await Task.Delay(delay);
|
||||||
|
var ch = false;
|
||||||
|
using (var ctx = new AppDbContext()) {
|
||||||
|
ch = await SyncService.ChangesAvailable(ctx, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
|
||||||
|
}
|
||||||
|
await App.MainDispatcher.BeginInvoke(() => {
|
||||||
|
if (ch) {
|
||||||
|
SyncButton_1.Text = "\uEA6A";
|
||||||
|
SyncButton_2.Text = "\uEA81";
|
||||||
|
} else {
|
||||||
|
SyncButton_1.Text = "\uE895";
|
||||||
|
SyncButton_2.Text = "";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SeasonFinish_Expanded(object sender, RoutedEventArgs evt) {
|
private void SeasonFinish_Expanded(object sender, RoutedEventArgs evt) {
|
||||||
@@ -579,7 +574,7 @@ namespace Elwig.Windows {
|
|||||||
App.HintContextChange();
|
App.HintContextChange();
|
||||||
|
|
||||||
using var ctx = new AppDbContext();
|
using var ctx = new AppDbContext();
|
||||||
var tbl = await MemberDeliveryPerVarietyData.ForSeason(ctx.MemberDeliveryPerVariantRows, year);
|
var tbl = await MemberDeliveryYieldsPerVarietyData.ForSeason(ctx.MemberDeliveryPerVariantRows, year);
|
||||||
using var ods = new OdsFile(d.FileName);
|
using var ods = new OdsFile(d.FileName);
|
||||||
await ods.AddTable(tbl);
|
await ods.AddTable(tbl);
|
||||||
} catch (Exception exc) {
|
} catch (Exception exc) {
|
||||||
|
|||||||
@@ -164,6 +164,14 @@
|
|||||||
<MenuItem x:Name="Menu_Export_UploadAll" Header="...von allen Mitgliedern hochladen"
|
<MenuItem x:Name="Menu_Export_UploadAll" Header="...von allen Mitgliedern hochladen"
|
||||||
Click="Menu_Export_UploadAll_Click"/>
|
Click="Menu_Export_UploadAll_Click"/>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
<MenuItem Header="Kontakte">
|
||||||
|
<MenuItem x:Name="Menu_Contacts_Selected" Header="...von ausgewähltem Mitglied speichern..." IsEnabled="False"
|
||||||
|
Click="Menu_Contacts_Selected_Click"/>
|
||||||
|
<MenuItem x:Name="Menu_Contacts_Filters" Header="...aus Filtern speichern..."
|
||||||
|
Click="Menu_Contacts_Filters_Click"/>
|
||||||
|
<MenuItem x:Name="Menu_Contacts_All" Header="...von allen Mitgliedern speichern..."
|
||||||
|
Click="Menu_Contacts_All_Click"/>
|
||||||
|
</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|
||||||
<Grid Grid.Row="1" Margin="5,0,0,0">
|
<Grid Grid.Row="1" Margin="5,0,0,0">
|
||||||
|
|||||||
@@ -319,9 +319,11 @@ namespace Elwig.Windows {
|
|||||||
if (MemberList.SelectedItem is Member m) {
|
if (MemberList.SelectedItem is Member m) {
|
||||||
Menu_Export_ExportSelected.IsEnabled = !IsEditing && !IsCreating;
|
Menu_Export_ExportSelected.IsEnabled = !IsEditing && !IsCreating;
|
||||||
Menu_Export_UploadSelected.IsEnabled = !IsEditing && !IsCreating && App.Config.SyncUrl != null;
|
Menu_Export_UploadSelected.IsEnabled = !IsEditing && !IsCreating && App.Config.SyncUrl != null;
|
||||||
|
Menu_Contacts_Selected.IsEnabled = !IsEditing && !IsCreating;
|
||||||
} else {
|
} else {
|
||||||
Menu_Export_ExportSelected.IsEnabled = false;
|
Menu_Export_ExportSelected.IsEnabled = false;
|
||||||
Menu_Export_UploadSelected.IsEnabled = false;
|
Menu_Export_UploadSelected.IsEnabled = false;
|
||||||
|
Menu_Contacts_Selected.IsEnabled = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -649,6 +651,13 @@ namespace Elwig.Windows {
|
|||||||
await ViewModel.GenerateMemberList(MemberService.ExportSubject.Selected, ExportMode.Upload);
|
await ViewModel.GenerateMemberList(MemberService.ExportSubject.Selected, ExportMode.Upload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void Menu_Contacts_All_Click(object sender, RoutedEventArgs evt) =>
|
||||||
|
await ViewModel.GenerateMemberList(MemberService.ExportSubject.All, ExportMode.Vcf);
|
||||||
|
private async void Menu_Contacts_Filters_Click(object sender, RoutedEventArgs evt) =>
|
||||||
|
await ViewModel.GenerateMemberList(MemberService.ExportSubject.FromFilters, ExportMode.Vcf);
|
||||||
|
private async void Menu_Contacts_Selected_Click(object sender, RoutedEventArgs evt) =>
|
||||||
|
await ViewModel.GenerateMemberList(MemberService.ExportSubject.Selected, ExportMode.Vcf);
|
||||||
|
|
||||||
private async void Menu_List_Order_Click(object sender, RoutedEventArgs evt) {
|
private async void Menu_List_Order_Click(object sender, RoutedEventArgs evt) {
|
||||||
Menu_List.IsSubmenuOpen = true;
|
Menu_List.IsSubmenuOpen = true;
|
||||||
if (sender == Menu_List_OrderMgNr) {
|
if (sender == Menu_List_OrderMgNr) {
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ namespace Elwig.Windows {
|
|||||||
.OrderBy(v => v.AvNr)
|
.OrderBy(v => v.AvNr)
|
||||||
.Include(v => v.Season.Currency)
|
.Include(v => v.Season.Currency)
|
||||||
.ToListAsync());
|
.ToListAsync());
|
||||||
|
if (PaymentVariantList.SelectedItem == null && PaymentVariantList.Items.Count > 0) {
|
||||||
|
PaymentVariantList.SelectedIndex = PaymentVariantList.Items.Count - 1;
|
||||||
|
}
|
||||||
Update();
|
Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,6 +122,11 @@ namespace Elwig.Windows {
|
|||||||
private async void DeleteButton_Click(object sender, RoutedEventArgs evt) {
|
private async void DeleteButton_Click(object sender, RoutedEventArgs evt) {
|
||||||
if (PaymentVariantList.SelectedItem is not PaymentVar v || !v.TestVariant)
|
if (PaymentVariantList.SelectedItem is not PaymentVar v || !v.TestVariant)
|
||||||
return;
|
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)
|
||||||
|
return;
|
||||||
Mouse.OverrideCursor = Cursors.Wait;
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
try {
|
try {
|
||||||
await PaymentVariantService.DeletePaymentVariant(v.Year, v.AvNr);
|
await PaymentVariantService.DeletePaymentVariant(v.Year, v.AvNr);
|
||||||
|
|||||||
@@ -5,27 +5,35 @@
|
|||||||
Title="Datenbankabfragen - Elwig" Height="450" Width="800" MinWidth="400" MinHeight="300">
|
Title="Datenbankabfragen - Elwig" Height="450" Width="800" MinWidth="400" MinHeight="300">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="1*" MinHeight="100"/>
|
|
||||||
<RowDefinition Height="5"/>
|
<RowDefinition Height="5"/>
|
||||||
<RowDefinition Height="3*" MinHeight="100"/>
|
<RowDefinition Height="1*" MinHeight="50"/>
|
||||||
|
<RowDefinition Height="5"/>
|
||||||
|
<RowDefinition Height="1*" MinHeight="50"/>
|
||||||
|
<RowDefinition Height="5"/>
|
||||||
|
<RowDefinition Height="6*" MinHeight="100"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<TextBox x:Name="QueryInput" Text="SELECT * FROM v_delivery"
|
<TextBox x:Name="QueryInput" Text="SELECT * FROM v_member" Grid.Row="1" Grid.RowSpan="3"
|
||||||
AcceptsReturn="True" VerticalScrollBarVisibility="Visible" TextWrapping="Wrap"
|
AcceptsReturn="True" VerticalScrollBarVisibility="Visible" TextWrapping="Wrap"
|
||||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10,10,120,5"
|
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10,5,120,5"
|
||||||
FontFamily="Cascadia Code Light" FontSize="13">
|
FontFamily="Cascadia Code Light" FontSize="13">
|
||||||
<TextBox.InputBindings>
|
<TextBox.InputBindings>
|
||||||
<KeyBinding Key="Return" Modifiers="Control" Command="{Binding EnterCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:QueryWindow}}}" />
|
<KeyBinding Key="Return" Modifiers="Control" Command="{Binding EnterCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:QueryWindow}}}" />
|
||||||
|
<KeyBinding Key="S" Modifiers="Control" Command="{Binding SaveCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:QueryWindow}}}" />
|
||||||
</TextBox.InputBindings>
|
</TextBox.InputBindings>
|
||||||
</TextBox>
|
</TextBox>
|
||||||
<Button x:Name="QueryButton" Content="Abfragen"
|
<Button x:Name="QueryButton" Content="Abfragen" Grid.Row="1"
|
||||||
HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="10,10,10,5"
|
HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="10,5,10,0"
|
||||||
Click="QueryButton_Click" Width="100"
|
Click="QueryButton_Click" Width="100"
|
||||||
FontSize="14"/>
|
FontSize="14"/>
|
||||||
|
<Button x:Name="SaveButton" Content="Speichern" Grid.Row="3"
|
||||||
|
HorizontalAlignment="Right" VerticalAlignment="Stretch" Margin="10,0,10,5"
|
||||||
|
Click="SaveButton_Click" Width="100"
|
||||||
|
FontSize="14"/>
|
||||||
|
|
||||||
<GridSplitter Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
|
<GridSplitter Grid.Row="4" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
|
||||||
|
|
||||||
<DataGrid x:Name="DataList" Grid.Row="2"
|
<DataGrid x:Name="DataList" Grid.Row="5"
|
||||||
AutoGenerateColumns="False" HeadersVisibility="Column" IsReadOnly="True" GridLinesVisibility="None" SelectionMode="Extended"
|
AutoGenerateColumns="False" HeadersVisibility="Column" IsReadOnly="True" GridLinesVisibility="None" SelectionMode="Extended"
|
||||||
CanUserDeleteRows="False" CanUserResizeRows="False" CanUserAddRows="False"
|
CanUserDeleteRows="False" CanUserResizeRows="False" CanUserAddRows="False"
|
||||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10,5,10,10"/>
|
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10,5,10,10"/>
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
using Elwig.Helpers;
|
using Elwig.Helpers;
|
||||||
|
using Elwig.Helpers.Export;
|
||||||
|
using Microsoft.Win32;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Data.Common;
|
using System.Data.Common;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
@@ -13,7 +16,12 @@ namespace Elwig.Windows {
|
|||||||
|
|
||||||
private ICommand? _enterCommand;
|
private ICommand? _enterCommand;
|
||||||
public ICommand EnterCommand => _enterCommand ??= new ActionCommand(async () => {
|
public ICommand EnterCommand => _enterCommand ??= new ActionCommand(async () => {
|
||||||
await ExecuteQuery();
|
await DisplayQuery();
|
||||||
|
});
|
||||||
|
|
||||||
|
private ICommand? _saveCommand;
|
||||||
|
public ICommand SaveCommand => _saveCommand ??= new ActionCommand(async () => {
|
||||||
|
await SaveQuery();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -22,33 +30,45 @@ namespace Elwig.Windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async void QueryButton_Click(object sender, RoutedEventArgs evt) {
|
private async void QueryButton_Click(object sender, RoutedEventArgs evt) {
|
||||||
await ExecuteQuery();
|
await DisplayQuery();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ExecuteQuery() {
|
private async void SaveButton_Click(object sender, RoutedEventArgs evt) {
|
||||||
|
await SaveQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DisplayQuery() {
|
||||||
try {
|
try {
|
||||||
await ExecuteQuery(QueryInput.Text);
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
|
await DisplayQuery(QueryInput.Text);
|
||||||
|
Mouse.OverrideCursor = null;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
Mouse.OverrideCursor = null;
|
||||||
MessageBox.Show(e.Message, "Fehler beim Ausführen", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show(e.Message, "Fehler beim Ausführen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ExecuteQuery(string sqlQuery) {
|
private async Task SaveQuery() {
|
||||||
var rows = new List<object[]>();
|
await SaveQuery(QueryInput.Text);
|
||||||
IList<DbColumn> header;
|
}
|
||||||
|
|
||||||
using (var cnx = await AppDbContext.ConnectAsync()) {
|
private static async Task<(IList<DbColumn>, IEnumerable<object[]>)> ExecuteQuery(string sqlQuery) {
|
||||||
|
var rows = new List<object[]>();
|
||||||
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
using var cmd = cnx.CreateCommand();
|
using var cmd = cnx.CreateCommand();
|
||||||
cmd.CommandText = sqlQuery;
|
cmd.CommandText = sqlQuery;
|
||||||
using var reader = await cmd.ExecuteReaderAsync();
|
using var reader = await cmd.ExecuteReaderAsync();
|
||||||
header = await reader.GetColumnSchemaAsync();
|
var header = await reader.GetColumnSchemaAsync();
|
||||||
while (await reader.ReadAsync()) {
|
while (await reader.ReadAsync()) {
|
||||||
var values = new object[reader.FieldCount];
|
var values = new object[reader.FieldCount];
|
||||||
reader.GetValues(values);
|
reader.GetValues(values);
|
||||||
rows.Add(values);
|
rows.Add(values);
|
||||||
}
|
}
|
||||||
|
return (header, rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task DisplayQuery(string sqlQuery) {
|
||||||
|
var (header, rows) = await ExecuteQuery(sqlQuery);
|
||||||
var styleRight = new Style();
|
var styleRight = new Style();
|
||||||
styleRight.Setters.Add(new Setter(TextBlock.TextAlignmentProperty, TextAlignment.Right));
|
styleRight.Setters.Add(new Setter(TextBlock.TextAlignmentProperty, TextAlignment.Right));
|
||||||
|
|
||||||
@@ -63,5 +83,27 @@ namespace Elwig.Windows {
|
|||||||
}
|
}
|
||||||
DataList.ItemsSource = rows;
|
DataList.ItemsSource = rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
Mouse.OverrideCursor = Cursors.Wait;
|
||||||
|
await Task.Run(async () => {
|
||||||
|
try {
|
||||||
|
var (header, rows) = await ExecuteQuery(sqlQuery);
|
||||||
|
using var csv = new CsvSimple(d.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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Mouse.OverrideCursor = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -13,7 +13,7 @@ About
|
|||||||
**Product:** Elwig
|
**Product:** Elwig
|
||||||
**Description:** Electronic Management for Vintners' Cooperatives
|
**Description:** Electronic Management for Vintners' Cooperatives
|
||||||
**Type:** ERP system
|
**Type:** ERP system
|
||||||
**Version:** 1.0.1.3 ([Changelog](./CHANGELOG.md))
|
**Version:** 1.0.3.0 ([Changelog](./CHANGELOG.md))
|
||||||
**License:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
|
**License:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
|
||||||
**Website:** https://elwig.at/
|
**Website:** https://elwig.at/
|
||||||
**Source code:** https://git.necronda.net/winzer/elwig
|
**Source code:** https://git.necronda.net/winzer/elwig
|
||||||
@@ -33,7 +33,7 @@ Packaging: [WiX Toolset](https://www.firegiant.com/wixtoolset/)
|
|||||||
**Produkt:** Elwig
|
**Produkt:** Elwig
|
||||||
**Beschreibung:** Elektronische Winzergenossenschaftsverwaltung
|
**Beschreibung:** Elektronische Winzergenossenschaftsverwaltung
|
||||||
**Typ:** Warenwirtschaftssystem (ERP-System)
|
**Typ:** Warenwirtschaftssystem (ERP-System)
|
||||||
**Version:** 1.0.1.2 ([Änderungsprotokoll](./CHANGELOG.md))
|
**Version:** 1.0.3.0 ([Änderungsprotokoll](./CHANGELOG.md))
|
||||||
**Lizenz:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
|
**Lizenz:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
|
||||||
**Website:** https://elwig.at/
|
**Website:** https://elwig.at/
|
||||||
**Quellcode:** https://git.necronda.net/winzer/elwig
|
**Quellcode:** https://git.necronda.net/winzer/elwig
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ namespace Tests {
|
|||||||
public async Task Setup_1_Database() {
|
public async Task Setup_1_Database() {
|
||||||
AppDbContext.ConnectionStringOverride = $"Data Source=ElwigTestDB; Mode=Memory; Foreign Keys=True; Cache=Shared";
|
AppDbContext.ConnectionStringOverride = $"Data Source=ElwigTestDB; Mode=Memory; Foreign Keys=True; Cache=Shared";
|
||||||
Connection = await AppDbContext.ConnectAsync();
|
Connection = await AppDbContext.ConnectAsync();
|
||||||
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.Create.sql");
|
await Connection.ExecuteEmbeddedScript(Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.Create.sql");
|
||||||
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.Insert.sql");
|
await Connection.ExecuteEmbeddedScript(Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.Insert.sql");
|
||||||
}
|
}
|
||||||
|
|
||||||
[OneTimeSetUp]
|
[OneTimeSetUp]
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ namespace Tests.E2ETests {
|
|||||||
public static async Task SetupDatabase() {
|
public static async Task SetupDatabase() {
|
||||||
if (File.Exists(Utils.TestDatabasePath)) File.Delete(Utils.TestDatabasePath);
|
if (File.Exists(Utils.TestDatabasePath)) File.Delete(Utils.TestDatabasePath);
|
||||||
using var cnx = await AppDbContext.ConnectAsync($"Data Source=\"{Utils.TestDatabasePath}\"; Mode=ReadWriteCreate; Foreign Keys=True; Cache=Default; Pooling=False");
|
using var cnx = await AppDbContext.ConnectAsync($"Data Source=\"{Utils.TestDatabasePath}\"; Mode=ReadWriteCreate; Foreign Keys=True; Cache=Default; Pooling=False");
|
||||||
await AppDbContext.ExecuteEmbeddedScript(cnx, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.Create.sql");
|
await cnx.ExecuteEmbeddedScript(Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.Create.sql");
|
||||||
await AppDbContext.ExecuteEmbeddedScript(cnx, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.Insert.sql");
|
await cnx.ExecuteEmbeddedScript(Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.Insert.sql");
|
||||||
await AppDbContext.ExecuteEmbeddedScript(cnx, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.E2EInsert.sql");
|
await cnx.ExecuteEmbeddedScript(Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.E2EInsert.sql");
|
||||||
}
|
}
|
||||||
|
|
||||||
[OneTimeTearDown]
|
[OneTimeTearDown]
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace Tests.E2ETests {
|
|||||||
|
|
||||||
public const int WINDOW_OPEN_SLEEP = 2000;
|
public const int WINDOW_OPEN_SLEEP = 2000;
|
||||||
|
|
||||||
public static readonly string ApplicationPath = Path.GetFullPath(@"..\..\..\..\Elwig\bin\Debug\net8.0-windows\Elwig.exe");
|
public static readonly string ApplicationPath = Path.GetFullPath(@"..\..\..\..\Elwig\bin\Debug\net10.0-windows\Elwig.exe");
|
||||||
public static readonly string ConfigPath = Path.GetFullPath(@"..\..\..\..\Tests\config.test.ini");
|
public static readonly string ConfigPath = Path.GetFullPath(@"..\..\..\..\Tests\config.test.ini");
|
||||||
public static readonly string TestDatabasePath = Path.GetFullPath(@"..\..\..\..\Tests\ElwigTestDB.sqlite3");
|
public static readonly string TestDatabasePath = Path.GetFullPath(@"..\..\..\..\Tests\ElwigTestDB.sqlite3");
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0-windows</TargetFramework>
|
<TargetFramework>net10.0-windows</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
@@ -19,12 +19,12 @@
|
|||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
|
||||||
<PackageReference Include="Appium.WebDriver" Version="4.4.5" />
|
<PackageReference Include="Appium.WebDriver" Version="4.4.5" />
|
||||||
<PackageReference Include="NReco.PdfRenderer" Version="1.6.0" />
|
<PackageReference Include="NReco.PdfRenderer" Version="1.6.0" />
|
||||||
<PackageReference Include="NUnit" Version="4.4.0" />
|
<PackageReference Include="NUnit" Version="4.4.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="5.1.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="6.1.0" />
|
||||||
<PackageReference Include="NUnit.Analyzers" Version="4.10.0">
|
<PackageReference Include="NUnit.Analyzers" Version="4.11.2">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ namespace Tests.UnitTests.DocumentTests {
|
|||||||
[OneTimeSetUp]
|
[OneTimeSetUp]
|
||||||
public async Task SetupDatabase() {
|
public async Task SetupDatabase() {
|
||||||
Connection = await AppDbContext.ConnectAsync();
|
Connection = await AppDbContext.ConnectAsync();
|
||||||
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.DocumentInsert.sql");
|
await Connection.ExecuteEmbeddedScript(Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.DocumentInsert.sql");
|
||||||
}
|
}
|
||||||
|
|
||||||
[OneTimeTearDown]
|
[OneTimeTearDown]
|
||||||
public async Task TeardownDatabase() {
|
public async Task TeardownDatabase() {
|
||||||
if (Connection == null) return;
|
if (Connection == null) return;
|
||||||
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.DocumentDelete.sql");
|
await Connection.ExecuteEmbeddedScript(Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.DocumentDelete.sql");
|
||||||
await Connection.DisposeAsync();
|
await Connection.DisposeAsync();
|
||||||
Connection = null;
|
Connection = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,13 +24,13 @@ namespace Tests.UnitTests.HelperTests {
|
|||||||
[OneTimeSetUp]
|
[OneTimeSetUp]
|
||||||
public async Task SetupDatabase() {
|
public async Task SetupDatabase() {
|
||||||
Connection = await AppDbContext.ConnectAsync();
|
Connection = await AppDbContext.ConnectAsync();
|
||||||
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.BillingInsert.sql");
|
await Connection.ExecuteEmbeddedScript(Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.BillingInsert.sql");
|
||||||
}
|
}
|
||||||
|
|
||||||
[OneTimeTearDown]
|
[OneTimeTearDown]
|
||||||
public async Task TeardownDatabase() {
|
public async Task TeardownDatabase() {
|
||||||
if (Connection == null) return;
|
if (Connection == null) return;
|
||||||
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.BillingDelete.sql");
|
await Connection.ExecuteEmbeddedScript(Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.BillingDelete.sql");
|
||||||
await Connection.DisposeAsync();
|
await Connection.DisposeAsync();
|
||||||
Connection = null;
|
Connection = null;
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ namespace Tests.UnitTests.HelperTests {
|
|||||||
[TearDown]
|
[TearDown]
|
||||||
public async Task CleanupDatabasePayment() {
|
public async Task CleanupDatabasePayment() {
|
||||||
if (Connection == null) return;
|
if (Connection == null) return;
|
||||||
await AppDbContext.ExecuteBatch(Connection, """
|
await Connection.ExecuteBatch("""
|
||||||
DELETE FROM credit;
|
DELETE FROM credit;
|
||||||
DELETE FROM payment_variant;
|
DELETE FROM payment_variant;
|
||||||
DELETE FROM delivery_part_bucket;
|
DELETE FROM delivery_part_bucket;
|
||||||
@@ -115,7 +115,7 @@ namespace Tests.UnitTests.HelperTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Task InsertPaymentVariant(int year, int avnr, string data) {
|
private Task InsertPaymentVariant(int year, int avnr, string data) {
|
||||||
return AppDbContext.ExecuteBatch(Connection!, $"""
|
return Connection!.ExecuteBatch($"""
|
||||||
INSERT INTO payment_variant (year, avnr, name, date, transfer_date, test_variant, calc_time, data)
|
INSERT INTO payment_variant (year, avnr, name, date, transfer_date, test_variant, calc_time, data)
|
||||||
VALUES ({year}, {avnr}, 'Test', '2021-01-15', NULL, TRUE, NULL, '{data}');
|
VALUES ({year}, {avnr}, 'Test', '2021-01-15', NULL, TRUE, NULL, '{data}');
|
||||||
""");
|
""");
|
||||||
@@ -188,7 +188,7 @@ namespace Tests.UnitTests.HelperTests {
|
|||||||
Assert.That(payment["GV"], Is.EqualTo(10_000));
|
Assert.That(payment["GV"], Is.EqualTo(10_000));
|
||||||
});
|
});
|
||||||
|
|
||||||
await b.Calculate(false, false, false);
|
await b.Calculate(true, false, false, false);
|
||||||
var prices = await GetMemberDeliveryPrices(year, mgnr);
|
var prices = await GetMemberDeliveryPrices(year, mgnr);
|
||||||
Assert.Multiple(() => {
|
Assert.Multiple(() => {
|
||||||
Assert.That(prices, Has.Count.EqualTo(7));
|
Assert.That(prices, Has.Count.EqualTo(7));
|
||||||
@@ -234,7 +234,7 @@ namespace Tests.UnitTests.HelperTests {
|
|||||||
Assert.That(payment["GV"], Is.EqualTo(8_000));
|
Assert.That(payment["GV"], Is.EqualTo(8_000));
|
||||||
});
|
});
|
||||||
|
|
||||||
await b.Calculate(true, false, false);
|
await b.Calculate(true, true, false, false);
|
||||||
var prices = await GetMemberDeliveryPrices(year, mgnr);
|
var prices = await GetMemberDeliveryPrices(year, mgnr);
|
||||||
Assert.Multiple(() => {
|
Assert.Multiple(() => {
|
||||||
Assert.That(prices, Has.Count.EqualTo(6));
|
Assert.That(prices, Has.Count.EqualTo(6));
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ namespace Tests.UnitTests.ServiceTests {
|
|||||||
[OneTimeSetUp]
|
[OneTimeSetUp]
|
||||||
public async Task SetupDatabase() {
|
public async Task SetupDatabase() {
|
||||||
Connection = await AppDbContext.ConnectAsync();
|
Connection = await AppDbContext.ConnectAsync();
|
||||||
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.ServiceInsert.sql");
|
await Connection.ExecuteEmbeddedScript(Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.ServiceInsert.sql");
|
||||||
}
|
}
|
||||||
|
|
||||||
[OneTimeTearDown]
|
[OneTimeTearDown]
|
||||||
public async Task TeardownDatabase() {
|
public async Task TeardownDatabase() {
|
||||||
if (Connection == null) return;
|
if (Connection == null) return;
|
||||||
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.ServiceDelete.sql");
|
await Connection.ExecuteEmbeddedScript(Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.ServiceDelete.sql");
|
||||||
await Connection.DisposeAsync();
|
await Connection.DisposeAsync();
|
||||||
Connection = null;
|
Connection = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ namespace Tests.UnitTests.WeighingTests {
|
|||||||
public void Test_03_Moving() {
|
public void Test_03_Moving() {
|
||||||
Mock.Weight = 1_000;
|
Mock.Weight = 1_000;
|
||||||
Mock.Error = "moving";
|
Mock.Error = "moving";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
var ex = Assert.ThrowsAsync<WeighingException>(async () => await Scale!.Weigh());
|
||||||
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
|
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ namespace Tests.UnitTests.WeighingTests {
|
|||||||
public void Test_04_Overloaded() {
|
public void Test_04_Overloaded() {
|
||||||
Mock.Weight = 10_000;
|
Mock.Weight = 10_000;
|
||||||
Mock.Error = "overloaded";
|
Mock.Error = "overloaded";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
var ex = Assert.ThrowsAsync<WeighingException>(async () => await Scale!.Weigh());
|
||||||
Assert.That(ex.Message, Contains.Substring("Waage in Überlast"));
|
Assert.That(ex.Message, Contains.Substring("Waage in Überlast"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,14 +87,14 @@ namespace Tests.UnitTests.WeighingTests {
|
|||||||
public void Test_05_InvalidResponse() {
|
public void Test_05_InvalidResponse() {
|
||||||
Mock.Weight = 1_000;
|
Mock.Weight = 1_000;
|
||||||
Mock.Error = "invalid";
|
Mock.Error = "invalid";
|
||||||
Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
Assert.ThrowsAsync<FormatException>(async () => await Scale!.Weigh());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test_06_InvalidCrc() {
|
public void Test_06_InvalidCrc() {
|
||||||
Mock.Weight = 1_000;
|
Mock.Weight = 1_000;
|
||||||
Mock.Error = "crc";
|
Mock.Error = "crc";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
var ex = Assert.ThrowsAsync<WeighingException>(async () => await Scale!.Weigh());
|
||||||
Assert.That(ex.Message, Contains.Substring("Invalid CRC16 checksum"));
|
Assert.That(ex.Message, Contains.Substring("Invalid CRC16 checksum"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ namespace Tests.UnitTests.WeighingTests {
|
|||||||
public void Test_07_InvalidUnit() {
|
public void Test_07_InvalidUnit() {
|
||||||
Mock.Weight = 1_000;
|
Mock.Weight = 1_000;
|
||||||
Mock.Error = "unit";
|
Mock.Error = "unit";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
var ex = Assert.ThrowsAsync<WeighingException>(async () => await Scale!.Weigh());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ namespace Tests.UnitTests.WeighingTests {
|
|||||||
Mock.Weight = 1_000;
|
Mock.Weight = 1_000;
|
||||||
Mock.Tare = 41;
|
Mock.Tare = 41;
|
||||||
Mock.Error = "moving";
|
Mock.Error = "moving";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
var ex = Assert.ThrowsAsync<WeighingException>(async () => await Scale!.Weigh());
|
||||||
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
|
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ namespace Tests.UnitTests.WeighingTests {
|
|||||||
Mock.Weight = 1_000;
|
Mock.Weight = 1_000;
|
||||||
Mock.Tare = 41;
|
Mock.Tare = 41;
|
||||||
Mock.Error = "invalid";
|
Mock.Error = "invalid";
|
||||||
Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
Assert.ThrowsAsync<FormatException>(async () => await Scale!.Weigh());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ namespace Tests.UnitTests.WeighingTests {
|
|||||||
public void Test_03_Moving() {
|
public void Test_03_Moving() {
|
||||||
Mock.Weight = 1_000;
|
Mock.Weight = 1_000;
|
||||||
Mock.Error = "moving";
|
Mock.Error = "moving";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
var ex = Assert.ThrowsAsync<WeighingException>(async () => await Scale!.Weigh());
|
||||||
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
|
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ namespace Tests.UnitTests.WeighingTests {
|
|||||||
public void Test_04_Overloaded() {
|
public void Test_04_Overloaded() {
|
||||||
Mock.Weight = 10_000;
|
Mock.Weight = 10_000;
|
||||||
Mock.Error = "overloaded";
|
Mock.Error = "overloaded";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
var ex = Assert.ThrowsAsync<WeighingException>(async () => await Scale!.Weigh());
|
||||||
Assert.That(ex.Message, Contains.Substring("Waage in Überlast"));
|
Assert.That(ex.Message, Contains.Substring("Waage in Überlast"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,14 +87,14 @@ namespace Tests.UnitTests.WeighingTests {
|
|||||||
public void Test_05_InvalidResponse() {
|
public void Test_05_InvalidResponse() {
|
||||||
Mock.Weight = 1_000;
|
Mock.Weight = 1_000;
|
||||||
Mock.Error = "invalid";
|
Mock.Error = "invalid";
|
||||||
Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
Assert.ThrowsAsync<FormatException>(async () => await Scale!.Weigh());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test_06_InvalidCrc() {
|
public void Test_06_InvalidCrc() {
|
||||||
Mock.Weight = 1_000;
|
Mock.Weight = 1_000;
|
||||||
Mock.Error = "crc";
|
Mock.Error = "crc";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
var ex = Assert.ThrowsAsync<WeighingException>(async () => await Scale!.Weigh());
|
||||||
Assert.That(ex.Message, Contains.Substring("Invalid CRC16 checksum"));
|
Assert.That(ex.Message, Contains.Substring("Invalid CRC16 checksum"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ namespace Tests.UnitTests.WeighingTests {
|
|||||||
public void Test_07_InvalidUnit() {
|
public void Test_07_InvalidUnit() {
|
||||||
Mock.Weight = 1_000;
|
Mock.Weight = 1_000;
|
||||||
Mock.Error = "unit";
|
Mock.Error = "unit";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
var ex = Assert.ThrowsAsync<WeighingException>(async () => await Scale!.Weigh());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ namespace Tests.UnitTests.WeighingTests {
|
|||||||
Mock.Weight = 1_000;
|
Mock.Weight = 1_000;
|
||||||
Mock.Tare = 41;
|
Mock.Tare = 41;
|
||||||
Mock.Error = "moving";
|
Mock.Error = "moving";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
var ex = Assert.ThrowsAsync<WeighingException>(async () => await Scale!.Weigh());
|
||||||
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
|
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ namespace Tests.UnitTests.WeighingTests {
|
|||||||
Mock.Weight = 1_000;
|
Mock.Weight = 1_000;
|
||||||
Mock.Tare = 41;
|
Mock.Tare = 41;
|
||||||
Mock.Error = "invalid";
|
Mock.Error = "invalid";
|
||||||
Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
Assert.ThrowsAsync<FormatException>(async () => await Scale!.Weigh());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ namespace Tests.UnitTests.WeighingTests {
|
|||||||
public void Test_03_Moving() {
|
public void Test_03_Moving() {
|
||||||
MockA.Weight = 1_000;
|
MockA.Weight = 1_000;
|
||||||
MockA.Error = "moving";
|
MockA.Error = "moving";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh());
|
var ex = Assert.ThrowsAsync<WeighingException>(async () => await ScaleA!.Weigh());
|
||||||
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
|
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ namespace Tests.UnitTests.WeighingTests {
|
|||||||
public void Test_04_Overloaded() {
|
public void Test_04_Overloaded() {
|
||||||
MockA.Weight = 10_000;
|
MockA.Weight = 10_000;
|
||||||
MockA.Error = "overloaded";
|
MockA.Error = "overloaded";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh());
|
var ex = Assert.ThrowsAsync<WeighingException>(async () => await ScaleA!.Weigh());
|
||||||
Assert.That(ex.Message, Contains.Substring("Waage in Überlast"));
|
Assert.That(ex.Message, Contains.Substring("Waage in Überlast"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,14 +107,14 @@ namespace Tests.UnitTests.WeighingTests {
|
|||||||
public void Test_05_InvalidResponse() {
|
public void Test_05_InvalidResponse() {
|
||||||
MockA.Weight = 1_000;
|
MockA.Weight = 1_000;
|
||||||
MockA.Error = "invalid";
|
MockA.Error = "invalid";
|
||||||
Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh());
|
Assert.ThrowsAsync<FormatException>(async () => await ScaleA!.Weigh());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test_06_InvalidCrc() {
|
public void Test_06_InvalidCrc() {
|
||||||
MockA.Weight = 1_000;
|
MockA.Weight = 1_000;
|
||||||
MockA.Error = "crc";
|
MockA.Error = "crc";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh());
|
var ex = Assert.ThrowsAsync<WeighingException>(async () => await ScaleA!.Weigh());
|
||||||
Assert.That(ex.Message, Contains.Substring("Invalid CRC16 checksum"));
|
Assert.That(ex.Message, Contains.Substring("Invalid CRC16 checksum"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +122,7 @@ namespace Tests.UnitTests.WeighingTests {
|
|||||||
public void Test_07_InvalidUnit() {
|
public void Test_07_InvalidUnit() {
|
||||||
MockA.Weight = 1_000;
|
MockA.Weight = 1_000;
|
||||||
MockA.Error = "unit";
|
MockA.Error = "unit";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh());
|
var ex = Assert.ThrowsAsync<WeighingException>(async () => await ScaleA!.Weigh());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
curl --fail -s -L "https://elwig.at/files/create.sql?v=33" -u "elwig:ganzGeheim123!" -o "Resources\Sql\Create.sql"
|
curl --fail -s -L "https://elwig.at/files/create.sql?v=36" -u "elwig:ganzGeheim123!" -o "Resources\Sql\Create.sql"
|
||||||
|
|||||||
Reference in New Issue
Block a user