Compare commits

..

98 Commits

Author SHA1 Message Date
2a3a69d96f Bump version to 0.7.1
Some checks failed
Deploy / Build and Deploy (push) Has been cancelled
2024-03-11 14:54:25 +01:00
3f09717922 App: Header cleanup 2024-03-11 10:22:09 +01:00
f242b077bd AppDbContext: Fix NextMgNr() for gaps greater than 1000 2024-03-10 13:06:49 +01:00
61c8d1ee97 MemberAdminWindow: Remove Rundschreiben capabilities 2024-03-10 00:26:18 +01:00
a5df03aa2c MailWindow: Move Location field to first page 2024-03-10 00:05:15 +01:00
c0f4a484ab Printing/Pdf: Update WinziPrint version to 0.2.9 2024-03-09 22:57:36 +01:00
6a5507060a MailWindow: Return cleanly if WinziPrint fails 2 2024-03-09 21:23:58 +01:00
c70772b47d MailWindow: Return cleanly if WinziPrint fails 2024-03-09 21:17:47 +01:00
d0fe264af4 [#40] BillingTest: Fix tests 2024-03-09 21:11:20 +01:00
dc83e64db6 [#40] Billing: Add Rebelzuschlag 2024-03-09 20:24:49 +01:00
34ebc8fa34 MailWindow: Add feature to change location 2024-03-09 15:46:32 +01:00
0629f4eb1b MailWindow: Wrap text in FooterInputs 2024-03-09 15:11:52 +01:00
746d0f10de MailWindow: Order members by billing address if applicable 2024-03-09 15:08:51 +01:00
58c7eec6f8 [#40] DeliveryConfirmation: Fix last row cell count 2024-03-08 16:33:32 +01:00
e1201bc6b8 [#40] DeliveryConfirmation: Add column indication gross/net weight 2024-03-08 15:18:47 +01:00
ccd4a58007 BillingData: Compact data even more 2024-03-07 10:42:26 +01:00
b5d060aca6 Bump version to 0.7.0
Some checks failed
Deploy / Build and Deploy (push) Has been cancelled
2024-03-06 09:20:06 +01:00
271e085fdf App: Fix Version comparison in auto updater 2024-03-06 09:19:53 +01:00
e7375c7f9f app.manifest: Remove weird assemblyIdentity 2024-03-05 23:17:57 +01:00
ea6621ee57 AreaComAdminWindow: Fix GstNrInput validation by removing CheckGstNr 2024-03-05 23:06:34 +01:00
d6f1ce01fb Utils: Fix spacing 2024-03-05 17:29:58 +01:00
5a488369be Printing/Pdf: Wait for WinziPrint to be ready 2024-03-05 17:18:59 +01:00
d944aabc06 Elwig: Update NuGet packages 2024-03-05 16:37:41 +01:00
74da1ba46f [#15] MailWindow: Add email sending feature 2024-03-05 16:32:21 +01:00
0812c6a8f9 App: Remove unused import 2024-03-05 16:07:05 +01:00
d3c232d550 Document: Rename DoubleSided to DoublePaged 2024-03-05 12:19:38 +01:00
95850c1d81 [#15] MailWindow: Add feature to print 2024-03-05 12:18:02 +01:00
234710887e MemberAdminWindow: Update member delete box text 2024-03-05 11:42:52 +01:00
b6269f8131 [#16] MessageBox: Update visual style to look like current windows style 2024-03-05 11:10:09 +01:00
a5a6915db1 UpdateDialog: Swap buttons 2024-03-05 10:58:34 +01:00
77cf47e154 App: Remove IsPrintingReady 2024-03-04 21:43:13 +01:00
e9d0eec3bd Printing/Pdf: Increase init delay to 2 seconds 2024-03-04 21:35:48 +01:00
7e1843a1b3 [#8] Add auto update checker 2024-03-04 21:19:08 +01:00
ac4026571e Printing/Pdf: Wait 1 sec for process to initialize 2024-03-02 20:12:36 +01:00
fb28ce5006 workflows/test: Add installer to PATH at first position 2024-03-02 20:05:35 +01:00
46c97089e7 [#19] Printing/Pdf: Use WinziPrint's daemon function to allow parallel usage 2024-03-02 19:55:51 +01:00
376af72700 MailWindow: Add try/catch block around document creation 2024-03-02 18:57:03 +01:00
9139557cc4 Printing/Pdf: Update WinziPrint version to 0.2.3 2024-03-02 18:49:32 +01:00
37e10136f4 MailWindow: Trim folder name when previewing email docs 2024-02-29 22:35:33 +01:00
a275385b5c MailWindow: Make first page more responsive 2024-02-29 22:31:59 +01:00
060acc56c3 MailWindow: Include all payment variants 2024-02-29 18:02:44 +01:00
55c447621b Windows: Get rid of more warnings 2024-02-29 16:14:13 +01:00
247367d1bf Dtos: Get rid of more warnings 2024-02-29 16:13:46 +01:00
e693f83152 Document: Overwrite any other file in SaveTo() 2024-02-29 16:13:28 +01:00
f922388db9 Entities: Use 'required' and '= null!' to get rid of warnings 2024-02-29 15:48:09 +01:00
53a25b3be4 ContextWindow: Context has not to be Disposed
https://stackoverflow.com/questions/15666824/entity-framework-and-calling-context-dispose

https://blog.jongallant.com/2012/10/do-i-have-to-call-dispose-on-dbcontext/
2024-02-29 13:03:32 +01:00
cc72a8365e AreaComWindow: Fix wine cultivation null crash 2024-02-29 13:03:00 +01:00
cc5396711d MailWindow: Use PDF-Dokument instead of PDF-Datei 2024-02-29 12:39:58 +01:00
ccb83911b1 Member: Use upper case Eszett in Administrative name 2024-02-29 12:38:48 +01:00
20772d09ae [#15] PaymentVariantsWindow: Use MailWindow 2024-02-29 11:32:38 +01:00
624c9a6b34 [#15] MailWindow: Small quality fixes 2024-02-29 11:19:51 +01:00
09a739d135 [#15] DeliveryConfirmationsWindow: Replace with MailWindow 2024-02-29 11:14:18 +01:00
e5c462b43f [#15] MailWindow: Add Rundschreiben-Funktion 2024-02-29 10:48:48 +01:00
92c3ed991b AppDbUpdater: Switch foreign keys off when heavily altering tables 2024-02-29 10:26:58 +01:00
614e0010fd AppDbUpdater: Do not turn off foreign keys per default 2024-02-29 02:10:57 +01:00
3b94875a7f ContextWindow: Dispose context after creating new one 2024-02-29 02:04:54 +01:00
d897e44f3b AppDbUpdater: Actually check foreign key violations after updating 2024-02-29 02:03:41 +01:00
546a9f23c1 [#34] AppDbUpdater: Fix migration for area commitments 2024-02-28 17:57:35 +01:00
3a0f2e9556 MemberAdminWindow: Cleanup deletion of telnr and email addresses 2024-02-28 15:08:28 +01:00
e9f6f22bc8 MemberAdminWindow: Fix crash when editing telnr or email 2024-02-28 14:44:42 +01:00
c5b1867de8 SeasonFinishWindow: Fix typo in 'nachzeichnen' 2024-02-26 14:33:44 +01:00
4673877d36 MemberDataSheet: Never show area commitments on first page 2024-02-26 10:31:29 +01:00
665e16d78f MemberAdminWindow+DeliveryAdminWindow: Add button to jumpt to member (predecessor) 2024-02-25 19:44:18 +01:00
7181d744fc AreaComAdminWindow: Add - Keine Angabe - to wine cultivation list 2024-02-25 18:44:18 +01:00
0a42d4776a App: Rename FocusPaymentVariantsWindow to FocusPaymentVariants 2024-02-24 16:33:22 +01:00
efe91192bc DeliveryAdminWindow: Add cooldown of one second to weighing buttons 2024-02-23 23:53:07 +01:00
06a095a199 [#35] Installer: Fix WIX version detection 2024-02-23 18:31:20 +01:00
8031654e86 Billing: Use attribute only if applicable 2024-02-23 18:18:54 +01:00
424bd87c94 [#34] Billing: Fix price calculation for attributes without area commitment use 2024-02-23 16:12:31 +01:00
190ef82872 [#34] DeliveryAdminWindow: Show cultivation beside attribute 2024-02-23 12:54:09 +01:00
7b1a3b4f8b [#34] DeliveryNote: Make Attribute column smaller 2024-02-23 12:42:50 +01:00
e6cab7993f BaseDataWindow: Attributes: add description to Max. Ertrag 2024-02-22 11:13:37 +01:00
25a0722f96 Migrate: Honor attribute Huber 2024-02-22 11:12:49 +01:00
3324a9a238 MemberDataSheet: Fix bug where program crashes when no cultid is set 2024-02-22 10:52:45 +01:00
5a6317fcdb DeliveryAdminWindow: When in member mode, show only deliveries of current season 2024-02-22 10:51:42 +01:00
9fec79ef8c [#34] Billing: Collapse data more compactly 2024-02-20 23:14:00 +01:00
56fdf62c5c [#34] Third step of not using Bio as Attribute 2024-02-20 21:16:06 +01:00
f8ee478a9e Utils: Code cleanup 2024-02-20 16:38:18 +01:00
c82e8de724 [#34] Second step of not using Bio as Attribute 2024-02-20 16:36:12 +01:00
049927f90c Delivery: Use also 'netto'/'brutto' for 'gerebelt gewogen' 2024-02-19 22:27:00 +01:00
abbb5a12a6 [#34] First step of not using Bio as Attribute 2024-02-19 22:14:47 +01:00
092c5788a4 Weighing: Fix Baden scale 2024-02-23 17:46:32 +01:00
96c9890b90 MainWindow: Ask user if all windows should be closed when closing 2024-02-23 16:45:58 +01:00
958fbaae50 PaymentVariantsWindow: Allow members to have no IBAN 2024-02-23 16:24:09 +01:00
04199376d2 [#39] ChartWindow: Add try/catch block around initialization 2024-02-22 09:22:04 +01:00
6e26bd8922 Bump version to 0.6.8
Some checks failed
Deploy / Build and Deploy (push) Has been cancelled
2024-02-22 00:28:30 +01:00
ae7fdef2ea Weighing: Use App.MainDispatcher.BeginInvoke in DeliveryAdminWindow 2024-02-21 22:24:05 +01:00
c0ff852f5e Weighing: Change Schember-Evt to Schember-Async 2024-02-21 22:09:36 +01:00
10b78dfb72 Weighing: Add SchemberEventScale 2024-02-21 18:33:36 +01:00
d289a5d4bf Weighing: Update SysTecITScale spelling 2024-02-21 16:29:44 +01:00
9172222307 Bump version to 0.6.7
Some checks failed
Deploy / Build and Deploy (push) Has been cancelled
2024-02-21 15:16:24 +01:00
05a75a52cc Windows: Use App.HintContextChange() where applicable 2024-02-21 15:12:45 +01:00
8732141e6b MemberDataSheet: Show area com buckets of current year (regardless of season) 2024-02-21 15:10:27 +01:00
99ca12b276 Weighing: Restructure class structure 2024-02-21 12:57:55 +01:00
7ff069d068 ScaleTestMatzen: Use hard coded date instead of current time 2024-02-21 12:06:50 +01:00
583d5b4e3e ClientParameters: Add WG Weinland and Baden 2024-02-21 11:16:52 +01:00
3f2b5b684c Weighing: Remove unused scales 2024-02-21 11:00:30 +01:00
5db14c09ad UtilsTest: Add Scale from Gr.Inzersdorf 2024-02-21 10:50:59 +01:00
124 changed files with 3138 additions and 1241 deletions

View File

@ -25,5 +25,5 @@ jobs:
- name: Run Tests - name: Run Tests
shell: powershell shell: powershell
run: | run: |
$env:PATH += ";$(pwd)\Installer\Files" $env:PATH = "$(pwd)\Installer\Files;" + $env:PATH
$(& dotnet test Tests; $a=$lastexitcode) | findstr x*; exit $a $(& dotnet test Tests; $a=$lastexitcode) | findstr x*; exit $a

View File

@ -1,10 +1,10 @@
<Application x:Class="Elwig.App" <Application
x:Class="Elwig.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Elwig"
xmlns:ctrl="clr-namespace:Elwig.Controls" xmlns:ctrl="clr-namespace:Elwig.Controls"
StartupUri="Windows\MainWindow.xaml" StartupUri="Windows\MainWindow.xaml"
xmlns:ui="http://schemas.modernwpf.com/2019"> Exit="Application_Exit">
<Application.Resources> <Application.Resources>
<ctrl:BoolToStringConverter x:Key="BoolToStarConverter" FalseValue="" TrueValue="*"/> <ctrl:BoolToStringConverter x:Key="BoolToStarConverter" FalseValue="" TrueValue="*"/>
<ctrl:WidthToPaddingConverter x:Key="WidthToPaddingConverter"/> <ctrl:WidthToPaddingConverter x:Key="WidthToPaddingConverter"/>

View File

@ -2,7 +2,6 @@ using System;
using System.Data; using System.Data;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls;
using System.IO; using System.IO;
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Helpers.Weighing; using Elwig.Helpers.Weighing;
@ -24,6 +23,9 @@ namespace Elwig {
protected static App CurrentApp; protected static App CurrentApp;
public static int NumWindows => CurrentApp.Windows.Count; public static int NumWindows => CurrentApp.Windows.Count;
public static bool ForceShutdown { get; private set; } = false;
private readonly DispatcherTimer _autoUpdateTimer = new() { Interval = TimeSpan.FromHours(1) };
public static readonly string DataPath = @"C:\ProgramData\Elwig\"; public static readonly string DataPath = @"C:\ProgramData\Elwig\";
public static readonly string ExePath = @"C:\Program Files\Elwig\"; public static readonly string ExePath = @"C:\Program Files\Elwig\";
@ -53,9 +55,10 @@ namespace Elwig {
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<IScale> Scales { get; private set; }
public static IList<ICommandScale> CommandScales => Scales.Where(s => s is ICommandScale).Cast<ICommandScale>().ToList();
public static IList<IEventScale> EventScales => Scales.Where(s => s is IEventScale).Cast<IEventScale>().ToList();
public static ClientParameters Client { get; set; } public static ClientParameters Client { get; set; }
public static bool IsPrintingReady => Html.IsReady && Pdf.IsReady;
public static Dispatcher MainDispatcher { get; private set; } public static Dispatcher MainDispatcher { get; private set; }
public App() : base() { public App() : base() {
@ -63,7 +66,7 @@ namespace Elwig {
Directory.CreateDirectory(TempPath); Directory.CreateDirectory(TempPath);
Directory.CreateDirectory(DataPath); Directory.CreateDirectory(DataPath);
MainDispatcher = Dispatcher; MainDispatcher = Dispatcher;
Scales = Array.Empty<IScale>(); Scales = [];
CurrentApp = this; CurrentApp = this;
OverrideCulture(); OverrideCulture();
} }
@ -86,7 +89,7 @@ namespace Elwig {
} }
protected override async void OnStartup(StartupEventArgs evt) { protected override async void OnStartup(StartupEventArgs evt) {
Version = typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion.Split("+")[0] ?? "0.0.0"; Version = typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion.Split('+')[0] ?? "0.0.0";
try { try {
await AppDbUpdater.CheckDb(); await AppDbUpdater.CheckDb();
@ -96,7 +99,7 @@ namespace Elwig {
return; return;
} }
Dictionary<string, (string, string, int?, string?, string?, string?, string?, string?)> branches = new(); Dictionary<string, (string, string, int?, string?, string?, string?, string?, string?)> branches = [];
using (var ctx = new AppDbContext()) { using (var ctx = new AppDbContext()) {
branches = ctx.Branches.ToDictionary(b => b.Name.ToLower(), b => (b.ZwstId, b.Name, b.PostalDest?.AtPlz?.Plz, b.PostalDest?.AtPlz?.Ort.Name, b.Address, b.PhoneNr, b.FaxNr, b.MobileNr)); branches = ctx.Branches.ToDictionary(b => b.Name.ToLower(), b => (b.ZwstId, b.Name, b.PostalDest?.AtPlz?.Plz, b.PostalDest?.AtPlz?.Ort.Name, b.Address, b.PhoneNr, b.FaxNr, b.MobileNr));
try { try {
@ -109,29 +112,33 @@ namespace Elwig {
BranchNum = branches.Count; BranchNum = branches.Count;
} }
Utils.RunBackground("HTML Initialization", () => Html.Init(PrintingReadyChanged)); Utils.RunBackground("Temp File Cleanup", () => {
Utils.RunBackground("PDF Initialization", () => Pdf.Init(PrintingReadyChanged)); Utils.CleanupTempFiles();
return Task.CompletedTask;
});
Utils.RunBackground("HTML Initialization", () => Html.Init());
Utils.RunBackground("PDF Initialization", () => Pdf.Init());
Utils.RunBackground("JSON Schema Initialization", BillingData.Init); Utils.RunBackground("JSON Schema Initialization", BillingData.Init);
if (Config.UpdateAuto && Config.UpdateUrl != null) {
if (Utils.HasInternetConnectivity()) {
Utils.RunBackground("Auto Updater", async () => {
await Task.Delay(500);
await CheckForUpdates();
});
}
_autoUpdateTimer.Tick += new EventHandler(OnAutoUpdateTimer);
_autoUpdateTimer.Start();
}
var list = new List<IScale>(); var list = new List<IScale>();
foreach (var s in Config.Scales) { foreach (var s in Config.Scales) {
var id = s[0];
try { try {
var type = s[1]?.ToLower(); list.Add(Scale.FromConfig(s));
var model = s[2];
var cnx = s[3];
var empty = s[4];
var filling = s[5];
int? limit = s[6] == null ? null : int.Parse(s[6]);
var log = s[7];
if (type == "systec") {
list.Add(new SystecScale(id, model, cnx, empty, filling, limit, log));
} else {
throw new ArgumentException($"Invalid scale type: \"{type}\"");
}
} catch (Exception e) { } catch (Exception e) {
list.Add(new InvalidScale(id)); list.Add(new InvalidScale(s.Id));
MessageBox.Show($"Unable to create scale {s[0]}:\n\n{e.Message}", "Scale Error", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show($"Unable to create scale {s.Id}:\n\n{e.Message}", "Scale Error", MessageBoxButton.OK, MessageBoxImage.Error);
} }
} }
Scales = list; Scales = list;
@ -153,6 +160,10 @@ namespace Elwig {
base.OnStartup(evt); base.OnStartup(evt);
} }
private async void Application_Exit(object sender, ExitEventArgs evt) {
await Pdf.Cleanup();
}
public static void SetBranch(Branch b) { public static void SetBranch(Branch b) {
SetBranch((b.ZwstId, b.Name, b.PostalDest?.AtPlz?.Plz, b.PostalDest?.AtPlz?.Ort.Name, b.Address, b.PhoneNr, b.FaxNr, b.MobileNr)); SetBranch((b.ZwstId, b.Name, b.PostalDest?.AtPlz?.Plz, b.PostalDest?.AtPlz?.Ort.Name, b.Address, b.PhoneNr, b.FaxNr, b.MobileNr));
} }
@ -168,21 +179,6 @@ namespace Elwig {
BranchMobileNr = entry.Item8; BranchMobileNr = entry.Item8;
} }
private void PrintingReadyChanged() {
Dispatcher.BeginInvoke(OnPrintingReadyChanged, new EventArgs());
}
protected void OnPrintingReadyChanged(EventArgs evt) {
foreach (Window w in Windows) {
foreach (var b in ControlUtils.FindAllChildren<Button>(w).Where(b => b.Tag?.ToString() == "Print")) {
b.IsEnabled = IsPrintingReady;
}
foreach (var i in ControlUtils.FindAllChildren<MenuItem>(w).Where(i => i.Tag?.ToString() == "Print")) {
i.IsEnabled = IsPrintingReady;
}
}
}
public static async Task HintContextChange() { public static async Task HintContextChange() {
foreach (Window w in CurrentApp.Windows) { foreach (Window w in CurrentApp.Windows) {
if (w is not ContextWindow c) continue; if (w is not ContextWindow c) continue;
@ -190,6 +186,29 @@ namespace Elwig {
} }
} }
private void OnAutoUpdateTimer(object? sender, EventArgs? evt) {
foreach (Window w in CurrentApp.Windows) {
if (w is UpdateDialog) return;
}
if (Utils.HasInternetConnectivity()) {
Utils.RunBackground("Auto Updater", CheckForUpdates);
}
}
public static async Task CheckForUpdates() {
if (Config.UpdateUrl == null) return;
var latest = await Utils.GetLatestInstallerUrl(Config.UpdateUrl);
if (latest != null && new Version(latest.Value.Version) > new Version(Version)) {
await MainDispatcher.BeginInvoke(() => {
var d = new UpdateDialog(latest.Value.Version, latest.Value.Url, latest.Value.Size);
if (d.ShowDialog() == true) {
ForceShutdown = true;
Current.Shutdown();
}
});
}
}
private static T FocusWindow<T>(Func<T> constructor, Predicate<T>? selector = null) where T : Window { private static T FocusWindow<T>(Func<T> constructor, Predicate<T>? selector = null) where T : Window {
foreach (Window w in CurrentApp.Windows) { foreach (Window w in CurrentApp.Windows) {
if (w is T t && (selector == null || selector(t))) { if (w is T t && (selector == null || selector(t))) {
@ -237,10 +256,6 @@ namespace Elwig {
return FocusWindow<SeasonFinishWindow>(() => new()); return FocusWindow<SeasonFinishWindow>(() => new());
} }
public static DeliveryConfirmationsWindow FocusDeliveryConfirmations(int year) {
return FocusWindow<DeliveryConfirmationsWindow>(() => new(year), w => w.Year == year);
}
public static OriginHierarchyWindow FocusOriginHierarchy() { public static OriginHierarchyWindow FocusOriginHierarchy() {
return FocusWindow<OriginHierarchyWindow>(() => new()); return FocusWindow<OriginHierarchyWindow>(() => new());
} }
@ -251,12 +266,22 @@ namespace Elwig {
return w; return w;
} }
public static PaymentVariantsWindow FocusPaymentVariantsWindow(int year) { public static PaymentVariantsWindow FocusPaymentVariants(int year) {
return FocusWindow<PaymentVariantsWindow>(() => new(year), w => w.Year == year); return FocusWindow<PaymentVariantsWindow>(() => new(year), w => w.Year == year);
} }
public static ChartWindow FocusChartWindow(int year, int avnr) { public static ChartWindow FocusChartWindow(int year, int avnr) {
return FocusWindow<ChartWindow>(() => new(year, avnr), w => w.Year == year && w.AvNr == avnr); return FocusWindow<ChartWindow>(() => new(year, avnr), w => w.Year == year && w.AvNr == avnr);
} }
public static MemberAdminWindow FocusMember(int mgnr) {
var w = FocusWindow<MemberAdminWindow>(() => new());
w.FocusMember(mgnr);
return w;
}
public static MailWindow FocusMailWindow(int? year = null) {
return FocusWindow<MailWindow>(() => new(year), w => year == null || w.Year == year);
}
} }
} }

View File

@ -0,0 +1,33 @@
<Window x:Class="Elwig.Dialogs.UpdateDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
ResizeMode="NoResize"
ShowInTaskbar="False"
Topmost="True"
WindowStartupLocation="CenterOwner"
Title="Neues Update verfügbar - Elwig" Height="180" Width="400">
<Grid>
<TextBlock x:Name="Description" FontSize="14" Margin="0,0,0,30"
HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center">
Version <Run x:Name="VersionText" FontWeight="Bold">0.0.0</Run> von Elwig ist verfügbar!<LineBreak/>
Soll das Update heruntergeladen und<LineBreak/>
installiert werden? (ca. <Run x:Name="SizeText">100</Run> MB)<LineBreak/>
<Run FontWeight="Bold">Achtung</Run>: Elwig wird dabei geschlossen!
</TextBlock>
<ProgressBar x:Name="ProgressBar" Margin="0,0,0,27" Visibility="Hidden"
HorizontalAlignment="Center" VerticalAlignment="Center"
Height="27" Width="300" SnapsToDevicePixels="True"/>
<Button x:Name="InstallButton" Content="Installieren" Margin="10,10,115,10"
FontSize="14" HorizontalAlignment="Right" VerticalAlignment="Bottom"
Width="100" Height="27"
Click="InstallButton_Click"/>
<Button x:Name="CancelButton" Content="Abbrechen" Margin="10,10,10,10" IsCancel="True" IsDefault="True"
FontSize="14" HorizontalAlignment="Right" VerticalAlignment="Bottom"
Width="100" Height="27"/>
</Grid>
</Window>

View File

@ -0,0 +1,47 @@
using Elwig.Helpers;
using System;
using System.Diagnostics;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using System.Windows;
namespace Elwig.Dialogs {
public partial class UpdateDialog : Window {
public string Version { get; private set; }
public string Url { get; private set; }
public UpdateDialog(string version, string url, long size) {
Version = version;
Url = url;
InitializeComponent();
VersionText.Text = version;
SizeText.Text = $"{size / 1024 / 1024}";
}
private async void InstallButton_Click(object sender, RoutedEventArgs evt) {
Description.Visibility = Visibility.Hidden;
ProgressBar.Visibility = Visibility.Visible;
InstallButton.IsEnabled = false;
await Install();
DialogResult = true;
Close();
}
public async Task Install() {
var fileName = Path.Combine(App.TempPath, $"Elwig-{Version}.exe");
{
using var stream = new FileStream(fileName, FileMode.Create);
using var client = new HttpClient() {
Timeout = TimeSpan.FromSeconds(5),
};
await client.DownloadAsync(Url, stream, new Progress<double>(p => {
ProgressBar.Value = p * 100.0;
}));
}
Process.Start(fileName);
}
}
}

View File

@ -7,13 +7,12 @@ using System.Linq;
namespace Elwig.Documents { namespace Elwig.Documents {
public abstract class BusinessDocument : Document { public abstract class BusinessDocument : Document {
public bool ShowDateAndLocation = false;
public Member Member; public Member Member;
public string? Location;
public bool IncludeSender = false; public bool IncludeSender = false;
public bool UseBillingAddress = false; public bool UseBillingAddress = false;
public bool ShowDateAndLocation = false;
public string Aside; public string Aside;
public string? Location;
public BusinessDocument(string title, Member m, bool includeSender = false) : base(title) { public BusinessDocument(string title, Member m, bool includeSender = false) : base(title) {
Member = m; Member = m;
@ -40,19 +39,19 @@ namespace Elwig.Documents {
return "<colgroup>\n" + string.Join("\n", cols.Select(g => $"<col style=\"width: {g.ToString(CultureInfo.InvariantCulture)}mm;\"/>")) + "\n</colgroup>\n"; return "<colgroup>\n" + string.Join("\n", cols.Select(g => $"<col style=\"width: {g.ToString(CultureInfo.InvariantCulture)}mm;\"/>")) + "\n</colgroup>\n";
} }
public static string PrintSortenaufteilung(Dictionary<string, MemberBucket> buckets) { public static string PrintSortenaufteilung(List<MemberStat> stats) {
List<string> attributes = ["_", ""]; List<string> discrs = [""];
List<string> names = ["kein Qual.Wein", "ohne Attribut"]; List<string> names = ["ohne Attr./Bewirt."];
List<(string, string)> bucketAttrs = [ List<string> bucketAttrs = [
.. buckets .. stats
.Where(b => b.Key.Length > 2 && b.Key[2] != '_' && b.Value.DeliveryStrict > 0) .Select(s => s.Discr)
.Select(b => (b.Key[2..], b.Value.Name.Split("(")[1][..^1]))
.Distinct() .Distinct()
.OrderBy(v => v.Item1) .Where(s => s.Length > 0)
.Order()
]; ];
names.AddRange(bucketAttrs.Select(b => b.Item2)); names.AddRange(bucketAttrs);
names.Add("Gesamt"); names.Add("Gesamt");
attributes.AddRange(bucketAttrs.Select(b => b.Item1)); discrs.AddRange(bucketAttrs);
List<double> cols = [40]; List<double> cols = [40];
cols.AddRange(names.Select(_ => 125.0 / names.Count)); cols.AddRange(names.Select(_ => 125.0 / names.Count));
@ -62,19 +61,18 @@ namespace Elwig.Documents {
string.Join("", names.Select(c => $"<th>{c}</th>")) + string.Join("", names.Select(c => $"<th>{c}</th>")) +
"</tr></thead>"; "</tr></thead>";
tbl += string.Join("\n", buckets tbl += string.Join("\n", stats
.GroupBy(b => (b.Key[..2], b.Value.Name.Split("(")[0].Trim())) .GroupBy(b => b.Variety)
.Where(g => g.Sum(a => a.Value.DeliveryStrict) > 0) .OrderBy(b => b.Key)
.OrderBy(g => g.Key.Item1)
.Select(g => { .Select(g => {
var dict = g.ToDictionary(a => a.Key[2..], a => a.Value); var dict = g.ToDictionary(a => a.Discr, a => a.Weight);
var vals = attributes.Select(a => dict.TryGetValue(a, out MemberBucket value) ? value.DeliveryStrict : 0).ToList(); var vals = discrs.Select(a => dict.GetValueOrDefault(a, 0)).ToList();
return $"<tr><th>{g.Key.Item2}</th>" + string.Join("", vals.Select(v => "<td class=\"number\">" + (v == 0 ? "-" : $"{v:N0}") + "</td>")) + return $"<tr><th>{g.Key}</th>" + string.Join("", vals.Select(v => "<td class=\"number\">" + (v == 0 ? "-" : $"{v:N0}") + "</td>")) +
$"<td class=\"number\">{dict.Values.Select(v => v.DeliveryStrict).Sum():N0}</td></tr>"; $"<td class=\"number\">{dict.Values.Sum():N0}</td></tr>";
}) })
); );
var totalDict = buckets.GroupBy(b => b.Key[2..]).ToDictionary(g => g.Key, g => g.Sum(a => a.Value.DeliveryStrict)); var totalDict = stats.GroupBy(s => s.Discr).ToDictionary(g => g.Key, g => g.Sum(a => a.Weight));
var totals = attributes.Select(a => totalDict.TryGetValue(a, out int value) ? value : 0); var totals = discrs.Select(a => totalDict.TryGetValue(a, out int value) ? value : 0);
tbl += "<tr class=\"sum bold\"><td></td>" + string.Join("", totals.Select(v => $"<td class=\"number\">{v:N0}</td>")) + tbl += "<tr class=\"sum bold\"><td></td>" + string.Join("", totals.Select(v => $"<td class=\"number\">{v:N0}</td>")) +
$"<td class=\"number\">{totalDict.Values.Sum():N0}</td></tr>"; $"<td class=\"number\">{totalDict.Values.Sum():N0}</td></tr>";

View File

@ -10,13 +10,14 @@
<colgroup> <colgroup>
<col style="width: 25mm;"/> <col style="width: 25mm;"/>
<col style="width: 5mm;"/> <col style="width: 5mm;"/>
<col style="width: 20mm;"/> <col style="width: 22mm;"/>
<col style="width: 20mm;"/> <col style="width: 15mm;"/>
<col style="width: 10mm;"/> <col style="width: 10mm;"/>
<col style="width: 10mm;"/> <col style="width: 10mm;"/>
<col style="width: 15mm;"/> <col style="width: 15mm;"/>
<col style="width: 12mm;"/> <col style="width: 12mm;"/>
<col style="width: 15mm;"/> <col style="width: 13mm;"/>
<col style="width: 5mm;"/>
<col style="width: 17mm;"/> <col style="width: 17mm;"/>
<col style="width: 16mm;"/> <col style="width: 16mm;"/>
</colgroup> </colgroup>
@ -25,10 +26,11 @@
<th rowspan="2" style="text-align: left;">Lieferschein-Nr.</th> <th rowspan="2" style="text-align: left;">Lieferschein-Nr.</th>
<th rowspan="2" class="narrow">Pos.</th> <th rowspan="2" class="narrow">Pos.</th>
<th rowspan="2" style="text-align: left;">Sorte</th> <th rowspan="2" style="text-align: left;">Sorte</th>
<th rowspan="2" style="text-align: left;">Attribut</th> <th rowspan="2" style="text-align: left;">Attr./Bewirt.</th>
<th colspan="2">Gradation</th> <th colspan="2">Gradation</th>
<th colspan="2">Flächenbindung</th> <th colspan="2">Flächenbindung</th>
<th>Preis</th> <th>Preis</th>
<th class="narrow">Rbl.</th>
<th class="narrow">Zu-/Abschläge</th> <th class="narrow">Zu-/Abschläge</th>
<th>Betrag</th> <th>Betrag</th>
</tr> </tr>
@ -37,6 +39,7 @@
<th class="unit narrow">[°KMW]</th> <th class="unit narrow">[°KMW]</th>
<th class="unit" colspan="2">[kg]</th> <th class="unit" colspan="2">[kg]</th>
<th class="unit">[@Model.CurrencySymbol/kg]</th> <th class="unit">[@Model.CurrencySymbol/kg]</th>
<th class="narrow unit">[%]</th>
<th class="unit">[@Model.CurrencySymbol]</th> <th class="unit">[@Model.CurrencySymbol]</th>
<th class="unit">[@Model.CurrencySymbol]</th> <th class="unit">[@Model.CurrencySymbol]</th>
</tr> </tr>
@ -50,14 +53,17 @@
<td rowspan="@rows">@p.LsNr</td> <td rowspan="@rows">@p.LsNr</td>
<td rowspan="@rows">@p.DPNr</td> <td rowspan="@rows">@p.DPNr</td>
<td class="small">@p.Variety</td> <td class="small">@p.Variety</td>
<td class="small">@p.Attribute</td> <td class="small">
@p.Attribute@(p.Attribute != null && p.Cultivation != null ? " / " : "")@p.Cultivation
@((p.Attribute != null || p.Cultivation != null) && p.QualId == "WEI" ? " / " : "")@Raw(p.QualId == "WEI" ? "<i>abgew.</i>" : "")
</td>
<td rowspan="@rows" class="center">@($"{p.Gradation.Oe:N0}")</td> <td rowspan="@rows" class="center">@($"{p.Gradation.Oe:N0}")</td>
<td rowspan="@rows" class="center">@($"{p.Gradation.Kmw:N1}")</td> <td rowspan="@rows" class="center">@($"{p.Gradation.Kmw:N1}")</td>
} }
@if (i > 0 && i <= p.Modifiers.Length) { @if (i > 0 && i <= p.Modifiers.Length) {
<td colspan="2" class="small mod">@p.Modifiers[i - 1]</td> <td colspan="4" class="small mod">@p.Modifiers[i - 1]</td>
} else if (i > 0) { } else if (i > 0) {
<td colspan="2"></td> <td colspan="4"></td>
} }
@if (i < p.Buckets.Length) { @if (i < p.Buckets.Length) {
var bucket = p.Buckets[i]; var bucket = p.Buckets[i];
@ -65,10 +71,12 @@
<td class="number">@($"{bucket.Value:N0}")</td> <td class="number">@($"{bucket.Value:N0}")</td>
<td class="number">@($"{bucket.Price:N4}")</td> <td class="number">@($"{bucket.Price:N4}")</td>
} else { } else {
<td colspan="3"></td> <td></td>
} }
@if (i == p.Buckets.Length - 1) { @if (i == p.Buckets.Length - 1) {
var rebelMod = p.WeighingModifier * 100;
var totalMod = p.TotalModifiers ?? 0; var totalMod = p.TotalModifiers ?? 0;
<td class="tiny center">@(rebelMod == 0 ? "-" : (Utils.GetSign(rebelMod) + $"{Math.Abs(rebelMod):0.0##}"))</td>
<td class="number@(totalMod == 0 ? " center" : "")">@(totalMod == 0 ? "-" : Utils.GetSign(totalMod) + $"{Math.Abs(totalMod):N2}")</td> <td class="number@(totalMod == 0 ? " center" : "")">@(totalMod == 0 ? "-" : Utils.GetSign(totalMod) + $"{Math.Abs(totalMod):N2}")</td>
<td class="number">@($"{p.Amount:N2}")</td> <td class="number">@($"{p.Amount:N2}")</td>
} else { } else {

View File

@ -13,6 +13,7 @@ namespace Elwig.Documents {
public DeliveryConfirmationDeliveryData Data; public DeliveryConfirmationDeliveryData Data;
public string? Text = App.Client.TextDeliveryConfirmation; public string? Text = App.Client.TextDeliveryConfirmation;
public Dictionary<string, MemberBucket> MemberBuckets; public Dictionary<string, MemberBucket> MemberBuckets;
public List<MemberStat> MemberStats;
public DeliveryConfirmation(AppDbContext ctx, int year, Member m, DeliveryConfirmationDeliveryData data) : public DeliveryConfirmation(AppDbContext ctx, int year, Member m, DeliveryConfirmationDeliveryData data) :
base($"{Name} {year}", m) { base($"{Name} {year}", m) {
@ -23,6 +24,7 @@ namespace Elwig.Documents {
DocumentId = $"Anl.-Best. {Season.Year}/{m.MgNr}"; DocumentId = $"Anl.-Best. {Season.Year}/{m.MgNr}";
Data = data; Data = data;
MemberBuckets = ctx.GetMemberBuckets(Season.Year, m.MgNr).GetAwaiter().GetResult(); MemberBuckets = ctx.GetMemberBuckets(Season.Year, m.MgNr).GetAwaiter().GetResult();
MemberStats = AppDbContext.GetMemberStats(Season.Year, m.MgNr).GetAwaiter().GetResult();
} }
} }
} }

View File

@ -10,14 +10,15 @@
<colgroup> <colgroup>
<col style="width: 25mm;"/> <col style="width: 25mm;"/>
<col style="width: 5mm;"/> <col style="width: 5mm;"/>
<col style="width: 20mm;"/> <col style="width: 24mm;"/>
<col style="width: 21mm;"/> <col style="width: 16mm;"/>
<col style="width: 19mm;"/> <col style="width: 17mm;"/>
<col style="width: 10mm;"/> <col style="width: 10mm;"/>
<col style="width: 10mm;"/> <col style="width: 10mm;"/>
<col style="width: 15mm;"/> <col style="width: 15mm;"/>
<col style="width: 12mm;"/> <col style="width: 12mm;"/>
<col style="width: 14mm;"/> <col style="width: 14mm;"/>
<col style="width: 3mm;"/>
<col style="width: 14mm;"/> <col style="width: 14mm;"/>
</colgroup> </colgroup>
<thead> <thead>
@ -25,11 +26,19 @@
<th rowspan="2" style="text-align: left;">Lieferschein-Nr.</th> <th rowspan="2" style="text-align: left;">Lieferschein-Nr.</th>
<th rowspan="2" class="narrow">Pos.</th> <th rowspan="2" class="narrow">Pos.</th>
<th rowspan="2" style="text-align: left;">Sorte</th> <th rowspan="2" style="text-align: left;">Sorte</th>
<th rowspan="2" style="text-align: left;">Attribut</th> <th rowspan="2" style="text-align: left;">Attr./Bewirt.</th>
<th rowspan="2" style="text-align: left;">Qualitätsstufe</th> <th rowspan="2" style="text-align: left;">Qualitätsstufe</th>
<th colspan="2">Gradation</th> <th colspan="2">Gradation</th>
<th colspan="2">Flächenbindung</th> <th colspan="2">Flächenbindung</th>
<th>Gewicht</th> <th>Gewicht</th>
<th rowspan="3" style="padding: 0;">
<svg width="10" height="40" xmlns="http://www.w3.org/2000/svg">
<text x="-40" y="5" transform="rotate(270)" font-size="8pt" font-style="italic" font-family="Times New Roman"
style="text-anchor: start; alignment-baseline: middle;">
bto./nto.
</text>
</svg>
</th>
<th>Davon<br/>abzuwerten</th> <th>Davon<br/>abzuwerten</th>
</tr> </tr>
<tr> <tr>
@ -53,7 +62,7 @@
<td rowspan="@rows">@p.LsNr</td> <td rowspan="@rows">@p.LsNr</td>
<td rowspan="@rows">@p.DPNr</td> <td rowspan="@rows">@p.DPNr</td>
<td class="small">@p.Variety</td> <td class="small">@p.Variety</td>
<td class="small">@p.Attribute</td> <td class="small">@p.Attribute@(p.Attribute != null && p.Cultivation != null ? " / " : "")@p.Cultivation</td>
<td class="small">@p.QualityLevel</td> <td class="small">@p.QualityLevel</td>
<td rowspan="@rows" class="center">@($"{p.Gradation.Oe:N0}")</td> <td rowspan="@rows" class="center">@($"{p.Gradation.Oe:N0}")</td>
<td rowspan="@rows" class="center">@($"{p.Gradation.Kmw:N1}")</td> <td rowspan="@rows" class="center">@($"{p.Gradation.Kmw:N1}")</td>
@ -72,8 +81,10 @@
} }
@if (i == p.Buckets.Length - 1) { @if (i == p.Buckets.Length - 1) {
<td class="number">@($"{p.Weight:N0}")</td> <td class="number">@($"{p.Weight:N0}")</td>
<td class="small">@(p.IsNetWeight ? "n" : "b")</td>
} else { } else {
<td></td> <td></td>
<td></td>
} }
@if (first) { @if (first) {
<td rowspan="@rows" class="number"></td> <td rowspan="@rows" class="number"></td>
@ -87,10 +98,11 @@
<td colspan="8">Gesamt:</td> <td colspan="8">Gesamt:</td>
<td colspan="2" class="number">@($"{Model.Data.Rows.Sum(p => p.Weight):N0}")</td> <td colspan="2" class="number">@($"{Model.Data.Rows.Sum(p => p.Weight):N0}")</td>
<td></td> <td></td>
<td></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@Raw(BusinessDocument.PrintSortenaufteilung(Model.MemberBuckets)) @Raw(BusinessDocument.PrintSortenaufteilung(Model.MemberStats))
@Raw(Model.PrintBucketTable(Model.Season, Model.MemberBuckets, includePayment: true)) @Raw(Model.PrintBucketTable(Model.Season, Model.MemberBuckets, includePayment: true))
<div style="margin-top: 2em;"> <div style="margin-top: 2em;">
@if (Model.Text != null) { @if (Model.Text != null) {

View File

@ -8,10 +8,10 @@
<table class="delivery large"> <table class="delivery large">
<colgroup> <colgroup>
<col style="width: 10.00mm;"/> <col style="width: 10.00mm;"/>
<col style="width: 21.25mm;"/> <col style="width: 21.00mm;"/>
<col style="width: 21.25mm;"/> <col style="width: 25.00mm;"/>
<col style="width: 21.25mm;"/> <col style="width: 19.50mm;"/>
<col style="width: 21.25mm;"/> <col style="width: 19.50mm;"/>
<col style="width: 30.00mm;"/> <col style="width: 30.00mm;"/>
<col style="width: 12.50mm;"/> <col style="width: 12.50mm;"/>
<col style="width: 12.50mm;"/> <col style="width: 12.50mm;"/>
@ -43,6 +43,14 @@
<td class="center">@($"{part.Kmw:N1}")</td> <td class="center">@($"{part.Kmw:N1}")</td>
<td class="number">@($"{part.Weight:N0}")</td> <td class="number">@($"{part.Weight:N0}")</td>
</tr> </tr>
@if (part.Cultivation != null) {
<tr><td></td><td><i>Bewirtschaftung:</i></td><td colspan="4"><b>
@part.Cultivation.Name
@if(part.Cultivation.Description != null) {
@("(")@part.Cultivation.Description@(")")
}
</b></td></tr>
}
<tr><td></td><td colspan="5" style="white-space: pre;"><i>Herkunft:</i> @part.OriginString</td></tr> <tr><td></td><td colspan="5" style="white-space: pre;"><i>Herkunft:</i> @part.OriginString</td></tr>
@if (part.Modifiers.Count() > 0) { @if (part.Modifiers.Count() > 0) {
var first = true; var first = true;
@ -52,8 +60,8 @@
} }
} }
<tr><td></td><td colspan="5"> <tr><td></td><td colspan="5">
@Raw(part.ManualWeighing ? "<i>Handwiegung</i>" : $"<i>Waage:</i> {part.ScaleId ?? "?"}, <i>ID:</i> {part.WeighingId ?? "?"}") @Raw(part.IsManualWeighing ? "<i>Handwiegung</i>" : $"<i>Waage:</i> {part.ScaleId ?? "?"}, <i>ID:</i> {part.WeighingId ?? "?"}")
(@(part.IsGerebelt ? "gerebelt gewogen" : "nicht gerebelt gewogen"))@Raw(part.WeighingReason != null ? $", <i>Begründung:</i>" : "") @part.WeighingReason (@(part.IsNetWeight ? "netto/gerebelt gewogen" : "brutto/nicht gerebelt gewogen"))@Raw(part.WeighingReason != null ? $", <i>Begründung:</i>" : "") @part.WeighingReason
</td></tr> </td></tr>
@if (part.Comment != null) { @if (part.Comment != null) {
<tr><td></td><td colspan="5"><i>Anmerkung:</i> @part.Comment</td></tr> <tr><td></td><td colspan="5"><i>Anmerkung:</i> @part.Comment</td></tr>

View File

@ -57,6 +57,9 @@ main table .small {
main table .large { main table .large {
font-size: 12pt; font-size: 12pt;
} }
main table .tiny {
font-size: 6pt;
}
main table.number td, main table.number td,
main table.number th { main table.number th {

View File

@ -5,18 +5,23 @@ using Elwig.Helpers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Elwig.Helpers.Printing; using Elwig.Helpers.Printing;
using MimeKit;
namespace Elwig.Documents { namespace Elwig.Documents {
public abstract partial class Document : IDisposable { public abstract partial class Document : IDisposable {
public static string Name => "Dokument"; public static string Name => "Dokument";
private static readonly double GenerationProportion = 0.125; protected static readonly double GenerationProportion = 0.125;
private TempFile? _pdfFile = null; protected TempFile? _pdfFile = null;
protected string? _pdfPath;
protected string? PdfPath => _pdfPath ?? _pdfFile?.FilePath;
public int? TotalPages { get; private set; }
public int? Pages => TotalPages / (DoublePaged ? 2 : 1);
public bool ShowFoldMarks = App.Config.Debug; public bool ShowFoldMarks = App.Config.Debug;
public bool DoubleSided = false; public bool DoublePaged = false;
public string DataPath; public string DataPath;
public int CurrentNextSeason; public int CurrentNextSeason;
@ -59,6 +64,10 @@ namespace Elwig.Documents {
return new MergedDocument(docs); return new MergedDocument(docs);
} }
public static Document FromPdf(string path) {
return new PdfDocument(path);
}
private async Task<string> Render() { private async Task<string> Render() {
string name; string name;
if (this is BusinessLetter) { if (this is BusinessLetter) {
@ -87,20 +96,29 @@ namespace Elwig.Documents {
public async Task Generate(IProgress<double>? progress = null) { public async Task Generate(IProgress<double>? progress = null) {
progress?.Report(0.0); progress?.Report(0.0);
if (this is MergedDocument m) { if (this is PdfDocument) {
// nothing to do
} else if (this is MergedDocument m) {
var pdf = new TempFile("pdf"); var pdf = new TempFile("pdf");
var tmpHtmls = new List<TempFile>(); var tmpHtmls = new List<TempFile>();
var tmpFiles = new List<string>();
var n = m.Documents.Count(); var n = m.Documents.Count();
int i = 0; int i = 0;
foreach (var doc in m.Documents) { foreach (var doc in m.Documents) {
if (doc is PdfDocument) {
tmpFiles.Add(doc.PdfPath!);
continue;
}
var tmpHtml = new TempFile("html"); var tmpHtml = new TempFile("html");
await File.WriteAllTextAsync(tmpHtml.FilePath, await doc.Render(), Utils.UTF8); await File.WriteAllTextAsync(tmpHtml.FilePath, await doc.Render(), Utils.UTF8);
tmpHtmls.Add(tmpHtml); tmpHtmls.Add(tmpHtml);
tmpFiles.Add((doc is Letterhead ? "#" : "") + tmpHtml.FileName);
i++; i++;
progress?.Report(GenerationProportion * 100 * i / n); progress?.Report(GenerationProportion * 100 * i / n);
} }
progress?.Report(GenerationProportion * 100); progress?.Report(GenerationProportion * 100);
await Pdf.Convert(tmpHtmls.Select(f => f.FileName), pdf.FileName, DoubleSided, new Progress<double>(v => progress?.Report(GenerationProportion * 100 + v * (1 - GenerationProportion)))); var pages = await Pdf.Convert(tmpFiles, pdf.FileName, DoublePaged, new Progress<double>(v => progress?.Report(GenerationProportion * 100 + v * (1 - GenerationProportion))));
TotalPages = pages.Pages;
foreach (var tmp in tmpHtmls) { foreach (var tmp in tmpHtmls) {
tmp.Dispose(); tmp.Dispose();
} }
@ -110,7 +128,8 @@ namespace Elwig.Documents {
using (var tmpHtml = new TempFile("html")) { using (var tmpHtml = new TempFile("html")) {
await File.WriteAllTextAsync(tmpHtml.FilePath, await Render(), Utils.UTF8); await File.WriteAllTextAsync(tmpHtml.FilePath, await Render(), Utils.UTF8);
progress?.Report(50.0); progress?.Report(50.0);
await Pdf.Convert(tmpHtml.FilePath, pdf.FilePath, DoubleSided); var pages = await Pdf.Convert(tmpHtml.FilePath, pdf.FilePath, DoublePaged);
TotalPages = pages.Pages;
} }
_pdfFile = pdf; _pdfFile = pdf;
} }
@ -118,13 +137,13 @@ namespace Elwig.Documents {
} }
public void SaveTo(string pdfPath) { public void SaveTo(string pdfPath) {
if (_pdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet"); if (PdfPath == null) throw new InvalidOperationException("Pdf file has not been generated yet");
File.Copy(_pdfFile.FilePath, pdfPath); File.Copy(PdfPath, pdfPath, true);
} }
public async Task Print(int copies = 1) { public async Task Print(int copies = 1) {
if (_pdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet"); if (PdfPath == null) throw new InvalidOperationException("Pdf file has not been generated yet");
await Pdf.Print(_pdfFile.FilePath, copies); await Pdf.Print(PdfPath, copies);
} }
public void Show() { public void Show() {
@ -132,10 +151,24 @@ namespace Elwig.Documents {
Pdf.Show(_pdfFile.NewReference(), Title + (this is BusinessDocument b ? $" - {b.Member.Name}" : "")); Pdf.Show(_pdfFile.NewReference(), Title + (this is BusinessDocument b ? $" - {b.Member.Name}" : ""));
} }
private class MergedDocument : Document { public MimePart AsEmailAttachment(string filename) {
public IEnumerable<Document> Documents; if (PdfPath == null) throw new InvalidOperationException("Pdf file has not been generated yet");
public MergedDocument(IEnumerable<Document> docs) : base("Mehrere Dokumente") { return new("application", "pdf") {
Documents = docs; Content = new MimeContent(File.OpenRead(PdfPath)),
ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
ContentTransferEncoding = ContentEncoding.Base64,
FileName = filename
};
}
private class MergedDocument(IEnumerable<Document> docs) : Document("Mehrere Dokumente") {
public IEnumerable<Document> Documents = docs;
}
private class PdfDocument : Document {
public PdfDocument(string pdfPath) :
base(Path.GetFileNameWithoutExtension(pdfPath)) {
_pdfPath = pdfPath;
} }
} }
} }

View File

@ -10,7 +10,7 @@
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\Document.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\Document.css"/>
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\Document.Page.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\Document.Page.css"/>
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\Document.Table.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\Document.Table.css"/>
@if (Model.DoubleSided) { @if (Model.DoublePaged) {
<style> <style>
@@page :left { @@page :left {
margin: 25mm 25mm 35mm 20mm; margin: 25mm 25mm 35mm 20mm;
@ -38,7 +38,7 @@
</div> </div>
<footer>@Raw(Model.Footer)</footer> <footer>@Raw(Model.Footer)</footer>
</div> </div>
@if (Model.DoubleSided) { @if (Model.DoublePaged) {
<div class="footer-wrapper left"> <div class="footer-wrapper left">
<div class="pre-footer"> <div class="pre-footer">
<span class="page"></span> <span class="page"></span>

View File

@ -15,7 +15,7 @@ namespace Elwig.Documents {
public MemberDataSheet(Member m, AppDbContext ctx) : base($"{Name} {m.AdministrativeName}", m) { public MemberDataSheet(Member m, AppDbContext ctx) : base($"{Name} {m.AdministrativeName}", m) {
DocumentId = $"{Name} {m.MgNr}"; DocumentId = $"{Name} {m.MgNr}";
Season = ctx.Seasons.ToList().MaxBy(s => s.Year) ?? throw new ArgumentException("invalid season"); Season = ctx.Seasons.ToList().MaxBy(s => s.Year) ?? throw new ArgumentException("invalid season");
MemberBuckets = ctx.GetMemberBuckets(Season.Year, m.MgNr).GetAwaiter().GetResult(); MemberBuckets = ctx.GetMemberBuckets(Utils.CurrentYear, m.MgNr).GetAwaiter().GetResult();
} }
} }
} }

View File

@ -158,6 +158,7 @@
} }
@if (areaComs.Count != 0) { @if (areaComs.Count != 0) {
<br class="area-commitements"/>
<h2>Flächenbindungen per @($"{Model.Date:dd.MM.yyyy}")</h2> <h2>Flächenbindungen per @($"{Model.Date:dd.MM.yyyy}")</h2>
<table class="area-commitements"> <table class="area-commitements">
<colgroup> <colgroup>
@ -196,7 +197,7 @@
<td>@areaCom.Rd?.Name</td> <td>@areaCom.Rd?.Name</td>
<td class="text">@areaCom.GstNr.Replace(",", ", ").Replace("-", "")</td> <td class="text">@areaCom.GstNr.Replace(",", ", ").Replace("-", "")</td>
<td class="number">@($"{areaCom.Area:N0}")</td> <td class="number">@($"{areaCom.Area:N0}")</td>
<td class="center">@areaCom.WineCult.Name</td> <td class="center">@areaCom.WineCult?.Name</td>
<td class="center">@(areaCom.YearTo == null ? $"ab {areaCom.YearFrom}" : $"{areaCom.YearFrom}{areaCom.YearTo}")</td> <td class="center">@(areaCom.YearTo == null ? $"ab {areaCom.YearFrom}" : $"{areaCom.YearFrom}{areaCom.YearTo}")</td>
</tr> </tr>
lastContract = contractType.AreaComType.DisplayName; lastContract = contractType.AreaComType.DisplayName;

View File

@ -22,3 +22,9 @@ table.area-commitements td.text {
table.area-commitements tr.sum { table.area-commitements tr.sum {
font-size: 12pt; font-size: 12pt;
} }
@page :not(:first) {
br.area-commitements {
display: none;
}
}

View File

@ -7,8 +7,10 @@
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<PreserveCompilationContext>true</PreserveCompilationContext> <PreserveCompilationContext>true</PreserveCompilationContext>
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon> <ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
<Version>0.6.6</Version> <Version>0.7.1</Version>
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages> <SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -23,16 +25,17 @@
</Target> </Target>
<ItemGroup> <ItemGroup>
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.5.1" /> <PackageReference Include="Extended.Wpf.Toolkit" Version="4.6.0" />
<PackageReference Include="LinqKit" Version="1.2.5" /> <PackageReference Include="LinqKit" Version="1.2.5" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.26" /> <PackageReference Include="MailKit" Version="4.4.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.1" /> <PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.27" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="8.0.0" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2210.55" /> <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2365.46" />
<PackageReference Include="NJsonSchema" Version="11.0.0" /> <PackageReference Include="NJsonSchema" Version="11.0.0" />
<PackageReference Include="RazorLight" Version="2.3.1" /> <PackageReference Include="RazorLight" Version="2.3.1" />
<PackageReference Include="ScottPlot.WPF" Version="5.0.19" /> <PackageReference Include="ScottPlot.WPF" Version="5.0.21" />
<PackageReference Include="System.IO.Ports" Version="8.0.0" /> <PackageReference Include="System.IO.Ports" Version="8.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" /> <PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
</ItemGroup> </ItemGroup>

View File

@ -0,0 +1,22 @@
using System;
using System.Windows.Input;
namespace Elwig.Helpers {
public class ActionCommand : ICommand {
public event EventHandler CanExecuteChanged;
private readonly Action Action;
public ActionCommand(Action action) {
Action = action;
}
public void Execute(object parameter) {
Action();
}
public bool CanExecute(object parameter) {
return true;
}
}
}

View File

@ -11,12 +11,14 @@ using System.Text.RegularExpressions;
using System.Collections.Generic; using System.Collections.Generic;
using Elwig.Models.Dtos; using Elwig.Models.Dtos;
using System.Reflection; using System.Reflection;
using System.Data;
namespace Elwig.Helpers { namespace Elwig.Helpers {
public record struct AreaComBucket(int Area, int Obligation, int Right); public record struct AreaComBucket(int Area, int Obligation, int Right);
public record struct UnderDelivery(int Weight, int Diff); public record struct UnderDelivery(int Weight, int Diff);
public record struct MemberBucket(string Name, int Area, int Obligation, int Right, int Delivery, int DeliveryStrict, int Payment); public record struct MemberBucket(string Name, int Area, int Obligation, int Right, int Delivery, int DeliveryStrict, int Payment);
public record struct MemberStat(string Variety, string Discr, int Weight);
public class AppDbContext : DbContext { public class AppDbContext : DbContext {
@ -122,6 +124,21 @@ namespace Elwig.Helpers {
return await cmd.ExecuteScalarAsync(); return await cmd.ExecuteScalarAsync();
} }
public static async Task<(string Table, long RowId, string Parent, long FkId)[]> ForeignKeyCheck(SqliteConnection cnx) {
using var cmd = cnx.CreateCommand();
cmd.CommandText = "PRAGMA foreign_key_check";
using var reader = await cmd.ExecuteReaderAsync();
var list = new List<(string, long, string, long)>();
while (await reader.ReadAsync()) {
var table = reader.GetString(0);
var rowid = reader.GetInt64(1);
var parent = reader.GetString(2);
var fkid = reader.GetInt64(3);
list.Add((table, rowid, parent, fkid));
}
return [.. list];
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
optionsBuilder.UseSqlite(ConnectionString); optionsBuilder.UseSqlite(ConnectionString);
optionsBuilder.UseLazyLoadingProxies(); optionsBuilder.UseLazyLoadingProxies();
@ -159,10 +176,14 @@ namespace Elwig.Helpers {
return await WineAttributes.FindAsync(attrId) != null; return await WineAttributes.FindAsync(attrId) != null;
} }
public async Task<bool> CultIdExists(string cultId) {
return await WineCultivations.FindAsync(cultId) != null;
}
public async Task<int> NextMgNr() { public async Task<int> NextMgNr() {
int c = 0; int c = 0;
(await Members.OrderBy(m => m.MgNr).Select(m => m.MgNr).ToListAsync()) (await Members.OrderBy(m => m.MgNr).Select(m => m.MgNr).ToListAsync())
.ForEach(a => { if (a <= c + 1000) c = a; }); .ForEach(a => { if (a <= c + 10000) c = a; });
return c + 1; return c + 1;
} }
@ -384,5 +405,31 @@ namespace Elwig.Helpers {
} }
return buckets; return buckets;
} }
public static async Task<List<MemberStat>> GetMemberStats(int year, int mgnr, SqliteConnection? cnx = null) {
var ownCnx = cnx == null;
cnx ??= await ConnectAsync();
var list = new List<MemberStat>();
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $"""
SELECT v.name AS variety,
COALESCE(a.name, '') || IIF(a.name IS NOT NULL AND c.name IS NOT NULL, ' / ', '') || COALESCE(c.name, '') AS disc,
SUM(weight) AS weight
FROM v_delivery d
LEFT JOIN wine_variety v ON v.sortid = d.sortid
LEFT JOIN wine_attribute a ON a.attrid = d.attrid
LEFT JOIN wine_cultivation c ON c.cultid = d.cultid
WHERE d.year = {year} AND d.mgnr = {mgnr}
GROUP BY d.sortid, d.attrid, d.cultid
ORDER BY variety, disc;
""";
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) {
list.Add(new(reader.GetString(0), reader.GetString(1), reader.GetInt32(2)));
}
}
if (ownCnx) await cnx.DisposeAsync();
return list;
}
} }
} }

View File

@ -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 = 16; public static readonly int RequiredSchemaVersion = 18;
private static int VersionOffset = 0; private static int VersionOffset = 0;
@ -73,16 +73,19 @@ namespace Elwig.Helpers {
await AppDbContext.ExecuteBatch(cnx, """ await AppDbContext.ExecuteBatch(cnx, """
PRAGMA locking_mode = EXCLUSIVE; PRAGMA locking_mode = EXCLUSIVE;
PRAGMA foreign_keys = OFF;
BEGIN EXCLUSIVE; BEGIN EXCLUSIVE;
"""); """);
foreach (var script in toExecute) { foreach (var script in toExecute) {
await AppDbContext.ExecuteEmbeddedScript(cnx, asm, script); await AppDbContext.ExecuteEmbeddedScript(cnx, asm, script);
} }
var violations = await AppDbContext.ForeignKeyCheck(cnx);
if (violations.Length > 0) {
throw new Exception($"Foreign key violations ({violations.Length}):\n" + string.Join("\n", violations
.Select(v => $"{v.Table} - {v.RowId} - {v.Parent} - {v.FkId}")));
}
await AppDbContext.ExecuteBatch(cnx, $""" await AppDbContext.ExecuteBatch(cnx, $"""
PRAGMA foreign_key_check;
COMMIT; COMMIT;
PRAGMA foreign_keys = ON;
VACUUM; VACUUM;
PRAGMA schema_version = {toVersion * 100 + VersionOffset}; PRAGMA schema_version = {toVersion * 100 + VersionOffset};
"""); """);

View File

@ -42,6 +42,15 @@ namespace Elwig.Helpers.Billing {
set => SetConsider(value, "consider_auto_business_shares"); set => SetConsider(value, "consider_auto_business_shares");
} }
public double NetWeightModifier {
get => GetWeightModifier("net_weight_modifier", "Rebelzuschlag");
set => SetWeightModifier(value, "net_weight_modifier", "Rebelzuschlag");
}
public double GrossWeightModifier {
get => GetWeightModifier("gross_weight_modifier");
set => SetWeightModifier(value, "gross_weight_modifier");
}
private bool GetConsider(string name, string? wgMasterName = null) { private bool GetConsider(string name, string? wgMasterName = null) {
return ((Mode == CalculationMode.Elwig) ? Data[name] : Data[wgMasterName ?? ""])?.AsValue().GetValue<bool>() ?? false; return ((Mode == CalculationMode.Elwig) ? Data[name] : Data[wgMasterName ?? ""])?.AsValue().GetValue<bool>() ?? false;
} }
@ -56,6 +65,23 @@ namespace Elwig.Helpers.Billing {
} }
} }
private double GetWeightModifier(string name, string? wgMasterName = null) {
var isElwig = (Mode == CalculationMode.Elwig);
var val = (isElwig ? Data[name] : Data[wgMasterName ?? ""])?.AsValue().GetValue<double>() ?? 0;
return isElwig ? val : val / 100.0;
}
private void SetWeightModifier(double value, string name, string? wgMasterName = null) {
var isElwig = (Mode == CalculationMode.Elwig);
if (Mode == CalculationMode.WgMaster && wgMasterName == null) {
return;
} else if (value != 0) {
Data[isElwig ? name : wgMasterName ?? ""] = isElwig ? value : value * 100.0;
} else {
Data.Remove(isElwig ? name : wgMasterName ?? "");
}
}
public BillingData(JsonObject data) { public BillingData(JsonObject data) {
Data = data; Data = data;
var mode = Data["mode"]?.GetValue<string>(); var mode = Data["mode"]?.GetValue<string>();
@ -150,38 +176,33 @@ namespace Elwig.Helpers.Billing {
return dict; return dict;
} }
protected static Dictionary<string, JsonValue> GetSelection(JsonNode value, IEnumerable<string> vaributes) { protected static Dictionary<RawVaribute, JsonValue> GetSelection(JsonNode value, IEnumerable<RawVaribute> vaributes) {
if (value is JsonValue flatRate) { if (value is JsonValue flatRate) {
return vaributes.ToDictionary(e => e, _ => flatRate); return vaributes.ToDictionary(e => e, _ => flatRate);
} if (value is not JsonObject data) { } if (value is not JsonObject data) {
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
Dictionary<string, JsonValue> dict; Dictionary<RawVaribute, JsonValue> dict;
if (data["default"] is JsonValue def) { if (data["default"] is JsonValue def) {
dict = vaributes.ToDictionary(e => e, _ => def); dict = vaributes.ToDictionary(e => e, _ => def);
} else { } else {
dict = []; dict = [];
} }
var varieties = data.Where(p => !p.Key.StartsWith('/') && p.Key.Length == 2); var conv = data
var attributes = data.Where(p => p.Key.StartsWith('/')); .Where(p => p.Key != "default")
var others = data.Where(p => !p.Key.StartsWith('/') && p.Key.Length > 2 && p.Key != "default"); .Select(p => (new RawVaribute(p.Key), p.Value))
foreach (var (idx, v) in varieties) { .OrderBy(p => (p.Item1.SortId != null ? 10 : 0) + (p.Item1.AttrId != null ? 12 : 0) + (p.Item1.CultId != null ? 11 : 0))
.ToList();
foreach (var (idx, v) in conv) {
var curve = v?.AsValue() ?? throw new InvalidOperationException(); var curve = v?.AsValue() ?? throw new InvalidOperationException();
foreach (var i in vaributes.Where(e => e.StartsWith(idx[..^1]))) { foreach (var i in vaributes.Where(e =>
(idx.SortId == null || idx.SortId == e.SortId) &&
(idx.AttrId == null || idx.AttrId == e.AttrId) &&
(idx.CultId == null || idx.CultId == e.CultId))) {
dict[i] = curve; dict[i] = curve;
} }
} }
foreach (var (idx, v) in attributes) {
var curve = v?.AsValue() ?? throw new InvalidOperationException();
foreach (var i in vaributes.Where(e => e[2..] == idx[1..])) {
dict[i] = curve;
}
}
foreach (var (idx, v) in others) {
var curve = v?.AsValue() ?? throw new InvalidOperationException();
dict[idx.Replace("/", "")] = curve;
}
return dict; return dict;
} }
@ -257,11 +278,11 @@ namespace Elwig.Helpers.Billing {
return curve; return curve;
} }
protected static void CollapsePaymentData(JsonObject data, IEnumerable<string> vaributes, bool useDefault = true) { protected static (Dictionary<string, List<string>>, Dictionary<decimal, List<string>>) GetReverseKeys(JsonObject data, bool strict = true) {
Dictionary<string, List<string>> rev1 = []; Dictionary<string, List<string>> rev1 = [];
Dictionary<decimal, List<string>> rev2 = []; Dictionary<decimal, List<string>> rev2 = [];
foreach (var (k, v) in data) { foreach (var (k, v) in data) {
if (k == "default" || k.StartsWith('/') || !k.Contains('/') || v is not JsonValue val) { if (k == "default" || (strict && (k.StartsWith('/') || !k.Contains('/'))) || v is not JsonValue val) {
continue; continue;
} else if (val.TryGetValue<decimal>(out var dec)) { } else if (val.TryGetValue<decimal>(out var dec)) {
rev2[dec] = rev2.GetValueOrDefault(dec) ?? []; rev2[dec] = rev2.GetValueOrDefault(dec) ?? [];
@ -271,6 +292,11 @@ namespace Elwig.Helpers.Billing {
rev1[cur].Add(k); rev1[cur].Add(k);
} }
} }
return (rev1, rev2);
}
protected static void CollapsePaymentData(JsonObject data, IEnumerable<RawVaribute> vaributes, bool useDefault = true) {
var (rev1, rev2) = GetReverseKeys(data);
if (!data.ContainsKey("default")) { if (!data.ContainsKey("default")) {
foreach (var (v, ks) in rev1) { foreach (var (v, ks) in rev1) {
if ((ks.Count >= vaributes.Count() * 0.5 && useDefault) || ks.Count == vaributes.Count()) { if ((ks.Count >= vaributes.Count() * 0.5 && useDefault) || ks.Count == vaributes.Count()) {
@ -289,26 +315,67 @@ namespace Elwig.Helpers.Billing {
} }
} }
} }
var attributes = data var attributes = data
.Select(e => e.Key) .Select(e => e.Key)
.Where(k => k.Length > 3 && k.Contains('/')) .Where(k => k.Length > 3 && k.Contains('/'))
.Select(k => "/" + k.Split('/')[1]) .Select(k => k.Split('/')[1])
.Distinct() .Distinct()
.ToList(); .ToList();
foreach (var idx in attributes) { foreach (var idx in attributes) {
var len = vaributes.Count(e => e.EndsWith(idx)); var len = vaributes.Count(e => $"{e.AttrId}{(e.CultId != null && e.CultId != "" ? "-" : "")}{e.CultId}" == idx);
foreach (var (v, ks) in rev1) { foreach (var (v, ks) in rev1) {
var myKs = ks.Where(k => k.EndsWith(idx)).ToList(); var myKs = ks.Where(k => k.EndsWith($"/{idx}")).ToList();
if (myKs.Count > 1 && ((myKs.Count >= len * 0.5 && useDefault) || myKs.Count == len)) { if (myKs.Count > 1 && ((myKs.Count >= len * 0.5 && useDefault) || myKs.Count == len)) {
foreach (var k in myKs) data.Remove(k); foreach (var k in myKs) data.Remove(k);
data[idx] = v; data[(idx.StartsWith('-') && !useDefault ? "" : "/") + idx] = v;
} }
} }
foreach (var (v, ks) in rev2) { foreach (var (v, ks) in rev2) {
var myKs = ks.Where(k => k.EndsWith(idx)).ToList(); var myKs = ks.Where(k => k.EndsWith($"/{idx}")).ToList();
if (myKs.Count > 1 && ((myKs.Count >= len * 0.5 && useDefault) || myKs.Count == len)) { if (myKs.Count > 1 && ((myKs.Count >= len * 0.5 && useDefault) || myKs.Count == len)) {
foreach (var k in myKs) data.Remove(k); foreach (var k in myKs) data.Remove(k);
data[idx] = v; data[(idx.StartsWith('-') && !useDefault ? "" : "/") + idx] = v;
}
}
}
if (!useDefault)
return;
var keys = data.Select(p => p.Key).ToList();
foreach (var k in keys) {
if (k.Length == 3 && k.EndsWith('/') && !keys.Contains(k[..2])) {
data.Remove(k, out var val);
data.Add(k[..2], val);
} else if (k.Contains("/-")) {
data.Remove(k, out var val);
data.Add(k.Replace("/-", "-"), val);
}
}
(rev1, rev2) = GetReverseKeys(data, false);
var keyVaributes = data
.Select(e => e.Key.Split('-')[0])
.Where(e => e.Length > 0 && e != "default")
.Distinct()
.ToList();
foreach (var idx in keyVaributes) {
var len = data.Count(e => e.Key == idx || (e.Key.Length > idx.Length && e.Key.StartsWith(idx) && e.Key[idx.Length] == '-'));
foreach (var (v, ks) in rev1) {
var myKs = ks.Where(k => k == idx || (k.Length > idx.Length && k.StartsWith(idx) && k[idx.Length] == '-' && !data.ContainsKey(k[idx.Length..]))).ToList();
if (myKs.Count == len) {
foreach (var k in myKs) {
if (k != idx) data.Remove(k);
}
}
}
foreach (var (v, ks) in rev2) {
var myKs = ks.Where(k => k == idx || (k.Length > idx.Length && k.StartsWith(idx) && k[idx.Length] == '-' && !data.ContainsKey(k[idx.Length..]))).ToList();
if (myKs.Count == len) {
foreach (var k in myKs) {
if (k != idx) data.Remove(k);
}
} }
} }
} }
@ -317,7 +384,7 @@ namespace Elwig.Helpers.Billing {
public static JsonObject FromGraphEntries( public static JsonObject FromGraphEntries(
IEnumerable<GraphEntry> graphEntries, IEnumerable<GraphEntry> graphEntries,
BillingData? origData = null, BillingData? origData = null,
IEnumerable<string>? vaributes = null, IEnumerable<RawVaribute>? vaributes = null,
bool useDefaultPayment = true, bool useDefaultPayment = true,
bool useDefaultQuality = true bool useDefaultQuality = true
) { ) {
@ -338,16 +405,18 @@ namespace Elwig.Helpers.Billing {
continue; continue;
} }
foreach (var c in entry.Vaributes) { foreach (var c in entry.Vaributes) {
if (entry.Abgewertet) { var v = new RawVaribute(c.Variety!.SortId, c.Attribute?.AttrId ?? "", c.Cultivation?.CultId);
qualityWei[$"{c.Variety?.SortId}/{c.Attribute?.AttrId}"] = node.DeepClone(); if (v.CultId == "") v.CultId = null;
if (entry.Abgewertet) {;
qualityWei[v.ToString()] = node.DeepClone();
} else { } else {
payment[$"{c.Variety?.SortId}/{c.Attribute?.AttrId}"] = node.DeepClone(); payment[v.ToString()] = node.DeepClone();
} }
} }
} }
CollapsePaymentData(payment, vaributes ?? payment.Select(e => e.Key).ToList(), useDefaultPayment); CollapsePaymentData(payment, vaributes ?? payment.Select(e => new RawVaribute(e.Key)).ToList(), useDefaultPayment);
CollapsePaymentData(qualityWei, vaributes ?? qualityWei.Select(e => e.Key).ToList(), useDefaultQuality); CollapsePaymentData(qualityWei, vaributes ?? qualityWei.Select(e => new RawVaribute(e.Key)).ToList(), useDefaultQuality);
var data = new JsonObject { var data = new JsonObject {
["mode"] = "elwig", ["mode"] = "elwig",

View File

@ -18,7 +18,7 @@ namespace Elwig.Helpers.Billing {
Data = PaymentBillingData.FromJson(PaymentVariant.Data, Utils.GetVaributes(Context, Year, onlyDelivered: false)); Data = PaymentBillingData.FromJson(PaymentVariant.Data, Utils.GetVaributes(Context, Year, onlyDelivered: false));
} }
public async Task Calculate(bool? honorGebunden = null, bool ? allowAttrsIntoLower = null, bool? avoidUnderDeliveries = null) { public async Task Calculate(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);
@ -123,31 +123,36 @@ namespace Elwig.Helpers.Billing {
} }
protected async Task CalculatePrices(SqliteConnection cnx) { protected async Task CalculatePrices(SqliteConnection cnx) {
var parts = new List<(int Year, int DId, int DPNr, int BktNr, string SortId, string? AttrId, string Discr, int Value, double Oe, double Kmw, string QualId)>(); 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 = $"""
SELECT d.year, d.did, d.dpnr, b.bktnr, d.sortid, d.attrid, b.discr, b.value, d.oe, d.kmw, d.qualid SELECT d.year, d.did, d.dpnr, b.bktnr, d.sortid, d.attrid, d.cultid, b.discr, b.value, d.oe, d.kmw, d.qualid, COALESCE(a.area_com, TRUE)
FROM delivery_part_bucket b FROM delivery_part_bucket b
JOIN v_delivery d ON (d.year, d.did, d.dpnr) = (b.year, b.did, b.dpnr) JOIN v_delivery d ON (d.year, d.did, d.dpnr) = (b.year, b.did, b.dpnr)
LEFT JOIN v_wine_attribute a ON a.attrid = d.attrid
WHERE b.year = {Year} WHERE b.year = {Year}
"""; """;
using var reader = await cmd.ExecuteReaderAsync(); using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync()) { while (await reader.ReadAsync()) {
parts.Add(( parts.Add((
reader.GetInt32(0), reader.GetInt32(1), reader.GetInt32(2), reader.GetInt32(3), reader.GetInt32(0), reader.GetInt32(1), reader.GetInt32(2), reader.GetInt32(3),
reader.GetString(4), reader.IsDBNull(5) ? null : reader.GetString(5), reader.GetString(6), reader.GetString(4), reader.IsDBNull(5) ? null : reader.GetString(5),
reader.GetInt32(7), reader.GetDouble(8), reader.GetDouble(9), reader.GetString(10) reader.IsDBNull(6) ? null : reader.GetString(6), reader.GetString(7),
reader.GetInt32(8), reader.GetDouble(9), reader.GetDouble(10), reader.GetString(11),
reader.GetBoolean(12)
)); ));
} }
} }
var inserts = new List<(int Year, int DId, int DPNr, int BktNr, long Price, long Amount)>(); var inserts = new List<(int Year, int DId, int DPNr, int BktNr, long Price, long Amount)>();
foreach (var part in parts) { foreach (var part in parts) {
if (part.Value == 0)
continue;
var ungeb = part.Discr == "_"; var ungeb = part.Discr == "_";
var payAttrId = (part.Discr is "" or "_") ? null : part.Discr; var payAttrId = (part.Discr is "" or "_") ? null : part.Discr;
var attrId = part.AttrId == "B" ? "B" : payAttrId; // FIXME var attrId = part.AttrAreaCom ? payAttrId : part.AttrId;
var geb = !ungeb; // FIXME && payAttrId == part.AttrId; var geb = !ungeb && (payAttrId == attrId || !part.AttrAreaCom);
var price = Data.CalculatePrice(part.SortId, attrId, part.QualId, geb, part.Oe, part.Kmw); var price = Data.CalculatePrice(part.SortId, attrId, part.CultId, part.QualId, geb, part.Oe, part.Kmw);
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));
} }
@ -159,7 +164,16 @@ namespace Elwig.Helpers.Billing {
} }
protected async Task CalculateDeliveryModifiers(SqliteConnection cnx) { protected async Task CalculateDeliveryModifiers(SqliteConnection cnx) {
var netMod = Data.NetWeightModifier.ToString().Replace(',', '.');
var grossMod = Data.GrossWeightModifier.ToString().Replace(',', '.');
await AppDbContext.ExecuteBatch(cnx, $""" await AppDbContext.ExecuteBatch(cnx, $"""
INSERT INTO payment_delivery_part (year, did, dpnr, avnr, net_amount, mod_abs, mod_rel)
SELECT d.year, d.did, d.dpnr, {AvNr}, 0, 0, IIF(d.net_weight, {netMod}, {grossMod})
FROM delivery_part d
WHERE d.year = {Year}
ON CONFLICT DO UPDATE
SET mod_rel = mod_rel + excluded.mod_rel;
INSERT INTO payment_delivery_part (year, did, dpnr, avnr, net_amount, mod_abs, mod_rel) INSERT INTO payment_delivery_part (year, did, dpnr, avnr, net_amount, mod_abs, mod_rel)
SELECT d.year, d.did, d.dpnr, {AvNr}, 0, COALESCE(m.abs, 0), COALESCE(m.rel, 0) SELECT d.year, d.did, d.dpnr, {AvNr}, 0, COALESCE(m.abs, 0), COALESCE(m.rel, 0)
FROM delivery_part d FROM delivery_part d
@ -168,7 +182,7 @@ namespace Elwig.Helpers.Billing {
WHERE d.year = {Year} WHERE d.year = {Year}
ON CONFLICT DO UPDATE ON CONFLICT DO UPDATE
SET mod_abs = mod_abs + excluded.mod_abs, SET mod_abs = mod_abs + excluded.mod_abs,
mod_rel = mod_rel + excluded.mod_rel mod_rel = mod_rel + excluded.mod_rel;
"""); """);
} }
} }

View File

@ -7,18 +7,18 @@ using System.Text.Json.Nodes;
namespace Elwig.Helpers.Billing { namespace Elwig.Helpers.Billing {
public class EditBillingData : BillingData { public class EditBillingData : BillingData {
protected readonly IEnumerable<string> Vaributes; protected readonly IEnumerable<RawVaribute> Vaributes;
public EditBillingData(JsonObject data, IEnumerable<string> vaributes) : public EditBillingData(JsonObject data, IEnumerable<RawVaribute> vaributes) :
base(data) { base(data) {
Vaributes = vaributes; Vaributes = vaributes;
} }
public static EditBillingData FromJson(string json, IEnumerable<string> vaributes) { public static EditBillingData FromJson(string json, IEnumerable<RawVaribute> vaributes) {
return new(ParseJson(json), vaributes); return new(ParseJson(json), vaributes);
} }
private (Dictionary<int, Curve>, Dictionary<int, List<string>>) GetGraphEntries(JsonNode root) { private (Dictionary<int, Curve>, Dictionary<int, List<RawVaribute>>) GetGraphEntries(JsonNode root) {
Dictionary<int, List<string>> dict1 = []; Dictionary<int, List<string>> dict1 = [];
Dictionary<decimal, List<string>> dict2 = []; Dictionary<decimal, List<string>> dict2 = [];
if (root is JsonObject paymentObj) { if (root is JsonObject paymentObj) {
@ -55,7 +55,7 @@ namespace Elwig.Helpers.Billing {
curves[i + virtOffset] = new Curve(CurveMode.Oe, new() { { 73, idx } }, null); curves[i + virtOffset] = new Curve(CurveMode.Oe, new() { { 73, idx } }, null);
} }
Dictionary<int, List<string>> dict3 = curves.ToDictionary(c => c.Key, _ => new List<string>()); Dictionary<int, List<RawVaribute>> dict3 = curves.ToDictionary(c => c.Key, _ => new List<RawVaribute>());
foreach (var (selector, value) in GetSelection(root, Vaributes)) { foreach (var (selector, value) in GetSelection(root, Vaributes)) {
int? idx = null; int? idx = null;
if (value.TryGetValue<decimal>(out var val)) { if (value.TryGetValue<decimal>(out var val)) {
@ -73,13 +73,14 @@ namespace Elwig.Helpers.Billing {
private static List<GraphEntry> CreateGraphEntries( private static List<GraphEntry> CreateGraphEntries(
AppDbContext ctx, int precision, AppDbContext ctx, int precision,
Dictionary<int, Curve> curves, Dictionary<int, Curve> curves,
Dictionary<int, List<string>> entries Dictionary<int, List<RawVaribute>> entries
) { ) {
var vars = ctx.WineVarieties.ToDictionary(v => v.SortId, v => v); var vars = ctx.WineVarieties.ToDictionary(v => v.SortId, v => v);
var attrs = ctx.WineAttributes.ToDictionary(a => a.AttrId, a => a); var attrs = ctx.WineAttributes.ToDictionary(a => a.AttrId, a => a);
var cults = ctx.WineCultivations.ToDictionary(c => c.CultId, c => c);
return entries return entries
.Select(e => new GraphEntry(e.Key, precision, curves[e.Key], e.Value .Select(e => new GraphEntry(e.Key, precision, curves[e.Key], e.Value
.Select(s => new Varibute(vars[s[..2]], s.Length > 2 ? attrs[s[2..]] : null)) .Select(s => new Varibute(s, vars, attrs, cults))
.ToList())) .ToList()))
.ToList(); .ToList();
} }

View File

@ -7,48 +7,46 @@ namespace Elwig.Helpers.Billing {
public class PaymentBillingData : BillingData { public class PaymentBillingData : BillingData {
protected readonly Dictionary<int, Curve> Curves; protected readonly Dictionary<int, Curve> Curves;
protected readonly Dictionary<string, Curve> PaymentData; protected readonly Dictionary<RawVaribute, Curve> PaymentData;
protected readonly Dictionary<string, Curve> QualityData; protected readonly Dictionary<RawQualVaribute, Curve> QualityData;
protected readonly IEnumerable<string> Vaributes; protected readonly IEnumerable<RawVaribute> Vaributes;
public PaymentBillingData(JsonObject data, IEnumerable<string> vaributes) : public PaymentBillingData(JsonObject data, IEnumerable<RawVaribute> vaributes) :
base(data) { base(data) {
if (vaributes.Any(e => e.Any(c => c < 'A' || c > 'Z')))
throw new ArgumentException("Invalid vaributes");
Vaributes = vaributes; Vaributes = vaributes;
Curves = GetCurves(); Curves = GetCurves();
PaymentData = GetPaymentData(); PaymentData = GetPaymentData();
QualityData = GetQualityData(); QualityData = GetQualityData();
} }
public static PaymentBillingData FromJson(string json, IEnumerable<string> vaributes) { public static PaymentBillingData FromJson(string json, IEnumerable<RawVaribute> vaributes) {
return new(ParseJson(json), vaributes); return new(ParseJson(json), vaributes);
} }
private Dictionary<string, Curve> GetData(JsonNode data) { private Dictionary<RawVaribute, Curve> GetData(JsonNode data) {
return GetSelection(data, Vaributes).ToDictionary(e => e.Key, e => LookupCurve(e.Value)); return GetSelection(data, Vaributes).ToDictionary(e => e.Key, e => LookupCurve(e.Value));
} }
protected Dictionary<string, Curve> GetPaymentData() { protected Dictionary<RawVaribute, Curve> GetPaymentData() {
return GetData(GetPaymentEntry()); return GetData(GetPaymentEntry());
} }
protected Dictionary<string, Curve> GetQualityData() { protected Dictionary<RawQualVaribute, Curve> GetQualityData() {
Dictionary<string, Curve> dict = []; Dictionary<RawQualVaribute, Curve> dict = [];
var q = GetQualityEntry(); var q = GetQualityEntry();
if (q == null) return dict; if (q == null) return dict;
foreach (var (qualid, data) in q) { foreach (var (qualid, data) in q) {
foreach (var (idx, d) in GetData(data ?? throw new InvalidOperationException())) { foreach (var (idx, d) in GetData(data ?? throw new InvalidOperationException())) {
dict[$"{qualid}/{idx}"] = d; dict[new(qualid, idx.SortId, idx.AttrId, idx.CultId)] = d;
} }
} }
return dict; return dict;
} }
public decimal CalculatePrice(string sortid, string? attrid, string qualid, bool gebunden, double oe, double kmw) { public decimal CalculatePrice(string sortid, string? attrid, string? cultid, string qualid, bool gebunden, double oe, double kmw) {
var curve = GetQualityCurve(qualid, sortid, attrid) ?? GetCurve(sortid, attrid); var curve = GetQualityCurve(qualid, sortid, attrid, cultid) ?? GetCurve(sortid, attrid, cultid);
return GetCurveValueAt((gebunden ? curve.Gebunden : null) ?? curve.Normal, curve.Mode == CurveMode.Oe ? oe : kmw); return GetCurveValueAt((gebunden ? curve.Gebunden : null) ?? curve.Normal, curve.Mode == CurveMode.Oe ? oe : kmw);
} }
@ -62,12 +60,12 @@ namespace Elwig.Helpers.Billing {
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
protected Curve GetCurve(string sortid, string? attrid) { protected Curve GetCurve(string sortid, string? attrid, string? cultid) {
return PaymentData[$"{sortid}{attrid}"]; return PaymentData[new(sortid, attrid ?? "", cultid ?? "")];
} }
protected Curve? GetQualityCurve(string qualid, string sortid, string? attrid) { protected Curve? GetQualityCurve(string qualid, string sortid, string? attrid, string? cultid) {
return QualityData.TryGetValue($"{qualid}/{sortid}{attrid}", out var curve) ? curve : null; return QualityData.TryGetValue(new(qualid, sortid, attrid ?? "", cultid ?? ""), out var curve) ? curve : null;
} }
} }
} }

View File

@ -1,20 +1,81 @@
using Elwig.Models.Entities; using Elwig.Models.Entities;
using System; using System;
using System.Collections.Generic;
namespace Elwig.Helpers.Billing { namespace Elwig.Helpers.Billing {
public record struct RawQualVaribute {
public string QualId;
public string? SortId;
public string? AttrId;
public string? CultId;
public RawQualVaribute(string qualid, string? sortid, string? attrid, string? cultid) {
QualId = qualid;
SortId = sortid;
AttrId = attrid;
CultId = cultid;
}
}
public record struct RawVaribute : IComparable<RawVaribute> {
public string? SortId;
public string? AttrId;
public string? CultId;
public RawVaribute(string? sortid, string? attrid, string? cultid) {
SortId = sortid;
AttrId = attrid;
CultId = cultid;
}
public RawVaribute(string id) {
var p1 = id.Split('/')[0].Split('-')[0];
SortId = p1 == "" ? null : p1;
AttrId = id.Contains('/') ? id.Split('/')[1].Split('-')[0] : null;
CultId = id.Contains('-') ? id.Split('-')[1] : null;
}
public readonly override string ToString() {
return $"{SortId}" + (AttrId != null ? $"/{AttrId}" : "") + (CultId != null ? $"-{CultId}" : "");
}
public readonly int CompareTo(RawVaribute other) {
return $"{SortId}/{AttrId}-{CultId}".CompareTo($"{other.SortId}/{other.AttrId}-{other.CultId}");
}
}
public class Varibute : IComparable<Varibute> { public class Varibute : IComparable<Varibute> {
public WineVar? Variety { get; } public WineVar? Variety { get; }
public WineAttr? Attribute { get; } public WineAttr? Attribute { get; }
public WineCult? Cultivation { get; }
public int? AssignedGraphId { get; set; } public int? AssignedGraphId { get; set; }
public int? AssignedAbgewGraphId { get; set; } public int? AssignedAbgewGraphId { get; set; }
public string Listing => $"{Variety?.SortId}{Attribute?.AttrId}"; public string Listing => $"{Variety?.SortId}" +
public string FullName => $"{Variety?.Name}" + (Variety != null && Attribute != null ? " " : "") + $"{Attribute?.Name}"; (Attribute != null ? $"/{Attribute.AttrId}" : "") +
(Cultivation != null ? $"-{Cultivation.CultId}" : "");
public string FullName => $"{Variety?.Name}" +
(Variety != null && Attribute != null ? " " : "") + $"{Attribute?.Name}" +
((Variety != null || Attribute != null) && Cultivation != null ? " " : "") + $"{Cultivation?.Name}";
public Varibute(WineVar? var, WineAttr? attr) { public Varibute(RawVaribute raw) :
this(raw.SortId != null ? new WineVar(raw.SortId, raw.SortId) : null,
raw.AttrId != null ? new WineAttr() { AttrId = raw.AttrId, Name = raw.AttrId } : null,
raw.CultId != null ? new WineCult() { CultId = raw.CultId, Name = raw.CultId } : null) {
}
public Varibute(RawVaribute raw, Dictionary<string, WineVar> vars, Dictionary<string, WineAttr> attrs, Dictionary<string, WineCult> cults) :
this(raw.SortId != null && raw.SortId != "" ? vars[raw.SortId] : null,
raw.AttrId != null && raw.AttrId != "" ? attrs[raw.AttrId] : null,
raw.CultId != null && raw.CultId != "" ? cults[raw.CultId] : null) {
}
public Varibute(WineVar? var, WineAttr? attr, WineCult? cult) {
Variety = var; Variety = var;
Attribute = attr; Attribute = attr;
Cultivation = cult;
} }
public override string ToString() { public override string ToString() {

View File

@ -7,20 +7,20 @@ using System.Threading.Tasks;
namespace Elwig.Helpers { namespace Elwig.Helpers {
public class ClientParameters { public class ClientParameters {
public enum Type { Matzen, Winzerkeller }; public enum Type { Matzen, Winzerkeller, Weinland, Baden };
public bool IsMatzen => Client == Type.Matzen; public bool IsMatzen => Client == Type.Matzen;
public bool IsWinzerkeller => Client == Type.Winzerkeller; public bool IsWinzerkeller => Client == Type.Winzerkeller;
public bool IsWolkersdorf => Client == Type.Winzerkeller && App.ZwstId == "W"; public bool IsWeinland => Client == Type.Weinland;
public bool IsHaugsdorf => Client == Type.Winzerkeller && App.ZwstId == "H"; public bool IsBaden => Client == Type.Baden;
public bool IsSitzendorf => Client == Type.Winzerkeller && App.ZwstId == "S"; public bool IsWolkersdorf => IsWinzerkeller && App.ZwstId == "W";
public bool IsHaugsdorf => IsWinzerkeller && App.ZwstId == "H";
public bool IsSitzendorf => IsWinzerkeller && App.ZwstId == "S";
public bool IsGrInzersdorf => IsWeinland;
public bool HasRebler(string? zwstId) => IsMatzen || (IsWinzerkeller && zwstId == "W"); public bool HasNetWeighing(string? zwstId) => IsMatzen || (IsWinzerkeller && zwstId == "W");
public bool HasRebler(Branch? b) => HasRebler(b?.ZwstId); public bool HasNetWeighing(Branch? b) => HasNetWeighing(b?.ZwstId);
public bool HasRebler() => HasRebler(App.ZwstId); public bool HasNetWeighing() => HasNetWeighing(App.ZwstId);
public bool HasKisten(string? zwstId) => IsWinzerkeller && (zwstId == "H" || zwstId == "S");
public bool HasKisten(Branch? b) => HasKisten(b?.ZwstId);
public bool HasKisten() => HasKisten(App.ZwstId);
public string NameToken; public string NameToken;
public string NameShort; public string NameShort;
@ -36,8 +36,8 @@ namespace Elwig.Helpers {
public PostalDest PostalDest { public PostalDest PostalDest {
set { set {
Plz = value.AtPlz.Plz; Plz = value.AtPlz!.Plz;
Ort = value.AtPlz.Ort.Name; Ort = value.AtPlz!.Ort.Name;
} }
} }
public int Plz; public int Plz;
@ -61,6 +61,8 @@ namespace Elwig.Helpers {
public string? TextDeliveryNote; public string? TextDeliveryNote;
public string? TextDeliveryConfirmation; public string? TextDeliveryConfirmation;
public string? TextCreditNote; public string? TextCreditNote;
public string? TextEmailSubject;
public string? TextEmailBody;
public ClientParameters(AppDbContext ctx) : this(ctx.ClientParameters.ToDictionary(e => e.Param, e => e.Value)) { } public ClientParameters(AppDbContext ctx) : this(ctx.ClientParameters.ToDictionary(e => e.Param, e => e.Value)) { }
@ -72,8 +74,14 @@ namespace Elwig.Helpers {
NameSuffix = parameters.GetValueOrDefault("CLIENT_NAME_SUFFIX"); NameSuffix = parameters.GetValueOrDefault("CLIENT_NAME_SUFFIX");
NameType = parameters["CLIENT_NAME_TYPE"] ?? throw new KeyNotFoundException(); NameType = parameters["CLIENT_NAME_TYPE"] ?? throw new KeyNotFoundException();
switch (Name) { switch (Name) {
case "Winzergenossenschaft für Matzen und Umgebung": Client = Type.Matzen; break; case "Winzergenossenschaft für Matzen und Umgebung":
case "Winzerkeller im Weinviertel": Client = Type.Winzerkeller; break; Client = Type.Matzen; break;
case "Winzerkeller im Weinviertel":
Client = Type.Winzerkeller; break;
case "Winzergenossenschaft Weinland":
Client = Type.Weinland; break;
case "Winzergenossenschaft Baden - Bad Vöslau":
Client = Type.Baden; break;
}; };
Plz = int.Parse(parameters["CLIENT_PLZ"] ?? ""); Plz = int.Parse(parameters["CLIENT_PLZ"] ?? "");
@ -102,6 +110,10 @@ namespace Elwig.Helpers {
if (TextDeliveryConfirmation == "") TextDeliveryConfirmation = null; if (TextDeliveryConfirmation == "") TextDeliveryConfirmation = null;
TextCreditNote = parameters.GetValueOrDefault("TEXT_CREDITNOTE"); TextCreditNote = parameters.GetValueOrDefault("TEXT_CREDITNOTE");
if (TextCreditNote == "") TextCreditNote = null; if (TextCreditNote == "") TextCreditNote = null;
TextEmailSubject = parameters.GetValueOrDefault("TEXT_EMAIL_SUBJECT");
if (TextEmailSubject == "") TextEmailSubject = null;
TextEmailBody = parameters.GetValueOrDefault("TEXT_EMAIL_BODY");
if (TextEmailBody == "") TextEmailBody = null;
} catch { } catch {
throw new KeyNotFoundException(); throw new KeyNotFoundException();
} }
@ -115,7 +127,7 @@ namespace Elwig.Helpers {
case 2: deliveryNoteStats = "SHORT"; break; case 2: deliveryNoteStats = "SHORT"; break;
case 3: deliveryNoteStats = "FULL"; break; case 3: deliveryNoteStats = "FULL"; break;
} }
return new (string, string?)[] { return [
("CLIENT_NAME_TOKEN", NameToken), ("CLIENT_NAME_TOKEN", NameToken),
("CLIENT_NAME_SHORT", NameShort), ("CLIENT_NAME_SHORT", NameShort),
("CLIENT_NAME", Name), ("CLIENT_NAME", Name),
@ -137,7 +149,9 @@ namespace Elwig.Helpers {
("TEXT_DELIVERYNOTE", TextDeliveryNote), ("TEXT_DELIVERYNOTE", TextDeliveryNote),
("TEXT_DELIVERYCONFIRMATION", TextDeliveryConfirmation), ("TEXT_DELIVERYCONFIRMATION", TextDeliveryConfirmation),
("TEXT_CREDITNOTE", TextCreditNote), ("TEXT_CREDITNOTE", TextCreditNote),
}; ("TEXT_EMAIL_SUBJECT", TextEmailSubject),
("TEXT_EMAIL_BODY", TextEmailBody)
];
} }
public async Task UpdateValues() { public async Task UpdateValues() {
@ -157,6 +171,7 @@ namespace Elwig.Helpers {
} }
await cmd.ExecuteNonQueryAsync(); await cmd.ExecuteNonQueryAsync();
await App.HintContextChange();
} }
} }
} }

View File

@ -4,16 +4,56 @@ using System.Linq;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
namespace Elwig.Helpers { namespace Elwig.Helpers {
public record struct ScaleConfig {
public string Id;
public string? Type;
public string? Model;
public string? Connection;
public string? Empty;
public string? Filling;
public string? Limit;
public string? Log;
public string? _Log;
public ScaleConfig(string id, string? type, string? model, string? cnx, string? empty, string? filling, string? limit, string? log) {
Id = id;
Type = type;
Model = model;
Connection = cnx;
Empty = empty;
Filling = filling;
Limit = limit;
_Log = log;
Log = log != null ? Path.Combine(App.DataPath, log) : null;
}
}
public class Config { public class Config {
private static readonly string[] TrueValues = ["1", "true", "yes", "on"];
private readonly string FileName; private readonly string FileName;
public bool Debug; public bool Debug;
public string DatabaseFile = App.DataPath + "database.sqlite3"; public string DatabaseFile = App.DataPath + "database.sqlite3";
public string? DatabaseLog = null; public string? DatabaseLog = null;
public string? Branch = null; public string? Branch = null;
public IList<string?[]> Scales; public string? UpdateUrl = null;
private readonly List<string?[]> ScaleList = []; public bool UpdateAuto = false;
private static readonly string[] trueValues = ["1", "true", "yes", "on"];
public string? SmtpHost = null;
public int? SmtpPort = null;
public string? SmtpMode = null;
public string? SmtpUsername = null;
public string? SmtpPassword = null;
public string? SmtpFrom = null;
public (string Host, int Port, string Mode, string Username, string Password, string From)? Smtp =>
SmtpHost == null || SmtpPort == null || SmtpMode == null || SmtpUsername == null || SmtpPassword == null || SmtpFrom == null ?
null : (SmtpHost, (int)SmtpPort, SmtpMode, SmtpUsername, SmtpPassword, SmtpFrom);
public IList<ScaleConfig> Scales;
private readonly List<ScaleConfig> ScaleList = [];
public Config(string filename) { public Config(string filename) {
FileName = filename; FileName = filename;
@ -28,34 +68,25 @@ namespace Elwig.Helpers {
var log = config["database:log"]; var log = config["database:log"];
DatabaseLog = log != null ? Path.Combine(App.DataPath, log) : null; DatabaseLog = log != null ? Path.Combine(App.DataPath, log) : null;
Branch = config["general:branch"]; Branch = config["general:branch"];
Debug = trueValues.Contains(config["general:debug"]?.ToLower()); Debug = TrueValues.Contains(config["general:debug"]?.ToLower());
UpdateUrl = config["update:url"];
UpdateAuto = TrueValues.Contains(config["update:auto"]?.ToLower());
SmtpHost = config["smtp:host"];
SmtpPort = config["smtp:port"]?.All(char.IsAsciiDigit) == true && config["smtp:port"]?.Length > 0 ? int.Parse(config["smtp:port"]!) : null;
SmtpMode = config["smtp:mode"];
SmtpUsername = config["smtp:username"];
SmtpPassword = config["smtp:password"];
SmtpFrom = config["smtp:from"];
var scales = config.AsEnumerable().Where(i => i.Key.StartsWith("scale.")).GroupBy(i => i.Key.Split(':')[0][6..]).Select(i => i.Key); var scales = config.AsEnumerable().Where(i => i.Key.StartsWith("scale.")).GroupBy(i => i.Key.Split(':')[0][6..]).Select(i => i.Key);
ScaleList.Clear(); ScaleList.Clear();
Scales = ScaleList; Scales = ScaleList;
foreach (var s in scales) { foreach (var s in scales) {
string? scaleLog = config[$"scale.{s}:log"]; ScaleList.Add(new(
if (scaleLog != null) scaleLog = Path.Combine(App.DataPath, scaleLog);
ScaleList.Add([
s, config[$"scale.{s}:type"], config[$"scale.{s}:model"], config[$"scale.{s}:connection"], s, config[$"scale.{s}:type"], config[$"scale.{s}:model"], config[$"scale.{s}:connection"],
config[$"scale.{s}:empty"], config[$"scale.{s}:filling"], config[$"scale.{s}:limit"], scaleLog config[$"scale.{s}:empty"], config[$"scale.{s}:filling"], config[$"scale.{s}:limit"], config[$"scale.{s}:log"]
]); ));
}
}
public void Write() {
using var file = new StreamWriter(FileName, false, Utils.UTF8);
file.Write($"\r\n[general]\r\n");
if (Branch != null) file.Write($"branch = {Branch}\r\n");
if (Debug) file.Write("debug = true\r\n");
file.Write($"\r\n[database]\r\nfile = {DatabaseFile}\r\n");
if (DatabaseLog != null) file.Write($"log = {DatabaseLog}\r\n");
foreach (var s in ScaleList) {
file.Write($"\r\n[scale.{s[0]}]\r\ntype = {s[1]}\r\nmodel = {s[2]}\r\nconnection = {s[3]}\r\n");
if (s[4] != null) file.Write($"empty = {s[4]}\r\n");
if (s[5] != null) file.Write($"filling = {s[5]}\r\n");
if (s[6] != null) file.Write($"limit = {s[6]}\r\n");
if (s[7] != null) file.Write($"log = {s[7]}\r\n");
} }
} }
} }

View File

@ -0,0 +1,24 @@
using System;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace Elwig.Helpers {
public static class HttpClientExtensions {
public static async Task DownloadAsync(this HttpClient client, string requestUri, Stream destination, IProgress<double>? progress = null, CancellationToken cancellationToken = default) {
using var response = await client.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
var contentLength = response.Content.Headers.ContentLength;
using var download = await response.Content.ReadAsStreamAsync(cancellationToken);
if (progress == null || !contentLength.HasValue) {
await download.CopyToAsync(destination, cancellationToken);
return;
}
var relativeProgress = new Progress<long>(totalBytes => progress.Report((double)totalBytes / contentLength.Value));
await download.CopyToAsync(destination, 81920, relativeProgress, cancellationToken);
progress.Report(100.0);
}
}
}

View File

@ -7,6 +7,8 @@ using System.Collections.Generic;
using System.Windows; using System.Windows;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Linq; using System.Linq;
using System.Net.Sockets;
using System.Text;
namespace Elwig.Helpers.Printing { namespace Elwig.Helpers.Printing {
public static class Pdf { public static class Pdf {
@ -14,54 +16,64 @@ namespace Elwig.Helpers.Printing {
private static readonly string PdfToPrinter = new string[] { App.ExePath } private static readonly string PdfToPrinter = new string[] { App.ExePath }
.Union(Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? []) .Union(Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? [])
.Select(x => Path.Combine(x, "PDFtoPrinter.exe")) .Select(x => Path.Combine(x, "PDFtoPrinter.exe"))
.Where(x => File.Exists(x)) .Where(File.Exists)
.FirstOrDefault() ?? throw new FileNotFoundException("PDFtoPrinter executable not found"); .FirstOrDefault() ?? throw new FileNotFoundException("PDFtoPrinter executable not found");
private static readonly string WinziPrint = new string[] { App.ExePath } private static readonly string WinziPrint = new string[] { App.ExePath }
.Union(Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? []) .Union(Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? [])
.Select(x => Path.Combine(x, "WinziPrint.exe")) .Select(x => Path.Combine(x, "WinziPrint.exe"))
.Where(x => File.Exists(x)) .Where(File.Exists)
.FirstOrDefault() ?? throw new FileNotFoundException("WiniPrint executable not found"); .FirstOrDefault() ?? throw new FileNotFoundException("WiniPrint executable not found");
private static Process? WinziPrintProc; private static Process? WinziPrintProc;
public static bool IsReady => WinziPrintProc != null; public static bool IsReady => WinziPrintProc != null;
public static async Task Init(Action? evtHandler = null) { public static async Task Init(Action? evtHandler = null) {
// NOTE: If the WinziPrint daemon is already running this will succeed, but the process will fail.
// Should be no problem, as long as the daemon is not closed
var p = new Process() { StartInfo = new() { var p = new Process() { StartInfo = new() {
FileName = WinziPrint, FileName = WinziPrint,
CreateNoWindow = true, CreateNoWindow = true,
UseShellExecute = false, UseShellExecute = false,
RedirectStandardInput = true, RedirectStandardOutput = true,
RedirectStandardOutput = true
} }; } };
p.StartInfo.ArgumentList.Add("-p"); p.StartInfo.ArgumentList.Add("-D");
p.StartInfo.ArgumentList.Add("-e");
p.StartInfo.ArgumentList.Add("utf-8");
p.StartInfo.ArgumentList.Add("-d"); p.StartInfo.ArgumentList.Add("-d");
p.StartInfo.ArgumentList.Add(App.TempPath); p.StartInfo.ArgumentList.Add(App.TempPath);
p.StartInfo.ArgumentList.Add("-");
p.Start(); p.Start();
await p.StandardOutput.ReadLineAsync();
WinziPrintProc = p; WinziPrintProc = p;
evtHandler?.Invoke(); evtHandler?.Invoke();
} }
public static async Task<IEnumerable<int>> Convert(string htmlPath, string pdfPath, bool doubleSided = false, IProgress<double>? progress = null) { public static Task Cleanup() {
return await Convert(new string[] { htmlPath }, pdfPath, doubleSided, progress); WinziPrintProc?.Kill(true);
WinziPrintProc?.Close();
return Task.CompletedTask;
} }
public static async Task<IEnumerable<int>> Convert(IEnumerable<string> htmlPath, string pdfPath, bool doubleSided = false, IProgress<double>? progress = null) { public static async Task<(int Pages, IEnumerable<int> PerDoc)> Convert(string htmlPath, string pdfPath, bool doublePaged = false, IProgress<double>? progress = null) {
return await Convert([htmlPath], pdfPath, doublePaged, progress);
}
public static async Task<(int Pages, IEnumerable<int> PerDoc)> Convert(IEnumerable<string> htmlPath, string pdfPath, bool doublePaged = false, IProgress<double>? progress = null) {
if (WinziPrintProc == null) throw new InvalidOperationException("The WinziPrint process has not been initialized yet"); if (WinziPrintProc == null) throw new InvalidOperationException("The WinziPrint process has not been initialized yet");
progress?.Report(0.0); progress?.Report(0.0);
await WinziPrintProc.StandardInput.WriteLineAsync((doubleSided ? "-2;" : "") + $"{string.Join(';', htmlPath)};{pdfPath}"); using var client = new TcpClient("127.0.0.1", 30983);
using var stream = client.GetStream();
await stream.WriteAsync(Encoding.UTF8.GetBytes(
"-e utf-8;-p;" + (doublePaged ? "-2;" : "") +
$"{string.Join(';', htmlPath)};{pdfPath}" +
"\r\n"));
using var reader = new StreamReader(stream);
while (true) { while (true) {
var line = await WinziPrintProc.StandardOutput.ReadLineAsync() ?? throw new IOException("Invalid response from WinziPrint"); var line = await reader.ReadLineAsync() ?? throw new IOException("Invalid response from WinziPrint");
if (line.StartsWith("error:")) { if (line.StartsWith("error:")) {
MessageBox.Show(line[6..].Trim(), "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); throw new IOException($"WinziPrint: {line[6..].Trim()}");
return Array.Empty<int>();
} else if (line.StartsWith("progress:")) { } else if (line.StartsWith("progress:")) {
var parts = line[9..].Trim().Split('/').Select(int.Parse).ToArray(); var parts = line[9..].Trim().Split('/').Select(int.Parse).ToArray();
progress?.Report(100.0 * parts[0] / parts[1]); progress?.Report(100.0 * parts[0] / parts[1]);
} else if (line.StartsWith("success:")) { } else if (line.StartsWith("success:")) {
var m = Regex.Match(line, @"\(([0-9, ]+)\)"); var m = Regex.Match(line, @"([0-9]+) pages \(([0-9, ]+)\)");
return m.Groups[1].Value.Split(", ").Select(int.Parse); return (int.Parse(m.Groups[1].Value), m.Groups[2].Value.Split(", ").Select(int.Parse).ToList());
} }
} }
} }

View File

@ -0,0 +1,25 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace Elwig.Helpers {
public static class StreamExtensions {
public static async Task CopyToAsync(this Stream source, Stream destination, int bufferSize, IProgress<long>? progress = null, CancellationToken cancellationToken = default) {
ArgumentNullException.ThrowIfNull(source);
if (!source.CanRead) throw new ArgumentException("Has to be readable", nameof(source));
ArgumentNullException.ThrowIfNull(destination);
if (!destination.CanWrite) throw new ArgumentException("Has to be writable", nameof(destination));
ArgumentOutOfRangeException.ThrowIfNegative(bufferSize);
var buffer = new byte[bufferSize];
long totalBytesRead = 0;
int bytesRead;
while ((bytesRead = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) != 0) {
await destination.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false);
totalBytesRead += bytesRead;
progress?.Report(totalBytesRead);
}
}
}
}

View File

@ -11,9 +11,14 @@ using Elwig.Dialogs;
using System.Text; using System.Text;
using System.Numerics; using System.Numerics;
using Elwig.Models.Entities; using Elwig.Models.Entities;
using System.IO;
using ScottPlot.TickGenerators.TimeUnits;
using Elwig.Helpers.Billing; using Elwig.Helpers.Billing;
using System.Runtime.InteropServices;
using System.Net.Http;
using System.Text.Json.Nodes;
using System.IO;
using MailKit.Net.Smtp;
using MailKit.Security;
using OpenTK.Compute.OpenCL;
namespace Elwig.Helpers { namespace Elwig.Helpers {
public static partial class Utils { public static partial class Utils {
@ -25,30 +30,33 @@ namespace Elwig.Helpers {
public static int CurrentLastSeason => DateTime.Now.Year - (DateTime.Now.Month <= 7 ? 1 : 0); public static int CurrentLastSeason => DateTime.Now.Year - (DateTime.Now.Month <= 7 ? 1 : 0);
public static DateTime Today => (DateTime.Now.Hour >= 3) ? DateTime.Today : DateTime.Today.AddDays(-1); public static DateTime Today => (DateTime.Now.Hour >= 3) ? DateTime.Today : DateTime.Today.AddDays(-1);
public static readonly Regex SerialRegex = GeneratedSerialRegex();
public static readonly Regex TcpRegex = GeneratedTcpRegex();
public static readonly Regex DateFromToRegex = GeneratedFromToDateRegex();
public static readonly Regex FromToRegex = GeneratedFromToRegex();
public static readonly Regex FromToTimeRegex = GeneratedFromToTimeRegex();
public static readonly Regex AddressRegex = GeneratedAddressRegex();
[GeneratedRegex("^serial://([A-Za-z0-9]+):([0-9]+)(,([5-9]),([NOEMSnoems]),(0|1|1\\.5|2|))?$", RegexOptions.Compiled)] [GeneratedRegex("^serial://([A-Za-z0-9]+):([0-9]+)(,([5-9]),([NOEMSnoems]),(0|1|1\\.5|2|))?$", RegexOptions.Compiled)]
private static partial Regex GeneratedSerialRegex(); private static partial Regex GeneratedSerialRegex();
public static readonly Regex SerialRegex = GeneratedSerialRegex();
[GeneratedRegex("^tcp://([A-Za-z0-9._-]+):([0-9]+)$", RegexOptions.Compiled)] [GeneratedRegex("^tcp://([A-Za-z0-9._-]+):([0-9]+)$", RegexOptions.Compiled)]
private static partial Regex GeneratedTcpRegex(); private static partial Regex GeneratedTcpRegex();
public static readonly Regex TcpRegex = GeneratedTcpRegex();
[GeneratedRegex(@"^(-?(0?[1-9]|[12][0-9]|3[01])\.(0?[1-9]|1[0-2])\.([0-9]{4})?-?){1,2}$", RegexOptions.Compiled)] [GeneratedRegex(@"^(-?(0?[1-9]|[12][0-9]|3[01])\.(0?[1-9]|1[0-2])\.([0-9]{4})?-?){1,2}$", RegexOptions.Compiled)]
private static partial Regex GeneratedFromToDateRegex(); private static partial Regex GeneratedFromToDateRegex();
public static readonly Regex DateFromToRegex = GeneratedFromToDateRegex();
[GeneratedRegex(@"^([0-9]+([\.,][0-9]+)?)?-([0-9]+([\.,][0-9]+)?)?$", RegexOptions.Compiled)] [GeneratedRegex(@"^([0-9]+([\.,][0-9]+)?)?-([0-9]+([\.,][0-9]+)?)?$", RegexOptions.Compiled)]
private static partial Regex GeneratedFromToRegex(); private static partial Regex GeneratedFromToRegex();
public static readonly Regex FromToRegex = GeneratedFromToRegex();
[GeneratedRegex(@"^([0-9]{1,2}:[0-9]{2})?-([0-9]{1,2}:[0-9]{2})?$", RegexOptions.Compiled)] [GeneratedRegex(@"^([0-9]{1,2}:[0-9]{2})?-([0-9]{1,2}:[0-9]{2})?$", RegexOptions.Compiled)]
private static partial Regex GeneratedFromToTimeRegex(); private static partial Regex GeneratedFromToTimeRegex();
public static readonly Regex FromToTimeRegex = GeneratedFromToTimeRegex();
[GeneratedRegex(@"^(.*?) +([0-9].*)$", RegexOptions.Compiled)] [GeneratedRegex(@"^(.*?) +([0-9].*)$", RegexOptions.Compiled)]
private static partial Regex GeneratedAddressRegex(); private static partial Regex GeneratedAddressRegex();
public static readonly Regex AddressRegex = GeneratedAddressRegex();
[GeneratedRegex(@"[^A-Za-z0-9ÄÜÖäöüß-]+")]
private static partial Regex GeneratedInvalidFileNamePartsRegex();
public static readonly Regex InvalidFileNamePartsRegex = GeneratedInvalidFileNamePartsRegex();
public static readonly string GroupSeparator = "\u202F"; public static readonly string GroupSeparator = "\u202F";
public static readonly string UnitSeparator = "\u00A0"; public static readonly string UnitSeparator = "\u00A0";
@ -63,7 +71,9 @@ namespace Elwig.Helpers {
return PhoneNrTypes.Where(t => t.Key == type).Select(t => t.Value).FirstOrDefault(type); return PhoneNrTypes.Where(t => t.Key == type).Select(t => t.Value).FirstOrDefault(type);
} }
private static readonly ushort[] Crc16ModbusTable = { private static readonly string[] TempWildcards = ["*.html", "*.pdf", "*.exe"];
private static readonly ushort[] Crc16ModbusTable = [
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
@ -96,7 +106,7 @@ namespace Elwig.Helpers {
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040, 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040,
}; ];
public static SerialPort OpenSerialConnection(string connection) { public static SerialPort OpenSerialConnection(string connection) {
var m = SerialRegex.Match(connection); var m = SerialRegex.Match(connection);
@ -329,7 +339,7 @@ namespace Elwig.Helpers {
public static (string, string?) SplitName(string fullName, string? familyName) { public static (string, string?) SplitName(string fullName, string? familyName) {
if (familyName == null || familyName == "") return (fullName, null); if (familyName == null || familyName == "") return (fullName, null);
var p0 = fullName.ToLower().IndexOf(familyName.ToLower()); var p0 = fullName.IndexOf(familyName, StringComparison.CurrentCultureIgnoreCase);
if (p0 == -1) return (fullName, null); if (p0 == -1) return (fullName, null);
var p1 = fullName.IndexOf(" und "); var p1 = fullName.IndexOf(" und ");
var p2 = fullName.ToLower().LastIndexOf(" und "); var p2 = fullName.ToLower().LastIndexOf(" und ");
@ -348,9 +358,9 @@ namespace Elwig.Helpers {
} }
public static IEnumerable<IEnumerable<T>> Permutate<T>(IEnumerable<T> input, IEnumerable<T>? forced = null) { public static IEnumerable<IEnumerable<T>> Permutate<T>(IEnumerable<T> input, IEnumerable<T>? forced = null) {
HashSet<IEnumerable<T>> output = new(); HashSet<IEnumerable<T>> output = [];
for (int i = 0; i < Math.Pow(2, input.Count()); i++) { for (int i = 0; i < Math.Pow(2, input.Count()); i++) {
List<T> t = new(); List<T> t = [];
for (int j = 0; j < 30; j++) { for (int j = 0; j < 30; j++) {
var e = input.ElementAtOrDefault(j); var e = input.ElementAtOrDefault(j);
if (e != null && ((forced?.Contains(e) ?? false) || (i & (1 << j)) != 0)) { if (e != null && ((forced?.Contains(e) ?? false) || (i & (1 << j)) != 0)) {
@ -362,11 +372,11 @@ namespace Elwig.Helpers {
return output.OrderByDescending(l => l.Count()); return output.OrderByDescending(l => l.Count());
} }
public static List<string> GetVaributes(AppDbContext ctx, int year, bool withSlash = false, bool onlyDelivered = true) { public static List<RawVaribute> GetVaributes(AppDbContext ctx, int year, bool onlyDelivered = true) {
var varieties = ctx.WineVarieties.Select(v => v.SortId).ToList(); var varieties = ctx.WineVarieties.Select(v => new RawVaribute(v.SortId, "", null)).ToList();
var delivered = ctx.DeliveryParts var delivered = ctx.DeliveryParts
.Where(d => d.Year == year) .Where(d => d.Year == year)
.Select(d => $"{d.SortId}{(withSlash ? "/" : "")}{d.AttrId}") .Select(d => new RawVaribute(d.SortId, d.AttrId ?? "", d.CultId ?? ""))
.Distinct() .Distinct()
.ToList(); .ToList();
return [.. (onlyDelivered ? delivered : delivered.Union(varieties)).Order()]; return [.. (onlyDelivered ? delivered : delivered.Union(varieties)).Order()];
@ -375,9 +385,52 @@ namespace Elwig.Helpers {
public static List<Varibute> GetVaributeList(AppDbContext ctx, int year, bool onlyDelivered = true) { public static List<Varibute> GetVaributeList(AppDbContext ctx, int year, bool onlyDelivered = true) {
var varieties = ctx.WineVarieties.ToDictionary(v => v.SortId, v => v); var varieties = ctx.WineVarieties.ToDictionary(v => v.SortId, v => v);
var attributes = ctx.WineAttributes.ToDictionary(a => a.AttrId, a => a); var attributes = ctx.WineAttributes.ToDictionary(a => a.AttrId, a => a);
return GetVaributes(ctx, year, false, onlyDelivered) var cultivations = ctx.WineCultivations.ToDictionary(c => c.CultId, c => c);
.Select(s => new Varibute(varieties[s[..2]], s.Length > 2 ? attributes[s[2..]] : null)) return GetVaributes(ctx, year, onlyDelivered)
.Select(s => new Varibute(s, varieties, attributes, cultivations))
.ToList(); .ToList();
} }
[LibraryImport("wininet.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool InternetGetConnectedState(out int description, int reservedValue);
public static bool HasInternetConnectivity() {
return InternetGetConnectedState(out var _, 0);
}
public static async Task<(string Version, string Url, long Size)?> GetLatestInstallerUrl(string url) {
try {
using var client = new HttpClient() {
Timeout = TimeSpan.FromSeconds(5),
};
var res = JsonNode.Parse(await client.GetStringAsync(url));
var data = res!["data"]![0]!;
return ((string)data["version"]!, (string)data["url"]!, (int)data["size"]!);
} catch {
return null;
}
}
public static void CleanupTempFiles() {
var dir = new DirectoryInfo(App.TempPath);
foreach (var file in TempWildcards.SelectMany(dir.EnumerateFiles)) {
file.Delete();
}
}
public static string NormalizeFileName(string filename) {
return InvalidFileNamePartsRegex.Replace(filename.Replace('/', '-'), "_");
}
public static async Task<SmtpClient?> GetSmtpClient() {
if (App.Config.Smtp == null)
return null;
var (host, port, mode, username, password, _) = App.Config.Smtp.Value;
var client = new SmtpClient();
await client.ConnectAsync(host, port, mode == "starttls" ? SecureSocketOptions.StartTls : SecureSocketOptions.None);
await client.AuthenticateAsync(username, password);
return client;
}
} }
} }

View File

@ -88,7 +88,9 @@ namespace Elwig.Helpers {
input.Text = text; input.Text = text;
input.CaretIndex = pos; input.CaretIndex = pos;
if (text.Length == 0) { if (text == "-") {
return new(false, "Ungültige Kommazahl");
} else if (text.Length == 0) {
return required ? new(false, "Wert ist nicht optional") : new(true, null); return required ? new(false, "Wert ist nicht optional") : new(true, null);
} else if (v2 == 0) { } else if (v2 == 0) {
return new(false, "Ungültige Kommazahl"); return new(false, "Ungültige Kommazahl");
@ -454,9 +456,9 @@ namespace Elwig.Helpers {
if (input.Text.Length < 2 || !ctx.SortIdExists(input.Text[0..2]).GetAwaiter().GetResult()) { if (input.Text.Length < 2 || !ctx.SortIdExists(input.Text[0..2]).GetAwaiter().GetResult()) {
return new(false, "Ungültige Sorte"); return new(false, "Ungültige Sorte");
} else if (input.Text.Length >= 3) { } else if (input.Text.Length >= 3) {
var attr = input.Text[2..]; var disc = input.Text[2..];
if (!ctx.AttrIdExists(attr).GetAwaiter().GetResult()) { if (!ctx.AttrIdExists(disc).GetAwaiter().GetResult() && !ctx.CultIdExists(disc).GetAwaiter().GetResult()) {
return new(false, "Ungültiges Attribut"); return new(false, "Ungültiges Attribut/Bewirt.");
} }
} }
@ -589,11 +591,6 @@ namespace Elwig.Helpers {
return new(true, null); return new(true, null);
} }
public static ValidationResult CheckGstNr(TextBox input, bool required) {
// TODO
return new(true, "Not implemented yet");
}
public static ValidationResult CheckGradatoinOe(TextBox input, bool required) { public static ValidationResult CheckGradatoinOe(TextBox input, bool required) {
var res = CheckInteger(input, required, 3); var res = CheckInteger(input, required, 3);
if (!res.IsValid) { if (!res.IsValid) {

View File

@ -0,0 +1,88 @@
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace Elwig.Helpers.Weighing {
public class AveryEventScale : Scale, IEventScale, IDisposable {
public string Manufacturer => "Avery";
public int InternalScaleNr => 1;
public string Model { get; private set; }
public string ScaleId { get; private set; }
public bool IsReady { get; private set; }
public bool HasFillingClearance { get; private set; }
public event IEventScale.EventHandler<WeighingEventArgs> WeighingEvent;
private bool IsRunning = true;
private readonly Thread BackgroundThread;
public AveryEventScale(string id, string model, string cnx, string? empty = null, string? filling = null, int? limit = null, string? log = null) :
base(cnx, empty, filling, limit, log) {
ScaleId = id;
Model = model;
IsReady = true;
HasFillingClearance = false;
BackgroundThread = new Thread(new ParameterizedThreadStart(BackgroundLoop));
BackgroundThread.Start();
}
protected virtual void RaiseWeighingEvent(WeighingEventArgs evt) {
WeighingEvent?.Invoke(this, evt);
}
public new void Dispose() {
IsRunning = false;
BackgroundThread.Interrupt();
BackgroundThread.Join();
base.Dispose();
GC.SuppressFinalize(this);
}
protected async void BackgroundLoop(object? parameters) {
while (IsRunning) {
try {
var data = await Receive();
RaiseWeighingEvent(new WeighingEventArgs(data));
} catch (Exception ex) {
MessageBox.Show($"Beim Wiegen ist ein Fehler Aufgetreten:\n\n{ex.Message}", "Waagenfehler",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
protected async Task<WeighingResult> Receive() {
string? line = null;
using (var reader = new StreamReader(Stream, Encoding.ASCII, false, -1, true)) {
line = await reader.ReadLineAsync();
if (LogPath != null) await File.AppendAllTextAsync(LogPath, $"{line}\r\n");
}
if (line == null || line.Length != 33 || line[0] != ' ' || line[9] != ' ' || line[15] != ' ' || line[20] != ' ' || line[32] != ' ') {
throw new IOException($"Invalid event from scale: '{line}'");
}
var date = line[ 1.. 9];
var time = line[10..15];
var identNr = line[16..20].Trim();
var netto = line[21..30].Trim();
var unit = line[30..32];
if (unit != "kg") {
throw new IOException($"Unsupported unit in weighing event: '{unit}'");
}
identNr = identNr.Length > 0 && identNr != "0" ? identNr : null;
var parsedDate = DateOnly.Parse(date);
return new() {
Weight = int.Parse(netto),
WeighingId = identNr,
FullWeighingId = identNr != null ? $"{parsedDate:yyyy-MM-dd}/{identNr}" : null,
Date = parsedDate,
Time = TimeOnly.Parse(time),
};
}
}
}

View File

@ -1,64 +0,0 @@
using System;
using System.IO;
using System.IO.Ports;
using System.Text;
using System.Threading.Tasks;
namespace Elwig.Helpers.Weighing {
public class GassnerScale : IScale {
protected SerialPort Serial = null;
protected StreamReader Reader;
protected StreamWriter Writer;
public string Manufacturer => "Gassner";
public int InternalScaleNr => 1;
public string Model { get; private set; }
public string ScaleId { get; private set; }
public bool IsReady { get; private set; }
public bool HasFillingClearance { get; private set; }
public int? WeightLimit { get; private set; }
public string? LogPath { get; private set; }
public GassnerScale(string id, string model, string connection) {
ScaleId = id;
Model = model;
IsReady = true;
HasFillingClearance = false;
if (!connection.StartsWith("serial:"))
throw new ArgumentException("Unsupported scheme");
Serial = Utils.OpenSerialConnection(connection);
Writer = new(Serial.BaseStream, Encoding.ASCII, -1, true);
Reader = new(Serial.BaseStream, Encoding.ASCII, false, -1, true);
}
public void Dispose() {
Writer.Close();
Reader.Close();
Serial.Close();
GC.SuppressFinalize(this);
}
public async Task<WeighingResult> Weigh(bool incIdentNr) {
await Writer.WriteAsync(incIdentNr ? "\x05" : "?");
// TODO receive response
return new();
}
public async Task<WeighingResult> GetCurrentWeight() {
return await Weigh(false);
}
public async Task<WeighingResult> Weigh() {
return await Weigh(true);
}
public async Task Empty() { }
public async Task GrantFillingClearance() { }
public async Task RevokeFillingClearance() { }
}
}

View File

@ -4,5 +4,8 @@
/// </summary> /// </summary>
public interface IEventScale : IScale { public interface IEventScale : IScale {
public event EventHandler<WeighingEventArgs> WeighingEvent;
delegate void EventHandler<WeighingEventArgs>(object sender, WeighingEventArgs args);
} }
} }

View File

@ -0,0 +1,95 @@
using System.IO.Ports;
using System.IO;
using System.Net.Sockets;
using System;
namespace Elwig.Helpers.Weighing {
public abstract class Scale : IDisposable {
protected enum Output { RTS, DTR, OUT1, OUT2 };
protected SerialPort? Serial = null;
protected SerialPort? ControlSerialEmpty = null, ControlSerialFilling = null;
protected TcpClient? Tcp = null;
protected Stream Stream;
protected readonly Output? EmptyMode = null;
protected readonly Output? FillingClearanceMode = null;
protected readonly int EmptyDelay;
public int? WeightLimit { get; private set; }
public string? LogPath { get; private set; }
public static IScale FromConfig(ScaleConfig config) {
int? limit = config.Limit != null ? int.Parse(config.Limit) : null;
if (config.Type == "SysTec-IT") {
return new SysTecITScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
} else if (config.Type == "Avery-Async") {
return new AveryEventScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
} else {
throw new ArgumentException($"Invalid scale type: \"{config.Type}\"");
}
}
protected Scale(string cnx, string? empty, string? filling, int? limit, string? log) {
if (cnx.StartsWith("serial:")) {
Serial = Utils.OpenSerialConnection(cnx);
Stream = Serial.BaseStream;
} else if (cnx.StartsWith("tcp:")) {
Tcp = Utils.OpenTcpConnection(cnx);
Stream = Tcp.GetStream();
} else {
throw new ArgumentException($"Unsupported scheme: \"{cnx.Split(':')[0]}\"");
}
LogPath = log;
if (empty != null) {
var parts = empty.Split(':');
if (parts.Length == 3) {
if (parts[0] != Serial?.PortName)
ControlSerialEmpty = Utils.OpenSerialConnection($"serial://{parts[0]}:9600");
} else if (parts.Length != 2) {
throw new ArgumentException("Invalid value for 'empty'");
}
EmptyMode = ConvertOutput(parts[^2]);
EmptyDelay = int.Parse(parts[^1]);
}
WeightLimit = limit;
if (filling != null) {
var parts = filling.Split(':');
if (parts.Length == 2) {
if (parts[0] != Serial?.PortName)
ControlSerialFilling = parts[0] != ControlSerialEmpty?.PortName ? Utils.OpenSerialConnection($"serial://{parts[0]}:9600") : ControlSerialEmpty;
} else if (parts.Length != 1) {
throw new ArgumentException("Invalid value for 'filling'");
}
FillingClearanceMode = ConvertOutput(parts[^1]);
}
if (FillingClearanceMode != null && WeightLimit == null)
throw new ArgumentException("Weight limit has to be set, if filling clearance supervision is enabled");
}
public void Dispose() {
Stream.Close();
Serial?.Close();
ControlSerialEmpty?.Close();
ControlSerialFilling?.Close();
Tcp?.Close();
GC.SuppressFinalize(this);
}
protected static Output? ConvertOutput(string? value) {
return value switch {
null => null,
"RTS" => Output.RTS,
"DTR" => Output.DTR,
"OUT1" => Output.OUT1,
"OUT2" => Output.OUT2,
_ => throw new ArgumentException($"Invalid value for argument: '{value}'"),
};
}
}
}

View File

@ -1,19 +0,0 @@
using System;
namespace Elwig.Helpers.Weighing {
public class SchemberScale : IEventScale {
public string Manufacturer => "Schember";
public string Model => throw new NotImplementedException();
public string ScaleId => throw new NotImplementedException();
public int InternalScaleNr => throw new NotImplementedException();
public bool IsReady => throw new NotImplementedException();
public bool HasFillingClearance => throw new NotImplementedException();
public int? WeightLimit => throw new NotImplementedException();
public string? LogPath => throw new NotImplementedException();
public void Dispose() {
GC.SuppressFinalize(this);
}
}
}

View File

@ -1,23 +1,11 @@
using System; using System;
using System.IO; using System.IO;
using System.IO.Ports; using System.IO.Ports;
using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Elwig.Helpers.Weighing { namespace Elwig.Helpers.Weighing {
public class SystecScale : ICommandScale { public class SysTecITScale : Scale, ICommandScale {
protected enum Output { RTS, DTR, OUT1, OUT2 };
protected SerialPort? Serial = null;
protected SerialPort? ControlSerialEmpty = null, ControlSerialFilling = null;
protected TcpClient? Tcp = null;
protected Stream Stream;
protected readonly Output? EmptyMode = null;
protected readonly Output? FillingClearanceMode = null;
protected readonly int EmptyDelay;
public string Manufacturer => "SysTec"; public string Manufacturer => "SysTec";
public int InternalScaleNr => 1; public int InternalScaleNr => 1;
@ -25,74 +13,15 @@ namespace Elwig.Helpers.Weighing {
public string ScaleId { get; private set; } public string ScaleId { get; private set; }
public bool IsReady { get; private set; } public bool IsReady { get; private set; }
public bool HasFillingClearance { get; private set; } public bool HasFillingClearance { get; private set; }
public int? WeightLimit { get; private set; }
public string? LogPath { get; private set; }
public SystecScale(string id, string model, string connection, string? empty = null, string? filling = null, int? limit = null, string? log = null) { public SysTecITScale(string id, string model, string cnx, string? empty = null, string? filling = null, int? limit = null, string? log = null) :
base(cnx, empty, filling, limit, log) {
ScaleId = id; ScaleId = id;
Model = model; Model = model;
IsReady = true; IsReady = true;
HasFillingClearance = false; HasFillingClearance = false;
LogPath = log;
if (connection.StartsWith("serial:")) {
Serial = Utils.OpenSerialConnection(connection);
Stream = Serial.BaseStream;
} else if (connection.StartsWith("tcp:")) {
Tcp = Utils.OpenTcpConnection(connection);
Stream = Tcp.GetStream();
} else {
throw new ArgumentException("Unsupported scheme");
}
Stream.WriteTimeout = 250; Stream.WriteTimeout = 250;
Stream.ReadTimeout = 11000; Stream.ReadTimeout = 11000;
if (empty != null) {
var parts = empty.Split(':');
if (parts.Length == 3) {
if (parts[0] != Serial?.PortName)
ControlSerialEmpty = Utils.OpenSerialConnection($"serial://{parts[0]}:9600");
} else if (parts.Length != 2) {
throw new ArgumentException("Invalid value for 'empty'");
}
EmptyMode = ConvertOutput(parts[^2]);
EmptyDelay = int.Parse(parts[^1]);
}
WeightLimit = limit;
if (filling != null) {
var parts = filling.Split(':');
if (parts.Length == 2) {
if (parts[0] != Serial?.PortName)
ControlSerialFilling = parts[0] != ControlSerialEmpty?.PortName ? Utils.OpenSerialConnection($"serial://{parts[0]}:9600") : ControlSerialEmpty;
} else if (parts.Length != 1) {
throw new ArgumentException("Invalid value for 'filling'");
}
FillingClearanceMode = ConvertOutput(parts[^1]);
}
if (FillingClearanceMode != null && WeightLimit == null)
throw new ArgumentException("Weight limit has to be set, if filling clearance supervision is enabled");
}
public void Dispose() {
Stream.Close();
Serial?.Close();
ControlSerialEmpty?.Close();
ControlSerialFilling?.Close();
Tcp?.Close();
GC.SuppressFinalize(this);
}
protected static Output? ConvertOutput(string? value) {
return value switch {
null => null,
"RTS" => Output.RTS,
"DTR" => Output.DTR,
"OUT1" => Output.OUT1,
"OUT2" => Output.OUT2,
_ => throw new ArgumentException($"Invalid value for argument: '{value}'"),
};
} }
protected async Task SendCommand(string command) { protected async Task SendCommand(string command) {

View File

@ -0,0 +1,12 @@
using System;
namespace Elwig.Helpers.Weighing {
public class WeighingEventArgs : EventArgs {
public WeighingResult Result { get; set; }
public WeighingEventArgs(WeighingResult result) {
Result = result;
}
}
}

View File

@ -83,17 +83,17 @@ namespace Elwig.Models.Dtos {
[Column("mgnr")] [Column("mgnr")]
public int MgNr { get; set; } public int MgNr { get; set; }
[Column("family_name")] [Column("family_name")]
public string Name { get; set; } public required string Name { get; set; }
[Column("given_name")] [Column("given_name")]
public string GivenName { get; set; } public required string GivenName { get; set; }
[Column("address")] [Column("address")]
public string Address { get; set; } public required string Address { get; set; }
[Column("plz")] [Column("plz")]
public int Plz { get; set; } public int Plz { get; set; }
[Column("ort")] [Column("ort")]
public string Locality { get; set; } public required string Locality { get; set; }
[Column("bucket")] [Column("bucket")]
public string VtrgId { get; set; } public required string VtrgId { get; set; }
[Column("area")] [Column("area")]
public int Area { get; set; } public int Area { get; set; }
[Column("min_kg")] [Column("min_kg")]

View File

@ -48,7 +48,7 @@ namespace Elwig.Models.Dtos {
} }
private static async Task<IEnumerable<CreditNoteRowSingle>> FromDbSet(DbSet<CreditNoteRowSingle> table, int year, int avnr) { private static async Task<IEnumerable<CreditNoteRowSingle>> FromDbSet(DbSet<CreditNoteRowSingle> table, int year, int avnr) {
return await table.FromSql($""" return await table.FromSqlRaw($"""
SELECT m.mgnr, m.family_name, m.given_name, p.plz, o.name AS ort, m.address, m.iban, c.tgnr, s.year, s.precision, SELECT m.mgnr, m.family_name, m.given_name, p.plz, o.name AS ort, m.address, m.iban, c.tgnr, s.year, s.precision,
p.amount - p.net_amount AS surcharge, p.amount - p.net_amount AS surcharge,
c.net_amount, c.prev_net_amount, c.vat, c.vat_amount, c.gross_amount, c.modifiers, c.prev_modifiers, c.amount, c.net_amount, c.prev_net_amount, c.vat, c.vat_amount, c.gross_amount, c.modifiers, c.prev_modifiers, c.amount,
@ -77,7 +77,7 @@ namespace Elwig.Models.Dtos {
public string Address; public string Address;
public int Plz; public int Plz;
public string Locality; public string Locality;
public string Iban; public string? Iban;
public string TgNr; public string TgNr;
public decimal Sum; public decimal Sum;
public decimal? Surcharge; public decimal? Surcharge;
@ -101,7 +101,7 @@ namespace Elwig.Models.Dtos {
Address = row.Address; Address = row.Address;
Plz = row.Plz; Plz = row.Plz;
Locality = row.Locality; Locality = row.Locality;
Iban = Utils.FormatIban(row.Iban); Iban = row.Iban != null ? Utils.FormatIban(row.Iban) : null;
TgNr = $"{row.Year}/{row.TgNr}"; TgNr = $"{row.Year}/{row.TgNr}";
Total = Utils.DecFromDb(row.NetAmount, prec1); Total = Utils.DecFromDb(row.NetAmount, prec1);
Surcharge = (row.Surcharge == null || row.Surcharge == 0) ? null : Utils.DecFromDb((long)row.Surcharge, prec2); Surcharge = (row.Surcharge == null || row.Surcharge == 0) ? null : Utils.DecFromDb((long)row.Surcharge, prec2);
@ -133,25 +133,25 @@ namespace Elwig.Models.Dtos {
[Column("mgnr")] [Column("mgnr")]
public int MgNr { get; set; } public int MgNr { get; set; }
[Column("family_name")] [Column("family_name")]
public string Name { get; set; } public required string Name { get; set; }
[Column("given_name")] [Column("given_name")]
public string GivenName { get; set; } public required string GivenName { get; set; }
[Column("address")] [Column("address")]
public string Address { get; set; } public required string Address { get; set; }
[Column("plz")] [Column("plz")]
public int Plz { get; set; } public int Plz { get; set; }
[Column("ort")] [Column("ort")]
public string LocalityFull { get; set; } public required string LocalityFull { get; set; }
[NotMapped] [NotMapped]
public string Locality => LocalityFull.Split(",")[0]; public string Locality => LocalityFull.Split(",")[0];
[Column("iban")] [Column("iban")]
public string Iban { get; set; } public string? Iban { get; set; }
[Column("year")] [Column("year")]
public int Year { get; set; } public int Year { get; set; }
[Column("precision")] [Column("precision")]
public byte Precision { get; set; } public byte Precision { get; set; }
[Column("tgnr")] [Column("tgnr")]
public string TgNr { get; set; } public required string TgNr { get; set; }
[Column("surcharge")] [Column("surcharge")]
public long? Surcharge { get; set; } public long? Surcharge { get; set; }
[Column("net_amount")] [Column("net_amount")]

View File

@ -1,5 +1,7 @@
using Elwig.Models.Entities; using Elwig.Helpers.Billing;
using Elwig.Models.Entities;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Linq; using System.Linq;
@ -26,10 +28,13 @@ namespace Elwig.Models.Dtos {
} }
public static async Task<IDictionary<int, CreditNoteDeliveryData>> ForPaymentVariant(DbSet<CreditNoteDeliveryRowSingle> table, DbSet<Season> seasons, int year, int avnr) { public static async Task<IDictionary<int, CreditNoteDeliveryData>> ForPaymentVariant(DbSet<CreditNoteDeliveryRowSingle> table, DbSet<Season> seasons, int year, int avnr) {
var variant = (await seasons.FindAsync(year))?.PaymentVariants.Where(v => v.AvNr == avnr).SingleOrDefault();
BillingData? varData = null;
try { varData = variant != null ? BillingData.FromJson(variant.Data) : null; } catch { }
return (await FromDbSet(table, year, avnr)) return (await FromDbSet(table, year, avnr))
.GroupBy( .GroupBy(
r => new { r.Year, r.AvNr, r.MgNr, r.TgNr, r.DId, r.DPNr }, r => new { r.Year, r.AvNr, r.MgNr, r.TgNr, r.DId, r.DPNr },
(k, g) => new CreditNoteDeliveryRow(g, seasons)) (k, g) => new CreditNoteDeliveryRow(g, seasons, varData?.NetWeightModifier ?? 0.0, varData?.GrossWeightModifier ?? 0.0))
.GroupBy( .GroupBy(
r => new { r.Year, r.AvNr, r.MgNr, r.TgNr }, r => new { r.Year, r.AvNr, r.MgNr, r.TgNr },
(k, g) => new CreditNoteDeliveryData(g, k.Year, k.TgNr, mgnr: k.MgNr)) (k, g) => new CreditNoteDeliveryData(g, k.Year, k.TgNr, mgnr: k.MgNr))
@ -43,10 +48,11 @@ namespace Elwig.Models.Dtos {
return await table.FromSqlRaw($""" return await table.FromSqlRaw($"""
SELECT d.year, c.tgnr, v.avnr, d.mgnr, d.did, d.lsnr, d.dpnr, d.weight, d.modifiers, SELECT d.year, c.tgnr, v.avnr, d.mgnr, d.did, d.lsnr, d.dpnr, d.weight, d.modifiers,
b.bktnr, d.sortid, b.discr, b.value, pb.price, pb.amount, p.net_amount, p.amount AS total_amount, b.bktnr, d.sortid, b.discr, b.value, pb.price, pb.amount, p.net_amount, p.amount AS total_amount,
s.name AS variety, a.name AS attribute, q.name AS quality_level, d.oe, d.kmw s.name AS variety, a.name AS attribute, c.name AS cultivation, q.qualid AS qualid, q.name AS quality_level, d.oe, d.kmw, d.net_weight
FROM v_delivery d FROM v_delivery d
JOIN wine_variety s ON s.sortid = d.sortid JOIN wine_variety s ON s.sortid = d.sortid
LEFT JOIN wine_attribute a ON a.attrid = d.attrid LEFT JOIN wine_attribute a ON a.attrid = d.attrid
LEFT JOIN wine_cultivation c ON c.cultid = d.cultid
JOIN wine_quality_level q ON q.qualid = d.qualid JOIN wine_quality_level q ON q.qualid = d.qualid
LEFT JOIN delivery_part_bucket b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr) LEFT JOIN delivery_part_bucket b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr)
LEFT JOIN payment_variant v ON v.year = d.year LEFT JOIN payment_variant v ON v.year = d.year
@ -63,24 +69,28 @@ namespace Elwig.Models.Dtos {
public int Year; public int Year;
public int? TgNr; public int? TgNr;
public int AvNr; public int? AvNr;
public int MgNr; public int MgNr;
public string LsNr; public string LsNr;
public int DPNr; public int DPNr;
public string Variety; public string Variety;
public string? Attribute; public string? Attribute;
public string? Cultivation;
public string[] Modifiers; public string[] Modifiers;
public string QualId;
public string QualityLevel; public string QualityLevel;
public (double Oe, double Kmw) Gradation; public (double Oe, double Kmw) Gradation;
public (string Name, int Value, decimal? Price, decimal? Amount)[] Buckets; public (string Name, int Value, decimal? Price, decimal? Amount)[] Buckets;
public decimal? TotalModifiers; public decimal? TotalModifiers;
public decimal? Amount; public decimal? Amount;
public double WeighingModifier;
public CreditNoteDeliveryRow(IEnumerable<CreditNoteDeliveryRowSingle> rows, DbSet<Season> seasons) { public CreditNoteDeliveryRow(IEnumerable<CreditNoteDeliveryRowSingle> rows, DbSet<Season> seasons, double netWeightModifier, double grossWeightModifier) {
var f = rows.First(); var f = rows.First();
Year = f.Year; Year = f.Year;
TgNr = f.TgNr; TgNr = f.TgNr;
AvNr = f.AvNr;
MgNr = f.MgNr; MgNr = f.MgNr;
var season = seasons.Find(Year); var season = seasons.Find(Year);
@ -88,12 +98,14 @@ namespace Elwig.Models.Dtos {
DPNr = f.DPNr; DPNr = f.DPNr;
Variety = f.Variety; Variety = f.Variety;
Attribute = f.Attribute; Attribute = f.Attribute;
Cultivation = f.Cultivation;
var modifiers = (IEnumerable<Modifier>)(f.Modifiers ?? "").Split(',') var modifiers = (IEnumerable<Modifier>)(f.Modifiers ?? "").Split(',')
.Select(m => season?.Modifiers.FirstOrDefault(s => s.ModId == m)) .Select(m => season?.Modifiers.FirstOrDefault(s => s.ModId == m))
.Where(m => m != null) .Where(m => m != null)
.OrderBy(m => m.Ordering) .OrderBy(m => m.Ordering)
.ToList(); .ToList();
Modifiers = modifiers.Select(m => m.Name).ToArray(); Modifiers = modifiers.Select(m => m.Name).ToArray();
QualId = f.QualId;
QualityLevel = f.QualityLevel; QualityLevel = f.QualityLevel;
Gradation = (f.Oe, f.Kmw); Gradation = (f.Oe, f.Kmw);
Buckets = rows Buckets = rows
@ -103,9 +115,11 @@ namespace Elwig.Models.Dtos {
b.Price != null ? season?.DecFromDb((long)b.Price) : null, b.Price != null ? season?.DecFromDb((long)b.Price) : null,
b.Amount != null ? season?.DecFromDb((long)b.Amount) : null)) b.Amount != null ? season?.DecFromDb((long)b.Amount) : null))
.ToArray(); .ToArray();
WeighingModifier = f.NetWeight ? netWeightModifier : grossWeightModifier;
Amount = f.TotalAmount != null ? season?.DecFromDb((long)f.TotalAmount) : null; Amount = f.TotalAmount != null ? season?.DecFromDb((long)f.TotalAmount) : null;
var netAmount = f.NetAmount != null ? season?.DecFromDb((long)f.NetAmount) : null; var netAmount = f.NetAmount != null ? season?.DecFromDb((long)f.NetAmount) : null;
TotalModifiers = Amount - netAmount; var amt = netAmount * (decimal)(1.0 + WeighingModifier);
TotalModifiers = Amount - (amt != null ? Math.Round((decimal)amt, season?.Precision ?? 0) : null);
} }
} }
@ -122,7 +136,7 @@ namespace Elwig.Models.Dtos {
[Column("did")] [Column("did")]
public int DId { get; set; } public int DId { get; set; }
[Column("lsnr")] [Column("lsnr")]
public string LsNr { get; set; } public required string LsNr { get; set; }
[Column("dpnr")] [Column("dpnr")]
public int DPNr { get; set; } public int DPNr { get; set; }
[Column("weight")] [Column("weight")]
@ -132,9 +146,9 @@ namespace Elwig.Models.Dtos {
[Column("bktnr")] [Column("bktnr")]
public int BktNr { get; set; } public int BktNr { get; set; }
[Column("sortid")] [Column("sortid")]
public string SortId { get; set; } public required string SortId { get; set; }
[Column("discr")] [Column("discr")]
public string Discr { get; set; } public required string Discr { get; set; }
[Column("value")] [Column("value")]
public int Value { get; set; } public int Value { get; set; }
[Column("price")] [Column("price")]
@ -146,14 +160,20 @@ namespace Elwig.Models.Dtos {
[Column("total_amount")] [Column("total_amount")]
public long? TotalAmount { get; set; } public long? TotalAmount { get; set; }
[Column("variety")] [Column("variety")]
public string Variety { get; set; } public required string Variety { get; set; }
[Column("attribute")] [Column("attribute")]
public string? Attribute { get; set; } public string? Attribute { get; set; }
[Column("cultivation")]
public string? Cultivation { get; set; }
[Column("qualid")]
public required string QualId { get; set; }
[Column("quality_level")] [Column("quality_level")]
public string QualityLevel { get; set; } public required string QualityLevel { get; set; }
[Column("oe")] [Column("oe")]
public double Oe { get; set; } public double Oe { get; set; }
[Column("kmw")] [Column("kmw")]
public double Kmw { get; set; } public double Kmw { get; set; }
[Column("net_weight")]
public bool NetWeight { get; set; }
} }
} }

View File

@ -36,7 +36,7 @@ namespace Elwig.Models.Dtos {
var elType = type?.GetElementType(); var elType = type?.GetElementType();
return type != null && type.IsValueType && type.Name.StartsWith("ValueTuple") ? type.GetFields().Select(f => f.FieldType) : return type != null && type.IsValueType && type.Name.StartsWith("ValueTuple") ? type.GetFields().Select(f => f.FieldType) :
type != null && elType != null && type.IsArray && elType.IsValueType && elType.Name.StartsWith("ValueTuple") ? elType.GetFields().Select(f => f.FieldType) : type != null && elType != null && type.IsArray && elType.IsValueType && elType.Name.StartsWith("ValueTuple") ? elType.GetFields().Select(f => f.FieldType) :
new Type?[] { type }; [type];
}).ToList(); }).ToList();
ColumnSpans = ColumnTypes.Select(type => { ColumnSpans = ColumnTypes.Select(type => {
var elType = type?.GetElementType(); var elType = type?.GetElementType();
@ -44,7 +44,7 @@ namespace Elwig.Models.Dtos {
type != null && elType != null && type.IsArray && elType.IsValueType && elType.Name.StartsWith("ValueTuple") ? elType.GetFields().Length : 1; type != null && elType != null && type.IsArray && elType.IsValueType && elType.Name.StartsWith("ValueTuple") ? elType.GetFields().Length : 1;
}).ToList(); }).ToList();
ColumnWidths = colNames.Select(c => c.Item4).ToList(); ColumnWidths = colNames.Select(c => c.Item4).ToList();
ColumnUnits = colNames.Select(c => c.Item3?.Split("|").Select(p => p.Length == 0 ? null : p).ToArray() ?? Array.Empty<string?>()).ToList(); ColumnUnits = colNames.Select(c => c.Item3?.Split("|").Select(p => p.Length == 0 ? null : p).ToArray() ?? []).ToList();
} }
public DataTable(string name, string fullName, IEnumerable<T> rows, IEnumerable<(string, string, string?)>? colNames = null) : public DataTable(string name, string fullName, IEnumerable<T> rows, IEnumerable<(string, string, string?)>? colNames = null) :

View File

@ -73,10 +73,12 @@ namespace Elwig.Models.Dtos {
public int DPNr; public int DPNr;
public string Variety; public string Variety;
public string? Attribute; public string? Attribute;
public string? Cultivation;
public string QualityLevel; public string QualityLevel;
public (double Oe, double Kmw) Gradation; public (double Oe, double Kmw) Gradation;
public string[] Modifiers; public string[] Modifiers;
public int Weight; public int Weight;
public bool IsNetWeight;
public (string Name, int Value)[] Buckets; public (string Name, int Value)[] Buckets;
public DeliveryConfirmationDeliveryRow(DeliveryPart p) { public DeliveryConfirmationDeliveryRow(DeliveryPart p) {
@ -85,12 +87,14 @@ namespace Elwig.Models.Dtos {
DPNr = p.DPNr; DPNr = p.DPNr;
Variety = p.Variety.Name; Variety = p.Variety.Name;
Attribute = p.Attribute?.Name; Attribute = p.Attribute?.Name;
Cultivation = p.Cultivation?.Name;
QualityLevel = p.Quality.Name; QualityLevel = p.Quality.Name;
Gradation = (p.Oe, p.Kmw); Gradation = (p.Oe, p.Kmw);
Modifiers = p.Modifiers Modifiers = p.Modifiers
.Select(m => m.Name) .Select(m => m.Name)
.ToArray(); .ToArray();
Weight = p.Weight; Weight = p.Weight;
IsNetWeight = p.IsNetWeight;
Buckets = p.Buckets Buckets = p.Buckets
.Where(b => b.Value > 0) .Where(b => b.Value > 0)
.OrderByDescending(b => b.BktNr) .OrderByDescending(b => b.BktNr)

View File

@ -99,17 +99,17 @@ namespace Elwig.Models.Dtos {
[Column("mgnr")] [Column("mgnr")]
public int MgNr { get; set; } public int MgNr { get; set; }
[Column("family_name")] [Column("family_name")]
public string Name { get; set; } public required string Name { get; set; }
[Column("given_name")] [Column("given_name")]
public string GivenName { get; set; } public required string GivenName { get; set; }
[Column("address")] [Column("address")]
public string Address { get; set; } public required string Address { get; set; }
[Column("plz")] [Column("plz")]
public int Plz { get; set; } public int Plz { get; set; }
[Column("ort")] [Column("ort")]
public string Locality { get; set; } public required string Locality { get; set; }
[Column("bucket")] [Column("bucket")]
public string VtrgId { get; set; } public required string VtrgId { get; set; }
[Column("area")] [Column("area")]
public int Area { get; set; } public int Area { get; set; }
[Column("weight")] [Column("weight")]

View File

@ -49,15 +49,15 @@ namespace Elwig.Models.Dtos {
[Column("mgnr")] [Column("mgnr")]
public int MgNr { get; set; } public int MgNr { get; set; }
[Column("family_name")] [Column("family_name")]
public string Name { get; set; } public required string Name { get; set; }
[Column("given_name")] [Column("given_name")]
public string GivenName { get; set; } public required string GivenName { get; set; }
[Column("address")] [Column("address")]
public string Address { get; set; } public required string Address { get; set; }
[Column("plz")] [Column("plz")]
public int Plz { get; set; } public int Plz { get; set; }
[Column("ort")] [Column("ort")]
public string LocalityFull { get; set; } public required string LocalityFull { get; set; }
[NotMapped] [NotMapped]
public string Locality => LocalityFull.Split(",")[0]; public string Locality => LocalityFull.Split(",")[0];
[Column("business_shares")] [Column("business_shares")]

View File

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

View File

@ -9,10 +9,10 @@ namespace Elwig.Models.Entities {
public int Gkz { get; private set; } public int Gkz { get; private set; }
[Column("name")] [Column("name")]
public string Name { get; private set; } public string Name { get; private set; } = null!;
[InverseProperty("Gem")] [InverseProperty("Gem")]
public virtual ISet<AT_Kg> Kgs { get; private set; } public virtual ISet<AT_Kg> Kgs { get; private set; } = null!;
[InverseProperty("AtGem")] [InverseProperty("AtGem")]
public virtual WbGem? WbGem { get; private set; } public virtual WbGem? WbGem { get; private set; }

View File

@ -11,10 +11,10 @@ namespace Elwig.Models.Entities {
public int Gkz { get; private set; } public int Gkz { get; private set; }
[Column("name")] [Column("name")]
public string Name { get; private set; } public string Name { get; private set; } = null!;
[ForeignKey("Gkz")] [ForeignKey("Gkz")]
public virtual AT_Gem Gem { get; private set; } public virtual AT_Gem Gem { get; private set; } = null!;
[InverseProperty("AtKg")] [InverseProperty("AtKg")]
public virtual WbKg? WbKg { get; private set; } public virtual WbKg? WbKg { get; private set; }

View File

@ -14,10 +14,10 @@ namespace Elwig.Models.Entities {
public int? KgNr { get; private set; } public int? KgNr { get; private set; }
[Column("name")] [Column("name")]
public string Name { get; private set; } public string Name { get; private set; } = null!;
[ForeignKey("Gkz")] [ForeignKey("Gkz")]
public virtual AT_Gem Gem { get; private set; } public virtual AT_Gem Gem { get; private set; } = null!;
[ForeignKey("KgNr")] [ForeignKey("KgNr")]
public virtual AT_Kg? Kg { get; private set; } public virtual AT_Kg? Kg { get; private set; }

View File

@ -9,13 +9,13 @@ namespace Elwig.Models.Entities {
public int Plz { get; private set; } public int Plz { get; private set; }
[Column("ort")] [Column("ort")]
public string Ort { get; private set; } public string Ort { get; private set; } = null!;
[Column("blnr")] [Column("blnr")]
public int BlNr { get; private set; } public int BlNr { get; private set; }
[Column("type")] [Column("type")]
public string Type { get; private set; } public string Type { get; private set; } = null!;
[Column("internal")] [Column("internal")]
public bool IsInternal { get; private set; } public bool IsInternal { get; private set; }
@ -27,6 +27,6 @@ namespace Elwig.Models.Entities {
public bool IsPoBox { get; private set; } public bool IsPoBox { get; private set; }
[InverseProperty("AtPlz")] [InverseProperty("AtPlz")]
public virtual ISet<AT_PlzDest> Orte { get; private set; } public virtual ISet<AT_PlzDest> Orte { get; private set; } = null!;
} }
} }

View File

@ -15,18 +15,18 @@ namespace Elwig.Models.Entities {
public int CountryNum { get; private set; } public int CountryNum { get; private set; }
[Column("id")] [Column("id")]
public string Id { get; private set; } public string Id { get; private set; } = null!;
[Column("dest")] [Column("dest")]
public string Dest { get; private set; } public string Dest { get; private set; } = null!;
[ForeignKey("Plz")] [ForeignKey("Plz")]
public virtual AT_Plz AtPlz { get; private set; } public virtual AT_Plz AtPlz { get; private set; } = null!;
[ForeignKey("Okz")] [ForeignKey("Okz")]
public virtual AT_Ort Ort { get; private set; } public virtual AT_Ort Ort { get; private set; } = null!;
[ForeignKey("CountryNum")] [ForeignKey("CountryNum")]
public virtual Country Country { get; private set; } public virtual Country Country { get; private set; } = null!;
} }
} }

View File

@ -2,7 +2,6 @@ using Elwig.Helpers;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
namespace Elwig.Models.Entities { namespace Elwig.Models.Entities {
[Table("area_commitment"), PrimaryKey("FbNr")] [Table("area_commitment"), PrimaryKey("FbNr")]
@ -14,10 +13,10 @@ namespace Elwig.Models.Entities {
public int MgNr { get; set; } public int MgNr { get; set; }
[Column("vtrgid")] [Column("vtrgid")]
public string VtrgId { get; set; } public required string VtrgId { get; set; }
[Column("cultid")] [Column("cultid")]
public string CultId { get; set; } public string? CultId { get; set; }
[Column("area")] [Column("area")]
public int Area { get; set; } public int Area { get; set; }
@ -26,7 +25,7 @@ namespace Elwig.Models.Entities {
public int KgNr { get; set; } public int KgNr { get; set; }
[Column("gstnr")] [Column("gstnr")]
public string GstNr { get; set; } public required string GstNr { get; set; }
[Column("rdnr")] [Column("rdnr")]
public int? RdNr { get; set; } public int? RdNr { get; set; }
@ -41,25 +40,24 @@ namespace Elwig.Models.Entities {
public string? Comment { get; set; } public string? Comment { get; set; }
[ForeignKey("MgNr")] [ForeignKey("MgNr")]
public virtual Member Member { get; private set; } public virtual Member Member { get; private set; } = null!;
[ForeignKey("VtrgId")] [ForeignKey("VtrgId")]
public virtual AreaComType AreaComType { get; private set; } public virtual AreaComType AreaComType { get; private set; } = null!;
[ForeignKey("CultId")] [ForeignKey("CultId")]
public virtual WineCult WineCult { get; private set; } public virtual WineCult? WineCult { get; private set; }
[ForeignKey("KgNr")] [ForeignKey("KgNr")]
public virtual WbKg Kg { get; private set; } public virtual WbKg Kg { get; private set; } = null!;
[ForeignKey("KgNr, RdNr")] [ForeignKey("KgNr, RdNr")]
public virtual WbRd? Rd { get; private set; } public virtual WbRd? Rd { get; private set; }
public int SearchScore(IEnumerable<string> keywords) { public int SearchScore(IEnumerable<string> keywords) {
var list = new string?[] { return Utils.GetSearchScore([
WineCult.Name, Kg.AtKg.Name, Rd.Name, GstNr, Comment, WineCult?.Name, Kg.AtKg.Name, Rd?.Name, GstNr, Comment,
}.ToList(); ], keywords);
return Utils.GetSearchScore(list, keywords);
} }
} }
} }

View File

@ -7,10 +7,10 @@ namespace Elwig.Models.Entities {
[Table("area_commitment_type"), PrimaryKey("VtrgId"), Index("SortId", "AttrId", "Discriminator", IsUnique = true)] [Table("area_commitment_type"), PrimaryKey("VtrgId"), Index("SortId", "AttrId", "Discriminator", IsUnique = true)]
public class AreaComType { public class AreaComType {
[Column("vtrgid")] [Column("vtrgid")]
public string VtrgId { get; set; } public required string VtrgId { get; set; }
[Column("sortid")] [Column("sortid")]
public string SortId { get; set; } public required string SortId { get; set; }
[Column("attrid")] [Column("attrid")]
public string? AttrId { get; set; } public string? AttrId { get; set; }
@ -46,7 +46,7 @@ namespace Elwig.Models.Entities {
} }
[ForeignKey("SortId")] [ForeignKey("SortId")]
public virtual WineVar WineVar { get; private set; } public virtual WineVar WineVar { get; private set; } = null!;
[ForeignKey("AttrId")] [ForeignKey("AttrId")]
public virtual WineAttr? WineAttr { get; private set; } public virtual WineAttr? WineAttr { get; private set; }

View File

@ -9,24 +9,24 @@ namespace Elwig.Models.Entities {
public int MgNr { get; set; } public int MgNr { get; set; }
[Column("name")] [Column("name")]
public string Name { get; set; } public required string Name { get; set; }
[Column("country")] [Column("country")]
public int CountryNum { get; set; } public int CountryNum { get; set; }
[Column("postal_dest")] [Column("postal_dest")]
public string PostalDestId { get; set; } public required string PostalDestId { get; set; }
[Column("address")] [Column("address")]
public string Address { get; set; } public required string Address { get; set; }
[ForeignKey("MgNr")] [ForeignKey("MgNr")]
public virtual Member Member { get; private set; } public virtual Member Member { get; private set; } = null!;
[ForeignKey("CountryNum")] [ForeignKey("CountryNum")]
public virtual Country Country { get; private set; } public virtual Country Country { get; private set; } = null!;
[ForeignKey("CountryNum, PostalDestId")] [ForeignKey("CountryNum, PostalDestId")]
public virtual PostalDest PostalDest { get; private set; } public virtual PostalDest PostalDest { get; private set; } = null!;
} }
} }

View File

@ -7,10 +7,10 @@ namespace Elwig.Models.Entities {
[Table("branch"), PrimaryKey("ZwstId"), Index("Name", IsUnique = true)] [Table("branch"), PrimaryKey("ZwstId"), Index("Name", IsUnique = true)]
public class Branch { public class Branch {
[Column("zwstid")] [Column("zwstid")]
public string ZwstId { get; set; } public required string ZwstId { get; set; }
[Column("name")] [Column("name")]
public string Name { get; set; } public required string Name { get; set; }
[Column("country")] [Column("country")]
public int? CountryNum { get; set; } public int? CountryNum { get; set; }
@ -37,6 +37,6 @@ namespace Elwig.Models.Entities {
public string? MobileNr { get; set; } public string? MobileNr { get; set; }
[InverseProperty("Branch")] [InverseProperty("Branch")]
public virtual ISet<Member> Members { get; private set; } public virtual ISet<Member> Members { get; private set; } = null!;
} }
} }

View File

@ -5,7 +5,7 @@ namespace Elwig.Models.Entities {
[Table("client_parameter"), PrimaryKey("Param")] [Table("client_parameter"), PrimaryKey("Param")]
public class ClientParam { public class ClientParam {
[Column("param")] [Column("param")]
public string Param { get; set; } public required string Param { get; set; }
[Column("value")] [Column("value")]
public string? Value { get; set; } public string? Value { get; set; }

View File

@ -9,13 +9,13 @@ namespace Elwig.Models.Entities {
public int Num { get; private set; } public int Num { get; private set; }
[Column("alpha2")] [Column("alpha2")]
public string Alpha2 { get; private set; } public string Alpha2 { get; private set; } = null!;
[Column("alpha3")] [Column("alpha3")]
public string Alpha3 { get; private set; } public string Alpha3 { get; private set; } = null!;
[Column("name")] [Column("name")]
public string Name { get; private set; } public string Name { get; private set; } = null!;
[Column("is_visible")] [Column("is_visible")]
public bool IsVisible { get; private set; } public bool IsVisible { get; private set; }

View File

@ -94,12 +94,12 @@ namespace Elwig.Models.Entities {
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime; public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
[ForeignKey("Year, AvNr, MgNr")] [ForeignKey("Year, AvNr, MgNr")]
public virtual PaymentMember Payment { get; private set; } public virtual PaymentMember Payment { get; private set; } = null!;
[ForeignKey("Year, AvNr")] [ForeignKey("Year, AvNr")]
public virtual PaymentVar Variant { get; private set; } public virtual PaymentVar Variant { get; private set; } = null!;
[ForeignKey("MgNr")] [ForeignKey("MgNr")]
public virtual Member Member { get; private set; } public virtual Member Member { get; private set; } = null!;
} }
} }

View File

@ -6,10 +6,10 @@ namespace Elwig.Models.Entities {
[Table("currency"), PrimaryKey("Code")] [Table("currency"), PrimaryKey("Code")]
public class Currency { public class Currency {
[Column("code")] [Column("code")]
public string Code { get; private set; } public string Code { get; private set; } = null!;
[Column("name")] [Column("name")]
public string Name { get; private set; } public string Name { get; private set; } = null!;
[Column("symbol")] [Column("symbol")]
public string? Symbol { get; private set; } public string? Symbol { get; private set; }

View File

@ -16,7 +16,7 @@ namespace Elwig.Models.Entities {
public int DId { get; set; } public int DId { get; set; }
[Column("date")] [Column("date")]
public string DateString { get; set; } public required string DateString { get; set; }
[NotMapped] [NotMapped]
public DateOnly Date { public DateOnly Date {
@ -43,31 +43,31 @@ namespace Elwig.Models.Entities {
} }
[Column("zwstid")] [Column("zwstid")]
public string ZwstId { get; set; } public required string ZwstId { get; set; }
[ForeignKey("ZwstId")] [ForeignKey("ZwstId")]
public virtual Branch Branch { get; private set; } public virtual Branch Branch { get; private set; } = null!;
[Column("lnr")] [Column("lnr")]
public int LNr { get; set; } public int LNr { get; set; }
[Column("lsnr")] [Column("lsnr")]
public string LsNr { get; set; } public required string LsNr { get; set; }
[Column("mgnr")] [Column("mgnr")]
public int MgNr { get; set; } public int MgNr { get; set; }
[ForeignKey("MgNr")] [ForeignKey("MgNr")]
public virtual Member Member { get; private set; } public virtual Member Member { get; private set; } = null!;
[Column("comment")] [Column("comment")]
public string? Comment { get; set; } public string? Comment { get; set; }
[ForeignKey("Year")] [ForeignKey("Year")]
public virtual Season Season { get; private set; } public virtual Season Season { get; private set; } = null!;
[InverseProperty("Delivery")] [InverseProperty("Delivery")]
public virtual ISet<DeliveryPart> Parts { get; private set; } public virtual ISet<DeliveryPart> Parts { get; private set; } = null!;
[NotMapped] [NotMapped]
public IEnumerable<DeliveryPart> FilteredParts => PartFilter == null ? Parts : Parts.Where(p => PartFilter(p)); public IEnumerable<DeliveryPart> FilteredParts => PartFilter == null ? Parts : Parts.Where(p => PartFilter(p));

View File

@ -14,16 +14,16 @@ namespace Elwig.Models.Entities {
public int DId { get; set; } public int DId { get; set; }
[ForeignKey("Year, DId")] [ForeignKey("Year, DId")]
public virtual Delivery Delivery { get; private set; } public virtual Delivery Delivery { get; private set; } = null!;
[Column("dpnr")] [Column("dpnr")]
public int DPNr { get; set; } public int DPNr { get; set; }
[Column("sortid")] [Column("sortid")]
public string SortId { get; set; } public required string SortId { get; set; }
[ForeignKey("SortId")] [ForeignKey("SortId")]
public virtual WineVar Variety { get; private set; } public virtual WineVar Variety { get; private set; } = null!;
[Column("attrid")] [Column("attrid")]
public string? AttrId { get; set; } public string? AttrId { get; set; }
@ -31,6 +31,12 @@ namespace Elwig.Models.Entities {
[ForeignKey("AttrId")] [ForeignKey("AttrId")]
public virtual WineAttr? Attribute { get; private set; } public virtual WineAttr? Attribute { get; private set; }
[Column("cultid")]
public string? CultId { get; set; }
[ForeignKey("CultId")]
public virtual WineCult? Cultivation { get; private set; }
[Column("weight")] [Column("weight")]
public int Weight { get; set; } public int Weight { get; set; }
@ -43,16 +49,16 @@ namespace Elwig.Models.Entities {
} }
[Column("qualid")] [Column("qualid")]
public string QualId { get; set; } public required string QualId { get; set; }
[ForeignKey("QualId")] [ForeignKey("QualId")]
public virtual WineQualLevel Quality { get; private set; } public virtual WineQualLevel Quality { get; private set; } = null!;
[Column("hkid")] [Column("hkid")]
public string HkId { get; set; } public required string HkId { get; set; }
[ForeignKey("HkId")] [ForeignKey("HkId")]
public virtual WineOrigin Origin { get; private set; } public virtual WineOrigin Origin { get; private set; } = null!;
[Column("kgnr")] [Column("kgnr")]
public int? KgNr { get; set; } public int? KgNr { get; set; }
@ -66,14 +72,14 @@ namespace Elwig.Models.Entities {
[ForeignKey("KgNr, RdNr")] [ForeignKey("KgNr, RdNr")]
public virtual WbRd? Rd { get; private set; } public virtual WbRd? Rd { get; private set; }
[Column("gerebelt")] [Column("net_weight")]
public bool IsGerebelt { get; set; } public bool IsNetWeight { get; set; }
[Column("manual_weighing")] [Column("manual_weighing")]
public bool ManualWeighing { get; set; } public bool IsManualWeighing { get; set; }
[Column("spl_check")] [Column("spl_check")]
public bool SplCheck { get; set; } public bool IsSplCheck { get; set; }
[Column("hand_picked")] [Column("hand_picked")]
public bool? IsHandPicked { get; set; } public bool? IsHandPicked { get; set; }
@ -103,7 +109,7 @@ namespace Elwig.Models.Entities {
public string? Comment { get; set; } public string? Comment { get; set; }
[InverseProperty("Part")] [InverseProperty("Part")]
public virtual ISet<DeliveryPartModifier> PartModifiers { get; private set; } public virtual ISet<DeliveryPartModifier> PartModifiers { get; private set; } = null!;
[NotMapped] [NotMapped]
public IEnumerable<Modifier> Modifiers => PartModifiers.Select(m => m.Modifier).OrderBy(m => m.Ordering); public IEnumerable<Modifier> Modifiers => PartModifiers.Select(m => m.Modifier).OrderBy(m => m.Ordering);
@ -115,6 +121,6 @@ namespace Elwig.Models.Entities {
public string OriginString => Origin.OriginString + "\n" + (Kg?.Gl != null ? $" / {Kg.Gl.Name}" : "") + (Kg != null ? $" / {Kg.AtKg.Gem.Name} / KG {Kg.AtKg.Name}" : "") + (Rd != null ? $" / Ried {Rd.Name}" : ""); public string OriginString => Origin.OriginString + "\n" + (Kg?.Gl != null ? $" / {Kg.Gl.Name}" : "") + (Kg != null ? $" / {Kg.AtKg.Gem.Name} / KG {Kg.AtKg.Name}" : "") + (Rd != null ? $" / Ried {Rd.Name}" : "");
[InverseProperty("Part")] [InverseProperty("Part")]
public virtual ISet<DeliveryPartBucket> Buckets { get; private set; } public virtual ISet<DeliveryPartBucket> Buckets { get; private set; } = null!;
} }
} }

View File

@ -17,12 +17,12 @@ namespace Elwig.Models.Entities {
public int BktNr { get; set; } public int BktNr { get; set; }
[Column("discr")] [Column("discr")]
public string Discr { get; set; } public required string Discr { get; set; }
[Column("value")] [Column("value")]
public int Value { get; set; } public int Value { get; set; }
[ForeignKey("Year, DId, DPNr")] [ForeignKey("Year, DId, DPNr")]
public virtual DeliveryPart Part { get; private set; } public virtual DeliveryPart Part { get; private set; } = null!;
} }
} }

View File

@ -14,12 +14,12 @@ namespace Elwig.Models.Entities {
public int DPNr { get; set; } public int DPNr { get; set; }
[ForeignKey("Year, DId, DPNr")] [ForeignKey("Year, DId, DPNr")]
public virtual DeliveryPart Part { get; private set; } public virtual DeliveryPart Part { get; private set; } = null!;
[Column("modid")] [Column("modid")]
public string ModId { get; set; } public required string ModId { get; set; }
[ForeignKey("Year, ModId")] [ForeignKey("Year, ModId")]
public virtual Modifier Modifier { get; private set; } public virtual Modifier Modifier { get; private set; } = null!;
} }
} }

View File

@ -18,19 +18,19 @@ namespace Elwig.Models.Entities {
public string? Prefix { get; set; } public string? Prefix { get; set; }
[Column("given_name")] [Column("given_name")]
public string GivenName { get; set; } public required string GivenName { get; set; }
[Column("middle_names")] [Column("middle_names")]
public string? MiddleName { get; set; } public string? MiddleName { get; set; }
[NotMapped] [NotMapped]
public string[] MiddleNames { public string[] MiddleNames {
get { return (MiddleName != null) ? MiddleName.Split(" ") : Array.Empty<string>(); } get => (MiddleName != null) ? MiddleName.Split(" ") : [];
set { MiddleName = (value.Length > 0) ? string.Join(" ", value) : null; } set => MiddleName = (value.Length > 0) ? string.Join(" ", value) : null;
} }
[Column("family_name")] [Column("family_name")]
public string FamilyName { get; set; } public required string FamilyName { get; set; }
[Column("suffix")] [Column("suffix")]
public string? Suffix { get; set; } public string? Suffix { get; set; }
@ -46,7 +46,7 @@ namespace Elwig.Models.Entities {
public string AdministrativeName => AdministrativeName1 + " " + AdministrativeName2; public string AdministrativeName => AdministrativeName1 + " " + AdministrativeName2;
public string AdministrativeName1 => FamilyName.ToUpper(); public string AdministrativeName1 => FamilyName.Replace('ß', 'ẞ').ToUpper();
public string AdministrativeName2 => public string AdministrativeName2 =>
(Prefix != null ? Prefix + " " : "") + (Prefix != null ? Prefix + " " : "") +
@ -118,10 +118,10 @@ namespace Elwig.Models.Entities {
public int CountryNum { get; set; } public int CountryNum { get; set; }
[Column("postal_dest")] [Column("postal_dest")]
public string PostalDestId { get; set; } public string PostalDestId { get; set; } = null!;
[Column("address")] [Column("address")]
public string Address { get; set; } public string Address { get; set; } = null!;
[Column("default_kgnr")] [Column("default_kgnr")]
public int? DefaultKgNr { get; set; } public int? DefaultKgNr { get; set; }
@ -139,19 +139,22 @@ namespace Elwig.Models.Entities {
public virtual Member? Predecessor { get; private set; } public virtual Member? Predecessor { get; private set; }
[ForeignKey("CountryNum")] [ForeignKey("CountryNum")]
public virtual Country Country { get; private set; } public virtual Country Country { get; private set; } = null!;
[ForeignKey("CountryNum, PostalDestId")] [ForeignKey("CountryNum, PostalDestId")]
public virtual PostalDest PostalDest { get; private set; } public virtual PostalDest PostalDest { get; private set; } = null!;
[ForeignKey("DefaultKgNr")] [ForeignKey("DefaultKgNr")]
public virtual AT_Kg? DefaultKg { get; private set; } public virtual WbKg? DefaultWbKg { get; private set; }
[NotMapped]
public AT_Kg? DefaultKg => DefaultWbKg?.AtKg;
[ForeignKey("ZwstId")] [ForeignKey("ZwstId")]
public virtual Branch? Branch { get; private set; } public virtual Branch? Branch { get; private set; }
[InverseProperty("Member")] [InverseProperty("Member")]
public virtual ISet<AreaCom> AreaCommitments { get; private set; } public virtual ISet<AreaCom> AreaCommitments { get; private set; } = null!;
[NotMapped] [NotMapped]
public IEnumerable<AreaCom> ActiveAreaCommitments => AreaCommitments public IEnumerable<AreaCom> ActiveAreaCommitments => AreaCommitments
@ -161,22 +164,22 @@ namespace Elwig.Models.Entities {
public virtual BillingAddr? BillingAddress { get; private set; } public virtual BillingAddr? BillingAddress { get; private set; }
[InverseProperty("Member")] [InverseProperty("Member")]
public virtual ISet<Delivery> Deliveries { get; private set; } public virtual ISet<Delivery> Deliveries { get; private set; } = null!;
[InverseProperty("Member")] [InverseProperty("Member")]
public virtual ISet<MemberTelNr> TelephoneNumbers { get; private set; } public virtual ISet<MemberTelNr> TelephoneNumbers { get; private set; } = null!;
[InverseProperty("member")] [InverseProperty("member")]
public virtual ISet<MemberEmailAddr> EmailAddresses { get; private set; } public virtual ISet<MemberEmailAddr> EmailAddresses { get; private set; } = null!;
public string FullAddress => $"{Address}, {PostalDest.AtPlz.Plz} {PostalDest.AtPlz.Ort.Name}"; public string FullAddress => $"{Address}, {PostalDest.AtPlz?.Plz} {PostalDest.AtPlz?.Ort.Name}";
public int SearchScore(IEnumerable<string> keywords) { public int SearchScore(IEnumerable<string> keywords) {
return Utils.GetSearchScore(new string?[] { return Utils.GetSearchScore([
FamilyName, MiddleName, GivenName, FamilyName, MiddleName, GivenName,
BillingAddress?.Name, BillingAddress?.Name,
Comment, Comment,
}, keywords); ], keywords);
} }
} }
} }

View File

@ -11,12 +11,12 @@ namespace Elwig.Models.Entities {
public int Nr { get; set; } public int Nr { get; set; }
[Column("address")] [Column("address")]
public string Address { get; set; } public required string Address { get; set; }
[Column("comment")] [Column("comment")]
public string? Comment { get; set; } public string? Comment { get; set; }
[ForeignKey("MgNr")] [ForeignKey("MgNr")]
public virtual Member Member { get; private set; } public virtual Member Member { get; private set; } = null!;
} }
} }

View File

@ -9,7 +9,7 @@ namespace Elwig.Models.Entities {
public int MgNr { get; set; } public int MgNr { get; set; }
[Column("date")] [Column("date")]
public string DateString { get; set; } public required string DateString { get; set; }
[NotMapped] [NotMapped]
public DateOnly Date { public DateOnly Date {
get => DateOnly.ParseExact(DateString, "yyyy-MM-dd"); get => DateOnly.ParseExact(DateString, "yyyy-MM-dd");
@ -20,12 +20,12 @@ namespace Elwig.Models.Entities {
public int BusinessShares { get; set; } public int BusinessShares { get; set; }
[Column("type")] [Column("type")]
public string Type { get; set; } public required string Type { get; set; }
[Column("comment")] [Column("comment")]
public string? Comment { get; set; } public string? Comment { get; set; }
[ForeignKey("MgNr")] [ForeignKey("MgNr")]
public virtual Member Member { get; private set; } public virtual Member Member { get; private set; } = null!;
} }
} }

View File

@ -11,15 +11,15 @@ namespace Elwig.Models.Entities {
public int Nr { get; set; } public int Nr { get; set; }
[Column("type")] [Column("type")]
public string Type { get; set; } public required string Type { get; set; }
[Column("number")] [Column("number")]
public string Number { get; set; } public required string Number { get; set; }
[Column("comment")] [Column("comment")]
public string? Comment { get; set; } public string? Comment { get; set; }
[ForeignKey("MgNr")] [ForeignKey("MgNr")]
public virtual Member Member { get; private set; } public virtual Member Member { get; private set; } = null!;
} }
} }

View File

@ -11,13 +11,13 @@ namespace Elwig.Models.Entities {
public int Year { get; set; } public int Year { get; set; }
[Column("modid")] [Column("modid")]
public string ModId { get; set; } public required string ModId { get; set; }
[Column("ordering")] [Column("ordering")]
public int Ordering { get; set; } public int Ordering { get; set; }
[Column("name")] [Column("name")]
public string Name { get; set; } public required string Name { get; set; }
[Column("abs")] [Column("abs")]
public long? AbsValue { get; set; } public long? AbsValue { get; set; }
@ -44,7 +44,7 @@ namespace Elwig.Models.Entities {
public bool IsQuickSelect { get; set; } public bool IsQuickSelect { get; set; }
[ForeignKey("Year")] [ForeignKey("Year")]
public virtual Season Season { get; private set; } public virtual Season Season { get; private set; } = null!;
public string ValueStr => public string ValueStr =>
(Abs != null) ? $"{Utils.GetSign(Abs.Value)}{Math.Abs(Abs.Value).ToString("0." + string.Concat(Enumerable.Repeat('0', Season.Precision)))}\u00a0{Season.Currency.Symbol}/kg" : (Abs != null) ? $"{Utils.GetSign(Abs.Value)}{Math.Abs(Abs.Value).ToString("0." + string.Concat(Enumerable.Repeat('0', Season.Precision)))}\u00a0{Season.Currency.Symbol}/kg" :

View File

@ -46,9 +46,9 @@ namespace Elwig.Models.Entities {
public decimal Amount => Variant.Season.DecFromDb(AmountValue); public decimal Amount => Variant.Season.DecFromDb(AmountValue);
[ForeignKey("Year, AvNr")] [ForeignKey("Year, AvNr")]
public virtual PaymentVar Variant { get; private set; } public virtual PaymentVar Variant { get; private set; } = null!;
[ForeignKey("Year, DId, DPNr")] [ForeignKey("Year, DId, DPNr")]
public virtual DeliveryPart DeliveryPart { get; private set; } public virtual DeliveryPart DeliveryPart { get; private set; } = null!;
} }
} }

View File

@ -36,9 +36,9 @@ namespace Elwig.Models.Entities {
} }
[ForeignKey("Year, AvNr")] [ForeignKey("Year, AvNr")]
public virtual PaymentVar Variant { get; private set; } public virtual PaymentVar Variant { get; private set; } = null!;
[ForeignKey("Year, DId, DPNr")] [ForeignKey("Year, DId, DPNr")]
public virtual DeliveryPart DeliveryPart { get; private set; } public virtual DeliveryPart DeliveryPart { get; private set; } = null!;
} }
} }

View File

@ -13,7 +13,6 @@ namespace Elwig.Models.Entities {
[Column("mgnr")] [Column("mgnr")]
public int MgNr { get; set; } public int MgNr { get; set; }
[Column("net_amount")] [Column("net_amount")]
public long NetAmountValue { get; set; } public long NetAmountValue { get; set; }
[NotMapped] [NotMapped]
@ -44,10 +43,10 @@ namespace Elwig.Models.Entities {
public decimal Amount => Variant.Season.DecFromDb(AmountValue); public decimal Amount => Variant.Season.DecFromDb(AmountValue);
[ForeignKey("Year, AvNr")] [ForeignKey("Year, AvNr")]
public virtual PaymentVar Variant { get; private set; } public virtual PaymentVar Variant { get; private set; } = null!;
[ForeignKey("MgNr")] [ForeignKey("MgNr")]
public virtual Member Member { get; private set; } public virtual Member Member { get; private set; } = null!;
[InverseProperty("Payment")] [InverseProperty("Payment")]
public virtual Credit? Credit { get; private set; } public virtual Credit? Credit { get; private set; }

View File

@ -13,10 +13,10 @@ namespace Elwig.Models.Entities {
public int AvNr { get; set; } public int AvNr { get; set; }
[Column("name")] [Column("name")]
public string Name { get; set; } public required string Name { get; set; }
[Column("date")] [Column("date")]
public string DateString { get; set; } public required string DateString { get; set; }
[NotMapped] [NotMapped]
public DateOnly Date { public DateOnly Date {
@ -43,18 +43,18 @@ namespace Elwig.Models.Entities {
public string? Comment { get; set; } public string? Comment { get; set; }
[Column("data")] [Column("data")]
public string Data { get; set; } public required string Data { get; set; }
[ForeignKey("Year")] [ForeignKey("Year")]
public virtual Season Season { get; private set; } public virtual Season Season { get; private set; } = null!;
[InverseProperty("Variant")] [InverseProperty("Variant")]
public virtual ISet<PaymentMember> MemberPayments { get; private set; } public virtual ISet<PaymentMember> MemberPayments { get; private set; } = null!;
[InverseProperty("Variant")] [InverseProperty("Variant")]
public virtual ISet<PaymentDeliveryPart> DeliveryPartPayments { get; private set; } public virtual ISet<PaymentDeliveryPart> DeliveryPartPayments { get; private set; } = null!;
[InverseProperty("Variant")] [InverseProperty("Variant")]
public virtual ISet<Credit> Credits { get; private set; } public virtual ISet<Credit> Credits { get; private set; } = null!;
} }
} }

View File

@ -8,10 +8,10 @@ namespace Elwig.Models.Entities {
public int CountryNum { get; private set; } public int CountryNum { get; private set; }
[Column("id")] [Column("id")]
public string Id { get; private set; } public string Id { get; private set; } = null!;
[ForeignKey("CountryNum")] [ForeignKey("CountryNum")]
public virtual Country Country { get; private set; } public virtual Country Country { get; private set; } = null!;
[ForeignKey("Id")] [ForeignKey("Id")]
public virtual AT_PlzDest? AtPlz { get; private set; } public virtual AT_PlzDest? AtPlz { get; private set; }

View File

@ -11,7 +11,7 @@ namespace Elwig.Models.Entities {
public int Year { get; set; } public int Year { get; set; }
[Column("currency")] [Column("currency")]
public string CurrencyCode { get; set; } public required string CurrencyCode { get; set; }
[Column("precision")] [Column("precision")]
public byte Precision { get; set; } public byte Precision { get; set; }
@ -98,16 +98,16 @@ namespace Elwig.Models.Entities {
} }
[ForeignKey("CurrencyCode")] [ForeignKey("CurrencyCode")]
public virtual Currency Currency { get; private set; } public virtual Currency Currency { get; private set; } = null!;
[InverseProperty("Season")] [InverseProperty("Season")]
public virtual ISet<Modifier> Modifiers { get; private set; } public virtual ISet<Modifier> Modifiers { get; private set; } = null!;
[InverseProperty("Season")] [InverseProperty("Season")]
public virtual ISet<PaymentVar> PaymentVariants { get; private set; } public virtual ISet<PaymentVar> PaymentVariants { get; private set; } = null!;
[InverseProperty("Season")] [InverseProperty("Season")]
public virtual ISet<Delivery> Deliveries { get; private set; } public virtual ISet<Delivery> Deliveries { get; private set; } = null!;
public decimal DecFromDb(long value) { public decimal DecFromDb(long value) {
return Utils.DecFromDb(value, Precision); return Utils.DecFromDb(value, Precision);

View File

@ -8,12 +8,12 @@ namespace Elwig.Models.Entities {
public int Gkz { get; private set; } public int Gkz { get; private set; }
[Column("hkid")] [Column("hkid")]
public string HkId { get; private set; } public string HkId { get; private set; } = null!;
[ForeignKey("Gkz")] [ForeignKey("Gkz")]
public virtual AT_Gem AtGem { get; private set; } public virtual AT_Gem AtGem { get; private set; } = null!;
[ForeignKey("HkId")] [ForeignKey("HkId")]
public virtual WineOrigin Origin { get; private set; } public virtual WineOrigin Origin { get; private set; } = null!;
} }
} }

View File

@ -9,9 +9,9 @@ namespace Elwig.Models.Entities {
public int GlNr { get; private set; } public int GlNr { get; private set; }
[Column("name")] [Column("name")]
public string Name { get; private set; } public string Name { get; private set; } = null!;
[InverseProperty("Gl")] [InverseProperty("Gl")]
public virtual ISet<WbKg> Kgs { get; private set; } public virtual ISet<WbKg> Kgs { get; private set; } = null!;
} }
} }

View File

@ -12,16 +12,19 @@ namespace Elwig.Models.Entities {
public int? GlNr { get; set; } public int? GlNr { get; set; }
[ForeignKey("KgNr")] [ForeignKey("KgNr")]
public virtual AT_Kg AtKg { get; private set; } public virtual AT_Kg AtKg { get; private set; } = null!;
[ForeignKey("GlNr")] [ForeignKey("GlNr")]
public virtual WbGl Gl { get; private set; } public virtual WbGl Gl { get; private set; } = null!;
[InverseProperty("Kg")] [InverseProperty("Kg")]
public virtual ISet<WbRd> Rds { get; private set; } public virtual ISet<WbRd> Rds { get; private set; } = null!;
[InverseProperty("DefaultWbKg")]
public virtual ISet<Member> Members { get; private set; } = null!;
[NotMapped] [NotMapped]
public WbGem Gem => AtKg.Gem.WbGem; public WbGem Gem => AtKg.Gem.WbGem!;
[NotMapped] [NotMapped]
public WineOrigin Origin => Gem.Origin; public WineOrigin Origin => Gem.Origin;

View File

@ -11,9 +11,9 @@ namespace Elwig.Models.Entities {
public int RdNr { get; set; } public int RdNr { get; set; }
[Column("name")] [Column("name")]
public string Name { get; set; } public required string Name { get; set; }
[ForeignKey("KgNr")] [ForeignKey("KgNr")]
public virtual WbKg Kg { get; private set; } public virtual WbKg Kg { get; private set; } = null!;
} }
} }

View File

@ -6,10 +6,10 @@ namespace Elwig.Models.Entities {
[Table("wine_attribute"), PrimaryKey("AttrId"), Index("Name", IsUnique = true)] [Table("wine_attribute"), PrimaryKey("AttrId"), Index("Name", IsUnique = true)]
public class WineAttr { public class WineAttr {
[Column("attrid")] [Column("attrid")]
public string AttrId { get; set; } public required string AttrId { get; set; }
[Column("name")] [Column("name")]
public string Name { get; set; } public required string Name { get; set; }
[Column("active")] [Column("active")]
public bool IsActive { get; set; } public bool IsActive { get; set; }
@ -23,13 +23,6 @@ namespace Elwig.Models.Entities {
[Column("fill_lower")] [Column("fill_lower")]
public int FillLower { get; set; } public int FillLower { get; set; }
public WineAttr() { }
public WineAttr(string attrId, string name) {
AttrId = attrId;
Name = name;
}
public override string ToString() { public override string ToString() {
return Name; return Name;
} }

View File

@ -6,12 +6,16 @@ namespace Elwig.Models.Entities {
[Table("wine_cultivation"), PrimaryKey("CultId"), Index("Name", IsUnique = true)] [Table("wine_cultivation"), PrimaryKey("CultId"), Index("Name", IsUnique = true)]
public class WineCult { public class WineCult {
[Column("cultid")] [Column("cultid")]
public string CultId { get; set; } public required string CultId { get; set; }
[Column("name")] [Column("name")]
public string Name { get; set; } public required string Name { get; set; }
[Column("description")] [Column("description")]
public string? Description { get; set; } public string? Description { get; set; }
public override string ToString() {
return Name;
}
} }
} }

View File

@ -9,7 +9,7 @@ namespace Elwig.Models.Entities {
[Table("wine_origin"), PrimaryKey("HkId"), Index("Name", IsUnique = true)] [Table("wine_origin"), PrimaryKey("HkId"), Index("Name", IsUnique = true)]
public class WineOrigin { public class WineOrigin {
[Column("hkid")] [Column("hkid")]
public string HkId { get; private set; } public string HkId { get; private set; } = null!;
[Column("parent_hkid")] [Column("parent_hkid")]
public string? ParentHkId { get; private set; } public string? ParentHkId { get; private set; }
@ -18,16 +18,16 @@ namespace Elwig.Models.Entities {
public virtual WineOrigin? Parent { get; private set; } public virtual WineOrigin? Parent { get; private set; }
[Column("name")] [Column("name")]
public string Name { get; private set; } public string Name { get; private set; } = null!;
[Column("blnr")] [Column("blnr")]
public int? BlNr { get; private set; } public int? BlNr { get; private set; }
[InverseProperty("Origin")] [InverseProperty("Origin")]
public virtual ISet<WbGem> Gems { get; private set; } public virtual ISet<WbGem> Gems { get; private set; } = null!;
[InverseProperty("Parent")] [InverseProperty("Parent")]
public virtual ISet<WineOrigin> Children { get; private set; } public virtual ISet<WineOrigin> Children { get; private set; } = null!;
public int Level => (Parent?.Level + 1) ?? 0; public int Level => (Parent?.Level + 1) ?? 0;

View File

@ -7,7 +7,7 @@ namespace Elwig.Models.Entities {
[Table("wine_quality_level"), PrimaryKey("QualId")] [Table("wine_quality_level"), PrimaryKey("QualId")]
public class WineQualLevel : IEquatable<WineQualLevel> { public class WineQualLevel : IEquatable<WineQualLevel> {
[Column("qualid")] [Column("qualid")]
public string QualId { get; private set; } public string QualId { get; private set; } = null!;
[Column("origin_level")] [Column("origin_level")]
public int? OriginLevel { get; private set; } public int? OriginLevel { get; private set; }
@ -22,7 +22,7 @@ namespace Elwig.Models.Entities {
public double? MinOe => MinKmw != null ? Utils.KmwToOe((double)MinKmw) : null; public double? MinOe => MinKmw != null ? Utils.KmwToOe((double)MinKmw) : null;
[Column("name")] [Column("name")]
public string Name { get; private set; } public string Name { get; private set; } = null!;
public string MinKmwStr => (MinKmw == null) ? "" : $"(mind. {MinKmw:#.0}°)"; public string MinKmwStr => (MinKmw == null) ? "" : $"(mind. {MinKmw:#.0}°)";

View File

@ -5,13 +5,13 @@ namespace Elwig.Models.Entities {
[Table("wine_variety"), PrimaryKey("SortId")] [Table("wine_variety"), PrimaryKey("SortId")]
public class WineVar { public class WineVar {
[Column("sortid")] [Column("sortid")]
public string SortId { get; private set; } public string SortId { get; private set; } = null!;
[Column("type")] [Column("type")]
public string Type { get; private set; } public string Type { get; private set; } = null!;
[Column("name")] [Column("name")]
public string Name { get; private set; } public string Name { get; private set; } = null!;
[Column("comment")] [Column("comment")]
public string? Comment { get; private set; } public string? Comment { get; private set; }

View File

@ -12,6 +12,8 @@
"consider_contract_penalties": {"type": "boolean"}, "consider_contract_penalties": {"type": "boolean"},
"consider_total_penalty": {"type": "boolean"}, "consider_total_penalty": {"type": "boolean"},
"consider_auto_business_shares": {"type": "boolean"}, "consider_auto_business_shares": {"type": "boolean"},
"net_weight_modifier": {"type": "number"},
"gross_weight_modifier": {"type": "number"},
"payment": {"$ref": "#/definitions/payment_1"}, "payment": {"$ref": "#/definitions/payment_1"},
"quality": {"$ref": "#/definitions/quality_1"}, "quality": {"$ref": "#/definitions/quality_1"},
"curves": { "curves": {
@ -23,6 +25,7 @@
"required": ["AuszahlungSorten", "Kurven"], "required": ["AuszahlungSorten", "Kurven"],
"properties": { "properties": {
"mode": {"enum": ["wgmaster"]}, "mode": {"enum": ["wgmaster"]},
"Rebelzuschlag": {"type": "number"},
"AuszahlungSorten": {"$ref": "#/definitions/payment_1"}, "AuszahlungSorten": {"$ref": "#/definitions/payment_1"},
"AuszahlungSortenQualitätsstufe": {"$ref": "#/definitions/quality_1"}, "AuszahlungSortenQualitätsstufe": {"$ref": "#/definitions/quality_1"},
"Kurven": { "Kurven": {
@ -43,7 +46,7 @@
} }
}, },
"patternProperties": { "patternProperties": {
"^([A-Z]{2})?(\/[A-Z]*)?$": { "^([A-Z]{2})?(\/[A-Z]*)?(-[A-Z][A-Z0-9]*)?$": {
"type": ["number", "string"], "type": ["number", "string"],
"pattern": "^curve:[0-9]+$" "pattern": "^curve:[0-9]+$"
} }
@ -64,7 +67,7 @@
} }
}, },
"patternProperties": { "patternProperties": {
"^([A-Z]{2})?(\/[A-Z]*)?$": { "^([A-Z]{2})?(\/[A-Z]*)?(-[A-Z][A-Z0-9]*)?$": {
"type": ["number", "string"], "type": ["number", "string"],
"pattern": "^curve:[0-9]+$" "pattern": "^curve:[0-9]+$"
} }

View File

@ -1,6 +1,6 @@
-- schema version 4 to 5 -- schema version 4 to 5
CREATE TABLE _area_commitment_type ( CREATE TABLE area_commitment_type_new (
vtrgid TEXT NOT NULL CHECK (vtrgid = sortid || COALESCE(attrid, '') || disc), vtrgid TEXT NOT NULL CHECK (vtrgid = sortid || COALESCE(attrid, '') || disc),
sortid TEXT NOT NULL, sortid TEXT NOT NULL,
attrid TEXT, attrid TEXT,
@ -20,13 +20,15 @@ CREATE TABLE _area_commitment_type (
ON DELETE RESTRICT ON DELETE RESTRICT
) STRICT; ) STRICT;
INSERT INTO _area_commitment_type (vtrgid, sortid, attrid, disc, min_kg_per_ha, max_kg_per_ha, penalty_amount) INSERT INTO area_commitment_type_new (vtrgid, sortid, attrid, disc, min_kg_per_ha, max_kg_per_ha, penalty_amount)
SELECT vtrgid, sortid, attrid_1, disc, min_kg_per_ha, max_kg_per_ha, penalty_amount FROM area_commitment_type; SELECT vtrgid, sortid, attrid_1, disc, min_kg_per_ha, max_kg_per_ha, penalty_amount FROM area_commitment_type;
PRAGMA foreign_keys = OFF;
PRAGMA writable_schema = ON; PRAGMA writable_schema = ON;
DROP TABLE area_commitment_type; DROP TABLE area_commitment_type;
ALTER TABLE _area_commitment_type RENAME TO area_commitment_type; ALTER TABLE area_commitment_type_new RENAME TO area_commitment_type;
PRAGMA writable_schema = OFF; PRAGMA writable_schema = OFF;
PRAGMA foreign_keys = ON;
ALTER TABLE delivery_part ADD COLUMN attrid TEXT DEFAULT NULL REFERENCES wine_attribute (attrid) ALTER TABLE delivery_part ADD COLUMN attrid TEXT DEFAULT NULL REFERENCES wine_attribute (attrid)
ON UPDATE CASCADE ON UPDATE CASCADE

View File

@ -22,10 +22,12 @@ CREATE TABLE payment_delivery_part_new (
INSERT INTO payment_delivery_part_new (year, did, dpnr, avnr, net_amount, mod_abs, mod_rel) INSERT INTO payment_delivery_part_new (year, did, dpnr, avnr, net_amount, mod_abs, mod_rel)
SELECT year, did, dpnr, avnr, net_amount, mod_abs, mod_rel SELECT year, did, dpnr, avnr, net_amount, mod_abs, mod_rel
FROM payment_delivery_part; FROM payment_delivery_part;
PRAGMA foreign_keys = OFF;
PRAGMA writable_schema = ON; PRAGMA writable_schema = ON;
DROP TABLE payment_delivery_part; DROP TABLE payment_delivery_part;
ALTER TABLE payment_delivery_part_new RENAME TO payment_delivery_part; ALTER TABLE payment_delivery_part_new RENAME TO payment_delivery_part;
PRAGMA writable_schema = OFF; PRAGMA writable_schema = OFF;
PRAGMA foreign_keys = ON;
DROP TRIGGER IF EXISTS t_payment_delivery_part_i; DROP TRIGGER IF EXISTS t_payment_delivery_part_i;
CREATE TRIGGER t_payment_delivery_part_i CREATE TRIGGER t_payment_delivery_part_i

View File

@ -0,0 +1,12 @@
-- schema version 16 to 17
CREATE VIEW v_virtual_season AS
SELECT year, max_kg_per_ha
FROM season
UNION
SELECT strftime('%Y', date()) + 0, (SELECT max_kg_per_ha FROM season ORDER BY year DESC LIMIT 1);
PRAGMA writable_schema = ON;
UPDATE sqlite_schema SET sql = REPLACE(sql, 'season s', 'v_virtual_season s')
WHERE type = 'view' AND name = 'v_area_commitment_bucket_strict';
PRAGMA writable_schema = OFF;

View File

@ -0,0 +1,97 @@
-- schema version 17 to 18
ALTER TABLE delivery_part ADD COLUMN cultid TEXT DEFAULT NULL;
PRAGMA writable_schema = ON;
UPDATE sqlite_schema SET sql = REPLACE(sql, CHAR(10) ||
') STRICT',
',' || CHAR(10) ||
' CONSTRAINT fk_delivery_part_wine_cultivation FOREIGN KEY (cultid) REFERENCES wine_cultivation (cultid)' || CHAR(10) ||
' ON UPDATE CASCADE' || CHAR(10) ||
' ON DELETE RESTRICT' || CHAR(10) ||
') STRICT')
WHERE type = 'table' AND name = 'delivery_part';
UPDATE sqlite_schema SET sql = REPLACE(sql, 'gerebelt ', 'net_weight')
WHERE type = 'table' AND name = 'delivery_part';
UPDATE sqlite_schema SET sql = REPLACE(sql, 'CHECK (cultid REGEXP ''^[A-Z]+$'')', 'CHECK (cultid REGEXP ''^[A-Z][A-Z0-9]*$'')')
WHERE type = 'table' AND name = 'wine_cultivation';
UPDATE sqlite_schema SET sql = REPLACE(sql, 'cultid TEXT NOT NULL', 'cultid TEXT DEFAULT NULL')
WHERE type = 'table' AND name = 'area_commitment';
DROP VIEW v_delivery;
CREATE VIEW v_delivery AS
SELECT p.year, p.did, p.dpnr,
d.date, d.time, d.zwstid, d.lnr, d.lsnr,
m.mgnr, m.family_name, m.given_name,
p.sortid, a.attrid, p.cultid,
p.weight, p.kmw, ROUND(p.kmw * (4.54 + 0.022 * p.kmw), 0) AS oe, p.qualid,
p.hkid, p.kgnr, p.rdnr,
p.net_weight, p.gebunden,
p.qualid IN (SELECT l.qualid FROM wine_quality_level l WHERE NOT l.predicate AND (p.kmw >= l.min_kmw OR l.min_kmw IS NULL) ORDER BY l.min_kmw DESC LIMIT 1,100) AS abgewertet,
p.qualid NOT IN ('WEI', 'RSW', 'LDW') AS min_quw,
IIF(a.strict, COALESCE(a.fill_lower, 0), 0) AS attribute_prio,
GROUP_CONCAT(o.modid) AS modifiers,
d.comment, p.comment AS part_comment
FROM delivery_part p
JOIN delivery d ON (d.year, d.did) = (p.year, p.did)
JOIN member m ON m.mgnr = d.mgnr
LEFT JOIN wine_attribute a ON a.attrid = p.attrid
LEFT JOIN delivery_part_modifier o ON (o.year, o.did, o.dpnr) = (p.year, p.did, p.dpnr)
GROUP BY p.year, p.did, p.dpnr
ORDER BY p.year, p.did, p.dpnr, o.modid;
CREATE VIEW v_wine_attribute AS
SELECT a.attrid, name, active, max_kg_per_ha, strict, fill_lower,
COUNT(t.attrid) > 0 AS area_com
FROM wine_attribute a
LEFT JOIN area_commitment_type t ON t.attrid = a.attrid
GROUP BY a.attrid;
DROP VIEW v_delivery_bucket_strict;
CREATE VIEW v_delivery_bucket_strict AS
SELECT year, mgnr,
sortid || IIF(min_quw OR NOT COALESCE(area_com, TRUE), COALESCE(a.attrid, ''), '_') AS bucket,
sortid, IIF(min_quw OR NOT COALESCE(area_com, TRUE), a.attrid, NULL) AS attrid,
SUM(weight) AS weight,
min_quw OR NOT COALESCE(area_com, TRUE) AS valid
FROM v_delivery d
LEFT JOIN v_wine_attribute a ON a.attrid = d.attrid
GROUP BY year, mgnr, bucket
ORDER BY year, mgnr, bucket;
DROP VIEW v_delivery_bucket;
CREATE VIEW v_delivery_bucket AS
SELECT year, mgnr, bucket, weight
FROM v_delivery_bucket_strict
WHERE attrid IS NOT NULL OR NOT valid
UNION ALL
SELECT b.year, b.mgnr, b.sortid,
SUM(b.weight) AS weight
FROM v_delivery_bucket_strict b
LEFT JOIN wine_attribute a ON a.attrid = b.attrid
WHERE valid AND (a.strict IS NULL OR a.strict = FALSE)
GROUP BY b.year, b.mgnr, b.sortid
ORDER BY year, mgnr, bucket;
PRAGMA schema_version = 1701;
PRAGMA writable_schema = OFF;
----------------------------------------------------------------
UPDATE area_commitment SET cultid = NULL WHERE cultid = 'N';
DELETE FROM wine_cultivation WHERE cultid = 'N';
UPDATE wine_cultivation SET cultid = 'B', name = 'Bio', description = 'AT-BIO-302' WHERE cultid = 'BIO';
UPDATE wine_cultivation SET description = 'Kontrollierte Integrierte Produktion' WHERE cultid = 'KIP';
UPDATE area_commitment SET cultid = 'B', vtrgid = SUBSTR(vtrgid, 1, 2) WHERE vtrgid LIKE '__B';
UPDATE area_commitment SET cultid = 'B' WHERE vtrgid LIKE '__HU';
DELETE FROM area_commitment_type WHERE attrid = 'B';
UPDATE delivery_part SET cultid = 'B', attrid = NULL WHERE attrid = 'B';
UPDATE delivery_part SET cultid = 'B' WHERE attrid = 'HU';
DELETE FROM wine_attribute WHERE attrid = 'B';
UPDATE wine_attribute SET name = 'Huber' WHERE attrid = 'HU';
UPDATE wine_attribute SET max_kg_per_ha = NULL WHERE max_kg_per_ha = 10000;
UPDATE payment_variant SET data = REPLACE(REPLACE(REPLACE(data, '/B', '-B'), '/"', '"'), '/-', '-');

View File

@ -184,7 +184,7 @@
<Label Content="Parzelle(n):" Margin="10,70,0,0" Grid.Column="0"/> <Label Content="Parzelle(n):" Margin="10,70,0,0" Grid.Column="0"/>
<TextBox x:Name="GstNrInput" Margin="0,70,10,0" Grid.Column="1" HorizontalAlignment="Stretch" <TextBox x:Name="GstNrInput" Margin="0,70,10,0" Grid.Column="1" HorizontalAlignment="Stretch"
TextChanged="GstNrInput_TextChanged" LostFocus="GstNrInput_LostFocus"/> TextChanged="TextBox_TextChanged"/>
<Label Content="Fläche:" Margin="10,100,0,0" Grid.Column="0"/> <Label Content="Fläche:" Margin="10,100,0,0" Grid.Column="0"/>
<ctrl:UnitTextBox x:Name="AreaInput" Unit="m²" TextChanged="IntegerInput_TextChanged" <ctrl:UnitTextBox x:Name="AreaInput" Unit="m²" TextChanged="IntegerInput_TextChanged"

View File

@ -16,21 +16,21 @@ namespace Elwig.Windows {
public int MgNr => Member.MgNr; public int MgNr => Member.MgNr;
private readonly Member Member; private readonly Member Member;
private List<string> TextFilter = new(); private List<string> TextFilter = [];
public AreaComAdminWindow(int mgnr) { public AreaComAdminWindow(int mgnr) {
InitializeComponent(); InitializeComponent();
Member = Context.Members.Find(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value"); Member = Context.Members.Find(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
Title = $"Flächenbindungen - {Member.AdministrativeName} - Elwig"; Title = $"Flächenbindungen - {Member.AdministrativeName} - Elwig";
ExemptInputs = new Control[] { ExemptInputs = [
MgNrInput, AreaCommitmentList, NewAreaCommitmentButton, MgNrInput, AreaCommitmentList, NewAreaCommitmentButton,
EditAreaCommitmentButton, DeleteAreaCommitmentButton, AreaCommitmentSaveButton, EditAreaCommitmentButton, DeleteAreaCommitmentButton, AreaCommitmentSaveButton,
AreaCommitmentResetButton, AreaCommitmentCancelButton, SearchInput, ActiveAreaCommitmentInput AreaCommitmentResetButton, AreaCommitmentCancelButton, SearchInput, ActiveAreaCommitmentInput
}; ];
RequiredInputs = new Control[] { RequiredInputs = [
FbNrInput, YearFromInput, KgInput, RdInput, FbNrInput, YearFromInput, KgInput, RdInput,
GstNrInput, AreaInput.TextBox, AreaComTypeInput, WineCultivationInput GstNrInput, AreaInput.TextBox, AreaComTypeInput, WineCultivationInput
}; ];
} }
private void Window_Loaded(object sender, RoutedEventArgs e) { private void Window_Loaded(object sender, RoutedEventArgs e) {
@ -76,10 +76,10 @@ namespace Elwig.Windows {
} }
private async Task<(List<string>, IQueryable<AreaCom>, List<string>)> GetFilters() { private async Task<(List<string>, IQueryable<AreaCom>, List<string>)> GetFilters() {
List<string> filterNames = new(); List<string> filterNames = [];
IQueryable<AreaCom> areaComQuery = Context.AreaCommitments.Where(a => a.MgNr == Member.MgNr).OrderBy(a => a.FbNr); IQueryable<AreaCom> areaComQuery = Context.AreaCommitments.Where(a => a.MgNr == Member.MgNr).OrderBy(a => a.FbNr);
if (ActiveAreaCommitmentInput.IsChecked == true) { if (ActiveAreaCommitmentInput.IsChecked == true) {
areaComQuery = areaComQuery.Where(a => (a.YearFrom <= Utils.CurrentNextSeason) && (a.YearTo == null || a.YearTo >= Utils.CurrentNextSeason)); areaComQuery = areaComQuery.Where(a => (a.YearFrom <= Utils.CurrentYear) && (a.YearTo == null || a.YearTo >= Utils.CurrentYear));
filterNames.Add("aktiv"); filterNames.Add("aktiv");
} }
@ -151,12 +151,12 @@ namespace Elwig.Windows {
YearToInput.Text = a.YearTo.ToString(); YearToInput.Text = a.YearTo.ToString();
KgInput.SelectedItem = a.Kg.AtKg; KgInput.SelectedItem = a.Kg.AtKg;
RdInput.SelectedItem = a.Rd; RdInput.SelectedItem = a.Rd ?? RdInput.Items[0];
GstNrInput.Text = a.GstNr; GstNrInput.Text = a.GstNr;
AreaInput.Text = a.Area.ToString(); AreaInput.Text = a.Area.ToString();
AreaComTypeInput.SelectedItem = a.AreaComType; AreaComTypeInput.SelectedItem = a.AreaComType;
WineCultivationInput.SelectedItem = a.WineCult; WineCultivationInput.SelectedItem = a.WineCult ?? WineCultivationInput.Items[0];
CommentInput.Text = a.Comment; CommentInput.Text = a.Comment;
@ -170,6 +170,7 @@ namespace Elwig.Windows {
FbNrInput.Text = (await Context.NextFbNr()).ToString(); FbNrInput.Text = (await Context.NextFbNr()).ToString();
MgNrInput.Text = Member.MgNr.ToString(); MgNrInput.Text = Member.MgNr.ToString();
YearFromInput.Text = DateTime.Now.Year.ToString(); YearFromInput.Text = DateTime.Now.Year.ToString();
WineCultivationInput.SelectedIndex = 0;
SetDefaultValue(FbNrInput); SetDefaultValue(FbNrInput);
ValidateRequiredInputs(); ValidateRequiredInputs();
@ -179,7 +180,9 @@ namespace Elwig.Windows {
await base.OnRenewContext(); await base.OnRenewContext();
ControlUtils.RenewItemsSource(KgInput, await Context.WbKgs.Select(k => k.AtKg).OrderBy(k => k.Name).ToListAsync(), i => (i as AT_Kg)?.KgNr); ControlUtils.RenewItemsSource(KgInput, await Context.WbKgs.Select(k => k.AtKg).OrderBy(k => k.Name).ToListAsync(), i => (i as AT_Kg)?.KgNr);
ControlUtils.RenewItemsSource(AreaComTypeInput, await Context.AreaCommitmentTypes.OrderBy(v => v.VtrgId).ToListAsync(), i => (i as AreaComType)?.VtrgId); ControlUtils.RenewItemsSource(AreaComTypeInput, await Context.AreaCommitmentTypes.OrderBy(v => v.VtrgId).ToListAsync(), i => (i as AreaComType)?.VtrgId);
ControlUtils.RenewItemsSource(WineCultivationInput, await Context.WineCultivations.OrderBy(c => c.Name).ToListAsync(), i => (i as WineCult)?.CultId); var cultList = await Context.WineCultivations.OrderBy(c => c.Name).Cast<object>().ToListAsync();
cultList.Insert(0, new NullItem());
ControlUtils.RenewItemsSource(WineCultivationInput, cultList, i => (i as WineCult)?.CultId, null, ControlUtils.RenewSourceDefault.First);
await RefreshAreaCommitmentList(); await RefreshAreaCommitmentList();
} }
@ -230,7 +233,7 @@ namespace Elwig.Windows {
a.RdNr = RdInput.SelectedItem.GetType() == typeof(NullItem) ? null : ((WbRd)RdInput.SelectedItem).RdNr; a.RdNr = RdInput.SelectedItem.GetType() == typeof(NullItem) ? null : ((WbRd)RdInput.SelectedItem).RdNr;
a.GstNr = GstNrInput.Text; a.GstNr = GstNrInput.Text;
a.Area = int.Parse(AreaInput.Text); a.Area = int.Parse(AreaInput.Text);
a.VtrgId = (AreaComTypeInput.SelectedItem as AreaComType)?.VtrgId; a.VtrgId = (AreaComTypeInput.SelectedItem as AreaComType)!.VtrgId;
a.CultId = (WineCultivationInput.SelectedItem as WineCult)?.CultId; a.CultId = (WineCultivationInput.SelectedItem as WineCult)?.CultId;
a.Comment = (CommentInput.Text == "") ? null : CommentInput.Text; a.Comment = (CommentInput.Text == "") ? null : CommentInput.Text;
@ -275,7 +278,7 @@ namespace Elwig.Windows {
MessageBox.Show(str, "Flächenbindung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show(str, "Flächenbindung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
} }
return a; return a!;
} }
private async void AreaCommitmentSaveButton_Click(object sender, RoutedEventArgs evt) { private async void AreaCommitmentSaveButton_Click(object sender, RoutedEventArgs evt) {
@ -287,7 +290,7 @@ namespace Elwig.Windows {
ShowAreaCommitmentNewEditDeleteButtons(); ShowAreaCommitmentNewEditDeleteButtons();
LockInputs(); LockInputs();
UnlockSearchInputs(); UnlockSearchInputs();
await RefreshAreaCommitmentList(); await App.HintContextChange();
AreaCommitmentList.SelectedItem = a; AreaCommitmentList.SelectedItem = a;
} }
@ -438,13 +441,5 @@ namespace Elwig.Windows {
private void FbNrInput_LostFocus(object sender, RoutedEventArgs evt) { private void FbNrInput_LostFocus(object sender, RoutedEventArgs evt) {
InputLostFocus((TextBox)sender, Validator.CheckFbNr); InputLostFocus((TextBox)sender, Validator.CheckFbNr);
} }
private void GstNrInput_TextChanged(object sender, RoutedEventArgs evt) {
InputTextChanged((TextBox)sender, Validator.CheckGstNr);
}
private void GstNrInput_LostFocus(object sender, RoutedEventArgs evt) {
InputLostFocus((TextBox)sender, Validator.CheckGstNr);
}
} }
} }

View File

@ -246,6 +246,7 @@
<Label Content="Max. Ertrag:" Margin="10,10,0,10"/> <Label Content="Max. Ertrag:" Margin="10,10,0,10"/>
<ctrl:UnitTextBox x:Name="WineAttributeMaxKgPerHaInput" Unit="kg/ha" TextChanged="WineAttributeMaxKgPerHaInput_TextChanged" <ctrl:UnitTextBox x:Name="WineAttributeMaxKgPerHaInput" Unit="kg/ha" TextChanged="WineAttributeMaxKgPerHaInput_TextChanged"
Grid.Column="1" Width="80" Margin="84,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/> Grid.Column="1" Width="80" Margin="84,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Label Content="(wenn abweichend vom max. Ertrag der Saison)" Margin="170,10,0,10"/>
<CheckBox x:Name="WineAttributeStrictInput" Content="Strikte Trennung zu Flächenbindung ohne Attribut" <CheckBox x:Name="WineAttributeStrictInput" Content="Strikte Trennung zu Flächenbindung ohne Attribut"
Margin="10,50,10,10" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,50,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"

View File

@ -52,13 +52,13 @@ namespace Elwig.Windows {
var year = (SeasonList.SelectedItem as Season)?.Year; var year = (SeasonList.SelectedItem as Season)?.Year;
foreach (var (modid, _) in _mods.Where(m => m.Value == null)) { foreach (var (modid, _) in _mods.Where(m => m.Value == null)) {
Context.Remove(Context.Modifiers.Find(year, modid)); Context.Remove(Context.Modifiers.Find(year, modid)!);
} }
foreach (var (mod, old) in _modIds) { foreach (var (mod, old) in _modIds) {
mod.ModId = old; mod.ModId = old;
} }
foreach (var (old, modid) in _mods.Where(m => m.Value != null)) { foreach (var (old, modid) in _mods.Where(m => m.Value != null)) {
Context.Update(Context.Modifiers.Find(year, old)); Context.Update(Context.Modifiers.Find(year, old)!);
} }
await Context.SaveChangesAsync(); await Context.SaveChangesAsync();
@ -103,7 +103,9 @@ namespace Elwig.Windows {
_modChanged = true; _modChanged = true;
var idx = (SeasonModifierList.SelectedIndex != -1) ? SeasonModifierList.SelectedIndex + 1 : _modList.Count; var idx = (SeasonModifierList.SelectedIndex != -1) ? SeasonModifierList.SelectedIndex + 1 : _modList.Count;
var item = new Modifier { var item = new Modifier {
Year = s.Year Year = s.Year,
ModId = "",
Name = "",
}; };
_modList.Insert(idx, item); _modList.Insert(idx, item);
SeasonModifierList.SelectedIndex = idx; SeasonModifierList.SelectedIndex = idx;

View File

@ -69,7 +69,8 @@
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Variety.Name}" Width="150"/> <TextBlock Text="{Binding Variety.Name}" Width="150"/>
<TextBlock Text="{Binding Variety.Type}" Width="30"/> <TextBlock Text="{Binding Variety.Type}" Width="30"/>
<TextBlock Text="{Binding Attribute.Name}" Width="120"/> <TextBlock Text="{Binding Attribute.Name}" Width="80"/>
<TextBlock Text="{Binding Cultivation.Name}" Width="80"/>
<TextBlock Text="{Binding AssignedGraphId}" Width="30"/> <TextBlock Text="{Binding AssignedGraphId}" Width="30"/>
<TextBlock Text="{Binding AssignedAbgewGraphId}" Width="30"/> <TextBlock Text="{Binding AssignedAbgewGraphId}" Width="30"/>
</StackPanel> </StackPanel>

View File

@ -14,6 +14,8 @@ using ScottPlot.Plottables;
using ScottPlot; using ScottPlot;
using Xceed.Wpf.Toolkit.Primitives; using Xceed.Wpf.Toolkit.Primitives;
using ScottPlot.Control; using ScottPlot.Control;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
namespace Elwig.Windows { namespace Elwig.Windows {
public partial class ChartWindow : ContextWindow { public partial class ChartWindow : ContextWindow {
@ -101,16 +103,30 @@ namespace Elwig.Windows {
PaymentVar = await Context.PaymentVariants.FindAsync(Year, AvNr) ?? throw new ArgumentException("PaymentVar not found"); PaymentVar = await Context.PaymentVariants.FindAsync(Year, AvNr) ?? throw new ArgumentException("PaymentVar not found");
Season = await Context.Seasons.FindAsync(Year) ?? throw new ArgumentException("Season not found"); Season = await Context.Seasons.FindAsync(Year) ?? throw new ArgumentException("Season not found");
try {
var data = EditBillingData.FromJson(PaymentVar.Data, Utils.GetVaributes(Context, Year)); var data = EditBillingData.FromJson(PaymentVar.Data, Utils.GetVaributes(Context, Year));
var paymentEntries = data.GetPaymentGraphEntries(Context, Season); var paymentEntries = data.GetPaymentGraphEntries(Context, Season);
GraphEntries = [ GraphEntries = [
..paymentEntries, ..paymentEntries,
..data.GetQualityGraphEntries(Context, Season, paymentEntries.Any() ? paymentEntries.Max(e => e.Id) : 0) ..data.GetQualityGraphEntries(Context, Season, paymentEntries.Any() ? paymentEntries.Max(e => e.Id) : 0)
]; ];
} catch (KeyNotFoundException ex) {
var key = ex.Message.Split('\'')[1].Split('\'')[0];
MessageBox.Show($"Fehler beim Laden der Auszahlungsvariante:\n\n" +
$"Mit unbekanntem Attribut '{key}' kann nicht umgegangen werden.", "Fehler",
MessageBoxButton.OK, MessageBoxImage.Error);
} catch (ArgumentException) {
MessageBox.Show($"Fehler beim Laden der Auszahlungsvariante:\n\n" +
$"Die Daten der Auszahlungsvariante entsprechen nicht dem benötigtem Format.", "Fehler",
MessageBoxButton.OK, MessageBoxImage.Error);
} catch (Exception ex) {
MessageBox.Show("Fehler beim Laden der Auszahlungsvariante:\n\n" + ex.Message, "Fehler",
MessageBoxButton.OK, MessageBoxImage.Error);
}
Vaributes = Utils.GetVaributeList(Context, Year); Vaributes = Utils.GetVaributeList(Context, Year);
GraphEntries.ForEach(e => { GraphEntries.ForEach(e => {
e.Vaributes.ForEach(v => { e.Vaributes.ForEach(v => {
var found = Vaributes.Find(a => a.Attribute?.AttrId == v.Attribute?.AttrId && a.Variety?.SortId == v.Variety?.SortId); var found = Vaributes.Find(a => a.Variety?.SortId == v.Variety?.SortId && a.Attribute?.AttrId == v.Attribute?.AttrId && a.Cultivation?.CultId == v.Cultivation?.CultId);
if (found == null) return; if (found == null) return;
if (e.Abgewertet) { if (e.Abgewertet) {
found.AssignedAbgewGraphId = e.Id; found.AssignedAbgewGraphId = e.Id;
@ -628,7 +644,7 @@ namespace Elwig.Windows {
private async void SaveButton_Click(object sender, RoutedEventArgs e) { private async void SaveButton_Click(object sender, RoutedEventArgs e) {
var origData = BillingData.FromJson(PaymentVar.Data); var origData = BillingData.FromJson(PaymentVar.Data);
var data = BillingData.FromGraphEntries(GraphEntries, origData, Utils.GetVaributes(Context, Year, withSlash: true), var data = BillingData.FromGraphEntries(GraphEntries, origData, Utils.GetVaributes(Context, Year),
AllVaributesAssigned, AllVaributesAssignedAbgew); AllVaributesAssigned, AllVaributesAssignedAbgew);
EntityEntry<PaymentVar>? tr = null; EntityEntry<PaymentVar>? tr = null;

View File

@ -39,14 +39,8 @@ namespace Elwig.Windows {
await OnRenewContext(); await OnRenewContext();
} }
protected override void OnClosed(EventArgs evt) {
base.OnClosed(evt);
Context.Dispose();
}
protected async Task RenewContext() { protected async Task RenewContext() {
if (!_renewPending) return; if (!_renewPending) return;
Context.Dispose();
Context = new(); Context = new();
await OnRenewContext(); await OnRenewContext();
_renewPending = false; _renewPending = false;

View File

@ -60,13 +60,13 @@
<MenuItem x:Name="Menu_Print_PrintDeliveryNote" Header="Lieferschein drucken" IsEnabled="False" <MenuItem x:Name="Menu_Print_PrintDeliveryNote" Header="Lieferschein drucken" IsEnabled="False"
Click="Menu_Print_PrintDeliveryNote_Click"/> Click="Menu_Print_PrintDeliveryNote_Click"/>
<MenuItem x:Name="Menu_Print_DeliveryJournal" Header="Lieferjournal"> <MenuItem x:Name="Menu_Print_DeliveryJournal" Header="Lieferjournal">
<MenuItem x:Name="Menu_Print_DeliveryJournal_ShowToday" Header="von heute anzeigen" IsEnabled="False" Tag="Print" <MenuItem x:Name="Menu_Print_DeliveryJournal_ShowToday" Header="von heute anzeigen"
Click="Menu_Print_DeliveryJournal_ShowToday_Click"/> Click="Menu_Print_DeliveryJournal_ShowToday_Click"/>
<MenuItem x:Name="Menu_Print_DeliveryJournal_PrintToday" Header="von heute drucken" IsEnabled="False" Tag="Print" <MenuItem x:Name="Menu_Print_DeliveryJournal_PrintToday" Header="von heute drucken"
Click="Menu_Print_DeliveryJournal_PrintToday_Click"/> Click="Menu_Print_DeliveryJournal_PrintToday_Click"/>
<MenuItem x:Name="Menu_Print_DeliveryJournal_ShowFilter" Header="aus Filtern anzeigen" IsEnabled="False" Tag="Print" <MenuItem x:Name="Menu_Print_DeliveryJournal_ShowFilter" Header="aus Filtern anzeigen"
Click="Menu_Print_DeliveryJournal_ShowFilter_Click"/> Click="Menu_Print_DeliveryJournal_ShowFilter_Click"/>
<MenuItem x:Name="Menu_Print_DeliveryJournal_PrintFilter" Header="aus Filtern drucken" IsEnabled="False" Tag="Print" <MenuItem x:Name="Menu_Print_DeliveryJournal_PrintFilter" Header="aus Filtern drucken"
Click="Menu_Print_DeliveryJournal_PrintFilter_Click"/> Click="Menu_Print_DeliveryJournal_PrintFilter_Click"/>
</MenuItem> </MenuItem>
</MenuItem> </MenuItem>
@ -107,6 +107,7 @@
<Bold>Saison</Bold>: z.B. 2020, &gt;2015, 2017-2019, &lt;2005, 2019-, ...<LineBreak/> <Bold>Saison</Bold>: z.B. 2020, &gt;2015, 2017-2019, &lt;2005, 2019-, ...<LineBreak/>
<Bold>Zweigstelle</Bold>: z.B. musterort, ...<LineBreak/> <Bold>Zweigstelle</Bold>: z.B. musterort, ...<LineBreak/>
<Bold>Attribut</Bold>: z.B. kabinett, !kabinett (alle außer kabinett), ...<LineBreak/> <Bold>Attribut</Bold>: z.B. kabinett, !kabinett (alle außer kabinett), ...<LineBreak/>
<Bold>Bewirtschaftung</Bold>: z.B. bio, !kip (alle außer KIP), ...<LineBreak/>
<Bold>Datum</Bold>: z.B. 1.9., 15.9.-10.10., -15.10.2020, ...<LineBreak/> <Bold>Datum</Bold>: z.B. 1.9., 15.9.-10.10., -15.10.2020, ...<LineBreak/>
<Bold>Uhrzeit</Bold>: z.B. 06:00-08:00, 18:00-, ...<LineBreak/> <Bold>Uhrzeit</Bold>: z.B. 06:00-08:00, 18:00-, ...<LineBreak/>
<Bold>Freitext</Bold>: z.B. Lieferscheinnummern, Anmerkung, "quw" (sucht nach dem Text "quw") <Bold>Freitext</Bold>: z.B. Lieferscheinnummern, Anmerkung, "quw" (sucht nach dem Text "quw")
@ -235,9 +236,12 @@
<Label Content="Mitglied:" Margin="10,10,0,0" Grid.Column="0"/> <Label Content="Mitglied:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="MgNrInput" Width="48" Grid.Row="1" Grid.Column="1" Margin="0,10,0,0" HorizontalAlignment="Left" TextAlignment="Right" <TextBox x:Name="MgNrInput" Width="48" Grid.Row="1" Grid.Column="1" Margin="0,10,0,0" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="MgNrInput_TextChanged" LostFocus="MgNrInput_LostFocus" KeyUp="Input_KeyUp"/> TextChanged="MgNrInput_TextChanged" LostFocus="MgNrInput_LostFocus" KeyUp="Input_KeyUp"/>
<ComboBox x:Name="MemberInput" Grid.Column="1" Margin="53,10,10,10" IsEditable="True" <ComboBox x:Name="MemberInput" Grid.Column="1" Margin="53,10,40,10" IsEditable="True"
ItemTemplate="{StaticResource MemberAdminNameTemplate}" TextSearch.TextPath="AdministrativeName" ItemTemplate="{StaticResource MemberAdminNameTemplate}" TextSearch.TextPath="AdministrativeName"
SelectionChanged="MemberInput_SelectionChanged" KeyUp="Input_KeyUp"/> SelectionChanged="MemberInput_SelectionChanged" KeyUp="Input_KeyUp"/>
<Button x:Name="MemberReferenceButton" Grid.Column="1" Height="25" Width="25" FontFamily="Segoe MDL2 Assets" Content="&#xEE35;" Padding="0,0,0,0"
Margin="10,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Right" ToolTip="Zu Mitglied springen"
Click="MemberReferenceButton_Click"/>
<Label Content="Wohnort:" Margin="10,38,0,0" Grid.Column="0"/> <Label Content="Wohnort:" Margin="10,38,0,0" Grid.Column="0"/>
<TextBox x:Name="MemberAddressField" Grid.Column="1" Margin="0,40,10,10" FontSize="12" Height="22" <TextBox x:Name="MemberAddressField" Grid.Column="1" Margin="0,40,10,10" FontSize="12" Height="22"
@ -280,21 +284,25 @@
<GroupBox Header="Sorte" Grid.Column="1" Grid.Row="0" Margin="5,5,5,5"> <GroupBox Header="Sorte" Grid.Column="1" Grid.Row="0" Margin="5,5,5,5">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/> <ColumnDefinition Width="80"/>
<ColumnDefinition/>
<ColumnDefinition/> <ColumnDefinition/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Label Content="Sorte:" Margin="10,10,0,0" Grid.Column="0"/> <Label Content="Sorte:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="SortIdInput" Width="36" Grid.Row="1" Grid.Column="1" Margin="0,10,0,0" HorizontalAlignment="Left" <TextBox x:Name="SortIdInput" Width="36" Grid.Row="1" Grid.Column="1" Margin="0,10,0,0" HorizontalAlignment="Left"
TextChanged="SortIdInput_TextChanged" LostFocus="SortIdInput_LostFocus" KeyUp="Input_KeyUp"/> TextChanged="SortIdInput_TextChanged" LostFocus="SortIdInput_LostFocus" KeyUp="Input_KeyUp"/>
<ComboBox x:Name="WineVarietyInput" Grid.Row="1" Grid.Column="1" Margin="41,10,10,10" <ComboBox x:Name="WineVarietyInput" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Margin="41,10,10,10"
ItemTemplate="{StaticResource WineVarietyTemplate}" TextSearch.TextPath="Name" ItemTemplate="{StaticResource WineVarietyTemplate}" TextSearch.TextPath="Name"
SelectionChanged="WineVarietyInput_SelectionChanged" KeyUp="Input_KeyUp"/> SelectionChanged="WineVarietyInput_SelectionChanged" KeyUp="Input_KeyUp"/>
<Label Content="Attribut:" Margin="10,40,0,0" Grid.Column="0"/> <Label Content="Attr./Bewirt.:" Margin="10,40,0,0" Grid.Column="0"/>
<ComboBox x:Name="AttributeInput" Grid.Row="1" Grid.Column="1" Margin="0,40,10,10" <ComboBox x:Name="AttributeInput" Grid.Row="1" Grid.Column="1" Margin="0,40,5,10"
DisplayMemberPath="Name" DisplayMemberPath="Name"
SelectionChanged="AttributeInput_SelectionChanged" KeyUp="Input_KeyUp"/> SelectionChanged="AttributeInput_SelectionChanged" KeyUp="Input_KeyUp"/>
<ComboBox x:Name="CultivationInput" Grid.Row="1" Grid.Column="2" Margin="0,40,10,10"
DisplayMemberPath="Name"
SelectionChanged="CultivationInput_SelectionChanged" KeyUp="Input_KeyUp"/>
</Grid> </Grid>
</GroupBox> </GroupBox>
@ -337,7 +345,7 @@
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,45,10,10" Grid.Column="0" Grid.ColumnSpan="2" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,45,10,10" Grid.Column="0" Grid.ColumnSpan="2"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/> Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
<CheckBox x:Name="GerebeltGewogenInput" Content="Gerebelt gewogen" <CheckBox x:Name="GerebeltGewogenInput" Content="Netto (gerebelt gewogen)"
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,75,10,10" Grid.Column="0" Grid.ColumnSpan="2" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,75,10,10" Grid.Column="0" Grid.ColumnSpan="2"
Checked="GerebeltGewogenInput_Changed" Unchecked="GerebeltGewogenInput_Changed"/> Checked="GerebeltGewogenInput_Changed" Unchecked="GerebeltGewogenInput_Changed"/>
@ -411,7 +419,8 @@
<TextBlock Text="{Binding Kmw, StringFormat='{}{0:0.0}°'}" Width="40" TextAlignment="Right" Padding="0,0,10,0"/> <TextBlock Text="{Binding Kmw, StringFormat='{}{0:0.0}°'}" Width="40" TextAlignment="Right" Padding="0,0,10,0"/>
<TextBlock Text="{Binding QualId}" Width="30"/> <TextBlock Text="{Binding QualId}" Width="30"/>
<TextBlock Text="{Binding Weight, StringFormat='{}{0:N0} kg'}" Width="60" TextAlignment="Right" Padding="0,0,10,0"/> <TextBlock Text="{Binding Weight, StringFormat='{}{0:N0} kg'}" Width="60" TextAlignment="Right" Padding="0,0,10,0"/>
<TextBlock Text="{Binding Attribute.Name}" Width="100"/> <TextBlock Text="{Binding Attribute.Name}" Width="60"/>
<TextBlock Text="{Binding Cultivation.Name}" Width="50"/>
</StackPanel> </StackPanel>
</DataTemplate> </DataTemplate>
</ListBox.ItemTemplate> </ListBox.ItemTemplate>

View File

@ -76,17 +76,20 @@ namespace Elwig.Windows {
if (IsReceipt) { if (IsReceipt) {
Title = $"Übernahme - {App.BranchName} - Elwig"; Title = $"Übernahme - {App.BranchName} - Elwig";
TodayOnlyInput.IsChecked = true; TodayOnlyInput.IsChecked = true;
var n = App.Scales.Count; var n = App.CommandScales.Count;
if (n < 1) WeighingAButton.Visibility = Visibility.Hidden; if (n < 1) WeighingAButton.Visibility = Visibility.Hidden;
if (n < 2) WeighingBButton.Visibility = Visibility.Hidden; if (n < 2) WeighingBButton.Visibility = Visibility.Hidden;
if (n < 3) WeighingCButton.Visibility = Visibility.Hidden; if (n < 3) WeighingCButton.Visibility = Visibility.Hidden;
if (n < 4) WeighingDButton.Visibility = Visibility.Hidden; if (n < 4) WeighingDButton.Visibility = Visibility.Hidden;
if (n == 1) WeighingAButton.Content = "Wiegen"; if (n == 1) WeighingAButton.Content = "Wiegen";
if (n > 1) WeighingAButton.Content = $"Wiegen {App.Scales[0].ScaleId}"; if (n > 1) WeighingAButton.Content = $"Wiegen {App.CommandScales[0].ScaleId}";
if (n >= 2) WeighingBButton.Content = $"Wiegen {App.Scales[1].ScaleId}"; if (n >= 2) WeighingBButton.Content = $"Wiegen {App.CommandScales[1].ScaleId}";
if (n >= 3) WeighingCButton.Content = $"Wiegen {App.Scales[2].ScaleId}"; if (n >= 3) WeighingCButton.Content = $"Wiegen {App.CommandScales[2].ScaleId}";
if (n >= 4) WeighingDButton.Content = $"Wiegen {App.Scales[3].ScaleId}"; if (n >= 4) WeighingDButton.Content = $"Wiegen {App.CommandScales[3].ScaleId}";
WeighingManualButton.Margin = new Thickness(10, 10 + n * 32, 10, 10); WeighingManualButton.Margin = new Thickness(10, 10 + n * 32, 10, 10);
foreach (var s in App.EventScales) {
s.WeighingEvent += Scale_Weighing;
}
} else { } else {
WeighingManualButton.Visibility = Visibility.Hidden; WeighingManualButton.Visibility = Visibility.Hidden;
WeighingAButton.Visibility = Visibility.Hidden; WeighingAButton.Visibility = Visibility.Hidden;
@ -100,15 +103,9 @@ namespace Elwig.Windows {
Member = Context.Members.Find(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value"); Member = Context.Members.Find(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
Title = $"Lieferungen - {Member.AdministrativeName} - Elwig"; Title = $"Lieferungen - {Member.AdministrativeName} - Elwig";
AllSeasonsInput.IsEnabled = true; AllSeasonsInput.IsEnabled = true;
AllSeasonsInput.IsChecked = true;
} }
private void Window_Loaded(object sender, RoutedEventArgs evt) { private void Window_Loaded(object sender, RoutedEventArgs evt) {
Menu_Print_DeliveryJournal_ShowToday.IsEnabled = App.IsPrintingReady;
Menu_Print_DeliveryJournal_PrintToday.IsEnabled = App.IsPrintingReady;
Menu_Print_DeliveryJournal_ShowFilter.IsEnabled = App.IsPrintingReady;
Menu_Print_DeliveryJournal_PrintFilter.IsEnabled = App.IsPrintingReady;
OnSecondPassed(null, null); OnSecondPassed(null, null);
Timer.Start(); Timer.Start();
LockInputs(); LockInputs();
@ -116,7 +113,7 @@ namespace Elwig.Windows {
NewDeliveryButton_Click(null, null); NewDeliveryButton_Click(null, null);
if ((Context.Seasons.Find(Utils.CurrentYear)) == null) { if ((Context.Seasons.Find(Utils.CurrentYear)) == null) {
MessageBox.Show("Die Saison für das aktuelle Jahr wurde noch nicht erstellt. Neue Lieferungen können nicht abgespeichert werden.", MessageBox.Show("Die Saison für das aktuelle Jahr wurde noch nicht erstellt. Neue Lieferungen können nicht abgespeichert werden.",
"Saison noch nicht erstellt", MessageBoxButton.OK, MessageBoxImage.Error); "Saison noch nicht erstellt", MessageBoxButton.OK, MessageBoxImage.Warning);
} }
} }
} }
@ -124,19 +121,27 @@ namespace Elwig.Windows {
private async void Menu_Print_ShowDeliveryNote_Click(object sender, RoutedEventArgs evt) { private async void Menu_Print_ShowDeliveryNote_Click(object sender, RoutedEventArgs evt) {
if (DeliveryList.SelectedItem is not Delivery d) return; if (DeliveryList.SelectedItem is not Delivery d) return;
Mouse.OverrideCursor = Cursors.AppStarting; Mouse.OverrideCursor = Cursors.AppStarting;
try {
using var doc = new DeliveryNote(d, Context); using var doc = new DeliveryNote(d, Context);
await doc.Generate(); await doc.Generate();
Mouse.OverrideCursor = null;
doc.Show(); doc.Show();
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
} }
private async void Menu_Print_PrintDeliveryNote_Click(object sender, RoutedEventArgs evt) { private async void Menu_Print_PrintDeliveryNote_Click(object sender, RoutedEventArgs evt) {
if (DeliveryList.SelectedItem is not Delivery d) return; if (DeliveryList.SelectedItem is not Delivery d) return;
Mouse.OverrideCursor = Cursors.AppStarting; Mouse.OverrideCursor = Cursors.AppStarting;
try {
using var doc = new DeliveryNote(d, Context); using var doc = new DeliveryNote(d, Context);
await doc.Generate(); await doc.Generate();
Mouse.OverrideCursor = null;
await doc.Print(); await doc.Print();
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
} }
private async void Menu_Export_Bki_Click(object sender, RoutedEventArgs evt) { private async void Menu_Export_Bki_Click(object sender, RoutedEventArgs evt) {
@ -158,36 +163,52 @@ namespace Elwig.Windows {
private async void Menu_Print_DeliveryJournal_ShowToday_Click(object sender, RoutedEventArgs evt) { private async void Menu_Print_DeliveryJournal_ShowToday_Click(object sender, RoutedEventArgs evt) {
Mouse.OverrideCursor = Cursors.AppStarting; Mouse.OverrideCursor = Cursors.AppStarting;
try {
var doc = new DeliveryJournal(Context, DateOnly.FromDateTime(Utils.Today)); var doc = new DeliveryJournal(Context, DateOnly.FromDateTime(Utils.Today));
await doc.Generate(); await doc.Generate();
Mouse.OverrideCursor = null;
doc.Show(); doc.Show();
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
} }
private async void Menu_Print_DeliveryJournal_PrintToday_Click(object sender, RoutedEventArgs evt) { private async void Menu_Print_DeliveryJournal_PrintToday_Click(object sender, RoutedEventArgs evt) {
Mouse.OverrideCursor = Cursors.AppStarting; Mouse.OverrideCursor = Cursors.AppStarting;
try {
var doc = new DeliveryJournal(Context, DateOnly.FromDateTime(Utils.Today)); var doc = new DeliveryJournal(Context, DateOnly.FromDateTime(Utils.Today));
await doc.Generate(); await doc.Generate();
Mouse.OverrideCursor = null;
await doc.Print(); await doc.Print();
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
} }
private async void Menu_Print_DeliveryJournal_ShowFilter_Click(object sender, RoutedEventArgs evt) { private async void Menu_Print_DeliveryJournal_ShowFilter_Click(object sender, RoutedEventArgs evt) {
Mouse.OverrideCursor = Cursors.AppStarting; Mouse.OverrideCursor = Cursors.AppStarting;
try {
var (f, _, d, _, _) = await GetFilters(); var (f, _, d, _, _) = await GetFilters();
var doc = new DeliveryJournal(string.Join(" / ", f), d); var doc = new DeliveryJournal(string.Join(" / ", f), d);
await doc.Generate(); await doc.Generate();
Mouse.OverrideCursor = null;
doc.Show(); doc.Show();
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
} }
private async void Menu_Print_DeliveryJournal_PrintFilter_Click(object sender, RoutedEventArgs evt) { private async void Menu_Print_DeliveryJournal_PrintFilter_Click(object sender, RoutedEventArgs evt) {
Mouse.OverrideCursor = Cursors.AppStarting; Mouse.OverrideCursor = Cursors.AppStarting;
try {
var (f, _, d, _, _) = await GetFilters(); var (f, _, d, _, _) = await GetFilters();
var doc = new DeliveryJournal(string.Join(" / ", f), d); var doc = new DeliveryJournal(string.Join(" / ", f), d);
await doc.Generate(); await doc.Generate();
Mouse.OverrideCursor = null;
doc.Show(); doc.Show();
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
} }
private void Menu_Settings_EnableFreeEditing_Checked(object sender, RoutedEventArgs evt) { private void Menu_Settings_EnableFreeEditing_Checked(object sender, RoutedEventArgs evt) {
@ -218,7 +239,7 @@ namespace Elwig.Windows {
} }
private void InitialDefaultInputs() { private void InitialDefaultInputs() {
if (App.Client.HasRebler(BranchInput.SelectedValue as Branch)) { if (App.Client.HasNetWeighing(BranchInput.SelectedValue as Branch)) {
GerebeltGewogenInput.IsEnabled = false; GerebeltGewogenInput.IsEnabled = false;
SetDefaultValue(GerebeltGewogenInput, true); SetDefaultValue(GerebeltGewogenInput, true);
} else { } else {
@ -226,7 +247,7 @@ namespace Elwig.Windows {
UnsetDefaultValue(GerebeltGewogenInput); UnsetDefaultValue(GerebeltGewogenInput);
} }
if (App.Client.HasKisten(BranchInput.SelectedValue as Branch)) { if (!App.Client.HasNetWeighing(BranchInput.SelectedValue as Branch)) {
LesewagenInput.IsEnabled = false; LesewagenInput.IsEnabled = false;
SetDefaultValue(LesewagenInput, false); SetDefaultValue(LesewagenInput, false);
HandPickedInput.IsThreeState = false; HandPickedInput.IsThreeState = false;
@ -256,9 +277,9 @@ namespace Elwig.Windows {
ClearOriginalValues(); ClearOriginalValues();
ClearDefaultValues(); ClearDefaultValues();
GerebeltGewogenInput.IsChecked = App.Client.HasRebler(BranchInput.SelectedValue as Branch); GerebeltGewogenInput.IsChecked = App.Client.HasNetWeighing(BranchInput.SelectedValue as Branch);
LesewagenInput.IsChecked = false; LesewagenInput.IsChecked = false;
HandPickedInput.IsChecked = App.Client.HasKisten(BranchInput.SelectedValue as Branch) ? true : null; HandPickedInput.IsChecked = !App.Client.HasNetWeighing(BranchInput.SelectedValue as Branch) ? true : null;
GebundenInput.IsChecked = null; GebundenInput.IsChecked = null;
InitialDefaultInputs(); InitialDefaultInputs();
@ -289,7 +310,7 @@ namespace Elwig.Windows {
if (ctrl == MgNrInput || ctrl == MemberInput) { if (ctrl == MgNrInput || ctrl == MemberInput) {
SortIdInput.Focus(); SortIdInput.Focus();
SortIdInput.SelectAll(); SortIdInput.SelectAll();
} else if (ctrl == SortIdInput || ctrl == WineVarietyInput || ctrl == AttributeInput) { } else if (ctrl == SortIdInput || ctrl == WineVarietyInput || ctrl == AttributeInput || ctrl == CultivationInput) {
GradationOeInput.Focus(); GradationOeInput.Focus();
GradationOeInput.TextBox.SelectAll(); GradationOeInput.TextBox.SelectAll();
} else if (ctrl == GradationKmwInput || ctrl == GradationOeInput || ctrl == WineQualityLevelInput) { } else if (ctrl == GradationKmwInput || ctrl == GradationOeInput || ctrl == WineQualityLevelInput) {
@ -333,6 +354,8 @@ namespace Elwig.Windows {
var filterZwst = new List<string>(); var filterZwst = new List<string>();
var filterAttr = new List<string>(); var filterAttr = new List<string>();
var filterNotAttr = new List<string>(); var filterNotAttr = new List<string>();
var filterCult = new List<string>();
var filterNotCult = new List<string>();
var filterDate = new List<(string?, string?)>(); var filterDate = new List<(string?, string?)>();
var filterTime = new List<(string?, string?)>(); var filterTime = new List<(string?, string?)>();
int filterYearGt = 0, filterYearLt = 0; int filterYearGt = 0, filterYearLt = 0;
@ -345,7 +368,8 @@ namespace Elwig.Windows {
var qual = await Context.WineQualityLevels.Where(q => !q.IsPredicate).ToDictionaryAsync(q => q.QualId, q => q); var qual = await Context.WineQualityLevels.Where(q => !q.IsPredicate).ToDictionaryAsync(q => q.QualId, q => q);
var mgnr = await Context.Members.ToDictionaryAsync(m => m.MgNr.ToString(), m => m); var mgnr = await Context.Members.ToDictionaryAsync(m => m.MgNr.ToString(), m => m);
var zwst = await Context.Branches.ToDictionaryAsync(b => b.Name.ToLower().Split(" ")[0], b => b); var zwst = await Context.Branches.ToDictionaryAsync(b => b.Name.ToLower().Split(" ")[0], b => b);
var attr = await Context.WineAttributes.ToDictionaryAsync(a => a.Name.ToLower().Split(" ")[0], a => a); var attr = await Context.WineAttributes.ToDictionaryAsync(a => a.Name.ToLower().Split(' ')[0], a => a);
var cult = await Context.WineCultivations.ToDictionaryAsync(c => c.Name.ToLower().Split(' ')[0], c => c);
for (int i = 0; i < filter.Count; i++) { for (int i = 0; i < filter.Count; i++) {
var e = filter[i]; var e = filter[i];
@ -397,6 +421,16 @@ namespace Elwig.Windows {
filterNotAttr.Add(a.AttrId); filterNotAttr.Add(a.AttrId);
filter.RemoveAt(i--); filter.RemoveAt(i--);
filterNames.Add($"ohne Attribut {a.Name}"); filterNames.Add($"ohne Attribut {a.Name}");
} else if (cult.ContainsKey(e.ToLower())) {
var c = cult[e.ToLower()];
filterCult.Add(c.CultId);
filter.RemoveAt(i--);
filterNames.Add($"Bewirtschaftung {c.Name}");
} else if (e[0] == '!' && cult.ContainsKey(e[1..].ToLower())) {
var c = cult[e[1..].ToLower()];
filterNotCult.Add(c.CultId);
filter.RemoveAt(i--);
filterNames.Add($"ohne Bewirtschaftung {c.Name}");
} else if (zwst.ContainsKey(e.ToLower())) { } else if (zwst.ContainsKey(e.ToLower())) {
var b = zwst[e.ToLower()]; var b = zwst[e.ToLower()];
filterZwst.Add(b.ZwstId); filterZwst.Add(b.ZwstId);
@ -454,8 +488,8 @@ namespace Elwig.Windows {
var s = date.ToString("yyyy-MM-dd"); var s = date.ToString("yyyy-MM-dd");
filterDate.Add((s, s)); filterDate.Add((s, s));
filter.RemoveAt(i--); filter.RemoveAt(i--);
if (filterNames.Contains(SeasonInput.Value.ToString()) && SeasonInput.Value == date.Year) if (filterNames.Contains(SeasonInput.Value.ToString()!) && SeasonInput.Value == date.Year)
filterNames.Remove(SeasonInput.Value.ToString()); filterNames.Remove(SeasonInput.Value.ToString()!);
filterNames.Add(date.ToString("dd.MM.yyyy")); filterNames.Add(date.ToString("dd.MM.yyyy"));
} else if (Utils.DateFromToRegex.IsMatch(e)) { } else if (Utils.DateFromToRegex.IsMatch(e)) {
var parts = e.Split("-"); var parts = e.Split("-");
@ -467,11 +501,11 @@ namespace Elwig.Windows {
filter.RemoveAt(i--); filter.RemoveAt(i--);
var n = string.Join('.', s.Split('-').Reverse()); var n = string.Join('.', s.Split('-').Reverse());
if (dParts[2] == "") { if (dParts[2] == "") {
filterNames.Remove(SeasonInput.Value.ToString()); filterNames.Remove(SeasonInput.Value.ToString()!);
filterNames.Add(n + SeasonInput.Value.ToString()); filterNames.Add(n + SeasonInput.Value.ToString());
} else { } else {
if (SeasonInput.Value.ToString() == dParts[2]) if (SeasonInput.Value.ToString() == dParts[2])
filterNames.Remove(SeasonInput.Value.ToString()); filterNames.Remove(SeasonInput.Value.ToString()!);
filterNames.Add(n); filterNames.Add(n);
} }
} else if (parts.Length == 2) { } else if (parts.Length == 2) {
@ -521,6 +555,8 @@ namespace Elwig.Windows {
if (filterZwst.Count > 0) prd = prd.And(p => filterZwst.Contains(p.Delivery.ZwstId)); if (filterZwst.Count > 0) prd = prd.And(p => filterZwst.Contains(p.Delivery.ZwstId));
if (filterAttr.Count > 0) prd = prd.And(p => p.AttrId != null && filterAttr.Contains(p.AttrId)); if (filterAttr.Count > 0) prd = prd.And(p => p.AttrId != null && filterAttr.Contains(p.AttrId));
if (filterNotAttr.Count > 0) prd = prd.And(p => p.AttrId == null || !filterNotAttr.Contains(p.AttrId)); if (filterNotAttr.Count > 0) prd = prd.And(p => p.AttrId == null || !filterNotAttr.Contains(p.AttrId));
if (filterCult.Count > 0) prd = prd.And(p => p.CultId != null && filterCult.Contains(p.CultId));
if (filterNotCult.Count > 0) prd = prd.And(p => p.CultId == null || !filterNotCult.Contains(p.CultId));
if (filterKmwGt > 0) prd = prd.And(p => p.Kmw >= filterKmwGt); if (filterKmwGt > 0) prd = prd.And(p => p.Kmw >= filterKmwGt);
if (filterKmwLt > 0) prd = prd.And(p => p.Kmw < filterKmwLt); if (filterKmwLt > 0) prd = prd.And(p => p.Kmw < filterKmwLt);
if (filterOeGt > 0) prd = prd.And(p => p.Kmw * (4.54 + 0.022 * p.Kmw) >= filterOeGt); if (filterOeGt > 0) prd = prd.And(p => p.Kmw * (4.54 + 0.022 * p.Kmw) >= filterOeGt);
@ -644,9 +680,10 @@ namespace Elwig.Windows {
AddGradationToolTipRow(1, "Gradation", null, kmwMin, kmwAvg, kmwMax); AddGradationToolTipRow(1, "Gradation", null, kmwMin, kmwAvg, kmwMax);
var attrGroups = await deliveryParts var attrGroups = await deliveryParts
.GroupBy(p => p.Attribute.Name) .GroupBy(p => new { Attr = p.Attribute.Name, Cult = p.Cultivation.Name })
.Select(g => new { .Select(g => new {
Attr = g.Key, g.Key.Attr,
g.Key.Cult,
Weight = g.Sum(p => p.Weight), Weight = g.Sum(p => p.Weight),
Min = g.Min(p => p.Kmw), Min = g.Min(p => p.Kmw),
Avg = g.Sum(p => p.Kmw * p.Weight) / g.Sum(p => p.Weight), Avg = g.Sum(p => p.Kmw * p.Weight) / g.Sum(p => p.Weight),
@ -669,11 +706,13 @@ namespace Elwig.Windows {
.ToListAsync(); .ToListAsync();
var groups = await deliveryParts var groups = await deliveryParts
.GroupBy(p => new { .GroupBy(p => new {
p.Attribute.Name, Attr = p.Attribute.Name,
Cult = p.Cultivation.Name,
p.SortId, p.SortId,
}) })
.Select(g => new { .Select(g => new {
Attr = g.Key.Name, g.Key.Attr,
g.Key.Cult,
g.Key.SortId, g.Key.SortId,
Weight = g.Sum(p => p.Weight), Weight = g.Sum(p => p.Weight),
Min = g.Min(p => p.Kmw), Min = g.Min(p => p.Kmw),
@ -688,25 +727,28 @@ namespace Elwig.Windows {
int rowNum = 1; int rowNum = 1;
foreach (var attrG in attrGroups) { foreach (var attrG in attrGroups) {
rowNum++; rowNum++;
AddWeightToolTipRow(rowNum++, attrG.Attr, null, attrG.Weight, attrG.Weight, weight); var name = attrG.Attr == null && attrG.Cult == null ? null : attrG.Attr + (attrG.Attr != null && attrG.Cult != null ? " / " : "") + attrG.Cult;
foreach (var g in groups.Where(g => g.Attr == attrG.Attr).OrderByDescending(g => g.Weight).ThenBy(g => g.SortId)) { AddWeightToolTipRow(rowNum++, name, null, attrG.Weight, attrG.Weight, weight);
foreach (var g in groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult).OrderByDescending(g => g.Weight).ThenBy(g => g.SortId)) {
AddWeightToolTipRow(rowNum++, null, g.SortId, g.Weight, attrG.Weight, weight); AddWeightToolTipRow(rowNum++, null, g.SortId, g.Weight, attrG.Weight, weight);
} }
} }
rowNum = 2; rowNum = 2;
foreach (var attrG in attrGroups) { foreach (var attrG in attrGroups) {
rowNum++; rowNum++;
AddGradationToolTipRow(rowNum++, attrG.Attr, null, attrG.Min, attrG.Avg, attrG.Max); var name = attrG.Attr == null && attrG.Cult == null ? null : attrG.Attr + (attrG.Attr != null && attrG.Cult != null ? " / " : "") + attrG.Cult;
foreach (var g in groups.Where(g => g.Attr == attrG.Attr).OrderByDescending(g => g.Avg).ThenBy(g => g.SortId)) { AddGradationToolTipRow(rowNum++, name, null, attrG.Min, attrG.Avg, attrG.Max);
foreach (var g in groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult).OrderByDescending(g => g.Avg).ThenBy(g => g.SortId)) {
AddGradationToolTipRow(rowNum++, null, g.SortId, g.Min, g.Avg, g.Max); AddGradationToolTipRow(rowNum++, null, g.SortId, g.Min, g.Avg, g.Max);
} }
} }
if (attrGroups.Count == 1) { if (attrGroups.Count == 1) {
var g = attrGroups.First().Attr; var g = attrGroups.First();
if (g != null) { var name = g.Attr == null && g.Cult == null ? null : g.Attr + (g.Attr != null && g.Cult != null ? " / " : "") + g.Cult;
StatusWeight.Text += $" [{g}]"; if (name != null) {
StatusGradation.Text += $" [{g}]"; StatusWeight.Text += $" [{name}]";
StatusGradation.Text += $" [{name}]";
} }
if (sortGroups.Count > 1 && sortGroups.Count <= 4) { if (sortGroups.Count > 1 && sortGroups.Count <= 4) {
StatusWeight.Text += $" = {string.Join(" + ", sortGroups.Select(g => $"{g.Weight:N0} kg ({(double)g.Weight / weight:0%})" + (g.SortId == null ? "" : $" [{g.SortId}]")))}"; StatusWeight.Text += $" = {string.Join(" + ", sortGroups.Select(g => $"{g.Weight:N0} kg ({(double)g.Weight / weight:0%})" + (g.SortId == null ? "" : $" [{g.SortId}]")))}";
@ -714,8 +756,8 @@ namespace Elwig.Windows {
} }
} else if (attrGroups.Count <= 4) { } else if (attrGroups.Count <= 4) {
StatusWeight.Text += $" = {string.Join(" + ", attrGroups.Select(g => $"{g.Weight:N0} kg ({(double)g.Weight / weight:0%})" + (g.Attr == null ? "" : $" [{g.Attr}]")))}"; StatusWeight.Text += $" = {string.Join(" + ", attrGroups.Select(g => $"{g.Weight:N0} kg ({(double)g.Weight / weight:0%})" + (g.Attr == null && g.Cult == null ? "" : $" [{g.Attr}{(g.Attr != null && g.Cult != null ? " / " : "")}{g.Cult}]")))}";
StatusGradation.Text += $" = {string.Join(" + ", attrGroups.Select(g => $"{g.Min:N1}/{g.Avg:N1}/{g.Max:N1}" + (g.Attr == null ? "" : $" [{g.Attr}]")))}"; StatusGradation.Text += $" = {string.Join(" + ", attrGroups.Select(g => $"{g.Min:N1}/{g.Avg:N1}/{g.Max:N1}" + (g.Attr == null && g.Cult == null ? "" : $" [{g.Attr}{(g.Attr != null && g.Cult != null ? " / " : "")}{g.Cult}]")))}";
} }
} else { } else {
StatusGradation.Text = "Gradation: -"; StatusGradation.Text = "Gradation: -";
@ -759,6 +801,9 @@ namespace Elwig.Windows {
var attrList = await Context.WineAttributes.Where(a => !IsCreating || a.IsActive).OrderBy(a => a.Name).Cast<object>().ToListAsync(); var attrList = await Context.WineAttributes.Where(a => !IsCreating || a.IsActive).OrderBy(a => a.Name).Cast<object>().ToListAsync();
attrList.Insert(0, new NullItem("")); attrList.Insert(0, new NullItem(""));
ControlUtils.RenewItemsSource(AttributeInput, attrList, i => (i as WineAttr)?.AttrId, null, ControlUtils.RenewSourceDefault.First); ControlUtils.RenewItemsSource(AttributeInput, attrList, i => (i as WineAttr)?.AttrId, null, ControlUtils.RenewSourceDefault.First);
var cultList = await Context.WineCultivations.OrderBy(a => a.Name).Cast<object>().ToListAsync();
cultList.Insert(0, new NullItem(""));
ControlUtils.RenewItemsSource(CultivationInput, cultList, i => (i as WineCult)?.CultId, null, ControlUtils.RenewSourceDefault.First);
ControlUtils.RenewItemsSource(WineQualityLevelInput, await Context.WineQualityLevels.ToListAsync(), i => (i as WineQualLevel)?.QualId); ControlUtils.RenewItemsSource(WineQualityLevelInput, await Context.WineQualityLevels.ToListAsync(), i => (i as WineQualLevel)?.QualId);
ControlUtils.RenewItemsSource(ModifiersInput, await Context.Modifiers.Where(m => m.Year == y).OrderBy(m => m.Ordering).ToListAsync(), i => (i as Modifier)?.ModId); ControlUtils.RenewItemsSource(ModifiersInput, await Context.Modifiers.Where(m => m.Year == y).OrderBy(m => m.Ordering).ToListAsync(), i => (i as Modifier)?.ModId);
ControlUtils.RenewItemsSource(WineOriginInput, (await Context.WineOrigins.ToListAsync()).OrderByDescending(o => o.SortKey).ThenBy(o => o.HkId), i => (i as WineOrigin)?.HkId); ControlUtils.RenewItemsSource(WineOriginInput, (await Context.WineOrigins.ToListAsync()).OrderByDescending(o => o.SortKey).ThenBy(o => o.HkId), i => (i as WineOrigin)?.HkId);
@ -833,14 +878,15 @@ namespace Elwig.Windows {
SortIdInput.Text = p?.SortId ?? ""; SortIdInput.Text = p?.SortId ?? "";
ControlUtils.SelectComboBoxItem(AttributeInput, p?.Attribute, i => (i as WineAttr)?.AttrId); ControlUtils.SelectComboBoxItem(AttributeInput, p?.Attribute, i => (i as WineAttr)?.AttrId);
ControlUtils.SelectComboBoxItem(CultivationInput, p?.Cultivation, i => (i as WineCult)?.CultId);
GradationKmwInput.Text = (p != null) ? $"{p.Kmw:N1}" : ""; GradationKmwInput.Text = (p != null) ? $"{p.Kmw:N1}" : "";
ControlUtils.SelectComboBoxItem(WineQualityLevelInput, q => (q as WineQualLevel)?.QualId, p?.QualId); ControlUtils.SelectComboBoxItem(WineQualityLevelInput, q => (q as WineQualLevel)?.QualId, p?.QualId);
ControlUtils.SelectComboBoxItem(WineKgInput, k => (k as AT_Kg)?.KgNr, p?.KgNr); ControlUtils.SelectComboBoxItem(WineKgInput, k => (k as AT_Kg)?.KgNr, p?.KgNr);
ControlUtils.SelectComboBoxItem(WineRdInput, r => (r as WbRd)?.RdNr, p?.RdNr); ControlUtils.SelectComboBoxItem(WineRdInput, r => (r as WbRd)?.RdNr, p?.RdNr);
ControlUtils.SelectComboBoxItem(WineOriginInput, r => (r as WineOrigin)?.HkId, p?.HkId); ControlUtils.SelectComboBoxItem(WineOriginInput, r => (r as WineOrigin)?.HkId, p?.HkId);
WeightInput.Text = (p != null) ? $"{p.Weight:N0}" : ""; WeightInput.Text = (p != null) ? $"{p.Weight:N0}" : "";
ManualWeighingInput.IsChecked = p?.ManualWeighing ?? false; ManualWeighingInput.IsChecked = p?.IsManualWeighing ?? false;
GerebeltGewogenInput.IsChecked = p?.IsGerebelt ?? false; GerebeltGewogenInput.IsChecked = p?.IsNetWeight ?? false;
ControlUtils.SelectCheckComboBoxItems(ModifiersInput, p?.Modifiers, i => (i as Modifier)?.ModId); ControlUtils.SelectCheckComboBoxItems(ModifiersInput, p?.Modifiers, i => (i as Modifier)?.ModId);
PartCommentInput.Text = p?.Comment ?? ""; PartCommentInput.Text = p?.Comment ?? "";
TemperatureInput.Text = (p != null && p.Temperature != null) ? $"{p.Temperature:N1}" : ""; TemperatureInput.Text = (p != null && p.Temperature != null) ? $"{p.Temperature:N1}" : "";
@ -890,20 +936,21 @@ namespace Elwig.Windows {
} else if (IsCreating || InputHasChanged(TimeInput)) { } else if (IsCreating || InputHasChanged(TimeInput)) {
d.TimeString = (TimeInput.Text != "") ? TimeInput.Text + ":00" : null; d.TimeString = (TimeInput.Text != "") ? TimeInput.Text + ":00" : null;
} }
d.ZwstId = (BranchInput.SelectedItem as Branch)?.ZwstId; d.ZwstId = (BranchInput.SelectedItem as Branch)!.ZwstId;
d.LsNr = LsNrInput.Text; d.LsNr = LsNrInput.Text;
d.MgNr = int.Parse(MgNrInput.Text); d.MgNr = int.Parse(MgNrInput.Text);
d.Comment = (CommentInput.Text == "") ? null : CommentInput.Text; d.Comment = (CommentInput.Text == "") ? null : CommentInput.Text;
p.SortId = (WineVarietyInput.SelectedItem as WineVar)?.SortId; p.SortId = (WineVarietyInput.SelectedItem as WineVar)!.SortId;
p.AttrId = (AttributeInput.SelectedItem as WineAttr)?.AttrId; p.AttrId = (AttributeInput.SelectedItem as WineAttr)?.AttrId;
p.CultId = (CultivationInput.SelectedItem as WineCult)?.CultId;
p.Kmw = double.Parse(GradationKmwInput.Text); p.Kmw = double.Parse(GradationKmwInput.Text);
p.QualId = (WineQualityLevelInput.SelectedItem as WineQualLevel)?.QualId; p.QualId = (WineQualityLevelInput.SelectedItem as WineQualLevel)!.QualId;
p.HkId = (WineOriginInput.SelectedItem as WineOrigin)?.HkId; p.HkId = (WineOriginInput.SelectedItem as WineOrigin)!.HkId;
p.KgNr = (WineKgInput.SelectedItem as AT_Kg)?.KgNr; p.KgNr = (WineKgInput.SelectedItem as AT_Kg)?.KgNr;
p.RdNr = (WineRdInput.SelectedItem as WbRd)?.RdNr; p.RdNr = (WineRdInput.SelectedItem as WbRd)?.RdNr;
p.IsGerebelt = GerebeltGewogenInput.IsChecked ?? false; p.IsNetWeight = GerebeltGewogenInput.IsChecked ?? false;
p.IsHandPicked = HandPickedInput.IsChecked; p.IsHandPicked = HandPickedInput.IsChecked;
p.IsLesewagen = LesewagenInput.IsChecked; p.IsLesewagen = LesewagenInput.IsChecked;
p.IsGebunden = GebundenInput.IsChecked; p.IsGebunden = GebundenInput.IsChecked;
@ -912,7 +959,7 @@ namespace Elwig.Windows {
p.Comment = (PartCommentInput.Text == "") ? null : PartCommentInput.Text; p.Comment = (PartCommentInput.Text == "") ? null : PartCommentInput.Text;
p.Weight = int.Parse(WeightInput.Text.Replace(Utils.GroupSeparator, "")); p.Weight = int.Parse(WeightInput.Text.Replace(Utils.GroupSeparator, ""));
p.ManualWeighing = ManualWeighingInput.IsChecked ?? false; p.IsManualWeighing = ManualWeighingInput.IsChecked ?? false;
p.ScaleId = ScaleId; p.ScaleId = ScaleId;
p.WeighingId = WeighingId; p.WeighingId = WeighingId;
p.WeighingReason = ManualWeighingReason; p.WeighingReason = ManualWeighingReason;
@ -964,38 +1011,50 @@ namespace Elwig.Windows {
private async void WeighingButton_Click(int index) { private async void WeighingButton_Click(int index) {
DisableWeighingButtons(); DisableWeighingButtons();
var start = DateTimeOffset.Now.ToUnixTimeMilliseconds();
FinishButton.IsEnabled = false; FinishButton.IsEnabled = false;
NewDeliveryPartButton.IsEnabled = false; NewDeliveryPartButton.IsEnabled = false;
CancelCreatingButton.IsEnabled = false; CancelCreatingButton.IsEnabled = false;
var s = App.CommandScales[index];
try { try {
var s = App.Scales[index]; var res = await s.Weigh();
if (s is not ICommandScale cs) return; OnWeighingResult(s, res);
var res = await cs.Weigh(); } catch (Exception ex) {
LastScaleError = ex.Message.Split(": ")[^1];
OnWeighingResult(s, new() { Weight = 0 });
MessageBox.Show($"Beim Wiegen ist ein Fehler aufgetreten:\n\n{ex.Message}", "Waagenfehler",
MessageBoxButton.OK, MessageBoxImage.Error);
}
ManualWeighingReason = null;
ManualWeighingInput.IsChecked = false;
var end = DateTimeOffset.Now.ToUnixTimeMilliseconds();
int diff = (int)(end - start);
if (diff < 1000 && WeightInput.Text.Length != 0) await Task.Delay(1000 - diff);
EnableWeighingButtons();
}
private void OnWeighingResult(IScale scale, WeighingResult res) {
if ((res.Weight ?? 0) > 0 && res.FullWeighingId != null) { if ((res.Weight ?? 0) > 0 && res.FullWeighingId != null) {
WeightInput.Text = $"{res.Weight:N0}"; WeightInput.Text = $"{res.Weight:N0}";
ScaleId = s.ScaleId; ScaleId = scale.ScaleId;
WeighingId = res.FullWeighingId; WeighingId = res.FullWeighingId;
ManualWeighingReason = null;
ManualWeighingInput.IsChecked = false;
} else { } else {
WeightInput.Text = ""; WeightInput.Text = "";
ScaleId = null; ScaleId = null;
WeighingId = null; WeighingId = null;
} }
LastScaleError = null; LastScaleError = null;
} catch (Exception e) { TextBox_TextChanged(WeightInput, null);
LastScaleError = e.Message.Split(": ")[^1];
WeightInput.Text = "";
ScaleId = null;
WeighingId = null;
MessageBox.Show($"Beim Wiegen ist ein Fehler aufgetreten:\n\n{e.Message}", "Waagenfehler",
MessageBoxButton.OK, MessageBoxImage.Error);
}
ManualWeighingReason = null;
ManualWeighingInput.IsChecked = false;
base.TextBox_TextChanged(WeightInput, null);
EnableWeighingButtons();
UpdateButtons(); UpdateButtons();
} }
private void Scale_Weighing(object sender, WeighingEventArgs evt) {
if (sender is not IScale scale) return;
App.MainDispatcher.BeginInvoke(() => OnWeighingResult(scale, evt.Result));
}
private async void SearchInput_TextChanged(object sender, RoutedEventArgs evt) { private async void SearchInput_TextChanged(object sender, RoutedEventArgs evt) {
TextFilter = SearchInput.Text.ToLower().Split(" ").ToList().FindAll(e => e.Length > 0); TextFilter = SearchInput.Text.ToLower().Split(" ").ToList().FindAll(e => e.Length > 0);
await RefreshDeliveryListQuery(true); await RefreshDeliveryListQuery(true);
@ -1113,14 +1172,18 @@ namespace Elwig.Windows {
await RefreshDeliveryParts(); await RefreshDeliveryParts();
if (p?.Delivery != null) { if (p?.Delivery != null) {
Mouse.OverrideCursor = Cursors.AppStarting; Mouse.OverrideCursor = Cursors.AppStarting;
try {
using var doc = new DeliveryNote(p.Delivery, Context); using var doc = new DeliveryNote(p.Delivery, Context);
await doc.Generate(); await doc.Generate();
Mouse.OverrideCursor = null;
if (App.Config.Debug) { if (App.Config.Debug) {
doc.Show(); doc.Show();
} else { } else {
await doc.Print(2); await doc.Print(2);
} }
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
} }
FinishButton.Cursor = null; FinishButton.Cursor = null;
DeliveryList.SelectedItem = null; DeliveryList.SelectedItem = null;
@ -1511,11 +1574,11 @@ namespace Elwig.Windows {
private void EnableWeighingButtons() { private void EnableWeighingButtons() {
WeighingManualButton.IsEnabled = true; WeighingManualButton.IsEnabled = true;
var n = App.Scales.Count; var n = App.CommandScales.Count;
WeighingAButton.IsEnabled = n > 0 && App.Scales[0].IsReady; WeighingAButton.IsEnabled = n > 0 && App.CommandScales[0].IsReady;
WeighingBButton.IsEnabled = n > 1 && App.Scales[1].IsReady; WeighingBButton.IsEnabled = n > 1 && App.CommandScales[1].IsReady;
WeighingCButton.IsEnabled = n > 2 && App.Scales[2].IsReady; WeighingCButton.IsEnabled = n > 2 && App.CommandScales[2].IsReady;
WeighingDButton.IsEnabled = n > 3 && App.Scales[3].IsReady; WeighingDButton.IsEnabled = n > 3 && App.CommandScales[3].IsReady;
} }
private async Task UpdateLsNr() { private async Task UpdateLsNr() {
@ -1552,11 +1615,13 @@ namespace Elwig.Windows {
WineVarietyInput.SelectedItem = Context.WineVarieties.Find(text[0..2]); WineVarietyInput.SelectedItem = Context.WineVarieties.Find(text[0..2]);
if (text.Length >= 3) { if (text.Length >= 3) {
ControlUtils.SelectComboBoxItem(AttributeInput, Context.WineAttributes.Find(text[2..]), a => (a as WineAttr)?.AttrId); ControlUtils.SelectComboBoxItem(AttributeInput, Context.WineAttributes.Find(text[2..]), a => (a as WineAttr)?.AttrId);
ControlUtils.SelectComboBoxItem(CultivationInput, Context.WineCultivations.Find(text[2..]), i => (i as WineCult)?.CultId);
SortIdInput.Text = text[0..2]; SortIdInput.Text = text[0..2];
} }
} else { } else {
WineVarietyInput.SelectedItem = null; WineVarietyInput.SelectedItem = null;
AttributeInput.SelectedIndex = 0; AttributeInput.SelectedIndex = 0;
CultivationInput.SelectedIndex = 0;
} }
} }
@ -1637,6 +1702,10 @@ namespace Elwig.Windows {
} }
private void CultivationInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
}
private void ModifiersInput_SelectionChanged(object sender, ItemSelectionChangedEventArgs evt) { private void ModifiersInput_SelectionChanged(object sender, ItemSelectionChangedEventArgs evt) {
if (!IsEditing && !IsCreating) return; if (!IsEditing && !IsCreating) return;
var mod = ModifiersInput.SelectedItems.Cast<Modifier>(); var mod = ModifiersInput.SelectedItems.Cast<Modifier>();
@ -1731,17 +1800,22 @@ namespace Elwig.Windows {
} }
private void GerebeltGewogenInput_Changed(object sender, RoutedEventArgs evt) { private void GerebeltGewogenInput_Changed(object sender, RoutedEventArgs evt) {
if (App.Client.HasKisten(BranchInput.SelectedValue as Branch)) { if (!App.Client.HasNetWeighing(BranchInput.SelectedValue as Branch)) {
HandPickedInput.IsChecked = !GerebeltGewogenInput.IsChecked; HandPickedInput.IsChecked = !GerebeltGewogenInput.IsChecked;
} }
CheckBox_Changed(sender, evt); CheckBox_Changed(sender, evt);
} }
private void HandPickedInput_Changed(object sender, RoutedEventArgs evt) { private void HandPickedInput_Changed(object sender, RoutedEventArgs evt) {
if (App.Client.HasKisten(BranchInput.SelectedValue as Branch)) { if (!App.Client.HasNetWeighing(BranchInput.SelectedValue as Branch)) {
GerebeltGewogenInput.IsChecked = !HandPickedInput.IsChecked; GerebeltGewogenInput.IsChecked = !HandPickedInput.IsChecked;
} }
CheckBox_Changed(sender, evt); CheckBox_Changed(sender, evt);
} }
private void MemberReferenceButton_Click(object sender, RoutedEventArgs evt) {
if (MemberInput.SelectedItem is not Member m) return;
App.FocusMember(m.MgNr);
}
} }
} }

View File

@ -1,35 +0,0 @@
<local:ContextWindow x:Class="Elwig.Dialogs.DeliveryConfirmationsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows"
mc:Ignorable="d"
Loaded="Window_Loaded"
Title="Anlieferungsbestätingungen - Elwig" Height="500" Width="800" MinHeight="400" MinWidth="600">
<Grid>
<GroupBox Header="Sortieren nach" Margin="10,10,10,10" Width="180" Height="80" VerticalAlignment="Top" HorizontalAlignment="Left">
<StackPanel Margin="5,5,0,5">
<RadioButton GroupName="Order" x:Name="OrderMgNrInput" Content="Mitgliedsnummer" IsChecked="True"/>
<RadioButton GroupName="Order" x:Name="OrderNameInput" Content="Name"/>
<RadioButton GroupName="Order" x:Name="OrderPlzInput" Content="PLZ, Ort, Name"/>
</StackPanel>
</GroupBox>
<CheckBox x:Name="AllMembersInput" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,100,10,10">
<TextBlock>Auch Mitglieder ohne<LineBreak/>Lieferungen miteinbeziehen</TextBlock>
</CheckBox>
<TextBox x:Name="TextElement" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" AcceptsReturn="True"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="200,10,10,10" Height="Auto"/>
<ProgressBar x:Name="ProgressBar" Margin="10,0,0,74" Height="27" Width="180"
VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
<Button x:Name="ShowButton" Content="Vorschau" FontSize="14" Width="180" Margin="10,10,10,42" Height="27" Tag="Print" IsEnabled="False"
Click="ShowButton_Click"
VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
<Button x:Name="PrintButton" Content="Drucken" FontSize="14" Width="180" Margin="10,10,10,10" Height="27" Tag="Print" IsEnabled="False"
Click="PrintButton_Click"
VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
</Grid>
</local:ContextWindow>

View File

@ -1,109 +0,0 @@
using Elwig.Documents;
using Elwig.Models.Dtos;
using Elwig.Models.Entities;
using Elwig.Windows;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace Elwig.Dialogs {
public partial class DeliveryConfirmationsWindow : ContextWindow {
public readonly int Year;
public DeliveryConfirmationsWindow(int year) {
InitializeComponent();
Year = year;
Title = $"Anlieferungsbestätigungen - Lese {Year} - Elwig";
TextElement.Text = App.Client.TextDeliveryConfirmation;
}
private void Window_Loaded(object sender, RoutedEventArgs evt) {
ShowButton.IsEnabled = App.IsPrintingReady;
PrintButton.IsEnabled = App.IsPrintingReady;
}
protected override async Task OnRenewContext() { }
private async Task UpdateTextParameter() {
var text = TextElement.Text;
if (text.Length == 0) text = null;
if (text != App.Client.TextDeliveryConfirmation) {
App.Client.TextDeliveryConfirmation = text;
await App.Client.UpdateValues();
}
}
private async Task Generate(int mode) {
Mouse.OverrideCursor = Cursors.AppStarting;
await UpdateTextParameter();
IQueryable<Member> members;
if (AllMembersInput.IsChecked == true) {
members = Context.Members.Where(m => m.IsActive);
} else {
members = Context.Members.FromSqlRaw($"""
SELECT m.*
FROM member m
INNER JOIN delivery d ON d.mgnr = m.mgnr
WHERE d.year = {Year}
GROUP BY m.mgnr
""");
}
if (OrderMgNrInput.IsChecked == true) {
members = members
.OrderBy(m => m.MgNr);
} else if (OrderNameInput.IsChecked == true) {
members = members
.OrderBy(m => m.FamilyName)
.ThenBy(m => m.GivenName)
.ThenBy(m => m.MgNr);
} else if (OrderPlzInput.IsChecked == true) {
members = members
.OrderBy(m => m.PostalDest.AtPlz.Plz)
.ThenBy(m => m.PostalDest.AtPlz.Ort.Name)
.ThenBy(m => m.FamilyName)
.ThenBy(m => m.GivenName)
.ThenBy(m => m.MgNr);
}
IEnumerable<Member> list = await members.ToListAsync();
var data = await DeliveryConfirmationDeliveryData.ForSeason(Context.DeliveryParts, Year);
using var doc = Document.Merge(list.Select(m =>
new DeliveryConfirmation(Context, Year, m, data.TryGetValue(m.MgNr, out var d) ? d : DeliveryConfirmationDeliveryData.CreateEmpty(Year, m)) {
//DoubleSided = true
}
));
//doc.DoubleSided = true;
await doc.Generate(new Progress<double>(v => {
ProgressBar.Value = v;
}));
Mouse.OverrideCursor = null;
if (mode < 2) {
doc.Show();
return;
}
if (App.Config.Debug) {
doc.Show();
} else {
await doc.Print();
}
Close();
}
private async void ShowButton_Click(object sender, RoutedEventArgs evt) {
await Generate(1);
}
private async void PrintButton_Click(object sender, RoutedEventArgs evt) {
await Generate(2);
}
}
}

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