Compare commits
246 Commits
df4e254305
...
v0.4.1
Author | SHA1 | Date | |
---|---|---|---|
ddd83bf63d | |||
685793f0fb | |||
f6712704ee | |||
de500854c4 | |||
d992b6a206 | |||
d2b96736bb | |||
50b9f4e207 | |||
45fc0893b1 | |||
c4dd56075d | |||
f1084c716a | |||
2f8e4ca812 | |||
450f5d8109 | |||
62d6707d10 | |||
2bcf26cc8d | |||
daddd069a3 | |||
3b3489b492 | |||
505ee0ad24 | |||
0b79fa192e | |||
ff2968c989 | |||
8c49cc8ef2 | |||
f0751499ea | |||
d4dd84394b | |||
3b4340b5e8 | |||
4db147e582 | |||
c997acc95d | |||
f8126c392e | |||
b6d74d3c07 | |||
62bf425313 | |||
5657a8f90a | |||
56578a0a9d | |||
e71d9516ec | |||
658c10c2a4 | |||
ff3defe52d | |||
a489f13d99 | |||
20d4bbf7ac | |||
604094a603 | |||
522fed818f | |||
3930534273 | |||
4d950b2597 | |||
badf4ce955 | |||
7d8c670ed2 | |||
91a3786cd9 | |||
73af12a64d | |||
0e9bae4ec9 | |||
9df8056616 | |||
9e9195b9c0 | |||
1625f15f92 | |||
6a8bd9c932 | |||
05da8eefac | |||
04badb658b | |||
8f2f5b28cf | |||
d5e4e0a29d | |||
96570dffd4 | |||
e79f4baa2f | |||
b79ba14f9e | |||
de298ffef1 | |||
eaf7b6bd41 | |||
2bb8205da0 | |||
f623aa1fee | |||
00e7eeb774 | |||
47d51ded51 | |||
532bb826e1 | |||
8193bf483c | |||
34dcaf26d9 | |||
7411f570ee | |||
52702f3fa2 | |||
7f3573cede | |||
72359dc8be | |||
0e17aa5408 | |||
2bf850bc55 | |||
aadf536d13 | |||
3be6371be1 | |||
ca1b68aa4f | |||
d4e5ac6753 | |||
c9f49927a8 | |||
1794b5b8ca | |||
7347439034 | |||
51ad8f99fd | |||
39279a5dda | |||
5271f357f5 | |||
13ba3f90f6 | |||
43be8bf391 | |||
826a76c56c | |||
efaae5f490 | |||
3a73265a75 | |||
a6fef7fd9b | |||
a08df4c3ed | |||
9701af9e36 | |||
b4f1eeee84 | |||
2922fe0138 | |||
704facbc6b | |||
404e8a0c27 | |||
ef621fab2d | |||
0938e33fe1 | |||
6b5c283e10 | |||
b6400c41c4 | |||
24b7078a05 | |||
dc215d3350 | |||
b80cbc037c | |||
4891501f62 | |||
6d3adc48f6 | |||
f5eea1e906 | |||
ba691f4d17 | |||
470f092482 | |||
e9e4c75edd | |||
ff1a4e7182 | |||
1e9cad6de7 | |||
62fe087598 | |||
7f01b85878 | |||
a659d07db2 | |||
b2a78907cf | |||
7bcf532b26 | |||
595f9a049c | |||
80ed90941d | |||
77cee53f2d | |||
1a673f4b3a | |||
5ad8c88319 | |||
30aaa64f59 | |||
352bf840c3 | |||
898cece0d3 | |||
0b05cc4e10 | |||
74fa08e95d | |||
bc6148624c | |||
f5f00a7739 | |||
2de4739e9d | |||
47e8ab7e62 | |||
8a678509c5 | |||
de53bfdd2b | |||
f9d95a48f2 | |||
be734b880f | |||
1c45e95ef3 | |||
7086a72fab | |||
540236f878 | |||
aab95ee444 | |||
2b7d19199a | |||
28b424fe65 | |||
324c5db94e | |||
faaeefe6ce | |||
b76d43a5ff | |||
8b48882c86 | |||
286279b89f | |||
c933fa3423 | |||
0958728418 | |||
cb8d405dcf | |||
6f4e3474b8 | |||
545622a2ab | |||
3a0736a73f | |||
048a7e0f13 | |||
cad475fc20 | |||
8b0f1e35c8 | |||
67dbf62eb7 | |||
9b59208373 | |||
146560c796 | |||
286e3515d4 | |||
c1d3a9042d | |||
e656bde54d | |||
33d763d968 | |||
56a0eb794e | |||
effc9f9230 | |||
4817a2aedc | |||
b67628c566 | |||
5aa8e45652 | |||
1f377483a5 | |||
9b84452b86 | |||
75e6c1bd5d | |||
a96a05d3de | |||
3f09e62c74 | |||
a98bc3ae4a | |||
93d4365d36 | |||
33e10d00bf | |||
7c2bf711c7 | |||
ff7fe34d5b | |||
973f49fabc | |||
8899e8bd32 | |||
6dda9e09cf | |||
c0a6f16374 | |||
ba71618463 | |||
6261217e7c | |||
5dd56d9666 | |||
83a82c17c6 | |||
03cf88a058 | |||
4bb0acb4a2 | |||
04db3fe0c5 | |||
28bb8f2d3f | |||
5074f945cf | |||
3a12837706 | |||
cfa1f1eeea | |||
85e4ccd215 | |||
0e13e269d0 | |||
4aa8fe2e2d | |||
a94bf651de | |||
00078a1f4e | |||
ded7dbec06 | |||
8534ff6bba | |||
f735da0059 | |||
5093706a4a | |||
8f7d699b4e | |||
b87710cc67 | |||
0d609c1013 | |||
58235e3b02 | |||
3ab2b8a8dc | |||
258b2b3c5b | |||
9e1ea293aa | |||
12a81bd924 | |||
84edec2b54 | |||
c871dea873 | |||
99a6f53307 | |||
f8af155c60 | |||
244de52b19 | |||
0ea77656e3 | |||
65bcc7bbb1 | |||
cbb57f7039 | |||
644362f492 | |||
b11dcd273d | |||
041e674af9 | |||
c65ffaf161 | |||
e7cb698026 | |||
2800b8b3e6 | |||
94a0adc55a | |||
a24edfffa3 | |||
dfda5e36c8 | |||
29b8f046f4 | |||
8290b57cd4 | |||
e1630e9919 | |||
91a20662da | |||
7af27ab5de | |||
f4c24d9578 | |||
1226e85c4a | |||
d05eb145b6 | |||
a984c4f16c | |||
921871cab1 | |||
baf694621c | |||
75dfdb155b | |||
704337aa1d | |||
a62fb9ab16 | |||
d06ea4f045 | |||
69d89ecd56 | |||
4571b06279 | |||
c68c798b49 | |||
bfedf436d8 | |||
fd7a15bc5a | |||
5cee928978 | |||
75322da405 | |||
9ba45e58f6 | |||
d43633cb65 | |||
cf1b1f7865 |
68
Elwig.sln
68
Elwig.sln
@@ -4,22 +4,88 @@ VisualStudioVersion = 17.3.32929.385
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elwig", "Elwig\Elwig.csproj", "{00868460-16F6-4B48-AA9B-998F6263693B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{30D7700A-7B0A-4E5D-B839-B4C1D95E307E}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{30D7700A-7B0A-4E5D-B839-B4C1D95E307E}"
|
||||
EndProject
|
||||
Project("{B7DD6F7E-DEF8-4E67-B5B7-07EF123DB6F0}") = "Installer", "Installer\Installer.wixproj", "{4A82C9C0-EB6D-409C-B0B2-09A5F727D16F}"
|
||||
EndProject
|
||||
Project("{B7DD6F7E-DEF8-4E67-B5B7-07EF123DB6F0}") = "Setup", "Setup\Setup.wixproj", "{952E309C-2090-4978-8996-65900D8E8FA4}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|ARM64 = Debug|ARM64
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|ARM64 = Release|ARM64
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{00868460-16F6-4B48-AA9B-998F6263693B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{00868460-16F6-4B48-AA9B-998F6263693B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{00868460-16F6-4B48-AA9B-998F6263693B}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{00868460-16F6-4B48-AA9B-998F6263693B}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||
{00868460-16F6-4B48-AA9B-998F6263693B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{00868460-16F6-4B48-AA9B-998F6263693B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{00868460-16F6-4B48-AA9B-998F6263693B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{00868460-16F6-4B48-AA9B-998F6263693B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{00868460-16F6-4B48-AA9B-998F6263693B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{00868460-16F6-4B48-AA9B-998F6263693B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{00868460-16F6-4B48-AA9B-998F6263693B}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||
{00868460-16F6-4B48-AA9B-998F6263693B}.Release|ARM64.Build.0 = Release|Any CPU
|
||||
{00868460-16F6-4B48-AA9B-998F6263693B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{00868460-16F6-4B48-AA9B-998F6263693B}.Release|x64.Build.0 = Release|Any CPU
|
||||
{00868460-16F6-4B48-AA9B-998F6263693B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{00868460-16F6-4B48-AA9B-998F6263693B}.Release|x86.Build.0 = Release|Any CPU
|
||||
{30D7700A-7B0A-4E5D-B839-B4C1D95E307E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{30D7700A-7B0A-4E5D-B839-B4C1D95E307E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{30D7700A-7B0A-4E5D-B839-B4C1D95E307E}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{30D7700A-7B0A-4E5D-B839-B4C1D95E307E}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||
{30D7700A-7B0A-4E5D-B839-B4C1D95E307E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{30D7700A-7B0A-4E5D-B839-B4C1D95E307E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{30D7700A-7B0A-4E5D-B839-B4C1D95E307E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{30D7700A-7B0A-4E5D-B839-B4C1D95E307E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{30D7700A-7B0A-4E5D-B839-B4C1D95E307E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{30D7700A-7B0A-4E5D-B839-B4C1D95E307E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{30D7700A-7B0A-4E5D-B839-B4C1D95E307E}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||
{30D7700A-7B0A-4E5D-B839-B4C1D95E307E}.Release|ARM64.Build.0 = Release|Any CPU
|
||||
{30D7700A-7B0A-4E5D-B839-B4C1D95E307E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{30D7700A-7B0A-4E5D-B839-B4C1D95E307E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{30D7700A-7B0A-4E5D-B839-B4C1D95E307E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{30D7700A-7B0A-4E5D-B839-B4C1D95E307E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{4A82C9C0-EB6D-409C-B0B2-09A5F727D16F}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{4A82C9C0-EB6D-409C-B0B2-09A5F727D16F}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{4A82C9C0-EB6D-409C-B0B2-09A5F727D16F}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{4A82C9C0-EB6D-409C-B0B2-09A5F727D16F}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{4A82C9C0-EB6D-409C-B0B2-09A5F727D16F}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{4A82C9C0-EB6D-409C-B0B2-09A5F727D16F}.Debug|x64.Build.0 = Debug|x64
|
||||
{4A82C9C0-EB6D-409C-B0B2-09A5F727D16F}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{4A82C9C0-EB6D-409C-B0B2-09A5F727D16F}.Debug|x86.Build.0 = Debug|x86
|
||||
{4A82C9C0-EB6D-409C-B0B2-09A5F727D16F}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{4A82C9C0-EB6D-409C-B0B2-09A5F727D16F}.Release|Any CPU.Build.0 = Release|x64
|
||||
{4A82C9C0-EB6D-409C-B0B2-09A5F727D16F}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{4A82C9C0-EB6D-409C-B0B2-09A5F727D16F}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{4A82C9C0-EB6D-409C-B0B2-09A5F727D16F}.Release|x64.ActiveCfg = Release|x64
|
||||
{4A82C9C0-EB6D-409C-B0B2-09A5F727D16F}.Release|x64.Build.0 = Release|x64
|
||||
{4A82C9C0-EB6D-409C-B0B2-09A5F727D16F}.Release|x86.ActiveCfg = Release|x86
|
||||
{4A82C9C0-EB6D-409C-B0B2-09A5F727D16F}.Release|x86.Build.0 = Release|x86
|
||||
{952E309C-2090-4978-8996-65900D8E8FA4}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{952E309C-2090-4978-8996-65900D8E8FA4}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{952E309C-2090-4978-8996-65900D8E8FA4}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{952E309C-2090-4978-8996-65900D8E8FA4}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{952E309C-2090-4978-8996-65900D8E8FA4}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{952E309C-2090-4978-8996-65900D8E8FA4}.Debug|x64.Build.0 = Debug|x64
|
||||
{952E309C-2090-4978-8996-65900D8E8FA4}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{952E309C-2090-4978-8996-65900D8E8FA4}.Debug|x86.Build.0 = Debug|x86
|
||||
{952E309C-2090-4978-8996-65900D8E8FA4}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{952E309C-2090-4978-8996-65900D8E8FA4}.Release|Any CPU.Build.0 = Release|x64
|
||||
{952E309C-2090-4978-8996-65900D8E8FA4}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{952E309C-2090-4978-8996-65900D8E8FA4}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{952E309C-2090-4978-8996-65900D8E8FA4}.Release|x64.ActiveCfg = Release|x64
|
||||
{952E309C-2090-4978-8996-65900D8E8FA4}.Release|x64.Build.0 = Release|x64
|
||||
{952E309C-2090-4978-8996-65900D8E8FA4}.Release|x86.ActiveCfg = Release|x86
|
||||
{952E309C-2090-4978-8996-65900D8E8FA4}.Release|x86.Build.0 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@@ -11,17 +11,13 @@
|
||||
<DataTemplate x:Key="PostalDestTemplate">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{Binding Dest}"/>
|
||||
<TextBlock Text=" ("/>
|
||||
<TextBlock Text="{Binding Ort.Name}"/>
|
||||
<TextBlock Text=")"/>
|
||||
<TextBlock Text="{Binding Ort.Name, StringFormat='{} ({0})'}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="KgNrTemplate">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{Binding Name}"/>
|
||||
<TextBlock Text=" ("/>
|
||||
<TextBlock Text="{Binding KgNr}"/>
|
||||
<TextBlock Text=")"/>
|
||||
<TextBlock Text="{Binding KgNr, StringFormat='{} ({0:00000})'}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="MemberAdminNameTemplate">
|
||||
@@ -55,11 +51,17 @@
|
||||
|
||||
<DataTemplate x:Key="ModifierTemplate">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{Binding Name}" MinWidth="150" Margin="0,0,10,0"/>
|
||||
<TextBlock Text="{Binding Name}" MinWidth="250" Margin="0,0,10,0"/>
|
||||
<TextBlock Text="{Binding ValueStr}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="WineAttributeTemplate">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{Binding Name}" MinWidth="150" Margin="0,0,10,0"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
|
||||
<ControlTemplate x:Key="WineQualityLevelTemplateSimple">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{Binding Name}"/>
|
||||
|
@@ -11,31 +11,56 @@ using System.Windows.Threading;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using System.Windows.Markup;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Elwig {
|
||||
public partial class App : Application {
|
||||
|
||||
public static readonly string DataPath = @"C:\ProgramData\Elwig\";
|
||||
public static readonly string ExePath = @"C:\Program Files\Elwig\";
|
||||
public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig");
|
||||
public static readonly Config Config = new(DataPath + "config.ini");
|
||||
|
||||
public static int VersionMajor { get; private set; }
|
||||
public static int VersionMinor { get; private set; }
|
||||
public static int VersionPatch { get; private set; }
|
||||
public static string Version {
|
||||
get => $"{VersionMajor}.{VersionMinor}.{VersionPatch}";
|
||||
private set {
|
||||
var p = value.Split(".").Select(p => int.Parse(p.Trim())).ToArray();
|
||||
VersionMajor = p.ElementAtOrDefault(0);
|
||||
VersionMinor = p.ElementAtOrDefault(1);
|
||||
VersionPatch = p.ElementAtOrDefault(2);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ZwstId { get; private set; }
|
||||
public static IEnumerable<IScale> Scales { get; private set; }
|
||||
public static ClientParameters Client { get; private set; }
|
||||
public static string BranchName { get; private set; }
|
||||
public static int? BranchPlz { get; private set; }
|
||||
public static string? BranchLocation { get; private set; }
|
||||
public static string? BranchAddress { get; private set; }
|
||||
public static string? BranchPhoneNr { get; private set; }
|
||||
public static string? BranchFaxNr { get; private set; }
|
||||
public static string? BranchMobileNr { get; private set; }
|
||||
public static IList<IScale> Scales { get; private set; }
|
||||
public static ClientParameters Client { get; private set; }
|
||||
|
||||
public static bool IsPrintingReady => Documents.Html.IsReady && Documents.Pdf.IsReady;
|
||||
public static Dispatcher MainDispatcher { get; private set; }
|
||||
|
||||
public App() : base() {
|
||||
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
|
||||
Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), "Elwig"));
|
||||
Directory.CreateDirectory(TempPath);
|
||||
Directory.CreateDirectory(DataPath);
|
||||
MainDispatcher = Dispatcher;
|
||||
Scales = Array.Empty<IScale>();
|
||||
}
|
||||
|
||||
protected override void OnStartup(StartupEventArgs evt) {
|
||||
var locale = new CultureInfo("de-DE"); // de-AT uses ' ' as thousands separator :(
|
||||
var locale = new CultureInfo("de-AT");
|
||||
locale.NumberFormat.CurrencyGroupSeparator = "\u202f";
|
||||
locale.NumberFormat.NumberGroupSeparator = "\u202f";
|
||||
locale.NumberFormat.PercentGroupSeparator = "\u202f";
|
||||
Thread.CurrentThread.CurrentCulture = locale;
|
||||
Thread.CurrentThread.CurrentUICulture = locale;
|
||||
CultureInfo.DefaultThreadCurrentCulture = locale;
|
||||
@@ -45,36 +70,49 @@ namespace Elwig {
|
||||
new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag))
|
||||
);
|
||||
|
||||
Dictionary<string, string> branches = new();
|
||||
Version = typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ?? "";
|
||||
|
||||
try {
|
||||
AppDbUpdater.CheckDb();
|
||||
} catch (Exception e) {
|
||||
MessageBox.Show($"Invalid Database:\n\n{e.Message}", "Invalid Database", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
Shutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<string, (string, string, int?, string?, string?, string?, string?, string?)> branches = new();
|
||||
using (var ctx = new AppDbContext()) {
|
||||
if (!ctx.Database.CanConnect()) {
|
||||
MessageBox.Show($"Invalid Database:\n\n{Config.DatabaseFile}", "Invalid Database", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
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 {
|
||||
Client = new(ctx);
|
||||
} catch (Exception e) {
|
||||
MessageBox.Show($"Fehler beim Laden der Mandantendaten:\n\n{e.Message}", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
Shutdown();
|
||||
} else {
|
||||
branches = ctx.Branches.ToDictionary(b => b.Name.ToLower(), b => b.ZwstId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Utils.RunBackground("HTML Initialization", () => Documents.Html.Init(PrintingReadyChanged));
|
||||
Utils.RunBackground("PDF Initialization", () => Documents.Pdf.Init(PrintingReadyChanged));
|
||||
|
||||
Client = new();
|
||||
|
||||
var list = new LinkedList<IScale>();
|
||||
var list = new List<IScale>();
|
||||
foreach (var s in Config.Scales) {
|
||||
var id = s[0];
|
||||
try {
|
||||
var scaleNr = int.Parse(s[0]);
|
||||
var type = s[1].ToLower();
|
||||
var type = s[1]?.ToLower();
|
||||
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.AddLast(new SystecScale(scaleNr, model, cnx, empty, filling, limit));
|
||||
list.Add(new SystecScale(id, model, cnx, empty, filling, limit, log));
|
||||
} else {
|
||||
throw new ArgumentException($"Invalid scale type: \"{type}\"");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
list.Add(new InvalidScale(id));
|
||||
MessageBox.Show($"Unable to create scale {s[0]}:\n\n{e.Message}", "Scale Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
@@ -85,10 +123,26 @@ namespace Elwig {
|
||||
MessageBox.Show("Invalid branch name in config!", "Invalid Branch Config", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
Shutdown();
|
||||
} else {
|
||||
ZwstId = branches[Config.Branch.ToLower()];
|
||||
var entry = branches[Config.Branch.ToLower()];
|
||||
ZwstId = entry.Item1;
|
||||
BranchName = entry.Item2;
|
||||
BranchPlz = entry.Item3;
|
||||
BranchLocation = entry.Item4;
|
||||
BranchAddress = entry.Item5;
|
||||
BranchPhoneNr = entry.Item6;
|
||||
BranchFaxNr = entry.Item7;
|
||||
BranchMobileNr = entry.Item8;
|
||||
}
|
||||
} else if (branches.Count == 1) {
|
||||
ZwstId = branches.First().Value;
|
||||
var entry = branches.First().Value;
|
||||
ZwstId = entry.Item1;
|
||||
BranchName = entry.Item2;
|
||||
BranchPlz = entry.Item3;
|
||||
BranchLocation = entry.Item4;
|
||||
BranchAddress = entry.Item5;
|
||||
BranchPhoneNr = entry.Item6;
|
||||
BranchFaxNr = entry.Item7;
|
||||
BranchMobileNr = entry.Item8;
|
||||
} else {
|
||||
MessageBox.Show("Unable to determine local branch!", "Invalid Branch Config", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
Shutdown();
|
||||
@@ -97,20 +151,18 @@ namespace Elwig {
|
||||
base.OnStartup(evt);
|
||||
}
|
||||
|
||||
protected override void OnExit(ExitEventArgs evt) {
|
||||
Utils.RunBackground("PDF Close", () => Documents.Pdf.Close());
|
||||
base.OnExit(evt);
|
||||
}
|
||||
|
||||
private void PrintingReadyChanged() {
|
||||
Dispatcher.BeginInvoke(OnPrintingReadyChanged, new EventArgs());
|
||||
}
|
||||
|
||||
protected void OnPrintingReadyChanged(EventArgs evt) {
|
||||
foreach (Window w in Windows) {
|
||||
foreach (var b in Utils.FindAllChildren<Button>(w).Where(b => "Print".Equals(b.Tag))) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
59
Elwig/Dialogs/AbwertenDialog.xaml
Normal file
59
Elwig/Dialogs/AbwertenDialog.xaml
Normal file
@@ -0,0 +1,59 @@
|
||||
<Window x:Class="Elwig.Dialogs.AbwertenDialog"
|
||||
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.Dialogs"
|
||||
mc:Ignorable="d"
|
||||
ResizeMode="NoResize"
|
||||
ShowInTaskbar="False"
|
||||
Topmost="True"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
FocusManager.FocusedElement="{Binding ElementName=WeightInput}"
|
||||
Title="Teillieferung abwerten" Height="190" Width="400">
|
||||
<Window.Resources>
|
||||
<Style TargetType="Label">
|
||||
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||
<Setter Property="Padding" Value="2,4,2,4"/>
|
||||
<Setter Property="Height" Value="25"/>
|
||||
</Style>
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="Padding" Value="2"/>
|
||||
<Setter Property="Height" Value="25"/>
|
||||
<Setter Property="TextWrapping" Value="NoWrap"/>
|
||||
</Style>
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||
<Setter Property="VerticalAlignment" Value="Bottom"/>
|
||||
<Setter Property="Width" Value="100"/>
|
||||
<Setter Property="Height" Value="25"/>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="70"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Margin="10,10,10,10" Grid.ColumnSpan="2" TextWrapping="Wrap" TextAlignment="Center">
|
||||
Welche Menge der Teillieferung <Run x:Name="TextLsNr" FontWeight="Bold" Text="20201010A000/1"/><LineBreak/>
|
||||
von <Run x:Name="TextMember" FontWeight="Bold" Text="Max Mustermann"/><LineBreak/>
|
||||
mit <Run x:Name="TextWeight" FontWeight="Bold" Text="1 000 kg"/> soll abgewertet werden?
|
||||
</TextBlock>
|
||||
|
||||
<Label Content="Gewicht:" Margin="10,70,10,10"/>
|
||||
<Grid Grid.Column="1" Width="70" Height="25" Margin="0,70,10,10" HorizontalAlignment="Left" VerticalAlignment="Top">
|
||||
<TextBox x:Name="WeightInput" TextAlignment="Right" Padding="2,2,17,2"
|
||||
TextChanged="WeightInput_TextChanged"/>
|
||||
<Label Content="kg" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10" Padding="2,4,2,4"/>
|
||||
</Grid>
|
||||
|
||||
<Button x:Name="ConfirmButton" Content="Bestätigen" Margin="10,10,115,10" Grid.Column="1" IsEnabled="False" IsDefault="True"
|
||||
Click="ConfirmButton_Click"/>
|
||||
<Button x:Name="CancelButton" Content="Abbrechen" Margin="10,10,10,10" Grid.Column="1" IsCancel="True"/>
|
||||
</Grid>
|
||||
</Window>
|
33
Elwig/Dialogs/AbwertenDialog.xaml.cs
Normal file
33
Elwig/Dialogs/AbwertenDialog.xaml.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using Elwig.Helpers;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Elwig.Dialogs {
|
||||
public partial class AbwertenDialog : Window {
|
||||
|
||||
public int Weight;
|
||||
|
||||
public AbwertenDialog(string lsnr, string name, int weight) {
|
||||
Weight = weight;
|
||||
InitializeComponent();
|
||||
TextLsNr.Text = lsnr;
|
||||
TextMember.Text = name;
|
||||
TextWeight.Text = $"{weight:N0}\u202fkg";
|
||||
}
|
||||
|
||||
private void ConfirmButton_Click(object sender, RoutedEventArgs evt) {
|
||||
DialogResult = true;
|
||||
Weight = int.Parse(WeightInput.Text);
|
||||
Close();
|
||||
}
|
||||
|
||||
private void UpdateButtons() {
|
||||
ConfirmButton.IsEnabled = int.TryParse(WeightInput.Text, out var w) && w > 0 && w <= Weight;
|
||||
}
|
||||
|
||||
private void WeightInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
||||
Validator.CheckInteger(WeightInput, true, 5);
|
||||
UpdateButtons();
|
||||
}
|
||||
}
|
||||
}
|
37
Elwig/Dialogs/DeliveryConfirmationsDialog.xaml
Normal file
37
Elwig/Dialogs/DeliveryConfirmationsDialog.xaml
Normal file
@@ -0,0 +1,37 @@
|
||||
<local:ContextWindow x:Class="Elwig.Dialogs.DeliveryConfirmationsDialog"
|
||||
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"
|
||||
ResizeMode="NoResize"
|
||||
Loaded="Window_Loaded"
|
||||
Title="Anlieferungsbestätingungen - Elwig" Height="400" Width="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"/>
|
||||
|
||||
<Button x:Name="TestButton" Content="Stichprobe" FontSize="14" Width="180" Margin="10,10,10,74" Height="27" Tag="Print" IsEnabled="False"
|
||||
Click="TestButton_Click"
|
||||
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>
|
113
Elwig/Dialogs/DeliveryConfirmationsDialog.xaml.cs
Normal file
113
Elwig/Dialogs/DeliveryConfirmationsDialog.xaml.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using Elwig.Documents;
|
||||
using Elwig.Models;
|
||||
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 DeliveryConfirmationsDialog : ContextWindow {
|
||||
|
||||
public readonly int Year;
|
||||
|
||||
public DeliveryConfirmationsDialog(int year) {
|
||||
InitializeComponent();
|
||||
Year = year;
|
||||
Title = $"Anlieferungsbestätigungen - Lese {Year} - Elwig";
|
||||
TextElement.Text = App.Client.TextDeliveryConfirmation;
|
||||
if (!App.Config.Debug) {
|
||||
TestButton.Visibility = Visibility.Hidden;
|
||||
}
|
||||
}
|
||||
|
||||
private void Window_Loaded(object sender, RoutedEventArgs evt) {
|
||||
TestButton.IsEnabled = App.IsPrintingReady;
|
||||
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();
|
||||
if (mode == 0) {
|
||||
var r = new Random().Next(0, 10);
|
||||
list = list.Where((_, n) => n % 10 == r);
|
||||
}
|
||||
|
||||
using var doc = await Document.Merge(list.Select(m => new DeliveryConfirmation(Context, Year, m))); ;
|
||||
await doc.Generate();
|
||||
Mouse.OverrideCursor = null;
|
||||
|
||||
if (mode < 2) {
|
||||
doc.Show();
|
||||
return;
|
||||
}
|
||||
if (App.Config.Debug) {
|
||||
doc.Show();
|
||||
} else {
|
||||
await doc.Print();
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
private async void TestButton_Click(object sender, RoutedEventArgs evt) {
|
||||
await Generate(0);
|
||||
}
|
||||
|
||||
private async void ShowButton_Click(object sender, RoutedEventArgs evt) {
|
||||
await Generate(1);
|
||||
}
|
||||
|
||||
private async void PrintButton_Click(object sender, RoutedEventArgs evt) {
|
||||
await Generate(2);
|
||||
}
|
||||
}
|
||||
}
|
60
Elwig/Dialogs/DeliveryExtractionDialog.xaml
Normal file
60
Elwig/Dialogs/DeliveryExtractionDialog.xaml
Normal file
@@ -0,0 +1,60 @@
|
||||
<Window x:Class="Elwig.Dialogs.DeliveryExtractionDialog"
|
||||
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.Dialogs"
|
||||
mc:Ignorable="d"
|
||||
ResizeMode="NoResize"
|
||||
ShowInTaskbar="False"
|
||||
Topmost="True"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
FocusManager.FocusedElement="{Binding ElementName=WeightInput}"
|
||||
Title="Teillieferung extrahieren" Height="210" Width="380">
|
||||
<Window.Resources>
|
||||
<Style TargetType="Label">
|
||||
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||
<Setter Property="Padding" Value="2,4,2,4"/>
|
||||
<Setter Property="Height" Value="25"/>
|
||||
</Style>
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="Padding" Value="2"/>
|
||||
<Setter Property="Height" Value="25"/>
|
||||
<Setter Property="TextWrapping" Value="NoWrap"/>
|
||||
</Style>
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||
<Setter Property="VerticalAlignment" Value="Bottom"/>
|
||||
<Setter Property="Width" Value="100"/>
|
||||
<Setter Property="Height" Value="25"/>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="200"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Margin="10,10,0,10" TextWrapping="Wrap">
|
||||
Was soll mit der Teillieferung <Run x:Name="TextLsNr" FontWeight="Bold" Text="20201010A000/1"/><LineBreak/>
|
||||
von <Run x:Name="TextMember" FontWeight="Bold" Text="Max Mustermann"/> geschehen?
|
||||
</TextBlock>
|
||||
<RadioButton x:Name="NewDeliveryButton" Content="Neue Lieferung erstellen"
|
||||
Margin="10,80,0,10" HorizontalAlignment="Left" VerticalAlignment="Top"
|
||||
Checked="Selection_Changed" Unchecked="Selection_Changed"/>
|
||||
<RadioButton x:Name="AddToDeliveryButton" Content="Zu Lieferung hinzufügen"
|
||||
Margin="10,100,0,10" HorizontalAlignment="Left" VerticalAlignment="Top"
|
||||
Checked="Selection_Changed" Unchecked="Selection_Changed"/>
|
||||
|
||||
<ListBox x:Name="DeliveryList" Grid.Column="1" Margin="10,10,10,45"
|
||||
SelectionChanged="DeliveryList_SelectionChanged"/>
|
||||
|
||||
<Button x:Name="ConfirmButton" Content="Bestätigen" Margin="10,10,115,10" Grid.ColumnSpan="2" IsEnabled="False" IsDefault="True"
|
||||
Click="ConfirmButton_Click"/>
|
||||
<Button x:Name="CancelButton" Content="Abbrechen" Margin="10,10,10,10" Grid.ColumnSpan="2" IsCancel="True"/>
|
||||
</Grid>
|
||||
</Window>
|
37
Elwig/Dialogs/DeliveryExtractionDialog.xaml.cs
Normal file
37
Elwig/Dialogs/DeliveryExtractionDialog.xaml.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
|
||||
namespace Elwig.Dialogs {
|
||||
public partial class DeliveryExtractionDialog : Window {
|
||||
|
||||
public string? AddTo;
|
||||
|
||||
public DeliveryExtractionDialog(string lsnr, string name, bool single, IEnumerable<string> lsnrs) {
|
||||
InitializeComponent();
|
||||
TextLsNr.Text = lsnr;
|
||||
TextMember.Text = name;
|
||||
NewDeliveryButton.IsEnabled = !single;
|
||||
DeliveryList.IsEnabled = false;
|
||||
DeliveryList.ItemsSource = lsnrs;
|
||||
}
|
||||
|
||||
private void ConfirmButton_Click(object sender, RoutedEventArgs evt) {
|
||||
DialogResult = true;
|
||||
AddTo = NewDeliveryButton.IsChecked == true ? "new" : DeliveryList.SelectedItem as string;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void UpdateButtons() {
|
||||
ConfirmButton.IsEnabled = NewDeliveryButton.IsChecked == true || (AddToDeliveryButton.IsChecked == true && DeliveryList.SelectedItem != null);
|
||||
DeliveryList.IsEnabled = AddToDeliveryButton.IsChecked == true;
|
||||
}
|
||||
|
||||
private void Selection_Changed(object sender, RoutedEventArgs evt) {
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
private void DeliveryList_SelectionChanged(object sender, RoutedEventArgs evt) {
|
||||
UpdateButtons();
|
||||
}
|
||||
}
|
||||
}
|
53
Elwig/Dialogs/LinearPriceIncreaseDialog.xaml
Normal file
53
Elwig/Dialogs/LinearPriceIncreaseDialog.xaml
Normal file
@@ -0,0 +1,53 @@
|
||||
<Window x:Class="Elwig.Dialogs.LinearPriceIncreaseDialog"
|
||||
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.Dialogs"
|
||||
mc:Ignorable="d"
|
||||
ResizeMode="NoResize"
|
||||
ShowInTaskbar="False"
|
||||
Topmost="True"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
FocusManager.FocusedElement="{Binding ElementName=PriceInput}"
|
||||
Title="Linear wachsen" Height="140" Width="270">
|
||||
<Window.Resources>
|
||||
<Style TargetType="Label">
|
||||
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||
<Setter Property="Padding" Value="2,4,2,4"/>
|
||||
<Setter Property="Height" Value="25"/>
|
||||
</Style>
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="Padding" Value="2"/>
|
||||
<Setter Property="Height" Value="25"/>
|
||||
<Setter Property="TextWrapping" Value="NoWrap"/>
|
||||
</Style>
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||
<Setter Property="VerticalAlignment" Value="Bottom"/>
|
||||
<Setter Property="Width" Value="100"/>
|
||||
<Setter Property="Height" Value="25"/>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="55"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Content="Preis:" Margin="10,20,10,10"/>
|
||||
<Grid Grid.Column="1" Width="145" Height="25" Margin="0,20,0,0" HorizontalAlignment="Left" VerticalAlignment="Top">
|
||||
<TextBox x:Name="PriceInput" TextAlignment="Right" Padding="2,2,30,2"
|
||||
TextChanged="PriceInput_TextChanged"/>
|
||||
<Label Content="€/kg" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10" Padding="2,4,2,4"/>
|
||||
</Grid>
|
||||
|
||||
<Button x:Name="ConfirmButton" Content="Bestätigen" Margin="17,0,133,10" IsEnabled="False" IsDefault="True"
|
||||
Click="ConfirmButton_Click" Grid.ColumnSpan="2" HorizontalAlignment="Stretch"/>
|
||||
<Button x:Name="CancelButton" Content="Abbrechen" Margin="0,0,20,10" Grid.Column="1" IsCancel="True"/>
|
||||
</Grid>
|
||||
</Window>
|
30
Elwig/Dialogs/LinearPriceIncreaseDialog.xaml.cs
Normal file
30
Elwig/Dialogs/LinearPriceIncreaseDialog.xaml.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using Elwig.Helpers;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Elwig.Dialogs {
|
||||
public partial class LinearPriceIncreaseDialog : Window {
|
||||
|
||||
public double Price = 0.0;
|
||||
|
||||
public LinearPriceIncreaseDialog() {
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void ConfirmButton_Click(object sender, RoutedEventArgs evt) {
|
||||
DialogResult = true;
|
||||
Price = double.Parse(PriceInput.Text.Replace("\u202f", ""));
|
||||
Close();
|
||||
}
|
||||
|
||||
private void UpdateButtons(ValidationResult res) {
|
||||
ConfirmButton.IsEnabled = res.IsValid;
|
||||
}
|
||||
|
||||
private void PriceInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
||||
var res = Validator.CheckDecimal(PriceInput, true, 2, 8);
|
||||
UpdateButtons(res);
|
||||
}
|
||||
}
|
||||
}
|
57
Elwig/Dialogs/ManualWeighingDialog.xaml
Normal file
57
Elwig/Dialogs/ManualWeighingDialog.xaml
Normal file
@@ -0,0 +1,57 @@
|
||||
<Window x:Class="Elwig.Dialogs.ManualWeighingDialog"
|
||||
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.Dialogs"
|
||||
mc:Ignorable="d"
|
||||
ResizeMode="NoResize"
|
||||
ShowInTaskbar="False"
|
||||
Topmost="True"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
FocusManager.FocusedElement="{Binding ElementName=WeightInput}"
|
||||
Title="Handwiegung" Height="170" Width="400">
|
||||
<Window.Resources>
|
||||
<Style TargetType="Label">
|
||||
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||
<Setter Property="Padding" Value="2,4,2,4"/>
|
||||
<Setter Property="Height" Value="25"/>
|
||||
</Style>
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="Padding" Value="2"/>
|
||||
<Setter Property="Height" Value="25"/>
|
||||
<Setter Property="TextWrapping" Value="NoWrap"/>
|
||||
</Style>
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="HorizontalAlignment" Value="Right"/>
|
||||
<Setter Property="VerticalAlignment" Value="Bottom"/>
|
||||
<Setter Property="Width" Value="100"/>
|
||||
<Setter Property="Height" Value="25"/>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="70"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Content="Gewicht:" Margin="10,20,10,10"/>
|
||||
<Grid Grid.Column="1" Width="70" Height="25" Margin="0,20,10,10" HorizontalAlignment="Left" VerticalAlignment="Top">
|
||||
<TextBox x:Name="WeightInput" TextAlignment="Right" Padding="2,2,17,2"
|
||||
TextChanged="WeightInput_TextChanged"/>
|
||||
<Label Content="kg" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10" Padding="2,4,2,4"/>
|
||||
</Grid>
|
||||
|
||||
<Label Content="Grund:" Margin="10,50,10,10"/>
|
||||
<TextBox x:Name="ReasonInput" Grid.Column="1" Margin="0,50,10,10"
|
||||
TextChanged="ReasonInput_TextChanged"/>
|
||||
|
||||
<Button x:Name="ConfirmButton" Content="Bestätigen" Margin="10,10,115,10" Grid.Column="1" IsEnabled="False" IsDefault="True"
|
||||
Click="ConfirmButton_Click"/>
|
||||
<Button x:Name="CancelButton" Content="Abbrechen" Margin="10,10,10,10" Grid.Column="1" IsCancel="True"/>
|
||||
</Grid>
|
||||
</Window>
|
38
Elwig/Dialogs/ManualWeighingDialog.xaml.cs
Normal file
38
Elwig/Dialogs/ManualWeighingDialog.xaml.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using Elwig.Helpers;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Elwig.Dialogs {
|
||||
public partial class ManualWeighingDialog : Window {
|
||||
|
||||
public int Weight = 0;
|
||||
public string? Reason = null;
|
||||
|
||||
public ManualWeighingDialog(string? reason = null) {
|
||||
InitializeComponent();
|
||||
ReasonInput.Text = reason;
|
||||
}
|
||||
|
||||
private void ConfirmButton_Click(object sender, RoutedEventArgs evt) {
|
||||
DialogResult = true;
|
||||
Weight = int.Parse(WeightInput.Text);
|
||||
Reason = Regex.Replace(ReasonInput.Text, @"\s+", " ").Trim();
|
||||
if (Reason == "") Reason = null;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void UpdateButtons() {
|
||||
ConfirmButton.IsEnabled = WeightInput.Text.Length > 0;
|
||||
}
|
||||
|
||||
private void WeightInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
||||
Validator.CheckInteger(WeightInput, true, 5);
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
private void ReasonInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
||||
UpdateButtons();
|
||||
}
|
||||
}
|
||||
}
|
@@ -2,7 +2,7 @@
|
||||
@inherits TemplatePage<Elwig.Documents.BusinessDocument>
|
||||
@model Elwig.Documents.BusinessDocument
|
||||
@{ Layout = "Document"; }
|
||||
|
||||
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\style-businessdocument.css" />
|
||||
<div class="info-wrapper">
|
||||
<div class="address-wrapper">
|
||||
<div class="sender">
|
||||
@@ -13,8 +13,9 @@
|
||||
</div>
|
||||
<address>@Model.Address</address>
|
||||
</div>
|
||||
<aside></aside>
|
||||
<aside>@Raw(Model.Aside)</aside>
|
||||
@if (Model.ShowDateAndLocation) {
|
||||
<div class="date">@Model.Location, am @($"{Model.Date:dd.MM.yyyy}")</div>
|
||||
}
|
||||
</div>
|
||||
<main>
|
||||
@RenderBody()
|
||||
</main>
|
||||
@RenderBody()
|
||||
|
@@ -1,25 +1,35 @@
|
||||
using Elwig.Helpers;
|
||||
using Elwig.Models;
|
||||
|
||||
namespace Elwig.Documents {
|
||||
public abstract class BusinessDocument : Document {
|
||||
|
||||
public bool ShowDateAndLocation = false;
|
||||
|
||||
public Member Member;
|
||||
public bool IncludeSender = false;
|
||||
public bool UseBillingAddress = false;
|
||||
public string Aside;
|
||||
public string? Location;
|
||||
|
||||
public BusinessDocument(string title, Member m, bool includeSender = false) : base(title) {
|
||||
Member = m;
|
||||
Location = App.BranchLocation;
|
||||
IncludeSender = includeSender;
|
||||
var uid = (m.UstIdNr ?? "-") + (m.IsBuchführend ? "" : " <i>(pauschaliert)</i>");
|
||||
Aside = $"<table><colgroup><col span='1' style='width: 22.5mm;'/><col span='1' style='width: 42.5mm;'/></colgroup>" +
|
||||
$"<thead><tr><th colspan='2'>Mitglied</th></tr></thead><tbody>" +
|
||||
$"<tr><th>Mitglieds-Nr.</th><td>{m.MgNr}</td></tr>" +
|
||||
$"<tr><th>Betriebs-Nr.</th><td>{m.LfbisNr}</td></tr>" +
|
||||
$"<tr><th>UID</th><td>{uid}</td></tr>" +
|
||||
$"</tbody></table>";
|
||||
}
|
||||
|
||||
public string Address {
|
||||
get {
|
||||
var b = Member.BillingAddress;
|
||||
var plz = (b == null) ? Member.PostalDest.AtPlz : b.PostalDest.AtPlz;
|
||||
if (b != null) {
|
||||
return $"{b.Name}\n{b.Address}\n{plz.Plz} {plz.Dest}";
|
||||
} else {
|
||||
return $"{Member.AdministrativeName}\n{Member.Address}\n{plz.Plz} {plz.Dest}";
|
||||
}
|
||||
IAddress addr = (Member.BillingAddress != null && UseBillingAddress) ? Member.BillingAddress : Member;
|
||||
var plz = addr.PostalDest.AtPlz;
|
||||
return (addr is BillingAddr ? $"{addr.Name}\n" : "") + $"{Member.AdministrativeName}\n{addr.Address}\n{plz?.Plz} {plz?.Ort.Name.Split(",")[0]}\n{addr.PostalDest.Country.Name}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,7 +2,8 @@
|
||||
@inherits TemplatePage<Elwig.Documents.BusinessLetter>
|
||||
@model Elwig.Documents.BusinessLetter
|
||||
@{ Layout = "BusinessDocument"; }
|
||||
|
||||
<main>
|
||||
<p>Sehr geehrtes Mitglied,</p>
|
||||
<p>nein.</p>
|
||||
<p>Mit freundlichen Grüßen<br/>Ihre Winzergenossenschaft</p>
|
||||
</main>
|
||||
|
@@ -2,8 +2,6 @@ using Elwig.Models;
|
||||
|
||||
namespace Elwig.Documents {
|
||||
public class BusinessLetter : BusinessDocument {
|
||||
public BusinessLetter(string title, Member m) : base(title, m) {
|
||||
|
||||
}
|
||||
public BusinessLetter(string title, Member m) : base(title, m) { }
|
||||
}
|
||||
}
|
||||
|
87
Elwig/Documents/CreditNote.cshtml
Normal file
87
Elwig/Documents/CreditNote.cshtml
Normal file
@@ -0,0 +1,87 @@
|
||||
@using RazorLight
|
||||
@inherits TemplatePage<Elwig.Documents.CreditNote>
|
||||
@model Elwig.Documents.CreditNote
|
||||
@{ Layout = "BusinessDocument"; }
|
||||
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\style-creditnote.css"/>
|
||||
@{
|
||||
var binNum = Model.BinNames.Length;
|
||||
}
|
||||
<main>
|
||||
<h1>@Model.Title</h1>
|
||||
<table class="credit">
|
||||
<colgroup>
|
||||
<col style="width: 24mm;"/>
|
||||
<col style="width: 6mm;"/>
|
||||
<col style="width: 31mm;"/>
|
||||
<col style="width: 15mm;"/>
|
||||
<col style="width: 8mm;"/>
|
||||
<col style="width: 10mm;"/>
|
||||
<col style="width: 12mm;"/>
|
||||
<col style="width: 10mm;"/>
|
||||
<col style="width: 12mm;"/>
|
||||
<col style="width: 15mm;"/>
|
||||
<col style="width: 17mm;"/>
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowspan="3" style="text-align: left;">Lieferschein-Nr.</th>
|
||||
<th rowspan="3">Pos.</th>
|
||||
<th rowspan="3" style="text-align: left;">Sorte</th>
|
||||
<th rowspan="3" style="text-align: left;">Attribut(e)</th>
|
||||
<th rowspan="2" colspan="2">Gradation</th>
|
||||
<th colspan="2">Zu-/Abschläge</th>
|
||||
<th colspan="2">@Raw(string.Join("<br/>", Model.BinNames))</th>
|
||||
<th rowspan="2">Betrag</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Abs.</th>
|
||||
<th>Rel.</th>
|
||||
<th>Gewicht</th>
|
||||
<th>Preis</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>[°Oe]</th>
|
||||
<th>[°KMW]</th>
|
||||
<th>[@Model.CurrencySymbol/kg]</th>
|
||||
<th>[%]</th>
|
||||
<th>[kg]</th>
|
||||
<th>[@Model.CurrencySymbol/kg]</th>
|
||||
<th>[@Model.CurrencySymbol]</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@{
|
||||
string FormatRow(int? weight, decimal? amount) {
|
||||
var w = weight == null || weight == 0 ? "-" : $"{weight:N0}";
|
||||
return $"<td class='weight'>{w}</td><td class='amount'>{amount?.ToString("0." + string.Concat(Enumerable.Repeat('0', Model.Precision)))}</td>";
|
||||
}
|
||||
string? last = null;
|
||||
}
|
||||
@foreach (var part in Model.Parts) {
|
||||
var pmt = part.Payment;
|
||||
var abs = pmt?.ModAbs == null || pmt?.ModAbs == 0 ? "-" : pmt?.ModAbs.ToString("0." + string.Concat(Enumerable.Repeat('0', Model.Precision)));
|
||||
var rel = pmt?.ModRel == null || pmt?.ModRel == 0 ? "-" : $"{pmt?.ModRel * 100:0.00##}";
|
||||
<tr class="first @(binNum <= 1 ? "last" : "") @(last != null && last != part.SortId ? "new" : "")">
|
||||
<td rowspan="@binNum" class="lsnr">@part.Delivery.LsNr</td>
|
||||
<td rowspan="@binNum" class="dpnr">@part.DPNr</td>
|
||||
<td rowspan="@binNum" class="variant">@part.Variant.Name</td>
|
||||
<td rowspan="@binNum" class="attribute">@string.Join(" / ", part.PartAttributes.Select(a => a.AttrId))</td>
|
||||
<td rowspan="@binNum" class="oe">@($"{part.Oe:N0}")</td>
|
||||
<td rowspan="@binNum" class="kmw">@($"{part.Kmw:N1}")</td>
|
||||
<td rowspan="@binNum" class="abs">@abs</td>
|
||||
<td rowspan="@binNum" class="rel">@rel</td>
|
||||
<!--FIXME price-->
|
||||
@Raw(FormatRow(pmt?.DeliveryPart.Bins?.ElementAtOrDefault(0)?.Value, 0))
|
||||
<td rowspan="@binNum" class="amount sum">@($"{pmt?.Amount:N2}")</td>
|
||||
</tr>
|
||||
@for (int i = 1; i < binNum; i++) {
|
||||
<tr class="@(i == binNum - 1 ? "last" : "")">
|
||||
<!--FIXME price-->
|
||||
@Raw(FormatRow(pmt?.DeliveryPart.Bins?.ElementAtOrDefault(i)?.Value, 0))
|
||||
</tr>
|
||||
}
|
||||
last = part.SortId;
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</main>
|
40
Elwig/Documents/CreditNote.cshtml.cs
Normal file
40
Elwig/Documents/CreditNote.cshtml.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using Elwig.Helpers;
|
||||
using Elwig.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Elwig.Documents {
|
||||
public class CreditNote : BusinessDocument {
|
||||
|
||||
public Credit Credit;
|
||||
public string? Text;
|
||||
public string CurrencySymbol;
|
||||
public string[] BinNames;
|
||||
public int Precision;
|
||||
public IEnumerable<DeliveryPart> Parts;
|
||||
|
||||
public CreditNote(Credit c, AppDbContext ctx) : base($"Traubengutschrift Nr. {c.TgId} – {c.Payment.Variant.Name}", c.Member) {
|
||||
UseBillingAddress = true;
|
||||
ShowDateAndLocation = true;
|
||||
Credit = c;
|
||||
Aside = Aside.Replace("</table>", "") +
|
||||
$"<thead><tr><th colspan='2'>Gutschrift</th></tr></thead><tbody>" +
|
||||
$"<tr><th>TG-Nr.</th><td>{c.TgId}</td></tr>" +
|
||||
$"<tr><th>Überw. am</th><td>{c.Payment.Variant.TransferDate:dd.MM.yyyy}</td></tr>" +
|
||||
$"<tr><th>Datum/Zeit</th><td>{c.ModifiedTimestamp:dd.MM.yyyy} / {c.ModifiedTimestamp:HH:mm}</td></tr>" +
|
||||
$"</tbody></table>";
|
||||
Text = App.Client.TextDeliveryNote;
|
||||
DocumentId = $"Tr.-Gutschr. {c.TgId}";
|
||||
CurrencySymbol = c.Payment.Variant.Season.Currency.Symbol ?? c.Payment.Variant.Season.Currency.Code;
|
||||
BinNames = new string[0]; // FIXME
|
||||
Precision = c.Payment.Variant.Season.Precision;
|
||||
Parts = ctx.DeliveryParts.FromSql($"""
|
||||
SELECT p.*
|
||||
FROM v_delivery v
|
||||
JOIN delivery_part p ON (p.year, p.did, p.dpnr) = (v.year, v.did, v.dpnr)
|
||||
WHERE (v.year, v.mgnr) = ({c.Year}, {c.Member.MgNr})
|
||||
ORDER BY sortid, LENGTH(attributes) DESC, attributes, kmw DESC, date, time, dpnr
|
||||
""").ToList();
|
||||
}
|
||||
}}
|
166
Elwig/Documents/DeliveryConfirmation.cshtml
Normal file
166
Elwig/Documents/DeliveryConfirmation.cshtml
Normal file
@@ -0,0 +1,166 @@
|
||||
@using RazorLight
|
||||
@inherits TemplatePage<Elwig.Documents.DeliveryConfirmation>
|
||||
@model Elwig.Documents.DeliveryConfirmation
|
||||
@{ Layout = "BusinessDocument"; }
|
||||
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\style-deliveryconfirmation.css"/>
|
||||
<main>
|
||||
<h1>@Model.Title</h1>
|
||||
<table class="delivery-confirmation">
|
||||
<colgroup>
|
||||
<col style="width: 25mm;"/>
|
||||
<col style="width: 5mm;"/>
|
||||
<col style="width: 20mm;"/>
|
||||
<col style="width: 21mm;"/>
|
||||
<col style="width: 19mm;"/>
|
||||
<col style="width: 10mm;"/>
|
||||
<col style="width: 10mm;"/>
|
||||
<col style="width: 15mm;"/>
|
||||
<col style="width: 12mm;"/>
|
||||
<col style="width: 14mm;"/>
|
||||
<col style="width: 14mm;"/>
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowspan="2" style="text-align: left;">Lieferschein-Nr.</th>
|
||||
<th rowspan="2">Pos.</th>
|
||||
<th rowspan="2" style="text-align: left;">Sorte</th>
|
||||
<th rowspan="2" style="text-align: left;">Attribut(e)</th>
|
||||
<th rowspan="2" style="text-align: left;">Qualitätsstufe</th>
|
||||
<th colspan="2">Gradation</th>
|
||||
<th colspan="2">Flächenbindung</th>
|
||||
<th>Gewicht</th>
|
||||
<th>Davon<br/>abzuwerten</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>[°Oe]</th>
|
||||
<th>[°KMW]</th>
|
||||
<th colspan="2">[kg]</th>
|
||||
<th>[kg]</th>
|
||||
<th>[kg]</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@{
|
||||
var lastSortId = "";
|
||||
}
|
||||
@foreach (var p in Model.Deliveries) {
|
||||
var bins = p.Bins.Where(b => b.Value > 0).OrderByDescending(b => b.BinNr).ToArray();
|
||||
var rowsBins = bins.Length;
|
||||
var mods = p.Modifiers.Select(m => m.Name).ToArray();
|
||||
var rowsMod = mods.Length + 1;
|
||||
var rows = Math.Max(rowsBins, rowsMod);
|
||||
var first = true;
|
||||
@for (int i = 0; i < rows; i++) {
|
||||
<tr class="@(first ? "first" : "") @(p.SortId != lastSortId && lastSortId != "" ? "new": "") @(rows > i + 1 ? "trailing" : "")">
|
||||
@if (first) {
|
||||
<td rowspan="@rows">@p.Delivery.LsNr</td>
|
||||
<td rowspan="@rows">@p.DPNr</td>
|
||||
<td class="small">@p.Variant.Name</td>
|
||||
<td class="small">@p.AttributesString</td>
|
||||
<td class="small">@p.Quality.Name</td>
|
||||
<td rowspan="@rows" class="grad">@($"{p.Oe:N0}")</td>
|
||||
<td rowspan="@rows" class="grad">@($"{p.Kmw:N1}")</td>
|
||||
}
|
||||
@if (i > 0 && i <= mods.Length) {
|
||||
<td colspan="3" class="mod">@(mods[i - 1])</td>
|
||||
} else if (i > 0) {
|
||||
<td colspan="3"></td>
|
||||
}
|
||||
@if (i < bins.Length) {
|
||||
var bin = bins[i];
|
||||
<td class="geb">@(bin.Discr == "_" ? "ungeb." : $"geb. {p.SortId}{bin.Discr}"):</td>
|
||||
<td class="weight">@($"{bin.Value:N0}")</td>
|
||||
} else {
|
||||
<td colspan="2"></td>
|
||||
}
|
||||
@if (i == bins.Length - 1) {
|
||||
<td class="weight">@($"{p.Weight:N0}")</td>
|
||||
} else {
|
||||
<td></td>
|
||||
}
|
||||
@if (first) {
|
||||
<td rowspan="@rows" class="weight"></td>
|
||||
first = false;
|
||||
}
|
||||
</tr>
|
||||
lastSortId = p.SortId;
|
||||
}
|
||||
}
|
||||
<tr class="sum">
|
||||
<td colspan="8">Gesamt:</td>
|
||||
<td colspan="2" class="weight">@($"{Model.Deliveries.Sum(p => p.Weight):N0}")</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="delivery-confirmation-stats">
|
||||
<colgroup>
|
||||
<col style="width: 45mm;"/>
|
||||
<col style="width: 17mm;"/>
|
||||
<col style="width: 17mm;"/>
|
||||
<col style="width: 17mm;"/>
|
||||
<col style="width: 19mm;"/>
|
||||
<col style="width: 16mm;"/>
|
||||
<col style="width: 17mm;"/>
|
||||
<col style="width: 17mm;"/>
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th><b>Lese @Model.Year</b> per @($"{Model.Date:dd.MM.yyyy}") [kg]</th>
|
||||
<th>Lieferpflicht</th>
|
||||
<th>Lieferrecht</th>
|
||||
<th>Unterliefert<br/>(bzgl. Zuget.)</th>
|
||||
<th>Noch zu liefern<br/>(bzgl. Gelft.)</th>
|
||||
<th>Überliefert<br/>(bzgl. Gelft.)</th>
|
||||
<th>Zugeteilt</th>
|
||||
<th>Geliefert</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@{
|
||||
string FormatRow(int mode, int obligation, int right, int sum, int? payment = null) {
|
||||
var isGa = mode == 0;
|
||||
payment ??= sum;
|
||||
return $"<td>{(mode == 1 ? "" : obligation == 0 ? "-" : $"{obligation:N0}")}</td>" +
|
||||
$"<td>{(mode == 1 ? "" : right == 0 ? "-" : $"{right:N0}")}</td>" +
|
||||
$"<td>{(mode == 1 ? "" : payment < obligation ? $"<b>{obligation - payment:N0}\x3c/b>" : "-")}</td>" +
|
||||
$"<td>{(mode == 1 ? "" : payment >= obligation && sum <= right ? $"{right - sum:N0}" : "-")}</td>" +
|
||||
$"<td>{(mode == 1 ? "" : obligation == 0 && right == 0 ? "-" : (sum > right ? ((isGa ? "<b>" : "") + $"{sum - right:N0}" + (isGa ? "</b>" : "")) : "-"))}</td>" +
|
||||
$"<td>{(mode != 2 ? "" : obligation == 0 && right == 0 ? "-" : $"{payment:N0}")}</td>" +
|
||||
$"<td>{sum:N0}</td>";
|
||||
}
|
||||
var mBins = Model.MemberBins.Where(b => b.Value.Item2 > 0 || b.Value.Item3 > 0 || b.Value.Item4 > 0).ToList();
|
||||
var fbVars = mBins.Where(b => b.Value.Item2 > 0 || b.Value.Item3 > 0).Select(b => b.Key.Replace("_", "")).Order().ToArray();
|
||||
var fbs = mBins.Where(b => fbVars.Contains(b.Key)).OrderBy(b => b.Value.Item1);
|
||||
var rem = mBins.Where(b => !fbVars.Contains(b.Key)).OrderBy(b => b.Value.Item1);
|
||||
}
|
||||
<tr>
|
||||
<th>Gesamtlieferung lt. gez. GA</th>
|
||||
@Raw(FormatRow(0, Model.Member.DeliveryObligation, Model.Member.DeliveryRight, Model.Member.Deliveries.Where(d => d.Year == Model.Year).Sum(d => d.Weight)))
|
||||
</tr>
|
||||
@if (rem.Any()) {
|
||||
<tr class="subheading"><th colspan="8">Sortenaufteilung:</th></tr>
|
||||
}
|
||||
@foreach (var (id, (name, right, obligation, sum, payment)) in rem) {
|
||||
<tr>
|
||||
<th>@name</th>
|
||||
@Raw(FormatRow(1, obligation, right, sum, payment))
|
||||
</tr>
|
||||
}
|
||||
@if (fbs.Any()){
|
||||
<tr class="subheading"><th colspan="8">Flächenbindungen:</th></tr>
|
||||
}
|
||||
@foreach (var (id, (name, right, obligation, sum, payment)) in fbs) {
|
||||
<tr>
|
||||
<th>@name</th>
|
||||
@Raw(FormatRow(2, obligation, right, sum, payment))
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="text" style="margin-top: 2em;">
|
||||
@if (Model.Text != null) {
|
||||
<p class="comment" style="white-space: pre-wrap; break-inside: avoid;">@Model.Text</p>
|
||||
}
|
||||
</div>
|
||||
</main>
|
36
Elwig/Documents/DeliveryConfirmation.cshtml.cs
Normal file
36
Elwig/Documents/DeliveryConfirmation.cshtml.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using Elwig.Helpers;
|
||||
using Elwig.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Elwig.Documents {
|
||||
public class DeliveryConfirmation : BusinessDocument {
|
||||
|
||||
public int Year;
|
||||
public IEnumerable<DeliveryPart> Deliveries;
|
||||
public string? Text = App.Client.TextDeliveryConfirmation;
|
||||
public Dictionary<string, (string, int, int, int, int)> MemberBins;
|
||||
|
||||
public DeliveryConfirmation(AppDbContext ctx, int year, Member m) :
|
||||
base($"Anlieferungsbestätigung {year}", m) {
|
||||
Year = year;
|
||||
ShowDateAndLocation = true;
|
||||
UseBillingAddress = true;
|
||||
IncludeSender = true;
|
||||
// FIXME footer in merged documents
|
||||
//DocumentId = $"Anl.-Best. {Year}/{m.MgNr}";
|
||||
Deliveries = ctx.DeliveryParts.FromSqlRaw($"""
|
||||
SELECT p.*
|
||||
FROM v_delivery v
|
||||
JOIN delivery_part p ON (p.year, p.did, p.dpnr) = (v.year, v.did, v.dpnr)
|
||||
WHERE (v.year, v.mgnr) = ({Year}, {m.MgNr})
|
||||
ORDER BY v.sortid, v.abgewertet ASC,
|
||||
COALESCE(LENGTH(v.attributes), 0) ASC, attribute_prio DESC, COALESCE(v.attributes, '~'),
|
||||
v.kmw DESC, v.lsnr, v.dpnr
|
||||
""")
|
||||
.ToList();
|
||||
MemberBins = ctx.GetMemberBins(Year, m.MgNr).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
}
|
67
Elwig/Documents/DeliveryJournal.cshtml
Normal file
67
Elwig/Documents/DeliveryJournal.cshtml
Normal file
@@ -0,0 +1,67 @@
|
||||
@using RazorLight
|
||||
@inherits TemplatePage<Elwig.Documents.DeliveryJournal>
|
||||
@model Elwig.Documents.DeliveryJournal
|
||||
@{ Layout = "Document"; }
|
||||
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\style-deliveryjournal.css"/>
|
||||
<main>
|
||||
<h1>Lieferjournal</h1>
|
||||
<h2>@Model.Filter</h2>
|
||||
<table class="journal">
|
||||
<colgroup>
|
||||
<col style="width: 25mm;"/>
|
||||
<col style="width: 5mm;"/>
|
||||
<col style="width: 17mm;"/>
|
||||
<col style="width: 10mm;"/>
|
||||
<col style="width: 8mm;"/>
|
||||
<col style="width: 38mm;"/>
|
||||
<col style="width: 28mm;"/>
|
||||
<col style="width: 10mm;"/>
|
||||
<col style="width: 10mm;"/>
|
||||
<col style="width: 14mm;"/>
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowspan="2" style="text-align: left;">Lieferschein-Nr.</th>
|
||||
<th rowspan="2">Pos.</th>
|
||||
<th rowspan="2">Datum</th>
|
||||
<th rowspan="2">Zeit</th>
|
||||
<th colspan="2" rowspan="2" style="text-align: left;">Mitglied</th>
|
||||
<th rowspan="2" style="text-align: left;">Sorte</th>
|
||||
<th colspan="2">Gradation</th>
|
||||
<th>Gewicht</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>[°Oe]</th>
|
||||
<th>[°KMW]</th>
|
||||
<th>[kg]</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var p in Model.Deliveries) {
|
||||
<tr>
|
||||
<td>@p.Delivery.LsNr</td>
|
||||
<td>@p.DPNr</td>
|
||||
<td>@($"{p.Delivery.Date:dd.MM.yyyy}")</td>
|
||||
<td>@($"{p.Delivery.Time:HH:mm}")</td>
|
||||
<td class="mgnr">@p.Delivery.Member.MgNr</td>
|
||||
<td>@p.Delivery.Member.AdministrativeName</td>
|
||||
<td>@p.Variant.Name</td>
|
||||
<td class="grad">@($"{p.Oe:N0}")</td>
|
||||
<td class="grad">@($"{p.Kmw:N1}")</td>
|
||||
<td class="weight">@($"{p.Weight:N0}")</td>
|
||||
</tr>
|
||||
}
|
||||
<tr class="sum">
|
||||
@{
|
||||
var kmw = Elwig.Helpers.Utils.AggregateDeliveryPartsKmw(Model.Deliveries);
|
||||
var oe = Elwig.Helpers.Utils.KmwToOe(kmw);
|
||||
}
|
||||
<td colspan="2">Gesamt:</td>
|
||||
<td colspan="5">(Teil-)Lieferungen: @($"{Model.Deliveries.DistinctBy(p => p.Delivery).Count():N0}") (@($"{Model.Deliveries.Count():N0}"))</td>
|
||||
<td class="grad">@($"{oe:N0}")</td>
|
||||
<td class="grad">@($"{kmw:N1}")</td>
|
||||
<td class="weight">@($"{Model.Deliveries.Sum(p => p.Weight):N0}")</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</main>
|
33
Elwig/Documents/DeliveryJournal.cshtml.cs
Normal file
33
Elwig/Documents/DeliveryJournal.cshtml.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using Elwig.Helpers;
|
||||
using Elwig.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Elwig.Documents {
|
||||
public class DeliveryJournal : Document {
|
||||
|
||||
public string Filter;
|
||||
public IEnumerable<DeliveryPart> Deliveries;
|
||||
|
||||
public DeliveryJournal(string filter, IEnumerable<DeliveryPart> deliveries) : base($"Lieferjournal {filter}") {
|
||||
Filter = filter;
|
||||
Deliveries = deliveries;
|
||||
}
|
||||
|
||||
public DeliveryJournal(string filter, IQueryable<DeliveryPart> deliveries) :
|
||||
this(filter, deliveries
|
||||
.Include(p => p.Delivery).ThenInclude(d => d.Member)
|
||||
.Include(p => p.Variant)
|
||||
.ToList()) { }
|
||||
|
||||
public DeliveryJournal(AppDbContext ctx, DateOnly date) :
|
||||
this(date.ToString("dd.MM.yyyy"), ctx.DeliveryParts
|
||||
.Where(p => p.Delivery.DateString == date.ToString("yyy-MM-dd"))
|
||||
.OrderBy(p => p.Delivery.DateString)
|
||||
.ThenBy(p => p.Delivery.TimeString)
|
||||
.ThenBy(p => p.Delivery.LsNr)
|
||||
.ThenBy(p => p.DPNr)) { }
|
||||
}
|
||||
}
|
@@ -2,31 +2,143 @@
|
||||
@inherits TemplatePage<Elwig.Documents.DeliveryNote>
|
||||
@model Elwig.Documents.DeliveryNote
|
||||
@{ Layout = "BusinessDocument"; }
|
||||
|
||||
<h1>Traubenübernahmeschein Nr. @Model.Delivery.LsNr</h1>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowspan="2">Sorte</th>
|
||||
<th rowspan="2">Qualitätsstufe</th>
|
||||
<th colspan="2">Gradation</th>
|
||||
<th>Gewicht</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>°KMW</th>
|
||||
<th>°Oe</th>
|
||||
<th>kg</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var part in Model.Delivery.Parts.OrderBy(p => p.DPNr)) {
|
||||
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\style-deliverynote.css" />
|
||||
<main>
|
||||
<h1>@Model.Title</h1>
|
||||
<table class="delivery">
|
||||
<colgroup>
|
||||
<col style="width: 10.00mm;"/>
|
||||
<col style="width: 21.25mm;"/>
|
||||
<col style="width: 21.25mm;"/>
|
||||
<col style="width: 21.25mm;"/>
|
||||
<col style="width: 21.25mm;"/>
|
||||
<col style="width: 30.00mm;"/>
|
||||
<col style="width: 12.50mm;"/>
|
||||
<col style="width: 12.50mm;"/>
|
||||
<col style="width: 15.00mm;"/>
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>@part.Variant.Name</td>
|
||||
<td>@part.Quality.Name</td>
|
||||
<td>@part.Kmw</td>
|
||||
<td>@part.Oe</td>
|
||||
<td>@part.Weight</td>
|
||||
<th class="main" rowspan="2" style="text-align: center;">Pos.</th>
|
||||
<th class="main" rowspan="2" colspan="2">Sorte</th>
|
||||
<th class="main" rowspan="2" colspan="2">Attribut(e)</th>
|
||||
<th class="main" rowspan="2">Qualitätsstufe</th>
|
||||
<th colspan="2">Gradation</th>
|
||||
<th>Gewicht</th>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
<tr>
|
||||
<th style="font-size: 8pt;">[°Oe]</th>
|
||||
<th style="font-size: 8pt;">[°KMW]</th>
|
||||
<th style="font-size: 8pt;">[kg]</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var part in Model.Delivery.Parts.OrderBy(p => p.DPNr)) {
|
||||
<tr class="main">
|
||||
<td style="text-align: center;">@part.DPNr</td>
|
||||
<td colspan="2">@part.Variant.Name</td>
|
||||
<td colspan="2">@string.Join(" / ", part.Attributes)</td>
|
||||
<td>@part.Quality.Name</td>
|
||||
<td class="narrow" style="text-align: center;">@($"{part.Oe:N0}")</td>
|
||||
<td class="narrow" style="text-align: center;">@($"{part.Kmw:N1}")</td>
|
||||
<td class="narrow" style="text-align: right;">@($"{part.Weight:N0}")</td>
|
||||
</tr>
|
||||
<tr><td></td><td colspan="5" style="white-space: pre;"><i>Herkunft:</i> @part.OriginString</td></tr>
|
||||
@if (part.Modifiers.Count() > 0) {
|
||||
var first = true;
|
||||
foreach (var mod in part.Modifiers) {
|
||||
<tr class="tight @(first ? "first" : "")"><td></td><td>@Raw(first ? "<i>Zu-/Abschläge:</i>" : "")</td><td colspan="3"><b>@mod.Name</b></td><td style="white-space: pre;">@mod.ValueStr</td></tr>
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
<tr><td></td><td colspan="5">
|
||||
@Raw(part.ManualWeighing ? "<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
|
||||
</td></tr>
|
||||
@if (part.Comment != null) {
|
||||
<tr><td></td><td colspan="5"><i>Anmerkung:</i> @part.Comment</td></tr>
|
||||
}
|
||||
@if (part.Temperature != null || part.Acid != null) {
|
||||
<tr><td></td><td colspan="5">@Raw(part.Temperature != null ? $"<i>Temperatur:</i> {part.Temperature:N1} °C" : "")@(part.Temperature != null && part.Acid != null ? ", " : "")@Raw(part.Acid != null ? $"<i>Säure:</i> {part.Acid:N1} g/l" : "")</td></tr>
|
||||
}
|
||||
}
|
||||
@if (Model.Delivery.Parts.Count() > 1) {
|
||||
<tr class="main sum">
|
||||
<td colspan="6">Gesamt:</td>
|
||||
<td style="text-align: center;">@($"{Model.Delivery.Oe:N0}")</td>
|
||||
<td style="text-align: center;">@($"{Model.Delivery.Kmw:N1}")</td>
|
||||
<td style="text-align: right;">@($"{Model.Delivery.Weight:N0}")</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
@if (Model.Delivery.Comment != null) {
|
||||
<p class="comment">Amerkung zur Lieferung: @Model.Delivery.Comment</p>
|
||||
}
|
||||
@if (Model.DisplayStats > 0) {
|
||||
<table class="delivery-note-stats @(Model.DisplayStats > 2 ? "expanded" : "")">
|
||||
<colgroup>
|
||||
<col style="width: 45mm;"/>
|
||||
<col style="width: 20mm;"/>
|
||||
<col style="width: 20mm;"/>
|
||||
<col style="width: 20mm;"/>
|
||||
<col style="width: 20mm;"/>
|
||||
<col style="width: 20mm;"/>
|
||||
<col style="width: 20mm;"/>
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th><b>Lese @Model.Delivery.Year</b> per @($"{Model.Date:dd.MM.yyyy}") [kg]</th>
|
||||
<th>Lieferpflicht</th>
|
||||
<th>Lieferrecht</th>
|
||||
<th>Unterliefert</th>
|
||||
<th>Noch zu liefern</th>
|
||||
<th>Überliefert</th>
|
||||
<th>Geliefert</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@{
|
||||
string FormatRow(int obligation, int right, int sum) {
|
||||
return $"<td>{obligation:N0}</td>" +
|
||||
$"<td>{right:N0}</td>" +
|
||||
$"<td>{(sum < obligation ? $"{obligation - sum:N0}" : "-")}</td>" +
|
||||
$"<td>{(sum >= obligation && sum <= right ? $"{right - sum:N0}" : "-")}</td>" +
|
||||
$"<td>{(sum > right ? $"{sum - right:N0}" : "-")}</td>" +
|
||||
$"<td>{sum:N0}</td>";
|
||||
}
|
||||
var sortids = Model.Delivery.Parts.Select(p => p.SortId).ToList();
|
||||
var bins = Model.MemberBins.GroupBy(b => b.Key[..2]).ToDictionary(g => g.Key, g => g.Count());
|
||||
}
|
||||
<tr>
|
||||
<th>Gesamtlieferung lt. gez. GA</th>
|
||||
@Raw(FormatRow(Model.Member.DeliveryObligation, Model.Member.DeliveryRight, Model.Member.Deliveries.Where(d => d.Year == Model.Delivery.Year).Sum(d => d.Weight)))
|
||||
</tr>
|
||||
@if (Model.DisplayStats > 1) {
|
||||
<tr class="subheading">
|
||||
<th>Flächenbindungen:</th>
|
||||
</tr>
|
||||
@foreach (var (id, (name, right, obligation, sum, _)) in Model.MemberBins.OrderBy(b => b.Key)) {
|
||||
if (right > 0 || obligation > 0 || (sum > 0 && bins[id[..2]] > 1 && !id.EndsWith('_'))) {
|
||||
<tr class="@(sortids.Contains(id[..2]) ? "" : "optional")">
|
||||
<th>@name</th>
|
||||
@Raw(FormatRow(obligation, right, sum))
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
</main>
|
||||
@for (int i = 0; i < 2; i++) {
|
||||
<div class="text @(i == 0 ? "hidden" : "bottom")">
|
||||
@if (Model.Text != null) {
|
||||
<p class="comment">@Model.Text</p>
|
||||
}
|
||||
<div class="signatures">
|
||||
<div>Genossenschaft</div>
|
||||
<div>Mitglied</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
@@ -1,12 +1,33 @@
|
||||
using Elwig.Helpers;
|
||||
using Elwig.Models;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Elwig.Documents {
|
||||
public class DeliveryNote : BusinessDocument {
|
||||
|
||||
public Delivery Delivery;
|
||||
public string? Text;
|
||||
public Dictionary<string, (string, int, int, int, int)> MemberBins;
|
||||
|
||||
public DeliveryNote(Delivery d) : base($"Lieferschein {d.LsNr}", d.Member) {
|
||||
// 0 - none
|
||||
// 1 - GA only
|
||||
// 2 - GA only and area commitments of varieties from delivery note
|
||||
// 3 - full
|
||||
public int DisplayStats = App.Client.ModeDeliveryNoteStats;
|
||||
|
||||
public DeliveryNote(Delivery d, AppDbContext ctx) : base($"Traubenübernahmeschein Nr. {d.LsNr}", d.Member) {
|
||||
UseBillingAddress = true;
|
||||
ShowDateAndLocation = true;
|
||||
Delivery = d;
|
||||
Aside = Aside.Replace("</table>", "") +
|
||||
$"<thead><tr><th colspan='2'>Lieferung</th></tr></thead><tbody>" +
|
||||
$"<tr><th>LS-Nr.</th><td>{d.LsNr}</td></tr>" +
|
||||
$"<tr><th>Datum/Zeit</th><td>{d.Date:dd.MM.yyyy} / {d.Time:HH:mm}</td></tr>" +
|
||||
$"<tr><th>Zweigstelle</th><td>{d.Branch.Name}</td></tr>" +
|
||||
$"</tbody></table>";
|
||||
Text = App.Client.TextDeliveryNote;
|
||||
DocumentId = d.LsNr;
|
||||
MemberBins = ctx.GetMemberBins(d.Year, d.Member.MgNr).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,34 +1,33 @@
|
||||
@using RazorLight
|
||||
@inherits TemplatePage<Elwig.Documents.Document>
|
||||
@model Elwig.Documents.Document
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="de-AT">
|
||||
<head>
|
||||
<title>@Model.Title</title>
|
||||
<meta name="author" value="@Model.Author"/>
|
||||
<meta charset="UTF-8"/>
|
||||
<script>
|
||||
window.PagedConfig = { auto: false };
|
||||
if (!navigator.webdriver) {
|
||||
window.addEventListener("beforeprint", async () => { await window.PagedPolyfill.preview(); });
|
||||
window.addEventListener("afterprint", () => { location.reload(); });
|
||||
}
|
||||
</script>
|
||||
<script src="@Raw(Model.DataPath)\resources\paged.polyfill.js"></script>
|
||||
<link rel="stylesheet" href="@Raw(Model.DataPath)\resources\style.css" />
|
||||
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\style.css"/>
|
||||
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\style-page.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="m1"></div>
|
||||
<div class="m2"></div>
|
||||
<div class="m3"></div>
|
||||
<header>@Raw(Model.Header)</header>
|
||||
@if (Model.ShowFoldMarks) {
|
||||
<div class="m1"></div>
|
||||
<div class="m2"></div>
|
||||
<div class="m3"></div>
|
||||
<div class="m1 r"></div>
|
||||
<div class="m2 r"></div>
|
||||
<div class="m3 r"></div>
|
||||
}
|
||||
<div class="footer-wrapper">
|
||||
<div class="pre-footer">
|
||||
<span class="date">@Model.FullDateString</span>
|
||||
<span class="date">@($"{Model.Date:dddd, d. MMMM yyyy}")</span>
|
||||
<span class="doc-id">@Model.DocumentId</span>
|
||||
<span class="page"></span>
|
||||
</div>
|
||||
<footer>@Raw(Model.Footer)</footer>
|
||||
</div>
|
||||
<header>@Raw(Model.Header)</header>
|
||||
<div class="spacing"></div>
|
||||
<div class="main-wrapper">
|
||||
@RenderBody()
|
||||
|
@@ -2,78 +2,143 @@ using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
using Elwig.Helpers;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Linq;
|
||||
|
||||
namespace Elwig.Documents {
|
||||
public abstract class Document : IDisposable {
|
||||
public abstract partial class Document : IDisposable {
|
||||
|
||||
private TempFile? PdfFile = null;
|
||||
private TempFile? _pdfFile = null;
|
||||
private string? _renderedHtml = null;
|
||||
|
||||
public bool ShowFoldMarks = App.Config.Debug;
|
||||
|
||||
public string DataPath;
|
||||
public int CurrentNextSeason;
|
||||
public string? DocumentId;
|
||||
public string Title;
|
||||
public string Author;
|
||||
public string Header;
|
||||
public string Footer;
|
||||
public DateTime Date;
|
||||
|
||||
public Document(string title) {
|
||||
var c = App.Client;
|
||||
DataPath = App.DataPath;
|
||||
CurrentNextSeason = Utils.CurrentNextSeason;
|
||||
Title = title;
|
||||
Header = $"<h1>{App.Client.Name}</h1>";
|
||||
Footer = App.Client.NameFull;
|
||||
Author = c.NameFull;
|
||||
Header = $"<div class='name'>{c.Name}</div><div class='suffix'>{c.NameSuffix}</div><div class='type'>{c.NameTypeFull}</div>";
|
||||
Footer = Utils.GenerateFooter("<br/>", " \u00b7 ")
|
||||
.Item(c.NameFull).NextLine()
|
||||
.Item(c.Address).Item($"{c.Plz} {c.Ort}").Item("Österreich").Item("Tel.", c.PhoneNr).Item("Fax", c.FaxNr).NextLine()
|
||||
.Item(c.EmailAddress).Item(c.Website).Item("Betriebs-Nr.", c.LfbisNr).Item("UID", c.UstIdNr).NextLine()
|
||||
.Item("BIC", c.Bic).Item("IBAN", c.Iban)
|
||||
.ToString();
|
||||
Date = DateTime.Today;
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"</body>.*?</footer>\s*</div>", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled)]
|
||||
private static partial Regex GeneratedDocumentHeaderRegex();
|
||||
private static readonly Regex DocumentHeaderRegex = GeneratedDocumentHeaderRegex();
|
||||
|
||||
[GeneratedRegex(@"<style>.*?/style>", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled)]
|
||||
private static partial Regex GeneratedHtmlStyleRegex();
|
||||
private static readonly Regex HtmlStyleRegex = GeneratedHtmlStyleRegex();
|
||||
|
||||
[GeneratedRegex(@"<link[^>]*>", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled)]
|
||||
private static partial Regex GeneratedHtmlLinkRegex();
|
||||
private static readonly Regex HtmlLinkRegex = GeneratedHtmlLinkRegex();
|
||||
|
||||
~Document() {
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
PdfFile?.Dispose();
|
||||
PdfFile = null;
|
||||
_pdfFile?.Dispose();
|
||||
_pdfFile = null;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public string DataPath { get; set; }
|
||||
|
||||
public string Title { get; set; }
|
||||
|
||||
public string Header { get; set; }
|
||||
|
||||
public string Footer { get; set; }
|
||||
|
||||
public string FullDateString {
|
||||
get => Date.ToString("dddd, d. MMMM yyyy");
|
||||
public static async Task<Document> Merge(IEnumerable<Document> docs) {
|
||||
string html = "";
|
||||
var styles = new List<string>();
|
||||
foreach (var d in docs) {
|
||||
var h = await d.Render();
|
||||
var s = HtmlStyleRegex.Matches(h).Select(m => m.Value).ToList();
|
||||
var l = HtmlLinkRegex.Matches(h).Select(m => m.Value).ToList();
|
||||
if (s.All(styles.Contains)) {
|
||||
h = HtmlStyleRegex.Replace(h, "");
|
||||
} else {
|
||||
styles.AddRange(s);
|
||||
}
|
||||
if (l.All(styles.Contains)) {
|
||||
h = HtmlLinkRegex.Replace(h, "");
|
||||
} else {
|
||||
styles.AddRange(l);
|
||||
}
|
||||
html += h;
|
||||
}
|
||||
html = DocumentHeaderRegex.Replace(html, "<div class='document-break'/>");
|
||||
return new InternalDocument("Mehrere Dokumente") {
|
||||
_renderedHtml = html,
|
||||
};
|
||||
}
|
||||
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
private async Task<string> Render() {
|
||||
if (_renderedHtml != null)
|
||||
return _renderedHtml;
|
||||
string name;
|
||||
if (this is BusinessLetter) {
|
||||
name = "BusinessLetter";
|
||||
} else if (this is DeliveryNote) {
|
||||
name = "DeliveryNote";
|
||||
} else if (this is CreditNote) {
|
||||
name = "CreditNote";
|
||||
} else if (this is DeliveryJournal) {
|
||||
name = "DeliveryJournal";
|
||||
} else if (this is Letterhead) {
|
||||
name = "Letterhead";
|
||||
} else if (this is DeliveryConfirmation) {
|
||||
name = "DeliveryConfirmation";
|
||||
} else {
|
||||
throw new InvalidOperationException("Invalid document object");
|
||||
}
|
||||
return await Html.CompileRenderAsync(name, this);
|
||||
return await Render(name);
|
||||
}
|
||||
|
||||
private async Task<string> Render(string name) {
|
||||
_renderedHtml = await Html.CompileRenderAsync(name, this);
|
||||
return _renderedHtml;
|
||||
}
|
||||
|
||||
public async Task Generate() {
|
||||
var pdf = new TempFile("pdf");
|
||||
using (var tmpHtml = new TempFile("html")) {
|
||||
await File.WriteAllTextAsync(tmpHtml.FilePath, await Render());
|
||||
await File.WriteAllTextAsync(tmpHtml.FilePath, await Render(), Utils.UTF8);
|
||||
await Pdf.Convert(tmpHtml.FilePath, pdf.FilePath);
|
||||
}
|
||||
Pdf.UpdateMetadata(pdf.FilePath, Title, "Winzergenossenschaft für Matzen und Umgebung reg. Gen.m.b.H.");
|
||||
PdfFile = pdf;
|
||||
_pdfFile = pdf;
|
||||
}
|
||||
|
||||
public void SaveTo(string pdfPath) {
|
||||
if (PdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet");
|
||||
File.Copy(PdfFile.FilePath, pdfPath);
|
||||
if (_pdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet");
|
||||
File.Copy(_pdfFile.FilePath, pdfPath);
|
||||
}
|
||||
|
||||
public async Task Print() {
|
||||
if (PdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet");
|
||||
Pdf.Print(PdfFile.FilePath);
|
||||
public async Task Print(int copies = 1) {
|
||||
if (_pdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet");
|
||||
await Pdf.Print(_pdfFile.FilePath, copies);
|
||||
}
|
||||
|
||||
public void Show() {
|
||||
if (PdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet");
|
||||
Pdf.Show(PdfFile.NewReference(), Title);
|
||||
if (_pdfFile == null) throw new InvalidOperationException("Pdf file has not been generated yet");
|
||||
Pdf.Show(_pdfFile.NewReference(), Title + (this is BusinessDocument b ? $" - {b.Member.Name}" : ""));
|
||||
}
|
||||
|
||||
private class InternalDocument : Document {
|
||||
public InternalDocument(string title) : base(title) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -18,6 +18,10 @@ namespace Elwig.Documents {
|
||||
await e.CompileTemplateAsync("BusinessDocument");
|
||||
await e.CompileTemplateAsync("BusinessLetter");
|
||||
await e.CompileTemplateAsync("DeliveryNote");
|
||||
await e.CompileTemplateAsync("CreditNote");
|
||||
await e.CompileTemplateAsync("DeliveryJournal");
|
||||
await e.CompileTemplateAsync("Letterhead");
|
||||
await e.CompileTemplateAsync("DeliveryConfirmation");
|
||||
|
||||
Engine = e;
|
||||
evtHandler();
|
||||
|
9
Elwig/Documents/Letterhead.cshtml
Normal file
9
Elwig/Documents/Letterhead.cshtml
Normal file
@@ -0,0 +1,9 @@
|
||||
@using RazorLight
|
||||
@inherits TemplatePage<Elwig.Documents.Letterhead>
|
||||
@model Elwig.Documents.Letterhead
|
||||
@{ Layout = "BusinessDocument"; }
|
||||
<style>
|
||||
header, .footer-wrapper {
|
||||
visibility: hidden;
|
||||
}
|
||||
</style>
|
9
Elwig/Documents/Letterhead.cshtml.cs
Normal file
9
Elwig/Documents/Letterhead.cshtml.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Elwig.Models;
|
||||
|
||||
namespace Elwig.Documents {
|
||||
public class Letterhead : BusinessDocument {
|
||||
public Letterhead(Member m) : base($"Briefkopf {m.Name}", m, true) {
|
||||
Aside = "";
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,62 +1,45 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using PdfSharp.Pdf.IO;
|
||||
using PuppeteerSharp;
|
||||
using Elwig.Helpers;
|
||||
using Elwig.Windows;
|
||||
using System.Diagnostics;
|
||||
using Balbarak.WeasyPrint;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Elwig.Documents {
|
||||
public static class Pdf {
|
||||
|
||||
private static readonly string Chromium = @"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe";
|
||||
private static IBrowser? Browser = null;
|
||||
public static bool IsReady => Browser != null;
|
||||
private static readonly string PdfToPrinter = App.ExePath + "PDFtoPrinter.exe";
|
||||
private static readonly FilesManager WeasyPrintManager = new();
|
||||
private static string? WeasyPrintPython = null;
|
||||
private static string? WeasyPrintDir => WeasyPrintManager.FolderPath;
|
||||
public static bool IsReady => WeasyPrintPython != null && WeasyPrintDir != null;
|
||||
|
||||
public static async Task Init(Action evtHandler) {
|
||||
Browser = await Puppeteer.LaunchAsync(new LaunchOptions {
|
||||
Headless = true,
|
||||
ExecutablePath = Chromium,
|
||||
// paged.js uses XHRs to load styles, so this is needed
|
||||
Args = new[] { "--allow-file-access-from-files" },
|
||||
});
|
||||
if (!WeasyPrintManager.IsFilesExsited()) {
|
||||
await WeasyPrintManager.InitFilesAsync();
|
||||
}
|
||||
WeasyPrintPython = Path.Combine(WeasyPrintManager.FolderPath, "python.exe");
|
||||
evtHandler();
|
||||
}
|
||||
|
||||
public static async Task Close() {
|
||||
if (Browser == null) return;
|
||||
await Browser.CloseAsync();
|
||||
Browser = null;
|
||||
}
|
||||
|
||||
public static async Task Convert(string htmlPath, string pdfPath) {
|
||||
if (Browser == null) throw new InvalidOperationException("The puppeteer engine has not been initialized yet");
|
||||
using var page = await Browser.NewPageAsync();
|
||||
page.Console += OnConsole;
|
||||
await page.GoToAsync($"file://{htmlPath}");
|
||||
await page.EvaluateFunctionAsync("async () => { await window.PagedPolyfill.preview(); }");
|
||||
await page.PdfAsync(pdfPath, new() {
|
||||
PreferCSSPageSize = true,
|
||||
//Format = PaperFormat.A4,
|
||||
DisplayHeaderFooter = false,
|
||||
MarginOptions = new() {
|
||||
Top = "0mm",
|
||||
Right = "0mm",
|
||||
Bottom = "0mm",
|
||||
Left = "0mm",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private static void OnConsole(object sender, ConsoleEventArgs e) {
|
||||
MessageBox.Show(e.Message.Text);
|
||||
}
|
||||
|
||||
public static void UpdateMetadata(string path, string title, string author) {
|
||||
using var doc = PdfReader.Open(path);
|
||||
doc.Info.Title = title;
|
||||
doc.Info.Author = author;
|
||||
doc.Save(path);
|
||||
var p = new Process() { StartInfo = new() {
|
||||
FileName = WeasyPrintPython,
|
||||
CreateNoWindow = true,
|
||||
WorkingDirectory = WeasyPrintDir,
|
||||
RedirectStandardError = true,
|
||||
} };
|
||||
p.StartInfo.EnvironmentVariables["PATH"] = "Scripts;gtk3;" + Environment.GetEnvironmentVariable("PATH");
|
||||
p.StartInfo.ArgumentList.Add("scripts/weasyprint.exe");
|
||||
p.StartInfo.ArgumentList.Add("-e");
|
||||
p.StartInfo.ArgumentList.Add("utf8");
|
||||
p.StartInfo.ArgumentList.Add(htmlPath);
|
||||
p.StartInfo.ArgumentList.Add(pdfPath);
|
||||
p.Start();
|
||||
await p.WaitForExitAsync();
|
||||
var stderr = await p.StandardError.ReadToEndAsync();
|
||||
if (p.ExitCode != 0) throw new Exception(stderr);
|
||||
}
|
||||
|
||||
public static void Show(TempFile file, string title) {
|
||||
@@ -73,8 +56,13 @@ namespace Elwig.Documents {
|
||||
});
|
||||
}
|
||||
|
||||
public static void Print(string path) {
|
||||
// TODO print pdf
|
||||
public static async Task Print(string path, int copies = 1) {
|
||||
var p = new Process() { StartInfo = new() { FileName = PdfToPrinter } };
|
||||
p.StartInfo.ArgumentList.Add(path);
|
||||
p.StartInfo.ArgumentList.Add("/s");
|
||||
p.StartInfo.ArgumentList.Add($"copies={copies}");
|
||||
p.Start();
|
||||
await p.WaitForExitAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
142
Elwig/Documents/style-businessdocument.css
Normal file
142
Elwig/Documents/style-businessdocument.css
Normal file
@@ -0,0 +1,142 @@
|
||||
|
||||
.address-wrapper, aside, main {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.spacing {
|
||||
height: 20mm;
|
||||
}
|
||||
|
||||
.info-wrapper {
|
||||
width: 100%;
|
||||
height: 45mm;
|
||||
margin: 0 0 2mm 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.info-wrapper .date {
|
||||
text-align: right;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: -1.5em;
|
||||
}
|
||||
|
||||
.address-wrapper {
|
||||
height: 45mm;
|
||||
width: 85mm;
|
||||
margin: 0;
|
||||
padding: 5mm;
|
||||
position: absolute;
|
||||
left: -5mm;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.address-wrapper .sender {
|
||||
height: 4em;
|
||||
font-size: 8pt;
|
||||
padding: 1em 0;
|
||||
}
|
||||
|
||||
address {
|
||||
height: 5em;
|
||||
white-space: pre-line;
|
||||
font-size: 12pt;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
aside {
|
||||
height: 40mm;
|
||||
width: 75mm;
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
left: 100mm;
|
||||
top: 5mm;
|
||||
}
|
||||
|
||||
aside table {
|
||||
border-collapse: collapse;
|
||||
border: 0.5pt solid #808080;
|
||||
width: 65mm;
|
||||
margin-right: 10mm;
|
||||
}
|
||||
|
||||
aside table thead:not(:first-child) tr {
|
||||
border-top: 0.5pt solid #808080;
|
||||
}
|
||||
|
||||
aside table thead th {
|
||||
background-color: #E0E0E0;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
aside table tbody th,
|
||||
aside table tbody td {
|
||||
text-align: left;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
aside table tbody th {
|
||||
padding: 0.25mm 0.5mm 0.25mm 1mm;
|
||||
}
|
||||
|
||||
aside table tbody td {
|
||||
padding: 0.25mm 0;
|
||||
}
|
||||
|
||||
aside table tbody th {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
main {
|
||||
margin: 2em 0 1em 0;
|
||||
}
|
||||
|
||||
main > *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
main h1, .main-wrapper p {
|
||||
font-size: 12pt;
|
||||
margin: 1em 0;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.main-wrapper p {
|
||||
widows: 3;
|
||||
orphans: 3;
|
||||
hyphens: manual;
|
||||
}
|
||||
|
||||
main h1 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.main-wrapper p.comment {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
.main-wrapper .hidden {
|
||||
break-before: avoid;
|
||||
}
|
||||
|
||||
.main-wrapper .bottom {
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
width: 165mm;
|
||||
}
|
||||
|
||||
.main-wrapper .signatures {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
margin: 20mm 0 2mm 0;
|
||||
}
|
||||
|
||||
.main-wrapper .signatures > * {
|
||||
width: 50mm;
|
||||
border-top: 0.5pt solid black;
|
||||
padding-top: 1mm;
|
||||
text-align: center;
|
||||
font-size: 10pt;
|
||||
}
|
64
Elwig/Documents/style-creditnote.css
Normal file
64
Elwig/Documents/style-creditnote.css
Normal file
@@ -0,0 +1,64 @@
|
||||
|
||||
table.credit {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
table.credit th,
|
||||
table.credit td {
|
||||
padding: 0 0.25mm;
|
||||
}
|
||||
|
||||
table.credit thead {
|
||||
font-size: 8pt
|
||||
}
|
||||
|
||||
table.credit thead th {
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
table.credit td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
table.credit .oe,
|
||||
table.credit .kmw {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table.credit .dpnr {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table.credit .amount,
|
||||
table.credit .weight {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
table.credit .rel,
|
||||
table.credit .abs {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table.credit .amount.sum {
|
||||
vertical-align: bottom;
|
||||
padding-bottom: 1mm;
|
||||
}
|
||||
|
||||
table.credit tbody tr:not(.first):not(.last) {
|
||||
break-before: avoid;
|
||||
break-after: avoid;
|
||||
}
|
||||
|
||||
table.credit tbody tr.first td {
|
||||
padding-top: 1mm;
|
||||
}
|
||||
|
||||
table.credit tbody tr.last td {
|
||||
padding-bottom: 1mm;
|
||||
}
|
||||
|
||||
table.credit tbody tr.new {
|
||||
border-top: 0.5pt solid black;
|
||||
}
|
113
Elwig/Documents/style-deliveryconfirmation.css
Normal file
113
Elwig/Documents/style-deliveryconfirmation.css
Normal file
@@ -0,0 +1,113 @@
|
||||
|
||||
table.delivery-confirmation {
|
||||
font-size: 10pt;
|
||||
margin-bottom: 5mm;
|
||||
}
|
||||
|
||||
table.delivery-confirmation thead {
|
||||
font-size: 8pt;
|
||||
}
|
||||
|
||||
table.delivery-confirmation thead th {
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
table.delivery-confirmation td {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
table.delivery-confirmation td[rowspan] {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
table.delivery-confirmation .weight {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
table.delivery-confirmation .grad {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table.delivery-confirmation .geb {
|
||||
font-size: 8pt;
|
||||
}
|
||||
|
||||
table.delivery-confirmation .mod {
|
||||
font-size: 8pt;
|
||||
padding-left: 5mm;
|
||||
}
|
||||
|
||||
table.delivery-confirmation .small {
|
||||
font-size: 8pt;
|
||||
}
|
||||
|
||||
table.delivery-confirmation tr.new td {
|
||||
border-top: 0.5pt solid black;
|
||||
}
|
||||
|
||||
table.delivery-confirmation tr:not(.first) {
|
||||
break-before: avoid;
|
||||
}
|
||||
|
||||
table.delivery-confirmation tr:not(.first) td {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
table.delivery-confirmation tr.trailing td {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
table.delivery-confirmation tr.sum {
|
||||
border-top: 0.5pt solid black;
|
||||
break-before: avoid;
|
||||
font-weight: bold;
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
table.delivery-confirmation tr.sum td {
|
||||
padding-top: 1mm;
|
||||
}
|
||||
|
||||
table.delivery-confirmation-stats {
|
||||
font-size: 10pt;
|
||||
break-inside: avoid;
|
||||
}
|
||||
|
||||
table.delivery-confirmation-stats th,
|
||||
table.delivery-confirmation-stats td {
|
||||
padding: 0.125mm 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
table.delivery-confirmation-stats tr.subheading th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table.delivery-confirmation-stats thead th {
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
text-align: right;
|
||||
font-size: 8pt;
|
||||
}
|
||||
|
||||
table.delivery-confirmation-stats thead th:first-child {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table.delivery-confirmation-stats td {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
table.delivery-confirmation-stats tbody th {
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table.delivery-confirmation-stats tr.subheading th {
|
||||
font-weight: bold;
|
||||
border-top: 0.5pt solid black;
|
||||
}
|
47
Elwig/Documents/style-deliveryjournal.css
Normal file
47
Elwig/Documents/style-deliveryjournal.css
Normal file
@@ -0,0 +1,47 @@
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
font-size: 24pt;
|
||||
margin-bottom: 2mm;
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
font-size: 14pt;
|
||||
margin-top: 2mm;
|
||||
}
|
||||
|
||||
table.journal {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
table.journal thead {
|
||||
font-size: 8pt;
|
||||
}
|
||||
|
||||
table.journal th {
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
table.journal td {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
table.journal .mgnr,
|
||||
table.journal .weight {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
table.journal .grad {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table.journal tr.sum {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table.journal tr.sum td {
|
||||
border-top: 0.5pt solid black;
|
||||
}
|
97
Elwig/Documents/style-deliverynote.css
Normal file
97
Elwig/Documents/style-deliverynote.css
Normal file
@@ -0,0 +1,97 @@
|
||||
|
||||
main h1 {
|
||||
margin-bottom: 1.5em !important;
|
||||
}
|
||||
|
||||
table.delivery {
|
||||
margin-bottom: 5mm;
|
||||
}
|
||||
|
||||
table.delivery tr:not(.main) {
|
||||
break-before: avoid;
|
||||
}
|
||||
|
||||
table.delivery th {
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
table.delivery th.main {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table.delivery tr.main td {
|
||||
font-weight: bold;
|
||||
padding-top: 2mm;
|
||||
}
|
||||
|
||||
table.delivery tbody tr:not(.main) td {
|
||||
font-size: 8pt;
|
||||
}
|
||||
|
||||
table.delivery tr.tight:not(.first) td {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
table.delivery tr.tight.first td {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
table.delivery tr.tight:has(+ tr:not(.tight)) td {
|
||||
padding-bottom: 0.5mm !important;
|
||||
}
|
||||
|
||||
table.delivery tr.sum {
|
||||
border-top: 0.5pt solid black;
|
||||
break-before: avoid;
|
||||
}
|
||||
|
||||
table.delivery tr.sum td {
|
||||
padding-top: 1mm;
|
||||
}
|
||||
|
||||
table.delivery-note-stats {
|
||||
font-size: 8pt;
|
||||
break-inside: avoid;
|
||||
break-after: avoid;
|
||||
}
|
||||
|
||||
table.delivery-note-stats th,
|
||||
table.delivery-note-stats td {
|
||||
padding: 0.125mm 0;
|
||||
}
|
||||
|
||||
table.delivery-note-stats:not(.expanded) tr.optional {
|
||||
display: none;
|
||||
}
|
||||
|
||||
table.delivery-note-stats tr.subheading th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table.delivery-note-stats.expanded tr.subheading:not(:has(~ tr)),
|
||||
table.delivery-note-stats tr.subheading:not(:has(~ tr:not(.optional))) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
table.delivery-note-stats thead th {
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
table.delivery-note-stats thead th:first-child {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table.delivery-note-stats td {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
table.delivery-note-stats tbody th {
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
text-align: left;
|
||||
}
|
74
Elwig/Documents/style-page.css
Normal file
74
Elwig/Documents/style-page.css
Normal file
@@ -0,0 +1,74 @@
|
||||
|
||||
.m1, .m2, .m3 {
|
||||
height: 0;
|
||||
width: 10mm;
|
||||
position: fixed;
|
||||
left: -25mm;
|
||||
border-top: 0.5pt solid black;
|
||||
}
|
||||
.m1.r, .m2.r, .m3.r {
|
||||
left: initial;
|
||||
right: -20mm;
|
||||
}
|
||||
.m1 {top: 80mm;}
|
||||
.m2 {top: 123.5mm;}
|
||||
.m3 {top: 185mm;}
|
||||
|
||||
.page-break {
|
||||
break-before: page;
|
||||
}
|
||||
hr.page-break {
|
||||
display: none;
|
||||
}
|
||||
.document-break {
|
||||
break-before: page;
|
||||
}
|
||||
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 25mm 20mm 35mm 25mm;
|
||||
|
||||
@bottom-center {
|
||||
content: element(page-footer);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen {
|
||||
body, header, .footer-wrapper {
|
||||
width: 210mm;
|
||||
}
|
||||
|
||||
header, .address-wrapper, aside, main {
|
||||
border: 1px solid lightgray;
|
||||
}
|
||||
|
||||
.m1, .m2, .m3 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
header {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.spacing {
|
||||
height: 45mm;
|
||||
}
|
||||
|
||||
.main-wrapper {
|
||||
margin: 0 20mm 40mm 25mm;
|
||||
}
|
||||
|
||||
.footer-wrapper {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
.page::after {
|
||||
content: "Seite " counter(page) " von " counter(pages) !important;
|
||||
}
|
||||
}
|
@@ -12,170 +12,95 @@ body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.m1, .m2, .m3 {
|
||||
height: 0;
|
||||
width: 1cm;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
border-top: 1pt solid black;
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.m1 {top: 105mm;}
|
||||
.m2 {top: 148.5mm;}
|
||||
.m3 {top: 210mm;}
|
||||
table td,
|
||||
table th {
|
||||
padding: 0.5mm 1mm;
|
||||
}
|
||||
|
||||
table th {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
header {
|
||||
height: 45mm;
|
||||
padding: 5mm;
|
||||
padding: 10mm 0 0 0;
|
||||
position: absolute;
|
||||
top: -25mm;
|
||||
left: 0;
|
||||
right: 0;
|
||||
text-align: center;
|
||||
}
|
||||
.spacing {height: 20mm;}
|
||||
.info-wrapper {
|
||||
width: 100%;
|
||||
height:45mm;
|
||||
margin: 0 0 8.46mm 0;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.address-wrapper {
|
||||
height: 45mm;
|
||||
width: 85mm;
|
||||
margin: 0;
|
||||
padding: 5mm;
|
||||
position: absolute;
|
||||
left: 20mm;
|
||||
top: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
header .name {
|
||||
font-size: 18pt;
|
||||
margin-top: 8mm;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.address-wrapper .sender {
|
||||
flex: 17.7mm 1 1;
|
||||
font-size: 8pt;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
padding-bottom: 2mm;
|
||||
header .suffix {
|
||||
font-size: 14pt;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
address {
|
||||
flex: 27.3mm 1 1;
|
||||
white-space: pre-line;
|
||||
header .type {
|
||||
font-size: 12pt;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
aside {
|
||||
height: 40mm;
|
||||
width: 75mm;
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
left: 125mm;
|
||||
top: 5mm;
|
||||
}
|
||||
|
||||
main {
|
||||
margin: 8.46mm 20mm 4.23mm 25mm;
|
||||
}
|
||||
|
||||
main :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
main h1, main p {
|
||||
font-size: 12pt;
|
||||
margin: 1em 0;
|
||||
text-align: justify;
|
||||
widows: 3;
|
||||
orphans: 3;
|
||||
}
|
||||
|
||||
main .date {
|
||||
margin-bottom: 2em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
main h1 {
|
||||
margin-bottom: 2em;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.footer-wrapper {
|
||||
padding: 0 20mm 0 25mm;
|
||||
position: running(page-footer);
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 165mm;
|
||||
}
|
||||
|
||||
.pre-footer {
|
||||
margin: 4.23mm 0;
|
||||
margin: 1em 0;
|
||||
font-size: 10pt;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.pre-footer .date, .pre-footer .page {
|
||||
flex: 100px 1 1;
|
||||
.pre-footer > * {
|
||||
display: inline-block;
|
||||
width: 33%;
|
||||
}
|
||||
|
||||
.pre-footer .date {text-align: left;}
|
||||
.pre-footer .page {text-align: right;}
|
||||
.page::after {
|
||||
.pre-footer .date {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.pre-footer .doc-id {
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.pre-footer .page {
|
||||
text-align: right;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.pre-footer .page::after {
|
||||
content: "Seite 1 von 1";
|
||||
}
|
||||
|
||||
footer {
|
||||
font-size: 10pt;
|
||||
border-top: 1pt solid black;
|
||||
border-top: 0.5pt solid black;
|
||||
height: 25mm;
|
||||
padding-top: 1mm;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
.hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 25mm 0 35mm 0;
|
||||
@bottom-center {
|
||||
content: element(page-footer);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen {
|
||||
body, header, .footer-wrapper {
|
||||
width: 210mm;
|
||||
}
|
||||
header, .address-wrapper, aside, main {
|
||||
border: 1px solid lightgray;
|
||||
}
|
||||
.m1, .m2, .m3 {display: none;}
|
||||
header {top: 0;}
|
||||
.spacing {height: 45mm;}
|
||||
.main-wrapper {
|
||||
margin-bottom: 40mm;
|
||||
}
|
||||
.footer-wrapper {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
.page::after {
|
||||
content: "Seite " counter(page) " von " counter(pages);
|
||||
}
|
||||
.footer-wrapper {
|
||||
display: none;
|
||||
}
|
||||
hr {
|
||||
border: none;
|
||||
border-top: 0.5pt solid black;
|
||||
margin: 5mm 0;
|
||||
}
|
||||
|
@@ -7,6 +7,8 @@
|
||||
<UseWPF>true</UseWPF>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
<ApplicationIcon>elwig.ico</ApplicationIcon>
|
||||
<Version>0.4.1</Version>
|
||||
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -14,15 +16,16 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.5.0" />
|
||||
<PackageReference Include="Balbarak.WeasyPrint" Version="2.0.2" />
|
||||
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.5.1" />
|
||||
<PackageReference Include="ini-parser" Version="2.5.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.16" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.5" />
|
||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1774.30" />
|
||||
<PackageReference Include="PdfSharp" Version="1.50.5147" />
|
||||
<PackageReference Include="PuppeteerSharp" Version="10.0.0" />
|
||||
<PackageReference Include="LinqKit" Version="1.2.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.22" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="7.0.11" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.11" />
|
||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1938.49" />
|
||||
<PackageReference Include="RazorLight" Version="2.3.1" />
|
||||
<PackageReference Include="ScottPlot.WPF" Version="4.1.67" />
|
||||
<PackageReference Include="System.IO.Ports" Version="7.0.0" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
|
@@ -6,6 +6,9 @@ using System.IO;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Elwig.Helpers {
|
||||
public class AppDbContext : DbContext {
|
||||
@@ -28,17 +31,20 @@ namespace Elwig.Helpers {
|
||||
public DbSet<WineAttr> WineAttributes { get; private set; }
|
||||
public DbSet<WineCult> WineCultivations { get; private set; }
|
||||
public DbSet<Branch> Branches { get; private set; }
|
||||
public DbSet<AreaComType> AreaCommitmentTypes { get; private set; }
|
||||
public DbSet<Member> Members { get; private set; }
|
||||
public DbSet<BillingAddr> BillingAddresses { get; private set; }
|
||||
public DbSet<MemberTelNr> MemberTelephoneNrs { get; private set; }
|
||||
public DbSet<AreaCom> AreaCommitments { get; private set; }
|
||||
public DbSet<AreaComAttr> AreaCommitmentAttributes { get; private set; }
|
||||
public DbSet<Season> Seasons { get; private set; }
|
||||
public DbSet<Modifier> Modifiers { get; private set; }
|
||||
public DbSet<Delivery> Deliveries { get; private set; }
|
||||
public DbSet<DeliveryPart> DeliveryParts { get; private set; }
|
||||
public DbSet<DeliveryPartAttr> DeliveryPartAttributes { get; private set; }
|
||||
public DbSet<DeliveryPartModifier> DeliveryPartModifiers { get; private set; }
|
||||
public DbSet<PaymentVar> PaymentVariants { get; private set; }
|
||||
public DbSet<PaymentMember> MemberPayments { get; private set; }
|
||||
public DbSet<Credit> Credits { get; private set; }
|
||||
|
||||
private readonly StreamWriter? LogFile = null;
|
||||
public static DateTime LastWriteTime => File.GetLastWriteTime(App.Config.DatabaseFile);
|
||||
@@ -47,6 +53,10 @@ namespace Elwig.Helpers {
|
||||
|
||||
public static string ConnectionString => $"Data Source=\"{App.Config.DatabaseFile}\"; Foreign Keys=True; Mode=ReadWrite; Cache=Default";
|
||||
|
||||
private readonly Dictionary<int, Dictionary<int, Dictionary<string, (int, int)>>> _memberRightsAndObligations = new();
|
||||
private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberDeliveryBins = new();
|
||||
private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberPaymentBins = new();
|
||||
|
||||
public AppDbContext() {
|
||||
if (App.Config.DatabaseLog != null) {
|
||||
try {
|
||||
@@ -60,7 +70,20 @@ namespace Elwig.Helpers {
|
||||
}
|
||||
SavedLastWriteTime = LastWriteTime;
|
||||
SavedChanges += OnSavedChanges;
|
||||
}
|
||||
|
||||
public static SqliteConnection Connect() {
|
||||
var cnx = new SqliteConnection(ConnectionString);
|
||||
cnx.CreateFunction<string, string?, bool?>("REGEXP", (pattern, value) => value == null ? null : Regex.Match(value, pattern).Success, true);
|
||||
cnx.Open();
|
||||
return cnx;
|
||||
}
|
||||
|
||||
public static async Task<SqliteConnection> ConnectAsync() {
|
||||
var cnx = new SqliteConnection(ConnectionString);
|
||||
cnx.CreateFunction<string, string?, bool?>("REGEXP", (pattern, value) => value == null ? null : Regex.Match(value, pattern).Success, true);
|
||||
await cnx.OpenAsync();
|
||||
return cnx;
|
||||
}
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
|
||||
@@ -103,14 +126,14 @@ namespace Elwig.Helpers {
|
||||
public async Task<int> NextMgNr() {
|
||||
int c = await Members.Select(m => m.MgNr).MinAsync();
|
||||
(await Members.OrderBy(m => m.MgNr).Select(m => m.MgNr).ToListAsync())
|
||||
.ForEach(a => { if (a <= c + 100) c = a; });
|
||||
.ForEach(a => { if (a <= c + 1000) c = a; });
|
||||
return c + 1;
|
||||
}
|
||||
|
||||
public async Task<int> NextFbNr() {
|
||||
int c = await AreaCommitments.Select(ac => ac.FbNr).MinAsync();
|
||||
(await AreaCommitments.OrderBy(ac => ac.FbNr).Select(ac => ac.FbNr).ToListAsync())
|
||||
.ForEach(a => { if (a <= c + 100) c = a; });
|
||||
.ForEach(a => { if (a <= c + 1000) c = a; });
|
||||
return c + 1;
|
||||
}
|
||||
|
||||
@@ -118,7 +141,21 @@ namespace Elwig.Helpers {
|
||||
var dateStr = date.ToString("yyyy-MM-dd");
|
||||
int c = 0;
|
||||
(await Deliveries.Where(d => d.DateString == dateStr).Select(d => d.LNr).ToListAsync())
|
||||
.ForEach(a => { if (a <= c + 10) c = a; });
|
||||
.ForEach(a => { if (a <= c + 100) c = a; });
|
||||
return c + 1;
|
||||
}
|
||||
|
||||
public async Task<int> NextDId(int year) {
|
||||
int c = 0;
|
||||
(await Deliveries.Where(d => d.Year == year).Select(d => d.DId).ToListAsync())
|
||||
.ForEach(a => { if (a <= c + 100) c = a; });
|
||||
return c + 1;
|
||||
}
|
||||
|
||||
public async Task<int> NextDPNr(int year, int did) {
|
||||
int c = 0;
|
||||
(await DeliveryParts.Where(p => p.Year == year && p.DId == did).Select(d => d.DPNr).ToListAsync())
|
||||
.ForEach(a => { if (a <= c + 100) c = a; });
|
||||
return c + 1;
|
||||
}
|
||||
|
||||
@@ -128,5 +165,146 @@ namespace Elwig.Helpers {
|
||||
.OrderBy(q => q.MinKmw)
|
||||
.LastOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateDeliveryPartAttributes(DeliveryPart part, IEnumerable<WineAttr> attributes) {
|
||||
foreach (var a in WineAttributes) {
|
||||
var attr = part.PartAttributes.Where(pa => pa.AttrId == a.AttrId).FirstOrDefault();
|
||||
if (attributes.Contains(a)) {
|
||||
DeliveryPartAttr dpa = attr ?? this.CreateProxy<DeliveryPartAttr>();
|
||||
dpa.Year = part.Year;
|
||||
dpa.DId = part.DId;
|
||||
dpa.DPNr = part.DPNr;
|
||||
dpa.AttrId = a.AttrId;
|
||||
if (attr == null) {
|
||||
await AddAsync(dpa);
|
||||
} else {
|
||||
Update(dpa);
|
||||
}
|
||||
} else {
|
||||
if (attr != null) {
|
||||
Remove(attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateDeliveryPartModifiers(DeliveryPart part, IEnumerable<Modifier> modifiers) {
|
||||
foreach (var m in Modifiers.Where(m => m.Year == part.Year)) {
|
||||
var mod = part.PartModifiers.Where(pa => pa.ModId == m.ModId).FirstOrDefault();
|
||||
if (modifiers.Contains(m)) {
|
||||
DeliveryPartModifier dpm = mod ?? this.CreateProxy<DeliveryPartModifier>();
|
||||
dpm.Year = part.Year;
|
||||
dpm.DId = part.DId;
|
||||
dpm.DPNr = part.DPNr;
|
||||
dpm.ModId = m.ModId;
|
||||
if (mod == null) {
|
||||
await AddAsync(dpm);
|
||||
} else {
|
||||
Update(dpm);
|
||||
}
|
||||
} else {
|
||||
if (mod != null) {
|
||||
Remove(mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task FetchMemberRightsAndObligations(int year, SqliteConnection? cnx = null) {
|
||||
var ownCnx = cnx == null;
|
||||
cnx ??= await ConnectAsync();
|
||||
var bins = new Dictionary<int, Dictionary<string, (int, int)>>();
|
||||
using (var cmd = cnx.CreateCommand()) {
|
||||
cmd.CommandText = $"SELECT mgnr, bin, min_kg, max_kg FROM v_area_commitment_bin WHERE year = {year}";
|
||||
using var reader = await cmd.ExecuteReaderAsync();
|
||||
while (await reader.ReadAsync()) {
|
||||
var mgnr = reader.GetInt32(0);
|
||||
var vtrgid = reader.GetString(1);
|
||||
if (!bins.ContainsKey(mgnr)) bins[mgnr] = new();
|
||||
bins[mgnr][vtrgid] = (reader.GetInt32(3), reader.GetInt32(2));
|
||||
}
|
||||
}
|
||||
if (ownCnx) await cnx.DisposeAsync();
|
||||
_memberRightsAndObligations[year] = bins;
|
||||
}
|
||||
|
||||
private async Task FetchMemberDeliveryBins(int year, SqliteConnection? cnx = null) {
|
||||
var ownCnx = cnx == null;
|
||||
cnx ??= await ConnectAsync();
|
||||
var bins = new Dictionary<int, Dictionary<string, int>>();
|
||||
using (var cmd = cnx.CreateCommand()) {
|
||||
cmd.CommandText = $"SELECT mgnr, bin, weight FROM v_delivery_bin WHERE year = {year}";
|
||||
using var reader = await cmd.ExecuteReaderAsync();
|
||||
while (await reader.ReadAsync()) {
|
||||
var mgnr = reader.GetInt32(0);
|
||||
var bin = reader.GetString(1);
|
||||
if (!bins.ContainsKey(mgnr)) bins[mgnr] = new();
|
||||
bins[mgnr][bin] = reader.GetInt32(2);
|
||||
}
|
||||
}
|
||||
if (ownCnx) await cnx.DisposeAsync();
|
||||
_memberDeliveryBins[year] = bins;
|
||||
}
|
||||
|
||||
private async Task FetchMemberPaymentBins(int year, SqliteConnection? cnx = null) {
|
||||
var ownCnx = cnx == null;
|
||||
cnx ??= await ConnectAsync();
|
||||
var bins = new Dictionary<int, Dictionary<string, int>>();
|
||||
using (var cmd = cnx.CreateCommand()) {
|
||||
cmd.CommandText = $"SELECT mgnr, bin, weight FROM v_payment_bin WHERE year = {year}";
|
||||
using var reader = await cmd.ExecuteReaderAsync();
|
||||
while (await reader.ReadAsync()) {
|
||||
var mgnr = reader.GetInt32(0);
|
||||
var bin = reader.GetString(1);
|
||||
if (!bins.ContainsKey(mgnr)) bins[mgnr] = new();
|
||||
bins[mgnr][bin] = reader.GetInt32(2);
|
||||
}
|
||||
}
|
||||
if (ownCnx) await cnx.DisposeAsync();
|
||||
_memberPaymentBins[year] = bins;
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, (int, int)>> GetMemberRightsAndObligations(int year, int mgnr, SqliteConnection? cnx = null) {
|
||||
if (!_memberRightsAndObligations.ContainsKey(year))
|
||||
await FetchMemberRightsAndObligations(year, cnx);
|
||||
return _memberRightsAndObligations[year].GetValueOrDefault(mgnr, new());
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, int>> GetMemberDeliveryBins(int year, int mgnr, SqliteConnection? cnx = null) {
|
||||
if (!_memberDeliveryBins.ContainsKey(year))
|
||||
await FetchMemberDeliveryBins(year, cnx);
|
||||
return _memberDeliveryBins[year].GetValueOrDefault(mgnr, new());
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, int>> GetMemberPaymentBins(int year, int mgnr, SqliteConnection? cnx = null) {
|
||||
if (!_memberPaymentBins.ContainsKey(year))
|
||||
await FetchMemberPaymentBins(year, cnx);
|
||||
return _memberPaymentBins[year].GetValueOrDefault(mgnr, new());
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, (string, int, int, int, int)>> GetMemberBins(int year, int mgnr, SqliteConnection? cnx = null) {
|
||||
var ownCnx = cnx == null;
|
||||
cnx ??= await ConnectAsync();
|
||||
var rightsAndObligations = await GetMemberRightsAndObligations(year, mgnr, cnx);
|
||||
var deliveryBins = await GetMemberDeliveryBins(year, mgnr, cnx);
|
||||
var paymentBins = await GetMemberPaymentBins(year, mgnr, cnx);
|
||||
if (ownCnx) await cnx.DisposeAsync();
|
||||
|
||||
var bins = new Dictionary<string, (string, int, int, int, int)>();
|
||||
foreach (var id in rightsAndObligations.Keys.Union(deliveryBins.Keys).Union(paymentBins.Keys)) {
|
||||
var variety = await WineVarieties.FindAsync(id[..2]);
|
||||
var attrIds = id[2..];
|
||||
var attrs = await WineAttributes.Where(a => attrIds.Contains(a.AttrId)).ToListAsync();
|
||||
var name = (variety?.Name ?? "") + (attrIds == "_" ? " (kein Qual.Wein)" : attrs.Count > 0 ? $" ({string.Join(" / ", attrs.Select(a => a.Name))})" : "");
|
||||
bins[id] = (
|
||||
name,
|
||||
rightsAndObligations.GetValueOrDefault(id).Item1,
|
||||
rightsAndObligations.GetValueOrDefault(id).Item2,
|
||||
deliveryBins.GetValueOrDefault(id),
|
||||
paymentBins.GetValueOrDefault(id)
|
||||
);
|
||||
}
|
||||
return bins;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
217
Elwig/Helpers/AppDbUpdater.cs
Normal file
217
Elwig/Helpers/AppDbUpdater.cs
Normal file
@@ -0,0 +1,217 @@
|
||||
using Microsoft.Data.Sqlite;
|
||||
using System;
|
||||
|
||||
namespace Elwig.Helpers {
|
||||
public static class AppDbUpdater {
|
||||
|
||||
public static readonly int RequiredSchemaVersion = 4;
|
||||
|
||||
private static int _versionOffset = 0;
|
||||
private static readonly Action<SqliteConnection>[] _updaters = new[] {
|
||||
UpdateDbSchema_1_To_2, UpdateDbSchema_2_To_3, UpdateDbSchema_3_To_4
|
||||
};
|
||||
|
||||
private static void ExecuteNonQuery(SqliteConnection cnx, string sql) {
|
||||
using var cmd = cnx.CreateCommand();
|
||||
cmd.CommandText = sql;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
private static object? ExecuteScalar(SqliteConnection cnx, string sql) {
|
||||
using var cmd = cnx.CreateCommand();
|
||||
cmd.CommandText = sql;
|
||||
return cmd.ExecuteScalar();
|
||||
}
|
||||
|
||||
public static string CheckDb() {
|
||||
using var cnx = AppDbContext.Connect();
|
||||
|
||||
var applId = (long?)ExecuteScalar(cnx, "PRAGMA application_id") ?? 0;
|
||||
if (applId != 0x454C5747) throw new Exception("Invalid application_id of database");
|
||||
|
||||
var schemaVers = (long?)ExecuteScalar(cnx, "PRAGMA schema_version") ?? 0;
|
||||
_versionOffset = (int)(schemaVers % 100);
|
||||
if (_versionOffset != 0) {
|
||||
// schema was modified manually/externally
|
||||
// TODO issue warning
|
||||
}
|
||||
UpdateDbSchema(cnx, (int)(schemaVers / 100), RequiredSchemaVersion);
|
||||
|
||||
var userVers = (long?)ExecuteScalar(cnx, "PRAGMA user_version") ?? 0;
|
||||
var major = userVers >> 24;
|
||||
var minor = (userVers >> 16) & 0xFF;
|
||||
var patch = userVers & 0xFFFF;
|
||||
|
||||
if (App.VersionMajor > major ||
|
||||
(App.VersionMajor == major && App.VersionMinor > minor) ||
|
||||
(App.VersionMajor == major && App.VersionMinor == minor && App.VersionPatch > patch))
|
||||
{
|
||||
long vers = (App.VersionMajor << 24) | (App.VersionMinor << 16) | App.VersionPatch;
|
||||
ExecuteNonQuery(cnx, $"PRAGMA user_version = {vers}");
|
||||
}
|
||||
|
||||
return $"{major}.{minor}.{patch}";
|
||||
}
|
||||
|
||||
private static void UpdateDbSchema(SqliteConnection cnx, int fromVersion, int toVersion) {
|
||||
if (fromVersion == toVersion) {
|
||||
return;
|
||||
} else if (fromVersion > toVersion) {
|
||||
throw new Exception("schema_version of database is too new");
|
||||
} else if (toVersion - 1 > _updaters.Length) {
|
||||
throw new Exception("Unable to update database schema: Updater not implemented");
|
||||
} else if (fromVersion <= 0) {
|
||||
throw new Exception("schema_version of database is invalid");
|
||||
}
|
||||
|
||||
ExecuteNonQuery(cnx, "PRAGMA locking_mode = EXCLUSIVE");
|
||||
ExecuteNonQuery(cnx, "BEGIN EXCLUSIVE");
|
||||
for (int i = fromVersion; i < toVersion; i++) {
|
||||
_updaters[i - 1](cnx);
|
||||
}
|
||||
ExecuteNonQuery(cnx, "COMMIT");
|
||||
ExecuteNonQuery(cnx, "VACUUM");
|
||||
ExecuteNonQuery(cnx, $"PRAGMA schema_version = {toVersion * 100 + _versionOffset}");
|
||||
}
|
||||
|
||||
private static void UpdateDbSchema_1_To_2(SqliteConnection cnx) {
|
||||
ExecuteNonQuery(cnx, "DROP VIEW v_area_commitment");
|
||||
ExecuteNonQuery(cnx, "ALTER TABLE delivery_part DROP COLUMN weighing_reason");
|
||||
ExecuteNonQuery(cnx, "ALTER TABLE delivery_part ADD COLUMN weighing_reason TEXT CHECK(NOT (manual_weighing = FALSE AND weighing_reason IS NOT NULL))");
|
||||
}
|
||||
|
||||
private static void UpdateDbSchema_2_To_3(SqliteConnection cnx) {
|
||||
ExecuteNonQuery(cnx, """
|
||||
CREATE TABLE delivery_part_bin (
|
||||
year INTEGER NOT NULL,
|
||||
did INTEGER NOT NULL,
|
||||
dpnr INTEGER NOT NULL,
|
||||
binnr INTEGER NOT NULL,
|
||||
|
||||
discr TEXT NOT NULL,
|
||||
value INTEGER NOT NULL,
|
||||
|
||||
CONSTRAINT pk_delivery_part_bin PRIMARY KEY (year, did, dpnr, binnr),
|
||||
CONSTRAINT fk_delivery_part_bin_delivery_part FOREIGN KEY (year, did, dpnr) REFERENCES delivery_part (year, did, dpnr)
|
||||
ON UPDATE CASCADE
|
||||
ON DELETE CASCADE
|
||||
) STRICT;
|
||||
""");
|
||||
ExecuteNonQuery(cnx, """
|
||||
INSERT INTO delivery_part_bin (year, did, dpnr, binnr, discr, value)
|
||||
SELECT year, did, dpnr, 0, '_', bucket_2 + bucket_3
|
||||
FROM payment_delivery_part
|
||||
WHERE COALESCE(bucket_1, bucket_2, bucket_3, bucket_4, bucket_5, bucket_6, bucket_7, bucket_8, bucket_9) IS NOT NULL
|
||||
ON CONFLICT DO NOTHING;
|
||||
""");
|
||||
ExecuteNonQuery(cnx, """
|
||||
INSERT INTO delivery_part_bin (year, did, dpnr, binnr, discr, value)
|
||||
SELECT d.year, d.did, d.dpnr, 1, COALESCE(attributes, ''), bucket_1
|
||||
FROM payment_delivery_part p
|
||||
JOIN v_delivery d ON (d.year, d.did, d.dpnr) = (p.year, p.did, p.dpnr)
|
||||
WHERE COALESCE(bucket_1, bucket_2, bucket_3, bucket_4, bucket_5, bucket_6, bucket_7, bucket_8, bucket_9) IS NOT NULL
|
||||
ON CONFLICT DO NOTHING;
|
||||
""");
|
||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_1");
|
||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_2");
|
||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_3");
|
||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_4");
|
||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_5");
|
||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_6");
|
||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_7");
|
||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_8");
|
||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_9");
|
||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_1_name");
|
||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_2_name");
|
||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_3_name");
|
||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_4_name");
|
||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_5_name");
|
||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_6_name");
|
||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_7_name");
|
||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_8_name");
|
||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_9_name");
|
||||
|
||||
ExecuteNonQuery(cnx, "ALTER TABLE delivery_part ADD COLUMN gebunden INTEGER CHECK (gebunden IN (TRUE, FALSE)) DEFAULT NULL");
|
||||
|
||||
ExecuteNonQuery(cnx, "DROP VIEW v_delivery");
|
||||
ExecuteNonQuery(cnx, """
|
||||
CREATE VIEW v_delivery AS
|
||||
SELECT s.*, GROUP_CONCAT(o.modid) AS modifiers
|
||||
FROM (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, 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.gerebelt, 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,
|
||||
GROUP_CONCAT(a.attrid) AS attributes,
|
||||
COALESCE(SUM(a.fill_lower_bins), 0) AS attribute_prio,
|
||||
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 delivery_part_attribute pa ON (pa.year, pa.did, pa.dpnr) = (p.year, p.did, p.dpnr)
|
||||
LEFT JOIN wine_attribute a ON a.attrid = pa.attrid
|
||||
GROUP BY p.year, p.did, p.dpnr
|
||||
ORDER BY p.year, p.did, p.dpnr, a.attrid) s
|
||||
LEFT JOIN delivery_part_modifier o ON (o.year, o.did, o.dpnr) = (s.year, s.did, s.dpnr)
|
||||
GROUP BY s.year, s.lsnr, s.dpnr
|
||||
ORDER BY s.year, s.lsnr, s.dpnr, o.modid;
|
||||
""");
|
||||
|
||||
ExecuteNonQuery(cnx, "DROP VIEW v_bucket");
|
||||
ExecuteNonQuery(cnx, """
|
||||
CREATE VIEW v_delivery_bin AS
|
||||
SELECT year, mgnr,
|
||||
sortid || IIF(min_quw, REPLACE(COALESCE(attributes, ''), ',', ''), '_') AS bin,
|
||||
SUM(weight) AS weight
|
||||
FROM v_delivery
|
||||
GROUP BY year, mgnr, bin
|
||||
ORDER BY year, mgnr, LENGTH(bin) DESC, bin;
|
||||
""");
|
||||
|
||||
ExecuteNonQuery(cnx, """
|
||||
CREATE VIEW v_payment_bin AS
|
||||
SELECT d.year, d.mgnr,
|
||||
sortid || discr AS bin,
|
||||
SUM(value) AS weight
|
||||
FROM v_delivery d
|
||||
JOIN delivery_part_bin b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr)
|
||||
GROUP BY d.year, d.mgnr, bin
|
||||
HAVING SUM(value) > 0
|
||||
ORDER BY d.year, d.mgnr, bin;
|
||||
""");
|
||||
|
||||
ExecuteNonQuery(cnx, "ALTER TABLE wine_attribute ADD COLUMN fill_lower_bins INTEGER NOT NULL CHECK (fill_lower_bins IN (0, 1, 2)) DEFAULT 0");
|
||||
}
|
||||
|
||||
private static void UpdateDbSchema_3_To_4(SqliteConnection cnx) {
|
||||
ExecuteNonQuery(cnx, "DROP VIEW v_payment_bin");
|
||||
ExecuteNonQuery(cnx, """
|
||||
CREATE VIEW v_payment_bin AS
|
||||
SELECT d.year, d.mgnr,
|
||||
sortid || discr AS bin,
|
||||
SUM(value) AS weight
|
||||
FROM v_delivery d
|
||||
JOIN delivery_part_bin b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr)
|
||||
GROUP BY d.year, d.mgnr, bin
|
||||
HAVING SUM(value) > 0
|
||||
ORDER BY d.year, d.mgnr, LENGTH(bin) DESC, bin;
|
||||
""");
|
||||
|
||||
ExecuteNonQuery(cnx, """
|
||||
CREATE VIEW v_area_commitment_bin AS
|
||||
SELECT s.year, c.mgnr,
|
||||
c.vtrgid AS bin,
|
||||
CAST(ROUND(SUM(COALESCE(area * min_kg_per_ha, 0)) / 10000.0, 0) AS INTEGER) AS min_kg,
|
||||
CAST(ROUND(SUM(COALESCE(area * max_kg_per_ha, 0)) / 10000.0, 0) AS INTEGER) AS max_kg
|
||||
FROM area_commitment c, season s
|
||||
JOIN area_commitment_type t ON t.vtrgid = c.vtrgid
|
||||
WHERE (year_from IS NULL OR year_from <= s.year) AND
|
||||
(year_to IS NULL OR year_to >= s.year)
|
||||
GROUP BY s.year, c.mgnr, c.vtrgid
|
||||
ORDER BY s.year, c.mgnr, LENGTH(c.vtrgid) DESC, c.vtrgid;
|
||||
""");
|
||||
}
|
||||
}
|
||||
}
|
205
Elwig/Helpers/Billing/Billing.cs
Normal file
205
Elwig/Helpers/Billing/Billing.cs
Normal file
@@ -0,0 +1,205 @@
|
||||
using Microsoft.Data.Sqlite;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elwig.Helpers.Billing {
|
||||
public class Billing {
|
||||
|
||||
protected readonly int Year;
|
||||
protected readonly AppDbContext Context;
|
||||
protected readonly Dictionary<string, string> Attributes;
|
||||
protected readonly Dictionary<string, (decimal?, decimal?)> Modifiers;
|
||||
protected readonly Dictionary<string, (string, string?, string?, string?, int?, int?, decimal?)> AreaComTypes;
|
||||
|
||||
public Billing(int year) {
|
||||
Year = year;
|
||||
Context = new AppDbContext();
|
||||
Attributes = Context.WineAttributes.ToDictionary(a => a.AttrId, a => a.Name);
|
||||
Modifiers = Context.Modifiers.Where(m => m.Year == Year).ToDictionary(m => m.ModId, m => (m.Abs, m.Rel));
|
||||
AreaComTypes = Context.AreaCommitmentTypes.ToDictionary(v => v.VtrgId, v => (v.SortId, v.AttrId1, v.AttrId2, v.Discriminator, v.MinKgPerHa, v.MaxKgPerHa, v.PenaltyAmount));
|
||||
}
|
||||
|
||||
public async Task FinishSeason() {
|
||||
using var cnx = await AppDbContext.ConnectAsync();
|
||||
using (var cmd = cnx.CreateCommand()) {
|
||||
cmd.CommandText = $"""
|
||||
UPDATE season
|
||||
SET (start_date, end_date) = (SELECT MIN(date), MAX(date) FROM delivery WHERE year = {Year})
|
||||
WHERE year = {Year}
|
||||
""";
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
using (var cmd = cnx.CreateCommand()) {
|
||||
cmd.CommandText = $"DELETE FROM delivery_part_bin WHERE year = {Year}";
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CalculateBins(bool allowAttrsIntoLowerBins, bool avoidUnderDeliveries, bool honorGebunden) {
|
||||
var attrVals = Context.WineAttributes.ToDictionary(a => a.AttrId, a => a.FillLowerBins);
|
||||
var attrForced = attrVals.Where(a => a.Value == 0).Select(a => a.Key).ToArray();
|
||||
using var cnx = await AppDbContext.ConnectAsync();
|
||||
await Context.GetMemberRightsAndObligations(Year, 0, cnx);
|
||||
var inserts = new List<(int, int, int, string, int)>();
|
||||
|
||||
var deliveries = new List<(int, int, int, string, int, double, string, string[], string[], bool?)>();
|
||||
using (var cmd = cnx.CreateCommand()) {
|
||||
cmd.CommandText = $"""
|
||||
SELECT mgnr, did, dpnr, sortid, weight, kmw, qualid, attributes, modifiers, gebunden
|
||||
FROM v_delivery
|
||||
WHERE year = {Year}
|
||||
ORDER BY mgnr, sortid, abgewertet ASC, {(honorGebunden ? "gebunden IS NOT NULL DESC, gebunden DESC," : "")}
|
||||
COALESCE(LENGTH(attributes), 0) ASC, attribute_prio DESC, COALESCE(attributes, '~'),
|
||||
kmw DESC, lsnr, dpnr
|
||||
""";
|
||||
using var reader = await cmd.ExecuteReaderAsync();
|
||||
while (await reader.ReadAsync()) {
|
||||
deliveries.Add((
|
||||
reader.GetInt32(0), reader.GetInt32(1), reader.GetInt32(2), reader.GetString(3), reader.GetInt32(4),
|
||||
reader.GetDouble(5), reader.GetString(6),
|
||||
reader.IsDBNull(7) ? Array.Empty<string>() : reader.GetString(7).Split(",").Order().ToArray(),
|
||||
reader.IsDBNull(8) ? Array.Empty<string>() : reader.GetString(8).Split(",").Order().ToArray(),
|
||||
reader.IsDBNull(9) ? null : reader.GetBoolean(9)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
int lastMgNr = 0;
|
||||
Dictionary<string, (int, int)>? rightsAndObligations = null;
|
||||
Dictionary<string, int> used = new();
|
||||
foreach (var (mgnr, did, dpnr, sortid, weight, kmw, qualid, attributes, modifiers, gebunden) in deliveries) {
|
||||
if (lastMgNr != mgnr) {
|
||||
rightsAndObligations = await Context.GetMemberRightsAndObligations(Year, mgnr);
|
||||
used = new();
|
||||
}
|
||||
if ((honorGebunden && gebunden == false) ||
|
||||
rightsAndObligations == null || rightsAndObligations.Count == 0 ||
|
||||
qualid == "WEI" || qualid == "RSW" || qualid == "LDW")
|
||||
{
|
||||
// Explizit als ungebunden markiert,
|
||||
// Mitglied hat keine Flächenbindungen, oder
|
||||
// Nicht mindestens Qualitätswein (QUW)
|
||||
// -> ungebunden
|
||||
inserts.Add((did, dpnr, 0, "_", weight));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (attributes.Length > 2)
|
||||
throw new NotSupportedException();
|
||||
|
||||
int w = weight;
|
||||
foreach (var p in Utils.Permutate(attributes, attributes.Intersect(attrForced))) {
|
||||
var c = p.Count();
|
||||
var key = sortid + string.Join("", p);
|
||||
if (rightsAndObligations.ContainsKey(key)) {
|
||||
int i = 4;
|
||||
if (c == 1) {
|
||||
i = (p.ElementAt(0) == attributes[0]) ? 2 : 3;
|
||||
} else if (c == 0) {
|
||||
i = 1;
|
||||
}
|
||||
var u = used.GetValueOrDefault(key, 0);
|
||||
var vr = Math.Max(0, Math.Min(rightsAndObligations[key].Item1 - u, w));
|
||||
var vo = Math.Max(0, Math.Min(rightsAndObligations[key].Item2 - u, w));
|
||||
var v = (attributes.Length == 0 || attributes.Select(a => attrVals[a]).Min() == 2) ? vr : vo;
|
||||
used[key] = u + v;
|
||||
inserts.Add((did, dpnr, i, key[2..], v));
|
||||
w -= v;
|
||||
}
|
||||
if (w == 0 || !allowAttrsIntoLowerBins) break;
|
||||
}
|
||||
inserts.Add((did, dpnr, 0, "_", w));
|
||||
lastMgNr = mgnr;
|
||||
}
|
||||
|
||||
using (var cmd = cnx.CreateCommand()) {
|
||||
cmd.CommandText = $"""
|
||||
INSERT INTO delivery_part_bin (year, did, dpnr, binnr, discr, value)
|
||||
VALUES {string.Join(",\n ", inserts.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '{i.Item4}', {i.Item5})"))}
|
||||
ON CONFLICT DO UPDATE
|
||||
SET discr = excluded.discr, value = value + excluded.value
|
||||
""";
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
if (!avoidUnderDeliveries)
|
||||
return;
|
||||
|
||||
var underDeliveries = new Dictionary<(int, string), int>();
|
||||
using (var cmd = cnx.CreateCommand()) {
|
||||
cmd.CommandText = $"""
|
||||
SELECT c.mgnr, c.bin, COALESCE(p.weight, 0) - c.min_kg AS diff
|
||||
FROM v_area_commitment_bin c
|
||||
LEFT JOIN v_payment_bin p ON (p.year, p.mgnr, p.bin) = (c.year, c.mgnr, c.bin)
|
||||
WHERE c.year = {Year} AND LENGTH(c.bin) = 2 AND diff < 0
|
||||
""";
|
||||
using var reader = await cmd.ExecuteReaderAsync();
|
||||
while (await reader.ReadAsync()) {
|
||||
underDeliveries[(reader.GetInt32(0), reader.GetString(1))] = reader.GetInt32(2);
|
||||
}
|
||||
}
|
||||
|
||||
var fittingBins = new Dictionary<(int, string), int>();
|
||||
using (var cmd = cnx.CreateCommand()) {
|
||||
cmd.CommandText = $"""
|
||||
SELECT c.mgnr, c.bin, COALESCE(p.weight, 0) - c.min_kg AS diff
|
||||
FROM v_area_commitment_bin c
|
||||
LEFT JOIN v_payment_bin p ON (p.year, p.mgnr, p.bin) = (c.year, c.mgnr, c.bin)
|
||||
WHERE c.year = {Year} AND LENGTH(c.bin) = 3 AND diff > 0
|
||||
""";
|
||||
using var reader = await cmd.ExecuteReaderAsync();
|
||||
while (await reader.ReadAsync()) {
|
||||
fittingBins[(reader.GetInt32(0), reader.GetString(1))] = reader.GetInt32(2);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var item in fittingBins) {
|
||||
var mgnr = item.Key.Item1;
|
||||
var id = item.Key.Item2[..2];
|
||||
var attr = item.Key.Item2[2..];
|
||||
var available = item.Value;
|
||||
var needed = -underDeliveries.GetValueOrDefault((mgnr, id), 0);
|
||||
if (needed <= 0) continue;
|
||||
|
||||
var fittingDeliveries = new List<(int, int, int, int)>();
|
||||
using (var cmd = cnx.CreateCommand()) {
|
||||
cmd.CommandText = $"""
|
||||
SELECT d.did, d.dpnr, d.weight, b.value
|
||||
FROM v_delivery d
|
||||
JOIN delivery_part_bin b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr) AND b.discr = '{attr}'
|
||||
WHERE d.year = {Year} AND mgnr = {mgnr} AND sortid = '{id}' AND attributes = '{attr}'
|
||||
ORDER BY kmw ASC, lsnr DESC, d.dpnr DESC
|
||||
""";
|
||||
using var reader = await cmd.ExecuteReaderAsync();
|
||||
while (await reader.ReadAsync()) {
|
||||
fittingDeliveries.Add((
|
||||
reader.GetInt32(0), reader.GetInt32(1), reader.GetInt32(2), reader.GetInt32(3)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
var changes = new List<(int, int, int, int)>();
|
||||
foreach (var (did, dpnr, _, w) in fittingDeliveries) {
|
||||
int v = Math.Min(needed, w);
|
||||
needed -= v;
|
||||
changes.Add((did, dpnr, 1, v));
|
||||
changes.Add((did, dpnr, 2, w - v));
|
||||
if (needed == 0) break;
|
||||
}
|
||||
|
||||
using (var cmd = cnx.CreateCommand()) {
|
||||
cmd.CommandText = $"""
|
||||
INSERT INTO delivery_part_bin (year, did, dpnr, binnr, discr, value)
|
||||
VALUES {string.Join(",\n ", changes.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '', {i.Item4})"))}
|
||||
ON CONFLICT DO UPDATE
|
||||
SET value = excluded.value
|
||||
""";
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
41
Elwig/Helpers/Billing/BillingVariant.cs
Normal file
41
Elwig/Helpers/Billing/BillingVariant.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elwig.Helpers.Billing {
|
||||
public class BillingVariant : Billing {
|
||||
|
||||
private readonly int AvNr;
|
||||
|
||||
public BillingVariant(int year, int avnr) : base(year) {
|
||||
AvNr = avnr;
|
||||
}
|
||||
|
||||
protected async Task DeleteInDb() {
|
||||
using var cnx = await AppDbContext.ConnectAsync();
|
||||
using (var cmd = cnx.CreateCommand()) {
|
||||
cmd.CommandText = $"DELETE FROM payment_delivery_part WHERE (year, avnr) = ({Year}, {AvNr})";
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
using (var cmd = cnx.CreateCommand()) {
|
||||
cmd.CommandText = $"DELETE FROM payment_member WHERE (year, avnr) = ({Year}, {AvNr})";
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CalculatePrices() {
|
||||
await DeleteInDb();
|
||||
var tasks = new List<Task>();
|
||||
foreach (var mgnr in Context.Members.Select(m => m.MgNr)) {
|
||||
tasks.Add(Task.Run(() => CalculateMemberPrices(mgnr)));
|
||||
}
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
protected async Task CalculateMemberPrices(int mgnr) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
119
Elwig/Helpers/Billing/Graph.cs
Normal file
119
Elwig/Helpers/Billing/Graph.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using ScottPlot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace Elwig.Helpers.Billing {
|
||||
public class Graph : ICloneable {
|
||||
|
||||
public string Type { get; set; }
|
||||
public int Num { get; set; }
|
||||
private int MinX { get; set; }
|
||||
private int MaxX { get; set; }
|
||||
public string Contracts { get; set; }
|
||||
public double[] DataX { get; set; }
|
||||
public double[] DataY { get; set; }
|
||||
|
||||
public Graph(int num, int minX, int maxX) {
|
||||
Type = "oe";
|
||||
Num = num;
|
||||
Contracts = "";
|
||||
MinX = minX;
|
||||
MaxX = maxX;
|
||||
|
||||
DataX = DataGen.Range(MinX, MaxX + 1);
|
||||
DataY = DataGen.Zeros(MaxX - MinX + 1);
|
||||
}
|
||||
|
||||
public Graph(string type, int num, JsonObject graphData, string contracts, int minX, int maxX) {
|
||||
Type = type;
|
||||
Num = num;
|
||||
Contracts = contracts;
|
||||
MinX = minX;
|
||||
MaxX = maxX;
|
||||
|
||||
DataX = DataGen.Range(MinX, MaxX + 1);
|
||||
DataY = DataGen.Zeros(MaxX - MinX + 1);
|
||||
ParseGraphData(graphData);
|
||||
}
|
||||
|
||||
public Graph(string type, int num, int minX, int maxX, string contracts, double[] dataX, double[] dataY) {
|
||||
Type = type;
|
||||
Num = num;
|
||||
MinX = minX;
|
||||
MaxX = maxX;
|
||||
Contracts = contracts;
|
||||
DataX = dataX;
|
||||
DataY = dataY;
|
||||
}
|
||||
|
||||
private void ParseGraphData(JsonObject graphData) {
|
||||
var GraphPoints = graphData.ToDictionary(p => int.Parse(p.Key[..^2]), p => (double)p.Value?.AsValue());
|
||||
|
||||
if (GraphPoints.Keys.Count < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var minKey = GraphPoints.Keys.Order().First();
|
||||
var maxKey = GraphPoints.Keys.OrderDescending().First();
|
||||
|
||||
if (!GraphPoints.ContainsKey(MinX)) {
|
||||
GraphPoints.Add(MinX, GraphPoints.GetValueOrDefault(minKey));
|
||||
}
|
||||
if (!GraphPoints.ContainsKey(MaxX)) {
|
||||
GraphPoints.Add(MaxX, GraphPoints.GetValueOrDefault(maxKey));
|
||||
}
|
||||
|
||||
var keys = GraphPoints.Keys.Order().ToArray();
|
||||
|
||||
for (int i = 0; i < keys.Length; i++) {
|
||||
double point1Value = GraphPoints[keys[i]];
|
||||
if (i + 1 < keys.Length) {
|
||||
double point2Value = GraphPoints[keys[i + 1]];
|
||||
if (point1Value == point2Value) {
|
||||
for (int j = keys[i] - MinX; j < keys[i + 1] - MinX; j++) {
|
||||
DataY[j] = point1Value;
|
||||
}
|
||||
} else {
|
||||
int steps = Math.Abs(keys[i + 1] - keys[i]);
|
||||
double step = (point2Value - point1Value) / steps;
|
||||
|
||||
DataY[keys[i] - MinX] = point1Value;
|
||||
DataY[keys[i + 1] - MinX] = point2Value;
|
||||
|
||||
for (int j = keys[i] - MinX; j < keys[i + 1] - MinX - 1; j++) {
|
||||
DataY[j + 1] = Math.Round(DataY[j] + step, 4); // TODO richtig runden
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int j = keys[i] - MinX; j < DataX.Length; j++) {
|
||||
DataY[j] = point1Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public JsonObject ToJson() {
|
||||
JsonObject graph = new();
|
||||
|
||||
if (DataY[0] != DataY[1]) {
|
||||
graph.Add(new KeyValuePair<string, JsonNode?>(DataX[0] + Type.ToLower(), Math.Round(DataY[0], 4)));
|
||||
}
|
||||
for (int i = 1; i < DataX.Length - 1; i++) {
|
||||
if (Math.Round(DataY[i] - DataY[i - 1], 4) != Math.Round(DataY[i + 1] - DataY[i], 4)) {
|
||||
graph.Add(new KeyValuePair<string, JsonNode?>(DataX[i] + Type.ToLower(), Math.Round(DataY[i], 4)));
|
||||
}
|
||||
}
|
||||
if (DataY[^1] != DataY[^2]) {
|
||||
graph.Add(new KeyValuePair<string, JsonNode?>(DataX[^1] + Type.ToLower(), Math.Round(DataY[^1], 4)));
|
||||
}
|
||||
return graph;
|
||||
}
|
||||
|
||||
public object Clone() {
|
||||
return new Graph(Type, Num, MinX, MaxX, Contracts, (double[])DataX.Clone(), (double[])DataY.Clone());
|
||||
}
|
||||
}
|
||||
}
|
32
Elwig/Helpers/Billing/Transaction.cs
Normal file
32
Elwig/Helpers/Billing/Transaction.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using Elwig.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Elwig.Helpers.Billing {
|
||||
public class Transaction {
|
||||
|
||||
public readonly Member Member;
|
||||
public readonly long AmountCent;
|
||||
public readonly string Currency;
|
||||
public readonly int Nr;
|
||||
|
||||
public Transaction(Member m, decimal amount, string currency, int nr) {
|
||||
Member = m;
|
||||
AmountCent = (long)Math.Round(amount * 100);
|
||||
Currency = currency;
|
||||
Nr = nr;
|
||||
}
|
||||
|
||||
public static IEnumerable<Transaction> FromPaymentVariant(PaymentVar variant) {
|
||||
var last = variant.Season.PaymentVariants.Where(v => v.TransferDate != null).OrderBy(v => v.TransferDate).LastOrDefault();
|
||||
var dict = last?.MemberPayments.ToDictionary(m => m.MgNr, m => m.Amount) ?? new();
|
||||
return variant.Credits
|
||||
.OrderBy(c => c.MgNr)
|
||||
.Select(c => new Transaction(c.Member, c.Amount, variant.Season.CurrencyCode, c.TgNr))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static string FormatAmountCent(long cents) => $"{cents / 100}.{cents % 100:00}";
|
||||
}
|
||||
}
|
@@ -1,47 +1,168 @@
|
||||
using Elwig.Models;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elwig.Helpers {
|
||||
public class ClientParameters {
|
||||
public class ClientParameters {
|
||||
|
||||
public string NameToken;
|
||||
public string NameShort;
|
||||
public string NameShortened;
|
||||
public string Name;
|
||||
public string NameSuffix;
|
||||
public string NameFull => $"{Name} {NameSuffix}";
|
||||
public enum Type { Matzen, Winzerkeller };
|
||||
|
||||
public PostalDest PostalDest {
|
||||
set {
|
||||
Plz = value.AtPlz.Plz;
|
||||
Ort = value.AtPlz.Ort.Name;
|
||||
}
|
||||
}
|
||||
public int Plz { get; private set; }
|
||||
public string Ort { get; private set; }
|
||||
public string Address;
|
||||
public string Sender1 => $"{NameShort} | {Address} | {Plz} {Ort}";
|
||||
public string Sender2;
|
||||
public bool IsMatzen => Client == Type.Matzen;
|
||||
public bool IsWinzerkeller => Client == Type.Winzerkeller;
|
||||
public bool IsWolkersdorf => Client == Type.Winzerkeller && App.ZwstId == "W";
|
||||
public bool IsHaugsdorf => Client == Type.Winzerkeller && App.ZwstId == "H";
|
||||
public bool IsSitzendorf => Client == Type.Winzerkeller && App.ZwstId == "S";
|
||||
|
||||
public string? Iban;
|
||||
public string? Bic;
|
||||
public string? UstId;
|
||||
public string? LfbisNr;
|
||||
public string NameToken;
|
||||
public string NameShort;
|
||||
public string Name;
|
||||
public string? NameSuffix;
|
||||
public string NameType;
|
||||
public string NameTypeFull => NameType.Replace(".", "").Replace(" ", "").ToLower() switch {
|
||||
"reggenmbh" => "registrierte Genossenschaft mit beschränkter Haftung",
|
||||
_ => NameType,
|
||||
};
|
||||
public string NameFull => NameSuffix == null ? $"{Name} {NameType}" : $"{Name}, {NameSuffix}, {NameType}";
|
||||
public Type? Client;
|
||||
|
||||
public string? PhoneNr;
|
||||
public string? FaxNr;
|
||||
public string? EmailAddress;
|
||||
public string? Website;
|
||||
public PostalDest PostalDest {
|
||||
set {
|
||||
Plz = value.AtPlz.Plz;
|
||||
Ort = value.AtPlz.Ort.Name;
|
||||
}
|
||||
}
|
||||
public int Plz;
|
||||
public string Ort;
|
||||
public string Address;
|
||||
public string Sender1 => $"{NameShort} | {Address} | {Plz} {Ort}";
|
||||
public string Sender2;
|
||||
|
||||
public ClientParameters() {
|
||||
NameToken = "WGM";
|
||||
NameShort = "WG Matzen";
|
||||
NameShortened = "Winzergenossenschaft f. Matzen u. Umgebung";
|
||||
Name = "Winzergenossenschaft für Matzen und Umgebung";
|
||||
NameSuffix = "reg. Gen.m.b.H.";
|
||||
Plz = 2243;
|
||||
Ort = "Matzen";
|
||||
Address = "Schloßstraße 6";
|
||||
Sender2 = "E Österreichische Post AG Eco Brief";
|
||||
}
|
||||
}
|
||||
public string? Iban;
|
||||
public string? Bic;
|
||||
public string? UstIdNr;
|
||||
public string? LfbisNr;
|
||||
|
||||
public string? PhoneNr;
|
||||
public string? FaxNr;
|
||||
public string? EmailAddress;
|
||||
public string? Website;
|
||||
|
||||
public int DeliveryObligation;
|
||||
public int DeliveryRight;
|
||||
public decimal VatNormal;
|
||||
public decimal VatReduced;
|
||||
public decimal VatFlatRate;
|
||||
|
||||
public int ModeDeliveryNoteStats;
|
||||
|
||||
public string? TextDeliveryNote;
|
||||
public string? TextDeliveryConfirmation;
|
||||
|
||||
public ClientParameters(AppDbContext ctx) : this(ctx.ClientParameters.ToDictionary(e => e.Param, e => e.Value)) { }
|
||||
|
||||
public ClientParameters(Dictionary<string, string?> parameters) {
|
||||
try {
|
||||
NameToken = parameters["CLIENT_NAME_TOKEN"] ?? throw new KeyNotFoundException();
|
||||
NameShort = parameters["CLIENT_NAME_SHORT"] ?? throw new KeyNotFoundException();
|
||||
Name = parameters["CLIENT_NAME"] ?? throw new KeyNotFoundException();
|
||||
NameSuffix = parameters.GetValueOrDefault("CLIENT_NAME_SUFFIX");
|
||||
NameType = parameters["CLIENT_NAME_TYPE"] ?? throw new KeyNotFoundException();
|
||||
switch (Name) {
|
||||
case "Winzergenossenschaft für Matzen und Umgebung": Client = Type.Matzen; break;
|
||||
case "Winzerkeller im Weinviertel": Client = Type.Winzerkeller; break;
|
||||
};
|
||||
|
||||
Plz = int.Parse(parameters["CLIENT_PLZ"] ?? "");
|
||||
Ort = parameters["CLIENT_ORT"] ?? throw new KeyNotFoundException();
|
||||
Address = parameters["CLIENT_ADDRESS"] ?? throw new KeyNotFoundException();
|
||||
PhoneNr = parameters.GetValueOrDefault("CLIENT_PHONE");
|
||||
FaxNr = parameters.GetValueOrDefault("CLIENT_FAX");
|
||||
EmailAddress = parameters.GetValueOrDefault("CLIENT_EMAIL");
|
||||
Website = parameters.GetValueOrDefault("CLIENT_WEBSITE");
|
||||
LfbisNr = parameters.GetValueOrDefault("CLIENT_LFBISNR");
|
||||
UstIdNr = parameters.GetValueOrDefault("CLIENT_USTIDNR");
|
||||
Bic = parameters.GetValueOrDefault("CLIENT_BIC");
|
||||
Iban = parameters.GetValueOrDefault("CLIENT_IBAN");
|
||||
|
||||
DeliveryObligation = int.Parse(parameters["DELIVERY_OBLIGATION"] ?? "");
|
||||
DeliveryRight = int.Parse(parameters["DELIVERY_RIGHT"] ?? "");
|
||||
VatNormal = decimal.Parse((parameters["VAT_NORMAL"] ?? "").Replace(".", ","));
|
||||
VatReduced = decimal.Parse((parameters["VAT_REDUCED"] ?? "").Replace(".", ","));
|
||||
VatFlatRate = decimal.Parse((parameters["VAT_FLATRATE"] ?? "").Replace(".", ","));
|
||||
|
||||
switch (parameters.GetValueOrDefault("MODE_DELIVERYNOTE_STATS", "SHORT")?.ToUpper()) {
|
||||
case "NONE": ModeDeliveryNoteStats = 0; break;
|
||||
case "GA_ONLY": ModeDeliveryNoteStats = 1; break;
|
||||
case "SHORT": ModeDeliveryNoteStats = 2; break;
|
||||
case "FULL": ModeDeliveryNoteStats = 3; break;
|
||||
}
|
||||
|
||||
Sender2 = parameters.GetValueOrDefault("DOCUMENT_SENDER") ?? "";
|
||||
TextDeliveryNote = parameters.GetValueOrDefault("TEXT_DELIVERYNOTE");
|
||||
if (TextDeliveryNote == "") TextDeliveryNote = null;
|
||||
TextDeliveryConfirmation = parameters.GetValueOrDefault("TEXT_DELIVERYCONFIRMATION");
|
||||
if (TextDeliveryConfirmation == "") TextDeliveryConfirmation = null;
|
||||
} catch {
|
||||
throw new KeyNotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<(string, string?)> GetParamValues() {
|
||||
string deliveryNoteStats = "SHORT";
|
||||
switch (ModeDeliveryNoteStats) {
|
||||
case 0: deliveryNoteStats = "NONE"; break;
|
||||
case 1: deliveryNoteStats = "GA_ONLY"; break;
|
||||
case 2: deliveryNoteStats = "SHORT"; break;
|
||||
case 3: deliveryNoteStats = "FULL"; break;
|
||||
}
|
||||
return new (string, string?)[] {
|
||||
("CLIENT_NAME_TOKEN", NameToken),
|
||||
("CLIENT_NAME_SHORT", NameShort),
|
||||
("CLIENT_NAME", Name),
|
||||
("CLIENT_NAME_SUFFIX", NameSuffix),
|
||||
("CLIENT_NAME_TYPE", NameType),
|
||||
("CLIENT_PLZ", Plz.ToString()),
|
||||
("CLIENT_ORT", Ort),
|
||||
("CLIENT_ADDRESS", Address),
|
||||
("CLIENT_PHONE", PhoneNr),
|
||||
("CLIENT_FAX", FaxNr),
|
||||
("CLIENT_EMAIL", EmailAddress),
|
||||
("CLIENT_WEBSITE", Website),
|
||||
("CLIENT_LFBISNR", LfbisNr),
|
||||
("CLIENT_USTIDNR", UstIdNr),
|
||||
("CLIENT_BIC", Bic),
|
||||
("CLIENT_IBAN", Iban),
|
||||
("DELIVERY_OBLIGATION", DeliveryObligation.ToString()),
|
||||
("DELIVERY_RIGHT", DeliveryRight.ToString()),
|
||||
("VAT_NORMAL", VatNormal.ToString().Replace(",", ".")),
|
||||
("VAT_REDUCED", VatReduced.ToString().Replace(",", ".")),
|
||||
("VAT_FLATRATE", VatFlatRate.ToString().Replace(",", ".")),
|
||||
("MODE_DELIVERYNOTE_STATS", deliveryNoteStats),
|
||||
("DOCUMENT_SENDER", Sender2),
|
||||
("TEXT_DELIVERYNOTE", TextDeliveryNote),
|
||||
("TEXT_DELIVERYCONFIRMATION", TextDeliveryConfirmation),
|
||||
};
|
||||
}
|
||||
|
||||
public async Task UpdateValues() {
|
||||
using var cnx = await AppDbContext.ConnectAsync();
|
||||
using var cmd = cnx.CreateCommand();
|
||||
var pv = GetParamValues();
|
||||
cmd.CommandText = "INSERT INTO client_parameter (param, value) VALUES " +
|
||||
string.Join(", ", pv.Select((pv, i) => $"(@p{i}, " + (pv.Item2 != null ? $"@v{i}" : "NULL") + ")")) +
|
||||
" ON CONFLICT DO UPDATE SET value = excluded.value";
|
||||
|
||||
int i = 0;
|
||||
foreach (var (p, v) in pv) {
|
||||
cmd.Parameters.Add(new SqliteParameter($"@p{i}", p));
|
||||
if (v != null)
|
||||
cmd.Parameters.Add(new SqliteParameter($"@v{i}", v));
|
||||
i++;
|
||||
}
|
||||
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using IniParser;
|
||||
using IniParser.Model;
|
||||
|
||||
@@ -9,11 +8,12 @@ namespace Elwig.Helpers {
|
||||
public class Config {
|
||||
|
||||
private readonly string FileName;
|
||||
public bool Debug;
|
||||
public string DatabaseFile = App.DataPath + "database.sqlite3";
|
||||
public string? DatabaseLog = null;
|
||||
public string? Branch = null;
|
||||
public IEnumerable<string[]> Scales;
|
||||
private readonly LinkedList<string[]> ScaleList = new();
|
||||
public IList<string?[]> Scales;
|
||||
private readonly List<string?[]> ScaleList = new();
|
||||
|
||||
public Config(string filename) {
|
||||
FileName = filename;
|
||||
@@ -25,7 +25,7 @@ namespace Elwig.Helpers {
|
||||
var parser = new FileIniDataParser();
|
||||
IniData? ini = null;
|
||||
try {
|
||||
ini = parser.ReadFile(FileName, Encoding.UTF8);
|
||||
ini = parser.ReadFile(FileName, Utils.UTF8);
|
||||
} catch {}
|
||||
|
||||
if (ini == null || !ini.TryGetKey("database.file", out string db)) {
|
||||
@@ -50,20 +50,37 @@ namespace Elwig.Helpers {
|
||||
Branch = branch;
|
||||
}
|
||||
|
||||
if (ini == null || !ini.TryGetKey("general.debug", out string debug)) {
|
||||
Debug = false;
|
||||
} else {
|
||||
debug = debug.ToLower();
|
||||
Debug = debug == "1" || debug == "true" || debug == "yes" || debug == "on";
|
||||
}
|
||||
|
||||
ScaleList.Clear();
|
||||
Scales = ScaleList;
|
||||
if (ini != null) {
|
||||
foreach (var s in ini.Sections.Where(s => s.SectionName.StartsWith("scale."))) {
|
||||
ScaleList.AddLast(new string[] {
|
||||
string? scaleLog = null;
|
||||
if (s.Keys["log"] != null) {
|
||||
scaleLog = s.Keys["log"];
|
||||
if (scaleLog.Length <= 1 || (scaleLog[1] != ':' && scaleLog[0] != '/' && scaleLog[0] != '\\')) {
|
||||
scaleLog = App.DataPath + scaleLog;
|
||||
}
|
||||
}
|
||||
ScaleList.Add(new string?[] {
|
||||
s.SectionName[6..], s.Keys["type"], s.Keys["model"], s.Keys["connection"],
|
||||
s.Keys["empty"], s.Keys["filling"], s.Keys["limit"]
|
||||
s.Keys["empty"], s.Keys["filling"], s.Keys["limit"], scaleLog
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Write() {
|
||||
using var file = new StreamWriter(FileName, false, Encoding.UTF8);
|
||||
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) {
|
||||
@@ -71,6 +88,7 @@ namespace Elwig.Helpers {
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
224
Elwig/Helpers/ControlUtils.cs
Normal file
224
Elwig/Helpers/ControlUtils.cs
Normal file
@@ -0,0 +1,224 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Elwig.Helpers {
|
||||
public class ControlUtils {
|
||||
|
||||
public enum RenewSourceDefault {
|
||||
None,
|
||||
IfOnly,
|
||||
First
|
||||
}
|
||||
|
||||
private static void SetControlBrush(Control input, Brush brush) {
|
||||
if (input is ComboBox cb) {
|
||||
var border = GetComboBoxBorder(cb);
|
||||
if (border != null) border.BorderBrush = brush;
|
||||
} else {
|
||||
input.BorderBrush = brush;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetInputNotDefault(Control input) {
|
||||
SetControlBrush(input, Brushes.Gold);
|
||||
}
|
||||
|
||||
public static void SetInputChanged(Control input) {
|
||||
SetControlBrush(input, Brushes.Orange);
|
||||
}
|
||||
|
||||
public static void SetInputInvalid(Control input) {
|
||||
SetControlBrush(input, Brushes.Red);
|
||||
}
|
||||
|
||||
public static void ClearInputState(Control input) {
|
||||
if (input is ComboBox cb) {
|
||||
GetComboBoxBorder(cb)?.ClearValue(Border.BorderBrushProperty);
|
||||
} else {
|
||||
input.ClearValue(Control.BorderBrushProperty);
|
||||
}
|
||||
}
|
||||
|
||||
private static Border? GetComboBoxBorder(ComboBox cb) {
|
||||
var toggleButton = cb.Template.FindName("toggleButton", cb) as ToggleButton;
|
||||
return toggleButton?.Template.FindName("templateRoot", toggleButton) as Border;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> FindAllChildren<T>(DependencyObject depObj) where T : DependencyObject {
|
||||
if (depObj == null)
|
||||
yield return (T)Enumerable.Empty<T>();
|
||||
foreach (var child in LogicalTreeHelper.GetChildren(depObj)) {
|
||||
if (child == null) {
|
||||
continue;
|
||||
} else if (child is T t) {
|
||||
yield return t;
|
||||
}
|
||||
if (child is DependencyObject childDepOpj) {
|
||||
foreach (T childOfChild in FindAllChildren<T>(childDepOpj)) {
|
||||
yield return childOfChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<T> FindAllChildren<T>(DependencyObject depObj, IEnumerable<DependencyObject> exempt) where T : DependencyObject {
|
||||
return FindAllChildren<T>(depObj).Where(c => !exempt.Contains(c));
|
||||
}
|
||||
|
||||
public static T? FindNextSibling<T>(Control me) where T : DependencyObject {
|
||||
var found = false;
|
||||
foreach (var child in LogicalTreeHelper.GetChildren(me.Parent)) {
|
||||
if (found && child is T c) {
|
||||
return c;
|
||||
} else if (child == me) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void RenewItemsSource(Selector selector, IEnumerable? source, Func<object?, object?> getId, SelectionChangedEventHandler? handler = null, RenewSourceDefault def = RenewSourceDefault.None) {
|
||||
if (selector.ItemsSource == source)
|
||||
return;
|
||||
var selectedId = getId(selector.SelectedItem);
|
||||
object? selItem = null;
|
||||
if (selectedId != null && source != null)
|
||||
selItem = source.Cast<object>().FirstOrDefault(i => selectedId.Equals(getId(i)));
|
||||
if (source != null && selItem == null) {
|
||||
if ((def == RenewSourceDefault.IfOnly && source.Cast<object>().Count() == 1) || def == RenewSourceDefault.First) {
|
||||
selItem = source.Cast<object>().First();
|
||||
}
|
||||
}
|
||||
if (handler != null && selItem != null) selector.SelectionChanged -= handler;
|
||||
selector.ItemsSource = source;
|
||||
if (handler != null && selItem != null) selector.SelectionChanged += handler;
|
||||
selector.SelectedItem = selItem;
|
||||
}
|
||||
|
||||
public static void RenewItemsSource(Xceed.Wpf.Toolkit.Primitives.Selector selector, IEnumerable? source, Func<object?, object?> getId) {
|
||||
if (selector.ItemsSource == source)
|
||||
return;
|
||||
var selectedIds = selector.SelectedItems.Cast<object>().Select(i => getId(i)).ToList();
|
||||
selector.ItemsSource = source;
|
||||
if (source != null) {
|
||||
selector.SelectedItems.Clear();
|
||||
foreach (var i in source.Cast<object>().Where(i => selectedIds.Contains(getId(i))))
|
||||
selector.SelectedItems.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RenewItemsSource(DataGrid dataGrid, IEnumerable? source, Func<object?, object?> getId, SelectionChangedEventHandler? handler = null, RenewSourceDefault def = RenewSourceDefault.None, bool keepSort = true) {
|
||||
if (dataGrid.ItemsSource == source)
|
||||
return;
|
||||
var column = dataGrid.CurrentCell.Column;
|
||||
var sortColumns = dataGrid.Columns.Select(c => c.SortDirection).ToList();
|
||||
var sort = dataGrid.Items.SortDescriptions.ToList();
|
||||
var selectedId = getId(dataGrid.SelectedItem);
|
||||
object? selItem = null;
|
||||
if (selectedId != null && source != null)
|
||||
selItem = source.Cast<object>().FirstOrDefault(i => selectedId.Equals(getId(i)));
|
||||
if (source != null && selItem == null) {
|
||||
if ((def == RenewSourceDefault.IfOnly && source.Cast<object>().Count() == 1) || def == RenewSourceDefault.First) {
|
||||
selItem = source.Cast<object>().First();
|
||||
}
|
||||
}
|
||||
if (handler != null && selItem != null) dataGrid.SelectionChanged -= handler;
|
||||
dataGrid.ItemsSource = source;
|
||||
if (handler != null && selItem != null) dataGrid.SelectionChanged += handler;
|
||||
dataGrid.SelectedItem = selItem;
|
||||
if (keepSort) {
|
||||
for (int i = 0; i < dataGrid.Columns.Count; i++)
|
||||
dataGrid.Columns[i].SortDirection = sortColumns[i];
|
||||
foreach (var s in sort)
|
||||
dataGrid.Items.SortDescriptions.Add(s);
|
||||
}
|
||||
if (dataGrid.SelectedItem != null && column != null)
|
||||
dataGrid.CurrentCell = new(dataGrid.SelectedItem, column);
|
||||
}
|
||||
|
||||
public static void RenewItemsSource(ListBox listBox, IEnumerable? source, Func<object?, object?> getId, SelectionChangedEventHandler? handler = null, RenewSourceDefault def = RenewSourceDefault.None) {
|
||||
if (listBox.ItemsSource == source)
|
||||
return;
|
||||
var selectedId = getId(listBox.SelectedItem);
|
||||
object? selItem = null;
|
||||
if (selectedId != null && source != null)
|
||||
selItem = source.Cast<object>().FirstOrDefault(i => selectedId.Equals(getId(i)));
|
||||
if (source != null && selItem == null) {
|
||||
if ((def == RenewSourceDefault.IfOnly && source.Cast<object>().Count() == 1) || def == RenewSourceDefault.First) {
|
||||
selItem = source.Cast<object>().FirstOrDefault();
|
||||
}
|
||||
}
|
||||
if (handler != null && selItem != null) listBox.SelectionChanged -= handler;
|
||||
listBox.ItemsSource = source;
|
||||
if (handler != null && selItem != null) listBox.SelectionChanged += handler;
|
||||
listBox.SelectedItem = selItem;
|
||||
}
|
||||
|
||||
public static object? GetItemFromSource(IEnumerable source, Func<object?, object?> getId, object? id) {
|
||||
if (source == null)
|
||||
return null;
|
||||
var items = source.Cast<object>();
|
||||
var item = items.Where(i => getId(i)?.Equals(id) ?? false).FirstOrDefault();
|
||||
if (item == null && items.Any(i => i is NullItem))
|
||||
return items.Where(i => i is NullItem).First();
|
||||
return item;
|
||||
}
|
||||
|
||||
public static object? GetItemFromSource(IEnumerable source, object? item, Func<object?, object?> getId) {
|
||||
return GetItemFromSource(source, getId, getId(item));
|
||||
}
|
||||
|
||||
public static void SelectComboBoxItem(ComboBox cb, Func<object?, object?> getId, object? id) {
|
||||
cb.SelectedItem = GetItemFromSource(cb.ItemsSource, getId, id);
|
||||
}
|
||||
|
||||
public static void SelectComboBoxItem(ComboBox cb, object? item, Func<object?, object?> getId) {
|
||||
SelectComboBoxItem(cb, getId, getId(item));
|
||||
}
|
||||
|
||||
public static IEnumerable<object?> GetItemsFromSource(IEnumerable source, Func<object?, object?> getId, IEnumerable<object?> ids) {
|
||||
if (source == null)
|
||||
return Array.Empty<object>();
|
||||
return source.Cast<object>().Where(i => ids.Any(c => c?.Equals(getId(i)) ?? false));
|
||||
}
|
||||
|
||||
public static IEnumerable<object?> GetItemsFromSource(IEnumerable source, IEnumerable<object?>? items, Func<object?, object?> getId) {
|
||||
if (items == null)
|
||||
return Array.Empty<object>();
|
||||
return GetItemsFromSource(source, getId, items.Select(i => getId(i)));
|
||||
}
|
||||
|
||||
public static void SelectCheckComboBoxItems(Xceed.Wpf.Toolkit.CheckComboBox ccb, Func<object?, object?> getId, IEnumerable<object?>? ids) {
|
||||
ccb.SelectedItems.Clear();
|
||||
if (ids == null) return;
|
||||
foreach (var id in ids)
|
||||
ccb.SelectedItems.Add(GetItemFromSource(ccb.ItemsSource, getId, id));
|
||||
}
|
||||
|
||||
public static void SelectCheckComboBoxItems(Xceed.Wpf.Toolkit.CheckComboBox ccb, IEnumerable<object>? items, Func<object?, object?> getId) {
|
||||
SelectCheckComboBoxItems(ccb, getId, items?.Select(i => getId(i)));
|
||||
}
|
||||
|
||||
public static object? GetInputValue(Control input) {
|
||||
if (input is TextBox tb) {
|
||||
return tb.Text;
|
||||
} else if (input is ComboBox sb) {
|
||||
return sb.SelectedItem;
|
||||
} else if (input is Xceed.Wpf.Toolkit.CheckComboBox ccb) {
|
||||
return ccb.SelectedItems.Cast<object>().ToArray();
|
||||
} else if (input is CheckBox cb) {
|
||||
return cb.IsChecked?.ToString();
|
||||
} else if (input is RadioButton rb) {
|
||||
return rb.IsChecked?.ToString();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
67
Elwig/Helpers/Export/Bki.cs
Normal file
67
Elwig/Helpers/Export/Bki.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
|
||||
namespace Elwig.Helpers.Export {
|
||||
|
||||
using Row = Tuple<(string?, string?, string?, string?, string, int, string, int), (string, int, string, string, string, int, string, double, double)>;
|
||||
|
||||
public class Bki : Csv<Row> {
|
||||
|
||||
private readonly string _clientData;
|
||||
|
||||
public Bki(string filename) : base(filename, ';', Encoding.Latin1) {
|
||||
Header = """
|
||||
EDV-Liste zum automatischen Einlesen in die Weindatenbank;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
Stammdaten;;;;;;;;;;;;;;Transportschein;;;;;;;;;;;;;;;;
|
||||
Empfänger:;;;;;;;Versender:;;;;;;;;;;;;;;;;;;;;;;;
|
||||
Betriebsnr;Name od. Firmenname;Vorname;Straße;Hausnr;Plz;Ort;Betriebsnr;Name od. Firmenname;Vorname;Straße;Hausnr;Plz;Ort;Datum der Lieferung;Menge in kg;Art;Weiß;Rot;Sorte1;Sorte2;Sorte3;Qualitätsstufe;Jahrgang;Herkunft;°KMW;°Oe;Vollablieferer;Ha Gesamt;Ha Ertragsfähig;Flächenbindung in Ha für AMA
|
||||
""";
|
||||
var c = App.Client;
|
||||
var (a1, a2) = Utils.SplitAddress(c.Address);
|
||||
_clientData = $"{c.LfbisNr};{c.NameFull};;{a1};{a2};{c.Plz};{c.Ort}";
|
||||
}
|
||||
|
||||
public async Task ExportAsync(int year) {
|
||||
using var cnx = await AppDbContext.ConnectAsync();
|
||||
using var cmd = cnx.CreateCommand();
|
||||
cmd.CommandText = $"""
|
||||
SELECT lfbis_nr, family_name, name, billing_name, address, plz, ort, area,
|
||||
date, weight, type, sortid, qualid, year, hkid, kmw, oe
|
||||
FROM v_bki_delivery
|
||||
WHERE year = {year}
|
||||
""";
|
||||
var r = await cmd.ExecuteReaderAsync();
|
||||
List<Row> rows = new();
|
||||
while (await r.ReadAsync()) {
|
||||
rows.Add(new(
|
||||
(r.IsDBNull(0) ? null : r.GetString(0), r.IsDBNull(1) ? null : r.GetString(1), r.IsDBNull(2) ? null : r.GetString(2), r.IsDBNull(3) ? null : r.GetString(3), r.GetString(4), r.GetInt32(5), r.GetString(6), r.GetInt32(7)),
|
||||
(r.GetString(8), r.GetInt32(9), r.GetString(10), r.GetString(11), r.GetString(12), r.GetInt32(13), r.GetString(14), r.GetDouble(15), r.GetDouble(16))
|
||||
));
|
||||
}
|
||||
|
||||
await ExportAsync(rows);
|
||||
}
|
||||
|
||||
public void Export(int year) {
|
||||
ExportAsync(year).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public override string FormatRow(Row row) {
|
||||
var (member, delivery) = row;
|
||||
var (lfBisNr, familyName, name, billingName, address, plz, ort, area) = member;
|
||||
var (date, weight, type, sortid, qualid, year, hkid, kmw, oe) = delivery;
|
||||
|
||||
var (n1, n2) = billingName == null ? (familyName, name) : Utils.SplitName(billingName, familyName);
|
||||
var (a1, a2) = Utils.SplitAddress(address);
|
||||
var memberData = $"{lfBisNr};{n1};{n2};{a1};{a2};{plz};{ort}";
|
||||
var deliveryData = $"{string.Join(".", date.Split("-").Reverse())};{weight};TB;{(type == "W" ? "J" : "")};{(type == "R" ? "J" : "")};{sortid};;;{qualid};{year};{hkid};{kmw:0.0};{oe:0}";
|
||||
var vollData = $"N;;;{area / 10_000.0}";
|
||||
|
||||
return $"{_clientData};{memberData};{deliveryData};{vollData}";
|
||||
}
|
||||
}
|
||||
}
|
61
Elwig/Helpers/Export/Csv.cs
Normal file
61
Elwig/Helpers/Export/Csv.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elwig.Helpers.Export {
|
||||
public abstract class Csv<T> : IExporter<T> {
|
||||
|
||||
public static string FileExtension => "csv";
|
||||
|
||||
private readonly StreamWriter _writer;
|
||||
protected readonly char Separator;
|
||||
protected string? Header;
|
||||
|
||||
public Csv(string filename, char separator = ';') : this(filename, separator, Utils.UTF8) { }
|
||||
|
||||
public Csv(string filename, char separator, Encoding encoding) {
|
||||
_writer = new StreamWriter(filename, false, encoding);
|
||||
Separator = separator;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
GC.SuppressFinalize(this);
|
||||
_writer.Dispose();
|
||||
}
|
||||
|
||||
public ValueTask DisposeAsync() {
|
||||
GC.SuppressFinalize(this);
|
||||
return _writer.DisposeAsync();
|
||||
}
|
||||
|
||||
public async Task ExportAsync(IEnumerable<T> data, IProgress<double>? progress = null) {
|
||||
progress?.Report(0.0);
|
||||
int count = data.Count() + 2, i = 0;
|
||||
|
||||
if (Header != null) await _writer.WriteLineAsync(Header);
|
||||
progress?.Report(100.0 * ++i / count);
|
||||
|
||||
foreach (var row in data) {
|
||||
await _writer.WriteLineAsync(FormatRow(row));
|
||||
progress?.Report(100.0 * ++i / count);
|
||||
}
|
||||
|
||||
await _writer.FlushAsync();
|
||||
progress?.Report(100.0);
|
||||
}
|
||||
|
||||
public void Export(IEnumerable<T> data, IProgress<double>? progress = null) {
|
||||
ExportAsync(data, progress).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public string FormatRow(IEnumerable row) {
|
||||
return string.Join(Separator, row);
|
||||
}
|
||||
|
||||
public abstract string FormatRow(T row);
|
||||
}
|
||||
}
|
109
Elwig/Helpers/Export/Ebics.cs
Normal file
109
Elwig/Helpers/Export/Ebics.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
using Elwig.Helpers.Billing;
|
||||
using Elwig.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elwig.Helpers.Export {
|
||||
public class Ebics : IBankingExporter {
|
||||
|
||||
public static string FileExtension => "xml";
|
||||
|
||||
private readonly StreamWriter _writer;
|
||||
private readonly DateOnly _date;
|
||||
private readonly int _year;
|
||||
private readonly string _name;
|
||||
private readonly int _nr;
|
||||
|
||||
public Ebics(PaymentVar variant, string filename) {
|
||||
_writer = new(filename, false, Utils.UTF8);
|
||||
_date = variant.TransferDate ?? DateOnly.Parse("2021-01-10"); //throw new ArgumentException("TransferDate has to be set in PaymentVar");
|
||||
_year = variant.Year;
|
||||
_name = variant.Name;
|
||||
_nr = variant.AvNr;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
GC.SuppressFinalize(this);
|
||||
_writer.Dispose();
|
||||
}
|
||||
|
||||
public ValueTask DisposeAsync() {
|
||||
GC.SuppressFinalize(this);
|
||||
return _writer.DisposeAsync();
|
||||
}
|
||||
|
||||
public void Export(IEnumerable<Transaction> transactions, IProgress<double>? progress = null) {
|
||||
ExportAsync(transactions, progress).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public async Task ExportAsync(IEnumerable<Transaction> transactions, IProgress<double>? progress = null) {
|
||||
progress?.Report(0.0);
|
||||
var nbOfTxs = transactions.Count();
|
||||
int count = nbOfTxs + 2, i = 0;
|
||||
var ctrlSum = Transaction.FormatAmountCent(transactions.Sum(tx => tx.AmountCent));
|
||||
var msgId = $"ELWIG-{App.Client.NameToken}-{_year}-AV{_nr:00}";
|
||||
var pmtInfId = $"{msgId}-1";
|
||||
|
||||
await _writer.WriteLineAsync($"""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09 pain.001.001.09.xsd">
|
||||
<CstmrCdtTrfInitn>
|
||||
<GrpHdr>
|
||||
<MsgId>{msgId}</MsgId>
|
||||
<CreDtTm>{DateTime.UtcNow:o}</CreDtTm>
|
||||
<NbOfTxs>{nbOfTxs}</NbOfTxs>
|
||||
<CtrlSum>{ctrlSum}</CtrlSum>
|
||||
<InitgPty><Nm>{App.Client.NameFull}</Nm></InitgPty>
|
||||
</GrpHdr>
|
||||
<PmtInf>
|
||||
<PmtInfId>{pmtInfId}</PmtInfId>
|
||||
<PmtMtd>TRF</PmtMtd>
|
||||
<NbOfTxs>{nbOfTxs}</NbOfTxs>
|
||||
<CtrlSum>{ctrlSum}</CtrlSum>
|
||||
<ReqdExctnDt><Dt>{_date:yyyy-MM-dd}</Dt></ReqdExctnDt>
|
||||
<Dbtr><Nm>{App.Client.NameFull}</Nm></Dbtr>
|
||||
<DbtrAcct><Id><IBAN>{App.Client.Iban?.Replace(" ", "")}</IBAN></Id></DbtrAcct>
|
||||
<DbtrAgt><FinInstnId><BICFI>{App.Client.Bic ?? "NOTPROVIDED"}</BICFI></FinInstnId></DbtrAgt>
|
||||
""");
|
||||
progress?.Report(100.0 * ++i / count);
|
||||
|
||||
foreach (var tx in transactions) {
|
||||
var a = (IAddress?)tx.Member.BillingAddress ?? tx.Member;
|
||||
var (a1, a2) = Utils.SplitAddress(a.Address);
|
||||
var id = $"ELWIG-{App.Client.NameToken}-{_year}-TG{tx.Nr:0000}";
|
||||
var info = $"{_name} - Traubengutschrift {_year}/{tx.Nr:000}";
|
||||
await _writer.WriteLineAsync($"""
|
||||
<CdtTrfTxInf>
|
||||
<PmtId><EndToEndId>{id}</EndToEndId></PmtId>
|
||||
<Amt><InstdAmt Ccy="{tx.Currency}">{Transaction.FormatAmountCent(tx.AmountCent)}</InstdAmt></Amt>
|
||||
<Cdtr>
|
||||
<Nm>{a.Name}</Nm>
|
||||
<PstlAdr>
|
||||
<StrtNm>{a1}</StrtNm><BldgNb>{a2}</BldgNb>
|
||||
<PstCd>{a.PostalDest.AtPlz?.Plz}</PstCd><TwnNm>{a.PostalDest.AtPlz?.Ort.Name}</TwnNm>
|
||||
<Ctry>{a.PostalDest.Country.Alpha2}</Ctry>
|
||||
</PstlAdr>
|
||||
</Cdtr>
|
||||
<CdtrAcct><Id><IBAN>{tx.Member.Iban}</IBAN></Id><CdtrAcct>
|
||||
<CdtrAgt><FinInstnId><BICFI>{tx.Member.Bic ?? "NOTPROVIDED"}</BICFI></FinInstnId></CdtrAgt>
|
||||
<RmtInf><Ustrd>{info}</Ustrd></RmtInf>
|
||||
</CdtTrfTxInf>
|
||||
""");
|
||||
progress?.Report(100.0 * ++i / count);
|
||||
}
|
||||
|
||||
await _writer.WriteLineAsync("""
|
||||
</PmtInf>
|
||||
</CstmrCdtTrfInitn>
|
||||
</Document>
|
||||
""");
|
||||
await _writer.FlushAsync();
|
||||
progress?.Report(100.0);
|
||||
}
|
||||
}
|
||||
}
|
15
Elwig/Helpers/Export/Elba.cs
Normal file
15
Elwig/Helpers/Export/Elba.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Elwig.Helpers.Billing;
|
||||
using System;
|
||||
|
||||
namespace Elwig.Helpers.Export {
|
||||
public class Elba : Csv<Transaction>, IBankingExporter {
|
||||
|
||||
public static new string FileExtension => "elba";
|
||||
|
||||
public Elba(string filename) : base(filename) { }
|
||||
|
||||
public override string FormatRow(Transaction row) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
8
Elwig/Helpers/Export/IBankingExporter.cs
Normal file
8
Elwig/Helpers/Export/IBankingExporter.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Elwig.Helpers.Billing;
|
||||
|
||||
namespace Elwig.Helpers.Export {
|
||||
/// <summary>
|
||||
/// Interface for exporting banking data
|
||||
/// </summary>
|
||||
public interface IBankingExporter : IExporter<Transaction> { }
|
||||
}
|
26
Elwig/Helpers/Export/IExporter.cs
Normal file
26
Elwig/Helpers/Export/IExporter.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elwig.Helpers.Export {
|
||||
public interface IExporter<T> : IDisposable, IAsyncDisposable {
|
||||
/// <summary>
|
||||
/// The default file extension of the exported files to be used (whithout a preceding ".")
|
||||
/// </summary>
|
||||
static abstract string FileExtension { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Export the given data to the given file.
|
||||
/// </summary>
|
||||
/// <param name="data">The data to be exported</param>
|
||||
/// <param name="progress">The progress object to report to</param>
|
||||
void Export(IEnumerable<T> data, IProgress<double>? progress = null);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronosly export the given data to the given file.
|
||||
/// </summary>
|
||||
/// <param name="data">The data to be exported</param>
|
||||
/// <param name="progress">The progress object to report to</param>
|
||||
Task ExportAsync(IEnumerable<T> data, IProgress<double>? progress = null);
|
||||
}
|
||||
}
|
9
Elwig/Helpers/IAddress.cs
Normal file
9
Elwig/Helpers/IAddress.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Elwig.Models;
|
||||
|
||||
namespace Elwig.Helpers {
|
||||
public interface IAddress {
|
||||
string Name { get; }
|
||||
string Address { get; }
|
||||
PostalDest PostalDest { get; }
|
||||
}
|
||||
}
|
@@ -8,7 +8,7 @@ namespace Elwig.Helpers {
|
||||
|
||||
public TempFile() : this(null) { }
|
||||
|
||||
public TempFile(string? ext) : this(Path.Combine(Path.GetTempPath(), "Elwig"), ext) { }
|
||||
public TempFile(string? ext) : this(App.TempPath, ext) { }
|
||||
|
||||
public TempFile(string dir, string? ext) {
|
||||
FilePath = Path.Combine(dir, Path.GetRandomFileName().Replace(".", "") + (ext != null ? $".{ext}" : ""));
|
||||
|
@@ -2,34 +2,88 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Diagnostics;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.IO.Ports;
|
||||
using System.Net.Sockets;
|
||||
using System.Collections;
|
||||
using Elwig.Dialogs;
|
||||
using System.Text;
|
||||
using System.Numerics;
|
||||
using Elwig.Models;
|
||||
|
||||
namespace Elwig.Helpers {
|
||||
public static partial class Utils {
|
||||
|
||||
public static readonly Encoding UTF8 = new UTF8Encoding(false, true);
|
||||
|
||||
public static int CurrentYear => DateTime.Now.Year;
|
||||
public static int CurrentNextSeason => DateTime.Now.Year - (DateTime.Now.Month <= 3 ? 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 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)]
|
||||
private static partial Regex 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();
|
||||
|
||||
[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();
|
||||
|
||||
[GeneratedRegex(@"^([0-9]+([\.,][0-9]+)?)?-([0-9]+([\.,][0-9]+)?)?$", RegexOptions.Compiled)]
|
||||
private static partial Regex GeneratedFromToRegex();
|
||||
|
||||
[GeneratedRegex(@"^([0-9]{1,2}:[0-9]{2})?-([0-9]{1,2}:[0-9]{2})?$", RegexOptions.Compiled)]
|
||||
private static partial Regex GeneratedFromToTimeRegex();
|
||||
|
||||
[GeneratedRegex(@"^(.*?) +([0-9].*)$", RegexOptions.Compiled)]
|
||||
private static partial Regex GeneratedAddressRegex();
|
||||
|
||||
private static readonly ushort[] Crc16ModbusTable = {
|
||||
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
|
||||
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
|
||||
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
|
||||
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
|
||||
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
|
||||
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
|
||||
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
|
||||
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
|
||||
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
|
||||
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
|
||||
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
|
||||
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
|
||||
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
|
||||
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
|
||||
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
|
||||
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
|
||||
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
|
||||
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
|
||||
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
|
||||
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
|
||||
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
|
||||
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
|
||||
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
|
||||
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
|
||||
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
|
||||
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
|
||||
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
|
||||
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
|
||||
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
|
||||
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
|
||||
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
|
||||
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040,
|
||||
};
|
||||
|
||||
public static SerialPort OpenSerialConnection(string connection) {
|
||||
var m = Utils.SerialRegex.Match(connection);
|
||||
var m = SerialRegex.Match(connection);
|
||||
if (!m.Success)
|
||||
throw new ArgumentException("Invalid connection string for scheme \"serial\"");
|
||||
|
||||
@@ -47,132 +101,26 @@ namespace Elwig.Helpers {
|
||||
DataBits = data == "" ? 8 : int.Parse(data),
|
||||
StopBits = (StopBits)(stop == "" ? 1 : stop == "1.5" ? 3 : stop[0] - '0'),
|
||||
Handshake = Handshake.None,
|
||||
WriteTimeout = 250,
|
||||
ReadTimeout = 11000,
|
||||
};
|
||||
port.Open();
|
||||
return port;
|
||||
}
|
||||
|
||||
public static TcpClient OpenTcpConnection(string connection) {
|
||||
var m = Utils.TcpRegex.Match(connection);
|
||||
var m = TcpRegex.Match(connection);
|
||||
if (!m.Success)
|
||||
throw new ArgumentException("Invalid connection string for scheme \"tcp\"");
|
||||
|
||||
var client = new TcpClient() {
|
||||
SendTimeout = 250,
|
||||
ReceiveTimeout = 250,
|
||||
ReceiveTimeout = 11000,
|
||||
};
|
||||
client.Connect(m.Groups[1].Value, int.Parse(m.Groups[2].Value));
|
||||
return client;
|
||||
}
|
||||
|
||||
public static void SetInputChanged(Control input) {
|
||||
var brush = Brushes.Orange;
|
||||
if (input is ComboBox cb) {
|
||||
var border = GetComboBoxBorder(cb);
|
||||
if (border != null)
|
||||
border.BorderBrush = brush;
|
||||
} else {
|
||||
input.BorderBrush = brush;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetInputInvalid(Control input) {
|
||||
var brush = Brushes.Red;
|
||||
if (input is ComboBox cb) {
|
||||
var border = GetComboBoxBorder(cb);
|
||||
if (border != null)
|
||||
border.BorderBrush = brush;
|
||||
} else {
|
||||
input.BorderBrush = brush;
|
||||
}
|
||||
}
|
||||
|
||||
public static void ClearInputState(Control input) {
|
||||
if (input is ComboBox cb) {
|
||||
GetComboBoxBorder(cb)?.ClearValue(Border.BorderBrushProperty);
|
||||
} else {
|
||||
input.ClearValue(Control.BorderBrushProperty);
|
||||
}
|
||||
}
|
||||
|
||||
private static Border? GetComboBoxBorder(ComboBox cb) {
|
||||
var toggleButton = cb.Template.FindName("toggleButton", cb) as ToggleButton;
|
||||
return toggleButton?.Template.FindName("templateRoot", toggleButton) as Border;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> FindAllChildren<T>(DependencyObject depObj) where T : DependencyObject {
|
||||
if (depObj == null)
|
||||
yield return (T)Enumerable.Empty<T>();
|
||||
foreach (var child in LogicalTreeHelper.GetChildren(depObj)) {
|
||||
if (child == null) {
|
||||
continue;
|
||||
} else if (child is T t) {
|
||||
yield return t;
|
||||
} else if (child is DependencyObject childDepOpj) {
|
||||
foreach (T childOfChild in FindAllChildren<T>(childDepOpj)) {
|
||||
yield return childOfChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<T> FindAllChildren<T>(DependencyObject depObj, IEnumerable<DependencyObject> exempt) where T : DependencyObject {
|
||||
return FindAllChildren<T>(depObj).Where(c => !exempt.Contains(c));
|
||||
}
|
||||
|
||||
public static T? FindNextSibling<T>(Control me) where T : DependencyObject {
|
||||
var found = false;
|
||||
foreach (var child in LogicalTreeHelper.GetChildren(me.Parent)) {
|
||||
if (found && child is T c) {
|
||||
return c;
|
||||
} else if (child == me) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void RenewItemsSource(Selector selector, IEnumerable? source, Func<object?, object?> getId) {
|
||||
var selectedId = getId(selector.SelectedItem);
|
||||
selector.ItemsSource = source;
|
||||
if (selectedId != null && source != null)
|
||||
selector.SelectedItem = source.Cast<object>().FirstOrDefault(i => selectedId.Equals(getId(i)));
|
||||
}
|
||||
|
||||
public static void RenewItemsSource(Xceed.Wpf.Toolkit.Primitives.Selector selector, IEnumerable? source, Func<object?, object?> getId) {
|
||||
var selectedIds = selector.SelectedItems.Cast<object>().Select(i => getId(i)).ToList();
|
||||
selector.ItemsSource = source;
|
||||
if (source != null) {
|
||||
foreach (var i in source.Cast<object>().Where(i => selectedIds.Contains(getId(i))))
|
||||
selector.SelectedItems.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RenewItemsSource(DataGrid dataGrid, IEnumerable? source, Func<object?, object?> getId, bool keepSort = true) {
|
||||
var column = dataGrid.CurrentCell.Column;
|
||||
var sortColumns = dataGrid.Columns.Select(c => c.SortDirection).ToList();
|
||||
var sort = dataGrid.Items.SortDescriptions.ToList();
|
||||
var selectedId = getId(dataGrid.SelectedItem);
|
||||
dataGrid.ItemsSource = source;
|
||||
if (keepSort) {
|
||||
for (int i = 0; i < dataGrid.Columns.Count; i++)
|
||||
dataGrid.Columns[i].SortDirection = sortColumns[i];
|
||||
foreach (var s in sort)
|
||||
dataGrid.Items.SortDescriptions.Add(s);
|
||||
}
|
||||
if (selectedId != null && source != null)
|
||||
dataGrid.SelectedItem = source.Cast<object>().FirstOrDefault(i => selectedId.Equals(getId(i)));
|
||||
if (dataGrid.SelectedItem != null && column != null)
|
||||
dataGrid.CurrentCell = new(dataGrid.SelectedItem, column);
|
||||
}
|
||||
|
||||
public static void RenewItemsSource(ListBox listBox, IEnumerable? source, Func<object?, object?> getId) {
|
||||
var selectedId = getId(listBox.SelectedItem);
|
||||
listBox.ItemsSource = source;
|
||||
if (selectedId != null && source != null)
|
||||
listBox.SelectedItem = source.Cast<object>().FirstOrDefault(i => selectedId.Equals(getId(i)));
|
||||
}
|
||||
|
||||
public static int Modulo(string a, int b) {
|
||||
if (a.Length == 0 || !a.All(char.IsAsciiDigit)) {
|
||||
throw new ArgumentException("First argument has to be a decimal string");
|
||||
@@ -182,6 +130,22 @@ namespace Elwig.Helpers {
|
||||
return a.Select(ch => ch - '0').Aggregate((sum, n) => (sum * 10 + n) % b);
|
||||
}
|
||||
|
||||
public static ushort CalcCrc16Modbus(byte[] data) {
|
||||
// https://www.modbustools.com/modbus_crc16.htm
|
||||
byte temp;
|
||||
ushort crc = 0xFFFF;
|
||||
foreach (byte b in data) {
|
||||
temp = (byte)(b ^ crc);
|
||||
crc >>= 8;
|
||||
crc ^= Crc16ModbusTable[temp];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
public static ushort CalcCrc16Modbus(string data) {
|
||||
return CalcCrc16Modbus(Encoding.ASCII.GetBytes(data));
|
||||
}
|
||||
|
||||
public static void RunBackground(string title, Func<Task> a) {
|
||||
Task.Run(async () => {
|
||||
try {
|
||||
@@ -193,8 +157,12 @@ namespace Elwig.Helpers {
|
||||
}
|
||||
|
||||
public static void MailTo(string emailAddress) {
|
||||
MailTo(new string[] { emailAddress });
|
||||
}
|
||||
|
||||
public static void MailTo(IEnumerable<string> emailAddresses) {
|
||||
Process.Start(new ProcessStartInfo() {
|
||||
FileName = $"mailto:{emailAddress}",
|
||||
FileName = $"mailto:{string.Join(",%20", emailAddresses)}",
|
||||
UseShellExecute = true,
|
||||
});
|
||||
}
|
||||
@@ -229,67 +197,149 @@ namespace Elwig.Helpers {
|
||||
if (!searchKeywords.Any())
|
||||
return 0;
|
||||
|
||||
words = words.Select(w => w?.ToLower());
|
||||
int i = 0;
|
||||
foreach (string? c in words) {
|
||||
if (c == null) continue;
|
||||
var parts = c.Split(" ");
|
||||
if (searchKeywords.Any(f => c == f)) {
|
||||
i += 100;
|
||||
} else if (searchKeywords.Any(f => parts.Any(a => a == f))) {
|
||||
i += 90;
|
||||
} else if (searchKeywords.Any(f => parts.Any(a => a.StartsWith(f)))) {
|
||||
i += 50;
|
||||
} else if (searchKeywords.Any(f => f != null && c.Contains(f))) {
|
||||
i += 1;
|
||||
}
|
||||
return searchKeywords
|
||||
.Select(k => {
|
||||
k = k.ToLower();
|
||||
var scores = words.Select(w => {
|
||||
w = w?.ToLower();
|
||||
var p = w?.ToLower()?.Split(" ");
|
||||
if (w == null || p == null) {
|
||||
return 0;
|
||||
} else if (k == w) {
|
||||
return 4 + k.Length;
|
||||
} else if (p.Any(a => a == k)) {
|
||||
return 3 + k.Length;
|
||||
} else if (p.Any(a => a.StartsWith(k))) {
|
||||
return 2 + k.Length;
|
||||
} else if (w.Contains(k)) {
|
||||
return 1 + k.Length;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
return scores.Max() + scores.Count(s => s > 0);
|
||||
})
|
||||
.Sum();
|
||||
}
|
||||
|
||||
public static (int, string?)? ShowManualWeighingDialog(string? reason = null) {
|
||||
var d = new ManualWeighingDialog(reason);
|
||||
return d.ShowDialog() == true ? (d.Weight, d.Reason) : null;
|
||||
}
|
||||
|
||||
public static int? ShowAbwertenDialog(string lsnr, string name, int weight) {
|
||||
var d = new AbwertenDialog(lsnr, name, weight);
|
||||
return d.ShowDialog() == true ? d.Weight : null;
|
||||
}
|
||||
|
||||
public static double? ShowLinearPriceIncreaseDialog() {
|
||||
var d = new LinearPriceIncreaseDialog();
|
||||
return d.ShowDialog() == true ? d.Price : null;
|
||||
}
|
||||
|
||||
public static string? ShowDeliveryExtractionDialog(string lsnr, string name, bool single, IEnumerable<string> lsnrs) {
|
||||
var d = new DeliveryExtractionDialog(lsnr, name, single, lsnrs);
|
||||
return d.ShowDialog() == true ? d.AddTo : null;
|
||||
}
|
||||
|
||||
public static Footer GenerateFooter(string lineBreak, string seperator) {
|
||||
return new Footer(lineBreak, seperator);
|
||||
}
|
||||
|
||||
public class Footer {
|
||||
private string Text = "";
|
||||
private readonly string LineBreak;
|
||||
private readonly string Seperator;
|
||||
private bool FirstLine = true;
|
||||
private bool FirstItemInLine = true;
|
||||
|
||||
public Footer(string lineBreak, string seperator) {
|
||||
LineBreak = lineBreak;
|
||||
Seperator = seperator;
|
||||
}
|
||||
|
||||
public Footer Item(string? text) {
|
||||
if (text == null) return this;
|
||||
Text += FirstItemInLine ? (FirstLine ? "" : LineBreak) : Seperator;
|
||||
Text += text;
|
||||
FirstLine = false;
|
||||
FirstItemInLine = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Footer Item(string name, string? text) {
|
||||
return text == null ? this : Item($"{name}: {text}");
|
||||
}
|
||||
|
||||
public Footer NextLine() {
|
||||
FirstItemInLine = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return Text;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
public static object? GetItemFromSource(IEnumerable source, Func<object?, object?> getId, object? id) {
|
||||
if (source == null)
|
||||
return null;
|
||||
var items = source.Cast<object>();
|
||||
var item = items.Where(i => getId(i)?.Equals(id) ?? false).FirstOrDefault();
|
||||
if (item == null && items.Any(i => i is NullItem))
|
||||
return items.Where(i => i is NullItem).First();
|
||||
return item;
|
||||
public static string GetSign<T>(T number) where T : INumber<T>
|
||||
=> T.Sign(number) switch {
|
||||
< 0 => "\u2212", // minus
|
||||
0 => "\u00b1", // plus minus
|
||||
> 0 => "+",
|
||||
};
|
||||
|
||||
public static double AggregateDeliveryPartsKmw(IEnumerable<DeliveryPart> parts)
|
||||
=> parts.Aggregate(
|
||||
(Weight: 0, Kmw: 0.0),
|
||||
(sum, item) => (
|
||||
sum.Weight + item.Weight,
|
||||
(sum.Kmw * sum.Weight + item.Kmw * item.Weight) / (sum.Weight + item.Weight)
|
||||
),
|
||||
sum => sum.Kmw
|
||||
);
|
||||
|
||||
public static string GenerateLsNr(Delivery d) => GenerateLsNr(d.Date, d.ZwstId, d.LNr);
|
||||
|
||||
public static string GenerateLsNr(DateOnly date, string zwstid, int lnr) => $"{date:yyyyMMdd}{zwstid}{lnr:000}";
|
||||
|
||||
public static (string, string?) SplitAddress(string address) {
|
||||
var m = AddressRegex.Match(address);
|
||||
return (m.Groups[1].Value, m.Groups[2].Value);
|
||||
}
|
||||
|
||||
public static object? GetItemFromSource(IEnumerable source, object? item, Func<object?, object?> getId) {
|
||||
return GetItemFromSource(source, getId, getId(item));
|
||||
public static (string, string?) SplitName(string fullName, string? familyName) {
|
||||
if (familyName == null || familyName == "") return (fullName, null);
|
||||
var p0 = fullName.ToLower().IndexOf(familyName.ToLower());
|
||||
if (p0 == -1) return (fullName, null);
|
||||
var p1 = fullName.IndexOf(" und ");
|
||||
var p2 = fullName.ToLower().LastIndexOf(" und ");
|
||||
if (p1 != p2) {
|
||||
if (p0 > p1) {
|
||||
// A und B familyName [und ...]
|
||||
return (fullName[p0..^0], fullName[0..(p0 - 1)]);
|
||||
} else {
|
||||
// familyName und ... A und B
|
||||
var p3 = fullName.LastIndexOf(' ', p2 - 1);
|
||||
return (fullName[0..p3], fullName[(p3 + 1)..^0]);
|
||||
}
|
||||
} else {
|
||||
return (familyName, fullName.Replace(familyName, "").Replace(" ", " ").Trim());
|
||||
}
|
||||
}
|
||||
|
||||
public static void SelectComboBoxItem(ComboBox cb, Func<object?, object?> getId, object? id) {
|
||||
cb.SelectedItem = GetItemFromSource(cb.ItemsSource, getId, id);
|
||||
}
|
||||
|
||||
public static void SelectComboBoxItem(ComboBox cb, object? item, Func<object?, object?> getId) {
|
||||
SelectComboBoxItem(cb, getId, getId(item));
|
||||
}
|
||||
|
||||
public static IEnumerable<object?> GetItemsFromSource(IEnumerable source, Func<object?, object?> getId, IEnumerable<object?> ids) {
|
||||
if (source == null)
|
||||
return Array.Empty<object>();
|
||||
return source.Cast<object>().Where(i => ids.Any(c => c?.Equals(getId(i)) ?? false));
|
||||
}
|
||||
|
||||
public static IEnumerable<object?> GetItemsFromSource(IEnumerable source, IEnumerable<object?>? items, Func<object?, object?> getId) {
|
||||
if (items == null)
|
||||
return Array.Empty<object>();
|
||||
return GetItemsFromSource(source, getId, items.Select(i => getId(i)));
|
||||
}
|
||||
|
||||
public static void SelectCheckComboBoxItems(Xceed.Wpf.Toolkit.CheckComboBox ccb, Func<object?, object?> getId, IEnumerable<object?>? ids) {
|
||||
ccb.SelectedItems.Clear();
|
||||
if (ids == null) return;
|
||||
foreach (var id in ids)
|
||||
ccb.SelectedItems.Add(GetItemFromSource(ccb.ItemsSource, getId, id));
|
||||
}
|
||||
|
||||
public static void SelectCheckComboBoxItems(Xceed.Wpf.Toolkit.CheckComboBox ccb, IEnumerable<object>? items, Func<object?, object?> getId) {
|
||||
SelectCheckComboBoxItems(ccb, getId, items?.Select(i => getId(i)));
|
||||
public static IEnumerable<IEnumerable<T>> Permutate<T>(IEnumerable<T> input, IEnumerable<T>? forced = null) {
|
||||
HashSet<IEnumerable<T>> output = new();
|
||||
for (int i = 0; i < Math.Pow(2, input.Count()); i++) {
|
||||
List<T> t = new();
|
||||
for (int j = 0; j < 30; j++) {
|
||||
var e = input.ElementAtOrDefault(j);
|
||||
if (e != null && ((forced?.Contains(e) ?? false) || (i & (1 << j)) != 0)) {
|
||||
t.Add(e);
|
||||
}
|
||||
}
|
||||
output.Add(t);
|
||||
}
|
||||
return output.OrderByDescending(l => l.Count());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -70,9 +70,9 @@ namespace Elwig.Helpers {
|
||||
for (int i = 0; i < input.Text.Length; i++) {
|
||||
char ch = input.Text[i];
|
||||
if (char.IsAsciiDigit(ch)) {
|
||||
if (v2 == -1 && v1 < maxLen) {
|
||||
if (v2 == -1 && (maxLen == -1 || v1 < maxLen)) {
|
||||
text += ch; v1++;
|
||||
} else if (v2 != -1 && v2 < maxDecimal) {
|
||||
} else if (v2 != -1 && (maxDecimal == -1 || v2 < maxDecimal)) {
|
||||
text += ch; v2++;
|
||||
}
|
||||
} else if (v2 == 0-1 && ch == ',' || ch == '.') {
|
||||
@@ -332,7 +332,7 @@ namespace Elwig.Helpers {
|
||||
return new(true, null);
|
||||
}
|
||||
|
||||
public static ValidationResult CheckUstId(TextBox input, bool required) {
|
||||
public static ValidationResult CheckUstIdNr(TextBox input, bool required) {
|
||||
string text = "";
|
||||
int pos = input.CaretIndex;
|
||||
for (int i = 0, v = 0; i < input.Text.Length; i++) {
|
||||
@@ -521,6 +521,36 @@ namespace Elwig.Helpers {
|
||||
return new(true, null);
|
||||
}
|
||||
|
||||
public static ValidationResult CheckTime(TextBox input, bool required) {
|
||||
string text = "";
|
||||
int pos = input.CaretIndex;
|
||||
int v = 0;
|
||||
for (int i = 0; i < input.Text.Length; i++) {
|
||||
char ch = input.Text[i];
|
||||
if (v >= 0 && v < 5 && v != 2 && char.IsAsciiDigit(ch)) {
|
||||
if ((v == 0 && ch <= '2') || (v == 1 && (text[0] < '2' || ch <= '3')) || (v == 3 && ch <= '5') || v == 4) {
|
||||
text += ch;
|
||||
v++;
|
||||
}
|
||||
} else if (v == 2 && ch == ':') {
|
||||
text += ch;
|
||||
v++;
|
||||
}
|
||||
if (i == input.CaretIndex - 1)
|
||||
pos = text.Length;
|
||||
}
|
||||
input.Text = text;
|
||||
input.CaretIndex = pos;
|
||||
|
||||
if (text.Length == 0) {
|
||||
return required ? new(false, "Wert ist nicht optional") : new(true, null);
|
||||
} else if (v != 5) {
|
||||
return new(false, "Zeit ist ungültig");
|
||||
} else {
|
||||
return new(true, null);
|
||||
}
|
||||
}
|
||||
|
||||
public static ValidationResult CheckFbNr(TextBox input, bool required, AppDbContext ctx, AreaCom? c) {
|
||||
var res = CheckInteger(input, required);
|
||||
if (!res.IsValid) {
|
||||
|
@@ -14,13 +14,14 @@ namespace Elwig.Helpers.Weighing {
|
||||
public string Manufacturer => "Gassner";
|
||||
public int InternalScaleNr => 1;
|
||||
public string Model { get; private set; }
|
||||
public int ScaleNr { 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(int scaleNr, string model, string connection) {
|
||||
ScaleNr = scaleNr;
|
||||
public GassnerScale(string id, string model, string connection) {
|
||||
ScaleId = id;
|
||||
Model = model;
|
||||
IsReady = true;
|
||||
HasFillingClearance = false;
|
||||
|
@@ -17,9 +17,9 @@ namespace Elwig.Helpers.Weighing {
|
||||
string Model { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Unique number of the scale
|
||||
/// Unique identificator of the scale
|
||||
/// </summary>
|
||||
int ScaleNr { get; }
|
||||
string ScaleId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal identifying number of the scale in its system
|
||||
@@ -41,6 +41,11 @@ namespace Elwig.Helpers.Weighing {
|
||||
/// </summary>
|
||||
int? WeightLimit { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Where to log the requests and responses from the scale to
|
||||
/// </summary>
|
||||
string? LogPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the current weight on the scale without performing a weighing process
|
||||
/// </summary>
|
||||
|
44
Elwig/Helpers/Weighing/InvalidScale.cs
Normal file
44
Elwig/Helpers/Weighing/InvalidScale.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elwig.Helpers.Weighing {
|
||||
public class InvalidScale : IScale {
|
||||
|
||||
public string Manufacturer => "NONE";
|
||||
public string Model => "NONE";
|
||||
public string ScaleId { get; private set; }
|
||||
public int InternalScaleNr => 0;
|
||||
public bool IsReady => false;
|
||||
public bool HasFillingClearance => false;
|
||||
public int? WeightLimit => null;
|
||||
public string? LogPath => null;
|
||||
|
||||
public InvalidScale(string id) {
|
||||
ScaleId = id;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public Task<WeighingResult> Weigh() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<WeighingResult> GetCurrentWeight() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task Empty() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task GrantFillingClearance() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task RevokeFillingClearance() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@@ -7,11 +7,12 @@ namespace Elwig.Helpers.Weighing {
|
||||
|
||||
public string Manufacturer => "Schember";
|
||||
public string Model => throw new NotImplementedException();
|
||||
public int ScaleNr => 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() {
|
||||
throw new NotImplementedException();
|
||||
|
@@ -11,9 +11,9 @@ namespace Elwig.Helpers.Weighing {
|
||||
protected enum Output { RTS, DTR, OUT1, OUT2 };
|
||||
|
||||
protected SerialPort? Serial = null;
|
||||
protected SerialPort? ControlSerialEmpty = null, ControlSerialFilling = null;
|
||||
protected TcpClient? Tcp = null;
|
||||
protected StreamReader Reader;
|
||||
protected StreamWriter Writer;
|
||||
protected Stream Stream;
|
||||
|
||||
protected readonly Output? EmptyMode = null;
|
||||
protected readonly Output? FillingClearanceMode = null;
|
||||
@@ -22,71 +22,89 @@ namespace Elwig.Helpers.Weighing {
|
||||
public string Manufacturer => "SysTec";
|
||||
public int InternalScaleNr => 1;
|
||||
public string Model { get; private set; }
|
||||
public int ScaleNr { 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 SystecScale(int scaleNr, string model, string connection, string? empty = null, string? fill = null, int? limit = null) {
|
||||
ScaleNr = scaleNr;
|
||||
public SystecScale(string id, string model, string connection, string? empty = null, string? filling = null, int? limit = null, string? log = null) {
|
||||
ScaleId = id;
|
||||
Model = model;
|
||||
IsReady = true;
|
||||
HasFillingClearance = false;
|
||||
LogPath = log;
|
||||
|
||||
Stream stream;
|
||||
if (connection.StartsWith("serial:")) {
|
||||
Serial = Utils.OpenSerialConnection(connection);
|
||||
stream = Serial.BaseStream;
|
||||
Stream = Serial.BaseStream;
|
||||
} else if (connection.StartsWith("tcp:")) {
|
||||
Tcp = Utils.OpenTcpConnection(connection);
|
||||
stream = Tcp.GetStream();
|
||||
Stream = Tcp.GetStream();
|
||||
} else {
|
||||
throw new ArgumentException("Unsupported scheme");
|
||||
}
|
||||
|
||||
if (empty != null) {
|
||||
var parts = empty.Split(':');
|
||||
EmptyMode = ConvertOutput(parts[0]);
|
||||
EmptyDelay = int.Parse(parts[1]);
|
||||
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]);
|
||||
}
|
||||
FillingClearanceMode = ConvertOutput(fill);
|
||||
WeightLimit = limit;
|
||||
if (FillingClearanceMode != null && WeightLimit == null)
|
||||
throw new ArgumentException("Weight limit has to be set, if filling clearance supervision is enalbed");
|
||||
|
||||
Writer = new(stream, Encoding.ASCII, -1, true);
|
||||
Reader = new(stream, Encoding.ASCII, false, -1, true);
|
||||
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() {
|
||||
Writer.Close();
|
||||
Reader.Close();
|
||||
Stream.Close();
|
||||
Serial?.Close();
|
||||
ControlSerialEmpty?.Close();
|
||||
ControlSerialFilling?.Close();
|
||||
Tcp?.Close();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected static Output? ConvertOutput(string? value) {
|
||||
if (value == null) return null;
|
||||
value = value.ToUpper();
|
||||
if (value == "RTS") {
|
||||
return Output.RTS;
|
||||
} else if (value == "DTR") {
|
||||
return Output.DTR;
|
||||
} else if (value == "OUT1") {
|
||||
return Output.OUT1;
|
||||
} else if (value == "OUT2") {
|
||||
return Output.OUT2;
|
||||
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) {
|
||||
byte[] bytes = Encoding.ASCII.GetBytes($"<{command}>");
|
||||
await Stream.WriteAsync(bytes);
|
||||
if (LogPath != null) await File.AppendAllTextAsync(LogPath, $"<{command}>");
|
||||
}
|
||||
|
||||
protected async Task<string> ReceiveResponse() {
|
||||
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");
|
||||
}
|
||||
throw new ArgumentException("Invalid value for argument empty");
|
||||
}
|
||||
|
||||
public async Task SendCommand(string command) {
|
||||
await Writer.WriteAsync($"<{command}>");
|
||||
}
|
||||
|
||||
public async Task<string> ReceiveResponse() {
|
||||
var line = await Reader.ReadLineAsync();
|
||||
if (line == null || line.Length < 4 || !line.StartsWith("<") || !line.EndsWith(">")) {
|
||||
throw new IOException("Invalid response from scale");
|
||||
}
|
||||
@@ -94,7 +112,7 @@ namespace Elwig.Helpers.Weighing {
|
||||
var error = line[1..3];
|
||||
if (error[0] == '0') {
|
||||
if (error[1] != '0') {
|
||||
throw new IOException($"Invalid response from scale (error code {error}");
|
||||
throw new IOException($"Invalid response from scale (error code {error})");
|
||||
}
|
||||
} else if (error[0] == '1') {
|
||||
string msg = $"Unbekannter Fehler (Fehler code {error})";
|
||||
@@ -119,32 +137,44 @@ namespace Elwig.Helpers.Weighing {
|
||||
throw new IOException($"Invalid response from scale (error code {error})");
|
||||
}
|
||||
|
||||
return line[3..(line.Length - 1)];
|
||||
return line[1..^1];
|
||||
}
|
||||
|
||||
protected async Task<WeighingResult> Weigh(bool incIdentNr) {
|
||||
await SendCommand(incIdentNr ? $"RM{InternalScaleNr}" : $"RN{InternalScaleNr}");
|
||||
string line = await ReceiveResponse();
|
||||
await SendCommand(incIdentNr ? $"RN{InternalScaleNr}" : $"RM{InternalScaleNr}");
|
||||
string record = await ReceiveResponse();
|
||||
if (record.Length != 62)
|
||||
throw new IOException("Invalid response from scale: Received record has invalid size");
|
||||
var line = record[2..];
|
||||
|
||||
var status = line[ 0.. 2];
|
||||
var date = line[ 2..10];
|
||||
var time = line[10..15];
|
||||
var identNr = line[15..19];
|
||||
var identNr = line[15..19].Trim();
|
||||
var scaleNr = line[19..20];
|
||||
var brutto = line[20..28];
|
||||
var tara = line[28..36];
|
||||
var netto = line[36..44];
|
||||
var brutto = line[20..28].Trim();
|
||||
var tara = line[28..36].Trim();
|
||||
var netto = line[36..44].Trim();
|
||||
var unit = line[44..46];
|
||||
var taraCode = line[46..48];
|
||||
var zone = line[48..49];
|
||||
var terminalNr = line[49..52];
|
||||
var crc16 = line[52..60];
|
||||
var zone = line[48..49].Trim();
|
||||
var terminalNr = line[49..52].Trim();
|
||||
var crc16 = line[52..60].Trim();
|
||||
|
||||
if (Utils.CalcCrc16Modbus(record[..54]) != ushort.Parse(crc16)) {
|
||||
throw new IOException($"Invalid response from scale: Invalid CRC16 checksum ({crc16} != {Utils.CalcCrc16Modbus(record[..54]).ToString()})");
|
||||
} else if (unit != "kg") {
|
||||
throw new IOException($"Unsupported unit in weighing response: '{unit}'");
|
||||
}
|
||||
|
||||
identNr = identNr.Length > 0 && identNr != "0" ? identNr : null;
|
||||
var parsedDate = DateOnly.Parse(date);
|
||||
return new() {
|
||||
Weight = int.TryParse(netto.Trim(), out int w) ? w : null,
|
||||
WeighingId = identNr.Trim().Length > 0 ? identNr.Trim() : null,
|
||||
Date = date,
|
||||
Time = time,
|
||||
Weight = int.Parse(netto),
|
||||
WeighingId = identNr,
|
||||
FullWeighingId = identNr != null ? $"{parsedDate:yyyy-MM-dd}/{identNr}" : null,
|
||||
Date = parsedDate,
|
||||
Time = TimeOnly.Parse(time),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -157,33 +187,35 @@ namespace Elwig.Helpers.Weighing {
|
||||
}
|
||||
|
||||
public async Task Empty() {
|
||||
if (EmptyMode == Output.RTS && Serial != null) {
|
||||
Serial.RtsEnable = true;
|
||||
SerialPort? p = ControlSerialEmpty ?? Serial;
|
||||
if (EmptyMode == Output.RTS && p != null) {
|
||||
p.RtsEnable = true;
|
||||
await Task.Delay(EmptyDelay);
|
||||
Serial.RtsEnable = false;
|
||||
} else if (EmptyMode == Output.DTR && Serial != null) {
|
||||
Serial.DtrEnable = true;
|
||||
p.RtsEnable = false;
|
||||
} else if (EmptyMode == Output.DTR && p != null) {
|
||||
p.DtrEnable = true;
|
||||
await Task.Delay(EmptyDelay);
|
||||
Serial.DtrEnable = false;
|
||||
p.DtrEnable = false;
|
||||
} else if (EmptyMode == Output.OUT1 || EmptyMode == Output.OUT2) {
|
||||
int output = EmptyMode == Output.OUT1 ? 1 : 2;
|
||||
await SendCommand($"OS{output:02i}");
|
||||
await SendCommand($"OS{output:00}");
|
||||
await ReceiveResponse();
|
||||
await Task.Delay(EmptyDelay);
|
||||
await SendCommand($"OC{output:02i}");
|
||||
await SendCommand($"OC{output:00}");
|
||||
await ReceiveResponse();
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task SetFillingClearance(bool status) {
|
||||
if (FillingClearanceMode == Output.RTS && Serial != null) {
|
||||
Serial.RtsEnable = status;
|
||||
} else if (FillingClearanceMode == Output.DTR && Serial != null) {
|
||||
Serial.DtrEnable = status;
|
||||
SerialPort? p = ControlSerialFilling ?? Serial;
|
||||
if (FillingClearanceMode == Output.RTS && p != null) {
|
||||
p.RtsEnable = status;
|
||||
} else if (FillingClearanceMode == Output.DTR && p != null) {
|
||||
p.DtrEnable = status;
|
||||
} else if (FillingClearanceMode == Output.OUT1 || FillingClearanceMode == Output.OUT2) {
|
||||
string cmd = status ? "OS" : "OC";
|
||||
int output = FillingClearanceMode == Output.OUT1 ? 1 : 2;
|
||||
await SendCommand($"{cmd}{output:02i}");
|
||||
await SendCommand($"{cmd}{output:00}");
|
||||
await ReceiveResponse();
|
||||
}
|
||||
}
|
||||
|
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
|
||||
namespace Elwig.Helpers.Weighing {
|
||||
/// <summary>
|
||||
/// Result of a weighing process on an industrial scale
|
||||
@@ -13,14 +15,25 @@ namespace Elwig.Helpers.Weighing {
|
||||
/// </summary>
|
||||
public string? WeighingId = null;
|
||||
|
||||
/// <summary>
|
||||
/// Wheighing id (or IdentNr) provided by the scale optionally combined with the current date
|
||||
/// </summary>
|
||||
public string? FullWeighingId = null;
|
||||
|
||||
/// <summary>
|
||||
/// Date string provided by the scale
|
||||
/// </summary>
|
||||
public string? Date = null;
|
||||
public DateOnly? Date = null;
|
||||
|
||||
/// <summary>
|
||||
/// Time string provided by the scale
|
||||
/// </summary>
|
||||
public string? Time = null;
|
||||
public TimeOnly? Time = null;
|
||||
|
||||
/// <returns><Weight/WeighingId/Date/Time></returns>
|
||||
override public string ToString() {
|
||||
var w = Weight != null ? $"{Weight}kg" : "";
|
||||
return $"<{w}/{WeighingId}/{Date:yyyy-MM-dd}/{Time:HH:mm}>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using IndexAttribute = Microsoft.EntityFrameworkCore.IndexAttribute;
|
||||
|
||||
namespace Elwig.Models {
|
||||
[Table("AT_plz_dest"), PrimaryKey("Id"), Index("Plz", "Okz", IsUnique = true)]
|
||||
|
@@ -1,7 +1,5 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
|
||||
namespace Elwig.Models {
|
||||
[Table("area_commitment"), PrimaryKey("FbNr")]
|
||||
@@ -12,8 +10,8 @@ namespace Elwig.Models {
|
||||
[Column("mgnr")]
|
||||
public int MgNr { get; set; }
|
||||
|
||||
[Column("sortid")]
|
||||
public string SortId { get; set; }
|
||||
[Column("vtrgid")]
|
||||
public string VtrgId { get; set; }
|
||||
|
||||
[Column("cultid")]
|
||||
public string CultId { get; set; }
|
||||
@@ -42,8 +40,8 @@ namespace Elwig.Models {
|
||||
[ForeignKey("MgNr")]
|
||||
public virtual Member Member { get; private set; }
|
||||
|
||||
[ForeignKey("SortId")]
|
||||
public virtual WineVar WineVar { get; private set; }
|
||||
[ForeignKey("VtrgId")]
|
||||
public virtual AreaComType AreaComType { get; private set; }
|
||||
|
||||
[ForeignKey("CultId")]
|
||||
public virtual WineCult WineCult { get; private set; }
|
||||
@@ -53,11 +51,5 @@ namespace Elwig.Models {
|
||||
|
||||
[ForeignKey("KgNr, RdNr")]
|
||||
public virtual WbRd? Rd { get; private set; }
|
||||
|
||||
[InverseProperty("AreaCom")]
|
||||
public virtual ISet<AreaComAttr> AttributeEntries { get; private set; }
|
||||
|
||||
[NotMapped]
|
||||
public IEnumerable<WineAttr> Attributes => AttributeEntries.Select(e => e.WineAttr);
|
||||
}
|
||||
}
|
||||
|
@@ -1,19 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Elwig.Models {
|
||||
[Table("area_commitment_attribute"), PrimaryKey("FbNr", "AttrId")]
|
||||
public class AreaComAttr {
|
||||
[Column("fbnr")]
|
||||
public int FbNr { get; set; }
|
||||
|
||||
[Column("attrid")]
|
||||
public string AttrId { get; set; }
|
||||
|
||||
[ForeignKey("FbNr")]
|
||||
public virtual AreaCom AreaCom { get; private set; }
|
||||
|
||||
[ForeignKey("AttrId")]
|
||||
public virtual WineAttr WineAttr { get; private set; }
|
||||
}
|
||||
}
|
52
Elwig/Models/AreaComType.cs
Normal file
52
Elwig/Models/AreaComType.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using Elwig.Helpers;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using IndexAttribute = Microsoft.EntityFrameworkCore.IndexAttribute;
|
||||
|
||||
namespace Elwig.Models {
|
||||
[Table("area_commitment_type"), PrimaryKey("VtrgId"), Index("SortId", "AttrId1", "AttrId2", "Discriminator")]
|
||||
public class AreaComType {
|
||||
[Column("vtrgid")]
|
||||
public string VtrgId { get; set; }
|
||||
|
||||
[Column("sortid")]
|
||||
public string SortId { get; set; }
|
||||
|
||||
[Column("attrid_1")]
|
||||
public string? AttrId1 { get; set; }
|
||||
|
||||
[Column("attrid_2")]
|
||||
public string? AttrId2 { get; set; }
|
||||
|
||||
[Column("disc")]
|
||||
public string? Discriminator { get; set; }
|
||||
|
||||
[Column("min_kg_per_ha")]
|
||||
public int? MinKgPerHa { get; set; }
|
||||
|
||||
[Column("max_kg_per_ha")]
|
||||
public int? MaxKgPerHa { get; set; }
|
||||
|
||||
[Column("penalty_amount")]
|
||||
public long? PenaltyAmoutValue { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public decimal? PenaltyAmount {
|
||||
get => PenaltyAmoutValue != null ? Utils.DecFromDb(PenaltyAmoutValue.Value, 4) : null;
|
||||
set => PenaltyAmoutValue = value != null ? Utils.DecToDb(value.Value, 4) : null;
|
||||
}
|
||||
|
||||
[ForeignKey("SortId")]
|
||||
public virtual WineVar WineVar { get; private set; }
|
||||
|
||||
[ForeignKey("AttrId1")]
|
||||
public virtual WineAttr? WineAttr1 { get; private set; }
|
||||
|
||||
[ForeignKey("AttrId2")]
|
||||
public virtual WineAttr? WineAttr2 { get; private set; }
|
||||
|
||||
[NotMapped]
|
||||
public string DisplayName => WineVar.Name + (WineAttr1 != null ? $" {WineAttr1.Name}" : "") +
|
||||
(WineAttr2 != null ? $" {WineAttr2.Name}" : "") + (Discriminator != null ? $" ({Discriminator})" : "");
|
||||
}
|
||||
}
|
@@ -1,9 +1,10 @@
|
||||
using Elwig.Helpers;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Elwig.Models {
|
||||
[Table("member_billing_address"), PrimaryKey("MgNr")]
|
||||
public class BillingAddr {
|
||||
public class BillingAddr : IAddress {
|
||||
[Column("mgnr")]
|
||||
public int MgNr { get; set; }
|
||||
|
||||
|
@@ -11,6 +11,30 @@ namespace Elwig.Models {
|
||||
[Column("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Column("country")]
|
||||
public int? CountryNum { get; set; }
|
||||
|
||||
[Column("postal_dest")]
|
||||
public string? PostalDestId { get; set; }
|
||||
|
||||
[Column("address")]
|
||||
public string? Address { get; set; }
|
||||
|
||||
[ForeignKey("CountryNum")]
|
||||
public virtual Country? Country { get; private set; }
|
||||
|
||||
[ForeignKey("CountryNum, PostalDestId")]
|
||||
public virtual PostalDest? PostalDest { get; private set; }
|
||||
|
||||
[Column("phone_nr")]
|
||||
public string? PhoneNr { get; set; }
|
||||
|
||||
[Column("fax_nr")]
|
||||
public string? FaxNr { get; set; }
|
||||
|
||||
[Column("mobile_nr")]
|
||||
public string? MobileNr { get; set; }
|
||||
|
||||
[InverseProperty("Branch")]
|
||||
public virtual ISet<Member> Members { get; private set; }
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using IndexAttribute = Microsoft.EntityFrameworkCore.IndexAttribute;
|
||||
|
||||
namespace Elwig.Models {
|
||||
[Table("country"), PrimaryKey("Num"), Index("Alpha2", IsUnique = true), Index("Alpha3", IsUnique = true)]
|
||||
|
105
Elwig/Models/Credit.cs
Normal file
105
Elwig/Models/Credit.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using Elwig.Helpers;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using IndexAttribute = Microsoft.EntityFrameworkCore.IndexAttribute;
|
||||
|
||||
namespace Elwig.Models {
|
||||
[Table("credit"), PrimaryKey("Year", "TgNr"), Index("Year", "AvNr", "MgNr", IsUnique = true)]
|
||||
public class Credit {
|
||||
[Column("year")]
|
||||
public int Year { get; set; }
|
||||
|
||||
[Column("tgnr")]
|
||||
public int TgNr { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public string TgId => $"{Year}/{TgNr:000}";
|
||||
|
||||
[Column("mgnr")]
|
||||
public int MgNr { get; set; }
|
||||
|
||||
[Column("avnr")]
|
||||
public int AvNr { get; set; }
|
||||
|
||||
[Column("net_amount")]
|
||||
public long NetAmountValue { get; set; }
|
||||
[NotMapped]
|
||||
public decimal NetAmount {
|
||||
get => Utils.DecFromDb(NetAmountValue, 2);
|
||||
set => NetAmountValue = Utils.DecToDb(value, 2);
|
||||
}
|
||||
|
||||
[Column("prev_net_amount")]
|
||||
public long? PrevNetAmountValue { get; set; }
|
||||
[NotMapped]
|
||||
public decimal? PrevNetAmount {
|
||||
get => PrevNetAmountValue != null ? Utils.DecFromDb(PrevNetAmountValue.Value, 2) : null;
|
||||
set => PrevNetAmountValue = value != null ? Utils.DecToDb(value.Value, 2) : null;
|
||||
}
|
||||
|
||||
[Column("vat")]
|
||||
public double VatValue { get; set; }
|
||||
[NotMapped]
|
||||
public decimal Vat {
|
||||
get => (decimal)VatValue;
|
||||
set => VatValue = (double)value;
|
||||
}
|
||||
|
||||
[Column("vat_amount")]
|
||||
public long VatAmountValue { get; private set; }
|
||||
[NotMapped]
|
||||
public decimal VatAmount {
|
||||
get => Utils.DecFromDb(VatAmountValue, 2);
|
||||
}
|
||||
|
||||
[Column("gross_amount")]
|
||||
public long GrossAmountValue { get; private set; }
|
||||
[NotMapped]
|
||||
public decimal GrossAmount {
|
||||
get => Utils.DecFromDb(GrossAmountValue, 2);
|
||||
}
|
||||
|
||||
[Column("modifiers")]
|
||||
public long? ModifiersValue { get; set; }
|
||||
[NotMapped]
|
||||
public decimal? Modifiers {
|
||||
get => ModifiersValue != null ? Utils.DecFromDb(ModifiersValue.Value, 2) : null;
|
||||
set => ModifiersValue = value != null ? Utils.DecToDb(value.Value, 2) : null;
|
||||
}
|
||||
|
||||
[Column("prev_modifiers")]
|
||||
public long? PrevModifiersValue { get; set; }
|
||||
[NotMapped]
|
||||
public decimal? PrevModifiers {
|
||||
get => PrevModifiersValue != null ? Utils.DecFromDb(PrevModifiersValue.Value, 2) : null;
|
||||
set => PrevModifiersValue = value != null ? Utils.DecToDb(value.Value, 2) : null;
|
||||
}
|
||||
|
||||
[Column("amount")]
|
||||
public long AmountValue { get; private set; }
|
||||
[NotMapped]
|
||||
public decimal Amount {
|
||||
get => Utils.DecFromDb(AmountValue, 2);
|
||||
}
|
||||
|
||||
[Column("ctime")]
|
||||
public long CTime { get; private set; }
|
||||
[NotMapped]
|
||||
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
|
||||
|
||||
[Column("mtime")]
|
||||
public long MTime { get; private set; }
|
||||
[NotMapped]
|
||||
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
|
||||
|
||||
[ForeignKey("Year, AvNr, MgNr")]
|
||||
public virtual PaymentMember Payment { get; private set; }
|
||||
|
||||
[ForeignKey("Year, AvNr")]
|
||||
public virtual PaymentVar Variant { get; private set; }
|
||||
|
||||
[ForeignKey("MgNr")]
|
||||
public virtual Member Member { get; private set; }
|
||||
}
|
||||
}
|
@@ -18,6 +18,6 @@ namespace Elwig.Models {
|
||||
public long? OneEuroValue { get; private set; }
|
||||
|
||||
[NotMapped]
|
||||
public decimal? OneEuro => OneEuroValue != null ? Utils.DecFromDb((long)OneEuroValue, 6) : null;
|
||||
public decimal? OneEuro => OneEuroValue != null ? Utils.DecFromDb(OneEuroValue.Value, 6) : null;
|
||||
}
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using IndexAttribute = Microsoft.EntityFrameworkCore.IndexAttribute;
|
||||
|
||||
namespace Elwig.Models {
|
||||
[Table("delivery"), PrimaryKey("Year", "DId"), Index("DateString", "ZwstId", "LNr", IsUnique = true), Index("LsNr", IsUnique = true)]
|
||||
@@ -19,12 +20,8 @@ namespace Elwig.Models {
|
||||
|
||||
[NotMapped]
|
||||
public DateOnly Date {
|
||||
get {
|
||||
return DateOnly.ParseExact(DateString, "yyyy-MM-dd");
|
||||
}
|
||||
set {
|
||||
DateString = value.ToString("yyyy-MM-dd");
|
||||
}
|
||||
get => DateOnly.ParseExact(DateString, "yyyy-MM-dd");
|
||||
set => DateString = value.ToString("yyyy-MM-dd");
|
||||
}
|
||||
|
||||
[Column("time")]
|
||||
@@ -32,19 +29,13 @@ namespace Elwig.Models {
|
||||
|
||||
[NotMapped]
|
||||
public TimeOnly? Time {
|
||||
get {
|
||||
return (TimeString == null) ? null : TimeOnly.ParseExact(TimeString, "HH:mm:ss");
|
||||
}
|
||||
set {
|
||||
TimeString = value?.ToString("HH:mm:ss");
|
||||
}
|
||||
get => (TimeString == null) ? null : TimeOnly.ParseExact(TimeString, "HH:mm:ss");
|
||||
set => TimeString = value?.ToString("HH:mm:ss");
|
||||
}
|
||||
|
||||
[NotMapped]
|
||||
public DateTime DateTime {
|
||||
get {
|
||||
return Date.ToDateTime(Time ?? TimeOnly.MinValue);
|
||||
}
|
||||
get => Date.ToDateTime(Time ?? TimeOnly.MinValue);
|
||||
set {
|
||||
Date = DateOnly.FromDateTime(value);
|
||||
Time = TimeOnly.FromDateTime(value);
|
||||
@@ -72,6 +63,9 @@ namespace Elwig.Models {
|
||||
[Column("comment")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
[ForeignKey("Year")]
|
||||
public virtual Season Season { get; private set; }
|
||||
|
||||
[InverseProperty("Delivery")]
|
||||
public virtual ISet<DeliveryPart> Parts { get; private set; }
|
||||
|
||||
@@ -84,13 +78,16 @@ namespace Elwig.Models {
|
||||
|
||||
public string SortIdString => string.Join(", ", SortIds);
|
||||
|
||||
public double Kmw => Utils.AggregateDeliveryPartsKmw(Parts);
|
||||
|
||||
public double Oe => Utils.KmwToOe(Kmw);
|
||||
|
||||
public int SearchScore(IEnumerable<string> keywords) {
|
||||
var list = new string?[] {
|
||||
LsNr, Year.ToString(), Date.ToString("dd.MM.yyyy"), Time?.ToString("HH:ss"),
|
||||
MgNr.ToString(), Member.FamilyName, Member.MiddleName, Member.GivenName, Member.BillingAddress?.Name,
|
||||
LsNr, Time?.ToString("HH:mm"),
|
||||
Member.FamilyName, Member.MiddleName, Member.GivenName, Member.BillingAddress?.Name,
|
||||
Comment
|
||||
}.ToList();
|
||||
list.AddRange(Parts.Select(p => p.SortId).Distinct());
|
||||
list.AddRange(Parts.Select(p => p.Comment).Distinct());
|
||||
return Utils.GetSearchScore(list, keywords);
|
||||
}
|
||||
|
@@ -30,15 +30,10 @@ namespace Elwig.Models {
|
||||
|
||||
[Column("kmw")]
|
||||
public double Kmw { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public double Oe {
|
||||
get {
|
||||
return Utils.KmwToOe(Kmw);
|
||||
}
|
||||
set {
|
||||
Kmw = Utils.OeToKmw(value);
|
||||
}
|
||||
get => Utils.KmwToOe(Kmw);
|
||||
set => Kmw = Utils.OeToKmw(value);
|
||||
}
|
||||
|
||||
[Column("qualid")]
|
||||
@@ -80,6 +75,9 @@ namespace Elwig.Models {
|
||||
[Column("lesewagen")]
|
||||
public bool? IsLesewagen { get; set; }
|
||||
|
||||
[Column("gebunden")]
|
||||
public bool? IsGebunden { get; set; }
|
||||
|
||||
[Column("temperature")]
|
||||
public double? Temperature { get; set; }
|
||||
|
||||
@@ -92,6 +90,9 @@ namespace Elwig.Models {
|
||||
[Column("weighing_id")]
|
||||
public string? WeighingId { get; set; }
|
||||
|
||||
[Column("weighing_reason")]
|
||||
public string? WeighingReason { get; set; }
|
||||
|
||||
[Column("comment")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
@@ -101,10 +102,22 @@ namespace Elwig.Models {
|
||||
[NotMapped]
|
||||
public IEnumerable<WineAttr> Attributes => PartAttributes.Select(a => a.Attr);
|
||||
|
||||
[NotMapped]
|
||||
public string AttributesString => string.Join(" / ", Attributes);
|
||||
|
||||
[InverseProperty("Part")]
|
||||
public virtual ISet<DeliveryPartModifier> PartModifiers { get; private set; }
|
||||
|
||||
[NotMapped]
|
||||
public IEnumerable<Modifier> Modifiers => PartModifiers.Select(m => m.Modifier);
|
||||
public IEnumerable<Modifier> Modifiers => PartModifiers.Select(m => m.Modifier).OrderBy(m => m.Ordering);
|
||||
|
||||
[InverseProperty("DeliveryPart")]
|
||||
public virtual PaymentDeliveryPart? Payment { get; private set; }
|
||||
|
||||
[NotMapped]
|
||||
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")]
|
||||
public virtual ISet<DeliveryPartBin> Bins { get; private set; }
|
||||
}
|
||||
}
|
||||
|
28
Elwig/Models/DeliveryPartBin.cs
Normal file
28
Elwig/Models/DeliveryPartBin.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Elwig.Models {
|
||||
[Table("delivery_part_bin"), PrimaryKey("Year", "DId", "DPNr", "BinNr")]
|
||||
public class DeliveryPartBin {
|
||||
[Column("year")]
|
||||
public int Year { get; set; }
|
||||
|
||||
[Column("did")]
|
||||
public int DId { get; set; }
|
||||
|
||||
[Column("dpnr")]
|
||||
public int DPNr { get; set; }
|
||||
|
||||
[Column("binnr")]
|
||||
public int BinNr { get; set; }
|
||||
|
||||
[Column("discr")]
|
||||
public string Discr { get; set; }
|
||||
|
||||
[Column("value")]
|
||||
public int Value { get; set; }
|
||||
|
||||
[ForeignKey("Year, DId, DPNr")]
|
||||
public virtual DeliveryPart Part { get; private set; }
|
||||
}
|
||||
}
|
@@ -7,7 +7,7 @@ using System.Linq;
|
||||
|
||||
namespace Elwig.Models {
|
||||
[Table("member"), PrimaryKey("MgNr")]
|
||||
public class Member {
|
||||
public class Member : IAddress {
|
||||
[Column("mgnr")]
|
||||
public int MgNr { get; set; }
|
||||
|
||||
@@ -95,8 +95,8 @@ namespace Elwig.Models {
|
||||
[Column("lfbis_nr")]
|
||||
public string? LfbisNr { get; set; }
|
||||
|
||||
[Column("ustid")]
|
||||
public string? UstId { get; set; }
|
||||
[Column("ustid_nr")]
|
||||
public string? UstIdNr { get; set; }
|
||||
|
||||
[Column("volllieferant")]
|
||||
public bool IsVollLieferant { get; set; }
|
||||
@@ -110,6 +110,9 @@ namespace Elwig.Models {
|
||||
[Column("active")]
|
||||
public bool IsActive { get; set; }
|
||||
|
||||
[Column("deceased")]
|
||||
public bool IsDeceased { get; set; }
|
||||
|
||||
[Column("iban")]
|
||||
public string? Iban { get; set; }
|
||||
|
||||
@@ -125,9 +128,6 @@ namespace Elwig.Models {
|
||||
[Column("address")]
|
||||
public string Address { get; set; }
|
||||
|
||||
[Column("email")]
|
||||
public string? Email { get; set; }
|
||||
|
||||
[Column("default_kgnr")]
|
||||
public int? DefaultKgNr { get; set; }
|
||||
|
||||
@@ -171,8 +171,14 @@ namespace Elwig.Models {
|
||||
[InverseProperty("Member")]
|
||||
public virtual ISet<MemberTelNr> TelephoneNumbers { get; private set; }
|
||||
|
||||
[InverseProperty("member")]
|
||||
public virtual ISet<MemberEmailAddr> EmailAddresses { get; private set; }
|
||||
|
||||
public string FullAddress => $"{Address}, {PostalDest.AtPlz.Plz} {PostalDest.AtPlz.Ort.Name}";
|
||||
|
||||
public int DeliveryRight => BusinessShares * App.Client.DeliveryRight;
|
||||
public int DeliveryObligation => BusinessShares * App.Client.DeliveryObligation;
|
||||
|
||||
public int SearchScore(IEnumerable<string> keywords) {
|
||||
return Utils.GetSearchScore(new string?[] {
|
||||
MgNr.ToString(),
|
||||
|
22
Elwig/Models/MemberEmailAddr.cs
Normal file
22
Elwig/Models/MemberEmailAddr.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Elwig.Models {
|
||||
[Table("member_email_address"), PrimaryKey("MgNr", "Nr")]
|
||||
public class MemberEmailAddr {
|
||||
[Column("mgnr")]
|
||||
public int MgNr { get; set; }
|
||||
|
||||
[Column("nr")]
|
||||
public int Nr { get; set; }
|
||||
|
||||
[Column("address")]
|
||||
public string Address { get; set; }
|
||||
|
||||
[Column("comment")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
[ForeignKey("MgNr")]
|
||||
public virtual Member Member { get; private set; }
|
||||
}
|
||||
}
|
@@ -1,6 +1,8 @@
|
||||
using Elwig.Helpers;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
|
||||
namespace Elwig.Models {
|
||||
[Table("modifier"), PrimaryKey("Year", "ModId")]
|
||||
@@ -11,6 +13,9 @@ namespace Elwig.Models {
|
||||
[Column("modid")]
|
||||
public string ModId { get; set; }
|
||||
|
||||
[Column("ordering")]
|
||||
public int Ordering { get; set; }
|
||||
|
||||
[Column("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
@@ -19,16 +24,18 @@ namespace Elwig.Models {
|
||||
|
||||
[NotMapped]
|
||||
public decimal? Abs {
|
||||
get {
|
||||
return AbsValue != null ? Season.DecFromDb((long)AbsValue) : null;
|
||||
}
|
||||
set {
|
||||
AbsValue = value != null ? Season.DecToDb((decimal)value) : null;
|
||||
}
|
||||
get => AbsValue != null ? Season.DecFromDb(AbsValue.Value) : null;
|
||||
set => AbsValue = value != null ? Season.DecToDb(value.Value) : null;
|
||||
}
|
||||
|
||||
[Column("rel")]
|
||||
public double? Rel { get; set; }
|
||||
public double? RelValue { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public decimal? Rel {
|
||||
get => (decimal?)RelValue;
|
||||
set => RelValue = (double?)value;
|
||||
}
|
||||
|
||||
[Column("standard")]
|
||||
public bool IsStandard { get; set; }
|
||||
@@ -39,9 +46,10 @@ namespace Elwig.Models {
|
||||
[ForeignKey("Year")]
|
||||
public virtual Season Season { get; private set; }
|
||||
|
||||
public string ValueStr => (Abs != null) ?
|
||||
$"{(Abs < 0 ? " -" : "+")}{Math.Abs(Abs.Value)} {Season.Currency.Symbol}/kg" :
|
||||
$"{(Rel < 0 ? " -" : "+")}{Math.Abs(Rel.Value):P2}";
|
||||
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" :
|
||||
(Rel != null) ? $"{Utils.GetSign(Rel.Value)}{(Math.Abs(Rel.Value) < 0.1m ? "\u2007" : "")}{Math.Abs(Rel.Value):0.00##\u00a0%}" :
|
||||
"";
|
||||
|
||||
public override string ToString() {
|
||||
return Name;
|
||||
|
49
Elwig/Models/PaymentDeliveryPart.cs
Normal file
49
Elwig/Models/PaymentDeliveryPart.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Elwig.Models {
|
||||
[Table("payment_delivery_part"), PrimaryKey("Year", "DId", "DPNr", "AvNr")]
|
||||
public class PaymentDeliveryPart {
|
||||
[Column("year")]
|
||||
public int Year { get; set; }
|
||||
|
||||
[Column("did")]
|
||||
public int DId { get; set; }
|
||||
|
||||
[Column("dpnr")]
|
||||
public int DPNr { get; set; }
|
||||
|
||||
[Column("avnr")]
|
||||
public int AvNr { get; set; }
|
||||
|
||||
[Column("mod_abs")]
|
||||
public long ModAbsValue { get; set; }
|
||||
[NotMapped]
|
||||
public decimal ModAbs {
|
||||
get => Variant.Season.DecFromDb(ModAbsValue);
|
||||
set => ModAbsValue = Variant.Season.DecToDb(value);
|
||||
}
|
||||
|
||||
[Column("mod_rel")]
|
||||
public double ModRelValue { get; set; }
|
||||
[NotMapped]
|
||||
public decimal ModRel {
|
||||
get => (decimal)ModRelValue;
|
||||
set => ModRelValue = (double)value;
|
||||
}
|
||||
|
||||
[Column("amount")]
|
||||
public long? AmountValue { get; set; }
|
||||
[NotMapped]
|
||||
public decimal? Amount {
|
||||
get => AmountValue != null ? Variant.Season.DecFromDb(AmountValue.Value) : null;
|
||||
set => AmountValue = value != null ? Variant.Season.DecToDb(value.Value) : null;
|
||||
}
|
||||
|
||||
[ForeignKey("Year, AvNr")]
|
||||
public virtual PaymentVar Variant { get; private set; }
|
||||
|
||||
[ForeignKey("Year, DId, DPNr")]
|
||||
public virtual DeliveryPart DeliveryPart { get; private set; }
|
||||
}
|
||||
}
|
44
Elwig/Models/PaymentDeliveryPartBin.cs
Normal file
44
Elwig/Models/PaymentDeliveryPartBin.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Elwig.Models {
|
||||
[Table("payment_delivery_part_bin"), PrimaryKey("Year", "DId", "DPNr", "BinNr", "AvNr")]
|
||||
public class PaymentDeliveryPartBin {
|
||||
[Column("year")]
|
||||
public int Year { get; set; }
|
||||
|
||||
[Column("did")]
|
||||
public int DId { get; set; }
|
||||
|
||||
[Column("dpnr")]
|
||||
public int DPNr { get; set; }
|
||||
|
||||
[Column("binnr")]
|
||||
public int BinNr { get; set; }
|
||||
|
||||
[Column("avnr")]
|
||||
public int AvNr { get; set; }
|
||||
|
||||
[Column("price")]
|
||||
public long PriceValue { get; set; }
|
||||
[NotMapped]
|
||||
public decimal Price {
|
||||
get => Variant.Season.DecFromDb(PriceValue);
|
||||
set => PriceValue = Variant.Season.DecToDb(value);
|
||||
}
|
||||
|
||||
[Column("amount")]
|
||||
public long AmountValue { get; set; }
|
||||
[NotMapped]
|
||||
public decimal Amount {
|
||||
get => Variant.Season.DecFromDb(AmountValue);
|
||||
set => AmountValue = Variant.Season.DecToDb(value);
|
||||
}
|
||||
|
||||
[ForeignKey("Year, AvNr")]
|
||||
public virtual PaymentVar Variant { get; private set; }
|
||||
|
||||
[ForeignKey("Year, DId, DPNr")]
|
||||
public virtual DeliveryPart DeliveryPart { get; private set; }
|
||||
}
|
||||
}
|
34
Elwig/Models/PaymentMember.cs
Normal file
34
Elwig/Models/PaymentMember.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Elwig.Models {
|
||||
[Table("payment_member"), PrimaryKey("Year", "AvNr", "MgNr")]
|
||||
public class PaymentMember {
|
||||
[Column("year")]
|
||||
public int Year { get; set; }
|
||||
|
||||
[Column("avnr")]
|
||||
public int AvNr { get; set; }
|
||||
|
||||
[Column("mgnr")]
|
||||
public int MgNr { get; set; }
|
||||
|
||||
[Column("amount")]
|
||||
public long AmountValue { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public decimal Amount {
|
||||
get => Variant.Season.DecFromDb(AmountValue);
|
||||
set => AmountValue = Variant.Season.DecToDb(value);
|
||||
}
|
||||
|
||||
[ForeignKey("Year, AvNr")]
|
||||
public virtual PaymentVar Variant { get; private set; }
|
||||
|
||||
[ForeignKey("MgNr")]
|
||||
public virtual Member Member { get; private set; }
|
||||
|
||||
[InverseProperty("Payment")]
|
||||
public virtual Credit? Credit { get; private set; }
|
||||
}
|
||||
}
|
58
Elwig/Models/PaymentVar.cs
Normal file
58
Elwig/Models/PaymentVar.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
|
||||
namespace Elwig.Models {
|
||||
[Table("payment_variant"), PrimaryKey("Year", "AvNr")]
|
||||
public class PaymentVar {
|
||||
[Column("year")]
|
||||
public int Year { get; set; }
|
||||
|
||||
[Column("avnr")]
|
||||
public int AvNr { get; set; }
|
||||
|
||||
[Column("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Column("date")]
|
||||
public string DateString { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public DateOnly Date {
|
||||
get => DateOnly.ParseExact(DateString, "yyyy-MM-dd");
|
||||
set => DateString = value.ToString("yyyy-MM-dd");
|
||||
}
|
||||
|
||||
[Column("transfer_date")]
|
||||
public string? TransferDateString { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public DateOnly? TransferDate {
|
||||
get => TransferDateString != null ? DateOnly.ParseExact(TransferDateString, "yyyy-MM-dd") : null;
|
||||
set => TransferDateString = value?.ToString("yyyy-MM-dd");
|
||||
}
|
||||
|
||||
[Column("test_variant")]
|
||||
public bool TestVariant { get; set; }
|
||||
|
||||
[Column("calc_time")]
|
||||
public int? CalcTime { get; set; }
|
||||
|
||||
[Column("comment")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
[Column("data")]
|
||||
public string Data { get; set; }
|
||||
|
||||
[ForeignKey("Year")]
|
||||
public virtual Season Season { get; private set; }
|
||||
|
||||
[InverseProperty("Variant")]
|
||||
public virtual ISet<PaymentMember> MemberPayments { get; private set; }
|
||||
|
||||
[InverseProperty("Variant")]
|
||||
public virtual ISet<Credit> Credits { get; private set; }
|
||||
}
|
||||
}
|
@@ -48,6 +48,12 @@ namespace Elwig.Models {
|
||||
[InverseProperty("Season")]
|
||||
public virtual ISet<Modifier> Modifiers { get; private set; }
|
||||
|
||||
[InverseProperty("Season")]
|
||||
public virtual ISet<PaymentVar> PaymentVariants { get; private set; }
|
||||
|
||||
[InverseProperty("Season")]
|
||||
public virtual ISet<Delivery> Deliveries { get; private set; }
|
||||
|
||||
public decimal DecFromDb(long value) {
|
||||
return Utils.DecFromDb(value, Precision);
|
||||
}
|
||||
|
@@ -13,7 +13,13 @@ namespace Elwig.Models {
|
||||
[Column("max_kg_per_ha")]
|
||||
public int? MaxKgPerHa { get; set; }
|
||||
|
||||
[Column("fill_lower_bins")]
|
||||
public int FillLowerBins { get; set; }
|
||||
|
||||
[Column("active")]
|
||||
public bool IsActive { get; set; }
|
||||
public override string ToString() {
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using IndexAttribute = Microsoft.EntityFrameworkCore.IndexAttribute;
|
||||
|
||||
namespace Elwig.Models {
|
||||
[Table("wine_origin"), PrimaryKey("HkId"), Index("Name", IsUnique = true)]
|
||||
@@ -40,5 +41,7 @@ namespace Elwig.Models {
|
||||
public override string ToString() {
|
||||
return Name;
|
||||
}
|
||||
|
||||
public string OriginString => (Parent != null ? $"{Parent.OriginString} / " : "") + Name;
|
||||
}
|
||||
}
|
||||
|
@@ -18,6 +18,9 @@ namespace Elwig.Models {
|
||||
|
||||
public string CommentFormat => (Comment != null) ? $" ({Comment})" : "";
|
||||
|
||||
public bool IsRed => Type == "R";
|
||||
public bool IsWhite => Type == "W";
|
||||
|
||||
public override string ToString() {
|
||||
return Name;
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
-->
|
||||
|
@@ -16,22 +16,23 @@ namespace Elwig.Windows {
|
||||
protected Control[] ExemptInputs { private get; set; }
|
||||
protected Control[] RequiredInputs { private get; set; }
|
||||
|
||||
private bool _IsEditing;
|
||||
private bool _IsCreating;
|
||||
private bool _isEditing;
|
||||
private bool _isCreating;
|
||||
protected bool IsEditing {
|
||||
get { return _IsEditing; }
|
||||
get { return _isEditing; }
|
||||
set {
|
||||
_IsEditing = value;
|
||||
LockContext = IsEditing;
|
||||
_isEditing = value;
|
||||
LockContext = IsEditing || IsCreating;
|
||||
}
|
||||
}
|
||||
protected bool IsCreating {
|
||||
get { return _IsCreating; }
|
||||
get { return _isCreating; }
|
||||
set {
|
||||
_IsCreating = value;
|
||||
LockContext = IsEditing;
|
||||
_isCreating = value;
|
||||
LockContext = IsEditing || IsCreating;
|
||||
}
|
||||
}
|
||||
protected bool DoShowWarningWindows = true;
|
||||
protected bool IsClosing { get; private set; }
|
||||
|
||||
private TextBox[] TextBoxInputs;
|
||||
@@ -43,6 +44,7 @@ namespace Elwig.Windows {
|
||||
private RadioButton[] RadioButtonInputs;
|
||||
private readonly Dictionary<Control, bool> Valid;
|
||||
private readonly Dictionary<Control, object?> OriginalValues;
|
||||
private readonly Dictionary<Control, object?> DefaultValues;
|
||||
|
||||
public AdministrationWindow() : base() {
|
||||
IsEditing = false;
|
||||
@@ -58,28 +60,36 @@ namespace Elwig.Windows {
|
||||
RadioButtonInputs = Array.Empty<RadioButton>();
|
||||
Valid = new();
|
||||
OriginalValues = new();
|
||||
DefaultValues = new();
|
||||
Closing += OnClosing;
|
||||
Loaded += OnLoaded;
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs evt) {
|
||||
TextBoxInputs = Utils.FindAllChildren<TextBox>(this, ExemptInputs).ToArray();
|
||||
ComboBoxInputs = Utils.FindAllChildren<ComboBox>(this, ExemptInputs).ToArray();
|
||||
CheckBoxInputs = Utils.FindAllChildren<CheckBox>(this, ExemptInputs).ToArray();
|
||||
CheckComboBoxInputs = Utils.FindAllChildren<CheckComboBox>(this, ExemptInputs).ToArray();
|
||||
RadioButtonInputs = Utils.FindAllChildren<RadioButton>(this, ExemptInputs).ToArray();
|
||||
TextBoxInputs = ControlUtils.FindAllChildren<TextBox>(this, ExemptInputs).ToArray();
|
||||
ComboBoxInputs = ControlUtils.FindAllChildren<ComboBox>(this, ExemptInputs).ToArray();
|
||||
CheckBoxInputs = ControlUtils.FindAllChildren<CheckBox>(this, ExemptInputs).ToArray();
|
||||
CheckComboBoxInputs = ControlUtils.FindAllChildren<CheckComboBox>(this, ExemptInputs).ToArray();
|
||||
RadioButtonInputs = ControlUtils.FindAllChildren<RadioButton>(this, ExemptInputs).ToArray();
|
||||
PlzInputs = TextBoxInputs.Where(tb => "PLZ".Equals(tb.Tag)).ToArray();
|
||||
PlzOrtInputs = PlzInputs.Select(tb => Utils.FindNextSibling<ComboBox>(tb) ?? throw new MissingMemberException()).ToArray();
|
||||
PlzOrtInputs = PlzInputs.Select(tb => ControlUtils.FindNextSibling<ComboBox>(tb) ?? throw new MissingMemberException()).ToArray();
|
||||
foreach (var tb in TextBoxInputs)
|
||||
Valid[tb] = true;
|
||||
foreach (var cb in ComboBoxInputs)
|
||||
cb.SelectionChanged += ComboBox_SelectionChanged;
|
||||
foreach (var cb in CheckComboBoxInputs)
|
||||
cb.ItemSelectionChanged += ComboBox_SelectionChanged;
|
||||
ValidateRequiredInputs();
|
||||
}
|
||||
|
||||
private void OnClosing(object? sender, CancelEventArgs evt) {
|
||||
if ((IsCreating || IsEditing) && HasChanged) {
|
||||
var r = System.Windows.MessageBox.Show("Soll das Fenster wirklich geschlossen werden?", "Schlie<69>en best<73>tigen",
|
||||
MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
|
||||
if (r != MessageBoxResult.Yes) {
|
||||
evt.Cancel = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
IsClosing = true;
|
||||
}
|
||||
|
||||
@@ -89,7 +99,7 @@ namespace Elwig.Windows {
|
||||
|
||||
abstract protected void UpdateButtons();
|
||||
|
||||
protected override async Task RenewContext() {
|
||||
protected override async Task OnRenewContext() {
|
||||
for (int i = 0; i < PlzInputs.Length; i++)
|
||||
UpdatePlz(PlzInputs[i], PlzOrtInputs[i]);
|
||||
}
|
||||
@@ -102,29 +112,51 @@ namespace Elwig.Windows {
|
||||
return (sender is Control c) && RequiredInputs.Contains(c);
|
||||
}
|
||||
|
||||
protected void FinishInputFilling() {
|
||||
FillOriginalValues();
|
||||
ValidateDefaultValues();
|
||||
ValidateRequiredInputs();
|
||||
}
|
||||
|
||||
protected void ClearInputStates() {
|
||||
foreach (var tb in TextBoxInputs)
|
||||
Utils.ClearInputState(tb);
|
||||
ControlUtils.ClearInputState(tb);
|
||||
foreach (var cb in ComboBoxInputs)
|
||||
Utils.ClearInputState(cb);
|
||||
// TODO ComboCheckBox
|
||||
ControlUtils.ClearInputState(cb);
|
||||
foreach (var ccb in CheckComboBoxInputs)
|
||||
ControlUtils.ClearInputState(ccb);
|
||||
foreach (var cb in CheckBoxInputs)
|
||||
Utils.ClearInputState(cb);
|
||||
ControlUtils.ClearInputState(cb);
|
||||
foreach (var rb in RadioButtonInputs)
|
||||
Utils.ClearInputState(rb);
|
||||
ControlUtils.ClearInputState(rb);
|
||||
}
|
||||
|
||||
protected void ValidateRequiredInputs() {
|
||||
foreach (var input in RequiredInputs) {
|
||||
if (input is TextBox tb && tb.Text.Length == 0) {
|
||||
Utils.SetInputInvalid(input);
|
||||
ControlUtils.SetInputInvalid(input);
|
||||
Valid[input] = false;
|
||||
} else if (input is ComboBox cb && cb.SelectedItem == null && cb.ItemsSource != null) {
|
||||
Utils.SetInputInvalid(input);
|
||||
ControlUtils.SetInputInvalid(input);
|
||||
} else if (input is CheckComboBox ccb && ccb.SelectedItem == null && ccb.ItemsSource != null) {
|
||||
ControlUtils.SetInputInvalid(input);
|
||||
} else if (input is CheckBox ckb && ckb.IsChecked != true) {
|
||||
ControlUtils.SetInputInvalid(input);
|
||||
Valid[input] = false;
|
||||
} else if (input is RadioButton rb && rb.IsChecked != true) {
|
||||
ControlUtils.SetInputInvalid(input);
|
||||
Valid[input] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void ValidateDefaultValues() {
|
||||
foreach (var input in DefaultValues.Keys) {
|
||||
if (InputIsNotDefault(input))
|
||||
ControlUtils.SetInputNotDefault(input);
|
||||
}
|
||||
}
|
||||
|
||||
protected void LockInputs() {
|
||||
foreach (var tb in TextBoxInputs)
|
||||
tb.IsReadOnly = true;
|
||||
@@ -155,36 +187,75 @@ namespace Elwig.Windows {
|
||||
OriginalValues.Clear();
|
||||
}
|
||||
|
||||
protected void ClearDefaultValues() {
|
||||
DefaultValues.Clear();
|
||||
}
|
||||
|
||||
protected void FillOriginalValues() {
|
||||
foreach (var tb in TextBoxInputs)
|
||||
OriginalValues[tb] = tb.Text;
|
||||
foreach (var cb in ComboBoxInputs)
|
||||
OriginalValues[cb] = cb.SelectedItem;
|
||||
// TODO ComboCheckBox
|
||||
foreach (var ccb in CheckComboBoxInputs)
|
||||
OriginalValues[ccb] = ccb.SelectedItems.Cast<object>().ToArray();
|
||||
foreach (var cb in CheckBoxInputs)
|
||||
OriginalValues[cb] = (cb.IsChecked ?? false) ? bool.TrueString : null;
|
||||
OriginalValues[cb] = cb.IsChecked?.ToString();
|
||||
foreach (var rb in RadioButtonInputs)
|
||||
OriginalValues[rb] = (rb.IsChecked ?? false) ? bool.TrueString : null;
|
||||
OriginalValues[rb] = rb.IsChecked?.ToString();
|
||||
}
|
||||
|
||||
protected void SetOriginalValue(Control input, object? value) {
|
||||
OriginalValues[input] = value;
|
||||
OriginalValues[input] = value is bool b ? b.ToString() : value;
|
||||
if (InputHasChanged(input)) {
|
||||
ControlUtils.SetInputChanged(input);
|
||||
} else {
|
||||
ControlUtils.ClearInputState(input);
|
||||
}
|
||||
}
|
||||
|
||||
protected void SetOriginalValue(Control input) {
|
||||
SetOriginalValue(input, ControlUtils.GetInputValue(input));
|
||||
}
|
||||
|
||||
protected void UnsetOriginalValue(Control input) {
|
||||
OriginalValues.Remove(input);
|
||||
ControlUtils.ClearInputState(input);
|
||||
}
|
||||
|
||||
protected void ClearInputs() {
|
||||
protected void SetDefaultValue(Control input, object? value) {
|
||||
DefaultValues[input] = value is bool b ? b.ToString() : value;
|
||||
if (!InputHasChanged(input)) {
|
||||
if (InputIsNotDefault(input)) {
|
||||
ControlUtils.SetInputNotDefault(input);
|
||||
} else {
|
||||
ControlUtils.ClearInputState(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void SetDefaultValue(Control input) {
|
||||
SetDefaultValue(input, ControlUtils.GetInputValue(input));
|
||||
}
|
||||
|
||||
protected void UnsetDefaultValue(Control input) {
|
||||
DefaultValues.Remove(input);
|
||||
if (!InputHasChanged(input)) {
|
||||
ControlUtils.ClearInputState(input);
|
||||
}
|
||||
}
|
||||
|
||||
protected void ClearInputs(bool validate = true) {
|
||||
foreach (var tb in TextBoxInputs)
|
||||
tb.Text = "";
|
||||
foreach (var cb in ComboBoxInputs)
|
||||
cb.SelectedItem = null;
|
||||
foreach (var ccb in CheckComboBoxInputs)
|
||||
ccb.SelectedItems.Clear();
|
||||
foreach (var cb in CheckBoxInputs)
|
||||
cb.IsChecked = false;
|
||||
foreach (var rb in RadioButtonInputs)
|
||||
rb.IsChecked = false;
|
||||
ValidateRequiredInputs();
|
||||
if (validate) ValidateRequiredInputs();
|
||||
}
|
||||
|
||||
protected bool IsValid => Valid.All(kv => kv.Value);
|
||||
@@ -200,27 +271,56 @@ namespace Elwig.Windows {
|
||||
return OriginalValues[tb]?.ToString() != tb.Text;
|
||||
} else if (input is ComboBox sb) {
|
||||
return OriginalValues[sb] != sb.SelectedItem;
|
||||
} else if (input is CheckComboBox ccb) {
|
||||
return !ccb.SelectedItems.Cast<object>().ToArray().SequenceEqual(((object[]?)OriginalValues[ccb]) ?? Array.Empty<object>());
|
||||
} else if (input is CheckBox cb) {
|
||||
return (OriginalValues[cb] != null) != (cb.IsChecked ?? false);
|
||||
return (string?)OriginalValues[cb] != cb.IsChecked?.ToString();
|
||||
} else if (input is RadioButton rb) {
|
||||
return (OriginalValues[rb] != null) != (rb.IsChecked ?? false);
|
||||
return (string?)OriginalValues[rb] != rb.IsChecked?.ToString();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool InputIsNotDefault(Control input) {
|
||||
if (!DefaultValues.ContainsKey(input)) {
|
||||
return false;
|
||||
} else if (input is TextBox tb) {
|
||||
return DefaultValues[tb]?.ToString() != tb.Text;
|
||||
} else if (input is ComboBox sb) {
|
||||
return DefaultValues[sb] != sb.SelectedItem;
|
||||
} else if (input is CheckComboBox ccb) {
|
||||
return !ccb.SelectedItems.Cast<object>().ToArray().SequenceEqual(((object[]?)DefaultValues[ccb]) ?? Array.Empty<object>());
|
||||
} else if (input is CheckBox cb) {
|
||||
return (string?)DefaultValues[cb] != cb.IsChecked?.ToString();
|
||||
} else if (input is RadioButton rb) {
|
||||
return (string?)DefaultValues[rb] != rb.IsChecked?.ToString();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool HasChanged =>
|
||||
!IsValid ||
|
||||
TextBoxInputs.Any(InputHasChanged) ||
|
||||
ComboBoxInputs.Any(InputHasChanged) ||
|
||||
CheckBoxInputs.Any(InputHasChanged) ||
|
||||
RadioButtonInputs.Any(InputHasChanged);
|
||||
IsEditing && (
|
||||
!IsValid ||
|
||||
TextBoxInputs.Any(InputHasChanged) ||
|
||||
ComboBoxInputs.Any(InputHasChanged) ||
|
||||
CheckComboBoxInputs.Any(InputHasChanged) ||
|
||||
CheckBoxInputs.Any(InputHasChanged) ||
|
||||
RadioButtonInputs.Any(InputHasChanged)
|
||||
) || IsCreating && (
|
||||
TextBoxInputs.Any(i => InputIsNotDefault(i) || (!i.IsReadOnly && i.Text != "")) ||
|
||||
ComboBoxInputs.Any(i => InputIsNotDefault(i) || (i.IsEnabled && i.SelectedItem != null)) ||
|
||||
CheckComboBoxInputs.Any(i => InputIsNotDefault(i) || i.SelectedItem != null) ||
|
||||
CheckBoxInputs.Any(InputIsNotDefault) ||
|
||||
RadioButtonInputs.Any(InputIsNotDefault)
|
||||
);
|
||||
|
||||
protected void UpdatePlz(TextBox plzInput, ComboBox ortInput) {
|
||||
var plzInputValid = GetInputValid(plzInput);
|
||||
var item = ortInput.SelectedItem;
|
||||
var list = plzInputValid && plzInput.Text.Length == 4 ? Context.Postleitzahlen.Find(int.Parse(plzInput.Text))?.Orte.ToList() : null;
|
||||
Utils.RenewItemsSource(ortInput, list, i => (i as AT_PlzDest)?.Id);
|
||||
ControlUtils.RenewItemsSource(ortInput, list, i => (i as AT_PlzDest)?.Id);
|
||||
if (list != null && ortInput.SelectedItem == null && list.Count == 1)
|
||||
ortInput.SelectedItem = list[0];
|
||||
UpdateComboBox(ortInput);
|
||||
@@ -242,6 +342,10 @@ namespace Elwig.Windows {
|
||||
};
|
||||
}
|
||||
|
||||
protected bool InputTextChanged(TextBox input) {
|
||||
return InputTextChanged(input, new ValidationResult(true, null));
|
||||
}
|
||||
|
||||
protected bool InputTextChanged(TextBox input, Func<TextBox, bool, ValidationResult> checker) {
|
||||
return InputTextChanged(input, (tb, required, ctx) => checker(tb, required));
|
||||
}
|
||||
@@ -254,12 +358,14 @@ namespace Elwig.Windows {
|
||||
ValidateInput(input, res.IsValid);
|
||||
if (res.IsValid) {
|
||||
if (InputHasChanged(input)) {
|
||||
Utils.SetInputChanged(input);
|
||||
ControlUtils.SetInputChanged(input);
|
||||
} else if (InputIsNotDefault(input)) {
|
||||
ControlUtils.SetInputNotDefault(input);
|
||||
} else {
|
||||
Utils.ClearInputState(input);
|
||||
ControlUtils.ClearInputState(input);
|
||||
}
|
||||
} else {
|
||||
Utils.SetInputInvalid(input);
|
||||
ControlUtils.SetInputInvalid(input);
|
||||
}
|
||||
UpdateButtons();
|
||||
return res.IsValid;
|
||||
@@ -274,7 +380,7 @@ namespace Elwig.Windows {
|
||||
}
|
||||
|
||||
protected bool InputLostFocus(TextBox input, ValidationResult res, string? msg = null) {
|
||||
if (!res.IsValid && !IsClosing)
|
||||
if (DoShowWarningWindows && !res.IsValid && !IsClosing && (IsEditing || IsCreating))
|
||||
System.Windows.MessageBox.Show(res.ErrorContent.ToString(), msg ?? res.ErrorContent.ToString(), MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return res.IsValid;
|
||||
}
|
||||
@@ -282,11 +388,13 @@ namespace Elwig.Windows {
|
||||
protected void CheckBox_Changed(object sender, RoutedEventArgs evt) {
|
||||
var input = (CheckBox)sender;
|
||||
if (SenderIsRequired(input) && input.IsChecked != true) {
|
||||
Utils.SetInputInvalid(input);
|
||||
ControlUtils.SetInputInvalid(input);
|
||||
} else if (InputHasChanged(input)) {
|
||||
Utils.SetInputChanged(input);
|
||||
ControlUtils.SetInputChanged(input);
|
||||
} else if (InputIsNotDefault(input)) {
|
||||
ControlUtils.SetInputNotDefault(input);
|
||||
} else {
|
||||
Utils.ClearInputState(input);
|
||||
ControlUtils.ClearInputState(input);
|
||||
}
|
||||
UpdateButtons();
|
||||
}
|
||||
@@ -294,26 +402,30 @@ namespace Elwig.Windows {
|
||||
protected void RadioButton_Changed(object sender, RoutedEventArgs evt) {
|
||||
var input = (RadioButton)sender;
|
||||
if (SenderIsRequired(input) && input.IsChecked != true) {
|
||||
Utils.SetInputInvalid(input);
|
||||
ControlUtils.SetInputInvalid(input);
|
||||
} else if (InputHasChanged(input)) {
|
||||
Utils.SetInputChanged(input);
|
||||
ControlUtils.SetInputChanged(input);
|
||||
} else if (InputIsNotDefault(input)) {
|
||||
ControlUtils.SetInputNotDefault(input);
|
||||
} else {
|
||||
Utils.ClearInputState(input);
|
||||
ControlUtils.ClearInputState(input);
|
||||
}
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
protected void TextBox_TextChanged(object sender, RoutedEventArgs evt) {
|
||||
protected void TextBox_TextChanged(object sender, RoutedEventArgs? evt) {
|
||||
var input = (TextBox)sender;
|
||||
if (SenderIsRequired(input) && input.Text.Length == 0) {
|
||||
ValidateInput(input, false);
|
||||
Utils.SetInputInvalid(input);
|
||||
ControlUtils.SetInputInvalid(input);
|
||||
} else {
|
||||
ValidateInput(input, true);
|
||||
if (InputHasChanged(input)) {
|
||||
Utils.SetInputChanged(input);
|
||||
ControlUtils.SetInputChanged(input);
|
||||
} else if (InputIsNotDefault(input)) {
|
||||
ControlUtils.SetInputNotDefault(input);
|
||||
} else {
|
||||
Utils.ClearInputState(input);
|
||||
ControlUtils.ClearInputState(input);
|
||||
}
|
||||
}
|
||||
UpdateButtons();
|
||||
@@ -329,13 +441,15 @@ namespace Elwig.Windows {
|
||||
if (valid) {
|
||||
ValidateInput(input, true);
|
||||
if (InputHasChanged(input)) {
|
||||
Utils.SetInputChanged(input);
|
||||
ControlUtils.SetInputChanged(input);
|
||||
} else if (InputIsNotDefault(input)) {
|
||||
ControlUtils.SetInputNotDefault(input);
|
||||
} else {
|
||||
Utils.ClearInputState(input);
|
||||
ControlUtils.ClearInputState(input);
|
||||
}
|
||||
} else {
|
||||
ValidateInput(input, false);
|
||||
Utils.SetInputInvalid(input);
|
||||
ControlUtils.SetInputInvalid(input);
|
||||
}
|
||||
UpdateButtons();
|
||||
}
|
||||
@@ -348,6 +462,10 @@ namespace Elwig.Windows {
|
||||
InputTextChanged((TextBox)sender, Validator.CheckInteger);
|
||||
}
|
||||
|
||||
protected void DecimalInput_TextChanged(object sender, RoutedEventArgs evt) {
|
||||
InputTextChanged((TextBox)sender, Validator.CheckDecimal);
|
||||
}
|
||||
|
||||
protected void PartialDateInput_TextChanged(object sender, RoutedEventArgs evt) {
|
||||
InputTextChanged((TextBox)sender, Validator.CheckPartialDate);
|
||||
}
|
||||
@@ -364,23 +482,33 @@ namespace Elwig.Windows {
|
||||
InputLostFocus((TextBox)sender, Validator.CheckDate);
|
||||
}
|
||||
|
||||
protected void TimeInput_TextChanged(object sender, RoutedEventArgs evt) {
|
||||
InputTextChanged((TextBox)sender, Validator.CheckTime);
|
||||
}
|
||||
|
||||
protected void TimeInput_LostFocus(object sender, RoutedEventArgs evt) {
|
||||
InputLostFocus((TextBox)sender, Validator.CheckTime);
|
||||
}
|
||||
|
||||
protected void PlzInput_TextChanged(object sender, RoutedEventArgs evt) {
|
||||
var plz = (TextBox)sender;
|
||||
InputTextChanged(plz, Validator.CheckPlz);
|
||||
UpdatePlz(plz, GetPlzOrtInput(plz));
|
||||
if ("PLZ".Equals(plz.Tag))
|
||||
UpdatePlz(plz, GetPlzOrtInput(plz));
|
||||
}
|
||||
|
||||
protected void PlzInput_LostFocus(object sender, RoutedEventArgs evt) {
|
||||
var plz = (TextBox)sender;
|
||||
InputLostFocus(plz, Validator.CheckPlz);
|
||||
UpdatePlz(plz, GetPlzOrtInput(plz));
|
||||
if ("PLZ".Equals(plz.Tag))
|
||||
UpdatePlz(plz, GetPlzOrtInput(plz));
|
||||
}
|
||||
|
||||
protected void EmailInput_TextChanged(object sender, RoutedEventArgs evt) {
|
||||
protected void EmailAddressInput_TextChanged(object sender, RoutedEventArgs evt) {
|
||||
InputTextChanged((TextBox)sender, Validator.CheckEmailAddress);
|
||||
}
|
||||
|
||||
protected void EmailInput_LostFocus(object sender, RoutedEventArgs evt) {
|
||||
protected void EmailAddressInput_LostFocus(object sender, RoutedEventArgs evt) {
|
||||
InputLostFocus((TextBox)sender, Validator.CheckEmailAddress);
|
||||
}
|
||||
|
||||
@@ -408,12 +536,12 @@ namespace Elwig.Windows {
|
||||
InputLostFocus((TextBox)sender, Validator.CheckBic);
|
||||
}
|
||||
|
||||
protected void UstIdInput_TextChanged(object sender, RoutedEventArgs evt) {
|
||||
InputTextChanged((TextBox)sender, Validator.CheckUstId);
|
||||
protected void UstIdNrInput_TextChanged(object sender, RoutedEventArgs evt) {
|
||||
InputTextChanged((TextBox)sender, Validator.CheckUstIdNr);
|
||||
}
|
||||
|
||||
protected void UstIdInput_LostFocus(object sender, RoutedEventArgs evt) {
|
||||
InputLostFocus((TextBox)sender, Validator.CheckUstId);
|
||||
protected void UstIdNrInput_LostFocus(object sender, RoutedEventArgs evt) {
|
||||
InputLostFocus((TextBox)sender, Validator.CheckUstIdNr);
|
||||
}
|
||||
|
||||
protected void LfbisNrInput_TextChanged(object sender, RoutedEventArgs evt) {
|
||||
|
@@ -5,7 +5,8 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Elwig.Windows"
|
||||
mc:Ignorable="d"
|
||||
Title="Flächenbindugen - Elwig" Height="500" Width="950"
|
||||
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
|
||||
Title="Flächenbindungen - Elwig" Height="480" Width="850"
|
||||
Loaded="Window_Loaded">
|
||||
<Window.Resources>
|
||||
<Style TargetType="Label">
|
||||
@@ -30,6 +31,13 @@
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||
</Style>
|
||||
<Style TargetType="xctk:CheckComboBox">
|
||||
<Setter Property="IsEnabled" Value="False"/>
|
||||
<Setter Property="Height" Value="25"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||
</Style>
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="Padding" Value="9,3"/>
|
||||
@@ -39,153 +47,120 @@
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="19"/>
|
||||
<RowDefinition Height="120"/>
|
||||
<RowDefinition Height="190"/>
|
||||
<RowDefinition Height="1*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="620"/>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="330"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Menu Grid.ColumnSpan="3" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
|
||||
<Menu Grid.ColumnSpan="2" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
|
||||
<MenuItem Header="Flächenbindugen"/>
|
||||
</Menu>
|
||||
|
||||
<Grid Grid.RowSpan="4" Grid.Row="1">
|
||||
<Grid Grid.RowSpan="2" Grid.Row="1" Grid.Column="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="125"/>
|
||||
<ColumnDefinition Width="120"/>
|
||||
<ColumnDefinition Width="125"/>
|
||||
<ColumnDefinition Width="125"/>
|
||||
<ColumnDefinition Width="125"/>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<DataGrid x:Name="ContractList" AutoGenerateColumns="False" HeadersVisibility="Column" IsReadOnly="True" GridLinesVisibility="None" SelectionMode="Single"
|
||||
CanUserDeleteRows="False" CanUserResizeRows="False" CanUserAddRows="False" SelectionChanged="ContractList_SelectionChanged" Grid.Column="0" Grid.Row="1"
|
||||
Margin="10,10,5,47" FontSize="14" Grid.ColumnSpan="2" Background="#e2e2e2">
|
||||
<DataGrid x:Name="AreaCommitmentList" AutoGenerateColumns="False" HeadersVisibility="Column" IsReadOnly="True" GridLinesVisibility="None" SelectionMode="Single"
|
||||
CanUserDeleteRows="False" CanUserResizeRows="False" CanUserAddRows="False" SelectionChanged="AreaCommitmentList_SelectionChanged" Grid.Column="0" Grid.Row="1"
|
||||
Margin="5,10,5,42" FontSize="14" Grid.ColumnSpan="3" Background="#e2e2e2">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="VNr." Binding="{Binding VNr}" Width="*"/>
|
||||
<DataGridTextColumn Header="Von" Binding="{Binding YearFrom}" Width="*"/>
|
||||
<DataGridTextColumn Header="Bis" Binding="{Binding YearTo}" Width="*"/>
|
||||
<DataGridTextColumn Header="FbNr." Binding="{Binding FbNr}" Width="2*"/>
|
||||
<DataGridTextColumn Header="Katastralgemeinde" Binding="{Binding Kg.AtKg.Name}" Width="6*"/>
|
||||
<DataGridTextColumn Header="Ried" Binding="{Binding Rd.Name}" Width="4*"/>
|
||||
<DataGridTextColumn Header="Parzelle" Binding="{Binding GstNr}" Width="4*"/>
|
||||
<DataGridTextColumn Header="Fläche" Binding="{Binding Area, StringFormat='{}{0:N0} m²'}" Width="4*">
|
||||
<DataGridTextColumn.CellStyle>
|
||||
<Style>
|
||||
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
|
||||
</Style>
|
||||
</DataGridTextColumn.CellStyle>
|
||||
</DataGridTextColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
|
||||
<Button x:Name="NewContractButton" Content="Neu" Click="NewContractButton_Click"
|
||||
HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="10,10,0,10" Width="110" Grid.Column="0"/>
|
||||
<Button x:Name="DeleteContractButton" Content="Löschen" Click="DeleteContractButton_Click" IsEnabled="False"
|
||||
HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="5,10,0,10" Width="110" Grid.Column="1"/>
|
||||
|
||||
<Button x:Name="ContractSaveButton" Content="Speichern" Click="ContractSaveButton_Click" IsEnabled="False" Visibility="Hidden"
|
||||
HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="10,10,0,10" Width="110" Grid.Column="0"/>
|
||||
<Button x:Name="ContractCancelButton" Content="Abbrechen" Click="ContractCancelButton_Click" IsEnabled="False" Visibility="Hidden" IsCancel="True"
|
||||
HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="5,10,0,10" Width="110" Grid.Column="1"/>
|
||||
|
||||
|
||||
|
||||
<Button x:Name="NewAreaCommitmentButton" Content="Neu" Click="NewAreaCommitmentButton_Click" IsEnabled="False"
|
||||
HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="5,10,0,10" Width="110" Grid.Column="2"/>
|
||||
<Button x:Name="NewAreaCommitmentButton" Content="Neu" Click="NewAreaCommitmentButton_Click"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0"/>
|
||||
<Button x:Name="EditAreaCommitmentButton" Content="Bearbeiten" Click="EditAreaCommitmentButton_Click" IsEnabled="False"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="-5,10,0,10" Width="110" Grid.Column="3"/>
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1"/>
|
||||
<Button x:Name="DeleteAreaCommitmentButton" Content="Löschen" Click="DeleteAreaCommitmentButton_Click" IsEnabled="False"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,10,10,10" Width="110" Grid.Column="4"/>
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2"/>
|
||||
|
||||
<Button x:Name="AreaCommitmentSaveButton" Content="Speichern" Click="AreaCommitmentSaveButton_Click" IsEnabled="False" Visibility="Hidden"
|
||||
HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="10,10,0,10" Width="110" Grid.Column="2"/>
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0"/>
|
||||
<Button x:Name="AreaCommitmentResetButton" Content="Zurücksetzen" Click="AreaCommitmentResetButton_Click" IsEnabled="False" Visibility="Hidden"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="-5,10,0,10" Width="110" Grid.Column="3"/>
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1"/>
|
||||
<Button x:Name="AreaCommitmentCancelButton" Content="Abbrechen" Click="AreaCommitmentCancelButton_Click" IsEnabled="False" Visibility="Hidden" IsCancel="True"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,10,10,10" Width="110" Grid.Column="4"/>
|
||||
|
||||
<DataGrid x:Name="AreaCommitmentList" AutoGenerateColumns="False" HeadersVisibility="Column" IsReadOnly="True" GridLinesVisibility="None" SelectionMode="Single"
|
||||
CanUserDeleteRows="False" CanUserResizeRows="False" CanUserAddRows="False" SelectionChanged="AreaCommitmentList_SelectionChanged" Grid.Column="2" Grid.Row="1"
|
||||
Margin="5,10,10,47" FontSize="14" Grid.ColumnSpan="3" Background="#e2e2e2">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Katastralgemeinde" Binding="{Binding Kg.Kg.Name}" Width="5*"/>
|
||||
<DataGridTextColumn Header="Ried" Binding="{Binding Rd.Name}" Width="4*"/>
|
||||
<DataGridTextColumn Header="Parzelle" Binding="{Binding GstNr}" Width="4*"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2"/>
|
||||
</Grid>
|
||||
|
||||
<GroupBox Header="Vertrag" Grid.Column="1" Grid.Row="1" Grid.RowSpan="1" Margin="5,5,5,5">
|
||||
<GroupBox Header="Vertrag" Grid.Column="2" Grid.Row="1" Grid.RowSpan="1" Margin="5,5,5,5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="60"/>
|
||||
<ColumnDefinition Width="70"/>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="50"/>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Content="VNr.:" Margin="10,10,0,0" Grid.Column="0"/>
|
||||
<TextBox x:Name="VNrInput" Margin="0,10,0,0" Width="48" Grid.Column="1" TextAlignment="Right" HorizontalAlignment="Left"
|
||||
TextChanged="VNrInput_TextChanged" LostFocus="VNrInput_LostFocus"/>
|
||||
|
||||
<Label Content="FbNr.:" Margin="10,10,0,0" Grid.Column="0"/>
|
||||
<TextBox x:Name="FbNrInput" Margin="0,10,0,0" Width="48" Grid.Column="1" TextAlignment="Right" HorizontalAlignment="Left"
|
||||
TextChanged="FbNrInput_TextChanged" LostFocus="FbNrInput_LostFocus"/>
|
||||
|
||||
<Label Content="MgNr.:" Margin="10,40,0,0" Grid.Column="0"/>
|
||||
<TextBox x:Name="MgNrInput" Margin="0,40,0,0" Width="48" Grid.Column="1" TextAlignment="Right" HorizontalAlignment="Left"/>
|
||||
|
||||
<TextBox x:Name="MgNrInput" IsEnabled="False"
|
||||
Margin="0,40,0,0" Width="48" Grid.Column="1" TextAlignment="Right" HorizontalAlignment="Left"/>
|
||||
|
||||
<Label Content="Von:" Margin="10,10,0,0" Grid.Column="2"/>
|
||||
<TextBox x:Name="YearFromInput" Margin="0,10,0,0" Width="100" Grid.Column="3" TextAlignment="Right" HorizontalAlignment="Left"
|
||||
TextChanged="YearFromInput_TextChanged"/>
|
||||
|
||||
<TextBox x:Name="YearFromInput" Margin="0,10,0,0" Width="41" Grid.Column="3" TextAlignment="Right" HorizontalAlignment="Left"
|
||||
TextChanged="IntegerInput_TextChanged"/>
|
||||
|
||||
<Label Content="Bis:" Margin="10,40,0,0" Grid.Column="2"/>
|
||||
<TextBox x:Name="YearToInput" Margin="0,40,0,0" Width="100" Grid.Column="3" TextAlignment="Right" HorizontalAlignment="Left"
|
||||
TextChanged="YearToInput_TextChanged"/>
|
||||
<TextBox x:Name="YearToInput" Margin="0,40,0,0" Width="41" Grid.Column="3" TextAlignment="Right" HorizontalAlignment="Left"
|
||||
TextChanged="IntegerInput_TextChanged"/>
|
||||
|
||||
<Label Content="Vertragsart:" Margin="10,70,0,0" Grid.Column="0" Grid.ColumnSpan="2"/>
|
||||
<ComboBox x:Name="AreaComTypeInput" DisplayMemberPath="DisplayName" TextSearch.TextPath="DisplayName"
|
||||
HorizontalAlignment="Stretch" Margin="0,70,10,0" Grid.Column="1" Grid.ColumnSpan="3"/>
|
||||
|
||||
<Label Content="Bewirt.-Art:" Margin="10,100,0,0" Grid.Column="0" Grid.ColumnSpan="2"/>
|
||||
<ComboBox x:Name="WineCultivationInput" DisplayMemberPath="Name" TextSearch.TextPath="Name"
|
||||
HorizontalAlignment="Stretch" Margin="0,100,10,0" Grid.Column="1" Grid.ColumnSpan="3"/>
|
||||
|
||||
<Label Content="Anmerkung:" Margin="10,130,0,0" Grid.Column="0" Grid.ColumnSpan="2"/>
|
||||
<TextBox x:Name="CommentInput" TextChanged="TextBox_TextChanged"
|
||||
HorizontalAlignment="Stretch" Margin="0,130,10,0" Grid.Column="1" Grid.ColumnSpan="3"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="Lage" Grid.Column="1" Grid.Row="2" Grid.RowSpan="1" Margin="5,5,5,5">
|
||||
|
||||
<GroupBox Header="Lage" Grid.Column="2" Grid.Row="2" Grid.RowSpan="1" Margin="5,5,5,10">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Content="Gemeinde:" Margin="10,10,0,0" Grid.Column="0"/>
|
||||
<Label Content="KG:" Margin="10,10,0,0" Grid.Column="0"/>
|
||||
<ComboBox x:Name="KgInput" ItemTemplate="{StaticResource KgNrTemplate}" TextSearch.TextPath="Name"
|
||||
Width="200" HorizontalAlignment="Left" SelectionChanged="KgInput_SelectionChanged"
|
||||
Margin="0,10,10,0" Grid.Column="1"/>
|
||||
|
||||
HorizontalAlignment="Stretch" Margin="0,10,10,0" Grid.Column="1"
|
||||
SelectionChanged="KgInput_SelectionChanged"/>
|
||||
|
||||
<Label Content="Ried:" Margin="10,40,0,0" Grid.Column="0"/>
|
||||
<ComboBox x:Name="RdInput" DisplayMemberPath="Name" TextSearch.TextPath="Name"
|
||||
Width="200" HorizontalAlignment="Left"
|
||||
Margin="0,40,0,0" Grid.Column="1"/>
|
||||
|
||||
<Label Content="Parzelle:" Margin="10,70,0,0" Grid.Column="0"/>
|
||||
<TextBox x:Name="GstNrInput" Margin="0,70,0,0" Width="100" Grid.Column="1" TextAlignment="Right" HorizontalAlignment="Left"
|
||||
HorizontalAlignment="Stretch" Margin="0,40,10,0" Grid.Column="1"/>
|
||||
|
||||
<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"
|
||||
TextChanged="GstNrInput_TextChanged" LostFocus="GstNrInput_LostFocus"/>
|
||||
|
||||
<Label Content="Fläche (m²):" Margin="10,100,0,0" Grid.Column="0"/>
|
||||
<Label Content="Fläche:" Margin="10,100,0,0" Grid.Column="0"/>
|
||||
<TextBox x:Name="AreaInput" Margin="0,100,0,0" Width="100" Grid.Column="1" TextAlignment="Right" HorizontalAlignment="Left"
|
||||
TextChanged="AreaInput_TextChanged"/>
|
||||
</Grid>
|
||||
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="Eigenschaften" Grid.Column="1" Grid.Row="3" Grid.RowSpan="1" Margin="5,5,5,5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="80"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Content="Sorte:" Margin="10,10,0,0" Grid.Column="0"/>
|
||||
<ComboBox x:Name="SortInput" ItemTemplate="{StaticResource WineVarietyTemplate}" TextSearch.TextPath="Name"
|
||||
Width="200" HorizontalAlignment="Left"
|
||||
Margin="0,10,10,0" Grid.Column="1"/>
|
||||
|
||||
<Label Content="Attribut:" Margin="10,40,0,0" Grid.Column="0"/>
|
||||
<ComboBox x:Name="AttrInput" DisplayMemberPath="Name" TextSearch.TextPath="Name"
|
||||
Width="200" HorizontalAlignment="Left"
|
||||
Margin="0,40,10,0" Grid.Column="1"/>
|
||||
|
||||
<Label Content="Bewirt. Art:" Margin="10,70,0,0" Grid.Column="0"/>
|
||||
<ComboBox x:Name="CultInput" DisplayMemberPath="Name" TextSearch.TextPath="Name"
|
||||
Width="200" HorizontalAlignment="Left"
|
||||
Margin="0,70,10,0" Grid.Column="1"/>
|
||||
|
||||
TextChanged="IntegerInput_TextChanged"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
</Grid>
|
||||
|
@@ -4,8 +4,11 @@ using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using Elwig.Helpers;
|
||||
using Elwig.Models;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||
using Xceed.Wpf.Toolkit.Primitives;
|
||||
|
||||
namespace Elwig.Windows {
|
||||
public partial class AreaComAdminWindow : AdministrationWindow {
|
||||
@@ -16,34 +19,18 @@ namespace Elwig.Windows {
|
||||
Member = Context.Members.Find(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
|
||||
Title = $"Flächenbindungen - {Member.AdministrativeName} - Elwig";
|
||||
ExemptInputs = new Control[] {
|
||||
MgNrInput, ContractList, AreaCommitmentList, NewContractButton, DeleteContractButton,
|
||||
ContractSaveButton, ContractCancelButton, NewAreaCommitmentButton,
|
||||
MgNrInput, AreaCommitmentList, NewAreaCommitmentButton,
|
||||
EditAreaCommitmentButton, DeleteAreaCommitmentButton, AreaCommitmentSaveButton,
|
||||
AreaCommitmentResetButton, AreaCommitmentCancelButton
|
||||
};
|
||||
RequiredInputs = new Control[] {
|
||||
VNrInput, YearFromInput, KgInput,
|
||||
GstNrInput, AreaInput, SortInput, CultInput
|
||||
FbNrInput, YearFromInput, KgInput,
|
||||
GstNrInput, AreaInput, AreaComTypeInput, WineCultivationInput
|
||||
};
|
||||
}
|
||||
|
||||
private async void Window_Loaded(object sender, RoutedEventArgs e) {
|
||||
await RefreshContractList();
|
||||
KgInput.ItemsSource = Context.WbKgs.Select(k => k.AtKg).OrderBy(k => k.Name).ToList();
|
||||
SortInput.ItemsSource = Context.WineVarieties.OrderBy(s => s.Name).ToList();
|
||||
AttrInput.ItemsSource = Context.WineAttributes.OrderBy(a => a.Name).ToList();
|
||||
CultInput.ItemsSource = Context.WineCultivations.OrderBy(c => c.Name).ToList();
|
||||
}
|
||||
|
||||
protected override async Task RenewContext() {
|
||||
await base.RenewContext();
|
||||
}
|
||||
|
||||
private async Task RefreshContractList() {
|
||||
/*
|
||||
await Context.Contracts.LoadAsync();
|
||||
await RefreshContractListQuery();
|
||||
*/
|
||||
private void Window_Loaded(object sender, RoutedEventArgs e) {
|
||||
LockInputs();
|
||||
}
|
||||
|
||||
private async Task RefreshAreaCommitmentList() {
|
||||
@@ -51,102 +38,78 @@ namespace Elwig.Windows {
|
||||
await RefreshAreaCommitmentListQuery();
|
||||
}
|
||||
|
||||
private async Task RefreshContractListQuery() {
|
||||
/*
|
||||
List<Contract> contracts = await Context.Contracts.Where(c => c.MgNr == member.MgNr).ToListAsync();
|
||||
|
||||
ContractList.ItemsSource = contracts;
|
||||
if (contracts.Count == 1)
|
||||
ContractList.SelectedIndex = 0;
|
||||
|
||||
// TODO notwendig?
|
||||
await RefreshAreaCommitmentList();
|
||||
RefreshInputs();
|
||||
*/
|
||||
}
|
||||
|
||||
private async Task RefreshAreaCommitmentListQuery() {
|
||||
/*
|
||||
Contract? contract = (Contract)ContractList.SelectedItem;
|
||||
|
||||
if (contract == null) {
|
||||
AreaCommitmentList.ItemsSource = null;
|
||||
return;
|
||||
}
|
||||
|
||||
List<AreaComParcel> parcels = (await Context.AreaCommitments.FindAsync(contract.VNr)).Parcels.ToList();
|
||||
|
||||
AreaCommitmentList.ItemsSource = parcels;
|
||||
if (parcels.Count == 1)
|
||||
AreaCommitmentList.SelectedIndex = 0;
|
||||
List<AreaCom> areaComs = await Context.AreaCommitments.Where(a => a.MgNr == Member.MgNr).OrderBy(a => a.FbNr).ToListAsync();
|
||||
ControlUtils.RenewItemsSource(AreaCommitmentList, areaComs, i => (i as AreaCom)?.FbNr,
|
||||
AreaCommitmentList_SelectionChanged, ControlUtils.RenewSourceDefault.None);
|
||||
RefreshInputs();
|
||||
*/
|
||||
}
|
||||
|
||||
private void RefreshInputs(bool validate = false) {
|
||||
/*
|
||||
ClearInputStates();
|
||||
Contract? c = (Contract)ContractList.SelectedItem;
|
||||
AreaComParcel? a = (AreaComParcel)AreaCommitmentList.SelectedItem;
|
||||
|
||||
if (c != null) {
|
||||
DeleteContractButton.IsEnabled = true;
|
||||
NewAreaCommitmentButton.IsEnabled = true;
|
||||
} else {
|
||||
DeleteContractButton.IsEnabled = false;
|
||||
NewAreaCommitmentButton.IsEnabled = false;
|
||||
}
|
||||
|
||||
if (a != null) {
|
||||
if (AreaCommitmentList.SelectedItem is AreaCom a) {
|
||||
EditAreaCommitmentButton.IsEnabled = true;
|
||||
DeleteAreaCommitmentButton.IsEnabled = true;
|
||||
FillInputs(a);
|
||||
} else {
|
||||
EditAreaCommitmentButton.IsEnabled = false;
|
||||
DeleteAreaCommitmentButton.IsEnabled = false;
|
||||
ClearInputs();
|
||||
MgNrInput.Text = "";
|
||||
ClearOriginalValues();
|
||||
ClearDefaultValues();
|
||||
ClearInputs(validate);
|
||||
ClearInputStates();
|
||||
}
|
||||
if (!validate) ClearInputStates();
|
||||
GC.Collect();
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
private void FillInputs(AreaComParcel a) {
|
||||
private void FillInputs(AreaCom a) {
|
||||
ClearOriginalValues();
|
||||
ClearDefaultValues();
|
||||
|
||||
VNrInput.Text = a.VNr.ToString();
|
||||
MgNrInput.Text = a.AreaCom.Contract.MgNr.ToString();
|
||||
YearFromInput.Text = a.AreaCom.Contract.YearFrom.ToString();
|
||||
YearToInput.Text = a.AreaCom.Contract.YearTo.ToString();
|
||||
FbNrInput.Text = a.FbNr.ToString();
|
||||
MgNrInput.Text = a.MgNr.ToString();
|
||||
YearFromInput.Text = a.YearFrom.ToString();
|
||||
YearToInput.Text = a.YearTo.ToString();
|
||||
|
||||
KgInput.SelectedItem = a.Kg.Kg;
|
||||
KgInput.SelectedItem = a.Kg.AtKg;
|
||||
RdInput.SelectedItem = a.Rd;
|
||||
GstNrInput.Text = a.GstNr;
|
||||
AreaInput.Text = a.Area.ToString();
|
||||
|
||||
SortInput.SelectedItem = a.AreaCom.WineVar;
|
||||
// FIXME
|
||||
//AttrInput.SelectedItem = a.WineAttr;
|
||||
CultInput.SelectedItem = a.AreaCom.WineCult;
|
||||
AreaComTypeInput.SelectedItem = a.AreaComType;
|
||||
WineCultivationInput.SelectedItem = a.WineCult;
|
||||
|
||||
FillOriginalValues();
|
||||
CommentInput.Text = a.Comment;
|
||||
|
||||
FinishInputFilling();
|
||||
}
|
||||
|
||||
private async void InitInputs() {
|
||||
ClearOriginalValues();
|
||||
ClearDefaultValues();
|
||||
|
||||
FbNrInput.Text = (await Context.NextFbNr()).ToString();
|
||||
MgNrInput.Text = Member.MgNr.ToString();
|
||||
|
||||
SetDefaultValue(FbNrInput);
|
||||
ValidateRequiredInputs();
|
||||
}
|
||||
|
||||
protected override async Task 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(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);
|
||||
await RefreshAreaCommitmentList();
|
||||
}
|
||||
*/
|
||||
|
||||
private void NewAreaCommitmentButton_Click(object sender, RoutedEventArgs evt) {
|
||||
IsCreating = true;
|
||||
AreaCommitmentList.IsEnabled = false;
|
||||
AreaCommitmentList.SelectedItem = null;
|
||||
ContractList.IsEnabled = false;
|
||||
HideAreaCommitmentNewEditDeleteButtons();
|
||||
ShowAreaCommitmentSaveResetCancelButtons();
|
||||
DisableContractNewDeleteButtons();
|
||||
UnlockInputs();
|
||||
VNrInput.IsReadOnly = true;
|
||||
YearFromInput.IsReadOnly = true;
|
||||
YearToInput.IsReadOnly = true;
|
||||
InitInputs();
|
||||
}
|
||||
|
||||
@@ -156,113 +119,65 @@ namespace Elwig.Windows {
|
||||
|
||||
IsEditing = true;
|
||||
AreaCommitmentList.IsEnabled = false;
|
||||
ContractList.IsEnabled = false;
|
||||
|
||||
HideAreaCommitmentNewEditDeleteButtons();
|
||||
ShowAreaCommitmentSaveResetCancelButtons();
|
||||
DisableContractNewDeleteButtons();
|
||||
UnlockInputs();
|
||||
}
|
||||
|
||||
private async void DeleteAreaCommitmentButton_Click(object sender, RoutedEventArgs evt) {
|
||||
/*
|
||||
AreaComParcel a = (AreaComParcel)AreaCommitmentList.SelectedItem;
|
||||
AreaCom a = (AreaCom)AreaCommitmentList.SelectedItem;
|
||||
if (a == null) return;
|
||||
|
||||
var r = MessageBox.Show(
|
||||
$"Soll die Parzelle {a.GstNr} ({a.Area} m²) wirklich unwiderruflich gelöscht werden?",
|
||||
"Parzelle löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
|
||||
$"Soll die Flächenbindung {a.GstNr} ({a.Area} m²) wirklich unwiderruflich gelöscht werden?",
|
||||
"Flächenbindung löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
|
||||
if (r == MessageBoxResult.Yes) {
|
||||
Context.Remove(a);
|
||||
Context.SaveChanges();
|
||||
await RefreshAreaCommitmentList();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
private void NewContractButton_Click(object sender, RoutedEventArgs evt) {
|
||||
IsCreating = true;
|
||||
ContractList.IsEnabled = false;
|
||||
ContractList.SelectedItem = null;
|
||||
AreaCommitmentList.IsEnabled = false;
|
||||
AreaCommitmentList.SelectedItem = null;
|
||||
HideContractNewDeleteButtons();
|
||||
ShowContractSaveCancelButtons();
|
||||
DisableAreaCommitmentNewEditDeleteButtons();
|
||||
UnlockInputs();
|
||||
InitInputs();
|
||||
}
|
||||
|
||||
private async void DeleteContractButton_Click(object sender, RoutedEventArgs evt) {
|
||||
/*
|
||||
Contract c = (Contract)ContractList.SelectedItem;
|
||||
if (c == null) return;
|
||||
|
||||
var r = MessageBox.Show(
|
||||
$"Soll der Vertrag {c.VNr} und alle enthaltenen Parzellen wirklich unwiderruflich gelöscht werden?",
|
||||
"Vertrag löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
|
||||
if (r == MessageBoxResult.Yes) {
|
||||
Context.Remove(c);
|
||||
Context.SaveChanges();
|
||||
await RefreshContractList();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
private async Task<Contract> UpdateContract(Contract c) {
|
||||
int newVNr = int.Parse(VNrInput.Text);
|
||||
c.MgNr = int.Parse(MgNrInput.Text);
|
||||
c.YearFrom = int.Parse(YearFromInput.Text);
|
||||
c.YearTo = (YearToInput.Text == "") ? null : int.Parse(YearToInput.Text);
|
||||
|
||||
EntityEntry<Contract>? tr = null;
|
||||
try {
|
||||
if (IsEditing) {
|
||||
tr = Context.Update(c);
|
||||
} else if (IsCreating) {
|
||||
c.VNr = newVNr;
|
||||
tr = (await Context.AddAsync(c));
|
||||
}
|
||||
await Context.SaveChangesAsync();
|
||||
|
||||
if (newVNr != c.VNr) {
|
||||
await Context.Database.ExecuteSqlAsync($"UPDATE contract SET vnr = {newVNr} WHERE mgnr = {c.VNr}");
|
||||
await Context.Contracts.LoadAsync();
|
||||
c = await Context.Contracts.FindAsync(newVNr);
|
||||
}
|
||||
} catch (Exception exc) {
|
||||
if (tr != null) await tr.ReloadAsync();
|
||||
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
|
||||
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
|
||||
MessageBox.Show(str, "Vertrag aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
private async Task<AreaComParcel> UpdateaAreaComParcel(AreaComParcel a) {
|
||||
a.VNr = int.Parse(VNrInput.Text);
|
||||
private async Task<AreaCom> UpdateAreaCom(AreaCom a) {
|
||||
int newFbNr = int.Parse(FbNrInput.Text);
|
||||
a.MgNr = int.Parse(MgNrInput.Text);
|
||||
a.YearFrom = int.Parse(YearFromInput.Text);
|
||||
a.YearTo = (YearToInput.Text == "") ? null : int.Parse(YearToInput.Text);
|
||||
a.KgNr = ((AT_Kg)KgInput.SelectedItem).KgNr;
|
||||
a.RdNr = ((WbRd)RdInput.SelectedItem).RdNr;
|
||||
a.RdNr = RdInput.SelectedItem.GetType() == typeof(NullItem) ? null : ((WbRd)RdInput.SelectedItem).RdNr;
|
||||
a.GstNr = GstNrInput.Text;
|
||||
a.Area = int.Parse(AreaInput.Text);
|
||||
a.AreaCom.SortId = ((WineVar)SortInput.SelectedItem).SortId;
|
||||
// FIXME
|
||||
//a.AttrId = ((WineAttr)AttrInput.SelectedItem).AttrId;
|
||||
a.AreaCom.CultId = ((WineCult)CultInput.SelectedItem).CultId;
|
||||
a.VtrgId = (AreaComTypeInput.SelectedItem as AreaComType)?.VtrgId;
|
||||
a.CultId = (WineCultivationInput.SelectedItem as WineCult)?.CultId;
|
||||
a.Comment = (CommentInput.Text == "") ? null : CommentInput.Text;
|
||||
|
||||
EntityEntry<AreaComParcel>? tr = null;
|
||||
EntityEntry<AreaCom>? tr = null;
|
||||
try {
|
||||
if (IsEditing) {
|
||||
tr = Context.Update(a);
|
||||
} else if (IsCreating) {
|
||||
a.FbNr = newFbNr;
|
||||
tr = (await Context.AddAsync(a));
|
||||
} else {
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
await Context.SaveChangesAsync();
|
||||
|
||||
if (newFbNr != a.FbNr) {
|
||||
await Context.Database.ExecuteSqlAsync($"UPDATE area_commitment SET fbnr = {newFbNr} WHERE fbnr = {a.FbNr}");
|
||||
tr.State = EntityState.Detached;
|
||||
await Context.SaveChangesAsync();
|
||||
await tr.ReloadAsync();
|
||||
a = await Context.AreaCommitments.FindAsync(newFbNr);
|
||||
}
|
||||
|
||||
} catch (Exception exc) {
|
||||
if (tr != null) await tr.ReloadAsync();
|
||||
if (tr != null) {
|
||||
tr.State = EntityState.Detached;
|
||||
await tr.ReloadAsync();
|
||||
}
|
||||
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
|
||||
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
|
||||
MessageBox.Show(str, "Flächenbindung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
@@ -270,26 +185,17 @@ namespace Elwig.Windows {
|
||||
|
||||
return a;
|
||||
}
|
||||
*/
|
||||
|
||||
private async void AreaCommitmentSaveButton_Click(object sender, RoutedEventArgs evt) {
|
||||
/*
|
||||
Contract c = await UpdateContract((Contract)ContractList.SelectedItem);
|
||||
AreaComParcel a = await UpdateaAreaComParcel(IsEditing ? (AreaComParcel)AreaCommitmentList.SelectedItem : Context.CreateProxy<AreaComParcel>());
|
||||
|
||||
AreaCom a = await UpdateAreaCom(IsEditing ? (AreaCom)AreaCommitmentList.SelectedItem : Context.CreateProxy<AreaCom>());
|
||||
IsEditing = false;
|
||||
IsCreating = false;
|
||||
AreaCommitmentList.IsEnabled = true;
|
||||
ContractList.IsEnabled = true;
|
||||
HideAreaCommitmentSaveResetCancelButtons();
|
||||
ShowAreaCommitmentNewEditDeleteButtons();
|
||||
EnableContractNewDeleteButtons();
|
||||
LockInputs();
|
||||
await RefreshContractList();
|
||||
await RefreshAreaCommitmentList();
|
||||
ContractList.SelectedItem = c;
|
||||
AreaCommitmentList.SelectedItem = a;
|
||||
*/
|
||||
}
|
||||
|
||||
private void AreaCommitmentResetButton_Click(object sender, RoutedEventArgs evt) {
|
||||
@@ -305,43 +211,8 @@ namespace Elwig.Windows {
|
||||
IsEditing = false;
|
||||
IsCreating = false;
|
||||
AreaCommitmentList.IsEnabled = true;
|
||||
ContractList.IsEnabled = true;
|
||||
HideAreaCommitmentSaveResetCancelButtons();
|
||||
ShowAreaCommitmentNewEditDeleteButtons();
|
||||
EnableContractNewDeleteButtons();
|
||||
RefreshInputs();
|
||||
ClearInputStates();
|
||||
LockInputs();
|
||||
}
|
||||
|
||||
private async void ContractSaveButton_Click(object sender, RoutedEventArgs evt) {
|
||||
/*
|
||||
Contract c = await UpdateContract(Context.CreateProxy<Contract>());
|
||||
AreaComParcel a = await UpdateaAreaComParcel(Context.CreateProxy<AreaComParcel>());
|
||||
|
||||
IsEditing = false;
|
||||
IsCreating = false;
|
||||
AreaCommitmentList.IsEnabled = true;
|
||||
ContractList.IsEnabled = true;
|
||||
HideContractSaveCancelButtons();
|
||||
ShowContractNewDeleteButtons();
|
||||
EnableAreaCommitmentNewEditDeleteButtons();
|
||||
LockInputs();
|
||||
await RefreshContractList();
|
||||
await RefreshAreaCommitmentList();
|
||||
ContractList.SelectedItem = c;
|
||||
AreaCommitmentList.SelectedItem = a;
|
||||
*/
|
||||
}
|
||||
|
||||
private void ContractCancelButton_Click(object sender, RoutedEventArgs evt) {
|
||||
IsEditing = false;
|
||||
IsCreating = false;
|
||||
ContractList.IsEnabled = true;
|
||||
AreaCommitmentList.IsEnabled = true;
|
||||
HideContractSaveCancelButtons();
|
||||
ShowContractNewDeleteButtons();
|
||||
EnableAreaCommitmentNewEditDeleteButtons();
|
||||
RefreshInputs();
|
||||
ClearInputStates();
|
||||
LockInputs();
|
||||
@@ -350,28 +221,8 @@ namespace Elwig.Windows {
|
||||
override protected void UpdateButtons() {
|
||||
if (!IsEditing && !IsCreating) return;
|
||||
bool ch = HasChanged, v = IsValid;
|
||||
ContractSaveButton.IsEnabled = (v && ch);
|
||||
AreaCommitmentResetButton.IsEnabled = (ch);
|
||||
AreaCommitmentSaveButton.IsEnabled = (v && ch);
|
||||
}
|
||||
|
||||
private async void InitInputs() {
|
||||
/*
|
||||
VNrInput.Text = ContractList.SelectedItem == null ? (await Context.NextVNr()).ToString() : ((Contract)ContractList.SelectedItem).VNr.ToString();
|
||||
MgNrInput.Text = member.MgNr.ToString();
|
||||
FillOriginalValues();
|
||||
ValidateRequiredInputs();
|
||||
*/
|
||||
}
|
||||
|
||||
private void EnableContractNewDeleteButtons() {
|
||||
NewContractButton.IsEnabled = true;
|
||||
DeleteContractButton.IsEnabled = ContractList.SelectedItem != null;
|
||||
}
|
||||
|
||||
private void DisableContractNewDeleteButtons() {
|
||||
NewContractButton.IsEnabled = false;
|
||||
DeleteContractButton.IsEnabled = false;
|
||||
AreaCommitmentSaveButton.IsEnabled = (ch && v);
|
||||
}
|
||||
|
||||
private void DisableAreaCommitmentNewEditDeleteButtons() {
|
||||
@@ -381,37 +232,11 @@ namespace Elwig.Windows {
|
||||
}
|
||||
|
||||
private void EnableAreaCommitmentNewEditDeleteButtons() {
|
||||
NewAreaCommitmentButton.IsEnabled = ContractList.SelectedItem != null;
|
||||
NewAreaCommitmentButton.IsEnabled = true;
|
||||
EditAreaCommitmentButton.IsEnabled = AreaCommitmentList.SelectedItem != null;
|
||||
DeleteAreaCommitmentButton.IsEnabled = AreaCommitmentList.SelectedItem != null;
|
||||
}
|
||||
|
||||
private void ShowContractSaveCancelButtons() {
|
||||
ContractSaveButton.IsEnabled = false;
|
||||
ContractCancelButton.IsEnabled = true;
|
||||
ContractSaveButton.Visibility = Visibility.Visible;
|
||||
ContractCancelButton.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
private void HideContractSaveCancelButtons() {
|
||||
ContractSaveButton.IsEnabled = false;
|
||||
ContractCancelButton.IsEnabled = false;
|
||||
ContractSaveButton.Visibility = Visibility.Hidden;
|
||||
ContractCancelButton.Visibility = Visibility.Hidden;
|
||||
}
|
||||
|
||||
private void ShowContractNewDeleteButtons() {
|
||||
EnableContractNewDeleteButtons();
|
||||
NewContractButton.Visibility = Visibility.Visible;
|
||||
DeleteContractButton.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
private void HideContractNewDeleteButtons() {
|
||||
DisableContractNewDeleteButtons();
|
||||
NewContractButton.Visibility = Visibility.Hidden;
|
||||
DeleteContractButton.Visibility = Visibility.Hidden;
|
||||
}
|
||||
|
||||
private void ShowAreaCommitmentSaveResetCancelButtons() {
|
||||
AreaCommitmentSaveButton.IsEnabled = false;
|
||||
AreaCommitmentResetButton.IsEnabled = false;
|
||||
@@ -444,57 +269,42 @@ namespace Elwig.Windows {
|
||||
DeleteAreaCommitmentButton.Visibility = Visibility.Hidden;
|
||||
}
|
||||
|
||||
private async void ContractList_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
|
||||
//await RefreshContractListQuery();
|
||||
await RefreshAreaCommitmentList();
|
||||
}
|
||||
|
||||
private void AreaCommitmentList_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
|
||||
RefreshInputs();
|
||||
}
|
||||
|
||||
private void KgInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
|
||||
AT_Kg? curr_kg = (AT_Kg)KgInput.SelectedItem;
|
||||
if (curr_kg != null) {
|
||||
RdInput.ItemsSource = Context.WbRde.Where(r => r.KgNr == curr_kg.KgNr).OrderBy(r => r.Name).ToList();
|
||||
private void AttributesInput_SelectionChanged(object sender, ItemSelectionChangedEventArgs evt) {
|
||||
|
||||
}
|
||||
|
||||
private async void KgInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
|
||||
if (KgInput.SelectedItem is AT_Kg curr_kg) {
|
||||
var rdList = await Context.WbRde.Where(r => r.KgNr == curr_kg.KgNr).OrderBy(r => r.Name).Cast<object>().ToListAsync();
|
||||
rdList.Insert(0, new NullItem());
|
||||
ControlUtils.RenewItemsSource(RdInput, rdList, i => (i as WbRd)?.RdNr);
|
||||
} else {
|
||||
var rdList = new object[] { new NullItem() };
|
||||
ControlUtils.RenewItemsSource(RdInput, rdList, i => (i as WbRd)?.RdNr);
|
||||
}
|
||||
ComboBox_SelectionChanged(sender, evt);
|
||||
}
|
||||
|
||||
/*
|
||||
protected void InputTextChanged(TextBox input, Func<TextBox, bool, AppDbContext, Contract?, ValidationResult> checker) {
|
||||
InputTextChanged(input, checker(input, SenderIsRequired(input), Context, (Contract)ContractList.SelectedItem));
|
||||
protected void InputTextChanged(TextBox input, Func<TextBox, bool, AppDbContext, AreaCom?, ValidationResult> checker) {
|
||||
InputTextChanged(input, checker(input, SenderIsRequired(input), Context, (AreaCom)AreaCommitmentList.SelectedItem));
|
||||
}
|
||||
|
||||
protected void InputLostFocus(TextBox input, Func<TextBox, bool, AppDbContext, Contract?, ValidationResult> checker, string? msg = null) {
|
||||
InputLostFocus(input, checker(input, SenderIsRequired(input), Context, (Contract)ContractList.SelectedItem), msg);
|
||||
}
|
||||
*/
|
||||
|
||||
private void NumericInput_TextChanged(object sender, RoutedEventArgs evt) {
|
||||
InputTextChanged((TextBox)sender, Validator.CheckInteger);
|
||||
protected void InputLostFocus(TextBox input, Func<TextBox, bool, AppDbContext, AreaCom?, ValidationResult> checker, string? msg = null) {
|
||||
InputLostFocus(input, checker(input, SenderIsRequired(input), Context, (AreaCom)AreaCommitmentList.SelectedItem), msg);
|
||||
}
|
||||
|
||||
private void VNrInput_TextChanged(object sender, RoutedEventArgs evt) {
|
||||
/*
|
||||
InputTextChanged((TextBox)sender, Validator.CheckVNr);
|
||||
*/
|
||||
private void FbNrInput_TextChanged(object sender, RoutedEventArgs evt) {
|
||||
InputTextChanged((TextBox)sender, Validator.CheckFbNr);
|
||||
}
|
||||
|
||||
private void VNrInput_LostFocus(object sender, RoutedEventArgs evt) {
|
||||
/*
|
||||
InputLostFocus((TextBox)sender, Validator.CheckVNr);
|
||||
*/
|
||||
private void FbNrInput_LostFocus(object sender, RoutedEventArgs evt) {
|
||||
InputLostFocus((TextBox)sender, Validator.CheckFbNr);
|
||||
}
|
||||
|
||||
private void YearFromInput_TextChanged(object sender, RoutedEventArgs evt) {
|
||||
InputTextChanged((TextBox)sender, Validator.CheckInteger);
|
||||
}
|
||||
|
||||
private void YearToInput_TextChanged(object sender, RoutedEventArgs evt) {
|
||||
InputTextChanged((TextBox)sender, Validator.CheckInteger);
|
||||
}
|
||||
|
||||
|
||||
private void GstNrInput_TextChanged(object sender, RoutedEventArgs evt) {
|
||||
InputTextChanged((TextBox)sender, Validator.CheckGstNr);
|
||||
}
|
||||
@@ -502,9 +312,5 @@ namespace Elwig.Windows {
|
||||
private void GstNrInput_LostFocus(object sender, RoutedEventArgs evt) {
|
||||
InputLostFocus((TextBox)sender, Validator.CheckGstNr);
|
||||
}
|
||||
|
||||
private void AreaInput_TextChanged(object sender, RoutedEventArgs evt) {
|
||||
InputTextChanged((TextBox)sender, Validator.CheckInteger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
273
Elwig/Windows/BaseDataWindow.xaml
Normal file
273
Elwig/Windows/BaseDataWindow.xaml
Normal file
@@ -0,0 +1,273 @@
|
||||
<local:AdministrationWindow x:Class="Elwig.Windows.BaseDataWindow"
|
||||
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"
|
||||
Title="Stammdaten - Elwig" Height="500" MinHeight="400" Width="800" MinWidth="800"
|
||||
Loaded="Window_Loaded">
|
||||
<Window.Resources>
|
||||
<Style TargetType="Label">
|
||||
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||
<Setter Property="Padding" Value="2,4,2,4"/>
|
||||
<Setter Property="Height" Value="25"/>
|
||||
</Style>
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="Padding" Value="2"/>
|
||||
<Setter Property="Height" Value="25"/>
|
||||
<Setter Property="TextWrapping" Value="NoWrap"/>
|
||||
</Style>
|
||||
<Style TargetType="ComboBox">
|
||||
<Setter Property="Height" Value="25"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||
</Style>
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="Padding" Value="9,3"/>
|
||||
<Setter Property="Height" Value="27"/>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
<Grid>
|
||||
<TabControl Margin="10,10,10,42">
|
||||
<TabItem Header="Mandant">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="130"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="90"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Content="Vollständiger Name:"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="0" Margin="10,10,0,10"/>
|
||||
<TextBox x:Name="ClientNameFull" IsEnabled="False"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Stretch" Grid.Column="1" Grid.ColumnSpan="3" Margin="0,10,10,10"/>
|
||||
|
||||
<Label Content="Name:"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="0" Margin="10,40,0,10"/>
|
||||
<TextBox x:Name="ClientNameInput"
|
||||
TextChanged="ClientNames_TextChanged"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Stretch" Grid.Column="1" Grid.ColumnSpan="3" Margin="0,40,10,10"/>
|
||||
|
||||
<Label Content="Namenszusatz:"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="0" Margin="10,70,0,10"/>
|
||||
<TextBox x:Name="ClientNameSuffixInput"
|
||||
TextChanged="ClientNames_TextChanged"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Stretch" Grid.Column="1" Grid.ColumnSpan="3" Margin="0,70,10,10"/>
|
||||
|
||||
<Label Content="Gesellschaftsform:"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="0" Margin="10,100,0,10"/>
|
||||
<TextBox x:Name="ClientNameTypeInput"
|
||||
TextChanged="ClientNames_TextChanged"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Stretch" Grid.Column="1" Margin="0,100,10,10"/>
|
||||
|
||||
<Label Content="Kürzel/Kurzform:"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="0" Margin="10,130,0,10"/>
|
||||
<TextBox x:Name="ClientNameTokenInput"
|
||||
TextChanged="TextBox_TextChanged"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="1" Margin="0,130,10,10" Width="70"/>
|
||||
<TextBox x:Name="ClientNameShortInput"
|
||||
TextChanged="TextBox_TextChanged"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Stretch" Grid.Column="1" Margin="75,130,10,10"/>
|
||||
|
||||
<Label Content="Adresse:"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="0" Margin="10,160,0,10"/>
|
||||
<TextBox x:Name="ClientAddressInput"
|
||||
TextChanged="TextBox_TextChanged"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Stretch" Grid.Column="1" Margin="0,160,10,10"/>
|
||||
|
||||
<Label Content="PLZ/Ort:"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="0" Margin="10,190,0,10"/>
|
||||
<TextBox x:Name="ClientPlzInput"
|
||||
Margin="0,190,0,0" Width="42" Grid.Column="1" HorizontalAlignment="Left"
|
||||
TextChanged="PlzInput_TextChanged" LostFocus="PlzInput_LostFocus"/>
|
||||
<TextBox x:Name="ClientOrtInput" Margin="47,190,10,0" Grid.Column="1"
|
||||
TextChanged="TextBox_TextChanged"/>
|
||||
|
||||
<Label Content="IBAN:" Margin="10,220,0,0" Grid.Column="0"/>
|
||||
<TextBox x:Name="ClientIbanInput" Margin="0,220,10,0" Grid.Column="1"
|
||||
TextChanged="IbanInput_TextChanged" LostFocus="IbanInput_LostFocus"/>
|
||||
|
||||
<Label Content="BIC:" Margin="10,250,0,0" Grid.Column="0"/>
|
||||
<TextBox x:Name="ClientBicInput" Margin="0,250,10,0" Grid.Column="1"
|
||||
TextChanged="BicInput_TextChanged" LostFocus="BicInput_LostFocus"/>
|
||||
|
||||
<Label Content="UID:"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="2" Margin="10,100,0,10"/>
|
||||
<TextBox x:Name="ClientUstIdNrInput"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="3" Margin="0,100,10,10" Width="96"
|
||||
TextChanged="UstIdNrInput_TextChanged" LostFocus="UstIdNrInput_LostFocus"/>
|
||||
|
||||
<Label Content="Betriebs-Nr.:"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="2" Margin="10,130,0,10"/>
|
||||
<TextBox x:Name="ClientLfbisNrInput"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="3" Margin="0,130,10,10" Width="64"
|
||||
TextChanged="LfbisNrInput_TextChanged" LostFocus="LfbisNrInput_LostFocus"/>
|
||||
|
||||
<Label Content="Telefon-Nr.:"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="2" Margin="10,160,0,10"/>
|
||||
<TextBox x:Name="ClientPhoneNrInput" Margin="0,160,10,10" Grid.Column="3"
|
||||
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
|
||||
|
||||
<Label Content="Fax-Nr.:"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="2" Margin="10,190,0,10"/>
|
||||
<TextBox x:Name="ClientFaxNrInput" Margin="0,190,10,10" Grid.Column="3"
|
||||
TextChanged="PhoneNrInput_TextChanged" LostFocus="PhoneNrInput_LostFocus"/>
|
||||
|
||||
<Label Content="E-Mail-Adr.:"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="2" Margin="10,220,0,10"/>
|
||||
<TextBox x:Name="ClientEmailAddressInput" Margin="0,220,10,0" Grid.Column="3"
|
||||
TextChanged="EmailAddressInput_TextChanged" LostFocus="EmailAddressInput_LostFocus"/>
|
||||
|
||||
<Label Content="Website:"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="2" Margin="10,250,0,10"/>
|
||||
<TextBox x:Name="ClientWebsiteInput" Margin="0,250,10,0" Grid.Column="3"
|
||||
TextChanged="TextBox_TextChanged"/>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<TabItem Header="Zweigstellen">
|
||||
|
||||
</TabItem>
|
||||
<TabItem Header="Sortenattribute">
|
||||
|
||||
</TabItem>
|
||||
<TabItem Header="Bewirtschaftungsarten">
|
||||
|
||||
</TabItem>
|
||||
<TabItem Header="Flächenbindungsverträge">
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Saisons">
|
||||
<Grid>
|
||||
<ListBox x:Name="SeasonList" HorizontalAlignment="Left" VerticalAlignment="Stretch" Width="150" Margin="10,10,10,10"
|
||||
SelectionChanged="SeasonList_SelectionChanged">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{Binding Year}" Width="40"/>
|
||||
<TextBlock Text="{Binding Currency.Name}" Width="60"/>
|
||||
<TextBlock Text="{Binding Precision}" Width="10"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
|
||||
<GroupBox Header="Zu-/Abschläge" Margin="170,150,10,10" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="400"/>
|
||||
<ColumnDefinition Width="70"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ListBox x:Name="SeasonModifierList" SelectionChanged="SeasonModifierList_SelectionChanged"
|
||||
HorizontalAlignment="Left" VerticalAlignment="Stretch" Width="350" Margin="10,10,10,10">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{Binding ModId}" Width="30"/>
|
||||
<TextBlock Text="{Binding Name}" Width="200" Margin="0,0,10,0"/>
|
||||
<TextBlock Text="{Binding ValueStr}"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
<Button x:Name="SeasonModifierUpButton" Content="" FontFamily="Segoe MDL2 Assets" FontSize="10" Padding="0,0,0,0" IsEnabled="False"
|
||||
Click="SeasonModifierUpButton_Click"
|
||||
VerticalAlignment="Center" HorizontalAlignment="Left" Width="25" Height="25" Margin="365,0,0,90"/>
|
||||
<Button x:Name="SeasonModifierDownButton" Content="" FontFamily="Segoe MDL2 Assets" FontSize="10" Padding="0,2,0,0" IsEnabled="False"
|
||||
Click="SeasonModifierDownButton_Click"
|
||||
VerticalAlignment="Center" HorizontalAlignment="Left" Width="25" Height="25" Margin="365,0,0,30"/>
|
||||
<Button x:Name="SeasonModifierAddButton" Content="" FontFamily="Segoe MDL2 Assets" FontSize="11" Padding="0,1.5,0,0" IsEnabled="False"
|
||||
Click="SeasonModifierAddButton_Click"
|
||||
VerticalAlignment="Center" HorizontalAlignment="Left" Width="25" Height="25" Margin="365,30,0,0"/>
|
||||
<Button x:Name="SeasonModifierDeleteButton" Content="" FontFamily="Segoe MDL2 Assets" FontSize="11" Padding="0,1.5,0,0" IsEnabled="False"
|
||||
Click="SeasonModifierDeleteButton_Click"
|
||||
VerticalAlignment="Center" HorizontalAlignment="Left" Width="25" Height="25" Margin="365,90,0,0"/>
|
||||
|
||||
<Label Content="ID:" Grid.Column="1" Margin="10,10,10,10"/>
|
||||
<TextBox x:Name="SeasonModifierIdInput" Grid.Column="2" Margin="0,10,10,10" Width="50" HorizontalAlignment="Left"
|
||||
TextChanged="SeasonModifierIdInput_TextChanged"/>
|
||||
|
||||
<Label Content="Name:" Grid.Column="1" Margin="10,40,10,10"/>
|
||||
<TextBox x:Name="SeasonModifierNameInput" Grid.Column="2" Margin="0,40,10,10"
|
||||
TextChanged="SeasonModifierNameInput_TextChanged"/>
|
||||
|
||||
<Label Content="Relativ:" Grid.Column="1" Margin="10,70,10,10"/>
|
||||
<TextBox x:Name="SeasonModifierRelInput" Grid.Column="2" Margin="0,70,10,10"
|
||||
TextChanged="SeasonModifierRelInput_TextChanged"/>
|
||||
|
||||
<Label Content="Absolut:" Grid.Column="1" Margin="10,100,10,10"/>
|
||||
<TextBox x:Name="SeasonModifierAbsInput" Grid.Column="2" Margin="0,100,10,10"
|
||||
TextChanged="SeasonModifierAbsInput_TextChanged"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<TabItem Header="Parameter">
|
||||
|
||||
</TabItem>
|
||||
<TabItem Header="Textelemente">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Visible">
|
||||
<StackPanel>
|
||||
<GroupBox Header="Lieferschein" Margin="10,10,10,10" Height="180">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="370"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox x:Name="TextElementDeliveryNote" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10,10,10,10" Height="Auto"
|
||||
TextChanged="TextBox_TextChanged"/>
|
||||
<GroupBox Grid.Column="1" Header="Tabelle zur Gesamtlieferung" Margin="10,10,10,10" VerticalAlignment="Center">
|
||||
<StackPanel Margin="5,5,0,5">
|
||||
<RadioButton GroupName="ModeDeliveryNote" x:Name="ModeDeliveryNoteNone" Content="Verbergen"
|
||||
Checked="RadioButton_Changed" Unchecked="RadioButton_Changed"/>
|
||||
<RadioButton GroupName="ModeDeliveryNote" x:Name="ModeDeliveryNoteGaOnly" Content="Nur Gesamtlieferung lt. Geschäftsanteilen anzeigen"
|
||||
Checked="RadioButton_Changed" Unchecked="RadioButton_Changed"/>
|
||||
<RadioButton GroupName="ModeDeliveryNote" x:Name="ModeDeliveryNoteShort" Content="Nur Gesamtlieferung und Flächenbindungen von auf dem
Lieferschein angeführten Sorten anzeigen"
|
||||
Checked="RadioButton_Changed" Unchecked="RadioButton_Changed"/>
|
||||
<RadioButton GroupName="ModeDeliveryNote" x:Name="ModeDeliveryNoteFull" Content="Vollständig anzeigen"
|
||||
Checked="RadioButton_Changed" Unchecked="RadioButton_Changed"/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
<GroupBox Header="Anlieferungsbestätigung" Margin="10,10,10,10" Height="250">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox x:Name="TextElementDeliveryConfirmation" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" AcceptsReturn="True"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10,10,10,10" Height="Auto"
|
||||
TextChanged="TextBox_TextChanged"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
|
||||
<Button x:Name="EditButton" Content="Bearbeiten"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,10" Width="120"
|
||||
Click="EditButton_Click"/>
|
||||
<Button x:Name="SaveButton" Content="Speichern" IsEnabled="False"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,250,10" Width="120"
|
||||
Click="SaveButton_Click"/>
|
||||
<Button x:Name="ResetButton" Content="Zurücksetzen" IsEnabled="False" Visibility="Hidden"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,10" Width="120"
|
||||
Click="ResetButton_Click"/>
|
||||
<Button x:Name="CancelButton" Content="Abbrechen" IsEnabled="False" IsCancel="True"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="250,0,0,10" Width="120"
|
||||
Click="CancelButton_Click"/>
|
||||
</Grid>
|
||||
</local:AdministrationWindow>
|
360
Elwig/Windows/BaseDataWindow.xaml.cs
Normal file
360
Elwig/Windows/BaseDataWindow.xaml.cs
Normal file
@@ -0,0 +1,360 @@
|
||||
using Elwig.Helpers;
|
||||
using Elwig.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace Elwig.Windows {
|
||||
public partial class BaseDataWindow : AdministrationWindow {
|
||||
|
||||
private Dictionary<string, string?>? Modifiers = null;
|
||||
private Dictionary<Modifier, string>? ModifierIds = null;
|
||||
private ObservableCollection<Modifier>? ModifierList = null;
|
||||
private bool ModifiersChanged = false;
|
||||
private bool ModifierUpdate = false;
|
||||
|
||||
public BaseDataWindow() {
|
||||
InitializeComponent();
|
||||
RequiredInputs = new Control[] {
|
||||
ClientNameInput, ClientNameTypeInput, ClientNameTokenInput, ClientNameShortInput,
|
||||
ClientAddressInput, ClientPlzInput, ClientOrtInput,
|
||||
};
|
||||
ExemptInputs = new Control[] {
|
||||
ClientNameFull,
|
||||
SeasonModifierIdInput, SeasonModifierNameInput, SeasonModifierRelInput, SeasonModifierAbsInput,
|
||||
};
|
||||
}
|
||||
|
||||
new protected void LockInputs() {
|
||||
base.LockInputs();
|
||||
SeasonModifierIdInput.IsReadOnly = true;
|
||||
SeasonModifierNameInput.IsReadOnly = true;
|
||||
SeasonModifierRelInput.IsReadOnly = true;
|
||||
SeasonModifierAbsInput.IsReadOnly = true;
|
||||
}
|
||||
|
||||
new protected void UnlockInputs() {
|
||||
base.UnlockInputs();
|
||||
SeasonModifierIdInput.IsReadOnly = false;
|
||||
SeasonModifierNameInput.IsReadOnly = false;
|
||||
SeasonModifierRelInput.IsReadOnly = false;
|
||||
SeasonModifierAbsInput.IsReadOnly = false;
|
||||
}
|
||||
|
||||
private void Window_Loaded(object sender, RoutedEventArgs evt) {
|
||||
LockInputs();
|
||||
FillInputs(App.Client);
|
||||
}
|
||||
|
||||
protected override async Task OnRenewContext() {
|
||||
await base.OnRenewContext();
|
||||
ControlUtils.RenewItemsSource(SeasonList, await Context.Seasons.OrderByDescending(s => s.Year).ToListAsync(), s => (s as Season)?.Year, null, ControlUtils.RenewSourceDefault.First);
|
||||
var year = (SeasonList.SelectedItem as Season)?.Year;
|
||||
ControlUtils.RenewItemsSource(SeasonModifierList, await Context.Modifiers.Where(m => m.Year == year).OrderBy(m => m.Ordering).ToListAsync(), m => (m as Modifier)?.ModId);
|
||||
}
|
||||
|
||||
protected override void UpdateButtons() {
|
||||
if (!IsEditing && !IsCreating) return;
|
||||
bool ch = ModifiersChanged || HasChanged, v = IsValid;
|
||||
CancelButton.IsEnabled = true;
|
||||
ResetButton.IsEnabled = ch;
|
||||
SaveButton.IsEnabled = ch && v;
|
||||
SeasonModifierUpButton.IsEnabled = SeasonModifierList.SelectedIndex >= 1;
|
||||
SeasonModifierDownButton.IsEnabled = SeasonModifierList.SelectedIndex != -1 && SeasonModifierList.SelectedIndex < (ModifierList?.Count - 1 ?? 0);
|
||||
SeasonModifierAddButton.IsEnabled = true;
|
||||
SeasonModifierDeleteButton.IsEnabled = SeasonModifierList.SelectedIndex != -1;
|
||||
}
|
||||
|
||||
private void ModifiersInitEditing() {
|
||||
var year = (SeasonList.SelectedItem as Season)?.Year;
|
||||
Context.ChangeTracker.Clear();
|
||||
ModifierList = new(Context.Modifiers.Where(m => m.Year == year).OrderBy(m => m.Ordering).ToList());
|
||||
Modifiers = ModifierList.ToDictionary(m => m.ModId, m => m.ModId);
|
||||
ModifierIds = ModifierList.ToDictionary(m => m, m => m.ModId);
|
||||
ControlUtils.RenewItemsSource(SeasonModifierList, ModifierList, m => (m as Modifier)?.ModId);
|
||||
}
|
||||
|
||||
private void ModifiersFinishEditing() {
|
||||
var year = (SeasonList.SelectedItem as Season)?.Year;
|
||||
ControlUtils.RenewItemsSource(SeasonModifierList, Context.Modifiers.Where(m => m.Year == year).OrderBy(m => m.Ordering).ToList(), m => (m as Modifier)?.ModId);
|
||||
ModifierList = null;
|
||||
Modifiers = null;
|
||||
ModifierIds = null;
|
||||
ModifiersChanged = false;
|
||||
|
||||
SeasonModifierUpButton.IsEnabled = false;
|
||||
SeasonModifierDownButton.IsEnabled = false;
|
||||
SeasonModifierAddButton.IsEnabled = false;
|
||||
SeasonModifierDeleteButton.IsEnabled = false;
|
||||
}
|
||||
|
||||
private async Task ModifiersSave() {
|
||||
if (!ModifiersChanged || ModifierList == null || Modifiers == null || ModifierIds == null) return;
|
||||
int i = 0;
|
||||
foreach (var mod in ModifierList) mod.Ordering = ++i;
|
||||
|
||||
var year = (SeasonList.SelectedItem as Season)?.Year;
|
||||
foreach (var (modid, _) in Modifiers.Where(m => m.Value == null)) {
|
||||
Context.Remove(Context.Modifiers.Find(new object?[] { year, modid }));
|
||||
}
|
||||
foreach (var (mod, old) in ModifierIds) {
|
||||
mod.ModId = old;
|
||||
}
|
||||
foreach (var (old, modid) in Modifiers.Where(m => m.Value != null)) {
|
||||
Context.Update(Context.Modifiers.Find(new object?[] { year, old }));
|
||||
}
|
||||
await Context.SaveChangesAsync();
|
||||
|
||||
foreach (var (old, modid) in Modifiers.Where(m => m.Value != null)) {
|
||||
await Context.Database.ExecuteSqlAsync($"UPDATE modifier SET modid = {modid} WHERE (year, modid) = ({year}, {old})");
|
||||
}
|
||||
await Context.SaveChangesAsync();
|
||||
|
||||
foreach (var mod in ModifierList.Where(m => !ModifierIds.ContainsKey(m))) {
|
||||
if (mod.ModId == null) continue;
|
||||
await Context.AddAsync(mod);
|
||||
}
|
||||
await Context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private void EditButton_Click(object sender, RoutedEventArgs evt) {
|
||||
IsEditing = true;
|
||||
EditButton.Visibility = Visibility.Hidden;
|
||||
ResetButton.Visibility = Visibility.Visible;
|
||||
|
||||
ModifiersInitEditing();
|
||||
UnlockInputs();
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
private void CancelButton_Click(object sender, RoutedEventArgs evt) {
|
||||
IsEditing = false;
|
||||
IsCreating = false;
|
||||
EditButton.Visibility = Visibility.Visible;
|
||||
ResetButton.Visibility = Visibility.Hidden;
|
||||
CancelButton.IsEnabled = false;
|
||||
SaveButton.IsEnabled = false;
|
||||
ResetButton.IsEnabled = false;
|
||||
ModifiersFinishEditing();
|
||||
|
||||
ClearInputStates();
|
||||
FillInputs(App.Client);
|
||||
LockInputs();
|
||||
}
|
||||
|
||||
private void ResetButton_Click(object sender, RoutedEventArgs evt) {
|
||||
ModifiersChanged = false;
|
||||
ModifiersInitEditing();
|
||||
ClearInputStates();
|
||||
FillInputs(App.Client);
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
private async void SaveButton_Click(object sender, RoutedEventArgs evt) {
|
||||
try {
|
||||
await UpdateClientParameters(App.Client);
|
||||
await ModifiersSave();
|
||||
} catch (Exception exc) {
|
||||
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
|
||||
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
|
||||
MessageBox.Show(str, "Stammdaten aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
|
||||
IsEditing = false;
|
||||
IsCreating = false;
|
||||
EditButton.Visibility = Visibility.Visible;
|
||||
ResetButton.Visibility = Visibility.Hidden;
|
||||
CancelButton.IsEnabled = false;
|
||||
SaveButton.IsEnabled = false;
|
||||
ResetButton.IsEnabled = false;
|
||||
ModifiersFinishEditing();
|
||||
|
||||
ClearInputStates();
|
||||
FillInputs(App.Client);
|
||||
LockInputs();
|
||||
}
|
||||
|
||||
private void FillInputs(ClientParameters p) {
|
||||
ClearOriginalValues();
|
||||
ClearDefaultValues();
|
||||
|
||||
ClientNameInput.Text = p.Name;
|
||||
ClientNameSuffixInput.Text = p.NameSuffix;
|
||||
ClientNameTypeInput.Text = p.NameType;
|
||||
ClientNameTokenInput.Text = p.NameToken;
|
||||
ClientNameShortInput.Text = p.NameShort;
|
||||
ClientAddressInput.Text = p.Address;
|
||||
ClientPlzInput.Text = p.Plz.ToString();
|
||||
ClientOrtInput.Text = p.Ort;
|
||||
ClientIbanInput.Text = p.Iban;
|
||||
ClientBicInput.Text = p.Bic;
|
||||
ClientUstIdNrInput.Text = p.UstIdNr;
|
||||
ClientLfbisNrInput.Text = p.LfbisNr;
|
||||
ClientPhoneNrInput.Text = p.PhoneNr;
|
||||
ClientFaxNrInput.Text = p.FaxNr;
|
||||
ClientEmailAddressInput.Text = p.EmailAddress;
|
||||
ClientWebsiteInput.Text = p.Website;
|
||||
|
||||
TextElementDeliveryNote.Text = p.TextDeliveryNote;
|
||||
switch (p.ModeDeliveryNoteStats) {
|
||||
case 0: ModeDeliveryNoteNone.IsChecked = true; break;
|
||||
case 1: ModeDeliveryNoteGaOnly.IsChecked = true; break;
|
||||
case 2: ModeDeliveryNoteShort.IsChecked = true; break;
|
||||
case 3: ModeDeliveryNoteFull.IsChecked = true; break;
|
||||
}
|
||||
TextElementDeliveryConfirmation.Text = p.TextDeliveryConfirmation;
|
||||
|
||||
FinishInputFilling();
|
||||
}
|
||||
|
||||
private async Task UpdateClientParameters(ClientParameters p) {
|
||||
p.Name = ClientNameInput.Text;
|
||||
p.NameSuffix = ClientNameSuffixInput.Text.Length > 0 ? ClientNameSuffixInput.Text : null;
|
||||
p.NameType = ClientNameTypeInput.Text;
|
||||
p.NameToken = ClientNameTokenInput.Text;
|
||||
p.NameShort = ClientNameShortInput.Text;
|
||||
p.Address = ClientAddressInput.Text;
|
||||
p.Plz = int.Parse(ClientPlzInput.Text);
|
||||
p.Ort = ClientOrtInput.Text;
|
||||
p.Iban = ClientIbanInput.Text.Length > 0 ? ClientIbanInput.Text : null;
|
||||
p.Bic = ClientBicInput.Text.Length > 0 ? ClientBicInput.Text : null;
|
||||
p.UstIdNr = ClientUstIdNrInput.Text.Length > 0 ? ClientUstIdNrInput.Text : null;
|
||||
p.LfbisNr = ClientLfbisNrInput.Text.Length > 0 ? ClientLfbisNrInput.Text : null;
|
||||
p.PhoneNr = ClientPhoneNrInput.Text.Length > 0 ? ClientPhoneNrInput.Text : null;
|
||||
p.FaxNr = ClientFaxNrInput.Text.Length > 0 ? ClientFaxNrInput.Text : null;
|
||||
p.EmailAddress = ClientEmailAddressInput.Text.Length > 0 ? ClientEmailAddressInput.Text : null;
|
||||
p.Website = ClientWebsiteInput.Text.Length > 0 ? ClientWebsiteInput.Text : null;
|
||||
|
||||
p.TextDeliveryNote = TextElementDeliveryNote.Text.Length > 0 ? TextElementDeliveryNote.Text : null;
|
||||
p.ModeDeliveryNoteStats = (ModeDeliveryNoteNone.IsChecked == true) ? 0 : (ModeDeliveryNoteGaOnly.IsChecked == true) ? 1 : (ModeDeliveryNoteShort.IsChecked == true) ? 2 : (ModeDeliveryNoteFull.IsChecked == true) ? 3 : 2;
|
||||
p.TextDeliveryConfirmation = TextElementDeliveryConfirmation.Text.Length > 0 ? TextElementDeliveryConfirmation.Text : null;
|
||||
|
||||
await p.UpdateValues();
|
||||
}
|
||||
|
||||
private void SeasonModifierUpButton_Click(object sender, RoutedEventArgs evt) {
|
||||
if (ModifierList == null) return;
|
||||
ModifiersChanged = true;
|
||||
var idx = SeasonModifierList.SelectedIndex;
|
||||
var item = ModifierList[idx];
|
||||
ModifierList.RemoveAt(idx);
|
||||
idx--;
|
||||
ModifierList.Insert(idx, item);
|
||||
SeasonModifierList.SelectedIndex = idx;
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
private void SeasonModifierDownButton_Click(object sender, RoutedEventArgs evt) {
|
||||
if (ModifierList == null) return;
|
||||
ModifiersChanged = true;
|
||||
var idx = SeasonModifierList.SelectedIndex;
|
||||
var item = ModifierList[idx];
|
||||
ModifierList.RemoveAt(idx);
|
||||
idx++;
|
||||
ModifierList.Insert(idx, item);
|
||||
SeasonModifierList.SelectedIndex = idx;
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
private void SeasonModifierAddButton_Click(object sender, RoutedEventArgs evt) {
|
||||
if (ModifierList == null || SeasonList.SelectedItem is not Season s) return;
|
||||
ModifiersChanged = true;
|
||||
var idx = (SeasonModifierList.SelectedIndex != -1) ? SeasonModifierList.SelectedIndex + 1 : ModifierList.Count;
|
||||
var item = Context.CreateProxy<Modifier>();
|
||||
item.Year = s.Year;
|
||||
ModifierList.Insert(idx, item);
|
||||
SeasonModifierList.SelectedIndex = idx;
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
private void SeasonModifierDeleteButton_Click(object sender, RoutedEventArgs evt) {
|
||||
if (ModifierList == null || Modifiers == null) return;
|
||||
ModifiersChanged = true;
|
||||
var idx = SeasonModifierList.SelectedIndex;
|
||||
var item = ModifierList[idx];
|
||||
Modifiers[item.ModId] = null;
|
||||
ModifierList.RemoveAt(idx);
|
||||
SeasonModifierList.SelectedIndex = idx < ModifierList.Count ? idx : idx - 1;
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
|
||||
private void ClientNames_TextChanged(object sender, TextChangedEventArgs evt) {
|
||||
var suffix = ClientNameSuffixInput.Text.Length > 0 ? ClientNameSuffixInput.Text : null;
|
||||
ClientNameFull.Text = $"{ClientNameInput.Text}{(suffix != null ? $", {suffix}," : "")} {ClientNameTypeInput.Text}";
|
||||
TextBox_TextChanged(sender, evt);
|
||||
}
|
||||
|
||||
private async void SeasonList_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
|
||||
var year = (SeasonList.SelectedItem as Season)?.Year;
|
||||
SeasonModifierList.ItemsSource = await Context.Modifiers.Where(m => m.Year == year).OrderBy(m => m.Ordering).ToListAsync();
|
||||
}
|
||||
|
||||
private void SeasonModifierList_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
|
||||
UpdateButtons();
|
||||
ModifierUpdate = true;
|
||||
if (SeasonModifierList.SelectedItem is not Modifier mod) {
|
||||
SeasonModifierIdInput.Text = "";
|
||||
SeasonModifierNameInput.Text = "";
|
||||
SeasonModifierRelInput.Text = "";
|
||||
SeasonModifierAbsInput.Text = "";
|
||||
} else {
|
||||
SeasonModifierIdInput.Text = mod.ModId;
|
||||
SeasonModifierNameInput.Text = mod.Name;
|
||||
SeasonModifierRelInput.Text = (mod.Rel * 100)?.ToString();
|
||||
SeasonModifierAbsInput.Text = mod.Abs?.ToString();
|
||||
}
|
||||
ModifierUpdate = false;
|
||||
}
|
||||
|
||||
private void SeasonModifierIdInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
||||
if ((!IsEditing && !IsCreating) || SeasonModifierList.SelectedItem is not Modifier mod || Modifiers == null || ModifierIds == null) return;
|
||||
ModifiersChanged = ModifiersChanged || (SeasonModifierIdInput.Text ?? "") != ((SeasonModifierList.SelectedItem as Modifier)?.ModId ?? "");
|
||||
if (ModifierUpdate) return;
|
||||
var old = ModifierIds.GetValueOrDefault(mod);
|
||||
var id = SeasonModifierIdInput.Text ?? "";
|
||||
if (old != null) Modifiers[old] = id;
|
||||
mod.ModId = id;
|
||||
CollectionViewSource.GetDefaultView(ModifierList).Refresh();
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
private void SeasonModifierNameInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
||||
if ((!IsEditing && !IsCreating) || SeasonModifierList.SelectedItem is not Modifier mod) return;
|
||||
ModifiersChanged = ModifiersChanged || (SeasonModifierNameInput.Text ?? "") != ((SeasonModifierList.SelectedItem as Modifier)?.Name ?? "");
|
||||
if (ModifierUpdate) return;
|
||||
mod.Name = SeasonModifierNameInput.Text ?? "";
|
||||
CollectionViewSource.GetDefaultView(ModifierList).Refresh();
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
private void SeasonModifierRelInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
||||
// DecimalInput_TextChanged(sender, evt); FIXME '-' is ignored
|
||||
if ((!IsEditing && !IsCreating) || SeasonModifierList.SelectedItem is not Modifier mod) return;
|
||||
ModifiersChanged = ModifiersChanged || (SeasonModifierRelInput.Text ?? "") != ((SeasonModifierList.SelectedItem as Modifier)?.Rel?.ToString() ?? "");
|
||||
if (ModifierUpdate) return;
|
||||
mod.Rel = decimal.TryParse(SeasonModifierRelInput.Text, out var v) ? v / 100 : null;
|
||||
if (mod.Rel != null) SeasonModifierAbsInput.Text = "";
|
||||
CollectionViewSource.GetDefaultView(ModifierList).Refresh();
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
private void SeasonModifierAbsInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
||||
// DecimalInput_TextChanged(sender, evt); FIXME '-' is ignored
|
||||
if ((!IsEditing && !IsCreating) || SeasonModifierList.SelectedItem is not Modifier mod || SeasonList.SelectedItem is not Season s) return;
|
||||
ModifiersChanged = ModifiersChanged || (SeasonModifierAbsInput.Text ?? "") != ((SeasonModifierList.SelectedItem as Modifier)?.Abs?.ToString() ?? "");
|
||||
if (ModifierUpdate) return;
|
||||
// FIXME ValueStr does not work in ModifierList when modifier is newly created
|
||||
mod.AbsValue = decimal.TryParse(SeasonModifierAbsInput.Text, out var v) ? Utils.DecToDb(v, s.Precision) : null;
|
||||
if (mod.AbsValue != null) SeasonModifierRelInput.Text = "";
|
||||
CollectionViewSource.GetDefaultView(ModifierList).Refresh();
|
||||
UpdateButtons();
|
||||
}
|
||||
}
|
||||
}
|
188
Elwig/Windows/ChartWindow.xaml
Normal file
188
Elwig/Windows/ChartWindow.xaml
Normal file
@@ -0,0 +1,188 @@
|
||||
<local:AdministrationWindow x:Class="Elwig.Windows.ChartWindow"
|
||||
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"
|
||||
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
|
||||
xmlns:ScottPlot="clr-namespace:ScottPlot;assembly=ScottPlot.WPF"
|
||||
mc:Ignorable="d"
|
||||
Title="Auszahlung - Elwig" Height="700" Width="1500"
|
||||
Loaded="Window_Loaded">
|
||||
|
||||
<Window.Resources>
|
||||
<Style TargetType="Label">
|
||||
<Setter Property="HorizontalAlignment" Value="Left"/>
|
||||
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||
<Setter Property="Padding" Value="2,4,2,4"/>
|
||||
<Setter Property="Height" Value="25"/>
|
||||
</Style>
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="Padding" Value="2"/>
|
||||
<Setter Property="IsReadOnly" Value="True"/>
|
||||
<Setter Property="Height" Value="25"/>
|
||||
<Setter Property="TextWrapping" Value="NoWrap"/>
|
||||
</Style>
|
||||
<Style TargetType="ComboBox">
|
||||
<Setter Property="IsEnabled" Value="False"/>
|
||||
<Setter Property="Height" Value="25"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||
</Style>
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="Padding" Value="9,3"/>
|
||||
<Setter Property="Height" Value="27"/>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="19"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="330"/>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="200"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.Row="1" Margin="5,0,0,0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="42"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<DataGrid x:Name="GraphList" AutoGenerateColumns="False" HeadersVisibility="Column" IsReadOnly="True" GridLinesVisibility="None" SelectionMode="Single"
|
||||
CanUserDeleteRows="False" CanUserResizeRows="False" CanUserAddRows="False"
|
||||
SelectionChanged="GraphList_SelectionChanged"
|
||||
Margin="5,15,5,0" Grid.Row="0" FontSize="14" Grid.ColumnSpan="3">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Nr." Binding="{Binding Num}" Width="40">
|
||||
<DataGridTextColumn.ElementStyle>
|
||||
<Style>
|
||||
<Setter Property="TextBlock.TextWrapping" Value="Wrap" />
|
||||
</Style>
|
||||
</DataGridTextColumn.ElementStyle>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Header="Typ" Binding="{Binding Type}" Width="40"/>
|
||||
<DataGridTextColumn Header="Angewandte Verträge" Binding="{Binding Contracts}" Width="4*"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
<Button x:Name="NewButton" Content="Neu"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2"
|
||||
Click="NewButton_Click"/>
|
||||
<Button x:Name="EditButton" Content="Bearbeiten" IsEnabled="False"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2"
|
||||
Click="EditButton_Click"/>
|
||||
<Button x:Name="DeleteButton" Content="Löschen" IsEnabled="False"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2"
|
||||
Click="DeleteButton_Click"/>
|
||||
|
||||
<Button x:Name="SaveButton" Content="Speichern" IsEnabled="False" Visibility="Hidden"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2"
|
||||
Click="SaveButton_Click"/>
|
||||
<Button x:Name="ResetButton" Content="Zurücksetzen" IsEnabled="False" Visibility="Hidden"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2"
|
||||
Click="ResetButton_Click"/>
|
||||
<Button x:Name="CancelButton" Content="Abbrechen" IsEnabled="False" Visibility="Hidden" IsCancel="True"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2"
|
||||
Click="CancelButton_Click"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="1" Grid.Column="1">
|
||||
<ScottPlot:WpfPlot x:Name="OechslePricePlot" MouseMove="OechslePricePlot_MouseMove" MouseDown="OechslePricePlot_MouseDown" IsEnabled="False"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="1" Grid.Column="2" Margin="0,0,5,0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="120"/>
|
||||
<RowDefinition Height="120"/>
|
||||
<RowDefinition Height="210"/>
|
||||
<RowDefinition Height="110"/>
|
||||
<RowDefinition Height="42"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<GroupBox Header="Graph" Grid.Row="0" Margin="0,5,5,5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="85"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Content="Nummer:" Margin="10,10,0,0" Grid.Column="0"/>
|
||||
<TextBox x:Name="GraphNumberInput" Grid.Column="1" HorizontalAlignment="Left" Margin="0,10,0,0" Text="" Width="90" TextChanged="GraphNumberInput_TextChanged" LostFocus="GraphNumberInput_LostFocus"/>
|
||||
|
||||
<Label Content="Typ:" Margin="10,45,0,0" Grid.Column="0"/>
|
||||
<RadioButton x:Name="OechsleGraphType_Input" GroupName="GraphType" Grid.Column="1" Margin="0,45,0,0">Oechsle</RadioButton>
|
||||
<RadioButton x:Name="KmwGraphType_Input" GroupName="GraphType" Grid.Column="1" Margin="0,60,0,0">KMW</RadioButton>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="Datenpunkt" Grid.Row="1" Margin="0,5,5,5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="85"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Content="Oechsle:" Margin="10,10,0,0" Grid.Column="0"/>
|
||||
<TextBox x:Name="OechsleInput" Grid.Column="1" HorizontalAlignment="Left" Margin="0,10,0,0" Text="" Width="90" TextChanged="OechsleInput_TextChanged" LostFocus="OechsleInput_LostFocus"/>
|
||||
|
||||
<Label Content="Preis pro kg:" Margin="10,40,0,0" Grid.Column="0"/>
|
||||
<TextBox x:Name="PriceInput" Grid.Column="1" HorizontalAlignment="Left" Margin="0,40,0,0" Text="" Width="90" TextChanged="PriceInput_TextChanged" LostFocus="PriceInput_LostFocus"/>
|
||||
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="Aktionen" Grid.Row="2" Margin="0,5,5,5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button x:Name="LeftFlatButton" Content="Links flach" Click="LeftFlatButton_Click" IsEnabled="False"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="10,10,10,10"/>
|
||||
|
||||
<Button x:Name="RightFlatButton" Content="Rechts flach" Click="RightFlatButton_Click" IsEnabled="False"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="10,50,10,10"/>
|
||||
|
||||
<Button x:Name="InterpolateButton" Content="Interpolieren" Click="InterpolateButton_Click" IsEnabled="False"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="10,90,10,10"/>
|
||||
|
||||
<Button x:Name="LinearIncreaseButton" Content="Linear wachsen" Click="LinearIncreaseButton_Click" IsEnabled="False"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="10,130,10,10"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="Optionen" Grid.Row="3" Margin="0,5,5,5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<CheckBox x:Name="FreeZoomInput" Content="Freier Zoom" IsEnabled="False"
|
||||
Checked="FreeZoomInput_Changed" Unchecked="FreeZoomInput_Changed"
|
||||
HorizontalAlignment="Left" Margin="10,10,10,0" VerticalAlignment="Top"/>
|
||||
|
||||
<CheckBox x:Name="GradationLinesInput" Content="Gradationslinien anzeigen" IsEnabled="False"
|
||||
Checked="GradationLinesInput_Changed" Unchecked="GradationLinesInput_Changed"
|
||||
HorizontalAlignment="Left" Margin="10,30,10,0" VerticalAlignment="Top" IsChecked="True"/>
|
||||
|
||||
<CheckBox x:Name="TooltipInput" Content="Tooltips anzeigen" IsEnabled="False"
|
||||
HorizontalAlignment="Left" Margin="10,50,10,0" VerticalAlignment="Top" IsChecked="True"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</local:AdministrationWindow>
|
749
Elwig/Windows/ChartWindow.xaml.cs
Normal file
749
Elwig/Windows/ChartWindow.xaml.cs
Normal file
@@ -0,0 +1,749 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using Elwig.Helpers;
|
||||
using Elwig.Helpers.Billing;
|
||||
using Elwig.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||
using ScottPlot;
|
||||
using ScottPlot.Plottable;
|
||||
|
||||
namespace Elwig.Windows {
|
||||
public partial class ChartWindow : AdministrationWindow {
|
||||
|
||||
private readonly int Year = 2021;
|
||||
private readonly int AvNr = 2;
|
||||
|
||||
private ScatterPlot OechslePricePlotScatter;
|
||||
private MarkerPlot HighlightedPoint;
|
||||
private MarkerPlot PrimaryMarkedPoint;
|
||||
private MarkerPlot SecondaryMarkedPoint;
|
||||
private Tooltip Tooltip;
|
||||
|
||||
private int LastHighlightedIndex = -1;
|
||||
private int HighlightedIndex = -1;
|
||||
private int PrimaryMarkedPointIndex = -1;
|
||||
private int SecondaryMarkedPointIndex = -1;
|
||||
private bool HoverChanged = false;
|
||||
private bool HoverActive = false;
|
||||
|
||||
private const int MinOechsle = 50;
|
||||
private const int MaxOechsle = 140;
|
||||
|
||||
private Graph? Graph;
|
||||
|
||||
public ChartWindow() {
|
||||
InitializeComponent();
|
||||
|
||||
ExemptInputs = new Control[] {
|
||||
GraphList, OechsleInput, PriceInput, FreeZoomInput, GradationLinesInput, TooltipInput
|
||||
};
|
||||
}
|
||||
|
||||
private void Window_Loaded(object sender, RoutedEventArgs evt) {
|
||||
LockInputs();
|
||||
OechslePricePlot.IsEnabled = false;
|
||||
}
|
||||
|
||||
private async Task RefreshGraphList() {
|
||||
await Context.PaymentVariants.LoadAsync();
|
||||
await RefreshGraphListQuery();
|
||||
}
|
||||
|
||||
private async Task RefreshGraphListQuery(bool updateSort = false) {
|
||||
List<PaymentVar> paymentVars = await Context.PaymentVariants.Where(p => p.Year == Year && p.AvNr == AvNr).ToListAsync();
|
||||
|
||||
if (paymentVars.Count != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
PaymentVar paymentVar = paymentVars[0];
|
||||
var data = JsonNode.Parse(paymentVar.Data).AsObject();
|
||||
|
||||
var auszahlungsSorten = data["AuszahlungSorten"]?.AsObject();
|
||||
if (auszahlungsSorten == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var Graphs = auszahlungsSorten["Kurven"]?.AsArray();
|
||||
|
||||
if (Graphs == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Graph> GraphsList = new();
|
||||
|
||||
int i = 1;
|
||||
foreach (var graph in Graphs) {
|
||||
GraphsList.Add(new Graph("Oe", i, graph?.AsObject(), ParseContracts(auszahlungsSorten, i - 1), 50, 140));
|
||||
i++;
|
||||
}
|
||||
|
||||
ControlUtils.RenewItemsSource(GraphList, GraphsList, g => (g as Graph)?.Num);
|
||||
if (GraphsList.Count == 1) {
|
||||
GraphList.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
RefreshInputs();
|
||||
}
|
||||
|
||||
private String ParseContracts(JsonObject auszahlungsSorten, int num) {
|
||||
List<string> contracts = new();
|
||||
|
||||
foreach (var sorte in auszahlungsSorten) {
|
||||
if (sorte.Key == "Kurven") continue;
|
||||
foreach (var attribut in sorte.Value.AsObject()) {
|
||||
foreach (var bindung in attribut.Value.AsObject()) {
|
||||
if ((int)bindung.Value.AsValue() == num) {
|
||||
contracts.Add($"{sorte.Key}/{attribut.Key}/{bindung.Key}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return string.Join("\n", contracts.ToArray());
|
||||
}
|
||||
|
||||
private async Task<bool> RemoveGraph(int num) {
|
||||
List<PaymentVar> paymentVars = await Context.PaymentVariants.Where(p => p.Year == Year && p.AvNr == AvNr).ToListAsync();
|
||||
|
||||
if (paymentVars.Count != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PaymentVar paymentVar = paymentVars[0];
|
||||
var data = JsonNode.Parse(paymentVar.Data).AsObject();
|
||||
|
||||
var auszahlungsSorten = data["AuszahlungSorten"]?.AsObject();
|
||||
if (auszahlungsSorten == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var Graphs = auszahlungsSorten["Kurven"]?.AsObject();
|
||||
if (Graphs == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int i = 1;
|
||||
foreach (var graph in Graphs) {
|
||||
if (i == num) {
|
||||
Graphs.Remove(graph.Key);
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
foreach (var sorte in auszahlungsSorten) {
|
||||
if (sorte.Key == "Kurven") continue;
|
||||
foreach (var attribut in sorte.Value.AsObject()) {
|
||||
var bindungen = attribut.Value.AsObject();
|
||||
foreach (var bindung in bindungen) {
|
||||
int v = (int)bindung.Value;
|
||||
if (v == num - 1) {
|
||||
bindungen.Remove(bindung.Key);
|
||||
} else if (v > num - 1) {
|
||||
bindungen[bindung.Key] = v - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EntityEntry<PaymentVar>? tr = null;
|
||||
try {
|
||||
paymentVar.Data = data.ToString();
|
||||
tr = Context.Update(paymentVar);
|
||||
|
||||
await Context.SaveChangesAsync();
|
||||
} catch (Exception exc) {
|
||||
if (tr != null) await tr.ReloadAsync();
|
||||
var str = "Der Eintrag konnte nicht in der Datenbank gelöscht werden!\n\n" + exc.Message;
|
||||
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
|
||||
MessageBox.Show(str, "Graph löschen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void RefreshInputs(bool validate = false) {
|
||||
ResetPlot();
|
||||
ClearInputStates();
|
||||
if (GraphList.SelectedItem is Graph g) {
|
||||
EditButton.IsEnabled = true;
|
||||
DeleteButton.IsEnabled = true;
|
||||
EnableOptionButtons();
|
||||
FillInputs(g);
|
||||
} else {
|
||||
EditButton.IsEnabled = false;
|
||||
DeleteButton.IsEnabled = false;
|
||||
DisableOptionButtons();
|
||||
ClearOriginalValues();
|
||||
ClearInputs(validate);
|
||||
ClearInputStates();
|
||||
}
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
private void FillInputs(Graph g) {
|
||||
ClearOriginalValues();
|
||||
|
||||
Graph = (Graph)g.Clone();
|
||||
|
||||
GraphNumberInput.Text = Graph.Num.ToString();
|
||||
if (Graph.Type == "oe") {
|
||||
OechsleGraphType_Input.IsChecked = true;
|
||||
} else if (Graph.Type == "kmw") {
|
||||
KmwGraphType_Input.IsChecked = true;
|
||||
}
|
||||
|
||||
InitPlot();
|
||||
OechslePricePlot.IsEnabled = true;
|
||||
|
||||
FinishInputFilling();
|
||||
}
|
||||
|
||||
private void InitInputs() {
|
||||
GraphNumberInput.Text = (GraphList.Items.Count + 1).ToString();
|
||||
OechsleGraphType_Input.IsChecked = true;
|
||||
FinishInputFilling();
|
||||
}
|
||||
|
||||
protected override async Task OnRenewContext() {
|
||||
await base.OnRenewContext();
|
||||
await RefreshGraphList();
|
||||
}
|
||||
|
||||
private void InitPlot() {
|
||||
OechslePricePlotScatter = OechslePricePlot.Plot.AddScatter(Graph.DataX, Graph.DataY);
|
||||
|
||||
OechslePricePlot.Configuration.DoubleClickBenchmark = false;
|
||||
OechslePricePlotScatter.LineColor = Color.Blue;
|
||||
OechslePricePlotScatter.MarkerColor = Color.Blue;
|
||||
OechslePricePlotScatter.MarkerSize = 9;
|
||||
|
||||
//OechslePricePlot.Plot.XAxis.ManualTickSpacing(1);
|
||||
OechslePricePlot.Plot.YAxis.ManualTickSpacing(0.1);
|
||||
OechslePricePlot.Plot.SetAxisLimits(MinOechsle - 1, MaxOechsle + 1, -0.1, 2);
|
||||
|
||||
OechslePricePlot.Plot.Layout(padding: 0);
|
||||
OechslePricePlot.Plot.XAxis2.Layout(padding: 0);
|
||||
OechslePricePlot.Plot.YAxis.Layout(padding: 0);
|
||||
OechslePricePlot.Plot.YAxis2.Layout(padding: 0);
|
||||
|
||||
HighlightedPoint = OechslePricePlot.Plot.AddPoint(0, 0);
|
||||
HighlightedPoint.Color = Color.Red;
|
||||
HighlightedPoint.MarkerSize = 10;
|
||||
HighlightedPoint.MarkerShape = MarkerShape.openCircle;
|
||||
HighlightedPoint.IsVisible = false;
|
||||
|
||||
PrimaryMarkedPoint = OechslePricePlot.Plot.AddPoint(0, 0);
|
||||
PrimaryMarkedPoint.Color = Color.Red;
|
||||
PrimaryMarkedPoint.MarkerSize = 6;
|
||||
PrimaryMarkedPoint.MarkerShape = MarkerShape.filledCircle;
|
||||
PrimaryMarkedPoint.IsVisible = false;
|
||||
|
||||
SecondaryMarkedPoint = OechslePricePlot.Plot.AddPoint(0, 0);
|
||||
SecondaryMarkedPoint.Color = Color.Red;
|
||||
SecondaryMarkedPoint.MarkerSize = 6;
|
||||
SecondaryMarkedPoint.MarkerShape = MarkerShape.filledCircle;
|
||||
SecondaryMarkedPoint.IsVisible = false;
|
||||
|
||||
OechslePricePlot.Refresh();
|
||||
|
||||
RefreshFreeZoom();
|
||||
RefreshGradationLines();
|
||||
}
|
||||
|
||||
private void ResetPlot() {
|
||||
Graph = null;
|
||||
PrimaryMarkedPointIndex = -1;
|
||||
OechslePricePlot.Plot.Remove(OechslePricePlotScatter);
|
||||
OechslePricePlot.Plot.Clear();
|
||||
OechslePricePlot.Reset();
|
||||
OechslePricePlot.Refresh();
|
||||
}
|
||||
|
||||
private void ChangeMarker(MarkerPlot point, bool visible, double x = 0, double y = 0) {
|
||||
point.X = x;
|
||||
point.Y = y;
|
||||
point.IsVisible = visible;
|
||||
}
|
||||
|
||||
private void FlattenGraph(int begin, int end, double value) {
|
||||
for (int i = begin; i <= end; i++) {
|
||||
Graph.DataY[i] = value;
|
||||
}
|
||||
OechslePricePlot.Render();
|
||||
}
|
||||
|
||||
private void LinearIncreaseGraph(int begin, int end, double inc) {
|
||||
for (int i = begin; i < end; i++) {
|
||||
Graph.DataY[i + 1] = Graph.DataY[i] + inc;
|
||||
}
|
||||
OechslePricePlot.Render();
|
||||
}
|
||||
|
||||
private void EnableActionButtons() {
|
||||
LeftFlatButton.IsEnabled = true;
|
||||
RightFlatButton.IsEnabled = true;
|
||||
LinearIncreaseButton.IsEnabled = true;
|
||||
}
|
||||
|
||||
private void DisableActionButtons() {
|
||||
LeftFlatButton.IsEnabled = false;
|
||||
RightFlatButton.IsEnabled = false;
|
||||
InterpolateButton.IsEnabled = false;
|
||||
LinearIncreaseButton.IsEnabled = false;
|
||||
}
|
||||
|
||||
private void FreeZoomInput_Changed(object sender, RoutedEventArgs evt) {
|
||||
RefreshFreeZoom();
|
||||
}
|
||||
|
||||
private void RefreshFreeZoom() {
|
||||
if (FreeZoomInput.IsChecked == true) {
|
||||
UnlockZoom();
|
||||
} else {
|
||||
LockZoom();
|
||||
}
|
||||
OechslePricePlot.Refresh();
|
||||
}
|
||||
|
||||
private void LockZoom() {
|
||||
OechslePricePlot.Plot.XAxis.SetBoundary(MinOechsle - 1, MaxOechsle + 1);
|
||||
OechslePricePlot.Plot.YAxis.SetBoundary(-0.1, 2);
|
||||
OechslePricePlot.Plot.XAxis.SetZoomOutLimit(MaxOechsle - MinOechsle + 2);
|
||||
OechslePricePlot.Plot.YAxis.SetZoomOutLimit(2.1);
|
||||
OechslePricePlot.Plot.SetAxisLimits(MinOechsle - 1, MaxOechsle + 1, -0.1, 2);
|
||||
}
|
||||
|
||||
private void UnlockZoom() {
|
||||
OechslePricePlot.Plot.XAxis.SetBoundary();
|
||||
OechslePricePlot.Plot.YAxis.SetBoundary();
|
||||
OechslePricePlot.Plot.XAxis.SetZoomOutLimit((MaxOechsle - MinOechsle) * 1.5);
|
||||
OechslePricePlot.Plot.YAxis.SetZoomOutLimit(3.5);
|
||||
}
|
||||
|
||||
private void EnableOptionButtons() {
|
||||
FreeZoomInput.IsEnabled = true;
|
||||
GradationLinesInput.IsEnabled = true;
|
||||
TooltipInput.IsEnabled = true;
|
||||
}
|
||||
|
||||
private void DisableOptionButtons() {
|
||||
FreeZoomInput.IsEnabled = false;
|
||||
GradationLinesInput.IsEnabled = false;
|
||||
TooltipInput.IsEnabled = false;
|
||||
}
|
||||
|
||||
private void GradationLinesInput_Changed(object sender, RoutedEventArgs evt) {
|
||||
RefreshGradationLines();
|
||||
}
|
||||
|
||||
private void RefreshGradationLines() {
|
||||
if (GradationLinesInput.IsChecked == true) {
|
||||
ShowGradationLines();
|
||||
ShowLegend();
|
||||
} else {
|
||||
HideGradationLines();
|
||||
HideLegend();
|
||||
}
|
||||
OechslePricePlot.Refresh();
|
||||
}
|
||||
|
||||
private void ShowGradationLines() {
|
||||
OechslePricePlot.Plot.AddVerticalLine(68, Color.Red, 2, label: "68 Oechsle (LDW)");
|
||||
OechslePricePlot.Plot.AddVerticalLine(73, Color.Orange, 2, label: "73 Oechsle (QUW)");
|
||||
OechslePricePlot.Plot.AddVerticalLine(84, Color.Green, 2, label: "84 Oechsle (KAB)");
|
||||
}
|
||||
|
||||
private void HideGradationLines() {
|
||||
OechslePricePlot.Plot.Clear(typeof(VLine));
|
||||
}
|
||||
|
||||
private void ShowLegend() {
|
||||
OechslePricePlot.Plot.Legend(true, Alignment.UpperRight);
|
||||
}
|
||||
|
||||
private void HideLegend() {
|
||||
OechslePricePlot.Plot.Legend(false, Alignment.UpperRight);
|
||||
}
|
||||
|
||||
private void OechsleInput_TextChanged(object sender, RoutedEventArgs evt) {
|
||||
IntegerInput_TextChanged(sender, evt);
|
||||
|
||||
bool success = int.TryParse(OechsleInput.Text, out int oechsle);
|
||||
|
||||
SecondaryMarkedPointIndex = -1;
|
||||
ChangeMarker(SecondaryMarkedPoint, false);
|
||||
|
||||
if (success) {
|
||||
if (oechsle >= MinOechsle && oechsle <= MaxOechsle) {
|
||||
PrimaryMarkedPointIndex = oechsle - MinOechsle;
|
||||
ChangeMarker(PrimaryMarkedPoint, true, Graph.DataX[PrimaryMarkedPointIndex], Graph.DataY[PrimaryMarkedPointIndex]);
|
||||
|
||||
PriceInput.Text = Graph.DataY[PrimaryMarkedPointIndex].ToString();
|
||||
|
||||
if (IsEditing || IsCreating) EnableActionButtons();
|
||||
OechslePricePlot.Render();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PrimaryMarkedPointIndex = -1;
|
||||
ChangeMarker(PrimaryMarkedPoint, false);
|
||||
DisableActionButtons();
|
||||
PriceInput.Text = "";
|
||||
OechslePricePlot.Render();
|
||||
}
|
||||
|
||||
private void PriceInput_TextChanged(object sender, RoutedEventArgs evt) {
|
||||
if (PrimaryMarkedPointIndex != -1) {
|
||||
bool success = Double.TryParse(PriceInput.Text, out double price);
|
||||
|
||||
if (success) {
|
||||
Graph.DataY[PrimaryMarkedPointIndex] = price;
|
||||
PrimaryMarkedPoint.Y = price;
|
||||
SaveButton.IsEnabled = true;
|
||||
ResetButton.IsEnabled = true;
|
||||
OechslePricePlot.Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LeftFlatButton_Click(object sender, RoutedEventArgs evt) {
|
||||
if (PrimaryMarkedPointIndex == -1) {
|
||||
return;
|
||||
}
|
||||
FlattenGraph(0, PrimaryMarkedPointIndex, Graph.DataY[PrimaryMarkedPointIndex]);
|
||||
SaveButton.IsEnabled = true;
|
||||
ResetButton.IsEnabled = true;
|
||||
}
|
||||
|
||||
private void RightFlatButton_Click(object sender, RoutedEventArgs evt) {
|
||||
if (PrimaryMarkedPointIndex == -1) {
|
||||
return;
|
||||
}
|
||||
FlattenGraph(PrimaryMarkedPointIndex, Graph.DataY.Length - 1, Graph.DataY[PrimaryMarkedPointIndex]);
|
||||
SaveButton.IsEnabled = true;
|
||||
ResetButton.IsEnabled = true;
|
||||
}
|
||||
|
||||
private void InterpolateButton_Click(object sender, RoutedEventArgs evt) {
|
||||
int steps = Math.Abs(PrimaryMarkedPointIndex - SecondaryMarkedPointIndex);
|
||||
if (PrimaryMarkedPointIndex == -1 || SecondaryMarkedPointIndex == -1 || steps < 2) {
|
||||
return;
|
||||
}
|
||||
var (lowIndex, highIndex) = PrimaryMarkedPointIndex < SecondaryMarkedPointIndex ? (PrimaryMarkedPointIndex, SecondaryMarkedPointIndex): (SecondaryMarkedPointIndex, PrimaryMarkedPointIndex);
|
||||
|
||||
double step = (Graph.DataY[highIndex] - Graph.DataY[lowIndex]) / steps;
|
||||
|
||||
for (int i = lowIndex; i < highIndex - 1; i++) {
|
||||
Graph.DataY[i + 1] = Math.Round(Graph.DataY[i] + step, 4); // TODO richtig runden
|
||||
}
|
||||
SaveButton.IsEnabled = true;
|
||||
ResetButton.IsEnabled = true;
|
||||
}
|
||||
|
||||
private void LinearIncreaseButton_Click(object sender, RoutedEventArgs e) {
|
||||
if (PrimaryMarkedPointIndex == -1) {
|
||||
return;
|
||||
}
|
||||
double? priceIncrease = Utils.ShowLinearPriceIncreaseDialog();
|
||||
if (priceIncrease == null) {
|
||||
return;
|
||||
}
|
||||
LinearIncreaseGraph(PrimaryMarkedPointIndex, Graph.DataY.Length - 1, priceIncrease.Value);
|
||||
SaveButton.IsEnabled = true;
|
||||
ResetButton.IsEnabled = true;
|
||||
}
|
||||
|
||||
private void OechslePricePlot_MouseDown(object sender, MouseEventArgs e) {
|
||||
if (!IsCreating && GraphList.SelectedItem == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (HoverActive) {
|
||||
if ((IsEditing || IsCreating) && Keyboard.IsKeyDown(Key.LeftCtrl)) {
|
||||
if (PrimaryMarkedPointIndex == -1) {
|
||||
return;
|
||||
}
|
||||
SecondaryMarkedPointIndex = HighlightedIndex;
|
||||
ChangeMarker(SecondaryMarkedPoint, true, Graph.DataX[SecondaryMarkedPointIndex], Graph.DataY[SecondaryMarkedPointIndex]);
|
||||
|
||||
InterpolateButton.IsEnabled = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
PrimaryMarkedPointIndex = HighlightedIndex;
|
||||
ChangeMarker(PrimaryMarkedPoint, true, Graph.DataX[PrimaryMarkedPointIndex], Graph.DataY[PrimaryMarkedPointIndex]);
|
||||
|
||||
OechsleInput.Text = Graph.DataX[HighlightedIndex].ToString();
|
||||
PriceInput.Text = Graph.DataY[HighlightedIndex].ToString();
|
||||
|
||||
if (IsEditing || IsCreating) {
|
||||
EnableActionButtons();
|
||||
}
|
||||
} else {
|
||||
PrimaryMarkedPointIndex = -1;
|
||||
SecondaryMarkedPointIndex = -1;
|
||||
ChangeMarker(PrimaryMarkedPoint, false);
|
||||
ChangeMarker(SecondaryMarkedPoint, false);
|
||||
|
||||
OechsleInput.Text = "";
|
||||
PriceInput.Text = "";
|
||||
|
||||
DisableActionButtons();
|
||||
}
|
||||
}
|
||||
|
||||
private void OechslePricePlot_MouseMove(object sender, MouseEventArgs e) {
|
||||
if (!IsCreating && GraphList.SelectedItem == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
(double mouseCoordX, double mouseCoordY) = OechslePricePlot.GetMouseCoordinates();
|
||||
double xyRatio = OechslePricePlot.Plot.XAxis.Dims.PxPerUnit / OechslePricePlot.Plot.YAxis.Dims.PxPerUnit;
|
||||
(double pointX, double pointY, int pointIndex) = OechslePricePlotScatter.GetPointNearest(mouseCoordX, mouseCoordY, xyRatio);
|
||||
|
||||
(double mousePixelX, double mousePixelY) = OechslePricePlot.GetMousePixel();
|
||||
(double pointPixelX, double pointPixelY) = OechslePricePlot.Plot.GetPixel(pointX, pointY);
|
||||
|
||||
HighlightedIndex = LastHighlightedIndex;
|
||||
|
||||
if (Math.Abs(mousePixelX - pointPixelX) < 3 && Math.Abs(mousePixelY - pointPixelY) < 3) {
|
||||
ChangeMarker(HighlightedPoint, true, pointX, pointY);
|
||||
HighlightedPoint.IsVisible = true;
|
||||
HoverChanged = true ^ HoverActive;
|
||||
HoverActive = true;
|
||||
} else {
|
||||
ChangeMarker(HighlightedPoint, false);
|
||||
HoverChanged= false ^ HoverActive;
|
||||
HoverActive= false;
|
||||
OechslePricePlot.Plot.Remove(Tooltip);
|
||||
OechslePricePlot.Render();
|
||||
}
|
||||
|
||||
if (LastHighlightedIndex != HighlightedIndex || HoverChanged) {
|
||||
OechslePricePlot.Plot.Remove(Tooltip);
|
||||
if (TooltipInput.IsChecked == true) {
|
||||
Tooltip = OechslePricePlot.Plot.AddTooltip($"Oechsle: {pointX:N2}, Preis: {Math.Round(pointY, 4)})", pointX, pointY);
|
||||
}
|
||||
LastHighlightedIndex = pointIndex;
|
||||
HoverChanged = false;
|
||||
OechslePricePlot.Render();
|
||||
}
|
||||
}
|
||||
|
||||
override protected void UpdateButtons() {
|
||||
if (!IsEditing && !IsCreating) return;
|
||||
bool ch = HasChanged, v = IsValid;
|
||||
}
|
||||
|
||||
private void DisableNewEditDeleteButtons() {
|
||||
NewButton.IsEnabled = false;
|
||||
EditButton.IsEnabled = false;
|
||||
DeleteButton.IsEnabled = false;
|
||||
}
|
||||
|
||||
private void EnableNewEditDeleteButtons() {
|
||||
NewButton.IsEnabled = true;
|
||||
EditButton.IsEnabled = GraphList.SelectedItem != null;
|
||||
DeleteButton.IsEnabled = GraphList.SelectedItem != null;
|
||||
}
|
||||
|
||||
private void ShowSaveResetCancelButtons() {
|
||||
SaveButton.IsEnabled = false;
|
||||
ResetButton.IsEnabled = false;
|
||||
CancelButton.IsEnabled = true;
|
||||
SaveButton.Visibility = Visibility.Visible;
|
||||
ResetButton.Visibility = Visibility.Visible;
|
||||
CancelButton.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
private void HideSaveResetCancelButtons() {
|
||||
SaveButton.IsEnabled = false;
|
||||
ResetButton.IsEnabled = false;
|
||||
CancelButton.IsEnabled = false;
|
||||
SaveButton.Visibility = Visibility.Hidden;
|
||||
ResetButton.Visibility = Visibility.Hidden;
|
||||
CancelButton.Visibility = Visibility.Hidden;
|
||||
}
|
||||
private void ShowNewEditDeleteButtons() {
|
||||
EnableNewEditDeleteButtons();
|
||||
NewButton.Visibility = Visibility.Visible;
|
||||
EditButton.Visibility = Visibility.Visible;
|
||||
DeleteButton.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
private void HideNewEditDeleteButtons() {
|
||||
DisableNewEditDeleteButtons();
|
||||
NewButton.Visibility = Visibility.Hidden;
|
||||
EditButton.Visibility = Visibility.Hidden;
|
||||
DeleteButton.Visibility = Visibility.Hidden;
|
||||
}
|
||||
|
||||
private void NewButton_Click(object sender, RoutedEventArgs e) {
|
||||
IsCreating = true;
|
||||
GraphList.IsEnabled = false;
|
||||
GraphList.SelectedItem = null;
|
||||
HideNewEditDeleteButtons();
|
||||
ShowSaveResetCancelButtons();
|
||||
UnlockInputs();
|
||||
PriceInput.IsReadOnly = false;
|
||||
OechsleInput.IsReadOnly = false;
|
||||
InitInputs();
|
||||
FillInputs(new Graph(GraphList.Items.Count + 1, MinOechsle, MaxOechsle));
|
||||
EnableOptionButtons();
|
||||
}
|
||||
|
||||
private void EditButton_Click(object sender, RoutedEventArgs e) {
|
||||
if (GraphList.SelectedItem == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
IsEditing = true;
|
||||
GraphList.IsEnabled = false;
|
||||
|
||||
HideNewEditDeleteButtons();
|
||||
ShowSaveResetCancelButtons();
|
||||
UnlockInputs();
|
||||
PriceInput.IsReadOnly = false;
|
||||
OechsleInput.IsReadOnly = false;
|
||||
if (PrimaryMarkedPointIndex != -1) EnableActionButtons();
|
||||
}
|
||||
|
||||
private async void DeleteButton_Click(object sender, RoutedEventArgs e) {
|
||||
Graph g = (Graph)GraphList.SelectedItem;
|
||||
if (g == null) return;
|
||||
|
||||
var r = MessageBox.Show(
|
||||
$"Soll der Graph {g.Num} (verwendet in folgenden Verträgen: {g.Contracts}) wirklich unwiderruflich gelöscht werden?",
|
||||
"Graph löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
|
||||
if (r == MessageBoxResult.Yes) {
|
||||
bool success = await RemoveGraph(g.Num);
|
||||
if (!success) {
|
||||
MessageBox.Show("Der Graph konnte nicht gelöscht werden", "Graph löschen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
await RefreshGraphList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async void SaveButton_Click(object sender, RoutedEventArgs e) {
|
||||
int? index = await UpdateGraph(Graph);
|
||||
if (index == null) {
|
||||
MessageBox.Show("Der Graph konnte nicht gespeichert werden", "Graph speichern", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
IsEditing = false;
|
||||
IsCreating = false;
|
||||
GraphList.IsEnabled = true;
|
||||
HideSaveResetCancelButtons();
|
||||
ShowNewEditDeleteButtons();
|
||||
LockInputs();
|
||||
PriceInput.IsReadOnly = true;
|
||||
OechsleInput.IsReadOnly = true;
|
||||
await RefreshGraphList();
|
||||
GraphList.SelectedIndex = index.Value;
|
||||
}
|
||||
|
||||
private async Task<int?> UpdateGraph(Graph g) {
|
||||
List<PaymentVar> paymentVars = await Context.PaymentVariants.Where(p => p.Year == Year && p.AvNr == AvNr).ToListAsync();
|
||||
|
||||
if (paymentVars.Count != 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PaymentVar paymentVar = paymentVars[0];
|
||||
var data = JsonNode.Parse(paymentVar.Data).AsObject();
|
||||
|
||||
var auszahlungsSorten = data["AuszahlungSorten"];
|
||||
if (auszahlungsSorten == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var Graphs = auszahlungsSorten["Kurven"].AsArray();
|
||||
if (Graphs == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (IsEditing) {
|
||||
Graphs[g.Num - 1] = g.ToJson();
|
||||
} else if(IsCreating) {
|
||||
Graphs.Add(g.ToJson());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
EntityEntry<PaymentVar>? tr = null;
|
||||
try {
|
||||
paymentVar.Data = data.ToString();
|
||||
tr = Context.Update(paymentVar);
|
||||
|
||||
await Context.SaveChangesAsync();
|
||||
} catch (Exception exc) {
|
||||
if (tr != null) await tr.ReloadAsync();
|
||||
var str = "Der Eintrag konnte nicht in der Datenbank gelöscht werden!\n\n" + exc.Message;
|
||||
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
|
||||
MessageBox.Show(str, "Graph löschen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
|
||||
return g.Num - 1;
|
||||
}
|
||||
|
||||
private void ResetButton_Click(object sender, RoutedEventArgs e) {
|
||||
if (IsEditing) {
|
||||
RefreshInputs();
|
||||
} else if (IsCreating) {
|
||||
InitInputs();
|
||||
}
|
||||
UpdateButtons();
|
||||
}
|
||||
|
||||
private void CancelButton_Click(object sender, RoutedEventArgs e) {
|
||||
IsEditing = false;
|
||||
IsCreating = false;
|
||||
GraphList.IsEnabled = true;
|
||||
HideSaveResetCancelButtons();
|
||||
ShowNewEditDeleteButtons();
|
||||
DisableActionButtons();
|
||||
RefreshInputs();
|
||||
PriceInput.Text = "";
|
||||
OechsleInput.Text = "";
|
||||
ClearInputStates();
|
||||
LockInputs();
|
||||
PriceInput.IsReadOnly = true;
|
||||
OechsleInput.IsReadOnly = true;
|
||||
}
|
||||
|
||||
private void GraphList_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) {
|
||||
RefreshInputs();
|
||||
|
||||
//var x = OechslePricePlot.Plot.GetPlottables().OfType<ScatterPlot>();
|
||||
//MessageBox.Show($"SelectionChanged\nLength: {x.ToList().Count}, Ys: {string.Join(", ", ((ScatterPlot)x.First()).Ys)}");
|
||||
}
|
||||
|
||||
private void PriceInput_LostFocus(object sender, RoutedEventArgs e) {
|
||||
|
||||
}
|
||||
|
||||
private void OechsleInput_LostFocus(object sender, RoutedEventArgs e) {
|
||||
|
||||
}
|
||||
|
||||
private void GraphNumberInput_TextChanged(object sender, TextChangedEventArgs e) {
|
||||
|
||||
}
|
||||
|
||||
private void GraphNumberInput_LostFocus(object sender, RoutedEventArgs e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
<local:AdministrationWindow x:Class="Elwig.Windows.ClientParamWindow"
|
||||
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"
|
||||
Title="ClientParamWindow" Height="450" Width="800">
|
||||
<Grid>
|
||||
|
||||
</Grid>
|
||||
</local:AdministrationWindow>
|
@@ -1,18 +0,0 @@
|
||||
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elwig.Windows {
|
||||
public partial class ClientParamWindow : AdministrationWindow {
|
||||
public ClientParamWindow() {
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void UpdateButtons() {
|
||||
|
||||
}
|
||||
|
||||
protected override async Task RenewContext() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@@ -7,30 +7,32 @@ using System.Windows.Threading;
|
||||
namespace Elwig.Windows {
|
||||
public abstract class ContextWindow : Window {
|
||||
|
||||
public static readonly int RenewSec = 10;
|
||||
|
||||
protected AppDbContext Context { get; private set; }
|
||||
protected bool LockContext { get; set; } = false;
|
||||
|
||||
private readonly DispatcherTimer ContextRenewTimer;
|
||||
private static readonly int ContextRenewSec = 10;
|
||||
private readonly DispatcherTimer _timer;
|
||||
private bool _renewPending = false;
|
||||
|
||||
public ContextWindow() : base() {
|
||||
ContextRenewTimer = new DispatcherTimer();
|
||||
ContextRenewTimer.Tick += new EventHandler(OnRenewContext);
|
||||
ContextRenewTimer.Interval = new TimeSpan(0, 0, ContextRenewSec);
|
||||
ContextRenewTimer.Start();
|
||||
_timer = new DispatcherTimer();
|
||||
_timer.Tick += new EventHandler(OnShouldRenewContext);
|
||||
_timer.Interval = new TimeSpan(0, 0, RenewSec);
|
||||
_timer.Start();
|
||||
Context = new();
|
||||
Loaded += OnLoaded;
|
||||
}
|
||||
|
||||
private void OnRenewContext(object? sender, EventArgs evt) {
|
||||
if (LockContext || !Context.HasBackendChanged) return;
|
||||
Context.Dispose();
|
||||
Context = new();
|
||||
RenewContext();
|
||||
private async void OnShouldRenewContext(object? sender, EventArgs evt) {
|
||||
if (!Context.HasBackendChanged) return;
|
||||
_renewPending = true;
|
||||
if (LockContext) return;
|
||||
await RenewContext();
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs evt) {
|
||||
RenewContext();
|
||||
OnRenewContext().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs evt) {
|
||||
@@ -38,6 +40,14 @@ namespace Elwig.Windows {
|
||||
Context.Dispose();
|
||||
}
|
||||
|
||||
abstract protected Task RenewContext();
|
||||
protected async Task RenewContext() {
|
||||
if (!_renewPending) return;
|
||||
Context.Dispose();
|
||||
Context = new();
|
||||
await OnRenewContext();
|
||||
_renewPending = false;
|
||||
}
|
||||
|
||||
abstract protected Task OnRenewContext();
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Elwig.Windows"
|
||||
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
|
||||
Title="Lieferungsverwaltung - Elwig" Height="700" Width="1100" MinHeight="700" MinWidth="1000"
|
||||
Title="Lieferungen - Elwig" Height="720" Width="1100" MinHeight="720" MinWidth="1000"
|
||||
Loaded="Window_Loaded">
|
||||
<Window.Resources>
|
||||
<Style TargetType="Label">
|
||||
@@ -43,29 +43,42 @@
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="19"/>
|
||||
<RowDefinition Height="0.625*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="24"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*" MinWidth="400"/>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="5"/>
|
||||
<ColumnDefinition Width="2*" MinWidth="560"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Menu Grid.ColumnSpan="3" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
|
||||
<MenuItem Header="Lieferung">
|
||||
</MenuItem>
|
||||
<MenuItem Header="Drucken">
|
||||
<MenuItem Header="Lieferschein drucken"/>
|
||||
<MenuItem x:Name="Menu_Print_ShowDeliveryNote" Header="Lieferschein anzeigen" IsEnabled="False"
|
||||
Click="Menu_Print_ShowDeliveryNote_Click"/>
|
||||
<MenuItem x:Name="Menu_Print_PrintDeliveryNote" Header="Lieferschein drucken" IsEnabled="False"
|
||||
Click="Menu_Print_PrintDeliveryNote_Click"/>
|
||||
<MenuItem x:Name="Menu_Print_DeliveryJournal" Header="Lieferjournal">
|
||||
<MenuItem x:Name="Menu_Print_DeliveryJournal_ShowToday" Header="von heute anzeigen" IsEnabled="False" Tag="Print"
|
||||
Click="Menu_Print_DeliveryJournal_ShowToday_Click"/>
|
||||
<MenuItem x:Name="Menu_Print_DeliveryJournal_PrintToday" Header="von heute drucken" IsEnabled="False" Tag="Print"
|
||||
Click="Menu_Print_DeliveryJournal_PrintToday_Click"/>
|
||||
<MenuItem x:Name="Menu_Print_DeliveryJournal_ShowFilter" Header="aus Filtern anzeigen" IsEnabled="False" Tag="Print"
|
||||
Click="Menu_Print_DeliveryJournal_ShowFilter_Click"/>
|
||||
<MenuItem x:Name="Menu_Print_DeliveryJournal_PrintFilter" Header="aus Filtern drucken" IsEnabled="False" Tag="Print"
|
||||
Click="Menu_Print_DeliveryJournal_PrintFilter_Click"/>
|
||||
</MenuItem>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Werkzeuge">
|
||||
<MenuItem Header="Alle Lieferscheine überprüfen"/>
|
||||
<MenuItem Header="Exportieren">
|
||||
<MenuItem x:Name="Menu_Export_Bki" Header="Traubentransportscheinliste (BKI)"/>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Einstellungen">
|
||||
<MenuItem x:Name="Menu_Settings_EnableFreeEditing" Header="Freie Bearbeitung aktivieren"
|
||||
IsCheckable="True" Checked="Menu_Settings_EnableFreeEditing_Checked" Unchecked="Menu_Settings_EnableFreeEditing_Unchecked"/>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
|
||||
<Grid Grid.RowSpan="5" Grid.Row="1" Margin="5,0,5,0">
|
||||
<Grid Grid.Row="1" Margin="5,0,0,0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="42"/>
|
||||
<RowDefinition Height="*"/>
|
||||
@@ -77,263 +90,399 @@
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox x:Name="SearchInput" Grid.ColumnSpan="3" Margin="5,10,95,0" IsReadOnly="False"
|
||||
TextChanged="SearchInput_TextChanged"/>
|
||||
<TextBox x:Name="SearchInput" Grid.ColumnSpan="3" Margin="5,10,161,0" IsReadOnly="False"
|
||||
TextChanged="SearchInput_TextChanged"
|
||||
ToolTip="Lieferungen filtern und durchsuchen. Die Filter sind beliebig kombinierbar.

Filtern nach:
Sorte: z.B. GV, ZW, rr, sa, !gv (ausgenommen GV), ...
Qualitätsstufe: z.B. QUW, kab, ldw, ...
Gradation: z.B. >73, <15, 17-18, 15-, >17,5, 62-75, ...
Mitglied: z.B. 1234, 987, ...
Saison: z.B. 2020, >2015, 2017-2019, <2005, 2019-, ...
Zweigstelle: z.B. musterort, ...
Attribute: z.B. kabinett, !kabinett (alle außer kabinett), ...
Datum: z.B. 1.9., 15.9.-10.10., -15.10.2020, ...
Uhrzeit: z.B. 06:00-08:00, 18:00-, ...
Freitext: z.B. Lieferscheinnummern, "quw" (sucht nach dem Text "quw")"/>
|
||||
<xctk:IntegerUpDown Name="SeasonInput" Grid.ColumnSpan="3" Height="25" Width="56" FontSize="14" Minimum="1000" Maximum="9999"
|
||||
Margin="0,10,100,0" VerticalAlignment="Top" HorizontalAlignment="Right"
|
||||
ValueChanged="SeasonInput_ValueChanged"/>
|
||||
<CheckBox x:Name="TodayOnlyInput" Content="Nur heute"
|
||||
HorizontalAlignment="Right" Margin="0,7,14.125,0" VerticalAlignment="Top" Grid.Column="1" Grid.ColumnSpan="2"
|
||||
HorizontalAlignment="Right" Margin="0,7,18,0" VerticalAlignment="Top" Grid.Column="1" Grid.ColumnSpan="2"
|
||||
Checked="TodayOnlyInput_Changed" Unchecked="TodayOnlyInput_Changed"/>
|
||||
<CheckBox x:Name="SeasonOnlyInput" Content="Nur Saison" IsChecked="True"
|
||||
<CheckBox x:Name="AllSeasonsInput" Content="Jede Saison" IsEnabled="False"
|
||||
HorizontalAlignment="Right" Margin="0,24,10,0" VerticalAlignment="Top" Grid.Column="1" Grid.ColumnSpan="2"
|
||||
Checked="SeasonOnlyInput_Changed" Unchecked="SeasonOnlyInput_Changed"/>
|
||||
Checked="AllSeasonsInput_Changed" Unchecked="AllSeasonsInput_Changed"/>
|
||||
<DataGrid x:Name="DeliveryList" AutoGenerateColumns="False" HeadersVisibility="Column" IsReadOnly="True" GridLinesVisibility="None" SelectionMode="Single"
|
||||
SelectionChanged="DeliveryList_SelectionChanged"
|
||||
CanUserDeleteRows="False" CanUserResizeRows="False" CanUserAddRows="False"
|
||||
Margin="5,0,5,0" Grid.Row="1" FontSize="14" Grid.ColumnSpan="3">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="MgNr." Binding="{Binding MgNr}" Width="70"/>
|
||||
<DataGridTextColumn Header="Datum" Binding="{Binding Date, StringFormat='dd.MM.yy'}" Width="70"/>
|
||||
<DataGridTextColumn Header="Zeit" Binding="{Binding Time, StringFormat='HH:mm'}" Width="70"/>
|
||||
<DataGridTextColumn Header="Sorte" Binding="{Binding SortIdString}" Width="60"/>
|
||||
<DataGridTextColumn Header="Gewicht" Binding="{Binding Weight, StringFormat='{}{0:N0} kg'}" Width="70">
|
||||
<DataGridTextColumn Header="MgNr." Binding="{Binding MgNr, StringFormat='{}{0} '}" Width="50">
|
||||
<DataGridTextColumn.CellStyle>
|
||||
<Style>
|
||||
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
|
||||
</Style>
|
||||
</DataGridTextColumn.CellStyle>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Header="Datum" Binding="{Binding Date, StringFormat='dd.MM.yy'}" Width="70">
|
||||
<DataGridTextColumn.CellStyle>
|
||||
<Style>
|
||||
<Setter Property="TextBlock.TextAlignment" Value="Center"/>
|
||||
</Style>
|
||||
</DataGridTextColumn.CellStyle>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Header="Zeit" Binding="{Binding Time, StringFormat='HH:mm'}" Width="50">
|
||||
<DataGridTextColumn.CellStyle>
|
||||
<Style>
|
||||
<Setter Property="TextBlock.TextAlignment" Value="Center"/>
|
||||
</Style>
|
||||
</DataGridTextColumn.CellStyle>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Header="Sorte" Binding="{Binding SortIdString}" Width="50">
|
||||
<DataGridTextColumn.CellStyle>
|
||||
<Style>
|
||||
<Setter Property="TextBlock.TextAlignment" Value="Center"/>
|
||||
</Style>
|
||||
</DataGridTextColumn.CellStyle>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Header="Gewicht" Binding="{Binding Weight, StringFormat='{}{0:N0} kg '}" Width="75">
|
||||
<DataGridTextColumn.CellStyle>
|
||||
<Style>
|
||||
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
|
||||
</Style>
|
||||
</DataGridTextColumn.CellStyle>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Header="Gradation" Binding="{Binding Kmw, StringFormat='{}{0:N1}° '}" Width="50">
|
||||
<DataGridTextColumn.CellStyle>
|
||||
<Style>
|
||||
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
|
||||
</Style>
|
||||
</DataGridTextColumn.CellStyle>
|
||||
</DataGridTextColumn>
|
||||
<DataGridTextColumn Header="LsNr." Binding="{Binding LsNr}" Width="120"/>
|
||||
<DataGridTextColumn Header="Mitglied" Binding="{Binding Member.AdministrativeName}" Width="180"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
<Button x:Name="NewMemberButton" Content="Neu"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2"/>
|
||||
<Button x:Name="EditMemberButton" Content="Bearbeiten" IsEnabled="False"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2"/>
|
||||
<Button x:Name="DeleteMemberButton" Content="Löschen" IsEnabled="False"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2"/>
|
||||
<Button x:Name="FinishButton" Content="Abschließen" IsEnabled="False" Visibility="Hidden"
|
||||
ToolTip="Übernahme abschließen und Lieferschein drucken"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2"
|
||||
Click="FinishButton_Click"/>
|
||||
<Button x:Name="NewDeliveryPartButton" Content="Neue Teil-Lfrg." IsEnabled="False" Visibility="Hidden"
|
||||
ToolTip="Neue Teillieferung auf selben Lieferschein hinzufügen"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2"
|
||||
Click="NewDeliveryPartButton_Click"/>
|
||||
<Button x:Name="CancelCreatingButton" Content="Abbrechen" IsEnabled="False" Visibility="Hidden"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2"
|
||||
Click="CancelCreatingButton_Click"/>
|
||||
|
||||
<Button x:Name="NewDeliveryButton" Content="Neu" IsEnabled="False" Visibility="Hidden"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2"
|
||||
Click="NewDeliveryButton_Click"/>
|
||||
<Button x:Name="AbwertenButton" Content="Abwerten" IsEnabled="False"
|
||||
ToolTip="Ausgewählte Teillieferung vollständig oder teilweise abwerten"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2"
|
||||
Click="AbwertenButton_Click"/>
|
||||
<Button x:Name="EditDeliveryButton" Content="Bearbeiten" IsEnabled="False"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2"
|
||||
Click="EditDeliveryButton_Click"/>
|
||||
<Button x:Name="DeleteDeliveryButton" Content="Löschen" IsEnabled="False"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2"
|
||||
Click="DeleteDeliveryButton_Click"/>
|
||||
|
||||
<Button x:Name="SaveButton" Content="Speichern" IsEnabled="False" Visibility="Hidden"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0"/>
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2"
|
||||
Click="SaveButton_Click"/>
|
||||
<Button x:Name="ResetButton" Content="Zurücksetzen" IsEnabled="False" Visibility="Hidden"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1"/>
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2"
|
||||
Click="ResetButton_Click"/>
|
||||
<Button x:Name="CancelButton" Content="Abbrechen" IsEnabled="False" Visibility="Hidden" IsCancel="True"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2"/>
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2"
|
||||
Click="CancelButton_Click"/>
|
||||
</Grid>
|
||||
|
||||
<GroupBox Header="Mitglied" Grid.Column="1" Grid.Row="1" Margin="5,5,5,5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="70"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<GridSplitter Grid.Column="1" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
|
||||
|
||||
<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"
|
||||
TextChanged="MgNrInput_TextChanged" LostFocus="MgNrInput_LostFocus"/>
|
||||
<ComboBox x:Name="MemberInput" Grid.Column="1" Margin="53,10,10,10" IsEditable="True"
|
||||
ItemTemplate="{StaticResource MemberAdminNameTemplate}" TextSearch.TextPath="AdministrativeName"
|
||||
SelectionChanged="MemberInput_SelectionChanged"/>
|
||||
<Grid Grid.Row="1" Grid.Column="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="0.625*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<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"
|
||||
IsReadOnly="True" IsTabStop="False"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
<GroupBox Header="Mitglied" Grid.Column="0" Grid.Row="0" Margin="5,5,5,5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="70"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<GroupBox Header="Lieferung" Grid.Column="1" Grid.Row="2" Margin="5,5,5,5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<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"
|
||||
TextChanged="MgNrInput_TextChanged" LostFocus="MgNrInput_LostFocus" KeyUp="Input_KeyUp"/>
|
||||
<ComboBox x:Name="MemberInput" Grid.Column="1" Margin="53,10,10,10" IsEditable="True"
|
||||
ItemTemplate="{StaticResource MemberAdminNameTemplate}" TextSearch.TextPath="AdministrativeName"
|
||||
SelectionChanged="MemberInput_SelectionChanged" KeyUp="Input_KeyUp"/>
|
||||
|
||||
<Label Content="LieferscheinNr.:" Margin="10,10,0,0" Grid.Column="0"/>
|
||||
<TextBox x:Name="LsNrInput" Width="126" Grid.Column="1" HorizontalAlignment="Left" Margin="0,10,0,0"
|
||||
IsReadOnly="True" IsTabStop="False"/>
|
||||
|
||||
<Label Content="Datum/Uhrzeit:" Margin="10,40,0,0" Grid.Column="0"/>
|
||||
<TextBox x:Name="DateInput" Width="77" Grid.Column="1" HorizontalAlignment="Left" Margin="0,40,0,0"
|
||||
IsReadOnly="True" IsTabStop="False"
|
||||
TextChanged="DateInput_TextChanged"/>
|
||||
<TextBox x:Name="TimeInput" Width="44" Grid.Column="1" HorizontalAlignment="Left" Margin="82,40,0,0"
|
||||
IsReadOnly="True" IsTabStop="False"/>
|
||||
|
||||
<Label Content="Zweigstelle:" Margin="10,70,0,0" Grid.Column="0"/>
|
||||
<ComboBox x:Name="BranchInput" Width="126" Margin="0,70,10,0" Grid.Column="1" HorizontalAlignment="Left"
|
||||
IsEnabled="False"
|
||||
DisplayMemberPath="Name" TextSearch.TextPath="Name"/>
|
||||
|
||||
<Label Content="Anmerkung:" Margin="10,100,0,10"/>
|
||||
<TextBox x:Name="CommentInput" Grid.Column="1" Margin="0,100,10,10"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="Sorte" Grid.Column="2" Grid.Row="1" Margin="5,5,5,5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="70"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<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"
|
||||
TextChanged="SortIdInput_TextChanged" LostFocus="SortIdInput_LostFocus"/>
|
||||
<ComboBox x:Name="WineVarietyInput" Grid.Row="1" Grid.Column="1" Margin="41,10,10,10"
|
||||
ItemTemplate="{StaticResource WineVarietyTemplate}" TextSearch.TextPath="Name"
|
||||
SelectionChanged="WineVarietyInput_SelectionChanged"/>
|
||||
|
||||
<Label Content="Attribute:" Margin="10,40,0,0" Grid.Column="0"/>
|
||||
<xctk:CheckComboBox x:Name="AttributesInput" Grid.Row="1" Grid.Column="1" Margin="0,40,10,10"
|
||||
DisplayMemberPath="Name" Delimiter=", " AllItemsSelectedContent="Alle"
|
||||
ItemSelectionChanged="AttributesInput_SelectionChanged"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="Gradation" Grid.Column="2" Grid.Row="2" Margin="5,5,5,5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="Gradation:" Margin="10,10,10,10"/>
|
||||
<Grid Grid.Column="1" Width="54" Height="25" Margin="0,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top">
|
||||
<TextBox x:Name="GradationOeInput" TextAlignment="Right" Padding="2,2,23,2"
|
||||
TextChanged="GradationOeInput_TextChanged" LostFocus="GradationOeInput_LostFocus"/>
|
||||
<Label Content="°Oe" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10"/>
|
||||
<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"
|
||||
IsReadOnly="True" IsTabStop="False"/>
|
||||
</Grid>
|
||||
<Label Content="=" Margin="60,10,10,10" Grid.Column="1"/>
|
||||
<Grid Grid.Column="1" Width="68" Height="25" Margin="78,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top">
|
||||
<TextBox x:Name="GradationKmwInput" TextAlignment="Right" Padding="2,2,34,2" SnapsToDevicePixels="True"
|
||||
TextChanged="GradationKmwInput_TextChanged" LostFocus="GradationKmwInput_LostFocus"/>
|
||||
<Label Content="°KMW" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10"/>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="Lieferung" Grid.Column="0" Grid.Row="1" Margin="5,5,5,5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Content="LieferscheinNr.:" Margin="10,10,0,0" Grid.Column="0"/>
|
||||
<TextBox x:Name="LsNrInput" Width="126" Grid.Column="1" HorizontalAlignment="Left" Margin="0,10,0,0"
|
||||
IsReadOnly="True" IsTabStop="False"
|
||||
TextChanged="TextBox_TextChanged"/>
|
||||
|
||||
<Label Content="Datum/Uhrzeit:" Margin="10,40,0,0" Grid.Column="0"/>
|
||||
<TextBox x:Name="DateInput" Width="77" Grid.Column="1" HorizontalAlignment="Left" Margin="0,40,0,0"
|
||||
IsReadOnly="True"
|
||||
TextChanged="DateInput_TextChanged" LostFocus="DateInput_LostFocus"/>
|
||||
<TextBox x:Name="TimeInput" Width="44" Grid.Column="1" HorizontalAlignment="Left" Margin="82,40,0,0"
|
||||
IsReadOnly="True"
|
||||
TextChanged="TimeInput_TextChanged" LostFocus="TimeInput_LostFocus"/>
|
||||
|
||||
<Label Content="Zweigstelle:" Margin="10,70,0,0" Grid.Column="0"/>
|
||||
<ComboBox x:Name="BranchInput" Width="126" Margin="0,70,10,0" Grid.Column="1" HorizontalAlignment="Left"
|
||||
IsEnabled="False"
|
||||
SelectionChanged="BranchInput_SelectionChanged"
|
||||
DisplayMemberPath="Name" TextSearch.TextPath="Name"/>
|
||||
|
||||
<Label Content="Anmerkung:" Margin="10,100,0,10"/>
|
||||
<TextBox x:Name="CommentInput" Grid.Column="1" Margin="0,100,10,10"
|
||||
TextChanged="TextBox_TextChanged"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<Label Content="Qualitätsstufe:" Margin="10,40,10,10"/>
|
||||
<ComboBox x:Name="WineQualityLevelInput" Width="146" Margin="0,40,10,10" Grid.Column="1" HorizontalAlignment="Left"
|
||||
ItemTemplate="{StaticResource WineQualityLevelTemplate}"
|
||||
SelectionChanged="WineQualityLevelInput_SelectionChanged"/>
|
||||
<GroupBox Header="Sorte" Grid.Column="1" Grid.Row="0" Margin="5,5,5,5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="70"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<CheckBox x:Name="AbgewertetInput" Content="Abgewertet"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="0,75,10,10" Grid.Column="1"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
<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"
|
||||
TextChanged="SortIdInput_TextChanged" LostFocus="SortIdInput_LostFocus" KeyUp="Input_KeyUp"/>
|
||||
<ComboBox x:Name="WineVarietyInput" Grid.Row="1" Grid.Column="1" Margin="41,10,10,10"
|
||||
ItemTemplate="{StaticResource WineVarietyTemplate}" TextSearch.TextPath="Name"
|
||||
SelectionChanged="WineVarietyInput_SelectionChanged" KeyUp="Input_KeyUp"/>
|
||||
|
||||
<GroupBox Header="Gewicht" Grid.Column="2" Grid.Row="3" Margin="5,5,5,5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="70"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Content="Gewicht:" Margin="10,10,10,10"/>
|
||||
<Grid Grid.Column="1" Width="70" Height="25" Margin="0,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top">
|
||||
<TextBox x:Name="WeightInput" TextAlignment="Right" Padding="2,2,17,2"
|
||||
IsReadOnly="True"/>
|
||||
<Label Content="kg" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10"/>
|
||||
<Label Content="Attribute:" Margin="10,40,0,0" Grid.Column="0"/>
|
||||
<xctk:CheckComboBox x:Name="AttributesInput" Grid.Row="1" Grid.Column="1" Margin="0,40,10,10"
|
||||
DisplayMemberPath="Name" Delimiter=", " AllItemsSelectedContent="Alle"
|
||||
ItemSelectionChanged="AttributesInput_SelectionChanged" KeyUp="Input_KeyUp"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<CheckBox x:Name="ManualWeighingInput" Content="Handwiegung"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,45,10,10" Grid.Column="0" Grid.ColumnSpan="2"/>
|
||||
<GroupBox Header="Gradation" Grid.Column="1" Grid.Row="1" Margin="5,5,5,5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="Gradation:" Margin="10,10,10,10"/>
|
||||
<Grid Grid.Column="1" Width="54" Height="25" Margin="0,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top">
|
||||
<TextBox x:Name="GradationOeInput" TextAlignment="Right" Padding="2,2,23,2"
|
||||
TextChanged="GradationOeInput_TextChanged" LostFocus="GradationOeInput_LostFocus" KeyUp="Input_KeyUp"/>
|
||||
<Label Content="°Oe" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10"/>
|
||||
</Grid>
|
||||
<Label Content="=" Margin="60,10,10,10" Grid.Column="1"/>
|
||||
<Grid Grid.Column="1" Width="68" Height="25" Margin="78,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top">
|
||||
<TextBox x:Name="GradationKmwInput" TextAlignment="Right" Padding="2,2,34,2" SnapsToDevicePixels="True"
|
||||
TextChanged="GradationKmwInput_TextChanged" LostFocus="GradationKmwInput_LostFocus" KeyUp="Input_KeyUp"/>
|
||||
<Label Content="°KMW" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10"/>
|
||||
</Grid>
|
||||
|
||||
<CheckBox x:Name="GerebeltGewogenInput" Content="Gerebelt gewogen"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,75,10,10" Grid.Column="0" Grid.ColumnSpan="2"/>
|
||||
<Label Content="Qualitätsstufe:" Margin="10,40,10,10"/>
|
||||
<ComboBox x:Name="WineQualityLevelInput" Width="146" Margin="0,40,10,10" Grid.Column="1" HorizontalAlignment="Left"
|
||||
ItemTemplate="{StaticResource WineQualityLevelTemplate}"
|
||||
SelectionChanged="WineQualityLevelInput_SelectionChanged" KeyUp="Input_KeyUp"/>
|
||||
|
||||
<Button x:Name="WeighingManualButton" Content="Handwiegung" Width="120"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,10,10,10" Grid.Column="2"/>
|
||||
<Button x:Name="WeighingAButton" Content="Wiegen A" Width="120"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,42,10,10" Grid.Column="2"/>
|
||||
<Button x:Name="WeighingBButton" Content="Wiegen B" Width="120"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,74,10,10" Grid.Column="2"/>
|
||||
<Button x:Name="WeighingCButton" Content="Wiegen C" Width="120"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,106,10,10" Grid.Column="2"/>
|
||||
<Button x:Name="WeighingDButton" Content="Wiegen D" Width="120"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,138,10,10" Grid.Column="2"/>
|
||||
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="Sonstiges" Grid.Column="2" Grid.Row="4" Margin="5,5,5,5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition Width="65"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Content="Zu-/Abschläge:" Margin="10,10,0,10"/>
|
||||
<xctk:CheckComboBox x:Name="ModifiersInput" Margin="0,10,10,10" Grid.Column="1" Grid.ColumnSpan="2"
|
||||
ItemTemplate="{StaticResource ModifierTemplate}" Delimiter=", " AllItemsSelectedContent="Alle"
|
||||
ItemSelectionChanged="ModifiersInput_SelectionChanged"/>
|
||||
|
||||
<Label Content="Anmerkung:" Margin="10,40,0,10"/>
|
||||
<TextBox x:Name="PartCommentInput" Grid.Column="1" Margin="0,40,10,10" Grid.ColumnSpan="2"/>
|
||||
|
||||
<Label Content="Temperatur:" Margin="10,70,0,10"/>
|
||||
<Grid Grid.Column="1" Height="25" Margin="0,70,10,10" VerticalAlignment="Top">
|
||||
<TextBox x:Name="TemperatureInput" TextAlignment="Right" Padding="2,2,16,2"/>
|
||||
<Label Content="°C" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10"/>
|
||||
<CheckBox x:Name="AbgewertetInput" Content="Abgewertet" IsEnabled="False"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,75,10,10" Grid.Column="0" Grid.ColumnSpan="2"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<Label Content="Säure:" Margin="10,100,0,10"/>
|
||||
<Grid Grid.Column="1" Height="25" Margin="0,100,10,10" VerticalAlignment="Top">
|
||||
<TextBox x:Name="AcidInput" TextAlignment="Right" Padding="2,2,19,2"/>
|
||||
<Label Content="g/l" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10"/>
|
||||
<GroupBox Header="Gewicht" Grid.Column="1" Grid.Row="2" Margin="5,5,5,5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="70"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Content="Gewicht:" Margin="10,10,10,10"/>
|
||||
<Grid Grid.Column="1" Width="70" Height="25" Margin="0,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top">
|
||||
<TextBox x:Name="WeightInput" TextAlignment="Right" Padding="2,2,17,2" IsReadOnly="True"
|
||||
TextChanged="WeightInput_TextChanged"/>
|
||||
<Label Content="kg" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10"/>
|
||||
</Grid>
|
||||
|
||||
<CheckBox x:Name="ManualWeighingInput" Content="Handwiegung" IsEnabled="False"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,45,10,10" Grid.Column="0" Grid.ColumnSpan="2"
|
||||
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
|
||||
|
||||
<CheckBox x:Name="GerebeltGewogenInput" Content="Gerebelt gewogen"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,75,10,10" Grid.Column="0" Grid.ColumnSpan="2"
|
||||
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
|
||||
|
||||
<Button x:Name="WeighingAButton" Content="Wiegen A" Width="120"
|
||||
Click="WeighingButton_Click"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,10,10,10" Grid.Column="2"/>
|
||||
<Button x:Name="WeighingBButton" Content="Wiegen B" Width="120"
|
||||
Click="WeighingButton_Click"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,42,10,10" Grid.Column="2"/>
|
||||
<Button x:Name="WeighingCButton" Content="Wiegen C" Width="120"
|
||||
Click="WeighingButton_Click"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,74,10,10" Grid.Column="2"/>
|
||||
<Button x:Name="WeighingDButton" Content="Wiegen D" Width="120"
|
||||
Click="WeighingButton_Click"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,106,10,10" Grid.Column="2"/>
|
||||
<Button x:Name="WeighingManualButton" Content="Handwiegung" Width="120"
|
||||
Click="WeighingManualButton_Click"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,138,10,10" Grid.Column="2"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<CheckBox x:Name="LesewagenInput" Content="Lesewagen" Margin="10,75,0,0" Grid.Column="2"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left"/>
|
||||
<CheckBox x:Name="HandPickedInput" Content="Handlese" Margin="10,105,0,0" Grid.Column="2" IsThreeState="True"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
<GroupBox Header="Sonstiges" Grid.Column="1" Grid.Row="3" Margin="5,5,5,10">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition Width="65"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<GroupBox Header="Teillieferungen" Grid.Column="1" Grid.Row="3" Margin="5,5,5,5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ListBox x:Name="DeliveryPartList" Margin="5,5,5,38" Grid.ColumnSpan="2"
|
||||
SelectionChanged="DeliveryPartList_SelectionChanged">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{Binding DPNr}" Width="20"/>
|
||||
<TextBlock Text="{Binding SortId}" Width="30"/>
|
||||
<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 Weight, StringFormat='{}{0:N0} kg'}" Width="60" TextAlignment="Right"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
<Label Content="Zu-/Abschläge:" Margin="10,10,0,10"/>
|
||||
<xctk:CheckComboBox x:Name="ModifiersInput" Margin="0,10,10,10" Grid.Column="1" Grid.ColumnSpan="2"
|
||||
ItemTemplate="{StaticResource ModifierTemplate}" Delimiter=", " AllItemsSelectedContent="Alle"
|
||||
ItemSelectionChanged="ModifiersInput_SelectionChanged"/>
|
||||
|
||||
<Button x:Name="ExtractDeliveryPartButton" Content="Herausheben" IsEnabled="False"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,10,2.5,5" Grid.Column="0" Grid.Row="2"/>
|
||||
<Button x:Name="DeleteDeliveryPartButton" Content="Löschen" IsEnabled="False"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,10,5,5" Grid.Column="1" Grid.Row="2"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
<Label Content="Anmerkung:" Margin="10,40,0,10"/>
|
||||
<TextBox x:Name="PartCommentInput" Grid.Column="1" Margin="0,40,10,10" Grid.ColumnSpan="2"
|
||||
TextChanged="TextBox_TextChanged"/>
|
||||
|
||||
<GroupBox Header="Herkunft" Grid.Column="1" Grid.Row="4" Margin="5,5,5,5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="Temperatur:" Margin="10,70,0,10"/>
|
||||
<Grid Grid.Column="1" Height="25" Margin="0,70,10,10" VerticalAlignment="Top">
|
||||
<TextBox x:Name="TemperatureInput" TextAlignment="Right" Padding="2,2,16,2"
|
||||
TextChanged="TemperatureAcidInput_TextChanged" LostFocus="TemperatureAcidInput_LostFocus"/>
|
||||
<Label Content="°C" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10"/>
|
||||
</Grid>
|
||||
|
||||
<Label Content="Weinbaugebiet:" Margin="10,10,0,10" Grid.Column="0"/>
|
||||
<ComboBox x:Name="WineOriginInput" Margin="0,10,10,10" Grid.Column="1"
|
||||
ItemTemplate="{StaticResource WineOriginTemplate}"/>
|
||||
<Label Content="Säure:" Margin="10,100,0,10"/>
|
||||
<Grid Grid.Column="1" Height="25" Margin="0,100,10,10" VerticalAlignment="Top">
|
||||
<TextBox x:Name="AcidInput" TextAlignment="Right" Padding="2,2,19,2"
|
||||
TextChanged="TemperatureAcidInput_TextChanged" LostFocus="TemperatureAcidInput_LostFocus"/>
|
||||
<Label Content="g/l" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10"/>
|
||||
</Grid>
|
||||
|
||||
<Label Content="Weinbau-KG:" Margin="10,40,0,10" Grid.Column="0"/>
|
||||
<ComboBox x:Name="WineKgInput" Margin="0,40,10,10" Grid.Column="1"
|
||||
DisplayMemberPath="Name"
|
||||
SelectionChanged="WineKgInput_SelectionChanged"/>
|
||||
<CheckBox x:Name="LesewagenInput" Content="Lesewagen" Margin="10,75,0,0" Grid.Column="2"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left"
|
||||
Checked="LesewagenInput_Changed" Unchecked="LesewagenInput_Changed"/>
|
||||
<CheckBox x:Name="HandPickedInput" Content="Handlese" Margin="10,105,0,0" Grid.Column="2" IsThreeState="True"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left"
|
||||
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<Label Content="Ried:" Margin="10,70,0,10" Grid.Column="0"/>
|
||||
<ComboBox x:Name="WineRdInput" Margin="0,70,10,10" Grid.Column="1"
|
||||
DisplayMemberPath="Name"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
<GroupBox Header="Teillieferungen" Grid.Column="0" Grid.Row="2" Margin="5,5,5,5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ListBox x:Name="DeliveryPartList" Margin="5,5,5,38" Grid.ColumnSpan="2"
|
||||
SelectionChanged="DeliveryPartList_SelectionChanged">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{Binding DPNr}" Width="20"/>
|
||||
<TextBlock Text="{Binding SortId}" Width="30"/>
|
||||
<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 Weight, StringFormat='{}{0:N0} kg'}" Width="60" TextAlignment="Right" Padding="0,0,10,0"/>
|
||||
<TextBlock Text="{Binding AttributesString}" Width="100"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
|
||||
<Button x:Name="ExtractDeliveryPartButton" Content="Extrahieren" IsEnabled="False"
|
||||
ToolTip="Ausgewählte Teillieferung aus aktueller Lieferung entfernen und entweder anderer oder neuer Lieferung zuordnen"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,10,2.5,5" Grid.Column="0" Grid.Row="2"
|
||||
Click="ExtractDeliveryPartButton_Click"/>
|
||||
<Button x:Name="DeleteDeliveryPartButton" Content="Löschen" IsEnabled="False"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,10,5,5" Grid.Column="1" Grid.Row="2"
|
||||
Click="DeleteDeliveryPartButton_Click"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="Herkunft" Grid.Column="0" Grid.Row="3" Margin="5,5,5,10">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Content="Weinbaugebiet:" Margin="10,10,0,10" Grid.Column="0"/>
|
||||
<ComboBox x:Name="WineOriginInput" Margin="0,10,10,10" Grid.Column="1"
|
||||
ItemTemplate="{StaticResource WineOriginTemplate}"/>
|
||||
|
||||
<Label Content="Weinbau-KG:" Margin="10,40,0,10" Grid.Column="0"/>
|
||||
<ComboBox x:Name="WineKgInput" Margin="0,40,10,10" Grid.Column="1"
|
||||
DisplayMemberPath="Name"
|
||||
SelectionChanged="WineKgInput_SelectionChanged"/>
|
||||
|
||||
<Label Content="Ried:" Margin="10,70,0,10" Grid.Column="0"/>
|
||||
<ComboBox x:Name="WineRdInput" Margin="0,70,10,10" Grid.Column="1"
|
||||
DisplayMemberPath="Name"/>
|
||||
|
||||
<CheckBox x:Name="GebundenInput" Content="Gebunden" Margin="10,105,0,0" Grid.Column="0" Grid.ColumnSpan="2" IsThreeState="True"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left"
|
||||
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
</Grid>
|
||||
|
||||
<StatusBar Grid.Row="5" Grid.ColumnSpan="3" BorderThickness="0,1,0,0" BorderBrush="Gray">
|
||||
<StatusBar.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="2*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="2*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="3*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="3*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
</Grid>
|
||||
</ItemsPanelTemplate>
|
||||
</StatusBar.ItemsPanel>
|
||||
<StatusBarItem>
|
||||
<TextBlock Name="StatusMembers" Text="Mitglieder: -"/>
|
||||
</StatusBarItem>
|
||||
<Separator Grid.Column="1"/>
|
||||
<StatusBarItem Grid.Column="2">
|
||||
<TextBlock Name="StatusDeliveries" Text="Lieferungen: -"/>
|
||||
</StatusBarItem>
|
||||
<Separator Grid.Column="3"/>
|
||||
<StatusBarItem Grid.Column="4">
|
||||
<TextBlock Name="StatusVarieties" Text="Sorten: -"/>
|
||||
</StatusBarItem>
|
||||
<Separator Grid.Column="5"/>
|
||||
<StatusBarItem Grid.Column="6">
|
||||
<TextBlock Name="StatusWeight" Text="Gewicht: -"/>
|
||||
</StatusBarItem>
|
||||
<Separator Grid.Column="7"/>
|
||||
<StatusBarItem Grid.Column="8">
|
||||
<TextBlock Name="StatusGradation" Text="Gradation: -"/>
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
</Grid>
|
||||
</local:AdministrationWindow>
|
||||
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user