Compare commits
116 Commits
Author | SHA1 | Date | |
---|---|---|---|
5b952c4eb1 | |||
48c441b787 | |||
be246d6f06 | |||
2b10e52ab0 | |||
e3fd705f52 | |||
81e18ac553 | |||
f95f0f0ef3 | |||
8eba40a8c1 | |||
69aa75a50a | |||
13340c0979 | |||
4bd378e048 | |||
602c237fa0 | |||
5e6d0ebf17 | |||
b064643010 | |||
3526234432 | |||
b03f81d4f2 | |||
f123bb44c5 | |||
30536819e7 | |||
384f7c9ec0 | |||
d102a1cb7a | |||
6906584ef0 | |||
c0c0b4a26a | |||
0ce8e488f9 | |||
eb4562dceb | |||
35e5a1dfff | |||
179c8bd4f7 | |||
fd17d294b9 | |||
2a4e8d69d0 | |||
8bf8362480 | |||
c4d68d11bc | |||
21fe5bc094 | |||
e7bfc69842 | |||
f53371ab19 | |||
5f8688f0cd | |||
fa00eaaefc | |||
443e111594 | |||
c6905bbb42 | |||
c360e6b6a7 | |||
9062d55b20 | |||
a9f38a3ccb | |||
cbc0d0ebff | |||
27b5d653e6 | |||
869f652afc | |||
80e91ad776 | |||
bce709efe4 | |||
12eb53cb44 | |||
657910ff48 | |||
5c3cf41d3d | |||
b8851fb241 | |||
66898714bb | |||
1047bc6e8f | |||
1419c834ac | |||
eddea88e77 | |||
7274d793c4 | |||
79d9e5d242 | |||
b2f52072f8 | |||
9aa6cba1ff | |||
d501cfaf72 | |||
c2b6486ede | |||
82ea5920f2 | |||
d011c69812 | |||
26a9902a13 | |||
57662534f3 | |||
d87f3ce6a6 | |||
7f21b7b231 | |||
cac0959fe7 | |||
a04f887b4d | |||
5c42ef8104 | |||
cf2ec3bdc4 | |||
b31b5f6164 | |||
85f48f1d2a | |||
e3cb20366c | |||
56ac79b4dd | |||
175d006d5b | |||
f4ef75ac40 | |||
5795c5e8ba | |||
04351a906f | |||
3f9c4cb1f6 | |||
c6e83ffff4 | |||
dd408ca40e | |||
555ce228d4 | |||
9d80c5913f | |||
e435e5da8d | |||
7b48385992 | |||
48f0ddd232 | |||
c9bb075910 | |||
ee1f4081f4 | |||
b6e37c0c67 | |||
87da56b7a9 | |||
afc143e1e4 | |||
545033daf5 | |||
1b822a88f3 | |||
a9bad4dd3f | |||
1806b02039 | |||
98688168b8 | |||
2f3524db9d | |||
51e345f1fd | |||
729d2fd76c | |||
5715c41a2e | |||
9353581a56 | |||
a72803f749 | |||
9d1ce4138c | |||
4afd2d8242 | |||
b7d33e6d89 | |||
711bab5d33 | |||
d9f9ab2391 | |||
ebb196b094 | |||
298e423de8 | |||
e2e46bc52a | |||
a8e3eb6c1c | |||
4e2c087260 | |||
0c63e315bb | |||
f3cdac8a61 | |||
acc159ed9c | |||
1eba3d9d20 | |||
239b8a9091 |
@ -48,7 +48,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
$content = [System.IO.File]::ReadAllBytes("Setup/bin/x64/Release/Elwig-${{ env.APP_VERSION }}.exe")
|
$content = [System.IO.File]::ReadAllBytes("Setup/bin/x64/Release/Elwig-${{ env.APP_VERSION }}.exe")
|
||||||
Invoke-WebRequest `
|
Invoke-WebRequest `
|
||||||
-Uri "https://www.necronda.net/elwig/files/Elwig-${{ env.APP_VERSION }}.exe" `
|
-Uri "https://elwig.at/files/Elwig-${{ env.APP_VERSION }}.exe" `
|
||||||
-Method PUT `
|
-Method PUT `
|
||||||
-Body $content `
|
-Body $content `
|
||||||
-Headers @{ Authorization = "${{ secrets.API_AUTHORIZATION }}" } `
|
-Headers @{ Authorization = "${{ secrets.API_AUTHORIZATION }}" } `
|
||||||
|
@ -3,11 +3,10 @@
|
|||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:ctrl="clr-namespace:Elwig.Controls"
|
xmlns:ctrl="clr-namespace:Elwig.Controls"
|
||||||
StartupUri="Windows\MainWindow.xaml"
|
|
||||||
Exit="Application_Exit">
|
Exit="Application_Exit">
|
||||||
<Application.Resources>
|
<Application.Resources>
|
||||||
<ctrl:BoolToStringConverter x:Key="BoolToStarConverter" FalseValue="" TrueValue="*"/>
|
<ctrl:BoolToStringConverter x:Key="BoolToStarConverter" FalseValue="" TrueValue="*"/>
|
||||||
<ctrl:WidthToPaddingConverter x:Key="WidthToPaddingConverter"/>
|
<ctrl:WidthToMarginConverter x:Key="WidthToMarginConverter"/>
|
||||||
|
|
||||||
<DataTemplate x:Key="PostalDestTemplate">
|
<DataTemplate x:Key="PostalDestTemplate">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
@ -29,25 +28,17 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<ControlTemplate x:Key="WineVarietyTemplateSimple">
|
<DataTemplate x:Key="WineVarietyTemplateCollapsed">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBlock Text="{Binding Name}"/>
|
<TextBlock Text="{Binding Name}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ControlTemplate>
|
</DataTemplate>
|
||||||
<ControlTemplate x:Key="WineVarietyTemplateExtended">
|
<DataTemplate x:Key="WineVarietyTemplateExpanded">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBlock Text="{Binding SortId}" MinWidth="36" Margin="0,0,10,0"/>
|
<TextBlock Text="{Binding SortId}" MinWidth="36" Margin="0,0,10,0"/>
|
||||||
<TextBlock Text="{Binding Name}"/>
|
<TextBlock Text="{Binding Name}"/>
|
||||||
<TextBlock Text="{Binding CommentFormat}" FontSize="10" VerticalAlignment="Bottom" Margin="0,0,0,2"/>
|
<TextBlock Text="{Binding CommentFormat}" FontSize="10" VerticalAlignment="Bottom" Margin="0,0,0,2"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ControlTemplate>
|
|
||||||
<DataTemplate x:Key="WineVarietyTemplate">
|
|
||||||
<Control x:Name="Control" Focusable="False" Template="{StaticResource WineVarietyTemplateExtended}"/>
|
|
||||||
<DataTemplate.Triggers>
|
|
||||||
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBoxItem}}, Path=IsSelected}" Value="{x:Null}">
|
|
||||||
<Setter TargetName="Control" Property="Template" Value="{StaticResource WineVarietyTemplateSimple}"/>
|
|
||||||
</DataTrigger>
|
|
||||||
</DataTemplate.Triggers>
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<DataTemplate x:Key="ModifierTemplate">
|
<DataTemplate x:Key="ModifierTemplate">
|
||||||
@ -63,25 +54,17 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<ControlTemplate x:Key="WineQualityLevelTemplateSimple">
|
<DataTemplate x:Key="WineQualityLevelTemplateCollapsed">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBlock Text="{Binding Name}"/>
|
<TextBlock Text="{Binding Name}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ControlTemplate>
|
</DataTemplate>
|
||||||
<ControlTemplate x:Key="WineQualityLevelTemplateExtended">
|
<DataTemplate x:Key="WineQualityLevelTemplateExpanded">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBlock Text="{Binding IsPredicate, Converter={StaticResource BoolToStarConverter}}" MinWidth="6"/>
|
<TextBlock Text="{Binding IsPredicate, Converter={StaticResource BoolToStarConverter}}" MinWidth="6"/>
|
||||||
<TextBlock Text="{Binding Name}" MinWidth="100" Margin="0,0,10,0"/>
|
<TextBlock Text="{Binding Name}" MinWidth="100" Margin="0,0,10,0"/>
|
||||||
<TextBlock Text="{Binding MinKmwStr}"/>
|
<TextBlock Text="{Binding MinKmwStr}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ControlTemplate>
|
|
||||||
<DataTemplate x:Key="WineQualityLevelTemplate">
|
|
||||||
<Control x:Name="Control" Focusable="False" Template="{StaticResource WineQualityLevelTemplateExtended}"/>
|
|
||||||
<DataTemplate.Triggers>
|
|
||||||
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBoxItem}}, Path=IsSelected}" Value="{x:Null}">
|
|
||||||
<Setter TargetName="Control" Property="Template" Value="{StaticResource WineQualityLevelTemplateSimple}"/>
|
|
||||||
</DataTrigger>
|
|
||||||
</DataTemplate.Triggers>
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<DataTemplate x:Key="WineOriginTemplate">
|
<DataTemplate x:Key="WineOriginTemplate">
|
||||||
@ -90,24 +73,16 @@
|
|||||||
<TextBlock Text="{Binding Name}"/>
|
<TextBlock Text="{Binding Name}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
<ControlTemplate x:Key="WineOriginTemplateSimple">
|
<DataTemplate x:Key="WineOriginTemplateCollapsed">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBlock Text="{Binding Name}"/>
|
<TextBlock Text="{Binding Name}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ControlTemplate>
|
</DataTemplate>
|
||||||
<ControlTemplate x:Key="WineOriginTemplateExtended">
|
<DataTemplate x:Key="WineOriginTemplateExpanded">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBlock Text="{Binding HkIdLevel}" MinWidth="70" Margin="0,0,10,0"/>
|
<TextBlock Text="{Binding HkIdLevel}" MinWidth="70" Margin="0,0,10,0"/>
|
||||||
<TextBlock Text="{Binding Name}"/>
|
<TextBlock Text="{Binding Name}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ControlTemplate>
|
|
||||||
<DataTemplate x:Key="WineOriginComboTemplate">
|
|
||||||
<Control x:Name="Control" Focusable="False" Template="{StaticResource WineOriginTemplateExtended}"/>
|
|
||||||
<DataTemplate.Triggers>
|
|
||||||
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBoxItem}}, Path=IsSelected}" Value="{x:Null}">
|
|
||||||
<Setter TargetName="Control" Property="Template" Value="{StaticResource WineOriginTemplateSimple}"/>
|
|
||||||
</DataTrigger>
|
|
||||||
</DataTemplate.Triggers>
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|
||||||
<DataTemplate x:Key="GemTemplate">
|
<DataTemplate x:Key="GemTemplate">
|
||||||
|
@ -17,6 +17,7 @@ using Elwig.Dialogs;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Elwig.Helpers.Billing;
|
using Elwig.Helpers.Billing;
|
||||||
using Elwig.Models.Entities;
|
using Elwig.Models.Entities;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Elwig {
|
namespace Elwig {
|
||||||
public partial class App : Application {
|
public partial class App : Application {
|
||||||
@ -60,15 +61,29 @@ namespace Elwig {
|
|||||||
public static ClientParameters Client { get; set; }
|
public static ClientParameters Client { get; set; }
|
||||||
|
|
||||||
public static Dispatcher MainDispatcher { get; private set; }
|
public static Dispatcher MainDispatcher { get; private set; }
|
||||||
|
private DateTime LastChanged;
|
||||||
|
private static DateTime CurrentLastWrite => File.GetLastWriteTime(Config.DatabaseFile);
|
||||||
|
private readonly DispatcherTimer ContextTimer = new() { Interval = TimeSpan.FromSeconds(2) };
|
||||||
|
|
||||||
public App() : base() {
|
public App() : base() {
|
||||||
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
|
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||||
Directory.CreateDirectory(TempPath);
|
Directory.CreateDirectory(TempPath);
|
||||||
Directory.CreateDirectory(DataPath);
|
Directory.CreateDirectory(DataPath);
|
||||||
MainDispatcher = Dispatcher;
|
MainDispatcher = Dispatcher;
|
||||||
Scales = [];
|
Scales = [];
|
||||||
CurrentApp = this;
|
CurrentApp = this;
|
||||||
OverrideCulture();
|
OverrideCulture();
|
||||||
|
|
||||||
|
ContextTimer.Tick += (object? sender, EventArgs evt) => {
|
||||||
|
if (CurrentLastWrite > LastChanged) {
|
||||||
|
LastChanged = CurrentLastWrite;
|
||||||
|
OnContextChanged();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnContextChanged() {
|
||||||
|
MainDispatcher.BeginInvoke(async () => await HintContextChange());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OverrideCulture() {
|
private static void OverrideCulture() {
|
||||||
@ -94,11 +109,17 @@ namespace Elwig {
|
|||||||
try {
|
try {
|
||||||
await AppDbUpdater.CheckDb();
|
await AppDbUpdater.CheckDb();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
if (Config.UpdateUrl != null && Utils.HasInternetConnectivity()) {
|
||||||
|
await CheckForUpdates();
|
||||||
|
}
|
||||||
MessageBox.Show($"Invalid Database:\n\n{e.Message}", "Invalid Database", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show($"Invalid Database:\n\n{e.Message}", "Invalid Database", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
Shutdown();
|
Shutdown();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LastChanged = CurrentLastWrite;
|
||||||
|
ContextTimer.Start();
|
||||||
|
|
||||||
Dictionary<string, (string, string, int?, string?, string?, string?, string?, string?)> branches = [];
|
Dictionary<string, (string, string, int?, string?, string?, string?, string?, string?)> branches = [];
|
||||||
using (var ctx = new AppDbContext()) {
|
using (var ctx = new AppDbContext()) {
|
||||||
branches = ctx.Branches.ToDictionary(b => b.Name.ToLower(), b => (b.ZwstId, b.Name, b.PostalDest?.AtPlz?.Plz, b.PostalDest?.AtPlz?.Ort.Name, b.Address, b.PhoneNr, b.FaxNr, b.MobileNr));
|
branches = ctx.Branches.ToDictionary(b => b.Name.ToLower(), b => (b.ZwstId, b.Name, b.PostalDest?.AtPlz?.Plz, b.PostalDest?.AtPlz?.Ort.Name, b.Address, b.PhoneNr, b.FaxNr, b.MobileNr));
|
||||||
@ -138,7 +159,9 @@ namespace Elwig {
|
|||||||
list.Add(Scale.FromConfig(s));
|
list.Add(Scale.FromConfig(s));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
list.Add(new InvalidScale(s.Id));
|
list.Add(new InvalidScale(s.Id));
|
||||||
MessageBox.Show($"Unable to create scale {s.Id}:\n\n{e.Message}", "Scale Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
if (Config.Debug || s.Required)
|
||||||
|
MessageBox.Show($"Unable to create scale {s.Id}:\n\n{e.Message}", "Scale Error",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Scales = list;
|
Scales = list;
|
||||||
@ -158,6 +181,9 @@ namespace Elwig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
base.OnStartup(evt);
|
base.OnStartup(evt);
|
||||||
|
|
||||||
|
var window = new MainWindow();
|
||||||
|
window.Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void Application_Exit(object sender, ExitEventArgs evt) {
|
private async void Application_Exit(object sender, ExitEventArgs evt) {
|
||||||
@ -180,6 +206,7 @@ namespace Elwig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static async Task HintContextChange() {
|
public static async Task HintContextChange() {
|
||||||
|
CurrentApp.LastChanged = CurrentLastWrite;
|
||||||
foreach (Window w in CurrentApp.Windows) {
|
foreach (Window w in CurrentApp.Windows) {
|
||||||
if (w is not ContextWindow c) continue;
|
if (w is not ContextWindow c) continue;
|
||||||
await c.HintContextChange();
|
await c.HintContextChange();
|
||||||
@ -191,11 +218,11 @@ namespace Elwig {
|
|||||||
if (w is UpdateDialog) return;
|
if (w is UpdateDialog) return;
|
||||||
}
|
}
|
||||||
if (Utils.HasInternetConnectivity()) {
|
if (Utils.HasInternetConnectivity()) {
|
||||||
Utils.RunBackground("Auto Updater", CheckForUpdates);
|
Utils.RunBackground("Auto Updater", async () => await CheckForUpdates());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task CheckForUpdates() {
|
public static async Task CheckForUpdates(bool showSuccess = false) {
|
||||||
if (Config.UpdateUrl == null) return;
|
if (Config.UpdateUrl == null) return;
|
||||||
var latest = await Utils.GetLatestInstallerUrl(Config.UpdateUrl);
|
var latest = await Utils.GetLatestInstallerUrl(Config.UpdateUrl);
|
||||||
if (latest != null && new Version(latest.Value.Version) > new Version(Version)) {
|
if (latest != null && new Version(latest.Value.Version) > new Version(Version)) {
|
||||||
@ -206,6 +233,9 @@ namespace Elwig {
|
|||||||
Current.Shutdown();
|
Current.Shutdown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else if (showSuccess) {
|
||||||
|
MessageBox.Show("Elwig ist auf dem aktuellsten Stand!", "Nach Updates suchen",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,14 +278,10 @@ namespace Elwig {
|
|||||||
public static BaseDataWindow FocusBaseDataSeason(int year) {
|
public static BaseDataWindow FocusBaseDataSeason(int year) {
|
||||||
var w = FocusBaseData();
|
var w = FocusBaseData();
|
||||||
w.Seasons.Focus();
|
w.Seasons.Focus();
|
||||||
ControlUtils.SelectListBoxItem(w.SeasonList, s => (s as Season)?.Year, year);
|
ControlUtils.SelectItemWithPk(w.SeasonList, year);
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SeasonFinishWindow FocusSeasonFinish() {
|
|
||||||
return FocusWindow<SeasonFinishWindow>(() => new());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static OriginHierarchyWindow FocusOriginHierarchy() {
|
public static OriginHierarchyWindow FocusOriginHierarchy() {
|
||||||
return FocusWindow<OriginHierarchyWindow>(() => new());
|
return FocusWindow<OriginHierarchyWindow>(() => new());
|
||||||
}
|
}
|
||||||
|
17
Elwig/Controls/UnitTextBox.cs
Normal file
17
Elwig/Controls/UnitTextBox.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace Elwig.Controls {
|
||||||
|
public class UnitTextBox : TextBox {
|
||||||
|
|
||||||
|
public static readonly DependencyProperty UnitProperty = DependencyProperty.Register("Unit", typeof(string), typeof(UnitTextBox), new FrameworkPropertyMetadata(""));
|
||||||
|
public string Unit {
|
||||||
|
get => (string)GetValue(UnitProperty);
|
||||||
|
set => SetValue(UnitProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static UnitTextBox() {
|
||||||
|
DefaultStyleKeyProperty.OverrideMetadata(typeof(UnitTextBox), new FrameworkPropertyMetadata(typeof(UnitTextBox)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,30 @@
|
|||||||
<UserControl x:Class="Elwig.Controls.UnitTextBox"
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
xmlns:ctrl="clr-namespace:Elwig.Controls">
|
||||||
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Height="25">
|
<Style TargetType="ctrl:UnitTextBox" BasedOn="{StaticResource {x:Type TextBox}}">
|
||||||
<TextBox x:Name="TextBox" TextAlignment="Right" FontSize="14" VerticalAlignment="Stretch"
|
<Setter Property="Template">
|
||||||
Padding="{Binding ElementName=UnitBlock, Path=ActualWidth, Converter={StaticResource WidthToPaddingConverter}}"
|
<Setter.Value>
|
||||||
Text="{Binding Path=Text}" TextChanged="TextBox_TextChanged"/>
|
<ControlTemplate TargetType="ctrl:UnitTextBox">
|
||||||
<TextBlock x:Name="UnitBlock" Text="{Binding Path=Unit}" Margin="0,0,4,4" FontSize="10"
|
<Border BorderThickness="{Binding Path=BorderThickness, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
|
||||||
HorizontalAlignment="Right" VerticalAlignment="Bottom"/>
|
BorderBrush="{Binding Path=BorderBrush, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
|
||||||
|
SnapsToDevicePixels="True">
|
||||||
|
<Grid>
|
||||||
|
<ScrollViewer x:Name="PART_ContentHost" VerticalAlignment="Bottom">
|
||||||
|
<ScrollViewer.Margin>
|
||||||
|
<Binding ElementName="UnitBlock" Path="ActualWidth">
|
||||||
|
<Binding.Converter>
|
||||||
|
<ctrl:WidthToMarginConverter/>
|
||||||
|
</Binding.Converter>
|
||||||
|
</Binding>
|
||||||
|
</ScrollViewer.Margin>
|
||||||
|
</ScrollViewer>
|
||||||
|
<TextBlock x:Name="UnitBlock" Text="{Binding Path=Unit, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
|
||||||
|
FontSize="10" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="3"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="TextAlignment" Value="Right"/>
|
||||||
|
</Style>
|
||||||
|
</ResourceDictionary>
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
using System.Windows;
|
|
||||||
using System.Windows.Controls;
|
|
||||||
|
|
||||||
namespace Elwig.Controls {
|
|
||||||
public partial class UnitTextBox : UserControl {
|
|
||||||
|
|
||||||
public event TextChangedEventHandler? TextChanged;
|
|
||||||
|
|
||||||
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(UnitTextBox));
|
|
||||||
public string Text {
|
|
||||||
get => (string)GetValue(TextProperty);
|
|
||||||
set => SetValue(TextProperty, value ?? "");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly DependencyProperty UnitProperty = DependencyProperty.Register("Unit", typeof(string), typeof(UnitTextBox));
|
|
||||||
public string Unit {
|
|
||||||
get => (string)GetValue(UnitProperty);
|
|
||||||
set => SetValue(UnitProperty, value ?? "");
|
|
||||||
}
|
|
||||||
|
|
||||||
public UnitTextBox() {
|
|
||||||
Text = "";
|
|
||||||
Unit = "";
|
|
||||||
InitializeComponent();
|
|
||||||
DataContext = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TextBox_TextChanged(object sender, TextChangedEventArgs evt) {
|
|
||||||
Text = TextBox.Text;
|
|
||||||
TextChanged?.Invoke(sender, evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
public new void Focus() {
|
|
||||||
TextBox.Focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,13 +4,13 @@ using System.Windows.Data;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
|
||||||
namespace Elwig.Controls {
|
namespace Elwig.Controls {
|
||||||
public class WidthToPaddingConverter : IValueConverter {
|
public class WidthToMarginConverter : IValueConverter {
|
||||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
|
||||||
return new Thickness(2, 2, 4 + (double)value, 2);
|
return new Thickness(0, 0, 2 + (double)value, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
|
||||||
return ((Thickness)value).Right - 4;
|
return ((Thickness)value).Right - 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
15
Elwig/Controls/WineOriginTemplateSelector.cs
Normal file
15
Elwig/Controls/WineOriginTemplateSelector.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace Elwig.Controls {
|
||||||
|
public class WineOriginTemplateSelector : DataTemplateSelector {
|
||||||
|
public override DataTemplate SelectTemplate(object item, DependencyObject container) {
|
||||||
|
ContentPresenter presenter = (ContentPresenter)container;
|
||||||
|
if (presenter.TemplatedParent is ComboBox) {
|
||||||
|
return (DataTemplate)presenter.FindResource("WineOriginTemplateCollapsed");
|
||||||
|
} else {
|
||||||
|
return (DataTemplate)presenter.FindResource("WineOriginTemplateExpanded");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
Elwig/Controls/WineQualityLevelTemplateSelector.cs
Normal file
15
Elwig/Controls/WineQualityLevelTemplateSelector.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace Elwig.Controls {
|
||||||
|
public class WineQualityLevelTemplateSelector : DataTemplateSelector {
|
||||||
|
public override DataTemplate SelectTemplate(object item, DependencyObject container) {
|
||||||
|
ContentPresenter presenter = (ContentPresenter)container;
|
||||||
|
if (presenter.TemplatedParent is ComboBox) {
|
||||||
|
return (DataTemplate)presenter.FindResource("WineQualityLevelTemplateCollapsed");
|
||||||
|
} else {
|
||||||
|
return (DataTemplate)presenter.FindResource("WineQualityLevelTemplateExpanded");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
Elwig/Controls/WineVarietyTemplateSelector.cs
Normal file
15
Elwig/Controls/WineVarietyTemplateSelector.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace Elwig.Controls {
|
||||||
|
public class WineVarietyTemplateSelector : DataTemplateSelector {
|
||||||
|
public override DataTemplate SelectTemplate(object item, DependencyObject container) {
|
||||||
|
ContentPresenter presenter = (ContentPresenter)container;
|
||||||
|
if (presenter.TemplatedParent is ComboBox) {
|
||||||
|
return (DataTemplate)presenter.FindResource("WineVarietyTemplateCollapsed");
|
||||||
|
} else {
|
||||||
|
return (DataTemplate)presenter.FindResource("WineVarietyTemplateExpanded");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using Elwig.Helpers;
|
using Elwig.Helpers;
|
||||||
|
using Elwig.Models;
|
||||||
using Elwig.Models.Entities;
|
using Elwig.Models.Entities;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
@ -156,7 +157,7 @@ namespace Elwig.Documents {
|
|||||||
tbl += $"<tr><th>Gesamtlieferung lt. gez. GA</th>{FormatRow(
|
tbl += $"<tr><th>Gesamtlieferung lt. gez. GA</th>{FormatRow(
|
||||||
Member.BusinessShares * season.MinKgPerBusinessShare,
|
Member.BusinessShares * season.MinKgPerBusinessShare,
|
||||||
Member.BusinessShares * season.MaxKgPerBusinessShare,
|
Member.BusinessShares * season.MaxKgPerBusinessShare,
|
||||||
Member.Deliveries.Where(d => d.Year == season.Year).Sum(d => d.Weight),
|
season.Deliveries.Where(d => d.MgNr == Member.MgNr).Sum(d => d.Weight),
|
||||||
isGa: true, showPayment: includePayment, showArea: !includeDelivery
|
isGa: true, showPayment: includePayment, showArea: !includeDelivery
|
||||||
)}</tr>";
|
)}</tr>";
|
||||||
if (fbs.Any()) {
|
if (fbs.Any()) {
|
||||||
|
@ -55,13 +55,13 @@ aside {
|
|||||||
|
|
||||||
aside table {
|
aside table {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
border: 0.5pt solid #808080;
|
border: var(--border-thickness) solid #808080;
|
||||||
width: 65mm;
|
width: 65mm;
|
||||||
margin-right: 10mm;
|
margin-right: 10mm;
|
||||||
}
|
}
|
||||||
|
|
||||||
aside table thead:not(:first-child) tr {
|
aside table thead:not(:first-child) tr {
|
||||||
border-top: 0.5pt solid #808080;
|
border-top: var(--border-thickness) solid #808080;
|
||||||
}
|
}
|
||||||
|
|
||||||
aside table thead tr {
|
aside table thead tr {
|
||||||
@ -143,7 +143,7 @@ main h1 {
|
|||||||
|
|
||||||
.main-wrapper .signatures > * {
|
.main-wrapper .signatures > * {
|
||||||
width: 50mm;
|
width: 50mm;
|
||||||
border-top: 0.5pt solid black;
|
border-top: var(--border-thickness) solid black;
|
||||||
padding-top: 1mm;
|
padding-top: 1mm;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 10pt;
|
font-size: 10pt;
|
||||||
|
@ -1,33 +1,21 @@
|
|||||||
using Elwig.Helpers;
|
using Elwig.Models.Dtos;
|
||||||
using Elwig.Models.Entities;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Elwig.Documents {
|
namespace Elwig.Documents {
|
||||||
public class DeliveryJournal : Document {
|
public class DeliveryJournal : Document {
|
||||||
|
|
||||||
public string Filter;
|
public new static string Name => "Lieferjournal";
|
||||||
public IEnumerable<DeliveryPart> Deliveries;
|
|
||||||
|
|
||||||
public DeliveryJournal(string filter, IEnumerable<DeliveryPart> deliveries) : base($"Lieferjournal {filter}") {
|
public string Filter;
|
||||||
|
public IEnumerable<DeliveryJournalRow> Deliveries;
|
||||||
|
|
||||||
|
public DeliveryJournal(string filter, IEnumerable<DeliveryJournalRow> deliveries) : base($"{Name} {filter}") {
|
||||||
Filter = filter;
|
Filter = filter;
|
||||||
Deliveries = deliveries;
|
Deliveries = deliveries;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeliveryJournal(string filter, IQueryable<DeliveryPart> deliveries) :
|
public DeliveryJournal(string filter, DeliveryJournalData data) :
|
||||||
this(filter, deliveries
|
this(filter, data.Rows) {
|
||||||
.Include(p => p.Delivery).ThenInclude(d => d.Member)
|
}
|
||||||
.Include(p => p.Variety)
|
|
||||||
.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)) { }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,8 @@
|
|||||||
<th rowspan="2" class="narrow">Pos.</th>
|
<th rowspan="2" class="narrow">Pos.</th>
|
||||||
<th rowspan="2">Datum</th>
|
<th rowspan="2">Datum</th>
|
||||||
<th rowspan="2">Zeit</th>
|
<th rowspan="2">Zeit</th>
|
||||||
<th colspan="2" rowspan="2" style="text-align: left;">Mitglied</th>
|
<th rowspan="2">MgNr.</th>
|
||||||
|
<th rowspan="2" style="text-align: left;">Mitglied</th>
|
||||||
<th rowspan="2" style="text-align: left;">Sorte</th>
|
<th rowspan="2" style="text-align: left;">Sorte</th>
|
||||||
<th colspan="2">Gradation</th>
|
<th colspan="2">Gradation</th>
|
||||||
<th>Gewicht</th>
|
<th>Gewicht</th>
|
||||||
@ -39,13 +40,13 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
@foreach (var p in Model.Deliveries) {
|
@foreach (var p in Model.Deliveries) {
|
||||||
<tr>
|
<tr>
|
||||||
<td>@p.Delivery.LsNr</td>
|
<td>@p.LsNr</td>
|
||||||
<td>@p.DPNr</td>
|
<td>@p.Pos</td>
|
||||||
<td class="small">@($"{p.Delivery.Date:dd.MM.yyyy}")</td>
|
<td class="small">@($"{p.Date:dd.MM.yyyy}")</td>
|
||||||
<td class="small">@($"{p.Delivery.Time:HH:mm}")</td>
|
<td class="small">@($"{p.Time:HH:mm}")</td>
|
||||||
<td class="number">@p.Delivery.Member.MgNr</td>
|
<td class="number">@p.MgNr</td>
|
||||||
<td class="small">@p.Delivery.Member.AdministrativeName</td>
|
<td class="small">@p.AdministrativeName</td>
|
||||||
<td class="small">@p.Variety.Name</td>
|
<td class="small">@p.Variety</td>
|
||||||
<td class="center">@($"{p.Oe:N0}")</td>
|
<td class="center">@($"{p.Oe:N0}")</td>
|
||||||
<td class="center">@($"{p.Kmw:N1}")</td>
|
<td class="center">@($"{p.Kmw:N1}")</td>
|
||||||
<td class="number">@($"{p.Weight:N0}")</td>
|
<td class="number">@($"{p.Weight:N0}")</td>
|
||||||
@ -57,7 +58,7 @@
|
|||||||
var oe = Elwig.Helpers.Utils.KmwToOe(kmw);
|
var oe = Elwig.Helpers.Utils.KmwToOe(kmw);
|
||||||
}
|
}
|
||||||
<td colspan="2">Gesamt:</td>
|
<td colspan="2">Gesamt:</td>
|
||||||
<td colspan="5">(Teil-)Lieferungen: @($"{Model.Deliveries.DistinctBy(p => p.Delivery).Count():N0}") (@($"{Model.Deliveries.Count():N0}"))</td>
|
<td colspan="5">(Teil-)Lieferungen: @($"{Model.Deliveries.DistinctBy(p => p.LsNr).Count():N0}") (@($"{Model.Deliveries.Count():N0}"))</td>
|
||||||
<td class="center">@($"{oe:N0}")</td>
|
<td class="center">@($"{oe:N0}")</td>
|
||||||
<td class="center">@($"{kmw:N1}")</td>
|
<td class="center">@($"{kmw:N1}")</td>
|
||||||
<td class="number">@($"{Model.Deliveries.Sum(p => p.Weight):N0}")</td>
|
<td class="number">@($"{Model.Deliveries.Sum(p => p.Weight):N0}")</td>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 24pt;
|
font-size: 24pt;
|
||||||
|
margin-top: 10mm;
|
||||||
margin-bottom: 2mm;
|
margin-bottom: 2mm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@ using System.Collections.Generic;
|
|||||||
namespace Elwig.Documents {
|
namespace Elwig.Documents {
|
||||||
public class DeliveryNote : BusinessDocument {
|
public class DeliveryNote : BusinessDocument {
|
||||||
|
|
||||||
|
public new static string Name => "Traubenübernahmeschein";
|
||||||
|
|
||||||
public Delivery Delivery;
|
public Delivery Delivery;
|
||||||
public string? Text;
|
public string? Text;
|
||||||
public Dictionary<string, MemberBucket> MemberBuckets;
|
public Dictionary<string, MemberBucket> MemberBuckets;
|
||||||
@ -15,7 +17,7 @@ namespace Elwig.Documents {
|
|||||||
// 3 - full
|
// 3 - full
|
||||||
public int DisplayStats = App.Client.ModeDeliveryNoteStats;
|
public int DisplayStats = App.Client.ModeDeliveryNoteStats;
|
||||||
|
|
||||||
public DeliveryNote(Delivery d, AppDbContext? ctx = null) : base($"Traubenübernahmeschein Nr. {d.LsNr}", d.Member) {
|
public DeliveryNote(Delivery d, AppDbContext? ctx = null) : base($"{Name} Nr. {d.LsNr}", d.Member) {
|
||||||
UseBillingAddress = true;
|
UseBillingAddress = true;
|
||||||
ShowDateAndLocation = true;
|
ShowDateAndLocation = true;
|
||||||
Delivery = d;
|
Delivery = d;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
width: 10mm;
|
width: 10mm;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: -25mm;
|
left: -25mm;
|
||||||
border-top: 0.5pt solid black;
|
border-top: var(--border-thickness) solid black;
|
||||||
}
|
}
|
||||||
.m1.r, .m2.r, .m3.r {
|
.m1.r, .m2.r, .m3.r {
|
||||||
left: initial;
|
left: initial;
|
||||||
|
@ -11,7 +11,7 @@ main table.tiny {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main table.border {
|
main table.border {
|
||||||
border: 0.5pt solid black;
|
border: var(--border-thickness) solid black;
|
||||||
}
|
}
|
||||||
|
|
||||||
main table tr {
|
main table tr {
|
||||||
@ -113,7 +113,28 @@ main table tr.sectionheading th {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 10pt;
|
font-size: 10pt;
|
||||||
border-top: 0.5pt solid black;
|
border-top: var(--border-thickness) solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
main table tr.header {
|
||||||
|
border: var(--border-thickness) solid black;
|
||||||
|
background-color: #E0E0E0;
|
||||||
|
}
|
||||||
|
|
||||||
|
main table tr.header th {
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 16pt;
|
||||||
|
padding: 1mm 2mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
main table tr.spacing td,
|
||||||
|
main table tr.spacing th {
|
||||||
|
height: 5mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
main table tr.spacing ~ tr.header {
|
||||||
|
break-before: avoid;
|
||||||
}
|
}
|
||||||
|
|
||||||
main table.number thead tr:first-child th:first-child {
|
main table.number thead tr:first-child th:first-child {
|
||||||
@ -128,7 +149,7 @@ main table tr.sum,
|
|||||||
main table td.sum,
|
main table td.sum,
|
||||||
main table tr.new,
|
main table tr.new,
|
||||||
main table tr.border {
|
main table tr.border {
|
||||||
border-top: 0.5pt solid black;
|
border-top: var(--border-thickness) solid black;
|
||||||
}
|
}
|
||||||
|
|
||||||
main table th.unit {
|
main table th.unit {
|
||||||
@ -144,13 +165,14 @@ main table th.narrow {
|
|||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
main table .lborder {border-left: 0.5pt solid black;}
|
main table .tborder {border-top: var(--border-thickness) solid black;}
|
||||||
main table .rborder {border-right: 0.5pt solid black;}
|
main table .lborder {border-left: var(--border-thickness) solid black;}
|
||||||
|
main table .rborder {border-right: var(--border-thickness) solid black;}
|
||||||
|
|
||||||
main table .fleft {
|
main table .fleft {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
main tbody.sum tr:last-child {
|
main tbody.sum tr:last-child {
|
||||||
border-bottom: 0.5pt solid black;
|
border-bottom: var(--border-thickness) solid black;
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,12 @@ namespace Elwig.Documents {
|
|||||||
name = "DeliveryConfirmation";
|
name = "DeliveryConfirmation";
|
||||||
} else if (this is MemberDataSheet) {
|
} else if (this is MemberDataSheet) {
|
||||||
name = "MemberDataSheet";
|
name = "MemberDataSheet";
|
||||||
|
} else if (this is MemberList) {
|
||||||
|
name = "MemberList";
|
||||||
|
} else if (this is WineQualityStatistics) {
|
||||||
|
name = "WineQualityStatistics";
|
||||||
|
} else if (this is PaymentVariantSummary) {
|
||||||
|
name = "PaymentVariantSummary";
|
||||||
} else {
|
} else {
|
||||||
throw new InvalidOperationException("Invalid document object");
|
throw new InvalidOperationException("Invalid document object");
|
||||||
}
|
}
|
||||||
@ -95,6 +101,8 @@ namespace Elwig.Documents {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task Generate(IProgress<double>? progress = null) {
|
public async Task Generate(IProgress<double>? progress = null) {
|
||||||
|
if (_pdfFile != null)
|
||||||
|
return;
|
||||||
progress?.Report(0.0);
|
progress?.Report(0.0);
|
||||||
if (this is PdfDocument) {
|
if (this is PdfDocument) {
|
||||||
// nothing to do
|
// nothing to do
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
:root {
|
:root {
|
||||||
font-family: "Times New Roman", serif;
|
font-family: "Times New Roman", serif;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
--border-thickness: 0.5pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
@ -58,7 +59,7 @@ header .type {
|
|||||||
position: running(page-footer);
|
position: running(page-footer);
|
||||||
width: 165mm;
|
width: 165mm;
|
||||||
/* for some reason the position without the following statement changes on the second page */
|
/* for some reason the position without the following statement changes on the second page */
|
||||||
border: 0.5pt solid #00000000;
|
border: var(--border-thickness) solid #00000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-wrapper.left {
|
.footer-wrapper.left {
|
||||||
@ -95,7 +96,7 @@ header .type {
|
|||||||
|
|
||||||
footer {
|
footer {
|
||||||
font-size: 10pt;
|
font-size: 10pt;
|
||||||
border-top: 0.5pt solid black;
|
border-top: var(--border-thickness) solid black;
|
||||||
height: 25mm;
|
height: 25mm;
|
||||||
padding-top: 1mm;
|
padding-top: 1mm;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -107,6 +108,6 @@ footer {
|
|||||||
|
|
||||||
hr {
|
hr {
|
||||||
border: none;
|
border: none;
|
||||||
border-top: 0.5pt solid black;
|
border-top: var(--border-thickness) solid black;
|
||||||
margin: 5mm 0;
|
margin: 5mm 0;
|
||||||
}
|
}
|
||||||
|
@ -11,11 +11,13 @@ namespace Elwig.Documents {
|
|||||||
|
|
||||||
public Season Season;
|
public Season Season;
|
||||||
public Dictionary<string, MemberBucket> MemberBuckets;
|
public Dictionary<string, MemberBucket> MemberBuckets;
|
||||||
|
public IEnumerable<AreaCom> ActiveAreaCommitments;
|
||||||
|
|
||||||
public MemberDataSheet(Member m, AppDbContext ctx) : base($"{Name} {m.AdministrativeName}", m) {
|
public MemberDataSheet(Member m, AppDbContext ctx) : base($"{Name} {m.AdministrativeName}", m) {
|
||||||
DocumentId = $"{Name} {m.MgNr}";
|
DocumentId = $"{Name} {m.MgNr}";
|
||||||
Season = ctx.Seasons.ToList().MaxBy(s => s.Year) ?? throw new ArgumentException("invalid season");
|
Season = ctx.Seasons.ToList().MaxBy(s => s.Year) ?? throw new ArgumentException("invalid season");
|
||||||
MemberBuckets = ctx.GetMemberBuckets(Utils.CurrentYear, m.MgNr).GetAwaiter().GetResult();
|
MemberBuckets = ctx.GetMemberBuckets(Utils.CurrentYear, m.MgNr).GetAwaiter().GetResult();
|
||||||
|
ActiveAreaCommitments = m.ActiveAreaCommitments(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,7 @@
|
|||||||
@using Elwig.Helpers
|
@using Elwig.Helpers
|
||||||
@inherits TemplatePage<Elwig.Documents.MemberDataSheet>
|
@inherits TemplatePage<Elwig.Documents.MemberDataSheet>
|
||||||
@model Elwig.Documents.MemberDataSheet
|
@model Elwig.Documents.MemberDataSheet
|
||||||
@{
|
@{ Layout = "BusinessDocument"; }
|
||||||
Layout = "BusinessDocument";
|
|
||||||
}
|
|
||||||
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\MemberDataSheet.css" />
|
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\MemberDataSheet.css" />
|
||||||
<main>
|
<main>
|
||||||
<h1>@Model.Title</h1>
|
<h1>@Model.Title</h1>
|
||||||
@ -149,7 +147,7 @@
|
|||||||
@Raw(Model.PrintBucketTable(Model.Season, Model.MemberBuckets, includeDelivery: false))
|
@Raw(Model.PrintBucketTable(Model.Season, Model.MemberBuckets, includeDelivery: false))
|
||||||
|
|
||||||
@{
|
@{
|
||||||
var areaComs = Model.Member.ActiveAreaCommitments.GroupBy(a => a.AreaComType).Select(group => new {
|
var areaComs = Model.ActiveAreaCommitments.GroupBy(a => a.AreaComType).Select(group => new {
|
||||||
AreaComType = group.Key,
|
AreaComType = group.Key,
|
||||||
AreaComs = group.OrderBy(c => c.Kg.AtKg.Name),
|
AreaComs = group.OrderBy(c => c.Kg.AtKg.Name),
|
||||||
Size = group.Sum(c => c.Area)
|
Size = group.Sum(c => c.Area)
|
||||||
@ -205,7 +203,7 @@
|
|||||||
}
|
}
|
||||||
<tr class="sum bold">
|
<tr class="sum bold">
|
||||||
<td colspan="3">Gesamt:</td>
|
<td colspan="3">Gesamt:</td>
|
||||||
<td class="number">@($"{Model.Member.ActiveAreaCommitments.Select(a => a.Area).Sum():N0}")</td>
|
<td class="number">@($"{Model.ActiveAreaCommitments.Sum(a => a.Area):N0}")</td>
|
||||||
<td colspan="2"></td>
|
<td colspan="2"></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
21
Elwig/Documents/MemberList.cs
Normal file
21
Elwig/Documents/MemberList.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using Elwig.Models.Dtos;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Elwig.Documents {
|
||||||
|
public class MemberList : Document {
|
||||||
|
|
||||||
|
public new static string Name => "Mitgliederliste";
|
||||||
|
|
||||||
|
public string Filter;
|
||||||
|
public IEnumerable<MemberListRow> Members;
|
||||||
|
|
||||||
|
public MemberList(string filter, IEnumerable<MemberListRow> members) : base(Name) {
|
||||||
|
Filter = filter;
|
||||||
|
Members = members;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MemberList(string filter, MemberListData data) :
|
||||||
|
this(filter, data.Rows) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
72
Elwig/Documents/MemberList.cshtml
Normal file
72
Elwig/Documents/MemberList.cshtml
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
@using RazorLight
|
||||||
|
@inherits TemplatePage<Elwig.Documents.MemberList>
|
||||||
|
@model Elwig.Documents.MemberList
|
||||||
|
@{ Layout = "Document"; }
|
||||||
|
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\MemberList.css"/>
|
||||||
|
<main>
|
||||||
|
<h1>Mitgliederliste</h1>
|
||||||
|
<h2>@Model.Filter</h2>
|
||||||
|
<table class="members">
|
||||||
|
<colgroup>
|
||||||
|
<col style="width: 8mm;"/>
|
||||||
|
<col style="width: 42mm;"/>
|
||||||
|
<col style="width: 40mm;"/>
|
||||||
|
<col style="width: 8mm;"/>
|
||||||
|
<col style="width: 20mm;"/>
|
||||||
|
<col style="width: 12mm;"/>
|
||||||
|
<col style="width: 5mm;" />
|
||||||
|
<col style="width: 18mm;"/>
|
||||||
|
<col style="width: 12mm;"/>
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th rowspan="2">Nr.</th>
|
||||||
|
<th rowspan="2" style="text-align: left;">Name</th>
|
||||||
|
<th rowspan="2" style="text-align: left;">Adresse</th>
|
||||||
|
<th rowspan="2">PLZ</th>
|
||||||
|
<th rowspan="2" style="text-align: left;">Ort</th>
|
||||||
|
<th rowspan="2">Betr.-Nr.</th>
|
||||||
|
<th rowspan="2">GA</th>
|
||||||
|
<th rowspan="2" style="text-align: left;">Stamm-KG</th>
|
||||||
|
<th>Geb. Fl.</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th class="unit">[m²]</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="small">
|
||||||
|
@{
|
||||||
|
string? lastBranch = Model.Members.Select(m => m.Branch).Distinct().Count() == 1 ? null : "";
|
||||||
|
}
|
||||||
|
@foreach (var m in Model.Members) {
|
||||||
|
if (lastBranch != null && m.Branch != lastBranch) {
|
||||||
|
<tr class="spacing"><td colspan="9"></td></tr>
|
||||||
|
<tr class="header">
|
||||||
|
<th colspan="9">@m.Branch</th>
|
||||||
|
</tr>
|
||||||
|
lastBranch = m.Branch;
|
||||||
|
}
|
||||||
|
<tr>
|
||||||
|
<td class="number" rowspan="@(m.BillingName != null ? 2 : 1)">@m.MgNr</td>
|
||||||
|
<td>@m.Name1.Replace('ß', 'ẞ').ToUpper() @m.Name2</td>
|
||||||
|
<td>@m.Address</td>
|
||||||
|
<td>@m.Plz</td>
|
||||||
|
<td class="tiny">@m.Locality</td>
|
||||||
|
<td>@m.LfbisNr</td>
|
||||||
|
<td class="number">@m.BusinessShares</td>
|
||||||
|
<td class="tiny">@m.DefaultKg</td>
|
||||||
|
<td class="number">@($"{m.AreaCommitment:N0}")</td>
|
||||||
|
</tr>
|
||||||
|
if (m.BillingName != null) {
|
||||||
|
<tr>
|
||||||
|
<td>@m.BillingName</td>
|
||||||
|
<td>@m.BillingAddress</td>
|
||||||
|
<td>@m.BillingPlz</td>
|
||||||
|
<td class="tiny">@m.BillingLocality</td>
|
||||||
|
<td colspan="4"></td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</main>
|
13
Elwig/Documents/MemberList.css
Normal file
13
Elwig/Documents/MemberList.css
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 24pt;
|
||||||
|
margin-top: 10mm;
|
||||||
|
margin-bottom: 2mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14pt;
|
||||||
|
margin-top: 2mm;
|
||||||
|
}
|
30
Elwig/Documents/PaymentVariantSummary.cs
Normal file
30
Elwig/Documents/PaymentVariantSummary.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using Elwig.Helpers.Billing;
|
||||||
|
using Elwig.Models.Dtos;
|
||||||
|
using Elwig.Models.Entities;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Elwig.Documents {
|
||||||
|
public class PaymentVariantSummary : Document {
|
||||||
|
|
||||||
|
public new static string Name => "Auszahlungsvariante";
|
||||||
|
|
||||||
|
public PaymentVariantSummaryData Data;
|
||||||
|
public PaymentVar Variant;
|
||||||
|
public BillingData BillingData;
|
||||||
|
public string CurrencySymbol;
|
||||||
|
public int MemberNum;
|
||||||
|
public int DeliveryNum;
|
||||||
|
public int DeliveryPartNum;
|
||||||
|
|
||||||
|
public PaymentVariantSummary(PaymentVar v, PaymentVariantSummaryData data) :
|
||||||
|
base($"{Name} {v.Year} - {v.Name}") {
|
||||||
|
Variant = v;
|
||||||
|
BillingData = BillingData.FromJson(v.Data);
|
||||||
|
Data = data;
|
||||||
|
CurrencySymbol = v.Season.Currency.Symbol ?? v.Season.Currency.Code;
|
||||||
|
MemberNum = v.Credits.Count;
|
||||||
|
DeliveryNum = v.DeliveryPartPayments.DistinctBy(p => p.DeliveryPart.Delivery).Count();
|
||||||
|
DeliveryPartNum = v.DeliveryPartPayments.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
217
Elwig/Documents/PaymentVariantSummary.cshtml
Normal file
217
Elwig/Documents/PaymentVariantSummary.cshtml
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
@using RazorLight
|
||||||
|
@using Elwig.Helpers
|
||||||
|
@inherits TemplatePage<Elwig.Documents.PaymentVariantSummary>
|
||||||
|
@model Elwig.Documents.PaymentVariantSummary
|
||||||
|
@{ Layout = "Document"; }
|
||||||
|
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\PaymentVariantSummary.css" />
|
||||||
|
<main>
|
||||||
|
<h1>Auszahlungsvariante Lese @Model.Variant.Year</h1>
|
||||||
|
<h2>@Model.Variant.Name</h2>
|
||||||
|
<table class="payment-variant border">
|
||||||
|
<colgroup>
|
||||||
|
<col style="width: 20.0mm;"/>
|
||||||
|
<col style="width: 30.0mm;"/>
|
||||||
|
<col style="width: 4.5mm;"/>
|
||||||
|
<col style="width: 28.0mm;"/>
|
||||||
|
<col style="width: 47.5mm;"/>
|
||||||
|
<col style="width: 15.0mm;"/>
|
||||||
|
<col style="width: 20.0mm;"/>
|
||||||
|
</colgroup>
|
||||||
|
@{
|
||||||
|
//var sum1 = Model.Variant.DeliveryPartPayments.Sum(p => p.NetAmount);
|
||||||
|
//var sum2 = Model.Variant.Credits.Sum(p => p.); //Model.Variant.MemberPayments.Sum(p => p.Amount);
|
||||||
|
var modifiers = Model.Variant.DeliveryPartPayments.Sum(p => p.Amount - p.NetAmount);
|
||||||
|
var sum2 = Model.Variant.Credits.Sum(p => p.NetAmount);
|
||||||
|
var sum1 = sum2 - modifiers;
|
||||||
|
var payed = -Model.Variant.Credits.Sum(p => p.PrevNetAmount ?? 0m);
|
||||||
|
var netSum = Model.Variant.Credits.Sum(p => p.NetAmount) - Model.Variant.Credits.Sum(p => p.PrevNetAmount ?? 0m);
|
||||||
|
var vat = Model.Variant.Credits.Sum(p => p.VatAmount);
|
||||||
|
var grossSum = Model.Variant.Credits.Sum(p => p.GrossAmount);
|
||||||
|
var totalMods = Model.Variant.Credits.Sum(p => p.Modifiers ?? 0m);
|
||||||
|
var considered = -Model.Variant.Credits.Sum(p => p.PrevModifiers ?? 0m);
|
||||||
|
var totalSum = Model.Variant.Credits.Sum(p => p.Amount);
|
||||||
|
}
|
||||||
|
<tbody>
|
||||||
|
<tr class="sectionheading">
|
||||||
|
<th colspan="4">Allgemein</th>
|
||||||
|
<th colspan="3" class="lborder">Berücksichtigt</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Name:</th>
|
||||||
|
<td colspan="3">@Model.Variant.Name</td>
|
||||||
|
<th colspan="2" class="lborder">Zu-/Abschläge bei Lieferungen:</th>
|
||||||
|
<td class="center">@(Model.BillingData.ConsiderDelieryModifiers ? "Ja" : "Nein")</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Beschr.:</th>
|
||||||
|
<td colspan="3">@Model.Variant.Comment</td>
|
||||||
|
<th colspan="2" class="lborder">Pönalen bei Unterlieferungen (FB):</th>
|
||||||
|
<td class="center">@(Model.BillingData.ConsiderContractPenalties ? "Ja" : "Nein")</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Datum:</th>
|
||||||
|
<td colspan="3">@($"{Model.Variant.Date:dd.MM.yyyy}")</td>
|
||||||
|
<th colspan="2" class="lborder">Strafen bei Unterlieferungen (GA):</th>
|
||||||
|
<td class="center">@(Model.BillingData.ConsiderTotalPenalty ? "Ja" : "Nein")</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Überw.:</th>
|
||||||
|
<td colspan="3">@($"{Model.Variant.TransferDate:dd.MM.yyyy}")</td>
|
||||||
|
<th colspan="2" class="lborder">Automatische Nachzeichnung der GA:</th>
|
||||||
|
<td class="center">@(Model.BillingData.ConsiderAutoBusinessShares ? "Ja" : "Nein")</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="sectionheading">
|
||||||
|
<th colspan="4">Beträge</th>
|
||||||
|
<th colspan="3" class="lborder">Statistik</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2">Zwischensumme:</th>
|
||||||
|
<td></td>
|
||||||
|
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{sum1:N2}")</td>
|
||||||
|
<th class="lborder">Lieferanten:</th>
|
||||||
|
<td colspan="2" class="number">@($"{Model.MemberNum:N0}")</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2">Zu-/Abschläge (Lieferungen):</th>
|
||||||
|
<td class="number">@Utils.GetSign(modifiers)</td>
|
||||||
|
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(modifiers):N2}")</td>
|
||||||
|
<th class="lborder">Lieferungen:</th>
|
||||||
|
<td colspan="2" class="number">@($"{Model.DeliveryNum:N0}")</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2">Gesamtsumme:</th>
|
||||||
|
<td class="number tborder"></td>
|
||||||
|
<td class="number tborder"><span class="fleft">@Model.CurrencySymbol</span>@($"{sum2:N2}")</td>
|
||||||
|
<th class="lborder">Teillieferungen:</th>
|
||||||
|
<td colspan="2" class="number">@($"{Model.DeliveryPartNum:N0}")</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2">Bisher ausgezahlt:</th>
|
||||||
|
<td class="number">@Utils.GetSign(payed)</td>
|
||||||
|
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(payed):N2}")</td>
|
||||||
|
<th class="lborder"></th>
|
||||||
|
<td colspan="2"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2">Nettosumme:</th>
|
||||||
|
<td class="number tborder"></td>
|
||||||
|
<td class="number tborder"><span class="fleft">@Model.CurrencySymbol</span>@($"{netSum:N2}")</td>
|
||||||
|
@{
|
||||||
|
var weiRows = Model.Data.Rows.Where(r => r.QualityLevel == "Wein");
|
||||||
|
var minWei = weiRows.Min(r => r.Ungeb.Price);
|
||||||
|
var maxWei = weiRows.Max(r => r.Ungeb.Price);
|
||||||
|
}
|
||||||
|
<th class="lborder tborder">Preis (abgewertet):</th>
|
||||||
|
<td colspan="2" class="center tborder">@(minWei != maxWei ? $"{minWei:N4}–{maxWei:N4}" : $"{minWei:N4}") @Model.CurrencySymbol/kg</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2">Mehrwertsteuer:</th>
|
||||||
|
<td class="number">@Utils.GetSign(vat)</td>
|
||||||
|
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(vat):N2}")</td>
|
||||||
|
@{
|
||||||
|
var quwRows = Model.Data.Rows.Where(r => r.QualityLevel != "Wein");
|
||||||
|
var minPrice = quwRows.Min(r => r.Ungeb.Price);
|
||||||
|
var maxPrice = quwRows.Max(r => r.Ungeb.Price);
|
||||||
|
}
|
||||||
|
<th class="lborder">Preis (ungeb., nicht abgew.):</th>
|
||||||
|
<td colspan="2" class="center">@(minPrice != maxPrice ? $"{minPrice:N4}–{maxPrice:N4}" : $"{minPrice:N4}") @Model.CurrencySymbol/kg</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2">Bruttosumme:</th>
|
||||||
|
<td class="number tborder"></td>
|
||||||
|
<td class="number tborder"><span class="fleft">@Model.CurrencySymbol</span>@($"{grossSum:N2}")</td>
|
||||||
|
@{
|
||||||
|
var gebRows = Model.Data.Rows
|
||||||
|
.Where(r => r.Geb.Price != null && r.Ungeb.Price != null)
|
||||||
|
.Select(r => r.Geb.Price - r.Ungeb.Price);
|
||||||
|
var minGeb = gebRows.Min();
|
||||||
|
var maxGeb = gebRows.Max();
|
||||||
|
}
|
||||||
|
<th class="lborder">Gebunden-Zuschlag:</th>
|
||||||
|
<td colspan="2" class="center">
|
||||||
|
@(minGeb != maxGeb ? $"{minGeb:N4}–{maxGeb:N4} {Model.CurrencySymbol}/kg" : minGeb == 0 ? "-" : $"{minGeb:N4} {Model.CurrencySymbol}/kg")
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2">Abzüge (Strafen/Pönalen, GA, ...):</th>
|
||||||
|
<td class="number">@Utils.GetSign(totalMods)</td>
|
||||||
|
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(totalMods):N2}")</td>
|
||||||
|
<th class="lborder tborder">Menge (ungebunden):</th>
|
||||||
|
<td colspan="2" class="number tborder">@($"{Model.Data.Rows.Sum(r => r.Ungeb.Weight):N0}") kg</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2">Bereits berücksichtigte Abzüge:</th>
|
||||||
|
<td class="number">@Utils.GetSign(considered)</td>
|
||||||
|
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(considered):N2}")</td>
|
||||||
|
<th class="lborder">Menge (gebunden):</th>
|
||||||
|
<td colspan="2" class="number">@($"{Model.Data.Rows.Sum(r => r.Geb.Weight):N0}") kg</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2">Auszahlungsbetrag:</th>
|
||||||
|
<td class="number tborder"></td>
|
||||||
|
<td class="number tborder"><span class="fleft">@Model.CurrencySymbol</span>@($"{totalSum:N2}")</td>
|
||||||
|
<th class="lborder">Gesamtmenge:</th>
|
||||||
|
<td colspan="2" class="number tborder">@($"{Model.Data.Rows.Sum(r => r.Ungeb.Weight + r.Geb.Weight):N0}") kg</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table class="payment-variant-data">
|
||||||
|
<colgroup>
|
||||||
|
<col style="width: 30mm;"/>
|
||||||
|
<col style="width: 20mm;"/>
|
||||||
|
<col style="width: 25mm;"/>
|
||||||
|
<col style="width: 20mm;"/>
|
||||||
|
<col style="width: 25mm;"/>
|
||||||
|
<col style="width: 20mm;"/>
|
||||||
|
<col style="width: 25mm;"/>
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th rowspan="2" style="text-align: left;">Qualitätsstufe</th>
|
||||||
|
<th>Gradation</th>
|
||||||
|
<th colspan="2">ungebunden</th>
|
||||||
|
<th colspan="2">gebunden</th>
|
||||||
|
<th>Gesamt</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>[@(true ? "°Oe" : "°KMW")]</th>
|
||||||
|
<th>[kg]</th>
|
||||||
|
<th>[@(Model.CurrencySymbol)/kg]</th>
|
||||||
|
<th>[kg]</th>
|
||||||
|
<th>[@(Model.CurrencySymbol)/kg]</th>
|
||||||
|
<th>[@(Model.CurrencySymbol)]</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@{
|
||||||
|
string? lastHdr = null;
|
||||||
|
}
|
||||||
|
@foreach (var row in Model.Data.Rows) {
|
||||||
|
var hdr = $"{row.Variety}{(row.Attribute != null ? " / " : "")}{row.Attribute}{(row.Cultivation != null ? " / " : "")}{row.Cultivation}";
|
||||||
|
if (lastHdr != hdr) {
|
||||||
|
var rows = Model.Data.Rows
|
||||||
|
.Where(r => r.Variety == row.Variety && r.Attribute == row.Attribute && r.Cultivation == row.Cultivation)
|
||||||
|
.ToList();
|
||||||
|
<tr class="subheading @(lastHdr != null ? "new" : "")">
|
||||||
|
<th colspan="2">@hdr</th>
|
||||||
|
<td class="number">@($"{rows.Sum(r => r.Ungeb.Weight):N0}")</td>
|
||||||
|
<td></td>
|
||||||
|
<td class="number">@($"{rows.Sum(r => r.Geb.Weight):N0}")</td>
|
||||||
|
<td></td>
|
||||||
|
<td class="number">@($"{rows.Sum(r => r.Amount):N2}")</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
<tr>
|
||||||
|
<td>@(row.QualityLevel)</td>
|
||||||
|
<td class="center">@($"{row.Oe:N0}")</td>
|
||||||
|
<td class="number">@(row.Ungeb.Weight != 0 ? $"{row.Ungeb.Weight:N0}" : "-")</td>
|
||||||
|
<td class="number">@(row.Ungeb.Price != null ? $"{row.Ungeb.Price:N4}" : "-")</td>
|
||||||
|
<td class="number">@(row.Geb.Weight != 0 ? $"{row.Geb.Weight:N0}" : "-")</td>
|
||||||
|
<td class="number">@(row.Geb.Price != null ? $"{row.Geb.Price:N4}" : "-")</td>
|
||||||
|
<td class="number">@($"{row.Amount:N2}")</td>
|
||||||
|
</tr>
|
||||||
|
lastHdr = hdr;
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</main>
|
21
Elwig/Documents/PaymentVariantSummary.css
Normal file
21
Elwig/Documents/PaymentVariantSummary.css
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 24pt;
|
||||||
|
margin-top: 10mm;
|
||||||
|
margin-bottom: 2mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14pt;
|
||||||
|
margin-top: 2mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.payment-variant {
|
||||||
|
margin-top: 10mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.payment-variant-data {
|
||||||
|
break-before: page;
|
||||||
|
}
|
27
Elwig/Documents/WineQualityStatistics.cs
Normal file
27
Elwig/Documents/WineQualityStatistics.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using Elwig.Models.Dtos;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Elwig.Documents {
|
||||||
|
public class WineQualityStatistics : Document {
|
||||||
|
|
||||||
|
public new static string Name => "Qualitätsstatistik";
|
||||||
|
|
||||||
|
public readonly string[][] QualIds = [["WEI"], ["RSW", "LDW"], ["QUW"], ["KAB"]];
|
||||||
|
public readonly Dictionary<string, string> QualityLevels = new() {
|
||||||
|
["WEI"] = "Wein",
|
||||||
|
["RSW"] = "Rebsortenwein",
|
||||||
|
["LDW"] = "Landwein",
|
||||||
|
["QUW"] = "Qualitätswein",
|
||||||
|
["KAB"] = "Kabinett",
|
||||||
|
};
|
||||||
|
|
||||||
|
public string Filter;
|
||||||
|
public WineQualityStatisticsData Data;
|
||||||
|
public bool UseOe => Data.UseOe;
|
||||||
|
|
||||||
|
public WineQualityStatistics(string filter, WineQualityStatisticsData data) : base($"{Name} {filter}") {
|
||||||
|
Filter = filter;
|
||||||
|
Data = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
81
Elwig/Documents/WineQualityStatistics.cshtml
Normal file
81
Elwig/Documents/WineQualityStatistics.cshtml
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
@using RazorLight
|
||||||
|
@using Elwig.Helpers
|
||||||
|
@inherits TemplatePage<Elwig.Documents.WineQualityStatistics>
|
||||||
|
@model Elwig.Documents.WineQualityStatistics
|
||||||
|
@{ Layout = "Document"; }
|
||||||
|
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\WineQualityStatistics.css"/>
|
||||||
|
<main>
|
||||||
|
<h1>Qualitätsstatistik</h1>
|
||||||
|
<h2>@Model.Filter</h2>
|
||||||
|
@foreach (var sec in Model.Data.Sections) {
|
||||||
|
<table>
|
||||||
|
<colgroup>
|
||||||
|
<col style="width: 25%;"/>
|
||||||
|
<col style="width: 25%;"/>
|
||||||
|
<col style="width: 25%;"/>
|
||||||
|
<col style="width: 25%;"/>
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colspan="4" class="header @(sec.Type == "R" ? "red" : sec.Type == "W" ? "green" : "")">
|
||||||
|
<h3>@sec.Name</h3>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
@foreach (var qualIds in Model.QualIds) {
|
||||||
|
<td class="container">
|
||||||
|
<div class="row">
|
||||||
|
<span class="units">[@(Model.UseOe ? "°Oe" : "°KMW")]</span>
|
||||||
|
<span class="units">[#]</span>
|
||||||
|
<span class="units">[kg]</span>
|
||||||
|
</div>
|
||||||
|
@foreach (var qualId in qualIds) {
|
||||||
|
<h4>@(Model.QualityLevels.GetValueOrDefault(qualId, qualId))</h4>
|
||||||
|
@foreach (var (grad, avgKmw, num, weight) in sec.Data.GetValueOrDefault(qualId, Array.Empty<(double, double, int, int)>())) {
|
||||||
|
<div class="row">
|
||||||
|
<span class="gradation">@(Model.UseOe ? $"{grad:N0}" : $"{grad:N1}")</span>
|
||||||
|
<span class="number">@($"{num:N0}")</span>
|
||||||
|
<span class="number">@($"{weight:N0}")</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
}
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
@foreach (var qualIds in Model.QualIds) {
|
||||||
|
var quals = qualIds.Select(q => sec.Data.GetValueOrDefault(q, Array.Empty<(double Grad, double AvgKmw, int Num, int Weight)>()));
|
||||||
|
var weight = quals.Sum(q => q.Sum(kv => kv.Weight));
|
||||||
|
var num = quals.Sum(q => q.Sum(kv => kv.Num));
|
||||||
|
var kmw = quals.Sum(q => q.Sum(kv => kv.AvgKmw * kv.Weight)) / weight;
|
||||||
|
<td class="container bold">
|
||||||
|
<div class="row">
|
||||||
|
<span class="gradation">@(weight == 0 ? "-" : Model.UseOe ? $"{Utils.KmwToOe(kmw):N0}" : $"{kmw:N1}")</span>
|
||||||
|
<span class="number">@($"{num:N0}")</span>
|
||||||
|
<span class="number">@($"{weight:N0}")</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
}
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
@{
|
||||||
|
var totalWeight = sec.Data.Values.Sum(q => q.Sum(kv => kv.Weight));
|
||||||
|
var totalNum = sec.Data.Values.Sum(q => q.Sum(kv => kv.Num));
|
||||||
|
var totalKmw = sec.Data.Values.Sum(q => q.Sum(kv => kv.AvgKmw * kv.Weight)) / totalWeight;
|
||||||
|
}
|
||||||
|
<td colspan="4" class="container bold footer @(sec.Type == "R" ? "red" : sec.Type == "W" ? "green" : "")">
|
||||||
|
<div class="row" style="width: 24%; margin-left: 76%;">
|
||||||
|
<span class="gradation">@(totalWeight == 0 ? "-" : Model.UseOe ? $"{Utils.KmwToOe(totalKmw):N0}" : $"{totalKmw:N1}")</span>
|
||||||
|
<span class="number">@($"{totalNum:N0}")</span>
|
||||||
|
<span class="number">@($"{totalWeight:N0}")</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
</main>
|
97
Elwig/Documents/WineQualityStatistics.css
Normal file
97
Elwig/Documents/WineQualityStatistics.css
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 24pt;
|
||||||
|
margin-top: 10mm;
|
||||||
|
margin-bottom: 2mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14pt;
|
||||||
|
margin-top: 2mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 14pt;
|
||||||
|
margin: 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 10pt;
|
||||||
|
margin: 0;
|
||||||
|
text-align: center;
|
||||||
|
margin: 2mm 0 2mm 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row:first-child { margin-top: 0.5mm; }
|
||||||
|
.row:last-child { margin-bottom: 0.5mm; }
|
||||||
|
|
||||||
|
.bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
margin-top: 10mm;
|
||||||
|
break-inside: avoid;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th,
|
||||||
|
table td {
|
||||||
|
border: var(--border-thickness) solid black;
|
||||||
|
vertical-align: top !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table .header {
|
||||||
|
padding: 1mm 2mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
table .header,
|
||||||
|
table .footer {
|
||||||
|
background-color: #E0E0E0;
|
||||||
|
}
|
||||||
|
|
||||||
|
table .header.red,
|
||||||
|
table .footer.red {
|
||||||
|
background-color: #FFC0C0;
|
||||||
|
}
|
||||||
|
|
||||||
|
table .header.green,
|
||||||
|
table .footer.green {
|
||||||
|
background-color: #C0FFC0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 10pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row span {
|
||||||
|
flex: 10mm 1 1;
|
||||||
|
display: block;
|
||||||
|
padding: 0 1mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row .units {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 8pt;
|
||||||
|
font-style: italic;
|
||||||
|
padding: 1mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gradation {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.number {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row span:first-child { flex-basis: 7.5mm; }
|
||||||
|
.row span:last-child { flex-basis: 17.5mm; }
|
@ -7,7 +7,7 @@
|
|||||||
<UseWPF>true</UseWPF>
|
<UseWPF>true</UseWPF>
|
||||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||||
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
|
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
|
||||||
<Version>0.7.1</Version>
|
<Version>0.8.2</Version>
|
||||||
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
|
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
@ -27,15 +27,15 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.6.0" />
|
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.6.0" />
|
||||||
<PackageReference Include="LinqKit" Version="1.2.5" />
|
<PackageReference Include="LinqKit" Version="1.2.5" />
|
||||||
<PackageReference Include="MailKit" Version="4.4.0" />
|
<PackageReference Include="MailKit" Version="4.5.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.27" />
|
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.29" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.2" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.4" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.2" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.4" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2365.46" />
|
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2478.35" />
|
||||||
<PackageReference Include="NJsonSchema" Version="11.0.0" />
|
<PackageReference Include="NJsonSchema" Version="11.0.0" />
|
||||||
<PackageReference Include="RazorLight" Version="2.3.1" />
|
<PackageReference Include="RazorLight" Version="2.3.1" />
|
||||||
<PackageReference Include="ScottPlot.WPF" Version="5.0.21" />
|
<PackageReference Include="ScottPlot.WPF" Version="5.0.31" />
|
||||||
<PackageReference Include="System.IO.Ports" Version="8.0.0" />
|
<PackageReference Include="System.IO.Ports" Version="8.0.0" />
|
||||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
|
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -4,18 +4,18 @@ using System.Windows.Input;
|
|||||||
namespace Elwig.Helpers {
|
namespace Elwig.Helpers {
|
||||||
public class ActionCommand : ICommand {
|
public class ActionCommand : ICommand {
|
||||||
|
|
||||||
public event EventHandler CanExecuteChanged;
|
public event EventHandler? CanExecuteChanged;
|
||||||
private readonly Action Action;
|
private readonly Action Action;
|
||||||
|
|
||||||
public ActionCommand(Action action) {
|
public ActionCommand(Action action) {
|
||||||
Action = action;
|
Action = action;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Execute(object parameter) {
|
public void Execute(object? parameter) {
|
||||||
Action();
|
Action();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanExecute(object parameter) {
|
public bool CanExecute(object? parameter) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,7 @@ namespace Elwig.Helpers {
|
|||||||
public DbSet<Member> Members { get; private set; }
|
public DbSet<Member> Members { get; private set; }
|
||||||
public DbSet<BillingAddr> BillingAddresses { get; private set; }
|
public DbSet<BillingAddr> BillingAddresses { get; private set; }
|
||||||
public DbSet<MemberTelNr> MemberTelephoneNrs { get; private set; }
|
public DbSet<MemberTelNr> MemberTelephoneNrs { get; private set; }
|
||||||
|
public DbSet<MemberEmailAddr> MemberEmailAddrs { get; private set; }
|
||||||
public DbSet<MemberHistory> MemberHistory { get; private set; }
|
public DbSet<MemberHistory> MemberHistory { get; private set; }
|
||||||
public DbSet<AreaCom> AreaCommitments { get; private set; }
|
public DbSet<AreaCom> AreaCommitments { get; private set; }
|
||||||
public DbSet<Season> Seasons { get; private set; }
|
public DbSet<Season> Seasons { get; private set; }
|
||||||
@ -55,6 +56,7 @@ namespace Elwig.Helpers {
|
|||||||
public DbSet<DeliveryPartModifier> DeliveryPartModifiers { get; private set; }
|
public DbSet<DeliveryPartModifier> DeliveryPartModifiers { get; private set; }
|
||||||
public DbSet<PaymentVar> PaymentVariants { get; private set; }
|
public DbSet<PaymentVar> PaymentVariants { get; private set; }
|
||||||
public DbSet<PaymentMember> MemberPayments { get; private set; }
|
public DbSet<PaymentMember> MemberPayments { get; private set; }
|
||||||
|
public DbSet<PaymentDeliveryPart> PaymentDeliveryParts { get; private set; }
|
||||||
public DbSet<Credit> Credits { get; private set; }
|
public DbSet<Credit> Credits { get; private set; }
|
||||||
|
|
||||||
public DbSet<OverUnderDeliveryRow> OverUnderDeliveryRows { get; private set; }
|
public DbSet<OverUnderDeliveryRow> OverUnderDeliveryRows { get; private set; }
|
||||||
@ -62,6 +64,8 @@ namespace Elwig.Helpers {
|
|||||||
public DbSet<MemberDeliveryPerVariantRowSingle> MemberDeliveryPerVariantRows { get; private set; }
|
public DbSet<MemberDeliveryPerVariantRowSingle> MemberDeliveryPerVariantRows { get; private set; }
|
||||||
public DbSet<CreditNoteDeliveryRowSingle> CreditNoteDeliveryRows { get; private set; }
|
public DbSet<CreditNoteDeliveryRowSingle> CreditNoteDeliveryRows { get; private set; }
|
||||||
public DbSet<CreditNoteRowSingle> CreditNoteRows { get; private set; }
|
public DbSet<CreditNoteRowSingle> CreditNoteRows { get; private set; }
|
||||||
|
public DbSet<WeightBreakdownRow> WeightBreakDownRows { get; private set; }
|
||||||
|
public DbSet<PaymentVariantSummaryRow> PaymentVariantSummaryRows { get; private set; }
|
||||||
|
|
||||||
private readonly StreamWriter? LogFile = null;
|
private readonly StreamWriter? LogFile = null;
|
||||||
public static DateTime LastWriteTime => File.GetLastWriteTime(App.Config.DatabaseFile);
|
public static DateTime LastWriteTime => File.GetLastWriteTime(App.Config.DatabaseFile);
|
||||||
@ -234,20 +238,21 @@ namespace Elwig.Helpers {
|
|||||||
return await WineQualityLevels
|
return await WineQualityLevels
|
||||||
.Where(q => !q.IsPredicate && (q.MinKmw == null || q.MinKmw <= kmw))
|
.Where(q => !q.IsPredicate && (q.MinKmw == null || q.MinKmw <= kmw))
|
||||||
.OrderBy(q => q.MinKmw)
|
.OrderBy(q => q.MinKmw)
|
||||||
.LastOrDefaultAsync();
|
.LastAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateDeliveryPartModifiers(DeliveryPart part, IEnumerable<Modifier> modifiers) {
|
public void UpdateDeliveryPartModifiers(DeliveryPart part, IEnumerable<Modifier> modifiers) {
|
||||||
foreach (var m in Modifiers.Where(m => m.Year == part.Year)) {
|
foreach (var m in Modifiers.Where(m => m.Year == part.Year)) {
|
||||||
var mod = part.PartModifiers.Where(pa => pa.ModId == m.ModId).FirstOrDefault();
|
var mod = part.PartModifiers.Where(pa => pa.ModId == m.ModId).FirstOrDefault();
|
||||||
if (modifiers.Contains(m)) {
|
if (modifiers.Contains(m)) {
|
||||||
DeliveryPartModifier dpm = mod ?? this.CreateProxy<DeliveryPartModifier>();
|
var dpm = new DeliveryPartModifier {
|
||||||
dpm.Year = part.Year;
|
Year = part.Year,
|
||||||
dpm.DId = part.DId;
|
DId = part.DId,
|
||||||
dpm.DPNr = part.DPNr;
|
DPNr = part.DPNr,
|
||||||
dpm.ModId = m.ModId;
|
ModId = m.ModId,
|
||||||
|
};
|
||||||
if (mod == null) {
|
if (mod == null) {
|
||||||
await AddAsync(dpm);
|
Add(dpm);
|
||||||
} else {
|
} else {
|
||||||
Update(dpm);
|
Update(dpm);
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ namespace Elwig.Helpers {
|
|||||||
public static class AppDbUpdater {
|
public static class AppDbUpdater {
|
||||||
|
|
||||||
// Don't forget to update value in Tests/fetch-resources.bat!
|
// Don't forget to update value in Tests/fetch-resources.bat!
|
||||||
public static readonly int RequiredSchemaVersion = 18;
|
public static readonly int RequiredSchemaVersion = 19;
|
||||||
|
|
||||||
private static int VersionOffset = 0;
|
private static int VersionOffset = 0;
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Elwig.Models.Entities;
|
using Elwig.Models.Entities;
|
||||||
using Microsoft.Data.Sqlite;
|
using Microsoft.Data.Sqlite;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -9,7 +10,6 @@ namespace Elwig.Helpers.Billing {
|
|||||||
public class Billing {
|
public class Billing {
|
||||||
|
|
||||||
protected readonly int Year;
|
protected readonly int Year;
|
||||||
protected readonly AppDbContext Context;
|
|
||||||
protected readonly Season Season;
|
protected readonly Season Season;
|
||||||
protected readonly Dictionary<string, string> Attributes;
|
protected readonly Dictionary<string, string> Attributes;
|
||||||
protected readonly Dictionary<string, (decimal?, decimal?)> Modifiers;
|
protected readonly Dictionary<string, (decimal?, decimal?)> Modifiers;
|
||||||
@ -17,11 +17,11 @@ namespace Elwig.Helpers.Billing {
|
|||||||
|
|
||||||
public Billing(int year) {
|
public Billing(int year) {
|
||||||
Year = year;
|
Year = year;
|
||||||
Context = new AppDbContext();
|
using var ctx = new AppDbContext();
|
||||||
Season = Context.Seasons.Find(Year)!;
|
Season = ctx.Seasons.Find(Year)!;
|
||||||
Attributes = Context.WineAttributes.ToDictionary(a => a.AttrId, a => a.Name);
|
Attributes = ctx.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));
|
Modifiers = ctx.Modifiers.Where(m => m.Year == Year).Include(m => m.Season).ToDictionary(m => m.ModId, m => (m.Abs, m.Rel));
|
||||||
AreaComTypes = Context.AreaCommitmentTypes.ToDictionary(v => v.VtrgId, v => (v.SortId, v.AttrId, v.Discriminator, v.MinKgPerHa, v.PenaltyAmount));
|
AreaComTypes = ctx.AreaCommitmentTypes.ToDictionary(v => v.VtrgId, v => (v.SortId, v.AttrId, v.Discriminator, v.MinKgPerHa, v.PenaltyAmount));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task FinishSeason() {
|
public async Task FinishSeason() {
|
||||||
@ -51,14 +51,15 @@ namespace Elwig.Helpers.Billing {
|
|||||||
bool? avoidUnderDeliveries = null,
|
bool? avoidUnderDeliveries = null,
|
||||||
SqliteConnection? cnx = null
|
SqliteConnection? cnx = null
|
||||||
) {
|
) {
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
var honorGebunden = honorGebundenField ?? Season.Billing_HonorGebunden;
|
var honorGebunden = honorGebundenField ?? Season.Billing_HonorGebunden;
|
||||||
var allowAttrsIntoLower = allowAttributesIntoLower ?? Season.Billing_AllowAttrsIntoLower;
|
var allowAttrsIntoLower = allowAttributesIntoLower ?? Season.Billing_AllowAttrsIntoLower;
|
||||||
var avoidUnderDlvrs = avoidUnderDeliveries ?? Season.Billing_AvoidUnderDeliveries;
|
var avoidUnderDlvrs = avoidUnderDeliveries ?? Season.Billing_AvoidUnderDeliveries;
|
||||||
var attrVals = Context.WineAttributes.ToDictionary(a => a.AttrId, a => (a.IsStrict, a.FillLower));
|
var attrVals = ctx.WineAttributes.ToDictionary(a => a.AttrId, a => (a.IsStrict, a.FillLower));
|
||||||
var attrForced = attrVals.Where(a => a.Value.IsStrict && a.Value.FillLower == 0).Select(a => a.Key).ToArray();
|
var attrForced = attrVals.Where(a => a.Value.IsStrict && a.Value.FillLower == 0).Select(a => a.Key).ToArray();
|
||||||
var ownCnx = cnx == null;
|
var ownCnx = cnx == null;
|
||||||
cnx ??= await AppDbContext.ConnectAsync();
|
cnx ??= await AppDbContext.ConnectAsync();
|
||||||
await Context.GetMemberAreaCommitmentBuckets(Year, 0, cnx);
|
await ctx.GetMemberAreaCommitmentBuckets(Year, 0, cnx);
|
||||||
var inserts = new List<(int, int, int, string, int)>();
|
var inserts = new List<(int, int, int, string, int)>();
|
||||||
|
|
||||||
var deliveries = new List<(int, int, int, string, int, double, string, string?, string[], bool?)>();
|
var deliveries = new List<(int, int, int, string, int, double, string, string?, string[], bool?)>();
|
||||||
@ -87,7 +88,7 @@ namespace Elwig.Helpers.Billing {
|
|||||||
Dictionary<string, int> used = [];
|
Dictionary<string, int> used = [];
|
||||||
foreach (var (mgnr, did, dpnr, sortid, weight, kmw, qualid, attrid, modifiers, gebunden) in deliveries) {
|
foreach (var (mgnr, did, dpnr, sortid, weight, kmw, qualid, attrid, modifiers, gebunden) in deliveries) {
|
||||||
if (lastMgNr != mgnr) {
|
if (lastMgNr != mgnr) {
|
||||||
rightsAndObligations = await Context.GetMemberAreaCommitmentBuckets(Year, mgnr);
|
rightsAndObligations = await ctx.GetMemberAreaCommitmentBuckets(Year, mgnr);
|
||||||
used = [];
|
used = [];
|
||||||
}
|
}
|
||||||
if ((honorGebunden && gebunden == false) ||
|
if ((honorGebunden && gebunden == false) ||
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Elwig.Models.Entities;
|
using Elwig.Models.Entities;
|
||||||
using Microsoft.Data.Sqlite;
|
using Microsoft.Data.Sqlite;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -14,8 +15,9 @@ namespace Elwig.Helpers.Billing {
|
|||||||
|
|
||||||
public BillingVariant(int year, int avnr) : base(year) {
|
public BillingVariant(int year, int avnr) : base(year) {
|
||||||
AvNr = avnr;
|
AvNr = avnr;
|
||||||
PaymentVariant = Context.PaymentVariants.Find(Year, AvNr) ?? throw new ArgumentException("PaymentVar not found");
|
using var ctx = new AppDbContext();
|
||||||
Data = PaymentBillingData.FromJson(PaymentVariant.Data, Utils.GetVaributes(Context, Year, onlyDelivered: false));
|
PaymentVariant = ctx.PaymentVariants.Include(v => v.Season).Where(v => v.Year == Year && v.AvNr == AvNr).Single() ?? throw new ArgumentException("PaymentVar not found");
|
||||||
|
Data = PaymentBillingData.FromJson(PaymentVariant.Data, Utils.GetVaributes(ctx, Year, onlyDelivered: false));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Calculate(bool? honorGebunden = null, bool? allowAttrsIntoLower = null, bool? avoidUnderDeliveries = null) {
|
public async Task Calculate(bool? honorGebunden = null, bool? allowAttrsIntoLower = null, bool? avoidUnderDeliveries = null) {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Elwig.Models.Entities;
|
using Elwig.Models.Entities;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
@ -45,6 +46,7 @@ namespace Elwig.Helpers.Billing {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[PrimaryKey("Listing")]
|
||||||
public class Varibute : IComparable<Varibute> {
|
public class Varibute : IComparable<Varibute> {
|
||||||
|
|
||||||
public WineVar? Variety { get; }
|
public WineVar? Variety { get; }
|
||||||
|
@ -57,6 +57,8 @@ namespace Elwig.Helpers {
|
|||||||
public string? Website;
|
public string? Website;
|
||||||
|
|
||||||
public int ModeDeliveryNoteStats;
|
public int ModeDeliveryNoteStats;
|
||||||
|
public int ModeWineQualityStatistics;
|
||||||
|
public int OrderingMemberList;
|
||||||
|
|
||||||
public string? TextDeliveryNote;
|
public string? TextDeliveryNote;
|
||||||
public string? TextDeliveryConfirmation;
|
public string? TextDeliveryConfirmation;
|
||||||
@ -102,6 +104,18 @@ namespace Elwig.Helpers {
|
|||||||
case "SHORT": ModeDeliveryNoteStats = 2; break;
|
case "SHORT": ModeDeliveryNoteStats = 2; break;
|
||||||
case "FULL": ModeDeliveryNoteStats = 3; break;
|
case "FULL": ModeDeliveryNoteStats = 3; break;
|
||||||
}
|
}
|
||||||
|
switch (parameters.GetValueOrDefault("MODE_WINEQUALITYSTATISTICS", "OE")?.ToUpper()) {
|
||||||
|
case "OE": ModeWineQualityStatistics = 0; break;
|
||||||
|
case "KMW/1": ModeWineQualityStatistics = 1; break;
|
||||||
|
case "KMW/2": ModeWineQualityStatistics = 2; break;
|
||||||
|
case "KMW/5": ModeWineQualityStatistics = 3; break;
|
||||||
|
case "KMW/10": ModeWineQualityStatistics = 4; break;
|
||||||
|
}
|
||||||
|
switch (parameters.GetValueOrDefault("ORDERING_MEMBERLIST", "")?.ToUpper()) {
|
||||||
|
case "MGNR": OrderingMemberList = 0; break;
|
||||||
|
case "NAME": OrderingMemberList = 1; break;
|
||||||
|
case "KG": OrderingMemberList = 2; break;
|
||||||
|
}
|
||||||
|
|
||||||
Sender2 = parameters.GetValueOrDefault("DOCUMENT_SENDER") ?? "";
|
Sender2 = parameters.GetValueOrDefault("DOCUMENT_SENDER") ?? "";
|
||||||
TextDeliveryNote = parameters.GetValueOrDefault("TEXT_DELIVERYNOTE");
|
TextDeliveryNote = parameters.GetValueOrDefault("TEXT_DELIVERYNOTE");
|
||||||
@ -127,6 +141,20 @@ namespace Elwig.Helpers {
|
|||||||
case 2: deliveryNoteStats = "SHORT"; break;
|
case 2: deliveryNoteStats = "SHORT"; break;
|
||||||
case 3: deliveryNoteStats = "FULL"; break;
|
case 3: deliveryNoteStats = "FULL"; break;
|
||||||
}
|
}
|
||||||
|
string modeWineQualityStatistics = "OE";
|
||||||
|
switch (ModeWineQualityStatistics) {
|
||||||
|
case 0: modeWineQualityStatistics = "OE"; break;
|
||||||
|
case 1: modeWineQualityStatistics = "KMW/1"; break;
|
||||||
|
case 2: modeWineQualityStatistics = "KMW/2"; break;
|
||||||
|
case 3: modeWineQualityStatistics = "KMW/5"; break;
|
||||||
|
case 4: modeWineQualityStatistics = "KMW/10"; break;
|
||||||
|
}
|
||||||
|
string orderingMemberList = "MGNR";
|
||||||
|
switch (OrderingMemberList) {
|
||||||
|
case 0: orderingMemberList = "MGNR"; break;
|
||||||
|
case 1: orderingMemberList = "NAME"; break;
|
||||||
|
case 2: orderingMemberList = "KG"; break;
|
||||||
|
}
|
||||||
return [
|
return [
|
||||||
("CLIENT_NAME_TOKEN", NameToken),
|
("CLIENT_NAME_TOKEN", NameToken),
|
||||||
("CLIENT_NAME_SHORT", NameShort),
|
("CLIENT_NAME_SHORT", NameShort),
|
||||||
@ -145,6 +173,8 @@ namespace Elwig.Helpers {
|
|||||||
("CLIENT_BIC", Bic),
|
("CLIENT_BIC", Bic),
|
||||||
("CLIENT_IBAN", Iban),
|
("CLIENT_IBAN", Iban),
|
||||||
("MODE_DELIVERYNOTE_STATS", deliveryNoteStats),
|
("MODE_DELIVERYNOTE_STATS", deliveryNoteStats),
|
||||||
|
("MODE_WINEQUALITYSTATISTICS", modeWineQualityStatistics),
|
||||||
|
("ORDERING_MEMBERLIST", orderingMemberList),
|
||||||
("DOCUMENT_SENDER", Sender2),
|
("DOCUMENT_SENDER", Sender2),
|
||||||
("TEXT_DELIVERYNOTE", TextDeliveryNote),
|
("TEXT_DELIVERYNOTE", TextDeliveryNote),
|
||||||
("TEXT_DELIVERYCONFIRMATION", TextDeliveryConfirmation),
|
("TEXT_DELIVERYCONFIRMATION", TextDeliveryConfirmation),
|
||||||
@ -171,7 +201,6 @@ namespace Elwig.Helpers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await cmd.ExecuteNonQueryAsync();
|
await cmd.ExecuteNonQueryAsync();
|
||||||
await App.HintContextChange();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,10 +13,11 @@ namespace Elwig.Helpers {
|
|||||||
public string? Empty;
|
public string? Empty;
|
||||||
public string? Filling;
|
public string? Filling;
|
||||||
public string? Limit;
|
public string? Limit;
|
||||||
|
public bool Required;
|
||||||
public string? Log;
|
public string? Log;
|
||||||
public string? _Log;
|
public string? _Log;
|
||||||
|
|
||||||
public ScaleConfig(string id, string? type, string? model, string? cnx, string? empty, string? filling, string? limit, string? log) {
|
public ScaleConfig(string id, string? type, string? model, string? cnx, string? empty, string? filling, string? limit, bool? required, string? log) {
|
||||||
Id = id;
|
Id = id;
|
||||||
Type = type;
|
Type = type;
|
||||||
Model = model;
|
Model = model;
|
||||||
@ -24,6 +25,7 @@ namespace Elwig.Helpers {
|
|||||||
Empty = empty;
|
Empty = empty;
|
||||||
Filling = filling;
|
Filling = filling;
|
||||||
Limit = limit;
|
Limit = limit;
|
||||||
|
Required = required ?? true;
|
||||||
_Log = log;
|
_Log = log;
|
||||||
Log = log != null ? Path.Combine(App.DataPath, log) : null;
|
Log = log != null ? Path.Combine(App.DataPath, log) : null;
|
||||||
}
|
}
|
||||||
@ -41,6 +43,9 @@ namespace Elwig.Helpers {
|
|||||||
public string? Branch = null;
|
public string? Branch = null;
|
||||||
public string? UpdateUrl = null;
|
public string? UpdateUrl = null;
|
||||||
public bool UpdateAuto = false;
|
public bool UpdateAuto = false;
|
||||||
|
public string? SyncUrl = null;
|
||||||
|
public string SyncUsername = "";
|
||||||
|
public string SyncPassword = "";
|
||||||
|
|
||||||
public string? SmtpHost = null;
|
public string? SmtpHost = null;
|
||||||
public int? SmtpPort = null;
|
public int? SmtpPort = null;
|
||||||
@ -71,6 +76,9 @@ namespace Elwig.Helpers {
|
|||||||
Debug = TrueValues.Contains(config["general:debug"]?.ToLower());
|
Debug = TrueValues.Contains(config["general:debug"]?.ToLower());
|
||||||
UpdateUrl = config["update:url"];
|
UpdateUrl = config["update:url"];
|
||||||
UpdateAuto = TrueValues.Contains(config["update:auto"]?.ToLower());
|
UpdateAuto = TrueValues.Contains(config["update:auto"]?.ToLower());
|
||||||
|
SyncUrl = config["sync:url"];
|
||||||
|
SyncUsername = config["sync:username"] ?? "";
|
||||||
|
SyncPassword = config["sync:password"] ?? "";
|
||||||
|
|
||||||
SmtpHost = config["smtp:host"];
|
SmtpHost = config["smtp:host"];
|
||||||
SmtpPort = config["smtp:port"]?.All(char.IsAsciiDigit) == true && config["smtp:port"]?.Length > 0 ? int.Parse(config["smtp:port"]!) : null;
|
SmtpPort = config["smtp:port"]?.All(char.IsAsciiDigit) == true && config["smtp:port"]?.Length > 0 ? int.Parse(config["smtp:port"]!) : null;
|
||||||
@ -85,7 +93,9 @@ namespace Elwig.Helpers {
|
|||||||
foreach (var s in scales) {
|
foreach (var s in scales) {
|
||||||
ScaleList.Add(new(
|
ScaleList.Add(new(
|
||||||
s, config[$"scale.{s}:type"], config[$"scale.{s}:model"], config[$"scale.{s}:connection"],
|
s, config[$"scale.{s}:type"], config[$"scale.{s}:model"], config[$"scale.{s}:connection"],
|
||||||
config[$"scale.{s}:empty"], config[$"scale.{s}:filling"], config[$"scale.{s}:limit"], config[$"scale.{s}:log"]
|
config[$"scale.{s}:empty"], config[$"scale.{s}:filling"], config[$"scale.{s}:limit"],
|
||||||
|
config[$"scale.{s}:required"] != null ? TrueValues.Contains(config[$"scale.{s}:required"]?.ToLower()) : null,
|
||||||
|
config[$"scale.{s}:log"]
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,13 +84,13 @@ namespace Elwig.Helpers {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RenewItemsSource(Selector selector, IEnumerable? source, Func<object?, object?> getId, SelectionChangedEventHandler? handler = null, RenewSourceDefault def = RenewSourceDefault.None) {
|
public static void RenewItemsSource(Selector selector, IEnumerable? source, SelectionChangedEventHandler? handler = null, RenewSourceDefault def = RenewSourceDefault.None) {
|
||||||
if (selector.ItemsSource == source)
|
if (selector.ItemsSource == source)
|
||||||
return;
|
return;
|
||||||
var selectedId = getId(selector.SelectedItem);
|
var selectedId = Utils.GetEntityIdentifier(selector.SelectedItem);
|
||||||
object? selItem = null;
|
object? selItem = null;
|
||||||
if (selectedId != null && source != null)
|
if (selectedId != 0 && source != null)
|
||||||
selItem = source.Cast<object>().FirstOrDefault(i => selectedId.Equals(getId(i)));
|
selItem = source.Cast<object>().FirstOrDefault(i => selectedId.Equals(Utils.GetEntityIdentifier(i)));
|
||||||
if (source != null && selItem == null) {
|
if (source != null && selItem == null) {
|
||||||
if ((def == RenewSourceDefault.IfOnly && source.Cast<object>().Count() == 1) || def == RenewSourceDefault.First) {
|
if ((def == RenewSourceDefault.IfOnly && source.Cast<object>().Count() == 1) || def == RenewSourceDefault.First) {
|
||||||
selItem = source.Cast<object>().First();
|
selItem = source.Cast<object>().First();
|
||||||
@ -102,28 +102,30 @@ namespace Elwig.Helpers {
|
|||||||
selector.SelectedItem = selItem;
|
selector.SelectedItem = selItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RenewItemsSource(Xceed.Wpf.Toolkit.Primitives.Selector selector, IEnumerable? source, Func<object?, object?> getId) {
|
public static void RenewItemsSource(Xceed.Wpf.Toolkit.Primitives.Selector selector, IEnumerable? source, Xceed.Wpf.Toolkit.Primitives.ItemSelectionChangedEventHandler? handler = null) {
|
||||||
if (selector.ItemsSource == source)
|
if (selector.ItemsSource == source)
|
||||||
return;
|
return;
|
||||||
var selectedIds = selector.SelectedItems.Cast<object>().Select(i => getId(i)).ToList();
|
var selectedIds = selector.SelectedItems.Cast<object>().Select(i => Utils.GetEntityIdentifier(i)).ToList();
|
||||||
|
if (handler != null && selectedIds != null) selector.ItemSelectionChanged -= handler;
|
||||||
selector.ItemsSource = source;
|
selector.ItemsSource = source;
|
||||||
if (source != null) {
|
if (source != null) {
|
||||||
selector.SelectedItems.Clear();
|
selector.SelectedItems.Clear();
|
||||||
foreach (var i in source.Cast<object>().Where(i => selectedIds.Contains(getId(i))))
|
foreach (var i in source.Cast<object>().Where(i => selectedIds.Contains(Utils.GetEntityIdentifier(i))))
|
||||||
selector.SelectedItems.Add(i);
|
selector.SelectedItems.Add(i);
|
||||||
}
|
}
|
||||||
|
if (handler != null && selectedIds != null) selector.ItemSelectionChanged += handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RenewItemsSource(DataGrid dataGrid, IEnumerable? source, Func<object?, object?> getId, SelectionChangedEventHandler? handler = null, RenewSourceDefault def = RenewSourceDefault.None, bool keepSort = true) {
|
public static void RenewItemsSource(DataGrid dataGrid, IEnumerable? source, SelectionChangedEventHandler? handler = null, RenewSourceDefault def = RenewSourceDefault.None, bool keepSort = true) {
|
||||||
if (dataGrid.ItemsSource == source)
|
if (dataGrid.ItemsSource == source)
|
||||||
return;
|
return;
|
||||||
var column = dataGrid.CurrentCell.Column;
|
var column = dataGrid.CurrentCell.Column;
|
||||||
var sortColumns = dataGrid.Columns.Select(c => c.SortDirection).ToList();
|
var sortColumns = dataGrid.Columns.Select(c => c.SortDirection).ToList();
|
||||||
var sort = dataGrid.Items.SortDescriptions.ToList();
|
var sort = dataGrid.Items.SortDescriptions.ToList();
|
||||||
var selectedId = getId(dataGrid.SelectedItem);
|
var selectedId = Utils.GetEntityIdentifier(dataGrid.SelectedItem);
|
||||||
object? selItem = null;
|
object? selItem = null;
|
||||||
if (selectedId != null && source != null)
|
if (selectedId != 0 && source != null)
|
||||||
selItem = source.Cast<object>().FirstOrDefault(i => selectedId.Equals(getId(i)));
|
selItem = source.Cast<object>().FirstOrDefault(i => selectedId.Equals(Utils.GetEntityIdentifier(i)));
|
||||||
if (source != null && selItem == null) {
|
if (source != null && selItem == null) {
|
||||||
if ((def == RenewSourceDefault.IfOnly && source.Cast<object>().Count() == 1) || def == RenewSourceDefault.First) {
|
if ((def == RenewSourceDefault.IfOnly && source.Cast<object>().Count() == 1) || def == RenewSourceDefault.First) {
|
||||||
selItem = source.Cast<object>().First();
|
selItem = source.Cast<object>().First();
|
||||||
@ -143,13 +145,13 @@ namespace Elwig.Helpers {
|
|||||||
dataGrid.CurrentCell = new(dataGrid.SelectedItem, column);
|
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) {
|
public static void RenewItemsSource(ListBox listBox, IEnumerable? source, SelectionChangedEventHandler? handler = null, RenewSourceDefault def = RenewSourceDefault.None) {
|
||||||
if (listBox.ItemsSource == source)
|
if (listBox.ItemsSource == source)
|
||||||
return;
|
return;
|
||||||
var selectedId = getId(listBox.SelectedItem);
|
var selectedId = Utils.GetEntityIdentifier(listBox.SelectedItem);
|
||||||
object? selItem = null;
|
object? selItem = null;
|
||||||
if (selectedId != null && source != null)
|
if (selectedId != 0 && source != null)
|
||||||
selItem = source.Cast<object>().FirstOrDefault(i => selectedId.Equals(getId(i)));
|
selItem = source.Cast<object>().FirstOrDefault(i => selectedId.Equals(Utils.GetEntityIdentifier(i)));
|
||||||
if (source != null && selItem == null) {
|
if (source != null && selItem == null) {
|
||||||
if ((def == RenewSourceDefault.IfOnly && source.Cast<object>().Count() == 1) || def == RenewSourceDefault.First) {
|
if ((def == RenewSourceDefault.IfOnly && source.Cast<object>().Count() == 1) || def == RenewSourceDefault.First) {
|
||||||
selItem = source.Cast<object>().FirstOrDefault();
|
selItem = source.Cast<object>().FirstOrDefault();
|
||||||
@ -161,71 +163,75 @@ namespace Elwig.Helpers {
|
|||||||
listBox.SelectedItem = selItem;
|
listBox.SelectedItem = selItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static object? GetItemFromSource(IEnumerable source, Func<object?, object?> getId, object? id) {
|
public static object? GetItemFromSource(IEnumerable source, int? hash) {
|
||||||
if (source == null)
|
if (source == null)
|
||||||
return null;
|
return null;
|
||||||
var items = source.Cast<object>();
|
var items = source.Cast<object>();
|
||||||
var item = items.Where(i => getId(i)?.Equals(id) ?? false).FirstOrDefault();
|
var item = items.Where(i => Utils.GetEntityIdentifier(i) == hash).FirstOrDefault();
|
||||||
if (item == null && items.Any(i => i is NullItem))
|
if (item == null && items.Any(i => i is NullItem))
|
||||||
return items.Where(i => i is NullItem).First();
|
return items.Where(i => i is NullItem).First();
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static object? GetItemFromSource(IEnumerable source, object? item, Func<object?, object?> getId) {
|
public static object? GetItemFromSource(IEnumerable source, object? item) {
|
||||||
return GetItemFromSource(source, getId, getId(item));
|
return GetItemFromSource(source, Utils.GetEntityIdentifier(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SelectComboBoxItem(ComboBox cb, Func<object?, object?> getId, object? id) {
|
public static void SelectItemWithHash(Selector input, int? hash) {
|
||||||
cb.SelectedItem = GetItemFromSource(cb.ItemsSource, getId, id);
|
if (hash == null) {
|
||||||
|
input.SelectedItem = null;
|
||||||
|
} else {
|
||||||
|
input.SelectedItem = GetItemFromSource(input.ItemsSource, (int)hash);
|
||||||
|
}
|
||||||
|
if (input is ListBox lb && lb.SelectedItem is object lbItem) {
|
||||||
|
lb.ScrollIntoView(lbItem);
|
||||||
|
} else if (input is DataGrid dg && dg.SelectedItem is object dgItem) {
|
||||||
|
dg.ScrollIntoView(dgItem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SelectComboBoxItem(ComboBox cb, object? item, Func<object?, object?> getId) {
|
public static void SelectItemWithPk(Selector input, params object?[] pk) {
|
||||||
SelectComboBoxItem(cb, getId, getId(item));
|
SelectItemWithHash(input, Utils.GetEntityIdentifier(pk));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SelectListBoxItem(ListBox lb, Func<object?, object?> getId, object? id) {
|
public static void SelectItem(Selector input, object? item) {
|
||||||
lb.SelectedItem = GetItemFromSource(lb.ItemsSource, getId, id);
|
SelectItemWithHash(input, Utils.GetEntityIdentifier(item));
|
||||||
lb.ScrollIntoView(lb.SelectedItem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SelectListBoxItem(ListBox lb, object? item, Func<object?, object?> getId) {
|
public static IEnumerable<object?> GetItemsFromSource(IEnumerable source, IEnumerable<int?> ids) {
|
||||||
SelectListBoxItem(lb, getId, getId(item));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<object?> GetItemsFromSource(IEnumerable source, Func<object?, object?> getId, IEnumerable<object?> ids) {
|
|
||||||
if (source == null)
|
if (source == null)
|
||||||
return Array.Empty<object>();
|
return [];
|
||||||
return source.Cast<object>().Where(i => ids.Any(c => c?.Equals(getId(i)) ?? false));
|
return source.Cast<object>().Where(i => ids.Any(c => c == Utils.GetEntityIdentifier(i)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<object?> GetItemsFromSource(IEnumerable source, IEnumerable<object?>? items, Func<object?, object?> getId) {
|
public static IEnumerable<object?> GetItemsFromSource(IEnumerable source, IEnumerable<object?>? items) {
|
||||||
if (items == null)
|
if (items == null)
|
||||||
return Array.Empty<object>();
|
return [];
|
||||||
return GetItemsFromSource(source, getId, items.Select(i => getId(i)));
|
return GetItemsFromSource(source, items.Select(Utils.GetEntityIdentifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SelectCheckComboBoxItems(Xceed.Wpf.Toolkit.CheckComboBox ccb, Func<object?, object?> getId, IEnumerable<object?>? ids) {
|
public static void SelectItems(Xceed.Wpf.Toolkit.CheckComboBox ccb, IEnumerable<int?>? ids) {
|
||||||
ccb.SelectedItems.Clear();
|
ccb.SelectedItems.Clear();
|
||||||
if (ids == null) return;
|
if (ids == null) return;
|
||||||
foreach (var id in ids)
|
foreach (var id in ids)
|
||||||
ccb.SelectedItems.Add(GetItemFromSource(ccb.ItemsSource, getId, id));
|
ccb.SelectedItems.Add(GetItemFromSource(ccb.ItemsSource, id));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SelectCheckComboBoxItems(Xceed.Wpf.Toolkit.CheckComboBox ccb, IEnumerable<object>? items, Func<object?, object?> getId) {
|
public static void SelectItems(Xceed.Wpf.Toolkit.CheckComboBox ccb, IEnumerable<object>? items) {
|
||||||
SelectCheckComboBoxItems(ccb, getId, items?.Select(i => getId(i)));
|
SelectItems(ccb, items?.Select(Utils.GetEntityIdentifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static object? GetInputValue(Control input) {
|
public static int? GetInputHashCode(Control input) {
|
||||||
if (input is TextBox tb) {
|
if (input is TextBox tb) {
|
||||||
return tb.Text;
|
return Utils.GetEntityIdentifier(tb.Text);
|
||||||
} else if (input is ComboBox sb) {
|
} else if (input is ComboBox sb) {
|
||||||
return sb.SelectedItem;
|
return Utils.GetEntityIdentifier(sb.SelectedItem);
|
||||||
} else if (input is Xceed.Wpf.Toolkit.CheckComboBox ccb) {
|
} else if (input is Xceed.Wpf.Toolkit.CheckComboBox ccb) {
|
||||||
return ccb.SelectedItems.Cast<object>().ToArray();
|
return Utils.GetEntityIdentifier(ccb.SelectedItems);
|
||||||
} else if (input is CheckBox cb) {
|
} else if (input is CheckBox cb) {
|
||||||
return cb.IsChecked?.ToString();
|
return Utils.GetEntityIdentifier(cb.IsChecked);
|
||||||
} else if (input is RadioButton rb) {
|
} else if (input is RadioButton rb) {
|
||||||
return rb.IsChecked?.ToString();
|
return Utils.GetEntityIdentifier(rb.IsChecked);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
using Elwig.Models;
|
||||||
using Elwig.Models.Dtos;
|
using Elwig.Models.Dtos;
|
||||||
using Elwig.Models.Entities;
|
using Elwig.Models.Entities;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security;
|
using System.Security;
|
||||||
|
251
Elwig/Helpers/Export/ElwigData.cs
Normal file
251
Elwig/Helpers/Export/ElwigData.cs
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
using System.IO.Compression;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Elwig.Models.Entities;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Elwig.Helpers.Export {
|
||||||
|
public static class ElwigData {
|
||||||
|
|
||||||
|
public static readonly string ImportedTxt = Path.Combine(App.DataPath, "imported.txt");
|
||||||
|
|
||||||
|
public static async Task<string[]> GetImportedFiles() {
|
||||||
|
try {
|
||||||
|
return await File.ReadAllLinesAsync(ImportedTxt, Utils.UTF8);
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task AddImportedFiles(IEnumerable<string> filenames) {
|
||||||
|
await File.AppendAllLinesAsync(ImportedTxt, filenames, Utils.UTF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task Import(string filename, bool interactive) => Import([filename], interactive);
|
||||||
|
|
||||||
|
public static async Task Import(IEnumerable<string> filenames, bool interactive) {
|
||||||
|
try {
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
|
var currentDids = await ctx.Deliveries
|
||||||
|
.GroupBy(d => d.Year)
|
||||||
|
.ToDictionaryAsync(g => g.Key, g => g.Max(d => d.DId));
|
||||||
|
|
||||||
|
var deliveries = new List<Delivery>();
|
||||||
|
var deliveryParts = new List<DeliveryPart>();
|
||||||
|
var modifiers = new List<DeliveryPartModifier>();
|
||||||
|
|
||||||
|
var metaData = new List<(string Name, int DeliveryNum, string Filters)>();
|
||||||
|
|
||||||
|
foreach (var filename in filenames) {
|
||||||
|
using var zip = ZipFile.Open(filename, ZipArchiveMode.Read);
|
||||||
|
|
||||||
|
var version = zip.GetEntry("version");
|
||||||
|
using (var reader = new StreamReader(version!.Open(), Utils.UTF8)) {
|
||||||
|
if (await reader.ReadToEndAsync() != "elwig:1")
|
||||||
|
throw new FileFormatException("Ungültige Export-Datei");
|
||||||
|
}
|
||||||
|
|
||||||
|
var metaJson = zip.GetEntry("meta.json");
|
||||||
|
var meta = await JsonNode.ParseAsync(metaJson!.Open());
|
||||||
|
var deliveryCount = meta!["deliveries"]?["count"]!.AsValue().GetValue<int>();
|
||||||
|
var deliveryFilters = meta!["deliveries"]?["filters"]!.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
|
||||||
|
if (deliveryCount != null && deliveryFilters != null)
|
||||||
|
metaData.Add((Path.GetFileName(filename), (int)deliveryCount, string.Join(" / ", deliveryFilters)));
|
||||||
|
|
||||||
|
var membersJson = zip.GetEntry("members.json");
|
||||||
|
if (membersJson != null) {
|
||||||
|
using var reader = new StreamReader(membersJson.Open(), Utils.UTF8);
|
||||||
|
string? line;
|
||||||
|
while ((line = await reader.ReadLineAsync()) != null) {
|
||||||
|
var obj = JsonNode.Parse(line)!.AsObject();
|
||||||
|
// TODO import members.json
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var areaComsJson = zip.GetEntry("area_commitments.json");
|
||||||
|
if (areaComsJson != null) {
|
||||||
|
using var reader = new StreamReader(areaComsJson.Open(), Utils.UTF8);
|
||||||
|
string? line;
|
||||||
|
while ((line = await reader.ReadLineAsync()) != null) {
|
||||||
|
var obj = JsonNode.Parse(line)!.AsObject();
|
||||||
|
// TODO import area_commitments.json
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var deliveriesJson = zip.GetEntry("deliveries.json");
|
||||||
|
if (deliveriesJson != null) {
|
||||||
|
using var reader = new StreamReader(deliveriesJson.Open(), Utils.UTF8);
|
||||||
|
string? line;
|
||||||
|
while ((line = await reader.ReadLineAsync()) != null) {
|
||||||
|
var obj = JsonNode.Parse(line)!.AsObject();
|
||||||
|
var (d, parts, mods) = JsonToDelivery(obj, currentDids);
|
||||||
|
deliveries.Add(d);
|
||||||
|
deliveryParts.AddRange(parts);
|
||||||
|
modifiers.AddRange(mods);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var lsnrs = deliveries.Select(d => d.LsNr).ToList();
|
||||||
|
var duplicateLsNrs = await ctx.Deliveries
|
||||||
|
.Where(d => lsnrs.Contains(d.LsNr))
|
||||||
|
.Select(d => d.LsNr)
|
||||||
|
.ToListAsync();
|
||||||
|
var duplicateDIds = deliveries
|
||||||
|
.Where(d => duplicateLsNrs.Contains(d.LsNr))
|
||||||
|
.Select(d => (d.Year, d.DId))
|
||||||
|
.ToList();
|
||||||
|
bool overwriteDelivieries = false;
|
||||||
|
if (duplicateLsNrs.Count > 0) {
|
||||||
|
var res = MessageBox.Show($"Sollen {duplicateLsNrs.Count} Lieferungen überschreiben werden?", "Lieferungen überschreiben",
|
||||||
|
MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
|
||||||
|
overwriteDelivieries = res == MessageBoxResult.Yes;
|
||||||
|
}
|
||||||
|
if (overwriteDelivieries) {
|
||||||
|
ctx.RemoveRange(ctx.Deliveries.Where(d => duplicateLsNrs.Contains(d.LsNr)));
|
||||||
|
ctx.AddRange(deliveries);
|
||||||
|
ctx.AddRange(deliveryParts);
|
||||||
|
ctx.AddRange(modifiers);
|
||||||
|
} else {
|
||||||
|
ctx.AddRange(deliveries.Where(d => !duplicateDIds.Contains((d.Year, d.DId))));
|
||||||
|
ctx.AddRange(deliveryParts.Where(p => !duplicateDIds.Contains((p.Year, p.DId))));
|
||||||
|
ctx.AddRange(modifiers.Where(m => !duplicateDIds.Contains((m.Year, m.DId))));
|
||||||
|
}
|
||||||
|
await ctx.SaveChangesAsync();
|
||||||
|
await AddImportedFiles(filenames.Select(f => Path.GetFileName(f)));
|
||||||
|
await App.HintContextChange();
|
||||||
|
|
||||||
|
MessageBox.Show(
|
||||||
|
$"Das importieren der Daten war erfolgreich!\n" +
|
||||||
|
$"Folgendes wurde importiert:\n" +
|
||||||
|
$" Lieferungen: {deliveries.Count}\n" +
|
||||||
|
string.Join("\n", metaData.Select(d => $" {d.Name} ({d.DeliveryNum})\n {d.Filters}")) +
|
||||||
|
"\n", "Importieren erfolgreich",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task ExportDeliveries(string filename, IEnumerable<Delivery> deliveries, IEnumerable<string> filters) {
|
||||||
|
File.Delete(filename);
|
||||||
|
using var zip = ZipFile.Open(filename, ZipArchiveMode.Create);
|
||||||
|
|
||||||
|
var version = zip.CreateEntry("version", CompressionLevel.NoCompression);
|
||||||
|
using (var writer = new StreamWriter(version.Open(), Utils.UTF8)) {
|
||||||
|
await writer.WriteAsync("elwig:1");
|
||||||
|
}
|
||||||
|
|
||||||
|
var meta = zip.CreateEntry("meta.json", CompressionLevel.NoCompression);
|
||||||
|
using (var writer = new StreamWriter(meta.Open(), Utils.UTF8)) {
|
||||||
|
await writer.WriteAsync(
|
||||||
|
$"{{\"timestamp\": \"{DateTime.UtcNow:yyyy-MM-ddTHH:mm:ssZ}\", " +
|
||||||
|
$"\"zwstid\": \"{App.ZwstId}\", \"device\": \"{Environment.MachineName}\", " +
|
||||||
|
$"\"deliveries\": {{" +
|
||||||
|
$"\"count\": {deliveries.Count()}, " +
|
||||||
|
$"\"parts\": {deliveries.Sum(d => d.Parts.Count)}, " +
|
||||||
|
$"\"filters\": [{string.Join(", ", filters.Select(f => '"' + f + '"'))}]" +
|
||||||
|
$"}}}}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var json = zip.CreateEntry("deliveries.json");
|
||||||
|
using (var writer = new StreamWriter(json.Open(), Utils.UTF8)) {
|
||||||
|
foreach (var d in deliveries) {
|
||||||
|
await writer.WriteLineAsync(DeliveryToJson(d).ToJsonString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JsonObject DeliveryToJson(Delivery d) {
|
||||||
|
return new JsonObject {
|
||||||
|
["lsnr"] = d.LsNr,
|
||||||
|
["year"] = d.Year,
|
||||||
|
["date"] = $"{d.Date:yyyy-MM-dd}",
|
||||||
|
["zwstid"] = d.ZwstId,
|
||||||
|
["lnr"] = d.LNr,
|
||||||
|
["time"] = d.Time != null ? $"{d.Time:HH:mm:ss}" : null,
|
||||||
|
["mgnr"] = d.MgNr,
|
||||||
|
["parts"] = new JsonArray(d.Parts.OrderBy(p => p.DPNr).Select(p => {
|
||||||
|
var obj = new JsonObject {
|
||||||
|
["dpnr"] = p.DPNr,
|
||||||
|
["sortid"] = p.SortId,
|
||||||
|
["attrid"] = p.AttrId,
|
||||||
|
["cultid"] = p.CultId,
|
||||||
|
["weight"] = p.Weight,
|
||||||
|
["kmw"] = p.Kmw,
|
||||||
|
["qualid"] = p.QualId,
|
||||||
|
["hkid"] = p.HkId,
|
||||||
|
["kgnr"] = p.KgNr,
|
||||||
|
["rdnr"] = p.RdNr,
|
||||||
|
["net_weight"] = p.IsNetWeight,
|
||||||
|
["manual_weighing"] = p.IsManualWeighing,
|
||||||
|
["modids"] = new JsonArray(p.Modifiers.Select(m => (JsonNode)m.ModId).ToArray()),
|
||||||
|
["comment"] = p.Comment,
|
||||||
|
};
|
||||||
|
if (p.IsSplCheck) obj["spl_check"] = p.IsSplCheck;
|
||||||
|
if (p.IsHandPicked != null) obj["hand_picked"] = p.IsHandPicked;
|
||||||
|
if (p.IsLesewagen != null) obj["lesewagen"] = p.IsLesewagen;
|
||||||
|
if (p.IsGebunden != null) obj["gebunden"] = p.IsGebunden;
|
||||||
|
if (p.Temperature != null) obj["temperature"] = p.Temperature;
|
||||||
|
if (p.Acid != null) obj["acid"] = p.Acid;
|
||||||
|
if (p.ScaleId != null) obj["scale_id"] = p.ScaleId;
|
||||||
|
if (p.WeighingId != null) obj["weighing_id"] = p.WeighingId;
|
||||||
|
if (p.WeighingReason != null) obj["weighing_reason"] = p.WeighingReason;
|
||||||
|
return obj;
|
||||||
|
}).ToArray()),
|
||||||
|
["comment"] = d.Comment,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static (Delivery, List<DeliveryPart>, List<DeliveryPartModifier>) JsonToDelivery(JsonNode json, Dictionary<int, int> currentDids) {
|
||||||
|
var year = json["year"]!.AsValue().GetValue<int>();
|
||||||
|
var did = ++currentDids[year];
|
||||||
|
return (new Delivery {
|
||||||
|
Year = year,
|
||||||
|
DId = did,
|
||||||
|
DateString = json["date"]!.AsValue().GetValue<string>(),
|
||||||
|
TimeString = json["time"]?.AsValue().GetValue<string>(),
|
||||||
|
ZwstId = json["zwstid"]!.AsValue().GetValue<string>(),
|
||||||
|
LNr = json["lnr"]!.AsValue().GetValue<int>(),
|
||||||
|
LsNr = json["lsnr"]!.AsValue().GetValue<string>(),
|
||||||
|
MgNr = json["mgnr"]!.AsValue().GetValue<int>(),
|
||||||
|
Comment = json["comment"]?.AsValue().GetValue<string>(),
|
||||||
|
}, json["parts"]!.AsArray().Select(p => p!.AsObject()).Select(p => new DeliveryPart {
|
||||||
|
Year = year,
|
||||||
|
DId = did,
|
||||||
|
DPNr = p["dpnr"]!.AsValue().GetValue<int>(),
|
||||||
|
SortId = p["sortid"]!.AsValue().GetValue<string>(),
|
||||||
|
AttrId = p["attrid"]?.AsValue().GetValue<string>(),
|
||||||
|
CultId = p["cultid"]?.AsValue().GetValue<string>(),
|
||||||
|
Weight = p["weight"]!.AsValue().GetValue<int>(),
|
||||||
|
Kmw = p["kmw"]!.AsValue().GetValue<double>(),
|
||||||
|
QualId = p["qualid"]!.AsValue().GetValue<string>(),
|
||||||
|
HkId = p["hkid"]!.AsValue().GetValue<string>(),
|
||||||
|
KgNr = p["kgnr"]?.AsValue().GetValue<int>(),
|
||||||
|
RdNr = p["rdnr"]?.AsValue().GetValue<int>(),
|
||||||
|
IsNetWeight = p["net_weight"]!.AsValue().GetValue<bool>(),
|
||||||
|
IsManualWeighing = p["manual_weighing"]!.AsValue().GetValue<bool>(),
|
||||||
|
Comment = p["comment"]?.AsValue().GetValue<string>(),
|
||||||
|
IsSplCheck = p["spl_check"]?.AsValue().GetValue<bool>() ?? false,
|
||||||
|
IsHandPicked = p["hand_picked"]?.AsValue().GetValue<bool>(),
|
||||||
|
IsLesewagen = p["lesewagen"]?.AsValue().GetValue<bool>(),
|
||||||
|
IsGebunden = p["gebunden"]?.AsValue().GetValue<bool>(),
|
||||||
|
Temperature = p["temperature"]?.AsValue().GetValue<double>(),
|
||||||
|
Acid = p["acid"]?.AsValue().GetValue<double>(),
|
||||||
|
ScaleId = p["scale_id"]?.AsValue().GetValue<string>(),
|
||||||
|
WeighingId = p["weighing_id"]?.AsValue().GetValue<string>(),
|
||||||
|
WeighingReason = p["weighing_reason"]?.AsValue().GetValue<string>(),
|
||||||
|
}).ToList(), json["parts"]!.AsArray().SelectMany(p => p!["modids"]!.AsArray().Select(m => new DeliveryPartModifier {
|
||||||
|
Year = year,
|
||||||
|
DId = did,
|
||||||
|
DPNr = p["dpnr"]!.AsValue().GetValue<int>(),
|
||||||
|
ModId = m!.AsValue().GetValue<string>(),
|
||||||
|
})).ToList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,7 @@ namespace Elwig.Helpers.Export {
|
|||||||
FileName = filename;
|
FileName = filename;
|
||||||
File.Delete(filename);
|
File.Delete(filename);
|
||||||
ZipArchive = ZipFile.Open(FileName, ZipArchiveMode.Create);
|
ZipArchive = ZipFile.Open(FileName, ZipArchiveMode.Create);
|
||||||
_tables = new();
|
_tables = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
@ -103,6 +103,11 @@ namespace Elwig.Helpers.Export {
|
|||||||
<style:paragraph-properties fo:text-align="center"/>
|
<style:paragraph-properties fo:text-align="center"/>
|
||||||
<style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold" fo:font-size="16pt"/>
|
<style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold" fo:font-size="16pt"/>
|
||||||
</style:style>
|
</style:style>
|
||||||
|
<style:style style:name="subheader" style:family="table-cell" style:parent-style-name="default">
|
||||||
|
<style:table-cell-properties style:text-align-source="fix" style:repeat-content="false"/>
|
||||||
|
<style:paragraph-properties fo:text-align="center"/>
|
||||||
|
<style:text-properties fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"/>
|
||||||
|
</style:style>
|
||||||
<style:style style:name="th" style:family="table-cell" style:parent-style-name="default">
|
<style:style style:name="th" style:family="table-cell" style:parent-style-name="default">
|
||||||
<style:table-cell-properties style:text-align-source="fix" style:repeat-content="false" style:vertical-align="middle"/>
|
<style:table-cell-properties style:text-align-source="fix" style:repeat-content="false" style:vertical-align="middle"/>
|
||||||
<style:paragraph-properties fo:text-align="center"/>
|
<style:paragraph-properties fo:text-align="center"/>
|
||||||
@ -122,6 +127,18 @@ namespace Elwig.Helpers.Export {
|
|||||||
<style:style style:name="N5" style:family="table-cell" style:parent-style-name="default" style:data-style-name="NN5"/>
|
<style:style style:name="N5" style:family="table-cell" style:parent-style-name="default" style:data-style-name="NN5"/>
|
||||||
<number:number-style style:name="NN6"><number:number number:decimal-places="6" number:min-decimal-places="6" number:min-integer-digits="1" number:grouping="true"/></number:number-style>
|
<number:number-style style:name="NN6"><number:number number:decimal-places="6" number:min-decimal-places="6" number:min-integer-digits="1" number:grouping="true"/></number:number-style>
|
||||||
<style:style style:name="N6" style:family="table-cell" style:parent-style-name="default" style:data-style-name="NN6"/>
|
<style:style style:name="N6" style:family="table-cell" style:parent-style-name="default" style:data-style-name="NN6"/>
|
||||||
|
<number:date-style style:name="_date"><number:day number:style="long"/><number:text>.</number:text><number:month number:style="long"/><number:text>.</number:text><number:year number:style="long"/></number:date-style>
|
||||||
|
<style:style style:name="date" style:family="table-cell" style:parent-style-name="default" style:data-style-name="_date"/>
|
||||||
|
<number:time-style style:name="_time"><number:hours number:style="long"/><number:text>:</number:text><number:minutes number:style="long"/><number:text>:</number:text><number:seconds number:style="long"/></number:time-style>
|
||||||
|
<style:style style:name="time" style:family="table-cell" style:parent-style-name="default" style:data-style-name="_time"/>
|
||||||
|
<number:date-style style:name="_datetime">
|
||||||
|
<number:day number:style="long"/><number:text>.</number:text><number:month number:style="long"/><number:text>.</number:text><number:year number:style="long"/>
|
||||||
|
<number:text> </number:text>
|
||||||
|
<number:hours number:style="long"/><number:text>:</number:text><number:minutes number:style="long"/><number:text>:</number:text><number:seconds number:style="long"/>
|
||||||
|
</number:date-style>
|
||||||
|
<style:style style:name="datetime" style:family="table-cell" style:parent-style-name="default" style:data-style-name="_datetime"/>
|
||||||
|
<number:boolean-style style:name="_boolean"><number:boolean/></number:boolean-style>
|
||||||
|
<style:style style:name="boolean" style:family="table-cell" style:parent-style-name="default" style:data-style-name="_boolean"/>
|
||||||
</office:automatic-styles>
|
</office:automatic-styles>
|
||||||
<office:body>
|
<office:body>
|
||||||
<office:spreadsheet>
|
<office:spreadsheet>
|
||||||
@ -161,8 +178,8 @@ namespace Elwig.Helpers.Export {
|
|||||||
await writer.WriteAsync($"""
|
await writer.WriteAsync($"""
|
||||||
<config:config-item-map-entry config:name="{tbl}">
|
<config:config-item-map-entry config:name="{tbl}">
|
||||||
<config:config-item config:name="VerticalSplitMode" config:type="short">2</config:config-item>
|
<config:config-item config:name="VerticalSplitMode" config:type="short">2</config:config-item>
|
||||||
<config:config-item config:name="VerticalSplitPosition" config:type="int">4</config:config-item>
|
<config:config-item config:name="VerticalSplitPosition" config:type="int">5</config:config-item>
|
||||||
<config:config-item config:name="PositionBottom" config:type="int">4</config:config-item>
|
<config:config-item config:name="PositionBottom" config:type="int">5</config:config-item>
|
||||||
</config:config-item-map-entry>
|
</config:config-item-map-entry>
|
||||||
|
|
||||||
""");
|
""");
|
||||||
@ -198,6 +215,9 @@ namespace Elwig.Helpers.Export {
|
|||||||
FormatCell(table.FullName, colSpan: totalSpan, style: "header") +
|
FormatCell(table.FullName, colSpan: totalSpan, style: "header") +
|
||||||
$" </table:table-row>\r\n" +
|
$" </table:table-row>\r\n" +
|
||||||
$" <table:table-row>\r\n" +
|
$" <table:table-row>\r\n" +
|
||||||
|
FormatCell(table.Subtitle, colSpan: totalSpan, style: "subheader") +
|
||||||
|
$" </table:table-row>\r\n" +
|
||||||
|
$" <table:table-row>\r\n" +
|
||||||
$" <table:table-cell table:number-columns-repeated=\"{totalSpan}\"/>\r\n" +
|
$" <table:table-cell table:number-columns-repeated=\"{totalSpan}\"/>\r\n" +
|
||||||
$" </table:table-row>\r\n" +
|
$" </table:table-row>\r\n" +
|
||||||
$" <table:table-row>\r\n");
|
$" <table:table-row>\r\n");
|
||||||
@ -251,8 +271,8 @@ namespace Elwig.Helpers.Export {
|
|||||||
|
|
||||||
protected static string FormatCell(object? data, int rowSpan = 1, int colSpan = 1, string? style = "default", bool isCovered = false, string?[]? units = null) {
|
protected static string FormatCell(object? data, int rowSpan = 1, int colSpan = 1, string? style = "default", bool isCovered = false, string?[]? units = null) {
|
||||||
if (data?.GetType().IsValueType == true && data.GetType().Name.StartsWith("ValueTuple"))
|
if (data?.GetType().IsValueType == true && data.GetType().Name.StartsWith("ValueTuple"))
|
||||||
return string.Join("", data.GetType().GetFields().Zip(units ?? Array.Empty<string?>())
|
return string.Join("", data.GetType().GetFields().Zip(units ?? [])
|
||||||
.Select(p => FormatCell(p.First.GetValue(data), rowSpan, colSpan, style, isCovered, new[] { p.Second }))
|
.Select(p => FormatCell(p.First.GetValue(data), rowSpan, colSpan, style, isCovered, [p.Second]))
|
||||||
);
|
);
|
||||||
|
|
||||||
var add = (style != null ? $" table:style-name=\"{style}\"" : "") + (rowSpan > 1 || colSpan > 1 ? $" table:number-rows-spanned=\"{rowSpan}\" table:number-columns-spanned=\"{colSpan}\"" : "");
|
var add = (style != null ? $" table:style-name=\"{style}\"" : "") + (rowSpan > 1 || colSpan > 1 ? $" table:number-rows-spanned=\"{rowSpan}\" table:number-columns-spanned=\"{colSpan}\"" : "");
|
||||||
@ -262,6 +282,18 @@ namespace Elwig.Helpers.Export {
|
|||||||
string c;
|
string c;
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
c = $"<{ct}{add}/>";
|
c = $"<{ct}{add}/>";
|
||||||
|
} else if (data is bool b) {
|
||||||
|
add = string.Join(' ', add.Split(' ').Select(p => p.StartsWith("table:style-name=") ? $"table:style-name=\"boolean\"" : p));
|
||||||
|
c = $"<{ct} office:value-type=\"boolean\" calcext:value-type=\"boolean\" office:boolean-value=\"{(b ? "true" : "false")}\"{add}><text:p>{(b ? "Ja" : "Nein")}</text:p></{ct}>";
|
||||||
|
} else if (data is DateOnly date) {
|
||||||
|
add = string.Join(' ', add.Split(' ').Select(p => p.StartsWith("table:style-name=") ? $"table:style-name=\"date\"" : p));
|
||||||
|
c = $"<{ct} office:value-type=\"date\" calcext:value-type=\"date\" office:date-value=\"{date:yyyy-MM-dd}\"{add}><text:p>{date:dd.MM.yyyy}</text:p></{ct}>";
|
||||||
|
} else if (data is TimeOnly time) {
|
||||||
|
add = string.Join(' ', add.Split(' ').Select(p => p.StartsWith("table:style-name=") ? $"table:style-name=\"time\"" : p));
|
||||||
|
c = $"<{ct} office:value-type=\"time\" calcext:value-type=\"time\" office:time-value=\"{time:\\P\\THH\\Hmm\\Mss\\S}\"{add}><text:p>{time:HH:mm:ss}</text:p></{ct}>";
|
||||||
|
} else if (data is DateTime dt) {
|
||||||
|
add = string.Join(' ', add.Split(' ').Select(p => p.StartsWith("table:style-name=") ? $"table:style-name=\"datetime\"" : p));
|
||||||
|
c = $"<{ct} office:value-type=\"date\" calcext:value-type=\"date\" office:date-value=\"{dt:yyyy-MM-dd\\THH:mm:ss}\"{add}><text:p>{dt:dd.MM.yyyy HH:mm:ss}</text:p></{ct}>";
|
||||||
} else if (data is decimal || data is float || data is double || data is byte || data is char ||
|
} else if (data is decimal || data is float || data is double || data is byte || data is char ||
|
||||||
data is short || data is ushort || data is int || data is uint || data is long || data is ulong) {
|
data is short || data is ushort || data is int || data is uint || data is long || data is ulong) {
|
||||||
double v = double.Parse(data?.ToString() ?? "0"); // use default culture for ToString and Parse()!
|
double v = double.Parse(data?.ToString() ?? "0"); // use default culture for ToString and Parse()!
|
||||||
@ -270,10 +302,13 @@ namespace Elwig.Helpers.Export {
|
|||||||
switch (units[0]) {
|
switch (units[0]) {
|
||||||
case "%": n = 1; data = $"{v:N1}"; break;
|
case "%": n = 1; data = $"{v:N1}"; break;
|
||||||
case "€": n = 2; data = $"{v:N2}"; break;
|
case "€": n = 2; data = $"{v:N2}"; break;
|
||||||
|
case "€/kg": n = 4; data = $"{v:N4}"; break;
|
||||||
case "°KMW": n = 1; data = $"{v:N1}"; break;
|
case "°KMW": n = 1; data = $"{v:N1}"; break;
|
||||||
case "°Oe": n = 0; data = $"{v:N0}"; break;
|
case "°Oe": n = 0; data = $"{v:N0}"; break;
|
||||||
|
case "m²": n = 0; data = $"{v:N0}"; break;
|
||||||
|
case "kg": n = 0; data = $"{v:N0}"; break;
|
||||||
}
|
}
|
||||||
if (n >= 0) add = string.Join(" ", add.Split(" ").Select(p => p.StartsWith("table:style-name=") ? $"table:style-name=\"N{n}\"" : p));
|
if (n >= 0) add = string.Join(' ', add.Split(' ').Select(p => p.StartsWith("table:style-name=") ? $"table:style-name=\"N{n}\"" : p));
|
||||||
}
|
}
|
||||||
c = $"<{ct} office:value-type=\"float\" calcext:value-type=\"float\" office:value=\"{v.ToString(CultureInfo.InvariantCulture)}\"{add}><text:p>{data}</text:p></{ct}>";
|
c = $"<{ct} office:value-type=\"float\" calcext:value-type=\"float\" office:value=\"{v.ToString(CultureInfo.InvariantCulture)}\"{add}><text:p>{data}</text:p></{ct}>";
|
||||||
} else {
|
} else {
|
||||||
|
5
Elwig/Helpers/ExportMode.cs
Normal file
5
Elwig/Helpers/ExportMode.cs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
namespace Elwig.Helpers {
|
||||||
|
public enum ExportMode {
|
||||||
|
Show, SaveList, SavePdf, Print, Email, Export, Upload
|
||||||
|
}
|
||||||
|
}
|
99
Elwig/Helpers/Extensions.cs
Normal file
99
Elwig/Helpers/Extensions.cs
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Elwig.Helpers {
|
||||||
|
static partial class Extensions {
|
||||||
|
|
||||||
|
[LibraryImport("msvcrt.dll")]
|
||||||
|
[UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
|
||||||
|
private static unsafe partial int memcmp(void* b1, void* b2, long count);
|
||||||
|
|
||||||
|
public static unsafe int CompareBuffers(char[] buffer1, int offset1, char[] buffer2, int offset2, int count) {
|
||||||
|
fixed (char* b1 = buffer1, b2 = buffer2) {
|
||||||
|
return memcmp(b1 + offset1, b2 + offset2, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string? ReadUntil(this StreamReader reader, string delimiter) {
|
||||||
|
return ReadUntil(reader, delimiter.ToCharArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string? ReadUntil(this StreamReader reader, char delimiter) {
|
||||||
|
return ReadUntil(reader, new char[] { delimiter });
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string? ReadUntil(this StreamReader reader, char[] delimiter) {
|
||||||
|
var buf = new char[512];
|
||||||
|
int bufSize = 0, ret;
|
||||||
|
while (!reader.EndOfStream && bufSize < buf.Length - 1) {
|
||||||
|
if ((ret = reader.Read()) == -1)
|
||||||
|
return null;
|
||||||
|
buf[bufSize++] = (char)ret;
|
||||||
|
|
||||||
|
if (bufSize >= delimiter.Length && CompareBuffers(buf, bufSize - delimiter.Length, delimiter, 0, delimiter.Length) == 0)
|
||||||
|
return new string(buf, 0, bufSize);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<string?> ReadUntilAsync(this StreamReader reader, string delimiter) {
|
||||||
|
return ReadUntilAsync(reader, delimiter.ToCharArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<string?> ReadUntilAsync(this StreamReader reader, char delimiter) {
|
||||||
|
return ReadUntilAsync(reader, new char[] { delimiter });
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<string?> ReadUntilAsync(this StreamReader reader, char[] delimiter) {
|
||||||
|
var buf = new char[512];
|
||||||
|
int bufSize = 0;
|
||||||
|
var tmpBuf = new char[1];
|
||||||
|
while (!reader.EndOfStream && bufSize < buf.Length - 1) {
|
||||||
|
if ((await reader.ReadAsync(tmpBuf, 0, 1)) != 1)
|
||||||
|
return null;
|
||||||
|
buf[bufSize++] = tmpBuf[0];
|
||||||
|
|
||||||
|
if (bufSize >= delimiter.Length && CompareBuffers(buf, bufSize - delimiter.Length, delimiter, 0, delimiter.Length) == 0)
|
||||||
|
return new string(buf, 0, bufSize);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task CopyToAsync(this Stream source, Stream destination, int bufferSize, IProgress<long>? progress = null, CancellationToken cancellationToken = default) {
|
||||||
|
ArgumentNullException.ThrowIfNull(source);
|
||||||
|
if (!source.CanRead) throw new ArgumentException("Has to be readable", nameof(source));
|
||||||
|
ArgumentNullException.ThrowIfNull(destination);
|
||||||
|
if (!destination.CanWrite) throw new ArgumentException("Has to be writable", nameof(destination));
|
||||||
|
ArgumentOutOfRangeException.ThrowIfNegative(bufferSize);
|
||||||
|
|
||||||
|
var buffer = new byte[bufferSize];
|
||||||
|
long totalBytesRead = 0;
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) != 0) {
|
||||||
|
await destination.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false);
|
||||||
|
totalBytesRead += bytesRead;
|
||||||
|
progress?.Report(totalBytesRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task DownloadAsync(this HttpClient client, string requestUri, Stream destination, IProgress<double>? progress = null, CancellationToken cancellationToken = default) {
|
||||||
|
using var response = await client.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
var contentLength = response.Content.Headers.ContentLength;
|
||||||
|
|
||||||
|
using var download = await response.Content.ReadAsStreamAsync(cancellationToken);
|
||||||
|
if (progress == null || !contentLength.HasValue) {
|
||||||
|
await download.CopyToAsync(destination, cancellationToken);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var relativeProgress = new Progress<long>(totalBytes => progress.Report((double)totalBytes / contentLength.Value));
|
||||||
|
await download.CopyToAsync(destination, 81920, relativeProgress, cancellationToken);
|
||||||
|
progress.Report(100.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,24 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Elwig.Helpers {
|
|
||||||
public static class HttpClientExtensions {
|
|
||||||
public static async Task DownloadAsync(this HttpClient client, string requestUri, Stream destination, IProgress<double>? progress = null, CancellationToken cancellationToken = default) {
|
|
||||||
using var response = await client.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
|
|
||||||
var contentLength = response.Content.Headers.ContentLength;
|
|
||||||
|
|
||||||
using var download = await response.Content.ReadAsStreamAsync(cancellationToken);
|
|
||||||
if (progress == null || !contentLength.HasValue) {
|
|
||||||
await download.CopyToAsync(destination, cancellationToken);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var relativeProgress = new Progress<long>(totalBytes => progress.Report((double)totalBytes / contentLength.Value));
|
|
||||||
await download.CopyToAsync(destination, 81920, relativeProgress, cancellationToken);
|
|
||||||
progress.Report(100.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -59,7 +59,7 @@ namespace Elwig.Helpers.Printing {
|
|||||||
progress?.Report(0.0);
|
progress?.Report(0.0);
|
||||||
using var client = new TcpClient("127.0.0.1", 30983);
|
using var client = new TcpClient("127.0.0.1", 30983);
|
||||||
using var stream = client.GetStream();
|
using var stream = client.GetStream();
|
||||||
await stream.WriteAsync(Encoding.UTF8.GetBytes(
|
await stream.WriteAsync(Utils.UTF8.GetBytes(
|
||||||
"-e utf-8;-p;" + (doublePaged ? "-2;" : "") +
|
"-e utf-8;-p;" + (doublePaged ? "-2;" : "") +
|
||||||
$"{string.Join(';', htmlPath)};{pdfPath}" +
|
$"{string.Join(';', htmlPath)};{pdfPath}" +
|
||||||
"\r\n"));
|
"\r\n"));
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Elwig.Helpers {
|
|
||||||
public static class StreamExtensions {
|
|
||||||
public static async Task CopyToAsync(this Stream source, Stream destination, int bufferSize, IProgress<long>? progress = null, CancellationToken cancellationToken = default) {
|
|
||||||
ArgumentNullException.ThrowIfNull(source);
|
|
||||||
if (!source.CanRead) throw new ArgumentException("Has to be readable", nameof(source));
|
|
||||||
ArgumentNullException.ThrowIfNull(destination);
|
|
||||||
if (!destination.CanWrite) throw new ArgumentException("Has to be writable", nameof(destination));
|
|
||||||
ArgumentOutOfRangeException.ThrowIfNegative(bufferSize);
|
|
||||||
|
|
||||||
var buffer = new byte[bufferSize];
|
|
||||||
long totalBytesRead = 0;
|
|
||||||
int bytesRead;
|
|
||||||
while ((bytesRead = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) != 0) {
|
|
||||||
await destination.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false);
|
|
||||||
totalBytesRead += bytesRead;
|
|
||||||
progress?.Report(totalBytesRead);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -18,7 +18,16 @@ using System.Text.Json.Nodes;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using MailKit.Net.Smtp;
|
using MailKit.Net.Smtp;
|
||||||
using MailKit.Security;
|
using MailKit.Security;
|
||||||
using OpenTK.Compute.OpenCL;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Collections;
|
||||||
|
using Elwig.Documents;
|
||||||
|
using MimeKit;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using LinqKit;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using Elwig.Models;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
|
||||||
namespace Elwig.Helpers {
|
namespace Elwig.Helpers {
|
||||||
public static partial class Utils {
|
public static partial class Utils {
|
||||||
@ -28,6 +37,7 @@ namespace Elwig.Helpers {
|
|||||||
public static int CurrentYear => DateTime.Now.Year;
|
public static int CurrentYear => DateTime.Now.Year;
|
||||||
public static int CurrentNextSeason => DateTime.Now.Year - (DateTime.Now.Month <= 3 ? 1 : 0);
|
public static int CurrentNextSeason => DateTime.Now.Year - (DateTime.Now.Month <= 3 ? 1 : 0);
|
||||||
public static int CurrentLastSeason => DateTime.Now.Year - (DateTime.Now.Month <= 7 ? 1 : 0);
|
public static int CurrentLastSeason => DateTime.Now.Year - (DateTime.Now.Month <= 7 ? 1 : 0);
|
||||||
|
public static int FollowingSeason => DateTime.Now.Year + (DateTime.Now.Month >= 11 ? 1 : 0);
|
||||||
public static DateTime Today => (DateTime.Now.Hour >= 3) ? DateTime.Today : DateTime.Today.AddDays(-1);
|
public static DateTime Today => (DateTime.Now.Hour >= 3) ? DateTime.Today : DateTime.Today.AddDays(-1);
|
||||||
|
|
||||||
[GeneratedRegex("^serial://([A-Za-z0-9]+):([0-9]+)(,([5-9]),([NOEMSnoems]),(0|1|1\\.5|2|))?$", RegexOptions.Compiled)]
|
[GeneratedRegex("^serial://([A-Za-z0-9]+):([0-9]+)(,([5-9]),([NOEMSnoems]),(0|1|1\\.5|2|))?$", RegexOptions.Compiled)]
|
||||||
@ -71,7 +81,7 @@ namespace Elwig.Helpers {
|
|||||||
return PhoneNrTypes.Where(t => t.Key == type).Select(t => t.Value).FirstOrDefault(type);
|
return PhoneNrTypes.Where(t => t.Key == type).Select(t => t.Value).FirstOrDefault(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly string[] TempWildcards = ["*.html", "*.pdf", "*.exe"];
|
private static readonly string[] TempWildcards = ["*.html", "*.pdf", "*.exe", "*.zip"];
|
||||||
|
|
||||||
private static readonly ushort[] Crc16ModbusTable = [
|
private static readonly ushort[] Crc16ModbusTable = [
|
||||||
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
|
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
|
||||||
@ -318,7 +328,7 @@ namespace Elwig.Helpers {
|
|||||||
> 0 => "+",
|
> 0 => "+",
|
||||||
};
|
};
|
||||||
|
|
||||||
public static double AggregateDeliveryPartsKmw(IEnumerable<DeliveryPart> parts)
|
public static double AggregateDeliveryPartsKmw(IEnumerable<IDelivery> parts)
|
||||||
=> parts.Aggregate(
|
=> parts.Aggregate(
|
||||||
(Weight: 0, Kmw: 0.0),
|
(Weight: 0, Kmw: 0.0),
|
||||||
(sum, item) => (
|
(sum, item) => (
|
||||||
@ -399,19 +409,48 @@ namespace Elwig.Helpers {
|
|||||||
return InternetGetConnectedState(out var _, 0);
|
return InternetGetConnectedState(out var _, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<(string Version, string Url, long Size)?> GetLatestInstallerUrl(string url) {
|
public static HttpClient GetHttpClient(string? username = null, string? password = null, string? accept = null) {
|
||||||
try {
|
var client = new HttpClient() {
|
||||||
using var client = new HttpClient() {
|
|
||||||
Timeout = TimeSpan.FromSeconds(5),
|
Timeout = TimeSpan.FromSeconds(5),
|
||||||
};
|
};
|
||||||
var res = JsonNode.Parse(await client.GetStringAsync(url));
|
client.DefaultRequestHeaders.Accept.Clear();
|
||||||
var data = res!["data"]![0]!;
|
if (accept != null)
|
||||||
return ((string)data["version"]!, (string)data["url"]!, (int)data["size"]!);
|
client.DefaultRequestHeaders.Accept.Add(new(accept));
|
||||||
|
if (username != null || password != null)
|
||||||
|
client.DefaultRequestHeaders.Authorization = new("Basic", Convert.ToBase64String(
|
||||||
|
Utils.UTF8.GetBytes($"{username}:{password}")));
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<(string Version, string Url, long Size)?> GetLatestInstallerUrl(string url) {
|
||||||
|
try {
|
||||||
|
using var client = GetHttpClient(accept: "application/json");
|
||||||
|
var resJson = JsonNode.Parse(await client.GetStringAsync(url));
|
||||||
|
var data = resJson!["data"]![0]!;
|
||||||
|
return ((string)data["version"]!, (string)data["url"]!, (long)data["size"]!);
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task UploadExportData(string zip, string url, string username, string password) {
|
||||||
|
if (!url.EndsWith('/')) url += "/";
|
||||||
|
using var client = GetHttpClient(username, password, accept: "application/json");
|
||||||
|
var content = new StreamContent(new FileStream(zip, FileMode.Open, FileAccess.Read));
|
||||||
|
content.Headers.ContentType = new("application/zip");
|
||||||
|
using var res = await client.PutAsync(url + Path.GetFileName(zip), content);
|
||||||
|
res.EnsureSuccessStatusCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<JsonArray> GetExportMetaData(string url, string username, string password) {
|
||||||
|
using var client = GetHttpClient(username, password, accept: "application/json");
|
||||||
|
using var res = await client.GetAsync(url);
|
||||||
|
res.EnsureSuccessStatusCode();
|
||||||
|
var resJson = JsonNode.Parse(await res.Content.ReadAsStringAsync());
|
||||||
|
var data = resJson!["data"]!;
|
||||||
|
return data.AsArray();
|
||||||
|
}
|
||||||
|
|
||||||
public static void CleanupTempFiles() {
|
public static void CleanupTempFiles() {
|
||||||
var dir = new DirectoryInfo(App.TempPath);
|
var dir = new DirectoryInfo(App.TempPath);
|
||||||
foreach (var file in TempWildcards.SelectMany(dir.EnumerateFiles)) {
|
foreach (var file in TempWildcards.SelectMany(dir.EnumerateFiles)) {
|
||||||
@ -432,5 +471,93 @@ namespace Elwig.Helpers {
|
|||||||
await client.AuthenticateAsync(username, password);
|
await client.AuthenticateAsync(username, password);
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<bool> SendEmail(Member member, string subject, string text, IEnumerable<Document> docs) {
|
||||||
|
if (App.Config.Smtp == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SmtpClient? client = null;
|
||||||
|
try {
|
||||||
|
Mouse.OverrideCursor = Cursors.AppStarting;
|
||||||
|
client = await GetSmtpClient();
|
||||||
|
|
||||||
|
using var msg = new MimeMessage();
|
||||||
|
msg.From.Add(new MailboxAddress(App.Client.NameFull, App.Config.Smtp.Value.From));
|
||||||
|
msg.To.AddRange(member.EmailAddresses.OrderBy(a => a.Nr).Select(a => new MailboxAddress(member.AdministrativeName, a.Address)));
|
||||||
|
msg.Subject = subject;
|
||||||
|
var body = new Multipart("mixed") {
|
||||||
|
new TextPart("plain") { Text = text }
|
||||||
|
};
|
||||||
|
foreach (var doc in docs) {
|
||||||
|
var name = NormalizeFileName(doc.Title);
|
||||||
|
body.Add(doc.AsEmailAttachment($"{name}.pdf"));
|
||||||
|
}
|
||||||
|
msg.Body = body;
|
||||||
|
await client!.SendAsync(msg);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
if (client != null)
|
||||||
|
await client.DisconnectAsync(true);
|
||||||
|
client?.Dispose();
|
||||||
|
Mouse.OverrideCursor = null;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task ExportDocument(Document doc, ExportMode mode, string? filename = null, (Member, string, string)? emailData = null) {
|
||||||
|
if (mode == ExportMode.Print && !App.Config.Debug) {
|
||||||
|
await doc.Generate();
|
||||||
|
await doc.Print();
|
||||||
|
} else if (mode == ExportMode.Email && emailData is (Member, string, string) e) {
|
||||||
|
await doc.Generate();
|
||||||
|
var success = await SendEmail(e.Item1, e.Item2, e.Item3, [doc]);
|
||||||
|
if (success)
|
||||||
|
MessageBox.Show("Die E-Mail wurde erfolgreich verschickt!", "E-Mail verschickt",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
} else if (mode == ExportMode.SavePdf) {
|
||||||
|
var d = new SaveFileDialog() {
|
||||||
|
FileName = $"{NormalizeFileName(filename ?? doc.Title)}.pdf",
|
||||||
|
DefaultExt = "pdf",
|
||||||
|
Filter = "PDF-Datei (*.pdf)|*.pdf",
|
||||||
|
Title = $"{doc.Title} speichern unter - Elwig"
|
||||||
|
};
|
||||||
|
if (d.ShowDialog() == true) {
|
||||||
|
await doc.Generate();
|
||||||
|
doc.SaveTo(d.FileName);
|
||||||
|
Process.Start("explorer.exe", d.FileName);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await doc.Generate();
|
||||||
|
doc.Show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int? GetEntityIdentifier(object? obj) {
|
||||||
|
if (obj == null) {
|
||||||
|
return null;
|
||||||
|
} else if (obj is IEnumerable list && obj is not string) {
|
||||||
|
var arr = list.Cast<object>().Select(GetEntityIdentifier).ToArray();
|
||||||
|
return ((IStructuralEquatable)arr).GetHashCode(EqualityComparer<int?>.Default);
|
||||||
|
} else if (obj.GetType().GetCustomAttribute(typeof(PrimaryKeyAttribute), true) is PrimaryKeyAttribute pkAttr) {
|
||||||
|
var pk = pkAttr.PropertyNames.Select(name => obj.GetType().GetProperty(name)!.GetValue(obj)?.GetHashCode() ?? 0).ToArray();
|
||||||
|
return ((IStructuralEquatable)pk).GetHashCode(EqualityComparer<int>.Default);
|
||||||
|
} else {
|
||||||
|
return obj.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Expression<Func<AreaCom, bool>> ActiveAreaCommitments() => ActiveAreaCommitments(CurrentYear);
|
||||||
|
public static Expression<Func<AreaCom, bool>> ActiveAreaCommitments(int yearTo) =>
|
||||||
|
c => (c.YearFrom <= yearTo) && (c.YearTo == null || c.YearTo >= yearTo);
|
||||||
|
|
||||||
|
public static IQueryable<AreaCom> ActiveAreaCommitments(IQueryable<AreaCom> query) => ActiveAreaCommitments(query, CurrentYear);
|
||||||
|
public static IQueryable<AreaCom> ActiveAreaCommitments(IQueryable<AreaCom> query, int yearTo) =>
|
||||||
|
query.Where(ActiveAreaCommitments(yearTo));
|
||||||
|
|
||||||
|
public static IEnumerable<AreaCom> ActiveAreaCommitments(IEnumerable<AreaCom> query) => ActiveAreaCommitments(query, CurrentYear);
|
||||||
|
public static IEnumerable<AreaCom> ActiveAreaCommitments(IEnumerable<AreaCom> query, int yearTo) =>
|
||||||
|
query.Where(c => ActiveAreaCommitments(yearTo).Invoke(c));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,13 +128,14 @@ namespace Elwig.Helpers {
|
|||||||
return new(true, null);
|
return new(true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ValidationResult CheckPlz(TextBox input, bool required, AppDbContext ctx) {
|
public static ValidationResult CheckPlz(TextBox input, bool required) {
|
||||||
CheckInteger(input, false, 4);
|
CheckInteger(input, false, 4);
|
||||||
if (!required && input.Text.Length == 0) {
|
if (!required && input.Text.Length == 0) {
|
||||||
return new(true, null);
|
return new(true, null);
|
||||||
} else if (input.Text.Length != 4) {
|
} else if (input.Text.Length != 4) {
|
||||||
return new(false, "PLZ zu kurz");
|
return new(false, "PLZ zu kurz");
|
||||||
}
|
}
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
int plz = int.Parse(input.Text);
|
int plz = int.Parse(input.Text);
|
||||||
if (ctx.Postleitzahlen.Find(plz) == null) {
|
if (ctx.Postleitzahlen.Find(plz) == null) {
|
||||||
return new(false, "Ungültige PLZ");
|
return new(false, "Ungültige PLZ");
|
||||||
@ -413,7 +414,7 @@ namespace Elwig.Helpers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ValidationResult CheckMgNr(TextBox input, bool required, AppDbContext ctx) {
|
public static ValidationResult CheckMgNr(TextBox input, bool required) {
|
||||||
var res = CheckInteger(input, required);
|
var res = CheckInteger(input, required);
|
||||||
if (!res.IsValid) {
|
if (!res.IsValid) {
|
||||||
return res;
|
return res;
|
||||||
@ -421,6 +422,7 @@ namespace Elwig.Helpers {
|
|||||||
return new(true, null);
|
return new(true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
int nr = int.Parse(input.Text);
|
int nr = int.Parse(input.Text);
|
||||||
if (!ctx.MgNrExists(nr).GetAwaiter().GetResult()) {
|
if (!ctx.MgNrExists(nr).GetAwaiter().GetResult()) {
|
||||||
return new(false, "Ungültige Mitgliedsnummer");
|
return new(false, "Ungültige Mitgliedsnummer");
|
||||||
@ -429,7 +431,7 @@ namespace Elwig.Helpers {
|
|||||||
return new(true, null);
|
return new(true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ValidationResult CheckNewMgNr(TextBox input, bool required, AppDbContext ctx, Member? m) {
|
public static ValidationResult CheckNewMgNr(TextBox input, bool required, Member? m) {
|
||||||
var res = CheckInteger(input, required);
|
var res = CheckInteger(input, required);
|
||||||
if (!res.IsValid) {
|
if (!res.IsValid) {
|
||||||
return res;
|
return res;
|
||||||
@ -437,6 +439,7 @@ namespace Elwig.Helpers {
|
|||||||
return new(true, null);
|
return new(true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
int nr = int.Parse(input.Text);
|
int nr = int.Parse(input.Text);
|
||||||
if (nr != m?.MgNr && ctx.MgNrExists(nr).GetAwaiter().GetResult()) {
|
if (nr != m?.MgNr && ctx.MgNrExists(nr).GetAwaiter().GetResult()) {
|
||||||
return new(false, "Mitgliedsnummer wird bereits verwendet");
|
return new(false, "Mitgliedsnummer wird bereits verwendet");
|
||||||
@ -445,7 +448,7 @@ namespace Elwig.Helpers {
|
|||||||
return new(true, null);
|
return new(true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ValidationResult CheckSortId(TextBox input, bool required, AppDbContext ctx) {
|
public static ValidationResult CheckSortId(TextBox input, bool required) {
|
||||||
var res = CheckUpperCase(input, required, 3);
|
var res = CheckUpperCase(input, required, 3);
|
||||||
if (!res.IsValid) {
|
if (!res.IsValid) {
|
||||||
return res;
|
return res;
|
||||||
@ -453,6 +456,7 @@ namespace Elwig.Helpers {
|
|||||||
return new(true, null);
|
return new(true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
if (input.Text.Length < 2 || !ctx.SortIdExists(input.Text[0..2]).GetAwaiter().GetResult()) {
|
if (input.Text.Length < 2 || !ctx.SortIdExists(input.Text[0..2]).GetAwaiter().GetResult()) {
|
||||||
return new(false, "Ungültige Sorte");
|
return new(false, "Ungültige Sorte");
|
||||||
} else if (input.Text.Length >= 3) {
|
} else if (input.Text.Length >= 3) {
|
||||||
@ -465,8 +469,9 @@ namespace Elwig.Helpers {
|
|||||||
return new(true, null);
|
return new(true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ValidationResult CheckPredecessorMgNr(TextBox input, bool required, AppDbContext ctx) {
|
public static ValidationResult CheckPredecessorMgNr(TextBox input, bool required) {
|
||||||
var res = CheckInteger(input, required);
|
var res = CheckInteger(input, required);
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
if (!res.IsValid) {
|
if (!res.IsValid) {
|
||||||
return res;
|
return res;
|
||||||
} else if (!required && input.Text.Length == 0) {
|
} else if (!required && input.Text.Length == 0) {
|
||||||
@ -575,7 +580,7 @@ namespace Elwig.Helpers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ValidationResult CheckFbNr(TextBox input, bool required, AppDbContext ctx, AreaCom? c) {
|
public static ValidationResult CheckFbNr(TextBox input, bool required, AreaCom? c) {
|
||||||
var res = CheckInteger(input, required);
|
var res = CheckInteger(input, required);
|
||||||
if (!res.IsValid) {
|
if (!res.IsValid) {
|
||||||
return res;
|
return res;
|
||||||
@ -583,6 +588,7 @@ namespace Elwig.Helpers {
|
|||||||
return new(true, null);
|
return new(true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
int nr = int.Parse(input.Text);
|
int nr = int.Parse(input.Text);
|
||||||
if (nr != c?.FbNr && ctx.FbNrExists(nr).GetAwaiter().GetResult()) {
|
if (nr != c?.FbNr && ctx.FbNrExists(nr).GetAwaiter().GetResult()) {
|
||||||
return new(false, "Flächenbindungsnummer wird bereits verwendet");
|
return new(false, "Flächenbindungsnummer wird bereits verwendet");
|
||||||
|
@ -26,6 +26,8 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
Model = model;
|
Model = model;
|
||||||
IsReady = true;
|
IsReady = true;
|
||||||
HasFillingClearance = false;
|
HasFillingClearance = false;
|
||||||
|
Stream.WriteTimeout = -1;
|
||||||
|
Stream.ReadTimeout = -1;
|
||||||
BackgroundThread = new Thread(new ParameterizedThreadStart(BackgroundLoop));
|
BackgroundThread = new Thread(new ParameterizedThreadStart(BackgroundLoop));
|
||||||
BackgroundThread.Start();
|
BackgroundThread.Start();
|
||||||
}
|
}
|
||||||
@ -46,7 +48,8 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
while (IsRunning) {
|
while (IsRunning) {
|
||||||
try {
|
try {
|
||||||
var data = await Receive();
|
var data = await Receive();
|
||||||
RaiseWeighingEvent(new WeighingEventArgs(data));
|
if (data != null)
|
||||||
|
RaiseWeighingEvent(new WeighingEventArgs(data.Value));
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
MessageBox.Show($"Beim Wiegen ist ein Fehler Aufgetreten:\n\n{ex.Message}", "Waagenfehler",
|
MessageBox.Show($"Beim Wiegen ist ein Fehler Aufgetreten:\n\n{ex.Message}", "Waagenfehler",
|
||||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
@ -54,13 +57,12 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task<WeighingResult> Receive() {
|
protected async Task<WeighingResult?> Receive() {
|
||||||
string? line = null;
|
var line = await Reader.ReadUntilAsync("\r\n");
|
||||||
using (var reader = new StreamReader(Stream, Encoding.ASCII, false, -1, true)) {
|
if (LogPath != null) await File.AppendAllTextAsync(LogPath, line);
|
||||||
line = await reader.ReadLineAsync();
|
if (line == null || line == "") {
|
||||||
if (LogPath != null) await File.AppendAllTextAsync(LogPath, $"{line}\r\n");
|
return null;
|
||||||
}
|
} else if (line.Length != 35 || line[0] != ' ' || line[9] != ' ' || line[15] != ' ' || line[20] != ' ' || line[32] != ' ') {
|
||||||
if (line == null || line.Length != 33 || line[0] != ' ' || line[9] != ' ' || line[15] != ' ' || line[20] != ' ' || line[32] != ' ') {
|
|
||||||
throw new IOException($"Invalid event from scale: '{line}'");
|
throw new IOException($"Invalid event from scale: '{line}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
122
Elwig/Helpers/Weighing/GassnerScale.cs
Normal file
122
Elwig/Helpers/Weighing/GassnerScale.cs
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Ports;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Elwig.Helpers.Weighing {
|
||||||
|
public class GassnerScale : Scale, ICommandScale {
|
||||||
|
|
||||||
|
public string Manufacturer => "Gassner";
|
||||||
|
public int InternalScaleNr => 1;
|
||||||
|
public string Model { get; private set; }
|
||||||
|
public string ScaleId { get; private set; }
|
||||||
|
public bool IsReady { get; private set; }
|
||||||
|
public bool HasFillingClearance { get; private set; }
|
||||||
|
|
||||||
|
public GassnerScale(string id, string model, string cnx, string? empty = null, string? filling = null, int? limit = null, string? log = null) :
|
||||||
|
base(cnx, empty, filling, limit, log) {
|
||||||
|
ScaleId = id;
|
||||||
|
Model = model;
|
||||||
|
IsReady = true;
|
||||||
|
HasFillingClearance = false;
|
||||||
|
Stream.WriteTimeout = 250;
|
||||||
|
Stream.ReadTimeout = 11000;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Task SendCommand(char command) => SendCommand(Convert.ToByte(command));
|
||||||
|
|
||||||
|
protected async Task SendCommand(byte command) {
|
||||||
|
byte[] cmd = [command];
|
||||||
|
await Stream.WriteAsync(cmd);
|
||||||
|
if (LogPath != null) await File.AppendAllTextAsync(LogPath, Encoding.ASCII.GetString(cmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task<string> ReceiveResponse() {
|
||||||
|
var line = await Reader.ReadUntilAsync('\x03');
|
||||||
|
if (LogPath != null) await File.AppendAllTextAsync(LogPath, $"{line}\r\n");
|
||||||
|
if (line == null || line.Length < 4 || !line.StartsWith('\x02')) {
|
||||||
|
throw new IOException("Invalid response from scale");
|
||||||
|
}
|
||||||
|
|
||||||
|
var status = line[1..3];
|
||||||
|
if (status[0] == 'E' || status[1] != 'S') {
|
||||||
|
string msg = $"Unbekannter Fehler (Fehler code {status})";
|
||||||
|
switch (status[1]) {
|
||||||
|
case 'M': msg = "Waage in Bewegung"; break;
|
||||||
|
}
|
||||||
|
throw new IOException($"Waagenfehler {status}: {msg}");
|
||||||
|
} else if (status[0] != ' ') {
|
||||||
|
throw new IOException($"Invalid response from scale (error code {status})");
|
||||||
|
}
|
||||||
|
|
||||||
|
return line[1..^1];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task<WeighingResult> Weigh(bool incIdentNr) {
|
||||||
|
await SendCommand(incIdentNr ? '\x05' : '?');
|
||||||
|
string record = await ReceiveResponse();
|
||||||
|
if (record.Length != 45)
|
||||||
|
throw new IOException("Invalid response from scale: Received record has invalid size");
|
||||||
|
var line = record[2..];
|
||||||
|
|
||||||
|
var status = line[ 0.. 2];
|
||||||
|
var brutto = line[ 2.. 9].Trim();
|
||||||
|
var tara = line[ 9..16].Trim();
|
||||||
|
var netto = line[16..23].Trim();
|
||||||
|
var scaleNr = line[23..25].Trim();
|
||||||
|
var identNr = line[25..31].Trim();
|
||||||
|
var date = line[31..39];
|
||||||
|
var time = line[39..45];
|
||||||
|
|
||||||
|
identNr = identNr.Length > 0 && identNr != "0" ? identNr : null;
|
||||||
|
var parsedDate = DateOnly.ParseExact(date, "yyyyMMdd");
|
||||||
|
return new() {
|
||||||
|
Weight = int.Parse(netto),
|
||||||
|
WeighingId = identNr,
|
||||||
|
FullWeighingId = identNr,
|
||||||
|
Date = parsedDate,
|
||||||
|
Time = TimeOnly.ParseExact(time, "HHmmss"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<WeighingResult> Weigh() {
|
||||||
|
return await Weigh(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<WeighingResult> GetCurrentWeight() {
|
||||||
|
return await Weigh(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Empty() {
|
||||||
|
SerialPort? p = ControlSerialEmpty ?? Serial;
|
||||||
|
if (EmptyMode == Output.RTS && p != null) {
|
||||||
|
p.RtsEnable = true;
|
||||||
|
await Task.Delay(EmptyDelay);
|
||||||
|
p.RtsEnable = false;
|
||||||
|
} else if (EmptyMode == Output.DTR && p != null) {
|
||||||
|
p.DtrEnable = true;
|
||||||
|
await Task.Delay(EmptyDelay);
|
||||||
|
p.DtrEnable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Task SetFillingClearance(bool status) {
|
||||||
|
SerialPort? p = ControlSerialFilling ?? Serial;
|
||||||
|
if (FillingClearanceMode == Output.RTS && p != null) {
|
||||||
|
p.RtsEnable = status;
|
||||||
|
} else if (FillingClearanceMode == Output.DTR && p != null) {
|
||||||
|
p.DtrEnable = status;
|
||||||
|
}
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task GrantFillingClearance() {
|
||||||
|
await SetFillingClearance(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RevokeFillingClearance() {
|
||||||
|
await SetFillingClearance(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Elwig.Helpers.Weighing {
|
namespace Elwig.Helpers.Weighing {
|
||||||
public abstract class Scale : IDisposable {
|
public abstract class Scale : IDisposable {
|
||||||
@ -12,6 +13,7 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
protected SerialPort? ControlSerialEmpty = null, ControlSerialFilling = null;
|
protected SerialPort? ControlSerialEmpty = null, ControlSerialFilling = null;
|
||||||
protected TcpClient? Tcp = null;
|
protected TcpClient? Tcp = null;
|
||||||
protected Stream Stream;
|
protected Stream Stream;
|
||||||
|
protected StreamReader Reader;
|
||||||
|
|
||||||
protected readonly Output? EmptyMode = null;
|
protected readonly Output? EmptyMode = null;
|
||||||
protected readonly Output? FillingClearanceMode = null;
|
protected readonly Output? FillingClearanceMode = null;
|
||||||
@ -26,6 +28,8 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
return new SysTecITScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
|
return new SysTecITScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
|
||||||
} else if (config.Type == "Avery-Async") {
|
} else if (config.Type == "Avery-Async") {
|
||||||
return new AveryEventScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
|
return new AveryEventScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
|
||||||
|
} else if (config.Type == "Gassner") {
|
||||||
|
return new GassnerScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
|
||||||
} else {
|
} else {
|
||||||
throw new ArgumentException($"Invalid scale type: \"{config.Type}\"");
|
throw new ArgumentException($"Invalid scale type: \"{config.Type}\"");
|
||||||
}
|
}
|
||||||
@ -41,6 +45,7 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
} else {
|
} else {
|
||||||
throw new ArgumentException($"Unsupported scheme: \"{cnx.Split(':')[0]}\"");
|
throw new ArgumentException($"Unsupported scheme: \"{cnx.Split(':')[0]}\"");
|
||||||
}
|
}
|
||||||
|
Reader = new(Stream, Encoding.ASCII, false, 512);
|
||||||
|
|
||||||
LogPath = log;
|
LogPath = log;
|
||||||
|
|
||||||
@ -73,6 +78,7 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
|
Reader.Close();
|
||||||
Stream.Close();
|
Stream.Close();
|
||||||
Serial?.Close();
|
Serial?.Close();
|
||||||
ControlSerialEmpty?.Close();
|
ControlSerialEmpty?.Close();
|
||||||
|
@ -31,12 +31,9 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async Task<string> ReceiveResponse() {
|
protected async Task<string> ReceiveResponse() {
|
||||||
string? line = null;
|
var line = await Reader.ReadUntilAsync("\r\n");
|
||||||
using (var reader = new StreamReader(Stream, Encoding.ASCII, false, -1, true)) {
|
if (LogPath != null) await File.AppendAllTextAsync(LogPath, line);
|
||||||
line = await reader.ReadLineAsync();
|
if (line == null || line.Length < 4 || !line.StartsWith('<') || !line.EndsWith(">\r\n")) {
|
||||||
if (LogPath != null) await File.AppendAllTextAsync(LogPath, $"{line}\r\n");
|
|
||||||
}
|
|
||||||
if (line == null || line.Length < 4 || !line.StartsWith('<') || !line.EndsWith('>')) {
|
|
||||||
throw new IOException("Invalid response from scale");
|
throw new IOException("Invalid response from scale");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +65,7 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
throw new IOException($"Invalid response from scale (error code {error})");
|
throw new IOException($"Invalid response from scale (error code {error})");
|
||||||
}
|
}
|
||||||
|
|
||||||
return line[1..^1];
|
return line[1..^3];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task<WeighingResult> Weigh(bool incIdentNr) {
|
protected async Task<WeighingResult> Weigh(bool incIdentNr) {
|
||||||
@ -82,7 +79,7 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
var date = line[ 2..10];
|
var date = line[ 2..10];
|
||||||
var time = line[10..15];
|
var time = line[10..15];
|
||||||
var identNr = line[15..19].Trim();
|
var identNr = line[15..19].Trim();
|
||||||
var scaleNr = line[19..20];
|
var scaleNr = line[19..20].Trim();
|
||||||
var brutto = line[20..28].Trim();
|
var brutto = line[20..28].Trim();
|
||||||
var tara = line[28..36].Trim();
|
var tara = line[28..36].Trim();
|
||||||
var netto = line[36..44].Trim();
|
var netto = line[36..44].Trim();
|
||||||
|
@ -9,8 +9,8 @@ namespace Elwig.Models.Dtos {
|
|||||||
|
|
||||||
private static readonly (string, string, string?, int)[] FieldNames = [
|
private static readonly (string, string, string?, int)[] FieldNames = [
|
||||||
("MgNr", "MgNr.", null, 12),
|
("MgNr", "MgNr.", null, 12),
|
||||||
("Name", "Name", null, 40),
|
("Name1", "Name", null, 40),
|
||||||
("GivenName", "Vorname", null, 40),
|
("Name2", "Vorname", null, 40),
|
||||||
("Address", "Adresse", null, 60),
|
("Address", "Adresse", null, 60),
|
||||||
("Plz", "PLZ", null, 10),
|
("Plz", "PLZ", null, 10),
|
||||||
("Locality", "Ort", null, 60),
|
("Locality", "Ort", null, 60),
|
||||||
@ -35,7 +35,10 @@ namespace Elwig.Models.Dtos {
|
|||||||
|
|
||||||
private static async Task<IEnumerable<AreaComUnderDeliveryRowSingle>> FromDbSet(DbSet<AreaComUnderDeliveryRowSingle> table, int year) {
|
private static async Task<IEnumerable<AreaComUnderDeliveryRowSingle>> FromDbSet(DbSet<AreaComUnderDeliveryRowSingle> table, int year) {
|
||||||
return await table.FromSqlRaw($"""
|
return await table.FromSqlRaw($"""
|
||||||
SELECT m.mgnr, m.family_name, m.given_name, p.plz, o.name AS ort, m.address,
|
SELECT m.mgnr, m.family_name AS name_1,
|
||||||
|
COALESCE(m.prefix || ' ', '') || m.given_name ||
|
||||||
|
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
|
||||||
|
p.plz, o.name AS ort, m.address,
|
||||||
c.bucket, c.area, u.min_kg, u.weight
|
c.bucket, c.area, u.min_kg, u.weight
|
||||||
FROM member m
|
FROM member m
|
||||||
LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
|
LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
|
||||||
@ -50,8 +53,8 @@ namespace Elwig.Models.Dtos {
|
|||||||
|
|
||||||
public class AreaComUnderDeliveryRow {
|
public class AreaComUnderDeliveryRow {
|
||||||
public int MgNr;
|
public int MgNr;
|
||||||
public string Name;
|
public string Name1;
|
||||||
public string GivenName;
|
public string Name2;
|
||||||
public string Address;
|
public string Address;
|
||||||
public int Plz;
|
public int Plz;
|
||||||
public string Locality;
|
public string Locality;
|
||||||
@ -66,8 +69,8 @@ namespace Elwig.Models.Dtos {
|
|||||||
public AreaComUnderDeliveryRow(IEnumerable<AreaComUnderDeliveryRowSingle> rows) {
|
public AreaComUnderDeliveryRow(IEnumerable<AreaComUnderDeliveryRowSingle> rows) {
|
||||||
var f = rows.First();
|
var f = rows.First();
|
||||||
MgNr = f.MgNr;
|
MgNr = f.MgNr;
|
||||||
Name = f.Name;
|
Name1 = f.Name1;
|
||||||
GivenName = f.GivenName;
|
Name2 = f.Name2;
|
||||||
Address = f.Address;
|
Address = f.Address;
|
||||||
Plz = f.Plz;
|
Plz = f.Plz;
|
||||||
Locality = f.Locality.Split(",")[0];
|
Locality = f.Locality.Split(",")[0];
|
||||||
@ -82,10 +85,10 @@ namespace Elwig.Models.Dtos {
|
|||||||
public class AreaComUnderDeliveryRowSingle {
|
public class AreaComUnderDeliveryRowSingle {
|
||||||
[Column("mgnr")]
|
[Column("mgnr")]
|
||||||
public int MgNr { get; set; }
|
public int MgNr { get; set; }
|
||||||
[Column("family_name")]
|
[Column("name_1")]
|
||||||
public required string Name { get; set; }
|
public required string Name1 { get; set; }
|
||||||
[Column("given_name")]
|
[Column("name_2")]
|
||||||
public required string GivenName { get; set; }
|
public required string Name2 { get; set; }
|
||||||
[Column("address")]
|
[Column("address")]
|
||||||
public required string Address { get; set; }
|
public required string Address { get; set; }
|
||||||
[Column("plz")]
|
[Column("plz")]
|
||||||
|
@ -12,8 +12,8 @@ namespace Elwig.Models.Dtos {
|
|||||||
|
|
||||||
private static readonly (string, string, string?, int)[] FieldNames = [
|
private static readonly (string, string, string?, int)[] FieldNames = [
|
||||||
("MgNr", "MgNr.", null, 12),
|
("MgNr", "MgNr.", null, 12),
|
||||||
("Name", "Name", null, 40),
|
("Name1", "Name", null, 40),
|
||||||
("GivenName", "Vorname", null, 40),
|
("Name2", "Vorname", null, 40),
|
||||||
("Address", "Adresse", null, 60),
|
("Address", "Adresse", null, 60),
|
||||||
("Plz", "PLZ", null, 10),
|
("Plz", "PLZ", null, 10),
|
||||||
("Locality", "Ort", null, 60),
|
("Locality", "Ort", null, 60),
|
||||||
@ -49,7 +49,10 @@ namespace Elwig.Models.Dtos {
|
|||||||
|
|
||||||
private static async Task<IEnumerable<CreditNoteRowSingle>> FromDbSet(DbSet<CreditNoteRowSingle> table, int year, int avnr) {
|
private static async Task<IEnumerable<CreditNoteRowSingle>> FromDbSet(DbSet<CreditNoteRowSingle> table, int year, int avnr) {
|
||||||
return await table.FromSqlRaw($"""
|
return await table.FromSqlRaw($"""
|
||||||
SELECT m.mgnr, m.family_name, m.given_name, p.plz, o.name AS ort, m.address, m.iban, c.tgnr, s.year, s.precision,
|
SELECT m.mgnr, m.family_name AS name_1,
|
||||||
|
COALESCE(m.prefix || ' ', '') || m.given_name ||
|
||||||
|
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
|
||||||
|
p.plz, o.name AS ort, m.address, m.iban, c.tgnr, s.year, s.precision,
|
||||||
p.amount - p.net_amount AS surcharge,
|
p.amount - p.net_amount AS surcharge,
|
||||||
c.net_amount, c.prev_net_amount, c.vat, c.vat_amount, c.gross_amount, c.modifiers, c.prev_modifiers, c.amount,
|
c.net_amount, c.prev_net_amount, c.vat, c.vat_amount, c.gross_amount, c.modifiers, c.prev_modifiers, c.amount,
|
||||||
ROUND(COALESCE(u.total_penalty, 0) / POW(10, 4 - 2)) AS fb_penalty,
|
ROUND(COALESCE(u.total_penalty, 0) / POW(10, 4 - 2)) AS fb_penalty,
|
||||||
@ -72,8 +75,8 @@ namespace Elwig.Models.Dtos {
|
|||||||
|
|
||||||
public class CreditNoteRow {
|
public class CreditNoteRow {
|
||||||
public int MgNr;
|
public int MgNr;
|
||||||
public string Name;
|
public string Name1;
|
||||||
public string GivenName;
|
public string Name2;
|
||||||
public string Address;
|
public string Address;
|
||||||
public int Plz;
|
public int Plz;
|
||||||
public string Locality;
|
public string Locality;
|
||||||
@ -96,8 +99,8 @@ namespace Elwig.Models.Dtos {
|
|||||||
public CreditNoteRow(CreditNoteRowSingle row, BillingData data) {
|
public CreditNoteRow(CreditNoteRowSingle row, BillingData data) {
|
||||||
byte prec1 = 2, prec2 = row.Precision;
|
byte prec1 = 2, prec2 = row.Precision;
|
||||||
MgNr = row.MgNr;
|
MgNr = row.MgNr;
|
||||||
Name = row.Name;
|
Name1 = row.Name1;
|
||||||
GivenName = row.GivenName;
|
Name2 = row.Name2;
|
||||||
Address = row.Address;
|
Address = row.Address;
|
||||||
Plz = row.Plz;
|
Plz = row.Plz;
|
||||||
Locality = row.Locality;
|
Locality = row.Locality;
|
||||||
@ -132,10 +135,10 @@ namespace Elwig.Models.Dtos {
|
|||||||
public class CreditNoteRowSingle {
|
public class CreditNoteRowSingle {
|
||||||
[Column("mgnr")]
|
[Column("mgnr")]
|
||||||
public int MgNr { get; set; }
|
public int MgNr { get; set; }
|
||||||
[Column("family_name")]
|
[Column("name_1")]
|
||||||
public required string Name { get; set; }
|
public required string Name1 { get; set; }
|
||||||
[Column("given_name")]
|
[Column("name_2")]
|
||||||
public required string GivenName { get; set; }
|
public required string Name2 { get; set; }
|
||||||
[Column("address")]
|
[Column("address")]
|
||||||
public required string Address { get; set; }
|
public required string Address { get; set; }
|
||||||
[Column("plz")]
|
[Column("plz")]
|
||||||
|
@ -8,6 +8,7 @@ namespace Elwig.Models.Dtos {
|
|||||||
|
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string FullName { get; set; }
|
public string FullName { get; set; }
|
||||||
|
public string? Subtitle { get; set; }
|
||||||
public IEnumerable<T> Rows { get; private set; }
|
public IEnumerable<T> Rows { get; private set; }
|
||||||
public int RowNum => Rows.Count();
|
public int RowNum => Rows.Count();
|
||||||
public int ColNum => ColumnNames.Count();
|
public int ColNum => ColumnNames.Count();
|
||||||
@ -24,6 +25,11 @@ namespace Elwig.Models.Dtos {
|
|||||||
private readonly FieldInfo[] _fields;
|
private readonly FieldInfo[] _fields;
|
||||||
private readonly (string, PropertyInfo?, FieldInfo?)[] _map;
|
private readonly (string, PropertyInfo?, FieldInfo?)[] _map;
|
||||||
|
|
||||||
|
public DataTable(string name, string fullName, string subtitle, IEnumerable<T> rows, IEnumerable<(string, string, string?, int?)>? colNames = null) :
|
||||||
|
this(name, fullName, rows, colNames) {
|
||||||
|
Subtitle = subtitle;
|
||||||
|
}
|
||||||
|
|
||||||
public DataTable(string name, string fullName, IEnumerable<T> rows, IEnumerable<(string, string, string?, int?)>? colNames = null) {
|
public DataTable(string name, string fullName, IEnumerable<T> rows, IEnumerable<(string, string, string?, int?)>? colNames = null) {
|
||||||
_fields = typeof(T).GetFields();
|
_fields = typeof(T).GetFields();
|
||||||
_properties = typeof(T).GetProperties();
|
_properties = typeof(T).GetProperties();
|
||||||
|
107
Elwig/Models/Dtos/DeliveryJournalData.cs
Normal file
107
Elwig/Models/Dtos/DeliveryJournalData.cs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
using Elwig.Documents;
|
||||||
|
using Elwig.Models.Entities;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Elwig.Models.Dtos {
|
||||||
|
public class DeliveryJournalData : DataTable<DeliveryJournalRow> {
|
||||||
|
|
||||||
|
private static readonly (string, string, string?, int?)[] FieldNames = [
|
||||||
|
("LsNr", "LsNr.", null, 30),
|
||||||
|
("Pos", "Pos.", null, 10),
|
||||||
|
("Date", "Datum", null, 20),
|
||||||
|
("Time", "Zeit", null, 20),
|
||||||
|
("MgNr", "MgNr.", null, 12),
|
||||||
|
("Name1", "Name", null, 40),
|
||||||
|
("Name2", "Vorname", null, 40),
|
||||||
|
("SortId", "Sorte", null, 10),
|
||||||
|
("AttrId", "Attr.", null, 15),
|
||||||
|
("CultId", "Bewirt.", null, 15),
|
||||||
|
("QualId", "Qualität", null, 15),
|
||||||
|
("Gradation", "Gradation", "°Oe|°KMW", 40),
|
||||||
|
("Weight", "Gewicht", "kg", 20),
|
||||||
|
("NetGross", "bto./nto.", null, 20),
|
||||||
|
("HkId", "Herkunft", null, 20),
|
||||||
|
("Modifiers", "Zu-/Abschläge", null, 40),
|
||||||
|
("Comment", "Anmerkung", null, 60),
|
||||||
|
];
|
||||||
|
|
||||||
|
public DeliveryJournalData(IEnumerable<DeliveryJournalRow> rows, List<string> filterNames) :
|
||||||
|
base(DeliveryJournal.Name, DeliveryJournal.Name, string.Join(" / ", filterNames), rows, FieldNames) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<DeliveryJournalData> FromQuery(IQueryable<DeliveryPart> query, List<string> filterNames) {
|
||||||
|
return new((await query
|
||||||
|
.Include(p => p.Delivery.Member)
|
||||||
|
.Include(p => p.Delivery.Branch)
|
||||||
|
.Include(p => p.PartModifiers).ThenInclude(m => m.Modifier)
|
||||||
|
.Include(p => p.Variety)
|
||||||
|
.Include(p => p.Attribute)
|
||||||
|
.Include(p => p.Cultivation)
|
||||||
|
.Include(p => p.Origin)
|
||||||
|
.Include(p => p.Quality)
|
||||||
|
.AsSplitQuery()
|
||||||
|
.ToListAsync()).Select(d => new DeliveryJournalRow(d)), filterNames);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DeliveryJournalRow : IDelivery {
|
||||||
|
public string LsNr;
|
||||||
|
public int Pos;
|
||||||
|
public DateOnly Date;
|
||||||
|
public TimeOnly? Time;
|
||||||
|
public int MgNr;
|
||||||
|
public string Name1;
|
||||||
|
public string Name2;
|
||||||
|
public string AdministrativeName;
|
||||||
|
public string SortId;
|
||||||
|
public string Variety;
|
||||||
|
public string? AttrId;
|
||||||
|
public string? Attribute;
|
||||||
|
public string? CultId;
|
||||||
|
public string? Cultivation;
|
||||||
|
public string HkId;
|
||||||
|
public string QualId;
|
||||||
|
public string Quality;
|
||||||
|
public (double Oe, double Kmw) Gradation;
|
||||||
|
public double Kmw => Gradation.Kmw;
|
||||||
|
public double Oe => Gradation.Oe;
|
||||||
|
public int Weight { get; set; }
|
||||||
|
public bool IsNetWeight;
|
||||||
|
public string NetGross => IsNetWeight ? "n" : "b";
|
||||||
|
public string? Modifiers;
|
||||||
|
public string? Comment;
|
||||||
|
|
||||||
|
public DeliveryJournalRow(DeliveryPart p) {
|
||||||
|
var d = p.Delivery;
|
||||||
|
var m = d.Member;
|
||||||
|
|
||||||
|
LsNr = d.LsNr;
|
||||||
|
Pos = p.DPNr;
|
||||||
|
Date = d.Date;
|
||||||
|
Time = d.Time;
|
||||||
|
MgNr = m.MgNr;
|
||||||
|
Name1 = m.FamilyName;
|
||||||
|
Name2 = m.AdministrativeName2;
|
||||||
|
AdministrativeName = m.AdministrativeName;
|
||||||
|
|
||||||
|
SortId = p.SortId;
|
||||||
|
Variety = p.Variety.Name;
|
||||||
|
AttrId = p.AttrId;
|
||||||
|
Attribute = p.Attribute?.Name;
|
||||||
|
CultId = p.CultId;
|
||||||
|
Cultivation = p.Cultivation?.Name;
|
||||||
|
HkId = p.HkId;
|
||||||
|
QualId = p.QualId;
|
||||||
|
Quality = p.Quality.Name;
|
||||||
|
Gradation = (p.Oe, p.Kmw);
|
||||||
|
Weight = p.Weight;
|
||||||
|
IsNetWeight = p.IsNetWeight;
|
||||||
|
Modifiers = string.Join(" / ", p.Modifiers.Select(m => m.Name).Order());
|
||||||
|
Comment = d.Comment == null && p.Comment == null ? null : (d.Comment + (d.Comment != null && p.Comment != null ? " / " : "") + p.Comment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,8 +10,8 @@ namespace Elwig.Models.Dtos {
|
|||||||
|
|
||||||
private static readonly (string, string, string?, int)[] FieldNames = [
|
private static readonly (string, string, string?, int)[] FieldNames = [
|
||||||
("MgNr", "MgNr.", null, 12),
|
("MgNr", "MgNr.", null, 12),
|
||||||
("Name", "Name", null, 40),
|
("Name1", "Name", null, 40),
|
||||||
("GivenName", "Vorname", null, 40),
|
("Name2", "Vorname", null, 40),
|
||||||
("Address", "Adresse", null, 60),
|
("Address", "Adresse", null, 60),
|
||||||
("Plz", "PLZ", null, 10),
|
("Plz", "PLZ", null, 10),
|
||||||
("Locality", "Ort", null, 60),
|
("Locality", "Ort", null, 60),
|
||||||
@ -37,7 +37,10 @@ namespace Elwig.Models.Dtos {
|
|||||||
|
|
||||||
private static async Task<IEnumerable<MemberDeliveryPerVariantRowSingle>> FromDbSet(DbSet<MemberDeliveryPerVariantRowSingle> table, int year) {
|
private static async Task<IEnumerable<MemberDeliveryPerVariantRowSingle>> FromDbSet(DbSet<MemberDeliveryPerVariantRowSingle> table, int year) {
|
||||||
return await table.FromSqlRaw($"""
|
return await table.FromSqlRaw($"""
|
||||||
SELECT m.mgnr, m.family_name, m.given_name, p.plz, o.name AS ort, m.address,
|
SELECT m.mgnr, m.family_name AS name_1,
|
||||||
|
COALESCE(m.prefix || ' ', '') || m.given_name ||
|
||||||
|
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
|
||||||
|
p.plz, o.name AS ort, m.address,
|
||||||
v.bucket, v.weight, v.area
|
v.bucket, v.weight, v.area
|
||||||
FROM (
|
FROM (
|
||||||
SELECT c.year AS year,
|
SELECT c.year AS year,
|
||||||
@ -68,8 +71,8 @@ namespace Elwig.Models.Dtos {
|
|||||||
|
|
||||||
public class MemberDeliveryPerVariantRow {
|
public class MemberDeliveryPerVariantRow {
|
||||||
public int MgNr;
|
public int MgNr;
|
||||||
public string Name;
|
public string Name1;
|
||||||
public string GivenName;
|
public string Name2;
|
||||||
public string Address;
|
public string Address;
|
||||||
public int Plz;
|
public int Plz;
|
||||||
public string Locality;
|
public string Locality;
|
||||||
@ -82,8 +85,8 @@ namespace Elwig.Models.Dtos {
|
|||||||
public MemberDeliveryPerVariantRow(IEnumerable<MemberDeliveryPerVariantRowSingle> rows) {
|
public MemberDeliveryPerVariantRow(IEnumerable<MemberDeliveryPerVariantRowSingle> rows) {
|
||||||
var f = rows.First();
|
var f = rows.First();
|
||||||
MgNr = f.MgNr;
|
MgNr = f.MgNr;
|
||||||
Name = f.Name;
|
Name1 = f.Name1;
|
||||||
GivenName = f.GivenName;
|
Name2 = f.Name2;
|
||||||
Address = f.Address;
|
Address = f.Address;
|
||||||
Plz = f.Plz;
|
Plz = f.Plz;
|
||||||
Locality = f.Locality.Split(",")[0];
|
Locality = f.Locality.Split(",")[0];
|
||||||
@ -98,10 +101,10 @@ namespace Elwig.Models.Dtos {
|
|||||||
public class MemberDeliveryPerVariantRowSingle {
|
public class MemberDeliveryPerVariantRowSingle {
|
||||||
[Column("mgnr")]
|
[Column("mgnr")]
|
||||||
public int MgNr { get; set; }
|
public int MgNr { get; set; }
|
||||||
[Column("family_name")]
|
[Column("name_1")]
|
||||||
public required string Name { get; set; }
|
public required string Name1 { get; set; }
|
||||||
[Column("given_name")]
|
[Column("name_2")]
|
||||||
public required string GivenName { get; set; }
|
public required string Name2 { get; set; }
|
||||||
[Column("address")]
|
[Column("address")]
|
||||||
public required string Address { get; set; }
|
public required string Address { get; set; }
|
||||||
[Column("plz")]
|
[Column("plz")]
|
||||||
|
110
Elwig/Models/Dtos/MemberListData.cs
Normal file
110
Elwig/Models/Dtos/MemberListData.cs
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
using Elwig.Documents;
|
||||||
|
using Elwig.Models.Entities;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Elwig.Helpers;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Elwig.Models.Dtos {
|
||||||
|
public class MemberListData : DataTable<MemberListRow> {
|
||||||
|
|
||||||
|
private static readonly (string, string, string?, int?)[] FieldNames = [
|
||||||
|
("MgNr", "MgNr.", null, 12),
|
||||||
|
("Name1", "Name", null, 40),
|
||||||
|
("Name2", "Vorname", null, 40),
|
||||||
|
("Address", "Adresse", null, 60),
|
||||||
|
("Plz", "PLZ", null, 10),
|
||||||
|
("Locality", "Ort", null, 60),
|
||||||
|
("DefaultKg", "Stammgemeinde", null, 60),
|
||||||
|
("Branch", "Zweigstelle", null, 40),
|
||||||
|
("BusinessShares", "GA", null, 10),
|
||||||
|
("BillingName", "Rechnungsname", null, 60),
|
||||||
|
("BillingAddress", "Rechnungsadresse", null, 60),
|
||||||
|
("BillingPlz", "PLZ", null, 10),
|
||||||
|
("BillingLocality", "Ort", null, 60),
|
||||||
|
("LfbisNr", "Betr.-Nr.", null, 20),
|
||||||
|
("IsBuchführend", "buchf.", null, 15),
|
||||||
|
("IsOrganic", "Bio", null, 15),
|
||||||
|
("IsActive", "aktiv", null, 15),
|
||||||
|
("EntryDate", "Eintritt", null, 20),
|
||||||
|
("ExitDate", "Austritt", null, 20),
|
||||||
|
("AreaCommitment", "geb. Fläche", "m²", 20),
|
||||||
|
("UstIdNr", "UID", null, 25),
|
||||||
|
("Iban", "IBAN", null, 45),
|
||||||
|
("Bic", "BIC", null, 30),
|
||||||
|
("Comment", "Anmerkung", null, 60),
|
||||||
|
];
|
||||||
|
|
||||||
|
public MemberListData(IEnumerable<MemberListRow> rows, List<string> filterNames) :
|
||||||
|
base(MemberList.Name, MemberList.Name, string.Join(" / ", filterNames), rows, FieldNames) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<MemberListData> FromQuery(IQueryable<Member> query, List<string> filterNames) {
|
||||||
|
var areaCom = await query.ToDictionaryAsync(m => m.MgNr, m => Utils.ActiveAreaCommitments(m.AreaCommitments).Sum(c => c.Area));
|
||||||
|
return new((await query
|
||||||
|
.Include(m => m.DefaultWbKg!.AtKg)
|
||||||
|
.Include(m => m.Branch)
|
||||||
|
.Include(m => m.PostalDest.AtPlz!.Ort)
|
||||||
|
.Include(m => m.BillingAddress!.PostalDest.AtPlz!.Ort)
|
||||||
|
.ToListAsync()).Select(m => new MemberListRow(m, areaCom[m.MgNr])), filterNames);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MemberListRow {
|
||||||
|
public int MgNr;
|
||||||
|
public string? Name1;
|
||||||
|
public string? Name2;
|
||||||
|
public string? DefaultKg;
|
||||||
|
public string? Branch;
|
||||||
|
public int BusinessShares;
|
||||||
|
public string Address;
|
||||||
|
public int Plz;
|
||||||
|
public string Locality;
|
||||||
|
public string? BillingName;
|
||||||
|
public string? BillingAddress;
|
||||||
|
public int? BillingPlz;
|
||||||
|
public string? BillingLocality;
|
||||||
|
public string? LfbisNr;
|
||||||
|
public string? UstIdNr;
|
||||||
|
public string? Iban;
|
||||||
|
public string? Bic;
|
||||||
|
public int? AreaCommitment;
|
||||||
|
public bool IsBuchführend;
|
||||||
|
public bool IsOrganic;
|
||||||
|
public bool IsActive;
|
||||||
|
public DateOnly? EntryDate;
|
||||||
|
public DateOnly? ExitDate;
|
||||||
|
public string? Comment;
|
||||||
|
|
||||||
|
public MemberListRow(Member m, int? areaCom = null) {
|
||||||
|
MgNr = m.MgNr;
|
||||||
|
Name1 = m.FamilyName;
|
||||||
|
Name2 = m.AdministrativeName2;
|
||||||
|
DefaultKg = m.DefaultKg?.Name;
|
||||||
|
Branch = m.Branch?.Name;
|
||||||
|
BusinessShares = m.BusinessShares;
|
||||||
|
Address = m.Address;
|
||||||
|
Plz = m.PostalDest.AtPlz!.Plz;
|
||||||
|
Locality = m.PostalDest.AtPlz!.Ort.Name;
|
||||||
|
if (m.BillingAddress is BillingAddr a) {
|
||||||
|
BillingName = a.Name;
|
||||||
|
BillingAddress = a.Address;
|
||||||
|
BillingPlz = a.PostalDest.AtPlz!.Plz;
|
||||||
|
BillingLocality = a.PostalDest.AtPlz!.Ort.Name;
|
||||||
|
}
|
||||||
|
LfbisNr = m.LfbisNr;
|
||||||
|
UstIdNr = m.UstIdNr;
|
||||||
|
Iban = m.Iban != null ? Utils.FormatIban(m.Iban) : null;
|
||||||
|
Bic = m.Bic;
|
||||||
|
IsBuchführend = m.IsBuchführend;
|
||||||
|
IsOrganic = m.IsOrganic;
|
||||||
|
IsActive = m.IsActive;
|
||||||
|
EntryDate = m.EntryDate;
|
||||||
|
ExitDate = m.ExitDate;
|
||||||
|
Comment = m.Comment;
|
||||||
|
AreaCommitment = areaCom == 0 ? null : areaCom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -9,8 +8,8 @@ namespace Elwig.Models.Dtos {
|
|||||||
|
|
||||||
private static readonly (string, string, string?, int)[] FieldNames = [
|
private static readonly (string, string, string?, int)[] FieldNames = [
|
||||||
("MgNr", "MgNr.", null, 12),
|
("MgNr", "MgNr.", null, 12),
|
||||||
("Name", "Name", null, 40),
|
("Name1", "Name", null, 40),
|
||||||
("GivenName", "Vorname", null, 40),
|
("Name2", "Vorname", null, 40),
|
||||||
("Address", "Adresse", null, 60),
|
("Address", "Adresse", null, 60),
|
||||||
("Plz", "PLZ", null, 10),
|
("Plz", "PLZ", null, 10),
|
||||||
("Locality", "Ort", null, 60),
|
("Locality", "Ort", null, 60),
|
||||||
@ -27,7 +26,10 @@ namespace Elwig.Models.Dtos {
|
|||||||
|
|
||||||
public static async Task<OverUnderDeliveryData> ForSeason(DbSet<OverUnderDeliveryRow> table, int year) {
|
public static async Task<OverUnderDeliveryData> ForSeason(DbSet<OverUnderDeliveryRow> table, int year) {
|
||||||
var rows = await table.FromSqlRaw($"""
|
var rows = await table.FromSqlRaw($"""
|
||||||
SELECT m.mgnr, m.family_name, m.given_name, p.plz, o.name AS ort, m.address, m.business_shares,
|
SELECT m.mgnr, m.family_name AS name_1,
|
||||||
|
COALESCE(m.prefix || ' ', '') || m.given_name ||
|
||||||
|
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
|
||||||
|
p.plz, o.name AS ort, m.address, m.business_shares,
|
||||||
m.business_shares * s.min_kg_per_bs AS min_kg,
|
m.business_shares * s.min_kg_per_bs AS min_kg,
|
||||||
m.business_shares * s.max_kg_per_bs AS max_kg,
|
m.business_shares * s.max_kg_per_bs AS max_kg,
|
||||||
COALESCE(SUM(d.weight), 0) AS sum
|
COALESCE(SUM(d.weight), 0) AS sum
|
||||||
@ -48,10 +50,10 @@ namespace Elwig.Models.Dtos {
|
|||||||
public class OverUnderDeliveryRow {
|
public class OverUnderDeliveryRow {
|
||||||
[Column("mgnr")]
|
[Column("mgnr")]
|
||||||
public int MgNr { get; set; }
|
public int MgNr { get; set; }
|
||||||
[Column("family_name")]
|
[Column("name_1")]
|
||||||
public required string Name { get; set; }
|
public required string Name1 { get; set; }
|
||||||
[Column("given_name")]
|
[Column("name_2")]
|
||||||
public required string GivenName { get; set; }
|
public required string Name2 { get; set; }
|
||||||
[Column("address")]
|
[Column("address")]
|
||||||
public required string Address { get; set; }
|
public required string Address { get; set; }
|
||||||
[Column("plz")]
|
[Column("plz")]
|
||||||
|
104
Elwig/Models/Dtos/PaymentVariantSummaryData.cs
Normal file
104
Elwig/Models/Dtos/PaymentVariantSummaryData.cs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
using Elwig.Documents;
|
||||||
|
using Elwig.Helpers;
|
||||||
|
using Elwig.Models.Entities;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Elwig.Models.Dtos {
|
||||||
|
public class PaymentVariantSummaryData : DataTable<PaymentVariantSummaryData.PaymentRow> {
|
||||||
|
|
||||||
|
private static readonly (string, string, string?, int?)[] FieldNames = [
|
||||||
|
("Type", "Typ", null, 10),
|
||||||
|
("Variety", "Sorte", null, 40),
|
||||||
|
("Attribute", "Attribut", null, 20),
|
||||||
|
("Cultivation", "Bewirt.", null, 20),
|
||||||
|
("QualityLevel", "Qualitätsstufe", null, 30),
|
||||||
|
("Oe", "Gradation", "°Oe", 20),
|
||||||
|
("Ungeb", "ungebunden", "kg|€/kg", 40),
|
||||||
|
("Geb", "gebunden", "kg|€/kg", 40),
|
||||||
|
("Amount", "Gesamt", "€", 25),
|
||||||
|
];
|
||||||
|
|
||||||
|
public record struct PaymentRow(
|
||||||
|
string Type,
|
||||||
|
string Variety,
|
||||||
|
string? Attribute,
|
||||||
|
string? Cultivation,
|
||||||
|
string QualityLevel,
|
||||||
|
double Oe,
|
||||||
|
(int Weight, decimal? Price) Ungeb,
|
||||||
|
(int Weight, decimal? Price) Geb,
|
||||||
|
decimal Amount
|
||||||
|
);
|
||||||
|
|
||||||
|
public PaymentVariantSummaryData(PaymentVar v, IEnumerable<PaymentRow> rows) :
|
||||||
|
base($"{PaymentVariantSummary.Name} {v.Year}", $"{PaymentVariantSummary.Name} Lese {v.Year}", v.Name, rows, FieldNames) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<PaymentVariantSummaryData> ForPaymentVariant(PaymentVar v, DbSet<PaymentVariantSummaryRow> table) {
|
||||||
|
return new(v, (await FromDbSet(table, v.Year, v.AvNr))
|
||||||
|
.Select(r => new PaymentRow(r.Type, r.Variety, r.Attribute, r.Cultivation, r.QualityLevel, r.Oe,
|
||||||
|
(r.WeightUngeb, r.PriceUngeb != null ? Utils.DecFromDb(r.PriceUngeb.Value, v.Season.Precision) : null),
|
||||||
|
(r.WeightGeb, r.PriceGeb != null ? Utils.DecFromDb(r.PriceGeb.Value, v.Season.Precision) : null),
|
||||||
|
Utils.DecFromDb(r.Amount, v.Season.Precision)))
|
||||||
|
.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IEnumerable<PaymentVariantSummaryRow>> FromDbSet(DbSet<PaymentVariantSummaryRow> table, int year, int avnr) {
|
||||||
|
return await table.FromSqlRaw($"""
|
||||||
|
SELECT v.type AS type,
|
||||||
|
v.name AS variety,
|
||||||
|
a.name AS attribute,
|
||||||
|
c.name AS cultivation,
|
||||||
|
q.name AS quality_level,
|
||||||
|
ROUND(kmw * (4.54 + 0.022 * kmw)) AS oe,
|
||||||
|
SUM(IIF(w.discr = '_', w.value, 0)) AS weight_ungeb,
|
||||||
|
MAX(IIF(w.discr = '_', b.price, NULL)) AS price_ungeb,
|
||||||
|
SUM(IIF(w.discr != '_', w.value, 0)) AS weight_geb,
|
||||||
|
MAX(IIF(w.discr != '_', b.price, NULL)) AS price_geb,
|
||||||
|
SUM(b.amount) AS amount
|
||||||
|
FROM payment_delivery_part_bucket b
|
||||||
|
LEFT JOIN delivery_part_bucket w ON (w.year, w.did, w.dpnr, w.bktnr) = (b.year, b.did, b.dpnr, b.bktnr)
|
||||||
|
LEFT JOIN delivery_part p ON (p.year, p.did, p.dpnr) = (b.year, b.did, b.dpnr)
|
||||||
|
LEFT JOIN delivery d ON (d.year, d.did) = (p.year, p.did)
|
||||||
|
LEFT JOIN wine_variety v ON v.sortid = p.sortid
|
||||||
|
LEFT JOIN wine_attribute a ON a.attrid = p.attrid
|
||||||
|
LEFT JOIN wine_cultivation c ON c.cultid = p.cultid
|
||||||
|
LEFT JOIN wine_quality_level q ON q.qualid = p.qualid
|
||||||
|
WHERE d.year = {year} AND b.avnr = {avnr}
|
||||||
|
GROUP BY variety, attribute, cultivation, q.min_kmw, oe
|
||||||
|
ORDER BY variety, attribute, cultivation, q.min_kmw, oe
|
||||||
|
""").ToListAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Keyless]
|
||||||
|
public class PaymentVariantSummaryRow {
|
||||||
|
[Column("type")]
|
||||||
|
public required string Type { get; set; }
|
||||||
|
[Column("variety")]
|
||||||
|
public required string Variety { get; set; }
|
||||||
|
[Column("attribute")]
|
||||||
|
public string? Attribute { get; set; }
|
||||||
|
[Column("cultivation")]
|
||||||
|
public string? Cultivation { get; set; }
|
||||||
|
[Column("quality_level")]
|
||||||
|
public required string QualityLevel { get; set; }
|
||||||
|
[Column("oe")]
|
||||||
|
public double Oe { get; set; }
|
||||||
|
[Column("weight_ungeb")]
|
||||||
|
public int WeightUngeb { get; set; }
|
||||||
|
[Column("price_ungeb")]
|
||||||
|
public long? PriceUngeb { get; set; }
|
||||||
|
[Column("weight_geb")]
|
||||||
|
public int WeightGeb { get; set; }
|
||||||
|
[Column("price_geb")]
|
||||||
|
public long? PriceGeb { get; set; }
|
||||||
|
[Column("amount")]
|
||||||
|
public long Amount { get; set; }
|
||||||
|
}
|
||||||
|
}
|
58
Elwig/Models/Dtos/WeightBreakdownData.cs
Normal file
58
Elwig/Models/Dtos/WeightBreakdownData.cs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
using Elwig.Models.Entities;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Elwig.Models.Dtos {
|
||||||
|
public class WeightBreakdownData : DataTable<WeightBreakdownRow> {
|
||||||
|
|
||||||
|
private static readonly (string, string, string?, int?)[] FieldNames = [
|
||||||
|
("Type", "R/W", null, 10),
|
||||||
|
("SortId", "Sorte", null, 10),
|
||||||
|
("AttrId", "Attr.", null, 10),
|
||||||
|
("CultId", "Bewirt.", null, 15),
|
||||||
|
("QualId", "Qual.", null, 15),
|
||||||
|
("Geb", "gebunden", null, 20),
|
||||||
|
("Weight", "Gewicht", "kg", 20),
|
||||||
|
];
|
||||||
|
|
||||||
|
public WeightBreakdownData(IEnumerable<WeightBreakdownRow> rows, int year, string name) :
|
||||||
|
base(name, $"Sorten-/Qualitätsaufschlüsselung {year}", name, rows, FieldNames) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<WeightBreakdownData> ForSeason(DbSet<WeightBreakdownRow> table, int year, Branch? branch = null) {
|
||||||
|
return new(await FromDbSet(table, year, branch?.ZwstId), year, branch?.Name ?? "Gesamt");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IEnumerable<WeightBreakdownRow>> FromDbSet(DbSet<WeightBreakdownRow> table, int year, string? zwstid) {
|
||||||
|
zwstid = zwstid == null ? "NULL" : $"'{zwstid}'";
|
||||||
|
return await table.FromSqlRaw($"""
|
||||||
|
SELECT type, sortid, attrid, cultid, v.qualid, geb, SUM(weight) AS weight
|
||||||
|
FROM v_stat_total v
|
||||||
|
LEFT JOIN wine_quality_level q ON q.qualid = v.qualid
|
||||||
|
WHERE year = {year} AND ({zwstid} IS NULL OR zwstid = {zwstid})
|
||||||
|
GROUP BY type, sortid, attrid, cultid, v.qualid, geb
|
||||||
|
ORDER BY type DESC, sortid, attrid, cultid, q.min_kmw, geb
|
||||||
|
""").ToListAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Keyless]
|
||||||
|
public class WeightBreakdownRow {
|
||||||
|
[Column("type")]
|
||||||
|
public required string Type { get; set; }
|
||||||
|
[Column("sortid")]
|
||||||
|
public required string SortId { get; set; }
|
||||||
|
[Column("attrid")]
|
||||||
|
public string? AttrId { get; set; }
|
||||||
|
[Column("cultid")]
|
||||||
|
public string? CultId { get; set; }
|
||||||
|
[Column("qualid")]
|
||||||
|
public required string QualId { get; set; }
|
||||||
|
[Column("geb")]
|
||||||
|
public required string Geb { get; set; }
|
||||||
|
[Column("weight")]
|
||||||
|
public int Weight { get; set; }
|
||||||
|
}
|
||||||
|
}
|
128
Elwig/Models/Dtos/WineQualityStatisticsData.cs
Normal file
128
Elwig/Models/Dtos/WineQualityStatisticsData.cs
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
using Elwig.Models.Entities;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Elwig.Models.Dtos {
|
||||||
|
public class WineQualityStatisticsData {
|
||||||
|
|
||||||
|
public record struct QualityRow(string? Variety, string? Attribute, string? Cultivation, string? Type, string QualId, double AvgKmw, double Grad, int Num, int Weight);
|
||||||
|
public record struct QualitySection(string Name, string? Type, Dictionary<string, (double Grad, double AvgKmw, int Num, int Weight)[]> Data);
|
||||||
|
|
||||||
|
public bool UseOe = true;
|
||||||
|
public QualitySection[] Sections;
|
||||||
|
|
||||||
|
public WineQualityStatisticsData(QualitySection[] sections) {
|
||||||
|
Sections = sections;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static QualitySection[] GetSections(IEnumerable<QualityRow> rows) {
|
||||||
|
var data = new List<QualitySection>();
|
||||||
|
var currentQual = new Dictionary<double, (double AvgKmw, int Num, int Weight)>();
|
||||||
|
var current = new Dictionary<string, (double, double, int, int)[]>();
|
||||||
|
string? lastSection = null;
|
||||||
|
string? lastType = null;
|
||||||
|
string? lastQual = null;
|
||||||
|
foreach (var row in rows) {
|
||||||
|
var sec = $"{row.Variety ?? (row.Type == "R" ? "Rotweinsorten" : row.Type == "W" ? "Weißweinsorten" : "Gesamt")}" +
|
||||||
|
$"{(row.Attribute != null ? " / " : "")}{row.Attribute}" +
|
||||||
|
$"{(row.Cultivation != null ? " / " : "")}{row.Cultivation}";
|
||||||
|
if (lastQual != null && lastQual != row.QualId) {
|
||||||
|
current[lastQual] = currentQual.Select(kv => (kv.Key, kv.Value.AvgKmw, kv.Value.Num, kv.Value.Weight)).ToArray();
|
||||||
|
currentQual.Clear();
|
||||||
|
}
|
||||||
|
if (lastSection != null && lastSection != sec) {
|
||||||
|
if (!current.ContainsKey(lastQual!)) {
|
||||||
|
current[lastQual!] = currentQual.Select(kv => (kv.Key, kv.Value.AvgKmw, kv.Value.Num, kv.Value.Weight)).ToArray();
|
||||||
|
currentQual.Clear();
|
||||||
|
}
|
||||||
|
data.Add(new(lastSection, lastType, current));
|
||||||
|
current = [];
|
||||||
|
currentQual.Clear();
|
||||||
|
}
|
||||||
|
currentQual[row.Grad] = (row.AvgKmw, row.Num, row.Weight);
|
||||||
|
lastSection = sec;
|
||||||
|
lastType = row.Type;
|
||||||
|
lastQual = row.QualId;
|
||||||
|
}
|
||||||
|
if (lastQual != null) {
|
||||||
|
current[lastQual] = currentQual.Select(kv => (kv.Key, kv.Value.AvgKmw, kv.Value.Num, kv.Value.Weight)).ToArray();
|
||||||
|
currentQual.Clear();
|
||||||
|
}
|
||||||
|
if (lastSection != null) {
|
||||||
|
data.Add(new(lastSection, lastType, current));
|
||||||
|
current = [];
|
||||||
|
currentQual.Clear();
|
||||||
|
}
|
||||||
|
return [.. data];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<WineQualityStatisticsData> FromQuery(IQueryable<DeliveryPart> query, int mode = 0) {
|
||||||
|
var rows = (await query
|
||||||
|
.GroupBy(p => new {
|
||||||
|
p.Variety.Type,
|
||||||
|
Variety = p.Variety.Name,
|
||||||
|
Attribute = p.Attribute!.Name,
|
||||||
|
Cultivation = p.Cultivation!.Name,
|
||||||
|
p.QualId,
|
||||||
|
Grad = mode == 0 ? Math.Round(p.Kmw * (4.54 + 0.022 * p.Kmw), 0) :
|
||||||
|
mode == 1 ? Math.Floor(p.Kmw) :
|
||||||
|
mode == 2 ? Math.Floor(p.Kmw * 2) / 2 :
|
||||||
|
mode == 3 ? Math.Floor(p.Kmw * 5) / 5 :
|
||||||
|
Math.Round(p.Kmw, 1),
|
||||||
|
}, (k, g) => new {
|
||||||
|
Key = k,
|
||||||
|
Num = g.Count(),
|
||||||
|
Weight = g.Sum(p => p.Weight),
|
||||||
|
AvgKmw = g.Sum(p => p.Weight * p.Kmw) / g.Sum(p => p.Weight),
|
||||||
|
})
|
||||||
|
.OrderBy(g => g.Key.Variety)
|
||||||
|
.ThenBy(g => g.Key.Attribute)
|
||||||
|
.ThenBy(g => g.Key.Cultivation)
|
||||||
|
.ThenBy(g => g.Key.QualId)
|
||||||
|
.ThenBy(g => g.Key.Grad)
|
||||||
|
.ToListAsync())
|
||||||
|
.Select(r => new QualityRow(r.Key.Variety, r.Key.Attribute, r.Key.Cultivation, r.Key.Type, r.Key.QualId, r.AvgKmw, r.Key.Grad, r.Num, r.Weight))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var data = GetSections(rows);
|
||||||
|
if (data.Length <= 1)
|
||||||
|
return new(data);
|
||||||
|
|
||||||
|
var typeRows = rows
|
||||||
|
.GroupBy(s => new { s.Type, s.QualId, s.Grad }, (k, g) => new QualityRow(
|
||||||
|
null, null, null,
|
||||||
|
k.Type, k.QualId,
|
||||||
|
g.Sum(p => p.Weight * p.AvgKmw) / g.Sum(p => p.Weight),
|
||||||
|
k.Grad,
|
||||||
|
g.Sum(p => p.Num),
|
||||||
|
g.Sum(p => p.Weight)
|
||||||
|
))
|
||||||
|
.OrderBy(g => g.Type)
|
||||||
|
.ThenBy(g => g.QualId)
|
||||||
|
.ThenBy(g => g.Grad)
|
||||||
|
.ToList();
|
||||||
|
var typeData = GetSections(typeRows);
|
||||||
|
if (typeData.Length <= 1)
|
||||||
|
return new([.. typeData, .. data]);
|
||||||
|
|
||||||
|
var totalRows = rows
|
||||||
|
.GroupBy(s => new { s.QualId, s.Grad }, (k, g) => new QualityRow(
|
||||||
|
null, null, null, null,
|
||||||
|
k.QualId,
|
||||||
|
g.Sum(p => p.Weight * p.AvgKmw) / g.Sum(p => p.Weight),
|
||||||
|
k.Grad,
|
||||||
|
g.Sum(p => p.Num),
|
||||||
|
g.Sum(p => p.Weight)
|
||||||
|
))
|
||||||
|
.OrderBy(g => g.QualId)
|
||||||
|
.ThenBy(g => g.Grad)
|
||||||
|
.ToList();
|
||||||
|
var totalData = GetSections(totalRows);
|
||||||
|
return new([.. totalData, .. typeData, .. data]) { UseOe = mode == 0 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,10 +11,10 @@ namespace Elwig.Models.Entities {
|
|||||||
[Column("name")]
|
[Column("name")]
|
||||||
public string Name { get; private set; } = null!;
|
public string Name { get; private set; } = null!;
|
||||||
|
|
||||||
[InverseProperty("Gem")]
|
[InverseProperty(nameof(AT_Kg.Gem))]
|
||||||
public virtual ISet<AT_Kg> Kgs { get; private set; } = null!;
|
public virtual ICollection<AT_Kg> Kgs { get; private set; } = null!;
|
||||||
|
|
||||||
[InverseProperty("AtGem")]
|
[InverseProperty(nameof(WbGem.AtGem))]
|
||||||
public virtual WbGem? WbGem { get; private set; }
|
public virtual WbGem? WbGem { get; private set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ namespace Elwig.Models.Entities {
|
|||||||
[ForeignKey("Gkz")]
|
[ForeignKey("Gkz")]
|
||||||
public virtual AT_Gem Gem { get; private set; } = null!;
|
public virtual AT_Gem Gem { get; private set; } = null!;
|
||||||
|
|
||||||
[InverseProperty("AtKg")]
|
[InverseProperty(nameof(WbKg.AtKg))]
|
||||||
public virtual WbKg? WbKg { get; private set; }
|
public virtual WbKg? WbKg { get; private set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ namespace Elwig.Models.Entities {
|
|||||||
[Column("po_box")]
|
[Column("po_box")]
|
||||||
public bool IsPoBox { get; private set; }
|
public bool IsPoBox { get; private set; }
|
||||||
|
|
||||||
[InverseProperty("AtPlz")]
|
[InverseProperty(nameof(AT_PlzDest.AtPlz))]
|
||||||
public virtual ISet<AT_PlzDest> Orte { get; private set; } = null!;
|
public virtual ICollection<AT_PlzDest> Orte { get; private set; } = null!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using Elwig.Helpers;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ namespace Elwig.Models.Entities {
|
|||||||
[Column("mobile_nr")]
|
[Column("mobile_nr")]
|
||||||
public string? MobileNr { get; set; }
|
public string? MobileNr { get; set; }
|
||||||
|
|
||||||
[InverseProperty("Branch")]
|
[InverseProperty(nameof(Member.Branch))]
|
||||||
public virtual ISet<Member> Members { get; private set; } = null!;
|
public virtual ICollection<Member> Members { get; private set; } = null!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,8 +66,8 @@ namespace Elwig.Models.Entities {
|
|||||||
[ForeignKey("Year")]
|
[ForeignKey("Year")]
|
||||||
public virtual Season Season { get; private set; } = null!;
|
public virtual Season Season { get; private set; } = null!;
|
||||||
|
|
||||||
[InverseProperty("Delivery")]
|
[InverseProperty(nameof(DeliveryPart.Delivery))]
|
||||||
public virtual ISet<DeliveryPart> Parts { get; private set; } = null!;
|
public virtual ICollection<DeliveryPart> Parts { get; private set; } = null!;
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public IEnumerable<DeliveryPart> FilteredParts => PartFilter == null ? Parts : Parts.Where(p => PartFilter(p));
|
public IEnumerable<DeliveryPart> FilteredParts => PartFilter == null ? Parts : Parts.Where(p => PartFilter(p));
|
||||||
|
|
||||||
@ -85,10 +85,22 @@ namespace Elwig.Models.Entities {
|
|||||||
.GroupBy(p => p.SortId)
|
.GroupBy(p => p.SortId)
|
||||||
.OrderByDescending(g => g.Select(p => p.Weight).Sum())
|
.OrderByDescending(g => g.Select(p => p.Weight).Sum())
|
||||||
.Select(g => g.Key);
|
.Select(g => g.Key);
|
||||||
|
|
||||||
public string SortIdString => string.Join(", ", SortIds);
|
public string SortIdString => string.Join(", ", SortIds);
|
||||||
public string FilteredSortIdString => string.Join(", ", FilteredSortIds);
|
public string FilteredSortIdString => string.Join(", ", FilteredSortIds);
|
||||||
|
|
||||||
|
public IEnumerable<string> Modifiers => Parts
|
||||||
|
.SelectMany(p => p.Modifiers)
|
||||||
|
.Select(m => m.Name)
|
||||||
|
.Distinct()
|
||||||
|
.Order();
|
||||||
|
public IEnumerable<string> FilteredModifiers => FilteredParts
|
||||||
|
.SelectMany(p => p.Modifiers)
|
||||||
|
.Select(m => m.Name)
|
||||||
|
.Distinct()
|
||||||
|
.Order();
|
||||||
|
public string ModifiersString => string.Join(" / ", Modifiers);
|
||||||
|
public string FilteredModifiersString => string.Join(" / ", FilteredModifiers);
|
||||||
|
|
||||||
public double Kmw => Utils.AggregateDeliveryPartsKmw(Parts);
|
public double Kmw => Utils.AggregateDeliveryPartsKmw(Parts);
|
||||||
public double FilteredKmw => Utils.AggregateDeliveryPartsKmw(FilteredParts);
|
public double FilteredKmw => Utils.AggregateDeliveryPartsKmw(FilteredParts);
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ using System.Linq;
|
|||||||
|
|
||||||
namespace Elwig.Models.Entities {
|
namespace Elwig.Models.Entities {
|
||||||
[Table("delivery_part"), PrimaryKey("Year", "DId", "DPNr")]
|
[Table("delivery_part"), PrimaryKey("Year", "DId", "DPNr")]
|
||||||
public class DeliveryPart {
|
public class DeliveryPart : IDelivery {
|
||||||
[Column("year")]
|
[Column("year")]
|
||||||
public int Year { get; set; }
|
public int Year { get; set; }
|
||||||
|
|
||||||
@ -108,19 +108,19 @@ namespace Elwig.Models.Entities {
|
|||||||
[Column("comment")]
|
[Column("comment")]
|
||||||
public string? Comment { get; set; }
|
public string? Comment { get; set; }
|
||||||
|
|
||||||
[InverseProperty("Part")]
|
[InverseProperty(nameof(DeliveryPartModifier.Part))]
|
||||||
public virtual ISet<DeliveryPartModifier> PartModifiers { get; private set; } = null!;
|
public virtual ICollection<DeliveryPartModifier> PartModifiers { get; private set; } = null!;
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public IEnumerable<Modifier> Modifiers => PartModifiers.Select(m => m.Modifier).OrderBy(m => m.Ordering);
|
public IEnumerable<Modifier> Modifiers => PartModifiers.Select(m => m.Modifier).OrderBy(m => m.Ordering);
|
||||||
|
|
||||||
[InverseProperty("DeliveryPart")]
|
[InverseProperty(nameof(PaymentDeliveryPart.DeliveryPart))]
|
||||||
public virtual PaymentDeliveryPart? Payment { get; private set; }
|
public virtual PaymentDeliveryPart? Payment { get; private set; }
|
||||||
|
|
||||||
[NotMapped]
|
[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}" : "");
|
public string OriginString => Origin.OriginString + "\n" + (Kg?.Gl != null ? $" / {Kg.Gl.Name}" : "") + (Kg != null ? $" / {Kg.AtKg.Gem.Name} / KG {Kg.AtKg.Name}" : "") + (Rd != null ? $" / Ried {Rd.Name}" : "");
|
||||||
|
|
||||||
[InverseProperty("Part")]
|
[InverseProperty(nameof(DeliveryPartBucket.Part))]
|
||||||
public virtual ISet<DeliveryPartBucket> Buckets { get; private set; } = null!;
|
public virtual ICollection<DeliveryPartBucket> Buckets { get; private set; } = null!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,24 +153,24 @@ namespace Elwig.Models.Entities {
|
|||||||
[ForeignKey("ZwstId")]
|
[ForeignKey("ZwstId")]
|
||||||
public virtual Branch? Branch { get; private set; }
|
public virtual Branch? Branch { get; private set; }
|
||||||
|
|
||||||
[InverseProperty("Member")]
|
[InverseProperty(nameof(AreaCom.Member))]
|
||||||
public virtual ISet<AreaCom> AreaCommitments { get; private set; } = null!;
|
public virtual ICollection<AreaCom> AreaCommitments { get; private set; } = null!;
|
||||||
|
|
||||||
[NotMapped]
|
public IQueryable<AreaCom> ActiveAreaCommitments(AppDbContext ctx) {
|
||||||
public IEnumerable<AreaCom> ActiveAreaCommitments => AreaCommitments
|
return ctx.AreaCommitments.Where(c => c.MgNr == MgNr).Where(Utils.ActiveAreaCommitments());
|
||||||
.Where(c => c.YearFrom <= Utils.CurrentYear && (c.YearTo ?? int.MaxValue) >= Utils.CurrentYear);
|
}
|
||||||
|
|
||||||
[InverseProperty("Member")]
|
[InverseProperty(nameof(BillingAddr.Member))]
|
||||||
public virtual BillingAddr? BillingAddress { get; private set; }
|
public virtual BillingAddr? BillingAddress { get; private set; }
|
||||||
|
|
||||||
[InverseProperty("Member")]
|
[InverseProperty(nameof(Delivery.Member))]
|
||||||
public virtual ISet<Delivery> Deliveries { get; private set; } = null!;
|
public virtual ICollection<Delivery> Deliveries { get; private set; } = null!;
|
||||||
|
|
||||||
[InverseProperty("Member")]
|
[InverseProperty(nameof(MemberTelNr.Member))]
|
||||||
public virtual ISet<MemberTelNr> TelephoneNumbers { get; private set; } = null!;
|
public virtual ICollection<MemberTelNr> TelephoneNumbers { get; private set; } = null!;
|
||||||
|
|
||||||
[InverseProperty("member")]
|
[InverseProperty(nameof(MemberEmailAddr.Member))]
|
||||||
public virtual ISet<MemberEmailAddr> EmailAddresses { get; private set; } = null!;
|
public virtual ICollection<MemberEmailAddr> EmailAddresses { get; private set; } = null!;
|
||||||
|
|
||||||
public string FullAddress => $"{Address}, {PostalDest.AtPlz?.Plz} {PostalDest.AtPlz?.Ort.Name}";
|
public string FullAddress => $"{Address}, {PostalDest.AtPlz?.Plz} {PostalDest.AtPlz?.Ort.Name}";
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ namespace Elwig.Models.Entities {
|
|||||||
[ForeignKey("MgNr")]
|
[ForeignKey("MgNr")]
|
||||||
public virtual Member Member { get; private set; } = null!;
|
public virtual Member Member { get; private set; } = null!;
|
||||||
|
|
||||||
[InverseProperty("Payment")]
|
[InverseProperty(nameof(Credit.Payment))]
|
||||||
public virtual Credit? Credit { get; private set; }
|
public virtual Credit? Credit { get; private set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,13 +48,13 @@ namespace Elwig.Models.Entities {
|
|||||||
[ForeignKey("Year")]
|
[ForeignKey("Year")]
|
||||||
public virtual Season Season { get; private set; } = null!;
|
public virtual Season Season { get; private set; } = null!;
|
||||||
|
|
||||||
[InverseProperty("Variant")]
|
[InverseProperty(nameof(PaymentMember.Variant))]
|
||||||
public virtual ISet<PaymentMember> MemberPayments { get; private set; } = null!;
|
public virtual ICollection<PaymentMember> MemberPayments { get; private set; } = null!;
|
||||||
|
|
||||||
[InverseProperty("Variant")]
|
[InverseProperty(nameof(PaymentDeliveryPart.Variant))]
|
||||||
public virtual ISet<PaymentDeliveryPart> DeliveryPartPayments { get; private set; } = null!;
|
public virtual ICollection<PaymentDeliveryPart> DeliveryPartPayments { get; private set; } = null!;
|
||||||
|
|
||||||
[InverseProperty("Variant")]
|
[InverseProperty(nameof(Credit.Variant))]
|
||||||
public virtual ISet<Credit> Credits { get; private set; } = null!;
|
public virtual ICollection<Credit> Credits { get; private set; } = null!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,14 +100,14 @@ namespace Elwig.Models.Entities {
|
|||||||
[ForeignKey("CurrencyCode")]
|
[ForeignKey("CurrencyCode")]
|
||||||
public virtual Currency Currency { get; private set; } = null!;
|
public virtual Currency Currency { get; private set; } = null!;
|
||||||
|
|
||||||
[InverseProperty("Season")]
|
[InverseProperty(nameof(Modifier.Season))]
|
||||||
public virtual ISet<Modifier> Modifiers { get; private set; } = null!;
|
public virtual ICollection<Modifier> Modifiers { get; private set; } = null!;
|
||||||
|
|
||||||
[InverseProperty("Season")]
|
[InverseProperty(nameof(PaymentVar.Season))]
|
||||||
public virtual ISet<PaymentVar> PaymentVariants { get; private set; } = null!;
|
public virtual ICollection<PaymentVar> PaymentVariants { get; private set; } = null!;
|
||||||
|
|
||||||
[InverseProperty("Season")]
|
[InverseProperty(nameof(Delivery.Season))]
|
||||||
public virtual ISet<Delivery> Deliveries { get; private set; } = null!;
|
public virtual ICollection<Delivery> Deliveries { get; private set; } = null!;
|
||||||
|
|
||||||
public decimal DecFromDb(long value) {
|
public decimal DecFromDb(long value) {
|
||||||
return Utils.DecFromDb(value, Precision);
|
return Utils.DecFromDb(value, Precision);
|
||||||
|
@ -11,7 +11,7 @@ namespace Elwig.Models.Entities {
|
|||||||
[Column("name")]
|
[Column("name")]
|
||||||
public string Name { get; private set; } = null!;
|
public string Name { get; private set; } = null!;
|
||||||
|
|
||||||
[InverseProperty("Gl")]
|
[InverseProperty(nameof(WbKg.Gl))]
|
||||||
public virtual ISet<WbKg> Kgs { get; private set; } = null!;
|
public virtual ICollection<WbKg> Kgs { get; private set; } = null!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,11 +17,11 @@ namespace Elwig.Models.Entities {
|
|||||||
[ForeignKey("GlNr")]
|
[ForeignKey("GlNr")]
|
||||||
public virtual WbGl Gl { get; private set; } = null!;
|
public virtual WbGl Gl { get; private set; } = null!;
|
||||||
|
|
||||||
[InverseProperty("Kg")]
|
[InverseProperty(nameof(WbRd.Kg))]
|
||||||
public virtual ISet<WbRd> Rds { get; private set; } = null!;
|
public virtual ICollection<WbRd> Rds { get; private set; } = null!;
|
||||||
|
|
||||||
[InverseProperty("DefaultWbKg")]
|
[InverseProperty(nameof(Member.DefaultWbKg))]
|
||||||
public virtual ISet<Member> Members { get; private set; } = null!;
|
public virtual ICollection<Member> Members { get; private set; } = null!;
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public WbGem Gem => AtKg.Gem.WbGem!;
|
public WbGem Gem => AtKg.Gem.WbGem!;
|
||||||
|
@ -23,11 +23,11 @@ namespace Elwig.Models.Entities {
|
|||||||
[Column("blnr")]
|
[Column("blnr")]
|
||||||
public int? BlNr { get; private set; }
|
public int? BlNr { get; private set; }
|
||||||
|
|
||||||
[InverseProperty("Origin")]
|
[InverseProperty(nameof(WbGem.Origin))]
|
||||||
public virtual ISet<WbGem> Gems { get; private set; } = null!;
|
public virtual ICollection<WbGem> Gems { get; private set; } = null!;
|
||||||
|
|
||||||
[InverseProperty("Parent")]
|
[InverseProperty(nameof(Parent))]
|
||||||
public virtual ISet<WineOrigin> Children { get; private set; } = null!;
|
public virtual ICollection<WineOrigin> Children { get; private set; } = null!;
|
||||||
|
|
||||||
public int Level => (Parent?.Level + 1) ?? 0;
|
public int Level => (Parent?.Level + 1) ?? 0;
|
||||||
|
|
||||||
|
@ -41,5 +41,9 @@ namespace Elwig.Models.Entities {
|
|||||||
public static bool operator !=(WineQualLevel? q1, WineQualLevel? q2) {
|
public static bool operator !=(WineQualLevel? q1, WineQualLevel? q2) {
|
||||||
return !(q1?.Equals(q2) ?? Equals(q1, q2));
|
return !(q1?.Equals(q2) ?? Equals(q1, q2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode() {
|
||||||
|
return QualId.GetHashCode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using Elwig.Models.Entities;
|
using Elwig.Models.Entities;
|
||||||
|
|
||||||
namespace Elwig.Helpers {
|
namespace Elwig.Models {
|
||||||
public interface IAddress {
|
public interface IAddress {
|
||||||
string Name { get; }
|
string Name { get; }
|
||||||
string Address { get; }
|
string Address { get; }
|
7
Elwig/Models/IDelivery.cs
Normal file
7
Elwig/Models/IDelivery.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Elwig.Models {
|
||||||
|
public interface IDelivery {
|
||||||
|
int Weight { get; }
|
||||||
|
double Kmw { get; }
|
||||||
|
double Oe { get; }
|
||||||
|
}
|
||||||
|
}
|
17
Elwig/Resources/Sql/18-19.sql
Normal file
17
Elwig/Resources/Sql/18-19.sql
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
-- schema version 18 to 19
|
||||||
|
|
||||||
|
CREATE VIEW v_stat_total AS
|
||||||
|
SELECT d.year, d.zwstid, v.type, v.sortid,
|
||||||
|
IIF(b.discr = a.attrid OR NOT a.area_com, a.attrid, NULL) AS attrid,
|
||||||
|
p.cultid, q.qualid,
|
||||||
|
IIF(b.discr = '_', 'ungeb', 'geb') AS geb,
|
||||||
|
SUM(value) AS weight
|
||||||
|
FROM delivery_part p
|
||||||
|
LEFT JOIN delivery d ON (d.year, d.did) = (p.year, p.did)
|
||||||
|
LEFT JOIN wine_variety v ON v.sortid = p.sortid
|
||||||
|
LEFT JOIN wine_quality_level q ON q.qualid = p.qualid
|
||||||
|
LEFT JOIN delivery_part_bucket b ON (b.year, b.did, b.dpnr) = (p.year, p.did, p.dpnr)
|
||||||
|
LEFT JOIN v_wine_attribute a ON a.attrid = p.attrid
|
||||||
|
GROUP BY d.year, d.zwstid, v.type, v.sortid, IIF(b.discr = a.attrid OR NOT a.area_com, a.attrid, NULL), p.cultid, q.qualid, geb
|
||||||
|
HAVING SUM(value) > 0
|
||||||
|
ORDER BY d.year, d.zwstid, v.type DESC, v.sortid, IIF(b.discr = a.attrid OR NOT a.area_com, a.attrid, NULL), p.cultid, q.min_kmw, geb;
|
5
Elwig/Themes/Generic.xaml
Normal file
5
Elwig/Themes/Generic.xaml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<ResourceDictionary Source="/Elwig;component/Controls/UnitTextBox.xaml"/>
|
||||||
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
</ResourceDictionary>
|
@ -4,12 +4,14 @@ using Elwig.Models.Entities;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
using Xceed.Wpf.Toolkit;
|
using Xceed.Wpf.Toolkit;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
namespace Elwig.Windows {
|
namespace Elwig.Windows {
|
||||||
public abstract class AdministrationWindow : ContextWindow {
|
public abstract class AdministrationWindow : ContextWindow {
|
||||||
@ -44,30 +46,53 @@ namespace Elwig.Windows {
|
|||||||
private CheckBox[] CheckBoxInputs;
|
private CheckBox[] CheckBoxInputs;
|
||||||
private RadioButton[] RadioButtonInputs;
|
private RadioButton[] RadioButtonInputs;
|
||||||
private readonly Dictionary<Control, bool> Valid;
|
private readonly Dictionary<Control, bool> Valid;
|
||||||
private readonly Dictionary<Control, object?> OriginalValues;
|
private readonly Dictionary<Control, int?> OriginalValues;
|
||||||
private readonly Dictionary<Control, object?> DefaultValues;
|
private readonly Dictionary<Control, int?> DefaultValues;
|
||||||
|
|
||||||
|
private readonly RoutedCommand AltInsert = new("AltInsert", typeof(AdministrationWindow), [new KeyGesture(Key.Insert, ModifierKeys.Alt)]);
|
||||||
|
private readonly RoutedCommand AltDelete = new("AltDelete", typeof(AdministrationWindow), [new KeyGesture(Key.Delete, ModifierKeys.Alt)]);
|
||||||
|
private readonly RoutedCommand CtrlZ = new("CtrlZ", typeof(AdministrationWindow), [new KeyGesture(Key.Z, ModifierKeys.Control)]);
|
||||||
|
private readonly RoutedCommand CtrlS = new("CtrlS", typeof(AdministrationWindow), [new KeyGesture(Key.S, ModifierKeys.Control)]);
|
||||||
|
private readonly RoutedCommand CtrlB = new("CtrlB", typeof(AdministrationWindow), [new KeyGesture(Key.B, ModifierKeys.Control)]);
|
||||||
|
|
||||||
public AdministrationWindow() : base() {
|
public AdministrationWindow() : base() {
|
||||||
|
CommandBindings.Add(new CommandBinding(AltInsert, ShortcutNew));
|
||||||
|
CommandBindings.Add(new CommandBinding(AltDelete, ShortcutDelete));
|
||||||
|
CommandBindings.Add(new CommandBinding(CtrlZ, ShortcutReset));
|
||||||
|
CommandBindings.Add(new CommandBinding(CtrlS, ShortcutSave));
|
||||||
|
CommandBindings.Add(new CommandBinding(CtrlB, ShortcutEdit));
|
||||||
IsEditing = false;
|
IsEditing = false;
|
||||||
IsCreating = false;
|
IsCreating = false;
|
||||||
ExemptInputs = Array.Empty<Control>();
|
ExemptInputs = [];
|
||||||
RequiredInputs = Array.Empty<Control>();
|
RequiredInputs = [];
|
||||||
TextBoxInputs = Array.Empty<TextBox>();
|
TextBoxInputs = [];
|
||||||
PlzInputs = Array.Empty<TextBox>();
|
PlzInputs = [];
|
||||||
ComboBoxInputs = Array.Empty<ComboBox>();
|
ComboBoxInputs = [];
|
||||||
CheckComboBoxInputs = Array.Empty<CheckComboBox>();
|
CheckComboBoxInputs = [];
|
||||||
PlzOrtInputs = Array.Empty<ComboBox>();
|
PlzOrtInputs = [];
|
||||||
CheckBoxInputs = Array.Empty<CheckBox>();
|
CheckBoxInputs = [];
|
||||||
RadioButtonInputs = Array.Empty<RadioButton>();
|
RadioButtonInputs = [];
|
||||||
Valid = new();
|
Valid = [];
|
||||||
OriginalValues = new();
|
OriginalValues = [];
|
||||||
DefaultValues = new();
|
DefaultValues = [];
|
||||||
Closing += OnClosing;
|
Closing += OnClosing;
|
||||||
Loaded -= base.OnLoaded;
|
Loaded -= base.OnLoaded;
|
||||||
Loaded += OnLoaded;
|
Loaded += OnLoaded;
|
||||||
Loaded += base.OnLoaded;
|
Loaded += base.OnLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract protected void ShortcutNew();
|
||||||
|
abstract protected void ShortcutDelete();
|
||||||
|
abstract protected void ShortcutReset();
|
||||||
|
abstract protected void ShortcutSave();
|
||||||
|
abstract protected void ShortcutEdit();
|
||||||
|
|
||||||
|
private void ShortcutNew(object sender, EventArgs evt) { ShortcutNew(); }
|
||||||
|
private void ShortcutDelete(object sender, EventArgs evt) { ShortcutDelete(); }
|
||||||
|
private void ShortcutReset(object sender, EventArgs evt) { ShortcutReset(); }
|
||||||
|
private void ShortcutSave(object sender, EventArgs evt) { ShortcutSave(); }
|
||||||
|
private void ShortcutEdit(object sender, EventArgs evt) { ShortcutEdit(); }
|
||||||
|
|
||||||
private new void OnLoaded(object sender, RoutedEventArgs evt) {
|
private new void OnLoaded(object sender, RoutedEventArgs evt) {
|
||||||
TextBoxInputs = ControlUtils.FindAllChildren<TextBox>(this, ExemptInputs).ToArray();
|
TextBoxInputs = ControlUtils.FindAllChildren<TextBox>(this, ExemptInputs).ToArray();
|
||||||
ComboBoxInputs = ControlUtils.FindAllChildren<ComboBox>(this, ExemptInputs).ToArray();
|
ComboBoxInputs = ControlUtils.FindAllChildren<ComboBox>(this, ExemptInputs).ToArray();
|
||||||
@ -102,13 +127,12 @@ namespace Elwig.Windows {
|
|||||||
|
|
||||||
abstract protected void UpdateButtons();
|
abstract protected void UpdateButtons();
|
||||||
|
|
||||||
protected override async Task OnRenewContext() {
|
protected override async Task OnRenewContext(AppDbContext ctx) {
|
||||||
for (int i = 0; i < PlzInputs.Length; i++)
|
for (int i = 0; i < PlzInputs.Length; i++)
|
||||||
UpdatePlz(PlzInputs[i], PlzOrtInputs[i]);
|
await UpdatePlz(PlzInputs[i], PlzOrtInputs[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void ValidateInput(Control input, bool valid) {
|
protected void ValidateInput(Control input, bool valid) {
|
||||||
if (input is UnitTextBox utbx) input = utbx.TextBox;
|
|
||||||
Valid[input] = valid;
|
Valid[input] = valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,20 +221,19 @@ namespace Elwig.Windows {
|
|||||||
|
|
||||||
protected void FillOriginalValues() {
|
protected void FillOriginalValues() {
|
||||||
foreach (var tb in TextBoxInputs)
|
foreach (var tb in TextBoxInputs)
|
||||||
OriginalValues[tb] = tb.Text;
|
OriginalValues[tb] = ControlUtils.GetInputHashCode(tb);
|
||||||
foreach (var cb in ComboBoxInputs)
|
foreach (var cb in ComboBoxInputs)
|
||||||
OriginalValues[cb] = cb.SelectedItem;
|
OriginalValues[cb] = ControlUtils.GetInputHashCode(cb);
|
||||||
foreach (var ccb in CheckComboBoxInputs)
|
foreach (var ccb in CheckComboBoxInputs)
|
||||||
OriginalValues[ccb] = ccb.SelectedItems.Cast<object>().ToArray();
|
OriginalValues[ccb] = ControlUtils.GetInputHashCode(ccb);
|
||||||
foreach (var cb in CheckBoxInputs)
|
foreach (var cb in CheckBoxInputs)
|
||||||
OriginalValues[cb] = cb.IsChecked?.ToString();
|
OriginalValues[cb] = ControlUtils.GetInputHashCode(cb);
|
||||||
foreach (var rb in RadioButtonInputs)
|
foreach (var rb in RadioButtonInputs)
|
||||||
OriginalValues[rb] = rb.IsChecked?.ToString();
|
OriginalValues[rb] = ControlUtils.GetInputHashCode(rb);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void SetOriginalValue(Control input, object? value) {
|
protected void SetOriginalValue(Control input, object? value) {
|
||||||
if (input is UnitTextBox utbx) input = utbx.TextBox;
|
OriginalValues[input] = Utils.GetEntityIdentifier(value);
|
||||||
OriginalValues[input] = value is bool b ? b.ToString() : value;
|
|
||||||
if (InputHasChanged(input)) {
|
if (InputHasChanged(input)) {
|
||||||
ControlUtils.SetInputChanged(input);
|
ControlUtils.SetInputChanged(input);
|
||||||
} else {
|
} else {
|
||||||
@ -219,19 +242,16 @@ namespace Elwig.Windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void SetOriginalValue(Control input) {
|
protected void SetOriginalValue(Control input) {
|
||||||
if (input is UnitTextBox utbx) input = utbx.TextBox;
|
SetOriginalValue(input, ControlUtils.GetInputHashCode(input));
|
||||||
SetOriginalValue(input, ControlUtils.GetInputValue(input));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void UnsetOriginalValue(Control input) {
|
protected void UnsetOriginalValue(Control input) {
|
||||||
if (input is UnitTextBox utbx) input = utbx.TextBox;
|
|
||||||
OriginalValues.Remove(input);
|
OriginalValues.Remove(input);
|
||||||
ControlUtils.ClearInputState(input);
|
ControlUtils.ClearInputState(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void SetDefaultValue(Control input, object? value) {
|
protected void SetDefaultValue(Control input, object? value) {
|
||||||
if (input is UnitTextBox utbx) input = utbx.TextBox;
|
DefaultValues[input] = Utils.GetEntityIdentifier(value);
|
||||||
DefaultValues[input] = value is bool b ? b.ToString() : value;
|
|
||||||
if (!InputHasChanged(input)) {
|
if (!InputHasChanged(input)) {
|
||||||
if (InputIsNotDefault(input)) {
|
if (InputIsNotDefault(input)) {
|
||||||
ControlUtils.SetInputNotDefault(input);
|
ControlUtils.SetInputNotDefault(input);
|
||||||
@ -242,12 +262,10 @@ namespace Elwig.Windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void SetDefaultValue(Control input) {
|
protected void SetDefaultValue(Control input) {
|
||||||
if (input is UnitTextBox utbx) input = utbx.TextBox;
|
SetDefaultValue(input, ControlUtils.GetInputHashCode(input));
|
||||||
SetDefaultValue(input, ControlUtils.GetInputValue(input));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void UnsetDefaultValue(Control input) {
|
protected void UnsetDefaultValue(Control input) {
|
||||||
if (input is UnitTextBox utbx) input = utbx.TextBox;
|
|
||||||
DefaultValues.Remove(input);
|
DefaultValues.Remove(input);
|
||||||
if (!InputHasChanged(input)) {
|
if (!InputHasChanged(input)) {
|
||||||
ControlUtils.ClearInputState(input);
|
ControlUtils.ClearInputState(input);
|
||||||
@ -271,45 +289,24 @@ namespace Elwig.Windows {
|
|||||||
protected bool IsValid => Valid.All(kv => kv.Value);
|
protected bool IsValid => Valid.All(kv => kv.Value);
|
||||||
|
|
||||||
protected bool GetInputValid(Control input) {
|
protected bool GetInputValid(Control input) {
|
||||||
if (input is UnitTextBox utbx) input = utbx.TextBox;
|
|
||||||
return Valid[input];
|
return Valid[input];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool InputHasChanged(Control input) {
|
protected bool InputHasChanged(Control input) {
|
||||||
if (input is UnitTextBox utbx) input = utbx.TextBox;
|
if (!OriginalValues.TryGetValue(input, out int? original)) {
|
||||||
if (!OriginalValues.ContainsKey(input)) {
|
|
||||||
return false;
|
return false;
|
||||||
} else if (input is TextBox tb) {
|
|
||||||
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 (string?)OriginalValues[cb] != cb.IsChecked?.ToString();
|
|
||||||
} else if (input is RadioButton rb) {
|
|
||||||
return (string?)OriginalValues[rb] != rb.IsChecked?.ToString();
|
|
||||||
} else {
|
} else {
|
||||||
return false;
|
var current = ControlUtils.GetInputHashCode(input);
|
||||||
|
return original != current;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool InputIsNotDefault(Control input) {
|
protected bool InputIsNotDefault(Control input) {
|
||||||
if (input is UnitTextBox utbx) input = utbx.TextBox;
|
if (!DefaultValues.TryGetValue(input, out int? defaultValue)) {
|
||||||
if (!DefaultValues.ContainsKey(input)) {
|
|
||||||
return false;
|
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 {
|
} else {
|
||||||
return false;
|
var current = ControlUtils.GetInputHashCode(input);
|
||||||
|
return defaultValue != current;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,11 +326,20 @@ namespace Elwig.Windows {
|
|||||||
RadioButtonInputs.Any(InputIsNotDefault)
|
RadioButtonInputs.Any(InputIsNotDefault)
|
||||||
);
|
);
|
||||||
|
|
||||||
protected void UpdatePlz(TextBox plzInput, ComboBox ortInput) {
|
protected async Task UpdatePlz(TextBox plzInput, ComboBox ortInput) {
|
||||||
var plzInputValid = Validator.CheckPlz(plzInput, RequiredInputs.Contains(plzInput), Context).IsValid;
|
var plzInputValid = Validator.CheckPlz(plzInput, RequiredInputs.Contains(plzInput)).IsValid;
|
||||||
var item = ortInput.SelectedItem;
|
|
||||||
var list = plzInputValid && plzInput.Text.Length == 4 ? Context.Postleitzahlen.Find(int.Parse(plzInput.Text))?.Orte.ToList() : null;
|
List<AT_PlzDest>? list = null;
|
||||||
ControlUtils.RenewItemsSource(ortInput, list, i => (i as AT_PlzDest)?.Id);
|
if (plzInputValid && plzInput.Text.Length == 4) {
|
||||||
|
var plz = int.Parse(plzInput.Text);
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
|
list = await ctx.PlzDestinations
|
||||||
|
.Where(p => p.Plz == plz)
|
||||||
|
.Include(p => p.Ort)
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
ControlUtils.RenewItemsSource(ortInput, list);
|
||||||
if (list != null && ortInput.SelectedItem == null && list.Count == 1)
|
if (list != null && ortInput.SelectedItem == null && list.Count == 1)
|
||||||
ortInput.SelectedItem = list[0];
|
ortInput.SelectedItem = list[0];
|
||||||
UpdateComboBox(ortInput);
|
UpdateComboBox(ortInput);
|
||||||
@ -360,11 +366,7 @@ namespace Elwig.Windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected bool InputTextChanged(TextBox input, Func<TextBox, bool, ValidationResult> checker) {
|
protected bool InputTextChanged(TextBox input, Func<TextBox, bool, ValidationResult> checker) {
|
||||||
return InputTextChanged(input, (tb, required, ctx) => checker(tb, required));
|
return InputTextChanged(input, checker(input, SenderIsRequired(input)));
|
||||||
}
|
|
||||||
|
|
||||||
protected bool InputTextChanged(TextBox input, Func<TextBox, bool, AppDbContext, ValidationResult> checker) {
|
|
||||||
return InputTextChanged(input, checker(input, SenderIsRequired(input), Context));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool InputTextChanged(TextBox input, ValidationResult res) {
|
protected bool InputTextChanged(TextBox input, ValidationResult res) {
|
||||||
@ -385,11 +387,7 @@ namespace Elwig.Windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected bool InputLostFocus(TextBox input, Func<TextBox, bool, ValidationResult> checker, string? msg = null) {
|
protected bool InputLostFocus(TextBox input, Func<TextBox, bool, ValidationResult> checker, string? msg = null) {
|
||||||
return InputLostFocus(input, (tb, requiered, ctx) => checker(tb, requiered), msg);
|
return InputLostFocus(input, checker(input, SenderIsRequired(input)), msg);
|
||||||
}
|
|
||||||
|
|
||||||
protected bool InputLostFocus(TextBox input, Func<TextBox, bool, AppDbContext, ValidationResult> checker, string? msg = null) {
|
|
||||||
return InputLostFocus(input, checker(input, SenderIsRequired(input), Context), msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool InputLostFocus(TextBox input, ValidationResult res, string? msg = null) {
|
protected bool InputLostFocus(TextBox input, ValidationResult res, string? msg = null) {
|
||||||
@ -428,7 +426,7 @@ namespace Elwig.Windows {
|
|||||||
|
|
||||||
protected void TextBox_TextChanged(object sender, RoutedEventArgs? evt) {
|
protected void TextBox_TextChanged(object sender, RoutedEventArgs? evt) {
|
||||||
var input = (Control)sender;
|
var input = (Control)sender;
|
||||||
var tb = input as TextBox ?? (input as UnitTextBox)?.TextBox;
|
var tb = input as TextBox;
|
||||||
if (SenderIsRequired(input) && tb?.Text.Length == 0) {
|
if (SenderIsRequired(input) && tb?.Text.Length == 0) {
|
||||||
ValidateInput(input, false);
|
ValidateInput(input, false);
|
||||||
ControlUtils.SetInputInvalid(input);
|
ControlUtils.SetInputInvalid(input);
|
||||||
@ -474,12 +472,12 @@ namespace Elwig.Windows {
|
|||||||
|
|
||||||
protected void IntegerInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
protected void IntegerInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
||||||
// FIXME
|
// FIXME
|
||||||
InputTextChanged((sender as UnitTextBox)?.TextBox ?? (TextBox)sender, Validator.CheckInteger);
|
InputTextChanged((TextBox)sender, Validator.CheckInteger);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void DecimalInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
protected void DecimalInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
||||||
// FIXME
|
// FIXME
|
||||||
InputTextChanged((sender as UnitTextBox)?.TextBox ?? (TextBox)sender, Validator.CheckDecimal);
|
InputTextChanged((TextBox)sender, Validator.CheckDecimal);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void PartialDateInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
protected void PartialDateInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
||||||
@ -506,18 +504,18 @@ namespace Elwig.Windows {
|
|||||||
InputLostFocus((TextBox)sender, Validator.CheckTime);
|
InputLostFocus((TextBox)sender, Validator.CheckTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void PlzInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
protected async void PlzInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
||||||
var plz = (TextBox)sender;
|
var plz = (TextBox)sender;
|
||||||
InputTextChanged(plz, Validator.CheckPlz);
|
InputTextChanged(plz, Validator.CheckPlz);
|
||||||
if ("PLZ".Equals(plz.Tag))
|
if ("PLZ".Equals(plz.Tag))
|
||||||
UpdatePlz(plz, GetPlzOrtInput(plz));
|
await UpdatePlz(plz, GetPlzOrtInput(plz));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void PlzInput_LostFocus(object sender, RoutedEventArgs evt) {
|
protected async void PlzInput_LostFocus(object sender, RoutedEventArgs evt) {
|
||||||
var plz = (TextBox)sender;
|
var plz = (TextBox)sender;
|
||||||
InputLostFocus(plz, Validator.CheckPlz);
|
InputLostFocus(plz, Validator.CheckPlz);
|
||||||
if ("PLZ".Equals(plz.Tag))
|
if ("PLZ".Equals(plz.Tag))
|
||||||
UpdatePlz(plz, GetPlzOrtInput(plz));
|
await UpdatePlz(plz, GetPlzOrtInput(plz));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void EmailAddressInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
protected void EmailAddressInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
||||||
|
@ -95,18 +95,42 @@
|
|||||||
</DataGrid>
|
</DataGrid>
|
||||||
|
|
||||||
<Button x:Name="NewAreaCommitmentButton" Content="Neu" Click="NewAreaCommitmentButton_Click"
|
<Button x:Name="NewAreaCommitmentButton" Content="Neu" Click="NewAreaCommitmentButton_Click"
|
||||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2"/>
|
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2">
|
||||||
|
<Button.ToolTip>
|
||||||
|
<TextBlock FontWeight="Bold">Alt+Einfg</TextBlock>
|
||||||
|
</Button.ToolTip>
|
||||||
|
</Button>
|
||||||
<Button x:Name="EditAreaCommitmentButton" Content="Bearbeiten" Click="EditAreaCommitmentButton_Click" IsEnabled="False"
|
<Button x:Name="EditAreaCommitmentButton" Content="Bearbeiten" Click="EditAreaCommitmentButton_Click" IsEnabled="False"
|
||||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2"/>
|
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2">
|
||||||
|
<Button.ToolTip>
|
||||||
|
<TextBlock FontWeight="Bold">Strg+B</TextBlock>
|
||||||
|
</Button.ToolTip>
|
||||||
|
</Button>
|
||||||
<Button x:Name="DeleteAreaCommitmentButton" Content="Löschen" Click="DeleteAreaCommitmentButton_Click" IsEnabled="False"
|
<Button x:Name="DeleteAreaCommitmentButton" Content="Löschen" Click="DeleteAreaCommitmentButton_Click" IsEnabled="False"
|
||||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2"/>
|
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2">
|
||||||
|
<Button.ToolTip>
|
||||||
|
<TextBlock FontWeight="Bold">Alt+Entf</TextBlock>
|
||||||
|
</Button.ToolTip>
|
||||||
|
</Button>
|
||||||
|
|
||||||
<Button x:Name="AreaCommitmentSaveButton" Content="Speichern" Click="AreaCommitmentSaveButton_Click" IsEnabled="False" Visibility="Hidden"
|
<Button x:Name="AreaCommitmentSaveButton" Content="Speichern" Click="AreaCommitmentSaveButton_Click" IsEnabled="False" Visibility="Hidden"
|
||||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2"/>
|
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2">
|
||||||
|
<Button.ToolTip>
|
||||||
|
<TextBlock FontWeight="Bold">Strg+S</TextBlock>
|
||||||
|
</Button.ToolTip>
|
||||||
|
</Button>
|
||||||
<Button x:Name="AreaCommitmentResetButton" Content="Zurücksetzen" Click="AreaCommitmentResetButton_Click" IsEnabled="False" Visibility="Hidden"
|
<Button x:Name="AreaCommitmentResetButton" Content="Zurücksetzen" Click="AreaCommitmentResetButton_Click" IsEnabled="False" Visibility="Hidden"
|
||||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2"/>
|
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2">
|
||||||
|
<Button.ToolTip>
|
||||||
|
<TextBlock FontWeight="Bold">Strg+Z</TextBlock>
|
||||||
|
</Button.ToolTip>
|
||||||
|
</Button>
|
||||||
<Button x:Name="AreaCommitmentCancelButton" Content="Abbrechen" Click="AreaCommitmentCancelButton_Click" IsEnabled="False" Visibility="Hidden" IsCancel="True"
|
<Button x:Name="AreaCommitmentCancelButton" Content="Abbrechen" Click="AreaCommitmentCancelButton_Click" IsEnabled="False" Visibility="Hidden" IsCancel="True"
|
||||||
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2"/>
|
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2">
|
||||||
|
<Button.ToolTip>
|
||||||
|
<TextBlock FontWeight="Bold">Esc</TextBlock>
|
||||||
|
</Button.ToolTip>
|
||||||
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<GridSplitter Grid.Column="1" Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
|
<GridSplitter Grid.Column="1" Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
|
||||||
|
@ -7,7 +7,6 @@ using Elwig.Models.Entities;
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
|
||||||
using Xceed.Wpf.Toolkit.Primitives;
|
using Xceed.Wpf.Toolkit.Primitives;
|
||||||
|
|
||||||
namespace Elwig.Windows {
|
namespace Elwig.Windows {
|
||||||
@ -20,7 +19,8 @@ namespace Elwig.Windows {
|
|||||||
|
|
||||||
public AreaComAdminWindow(int mgnr) {
|
public AreaComAdminWindow(int mgnr) {
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
Member = Context.Members.Find(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
|
using var ctx = new AppDbContext();
|
||||||
|
Member = ctx.Members.Find(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
|
||||||
Title = $"Flächenbindungen - {Member.AdministrativeName} - Elwig";
|
Title = $"Flächenbindungen - {Member.AdministrativeName} - Elwig";
|
||||||
ExemptInputs = [
|
ExemptInputs = [
|
||||||
MgNrInput, AreaCommitmentList, NewAreaCommitmentButton,
|
MgNrInput, AreaCommitmentList, NewAreaCommitmentButton,
|
||||||
@ -29,7 +29,7 @@ namespace Elwig.Windows {
|
|||||||
];
|
];
|
||||||
RequiredInputs = [
|
RequiredInputs = [
|
||||||
FbNrInput, YearFromInput, KgInput, RdInput,
|
FbNrInput, YearFromInput, KgInput, RdInput,
|
||||||
GstNrInput, AreaInput.TextBox, AreaComTypeInput, WineCultivationInput
|
GstNrInput, AreaInput, AreaComTypeInput, WineCultivationInput
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,13 +39,16 @@ namespace Elwig.Windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task RefreshAreaCommitmentList() {
|
private async Task RefreshAreaCommitmentList() {
|
||||||
await Context.AreaCommitments.LoadAsync();
|
|
||||||
await RefreshAreaCommitmentListQuery();
|
await RefreshAreaCommitmentListQuery();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RefreshAreaCommitmentListQuery(bool updateSort = false) {
|
private async Task RefreshAreaCommitmentListQuery(bool updateSort = false) {
|
||||||
var (_, areaComQuery, filter) = await GetFilters();
|
using var ctx = new AppDbContext();
|
||||||
var areaComs = await areaComQuery.ToListAsync();
|
var (_, areaComQuery, filter) = await GetFilters(ctx);
|
||||||
|
var areaComs = await areaComQuery
|
||||||
|
.Include(a => a.Kg.AtKg)
|
||||||
|
.Include(a => a.Rd!.Kg.AtKg)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
if (filter.Count > 0 && areaComs.Count > 0) {
|
if (filter.Count > 0 && areaComs.Count > 0) {
|
||||||
var dict = areaComs.AsParallel()
|
var dict = areaComs.AsParallel()
|
||||||
@ -58,7 +61,7 @@ namespace Elwig.Windows {
|
|||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
ControlUtils.RenewItemsSource(AreaCommitmentList, areaComs, i => (i as AreaCom)?.FbNr,
|
ControlUtils.RenewItemsSource(AreaCommitmentList, areaComs,
|
||||||
AreaCommitmentList_SelectionChanged, filter.Count > 0 ? ControlUtils.RenewSourceDefault.IfOnly : ControlUtils.RenewSourceDefault.None, !updateSort);
|
AreaCommitmentList_SelectionChanged, filter.Count > 0 ? ControlUtils.RenewSourceDefault.IfOnly : ControlUtils.RenewSourceDefault.None, !updateSort);
|
||||||
RefreshInputs();
|
RefreshInputs();
|
||||||
|
|
||||||
@ -75,11 +78,11 @@ namespace Elwig.Windows {
|
|||||||
StatusContracts.ToolTip = $"Vertragsarten: {groups.Count}\n" + string.Join($"\n", groups.Select(g => $"{g.Key}: {g.Item2:N0} m²"));
|
StatusContracts.ToolTip = $"Vertragsarten: {groups.Count}\n" + string.Join($"\n", groups.Select(g => $"{g.Key}: {g.Item2:N0} m²"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<(List<string>, IQueryable<AreaCom>, List<string>)> GetFilters() {
|
private async Task<(List<string>, IQueryable<AreaCom>, List<string>)> GetFilters(AppDbContext ctx) {
|
||||||
List<string> filterNames = [];
|
List<string> filterNames = [];
|
||||||
IQueryable<AreaCom> areaComQuery = Context.AreaCommitments.Where(a => a.MgNr == Member.MgNr).OrderBy(a => a.FbNr);
|
IQueryable<AreaCom> areaComQuery = ctx.AreaCommitments.Where(a => a.MgNr == Member.MgNr).OrderBy(a => a.FbNr);
|
||||||
if (ActiveAreaCommitmentInput.IsChecked == true) {
|
if (ActiveAreaCommitmentInput.IsChecked == true) {
|
||||||
areaComQuery = areaComQuery.Where(a => (a.YearFrom <= Utils.CurrentYear) && (a.YearTo == null || a.YearTo >= Utils.CurrentYear));
|
areaComQuery = Utils.ActiveAreaCommitments(areaComQuery);
|
||||||
filterNames.Add("aktiv");
|
filterNames.Add("aktiv");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,8 +93,8 @@ namespace Elwig.Windows {
|
|||||||
|
|
||||||
var filter = TextFilter.ToList();
|
var filter = TextFilter.ToList();
|
||||||
if (filter.Count > 0) {
|
if (filter.Count > 0) {
|
||||||
var var = await Context.WineVarieties.ToDictionaryAsync(v => v.SortId, v => v);
|
var var = await ctx.WineVarieties.ToDictionaryAsync(v => v.SortId, v => v);
|
||||||
var attr = await Context.WineAttributes.ToDictionaryAsync(a => a.Name.ToLower().Split(" ")[0], a => a);
|
var attr = await ctx.WineAttributes.ToDictionaryAsync(a => a.Name.ToLower().Split(" ")[0], a => a);
|
||||||
|
|
||||||
for (int i = 0; i < filter.Count; i++) {
|
for (int i = 0; i < filter.Count; i++) {
|
||||||
var e = filter[i];
|
var e = filter[i];
|
||||||
@ -150,13 +153,21 @@ namespace Elwig.Windows {
|
|||||||
YearFromInput.Text = a.YearFrom.ToString();
|
YearFromInput.Text = a.YearFrom.ToString();
|
||||||
YearToInput.Text = a.YearTo.ToString();
|
YearToInput.Text = a.YearTo.ToString();
|
||||||
|
|
||||||
KgInput.SelectedItem = a.Kg.AtKg;
|
ControlUtils.SelectItemWithPk(KgInput, a.KgNr);
|
||||||
RdInput.SelectedItem = a.Rd ?? RdInput.Items[0];
|
if (a.RdNr != null) {
|
||||||
|
ControlUtils.SelectItemWithPk(RdInput, a.KgNr, a.RdNr);
|
||||||
|
} else {
|
||||||
|
RdInput.SelectedIndex = 0;
|
||||||
|
}
|
||||||
GstNrInput.Text = a.GstNr;
|
GstNrInput.Text = a.GstNr;
|
||||||
AreaInput.Text = a.Area.ToString();
|
AreaInput.Text = a.Area.ToString();
|
||||||
|
|
||||||
AreaComTypeInput.SelectedItem = a.AreaComType;
|
ControlUtils.SelectItemWithPk(AreaComTypeInput, a.VtrgId);
|
||||||
WineCultivationInput.SelectedItem = a.WineCult ?? WineCultivationInput.Items[0];
|
if (a.CultId != null) {
|
||||||
|
ControlUtils.SelectItemWithPk(WineCultivationInput, a.CultId);
|
||||||
|
} else {
|
||||||
|
WineCultivationInput.SelectedIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
CommentInput.Text = a.Comment;
|
CommentInput.Text = a.Comment;
|
||||||
|
|
||||||
@ -167,26 +178,44 @@ namespace Elwig.Windows {
|
|||||||
ClearOriginalValues();
|
ClearOriginalValues();
|
||||||
ClearDefaultValues();
|
ClearDefaultValues();
|
||||||
|
|
||||||
FbNrInput.Text = (await Context.NextFbNr()).ToString();
|
using (var ctx = new AppDbContext()) {
|
||||||
|
FbNrInput.Text = (await ctx.NextFbNr()).ToString();
|
||||||
MgNrInput.Text = Member.MgNr.ToString();
|
MgNrInput.Text = Member.MgNr.ToString();
|
||||||
YearFromInput.Text = DateTime.Now.Year.ToString();
|
YearFromInput.Text = DateTime.Now.Year.ToString();
|
||||||
WineCultivationInput.SelectedIndex = 0;
|
WineCultivationInput.SelectedIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
SetDefaultValue(FbNrInput);
|
SetDefaultValue(FbNrInput);
|
||||||
ValidateRequiredInputs();
|
ValidateRequiredInputs();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnRenewContext() {
|
protected override async Task OnRenewContext(AppDbContext ctx) {
|
||||||
await base.OnRenewContext();
|
await base.OnRenewContext(ctx);
|
||||||
ControlUtils.RenewItemsSource(KgInput, await Context.WbKgs.Select(k => k.AtKg).OrderBy(k => k.Name).ToListAsync(), i => (i as AT_Kg)?.KgNr);
|
ControlUtils.RenewItemsSource(KgInput, await ctx.WbKgs
|
||||||
ControlUtils.RenewItemsSource(AreaComTypeInput, await Context.AreaCommitmentTypes.OrderBy(v => v.VtrgId).ToListAsync(), i => (i as AreaComType)?.VtrgId);
|
.Include(k => k.AtKg.WbKg!.Rds)
|
||||||
var cultList = await Context.WineCultivations.OrderBy(c => c.Name).Cast<object>().ToListAsync();
|
.Select(k => k.AtKg)
|
||||||
|
.OrderBy(k => k.Name)
|
||||||
|
.ToListAsync());
|
||||||
|
ControlUtils.RenewItemsSource(AreaComTypeInput, await ctx.AreaCommitmentTypes
|
||||||
|
.Include(c => c.WineVar)
|
||||||
|
.Include(c => c.WineAttr)
|
||||||
|
.OrderBy(v => v.VtrgId)
|
||||||
|
.ToListAsync());
|
||||||
|
var cultList = await ctx.WineCultivations
|
||||||
|
.OrderBy(c => c.Name)
|
||||||
|
.Cast<object>().ToListAsync();
|
||||||
cultList.Insert(0, new NullItem());
|
cultList.Insert(0, new NullItem());
|
||||||
ControlUtils.RenewItemsSource(WineCultivationInput, cultList, i => (i as WineCult)?.CultId, null, ControlUtils.RenewSourceDefault.First);
|
ControlUtils.RenewItemsSource(WineCultivationInput, cultList, null, ControlUtils.RenewSourceDefault.First);
|
||||||
await RefreshAreaCommitmentList();
|
await RefreshAreaCommitmentList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void NewAreaCommitmentButton_Click(object sender, RoutedEventArgs evt) {
|
protected override void ShortcutNew() {
|
||||||
|
if (!NewAreaCommitmentButton.IsEnabled || NewAreaCommitmentButton.Visibility != Visibility.Visible)
|
||||||
|
return;
|
||||||
|
NewAreaCommitmentButton_Click(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NewAreaCommitmentButton_Click(object? sender, RoutedEventArgs? evt) {
|
||||||
IsCreating = true;
|
IsCreating = true;
|
||||||
AreaCommitmentList.IsEnabled = false;
|
AreaCommitmentList.IsEnabled = false;
|
||||||
AreaCommitmentList.SelectedItem = null;
|
AreaCommitmentList.SelectedItem = null;
|
||||||
@ -197,7 +226,14 @@ namespace Elwig.Windows {
|
|||||||
LockSearchInputs();
|
LockSearchInputs();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EditAreaCommitmentButton_Click(object sender, RoutedEventArgs evt) {
|
|
||||||
|
protected override void ShortcutEdit() {
|
||||||
|
if (!EditAreaCommitmentButton.IsEnabled || EditAreaCommitmentButton.Visibility != Visibility.Visible)
|
||||||
|
return;
|
||||||
|
EditAreaCommitmentButton_Click(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EditAreaCommitmentButton_Click(object? sender, RoutedEventArgs? evt) {
|
||||||
if (AreaCommitmentList.SelectedItem == null)
|
if (AreaCommitmentList.SelectedItem == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -210,79 +246,85 @@ namespace Elwig.Windows {
|
|||||||
LockSearchInputs();
|
LockSearchInputs();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void DeleteAreaCommitmentButton_Click(object sender, RoutedEventArgs evt) {
|
protected override void ShortcutDelete() {
|
||||||
|
if (!DeleteAreaCommitmentButton.IsEnabled || DeleteAreaCommitmentButton.Visibility != Visibility.Visible)
|
||||||
|
return;
|
||||||
|
DeleteAreaCommitmentButton_Click(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void DeleteAreaCommitmentButton_Click(object? sender, RoutedEventArgs? evt) {
|
||||||
AreaCom a = (AreaCom)AreaCommitmentList.SelectedItem;
|
AreaCom a = (AreaCom)AreaCommitmentList.SelectedItem;
|
||||||
if (a == null) return;
|
if (a == null) return;
|
||||||
|
|
||||||
var r = MessageBox.Show(
|
var r = MessageBox.Show(
|
||||||
$"Soll die Flächenbindung {a.GstNr} ({a.Area} m²) wirklich unwiderruflich gelöscht werden?",
|
$"Soll die Flächenbindung {a.GstNr} ({a.Area} m²) wirklich unwiderruflich gelöscht werden?",
|
||||||
"Flächenbindung löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
|
"Flächenbindung löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
|
||||||
if (r == MessageBoxResult.Yes) {
|
if (r == MessageBoxResult.OK) {
|
||||||
Context.Remove(a);
|
using var ctx = new AppDbContext();
|
||||||
Context.SaveChanges();
|
ctx.Remove(a);
|
||||||
|
await ctx.SaveChangesAsync();
|
||||||
await RefreshAreaCommitmentList();
|
await RefreshAreaCommitmentList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<AreaCom> UpdateAreaCom(AreaCom a) {
|
private async Task<int> UpdateAreaCom(int? oldFbNr) {
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
int newFbNr = int.Parse(FbNrInput.Text);
|
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 = RdInput.SelectedItem.GetType() == typeof(NullItem) ? null : ((WbRd)RdInput.SelectedItem).RdNr;
|
|
||||||
a.GstNr = GstNrInput.Text;
|
|
||||||
a.Area = int.Parse(AreaInput.Text);
|
|
||||||
a.VtrgId = (AreaComTypeInput.SelectedItem as AreaComType)!.VtrgId;
|
|
||||||
a.CultId = (WineCultivationInput.SelectedItem as WineCult)?.CultId;
|
|
||||||
a.Comment = (CommentInput.Text == "") ? null : CommentInput.Text;
|
|
||||||
|
|
||||||
EntityEntry<AreaCom>? tr = null;
|
var a = new AreaCom {
|
||||||
try {
|
FbNr = oldFbNr ?? newFbNr,
|
||||||
if (RdInput.SelectedItem is WbRd wbRd) {
|
MgNr = int.Parse(MgNrInput.Text),
|
||||||
a.RdNr = wbRd.RdNr;
|
YearFrom = int.Parse(YearFromInput.Text),
|
||||||
var e = Context.Entry(wbRd);
|
YearTo = (YearToInput.Text == "") ? null : int.Parse(YearToInput.Text),
|
||||||
if (e.State == EntityState.Detached) {
|
KgNr = ((AT_Kg)KgInput.SelectedItem).KgNr,
|
||||||
await Context.AddAsync(wbRd);
|
RdNr = (RdInput.SelectedItem as WbRd)?.RdNr,
|
||||||
|
GstNr = GstNrInput.Text.Trim(),
|
||||||
|
Area = int.Parse(AreaInput.Text),
|
||||||
|
VtrgId = (AreaComTypeInput.SelectedItem as AreaComType)!.VtrgId,
|
||||||
|
CultId = (WineCultivationInput.SelectedItem as WineCult)?.CultId,
|
||||||
|
Comment = (CommentInput.Text == "") ? null : CommentInput.Text.Trim(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (RdInput.SelectedItem is WbRd rd) {
|
||||||
|
if (rd.RdNr == 0) {
|
||||||
|
rd.RdNr = await ctx.NextRdNr(a.KgNr);
|
||||||
|
a.RdNr = rd.RdNr;
|
||||||
|
ctx.Add(rd);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldFbNr != null) {
|
||||||
|
ctx.Update(a);
|
||||||
} else {
|
} else {
|
||||||
a.RdNr = null;
|
ctx.Add(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsEditing) {
|
await ctx.SaveChangesAsync();
|
||||||
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) {
|
if (newFbNr != a.FbNr) {
|
||||||
await Context.Database.ExecuteSqlAsync($"UPDATE area_commitment SET fbnr = {newFbNr} WHERE fbnr = {a.FbNr}");
|
await ctx.Database.ExecuteSqlAsync($"UPDATE area_commitment SET fbnr = {newFbNr} WHERE fbnr = {oldFbNr}");
|
||||||
tr.State = EntityState.Detached;
|
|
||||||
await Context.SaveChangesAsync();
|
|
||||||
await tr.ReloadAsync();
|
|
||||||
a = await Context.AreaCommitments.FindAsync(newFbNr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception exc) {
|
await App.HintContextChange();
|
||||||
if (tr != null) {
|
|
||||||
tr.State = EntityState.Detached;
|
return newFbNr;
|
||||||
await tr.ReloadAsync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void ShortcutSave() {
|
||||||
|
if (!AreaCommitmentSaveButton.IsEnabled || AreaCommitmentSaveButton.Visibility != Visibility.Visible)
|
||||||
|
return;
|
||||||
|
AreaCommitmentSaveButton_Click(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void AreaCommitmentSaveButton_Click(object? sender, RoutedEventArgs? evt) {
|
||||||
|
int? fbnr = null;
|
||||||
|
try {
|
||||||
|
fbnr = await UpdateAreaCom((AreaCommitmentList.SelectedItem as AreaCom)?.FbNr);
|
||||||
|
} catch (Exception exc) {
|
||||||
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
|
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;
|
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
|
||||||
MessageBox.Show(str, "Flächenbindung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show(str, "Flächenbindung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return a!;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void AreaCommitmentSaveButton_Click(object sender, RoutedEventArgs evt) {
|
|
||||||
AreaCom a = await UpdateAreaCom(IsEditing ? (AreaCom)AreaCommitmentList.SelectedItem : Context.CreateProxy<AreaCom>());
|
|
||||||
IsEditing = false;
|
IsEditing = false;
|
||||||
IsCreating = false;
|
IsCreating = false;
|
||||||
AreaCommitmentList.IsEnabled = true;
|
AreaCommitmentList.IsEnabled = true;
|
||||||
@ -290,11 +332,20 @@ namespace Elwig.Windows {
|
|||||||
ShowAreaCommitmentNewEditDeleteButtons();
|
ShowAreaCommitmentNewEditDeleteButtons();
|
||||||
LockInputs();
|
LockInputs();
|
||||||
UnlockSearchInputs();
|
UnlockSearchInputs();
|
||||||
await App.HintContextChange();
|
FinishInputFilling();
|
||||||
AreaCommitmentList.SelectedItem = a;
|
await RefreshAreaCommitmentList();
|
||||||
|
RefreshInputs();
|
||||||
|
SearchInput.Text = "";
|
||||||
|
ControlUtils.SelectItem(AreaCommitmentList, AreaCommitmentList.ItemsSource.Cast<AreaCom>().Where(a => a.FbNr == fbnr).FirstOrDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AreaCommitmentResetButton_Click(object sender, RoutedEventArgs evt) {
|
protected override void ShortcutReset() {
|
||||||
|
if (!AreaCommitmentResetButton.IsEnabled || AreaCommitmentResetButton.Visibility != Visibility.Visible)
|
||||||
|
return;
|
||||||
|
AreaCommitmentResetButton_Click(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AreaCommitmentResetButton_Click(object? sender, RoutedEventArgs? evt) {
|
||||||
if (IsEditing) {
|
if (IsEditing) {
|
||||||
RefreshInputs();
|
RefreshInputs();
|
||||||
} else if (IsCreating) {
|
} else if (IsCreating) {
|
||||||
@ -393,14 +444,14 @@ namespace Elwig.Windows {
|
|||||||
await RefreshAreaCommitmentListQuery(true);
|
await RefreshAreaCommitmentListQuery(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void KgInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
|
private void KgInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
|
||||||
if (KgInput.SelectedItem is AT_Kg curr_kg) {
|
if (KgInput.SelectedItem is AT_Kg kg) {
|
||||||
var rdList = await Context.WbRde.Where(r => r.KgNr == curr_kg.KgNr).OrderBy(r => r.Name).Cast<object>().ToListAsync();
|
var rdList = kg.WbKg!.Rds.OrderBy(r => r.Name).Cast<object>().ToList();
|
||||||
rdList.Insert(0, new NullItem());
|
rdList.Insert(0, new NullItem());
|
||||||
ControlUtils.RenewItemsSource(RdInput, rdList, i => (i as WbRd)?.RdNr, null, ControlUtils.RenewSourceDefault.First);
|
ControlUtils.RenewItemsSource(RdInput, rdList, null, ControlUtils.RenewSourceDefault.First);
|
||||||
} else {
|
} else {
|
||||||
var rdList = new object[] { new NullItem() };
|
var rdList = new object[] { new NullItem() };
|
||||||
ControlUtils.RenewItemsSource(RdInput, rdList, i => (i as WbRd)?.RdNr, null, ControlUtils.RenewSourceDefault.First);
|
ControlUtils.RenewItemsSource(RdInput, rdList, null, ControlUtils.RenewSourceDefault.First);
|
||||||
}
|
}
|
||||||
ComboBox_SelectionChanged(sender, evt);
|
ComboBox_SelectionChanged(sender, evt);
|
||||||
}
|
}
|
||||||
@ -417,21 +468,25 @@ namespace Elwig.Windows {
|
|||||||
RdAddButton.IsEnabled = RdInput.SelectedIndex == -1;
|
RdAddButton.IsEnabled = RdInput.SelectedIndex == -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void RdAddButton_Click(object sender, RoutedEventArgs evt) {
|
private void RdAddButton_Click(object sender, RoutedEventArgs evt) {
|
||||||
if (KgInput.SelectedItem is not AT_Kg kg) return;
|
if (KgInput.SelectedItem is not AT_Kg kg) return;
|
||||||
string name = RdInput.Text.Trim();
|
string name = RdInput.Text.Trim();
|
||||||
if (name.Length == 0) return;
|
if (name.Length == 0) return;
|
||||||
var s = RdInput.ItemsSource.Cast<object?>();
|
var s = RdInput.ItemsSource.Cast<object?>();
|
||||||
RdInput.ItemsSource = s.Append(new WbRd() { KgNr = kg.KgNr, Name = name, RdNr = await Context.NextRdNr(kg.KgNr)});
|
RdInput.ItemsSource = s.Append(new WbRd {
|
||||||
|
KgNr = kg.KgNr,
|
||||||
|
RdNr = 0,
|
||||||
|
Name = name,
|
||||||
|
});
|
||||||
RdInput.SelectedIndex = s.Count();
|
RdInput.SelectedIndex = s.Count();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void InputTextChanged(TextBox input, Func<TextBox, bool, AppDbContext, AreaCom?, ValidationResult> checker) {
|
protected void InputTextChanged(TextBox input, Func<TextBox, bool, AreaCom?, ValidationResult> checker) {
|
||||||
InputTextChanged(input, checker(input, SenderIsRequired(input), Context, (AreaCom)AreaCommitmentList.SelectedItem));
|
InputTextChanged(input, checker(input, SenderIsRequired(input), (AreaCom)AreaCommitmentList.SelectedItem));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void InputLostFocus(TextBox input, Func<TextBox, bool, AppDbContext, AreaCom?, ValidationResult> checker, string? msg = null) {
|
protected void InputLostFocus(TextBox input, Func<TextBox, bool, AreaCom?, ValidationResult> checker, string? msg = null) {
|
||||||
InputLostFocus(input, checker(input, SenderIsRequired(input), Context, (AreaCom)AreaCommitmentList.SelectedItem), msg);
|
InputLostFocus(input, checker(input, SenderIsRequired(input), (AreaCom)AreaCommitmentList.SelectedItem), msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FbNrInput_TextChanged(object sender, RoutedEventArgs evt) {
|
private void FbNrInput_TextChanged(object sender, RoutedEventArgs evt) {
|
||||||
|
@ -24,6 +24,14 @@
|
|||||||
<Setter Property="Height" Value="25"/>
|
<Setter Property="Height" Value="25"/>
|
||||||
<Setter Property="TextWrapping" Value="NoWrap"/>
|
<Setter Property="TextWrapping" Value="NoWrap"/>
|
||||||
</Style>
|
</Style>
|
||||||
|
<Style TargetType="ctrl:UnitTextBox">
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||||
|
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
<Setter Property="Padding" Value="2"/>
|
||||||
|
<Setter Property="Height" Value="25"/>
|
||||||
|
<Setter Property="TextWrapping" Value="NoWrap"/>
|
||||||
|
</Style>
|
||||||
<Style TargetType="ComboBox">
|
<Style TargetType="ComboBox">
|
||||||
<Setter Property="Height" Value="25"/>
|
<Setter Property="Height" Value="25"/>
|
||||||
<Setter Property="FontSize" Value="14"/>
|
<Setter Property="FontSize" Value="14"/>
|
||||||
@ -347,8 +355,12 @@
|
|||||||
|
|
||||||
<Label Content="Sorte:" Margin="10,40,0,10"/>
|
<Label Content="Sorte:" Margin="10,40,0,10"/>
|
||||||
<ComboBox x:Name="AreaCommitmentTypeWineVariantInput" Grid.Column="1" Grid.ColumnSpan="2" Margin="0,40,10,10" Width="250" HorizontalAlignment="Left"
|
<ComboBox x:Name="AreaCommitmentTypeWineVariantInput" Grid.Column="1" Grid.ColumnSpan="2" Margin="0,40,10,10" Width="250" HorizontalAlignment="Left"
|
||||||
ItemTemplate="{StaticResource WineVarietyTemplate}" TextSearch.TextPath="Name"
|
TextSearch.TextPath="Name"
|
||||||
SelectionChanged="AreaCommitmentType_Changed"/>
|
SelectionChanged="AreaCommitmentType_Changed">
|
||||||
|
<ComboBox.ItemTemplateSelector>
|
||||||
|
<ctrl:WineVarietyTemplateSelector/>
|
||||||
|
</ComboBox.ItemTemplateSelector>
|
||||||
|
</ComboBox>
|
||||||
|
|
||||||
<Label Content="Attribut:" Margin="10,70,0,10"/>
|
<Label Content="Attribut:" Margin="10,70,0,10"/>
|
||||||
<ComboBox x:Name="AreaCommitmentTypeWineAttributeInput" Grid.Column="1" Grid.ColumnSpan="2" Margin="0,70,10,10" Width="250" HorizontalAlignment="Left"
|
<ComboBox x:Name="AreaCommitmentTypeWineAttributeInput" Grid.Column="1" Grid.ColumnSpan="2" Margin="0,70,10,10" Width="250" HorizontalAlignment="Left"
|
||||||
@ -505,7 +517,22 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem Header="Parameter">
|
<TabItem Header="Parameter">
|
||||||
|
<Grid>
|
||||||
|
<GroupBox x:Name="ParameterAreaComGroup" Header="Berechnung Flächenbindungen (aktuelle Saison)" Margin="10,10,10,10" VerticalAlignment="Top">
|
||||||
|
<Grid>
|
||||||
|
<CheckBox x:Name="ParameterAllowAttrIntoLowerInput" Content="Erlauben Lieferungen auch auf (konfigurierte) "schlechtere" Flächenbindungen aufzuteilen"
|
||||||
|
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,10,10,10"
|
||||||
|
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
|
||||||
|
<CheckBox x:Name="ParameterAvoidUnderDeliveriesInput" Content="Unterlieferungen durch Abzug bei "besseren" Flächenbindungen vermeiden"
|
||||||
|
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,30,10,10"
|
||||||
|
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
|
||||||
|
<CheckBox x:Name="ParameterHonorGebundenInput" Margin="10,50,10,10" VerticalAlignment="Top"
|
||||||
|
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed">
|
||||||
|
<TextBlock>Bei Lieferungen das Feld <Italic>Gebunden</Italic> berücksichtigen</TextBlock>
|
||||||
|
</CheckBox>
|
||||||
|
</Grid>
|
||||||
|
</GroupBox>
|
||||||
|
</Grid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem Header="Textelemente">
|
<TabItem Header="Textelemente">
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Visible">
|
<ScrollViewer VerticalScrollBarVisibility="Visible">
|
||||||
@ -559,15 +586,31 @@
|
|||||||
|
|
||||||
<Button x:Name="EditButton" Content="Bearbeiten"
|
<Button x:Name="EditButton" Content="Bearbeiten"
|
||||||
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,10" Width="120"
|
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,10" Width="120"
|
||||||
Click="EditButton_Click"/>
|
Click="EditButton_Click">
|
||||||
|
<Button.ToolTip>
|
||||||
|
<TextBlock FontWeight="Bold">Strg+B</TextBlock>
|
||||||
|
</Button.ToolTip>
|
||||||
|
</Button>
|
||||||
<Button x:Name="SaveButton" Content="Speichern" IsEnabled="False"
|
<Button x:Name="SaveButton" Content="Speichern" IsEnabled="False"
|
||||||
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,250,10" Width="120"
|
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,250,10" Width="120"
|
||||||
Click="SaveButton_Click"/>
|
Click="SaveButton_Click">
|
||||||
|
<Button.ToolTip>
|
||||||
|
<TextBlock FontWeight="Bold">Strg+S</TextBlock>
|
||||||
|
</Button.ToolTip>
|
||||||
|
</Button>
|
||||||
<Button x:Name="ResetButton" Content="Zurücksetzen" IsEnabled="False" Visibility="Hidden"
|
<Button x:Name="ResetButton" Content="Zurücksetzen" IsEnabled="False" Visibility="Hidden"
|
||||||
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,10" Width="120"
|
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,10" Width="120"
|
||||||
Click="ResetButton_Click"/>
|
Click="ResetButton_Click">
|
||||||
|
<Button.ToolTip>
|
||||||
|
<TextBlock FontWeight="Bold">Strg+Z</TextBlock>
|
||||||
|
</Button.ToolTip>
|
||||||
|
</Button>
|
||||||
<Button x:Name="CancelButton" Content="Abbrechen" IsEnabled="False" IsCancel="True"
|
<Button x:Name="CancelButton" Content="Abbrechen" IsEnabled="False" IsCancel="True"
|
||||||
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="250,0,0,10" Width="120"
|
HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="250,0,0,10" Width="120"
|
||||||
Click="CancelButton_Click"/>
|
Click="CancelButton_Click">
|
||||||
|
<Button.ToolTip>
|
||||||
|
<TextBlock FontWeight="Bold">Esc</TextBlock>
|
||||||
|
</Button.ToolTip>
|
||||||
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
</local:AdministrationWindow>
|
</local:AdministrationWindow>
|
||||||
|
@ -18,16 +18,24 @@ namespace Elwig.Windows {
|
|||||||
private bool _actChanged = false;
|
private bool _actChanged = false;
|
||||||
private bool _actUpdate = false;
|
private bool _actUpdate = false;
|
||||||
|
|
||||||
private void AreaCommitmentTypesInitEditing() {
|
private async Task AreaCommitmentTypesInitEditing(AppDbContext ctx) {
|
||||||
_actList = new(Context.AreaCommitmentTypes.OrderBy(v => v.VtrgId).ToList());
|
_actList = new(await ctx.AreaCommitmentTypes
|
||||||
|
.OrderBy(v => v.VtrgId)
|
||||||
|
.Include(t => t.WineVar)
|
||||||
|
.Include(t => t.WineAttr)
|
||||||
|
.ToListAsync());
|
||||||
_acts = _actList.ToDictionary(v => v.VtrgId, v => (string?)v.VtrgId);
|
_acts = _actList.ToDictionary(v => v.VtrgId, v => (string?)v.VtrgId);
|
||||||
_actIds = _actList.ToDictionary(v => v, v => v.VtrgId);
|
_actIds = _actList.ToDictionary(v => v, v => v.VtrgId);
|
||||||
ControlUtils.RenewItemsSource(AreaCommitmentTypeList, _actList, a => (a as AreaComType)?.VtrgId);
|
ControlUtils.RenewItemsSource(AreaCommitmentTypeList, _actList);
|
||||||
AreaCommitmentTypeList_SelectionChanged(null, null);
|
AreaCommitmentTypeList_SelectionChanged(null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AreaCommitmentTypesFinishEditing() {
|
private async Task AreaCommitmentTypesFinishEditing(AppDbContext ctx) {
|
||||||
ControlUtils.RenewItemsSource(AreaCommitmentTypeList, Context.AreaCommitmentTypes.OrderBy(v => v.SortId).ToList(), v => (v as AreaComType)?.VtrgId);
|
ControlUtils.RenewItemsSource(AreaCommitmentTypeList, await ctx.AreaCommitmentTypes
|
||||||
|
.OrderBy(v => v.VtrgId)
|
||||||
|
.Include(t => t.WineVar)
|
||||||
|
.Include(t => t.WineAttr)
|
||||||
|
.ToListAsync());
|
||||||
_actList = null;
|
_actList = null;
|
||||||
_acts = null;
|
_acts = null;
|
||||||
_actIds = null;
|
_actIds = null;
|
||||||
@ -37,31 +45,31 @@ namespace Elwig.Windows {
|
|||||||
AreaCommitmentTypeDeleteButton.IsEnabled = false;
|
AreaCommitmentTypeDeleteButton.IsEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AreaCommitmentTypesSave() {
|
private async Task AreaCommitmentTypesSave(AppDbContext ctx) {
|
||||||
if (!_actChanged || _actList == null || _acts == null || _actIds == null)
|
if (!_actChanged || _actList == null || _acts == null || _actIds == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var (vtrgid, _) in _acts.Where(a => a.Value == null)) {
|
foreach (var (vtrgid, _) in _acts.Where(a => a.Value == null)) {
|
||||||
Context.Remove(Context.AreaCommitmentTypes.Find(vtrgid));
|
ctx.Remove(ctx.AreaCommitmentTypes.Find(vtrgid)!);
|
||||||
}
|
}
|
||||||
foreach (var (attr, old) in _actIds) {
|
foreach (var (attr, old) in _actIds) {
|
||||||
attr.VtrgId = old;
|
attr.VtrgId = old;
|
||||||
}
|
}
|
||||||
foreach (var (old, vtrgid) in _acts.Where(a => a.Value != null)) {
|
foreach (var (old, vtrgid) in _acts.Where(a => a.Value != null)) {
|
||||||
Context.Update(Context.AreaCommitmentTypes.Find(old));
|
ctx.Update(ctx.AreaCommitmentTypes.Find(old)!);
|
||||||
}
|
}
|
||||||
await Context.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
|
|
||||||
foreach (var (old, vtrgid) in _acts.Where(a => a.Value != null)) {
|
foreach (var (old, vtrgid) in _acts.Where(a => a.Value != null)) {
|
||||||
await Context.Database.ExecuteSqlAsync($"UPDATE area_commitment_type SET vtrgid = {vtrgid} WHERE vtrgid = {old}");
|
await ctx.Database.ExecuteSqlAsync($"UPDATE area_commitment_type SET vtrgid = {vtrgid} WHERE vtrgid = {old}");
|
||||||
}
|
}
|
||||||
await Context.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
|
|
||||||
foreach (var type in _actList.Where(a => !_actIds.ContainsKey(a))) {
|
foreach (var type in _actList.Where(a => !_actIds.ContainsKey(a))) {
|
||||||
if (type.VtrgId == null) continue;
|
if (type.VtrgId == null) continue;
|
||||||
await Context.AddAsync(type);
|
ctx.Add(type);
|
||||||
}
|
}
|
||||||
await Context.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AreaCommitmentTypeList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
|
private void AreaCommitmentTypeList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
|
||||||
@ -69,8 +77,8 @@ namespace Elwig.Windows {
|
|||||||
_actUpdate = true;
|
_actUpdate = true;
|
||||||
if (AreaCommitmentTypeList.SelectedItem is AreaComType type) {
|
if (AreaCommitmentTypeList.SelectedItem is AreaComType type) {
|
||||||
AreaCommitmentTypeIdInput.Text = $"{type.SortId}{type.AttrId}";
|
AreaCommitmentTypeIdInput.Text = $"{type.SortId}{type.AttrId}";
|
||||||
ControlUtils.SelectComboBoxItem(AreaCommitmentTypeWineVariantInput, s => (s as WineVar)?.SortId, type.SortId);
|
ControlUtils.SelectItemWithPk(AreaCommitmentTypeWineVariantInput, type.SortId);
|
||||||
ControlUtils.SelectComboBoxItem(AreaCommitmentTypeWineAttributeInput, a => (a as WineAttr)?.AttrId, type.AttrId);
|
ControlUtils.SelectItemWithPk(AreaCommitmentTypeWineAttributeInput, type.AttrId);
|
||||||
AreaCommitmentTypeMinKgPerHaInput.Text = $"{type.MinKgPerHa}";
|
AreaCommitmentTypeMinKgPerHaInput.Text = $"{type.MinKgPerHa}";
|
||||||
AreaCommitmentTypePenaltyPerKgInput.Text = $"{type.PenaltyPerKg}";
|
AreaCommitmentTypePenaltyPerKgInput.Text = $"{type.PenaltyPerKg}";
|
||||||
AreaCommitmentTypePenaltyInput.Text = $"{type.PenaltyAmount}";
|
AreaCommitmentTypePenaltyInput.Text = $"{type.PenaltyAmount}";
|
||||||
@ -90,7 +98,7 @@ namespace Elwig.Windows {
|
|||||||
private void AreaCommitmentTypeAddButton_Click(object sender, RoutedEventArgs evt) {
|
private void AreaCommitmentTypeAddButton_Click(object sender, RoutedEventArgs evt) {
|
||||||
if (_actList == null) return;
|
if (_actList == null) return;
|
||||||
_actChanged = true;
|
_actChanged = true;
|
||||||
var item = Context.CreateProxy<AreaComType>();
|
var item = new AreaComType { VtrgId = "", SortId = "" };
|
||||||
_actList.Add(item);
|
_actList.Add(item);
|
||||||
AreaCommitmentTypeList.SelectedItem = item;
|
AreaCommitmentTypeList.SelectedItem = item;
|
||||||
UpdateButtons();
|
UpdateButtons();
|
||||||
|
@ -18,16 +18,22 @@ namespace Elwig.Windows {
|
|||||||
private bool _branchChanged = false;
|
private bool _branchChanged = false;
|
||||||
private bool _branchUpdate = false;
|
private bool _branchUpdate = false;
|
||||||
|
|
||||||
private void BranchesInitEditing() {
|
private async Task BranchesInitEditing(AppDbContext ctx) {
|
||||||
_branchList = new(Context.Branches.OrderBy(b => b.Name).ToList());
|
_branchList = new(await ctx.Branches
|
||||||
|
.OrderBy(b => b.Name)
|
||||||
|
.Include(b => b.PostalDest!.AtPlz)
|
||||||
|
.ToListAsync());
|
||||||
_branches = _branchList.ToDictionary(b => b.ZwstId, b => (string?)b.ZwstId);
|
_branches = _branchList.ToDictionary(b => b.ZwstId, b => (string?)b.ZwstId);
|
||||||
_branchIds = _branchList.ToDictionary(b => b, b => b.ZwstId);
|
_branchIds = _branchList.ToDictionary(b => b, b => b.ZwstId);
|
||||||
ControlUtils.RenewItemsSource(BranchList, _branchList, b => (b as Branch)?.ZwstId);
|
ControlUtils.RenewItemsSource(BranchList, _branchList);
|
||||||
BranchList_SelectionChanged(null, null);
|
BranchList_SelectionChanged(null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BranchesFinishEditing() {
|
private async Task BranchesFinishEditing(AppDbContext ctx) {
|
||||||
ControlUtils.RenewItemsSource(BranchList, Context.Branches.OrderBy(b => b.Name).ToList(), b => (b as Branch)?.ZwstId);
|
ControlUtils.RenewItemsSource(BranchList, await ctx.Branches
|
||||||
|
.OrderBy(b => b.Name)
|
||||||
|
.Include(b => b.PostalDest!.AtPlz)
|
||||||
|
.ToListAsync());
|
||||||
_branchList = null;
|
_branchList = null;
|
||||||
_branches = null;
|
_branches = null;
|
||||||
_branchIds = null;
|
_branchIds = null;
|
||||||
@ -37,31 +43,31 @@ namespace Elwig.Windows {
|
|||||||
BranchDeleteButton.IsEnabled = false;
|
BranchDeleteButton.IsEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task BranchesSave() {
|
private async Task BranchesSave(AppDbContext ctx) {
|
||||||
if (!_branchChanged || _branchList == null || _branches == null || _branchIds == null)
|
if (!_branchChanged || _branchList == null || _branches == null || _branchIds == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var (zwstid, _) in _branches.Where(b => b.Value == null)) {
|
foreach (var (zwstid, _) in _branches.Where(b => b.Value == null)) {
|
||||||
Context.Remove(Context.Branches.Find(zwstid));
|
ctx.Remove(ctx.Branches.Find(zwstid)!);
|
||||||
}
|
}
|
||||||
foreach (var (branch, old) in _branchIds) {
|
foreach (var (branch, old) in _branchIds) {
|
||||||
branch.ZwstId = old;
|
branch.ZwstId = old;
|
||||||
}
|
}
|
||||||
foreach (var (old, zwstid) in _branches.Where(b => b.Value != null)) {
|
foreach (var (old, zwstid) in _branches.Where(b => b.Value != null)) {
|
||||||
Context.Update(Context.Branches.Find(old));
|
ctx.Update(ctx.Branches.Find(old)!);
|
||||||
}
|
}
|
||||||
await Context.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
|
|
||||||
foreach (var (old, zwstid) in _branches.Where(b => b.Value != null)) {
|
foreach (var (old, zwstid) in _branches.Where(b => b.Value != null)) {
|
||||||
await Context.Database.ExecuteSqlAsync($"UPDATE branch SET zwstid = {zwstid} WHERE zwstid = {old}");
|
await ctx.Database.ExecuteSqlAsync($"UPDATE branch SET zwstid = {zwstid} WHERE zwstid = {old}");
|
||||||
}
|
}
|
||||||
await Context.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
|
|
||||||
foreach (var branch in _branchList.Where(b => !_branchIds.ContainsKey(b))) {
|
foreach (var branch in _branchList.Where(b => !_branchIds.ContainsKey(b))) {
|
||||||
if (branch.ZwstId == null) continue;
|
if (branch.ZwstId == null) continue;
|
||||||
await Context.AddAsync(branch);
|
ctx.Add(branch);
|
||||||
}
|
}
|
||||||
await Context.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BranchList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
|
private void BranchList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
|
||||||
@ -79,7 +85,7 @@ namespace Elwig.Windows {
|
|||||||
BranchIdInput.Text = branch.ZwstId;
|
BranchIdInput.Text = branch.ZwstId;
|
||||||
BranchNameInput.Text = branch.Name;
|
BranchNameInput.Text = branch.Name;
|
||||||
BranchPlzInput.Text = branch.PostalDest?.AtPlz?.Plz.ToString() ?? "";
|
BranchPlzInput.Text = branch.PostalDest?.AtPlz?.Plz.ToString() ?? "";
|
||||||
ControlUtils.SelectComboBoxItem(BranchOrtInput, o => (o as AT_PlzDest)?.Okz, branch.PostalDest?.AtPlz?.Okz);
|
ControlUtils.SelectItem(BranchOrtInput, branch.PostalDest?.AtPlz);
|
||||||
BranchAddressInput.Text = branch.Address;
|
BranchAddressInput.Text = branch.Address;
|
||||||
BranchPhoneNrInput.Text = branch.PhoneNr;
|
BranchPhoneNrInput.Text = branch.PhoneNr;
|
||||||
BranchFaxNrInput.Text = branch.FaxNr;
|
BranchFaxNrInput.Text = branch.FaxNr;
|
||||||
@ -91,7 +97,7 @@ namespace Elwig.Windows {
|
|||||||
private void BranchAddButton_Click(object sender, RoutedEventArgs evt) {
|
private void BranchAddButton_Click(object sender, RoutedEventArgs evt) {
|
||||||
if (_branchList == null) return;
|
if (_branchList == null) return;
|
||||||
_branchChanged = true;
|
_branchChanged = true;
|
||||||
var item = Context.CreateProxy<Branch>();
|
var item = new Branch { ZwstId = "", Name = "" };
|
||||||
_branchList.Add(item);
|
_branchList.Add(item);
|
||||||
BranchList.SelectedItem = item;
|
BranchList.SelectedItem = item;
|
||||||
UpdateButtons();
|
UpdateButtons();
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Elwig.Helpers;
|
using Elwig.Helpers;
|
||||||
using Elwig.Models.Entities;
|
using Elwig.Models.Entities;
|
||||||
|
using LinqKit;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
@ -18,19 +19,25 @@ namespace Elwig.Windows {
|
|||||||
private bool _modChanged = false;
|
private bool _modChanged = false;
|
||||||
private bool _modUpdate = false;
|
private bool _modUpdate = false;
|
||||||
|
|
||||||
private void ModifiersInitEditing() {
|
private async Task ModifiersInitEditing(AppDbContext ctx) {
|
||||||
SeasonList.IsEnabled = false;
|
SeasonList.IsEnabled = false;
|
||||||
var year = (SeasonList.SelectedItem as Season)?.Year;
|
var year = (SeasonList.SelectedItem as Season)?.Year;
|
||||||
_modList = new(Context.Modifiers.Where(m => m.Year == year).OrderBy(m => m.Ordering).ToList());
|
_modList = new(await ctx.Modifiers
|
||||||
|
.Where(m => m.Year == year)
|
||||||
|
.OrderBy(m => m.Ordering)
|
||||||
|
.ToListAsync());
|
||||||
_mods = _modList.ToDictionary(m => m.ModId, m => (string?)m.ModId);
|
_mods = _modList.ToDictionary(m => m.ModId, m => (string?)m.ModId);
|
||||||
_modIds = _modList.ToDictionary(m => m, m => m.ModId);
|
_modIds = _modList.ToDictionary(m => m, m => m.ModId);
|
||||||
ControlUtils.RenewItemsSource(SeasonModifierList, _modList, m => (m as Modifier)?.ModId);
|
ControlUtils.RenewItemsSource(SeasonModifierList, _modList);
|
||||||
SeasonModifierList_SelectionChanged(null, null);
|
SeasonModifierList_SelectionChanged(null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ModifiersFinishEditing() {
|
private async Task ModifiersFinishEditing(AppDbContext ctx) {
|
||||||
var year = (SeasonList.SelectedItem as Season)?.Year;
|
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);
|
ControlUtils.RenewItemsSource(SeasonModifierList, await ctx.Modifiers
|
||||||
|
.Where(m => m.Year == year)
|
||||||
|
.OrderBy(m => m.Ordering)
|
||||||
|
.ToListAsync());
|
||||||
_modList = null;
|
_modList = null;
|
||||||
_mods = null;
|
_mods = null;
|
||||||
_modIds = null;
|
_modIds = null;
|
||||||
@ -43,7 +50,7 @@ namespace Elwig.Windows {
|
|||||||
SeasonList.IsEnabled = true;
|
SeasonList.IsEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ModifiersSave() {
|
private async Task ModifiersSave(AppDbContext ctx) {
|
||||||
if (!_modChanged || _modList == null || _mods == null || _modIds == null)
|
if (!_modChanged || _modList == null || _mods == null || _modIds == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -52,26 +59,26 @@ namespace Elwig.Windows {
|
|||||||
|
|
||||||
var year = (SeasonList.SelectedItem as Season)?.Year;
|
var year = (SeasonList.SelectedItem as Season)?.Year;
|
||||||
foreach (var (modid, _) in _mods.Where(m => m.Value == null)) {
|
foreach (var (modid, _) in _mods.Where(m => m.Value == null)) {
|
||||||
Context.Remove(Context.Modifiers.Find(year, modid)!);
|
ctx.Remove(ctx.Modifiers.Find(year, modid)!);
|
||||||
}
|
}
|
||||||
foreach (var (mod, old) in _modIds) {
|
foreach (var (mod, old) in _modIds) {
|
||||||
mod.ModId = old;
|
mod.ModId = old;
|
||||||
}
|
}
|
||||||
foreach (var (old, modid) in _mods.Where(m => m.Value != null)) {
|
foreach (var (old, modid) in _mods.Where(m => m.Value != null)) {
|
||||||
Context.Update(Context.Modifiers.Find(year, old)!);
|
ctx.Update(ctx.Modifiers.Find(year, old)!);
|
||||||
}
|
}
|
||||||
await Context.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
|
|
||||||
foreach (var (old, modid) in _mods.Where(m => m.Value != null)) {
|
foreach (var (old, modid) in _mods.Where(m => m.Value != null)) {
|
||||||
await Context.Database.ExecuteSqlAsync($"UPDATE modifier SET modid = {modid} WHERE (year, modid) = ({year}, {old})");
|
await ctx.Database.ExecuteSqlAsync($"UPDATE modifier SET modid = {modid} WHERE (year, modid) = ({year}, {old})");
|
||||||
}
|
}
|
||||||
await Context.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
|
|
||||||
foreach (var mod in _modList.Where(m => !_modIds.ContainsKey(m))) {
|
foreach (var mod in _modList.Where(m => !_modIds.ContainsKey(m))) {
|
||||||
if (mod.ModId == null) continue;
|
if (mod.ModId == null) continue;
|
||||||
await Context.AddAsync(mod);
|
await ctx.AddAsync(mod);
|
||||||
}
|
}
|
||||||
await Context.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SeasonModifierUpButton_Click(object sender, RoutedEventArgs evt) {
|
private void SeasonModifierUpButton_Click(object sender, RoutedEventArgs evt) {
|
||||||
@ -102,11 +109,7 @@ namespace Elwig.Windows {
|
|||||||
if (_modList == null || SeasonList.SelectedItem is not Season s) return;
|
if (_modList == null || SeasonList.SelectedItem is not Season s) return;
|
||||||
_modChanged = true;
|
_modChanged = true;
|
||||||
var idx = (SeasonModifierList.SelectedIndex != -1) ? SeasonModifierList.SelectedIndex + 1 : _modList.Count;
|
var idx = (SeasonModifierList.SelectedIndex != -1) ? SeasonModifierList.SelectedIndex + 1 : _modList.Count;
|
||||||
var item = new Modifier {
|
var item = new Modifier { Year = s.Year, ModId = "", Name = "" };
|
||||||
Year = s.Year,
|
|
||||||
ModId = "",
|
|
||||||
Name = "",
|
|
||||||
};
|
|
||||||
_modList.Insert(idx, item);
|
_modList.Insert(idx, item);
|
||||||
SeasonModifierList.SelectedIndex = idx;
|
SeasonModifierList.SelectedIndex = idx;
|
||||||
UpdateButtons();
|
UpdateButtons();
|
||||||
|
@ -12,27 +12,33 @@ namespace Elwig.Windows {
|
|||||||
private bool _seasonChanged = false;
|
private bool _seasonChanged = false;
|
||||||
private bool _seasonUpdate = false;
|
private bool _seasonUpdate = false;
|
||||||
|
|
||||||
private void SeasonsInitEditing() {
|
private async Task SeasonsInitEditing(AppDbContext ctx) {
|
||||||
ControlUtils.RenewItemsSource(SeasonList, Context.Seasons.OrderByDescending(s => s.Year).ToList(), s => (s as Season)?.Year);
|
ControlUtils.RenewItemsSource(SeasonList, await ctx.Seasons
|
||||||
|
.OrderByDescending(s => s.Year)
|
||||||
|
.Include(s => s.Modifiers)
|
||||||
|
.ToListAsync());
|
||||||
SeasonList_SelectionChanged(null, null);
|
SeasonList_SelectionChanged(null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SeasonsFinishEditing() {
|
private async Task SeasonsFinishEditing(AppDbContext ctx) {
|
||||||
ControlUtils.RenewItemsSource(SeasonList, Context.Seasons.OrderByDescending(s => s.Year).ToList(), s => (s as Season)?.Year);
|
ControlUtils.RenewItemsSource(SeasonList, await ctx.Seasons
|
||||||
|
.OrderByDescending(s => s.Year)
|
||||||
|
.Include(s => s.Modifiers)
|
||||||
|
.ToListAsync());
|
||||||
_seasonChanged = false;
|
_seasonChanged = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SeasonsSave() {
|
private async Task SeasonsSave(AppDbContext ctx) {
|
||||||
if (!_seasonChanged || SeasonList.SelectedItem is not Season s)
|
if (!_seasonChanged || SeasonList.SelectedItem is not Season s)
|
||||||
return;
|
return;
|
||||||
Context.Update(s);
|
ctx.Update(s);
|
||||||
await Context.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void SeasonList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
|
private void SeasonList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
|
||||||
_seasonUpdate = true;
|
_seasonUpdate = true;
|
||||||
if (SeasonList.SelectedItem is Season s) {
|
if (SeasonList.SelectedItem is Season s) {
|
||||||
SeasonModifierList.ItemsSource = await Context.Modifiers.Where(m => m.Year == s.Year).OrderBy(m => m.Ordering).ToListAsync();
|
SeasonModifierList.ItemsSource = s.Modifiers.OrderBy(m => m.Ordering).ToList();
|
||||||
SeasonMaxKgPerHaInput.Text = s.MaxKgPerHa.ToString();
|
SeasonMaxKgPerHaInput.Text = s.MaxKgPerHa.ToString();
|
||||||
SeasonVatNormalInput.Text = (s.VatNormal * 100).ToString();
|
SeasonVatNormalInput.Text = (s.VatNormal * 100).ToString();
|
||||||
SeasonVatFlatrateInput.Text = (s.VatFlatrate * 100).ToString();
|
SeasonVatFlatrateInput.Text = (s.VatFlatrate * 100).ToString();
|
||||||
|
@ -18,16 +18,20 @@ namespace Elwig.Windows {
|
|||||||
private bool _attrChanged = false;
|
private bool _attrChanged = false;
|
||||||
private bool _attrUpdate = false;
|
private bool _attrUpdate = false;
|
||||||
|
|
||||||
private void WineAttributesInitEditing() {
|
private async Task WineAttributesInitEditing(AppDbContext ctx) {
|
||||||
_attrList = new(Context.WineAttributes.OrderBy(a => a.Name).ToList());
|
_attrList = new(await ctx.WineAttributes
|
||||||
|
.OrderBy(a => a.Name)
|
||||||
|
.ToListAsync());
|
||||||
_attrs = _attrList.ToDictionary(a => a.AttrId, a => (string?)a.AttrId);
|
_attrs = _attrList.ToDictionary(a => a.AttrId, a => (string?)a.AttrId);
|
||||||
_attrIds = _attrList.ToDictionary(a => a, a => a.AttrId);
|
_attrIds = _attrList.ToDictionary(a => a, a => a.AttrId);
|
||||||
ControlUtils.RenewItemsSource(WineAttributeList, _attrList, a => (a as WineAttr)?.AttrId);
|
ControlUtils.RenewItemsSource(WineAttributeList, _attrList);
|
||||||
WineAttributeList_SelectionChanged(null, null);
|
WineAttributeList_SelectionChanged(null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WineAttributesFinishEditing() {
|
private async Task WineAttributesFinishEditing(AppDbContext ctx) {
|
||||||
ControlUtils.RenewItemsSource(WineAttributeList, Context.WineAttributes.OrderBy(a => a.Name).ToList(), a => (a as WineAttr)?.AttrId);
|
ControlUtils.RenewItemsSource(WineAttributeList, await ctx.WineAttributes
|
||||||
|
.OrderBy(a => a.Name)
|
||||||
|
.ToListAsync());
|
||||||
_attrList = null;
|
_attrList = null;
|
||||||
_attrs = null;
|
_attrs = null;
|
||||||
_attrIds = null;
|
_attrIds = null;
|
||||||
@ -37,31 +41,31 @@ namespace Elwig.Windows {
|
|||||||
WineAttributeDeleteButton.IsEnabled = false;
|
WineAttributeDeleteButton.IsEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task WineAttributesSave() {
|
private async Task WineAttributesSave(AppDbContext ctx) {
|
||||||
if (!_attrChanged || _attrList == null || _attrs == null || _attrIds == null)
|
if (!_attrChanged || _attrList == null || _attrs == null || _attrIds == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var (attrid, _) in _attrs.Where(a => a.Value == null)) {
|
foreach (var (attrid, _) in _attrs.Where(a => a.Value == null)) {
|
||||||
Context.Remove(Context.WineAttributes.Find(attrid));
|
ctx.Remove(ctx.WineAttributes.Find(attrid)!);
|
||||||
}
|
}
|
||||||
foreach (var (attr, old) in _attrIds) {
|
foreach (var (attr, old) in _attrIds) {
|
||||||
attr.AttrId = old;
|
attr.AttrId = old;
|
||||||
}
|
}
|
||||||
foreach (var (old, attrid) in _attrs.Where(a => a.Value != null)) {
|
foreach (var (old, attrid) in _attrs.Where(a => a.Value != null)) {
|
||||||
Context.Update(Context.WineAttributes.Find(old));
|
ctx.Update(ctx.WineAttributes.Find(old)!);
|
||||||
}
|
}
|
||||||
await Context.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
|
|
||||||
foreach (var (old, attrid) in _attrs.Where(a => a.Value != null)) {
|
foreach (var (old, attrid) in _attrs.Where(a => a.Value != null)) {
|
||||||
await Context.Database.ExecuteSqlAsync($"UPDATE wine_attribute SET attrid = {attrid} WHERE attrid = {old}");
|
await ctx.Database.ExecuteSqlAsync($"UPDATE wine_attribute SET attrid = {attrid} WHERE attrid = {old}");
|
||||||
}
|
}
|
||||||
await Context.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
|
|
||||||
foreach (var attr in _attrList.Where(a => !_attrIds.ContainsKey(a))) {
|
foreach (var attr in _attrList.Where(a => !_attrIds.ContainsKey(a))) {
|
||||||
if (attr.AttrId == null) continue;
|
if (attr.AttrId == null) continue;
|
||||||
await Context.AddAsync(attr);
|
ctx.Add(attr);
|
||||||
}
|
}
|
||||||
await Context.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WineAttributeList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
|
private void WineAttributeList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
|
||||||
@ -88,7 +92,7 @@ namespace Elwig.Windows {
|
|||||||
private void WineAttributeAddButton_Click(object sender, RoutedEventArgs evt) {
|
private void WineAttributeAddButton_Click(object sender, RoutedEventArgs evt) {
|
||||||
if (_attrList == null) return;
|
if (_attrList == null) return;
|
||||||
_attrChanged = true;
|
_attrChanged = true;
|
||||||
var item = Context.CreateProxy<WineAttr>();
|
var item = new WineAttr { AttrId = "", Name = "" };
|
||||||
_attrList.Add(item);
|
_attrList.Add(item);
|
||||||
WineAttributeList.SelectedItem = item;
|
WineAttributeList.SelectedItem = item;
|
||||||
UpdateButtons();
|
UpdateButtons();
|
||||||
|
@ -18,16 +18,20 @@ namespace Elwig.Windows {
|
|||||||
private bool _cultChanged = false;
|
private bool _cultChanged = false;
|
||||||
private bool _cultUpdate = false;
|
private bool _cultUpdate = false;
|
||||||
|
|
||||||
private void WineCultivationsInitEditing() {
|
private async Task WineCultivationsInitEditing(AppDbContext ctx) {
|
||||||
_cultList = new(Context.WineCultivations.OrderBy(c => c.Name).ToList());
|
_cultList = new(await ctx.WineCultivations
|
||||||
|
.OrderBy(c => c.Name)
|
||||||
|
.ToListAsync());
|
||||||
_cults = _cultList.ToDictionary(c => c.CultId, c => (string?)c.CultId);
|
_cults = _cultList.ToDictionary(c => c.CultId, c => (string?)c.CultId);
|
||||||
_cultIds = _cultList.ToDictionary(c => c, c => c.CultId);
|
_cultIds = _cultList.ToDictionary(c => c, c => c.CultId);
|
||||||
ControlUtils.RenewItemsSource(WineCultivationList, _cultList, c => (c as WineCult)?.CultId);
|
ControlUtils.RenewItemsSource(WineCultivationList, _cultList);
|
||||||
WineCultivationList_SelectionChanged(null, null);
|
WineCultivationList_SelectionChanged(null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WineCultivationsFinishEditing() {
|
private async Task WineCultivationsFinishEditing(AppDbContext ctx) {
|
||||||
ControlUtils.RenewItemsSource(WineCultivationList, Context.WineCultivations.OrderBy(c => c.Name).ToList(), c => (c as WineCult)?.CultId);
|
ControlUtils.RenewItemsSource(WineCultivationList, await ctx.WineCultivations
|
||||||
|
.OrderBy(c => c.Name)
|
||||||
|
.ToListAsync());
|
||||||
_cultList = null;
|
_cultList = null;
|
||||||
_cults = null;
|
_cults = null;
|
||||||
_cultIds = null;
|
_cultIds = null;
|
||||||
@ -37,31 +41,31 @@ namespace Elwig.Windows {
|
|||||||
WineCultivationDeleteButton.IsEnabled = false;
|
WineCultivationDeleteButton.IsEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task WineCultivationsSave() {
|
private async Task WineCultivationsSave(AppDbContext ctx) {
|
||||||
if (!_cultChanged || _cultList == null || _cults == null || _cultIds == null)
|
if (!_cultChanged || _cultList == null || _cults == null || _cultIds == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var (cultid, _) in _cults.Where(c => c.Value == null)) {
|
foreach (var (cultid, _) in _cults.Where(c => c.Value == null)) {
|
||||||
Context.Remove(Context.WineCultivations.Find(cultid));
|
ctx.Remove(ctx.WineCultivations.Find(cultid)!);
|
||||||
}
|
}
|
||||||
foreach (var (cult, old) in _cultIds) {
|
foreach (var (cult, old) in _cultIds) {
|
||||||
cult.CultId = old;
|
cult.CultId = old;
|
||||||
}
|
}
|
||||||
foreach (var (old, cultid) in _cults.Where(c => c.Value != null)) {
|
foreach (var (old, cultid) in _cults.Where(c => c.Value != null)) {
|
||||||
Context.Update(Context.WineCultivations.Find(old));
|
ctx.Update(ctx.WineCultivations.Find(old)!);
|
||||||
}
|
}
|
||||||
await Context.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
|
|
||||||
foreach (var (old, cultid) in _cults.Where(c => c.Value != null)) {
|
foreach (var (old, cultid) in _cults.Where(c => c.Value != null)) {
|
||||||
await Context.Database.ExecuteSqlAsync($"UPDATE wine_cultivation SET cultid = {cultid} WHERE cultid = {old}");
|
await ctx.Database.ExecuteSqlAsync($"UPDATE wine_cultivation SET cultid = {cultid} WHERE cultid = {old}");
|
||||||
}
|
}
|
||||||
await Context.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
|
|
||||||
foreach (var cult in _cultList.Where(c => !_cultIds.ContainsKey(c))) {
|
foreach (var cult in _cultList.Where(c => !_cultIds.ContainsKey(c))) {
|
||||||
if (cult.CultId == null) continue;
|
if (cult.CultId == null) continue;
|
||||||
await Context.AddAsync(cult);
|
ctx.Add(cult);
|
||||||
}
|
}
|
||||||
await Context.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WineCultivationList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
|
private void WineCultivationList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
|
||||||
@ -82,7 +86,7 @@ namespace Elwig.Windows {
|
|||||||
private void WineCultivationAddButton_Click(object sender, RoutedEventArgs evt) {
|
private void WineCultivationAddButton_Click(object sender, RoutedEventArgs evt) {
|
||||||
if (_cultList == null) return;
|
if (_cultList == null) return;
|
||||||
_cultChanged = true;
|
_cultChanged = true;
|
||||||
var item = Context.CreateProxy<WineCult>();
|
var item = new WineCult { CultId = "", Name = "" };
|
||||||
_cultList.Add(item);
|
_cultList.Add(item);
|
||||||
WineCultivationList.SelectedItem = item;
|
WineCultivationList.SelectedItem = item;
|
||||||
UpdateButtons();
|
UpdateButtons();
|
||||||
|
@ -10,6 +10,8 @@ using System.Windows.Controls;
|
|||||||
namespace Elwig.Windows {
|
namespace Elwig.Windows {
|
||||||
public partial class BaseDataWindow : AdministrationWindow {
|
public partial class BaseDataWindow : AdministrationWindow {
|
||||||
|
|
||||||
|
protected AppDbContext? EditContext;
|
||||||
|
|
||||||
public BaseDataWindow() {
|
public BaseDataWindow() {
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
RequiredInputs = [
|
RequiredInputs = [
|
||||||
@ -21,20 +23,25 @@ namespace Elwig.Windows {
|
|||||||
BranchIdInput, BranchNameInput, BranchPlzInput, BranchOrtInput,
|
BranchIdInput, BranchNameInput, BranchPlzInput, BranchOrtInput,
|
||||||
BranchAddressInput, BranchPhoneNrInput, BranchFaxNrInput, BranchMobileNrInput,
|
BranchAddressInput, BranchPhoneNrInput, BranchFaxNrInput, BranchMobileNrInput,
|
||||||
WineAttributeIdInput, WineAttributeNameInput, WineAttributeActiveInput,
|
WineAttributeIdInput, WineAttributeNameInput, WineAttributeActiveInput,
|
||||||
WineAttributeMaxKgPerHaInput.TextBox, WineAttributeStrictInput, WineAttributeFillLowerInput,
|
WineAttributeMaxKgPerHaInput, WineAttributeStrictInput, WineAttributeFillLowerInput,
|
||||||
WineCultivationIdInput, WineCultivationNameInput, WineCultivationDescriptionInput,
|
WineCultivationIdInput, WineCultivationNameInput, WineCultivationDescriptionInput,
|
||||||
AreaCommitmentTypeIdInput, AreaCommitmentTypeWineVariantInput, AreaCommitmentTypeWineAttributeInput,
|
AreaCommitmentTypeIdInput, AreaCommitmentTypeWineVariantInput, AreaCommitmentTypeWineAttributeInput,
|
||||||
AreaCommitmentTypeMinKgPerHaInput.TextBox, AreaCommitmentTypePenaltyPerKgInput.TextBox,
|
AreaCommitmentTypeMinKgPerHaInput, AreaCommitmentTypePenaltyPerKgInput,
|
||||||
AreaCommitmentTypePenaltyInput.TextBox, AreaCommitmentTypePenaltyNoneInput.TextBox,
|
AreaCommitmentTypePenaltyInput, AreaCommitmentTypePenaltyNoneInput,
|
||||||
SeasonMaxKgPerHaInput.TextBox, SeasonVatNormalInput.TextBox, SeasonVatFlatrateInput.TextBox, SeasonStartInput, SeasonEndInput,
|
SeasonMaxKgPerHaInput, SeasonVatNormalInput, SeasonVatFlatrateInput, SeasonStartInput, SeasonEndInput,
|
||||||
SeasonMinKgPerBsInput.TextBox, SeasonMaxKgPerBsInput.TextBox, SeasonBsValueInput.TextBox,
|
SeasonMinKgPerBsInput, SeasonMaxKgPerBsInput, SeasonBsValueInput,
|
||||||
SeasonPenaltyPerKgInput.TextBox, SeasonPenaltyInput.TextBox, SeasonPenaltyNoneInput.TextBox,
|
SeasonPenaltyPerKgInput, SeasonPenaltyInput, SeasonPenaltyNoneInput,
|
||||||
SeasonModifierIdInput, SeasonModifierNameInput, SeasonModifierRelInput.TextBox, SeasonModifierAbsInput.TextBox,
|
SeasonModifierIdInput, SeasonModifierNameInput, SeasonModifierRelInput, SeasonModifierAbsInput,
|
||||||
];
|
];
|
||||||
WineAttributeFillLowerInput.Visibility = Visibility.Hidden;
|
WineAttributeFillLowerInput.Visibility = Visibility.Hidden;
|
||||||
WineAttributeFillLowerLabel.Visibility = Visibility.Hidden;
|
WineAttributeFillLowerLabel.Visibility = Visibility.Hidden;
|
||||||
|
ParameterAreaComGroup.Header = ParameterAreaComGroup.Header.ToString()!.Split('(')[0] + $"({Utils.CurrentLastSeason})";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void ShortcutNew() { }
|
||||||
|
|
||||||
|
protected override void ShortcutDelete() { }
|
||||||
|
|
||||||
new protected void LockInputs() {
|
new protected void LockInputs() {
|
||||||
base.LockInputs();
|
base.LockInputs();
|
||||||
|
|
||||||
@ -50,7 +57,7 @@ namespace Elwig.Windows {
|
|||||||
WineAttributeIdInput.IsReadOnly = true;
|
WineAttributeIdInput.IsReadOnly = true;
|
||||||
WineAttributeNameInput.IsReadOnly = true;
|
WineAttributeNameInput.IsReadOnly = true;
|
||||||
WineAttributeActiveInput.IsEnabled = false;
|
WineAttributeActiveInput.IsEnabled = false;
|
||||||
WineAttributeMaxKgPerHaInput.TextBox.IsReadOnly = true;
|
WineAttributeMaxKgPerHaInput.IsReadOnly = true;
|
||||||
WineAttributeStrictInput.IsEnabled = false;
|
WineAttributeStrictInput.IsEnabled = false;
|
||||||
WineAttributeFillLowerInput.IsEnabled = false;
|
WineAttributeFillLowerInput.IsEnabled = false;
|
||||||
|
|
||||||
@ -60,25 +67,29 @@ namespace Elwig.Windows {
|
|||||||
|
|
||||||
AreaCommitmentTypeWineVariantInput.IsEnabled = false;
|
AreaCommitmentTypeWineVariantInput.IsEnabled = false;
|
||||||
AreaCommitmentTypeWineAttributeInput.IsEnabled = false;
|
AreaCommitmentTypeWineAttributeInput.IsEnabled = false;
|
||||||
AreaCommitmentTypeMinKgPerHaInput.TextBox.IsReadOnly = true;
|
AreaCommitmentTypeMinKgPerHaInput.IsReadOnly = true;
|
||||||
AreaCommitmentTypePenaltyPerKgInput.TextBox.IsReadOnly = true;
|
AreaCommitmentTypePenaltyPerKgInput.IsReadOnly = true;
|
||||||
AreaCommitmentTypePenaltyInput.TextBox.IsReadOnly = true;
|
AreaCommitmentTypePenaltyInput.IsReadOnly = true;
|
||||||
AreaCommitmentTypePenaltyNoneInput.TextBox.IsReadOnly = true;
|
AreaCommitmentTypePenaltyNoneInput.IsReadOnly = true;
|
||||||
|
|
||||||
SeasonMaxKgPerHaInput.TextBox.IsReadOnly = true;
|
SeasonMaxKgPerHaInput.IsReadOnly = true;
|
||||||
SeasonVatNormalInput.TextBox.IsReadOnly = true;
|
SeasonVatNormalInput.IsReadOnly = true;
|
||||||
SeasonVatFlatrateInput.TextBox.IsReadOnly = true;
|
SeasonVatFlatrateInput.IsReadOnly = true;
|
||||||
SeasonMinKgPerBsInput.TextBox.IsReadOnly = true;
|
SeasonMinKgPerBsInput.IsReadOnly = true;
|
||||||
SeasonMaxKgPerBsInput.TextBox.IsReadOnly = true;
|
SeasonMaxKgPerBsInput.IsReadOnly = true;
|
||||||
SeasonPenaltyPerKgInput.TextBox.IsReadOnly = true;
|
SeasonPenaltyPerKgInput.IsReadOnly = true;
|
||||||
SeasonPenaltyInput.TextBox.IsReadOnly = true;
|
SeasonPenaltyInput.IsReadOnly = true;
|
||||||
SeasonPenaltyNoneInput.TextBox.IsReadOnly = true;
|
SeasonPenaltyNoneInput.IsReadOnly = true;
|
||||||
SeasonBsValueInput.TextBox.IsReadOnly = true;
|
SeasonBsValueInput.IsReadOnly = true;
|
||||||
|
|
||||||
SeasonModifierIdInput.IsReadOnly = true;
|
SeasonModifierIdInput.IsReadOnly = true;
|
||||||
SeasonModifierNameInput.IsReadOnly = true;
|
SeasonModifierNameInput.IsReadOnly = true;
|
||||||
SeasonModifierRelInput.TextBox.IsReadOnly = true;
|
SeasonModifierRelInput.IsReadOnly = true;
|
||||||
SeasonModifierAbsInput.TextBox.IsReadOnly = true;
|
SeasonModifierAbsInput.IsReadOnly = true;
|
||||||
|
|
||||||
|
ParameterAllowAttrIntoLowerInput.IsEnabled = false;
|
||||||
|
ParameterAvoidUnderDeliveriesInput.IsEnabled = false;
|
||||||
|
ParameterHonorGebundenInput.IsEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
new protected void UnlockInputs() {
|
new protected void UnlockInputs() {
|
||||||
@ -96,7 +107,7 @@ namespace Elwig.Windows {
|
|||||||
WineAttributeIdInput.IsReadOnly = false;
|
WineAttributeIdInput.IsReadOnly = false;
|
||||||
WineAttributeNameInput.IsReadOnly = false;
|
WineAttributeNameInput.IsReadOnly = false;
|
||||||
WineAttributeActiveInput.IsEnabled = true;
|
WineAttributeActiveInput.IsEnabled = true;
|
||||||
WineAttributeMaxKgPerHaInput.TextBox.IsReadOnly = false;
|
WineAttributeMaxKgPerHaInput.IsReadOnly = false;
|
||||||
WineAttributeStrictInput.IsEnabled = true;
|
WineAttributeStrictInput.IsEnabled = true;
|
||||||
WineAttributeFillLowerInput.IsEnabled = true;
|
WineAttributeFillLowerInput.IsEnabled = true;
|
||||||
|
|
||||||
@ -106,45 +117,68 @@ namespace Elwig.Windows {
|
|||||||
|
|
||||||
AreaCommitmentTypeWineVariantInput.IsEnabled = true;
|
AreaCommitmentTypeWineVariantInput.IsEnabled = true;
|
||||||
AreaCommitmentTypeWineAttributeInput.IsEnabled = true;
|
AreaCommitmentTypeWineAttributeInput.IsEnabled = true;
|
||||||
AreaCommitmentTypeMinKgPerHaInput.TextBox.IsReadOnly = false;
|
AreaCommitmentTypeMinKgPerHaInput.IsReadOnly = false;
|
||||||
AreaCommitmentTypePenaltyPerKgInput.TextBox.IsReadOnly = false;
|
AreaCommitmentTypePenaltyPerKgInput.IsReadOnly = false;
|
||||||
AreaCommitmentTypePenaltyInput.TextBox.IsReadOnly = false;
|
AreaCommitmentTypePenaltyInput.IsReadOnly = false;
|
||||||
AreaCommitmentTypePenaltyNoneInput.TextBox.IsReadOnly = false;
|
AreaCommitmentTypePenaltyNoneInput.IsReadOnly = false;
|
||||||
|
|
||||||
SeasonMaxKgPerHaInput.TextBox.IsReadOnly = false;
|
SeasonMaxKgPerHaInput.IsReadOnly = false;
|
||||||
SeasonVatNormalInput.TextBox.IsReadOnly = false;
|
SeasonVatNormalInput.IsReadOnly = false;
|
||||||
SeasonVatFlatrateInput.TextBox.IsReadOnly = false;
|
SeasonVatFlatrateInput.IsReadOnly = false;
|
||||||
SeasonMinKgPerBsInput.TextBox.IsReadOnly = false;
|
SeasonMinKgPerBsInput.IsReadOnly = false;
|
||||||
SeasonMaxKgPerBsInput.TextBox.IsReadOnly = false;
|
SeasonMaxKgPerBsInput.IsReadOnly = false;
|
||||||
SeasonPenaltyPerKgInput.TextBox.IsReadOnly = false;
|
SeasonPenaltyPerKgInput.IsReadOnly = false;
|
||||||
SeasonPenaltyInput.TextBox.IsReadOnly = false;
|
SeasonPenaltyInput.IsReadOnly = false;
|
||||||
SeasonPenaltyNoneInput.TextBox.IsReadOnly = false;
|
SeasonPenaltyNoneInput.IsReadOnly = false;
|
||||||
SeasonBsValueInput.TextBox.IsReadOnly = false;
|
SeasonBsValueInput.IsReadOnly = false;
|
||||||
|
|
||||||
SeasonModifierIdInput.IsReadOnly = false;
|
SeasonModifierIdInput.IsReadOnly = false;
|
||||||
SeasonModifierNameInput.IsReadOnly = false;
|
SeasonModifierNameInput.IsReadOnly = false;
|
||||||
SeasonModifierRelInput.TextBox.IsReadOnly = false;
|
SeasonModifierRelInput.IsReadOnly = false;
|
||||||
SeasonModifierAbsInput.TextBox.IsReadOnly = false;
|
SeasonModifierAbsInput.IsReadOnly = false;
|
||||||
|
|
||||||
|
ParameterAllowAttrIntoLowerInput.IsEnabled = true;
|
||||||
|
ParameterAvoidUnderDeliveriesInput.IsEnabled = true;
|
||||||
|
ParameterHonorGebundenInput.IsEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Window_Loaded(object sender, RoutedEventArgs evt) {
|
private void Window_Loaded(object sender, RoutedEventArgs evt) {
|
||||||
LockInputs();
|
LockInputs();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnRenewContext() {
|
protected override async Task OnRenewContext(AppDbContext ctx) {
|
||||||
await base.OnRenewContext();
|
await base.OnRenewContext(ctx);
|
||||||
FillInputs(App.Client);
|
FillInputs(App.Client, await ctx.Seasons.FindAsync(Utils.CurrentLastSeason));
|
||||||
ControlUtils.RenewItemsSource(SeasonList, await Context.Seasons.OrderByDescending(s => s.Year).ToListAsync(), s => (s as Season)?.Year, null, ControlUtils.RenewSourceDefault.First);
|
ControlUtils.RenewItemsSource(SeasonList, await ctx.Seasons
|
||||||
|
.OrderByDescending(s => s.Year)
|
||||||
|
.Include(s => s.Modifiers)
|
||||||
|
.ToListAsync(), null, ControlUtils.RenewSourceDefault.First);
|
||||||
var year = (SeasonList.SelectedItem as Season)?.Year;
|
var year = (SeasonList.SelectedItem as Season)?.Year;
|
||||||
ControlUtils.RenewItemsSource(BranchList, await Context.Branches.OrderBy(b => b.Name).ToListAsync(), b => (b as Branch)?.ZwstId, null, ControlUtils.RenewSourceDefault.First);
|
ControlUtils.RenewItemsSource(BranchList, await ctx.Branches
|
||||||
ControlUtils.RenewItemsSource(WineAttributeList, await Context.WineAttributes.OrderBy(a => a.Name).ToListAsync(), a => (a as WineAttr)?.AttrId, null, ControlUtils.RenewSourceDefault.First);
|
.OrderBy(b => b.Name)
|
||||||
ControlUtils.RenewItemsSource(AreaCommitmentTypeWineVariantInput, await Context.WineVarieties.OrderBy(s => s.Name).ToListAsync(), s => (s as WineVar)?.SortId);
|
.Include(b => b.PostalDest!.AtPlz)
|
||||||
var attrList = await Context.WineAttributes.OrderBy(a => a.Name).Cast<object>().ToListAsync();
|
.ToListAsync(), null, ControlUtils.RenewSourceDefault.First);
|
||||||
|
ControlUtils.RenewItemsSource(WineAttributeList, await ctx.WineAttributes
|
||||||
|
.OrderBy(a => a.Name)
|
||||||
|
.ToListAsync(), null, ControlUtils.RenewSourceDefault.First);
|
||||||
|
ControlUtils.RenewItemsSource(AreaCommitmentTypeWineVariantInput, await ctx.WineVarieties
|
||||||
|
.OrderBy(s => s.Name)
|
||||||
|
.ToListAsync());
|
||||||
|
var attrList = await ctx.WineAttributes.OrderBy(a => a.Name).Cast<object>().ToListAsync();
|
||||||
attrList.Insert(0, new NullItem(""));
|
attrList.Insert(0, new NullItem(""));
|
||||||
ControlUtils.RenewItemsSource(AreaCommitmentTypeWineAttributeInput, attrList, a => (a as WineAttr)?.AttrId);
|
ControlUtils.RenewItemsSource(AreaCommitmentTypeWineAttributeInput, attrList);
|
||||||
ControlUtils.RenewItemsSource(AreaCommitmentTypeList, await Context.AreaCommitmentTypes.OrderBy(v => v.VtrgId).ToListAsync(), v => (v as AreaComType)?.VtrgId, null, ControlUtils.RenewSourceDefault.First);
|
ControlUtils.RenewItemsSource(AreaCommitmentTypeList, await ctx.AreaCommitmentTypes
|
||||||
ControlUtils.RenewItemsSource(WineCultivationList, await Context.WineCultivations.OrderBy(c => c.Name).ToListAsync(), c=> (c as WineCult)?.CultId, null, ControlUtils.RenewSourceDefault.First);
|
.OrderBy(t => t.VtrgId)
|
||||||
ControlUtils.RenewItemsSource(SeasonModifierList, await Context.Modifiers.Where(m => m.Year == year).OrderBy(m => m.Ordering).ToListAsync(), m => (m as Modifier)?.ModId, null, ControlUtils.RenewSourceDefault.First);
|
.Include(t => t.WineVar)
|
||||||
|
.Include(t => t.WineAttr)
|
||||||
|
.ToListAsync(), null, ControlUtils.RenewSourceDefault.First);
|
||||||
|
ControlUtils.RenewItemsSource(WineCultivationList, await ctx.WineCultivations
|
||||||
|
.OrderBy(c => c.Name)
|
||||||
|
.ToListAsync(), null, ControlUtils.RenewSourceDefault.First);
|
||||||
|
ControlUtils.RenewItemsSource(SeasonModifierList, await ctx.Modifiers
|
||||||
|
.Where(m => m.Year == year)
|
||||||
|
.OrderBy(m => m.Ordering)
|
||||||
|
.ToListAsync(), null, ControlUtils.RenewSourceDefault.First);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateButtons() {
|
protected override void UpdateButtons() {
|
||||||
@ -174,23 +208,60 @@ namespace Elwig.Windows {
|
|||||||
App.FocusOriginHierarchy();
|
App.FocusOriginHierarchy();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EditButton_Click(object sender, RoutedEventArgs evt) {
|
private async Task InitEditing() {
|
||||||
|
EditContext = new AppDbContext();
|
||||||
|
await BranchesInitEditing(EditContext);
|
||||||
|
await WineAttributesInitEditing(EditContext);
|
||||||
|
await WineCultivationsInitEditing(EditContext);
|
||||||
|
await AreaCommitmentTypesInitEditing(EditContext);
|
||||||
|
await SeasonsInitEditing(EditContext);
|
||||||
|
await ModifiersInitEditing(EditContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Save() {
|
||||||
|
await UpdateClientParameters(App.Client);
|
||||||
|
await UpdateParameters(Utils.CurrentLastSeason);
|
||||||
|
using var tx = await EditContext!.Database.BeginTransactionAsync();
|
||||||
|
await BranchesSave(EditContext!);
|
||||||
|
await WineAttributesSave(EditContext!);
|
||||||
|
await WineCultivationsSave(EditContext!);
|
||||||
|
await AreaCommitmentTypesSave(EditContext!);
|
||||||
|
await SeasonsSave(EditContext!);
|
||||||
|
await ModifiersSave(EditContext!);
|
||||||
|
await tx.CommitAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task FinishEditing() {
|
||||||
|
EditContext?.Dispose();
|
||||||
|
EditContext = null;
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
|
await BranchesFinishEditing(ctx);
|
||||||
|
await WineAttributesFinishEditing(ctx);
|
||||||
|
await WineCultivationsFinishEditing(ctx);
|
||||||
|
await AreaCommitmentTypesFinishEditing(ctx);
|
||||||
|
await SeasonsFinishEditing(ctx);
|
||||||
|
await ModifiersFinishEditing(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected override void ShortcutEdit() {
|
||||||
|
if (!EditButton.IsEnabled || EditButton.Visibility != Visibility.Visible)
|
||||||
|
return;
|
||||||
|
EditButton_Click(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void EditButton_Click(object? sender, RoutedEventArgs? evt) {
|
||||||
IsEditing = true;
|
IsEditing = true;
|
||||||
EditButton.Visibility = Visibility.Hidden;
|
EditButton.Visibility = Visibility.Hidden;
|
||||||
ResetButton.Visibility = Visibility.Visible;
|
ResetButton.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
BranchesInitEditing();
|
await InitEditing();
|
||||||
WineAttributesInitEditing();
|
|
||||||
WineCultivationsInitEditing();
|
|
||||||
AreaCommitmentTypesInitEditing();
|
|
||||||
SeasonsInitEditing();
|
|
||||||
ModifiersInitEditing();
|
|
||||||
|
|
||||||
UnlockInputs();
|
UnlockInputs();
|
||||||
UpdateButtons();
|
UpdateButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CancelButton_Click(object sender, RoutedEventArgs evt) {
|
private async void CancelButton_Click(object sender, RoutedEventArgs evt) {
|
||||||
IsEditing = false;
|
IsEditing = false;
|
||||||
IsCreating = false;
|
IsCreating = false;
|
||||||
EditButton.Visibility = Visibility.Visible;
|
EditButton.Visibility = Visibility.Visible;
|
||||||
@ -199,47 +270,43 @@ namespace Elwig.Windows {
|
|||||||
SaveButton.IsEnabled = false;
|
SaveButton.IsEnabled = false;
|
||||||
ResetButton.IsEnabled = false;
|
ResetButton.IsEnabled = false;
|
||||||
|
|
||||||
Context.ChangeTracker.Clear();
|
await FinishEditing();
|
||||||
BranchesFinishEditing();
|
|
||||||
WineCultivationsFinishEditing();
|
|
||||||
WineAttributesFinishEditing();
|
|
||||||
AreaCommitmentTypesFinishEditing();
|
|
||||||
SeasonsFinishEditing();
|
|
||||||
ModifiersFinishEditing();
|
|
||||||
|
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
ClearInputStates();
|
ClearInputStates();
|
||||||
FillInputs(App.Client);
|
FillInputs(App.Client, await ctx.Seasons.FindAsync(Utils.CurrentLastSeason));
|
||||||
LockInputs();
|
LockInputs();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ResetButton_Click(object sender, RoutedEventArgs evt) {
|
protected override void ShortcutReset() {
|
||||||
|
if (!ResetButton.IsEnabled || ResetButton.Visibility != Visibility.Visible)
|
||||||
|
return;
|
||||||
|
ResetButton_Click(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void ResetButton_Click(object? sender, RoutedEventArgs? evt) {
|
||||||
_branchChanged = false;
|
_branchChanged = false;
|
||||||
_attrChanged = false;
|
_attrChanged = false;
|
||||||
_cultChanged = false;
|
_cultChanged = false;
|
||||||
_modChanged = false;
|
_modChanged = false;
|
||||||
Context.ChangeTracker.Clear();
|
|
||||||
|
|
||||||
BranchesInitEditing();
|
await InitEditing();
|
||||||
WineAttributesInitEditing();
|
|
||||||
WineCultivationsInitEditing();
|
|
||||||
AreaCommitmentTypesInitEditing();
|
|
||||||
SeasonsInitEditing();
|
|
||||||
ModifiersInitEditing();
|
|
||||||
|
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
ClearInputStates();
|
ClearInputStates();
|
||||||
FillInputs(App.Client);
|
FillInputs(App.Client, await ctx.Seasons.FindAsync(Utils.CurrentLastSeason));
|
||||||
UpdateButtons();
|
UpdateButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void SaveButton_Click(object sender, RoutedEventArgs evt) {
|
protected override void ShortcutSave() {
|
||||||
|
if (!SaveButton.IsEnabled || SaveButton.Visibility != Visibility.Visible)
|
||||||
|
return;
|
||||||
|
SaveButton_Click(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void SaveButton_Click(object? sender, RoutedEventArgs? evt) {
|
||||||
try {
|
try {
|
||||||
await UpdateClientParameters(App.Client);
|
await Save();
|
||||||
await BranchesSave();
|
|
||||||
await WineAttributesSave();
|
|
||||||
await WineCultivationsSave();
|
|
||||||
await AreaCommitmentTypesSave();
|
|
||||||
await SeasonsSave();
|
|
||||||
await ModifiersSave();
|
|
||||||
} catch (Exception exc) {
|
} catch (Exception exc) {
|
||||||
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
|
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;
|
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
|
||||||
@ -254,21 +321,18 @@ namespace Elwig.Windows {
|
|||||||
SaveButton.IsEnabled = false;
|
SaveButton.IsEnabled = false;
|
||||||
ResetButton.IsEnabled = false;
|
ResetButton.IsEnabled = false;
|
||||||
|
|
||||||
BranchesFinishEditing();
|
await FinishEditing();
|
||||||
WineAttributesFinishEditing();
|
|
||||||
WineCultivationsFinishEditing();
|
|
||||||
AreaCommitmentTypesFinishEditing();
|
|
||||||
SeasonsFinishEditing();
|
|
||||||
ModifiersFinishEditing();
|
|
||||||
|
|
||||||
|
using (var ctx = new AppDbContext()) {
|
||||||
ClearInputStates();
|
ClearInputStates();
|
||||||
FillInputs(App.Client);
|
FillInputs(App.Client, await ctx.Seasons.FindAsync(Utils.CurrentLastSeason));
|
||||||
LockInputs();
|
LockInputs();
|
||||||
|
}
|
||||||
|
|
||||||
await HintContextChange();
|
await HintContextChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FillInputs(ClientParameters p) {
|
private void FillInputs(ClientParameters p, Season? s) {
|
||||||
ClearOriginalValues();
|
ClearOriginalValues();
|
||||||
ClearDefaultValues();
|
ClearDefaultValues();
|
||||||
|
|
||||||
@ -299,6 +363,10 @@ namespace Elwig.Windows {
|
|||||||
TextElementDeliveryConfirmation.Text = p.TextDeliveryConfirmation;
|
TextElementDeliveryConfirmation.Text = p.TextDeliveryConfirmation;
|
||||||
TextElementCreditNote.Text = p.TextCreditNote;
|
TextElementCreditNote.Text = p.TextCreditNote;
|
||||||
|
|
||||||
|
ParameterAllowAttrIntoLowerInput.IsChecked = s?.Billing_AllowAttrsIntoLower ?? false;
|
||||||
|
ParameterAvoidUnderDeliveriesInput.IsChecked = s?.Billing_AvoidUnderDeliveries ?? false;
|
||||||
|
ParameterHonorGebundenInput.IsChecked = s?.Billing_HonorGebunden ?? false;
|
||||||
|
|
||||||
FinishInputFilling();
|
FinishInputFilling();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,5 +401,19 @@ namespace Elwig.Windows {
|
|||||||
ClientNameFull.Text = $"{ClientNameInput.Text}{(suffix != null ? $", {suffix}," : "")} {ClientNameTypeInput.Text}";
|
ClientNameFull.Text = $"{ClientNameInput.Text}{(suffix != null ? $", {suffix}," : "")} {ClientNameTypeInput.Text}";
|
||||||
TextBox_TextChanged(sender, evt);
|
TextBox_TextChanged(sender, evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task UpdateParameters(int year) {
|
||||||
|
try {
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
|
if (await ctx.Seasons.FindAsync(year) is not Season s)
|
||||||
|
return;
|
||||||
|
|
||||||
|
s.Billing_AllowAttrsIntoLower = ParameterAllowAttrIntoLowerInput.IsChecked ?? false;
|
||||||
|
s.Billing_AvoidUnderDeliveries = ParameterAvoidUnderDeliveriesInput.IsChecked ?? false;
|
||||||
|
s.Billing_HonorGebunden = ParameterHonorGebundenInput.IsChecked ?? false;
|
||||||
|
ctx.Update(s);
|
||||||
|
await ctx.SaveChangesAsync();
|
||||||
|
} catch { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,15 @@
|
|||||||
<Setter Property="Height" Value="25"/>
|
<Setter Property="Height" Value="25"/>
|
||||||
<Setter Property="TextWrapping" Value="NoWrap"/>
|
<Setter Property="TextWrapping" Value="NoWrap"/>
|
||||||
</Style>
|
</Style>
|
||||||
|
<Style TargetType="ctrl:UnitTextBox">
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||||
|
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
<Setter Property="Padding" Value="2"/>
|
||||||
|
<Setter Property="IsReadOnly" Value="True"/>
|
||||||
|
<Setter Property="Height" Value="25"/>
|
||||||
|
<Setter Property="TextWrapping" Value="NoWrap"/>
|
||||||
|
</Style>
|
||||||
<Style TargetType="ComboBox">
|
<Style TargetType="ComboBox">
|
||||||
<Setter Property="IsEnabled" Value="False"/>
|
<Setter Property="IsEnabled" Value="False"/>
|
||||||
<Setter Property="Height" Value="25"/>
|
<Setter Property="Height" Value="25"/>
|
||||||
|
@ -38,21 +38,21 @@ namespace Elwig.Windows {
|
|||||||
|
|
||||||
private static readonly LegendItem
|
private static readonly LegendItem
|
||||||
UngebundenLegend = new() {
|
UngebundenLegend = new() {
|
||||||
Label = "Ungebunden", LineWidth = 1, LineColor = ColorUngebunden,
|
LabelText = "Ungebunden", LineWidth = 1, LineColor = ColorUngebunden,
|
||||||
Marker = new(MarkerShape.FilledCircle, 5, ColorUngebunden)
|
MarkerStyle = new(MarkerShape.FilledCircle, 5, ColorUngebunden)
|
||||||
},
|
},
|
||||||
GebundenLegend = new() {
|
GebundenLegend = new() {
|
||||||
Label = "Gebunden", LineWidth = 1, LineColor = ColorGebunden,
|
LabelText = "Gebunden", LineWidth = 1, LineColor = ColorGebunden,
|
||||||
Marker = new(MarkerShape.FilledCircle, 5, ColorGebunden)
|
MarkerStyle = new(MarkerShape.FilledCircle, 5, ColorGebunden)
|
||||||
},
|
},
|
||||||
LdwLegend = new() {
|
LdwLegend = new() {
|
||||||
Label = "68 °Oe (LDW)", LineWidth = 2, LineColor = Colors.Red, Marker = MarkerStyle.None
|
LabelText = "68 °Oe (LDW)", LineWidth = 2, LineColor = Colors.Red, MarkerStyle = MarkerStyle.None
|
||||||
},
|
},
|
||||||
QuwLegend = new() {
|
QuwLegend = new() {
|
||||||
Label = "73 °Oe (QUW)", LineWidth = 2, LineColor = Colors.Orange, Marker = MarkerStyle.None
|
LabelText = "73 °Oe (QUW)", LineWidth = 2, LineColor = Colors.Orange, MarkerStyle = MarkerStyle.None
|
||||||
},
|
},
|
||||||
KabLegend = new() {
|
KabLegend = new() {
|
||||||
Label = "84 °Oe (KAB)", LineWidth = 2, LineColor = Colors.Green, Marker = MarkerStyle.None
|
LabelText = "84 °Oe (KAB)", LineWidth = 2, LineColor = Colors.Green, MarkerStyle = MarkerStyle.None
|
||||||
};
|
};
|
||||||
|
|
||||||
private (Graph? Graph, int Index) LastHighlighted = (null, -1);
|
private (Graph? Graph, int Index) LastHighlighted = (null, -1);
|
||||||
@ -74,8 +74,9 @@ namespace Elwig.Windows {
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
Year = year;
|
Year = year;
|
||||||
AvNr = avnr;
|
AvNr = avnr;
|
||||||
Season = Context.Seasons.Find(year) ?? throw new ArgumentException("Season not found");
|
using var ctx = new AppDbContext();
|
||||||
PaymentVar = Context.PaymentVariants.Find(year, avnr) ?? throw new ArgumentException("PaymentVar not found");
|
Season = ctx.Seasons.Find(year) ?? throw new ArgumentException("Season not found");
|
||||||
|
PaymentVar = ctx.PaymentVariants.Find(year, avnr) ?? throw new ArgumentException("PaymentVar not found");
|
||||||
Title = $"{PaymentVar?.Name} - Lese {year} - Elwig";
|
Title = $"{PaymentVar?.Name} - Lese {year} - Elwig";
|
||||||
LockContext = true;
|
LockContext = true;
|
||||||
}
|
}
|
||||||
@ -99,16 +100,16 @@ namespace Elwig.Windows {
|
|||||||
SaveButton.IsEnabled = hasChanged;
|
SaveButton.IsEnabled = hasChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RefreshGraphList() {
|
private async Task RefreshGraphList(AppDbContext ctx) {
|
||||||
PaymentVar = await Context.PaymentVariants.FindAsync(Year, AvNr) ?? throw new ArgumentException("PaymentVar not found");
|
PaymentVar = await ctx.PaymentVariants.FindAsync(Year, AvNr) ?? throw new ArgumentException("PaymentVar not found");
|
||||||
Season = await Context.Seasons.FindAsync(Year) ?? throw new ArgumentException("Season not found");
|
Season = await ctx.Seasons.FindAsync(Year) ?? throw new ArgumentException("Season not found");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var data = EditBillingData.FromJson(PaymentVar.Data, Utils.GetVaributes(Context, Year));
|
var data = EditBillingData.FromJson(PaymentVar.Data, Utils.GetVaributes(ctx, Year));
|
||||||
var paymentEntries = data.GetPaymentGraphEntries(Context, Season);
|
var paymentEntries = data.GetPaymentGraphEntries(ctx, Season);
|
||||||
GraphEntries = [
|
GraphEntries = [
|
||||||
..paymentEntries,
|
..paymentEntries,
|
||||||
..data.GetQualityGraphEntries(Context, Season, paymentEntries.Any() ? paymentEntries.Max(e => e.Id) : 0)
|
..data.GetQualityGraphEntries(ctx, Season, paymentEntries.Any() ? paymentEntries.Max(e => e.Id) : 0)
|
||||||
];
|
];
|
||||||
} catch (KeyNotFoundException ex) {
|
} catch (KeyNotFoundException ex) {
|
||||||
var key = ex.Message.Split('\'')[1].Split('\'')[0];
|
var key = ex.Message.Split('\'')[1].Split('\'')[0];
|
||||||
@ -123,7 +124,7 @@ namespace Elwig.Windows {
|
|||||||
MessageBox.Show("Fehler beim Laden der Auszahlungsvariante:\n\n" + ex.Message, "Fehler",
|
MessageBox.Show("Fehler beim Laden der Auszahlungsvariante:\n\n" + ex.Message, "Fehler",
|
||||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
Vaributes = Utils.GetVaributeList(Context, Year);
|
Vaributes = Utils.GetVaributeList(ctx, Year);
|
||||||
GraphEntries.ForEach(e => {
|
GraphEntries.ForEach(e => {
|
||||||
e.Vaributes.ForEach(v => {
|
e.Vaributes.ForEach(v => {
|
||||||
var found = Vaributes.Find(a => a.Variety?.SortId == v.Variety?.SortId && a.Attribute?.AttrId == v.Attribute?.AttrId && a.Cultivation?.CultId == v.Cultivation?.CultId);
|
var found = Vaributes.Find(a => a.Variety?.SortId == v.Variety?.SortId && a.Attribute?.AttrId == v.Attribute?.AttrId && a.Cultivation?.CultId == v.Cultivation?.CultId);
|
||||||
@ -137,9 +138,9 @@ namespace Elwig.Windows {
|
|||||||
});
|
});
|
||||||
|
|
||||||
FillingInputs = true;
|
FillingInputs = true;
|
||||||
ControlUtils.RenewItemsSource(VaributeInput, Vaributes, v => (v as Varibute)?.Listing);
|
ControlUtils.RenewItemsSource(VaributeInput, Vaributes);
|
||||||
FillingInputs = false;
|
FillingInputs = false;
|
||||||
ControlUtils.RenewItemsSource(GraphList, GraphEntries, g => (g as GraphEntry)?.VaributeStringChange, GraphList_SelectionChanged, ControlUtils.RenewSourceDefault.First);
|
ControlUtils.RenewItemsSource(GraphList, GraphEntries, GraphList_SelectionChanged, ControlUtils.RenewSourceDefault.First);
|
||||||
|
|
||||||
RefreshInputs();
|
RefreshInputs();
|
||||||
}
|
}
|
||||||
@ -192,15 +193,15 @@ namespace Elwig.Windows {
|
|||||||
GebundenFlatBonus.Text = "";
|
GebundenFlatBonus.Text = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
ControlUtils.SelectCheckComboBoxItems(VaributeInput, SelectedGraphEntry?.Vaributes ?? [], i => (i as Varibute)?.Listing);
|
ControlUtils.SelectItems(VaributeInput, SelectedGraphEntry?.Vaributes ?? []);
|
||||||
|
|
||||||
InitPlot();
|
InitPlot();
|
||||||
OechslePricePlot.IsEnabled = true;
|
OechslePricePlot.IsEnabled = true;
|
||||||
FillingInputs = false;
|
FillingInputs = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnRenewContext() {
|
protected override async Task OnRenewContext(AppDbContext ctx) {
|
||||||
await RefreshGraphList();
|
await RefreshGraphList(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitPlot() {
|
private void InitPlot() {
|
||||||
@ -378,7 +379,7 @@ namespace Elwig.Windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void ShowLegend() {
|
private void ShowLegend() {
|
||||||
OechslePricePlot.Plot.Legend.Location = Alignment.UpperLeft;
|
OechslePricePlot.Plot.Legend.Alignment = Alignment.UpperLeft;
|
||||||
OechslePricePlot.Plot.Legend.IsVisible = true;
|
OechslePricePlot.Plot.Legend.IsVisible = true;
|
||||||
|
|
||||||
OechslePricePlot.Plot.Legend.ManualItems.Add(LdwLegend);
|
OechslePricePlot.Plot.Legend.ManualItems.Add(LdwLegend);
|
||||||
@ -428,7 +429,7 @@ namespace Elwig.Windows {
|
|||||||
|
|
||||||
private void PriceInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
private void PriceInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
||||||
if (PrimaryMarkedPoint != -1 && ActiveGraph != null && PriceInput.IsKeyboardFocusWithin == true) {
|
if (PrimaryMarkedPoint != -1 && ActiveGraph != null && PriceInput.IsKeyboardFocusWithin == true) {
|
||||||
var res = Validator.CheckDecimal(PriceInput.TextBox, true, 2, Season.Precision);
|
var res = Validator.CheckDecimal(PriceInput, true, 2, Season.Precision);
|
||||||
if (res.IsValid && double.TryParse(PriceInput.Text, out double price)) {
|
if (res.IsValid && double.TryParse(PriceInput.Text, out double price)) {
|
||||||
ActiveGraph.SetPriceAt(PrimaryMarkedPoint, price);
|
ActiveGraph.SetPriceAt(PrimaryMarkedPoint, price);
|
||||||
PrimaryMarkedPointPlot.Location = new Coordinates(PrimaryMarkedPointPlot.Location.X, price);
|
PrimaryMarkedPointPlot.Location = new Coordinates(PrimaryMarkedPointPlot.Location.X, price);
|
||||||
@ -505,9 +506,9 @@ namespace Elwig.Windows {
|
|||||||
PrimaryMarkedPoint = Highlighted.Index;
|
PrimaryMarkedPoint = Highlighted.Index;
|
||||||
if (ActiveGraph != Highlighted.Graph) ChangeActiveGraph(Highlighted.Graph);
|
if (ActiveGraph != Highlighted.Graph) ChangeActiveGraph(Highlighted.Graph);
|
||||||
|
|
||||||
ChangeMarker(PrimaryMarkedPointPlot, true, ActiveGraph.GetOechsleAt(PrimaryMarkedPoint), ActiveGraph.GetPriceAt(PrimaryMarkedPoint));
|
ChangeMarker(PrimaryMarkedPointPlot, true, ActiveGraph!.GetOechsleAt(PrimaryMarkedPoint), ActiveGraph.GetPriceAt(PrimaryMarkedPoint));
|
||||||
|
|
||||||
OechsleInput.Text = Highlighted.Graph.GetOechsleAt(Highlighted.Index).ToString();
|
OechsleInput.Text = Highlighted.Graph!.GetOechsleAt(Highlighted.Index).ToString();
|
||||||
PriceInput.Text = Highlighted.Graph.GetPriceAt(Highlighted.Index).ToString();
|
PriceInput.Text = Highlighted.Graph.GetPriceAt(Highlighted.Index).ToString();
|
||||||
|
|
||||||
EnableActionButtons();
|
EnableActionButtons();
|
||||||
@ -591,13 +592,13 @@ namespace Elwig.Windows {
|
|||||||
Pixel mousePixel = new(p.X, p.Y - 30);
|
Pixel mousePixel = new(p.X, p.Y - 30);
|
||||||
Coordinates mouseLocation = OechslePricePlot.Plot.GetCoordinates(mousePixel);
|
Coordinates mouseLocation = OechslePricePlot.Plot.GetCoordinates(mousePixel);
|
||||||
TooltipPlot = OechslePricePlot.Plot.Add.Text($"Oechsle: {pointX:N2}, Preis: {Math.Round(pointY, Season.Precision)}€/kg", mouseLocation.X, mouseLocation.Y);
|
TooltipPlot = OechslePricePlot.Plot.Add.Text($"Oechsle: {pointX:N2}, Preis: {Math.Round(pointY, Season.Precision)}€/kg", mouseLocation.X, mouseLocation.Y);
|
||||||
TooltipPlot.Label.FontSize = 12;
|
TooltipPlot.LabelFontSize = 12;
|
||||||
TooltipPlot.Label.Bold = true;
|
TooltipPlot.LabelBold = true;
|
||||||
TooltipPlot.Label.BorderColor = Colors.Black;
|
TooltipPlot.LabelBorderColor = Colors.Black;
|
||||||
TooltipPlot.Label.BorderWidth = 2;
|
TooltipPlot.LabelBorderWidth = 2;
|
||||||
TooltipPlot.Label.BackColor = Colors.White;
|
TooltipPlot.LabelBackgroundColor = Colors.White;
|
||||||
TooltipPlot.Label.Padding = 10;
|
TooltipPlot.LabelPadding = 10;
|
||||||
TooltipPlot.Label.Alignment = Alignment.MiddleLeft;
|
TooltipPlot.LabelAlignment = Alignment.MiddleLeft;
|
||||||
}
|
}
|
||||||
LastHighlighted = (g, pointIndex);
|
LastHighlighted = (g, pointIndex);
|
||||||
HoverChanged = false;
|
HoverChanged = false;
|
||||||
@ -643,20 +644,18 @@ namespace Elwig.Windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async void SaveButton_Click(object sender, RoutedEventArgs e) {
|
private async void SaveButton_Click(object sender, RoutedEventArgs e) {
|
||||||
|
try {
|
||||||
|
using var ctx = new AppDbContext();
|
||||||
var origData = BillingData.FromJson(PaymentVar.Data);
|
var origData = BillingData.FromJson(PaymentVar.Data);
|
||||||
var data = BillingData.FromGraphEntries(GraphEntries, origData, Utils.GetVaributes(Context, Year),
|
var data = BillingData.FromGraphEntries(GraphEntries, origData, Utils.GetVaributes(ctx, Year),
|
||||||
AllVaributesAssigned, AllVaributesAssignedAbgew);
|
AllVaributesAssigned, AllVaributesAssignedAbgew);
|
||||||
|
|
||||||
EntityEntry<PaymentVar>? tr = null;
|
|
||||||
try {
|
|
||||||
PaymentVar.Data = data.ToJsonString();
|
PaymentVar.Data = data.ToJsonString();
|
||||||
tr = Context.Update(PaymentVar);
|
ctx.Update(PaymentVar);
|
||||||
await Context.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
LockContext = false;
|
LockContext = false;
|
||||||
tr = null;
|
|
||||||
await App.HintContextChange();
|
await App.HintContextChange();
|
||||||
} catch (Exception exc) {
|
} catch (Exception exc) {
|
||||||
if (tr != null) await tr.ReloadAsync();
|
|
||||||
var str = "Der Eintrag konnte nicht in der Datenbank gespeichert werden!\n\n" + exc.Message;
|
var str = "Der Eintrag konnte nicht in der Datenbank gespeichert werden!\n\n" + exc.Message;
|
||||||
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
|
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
|
||||||
MessageBox.Show(str, "Auszahlungsvariante speichern", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show(str, "Auszahlungsvariante speichern", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
@ -668,13 +667,13 @@ namespace Elwig.Windows {
|
|||||||
private void EnableUnitTextBox(UnitTextBox u) {
|
private void EnableUnitTextBox(UnitTextBox u) {
|
||||||
if (PaymentVar.TestVariant) {
|
if (PaymentVar.TestVariant) {
|
||||||
u.IsEnabled = true;
|
u.IsEnabled = true;
|
||||||
u.TextBox.IsReadOnly = false;
|
u.IsReadOnly = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DisableUnitTextBox(UnitTextBox u) {
|
private void DisableUnitTextBox(UnitTextBox u) {
|
||||||
u.IsEnabled = false;
|
u.IsEnabled = false;
|
||||||
u.TextBox.IsReadOnly = true;
|
u.IsReadOnly = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ChangeActiveGraph(Graph? g) {
|
private void ChangeActiveGraph(Graph? g) {
|
||||||
@ -717,7 +716,7 @@ namespace Elwig.Windows {
|
|||||||
|
|
||||||
private void GebundenFlatBonus_TextChanged(object sender, TextChangedEventArgs e) {
|
private void GebundenFlatBonus_TextChanged(object sender, TextChangedEventArgs e) {
|
||||||
if (FillingInputs) return;
|
if (FillingInputs) return;
|
||||||
var r = Validator.CheckDecimal(GebundenFlatBonus.TextBox, true, 2, Season.Precision);
|
var r = Validator.CheckDecimal(GebundenFlatBonus, true, 2, Season.Precision);
|
||||||
if (r.IsValid && SelectedGraphEntry != null) {
|
if (r.IsValid && SelectedGraphEntry != null) {
|
||||||
SelectedGraphEntry.GebundenFlatBonus = double.Parse(GebundenFlatBonus.Text);
|
SelectedGraphEntry.GebundenFlatBonus = double.Parse(GebundenFlatBonus.Text);
|
||||||
ResetPlot();
|
ResetPlot();
|
||||||
|
@ -2,50 +2,55 @@ using Elwig.Helpers;
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Input;
|
||||||
|
|
||||||
namespace Elwig.Windows {
|
namespace Elwig.Windows {
|
||||||
public abstract class ContextWindow : Window {
|
public abstract class ContextWindow : Window {
|
||||||
|
|
||||||
public static readonly int RenewSec = 10;
|
private bool _lockContext = false;
|
||||||
|
protected bool LockContext {
|
||||||
|
get => _lockContext;
|
||||||
|
set {
|
||||||
|
_lockContext = value;
|
||||||
|
if (!_lockContext && _renewPending) {
|
||||||
|
Dispatcher.BeginInvoke(async () => await RenewContext());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected AppDbContext Context { get; private set; }
|
|
||||||
protected bool LockContext { get; set; } = false;
|
|
||||||
|
|
||||||
private readonly DispatcherTimer _timer;
|
|
||||||
private bool _renewPending = false;
|
private bool _renewPending = false;
|
||||||
|
|
||||||
|
private readonly RoutedCommand CtrlR = new("CtrlR", typeof(ContextWindow), [new KeyGesture(Key.R, ModifierKeys.Control)]);
|
||||||
|
private readonly RoutedCommand F5 = new("F5", typeof(ContextWindow), [new KeyGesture(Key.F5)]);
|
||||||
|
|
||||||
public ContextWindow() : base() {
|
public ContextWindow() : base() {
|
||||||
_timer = new DispatcherTimer();
|
CommandBindings.Add(new CommandBinding(CtrlR, ForceContextReload));
|
||||||
_timer.Tick += new EventHandler(OnShouldRenewContext);
|
CommandBindings.Add(new CommandBinding(F5, ForceContextReload));
|
||||||
_timer.Interval = new TimeSpan(0, 0, RenewSec);
|
|
||||||
_timer.Start();
|
|
||||||
Context = new();
|
|
||||||
Loaded += OnLoaded;
|
Loaded += OnLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async void ForceContextReload(object sender, EventArgs evt) {
|
||||||
|
await HintContextChange();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task HintContextChange() {
|
public async Task HintContextChange() {
|
||||||
_renewPending = true;
|
_renewPending = true;
|
||||||
if (LockContext) return;
|
if (LockContext) return;
|
||||||
await RenewContext();
|
await RenewContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnShouldRenewContext(object? sender, EventArgs? evt) {
|
|
||||||
if (!Context.HasBackendChanged) return;
|
|
||||||
await HintContextChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async void OnLoaded(object? sender, RoutedEventArgs? evt) {
|
protected async void OnLoaded(object? sender, RoutedEventArgs? evt) {
|
||||||
await OnRenewContext();
|
using var ctx = new AppDbContext();
|
||||||
|
await OnRenewContext(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task RenewContext() {
|
protected async Task RenewContext() {
|
||||||
if (!_renewPending) return;
|
if (!_renewPending) return;
|
||||||
Context = new();
|
using var ctx = new AppDbContext();
|
||||||
await OnRenewContext();
|
await OnRenewContext(ctx);
|
||||||
_renewPending = false;
|
_renewPending = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract protected Task OnRenewContext();
|
abstract protected Task OnRenewContext(AppDbContext ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
<local:AdministrationWindow x:Class="Elwig.Windows.DeliveryAdminWindow"
|
<local:AdministrationWindow x:Class="Elwig.Windows.DeliveryAdminWindow"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:local="clr-namespace:Elwig.Windows"
|
xmlns:local="clr-namespace:Elwig.Windows"
|
||||||
xmlns:ctrl="clr-namespace:Elwig.Controls"
|
xmlns:ctrl="clr-namespace:Elwig.Controls"
|
||||||
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
|
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
|
||||||
@ -23,6 +21,14 @@
|
|||||||
<Setter Property="Height" Value="25"/>
|
<Setter Property="Height" Value="25"/>
|
||||||
<Setter Property="TextWrapping" Value="NoWrap"/>
|
<Setter Property="TextWrapping" Value="NoWrap"/>
|
||||||
</Style>
|
</Style>
|
||||||
|
<Style TargetType="ctrl:UnitTextBox">
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Stretch"/>
|
||||||
|
<Setter Property="VerticalAlignment" Value="Top"/>
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
<Setter Property="Padding" Value="2"/>
|
||||||
|
<Setter Property="Height" Value="25"/>
|
||||||
|
<Setter Property="TextWrapping" Value="NoWrap"/>
|
||||||
|
</Style>
|
||||||
<Style TargetType="ComboBox">
|
<Style TargetType="ComboBox">
|
||||||
<Setter Property="Height" Value="25"/>
|
<Setter Property="Height" Value="25"/>
|
||||||
<Setter Property="FontSize" Value="14"/>
|
<Setter Property="FontSize" Value="14"/>
|
||||||
@ -54,24 +60,74 @@
|
|||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<Menu Grid.ColumnSpan="3" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
|
<Menu Grid.ColumnSpan="3" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
|
||||||
<MenuItem Header="Drucken">
|
<MenuItem Header="Lieferschein">
|
||||||
<MenuItem x:Name="Menu_Print_ShowDeliveryNote" Header="Lieferschein anzeigen" IsEnabled="False"
|
<MenuItem x:Name="Menu_DeliveryNote_Show" Header="...anzeigen (PDF)" IsEnabled="False"
|
||||||
Click="Menu_Print_ShowDeliveryNote_Click"/>
|
Click="Menu_DeliveryNote_Show_Click" InputGestureText="Strg+P"/>
|
||||||
<MenuItem x:Name="Menu_Print_PrintDeliveryNote" Header="Lieferschein drucken" IsEnabled="False"
|
<MenuItem x:Name="Menu_DeliveryNote_SavePdf" Header="...speichern... (PDF)" IsEnabled="False"
|
||||||
Click="Menu_Print_PrintDeliveryNote_Click"/>
|
Click="Menu_DeliveryNote_SavePdf_Click"/>
|
||||||
<MenuItem x:Name="Menu_Print_DeliveryJournal" Header="Lieferjournal">
|
<MenuItem x:Name="Menu_DeliveryNote_Print" Header="...drucken" IsEnabled="False"
|
||||||
<MenuItem x:Name="Menu_Print_DeliveryJournal_ShowToday" Header="von heute anzeigen"
|
Click="Menu_DeliveryNote_Print_Click" InputGestureText="Strg+Shift+P"/>
|
||||||
Click="Menu_Print_DeliveryJournal_ShowToday_Click"/>
|
<MenuItem x:Name="Menu_DeliveryNote_Email" Header="...per E-Mail schicken" IsEnabled="False"
|
||||||
<MenuItem x:Name="Menu_Print_DeliveryJournal_PrintToday" Header="von heute drucken"
|
Click="Menu_DeliveryNote_Email_Click"/>
|
||||||
Click="Menu_Print_DeliveryJournal_PrintToday_Click"/>
|
|
||||||
<MenuItem x:Name="Menu_Print_DeliveryJournal_ShowFilter" Header="aus Filtern anzeigen"
|
|
||||||
Click="Menu_Print_DeliveryJournal_ShowFilter_Click"/>
|
|
||||||
<MenuItem x:Name="Menu_Print_DeliveryJournal_PrintFilter" Header="aus Filtern drucken"
|
|
||||||
Click="Menu_Print_DeliveryJournal_PrintFilter_Click"/>
|
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
<MenuItem Header="Lieferjournal">
|
||||||
|
<MenuItem x:Name="Menu_DeliveryJournal_SaveFilters" Header="...aus Filtern speichern... (Excel)"
|
||||||
|
Click="Menu_DeliveryJournal_SaveFilters_Click" InputGestureText="Strg+L"/>
|
||||||
|
<MenuItem x:Name="Menu_DeliveryJournal_ShowFilters" Header="...aus Filtern anzeigen (PDF)"
|
||||||
|
Click="Menu_DeliveryJournal_ShowFilters_Click" InputGestureText="Strg+O"/>
|
||||||
|
<MenuItem x:Name="Menu_DeliveryJournal_SavePdfFilters" Header="...aus Filtern speichern... (PDF)"
|
||||||
|
Click="Menu_DeliveryJournal_SavePdfFilters_Click"/>
|
||||||
|
<MenuItem x:Name="Menu_DeliveryJournal_PrintFilters" Header="...aus Filtern drucken"
|
||||||
|
Click="Menu_DeliveryJournal_PrintFilters_Click" InputGestureText="Strg+Shift+O"/>
|
||||||
|
<Separator/>
|
||||||
|
<MenuItem x:Name="Menu_DeliveryJournal_SaveToday" Header="...von heute speichern... (Excel)"
|
||||||
|
Click="Menu_DeliveryJournal_SaveToday_Click"/>
|
||||||
|
<MenuItem x:Name="Menu_DeliveryJournal_ShowToday" Header="...von heute anzeigen (PDF)"
|
||||||
|
Click="Menu_DeliveryJournal_ShowToday_Click"/>
|
||||||
|
<MenuItem x:Name="Menu_DeliveryJournal_SavePdfToday" Header="...von heute speichern... (PDF)"
|
||||||
|
Click="Menu_DeliveryJournal_SavePdfToday_Click"/>
|
||||||
|
<MenuItem x:Name="Menu_DeliveryJournal_PrintToday" Header="...von heute drucken"
|
||||||
|
Click="Menu_DeliveryJournal_PrintToday_Click" InputGestureText="Strg+J"/>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem Header="Exportieren">
|
<MenuItem Header="Qualitätsstatistik" x:Name="Menu_WineQualityStatistics">
|
||||||
<MenuItem x:Name="Menu_Export_Bki" Header="Traubentransportscheinliste (BKI)"/>
|
<MenuItem x:Name="Menu_WineQualityStatistics_ShowFilters" Header="...aus Filtern anzeigen (PDF)"
|
||||||
|
Click="Menu_WineQualityStatistics_ShowFilters_Click"/>
|
||||||
|
<MenuItem x:Name="Menu_WineQualityStatistics_SavePdfFilters" Header="...aus Filtern speichern... (PDF)"
|
||||||
|
Click="Menu_WineQualityStatistics_SavePdfFilters_Click"/>
|
||||||
|
<MenuItem x:Name="Menu_WineQualityStatistics_PrintFilters" Header="...aus Filtern drucken"
|
||||||
|
Click="Menu_WineQualityStatistics_PrintFilters_Click"/>
|
||||||
|
<Separator/>
|
||||||
|
<MenuItem x:Name="Menu_WineQualityStatistics_ShowToday" Header="...von heute anzeigen (PDF)"
|
||||||
|
Click="Menu_WineQualityStatistics_ShowToday_Click"/>
|
||||||
|
<MenuItem x:Name="Menu_WineQualityStatistics_SavePdfToday" Header="...von heute speichern... (PDF)"
|
||||||
|
Click="Menu_WineQualityStatistics_SavePdfToday_Click"/>
|
||||||
|
<MenuItem x:Name="Menu_WineQualityStatistics_PrintToday" Header="...von heute drucken"
|
||||||
|
Click="Menu_WineQualityStatistics_PrintToday_Click" InputGestureText="Strg+Q"/>
|
||||||
|
<Separator/>
|
||||||
|
<MenuItem x:Name="Menu_WineQualityStatistics_ModeOe" Header="...nach °Oe aufschlüsseln" IsCheckable="True" IsChecked="True"
|
||||||
|
Click="Menu_WineQualityStatistics_Mode_Click"/>
|
||||||
|
<MenuItem x:Name="Menu_WineQualityStatistics_ModeKmw10" Header="...nach °KMW aufschlüsseln (⅒)" IsCheckable="True"
|
||||||
|
Click="Menu_WineQualityStatistics_Mode_Click"/>
|
||||||
|
<MenuItem x:Name="Menu_WineQualityStatistics_ModeKmw5" Header="...nach °KMW aufschlüsseln (⅕)" IsCheckable="True"
|
||||||
|
Click="Menu_WineQualityStatistics_Mode_Click"/>
|
||||||
|
<MenuItem x:Name="Menu_WineQualityStatistics_ModeKmw2" Header="...nach °KMW aufschlüsseln (½)" IsCheckable="True"
|
||||||
|
Click="Menu_WineQualityStatistics_Mode_Click"/>
|
||||||
|
<MenuItem x:Name="Menu_WineQualityStatistics_ModeKmw1" Header="...nach °KMW aufschlüsseln (ganze)" IsCheckable="True"
|
||||||
|
Click="Menu_WineQualityStatistics_Mode_Click"/>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem Header="BKI">
|
||||||
|
<MenuItem x:Name="Menu_Bki_SaveList" Header="Traubentransportscheinliste speichern..."/>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem Header="Export">
|
||||||
|
<MenuItem x:Name="Menu_Export_ExportFilters" Header="...aus Filtern speichern..."
|
||||||
|
Click="Menu_Export_ExportFilters_Click"/>
|
||||||
|
<MenuItem x:Name="Menu_Export_UploadFilters" Header="...aus Filtern hochladen"
|
||||||
|
Click="Menu_Export_UploadFilters_Click"/>
|
||||||
|
<Separator/>
|
||||||
|
<MenuItem x:Name="Menu_Export_ExportToday" Header="...von heute speichern..."
|
||||||
|
Click="Menu_Export_ExportToday_Click"/>
|
||||||
|
<MenuItem x:Name="Menu_Export_UploadToday" Header="...von heute hochladen"
|
||||||
|
Click="Menu_Export_UploadToday_Click" InputGestureText="Strg+H"/>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem Header="Einstellungen">
|
<MenuItem Header="Einstellungen">
|
||||||
<MenuItem x:Name="Menu_Settings_EnableFreeEditing" Header="Freie Bearbeitung aktivieren"
|
<MenuItem x:Name="Menu_Settings_EnableFreeEditing" Header="Freie Bearbeitung aktivieren"
|
||||||
@ -95,6 +151,7 @@
|
|||||||
TextChanged="SearchInput_TextChanged">
|
TextChanged="SearchInput_TextChanged">
|
||||||
<TextBox.ToolTip>
|
<TextBox.ToolTip>
|
||||||
<TextBlock>
|
<TextBlock>
|
||||||
|
<Bold>Strg+F</Bold><LineBreak/><LineBreak/>
|
||||||
Lieferungen filtern und durchsuchen. Die Filter sind beliebig kombinierbar.<LineBreak/>
|
Lieferungen filtern und durchsuchen. Die Filter sind beliebig kombinierbar.<LineBreak/>
|
||||||
Groß- und Kleinschreibung ist in den meisten Fällen egal.<LineBreak/>
|
Groß- und Kleinschreibung ist in den meisten Fällen egal.<LineBreak/>
|
||||||
<LineBreak/>
|
<LineBreak/>
|
||||||
@ -110,6 +167,10 @@
|
|||||||
<Bold>Bewirtschaftung</Bold>: z.B. bio, !kip (alle außer KIP), ...<LineBreak/>
|
<Bold>Bewirtschaftung</Bold>: z.B. bio, !kip (alle außer KIP), ...<LineBreak/>
|
||||||
<Bold>Datum</Bold>: z.B. 1.9., 15.9.-10.10., -15.10.2020, ...<LineBreak/>
|
<Bold>Datum</Bold>: z.B. 1.9., 15.9.-10.10., -15.10.2020, ...<LineBreak/>
|
||||||
<Bold>Uhrzeit</Bold>: z.B. 06:00-08:00, 18:00-, ...<LineBreak/>
|
<Bold>Uhrzeit</Bold>: z.B. 06:00-08:00, 18:00-, ...<LineBreak/>
|
||||||
|
<Bold>Handwiegung</Bold>: handw[iegung], !Handw[iegung] (alle ohne Handwiegung)<LineBreak/>
|
||||||
|
<Bold>Handlese</Bold>: Handl[ese], !handl[ese] (alle ohne Handlese)<LineBreak/>
|
||||||
|
<Bold>Gebunden</Bold>: geb[unden], ungeb[unden], !geb[unden], !ungeb[unden]<LineBreak/>
|
||||||
|
<Bold>Brutto/Netto Wiegung</Bold>: bto, Brut[to], nto, Net[to], gerebelt, !Gerebelt<LineBreak/>
|
||||||
<Bold>Freitext</Bold>: z.B. Lieferscheinnummern, Anmerkung, "quw" (sucht nach dem Text "quw")
|
<Bold>Freitext</Bold>: z.B. Lieferscheinnummern, Anmerkung, "quw" (sucht nach dem Text "quw")
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</TextBox.ToolTip>
|
</TextBox.ToolTip>
|
||||||
@ -172,6 +233,7 @@
|
|||||||
</DataGridTextColumn>
|
</DataGridTextColumn>
|
||||||
<DataGridTextColumn Header="LsNr." Binding="{Binding LsNr}" Width="120"/>
|
<DataGridTextColumn Header="LsNr." Binding="{Binding LsNr}" Width="120"/>
|
||||||
<DataGridTextColumn Header="Mitglied" Binding="{Binding Member.AdministrativeName}" Width="180"/>
|
<DataGridTextColumn Header="Mitglied" Binding="{Binding Member.AdministrativeName}" Width="180"/>
|
||||||
|
<DataGridTextColumn Header="Zu-/Abschläge" Binding="{Binding FilteredModifiersString}" Width="150"/>
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
|
|
||||||
@ -185,31 +247,59 @@
|
|||||||
Click="NewDeliveryPartButton_Click"/>
|
Click="NewDeliveryPartButton_Click"/>
|
||||||
<Button x:Name="CancelCreatingButton" Content="Abbrechen" IsEnabled="False" Visibility="Hidden"
|
<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"
|
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2"
|
||||||
Click="CancelCreatingButton_Click"/>
|
Click="CancelCreatingButton_Click">
|
||||||
|
<Button.ToolTip>
|
||||||
|
<TextBlock FontWeight="Bold">Esc</TextBlock>
|
||||||
|
</Button.ToolTip>
|
||||||
|
</Button>
|
||||||
|
|
||||||
<Button x:Name="NewDeliveryButton" Content="Neu" IsEnabled="False" Visibility="Hidden"
|
<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"
|
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2"
|
||||||
Click="NewDeliveryButton_Click"/>
|
Click="NewDeliveryButton_Click">
|
||||||
|
<Button.ToolTip>
|
||||||
|
<TextBlock FontWeight="Bold">Alt+Einfg</TextBlock>
|
||||||
|
</Button.ToolTip>
|
||||||
|
</Button>
|
||||||
<Button x:Name="AbwertenButton" Content="Abwerten" IsEnabled="False"
|
<Button x:Name="AbwertenButton" Content="Abwerten" IsEnabled="False"
|
||||||
ToolTip="Ausgewählte Teillieferung vollständig oder teilweise abwerten"
|
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"
|
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2"
|
||||||
Click="AbwertenButton_Click"/>
|
Click="AbwertenButton_Click"/>
|
||||||
<Button x:Name="EditDeliveryButton" Content="Bearbeiten" IsEnabled="False"
|
<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"
|
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2"
|
||||||
Click="EditDeliveryButton_Click"/>
|
Click="EditDeliveryButton_Click">
|
||||||
|
<Button.ToolTip>
|
||||||
|
<TextBlock FontWeight="Bold">Strg+B</TextBlock>
|
||||||
|
</Button.ToolTip>
|
||||||
|
</Button>
|
||||||
<Button x:Name="DeleteDeliveryButton" Content="Löschen" IsEnabled="False"
|
<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"
|
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2"
|
||||||
Click="DeleteDeliveryButton_Click"/>
|
Click="DeleteDeliveryButton_Click">
|
||||||
|
<Button.ToolTip>
|
||||||
|
<TextBlock FontWeight="Bold">Alt+Entf</TextBlock>
|
||||||
|
</Button.ToolTip>
|
||||||
|
</Button>
|
||||||
|
|
||||||
<Button x:Name="SaveButton" Content="Speichern" IsEnabled="False" Visibility="Hidden"
|
<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"
|
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2"
|
||||||
Click="SaveButton_Click"/>
|
Click="SaveButton_Click">
|
||||||
|
<Button.ToolTip>
|
||||||
|
<TextBlock FontWeight="Bold">Strg+S</TextBlock>
|
||||||
|
</Button.ToolTip>
|
||||||
|
</Button>
|
||||||
<Button x:Name="ResetButton" Content="Zurücksetzen" IsEnabled="False" Visibility="Hidden"
|
<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"
|
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2"
|
||||||
Click="ResetButton_Click"/>
|
Click="ResetButton_Click">
|
||||||
|
<Button.ToolTip>
|
||||||
|
<TextBlock FontWeight="Bold">Strg+Z</TextBlock>
|
||||||
|
</Button.ToolTip>
|
||||||
|
</Button>
|
||||||
<Button x:Name="CancelButton" Content="Abbrechen" IsEnabled="False" Visibility="Hidden" IsCancel="True"
|
<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"
|
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2"
|
||||||
Click="CancelButton_Click"/>
|
Click="CancelButton_Click">
|
||||||
|
<Button.ToolTip>
|
||||||
|
<TextBlock FontWeight="Bold">Esc</TextBlock>
|
||||||
|
</Button.ToolTip>
|
||||||
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<GridSplitter Grid.Column="1" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
|
<GridSplitter Grid.Column="1" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
|
||||||
@ -293,8 +383,12 @@
|
|||||||
<TextBox x:Name="SortIdInput" Width="36" Grid.Row="1" Grid.Column="1" Margin="0,10,0,0" HorizontalAlignment="Left"
|
<TextBox x:Name="SortIdInput" Width="36" Grid.Row="1" Grid.Column="1" Margin="0,10,0,0" HorizontalAlignment="Left"
|
||||||
TextChanged="SortIdInput_TextChanged" LostFocus="SortIdInput_LostFocus" KeyUp="Input_KeyUp"/>
|
TextChanged="SortIdInput_TextChanged" LostFocus="SortIdInput_LostFocus" KeyUp="Input_KeyUp"/>
|
||||||
<ComboBox x:Name="WineVarietyInput" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Margin="41,10,10,10"
|
<ComboBox x:Name="WineVarietyInput" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Margin="41,10,10,10"
|
||||||
ItemTemplate="{StaticResource WineVarietyTemplate}" TextSearch.TextPath="Name"
|
TextSearch.TextPath="Name"
|
||||||
SelectionChanged="WineVarietyInput_SelectionChanged" KeyUp="Input_KeyUp"/>
|
SelectionChanged="WineVarietyInput_SelectionChanged" KeyUp="Input_KeyUp">
|
||||||
|
<ComboBox.ItemTemplateSelector>
|
||||||
|
<ctrl:WineVarietyTemplateSelector/>
|
||||||
|
</ComboBox.ItemTemplateSelector>
|
||||||
|
</ComboBox>
|
||||||
|
|
||||||
<Label Content="Attr./Bewirt.:" Margin="10,40,0,0" Grid.Column="0"/>
|
<Label Content="Attr./Bewirt.:" Margin="10,40,0,0" Grid.Column="0"/>
|
||||||
<ComboBox x:Name="AttributeInput" Grid.Row="1" Grid.Column="1" Margin="0,40,5,10"
|
<ComboBox x:Name="AttributeInput" Grid.Row="1" Grid.Column="1" Margin="0,40,5,10"
|
||||||
@ -322,8 +416,12 @@
|
|||||||
|
|
||||||
<Label Content="Qualitätsstufe:" Margin="10,40,10,10"/>
|
<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"
|
<ComboBox x:Name="WineQualityLevelInput" Width="146" Margin="0,40,10,10" Grid.Column="1" HorizontalAlignment="Left"
|
||||||
ItemTemplate="{StaticResource WineQualityLevelTemplate}"
|
TextSearch.TextPath="Name"
|
||||||
SelectionChanged="WineQualityLevelInput_SelectionChanged" KeyUp="Input_KeyUp"/>
|
SelectionChanged="WineQualityLevelInput_SelectionChanged" KeyUp="Input_KeyUp">
|
||||||
|
<ComboBox.ItemTemplateSelector>
|
||||||
|
<ctrl:WineQualityLevelTemplateSelector/>
|
||||||
|
</ComboBox.ItemTemplateSelector>
|
||||||
|
</ComboBox>
|
||||||
|
|
||||||
<CheckBox x:Name="AbgewertetInput" Content="Abgewertet" IsEnabled="False"
|
<CheckBox x:Name="AbgewertetInput" Content="Abgewertet" IsEnabled="False"
|
||||||
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,75,10,10" Grid.Column="0" Grid.ColumnSpan="2"/>
|
VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,75,10,10" Grid.Column="0" Grid.ColumnSpan="2"/>
|
||||||
@ -396,10 +494,10 @@
|
|||||||
|
|
||||||
<CheckBox x:Name="LesewagenInput" Content="Lesewagen" Margin="10,75,0,0" Grid.Column="2"
|
<CheckBox x:Name="LesewagenInput" Content="Lesewagen" Margin="10,75,0,0" Grid.Column="2"
|
||||||
VerticalAlignment="Top" HorizontalAlignment="Left"
|
VerticalAlignment="Top" HorizontalAlignment="Left"
|
||||||
Checked="LesewagenInput_Changed" Unchecked="LesewagenInput_Changed"/>
|
Checked="LesewagenInput_Changed" Unchecked="LesewagenInput_Changed" Click="HandPickedInput_Changed"/>
|
||||||
<CheckBox x:Name="HandPickedInput" Content="Handlese" Margin="10,105,0,0" Grid.Column="2" IsThreeState="True"
|
<CheckBox x:Name="HandPickedInput" Content="Handlese" Margin="10,105,0,0" Grid.Column="2" IsThreeState="True"
|
||||||
VerticalAlignment="Top" HorizontalAlignment="Left"
|
VerticalAlignment="Top" HorizontalAlignment="Left"
|
||||||
Checked="HandPickedInput_Changed" Unchecked="HandPickedInput_Changed"/>
|
Checked="HandPickedInput_Changed" Unchecked="HandPickedInput_Changed" Click="HandPickedInput_Changed"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</GroupBox>
|
</GroupBox>
|
||||||
|
|
||||||
@ -445,7 +543,11 @@
|
|||||||
|
|
||||||
<Label Content="Weinbaugebiet:" Margin="10,10,0,10" Grid.Column="0"/>
|
<Label Content="Weinbaugebiet:" Margin="10,10,0,10" Grid.Column="0"/>
|
||||||
<ComboBox x:Name="WineOriginInput" Margin="0,10,10,10" Grid.Column="1"
|
<ComboBox x:Name="WineOriginInput" Margin="0,10,10,10" Grid.Column="1"
|
||||||
ItemTemplate="{StaticResource WineOriginComboTemplate}"/>
|
TextSearch.TextPath="Name">
|
||||||
|
<ComboBox.ItemTemplateSelector>
|
||||||
|
<ctrl:WineOriginTemplateSelector/>
|
||||||
|
</ComboBox.ItemTemplateSelector>
|
||||||
|
</ComboBox>
|
||||||
|
|
||||||
<Label Content="Weinbau-KG:" Margin="10,40,0,10" Grid.Column="0"/>
|
<Label Content="Weinbau-KG:" Margin="10,40,0,10" Grid.Column="0"/>
|
||||||
<ComboBox x:Name="WineKgInput" Margin="0,40,10,10" Grid.Column="1"
|
<ComboBox x:Name="WineKgInput" Margin="0,40,10,10" Grid.Column="1"
|
||||||
|
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