Compare commits

...

12 Commits

85 changed files with 591 additions and 257 deletions
CHANGELOG.md
Elwig
Installer
Tests

@ -3,6 +3,19 @@ Changelog
========= =========
[v0.13.9][v0.13.9] (2025-05-05) {#v0.13.9}
------------------------------------------
### Sonstiges {#v0.13.9-misc}
* Abhängigkeiten aktualisiert. (bf0db37872, 4af2fa256e, d1c07ee92a, 41c5288fc5)
* Automatisches Aktualisieren der Synchroisations-URL (`https://elwig.at/clients/` -> `https://sync.elwig.at/`). (e50e7337e6)
[v0.13.9]: https://git.necronda.net/winzer/elwig/releases/tag/v0.13.9
[v0.13.8][v0.13.8] (2025-02-21) {#v0.13.8} [v0.13.8][v0.13.8] (2025-02-21) {#v0.13.8}
------------------------------------------ ------------------------------------------

@ -25,10 +25,13 @@ namespace Elwig {
private readonly DispatcherTimer _autoUpdateTimer = new() { Interval = TimeSpan.FromHours(1) }; private readonly DispatcherTimer _autoUpdateTimer = new() { Interval = TimeSpan.FromHours(1) };
public static readonly string DataPath = @"C:\ProgramData\Elwig\"; public static readonly string DataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Elwig");
public static readonly string MailsPath = Path.Combine(DataPath, "mails"); public static readonly string MailsPath = Path.Combine(DataPath, "mails");
public static readonly string ConfigPath = Path.Combine(DataPath, "config.ini"); public static readonly string ConfigPath = Path.Combine(DataPath, "config.ini");
public static readonly string ExePath = @"C:\Program Files\Elwig\"; public static readonly string InstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Elwig");
public static readonly string DocumentsPath = (Assembly.GetEntryAssembly()?.Location.Contains(@"\bin\") ?? false) ?
Path.Combine(Assembly.GetEntryAssembly()!.Location.Split(@"\bin\")[0], "../Elwig/Documents") :
Path.Combine(InstallPath, "resources/Documents");
public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig"); public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig");
public static Config Config { get; private set; } = new(ConfigPath); public static Config Config { get; private set; } = new(ConfigPath);

@ -2,7 +2,7 @@
@inherits TemplatePage<Elwig.Documents.BusinessDocument> @inherits TemplatePage<Elwig.Documents.BusinessDocument>
@model Elwig.Documents.BusinessDocument @model Elwig.Documents.BusinessDocument
@{ Layout = "Document"; } @{ Layout = "Document"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\BusinessDocument.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\BusinessDocument.css"/>
<div class="info-wrapper"> <div class="info-wrapper">
<div class="address-wrapper"> <div class="address-wrapper">
<div class="sender"> <div class="sender">

@ -3,7 +3,7 @@
@inherits TemplatePage<Elwig.Documents.CreditNote> @inherits TemplatePage<Elwig.Documents.CreditNote>
@model Elwig.Documents.CreditNote @model Elwig.Documents.CreditNote
@{ Layout = "BusinessDocument"; } @{ Layout = "BusinessDocument"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\CreditNote.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\CreditNote.css" />
<main> <main>
<h1>@Model.Title</h1> <h1>@Model.Title</h1>
<table class="credit"> <table class="credit">

@ -2,7 +2,7 @@
@inherits TemplatePage<Elwig.Documents.DeliveryAncmtList> @inherits TemplatePage<Elwig.Documents.DeliveryAncmtList>
@model Elwig.Documents.DeliveryAncmtList @model Elwig.Documents.DeliveryAncmtList
@{ Layout = "Document"; } @{ Layout = "Document"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\DeliveryAncmtList.css" /> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\DeliveryAncmtList.css" />
<main> <main>
<h1>Anmeldeliste</h1> <h1>Anmeldeliste</h1>
<h2>@Model.Filter</h2> <h2>@Model.Filter</h2>
@ -24,7 +24,7 @@
<th rowspan="2" style="text-align: left;">Ort</th> <th rowspan="2" style="text-align: left;">Ort</th>
<th rowspan="2" style="text-align: left;">Sorte</th> <th rowspan="2" style="text-align: left;">Sorte</th>
<th rowspan="2">Anmldg.</th> <th rowspan="2">Anmldg.</th>
<th>Gewicht</th> <th>Menge</th>
</tr> </tr>
<tr> <tr>
<th class="unit">[kg]</th> <th class="unit">[kg]</th>

@ -3,7 +3,7 @@
@inherits TemplatePage<Elwig.Documents.DeliveryConfirmation> @inherits TemplatePage<Elwig.Documents.DeliveryConfirmation>
@model Elwig.Documents.DeliveryConfirmation @model Elwig.Documents.DeliveryConfirmation
@{ Layout = "BusinessDocument"; } @{ Layout = "BusinessDocument"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\DeliveryConfirmation.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\DeliveryConfirmation.css" />
<main> <main>
<h1>@Model.Title</h1> <h1>@Model.Title</h1>
<table class="delivery-confirmation"> <table class="delivery-confirmation">
@ -30,7 +30,7 @@
<th rowspan="2" style="text-align: left;">Qualitätsstufe</th> <th rowspan="2" style="text-align: left;">Qualitätsstufe</th>
<th colspan="2">Gradation</th> <th colspan="2">Gradation</th>
<th colspan="2">Flächenbindung</th> <th colspan="2">Flächenbindung</th>
<th>Gewicht</th> <th>Menge</th>
<th rowspan="3" style="padding: 0;"> <th rowspan="3" style="padding: 0;">
<svg width="10" height="40" xmlns="http://www.w3.org/2000/svg"> <svg width="10" height="40" xmlns="http://www.w3.org/2000/svg">
<text x="-40" y="4" transform="rotate(270)" font-size="8pt" font-style="italic" font-family="Times New Roman" <text x="-40" y="4" transform="rotate(270)" font-size="8pt" font-style="italic" font-family="Times New Roman"

@ -2,7 +2,7 @@
@inherits TemplatePage<Elwig.Documents.DeliveryDepreciationList> @inherits TemplatePage<Elwig.Documents.DeliveryDepreciationList>
@model Elwig.Documents.DeliveryDepreciationList @model Elwig.Documents.DeliveryDepreciationList
@{ Layout = "Document"; } @{ Layout = "Document"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\DeliveryDepreciationList.css" /> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\DeliveryDepreciationList.css" />
<main> <main>
<h1>Abwertungsliste</h1> <h1>Abwertungsliste</h1>
<h2>@Model.Filter</h2> <h2>@Model.Filter</h2>
@ -27,7 +27,7 @@
<th rowspan="2" style="text-align: left;">Sorte</th> <th rowspan="2" style="text-align: left;">Sorte</th>
<th rowspan="2" style="text-align: left;">Attr./Bewirt.</th> <th rowspan="2" style="text-align: left;">Attr./Bewirt.</th>
<th colspan="2">Gradation</th> <th colspan="2">Gradation</th>
<th>Gewicht</th> <th>Menge</th>
</tr> </tr>
<tr> <tr>
<th class="unit">[°Oe]</th> <th class="unit">[°Oe]</th>

@ -2,7 +2,7 @@
@inherits TemplatePage<Elwig.Documents.DeliveryJournal> @inherits TemplatePage<Elwig.Documents.DeliveryJournal>
@model Elwig.Documents.DeliveryJournal @model Elwig.Documents.DeliveryJournal
@{ Layout = "Document"; } @{ Layout = "Document"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\DeliveryJournal.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\DeliveryJournal.css"/>
<main> <main>
<h1>Lieferjournal</h1> <h1>Lieferjournal</h1>
<h2>@Model.Filter</h2> <h2>@Model.Filter</h2>
@ -29,7 +29,7 @@
<th rowspan="2" style="text-align: left;">Mitglied</th> <th rowspan="2" style="text-align: left;">Mitglied</th>
<th rowspan="2" style="text-align: left;">Sorte</th> <th rowspan="2" style="text-align: left;">Sorte</th>
<th colspan="2">Gradation</th> <th colspan="2">Gradation</th>
<th>Gewicht</th> <th>Menge</th>
</tr> </tr>
<tr> <tr>
<th class="unit">[°Oe]</th> <th class="unit">[°Oe]</th>

@ -2,7 +2,7 @@
@inherits TemplatePage<Elwig.Documents.DeliveryNote> @inherits TemplatePage<Elwig.Documents.DeliveryNote>
@model Elwig.Documents.DeliveryNote @model Elwig.Documents.DeliveryNote
@{ Layout = "BusinessDocument"; } @{ Layout = "BusinessDocument"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\DeliveryNote.css" /> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\DeliveryNote.css" />
<main> <main>
<h1>@Model.Title</h1> <h1>@Model.Title</h1>
<table class="delivery large"> <table class="delivery large">
@ -24,7 +24,7 @@
<th class="main" rowspan="2" colspan="2">Attribut</th> <th class="main" rowspan="2" colspan="2">Attribut</th>
<th class="main" rowspan="2">Qualitätsstufe</th> <th class="main" rowspan="2">Qualitätsstufe</th>
<th colspan="2">Gradation</th> <th colspan="2">Gradation</th>
<th>Gewicht</th> <th>Menge</th>
</tr> </tr>
<tr> <tr>
<th class="unit">[°Oe]</th> <th class="unit">[°Oe]</th>

@ -23,7 +23,7 @@ namespace Elwig.Documents {
public bool ShowFoldMarks = App.Config.Debug; public bool ShowFoldMarks = App.Config.Debug;
public bool DoublePaged = false; public bool DoublePaged = false;
public string DataPath; public string DocumentsPath;
public int CurrentNextSeason; public int CurrentNextSeason;
public string? DocumentId; public string? DocumentId;
public string Title; public string Title;
@ -34,7 +34,7 @@ namespace Elwig.Documents {
public Document(string title) { public Document(string title) {
var c = App.Client; var c = App.Client;
DataPath = App.DataPath; DocumentsPath = App.DocumentsPath;
CurrentNextSeason = Utils.CurrentNextSeason; CurrentNextSeason = Utils.CurrentNextSeason;
Title = title; Title = title;
Author = c.NameFull; Author = c.NameFull;

@ -7,9 +7,9 @@
<title>@Model.Title</title> <title>@Model.Title</title>
<meta name="author" value="@Model.Author"/> <meta name="author" value="@Model.Author"/>
<meta charset="UTF-8"/> <meta charset="UTF-8"/>
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\Document.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\Document.css" />
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\Document.Page.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\Document.Page.css" />
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\Document.Table.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\Document.Table.css" />
@if (Model.DoublePaged) { @if (Model.DoublePaged) {
<style> <style>
@@page :left { @@page :left {

@ -3,7 +3,7 @@
@inherits TemplatePage<Elwig.Documents.MemberDataSheet> @inherits TemplatePage<Elwig.Documents.MemberDataSheet>
@model Elwig.Documents.MemberDataSheet @model Elwig.Documents.MemberDataSheet
@{ Layout = "BusinessDocument"; } @{ Layout = "BusinessDocument"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\MemberDataSheet.css" /> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\MemberDataSheet.css" />
<main> <main>
<h1>@Model.Title</h1> <h1>@Model.Title</h1>
<table class="member border"> <table class="member border">

@ -2,7 +2,7 @@
@inherits TemplatePage<Elwig.Documents.MemberList> @inherits TemplatePage<Elwig.Documents.MemberList>
@model Elwig.Documents.MemberList @model Elwig.Documents.MemberList
@{ Layout = "Document"; } @{ Layout = "Document"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\MemberList.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\MemberList.css" />
<main> <main>
<h1>Mitgliederliste</h1> <h1>Mitgliederliste</h1>
<h2>@Model.Filter</h2> <h2>@Model.Filter</h2>

@ -3,7 +3,7 @@
@inherits TemplatePage<Elwig.Documents.PaymentVariantSummary> @inherits TemplatePage<Elwig.Documents.PaymentVariantSummary>
@model Elwig.Documents.PaymentVariantSummary @model Elwig.Documents.PaymentVariantSummary
@{ Layout = "Document"; } @{ Layout = "Document"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\PaymentVariantSummary.css" /> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\PaymentVariantSummary.css" />
<main> <main>
<h1>Auszahlungsvariante Lese @Model.Variant.Year</h1> <h1>Auszahlungsvariante Lese @Model.Variant.Year</h1>
<h2>@Model.Variant.Name</h2> <h2>@Model.Variant.Name</h2>
@ -111,8 +111,8 @@
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(payed):N2}")</td> <td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(payed):N2}")</td>
@{ @{
var weiRows = Model.Data.Rows.Where(r => r.QualityLevel == "Wein"); var weiRows = Model.Data.Rows.Where(r => r.QualityLevel == "Wein");
var minWei = weiRows.Min(r => r.Ungeb.Price); var minWei = weiRows.Min(r => r.Ungeb.MinPrice);
var maxWei = weiRows.Max(r => r.Ungeb.Price); var maxWei = weiRows.Max(r => r.Ungeb.MaxPrice);
} }
<th class="lborder tborder">Preis (abgewertet):</th> <th class="lborder tborder">Preis (abgewertet):</th>
<td colspan="2" class="center tborder">@(minWei != maxWei ? $"{minWei:N4}{maxWei:N4}" : $"{minWei:N4}") @Model.CurrencySymbol/kg</td> <td colspan="2" class="center tborder">@(minWei != maxWei ? $"{minWei:N4}{maxWei:N4}" : $"{minWei:N4}") @Model.CurrencySymbol/kg</td>
@ -123,8 +123,8 @@
<td class="number tborder"><span class="fleft">@Model.CurrencySymbol</span>@($"{netSum:N2}")</td> <td class="number tborder"><span class="fleft">@Model.CurrencySymbol</span>@($"{netSum:N2}")</td>
@{ @{
var quwRows = Model.Data.Rows.Where(r => r.QualityLevel != "Wein"); var quwRows = Model.Data.Rows.Where(r => r.QualityLevel != "Wein");
var minPrice = quwRows.Min(r => r.Ungeb.Price); var minPrice = quwRows.Min(r => r.Ungeb.MinPrice);
var maxPrice = quwRows.Max(r => r.Ungeb.Price); var maxPrice = quwRows.Max(r => r.Ungeb.MaxPrice);
} }
<th class="lborder">Preis (ungeb., nicht abgew.):</th> <th class="lborder">Preis (ungeb., nicht abgew.):</th>
<td colspan="2" class="center">@(minPrice != maxPrice ? $"{minPrice:N4}{maxPrice:N4}" : $"{minPrice:N4}") @Model.CurrencySymbol/kg</td> <td colspan="2" class="center">@(minPrice != maxPrice ? $"{minPrice:N4}{maxPrice:N4}" : $"{minPrice:N4}") @Model.CurrencySymbol/kg</td>
@ -135,8 +135,8 @@
<td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(vat):N2}")</td> <td class="number"><span class="fleft">@Model.CurrencySymbol</span>@($"{Math.Abs(vat):N2}")</td>
@{ @{
var gebRows = Model.Data.Rows var gebRows = Model.Data.Rows
.Where(r => r.Geb.Price != null && r.Ungeb.Price != null) .Where(r => r.Geb.MaxPrice != null && r.Ungeb.MinPrice != null)
.Select(r => r.Geb.Price - r.Ungeb.Price); .Select(r => r.Geb.MaxPrice - r.Ungeb.MinPrice);
var minGeb = gebRows.Min(); var minGeb = gebRows.Min();
var maxGeb = gebRows.Max(); var maxGeb = gebRows.Max();
} }
@ -219,19 +219,22 @@
</table> </table>
<table class="payment-variant-data"> <table class="payment-variant-data">
<colgroup> <colgroup>
<col style="width: 30mm;"/>
<col style="width: 20mm;"/>
<col style="width: 25mm;"/>
<col style="width: 20mm;"/>
<col style="width: 25mm;"/>
<col style="width: 20mm;"/>
<col style="width: 25mm;"/> <col style="width: 25mm;"/>
<col style="width: 19mm;"/>
<col style="width: 18mm;"/>
<col style="width: 15mm;"/>
<col style="width: 18mm;"/>
<col style="width: 15mm;"/>
<col style="width: 18mm;"/>
<col style="width: 15mm;"/>
<col style="width: 22mm;"/>
</colgroup> </colgroup>
<thead> <thead>
<tr> <tr>
<th rowspan="2" style="text-align: left;">Qualitätsstufe</th> <th rowspan="2" style="text-align: left;">Qualitätsstufe</th>
<th>Gradation</th> <th>Gradation</th>
<th colspan="2">ungebunden</th> <th colspan="2">ungebunden</th>
<th colspan="2">attributlos gebunden</th>
<th colspan="2">gebunden</th> <th colspan="2">gebunden</th>
<th>Gesamt</th> <th>Gesamt</th>
</tr> </tr>
@ -241,6 +244,8 @@
<th>[@(Model.CurrencySymbol)/kg]</th> <th>[@(Model.CurrencySymbol)/kg]</th>
<th>[kg]</th> <th>[kg]</th>
<th>[@(Model.CurrencySymbol)/kg]</th> <th>[@(Model.CurrencySymbol)/kg]</th>
<th>[kg]</th>
<th>[@(Model.CurrencySymbol)/kg]</th>
<th>[@(Model.CurrencySymbol)]</th> <th>[@(Model.CurrencySymbol)]</th>
</tr> </tr>
</thead> </thead>
@ -258,6 +263,8 @@
<th colspan="2">@hdr</th> <th colspan="2">@hdr</th>
<td class="number">@($"{rows.Sum(r => r.Ungeb.Weight):N0}")</td> <td class="number">@($"{rows.Sum(r => r.Ungeb.Weight):N0}")</td>
<td></td> <td></td>
<td class="number">@($"{rows.Sum(r => r.LowGeb.Weight):N0}")</td>
<td></td>
<td class="number">@($"{rows.Sum(r => r.Geb.Weight):N0}")</td> <td class="number">@($"{rows.Sum(r => r.Geb.Weight):N0}")</td>
<td></td> <td></td>
<td class="number">@($"{rows.Sum(r => r.Amount):N2}")</td> <td class="number">@($"{rows.Sum(r => r.Amount):N2}")</td>
@ -267,9 +274,11 @@
<td>@(row.QualityLevel)</td> <td>@(row.QualityLevel)</td>
<td class="center">@($"{row.Oe:N0}")</td> <td class="center">@($"{row.Oe:N0}")</td>
<td class="number">@(row.Ungeb.Weight != 0 ? $"{row.Ungeb.Weight:N0}" : "-")</td> <td class="number">@(row.Ungeb.Weight != 0 ? $"{row.Ungeb.Weight:N0}" : "-")</td>
<td class="number">@(row.Ungeb.Price != null ? $"{row.Ungeb.Price:N4}" : "-")</td> <td class="number">@(row.Ungeb.MaxPrice != null ? $"{row.Ungeb.MaxPrice:N4}" : "-")</td>
<td class="number">@(row.LowGeb.Weight != 0 ? $"{row.LowGeb.Weight:N0}" : "-")</td>
<td class="number">@(row.LowGeb.MaxPrice != null ? $"{row.LowGeb.MaxPrice:N4}" : "-")</td>
<td class="number">@(row.Geb.Weight != 0 ? $"{row.Geb.Weight:N0}" : "-")</td> <td class="number">@(row.Geb.Weight != 0 ? $"{row.Geb.Weight:N0}" : "-")</td>
<td class="number">@(row.Geb.Price != null ? $"{row.Geb.Price:N4}" : "-")</td> <td class="number">@(row.Geb.MaxPrice != null ? $"{row.Geb.MaxPrice:N4}" : "-")</td>
<td class="number">@($"{row.Amount:N2}")</td> <td class="number">@($"{row.Amount:N2}")</td>
</tr> </tr>
lastHdr = hdr; lastHdr = hdr;

@ -3,7 +3,7 @@
@inherits TemplatePage<Elwig.Documents.WineQualityStatistics> @inherits TemplatePage<Elwig.Documents.WineQualityStatistics>
@model Elwig.Documents.WineQualityStatistics @model Elwig.Documents.WineQualityStatistics
@{ Layout = "Document"; } @{ Layout = "Document"; }
<link rel="stylesheet" href="file:///@Raw(Model.DataPath)\resources\WineQualityStatistics.css"/> <link rel="stylesheet" href="file:///@Raw(Model.DocumentsPath)\WineQualityStatistics.css" />
<main> <main>
<h1>Qualitätsstatistik</h1> <h1>Qualitätsstatistik</h1>
<h2>@Model.Filter</h2> <h2>@Model.Filter</h2>

@ -7,7 +7,7 @@
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<PreserveCompilationContext>true</PreserveCompilationContext> <PreserveCompilationContext>true</PreserveCompilationContext>
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon> <ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
<Version>0.13.8</Version> <Version>0.13.9</Version>
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages> <SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ApplicationManifest>app.manifest</ApplicationManifest> <ApplicationManifest>app.manifest</ApplicationManifest>
@ -20,20 +20,16 @@
<EmbeddedResource Include="Resources\Sql\*" /> <EmbeddedResource Include="Resources\Sql\*" />
</ItemGroup> </ItemGroup>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="call fetch-resources.bat" />
</Target>
<ItemGroup> <ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="LinqKit" Version="1.3.8" /> <PackageReference Include="LinqKit" Version="1.3.8" />
<PackageReference Include="MailKit" Version="4.11.0" /> <PackageReference Include="MailKit" Version="4.12.0" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.36" /> <PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.36" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="9.0.4" /> <PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="9.0.4" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3179.45" /> <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.3179.45" />
<PackageReference Include="NJsonSchema" Version="11.2.0" /> <PackageReference Include="NJsonSchema" Version="11.3.2" />
<PackageReference Include="PdfiumViewer" Version="2.13.0" /> <PackageReference Include="PdfiumViewer" Version="2.13.0" />
<PackageReference Include="PdfiumViewer.Native.x86_64.no_v8-no_xfa" Version="2018.4.8.256" /> <PackageReference Include="PdfiumViewer.Native.x86_64.no_v8-no_xfa" Version="2018.4.8.256" />
<PackageReference Include="RazorLight" Version="2.3.1" /> <PackageReference Include="RazorLight" Version="2.3.1" />

@ -9,7 +9,7 @@ namespace Elwig.Helpers {
public static class AppDbUpdater { public static class AppDbUpdater {
// Don't forget to update value in Tests/fetch-resources.bat! // Don't forget to update value in Tests/fetch-resources.bat!
public static readonly int RequiredSchemaVersion = 31; public static readonly int RequiredSchemaVersion = 32;
private static int VersionOffset = 0; private static int VersionOffset = 0;

@ -1,14 +1,15 @@
using System.IO.Compression;
using System.IO;
using System.Threading.Tasks;
using Elwig.Models.Entities; using Elwig.Models.Entities;
using System.Collections.Generic;
using System;
using System.Text.Json.Nodes;
using System.Linq;
using System.Windows;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
using System.Windows;
namespace Elwig.Helpers.Export { namespace Elwig.Helpers.Export {
public static class ElwigData { public static class ElwigData {
@ -63,7 +64,8 @@ namespace Elwig.Helpers.Export {
List<WbRd> Riede, List<WbRd> Riede,
List<Delivery> Deliveries, List<Delivery> Deliveries,
List<DeliveryPart> DeliveryParts, List<DeliveryPart> DeliveryParts,
List<DeliveryPartModifier> Modifiers)>(); List<DeliveryPartModifier> Modifiers,
Dictionary<string, List<(int Id1, int Id2, DateTime CreatedAt, DateTime ModifiedAt)>> Timestamps)>();
var metaData = new List<(string FileName, string ZwstId, string Device, var metaData = new List<(string FileName, string ZwstId, string Device,
int? MemberNum, string? MemberFilters, int? MemberNum, string? MemberFilters,
@ -94,7 +96,11 @@ namespace Elwig.Helpers.Export {
areaComCount, areaComFilters != null ? string.Join(" / ", areaComFilters) : null, areaComCount, areaComFilters != null ? string.Join(" / ", areaComFilters) : null,
deliveryCount, deliveryFilters != null ? string.Join(" / ", deliveryFilters) : null)); deliveryCount, deliveryFilters != null ? string.Join(" / ", deliveryFilters) : null));
data.Add(new([], [], [], [], [], [], [], new([], []))); data.Add(new([], [], [], [], [], [], [], new([], [], new() {
["member"] = [],
["area_commitment"] = [],
["delivery"] = [],
})));
var r = data[^1]; var r = data[^1];
var membersJson = zip.GetEntry("members.json"); var membersJson = zip.GetEntry("members.json");
@ -103,11 +109,13 @@ namespace Elwig.Helpers.Export {
string? line; string? line;
while ((line = await reader.ReadLineAsync()) != null) { while ((line = await reader.ReadLineAsync()) != null) {
var obj = JsonNode.Parse(line)!.AsObject(); var obj = JsonNode.Parse(line)!.AsObject();
var (m, b, telNrs, emailAddrs) = obj.ToMember(kgs); var (m, b, telNrs, emailAddrs, timestamps) = obj.ToMember(kgs);
r.Members.Add(m); r.Members.Add(m);
if (b != null) r.BillingAddresses.Add(b); if (b != null) r.BillingAddresses.Add(b);
r.TelephoneNumbers.AddRange(telNrs); r.TelephoneNumbers.AddRange(telNrs);
r.EmailAddresses.AddRange(emailAddrs); r.EmailAddresses.AddRange(emailAddrs);
if (timestamps.HasValue)
r.Timestamps["member"].Add((m.MgNr, 0, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt));
} }
} }
@ -117,12 +125,14 @@ namespace Elwig.Helpers.Export {
string? line; string? line;
while ((line = await reader.ReadLineAsync()) != null) { while ((line = await reader.ReadLineAsync()) != null) {
var obj = JsonNode.Parse(line)!.AsObject(); var obj = JsonNode.Parse(line)!.AsObject();
var (areaCom, wbrd) = obj.ToAreaCom(kgs, currentWbRde); var (areaCom, wbrd, timestamps) = obj.ToAreaCom(kgs, currentWbRde);
r.AreaCommitments.Add(areaCom); r.AreaCommitments.Add(areaCom);
if (wbrd != null) { if (wbrd != null) {
currentWbRde[wbrd.KgNr].Add(wbrd); currentWbRde[wbrd.KgNr].Add(wbrd);
r.Riede.Add(wbrd); r.Riede.Add(wbrd);
} }
if (timestamps.HasValue)
r.Timestamps["area_commitment"].Add((areaCom.FbNr, 0, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt));
} }
} }
@ -132,10 +142,12 @@ namespace Elwig.Helpers.Export {
string? line; string? line;
while ((line = await reader.ReadLineAsync()) != null) { while ((line = await reader.ReadLineAsync()) != null) {
var obj = JsonNode.Parse(line)!.AsObject(); var obj = JsonNode.Parse(line)!.AsObject();
var (d, parts, mods) = obj.ToDelivery(currentLsNrs, currentDids); var (d, parts, mods, timestamps) = obj.ToDelivery(currentLsNrs, currentDids);
r.Deliveries.Add(d); r.Deliveries.Add(d);
r.DeliveryParts.AddRange(parts); r.DeliveryParts.AddRange(parts);
r.Modifiers.AddRange(mods); r.Modifiers.AddRange(mods);
if (timestamps.HasValue)
r.Timestamps["delivery"].Add((d.Year, d.DId, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt));
} }
} }
} }
@ -144,7 +156,7 @@ namespace Elwig.Helpers.Export {
var importedAreaComs = new List<(string FileName, string ZwstId, string Device, int Imported, int NotImported, string Filters)>(); var importedAreaComs = new List<(string FileName, string ZwstId, string Device, int Imported, int NotImported, string Filters)>();
var importedDeliveries = new List<(string FileName, string ZwstId, string Device, int New, int Overwritten, int NotImported, string Filters)>(); var importedDeliveries = new List<(string FileName, string ZwstId, string Device, int New, int Overwritten, int NotImported, string Filters)>();
foreach (var ((members, billingAddresses, telephoneNumbers, emailAddresses, areaCommitments, riede, deliveries, deliveryParts, modifiers), meta) in data.Zip(metaData)) { foreach (var ((members, billingAddresses, telephoneNumbers, emailAddresses, areaCommitments, riede, deliveries, deliveryParts, modifiers, timestamps), meta) in data.Zip(metaData)) {
var branch = branches[meta.ZwstId]; var branch = branches[meta.ZwstId];
var device = meta.Device; var device = meta.Device;
@ -272,6 +284,26 @@ namespace Elwig.Helpers.Export {
} }
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
var primaryKeys = new Dictionary<string, string>() {
["member"] = "mgnr, 0",
["area_commitment"] = "fbnr, 0",
["delivery"] = "year, did",
};
var updateStmts = timestamps
.SelectMany(e => e.Value.Select(m => $"UPDATE {e.Key} " +
$"SET ctime = {((DateTimeOffset)m.CreatedAt.ToUniversalTime()).ToUnixTimeSeconds()}, " +
$"mtime = {((DateTimeOffset)m.ModifiedAt.ToUniversalTime()).ToUnixTimeSeconds()} " +
$"WHERE ({primaryKeys[e.Key]}) = ({m.Id1}, {m.Id2});"));
using var cnx = AppDbContext.Connect();
await AppDbContext.ExecuteBatch(cnx, $"""
BEGIN;
UPDATE client_parameter SET value = '0' WHERE param = 'ENABLE_TIME_TRIGGERS';
{string.Join("\n", updateStmts)}
UPDATE client_parameter SET value = '1' WHERE param = 'ENABLE_TIME_TRIGGERS';
COMMIT;
""");
await AddImportedFiles(Path.GetFileName(meta.FileName)); await AddImportedFiles(Path.GetFileName(meta.FileName));
} }
App.HintContextChange(); App.HintContextChange();
@ -460,15 +492,19 @@ namespace Elwig.Helpers.Export {
return obj; return obj;
}).ToArray()), }).ToArray()),
["comment"] = m.Comment, ["comment"] = m.Comment,
["created_at"] = $"{m.CreatedAt:yyyy-MM-ddTHH:mm:ssK}",
["modified_at"] = $"{m.ModifiedAt:yyyy-MM-ddTHH:mm:ssK}",
}; };
} }
public static (Member, BillingAddr?, List<MemberTelNr>, List<MemberEmailAddr>) ToMember(this JsonNode json, Dictionary<int, AT_Kg> kgs) { public static (Member, BillingAddr?, List<MemberTelNr>, List<MemberEmailAddr>, (DateTime CreatedAt, DateTime ModifiedAt)?) ToMember(this JsonNode json, Dictionary<int, AT_Kg> kgs) {
var mgnr = json["mgnr"]!.AsValue().GetValue<int>(); var mgnr = json["mgnr"]!.AsValue().GetValue<int>();
var kgnr = json["default_kgnr"]?.AsValue().GetValue<int>(); var kgnr = json["default_kgnr"]?.AsValue().GetValue<int>();
if (kgnr != null && !kgs.Values.Any(k => k.WbKg?.KgNr == kgnr)) { if (kgnr != null && !kgs.Values.Any(k => k.WbKg?.KgNr == kgnr)) {
throw new ArgumentException($"Für KG {(kgs.TryGetValue(kgnr.Value, out var k) ? k.Name : "?")} ({kgnr:00000}) ist noch keine Großlage festgelegt!\n(Stammdaten → Herkunftshierarchie)"); throw new ArgumentException($"Für KG {(kgs.TryGetValue(kgnr.Value, out var k) ? k.Name : "?")} ({kgnr:00000}) ist noch keine Großlage festgelegt!\n(Stammdaten → Herkunftshierarchie)");
} }
var createdAt = json["created_at"]?.AsValue().GetValue<string>();
var modifiedAt = json["modified_at"]?.AsValue().GetValue<string>();
return (new Member { return (new Member {
MgNr = mgnr, MgNr = mgnr,
PredecessorMgNr = json["predecessor_mgnr"]?.AsValue().GetValue<int>(), PredecessorMgNr = json["predecessor_mgnr"]?.AsValue().GetValue<int>(),
@ -502,6 +538,7 @@ namespace Elwig.Helpers.Export {
ContactViaPost = json["contact_postal"]?.AsValue().GetValue<bool>() ?? false, ContactViaPost = json["contact_postal"]?.AsValue().GetValue<bool>() ?? false,
ContactViaEmail = json["contact_email"]?.AsValue().GetValue<bool>() ?? false, ContactViaEmail = json["contact_email"]?.AsValue().GetValue<bool>() ?? false,
Comment = json["comment"]?.AsValue().GetValue<string>(), Comment = json["comment"]?.AsValue().GetValue<string>(),
ImportedAt = DateTime.Now,
}, json["billing_address"] is JsonObject a ? new BillingAddr { }, json["billing_address"] is JsonObject a ? new BillingAddr {
MgNr = mgnr, MgNr = mgnr,
FullName = a["name"]!.AsValue().GetValue<string>(), FullName = a["name"]!.AsValue().GetValue<string>(),
@ -519,7 +556,10 @@ namespace Elwig.Helpers.Export {
Nr = i + 1, Nr = i + 1,
Address = a["address"]!.AsValue().GetValue<string>(), Address = a["address"]!.AsValue().GetValue<string>(),
Comment = a["comment"]?.AsValue().GetValue<string>(), Comment = a["comment"]?.AsValue().GetValue<string>(),
}).ToList()); }).ToList(),
createdAt == null || modifiedAt == null ? null :
(DateTime.ParseExact(createdAt, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None),
DateTime.ParseExact(modifiedAt, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None)));
} }
public static JsonObject ToJson(this AreaCom c) { public static JsonObject ToJson(this AreaCom c) {
@ -535,10 +575,12 @@ namespace Elwig.Helpers.Export {
["year_from"] = c.YearFrom, ["year_from"] = c.YearFrom,
["year_to"] = c.YearTo, ["year_to"] = c.YearTo,
["comment"] = c.Comment, ["comment"] = c.Comment,
["created_at"] = $"{c.CreatedAt:yyyy-MM-ddTHH:mm:ssK}",
["modified_at"] = $"{c.ModifiedAt:yyyy-MM-ddTHH:mm:ssK}",
}; };
} }
public static (AreaCom, WbRd?) ToAreaCom(this JsonNode json, Dictionary<int, AT_Kg> kgs, Dictionary<int, List<WbRd>> riede) { public static (AreaCom, WbRd?, (DateTime CreatedAt, DateTime ModifiedAt)?) ToAreaCom(this JsonNode json, Dictionary<int, AT_Kg> kgs, Dictionary<int, List<WbRd>> riede) {
var kgnr = json["kgnr"]!.AsValue().GetValue<int>(); var kgnr = json["kgnr"]!.AsValue().GetValue<int>();
var ried = json["ried"]?.AsValue().GetValue<string>(); var ried = json["ried"]?.AsValue().GetValue<string>();
WbRd? rd = null; WbRd? rd = null;
@ -556,6 +598,8 @@ namespace Elwig.Helpers.Export {
rde.Add(rd); rde.Add(rd);
} }
} }
var createdAt = json["created_at"]?.AsValue().GetValue<string>();
var modifiedAt = json["modified_at"]?.AsValue().GetValue<string>();
return (new AreaCom { return (new AreaCom {
FbNr = json["fbnr"]!.AsValue().GetValue<int>(), FbNr = json["fbnr"]!.AsValue().GetValue<int>(),
MgNr = json["mgnr"]!.AsValue().GetValue<int>(), MgNr = json["mgnr"]!.AsValue().GetValue<int>(),
@ -568,7 +612,11 @@ namespace Elwig.Helpers.Export {
YearFrom = json["year_from"]?.AsValue().GetValue<int>(), YearFrom = json["year_from"]?.AsValue().GetValue<int>(),
YearTo = json["year_to"]?.AsValue().GetValue<int>(), YearTo = json["year_to"]?.AsValue().GetValue<int>(),
Comment = json["comment"]?.AsValue().GetValue<string>(), Comment = json["comment"]?.AsValue().GetValue<string>(),
}, newRd ? rd : null); ImportedAt = DateTime.Now,
}, newRd ? rd : null,
createdAt == null || modifiedAt == null ? null :
(DateTime.ParseExact(createdAt, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None),
DateTime.ParseExact(modifiedAt, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None)));
} }
public static JsonObject ToJson(this Delivery d) { public static JsonObject ToJson(this Delivery d) {
@ -596,6 +644,8 @@ namespace Elwig.Helpers.Export {
["manual_weighing"] = p.IsManualWeighing, ["manual_weighing"] = p.IsManualWeighing,
["modids"] = new JsonArray(p.Modifiers.Select(m => (JsonNode)m.ModId).ToArray()), ["modids"] = new JsonArray(p.Modifiers.Select(m => (JsonNode)m.ModId).ToArray()),
["comment"] = p.Comment, ["comment"] = p.Comment,
["created_at"] = $"{p.CreatedAt:yyyy-MM-ddTHH:mm:ssK}",
["modified_at"] = $"{p.ModifiedAt:yyyy-MM-ddTHH:mm:ssK}",
}; };
if (p.IsSplCheck) obj["spl_check"] = p.IsSplCheck; if (p.IsSplCheck) obj["spl_check"] = p.IsSplCheck;
if (p.IsHandPicked != null) obj["hand_picked"] = p.IsHandPicked; if (p.IsHandPicked != null) obj["hand_picked"] = p.IsHandPicked;
@ -609,10 +659,12 @@ namespace Elwig.Helpers.Export {
return obj; return obj;
}).ToArray()), }).ToArray()),
["comment"] = d.Comment, ["comment"] = d.Comment,
["created_at"] = $"{d.CreatedAt:yyyy-MM-ddTHH:mm:ssK}",
["modified_at"] = $"{d.ModifiedAt:yyyy-MM-ddTHH:mm:ssK}",
}; };
} }
public static (Delivery, List<DeliveryPart>, List<DeliveryPartModifier>) ToDelivery(this JsonNode json, Dictionary<string, int> currentLsNrs, Dictionary<int, int> currentDids) { public static (Delivery, List<DeliveryPart>, List<DeliveryPartModifier>, (DateTime CreatedAt, DateTime ModifiedAt)?) ToDelivery(this JsonNode json, Dictionary<string, int> currentLsNrs, Dictionary<int, int> currentDids) {
var year = json["year"]!.AsValue().GetValue<int>(); var year = json["year"]!.AsValue().GetValue<int>();
var lsnr = json["lsnr"]!.AsValue().GetValue<string>(); var lsnr = json["lsnr"]!.AsValue().GetValue<string>();
var did = currentLsNrs.GetValueOrDefault(lsnr, -1); var did = currentLsNrs.GetValueOrDefault(lsnr, -1);
@ -621,6 +673,8 @@ namespace Elwig.Helpers.Export {
did = ++currentDids[year]; did = ++currentDids[year];
} }
currentLsNrs[lsnr] = did; currentLsNrs[lsnr] = did;
var createdAt = json["created_at"]?.AsValue().GetValue<string>();
var modifiedAt = json["modified_at"]?.AsValue().GetValue<string>();
return (new Delivery { return (new Delivery {
Year = year, Year = year,
DId = did, DId = did,
@ -631,6 +685,7 @@ namespace Elwig.Helpers.Export {
LsNr = lsnr, LsNr = lsnr,
MgNr = json["mgnr"]!.AsValue().GetValue<int>(), MgNr = json["mgnr"]!.AsValue().GetValue<int>(),
Comment = json["comment"]?.AsValue().GetValue<string>(), Comment = json["comment"]?.AsValue().GetValue<string>(),
ImportedAt = DateTime.Now,
}, json["parts"]!.AsArray().Select(p => p!.AsObject()).Select(p => new DeliveryPart { }, json["parts"]!.AsArray().Select(p => p!.AsObject()).Select(p => new DeliveryPart {
Year = year, Year = year,
DId = did, DId = did,
@ -661,7 +716,10 @@ namespace Elwig.Helpers.Export {
DId = did, DId = did,
DPNr = p["dpnr"]!.AsValue().GetValue<int>(), DPNr = p["dpnr"]!.AsValue().GetValue<int>(),
ModId = m!.AsValue().GetValue<string>(), ModId = m!.AsValue().GetValue<string>(),
})).ToList()); })).ToList(),
createdAt == null || modifiedAt == null ? null :
(DateTime.ParseExact(createdAt, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None),
DateTime.ParseExact(modifiedAt, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None)));
} }
} }
} }

