Compare commits
34 Commits
v1.0.1.0
...
19d5f8650a
| Author | SHA1 | Date | |
|---|---|---|---|
| 19d5f8650a | |||
| e97c29db43 | |||
| 3419113dec | |||
| bf6297f63b | |||
| f228ba3019 | |||
| 495aa8a691 | |||
| 811916a8b9 | |||
| 3b6333a6a2 | |||
| 9b37330362 | |||
| 889a17b21c | |||
| ac6d559e5d | |||
| 6d80cca241 | |||
| 9dc225d3e4 | |||
| 0d513f7bff | |||
| b10c744bf9 | |||
| e6367da286 | |||
| 01f4480a08 | |||
| e9722c790c | |||
| af98c32026 | |||
| 7300b30cf5 | |||
| 428cd6ddc2 | |||
| 2de8af878b | |||
| 34d95eab9d | |||
| 548aeb2ce9 | |||
| 7edd888aa2 | |||
| a0d4f19f30 | |||
| 67ba342c28 | |||
| 1b69fcb16a | |||
| c8a95422af | |||
| 9d02f18bac | |||
| f9ee2cb120 | |||
| b27b89f599 | |||
| bfbd0a6a22 | |||
| e2de7a1f1c |
92
CHANGELOG.md
92
CHANGELOG.md
@@ -2,6 +2,98 @@
|
|||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
[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}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Neue Funktionen {#v1.0.1.3-features}
|
||||||
|
|
||||||
|
* In der Liste des Lieferungen-Fenster (`DeliveryAdminWindow`) werden
|
||||||
|
* statt ausschließlich der Sorte auch Attribut und Bewirtschaftungsart angezeigt. (a0d4f19f30)
|
||||||
|
* Kommentare der Lieferungen (und Teillieferungen) angezeigt. (548aeb2ce9)
|
||||||
|
|
||||||
|
### Sonstiges {#v1.0.1.3-misc}
|
||||||
|
|
||||||
|
* Verzögerung der Überprüfung auf automatische Updates auf einige Sekunden verlängert. (67ba342c28)
|
||||||
|
* Verbesserung der Ladezeiten im Anmeldungen-Fenster (`DeliveryAncmtAdminWindow`). (7edd888aa2)
|
||||||
|
|
||||||
|
[v1.0.1.3]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.1.3
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v1.0.1.2][v1.0.1.2] (2025-09-25) {#v1.0.1.2}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Behobene Fehler {#v1.0.1.2-bugfixes}
|
||||||
|
|
||||||
|
* Beim automatischen Importieren/Synchronisieren wird bei einem Fehlerfall der Benutzer verständigt, aber der Vorgang nicht abgebrochen. (9d02f18bac)
|
||||||
|
|
||||||
|
### Sonstiges {#v1.0.1.2-misc}
|
||||||
|
|
||||||
|
* Beim Sichern der Datenbank werden Meta-Informationen in der ZIP-Datei gespeichert. (c8a95422af)
|
||||||
|
|
||||||
|
[v1.0.1.2]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.1.2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v1.0.1.1][v1.0.1.1] (2025-09-21) {#v1.0.1.1}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Sonstiges {#v1.0.1.1-misc}
|
||||||
|
|
||||||
|
* Eingabe von Sorten und Qualitätsstufen im Übernahme-Fenster (`DeliveryAdminWindows`) verbessert. (e2de7a1f1c, b27b89f599)
|
||||||
|
|
||||||
|
[v1.0.1.1]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.1.1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[v1.0.1.0][v1.0.1.0] (2025-09-18) {#v1.0.1.0}
|
[v1.0.1.0][v1.0.1.0] (2025-09-18) {#v1.0.1.0}
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -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; }
|
||||||
@@ -128,7 +130,7 @@ namespace Elwig {
|
|||||||
if (Config.UpdateAuto && Config.UpdateUrl != null) {
|
if (Config.UpdateAuto && Config.UpdateUrl != null) {
|
||||||
if (Utils.HasInternetConnectivity()) {
|
if (Utils.HasInternetConnectivity()) {
|
||||||
Utils.RunBackground("Auto Updater", async () => {
|
Utils.RunBackground("Auto Updater", async () => {
|
||||||
await Task.Delay(500);
|
await Task.Delay(1000);
|
||||||
await CheckForUpdates();
|
await CheckForUpdates();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -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}", "Waagen-Fehler",
|
||||||
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();
|
||||||
}
|
}
|
||||||
@@ -234,12 +240,53 @@ namespace Elwig {
|
|||||||
if (!evt.IsAvailable) return;
|
if (!evt.IsAvailable) return;
|
||||||
if (Utils.HasInternetConnectivity()) {
|
if (Utils.HasInternetConnectivity()) {
|
||||||
Utils.RunBackground("Auto Updater", async () => {
|
Utils.RunBackground("Auto Updater", async () => {
|
||||||
await Task.Delay(500);
|
await Task.Delay(2000);
|
||||||
await CheckForUpdates();
|
await CheckForUpdates();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) && 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}", "Waagen-Fehler",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,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";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.0</Version>
|
<Version>1.0.2.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.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.9" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="9.0.9" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.0" />
|
||||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3485.44" />
|
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3595.46" />
|
||||||
<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.0" />
|
||||||
<PackageReference Include="System.IO.Ports" Version="9.0.9" />
|
<PackageReference Include="System.IO.Ports" Version="10.0.0" />
|
||||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.9" />
|
<PackageReference Include="System.Management" Version="10.0.0" />
|
||||||
|
<PackageReference Include="System.Text.Encoding.CodePages" Version="10.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -67,7 +67,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; }
|
||||||
@@ -120,7 +120,8 @@ namespace Elwig.Helpers {
|
|||||||
public static async Task ExecuteBatch(SqliteConnection cnx, string sql) {
|
public static async Task ExecuteBatch(SqliteConnection cnx, string sql) {
|
||||||
using var cmd = cnx.CreateCommand();
|
using var cmd = cnx.CreateCommand();
|
||||||
cmd.CommandText = sql;
|
cmd.CommandText = sql;
|
||||||
await (await cmd.ExecuteReaderAsync()).CloseAsync();
|
using var reader = await cmd.ExecuteReaderAsync();
|
||||||
|
while (await reader.NextResultAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task ExecuteEmbeddedScript(SqliteConnection cnx, Assembly asm, string name) {
|
public static async Task ExecuteEmbeddedScript(SqliteConnection cnx, Assembly asm, string name) {
|
||||||
@@ -248,14 +249,6 @@ namespace Elwig.Helpers {
|
|||||||
return c + 1;
|
return c + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<WineQualLevel> GetWineQualityLevel(double kmw, string? maxQualId = null) {
|
|
||||||
return await WineQualityLevels
|
|
||||||
.Where(q => !q.IsPredicate && (q.MinKmw == null || q.MinKmw <= kmw))
|
|
||||||
.Where(q => maxQualId == null || q.QualId == "WEI" || q.QualId == maxQualId)
|
|
||||||
.OrderBy(q => q.MinKmw)
|
|
||||||
.LastAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateDeliveryPartModifiers(DeliveryPart part, IEnumerable<Modifier> oldModifiers, IEnumerable<Modifier> newModifiers) {
|
public void UpdateDeliveryPartModifiers(DeliveryPart part, IEnumerable<Modifier> oldModifiers, IEnumerable<Modifier> newModifiers) {
|
||||||
foreach (var m in Modifiers.Where(m => m.Year == part.Year)) {
|
foreach (var m in Modifiers.Where(m => m.Year == part.Year)) {
|
||||||
var mod = new DeliveryPartModifier {
|
var mod = new DeliveryPartModifier {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ 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 = 35;
|
||||||
|
|
||||||
private static int VersionOffset = 0;
|
private static int VersionOffset = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -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,6 +40,8 @@ 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() {
|
||||||
@@ -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,15 +181,25 @@ 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 AppDbContext.ExecuteBatch(cnx, $"""
|
await AppDbContext.ExecuteBatch(cnx, $"""
|
||||||
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) {
|
||||||
|
|||||||
@@ -2,16 +2,48 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Elwig.Helpers.Export {
|
namespace Elwig.Helpers.Export {
|
||||||
public static class Database {
|
public static class Database {
|
||||||
|
|
||||||
|
private static async Task<(long? ApplicationId, string? UserVersion, long? SchemaVersion, long FileSize)> GetMeta() {
|
||||||
|
long size = new FileInfo(App.Config.DatabaseFile).Length;
|
||||||
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
|
var applId = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA application_id");
|
||||||
|
var userVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA user_version");
|
||||||
|
var schemaVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA schema_version");
|
||||||
|
return (applId, userVers != null ? $"{userVers >> 24}.{(userVers >> 16) & 0xFF}.{(userVers >> 8) & 0xFF}.{userVers & 0xFF}" : null, schemaVers, size);
|
||||||
|
}
|
||||||
|
|
||||||
public static async Task ExportSqlite(string filename, bool zipFile) {
|
public static async Task ExportSqlite(string filename, bool zipFile) {
|
||||||
if (zipFile) {
|
if (zipFile) {
|
||||||
File.Delete(filename);
|
File.Delete(filename);
|
||||||
using var zip = ZipFile.Open(filename, ZipArchiveMode.Create);
|
using var zip = ZipFile.Open(filename, ZipArchiveMode.Create);
|
||||||
await zip.CheckIntegrity();
|
|
||||||
|
var version = zip.CreateEntry("version", CompressionLevel.NoCompression);
|
||||||
|
using (var writer = new StreamWriter(version.Open(), Utils.UTF8)) {
|
||||||
|
await writer.WriteAsync("elwig:1");
|
||||||
|
}
|
||||||
|
|
||||||
|
var (applId, userVers, schemaVers, size) = await GetMeta();
|
||||||
|
var meta = zip.CreateEntry("meta.json", CompressionLevel.NoCompression);
|
||||||
|
using (var writer = new StreamWriter(meta.Open(), Utils.UTF8)) {
|
||||||
|
var obj = new JsonObject {
|
||||||
|
["timestamp"] = $"{DateTime.UtcNow:yyyy-MM-ddTHH:mm:ssZ}",
|
||||||
|
["zwstid"] = App.ZwstId,
|
||||||
|
["device"] = Environment.MachineName,
|
||||||
|
["database"] = new JsonObject {
|
||||||
|
["application_id"] = applId,
|
||||||
|
["user_version"] = userVers,
|
||||||
|
["schema_version"] = schemaVers,
|
||||||
|
["file_size"] = size,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await writer.WriteAsync(obj.ToJsonString(Utils.JsonOpts));
|
||||||
|
}
|
||||||
|
|
||||||
var db = zip.CreateEntryFromFile(App.Config.DatabaseFile, "database.sqlite3", CompressionLevel.SmallestSize);
|
var db = zip.CreateEntryFromFile(App.Config.DatabaseFile, "database.sqlite3", CompressionLevel.SmallestSize);
|
||||||
} else {
|
} else {
|
||||||
File.Copy(App.Config.DatabaseFile, filename, true);
|
File.Copy(App.Config.DatabaseFile, filename, true);
|
||||||
@@ -22,10 +54,33 @@ namespace Elwig.Helpers.Export {
|
|||||||
if (zipFile) {
|
if (zipFile) {
|
||||||
File.Delete(filename);
|
File.Delete(filename);
|
||||||
using var zip = ZipFile.Open(filename, ZipArchiveMode.Create);
|
using var zip = ZipFile.Open(filename, ZipArchiveMode.Create);
|
||||||
var entry = zip.CreateEntry("database.sql", CompressionLevel.SmallestSize);
|
|
||||||
using var stream = entry.Open();
|
var version = zip.CreateEntry("version", CompressionLevel.NoCompression);
|
||||||
using var writer = new StreamWriter(stream, Utils.UTF8);
|
using (var writer = new StreamWriter(version.Open(), Utils.UTF8)) {
|
||||||
await ExportSql(writer);
|
await writer.WriteAsync("elwig:1");
|
||||||
|
}
|
||||||
|
|
||||||
|
var (applId, userVers, schemaVers, size) = await GetMeta();
|
||||||
|
var meta = zip.CreateEntry("meta.json", CompressionLevel.NoCompression);
|
||||||
|
using (var writer = new StreamWriter(meta.Open(), Utils.UTF8)) {
|
||||||
|
var obj = new JsonObject {
|
||||||
|
["timestamp"] = $"{DateTime.UtcNow:yyyy-MM-ddTHH:mm:ssZ}",
|
||||||
|
["zwstid"] = App.ZwstId,
|
||||||
|
["device"] = Environment.MachineName,
|
||||||
|
["database"] = new JsonObject {
|
||||||
|
["application_id"] = applId,
|
||||||
|
["user_version"] = userVers,
|
||||||
|
["schema_version"] = schemaVers,
|
||||||
|
["file_size"] = size,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await writer.WriteAsync(obj.ToJsonString(Utils.JsonOpts));
|
||||||
|
}
|
||||||
|
|
||||||
|
var sql = zip.CreateEntry("database.sql", CompressionLevel.SmallestSize);
|
||||||
|
using (var writer = new StreamWriter(sql.Open(), Utils.UTF8)) {
|
||||||
|
await ExportSql(writer);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
using var stream = File.OpenWrite(filename);
|
using var stream = File.OpenWrite(filename);
|
||||||
using var writer = new StreamWriter(stream, Utils.UTF8);
|
using var writer = new StreamWriter(stream, Utils.UTF8);
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ using System.Globalization;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
|
||||||
using System.Text.Json.Nodes;
|
using System.Text.Json.Nodes;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
@@ -18,8 +17,6 @@ namespace Elwig.Helpers.Export {
|
|||||||
|
|
||||||
public static readonly string ImportedTxt = Path.Combine(App.DataPath, "imported.txt");
|
public static readonly string ImportedTxt = Path.Combine(App.DataPath, "imported.txt");
|
||||||
|
|
||||||
private static readonly JsonSerializerOptions JsonOpts = new() { Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping };
|
|
||||||
|
|
||||||
public static async Task<string[]> GetImportedFiles() {
|
public static async Task<string[]> GetImportedFiles() {
|
||||||
try {
|
try {
|
||||||
return await File.ReadAllLinesAsync(ImportedTxt, Utils.UTF8);
|
return await File.ReadAllLinesAsync(ImportedTxt, Utils.UTF8);
|
||||||
@@ -77,97 +74,117 @@ namespace Elwig.Helpers.Export {
|
|||||||
int? DeliveryNum, string? DeliveryFilters)>();
|
int? DeliveryNum, string? DeliveryFilters)>();
|
||||||
|
|
||||||
foreach (var filename in filenames) {
|
foreach (var filename in filenames) {
|
||||||
// TODO read encrypted files
|
try {
|
||||||
using var zip = ZipFile.Open(filename, ZipArchiveMode.Read);
|
data.Add(new([], [], [], [], [], [], [], new([], [], [], [], new() {
|
||||||
await zip.CheckIntegrity();
|
["member"] = [],
|
||||||
|
["area_commitment"] = [],
|
||||||
|
["delivery"] = [],
|
||||||
|
})));
|
||||||
|
var r = data[^1];
|
||||||
|
|
||||||
var version = zip.GetEntry("version");
|
// TODO read encrypted files
|
||||||
using (var reader = new StreamReader(version!.Open(), Utils.UTF8)) {
|
using var zip = ZipFile.Open(filename, ZipArchiveMode.Read);
|
||||||
if (await reader.ReadToEndAsync() != "elwig:1")
|
await zip.CheckIntegrity();
|
||||||
throw new FileFormatException($"Ungültige Export-Datei ({filename})");
|
|
||||||
}
|
|
||||||
|
|
||||||
var metaJson = zip.GetEntry("meta.json");
|
var version = zip.GetEntry("version");
|
||||||
var meta = await JsonNode.ParseAsync(metaJson!.Open());
|
using (var reader = new StreamReader(version!.Open(), Utils.UTF8)) {
|
||||||
var memberCount = meta!["members"]?["count"]?.AsValue().GetValue<int>();
|
if (await reader.ReadToEndAsync() != "elwig:1")
|
||||||
var memberFilters = meta!["members"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
|
throw new FileFormatException($"Ungültige Elwig-Export-Datei ({filename})");
|
||||||
var areaComCount = meta!["area_commitments"]?["count"]?.AsValue().GetValue<int>();
|
}
|
||||||
var areaComFilters = meta!["area_commitments"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
|
|
||||||
var deliveryCount = meta!["deliveries"]?["count"]?.AsValue().GetValue<int>();
|
|
||||||
var deliveryFilters = meta!["deliveries"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
|
|
||||||
metaData.Add((Path.GetFileName(filename),
|
|
||||||
meta["zwstid"]!.AsValue().GetValue<string>(), meta["device"]!.AsValue().GetValue<string>(),
|
|
||||||
memberCount, memberFilters != null ? string.Join(" / ", memberFilters) : null,
|
|
||||||
areaComCount, areaComFilters != null ? string.Join(" / ", areaComFilters) : null,
|
|
||||||
deliveryCount, deliveryFilters != null ? string.Join(" / ", deliveryFilters) : null));
|
|
||||||
|
|
||||||
data.Add(new([], [], [], [], [], [], [], new([], [], [], [], new() {
|
var metaJson = zip.GetEntry("meta.json");
|
||||||
["member"] = [],
|
var meta = await JsonNode.ParseAsync(metaJson!.Open());
|
||||||
["area_commitment"] = [],
|
var memberCount = meta!["members"]?["count"]?.AsValue().GetValue<int>();
|
||||||
["delivery"] = [],
|
var memberFilters = meta!["members"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
|
||||||
})));
|
var areaComCount = meta!["area_commitments"]?["count"]?.AsValue().GetValue<int>();
|
||||||
var r = data[^1];
|
var areaComFilters = meta!["area_commitments"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
|
||||||
|
var deliveryCount = meta!["deliveries"]?["count"]?.AsValue().GetValue<int>();
|
||||||
|
var deliveryFilters = meta!["deliveries"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
|
||||||
|
metaData.Add((Path.GetFileName(filename),
|
||||||
|
meta["zwstid"]!.AsValue().GetValue<string>(), meta["device"]!.AsValue().GetValue<string>(),
|
||||||
|
memberCount, memberFilters != null ? string.Join(" / ", memberFilters) : null,
|
||||||
|
areaComCount, areaComFilters != null ? string.Join(" / ", areaComFilters) : null,
|
||||||
|
deliveryCount, deliveryFilters != null ? string.Join(" / ", deliveryFilters) : null));
|
||||||
|
|
||||||
var wbKgsJson = zip.GetEntry("wb_kgs.json");
|
var wbKgsJson = zip.GetEntry("wb_kgs.json");
|
||||||
if (wbKgsJson != null) {
|
if (wbKgsJson != null) {
|
||||||
using var reader = new StreamReader(wbKgsJson.Open(), Utils.UTF8);
|
using var reader = new StreamReader(wbKgsJson.Open(), Utils.UTF8);
|
||||||
string? line;
|
string? line;
|
||||||
while ((line = await reader.ReadLineAsync()) != null) {
|
while ((line = await reader.ReadLineAsync()) != null) {
|
||||||
var obj = JsonNode.Parse(line)!.AsObject();
|
var obj = JsonNode.Parse(line)!.AsObject();
|
||||||
var (k, g) = obj.ToWbKg(currentWbGls);
|
var (k, g) = obj.ToWbKg(currentWbGls);
|
||||||
r.WbKgs.Add(k);
|
r.WbKgs.Add(k);
|
||||||
if (g != null) {
|
if (g != null) {
|
||||||
currentWbGls[g.GlNr] = g;
|
currentWbGls[g.GlNr] = g;
|
||||||
r.WbGls.Add(g);
|
r.WbGls.Add(g);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var membersJson = zip.GetEntry("members.json");
|
var membersJson = zip.GetEntry("members.json");
|
||||||
if (membersJson != null) {
|
if (membersJson != null) {
|
||||||
using var reader = new StreamReader(membersJson.Open(), Utils.UTF8);
|
using var reader = new StreamReader(membersJson.Open(), Utils.UTF8);
|
||||||
string? line;
|
string? line;
|
||||||
while ((line = await reader.ReadLineAsync()) != null) {
|
while ((line = await reader.ReadLineAsync()) != null) {
|
||||||
var obj = JsonNode.Parse(line)!.AsObject();
|
var obj = JsonNode.Parse(line)!.AsObject();
|
||||||
var (m, b, telNrs, emailAddrs, timestamps) = obj.ToMember(kgs);
|
var (m, b, telNrs, emailAddrs, timestamps) = obj.ToMember(kgs);
|
||||||
r.Members.Add(m);
|
r.Members.Add(m);
|
||||||
if (b != null) r.BillingAddresses.Add(b);
|
if (b != null) r.BillingAddresses.Add(b);
|
||||||
r.TelephoneNumbers.AddRange(telNrs);
|
r.TelephoneNumbers.AddRange(telNrs);
|
||||||
r.EmailAddresses.AddRange(emailAddrs);
|
r.EmailAddresses.AddRange(emailAddrs);
|
||||||
if (timestamps.HasValue)
|
if (timestamps.HasValue)
|
||||||
r.Timestamps["member"].Add((m.MgNr, 0, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt));
|
r.Timestamps["member"].Add((m.MgNr, 0, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var areaComsJson = zip.GetEntry("area_commitments.json");
|
|
||||||
if (areaComsJson != null) {
|
|
||||||
using var reader = new StreamReader(areaComsJson.Open(), Utils.UTF8);
|
|
||||||
string? line;
|
|
||||||
while ((line = await reader.ReadLineAsync()) != null) {
|
|
||||||
var obj = JsonNode.Parse(line)!.AsObject();
|
|
||||||
var (areaCom, wbrd, timestamps) = obj.ToAreaCom(currentWbRde);
|
|
||||||
r.AreaCommitments.Add(areaCom);
|
|
||||||
if (wbrd != null) {
|
|
||||||
r.Riede.Add(wbrd);
|
|
||||||
}
|
}
|
||||||
if (timestamps.HasValue)
|
|
||||||
r.Timestamps["area_commitment"].Add((areaCom.FbNr, 0, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var deliveriesJson = zip.GetEntry("deliveries.json");
|
var areaComsJson = zip.GetEntry("area_commitments.json");
|
||||||
if (deliveriesJson != null) {
|
if (areaComsJson != null) {
|
||||||
using var reader = new StreamReader(deliveriesJson.Open(), Utils.UTF8);
|
using var reader = new StreamReader(areaComsJson.Open(), Utils.UTF8);
|
||||||
string? line;
|
string? line;
|
||||||
while ((line = await reader.ReadLineAsync()) != null) {
|
while ((line = await reader.ReadLineAsync()) != null) {
|
||||||
var obj = JsonNode.Parse(line)!.AsObject();
|
var obj = JsonNode.Parse(line)!.AsObject();
|
||||||
var (d, parts, mods, rde, timestamps) = obj.ToDelivery(currentLsNrs, currentDids, kgs, currentWbRde);
|
var (areaCom, wbrd, timestamps) = obj.ToAreaCom(currentWbRde);
|
||||||
r.Deliveries.Add(d);
|
r.AreaCommitments.Add(areaCom);
|
||||||
r.DeliveryParts.AddRange(parts);
|
if (wbrd != null) {
|
||||||
r.Modifiers.AddRange(mods);
|
r.Riede.Add(wbrd);
|
||||||
r.Riede.AddRange(rde);
|
}
|
||||||
if (timestamps.HasValue)
|
if (timestamps.HasValue)
|
||||||
r.Timestamps["delivery"].Add((d.Year, d.DId, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt));
|
r.Timestamps["area_commitment"].Add((areaCom.FbNr, 0, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var deliveriesJson = zip.GetEntry("deliveries.json");
|
||||||
|
if (deliveriesJson != null) {
|
||||||
|
using var reader = new StreamReader(deliveriesJson.Open(), Utils.UTF8);
|
||||||
|
string? line;
|
||||||
|
while ((line = await reader.ReadLineAsync()) != null) {
|
||||||
|
var obj = JsonNode.Parse(line)!.AsObject();
|
||||||
|
var (d, parts, mods, rde, timestamps) = obj.ToDelivery(currentLsNrs, currentDids, kgs, currentWbRde);
|
||||||
|
r.Deliveries.Add(d);
|
||||||
|
r.DeliveryParts.AddRange(parts);
|
||||||
|
r.Modifiers.AddRange(mods);
|
||||||
|
r.Riede.AddRange(rde);
|
||||||
|
if (timestamps.HasValue)
|
||||||
|
r.Timestamps["delivery"].Add((d.Year, d.DId, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception exc) when (
|
||||||
|
exc is InvalidDataException ||
|
||||||
|
exc is FileFormatException ||
|
||||||
|
exc is FileNotFoundException ||
|
||||||
|
exc is IOException) {
|
||||||
|
data.RemoveAt(data.Count - 1);
|
||||||
|
var str = $"Die Elwig-Export-Datei '{Path.GetFileName(filename)}' konnte nicht verarbeitet werden und wird übersprungen.\n\n" + exc.Message;
|
||||||
|
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
|
||||||
|
MessageBox.Show(str, "Fehler beim Importieren", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
await AddImportedFiles(Path.GetFileName(filename));
|
||||||
|
} catch (Exception exc) {
|
||||||
|
data.RemoveAt(data.Count - 1);
|
||||||
|
var str = $"Die Elwig-Export-Datei '{Path.GetFileName(filename)}' konnte nicht verarbeitet werden. Soll sie in Zukunft übersprungen werden?\n\n" + exc.Message;
|
||||||
|
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
|
||||||
|
var r = MessageBox.Show(str, "Fehler beim Importieren", MessageBoxButton.YesNo, MessageBoxImage.Error, MessageBoxResult.No);
|
||||||
|
if (r == MessageBoxResult.Yes) {
|
||||||
|
await AddImportedFiles(Path.GetFileName(filename));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -449,7 +466,7 @@ namespace Elwig.Helpers.Export {
|
|||||||
["parts"] = Deliveries.Value.Deliveries.Sum(d => d.Parts.Count),
|
["parts"] = Deliveries.Value.Deliveries.Sum(d => d.Parts.Count),
|
||||||
["filters"] = new JsonArray(Deliveries.Value.Filters.Select(f => (JsonNode)f).ToArray()),
|
["filters"] = new JsonArray(Deliveries.Value.Filters.Select(f => (JsonNode)f).ToArray()),
|
||||||
};
|
};
|
||||||
await writer.WriteAsync(obj.ToJsonString(JsonOpts));
|
await writer.WriteAsync(obj.ToJsonString(Utils.JsonOpts));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO encrypt files
|
// TODO encrypt files
|
||||||
@@ -457,28 +474,28 @@ namespace Elwig.Helpers.Export {
|
|||||||
var json = zip.CreateEntry("wb_kgs.json", CompressionLevel.SmallestSize);
|
var json = zip.CreateEntry("wb_kgs.json", CompressionLevel.SmallestSize);
|
||||||
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
||||||
foreach (var k in WbKgs.Value.WbKgs) {
|
foreach (var k in WbKgs.Value.WbKgs) {
|
||||||
await writer.WriteLineAsync(k.ToJson().ToJsonString(JsonOpts));
|
await writer.WriteLineAsync(k.ToJson().ToJsonString(Utils.JsonOpts));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Members != null) {
|
if (Members != null) {
|
||||||
var json = zip.CreateEntry("members.json", CompressionLevel.SmallestSize);
|
var json = zip.CreateEntry("members.json", CompressionLevel.SmallestSize);
|
||||||
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
||||||
foreach (var m in Members.Value.Members) {
|
foreach (var m in Members.Value.Members) {
|
||||||
await writer.WriteLineAsync(m.ToJson().ToJsonString(JsonOpts));
|
await writer.WriteLineAsync(m.ToJson().ToJsonString(Utils.JsonOpts));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (AreaComs != null) {
|
if (AreaComs != null) {
|
||||||
var json = zip.CreateEntry("area_commitments.json", CompressionLevel.SmallestSize);
|
var json = zip.CreateEntry("area_commitments.json", CompressionLevel.SmallestSize);
|
||||||
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
||||||
foreach (var c in AreaComs.Value.AreaComs) {
|
foreach (var c in AreaComs.Value.AreaComs) {
|
||||||
await writer.WriteLineAsync(c.ToJson().ToJsonString(JsonOpts));
|
await writer.WriteLineAsync(c.ToJson().ToJsonString(Utils.JsonOpts));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Deliveries != null) {
|
if (Deliveries != null) {
|
||||||
var json = zip.CreateEntry("deliveries.json", CompressionLevel.SmallestSize);
|
var json = zip.CreateEntry("deliveries.json", CompressionLevel.SmallestSize);
|
||||||
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
||||||
foreach (var d in Deliveries.Value.Deliveries) {
|
foreach (var d in Deliveries.Value.Deliveries) {
|
||||||
await writer.WriteLineAsync(d.ToJson().ToJsonString(JsonOpts));
|
await writer.WriteLineAsync(d.ToJson().ToJsonString(Utils.JsonOpts));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -727,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;
|
||||||
@@ -801,12 +818,12 @@ 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>(),
|
||||||
WeighingData = p["weighing_data"]?.AsObject().ToJsonString(JsonOpts),
|
WeighingData = p["weighing_data"]?.AsObject().ToJsonString(Utils.JsonOpts),
|
||||||
WeighingReason = p["weighing_reason"]?.AsValue().GetValue<string>(),
|
WeighingReason = p["weighing_reason"]?.AsValue().GetValue<string>(),
|
||||||
};
|
};
|
||||||
}).ToList(), json["parts"]!.AsArray().SelectMany(p => p!["modids"]!.AsArray().Select(m => new DeliveryPartModifier {
|
}).ToList(), json["parts"]!.AsArray().SelectMany(p => p!["modids"]!.AsArray().Select(m => new DeliveryPartModifier {
|
||||||
|
|||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
54
Elwig/Helpers/SerialPortWatcher.cs
Normal file
54
Elwig/Helpers/SerialPortWatcher.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
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 += (s, e) => OnDeviceArrived();
|
||||||
|
_deviceRemovalWatcher = new ManagementEventWatcher("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 3");
|
||||||
|
_deviceRemovalWatcher.EventArrived += (s, e) => OnDeviceRemoved();
|
||||||
|
_deviceArrivalWatcher.Start();
|
||||||
|
_deviceRemovalWatcher.Start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDeviceArrived() {
|
||||||
|
string[] currentPorts = SerialPort.GetPortNames();
|
||||||
|
var newPorts = currentPorts.Except(_knownPorts).ToArray();
|
||||||
|
foreach (var port in newPorts)
|
||||||
|
SerialPortConnected?.Invoke(this, port);
|
||||||
|
_knownPorts = currentPorts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDeviceRemoved() {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,40 +1,42 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.IO.Ports;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using Elwig.Dialogs;
|
using Elwig.Dialogs;
|
||||||
using System.Text;
|
using Elwig.Documents;
|
||||||
using System.Numerics;
|
|
||||||
using Elwig.Models.Entities;
|
|
||||||
using Elwig.Helpers.Billing;
|
using Elwig.Helpers.Billing;
|
||||||
using System.Runtime.InteropServices;
|
using Elwig.Models;
|
||||||
using System.Net.Http;
|
using Elwig.Models.Entities;
|
||||||
using System.Text.Json.Nodes;
|
using LinqKit;
|
||||||
using System.IO;
|
|
||||||
using MailKit.Net.Smtp;
|
using MailKit.Net.Smtp;
|
||||||
using MailKit.Security;
|
using MailKit.Security;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Reflection;
|
|
||||||
using System.Collections;
|
|
||||||
using Elwig.Documents;
|
|
||||||
using MimeKit;
|
|
||||||
using LinqKit;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
using Elwig.Models;
|
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
|
using MimeKit;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Ports;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
using System.Windows.Markup;
|
using System.Windows.Markup;
|
||||||
|
|
||||||
namespace Elwig.Helpers {
|
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 JsonSerializerOptions JsonOpts = new() { Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping };
|
||||||
|
|
||||||
public static int CurrentYear => DateTime.Now.Year;
|
public static int CurrentYear => DateTime.Now.Year;
|
||||||
public static int CurrentNextSeason => DateTime.Now.Year - (DateTime.Now.Month <= 3 ? 1 : 0);
|
public static int CurrentNextSeason => DateTime.Now.Year - (DateTime.Now.Month <= 3 ? 1 : 0);
|
||||||
@@ -500,6 +502,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();
|
||||||
@@ -517,6 +520,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;
|
||||||
@@ -537,7 +545,7 @@ namespace Elwig.Helpers {
|
|||||||
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) {
|
||||||
var d = new SaveFileDialog() {
|
var d = new SaveFileDialog() {
|
||||||
@@ -652,9 +660,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)
|
||||||
|
|||||||
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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Elwig.Helpers;
|
using Elwig.Helpers;
|
||||||
|
using Elwig.Helpers.Billing;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -62,6 +63,11 @@ namespace Elwig.Models.Entities {
|
|||||||
[Column("comment")]
|
[Column("comment")]
|
||||||
public string? Comment { get; set; }
|
public string? Comment { get; set; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string[] Comments => [.. Parts.Select(p => p.Comment).Prepend(Comment).Where(c => c != null).Cast<string>()];
|
||||||
|
[NotMapped]
|
||||||
|
public string CommentsString => string.Join(" / ", Comments);
|
||||||
|
|
||||||
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||||
public long CTime { get; set; }
|
public long CTime { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
@@ -108,16 +114,16 @@ namespace Elwig.Models.Entities {
|
|||||||
public int Weight => Parts.Select(p => p.Weight).Sum();
|
public int Weight => Parts.Select(p => p.Weight).Sum();
|
||||||
public int FilteredWeight => FilteredParts.Select(p => p.Weight).Sum();
|
public int FilteredWeight => FilteredParts.Select(p => p.Weight).Sum();
|
||||||
|
|
||||||
public IEnumerable<string> SortIds => Parts
|
public IEnumerable<RawVaribute> Vaributes => Parts
|
||||||
.GroupBy(p => p.SortId)
|
.GroupBy(p => (p.SortId, p.AttrId, p.CultId))
|
||||||
.OrderByDescending(g => g.Select(p => p.Weight).Sum())
|
.OrderByDescending(g => g.Select(p => p.Weight).Sum())
|
||||||
.Select(g => g.Key);
|
.Select(g => new RawVaribute(g.Key.SortId, g.Key.AttrId, g.Key.CultId));
|
||||||
public IEnumerable<string> FilteredSortIds => FilteredParts
|
public IEnumerable<RawVaribute> FilteredVaributes => FilteredParts
|
||||||
.GroupBy(p => p.SortId)
|
.GroupBy(p => (p.SortId, p.AttrId, p.CultId))
|
||||||
.OrderByDescending(g => g.Select(p => p.Weight).Sum())
|
.OrderByDescending(g => g.Select(p => p.Weight).Sum())
|
||||||
.Select(g => g.Key);
|
.Select(g => new RawVaribute(g.Key.SortId, g.Key.AttrId, g.Key.CultId));
|
||||||
public string SortIdString => string.Join(", ", SortIds);
|
public string VaributeString => string.Join(", ", Vaributes);
|
||||||
public string FilteredSortIdString => string.Join(", ", FilteredSortIds);
|
public string FilteredVaributeString => string.Join(", ", FilteredVaributes);
|
||||||
|
|
||||||
public Brush? Color => Parts.Select(p => p.Variety.Color).Distinct().SingleOrDefault();
|
public Brush? Color => Parts.Select(p => p.Variety.Color).Distinct().SingleOrDefault();
|
||||||
public Brush? FilteredColor => FilteredParts.Select(p => p.Variety.Color).Distinct().SingleOrDefault();
|
public Brush? FilteredColor => FilteredParts.Select(p => p.Variety.Color).Distinct().SingleOrDefault();
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,9 @@ namespace Elwig.Models.Entities {
|
|||||||
[Column("max_weight")]
|
[Column("max_weight")]
|
||||||
public int? MaxWeight { get; set; }
|
public int? MaxWeight { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public int AnnouncedWeight => Announcements.Sum(a => a.Weight);
|
public int AnnouncedWeight => AnnouncedWeightOverride ?? Announcements.Sum(a => a.Weight);
|
||||||
|
[NotMapped]
|
||||||
|
public int? AnnouncedWeightOverride { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public double? Percent => (double)AnnouncedWeight / MaxWeight * 100;
|
public double? Percent => (double)AnnouncedWeight / MaxWeight * 100;
|
||||||
|
|
||||||
|
|||||||
@@ -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 33
|
||||||
|
|
||||||
|
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;
|
||||||
@@ -80,9 +80,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 +188,38 @@ 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 (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 +507,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,
|
||||||
@@ -938,6 +962,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,
|
||||||
|
|||||||
@@ -493,6 +493,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",
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<TextBlock Margin="20,10" FontSize="12">
|
<TextBlock Margin="20,10" FontSize="12">
|
||||||
<Bold>Produkt:</Bold> Elwig<LineBreak/>
|
<Bold>Produkt:</Bold> Elwig<LineBreak/>
|
||||||
<Bold>Beschreibung:</Bold> Elektronische Winzergenossenschaftsverwaltung<LineBreak/>
|
<Bold>Beschreibung:</Bold> Elektronische Winzergenossenschaftsverwaltung<LineBreak/>
|
||||||
<Bold>Typ:</Bold> Warenwirschaftssystem<LineBreak/>
|
<Bold>Typ:</Bold> Warenwirtschaftssystem (ERP-System)<LineBreak/>
|
||||||
<Bold>Version:</Bold> <Run x:Name="Version">0.0.0.0</Run> (<Hyperlink NavigateUri="https://elwig.at/changelog" RequestNavigate="Hyperlink_RequestNavigate">Änderungsprotokoll</Hyperlink>)<LineBreak/>
|
<Bold>Version:</Bold> <Run x:Name="Version">0.0.0.0</Run> (<Hyperlink NavigateUri="https://elwig.at/changelog" RequestNavigate="Hyperlink_RequestNavigate">Änderungsprotokoll</Hyperlink>)<LineBreak/>
|
||||||
<Bold>Lizenz:</Bold> <Hyperlink NavigateUri="https://www.gnu.org/licenses/gpl-3.0.html" RequestNavigate="Hyperlink_RequestNavigate">GNU General Public License 3.0 (GPLv3)</Hyperlink><LineBreak/>
|
<Bold>Lizenz:</Bold> <Hyperlink NavigateUri="https://www.gnu.org/licenses/gpl-3.0.html" RequestNavigate="Hyperlink_RequestNavigate">GNU General Public License 3.0 (GPLv3)</Hyperlink><LineBreak/>
|
||||||
<Bold>Website:</Bold> <Hyperlink NavigateUri="https://elwig.at/" RequestNavigate="Hyperlink_RequestNavigate">https://elwig.at/</Hyperlink><LineBreak/>
|
<Bold>Website:</Bold> <Hyperlink NavigateUri="https://elwig.at/" RequestNavigate="Hyperlink_RequestNavigate">https://elwig.at/</Hyperlink><LineBreak/>
|
||||||
|
|||||||
@@ -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)"
|
||||||
@@ -265,13 +269,18 @@
|
|||||||
</Style>
|
</Style>
|
||||||
</DataGridTextColumn.CellStyle>
|
</DataGridTextColumn.CellStyle>
|
||||||
</DataGridTextColumn>
|
</DataGridTextColumn>
|
||||||
<DataGridTextColumn Header="Sorte" Binding="{Binding FilteredSortIdString}" Width="50">
|
<DataGridTextColumn Header="Sorte" Binding="{Binding FilteredVaributeString}" Width="60">
|
||||||
<DataGridTextColumn.CellStyle>
|
<DataGridTextColumn.CellStyle>
|
||||||
<Style>
|
<Style>
|
||||||
<Setter Property="TextBlock.Foreground" Value="{Binding FilteredColor}"/>
|
<Setter Property="TextBlock.Foreground" Value="{Binding FilteredColor}"/>
|
||||||
<Setter Property="TextBlock.TextAlignment" Value="Center"/>
|
<Setter Property="TextBlock.TextAlignment" Value="Center"/>
|
||||||
</Style>
|
</Style>
|
||||||
</DataGridTextColumn.CellStyle>
|
</DataGridTextColumn.CellStyle>
|
||||||
|
<DataGridTextColumn.ElementStyle>
|
||||||
|
<Style TargetType="TextBlock">
|
||||||
|
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
|
||||||
|
</Style>
|
||||||
|
</DataGridTextColumn.ElementStyle>
|
||||||
</DataGridTextColumn>
|
</DataGridTextColumn>
|
||||||
<DataGridTextColumn Header="Menge" Binding="{Binding FilteredWeight, StringFormat='{}{0:N0} kg '}" Width="75">
|
<DataGridTextColumn Header="Menge" Binding="{Binding FilteredWeight, StringFormat='{}{0:N0} kg '}" Width="75">
|
||||||
<DataGridTextColumn.CellStyle>
|
<DataGridTextColumn.CellStyle>
|
||||||
@@ -290,6 +299,7 @@
|
|||||||
<DataGridTextColumn Header="LsNr." Binding="{Binding LsNr}" Width="120"/>
|
<DataGridTextColumn Header="LsNr." Binding="{Binding LsNr}" Width="120"/>
|
||||||
<DataGridTextColumn Header="Mitglied" Binding="{Binding Member.AdministrativeName}" Width="180"/>
|
<DataGridTextColumn Header="Mitglied" Binding="{Binding Member.AdministrativeName}" Width="180"/>
|
||||||
<DataGridTextColumn Header="Zu-/Abschläge" Binding="{Binding FilteredModifiersString}" Width="150"/>
|
<DataGridTextColumn Header="Zu-/Abschläge" Binding="{Binding FilteredModifiersString}" Width="150"/>
|
||||||
|
<DataGridTextColumn Header="Kommentar" Binding="{Binding CommentsString}" Width="150"/>
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
|
|
||||||
@@ -363,8 +373,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>
|
||||||
@@ -398,7 +409,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"/>
|
||||||
@@ -557,7 +568,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"/>
|
||||||
@@ -586,18 +597,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"/>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using Elwig.ViewModels;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
@@ -34,6 +35,8 @@ namespace Elwig.Windows {
|
|||||||
|
|
||||||
private readonly Button[] WeighingButtons;
|
private readonly Button[] WeighingButtons;
|
||||||
|
|
||||||
|
private List<WineQualLevel> WineQualityLevels = [];
|
||||||
|
|
||||||
public DeliveryAdminWindow(bool receipt = false) {
|
public DeliveryAdminWindow(bool receipt = false) {
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput));
|
CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput));
|
||||||
@@ -92,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;
|
||||||
@@ -256,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) =>
|
||||||
@@ -276,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;
|
||||||
@@ -316,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) {
|
||||||
@@ -350,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();
|
||||||
|
|
||||||
@@ -483,7 +495,8 @@ namespace Elwig.Windows {
|
|||||||
var cultList = await ctx.WineCultivations.OrderBy(a => a.Name).Cast<object>().ToListAsync();
|
var cultList = await ctx.WineCultivations.OrderBy(a => a.Name).Cast<object>().ToListAsync();
|
||||||
cultList.Insert(0, new NullItem(""));
|
cultList.Insert(0, new NullItem(""));
|
||||||
ControlUtils.RenewItemsSource(CultivationInput, cultList, null, ControlUtils.RenewSourceDefault.First);
|
ControlUtils.RenewItemsSource(CultivationInput, cultList, null, ControlUtils.RenewSourceDefault.First);
|
||||||
ControlUtils.RenewItemsSource(WineQualityLevelInput, await ctx.WineQualityLevels.ToListAsync());
|
WineQualityLevels = await ctx.WineQualityLevels.ToListAsync();
|
||||||
|
ControlUtils.RenewItemsSource(WineQualityLevelInput, WineQualityLevels);
|
||||||
ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
|
ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
|
||||||
.Where(m => m.Year == y && (!IsCreating || m.IsActive))
|
.Where(m => m.Year == y && (!IsCreating || m.IsActive))
|
||||||
.OrderBy(m => m.Ordering)
|
.OrderBy(m => m.Ordering)
|
||||||
@@ -1164,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 = "";
|
||||||
@@ -1230,22 +1256,30 @@ namespace Elwig.Windows {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private WineQualLevel GetWineQualityLevel(double kmw, string? maxQualId = null) {
|
||||||
|
return WineQualityLevels
|
||||||
|
.Where(q => !q.IsPredicate && (q.MinKmw == null || q.MinKmw <= kmw))
|
||||||
|
.Where(q => maxQualId == null || q.QualId == "WEI" || q.QualId == maxQualId)
|
||||||
|
.OrderBy(q => q.MinKmw)
|
||||||
|
.Last();
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateWineQualityLevels() {
|
private void UpdateWineQualityLevels() {
|
||||||
using var ctx = new AppDbContext();
|
|
||||||
if (!GetInputValid(GradationKmwInput)) {
|
if (!GetInputValid(GradationKmwInput)) {
|
||||||
UnsetDefaultValue(WineQualityLevelInput);
|
UnsetDefaultValue(WineQualityLevelInput);
|
||||||
ComboBox_SelectionChanged(WineQualityLevelInput, null);
|
ComboBox_SelectionChanged(WineQualityLevelInput, null);
|
||||||
WineQualityLevelInput.ItemsSource = ctx.WineQualityLevels.ToList();
|
WineQualityLevelInput.ItemsSource = WineQualityLevels;
|
||||||
|
WineQualityLevelInput.SelectedItem = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var kmw = (double)ViewModel.GradationKmw!;
|
var kmw = (double)ViewModel.GradationKmw!;
|
||||||
var max = ViewModel.WineVar?.MaxQualId;
|
var max = ViewModel.WineVar?.MaxQualId;
|
||||||
var quw = ViewModel.WineVar?.IsQuw ?? true;
|
var quw = ViewModel.WineVar?.IsQuw ?? true;
|
||||||
WineQualityLevelInput.ItemsSource = ctx.WineQualityLevels
|
WineQualityLevelInput.ItemsSource = WineQualityLevels
|
||||||
.Where(q => q.MinKmw == null || q.MinKmw <= kmw)
|
.Where(q => q.MinKmw == null || q.MinKmw <= kmw)
|
||||||
.Where(q => quw || q.QualId == "WEI" || q.QualId == max)
|
.Where(q => quw || q.QualId == "WEI" || q.QualId == max)
|
||||||
.ToList();
|
.ToList();
|
||||||
var qual = ctx.GetWineQualityLevel(kmw, !quw ? max : null).GetAwaiter().GetResult();
|
var qual = GetWineQualityLevel(kmw, !quw ? max : null);
|
||||||
SetDefaultValue(WineQualityLevelInput, qual);
|
SetDefaultValue(WineQualityLevelInput, qual);
|
||||||
if (WineQualityLevelInput.SelectedItem == null || (WineQualityLevelInput.SelectedItem is WineQualLevel selected && !selected.IsPredicate)) {
|
if (WineQualityLevelInput.SelectedItem == null || (WineQualityLevelInput.SelectedItem is WineQualLevel selected && !selected.IsPredicate)) {
|
||||||
ControlUtils.SelectItem(WineQualityLevelInput, qual);
|
ControlUtils.SelectItem(WineQualityLevelInput, qual);
|
||||||
@@ -1299,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 (mod.Any(m => m.Name.Contains("Lesewagen"))) {
|
if (source.Any(m => m.Name.Contains("Lesewagen"))) {
|
||||||
ViewModel.IsLesewagen = true;
|
if (mod.Any(m => m.Name.Contains("Lesewagen"))) {
|
||||||
} else {
|
ViewModel.IsUnloadingPumped = true;
|
||||||
ViewModel.IsLesewagen = false;
|
} else {
|
||||||
|
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")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} 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")));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CheckBox_Changed(sender, evt);
|
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() {
|
||||||
@@ -1385,8 +1450,7 @@ namespace Elwig.Windows {
|
|||||||
AbgewertetInput.IsChecked = false;
|
AbgewertetInput.IsChecked = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
using var ctx = new AppDbContext();
|
var defQual = GetWineQualityLevel(ViewModel.GradationKmw!.Value, !(ViewModel.WineVar?.IsQuw ?? true) ? ViewModel.WineVar?.MaxQualId : null);
|
||||||
var defQual = ctx.GetWineQualityLevel(ViewModel.GradationKmw!.Value, !(ViewModel.WineVar?.IsQuw ?? true) ? ViewModel.WineVar?.MaxQualId : null).GetAwaiter().GetResult();
|
|
||||||
AbgewertetInput.IsChecked = !qual.IsPredicate && defQual != qual;
|
AbgewertetInput.IsChecked = !qual.IsPredicate && defQual != qual;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,14 +83,19 @@ namespace Elwig.Windows {
|
|||||||
|
|
||||||
private async Task RefreshDeliveryScheduleList() {
|
private async Task RefreshDeliveryScheduleList() {
|
||||||
using var ctx = new AppDbContext();
|
using var ctx = new AppDbContext();
|
||||||
var deliverySchedules = await ctx.DeliverySchedules
|
var list = await ctx.DeliverySchedules
|
||||||
.Where(s => s.Year == ViewModel.FilterSeason)
|
.Where(s => s.Year == ViewModel.FilterSeason)
|
||||||
.Include(s => s.Branch)
|
.Include(s => s.Branch)
|
||||||
.Include(s => s.Announcements)
|
|
||||||
.OrderBy(s => s.DateString)
|
.OrderBy(s => s.DateString)
|
||||||
.ThenBy(s => s.Branch.Name)
|
.ThenBy(s => s.Branch.Name)
|
||||||
.ThenBy(s => s.Description)
|
.ThenBy(s => s.Description)
|
||||||
|
.Select(s => new {
|
||||||
|
Schedule = s,
|
||||||
|
AnnouncedWeight = s.Announcements.Sum(a => a.Weight)
|
||||||
|
})
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
list.ForEach(v => v.Schedule.AnnouncedWeightOverride = v.AnnouncedWeight);
|
||||||
|
var deliverySchedules = list.Select(v => v.Schedule).ToList();
|
||||||
ControlUtils.RenewItemsSource(DeliveryScheduleList, deliverySchedules
|
ControlUtils.RenewItemsSource(DeliveryScheduleList, deliverySchedules
|
||||||
.Where(s => !ViewModel.FilterOnlyUpcoming || s.DateString.CompareTo(Utils.Today.ToString("yyyy-MM-dd")) >= 0)
|
.Where(s => !ViewModel.FilterOnlyUpcoming || s.DateString.CompareTo(Utils.Today.ToString("yyyy-MM-dd")) >= 0)
|
||||||
.ToList(), DeliveryScheduleList_SelectionChanged, ViewModel.FilterFromAllSchedules ? ControlUtils.RenewSourceDefault.None : ControlUtils.RenewSourceDefault.First);
|
.ToList(), DeliveryScheduleList_SelectionChanged, ViewModel.FilterFromAllSchedules ? ControlUtils.RenewSourceDefault.None : ControlUtils.RenewSourceDefault.First);
|
||||||
|
|||||||
@@ -738,7 +738,7 @@ namespace Elwig.Windows {
|
|||||||
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;
|
||||||
|
|
||||||
@@ -929,13 +929,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 +954,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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -579,7 +579,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) {
|
||||||
|
|||||||
40
README.md
40
README.md
@@ -5,3 +5,43 @@ Elwig
|
|||||||
**El**ektronische **Wi**nzer**g**enossenschaftsverwaltung (Electronic Management for Vintners' Cooperatives).
|
**El**ektronische **Wi**nzer**g**enossenschaftsverwaltung (Electronic Management for Vintners' Cooperatives).
|
||||||
|
|
||||||
Further information may be found on [the website](https://elwig.at).
|
Further information may be found on [the website](https://elwig.at).
|
||||||
|
|
||||||
|
|
||||||
|
About
|
||||||
|
=====
|
||||||
|
|
||||||
|
**Product:** Elwig
|
||||||
|
**Description:** Electronic Management for Vintners' Cooperatives
|
||||||
|
**Type:** ERP system
|
||||||
|
**Version:** 1.0.2.0 ([Changelog](./CHANGELOG.md))
|
||||||
|
**License:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
|
||||||
|
**Website:** https://elwig.at/
|
||||||
|
**Source code:** https://git.necronda.net/winzer/elwig
|
||||||
|
**Developement period:** 2022–2025
|
||||||
|
|
||||||
|
**Technology Stack:**
|
||||||
|
Language: C#
|
||||||
|
Framework: Windows Presentation Framework (WPF)
|
||||||
|
Database: [SQLite](https://sqlite.org/)
|
||||||
|
PDF creation: [WeasyPrint](https://weasyprint.org/), [RazorLight](https://github.com/toddams/RazorLight), [PdfiumViewer](https://github.com/pvginkel/PdfiumViewer)
|
||||||
|
Packaging: [WiX Toolset](https://www.firegiant.com/wixtoolset/)
|
||||||
|
|
||||||
|
|
||||||
|
Über
|
||||||
|
====
|
||||||
|
|
||||||
|
**Produkt:** Elwig
|
||||||
|
**Beschreibung:** Elektronische Winzergenossenschaftsverwaltung
|
||||||
|
**Typ:** Warenwirtschaftssystem (ERP-System)
|
||||||
|
**Version:** 1.0.2.0 ([Änderungsprotokoll](./CHANGELOG.md))
|
||||||
|
**Lizenz:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
|
||||||
|
**Website:** https://elwig.at/
|
||||||
|
**Quellcode:** https://git.necronda.net/winzer/elwig
|
||||||
|
**Entwicklungszeitraum:** 2022–2025
|
||||||
|
|
||||||
|
**Verwendete Technologien:**
|
||||||
|
Programmiersprache: C#
|
||||||
|
Framework: Windows Presentation Framework (WPF)
|
||||||
|
Datenbank: [SQLite](https://sqlite.org/)
|
||||||
|
PDF-Erstellung: [WeasyPrint](https://weasyprint.org/), [RazorLight](https://github.com/toddams/RazorLight), [PdfiumViewer](https://github.com/pvginkel/PdfiumViewer)
|
||||||
|
Paketierung: [WiX Toolset](https://www.firegiant.com/wixtoolset/)
|
||||||
|
|||||||
@@ -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="5.2.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>
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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=35" -u "elwig:ganzGeheim123!" -o "Resources\Sql\Create.sql"
|
||||||
|
|||||||
Reference in New Issue
Block a user