Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
9a2fa3ee3d | |||
ba9e1d7201 | |||
49f03c0a3c | |||
37adf92e80 | |||
4c75dbe4aa | |||
5c31ad8851 | |||
3ac9536e76 | |||
7246852181 | |||
dd48a24c58 | |||
ffef1fd6e4 | |||
01d658f51d | |||
5a36e84b1f | |||
5b2f617a68 | |||
dd5049faae | |||
ffe0ff5508 | |||
34178105a7 | |||
6b48a1090c | |||
ddd821e478 | |||
658a1f4dc1 | |||
daf83c4bbc | |||
26d75ea3cd | |||
86937485e4 | |||
e9de54415a | |||
62f63ef63d | |||
44656e0022 |
@ -1,6 +1,7 @@
|
|||||||
name: Test
|
name: Test
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
branches: ["**"]
|
||||||
paths: ["Elwig/**", "Tests/**", "Installer/Files/*.exe"]
|
paths: ["Elwig/**", "Tests/**", "Installer/Files/*.exe"]
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
@ -26,4 +27,4 @@ jobs:
|
|||||||
shell: powershell
|
shell: powershell
|
||||||
run: |
|
run: |
|
||||||
$env:PATH = "$(pwd)\Installer\Files;" + $env:PATH
|
$env:PATH = "$(pwd)\Installer\Files;" + $env:PATH
|
||||||
$(& dotnet test Tests; $a=$lastexitcode) | findstr x*; exit $a
|
$(& dotnet test Tests --filter "FullyQualifiedName!~E2ETests"; $a=$lastexitcode) | findstr x*; exit $a
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,3 +6,4 @@ bin/
|
|||||||
Tests/Resources/Sql/Create.sql
|
Tests/Resources/Sql/Create.sql
|
||||||
*.exe
|
*.exe
|
||||||
!WinziPrint.exe
|
!WinziPrint.exe
|
||||||
|
*.sqlite3
|
||||||
|
47
CHANGELOG.md
47
CHANGELOG.md
@ -3,6 +3,53 @@ Changelog
|
|||||||
=========
|
=========
|
||||||
|
|
||||||
|
|
||||||
|
[v0.8.9][v0.8.9] (2024-07-23) {#v0.8.9}
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
### Behobene Fehler {#v0.8.8-bugfixes}
|
||||||
|
|
||||||
|
* Absturz im Rundschreiben-Fenster (`MailWindow`). Fehler bei `CheckComboBox`. (49f03c0a3c)
|
||||||
|
* Excel-Exporte können nun auch Sonderzeichen (wie `&`) enthalten. (ba9e1d7201)
|
||||||
|
|
||||||
|
[v0.8.9]: https://git.necronda.net/winzer/elwig/releases/tag/v0.8.9
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v0.8.8][v0.8.8] (2024-07-22) {#v0.8.8}
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
### Behobene Fehler {#v0.8.8-bugfixes}
|
||||||
|
|
||||||
|
* Schnittstelle für Gassner-Waagen nicht funktionsfähig. (4c75dbe4aa)
|
||||||
|
|
||||||
|
[v0.8.8]: https://git.necronda.net/winzer/elwig/releases/tag/v0.8.8
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v0.8.7][v0.8.7] (2024-07-22) {#v0.8.7}
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
### Neue Funktionen {#v0.8.7-features}
|
||||||
|
|
||||||
|
* Im Mitglieder-Fenster (`MemberAdminWindow`) muss eine E-Mail-Adresse angegeben werden, wenn E-Mail als Kontaktart angegeben wird. (5a36e84b1f)
|
||||||
|
|
||||||
|
### Sonstiges {#v0.8.7-misc}
|
||||||
|
|
||||||
|
* Automatisierte Ende-zu-Ende-Tests (`E2ETests`) hinzugefügt. (ddd821e478, 6b48a1090c, dd5049faae, 5b2f617a68, 7246852181)
|
||||||
|
* Automatisierte Tests überarbeitet. (44656e0022, 86937485e4, 34178105a7, ffe0ff5508, 01d658f51d)
|
||||||
|
* Update-Dialog (`UpdateDialog`) überarbeitet. (62f63ef63d, e9de54415a)
|
||||||
|
* Eingebefelder vom Typ _Mehrfachauswahl mit Dropdown_ (`CheckComboBox`) etwas verbessert. (26d75ea3cd, daf83c4bbc)
|
||||||
|
* Im Übernahme-Fenster (`DeliveryAdminWindow`) werden nur noch aktive Mitglieder angezeigt. (658a1f4dc1)
|
||||||
|
* Abhängigkeiten aktualisiert. (ffef1fd6e4, dd48a24c58)
|
||||||
|
* Auto-Update-Funktion überarbeitet. (3ac9536e76)
|
||||||
|
|
||||||
|
[v0.8.7]: https://git.necronda.net/winzer/elwig/releases/tag/v0.8.7
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[v0.8.6][v0.8.6] (2024-07-01) {#v0.8.6}
|
[v0.8.6][v0.8.6] (2024-07-01) {#v0.8.6}
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
|
@ -31,8 +31,8 @@ namespace Elwig {
|
|||||||
public static readonly string DataPath = @"C:\ProgramData\Elwig\";
|
public static readonly string DataPath = @"C:\ProgramData\Elwig\";
|
||||||
public static readonly string ExePath = @"C:\Program Files\Elwig\";
|
public static readonly string ExePath = @"C:\Program Files\Elwig\";
|
||||||
public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig");
|
public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig");
|
||||||
public static readonly Config Config = new(DataPath + "config.ini");
|
|
||||||
|
|
||||||
|
public static Config Config { get; private set; } = new(Path.Combine(DataPath, "config.ini"));
|
||||||
public static int VersionMajor { get; private set; }
|
public static int VersionMajor { get; private set; }
|
||||||
public static int VersionMinor { get; private set; }
|
public static int VersionMinor { get; private set; }
|
||||||
public static int VersionPatch { get; private set; }
|
public static int VersionPatch { get; private set; }
|
||||||
@ -74,6 +74,11 @@ namespace Elwig {
|
|||||||
CurrentApp = this;
|
CurrentApp = this;
|
||||||
OverrideCulture();
|
OverrideCulture();
|
||||||
|
|
||||||
|
var args = Environment.GetCommandLineArgs();
|
||||||
|
if (args.Length >= 2) {
|
||||||
|
Config = new(Path.GetFullPath(args[1]));
|
||||||
|
}
|
||||||
|
|
||||||
ContextTimer.Tick += (object? sender, EventArgs evt) => {
|
ContextTimer.Tick += (object? sender, EventArgs evt) => {
|
||||||
if (CurrentLastWrite > LastChanged) {
|
if (CurrentLastWrite > LastChanged) {
|
||||||
LastChanged = CurrentLastWrite;
|
LastChanged = CurrentLastWrite;
|
||||||
@ -159,7 +164,7 @@ namespace Elwig {
|
|||||||
list.Add(Scale.FromConfig(s));
|
list.Add(Scale.FromConfig(s));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
list.Add(new InvalidScale(s.Id));
|
list.Add(new InvalidScale(s.Id));
|
||||||
if (Config.Debug || s.Required)
|
if (s.Required)
|
||||||
MessageBox.Show($"Unable to create scale {s.Id}:\n\n{e.Message}", "Scale Error",
|
MessageBox.Show($"Unable to create scale {s.Id}:\n\n{e.Message}", "Scale Error",
|
||||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
@ -222,7 +227,7 @@ namespace Elwig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task CheckForUpdates(bool showSuccess = false) {
|
public static async Task CheckForUpdates(bool showAlert = false) {
|
||||||
if (Config.UpdateUrl == null) return;
|
if (Config.UpdateUrl == null) return;
|
||||||
var latest = await Utils.GetLatestInstallerUrl(Config.UpdateUrl);
|
var latest = await Utils.GetLatestInstallerUrl(Config.UpdateUrl);
|
||||||
if (latest != null && new Version(latest.Value.Version) > new Version(Version)) {
|
if (latest != null && new Version(latest.Value.Version) > new Version(Version)) {
|
||||||
@ -233,11 +238,16 @@ namespace Elwig {
|
|||||||
Current.Shutdown();
|
Current.Shutdown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (showSuccess) {
|
} else if (showAlert) {
|
||||||
MessageBox.Show("Elwig ist auf dem aktuellsten Stand!", "Nach Updates suchen",
|
if (latest == null) {
|
||||||
|
MessageBox.Show("Informationen konnten nicht abgerufen werden!", "Nach Updates suchen",
|
||||||
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
} else {
|
||||||
|
MessageBox.Show($"Elwig ist auf dem aktuellsten Stand! (Version: {latest.Value.Version})", "Nach Updates suchen",
|
||||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static T FocusWindow<T>(Func<T> constructor, Predicate<T>? selector = null) where T : Window {
|
private static T FocusWindow<T>(Func<T> constructor, Predicate<T>? selector = null) where T : Window {
|
||||||
foreach (Window w in CurrentApp.Windows) {
|
foreach (Window w in CurrentApp.Windows) {
|
||||||
|
@ -1,73 +1,106 @@
|
|||||||
using System.Linq;
|
using System.Collections;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Linq;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
|
||||||
namespace Elwig.Controls {
|
namespace Elwig.Controls {
|
||||||
public class CheckComboBox : ListBox {
|
public class CheckComboBox : ListBox {
|
||||||
|
|
||||||
public static readonly DependencyProperty DelimiterProperty = DependencyProperty.Register("Delimiter", typeof(string), typeof(CheckComboBox), new FrameworkPropertyMetadata(", "));
|
public new static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register(nameof(SelectedItems), typeof(IList), typeof(CheckComboBox), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemsChangedCallback));
|
||||||
|
public new IList SelectedItems {
|
||||||
|
get => (IList)GetValue(SelectedItemsProperty);
|
||||||
|
set => SetValue(SelectedItemsProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty DelimiterProperty = DependencyProperty.Register(nameof(Delimiter), typeof(string), typeof(CheckComboBox), new FrameworkPropertyMetadata(", "));
|
||||||
public string Delimiter {
|
public string Delimiter {
|
||||||
get => (string)GetValue(DelimiterProperty);
|
get => (string)GetValue(DelimiterProperty);
|
||||||
set => SetValue(DelimiterProperty, value);
|
set => SetValue(DelimiterProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly DependencyProperty AllItemsSelectedContentProperty = DependencyProperty.Register("AllItemsSelectedContent", typeof(string), typeof(CheckComboBox), new FrameworkPropertyMetadata("All"));
|
public static readonly DependencyProperty AllItemsSelectedContentProperty = DependencyProperty.Register(nameof(AllItemsSelectedContent), typeof(string), typeof(CheckComboBox), new FrameworkPropertyMetadata("All"));
|
||||||
public string AllItemsSelectedContent {
|
public string AllItemsSelectedContent {
|
||||||
get => (string)GetValue(AllItemsSelectedContentProperty);
|
get => (string)GetValue(AllItemsSelectedContentProperty);
|
||||||
set => SetValue(AllItemsSelectedContentProperty, value);
|
set => SetValue(AllItemsSelectedContentProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly DependencyProperty IsSelectAllActiveProperty = DependencyProperty.Register("IsSelectAllActive", typeof(bool), typeof(CheckComboBox), new FrameworkPropertyMetadata(false));
|
public static readonly DependencyProperty IsSelectAllActiveProperty = DependencyProperty.Register(nameof(IsSelectAllActive), typeof(bool), typeof(CheckComboBox), new FrameworkPropertyMetadata(false));
|
||||||
public bool IsSelectAllActive {
|
public bool IsSelectAllActive {
|
||||||
get => (bool)GetValue(IsSelectAllActiveProperty);
|
get => (bool)GetValue(IsSelectAllActiveProperty);
|
||||||
set => SetValue(IsSelectAllActiveProperty, value);
|
set => SetValue(IsSelectAllActiveProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly DependencyProperty SelectAllContentProperty = DependencyProperty.Register("SelectAllContent", typeof(string), typeof(CheckComboBox), new FrameworkPropertyMetadata("All"));
|
public static readonly DependencyProperty SelectAllContentProperty = DependencyProperty.Register(nameof(SelectAllContent), typeof(string), typeof(CheckComboBox), new FrameworkPropertyMetadata("All"));
|
||||||
public string SelectAllContent {
|
public string SelectAllContent {
|
||||||
get => (string)GetValue(SelectAllContentProperty);
|
get => (string)GetValue(SelectAllContentProperty);
|
||||||
set => SetValue(SelectAllContentProperty, value);
|
set => SetValue(SelectAllContentProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly DependencyProperty AllItemsSelectedProperty = DependencyProperty.Register("AllItemsSelected", typeof(bool?), typeof(CheckComboBox), new FrameworkPropertyMetadata(false));
|
public static readonly DependencyProperty AllItemsSelectedProperty = DependencyProperty.Register(nameof(AllItemsSelected), typeof(bool?), typeof(CheckComboBox), new FrameworkPropertyMetadata(false));
|
||||||
public bool? AllItemsSelected {
|
public bool? AllItemsSelected {
|
||||||
get => (bool?)GetValue(AllItemsSelectedProperty);
|
get => (bool?)GetValue(AllItemsSelectedProperty);
|
||||||
set => SetValue(AllItemsSelectedProperty, value);
|
set => SetValue(AllItemsSelectedProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(CheckComboBox), new FrameworkPropertyMetadata(false));
|
public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register(nameof(IsDropDownOpen), typeof(bool), typeof(CheckComboBox), new FrameworkPropertyMetadata(false));
|
||||||
public bool IsDropDownOpen {
|
public bool IsDropDownOpen {
|
||||||
get => (bool)GetValue(IsDropDownOpenProperty);
|
get => (bool)GetValue(IsDropDownOpenProperty);
|
||||||
set => SetValue(IsDropDownOpenProperty, value);
|
set => SetValue(IsDropDownOpenProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly DependencyProperty MaxDropDownHeightProperty = DependencyProperty.Register("MaxDropDownHeight", typeof(double), typeof(CheckComboBox), new FrameworkPropertyMetadata(ComboBox.MaxDropDownHeightProperty.DefaultMetadata.DefaultValue));
|
public static readonly DependencyProperty MaxDropDownHeightProperty = DependencyProperty.Register(nameof(MaxDropDownHeight), typeof(double), typeof(CheckComboBox), new FrameworkPropertyMetadata(ComboBox.MaxDropDownHeightProperty.DefaultMetadata.DefaultValue));
|
||||||
public double MaxDropDownHeight {
|
public double MaxDropDownHeight {
|
||||||
get => (double)GetValue(MaxDropDownHeightProperty);
|
get => (double)GetValue(MaxDropDownHeightProperty);
|
||||||
set => SetValue(MaxDropDownHeightProperty, value);
|
set => SetValue(MaxDropDownHeightProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public new static readonly RoutedEvent SelectionChangedEvent = EventManager.RegisterRoutedEvent(nameof(SelectionChanged), RoutingStrategy.Bubble, typeof(SelectionChangedEventHandler), typeof(CheckComboBox));
|
||||||
|
public new event SelectionChangedEventHandler SelectionChanged {
|
||||||
|
add => AddHandler(SelectionChangedEvent, value);
|
||||||
|
remove => RemoveHandler(SelectionChangedEvent, value);
|
||||||
|
}
|
||||||
|
|
||||||
static CheckComboBox() {
|
static CheckComboBox() {
|
||||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(CheckComboBox), new FrameworkPropertyMetadata(typeof(CheckComboBox)));
|
DefaultStyleKeyProperty.OverrideMetadata(typeof(CheckComboBox), new FrameworkPropertyMetadata(typeof(CheckComboBox)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private TextBlock TextBox;
|
private bool _viewHandled;
|
||||||
|
private bool _modelHandled;
|
||||||
|
private TextBlock _textBox;
|
||||||
|
|
||||||
public CheckComboBox() {
|
public CheckComboBox() {
|
||||||
SelectionMode = SelectionMode.Multiple;
|
SelectionMode = SelectionMode.Multiple;
|
||||||
|
SelectedItems = new ObservableCollection<object>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnApplyTemplate() {
|
public override void OnApplyTemplate() {
|
||||||
TextBox = (GetTemplateChild("TextBox") as TextBlock)!;
|
_textBox = (GetTemplateChild("TextBox") as TextBlock)!;
|
||||||
var button = GetTemplateChild("Button") as Button;
|
var button = GetTemplateChild("Button") as Button;
|
||||||
button!.Click += Button_MouseDown;
|
button!.Click += Button_MouseDown;
|
||||||
var item = GetTemplateChild("SelectAllItem") as ListBoxItem;
|
var item = GetTemplateChild("SelectAllItem") as ListBoxItem;
|
||||||
item!.PreviewMouseDown += SelectAllItem_MouseDown;
|
item!.PreviewMouseDown += SelectAllItem_MouseDown;
|
||||||
SelectionChanged += OnSelectionChanged;
|
if (SelectedItems is INotifyCollectionChanged collection) {
|
||||||
|
collection.CollectionChanged += (s, e) => { SelectItems(); };
|
||||||
|
}
|
||||||
IsEnabledChanged += OnIsEnabledChanged;
|
IsEnabledChanged += OnIsEnabledChanged;
|
||||||
|
base.SelectionChanged += OnSelectionChanged;
|
||||||
base.OnApplyTemplate();
|
base.OnApplyTemplate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void OnSelectedItemsChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e) {
|
||||||
|
if (sender is CheckComboBox ccb)
|
||||||
|
ccb.OnSelectedItemsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSelectedItemsChanged() {
|
||||||
|
if (SelectedItems is INotifyCollectionChanged collection) {
|
||||||
|
collection.CollectionChanged += (s, e) => { SelectItems(); };
|
||||||
|
}
|
||||||
|
SelectItems();
|
||||||
|
}
|
||||||
|
|
||||||
private void Button_MouseDown(object sender, RoutedEventArgs evt) {
|
private void Button_MouseDown(object sender, RoutedEventArgs evt) {
|
||||||
IsDropDownOpen = !IsDropDownOpen;
|
IsDropDownOpen = !IsDropDownOpen;
|
||||||
}
|
}
|
||||||
@ -82,25 +115,47 @@ namespace Elwig.Controls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void OnSelectionChanged(object sender, SelectionChangedEventArgs evt) {
|
private void OnSelectionChanged(object sender, SelectionChangedEventArgs evt) {
|
||||||
|
SelectItemsReverse();
|
||||||
var dmp = DisplayMemberPath != null && DisplayMemberPath != "" ? DisplayMemberPath : null;
|
var dmp = DisplayMemberPath != null && DisplayMemberPath != "" ? DisplayMemberPath : null;
|
||||||
if (SelectedItems.Count == ItemsSource.Cast<object>().Count() && AllItemsSelectedContent != null) {
|
if (SelectedItems.Count == ItemsSource.Cast<object>().Count() && AllItemsSelectedContent != null) {
|
||||||
TextBox.Text = AllItemsSelectedContent;
|
_textBox.Text = AllItemsSelectedContent;
|
||||||
AllItemsSelected = true;
|
AllItemsSelected = true;
|
||||||
} else if (SelectedItems.Count == 0) {
|
} else if (SelectedItems.Count == 0) {
|
||||||
TextBox.Text = "";
|
_textBox.Text = "";
|
||||||
AllItemsSelected = false;
|
AllItemsSelected = false;
|
||||||
} else {
|
} else {
|
||||||
TextBox.Text = string.Join(Delimiter,
|
_textBox.Text = string.Join(Delimiter,
|
||||||
dmp == null ? SelectedItems.Cast<object>() :
|
dmp == null ? SelectedItems.Cast<object>() :
|
||||||
SelectedItems.Cast<object>()
|
SelectedItems.Cast<object>()
|
||||||
.Select(i => i.GetType().GetProperty(dmp)?.GetValue(i))
|
.Select(i => i.GetType().GetProperty(dmp)?.GetValue(i))
|
||||||
);
|
);
|
||||||
AllItemsSelected = null;
|
AllItemsSelected = null;
|
||||||
}
|
}
|
||||||
|
RaiseEvent(new SelectionChangedEventArgs(SelectionChangedEvent, evt.RemovedItems, evt.AddedItems));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs evt) {
|
private void OnIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs evt) {
|
||||||
if (!IsEnabled) IsDropDownOpen = false;
|
if (!IsEnabled) IsDropDownOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SelectItems() {
|
||||||
|
if (_viewHandled || _modelHandled)
|
||||||
|
return;
|
||||||
|
_viewHandled = true;
|
||||||
|
base.SelectedItems.Clear();
|
||||||
|
foreach (var item in SelectedItems)
|
||||||
|
base.SelectedItems.Add(item);
|
||||||
|
_viewHandled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectItemsReverse() {
|
||||||
|
if (_modelHandled || _viewHandled)
|
||||||
|
return;
|
||||||
|
_modelHandled = true;
|
||||||
|
SelectedItems.Clear();
|
||||||
|
foreach (var item in base.SelectedItems)
|
||||||
|
SelectedItems.Add(item);
|
||||||
|
_modelHandled = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,13 @@ using System.Windows.Input;
|
|||||||
namespace Elwig.Controls {
|
namespace Elwig.Controls {
|
||||||
public class IntegerUpDown : TextBox {
|
public class IntegerUpDown : TextBox {
|
||||||
|
|
||||||
public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register("Miminum", typeof(int?), typeof(IntegerUpDown), new FrameworkPropertyMetadata(null));
|
public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register(nameof(Minimum), typeof(int?), typeof(IntegerUpDown), new FrameworkPropertyMetadata(null));
|
||||||
public int? Minimum {
|
public int? Minimum {
|
||||||
get => (int?)GetValue(MinimumProperty);
|
get => (int?)GetValue(MinimumProperty);
|
||||||
set => SetValue(MinimumProperty, value);
|
set => SetValue(MinimumProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register("Maximum", typeof(int?), typeof(IntegerUpDown), new FrameworkPropertyMetadata(null));
|
public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register(nameof(Maximum), typeof(int?), typeof(IntegerUpDown), new FrameworkPropertyMetadata(null));
|
||||||
public int? Maximum {
|
public int? Maximum {
|
||||||
get => (int?)GetValue(MaximumProperty);
|
get => (int?)GetValue(MaximumProperty);
|
||||||
set => SetValue(MaximumProperty, value);
|
set => SetValue(MaximumProperty, value);
|
||||||
|
@ -6,13 +6,13 @@ using System.Windows.Data;
|
|||||||
namespace Elwig.Controls {
|
namespace Elwig.Controls {
|
||||||
public class UnitConverter : DependencyObject, IValueConverter {
|
public class UnitConverter : DependencyObject, IValueConverter {
|
||||||
|
|
||||||
public static readonly DependencyProperty UnitProperty = DependencyProperty.Register("Unit", typeof(string), typeof(UnitConverter), new FrameworkPropertyMetadata(null));
|
public static readonly DependencyProperty UnitProperty = DependencyProperty.Register(nameof(Unit), typeof(string), typeof(UnitConverter), new FrameworkPropertyMetadata(null));
|
||||||
public string Unit {
|
public string Unit {
|
||||||
get => (string)GetValue(UnitProperty);
|
get => (string)GetValue(UnitProperty);
|
||||||
set => SetValue(UnitProperty, value);
|
set => SetValue(UnitProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly DependencyProperty PrecisionProperty = DependencyProperty.Register("Precision", typeof(byte), typeof(UnitConverter), new FrameworkPropertyMetadata((byte)0));
|
public static readonly DependencyProperty PrecisionProperty = DependencyProperty.Register(nameof(Precision), typeof(byte), typeof(UnitConverter), new FrameworkPropertyMetadata((byte)0));
|
||||||
public byte Precision {
|
public byte Precision {
|
||||||
get => (byte)GetValue(PrecisionProperty);
|
get => (byte)GetValue(PrecisionProperty);
|
||||||
set => SetValue(PrecisionProperty, value);
|
set => SetValue(PrecisionProperty, value);
|
||||||
|
@ -4,7 +4,7 @@ using System.Windows.Controls;
|
|||||||
namespace Elwig.Controls {
|
namespace Elwig.Controls {
|
||||||
public class UnitTextBox : TextBox {
|
public class UnitTextBox : TextBox {
|
||||||
|
|
||||||
public static readonly DependencyProperty UnitProperty = DependencyProperty.Register("Unit", typeof(string), typeof(UnitTextBox), new FrameworkPropertyMetadata(""));
|
public static readonly DependencyProperty UnitProperty = DependencyProperty.Register(nameof(Unit), typeof(string), typeof(UnitTextBox), new FrameworkPropertyMetadata(""));
|
||||||
public string Unit {
|
public string Unit {
|
||||||
get => (string)GetValue(UnitProperty);
|
get => (string)GetValue(UnitProperty);
|
||||||
set => SetValue(UnitProperty, value);
|
set => SetValue(UnitProperty, value);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<Window x:Class="Elwig.Dialogs.DeleteMemberDialog"
|
<Window x:Class="Elwig.Dialogs.DeleteMemberDialog"
|
||||||
|
AutomationProperties.AutomationId="DeleteMemberDialog"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<Window x:Class="Elwig.Dialogs.NewSeasonDialog"
|
<Window x:Class="Elwig.Dialogs.NewSeasonDialog"
|
||||||
|
AutomationProperties.AutomationId="NewSeasonDialog"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
@ -5,11 +5,13 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
ResizeMode="NoResize" ShowInTaskbar="False" Topmost="True"
|
ResizeMode="NoResize" ShowInTaskbar="False" Topmost="True"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
Title="Neues Update verfügbar - Elwig" Height="180" Width="400">
|
Title="Neues Update verfügbar - Elwig" Height="190" Width="400"
|
||||||
|
Closed="OnClosed">
|
||||||
<Grid>
|
<Grid>
|
||||||
<TextBlock x:Name="Description" FontSize="14" Margin="0,0,0,30"
|
<TextBlock x:Name="Description" FontSize="14" Margin="0,0,0,40"
|
||||||
HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center">
|
HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center">
|
||||||
Version <Run x:Name="VersionText" FontWeight="Bold">0.0.0</Run> von Elwig ist verfügbar!<LineBreak/>
|
Version <Run x:Name="VersionText" FontWeight="Bold">0.0.0</Run> von Elwig ist verfügbar!
|
||||||
|
(<Hyperlink NavigateUri="https://elwig.at/changelog" RequestNavigate="Hyperlink_RequestNavigate">Änderungen</Hyperlink>)<LineBreak/>
|
||||||
Soll das Update heruntergeladen und<LineBreak/>
|
Soll das Update heruntergeladen und<LineBreak/>
|
||||||
installiert werden? (ca. <Run x:Name="SizeText">100</Run> MB)<LineBreak/>
|
installiert werden? (ca. <Run x:Name="SizeText">100</Run> MB)<LineBreak/>
|
||||||
<Run FontWeight="Bold">Achtung</Run>: Elwig wird dabei geschlossen!
|
<Run FontWeight="Bold">Achtung</Run>: Elwig wird dabei geschlossen!
|
||||||
@ -19,12 +21,12 @@
|
|||||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||||
Height="27" Width="300" SnapsToDevicePixels="True"/>
|
Height="27" Width="300" SnapsToDevicePixels="True"/>
|
||||||
|
|
||||||
<Button x:Name="InstallButton" Content="Installieren" Margin="10,10,115,10"
|
<Button x:Name="InstallButton" Content="Installieren" Margin="10,10,115,20"
|
||||||
FontSize="14" HorizontalAlignment="Right" VerticalAlignment="Bottom"
|
FontSize="14" HorizontalAlignment="Center" VerticalAlignment="Bottom"
|
||||||
Width="100" Height="27"
|
Width="100" Height="27"
|
||||||
Click="InstallButton_Click"/>
|
Click="InstallButton_Click"/>
|
||||||
<Button x:Name="CancelButton" Content="Abbrechen" Margin="10,10,10,10" IsCancel="True" IsDefault="True"
|
<Button x:Name="CancelButton" Content="Abbrechen" Margin="115,10,10,20" IsCancel="True"
|
||||||
FontSize="14" HorizontalAlignment="Right" VerticalAlignment="Bottom"
|
FontSize="14" HorizontalAlignment="Center" VerticalAlignment="Bottom"
|
||||||
Width="100" Height="27"/>
|
Width="100" Height="27"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Window>
|
</Window>
|
||||||
|
@ -3,8 +3,10 @@ using System;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Navigation;
|
||||||
|
|
||||||
namespace Elwig.Dialogs {
|
namespace Elwig.Dialogs {
|
||||||
public partial class UpdateDialog : Window {
|
public partial class UpdateDialog : Window {
|
||||||
@ -12,36 +14,54 @@ namespace Elwig.Dialogs {
|
|||||||
public string Version { get; private set; }
|
public string Version { get; private set; }
|
||||||
public string Url { get; private set; }
|
public string Url { get; private set; }
|
||||||
|
|
||||||
|
private readonly CancellationTokenSource Cancellation;
|
||||||
|
|
||||||
public UpdateDialog(string version, string url, long size) {
|
public UpdateDialog(string version, string url, long size) {
|
||||||
Version = version;
|
Version = version;
|
||||||
Url = url;
|
Url = url;
|
||||||
|
Cancellation = new();
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
VersionText.Text = version;
|
VersionText.Text = version;
|
||||||
SizeText.Text = $"{size / 1024 / 1024}";
|
SizeText.Text = $"{size / 1024 / 1024}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnClosed(object sender, EventArgs evt) {
|
||||||
|
Cancellation.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
private async void InstallButton_Click(object sender, RoutedEventArgs evt) {
|
private async void InstallButton_Click(object sender, RoutedEventArgs evt) {
|
||||||
Description.Visibility = Visibility.Hidden;
|
Description.Visibility = Visibility.Hidden;
|
||||||
ProgressBar.Visibility = Visibility.Visible;
|
ProgressBar.Visibility = Visibility.Visible;
|
||||||
InstallButton.IsEnabled = false;
|
InstallButton.IsEnabled = false;
|
||||||
await Install();
|
await Install();
|
||||||
DialogResult = true;
|
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Install() {
|
public async Task Install() {
|
||||||
var fileName = Path.Combine(App.TempPath, $"Elwig-{Version}.exe");
|
var fileName = Path.Combine(App.TempPath, $"Elwig-{Version}.exe");
|
||||||
{
|
try {
|
||||||
using var stream = new FileStream(fileName, FileMode.Create);
|
using var stream = new FileStream(fileName, FileMode.Create);
|
||||||
using var client = new HttpClient() {
|
using var client = new HttpClient() {
|
||||||
Timeout = TimeSpan.FromSeconds(5),
|
Timeout = TimeSpan.FromSeconds(5),
|
||||||
};
|
};
|
||||||
await client.DownloadAsync(Url, stream, new Progress<double>(p => {
|
await client.DownloadAsync(Url, stream, new Progress<double>(p => {
|
||||||
ProgressBar.Value = p * 100.0;
|
ProgressBar.Value = p * 100.0;
|
||||||
}));
|
}), Cancellation.Token);
|
||||||
|
} catch (OperationCanceledException) {
|
||||||
|
File.Delete(fileName);
|
||||||
|
return;
|
||||||
|
} catch (Exception exc) {
|
||||||
|
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
Process.Start(fileName);
|
||||||
|
DialogResult = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Process.Start(fileName);
|
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e) {
|
||||||
|
Process.Start(new ProcessStartInfo {
|
||||||
|
FileName = e.Uri.ToString(),
|
||||||
|
UseShellExecute = true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.8.6</Version>
|
<Version>0.8.9</Version>
|
||||||
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
|
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
@ -25,16 +25,16 @@
|
|||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="LinqKit" Version="1.2.5" />
|
<PackageReference Include="LinqKit" Version="1.3.0" />
|
||||||
<PackageReference Include="MailKit" Version="4.6.0" />
|
<PackageReference Include="MailKit" Version="4.7.1.1" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.31" />
|
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.32" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.7" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.7" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2535.41" />
|
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2592.51" />
|
||||||
<PackageReference Include="NJsonSchema" Version="11.0.0" />
|
<PackageReference Include="NJsonSchema" Version="11.0.2" />
|
||||||
<PackageReference Include="RazorLight" Version="2.3.1" />
|
<PackageReference Include="RazorLight" Version="2.3.1" />
|
||||||
<PackageReference Include="ScottPlot.WPF" Version="5.0.34" />
|
<PackageReference Include="ScottPlot.WPF" Version="5.0.36" />
|
||||||
<PackageReference Include="System.IO.Ports" Version="8.0.0" />
|
<PackageReference Include="System.IO.Ports" Version="8.0.0" />
|
||||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
|
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -98,15 +98,15 @@ namespace Elwig.Helpers {
|
|||||||
SavedChanges += OnSavedChanges;
|
SavedChanges += OnSavedChanges;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SqliteConnection Connect() {
|
public static SqliteConnection Connect(string? connectionString = null) {
|
||||||
var cnx = new SqliteConnection(ConnectionString);
|
var cnx = new SqliteConnection(connectionString ?? ConnectionString);
|
||||||
cnx.CreateFunction<string, string?, bool?>("REGEXP", (pattern, value) => value == null ? null : Regex.Match(value, pattern).Success, true);
|
cnx.CreateFunction<string, string?, bool?>("REGEXP", (pattern, value) => value == null ? null : Regex.Match(value, pattern).Success, true);
|
||||||
cnx.Open();
|
cnx.Open();
|
||||||
return cnx;
|
return cnx;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<SqliteConnection> ConnectAsync() {
|
public static async Task<SqliteConnection> ConnectAsync(string? connectionString = null) {
|
||||||
var cnx = new SqliteConnection(ConnectionString);
|
var cnx = new SqliteConnection(connectionString ?? ConnectionString);
|
||||||
cnx.CreateFunction<string, string?, bool?>("REGEXP", (pattern, value) => value == null ? null : Regex.Match(value, pattern).Success, true);
|
cnx.CreateFunction<string, string?, bool?>("REGEXP", (pattern, value) => value == null ? null : Regex.Match(value, pattern).Success, true);
|
||||||
await cnx.OpenAsync();
|
await cnx.OpenAsync();
|
||||||
return cnx;
|
return cnx;
|
||||||
|
@ -9,7 +9,7 @@ namespace Elwig.Helpers {
|
|||||||
public static class AppDbUpdater {
|
public static class AppDbUpdater {
|
||||||
|
|
||||||
// Don't forget to update value in Tests/fetch-resources.bat!
|
// Don't forget to update value in Tests/fetch-resources.bat!
|
||||||
public static readonly int RequiredSchemaVersion = 23;
|
public static readonly int RequiredSchemaVersion = 24;
|
||||||
|
|
||||||
private static int VersionOffset = 0;
|
private static int VersionOffset = 0;
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ namespace Elwig.Helpers {
|
|||||||
using var cnx = AppDbContext.Connect();
|
using var cnx = AppDbContext.Connect();
|
||||||
|
|
||||||
var applId = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA application_id") ?? 0;
|
var applId = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA application_id") ?? 0;
|
||||||
if (applId != 0x454C5747) throw new Exception("Invalid application_id of database");
|
if (applId != 0x454C5747) throw new Exception($"Invalid application_id in database (0x{applId:X08})");
|
||||||
|
|
||||||
var schemaVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA schema_version") ?? 0;
|
var schemaVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA schema_version") ?? 0;
|
||||||
VersionOffset = (int)(schemaVers % 100);
|
VersionOffset = (int)(schemaVers % 100);
|
||||||
|
@ -69,9 +69,9 @@ namespace Elwig.Helpers {
|
|||||||
public void Read() {
|
public void Read() {
|
||||||
var config = new ConfigurationBuilder().AddIniFile(FileName).Build();
|
var config = new ConfigurationBuilder().AddIniFile(FileName).Build();
|
||||||
|
|
||||||
DatabaseFile = Path.Combine(App.DataPath, config["database:file"] ?? "database.sqlite3");
|
DatabaseFile = Path.Combine(Path.GetDirectoryName(FileName) ?? App.DataPath, config["database:file"] ?? "database.sqlite3");
|
||||||
var log = config["database:log"];
|
var log = config["database:log"];
|
||||||
DatabaseLog = log != null ? Path.Combine(App.DataPath, log) : null;
|
DatabaseLog = log != null ? Path.Combine(Path.GetDirectoryName(FileName) ?? App.DataPath, log) : null;
|
||||||
Branch = config["general:branch"];
|
Branch = config["general:branch"];
|
||||||
Debug = TrueValues.Contains(config["general:debug"]?.ToLower());
|
Debug = TrueValues.Contains(config["general:debug"]?.ToLower());
|
||||||
UpdateUrl = config["update:url"];
|
UpdateUrl = config["update:url"];
|
||||||
|
@ -6,6 +6,7 @@ using System.Globalization;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Security;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Elwig.Helpers.Export {
|
namespace Elwig.Helpers.Export {
|
||||||
@ -312,7 +313,7 @@ namespace Elwig.Helpers.Export {
|
|||||||
}
|
}
|
||||||
c = $"<{ct} office:value-type=\"float\" calcext:value-type=\"float\" office:value=\"{v.ToString(CultureInfo.InvariantCulture)}\"{add}><text:p>{data}</text:p></{ct}>";
|
c = $"<{ct} office:value-type=\"float\" calcext:value-type=\"float\" office:value=\"{v.ToString(CultureInfo.InvariantCulture)}\"{add}><text:p>{data}</text:p></{ct}>";
|
||||||
} else {
|
} else {
|
||||||
c = $"<{ct} office:value-type=\"string\" calcext:value-type=\"string\"{add}><text:p>{data}</text:p></{ct}>";
|
c = $"<{ct} office:value-type=\"string\" calcext:value-type=\"string\"{add}><text:p>{SecurityElement.Escape(data.ToString())}</text:p></{ct}>";
|
||||||
}
|
}
|
||||||
|
|
||||||
return $" {c}\r\n" + (colSpan > 1 ? $" <table:covered-table-cell table:number-rows-repeated=\"{colSpan - 1}\"/>\r\n" : "");
|
return $" {c}\r\n" + (colSpan > 1 ? $" <table:covered-table-cell table:number-rows-repeated=\"{colSpan - 1}\"/>\r\n" : "");
|
||||||
|
@ -425,8 +425,11 @@ namespace Elwig.Helpers {
|
|||||||
public static async Task<(string Version, string Url, long Size)?> GetLatestInstallerUrl(string url) {
|
public static async Task<(string Version, string Url, long Size)?> GetLatestInstallerUrl(string url) {
|
||||||
try {
|
try {
|
||||||
using var client = GetHttpClient(accept: "application/json");
|
using var client = GetHttpClient(accept: "application/json");
|
||||||
var resJson = JsonNode.Parse(await client.GetStringAsync(url));
|
using var res = await client.GetAsync(url);
|
||||||
var data = resJson!["data"]![0]!;
|
if (!res.IsSuccessStatusCode)
|
||||||
|
return null;
|
||||||
|
var resJson = JsonNode.Parse(await res.Content.ReadAsStringAsync());
|
||||||
|
var data = resJson!["data"]!.AsArray()[^1]!;
|
||||||
return ((string)data["version"]!, (string)data["url"]!, (long)data["size"]!);
|
return ((string)data["version"]!, (string)data["url"]!, (long)data["size"]!);
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
|
@ -60,14 +60,13 @@ namespace Elwig.Helpers.Weighing {
|
|||||||
throw new IOException("Invalid response from scale: Received record has invalid size");
|
throw new IOException("Invalid response from scale: Received record has invalid size");
|
||||||
var line = record[2..];
|
var line = record[2..];
|
||||||
|
|
||||||
var status = line[ 0.. 2];
|
var brutto = line[ 0.. 7].Trim();
|
||||||
var brutto = line[ 2.. 9].Trim();
|
var tara = line[ 7..14].Trim();
|
||||||
var tara = line[ 9..16].Trim();
|
var netto = line[14..21].Trim();
|
||||||
var netto = line[16..23].Trim();
|
var scaleNr = line[21..23].Trim();
|
||||||
var scaleNr = line[23..25].Trim();
|
var identNr = line[23..29].Trim();
|
||||||
var identNr = line[25..31].Trim();
|
var date = line[29..37];
|
||||||
var date = line[31..39];
|
var time = line[37..43];
|
||||||
var time = line[39..45];
|
|
||||||
|
|
||||||
identNr = identNr.Length > 0 && identNr != "0" ? identNr : null;
|
identNr = identNr.Length > 0 && identNr != "0" ? identNr : null;
|
||||||
var parsedDate = DateOnly.ParseExact(date, "yyyyMMdd");
|
var parsedDate = DateOnly.ParseExact(date, "yyyyMMdd");
|
||||||
|
@ -13,6 +13,9 @@ namespace Elwig.Models.Entities {
|
|||||||
[Column("modid")]
|
[Column("modid")]
|
||||||
public required string ModId { get; set; }
|
public required string ModId { get; set; }
|
||||||
|
|
||||||
|
[Column("active")]
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
|
||||||
[Column("ordering")]
|
[Column("ordering")]
|
||||||
public int Ordering { get; set; }
|
public int Ordering { get; set; }
|
||||||
|
|
||||||
@ -21,7 +24,6 @@ namespace Elwig.Models.Entities {
|
|||||||
|
|
||||||
[Column("abs")]
|
[Column("abs")]
|
||||||
public long? AbsValue { get; set; }
|
public long? AbsValue { get; set; }
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public decimal? Abs {
|
public decimal? Abs {
|
||||||
get => AbsValue != null ? Season.DecFromDb(AbsValue.Value) : null;
|
get => AbsValue != null ? Season.DecFromDb(AbsValue.Value) : null;
|
||||||
@ -30,19 +32,12 @@ namespace Elwig.Models.Entities {
|
|||||||
|
|
||||||
[Column("rel")]
|
[Column("rel")]
|
||||||
public double? RelValue { get; set; }
|
public double? RelValue { get; set; }
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public decimal? Rel {
|
public decimal? Rel {
|
||||||
get => (decimal?)RelValue;
|
get => (decimal?)RelValue;
|
||||||
set => RelValue = (double?)value;
|
set => RelValue = (double?)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Column("standard")]
|
|
||||||
public bool IsStandard { get; set; }
|
|
||||||
|
|
||||||
[Column("quick_select")]
|
|
||||||
public bool IsQuickSelect { get; set; }
|
|
||||||
|
|
||||||
[ForeignKey("Year")]
|
[ForeignKey("Year")]
|
||||||
public virtual Season Season { get; private set; } = null!;
|
public virtual Season Season { get; private set; } = null!;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
-- schema version 20 to 21
|
-- schema version 21 to 22
|
||||||
|
|
||||||
CREATE VIEW v_penalty_business_shares AS
|
CREATE VIEW v_penalty_business_shares AS
|
||||||
SELECT u.year, u.mgnr,
|
SELECT u.year, u.mgnr,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
-- schema version 20 to 21
|
-- schema version 22 to 23
|
||||||
|
|
||||||
CREATE VIEW v_stat_modifier AS
|
CREATE VIEW v_stat_modifier AS
|
||||||
SELECT v.year, v.avnr, m.modid, m.name, m.abs, m.rel,
|
SELECT v.year, v.avnr, m.modid, m.name, m.abs, m.rel,
|
||||||
|
5
Elwig/Resources/Sql/23-24.sql
Normal file
5
Elwig/Resources/Sql/23-24.sql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
-- schema version 23 to 24
|
||||||
|
|
||||||
|
ALTER TABLE modifier DROP COLUMN standard;
|
||||||
|
ALTER TABLE modifier DROP COLUMN quick_select;
|
||||||
|
ALTER TABLE modifier ADD COLUMN active INTEGER NOT NULL CHECK (active IN (TRUE, FALSE)) DEFAULT TRUE;
|
@ -1,5 +1,6 @@
|
|||||||
<local:AdministrationWindow
|
<local:AdministrationWindow
|
||||||
x:Class="Elwig.Windows.BaseDataWindow"
|
x:Class="Elwig.Windows.BaseDataWindow"
|
||||||
|
AutomationProperties.AutomationId="BaseDataWindow"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
@ -521,6 +522,10 @@
|
|||||||
<Label Content="Absolut:" Grid.Column="1" Margin="10,100,10,10"/>
|
<Label Content="Absolut:" Grid.Column="1" Margin="10,100,10,10"/>
|
||||||
<ctrl:UnitTextBox x:Name="SeasonModifierAbsInput" Unit="€/kg" TextChanged="SeasonModifierAbsInput_TextChanged"
|
<ctrl:UnitTextBox x:Name="SeasonModifierAbsInput" Unit="€/kg" TextChanged="SeasonModifierAbsInput_TextChanged"
|
||||||
Grid.Column="2" Width="86" Margin="0,100,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
|
Grid.Column="2" Width="86" Margin="0,100,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
|
||||||
|
|
||||||
|
<CheckBox x:Name="SeasonModifierActiveInput" Content="In Übernahme-Fenster anzeigen"
|
||||||
|
Grid.Column="1" Grid.ColumnSpan="2" Margin="10,134,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"
|
||||||
|
Checked="SeasonModifier_Changed" Unchecked="SeasonModifier_Changed"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</GroupBox>
|
</GroupBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -134,11 +134,13 @@ namespace Elwig.Windows {
|
|||||||
SeasonModifierNameInput.Text = "";
|
SeasonModifierNameInput.Text = "";
|
||||||
SeasonModifierRelInput.Text = "";
|
SeasonModifierRelInput.Text = "";
|
||||||
SeasonModifierAbsInput.Text = "";
|
SeasonModifierAbsInput.Text = "";
|
||||||
|
SeasonModifierActiveInput.IsChecked = false;
|
||||||
} else {
|
} else {
|
||||||
SeasonModifierIdInput.Text = mod.ModId;
|
SeasonModifierIdInput.Text = mod.ModId;
|
||||||
SeasonModifierNameInput.Text = mod.Name;
|
SeasonModifierNameInput.Text = mod.Name;
|
||||||
SeasonModifierRelInput.Text = (mod.Rel * 100)?.ToString() ?? "";
|
SeasonModifierRelInput.Text = (mod.Rel * 100)?.ToString() ?? "";
|
||||||
SeasonModifierAbsInput.Text = mod.Abs?.ToString() ?? "";
|
SeasonModifierAbsInput.Text = mod.Abs?.ToString() ?? "";
|
||||||
|
SeasonModifierActiveInput.IsChecked = mod.IsActive;
|
||||||
}
|
}
|
||||||
_modUpdate = false;
|
_modUpdate = false;
|
||||||
}
|
}
|
||||||
@ -154,6 +156,7 @@ namespace Elwig.Windows {
|
|||||||
mod.Name = SeasonModifierNameInput.Text;
|
mod.Name = SeasonModifierNameInput.Text;
|
||||||
mod.Rel = decimal.TryParse(SeasonModifierRelInput.Text, out var vRel) ? vRel / 100 : null;
|
mod.Rel = decimal.TryParse(SeasonModifierRelInput.Text, out var vRel) ? vRel / 100 : null;
|
||||||
mod.AbsValue = decimal.TryParse(SeasonModifierAbsInput.Text, out var vAbs) ? Utils.DecToDb(vAbs, s.Precision) : null;
|
mod.AbsValue = decimal.TryParse(SeasonModifierAbsInput.Text, out var vAbs) ? Utils.DecToDb(vAbs, s.Precision) : null;
|
||||||
|
mod.IsActive = SeasonModifierActiveInput.IsChecked ?? false;
|
||||||
|
|
||||||
CollectionViewSource.GetDefaultView(_modList).Refresh();
|
CollectionViewSource.GetDefaultView(_modList).Refresh();
|
||||||
UpdateButtons();
|
UpdateButtons();
|
||||||
|
@ -187,8 +187,7 @@ namespace Elwig.Windows {
|
|||||||
Name = m.Name,
|
Name = m.Name,
|
||||||
AbsValue = m.AbsValue * mult / div,
|
AbsValue = m.AbsValue * mult / div,
|
||||||
RelValue = m.RelValue,
|
RelValue = m.RelValue,
|
||||||
IsStandard = m.IsStandard,
|
IsActive = m.IsActive,
|
||||||
IsQuickSelect = m.IsQuickSelect,
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
await ctx.SaveChangesAsync();
|
await ctx.SaveChangesAsync();
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
<local:AdministrationWindow x:Class="Elwig.Windows.DeliveryAdminWindow"
|
<local:AdministrationWindow
|
||||||
|
x:Class="Elwig.Windows.DeliveryAdminWindow"
|
||||||
|
AutomationProperties.AutomationId="DeliveryAdminWindow"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:local="clr-namespace:Elwig.Windows"
|
xmlns:local="clr-namespace:Elwig.Windows"
|
||||||
|
@ -1084,7 +1084,7 @@ namespace Elwig.Windows {
|
|||||||
ControlUtils.RenewItemsSource(CultivationInput, cultList, null, ControlUtils.RenewSourceDefault.First);
|
ControlUtils.RenewItemsSource(CultivationInput, cultList, null, ControlUtils.RenewSourceDefault.First);
|
||||||
ControlUtils.RenewItemsSource(WineQualityLevelInput, await ctx.WineQualityLevels.ToListAsync());
|
ControlUtils.RenewItemsSource(WineQualityLevelInput, await ctx.WineQualityLevels.ToListAsync());
|
||||||
ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
|
ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
|
||||||
.Where(m => m.Year == y)
|
.Where(m => m.Year == y && (!IsCreating || m.IsActive))
|
||||||
.OrderBy(m => m.Ordering)
|
.OrderBy(m => m.Ordering)
|
||||||
.Include(m => m.Season.Currency)
|
.Include(m => m.Season.Currency)
|
||||||
.ToListAsync());
|
.ToListAsync());
|
||||||
@ -1116,14 +1116,14 @@ namespace Elwig.Windows {
|
|||||||
using var ctx = new AppDbContext();
|
using var ctx = new AppDbContext();
|
||||||
if (DeliveryList.SelectedItem is Delivery d) {
|
if (DeliveryList.SelectedItem is Delivery d) {
|
||||||
ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
|
ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
|
||||||
.Where(m => m.Year == d.Year)
|
.Where(m => m.Year == d.Year && (!IsCreating || m.IsActive))
|
||||||
.OrderBy(m => m.Ordering)
|
.OrderBy(m => m.Ordering)
|
||||||
.Include(m => m.Season.Currency)
|
.Include(m => m.Season.Currency)
|
||||||
.ToListAsync());
|
.ToListAsync());
|
||||||
ControlUtils.RenewItemsSource(DeliveryPartList, d.FilteredParts.OrderBy(p => p.DPNr).ToList(), DeliveryPartList_SelectionChanged, ControlUtils.RenewSourceDefault.First);
|
ControlUtils.RenewItemsSource(DeliveryPartList, d.FilteredParts.OrderBy(p => p.DPNr).ToList(), DeliveryPartList_SelectionChanged, ControlUtils.RenewSourceDefault.First);
|
||||||
} else {
|
} else {
|
||||||
ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
|
ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
|
||||||
.Where(m => m.Year == SeasonInput.Value)
|
.Where(m => m.Year == SeasonInput.Value && (!IsCreating || m.IsActive))
|
||||||
.OrderBy(m => m.Ordering)
|
.OrderBy(m => m.Ordering)
|
||||||
.Include(m => m.Season.Currency)
|
.Include(m => m.Season.Currency)
|
||||||
.ToListAsync());
|
.ToListAsync());
|
||||||
@ -1555,6 +1555,11 @@ namespace Elwig.Windows {
|
|||||||
var attrList = await ctx.WineAttributes.Where(a => a.IsActive).OrderBy(a => a.Name).Cast<object>().ToListAsync();
|
var attrList = await ctx.WineAttributes.Where(a => a.IsActive).OrderBy(a => a.Name).Cast<object>().ToListAsync();
|
||||||
attrList.Insert(0, new NullItem(""));
|
attrList.Insert(0, new NullItem(""));
|
||||||
ControlUtils.RenewItemsSource(AttributeInput, attrList, null, ControlUtils.RenewSourceDefault.First);
|
ControlUtils.RenewItemsSource(AttributeInput, attrList, null, ControlUtils.RenewSourceDefault.First);
|
||||||
|
ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
|
||||||
|
.Where(m => m.Year == SeasonInput.Value && m.IsActive)
|
||||||
|
.OrderBy(m => m.Ordering)
|
||||||
|
.Include(m => m.Season.Currency)
|
||||||
|
.ToListAsync());
|
||||||
ControlUtils.RenewItemsSource(MemberInput, await ctx.Members
|
ControlUtils.RenewItemsSource(MemberInput, await ctx.Members
|
||||||
.Where(m => m.IsActive || !IsReceipt)
|
.Where(m => m.IsActive || !IsReceipt)
|
||||||
.Include(m => m.PostalDest.AtPlz!.Ort)
|
.Include(m => m.PostalDest.AtPlz!.Ort)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<Window x:Class="Elwig.Windows.DocumentViewerWindow"
|
<Window x:Class="Elwig.Windows.DocumentViewerWindow"
|
||||||
|
AutomationProperties.AutomationId="DocumentViewerWindow"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<local:ContextWindow
|
<local:ContextWindow
|
||||||
x:Class="Elwig.Windows.MailWindow"
|
x:Class="Elwig.Windows.MailWindow"
|
||||||
|
AutomationProperties.AutomationId="MailWindow"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
@ -60,31 +60,31 @@ namespace Elwig.Windows {
|
|||||||
protected Document? PrintDocument;
|
protected Document? PrintDocument;
|
||||||
protected Dictionary<Member, List<Document>>? EmailDocuments;
|
protected Dictionary<Member, List<Document>>? EmailDocuments;
|
||||||
|
|
||||||
public static readonly DependencyProperty PostalAllCountProperty = DependencyProperty.Register("PostalAllCount", typeof(int), typeof(MailWindow));
|
public static readonly DependencyProperty PostalAllCountProperty = DependencyProperty.Register(nameof(PostalAllCount), typeof(int), typeof(MailWindow));
|
||||||
public int PostalAllCount {
|
public int PostalAllCount {
|
||||||
get => (int)GetValue(PostalAllCountProperty);
|
get => (int)GetValue(PostalAllCountProperty);
|
||||||
private set => SetValue(PostalAllCountProperty, value);
|
private set => SetValue(PostalAllCountProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly DependencyProperty PostalWishCountProperty = DependencyProperty.Register("PostalWishCount", typeof(int), typeof(MailWindow));
|
public static readonly DependencyProperty PostalWishCountProperty = DependencyProperty.Register(nameof(PostalWishCount), typeof(int), typeof(MailWindow));
|
||||||
public int PostalWishCount {
|
public int PostalWishCount {
|
||||||
get => (int)GetValue(PostalWishCountProperty);
|
get => (int)GetValue(PostalWishCountProperty);
|
||||||
private set => SetValue(PostalWishCountProperty, value);
|
private set => SetValue(PostalWishCountProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly DependencyProperty PostalNoEmailCountProperty = DependencyProperty.Register("PostalNoEmailCount", typeof(int), typeof(MailWindow));
|
public static readonly DependencyProperty PostalNoEmailCountProperty = DependencyProperty.Register(nameof(PostalNoEmailCount), typeof(int), typeof(MailWindow));
|
||||||
public int PostalNoEmailCount {
|
public int PostalNoEmailCount {
|
||||||
get => (int)GetValue(PostalNoEmailCountProperty);
|
get => (int)GetValue(PostalNoEmailCountProperty);
|
||||||
private set => SetValue(PostalNoEmailCountProperty, value);
|
private set => SetValue(PostalNoEmailCountProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly DependencyProperty EmailAllCountProperty = DependencyProperty.Register("EmailAllCount", typeof(int), typeof(MailWindow));
|
public static readonly DependencyProperty EmailAllCountProperty = DependencyProperty.Register(nameof(EmailAllCount), typeof(int), typeof(MailWindow));
|
||||||
public int EmailAllCount {
|
public int EmailAllCount {
|
||||||
get => (int)GetValue(EmailAllCountProperty);
|
get => (int)GetValue(EmailAllCountProperty);
|
||||||
private set => SetValue(EmailAllCountProperty, value);
|
private set => SetValue(EmailAllCountProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly DependencyProperty EmailWishCountProperty = DependencyProperty.Register("EmailWishCount", typeof(int), typeof(MailWindow));
|
public static readonly DependencyProperty EmailWishCountProperty = DependencyProperty.Register(nameof(EmailWishCount), typeof(int), typeof(MailWindow));
|
||||||
public int EmailWishCount {
|
public int EmailWishCount {
|
||||||
get => (int)GetValue(EmailWishCountProperty);
|
get => (int)GetValue(EmailWishCountProperty);
|
||||||
private set => SetValue(EmailWishCountProperty, value);
|
private set => SetValue(EmailWishCountProperty, value);
|
||||||
@ -139,7 +139,7 @@ namespace Elwig.Windows {
|
|||||||
AvaiableDocumentsList.ItemsSource = l;
|
AvaiableDocumentsList.ItemsSource = l;
|
||||||
|
|
||||||
ControlUtils.RenewItemsSource(MemberBranchInput, await ctx.Branches
|
ControlUtils.RenewItemsSource(MemberBranchInput, await ctx.Branches
|
||||||
.Where(b => b.Members.Any())
|
.Where(b => b.Members.Count != 0)
|
||||||
.OrderBy(b => b.Name)
|
.OrderBy(b => b.Name)
|
||||||
.ToListAsync(), MemberInput_SelectionChanged);
|
.ToListAsync(), MemberInput_SelectionChanged);
|
||||||
if (MemberBranchInput.SelectedItems.Count == 0) {
|
if (MemberBranchInput.SelectedItems.Count == 0) {
|
||||||
@ -148,7 +148,7 @@ namespace Elwig.Windows {
|
|||||||
MemberBranchInput.SelectionChanged += MemberInput_SelectionChanged;
|
MemberBranchInput.SelectionChanged += MemberInput_SelectionChanged;
|
||||||
}
|
}
|
||||||
ControlUtils.RenewItemsSource(MemberKgInput, await ctx.Katastralgemeinden
|
ControlUtils.RenewItemsSource(MemberKgInput, await ctx.Katastralgemeinden
|
||||||
.Where(k => k.WbKg!.Members.Any())
|
.Where(k => k.WbKg!.Members.Count != 0)
|
||||||
.OrderBy(k => k.Name)
|
.OrderBy(k => k.Name)
|
||||||
.ToListAsync(), MemberInput_SelectionChanged);
|
.ToListAsync(), MemberInput_SelectionChanged);
|
||||||
if (MemberKgInput.SelectedItems.Count == 0) {
|
if (MemberKgInput.SelectedItems.Count == 0) {
|
||||||
@ -561,7 +561,7 @@ namespace Elwig.Windows {
|
|||||||
double totalNum = printNum + emailNum;
|
double totalNum = printNum + emailNum;
|
||||||
|
|
||||||
var email = memberDocs
|
var email = memberDocs
|
||||||
.Where(d => d.Docs.Count > 0 && d.Member.EmailAddresses.Any() && (emailMode == 2 || (emailMode == 1 && d.Member.ContactViaEmail)))
|
.Where(d => d.Docs.Count > 0 && d.Member.EmailAddresses.Count > 0 && (emailMode == 2 || (emailMode == 1 && d.Member.ContactViaEmail)))
|
||||||
.ToDictionary(d => d.Member, m => {
|
.ToDictionary(d => d.Member, m => {
|
||||||
var docs = m.Docs.Select(d => d.Doc).ToList();
|
var docs = m.Docs.Select(d => d.Doc).ToList();
|
||||||
foreach (var doc in docs) {
|
foreach (var doc in docs) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<local:AdministrationWindow
|
<local:AdministrationWindow
|
||||||
x:Class="Elwig.Windows.MemberAdminWindow"
|
x:Class="Elwig.Windows.MemberAdminWindow"
|
||||||
|
AutomationProperties.AutomationId="MemberAdminWindow"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:local="clr-namespace:Elwig.Windows"
|
xmlns:local="clr-namespace:Elwig.Windows"
|
||||||
@ -523,7 +524,7 @@
|
|||||||
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
|
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
|
||||||
HorizontalAlignment="Left" Margin="0,0,0,15" VerticalAlignment="Bottom" Grid.Column="1" Grid.ColumnSpan="2"/>
|
HorizontalAlignment="Left" Margin="0,0,0,15" VerticalAlignment="Bottom" Grid.Column="1" Grid.ColumnSpan="2"/>
|
||||||
<CheckBox x:Name="ContactEmailInput" Content="E-Mail" IsEnabled="False"
|
<CheckBox x:Name="ContactEmailInput" Content="E-Mail" IsEnabled="False"
|
||||||
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
|
Checked="ContactEmailInput_Changed" Unchecked="ContactEmailInput_Changed"
|
||||||
HorizontalAlignment="Left" Margin="60,0,0,15" VerticalAlignment="Bottom" Grid.Column="1" Grid.ColumnSpan="2"/>
|
HorizontalAlignment="Left" Margin="60,0,0,15" VerticalAlignment="Bottom" Grid.Column="1" Grid.ColumnSpan="2"/>
|
||||||
|
|
||||||
<Button x:Name="DeliveryButton" Content="Lieferungen" Click="DeliveryButton_Click" IsEnabled="False"
|
<Button x:Name="DeliveryButton" Content="Lieferungen" Click="DeliveryButton_Click" IsEnabled="False"
|
||||||
|
@ -1243,7 +1243,11 @@ namespace Elwig.Windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private new void EmailAddressInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
private new void EmailAddressInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
||||||
|
if (sender == EmailAddress1Input && ContactEmailInput.IsChecked == true) {
|
||||||
|
InputTextChanged((TextBox)sender, Validator.CheckEmailAddress((TextBox)sender, true));
|
||||||
|
} else {
|
||||||
base.EmailAddressInput_TextChanged(sender, evt);
|
base.EmailAddressInput_TextChanged(sender, evt);
|
||||||
|
}
|
||||||
UpdateContactInfoVisibility(IsEditing || IsCreating);
|
UpdateContactInfoVisibility(IsEditing || IsCreating);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1252,6 +1256,10 @@ namespace Elwig.Windows {
|
|||||||
UpdateContactInfoVisibility(IsEditing || IsCreating);
|
UpdateContactInfoVisibility(IsEditing || IsCreating);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ContactEmailInput_Changed(object sender, RoutedEventArgs evt) {
|
||||||
|
EmailAddressInput_TextChanged(EmailAddress1Input, new TextChangedEventArgs(evt.RoutedEvent, UndoAction.None));
|
||||||
|
}
|
||||||
|
|
||||||
private void KgDetailsButton_Click(object sender, RoutedEventArgs evt) {
|
private void KgDetailsButton_Click(object sender, RoutedEventArgs evt) {
|
||||||
if (DefaultKgInput.SelectedItem is AT_Kg kg) {
|
if (DefaultKgInput.SelectedItem is AT_Kg kg) {
|
||||||
App.FocusOriginHierarchyKg(kg.KgNr);
|
App.FocusOriginHierarchyKg(kg.KgNr);
|
||||||
|
@ -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.0" />
|
<PackageReference Include="WixToolset.Bal.wixext" Version="5.0.1" />
|
||||||
<PackageReference Include="WixToolset.Util.wixext" Version="5.0.0" />
|
<PackageReference Include="WixToolset.Util.wixext" Version="5.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -22,11 +22,11 @@ namespace Tests.DocumentTests {
|
|||||||
Winzerstraße 1
|
Winzerstraße 1
|
||||||
2223 Hohenruppersdorf
|
2223 Hohenruppersdorf
|
||||||
"""));
|
"""));
|
||||||
Assert.That(text, Contains.Substring("1472583")); // Betriebsnummer
|
Assert.That(text, Contains.Substring("0123463")); // Betriebsnummer
|
||||||
Assert.That(text, Contains.Substring("pauschaliert"));
|
Assert.That(text, Contains.Substring("pauschaliert"));
|
||||||
Assert.That(text, Contains.Substring($"Wolkersdorf, am {DateTime.Now:dd.MM.yyyy}"));
|
Assert.That(text, Contains.Substring($"Wolkersdorf, am {Elwig.Helpers.Utils.Today:dd.MM.yyyy}"));
|
||||||
Assert.That(text, Contains.Substring("Traubengutschrift Max Mustermann – Probevariante"));
|
Assert.That(text, Contains.Substring("Traubengutschrift Max Mustermann – Probevariante"));
|
||||||
Assert.That(text, Contains.Substring("AT12 3456 7890 1234 5678"));
|
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 75 15,4 ungeb.: 2 561 - -
|
||||||
|
@ -19,9 +19,9 @@ namespace Tests.DocumentTests {
|
|||||||
Winzerstraße 1
|
Winzerstraße 1
|
||||||
2223 Hohenruppersdorf
|
2223 Hohenruppersdorf
|
||||||
"""));
|
"""));
|
||||||
Assert.That(text, Contains.Substring("1472583")); // Betriebsnummer
|
Assert.That(text, Contains.Substring("0123463")); // Betriebsnummer
|
||||||
Assert.That(text, Contains.Substring("pauschaliert"));
|
Assert.That(text, Contains.Substring("pauschaliert"));
|
||||||
Assert.That(text, Contains.Substring($"Wolkersdorf, am {DateTime.Now: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 n
|
20201001X003 2 Grüner Veltliner Kabinett Kabinett 87 17,6 ungeb.: 3 129 3 129 n
|
||||||
|
@ -17,9 +17,9 @@ namespace Tests.DocumentTests {
|
|||||||
Winzerstraße 1
|
Winzerstraße 1
|
||||||
2223 Hohenruppersdorf
|
2223 Hohenruppersdorf
|
||||||
"""));
|
"""));
|
||||||
Assert.That(text, Contains.Substring("1472583")); // Betriebsnummer
|
Assert.That(text, Contains.Substring("0123463")); // Betriebsnummer
|
||||||
Assert.That(text, Contains.Substring("pauschaliert"));
|
Assert.That(text, Contains.Substring("pauschaliert"));
|
||||||
Assert.That(text, Contains.Substring($"Wolkersdorf, am {DateTime.Now:dd.MM.yyyy}"));
|
Assert.That(text, Contains.Substring($"Wolkersdorf, am {Elwig.Helpers.Utils.Today:dd.MM.yyyy}"));
|
||||||
Assert.That(text, Contains.Substring("Traubenübernahmeschein Nr. 20201001X001"));
|
Assert.That(text, Contains.Substring("Traubenübernahmeschein Nr. 20201001X001"));
|
||||||
Assert.That(text, Contains.Substring("Ich bin der Text, der auf einem Traubenübernahmeschein steht!"));
|
Assert.That(text, Contains.Substring("Ich bin der Text, der auf einem Traubenübernahmeschein steht!"));
|
||||||
Assert.That(text, Contains.Substring("""
|
Assert.That(text, Contains.Substring("""
|
||||||
@ -44,9 +44,9 @@ namespace Tests.DocumentTests {
|
|||||||
Winzerstraße 2
|
Winzerstraße 2
|
||||||
2223 Hohenruppersdorf
|
2223 Hohenruppersdorf
|
||||||
"""));
|
"""));
|
||||||
Assert.That(text, Contains.Substring("4725836")); // Betriebsnummer
|
Assert.That(text, Contains.Substring("0123471")); // Betriebsnummer
|
||||||
Assert.That(text, Contains.Substring("pauschaliert"));
|
Assert.That(text, Contains.Substring("pauschaliert"));
|
||||||
Assert.That(text, Contains.Substring($"Wolkersdorf, am {DateTime.Now:dd.MM.yyyy}"));
|
Assert.That(text, Contains.Substring($"Wolkersdorf, am {Elwig.Helpers.Utils.Today:dd.MM.yyyy}"));
|
||||||
Assert.That(text, Contains.Substring("Traubenübernahmeschein Nr. 20201001X004"));
|
Assert.That(text, Contains.Substring("Traubenübernahmeschein Nr. 20201001X004"));
|
||||||
Assert.That(text, Contains.Substring("Ich bin der Text, der auf einem Traubenübernahmeschein steht!"));
|
Assert.That(text, Contains.Substring("Ich bin der Text, der auf einem Traubenübernahmeschein steht!"));
|
||||||
Assert.That(text, Contains.Substring("""
|
Assert.That(text, Contains.Substring("""
|
||||||
@ -77,9 +77,9 @@ namespace Tests.DocumentTests {
|
|||||||
Winzerstraße 1
|
Winzerstraße 1
|
||||||
2223 Hohenruppersdorf
|
2223 Hohenruppersdorf
|
||||||
"""));
|
"""));
|
||||||
Assert.That(text, Contains.Substring("1472583")); // Betriebsnummer
|
Assert.That(text, Contains.Substring("0123463")); // Betriebsnummer
|
||||||
Assert.That(text, Contains.Substring("pauschaliert"));
|
Assert.That(text, Contains.Substring("pauschaliert"));
|
||||||
Assert.That(text, Contains.Substring($"Wolkersdorf, am {DateTime.Now:dd.MM.yyyy}"));
|
Assert.That(text, Contains.Substring($"Wolkersdorf, am {Elwig.Helpers.Utils.Today:dd.MM.yyyy}"));
|
||||||
Assert.That(text, Contains.Substring("Traubenübernahmeschein Nr. 20201001X003"));
|
Assert.That(text, Contains.Substring("Traubenübernahmeschein Nr. 20201001X003"));
|
||||||
Assert.That(text, Contains.Substring("Ich bin der Text, der auf einem Traubenübernahmeschein steht!"));
|
Assert.That(text, Contains.Substring("Ich bin der Text, der auf einem Traubenübernahmeschein steht!"));
|
||||||
Assert.That(text, Contains.Substring("""
|
Assert.That(text, Contains.Substring("""
|
||||||
@ -116,9 +116,9 @@ namespace Tests.DocumentTests {
|
|||||||
Brünner Straße 10
|
Brünner Straße 10
|
||||||
2120 Wolkersdorf im Weinviertel
|
2120 Wolkersdorf im Weinviertel
|
||||||
"""));
|
"""));
|
||||||
Assert.That(text, Contains.Substring("7258369")); // Betriebsnummer
|
Assert.That(text, Contains.Substring("0123480")); // Betriebsnummer
|
||||||
Assert.That(text, Contains.Substring("pauschaliert"));
|
Assert.That(text, Contains.Substring("pauschaliert"));
|
||||||
Assert.That(text, Contains.Substring($"Wolkersdorf, am {DateTime.Now:dd.MM.yyyy}"));
|
Assert.That(text, Contains.Substring($"Wolkersdorf, am {Elwig.Helpers.Utils.Today:dd.MM.yyyy}"));
|
||||||
Assert.That(text, Contains.Substring("Traubenübernahmeschein Nr. 20201002X001"));
|
Assert.That(text, Contains.Substring("Traubenübernahmeschein Nr. 20201002X001"));
|
||||||
Assert.That(text, Contains.Substring("Ich bin der Text, der auf einem Traubenübernahmeschein steht!"));
|
Assert.That(text, Contains.Substring("Ich bin der Text, der auf einem Traubenübernahmeschein steht!"));
|
||||||
Assert.That(text, Contains.Substring("""
|
Assert.That(text, Contains.Substring("""
|
||||||
|
@ -32,8 +32,8 @@ namespace Tests.DocumentTests {
|
|||||||
Adresse: Hauptstraße 1
|
Adresse: Hauptstraße 1
|
||||||
PLZ/Ort: 2122 Riedenthal (Riedenthal)
|
PLZ/Ort: 2122 Riedenthal (Riedenthal)
|
||||||
"""));
|
"""));
|
||||||
Assert.That(text, Contains.Substring("IBAN: AT12 3456 7890 1234 5678"));
|
Assert.That(text, Contains.Substring("IBAN: AT97 1234 5678 9012 3460"));
|
||||||
Assert.That(text, Contains.Substring("Betriebs-Nr.: 2583691"));
|
Assert.That(text, Contains.Substring("Betriebs-Nr.: 0123498"));
|
||||||
Assert.That(text, Contains.Substring("Stammgemeinde: Wolkersdorf"));
|
Assert.That(text, Contains.Substring("Stammgemeinde: Wolkersdorf"));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,9 @@ namespace Tests.DocumentTests {
|
|||||||
Assert.That(text, Contains.Substring("Mitgliederliste"));
|
Assert.That(text, Contains.Substring("Mitgliederliste"));
|
||||||
Assert.That(text, Contains.Substring("Alle Mitglieder"));
|
Assert.That(text, Contains.Substring("Alle Mitglieder"));
|
||||||
Assert.That(table.Take(3), Is.EqualTo(new string[][] {
|
Assert.That(table.Take(3), Is.EqualTo(new string[][] {
|
||||||
["101 MUSTERMANN Max", "Winzerstraße 1", "2223", "Hohenruppersdorf", "1472583", "0", "Hohenruppersdorf"],
|
["101 MUSTERMANN Max", "Winzerstraße 1", "2223", "Hohenruppersdorf", "0123463", "0", "Hohenruppersdorf"],
|
||||||
["102 WEINBAUER Wernhardt", "Winzerstraße 2", "2223", "Hohenruppersdorf", "4725836", "0", "Hohenruppersdorf"],
|
["102 WEINBAUER Wernhardt", "Winzerstraße 2", "2223", "Hohenruppersdorf", "0123471", "0", "Hohenruppersdorf"],
|
||||||
["", "W&B Weinbauer GesbR", "Winzerstraße 2", "2223", "Hohenruppersdorf"],
|
[ "W&B Weinbauer GesbR", "Winzerstraße 2", "2223", "Hohenruppersdorf"],
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ namespace Tests.DocumentTests {
|
|||||||
|
|
||||||
public static string[][] ExtractTable(string text) {
|
public static string[][] ExtractTable(string text) {
|
||||||
return text.Split('\n')
|
return text.Split('\n')
|
||||||
.Select(row => Regex.Split(row, @"\s{2,}").Select(c => c.Trim()).ToArray())
|
.Select(row => Regex.Split(row, @"\s{2,}").Select(c => c.Trim()).Where(c => c.Length > 0).ToArray())
|
||||||
.Where(row => row.Length > 3)
|
.Where(row => row.Length > 3)
|
||||||
.Skip(1)
|
.Skip(1)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
49
Tests/E2ETests/AppSession.cs
Normal file
49
Tests/E2ETests/AppSession.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
using OpenQA.Selenium.Appium.Windows;
|
||||||
|
using OpenQA.Selenium.Appium;
|
||||||
|
|
||||||
|
namespace Tests.E2ETests {
|
||||||
|
public class AppSession : IDisposable {
|
||||||
|
|
||||||
|
private const int WaitForAppLaunch = 3;
|
||||||
|
private readonly string WinAppDriverUrl;
|
||||||
|
public readonly WindowsDriver<WindowsElement> App;
|
||||||
|
public readonly WindowsDriver<WindowsElement> Desktop;
|
||||||
|
|
||||||
|
public AppSession(string appPath, string? appArgs, string winAppDriverUrl) {
|
||||||
|
WinAppDriverUrl = winAppDriverUrl;
|
||||||
|
var appiumOptions = new AppiumOptions();
|
||||||
|
appiumOptions.AddAdditionalCapability("app", appPath);
|
||||||
|
if (appArgs != null)
|
||||||
|
appiumOptions.AddAdditionalCapability("appArguments", appArgs);
|
||||||
|
appiumOptions.AddAdditionalCapability("deviceName", "WindowsPC");
|
||||||
|
appiumOptions.AddAdditionalCapability("ms:waitForAppLaunch", WaitForAppLaunch);
|
||||||
|
App = new WindowsDriver<WindowsElement>(new Uri(WinAppDriverUrl), appiumOptions);
|
||||||
|
Assert.That(App, Is.Not.Null);
|
||||||
|
Assert.That(App.SessionId, Is.Not.Null);
|
||||||
|
App.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(1.5);
|
||||||
|
var desktopOptions = new AppiumOptions();
|
||||||
|
desktopOptions.AddAdditionalCapability("app", "Root");
|
||||||
|
desktopOptions.AddAdditionalCapability("deviceName", "WindowsPC");
|
||||||
|
Desktop = new WindowsDriver<WindowsElement>(new Uri(WinAppDriverUrl), desktopOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowsDriver<WindowsElement> CreateWindowDriver(string windowName) {
|
||||||
|
var window = Desktop.FindElementByAccessibilityId(windowName);
|
||||||
|
var windowHandle = int.Parse(window.GetAttribute("NativeWindowHandle")).ToString("x"); // Convert to Hex
|
||||||
|
var appiumOptions = new AppiumOptions();
|
||||||
|
appiumOptions.AddAdditionalCapability("appTopLevelWindow", windowHandle);
|
||||||
|
return new WindowsDriver<WindowsElement>(new Uri(WinAppDriverUrl), appiumOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() {
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
App.Close();
|
||||||
|
try {
|
||||||
|
Desktop.FindElement(By.Name("Ja")).Click();
|
||||||
|
} catch { }
|
||||||
|
App.Dispose();
|
||||||
|
Desktop.Close();
|
||||||
|
Desktop.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
85
Tests/E2ETests/DeliveryAdminWindowReceiptTest.cs
Normal file
85
Tests/E2ETests/DeliveryAdminWindowReceiptTest.cs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
using OpenQA.Selenium;
|
||||||
|
using OpenQA.Selenium.Appium.Windows;
|
||||||
|
using Tests.WeighingTests;
|
||||||
|
|
||||||
|
namespace Tests.E2ETests {
|
||||||
|
[TestFixture, Order(2)]
|
||||||
|
public class DeliveryAdminWindowReceiptTest {
|
||||||
|
|
||||||
|
private MockScale Mock;
|
||||||
|
private AppSession Session;
|
||||||
|
|
||||||
|
[OneTimeSetUp]
|
||||||
|
public void Setup() {
|
||||||
|
Mock = new CommandMockScale(12345, ScaleHandlers.Handle_IT3000A) {
|
||||||
|
Weight = 3210,
|
||||||
|
};
|
||||||
|
Session = new(Utils.ApplicationPath, Utils.ConfigPath, WinAppDriver.WinAppDriverUrl);
|
||||||
|
Session.App.FindElement(By.Name("Stammdaten")).Click();
|
||||||
|
Thread.Sleep(500);
|
||||||
|
var window = Session.CreateWindowDriver("BaseDataWindow");
|
||||||
|
window.FindElement(By.Name("Saisons")).Click();
|
||||||
|
window.FindElement(By.Name("Neu anlegen...")).Click();
|
||||||
|
var dialog = Session.CreateWindowDriver("NewSeasonDialog");
|
||||||
|
dialog.FindElement(By.Name("Bestätigen")).Click();
|
||||||
|
dialog.Close();
|
||||||
|
Thread.Sleep(500);
|
||||||
|
window.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
[OneTimeTearDown]
|
||||||
|
public void Teardown() {
|
||||||
|
Session.Dispose();
|
||||||
|
Mock.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private WindowsDriver<WindowsElement> OpenReceiptWindow() {
|
||||||
|
Session.App.FindElement(By.Name("Übernahme")).Click();
|
||||||
|
Thread.Sleep(Utils.WINDOW_OPEN_SLEEP);
|
||||||
|
return Session.CreateWindowDriver("DeliveryAdminWindow");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FinishDeliveryNote(WindowsDriver<WindowsElement> window) {
|
||||||
|
window.FindElement(By.Name("Abschließen")).Click();
|
||||||
|
Thread.Sleep(2000);
|
||||||
|
var doc = Session.CreateWindowDriver("DocumentViewerWindow");
|
||||||
|
Assert.That(doc.Title, Contains.Substring("Traubenübernahmeschein"));
|
||||||
|
Thread.Sleep(500);
|
||||||
|
doc.Close();
|
||||||
|
Thread.Sleep(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test_1_Minimal() {
|
||||||
|
var window = OpenReceiptWindow();
|
||||||
|
window.FindElement(By.WpfId("MgNrInput")).SendKeys("101" + Keys.Enter + "GV" + Keys.Enter + "73" + Keys.Enter + Keys.Enter);
|
||||||
|
Thread.Sleep(500);
|
||||||
|
FinishDeliveryNote(window);
|
||||||
|
window.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test_2_OtherInputs() {
|
||||||
|
var window = OpenReceiptWindow();
|
||||||
|
window.FindElement(By.WpfId("MemberInput")).SendKeys("Mustermann Max");
|
||||||
|
window.FindElement(By.WpfId("WineVarietyInput")).SelectItem("Zweigelt");
|
||||||
|
window.FindElement(By.WpfId("GradationKmwInput")).SendKeys("18");
|
||||||
|
window.FindElement(By.Name("Wiegen")).Click();
|
||||||
|
Thread.Sleep(500);
|
||||||
|
FinishDeliveryNote(window);
|
||||||
|
window.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test_3_AttributeCultivationModifier() {
|
||||||
|
var window = OpenReceiptWindow();
|
||||||
|
window.FindElement(By.WpfId("MgNrInput")).SendKeys("102" + Keys.Enter + "GVK");
|
||||||
|
window.FindElement(By.WpfId("CultivationInput")).SelectItem("Bio");
|
||||||
|
window.FindElement(By.WpfId("GradationOeInput")).SendKeys("73" + Keys.Enter + Keys.Enter);
|
||||||
|
|
||||||
|
Thread.Sleep(500);
|
||||||
|
FinishDeliveryNote(window);
|
||||||
|
window.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
61
Tests/E2ETests/MainWindowTest.cs
Normal file
61
Tests/E2ETests/MainWindowTest.cs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
namespace Tests.E2ETests {
|
||||||
|
[TestFixture, Order(1)]
|
||||||
|
public class MainWindowTest {
|
||||||
|
|
||||||
|
private AppSession Session;
|
||||||
|
|
||||||
|
[OneTimeSetUp]
|
||||||
|
public void Setup() {
|
||||||
|
Session = new(Utils.ApplicationPath, Utils.ConfigPath, WinAppDriver.WinAppDriverUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
[OneTimeTearDown]
|
||||||
|
public void Teardown() {
|
||||||
|
Session.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test_Open_MemberAdminWindow() {
|
||||||
|
Assert.DoesNotThrow(() => {
|
||||||
|
Session.App.FindElement(By.Name("Mitglieder")).Click();
|
||||||
|
Thread.Sleep(Utils.WINDOW_OPEN_SLEEP);
|
||||||
|
var window = Session.CreateWindowDriver("MemberAdminWindow");
|
||||||
|
Assert.That(window.Title, Is.EqualTo("Mitglieder - Elwig"));
|
||||||
|
window.Close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test_Open_DeliveryAdminWindow() {
|
||||||
|
Assert.DoesNotThrow(() => {
|
||||||
|
Session.App.FindElement(By.Name("Lieferungen")).Click();
|
||||||
|
Thread.Sleep(Utils.WINDOW_OPEN_SLEEP);
|
||||||
|
var window = Session.CreateWindowDriver("DeliveryAdminWindow");
|
||||||
|
Assert.That(window.Title, Is.EqualTo("Lieferungen - Elwig"));
|
||||||
|
window.Close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test_Open_BaseDataWindow() {
|
||||||
|
Assert.DoesNotThrow(() => {
|
||||||
|
Session.App.FindElement(By.Name("Stammdaten")).Click();
|
||||||
|
Thread.Sleep(Utils.WINDOW_OPEN_SLEEP);
|
||||||
|
var window = Session.CreateWindowDriver("BaseDataWindow");
|
||||||
|
Assert.That(window.Title, Is.EqualTo("Stammdaten - Elwig"));
|
||||||
|
window.Close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test_Open_MailWindow() {
|
||||||
|
Assert.DoesNotThrow(() => {
|
||||||
|
Session.App.FindElement(By.Name("Rundschreiben")).Click();
|
||||||
|
Thread.Sleep(Utils.WINDOW_OPEN_SLEEP);
|
||||||
|
var window = Session.CreateWindowDriver("MailWindow");
|
||||||
|
Assert.That(window.Title, Is.EqualTo($"Rundschreiben - Lese {Elwig.Helpers.Utils.Today.Year} - Elwig"));
|
||||||
|
window.Close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
146
Tests/E2ETests/MemberAdminWindowTest.cs
Normal file
146
Tests/E2ETests/MemberAdminWindowTest.cs
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
using OpenQA.Selenium.Appium.Windows;
|
||||||
|
|
||||||
|
namespace Tests.E2ETests {
|
||||||
|
[TestFixture, Order(3)]
|
||||||
|
public class MemberAdminWindowTest {
|
||||||
|
|
||||||
|
private AppSession Session;
|
||||||
|
private WindowsDriver<WindowsElement> Window;
|
||||||
|
|
||||||
|
[OneTimeSetUp]
|
||||||
|
public void WindowSetup() {
|
||||||
|
Session = new(Utils.ApplicationPath, Utils.ConfigPath, WinAppDriver.WinAppDriverUrl);
|
||||||
|
Session.App.FindElement(By.Name("Mitglieder")).Click();
|
||||||
|
Thread.Sleep(Utils.WINDOW_OPEN_SLEEP);
|
||||||
|
Window = Session.CreateWindowDriver("MemberAdminWindow");
|
||||||
|
}
|
||||||
|
|
||||||
|
[OneTimeTearDown]
|
||||||
|
public void WindowTeardown() {
|
||||||
|
Window.Close();
|
||||||
|
Window.Quit();
|
||||||
|
Session.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
[TearDown]
|
||||||
|
public void Teardown() {
|
||||||
|
Window.FindElement(By.WpfId("SearchInput")).Clear();
|
||||||
|
Thread.Sleep(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test_1_CreateMember() {
|
||||||
|
Window.FindElement(By.WpfId("NewMemberButton")).Click();
|
||||||
|
|
||||||
|
Window.FindElement(By.WpfId("MgNrInput")).Clear();
|
||||||
|
Window.FindElement(By.WpfId("MgNrInput")).SendKeys("9999");
|
||||||
|
|
||||||
|
Window.FindElement(By.WpfId("GivenNameInput")).SendKeys("Norbert");
|
||||||
|
Window.FindElement(By.WpfId("FamilyNameInput")).SendKeys("Neuling");
|
||||||
|
Window.FindElement(By.WpfId("PrefixInput")).SendKeys("Ing.");
|
||||||
|
Window.FindElement(By.WpfId("SuffixInput")).SendKeys("jun.");
|
||||||
|
Window.FindElement(By.WpfId("BirthdayInput")).SendKeys("1987");
|
||||||
|
|
||||||
|
Window.FindElement(By.WpfId("AddressInput")).SendKeys("Musterstraße 9");
|
||||||
|
Window.FindElement(By.WpfId("PlzInput")).SendKeys("2120");
|
||||||
|
Window.FindElement(By.WpfId("OrtInput")).SelectItem(1);
|
||||||
|
|
||||||
|
Window.FindElement(By.WpfId("EmailAddress1Input")).SendKeys("norbert.neuling@aon.at");
|
||||||
|
Window.FindElement(By.WpfId("EmailAddress2Input")).SendKeys("nathalie.neuling@aon.at");
|
||||||
|
|
||||||
|
Window.FindElement(By.WpfId("PhoneNr1TypeInput")).SelectItem(1);
|
||||||
|
Window.FindElement(By.WpfId("PhoneNr1Input")).SendKeys("012345678");
|
||||||
|
|
||||||
|
Window.FindElement(By.WpfId("PhoneNr2TypeInput")).SelectItem(2);
|
||||||
|
Window.FindElement(By.WpfId("PhoneNr2Input")).SendKeys("0664123456");
|
||||||
|
|
||||||
|
Window.FindElement(By.WpfId("IbanInput")).SendKeys("AT611904300234573201");
|
||||||
|
Window.FindElement(By.WpfId("BicInput")).SendKeys("RLNWATWWWDF");
|
||||||
|
|
||||||
|
Window.FindElement(By.WpfId("UstIdNrInput")).SendKeys("ATU66192906"); // TODO: Testdaten?
|
||||||
|
Window.FindElement(By.WpfId("LfbisNrInput")).SendKeys("1251074"); // TODO: Testdaten?
|
||||||
|
|
||||||
|
Window.FindElement(By.WpfId("BuchführendInput")).Click();
|
||||||
|
Window.FindElement(By.WpfId("OrganicInput")).Click();
|
||||||
|
|
||||||
|
Window.FindElement(By.WpfId("BillingNameInput")).SendKeys("Neuling KG");
|
||||||
|
Window.FindElement(By.WpfId("BillingAddressInput")).SendKeys("Betriebsstraße 1");
|
||||||
|
Window.FindElement(By.WpfId("BillingPlzInput")).SendKeys("2120");
|
||||||
|
Window.FindElement(By.WpfId("BillingOrtInput")).SelectItem(2);
|
||||||
|
|
||||||
|
Window.FindElement(By.WpfId("BusinessSharesInput")).SendKeys("10");
|
||||||
|
Window.FindElement(By.WpfId("BranchInput")).SelectItem("Matzen");
|
||||||
|
Window.FindElement(By.WpfId("DefaultKgInput")).SelectItem(3);
|
||||||
|
|
||||||
|
Window.FindElement(By.WpfId("VollLieferantInput")).Click();
|
||||||
|
Window.FindElement(By.WpfId("FunkionärInput")).Click();
|
||||||
|
|
||||||
|
Window.FindElement(By.WpfId("CommentInput")).SendKeys("Die lieben Mustermänner und Musterfrauen!");
|
||||||
|
Window.FindElement(By.WpfId("ContactEmailInput")).Click();
|
||||||
|
|
||||||
|
Window.FindElement(By.WpfId("SaveButton")).Click();
|
||||||
|
|
||||||
|
Window.FindElement(By.WpfId("SearchInput")).SendKeys("9999");
|
||||||
|
Thread.Sleep(500);
|
||||||
|
var memberListRow = Window.FindElement(By.WpfId("MemberList")).FindElement(By.ClassName("DataGridRow"));
|
||||||
|
Assert.Multiple(() => {
|
||||||
|
Assert.That(memberListRow, Is.Not.Null);
|
||||||
|
Assert.That(memberListRow.FindElement(By.Name("9999 ")), Is.Not.Null);
|
||||||
|
Assert.That(memberListRow.FindElement(By.Name("Norbert")), Is.Not.Null);
|
||||||
|
Assert.That(memberListRow.FindElement(By.Name("Neuling")), Is.Not.Null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test_2_EditMember() {
|
||||||
|
Window!.FindElement(By.WpfId("SearchInput")).SendKeys("9999");
|
||||||
|
Thread.Sleep(500);
|
||||||
|
var memberList = Window.FindElement(By.WpfId("MemberList"));
|
||||||
|
Assert.That(memberList, Is.Not.Null);
|
||||||
|
|
||||||
|
var memberListRows = memberList.FindElements(By.ClassName("DataGridRow"));
|
||||||
|
Assert.That(memberListRows, Has.Count.EqualTo(1));
|
||||||
|
|
||||||
|
Window.FindElement(By.WpfId("EditMemberButton")).Click();
|
||||||
|
|
||||||
|
var businessSharesInput = Window.FindElement(By.WpfId("BusinessSharesInput"));
|
||||||
|
Assert.That(businessSharesInput, Is.Not.Null);
|
||||||
|
|
||||||
|
var businessShares = int.Parse(businessSharesInput.Text);
|
||||||
|
businessSharesInput.Clear();
|
||||||
|
businessSharesInput.SendKeys($"{businessShares + 5}");
|
||||||
|
|
||||||
|
Window.FindElement(By.WpfId("SaveButton")).Click();
|
||||||
|
|
||||||
|
var newBusinessShares = int.Parse(businessSharesInput.Text);
|
||||||
|
Assert.That(newBusinessShares, Is.EqualTo(businessShares + 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Test_3_DeleteMember() {
|
||||||
|
Window!.FindElement(By.WpfId("SearchInput")).SendKeys("9999");
|
||||||
|
Thread.Sleep(500);
|
||||||
|
var memberList = Window.FindElement(By.WpfId("MemberList"));
|
||||||
|
Assert.That(memberList, Is.Not.Null);
|
||||||
|
|
||||||
|
var memberListRows = memberList.FindElements(By.ClassName("DataGridRow"));
|
||||||
|
Assert.That(memberListRows, Has.Count.EqualTo(1));
|
||||||
|
|
||||||
|
var memberListRow = memberListRows.First();
|
||||||
|
Assert.Multiple(() => {
|
||||||
|
Assert.That(memberListRow, Is.Not.Null);
|
||||||
|
Assert.That(memberListRow.FindElement(By.Name("9999 ")), Is.Not.Null);
|
||||||
|
Assert.That(memberListRow.FindElement(By.Name("Norbert")), Is.Not.Null);
|
||||||
|
Assert.That(memberListRow.FindElement(By.Name("Neuling")), Is.Not.Null);
|
||||||
|
});
|
||||||
|
|
||||||
|
Window.FindElement(By.WpfId("DeleteMemberButton")).Click();
|
||||||
|
var dialog = Session.CreateWindowDriver("DeleteMemberDialog");
|
||||||
|
dialog.FindElement(By.WpfId("NameInput")).SendKeys("9999 Ing. Norbert Neuling jun.");
|
||||||
|
dialog.FindElement(By.WpfId("ConfirmButton")).Click();
|
||||||
|
|
||||||
|
memberListRows = memberList.FindElements(By.ClassName("DataGridRow"));
|
||||||
|
Assert.That(memberListRows, Has.Count.EqualTo(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
Tests/E2ETests/Setup.cs
Normal file
37
Tests/E2ETests/Setup.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using Elwig.Helpers;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Tests.E2ETests {
|
||||||
|
[SetUpFixture]
|
||||||
|
public static class Setup {
|
||||||
|
|
||||||
|
private static WinAppDriver? Driver;
|
||||||
|
|
||||||
|
[OneTimeSetUp]
|
||||||
|
public static void SetupWinAppDriver() {
|
||||||
|
Driver = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
[OneTimeSetUp]
|
||||||
|
public static async Task SetupDatabase() {
|
||||||
|
if (File.Exists(Utils.TestDatabasePath)) File.Delete(Utils.TestDatabasePath);
|
||||||
|
using var cnx = await AppDbContext.ConnectAsync($"Data Source=\"{Utils.TestDatabasePath}\"; Mode=ReadWriteCreate; Foreign Keys=True; Cache=Default");
|
||||||
|
await AppDbContext.ExecuteEmbeddedScript(cnx, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.Create.sql");
|
||||||
|
await AppDbContext.ExecuteEmbeddedScript(cnx, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.Insert.sql");
|
||||||
|
await AppDbContext.ExecuteEmbeddedScript(cnx, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.E2EInsert.sql");
|
||||||
|
}
|
||||||
|
|
||||||
|
[OneTimeTearDown]
|
||||||
|
public static void TeardownWinAppDriver() {
|
||||||
|
Driver?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
[OneTimeTearDown]
|
||||||
|
public static void TeardownDatabase() {
|
||||||
|
try {
|
||||||
|
// FIXME not working - other process using file
|
||||||
|
File.Delete(Utils.TestDatabasePath);
|
||||||
|
} catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
Tests/E2ETests/Utils.cs
Normal file
31
Tests/E2ETests/Utils.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using OpenQA.Selenium;
|
||||||
|
using OpenQA.Selenium.Appium;
|
||||||
|
|
||||||
|
namespace Tests.E2ETests {
|
||||||
|
public static class Utils {
|
||||||
|
|
||||||
|
public const int WINDOW_OPEN_SLEEP = 2000;
|
||||||
|
|
||||||
|
public static readonly string ApplicationPath = Path.GetFullPath(@"..\..\..\..\Elwig\bin\Debug\net8.0-windows\Elwig.exe");
|
||||||
|
public static readonly string ConfigPath = Path.GetFullPath(@"..\..\..\..\Tests\config.test.ini");
|
||||||
|
public static readonly string TestDatabasePath = Path.GetFullPath(@"..\..\..\..\Tests\ElwigTestDB.sqlite3");
|
||||||
|
|
||||||
|
public static void SelectItem(this IWebElement element, int count) {
|
||||||
|
element.Click();
|
||||||
|
element.SendKeys(string.Concat(Enumerable.Repeat(Keys.Down, count)));
|
||||||
|
element.SendKeys(Keys.Enter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SelectItem(this IWebElement element, string text) {
|
||||||
|
element.Click();
|
||||||
|
element.SendKeys(text);
|
||||||
|
element.SendKeys(Keys.Enter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class By : OpenQA.Selenium.By {
|
||||||
|
public static OpenQA.Selenium.By WpfId(string wpfName) {
|
||||||
|
return new ByAccessibilityId(wpfName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
Tests/E2ETests/WinAppDriver.cs
Normal file
27
Tests/E2ETests/WinAppDriver.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Tests.E2ETests {
|
||||||
|
public class WinAppDriver : IDisposable {
|
||||||
|
|
||||||
|
public const string WinAppDriverUrl = "http://127.0.0.1:4723";
|
||||||
|
private const string WinAppDriverPath = @"C:\Program Files (x86)\Windows Application Driver\WinAppDriver.exe";
|
||||||
|
private readonly Process WinAppDriverProcess;
|
||||||
|
|
||||||
|
public WinAppDriver() {
|
||||||
|
WinAppDriverProcess = Process.Start(new ProcessStartInfo(WinAppDriverPath) {
|
||||||
|
//UseShellExecute = true,
|
||||||
|
//Verb = "runas", // run as administrator
|
||||||
|
RedirectStandardInput = true,
|
||||||
|
EnvironmentVariables = {
|
||||||
|
// { "DX.UITESTINGENABLED", "1" },
|
||||||
|
}
|
||||||
|
})!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() {
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
WinAppDriverProcess.StandardInput.WriteLine("");
|
||||||
|
WinAppDriverProcess.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -31,9 +31,9 @@ INSERT INTO season (year, currency, min_kg_per_bs, max_kg_per_bs, penalty_per_kg
|
|||||||
(2020, 'EUR', 1000, 2000, NULL, NULL, NULL, NULL, NULL),
|
(2020, 'EUR', 1000, 2000, NULL, NULL, NULL, NULL, NULL),
|
||||||
(2021, 'EUR', 2000, 4000, NULL, NULL, NULL, NULL, NULL);
|
(2021, 'EUR', 2000, 4000, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
|
||||||
INSERT INTO modifier (year, modid, ordering, name, abs, rel, standard, quick_select) VALUES
|
INSERT INTO modifier (year, modid, ordering, name, abs, rel, active) VALUES
|
||||||
(2020, 'S', 0, 'Geschädigte Trauben', NULL, -0.1, FALSE, FALSE),
|
(2020, 'S', 0, 'Geschädigte Trauben', NULL, -0.1, TRUE),
|
||||||
(2020, 'A', 0, 'Keine Voranmeldung', -1000, NULL, FALSE, FALSE);
|
(2020, 'A', 0, 'Keine Voranmeldung', -1000, NULL, TRUE);
|
||||||
|
|
||||||
-- Test 01
|
-- Test 01
|
||||||
INSERT INTO delivery (mgnr, year, did, date, time, zwstid, lnr) VALUES
|
INSERT INTO delivery (mgnr, year, did, date, time, zwstid, lnr) VALUES
|
||||||
|
@ -9,9 +9,9 @@ INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lo
|
|||||||
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
|
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
|
||||||
(2020, 'EUR', 1000, 2000, NULL, NULL, NULL, NULL, NULL);
|
(2020, 'EUR', 1000, 2000, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
|
||||||
INSERT INTO modifier (year, modid, ordering, name, abs, rel, standard, quick_select) VALUES
|
INSERT INTO modifier (year, modid, ordering, name, abs, rel, active) VALUES
|
||||||
(2020, 'S', 0, 'Geschädigte Trauben', NULL, -0.1, FALSE, FALSE),
|
(2020, 'S', 0, 'Geschädigte Trauben', NULL, -0.1, TRUE),
|
||||||
(2020, 'A', 0, 'Keine Voranmeldung', -1000, NULL, FALSE, FALSE);
|
(2020, 'A', 0, 'Keine Voranmeldung', -1000, NULL, TRUE);
|
||||||
|
|
||||||
INSERT INTO delivery (mgnr, year, did, date, time, zwstid, lnr) VALUES
|
INSERT INTO delivery (mgnr, year, did, date, time, zwstid, lnr) VALUES
|
||||||
(101, 2020, 1, '2020-10-01', '09:03:12', 'X', 1),
|
(101, 2020, 1, '2020-10-01', '09:03:12', 'X', 1),
|
||||||
|
9
Tests/Resources/Sql/E2EInsert.sql
Normal file
9
Tests/Resources/Sql/E2EInsert.sql
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
-- inserts for E2ETests
|
||||||
|
|
||||||
|
INSERT INTO wine_cultivation (cultid, name, description) VALUES
|
||||||
|
('KIP', 'KIP', 'Kontrollierte Integrierte Produktion'),
|
||||||
|
('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),
|
||||||
|
('D', 'DAC', TRUE, 7500, FALSE, 0);
|
@ -42,7 +42,9 @@ INSERT INTO client_parameter (param, value) VALUES
|
|||||||
('CLIENT_PLZ', '2120'),
|
('CLIENT_PLZ', '2120'),
|
||||||
('CLIENT_ORT', 'Wolkersdorf'),
|
('CLIENT_ORT', 'Wolkersdorf'),
|
||||||
('CLIENT_ADDRESS', 'Genossenschaftsstraße 1'),
|
('CLIENT_ADDRESS', 'Genossenschaftsstraße 1'),
|
||||||
('CLIENT_IBAN', 'AT12 3456 7890 1234 5678'),
|
('CLIENT_IBAN', 'AT11 1234 5678 9012 3456'),
|
||||||
|
('CLIENT_LFBISNR', '0123455'),
|
||||||
|
('CLIENT_USTIDNR', 'ATU12345675'),
|
||||||
('TEXT_DELIVERYNOTE', 'Ich bin der Text, der auf einem Traubenübernahmeschein steht!');
|
('TEXT_DELIVERYNOTE', 'Ich bin der Text, der auf einem Traubenübernahmeschein steht!');
|
||||||
|
|
||||||
INSERT INTO branch (zwstid, name, country, postal_dest, address) VALUES
|
INSERT INTO branch (zwstid, name, country, postal_dest, address) VALUES
|
||||||
@ -64,11 +66,11 @@ INSERT INTO wb_kg (kgnr, glnr) VALUES
|
|||||||
(15216, 2),
|
(15216, 2),
|
||||||
(15224, 2);
|
(15224, 2);
|
||||||
|
|
||||||
INSERT INTO member (mgnr, given_name, family_name, zwstid, volllieferant, buchführend, country, postal_dest, address, default_kgnr, iban, lfbis_nr) VALUES
|
INSERT INTO member (mgnr, given_name, family_name, zwstid, volllieferant, buchführend, country, postal_dest, address, default_kgnr, iban, lfbis_nr, ustid_nr) VALUES
|
||||||
(101, 'Max', 'Mustermann', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 1', 06109, 'AT123456789012345678', '1472583'),
|
(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, 'AT123456789012345678', '4725836'),
|
(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, 'AT123456789012345678', '7258369'),
|
(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, 'AT123456789012345678', '2583691');
|
(104, 'Waltraud', 'Winzer', 'X', FALSE, FALSE, 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'),
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||||
|
<PackageReference Include="Appium.WebDriver" Version="4.4.5" />
|
||||||
<PackageReference Include="NReco.PdfRenderer" Version="1.5.3" />
|
<PackageReference Include="NReco.PdfRenderer" Version="1.5.3" />
|
||||||
<PackageReference Include="NUnit" Version="4.1.0" />
|
<PackageReference Include="NUnit" Version="4.1.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Tests.WeighingTests {
|
namespace Tests.WeighingTests {
|
||||||
public abstract class MockScale : IDisposable {
|
public abstract class MockScale : IDisposable {
|
||||||
|
104
Tests/WeighingTests/ScaleHandlers.cs
Normal file
104
Tests/WeighingTests/ScaleHandlers.cs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
namespace Tests.WeighingTests {
|
||||||
|
public static class ScaleHandlers {
|
||||||
|
|
||||||
|
public static (string, bool) Handle_IT3000A(string req, int weight, string? error, int identNr) {
|
||||||
|
var modes = error?.Split(';') ?? [];
|
||||||
|
var overloaded = modes.Contains("overloaded");
|
||||||
|
var moving = modes.Contains("moving");
|
||||||
|
var invalid = modes.Contains("invalid");
|
||||||
|
var crc = modes.Contains("crc");
|
||||||
|
var unit = modes.Contains("unit");
|
||||||
|
|
||||||
|
Thread.Sleep(100);
|
||||||
|
|
||||||
|
if (invalid) {
|
||||||
|
return ("abcd\r\n", false);
|
||||||
|
} else if (!req.StartsWith('<') || !req.EndsWith('>')) {
|
||||||
|
return ("<31>\r\n", false);
|
||||||
|
}
|
||||||
|
req = req[1..^1];
|
||||||
|
|
||||||
|
bool incr;
|
||||||
|
if (req.Length > 3) {
|
||||||
|
return ("<32>\r\n", false);
|
||||||
|
} else if (req.StartsWith("RN")) {
|
||||||
|
incr = true;
|
||||||
|
} else if (req.StartsWith("RM")) {
|
||||||
|
incr = false;
|
||||||
|
} else {
|
||||||
|
return ("<32>\r\n", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overloaded) {
|
||||||
|
return ("<12>\r\n", false);
|
||||||
|
} else if (weight == 0) {
|
||||||
|
incr = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moving && incr)
|
||||||
|
return ("<13>\r\n", false);
|
||||||
|
|
||||||
|
string data = $"00{(moving ? 1 : 0)}0{new DateTime(2020, 10, 15, 12, 34, 0):dd.MM.yyHH:mm}{(incr ? identNr : 0),4}1" +
|
||||||
|
$"{weight,8}{0,8}{weight,8}{(unit ? "lb" : "kg")} {"1",3}";
|
||||||
|
ushort checksum = Elwig.Helpers.Utils.CalcCrc16Modbus(data);
|
||||||
|
if (crc) checksum += 10;
|
||||||
|
return ($"<{data}{checksum,8}>\r\n", incr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static (string, bool) Handle_L246(string req, int weight, string? error, int identNr) {
|
||||||
|
var modes = error?.Split(';') ?? [];
|
||||||
|
var overloaded = modes.Contains("overloaded");
|
||||||
|
var moving = modes.Contains("moving");
|
||||||
|
var invalid = modes.Contains("invalid");
|
||||||
|
var crc = modes.Contains("crc");
|
||||||
|
var unit = modes.Contains("unit");
|
||||||
|
|
||||||
|
Thread.Sleep(100);
|
||||||
|
|
||||||
|
if (invalid) {
|
||||||
|
return ("abcd\r\n", false);
|
||||||
|
} else if (!req.StartsWith('<') || !req.EndsWith('>')) {
|
||||||
|
return ("<31>\r\n", false);
|
||||||
|
}
|
||||||
|
req = req[1..^1];
|
||||||
|
|
||||||
|
bool incr;
|
||||||
|
if (req.Length > 3) {
|
||||||
|
return ("<32>\r\n", false);
|
||||||
|
} else if (req.StartsWith("RN")) {
|
||||||
|
incr = true;
|
||||||
|
} else if (req.StartsWith("RM")) {
|
||||||
|
incr = false;
|
||||||
|
} else {
|
||||||
|
return ("<32>\r\n", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overloaded)
|
||||||
|
return ("<12>\r\n", false);
|
||||||
|
|
||||||
|
if (moving && incr)
|
||||||
|
return ("<13>\r\n", false);
|
||||||
|
|
||||||
|
string data = $"00{(moving ? 1 : 0)}0{new DateTime(2020, 10, 17, 14, 23, 0):dd.MM.yyHH:mm}{(incr ? identNr : 0),4}1" +
|
||||||
|
$"{weight,8}{0,8}{weight,8}{(unit ? "lb" : "kg")} {"001",3}";
|
||||||
|
ushort checksum = Elwig.Helpers.Utils.CalcCrc16Modbus(data);
|
||||||
|
if (crc) checksum += 10;
|
||||||
|
return ($"<{data}{checksum,8}>\r\n", incr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static (string, bool) Handle_L320(int weight, string? error, int identNr) {
|
||||||
|
var modes = error?.Split(';') ?? [];
|
||||||
|
var invalid = modes.Contains("invalid");
|
||||||
|
var unit = modes.Contains("unit");
|
||||||
|
|
||||||
|
Thread.Sleep(100);
|
||||||
|
|
||||||
|
if (invalid) {
|
||||||
|
return ("abcd\r\n", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,49 +4,36 @@ namespace Tests.WeighingTests {
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class ScaleTestBadenL320 {
|
public class ScaleTestBadenL320 {
|
||||||
|
|
||||||
private EventMockScale? Mock;
|
private EventMockScale Mock;
|
||||||
private AveryEventScale? Scale;
|
private AveryEventScale Scale;
|
||||||
|
|
||||||
private static (string, bool) ScaleHandler(int weight, string? error, int identNr) {
|
|
||||||
var modes = error?.Split(';') ?? [];
|
|
||||||
var invalid = modes.Contains("invalid");
|
|
||||||
var unit = modes.Contains("unit");
|
|
||||||
|
|
||||||
if (invalid) {
|
|
||||||
return ("abcd\r\n", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
[OneTimeSetUp]
|
[OneTimeSetUp]
|
||||||
public void SetupScale() {
|
public void SetupScale() {
|
||||||
Mock = new EventMockScale(12345, ScaleHandler);
|
Mock = new EventMockScale(12345, ScaleHandlers.Handle_L320);
|
||||||
Scale = new("1", "L320", "tcp://127.0.0.1:12345");
|
Scale = new("1", "L320", "tcp://127.0.0.1:12345");
|
||||||
}
|
}
|
||||||
|
|
||||||
[OneTimeTearDown]
|
[OneTimeTearDown]
|
||||||
public void TeardownScale() {
|
public void TeardownScale() {
|
||||||
Mock?.Dispose();
|
Mock.Dispose();
|
||||||
Scale?.Dispose();
|
Scale.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void ResetScale() {
|
public void ResetScale() {
|
||||||
Mock!.IdentNr = 0;
|
Mock.IdentNr = 0;
|
||||||
Mock!.Weight = 0;
|
Mock.Weight = 0;
|
||||||
Mock!.Error = null;
|
Mock.Error = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task Test_01_Normal() {
|
public async Task Test_01_Normal() {
|
||||||
WeighingResult? res = null;
|
WeighingResult? res = null;
|
||||||
Scale!.WeighingEvent += (sender, evt) => {
|
Scale.WeighingEvent += (sender, evt) => {
|
||||||
res = evt.Result;
|
res = evt.Result;
|
||||||
};
|
};
|
||||||
|
|
||||||
await Mock!.Weigh(2345);
|
await Mock.Weigh(2345);
|
||||||
await Task.Delay(100);
|
await Task.Delay(100);
|
||||||
Assert.That(res, Is.Not.Null);
|
Assert.That(res, Is.Not.Null);
|
||||||
Assert.That(res, Is.EqualTo(new WeighingResult {
|
Assert.That(res, Is.EqualTo(new WeighingResult {
|
||||||
@ -55,7 +42,7 @@ namespace Tests.WeighingTests {
|
|||||||
Date = new DateOnly(2020, 9, 28), Time = new TimeOnly(9, 8),
|
Date = new DateOnly(2020, 9, 28), Time = new TimeOnly(9, 8),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
await Mock!.Weigh(4215);
|
await Mock.Weigh(4215);
|
||||||
await Task.Delay(100);
|
await Task.Delay(100);
|
||||||
Assert.That(res, Is.Not.Null);
|
Assert.That(res, Is.Not.Null);
|
||||||
Assert.That(res, Is.EqualTo(new WeighingResult {
|
Assert.That(res, Is.EqualTo(new WeighingResult {
|
||||||
|
@ -4,78 +4,39 @@ namespace Tests.WeighingTests {
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
class ScaleTestGrInzersdorfL246 {
|
class ScaleTestGrInzersdorfL246 {
|
||||||
|
|
||||||
private MockScale? Mock;
|
private MockScale Mock;
|
||||||
private SysTecITScale? Scale;
|
private SysTecITScale Scale;
|
||||||
|
|
||||||
private static (string, bool) ScaleHandler(string req, int weight, string? error, int identNr) {
|
|
||||||
var modes = error?.Split(';') ?? [];
|
|
||||||
var overloaded = modes.Contains("overloaded");
|
|
||||||
var moving = modes.Contains("moving");
|
|
||||||
var invalid = modes.Contains("invalid");
|
|
||||||
var crc = modes.Contains("crc");
|
|
||||||
var unit = modes.Contains("unit");
|
|
||||||
|
|
||||||
if (invalid) {
|
|
||||||
return ("abcd\r\n", false);
|
|
||||||
} else if (!req.StartsWith('<') || !req.EndsWith('>')) {
|
|
||||||
return ("<31>\r\n", false);
|
|
||||||
}
|
|
||||||
req = req[1..^1];
|
|
||||||
|
|
||||||
bool incr;
|
|
||||||
if (req.Length > 3) {
|
|
||||||
return ("<32>\r\n", false);
|
|
||||||
} else if (req.StartsWith("RN")) {
|
|
||||||
incr = true;
|
|
||||||
} else if (req.StartsWith("RM")) {
|
|
||||||
incr = false;
|
|
||||||
} else {
|
|
||||||
return ("<32>\r\n", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (overloaded)
|
|
||||||
return ("<12>\r\n", false);
|
|
||||||
|
|
||||||
if (moving && incr)
|
|
||||||
return ("<13>\r\n", false);
|
|
||||||
|
|
||||||
string data = $"00{(moving ? 1 : 0)}0{new DateTime(2020, 10, 17, 14, 23, 0):dd.MM.yyHH:mm}{(incr ? identNr : 0),4}1" +
|
|
||||||
$"{weight,8}{0,8}{weight,8}{(unit ? "lb" : "kg")} {"001",3}";
|
|
||||||
ushort checksum = Elwig.Helpers.Utils.CalcCrc16Modbus(data);
|
|
||||||
if (crc) checksum += 10;
|
|
||||||
return ($"<{data}{checksum,8}>\r\n", incr);
|
|
||||||
}
|
|
||||||
|
|
||||||
[OneTimeSetUp]
|
[OneTimeSetUp]
|
||||||
public void SetupScale() {
|
public void SetupScale() {
|
||||||
Mock = new CommandMockScale(12345, ScaleHandler);
|
Mock = new CommandMockScale(12345, ScaleHandlers.Handle_L246);
|
||||||
Scale = new("1", "L246", "tcp://127.0.0.1:12345");
|
Scale = new("1", "L246", "tcp://127.0.0.1:12345");
|
||||||
}
|
}
|
||||||
|
|
||||||
[OneTimeTearDown]
|
[OneTimeTearDown]
|
||||||
public void TeardownScale() {
|
public void TeardownScale() {
|
||||||
Mock?.Dispose();
|
Mock.Dispose();
|
||||||
Scale?.Dispose();
|
Scale.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void ResetScale() {
|
public void ResetScale() {
|
||||||
Mock!.IdentNr = 0;
|
Mock.IdentNr = 0;
|
||||||
Mock!.Weight = 0;
|
Mock.Weight = 0;
|
||||||
Mock!.Error = null;
|
Mock.Error = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task Test_01_CurrentWeight() {
|
public async Task Test_01_CurrentWeight() {
|
||||||
Mock!.Weight = 1235;
|
Mock.Weight = 1235;
|
||||||
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||||
Weight = 1235, Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23),
|
Weight = 1235, Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23),
|
||||||
}));
|
}));
|
||||||
Mock!.Weight = 1240;
|
Mock.Weight = 1240;
|
||||||
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||||
Weight = 1240, Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23),
|
Weight = 1240, Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23),
|
||||||
}));
|
}));
|
||||||
Mock!.Weight = 1245;
|
Mock.Weight = 1245;
|
||||||
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||||
Weight = 1245, Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23),
|
Weight = 1245, Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23),
|
||||||
}));
|
}));
|
||||||
@ -83,19 +44,19 @@ namespace Tests.WeighingTests {
|
|||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task Test_02_Normal() {
|
public async Task Test_02_Normal() {
|
||||||
Mock!.Weight = 1235;
|
Mock.Weight = 1235;
|
||||||
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
|
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||||
Weight = 1235, WeighingId = "1",
|
Weight = 1235, WeighingId = "1",
|
||||||
FullWeighingId = $"2020-10-17/1",
|
FullWeighingId = $"2020-10-17/1",
|
||||||
Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23),
|
Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23),
|
||||||
}));
|
}));
|
||||||
Mock!.Weight = 3335;
|
Mock.Weight = 3335;
|
||||||
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
|
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||||
Weight = 3335, WeighingId = "2",
|
Weight = 3335, WeighingId = "2",
|
||||||
FullWeighingId = $"2020-10-17/2",
|
FullWeighingId = $"2020-10-17/2",
|
||||||
Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23),
|
Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23),
|
||||||
}));
|
}));
|
||||||
Mock!.Weight = 6420;
|
Mock.Weight = 6420;
|
||||||
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
|
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||||
Weight = 6420, WeighingId = "3",
|
Weight = 6420, WeighingId = "3",
|
||||||
FullWeighingId = $"2020-10-17/3",
|
FullWeighingId = $"2020-10-17/3",
|
||||||
@ -105,39 +66,39 @@ namespace Tests.WeighingTests {
|
|||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test_03_Moving() {
|
public void Test_03_Moving() {
|
||||||
Mock!.Weight = 1_000;
|
Mock.Weight = 1_000;
|
||||||
Mock!.Error = "moving";
|
Mock.Error = "moving";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||||
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
|
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test_04_Overloaded() {
|
public void Test_04_Overloaded() {
|
||||||
Mock!.Weight = 10_000;
|
Mock.Weight = 10_000;
|
||||||
Mock!.Error = "overloaded";
|
Mock.Error = "overloaded";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||||
Assert.That(ex.Message, Contains.Substring("Waage in Überlast"));
|
Assert.That(ex.Message, Contains.Substring("Waage in Überlast"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test_05_InvalidResponse() {
|
public void Test_05_InvalidResponse() {
|
||||||
Mock!.Weight = 1_000;
|
Mock.Weight = 1_000;
|
||||||
Mock!.Error = "invalid";
|
Mock.Error = "invalid";
|
||||||
Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test_06_InvalidCrc() {
|
public void Test_06_InvalidCrc() {
|
||||||
Mock!.Weight = 1_000;
|
Mock.Weight = 1_000;
|
||||||
Mock!.Error = "crc";
|
Mock.Error = "crc";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||||
Assert.That(ex.Message, Contains.Substring("Invalid CRC16 checksum"));
|
Assert.That(ex.Message, Contains.Substring("Invalid CRC16 checksum"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test_07_InvalidUnit() {
|
public void Test_07_InvalidUnit() {
|
||||||
Mock!.Weight = 1_000;
|
Mock.Weight = 1_000;
|
||||||
Mock!.Error = "unit";
|
Mock.Error = "unit";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,81 +4,39 @@ namespace Tests.WeighingTests {
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
class ScaleTestMatzenIT3000A {
|
class ScaleTestMatzenIT3000A {
|
||||||
|
|
||||||
private MockScale? Mock;
|
private MockScale Mock;
|
||||||
private SysTecITScale? Scale;
|
private SysTecITScale Scale;
|
||||||
|
|
||||||
private static (string, bool) ScaleHandler(string req, int weight, string? error, int identNr) {
|
|
||||||
var modes = error?.Split(';') ?? [];
|
|
||||||
var overloaded = modes.Contains("overloaded");
|
|
||||||
var moving = modes.Contains("moving");
|
|
||||||
var invalid = modes.Contains("invalid");
|
|
||||||
var crc = modes.Contains("crc");
|
|
||||||
var unit = modes.Contains("unit");
|
|
||||||
|
|
||||||
if (invalid) {
|
|
||||||
return ("abcd\r\n", false);
|
|
||||||
} else if (!req.StartsWith('<') || !req.EndsWith('>')) {
|
|
||||||
return ("<31>\r\n", false);
|
|
||||||
}
|
|
||||||
req = req[1..^1];
|
|
||||||
|
|
||||||
bool incr;
|
|
||||||
if (req.Length > 3) {
|
|
||||||
return ("<32>\r\n", false);
|
|
||||||
} else if (req.StartsWith("RN")) {
|
|
||||||
incr = true;
|
|
||||||
} else if (req.StartsWith("RM")) {
|
|
||||||
incr = false;
|
|
||||||
} else {
|
|
||||||
return ("<32>\r\n", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (overloaded) {
|
|
||||||
return ("<12>\r\n", false);
|
|
||||||
} else if (weight == 0) {
|
|
||||||
incr = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (moving && incr)
|
|
||||||
return ("<13>\r\n", false);
|
|
||||||
|
|
||||||
string data = $"00{(moving ? 1 : 0)}0{new DateTime(2020, 10, 15, 12, 34, 0):dd.MM.yyHH:mm}{(incr ? identNr : 0),4}1" +
|
|
||||||
$"{weight,8}{0,8}{weight,8}{(unit ? "lb" : "kg")} {"1",3}";
|
|
||||||
ushort checksum = Elwig.Helpers.Utils.CalcCrc16Modbus(data);
|
|
||||||
if (crc) checksum += 10;
|
|
||||||
return ($"<{data}{checksum,8}>\r\n", incr);
|
|
||||||
}
|
|
||||||
|
|
||||||
[OneTimeSetUp]
|
[OneTimeSetUp]
|
||||||
public void SetupScale() {
|
public void SetupScale() {
|
||||||
Mock = new CommandMockScale(12345, ScaleHandler);
|
Mock = new CommandMockScale(12345, ScaleHandlers.Handle_IT3000A);
|
||||||
Scale = new("1", "IT3000A", "tcp://127.0.0.1:12345");
|
Scale = new("1", "IT3000A", "tcp://127.0.0.1:12345");
|
||||||
}
|
}
|
||||||
|
|
||||||
[OneTimeTearDown]
|
[OneTimeTearDown]
|
||||||
public void TeardownScale() {
|
public void TeardownScale() {
|
||||||
Mock?.Dispose();
|
Mock.Dispose();
|
||||||
Scale?.Dispose();
|
Scale.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void ResetScale() {
|
public void ResetScale() {
|
||||||
Mock!.IdentNr = 0;
|
Mock.IdentNr = 0;
|
||||||
Mock!.Weight = 0;
|
Mock.Weight = 0;
|
||||||
Mock!.Error = null;
|
Mock.Error = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task Test_01_CurrentWeight() {
|
public async Task Test_01_CurrentWeight() {
|
||||||
Mock!.Weight = 1234;
|
Mock.Weight = 1234;
|
||||||
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||||
Weight = 1234, Date = new DateOnly(2020, 10, 15), Time = new TimeOnly(12, 34),
|
Weight = 1234, Date = new DateOnly(2020, 10, 15), Time = new TimeOnly(12, 34),
|
||||||
}));
|
}));
|
||||||
Mock!.Weight = 1235;
|
Mock.Weight = 1235;
|
||||||
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||||
Weight = 1235, Date = new DateOnly(2020, 10, 15), Time = new TimeOnly(12, 34),
|
Weight = 1235, Date = new DateOnly(2020, 10, 15), Time = new TimeOnly(12, 34),
|
||||||
}));
|
}));
|
||||||
Mock!.Weight = 1236;
|
Mock.Weight = 1236;
|
||||||
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||||
Weight = 1236, Date = new DateOnly(2020, 10, 15), Time = new TimeOnly(12, 34),
|
Weight = 1236, Date = new DateOnly(2020, 10, 15), Time = new TimeOnly(12, 34),
|
||||||
}));
|
}));
|
||||||
@ -86,19 +44,19 @@ namespace Tests.WeighingTests {
|
|||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task Test_02_Normal() {
|
public async Task Test_02_Normal() {
|
||||||
Mock!.Weight = 1234;
|
Mock.Weight = 1234;
|
||||||
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
|
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||||
Weight = 1234, WeighingId = "1",
|
Weight = 1234, WeighingId = "1",
|
||||||
FullWeighingId = $"2020-10-15/1",
|
FullWeighingId = $"2020-10-15/1",
|
||||||
Date = new DateOnly(2020, 10, 15), Time = new TimeOnly(12, 34),
|
Date = new DateOnly(2020, 10, 15), Time = new TimeOnly(12, 34),
|
||||||
}));
|
}));
|
||||||
Mock!.Weight = 3333;
|
Mock.Weight = 3333;
|
||||||
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
|
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||||
Weight = 3333, WeighingId = "2",
|
Weight = 3333, WeighingId = "2",
|
||||||
FullWeighingId = $"2020-10-15/2",
|
FullWeighingId = $"2020-10-15/2",
|
||||||
Date = new DateOnly(2020, 10, 15), Time = new TimeOnly(12, 34),
|
Date = new DateOnly(2020, 10, 15), Time = new TimeOnly(12, 34),
|
||||||
}));
|
}));
|
||||||
Mock!.Weight = 4321;
|
Mock.Weight = 4321;
|
||||||
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
|
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||||
Weight = 4321, WeighingId = "3",
|
Weight = 4321, WeighingId = "3",
|
||||||
FullWeighingId = $"2020-10-15/3",
|
FullWeighingId = $"2020-10-15/3",
|
||||||
@ -108,39 +66,39 @@ namespace Tests.WeighingTests {
|
|||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test_03_Moving() {
|
public void Test_03_Moving() {
|
||||||
Mock!.Weight = 1_000;
|
Mock.Weight = 1_000;
|
||||||
Mock!.Error = "moving";
|
Mock.Error = "moving";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||||
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
|
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test_04_Overloaded() {
|
public void Test_04_Overloaded() {
|
||||||
Mock!.Weight = 10_000;
|
Mock.Weight = 10_000;
|
||||||
Mock!.Error = "overloaded";
|
Mock.Error = "overloaded";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||||
Assert.That(ex.Message, Contains.Substring("Waage in Überlast"));
|
Assert.That(ex.Message, Contains.Substring("Waage in Überlast"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test_05_InvalidResponse() {
|
public void Test_05_InvalidResponse() {
|
||||||
Mock!.Weight = 1_000;
|
Mock.Weight = 1_000;
|
||||||
Mock!.Error = "invalid";
|
Mock.Error = "invalid";
|
||||||
Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test_06_InvalidCrc() {
|
public void Test_06_InvalidCrc() {
|
||||||
Mock!.Weight = 1_000;
|
Mock.Weight = 1_000;
|
||||||
Mock!.Error = "crc";
|
Mock.Error = "crc";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||||
Assert.That(ex.Message, Contains.Substring("Invalid CRC16 checksum"));
|
Assert.That(ex.Message, Contains.Substring("Invalid CRC16 checksum"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test_07_InvalidUnit() {
|
public void Test_07_InvalidUnit() {
|
||||||
Mock!.Weight = 1_000;
|
Mock.Weight = 1_000;
|
||||||
Mock!.Error = "unit";
|
Mock.Error = "unit";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,10 @@ namespace Tests.WeighingTests {
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
class ScaleTestWolkersdorfIT6000E {
|
class ScaleTestWolkersdorfIT6000E {
|
||||||
|
|
||||||
private MockScale? MockA;
|
private MockScale MockA;
|
||||||
private MockScale? MockB;
|
private MockScale MockB;
|
||||||
private SysTecITScale? ScaleA;
|
private SysTecITScale ScaleA;
|
||||||
private SysTecITScale? ScaleB;
|
private SysTecITScale ScaleB;
|
||||||
|
|
||||||
private static (string, bool) ScaleHandler(string req, int weight, string? error, int identNr, string terminalNr) {
|
private static (string, bool) ScaleHandler(string req, int weight, string? error, int identNr, string terminalNr) {
|
||||||
var modes = error?.Split(';') ?? [];
|
var modes = error?.Split(';') ?? [];
|
||||||
@ -58,37 +58,37 @@ namespace Tests.WeighingTests {
|
|||||||
|
|
||||||
[OneTimeTearDown]
|
[OneTimeTearDown]
|
||||||
public void TeardownScale() {
|
public void TeardownScale() {
|
||||||
MockA?.Dispose();
|
MockA.Dispose();
|
||||||
MockB?.Dispose();
|
MockB.Dispose();
|
||||||
ScaleA?.Dispose();
|
ScaleA.Dispose();
|
||||||
ScaleB?.Dispose();
|
ScaleB.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void ResetScale() {
|
public void ResetScale() {
|
||||||
MockA!.IdentNr = 0;
|
MockA.IdentNr = 0;
|
||||||
MockA!.Weight = 0;
|
MockA.Weight = 0;
|
||||||
MockA!.Error = null;
|
MockA.Error = null;
|
||||||
MockB!.IdentNr = 0;
|
MockB.IdentNr = 0;
|
||||||
MockB!.Weight = 0;
|
MockB.Weight = 0;
|
||||||
MockB!.Error = null;
|
MockB.Error = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task Test_01_CurrentWeight() {
|
public async Task Test_01_CurrentWeight() {
|
||||||
MockA!.Weight = 1234;
|
MockA.Weight = 1234;
|
||||||
Assert.That(await ScaleA!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
Assert.That(await ScaleA!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||||
Weight = 1234, Date = new DateOnly(2020, 10, 8), Time = new TimeOnly(8, 47),
|
Weight = 1234, Date = new DateOnly(2020, 10, 8), Time = new TimeOnly(8, 47),
|
||||||
}));
|
}));
|
||||||
MockB!.Weight = 3456;
|
MockB.Weight = 3456;
|
||||||
Assert.That(await ScaleB!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
Assert.That(await ScaleB!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||||
Weight = 3456, Date = new DateOnly(2020, 10, 8), Time = new TimeOnly(8, 47),
|
Weight = 3456, Date = new DateOnly(2020, 10, 8), Time = new TimeOnly(8, 47),
|
||||||
}));
|
}));
|
||||||
MockA!.Weight = 1236;
|
MockA.Weight = 1236;
|
||||||
Assert.That(await ScaleA!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
Assert.That(await ScaleA!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||||
Weight = 1236, Date = new DateOnly(2020, 10, 8), Time = new TimeOnly(8, 47),
|
Weight = 1236, Date = new DateOnly(2020, 10, 8), Time = new TimeOnly(8, 47),
|
||||||
}));
|
}));
|
||||||
MockB!.Weight = 3457;
|
MockB.Weight = 3457;
|
||||||
Assert.That(await ScaleB!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
Assert.That(await ScaleB!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||||
Weight = 3457, Date = new DateOnly(2020, 10, 8), Time = new TimeOnly(8, 47),
|
Weight = 3457, Date = new DateOnly(2020, 10, 8), Time = new TimeOnly(8, 47),
|
||||||
}));
|
}));
|
||||||
@ -96,25 +96,25 @@ namespace Tests.WeighingTests {
|
|||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task Test_02_Normal() {
|
public async Task Test_02_Normal() {
|
||||||
MockA!.Weight = 1234;
|
MockA.Weight = 1234;
|
||||||
Assert.That(await ScaleA!.Weigh(), Is.EqualTo(new WeighingResult {
|
Assert.That(await ScaleA!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||||
Weight = 1234, WeighingId = "1",
|
Weight = 1234, WeighingId = "1",
|
||||||
FullWeighingId = $"2020-10-08/1",
|
FullWeighingId = $"2020-10-08/1",
|
||||||
Date = new DateOnly(2020, 10, 8), Time = new TimeOnly(8, 47),
|
Date = new DateOnly(2020, 10, 8), Time = new TimeOnly(8, 47),
|
||||||
}));
|
}));
|
||||||
MockB!.Weight = 3456;
|
MockB.Weight = 3456;
|
||||||
Assert.That(await ScaleB!.Weigh(), Is.EqualTo(new WeighingResult {
|
Assert.That(await ScaleB!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||||
Weight = 3456, WeighingId = "1",
|
Weight = 3456, WeighingId = "1",
|
||||||
FullWeighingId = $"2020-10-08/1",
|
FullWeighingId = $"2020-10-08/1",
|
||||||
Date = new DateOnly(2020, 10, 8), Time = new TimeOnly(8, 47),
|
Date = new DateOnly(2020, 10, 8), Time = new TimeOnly(8, 47),
|
||||||
}));
|
}));
|
||||||
MockA!.Weight = 4321;
|
MockA.Weight = 4321;
|
||||||
Assert.That(await ScaleA!.Weigh(), Is.EqualTo(new WeighingResult {
|
Assert.That(await ScaleA!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||||
Weight = 4321, WeighingId = "2",
|
Weight = 4321, WeighingId = "2",
|
||||||
FullWeighingId = $"2020-10-08/2",
|
FullWeighingId = $"2020-10-08/2",
|
||||||
Date = new DateOnly(2020, 10, 8), Time = new TimeOnly(8, 47),
|
Date = new DateOnly(2020, 10, 8), Time = new TimeOnly(8, 47),
|
||||||
}));
|
}));
|
||||||
MockB!.Weight = 3333;
|
MockB.Weight = 3333;
|
||||||
Assert.That(await ScaleB!.Weigh(), Is.EqualTo(new WeighingResult {
|
Assert.That(await ScaleB!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||||
Weight = 3333, WeighingId = "2",
|
Weight = 3333, WeighingId = "2",
|
||||||
FullWeighingId = $"2020-10-08/2",
|
FullWeighingId = $"2020-10-08/2",
|
||||||
@ -124,39 +124,39 @@ namespace Tests.WeighingTests {
|
|||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test_03_Moving() {
|
public void Test_03_Moving() {
|
||||||
MockA!.Weight = 1_000;
|
MockA.Weight = 1_000;
|
||||||
MockA!.Error = "moving";
|
MockA.Error = "moving";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh());
|
IOException ex = Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh());
|
||||||
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
|
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test_04_Overloaded() {
|
public void Test_04_Overloaded() {
|
||||||
MockA!.Weight = 10_000;
|
MockA.Weight = 10_000;
|
||||||
MockA!.Error = "overloaded";
|
MockA.Error = "overloaded";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh());
|
IOException ex = Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh());
|
||||||
Assert.That(ex.Message, Contains.Substring("Waage in Überlast"));
|
Assert.That(ex.Message, Contains.Substring("Waage in Überlast"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test_05_InvalidResponse() {
|
public void Test_05_InvalidResponse() {
|
||||||
MockA!.Weight = 1_000;
|
MockA.Weight = 1_000;
|
||||||
MockA!.Error = "invalid";
|
MockA.Error = "invalid";
|
||||||
Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh());
|
Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test_06_InvalidCrc() {
|
public void Test_06_InvalidCrc() {
|
||||||
MockA!.Weight = 1_000;
|
MockA.Weight = 1_000;
|
||||||
MockA!.Error = "crc";
|
MockA.Error = "crc";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh());
|
IOException ex = Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh());
|
||||||
Assert.That(ex.Message, Contains.Substring("Invalid CRC16 checksum"));
|
Assert.That(ex.Message, Contains.Substring("Invalid CRC16 checksum"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Test_07_InvalidUnit() {
|
public void Test_07_InvalidUnit() {
|
||||||
MockA!.Weight = 1_000;
|
MockA.Weight = 1_000;
|
||||||
MockA!.Error = "unit";
|
MockA.Error = "unit";
|
||||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh());
|
IOException ex = Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
19
Tests/config.test.ini
Normal file
19
Tests/config.test.ini
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
[general]
|
||||||
|
debug = true
|
||||||
|
|
||||||
|
[database]
|
||||||
|
file = ElwigTestDB.sqlite3
|
||||||
|
|
||||||
|
[scale.1]
|
||||||
|
type = SysTec-IT
|
||||||
|
model = IT3000A
|
||||||
|
connection = tcp://127.0.0.1:12345
|
||||||
|
required = false
|
||||||
|
|
||||||
|
[scale.2]
|
||||||
|
type = Avery-Async
|
||||||
|
model = L320
|
||||||
|
connection = tcp://127.0.0.1:12346
|
||||||
|
required = false
|
||||||
|
|
@ -1 +1 @@
|
|||||||
curl --fail -s -L "https://elwig.at/files/create.sql?v=23" -u "elwig:ganzGeheim123!" -o "Resources\Sql\Create.sql"
|
curl --fail -s -L "https://elwig.at/files/create.sql?v=24" -u "elwig:ganzGeheim123!" -o "Resources\Sql\Create.sql"
|
||||||
|
Reference in New Issue
Block a user