@ -317,7 +317,7 @@ namespace Elwig.Helpers.Export {
c = $"<{ct} office:value-type=\"string\" calcext:value-type=\"string\"{add}><text:p>{SecurityElement.Escape(data.ToString())}</text:p></{ct}>"; c = $"<{ct} office:value-type=\"string\" calcext:value-type=\"string\"{add}><text:p>{SecurityElement.Escape(data.ToString())}</text:p></{ct}>";
} }
return $" {c}\r\n" + (colSpan > 1 ? $" <table:covered-table-cell table:number-rows-repeated=\"{colSpan - 1}\"/>\r\n" : ""); return $" {c}\r\n" + (colSpan > 1 ? $" <table:covered-table-cell table:number-columns-repeated=\"{colSpan - 1}\"/>\r\n" : "");
} }
} }
} }

@ -10,7 +10,7 @@ namespace Elwig.Helpers.Printing {
public static async Task Init(Action? evtHandler = null) { public static async Task Init(Action? evtHandler = null) {
var e = new RazorLightEngineBuilder() var e = new RazorLightEngineBuilder()
.UseFileSystemProject(App.DataPath + "resources") .UseFileSystemProject(App.DocumentsPath)
.UseMemoryCachingProvider() .UseMemoryCachingProvider()
.Build(); .Build();

@ -14,7 +14,7 @@ using System.Drawing.Printing;
namespace Elwig.Helpers.Printing { namespace Elwig.Helpers.Printing {
public static class Pdf { public static class Pdf {
private static readonly string WinziPrint = new string[] { App.ExePath } private static readonly string WinziPrint = new string[] { App.InstallPath }
.Union(Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? []) .Union(Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? [])
.Select(x => Path.Combine(x, "WinziPrint.exe")) .Select(x => Path.Combine(x, "WinziPrint.exe"))
.Where(File.Exists) .Where(File.Exists)

@ -434,8 +434,7 @@ namespace Elwig.Helpers {
if (accept != null) if (accept != null)
client.DefaultRequestHeaders.Accept.Add(new(accept)); client.DefaultRequestHeaders.Accept.Add(new(accept));
if (username != null || password != null) if (username != null || password != null)
client.DefaultRequestHeaders.Authorization = new("Basic", Convert.ToBase64String( client.DefaultRequestHeaders.Authorization = new("Basic", Convert.ToBase64String(Utils.UTF8.GetBytes($"{username}:{password}")));
Utils.UTF8.GetBytes($"{username}:{password}")));
return client; return client;
} }
@ -454,6 +453,8 @@ namespace Elwig.Helpers {
} }
public static async Task UploadExportData(string zip, string url, string username, string password) { public static async Task UploadExportData(string zip, string url, string username, string password) {
if (url.StartsWith("https://elwig.at/clients/"))
url = "https://sync.elwig.at/" + url[25..];
if (!url.EndsWith('/')) url += "/"; if (!url.EndsWith('/')) url += "/";
using var client = GetHttpClient(username, password, accept: "application/json"); using var client = GetHttpClient(username, password, accept: "application/json");
var content = new StreamContent(new FileStream(zip, FileMode.Open, FileAccess.Read)); var content = new StreamContent(new FileStream(zip, FileMode.Open, FileAccess.Read));
@ -463,6 +464,8 @@ namespace Elwig.Helpers {
} }
public static async Task<JsonArray> GetExportMetaData(string url, string username, string password) { public static async Task<JsonArray> GetExportMetaData(string url, string username, string password) {
if (url.StartsWith("https://elwig.at/clients/"))
url = "https://sync.elwig.at/" + url[25..];
using var client = GetHttpClient(username, password, accept: "application/json"); using var client = GetHttpClient(username, password, accept: "application/json");
using var res = await client.GetAsync(url); using var res = await client.GetAsync(url);
res.EnsureSuccessStatusCode(); res.EnsureSuccessStatusCode();

@ -27,14 +27,14 @@ namespace Elwig.Models.Dtos {
MgNr = mgnr; MgNr = mgnr;
} }
public static async Task<IDictionary<int, CreditNoteDeliveryData>> ForPaymentVariant(DbSet<CreditNoteDeliveryRowSingle> table, DbSet<Season> seasons, int year, int avnr) { public static async Task<IDictionary<int, CreditNoteDeliveryData>> ForPaymentVariant(DbSet<CreditNoteDeliveryRowSingle> table, DbSet<PaymentVar> paymentVariants, int year, int avnr) {
var variant = (await seasons.FindAsync(year))?.PaymentVariants.Where(v => v.AvNr == avnr).SingleOrDefault(); var variant = await paymentVariants.FindAsync(year, avnr);
BillingData? varData = null; BillingData? varData = null;
try { varData = variant != null ? BillingData.FromJson(variant.Data) : null; } catch { } try { varData = variant != null ? BillingData.FromJson(variant.Data) : null; } catch { }
return (await FromDbSet(table, year, avnr)) return (await FromDbSet(table, year, avnr))
.GroupBy( .GroupBy(
r => new { r.Year, r.AvNr, r.MgNr, r.TgNr, r.DId, r.DPNr }, r => new { r.Year, r.AvNr, r.MgNr, r.TgNr, r.DId, r.DPNr },
(k, g) => new CreditNoteDeliveryRow(g, seasons, varData?.NetWeightModifier ?? 0.0, varData?.GrossWeightModifier ?? 0.0)) (k, g) => new CreditNoteDeliveryRow(g, variant, varData))
.GroupBy( .GroupBy(
r => new { r.Year, r.AvNr, r.MgNr, r.TgNr }, r => new { r.Year, r.AvNr, r.MgNr, r.TgNr },
(k, g) => new CreditNoteDeliveryData(g, k.Year, k.TgNr, mgnr: k.MgNr)) (k, g) => new CreditNoteDeliveryData(g, k.Year, k.TgNr, mgnr: k.MgNr))
@ -86,13 +86,13 @@ namespace Elwig.Models.Dtos {
public decimal? Amount; public decimal? Amount;
public double WeighingModifier; public double WeighingModifier;
public CreditNoteDeliveryRow(IEnumerable<CreditNoteDeliveryRowSingle> rows, DbSet<Season> seasons, double netWeightModifier, double grossWeightModifier) { public CreditNoteDeliveryRow(IEnumerable<CreditNoteDeliveryRowSingle> rows, PaymentVar paymentVariant, BillingData? varData) {
var f = rows.First(); var f = rows.First();
Year = f.Year; Year = f.Year;
TgNr = f.TgNr; TgNr = f.TgNr;
AvNr = f.AvNr; AvNr = f.AvNr;
MgNr = f.MgNr; MgNr = f.MgNr;
var season = seasons.Find(Year); var season = paymentVariant.Season;
LsNr = f.LsNr; LsNr = f.LsNr;
DPNr = f.DPNr; DPNr = f.DPNr;
@ -115,7 +115,7 @@ namespace Elwig.Models.Dtos {
b.Price != null ? season?.DecFromDb((long)b.Price) : null, b.Price != null ? season?.DecFromDb((long)b.Price) : null,
b.Amount != null ? season?.DecFromDb((long)b.Amount) : null)) b.Amount != null ? season?.DecFromDb((long)b.Amount) : null))
.ToArray(); .ToArray();
WeighingModifier = f.NetWeight ? netWeightModifier : grossWeightModifier; WeighingModifier = (varData == null || !varData.ConsiderDelieryModifiers) ? 0 : f.NetWeight ? varData.NetWeightModifier : varData.GrossWeightModifier;
Amount = f.TotalAmount != null ? season?.DecFromDb((long)f.TotalAmount) : null; Amount = f.TotalAmount != null ? season?.DecFromDb((long)f.TotalAmount) : null;
var netAmount = f.NetAmount != null ? season?.DecFromDb((long)f.NetAmount) : null; var netAmount = f.NetAmount != null ? season?.DecFromDb((long)f.NetAmount) : null;
var amt = netAmount * (decimal)(1.0 + WeighingModifier); var amt = netAmount * (decimal)(1.0 + WeighingModifier);

@ -17,9 +17,9 @@ namespace Elwig.Models.Dtos {
("Name2", "Vorname", null, 40), ("Name2", "Vorname", null, 40),
("DefaultKg", "Ort", null, 40), ("DefaultKg", "Ort", null, 40),
("SortId", "Sorte", null, 10), ("SortId", "Sorte", null, 10),
("Weight", "Gewicht", "kg", 20), ("Weight", "Menge", "kg", 20),
("CreatedTimestamp", "Angemeldet", null, 35), ("CreatedAt", "Angemeldet", null, 35),
("ModifiedTimestamp", "Geändert", null, 35), ("ModifiedAt", "Geändert", null, 35),
("Status", "Status", null, 20), ("Status", "Status", null, 20),
]; ];
@ -47,8 +47,8 @@ namespace Elwig.Models.Dtos {
public string? DefaultKg; public string? DefaultKg;
public string SortId; public string SortId;
public string Variety; public string Variety;
public DateTime CreatedTimestamp; public DateTime CreatedAt;
public DateTime? ModifiedTimestamp; public DateTime? ModifiedAt;
public int Weight; public int Weight;
public string? Status; public string? Status;
@ -65,10 +65,10 @@ namespace Elwig.Models.Dtos {
DefaultKg = m.DefaultKg?.Name; DefaultKg = m.DefaultKg?.Name;
SortId = a.SortId; SortId = a.SortId;
Variety = a.Variety.Name; Variety = a.Variety.Name;
CreatedTimestamp = a.CreatedTimestamp; CreatedAt = a.CreatedAt;
ModifiedTimestamp = a.ModifiedTimestamp == a.CreatedTimestamp ? null : a.ModifiedTimestamp; ModifiedAt = a.ModifiedAt == a.CreatedAt ? null : a.ModifiedAt;
Weight = a.Weight; Weight = a.Weight;
Status = s.AncmtTo == null ? null : a.CreatedTimestamp >= s.AncmtTo ? "verspät." : "ok"; Status = s.AncmtTo == null ? null : a.CreatedAt >= s.AncmtTo ? "verspät." : "ok";
} }
} }
} }

@ -16,7 +16,7 @@ namespace Elwig.Models.Dtos {
("QualityLevel", "Qualitätsstufe", null, 25), ("QualityLevel", "Qualitätsstufe", null, 25),
("Gradation", "Gradation", "°Oe|°KMW", 32), ("Gradation", "Gradation", "°Oe|°KMW", 32),
("Buckets", "Flächenbindung", "|kg", 36), ("Buckets", "Flächenbindung", "|kg", 36),
("Weight", "Gewicht", "kg", 16), ("Weight", "Menge", "kg", 16),
]; ];
private readonly int MgNr; private readonly int MgNr;

@ -24,7 +24,7 @@ namespace Elwig.Models.Dtos {
("CultId", "Bewirt.", null, 15), ("CultId", "Bewirt.", null, 15),
("QualId", "Qualität", null, 15), ("QualId", "Qualität", null, 15),
("Gradation", "Gradation", "°Oe|°KMW", 40), ("Gradation", "Gradation", "°Oe|°KMW", 40),
("Weight", "Gewicht", "kg", 20), ("Weight", "Menge", "kg", 20),
("IsNetWeight", "Gerebelt", null, 20), ("IsNetWeight", "Gerebelt", null, 20),
("HkId", "Herkunft", null, 20), ("HkId", "Herkunft", null, 20),
("Modifiers", "Zu-/Abschläge", null, 40), ("Modifiers", "Zu-/Abschläge", null, 40),

@ -18,8 +18,9 @@ namespace Elwig.Models.Dtos {
("Cultivation", "Bewirt.", null, 20), ("Cultivation", "Bewirt.", null, 20),
("QualityLevel", "Qualitätsstufe", null, 30), ("QualityLevel", "Qualitätsstufe", null, 30),
("Oe", "Gradation", "°Oe", 20), ("Oe", "Gradation", "°Oe", 20),
("Ungeb", "ungebunden", "kg|€/kg", 40), ("Ungeb", "ungebunden", "kg|€/kg|€/kg", 60),
("Geb", "gebunden", "kg|€/kg", 40), ("LowGeb", "attributlos gebunden", "kg|€/kg|€/kg", 60),
("Geb", "gebunden", "kg|€/kg|€/kg", 60),
("Amount", "Gesamt", "€", 25), ("Amount", "Gesamt", "€", 25),
]; ];
@ -30,8 +31,9 @@ namespace Elwig.Models.Dtos {
string? Cultivation, string? Cultivation,
string QualityLevel, string QualityLevel,
double Oe, double Oe,
(int Weight, decimal? Price) Ungeb, (int Weight, decimal? MinPrice, decimal? MaxPrice) Ungeb,
(int Weight, decimal? Price) Geb, (int Weight, decimal? MinPrice, decimal? MaxPrice) LowGeb,
(int Weight, decimal? MinPrice, decimal? MaxPrice) Geb,
decimal Amount decimal Amount
); );
@ -42,8 +44,9 @@ namespace Elwig.Models.Dtos {
public static async Task<PaymentVariantSummaryData> ForPaymentVariant(PaymentVar v, DbSet<PaymentVariantSummaryRow> table) { public static async Task<PaymentVariantSummaryData> ForPaymentVariant(PaymentVar v, DbSet<PaymentVariantSummaryRow> table) {
return new(v, (await FromDbSet(table, v.Year, v.AvNr)) return new(v, (await FromDbSet(table, v.Year, v.AvNr))
.Select(r => new PaymentRow(r.Type, r.Variety, r.Attribute, r.Cultivation, r.QualityLevel, r.Oe, .Select(r => new PaymentRow(r.Type, r.Variety, r.Attribute, r.Cultivation, r.QualityLevel, r.Oe,
(r.WeightUngeb, r.PriceUngeb != null ? Utils.DecFromDb(r.PriceUngeb.Value, v.Season.Precision) : null), (r.WeightUngeb, r.MinPriceUngeb != null ? Utils.DecFromDb(r.MinPriceUngeb.Value, v.Season.Precision) : null, r.MaxPriceUngeb != null ? Utils.DecFromDb(r.MaxPriceUngeb.Value, v.Season.Precision) : null),
(r.WeightGeb, r.PriceGeb != null ? Utils.DecFromDb(r.PriceGeb.Value, v.Season.Precision) : null), (r.WeightLowGeb, r.MinPriceLowGeb != null ? Utils.DecFromDb(r.MinPriceLowGeb.Value, v.Season.Precision) : null, r.MaxPriceLowGeb != null ? Utils.DecFromDb(r.MaxPriceLowGeb.Value, v.Season.Precision) : null),
(r.WeightGeb, r.MinPriceGeb != null ? Utils.DecFromDb(r.MinPriceGeb.Value, v.Season.Precision) : null, r.MaxPriceGeb != null ? Utils.DecFromDb(r.MaxPriceGeb.Value, v.Season.Precision) : null),
Utils.DecFromDb(r.Amount, v.Season.Precision))) Utils.DecFromDb(r.Amount, v.Season.Precision)))
.ToArray()); .ToArray());
} }
@ -57,19 +60,23 @@ namespace Elwig.Models.Dtos {
q.name AS quality_level, q.name AS quality_level,
ROUND(kmw * (4.54 + 0.022 * kmw)) AS oe, ROUND(kmw * (4.54 + 0.022 * kmw)) AS oe,
SUM(IIF(w.discr = '_', w.value, 0)) AS weight_ungeb, SUM(IIF(w.discr = '_', w.value, 0)) AS weight_ungeb,
MAX(IIF(w.discr = '_', b.price, NULL)) AS price_ungeb, MIN(IIF(w.discr = '_', b.price, NULL)) AS min_price_ungeb,
SUM(IIF(w.discr != '_', w.value, 0)) AS weight_geb, MAX(IIF(w.discr = '_', b.price, NULL)) AS max_price_ungeb,
MAX(IIF(w.discr != '_', b.price, NULL)) AS price_geb, SUM(IIF(w.discr NOT IN (COALESCE(p.attrid, ''), '_'), w.value, 0)) AS weight_lowgeb,
MIN(IIF(w.discr NOT IN (COALESCE(p.attrid, ''), '_'), b.price, NULL)) AS min_price_lowgeb,
MAX(IIF(w.discr NOT IN (COALESCE(p.attrid, ''), '_'), b.price, NULL)) AS max_price_lowgeb,
SUM(IIF(w.discr = COALESCE(p.attrid, ''), w.value, 0)) AS weight_geb,
MIN(IIF(w.discr = COALESCE(p.attrid, ''), b.price, NULL)) AS min_price_geb,
MAX(IIF(w.discr = COALESCE(p.attrid, ''), b.price, NULL)) AS max_price_geb,
SUM(b.amount) AS amount SUM(b.amount) AS amount
FROM payment_delivery_part_bucket b FROM payment_delivery_part_bucket b
LEFT JOIN delivery_part_bucket w ON (w.year, w.did, w.dpnr, w.bktnr) = (b.year, b.did, b.dpnr, b.bktnr) LEFT JOIN delivery_part_bucket w ON (w.year, w.did, w.dpnr, w.bktnr) = (b.year, b.did, b.dpnr, b.bktnr)
LEFT JOIN delivery_part p ON (p.year, p.did, p.dpnr) = (b.year, b.did, b.dpnr) LEFT JOIN delivery_part p ON (p.year, p.did, p.dpnr) = (b.year, b.did, b.dpnr)
LEFT JOIN delivery d ON (d.year, d.did) = (p.year, p.did)
LEFT JOIN wine_variety v ON v.sortid = p.sortid LEFT JOIN wine_variety v ON v.sortid = p.sortid
LEFT JOIN wine_attribute a ON a.attrid = p.attrid LEFT JOIN wine_attribute a ON a.attrid = p.attrid
LEFT JOIN wine_cultivation c ON c.cultid = p.cultid LEFT JOIN wine_cultivation c ON c.cultid = p.cultid
LEFT JOIN wine_quality_level q ON q.qualid = p.qualid LEFT JOIN wine_quality_level q ON q.qualid = p.qualid
WHERE d.year = {year} AND b.avnr = {avnr} WHERE p.year = {year} AND b.avnr = {avnr}
GROUP BY variety, attribute, cultivation, q.min_kmw, oe GROUP BY variety, attribute, cultivation, q.min_kmw, oe
ORDER BY variety, attribute, cultivation, q.min_kmw, oe ORDER BY variety, attribute, cultivation, q.min_kmw, oe
""").ToListAsync(); """).ToListAsync();
@ -92,12 +99,22 @@ namespace Elwig.Models.Dtos {
public double Oe { get; set; } public double Oe { get; set; }
[Column("weight_ungeb")] [Column("weight_ungeb")]
public int WeightUngeb { get; set; } public int WeightUngeb { get; set; }
[Column("price_ungeb")] [Column("min_price_ungeb")]
public long? PriceUngeb { get; set; } public long? MinPriceUngeb { get; set; }
[Column("max_price_ungeb")]
public long? MaxPriceUngeb { get; set; }
[Column("weight_lowgeb")]
public int WeightLowGeb { get; set; }
[Column("min_price_lowgeb")]
public long? MinPriceLowGeb { get; set; }
[Column("max_price_lowgeb")]
public long? MaxPriceLowGeb { get; set; }
[Column("weight_geb")] [Column("weight_geb")]
public int WeightGeb { get; set; } public int WeightGeb { get; set; }
[Column("price_geb")] [Column("min_price_geb")]
public long? PriceGeb { get; set; } public long? MinPriceGeb { get; set; }
[Column("max_price_geb")]
public long? MaxPriceGeb { get; set; }
[Column("amount")] [Column("amount")]
public long Amount { get; set; } public long Amount { get; set; }
} }

@ -14,7 +14,7 @@ namespace Elwig.Models.Dtos {
("CultId", "Bewirt.", null, 15), ("CultId", "Bewirt.", null, 15),
("QualId", "Qual.", null, 15), ("QualId", "Qual.", null, 15),
("Geb", "gebunden", null, 20), ("Geb", "gebunden", null, 20),
("Weight", "Gewicht", "kg", 20), ("Weight", "Menge", "kg", 20),
]; ];
public WeightBreakdownData(IEnumerable<WeightBreakdownRow> rows, int year, string name) : public WeightBreakdownData(IEnumerable<WeightBreakdownRow> rows, int year, string name) :

@ -17,7 +17,7 @@ namespace Elwig.Models.Dtos {
("Members", "Mitgl.", "#", 15), ("Members", "Mitgl.", "#", 15),
("Deliveries", "Lfrg.", "#", 15), ("Deliveries", "Lfrg.", "#", 15),
("Parts", "Teill.", "#", 15), ("Parts", "Teill.", "#", 15),
("Weight", "Gewicht", "kg", 20), ("Weight", "Menge", "kg", 20),
("Gradation", "Gradation", "°Oe|°KMW", 30), ("Gradation", "Gradation", "°Oe|°KMW", 30),
]; ];

