Compare commits

...

51 Commits

Author SHA1 Message Date
lorenz.stechauner 42bf01656e Some Bugfixes 2024-01-19 00:22:49 +01:00
lorenz.stechauner 51293baaae App: Fix GroupSeparator bug 2024-01-18 23:48:42 +01:00
lorenz.stechauner 1d1398a9cd Config: Use Path.Combine() instead of GetAbsolutePath() 2024-01-18 22:07:31 +01:00
lorenz.stechauner 7d199282d0 Elwig: Upgrade publish profile to .Net8 2024-01-18 21:48:11 +01:00
lorenz.stechauner b56a5ed5c6 Setup: Update dependencies 2024-01-18 21:44:28 +01:00
lorenz.stechauner 201b63c2f1 Installer: Update dependencies 2024-01-18 21:42:34 +01:00
lorenz.stechauner b2bd0c9a21 Tests: Update dependencies 2024-01-18 21:42:07 +01:00
lorenz.stechauner 8502afdc9a Elwig: Update dependencies (except ScottPlot) 2024-01-18 21:38:50 +01:00
lorenz.stechauner cb541cb6e6 Billing: Add EditBillingData class 2024-01-18 21:30:42 +01:00
lorenz.stechauner 403e7723d2 Bump version to 0.6.0 2024-01-18 21:26:33 +01:00
lorenz.stechauner 8fbce03031 Tests: Adapt to new PaymentBillingData class usage 2024-01-18 21:23:10 +01:00
lorenz.stechauner b32a935150 DeliveryAdminWindow: Add filter for red/white 2024-01-18 20:02:16 +01:00
lorenz.stechauner 337bfa89d9 DeliveryAdminWindow: Fix filters for quality and attributes 2024-01-18 19:49:43 +01:00
lorenz.stechauner f886888ccc Billing: Split BillingData into BillingData and PaymentBillingData 2024-01-18 01:22:12 +01:00
thomas.hilscher 4dd036babd ChartWindow: wip 2024-01-17 22:33:20 +01:00
thomas.hilscher b6fd62f8ca ChartWindow: Use BillingData and Curve 2024-01-17 22:08:36 +01:00
lorenz.stechauner b52c09a176 Billing: Add possibility to automatically add business shares 2024-01-17 18:59:25 +01:00
lorenz.stechauner 668eb9a2d0 BillingData: Fix setter for ConsiderAutoBusinessShares 2024-01-17 17:37:08 +01:00
lorenz.stechauner 9eb013ce11 PaymentVariantsWindow: Add possibility to switch options on/off 2024-01-17 14:57:45 +01:00
lorenz.stechauner 38ad433b4e Fix ods export with doubles 2024-01-17 12:10:11 +01:00
lorenz.stechauner 0a60f01979 MemberDataSheet: Fix constructor 2024-01-14 21:41:53 +01:00
lorenz.stechauner a1ddef4666 MemberAdminWindow: Fix ToolTip for search bar 2024-01-14 21:40:23 +01:00
lorenz.stechauner 788d0efa4a CreditNote: Fix double-border for sum without Treuebonus 2024-01-14 10:15:29 +01:00
lorenz.stechauner 0f06d98d39 [#9] MemberAdminWindow: Add "Bio-Knopf" 2024-01-14 01:26:35 +01:00
lorenz.stechauner 228d17f8cb MemberAdminWindow: Enlarge comment field 2024-01-14 00:31:45 +01:00
lorenz.stechauner 1664024e64 [#2] MemberAdminWindow: Add search ToolTip 2024-01-13 20:38:51 +01:00
lorenz.stechauner 8fbfd33e8f DeliveryAdminWindow: Update search ToolTip 2024-01-13 20:38:49 +01:00
lorenz.stechauner 95853099bb [#2] MemberAdminWindow: Update search filters 2024-01-13 20:38:36 +01:00
lorenz.stechauner 8072febd5b DeliveryAdminWindow: Add branch name in title for übernahme mode 2024-01-13 19:40:00 +01:00
lorenz.stechauner 62d9641b28 Tests/DatabaseSetup: Add Insert.sql 2024-01-09 13:10:06 +01:00
lorenz.stechauner 3aabfbc603 PaymentVariantsWindow: Implement SeasonLock 2024-01-08 20:08:07 +01:00
lorenz.stechauner 09e55264bb BillingData: Implement WG Master parsing 2024-01-08 19:31:13 +01:00
lorenz.stechauner f894c3b212 PaymentVariantsWindow: Rework Buttons 2024-01-08 14:20:20 +01:00
lorenz.stechauner 09a7889044 AppDbContext: Add NextAvNr() 2024-01-08 14:18:15 +01:00
lorenz.stechauner d77aac43ec App: Add FocusBaseDataSeason() 2024-01-08 14:17:56 +01:00
lorenz.stechauner 062c7bed5e AppDbUpdater: Fix migration 10 to 11 by creating a new table 2024-01-08 11:21:01 +01:00
lorenz.stechauner b723161fa5 BillingVariant: Fix CalculateModifiers() by restricting modifier year 2024-01-08 02:40:32 +01:00
lorenz.stechauner debee3b4bf PaymentVariantsWindow: Fix once wrong, every time wrong error 2024-01-08 02:24:19 +01:00
lorenz.stechauner f15733f827 CreditNote: Fix styling 2024-01-07 23:43:56 +01:00
lorenz.stechauner 05b6e8ddd6 CreditNote: Add hint regarding calculations 2024-01-07 22:24:06 +01:00
lorenz.stechauner 5ae326074f Document: Fix GenerationProportion 2024-01-07 22:08:21 +01:00
lorenz.stechauner 60b99bf95b CreditNote: Split table into two tables 2024-01-07 21:47:25 +01:00
lorenz.stechauner 3562d304de Document: Remove break-before: avoid for tr.sum 2024-01-07 21:39:42 +01:00
lorenz.stechauner 9f73d13dbf Document: Add GenerationProportion and set to 1/8 2024-01-07 15:26:51 +01:00
lorenz.stechauner 5e665ffb50 PaymentVariantsWindow: Impelment BillingVariant 2024-01-07 15:21:32 +01:00
lorenz.stechauner 1e751c473a BillingData: Implement elwig json format parsing 2024-01-06 14:45:18 +01:00
lorenz.stechauner d67e434fed Use InvariantCulture instead of replacing ',' with '.' 2024-01-06 14:25:42 +01:00
lorenz.stechauner 3a89e16db3 Tests: Move tests into package 2024-01-05 18:03:28 +01:00
lorenz.stechauner 2556033a07 Tests: Add in-memory database for testing 2024-01-05 18:03:21 +01:00
lorenz.stechauner 3f6a94e773 AppDbContext: Add ExecuteEmbeddedScript() 2024-01-05 14:49:28 +01:00
lorenz.stechauner e75e2ddbda BillingData: Move schema to Resources/ 2024-01-05 13:45:37 +01:00
62 changed files with 2260 additions and 831 deletions
+1
View File
@@ -3,3 +3,4 @@ bin/
*.user *.user
.vs .vs
.idea .idea
Tests/Resources/Create.sql
+19 -6
View File
@@ -17,6 +17,7 @@ using Elwig.Windows;
using Elwig.Dialogs; using Elwig.Dialogs;
using System.Threading.Tasks; using System.Threading.Tasks;
using Elwig.Helpers.Billing; using Elwig.Helpers.Billing;
using Elwig.Models.Entities;
namespace Elwig { namespace Elwig {
public partial class App : Application { public partial class App : Application {
@@ -64,22 +65,27 @@ namespace Elwig {
MainDispatcher = Dispatcher; MainDispatcher = Dispatcher;
Scales = Array.Empty<IScale>(); Scales = Array.Empty<IScale>();
CurrentApp = this; CurrentApp = this;
OverrideCulture();
} }
protected override async void OnStartup(StartupEventArgs evt) { private static void OverrideCulture() {
var locale = new CultureInfo("de-AT"); var locale = new CultureInfo("de-AT", false);
locale.NumberFormat.CurrencyGroupSeparator = "\u202f"; locale.NumberFormat.CurrencyGroupSeparator = Utils.GroupSeparator;
locale.NumberFormat.NumberGroupSeparator = "\u202f"; locale.NumberFormat.NumberGroupSeparator = Utils.GroupSeparator;
locale.NumberFormat.PercentGroupSeparator = "\u202f"; locale.NumberFormat.PercentGroupSeparator = Utils.GroupSeparator;
CultureInfo.CurrentCulture = locale;
CultureInfo.CurrentUICulture = locale;
Thread.CurrentThread.CurrentCulture = locale; Thread.CurrentThread.CurrentCulture = locale;
Thread.CurrentThread.CurrentUICulture = locale; Thread.CurrentThread.CurrentUICulture = locale;
CultureInfo.DefaultThreadCurrentCulture = locale; CultureInfo.DefaultThreadCurrentCulture = locale;
CultureInfo.DefaultThreadCurrentUICulture = locale; CultureInfo.DefaultThreadCurrentUICulture = locale;
FrameworkElement.LanguageProperty.OverrideMetadata( FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement), typeof(FrameworkElement),
new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)) new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.Name))
); );
}
protected override async void OnStartup(StartupEventArgs evt) {
Version = typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion.Split("+")[0] ?? "0.0.0"; Version = typeof(App).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion.Split("+")[0] ?? "0.0.0";
try { try {
@@ -221,6 +227,13 @@ namespace Elwig {
return w; return w;
} }
public static BaseDataWindow FocusBaseDataSeason(int year) {
var w = FocusBaseData();
w.Seasons.Focus();
ControlUtils.SelectListBoxItem(w.SeasonList, s => (s as Season)?.Year, year);
return w;
}
public static SeasonFinishWindow FocusSeasonFinish() { public static SeasonFinishWindow FocusSeasonFinish() {
return FocusWindow<SeasonFinishWindow>(() => new()); return FocusWindow<SeasonFinishWindow>(() => new());
} }
+1 -1
View File
@@ -12,7 +12,7 @@ namespace Elwig.Dialogs {
InitializeComponent(); InitializeComponent();
TextLsNr.Text = lsnr; TextLsNr.Text = lsnr;
TextMember.Text = name; TextMember.Text = name;
TextWeight.Text = $"{weight:N0}\u202fkg"; TextWeight.Text = $"{weight:N0}{Utils.UnitSeparator}kg";
} }
private void ConfirmButton_Click(object sender, RoutedEventArgs evt) { private void ConfirmButton_Click(object sender, RoutedEventArgs evt) {
@@ -1,5 +1,4 @@
using Elwig.Helpers; using Elwig.Helpers;
using System.Text.RegularExpressions;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
@@ -14,7 +13,7 @@ namespace Elwig.Dialogs {
private void ConfirmButton_Click(object sender, RoutedEventArgs evt) { private void ConfirmButton_Click(object sender, RoutedEventArgs evt) {
DialogResult = true; DialogResult = true;
Price = double.Parse(PriceInput.Text.Replace("\u202f", "")); Price = double.Parse(PriceInput.Text.Replace(Utils.GroupSeparator, ""));
Close(); Close();
} }
+2 -1
View File
@@ -1,6 +1,7 @@
using Elwig.Helpers; using Elwig.Helpers;
using Elwig.Models.Entities; using Elwig.Models.Entities;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
namespace Elwig.Documents { namespace Elwig.Documents {
@@ -36,7 +37,7 @@ namespace Elwig.Documents {
} }
private static string GetColGroup(IEnumerable<double> cols) { private static string GetColGroup(IEnumerable<double> cols) {
return "<colgroup>\n" + string.Join("\n", cols.Select(g => $"<col style=\"width: {g.ToString().Replace(',', '.')}mm;\"/>")) + "\n</colgroup>\n"; return "<colgroup>\n" + string.Join("\n", cols.Select(g => $"<col style=\"width: {g.ToString(CultureInfo.InvariantCulture)}mm;\"/>")) + "\n</colgroup>\n";
} }
public static string PrintSortenaufteilung(Dictionary<string, MemberBucket> buckets) { public static string PrintSortenaufteilung(Dictionary<string, MemberBucket> buckets) {
+12
View File
@@ -17,6 +17,8 @@ namespace Elwig.Documents {
public int Precision; public int Precision;
public string MemberModifier; public string MemberModifier;
public IEnumerable<(string Name, int Kg, decimal Amount)>? MemberUnderDeliveries; public IEnumerable<(string Name, int Kg, decimal Amount)>? MemberUnderDeliveries;
public decimal MemberTotalUnderDelivery;
public decimal MemberAutoBusinessShares;
public CreditNote(AppDbContext ctx, PaymentMember p, CreditNoteData data, Dictionary<string, UnderDelivery>? underDeliveries = null) : public CreditNote(AppDbContext ctx, PaymentMember p, CreditNoteData data, Dictionary<string, UnderDelivery>? underDeliveries = null) :
base($"{Name} {(p.Credit != null ? $"Nr. {p.Credit.Year}/{p.Credit.TgNr:000}" : p.Member.Name)} {p.Variant.Name}", p.Member) { base($"{Name} {(p.Credit != null ? $"Nr. {p.Credit.Year}/{p.Credit.TgNr:000}" : p.Member.Name)} {p.Variant.Name}", p.Member) {
@@ -32,6 +34,16 @@ namespace Elwig.Documents {
} else { } else {
MemberModifier = "Sonstige Zu-/Abschläge"; MemberModifier = "Sonstige Zu-/Abschläge";
} }
var total = data.Rows.SelectMany(r => r.Buckets).Sum(b => b.Value);
var totalUnderDelivery = total - p.Member.BusinessShares * season.MinKgPerBusinessShare;
MemberTotalUnderDelivery = totalUnderDelivery < 0 ? totalUnderDelivery * (season.PenaltyPerKg ?? 0) - (season.PenaltyAmount ?? 0) : 0;
var fromDate = $"{season.Year}-06-01";
var toDate = $"{season.Year + 1}-06-01";
MemberAutoBusinessShares = ctx.MemberHistory
.Where(h => h.MgNr == p.Member.MgNr && h.Type == "auto")
.Where(h => h.DateString.CompareTo(fromDate) >= 0 && h.DateString.CompareTo(toDate) < 0)
.Sum(h => h.BusinessShares) * (-season.BusinessShareValue ?? 0);
if (total == 0) MemberTotalUnderDelivery -= (season.PenaltyNone ?? 0);
Aside = Aside.Replace("</table>", "") + Aside = Aside.Replace("</table>", "") +
$"<thead><tr><th colspan='2'>Gutschrift</th></tr></thead><tbody>" + $"<thead><tr><th colspan='2'>Gutschrift</th></tr></thead><tbody>" +
$"<tr><th>TG-Nr.</th><td>{(p.Credit != null ? $"{p.Credit.Year}/{p.Credit.TgNr:000}" : "-")}</td></tr>" + $"<tr><th>TG-Nr.</th><td>{(p.Credit != null ? $"{p.Credit.Year}/{p.Credit.TgNr:000}" : "-")}</td></tr>" +
+32 -20
View File
@@ -41,7 +41,7 @@
<th class="unit">[@Model.CurrencySymbol]</th> <th class="unit">[@Model.CurrencySymbol]</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody class="sum">
@foreach (var p in Model.Data.Rows) { @foreach (var p in Model.Data.Rows) {
var rows = Math.Max(p.Buckets.Length, p.Modifiers.Length + 1); var rows = Math.Max(p.Buckets.Length, p.Modifiers.Length + 1);
@for (int i = 0; i < rows; i++) { @for (int i = 0; i < rows; i++) {
@@ -78,36 +78,41 @@
} }
} }
</tbody> </tbody>
</table>
<div class="hint">
Hinweis:<br/>
Die Summe der Lieferungen und die Summe der anfallenden Pönalen werden mit
@Model.Payment?.Variant.Season.Precision Nachkommastellen berechnent,
erst das Ergebnis wird kaufmännisch auf 2 Nachkommastellen gerundet.
</div>
<table class="credit-sum">
@{ @{
string FormatRow(string name, decimal? value, bool add = false, bool halfLine = true, bool bold = false, bool subCat = false) { string FormatRow(string name, decimal? value, bool add = false, bool bold = false, bool subCat = false, bool noTopBorder = false) {
return $"<tr class=\"{(halfLine || add ? "" : "sum")} {(bold ? "large bold" : "")}\">" return $"<tr class=\"{(!add && !noTopBorder ? "sum" : !add ? "large" : "")} {(bold ? "large bold" : "")}\">"
+ $"<td colspan=\"4\"></td>" + $"<td class=\"{(subCat ? "small" : "")}\" style=\"overflow: visible;\">{name}:</td>"
+ $"<td class=\"{(halfLine && !add ? "sum" : "")} {(subCat ? "small" : "")}\" colspan=\"4\" style=\"overflow: visible;\">{name}:</td>" + $"<td class=\"number {(subCat ? "small" : "large")}\">{(value < 0 ? "" : (add ? "+" : ""))}</td>"
+ $"<td class=\"number {(subCat ? "small" : "large")} {(halfLine && !add ? "sum" : "")}\">{(value < 0 ? "" : (add ? "+" : ""))}</td>" + $"<td class=\"number {(subCat ? "small" : "large")}\">"
+ $"<td colspan=\"2\" class=\"number {(subCat ? "small" : "large")} {(halfLine && !add ? "sum" : "")}\">"
+ $"<span class=\"fleft\">{Model.CurrencySymbol}</span>{Math.Abs(value ?? 0):N2}</td>" + $"<span class=\"fleft\">{Model.CurrencySymbol}</span>{Math.Abs(value ?? 0):N2}</td>"
+ $"</tr>\n"; + $"</tr>\n";
} }
} }
<tbody style="break-inside: avoid; break-before: avoid;"> <tbody style="break-inside: avoid;">
@{ var sum = Model.Data.Rows.Sum(p => p.Amount); } @{ var sum = Model.Data.Rows.Sum(p => p.Amount); }
@if (Model.Payment == null) { @if (Model.Payment == null) {
@Raw(FormatRow("Gesamt", sum, halfLine: false, bold: true)) @Raw(FormatRow("Gesamt", sum, bold: true, noTopBorder: true))
} else { } else {
var totalLine = false; var noBorder = true;
if (Model.Payment.NetAmount != Model.Payment.Amount) { if (Model.Payment.NetAmount != Model.Payment.Amount) {
@Raw(FormatRow("Zwischensumme", Model.Payment.NetAmount, halfLine: totalLine)) @Raw(FormatRow("Zwischensumme", Model.Payment.NetAmount, noTopBorder: noBorder))
totalLine = true; noBorder = false;
@Raw(FormatRow(Model.MemberModifier, Model.Payment.Amount - Model.Payment.NetAmount, add: true, halfLine: totalLine)) @Raw(FormatRow(Model.MemberModifier, Model.Payment.Amount - Model.Payment.NetAmount, add: true))
} }
if (Model.Credit == null) { if (Model.Credit == null) {
@Raw(FormatRow("Gesamtbetrag", Model.Payment.Amount, bold: true, halfLine: totalLine)) @Raw(FormatRow("Gesamtbetrag", Model.Payment.Amount, bold: true, noTopBorder: noBorder))
totalLine = true;
// TODO Mock VAT // TODO Mock VAT
} else { } else {
var hasPrev = Model.Credit.PrevNetAmount != null; var hasPrev = Model.Credit.PrevNetAmount != null;
@Raw(FormatRow(hasPrev ? "Gesamtbetrag" : "Nettobetrag", Model.Credit.NetAmount, bold: true, halfLine: totalLine)) @Raw(FormatRow(hasPrev ? "Gesamtbetrag" : "Nettobetrag", Model.Credit.NetAmount, bold: true, noTopBorder: noBorder))
totalLine = true;
if (hasPrev) { if (hasPrev) {
@Raw(FormatRow("Bisher berücksichtigt", -Model.Credit.PrevNetAmount, add: true)) @Raw(FormatRow("Bisher berücksichtigt", -Model.Credit.PrevNetAmount, add: true))
@Raw(FormatRow("Nettobetrag", Model.Credit.NetAmount - (Model.Credit.PrevNetAmount ?? 0))) @Raw(FormatRow("Nettobetrag", Model.Credit.NetAmount - (Model.Credit.PrevNetAmount ?? 0)))
@@ -122,9 +127,8 @@
@if (Model.MemberUnderDeliveries != null && Model.MemberUnderDeliveries.Count() > 0) { @if (Model.MemberUnderDeliveries != null && Model.MemberUnderDeliveries.Count() > 0) {
<tr class="small"> <tr class="small">
<td colspan="4"></td> <td colspan="2" style="padding-top: 5mm;">Anfallende Pönalen durch Unterlieferungen:</td>
<td colspan="5" style="padding-top: 5mm;">Anfallende Pönalen durch Unterlieferungen:</td> <td></td>
<td colspan="2"></td>
</tr> </tr>
foreach (var u in Model.MemberUnderDeliveries) { foreach (var u in Model.MemberUnderDeliveries) {
@Raw(FormatRow($"{u.Name} ({u.Kg:N0} kg)", u.Amount, add: true, subCat: true)) @Raw(FormatRow($"{u.Name} ({u.Kg:N0} kg)", u.Amount, add: true, subCat: true))
@@ -132,6 +136,14 @@
} }
penalty = Math.Round(penalty, 2, MidpointRounding.AwayFromZero); penalty = Math.Round(penalty, 2, MidpointRounding.AwayFromZero);
} }
@if (Model.MemberTotalUnderDelivery != 0) {
@Raw(FormatRow("Unterlieferung (GA)", Model.MemberTotalUnderDelivery, add: true));
penalty += Model.MemberTotalUnderDelivery;
}
@if (Model.MemberAutoBusinessShares != 0) {
@Raw(FormatRow("Autom. Nachz. von GA", Model.MemberAutoBusinessShares, add: true));
penalty += Model.MemberAutoBusinessShares;
}
@if (Model.Credit == null) { @if (Model.Credit == null) {
@Raw(FormatRow("Auszahlungsbetrag", (Model.Payment?.Amount + penalty) ?? (sum + penalty), bold: true)) @Raw(FormatRow("Auszahlungsbetrag", (Model.Payment?.Amount + penalty) ?? (sum + penalty), bold: true))
+22 -3
View File
@@ -1,4 +1,8 @@
table.credit {
margin-bottom: 0;
}
table.credit .mod { table.credit .mod {
padding-left: 5mm; padding-left: 5mm;
} }
@@ -19,11 +23,26 @@ table.credit tr.last td {
padding-bottom: 0; padding-bottom: 0;
} }
table.credit .sum { table.credit-sum {
width: 50%;
margin-left: 50%;
}
table.credit-sum tr.sum,
table.credit-sum tr .sum {
font-size: 12pt; font-size: 12pt;
} }
table.credit tr.sum td, table.credit-sum tr.sum td,
table.credit td.sum { table.credit-sum td.sum {
padding-top: 1mm !important; padding-top: 1mm !important;
} }
.hint {
font-style: italic;
font-size: 8pt;
width: 74mm;
position: absolute;
left: 0;
margin: 2mm 4mm;
}
+4 -5
View File
@@ -128,11 +128,6 @@ main table tr.border {
border-top: 0.5pt solid black; border-top: 0.5pt solid black;
} }
main table tr.sum,
main table td.sum {
break-before: avoid;
}
main table th.unit { main table th.unit {
font-size: 8pt; font-size: 8pt;
} }
@@ -152,3 +147,7 @@ main table .rborder {border-right: 0.5pt solid black;}
main table .fleft { main table .fleft {
float: left; float: left;
} }
main tbody.sum tr:last-child {
border-bottom: 0.5pt solid black;
}
+5 -3
View File
@@ -11,6 +11,8 @@ namespace Elwig.Documents {
public static string Name => "Dokument"; public static string Name => "Dokument";
private static readonly double GenerationProportion = 0.125;
private TempFile? _pdfFile = null; private TempFile? _pdfFile = null;
public bool ShowFoldMarks = App.Config.Debug; public bool ShowFoldMarks = App.Config.Debug;
@@ -95,10 +97,10 @@ namespace Elwig.Documents {
await File.WriteAllTextAsync(tmpHtml.FilePath, await doc.Render(), Utils.UTF8); await File.WriteAllTextAsync(tmpHtml.FilePath, await doc.Render(), Utils.UTF8);
tmpHtmls.Add(tmpHtml); tmpHtmls.Add(tmpHtml);
i++; i++;
progress?.Report(25.0 * i / n); progress?.Report(GenerationProportion * 100 * i / n);
} }
progress?.Report(25.0); progress?.Report(GenerationProportion * 100);
await Pdf.Convert(tmpHtmls.Select(f => f.FileName), pdf.FileName, DoubleSided, new Progress<double>(v => progress?.Report(25.0 + v * 0.75))); await Pdf.Convert(tmpHtmls.Select(f => f.FileName), pdf.FileName, DoubleSided, new Progress<double>(v => progress?.Report(GenerationProportion * 100 + v * (1 - GenerationProportion))));
foreach (var tmp in tmpHtmls) { foreach (var tmp in tmpHtmls) {
tmp.Dispose(); tmp.Dispose();
} }
+4 -4
View File
@@ -2,6 +2,7 @@
using Elwig.Models.Entities; using Elwig.Models.Entities;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Elwig.Documents { namespace Elwig.Documents {
public class MemberDataSheet : BusinessDocument { public class MemberDataSheet : BusinessDocument {
@@ -9,13 +10,12 @@ namespace Elwig.Documents {
public new static string Name => "Stammdatenblatt"; public new static string Name => "Stammdatenblatt";
public Season Season; public Season Season;
public int Year = Utils.CurrentYear;
public Dictionary<string, MemberBucket> MemberBuckets; public Dictionary<string, MemberBucket> MemberBuckets;
public MemberDataSheet(Member m, AppDbContext ctx) : base($"{Name} {m.AdministrativeName}", m) { public MemberDataSheet(Member m, AppDbContext ctx) : base($"{Name} {m.AdministrativeName}", m) {
DocumentId = $"{Name} {m.MgNr}"; DocumentId = $"{Name} {m.MgNr}";
Season = ctx.Seasons.Find(Year) ?? throw new ArgumentException("invalid season"); Season = ctx.Seasons.ToList().MaxBy(s => s.Year) ?? throw new ArgumentException("invalid season");
MemberBuckets = ctx.GetMemberBuckets(Year, m.MgNr).GetAwaiter().GetResult(); MemberBuckets = ctx.GetMemberBuckets(Season.Year, m.MgNr).GetAwaiter().GetResult();
} }
} }
} }
+6 -6
View File
@@ -7,14 +7,14 @@
<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.5.1</Version> <Version>0.6.0</Version>
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages> <SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Resource Include="Resources\Images\Elwig.png" /> <Resource Include="Resources\Images\Elwig.png" />
<Content Include="Resources\Images\Elwig.ico" /> <Content Include="Resources\Images\Elwig.ico" />
<EmbeddedResource Include="Schemas\payment_variant.json" /> <EmbeddedResource Include="Resources\Schemas\PaymentVariantData.json" />
<EmbeddedResource Include="Resources\Sql\*" /> <EmbeddedResource Include="Resources\Sql\*" />
</ItemGroup> </ItemGroup>
@@ -25,11 +25,11 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.5.1" /> <PackageReference Include="Extended.Wpf.Toolkit" Version="4.5.1" />
<PackageReference Include="LinqKit" Version="1.2.5" /> <PackageReference Include="LinqKit" Version="1.2.5" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.25" /> <PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.26" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="8.0.0" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2151.40" /> <PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2210.55" />
<PackageReference Include="NJsonSchema" Version="11.0.0" /> <PackageReference Include="NJsonSchema" Version="11.0.0" />
<PackageReference Include="RazorLight" Version="2.3.1" /> <PackageReference Include="RazorLight" Version="2.3.1" />
<PackageReference Include="ScottPlot.WPF" Version="4.1.68" /> <PackageReference Include="ScottPlot.WPF" Version="4.1.68" />
+17 -1
View File
@@ -10,6 +10,7 @@ using Microsoft.Data.Sqlite;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Collections.Generic; using System.Collections.Generic;
using Elwig.Models.Dtos; using Elwig.Models.Dtos;
using System.Reflection;
namespace Elwig.Helpers { namespace Elwig.Helpers {
@@ -43,6 +44,7 @@ namespace Elwig.Helpers {
public DbSet<Member> Members { get; private set; } public DbSet<Member> Members { get; private set; }
public DbSet<BillingAddr> BillingAddresses { get; private set; } public DbSet<BillingAddr> BillingAddresses { get; private set; }
public DbSet<MemberTelNr> MemberTelephoneNrs { get; private set; } public DbSet<MemberTelNr> MemberTelephoneNrs { get; private set; }
public DbSet<MemberHistory> MemberHistory { get; private set; }
public DbSet<AreaCom> AreaCommitments { get; private set; } public DbSet<AreaCom> AreaCommitments { get; private set; }
public DbSet<Season> Seasons { get; private set; } public DbSet<Season> Seasons { get; private set; }
public DbSet<Modifier> Modifiers { get; private set; } public DbSet<Modifier> Modifiers { get; private set; }
@@ -63,7 +65,8 @@ namespace Elwig.Helpers {
public DateTime SavedLastWriteTime { get; private set; } public DateTime SavedLastWriteTime { get; private set; }
public bool HasBackendChanged => SavedLastWriteTime != LastWriteTime; public bool HasBackendChanged => SavedLastWriteTime != LastWriteTime;
public static string ConnectionString => $"Data Source=\"{App.Config.DatabaseFile}\"; Foreign Keys=True; Mode=ReadWrite; Cache=Default"; public static string? ConnectionStringOverride { get; set; } = null;
public static string ConnectionString => ConnectionStringOverride ?? $"Data Source=\"{App.Config.DatabaseFile}\"; Mode=ReadWrite; Foreign Keys=True; Cache=Default";
private readonly Dictionary<int, Dictionary<int, Dictionary<string, AreaComBucket>>> _memberAreaCommitmentBuckets = []; private readonly Dictionary<int, Dictionary<int, Dictionary<string, AreaComBucket>>> _memberAreaCommitmentBuckets = [];
private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberDeliveryBuckets = []; private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberDeliveryBuckets = [];
@@ -106,6 +109,12 @@ namespace Elwig.Helpers {
await (await cmd.ExecuteReaderAsync()).CloseAsync(); await (await cmd.ExecuteReaderAsync()).CloseAsync();
} }
public static async Task ExecuteEmbeddedScript(SqliteConnection cnx, Assembly asm, string name) {
using var stream = asm.GetManifestResourceStream(name) ?? throw new FileNotFoundException("Unable to load embedded resource");
using var reader = new StreamReader(stream);
await ExecuteBatch(cnx, await reader.ReadToEndAsync());
}
public static async Task<object?> ExecuteScalar(SqliteConnection cnx, string sql) { public static async Task<object?> ExecuteScalar(SqliteConnection cnx, string sql) {
using var cmd = cnx.CreateCommand(); using var cmd = cnx.CreateCommand();
cmd.CommandText = sql; cmd.CommandText = sql;
@@ -192,6 +201,13 @@ namespace Elwig.Helpers {
return c + 1; return c + 1;
} }
public async Task<int> NextAvNr(int year) {
int c = 0;
(await PaymentVariants.Where(v => v.Year == year).Select(v => v.AvNr).ToListAsync())
.ForEach(a => { if (a <= c + 100) c = a; });
return c + 1;
}
public async Task<WineQualLevel> GetWineQualityLevel(double kmw) { public async Task<WineQualLevel> GetWineQualityLevel(double kmw) {
return await WineQualityLevels return await WineQualityLevels
.Where(q => !q.IsPredicate && (q.MinKmw == null || q.MinKmw <= kmw)) .Where(q => !q.IsPredicate && (q.MinKmw == null || q.MinKmw <= kmw))
+4 -6
View File
@@ -1,7 +1,6 @@
using Microsoft.Data.Sqlite; using Microsoft.Data.Sqlite;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -9,7 +8,8 @@ using System.Threading.Tasks;
namespace Elwig.Helpers { namespace Elwig.Helpers {
public static class AppDbUpdater { public static class AppDbUpdater {
public static readonly int RequiredSchemaVersion = 12; // Don't forget to update value in Tests/fetch-resources.bat!
public static readonly int RequiredSchemaVersion = 13;
private static int VersionOffset = 0; private static int VersionOffset = 0;
@@ -44,7 +44,7 @@ namespace Elwig.Helpers {
private static async Task UpdateDbSchema(SqliteConnection cnx, int fromVersion, int toVersion) { private static async Task UpdateDbSchema(SqliteConnection cnx, int fromVersion, int toVersion) {
if (fromVersion == toVersion) { if (fromVersion == toVersion) {
//return; return;
} else if (fromVersion > toVersion) { } else if (fromVersion > toVersion) {
throw new Exception("schema_version of database is too new"); throw new Exception("schema_version of database is too new");
} else if (fromVersion <= 0) { } else if (fromVersion <= 0) {
@@ -77,9 +77,7 @@ namespace Elwig.Helpers {
BEGIN EXCLUSIVE; BEGIN EXCLUSIVE;
"""); """);
foreach (var script in toExecute) { foreach (var script in toExecute) {
using var stream = asm.GetManifestResourceStream(script) ?? throw new Exception("Unable to load embedded resource"); await AppDbContext.ExecuteEmbeddedScript(cnx, asm, script);
using var reader = new StreamReader(stream);
await AppDbContext.ExecuteBatch(cnx, await reader.ReadToEndAsync());
} }
await AppDbContext.ExecuteBatch(cnx, $""" await AppDbContext.ExecuteBatch(cnx, $"""
PRAGMA foreign_key_check; PRAGMA foreign_key_check;
+12
View File
@@ -31,6 +31,18 @@ namespace Elwig.Helpers.Billing {
"""); """);
} }
public async Task AutoAdjustBusinessShare() {
using var cnx = await AppDbContext.ConnectAsync();
await AppDbContext.ExecuteBatch(cnx, $"""
INSERT INTO member_history (mgnr, date, business_shares, type)
SELECT u.mgnr, '{Utils.Today:yyyy-MM-dd}', u.diff / s.max_kg_per_bs AS bs, 'auto'
FROM v_total_under_delivery u
JOIN season s ON s.year = u.year
WHERE s.year = {Year} AND bs > 0
ON CONFLICT DO NOTHING
""");
}
public async Task CalculateBuckets(bool allowAttrsIntoLower, bool avoidUnderDeliveries, bool honorGebunden) { public async Task CalculateBuckets(bool allowAttrsIntoLower, bool avoidUnderDeliveries, bool honorGebunden) {
var attrVals = Context.WineAttributes.ToDictionary(a => a.AttrId, a => (a.IsStrict, a.FillLower)); var attrVals = Context.WineAttributes.ToDictionary(a => a.AttrId, a => (a.IsStrict, a.FillLower));
var attrForced = attrVals.Where(a => a.Value.IsStrict && a.Value.FillLower == 0).Select(a => a.Key).ToArray(); var attrForced = attrVals.Where(a => a.Value.IsStrict && a.Value.FillLower == 0).Select(a => a.Key).ToArray();
+111 -6
View File
@@ -1,6 +1,9 @@
using Newtonsoft.Json; using Newtonsoft.Json;
using NJsonSchema; using NJsonSchema;
using System; using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text.Json.Nodes; using System.Text.Json.Nodes;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -8,21 +11,58 @@ using System.Threading.Tasks;
namespace Elwig.Helpers.Billing { namespace Elwig.Helpers.Billing {
public class BillingData { public class BillingData {
public enum CalculationMode { Elwig, WgMaster }
public enum CurveMode { Oe, Kmw }
public record struct Curve(CurveMode Mode, Dictionary<double, decimal> Normal, Dictionary<double, decimal>? Gebunden);
public static JsonSchema? Schema { get; private set; } public static JsonSchema? Schema { get; private set; }
public static async Task Init() { public static async Task Init() {
var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Elwig.Schemas.payment_variant.json"); var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Elwig.Resources.Schemas.PaymentVariantData.json");
Schema = await JsonSchema.FromJsonAsync(stream ?? throw new ArgumentException("JSON schema not found")); Schema = await JsonSchema.FromJsonAsync(stream ?? throw new ArgumentException("JSON schema not found"));
} }
private JsonObject Data; public readonly JsonObject Data;
public readonly CalculationMode Mode;
public bool ConsiderDelieryModifiers {
get => GetConsider("consider_delivery_modifiers");
set => SetConsider(value, "consider_delivery_modifiers");
}
public bool ConsiderContractPenalties {
get => GetConsider("consider_contract_penalties");
set => SetConsider(value, "consider_contract_penalties");
}
public bool ConsiderTotalPenalty {
get => GetConsider("consider_total_penalty");
set => SetConsider(value, "consider_total_penalty");
}
public bool ConsiderAutoBusinessShares {
get => GetConsider("consider_auto_business_shares");
set => SetConsider(value, "consider_auto_business_shares");
}
private bool GetConsider(string name, string? wgMasterName = null) {
return ((Mode == CalculationMode.Elwig) ? Data[name] : Data[wgMasterName ?? ""])?.AsValue().GetValue<bool>() ?? false;
}
private void SetConsider(bool value, string name, string? wgMasterName = null) {
if (Mode == CalculationMode.WgMaster && wgMasterName == null) {
return;
} else if (value) {
Data[(Mode == CalculationMode.Elwig) ? name : wgMasterName ?? ""] = value;
} else {
Data.Remove((Mode == CalculationMode.Elwig) ? name : wgMasterName ?? "");
}
}
public BillingData(JsonObject data) { public BillingData(JsonObject data) {
Data = data; Data = data;
var mode = Data["mode"]?.GetValue<string>();
Mode = (mode == "elwig") ? CalculationMode.Elwig : CalculationMode.WgMaster;
} }
public static JsonObject ParseJson(string json) { protected static JsonObject ParseJson(string json) {
if (Schema == null) throw new InvalidOperationException("Schema has to be initialized first"); if (Schema == null) throw new InvalidOperationException("Schema has to be initialized first");
try { try {
var errors = Schema.Validate(json); var errors = Schema.Validate(json);
@@ -37,8 +77,73 @@ namespace Elwig.Helpers.Billing {
return new(ParseJson(json)); return new(ParseJson(json));
} }
public decimal CalculatePrice(string sortId, string? attrid, string qualId, bool gebunden, double oe, double kmw, bool minQuw) { protected JsonArray GetCurvesEntry() {
return 0m; return Data[Mode == CalculationMode.Elwig ? "curves" : "Kurven"]?.AsArray() ?? throw new InvalidOperationException();
}
protected JsonNode GetPaymentEntry() {
return Data[Mode == CalculationMode.Elwig ? "payment" : "AuszahlungSorten"] ?? throw new InvalidOperationException();
}
protected JsonObject? GetQualityEntry() {
return Data[Mode == CalculationMode.Elwig ? "quality" : "AuszahlungSortenQualitätsstufe"]?.AsObject();
}
private static Dictionary<double, decimal> GetCurveData(JsonObject data, CurveMode mode) {
var dict = new Dictionary<double, decimal>();
foreach (var (index, price) in data) {
double idx;
bool? gtlt = index.StartsWith('>') ? true : index.StartsWith('<') ? false : null;
if (index.EndsWith("kmw")) {
idx = double.Parse(index[(gtlt != null ? 1 : 0)..^3], CultureInfo.InvariantCulture);
if (mode == CurveMode.Oe) {
idx = Utils.KmwToOe(idx);
}
} else if (index.EndsWith("oe")) {
idx = double.Parse(index[(gtlt != null ? 1 : 0)..^2], CultureInfo.InvariantCulture);
if (mode == CurveMode.Kmw) {
idx = Utils.OeToKmw(idx);
}
} else {
throw new InvalidOperationException();
}
if (gtlt == true) {
idx = Math.BitIncrement(idx);
} else if (gtlt == false) {
idx = Math.BitDecrement(idx);
}
dict[idx] = price?.AsValue().GetValue<decimal>() ?? throw new InvalidOperationException();
}
return dict;
}
protected Dictionary<int, Curve> GetCurves() {
var dict = new Dictionary<int, Curve>();
var curves = GetCurvesEntry();
foreach (var c in curves) {
var obj = c?.AsObject() ?? throw new InvalidOperationException();
var id = obj["id"]?.GetValue<int>() ?? throw new InvalidOperationException();
var cMode = (obj["mode"]?.GetValue<string>() == "kmw") ? CurveMode.Kmw : CurveMode.Oe;
Dictionary<double, decimal> c1;
Dictionary<double, decimal>? c2 = null;
var norm = obj["data"];
if (norm is JsonObject) {
c1 = GetCurveData(norm.AsObject(), cMode);
} else if (norm?.AsValue().TryGetValue(out decimal v) == true) {
c1 = new() { { cMode == CurveMode.Oe ? 73 : 15, v } };
} else {
throw new InvalidOperationException();
}
var geb = obj["geb"];
if (geb is JsonObject) {
c2 = GetCurveData(geb.AsObject(), cMode);
} else if (geb?.AsValue().TryGetValue(out decimal v) == true) {
c2 = c1.ToDictionary(e => e.Key, e => e.Value + v);
}
dict.Add(id, new(cMode, c1, c2));
}
return dict;
} }
} }
} }
+55 -27
View File
@@ -1,3 +1,6 @@
using Elwig.Models.Entities;
using Microsoft.Data.Sqlite;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -5,18 +8,33 @@ using System.Threading.Tasks;
namespace Elwig.Helpers.Billing { namespace Elwig.Helpers.Billing {
public class BillingVariant : Billing { public class BillingVariant : Billing {
private readonly int AvNr; protected readonly int AvNr;
protected readonly PaymentVar PaymentVariant;
protected readonly PaymentBillingData Data;
public BillingVariant(int year, int avnr) : base(year) { public BillingVariant(int year, int avnr) : base(year) {
AvNr = avnr; AvNr = avnr;
PaymentVariant = Context.PaymentVariants.Find(Year, AvNr) ?? throw new ArgumentException("PaymentVar not found");
var attrVariants = Context.DeliveryParts
.Where(d => d.Year == Year)
.Select(d => $"{d.SortId}{d.AttrId}")
.Distinct()
.ToList()
.Union(Context.WineVarieties.Select(v => v.SortId))
.ToList();
Data = PaymentBillingData.FromJson(PaymentVariant.Data, attrVariants);
} }
public async Task Calculate() { public async Task Calculate() {
await DeleteInDb(); using var cnx = await AppDbContext.ConnectAsync();
await SetCalcTime(); using var tx = await cnx.BeginTransactionAsync();
await CalculatePrices(); await DeleteInDb(cnx);
await CalculateModifiers(); await SetCalcTime(cnx);
await CalculateMemberModifiers(); await CalculatePrices(cnx);
if (Data.ConsiderDelieryModifiers)
await CalculateDeliveryModifiers(cnx);
await CalculateMemberModifiers(cnx);
await tx.CommitAsync();
} }
public async Task Commit() { public async Task Commit() {
@@ -31,7 +49,11 @@ namespace Elwig.Helpers.Billing {
ROUND(p.amount / POW(10, s.precision - 2)) AS net_amount, ROUND(p.amount / POW(10, s.precision - 2)) AS net_amount,
ROUND(lp.amount / POW(10, s.precision - 2)) AS prev_amount, ROUND(lp.amount / POW(10, s.precision - 2)) AS prev_amount,
IIF(m.buchführend, s.vat_normal, s.vat_flatrate) AS vat, IIF(m.buchführend, s.vat_normal, s.vat_flatrate) AS vat,
ROUND(COALESCE(u.total_penalty, 0) / POW(10, 4 - 2)) AS modifiers, ROUND(
IIF({Data.ConsiderContractPenalties}, COALESCE(u.total_penalty, 0) / POW(10, 4 - 2), 0) +
IIF({Data.ConsiderTotalPenalty}, COALESCE(b.total_penalty, 0), 0) +
IIF({Data.ConsiderAutoBusinessShares}, -COALESCE(a.business_shares * s.bs_value, 0), 0) / POW(10, s.precision - 2)
) AS modifiers,
lc.modifiers AS prev_modifiers lc.modifiers AS prev_modifiers
FROM season s FROM season s
JOIN payment_variant v ON v.year = s.year JOIN payment_variant v ON v.year = s.year
@@ -54,6 +76,19 @@ namespace Elwig.Helpers.Billing {
FROM v_under_delivery u FROM v_under_delivery u
JOIN area_commitment_type t ON t.vtrgid = u.bucket JOIN area_commitment_type t ON t.vtrgid = u.bucket
GROUP BY year, mgnr) u ON (u.year, u.mgnr) = (s.year, m.mgnr) GROUP BY year, mgnr) u ON (u.year, u.mgnr) = (s.year, m.mgnr)
LEFT JOIN (SELECT s.year, u.mgnr,
(COALESCE(IIF(u.weight = 0, -s.penalty_none, 0), 0) +
COALESCE(IIF(u.diff < 0, -s.penalty_amount, 0), 0) +
COALESCE(u.diff * s.penalty_per_kg, 0)
) / POW(10, s.precision - 2) AS total_penalty
FROM v_total_under_delivery u
JOIN season s ON s.year = u.year
WHERE u.diff < 0) b ON (b.year, b.mgnr) = (s.year, m.mgnr)
LEFT JOIN (SELECT h.mgnr, h.business_shares
FROM member_history h
WHERE type = 'auto' AND
date >= '{Year}-06-01' AND
date < '{Year + 1}-06-01') a ON a.mgnr = m.mgnr
WHERE s.year = {Year} AND v.avnr = {AvNr}; WHERE s.year = {Year} AND v.avnr = {AvNr};
UPDATE payment_variant SET test_variant = FALSE WHERE (year, avnr) = ({Year}, {AvNr}); UPDATE payment_variant SET test_variant = FALSE WHERE (year, avnr) = ({Year}, {AvNr});
@@ -68,15 +103,13 @@ namespace Elwig.Helpers.Billing {
"""); """);
} }
protected async Task SetCalcTime() { protected async Task SetCalcTime(SqliteConnection cnx) {
using var cnx = await AppDbContext.ConnectAsync();
await AppDbContext.ExecuteBatch(cnx, $""" await AppDbContext.ExecuteBatch(cnx, $"""
UPDATE payment_variant SET calc_time = UNIXEPOCH() WHERE (year, avnr) = ({Year}, {AvNr}) UPDATE payment_variant SET calc_time = UNIXEPOCH() WHERE (year, avnr) = ({Year}, {AvNr})
"""); """);
} }
protected async Task DeleteInDb() { protected async Task DeleteInDb(SqliteConnection cnx) {
using var cnx = await AppDbContext.ConnectAsync();
await AppDbContext.ExecuteBatch(cnx, $""" await AppDbContext.ExecuteBatch(cnx, $"""
DELETE FROM payment_delivery_part_bucket WHERE (year, avnr) = ({Year}, {AvNr}); DELETE FROM payment_delivery_part_bucket WHERE (year, avnr) = ({Year}, {AvNr});
DELETE FROM payment_delivery_part WHERE (year, avnr) = ({Year}, {AvNr}); DELETE FROM payment_delivery_part WHERE (year, avnr) = ({Year}, {AvNr});
@@ -85,8 +118,7 @@ namespace Elwig.Helpers.Billing {
"""); """);
} }
protected async Task CalculateMemberModifiers() { protected async Task CalculateMemberModifiers(SqliteConnection cnx) {
using var cnx = await AppDbContext.ConnectAsync();
if (App.Client.IsMatzen) { if (App.Client.IsMatzen) {
var lastYears = 3; var lastYears = 3;
var multiplier = 0.50; var multiplier = 0.50;
@@ -113,13 +145,11 @@ namespace Elwig.Helpers.Billing {
} }
} }
protected async Task CalculatePrices() { protected async Task CalculatePrices(SqliteConnection cnx) {
using var cnx = await AppDbContext.ConnectAsync(); var parts = new List<(int Year, int DId, int DPNr, int BktNr, string SortId, string Discr, int Value, double Oe, double Kmw, string QualId)>();
var parts = new List<(int Year, int DId, int DPNr, int BktNr, string SortId, string Discr, int Value, bool MinQuw, double Oe, double Kmw)>();
using (var cmd = cnx.CreateCommand()) { using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = $""" cmd.CommandText = $"""
SELECT d.year, d.did, d.dpnr, b.bktnr, d.sortid, b.discr, b.value, d.min_quw, d.oe, d.kmw SELECT d.year, d.did, d.dpnr, b.bktnr, d.sortid, b.discr, b.value, d.oe, d.kmw, d.qualid
FROM delivery_part_bucket b FROM delivery_part_bucket b
JOIN v_delivery d ON (d.year, d.did, d.dpnr) = (b.year, b.did, b.dpnr) JOIN v_delivery d ON (d.year, d.did, d.dpnr) = (b.year, b.did, b.dpnr)
WHERE b.year = {Year} WHERE b.year = {Year}
@@ -129,18 +159,17 @@ namespace Elwig.Helpers.Billing {
parts.Add(( parts.Add((
reader.GetInt32(0), reader.GetInt32(1), reader.GetInt32(2), reader.GetInt32(3), reader.GetInt32(0), reader.GetInt32(1), reader.GetInt32(2), reader.GetInt32(3),
reader.GetString(4), reader.GetString(5), reader.GetInt32(6), reader.GetString(4), reader.GetString(5), reader.GetInt32(6),
reader.GetBoolean(7), reader.GetDouble(8), reader.GetDouble(9) reader.GetDouble(7), reader.GetDouble(8), reader.GetString(9)
)); ));
} }
} }
var inserts = new List<(int Year, int DId, int DPNr, int BktNr, long Price, long Amount)>(); var inserts = new List<(int Year, int DId, int DPNr, int BktNr, long Price, long Amount)>();
foreach (var part in parts) { foreach (var part in parts) {
var price = !part.MinQuw ? 0.5m : ((part.BktNr == 2 ? 0.8m : (part.BktNr == 1 ? 0.7m : 0.6m)) + ((decimal)(part.Oe - 73) * 0.005m)); // TODO var attrId = (part.Discr == "_" || part.Discr == "") ? null : part.Discr;
price += (AvNr - 1) * 0.2m; var price = Data.CalculatePrice(part.SortId, attrId, part.QualId, part.Discr != "_", part.Oe, part.Kmw);
var priceL = Utils.DecToDb(price, 4); var priceL = PaymentVariant.Season.DecToDb(price);
var amount = Utils.DecToDb(price * part.Value, 4); inserts.Add((part.Year, part.DId, part.DPNr, part.BktNr, priceL, priceL * part.Value));
inserts.Add((part.Year, part.DId, part.DPNr, part.BktNr, priceL, amount));
} }
await AppDbContext.ExecuteBatch(cnx, $""" await AppDbContext.ExecuteBatch(cnx, $"""
@@ -149,14 +178,13 @@ namespace Elwig.Helpers.Billing {
"""); """);
} }
protected async Task CalculateModifiers() { protected async Task CalculateDeliveryModifiers(SqliteConnection cnx) {
using var cnx = await AppDbContext.ConnectAsync();
await AppDbContext.ExecuteBatch(cnx, $""" await AppDbContext.ExecuteBatch(cnx, $"""
INSERT INTO payment_delivery_part (year, did, dpnr, avnr, net_amount, mod_abs, mod_rel) INSERT INTO payment_delivery_part (year, did, dpnr, avnr, net_amount, mod_abs, mod_rel)
SELECT d.year, d.did, d.dpnr, {AvNr}, 0, COALESCE(m.abs, 0), COALESCE(m.rel, 0) SELECT d.year, d.did, d.dpnr, {AvNr}, 0, COALESCE(m.abs, 0), COALESCE(m.rel, 0)
FROM delivery_part d FROM delivery_part d
LEFT JOIN delivery_part_modifier p ON (p.year, p.did, p.dpnr) = (d.year, d.did, d.dpnr) LEFT JOIN delivery_part_modifier p ON (p.year, p.did, p.dpnr) = (d.year, d.did, d.dpnr)
LEFT JOIN modifier m ON m.modid = p.modid LEFT JOIN modifier m ON (m.year, m.modid) = (d.year, p.modid)
WHERE d.year = {Year} WHERE d.year = {Year}
ON CONFLICT DO UPDATE ON CONFLICT DO UPDATE
SET mod_abs = mod_abs + excluded.mod_abs, SET mod_abs = mod_abs + excluded.mod_abs,
+90
View File
@@ -0,0 +1,90 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Nodes;
namespace Elwig.Helpers.Billing {
public class EditBillingData : BillingData {
protected readonly IEnumerable<string> AttributeVariants;
public EditBillingData(JsonObject data, IEnumerable<string> attributeVariants) :
base(data) {
AttributeVariants = attributeVariants;
}
public static EditBillingData FromJson(string json, IEnumerable<string> attributeVariants) {
return new(ParseJson(json), attributeVariants);
}
public IEnumerable<GraphEntry> GetPaymentGraphEntries() {
Dictionary<int, List<string>> dict1 = [];
Dictionary<decimal, List<string>> dict2 = [];
var p = GetPaymentEntry();
if (p is JsonObject paymentObj) {
foreach (var (selector, node) in paymentObj) {
var val = node?.AsValue();
if (val == null) {
continue;
} else if (val.TryGetValue<decimal>(out var price)) {
if (!dict2.ContainsKey(price)) dict2[price] = [];
dict2[price].Add(selector);
} else if (val.TryGetValue<string>(out var curve)) {
var idx = int.Parse(curve.Split(":")[1] ?? "0");
if (!dict1.ContainsKey(idx)) dict1[idx] = [];
dict1[idx].Add(selector);
}
}
} else if (p is JsonValue paymentVal) {
var idx = paymentVal.GetValue<decimal>();
if (!dict2.ContainsKey(idx)) dict2[idx] = [];
dict2[idx].Add("default");
}
Dictionary<int, Curve> curves = GetCurves();
decimal[] virtCurves = [.. dict2.Keys.Order()];
for (int i = 0; i < virtCurves.Length; i++) {
var idx = virtCurves[i];
dict1[1000 + i] = dict2[idx];
curves[1000 + i] = new Curve(CurveMode.Oe, new() { { 73, idx } }, null);
}
Dictionary<int, List<string>> dict3 = [];
return dict3.Select(e => new GraphEntry(e.Key, curves[e.Key], 50, 120)).ToList();
}
public IEnumerable<GraphEntry> GetQualityGraphEntries() {
Dictionary<int, List<string>> dict1 = [];
Dictionary<decimal, List<string>> dict2 = [];
foreach (var (qualid, q) in GetQualityEntry() ?? []) {
if (q is JsonObject qualityObj) {
foreach (var (selector, node) in qualityObj) {
var val = node?.AsValue();
if (val == null) {
continue;
} else if (val.TryGetValue<decimal>(out var price)) {
if (!dict2.ContainsKey(price)) dict2[price] = [];
dict2[price].Add(selector);
} else if (val.TryGetValue<string>(out var curve)) {
var idx = int.Parse(curve.Split(":")[1] ?? "0");
if (!dict1.ContainsKey(idx)) dict1[idx] = [];
dict1[idx].Add(selector);
}
}
} else if (q is JsonValue qualityVal) {
var idx = qualityVal.GetValue<decimal>();
if (!dict2.ContainsKey(idx)) dict2[idx] = [];
dict2[idx].Add($"{qualid}/");
}
}
// TODO
List<GraphEntry> list = [];
return list;
}
}
}
+49 -61
View File
@@ -7,113 +7,101 @@ using System.Text.Json.Nodes;
namespace Elwig.Helpers.Billing { namespace Elwig.Helpers.Billing {
public class Graph : ICloneable { public class Graph : ICloneable {
public string Type { get; set; }
public int Num { get; set; }
private int MinX { get; set; }
private int MaxX { get; set; }
public string Contracts { get; set; }
public double[] DataX { get; set; } public double[] DataX { get; set; }
public double[] DataY { get; set; } public double[] DataY { get; set; }
public Graph(int num, int minX, int maxX) { public Graph(int minX, int maxX) {
Type = "oe"; DataX = DataGen.Range(minX, maxX + 1);
Num = num; DataY = DataGen.Zeros(maxX - minX + 1);
Contracts = "";
MinX = minX;
MaxX = maxX;
DataX = DataGen.Range(MinX, MaxX + 1);
DataY = DataGen.Zeros(MaxX - MinX + 1);
} }
public Graph(string type, int num, JsonObject graphData, string contracts, int minX, int maxX) { public Graph(Dictionary<double, decimal> data, int minX, int maxX) {
Type = type; DataX = DataGen.Range(minX, maxX + 1);
Num = num; DataY = DataGen.Zeros(maxX - minX + 1);
Contracts = contracts; ParseGraphData(data, minX, maxX);
MinX = minX;
MaxX = maxX;
DataX = DataGen.Range(MinX, MaxX + 1);
DataY = DataGen.Zeros(MaxX - MinX + 1);
ParseGraphData(graphData);
} }
public Graph(string type, int num, int minX, int maxX, string contracts, double[] dataX, double[] dataY) { public Graph(double[] dataX, double[] dataY) {
Type = type;
Num = num;
MinX = minX;
MaxX = maxX;
Contracts = contracts;
DataX = dataX; DataX = dataX;
DataY = dataY; DataY = dataY;
} }
private void ParseGraphData(JsonObject graphData) { private void ParseGraphData(Dictionary<double, decimal> graphPoints, int minX, int maxX) {
var GraphPoints = graphData.ToDictionary(p => int.Parse(p.Key[..^2]), p => (double)p.Value?.AsValue()); if (graphPoints.Keys.Count < 1) {
if (GraphPoints.Keys.Count < 1) {
return; return;
} }
var minKey = GraphPoints.Keys.Order().First(); var minKey = graphPoints.Keys.Order().First();
var maxKey = GraphPoints.Keys.OrderDescending().First(); var maxKey = graphPoints.Keys.OrderDescending().First();
if (!GraphPoints.ContainsKey(MinX)) { if (!graphPoints.ContainsKey(minX)) {
GraphPoints.Add(MinX, GraphPoints.GetValueOrDefault(minKey)); graphPoints.Add(minX, graphPoints.GetValueOrDefault(minKey));
} }
if (!GraphPoints.ContainsKey(MaxX)) { if (!graphPoints.ContainsKey(maxX)) {
GraphPoints.Add(MaxX, GraphPoints.GetValueOrDefault(maxKey)); graphPoints.Add(maxX, graphPoints.GetValueOrDefault(maxKey));
} }
var keys = GraphPoints.Keys.Order().ToArray(); var keys = graphPoints.Keys.Order().ToArray();
for (int i = 0; i < keys.Length; i++) { for (int i = 0; i < keys.Length; i++) {
double point1Value = GraphPoints[keys[i]]; decimal point1Value = graphPoints[keys[i]];
if (i + 1 < keys.Length) { if (i + 1 < keys.Length) {
double point2Value = GraphPoints[keys[i + 1]]; decimal point2Value = graphPoints[keys[i + 1]];
if (point1Value == point2Value) { if (point1Value == point2Value) {
for (int j = keys[i] - MinX; j < keys[i + 1] - MinX; j++) { for (int j = (int)(keys[i] - minX); j < keys[i + 1] - minX; j++) {
DataY[j] = point1Value; DataY[j] = (double)point1Value;
} }
} else { } else {
int steps = Math.Abs(keys[i + 1] - keys[i]); int steps = (int)Math.Abs(keys[i + 1] - keys[i]);
double step = (point2Value - point1Value) / steps; decimal step = (point2Value - point1Value) / steps;
DataY[keys[i] - MinX] = point1Value; DataY[(int)(keys[i] - minX)] = (double)point1Value;
DataY[keys[i + 1] - MinX] = point2Value; DataY[(int)(keys[i + 1] - minX)] = (double)point2Value;
for (int j = keys[i] - MinX; j < keys[i + 1] - MinX - 1; j++) { for (int j = (int)(keys[i] - minX); j < keys[i + 1] - minX - 1; j++) {
DataY[j + 1] = Math.Round(DataY[j] + step, 4); // TODO richtig runden DataY[j + 1] = Math.Round(DataY[j] + (double)step, 4); // TODO richtig runden
} }
} }
} }
else { else {
for (int j = keys[i] - MinX; j < DataX.Length; j++) { for (int j = (int)(keys[i] - minX); j < DataX.Length; j++) {
DataY[j] = point1Value; DataY[j] = (double)point1Value;
} }
} }
} }
} }
public JsonObject ToJson() { public void FlattenGraph(int begin, int end, double value) {
JsonObject graph = new(); for (int i = begin; i <= end; i++) {
DataY[i] = value;
}
}
public void LinearIncreaseGraph(int begin, int end, double inc) {
for (int i = begin; i < end; i++) {
DataY[i + 1] = DataY[i] + inc;
}
}
public JsonObject ToJson(string mode) {
var data = new JsonObject();
if (DataY[0] != DataY[1]) { if (DataY[0] != DataY[1]) {
graph.Add(new KeyValuePair<string, JsonNode?>(DataX[0] + Type.ToLower(), Math.Round(DataY[0], 4))); data.Add(new KeyValuePair<string, JsonNode?>(DataX[0] + mode, Math.Round(DataY[0], 4)));
} }
for (int i = 1; i < DataX.Length - 1; i++) { for (int i = 1; i < DataX.Length - 1; i++) {
if (Math.Round(DataY[i] - DataY[i - 1], 4) != Math.Round(DataY[i + 1] - DataY[i], 4)) { if (Math.Round(DataY[i] - DataY[i - 1], 10) != Math.Round(DataY[i + 1] - DataY[i], 10)) {
graph.Add(new KeyValuePair<string, JsonNode?>(DataX[i] + Type.ToLower(), Math.Round(DataY[i], 4))); data.Add(new KeyValuePair<string, JsonNode?>(DataX[i] + mode, Math.Round(DataY[i], 4)));
} }
} }
if (DataY[^1] != DataY[^2]) { if (DataY[^1] != DataY[^2]) {
graph.Add(new KeyValuePair<string, JsonNode?>(DataX[^1] + Type.ToLower(), Math.Round(DataY[^1], 4))); data.Add(new KeyValuePair<string, JsonNode?>(DataX[^1] + mode, Math.Round(DataY[^1], 4)));
} }
return graph; return data;
} }
public object Clone() { public object Clone() {
return new Graph(Type, Num, MinX, MaxX, Contracts, (double[])DataX.Clone(), (double[])DataY.Clone()); return new Graph((double[])DataX.Clone(), (double[])DataY.Clone());
} }
} }
} }
+70
View File
@@ -0,0 +1,70 @@
using System.Collections.Generic;
using System.Text.Json.Nodes;
namespace Elwig.Helpers.Billing {
public class GraphEntry {
public int Id { get; set; }
public BillingData.CurveMode Mode { get; set; }
public Graph DataGraph { get; set; }
public Graph? GebundenGraph { get; set; }
public decimal? GebundenFlatPrice { get; set; }
public List<string> Contracts { get; set; }
private int MinX { get; set; }
private int MaxX { get; set; }
public GraphEntry(int id, BillingData.CurveMode mode, int minX, int maxX) {
Id = id;
Mode = mode;
MinX = minX;
MaxX = maxX;
DataGraph = new Graph(minX, maxX);
Contracts = [];
}
public GraphEntry(int id, BillingData.CurveMode mode, Dictionary<double, decimal> data, int minX, int maxX) :
this(id, mode, minX, maxX) {
DataGraph = new Graph(data, minX, maxX);
}
public GraphEntry(int id, BillingData.Curve curve, int minX, int maxX) :
this(id, curve.Mode, minX, maxX) {
DataGraph = new Graph(curve.Normal, minX, maxX);
if (curve.Gebunden != null)
GebundenGraph = new Graph(curve.Gebunden, minX, maxX);
}
private GraphEntry(int id, BillingData.CurveMode mode, Graph dataGraph, Graph? gebundenGraph,
decimal? gebundenFlatPrice, List<string> contracts, int minX, int maxX) {
Id = id;
Mode = mode;
MinX = minX;
MaxX = maxX;
DataGraph = dataGraph;
GebundenGraph = gebundenGraph;
GebundenFlatPrice = gebundenFlatPrice;
Contracts = contracts;
}
public JsonObject ToJson() {
var curve = new JsonObject {
["id"] = Id,
["mode"] = Mode.ToString().ToLower(),
};
curve["data"] = DataGraph.ToJson(Mode.ToString().ToLower());
if (GebundenFlatPrice != null) {
curve["geb"] = GebundenFlatPrice.ToString();
} else if (GebundenGraph != null) {
curve["geb"] = GebundenGraph.ToJson(Mode.ToString().ToLower());
}
return curve;
}
public GraphEntry Copy(int id) {
return new GraphEntry(id, Mode, (Graph)DataGraph.Clone(), (Graph?)GebundenGraph?.Clone(), GebundenFlatPrice, Contracts, MinX, MaxX);
}
}
}
+132
View File
@@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.Json.Nodes;
namespace Elwig.Helpers.Billing {
public class PaymentBillingData : BillingData {
protected readonly Dictionary<int, Curve> Curves;
protected readonly Dictionary<string, Curve> PaymentData;
protected readonly Dictionary<string, Curve> QualityData;
protected readonly IEnumerable<string> AttributeVariants;
public PaymentBillingData(JsonObject data, IEnumerable<string> attributeVariants) :
base(data) {
if (attributeVariants.Any(e => e.Any(c => c < 'A' || c > 'Z')))
throw new ArgumentException("Invalid attributeVariants");
AttributeVariants = attributeVariants;
Curves = GetCurves();
PaymentData = GetPaymentData();
QualityData = GetQualityData();
}
public static PaymentBillingData FromJson(string json, IEnumerable<string> attributeVariants) {
return new(ParseJson(json), attributeVariants);
}
private Dictionary<string, Curve> GetData(JsonObject data) {
Dictionary<string, Curve> dict;
if (data["default"] is JsonValue def) {
var c = LookupCurve(def);
dict = AttributeVariants.ToDictionary(e => e, _ => c);
} else {
dict = [];
}
var variants = data.Where(p => !p.Key.StartsWith('/') && p.Key.Length == 2);
var attributes = data.Where(p => p.Key.StartsWith('/'));
var others = data.Where(p => !p.Key.StartsWith('/') && p.Key.Length > 2);
foreach (var (idx, v) in variants) {
var curve = LookupCurve(v?.AsValue() ?? throw new InvalidOperationException());
foreach (var i in AttributeVariants.Where(e => e.StartsWith(idx[..^1]))) {
dict[i] = curve;
}
}
foreach (var (idx, v) in attributes) {
var curve = LookupCurve(v?.AsValue() ?? throw new InvalidOperationException());
foreach (var i in AttributeVariants.Where(e => e[2..] == idx[1..])) {
dict[i] = curve;
}
}
foreach (var (idx, v) in others) {
var curve = LookupCurve(v?.AsValue() ?? throw new InvalidOperationException());
dict[idx.Replace("/", "")] = curve;
}
return dict;
}
protected Dictionary<string, Curve> GetPaymentData() {
var p = GetPaymentEntry();
if (p is JsonValue val) {
var c = LookupCurve(val);
return AttributeVariants.ToDictionary(e => e, _ => c);
}
return GetData(p?.AsObject() ?? throw new InvalidOperationException());
}
protected Dictionary<string, Curve> GetQualityData() {
Dictionary<string, Curve> dict = [];
var q = GetQualityEntry();
if (q == null) return dict;
foreach (var (qualid, data) in q) {
Dictionary<string, Curve> qualDict;
if (data is JsonValue val) {
var c = LookupCurve(val);
qualDict = AttributeVariants.ToDictionary(e => e, _ => c);
} else {
qualDict = GetData(data?.AsObject() ?? throw new InvalidOperationException());
}
foreach (var (idx, d) in qualDict) {
dict[$"{qualid}/{idx}"] = d;
}
}
return dict;
}
public decimal CalculatePrice(string sortid, string? attrid, string qualid, bool gebunden, double oe, double kmw) {
var curve = GetQualityCurve(qualid, sortid, attrid) ?? GetCurve(sortid, attrid);
var d = (gebunden ? curve.Gebunden : null) ?? curve.Normal;
if (d.Count == 1) return d.First().Value;
var r = curve.Mode == CurveMode.Oe ? oe : kmw;
var lt = d.Keys.Where(v => v <= r);
var gt = d.Keys.Where(v => v >= r);
if (!lt.Any()) {
return d[gt.Min()];
} else if (!gt.Any()) {
return d[lt.Max()];
}
var max = lt.Max();
var min = gt.Min();
if (max == min) return d[r];
var p1 = ((decimal)r - (decimal)min) / ((decimal)max - (decimal)min);
var p2 = 1 - p1;
return d[min] * p2 + d[max] * p1;
}
private Curve LookupCurve(JsonValue val) {
if (val.TryGetValue(out string? curve)) {
var curveId = int.Parse(curve.Split(":")[1]);
return Curves[curveId];
} else if (val.TryGetValue(out decimal value)) {
return new(CurveMode.Oe, new() { { 73, value } }, null);
}
throw new InvalidOperationException();
}
protected Curve GetCurve(string sortid, string? attrid) {
return PaymentData[$"{sortid}{attrid ?? ""}"];
}
protected Curve? GetQualityCurve(string qualid, string sortid, string? attrid) {
return QualityData.TryGetValue($"{qualid}/{sortid}{attrid ?? ""}", out var curve) ? curve : null;
}
}
}
+3 -3
View File
@@ -24,9 +24,9 @@ namespace Elwig.Helpers {
public void Read() { public void Read() {
var config = new ConfigurationBuilder().AddIniFile(FileName).Build(); var config = new ConfigurationBuilder().AddIniFile(FileName).Build();
DatabaseFile = Utils.GetAbsolutePath(config["database:file"] ?? "database.sqlite3", App.DataPath); DatabaseFile = Path.Combine(App.DataPath, config["database:file"] ?? "database.sqlite3");
var log = config["database:log"]; var log = config["database:log"];
DatabaseLog = log != null ? Utils.GetAbsolutePath(log, App.DataPath) : null; DatabaseLog = log != null ? Path.Combine(App.DataPath, log) : null;
Branch = config["general:branch"]; Branch = config["general:branch"];
Debug = trueValues.Contains(config["general:debug"]?.ToLower()); Debug = trueValues.Contains(config["general:debug"]?.ToLower());
@@ -35,7 +35,7 @@ namespace Elwig.Helpers {
Scales = ScaleList; Scales = ScaleList;
foreach (var s in scales) { foreach (var s in scales) {
string? scaleLog = config[$"scale.{s}:log"]; string? scaleLog = config[$"scale.{s}:log"];
if (scaleLog != null) scaleLog = Utils.GetAbsolutePath(scaleLog, App.DataPath); if (scaleLog != null) scaleLog = Path.Combine(App.DataPath, scaleLog);
ScaleList.Add([ ScaleList.Add([
s, config[$"scale.{s}:type"], config[$"scale.{s}:model"], config[$"scale.{s}:connection"], s, config[$"scale.{s}:type"], config[$"scale.{s}:model"], config[$"scale.{s}:connection"],
config[$"scale.{s}:empty"], config[$"scale.{s}:filling"], config[$"scale.{s}:limit"], scaleLog config[$"scale.{s}:empty"], config[$"scale.{s}:filling"], config[$"scale.{s}:limit"], scaleLog
+3 -3
View File
@@ -2,6 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder; using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder;
using System.Globalization;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
@@ -263,8 +264,7 @@ namespace Elwig.Helpers.Export {
c = $"<{ct}{add}/>"; c = $"<{ct}{add}/>";
} else if (data is float || data is double || data is byte || data is char || } else if (data is float || data is double || data is byte || data is char ||
data is short || data is ushort || data is int || data is uint || data is long || data is ulong) { data is short || data is ushort || data is int || data is uint || data is long || data is ulong) {
double v = double.Parse(data?.ToString() ?? "0"); // use default culture for ToString and Parse()!
double v = double.Parse(data?.ToString() ?? "0");
if (units != null && units.Length > 0) { if (units != null && units.Length > 0) {
int n = -1; int n = -1;
switch (units[0]) { switch (units[0]) {
@@ -274,7 +274,7 @@ namespace Elwig.Helpers.Export {
} }
if (n >= 0) add = string.Join(" ", add.Split(" ").Select(p => p.StartsWith("table:style-name=") ? $"table:style-name=\"N{n}\"" : p)); if (n >= 0) add = string.Join(" ", add.Split(" ").Select(p => p.StartsWith("table:style-name=") ? $"table:style-name=\"N{n}\"" : p));
} }
c = $"<{ct} office:value-type=\"float\" calcext:value-type=\"float\" office:value=\"{v.ToString()?.Replace(",", ".")}\"{add}><text:p>{data}</text:p></{ct}>"; c = $"<{ct} office:value-type=\"float\" calcext:value-type=\"float\" office:value=\"{v.ToString(CultureInfo.InvariantCulture)}\"{add}><text:p>{data}</text:p></{ct}>";
} else { } else {
c = $"<{ct} office:value-type=\"string\" calcext:value-type=\"string\"{add}><text:p>{data}</text:p></{ct}>"; c = $"<{ct} office:value-type=\"string\" calcext:value-type=\"string\"{add}><text:p>{data}</text:p></{ct}>";
} }
+3 -4
View File
@@ -48,6 +48,9 @@ namespace Elwig.Helpers {
[GeneratedRegex(@"^(.*?) +([0-9].*)$", RegexOptions.Compiled)] [GeneratedRegex(@"^(.*?) +([0-9].*)$", RegexOptions.Compiled)]
private static partial Regex GeneratedAddressRegex(); private static partial Regex GeneratedAddressRegex();
public static readonly string GroupSeparator = "\u202F";
public static readonly string UnitSeparator = "\u00A0";
public static readonly KeyValuePair<string, string>[] PhoneNrTypes = [ public static readonly KeyValuePair<string, string>[] PhoneNrTypes = [
new("landline", "Tel.-Nr. (Festnetz)"), new("landline", "Tel.-Nr. (Festnetz)"),
new("mobile", "Tel.-Nr. (mobil)"), new("mobile", "Tel.-Nr. (mobil)"),
@@ -356,9 +359,5 @@ namespace Elwig.Helpers {
} }
return output.OrderByDescending(l => l.Count()); return output.OrderByDescending(l => l.Count());
} }
public static string GetAbsolutePath(string path, string basePath) {
return (path.Length > 1 && (path[1] == ':' || path[0] == '/' || path[0] == '\\')) ? Path.Combine(basePath, path) : path;
}
} }
} }
+39 -19
View File
@@ -9,14 +9,14 @@ namespace Elwig.Helpers {
private static readonly Dictionary<string, string[][]> PHONE_NRS = new() { private static readonly Dictionary<string, string[][]> PHONE_NRS = new() {
{ "43", new string[][] { { "43", new string[][] {
Array.Empty<string>(), [],
new string[] { "57", "59" }, ["57", "59"],
new string[] { [
"50", "517", "718", "804", "720", "780", "800", "802", "810", "50", "517", "718", "804", "720", "780", "800", "802", "810",
"820", "821", "828", "900", "901", "930", "931", "939", "820", "821", "828", "900", "901", "930", "931", "939",
"650", "651", "652", "653", "655", "657", "659", "660", "661", "650", "651", "652", "653", "655", "657", "659", "660", "661",
"663", "664", "665", "666", "667", "668", "669", "67", "68", "69" "663", "664", "665", "666", "667", "668", "669", "67", "68", "69"
} ]
} }, } },
{ "49", Array.Empty<string[]>() }, { "49", Array.Empty<string[]>() },
{ "48", Array.Empty<string[]>() }, { "48", Array.Empty<string[]>() },
@@ -162,7 +162,7 @@ namespace Elwig.Helpers {
if (text.StartsWith("+43 ")) { if (text.StartsWith("+43 ")) {
var nr = text[4..]; var nr = text[4..];
var vws = PHONE_NRS["43"]; var vws = PHONE_NRS["43"];
if (!text.EndsWith(" ") && v >= 4 && v - 4 < vws.Length && vws[v - 4].Any(vw => nr.StartsWith(vw))) { if (!text.EndsWith(' ') && v >= 4 && v - 4 < vws.Length && vws[v - 4].Any(vw => nr.StartsWith(vw))) {
text += ' '; text += ' ';
} else if (nr == "1") { } else if (nr == "1") {
text += ' '; text += ' ';
@@ -320,18 +320,24 @@ namespace Elwig.Helpers {
return new(true, null); return new(true, null);
} else if (input.Text.Length != 7) { } else if (input.Text.Length != 7) {
return new(false, "Betriebsnummer zu kurz"); return new(false, "Betriebsnummer zu kurz");
} else if (!CheckLfbisNr(input.Text)) {
return new(false, "Prüfsumme der Betriebsnummer ist falsch");
} else {
return new(true, null);
} }
}
public static bool CheckLfbisNr(string nr) {
if (nr.Length != 7 || !nr.All(char.IsAsciiDigit))
return false;
// https://statistik.at/fileadmin/shared/QM/Standarddokumentationen/RW/std_r_land-forstw_register.pdf#page=41 // https://statistik.at/fileadmin/shared/QM/Standarddokumentationen/RW/std_r_land-forstw_register.pdf#page=41
int s = 0, v; int s = 0, v;
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
s += (input.Text[i] - '0') * (7 - i); s += (nr[i] - '0') * (7 - i);
v = (11 - (s % 11)) % 10; v = (11 - (s % 11)) % 10;
if (v != (input.Text[6] - '0')) return v == (nr[6] - '0');
return new(false, "Prüfsumme der Betriebsnummer ist falsch");
return new(true, null);
} }
public static ValidationResult CheckUstIdNr(TextBox input, bool required) { public static ValidationResult CheckUstIdNr(TextBox input, bool required) {
@@ -373,17 +379,11 @@ namespace Elwig.Helpers {
return required ? new(false, "UID ist nicht optional") : new(true, null); return required ? new(false, "UID ist nicht optional") : new(true, null);
if (text.StartsWith("AT")) { if (text.StartsWith("AT")) {
if (text.Length != 11 || text[2] != 'U') if (text.Length != 11 || text[2] != 'U') {
return new(false, "UID ist ungültig"); return new(false, "UID ist ungültig");
} else if (!CheckUstIdNr(text)) {
// http://www.pruefziffernberechnung.de/U/USt-IdNr.shtml
int s = 0, v = 0;
for (int i = 0; i < 7; i++)
s += ((text[i + 3] - '0') * (i % 2 + 1)).ToString().Select(ch => ch - '0').Sum();
v = (96 - s) % 10;
if (v != (text[10] - '0'))
return new(false, "Prüfsumme der UID ist falsch"); return new(false, "Prüfsumme der UID ist falsch");
}
} else { } else {
return new(false, "Not implemented yet"); return new(false, "Not implemented yet");
} }
@@ -391,6 +391,26 @@ namespace Elwig.Helpers {
return new(true, null); return new(true, null);
} }
public static bool CheckUstIdNr(string nr) {
if (nr.Length < 4 || nr.Length > 14 || !nr.All(char.IsAsciiLetterOrDigit))
return false;
if (nr.StartsWith("AT")) {
if (nr.Length != 11 || nr[2] != 'U')
return false;
// http://www.pruefziffernberechnung.de/U/USt-IdNr.shtml
int s = 0, v = 0;
for (int i = 0; i < 7; i++)
s += ((nr[i + 3] - '0') * (i % 2 + 1)).ToString().Select(ch => ch - '0').Sum();
v = (96 - s) % 10;
return v == (nr[10] - '0');
} else {
return false;
}
}
public static ValidationResult CheckMgNr(TextBox input, bool required, AppDbContext ctx) { public static ValidationResult CheckMgNr(TextBox input, bool required, AppDbContext ctx) {
var res = CheckInteger(input, required); var res = CheckInteger(input, required);
if (!res.IsValid) { if (!res.IsValid) {
@@ -26,6 +26,10 @@ namespace Elwig.Models.Dtos {
MgNr = m.MgNr; MgNr = m.MgNr;
} }
public static DeliveryConfirmationData CreateEmpty(int year, Member m) {
return new([], year, m);
}
public static async Task<IDictionary<int, DeliveryConfirmationData>> ForSeason(DbSet<DeliveryPart> table, int year) { public static async Task<IDictionary<int, DeliveryConfirmationData>> ForSeason(DbSet<DeliveryPart> table, int year) {
return (await FromDbSet(table, year)) return (await FromDbSet(table, year))
.GroupBy( .GroupBy(
+1 -1
View File
@@ -71,6 +71,6 @@ namespace Elwig.Models.Dtos {
[NotMapped] [NotMapped]
public (int? Kg, double? Percent) OverUnderDelivery => public (int? Kg, double? Percent) OverUnderDelivery =>
Weight < DeliveryObligation ? (Weight - DeliveryObligation, Weight * 100.0 / DeliveryObligation - 100.0) : Weight < DeliveryObligation ? (Weight - DeliveryObligation, Weight * 100.0 / DeliveryObligation - 100.0) :
Weight > DeliveryRight ? (Weight - DeliveryRight, Weight * 100.0 / DeliveryRight - 100) : (null, null); Weight > DeliveryRight ? (Weight - DeliveryRight, Weight * 100.0 / DeliveryRight - 100.0) : (null, null);
} }
} }
+4 -13
View File
@@ -62,12 +62,8 @@ namespace Elwig.Models.Entities {
[NotMapped] [NotMapped]
public DateOnly? EntryDate { public DateOnly? EntryDate {
get { get => EntryDateString != null ? DateOnly.ParseExact(EntryDateString, "yyyy-MM-dd") : null;
return EntryDateString != null ? DateOnly.ParseExact(EntryDateString, "yyyy-MM-dd") : null; set => EntryDateString = value?.ToString("yyyy-MM-dd");
}
set {
EntryDateString = value?.ToString("yyyy-MM-dd");
}
} }
[Column("exit_date")] [Column("exit_date")]
@@ -75,12 +71,8 @@ namespace Elwig.Models.Entities {
[NotMapped] [NotMapped]
public DateOnly? ExitDate { public DateOnly? ExitDate {
get { get => ExitDateString != null ? DateOnly.ParseExact(ExitDateString, "yyyy-MM-dd") : null;
return ExitDateString != null ? DateOnly.ParseExact(ExitDateString, "yyyy-MM-dd") : null; set => ExitDateString = value?.ToString("yyyy-MM-dd");
}
set {
ExitDateString = value?.ToString("yyyy-MM-dd");
}
} }
[Column("business_shares")] [Column("business_shares")]
@@ -181,7 +173,6 @@ namespace Elwig.Models.Entities {
public int SearchScore(IEnumerable<string> keywords) { public int SearchScore(IEnumerable<string> keywords) {
return Utils.GetSearchScore(new string?[] { return Utils.GetSearchScore(new string?[] {
MgNr.ToString(),
FamilyName, MiddleName, GivenName, FamilyName, MiddleName, GivenName,
BillingAddress?.Name, BillingAddress?.Name,
Comment, Comment,
+31
View File
@@ -0,0 +1,31 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.ComponentModel.DataAnnotations.Schema;
namespace Elwig.Models.Entities {
[Table("member_history"), PrimaryKey("MgNr", "DateString")]
public class MemberHistory {
[Column("mgnr")]
public int MgNr { get; set; }
[Column("date")]
public string DateString { get; set; }
[NotMapped]
public DateOnly Date {
get => DateOnly.ParseExact(DateString, "yyyy-MM-dd");
set => value.ToString("yyyy-MM-dd");
}
[Column("business_shares")]
public int BusinessShares { get; set; }
[Column("type")]
public string Type { get; set; }
[Column("comment")]
public string? Comment { get; set; }
[ForeignKey("MgNr")]
public virtual Member Member { get; private set; }
}
}
+8
View File
@@ -55,6 +55,14 @@ namespace Elwig.Models.Entities {
set => PenaltyNoneValue = value != null ? DecToDb(value.Value) : null; set => PenaltyNoneValue = value != null ? DecToDb(value.Value) : null;
} }
[Column("bs_value")]
public long? BusinessShareValueValue { get; set; }
[NotMapped]
public decimal? BusinessShareValue {
get => BusinessShareValueValue != null ? DecFromDb(BusinessShareValueValue.Value) : null;
set => BusinessShareValueValue = value != null ? DecToDb(value.Value) : null;
}
[Column("start_date")] [Column("start_date")]
public string? StartDateString { get; set; } public string? StartDateString { get; set; }
@@ -9,7 +9,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<PublishDir>bin\Publish</PublishDir> <PublishDir>bin\Publish</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol> <PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId> <_TargetId>Folder</_TargetId>
<TargetFramework>net7.0-windows</TargetFramework> <TargetFramework>net8.0-windows</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained> <SelfContained>true</SelfContained>
<PublishSingleFile>false</PublishSingleFile> <PublishSingleFile>false</PublishSingleFile>
@@ -8,25 +8,49 @@
"properties": { "properties": {
"mode": {"enum": ["elwig"]}, "mode": {"enum": ["elwig"]},
"version": {"enum": [1]}, "version": {"enum": [1]},
"payment": { "consider_delivery_modifiers": {"type": "boolean"},
"type": ["number", "string", "object"], "consider_contract_penalties": {"type": "boolean"},
"pattern": "^curve:[0-9]+$", "consider_total_penalty": {"type": "boolean"},
"additionalProperties": false, "consider_auto_business_shares": {"type": "boolean"},
"properties": { "payment": {"$ref": "#/definitions/payment_1"},
"default": { "quality": {"$ref": "#/definitions/quality_1"},
"type": ["number", "string"], "curves": {
"pattern": "^curve:[0-9]+$" "type": "array",
} "items": {"$ref": "#/definitions/curve"}
}, }
"patternProperties": { }
"^[A-Z]+$": { }, {
"type": ["number", "string"], "required": ["AuszahlungSorten", "Kurven"],
"pattern": "^curve:[0-9]+$" "properties": {
} "mode": {"enum": ["wgmaster"]},
"AuszahlungSorten": {"$ref": "#/definitions/payment_1"},
"AuszahlungSortenQualitätsstufe": {"$ref": "#/definitions/quality_1"},
"Kurven": {
"type": "array",
"items": {"$ref": "#/definitions/curve"}
}
}
}],
"definitions": {
"payment_1": {
"type": ["number", "string", "object"],
"pattern": "^curve:[0-9]+$",
"additionalProperties": false,
"properties": {
"default": {
"type": ["number", "string"],
"pattern": "^curve:[0-9]+$"
} }
}, },
"quality": { "patternProperties": {
"type": "object", "^([A-Z]{2})?(\/[A-Z]*)?$": {
"type": ["number", "string"],
"pattern": "^curve:[0-9]+$"
}
}
},
"quality_1": {
"type": "object",
"additionalProperties": false, "additionalProperties": false,
"patternProperties": { "patternProperties": {
"^[A-Z]{3}$": { "^[A-Z]{3}$": {
@@ -40,65 +64,14 @@
} }
}, },
"patternProperties": { "patternProperties": {
"^[A-Z]+$": { "^([A-Z]{2})?(\/[A-Z]*)?$": {
"type": ["number", "string"], "type": ["number", "string"],
"pattern": "^curve:[0-9]+$" "pattern": "^curve:[0-9]+$"
} }
} }
} }
} }
}, },
"curves": {
"type": "array",
"items": {"$ref": "#/definitions/curve"}
}
}
}, {
"properties": {
"mode": {"enum": ["wgmaster"]},
"AuszahlungSorten": {
"type": "object",
"additionalProperties": false,
"required": ["Kurven"],
"properties": {
"Kurven": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"minProperties": 1,
"patternProperties": {
"^[0-9]+(\\.[0-9]+)?oe$": {"type": "number"}
}
}
}
},
"patternProperties": {
"^[A-Z]{2}$": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[A-Z]*$": {
"type": "object",
"additionalProperties": false,
"properties": {
"Gebunden": {
"type": "integer",
"minimum": 0
},
"NichtGebunden": {
"type": "integer",
"minimum": 0
}
}
}
}
}
}
}
}
}],
"definitions": {
"curve": { "curve": {
"type": "object", "type": "object",
"required": ["id", "mode", "data"], "required": ["id", "mode", "data"],
@@ -111,15 +84,15 @@
"mode": {"enum": ["oe", "kmw"]}, "mode": {"enum": ["oe", "kmw"]},
"data": { "data": {
"anyOf": [ "anyOf": [
{"type": "number" }, {"type": "number"},
{"$ref": "#/definitions/curve_data"} {"$ref": "#/definitions/curve_data"}
] ]
}, },
"geb": { "geb": {
"anyOf": [ "anyOf": [
{"type": "number"}, {"type": "number"},
{"$ref": "#/definitions/curve_data"} {"$ref": "#/definitions/curve_data"}
] ]
} }
} }
}, },
@@ -128,7 +101,7 @@
"additionalProperties": false, "additionalProperties": false,
"minProperties": 1, "minProperties": 1,
"patternProperties": { "patternProperties": {
"^([0-9]+(\\.[0-9]+)?)(oe|kmw)$": {"type": "number"} "^[<>]?([0-9]+(\\.[0-9]+)?)(oe|kmw)$": {"type": "number"}
} }
} }
} }
+30 -14
View File
@@ -1,17 +1,33 @@
-- schema version 10 to 11 -- schema version 10 to 11
-- Drop columns, if they exist... CREATE TABLE payment_delivery_part_new (
ALTER TABLE payment_delivery_part DROP COLUMN price_1; year INTEGER NOT NULL,
ALTER TABLE payment_delivery_part DROP COLUMN price_2; did INTEGER NOT NULL,
ALTER TABLE payment_delivery_part DROP COLUMN price_3; dpnr INTEGER NOT NULL,
ALTER TABLE payment_delivery_part DROP COLUMN price_4; avnr INTEGER NOT NULL,
ALTER TABLE payment_delivery_part DROP COLUMN price_5;
ALTER TABLE payment_delivery_part DROP COLUMN price_6;
ALTER TABLE payment_delivery_part DROP COLUMN price_7;
ALTER TABLE payment_delivery_part DROP COLUMN price_8;
ALTER TABLE payment_delivery_part DROP COLUMN price_9;
DROP TRIGGER t_payment_delivery_part_i; net_amount INTEGER NOT NULL,
mod_abs INTEGER NOT NULL DEFAULT 0,
mod_rel REAL NOT NULL DEFAULT 0,
amount INTEGER NOT NULL GENERATED ALWAYS AS (ROUND(net_amount * (1 + mod_rel) + mod_abs)) STORED,
CONSTRAINT pk_payment_delivery_part PRIMARY KEY (year, did, dpnr, avnr),
CONSTRAINT fk_payment_delivery_part_delivery_part FOREIGN KEY (year, did, dpnr) REFERENCES delivery_part (year, did, dpnr)
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT fk_payment_delivery_part_payment_variant FOREIGN KEY (year, avnr) REFERENCES payment_variant (year, avnr)
ON UPDATE CASCADE
ON DELETE CASCADE
) STRICT;
INSERT INTO payment_delivery_part_new (year, did, dpnr, avnr, net_amount, mod_abs, mod_rel)
SELECT year, did, dpnr, avnr, net_amount, mod_abs, mod_rel
FROM payment_delivery_part;
PRAGMA writable_schema = ON;
DROP TABLE payment_delivery_part;
ALTER TABLE payment_delivery_part_new RENAME TO payment_delivery_part;
PRAGMA writable_schema = OFF;
DROP TRIGGER IF EXISTS t_payment_delivery_part_i;
CREATE TRIGGER t_payment_delivery_part_i CREATE TRIGGER t_payment_delivery_part_i
AFTER INSERT ON payment_delivery_part FOR EACH ROW AFTER INSERT ON payment_delivery_part FOR EACH ROW
BEGIN BEGIN
@@ -20,7 +36,7 @@ BEGIN
ON CONFLICT DO UPDATE SET net_amount = net_amount + excluded.net_amount; ON CONFLICT DO UPDATE SET net_amount = net_amount + excluded.net_amount;
END; END;
DROP TRIGGER t_payment_delivery_part_u; DROP TRIGGER IF EXISTS t_payment_delivery_part_u;
CREATE TRIGGER t_payment_delivery_part_u CREATE TRIGGER t_payment_delivery_part_u
AFTER UPDATE ON payment_delivery_part FOR EACH ROW AFTER UPDATE ON payment_delivery_part FOR EACH ROW
BEGIN BEGIN
@@ -32,7 +48,7 @@ BEGIN
ON CONFLICT DO UPDATE SET net_amount = net_amount + excluded.net_amount; ON CONFLICT DO UPDATE SET net_amount = net_amount + excluded.net_amount;
END; END;
DROP TRIGGER t_payment_delivery_part_d; DROP TRIGGER IF EXISTS t_payment_delivery_part_d;
CREATE TRIGGER t_payment_delivery_part_d CREATE TRIGGER t_payment_delivery_part_d
AFTER DELETE ON payment_delivery_part FOR EACH ROW AFTER DELETE ON payment_delivery_part FOR EACH ROW
BEGIN BEGIN
@@ -41,7 +57,7 @@ BEGIN
WHERE (year, avnr, mgnr) IN (SELECT year, OLD.avnr, mgnr FROM delivery WHERE (year, did) = (OLD.year, OLD.did)); WHERE (year, avnr, mgnr) IN (SELECT year, OLD.avnr, mgnr FROM delivery WHERE (year, did) = (OLD.year, OLD.did));
END; END;
DROP TRIGGER t_payment_delivery_part_bucket_u; DROP TRIGGER IF EXISTS t_payment_delivery_part_bucket_u;
CREATE TRIGGER t_payment_delivery_part_bucket_u CREATE TRIGGER t_payment_delivery_part_bucket_u
AFTER UPDATE ON payment_delivery_part_bucket FOR EACH ROW AFTER UPDATE ON payment_delivery_part_bucket FOR EACH ROW
BEGIN BEGIN
+31
View File
@@ -0,0 +1,31 @@
-- schema version 11 to 12
ALTER TABLE season ADD COLUMN bs_value INTEGER;
CREATE TABLE member_history (
mgnr INTEGER NOT NULL,
date TEXT NOT NULL CHECK (date REGEXP '^[1-9][0-9]{3}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$') DEFAULT CURRENT_DATE,
business_shares INTEGER NOT NULL,
type TEXT NOT NULL CHECK (type REGEXP '^[a-z_]+$'),
comment TEXT DEFAULT NULL,
CONSTRAINT pk_member_history PRIMARY KEY (mgnr, date),
CONSTRAINT fk_member_history_member FOREIGN KEY (mgnr) REFERENCES member (mgnr)
ON UPDATE CASCADE
ON DELETE CASCADE
) STRICT;
CREATE VIEW v_total_under_delivery AS
SELECT s.year, m.mgnr, m.business_shares,
m.business_shares * s.min_kg_per_bs AS min_kg,
m.business_shares * s.max_kg_per_bs AS max_kg,
COALESCE(d.sum, 0) AS weight,
IIF(COALESCE(d.sum, 0) < m.business_shares * s.min_kg_per_bs,
COALESCE(d.sum, 0) - m.business_shares * s.min_kg_per_bs,
IIF(COALESCE(d.sum, 0) > m.business_shares * s.max_kg_per_bs,
COALESCE(d.sum, 0) - m.business_shares * s.max_kg_per_bs,
0)) AS diff
FROM member m, season s
LEFT JOIN v_stat_member d ON (d.year, d.mgnr) = (s.year, m.mgnr)
ORDER BY s.year, m.mgnr;
+6 -2
View File
@@ -375,14 +375,14 @@
</Grid> </Grid>
</TabItem> </TabItem>
<TabItem Header="Saisons"> <TabItem Header="Saisons" x:Name="Seasons">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="180"/> <ColumnDefinition Width="180"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="180"/> <RowDefinition Height="205"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
@@ -446,6 +446,10 @@
<Label Content="Strafe (Nicht-Lieferung):" Margin="10,130,0,10" Grid.Column="2"/> <Label Content="Strafe (Nicht-Lieferung):" Margin="10,130,0,10" Grid.Column="2"/>
<ctrl:UnitTextBox x:Name="SeasonPenaltyNoneInput" Unit="€" TextChanged="SeasonPenaltyInput_TextChanged" <ctrl:UnitTextBox x:Name="SeasonPenaltyNoneInput" Unit="€" TextChanged="SeasonPenaltyInput_TextChanged"
Grid.Column="3" Width="68" Margin="0,130,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/> Grid.Column="3" Width="68" Margin="0,130,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Label Content="GA-Wert:" Margin="10,160,0,10" Grid.Column="2"/>
<ctrl:UnitTextBox x:Name="SeasonBsValueInput" Unit="€/GA" TextChanged="SeasonPenaltyInput_TextChanged"
Grid.Column="3" Width="85" Margin="0,160,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</Grid> </Grid>
<GroupBox Grid.Column="1" Grid.Row="1" Header="Zu-/Abschläge" Margin="0,0,10,10" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"> <GroupBox Grid.Column="1" Grid.Row="1" Header="Zu-/Abschläge" Margin="0,0,10,10" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
@@ -125,6 +125,7 @@ namespace Elwig.Windows {
if (old != null) _branches[old] = id; if (old != null) _branches[old] = id;
branch.ZwstId = id; branch.ZwstId = id;
branch.Name = BranchNameInput.Text; branch.Name = BranchNameInput.Text;
branch.CountryNum = 40;
branch.PostalDestId = (BranchOrtInput.SelectedItem as AT_PlzDest)?.Id; branch.PostalDestId = (BranchOrtInput.SelectedItem as AT_PlzDest)?.Id;
branch.Address = BranchAddressInput.Text; branch.Address = BranchAddressInput.Text;
branch.PhoneNr = BranchPhoneNrInput.Text; branch.PhoneNr = BranchPhoneNrInput.Text;
+5 -4
View File
@@ -52,13 +52,13 @@ namespace Elwig.Windows {
var year = (SeasonList.SelectedItem as Season)?.Year; var year = (SeasonList.SelectedItem as Season)?.Year;
foreach (var (modid, _) in _mods.Where(m => m.Value == null)) { foreach (var (modid, _) in _mods.Where(m => m.Value == null)) {
Context.Remove(Context.Modifiers.Find(new object?[] { year, modid })); Context.Remove(Context.Modifiers.Find(year, modid));
} }
foreach (var (mod, old) in _modIds) { foreach (var (mod, old) in _modIds) {
mod.ModId = old; mod.ModId = old;
} }
foreach (var (old, modid) in _mods.Where(m => m.Value != null)) { foreach (var (old, modid) in _mods.Where(m => m.Value != null)) {
Context.Update(Context.Modifiers.Find(new object?[] { year, old })); Context.Update(Context.Modifiers.Find(year, old));
} }
await Context.SaveChangesAsync(); await Context.SaveChangesAsync();
@@ -102,8 +102,9 @@ namespace Elwig.Windows {
if (_modList == null || SeasonList.SelectedItem is not Season s) return; if (_modList == null || SeasonList.SelectedItem is not Season s) return;
_modChanged = true; _modChanged = true;
var idx = (SeasonModifierList.SelectedIndex != -1) ? SeasonModifierList.SelectedIndex + 1 : _modList.Count; var idx = (SeasonModifierList.SelectedIndex != -1) ? SeasonModifierList.SelectedIndex + 1 : _modList.Count;
var item = Context.CreateProxy<Modifier>(); var item = new Modifier {
item.Year = s.Year; Year = s.Year
};
_modList.Insert(idx, item); _modList.Insert(idx, item);
SeasonModifierList.SelectedIndex = idx; SeasonModifierList.SelectedIndex = idx;
UpdateButtons(); UpdateButtons();
@@ -43,12 +43,14 @@ namespace Elwig.Windows {
SeasonPenaltyPerKgInput.Text = s.PenaltyPerKg?.ToString() ?? ""; SeasonPenaltyPerKgInput.Text = s.PenaltyPerKg?.ToString() ?? "";
SeasonPenaltyInput.Text = s.PenaltyAmount?.ToString() ?? ""; SeasonPenaltyInput.Text = s.PenaltyAmount?.ToString() ?? "";
SeasonPenaltyNoneInput.Text = s.PenaltyNone?.ToString() ?? ""; SeasonPenaltyNoneInput.Text = s.PenaltyNone?.ToString() ?? "";
SeasonBsValueInput.Text = s.BusinessShareValue?.ToString() ?? "";
var sym = s.Currency.Symbol ?? ""; var sym = s.Currency.Symbol ?? "";
SeasonModifierAbsInput.Unit = $"{sym}/kg"; SeasonModifierAbsInput.Unit = $"{sym}/kg";
SeasonPenaltyPerKgInput.Unit = $"{sym}/kg"; SeasonPenaltyPerKgInput.Unit = $"{sym}/kg";
SeasonPenaltyInput.Unit = sym; SeasonPenaltyInput.Unit = sym;
SeasonPenaltyNoneInput.Unit = sym; SeasonPenaltyNoneInput.Unit = sym;
SeasonBsValueInput.Unit = $"{sym}/GA";
AreaCommitmentTypePenaltyPerKgInput.Unit = $"{sym}/kg"; AreaCommitmentTypePenaltyPerKgInput.Unit = $"{sym}/kg";
AreaCommitmentTypePenaltyInput.Unit = sym; AreaCommitmentTypePenaltyInput.Unit = sym;
AreaCommitmentTypePenaltyNoneInput.Unit = sym; AreaCommitmentTypePenaltyNoneInput.Unit = sym;
@@ -64,6 +66,7 @@ namespace Elwig.Windows {
SeasonPenaltyPerKgInput.Text = ""; SeasonPenaltyPerKgInput.Text = "";
SeasonPenaltyInput.Text = ""; SeasonPenaltyInput.Text = "";
SeasonPenaltyNoneInput.Text = ""; SeasonPenaltyNoneInput.Text = "";
SeasonBsValueInput.Text = "";
} }
_seasonUpdate = false; _seasonUpdate = false;
} }
@@ -85,6 +88,7 @@ namespace Elwig.Windows {
s.PenaltyPerKg = (SeasonPenaltyPerKgInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyPerKgInput.Text) : null; s.PenaltyPerKg = (SeasonPenaltyPerKgInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyPerKgInput.Text) : null;
s.PenaltyAmount = (SeasonPenaltyInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyInput.Text) : null; s.PenaltyAmount = (SeasonPenaltyInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyInput.Text) : null;
s.PenaltyNone = (SeasonPenaltyNoneInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyNoneInput.Text) : null; s.PenaltyNone = (SeasonPenaltyNoneInput.Text.Length > 0) ? decimal.Parse(SeasonPenaltyNoneInput.Text) : null;
s.BusinessShareValue = (SeasonBsValueInput.Text.Length > 0) ? decimal.Parse(SeasonBsValueInput.Text) : null;
UpdateButtons(); UpdateButtons();
} }
+6 -1
View File
@@ -27,7 +27,8 @@ namespace Elwig.Windows {
AreaCommitmentTypeMinKgPerHaInput.TextBox, AreaCommitmentTypePenaltyPerKgInput.TextBox, AreaCommitmentTypeMinKgPerHaInput.TextBox, AreaCommitmentTypePenaltyPerKgInput.TextBox,
AreaCommitmentTypePenaltyInput.TextBox, AreaCommitmentTypePenaltyNoneInput.TextBox, AreaCommitmentTypePenaltyInput.TextBox, AreaCommitmentTypePenaltyNoneInput.TextBox,
SeasonMaxKgPerHaInput.TextBox, SeasonVatNormalInput.TextBox, SeasonVatFlatrateInput.TextBox, SeasonStartInput, SeasonEndInput, SeasonMaxKgPerHaInput.TextBox, SeasonVatNormalInput.TextBox, SeasonVatFlatrateInput.TextBox, SeasonStartInput, SeasonEndInput,
SeasonMinKgPerBsInput.TextBox, SeasonMaxKgPerBsInput.TextBox, SeasonPenaltyPerKgInput.TextBox, SeasonPenaltyInput.TextBox, SeasonPenaltyNoneInput.TextBox, SeasonMinKgPerBsInput.TextBox, SeasonMaxKgPerBsInput.TextBox, SeasonBsValueInput.TextBox,
SeasonPenaltyPerKgInput.TextBox, SeasonPenaltyInput.TextBox, SeasonPenaltyNoneInput.TextBox,
SeasonModifierIdInput, SeasonModifierNameInput, SeasonModifierRelInput.TextBox, SeasonModifierAbsInput.TextBox, SeasonModifierIdInput, SeasonModifierNameInput, SeasonModifierRelInput.TextBox, SeasonModifierAbsInput.TextBox,
}; };
WineAttributeFillLowerInput.Visibility = Visibility.Hidden; WineAttributeFillLowerInput.Visibility = Visibility.Hidden;
@@ -72,6 +73,7 @@ namespace Elwig.Windows {
SeasonPenaltyPerKgInput.TextBox.IsReadOnly = true; SeasonPenaltyPerKgInput.TextBox.IsReadOnly = true;
SeasonPenaltyInput.TextBox.IsReadOnly = true; SeasonPenaltyInput.TextBox.IsReadOnly = true;
SeasonPenaltyNoneInput.TextBox.IsReadOnly = true; SeasonPenaltyNoneInput.TextBox.IsReadOnly = true;
SeasonBsValueInput.TextBox.IsReadOnly = true;
SeasonModifierIdInput.IsReadOnly = true; SeasonModifierIdInput.IsReadOnly = true;
SeasonModifierNameInput.IsReadOnly = true; SeasonModifierNameInput.IsReadOnly = true;
@@ -117,6 +119,7 @@ namespace Elwig.Windows {
SeasonPenaltyPerKgInput.TextBox.IsReadOnly = false; SeasonPenaltyPerKgInput.TextBox.IsReadOnly = false;
SeasonPenaltyInput.TextBox.IsReadOnly = false; SeasonPenaltyInput.TextBox.IsReadOnly = false;
SeasonPenaltyNoneInput.TextBox.IsReadOnly = false; SeasonPenaltyNoneInput.TextBox.IsReadOnly = false;
SeasonBsValueInput.TextBox.IsReadOnly = false;
SeasonModifierIdInput.IsReadOnly = false; SeasonModifierIdInput.IsReadOnly = false;
SeasonModifierNameInput.IsReadOnly = false; SeasonModifierNameInput.IsReadOnly = false;
@@ -261,6 +264,8 @@ namespace Elwig.Windows {
ClearInputStates(); ClearInputStates();
FillInputs(App.Client); FillInputs(App.Client);
LockInputs(); LockInputs();
await HintContextChange();
} }
private void FillInputs(ClientParameters p) { private void FillInputs(ClientParameters p) {
+69 -69
View File
@@ -1,4 +1,4 @@
<local:AdministrationWindow <local:ContextWindow
x:Class="Elwig.Windows.ChartWindow" x:Class="Elwig.Windows.ChartWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -8,7 +8,7 @@
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:ScottPlot="clr-namespace:ScottPlot;assembly=ScottPlot.WPF" xmlns:ScottPlot="clr-namespace:ScottPlot;assembly=ScottPlot.WPF"
mc:Ignorable="d" mc:Ignorable="d"
Title="Auszahlung - Elwig" Height="700" Width="1500" Title="Auszahlung - Elwig" Height="700" Width="1500" MinWidth="1000" MinHeight="500"
Loaded="Window_Loaded"> Loaded="Window_Loaded">
<Window.Resources> <Window.Resources>
@@ -43,94 +43,77 @@
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="19"/> <RowDefinition Height="40"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="330"/> <ColumnDefinition Width="230"/>
<ColumnDefinition Width="1*"/> <ColumnDefinition Width="1*"/>
<ColumnDefinition Width="200"/> <ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid Grid.Row="1" Margin="5,0,0,0"> <Grid Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="42"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="150"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="500"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<DataGrid x:Name="GraphList" AutoGenerateColumns="False" HeadersVisibility="Column" IsReadOnly="True" GridLinesVisibility="None" SelectionMode="Single"
CanUserDeleteRows="False" CanUserResizeRows="False" CanUserAddRows="False"
SelectionChanged="GraphList_SelectionChanged"
Margin="5,15,5,0" Grid.Row="0" FontSize="14" Grid.ColumnSpan="3">
<DataGrid.Columns>
<DataGridTextColumn Header="Nr." Binding="{Binding Num}" Width="40">
<DataGridTextColumn.ElementStyle>
<Style>
<Setter Property="TextBlock.TextWrapping" Value="Wrap" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Typ" Binding="{Binding Type}" Width="40"/>
<DataGridTextColumn Header="Angewandte Verträge" Binding="{Binding Contracts}" Width="4*"/>
</DataGrid.Columns>
</DataGrid>
<Button x:Name="NewButton" Content="Neu" <Label Content="Graph:" Margin="10,0,0,0" FontSize="14" Grid.Column="0" VerticalAlignment="Center"/>
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2" <TextBlock x:Name="GraphNum" Margin="0,0,40,0" FontSize="14" Width="50" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right"/>
Click="NewButton_Click"/>
<Button x:Name="EditButton" Content="Bearbeiten" IsEnabled="False"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2"
Click="EditButton_Click"/>
<Button x:Name="DeleteButton" Content="Löschen" IsEnabled="False"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2"
Click="DeleteButton_Click"/>
<Button x:Name="SaveButton" Content="Speichern" IsEnabled="False" Visibility="Hidden" <Label Content="Für:" Margin="10,0,0,0" FontSize="14" Grid.Column="1" VerticalAlignment="Center"/>
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,5,2.5,10" Grid.Column="0" Grid.Row="2" <xctk:CheckComboBox x:Name="AppliedInput" Margin="0,10,10,10" Grid.Column="1"
Click="SaveButton_Click"/> Delimiter=", " AllItemsSelectedContent="Alle"
<Button x:Name="ResetButton" Content="Zurücksetzen" IsEnabled="False" Visibility="Hidden" Width="400" HorizontalAlignment="Right">
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,2.5,10" Grid.Column="1" Grid.Row="2" <!--<xctk:CheckComboBox.ItemTemplate>
Click="ResetButton_Click"/> <DataTemplate>
<Button x:Name="CancelButton" Content="Abbrechen" IsEnabled="False" Visibility="Hidden" IsCancel="True" <StackPanel Orientation="Horizontal">
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="2.5,5,5,10" Grid.Column="2" Grid.Row="2" <TextBlock Text="{}" Width="40"/>
Click="CancelButton_Click"/> </StackPanel>
</DataTemplate>
</xctk:CheckComboBox.ItemTemplate>-->
</xctk:CheckComboBox>
</Grid> </Grid>
<ListBox x:Name="GraphList" Margin="10,10,35,50" Grid.Column="0" Grid.Row="1" SelectionChanged="GraphList_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Id}" Width="40"/>
<TextBlock Text="{Binding Contracts}" Width="100"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button x:Name="SaveButton" Content="Speichern" IsEnabled="True"
HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="10,5,35,15" Grid.Column="0" Grid.Row="2"
Click="SaveButton_Click"/>
<Button x:Name="AddButton" Content="&#xF8AA;" FontFamily="Segoe MDL2 Assets" FontSize="11" Padding="0,1.5,0,0" ToolTip="Neue Auszahlungsvariante hinzufügen"
VerticalAlignment="Center" HorizontalAlignment="Right" Width="25" Height="25" Margin="5,0,5,60" Grid.Column="0" Grid.Row="1"
Click="AddButton_Click"/>
<Button x:Name="CopyButton" Content="&#xE8C8;" FontFamily="Segoe MDL2 Assets" FontSize="12" Padding="0,0,0,0" IsEnabled="False" ToolTip="Ausgewählte Auszahlungsvariante duplizieren"
VerticalAlignment="Center" HorizontalAlignment="Right" Width="25" Height="25" Margin="0,0,5,0" Grid.Column="0" Grid.Row="1"
Click="CopyButton_Click"/>
<Button x:Name="DeleteButton" Content="&#xF8AB;" FontFamily="Segoe MDL2 Assets" FontSize="11" Padding="0,1.5,0,0" IsEnabled="False" ToolTip="Ausgewählte Auszahlungsvariante löschen"
VerticalAlignment="Center" HorizontalAlignment="Right" Width="25" Height="25" Margin="5,60,5,0" Grid.Column="0" Grid.Row="1"
Click="DeleteButton_Click"/>
<Grid Grid.Row="1" Grid.Column="1"> <Grid Grid.Row="1" Grid.Column="1">
<ScottPlot:WpfPlot x:Name="OechslePricePlot" MouseMove="OechslePricePlot_MouseMove" MouseDown="OechslePricePlot_MouseDown" IsEnabled="False"/> <ScottPlot:WpfPlot x:Name="OechslePricePlot" MouseMove="OechslePricePlot_MouseMove" MouseDown="OechslePricePlot_MouseDown" IsEnabled="False"/>
</Grid> </Grid>
<Grid Grid.Row="1" Grid.Column="2" Margin="0,0,5,0"> <Grid Grid.Row="1" Grid.Column="2" Margin="0,0,5,36">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="120"/> <RowDefinition Height="120"/>
<RowDefinition Height="120"/> <RowDefinition Height="90"/>
<RowDefinition Height="210"/> <RowDefinition Height="210"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="110"/> <RowDefinition Height="110"/>
<RowDefinition Height="42"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<GroupBox Header="Graph" Grid.Row="0" Margin="0,5,5,5"> <GroupBox Header="Datenpunkt" Grid.Row="0" Margin="0,5,5,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="85"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="Nummer:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="GraphNumberInput" Grid.Column="1" HorizontalAlignment="Left" Margin="0,10,0,0" Text="" Width="90" TextChanged="GraphNumberInput_TextChanged" LostFocus="GraphNumberInput_LostFocus"/>
<Label Content="Typ:" Margin="10,45,0,0" Grid.Column="0"/>
<RadioButton x:Name="OechsleGraphType_Input" GroupName="GraphType" Grid.Column="1" Margin="0,45,0,0">Oechsle</RadioButton>
<RadioButton x:Name="KmwGraphType_Input" GroupName="GraphType" Grid.Column="1" Margin="0,60,0,0">KMW</RadioButton>
</Grid>
</GroupBox>
<GroupBox Header="Datenpunkt" Grid.Row="1" Margin="0,5,5,5">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="85"/> <ColumnDefinition Width="85"/>
@@ -146,6 +129,23 @@
</Grid> </Grid>
</GroupBox> </GroupBox>
<GroupBox Header="Gebunden" Grid.Row="1" Margin="0,5,5,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="85"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Margin="10,10,0,0">
<RadioButton GroupName="GebundenType">Fix</RadioButton>
<RadioButton GroupName="GebundenType">Graph</RadioButton>
<RadioButton GroupName="GebundenType" IsChecked="True">Nein</RadioButton>
</StackPanel>
<TextBox x:Name="GebundenBonus" IsReadOnly="True" Grid.Column="1" HorizontalAlignment="Left" Margin="0,12,0,0" Text="" Width="90" TextChanged="GebundenBonus_TextChanged"/>
</Grid>
</GroupBox>
<GroupBox Header="Aktionen" Grid.Row="2" Margin="0,5,5,5"> <GroupBox Header="Aktionen" Grid.Row="2" Margin="0,5,5,5">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
@@ -166,7 +166,7 @@
</Grid> </Grid>
</GroupBox> </GroupBox>
<GroupBox Header="Optionen" Grid.Row="3" Margin="0,5,5,5"> <GroupBox Header="Optionen" Grid.Row="4" Margin="0,5,5,5">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
@@ -186,4 +186,4 @@
</GroupBox> </GroupBox>
</Grid> </Grid>
</Grid> </Grid>
</local:AdministrationWindow> </local:ContextWindow>
+86 -355
View File
@@ -17,10 +17,11 @@ using ScottPlot;
using ScottPlot.Plottable; using ScottPlot.Plottable;
namespace Elwig.Windows { namespace Elwig.Windows {
public partial class ChartWindow : AdministrationWindow { public partial class ChartWindow : ContextWindow {
public readonly int Year; public readonly int Year;
public readonly int AvNr; public readonly int AvNr;
private readonly PaymentVar PaymentVar;
private ScatterPlot OechslePricePlotScatter; private ScatterPlot OechslePricePlotScatter;
private MarkerPlot HighlightedPoint; private MarkerPlot HighlightedPoint;
@@ -38,21 +39,18 @@ namespace Elwig.Windows {
private const int MinOechsle = 50; private const int MinOechsle = 50;
private const int MaxOechsle = 140; private const int MaxOechsle = 140;
private Graph? Graph; private List<GraphEntry> GraphEntries = [];
private GraphEntry? SelectedGraphEntry;
public ChartWindow(int year, int avnr) { public ChartWindow(int year, int avnr) {
InitializeComponent(); InitializeComponent();
Year = year; Year = year;
AvNr = avnr; AvNr = avnr;
var v = Context.PaymentVariants.Find(year, avnr); PaymentVar = Context.PaymentVariants.Find(year, avnr) ?? throw new ArgumentException("PaymentVar not found");
Title = $"{v?.Name} - Lese {year} - Elwig"; Title = $"{PaymentVar?.Name} - Lese {year} - Elwig";
ExemptInputs = [
GraphList, OechsleInput, PriceInput, FreeZoomInput, GradationLinesInput, TooltipInput
];
} }
private void Window_Loaded(object sender, RoutedEventArgs evt) { private void Window_Loaded(object sender, RoutedEventArgs evt) {
LockInputs();
OechslePricePlot.IsEnabled = false; OechslePricePlot.IsEnabled = false;
} }
@@ -61,170 +59,65 @@ namespace Elwig.Windows {
await RefreshGraphListQuery(); await RefreshGraphListQuery();
} }
private static JsonObject? ParseData(PaymentVar variant) { private async Task RefreshGraphListQuery() {
try { var attrVariants = Context.DeliveryParts
return BillingData.ParseJson(variant.Data); .Where(d => d.Year == Year)
} catch (ArgumentException) { .Select(d => $"{d.SortId}{d.AttrId}")
return null; .Distinct()
} .ToList()
} .Union(Context.WineVarieties.Select(v => v.SortId))
.Order()
.ToList();
var data = EditBillingData.FromJson(PaymentVar.Data, attrVariants);
GraphEntries.AddRange(data.GetPaymentGraphEntries());
GraphEntries.AddRange(data.GetQualityGraphEntries());
private async Task RefreshGraphListQuery(bool updateSort = false) { ControlUtils.RenewItemsSource(AppliedInput, attrVariants, g => g);
var paymentVar = await Context.PaymentVariants.FindAsync(Year, AvNr); ControlUtils.RenewItemsSource(GraphList, GraphEntries, g => (g as GraphEntry)?.Id, null, ControlUtils.RenewSourceDefault.IfOnly);
if (paymentVar == null) return;
var data = ParseData(paymentVar);
if (data == null) return;
var auszahlungsSorten = data["AuszahlungSorten"]?.AsObject();
if (auszahlungsSorten == null) {
return;
}
var Graphs = auszahlungsSorten["Kurven"]?.AsArray();
if (Graphs == null) {
return;
}
List<Graph> GraphsList = [];
int i = 1;
foreach (var graph in Graphs) {
GraphsList.Add(new Graph("Oe", i, graph?.AsObject(), ParseContracts(auszahlungsSorten, i - 1), 50, 140));
i++;
}
ControlUtils.RenewItemsSource(GraphList, GraphsList, g => (g as Graph)?.Num);
if (GraphsList.Count == 1) {
GraphList.SelectedIndex = 0;
}
RefreshInputs(); RefreshInputs();
} }
private string ParseContracts(JsonObject auszahlungsSorten, int num) { private string ParseContracts(JsonObject auszahlungsSorten, int num) {
List<string> contracts = []; return "";
foreach (var sorte in auszahlungsSorten) {
if (sorte.Key == "Kurven") continue;
foreach (var attribut in sorte.Value.AsObject()) {
foreach (var bindung in attribut.Value.AsObject()) {
if ((int)bindung.Value.AsValue() == num) {
contracts.Add($"{sorte.Key}/{attribut.Key}/{bindung.Key}");
}
}
}
}
return string.Join("\n", contracts);
} }
private async Task<bool> RemoveGraph(int num) {
var paymentVar = await Context.PaymentVariants.FindAsync(Year, AvNr);
if (paymentVar == null) return false;
var data = ParseData(paymentVar);
if (data == null) return false;
var auszahlungsSorten = data["AuszahlungSorten"]?.AsObject();
if (auszahlungsSorten == null) {
return false;
}
var Graphs = auszahlungsSorten["Kurven"]?.AsObject();
if (Graphs == null) {
return false;
}
int i = 1;
foreach (var graph in Graphs) {
if (i == num) {
Graphs.Remove(graph.Key);
break;
}
i++;
}
foreach (var sorte in auszahlungsSorten) {
if (sorte.Key == "Kurven") continue;
foreach (var attribut in sorte.Value.AsObject()) {
var bindungen = attribut.Value.AsObject();
foreach (var bindung in bindungen) {
int v = (int)bindung.Value;
if (v == num - 1) {
bindungen.Remove(bindung.Key);
} else if (v > num - 1) {
bindungen[bindung.Key] = v - 1;
}
}
}
}
EntityEntry<PaymentVar>? tr = null;
try {
paymentVar.Data = data.ToString();
tr = Context.Update(paymentVar);
await Context.SaveChangesAsync();
} catch (Exception exc) {
if (tr != null) await tr.ReloadAsync();
var str = "Der Eintrag konnte nicht in der Datenbank gelöscht werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Graph löschen", MessageBoxButton.OK, MessageBoxImage.Error);
}
return true;
}
private void RefreshInputs(bool validate = false) { private void RefreshInputs(bool validate = false) {
ResetPlot(); ResetPlot();
ClearInputStates(); if (!PaymentVar.TestVariant) {
if (GraphList.SelectedItem is Graph g) { AddButton.IsEnabled = false;
EditButton.IsEnabled = true; CopyButton.IsEnabled = false;
DeleteButton.IsEnabled = true;
EnableOptionButtons();
FillInputs(g);
} else {
EditButton.IsEnabled = false;
DeleteButton.IsEnabled = false; DeleteButton.IsEnabled = false;
OechsleInput.IsReadOnly = true;
PriceInput.IsReadOnly = true;
} else if (SelectedGraphEntry != null) {
CopyButton.IsEnabled = true;
DeleteButton.IsEnabled = true;
OechsleInput.IsReadOnly = false;
EnableOptionButtons();
FillInputs();
} else {
CopyButton.IsEnabled = false;
DeleteButton.IsEnabled = false;
OechsleInput.IsReadOnly = true;
DisableOptionButtons(); DisableOptionButtons();
ClearOriginalValues();
ClearInputs(validate);
ClearInputStates();
} }
GC.Collect(); GC.Collect();
} }
private void FillInputs(Graph g) { private void FillInputs() {
ClearOriginalValues(); GraphNum.Text = SelectedGraphEntry.Id.ToString();
Graph = (Graph)g.Clone();
GraphNumberInput.Text = Graph.Num.ToString();
if (Graph.Type == "oe") {
OechsleGraphType_Input.IsChecked = true;
} else if (Graph.Type == "kmw") {
KmwGraphType_Input.IsChecked = true;
}
InitPlot(); InitPlot();
OechslePricePlot.IsEnabled = true; OechslePricePlot.IsEnabled = true;
FinishInputFilling();
}
private void InitInputs() {
GraphNumberInput.Text = (GraphList.Items.Count + 1).ToString();
OechsleGraphType_Input.IsChecked = true;
FinishInputFilling();
} }
protected override async Task OnRenewContext() { protected override async Task OnRenewContext() {
await base.OnRenewContext();
await RefreshGraphList(); await RefreshGraphList();
} }
private void InitPlot() { private void InitPlot() {
OechslePricePlotScatter = OechslePricePlot.Plot.AddScatter(Graph.DataX, Graph.DataY); OechslePricePlotScatter = OechslePricePlot.Plot.AddScatter(SelectedGraphEntry.DataGraph.DataX, SelectedGraphEntry.DataGraph.DataY);
OechslePricePlot.Configuration.DoubleClickBenchmark = false; OechslePricePlot.Configuration.DoubleClickBenchmark = false;
OechslePricePlotScatter.LineColor = Color.Blue; OechslePricePlotScatter.LineColor = Color.Blue;
@@ -265,7 +158,6 @@ namespace Elwig.Windows {
} }
private void ResetPlot() { private void ResetPlot() {
Graph = null;
PrimaryMarkedPointIndex = -1; PrimaryMarkedPointIndex = -1;
OechslePricePlot.Plot.Remove(OechslePricePlotScatter); OechslePricePlot.Plot.Remove(OechslePricePlotScatter);
OechslePricePlot.Plot.Clear(); OechslePricePlot.Plot.Clear();
@@ -280,16 +172,12 @@ namespace Elwig.Windows {
} }
private void FlattenGraph(int begin, int end, double value) { private void FlattenGraph(int begin, int end, double value) {
for (int i = begin; i <= end; i++) { SelectedGraphEntry.DataGraph.FlattenGraph(begin, end, value);
Graph.DataY[i] = value;
}
OechslePricePlot.Render(); OechslePricePlot.Render();
} }
private void LinearIncreaseGraph(int begin, int end, double inc) { private void LinearIncreaseGraph(int begin, int end, double inc) {
for (int i = begin; i < end; i++) { SelectedGraphEntry.DataGraph.LinearIncreaseGraph(begin, end, inc);
Graph.DataY[i + 1] = Graph.DataY[i] + inc;
}
OechslePricePlot.Render(); OechslePricePlot.Render();
} }
@@ -380,8 +268,6 @@ namespace Elwig.Windows {
} }
private void OechsleInput_TextChanged(object sender, TextChangedEventArgs evt) { private void OechsleInput_TextChanged(object sender, TextChangedEventArgs evt) {
IntegerInput_TextChanged(sender, evt);
bool success = int.TryParse(OechsleInput.Text, out int oechsle); bool success = int.TryParse(OechsleInput.Text, out int oechsle);
SecondaryMarkedPointIndex = -1; SecondaryMarkedPointIndex = -1;
@@ -390,12 +276,13 @@ namespace Elwig.Windows {
if (success) { if (success) {
if (oechsle >= MinOechsle && oechsle <= MaxOechsle) { if (oechsle >= MinOechsle && oechsle <= MaxOechsle) {
PrimaryMarkedPointIndex = oechsle - MinOechsle; PrimaryMarkedPointIndex = oechsle - MinOechsle;
ChangeMarker(PrimaryMarkedPoint, true, Graph.DataX[PrimaryMarkedPointIndex], Graph.DataY[PrimaryMarkedPointIndex]); ChangeMarker(PrimaryMarkedPoint, true, SelectedGraphEntry.DataGraph.DataX[PrimaryMarkedPointIndex], SelectedGraphEntry.DataGraph.DataY[PrimaryMarkedPointIndex]);
PriceInput.Text = Graph.DataY[PrimaryMarkedPointIndex].ToString(); PriceInput.Text = SelectedGraphEntry.DataGraph.DataY[PrimaryMarkedPointIndex].ToString();
if (IsEditing || IsCreating) EnableActionButtons(); EnableActionButtons();
OechslePricePlot.Render(); OechslePricePlot.Render();
PriceInput.IsReadOnly = false;
return; return;
} }
} }
@@ -405,6 +292,7 @@ namespace Elwig.Windows {
DisableActionButtons(); DisableActionButtons();
PriceInput.Text = ""; PriceInput.Text = "";
OechslePricePlot.Render(); OechslePricePlot.Render();
PriceInput.IsReadOnly = true;
} }
private void PriceInput_TextChanged(object sender, RoutedEventArgs evt) { private void PriceInput_TextChanged(object sender, RoutedEventArgs evt) {
@@ -412,10 +300,8 @@ namespace Elwig.Windows {
bool success = Double.TryParse(PriceInput.Text, out double price); bool success = Double.TryParse(PriceInput.Text, out double price);
if (success) { if (success) {
Graph.DataY[PrimaryMarkedPointIndex] = price; SelectedGraphEntry.DataGraph.DataY[PrimaryMarkedPointIndex] = price;
PrimaryMarkedPoint.Y = price; PrimaryMarkedPoint.Y = price;
SaveButton.IsEnabled = true;
ResetButton.IsEnabled = true;
OechslePricePlot.Refresh(); OechslePricePlot.Refresh();
} }
} }
@@ -425,18 +311,14 @@ namespace Elwig.Windows {
if (PrimaryMarkedPointIndex == -1) { if (PrimaryMarkedPointIndex == -1) {
return; return;
} }
FlattenGraph(0, PrimaryMarkedPointIndex, Graph.DataY[PrimaryMarkedPointIndex]); FlattenGraph(0, PrimaryMarkedPointIndex, SelectedGraphEntry.DataGraph.DataY[PrimaryMarkedPointIndex]);
SaveButton.IsEnabled = true;
ResetButton.IsEnabled = true;
} }
private void RightFlatButton_Click(object sender, RoutedEventArgs evt) { private void RightFlatButton_Click(object sender, RoutedEventArgs evt) {
if (PrimaryMarkedPointIndex == -1) { if (PrimaryMarkedPointIndex == -1) {
return; return;
} }
FlattenGraph(PrimaryMarkedPointIndex, Graph.DataY.Length - 1, Graph.DataY[PrimaryMarkedPointIndex]); FlattenGraph(PrimaryMarkedPointIndex, SelectedGraphEntry.DataGraph.DataY.Length - 1, SelectedGraphEntry.DataGraph.DataY[PrimaryMarkedPointIndex]);
SaveButton.IsEnabled = true;
ResetButton.IsEnabled = true;
} }
private void InterpolateButton_Click(object sender, RoutedEventArgs evt) { private void InterpolateButton_Click(object sender, RoutedEventArgs evt) {
@@ -446,13 +328,11 @@ namespace Elwig.Windows {
} }
var (lowIndex, highIndex) = PrimaryMarkedPointIndex < SecondaryMarkedPointIndex ? (PrimaryMarkedPointIndex, SecondaryMarkedPointIndex): (SecondaryMarkedPointIndex, PrimaryMarkedPointIndex); var (lowIndex, highIndex) = PrimaryMarkedPointIndex < SecondaryMarkedPointIndex ? (PrimaryMarkedPointIndex, SecondaryMarkedPointIndex): (SecondaryMarkedPointIndex, PrimaryMarkedPointIndex);
double step = (Graph.DataY[highIndex] - Graph.DataY[lowIndex]) / steps; double step = (SelectedGraphEntry.DataGraph.DataY[highIndex] - SelectedGraphEntry.DataGraph.DataY[lowIndex]) / steps;
for (int i = lowIndex; i < highIndex - 1; i++) { for (int i = lowIndex; i < highIndex - 1; i++) {
Graph.DataY[i + 1] = Math.Round(Graph.DataY[i] + step, 4); // TODO richtig runden SelectedGraphEntry.DataGraph.DataY[i + 1] = Math.Round(SelectedGraphEntry.DataGraph.DataY[i] + step, 4); // TODO richtig runden
} }
SaveButton.IsEnabled = true;
ResetButton.IsEnabled = true;
} }
private void LinearIncreaseButton_Click(object sender, RoutedEventArgs e) { private void LinearIncreaseButton_Click(object sender, RoutedEventArgs e) {
@@ -463,23 +343,21 @@ namespace Elwig.Windows {
if (priceIncrease == null) { if (priceIncrease == null) {
return; return;
} }
LinearIncreaseGraph(PrimaryMarkedPointIndex, Graph.DataY.Length - 1, priceIncrease.Value); LinearIncreaseGraph(PrimaryMarkedPointIndex, SelectedGraphEntry.DataGraph.DataY.Length - 1, priceIncrease.Value);
SaveButton.IsEnabled = true;
ResetButton.IsEnabled = true;
} }
private void OechslePricePlot_MouseDown(object sender, MouseEventArgs e) { private void OechslePricePlot_MouseDown(object sender, MouseEventArgs e) {
if (!IsCreating && GraphList.SelectedItem == null) { if (GraphList.SelectedItem == null) {
return; return;
} }
if (HoverActive) { if (HoverActive) {
if ((IsEditing || IsCreating) && Keyboard.IsKeyDown(Key.LeftCtrl)) { if (PaymentVar.TestVariant && Keyboard.IsKeyDown(Key.LeftCtrl)) {
if (PrimaryMarkedPointIndex == -1) { if (PrimaryMarkedPointIndex == -1) {
return; return;
} }
SecondaryMarkedPointIndex = HighlightedIndex; SecondaryMarkedPointIndex = HighlightedIndex;
ChangeMarker(SecondaryMarkedPoint, true, Graph.DataX[SecondaryMarkedPointIndex], Graph.DataY[SecondaryMarkedPointIndex]); ChangeMarker(SecondaryMarkedPoint, true, SelectedGraphEntry.DataGraph.DataX[SecondaryMarkedPointIndex], SelectedGraphEntry.DataGraph.DataY[SecondaryMarkedPointIndex]);
InterpolateButton.IsEnabled = true; InterpolateButton.IsEnabled = true;
@@ -487,12 +365,12 @@ namespace Elwig.Windows {
} }
PrimaryMarkedPointIndex = HighlightedIndex; PrimaryMarkedPointIndex = HighlightedIndex;
ChangeMarker(PrimaryMarkedPoint, true, Graph.DataX[PrimaryMarkedPointIndex], Graph.DataY[PrimaryMarkedPointIndex]); ChangeMarker(PrimaryMarkedPoint, true, SelectedGraphEntry.DataGraph.DataX[PrimaryMarkedPointIndex], SelectedGraphEntry.DataGraph.DataY[PrimaryMarkedPointIndex]);
OechsleInput.Text = Graph.DataX[HighlightedIndex].ToString(); OechsleInput.Text = SelectedGraphEntry.DataGraph.DataX[HighlightedIndex].ToString();
PriceInput.Text = Graph.DataY[HighlightedIndex].ToString(); PriceInput.Text = SelectedGraphEntry.DataGraph.DataY[HighlightedIndex].ToString();
if (IsEditing || IsCreating) { if (PaymentVar.TestVariant) {
EnableActionButtons(); EnableActionButtons();
} }
} else { } else {
@@ -509,7 +387,7 @@ namespace Elwig.Windows {
} }
private void OechslePricePlot_MouseMove(object sender, MouseEventArgs e) { private void OechslePricePlot_MouseMove(object sender, MouseEventArgs e) {
if (!IsCreating && GraphList.SelectedItem == null) { if (GraphList.SelectedItem == null) {
return; return;
} }
@@ -546,188 +424,45 @@ namespace Elwig.Windows {
} }
} }
override protected void UpdateButtons() { private int getMaxGraphId() {
if (!IsEditing && !IsCreating) return; return GraphEntries.Count == 0 ? 0 : GraphEntries.Select(g => g.Id).Max();
bool ch = HasChanged, v = IsValid;
} }
private void DisableNewEditDeleteButtons() { private void AddButton_Click(object sender, RoutedEventArgs e) {
NewButton.IsEnabled = false; GraphEntry newGraphEntry = new(getMaxGraphId() + 1, BillingData.CurveMode.Oe, MinOechsle, MaxOechsle);
EditButton.IsEnabled = false; GraphEntries.Add(newGraphEntry);
DeleteButton.IsEnabled = false; GraphList.Items.Refresh();
GraphList.SelectedItem = newGraphEntry;
} }
private void EnableNewEditDeleteButtons() { private void CopyButton_Click(object sender, RoutedEventArgs e) {
NewButton.IsEnabled = true; if (SelectedGraphEntry == null) return;
EditButton.IsEnabled = GraphList.SelectedItem != null;
DeleteButton.IsEnabled = GraphList.SelectedItem != null; GraphEntry newGraphEntry = SelectedGraphEntry.Copy(getMaxGraphId() + 1);
GraphEntries.Add(newGraphEntry);
GraphList.Items.Refresh();
GraphList.SelectedItem = newGraphEntry;
} }
private void ShowSaveResetCancelButtons() { private void DeleteButton_Click(object sender, RoutedEventArgs e) {
SaveButton.IsEnabled = false; if (SelectedGraphEntry == null) return;
ResetButton.IsEnabled = false;
CancelButton.IsEnabled = true;
SaveButton.Visibility = Visibility.Visible;
ResetButton.Visibility = Visibility.Visible;
CancelButton.Visibility = Visibility.Visible;
}
private void HideSaveResetCancelButtons() {
SaveButton.IsEnabled = false;
ResetButton.IsEnabled = false;
CancelButton.IsEnabled = false;
SaveButton.Visibility = Visibility.Hidden;
ResetButton.Visibility = Visibility.Hidden;
CancelButton.Visibility = Visibility.Hidden;
}
private void ShowNewEditDeleteButtons() {
EnableNewEditDeleteButtons();
NewButton.Visibility = Visibility.Visible;
EditButton.Visibility = Visibility.Visible;
DeleteButton.Visibility = Visibility.Visible;
}
private void HideNewEditDeleteButtons() {
DisableNewEditDeleteButtons();
NewButton.Visibility = Visibility.Hidden;
EditButton.Visibility = Visibility.Hidden;
DeleteButton.Visibility = Visibility.Hidden;
}
private void NewButton_Click(object sender, RoutedEventArgs e) {
IsCreating = true;
GraphList.IsEnabled = false;
GraphList.SelectedItem = null;
HideNewEditDeleteButtons();
ShowSaveResetCancelButtons();
UnlockInputs();
PriceInput.IsReadOnly = false;
OechsleInput.IsReadOnly = false;
InitInputs();
FillInputs(new Graph(GraphList.Items.Count + 1, MinOechsle, MaxOechsle));
EnableOptionButtons();
}
private void EditButton_Click(object sender, RoutedEventArgs e) {
if (GraphList.SelectedItem == null) {
return;
}
IsEditing = true;
GraphList.IsEnabled = false;
HideNewEditDeleteButtons();
ShowSaveResetCancelButtons();
UnlockInputs();
PriceInput.IsReadOnly = false;
OechsleInput.IsReadOnly = false;
if (PrimaryMarkedPointIndex != -1) EnableActionButtons();
}
private async void DeleteButton_Click(object sender, RoutedEventArgs e) {
Graph g = (Graph)GraphList.SelectedItem;
if (g == null) return;
var r = MessageBox.Show( var r = MessageBox.Show(
$"Soll der Graph {g.Num} (verwendet in folgenden Verträgen: {g.Contracts}) wirklich unwiderruflich gelöscht werden?", $"Soll der Graph {SelectedGraphEntry.Id} (verwendet in folgenden Verträgen: {SelectedGraphEntry.Contracts}) wirklich gelöscht werden?",
"Graph löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No); "Graph löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (r == MessageBoxResult.Yes) { if (r == MessageBoxResult.Yes) {
bool success = await RemoveGraph(g.Num); GraphEntries.Remove(SelectedGraphEntry);
if (!success) { GraphList.Items.Refresh();
MessageBox.Show("Der Graph konnte nicht gelöscht werden", "Graph löschen", MessageBoxButton.OK, MessageBoxImage.Error);
}
await RefreshGraphList();
} }
} }
private void SaveButton_Click(object sender, RoutedEventArgs e) {
private async void SaveButton_Click(object sender, RoutedEventArgs e) { //TODO SAVE
int? index = await UpdateGraph(Graph);
if (index == null) {
MessageBox.Show("Der Graph konnte nicht gespeichert werden", "Graph speichern", MessageBoxButton.OK, MessageBoxImage.Error);
}
IsEditing = false;
IsCreating = false;
GraphList.IsEnabled = true;
HideSaveResetCancelButtons();
ShowNewEditDeleteButtons();
LockInputs();
PriceInput.IsReadOnly = true;
OechsleInput.IsReadOnly = true;
await RefreshGraphList();
GraphList.SelectedIndex = index.Value;
} }
private async Task<int?> UpdateGraph(Graph g) { private void GraphList_SelectionChanged(object sender, SelectionChangedEventArgs e) {
List<PaymentVar> paymentVars = await Context.PaymentVariants.Where(p => p.Year == Year && p.AvNr == AvNr).ToListAsync(); SelectedGraphEntry = (GraphEntry)GraphList.SelectedItem;
if (paymentVars.Count != 1) {
return null;
}
PaymentVar paymentVar = paymentVars[0];
var data = JsonNode.Parse(paymentVar.Data).AsObject();
var auszahlungsSorten = data["AuszahlungSorten"];
if (auszahlungsSorten == null) {
return null;
}
var Graphs = auszahlungsSorten["Kurven"].AsArray();
if (Graphs == null) {
return null;
}
if (IsEditing) {
Graphs[g.Num - 1] = g.ToJson();
} else if(IsCreating) {
Graphs.Add(g.ToJson());
} else {
return null;
}
EntityEntry<PaymentVar>? tr = null;
try {
paymentVar.Data = data.ToString();
tr = Context.Update(paymentVar);
await Context.SaveChangesAsync();
} catch (Exception exc) {
if (tr != null) await tr.ReloadAsync();
var str = "Der Eintrag konnte nicht in der Datenbank gelöscht werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Graph löschen", MessageBoxButton.OK, MessageBoxImage.Error);
}
return g.Num - 1;
}
private void ResetButton_Click(object sender, RoutedEventArgs e) {
if (IsEditing) {
RefreshInputs();
} else if (IsCreating) {
InitInputs();
}
UpdateButtons();
}
private void CancelButton_Click(object sender, RoutedEventArgs e) {
IsEditing = false;
IsCreating = false;
GraphList.IsEnabled = true;
HideSaveResetCancelButtons();
ShowNewEditDeleteButtons();
DisableActionButtons();
RefreshInputs();
PriceInput.Text = "";
OechsleInput.Text = "";
ClearInputStates();
LockInputs();
PriceInput.IsReadOnly = true;
OechsleInput.IsReadOnly = true;
}
private void GraphList_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) {
RefreshInputs(); RefreshInputs();
//var x = OechslePricePlot.Plot.GetPlottables().OfType<ScatterPlot>(); //var x = OechslePricePlot.Plot.GetPlottables().OfType<ScatterPlot>();
@@ -742,11 +477,7 @@ namespace Elwig.Windows {
} }
private void GraphNumberInput_TextChanged(object sender, TextChangedEventArgs e) { private void GebundenBonus_TextChanged(object sender, TextChangedEventArgs evt) {
}
private void GraphNumberInput_LostFocus(object sender, RoutedEventArgs e) {
} }
} }
+21 -2
View File
@@ -92,8 +92,27 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<TextBox x:Name="SearchInput" Grid.ColumnSpan="3" Margin="5,10,161,0" IsReadOnly="False" <TextBox x:Name="SearchInput" Grid.ColumnSpan="3" Margin="5,10,161,0" IsReadOnly="False"
TextChanged="SearchInput_TextChanged" TextChanged="SearchInput_TextChanged">
ToolTip="Lieferungen filtern und durchsuchen. Die Filter sind beliebig kombinierbar.&#xA;&#xA;Filtern nach:&#xA;Sorte: z.B. GV, ZW, rr, sa, !gv (ausgenommen GV), ...&#xA;Qualitätsstufe: z.B. QUW, kab, ldw, ...&#xA;Gradation: z.B. &gt;73, &lt;15, 17-18, 15-, &gt;17,5, 62-75, ...&#xA;Mitglied: z.B. 1234, 987, ...&#xA;Saison: z.B. 2020, &gt;2015, 2017-2019, &lt;2005, 2019-, ...&#xA;Zweigstelle: z.B. musterort, ...&#xA;Attribute: z.B. kabinett, !kabinett (alle außer kabinett), ...&#xA;Datum: z.B. 1.9., 15.9.-10.10., -15.10.2020, ...&#xA;Uhrzeit: z.B. 06:00-08:00, 18:00-, ...&#xA;Freitext: z.B. Lieferscheinnummern, &quot;quw&quot; (sucht nach dem Text &quot;quw&quot;)"/> <TextBox.ToolTip>
<TextBlock>
Lieferungen filtern und durchsuchen. Die Filter sind beliebig kombinierbar.<LineBreak/>
Groß- und Kleinschreibung ist in den meisten Fällen egal.<LineBreak/>
<LineBreak/>
Filtern nach:<LineBreak/>
<Bold>Sorte</Bold>: z.B. GV, ZW, rr, sa, !gv (ausgenommen GV), ...<LineBreak/>
<Bold>Rot/Weiß</Bold>: z.B. r, Rot, w, weiß, ...<LineBreak/>
<Bold>Qualitätsstufe</Bold>: z.B. QUW, kab, !ldw (ausgenommen LDW), ...<LineBreak/>
<Bold>Gradation</Bold>: z.B. &gt;73, &lt;15, 17-18, 15-, &gt;17,5, 62-75, ...<LineBreak/>
<Bold>Mitglied</Bold>: z.B. 1234, 987, ...<LineBreak/>
<Bold>Saison</Bold>: z.B. 2020, &gt;2015, 2017-2019, &lt;2005, 2019-, ...<LineBreak/>
<Bold>Zweigstelle</Bold>: z.B. musterort, ...<LineBreak/>
<Bold>Attribute</Bold>: z.B. kabinett, !kabinett (alle außer kabinett), ...<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>Freitext</Bold>: z.B. Lieferscheinnummern, Anmerkung, "quw" (sucht nach dem Text "quw")
</TextBlock>
</TextBox.ToolTip>
</TextBox>
<xctk:IntegerUpDown x:Name="SeasonInput" Grid.ColumnSpan="3" Height="25" Width="56" FontSize="14" Minimum="1000" Maximum="9999" <xctk:IntegerUpDown x:Name="SeasonInput" Grid.ColumnSpan="3" Height="25" Width="56" FontSize="14" Minimum="1000" Maximum="9999"
Margin="0,10,100,0" VerticalAlignment="Top" HorizontalAlignment="Right" Margin="0,10,100,0" VerticalAlignment="Top" HorizontalAlignment="Right"
ValueChanged="SeasonInput_ValueChanged"/> ValueChanged="SeasonInput_ValueChanged"/>
+35 -20
View File
@@ -8,6 +8,7 @@ using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.Win32; using Microsoft.Win32;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
@@ -25,7 +26,7 @@ namespace Elwig.Windows {
private bool IsUpdatingGradation = false; private bool IsUpdatingGradation = false;
private Member? Member = null; private Member? Member = null;
private readonly DispatcherTimer Timer; private readonly DispatcherTimer Timer;
private List<string> TextFilter = new(); private List<string> TextFilter = [];
private readonly RoutedCommand CtrlF = new(); private readonly RoutedCommand CtrlF = new();
private string? LastScaleError = null; private string? LastScaleError = null;
@@ -38,22 +39,22 @@ namespace Elwig.Windows {
InitializeComponent(); InitializeComponent();
CtrlF.InputGestures.Add(new KeyGesture(Key.F, ModifierKeys.Control)); CtrlF.InputGestures.Add(new KeyGesture(Key.F, ModifierKeys.Control));
CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput)); CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput));
RequiredInputs = new Control[] { RequiredInputs = [
MgNrInput, MemberInput, MgNrInput, MemberInput,
LsNrInput, DateInput, BranchInput, LsNrInput, DateInput, BranchInput,
SortIdInput, WineVarietyInput, SortIdInput, WineVarietyInput,
GradationOeInput.TextBox, GradationKmwInput.TextBox, WineQualityLevelInput, GradationOeInput.TextBox, GradationKmwInput.TextBox, WineQualityLevelInput,
WineOriginInput, WineKgInput, WineOriginInput, WineKgInput,
WeightInput.TextBox WeightInput.TextBox
}; ];
ExemptInputs = new Control[] { ExemptInputs = [
SearchInput, SeasonInput, TodayOnlyInput, AllSeasonsInput, SearchInput, SeasonInput, TodayOnlyInput, AllSeasonsInput,
DeliveryList, DeliveryPartList, DeliveryList, DeliveryPartList,
MemberAddressField, MemberAddressField,
}; ];
WeighingButtons = new Button[] { WeighingButtons = [
WeighingAButton, WeighingBButton, WeighingCButton, WeighingDButton, WeighingAButton, WeighingBButton, WeighingCButton, WeighingDButton,
}; ];
IsReceipt = receipt; IsReceipt = receipt;
Timer = new DispatcherTimer(); Timer = new DispatcherTimer();
@@ -67,7 +68,7 @@ namespace Elwig.Windows {
DoShowWarningWindows = false; DoShowWarningWindows = false;
if (IsReceipt) { if (IsReceipt) {
Title = "Übernahme - Elwig"; Title = $"Übernahme - {App.BranchName} - Elwig";
TodayOnlyInput.IsChecked = true; TodayOnlyInput.IsChecked = true;
var n = App.Scales.Count; var n = App.Scales.Count;
if (n < 1) WeighingAButton.Visibility = Visibility.Hidden; if (n < 1) WeighingAButton.Visibility = Visibility.Hidden;
@@ -296,7 +297,7 @@ namespace Elwig.Windows {
} }
private async Task<(List<string>, IQueryable<Delivery>, IQueryable<DeliveryPart>, List<string>)> GetFilters() { private async Task<(List<string>, IQueryable<Delivery>, IQueryable<DeliveryPart>, List<string>)> GetFilters() {
List<string> filterNames = new(); List<string> filterNames = [];
IQueryable<Delivery> deliveryQuery = Context.Deliveries; IQueryable<Delivery> deliveryQuery = Context.Deliveries;
if (IsReceipt && App.BranchNum > 1) { if (IsReceipt && App.BranchNum > 1) {
deliveryQuery = deliveryQuery.Where(d => d.ZwstId == App.ZwstId); deliveryQuery = deliveryQuery.Where(d => d.ZwstId == App.ZwstId);
@@ -325,6 +326,7 @@ namespace Elwig.Windows {
var filterVar = new List<string>(); var filterVar = new List<string>();
var filterNotVar = new List<string>(); var filterNotVar = new List<string>();
var filterQual = new List<string>(); var filterQual = new List<string>();
var filterNotQual = new List<string>();
var filterMgNr = new List<int>(); var filterMgNr = new List<int>();
var filterZwst = new List<string>(); var filterZwst = new List<string>();
var filterAttr = new List<string>(); var filterAttr = new List<string>();
@@ -345,7 +347,15 @@ namespace Elwig.Windows {
for (int i = 0; i < filter.Count; i++) { for (int i = 0; i < filter.Count; i++) {
var e = filter[i]; var e = filter[i];
if (e.Length == 2 && var.ContainsKey(e.ToUpper())) { if (e.ToLower() is "r" or "rot") {
filterVar.AddRange(var.Values.Where(v => v.IsRed).Select(v => v.SortId));
filter.RemoveAt(i--);
filterNames.Add("Rotweinsorten");
} else if (e.ToLower() is "w" or "weiß" or "weiss") {
filterVar.AddRange(var.Values.Where(v => v.IsWhite).Select(v => v.SortId));
filter.RemoveAt(i--);
filterNames.Add("Weißweinsorten");
} else if (e.Length == 2 && var.ContainsKey(e.ToUpper())) {
filterVar.Add(e.ToUpper()); filterVar.Add(e.ToUpper());
filter.RemoveAt(i--); filter.RemoveAt(i--);
filterNames.Add(var[e.ToUpper()].Name); filterNames.Add(var[e.ToUpper()].Name);
@@ -357,10 +367,14 @@ namespace Elwig.Windows {
filterQual.Add(e.ToUpper()); filterQual.Add(e.ToUpper());
filter.RemoveAt(i--); filter.RemoveAt(i--);
filterNames.Add(qual[e.ToUpper()].Name); filterNames.Add(qual[e.ToUpper()].Name);
} else if (e.All(char.IsAsciiDigit) && mgnr.ContainsKey(e)) { } else if (e[0] == '!' && qual.ContainsKey(e[1..].ToUpper())) {
filterNotQual.Add(e[1..].ToUpper());
filter.RemoveAt(i--);
filterNames.Add("außer " + qual[e[1..].ToUpper()].Name);
} else if (e.All(char.IsAsciiDigit) && mgnr.TryGetValue(e, out var member)) {
filterMgNr.Add(int.Parse(e)); filterMgNr.Add(int.Parse(e));
filter.RemoveAt(i--); filter.RemoveAt(i--);
filterNames.Add(mgnr[e].AdministrativeName); filterNames.Add(member.AdministrativeName);
} else if (attr.ContainsKey(e.ToLower())) { } else if (attr.ContainsKey(e.ToLower())) {
var a = attr[e.ToLower()]; var a = attr[e.ToLower()];
filterAttr.Add(a.AttrId); filterAttr.Add(a.AttrId);
@@ -376,7 +390,7 @@ namespace Elwig.Windows {
filterZwst.Add(b.ZwstId); filterZwst.Add(b.ZwstId);
filter.RemoveAt(i--); filter.RemoveAt(i--);
filterNames.Add($"Zweigstelle {b.Name}"); filterNames.Add($"Zweigstelle {b.Name}");
} else if (e.StartsWith(">") || e.StartsWith("<")) { } else if (e.StartsWith('>') || e.StartsWith('<')) {
if (double.TryParse(e[1..], out var num)) { if (double.TryParse(e[1..], out var num)) {
switch ((e[0], num)) { switch ((e[0], num)) {
case ('>', <= 30): filterKmwGt = num; break; case ('>', <= 30): filterKmwGt = num; break;
@@ -391,8 +405,8 @@ namespace Elwig.Windows {
if (e.Length == 1) filter.RemoveAt(i--); if (e.Length == 1) filter.RemoveAt(i--);
} else if (e.Length > 1 && Utils.FromToRegex.IsMatch(e)) { } else if (e.Length > 1 && Utils.FromToRegex.IsMatch(e)) {
var parts = e.Split("-"); var parts = e.Split("-");
double? from = (parts[0].Length > 0) ? double.Parse(parts[0].Replace(".", ",")) : null; double? from = (parts[0].Length > 0) ? double.Parse(parts[0], CultureInfo.InvariantCulture) : null;
double? to = (parts[1].Length > 0) ? double.Parse(parts[1].Replace(".", ",")) : null; double? to = (parts[1].Length > 0) ? double.Parse(parts[1], CultureInfo.InvariantCulture) : null;
switch ((from, to)) { switch ((from, to)) {
case ( <= 30, <= 30): case ( <= 30, <= 30):
case ( <= 30, null): case ( <= 30, null):
@@ -466,7 +480,7 @@ namespace Elwig.Windows {
filterNames.Add($"bis zum {n2}"); filterNames.Add($"bis zum {n2}");
} }
} }
} else if (e.Length > 2 && e.StartsWith("\"") && e.EndsWith("\"")) { } else if (e.Length > 2 && e.StartsWith('"') && e.EndsWith('"')) {
filter[i] = e[1..^1]; filter[i] = e[1..^1];
} else if (e.Length <= 2) { } else if (e.Length <= 2) {
filter.RemoveAt(i--); filter.RemoveAt(i--);
@@ -491,9 +505,10 @@ namespace Elwig.Windows {
if (filterVar.Count > 0) dpq = dpq.Where(p => filterVar.Contains(p.SortId)); if (filterVar.Count > 0) dpq = dpq.Where(p => filterVar.Contains(p.SortId));
if (filterNotVar.Count > 0) dpq = dpq.Where(p => !filterNotVar.Contains(p.SortId)); if (filterNotVar.Count > 0) dpq = dpq.Where(p => !filterNotVar.Contains(p.SortId));
if (filterQual.Count > 0) dpq = dpq.Where(p => filterQual.Contains(p.QualId)); if (filterQual.Count > 0) dpq = dpq.Where(p => filterQual.Contains(p.QualId));
if (filterNotQual.Count > 0) dpq = dpq.Where(p => !filterNotQual.Contains(p.QualId));
if (filterZwst.Count > 0) dpq = dpq.Where(p => filterZwst.Contains(p.Delivery.ZwstId)); if (filterZwst.Count > 0) dpq = dpq.Where(p => filterZwst.Contains(p.Delivery.ZwstId));
if (filterAttr.Count > 0) dpq = dpq.Where(p => p.AttrId != null && filterAttr.Contains(p.AttrId)); if (filterAttr.Count > 0) dpq = dpq.Where(p => p.AttrId != null && filterAttr.Contains(p.AttrId));
if (filterNotAttr.Count > 0) dpq = dpq.Where(p => p.AttrId == null || !filterAttr.Contains(p.AttrId)); if (filterNotAttr.Count > 0) dpq = dpq.Where(p => p.AttrId == null || !filterNotAttr.Contains(p.AttrId));
if (filterKmwGt > 0) dpq = dpq.Where(p => p.Kmw >= filterKmwGt); if (filterKmwGt > 0) dpq = dpq.Where(p => p.Kmw >= filterKmwGt);
if (filterKmwLt > 0) dpq = dpq.Where(p => p.Kmw < filterKmwLt); if (filterKmwLt > 0) dpq = dpq.Where(p => p.Kmw < filterKmwLt);
if (filterOeGt > 0) dpq = dpq.Where(p => p.Kmw * (4.54 + 0.022 * p.Kmw) >= filterOeGt); if (filterOeGt > 0) dpq = dpq.Where(p => p.Kmw * (4.54 + 0.022 * p.Kmw) >= filterOeGt);
@@ -874,7 +889,7 @@ namespace Elwig.Windows {
p.Acid = (AcidInput.Text == "") ? null : double.Parse(AcidInput.Text); p.Acid = (AcidInput.Text == "") ? null : double.Parse(AcidInput.Text);
p.Comment = (PartCommentInput.Text == "") ? null : PartCommentInput.Text; p.Comment = (PartCommentInput.Text == "") ? null : PartCommentInput.Text;
p.Weight = int.Parse(WeightInput.Text.Replace("\u202f", "")); p.Weight = int.Parse(WeightInput.Text.Replace(Utils.GroupSeparator, ""));
p.ManualWeighing = ManualWeighingInput.IsChecked ?? false; p.ManualWeighing = ManualWeighingInput.IsChecked ?? false;
p.ScaleId = ScaleId; p.ScaleId = ScaleId;
p.WeighingId = WeighingId; p.WeighingId = WeighingId;
@@ -1589,7 +1604,7 @@ namespace Elwig.Windows {
} }
private void GradationKmwInput_LostFocus(object sender, RoutedEventArgs evt) { private void GradationKmwInput_LostFocus(object sender, RoutedEventArgs evt) {
if (GradationKmwInput.Text.EndsWith(",")) GradationKmwInput.Text += "0"; if (GradationKmwInput.Text.EndsWith(',')) GradationKmwInput.Text += "0";
InputLostFocus((TextBox)sender, Validator.CheckGradationKmw); InputLostFocus((TextBox)sender, Validator.CheckGradationKmw);
if (GradationKmwInput.Text.Length != 0 && !GradationKmwInput.Text.Contains(',')) if (GradationKmwInput.Text.Length != 0 && !GradationKmwInput.Text.Contains(','))
GradationKmwInput.Text += ",0"; GradationKmwInput.Text += ",0";
@@ -1687,7 +1702,7 @@ namespace Elwig.Windows {
if (sender is not TextBox tb) return; if (sender is not TextBox tb) return;
if (tb.Text.Length > 0) { if (tb.Text.Length > 0) {
if (!tb.Text.Contains(',')) tb.Text += ",0"; if (!tb.Text.Contains(',')) tb.Text += ",0";
if (tb.Text.EndsWith(",")) tb.Text += "0"; if (tb.Text.EndsWith(',')) tb.Text += "0";
} }
InputLostFocus(tb, Validator.CheckDecimal(tb, false, 2, 1)); InputLostFocus(tb, Validator.CheckDecimal(tb, false, 2, 1));
} }
@@ -76,7 +76,7 @@ namespace Elwig.Dialogs {
IEnumerable<Member> list = await members.ToListAsync(); IEnumerable<Member> list = await members.ToListAsync();
var data = await DeliveryConfirmationData.ForSeason(Context.DeliveryParts, Year); var data = await DeliveryConfirmationData.ForSeason(Context.DeliveryParts, Year);
using var doc = Document.Merge(list.Select(m => using var doc = Document.Merge(list.Select(m =>
new DeliveryConfirmation(Context, Year, m, data[m.MgNr]) { new DeliveryConfirmation(Context, Year, m, data.TryGetValue(m.MgNr, out var d) ? d : DeliveryConfirmationData.CreateEmpty(Year, m)) {
//DoubleSided = true //DoubleSided = true
} }
)); ));
+39 -13
View File
@@ -89,7 +89,29 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<TextBox x:Name="SearchInput" Grid.ColumnSpan="3" Margin="5,7,145,0" IsReadOnly="False" <TextBox x:Name="SearchInput" Grid.ColumnSpan="3" Margin="5,7,145,0" IsReadOnly="False"
TextChanged="SearchInput_TextChanged"/> TextChanged="SearchInput_TextChanged">
<TextBox.ToolTip>
<TextBlock>
Mitglieder filtern und durchsuchen. Die Filter sind beliebig kombinierbar.<LineBreak/>
Groß- und Kleinschreibung ist in den meisten Fällen egal.<LineBreak/>
<LineBreak/>
Filtern nach:<LineBreak/>
<Bold>MgNr.</Bold>: z.B. 1234, 89, ...<LineBreak/>
<Bold>Vor-/Nachname</Bold>: z.B. Max Mustermann... <LineBreak/>
<Bold>Stamm-Zweigstelle</Bold>: z.B. zwst:Matzen, zwst:wolkersdorf, ...<LineBreak/>
<Bold>Stammgemeinde</Bold>: z.B. matzen, Wolkersdorf, ...<LineBreak/>
<Bold>UID</Bold>: z.B. ATU12345678, ...<LineBreak/>
<Bold>Betriebs-Nr.</Bold>: z.B. 0123456, ...<LineBreak/>
<Bold>Bio-Betrieb</Bold>: BIO, !bio (ausgenommen Bio)<LineBreak/>
<Bold>Buchführend</Bold>: buchf[ührend], Pauschal[iert], !buchf[ührend]<LineBreak/>
<Bold>Volllieferant</Bold>: voll[lieferant], !Voll[lieferant] (nicht-Volllieferant)<LineBreak/>
<Bold>Funktionär</Bold>: Funkt[ionär], !funkt[ionär] (nicht-Funktionär)<LineBreak/>
<Bold>Telefon-Nr.</Bold>: z.B. +436641234, ....<LineBreak/>
<Bold>Flächenbindungen</Bold>: z.B. zw, GVK, WRB, ... (Mitglieder mit aktiven Flächenbindungen)<LineBreak/>
<Bold>Freitext</Bold>: z.B. Rechnungsaddresse, Anmerkung, "matzen" (sucht nach dem Text "matzen")
</TextBlock>
</TextBox.ToolTip>
</TextBox>
<CheckBox x:Name="ActiveMemberInput" Content="Nur aktive anzeigen" <CheckBox x:Name="ActiveMemberInput" Content="Nur aktive anzeigen"
Checked="ActiveMemberInput_Changed" Unchecked="ActiveMemberInput_Changed" Checked="ActiveMemberInput_Changed" Unchecked="ActiveMemberInput_Changed"
HorizontalAlignment="Right" Margin="0,12,10,0" VerticalAlignment="Top" Grid.Column="1" Grid.ColumnSpan="2"/> HorizontalAlignment="Right" Margin="0,12,10,0" VerticalAlignment="Top" Grid.Column="1" Grid.ColumnSpan="2"/>
@@ -143,11 +165,11 @@
<Grid Grid.Column="2" Grid.Row="1"> <Grid Grid.Column="2" Grid.Row="1">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="0.8*"/> <RowDefinition Height="108"/>
<RowDefinition Height="0.8*"/> <RowDefinition Height="120"/>
<RowDefinition Height="0.2*"/> <RowDefinition Height="18"/>
<RowDefinition Height="1.3*"/> <RowDefinition Height="*"/>
<RowDefinition Height="0.8*"/> <RowDefinition Height="113"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
@@ -288,11 +310,11 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Label Content="IBAN:" Margin="10,10,0,0" Grid.Column="0"/> <Label Content="IBAN:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="IbanInput" Margin="0,10,10,0" Grid.Column="1" <TextBox x:Name="IbanInput" Margin="0,10,10,0" Grid.Column="1" Width="290" HorizontalAlignment="Left"
TextChanged="IbanInput_TextChanged" LostFocus="IbanInput_LostFocus"/> TextChanged="IbanInput_TextChanged" LostFocus="IbanInput_LostFocus"/>
<Label Content="BIC:" Margin="10,40,0,0" Grid.Column="0"/> <Label Content="BIC:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="BicInput" Margin="0,40,10,0" Grid.Column="1" <TextBox x:Name="BicInput" Margin="0,40,10,0" Grid.Column="1" Width="150" HorizontalAlignment="Left"
TextChanged="BicInput_TextChanged" LostFocus="BicInput_LostFocus"/> TextChanged="BicInput_TextChanged" LostFocus="BicInput_LostFocus"/>
</Grid> </Grid>
</GroupBox> </GroupBox>
@@ -319,6 +341,9 @@
<CheckBox x:Name="OrganicInput" Content="Bio" IsEnabled="False" <CheckBox x:Name="OrganicInput" Content="Bio" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed" Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,45,0,0" VerticalAlignment="Top" IsChecked="False"/> Grid.Column="2" HorizontalAlignment="Left" Margin="10,45,0,0" VerticalAlignment="Top" IsChecked="False"/>
<Button x:Name="OrganicButton" Content="easy-cert.com" Height="25" FontSize="12" IsEnabled="False"
Click="OrganicButton_Click"
Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="60,40,0,0"/>
</Grid> </Grid>
</GroupBox> </GroupBox>
<GroupBox Header="Rechnungsadresse (optional)" Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" Margin="5,5,5,5"> <GroupBox Header="Rechnungsadresse (optional)" Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" Margin="5,5,5,5">
@@ -391,16 +416,17 @@
Grid.Column="2" VerticalAlignment="Top" HorizontalAlignment="Right" Width="25" Height="25" Margin="10,160,10,10"/> Grid.Column="2" VerticalAlignment="Top" HorizontalAlignment="Right" Width="25" Height="25" Margin="10,160,10,10"/>
<Label Content="Anmerkung:" Margin="10,190,0,0" Grid.Column="0"/> <Label Content="Anmerkung:" Margin="10,190,0,0" Grid.Column="0"/>
<TextBox x:Name="CommentInput" Margin="0,190,10,0" Grid.Column="1" Grid.ColumnSpan="2" <TextBox x:Name="CommentInput" Margin="0,190,10,70" Grid.Column="1" Grid.ColumnSpan="2"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"
VerticalAlignment="Stretch" Height="auto" AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible"/>
<Label Content="Kontaktart:" Margin="10,220,0,0" Grid.Column="0"/> <Label Content="Kontaktart:" Margin="10,10,0,10" Grid.Column="0" VerticalAlignment="Bottom"/>
<CheckBox x:Name="ContactPostalInput" Content="Post" IsEnabled="False" <CheckBox x:Name="ContactPostalInput" Content="Post" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed" Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
HorizontalAlignment="Left" Margin="0,225,0,0" VerticalAlignment="Top" Grid.Column="1" Grid.ColumnSpan="2"/> HorizontalAlignment="Left" Margin="0,0,0,15" VerticalAlignment="Bottom" Grid.Column="1" Grid.ColumnSpan="2"/>
<CheckBox x:Name="ContactEmailInput" Content="E-Mail" IsEnabled="False" <CheckBox x:Name="ContactEmailInput" Content="E-Mail" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed" Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
HorizontalAlignment="Left" Margin="60,225,0,0" VerticalAlignment="Top" Grid.Column="1" Grid.ColumnSpan="2"/> HorizontalAlignment="Left" Margin="60,0,0,15" VerticalAlignment="Bottom" Grid.Column="1" Grid.ColumnSpan="2"/>
<Button x:Name="DeliveryButton" Content="Lieferungen" Click="DeliveryButton_Click" IsEnabled="False" <Button x:Name="DeliveryButton" Content="Lieferungen" Click="DeliveryButton_Click" IsEnabled="False"
HorizontalAlignment="Right" Margin="10,00,10,37" Width="150" VerticalAlignment="Bottom" Grid.ColumnSpan="3"/> HorizontalAlignment="Right" Margin="10,00,10,37" Width="150" VerticalAlignment="Bottom" Grid.ColumnSpan="3"/>
+103 -4
View File
@@ -11,6 +11,7 @@ using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.ChangeTracking;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using Elwig.Documents; using Elwig.Documents;
using System.Diagnostics;
namespace Elwig.Windows { namespace Elwig.Windows {
public partial class MemberAdminWindow : AdministrationWindow { public partial class MemberAdminWindow : AdministrationWindow {
@@ -69,14 +70,98 @@ namespace Elwig.Windows {
private async Task RefreshMemberListQuery(bool updateSort = false) { private async Task RefreshMemberListQuery(bool updateSort = false) {
IQueryable<Member> memberQuery = Context.Members; IQueryable<Member> memberQuery = Context.Members;
if (ActiveMemberInput.IsChecked == true) if (ActiveMemberInput.IsChecked == true) memberQuery = memberQuery.Where(m => m.IsActive);
memberQuery = memberQuery.Where(m => m.IsActive);
var filterMgNr = new List<int>();
var filterZwst = new List<string>();
var filterKgNr = new List<int>();
var filterLfbisNr = new List<string>();
var filterUstIdNr = new List<string>();
var filterAreaCom = new List<string>();
var filter = TextFilter.ToList();
if (filter.Count > 0) {
var branches = await Context.Branches.ToListAsync();
var mgnr = await Context.Members.ToDictionaryAsync(m => m.MgNr.ToString(), m => m);
var kgs = await Context.WbKgs.ToDictionaryAsync(k => k.AtKg.Name.ToLower(), k => k.AtKg);
var areaComs = await Context.AreaCommitmentTypes.ToDictionaryAsync(t => $"{t.SortId}{t.AttrId}", t => t);
for (int i = 0; i < filter.Count; i++) {
var e = filter[i];
if (e.Length >= 5 && e.Length <= 10 && "funktionär".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => m.IsFunktionär);
filter.RemoveAt(i--);
} else if (e.Length >= 6 && e.Length <= 11 && e[0] == '!' && "funktionär".StartsWith(e[1..], StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => !m.IsFunktionär);
filter.RemoveAt(i--);
} else if (e.Equals("bio", StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => m.IsOrganic);
filter.RemoveAt(i--);
} else if (e.Equals("!bio", StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => !m.IsOrganic);
filter.RemoveAt(i--);
} else if (e.Length >= 4 && e.Length <= 13 && "volllieferant".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => m.IsVollLieferant);
filter.RemoveAt(i--);
} else if (e.Length >= 5 && e.Length <= 14 && e[0] == '!' && "volllieferant".StartsWith(e[1..], StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => !m.IsVollLieferant);
filter.RemoveAt(i--);
} else if (e.Length >= 5 && e.Length <= 11 && "buchführend".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => m.IsBuchführend);
filter.RemoveAt(i--);
} else if (e.Length >= 6 && e.Length <= 12 && e[0] == '!' && "buchführend".StartsWith(e[1..], StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => !m.IsBuchführend);
filter.RemoveAt(i--);
} else if (e.Length >= 8 && e.Length <= 12 && "pauschaliert".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => !m.IsBuchführend);
filter.RemoveAt(i--);
} else if (e.Length >= 9 && e.Length <= 13 && e[0] == '!' && "pauschaliert".StartsWith(e[1..], StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(m => m.IsBuchführend);
filter.RemoveAt(i--);
} else if (e.All(char.IsAsciiDigit) && mgnr.ContainsKey(e)) {
filterMgNr.Add(int.Parse(e));
filter.RemoveAt(i--);
} else if (kgs.TryGetValue(e, out var kg)) {
filterKgNr.Add(kg.KgNr);
filter.RemoveAt(i--);
} else if (e.StartsWith("zwst:")) {
try {
filterZwst.Add(branches.Where(b => b.Name.StartsWith(e[5..], StringComparison.CurrentCultureIgnoreCase)).Single().ZwstId);
filter.RemoveAt(i--);
} catch (InvalidOperationException) { }
} else if (e.StartsWith('+') && e[1..].All(char.IsAsciiDigit)) {
memberQuery = memberQuery.Where(m => m.TelephoneNumbers.Any(t => t.Number.Replace(" ", "").StartsWith(e)));
filter.RemoveAt(i--);
} else if (areaComs.ContainsKey(e.ToUpper())) {
filterAreaCom.Add(e.ToUpper());
filter.RemoveAt(i--);
} else if (Validator.CheckLfbisNr(e)) {
filterLfbisNr.Add(e);
filter.RemoveAt(i--);
} else if (Validator.CheckUstIdNr(e.ToUpper())) {
filterUstIdNr.Add(e.ToUpper());
filter.RemoveAt(i--);
} else if (e.Length > 2 && e.StartsWith('"') && e.EndsWith('"')) {
filter[i] = e[1..^1];
} else if (e.Length <= 2) {
filter.RemoveAt(i--);
}
}
if (filterMgNr.Count > 0) memberQuery = memberQuery.Where(m => filterMgNr.Contains(m.MgNr));
if (filterKgNr.Count > 0) memberQuery = memberQuery.Where(m => m.DefaultKgNr != null && filterKgNr.Contains((int)m.DefaultKgNr));
if (filterZwst.Count > 0) memberQuery = memberQuery.Where(m => m.ZwstId != null && filterZwst.Contains(m.ZwstId));
if (filterAreaCom.Count > 0) memberQuery = memberQuery.Where(m => m.AreaCommitments.Where(c => c.YearFrom <= Utils.CurrentYear && (c.YearTo ?? int.MaxValue) >= Utils.CurrentYear).Any(c => filterAreaCom.Contains(c.VtrgId)));
if (filterLfbisNr.Count > 0) memberQuery = memberQuery.Where(m => m.LfbisNr != null && filterLfbisNr.Contains(m.LfbisNr));
if (filterUstIdNr.Count > 0) memberQuery = memberQuery.Where(m => m.UstIdNr != null && filterUstIdNr.Contains(m.UstIdNr));
}
List<Member> members = await memberQuery.ToListAsync(); List<Member> members = await memberQuery.ToListAsync();
if (TextFilter.Count > 0) { if (filter.Count > 0 && members.Count > 0) {
var dict = members.AsParallel() var dict = members.AsParallel()
.ToDictionary(m => m, m => m.SearchScore(TextFilter)) .ToDictionary(m => m, m => m.SearchScore(filter))
.OrderByDescending(a => a.Value) .OrderByDescending(a => a.Value)
.ThenBy(a => a.Key.FamilyName) .ThenBy(a => a.Key.FamilyName)
.ThenBy(a => a.Key.GivenName); .ThenBy(a => a.Key.GivenName);
@@ -103,12 +188,14 @@ namespace Elwig.Windows {
DeleteMemberButton.IsEnabled = true; DeleteMemberButton.IsEnabled = true;
AreaCommitmentButton.IsEnabled = true; AreaCommitmentButton.IsEnabled = true;
DeliveryButton.IsEnabled = true; DeliveryButton.IsEnabled = true;
OrganicButton.IsEnabled = true;
FillInputs(m); FillInputs(m);
} else { } else {
EditMemberButton.IsEnabled = false; EditMemberButton.IsEnabled = false;
DeleteMemberButton.IsEnabled = false; DeleteMemberButton.IsEnabled = false;
AreaCommitmentButton.IsEnabled = false; AreaCommitmentButton.IsEnabled = false;
DeliveryButton.IsEnabled = false; DeliveryButton.IsEnabled = false;
OrganicButton.IsEnabled = false;
ClearOriginalValues(); ClearOriginalValues();
ClearDefaultValues(); ClearDefaultValues();
ClearInputs(validate); ClearInputs(validate);
@@ -707,5 +794,17 @@ namespace Elwig.Windows {
App.FocusOriginHierarchy(); App.FocusOriginHierarchy();
} }
} }
private void OrganicButton_Click(object sender, RoutedEventArgs evt) {
if (MemberList.SelectedItem is not Member m)
return;
var url = "https://www.easy-cert.com/htm/suchergebnis.htm?" +
//$"CustomerNumber={m.LfbisNr}&" +
$"Name={(m.BillingAddress?.Name ?? m.Name).Replace(' ', '+')}&" +
$"PostalCode={(m.BillingAddress?.PostalDest ?? m.PostalDest).AtPlz?.Plz}";
Process.Start(new ProcessStartInfo(url) {
UseShellExecute = true,
});
}
} }
} }
+144 -43
View File
@@ -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"
mc:Ignorable="d" mc:Ignorable="d"
Title="Auszahlungsvarianten - Elwig" Height="450" Width="800" MinHeight="400" MinWidth="700"> Title="Auszahlungsvarianten - Elwig" Height="500" Width="820" MinHeight="500" MinWidth="820">
<Window.Resources> <Window.Resources>
<Style TargetType="Label"> <Style TargetType="Label">
<Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="HorizontalAlignment" Value="Left"/>
@@ -41,8 +41,8 @@
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="200"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="200"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<ListBox x:Name="PaymentVariantList" Margin="10,10,35,10" Grid.RowSpan="2" SelectionChanged="PaymentVariantList_SelectionChanged"> <ListBox x:Name="PaymentVariantList" Margin="10,10,35,10" Grid.RowSpan="2" SelectionChanged="PaymentVariantList_SelectionChanged">
@@ -55,56 +55,157 @@
</DataTemplate> </DataTemplate>
</ListBox.ItemTemplate> </ListBox.ItemTemplate>
</ListBox> </ListBox>
<Button x:Name="AddButton" Content="&#xF8AA;" FontFamily="Segoe MDL2 Assets" FontSize="11" Padding="0,1.5,0,0" <Button x:Name="AddButton" Content="&#xF8AA;" FontFamily="Segoe MDL2 Assets" FontSize="11" Padding="0,1.5,0,0" ToolTip="Neue Auszahlungsvariante hinzufügen"
Click="AddButton_Click" VerticalAlignment="Center" HorizontalAlignment="Right" Width="25" Height="25" Margin="5,0,5,60" Grid.RowSpan="2"
VerticalAlignment="Center" HorizontalAlignment="Right" Width="25" Height="25" Margin="5,0,5,60" Grid.RowSpan="2"/> Click="AddButton_Click"/>
<Button x:Name="CopyButton" Content="&#xE8C8;" FontFamily="Segoe MDL2 Assets" FontSize="12" Padding="0,0,0,0" IsEnabled="False" <Button x:Name="CopyButton" Content="&#xE8C8;" FontFamily="Segoe MDL2 Assets" FontSize="12" Padding="0,0,0,0" IsEnabled="False" ToolTip="Ausgewählte Auszahlungsvariante duplizieren"
Click="CopyButton_Click" VerticalAlignment="Center" HorizontalAlignment="Right" Width="25" Height="25" Margin="5,0,5,0" Grid.RowSpan="2"
VerticalAlignment="Center" HorizontalAlignment="Right" Width="25" Height="25" Margin="5,0,5,0" Grid.RowSpan="2"/> Click="CopyButton_Click"/>
<Button x:Name="DeleteButton" Content="&#xF8AB;" FontFamily="Segoe MDL2 Assets" FontSize="11" Padding="0,1.5,0,0" IsEnabled="False" <Button x:Name="DeleteButton" Content="&#xF8AB;" FontFamily="Segoe MDL2 Assets" FontSize="11" Padding="0,1.5,0,0" IsEnabled="False" ToolTip="Ausgewählte Auszahlungsvariante löschen"
Click="DeleteButton_Click" VerticalAlignment="Center" HorizontalAlignment="Right" Width="25" Height="25" Margin="5,60,5,0" Grid.RowSpan="2"
VerticalAlignment="Center" HorizontalAlignment="Right" Width="25" Height="25" Margin="5,60,5,0" Grid.RowSpan="2"/> Click="DeleteButton_Click"/>
<TextBox x:Name="DataInput" Margin="10,200,35,10" Grid.Column="0" Grid.RowSpan="2"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="auto"
AcceptsReturn="True" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Auto"
FontFamily="Cascadia Code Light" FontSize="13"
TextChanged="DataInput_TextChanged"/>
<Label Content="Name:" Margin="10,10,0,0" Grid.Column="1"/> <Label Content="Name:" Margin="10,10,0,0" Grid.Column="1"/>
<TextBox x:Name="NameInput" Width="200" Grid.Column="2" HorizontalAlignment="Left" Margin="0,10,0,0"/> <TextBox x:Name="NameInput" Width="200" Grid.Column="2" HorizontalAlignment="Left" Margin="0,10,0,0"
TextChanged="NameInput_TextChanged"/>
<Label Content="Beschreibung:" Margin="10,40,0,0" Grid.Column="1"/> <Label Content="Beschreibung:" Margin="10,40,0,0" Grid.Column="1"/>
<TextBox x:Name="CommentInput" Grid.Column="2" HorizontalAlignment="Stretch" Margin="0,40,10,0"/> <TextBox x:Name="CommentInput" Grid.Column="2" HorizontalAlignment="Stretch" Margin="0,40,10,0"
TextChanged="CommentInput_TextChanged"/>
<Label Content="Erstellt am:" Margin="10,70,0,0" Grid.Column="1"/> <Label Content="Erstellt am:" Margin="10,70,0,0" Grid.Column="1"/>
<TextBox x:Name="DateInput" Grid.Column="2" Width="77" HorizontalAlignment="Left" Margin="0,70,10,0"/> <TextBox x:Name="DateInput" Grid.Column="2" Width="77" HorizontalAlignment="Left" Margin="0,70,10,0" IsReadOnly="True"/>
<Label Content="Überwiesen am:" Margin="10,100,0,0" Grid.Column="1"/> <Label Content="Überwiesen am:" Margin="10,100,0,0" Grid.Column="1"/>
<TextBox x:Name="TransferDateInput" Grid.Column="2" Width="77" HorizontalAlignment="Left" Margin="0,100,10,0"/> <TextBox x:Name="TransferDateInput" Grid.Column="2" Width="77" HorizontalAlignment="Left" Margin="0,100,10,0"
<Button x:Name="CalculateButton" Content="Berechnen" Grid.Column="1" Grid.ColumnSpan="2" TextChanged="TransferDateInput_TextChanged"/>
Click="CalculateButton_Click"
VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="100" Margin="10,10,115,10"/>
<Button x:Name="EditButton" Content="Bearbeiten" Grid.Column="1" Grid.ColumnSpan="2"
Click="EditButton_Click"
VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="100" Margin="10,10,10,10"/>
<GroupBox Header="Abschluss" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Margin="10,0,10,10"> <Label Content="Berücksichtigen:" Margin="90,70,10,10" Grid.Column="2"/>
<Grid> <CheckBox x:Name="ConsiderModifiersInput" Content="Zu-/Abschläge bei Lieferungen"
<Button x:Name="CommitButton" FontSize="12" Width="160" Height="40" Margin="0,0,10,55" Margin="110,95,10,10" Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Top"
Click="CommitButton_Click" Checked="ConsiderModifiersInput_Changed" Unchecked="ConsiderModifiersInput_Changed"/>
VerticalAlignment="Bottom" HorizontalAlignment="Right"> <CheckBox x:Name="ConsiderPenaltiesInput" Content="Pönalen bei Unterlieferungen (FB)"
<TextBlock TextAlignment="Center">Traubengutschriften<LineBreak/>erstellen</TextBlock> Margin="110,115,10,10" Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Top"
</Button> Checked="ConsiderPenaltiesInput_Changed" Unchecked="ConsiderPenaltiesInput_Changed"/>
<Button x:Name="RevertButton" FontSize="12" Width="160" Height="40" Margin="0,0,10,10" <CheckBox x:Name="ConsiderPenaltyInput" Content="Strafen bei Unterlieferungen (GA)"
Click="RevertButton_Click" Margin="110,135,10,10" Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Top"
VerticalAlignment="Bottom" HorizontalAlignment="Right"> Checked="ConsiderPenaltyInput_Changed" Unchecked="ConsiderPenaltyInput_Changed"/>
<TextBlock TextAlignment="Center">Traubengutschriften<LineBreak/>löschen</TextBlock> <CheckBox x:Name="ConsiderAutoInput" Content="Automatische Nachzeichnungen der GA"
</Button> Margin="110,155,10,10" Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Top"
Checked="ConsiderAutoInput_Changed" Unchecked="ConsiderAutoInput_Changed"/>
<Label Content="&#xF0AE;" FontFamily="Segoe MDL2 Assets" FontSize="16" Grid.Row="0" Grid.Column="2" Margin="108,175,10,10"/>
<ProgressBar x:Name="ProgressBar" Margin="10,0,0,74" Height="27" Width="180" <Grid Grid.Column="1" Grid.ColumnSpan="2" VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="10,10,10,10">
VerticalAlignment="Bottom" HorizontalAlignment="Left"/> <Grid.ColumnDefinitions>
<Button x:Name="ShowButton" Content="Vorschau" FontSize="14" Width="180" Margin="10,10,10,42" Height="27" Tag="Print" IsEnabled="False" <ColumnDefinition Width="110"/>
Click="ShowButton_Click" <ColumnDefinition Width="27"/>
VerticalAlignment="Bottom" HorizontalAlignment="Left"/> <ColumnDefinition Width="110"/>
<Button x:Name="PrintButton" Content="Drucken" FontSize="14" Width="180" Margin="10,10,10,10" Height="27" Tag="Print" IsEnabled="False" <ColumnDefinition Width="27"/>
Click="PrintButton_Click" <ColumnDefinition Width="110"/>
VerticalAlignment="Bottom" HorizontalAlignment="Left"/> </Grid.ColumnDefinitions>
</Grid> <Grid.RowDefinitions>
</GroupBox> <RowDefinition Height="27"/>
<RowDefinition Height="5"/>
<RowDefinition Height="27"/>
</Grid.RowDefinitions>
<Grid.Resources>
<Style TargetType="Label">
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Height" Value="auto"/>
</Style>
<Style TargetType="Button">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="FontSize" Value="14"/>
</Style>
</Grid.Resources>
<Button x:Name="ModifierButton" Content="Zu-/Abschläge" Grid.Column="0" Grid.Row="0"
Click="ModifierButton_Click"/>
<Label Content="&#xF0AF;" FontFamily="Segoe MDL2 Assets" FontSize="16" Grid.Row="0" Grid.Column="1" RenderTransformOrigin="0.5,0.5" >
<Label.RenderTransform>
<TransformGroup>
<RotateTransform Angle="45"/>
<TranslateTransform Y="5"/>
</TransformGroup>
</Label.RenderTransform>
</Label>
<Button x:Name="EditButton" Content="Bearbeiten" Grid.Column="0" Grid.Row="2"
Click="EditButton_Click"/>
<Label Content="&#xF0AF;" FontFamily="Segoe MDL2 Assets" FontSize="16" Grid.Row="2" Grid.Column="1"/>
<Button x:Name="CalculateButton" Content="Berechnen" Grid.Column="2" Grid.Row="2"
Click="CalculateButton_Click"/>
<Label Content="&#xF0AF;" FontFamily="Segoe MDL2 Assets" FontSize="16" Grid.Row="2" Grid.Column="3" x:Name="Arrow3"/>
<Button x:Name="CommitButton" Content="Festsetzen" Grid.Column="4" Grid.Row="2"
Click="CommitButton_Click"/>
<Button x:Name="RevertButton" Content="Freigeben" Grid.Column="4" Grid.Row="2"
Click="RevertButton_Click"/>
<Button x:Name="SaveButton" Content="Speichern" Grid.Column="4" Grid.Row="0"
Click="SaveButton_Click"/>
</Grid>
<Grid Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="250"/>
</Grid.ColumnDefinitions>
<GroupBox Header="Abschluss" Margin="10,0,5,10">
<Grid>
<Button x:Name="ExportButton" Content="Exportieren" FontSize="14" Width="180" Margin="10,10,10,10" Height="27" IsEnabled="False"
Click="ExportButton_Click"
VerticalAlignment="Top" HorizontalAlignment="Left"/>
<ProgressBar x:Name="ProgressBar" Margin="10,10,10,74" Height="27" Width="180"
VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
<Button x:Name="ShowButton" Content="Vorschau" FontSize="14" Width="180" Margin="10,10,10,42" Height="27" Tag="Print" IsEnabled="False"
Click="ShowButton_Click"
VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
<Button x:Name="PrintButton" Content="Drucken" FontSize="14" Width="180" Margin="10,10,10,10" Height="27" Tag="Print" IsEnabled="False"
Click="PrintButton_Click"
VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
</Grid>
</GroupBox>
<GroupBox Header="Ergebnis" Margin="5,0,10,10" Grid.Column="1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="Grid.Column" Value="1"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="TextAlignment" Value="Right"/>
</Style>
</Grid.Resources>
<Label Content="Zu-/Abschläge:" Margin="10,10,0,0"/>
<TextBlock x:Name="ModifierSum" Margin="0,12,10,0"/>
<Label Content="Gesamtbeträge:" Margin="10,40,0,0"/>
<TextBlock x:Name="TotalSum" Margin="0,42,10,0"/>
<Label Content="Mehrwertsteuer:" Margin="10,70,0,0"/>
<TextBlock x:Name="VatSum" Margin="0,72,10,0"/>
<Label Content="Abzüge:" Margin="10,100,0,0"/>
<TextBlock x:Name="DeductionSum" Margin="0,102,10,0"/>
<Label Content="Auszuzahlen:" Margin="10,130,0,0"/>
<TextBlock x:Name="PaymentSum" Margin="0,132,10,0"/>
</Grid>
</GroupBox>
</Grid>
</Grid> </Grid>
</local:ContextWindow> </local:ContextWindow>
+366 -16
View File
@@ -11,16 +11,28 @@ using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using Elwig.Helpers.Billing; using Elwig.Helpers.Billing;
using Elwig.Helpers.Export;
using Microsoft.Win32;
using System.Text.Json;
namespace Elwig.Windows { namespace Elwig.Windows {
public partial class PaymentVariantsWindow : ContextWindow { public partial class PaymentVariantsWindow : ContextWindow {
public readonly int Year; public readonly int Year;
public readonly bool SeasonLocked;
private bool DataValid, DataChanged, NameChanged, CommentChanged, TransferDateValid, TransferDateChanged;
private BillingData? BillingData;
private static readonly JsonSerializerOptions JsonOpt = new() { WriteIndented = true };
public PaymentVariantsWindow(int year) { public PaymentVariantsWindow(int year) {
InitializeComponent(); InitializeComponent();
Year = year; Year = year;
SeasonLocked = Context.Seasons.Find(Year + 1) != null;
Title = $"Auszahlungsvarianten - Lese {Year} - Elwig"; Title = $"Auszahlungsvarianten - Lese {Year} - Elwig";
if (!App.Config.Debug) {
DataInput.Visibility = Visibility.Hidden;
}
} }
protected override async Task OnRenewContext() { protected override async Task OnRenewContext() {
@@ -33,27 +45,118 @@ namespace Elwig.Windows {
var locked = !v.TestVariant; var locked = !v.TestVariant;
DeleteButton.IsEnabled = !locked; DeleteButton.IsEnabled = !locked;
CalculateButton.IsEnabled = !locked; CalculateButton.IsEnabled = !locked;
CommitButton.IsEnabled = !locked; CommitButton.IsEnabled = !locked && !SeasonLocked;
RevertButton.IsEnabled = locked; CommitButton.Visibility = !locked ? Visibility.Visible : Visibility.Hidden;
RevertButton.IsEnabled = locked && !SeasonLocked;
RevertButton.Visibility = locked ? Visibility.Visible : Visibility.Hidden;
Arrow3.Content = locked ? "\xF0B0" : "\xF0AF";
CopyButton.IsEnabled = true; CopyButton.IsEnabled = true;
EditButton.IsEnabled = true; EditButton.Content = locked ? "Ansehen" : "Bearbeiten";
ShowButton.IsEnabled = true; ShowButton.IsEnabled = true;
PrintButton.IsEnabled = true; PrintButton.IsEnabled = true;
ExportButton.IsEnabled = locked;
NameInput.Text = v.Name; NameInput.Text = v.Name;
NameInput.IsReadOnly = false;
CommentInput.Text = v.Comment; CommentInput.Text = v.Comment;
CommentInput.IsReadOnly = false;
DateInput.Text = $"{v.Date:dd.MM.yyyy}";
DateInput.IsReadOnly = false;
TransferDateInput.Text = $"{v.TransferDate:dd.MM.yyyy}";
TransferDateInput.IsReadOnly = false;
try {
BillingData = BillingData.FromJson(v.Data);
ConsiderModifiersInput.IsChecked = BillingData.ConsiderDelieryModifiers;
ConsiderModifiersInput.IsEnabled = !locked;
ConsiderPenaltiesInput.IsChecked = BillingData.ConsiderContractPenalties;
ConsiderPenaltiesInput.IsEnabled = !locked;
ConsiderPenaltyInput.IsChecked = BillingData.ConsiderTotalPenalty;
ConsiderPenaltyInput.IsEnabled = !locked;
ConsiderAutoInput.IsChecked = BillingData.ConsiderAutoBusinessShares;
ConsiderAutoInput.IsEnabled = !locked;
DataInput.Text = JsonSerializer.Serialize(BillingData.Data, JsonOpt);
DataInput.IsReadOnly = locked;
} catch {
BillingData = null;
ConsiderModifiersInput.IsChecked = false;
ConsiderModifiersInput.IsEnabled = false;
ConsiderPenaltiesInput.IsChecked = false;
ConsiderPenaltiesInput.IsEnabled = false;
ConsiderPenaltyInput.IsChecked = false;
ConsiderPenaltyInput.IsEnabled = false;
ConsiderAutoInput.IsChecked = false;
ConsiderAutoInput.IsEnabled = false;
DataInput.Text = v.Data;
DataInput.IsEnabled = false;
}
} else { } else {
EditButton.IsEnabled = false; EditButton.Content = "Bearbeiten";
CopyButton.IsEnabled = false; CopyButton.IsEnabled = false;
CalculateButton.IsEnabled = false; CalculateButton.IsEnabled = false;
CommitButton.IsEnabled = false; CommitButton.IsEnabled = false;
CommitButton.Visibility = Visibility.Visible;
RevertButton.IsEnabled = false; RevertButton.IsEnabled = false;
RevertButton.Visibility = Visibility.Hidden;
Arrow3.Content = "\xF0AF";
DeleteButton.IsEnabled = false; DeleteButton.IsEnabled = false;
ShowButton.IsEnabled = false; ShowButton.IsEnabled = false;
PrintButton.IsEnabled = false; PrintButton.IsEnabled = false;
ExportButton.IsEnabled = false;
BillingData = null;
NameInput.Text = ""; NameInput.Text = "";
NameInput.IsReadOnly = true;
CommentInput.Text = ""; CommentInput.Text = "";
CommentInput.IsReadOnly = true;
DateInput.Text = "";
DateInput.IsReadOnly = true;
TransferDateInput.Text = "";
TransferDateInput.IsReadOnly = true;
ConsiderModifiersInput.IsChecked = false;
ConsiderModifiersInput.IsEnabled = false;
ConsiderPenaltiesInput.IsChecked = false;
ConsiderPenaltiesInput.IsEnabled = false;
ConsiderPenaltyInput.IsChecked = false;
ConsiderPenaltyInput.IsEnabled = false;
ConsiderAutoInput.IsChecked = false;
ConsiderAutoInput.IsEnabled = false;
DataInput.Text = "";
DataInput.IsReadOnly = true;
}
UpdateSums();
UpdateSaveButton();
}
private void UpdateSaveButton() {
SaveButton.IsEnabled = PaymentVariantList.SelectedItem != null &&
((DataChanged && DataValid) || NameChanged || CommentChanged ||
(TransferDateChanged && TransferDateValid)) ||
(ConsiderModifiersInput.IsChecked != BillingData?.ConsiderDelieryModifiers) ||
(ConsiderPenaltiesInput.IsChecked != BillingData?.ConsiderContractPenalties) ||
(ConsiderPenaltyInput.IsChecked != BillingData?.ConsiderTotalPenalty) ||
(ConsiderAutoInput.IsChecked != BillingData?.ConsiderAutoBusinessShares);
}
private void UpdateSums() {
if (PaymentVariantList.SelectedItem is PaymentVar v) {
var sym = v.Season.Currency.Symbol;
ModifierSum.Text = $"{v.DeliveryPartPayments.Sum(p => p.Amount - p.NetAmount):N2} {sym}";
TotalSum.Text = $"{v.MemberPayments.Sum(p => p.Amount):N2} {sym}";
if (v.Credits.Count == 0) {
VatSum.Text = $"- {sym}";
DeductionSum.Text = $"- {sym}";
PaymentSum.Text = $"- {sym}";
} else {
VatSum.Text = $"{v.Credits.Sum(c => c.VatAmount):N2} {sym}";
DeductionSum.Text = $"{-v.Credits.Sum(c => c.Modifiers ?? 0):N2} {sym}";
PaymentSum.Text = $"{v.Credits.Sum(c => c.Amount):N2} {sym}";
}
} else {
ModifierSum.Text = "-";
TotalSum.Text = "-";
VatSum.Text = "-";
DeductionSum.Text = "-";
PaymentSum.Text = "-";
} }
} }
@@ -61,16 +164,64 @@ namespace Elwig.Windows {
Update(); Update();
} }
private void AddButton_Click(object sender, RoutedEventArgs evt) { private async void AddButton_Click(object sender, RoutedEventArgs evt) {
try {
PaymentVar v = Context.CreateProxy<PaymentVar>();
v.Year = Year;
v.AvNr = await Context.NextAvNr(Year);
v.Name = "Neue Auszahlungsvariante";
v.TestVariant = true;
v.DateString = $"{DateTime.Today:yyyy-MM-dd}";
v.Data = "{\"mode\": \"elwig\", \"version\": 1, \"payment\": 1.0, \"curves\": []}";
await Context.AddAsync(v);
await Context.SaveChangesAsync();
await App.HintContextChange();
ControlUtils.SelectListBoxItem(PaymentVariantList, v, v => (v as PaymentVar)?.AvNr);
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Auszahlungsvariante erstellen", MessageBoxButton.OK, MessageBoxImage.Error);
}
} }
private void CopyButton_Click(object sender, RoutedEventArgs evt) { private async void CopyButton_Click(object sender, RoutedEventArgs evt) {
if (PaymentVariantList.SelectedItem is not PaymentVar orig) return;
try {
PaymentVar n = Context.CreateProxy<PaymentVar>();
n.Year = orig.Year;
n.AvNr = await Context.NextAvNr(Year);
n.Name = $"{orig.Name} (Kopie)";
n.TestVariant = true;
n.DateString = $"{DateTime.Today:yyyy-MM-dd}";
n.Data = orig.Data;
await Context.AddAsync(n);
await Context.SaveChangesAsync();
await App.HintContextChange();
ControlUtils.SelectListBoxItem(PaymentVariantList, n, v => (v as PaymentVar)?.AvNr);
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Auszahlungsvariante kopieren", MessageBoxButton.OK, MessageBoxImage.Error);
}
} }
private void DeleteButton_Click(object sender, RoutedEventArgs evt) { private async void DeleteButton_Click(object sender, RoutedEventArgs evt) {
if (PaymentVariantList.SelectedItem is not PaymentVar v || !v.TestVariant) return;
try {
Context.Remove(v);
await Context.SaveChangesAsync();
await HintContextChange();
} catch (Exception exc) {
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Auszahlungsvariante löschen", MessageBoxButton.OK, MessageBoxImage.Error);
}
} }
private async void CalculateButton_Click(object sender, RoutedEventArgs evt) { private async void CalculateButton_Click(object sender, RoutedEventArgs evt) {
@@ -78,8 +229,13 @@ namespace Elwig.Windows {
return; return;
CalculateButton.IsEnabled = false; CalculateButton.IsEnabled = false;
Mouse.OverrideCursor = Cursors.AppStarting; Mouse.OverrideCursor = Cursors.AppStarting;
var b = new BillingVariant(v.Year, v.AvNr); try {
await b.Calculate(); var b = new BillingVariant(v.Year, v.AvNr);
await b.Calculate();
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Berechnungsfehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
await App.HintContextChange();
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
CalculateButton.IsEnabled = true; CalculateButton.IsEnabled = true;
} }
@@ -98,28 +254,222 @@ namespace Elwig.Windows {
await Generate(2); await Generate(2);
} }
private async void CommitButton_Click(object sender, RoutedEventArgs e) { private async void CommitButton_Click(object sender, RoutedEventArgs evt) {
if (PaymentVariantList.SelectedValue is not PaymentVar v) if (PaymentVariantList.SelectedValue is not PaymentVar v)
return; return;
CommitButton.IsEnabled = false; CommitButton.IsEnabled = false;
Mouse.OverrideCursor = Cursors.AppStarting; Mouse.OverrideCursor = Cursors.AppStarting;
var b = new BillingVariant(v.Year, v.AvNr); try {
await b.Commit(); var b = new BillingVariant(v.Year, v.AvNr);
await b.Commit();
} catch (Exception exc) {
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
}
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
RevertButton.IsEnabled = true; RevertButton.IsEnabled = true;
await HintContextChange(); await App.HintContextChange();
} }
private async void RevertButton_Click(object sender, RoutedEventArgs e) { private async void RevertButton_Click(object sender, RoutedEventArgs evt) {
if (PaymentVariantList.SelectedValue is not PaymentVar v) if (PaymentVariantList.SelectedValue is not PaymentVar v)
return; return;
var res = MessageBox.Show(
"Sollen wirklich alle festgesetzten Traubengutschriften der ausgewählten Auszahlungsvariante unwiderruflich gelöscht werden?\n\n" +
"Dies ist im Allgemeinen nie empfohlen. Handelt es sich um die aktuellste Auszahlungsvariante könnte das eine Ausnahme sein.",
"Traubengutschriften löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
if (res != MessageBoxResult.Yes)
return;
RevertButton.IsEnabled = false; RevertButton.IsEnabled = false;
Mouse.OverrideCursor = Cursors.AppStarting; Mouse.OverrideCursor = Cursors.AppStarting;
var b = new BillingVariant(v.Year, v.AvNr); var b = new BillingVariant(v.Year, v.AvNr);
await b.Revert(); await b.Revert();
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
CommitButton.IsEnabled = true; CommitButton.IsEnabled = true;
await HintContextChange(); await App.HintContextChange();
}
private async void ExportButton_Click(object sender, RoutedEventArgs evt) {
if (PaymentVariantList.SelectedValue is not PaymentVar v) {
return;
} else if (v.TransferDate == null) {
MessageBox.Show("Überweisungsdatum muss gesetzt sein!", "Exportieren nicht möglich", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
var d = new SaveFileDialog() {
FileName = $"{App.Client.NameToken}-Überweisungsdaten-{v.Year}-{v.Name.Trim().Replace(' ', '-')}.{Ebics.FileExtension}",
DefaultExt = Ebics.FileExtension,
Filter = "EBICS-Datei (*.xml)|*.xml",
Title = $"Überweisungsdaten speichern unter - Elwig",
};
if (d.ShowDialog() == true) {
ExportButton.IsEnabled = false;
Mouse.OverrideCursor = Cursors.AppStarting;
using var e = new Ebics(v, d.FileName);
await e.ExportAsync(Transaction.FromPaymentVariant(v));
Mouse.OverrideCursor = null;
ExportButton.IsEnabled = true;
}
}
private async void SaveButton_Click(object sender, RoutedEventArgs evt) {
if (PaymentVariantList.SelectedItem is not PaymentVar v || BillingData == null) return;
try {
v.Name = NameInput.Text;
v.Comment = (CommentInput.Text != "") ? CommentInput.Text : null;
v.TransferDateString = (TransferDateInput.Text != "") ? string.Join("-", TransferDateInput.Text.Split(".").Reverse()) : null;
var d = App.Config.Debug ? BillingData.FromJson(DataInput.Text) : BillingData;
d.ConsiderDelieryModifiers = ConsiderModifiersInput.IsChecked ?? false;
d.ConsiderContractPenalties = ConsiderPenaltiesInput.IsChecked ?? false;
d.ConsiderTotalPenalty = ConsiderPenaltyInput.IsChecked ?? false;
d.ConsiderAutoBusinessShares = ConsiderAutoInput.IsChecked ?? false;
v.Data = JsonSerializer.Serialize(d.Data);
Context.Update(v);
await Context.SaveChangesAsync();
await App.HintContextChange();
CommentInput_TextChanged(null, null);
ConsiderModifiersInput_Changed(null, null);
ConsiderPenaltiesInput_Changed(null, null);
ConsiderPenaltyInput_Changed(null, null);
ConsiderAutoInput_Changed(null, null);
} catch (Exception exc) {
await HintContextChange();
var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
MessageBox.Show(str, "Auszahlungsvariante aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void ModifierButton_Click(object sender, RoutedEventArgs evt) {
App.FocusBaseDataSeason(Year);
}
private void NameInput_TextChanged(object sender, TextChangedEventArgs evt) {
if (PaymentVariantList.SelectedItem is not PaymentVar v) {
ControlUtils.ClearInputState(NameInput);
return;
}
if (NameInput.Text != v.Name) {
ControlUtils.SetInputChanged(NameInput);
NameChanged = true;
} else {
ControlUtils.ClearInputState(NameInput);
NameChanged = false;
}
UpdateSaveButton();
}
private void CommentInput_TextChanged(object? sender, TextChangedEventArgs? evt) {
if (PaymentVariantList.SelectedItem is not PaymentVar v) {
ControlUtils.ClearInputState(CommentInput);
return;
}
if (CommentInput.Text != (v.Comment ?? "")) {
ControlUtils.SetInputChanged(CommentInput);
CommentChanged = true;
} else {
ControlUtils.ClearInputState(CommentInput);
CommentChanged = false;
}
UpdateSaveButton();
}
private void TransferDateInput_TextChanged(object sender, TextChangedEventArgs evt) {
if (PaymentVariantList.SelectedItem is not PaymentVar v) {
ControlUtils.ClearInputState(TransferDateInput);
return;
}
var res = Validator.CheckDate(TransferDateInput, false);
if (!res.IsValid) {
ControlUtils.SetInputInvalid(TransferDateInput);
TransferDateValid = false;
} else if (TransferDateInput.Text != $"{v.TransferDate:dd.MM.yyyy}") {
ControlUtils.SetInputChanged(TransferDateInput);
TransferDateValid = true;
TransferDateChanged = true;
} else {
ControlUtils.ClearInputState(TransferDateInput);
TransferDateValid = true;
TransferDateChanged = false;
}
UpdateSaveButton();
}
private void DataInput_TextChanged(object sender, TextChangedEventArgs evt) {
if (PaymentVariantList.SelectedItem is not PaymentVar v) {
ControlUtils.ClearInputState(DataInput);
return;
}
try {
var data = BillingData.FromJson(DataInput.Text);
var origJson = v.Data;
try {
origJson = JsonSerializer.Serialize(BillingData.FromJson(v.Data).Data);
} catch { }
DataValid = true;
if (JsonSerializer.Serialize(data.Data) != origJson) {
ControlUtils.SetInputChanged(DataInput);
DataChanged = true;
} else {
ControlUtils.ClearInputState(DataInput);
DataChanged = false;
}
} catch {
ControlUtils.SetInputInvalid(DataInput);
DataValid = false;
}
UpdateSaveButton();
}
private void ConsiderModifiersInput_Changed(object? sender, RoutedEventArgs? evt) {
if (BillingData == null) {
ControlUtils.ClearInputState(ConsiderModifiersInput);
return;
}
if (BillingData.ConsiderDelieryModifiers != ConsiderModifiersInput.IsChecked) {
ControlUtils.SetInputChanged(ConsiderModifiersInput);
} else {
ControlUtils.ClearInputState(ConsiderModifiersInput);
}
UpdateSaveButton();
}
private void ConsiderPenaltiesInput_Changed(object? sender, RoutedEventArgs? evt) {
if (BillingData == null) {
ControlUtils.ClearInputState(ConsiderPenaltiesInput);
return;
}
if (BillingData.ConsiderContractPenalties != ConsiderPenaltiesInput.IsChecked) {
ControlUtils.SetInputChanged(ConsiderPenaltiesInput);
} else {
ControlUtils.ClearInputState(ConsiderPenaltiesInput);
}
UpdateSaveButton();
}
private void ConsiderPenaltyInput_Changed(object? sender, RoutedEventArgs? evt) {
if (BillingData == null) {
ControlUtils.ClearInputState(ConsiderPenaltyInput);
return;
}
if (BillingData.ConsiderTotalPenalty != ConsiderPenaltyInput.IsChecked) {
ControlUtils.SetInputChanged(ConsiderPenaltyInput);
} else {
ControlUtils.ClearInputState(ConsiderPenaltyInput);
}
UpdateSaveButton();
}
private void ConsiderAutoInput_Changed(object? sender, RoutedEventArgs? evt) {
if (BillingData == null) {
ControlUtils.ClearInputState(ConsiderAutoInput);
return;
}
if (BillingData.ConsiderAutoBusinessShares != ConsiderAutoInput.IsChecked) {
ControlUtils.SetInputChanged(ConsiderAutoInput);
} else {
ControlUtils.ClearInputState(ConsiderAutoInput);
}
UpdateSaveButton();
} }
private async Task Generate(int mode) { private async Task Generate(int mode) {
+7 -3
View File
@@ -37,14 +37,18 @@
<Button x:Name="DeliveryConfirmationButton" Content="Anlieferungsbestätigungen" <Button x:Name="DeliveryConfirmationButton" Content="Anlieferungsbestätigungen"
Click="DeliveryConfirmationButton_Click" Click="DeliveryConfirmationButton_Click"
Margin="50,122,0,0"/> Margin="50,120,0,0"/>
<Button x:Name="OverUnderDeliveryButton" Content="Über-/Unterlieferungen" <Button x:Name="OverUnderDeliveryButton" Content="Über-/Unterlieferungen"
Click="OverUnderDeliveryButton_Click" Click="OverUnderDeliveryButton_Click"
Margin="50,164,0,0"/> Margin="50,160,0,0"/>
<Button x:Name="AutoBusinessSharesButton" Content="Autom. GA nachzeichen"
Click="AutoBusinessSharesButton_Click"
Margin="50,200,0,0"/>
<Button x:Name="PaymentButton" Content="Auszahlung" <Button x:Name="PaymentButton" Content="Auszahlung"
Click="PaymentButton_Click" Click="PaymentButton_Click"
Margin="50,206,0,0"/> Margin="50,240,0,0"/>
</Grid> </Grid>
</local:ContextWindow> </local:ContextWindow>
+22
View File
@@ -31,6 +31,7 @@ namespace Elwig.Windows {
CalculateBucketsButton.IsEnabled = valid && last; CalculateBucketsButton.IsEnabled = valid && last;
DeliveryConfirmationButton.IsEnabled = valid; DeliveryConfirmationButton.IsEnabled = valid;
OverUnderDeliveryButton.IsEnabled = valid; OverUnderDeliveryButton.IsEnabled = valid;
AutoBusinessSharesButton.IsEnabled = valid;
PaymentButton.IsEnabled = valid; PaymentButton.IsEnabled = valid;
} }
@@ -82,6 +83,27 @@ namespace Elwig.Windows {
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
} }
private async void AutoBusinessSharesButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year)
return;
if (false && App.Client.IsMatzen) {
AutoBusinessSharesButton.IsEnabled = false;
Mouse.OverrideCursor = Cursors.AppStarting;
var b = new Billing(year);
await b.AutoAdjustBusinessShare();
Mouse.OverrideCursor = null;
AutoBusinessSharesButton.IsEnabled = true;
} else {
MessageBox.Show(
"Es ist kein automatisches Nachzeichnen der Geschäftsanteile\n" +
"für diese Genossenschaft eingestellt!\n" +
"Bitte wenden Sie sich an die Programmierer!", "Fehler",
MessageBoxButton.OK, MessageBoxImage.Information);
}
}
private void PaymentButton_Click(object sender, RoutedEventArgs evt) { private void PaymentButton_Click(object sender, RoutedEventArgs evt) {
if (SeasonInput.Value is not int year) if (SeasonInput.Value is not int year)
return; return;
+2 -2
View File
@@ -60,6 +60,6 @@
<None Include="Files\config.ini" /> <None Include="Files\config.ini" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="WixToolset.Heat" Version="4.0.1" /> <PackageReference Include="WixToolset.Heat" Version="4.0.3" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+3 -3
View File
@@ -13,7 +13,7 @@
</Target> </Target>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Installer\Installer.wixproj" /> <ProjectReference Include="..\Installer\Installer.wixproj" />
<PackageReference Include="WixToolset.Bal.wixext" Version="4.0.1" /> <PackageReference Include="WixToolset.Bal.wixext" Version="4.0.3" />
<PackageReference Include="WixToolset.Util.wixext" Version="4.0.1" /> <PackageReference Include="WixToolset.Util.wixext" Version="4.0.3" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+28
View File
@@ -0,0 +1,28 @@
using Elwig.Helpers;
using Microsoft.Data.Sqlite;
using System.Reflection;
namespace Tests {
[SetUpFixture]
public class DatabaseSetup {
private SqliteConnection? Connection;
[OneTimeSetUp]
public async Task SetupDatabase() {
AppDbContext.ConnectionStringOverride = $"Data Source=ElwigTestDB; Mode=Memory; Foreign Keys=True; Cache=Shared";
Connection = await AppDbContext.ConnectAsync();
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Create.sql");
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Insert.sql");
}
[OneTimeTearDown]
public async Task TeardownDatabase() {
AppDbContext.ConnectionStringOverride = null;
if (Connection == null) return;
await Connection.DisposeAsync();
Connection = null;
// The in-memory database will be dropped if all connections to it are closed
}
}
}
+349
View File
@@ -0,0 +1,349 @@
using Elwig.Helpers;
using Elwig.Helpers.Billing;
namespace Tests.Helpers {
[TestFixture]
public class BillingDataTest {
private static readonly string[] AttributeVariants = ["GV", "GVD", "GVK", "GVS", "GVZ", "WR", "WRS", "ZW", "ZWS", "ZWZ"];
[OneTimeSetUp]
public async Task SetupBilling() {
await BillingData.Init();
}
private static (string, string?) GetSortIdAttrId(string bucket) {
return (bucket[..2], bucket.Length > 2 ? bucket[2..] : null);
}
private static string GetQualId(double kmw) {
return kmw switch {
>= 17.0 => "KAB",
>= 15.0 => "QUW",
>= 14.0 => "LDW",
>= 10.6 => "RSW",
_ => "WEI",
};
}
private static void TestCalcOe(PaymentBillingData data, string bucket, double oe, decimal expected, string? qualid = null, bool geb = false) {
var (sortid, attrid) = GetSortIdAttrId(bucket);
var kmw = Utils.OeToKmw(oe);
var v = data.CalculatePrice(sortid, attrid, qualid ?? GetQualId(kmw), geb, oe, kmw);
Assert.That(Math.Round(v, 6), Is.EqualTo(expected));
}
private static void TestCalcKmw(PaymentBillingData data, string bucket, double kmw, decimal expected, string? qualid = null, bool geb = false) {
var (sortid, attrid) = GetSortIdAttrId(bucket);
var oe = Utils.KmwToOe(kmw);
var v = data.CalculatePrice(sortid, attrid, qualid ?? GetQualId(kmw), geb, oe, kmw);
Assert.That(Math.Round(v, 6), Is.EqualTo(expected));
}
[Test]
public void Test_01_Flatrate() {
var data = PaymentBillingData.FromJson("""
{
"mode": "elwig",
"version": 1,
"payment": 0.5,
"curves": []
}
""", AttributeVariants);
Assert.Multiple(() => {
TestCalcOe(data, "GV", 73, 0.5m);
TestCalcOe(data, "WRS", 74, 0.5m);
});
}
[Test]
public void Test_02_Simple() {
var data = PaymentBillingData.FromJson("""
{
"mode": "elwig",
"version": 1,
"payment": "curve:1",
"curves": [{
"id": 1,
"mode": "oe",
"data": {
"72.0oe": 0.25,
"15.0kmw": 0.5,
"83oe": 1
},
"geb": 0.10
}]
}
""", AttributeVariants);
Assert.Multiple(() => {
TestCalcOe(data, "GV", 70, 0.25m);
TestCalcOe(data, "GV", 72, 0.25m);
TestCalcOe(data, "GV", 73, 0.50m);
TestCalcOe(data, "GV", 74, 0.55m);
TestCalcOe(data, "GV", 80, 0.85m);
TestCalcOe(data, "GV", 83, 1.00m);
TestCalcOe(data, "GV", 90, 1.00m);
TestCalcOe(data, "GV", 73, 0.60m, geb: true);
TestCalcOe(data, "GV", 74, 0.65m, geb: true);
TestCalcOe(data, "GV", 80, 0.95m, geb: true);
TestCalcOe(data, "GV", 83, 1.10m, geb: true);
TestCalcOe(data, "GV", 90, 1.10m, geb: true);
});
}
[Test]
public void Test_03_GreaterThanAndLessThan() {
var data = PaymentBillingData.FromJson("""
{
"mode": "elwig",
"version": 1,
"payment": "curve:1",
"curves": [{
"id": 1,
"mode": "kmw",
"data": {
"<14kmw": 0.1,
"14kmw": 0.2,
"<15kmw": 0.25,
"15kmw": 0.5,
"17kmw": 1,
">17kmw": 1.25
}
}]
}
""", AttributeVariants);
Assert.Multiple(() => {
TestCalcKmw(data, "GV", 13.00, 0.10m);
TestCalcKmw(data, "GV", 13.50, 0.10m);
TestCalcKmw(data, "GV", 13.99, 0.10m);
TestCalcKmw(data, "GV", 14.00, 0.20m);
TestCalcKmw(data, "GV", 14.50, 0.225m);
TestCalcKmw(data, "GV", 15.00, 0.50m);
TestCalcKmw(data, "GV", 15.50, 0.625m);
TestCalcKmw(data, "GV", 16.00, 0.75m);
TestCalcKmw(data, "GV", 16.50, 0.875m);
TestCalcKmw(data, "GV", 17.00, 1.00m);
TestCalcKmw(data, "GV", 17.01, 1.25m);
TestCalcKmw(data, "GV", 17.50, 1.25m);
TestCalcKmw(data, "GV", 18.00, 1.25m);
TestCalcKmw(data, "GV", 18.50, 1.25m);
});
}
[Test]
public void Test_04_VariantsAndAttributes() {
var data = PaymentBillingData.FromJson("""
{
"mode": "elwig",
"version": 1,
"payment": {
"default": 0.10,
"GV/": 0.20,
"ZW": 0.25,
"/S": 0.15,
"GV/K": 0.30
},
"curves": []
}
""", AttributeVariants);
Assert.Multiple(() => {
TestCalcOe(data, "WR", 73, 0.10m);
TestCalcOe(data, "WRS", 73, 0.15m);
TestCalcOe(data, "GV", 73, 0.20m);
TestCalcOe(data, "GVD", 73, 0.10m);
TestCalcOe(data, "GVK", 73, 0.30m);
TestCalcOe(data, "GVS", 73, 0.15m);
TestCalcOe(data, "GVZ", 73, 0.10m);
TestCalcOe(data, "ZW", 73, 0.25m);
TestCalcOe(data, "ZWS", 73, 0.15m);
TestCalcOe(data, "ZWZ", 73, 0.25m);
});
}
[Test]
public void Test_05_QualityLevel() {
var data = PaymentBillingData.FromJson("""
{
"mode": "elwig",
"version": 1,
"payment": 0.5,
"quality": {
"WEI": {
"default": 0.25,
"GV": 0.3,
"/S": 0.2
}
},
"curves": []
}
""", AttributeVariants);
Assert.Multiple(() => {
TestCalcOe(data, "GV", 75, 0.30m, qualid: "WEI");
TestCalcOe(data, "ZW", 76, 0.25m, qualid: "WEI");
TestCalcOe(data, "GVS", 75, 0.20m, qualid: "WEI");
TestCalcOe(data, "GVK", 74, 0.30m, qualid: "WEI");
TestCalcOe(data, "ZWS", 73, 0.20m, qualid: "WEI");
TestCalcOe(data, "GV", 70, 0.5m);
TestCalcOe(data, "GV", 72, 0.5m);
TestCalcOe(data, "GV", 73, 0.5m);
TestCalcOe(data, "ZWS", 74, 0.5m);
TestCalcOe(data, "GVK", 80, 0.5m);
});
}
[Test]
public void Test_06_ModeOeAndKmw() {
var data = PaymentBillingData.FromJson("""
{
"mode": "elwig",
"version": 1,
"payment": {
"default": 1.0,
"GV": "curve:1",
"ZW": "curve:2"
},
"curves": [{
"id": 1,
"mode": "oe",
"data": {
"73oe": 2.0,
"17kmw": 3.0
}
}, {
"id": 2,
"mode": "kmw",
"data": {
"73oe": 2.0,
"17kmw": 3.0
}
}]
}
""", AttributeVariants);
Assert.Multiple(() => {
TestCalcKmw(data, "GV", 15.0, 2.0m);
TestCalcKmw(data, "GV", 15.5, 2.272727m);
TestCalcKmw(data, "GV", 16.0, 2.454545m);
TestCalcKmw(data, "GV", 16.5, 2.727273m);
TestCalcKmw(data, "GV", 17.0, 3.0m);
TestCalcKmw(data, "ZW", 15.0, 2.0m);
TestCalcKmw(data, "ZW", 15.5, 2.25m);
TestCalcKmw(data, "ZW", 16.0, 2.50m);
TestCalcKmw(data, "ZW", 16.5, 2.75m);
TestCalcKmw(data, "ZW", 17.0, 3.0m);
});
}
[Test]
public void Test_07_MultipleCurves() {
var data = PaymentBillingData.FromJson("""
{
"mode": "elwig",
"version": 1,
"payment": {
"default": 0.25,
"/S": "curve:1",
"GV/": 0.75,
"GV/K": "curve:2",
"WR/S": "curve:3"
},
"curves": [{
"id": 3,
"mode": "kmw",
"data": {
"73oe": 0.7,
"17kmw": 0.8
},
"geb": {
"15kmw": 0.8,
"17kmw": 0.95
}
}, {
"id": 1,
"mode": "kmw",
"data": {
"73oe": 0.5,
"17kmw": 0.6
},
"geb": 0.1
}, {
"id": 2,
"mode": "kmw",
"data": {
"15kmw": 0.6,
"17kmw": 0.7
},
"geb": {
"73oe": 0.65,
"17kmw": 0.80
}
}]
}
""", AttributeVariants);
Assert.Multiple(() => {
TestCalcKmw(data, "GV", 15.0, 0.75m);
TestCalcKmw(data, "GVS", 15.0, 0.50m);
TestCalcKmw(data, "GVS", 16.0, 0.55m);
TestCalcKmw(data, "GVS", 17.0, 0.60m);
TestCalcKmw(data, "GVS", 15.0, 0.60m, geb: true);
TestCalcKmw(data, "GVS", 16.0, 0.65m, geb: true);
TestCalcKmw(data, "GVS", 17.0, 0.70m, geb: true);
TestCalcKmw(data, "GVK", 15.0, 0.60m);
TestCalcKmw(data, "GVK", 16.0, 0.65m);
TestCalcKmw(data, "GVK", 17.0, 0.70m);
TestCalcKmw(data, "GVK", 15.0, 0.65m, geb: true);
TestCalcKmw(data, "GVK", 16.0, 0.725m, geb: true);
TestCalcKmw(data, "GVK", 17.0, 0.80m, geb: true);
TestCalcKmw(data, "WRS", 15.0, 0.70m);
TestCalcKmw(data, "WRS", 16.0, 0.75m);
TestCalcKmw(data, "WRS", 17.0, 0.80m);
TestCalcKmw(data, "WRS", 15.0, 0.80m, geb: true);
TestCalcKmw(data, "WRS", 16.0, 0.875m, geb: true);
TestCalcKmw(data, "WRS", 17.0, 0.95m, geb: true);
});
}
[Test]
public void Test_08_WgMaster() {
var data = PaymentBillingData.FromJson("""
{
"mode": "wgmaster",
"Grundbetrag": 0.033,
"GBZS": 0.0,
"Ausgabefaktor": 1.0,
"Rebelzuschlag": 0.0,
"AufschlagVolllieferanten": 0.0,
"AuszahlungSorten": {
"BL/": 0.097,
"BP/": 0.097,
"GV/K": "curve:1",
"SL/": 0.097,
"ZW/": 0.097,
"default": "curve:0"
},
"AuszahlungSortenQualitätsstufe": {
"WEI": 0.005
},
"Kurven": [{
"id": 0,
"mode": "oe",
"data": 0.033,
"geb": 0
}, {
"id": 1,
"mode": "oe",
"data": {
"88oe": 0.032,
"89oe": 0.065
}
}]
}
""", AttributeVariants);
Assert.Multiple(() => {
TestCalcOe(data, "GVK", 73, 0.032m);
TestCalcOe(data, "ZWS", 74, 0.033m);
TestCalcOe(data, "GV", 75, 0.005m, qualid: "WEI");
TestCalcOe(data, "GVK", 115, 0.065m);
});
}
}
}
+30
View File
@@ -0,0 +1,30 @@
using Elwig.Helpers;
using Microsoft.Data.Sqlite;
using System.Reflection;
namespace Tests.Helpers {
[TestFixture]
public class BillingTest {
private SqliteConnection? Connection;
[OneTimeSetUp]
public async Task SetupDatabase() {
Connection = await AppDbContext.ConnectAsync();
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.BillingInsert.sql");
}
[OneTimeTearDown]
public async Task TeardownDatabase() {
if (Connection == null) return;
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.BillingDelete.sql");
await Connection.DisposeAsync();
Connection = null;
}
[Test]
public void Test() {
// TODO
}
}
}
@@ -1,8 +1,8 @@
using Elwig.Helpers; using Elwig.Helpers;
namespace Tests { namespace Tests.Helpers {
[TestFixture] [TestFixture]
public class HelpersUtilsTest { public class UtilsTest {
private static readonly double[,] Gradation = new double[,] { private static readonly double[,] Gradation = new double[,] {
{ 14.0, 68.0 }, { 14.0, 68.0 },
@@ -1,10 +1,10 @@
using Elwig.Helpers; using Elwig.Helpers;
using System.Windows.Controls; using System.Windows.Controls;
namespace Tests { namespace Tests.Helpers {
[TestFixture] [TestFixture]
[Apartment(ApartmentState.STA)] [Apartment(ApartmentState.STA)]
public class HelpersValidatorTest { public class ValidatorTest {
private static TextBox TB(string value, int caret = 0) { private static TextBox TB(string value, int caret = 0) {
return new() { return new() {
+1
View File
@@ -0,0 +1 @@
-- deletes for HelpersBillingTest
+1
View File
@@ -0,0 +1 @@
-- inserts for HelpersBillingTest
+53
View File
@@ -0,0 +1,53 @@
-- inserts for DatabaseSetup
INSERT INTO branch (zwstid, name) VALUES
('X', 'Test');
INSERT INTO wb_gl (glnr, name) VALUES
(1, 'Matzner Hügel'),
(2, 'Wolkersdorfer Hochleithen');
INSERT INTO AT_gem (gkz, name) VALUES
(30828, 'Hohenruppersdorf'),
(31655, 'Wolkersdorf im Weinviertel');
INSERT INTO wb_gem (gkz, hkid) VALUES
(30828, 'WLWV'),
(31655, 'WLWV');
INSERT INTO AT_kg (kgnr, gkz, name) VALUES
(06109, 30828, 'Hohenruppersdorf'),
(15209, 31655, 'Münichsthal'),
(15211, 31655, 'Obersdorf'),
(15212, 31655, 'Pfösing'),
(15216, 31655, 'Riedentahl'),
(15224, 31655, 'Wolkersdorf');
INSERT INTO wb_kg (kgnr, glnr) VALUES
(06109, 1),
(15209, 2),
(15211, 2),
(15212, 2),
(15216, 2),
(15224, 2);
INSERT INTO AT_ort (okz, gkz, kgnr, name) VALUES
(03524, 30828, 06109, 'Hohenruppersdorf'),
(05092, 31655, 15211, 'Obersdorf'),
(05135, 31655, 15209, 'Münichsthal'),
(05136, 31655, 15212, 'Pfösing'),
(05137, 31655, 15216, 'Riedenthal'),
(05138, 31655, 15224, 'Wolkersdorf im Weinviertel');
INSERT INTO AT_plz (plz, ort, blnr, type, internal, addressable, po_box) VALUES
(2223, 'Hohenruppersdorf', 3, 'PLZ-Adressierung', FALSE, TRUE, FALSE),
(2120, 'Wolkersdorf im Weinviertel', 3, 'PLZ-Adressierung', FALSE, TRUE, TRUE ),
(2122, 'Ulrichskirchen', 3, 'PLZ-Adressierung', FALSE, TRUE, FALSE);
INSERT INTO AT_plz_dest (plz, okz, dest) VALUES
(2223, 03524, 'Hohenruppersdorf'),
(2120, 05092, 'Obersdorf'),
(2120, 05138, 'Wolkersdorf im Weinviertel'),
(2122, 05135, 'Münichsthal'),
(2122, 05136, 'Pfösing'),
(2122, 05137, 'Riedenthal');
+19 -5
View File
@@ -10,11 +10,25 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" /> <EmbeddedResource Include="Resources\*.sql" />
<PackageReference Include="NUnit" Version="3.13.3" /> </ItemGroup>
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
<PackageReference Include="NUnit.Analyzers" Version="3.5.0" /> <Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<PackageReference Include="coverlet.collector" Version="3.1.2" /> <Exec Command="call fetch-resources.bat" />
</Target>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="NUnit" Version="4.0.1" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageReference Include="NUnit.Analyzers" Version="3.10.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
+1
View File
@@ -0,0 +1 @@
curl -s "https://www.necronda.net/elwig/files/create.sql?v=13" -u "elwig:ganzGeheim123!" -o "Resources\Create.sql"