Compare commits

...

33 Commits

Author SHA1 Message Date
c484d27520 Bump version to 0.13.4
All checks were successful
Test / Run tests (push) Successful in 1m54s
Deploy / Build and Deploy (push) Successful in 2m10s
2024-11-25 19:23:20 +01:00
6c7f10cb26 Tests: Update dependencies
All checks were successful
Test / Run tests (push) Successful in 1m55s
2024-11-25 19:19:31 +01:00
4a10e94d71 Elwig: Update dependencies 2024-11-25 19:19:28 +01:00
338f9fe092 AreaComUnderDeliveryData: Also include inactive members with active area commitments
All checks were successful
Test / Run tests (push) Successful in 2m9s
2024-11-25 12:38:04 +01:00
3b97c2243a AreaComUnderDeliveryData: Fix file name 2024-11-25 12:34:43 +01:00
6ba2aa7143 OverUnderDeliveryData: Fix absence of non-deliverers in list
Bug was introduced by commit 9930e6173c772fe433ff69397205872ad30f6a08
and shipped with v0.10.6 (2024-08-30)
2024-11-25 12:17:23 +01:00
a99a23fd08 Tests: Update dependencies
All checks were successful
Test / Run tests (push) Successful in 2m52s
2024-11-17 16:58:09 +01:00
70e01849be Setup: Update dependencies 2024-11-17 16:57:53 +01:00
c3a2f983d5 Elwig: Update dependencies 2024-11-17 16:57:40 +01:00
ebf0c20a90 Bump version to 0.13.3
All checks were successful
Test / Run tests (push) Successful in 2m26s
Deploy / Build and Deploy (push) Successful in 2m21s
2024-11-13 23:35:29 +01:00
2ee0d56dcc Windows: Add MailLogWindow
All checks were successful
Test / Run tests (push) Successful in 1m54s
2024-11-13 18:37:50 +01:00
0a9731af09 MailWindow: Fix small bugs and persist all settings
All checks were successful
Test / Run tests (push) Successful in 1m54s
2024-11-13 18:07:03 +01:00
6718ad4c8d AreaComAdminWindow: Add filters for season
All checks were successful
Test / Run tests (push) Successful in 2m26s
2024-11-10 16:35:44 +01:00
a1d84dd988 MemberAdminWindow: Add more filters for AreaComs 2024-11-10 16:35:03 +01:00
f4fa549130 Windows: Add icons on Buttons and MenuItems
All checks were successful
Test / Run tests (push) Successful in 2m15s
2024-11-10 10:58:44 +01:00
c5453c2fe6 MainWindow: Add 'Flächenbindungen' and 'Liefermenge/Ertrag' button 2024-11-10 10:58:35 +01:00
54deccf021 MainWindow: Add statistics table in Leseabschluss 2024-11-09 09:49:19 +01:00
f5d3a04cb1 Bump version to 0.13.2
All checks were successful
Test / Run tests (push) Successful in 1m50s
Deploy / Build and Deploy (push) Successful in 2m6s
2024-10-13 19:08:20 +02:00
0675c45617 Elwig: Use ExecuteSql/FromSql instead of ExecuteSqlRaw/FromSqlRaw where possible
All checks were successful
Test / Run tests (push) Successful in 1m42s
2024-10-13 18:24:40 +02:00
d1f67dc57d BaseDataWindow: Fix WineAttr/WineCult Id updates in payment variant data
All checks were successful
Test / Run tests (push) Successful in 2m10s
2024-10-13 18:18:31 +02:00
3cbffdbf27 Documents: Add DeliveryDepreciationList
All checks were successful
Test / Run tests (push) Successful in 1m55s
2024-10-13 11:54:18 +02:00
e247925472 Controls: Fix blurry borders when system scaling is enabled
All checks were successful
Test / Run tests (push) Successful in 2m21s
2024-10-12 14:25:24 +02:00
a1b3cff624 [#11] Tests: Add DeliveryServiceTest
All checks were successful
Test / Run tests (push) Successful in 1m56s
2024-10-07 23:43:22 +02:00
65498dd18f App: Fix BranchLocation shortening
All checks were successful
Test / Run tests (push) Successful in 1m46s
2024-10-05 19:31:59 +02:00
27dc4f648f [#11] Tests: Add MemberServiceTest 2024-10-05 19:31:51 +02:00
86f7f693a0 Export/Bki: Format house nr that excel interprets it correctly
All checks were successful
Test / Run tests (push) Successful in 2m10s
2024-10-02 11:08:14 +02:00
8680e51052 Weighing: Fix scale L320 for Baden 2024-10-02 10:45:04 +02:00
1d97f3c422 Bump version to 0.13.1
All checks were successful
Test / Run tests (push) Successful in 1m38s
Deploy / Build and Deploy (push) Successful in 2m12s
2024-09-29 22:25:39 +02:00
b6ae1f5675 Elwig: Update dependencies
All checks were successful
Test / Run tests (push) Successful in 1m37s
2024-09-29 22:24:31 +02:00
c185437b9a DeliveryService: Do not delete own delivery in SplitDeliveryToLsNr()
All checks were successful
Test / Run tests (push) Successful in 2m8s
2024-09-29 22:12:19 +02:00
a2315e84bd Windows: Ask user if they really want to send an email
All checks were successful
Test / Run tests (push) Successful in 1m46s
2024-09-29 09:43:17 +02:00
6ba1973087 MemberAdminWindow: Fix 'Save as PDF' option for DeliveryConfirmation and CreditNote
All checks were successful
Test / Run tests (push) Successful in 2m5s
2024-09-29 09:16:39 +02:00
c62947dacd DeliveryAdminWindow: Add DeliverySplittingDialog
All checks were successful
Test / Run tests (push) Successful in 2m10s
2024-09-28 19:54:50 +02:00
75 changed files with 3829 additions and 973 deletions

View File

@ -3,6 +3,93 @@ Changelog
========= =========
[v0.13.4][v0.13.4] (2024-11-25) {#v0.13.4}
------------------------------------------
### Behobene Fehler {#0.13.4-bugfixes}
* Bei _Unter-/Überlieferungen lt. gez. GA_ waren seit [v0.10.6](#v0.10.6) (2023-08-30) Nicht-Lieferanten nicht aufgeführt. (6ba2aa7143)
* Bei _Unterlieferungen laut Flächenbindungen_ wurden nur Mitglieder berücksichtigt, die zum Zeitpunkt des Exports aktiv waren. (3b97c2243a, 338f9fe092)
### Sonstiges {#0.13.4-misc}
* Abhängigkeiten aktualisiert. (c3a2f983d5, 70e01849be, a99a23fd08, 4a10e94d71, 6c7f10cb26)
[v0.13.4]: https://git.necronda.net/winzer/elwig/releases/tag/v0.13.4
[v0.13.3][v0.13.3] (2024-11-13) {#v0.13.3}
------------------------------------------
### Neue Funktionen {#0.13.3-features}
* Im Haupt-Fenster (`MainWindow`) unter _Leseabschluss_ eine Statistik-Tabelle (Gebunden/Ungeb., Mitglieder/Gewicht/Fläche) hinzugefügt. (54deccf021)
* Im Haupt-Fenster (`MainWindow`) unter _Leseabschluss_ zwei Exportmöglichkeiten (_Flächenbindungen_, _Liefermenge/Ertrag_) hinzugefügt. (c5453c2fe6)
* Ausgangs-Protokoll-Fenster (`MailLogWindow`) zum Ansehen aller ausgehenden E-Mails oder ausgedruckten Rundschreiben hinzugefügt (_Rundschreiben_ -> _Hilfe_). (2ee0d56dcc)
### Behobene Fehler {#0.13.3-bugfixes}
* Im Rundschreiben-Fenster (`MailWindow`) kleinere Fehler behoben und alle Einstellungen werden nun gespeichert. (0a9731af09)
### Sonstiges {#0.13.3-misc}
* In allen Fenstern an passenden Stellen Symbole/Icons hinzugefügt. (f4fa549130)
* Im Mitglieder-Fenster (`MemberAdminWindow`) Filter `Flächenbindung` (Mitglieder mit irgendeiner aktiven Flächenbindung) hinzugefügt. (a1d84dd988)
* Im Flächenbindungen-Fenster (`AreaComAdminWindow`) Filter für explizite Saisons hinzugefügt. (6718ad4c8d)
[v0.13.3]: https://git.necronda.net/winzer/elwig/releases/tag/v0.13.3
[v0.13.2][v0.13.2] (2024-10-13) {#v0.13.2}
------------------------------------------
### Neue Funktionen {#0.13.2-features}
* Im Lieferungen-Fenster den Menüpunkt _Abwertungsliste_ (`DeliveryDepreciationList`) hinzugefügt. (3cbffdbf27)
### Behobene Fehler {#0.13.2-bugfixes}
* Fehler im Waagenprotokoll `Avery-Async` (L320) behoben. (8680e51052)
* Hausnummern in der BKI Traubentransportscheinliste werden in Excel richtig angezeigt. (86f7f693a0)
* Beim Ändern der Identifikatoren von Attributen/Bewirtschaftungsarten wurden diese in Auszahlungsvarianten nicht aktualisiert. (d1f67dc57d)
### Sonstiges {#0.13.2-misc}
* Weitere automatisierte Tests hinzugefügt. ([#11][i11])
* Namenszusätze bei Gemeinden (z.B. an, bei, im, am) genauer angegeben. (65498dd18f)
* Bei einigen Eingabefeldern waren die Ränder unscharf. (e247925472)
* Wo leicht möglich wird `ExecuteSql`/`FromSql` statt `ExecuteSqlRaw`/`FromSqlRaw` verwendet. (0675c45617)
[v0.13.2]: https://git.necronda.net/winzer/elwig/releases/tag/v0.13.2
[i11]: https://git.necronda.net/winzer/elwig/issues/11
[v0.13.1][v0.13.1] (2024-09-29) {#v0.13.1}
------------------------------------------
### Neue Funktionen {#0.13.1-features}
* Das Extrahieren/Abwerten/Aufteilen von (Teil-)Lieferungen wurde grundlegend überarbeitet und funktioniert ab jetzt in einem einzigen, übersichtlicheren Dialog. (c62947dacd, c185437b9a)
### Behobene Fehler {#0.13.1-bugfixes}
* Im Mitglieder-Fenster (`MemberAdminWinodw`) wurden bei `Anlieferungsbestätigung -> speichern (PDF)` und `Traubengutschrift -> speichern (PDF)` E-Mails verschickt, anstatt ein PDF gespeichert. (6ba1973087, a2315e84bd)
### Sonstiges {#0.13.1-misc}
* Abhängigkeiten aktualisiert. (b6ae1f5675)
[v0.13.1]: https://git.necronda.net/winzer/elwig/releases/tag/v0.13.1
[v0.13.0][v0.13.0] (2024-09-25) {#v0.13.0} [v0.13.0][v0.13.0] (2024-09-25) {#v0.13.0}
------------------------------------------ ------------------------------------------
@ -86,7 +173,7 @@ Changelog
### Neue Funktionen {#v0.11.2-features} ### Neue Funktionen {#v0.11.2-features}
* In der Anmeldeliste (`DeliveryAncmtList`) wird die Stamm-KG des Mitgliedes angegeben. (165770fa37) * In der Anmeldeliste (`DeliveryAncmtList`) wird die Stamm-KG des Mitgliedes angegeben. (165770fa37)
* Im Hauptmenü wurde im Menüpunkt _Hilfe_ ein Fehler-Protokoll-Fenster (`LogWindow`) hinzugefügt. (526e951029) * Im Haupt-Fenster (`MainWindow`) wurde im Menüpunkt _Hilfe_ ein Fehler-Protokoll-Fenster (`LogWindow`) hinzugefügt. (526e951029)
### Behobene Fehler {#v0.11.2-bugfixes} ### Behobene Fehler {#v0.11.2-bugfixes}
@ -193,7 +280,7 @@ Changelog
### Behobene Fehler {#v0.10.6-bugfixes} ### Behobene Fehler {#v0.10.6-bugfixes}
* Der Titel des Flächenbindungs-Fensters (`AreaComAdminWindow`) ist jetzt _Flächenbindungen_, nicht mehr _Lieferungen_. (ee1315929c) * Der Titel des Flächenbindungen-Fensters (`AreaComAdminWindow`) ist jetzt _Flächenbindungen_, nicht mehr _Lieferungen_. (ee1315929c)
* Im Auszahlungsvariante-Fenster (`ChartWindow`) einen Skalierungs-Fehler behoben. ([#33][i33]) * Im Auszahlungsvariante-Fenster (`ChartWindow`) einen Skalierungs-Fehler behoben. ([#33][i33])
* Versuch: Fehler bei automatischer Daten-Erneuerung bei längerer Benutzung. (8c8c0a8c2b) * Versuch: Fehler bei automatischer Daten-Erneuerung bei längerer Benutzung. (8c8c0a8c2b)
@ -510,7 +597,7 @@ Changelog
### Behobene Fehler {#v0.8.4-bugfixes} ### Behobene Fehler {#v0.8.4-bugfixes}
* Falls beim Schließen des Hauptmenüs ein anderes Fenster im Bearbeiten-/Erstellen-Modus war führte das zu einem Absturz. (70f8276808) * Falls beim Schließen des Haupt-Fensters (`MainWindow`) ein anderes Fenster im Bearbeiten-/Erstellen-Modus war führte das zu einem Absturz. (70f8276808)
### Sonstiges {#v0.8.4-misc} ### Sonstiges {#v0.8.4-misc}

View File

@ -7,9 +7,6 @@ using Elwig.Helpers;
using Elwig.Helpers.Weighing; using Elwig.Helpers.Weighing;
using System.Collections.Generic; using System.Collections.Generic;
using System.Windows.Threading; using System.Windows.Threading;
using System.Globalization;
using System.Threading;
using System.Windows.Markup;
using System.Reflection; using System.Reflection;
using Elwig.Helpers.Printing; using Elwig.Helpers.Printing;
using Elwig.Windows; using Elwig.Windows;
@ -29,6 +26,7 @@ namespace Elwig {
private readonly DispatcherTimer _autoUpdateTimer = new() { Interval = TimeSpan.FromHours(1) }; private readonly DispatcherTimer _autoUpdateTimer = new() { Interval = TimeSpan.FromHours(1) };
public static readonly string DataPath = @"C:\ProgramData\Elwig\"; public static readonly string DataPath = @"C:\ProgramData\Elwig\";
public static readonly string MailsPath = Path.Combine(DataPath, "mails");
public static readonly string ConfigPath = Path.Combine(DataPath, "config.ini"); public static readonly string ConfigPath = Path.Combine(DataPath, "config.ini");
public static readonly string ExePath = @"C:\Program Files\Elwig\"; public static readonly string ExePath = @"C:\Program Files\Elwig\";
public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig"); public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig");
@ -59,10 +57,11 @@ namespace Elwig {
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Directory.CreateDirectory(TempPath); Directory.CreateDirectory(TempPath);
Directory.CreateDirectory(DataPath); Directory.CreateDirectory(DataPath);
Directory.CreateDirectory(MailsPath);
MainDispatcher = Dispatcher; MainDispatcher = Dispatcher;
Scales = []; Scales = [];
CurrentApp = this; CurrentApp = this;
OverrideCulture(); Utils.OverrideCulture();
var args = Environment.GetCommandLineArgs(); var args = Environment.GetCommandLineArgs();
if (args.Length >= 2) { if (args.Length >= 2) {
@ -82,23 +81,6 @@ namespace Elwig {
MainDispatcher.BeginInvoke(HintContextChange); MainDispatcher.BeginInvoke(HintContextChange);
} }
private static void OverrideCulture() {
var locale = new CultureInfo("de-AT", false);
locale.NumberFormat.CurrencyGroupSeparator = Utils.GroupSeparator;
locale.NumberFormat.NumberGroupSeparator = Utils.GroupSeparator;
locale.NumberFormat.PercentGroupSeparator = Utils.GroupSeparator;
CultureInfo.CurrentCulture = locale;
CultureInfo.CurrentUICulture = locale;
Thread.CurrentThread.CurrentCulture = locale;
Thread.CurrentThread.CurrentUICulture = locale;
CultureInfo.DefaultThreadCurrentCulture = locale;
CultureInfo.DefaultThreadCurrentUICulture = locale;
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.Name))
);
}
protected override async void OnStartup(StartupEventArgs evt) { protected override async void OnStartup(StartupEventArgs evt) {
Version = new Version(typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion.Split('+')[0] ?? "0.0.0"); Version = new Version(typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion.Split('+')[0] ?? "0.0.0");
@ -183,6 +165,9 @@ namespace Elwig {
} }
private async void Application_Exit(object sender, ExitEventArgs evt) { private async void Application_Exit(object sender, ExitEventArgs evt) {
foreach (var s in EventScales) {
s.Dispose();
}
await Pdf.Cleanup(); await Pdf.Cleanup();
} }
@ -194,7 +179,13 @@ namespace Elwig {
ZwstId = entry.Item1; ZwstId = entry.Item1;
BranchName = entry.Item2; BranchName = entry.Item2;
BranchPlz = entry.Item3; BranchPlz = entry.Item3;
BranchLocation = entry.Item4?.Split(" im ")[0].Split(" an ")[0].Split(" bei ")[0]; // FIXME BranchLocation = entry.Item4?
.Split(" in ")[0]
.Split(" im ")[0]
.Split(" an ")[0]
.Split(" am ")[0]
.Split(" bei ")[0]
.Split(" beim ")[0];
BranchAddress = entry.Item5; BranchAddress = entry.Item5;
BranchPhoneNr = entry.Item6; BranchPhoneNr = entry.Item6;
BranchFaxNr = entry.Item7; BranchFaxNr = entry.Item7;
@ -202,6 +193,7 @@ namespace Elwig {
} }
public static void HintContextChange() { public static void HintContextChange() {
if (CurrentApp == null) return;
var ch = CurrentLastWrite; var ch = CurrentLastWrite;
if (ch > CurrentApp.LastChanged) if (ch > CurrentApp.LastChanged)
CurrentApp.LastChanged = ch; CurrentApp.LastChanged = ch;

View File

@ -15,9 +15,8 @@
<ColumnDefinition Width="18"/> <ColumnDefinition Width="18"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Border x:Name="Border" BorderThickness="1,1,0,1" <Border x:Name="Border" BorderThickness="1,1,0,1" Grid.RowSpan="2"
BorderBrush="{Binding Path=BorderBrush, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}" BorderBrush="{Binding Path=BorderBrush, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}">
SnapsToDevicePixels="True" Grid.RowSpan="2">
<ScrollViewer x:Name="PART_ContentHost" VerticalAlignment="Center"/> <ScrollViewer x:Name="PART_ContentHost" VerticalAlignment="Center"/>
</Border> </Border>
@ -43,6 +42,8 @@
</Setter.Value> </Setter.Value>
</Setter> </Setter>
<Setter Property="TextAlignment" Value="Right"/> <Setter Property="TextAlignment" Value="Right"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="UseLayoutRounding" Value="True"/>
<Style.Triggers> <Style.Triggers>
<Trigger Property="IsEnabled" Value="False"> <Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="Gray"/> <Setter Property="Foreground" Value="Gray"/>

View File

@ -7,9 +7,8 @@
<ControlTemplate TargetType="ctrl:UnitTextBox"> <ControlTemplate TargetType="ctrl:UnitTextBox">
<Border x:Name="Border" <Border x:Name="Border"
BorderThickness="{Binding Path=BorderThickness, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}" BorderThickness="{Binding Path=BorderThickness, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
BorderBrush="{Binding Path=BorderBrush, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}" BorderBrush="{Binding Path=BorderBrush, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}">
SnapsToDevicePixels="True"> <Grid Background="{Binding Path=Background, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}">
<Grid>
<ScrollViewer x:Name="PART_ContentHost" VerticalAlignment="Bottom"> <ScrollViewer x:Name="PART_ContentHost" VerticalAlignment="Bottom">
<ScrollViewer.Margin> <ScrollViewer.Margin>
<Binding ElementName="UnitBlock" Path="ActualWidth"> <Binding ElementName="UnitBlock" Path="ActualWidth">
@ -32,6 +31,8 @@
</Setter.Value> </Setter.Value>
</Setter> </Setter>
<Setter Property="TextAlignment" Value="Right"/> <Setter Property="TextAlignment" Value="Right"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="UseLayoutRounding" Value="True"/>
<Style.Triggers> <Style.Triggers>
<Trigger Property="IsEnabled" Value="False"> <Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="Gray"/> <Setter Property="Foreground" Value="Gray"/>

View File

@ -1,56 +0,0 @@
<Window x:Class="Elwig.Dialogs.AbwertenDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Dialogs"
ResizeMode="NoResize" ShowInTaskbar="False" Topmost="True"
WindowStartupLocation="CenterOwner"
FocusManager.FocusedElement="{Binding ElementName=WeightInput}"
Title="Teillieferung abwerten" Height="190" Width="400">
<Window.Resources>
<Style TargetType="Label">
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="Padding" Value="2,4,2,4"/>
<Setter Property="Height" Value="25"/>
</Style>
<Style TargetType="TextBox">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="Height" Value="25"/>
<Setter Property="TextWrapping" Value="NoWrap"/>
</Style>
<Style TargetType="Button">
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="25"/>
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Margin="10,10,10,10" Grid.ColumnSpan="2" TextWrapping="Wrap" TextAlignment="Center">
Welche Menge der Teillieferung <Run x:Name="TextLsNr" FontWeight="Bold" Text="20201010A000/1"/><LineBreak/>
von <Run x:Name="TextMember" FontWeight="Bold" Text="Max Mustermann"/><LineBreak/>
mit <Run x:Name="TextWeight" FontWeight="Bold" Text="1&#x202f;000&#x202f;kg"/> soll abgewertet werden?
</TextBlock>
<Label Content="Gewicht:" Margin="10,70,10,10"/>
<Grid Grid.Column="1" Width="70" Height="25" Margin="0,70,10,10" HorizontalAlignment="Left" VerticalAlignment="Top">
<TextBox x:Name="WeightInput" TextAlignment="Right" Padding="2,2,17,2"
TextChanged="WeightInput_TextChanged"/>
<Label Content="kg" Margin="0,4,3,0" HorizontalAlignment="Right" FontSize="10" Padding="2,4,2,4"/>
</Grid>
<Button x:Name="ConfirmButton" Content="Bestätigen" Margin="10,10,115,10" Grid.Column="1" IsEnabled="False" IsDefault="True"
Click="ConfirmButton_Click"/>
<Button x:Name="CancelButton" Content="Abbrechen" Margin="10,10,10,10" Grid.Column="1" IsCancel="True"/>
</Grid>
</Window>

View File

@ -1,33 +0,0 @@
using Elwig.Helpers;
using System.Windows;
using System.Windows.Controls;
namespace Elwig.Dialogs {
public partial class AbwertenDialog : Window {
public int Weight;
public AbwertenDialog(string lsnr, string name, int weight) {
Weight = weight;
InitializeComponent();
TextLsNr.Text = lsnr;
TextMember.Text = name;
TextWeight.Text = $"{weight:N0}{Utils.UnitSeparator}kg";
}
private void ConfirmButton_Click(object sender, RoutedEventArgs evt) {
DialogResult = true;
Weight = int.Parse(WeightInput.Text);
Close();
}
private void UpdateButtons() {
ConfirmButton.IsEnabled = int.TryParse(WeightInput.Text, out var w) && w > 0 && w <= Weight;
}
private void WeightInput_TextChanged(object sender, TextChangedEventArgs evt) {
Validator.CheckInteger(WeightInput, true, 5);
UpdateButtons();
}
}
}

View File

@ -57,8 +57,8 @@
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed" Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
IsChecked="{Binding DeletePaymentData}"/> IsChecked="{Binding DeletePaymentData}"/>
<Button x:Name="ConfirmButton" Content="Bestätigen" Margin="10,10,115,10" Grid.Column="1" IsEnabled="False" <Button x:Name="ConfirmButton" Content="Bestätigen" Margin="10,10,115,10" IsEnabled="False"
Click="ConfirmButton_Click"/> Click="ConfirmButton_Click"/>
<Button x:Name="CancelButton" Content="Abbrechen" Margin="10,10,10,10" Grid.Column="1" IsCancel="True"/> <Button x:Name="CancelButton" Content="Abbrechen" Margin="10,10,10,10" IsCancel="True"/>
</Grid> </Grid>
</Window> </Window>

View File

@ -1,57 +0,0 @@
<Window x:Class="Elwig.Dialogs.DeliveryExtractionDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Dialogs"
ResizeMode="NoResize" ShowInTaskbar="False" Topmost="True"
WindowStartupLocation="CenterOwner"
FocusManager.FocusedElement="{Binding ElementName=WeightInput}"
Title="Teillieferung extrahieren" Height="210" Width="380">
<Window.Resources>
<Style TargetType="Label">
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="Padding" Value="2,4,2,4"/>
<Setter Property="Height" Value="25"/>
</Style>
<Style TargetType="TextBox">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="Height" Value="25"/>
<Setter Property="TextWrapping" Value="NoWrap"/>
</Style>
<Style TargetType="Button">
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="25"/>
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Margin="10,10,0,10" TextWrapping="Wrap">
Was soll mit der Teillieferung <Run x:Name="TextLsNr" FontWeight="Bold" Text="20201010A000/1"/><LineBreak/>
von <Run x:Name="TextMember" FontWeight="Bold" Text="Max Mustermann"/> geschehen?
</TextBlock>
<RadioButton x:Name="NewDeliveryButton" Content="Neue Lieferung erstellen"
Margin="10,80,0,10" HorizontalAlignment="Left" VerticalAlignment="Top"
Checked="Selection_Changed" Unchecked="Selection_Changed"/>
<RadioButton x:Name="AddToDeliveryButton" Content="Zu Lieferung hinzufügen"
Margin="10,100,0,10" HorizontalAlignment="Left" VerticalAlignment="Top"
Checked="Selection_Changed" Unchecked="Selection_Changed"/>
<ListBox x:Name="DeliveryList" Grid.Column="1" Margin="10,10,10,45"
SelectionChanged="DeliveryList_SelectionChanged"/>
<Button x:Name="ConfirmButton" Content="Bestätigen" Margin="10,10,115,10" Grid.ColumnSpan="2" IsEnabled="False" IsDefault="True"
Click="ConfirmButton_Click"/>
<Button x:Name="CancelButton" Content="Abbrechen" Margin="10,10,10,10" Grid.ColumnSpan="2" IsCancel="True"/>
</Grid>
</Window>

View File

@ -1,37 +0,0 @@
using System.Collections.Generic;
using System.Windows;
namespace Elwig.Dialogs {
public partial class DeliveryExtractionDialog : Window {
public string? AddTo;
public DeliveryExtractionDialog(string lsnr, string name, bool single, IEnumerable<string> lsnrs) {
InitializeComponent();
TextLsNr.Text = lsnr;
TextMember.Text = name;
NewDeliveryButton.IsEnabled = !single;
DeliveryList.IsEnabled = false;
DeliveryList.ItemsSource = lsnrs;
}
private void ConfirmButton_Click(object sender, RoutedEventArgs evt) {
DialogResult = true;
AddTo = NewDeliveryButton.IsChecked == true ? "new" : DeliveryList.SelectedItem as string;
Close();
}
private void UpdateButtons() {
ConfirmButton.IsEnabled = NewDeliveryButton.IsChecked == true || (AddToDeliveryButton.IsChecked == true && DeliveryList.SelectedItem != null);
DeliveryList.IsEnabled = AddToDeliveryButton.IsChecked == true;
}
private void Selection_Changed(object sender, RoutedEventArgs evt) {
UpdateButtons();
}
private void DeliveryList_SelectionChanged(object sender, RoutedEventArgs evt) {
UpdateButtons();
}
}
}

View File

@ -0,0 +1,104 @@
<local:ContextWindow
x:Class="Elwig.Dialogs.DeliverySplittingDialog"
AutomationProperties.AutomationId="DeliverySplittingDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Elwig.Windows"
xmlns:ctrl="clr-namespace:Elwig.Controls"
ResizeMode="NoResize" ShowInTaskbar="False" Topmost="True"
WindowStartupLocation="CenterOwner"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="Lieferung abwerten oder aufteilen" Height="400" Width="600">
<Window.Resources>
<Style TargetType="Button">
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="25"/>
</Style>
</Window.Resources>
<Grid>
<RadioButton x:Name="DepreciateModeInput" GroupName="ModeInput" Content="Abwerten" Margin="15,10,10,10" IsChecked="True"
VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="ModeInput_Changed"/>
<RadioButton x:Name="MemberModeInput" GroupName="ModeInput" Content="Auf Mitglied übertragen" Margin="15,30,10,10"
VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="ModeInput_Changed"/>
<RadioButton x:Name="DeliveryModeInput" GroupName="ModeInput" Content="Zu anderer Lieferung hinzufügen" Margin="15,50,10,10"
VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="ModeInput_Changed"/>
<TextBox x:Name="MgNrInput" FontSize="14" Padding="2" Visibility="Hidden"
Width="48" Margin="220,10,0,0" Height="25" TextAlignment="Right"
TextChanged="MgNrInput_TextChanged"
VerticalAlignment="Top" HorizontalAlignment="Left"/>
<ComboBox x:Name="MemberInput" FontSize="14" Visibility="Hidden"
Margin="273,10,40,10" IsEditable="True" Height="25"
ItemTemplate="{StaticResource MemberAdminNameTemplate}" TextSearch.TextPath="AdministrativeName"
SelectionChanged="MemberInput_SelectionChanged"
VerticalAlignment="Top" HorizontalAlignment="Stretch"/>
<Button x:Name="MemberReferenceButton" Height="25" Width="25" FontFamily="Segoe MDL2 Assets" Content="&#xEE35;" Padding="0"
Margin="10,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Right" ToolTip="Zu Mitglied springen" FontSize="14" Visibility="Hidden"
Click="MemberReferenceButton_Click"/>
<ComboBox x:Name="DeliveryInput" FontSize="14" Visibility="Hidden"
Margin="220,10,10,10" Height="25"
TextSearch.TextPath="LsNr"
SelectionChanged="DeliveryInput_SelectionChanged"
VerticalAlignment="Top" HorizontalAlignment="Stretch">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding LsNr}" Width="100"/>
<TextBlock Text="{Binding Weight, StringFormat='{}{0:N0} kg'}" Width="80" TextAlignment="Right" Margin="0,0,10,0"/>
<TextBlock Text="{Binding Member.AdministrativeName}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock x:Name="InfoBlock" Margin="230,45,10,10" FontSize="14" TextAlignment="Right"
VerticalAlignment="Top" HorizontalAlignment="Stretch"
Text="Insgesamt 0 kg / 0 kg ausgewählt."/>
<ListBox x:Name="DeliveryPartList" Margin="10,75,10,40" ItemsSource="{Binding DeliveryParts, Mode=TwoWay}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Focusable" Value="False"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Part.DPNr}" Width="20" TextAlignment="Right"
VerticalAlignment="Center" Margin="0,0,5,0" FontSize="14"/>
<TextBlock Text="{Binding Part.SortId}" Width="40" TextAlignment="Center"
VerticalAlignment="Center" Margin="0,0,0,0" FontSize="14"/>
<TextBlock Text="{Binding Part.Kmw, StringFormat='{}{0:N1}°'}" Width="40" TextAlignment="Right" Padding="0,0,10,0"
VerticalAlignment="Center"/>
<TextBlock Text="{Binding Part.QualId}" Width="30"
VerticalAlignment="Center"/>
<TextBlock Text="{Binding Part.Weight, StringFormat='{}{0:N0} kg'}" Width="70" TextAlignment="Right"
VerticalAlignment="Center" Margin="0,0,10,0" FontSize="14"/>
<TextBlock Text="{Binding Part.Attribute.Name}" Width="60"
VerticalAlignment="Center"/>
<TextBlock Text="{Binding Part.Cultivation.Name}" Width="50"
VerticalAlignment="Center"/>
<CheckBox IsChecked="{Binding SplitCompletely, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Content="Vollständig" Tag="{Binding Part.DPNr}"
VerticalAlignment="Center" Margin="20,0,5,0"
Checked="SplitCompletelyInput_Changed" Unchecked="SplitCompletelyInput_Changed"/>
<ctrl:UnitTextBox Text="{Binding SplitWeightString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Unit="kg" Width="70" Height="25" IsEnabled="{Binding SplitWeightEnabled}" Tag="{Binding Part.DPNr}"
VerticalAlignment="Center" Margin="5,0,5,0" FontSize="14" Padding="2" Background="White"
TextChanged="SplitWeightInput_TextChanged"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button x:Name="ConfirmButton" Content="Bestätigen" Margin="10,10,115,10" IsEnabled="False"
Click="ConfirmButton_Click"/>
<Button x:Name="CancelButton" Content="Abbrechen" Margin="10,10,10,10" IsCancel="True"/>
</Grid>
</local:ContextWindow>

View File

@ -0,0 +1,163 @@
using Elwig.Helpers;
using Elwig.Models.Entities;
using Elwig.Windows;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace Elwig.Dialogs {
public partial class DeliverySplittingDialog : ContextWindow {
public class Row {
public DeliveryPart Part { get; set; }
public bool SplitCompletely { get; set; }
public string? SplitWeightString { get; set; }
public int? SplitWeight {
get => int.TryParse(SplitWeightString, out var v) ? v : null;
set => SplitWeightString = $"{value}";
}
public bool SplitWeightEnabled { get; set; }
}
private readonly Delivery _delivery;
public int? MgNr { get; set; }
public string? LsNr { get; set; }
public int[]? Weights => DeliveryParts.Select(r => r.SplitCompletely ? r.Part.Weight : r.SplitWeight ?? 0).ToArray();
public ObservableCollection<Row> DeliveryParts { get; set; }
public DeliverySplittingDialog(Delivery d) {
_delivery = d;
DeliveryParts = new(d.Parts.Select(p => new Row {
Part = p,
SplitCompletely = false,
SplitWeight = null,
SplitWeightEnabled = true,
}).ToList());
InitializeComponent();
}
protected override async Task OnRenewContext(AppDbContext ctx) {
ControlUtils.RenewItemsSource(MemberInput, await ctx.Members
.Where(m => m.IsActive)
.OrderBy(m => m.Name)
.ThenBy(m => m.GivenName)
.ToListAsync());
ControlUtils.RenewItemsSource(DeliveryInput, await ctx.Deliveries
.Where(d => d.DateString == $"{_delivery.Date:yyyy-MM-dd}" && d.ZwstId == _delivery.ZwstId)
.OrderBy(d => d.LsNr)
.Include(d => d.Member)
.Include(d => d.Parts)
.ToListAsync());
if (DeliveryInput.SelectedItem == null)
ControlUtils.SelectItem(DeliveryInput, _delivery);
CheckValidity();
}
private void ConfirmButton_Click(object sender, RoutedEventArgs evt) {
if (DepreciateModeInput.IsChecked == true) {
MgNr = null;
LsNr = null;
} else if (MemberModeInput.IsChecked == true) {
MgNr = ((Member)MemberInput.SelectedItem).MgNr;
LsNr = null;
} else if (DeliveryModeInput.IsChecked == true) {
MgNr = null;
LsNr = ((Delivery)DeliveryInput.SelectedItem).LsNr;
}
DialogResult = true;
Close();
}
private void ModeInput_Changed(object sender, RoutedEventArgs evt) {
if (!IsLoaded) return;
CheckValidity();
if (DepreciateModeInput.IsChecked == true) {
MgNrInput.Visibility = Visibility.Hidden;
MemberInput.Visibility = Visibility.Hidden;
MemberReferenceButton.Visibility = Visibility.Hidden;
DeliveryInput.Visibility = Visibility.Hidden;
} else if (MemberModeInput.IsChecked == true) {
MgNrInput.Visibility = Visibility.Visible;
MemberInput.Visibility = Visibility.Visible;
MemberReferenceButton.Visibility = Visibility.Visible;
DeliveryInput.Visibility = Visibility.Hidden;
} else if (DeliveryModeInput.IsChecked == true) {
MgNrInput.Visibility = Visibility.Hidden;
MemberInput.Visibility = Visibility.Hidden;
MemberReferenceButton.Visibility = Visibility.Hidden;
DeliveryInput.Visibility = Visibility.Visible;
}
}
private void CheckValidity() {
var weight = DeliveryParts.Sum(r => r.SplitCompletely ? r.Part.Weight : r.SplitWeight ?? 0);
var total = DeliveryParts.Sum(r => r.Part.Weight);
InfoBlock.Text = $"Insgesamt {weight:N0} kg / {total:N0} kg ausgewählt.";
ConfirmButton.IsEnabled = DeliveryParts.Any(r => r.SplitCompletely || r.SplitWeight > 0) && (
DepreciateModeInput.IsChecked == true ||
(MemberModeInput.IsChecked == true && MemberInput.SelectedItem != null && !DeliveryParts.All(r => r.SplitCompletely)) ||
(DeliveryModeInput.IsChecked == true && DeliveryInput.SelectedItem != null));
}
private void MgNrInput_TextChanged(object sender, TextChangedEventArgs evt) {
var res = Validator.CheckMgNr((TextBox)sender, true);
var text = MgNrInput.Text;
var caret = MgNrInput.CaretIndex;
ControlUtils.SelectItemWithPk(MemberInput, res.IsValid ? int.Parse(MgNrInput.Text) : null);
MgNrInput.Text = text;
MgNrInput.CaretIndex = caret;
}
private void MemberInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
var m = MemberInput.SelectedItem as Member;
MgNrInput.Text = m?.MgNr.ToString();
CheckValidity();
}
private void MemberReferenceButton_Click(object sender, RoutedEventArgs evt) {
if (MemberInput.SelectedItem is not Member m) return;
App.FocusMember(m.MgNr);
}
private void DeliveryInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
CheckValidity();
}
private void SplitCompletelyInput_Changed(object sender, RoutedEventArgs evt) {
var checkbox = (CheckBox)sender;
var dpnr = int.Parse(checkbox.Tag.ToString()!);
var row = DeliveryParts.First(d => d.Part.DPNr == dpnr);
if (checkbox.IsChecked == true) {
row.SplitWeightEnabled = false;
row.SplitWeight = row.Part.Weight;
} else if (checkbox.IsChecked == false) {
row.SplitWeightEnabled = true;
row.SplitWeight = null;
}
CollectionViewSource.GetDefaultView(DeliveryParts).Refresh();
CheckValidity();
}
private void SplitWeightInput_TextChanged(object sender, TextChangedEventArgs evt) {
var textbox = (TextBox)sender;
Validator.CheckInteger(textbox, false, 6);
var dpnr = int.Parse(textbox.Tag.ToString()!);
var row = DeliveryParts.First(d => d.Part.DPNr == dpnr);
var w = int.TryParse(textbox.Text, out var v) ? v : (int?)null;
if (w >= row.Part.Weight) {
row.SplitCompletely = true;
row.SplitWeightEnabled = false;
row.SplitWeight = row.Part.Weight;
CollectionViewSource.GetDefaultView(DeliveryParts).Refresh();
}
CheckValidity();
}
}
}

View File

@ -9,8 +9,8 @@
<table class="credit"> <table class="credit">
<colgroup> <colgroup>
<col style="width: 25mm;"/> <col style="width: 25mm;"/>
<col style="width: 5mm;"/> <col style="width: 6mm;"/>
<col style="width: 22mm;"/> <col style="width: 21mm;"/>
<col style="width: 15mm;"/> <col style="width: 15mm;"/>
<col style="width: 10mm;"/> <col style="width: 10mm;"/>
<col style="width: 10mm;"/> <col style="width: 10mm;"/>
@ -51,7 +51,7 @@
<tr class="@(i == 0 ? "first" : "") @(rows == i + 1 ? "last" : "")"> <tr class="@(i == 0 ? "first" : "") @(rows == i + 1 ? "last" : "")">
@if (i == 0) { @if (i == 0) {
<td rowspan="@rows">@p.LsNr</td> <td rowspan="@rows">@p.LsNr</td>
<td rowspan="@rows">@p.DPNr</td> <td rowspan="@rows" class="center narrow">@p.DPNr</td>
<td class="small">@p.Variety</td> <td class="small">@p.Variety</td>
<td class="small"> <td class="small">
@p.Attribute@(p.Attribute != null && p.Cultivation != null ? " / " : "")@p.Cultivation @p.Attribute@(p.Attribute != null && p.Cultivation != null ? " / " : "")@p.Cultivation

View File

@ -9,8 +9,8 @@
<table class="delivery-confirmation"> <table class="delivery-confirmation">
<colgroup> <colgroup>
<col style="width: 25mm;"/> <col style="width: 25mm;"/>
<col style="width: 5mm;"/> <col style="width: 6mm;"/>
<col style="width: 24mm;"/> <col style="width: 23mm;"/>
<col style="width: 16mm;"/> <col style="width: 16mm;"/>
<col style="width: 17mm;"/> <col style="width: 17mm;"/>
<col style="width: 10mm;"/> <col style="width: 10mm;"/>
@ -60,7 +60,7 @@
<tr class="@(first ? "first" : "") @(p.Variety != lastVariety && lastVariety != "" ? "new": "") @(rows > i + 1 ? "last" : "")"> <tr class="@(first ? "first" : "") @(p.Variety != lastVariety && lastVariety != "" ? "new": "") @(rows > i + 1 ? "last" : "")">
@if (first) { @if (first) {
<td rowspan="@rows">@p.LsNr</td> <td rowspan="@rows">@p.LsNr</td>
<td rowspan="@rows">@p.DPNr</td> <td rowspan="@rows" class="center narrow">@p.DPNr</td>
<td class="small">@p.Variety</td> <td class="small">@p.Variety</td>
<td class="small">@p.Attribute@(p.Attribute != null && p.Cultivation != null ? " / " : "")@p.Cultivation</td> <td class="small">@p.Attribute@(p.Attribute != null && p.Cultivation != null ? " / " : "")@p.Cultivation</td>
<td class="small">@p.QualityLevel</td> <td class="small">@p.QualityLevel</td>

View File

@ -0,0 +1,22 @@
using Elwig.Models.Dtos;
using System.Collections.Generic;
namespace Elwig.Documents {
public class DeliveryDepreciationList : Document {
public new static string Name => "Abwertungsliste";
public string Filter;
public IEnumerable<DeliveryJournalRow> Deliveries;
public DeliveryDepreciationList(string filter, IEnumerable<DeliveryJournalRow> deliveries) :
base($"{Name} {filter}") {
Filter = filter;
Deliveries = deliveries;
}
public DeliveryDepreciationList(string filter, DeliveryJournalData data) :
this(filter, data.Rows) {
}
}
}

View File

@ -0,0 +1,104 @@
@using RazorLight
@inherits TemplatePage<Elwig.Documents.DeliveryDepreciationList>
@model Elwig.Documents.DeliveryDepreciationList
@{ Layout = "Document"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\DeliveryDepreciationList.css" />
<main>
<h1>Abwertungsliste</h1>
<h2>@Model.Filter</h2>
<table class="journal">
<colgroup>
<col style="width: 25mm;"/>
<col style="width: 6mm;"/>
<col style="width: 20mm;"/>
<col style="width: 15mm;"/>
<col style="width: 35mm;"/>
<col style="width: 30mm;"/>
<col style="width: 10mm;"/>
<col style="width: 10mm;"/>
<col style="width: 14mm;"/>
</colgroup>
<thead>
<tr>
<th rowspan="2" style="text-align: left;">Lieferschein-Nr.</th>
<th rowspan="2" class="narrow">Pos.</th>
<th rowspan="2">Datum</th>
<th rowspan="2">Zeit</th>
<th rowspan="2" style="text-align: left;">Sorte</th>
<th rowspan="2" style="text-align: left;">Attr./Bewirt.</th>
<th colspan="2">Gradation</th>
<th>Gewicht</th>
</tr>
<tr>
<th class="unit">[°Oe]</th>
<th class="unit narrow">[°KMW]</th>
<th class="unit">[kg]</th>
</tr>
</thead>
<tbody>
@{
int? lastMember = null;
}
@foreach (var p in Model.Deliveries) {
if (lastMember != p.MgNr) {
<tr class="subheading @(lastMember != null ? "new" : "")">
@{
var memberDeliveries = Model.Deliveries.Where(d => d.MgNr == p.MgNr).ToList();
var memberKmw = Elwig.Helpers.Utils.AggregateDeliveryPartsKmw(memberDeliveries);
var memberOe = Elwig.Helpers.Utils.KmwToOe(memberKmw);
}
<th colspan="5">
@($"{p.MgNr}, {p.AdministrativeName}")
</th>
<td>Teil-Lfrg.: <span style="float: right;">@($"{memberDeliveries.Count():N0}")</span></td>
<td class="center">@($"{memberOe:N0}")</td>
<td class="center">@($"{memberKmw:N1}")</td>
<td class="number">@($"{memberDeliveries.Sum(p => p.Weight):N0}")</td>
</tr>
}
<tr>
<td>@p.LsNr</td>
<td class="center narrow">@p.Pos</td>
<td>@($"{p.Date:dd.MM.yyyy}")</td>
<td>@($"{p.Time:HH:mm}")</td>
<td>@p.Variety</td>
<td>@p.Attribute@(p.Attribute != null && p.Cultivation != null ? " / " : "")@p.Cultivation</td>
<td class="center">@($"{p.Oe:N0}")</td>
<td class="center">@($"{p.Kmw:N1}")</td>
<td class="number">@($"{p.Weight:N0}")</td>
</tr>
lastMember = p.MgNr;
}
@{
var branches = Model.Deliveries.Select(d => d.DeliveryBranch).Distinct().Order().ToArray();
if (branches.Length > 1) {
foreach (var b in branches) {
<tr class="@(branches[0] == b ? "sum" : "") bold">
@{
var branchDeliveries = Model.Deliveries.Where(d => d.DeliveryBranch == b).ToList();
var branchKmw = Elwig.Helpers.Utils.AggregateDeliveryPartsKmw(branchDeliveries);
var branchOe = Elwig.Helpers.Utils.KmwToOe(branchKmw);
}
<td colspan="2">@b:</td>
<td colspan="4">(Teil-)Lieferungen: @($"{branchDeliveries.DistinctBy(p => p.LsNr).Count():N0}") (@($"{branchDeliveries.Count():N0}"))</td>
<td class="center">@($"{branchOe:N0}")</td>
<td class="center">@($"{branchKmw:N1}")</td>
<td class="number">@($"{branchDeliveries.Sum(p => p.Weight):N0}")</td>
</tr>
}
}
}
<tr class="sum bold">
@{
var kmw = Elwig.Helpers.Utils.AggregateDeliveryPartsKmw(Model.Deliveries);
var oe = Elwig.Helpers.Utils.KmwToOe(kmw);
}
<td colspan="2">Gesamt:</td>
<td colspan="4">(Teil-)Lieferungen: @($"{Model.Deliveries.DistinctBy(p => p.LsNr).Count():N0}") (@($"{Model.Deliveries.Count():N0}"))</td>
<td class="center">@($"{oe:N0}")</td>
<td class="center">@($"{kmw:N1}")</td>
<td class="number">@($"{Model.Deliveries.Sum(p => p.Weight):N0}")</td>
</tr>
</tbody>
</table>
</main>

View 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;
}

View File

@ -9,7 +9,8 @@ namespace Elwig.Documents {
public string Filter; public string Filter;
public IEnumerable<DeliveryJournalRow> Deliveries; public IEnumerable<DeliveryJournalRow> Deliveries;
public DeliveryJournal(string filter, IEnumerable<DeliveryJournalRow> deliveries) : base($"{Name} {filter}") { public DeliveryJournal(string filter, IEnumerable<DeliveryJournalRow> deliveries) :
base($"{Name} {filter}") {
Filter = filter; Filter = filter;
Deliveries = deliveries; Deliveries = deliveries;
} }

View File

@ -9,10 +9,10 @@
<table class="journal"> <table class="journal">
<colgroup> <colgroup>
<col style="width: 25mm;"/> <col style="width: 25mm;"/>
<col style="width: 5mm;"/> <col style="width: 6mm;"/>
<col style="width: 15mm;"/> <col style="width: 15mm;"/>
<col style="width: 8mm;"/> <col style="width: 8mm;"/>
<col style="width: 12mm;"/> <col style="width: 11mm;"/>
<col style="width: 38mm;"/> <col style="width: 38mm;"/>
<col style="width: 28mm;"/> <col style="width: 28mm;"/>
<col style="width: 10mm;"/> <col style="width: 10mm;"/>
@ -41,7 +41,7 @@
@foreach (var p in Model.Deliveries) { @foreach (var p in Model.Deliveries) {
<tr> <tr>
<td>@p.LsNr</td> <td>@p.LsNr</td>
<td>@p.Pos</td> <td class="center narrow">@p.Pos</td>
<td class="small">@($"{p.Date:dd.MM.yyyy}")</td> <td class="small">@($"{p.Date:dd.MM.yyyy}")</td>
<td class="small">@($"{p.Time:HH:mm}")</td> <td class="small">@($"{p.Time:HH:mm}")</td>
<td class="number">@p.MgNr</td> <td class="number">@p.MgNr</td>
@ -52,6 +52,25 @@
<td class="number">@($"{p.Weight:N0}")</td> <td class="number">@($"{p.Weight:N0}")</td>
</tr> </tr>
} }
@{
var branches = Model.Deliveries.Select(d => d.DeliveryBranch).Distinct().Order().ToArray();
if (branches.Length > 1) {
foreach (var b in branches) {
<tr class="@(branches[0] == b ? "sum" : "") bold">
@{
var branchDeliveries = Model.Deliveries.Where(d => d.DeliveryBranch == b).ToList();
var branchKmw = Elwig.Helpers.Utils.AggregateDeliveryPartsKmw(branchDeliveries);
var branchOe = Elwig.Helpers.Utils.KmwToOe(branchKmw);
}
<td colspan="2">@b:</td>
<td colspan="5">(Teil-)Lieferungen: @($"{branchDeliveries.DistinctBy(p => p.LsNr).Count():N0}") (@($"{branchDeliveries.Count():N0}"))</td>
<td class="center">@($"{branchOe:N0}")</td>
<td class="center">@($"{branchKmw:N1}")</td>
<td class="number">@($"{branchDeliveries.Sum(p => p.Weight):N0}")</td>
</tr>
}
}
}
<tr class="sum bold"> <tr class="sum bold">
@{ @{
var kmw = Elwig.Helpers.Utils.AggregateDeliveryPartsKmw(Model.Deliveries); var kmw = Elwig.Helpers.Utils.AggregateDeliveryPartsKmw(Model.Deliveries);

View File

@ -78,6 +78,8 @@ namespace Elwig.Documents {
name = "CreditNote"; name = "CreditNote";
} else if (this is DeliveryJournal) { } else if (this is DeliveryJournal) {
name = "DeliveryJournal"; name = "DeliveryJournal";
} else if (this is DeliveryDepreciationList) {
name = "DeliveryDepreciationList";
} else if (this is Letterhead) { } else if (this is Letterhead) {
name = "Letterhead"; name = "Letterhead";
} else if (this is DeliveryConfirmation) { } else if (this is DeliveryConfirmation) {

View File

@ -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.13.0</Version> <Version>0.13.4</Version>
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages> <SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ApplicationManifest>app.manifest</ApplicationManifest> <ApplicationManifest>app.manifest</ApplicationManifest>
@ -25,21 +25,21 @@
</Target> </Target>
<ItemGroup> <ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.1" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" />
<PackageReference Include="LinqKit" Version="1.3.0" /> <PackageReference Include="LinqKit" Version="1.3.7" />
<PackageReference Include="MailKit" Version="4.7.1.1" /> <PackageReference Include="MailKit" Version="4.8.0" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.33" /> <PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.36" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.8" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.8" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="9.0.0" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2739.15" /> <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2903.40" />
<PackageReference Include="NJsonSchema" Version="11.0.2" /> <PackageReference Include="NJsonSchema" Version="11.1.0" />
<PackageReference Include="PdfiumViewer" Version="2.13.0" /> <PackageReference Include="PdfiumViewer" Version="2.13.0" />
<PackageReference Include="PdfiumViewer.Native.x86_64.no_v8-no_xfa" Version="2018.4.8.256" /> <PackageReference Include="PdfiumViewer.Native.x86_64.no_v8-no_xfa" Version="2018.4.8.256" />
<PackageReference Include="RazorLight" Version="2.3.1" /> <PackageReference Include="RazorLight" Version="2.3.1" />
<PackageReference Include="ScottPlot.WPF" Version="5.0.39" /> <PackageReference Include="ScottPlot.WPF" Version="5.0.47" />
<PackageReference Include="System.IO.Ports" Version="8.0.0" /> <PackageReference Include="System.IO.Ports" Version="9.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" /> <PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -63,10 +63,12 @@ namespace Elwig.Helpers {
public DbSet<PaymentDeliveryPart> PaymentDeliveryParts { get; private set; } public DbSet<PaymentDeliveryPart> PaymentDeliveryParts { get; private set; }
public DbSet<PaymentCustom> CustomPayments { get; private set; } public DbSet<PaymentCustom> CustomPayments { get; private set; }
public DbSet<Credit> Credits { get; private set; } public DbSet<Credit> Credits { get; private set; }
public DbSet<DeliveryPartBucket> DeliveryPartBuckets { get; private set; }
public DbSet<OverUnderDeliveryRow> OverUnderDeliveryRows { get; private set; } public DbSet<OverUnderDeliveryRow> OverUnderDeliveryRows { get; private set; }
public DbSet<AreaComUnderDeliveryRowSingle> AreaComUnderDeliveryRows { get; private set; } public DbSet<AreaComUnderDeliveryRowSingle> AreaComUnderDeliveryRows { get; private set; }
public DbSet<MemberDeliveryPerVariantRowSingle> MemberDeliveryPerVariantRows { get; private set; } public DbSet<MemberDeliveryPerVariantRowSingle> MemberDeliveryPerVariantRows { get; private set; }
public DbSet<MemberAreaComsRowSingle> MemberAreaComsRows { get; private set; }
public DbSet<CreditNoteDeliveryRowSingle> CreditNoteDeliveryRows { get; private set; } public DbSet<CreditNoteDeliveryRowSingle> CreditNoteDeliveryRows { get; private set; }
public DbSet<CreditNoteRowSingle> CreditNoteRows { get; private set; } public DbSet<CreditNoteRowSingle> CreditNoteRows { get; private set; }
public DbSet<WeightBreakdownRow> WeightBreakDownRows { get; private set; } public DbSet<WeightBreakdownRow> WeightBreakDownRows { get; private set; }

View File

@ -67,6 +67,12 @@ namespace Elwig.Helpers {
public string? TextEmailSubject; public string? TextEmailSubject;
public string? TextEmailBody; public string? TextEmailBody;
public bool MailIncludeNonDeliverers;
public bool MailDoublePaged;
public int MailSendPostal;
public int MailSendEmail;
public int MailOrdering;
public int ExportEbicsVersion; public int ExportEbicsVersion;
public int ExportEbicsAddress; public int ExportEbicsAddress;
@ -117,7 +123,7 @@ namespace Elwig.Helpers {
case "KMW/5": ModeWineQualityStatistics = 3; break; case "KMW/5": ModeWineQualityStatistics = 3; break;
case "KMW/10": ModeWineQualityStatistics = 4; break; case "KMW/10": ModeWineQualityStatistics = 4; break;
} }
switch (parameters.GetValueOrDefault("ORDERING_MEMBERLIST", "")?.ToUpper()) { switch (parameters.GetValueOrDefault("ORDERING_MEMBERLIST", "MGNR")?.ToUpper()) {
case "MGNR": OrderingMemberList = 0; break; case "MGNR": OrderingMemberList = 0; break;
case "NAME": OrderingMemberList = 1; break; case "NAME": OrderingMemberList = 1; break;
case "KG": OrderingMemberList = 2; break; case "KG": OrderingMemberList = 2; break;
@ -135,6 +141,31 @@ namespace Elwig.Helpers {
TextEmailBody = parameters.GetValueOrDefault("TEXT_EMAIL_BODY"); TextEmailBody = parameters.GetValueOrDefault("TEXT_EMAIL_BODY");
if (TextEmailBody == "") TextEmailBody = null; if (TextEmailBody == "") TextEmailBody = null;
MailIncludeNonDeliverers = (parameters.GetValueOrDefault("MAIL_INCLUDE_NON_DELIVERERS")?.ToUpper()) switch {
"1" or "TRUE" or "YES" or "JA" => true,
_ => false,
};
MailDoublePaged = (parameters.GetValueOrDefault("MAIL_DOUBLE_PAGED")?.ToUpper()) switch {
"1" or "TRUE" or "YES" or "JA" => true,
_ => false,
};
switch (parameters.GetValueOrDefault("MAIL_SEND_POSTAL", "WISH")?.ToUpper()) {
case "ALL": MailSendPostal = 3; break;
case "WISH": MailSendPostal = 2; break;
case "NO_EMAIL": MailSendPostal = 1; break;
case "NONE": MailSendPostal = 0; break;
}
switch (parameters.GetValueOrDefault("MAIL_SEND_EMAIL", "WISH")?.ToUpper()) {
case "ALL": MailSendEmail = 2; break;
case "WISH": MailSendEmail = 1; break;
case "NONE": MailSendEmail = 0; break;
}
switch (parameters.GetValueOrDefault("MAIL_ORDERING", "MGNR")?.ToUpper()) {
case "MGNR": MailOrdering = 0; break;
case "NAME": MailOrdering = 1; break;
case "PLZ": MailOrdering = 2; break;
}
ExportEbicsVersion = int.TryParse(parameters.GetValueOrDefault("EXPORT_EBICS_VERSION"), out var v) ? v : 9; ExportEbicsVersion = int.TryParse(parameters.GetValueOrDefault("EXPORT_EBICS_VERSION"), out var v) ? v : 9;
switch (parameters.GetValueOrDefault("EXPORT_EBICS_ADDRESS", "FULL")?.ToUpper()) { switch (parameters.GetValueOrDefault("EXPORT_EBICS_ADDRESS", "FULL")?.ToUpper()) {
case "OMIT": ExportEbicsAddress = 0; break; case "OMIT": ExportEbicsAddress = 0; break;
@ -177,6 +208,25 @@ namespace Elwig.Helpers {
case 1: orderingMemberList = "NAME"; break; case 1: orderingMemberList = "NAME"; break;
case 2: orderingMemberList = "KG"; break; case 2: orderingMemberList = "KG"; break;
} }
string mailSendPostal = "MGNR";
switch (MailOrdering) {
case 0: mailSendPostal = "NONE"; break;
case 1: mailSendPostal = "NO_EMAIL"; break;
case 2: mailSendPostal = "WISH"; break;
case 3: mailSendPostal = "ALL"; break;
}
string mailSendEmail = "MGNR";
switch (MailOrdering) {
case 0: mailSendEmail = "NONE"; break;
case 1: mailSendEmail = "WISH"; break;
case 2: mailSendEmail = "ALL"; break;
}
string mailOrdering = "MGNR";
switch (MailOrdering) {
case 0: mailOrdering = "MGNR"; break;
case 1: mailOrdering = "NAME"; break;
case 2: mailOrdering = "PLZ"; break;
}
string exportEbicsAddress = "FULL"; string exportEbicsAddress = "FULL";
switch (ExportEbicsAddress) { switch (ExportEbicsAddress) {
case 0: exportEbicsAddress = "OMIT"; break; case 0: exportEbicsAddress = "OMIT"; break;
@ -212,6 +262,11 @@ namespace Elwig.Helpers {
("TEXT_CREDITNOTE", TextCreditNote), ("TEXT_CREDITNOTE", TextCreditNote),
("TEXT_EMAIL_SUBJECT", TextEmailSubject), ("TEXT_EMAIL_SUBJECT", TextEmailSubject),
("TEXT_EMAIL_BODY", TextEmailBody), ("TEXT_EMAIL_BODY", TextEmailBody),
("MAIL_INCLUDE_NON_DELIVERERS", MailIncludeNonDeliverers ? "YES" : "NO"),
("MAIL_DOUBLE_PAGED", MailDoublePaged ? "YES" : "NO"),
("MAIL_SEND_POSTAL", mailSendPostal),
("MAIL_SEND_EMAIL", mailSendEmail),
("MAIL_ORDERING", mailOrdering),
("EXPORT_EBICS_VERSION", ExportEbicsVersion.ToString()), ("EXPORT_EBICS_VERSION", ExportEbicsVersion.ToString()),
("EXPORT_EBICS_ADDRESS", exportEbicsAddress), ("EXPORT_EBICS_ADDRESS", exportEbicsAddress),
("AUTOADJUST_BUSINESSSHARES", autoAdjust), ("AUTOADJUST_BUSINESSSHARES", autoAdjust),

View File

@ -5,6 +5,7 @@ using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Controls.Primitives; using System.Windows.Controls.Primitives;
using System.Windows.Threading;
using Brush = System.Windows.Media.Brush; using Brush = System.Windows.Media.Brush;
using Brushes = System.Windows.Media.Brushes; using Brushes = System.Windows.Media.Brushes;
@ -234,5 +235,21 @@ namespace Elwig.Helpers {
return null; return null;
} }
} }
public static void InitializeDelayTimer(TextBox tb, Action<object, TextChangedEventArgs> handler) {
var timer = new DispatcherTimer {
Interval = TimeSpan.FromMilliseconds(250)
};
timer.Tick += (object? sender, EventArgs evt) => {
timer.Stop();
var (oSender, oEvent) = ((object, TextChangedEventArgs))timer.Tag;
handler(oSender, oEvent);
};
tb.TextChanged += (object sender, TextChangedEventArgs evt) => {
timer.Stop();
timer.Tag = (sender, evt);
timer.Start();
};
}
} }
} }

View File

@ -22,7 +22,7 @@ namespace Elwig.Helpers.Export {
"""; """;
var c = App.Client; var c = App.Client;
var (a1, a2) = Utils.SplitAddress(c.Address); var (a1, a2) = Utils.SplitAddress(c.Address);
_clientData = $"{c.LfbisNr};{c.NameFull};;{a1};{a2};{c.Plz};{c.Ort}"; _clientData = $"{c.LfbisNr};{c.NameFull};;{a1};\t{a2};{c.Plz};{c.Ort}";
} }
public async Task ExportAsync(int year) { public async Task ExportAsync(int year) {
@ -35,7 +35,7 @@ namespace Elwig.Helpers.Export {
WHERE year = {year} WHERE year = {year}
"""; """;
var r = await cmd.ExecuteReaderAsync(); var r = await cmd.ExecuteReaderAsync();
List<Row> rows = new(); List<Row> rows = [];
while (await r.ReadAsync()) { while (await r.ReadAsync()) {
rows.Add(new( rows.Add(new(
(r.IsDBNull(0) ? null : r.GetString(0), r.IsDBNull(1) ? null : r.GetString(1), r.IsDBNull(2) ? null : r.GetString(2), r.IsDBNull(3) ? null : r.GetString(3), r.GetString(4), r.GetInt32(5), r.GetString(6), r.GetInt32(7)), (r.IsDBNull(0) ? null : r.GetString(0), r.IsDBNull(1) ? null : r.GetString(1), r.IsDBNull(2) ? null : r.GetString(2), r.IsDBNull(3) ? null : r.GetString(3), r.GetString(4), r.GetInt32(5), r.GetString(6), r.GetInt32(7)),
@ -57,7 +57,7 @@ namespace Elwig.Helpers.Export {
var (n1, n2) = billingName == null ? (familyName, name) : Utils.SplitName(billingName, familyName); var (n1, n2) = billingName == null ? (familyName, name) : Utils.SplitName(billingName, familyName);
var (a1, a2) = Utils.SplitAddress(address); var (a1, a2) = Utils.SplitAddress(address);
var memberData = $"{lfBisNr};{n1};{n2};{a1};{a2};{plz};{ort}"; var memberData = $"{lfBisNr};{n1};{n2};{a1};\t{a2};{plz};{ort}";
var deliveryData = $"{string.Join(".", date.Split("-").Reverse())};{weight};TB;{(type == "W" ? "J" : "")};{(type == "R" ? "J" : "")};{sortid};;;{qualid};{year};{hkid};{kmw:0.0};{oe:0}"; var deliveryData = $"{string.Join(".", date.Split("-").Reverse())};{weight};TB;{(type == "W" ? "J" : "")};{(type == "R" ? "J" : "")};{sortid};;;{qualid};{year};{hkid};{kmw:0.0};{oe:0}";
var vollData = $"N;;;{area / 10_000.0}"; var vollData = $"N;;;{area / 10_000.0}";

View File

@ -23,7 +23,7 @@ namespace Elwig.Helpers {
} }
public static string? ReadUntil(this StreamReader reader, char delimiter) { public static string? ReadUntil(this StreamReader reader, char delimiter) {
return ReadUntil(reader, new char[] { delimiter }); return ReadUntil(reader, [ delimiter ]);
} }
public static string? ReadUntil(this StreamReader reader, char[] delimiter) { public static string? ReadUntil(this StreamReader reader, char[] delimiter) {
@ -45,7 +45,7 @@ namespace Elwig.Helpers {
} }
public static Task<string?> ReadUntilAsync(this StreamReader reader, char delimiter) { public static Task<string?> ReadUntilAsync(this StreamReader reader, char delimiter) {
return ReadUntilAsync(reader, new char[] { delimiter }); return ReadUntilAsync(reader, [ delimiter ]);
} }
public static async Task<string?> ReadUntilAsync(this StreamReader reader, char[] delimiter) { public static async Task<string?> ReadUntilAsync(this StreamReader reader, char[] delimiter) {

View File

@ -28,6 +28,9 @@ using LinqKit;
using System.Linq.Expressions; using System.Linq.Expressions;
using Elwig.Models; using Elwig.Models;
using Microsoft.Win32; using Microsoft.Win32;
using System.Globalization;
using System.Threading;
using System.Windows.Markup;
namespace Elwig.Helpers { namespace Elwig.Helpers {
public static partial class Utils { public static partial class Utils {
@ -118,6 +121,23 @@ namespace Elwig.Helpers {
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040, 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040,
]; ];
public static void OverrideCulture() {
var locale = new CultureInfo("de-AT", false);
locale.NumberFormat.CurrencyGroupSeparator = Utils.GroupSeparator;
locale.NumberFormat.NumberGroupSeparator = Utils.GroupSeparator;
locale.NumberFormat.PercentGroupSeparator = Utils.GroupSeparator;
CultureInfo.CurrentCulture = locale;
CultureInfo.CurrentUICulture = locale;
Thread.CurrentThread.CurrentCulture = locale;
Thread.CurrentThread.CurrentUICulture = locale;
CultureInfo.DefaultThreadCurrentCulture = locale;
CultureInfo.DefaultThreadCurrentUICulture = locale;
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.Name))
);
}
public static SerialPort OpenSerialConnection(string connection) { public static SerialPort OpenSerialConnection(string connection) {
var m = SerialRegex.Match(connection); var m = SerialRegex.Match(connection);
if (!m.Success) if (!m.Success)
@ -267,9 +287,9 @@ namespace Elwig.Helpers {
return d.ShowDialog() == true ? (d.Weight, d.Reason) : null; return d.ShowDialog() == true ? (d.Weight, d.Reason) : null;
} }
public static int? ShowAbwertenDialog(string lsnr, string name, int weight) { public static (string?, int[])? ShowDeliverySplittingDialog(Delivery delivery) {
var d = new AbwertenDialog(lsnr, name, weight); var d = new DeliverySplittingDialog(delivery);
return d.ShowDialog() == true ? d.Weight : null; return d.ShowDialog() == true ? (d.MgNr?.ToString() ?? d.LsNr, d.Weights ?? []) : null;
} }
public static double? ShowLinearPriceIncreaseDialog() { public static double? ShowLinearPriceIncreaseDialog() {
@ -277,11 +297,6 @@ namespace Elwig.Helpers {
return d.ShowDialog() == true ? d.Price : null; return d.ShowDialog() == true ? d.Price : null;
} }
public static string? ShowDeliveryExtractionDialog(string lsnr, string name, bool single, IEnumerable<string> lsnrs) {
var d = new DeliveryExtractionDialog(lsnr, name, single, lsnrs);
return d.ShowDialog() == true ? d.AddTo : null;
}
public static Footer GenerateFooter(string lineBreak, string seperator) { public static Footer GenerateFooter(string lineBreak, string seperator) {
return new Footer(lineBreak, seperator); return new Footer(lineBreak, seperator);
} }
@ -579,5 +594,76 @@ namespace Elwig.Helpers {
public static IEnumerable<AreaCom> ActiveAreaCommitments(IEnumerable<AreaCom> query) => ActiveAreaCommitments(query, CurrentYear); public static IEnumerable<AreaCom> ActiveAreaCommitments(IEnumerable<AreaCom> query) => ActiveAreaCommitments(query, CurrentYear);
public static IEnumerable<AreaCom> ActiveAreaCommitments(IEnumerable<AreaCom> query, int year) => public static IEnumerable<AreaCom> ActiveAreaCommitments(IEnumerable<AreaCom> query, int year) =>
query.Where(c => ActiveAreaCommitments(year).Invoke(c)); query.Where(c => ActiveAreaCommitments(year).Invoke(c));
public static async Task<(DateTime DateTime, string Type, int MgNr, string Name, string[] Addresses, string Subject, string[] Attachments)[]> GetSentMails(DateOnly? fromDate = null, DateOnly? toDate = null) {
var f = $"{fromDate:yyyy-MM-dd}";
var t = $"{toDate:yyyy-MM-dd}";
try {
var rows = new List<(DateTime, string, int, string, string[], string, string[])>();
var filenames = Directory.GetFiles(App.MailsPath, "????.csv")
.Where(n => fromDate == null || Path.GetFileName(n).CompareTo($"{fromDate.Value.Year}.csv") >= 0)
.Where(n => toDate == null || Path.GetFileName(n).CompareTo($"{toDate.Value.Year}.csv") <= 0)
.Order();
foreach (var filename in filenames) {
using var reader = new StreamReader(filename, Utils.UTF8);
string? line;
while ((line = await reader.ReadLineAsync()) != null) {
try {
if (line.Length < 20 || line[10] != ';' || line[19] != ';')
continue;
var date = line[..10];
if (fromDate != null && date.CompareTo(f) < 0) {
continue;
} else if (toDate != null && date.CompareTo(t) > 0) {
break;
}
var p = line.Split(';');
rows.Add((
DateOnly.ParseExact(p[0], "yyyy-MM-dd").ToDateTime(TimeOnly.ParseExact(p[1], "HH:mm:ss")),
p[2],
int.Parse(p[3]),
p[4],
p[5].Split(',').Select(a => a.Replace(" | ", "\n")).ToArray(),
p[6],
p[7].Split(',')
));
} catch {
continue;
}
}
}
return [.. rows];
} catch {
return [];
}
}
public static async Task AddSentMails(IEnumerable<(string Type, int MgNr, string Name, string[] Addresses, string Subject, string[] Attachments)> data) {
var now = DateTime.Now;
var filename = Path.Combine(App.MailsPath, $"{now.Year}.csv");
await File.AppendAllLinesAsync(filename, data.Select(d =>
$"{now:yyyy-MM-dd;HH:mm:ss};{d.Type};" +
$"{d.MgNr};{d.Name.Replace(';', ' ')};" +
$"{string.Join(',', d.Addresses.Select(a => a.Replace(';', ' ').Replace(',', ' ').Replace("\n", " | ")))};" +
$"{d.Subject.Replace(';', ' ')};" +
$"{string.Join(',', d.Attachments.Select(a => a.Replace(';', ' ').Replace(',', ' ')))}"
), Utils.UTF8);
}
public static async Task<string?> FindSentMailBody(DateTime target) {
var dt = $"{target:yyyy-MM-dd_HH-mm-ss}_";
var filename = Directory.GetFiles(App.MailsPath, "????-??-??_??-??-??_*.txt")
.Where(n => Path.GetFileName(n).CompareTo(dt) <= 0)
.Order()
.LastOrDefault();
if (filename == null)
return null;
return await File.ReadAllTextAsync(filename, Utils.UTF8);
}
public static async Task AddSentMailBody(string subject, string body, int recipients) {
var filename = Path.Combine(App.MailsPath, $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{NormalizeFileName(subject)}.txt");
await File.WriteAllTextAsync(filename, $"# {subject}\r\n# Vorgesehene Empfänger: {recipients}\r\n\r\n" + body, Utils.UTF8);
}
} }
} }

View File

@ -58,11 +58,19 @@ namespace Elwig.Helpers.Weighing {
} }
protected async Task<WeighingResult?> Receive() { protected async Task<WeighingResult?> Receive() {
var line = await Reader.ReadUntilAsync("\r\n"); var line = "";
while (line.Length < 33) {
var ch = Reader.Read();
if (ch == -1) {
return null;
} else if (line.Length > 0 || ch == ' ') {
line += char.ToString((char)ch);
}
}
if (LogPath != null) await File.AppendAllTextAsync(LogPath, line); if (LogPath != null) await File.AppendAllTextAsync(LogPath, line);
if (line == null || line == "") { if (line == null || line == "") {
return null; return null;
} else if (line.Length != 35 || line[0] != ' ' || line[9] != ' ' || line[15] != ' ' || line[20] != ' ' || line[32] != ' ') { } else if (line.Length != 33 || line[0] != ' ' || line[9] != ' ' || line[15] != ' ' || line[20] != ' ' || line[32] != ' ') {
throw new IOException($"Invalid event from scale: '{line}'"); throw new IOException($"Invalid event from scale: '{line}'");
} }

View File

@ -34,7 +34,7 @@ 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.FromSql($"""
SELECT m.mgnr, m.name AS name_1, SELECT m.mgnr, m.name AS name_1,
COALESCE(m.prefix || ' ', '') || m.given_name || COALESCE(m.prefix || ' ', '') || m.given_name ||
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2, COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
@ -45,7 +45,7 @@ namespace Elwig.Models.Dtos {
LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
LEFT JOIN AT_ort o ON o.okz = p.okz LEFT JOIN AT_ort o ON o.okz = p.okz
JOIN v_under_delivery u ON (u.mgnr, u.bucket, u.year) = (m.mgnr, c.bucket, c.year) JOIN v_under_delivery u ON (u.mgnr, u.bucket, u.year) = (m.mgnr, c.bucket, c.year)
WHERE c.year = {year} AND m.active = 1 WHERE c.year = {year}
ORDER BY m.mgnr, c.bucket ORDER BY m.mgnr, c.bucket
""").ToListAsync(); """).ToListAsync();
} }

View File

@ -49,7 +49,7 @@ namespace Elwig.Models.Dtos {
} }
private static async Task<IEnumerable<CreditNoteRowSingle>> FromDbSet(DbSet<CreditNoteRowSingle> table, int year, int avnr) { private static async Task<IEnumerable<CreditNoteRowSingle>> FromDbSet(DbSet<CreditNoteRowSingle> table, int year, int avnr) {
return await table.FromSqlRaw($""" return await table.FromSql($"""
SELECT m.mgnr, m.name AS name_1, SELECT m.mgnr, m.name AS name_1,
COALESCE(m.prefix || ' ', '') || m.given_name || COALESCE(m.prefix || ' ', '') || m.given_name ||
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2, COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,

View File

@ -0,0 +1,105 @@
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
namespace Elwig.Models.Dtos {
class MemberAreaComsData : DataTable<MemberAreaComsRow> {
private static readonly (string, string, string?, int)[] FieldNames = [
("MgNr", "MgNr.", null, 12),
("Name1", "Name", null, 40),
("Name2", "Vorname", null, 40),
("Address", "Adresse", null, 60),
("Plz", "PLZ", null, 10),
("Locality", "Ort", null, 60),
("SortIds", "Sorte", null, 12),
("AttrIds", "Attribut", null, 16),
("Areas", "Fläche", "m²", 22),
("DeliveryObligations", "Lieferpflicht", "kg", 22),
("DeliveryRights", "Lieferrecht", "kg", 22),
];
public MemberAreaComsData(IEnumerable<MemberAreaComsRow> rows, int year) :
base($"Flächenbindungen", $"Flächenbindungen pro Mitglied {year}", rows, FieldNames) {
}
public static async Task<MemberAreaComsData> ForSeason(DbSet<MemberAreaComsRowSingle> table, int year) {
return new MemberAreaComsData(
(await FromDbSet(table, year)).GroupBy(
r => r.MgNr,
(k, g) => new MemberAreaComsRow(g)
), year);
}
private static async Task<IEnumerable<MemberAreaComsRowSingle>> FromDbSet(DbSet<MemberAreaComsRowSingle> table, int year) {
return await table.FromSql($"""
SELECT m.mgnr, m.name AS name_1,
COALESCE(m.prefix || ' ', '') || m.given_name ||
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
p.plz, o.name AS ort, m.address,
c.bucket, c.area, c.min_kg, c.max_kg
FROM v_area_commitment_bucket_strict c
LEFT JOIN member m ON m.mgnr = c.mgnr
LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
LEFT JOIN AT_ort o ON o.okz = p.okz
WHERE c.year = {year}
ORDER BY m.mgnr, c.bucket
""").ToListAsync();
}
}
public class MemberAreaComsRow {
public int MgNr;
public string Name1;
public string? Name2;
public string Address;
public int Plz;
public string Locality;
public string[] SortIds;
public string[] AttrIds;
public int[] Areas;
public int[] DeliveryObligations;
public int[] DeliveryRights;
public MemberAreaComsRow(IEnumerable<MemberAreaComsRowSingle> rows) {
var f = rows.First();
MgNr = f.MgNr;
Name1 = f.Name1;
Name2 = f.Name2;
Address = f.Address;
Plz = f.Plz;
Locality = f.Locality.Split(",")[0];
SortIds = rows.Select(r => r.VtrgId[..2]).ToArray();
AttrIds = rows.Select(r => r.VtrgId[2..]).ToArray();
Areas = rows.Select(r => r.Area).ToArray();
DeliveryObligations = rows.Select(r => r.MinKg).ToArray();
DeliveryRights = rows.Select(r => r.MaxKg).ToArray();
}
}
[Keyless]
public class MemberAreaComsRowSingle {
[Column("mgnr")]
public int MgNr { get; set; }
[Column("name_1")]
public required string Name1 { get; set; }
[Column("name_2")]
public string? Name2 { get; set; }
[Column("address")]
public required string Address { get; set; }
[Column("plz")]
public int Plz { get; set; }
[Column("ort")]
public required string Locality { get; set; }
[Column("bucket")]
public required string VtrgId { get; set; }
[Column("area")]
public int Area { get; set; }
[Column("min_kg")]
public int MinKg { get; set; }
[Column("max_kg")]
public int MaxKg { get; set; }
}
}

View File

@ -6,7 +6,7 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Elwig.Models.Dtos { namespace Elwig.Models.Dtos {
public class MemberDeliveryPerVariantData : DataTable<MemberDeliveryPerVariantRow> { public class MemberDeliveryPerVarietyData : DataTable<MemberDeliveryPerVariantRow> {
private static readonly (string, string, string?, int)[] FieldNames = [ private static readonly (string, string, string?, int)[] FieldNames = [
("MgNr", "MgNr.", null, 12), ("MgNr", "MgNr.", null, 12),
@ -22,13 +22,12 @@ namespace Elwig.Models.Dtos {
("Yields", "Ertrag", "kg/ha", 22), ("Yields", "Ertrag", "kg/ha", 22),
]; ];
public MemberDeliveryPerVarietyData(IEnumerable<MemberDeliveryPerVariantRow> rows, int year) :
public MemberDeliveryPerVariantData(IEnumerable<MemberDeliveryPerVariantRow> rows, int year) :
base($"Liefermengen", $"Liefermengen pro Mitglied, Sorte und Attribut {year}", rows, FieldNames) { base($"Liefermengen", $"Liefermengen pro Mitglied, Sorte und Attribut {year}", rows, FieldNames) {
} }
public static async Task<MemberDeliveryPerVariantData> ForSeason(DbSet<MemberDeliveryPerVariantRowSingle> table, int year) { public static async Task<MemberDeliveryPerVarietyData> ForSeason(DbSet<MemberDeliveryPerVariantRowSingle> table, int year) {
return new MemberDeliveryPerVariantData( return new MemberDeliveryPerVarietyData(
(await FromDbSet(table, year)).GroupBy( (await FromDbSet(table, year)).GroupBy(
r => r.MgNr, r => r.MgNr,
(k, g) => new MemberDeliveryPerVariantRow(g) (k, g) => new MemberDeliveryPerVariantRow(g)
@ -36,7 +35,7 @@ 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.FromSql($"""
SELECT m.mgnr, m.name AS name_1, SELECT m.mgnr, m.name AS name_1,
COALESCE(m.prefix || ' ', '') || m.given_name || COALESCE(m.prefix || ' ', '') || m.given_name ||
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2, COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,

View File

@ -25,7 +25,7 @@ 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.FromSql($"""
SELECT m.mgnr, m.name AS name_1, SELECT m.mgnr, m.name AS name_1,
COALESCE(m.prefix || ' ', '') || m.given_name || COALESCE(m.prefix || ' ', '') || m.given_name ||
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2, COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,
@ -33,14 +33,13 @@ namespace Elwig.Models.Dtos {
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
FROM v_delivery d FROM season s, member m
LEFT JOIN member m ON m.mgnr = d.mgnr
LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
LEFT JOIN AT_ort o ON o.okz = p.okz LEFT JOIN AT_ort o ON o.okz = p.okz
LEFT JOIN season s ON s.year = d.year LEFT JOIN v_delivery d ON (d.year, d.mgnr) = (s.year, m.mgnr)
WHERE s.year = {year} AND (m.active = TRUE OR d.weight > 0) WHERE s.year = {year} AND (m.active = TRUE OR d.weight > 0)
GROUP BY d.year, m.mgnr GROUP BY s.year, m.mgnr
ORDER BY 100.0 * sum / max_kg, m.mgnr; ORDER BY 100.0 * sum / max_kg, m.mgnr
""").ToListAsync(); """).ToListAsync();
return new OverUnderDeliveryData(rows, year); return new OverUnderDeliveryData(rows, year);
} }

View File

@ -49,7 +49,7 @@ namespace Elwig.Models.Dtos {
} }
private static async Task<IEnumerable<PaymentVariantSummaryRow>> FromDbSet(DbSet<PaymentVariantSummaryRow> table, int year, int avnr) { private static async Task<IEnumerable<PaymentVariantSummaryRow>> FromDbSet(DbSet<PaymentVariantSummaryRow> table, int year, int avnr) {
return await table.FromSqlRaw($""" return await table.FromSql($"""
SELECT v.type AS type, SELECT v.type AS type,
v.name AS variety, v.name AS variety,
a.name AS attribute, a.name AS attribute,

View File

@ -48,6 +48,7 @@ namespace Elwig.Services {
var filterNotVar = new List<string>(); var filterNotVar = new List<string>();
var filterAttr = new List<string>(); var filterAttr = new List<string>();
var filterNotAttr = new List<string>(); var filterNotAttr = new List<string>();
var filterSeasons = new List<int>();
var filter = vm.TextFilter; var filter = vm.TextFilter;
if (filter.Count > 0) { if (filter.Count > 0) {
@ -87,6 +88,10 @@ namespace Elwig.Services {
filter.RemoveAt(i--); filter.RemoveAt(i--);
filterNames.Add($"ohne {var[e[1..3].ToUpper()].Name}"); filterNames.Add($"ohne {var[e[1..3].ToUpper()].Name}");
filterNames.Add($"ohne Attribut {attrId[e[3..].ToUpper()].Name}"); filterNames.Add($"ohne Attribut {attrId[e[3..].ToUpper()].Name}");
} else if (e.Length == 4 && int.TryParse(e, out var year)) {
filterSeasons.Add(year);
filter.RemoveAt(i--);
filterNames.Add($"laufend {e}");
} }
} }
@ -94,6 +99,7 @@ namespace Elwig.Services {
if (filterNotVar.Count > 0) areaComQuery = areaComQuery.Where(a => !filterNotVar.Contains(a.AreaComType.WineVar.SortId)); if (filterNotVar.Count > 0) areaComQuery = areaComQuery.Where(a => !filterNotVar.Contains(a.AreaComType.WineVar.SortId));
if (filterAttr.Count > 0) areaComQuery = areaComQuery.Where(a => a.AreaComType.WineAttr!.AttrId != null && filterAttr.Contains(a.AreaComType.WineAttr.AttrId)); if (filterAttr.Count > 0) areaComQuery = areaComQuery.Where(a => a.AreaComType.WineAttr!.AttrId != null && filterAttr.Contains(a.AreaComType.WineAttr.AttrId));
if (filterNotAttr.Count > 0) areaComQuery = areaComQuery.Where(a => a.AreaComType.WineAttr!.AttrId == null || !filterNotAttr.Contains(a.AreaComType.WineAttr.AttrId)); if (filterNotAttr.Count > 0) areaComQuery = areaComQuery.Where(a => a.AreaComType.WineAttr!.AttrId == null || !filterNotAttr.Contains(a.AreaComType.WineAttr.AttrId));
foreach (var year in filterSeasons) areaComQuery = Utils.ActiveAreaCommitments(areaComQuery, year);
} }
return (filterNames, areaComQuery, filter); return (filterNames, areaComQuery, filter);

View File

@ -230,7 +230,7 @@ namespace Elwig.Services {
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
if (oldDsNr != null && (oldYear != year || oldDsNr != dsnr || oldMgNr != newMgNr || oldSortId != newSortId)) { if (oldDsNr != null && (oldYear != year || oldDsNr != dsnr || oldMgNr != newMgNr || oldSortId != newSortId)) {
await ctx.Database.ExecuteSqlRawAsync($"UPDATE delivery_announcement SET year = {year}, dsnr = {dsnr}, mgnr = {newMgNr}, sortid = '{newSortId}' WHERE (year, dsnr, mgnr, sortid) = ({a.Year}, {a.DsNr}, {a.MgNr}, '{a.SortId}')"); await ctx.Database.ExecuteSqlAsync($"UPDATE delivery_announcement SET year = {year}, dsnr = {dsnr}, mgnr = {newMgNr}, sortid = {newSortId} WHERE (year, dsnr, mgnr, sortid) = ({a.Year}, {a.DsNr}, {a.MgNr}, {a.SortId})");
} }
} }

View File

@ -24,7 +24,7 @@ namespace Elwig.Services {
public static class DeliveryService { public static class DeliveryService {
public enum ExportSubject { public enum ExportSubject {
FromFilters, FromToday, FromSeasonAndBranch, Selected, FromFilters, FromToday, FromSeason, FromSeasonAndBranch, Selected,
}; };
public static async Task<Member?> GetMemberAsync(int mgnr) { public static async Task<Member?> GetMemberAsync(int mgnr) {
@ -430,103 +430,229 @@ namespace Elwig.Services {
} }
public static async Task<DeliveryPart> UpdateDeliveryPart(this DeliveryAdminViewModel vm, int? oldYear, int? oldDid, int? oldDpnr, bool dateHasChanged, bool timeHasChanged, bool timeIsDefault) { public static async Task<DeliveryPart> UpdateDeliveryPart(this DeliveryAdminViewModel vm, int? oldYear, int? oldDid, int? oldDpnr, bool dateHasChanged, bool timeHasChanged, bool timeIsDefault) {
using var ctx = new AppDbContext(); DeliveryPart p;
int year = oldYear ?? Utils.CurrentYear; using (var ctx = new AppDbContext()) {
int did = oldDid ?? await ctx.NextDId(year); int year = oldYear ?? Utils.CurrentYear;
int dpnr = oldDpnr ?? await ctx.NextDPNr(year, did); int did = oldDid ?? await ctx.NextDId(year);
int dpnr = oldDpnr ?? await ctx.NextDPNr(year, did);
var oldDelivery = await ctx.Deliveries.FindAsync(year, did); var oldDelivery = await ctx.Deliveries.FindAsync(year, did);
bool deliveryNew = (oldDid == null); bool deliveryNew = (oldDid == null);
bool partNew = (oldDpnr == null); bool partNew = (oldDpnr == null);
var originalMgNr = oldDelivery?.MgNr; var originalMgNr = oldDelivery?.MgNr;
var originalMemberKgNr = oldDelivery?.Member.DefaultKgNr; var originalMemberKgNr = oldDelivery?.Member.DefaultKgNr;
var date = DateOnly.ParseExact(vm.Date!, "dd.MM.yyyy"); var date = DateOnly.ParseExact(vm.Date!, "dd.MM.yyyy");
int? newLnr = (deliveryNew || dateHasChanged) ? await ctx.NextLNr(date, vm.Branch!.ZwstId) : null; int? newLnr = (deliveryNew || dateHasChanged) ? await ctx.NextLNr(date, vm.Branch!.ZwstId) : null;
string? newLsNr = (newLnr != null) ? Utils.GenerateLsNr(date, vm.Branch!.ZwstId, newLnr.Value) : null; string? newLsNr = (newLnr != null) ? Utils.GenerateLsNr(date, vm.Branch!.ZwstId, newLnr.Value) : null;
string? newTimeString = null; string? newTimeString = null;
if (partNew && timeIsDefault) { if (partNew && timeIsDefault) {
newTimeString = DateTime.Now.ToString("HH:mm:ss"); newTimeString = DateTime.Now.ToString("HH:mm:ss");
} else if (partNew || timeHasChanged) { } else if (partNew || timeHasChanged) {
newTimeString = string.IsNullOrEmpty(vm.Time) ? null : vm.Time + ":00"; newTimeString = string.IsNullOrEmpty(vm.Time) ? null : vm.Time + ":00";
}
var d = new Delivery {
Year = year,
DId = did,
DateString = $"{date:yyyy-MM-dd}",
TimeString = newTimeString ?? oldDelivery?.TimeString,
LNr = newLnr ?? oldDelivery!.LNr,
ZwstId = vm.Branch!.ZwstId,
LsNr = newLsNr ?? vm.LsNr!,
MgNr = (int)vm.MgNr!,
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
};
var p = new DeliveryPart {
Year = year,
DId = did,
DPNr = dpnr,
SortId = vm.WineVar!.SortId,
AttrId = vm.WineAttr?.AttrId,
CultId = vm.WineCult?.CultId,
Kmw = (double)vm.GradationKmw!,
QualId = vm.WineQualityLevel!.QualId,
HkId = vm.WineOrigin!.HkId,
KgNr = vm.WineKg?.KgNr,
RdNr = vm.WineRd?.RdNr,
IsNetWeight = vm.IsNetWeight,
IsHandPicked = vm.IsHandPicked,
IsLesewagen = vm.IsLesewagen,
IsGebunden = vm.IsGebunden,
Temperature = vm.Temperature,
Acid = vm.Acid,
Comment = string.IsNullOrEmpty(vm.PartComment) ? null : vm.PartComment,
Weight = (int)vm.Weight!,
IsManualWeighing = vm.IsManualWeighing,
ScaleId = vm.ScaleId,
WeighingData = vm.WeighingData,
WeighingReason = vm.ManualWeighingReason,
};
if (oldDelivery != null && ctx.Entry(oldDelivery) is EntityEntry<Delivery> entry) {
entry.State = EntityState.Detached;
}
if (!deliveryNew) {
ctx.Update(d);
} else {
ctx.Add(d);
}
if (!partNew) {
ctx.Update(p);
} else {
ctx.Add(p);
}
ctx.UpdateDeliveryPartModifiers(p, await ctx.DeliveryPartModifiers
.Where(m => m.Year == p.Year && m.DId == p.DId && m.DPNr == p.DPNr)
.Select(m => m.Modifier)
.ToListAsync(), vm.Modifiers);
if (originalMgNr != null && originalMgNr.Value != d.MgNr) {
// update origin (KgNr), if default is selected
var newKgNr = (await ctx.Members.FindAsync(d.MgNr))?.DefaultKgNr;
foreach (var part in d.Parts.Where(part => part.DPNr != dpnr && part.KgNr == originalMemberKgNr)) {
part.KgNr = newKgNr;
ctx.Update(part);
} }
var d = new Delivery {
Year = year,
DId = did,
DateString = $"{date:yyyy-MM-dd}",
TimeString = newTimeString ?? oldDelivery?.TimeString,
LNr = newLnr ?? oldDelivery!.LNr,
ZwstId = vm.Branch!.ZwstId,
LsNr = newLsNr ?? vm.LsNr!,
MgNr = vm.MgNr!.Value,
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
};
p = new DeliveryPart {
Year = year,
DId = did,
DPNr = dpnr,
SortId = vm.WineVar!.SortId,
AttrId = vm.WineAttr?.AttrId,
CultId = vm.WineCult?.CultId,
Kmw = vm.GradationKmw!.Value,
QualId = vm.WineQualityLevel!.QualId,
HkId = vm.WineOrigin!.HkId,
KgNr = vm.WineKg?.KgNr,
RdNr = vm.WineRd?.RdNr,
IsNetWeight = vm.IsNetWeight,
IsHandPicked = vm.IsHandPicked,
IsLesewagen = vm.IsLesewagen,
IsGebunden = vm.IsGebunden,
Temperature = vm.Temperature,
Acid = vm.Acid,
Comment = string.IsNullOrEmpty(vm.PartComment) ? null : vm.PartComment,
Weight = vm.Weight!.Value,
IsManualWeighing = vm.IsManualWeighing,
ScaleId = vm.ScaleId,
WeighingData = vm.WeighingData,
WeighingReason = vm.ManualWeighingReason,
};
if (oldDelivery != null && ctx.Entry(oldDelivery) is EntityEntry<Delivery> entry) {
entry.State = EntityState.Detached;
}
if (!deliveryNew) {
ctx.Update(d);
} else {
ctx.Add(d);
}
if (!partNew) {
ctx.Update(p);
} else {
ctx.Add(p);
}
ctx.UpdateDeliveryPartModifiers(p, await ctx.DeliveryPartModifiers
.Where(m => m.Year == p.Year && m.DId == p.DId && m.DPNr == p.DPNr)
.Select(m => m.Modifier)
.ToListAsync(), vm.Modifiers);
if (originalMgNr != null && originalMgNr.Value != d.MgNr) {
// update origin (KgNr), if default is selected
var newKgNr = (await ctx.Members.FindAsync(d.MgNr))?.DefaultKgNr;
foreach (var part in d.Parts.Where(part => part.DPNr != dpnr && part.KgNr == originalMemberKgNr)) {
part.KgNr = newKgNr;
ctx.Update(part);
}
}
await ctx.SaveChangesAsync();
} }
await ctx.SaveChangesAsync(); App.HintContextChange();
return p; return p;
} }
public static async Task<Delivery> SplitDeliveryToMember(int year, int did, int[] weights, int mgnr) {
Delivery n;
using (var ctx = new AppDbContext()) {
bool anyLeft = false;
var d = (await ctx.Deliveries.FindAsync(year, did))!;
var lnr = await ctx.NextLNr(d.Date, d.ZwstId);
n = new Delivery {
Year = year,
DId = await ctx.NextDId(d.Year),
DateString = d.DateString,
TimeString = d.TimeString,
ZwstId = d.ZwstId,
LNr = lnr,
LsNr = Utils.GenerateLsNr(d.Date, d.ZwstId, lnr),
MgNr = mgnr,
Comment = d.Comment,
};
ctx.Add(n);
await ctx.SaveChangesAsync();
var dpnr = 1;
foreach (var (p, w) in d.Parts.ToList().Zip(weights)) {
if (w <= 0) {
anyLeft = true;
continue;
} else if (w >= p.Weight) {
await ctx.Database.ExecuteSqlAsync($"UPDATE delivery_part SET year = {n.Year}, did = {n.DId}, dpnr = {dpnr++} WHERE (year, did, dpnr) = ({p.Year}, {p.DId}, {p.DPNr})");
} else {
anyLeft = true;
p.Weight -= w;
ctx.Update(p);
var s = ctx.CreateProxy<DeliveryPart>();
var values = ctx.Entry(p).CurrentValues;
ctx.Entry(s).CurrentValues.SetValues(values);
s.Year = n.Year;
s.DId = n.DId;
s.DPNr = dpnr++;
s.Weight = w;
ctx.Add(s);
}
}
await ctx.SaveChangesAsync();
if (!anyLeft)
await ctx.Database.ExecuteSqlAsync($"DELETE FROM delivery WHERE (year, did) = ({d.Year}, {d.DId})");
}
App.HintContextChange();
return n;
}
public static async Task<Delivery> SplitDeliveryToLsNr(int year, int did, int[] weights, string lsnr) {
Delivery n;
using (var ctx = new AppDbContext()) {
var anyLeft = false;
n = (await ctx.Deliveries.FirstAsync(d => d.LsNr == lsnr))!;
var d = (await ctx.Deliveries.FindAsync(year, did))!;
var dpnr = await ctx.NextDPNr(n.Year, n.DId);
foreach (var (p, w) in d.Parts.ToList().Zip(weights)) {
if (w <= 0) {
anyLeft = true;
continue;
} else if (w >= p.Weight) {
await ctx.Database.ExecuteSqlAsync($"UPDATE delivery_part SET year = {n.Year}, did = {n.DId}, dpnr = {dpnr++} WHERE (year, did, dpnr) = ({p.Year}, {p.DId}, {p.DPNr})");
} else {
anyLeft = true;
p.Weight -= w;
ctx.Update(p);
var s = ctx.CreateProxy<DeliveryPart>();
var values = ctx.Entry(p).CurrentValues;
ctx.Entry(s).CurrentValues.SetValues(values);
s.Year = n.Year;
s.DId = n.DId;
s.DPNr = dpnr++;
s.Weight = w;
ctx.Add(s);
}
}
await ctx.SaveChangesAsync();
if (!anyLeft && n.LsNr != d.LsNr)
await ctx.Database.ExecuteSqlAsync($"DELETE FROM delivery WHERE (year, did) = ({d.Year}, {d.DId})");
}
App.HintContextChange();
return n;
}
public static async Task DepreciateDelivery(int year, int did, int[] weights) {
using (var ctx = new AppDbContext()) {
var d = (await ctx.Deliveries.FindAsync(year, did))!;
var dpnr = await ctx.NextDPNr(year, did);
foreach (var (p, w) in d.Parts.ToList().Zip(weights)) {
if (w <= 0) {
continue;
} else if (w >= p.Weight) {
p.QualId = "WEI";
p.HkId = "OEST";
ctx.Update(p);
} else {
p.Weight -= w;
ctx.Update(p);
var n = ctx.CreateProxy<DeliveryPart>();
var values = ctx.Entry(p).CurrentValues;
ctx.Entry(n).CurrentValues.SetValues(values);
n.DPNr = dpnr++;
n.Weight = w;
n.QualId = "WEI";
n.HkId = "OEST";
ctx.Add(n);
}
}
await ctx.SaveChangesAsync();
}
App.HintContextChange();
}
public static async Task GenerateDeliveryNote(int year, int did, ExportMode mode) { public static async Task GenerateDeliveryNote(int year, int did, ExportMode mode) {
Mouse.OverrideCursor = Cursors.AppStarting; Mouse.OverrideCursor = Cursors.AppStarting;
try { try {
@ -683,6 +809,71 @@ namespace Elwig.Services {
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
} }
public static async Task GenerateDeliveryDepreciationList(this DeliveryAdminViewModel vm, ExportSubject subject, ExportMode mode) {
using var ctx = new AppDbContext();
IQueryable<DeliveryPart> query;
List<string> filterNames = [];
if (subject == ExportSubject.FromFilters) {
var (f, _, q, _, _) = await vm.GetFilters(ctx);
query = q;
filterNames.AddRange(f);
} else if (subject == ExportSubject.FromSeason) {
var year = vm.FilterSeason ?? Utils.CurrentLastSeason;
query = ctx.DeliveryParts
.Where(p => p.Year == year);
filterNames.Add($"{year}");
} else {
throw new ArgumentException("Invalid value for ExportSubject");
}
query = query
.Where(p => p.QualId == "WEI")
.OrderBy(p => p.Delivery.MgNr)
.ThenBy(p => p.Delivery.DateString)
.ThenBy(p => p.Delivery.TimeString)
.ThenBy(p => p.Delivery.LsNr)
.ThenBy(p => p.DPNr);
filterNames.Remove("abgewertet");
if (mode == ExportMode.SaveList) {
var d = new SaveFileDialog() {
FileName = $"{DeliveryDepreciationList.Name}-{vm.FilterSeason ?? Utils.CurrentLastSeason}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"{DeliveryDepreciationList.Name} speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.AppStarting;
try {
using var ods = new OdsFile(d.FileName);
var tblTotal = await DeliveryJournalData.FromQuery(query, filterNames);
tblTotal.FullName = DeliveryDepreciationList.Name;
tblTotal.Name = "Gesamt";
await ods.AddTable(tblTotal);
foreach (var branch in await ctx.Branches.OrderBy(b => b.Name).ToListAsync()) {
var tbl = await DeliveryJournalData.FromQuery(query.Where(p => p.Delivery.ZwstId == branch.ZwstId), filterNames);
tbl.FullName = DeliveryDepreciationList.Name;
tbl.Name = branch.Name;
await ods.AddTable(tbl);
}
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
} else {
Mouse.OverrideCursor = Cursors.AppStarting;
try {
var data = await DeliveryJournalData.FromQuery(query, filterNames);
using var doc = new DeliveryDepreciationList(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
}
private static void AddToolTipCell(Grid grid, string text, int row, int col, int colSpan = 1, bool bold = false, bool alignRight = false, bool alignCenter = false) { private static void AddToolTipCell(Grid grid, string text, int row, int col, int colSpan = 1, bool bold = false, bool alignRight = false, bool alignCenter = false) {
var tb = new TextBlock() { var tb = new TextBlock() {
Text = text, Text = text,
@ -845,5 +1036,13 @@ namespace Elwig.Services {
return (wGrid, gGrid); return (wGrid, gGrid);
} }
public static async Task DeleteDelivery(string lsnr) {
using (var ctx = new AppDbContext()) {
await ctx.Deliveries.Where(d => d.LsNr == lsnr).ExecuteDeleteAsync();
await ctx.SaveChangesAsync();
}
App.HintContextChange();
}
} }
} }

View File

@ -25,7 +25,7 @@ namespace Elwig.Services {
public static async Task InitInputs(this MemberAdminViewModel vm) { public static async Task InitInputs(this MemberAdminViewModel vm) {
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
vm.MgNrString = $"{await ctx.NextMgNr()}"; vm.MgNr = await ctx.NextMgNr();
vm.EntryDate = DateTime.Now.ToString("dd.MM.yyyy"); vm.EntryDate = DateTime.Now.ToString("dd.MM.yyyy");
if (vm.BranchSource.Count() == 1) if (vm.BranchSource.Count() == 1)
vm.Branch = vm.BranchSource.First(); vm.Branch = vm.BranchSource.First();
@ -53,8 +53,8 @@ namespace Elwig.Services {
public static void FillInputs(this MemberAdminViewModel vm, Member m) { public static void FillInputs(this MemberAdminViewModel vm, Member m) {
vm.IsMemberSelected = true; vm.IsMemberSelected = true;
vm.MgNrString = $"{m.MgNr}"; vm.MgNr = m.MgNr;
vm.PredecessorMgNrString = $"{m.PredecessorMgNr}"; vm.PredecessorMgNr = m.PredecessorMgNr;
vm.IsJuridicalPerson = m.IsJuridicalPerson; vm.IsJuridicalPerson = m.IsJuridicalPerson;
vm.EnableMemberReferenceButton = m.PredecessorMgNr != null; vm.EnableMemberReferenceButton = m.PredecessorMgNr != null;
vm.Prefix = m.Prefix; vm.Prefix = m.Prefix;
@ -73,10 +73,10 @@ namespace Elwig.Services {
vm.IsDeceased = m.IsDeceased; vm.IsDeceased = m.IsDeceased;
vm.Address = m.Address; vm.Address = m.Address;
if (m.PostalDest.AtPlz is AT_PlzDest p) { if (m.PostalDest.AtPlz is AT_PlzDest p) {
vm.PlzString = $"{p.Plz}"; vm.Plz = p.Plz;
vm.Ort = ControlUtils.GetItemFromSource(vm.OrtSource, p); vm.Ort = ControlUtils.GetItemFromSource(vm.OrtSource, p);
} else { } else {
vm.PlzString = null; vm.Plz = null;
vm.Ort = null; vm.Ort = null;
} }
@ -114,19 +114,19 @@ namespace Elwig.Services {
vm.BillingName = billingAddr.FullName; vm.BillingName = billingAddr.FullName;
vm.BillingAddress = billingAddr.Address; vm.BillingAddress = billingAddr.Address;
if (billingAddr.PostalDest.AtPlz is AT_PlzDest b) { if (billingAddr.PostalDest.AtPlz is AT_PlzDest b) {
vm.BillingPlzString = $"{b.Plz}"; vm.BillingPlz = b.Plz;
vm.BillingOrt = ControlUtils.GetItemFromSource(vm.BillingOrtSource, b); vm.BillingOrt = ControlUtils.GetItemFromSource(vm.BillingOrtSource, b);
} }
} else { } else {
vm.BillingName = null; vm.BillingName = null;
vm.BillingAddress = null; vm.BillingAddress = null;
vm.BillingPlzString = null; vm.BillingPlz = null;
vm.BillingOrt = null; vm.BillingOrt = null;
} }
vm.EntryDate = (m.EntryDateString != null) ? string.Join(".", m.EntryDateString.Split("-").Reverse()) : null; vm.EntryDate = (m.EntryDateString != null) ? string.Join(".", m.EntryDateString.Split("-").Reverse()) : null;
vm.ExitDate = (m.ExitDateString != null) ? string.Join(".", m.ExitDateString.Split("-").Reverse()) : null; vm.ExitDate = (m.ExitDateString != null) ? string.Join(".", m.ExitDateString.Split("-").Reverse()) : null;
vm.BusinessSharesString = $"{m.BusinessShares}"; vm.BusinessShares = m.BusinessShares;
vm.AccountingNr = m.AccountingNr; vm.AccountingNr = m.AccountingNr;
vm.Branch = (Branch?)ControlUtils.GetItemFromSourceWithPk(vm.BranchSource, m.ZwstId); vm.Branch = (Branch?)ControlUtils.GetItemFromSourceWithPk(vm.BranchSource, m.ZwstId);
vm.DefaultKg = (AT_Kg?)ControlUtils.GetItemFromSourceWithPk(vm.DefaultKgSource, m.DefaultKgNr); vm.DefaultKg = (AT_Kg?)ControlUtils.GetItemFromSourceWithPk(vm.DefaultKgSource, m.DefaultKgNr);
@ -148,6 +148,8 @@ namespace Elwig.Services {
vm.StatusAreaCommitmentToolTip = null; vm.StatusAreaCommitmentToolTip = null;
Utils.RunBackground("Mitgliederdaten laden", async () => { Utils.RunBackground("Mitgliederdaten laden", async () => {
if (App.MainDispatcher == null)
return;
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
var d1 = ctx.Deliveries.Where(d => d.Year == Utils.CurrentLastSeason - 1 && d.MgNr == m.MgNr); var d1 = ctx.Deliveries.Where(d => d.Year == Utils.CurrentLastSeason - 1 && d.MgNr == m.MgNr);
@ -210,6 +212,7 @@ namespace Elwig.Services {
var filterLfbisNr = new List<string>(); var filterLfbisNr = new List<string>();
var filterUstIdNr = new List<string>(); var filterUstIdNr = new List<string>();
var filterAreaCom = new List<string>(); var filterAreaCom = new List<string>();
var filterNotAreaCom = new List<string>();
var filter = vm.TextFilter; var filter = vm.TextFilter;
if (filter.Count > 0) { if (filter.Count > 0) {
@ -312,10 +315,22 @@ namespace Elwig.Services {
memberQuery = memberQuery.Where(m => m.TelephoneNumbers.Any(t => t.Number.Replace(" ", "").StartsWith(e))); memberQuery = memberQuery.Where(m => m.TelephoneNumbers.Any(t => t.Number.Replace(" ", "").StartsWith(e)));
filter.RemoveAt(i--); filter.RemoveAt(i--);
filterNames.Add($"Tel.-Nr. {e}"); filterNames.Add($"Tel.-Nr. {e}");
} else if (e.Length >= 5 && e.Length <= 14 && "flächenbindung".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
filterAreaCom.AddRange(areaComs.Keys);
filter.RemoveAt(i--);
filterNames.Add($"Flächenbindung");
} else if (e.Length >= 6 && e.Length <= 15 && "!flächenbindung".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
filterNotAreaCom.AddRange(areaComs.Keys);
filter.RemoveAt(i--);
filterNames.Add($"keine Flächenbindung");
} else if (areaComs.ContainsKey(e.ToUpper())) { } else if (areaComs.ContainsKey(e.ToUpper())) {
filterAreaCom.Add(e.ToUpper()); filterAreaCom.Add(e.ToUpper());
filter.RemoveAt(i--); filter.RemoveAt(i--);
filterNames.Add($"Flächenbindung {e.ToUpper()}"); filterNames.Add($"Flächenbindung {e.ToUpper()}");
} else if (e.Length >= 3 && e[0] == '!' && areaComs.ContainsKey(e[1..].ToUpper())) {
filterNotAreaCom.Add(e[1..].ToUpper());
filter.RemoveAt(i--);
filterNames.Add($"ohne Flächenbindung {e[1..].ToUpper()}");
} else if (Validator.CheckLfbisNr(e)) { } else if (Validator.CheckLfbisNr(e)) {
filterLfbisNr.Add(e); filterLfbisNr.Add(e);
filter.RemoveAt(i--); filter.RemoveAt(i--);
@ -335,6 +350,7 @@ namespace Elwig.Services {
if (filterKgNr.Count > 0) memberQuery = memberQuery.Where(m => m.DefaultKgNr != null && filterKgNr.Contains((int)m.DefaultKgNr)); if (filterKgNr.Count > 0) memberQuery = memberQuery.Where(m => m.DefaultKgNr != null && filterKgNr.Contains((int)m.DefaultKgNr));
if (filterZwst.Count > 0) memberQuery = memberQuery.Where(m => m.ZwstId != null && filterZwst.Contains(m.ZwstId)); if (filterZwst.Count > 0) memberQuery = memberQuery.Where(m => m.ZwstId != null && filterZwst.Contains(m.ZwstId));
if (filterAreaCom.Count > 0) memberQuery = memberQuery.Where(m => m.AreaCommitments.AsQueryable().Where(Utils.ActiveAreaCommitments()).Any(c => filterAreaCom.Contains(c.VtrgId))); if (filterAreaCom.Count > 0) memberQuery = memberQuery.Where(m => m.AreaCommitments.AsQueryable().Where(Utils.ActiveAreaCommitments()).Any(c => filterAreaCom.Contains(c.VtrgId)));
if (filterNotAreaCom.Count > 0) memberQuery = memberQuery.Where(m => !m.AreaCommitments.AsQueryable().Where(Utils.ActiveAreaCommitments()).All(c => filterNotAreaCom.Contains(c.VtrgId)));
if (filterLfbisNr.Count > 0) memberQuery = memberQuery.Where(m => m.LfbisNr != null && filterLfbisNr.Contains(m.LfbisNr)); if (filterLfbisNr.Count > 0) memberQuery = memberQuery.Where(m => m.LfbisNr != null && filterLfbisNr.Contains(m.LfbisNr));
if (filterUstIdNr.Count > 0) memberQuery = memberQuery.Where(m => m.UstIdNr != null && filterUstIdNr.Contains(m.UstIdNr)); if (filterUstIdNr.Count > 0) memberQuery = memberQuery.Where(m => m.UstIdNr != null && filterUstIdNr.Contains(m.UstIdNr));
} }
@ -652,5 +668,23 @@ namespace Elwig.Services {
return newMgNr; return newMgNr;
} }
public static async Task DeleteMember(int mgnr, bool deletePaymentData, bool deleteDeliveries, bool deleteAreaComs) {
using (var ctx = new AppDbContext()) {
var l = (await ctx.Members.FindAsync(mgnr))!;
if (deletePaymentData) {
ctx.RemoveRange(l.Credits);
}
if (deleteDeliveries) {
ctx.RemoveRange(l.Deliveries);
}
if (deleteAreaComs) {
ctx.RemoveRange(l.AreaCommitments);
}
ctx.Remove(l);
await ctx.SaveChangesAsync();
}
App.HintContextChange();
}
} }
} }

View File

@ -96,10 +96,16 @@ namespace Elwig.ViewModels {
private IEnumerable<object> _wineCultSource = []; private IEnumerable<object> _wineCultSource = [];
[ObservableProperty] [ObservableProperty]
private string? _gradationOeString; private string? _gradationOeString;
public double? GradationOe => double.TryParse(GradationOeString, out var oe) ? oe : null; public double? GradationOe {
get => double.TryParse(GradationOeString, out var oe) ? oe : null;
set => GradationOeString = $"{value:0}";
}
[ObservableProperty] [ObservableProperty]
private string? _gradationKmwString; private string? _gradationKmwString;
public double? GradationKmw => double.TryParse(GradationKmwString, out var kmw) ? kmw : null; public double? GradationKmw {
get => double.TryParse(GradationKmwString, out var kmw) ? kmw : null;
set => GradationKmwString = $"{value:0.0}";
}
[ObservableProperty] [ObservableProperty]
private WineQualLevel? _wineQualityLevel; private WineQualLevel? _wineQualityLevel;
[ObservableProperty] [ObservableProperty]
@ -152,10 +158,16 @@ namespace Elwig.ViewModels {
private string? _partComment; private string? _partComment;
[ObservableProperty] [ObservableProperty]
private string? _temperatureString; private string? _temperatureString;
public double? Temperature => double.TryParse(TemperatureString, out var t) ? t : null; public double? Temperature {
get => double.TryParse(TemperatureString, out var t) ? t : null;
set => TemperatureString = $"{value:0.0}";
}
[ObservableProperty] [ObservableProperty]
private string? _acidString; private string? _acidString;
public double? Acid => double.TryParse(AcidString, out var a) ? a : null; public double? Acid {
get => double.TryParse(AcidString, out var a) ? a : null;
set => AcidString = $"{value:0.0}";
}
[ObservableProperty] [ObservableProperty]
private bool _isLesewagen; private bool _isLesewagen;
[ObservableProperty] [ObservableProperty]

View File

@ -39,7 +39,7 @@ namespace Elwig.ViewModels {
[ObservableProperty] [ObservableProperty]
private bool _enableSearchInputs = true; private bool _enableSearchInputs = true;
[ObservableProperty] [ObservableProperty]
private IEnumerable<bool> _memberHasDeliveries = [ .. Enumerable.Range(0, 9999).Select(i => false) ]; private IEnumerable<bool> _memberHasDeliveries = [.. Enumerable.Range(0, 9999).Select(i => false)];
[ObservableProperty] [ObservableProperty]
private bool _memberListOrderByMgNr; private bool _memberListOrderByMgNr;
@ -50,10 +50,16 @@ namespace Elwig.ViewModels {
[ObservableProperty] [ObservableProperty]
private string? _mgNrString; private string? _mgNrString;
public int? MgNr => int.TryParse(MgNrString, out var mgnr) ? mgnr : null; public int? MgNr {
get => int.TryParse(MgNrString, out var mgnr) ? mgnr : null;
set => MgNrString = $"{value}";
}
[ObservableProperty] [ObservableProperty]
private string? _predecessorMgNrString; private string? _predecessorMgNrString;
public int? PredecessorMgNr => int.TryParse(PredecessorMgNrString, out var mgnr) ? mgnr : null; public int? PredecessorMgNr {
get => int.TryParse(PredecessorMgNrString, out var mgnr) ? mgnr : null;
set => PredecessorMgNrString = $"{value}";
}
[ObservableProperty] [ObservableProperty]
private bool _isJuridicalPerson; private bool _isJuridicalPerson;
[ObservableProperty] [ObservableProperty]
@ -76,7 +82,10 @@ namespace Elwig.ViewModels {
private string? _address; private string? _address;
[ObservableProperty] [ObservableProperty]
private string? _plzString; private string? _plzString;
public int? Plz => int.TryParse(PlzString, out var plz) ? plz : null; public int? Plz {
get => int.TryParse(PlzString, out var plz) ? plz : null;
set => PlzString = $"{value}";
}
[ObservableProperty] [ObservableProperty]
private AT_PlzDest? _ort; private AT_PlzDest? _ort;
[ObservableProperty] [ObservableProperty]
@ -88,7 +97,10 @@ namespace Elwig.ViewModels {
private string? _billingAddress; private string? _billingAddress;
[ObservableProperty] [ObservableProperty]
private string? _billingPlzString; private string? _billingPlzString;
public int? BillingPlz => int.TryParse(BillingPlzString, out var plz) ? plz : null; public int? BillingPlz {
get => int.TryParse(BillingPlzString, out var plz) ? plz : null;
set => BillingPlzString = $"{value}";
}
[ObservableProperty] [ObservableProperty]
private AT_PlzDest? _billingOrt; private AT_PlzDest? _billingOrt;
[ObservableProperty] [ObservableProperty]
@ -114,7 +126,10 @@ namespace Elwig.ViewModels {
private string? _exitDate; private string? _exitDate;
[ObservableProperty] [ObservableProperty]
private string? _businessSharesString; private string? _businessSharesString;
public int? BusinessShares => int.TryParse(BusinessSharesString, out var bs) ? bs : null; public int? BusinessShares {
get => int.TryParse(BusinessSharesString, out var bs) ? bs : null;
set => BusinessSharesString = $"{value}";
}
[ObservableProperty] [ObservableProperty]
private string? _accountingNr; private string? _accountingNr;
[ObservableProperty] [ObservableProperty]
@ -147,6 +162,14 @@ namespace Elwig.ViewModels {
public string? _number = number; public string? _number = number;
[ObservableProperty] [ObservableProperty]
public string? _comment = comment; public string? _comment = comment;
public override bool Equals(object? obj) {
return obj is PhoneNr nr && Type == nr.Type && Number == nr.Number && Comment == nr.Comment;
}
public override int GetHashCode() {
return Type ^ (Number?.GetHashCode() ?? 0) ^ (Comment?.GetHashCode() ?? 0);
}
} }
public ObservableCollection<PhoneNr> PhoneNrs { get; private set; } = [new(), new(), new(), new(), new(), new(), new(), new(), new()]; public ObservableCollection<PhoneNr> PhoneNrs { get; private set; } = [new(), new(), new(), new(), new(), new(), new(), new(), new()];

View File

@ -8,7 +8,6 @@ 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.Input; using System.Windows.Input;
namespace Elwig.Windows { namespace Elwig.Windows {
@ -358,22 +357,6 @@ namespace Elwig.Windows {
UpdateComboBox(ortInput); UpdateComboBox(ortInput);
} }
protected static void InitializeDelayTimer(TextBox tb, Action<object, TextChangedEventArgs> handler) {
var timer = new DispatcherTimer {
Interval = TimeSpan.FromMilliseconds(250)
};
timer.Tick += (object? sender, EventArgs evt) => {
timer.Stop();
var (oSender, oEvent) = ((object, TextChangedEventArgs))timer.Tag;
handler(oSender, oEvent);
};
tb.TextChanged += (object sender, TextChangedEventArgs evt) => {
timer.Stop();
timer.Tag = (sender, evt);
timer.Start();
};
}
protected bool InputTextChanged(TextBox input) { protected bool InputTextChanged(TextBox input) {
return InputTextChanged(input, new ValidationResult(true, null)); return InputTextChanged(input, new ValidationResult(true, null));
} }

View File

@ -88,8 +88,9 @@
<LineBreak/> <LineBreak/>
Filtern nach:<LineBreak/> Filtern nach:<LineBreak/>
<Bold>Sorte</Bold>: z.B. GV, zw, RR, ...<LineBreak/> <Bold>Sorte</Bold>: z.B. GV, zw, RR, ...<LineBreak/>
<Bold>Attribut</Bold>: z.B. Kabinett, dac, ... <LineBreak/> <Bold>Attribut</Bold>: z.B. Kabinett, dac, ...<LineBreak/>
<Bold>Flächenbindung</Bold>: z.B. GVK, GVD, ... <Bold>Flächenbindung</Bold>: z.B. GVK, GVD, ...<LineBreak/>
<Bold>Saison</Bold>: z.B. 2020, 2019... (in dieser Saison aktiv)
</TextBlock> </TextBlock>
</TextBox.ToolTip> </TextBox.ToolTip>
</TextBox> </TextBox>

View File

@ -33,7 +33,7 @@ namespace Elwig.Windows {
GstNrInput, AreaInput, AreaComTypeInput, WineCultivationInput GstNrInput, AreaInput, AreaComTypeInput, WineCultivationInput
]; ];
InitializeDelayTimer(SearchInput, SearchInput_TextChanged); ControlUtils.InitializeDelayTimer(SearchInput, SearchInput_TextChanged);
SearchInput.TextChanged -= SearchInput_TextChanged; SearchInput.TextChanged -= SearchInput_TextChanged;
ActiveAreaCommitmentInput.Content = ((string)ActiveAreaCommitmentInput.Content).Replace("2020", $"{Utils.CurrentLastSeason}"); ActiveAreaCommitmentInput.Content = ((string)ActiveAreaCommitmentInput.Content).Replace("2020", $"{Utils.CurrentLastSeason}");
} }

View File

@ -58,6 +58,8 @@ namespace Elwig.Windows {
foreach (var (old, attrid) in _attrs.Where(a => a.Value != null)) { foreach (var (old, attrid) in _attrs.Where(a => a.Value != null)) {
await ctx.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 ctx.Database.ExecuteSqlAsync($"UPDATE area_commitment_type SET vtrgid = (sortid || COALESCE(attrid, '') || COALESCE(disc, '')) WHERE attrid = {attrid}");
await ctx.Database.ExecuteSqlRawAsync($"UPDATE payment_variant SET data = REPLACE(REPLACE(data, '/{old}\"', '/{attrid}\"'), '/{old}-', '/{attrid}-')");
} }
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();

View File

@ -58,6 +58,7 @@ namespace Elwig.Windows {
foreach (var (old, cultid) in _cults.Where(c => c.Value != null)) { foreach (var (old, cultid) in _cults.Where(c => c.Value != null)) {
await ctx.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 ctx.Database.ExecuteSqlRawAsync($"UPDATE payment_variant SET data = REPLACE(data, '-{old}\"', '-{cultid}\"')");
} }
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();

View File

@ -67,13 +67,29 @@
<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="Lieferschein"> <MenuItem Header="Lieferschein">
<MenuItem x:Name="Menu_DeliveryNote_Show" Header="...anzeigen (PDF)" IsEnabled="False" <MenuItem x:Name="Menu_DeliveryNote_Show" Header="...anzeigen (PDF)" IsEnabled="False"
Click="Menu_DeliveryNote_Show_Click" InputGestureText="Strg+P"/> Click="Menu_DeliveryNote_Show_Click" InputGestureText="Strg+P">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE8FF;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_DeliveryNote_SavePdf" Header="...speichern... (PDF)" IsEnabled="False" <MenuItem x:Name="Menu_DeliveryNote_SavePdf" Header="...speichern... (PDF)" IsEnabled="False"
Click="Menu_DeliveryNote_SavePdf_Click"/> Click="Menu_DeliveryNote_SavePdf_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xEA90;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_DeliveryNote_Print" Header="...drucken" IsEnabled="False" <MenuItem x:Name="Menu_DeliveryNote_Print" Header="...drucken" IsEnabled="False"
Click="Menu_DeliveryNote_Print_Click" InputGestureText="Strg+Shift+P"/> Click="Menu_DeliveryNote_Print_Click" InputGestureText="Strg+Shift+P">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE749;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_DeliveryNote_Email" Header="...per E-Mail schicken" IsEnabled="False" <MenuItem x:Name="Menu_DeliveryNote_Email" Header="...per E-Mail schicken" IsEnabled="False"
Click="Menu_DeliveryNote_Email_Click"/> Click="Menu_DeliveryNote_Email_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE89C;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="Lieferjournal"> <MenuItem Header="Lieferjournal">
<MenuItem x:Name="Menu_DeliveryJournal_SaveFilters" Header="...aus Filtern speichern... (Excel)" <MenuItem x:Name="Menu_DeliveryJournal_SaveFilters" Header="...aus Filtern speichern... (Excel)"
@ -120,6 +136,25 @@
<MenuItem x:Name="Menu_WineQualityStatistics_ModeKmw1" Header="...nach °KMW aufschlüsseln (ganze)" IsCheckable="True" <MenuItem x:Name="Menu_WineQualityStatistics_ModeKmw1" Header="...nach °KMW aufschlüsseln (ganze)" IsCheckable="True"
Click="Menu_WineQualityStatistics_Mode_Click"/> Click="Menu_WineQualityStatistics_Mode_Click"/>
</MenuItem> </MenuItem>
<MenuItem Header="Abwertungsliste" x:Name="Menu_DeliveryDepreciationList">
<MenuItem x:Name="Menu_DeliveryDepreciationList_SaveFilters" Header="...aus Filtern speichern... (Excel)"
Click="Menu_DeliveryDepreciationList_SaveFilters_Click"/>
<MenuItem x:Name="Menu_DeliveryDepreciationList_ShowFilters" Header="...aus Filtern anzeigen (PDF)"
Click="Menu_DeliveryDepreciationList_ShowFilters_Click"/>
<MenuItem x:Name="Menu_DeliveryDepreciationList_SavePdfFilters" Header="...aus Filtern speichern... (PDF)"
Click="Menu_DeliveryDepreciationList_SavePdfFilters_Click"/>
<MenuItem x:Name="Menu_DeliveryDepreciationList_PrintFilters" Header="...aus Filtern drucken"
Click="Menu_DeliveryDepreciationList_PrintFilters_Click"/>
<Separator/>
<MenuItem x:Name="Menu_DeliveryDepreciationList_SaveSeason" Header="...von Saison speichern... (Excel)"
Click="Menu_DeliveryDepreciationList_SaveSeason_Click"/>
<MenuItem x:Name="Menu_DeliveryDepreciationList_ShowSeason" Header="...von Saison anzeigen (PDF)"
Click="Menu_DeliveryDepreciationList_ShowSeason_Click"/>
<MenuItem x:Name="Menu_DeliveryDepreciationList_SavePdfSeason" Header="...von Saison speichern... (PDF)"
Click="Menu_DeliveryDepreciationList_SavePdfSeason_Click"/>
<MenuItem x:Name="Menu_DeliveryDepreciationList_PrintSeason" Header="...von Saison drucken"
Click="Menu_DeliveryDepreciationList_PrintSeason_Click"/>
</MenuItem>
<MenuItem Header="BKI"> <MenuItem Header="BKI">
<MenuItem x:Name="Menu_Bki_SaveList" Header="Traubentransportscheinliste speichern..."/> <MenuItem x:Name="Menu_Bki_SaveList" Header="Traubentransportscheinliste speichern..."/>
</MenuItem> </MenuItem>
@ -274,10 +309,10 @@
<TextBlock FontWeight="Bold">Alt+Einfg</TextBlock> <TextBlock FontWeight="Bold">Alt+Einfg</TextBlock>
</Button.ToolTip> </Button.ToolTip>
</Button> </Button>
<Button x:Name="AbwertenButton" Content="Abwerten" IsEnabled="False" <Button x:Name="DepreciateButton" Content="Abwert./Aufteil." IsEnabled="False"
ToolTip="Ausgewählte Teillieferung vollständig oder teilweise abwerten" ToolTip="Lieferung vollständig oder teilweise abwerten bzw. auf anderes Mitglied aufteilen"
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="DepreciateButton_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">
@ -391,36 +426,22 @@
Grid.Column="1" Margin="0,100,10,10" Grid.Column="1" Margin="0,100,10,10"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<Grid Grid.ColumnSpan="2" Margin="0,130,0,0"> <ListBox x:Name="DeliveryPartList" Margin="5,135,5,5" Grid.ColumnSpan="2"
<Grid.ColumnDefinitions> SelectionChanged="DeliveryPartList_SelectionChanged">
<ColumnDefinition/> <ListBox.ItemTemplate>
<ColumnDefinition/> <DataTemplate>
</Grid.ColumnDefinitions> <StackPanel Orientation="Horizontal">
<ListBox x:Name="DeliveryPartList" Margin="5,5,5,38" Grid.ColumnSpan="2" <TextBlock Text="{Binding DPNr}" Width="13" TextAlignment="Right" Margin="0,0,7,0"/>
SelectionChanged="DeliveryPartList_SelectionChanged"> <TextBlock Text="{Binding SortId}" Width="30"/>
<ListBox.ItemTemplate> <TextBlock Text="{Binding Kmw, StringFormat='{}{0:N1}°'}" Width="40" TextAlignment="Right" Padding="0,0,10,0"/>
<DataTemplate> <TextBlock Text="{Binding QualId}" Width="30"/>
<StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Weight, StringFormat='{}{0:N0} kg'}" Width="60" TextAlignment="Right" Padding="0,0,10,0"/>
<TextBlock Text="{Binding DPNr}" Width="20"/> <TextBlock Text="{Binding Attribute.Name}" Width="60"/>
<TextBlock Text="{Binding SortId}" Width="30"/> <TextBlock Text="{Binding Cultivation.Name}" Width="50"/>
<TextBlock Text="{Binding Kmw, StringFormat='{}{0:0.0}°'}" Width="40" TextAlignment="Right" Padding="0,0,10,0"/> </StackPanel>
<TextBlock Text="{Binding QualId}" Width="30"/> </DataTemplate>
<TextBlock Text="{Binding Weight, StringFormat='{}{0:N0} kg'}" Width="60" TextAlignment="Right" Padding="0,0,10,0"/> </ListBox.ItemTemplate>
<TextBlock Text="{Binding Attribute.Name}" Width="60"/> </ListBox>
<TextBlock Text="{Binding Cultivation.Name}" Width="50"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button x:Name="ExtractDeliveryPartButton" Content="Extrahieren" IsEnabled="False"
ToolTip="Ausgewählte Teillieferung aus aktueller Lieferung entfernen und entweder anderer oder neuer Lieferung zuordnen"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,10,2.5,5" Grid.Column="0" Grid.Row="2"
Click="ExtractDeliveryPartButton_Click"/>
<Button x:Name="DeleteDeliveryPartButton" Content="Löschen" IsEnabled="False"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,10,5,5" Grid.Column="1" Grid.Row="2"
Click="DeleteDeliveryPartButton_Click"/>
</Grid>
</Grid> </Grid>
</GroupBox> </GroupBox>

View File

@ -66,7 +66,7 @@ namespace Elwig.Windows {
SecondsTimer.Tick += new EventHandler(OnSecondPassed); SecondsTimer.Tick += new EventHandler(OnSecondPassed);
SecondsTimer.Interval = new TimeSpan(0, 0, 1); SecondsTimer.Interval = new TimeSpan(0, 0, 1);
InitializeDelayTimer(SearchInput, SearchInput_TextChanged); ControlUtils.InitializeDelayTimer(SearchInput, SearchInput_TextChanged);
SearchInput.TextChanged -= SearchInput_TextChanged; SearchInput.TextChanged -= SearchInput_TextChanged;
ViewModel.FilterSeason = Utils.CurrentLastSeason; ViewModel.FilterSeason = Utils.CurrentLastSeason;
@ -155,6 +155,10 @@ namespace Elwig.Windows {
private async void Menu_DeliveryNote_Email_Click(object sender, RoutedEventArgs evt) { private async void Menu_DeliveryNote_Email_Click(object sender, RoutedEventArgs evt) {
if (DeliveryList.SelectedItem is not Delivery d) if (DeliveryList.SelectedItem is not Delivery d)
return; return;
var res = MessageBox.Show("Soll eine E-Mail verschickt werden?", "Lieferschein verschicken",
MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
if (res != MessageBoxResult.Yes)
return;
await DeliveryService.GenerateDeliveryNote(d.Year, d.DId, ExportMode.Email); await DeliveryService.GenerateDeliveryNote(d.Year, d.DId, ExportMode.Email);
} }
@ -168,9 +172,11 @@ namespace Elwig.Windows {
Title = $"Traubentransportscheinliste (BKI) speichern unter - Elwig" Title = $"Traubentransportscheinliste (BKI) speichern unter - Elwig"
}; };
if (d.ShowDialog() == true) { if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.AppStarting; Mouse.OverrideCursor = Cursors.Wait;
using var file = new Bki(d.FileName); await Task.Run(async () => {
await file.ExportAsync(year); using var file = new Bki(d.FileName);
await file.ExportAsync(year);
});
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
} }
} }
@ -246,6 +252,23 @@ namespace Elwig.Windows {
await App.Client.UpdateValues(); await App.Client.UpdateValues();
} }
private async void Menu_DeliveryDepreciationList_SaveFilters_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromFilters, ExportMode.SaveList);
private async void Menu_DeliveryDepreciationList_ShowFilters_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromFilters, ExportMode.Show);
private async void Menu_DeliveryDepreciationList_SavePdfFilters_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromFilters, ExportMode.SavePdf);
private async void Menu_DeliveryDepreciationList_PrintFilters_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromFilters, ExportMode.Print);
private async void Menu_DeliveryDepreciationList_SaveSeason_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromSeason, ExportMode.SaveList);
private async void Menu_DeliveryDepreciationList_ShowSeason_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromSeason, ExportMode.Show);
private async void Menu_DeliveryDepreciationList_SavePdfSeason_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromSeason, ExportMode.SavePdf);
private async void Menu_DeliveryDepreciationList_PrintSeason_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromSeason, ExportMode.Print);
private void Menu_Settings_EnableFreeEditing_Checked(object sender, RoutedEventArgs evt) { private void Menu_Settings_EnableFreeEditing_Checked(object sender, RoutedEventArgs evt) {
if (IsEditing || IsCreating) { if (IsEditing || IsCreating) {
DateInput.IsReadOnly = false; DateInput.IsReadOnly = false;
@ -638,15 +661,11 @@ namespace Elwig.Windows {
private void DeliveryPartList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) { private void DeliveryPartList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
RefreshInputs(); RefreshInputs();
if (DeliveryPartList.SelectedItem is DeliveryPart p) { if (DeliveryPartList.SelectedItem is DeliveryPart p) {
AbwertenButton.IsEnabled = p.QualId != "WEI"; DepreciateButton.IsEnabled = true;
EditDeliveryButton.IsEnabled = true; EditDeliveryButton.IsEnabled = true;
ExtractDeliveryPartButton.IsEnabled = !IsCreating;
DeleteDeliveryPartButton.IsEnabled = DeliveryList.SelectedItem is Delivery { Parts.Count: > 1 } && !IsCreating;
} else { } else {
AbwertenButton.IsEnabled = false; DepreciateButton.IsEnabled = false;
EditDeliveryButton.IsEnabled = false; EditDeliveryButton.IsEnabled = false;
ExtractDeliveryPartButton.IsEnabled = false;
DeleteDeliveryPartButton.IsEnabled = false;
} }
} }
@ -855,43 +874,29 @@ namespace Elwig.Windows {
LockSearchInputs(); LockSearchInputs();
} }
private async void AbwertenButton_Click(object sender, RoutedEventArgs evt) { private async void DepreciateButton_Click(object sender, RoutedEventArgs evt) {
if (DeliveryPartList.SelectedItem is not DeliveryPart p) return; if (DeliveryList.SelectedItem is not Delivery d) return;
var res = Utils.ShowAbwertenDialog($"{p.Delivery.LsNr}/{p.DPNr}", p.Delivery.Member.AdministrativeName, p.Weight); var res = Utils.ShowDeliverySplittingDialog(d);
if (res == null || res <= 0) if (res == null)
return; return;
Mouse.OverrideCursor = Cursors.AppStarting; Mouse.OverrideCursor = Cursors.AppStarting;
try { try {
using var ctx = new AppDbContext(); var id = res.Value.Item1;
ClearOriginalValues(); var weights = res.Value.Item2;
if (res >= p.Weight) { if (id == null) {
ControlUtils.SelectItemWithPk(WineQualityLevelInput, "WEI"); // abwerten
ControlUtils.SelectItemWithPk(WineOriginInput, "OEST"); await DeliveryService.DepreciateDelivery(d.Year, d.DId, weights);
p.QualId = "WEI"; } else if (id.All(char.IsAsciiDigit)) {
p.HkId = "OEST"; // auf Mitglied übertragen
ctx.Update(p); var n = await DeliveryService.SplitDeliveryToMember(d.Year, d.DId, weights, int.Parse(id));
await Task.Delay(500);
ControlUtils.SelectItemWithPk(DeliveryList, n.Year, n.DId);
} else { } else {
var w = p.Weight - res.Value; // zu existierender Lieferung hinzufügen
ViewModel.Weight = w; var n = await DeliveryService.SplitDeliveryToLsNr(d.Year, d.DId, weights, id);
p.Weight = w; ControlUtils.SelectItemWithPk(DeliveryList, n.Year, n.DId);
ctx.Update(p);
var d = p.Delivery;
var p2 = ctx.CreateProxy<DeliveryPart>();
var values = ctx.Entry(p).CurrentValues;
ctx.Entry(p2).CurrentValues.SetValues(values);
p2.DPNr = await ctx.NextDPNr(d.Year, d.DId);
p2.Weight = res.Value;
p2.QualId = "WEI";
p2.HkId = "OEST";
ctx.Add(p2);
ctx.UpdateDeliveryPartModifiers(p2, [], p.Modifiers);
} }
await ctx.SaveChangesAsync();
await RefreshDeliveryParts();
FinishInputFilling();
} 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;
@ -929,9 +934,7 @@ namespace Elwig.Windows {
UnlockInputs(); UnlockInputs();
LockSearchInputs(); LockSearchInputs();
AbwertenButton.IsEnabled = false; DepreciateButton.IsEnabled = false;
ExtractDeliveryPartButton.IsEnabled = false;
DeleteDeliveryPartButton.IsEnabled = false;
} }
protected override void ShortcutDelete() { protected override void ShortcutDelete() {
@ -949,12 +952,13 @@ namespace Elwig.Windows {
"Lieferung löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel); "Lieferung löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (r == MessageBoxResult.OK) { if (r == MessageBoxResult.OK) {
Mouse.OverrideCursor = Cursors.AppStarting; Mouse.OverrideCursor = Cursors.AppStarting;
using (var ctx = new AppDbContext()) { try {
ctx.Remove(d); await DeliveryService.DeleteDelivery(d.LsNr);
await ctx.SaveChangesAsync(); } catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Lieferung löschen", MessageBoxButton.OK, MessageBoxImage.Error);
} }
await RefreshList();
await RefreshDeliveryParts();
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
} }
} }
@ -1003,9 +1007,7 @@ namespace Elwig.Windows {
await RefreshDeliveryParts(); await RefreshDeliveryParts();
RefreshInputs(); RefreshInputs();
AbwertenButton.IsEnabled = p.QualId != "WEI"; DepreciateButton.IsEnabled = true;
ExtractDeliveryPartButton.IsEnabled = DeliveryPartList.SelectedItem != null && !IsCreating;
DeleteDeliveryPartButton.IsEnabled = DeliveryList.SelectedItem is Delivery { Parts.Count: > 1 } && !IsCreating;
} }
protected override void ShortcutReset() { protected override void ShortcutReset() {
@ -1036,93 +1038,7 @@ namespace Elwig.Windows {
LockInputs(); LockInputs();
UnlockSearchInputs(); UnlockSearchInputs();
AbwertenButton.IsEnabled = DeliveryPartList.SelectedItem is DeliveryPart p && p.QualId != "WEI"; DepreciateButton.IsEnabled = DeliveryPartList.SelectedItem != null;
ExtractDeliveryPartButton.IsEnabled = DeliveryPartList.SelectedItem != null && !IsCreating;
DeleteDeliveryPartButton.IsEnabled = DeliveryList.SelectedItem is Delivery { Parts.Count: > 1 } && !IsCreating;
}
private async void ExtractDeliveryPartButton_Click(object sender, RoutedEventArgs evt) {
if (DeliveryPartList.SelectedItem is not DeliveryPart p)
return;
var delivery = p.Delivery;
var day = delivery.Date;
var count = delivery.Parts.Count;
if (delivery.Time <= new TimeOnly(3, 0))
day = day.AddDays(-1);
string? res;
using (var ctx = new AppDbContext()) {
var lsnrs = await ctx.Deliveries
.Where(d => d.ZwstId == delivery.ZwstId)
.Where(d => (d.DateString == day.ToString("yyyy-MM-dd") && (d.TimeString == null || d.TimeString.CompareTo("03:00:00") > 0)) ||
(d.DateString == day.AddDays(1).ToString("yyyy-MM-dd") && (d.TimeString == null || d.TimeString.CompareTo("03:00:00") <= 0)))
.Where(d => d.LsNr != delivery.LsNr)
.OrderBy(d => d.LsNr)
.Select(d => d.LsNr)
.ToListAsync();
res = Utils.ShowDeliveryExtractionDialog($"{delivery.LsNr}/{p.DPNr}", delivery.Member.AdministrativeName, count == 1, lsnrs);
if (res == null)
return;
}
Mouse.OverrideCursor = Cursors.AppStarting;
try {
using var ctx = new AppDbContext();
if (res == "new") {
var lnr = await ctx.NextLNr(delivery.Date, delivery.ZwstId);
var lsnr = Utils.GenerateLsNr(delivery.Date, delivery.ZwstId, lnr);
ctx.Add(new Delivery {
Year = p.Year,
DId = await ctx.NextDId(p.Year),
LNr = lnr,
DateString = $"{delivery.Date:yyyy-MM-dd}",
TimeString = $"{delivery.Time:HH:mm:ss}",
ZwstId = delivery.ZwstId,
MgNr = delivery.MgNr,
Comment = delivery.Comment,
LsNr = lsnr,
});
await ctx.SaveChangesAsync();
res = lsnr;
}
Delivery? d = await ctx.Deliveries.Where(d => d.LsNr == res).FirstOrDefaultAsync();
if (d == null) return;
await ctx.Database.ExecuteSqlAsync($"UPDATE delivery_part SET year = {d.Year}, did = {d.DId}, dpnr = {await ctx.NextDPNr(d.Year, d.DId)} WHERE (year, did, dpnr) = ({p.Year}, {p.DId}, {p.DPNr})");
if (count == 1) {
await ctx.Database.ExecuteSqlAsync($"DELETE FROM delivery WHERE (year, did) = ({delivery.Year}, {delivery.DId})");
}
await ctx.SaveChangesAsync();
await RefreshList();
ControlUtils.SelectItem(DeliveryList, d);
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Lieferung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
private async void DeleteDeliveryPartButton_Click(object sender, RoutedEventArgs evt) {
if (DeliveryPartList.SelectedItem is not DeliveryPart p)
return;
var r = MessageBox.Show(
$"Soll die Teillieferung Nr. {p.DPNr} wirklich unwiderruflich gelöscht werden?",
"Lieferung löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (r == MessageBoxResult.OK) {
Mouse.OverrideCursor = Cursors.AppStarting;
using (var ctx = new AppDbContext()) {
ctx.Remove(p);
await ctx.SaveChangesAsync();
}
await RefreshDeliveryParts();
Mouse.OverrideCursor = null;
}
} }
private void ShowSaveResetCancelButtons() { private void ShowSaveResetCancelButtons() {
@ -1145,22 +1061,22 @@ namespace Elwig.Windows {
private void ShowNewEditDeleteButtons() { private void ShowNewEditDeleteButtons() {
NewDeliveryButton.IsEnabled = ViewModel.IsReceipt; NewDeliveryButton.IsEnabled = ViewModel.IsReceipt;
AbwertenButton.IsEnabled = DeliveryPartList.SelectedItem is DeliveryPart p && p.QualId == "WEI"; DepreciateButton.IsEnabled = DeliveryList.SelectedItem != null;
EditDeliveryButton.IsEnabled = DeliveryPartList.SelectedItem != null; EditDeliveryButton.IsEnabled = DeliveryPartList.SelectedItem != null;
DeleteDeliveryButton.IsEnabled = DeliveryList.SelectedItem != null; DeleteDeliveryButton.IsEnabled = DeliveryList.SelectedItem != null;
NewDeliveryButton.Visibility = ViewModel.IsReceipt ? Visibility.Visible : Visibility.Hidden; NewDeliveryButton.Visibility = ViewModel.IsReceipt ? Visibility.Visible : Visibility.Hidden;
AbwertenButton.Visibility = !ViewModel.IsReceipt ? Visibility.Visible : Visibility.Hidden; DepreciateButton.Visibility = !ViewModel.IsReceipt ? Visibility.Visible : Visibility.Hidden;
EditDeliveryButton.Visibility = Visibility.Visible; EditDeliveryButton.Visibility = Visibility.Visible;
DeleteDeliveryButton.Visibility = Visibility.Visible; DeleteDeliveryButton.Visibility = Visibility.Visible;
} }
private void HideNewEditDeleteButtons() { private void HideNewEditDeleteButtons() {
NewDeliveryButton.IsEnabled = false; NewDeliveryButton.IsEnabled = false;
AbwertenButton.IsEnabled = false; DepreciateButton.IsEnabled = false;
EditDeliveryButton.IsEnabled = false; EditDeliveryButton.IsEnabled = false;
DeleteDeliveryButton.IsEnabled = false; DeleteDeliveryButton.IsEnabled = false;
NewDeliveryButton.Visibility = Visibility.Hidden; NewDeliveryButton.Visibility = Visibility.Hidden;
AbwertenButton.Visibility = Visibility.Hidden; DepreciateButton.Visibility = Visibility.Hidden;
EditDeliveryButton.Visibility = Visibility.Hidden; EditDeliveryButton.Visibility = Visibility.Hidden;
DeleteDeliveryButton.Visibility = Visibility.Hidden; DeleteDeliveryButton.Visibility = Visibility.Hidden;
} }

View File

@ -69,13 +69,29 @@
<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="Anmeldeliste"> <MenuItem Header="Anmeldeliste">
<MenuItem x:Name="Menu_DeliveryAncmtList_SaveSelected" Header="...von ausgewähltem Leseplan speichern... (Excel)" <MenuItem x:Name="Menu_DeliveryAncmtList_SaveSelected" Header="...von ausgewähltem Leseplan speichern... (Excel)"
Click="Menu_DeliveryAncmtList_SaveSelected_Click"/> Click="Menu_DeliveryAncmtList_SaveSelected_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE9F9;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_DeliveryAncmtList_ShowSelected" Header="...von ausgewähltem Leseplan anzeigen (PDF)" <MenuItem x:Name="Menu_DeliveryAncmtList_ShowSelected" Header="...von ausgewähltem Leseplan anzeigen (PDF)"
Click="Menu_DeliveryAncmtList_ShowSelected_Click" InputGestureText="Strg+P"/> Click="Menu_DeliveryAncmtList_ShowSelected_Click" InputGestureText="Strg+P">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE8FF;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_DeliveryAncmtList_SavePdfSelected" Header="...von ausgewähltem Leseplan speichern... (PDF)" <MenuItem x:Name="Menu_DeliveryAncmtList_SavePdfSelected" Header="...von ausgewähltem Leseplan speichern... (PDF)"
Click="Menu_DeliveryAncmtList_SavePdfSelected_Click"/> Click="Menu_DeliveryAncmtList_SavePdfSelected_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xEA90;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_DeliveryAncmtList_PrintSelected" Header="...von ausgewähltem Leseplan drucken" <MenuItem x:Name="Menu_DeliveryAncmtList_PrintSelected" Header="...von ausgewähltem Leseplan drucken"
Click="Menu_DeliveryAncmtList_PrintSelected_Click" InputGestureText="Strg+Shift+P"/> Click="Menu_DeliveryAncmtList_PrintSelected_Click" InputGestureText="Strg+Shift+P">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE749;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
</Menu> </Menu>

View File

@ -33,7 +33,7 @@ namespace Elwig.Windows {
DoShowWarningWindows = false; DoShowWarningWindows = false;
InitializeDelayTimer(SearchInput, SearchInput_TextChanged); ControlUtils.InitializeDelayTimer(SearchInput, SearchInput_TextChanged);
SearchInput.TextChanged -= SearchInput_TextChanged; SearchInput.TextChanged -= SearchInput_TextChanged;
ViewModel.FilterSeason = Utils.CurrentLastSeason; ViewModel.FilterSeason = Utils.CurrentLastSeason;
} }

View File

@ -27,7 +27,7 @@ namespace Elwig.Windows {
DateInput, BranchInput, DescriptionInput, MainWineVarietiesInput, DateInput, BranchInput, DescriptionInput, MainWineVarietiesInput,
]; ];
InitializeDelayTimer(SearchInput, SearchInput_TextChanged); ControlUtils.InitializeDelayTimer(SearchInput, SearchInput_TextChanged);
SearchInput.TextChanged -= SearchInput_TextChanged; SearchInput.TextChanged -= SearchInput_TextChanged;
ViewModel.FilterSeason = Utils.CurrentLastSeason; ViewModel.FilterSeason = Utils.CurrentLastSeason;
} }

View File

@ -36,9 +36,10 @@ namespace Elwig.Windows {
} }
private void EventList_SelectionChanged(object sender, RoutedEventArgs evt) { private void EventList_SelectionChanged(object sender, RoutedEventArgs evt) {
var t = EventList.SelectedItem.GetType(); var item = EventList.SelectedItem;
var t = item.GetType();
var p = t.GetProperty("Event")!; var p = t.GetProperty("Event")!;
EventData.Text = ((EventLogEntry)p.GetValue(EventList.SelectedItem)!).Message; EventData.Text = ((EventLogEntry)p.GetValue(item)!).Message;
} }
} }
} }

View File

@ -0,0 +1,54 @@
<Window x:Class="Elwig.Windows.MailLogWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Elwig.Windows"
Title="Ausgangs-Protokoll - Rundschreiben - Elwig" Height="600" Width="1000">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="FilterInput" Margin="10,10,300,10" Height="25" FontSize="14" Padding="2" TextWrapping="NoWrap"
HorizontalAlignment="Stretch" VerticalAlignment="Top"
TextChanged="FilterInput_TextChanged"/>
<ComboBox x:Name="TypeInput" Margin="10,10,165,10" Width="130" Height="25" FontSize="14"
HorizontalAlignment="Right" VerticalAlignment="Top"
SelectionChanged="TypeInput_SelectionChanged">
<ComboBoxItem Content="Post &amp; E-Mail" IsSelected="True"/>
<ComboBoxItem Content="Nur Post"/>
<ComboBoxItem Content="Nur E-Mail"/>
</ComboBox>
<ComboBox x:Name="TimeSpanInput" Margin="10,10,10,10" Width="150" Height="25" FontSize="14"
HorizontalAlignment="Right" VerticalAlignment="Top"
SelectionChanged="TimeSpanInput_SelectionChanged">
<ComboBoxItem Content="letzten 7 Tage" IsSelected="True"/>
<ComboBoxItem Content="letzten 6 Monate"/>
<ComboBoxItem Content="Immer"/>
</ComboBox>
<DataGrid x:Name="MailList" AutoGenerateColumns="False" HeadersVisibility="Column" IsReadOnly="True" GridLinesVisibility="None" SelectionMode="Single"
CanUserDeleteRows="False" CanUserResizeRows="False" CanUserAddRows="False"
SelectionChanged="MailList_SelectionChanged"
Margin="10,40,5,10">
<DataGrid.Columns>
<DataGridTextColumn Header="Zeitpunkt" Binding="{Binding DateTime, StringFormat='{}{0:dd.MM.yyyy HH:mm:ss}'}" Width="120"/>
<DataGridTextColumn Header="Typ" Binding="{Binding Type}" Width="50"/>
<DataGridTextColumn Header="MgNr." Binding="{Binding MgNr}" Width="50"/>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="200"/>
<DataGridTextColumn Header="Adressen" Binding="{Binding Addresses}" Width="200"/>
<DataGridTextColumn Header="Betreff" Binding="{Binding Subject}" Width="250"/>
<DataGridTextColumn Header="Anhänge" Binding="{Binding Attachments}" Width="200"/>
</DataGrid.Columns>
</DataGrid>
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<TextBox x:Name="MailData" Grid.Column="2" Margin="5,10,10,10" IsReadOnly="True"
HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible"/>
</Grid>
</Window>

View File

@ -0,0 +1,83 @@
using Elwig.Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
namespace Elwig.Windows {
public partial class MailLogWindow : Window {
record struct Row (DateTime DateTime, string Type, int MgNr, string Name, string Addresses, string Subject, string Attachments);
private List<Row> Data = [];
public MailLogWindow() {
InitializeComponent();
WindowState = WindowState.Maximized;
ControlUtils.InitializeDelayTimer(FilterInput, FilterInput_TextChanged);
FilterInput.TextChanged -= FilterInput_TextChanged;
}
private async void TimeSpanInput_SelectionChanged(object sender, RoutedEventArgs evt) {
DateTime? fromDate = DateTime.Now;
if (TimeSpanInput.SelectedIndex == 0) {
fromDate = fromDate.Value.AddDays(-7);
} else if (TimeSpanInput.SelectedIndex == 1) {
fromDate = fromDate.Value.AddMonths(-6);
} else {
fromDate = null;
}
var mails = await Utils.GetSentMails(fromDate: fromDate == null ? null : DateOnly.FromDateTime(fromDate.Value));
Data = mails.Reverse().Select(m => new Row(
m.DateTime,
m.Type == "email" ? "E-Mail" : m.Type == "postal" ? "Post" : "?",
m.MgNr,
m.Name,
string.Join("\n", m.Addresses),
m.Subject,
string.Join("\n", m.Attachments)
)).ToList();
MailList.ItemsSource = Data;
ApplyFilters();
}
private void FilterInput_TextChanged(object sender, RoutedEventArgs evt) {
ApplyFilters();
}
private void TypeInput_SelectionChanged(object sender, RoutedEventArgs evt) {
ApplyFilters();
}
private void ApplyFilters() {
var filters = FilterInput.Text.Split(' ');
IEnumerable<Row> data = Data;
switch (TypeInput.SelectedIndex) {
case 1: data = data.Where(d => d.Type == "Post"); break;
case 2: data = data.Where(d => d.Type == "E-Mail"); break;
}
foreach (var filter in filters) {
if (int.TryParse(filter, out var mgnr)) {
data = data.Where(d => d.MgNr == mgnr);
} else {
var f = filter.ToLower();
data = data.Where(d => d.Name.Contains(f, StringComparison.CurrentCultureIgnoreCase) ||
d.Addresses.Contains(f, StringComparison.CurrentCultureIgnoreCase) ||
d.Subject.Contains(f, StringComparison.CurrentCultureIgnoreCase) ||
d.Attachments.Contains(f, StringComparison.CurrentCultureIgnoreCase));
}
}
if (IsLoaded)
MailList.ItemsSource = data.ToList();
}
private async void MailList_SelectionChanged(object sender, RoutedEventArgs evt) {
if (MailList.SelectedItem is not Row item) return;
if (item.Type == "E-Mail") {
MailData.Text = await Utils.FindSentMailBody(item.DateTime);
} else {
MailData.Text = "";
}
}
}
}

View File

@ -27,281 +27,334 @@
<Setter Property="TextWrapping" Value="NoWrap"/> <Setter Property="TextWrapping" Value="NoWrap"/>
</Style> </Style>
</Window.Resources> </Window.Resources>
<TabControl x:Name="TabControl" BorderThickness="0" PreviewDragOver="Document_PreviwDragOver" AllowDrop="True" Drop="Document_Drop"> <Grid>
<TabItem Header="Dokumente" Visibility="Collapsed"> <Grid.RowDefinitions>
<Grid> <RowDefinition Height="19"/>
<Grid.ColumnDefinitions> <RowDefinition Height="1*"/>
<ColumnDefinition Width="*"/> <RowDefinition Height="24"/>
<ColumnDefinition Width="320"/> </Grid.RowDefinitions>
</Grid.ColumnDefinitions>
<Grid Height="200" VerticalAlignment="Top" HorizontalAlignment="Stretch"> <Menu BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
<MenuItem Header="Hilfe">
<MenuItem x:Name="Menu_Help_Log" Header="Ausgangs-Protokoll anzeigen"
Click="Menu_Help_Log_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xF168;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem>
</Menu>
<TabControl x:Name="TabControl" BorderThickness="0" Grid.Row="1"
PreviewDragOver="Document_PreviwDragOver" AllowDrop="True" Drop="Document_Drop">
<TabItem Header="Dokumente" Visibility="Collapsed">
<Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="25"/> <ColumnDefinition Width="320"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<Label Content="Verfügbare Dokumente" <Grid Height="200" VerticalAlignment="Top" HorizontalAlignment="Stretch">
Grid.Column="0" Margin="10,8,10,10"/>
<ListBox x:Name="AvaiableDocumentsList"
Grid.Column="0" Margin="10,30,10,10"
SelectionChanged="AvaiableDocumentsList_SelectionChanged"/>
<Button x:Name="DocumentAddButton" Content="&#xF0AF;" FontFamily="Segoe MDL2 Assets" FontSize="14"
Grid.Column="1" Margin="0,0,0,30" VerticalAlignment="Center" Height="25" IsEnabled="False"
Click="DocumentAddButton_Click"/>
<Button x:Name="DocumentRemoveButton" Content="&#xE74D;" FontFamily="Segoe MDL2 Assets" FontSize="16" Padding="1.5,0,0,0"
Grid.Column="1" Margin="0,30,0,0" VerticalAlignment="Center" Height="25" IsEnabled="False"
Click="DocumentRemoveButton_Click"/>
<Label Content="Ausgewählte Dokumente"
Grid.Column="2" Margin="10,8,10,10"/>
<ListBox x:Name="SelectedDocumentsList" DisplayMemberPath="Name"
Grid.Column="2" Margin="10,30,10,37"
SelectionChanged="SelectedDocumentsList_SelectionChanged">
<ListBox.InputBindings>
<KeyBinding Key="Delete" Command="{Binding Path=DeleteCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>
</ListBox.InputBindings>
</ListBox>
<Button x:Name="SelectDocumentButton" Content="Durchsuchen..."
Grid.Column="2" VerticalAlignment="Bottom" Margin="10,10,10,10" Height="22"
Click="SelectDocumentButton_Click"/>
</Grid>
<GroupBox x:Name="DocumentBox" Header="Dokument" Margin="10,170,10,47" HorizontalAlignment="Stretch">
<Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<CheckBox x:Name="DocumentNonDeliverersInput" Content="Auch Nicht-Lieferanten miteinbeziehen" <Label Content="Verfügbare Dokumente"
Margin="10,10,10,10" Grid.Column="1"/> Grid.Column="0" Margin="10,8,10,10"/>
<ListBox x:Name="AvaiableDocumentsList"
Grid.Column="0" Margin="10,30,10,10"
SelectionChanged="AvaiableDocumentsList_SelectionChanged"/>
<Label x:Name="DocumentFooterLabel" Content="Fußtext:" Margin="10,40,0,10"/> <Button x:Name="DocumentAddButton" Content="&#xF0AF;" FontFamily="Segoe MDL2 Assets" FontSize="14"
<TextBox x:Name="DeliveryConfirmationFooterInput" Grid.Column="1" Grid.Column="1" Margin="0,0,0,30" VerticalAlignment="Center" Height="25" IsEnabled="False"
Margin="0,40,10,10" Height="Auto" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Click="DocumentAddButton_Click"/>
AcceptsReturn="True" VerticalScrollBarVisibility="Visible" TextWrapping="Wrap" <Button x:Name="DocumentRemoveButton" Content="&#xE74D;" FontFamily="Segoe MDL2 Assets" FontSize="16" Padding="1.5,0,0,0"
TextChanged="DocumentInput_TextChanged"/> Grid.Column="1" Margin="0,30,0,0" VerticalAlignment="Center" Height="25" IsEnabled="False"
<TextBox x:Name="CreditNoteFooterInput" Grid.Column="1" Click="DocumentRemoveButton_Click"/>
Margin="0,10,10,10" Height="Auto" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
AcceptsReturn="True" VerticalScrollBarVisibility="Visible" TextWrapping="Wrap" <Label Content="Ausgewählte Dokumente"
TextChanged="DocumentInput_TextChanged"/> Grid.Column="2" Margin="10,8,10,10"/>
<ListBox x:Name="SelectedDocumentsList" DisplayMemberPath="Name"
Grid.Column="2" Margin="10,30,10,37"
SelectionChanged="SelectedDocumentsList_SelectionChanged">
<ListBox.InputBindings>
<KeyBinding Key="Delete" Command="{Binding Path=DeleteCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>
</ListBox.InputBindings>
</ListBox>
<Button x:Name="SelectDocumentButton" Content="Durchsuchen..."
Grid.Column="2" VerticalAlignment="Bottom" Margin="10,10,10,10" Height="22"
Click="SelectDocumentButton_Click"/>
</Grid> </Grid>
</GroupBox>
<TextBox x:Name="PostalLocation" Grid.Column="1" TextChanged="PostalLocation_TextChanged" <GroupBox x:Name="DocumentBox" Header="Dokument" Margin="10,170,10,47" HorizontalAlignment="Stretch">
Margin="10,30,10,10" Width="120" HorizontalAlignment="Left"/> <Grid>
<Label Content=", am" Margin="130,30,10,10" FontSize="14" Grid.Column="1"/> <Grid.ColumnDefinitions>
<TextBox x:Name="PostalDate" Grid.Column="1" Text="01.01.2020" <ColumnDefinition Width="70"/>
Margin="162,30,10,10" Width="78" HorizontalAlignment="Left" <ColumnDefinition Width="*"/>
TextChanged="Date_TextChanged" LostFocus="Date_LostFocus"/> </Grid.ColumnDefinitions>
<GroupBox Header="Adressaten" Margin="10,70,10,47" Grid.Column="1"> <CheckBox x:Name="DocumentNonDeliverersInput" Content="Auch Nicht-Lieferanten miteinbeziehen"
<Grid> Margin="10,10,10,10" Grid.Column="1"/>
<RadioButton GroupName="Recipients" x:Name="RecipientsActiveMembersInput" Content="aktive Mitglieder"
Margin="10,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<RadioButton GroupName="Recipients" x:Name="RecipientsAreaComMembersInput" Content="Mitglieder mit Flächenbindung"
Margin="10,30,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<RadioButton GroupName="Recipients" x:Name="RecipientsDeliveryAncmtMembersInput" Content="Mitglieder mit Anmeldung"
Margin="10,50,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<RadioButton GroupName="Recipients" x:Name="RecipientsDeliveryMembersInput" Content="Lieferanten der Saison"
Margin="10,70,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<RadioButton GroupName="Recipients" x:Name="RecipientsNonDeliveryMembersInput" Content="Nicht-Lieferanten der Saison"
Margin="10,90,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<RadioButton GroupName="Recipients" x:Name="RecipientsCustomInput" Content="Benutzerdefiniert"
Margin="10,110,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<Label Content="Zwst.:" x:Name="MemberBranchLabel" Margin="10,140,0,10"/> <Label x:Name="DocumentFooterLabel" Content="Fußtext:" Margin="10,40,0,10"/>
<ctrl:CheckComboBox x:Name="MemberBranchInput" AllItemsSelectedContent="Alle Stammzweigstellen" Delimiter=", " DisplayMemberPath="Name" <TextBox x:Name="DeliveryConfirmationFooterInput" Grid.Column="1"
Margin="50,140,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25" Margin="0,40,10,10" Height="Auto" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
SelectionChanged="MemberInput_SelectionChanged"/> AcceptsReturn="True" VerticalScrollBarVisibility="Visible" TextWrapping="Wrap"
TextChanged="DocumentInput_TextChanged"/>
<TextBox x:Name="CreditNoteFooterInput" Grid.Column="1"
Margin="0,10,10,10" Height="Auto" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
AcceptsReturn="True" VerticalScrollBarVisibility="Visible" TextWrapping="Wrap"
TextChanged="DocumentInput_TextChanged"/>
</Grid>
</GroupBox>
<Label Content="Gem.:" x:Name="MemberKgLabel" Margin="10,170,0,10"/> <TextBox x:Name="PostalLocation" Grid.Column="1" TextChanged="PostalLocation_TextChanged"
<ctrl:CheckComboBox x:Name="MemberKgInput" AllItemsSelectedContent="Alle Stammgemeinden" Delimiter=", " DisplayMemberPath="Name" Margin="10,30,10,10" Width="120" HorizontalAlignment="Left"/>
IsSelectAllActive="True" SelectAllContent="Alle Stammgemeinden" <Label Content=", am" Margin="130,30,10,10" FontSize="14" Grid.Column="1"/>
Margin="50,170,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25" <TextBox x:Name="PostalDate" Grid.Column="1" Text="01.01.2020"
SelectionChanged="MemberInput_SelectionChanged"/> Margin="162,30,10,10" Width="78" HorizontalAlignment="Left"
TextChanged="Date_TextChanged" LostFocus="Date_LostFocus"/>
<Label Content="Bio-Betrieb:" x:Name="MemberOrganicLabel" Margin="10,200,0,10"/> <GroupBox Header="Adressaten" Margin="10,70,10,47" Grid.Column="1">
<RadioButton x:Name="MemberOrganicYesInput" Content="Ja" GroupName="MemberOrganic" <Grid>
Margin="80,205,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" <RadioButton GroupName="Recipients" x:Name="RecipientsActiveMembersInput" Content="aktive Mitglieder"
Checked="MemberInput_Checked"/> Margin="10,10,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
<RadioButton x:Name="MemberOrganicNoInput" Content="Nein" GroupName="MemberOrganic" Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
Margin="125,205,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" <RadioButton GroupName="Recipients" x:Name="RecipientsAreaComMembersInput" Content="Mitglieder mit Flächenbindung"
Checked="MemberInput_Checked"/> Margin="10,30,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
<RadioButton x:Name="MemberOrganicIndifferentInput" Content="Egal" GroupName="MemberOrganic" Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
Margin="180,205,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" <RadioButton GroupName="Recipients" x:Name="RecipientsDeliveryAncmtMembersInput" Content="Mitglieder mit Anmeldung"
Checked="MemberInput_Checked"/> Margin="10,50,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<RadioButton GroupName="Recipients" x:Name="RecipientsDeliveryMembersInput" Content="Lieferanten der Saison"
Margin="10,70,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<RadioButton GroupName="Recipients" x:Name="RecipientsNonDeliveryMembersInput" Content="Nicht-Lieferanten der Saison"
Margin="10,90,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<RadioButton GroupName="Recipients" x:Name="RecipientsCustomInput" Content="Benutzerdefiniert"
Margin="10,110,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="RecipientsInput_Changed" Unchecked="RecipientsInput_Changed"/>
<Label Content="Funktionär:" x:Name="MemberFunktionärLabel" Margin="10,230,0,10"/> <Label Content="Zwst.:" x:Name="MemberBranchLabel" Margin="10,140,0,10"/>
<RadioButton x:Name="MemberFunktionärYesInput" Content="Ja" GroupName="MemberFunktionär" <ctrl:CheckComboBox x:Name="MemberBranchInput" AllItemsSelectedContent="Alle Stammzweigstellen" Delimiter=", " DisplayMemberPath="Name"
Margin="80,235,10,10" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="50,140,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25"
Checked="MemberInput_Checked"/> SelectionChanged="MemberInput_SelectionChanged"/>
<RadioButton x:Name="MemberFunktionärNoInput" Content="Nein" GroupName="MemberFunktionär"
Margin="125,235,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="MemberInput_Checked"/>
<RadioButton x:Name="MemberFunktionärIndifferentInput" Content="Egal" GroupName="MemberFunktionär"
Margin="180,235,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="MemberInput_Checked"/>
<Label Content="Vtrg.:" x:Name="MemberAreaComLabel" Margin="10,260,0,10"/> <Label Content="Gem.:" x:Name="MemberKgLabel" Margin="10,170,0,10"/>
<ctrl:CheckComboBox x:Name="MemberAreaComInput" AllItemsSelectedContent="Alle Vertragsarten" Delimiter=", " DisplayMemberPath="VtrgId" <ctrl:CheckComboBox x:Name="MemberKgInput" AllItemsSelectedContent="Alle Stammgemeinden" Delimiter=", " DisplayMemberPath="Name"
IsSelectAllActive="True" SelectAllContent="Alle Vertragsarten" IsSelectAllActive="True" SelectAllContent="Alle Stammgemeinden"
Margin="50,260,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25" Margin="50,170,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25"
SelectionChanged="MemberInput_SelectionChanged"/> SelectionChanged="MemberInput_SelectionChanged"/>
<Label Content="Tag:" x:Name="MemberDeliveryAncmtLabel" Margin="10,260,0,10"/> <Label Content="Bio-Betrieb:" x:Name="MemberOrganicLabel" Margin="10,200,0,10"/>
<ctrl:CheckComboBox x:Name="MemberDeliveryAncmtInput" AllItemsSelectedContent="Alle Lesepläne" Delimiter=", " DisplayMemberPath="Identifier" <RadioButton x:Name="MemberOrganicYesInput" Content="Ja" GroupName="MemberOrganic"
IsSelectAllActive="True" SelectAllContent="Alle Lesepläne" Margin="80,205,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Margin="50,260,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25" Checked="MemberInput_Checked"/>
SelectionChanged="MemberInput_SelectionChanged"/> <RadioButton x:Name="MemberOrganicNoInput" Content="Nein" GroupName="MemberOrganic"
Margin="125,205,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="MemberInput_Checked"/>
<RadioButton x:Name="MemberOrganicIndifferentInput" Content="Egal" GroupName="MemberOrganic"
Margin="180,205,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="MemberInput_Checked"/>
<ctrl:CheckComboBox x:Name="MemberCustomInput" AllItemsSelectedContent="Alle Mitglieder" Delimiter=", " DisplayMemberPath="AdministrativeName" <Label Content="Funktionär:" x:Name="MemberFunktionärLabel" Margin="10,230,0,10"/>
IsSelectAllActive="True" SelectAllContent="Alle Mitglieder" <RadioButton x:Name="MemberFunktionärYesInput" Content="Ja" GroupName="MemberFunktionär"
Margin="10,140,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25" Margin="80,235,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
SelectionChanged="MemberInput_SelectionChanged"/> Checked="MemberInput_Checked"/>
</Grid> <RadioButton x:Name="MemberFunktionärNoInput" Content="Nein" GroupName="MemberFunktionär"
</GroupBox> Margin="125,235,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="MemberInput_Checked"/>
<RadioButton x:Name="MemberFunktionärIndifferentInput" Content="Egal" GroupName="MemberFunktionär"
Margin="180,235,10,10" VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="MemberInput_Checked"/>
<Button x:Name="ContinueButton" Content="Weiter" Grid.Column="1" <Label Content="Vtrg.:" x:Name="MemberAreaComLabel" Margin="10,260,0,10"/>
Margin="10,10,10,10" Height="27" Width="100" Padding="9,3" FontSize="14" <ctrl:CheckComboBox x:Name="MemberAreaComInput" AllItemsSelectedContent="Alle Vertragsarten" Delimiter=", " DisplayMemberPath="VtrgId"
VerticalAlignment="Bottom" HorizontalAlignment="Right" IsSelectAllActive="True" SelectAllContent="Alle Vertragsarten"
Click="ContinueButton_Click"/> Margin="50,260,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25"
</Grid> SelectionChanged="MemberInput_SelectionChanged"/>
</TabItem>
<TabItem Header="Absenden" Visibility="Collapsed"> <Label Content="Tag:" x:Name="MemberDeliveryAncmtLabel" Margin="10,260,0,10"/>
<Grid> <ctrl:CheckComboBox x:Name="MemberDeliveryAncmtInput" AllItemsSelectedContent="Alle Lesepläne" Delimiter=", " DisplayMemberPath="Identifier"
<Grid.ColumnDefinitions> IsSelectAllActive="True" SelectAllContent="Alle Lesepläne"
<ColumnDefinition Width="*"/> Margin="50,260,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25"
<ColumnDefinition Width="1.5*"/> SelectionChanged="MemberInput_SelectionChanged"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="80"/>
</Grid.RowDefinitions>
<GroupBox Header="Post" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10,10,5,10" Grid.Column="0"> <ctrl:CheckComboBox x:Name="MemberCustomInput" AllItemsSelectedContent="Alle Mitglieder" Delimiter=", " DisplayMemberPath="AdministrativeName"
<Grid> IsSelectAllActive="True" SelectAllContent="Alle Mitglieder"
<GroupBox Header="Zusenden an..." Margin="10,10,10,10" Height="150" Width="220" VerticalAlignment="Top" HorizontalAlignment="Left"> Margin="10,140,10,10" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="25"
<StackPanel> SelectionChanged="MemberInput_SelectionChanged"/>
<RadioButton x:Name="PostalAllInput" Margin="10,10,10,2.5" Click="PostalInput_Changed"> </Grid>
<TextBlock> </GroupBox>
...alle (<Run Text="{Binding Path=PostalAllCount, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>)
</TextBlock>
</RadioButton>
<RadioButton x:Name="PostalWishInput" Margin="10,2.5,10,2.5" Click="PostalInput_Changed" IsChecked="True">
<TextBlock>
...Mitglieder, die Zusendung<LineBreak/>
per Post wünschen (<Run Text="{Binding Path=PostalWishCount, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>)
</TextBlock>
</RadioButton>
<RadioButton x:Name="PostalNoEmailInput" Margin="10,2.5,10,2.5" Click="PostalInput_Changed">
<TextBlock>
...Mitglieder, die keine<LineBreak/>
E-Mail erhalten würden (<Run Text="{Binding Path=PostalNoEmailCount, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>)
</TextBlock>
</RadioButton>
<RadioButton x:Name="PostalNobodyInput" Margin="10,2.5,10,10" Content="...niemanden (0)" Click="PostalInput_Changed"/>
</StackPanel>
</GroupBox>
<GroupBox Header="Sortieren nach" Margin="10,180,10,10" Width="180" Height="80" VerticalAlignment="Top" HorizontalAlignment="Left"> <Button x:Name="ContinueButton" Content="Weiter" Grid.Column="1"
<StackPanel Margin="5,5,0,5"> Margin="10,10,10,10" Height="27" Width="100" Padding="9,3" FontSize="14"
<RadioButton GroupName="Order" x:Name="OrderMgNrInput" Content="Mitgliedsnummer" Click="OrderInput_Changed" IsChecked="True"/> VerticalAlignment="Bottom" HorizontalAlignment="Right"
<RadioButton GroupName="Order" x:Name="OrderNameInput" Content="Name" Click="OrderInput_Changed"/> Click="ContinueButton_Click"/>
<RadioButton GroupName="Order" x:Name="OrderPlzInput" Content="PLZ, Ort, Name" Click="OrderInput_Changed"/> </Grid>
</StackPanel> </TabItem>
</GroupBox>
<CheckBox x:Name="DoublePagedInput" Margin="20,270,10,10" Content="Doppelseitig drucken" <TabItem Header="Absenden" Visibility="Collapsed">
VerticalAlignment="Top" HorizontalAlignment="Left" <Grid>
Checked="DoublePagedInput_Changed" Unchecked="DoublePagedInput_Changed"/>
<TextBox x:Name="PostalSender1" TextChanged="PostalSender_TextChanged" IsEnabled="False"
Margin="10,300,10,10"/>
<TextBox x:Name="PostalSender2" TextChanged="PostalSender_TextChanged"
Margin="10,330,10,10"/>
</Grid>
</GroupBox>
<GroupBox Header="E-Mail" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5,10,10,10" Grid.Column="1">
<Grid>
<GroupBox Header="Zusenden an..." Margin="80,10,10,10" Width="220" Height="110" VerticalAlignment="Top" HorizontalAlignment="Left">
<StackPanel>
<RadioButton x:Name="EmailAllInput" Margin="10,10,10,2.5" Checked="EmailInput_Changed">
<TextBlock>
...alle mit E-Mail-Adressen (<Run Text="{Binding Path=EmailAllCount, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>)
</TextBlock>
</RadioButton>
<RadioButton x:Name="EmailWishInput" Margin="10,2.5,10,2.5" IsChecked="True" Checked="EmailInput_Changed">
<TextBlock>
...Mitglieder, die Zusendung<LineBreak/>
per E-Mail wünschen (<Run Text="{Binding Path=EmailWishCount, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>)
</TextBlock>
</RadioButton>
<RadioButton x:Name="EmailNobodyInput" Margin="10,2.5,10,10" Content="...niemanden (0)" Checked="EmailInput_Changed"/>
</StackPanel>
</GroupBox>
<Label Content="Betreff:" Margin="10,130,10,10"/>
<TextBox x:Name="EmailSubjectInput" Margin="80,130,10,10"/>
<Label Content="Nachricht:" Margin="10,160,10,10"/>
<TextBox x:Name="EmailBodyInput"
Margin="80,160,10,10" VerticalAlignment="Stretch" Height="Auto"
TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" AcceptsReturn="True"/>
</Grid>
</GroupBox>
<Grid Grid.Row="1" Grid.ColumnSpan="2" Width="400" Height="59">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="0.7*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="10"/> <ColumnDefinition Width="1.5*"/>
<ColumnDefinition Width="0.4*"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="0.6*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="5"/> <RowDefinition Height="80"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Button x:Name="GenerateButton" Content="Generieren" <GroupBox Header="Post" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10,10,5,10" Grid.Column="0">
Grid.Row="0" Grid.Column="0" FontSize="14" <Grid>
Click="GenerateButton_Click"/> <GroupBox Header="Zusenden an..." Margin="10,10,10,10" Height="150" Width="220" VerticalAlignment="Top" HorizontalAlignment="Left">
<ProgressBar x:Name="ProgressBar" <StackPanel>
Grid.Row="2" Grid.Column="0" SnapsToDevicePixels="True"/> <RadioButton x:Name="PostalAllInput" Margin="10,10,10,2.5" Click="PostalInput_Changed">
<TextBlock>
...alle (<Run Text="{Binding Path=PostalAllCount, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>)
</TextBlock>
</RadioButton>
<RadioButton x:Name="PostalWishInput" Margin="10,2.5,10,2.5" Click="PostalInput_Changed" IsChecked="True">
<TextBlock>
...Mitglieder, die Zusendung<LineBreak/>
per Post wünschen (<Run Text="{Binding Path=PostalWishCount, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>)
</TextBlock>
</RadioButton>
<RadioButton x:Name="PostalNoEmailInput" Margin="10,2.5,10,2.5" Click="PostalInput_Changed">
<TextBlock>
...Mitglieder, die keine<LineBreak/>
E-Mail erhalten würden (<Run Text="{Binding Path=PostalNoEmailCount, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>)
</TextBlock>
</RadioButton>
<RadioButton x:Name="PostalNobodyInput" Margin="10,2.5,10,10" Content="...niemanden (0)" Click="PostalInput_Changed"/>
</StackPanel>
</GroupBox>
<Button x:Name="PreviewButton" Content="Vorschau" IsEnabled="False" <GroupBox Header="Sortieren nach" Margin="10,180,10,10" Width="180" Height="80" VerticalAlignment="Top" HorizontalAlignment="Left">
Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="3" FontSize="14" <StackPanel Margin="5,5,0,5">
Click="PreviewButton_Click"/> <RadioButton GroupName="Order" x:Name="OrderMgNrInput" Content="Mitgliedsnummer" Click="OrderInput_Changed" IsChecked="True"/>
<Button x:Name="PrintButton" Content="Drucken" IsEnabled="False" <RadioButton GroupName="Order" x:Name="OrderNameInput" Content="Name" Click="OrderInput_Changed"/>
Grid.Row="2" Grid.Column="2" FontSize="14" <RadioButton GroupName="Order" x:Name="OrderPlzInput" Content="PLZ, Ort, Name" Click="OrderInput_Changed"/>
Click="PrintButton_Click"/> </StackPanel>
<Button x:Name="EmailButton" Content="E-Mails verschicken" IsEnabled="False" </GroupBox>
Grid.Row="2" Grid.Column="4" FontSize="14"
Click="EmailButton_Click"/> <CheckBox x:Name="DoublePagedInput" Margin="20,270,10,10" Content="Doppelseitig drucken"
VerticalAlignment="Top" HorizontalAlignment="Left"
Checked="DoublePagedInput_Changed" Unchecked="DoublePagedInput_Changed"/>
<TextBox x:Name="PostalSender1" TextChanged="PostalSender_TextChanged" IsEnabled="False"
Margin="10,300,10,10"/>
<TextBox x:Name="PostalSender2" TextChanged="PostalSender_TextChanged"
Margin="10,330,10,10"/>
</Grid>
</GroupBox>
<GroupBox Header="E-Mail" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5,10,10,10" Grid.Column="1">
<Grid>
<GroupBox Header="Zusenden an..." Margin="80,10,10,10" Width="220" Height="110" VerticalAlignment="Top" HorizontalAlignment="Left">
<StackPanel>
<RadioButton x:Name="EmailAllInput" Margin="10,10,10,2.5" Checked="EmailInput_Changed">
<TextBlock>
...alle mit E-Mail-Adressen (<Run Text="{Binding Path=EmailAllCount, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>)
</TextBlock>
</RadioButton>
<RadioButton x:Name="EmailWishInput" Margin="10,2.5,10,2.5" IsChecked="True" Checked="EmailInput_Changed">
<TextBlock>
...Mitglieder, die Zusendung<LineBreak/>
per E-Mail wünschen (<Run Text="{Binding Path=EmailWishCount, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MailWindow}}}"/>)
</TextBlock>
</RadioButton>
<RadioButton x:Name="EmailNobodyInput" Margin="10,2.5,10,10" Content="...niemanden (0)" Checked="EmailInput_Changed"/>
</StackPanel>
</GroupBox>
<Label Content="Betreff:" Margin="10,130,10,10"/>
<TextBox x:Name="EmailSubjectInput" Margin="80,130,10,10"/>
<Label Content="Nachricht:" Margin="10,160,10,10"/>
<TextBox x:Name="EmailBodyInput"
Margin="80,160,10,10" VerticalAlignment="Stretch" Height="Auto"
TextWrapping="Wrap" VerticalScrollBarVisibility="Visible" AcceptsReturn="True"/>
</Grid>
</GroupBox>
<Grid Grid.Row="1" Grid.ColumnSpan="2" Width="400" Height="59">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.7*"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="0.4*"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="0.6*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="5"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button x:Name="GenerateButton" Content="Generieren"
Grid.Row="0" Grid.Column="0" FontSize="14"
Click="GenerateButton_Click"/>
<ProgressBar x:Name="ProgressBar"
Grid.Row="2" Grid.Column="0" SnapsToDevicePixels="True"/>
<Button x:Name="PreviewButton" Content="Vorschau" IsEnabled="False"
Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="3" FontSize="14"
Click="PreviewButton_Click"/>
<Button x:Name="PrintButton" Content="Drucken" IsEnabled="False"
Grid.Row="2" Grid.Column="2" FontSize="14"
Click="PrintButton_Click"/>
<Button x:Name="EmailButton" Content="E-Mails verschicken" IsEnabled="False"
Grid.Row="2" Grid.Column="4" FontSize="14"
Click="EmailButton_Click"/>
</Grid>
<Button x:Name="BackButton" Content="Zurück" Grid.Row="1"
Margin="10,10,10,10" Height="27" Width="100" Padding="9,3" FontSize="14"
VerticalAlignment="Bottom" HorizontalAlignment="Left"
Click="BackButton_Click"/>
</Grid> </Grid>
</TabItem>
</TabControl>
<Button x:Name="BackButton" Content="Zurück" Grid.Row="1" <StatusBar Grid.Row="2" BorderThickness="0,1,0,0" BorderBrush="Gray">
Margin="10,10,10,10" Height="27" Width="100" Padding="9,3" FontSize="14" <StatusBar.ItemsPanel>
VerticalAlignment="Bottom" HorizontalAlignment="Left" <ItemsPanelTemplate>
Click="BackButton_Click"/> <Grid>
</Grid> <Grid.ColumnDefinitions>
</TabItem> <ColumnDefinition Width="1*"/>
</TabControl> <ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem>
<TextBlock>
Adressaten: <Run x:Name="StatusRecipients" Text="0"/>
</TextBlock>
</StatusBarItem>
<Separator Grid.Column="1"/>
<StatusBarItem Grid.Column="2">
<TextBlock>
Adressaten (Post): <Run x:Name="StatusPostalRecipients" Text="0"/>
</TextBlock>
</StatusBarItem>
<Separator Grid.Column="3"/>
<StatusBarItem Grid.Column="4">
<TextBlock>
Adressaten (E-Mail): <Run x:Name="StatusEmailRecipients" Text="0"/>
</TextBlock>
</StatusBarItem>
</StatusBar>
</Grid>
</local:ContextWindow> </local:ContextWindow>

View File

@ -58,6 +58,7 @@ namespace Elwig.Windows {
public IEnumerable<Member> Recipients = []; public IEnumerable<Member> Recipients = [];
protected Document? PrintDocument; protected Document? PrintDocument;
protected Dictionary<Member, List<Document>>? PrintMemberDocuments;
protected Dictionary<Member, List<Document>>? EmailDocuments; protected Dictionary<Member, List<Document>>? EmailDocuments;
public static readonly DependencyProperty PostalAllCountProperty = DependencyProperty.Register(nameof(PostalAllCount), typeof(int), typeof(MailWindow)); public static readonly DependencyProperty PostalAllCountProperty = DependencyProperty.Register(nameof(PostalAllCount), typeof(int), typeof(MailWindow));
@ -122,6 +123,26 @@ namespace Elwig.Windows {
DeliveryConfirmationFooterInput.Text = App.Client.TextDeliveryConfirmation; DeliveryConfirmationFooterInput.Text = App.Client.TextDeliveryConfirmation;
CreditNoteFooterInput.Text = App.Client.TextCreditNote; CreditNoteFooterInput.Text = App.Client.TextCreditNote;
DocumentNonDeliverersInput.IsChecked = App.Client.MailIncludeNonDeliverers;
DoublePagedInput.IsChecked = App.Client.MailDoublePaged;
switch (App.Client.MailSendPostal) {
case 0: PostalNobodyInput.IsChecked = true; break;
case 1: PostalNoEmailInput.IsChecked = true; break;
case 2: PostalWishInput.IsChecked = true; break;
case 3: PostalAllInput.IsChecked = true; break;
}
switch (App.Client.MailSendEmail) {
case 0: EmailNobodyInput.IsChecked = true; break;
case 1: EmailWishInput.IsChecked = true; break;
case 2: EmailAllInput.IsChecked = true; break;
}
switch (App.Client.MailOrdering) {
case 0: OrderMgNrInput.IsChecked = true; break;
case 1: OrderNameInput.IsChecked = true; break;
case 2: OrderPlzInput.IsChecked = true; break;
}
PostalSender1.Text = App.Client.Sender1; PostalSender1.Text = App.Client.Sender1;
PostalSender2.Text = App.Client.Sender2; PostalSender2.Text = App.Client.Sender2;
PostalLocation.Text = App.BranchLocation; PostalLocation.Text = App.BranchLocation;
@ -242,6 +263,11 @@ namespace Elwig.Windows {
rb.IsEnabled = true; rb.IsEnabled = true;
} }
private void Menu_Help_Log_Click(object sender, RoutedEventArgs evt) {
var w = new MailLogWindow();
w.Show();
}
private void ContinueButton_Click(object sender, RoutedEventArgs evt) { private void ContinueButton_Click(object sender, RoutedEventArgs evt) {
TabControl.SelectedIndex = 1; TabControl.SelectedIndex = 1;
TabControl.AllowDrop = false; TabControl.AllowDrop = false;
@ -312,7 +338,7 @@ namespace Elwig.Windows {
if (idx == 0) { if (idx == 0) {
SelectedDocs.Add(new(DocType.MemberDataSheet, s, null)); SelectedDocs.Add(new(DocType.MemberDataSheet, s, null));
} else if (idx == 1) { } else if (idx == 1) {
SelectedDocs.Add(new(DocType.DeliveryConfirmation, s, (Year, DocumentNonDeliverersInput.IsChecked == true))); SelectedDocs.Add(new(DocType.DeliveryConfirmation, s, Year));
RecipientsDeliveryMembersInput.IsChecked = true; RecipientsDeliveryMembersInput.IsChecked = true;
} else if (idx >= 2) { } else if (idx >= 2) {
var name = s.Split(" ")[^1]; var name = s.Split(" ")[^1];
@ -449,18 +475,32 @@ namespace Elwig.Windows {
} }
private void UpdatePostalEmailRecipients() { private void UpdatePostalEmailRecipients() {
var modeEmail = EmailAllInput.IsChecked == true ? 2 : EmailWishInput.IsChecked == true ? 1 : 0;
var modePostal = PostalAllInput.IsChecked == true ? 3 : PostalWishInput.IsChecked == true ? 2 : PostalNoEmailInput.IsChecked == true ? 1 : 0;
EmailAllCount = Recipients.Count(m => m.EmailAddresses.Count > 0); EmailAllCount = Recipients.Count(m => m.EmailAddresses.Count > 0);
EmailWishCount = Recipients.Count(m => m.EmailAddresses.Count > 0 && m.ContactViaEmail); EmailWishCount = Recipients.Count(m => m.EmailAddresses.Count > 0 && m.ContactViaEmail);
PostalAllCount = Recipients.Count(); PostalAllCount = Recipients.Count();
PostalWishCount = Recipients.Count(m => m.ContactViaPost); PostalWishCount = Recipients.Count(m => m.ContactViaPost);
var m = EmailAllInput.IsChecked == true ? 3 : EmailWishInput.IsChecked == true ? 2 : 1; var countEmail = (modeEmail == 2 ? EmailAllCount : modeEmail == 1 ? EmailWishCount : 0);
PostalNoEmailCount = PostalAllCount - (m == 3 ? EmailAllCount : m == 2 ? EmailWishCount : 0); PostalNoEmailCount = PostalAllCount - countEmail;
var countPostal = (modePostal == 3 ? PostalAllCount : modePostal == 2 ? PostalWishCount : modePostal == 1 ? PostalNoEmailCount : 0);
if (IsLoaded) {
StatusRecipients.Text = $"{Recipients.Count():N0}";
StatusPostalRecipients.Text = $"{countPostal:N0}";
StatusEmailRecipients.Text = $"{countEmail:N0}";
}
ResetDocuments(); ResetDocuments();
} }
private async Task UpdateTextParameters() { private async Task UpdateClientParameters() {
var changed = false; var changed = false;
var dcInclude = DocumentNonDeliverersInput.IsChecked ?? false;
if (dcInclude != App.Client.MailIncludeNonDeliverers) {
App.Client.MailIncludeNonDeliverers = dcInclude;
changed = true;
}
var dcText = DeliveryConfirmationFooterInput.Text.Trim(); var dcText = DeliveryConfirmationFooterInput.Text.Trim();
if (dcText.Length == 0) dcText = null; if (dcText.Length == 0) dcText = null;
if (dcText != App.Client.TextDeliveryConfirmation) { if (dcText != App.Client.TextDeliveryConfirmation) {
@ -475,6 +515,53 @@ namespace Elwig.Windows {
changed = true; changed = true;
} }
var sendPostal = 0;
if (PostalAllInput.IsChecked ?? false) {
sendPostal = 3;
} else if (PostalWishInput.IsChecked ?? false) {
sendPostal = 2;
} else if (PostalNoEmailInput.IsChecked ?? false) {
sendPostal = 1;
}
if (sendPostal != App.Client.MailSendPostal) {
App.Client.MailSendPostal = sendPostal;
changed = true;
}
var sendEmail = 0;
if (EmailAllInput.IsChecked ?? false) {
sendEmail = 2;
} else if (EmailWishInput.IsChecked ?? false) {
sendEmail = 1;
}
if (sendEmail != App.Client.MailSendEmail) {
App.Client.MailSendEmail = sendEmail;
changed = true;
}
var ordering = 0;
if (OrderNameInput.IsChecked ?? false) {
ordering = 1;
} else if (OrderPlzInput.IsChecked ?? false) {
ordering = 2;
}
if (ordering != App.Client.MailOrdering) {
App.Client.MailOrdering = ordering;
changed = true;
}
var mailDoublePaged = DoublePagedInput.IsChecked ?? false;
if (mailDoublePaged != App.Client.MailDoublePaged) {
App.Client.MailDoublePaged = mailDoublePaged;
changed = true;
}
var sender2 = PostalSender2.Text.Trim();
if (sender2 != App.Client.Sender2) {
App.Client.Sender2 = sender2;
changed = true;
}
var emailSubject = EmailSubjectInput.Text.Trim(); var emailSubject = EmailSubjectInput.Text.Trim();
if (emailSubject.Length == 0) emailSubject = null; if (emailSubject.Length == 0) emailSubject = null;
if (emailSubject != App.Client.TextEmailSubject) { if (emailSubject != App.Client.TextEmailSubject) {
@ -519,7 +606,7 @@ namespace Elwig.Windows {
GenerateButton.IsEnabled = false; GenerateButton.IsEnabled = false;
DisposeDocs(); DisposeDocs();
await UpdateTextParameters(); await UpdateClientParameters();
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
@ -562,8 +649,7 @@ namespace Elwig.Windows {
Dictionary<(int, int), (IDictionary<int, CreditNoteDeliveryData>, IDictionary<int, PaymentMember>, BillingData)> cnData = []; Dictionary<(int, int), (IDictionary<int, CreditNoteDeliveryData>, IDictionary<int, PaymentMember>, BillingData)> cnData = [];
foreach (var doc in docs) { foreach (var doc in docs) {
if (doc.Type == DocType.DeliveryConfirmation) { if (doc.Type == DocType.DeliveryConfirmation) {
var details = ((int, bool))doc.Details!; var year = (int)doc.Details!;
var year = details.Item1;
try { try {
var b = new Billing(year); var b = new Billing(year);
@ -610,13 +696,11 @@ namespace Elwig.Windows {
} else if (doc.Type == DocType.MemberDataSheet) { } else if (doc.Type == DocType.MemberDataSheet) {
return [new GeneratedDoc(new MemberDataSheet(m, ctx) { Date = postalDate })]; return [new GeneratedDoc(new MemberDataSheet(m, ctx) { Date = postalDate })];
} else if (doc.Type == DocType.DeliveryConfirmation) { } else if (doc.Type == DocType.DeliveryConfirmation) {
var details = ((int, bool))doc.Details!; var year = (int)doc.Details!;
var year = details.Item1;
var include = details.Item2;
DeliveryConfirmationDeliveryData data; DeliveryConfirmationDeliveryData data;
if (dcData[year].TryGetValue(m.MgNr, out var d)) { if (dcData[year].TryGetValue(m.MgNr, out var d)) {
data = d; data = d;
} else if (include) { } else if (App.Client.MailIncludeNonDeliverers) {
data = DeliveryConfirmationDeliveryData.CreateEmpty(year, m); data = DeliveryConfirmationDeliveryData.CreateEmpty(year, m);
} else { } else {
return []; return [];
@ -691,11 +775,13 @@ namespace Elwig.Windows {
EmailDocuments = email; EmailDocuments = email;
} }
var printDocs = memberDocs var printMemberDocs = memberDocs
.Where(d => .Where(d =>
printMode == 3 || printMode == 3 ||
(printMode == 2 && d.Member.ContactViaPost) || (printMode == 2 && d.Member.ContactViaPost) ||
(printMode == 1 && !emailRecipients.Contains(d.Member.MgNr))) (printMode == 1 && !emailRecipients.Contains(d.Member.MgNr)))
.ToList();
var printDocs = printMemberDocs
.SelectMany(m => { .SelectMany(m => {
var docs = m.Docs.Select(d => d.Doc).ToList(); var docs = m.Docs.Select(d => d.Doc).ToList();
if (docs.Count == 0 || m.Docs[0].Type == DocType.Custom) { if (docs.Count == 0 || m.Docs[0].Type == DocType.Custom) {
@ -720,6 +806,7 @@ namespace Elwig.Windows {
ProgressBar.Value = 100.0 * emailNum / totalNum + v * printNum / totalNum; ProgressBar.Value = 100.0 * emailNum / totalNum + v * printNum / totalNum;
})); }));
PrintDocument = print; PrintDocument = print;
PrintMemberDocuments = printMemberDocs.ToDictionary(m => m.Member, m => m.Docs.Select(d => d.Doc).ToList());
} catch (Exception exc) { } catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
UnlockInputs(); UnlockInputs();
@ -727,6 +814,9 @@ namespace Elwig.Windows {
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
return; return;
} }
} else {
PrintDocument = null;
PrintMemberDocuments = null;
} }
ProgressBar.Value = 100.0; ProgressBar.Value = 100.0;
@ -763,7 +853,7 @@ namespace Elwig.Windows {
} }
private async void PrintButton_Click(object sender, RoutedEventArgs evt) { private async void PrintButton_Click(object sender, RoutedEventArgs evt) {
if (PrintDocument == null) return; if (PrintDocument == null || PrintMemberDocuments == null) return;
PrintButton.IsEnabled = false; PrintButton.IsEnabled = false;
GenerateButton.IsEnabled = false; GenerateButton.IsEnabled = false;
@ -777,6 +867,14 @@ namespace Elwig.Windows {
PrintDocument.Show(); PrintDocument.Show();
} else { } else {
await PrintDocument.Print(); await PrintDocument.Print();
await Utils.AddSentMails(
PrintMemberDocuments.Select(d => (
"postal", d.Key.MgNr, d.Key.AdministrativeName,
new string[] { d.Value.Select(d => (d as BusinessDocument)?.Address).FirstOrDefault(a => a != null) ?? d.Key.FullAddress },
d.Value.Select(d => d.Title).FirstOrDefault("Briefkopf"),
d.Value.Select(d => d.Title).ToArray()
))
);
} }
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
} }
@ -808,6 +906,7 @@ namespace Elwig.Windows {
Mouse.OverrideCursor = Cursors.AppStarting; Mouse.OverrideCursor = Cursors.AppStarting;
var subject = EmailSubjectInput.Text; var subject = EmailSubjectInput.Text;
var text = EmailBodyInput.Text; var text = EmailBodyInput.Text;
await Utils.AddSentMailBody(subject, text, EmailDocuments.Count);
foreach (var (m, docs) in EmailDocuments) { foreach (var (m, docs) in EmailDocuments) {
using var msg = new MimeMessage(); using var msg = new MimeMessage();
msg.From.Add(new MailboxAddress(App.Client.NameFull, App.Config.Smtp.Value.From)); msg.From.Add(new MailboxAddress(App.Client.NameFull, App.Config.Smtp.Value.From));
@ -822,6 +921,12 @@ namespace Elwig.Windows {
} }
msg.Body = body; msg.Body = body;
await client!.SendAsync(msg); await client!.SendAsync(msg);
await Utils.AddSentMails([(
"email", m.MgNr, m.AdministrativeName,
m.EmailAddresses.OrderBy(a => a.Nr).Select(a => a.Address).ToArray(),
subject,
docs.Select(d => d.Title).ToArray()
)]);
} }
MessageBox.Show("Erfolgreich alle E-Mails verschickt!", "Rundschreiben verschicken", MessageBoxButton.OK, MessageBoxImage.Information); MessageBox.Show("Erfolgreich alle E-Mails verschickt!", "Rundschreiben verschicken", MessageBoxButton.OK, MessageBoxImage.Information);
@ -870,7 +975,7 @@ namespace Elwig.Windows {
} }
private void PostalInput_Changed(object sender, RoutedEventArgs evt) { private void PostalInput_Changed(object sender, RoutedEventArgs evt) {
ResetDocuments(); UpdatePostalEmailRecipients();
} }
private void OrderInput_Changed(object sender, RoutedEventArgs evt) { private void OrderInput_Changed(object sender, RoutedEventArgs evt) {

View File

@ -10,6 +10,7 @@
<Style TargetType="Button"> <Style TargetType="Button">
<Setter Property="VerticalAlignment" Value="Top"/> <Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="HorizontalAlignment" Value="Center"/> <Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="FontSize" Value="14"/> <Setter Property="FontSize" Value="14"/>
<Setter Property="Padding" Value="9,3"/> <Setter Property="Padding" Value="9,3"/>
<Setter Property="Height" Value="35"/> <Setter Property="Height" Value="35"/>
@ -19,23 +20,67 @@
<Grid> <Grid>
<Menu BorderThickness="0,0,0,1" VerticalAlignment="Top" Height="19" BorderBrush="LightGray" Background="White"> <Menu BorderThickness="0,0,0,1" VerticalAlignment="Top" Height="19" BorderBrush="LightGray" Background="White">
<MenuItem Header="Datenbank"> <MenuItem Header="Datenbank">
<MenuItem Header="Daten exportieren..." Click="Menu_Database_Export_Click" IsEnabled="False"/> <MenuItem Header="Daten exportieren..." Click="Menu_Database_Export_Click" IsEnabled="False">
<MenuItem Header="Daten importieren..." Click="Menu_Database_Import_Click"/> <MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xEDE1;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Daten importieren..." Click="Menu_Database_Import_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE8B5;"/>
</MenuItem.Icon>
</MenuItem>
<Separator/> <Separator/>
<MenuItem Header="Abfragen stellen" Click="Menu_Database_Query_Click"/> <MenuItem Header="Abfragen stellen" Click="Menu_Database_Query_Click">
<MenuItem Header="Speicherort öffnen..." Click="Menu_Database_Open_Click"/> <MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE756;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Speicherort öffnen..." Click="Menu_Database_Open_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xED25;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="Waage"> <MenuItem Header="Waage">
<MenuItem Header="Datum und Uhrzeit setzen" Click="Menu_Scale_SetDateTime_Click"/> <MenuItem Header="Datum und Uhrzeit setzen" Click="Menu_Scale_SetDateTime_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xEC92;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
<MenuItem x:Name="HelpMenu" Header="Hilfe"> <MenuItem x:Name="HelpMenu" Header="Hilfe">
<MenuItem Header="Über"/> <MenuItem Header="Über">
<MenuItem x:Name="Menu_Help_Update" Header="Nach Updates suchen" Click="Menu_Help_Update_Click"/> <MenuItem.Icon>
<MenuItem x:Name="Menu_Help_Smtp" Header="E-Mail-Einstellungen testen" Click="Menu_Help_Smtp_Click"/> <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE946;"/>
<MenuItem x:Name="Menu_Help_Log" Header="Fehler-Protokoll anzeigen" Click="Menu_Help_Log_Click"/> </MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_Help_Update" Header="Nach Updates suchen" Click="Menu_Help_Update_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE895;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_Help_Smtp" Header="E-Mail-Einstellungen testen" Click="Menu_Help_Smtp_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE715;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_Help_Log" Header="Fehler-Protokoll anzeigen" Click="Menu_Help_Log_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xF168;"/>
</MenuItem.Icon>
</MenuItem>
<Separator/> <Separator/>
<MenuItem x:Name="Menu_Help_Config" Header="Konfigurationsdatei öffnen..." Click="Menu_Help_Config_Click"/> <MenuItem x:Name="Menu_Help_Config" Header="Konfigurationsdatei öffnen..." Click="Menu_Help_Config_Click">
<MenuItem x:Name="Menu_Help_Directory" Header="Konfigurationsspeicherort öffnen..." Click="Menu_Help_Directory_Click"/> <MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xEC7A;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_Help_Directory" Header="Konfigurationsspeicherort öffnen..." Click="Menu_Help_Directory_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xED25;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
</Menu> </Menu>
@ -58,26 +103,64 @@
</TextBlock> </TextBlock>
</Grid> </Grid>
<Button x:Name="MemberAdminButton" Content="Mitglieder" Click="MemberAdminButton_Click" <Button x:Name="MemberAdminButton" Click="MemberAdminButton_Click"
Margin="0,170,205,0"/> Margin="0,170,205,0">
<Button x:Name="MailButton" Content="Rundschreiben" Click="MailButton_Click" <Grid>
Margin="205,170,0,0"/> <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE77B;"
<Button x:Name="DeliveryAdminButton" Content="Lieferungen" Click="DeliveryAdminButton_Click" TextAlignment="Left" HorizontalAlignment="Left" Padding="0.5,1.5,0,0"/>
Margin="0,210,205,0"/> <TextBlock TextAlignment="Center">Mitglieder</TextBlock>
<Button x:Name="ReceiptButton" Content="Übernahme" Click="ReceiptButton_Click" </Grid>
Margin="205,210,0,0"/> </Button>
<Button x:Name="BaseDataButton" Content="Stammdaten" Click="BaseDataButton_Click" <Button x:Name="MailButton" Click="MailButton_Click"
Margin="0,250,205,0"/> Margin="205,170,0,0">
<Button x:Name="DeliveryAncmtButton" Content="Anmeldungen" Click="DeliveryAncmtButton_Click" <Grid>
Margin="205,250,0,0"/> <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE715;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="0.5,1.5,0,0"/>
<TextBlock TextAlignment="Center">Rundschreiben</TextBlock>
</Grid>
</Button>
<Button x:Name="DeliveryAdminButton" Click="DeliveryAdminButton_Click"
Margin="0,210,205,0">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE736;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="0.5,1.5,0,0"/>
<TextBlock TextAlignment="Center">Lieferungen</TextBlock>
</Grid>
</Button>
<Button x:Name="ReceiptButton" Click="ReceiptButton_Click"
Margin="205,210,0,0">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xEC5B;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="0.5,1.5,0,0"/>
<TextBlock TextAlignment="Center">Übernahme</TextBlock>
</Grid>
</Button>
<Button x:Name="BaseDataButton" Click="BaseDataButton_Click"
Margin="0,250,205,0">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE713;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="0.5,1.5,0,0"/>
<TextBlock TextAlignment="Center">Stammdaten</TextBlock>
</Grid>
</Button>
<Button x:Name="DeliveryAncmtButton" Click="DeliveryAncmtButton_Click"
Margin="205,250,0,0">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xF0E4;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="0.5,1.5,0,0"/>
<TextBlock TextAlignment="Center">Anmeldungen</TextBlock>
</Grid>
</Button>
<Button x:Name="DownloadButton" Click="DownloadButton_Click" <Button x:Name="DownloadButton" Click="DownloadButton_Click"
Margin="310,135,0,0" Padding="1.5,0,0,0" Height="30" Width="30" Margin="310,135,0,0" Padding="0.375,0.5,0,0" Height="30" Width="30"
Content="&#xE896;" FontFamily="Segoe MDL2 Assets" FontSize="16" Content="&#xE896;" FontFamily="Segoe MDL2 Assets" FontSize="16"
HorizontalContentAlignment="Center"
ToolTip="Lieferungen und Mitgliederdaten anderer Zweigstellen herunterladen"/> ToolTip="Lieferungen und Mitgliederdaten anderer Zweigstellen herunterladen"/>
<Button x:Name="UploadButton" Click="UploadButton_Click" <Button x:Name="UploadButton" Click="UploadButton_Click"
Margin="375,135,0,0" Padding="1.5,0,0,0" Height="30" Width="30" Margin="375,135,0,0" Padding="1.0,0.5,0,0" Height="30" Width="30"
Content="&#xE898;" FontFamily="Segoe MDL2 Assets" FontSize="16" Content="&#xE898;" FontFamily="Segoe MDL2 Assets" FontSize="16"
HorizontalContentAlignment="Center"
ToolTip="Lieferungen dieser Zweigstelle hochladen"/> ToolTip="Lieferungen dieser Zweigstelle hochladen"/>
<Expander x:Name="SeasonFinish" Header="Leseabschluss" SnapsToDevicePixels="True" <Expander x:Name="SeasonFinish" Header="Leseabschluss" SnapsToDevicePixels="True"
@ -90,21 +173,104 @@
Margin="0,13,0,0" VerticalAlignment="Top" HorizontalAlignment="Center" Margin="0,13,0,0" VerticalAlignment="Top" HorizontalAlignment="Center"
TextChanged="SeasonInput_TextChanged"/> TextChanged="SeasonInput_TextChanged"/>
<Button x:Name="OverUnderDeliveryButton" Content="Über-/Unterlieferungen" <Button x:Name="DeliveryConfirmationButton"
Click="OverUnderDeliveryButton_Click"
Margin="0,50,195,10" Width="190"/>
<Button x:Name="DeliveryConfirmationButton" Content="Anlieferungsbestätigung"
Click="DeliveryConfirmationButton_Click" Click="DeliveryConfirmationButton_Click"
Margin="195,50,0,10" Width="190"/> Margin="0,50,195,10" Width="190">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE715;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="0.5,0.5,0,0"/>
<TextBlock FontSize="12" Margin="18,1,0,0" TextAlignment="Center">Anlieferungsbestätigung</TextBlock>
</Grid>
</Button>
<Button x:Name="BreakdownButton" Content="Sorten-/Qual.aufteilung" <Button x:Name="PaymentButton"
Click="BreakdownButton_Click"
Margin="0,90,195,10" Width="190"/>
<Button x:Name="PaymentButton" Content="Auszahlung"
Click="PaymentButton_Click" Click="PaymentButton_Click"
Margin="195,90,0,10" Width="190"/> Margin="195,50,0,10" Width="190">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE8EF;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="0.5,1.5,0,0"/>
<TextBlock TextAlignment="Center">Auszahlung</TextBlock>
</Grid>
</Button>
<Button x:Name="OverUnderDeliveryButton"
Click="OverUnderDeliveryButton_Click"
Margin="0,90,195,10" Width="190" Padding="3,5,5,5"
ToolTip="Über-/Unterlieferungen laut gezeichneten Geschäftsanteilen und Unterlieferungen nach Flächenbindungen">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE9F9;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="6.5,0.5,0,0"/>
<TextBlock FontSize="12" Margin="18,1,0,0" TextAlignment="Center">Über-/Unterlieferungen</TextBlock>
</Grid>
</Button>
<Button x:Name="BreakdownButton"
Click="BreakdownButton_Click"
Margin="195,90,0,10" Width="190" Padding="3,5,5,5"
ToolTip="Aufschlüsselung des Gewichts nach Zweigstelle, Mitglied, Sorte, Attribut/Bewirt., Qualitätsstufe, gebunden/ungebunden">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE9F9;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="6.5,0.5,0,0"/>
<TextBlock FontSize="12" Margin="18,1,0,0" TextAlignment="Center">Sorten-/Qual.aufschlüssel.</TextBlock>
</Grid>
</Button>
<Button x:Name="AreaCommitmentsButton"
Click="AreaCommitmentsButton_Click"
Margin="0,130,195,10" Width="190" Padding="3,5,5,5"
ToolTip="Aktive Flächenbindungen der Saison pro Mitglied und Sorte/Attribut">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE9F9;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="6.5,1.5,0,0"/>
<TextBlock Margin="18,0,0,0" TextAlignment="Center">Flächenbindungen</TextBlock>
</Grid>
</Button>
<Button x:Name="BreakdownMemberVarietyButton"
Click="BreakdownMemberVarietyButton_Click"
Margin="195,130,0,10" Width="190" Padding="3,5,5,5"
ToolTip="Liefermengen und Ertrag (kg/ha) pro Mitglied">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE9F9;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="6.5,1.5,0,0"/>
<TextBlock Margin="18,0,0,0" TextAlignment="Center">Liefermengen/Ertrag</TextBlock>
</Grid>
</Button>
<Grid VerticalAlignment="Bottom" Margin="50,175,50,15">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="1" TextAlignment="Right"><Bold>Mitglieder</Bold></TextBlock>
<TextBlock Grid.Row="0" Grid.Column="2" TextAlignment="Right"><Bold>Gewicht</Bold></TextBlock>
<TextBlock Grid.Row="0" Grid.Column="3" TextAlignment="Right"><Bold>Fläche</Bold></TextBlock>
<TextBlock Grid.Row="1" Grid.Column="0">Gesamt:</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="0">Gebunden:</TextBlock>
<TextBlock Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2">Ungebunden:</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="1" TextAlignment="Right" x:Name="SeasonStatMembersTotal">-</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="1" TextAlignment="Right" x:Name="SeasonStatMembersGeb">-</TextBlock>
<TextBlock Grid.Row="3" Grid.Column="1" TextAlignment="Right">-</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="2" TextAlignment="Right" x:Name="SeasonStatWeightTotal">-</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="2" TextAlignment="Right" x:Name="SeasonStatWeightGeb">-</TextBlock>
<TextBlock Grid.Row="3" Grid.Column="2" TextAlignment="Right" x:Name="SeasonStatWeightUngeb">-</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="3" TextAlignment="Right">-</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="3" TextAlignment="Right" x:Name="SeasonStatArea">-</TextBlock>
<TextBlock Grid.Row="3" Grid.Column="3" TextAlignment="Right">-</TextBlock>
</Grid>
</Grid> </Grid>
</Expander> </Expander>
</Grid> </Grid>

View File

@ -251,7 +251,7 @@ namespace Elwig.Windows {
} }
private void SeasonFinish_Expanded(object sender, RoutedEventArgs evt) { private void SeasonFinish_Expanded(object sender, RoutedEventArgs evt) {
Height = 530; Height = 660;
} }
private void SeasonFinish_Collapsed(object sender, RoutedEventArgs evt) { private void SeasonFinish_Collapsed(object sender, RoutedEventArgs evt) {
@ -260,12 +260,34 @@ namespace Elwig.Windows {
private async void SeasonInput_TextChanged(object? sender, TextChangedEventArgs? evt) { private async void SeasonInput_TextChanged(object? sender, TextChangedEventArgs? evt) {
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
var s0 = await ctx.Seasons.FindAsync(SeasonInput.Value); var year = SeasonInput.Value;
var s0 = await ctx.Seasons.FindAsync(year);
var valid = (s0 != null); var valid = (s0 != null);
DeliveryConfirmationButton.IsEnabled = valid; DeliveryConfirmationButton.IsEnabled = valid;
OverUnderDeliveryButton.IsEnabled = valid;
PaymentButton.IsEnabled = valid; PaymentButton.IsEnabled = valid;
OverUnderDeliveryButton.IsEnabled = valid;
BreakdownButton.IsEnabled = valid; BreakdownButton.IsEnabled = valid;
AreaCommitmentsButton.IsEnabled = valid;
BreakdownMemberVarietyButton.IsEnabled = valid;
if (valid) {
var areaComs = Utils.ActiveAreaCommitments(ctx.AreaCommitments, year!.Value);
var weightTotal = await ctx.DeliveryParts.Where(p => p.Year == year).SumAsync(p => p.Weight);
var gebWeight = await ctx.DeliveryPartBuckets.Where(b => b.Year == year && b.Discr != "_").SumAsync(b => b.Value);
SeasonStatMembersTotal.Text = $"{await ctx.Deliveries.Where(d => d.Year == year).Select(d => d.Member).Distinct().CountAsync():N0}";
SeasonStatMembersGeb.Text = $"{await areaComs.Select(c => c.Member).Distinct().CountAsync():N0}";
SeasonStatWeightTotal.Text = $"{weightTotal:N0} kg";
SeasonStatWeightGeb.Text = $"{gebWeight:N0} kg";
SeasonStatWeightUngeb.Text = $"{weightTotal - gebWeight:N0} kg";
SeasonStatArea.Text = $"{await areaComs.SumAsync(c => c.Area):N0} m²";
} else {
SeasonStatMembersTotal.Text = "-";
SeasonStatMembersGeb.Text = "-";
SeasonStatWeightTotal.Text = "-";
SeasonStatWeightGeb.Text = "-";
SeasonStatWeightUngeb.Text = "-";
SeasonStatArea.Text = "-";
}
} }
private void DeliveryConfirmationButton_Click(object sender, RoutedEventArgs evt) { private void DeliveryConfirmationButton_Click(object sender, RoutedEventArgs evt) {
@ -275,6 +297,12 @@ namespace Elwig.Windows {
w.AddDeliveryConfirmation(); w.AddDeliveryConfirmation();
} }
private void PaymentButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year)
return;
App.FocusPaymentVariants(year);
}
private async void OverUnderDeliveryButton_Click(object sender, RoutedEventArgs evt) { private async void OverUnderDeliveryButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year) if (SeasonInput.Value is not int year)
return; return;
@ -297,23 +325,15 @@ namespace Elwig.Windows {
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
var tbl1 = await OverUnderDeliveryData.ForSeason(ctx.OverUnderDeliveryRows, year); var tbl1 = await OverUnderDeliveryData.ForSeason(ctx.OverUnderDeliveryRows, year);
var tbl2 = await AreaComUnderDeliveryData.ForSeason(ctx.AreaComUnderDeliveryRows, year); var tbl2 = await AreaComUnderDeliveryData.ForSeason(ctx.AreaComUnderDeliveryRows, year);
var tbl3 = await MemberDeliveryPerVariantData.ForSeason(ctx.MemberDeliveryPerVariantRows, year);
using var ods = new OdsFile(d.FileName); using var ods = new OdsFile(d.FileName);
await ods.AddTable(tbl1); await ods.AddTable(tbl1);
await ods.AddTable(tbl2); await ods.AddTable(tbl2);
await ods.AddTable(tbl3);
} catch (Exception exc) { } catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
} }
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
} }
private void PaymentButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year)
return;
App.FocusPaymentVariants(year);
}
private async void BreakdownButton_Click(object sender, RoutedEventArgs evt) { private async void BreakdownButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year) if (SeasonInput.Value is not int year)
return; return;
@ -346,5 +366,63 @@ namespace Elwig.Windows {
} }
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
} }
private async void AreaCommitmentsButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year)
return;
var d = new SaveFileDialog() {
FileName = $"Flächenbindungen-{year}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"Flächenbindungen {year} speichern unter - Elwig"
};
if (d.ShowDialog() == false)
return;
Mouse.OverrideCursor = Cursors.AppStarting;
try {
var b = new Billing(year);
await b.FinishSeason();
await b.CalculateBuckets();
App.HintContextChange();
using var ctx = new AppDbContext();
var tbl = await MemberAreaComsData.ForSeason(ctx.MemberAreaComsRows, year);
using var ods = new OdsFile(d.FileName);
await ods.AddTable(tbl);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
private async void BreakdownMemberVarietyButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year)
return;
var d = new SaveFileDialog() {
FileName = $"Liefermengen-Ertrag-{year}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"Liefermengen/Ertrag {year} speichern unter - Elwig"
};
if (d.ShowDialog() == false)
return;
Mouse.OverrideCursor = Cursors.AppStarting;
try {
var b = new Billing(year);
await b.FinishSeason();
await b.CalculateBuckets();
App.HintContextChange();
using var ctx = new AppDbContext();
var tbl = await MemberDeliveryPerVarietyData.ForSeason(ctx.MemberDeliveryPerVariantRows, year);
using var ods = new OdsFile(d.FileName);
await ods.AddTable(tbl);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
} }
} }

View File

@ -52,19 +52,43 @@
<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="Kontaktieren"> <MenuItem Header="Kontaktieren">
<MenuItem x:Name="Menu_Contact_Email" Header="E-Mail senden..." IsEnabled="{Binding MemberHasEmail}" <MenuItem x:Name="Menu_Contact_Email" Header="E-Mail senden..." IsEnabled="{Binding MemberHasEmail}"
Click="Menu_Contact_Email_Click"/> Click="Menu_Contact_Email_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE715;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_Contact_Letterhead" Header="Briefkopf drucken" IsEnabled="{Binding IsMemberSelected}" <MenuItem x:Name="Menu_Contact_Letterhead" Header="Briefkopf drucken" IsEnabled="{Binding IsMemberSelected}"
Click="Menu_Contact_Letterhead_Click"/> Click="Menu_Contact_Letterhead_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE749;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="Stammdatenblatt"> <MenuItem Header="Stammdatenblatt">
<MenuItem x:Name="Menu_MemberDataSheet_Show" Header="...anzeigen (PDF)" IsEnabled="{Binding IsMemberSelected}" <MenuItem x:Name="Menu_MemberDataSheet_Show" Header="...anzeigen (PDF)" IsEnabled="{Binding IsMemberSelected}"
Click="Menu_MemberDataSheet_Show_Click" InputGestureText="Strg+P"/> Click="Menu_MemberDataSheet_Show_Click" InputGestureText="Strg+P">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE8FF;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_MemberDataSheet_SavePdf" Header="...speichern... (PDF)" IsEnabled="{Binding IsMemberSelected}" <MenuItem x:Name="Menu_MemberDataSheet_SavePdf" Header="...speichern... (PDF)" IsEnabled="{Binding IsMemberSelected}"
Click="Menu_MemberDataSheet_SavePdf_Click"/> Click="Menu_MemberDataSheet_SavePdf_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xEA90;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_MemberDataSheet_Print" Header="...drucken" IsEnabled="{Binding IsMemberSelected}" <MenuItem x:Name="Menu_MemberDataSheet_Print" Header="...drucken" IsEnabled="{Binding IsMemberSelected}"
Click="Menu_MemberDataSheet_Print_Click" InputGestureText="Strg+Shift+P"/> Click="Menu_MemberDataSheet_Print_Click" InputGestureText="Strg+Shift+P">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE749;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_MemberDataSheet_Email" Header="...per E-Mail schicken" IsEnabled="{Binding MemberCanSendEmail}" <MenuItem x:Name="Menu_MemberDataSheet_Email" Header="...per E-Mail schicken" IsEnabled="{Binding MemberCanSendEmail}"
Click="Menu_MemberDataSheet_Email_Click"/> Click="Menu_MemberDataSheet_Email_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE89C;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="Anlieferungsbestätigung" x:Name="Menu_DeliveryConfirmation"> <MenuItem Header="Anlieferungsbestätigung" x:Name="Menu_DeliveryConfirmation">
<MenuItem x:Name="Menu_DeliveryConfirmation_Show" Header="...anzeigen (PDF)" IsEnabled="False" <MenuItem x:Name="Menu_DeliveryConfirmation_Show" Header="...anzeigen (PDF)" IsEnabled="False"
@ -177,7 +201,7 @@
<Bold>Telefon-Nr.</Bold>: z.B. +436641234, ....<LineBreak/> <Bold>Telefon-Nr.</Bold>: z.B. +436641234, ....<LineBreak/>
<Bold>Kontaktdaten</Bold>: email (mind. 1 E-Mail-Adr.), telnr (mind. 1 Tel.-Nr.), !email (keine E-Mail-Adr.), !telnr (keine Tel.-Nr.)<LineBreak/> <Bold>Kontaktdaten</Bold>: email (mind. 1 E-Mail-Adr.), telnr (mind. 1 Tel.-Nr.), !email (keine E-Mail-Adr.), !telnr (keine Tel.-Nr.)<LineBreak/>
<Bold>Kontaktart</Bold>: kontakt:email, kontakt:post, !kontakt:email, !kontakt:post<LineBreak/> <Bold>Kontaktart</Bold>: kontakt:email, kontakt:post, !kontakt:email, !kontakt:post<LineBreak/>
<Bold>Flächenbindungen</Bold>: z.B. zw, GVK, WRB, ... (Mitglieder mit aktiven Flächenbindungen)<LineBreak/> <Bold>Flächenbindungen</Bold>: z.B. zw, GVK, WRB, Fläch[enbindung], !GVK, ... (Mitglieder mit aktiven Flächenbindungen)<LineBreak/>
<Bold>Freitext</Bold>: z.B. Rechnungsaddresse, Anmerkung, "matzen" (sucht nach dem Text "matzen") <Bold>Freitext</Bold>: z.B. Rechnungsaddresse, Anmerkung, "matzen" (sucht nach dem Text "matzen")
</TextBlock> </TextBlock>
</TextBox.ToolTip> </TextBox.ToolTip>

View File

@ -68,7 +68,7 @@ namespace Elwig.Windows {
(PhoneNr9TypeInput, PhoneNr9Input, PhoneNr9CommentInput), (PhoneNr9TypeInput, PhoneNr9Input, PhoneNr9CommentInput),
]; ];
InitializeDelayTimer(SearchInput, SearchInput_TextChanged); ControlUtils.InitializeDelayTimer(SearchInput, SearchInput_TextChanged);
SearchInput.TextChanged -= SearchInput_TextChanged; SearchInput.TextChanged -= SearchInput_TextChanged;
ViewModel.MemberListOrderByMgNr = false; ViewModel.MemberListOrderByMgNr = false;
@ -187,49 +187,80 @@ namespace Elwig.Windows {
ControlUtils.RenewItemsSource(BranchInput, await ctx.Branches.OrderBy(b => b.Name).ToListAsync()); ControlUtils.RenewItemsSource(BranchInput, await ctx.Branches.OrderBy(b => b.Name).ToListAsync());
ControlUtils.RenewItemsSource(DefaultKgInput, await ctx.WbKgs.Select(k => k.AtKg).OrderBy(k => k.Name).ToListAsync()); ControlUtils.RenewItemsSource(DefaultKgInput, await ctx.WbKgs.Select(k => k.AtKg).OrderBy(k => k.Name).ToListAsync());
var font = new System.Windows.Media.FontFamily("Segoe MDL2 Assets");
MenuItem? temp = null;
var seasons = await ctx.Seasons.OrderByDescending(s => s.Year).ToListAsync(); var seasons = await ctx.Seasons.OrderByDescending(s => s.Year).ToListAsync();
Menu_DeliveryConfirmation.Items.Clear(); Menu_DeliveryConfirmation.Items.Clear();
foreach (var s in seasons) { foreach (var s in seasons) {
var i = new MenuItem { Header = $"Saison {s.Year}...", Tag = s.Year }; var i = new MenuItem {
Header = $"Saison {s.Year}...",
Tag = s.Year,
Icon = s.Year == seasons[0].Year ? new TextBlock { FontSize = 16, Text = "\uE734", FontFamily = font } : null,
};
i.SetBinding(IsEnabledProperty, new Binding() { Path = new("IsMemberSelected") }); i.SetBinding(IsEnabledProperty, new Binding() { Path = new("IsMemberSelected") });
var show = new MenuItem { Header = "...anzeigen (PDF)" }; var show = new MenuItem { Header = "...anzeigen (PDF)", Icon = new TextBlock { FontSize = 16, Text = "\uE8FF", FontFamily = font } };
show.Click += Menu_DeliveryConfirmation_Show_Click; show.Click += Menu_DeliveryConfirmation_Show_Click;
i.Items.Add(show); i.Items.Add(show);
var pdf = new MenuItem { Header = "...speichern... (PDF)" }; var pdf = new MenuItem { Header = "...speichern... (PDF)", Icon = new TextBlock { FontSize = 16, Text = "\uEA90", FontFamily = font } };
pdf.Click += Menu_DeliveryConfirmation_Email_Click; pdf.Click += Menu_DeliveryConfirmation_SavePdf_Click;
i.Items.Add(pdf); i.Items.Add(pdf);
var print = new MenuItem { Header = "...drucken" }; var print = new MenuItem { Header = "...drucken", Icon = new TextBlock { FontSize = 16, Text = "\uE749", FontFamily = font } };
print.Click += Menu_DeliveryConfirmation_Print_Click; print.Click += Menu_DeliveryConfirmation_Print_Click;
i.Items.Add(print); i.Items.Add(print);
var email = new MenuItem { Header = "...per E-Mail schicken" }; var email = new MenuItem { Header = "...per E-Mail schicken", Icon = new TextBlock { FontSize = 16, Text = "\uE89C", FontFamily = font } };
email.Click += Menu_DeliveryConfirmation_Email_Click; email.Click += Menu_DeliveryConfirmation_Email_Click;
email.SetBinding(IsEnabledProperty, new Binding() { Path = new("MemberCanSendEmail") }); email.SetBinding(IsEnabledProperty, new Binding() { Path = new("MemberCanSendEmail") });
i.Items.Add(email); i.Items.Add(email);
Menu_DeliveryConfirmation.Items.Add(i); var decade = s.Year / 10;
if (seasons[0].Year / 10 != decade) {
if (temp == null || !temp.Header.ToString()!.Contains($"{decade}0er")) {
temp = new MenuItem { Header = $"Saisons {decade}0er..." };
Menu_DeliveryConfirmation.Items.Add(temp);
}
temp?.Items.Add(i);
} else {
Menu_DeliveryConfirmation.Items.Add(i);
}
} }
temp = null;
Menu_CreditNote.Items.Clear(); Menu_CreditNote.Items.Clear();
foreach (var s in seasons) { foreach (var s in seasons) {
var i1 = new MenuItem { Header = $"Saison {s.Year}...", Tag = s.Year, IsEnabled = MemberList.SelectedItem != null }; var i1 = new MenuItem {
Header = $"Saison {s.Year}...",
Tag = s.Year,
IsEnabled = MemberList.SelectedItem != null,
Icon = s.Year == seasons[0].Year ? new TextBlock { FontSize = 16, Text = "\uE734", FontFamily = font } : null,
};
i1.SetBinding(IsEnabledProperty, new Binding() { Path = new($"MemberHasDeliveries[{s.Year}]") }); i1.SetBinding(IsEnabledProperty, new Binding() { Path = new($"MemberHasDeliveries[{s.Year}]") });
foreach (var v in s.PaymentVariants.OrderByDescending(v => v.AvNr)) { foreach (var v in s.PaymentVariants.OrderByDescending(v => v.AvNr)) {
var i2 = new MenuItem { Header = $"...{v.Name}...", Tag = v.AvNr }; var i2 = new MenuItem { Header = $"...{v.Name}...", Tag = v.AvNr };
var show = new MenuItem { Header = "...anzeigen (PDF)" }; var show = new MenuItem { Header = "...anzeigen (PDF)", Icon = new TextBlock { FontSize = 16, Text = "\uE8FF", FontFamily = font } };
show.Click += Menu_CreditNote_Show_Click; show.Click += Menu_CreditNote_Show_Click;
i2.Items.Add(show); i2.Items.Add(show);
var pdf = new MenuItem { Header = "...speichern... (PDF)" }; var pdf = new MenuItem { Header = "...speichern... (PDF)", Icon = new TextBlock { FontSize = 16, Text = "\uEA90", FontFamily = font } };
pdf.Click += Menu_CreditNote_Email_Click; pdf.Click += Menu_CreditNote_SavePdf_Click;
i2.Items.Add(pdf); i2.Items.Add(pdf);
var print = new MenuItem { Header = "...drucken" }; var print = new MenuItem { Header = "...drucken", Icon = new TextBlock { FontSize = 16, Text = "\uE749", FontFamily = font } };
print.Click += Menu_CreditNote_Print_Click; print.Click += Menu_CreditNote_Print_Click;
i2.Items.Add(print); i2.Items.Add(print);
var email = new MenuItem { Header = "...per E-Mail schicken" }; var email = new MenuItem { Header = "...per E-Mail schicken", Icon = new TextBlock { FontSize = 16, Text = "\uE89C", FontFamily = font } };
email.SetBinding(IsEnabledProperty, new Binding { Path = new("MemberCanSendEmail") }); email.SetBinding(IsEnabledProperty, new Binding { Path = new("MemberCanSendEmail") });
email.Click += Menu_CreditNote_Email_Click; email.Click += Menu_CreditNote_Email_Click;
i2.Items.Add(email); i2.Items.Add(email);
i1.Items.Add(i2); i1.Items.Add(i2);
} }
Menu_CreditNote.Items.Add(i1); var decade = s.Year / 10;
if (seasons[0].Year / 10 != decade) {
if (temp == null || !temp.Header.ToString()!.Contains($"{decade}0er")) {
temp = new MenuItem { Header = $"Saisons {decade}0er..." };
Menu_CreditNote.Items.Add(temp);
}
temp?.Items.Add(i1);
} else {
Menu_CreditNote.Items.Add(i1);
}
} }
temp = null;
await RefreshList(); await RefreshList();
@ -360,21 +391,7 @@ namespace Elwig.Windows {
if (d.ShowDialog() == true) { if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.AppStarting; Mouse.OverrideCursor = Cursors.AppStarting;
try { try {
using (var ctx = new AppDbContext()) { await MemberService.DeleteMember(m.MgNr, d.DeletePaymentData, d.DeleteDeliveries, d.DeleteAreaComs);
var l = (await ctx.Members.FindAsync(m.MgNr))!;
if (d.DeletePaymentData) {
ctx.RemoveRange(l.Credits);
}
if (d.DeleteDeliveries) {
ctx.RemoveRange(l.Deliveries);
}
if (d.DeleteAreaComs) {
ctx.RemoveRange(l.AreaCommitments);
}
ctx.Remove(l);
await ctx.SaveChangesAsync();
}
App.HintContextChange();
} 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;
@ -506,6 +523,10 @@ namespace Elwig.Windows {
private async void Menu_MemberDataSheet_Email_Click(object sender, RoutedEventArgs evt) { private async void Menu_MemberDataSheet_Email_Click(object sender, RoutedEventArgs evt) {
if (ViewModel.SelectedMember is not Member m) return; if (ViewModel.SelectedMember is not Member m) return;
var res = MessageBox.Show("Soll eine E-Mail verschickt werden?", "Stammdatenblatt verschicken",
MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
if (res != MessageBoxResult.Yes)
return;
await MemberService.GenerateMemberDataSheet(m, ExportMode.Email); await MemberService.GenerateMemberDataSheet(m, ExportMode.Email);
} }
@ -534,6 +555,10 @@ namespace Elwig.Windows {
var year = (int?)((sender as MenuItem)?.Parent as MenuItem)?.Tag; var year = (int?)((sender as MenuItem)?.Parent as MenuItem)?.Tag;
if (ViewModel.SelectedMember is not Member m || year == null) if (ViewModel.SelectedMember is not Member m || year == null)
return; return;
var res = MessageBox.Show("Soll eine E-Mail verschickt werden?", "Anlieferungsbestätigung verschicken",
MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
if (res != MessageBoxResult.Yes)
return;
await MemberService.GenerateDeliveryConfirmation(m, (int)year, ExportMode.Email); await MemberService.GenerateDeliveryConfirmation(m, (int)year, ExportMode.Email);
} }
@ -566,6 +591,10 @@ namespace Elwig.Windows {
var avnr = (int?)((sender as MenuItem)?.Parent as MenuItem)?.Tag; var avnr = (int?)((sender as MenuItem)?.Parent as MenuItem)?.Tag;
if (ViewModel.SelectedMember is not Member m || year == null || avnr == null) if (ViewModel.SelectedMember is not Member m || year == null || avnr == null)
return; return;
var res = MessageBox.Show("Soll eine E-Mail verschickt werden?", "Traubengutschrift verschicken",
MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
if (res != MessageBoxResult.Yes)
return;
await MemberService.GenerateCreditNote(m, (int)year, (int)avnr, ExportMode.Email); await MemberService.GenerateCreditNote(m, (int)year, (int)avnr, ExportMode.Email);
} }

View File

@ -56,21 +56,45 @@
<Menu Grid.ColumnSpan="2" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White"> <Menu Grid.ColumnSpan="2" BorderThickness="0,0,0,1" BorderBrush="LightGray" Background="White">
<MenuItem Header="Variantendaten"> <MenuItem Header="Variantendaten">
<MenuItem x:Name="Menu_SummaryShow" Header="...anzeigen (PDF)" IsEnabled="False" <MenuItem x:Name="Menu_SummaryShow" Header="...anzeigen (PDF)" IsEnabled="False"
Click="Menu_SummaryShow_Click" InputGestureText="Strg+P"/> Click="Menu_SummaryShow_Click" InputGestureText="Strg+P">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE8FF;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_SummarySave" Header="...speichern... (PDF)" IsEnabled="False" <MenuItem x:Name="Menu_SummarySave" Header="...speichern... (PDF)" IsEnabled="False"
Click="Menu_SummarySave_Click"/> Click="Menu_SummarySave_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xEA90;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_SummaryExport" Header="...speichern... (Excel)" IsEnabled="False" <MenuItem x:Name="Menu_SummaryExport" Header="...speichern... (Excel)" IsEnabled="False"
Click="Menu_SummaryExport_Click"/> Click="Menu_SummaryExport_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE9F9;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_SummaryPrint" Header="...drucken" IsEnabled="False" <MenuItem x:Name="Menu_SummaryPrint" Header="...drucken" IsEnabled="False"
Click="Menu_SummaryPrint_Click" InputGestureText="Strg+Shift+P"/> Click="Menu_SummaryPrint_Click" InputGestureText="Strg+Shift+P">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE749;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="Buchungsliste"> <MenuItem Header="Buchungsliste">
<MenuItem x:Name="Menu_ExportSave" Header="...speichern... (Excel)" IsEnabled="False" <MenuItem x:Name="Menu_ExportSave" Header="...speichern... (Excel)" IsEnabled="False"
Click="Menu_ExportSave_Click" InputGestureText="Strg+L"/> Click="Menu_ExportSave_Click" InputGestureText="Strg+L">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE9F9;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="Überweisungsdaten"> <MenuItem Header="Überweisungsdaten">
<MenuItem x:Name="Menu_EbicsSave" Header="...exportieren... (EBICS)" IsEnabled="False" <MenuItem x:Name="Menu_EbicsSave" Header="...exportieren... (EBICS)" IsEnabled="False"
Click="Menu_EbicsSave_Click" InputGestureText="Strg+Ü"/> Click="Menu_EbicsSave_Click" InputGestureText="Strg+Ü">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE792;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem> </MenuItem>
</Menu> </Menu>
@ -215,10 +239,16 @@
Click="SaveButton_Click"/> Click="SaveButton_Click"/>
</Grid> </Grid>
<Button x:Name="MailButton" Content="Traubengutschriften" <Button x:Name="MailButton"
FontSize="14" Width="160" Margin="10,10,10,10" Height="27" IsEnabled="False" FontSize="14" Width="180" Margin="10,10,10,10" Height="30" IsEnabled="False"
Click="MailButton_Click" Click="MailButton_Click"
VerticalAlignment="Bottom" HorizontalAlignment="Right" Grid.Column="1"/> VerticalAlignment="Bottom" HorizontalAlignment="Right" HorizontalContentAlignment="Stretch" Grid.Column="1">
<Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE715;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="0,1.5,0,0" Margin="0,0,10,0"/>
<TextBlock Margin="18,0,0,0" TextAlignment="Center">Traubengutschriften</TextBlock>
</Grid>
</Button>
</Grid> </Grid>
<StatusBar Grid.Row="2" Grid.ColumnSpan="2" BorderThickness="0,1,0,0" BorderBrush="Gray"> <StatusBar Grid.Row="2" Grid.ColumnSpan="2" BorderThickness="0,1,0,0" BorderBrush="Gray">

View File

@ -13,7 +13,7 @@
</Target> </Target>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Installer\Installer.wixproj" /> <ProjectReference Include="..\Installer\Installer.wixproj" />
<PackageReference Include="WixToolset.Bal.wixext" Version="5.0.1" /> <PackageReference Include="WixToolset.Bal.wixext" Version="5.0.2" />
<PackageReference Include="WixToolset.Util.wixext" Version="5.0.1" /> <PackageReference Include="WixToolset.Util.wixext" Version="5.0.2" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -30,6 +30,11 @@ namespace Tests {
await BillingData.Init(); await BillingData.Init();
} }
[OneTimeSetUp]
public void Setup_4_Culture() {
Utils.OverrideCulture();
}
[OneTimeTearDown] [OneTimeTearDown]
public async Task TeardownDatabase() { public async Task TeardownDatabase() {
AppDbContext.ConnectionStringOverride = null; AppDbContext.ConnectionStringOverride = null;

View File

@ -29,9 +29,10 @@ namespace Tests.DocumentTests {
Assert.That(text, Contains.Substring("AT81 1234 5678 9012 3457")); Assert.That(text, Contains.Substring("AT81 1234 5678 9012 3457"));
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
20201001X001 1 Grüner Veltliner 73 15,0 ungeb.: 3 219 0,5000 - - 1 609,50 20201001X001 1 Grüner Veltliner 73 15,0 ungeb.: 3 219 0,5000 - - 1 609,50
20201001X003 1 Grüner Veltliner 75 15,4 ungeb.: 2 561 - - 20201001X003 1 Grüner Veltliner abgew. 75 15,4 ungeb.: 2 561 - -
20201001X003 2 Grüner Veltliner Kabinett 87 17,6 ungeb.: 3 129 - - 20201001X003 2 Grüner Veltliner Kabinett / abgew.
20201001X003 3 Grüner Veltliner 79 16,1 ungeb.: 1 280 - - 87 17,6 ungeb.: 3 129 - -
20201001X003 3 Grüner Veltliner abgew. 79 16,1 ungeb.: 1 280 - -
20201001X005 1 Welschriesling 84 17,0 ungeb.: 3 192 - - 20201001X005 1 Welschriesling 84 17,0 ungeb.: 3 192 - -
20201001X005 2 Welschriesling 84 17,1 ungeb.: 2 190 - - 20201001X005 2 Welschriesling 84 17,1 ungeb.: 2 190 - -
""")); """));

View File

@ -24,10 +24,10 @@ namespace Tests.DocumentTests {
Assert.That(text, Contains.Substring($"Wolkersdorf, am {Elwig.Helpers.Utils.Today:dd.MM.yyyy}")); Assert.That(text, Contains.Substring($"Wolkersdorf, am {Elwig.Helpers.Utils.Today:dd.MM.yyyy}"));
Assert.That(text, Contains.Substring("Anlieferungsbestätigung 2020")); Assert.That(text, Contains.Substring("Anlieferungsbestätigung 2020"));
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
20201001X003 2 Grüner Veltliner Kabinett Kabinett 87 17,6 ungeb.: 3 129 3 129
20201001X003 3 Grüner Veltliner Qualitätswein 79 16,1 ungeb.: 1 280 1 280
20201001X003 1 Grüner Veltliner Qualitätswein 75 15,4 ungeb.: 2 561 2 561
20201001X001 1 Grüner Veltliner Qualitätswein 73 15,0 ungeb.: 3 219 3 219 20201001X001 1 Grüner Veltliner Qualitätswein 73 15,0 ungeb.: 3 219 3 219
20201001X003 2 Grüner Veltliner Kabinett Wein 87 17,6 ungeb.: 3 129 3 129
20201001X003 3 Grüner Veltliner Wein 79 16,1 ungeb.: 1 280 1 280
20201001X003 1 Grüner Veltliner Wein 75 15,4 ungeb.: 2 561 2 561
20201001X005 2 Welschriesling Kabinett 84 17,1 ungeb.: 2 190 2 190 20201001X005 2 Welschriesling Kabinett 84 17,1 ungeb.: 2 190 2 190
20201001X005 1 Welschriesling Kabinett 84 17,0 ungeb.: 3 192 3 192 20201001X005 1 Welschriesling Kabinett 84 17,0 ungeb.: 3 192 3 192
""")); """));

View File

@ -0,0 +1,32 @@
using Elwig.Documents;
using Elwig.Helpers;
using Elwig.Models.Dtos;
namespace Tests.DocumentTests {
[TestFixture]
public class DeliveryDepreciationListTest {
[Test]
public async Task Test_01_DepreciatedDeliveries2020() {
using var ctx = new AppDbContext();
var data = await DeliveryJournalData.FromQuery(ctx.Deliveries.Where(d => d.Year == 2020).SelectMany(d => d.Parts).Where(d => d.QualId == "WEI"), ["Saison 2020"]);
using var doc = new DeliveryDepreciationList("Saison 2020", data);
var text = await Utils.GeneratePdfText(doc, true);
var table = Utils.ExtractTable(text);
Assert.Multiple(() => {
Assert.That(text, Contains.Substring("Abwertungsliste"));
Assert.That(text, Contains.Substring("Saison 2020"));
Assert.That(table, Is.EqualTo(new string[][] {
["101, MUSTERMANN Max", "Teil-Lfrg.:", "3", "81", "16,5", "6 970"],
["20201001X003 1 01.10.2020 10:24", "Grüner Veltliner", "75", "15,4", "2 561"],
["20201001X003 2 01.10.2020 10:24", "Grüner Veltliner", "Kabinett", "87", "17,6", "3 129"],
["20201001X003 3 01.10.2020 10:24", "Grüner Veltliner", "79", "16,1", "1 280"],
["103, MUSTERBAUER Matthäus", "Teil-Lfrg.:", "2", "79", "16,2", "6 099"],
["20201002X001 1 02.10.2020 09:13", "Grüner Veltliner", "Bio", "80", "16,3", "3 198"],
["20201002X002 1 02.10.2020 09:28", "Grüner Veltliner", "Bio", "78", "16,0", "2 901"],
["Gesamt:", "(Teil-)Lieferungen: 3 (5)", "80", "16,3", "13 069"],
}));
});
}
}
}

View File

@ -84,20 +84,20 @@ namespace Tests.DocumentTests {
Assert.That(text, Contains.Substring("Traubenübernahmeschein Nr. 20201001X003")); Assert.That(text, Contains.Substring("Traubenübernahmeschein Nr. 20201001X003"));
Assert.That(text, Contains.Substring("Das Mitglied erklärt, dass die gelieferte Ware dem österreichischen Weingesetz entspricht")); Assert.That(text, Contains.Substring("Das Mitglied erklärt, dass die gelieferte Ware dem österreichischen Weingesetz entspricht"));
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
1 Grüner Veltliner Qualitätswein 75 15,4 2 561 1 Grüner Veltliner Wein 75 15,4 2 561
Herkunft: Österreich / Weinland / Niederösterreich Herkunft: Österreich
/ Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf / Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf
Waage: ?, ID: ? (gerebelt gewogen) Waage: ?, ID: ? (gerebelt gewogen)
""")); """));
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
2 Grüner Veltliner Kabinett Kabinett 87 17,6 3 129 2 Grüner Veltliner Kabinett Wein 87 17,6 3 129
Herkunft: Österreich / Weinland / Niederösterreich Herkunft: Österreich
/ Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf / Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf
Waage: ?, ID: ? (gerebelt gewogen) Waage: ?, ID: ? (gerebelt gewogen)
""")); """));
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
3 Grüner Veltliner Qualitätswein 79 16,1 1 280 3 Grüner Veltliner Wein 79 16,1 1 280
Herkunft: Österreich / Weinland / Niederösterreich Herkunft: Österreich
/ Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf / Matzner Hügel / Hohenruppersdorf / KG Hohenruppersdorf
Waage: ?, ID: ? (gerebelt gewogen) Waage: ?, ID: ? (gerebelt gewogen)
""")); """));
@ -123,9 +123,9 @@ namespace Tests.DocumentTests {
Assert.That(text, Contains.Substring("Traubenübernahmeschein Nr. 20201002X001")); Assert.That(text, Contains.Substring("Traubenübernahmeschein Nr. 20201002X001"));
Assert.That(text, Contains.Substring("Das Mitglied erklärt, dass die gelieferte Ware dem österreichischen Weingesetz entspricht")); Assert.That(text, Contains.Substring("Das Mitglied erklärt, dass die gelieferte Ware dem österreichischen Weingesetz entspricht"));
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
1 Grüner Veltliner Qualitätswein 80 16,3 3 198 1 Grüner Veltliner Wein 80 16,3 3 198
Bewirtschaftung: Bio (AT-BIO-302) Bewirtschaftung: Bio (AT-BIO-302)
Herkunft: Österreich / Weinland / Niederösterreich Herkunft: Österreich
/ Wolkersdorfer Hochleithen / Wolkersdorf im Weinviertel / KG Wolkersdorf / Wolkersdorfer Hochleithen / Wolkersdorf im Weinviertel / KG Wolkersdorf
Waage: ?, ID: ? (gerebelt gewogen) Waage: ?, ID: ? (gerebelt gewogen)
""")); """));

View File

@ -16,14 +16,19 @@ namespace Tests.DocumentTests {
Assert.That(text, Contains.Substring("Qualitätsstatistik")); Assert.That(text, Contains.Substring("Qualitätsstatistik"));
Assert.That(text, Contains.Substring("Saison 2020")); Assert.That(text, Contains.Substring("Saison 2020"));
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
Qualitätswein Wein
73 2 4 431 75 1 2 561
74 2 4 203
75 3 5 176
77 1 842
78 1 2 901 78 1 2 901
79 1 1 280 79 1 1 280
80 1 3 198 80 1 3 198
87 1 3 129
"""));
Assert.That(text, Contains.Substring("""
Qualitätswein
73 2 4 431
74 2 4 203
75 2 2 615
77 1 842
82 1 4 002 82 1 4 002
""")); """));
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
@ -31,14 +36,14 @@ namespace Tests.DocumentTests {
84 3 8 960 84 3 8 960
85 3 11 181 85 3 11 181
86 1 2 987 86 1 2 987
87 2 5 002 87 1 1 873
89 2 4 723 89 2 4 723
""")); """));
Assert.That(text, Contains.Substring( Assert.That(text, Contains.Substring(
"80 5 " + "13 069 " +
"- 0 0 " + "- 0 0 " +
"- 0 0 " + "77 5 " + "11 568 " +
"77 12 " + "26 033 " + "85 6 " + "17 561"));
"86 11 " + "32 853"));
}); });
} }
} }

View File

@ -43,17 +43,17 @@ INSERT INTO delivery_part (year, did, dpnr, sortid, attrid, cultid, weight, kmw,
(2020, 1, 1, 'GV', NULL, NULL, 3219, 15.0, 'QUW', 'WLNO', 06109, TRUE, FALSE, FALSE, '1', '{"id":"321","nr":321,"gross_weight":3219,"tare_weight":0,"net_weight":3219,"date":"2020-10-01","time":"09:02:46"}', NULL), (2020, 1, 1, 'GV', NULL, NULL, 3219, 15.0, 'QUW', 'WLNO', 06109, TRUE, FALSE, FALSE, '1', '{"id":"321","nr":321,"gross_weight":3219,"tare_weight":0,"net_weight":3219,"date":"2020-10-01","time":"09:02:46"}', NULL),
(2020, 2, 1, 'GV', 'K', NULL, 2987, 17.5, 'KAB', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL), (2020, 2, 1, 'GV', 'K', NULL, 2987, 17.5, 'KAB', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 2, 2, 'GV', 'K', NULL, 1873, 17.7, 'KAB', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL), (2020, 2, 2, 'GV', 'K', NULL, 1873, 17.7, 'KAB', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 3, 1, 'GV', NULL, NULL, 2561, 15.4, 'QUW', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL), (2020, 3, 1, 'GV', NULL, NULL, 2561, 15.4, 'WEI', 'OEST', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 3, 2, 'GV', 'K', NULL, 3129, 17.6, 'KAB', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL), (2020, 3, 2, 'GV', 'K', NULL, 3129, 17.6, 'WEI', 'OEST', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 3, 3, 'GV', NULL, NULL, 1280, 16.1, 'QUW', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL), (2020, 3, 3, 'GV', NULL, NULL, 1280, 16.1, 'WEI', 'OEST', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 4, 1, 'GV', NULL, NULL, 4002, 16.7, 'QUW', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL), (2020, 4, 1, 'GV', NULL, NULL, 4002, 16.7, 'QUW', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 4, 2, 'GV', NULL, NULL, 481, 15.3, 'QUW', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL), (2020, 4, 2, 'GV', NULL, NULL, 481, 15.3, 'QUW', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 5, 1, 'WR', NULL, NULL, 3192, 17.0, 'KAB', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL), (2020, 5, 1, 'WR', NULL, NULL, 3192, 17.0, 'KAB', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 5, 2, 'WR', NULL, NULL, 2190, 17.1, 'KAB', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL), (2020, 5, 2, 'WR', NULL, NULL, 2190, 17.1, 'KAB', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 6, 1, 'GV', NULL, 'B', 1732, 15.2, 'QUW', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL), (2020, 6, 1, 'GV', NULL, 'B', 1732, 15.2, 'QUW', 'WLNO', 06109, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 7, 1, 'GV', NULL, 'B', 3198, 16.3, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL), (2020, 7, 1, 'GV', NULL, 'B', 3198, 16.3, 'WEI', 'OEST', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 7, 2, 'GV', NULL, 'B', 2134, 15.4, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL), (2020, 7, 2, 'GV', NULL, 'B', 2134, 15.4, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 8, 1, 'GV', NULL, 'B', 2901, 16.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL), (2020, 8, 1, 'GV', NULL, 'B', 2901, 16.0, 'WEI', 'OEST', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 8, 2, 'GV', NULL, 'B', 3321, 17.3, 'KAB', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL), (2020, 8, 2, 'GV', NULL, 'B', 3321, 17.3, 'KAB', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 9, 1, 'WR', NULL, 'B', 3998, 17.2, 'KAB', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL), (2020, 9, 1, 'WR', NULL, 'B', 3998, 17.2, 'KAB', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2020, 10, 1, 'ZW', NULL, NULL, 1212, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL), (2020, 10, 1, 'ZW', NULL, NULL, 1212, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),

View File

@ -70,7 +70,7 @@ INSERT INTO member (mgnr, given_name, name, zwstid, volllieferant, buchführend,
(101, 'Max', 'Mustermann', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 1', 06109, 'AT811234567890123457', '0123463', NULL ), (101, 'Max', 'Mustermann', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 1', 06109, 'AT811234567890123457', '0123463', NULL ),
(102, 'Wernhardt', 'Weinbauer', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 2', 06109, 'AT541234567890123458', '0123471', 'ATU12345684'), (102, 'Wernhardt', 'Weinbauer', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 2', 06109, 'AT541234567890123458', '0123471', 'ATU12345684'),
(103, 'Matthäus', 'Musterbauer', 'X', FALSE, FALSE, 40, 212005138, 'Brünner Straße 10', 15224, 'AT271234567890123459', '0123480', NULL ), (103, 'Matthäus', 'Musterbauer', 'X', FALSE, FALSE, 40, 212005138, 'Brünner Straße 10', 15224, 'AT271234567890123459', '0123480', NULL ),
(104, 'Waltraud', 'Winzer', 'X', FALSE, FALSE, 40, 212005138, 'Wiener Straße 15', 15224, 'AT971234567890123460', '0123498', 'ATU12345693'); (104, 'Waltraud', 'Winzer', 'X', FALSE, TRUE , 40, 212005138, 'Wiener Straße 15', 15224, 'AT971234567890123460', '0123498', 'ATU12345693');
INSERT INTO member_billing_address (mgnr, name, country, postal_dest, address) VALUES INSERT INTO member_billing_address (mgnr, name, country, postal_dest, address) VALUES
(102, 'W&B Weinbauer GesbR', 40, 222303524, 'Winzerstraße 2'), (102, 'W&B Weinbauer GesbR', 40, 222303524, 'Winzerstraße 2'),

View File

@ -0,0 +1,11 @@
-- deletes for ServiceTests
DELETE FROM credit;
DELETE FROM payment_variant;
DELETE FROM delivery;
DELETE FROM season;
DELETE FROM area_commitment;
DELETE FROM area_commitment_type;
DELETE FROM member WHERE mgnr >= 200;
DELETE FROM wine_cultivation;
DELETE FROM wine_attribute;

View File

@ -0,0 +1,135 @@
-- inserts for ServiceTests
INSERT INTO wine_cultivation (cultid, name, description) VALUES
('B', 'Bio', 'AT-BIO-302');
INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lower) VALUES
('K', 'Kabinett', TRUE, NULL, FALSE, 0);
INSERT INTO member (mgnr, given_name, name, zwstid, volllieferant, buchführend, country, postal_dest, address, default_kgnr, iban, lfbis_nr, ustid_nr) VALUES
(201, 'Theodor', 'Testbauer', 'X', FALSE, FALSE, 40, 222303524, 'Teststraße 1', 06109, 'AT811234567890123457', '0123463', NULL ),
(202, 'Thomas', 'Testmann', 'X', FALSE, FALSE, 40, 222303524, 'Teststraße 2', 06109, 'AT541234567890123458', '0123471', 'ATU12345684'),
(203, 'Tina', 'Testwinzer', 'X', FALSE, FALSE, 40, 212005138, 'Teststraße 3', 15224, 'AT271234567890123459', '0123480', NULL ),
(204, 'Trude', 'Testerin', 'X', FALSE, TRUE , 40, 212005138, 'Teststraße 4', 15224, 'AT971234567890123460', '0123498', 'ATU12345693');
INSERT INTO member_billing_address (mgnr, name, country, postal_dest, address) VALUES
(202, 'T&T Testmann GesbR', 40, 222303524, 'Teststraße 11'),
(203, 'Testwinzer GesbR', 40, 212005138, 'Teststraße 3' ),
(204, 'Trauben Trude GmbH', 40, 212205137, 'Teststraße 12');
INSERT INTO member_telephone_number (mgnr, nr, type, number, comment) VALUES
(203, 1, 'mobile', '+43 664 987654321', NULL),
(204, 1, 'mobile', '+43 664 123456789', NULL);
INSERT INTO member_email_address (mgnr, nr, address, comment) VALUES
(203, 1, 'tina@winzer.com', NULL),
(204, 1, 'trude@trauben.at', NULL);
INSERT INTO area_commitment_type (vtrgid, sortid, attrid, disc, min_kg_per_ha, penalty_per_kg, penalty_amount, penalty_none) VALUES
('GV', 'GV', NULL, NULL, 5000, 500, NULL, NULL);
INSERT INTO area_commitment (fbnr, mgnr, vtrgid, cultid, area, kgnr, gstnr, rdnr, year_from, year_to) VALUES
( 1, 203, 'GV', NULL, 10000, 15224, '321/9', NULL, NULL, NULL),
( 2, 204, 'GV', NULL, 10000, 15224, '123/1', NULL, 2000, 2019),
( 3, 204, 'GV', NULL, 10000, 15224, '123/2', NULL, 2025, 2030),
( 4, 204, 'GV', NULL, 10000, 15224, '123/3', NULL, 2021, 2031);
INSERT INTO season (year, currency, min_kg_per_bs, max_kg_per_bs, penalty_per_kg, penalty_amount, penalty_none, start_date, end_date) VALUES
(2021, 'EUR', 2000, 4000, NULL, NULL, NULL, NULL, NULL),
(2022, 'EUR', 2000, 4000, NULL, NULL, NULL, NULL, NULL),
(2023, 'EUR', 2000, 4000, NULL, NULL, NULL, NULL, NULL);
INSERT INTO modifier (year, modid, ordering, name, abs, rel, active) VALUES
(2021, 'S', 0, 'Geschädigte Trauben', NULL, -0.1, TRUE),
(2022, 'S', 0, 'Geschädigte Trauben', NULL, -0.1, TRUE);
INSERT INTO delivery (mgnr, year, did, date, time, zwstid, lnr) VALUES
(203, 2021, 1, '2021-10-01', NULL, 'X', 1),
(204, 2021, 2, '2021-10-01', NULL, 'X', 2),
(101, 2022, 1, '2022-09-29', NULL, 'X', 1),
(101, 2022, 2, '2022-09-30', NULL, 'X', 1),
(101, 2023, 1, '2023-10-01', NULL, 'X', 1),
(101, 2023, 2, '2023-10-01', NULL, 'X', 2),
(101, 2023, 3, '2023-10-01', NULL, 'X', 3),
(101, 2023, 4, '2023-10-01', NULL, 'X', 4),
(101, 2023, 5, '2023-10-02', NULL, 'X', 1),
(101, 2023, 6, '2023-10-03', NULL, 'X', 1),
(101, 2023, 7, '2023-10-04', NULL, 'X', 1),
(101, 2023, 8, '2023-10-05', NULL, 'X', 1),
(101, 2023, 9, '2023-10-06', NULL, 'X', 1),
(101, 2023, 10, '2023-10-06', NULL, 'X', 2),
(101, 2023, 11, '2023-10-07', NULL, 'X', 1),
(101, 2023, 12, '2023-10-07', NULL, 'X', 2),
(101, 2023, 13, '2023-10-08', NULL, 'X', 1),
(101, 2023, 14, '2023-10-08', NULL, 'X', 2),
(101, 2023, 15, '2023-10-09', NULL, 'X', 1),
(101, 2023, 16, '2023-10-09', NULL, 'X', 2),
(101, 2023, 17, '2023-10-10', NULL, 'X', 1),
(101, 2023, 18, '2023-10-10', NULL, 'X', 2),
(101, 2023, 19, '2023-10-10', NULL, 'X', 3);
INSERT INTO delivery_part (year, did, dpnr, sortid, attrid, cultid, weight, kmw, qualid, hkid, kgnr, net_weight, manual_weighing, spl_check, scale_id, weighing_data, weighing_reason) VALUES
(2021, 1, 1, 'GV', NULL, NULL, 3500, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2021, 2, 1, 'GV', NULL, NULL, 4000, 17.0, 'KAB', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2021, 2, 2, 'GV', NULL, NULL, 4000, 16.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2022, 1, 1, 'GV', NULL, NULL, 3700, 16.5, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2022, 1, 2, 'GV', NULL, NULL, 3700, 16.5, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 1, 1, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 1, 2, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 1, 3, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 2, 1, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 2, 2, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 2, 3, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 3, 1, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 3, 2, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 3, 3, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 4, 1, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 4, 2, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 4, 3, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 5, 1, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 5, 2, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 5, 3, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 6, 1, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 6, 2, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 6, 3, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 7, 1, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 7, 2, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 7, 3, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 8, 1, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 8, 2, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 8, 3, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 9, 1, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 9, 2, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 9, 3, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 10, 1, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 11, 1, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 11, 2, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 11, 3, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 12, 1, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 13, 1, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 13, 2, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 13, 3, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 14, 1, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 15, 1, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 15, 2, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 15, 3, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 16, 1, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 17, 1, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 18, 1, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL),
(2023, 19, 1, 'GV', NULL, NULL, 1000, 15.0, 'QUW', 'WLNO', 15224, TRUE, FALSE, FALSE, NULL, NULL, NULL);
INSERT INTO delivery_part_modifier (year, did, dpnr, modid) VALUES
(2021, 1, 1, 'S'),
(2021, 2, 2, 'S'),
(2022, 1, 2, 'S');
INSERT INTO payment_variant (year, avnr, name, date, transfer_date, test_variant, calc_time, comment, data) VALUES
(2021, 1, 'Probevariante', '2022-01-15', '2022-01-15', TRUE, NULL, NULL, '{"mode":"elwig","version":1,"payment":0.5,"curves":[]}');
INSERT INTO payment_member (year, avnr, mgnr, net_amount) VALUES
(2021, 1, 203, 10000000),
(2021, 1, 204, 10000000);
INSERT INTO credit (year, tgnr, mgnr, avnr, net_amount, prev_net_amount, vat, modifiers, prev_modifiers) VALUES
(2021, 1, 203, 1, 10000000, 0, 0.1, 0, 0),
(2021, 2, 204, 1, 10000000, 0, 0.1, 0, 0);

View File

@ -0,0 +1,847 @@
using Elwig.Helpers;
using Elwig.Models.Entities;
using Elwig.Services;
using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
namespace Tests.ServiceTests {
[TestFixture]
public class DeliveryServiceTest {
private static async Task InitViewModel(DeliveryAdminViewModel vm) {
using var ctx = new AppDbContext();
vm.MemberSource = await ctx.Members.ToListAsync();
vm.BranchSource = await ctx.Branches.ToListAsync();
vm.WineVarSource = await ctx.WineVarieties.ToListAsync();
List<object> attrs = (await ctx.WineAttributes.ToListAsync()).Cast<object>().ToList();
attrs.Insert(0, new NullItem());
vm.WineAttrSource = attrs;
List<object> cults = (await ctx.WineCultivations.ToListAsync()).Cast<object>().ToList();
cults.Insert(0, new NullItem());
vm.WineCultSource = cults;
vm.WineQualityLevelSource = await ctx.WineQualityLevels.ToListAsync();
vm.WineOriginSource = await ctx.WineOrigins.ToListAsync();
vm.WineKgSource = await ctx.Katastralgemeinden.ToListAsync();
vm.ModifiersSource = await ctx.Modifiers.Where(m => m.Year == 2022).ToListAsync();
}
private static async Task<Delivery?> GetDelivery(string lsnr) {
using var ctx = new AppDbContext();
return await ctx.Deliveries
.Where(d => d.LsNr == lsnr)
.Include(d => d.Parts)
.ThenInclude(p => p.PartModifiers)
.ThenInclude(m => m.Modifier)
.AsSplitQuery()
.FirstOrDefaultAsync();
}
private static DeliveryPart[] GetParts(Delivery d) {
return [.. d.Parts.OrderBy(p => p.DPNr)];
}
[Test]
public async Task TestCreate_01_Minimal() {
var vm = new DeliveryAdminViewModel();
await InitViewModel(vm);
vm.Date = "01.10.2022";
vm.Branch = vm.BranchSource.First();
vm.MgNr = 101;
vm.Member = vm.MemberSource.First(m => m.MgNr == vm.MgNr);
vm.SortId = "GV";
vm.WineVar = vm.WineVarSource.First(v => v.SortId == vm.SortId);
vm.GradationKmw = 15.0;
vm.GradationOe = 73;
vm.Weight = 1234;
vm.IsManualWeighing = true;
vm.IsNetWeight = false;
vm.WineQualityLevel = vm.WineQualityLevelSource.First(l => l.QualId == "QUW");
vm.WineOrigin = vm.WineOriginSource.First(o => o.HkId == "WLNO");
var p1 = await vm.UpdateDeliveryPart(2022, null, null, false, false, true);
Assert.That(p1.Delivery.LsNr, Is.EqualTo("20221001X001"));
var d = await GetDelivery("20221001X001");
Assert.That(d, Is.Not.Null);
Assert.That(d.Parts, Has.Count.EqualTo(1));
var p = d.Parts.First();
Assert.That(p, Is.Not.Null);
Assert.Multiple(() => {
Assert.That(d.LsNr, Is.EqualTo("20221001X001"));
Assert.That(d.Date, Is.EqualTo(new DateOnly(2022, 10, 1)));
Assert.That(d.ZwstId, Is.EqualTo("X"));
Assert.That(d.MgNr, Is.EqualTo(101));
Assert.That(p.SortId, Is.EqualTo("GV"));
Assert.That(p.AttrId, Is.Null);
Assert.That(p.CultId, Is.Null);
Assert.That(p.Kmw, Is.EqualTo(15.0));
Assert.That(p.Oe, Is.EqualTo(73));
Assert.That(p.QualId, Is.EqualTo("QUW"));
Assert.That(p.Weight, Is.EqualTo(1234));
Assert.That(p.IsNetWeight, Is.False);
Assert.That(p.IsManualWeighing, Is.True);
Assert.That(p.HkId, Is.EqualTo("WLNO"));
});
vm = new DeliveryAdminViewModel();
await InitViewModel(vm);
Assert.DoesNotThrow(() => {
vm.FillInputs(d);
vm.FillInputs(p);
});
Assert.Multiple(() => {
Assert.That(vm.LsNr, Is.EqualTo("20221001X001"));
Assert.That(vm.Date, Is.EqualTo("01.10.2022"));
Assert.That(vm.Branch?.ZwstId, Is.EqualTo("X"));
Assert.That(vm.MgNr, Is.EqualTo(101));
Assert.That(vm.SortId, Is.EqualTo("GV"));
Assert.That(vm.WineAttr?.AttrId, Is.Null);
Assert.That(vm.WineCult?.CultId, Is.Null);
Assert.That(vm.GradationKmw, Is.EqualTo(15.0));
Assert.That(vm.WineQualityLevel?.QualId, Is.EqualTo("QUW"));
Assert.That(vm.Weight, Is.EqualTo(1234));
Assert.That(vm.IsNetWeight, Is.False);
Assert.That(vm.IsManualWeighing, Is.True);
Assert.That(vm.WineOrigin?.HkId, Is.EqualTo("WLNO"));
});
}
[Test]
public async Task TestCreate_02_Advanced() {
var vm = new DeliveryAdminViewModel();
await InitViewModel(vm);
vm.Date = "02.10.2022";
vm.Branch = vm.BranchSource.First();
vm.MgNr = 102;
vm.Member = vm.MemberSource.First(m => m.MgNr == vm.MgNr);
vm.SortId = "ZW";
vm.WineVar = vm.WineVarSource.First(v => v.SortId == vm.SortId);
vm.WineAttr = vm.WineAttrSource.Skip(1).First() as WineAttr;
vm.WineCult = vm.WineCultSource.Skip(1).First() as WineCult;
vm.GradationKmw = 15.9;
vm.GradationOe = 78;
vm.Weight = 3456;
vm.IsManualWeighing = false;
vm.IsNetWeight = true;
vm.WeighingData = "{}";
vm.WineQualityLevel = vm.WineQualityLevelSource.First(l => l.QualId == "QUW");
vm.WineOrigin = vm.WineOriginSource.First(o => o.HkId == "WLNO");
vm.Modifiers.Add(vm.ModifiersSource.First());
var p1 = await vm.UpdateDeliveryPart(2022, null, null, false, false, true);
Assert.That(p1.Delivery.LsNr, Is.EqualTo("20221002X001"));
var d = await GetDelivery("20221002X001");
Assert.That(d, Is.Not.Null);
Assert.That(d.Parts, Has.Count.EqualTo(1));
var p = d.Parts.First();
Assert.That(p, Is.Not.Null);
Assert.Multiple(() => {
Assert.That(d.LsNr, Is.EqualTo("20221002X001"));
Assert.That(d.Date, Is.EqualTo(new DateOnly(2022, 10, 2)));
Assert.That(d.ZwstId, Is.EqualTo("X"));
Assert.That(d.MgNr, Is.EqualTo(102));
Assert.That(p.SortId, Is.EqualTo("ZW"));
Assert.That(p.AttrId, Is.Not.Null);
Assert.That(p.CultId, Is.Not.Null);
Assert.That(p.Kmw, Is.EqualTo(15.9));
Assert.That(p.Oe, Is.EqualTo(78));
Assert.That(p.QualId, Is.EqualTo("QUW"));
Assert.That(p.Weight, Is.EqualTo(3456));
Assert.That(p.IsNetWeight, Is.True);
Assert.That(p.IsManualWeighing, Is.False);
Assert.That(p.WeighingData, Is.EqualTo("{}"));
Assert.That(p.HkId, Is.EqualTo("WLNO"));
Assert.That(p.Modifiers.Count(), Is.EqualTo(1));
});
vm = new DeliveryAdminViewModel();
await InitViewModel(vm);
Assert.DoesNotThrow(() => {
vm.FillInputs(d);
vm.FillInputs(p);
});
Assert.Multiple(() => {
Assert.That(vm.LsNr, Is.EqualTo("20221002X001"));
Assert.That(vm.Date, Is.EqualTo("02.10.2022"));
Assert.That(vm.Branch?.ZwstId, Is.EqualTo("X"));
Assert.That(vm.MgNr, Is.EqualTo(102));
Assert.That(vm.SortId, Is.EqualTo("ZW"));
Assert.That(vm.WineAttr?.AttrId, Is.Not.Null);
Assert.That(vm.WineCult?.CultId, Is.Not.Null);
Assert.That(vm.GradationKmw, Is.EqualTo(15.9));
Assert.That(vm.WineQualityLevel?.QualId, Is.EqualTo("QUW"));
Assert.That(vm.Weight, Is.EqualTo(3456));
Assert.That(vm.IsNetWeight, Is.True);
Assert.That(vm.IsManualWeighing, Is.False);
Assert.That(vm.WeighingData, Is.EqualTo("{}"));
Assert.That(vm.WineOrigin?.HkId, Is.EqualTo("WLNO"));
Assert.That(vm.Modifiers, Has.Count.EqualTo(1));
});
}
[Test]
public async Task TestCreate_03_TwoParts() {
var vm = new DeliveryAdminViewModel();
await InitViewModel(vm);
vm.Date = "03.10.2022";
vm.Branch = vm.BranchSource.First();
vm.MgNr = 101;
vm.Member = vm.MemberSource.First(m => m.MgNr == vm.MgNr);
vm.SortId = "GV";
vm.WineVar = vm.WineVarSource.First(v => v.SortId == vm.SortId);
vm.GradationKmw = 15.0;
vm.GradationOe = 73;
vm.WineQualityLevel = vm.WineQualityLevelSource.First(l => l.QualId == "QUW");
vm.Weight = 1234;
vm.IsManualWeighing = true;
vm.IsNetWeight = true;
vm.WineOrigin = vm.WineOriginSource.First(o => o.HkId == "WLNO");
var p1 = await vm.UpdateDeliveryPart(2022, null, null, false, false, true);
Assert.That(p1.Delivery.LsNr, Is.EqualTo("20221003X001"));
vm.FillInputs(p1.Delivery);
vm.SortId = "WR";
vm.WineVar = vm.WineVarSource.First(v => v.SortId == vm.SortId);
vm.GradationKmw = 18.0;
vm.GradationOe = 89;
vm.WineQualityLevel = vm.WineQualityLevelSource.First(l => l.QualId == "KAB");
vm.Weight = 2345;
var p2 = await vm.UpdateDeliveryPart(p1.Year, p1.DId, null, false, false, true);
Assert.That(p2.Delivery.LsNr, Is.EqualTo("20221003X001"));
var d = await GetDelivery("20221003X001");
Assert.That(d, Is.Not.Null);
Assert.Multiple(() => {
Assert.That(d.Parts, Has.Count.EqualTo(2));
Assert.That(d.MgNr, Is.EqualTo(101));
});
var p = d.Parts.First();
Assert.That(p, Is.Not.Null);
Assert.Multiple(() => {
Assert.That(p.DPNr, Is.EqualTo(1));
Assert.That(p.SortId, Is.EqualTo("GV"));
});
p = d.Parts.Skip(1).First();
Assert.That(p, Is.Not.Null);
Assert.Multiple(() => {
Assert.That(p.DPNr, Is.EqualTo(2));
Assert.That(p.SortId, Is.EqualTo("WR"));
});
}
[Test]
public async Task TestCreate_04_ChangeMember() {
var vm = new DeliveryAdminViewModel();
await InitViewModel(vm);
vm.Date = "04.10.2022";
vm.Branch = vm.BranchSource.First();
vm.MgNr = 101;
vm.Member = vm.MemberSource.First(m => m.MgNr == vm.MgNr);
vm.SortId = "GV";
vm.WineVar = vm.WineVarSource.First(v => v.SortId == vm.SortId);
vm.GradationKmw = 15.0;
vm.GradationOe = 73;
vm.WineQualityLevel = vm.WineQualityLevelSource.First(l => l.QualId == "QUW");
vm.Weight = 1234;
vm.IsManualWeighing = true;
vm.IsNetWeight = true;
vm.WineOrigin = vm.WineOriginSource.First(o => o.HkId == "WLNO");
var p1 = await vm.UpdateDeliveryPart(2022, null, null, false, false, true);
Assert.That(p1.Delivery.LsNr, Is.EqualTo("20221004X001"));
vm.FillInputs(p1.Delivery);
vm.MgNr = 102;
vm.Member = vm.MemberSource.First(m => m.MgNr == vm.MgNr);
vm.SortId = "GV";
vm.WineVar = vm.WineVarSource.First(v => v.SortId == vm.SortId);
vm.GradationKmw = 15.0;
vm.GradationOe = 73;
vm.WineQualityLevel = vm.WineQualityLevelSource.First(l => l.QualId == "QUW");
vm.Weight = 1234;
vm.IsManualWeighing = true;
vm.IsNetWeight = true;
vm.WineOrigin = vm.WineOriginSource.First(o => o.HkId == "WLNO");
var p2 = await vm.UpdateDeliveryPart(p1.Year, p1.DId, null, false, false, true);
Assert.That(p2.Delivery.LsNr, Is.EqualTo("20221004X001"));
var d = await GetDelivery("20221004X001");
Assert.That(d, Is.Not.Null);
Assert.Multiple(() => {
Assert.That(d.Parts, Has.Count.EqualTo(2));
Assert.That(d.MgNr, Is.EqualTo(102));
});
}
[Test]
public async Task TestCreate_05_LNrChanged() {
var vm = new DeliveryAdminViewModel();
await InitViewModel(vm);
vm.Date = "29.09.2022";
vm.Branch = vm.BranchSource.First();
vm.LsNr = "20220929X001"; // "old" LNr, simulated change in DB
vm.MgNr = 101;
vm.Member = vm.MemberSource.First(m => m.MgNr == vm.MgNr);
vm.SortId = "GV";
vm.WineVar = vm.WineVarSource.First(v => v.SortId == vm.SortId);
vm.GradationKmw = 15.0;
vm.GradationOe = 73;
vm.WineQualityLevel = vm.WineQualityLevelSource.First(l => l.QualId == "QUW");
vm.Weight = 1234;
vm.IsManualWeighing = true;
vm.IsNetWeight = true;
vm.WineOrigin = vm.WineOriginSource.First(o => o.HkId == "WLNO");
var p1 = await vm.UpdateDeliveryPart(2022, null, null, false, false, true);
Assert.That(p1.Delivery.LsNr, Is.EqualTo("20220929X002"));
var d = GetDelivery("20220929X002");
Assert.That(d, Is.Not.Null);
}
[Test]
public async Task TestUpdate_01_Simple() {
var vm = new DeliveryAdminViewModel();
await InitViewModel(vm);
var d = await GetDelivery("20231010X001");
Assert.That(d, Is.Not.Null);
var p = d.Parts.First();
Assert.That(p, Is.Not.Null);
Assert.DoesNotThrow(() => {
vm.FillInputs(d);
vm.FillInputs(p);
});
vm.SortId = "WR";
vm.WineVar = vm.WineVarSource.First(v => v.SortId == vm.SortId);
vm.GradationKmw = 15.9;
vm.GradationOe = 79;
Assert.DoesNotThrowAsync(async () => await DeliveryService.UpdateDeliveryPart(vm, p.Year, p.DId, p.DPNr, false, false, true));
d = await GetDelivery("20231010X001");
Assert.That(d, Is.Not.Null);
p = d.Parts.First();
Assert.That(p, Is.Not.Null);
vm = new DeliveryAdminViewModel();
await InitViewModel(vm);
Assert.DoesNotThrow(() => {
vm.FillInputs(d);
vm.FillInputs(p);
});
Assert.Multiple(() => {
Assert.That(vm.SortId, Is.EqualTo("WR"));
Assert.That(vm.GradationKmw, Is.EqualTo(15.9));
});
}
[Test]
public async Task TestUpdate_02_Member() {
var vm = new DeliveryAdminViewModel();
await InitViewModel(vm);
var d = await GetDelivery("20231010X002");
Assert.That(d, Is.Not.Null);
var p = d.Parts.First();
Assert.That(p, Is.Not.Null);
Assert.DoesNotThrow(() => {
vm.FillInputs(d);
vm.FillInputs(p);
});
Assert.That(d.MgNr, Is.EqualTo(101));
vm.SortId = "GV";
vm.WineVar = vm.WineVarSource.First(v => v.SortId == vm.SortId);
vm.MgNr = 102;
vm.Member = vm.MemberSource.First(m => m.MgNr == vm.MgNr);
Assert.DoesNotThrowAsync(async () => await DeliveryService.UpdateDeliveryPart(vm, p.Year, p.DId, p.DPNr, false, false, true));
d = await GetDelivery("20231010X002");
Assert.That(d, Is.Not.Null);
Assert.That(d.MgNr, Is.EqualTo(102));
}
[Test]
public async Task TestUpdate_03_DateAndTime() {
var vm = new DeliveryAdminViewModel();
await InitViewModel(vm);
var d = await GetDelivery("20231015X001");
Assert.That(d, Is.Null);
d = await GetDelivery("20231010X003");
Assert.That(d, Is.Not.Null);
var p = d.Parts.First();
Assert.That(p, Is.Not.Null);
Assert.DoesNotThrow(() => {
vm.FillInputs(d);
vm.FillInputs(p);
});
Assert.That(d.MgNr, Is.EqualTo(101));
vm.SortId = "GV";
vm.WineVar = vm.WineVarSource.First(v => v.SortId == vm.SortId);
vm.Date = "15.10.2023";
vm.Time = "12:00";
Assert.DoesNotThrowAsync(async () => await DeliveryService.UpdateDeliveryPart(vm, p.Year, p.DId, p.DPNr, true, true, false));
d = await GetDelivery("20231010X003");
Assert.That(d, Is.Null);
d = await GetDelivery("20231015X001");
Assert.That(d, Is.Not.Null);
}
[Test]
public async Task TestSplit_01_Depreciate_One() {
var d = await GetDelivery("20231001X001");
Assert.That(d, Is.Not.Null);
Assert.Multiple(() => {
var ps = GetParts(d);
Assert.That(ps, Has.Length.EqualTo(3));
Assert.That(ps[0].Weight, Is.EqualTo(1000));
Assert.That(ps[0].QualId, Is.Not.EqualTo("WEI"));
Assert.That(ps[1].QualId, Is.Not.EqualTo("WEI"));
Assert.That(ps[2].QualId, Is.Not.EqualTo("WEI"));
});
Assert.DoesNotThrowAsync(async () => await DeliveryService.DepreciateDelivery(d.Year, d.DId, [1000, 0, 0]));
d = await GetDelivery("20231001X001");
Assert.That(d, Is.Not.Null);
Assert.Multiple(() => {
var ps = GetParts(d);
Assert.That(ps, Has.Length.EqualTo(3));
Assert.That(ps[0].Weight, Is.EqualTo(1000));
Assert.That(ps[0].QualId, Is.EqualTo("WEI"));
Assert.That(ps[1].QualId, Is.Not.EqualTo("WEI"));
Assert.That(ps[2].QualId, Is.Not.EqualTo("WEI"));
});
}
[Test]
public async Task TestSplit_02_Depreciate_Partial() {
var d = await GetDelivery("20231001X002");
Assert.That(d, Is.Not.Null);
Assert.Multiple(() => {
var ps = GetParts(d);
Assert.That(ps, Has.Length.EqualTo(3));
Assert.That(ps[0].Weight, Is.EqualTo(1000));
Assert.That(ps[0].QualId, Is.Not.EqualTo("WEI"));
Assert.That(ps[1].QualId, Is.Not.EqualTo("WEI"));
Assert.That(ps[2].QualId, Is.Not.EqualTo("WEI"));
});
Assert.DoesNotThrowAsync(async () => await DeliveryService.DepreciateDelivery(d.Year, d.DId, [600, 0, 0]));
d = await GetDelivery("20231001X002");
Assert.That(d, Is.Not.Null);
Assert.Multiple(() => {
var ps = GetParts(d);
Assert.That(ps, Has.Length.EqualTo(4));
Assert.That(ps[0].Weight, Is.EqualTo(400));
Assert.That(ps[0].QualId, Is.Not.EqualTo("WEI"));
Assert.That(ps[1].QualId, Is.Not.EqualTo("WEI"));
Assert.That(ps[2].QualId, Is.Not.EqualTo("WEI"));
Assert.That(ps[3].Weight, Is.EqualTo(600));
Assert.That(ps[3].QualId, Is.EqualTo("WEI"));
});
}
[Test]
public async Task TestSplit_03_Depreciate_Mixed() {
var d = await GetDelivery("20231001X003");
Assert.That(d, Is.Not.Null);
Assert.Multiple(() => {
var ps = GetParts(d);
Assert.That(ps, Has.Length.EqualTo(3));
Assert.That(ps[0].Weight, Is.EqualTo(1000));
Assert.That(ps[0].QualId, Is.Not.EqualTo("WEI"));
Assert.That(ps[1].Weight, Is.EqualTo(1000));
Assert.That(ps[1].QualId, Is.Not.EqualTo("WEI"));
Assert.That(ps[2].QualId, Is.Not.EqualTo("WEI"));
});
Assert.DoesNotThrowAsync(async () => await DeliveryService.DepreciateDelivery(d.Year, d.DId, [1000, 700, -5]));
d = await GetDelivery("20231001X003");
Assert.That(d, Is.Not.Null);
Assert.Multiple(() => {
var ps = GetParts(d);
Assert.That(ps, Has.Length.EqualTo(4));
Assert.That(ps[0].Weight, Is.EqualTo(1000));
Assert.That(ps[0].QualId, Is.EqualTo("WEI"));
Assert.That(ps[1].Weight, Is.EqualTo(300));
Assert.That(ps[1].QualId, Is.Not.EqualTo("WEI"));
Assert.That(ps[2].Weight, Is.EqualTo(1000));
Assert.That(ps[2].QualId, Is.Not.EqualTo("WEI"));
Assert.That(ps[3].Weight, Is.EqualTo(700));
Assert.That(ps[3].QualId, Is.EqualTo("WEI"));
});
}
[Test]
public async Task TestSplit_04_Depreciate_Complete() {
var d = await GetDelivery("20231001X004");
Assert.That(d, Is.Not.Null);
Assert.Multiple(() => {
var ps = GetParts(d);
Assert.That(ps, Has.Length.EqualTo(3));
Assert.That(ps[0].Weight, Is.EqualTo(1000));
Assert.That(ps[0].QualId, Is.Not.EqualTo("WEI"));
Assert.That(ps[1].Weight, Is.EqualTo(1000));
Assert.That(ps[1].QualId, Is.Not.EqualTo("WEI"));
Assert.That(ps[2].Weight, Is.EqualTo(1000));
Assert.That(ps[2].QualId, Is.Not.EqualTo("WEI"));
});
Assert.DoesNotThrowAsync(async () => await DeliveryService.DepreciateDelivery(d.Year, d.DId, [1000, 1100, int.MaxValue]));
d = await GetDelivery("20231001X004");
Assert.That(d, Is.Not.Null);
Assert.Multiple(() => {
var ps = GetParts(d);
Assert.That(ps, Has.Length.EqualTo(3));
Assert.That(ps[0].Weight, Is.EqualTo(1000));
Assert.That(ps[0].QualId, Is.EqualTo("WEI"));
Assert.That(ps[1].Weight, Is.EqualTo(1000));
Assert.That(ps[1].QualId, Is.EqualTo("WEI"));
Assert.That(ps[2].Weight, Is.EqualTo(1000));
Assert.That(ps[2].QualId, Is.EqualTo("WEI"));
});
}
[Test]
public async Task TestSplit_05_OtherMember_One() {
var d1 = await GetDelivery("20231002X001");
var d2 = await GetDelivery("20231002X002");
Assert.Multiple(() => {
Assert.That(d1, Is.Not.Null);
Assert.That(d2, Is.Null);
});
Assert.Multiple(() => {
var ps1 = GetParts(d1);
Assert.That(d1.MgNr, Is.EqualTo(101));
Assert.That(ps1, Has.Length.EqualTo(3));
Assert.That(ps1[0].Weight, Is.EqualTo(1000));
Assert.That(ps1[1].Weight, Is.EqualTo(1000));
Assert.That(ps1[2].Weight, Is.EqualTo(1000));
});
Assert.DoesNotThrowAsync(async () => await DeliveryService.SplitDeliveryToMember(d1.Year, d1.DId, [1000, 0, 0], 102));
d1 = await GetDelivery("20231002X001");
d2 = await GetDelivery("20231002X002");
Assert.Multiple(() => {
Assert.That(d1, Is.Not.Null);
Assert.That(d2, Is.Not.Null);
});
Assert.Multiple(() => {
var ps1 = GetParts(d1);
var ps2 = GetParts(d2);
Assert.That(d1.MgNr, Is.EqualTo(101));
Assert.That(d2.MgNr, Is.EqualTo(102));
Assert.That(ps1, Has.Length.EqualTo(2));
Assert.That(ps1[0].DPNr, Is.EqualTo(2));
Assert.That(ps1[0].Weight, Is.EqualTo(1000));
Assert.That(ps1[1].DPNr, Is.EqualTo(3));
Assert.That(ps1[1].Weight, Is.EqualTo(1000));
Assert.That(ps2, Has.Length.EqualTo(1));
Assert.That(ps2[0].DPNr, Is.EqualTo(1));
Assert.That(ps2[0].Weight, Is.EqualTo(1000));
});
}
[Test]
public async Task TestSplit_06_OtherMember_Partial() {
var d1 = await GetDelivery("20231003X001");
var d2 = await GetDelivery("20231003X002");
Assert.Multiple(() => {
Assert.That(d1, Is.Not.Null);
Assert.That(d2, Is.Null);
});
Assert.Multiple(() => {
var ps1 = GetParts(d1);
Assert.That(d1.MgNr, Is.EqualTo(101));
Assert.That(ps1, Has.Length.EqualTo(3));
Assert.That(ps1[0].Weight, Is.EqualTo(1000));
Assert.That(ps1[1].Weight, Is.EqualTo(1000));
Assert.That(ps1[2].Weight, Is.EqualTo(1000));
});
Assert.DoesNotThrowAsync(async () => await DeliveryService.SplitDeliveryToMember(d1.Year, d1.DId, [400, -1, -2], 102));
d1 = await GetDelivery("20231003X001");
d2 = await GetDelivery("20231003X002");
Assert.Multiple(() => {
Assert.That(d1, Is.Not.Null);
Assert.That(d2, Is.Not.Null);
});
Assert.Multiple(() => {
var ps1 = GetParts(d1);
var ps2 = GetParts(d2);
Assert.That(d1.MgNr, Is.EqualTo(101));
Assert.That(d2.MgNr, Is.EqualTo(102));
Assert.That(ps1, Has.Length.EqualTo(3));
Assert.That(ps1[0].Weight, Is.EqualTo(600));
Assert.That(ps1[1].Weight, Is.EqualTo(1000));
Assert.That(ps1[2].Weight, Is.EqualTo(1000));
Assert.That(ps2, Has.Length.EqualTo(1));
Assert.That(ps2[0].Weight, Is.EqualTo(400));
});
}
[Test]
public async Task TestSplit_07_OtherMember_Mixed() {
var d1 = await GetDelivery("20231004X001");
var d2 = await GetDelivery("20231004X002");
Assert.Multiple(() => {
Assert.That(d1, Is.Not.Null);
Assert.That(d2, Is.Null);
});
Assert.Multiple(() => {
var ps1 = GetParts(d1);
Assert.That(d1.MgNr, Is.EqualTo(101));
Assert.That(ps1, Has.Length.EqualTo(3));
Assert.That(ps1[0].Weight, Is.EqualTo(1000));
Assert.That(ps1[1].Weight, Is.EqualTo(1000));
Assert.That(ps1[2].Weight, Is.EqualTo(1000));
});
Assert.DoesNotThrowAsync(async () => await DeliveryService.SplitDeliveryToMember(d1.Year, d1.DId, [200, 1000, int.MinValue], 102));
d1 = await GetDelivery("20231004X001");
d2 = await GetDelivery("20231004X002");
Assert.Multiple(() => {
Assert.That(d1, Is.Not.Null);
Assert.That(d2, Is.Not.Null);
});
Assert.Multiple(() => {
var ps1 = GetParts(d1);
var ps2 = GetParts(d2);
Assert.That(d1.MgNr, Is.EqualTo(101));
Assert.That(d2.MgNr, Is.EqualTo(102));
Assert.That(ps1, Has.Length.EqualTo(2));
Assert.That(ps1[0].DPNr, Is.EqualTo(1));
Assert.That(ps1[0].Weight, Is.EqualTo(800));
Assert.That(ps1[1].DPNr, Is.EqualTo(3));
Assert.That(ps1[1].Weight, Is.EqualTo(1000));
Assert.That(ps2, Has.Length.EqualTo(2));
Assert.That(ps2[0].DPNr, Is.EqualTo(1));
Assert.That(ps2[0].Weight, Is.EqualTo(200));
Assert.That(ps2[1].DPNr, Is.EqualTo(2));
Assert.That(ps2[1].Weight, Is.EqualTo(1000));
});
}
[Test]
public async Task TestSplit_08_OtherMember_Complete() {
var d1 = await GetDelivery("20231005X001");
var d2 = await GetDelivery("20231005X002");
Assert.Multiple(() => {
Assert.That(d1, Is.Not.Null);
Assert.That(d2, Is.Null);
});
Assert.Multiple(() => {
var ps1 = GetParts(d1);
Assert.That(d1.MgNr, Is.EqualTo(101));
Assert.That(ps1, Has.Length.EqualTo(3));
Assert.That(ps1[0].Weight, Is.EqualTo(1000));
Assert.That(ps1[1].Weight, Is.EqualTo(1000));
Assert.That(ps1[2].Weight, Is.EqualTo(1000));
});
Assert.DoesNotThrowAsync(async () => await DeliveryService.SplitDeliveryToMember(d1.Year, d1.DId, [1000, int.MaxValue, 1100], 102));
d1 = await GetDelivery("20231005X001");
d2 = await GetDelivery("20231005X002");
Assert.Multiple(() => {
Assert.That(d1, Is.Null);
Assert.That(d2, Is.Not.Null);
});
Assert.Multiple(() => {
var ps2 = GetParts(d2);
Assert.That(d2.MgNr, Is.EqualTo(102));
Assert.That(ps2, Has.Length.EqualTo(3));
Assert.That(ps2[0].Weight, Is.EqualTo(1000));
Assert.That(ps2[1].Weight, Is.EqualTo(1000));
Assert.That(ps2[2].Weight, Is.EqualTo(1000));
});
}
[Test]
public async Task TestSplit_09_OtherDelivery_One() {
var d1 = await GetDelivery("20231006X001");
var d2 = await GetDelivery("20231006X002");
Assert.Multiple(() => {
Assert.That(d1, Is.Not.Null);
Assert.That(d2, Is.Not.Null);
});
Assert.Multiple(() => {
var ps1 = GetParts(d1);
var ps2 = GetParts(d2);
Assert.That(ps1, Has.Length.EqualTo(3));
Assert.That(ps1[0].Weight, Is.EqualTo(1000));
Assert.That(ps1[1].Weight, Is.EqualTo(1000));
Assert.That(ps1[2].Weight, Is.EqualTo(1000));
Assert.That(ps2, Has.Length.EqualTo(1));
Assert.That(ps2[0].Weight, Is.EqualTo(1000));
});
Assert.DoesNotThrowAsync(async () => await DeliveryService.SplitDeliveryToLsNr(d1.Year, d1.DId, [0, 1000, -4], d2.LsNr));
d1 = await GetDelivery("20231006X001");
d2 = await GetDelivery("20231006X002");
Assert.Multiple(() => {
Assert.That(d1, Is.Not.Null);
Assert.That(d2, Is.Not.Null);
});
Assert.Multiple(() => {
var ps1 = GetParts(d1);
var ps2 = GetParts(d2);
Assert.That(ps1, Has.Length.EqualTo(2));
Assert.That(ps1[0].DPNr, Is.EqualTo(1));
Assert.That(ps1[0].Weight, Is.EqualTo(1000));
Assert.That(ps1[1].DPNr, Is.EqualTo(3));
Assert.That(ps1[1].Weight, Is.EqualTo(1000));
Assert.That(ps2, Has.Length.EqualTo(2));
Assert.That(ps2[0].Weight, Is.EqualTo(1000));
Assert.That(ps2[1].Weight, Is.EqualTo(1000));
});
}
[Test]
public async Task TestSplit_10_OtherDelivery_Partial() {
var d1 = await GetDelivery("20231007X001");
var d2 = await GetDelivery("20231007X002");
Assert.Multiple(() => {
Assert.That(d1, Is.Not.Null);
Assert.That(d2, Is.Not.Null);
});
Assert.Multiple(() => {
var ps1 = GetParts(d1);
var ps2 = GetParts(d2);
Assert.That(ps1, Has.Length.EqualTo(3));
Assert.That(ps1[0].Weight, Is.EqualTo(1000));
Assert.That(ps1[1].Weight, Is.EqualTo(1000));
Assert.That(ps1[2].Weight, Is.EqualTo(1000));
Assert.That(ps2, Has.Length.EqualTo(1));
Assert.That(ps2[0].Weight, Is.EqualTo(1000));
});
Assert.DoesNotThrowAsync(async () => await DeliveryService.SplitDeliveryToLsNr(d1.Year, d1.DId, [0, 300, int.MinValue], d2.LsNr));
d1 = await GetDelivery("20231007X001");
d2 = await GetDelivery("20231007X002");
Assert.Multiple(() => {
Assert.That(d1, Is.Not.Null);
Assert.That(d2, Is.Not.Null);
});
Assert.Multiple(() => {
var ps1 = GetParts(d1);
var ps2 = GetParts(d2);
Assert.That(ps1, Has.Length.EqualTo(3));
Assert.That(ps1[0].Weight, Is.EqualTo(1000));
Assert.That(ps1[1].Weight, Is.EqualTo(700));
Assert.That(ps1[2].Weight, Is.EqualTo(1000));
Assert.That(ps2, Has.Length.EqualTo(2));
Assert.That(ps2[0].Weight, Is.EqualTo(1000));
Assert.That(ps2[1].Weight, Is.EqualTo(300));
});
}
[Test]
public async Task TestSplit_11_OtherDelivery_Mixed() {
var d1 = await GetDelivery("20231008X001");
var d2 = await GetDelivery("20231008X002");
Assert.Multiple(() => {
Assert.That(d1, Is.Not.Null);
Assert.That(d2, Is.Not.Null);
});
Assert.Multiple(() => {
var ps1 = GetParts(d1);
var ps2 = GetParts(d2);
Assert.That(ps1, Has.Length.EqualTo(3));
Assert.That(ps1[0].Weight, Is.EqualTo(1000));
Assert.That(ps1[1].Weight, Is.EqualTo(1000));
Assert.That(ps1[2].Weight, Is.EqualTo(1000));
Assert.That(ps2, Has.Length.EqualTo(1));
Assert.That(ps2[0].Weight, Is.EqualTo(1000));
});
Assert.DoesNotThrowAsync(async () => await DeliveryService.SplitDeliveryToLsNr(d1.Year, d1.DId, [850, 1000, -4], d2.LsNr));
d1 = await GetDelivery("20231008X001");
d2 = await GetDelivery("20231008X002");
Assert.Multiple(() => {
Assert.That(d1, Is.Not.Null);
Assert.That(d2, Is.Not.Null);
});
Assert.Multiple(() => {
var ps1 = GetParts(d1);
var ps2 = GetParts(d2);
Assert.That(ps1, Has.Length.EqualTo(2));
Assert.That(ps1[0].DPNr, Is.EqualTo(1));
Assert.That(ps1[0].Weight, Is.EqualTo(150));
Assert.That(ps1[1].DPNr, Is.EqualTo(3));
Assert.That(ps1[1].Weight, Is.EqualTo(1000));
Assert.That(ps2, Has.Length.EqualTo(3));
Assert.That(ps2[0].Weight, Is.EqualTo(1000));
Assert.That(ps2[1].Weight, Is.EqualTo(850));
Assert.That(ps2[2].Weight, Is.EqualTo(1000));
});
}
[Test]
public async Task TestSplit_12_OtherDelivery_Complete() {
var d1 = await GetDelivery("20231009X001");
var d2 = await GetDelivery("20231009X002");
Assert.Multiple(() => {
Assert.That(d1, Is.Not.Null);
Assert.That(d2, Is.Not.Null);
});
Assert.Multiple(() => {
var ps1 = GetParts(d1);
var ps2 = GetParts(d2);
Assert.That(ps1, Has.Length.EqualTo(3));
Assert.That(ps1[0].Weight, Is.EqualTo(1000));
Assert.That(ps1[1].Weight, Is.EqualTo(1000));
Assert.That(ps1[2].Weight, Is.EqualTo(1000));
Assert.That(ps2, Has.Length.EqualTo(1));
Assert.That(ps2[0].Weight, Is.EqualTo(1000));
});
Assert.DoesNotThrowAsync(async () => await DeliveryService.SplitDeliveryToLsNr(d1.Year, d1.DId, [1200, int.MaxValue, 1000], d2.LsNr));
d1 = await GetDelivery("20231009X001");
d2 = await GetDelivery("20231009X002");
Assert.Multiple(() => {
Assert.That(d1, Is.Null);
Assert.That(d2, Is.Not.Null);
});
Assert.Multiple(() => {
var ps2 = GetParts(d2);
Assert.That(ps2, Has.Length.EqualTo(4));
Assert.That(ps2[0].Weight, Is.EqualTo(1000));
Assert.That(ps2[1].Weight, Is.EqualTo(1000));
Assert.That(ps2[2].Weight, Is.EqualTo(1000));
Assert.That(ps2[3].Weight, Is.EqualTo(1000));
});
}
[Test]
public async Task TestDelete_01_Normal() {
using (var ctx = new AppDbContext()) {
Assert.That(await ctx.Deliveries.FindAsync(2022, 2), Is.Not.Null);
}
Assert.DoesNotThrowAsync(async () => await DeliveryService.DeleteDelivery("20220930X001"));
using (var ctx = new AppDbContext()) {
Assert.That(await ctx.Deliveries.FindAsync(2022, 2), Is.Null);
}
}
}
}

View File

@ -0,0 +1,361 @@
using Elwig.Helpers;
using Elwig.Models.Entities;
using Elwig.Services;
using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
namespace Tests.ServiceTests {
[TestFixture]
public class MemberServiceTest {
private static async Task InitViewModel(MemberAdminViewModel vm) {
using var ctx = new AppDbContext();
vm.BranchSource = await ctx.Branches.ToListAsync();
vm.DefaultKgSource = await ctx.Katastralgemeinden.ToListAsync();
vm.OrtSource = await ctx.PlzDestinations.Include(p => p.Ort).ToListAsync();
vm.BillingOrtSource = await ctx.PlzDestinations.Include(p => p.Ort).ToListAsync();
}
[Test]
public async Task TestCreate_01_Minimal() {
var vm = new MemberAdminViewModel();
await InitViewModel(vm);
await vm.InitInputs();
vm.Name = "Neuling";
vm.GivenName = "Nadine";
vm.Address = "Neubaugasse 1";
vm.Plz = 2120;
vm.Ort = vm.OrtSource.First(d => d.Ort.Name == "Wolkersdorf im Weinviertel");
vm.BusinessShares = 1;
vm.DefaultKg = vm.DefaultKgSource.First(k => k.Name == "Wolkersdorf");
Assert.That(vm.MgNr, Is.EqualTo(205));
using (var ctx = new AppDbContext()) {
Assert.That(await ctx.Members.FindAsync(205), Is.Null);
}
Assert.DoesNotThrowAsync(async () => await vm.UpdateMember(null));
Member? m;
using (var ctx = new AppDbContext()) {
m = await ctx.Members
.Where(m => m.MgNr == vm.MgNr)
.Include(m => m.BillingAddress!.PostalDest.AtPlz!.Ort)
.Include(m => m.PostalDest.AtPlz!.Ort)
.Include(m => m.DefaultWbKg!.AtKg)
.Include(m => m.EmailAddresses)
.Include(m => m.TelephoneNumbers)
.AsSplitQuery()
.FirstOrDefaultAsync();
}
Assert.That(m, Is.Not.Null);
Assert.Multiple(() => {
Assert.That(m.MgNr, Is.EqualTo(205));
Assert.That(m.Name, Is.EqualTo("Neuling"));
Assert.That(m.GivenName, Is.EqualTo("Nadine"));
Assert.That(m.Address, Is.EqualTo("Neubaugasse 1"));
Assert.That(m.PostalDest.AtPlz?.Plz, Is.EqualTo(2120));
Assert.That(m.PostalDest.AtPlz?.Ort.Name, Is.EqualTo("Wolkersdorf im Weinviertel"));
Assert.That(m.BusinessShares, Is.EqualTo(1));
Assert.That(m.DefaultKg?.Name, Is.EqualTo("Wolkersdorf"));
});
vm = new MemberAdminViewModel();
await InitViewModel(vm);
Assert.DoesNotThrow(() => vm.FillInputs(m));
Assert.Multiple(() => {
Assert.That(vm.MgNr, Is.EqualTo(205));
Assert.That(vm.Name, Is.EqualTo("Neuling"));
Assert.That(vm.GivenName, Is.EqualTo("Nadine"));
Assert.That(vm.Address, Is.EqualTo("Neubaugasse 1"));
Assert.That(vm.Plz, Is.EqualTo(2120));
Assert.That(vm.Ort?.Ort.Name, Is.EqualTo("Wolkersdorf im Weinviertel"));
Assert.That(vm.BusinessShares, Is.EqualTo(1));
Assert.That(vm.DefaultKg?.Name, Is.EqualTo("Wolkersdorf"));
});
}
[Test]
public async Task TestCreate_02_Full() {
var vm = new MemberAdminViewModel();
await InitViewModel(vm);
await vm.InitInputs();
vm.MgNr = 999;
vm.IsJuridicalPerson = true;
vm.Name = "Neue GmbH";
vm.ForTheAttentionOf = "Norbert Neuling";
vm.Address = "Neuegasse 2";
vm.Plz = 2120;
vm.Ort = vm.OrtSource.First(d => d.Ort.Name == "Wolkersdorf im Weinviertel");
vm.EmailAddresses[0] = "neue.gmbh@mail.com";
vm.EmailAddresses[1] = "norbert.neuling@mail.com";
vm.PhoneNrs[0] = new(0, "+43 2245 9876", "Büro");
vm.PhoneNrs[1] = new(1, "+43 664 123456789", "Hr. Neuling");
vm.PhoneNrs[2] = new(2, "+43 2245 9876-2", null);
vm.Iban = "AT97 1234 5678 9012 3460";
vm.Bic = "RLNWATWWWDF";
vm.UstIdNr = "ATU12345693";
vm.LfbisNr = "0123498";
vm.IsBuchführend = true;
vm.IsOrganic = true;
vm.BillingName = "Neue Holding AG";
vm.BillingAddress = "Neuegasse 3";
vm.BillingPlz = 2120;
vm.BillingOrt = vm.BillingOrtSource.First(d => d.Ort.Name == "Wolkersdorf im Weinviertel");
vm.BusinessShares = 10;
vm.AccountingNr = "330999";
vm.DefaultKg = vm.DefaultKgSource.First(k => k.Name == "Wolkersdorf");
vm.Comment = "Ich bin eine Anmerkung";
vm.ContactViaPost = true;
vm.ContactViaEmail = true;
vm.IsVollLieferant = true;
vm.IsFunktionär = true;
using (var ctx = new AppDbContext()) {
Assert.That(await ctx.Members.FindAsync(999), Is.Null);
}
Assert.DoesNotThrowAsync(async () => await vm.UpdateMember(null));
Member? m;
using (var ctx = new AppDbContext()) {
m = await ctx.Members
.Where(m => m.MgNr == vm.MgNr)
.Include(m => m.BillingAddress!.PostalDest.AtPlz!.Ort)
.Include(m => m.PostalDest.AtPlz!.Ort)
.Include(m => m.DefaultWbKg!.AtKg)
.Include(m => m.EmailAddresses)
.Include(m => m.TelephoneNumbers)
.AsSplitQuery()
.FirstOrDefaultAsync();
}
Assert.That(m, Is.Not.Null);
Assert.Multiple(() => {
Assert.That(m.MgNr, Is.EqualTo(999));
Assert.That(m.IsJuridicalPerson, Is.True);
Assert.That(m.Name, Is.EqualTo("Neue GmbH"));
Assert.That(m.ForTheAttentionOf, Is.EqualTo("Norbert Neuling"));
Assert.That(m.Address, Is.EqualTo("Neuegasse 2"));
Assert.That(m.PostalDest.AtPlz?.Plz, Is.EqualTo(2120));
Assert.That(m.PostalDest.AtPlz?.Ort.Name, Is.EqualTo("Wolkersdorf im Weinviertel"));
Assert.That(m.EmailAddresses.Select(a => (a.Nr, a.Address)), Is.EquivalentTo(new (int, string)[] {
(1, "neue.gmbh@mail.com"),
(2, "norbert.neuling@mail.com"),
}));
Assert.That(m.TelephoneNumbers.Select(n => (n.Nr, n.Type, n.Number, n.Comment)), Is.EquivalentTo(new (int, string, string, string?)[] {
(1, "landline", "+43 2245 9876", "Büro"),
(2, "mobile", "+43 664 123456789", "Hr. Neuling"),
(3, "fax", "+43 2245 9876-2", null),
}));
Assert.That(m.Iban, Is.EqualTo("AT971234567890123460"));
Assert.That(m.Bic, Is.EqualTo("RLNWATWWWDF"));
Assert.That(m.UstIdNr, Is.EqualTo("ATU12345693"));
Assert.That(m.LfbisNr, Is.EqualTo("0123498"));
Assert.That(m.IsBuchführend, Is.True);
Assert.That(m.IsOrganic, Is.True);
Assert.That(m.BillingAddress, Is.Not.Null);
Assert.That(m.BillingAddress?.FullName, Is.EqualTo("Neue Holding AG"));
Assert.That(m.BillingAddress?.Address, Is.EqualTo("Neuegasse 3"));
Assert.That(m.BillingAddress?.PostalDest.AtPlz?.Plz, Is.EqualTo(2120));
Assert.That(m.BillingAddress?.PostalDest.AtPlz?.Ort.Name, Is.EqualTo("Wolkersdorf im Weinviertel"));
Assert.That(m.BusinessShares, Is.EqualTo(10));
Assert.That(m.AccountingNr, Is.EqualTo("330999"));
Assert.That(m.DefaultKg?.Name, Is.EqualTo("Wolkersdorf"));
Assert.That(m.Comment, Is.EqualTo("Ich bin eine Anmerkung"));
Assert.That(m.ContactViaPost, Is.True);
Assert.That(m.ContactViaEmail, Is.True);
Assert.That(m.IsVollLieferant, Is.True);
Assert.That(m.IsFunktionär, Is.True);
});
vm = new MemberAdminViewModel();
await InitViewModel(vm);
Assert.DoesNotThrow(() => vm.FillInputs(m));
Assert.Multiple(() => {
Assert.That(vm.MgNr, Is.EqualTo(999));
Assert.That(vm.IsJuridicalPerson, Is.True);
Assert.That(vm.Name, Is.EqualTo("Neue GmbH"));
Assert.That(vm.ForTheAttentionOf, Is.EqualTo("Norbert Neuling"));
Assert.That(vm.Address, Is.EqualTo("Neuegasse 2"));
Assert.That(vm.Plz, Is.EqualTo(2120));
Assert.That(vm.Ort?.Ort.Name, Is.EqualTo("Wolkersdorf im Weinviertel"));
Assert.That(vm.EmailAddresses, Is.EquivalentTo(new string?[] {
"neue.gmbh@mail.com",
"norbert.neuling@mail.com",
null, null, null, null, null, null, null
}));
Assert.That(vm.PhoneNrs, Is.EquivalentTo(new MemberAdminViewModel.PhoneNr?[] {
new(0, "+43 2245 9876", "Büro"),
new(1, "+43 664 123456789", "Hr. Neuling"),
new(2, "+43 2245 9876-2", null),
new(), new(), new(), new(), new(), new()
}));
Assert.That(vm.Iban, Is.EqualTo("AT971234567890123460"));
Assert.That(vm.Bic, Is.EqualTo("RLNWATWWWDF"));
Assert.That(vm.UstIdNr, Is.EqualTo("ATU12345693"));
Assert.That(vm.LfbisNr, Is.EqualTo("0123498"));
Assert.That(vm.IsBuchführend, Is.True);
Assert.That(vm.IsOrganic, Is.True);
Assert.That(vm.BillingName, Is.EqualTo("Neue Holding AG"));
Assert.That(vm.BillingAddress, Is.EqualTo("Neuegasse 3"));
Assert.That(vm.BillingPlz, Is.EqualTo(2120));
Assert.That(vm.BillingOrt?.Ort.Name, Is.EqualTo("Wolkersdorf im Weinviertel"));
Assert.That(vm.BusinessShares, Is.EqualTo(10));
Assert.That(vm.AccountingNr, Is.EqualTo("330999"));
Assert.That(vm.DefaultKg?.Name, Is.EqualTo("Wolkersdorf"));
Assert.That(vm.Comment, Is.EqualTo("Ich bin eine Anmerkung"));
Assert.That(vm.ContactViaPost, Is.True);
Assert.That(vm.ContactViaEmail, Is.True);
Assert.That(vm.IsVollLieferant, Is.True);
Assert.That(vm.IsFunktionär, Is.True);
});
}
[Test]
public async Task TestUpdate_01_Inactive() {
var vm = new MemberAdminViewModel();
await InitViewModel(vm);
using (var ctx = new AppDbContext()) {
vm.FillInputs(await ctx.Members
.Where(m => m.MgNr == 202)
.Include(m => m.BillingAddress!.PostalDest.AtPlz!.Ort)
.Include(m => m.PostalDest.AtPlz!.Ort)
.Include(m => m.DefaultWbKg!.AtKg)
.Include(m => m.EmailAddresses)
.Include(m => m.TelephoneNumbers)
.AsSplitQuery()
.FirstAsync());
}
Assert.That(vm.IsActive, Is.True);
var exitDate = DateTime.Now;
vm.IsActive = false;
vm.ExitDate = $"{exitDate:dd.MM.yyyy}";
Assert.DoesNotThrowAsync(async () => await vm.UpdateMember(202));
Member? m;
using (var ctx = new AppDbContext()) {
m = await ctx.Members
.Where(m => m.MgNr == 202)
.Include(m => m.BillingAddress!.PostalDest.AtPlz!.Ort)
.Include(m => m.PostalDest.AtPlz!.Ort)
.Include(m => m.DefaultWbKg!.AtKg)
.Include(m => m.EmailAddresses)
.Include(m => m.TelephoneNumbers)
.AsSplitQuery()
.FirstOrDefaultAsync();
}
Assert.That(m, Is.Not.Null);
Assert.Multiple(() => {
Assert.That(m.IsActive, Is.False);
Assert.That(m.ExitDateString, Is.EqualTo($"{exitDate:yyyy-MM-dd}"));
});
vm = new MemberAdminViewModel();
await InitViewModel(vm);
Assert.DoesNotThrow(() => vm.FillInputs(m));
Assert.Multiple(() => {
Assert.That(vm.IsActive, Is.False);
Assert.That(vm.ExitDate, Is.EqualTo($"{exitDate:dd.MM.yyyy}"));
});
}
[Test]
public async Task TestUpdate_02_MgNr() {
var vm = new MemberAdminViewModel();
await InitViewModel(vm);
using (var ctx = new AppDbContext()) {
vm.FillInputs(await ctx.Members
.Where(m => m.MgNr == 203)
.Include(m => m.BillingAddress!.PostalDest.AtPlz!.Ort)
.Include(m => m.PostalDest.AtPlz!.Ort)
.Include(m => m.DefaultWbKg!.AtKg)
.Include(m => m.EmailAddresses)
.Include(m => m.TelephoneNumbers)
.AsSplitQuery()
.FirstAsync());
}
Assert.Multiple(() => {
Assert.That(vm.MgNr, Is.EqualTo(203));
Assert.That(vm.EmailAddresses[0], Is.Not.Null);
Assert.That(vm.PhoneNrs[0], Is.Not.Null);
Assert.That(vm.BillingName, Is.Not.Null);
});
vm.MgNr = 210;
Assert.DoesNotThrowAsync(async () => await vm.UpdateMember(203));
Member? m;
using (var ctx = new AppDbContext()) {
m = await ctx.Members
.Where(m => m.MgNr == 210)
.Include(m => m.BillingAddress!.PostalDest.AtPlz!.Ort)
.Include(m => m.PostalDest.AtPlz!.Ort)
.Include(m => m.DefaultWbKg!.AtKg)
.Include(m => m.EmailAddresses)
.Include(m => m.TelephoneNumbers)
.AsSplitQuery()
.FirstOrDefaultAsync();
}
Assert.That(m, Is.Not.Null);
vm = new MemberAdminViewModel();
await InitViewModel(vm);
Assert.DoesNotThrow(() => vm.FillInputs(m));
Assert.Multiple(() => {
Assert.That(vm.MgNr, Is.EqualTo(210));
Assert.That(vm.EmailAddresses[0], Is.Not.Null);
Assert.That(vm.PhoneNrs[0], Is.Not.Null);
Assert.That(vm.BillingName, Is.Not.Null);
});
}
[Test]
public async Task TestDelete_01_NoReferences() {
using (var ctx = new AppDbContext()) {
Assert.That(await ctx.Members.FindAsync(201), Is.Not.Null);
}
Assert.DoesNotThrowAsync(async () => await MemberService.DeleteMember(201, false, false, false));
using (var ctx = new AppDbContext()) {
Assert.That(await ctx.Members.FindAsync(201), Is.Null);
}
}
[Test]
public async Task TestDelete_02_AllReferences() {
using (var ctx = new AppDbContext()) {
Assert.That(await ctx.Members.FindAsync(204), Is.Not.Null);
}
for (int i = 0; i < 7; i++) {
Assert.ThrowsAsync<DbUpdateException>(async () => await MemberService.DeleteMember(204, (i & 1) != 0, (i & 2) != 0, (i & 4) != 0));
using var ctx = new AppDbContext();
Assert.That(await ctx.Members.FindAsync(204), Is.Not.Null);
}
Assert.DoesNotThrowAsync(async () => await MemberService.DeleteMember(204, true, true, true));
using (var ctx = new AppDbContext()) {
Assert.That(await ctx.Members.FindAsync(204), Is.Null);
}
}
}
}

View File

@ -0,0 +1,25 @@
using Elwig.Helpers;
using System.Reflection;
using Microsoft.Data.Sqlite;
namespace Tests.ServiceTests {
[SetUpFixture]
public class Setup {
private SqliteConnection? Connection;
[OneTimeSetUp]
public async Task SetupDatabase() {
Connection = await AppDbContext.ConnectAsync();
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.ServiceInsert.sql");
}
[OneTimeTearDown]
public async Task TeardownDatabase() {
if (Connection == null) return;
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.ServiceDelete.sql");
await Connection.DisposeAsync();
Connection = null;
}
}
}

View File

@ -19,12 +19,12 @@
</Target> </Target>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="Appium.WebDriver" Version="4.4.5" /> <PackageReference Include="Appium.WebDriver" Version="4.4.5" />
<PackageReference Include="NReco.PdfRenderer" Version="1.5.4" /> <PackageReference Include="NReco.PdfRenderer" Version="1.6.0" />
<PackageReference Include="NUnit" Version="4.2.2" /> <PackageReference Include="NUnit" Version="4.2.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" /> <PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.3.0"> <PackageReference Include="NUnit.Analyzers" Version="4.4.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View File

@ -171,11 +171,11 @@ namespace Tests.WeighingTests {
Thread.Sleep(100); Thread.Sleep(100);
if (invalid) { if (invalid) {
return ("abcd\r\n", false); return ("abcd", false);
} }
bool incr = true; bool incr = true;
return ($" {new DateTime(2020, 9, 28, 9, 8, 0):dd.MM.yy HH:mm} {identNr,4} {weight,9}{(unit ? "lb" : "kg")} \r\n", incr); return ($" {new DateTime(2020, 9, 28, 9, 8, 0):dd.MM.yy HH:mm} {identNr,4} {weight,9}{(unit ? "lb" : "kg")} ", incr);
} }
} }
} }