@ -41,14 +41,36 @@ namespace Elwig.Models.Entities {
public string? Comment { get; set; } public string? Comment { get; set; }
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)] [Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long CTime { get; private set; } public long CTime { get; set; }
[NotMapped] [NotMapped]
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime; public DateTime CreatedAt {
get => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
set => CTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
}
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)] [Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long MTime { get; private set; } public long MTime { get; set; }
[NotMapped] [NotMapped]
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime; public DateTime ModifiedAt {
get => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
set => MTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
}
[Column("xtime")]
public long? XTime { get; set; }
[NotMapped]
public DateTime? ExportedAt {
get => XTime == null ? null : DateTimeOffset.FromUnixTimeSeconds(XTime.Value).LocalDateTime;
set => XTime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
}
[Column("itime")]
public long? ITime { get; set; }
[NotMapped]
public DateTime? ImportedAt {
get => ITime == null ? null : DateTimeOffset.FromUnixTimeSeconds(ITime.Value).LocalDateTime;
set => ITime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
}
[ForeignKey("MgNr")] [ForeignKey("MgNr")]
public virtual Member Member { get; private set; } = null!; public virtual Member Member { get; private set; } = null!;

@ -84,14 +84,36 @@ namespace Elwig.Models.Entities {
} }
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)] [Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long CTime { get; private set; } public long CTime { get; set; }
[NotMapped] [NotMapped]
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime; public DateTime CreatedAt {
get => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
set => CTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
}
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)] [Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long MTime { get; private set; } public long MTime { get; set; }
[NotMapped] [NotMapped]
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime; public DateTime ModifiedAt {
get => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
set => MTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
}
[Column("xtime")]
public long? XTime { get; set; }
[NotMapped]
public DateTime? ExportedAt {
get => XTime == null ? null : DateTimeOffset.FromUnixTimeSeconds(XTime.Value).LocalDateTime;
set => XTime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
}
[Column("itime")]
public long? ITime { get; set; }
[NotMapped]
public DateTime? ImportedAt {
get => ITime == null ? null : DateTimeOffset.FromUnixTimeSeconds(ITime.Value).LocalDateTime;
set => ITime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
}
[ForeignKey("Year, AvNr, MgNr")] [ForeignKey("Year, AvNr, MgNr")]
public virtual PaymentMember Payment { get; private set; } = null!; public virtual PaymentMember Payment { get; private set; } = null!;

@ -62,14 +62,36 @@ namespace Elwig.Models.Entities {
public string? Comment { get; set; } public string? Comment { get; set; }
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)] [Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long CTime { get; private set; } public long CTime { get; set; }
[NotMapped] [NotMapped]
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime; public DateTime CreatedAt {
get => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
set => CTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
}
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)] [Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long MTime { get; private set; } public long MTime { get; set; }
[NotMapped] [NotMapped]
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime; public DateTime ModifiedAt {
get => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
set => MTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
}
[Column("xtime")]
public long? XTime { get; set; }
[NotMapped]
public DateTime? ExportedAt {
get => XTime == null ? null : DateTimeOffset.FromUnixTimeSeconds(XTime.Value).LocalDateTime;
set => XTime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
}
[Column("itime")]
public long? ITime { get; set; }
[NotMapped]
public DateTime? ImportedAt {
get => ITime == null ? null : DateTimeOffset.FromUnixTimeSeconds(ITime.Value).LocalDateTime;
set => ITime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
}
[ForeignKey("Year")] [ForeignKey("Year")]
public virtual Season Season { get; private set; } = null!; public virtual Season Season { get; private set; } = null!;

@ -27,14 +27,36 @@ namespace Elwig.Models.Entities {
public required string Type { get; set; } public required string Type { get; set; }
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)] [Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long CTime { get; private set; } public long CTime { get; set; }
[NotMapped] [NotMapped]
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime; public DateTime CreatedAt {
get => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
set => CTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
}
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)] [Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long MTime { get; private set; } public long MTime { get; set; }
[NotMapped] [NotMapped]
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime; public DateTime ModifiedAt {
get => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
set => MTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
}
[Column("xtime")]
public long? XTime { get; set; }
[NotMapped]
public DateTime? ExportedAt {
get => XTime == null ? null : DateTimeOffset.FromUnixTimeSeconds(XTime.Value).LocalDateTime;
set => XTime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
}
[Column("itime")]
public long? ITime { get; set; }
[NotMapped]
public DateTime? ImportedAt {
get => ITime == null ? null : DateTimeOffset.FromUnixTimeSeconds(ITime.Value).LocalDateTime;
set => ITime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
}
[ForeignKey("Year, DsNr")] [ForeignKey("Year, DsNr")]
public virtual DeliverySchedule Schedule { get; private set; } = null!; public virtual DeliverySchedule Schedule { get; private set; } = null!;

@ -129,14 +129,36 @@ namespace Elwig.Models.Entities {
public string? Comment { get; set; } public string? Comment { get; set; }
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)] [Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long CTime { get; private set; } public long CTime { get; set; }
[NotMapped] [NotMapped]
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime; public DateTime CreatedAt {
get => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
set => CTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
}
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)] [Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long MTime { get; private set; } public long MTime { get; set; }
[NotMapped] [NotMapped]
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime; public DateTime ModifiedAt {
get => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
set => MTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
}
[Column("xtime")]
public long? XTime { get; set; }
[NotMapped]
public DateTime? ExportedAt {
get => XTime == null ? null : DateTimeOffset.FromUnixTimeSeconds(XTime.Value).LocalDateTime;
set => XTime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
}
[Column("itime")]
public long? ITime { get; set; }
[NotMapped]
public DateTime? ImportedAt {
get => ITime == null ? null : DateTimeOffset.FromUnixTimeSeconds(ITime.Value).LocalDateTime;
set => ITime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
}
[InverseProperty(nameof(DeliveryPartModifier.Part))] [InverseProperty(nameof(DeliveryPartModifier.Part))]
public virtual ICollection<DeliveryPartModifier> PartModifiers { get; private set; } = null!; public virtual ICollection<DeliveryPartModifier> PartModifiers { get; private set; } = null!;

@ -145,14 +145,36 @@ namespace Elwig.Models.Entities {
public AT_Kg? DefaultKg => DefaultWbKg?.AtKg; public AT_Kg? DefaultKg => DefaultWbKg?.AtKg;
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)] [Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long CTime { get; private set; } public long CTime { get; set; }
[NotMapped] [NotMapped]
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime; public DateTime CreatedAt {
get => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
set => CTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
}
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)] [Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long MTime { get; private set; } public long MTime { get; set; }
[NotMapped] [NotMapped]
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime; public DateTime ModifiedAt {
get => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
set => MTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
}
[Column("xtime")]
public long? XTime { get; set; }
[NotMapped]
public DateTime? ExportedAt {
get => XTime == null ? null : DateTimeOffset.FromUnixTimeSeconds(XTime.Value).LocalDateTime;
set => XTime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
}
[Column("itime")]
public long? ITime { get; set; }
[NotMapped]
public DateTime? ImportedAt {
get => ITime == null ? null : DateTimeOffset.FromUnixTimeSeconds(ITime.Value).LocalDateTime;
set => ITime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
}
[ForeignKey("ZwstId")] [ForeignKey("ZwstId")]
public virtual Branch? Branch { get; private set; } public virtual Branch? Branch { get; private set; }

@ -46,14 +46,36 @@ namespace Elwig.Models.Entities {
public required string Data { get; set; } public required string Data { get; set; }
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)] [Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long CTime { get; private set; } public long CTime { get; set; }
[NotMapped] [NotMapped]
public DateTime CreatedTimestamp => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime; public DateTime CreatedAt {
get => DateTimeOffset.FromUnixTimeSeconds(CTime).LocalDateTime;
set => CTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
}
[Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)] [Column("mtime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public long MTime { get; private set; } public long MTime { get; set; }
[NotMapped] [NotMapped]
public DateTime ModifiedTimestamp => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime; public DateTime ModifiedAt {
get => DateTimeOffset.FromUnixTimeSeconds(MTime).LocalDateTime;
set => MTime = ((DateTimeOffset)value.ToUniversalTime()).ToUnixTimeSeconds();
}
[Column("xtime")]
public long? XTime { get; set; }
[NotMapped]
public DateTime? ExportedAt {
get => XTime == null ? null : DateTimeOffset.FromUnixTimeSeconds(XTime.Value).LocalDateTime;
set => XTime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
}
[Column("itime")]
public long? ITime { get; set; }
[NotMapped]
public DateTime? ImportedAt {
get => ITime == null ? null : DateTimeOffset.FromUnixTimeSeconds(ITime.Value).LocalDateTime;
set => ITime = value == null ? null : ((DateTimeOffset)value.Value.ToUniversalTime()).ToUnixTimeSeconds();
}
[ForeignKey("Year")] [ForeignKey("Year")]
public virtual Season Season { get; private set; } = null!; public virtual Season Season { get; private set; } = null!;

@ -0,0 +1,33 @@
-- schema version 31 to 32
INSERT INTO client_parameter (param, value) VALUES ('ENABLE_TIME_TRIGGERS', '1');
ALTER TABLE member ADD COLUMN xtime INTEGER DEFAULT NULL;
ALTER TABLE member ADD COLUMN itime INTEGER DEFAULT NULL;
ALTER TABLE area_commitment ADD COLUMN xtime INTEGER DEFAULT NULL;
ALTER TABLE area_commitment ADD COLUMN itime INTEGER DEFAULT NULL;
ALTER TABLE delivery_announcement ADD COLUMN xtime INTEGER DEFAULT NULL;
ALTER TABLE delivery_announcement ADD COLUMN itime INTEGER DEFAULT NULL;
ALTER TABLE delivery ADD COLUMN xtime INTEGER DEFAULT NULL;
ALTER TABLE delivery ADD COLUMN itime INTEGER DEFAULT NULL;
ALTER TABLE delivery_part ADD COLUMN xtime INTEGER DEFAULT NULL;
ALTER TABLE delivery_part ADD COLUMN itime INTEGER DEFAULT NULL;
ALTER TABLE payment_variant ADD COLUMN xtime INTEGER DEFAULT NULL;
ALTER TABLE payment_variant ADD COLUMN itime INTEGER DEFAULT NULL;
ALTER TABLE credit ADD COLUMN xtime INTEGER DEFAULT NULL;
ALTER TABLE credit ADD COLUMN itime INTEGER DEFAULT NULL;
PRAGMA writable_schema = ON;
UPDATE sqlite_schema SET sql = REPLACE(REPLACE(sql,
' WHEN',
' WHEN (SELECT value FROM client_parameter WHERE param = ''ENABLE_TIME_TRIGGERS'') = 1 AND'),
'FOR EACH ROW' || char(10) ||
'BEGIN',
'FOR EACH ROW' || char(10) ||
' WHEN (SELECT value FROM client_parameter WHERE param = ''ENABLE_TIME_TRIGGERS'') = 1' || char(10) ||
'BEGIN')
WHERE type = 'trigger' AND name LIKE '%time%';
PRAGMA writable_schema = OFF;
PRAGMA schema_version = 3101;

@ -37,8 +37,8 @@ namespace Elwig.Services {
vm.DeliverySchedule = (DeliverySchedule?)ControlUtils.GetItemFromSourceWithPk(vm.DeliveryScheduleSource, a.Year, a.DsNr); vm.DeliverySchedule = (DeliverySchedule?)ControlUtils.GetItemFromSourceWithPk(vm.DeliveryScheduleSource, a.Year, a.DsNr);
vm.SortId = a.SortId; vm.SortId = a.SortId;
vm.Weight = a.Weight; vm.Weight = a.Weight;
vm.StatusAncmtCreated = $"{a.CreatedTimestamp:dd.MM.yyyy, HH:mm} ({a.Type})"; vm.StatusAncmtCreated = $"{a.CreatedAt:dd.MM.yyyy, HH:mm} ({a.Type})";
vm.StatusAncmtModified = a.ModifiedTimestamp != a.CreatedTimestamp ? $"{a.ModifiedTimestamp:dd.MM.yyyy, HH:mm}" : "-"; vm.StatusAncmtModified = a.ModifiedAt != a.CreatedAt ? $"{a.ModifiedAt:dd.MM.yyyy, HH:mm}" : "-";
} }
public static async Task<(List<string>, IQueryable<DeliveryAncmt>, List<string>)> GetFilters(this DeliveryAncmtAdminViewModel vm, AppDbContext ctx) { public static async Task<(List<string>, IQueryable<DeliveryAncmt>, List<string>)> GetFilters(this DeliveryAncmtAdminViewModel vm, AppDbContext ctx) {
@ -326,7 +326,7 @@ namespace Elwig.Services {
var weight = await deliveryAncmts.SumAsync(p => p.Weight); var weight = await deliveryAncmts.SumAsync(p => p.Weight);
text = $"{weight:N0} kg"; text = $"{weight:N0} kg";
AddToolTipRow(grid, 0, "Gewicht", null, weight, null, weight); AddToolTipRow(grid, 0, "Menge", null, weight, null, weight);
if (await deliveryAncmts.AnyAsync()) { if (await deliveryAncmts.AnyAsync()) {
var attrGroups = await deliveryAncmts var attrGroups = await deliveryAncmts

@ -946,7 +946,7 @@ namespace Elwig.Services {
var weight = await deliveryParts.SumAsync(p => p.Weight); var weight = await deliveryParts.SumAsync(p => p.Weight);
wText = $"{weight:N0} kg"; wText = $"{weight:N0} kg";
wGrid.Add(("Gewicht", null, weight, null, weight)); wGrid.Add(("Menge", null, weight, null, weight));
if (await deliveryParts.AnyAsync()) { if (await deliveryParts.AnyAsync()) {
var kmwMin = await deliveryParts.MinAsync(p => p.Kmw); var kmwMin = await deliveryParts.MinAsync(p => p.Kmw);

@ -1,20 +1,20 @@
using Elwig.Helpers;
using Elwig.Models.Entities;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System;
using Microsoft.EntityFrameworkCore;
using Elwig.Documents; using Elwig.Documents;
using System.Windows.Input; using Elwig.Helpers;
using System.Windows;
using Elwig.Helpers.Billing; using Elwig.Helpers.Billing;
using Elwig.Models.Dtos;
using Elwig.Helpers.Export; using Elwig.Helpers.Export;
using Microsoft.Win32; using Elwig.Models.Dtos;
using Elwig.Models.Entities;
using Elwig.ViewModels; using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace Elwig.Services { namespace Elwig.Services {
public static class MemberService { public static class MemberService {
@ -49,6 +49,11 @@ namespace Elwig.Services {
vm.StatusAreaCommitmentInfo = $"{Utils.CurrentLastSeason}"; vm.StatusAreaCommitmentInfo = $"{Utils.CurrentLastSeason}";
vm.StatusAreaCommitmentToolTip = null; vm.StatusAreaCommitmentToolTip = null;
vm.Age = "-"; vm.Age = "-";
vm.CreatedAt = "-";
vm.ModifiedAt = "-";
vm.ModifiedAtShort = "-";
vm.ExportedAt = "-";
vm.ImportedAt = "-";
} }
public static void FillInputs(this MemberAdminViewModel vm, Member m) { public static void FillInputs(this MemberAdminViewModel vm, Member m) {
@ -137,6 +142,12 @@ namespace Elwig.Services {
vm.ContactViaPost = m.ContactViaPost; vm.ContactViaPost = m.ContactViaPost;
vm.ContactViaEmail = m.ContactViaEmail; vm.ContactViaEmail = m.ContactViaEmail;
vm.CreatedAt = $"{m.CreatedAt:dd.MM.yyyy, HH:mm:ss}";
vm.ModifiedAt = $"{m.ModifiedAt:dd.MM.yyyy, HH:mm:ss}";
vm.ModifiedAtShort = $"{m.ModifiedAt:dd.MM.yyyy, HH:mm}";
vm.ExportedAt = m.ExportedAt == null ? "-" : $"{m.ExportedAt:dd.MM.yyyy, HH:mm:ss}";
vm.ImportedAt = m.ImportedAt == null ? "-" : $"{m.ImportedAt:dd.MM.yyyy, HH:mm:ss}";
vm.StatusDeliveriesLastSeasonInfo = $"{Utils.CurrentLastSeason - 1}"; vm.StatusDeliveriesLastSeasonInfo = $"{Utils.CurrentLastSeason - 1}";
vm.StatusDeliveriesLastSeason = "..."; vm.StatusDeliveriesLastSeason = "...";
vm.StatusDeliveriesLastSeasonToolTip = null; vm.StatusDeliveriesLastSeasonToolTip = null;
@ -401,7 +412,7 @@ namespace Elwig.Services {
try { try {
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
var v = (await ctx.PaymentVariants.FindAsync(year, avnr))!; var v = (await ctx.PaymentVariants.FindAsync(year, avnr))!;
var data = await CreditNoteDeliveryData.ForPaymentVariant(ctx.CreditNoteDeliveryRows, ctx.Seasons, year, avnr); var data = await CreditNoteDeliveryData.ForPaymentVariant(ctx.CreditNoteDeliveryRows, ctx.PaymentVariants, year, avnr);
var p = (await ctx.MemberPayments.FindAsync(year, avnr, m.MgNr))!; var p = (await ctx.MemberPayments.FindAsync(year, avnr, m.MgNr))!;
var b = BillingData.FromJson((await ctx.PaymentVariants.FindAsync(year, avnr))!.Data); var b = BillingData.FromJson((await ctx.PaymentVariants.FindAsync(year, avnr))!.Data);

@ -153,6 +153,17 @@ namespace Elwig.ViewModels {
[ObservableProperty] [ObservableProperty]
private bool _contactViaEmail; private bool _contactViaEmail;
[ObservableProperty]
private string _createdAt = "-";
[ObservableProperty]
private string _modifiedAt = "-";
[ObservableProperty]
private string _modifiedAtShort = "-";
[ObservableProperty]
private string _exportedAt = "-";
[ObservableProperty]
private string _importedAt = "-";
public ObservableCollection<string?> EmailAddresses { get; private set; } = [null, null, null, null, null, null, null, null, null]; public ObservableCollection<string?> EmailAddresses { get; private set; } = [null, null, null, null, null, null, null, null, null];
public partial class PhoneNr(int? type = null, string? number = null, string? comment = null) : ObservableObject { public partial class PhoneNr(int? type = null, string? number = null, string? comment = null) : ObservableObject {

@ -217,7 +217,7 @@
<Bold>Zweigstelle</Bold>: z.B. musterort, ...<LineBreak/> <Bold>Zweigstelle</Bold>: z.B. musterort, ...<LineBreak/>
<Bold>Attribut</Bold>: z.B. kabinett, !kabinett (alle außer kabinett), ...<LineBreak/> <Bold>Attribut</Bold>: z.B. kabinett, !kabinett (alle außer kabinett), ...<LineBreak/>
<Bold>Bewirtschaftung</Bold>: z.B. bio, !kip (alle außer KIP), ...<LineBreak/> <Bold>Bewirtschaftung</Bold>: z.B. bio, !kip (alle außer KIP), ...<LineBreak/>
<Bold>Gewicht</Bold>: z.B. &lt;500kg, &gt;6000kg, ... (gilt für Gewicht der gesamten Lieferung)<LineBreak/> <Bold>Gewicht</Bold>: z.B. &lt;500kg, &gt;6000kg, ... (gilt für Menge der gesamten Lieferung)<LineBreak/>
<Bold>Datum</Bold>: z.B. 1.9., 15.9.-10.10., -15.10.2020, ...<LineBreak/> <Bold>Datum</Bold>: z.B. 1.9., 15.9.-10.10., -15.10.2020, ...<LineBreak/>
<Bold>Uhrzeit</Bold>: z.B. 06:00-08:00, 18:00-, ...<LineBreak/> <Bold>Uhrzeit</Bold>: z.B. 06:00-08:00, 18:00-, ...<LineBreak/>
<Bold>Handwiegung</Bold>: handw[iegung], !Handw[iegung] (alle ohne Handwiegung)<LineBreak/> <Bold>Handwiegung</Bold>: handw[iegung], !Handw[iegung] (alle ohne Handwiegung)<LineBreak/>
@ -272,7 +272,7 @@
</Style> </Style>
</DataGridTextColumn.CellStyle> </DataGridTextColumn.CellStyle>
</DataGridTextColumn> </DataGridTextColumn>
<DataGridTextColumn Header="Gewicht" Binding="{Binding FilteredWeight, StringFormat='{}{0:N0} kg '}" Width="75"> <DataGridTextColumn Header="Menge" Binding="{Binding FilteredWeight, StringFormat='{}{0:N0} kg '}" Width="75">
<DataGridTextColumn.CellStyle> <DataGridTextColumn.CellStyle>
<Style> <Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/> <Setter Property="TextBlock.TextAlignment" Value="Right"/>
@ -516,7 +516,7 @@
</Grid> </Grid>
</GroupBox> </GroupBox>
<GroupBox Header="Gewicht" Grid.Column="1" Grid.Row="2" Margin="5,5,5,5"> <GroupBox Header="Menge" Grid.Column="1" Grid.Row="2" Margin="5,5,5,5">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/> <ColumnDefinition Width="70"/>
@ -669,7 +669,7 @@
<Separator Grid.Column="5"/> <Separator Grid.Column="5"/>
<StatusBarItem Grid.Column="6"> <StatusBarItem Grid.Column="6">
<TextBlock ToolTip="{Binding StatusWeightToolTip}"> <TextBlock ToolTip="{Binding StatusWeightToolTip}">
Gewicht: <Run Text="{Binding StatusWeight}"/> Menge: <Run Text="{Binding StatusWeight}"/>
</TextBlock> </TextBlock>
</StatusBarItem> </StatusBarItem>
<Separator Grid.Column="7"/> <Separator Grid.Column="7"/>

@ -125,7 +125,7 @@
<Bold>Zweigstelle</Bold>: z.B. musterort, ...<LineBreak/> <Bold>Zweigstelle</Bold>: z.B. musterort, ...<LineBreak/>
<Bold>Attribut</Bold>: z.B. kabinett, !kabinett (alle außer kabinett), ...<LineBreak/> <Bold>Attribut</Bold>: z.B. kabinett, !kabinett (alle außer kabinett), ...<LineBreak/>
<Bold>Bewirtschaftung</Bold>: z.B. bio, !kip (alle außer KIP), ...<LineBreak/> <Bold>Bewirtschaftung</Bold>: z.B. bio, !kip (alle außer KIP), ...<LineBreak/>
<Bold>Gewicht</Bold>: z.B. &lt;500kg, &gt;6000kg, ...<LineBreak/> <Bold>Menge</Bold>: z.B. &lt;500kg, &gt;6000kg, ...<LineBreak/>
<Bold>Datum</Bold>: z.B. 1.9., 15.9.-10.10., -15.10.2020, ... <Bold>Datum</Bold>: z.B. 1.9., 15.9.-10.10., -15.10.2020, ...
</TextBlock> </TextBlock>
</TextBox.ToolTip> </TextBox.ToolTip>
@ -191,7 +191,7 @@
</Style> </Style>
</DataGridTextColumn.CellStyle> </DataGridTextColumn.CellStyle>
</DataGridTextColumn> </DataGridTextColumn>
<DataGridTextColumn Header="Gewicht" Binding="{Binding Weight, StringFormat='{}{0:N0} kg'}" Width="75"> <DataGridTextColumn Header="Menge" Binding="{Binding Weight, StringFormat='{}{0:N0} kg'}" Width="75">
<DataGridTextColumn.CellStyle> <DataGridTextColumn.CellStyle>
<Style> <Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/> <Setter Property="TextBlock.TextAlignment" Value="Right"/>
@ -328,7 +328,7 @@
</ComboBox.ItemTemplateSelector> </ComboBox.ItemTemplateSelector>
</ComboBox> </ComboBox>
<Label Content="Gewicht:" Margin="10,70,0,0" Grid.Column="0"/> <Label Content="Menge:" Margin="10,70,0,0" Grid.Column="0"/>
<ctrl:UnitTextBox x:Name="WeightInput" Unit="kg" Text="{Binding WeightString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" <ctrl:UnitTextBox x:Name="WeightInput" Unit="kg" Text="{Binding WeightString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="1" Margin="0,70,10,10" Width="61" HorizontalAlignment="Left" Grid.Column="1" Margin="0,70,10,10" Width="61" HorizontalAlignment="Left"
TextChanged="WeightInput_TextChanged" KeyUp="Input_KeyUp"/> TextChanged="WeightInput_TextChanged" KeyUp="Input_KeyUp"/>
@ -364,7 +364,7 @@
<Separator Grid.Column="1"/> <Separator Grid.Column="1"/>
<StatusBarItem Grid.Column="2"> <StatusBarItem Grid.Column="2">
<TextBlock ToolTip="{Binding StatusWeightToolTip}"> <TextBlock ToolTip="{Binding StatusWeightToolTip}">
Gewicht: <Run Text="{Binding StatusWeight}"/> Menge: <Run Text="{Binding StatusWeight}"/>
</TextBlock> </TextBlock>
</StatusBarItem> </StatusBarItem>
<Separator Grid.Column="3"/> <Separator Grid.Column="3"/>

@ -127,7 +127,7 @@
</DataGridTextColumn.CellStyle> </DataGridTextColumn.CellStyle>
</DataGridTextColumn> </DataGridTextColumn>
<DataGridTextColumn Header="Beschreibung" Binding="{Binding Description}" Width="200"/> <DataGridTextColumn Header="Beschreibung" Binding="{Binding Description}" Width="200"/>
<DataGridTextColumn Header="Max. Gew." Binding="{Binding MaxWeight, StringFormat='{}{0:N0} kg'}" Width="80"> <DataGridTextColumn Header="Max. Mg." Binding="{Binding MaxWeight, StringFormat='{}{0:N0} kg'}" Width="80">
<DataGridTextColumn.CellStyle> <DataGridTextColumn.CellStyle>
<Style> <Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/> <Setter Property="TextBlock.TextAlignment" Value="Right"/>
@ -211,7 +211,7 @@
Margin="0,40,10,10" Grid.Column="1" Grid.ColumnSpan="2" Margin="0,40,10,10" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<Label Content="Max. Gewicht:" Margin="10,70,0,10"/> <Label Content="Max. Menge:" Margin="10,70,0,10"/>
<ctrl:UnitTextBox x:Name="MaxWeightInput" Unit="kg" Text="{Binding MaxWeightString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" <ctrl:UnitTextBox x:Name="MaxWeightInput" Unit="kg" Text="{Binding MaxWeightString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,70,10,10" Grid.Column="1" Grid.ColumnSpan="2" Width="68" HorizontalAlignment="Left" Margin="0,70,10,10" Grid.Column="1" Grid.ColumnSpan="2" Width="68" HorizontalAlignment="Left"
TextChanged="MaxWeightInput_TextChanged"/> TextChanged="MaxWeightInput_TextChanged"/>

@ -671,7 +671,7 @@ namespace Elwig.Windows {
var avnr = details.Item2; var avnr = details.Item2;
try { try {
cnData[(year, avnr)] = ( cnData[(year, avnr)] = (
await CreditNoteDeliveryData.ForPaymentVariant(ctx.CreditNoteDeliveryRows, ctx.Seasons, year, avnr), await CreditNoteDeliveryData.ForPaymentVariant(ctx.CreditNoteDeliveryRows, ctx.PaymentVariants, year, avnr),
await ctx.MemberPayments.Where(p => p.Year == year && p.AvNr == avnr).ToDictionaryAsync(c => c.MgNr), await ctx.MemberPayments.Where(p => p.Year == year && p.AvNr == avnr).ToDictionaryAsync(c => c.MgNr),
BillingData.FromJson((await ctx.PaymentVariants.FindAsync(year, avnr))!.Data) BillingData.FromJson((await ctx.PaymentVariants.FindAsync(year, avnr))!.Data)
); );

@ -207,7 +207,7 @@
<Button x:Name="BreakdownButton" <Button x:Name="BreakdownButton"
Click="BreakdownButton_Click" Click="BreakdownButton_Click"
Margin="195,90,0,10" Width="190" Padding="3,5,5,5" Margin="195,90,0,10" Width="190" Padding="3,5,5,5"
ToolTip="Aufschlüsselung des Gewichts nach Zweigstelle, Mitglied, Sorte, Attribut/Bewirt., Qualitätsstufe, gebunden/ungebunden"> ToolTip="Aufschlüsselung der Menge nach Zweigstelle, Mitglied, Sorte, Attribut/Bewirt., Qualitätsstufe, gebunden/ungebunden">
<Grid> <Grid>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE9F9;" <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE9F9;"
TextAlignment="Left" HorizontalAlignment="Left" Padding="6.5,0.5,0,0"/> TextAlignment="Left" HorizontalAlignment="Left" Padding="6.5,0.5,0,0"/>
@ -252,7 +252,7 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="1" TextAlignment="Right"><Bold>Mitglieder</Bold></TextBlock> <TextBlock Grid.Row="0" Grid.Column="1" TextAlignment="Right"><Bold>Mitglieder</Bold></TextBlock>
<TextBlock Grid.Row="0" Grid.Column="2" TextAlignment="Right"><Bold>Gewicht</Bold></TextBlock> <TextBlock Grid.Row="0" Grid.Column="2" TextAlignment="Right"><Bold>Menge</Bold></TextBlock>
<TextBlock Grid.Row="0" Grid.Column="3" TextAlignment="Right"><Bold>Fläche</Bold></TextBlock> <TextBlock Grid.Row="0" Grid.Column="3" TextAlignment="Right"><Bold>Fläche</Bold></TextBlock>
<TextBlock Grid.Row="1" Grid.Column="0">Gesamt:</TextBlock> <TextBlock Grid.Row="1" Grid.Column="0">Gesamt:</TextBlock>

@ -212,7 +212,7 @@ namespace Elwig.Windows {
} catch (HttpRequestException exc) { } catch (HttpRequestException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
} catch (TaskCanceledException exc) { } catch (TaskCanceledException exc) {
MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
} catch (Exception exc) { } catch (Exception exc) {
MessageBox.Show(exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error); MessageBox.Show(exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error);
} }

@ -653,13 +653,40 @@
TextChanged="TextBox_TextChanged" TextChanged="TextBox_TextChanged"
VerticalAlignment="Stretch" Height="auto" AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible"/> VerticalAlignment="Stretch" Height="auto" AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible"/>
<Label Content="Kontaktart:" Margin="10,10,0,10" Grid.Column="0" VerticalAlignment="Bottom"/> <Label Content="Kontaktart:" Margin="10,10,0,40" Grid.Column="0" VerticalAlignment="Bottom"/>
<CheckBox x:Name="ContactPostalInput" Content="Post" IsChecked="{Binding ContactViaPost, Mode=TwoWay}" IsEnabled="False" <CheckBox x:Name="ContactPostalInput" Content="Post" IsChecked="{Binding ContactViaPost, Mode=TwoWay}" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed" Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
HorizontalAlignment="Left" Margin="0,0,0,15" VerticalAlignment="Bottom" Grid.Column="1" Grid.ColumnSpan="2"/> HorizontalAlignment="Left" Margin="0,0,0,45" VerticalAlignment="Bottom" Grid.Column="1" Grid.ColumnSpan="2"/>
<CheckBox x:Name="ContactEmailInput" Content="E-Mail" IsChecked="{Binding ContactViaEmail, Mode=TwoWay}" IsEnabled="False" <CheckBox x:Name="ContactEmailInput" Content="E-Mail" IsChecked="{Binding ContactViaEmail, Mode=TwoWay}" IsEnabled="False"
Checked="ContactEmailInput_Changed" Unchecked="ContactEmailInput_Changed" Checked="ContactEmailInput_Changed" Unchecked="ContactEmailInput_Changed"
HorizontalAlignment="Left" Margin="60,0,0,15" VerticalAlignment="Bottom" Grid.Column="1" Grid.ColumnSpan="2"/> HorizontalAlignment="Left" Margin="60,0,0,45" VerticalAlignment="Bottom" Grid.Column="1" Grid.ColumnSpan="2"/>
<Label Content="Zuletzt geändert:" Margin="10,10,0,10" Grid.Column="0" VerticalAlignment="Bottom"/>
<TextBlock x:Name="LastModified" Text="{Binding ModifiedAtShort}"
Margin="0,10,0,15" Grid.Column="1" TextWrapping="NoWrap" VerticalAlignment="Bottom">
<TextBlock.ToolTip>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="110"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Erstellt:"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding CreatedAt}"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Zuletzt geändert:"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding ModifiedAt}"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Zuletzt von diesem Gerät exportiert:"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding ExportedAt}"/>
<TextBlock Grid.Row="3" Grid.Column="0" Text="Zuletzt auf dieses Gerät importiert:"/>
<TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding ImportedAt}"/>
</Grid>
</TextBlock.ToolTip>
</TextBlock>
<Button x:Name="DeliveryButton" Content="Lieferungen" IsEnabled="{Binding IsMemberSelected}" <Button x:Name="DeliveryButton" Content="Lieferungen" IsEnabled="{Binding IsMemberSelected}"
Click="DeliveryButton_Click" Click="DeliveryButton_Click"

@ -6,7 +6,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows" xmlns:local="clr-namespace:Elwig.Windows"
xmlns:ctrl="clr-namespace:Elwig.Controls" xmlns:ctrl="clr-namespace:Elwig.Controls"
Title="Auszahlungsvarianten - Elwig" Height="480" Width="850" MinHeight="400" MinWidth="830"> Title="Auszahlungsvarianten - Elwig" Height="480" Width="850" MinHeight="400" MinWidth="850">
<Window.Resources> <Window.Resources>
<Style TargetType="Label"> <Style TargetType="Label">
<Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="HorizontalAlignment" Value="Left"/>
@ -160,7 +160,7 @@
</TextBlock> </TextBlock>
<Label Content="Berücksichtigen:" Margin="90,70,10,10" Grid.Column="1"/> <Label Content="Berücksichtigen:" Margin="90,70,10,10" Grid.Column="1"/>
<CheckBox x:Name="ConsiderModifiersInput" Content="Zu-/Abschläge bei Lieferungen" <CheckBox x:Name="ConsiderModifiersInput" Content="Zu-/Abschläge bei Lieferungen (inkl. Rebelzuschl.)"
Margin="110,95,10,10" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="110,95,10,10" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top"
Checked="ConsiderModifiersInput_Changed" Unchecked="ConsiderModifiersInput_Changed"/> Checked="ConsiderModifiersInput_Changed" Unchecked="ConsiderModifiersInput_Changed"/>
<CheckBox x:Name="ConsiderPenaltiesInput" Content="Pönalen bei Unterlieferungen (FB)" <CheckBox x:Name="ConsiderPenaltiesInput" Content="Pönalen bei Unterlieferungen (FB)"

@ -1,5 +0,0 @@
::mkdir "C:\Program Files\Elwig"
mkdir "C:\ProgramData\Elwig\resources"
copy /b /y Documents\*.css "C:\ProgramData\Elwig\resources"
copy /b /y Documents\*.cshtml "C:\ProgramData\Elwig\resources"
::copy /b /y ..\Installer\Files\*.exe "C:\Program Files\Elwig\"

@ -1,23 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Fragment> <Fragment>
<!-- C:\Program Files (x86)\Elwig oder C:\Program Files\Elwig --> <!-- C:\Program Files (x86)\Elwig oder C:\Program Files\Elwig -->
<StandardDirectory Id="ProgramFiles64Folder"> <StandardDirectory Id="ProgramFiles64Folder">
<Directory Id="InstallFolder" Name="!(bind.Property.ProductName)" /> <Directory Id="InstallFolder" Name="!(bind.Property.ProductName)">
<Directory Id="InstallFolderResources" Name="resources">
<Directory Id="InstallFolderDocuments" Name="Documents" />
</Directory>
</Directory>
</StandardDirectory> </StandardDirectory>
<!-- C:\ProgramData\Elwig --> <!-- C:\ProgramData\Elwig -->
<StandardDirectory Id="CommonAppDataFolder"> <StandardDirectory Id="CommonAppDataFolder">
<Directory Id="ConfigFolder" Name="!(bind.Property.ProductName)"> <Directory Id="ConfigFolder" Name="!(bind.Property.ProductName)" />
<Directory Id="ConfigFolderResources" Name="resources" /> </StandardDirectory>
</Directory>
</StandardDirectory>
<!-- C:\ProgramData\Microsoft\Windows\Start Menu\Programs --> <!-- C:\ProgramData\Microsoft\Windows\Start Menu\Programs -->
<StandardDirectory Id="ProgramMenuFolder"> <StandardDirectory Id="ProgramMenuFolder">
<Directory Id="StartMenuProgramsFolder" Name="!(bind.Property.ProductName)" /> <Directory Id="StartMenuProgramsFolder" Name="!(bind.Property.ProductName)" />
</StandardDirectory> </StandardDirectory>
<!-- C:\Users{USERNAME}\Desktop --> <!-- C:\Users\{USERNAME}\Desktop -->
<StandardDirectory Id="DesktopFolder" /> <StandardDirectory Id="DesktopFolder" />
</Fragment> </Fragment>
</Wix> </Wix>

@ -1,14 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Fragment> <Fragment>
<ComponentGroup Id="HeatComponents"> <ComponentGroup Id="HeatComponents">
<Files Directory="InstallFolder" Include="..\Elwig\bin\Publish\**"> <Files Directory="InstallFolder" Include="..\Elwig\bin\Publish\**">
<Exclude Files="..\Elwig\bin\Publish\**.exe" /> <Exclude Files="..\Elwig\bin\Publish\**.exe" />
<Exclude Files="..\Elwig\bin\Publish\**.pdb" /> <Exclude Files="..\Elwig\bin\Publish\**.pdb" />
</Files> </Files>
<Files Directory="ConfigFolderResources" Include="..\Elwig\Documents\**"> <Files Directory="InstallFolderDocuments" Include="..\Elwig\Documents\**">
<Exclude Files="..\Elwig\Documents\**.cs" /> <Exclude Files="..\Elwig\Documents\**.cs" />
</Files> </Files>
</ComponentGroup> </ComponentGroup>
</Fragment> </Fragment>
</Wix> </Wix>

@ -1,28 +0,0 @@
using Elwig.Documents;
using Elwig.Helpers;
using Elwig.Models.Dtos;
namespace Tests.DocumentTests {
[TestFixture]
public class PaymentVariantSummaryTest {
[Test]
public async Task Test_01_PaymentVariant2020() {
using var ctx = new AppDbContext();
var v = (await ctx.PaymentVariants.FindAsync(2020, 1))!;
var data = await PaymentVariantSummaryData.ForPaymentVariant(v, ctx.PaymentVariantSummaryRows);
using var doc = new PaymentVariantSummary(v, data);
var text = await Utils.GeneratePdfText(doc);
Assert.Multiple(() => {
Assert.That(text, Contains.Substring("Auszahlungsvariante"));
Assert.That(text, Contains.Substring(v.Name));
Assert.That(text, Contains.Substring("""
Gradation ungebunden gebunden Gesamt
[°Oe] [kg] [/kg] [kg] [/kg] []
Grüner Veltliner 3 219 0 1 609,50
Qualitätswein 73 3 219 0,5000 - - 1 609,50
"""));
});
}
}
}

@ -1,6 +1,6 @@
using OpenQA.Selenium; using OpenQA.Selenium;
using OpenQA.Selenium.Appium.Windows; using OpenQA.Selenium.Appium.Windows;
using Tests.WeighingTests; using Tests.UnitTests.WeighingTests;
namespace Tests.E2ETests { namespace Tests.E2ETests {
[TestFixture, Order(2)] [TestFixture, Order(2)]

@ -3,7 +3,7 @@ using Elwig.Helpers;
using Elwig.Models.Dtos; using Elwig.Models.Dtos;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace Tests.DocumentTests { namespace Tests.UnitTests.DocumentTests {
[TestFixture] [TestFixture]
public class CreditNoteTest { public class CreditNoteTest {
@ -12,7 +12,7 @@ namespace Tests.DocumentTests {
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
var m = await ctx.Members.FindAsync(101); var m = await ctx.Members.FindAsync(101);
var p = await ctx.MemberPayments.Where(p => p.Year == 2020 && p.AvNr == 1).SingleAsync(); var p = await ctx.MemberPayments.Where(p => p.Year == 2020 && p.AvNr == 1).SingleAsync();
var data = await CreditNoteDeliveryData.ForPaymentVariant(ctx.CreditNoteDeliveryRows, ctx.Seasons, 2020, 1); var data = await CreditNoteDeliveryData.ForPaymentVariant(ctx.CreditNoteDeliveryRows, ctx.PaymentVariants, 2020, 1);
using var doc = new CreditNote(ctx, p, data[m!.MgNr], false, false, false, false, using var doc = new CreditNote(ctx, p, data[m!.MgNr], false, false, false, false,
ctx.GetMemberUnderDelivery(2020, m!.MgNr).GetAwaiter().GetResult()); ctx.GetMemberUnderDelivery(2020, m!.MgNr).GetAwaiter().GetResult());
var text = await Utils.GeneratePdfText(doc); var text = await Utils.GeneratePdfText(doc);

@ -2,7 +2,7 @@ using Elwig.Documents;
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Models.Dtos; using Elwig.Models.Dtos;
namespace Tests.DocumentTests { namespace Tests.UnitTests.DocumentTests {
[TestFixture] [TestFixture]
public class DeliveryAncmtListTest { public class DeliveryAncmtListTest {

@ -2,7 +2,7 @@ using Elwig.Documents;
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Models.Dtos; using Elwig.Models.Dtos;
namespace Tests.DocumentTests { namespace Tests.UnitTests.DocumentTests {
[TestFixture] [TestFixture]
public class DeliveryConfirmationTest { public class DeliveryConfirmationTest {

@ -2,7 +2,7 @@ using Elwig.Documents;
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Models.Dtos; using Elwig.Models.Dtos;
namespace Tests.DocumentTests { namespace Tests.UnitTests.DocumentTests {
[TestFixture] [TestFixture]
public class DeliveryDepreciationListTest { public class DeliveryDepreciationListTest {

@ -2,7 +2,7 @@ using Elwig.Documents;
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Models.Dtos; using Elwig.Models.Dtos;
namespace Tests.DocumentTests { namespace Tests.UnitTests.DocumentTests {
[TestFixture] [TestFixture]
public class DeliveryJournalTest { public class DeliveryJournalTest {

@ -1,7 +1,7 @@
using Elwig.Documents; using Elwig.Documents;
using Elwig.Helpers; using Elwig.Helpers;
namespace Tests.DocumentTests { namespace Tests.UnitTests.DocumentTests {
[TestFixture] [TestFixture]
public class DeliveryNoteTest { public class DeliveryNoteTest {

@ -1,7 +1,7 @@
using Elwig.Documents; using Elwig.Documents;
using Elwig.Helpers; using Elwig.Helpers;
namespace Tests.DocumentTests { namespace Tests.UnitTests.DocumentTests {
[TestFixture] [TestFixture]
public class LetterheadTest { public class LetterheadTest {

@ -1,7 +1,7 @@
using Elwig.Documents; using Elwig.Documents;
using Elwig.Helpers; using Elwig.Helpers;
namespace Tests.DocumentTests { namespace Tests.UnitTests.DocumentTests {
[TestFixture] [TestFixture]
public class MemberDataSheetTest { public class MemberDataSheetTest {

@ -2,7 +2,7 @@ using Elwig.Documents;
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Models.Dtos; using Elwig.Models.Dtos;
namespace Tests.DocumentTests { namespace Tests.UnitTests.DocumentTests {
[TestFixture] [TestFixture]
public class MemberListTest { public class MemberListTest {

@ -0,0 +1,29 @@
using Elwig.Documents;
using Elwig.Helpers;
using Elwig.Models.Dtos;
namespace Tests.UnitTests.DocumentTests {
[TestFixture]
public class PaymentVariantSummaryTest {
[Test]
public async Task Test_01_PaymentVariant2020() {
using var ctx = new AppDbContext();
var v = (await ctx.PaymentVariants.FindAsync(2020, 1))!;
var data = await PaymentVariantSummaryData.ForPaymentVariant(v, ctx.PaymentVariantSummaryRows);
using var doc = new PaymentVariantSummary(v, data);
var text = await Utils.GeneratePdfText(doc, true);
var table = Utils.ExtractTable(text);
Assert.Multiple(() => {
Assert.That(text, Contains.Substring("Auszahlungsvariante"));
Assert.That(text, Contains.Substring(v.Name));
Assert.That(table.Skip(17).ToArray(), Is.EqualTo(new string[][] {
[ "Gradation", "ungebunden", "attributlos gebunden", "gebunden", "Gesamt" ],
[ "[°Oe]", "[kg]", "[/kg]", "[kg]", "[/kg]", "[kg]", "[/kg]", "[]" ],
["Grüner Veltliner", "3 219", "0", "0", "1 609,50"],
["Qualitätswein", "73", "3 219", "0,5000", "-", "-", "-", "-", "1 609,50"]
}));
});
}
}
}

@ -3,7 +3,7 @@ using System.Reflection;
using Microsoft.Data.Sqlite; using Microsoft.Data.Sqlite;
using Elwig.Helpers.Printing; using Elwig.Helpers.Printing;
namespace Tests.DocumentTests { namespace Tests.UnitTests.DocumentTests {
[SetUpFixture] [SetUpFixture]
public class Setup { public class Setup {

@ -2,7 +2,7 @@ using Elwig.Documents;
using NReco.PdfRenderer; using NReco.PdfRenderer;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace Tests.DocumentTests { namespace Tests.UnitTests.DocumentTests {
public static class Utils { public static class Utils {
private static readonly string FileName = Path.Combine(Path.GetTempPath(), "test_document.pdf"); private static readonly string FileName = Path.Combine(Path.GetTempPath(), "test_document.pdf");

@ -2,7 +2,7 @@ using Elwig.Documents;
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Models.Dtos; using Elwig.Models.Dtos;
namespace Tests.DocumentTests { namespace Tests.UnitTests.DocumentTests {
[TestFixture] [TestFixture]
public class WineQualityStatisticsTest { public class WineQualityStatisticsTest {

@ -2,7 +2,7 @@ using Elwig.Helpers;
using Elwig.Helpers.Billing; using Elwig.Helpers.Billing;
using System.Text.Json; using System.Text.Json;
namespace Tests.HelperTests { namespace Tests.UnitTests.HelperTests {
[TestFixture] [TestFixture]
public class BillingDataTest { public class BillingDataTest {

@ -3,7 +3,7 @@ using Elwig.Helpers.Billing;
using Microsoft.Data.Sqlite; using Microsoft.Data.Sqlite;
using System.Reflection; using System.Reflection;
namespace Tests.HelperTests { namespace Tests.UnitTests.HelperTests {
[TestFixture] [TestFixture]
public class BillingTest { public class BillingTest {

@ -5,7 +5,7 @@ using Elwig.Models.Entities;
using System.Reflection; using System.Reflection;
using System.Xml; using System.Xml;
namespace Tests.HelperTests { namespace Tests.UnitTests.HelperTests {
// see https://www.iso20022.org/iso-20022-message-definitions // see https://www.iso20022.org/iso-20022-message-definitions
// and https://www.iso20022.org/catalogue-messages/iso-20022-messages-archive?search=pain // and https://www.iso20022.org/catalogue-messages/iso-20022-messages-archive?search=pain
[TestFixture] [TestFixture]

@ -1,6 +1,6 @@
using Elwig.Helpers; using Elwig.Helpers;
namespace Tests.HelperTests { namespace Tests.UnitTests.HelperTests {
[TestFixture] [TestFixture]
public class UtilsTest { public class UtilsTest {

@ -1,7 +1,7 @@
using Elwig.Helpers; using Elwig.Helpers;
using System.Windows.Controls; using System.Windows.Controls;
namespace Tests.HelperTests { namespace Tests.UnitTests.HelperTests {
[TestFixture] [TestFixture]
[Apartment(ApartmentState.STA)] [Apartment(ApartmentState.STA)]
public class ValidatorTest { public class ValidatorTest {

@ -4,7 +4,7 @@ using Elwig.Services;
using Elwig.ViewModels; using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace Tests.ServiceTests { namespace Tests.UnitTests.ServiceTests {
[TestFixture] [TestFixture]
public class DeliveryServiceTest { public class DeliveryServiceTest {

@ -4,7 +4,7 @@ using Elwig.Services;
using Elwig.ViewModels; using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace Tests.ServiceTests { namespace Tests.UnitTests.ServiceTests {
[TestFixture] [TestFixture]
public class MemberServiceTest { public class MemberServiceTest {

@ -2,7 +2,7 @@ using Elwig.Helpers;
using System.Reflection; using System.Reflection;
using Microsoft.Data.Sqlite; using Microsoft.Data.Sqlite;
namespace Tests.ServiceTests { namespace Tests.UnitTests.ServiceTests {
[SetUpFixture] [SetUpFixture]
public class Setup { public class Setup {

@ -2,7 +2,7 @@ using System.Net.Sockets;
using System.Net; using System.Net;
using System.Text; using System.Text;
namespace Tests.WeighingTests { namespace Tests.UnitTests.WeighingTests {
public abstract class MockScale : IDisposable { public abstract class MockScale : IDisposable {
protected readonly TcpListener Server; protected readonly TcpListener Server;

@ -1,4 +1,4 @@
namespace Tests.WeighingTests { namespace Tests.UnitTests.WeighingTests {
public static class ScaleHandlers { public static class ScaleHandlers {
public static (string, bool) Handle_IT3000A(string req, int weight, int tare, string? error, int identNr) { public static (string, bool) Handle_IT3000A(string req, int weight, int tare, string? error, int identNr) {

@ -1,6 +1,6 @@
using Elwig.Helpers.Weighing; using Elwig.Helpers.Weighing;
namespace Tests.WeighingTests { namespace Tests.UnitTests.WeighingTests {
[TestFixture] [TestFixture]
public class ScaleTestBadenL320 { public class ScaleTestBadenL320 {

@ -1,6 +1,6 @@
using Elwig.Helpers.Weighing; using Elwig.Helpers.Weighing;
namespace Tests.WeighingTests { namespace Tests.UnitTests.WeighingTests {
[TestFixture] [TestFixture]
class ScaleTestGrInzersdorfL246 { class ScaleTestGrInzersdorfL246 {

@ -1,6 +1,6 @@
using Elwig.Helpers.Weighing; using Elwig.Helpers.Weighing;
namespace Tests.WeighingTests { namespace Tests.UnitTests.WeighingTests {
[TestFixture] [TestFixture]
public class ScaleTestHaugsdorfDMA02 { public class ScaleTestHaugsdorfDMA02 {

@ -1,6 +1,6 @@
using Elwig.Helpers.Weighing; using Elwig.Helpers.Weighing;
namespace Tests.WeighingTests { namespace Tests.UnitTests.WeighingTests {
[TestFixture] [TestFixture]
class ScaleTestMatzenIT3000A { class ScaleTestMatzenIT3000A {

@ -1,6 +1,6 @@
using Elwig.Helpers.Weighing; using Elwig.Helpers.Weighing;
namespace Tests.WeighingTests { namespace Tests.UnitTests.WeighingTests {
[TestFixture] [TestFixture]
public class ScaleTestSitzendorfDMA03Baby { public class ScaleTestSitzendorfDMA03Baby {

@ -1,6 +1,6 @@
using Elwig.Helpers.Weighing; using Elwig.Helpers.Weighing;
namespace Tests.WeighingTests { namespace Tests.UnitTests.WeighingTests {
[TestFixture] [TestFixture]
class ScaleTestWolkersdorfIT6000E { class ScaleTestWolkersdorfIT6000E {

@ -1,4 +1,4 @@
namespace Tests.WeighingTests { namespace Tests.UnitTests.WeighingTests {
public static class Utils { public static class Utils {
public static DateOnly Today => DateOnly.FromDateTime(DateTime.Now); public static DateOnly Today => DateOnly.FromDateTime(DateTime.Now);
public static TimeOnly Time => new(DateTime.Now.Hour, DateTime.Now.Minute); public static TimeOnly Time => new(DateTime.Now.Hour, DateTime.Now.Minute);

@ -1 +1 @@
curl --fail -s -L "https://elwig.at/files/create.sql?v=31" -u "elwig:ganzGeheim123!" -o "Resources\Sql\Create.sql" curl --fail -s -L "https://elwig.at/files/create.sql?v=32" -u "elwig:ganzGeheim123!" -o "Resources\Sql\Create.sql"