Compare commits

...

10 Commits

46 changed files with 1913 additions and 128 deletions

@ -3,6 +3,32 @@ Changelog
=========
[v0.13.2][v0.13.2] (2024-10-13) {#v0.13.2}
------------------------------------------
### Neue Funktionen {#0.13.2-features}
* Im Lieferungen-Fenster den Menüpunkt _Abwerteliste_ (`DeliveryDepreciationList`) hinzugefügt. (3cbffdbf27)
### Behobene Fehler {#0.13.2-bugfixes}
* Fehler im Waagenprotokoll `Avery-Async` (L320) behoben. (8680e51052)
* Hausnummern in der BKI Traubentransportscheinliste werden in Excel richtig angezeigt. (86f7f693a0)
* Beim Ändern der Identifikatoren von Attributen/Bewirtschaftungsarten wurden diese in Auszahlungsvarianten nicht aktualisiert. (d1f67dc57d)
### Sonstiges {#0.13.2-misc}
* Weitere automatisierte Tests hinzugefügt. ([#11][i11])
* Namenszusätze bei Gemeinden (z.B. an, bei, im, am) genauer angegeben. (65498dd18f)
* Bei einigen Eingabefeldern waren die Ränder unscharf. (e247925472)
* Wo leicht möglich wird `ExecuteSql`/`FromSql` statt `ExecuteSqlRaw`/`FromSqlRaw` verwendet. (0675c45617)
[v0.13.2]: https://git.necronda.net/winzer/elwig/releases/tag/v0.13.2
[i11]: https://git.necronda.net/winzer/elwig/issues/11
[v0.13.1][v0.13.1] (2024-09-29) {#v0.13.1}
------------------------------------------

@ -7,9 +7,6 @@ using Elwig.Helpers;
using Elwig.Helpers.Weighing;
using System.Collections.Generic;
using System.Windows.Threading;
using System.Globalization;
using System.Threading;
using System.Windows.Markup;
using System.Reflection;
using Elwig.Helpers.Printing;
using Elwig.Windows;
@ -62,7 +59,7 @@ namespace Elwig {
MainDispatcher = Dispatcher;
Scales = [];
CurrentApp = this;
OverrideCulture();
Utils.OverrideCulture();
var args = Environment.GetCommandLineArgs();
if (args.Length >= 2) {
@ -82,23 +79,6 @@ namespace Elwig {
MainDispatcher.BeginInvoke(HintContextChange);
}
private static void OverrideCulture() {
var locale = new CultureInfo("de-AT", false);
locale.NumberFormat.CurrencyGroupSeparator = Utils.GroupSeparator;
locale.NumberFormat.NumberGroupSeparator = Utils.GroupSeparator;
locale.NumberFormat.PercentGroupSeparator = Utils.GroupSeparator;
CultureInfo.CurrentCulture = locale;
CultureInfo.CurrentUICulture = locale;
Thread.CurrentThread.CurrentCulture = locale;
Thread.CurrentThread.CurrentUICulture = locale;
CultureInfo.DefaultThreadCurrentCulture = locale;
CultureInfo.DefaultThreadCurrentUICulture = locale;
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.Name))
);
}
protected override async void OnStartup(StartupEventArgs evt) {
Version = new Version(typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion.Split('+')[0] ?? "0.0.0");
@ -183,6 +163,9 @@ namespace Elwig {
}
private async void Application_Exit(object sender, ExitEventArgs evt) {
foreach (var s in EventScales) {
s.Dispose();
}
await Pdf.Cleanup();
}
@ -194,7 +177,13 @@ namespace Elwig {
ZwstId = entry.Item1;
BranchName = entry.Item2;
BranchPlz = entry.Item3;
BranchLocation = entry.Item4?.Split(" im ")[0].Split(" an ")[0].Split(" bei ")[0]; // FIXME
BranchLocation = entry.Item4?
.Split(" in ")[0]
.Split(" im ")[0]
.Split(" an ")[0]
.Split(" am ")[0]
.Split(" bei ")[0]
.Split(" beim ")[0];
BranchAddress = entry.Item5;
BranchPhoneNr = entry.Item6;
BranchFaxNr = entry.Item7;
@ -202,6 +191,7 @@ namespace Elwig {
}
public static void HintContextChange() {
if (CurrentApp == null) return;
var ch = CurrentLastWrite;
if (ch > CurrentApp.LastChanged)
CurrentApp.LastChanged = ch;

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

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

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

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

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

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

@ -0,0 +1,13 @@
h1 {
text-align: center;
font-size: 24pt;
margin-top: 10mm;
margin-bottom: 2mm;
}
h2 {
text-align: center;
font-size: 14pt;
margin-top: 2mm;
}

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

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

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

@ -7,7 +7,7 @@
<UseWPF>true</UseWPF>
<PreserveCompilationContext>true</PreserveCompilationContext>
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
<Version>0.13.1</Version>
<Version>0.13.2</Version>
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ApplicationManifest>app.manifest</ApplicationManifest>

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

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

@ -28,6 +28,9 @@ using LinqKit;
using System.Linq.Expressions;
using Elwig.Models;
using Microsoft.Win32;
using System.Globalization;
using System.Threading;
using System.Windows.Markup;
namespace Elwig.Helpers {
public static partial class Utils {
@ -118,6 +121,23 @@ namespace Elwig.Helpers {
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040,
];
public static void OverrideCulture() {
var locale = new CultureInfo("de-AT", false);
locale.NumberFormat.CurrencyGroupSeparator = Utils.GroupSeparator;
locale.NumberFormat.NumberGroupSeparator = Utils.GroupSeparator;
locale.NumberFormat.PercentGroupSeparator = Utils.GroupSeparator;
CultureInfo.CurrentCulture = locale;
CultureInfo.CurrentUICulture = locale;
Thread.CurrentThread.CurrentCulture = locale;
Thread.CurrentThread.CurrentUICulture = locale;
CultureInfo.DefaultThreadCurrentCulture = locale;
CultureInfo.DefaultThreadCurrentUICulture = locale;
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.Name))
);
}
public static SerialPort OpenSerialConnection(string connection) {
var m = SerialRegex.Match(connection);
if (!m.Success)

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

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

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

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

@ -25,7 +25,7 @@ namespace Elwig.Models.Dtos {
}
public static async Task<OverUnderDeliveryData> ForSeason(DbSet<OverUnderDeliveryRow> table, int year) {
var rows = await table.FromSqlRaw($"""
var rows = await table.FromSql($"""
SELECT m.mgnr, m.name AS name_1,
COALESCE(m.prefix || ' ', '') || m.given_name ||
COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, '') AS name_2,

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

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

@ -24,7 +24,7 @@ namespace Elwig.Services {
public static class DeliveryService {
public enum ExportSubject {
FromFilters, FromToday, FromSeasonAndBranch, Selected,
FromFilters, FromToday, FromSeason, FromSeasonAndBranch, Selected,
};
public static async Task<Member?> GetMemberAsync(int mgnr) {
@ -462,7 +462,7 @@ namespace Elwig.Services {
LNr = newLnr ?? oldDelivery!.LNr,
ZwstId = vm.Branch!.ZwstId,
LsNr = newLsNr ?? vm.LsNr!,
MgNr = (int)vm.MgNr!,
MgNr = vm.MgNr!.Value,
Comment = string.IsNullOrEmpty(vm.Comment) ? null : vm.Comment,
};
@ -474,7 +474,7 @@ namespace Elwig.Services {
SortId = vm.WineVar!.SortId,
AttrId = vm.WineAttr?.AttrId,
CultId = vm.WineCult?.CultId,
Kmw = (double)vm.GradationKmw!,
Kmw = vm.GradationKmw!.Value,
QualId = vm.WineQualityLevel!.QualId,
HkId = vm.WineOrigin!.HkId,
KgNr = vm.WineKg?.KgNr,
@ -488,7 +488,7 @@ namespace Elwig.Services {
Acid = vm.Acid,
Comment = string.IsNullOrEmpty(vm.PartComment) ? null : vm.PartComment,
Weight = (int)vm.Weight!,
Weight = vm.Weight!.Value,
IsManualWeighing = vm.IsManualWeighing,
ScaleId = vm.ScaleId,
WeighingData = vm.WeighingData,
@ -809,6 +809,71 @@ namespace Elwig.Services {
Mouse.OverrideCursor = null;
}
public static async Task GenerateDeliveryDepreciationList(this DeliveryAdminViewModel vm, ExportSubject subject, ExportMode mode) {
using var ctx = new AppDbContext();
IQueryable<DeliveryPart> query;
List<string> filterNames = [];
if (subject == ExportSubject.FromFilters) {
var (f, _, q, _, _) = await vm.GetFilters(ctx);
query = q;
filterNames.AddRange(f);
} else if (subject == ExportSubject.FromSeason) {
var year = vm.FilterSeason ?? Utils.CurrentLastSeason;
query = ctx.DeliveryParts
.Where(p => p.Year == year);
filterNames.Add($"{year}");
} else {
throw new ArgumentException("Invalid value for ExportSubject");
}
query = query
.Where(p => p.QualId == "WEI")
.OrderBy(p => p.Delivery.MgNr)
.ThenBy(p => p.Delivery.DateString)
.ThenBy(p => p.Delivery.TimeString)
.ThenBy(p => p.Delivery.LsNr)
.ThenBy(p => p.DPNr);
filterNames.Remove("abgewertet");
if (mode == ExportMode.SaveList) {
var d = new SaveFileDialog() {
FileName = $"{DeliveryDepreciationList.Name}-{vm.FilterSeason ?? Utils.CurrentLastSeason}.ods",
DefaultExt = "ods",
Filter = "OpenDocument Format Spreadsheet (*.ods)|*.ods",
Title = $"{DeliveryDepreciationList.Name} speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.AppStarting;
try {
using var ods = new OdsFile(d.FileName);
var tblTotal = await DeliveryJournalData.FromQuery(query, filterNames);
tblTotal.FullName = DeliveryDepreciationList.Name;
tblTotal.Name = "Gesamt";
await ods.AddTable(tblTotal);
foreach (var branch in await ctx.Branches.OrderBy(b => b.Name).ToListAsync()) {
var tbl = await DeliveryJournalData.FromQuery(query.Where(p => p.Delivery.ZwstId == branch.ZwstId), filterNames);
tbl.FullName = DeliveryDepreciationList.Name;
tbl.Name = branch.Name;
await ods.AddTable(tbl);
}
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
} else {
Mouse.OverrideCursor = Cursors.AppStarting;
try {
var data = await DeliveryJournalData.FromQuery(query, filterNames);
using var doc = new DeliveryDepreciationList(string.Join(" / ", filterNames), data);
await Utils.ExportDocument(doc, mode);
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null;
}
}
private static void AddToolTipCell(Grid grid, string text, int row, int col, int colSpan = 1, bool bold = false, bool alignRight = false, bool alignCenter = false) {
var tb = new TextBlock() {
Text = text,
@ -971,5 +1036,13 @@ namespace Elwig.Services {
return (wGrid, gGrid);
}
public static async Task DeleteDelivery(string lsnr) {
using (var ctx = new AppDbContext()) {
await ctx.Deliveries.Where(d => d.LsNr == lsnr).ExecuteDeleteAsync();
await ctx.SaveChangesAsync();
}
App.HintContextChange();
}
}
}

@ -25,7 +25,7 @@ namespace Elwig.Services {
public static async Task InitInputs(this MemberAdminViewModel vm) {
using var ctx = new AppDbContext();
vm.MgNrString = $"{await ctx.NextMgNr()}";
vm.MgNr = await ctx.NextMgNr();
vm.EntryDate = DateTime.Now.ToString("dd.MM.yyyy");
if (vm.BranchSource.Count() == 1)
vm.Branch = vm.BranchSource.First();
@ -53,8 +53,8 @@ namespace Elwig.Services {
public static void FillInputs(this MemberAdminViewModel vm, Member m) {
vm.IsMemberSelected = true;
vm.MgNrString = $"{m.MgNr}";
vm.PredecessorMgNrString = $"{m.PredecessorMgNr}";
vm.MgNr = m.MgNr;
vm.PredecessorMgNr = m.PredecessorMgNr;
vm.IsJuridicalPerson = m.IsJuridicalPerson;
vm.EnableMemberReferenceButton = m.PredecessorMgNr != null;
vm.Prefix = m.Prefix;
@ -73,10 +73,10 @@ namespace Elwig.Services {
vm.IsDeceased = m.IsDeceased;
vm.Address = m.Address;
if (m.PostalDest.AtPlz is AT_PlzDest p) {
vm.PlzString = $"{p.Plz}";
vm.Plz = p.Plz;
vm.Ort = ControlUtils.GetItemFromSource(vm.OrtSource, p);
} else {
vm.PlzString = null;
vm.Plz = null;
vm.Ort = null;
}
@ -114,19 +114,19 @@ namespace Elwig.Services {
vm.BillingName = billingAddr.FullName;
vm.BillingAddress = billingAddr.Address;
if (billingAddr.PostalDest.AtPlz is AT_PlzDest b) {
vm.BillingPlzString = $"{b.Plz}";
vm.BillingPlz = b.Plz;
vm.BillingOrt = ControlUtils.GetItemFromSource(vm.BillingOrtSource, b);
}
} else {
vm.BillingName = null;
vm.BillingAddress = null;
vm.BillingPlzString = null;
vm.BillingPlz = null;
vm.BillingOrt = null;
}
vm.EntryDate = (m.EntryDateString != null) ? string.Join(".", m.EntryDateString.Split("-").Reverse()) : null;
vm.ExitDate = (m.ExitDateString != null) ? string.Join(".", m.ExitDateString.Split("-").Reverse()) : null;
vm.BusinessSharesString = $"{m.BusinessShares}";
vm.BusinessShares = m.BusinessShares;
vm.AccountingNr = m.AccountingNr;
vm.Branch = (Branch?)ControlUtils.GetItemFromSourceWithPk(vm.BranchSource, m.ZwstId);
vm.DefaultKg = (AT_Kg?)ControlUtils.GetItemFromSourceWithPk(vm.DefaultKgSource, m.DefaultKgNr);
@ -148,6 +148,8 @@ namespace Elwig.Services {
vm.StatusAreaCommitmentToolTip = null;
Utils.RunBackground("Mitgliederdaten laden", async () => {
if (App.MainDispatcher == null)
return;
using var ctx = new AppDbContext();
var d1 = ctx.Deliveries.Where(d => d.Year == Utils.CurrentLastSeason - 1 && d.MgNr == m.MgNr);
@ -652,5 +654,23 @@ namespace Elwig.Services {
return newMgNr;
}
public static async Task DeleteMember(int mgnr, bool deletePaymentData, bool deleteDeliveries, bool deleteAreaComs) {
using (var ctx = new AppDbContext()) {
var l = (await ctx.Members.FindAsync(mgnr))!;
if (deletePaymentData) {
ctx.RemoveRange(l.Credits);
}
if (deleteDeliveries) {
ctx.RemoveRange(l.Deliveries);
}
if (deleteAreaComs) {
ctx.RemoveRange(l.AreaCommitments);
}
ctx.Remove(l);
await ctx.SaveChangesAsync();
}
App.HintContextChange();
}
}
}

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

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

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

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

@ -120,6 +120,25 @@
<MenuItem x:Name="Menu_WineQualityStatistics_ModeKmw1" Header="...nach °KMW aufschlüsseln (ganze)" IsCheckable="True"
Click="Menu_WineQualityStatistics_Mode_Click"/>
</MenuItem>
<MenuItem Header="Abwertungsliste" x:Name="Menu_DeliveryDepreciationList">
<MenuItem x:Name="Menu_DeliveryDepreciationList_SaveFilters" Header="...aus Filtern speichern... (Excel)"
Click="Menu_DeliveryDepreciationList_SaveFilters_Click"/>
<MenuItem x:Name="Menu_DeliveryDepreciationList_ShowFilters" Header="...aus Filtern anzeigen (PDF)"
Click="Menu_DeliveryDepreciationList_ShowFilters_Click"/>
<MenuItem x:Name="Menu_DeliveryDepreciationList_SavePdfFilters" Header="...aus Filtern speichern... (PDF)"
Click="Menu_DeliveryDepreciationList_SavePdfFilters_Click"/>
<MenuItem x:Name="Menu_DeliveryDepreciationList_PrintFilters" Header="...aus Filtern drucken"
Click="Menu_DeliveryDepreciationList_PrintFilters_Click"/>
<Separator/>
<MenuItem x:Name="Menu_DeliveryDepreciationList_SaveSeason" Header="...von Saison speichern... (Excel)"
Click="Menu_DeliveryDepreciationList_SaveSeason_Click"/>
<MenuItem x:Name="Menu_DeliveryDepreciationList_ShowSeason" Header="...von Saison anzeigen (PDF)"
Click="Menu_DeliveryDepreciationList_ShowSeason_Click"/>
<MenuItem x:Name="Menu_DeliveryDepreciationList_SavePdfSeason" Header="...von Saison speichern... (PDF)"
Click="Menu_DeliveryDepreciationList_SavePdfSeason_Click"/>
<MenuItem x:Name="Menu_DeliveryDepreciationList_PrintSeason" Header="...von Saison drucken"
Click="Menu_DeliveryDepreciationList_PrintSeason_Click"/>
</MenuItem>
<MenuItem Header="BKI">
<MenuItem x:Name="Menu_Bki_SaveList" Header="Traubentransportscheinliste speichern..."/>
</MenuItem>

@ -172,9 +172,11 @@ namespace Elwig.Windows {
Title = $"Traubentransportscheinliste (BKI) speichern unter - Elwig"
};
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.AppStarting;
using var file = new Bki(d.FileName);
await file.ExportAsync(year);
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
using var file = new Bki(d.FileName);
await file.ExportAsync(year);
});
Mouse.OverrideCursor = null;
}
}
@ -250,6 +252,23 @@ namespace Elwig.Windows {
await App.Client.UpdateValues();
}
private async void Menu_DeliveryDepreciationList_SaveFilters_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromFilters, ExportMode.SaveList);
private async void Menu_DeliveryDepreciationList_ShowFilters_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromFilters, ExportMode.Show);
private async void Menu_DeliveryDepreciationList_SavePdfFilters_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromFilters, ExportMode.SavePdf);
private async void Menu_DeliveryDepreciationList_PrintFilters_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromFilters, ExportMode.Print);
private async void Menu_DeliveryDepreciationList_SaveSeason_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromSeason, ExportMode.SaveList);
private async void Menu_DeliveryDepreciationList_ShowSeason_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromSeason, ExportMode.Show);
private async void Menu_DeliveryDepreciationList_SavePdfSeason_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromSeason, ExportMode.SavePdf);
private async void Menu_DeliveryDepreciationList_PrintSeason_Click(object sender, RoutedEventArgs evt) =>
await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromSeason, ExportMode.Print);
private void Menu_Settings_EnableFreeEditing_Checked(object sender, RoutedEventArgs evt) {
if (IsEditing || IsCreating) {
DateInput.IsReadOnly = false;
@ -933,12 +952,13 @@ namespace Elwig.Windows {
"Lieferung löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
if (r == MessageBoxResult.OK) {
Mouse.OverrideCursor = Cursors.AppStarting;
using (var ctx = new AppDbContext()) {
ctx.Remove(d);
await ctx.SaveChangesAsync();
try {
await DeliveryService.DeleteDelivery(d.LsNr);
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Lieferung löschen", MessageBoxButton.OK, MessageBoxImage.Error);
}
await RefreshList();
await RefreshDeliveryParts();
Mouse.OverrideCursor = null;
}
}

@ -360,21 +360,7 @@ namespace Elwig.Windows {
if (d.ShowDialog() == true) {
Mouse.OverrideCursor = Cursors.AppStarting;
try {
using (var ctx = new AppDbContext()) {
var l = (await ctx.Members.FindAsync(m.MgNr))!;
if (d.DeletePaymentData) {
ctx.RemoveRange(l.Credits);
}
if (d.DeleteDeliveries) {
ctx.RemoveRange(l.Deliveries);
}
if (d.DeleteAreaComs) {
ctx.RemoveRange(l.AreaCommitments);
}
ctx.Remove(l);
await ctx.SaveChangesAsync();
}
App.HintContextChange();
await MemberService.DeleteMember(m.MgNr, d.DeletePaymentData, d.DeleteDeliveries, d.DeleteAreaComs);
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;

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

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

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

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

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

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

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

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

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

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

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

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

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

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