Compare commits
71 Commits
b9a2893d80
...
v0.6.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 42bf01656e | |||
| 51293baaae | |||
| 1d1398a9cd | |||
| 7d199282d0 | |||
| b56a5ed5c6 | |||
| 201b63c2f1 | |||
| b2bd0c9a21 | |||
| 8502afdc9a | |||
| cb541cb6e6 | |||
| 403e7723d2 | |||
| 8fbce03031 | |||
| b32a935150 | |||
| 337bfa89d9 | |||
| f886888ccc | |||
| 4dd036babd | |||
| b6fd62f8ca | |||
| b52c09a176 | |||
| 668eb9a2d0 | |||
| 9eb013ce11 | |||
| 38ad433b4e | |||
| 0a60f01979 | |||
| a1ddef4666 | |||
| 788d0efa4a | |||
| 0f06d98d39 | |||
| 228d17f8cb | |||
| 1664024e64 | |||
| 8fbfd33e8f | |||
| 95853099bb | |||
| 8072febd5b | |||
| 62d9641b28 | |||
| 3aabfbc603 | |||
| 09e55264bb | |||
| f894c3b212 | |||
| 09a7889044 | |||
| d77aac43ec | |||
| 062c7bed5e | |||
| b723161fa5 | |||
| debee3b4bf | |||
| f15733f827 | |||
| 05b6e8ddd6 | |||
| 5ae326074f | |||
| 60b99bf95b | |||
| 3562d304de | |||
| 9f73d13dbf | |||
| 5e665ffb50 | |||
| 1e751c473a | |||
| d67e434fed | |||
| 3a89e16db3 | |||
| 2556033a07 | |||
| 3f6a94e773 | |||
| e75e2ddbda | |||
| 121ca10261 | |||
| f28a1a2db9 | |||
| ab61edc402 | |||
| ba55692cbe | |||
| eb46955b3b | |||
| 37bf8d0855 | |||
| be87f31211 | |||
| 4738cde9e4 | |||
| 6cf5e0d45e | |||
| 8e71e82efc | |||
| 4f07d9b129 | |||
| 8555748202 | |||
| a0914f4d54 | |||
| 2301251420 | |||
| 83f9b58d4d | |||
| f8aef20b0d | |||
| 0dcffc8677 | |||
| bc578b212e | |||
| 8368caf58a | |||
| c836b45920 |
@@ -3,3 +3,4 @@ bin/
|
|||||||
*.user
|
*.user
|
||||||
.vs
|
.vs
|
||||||
.idea
|
.idea
|
||||||
|
Tests/Resources/Create.sql
|
||||||
|
|||||||
+22
-7
@@ -16,6 +16,8 @@ using Elwig.Helpers.Printing;
|
|||||||
using Elwig.Windows;
|
using Elwig.Windows;
|
||||||
using Elwig.Dialogs;
|
using Elwig.Dialogs;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Elwig.Helpers.Billing;
|
||||||
|
using Elwig.Models.Entities;
|
||||||
|
|
||||||
namespace Elwig {
|
namespace Elwig {
|
||||||
public partial class App : Application {
|
public partial class App : Application {
|
||||||
@@ -63,26 +65,31 @@ namespace Elwig {
|
|||||||
MainDispatcher = Dispatcher;
|
MainDispatcher = Dispatcher;
|
||||||
Scales = Array.Empty<IScale>();
|
Scales = Array.Empty<IScale>();
|
||||||
CurrentApp = this;
|
CurrentApp = this;
|
||||||
|
OverrideCulture();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override 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 {
|
||||||
AppDbUpdater.CheckDb();
|
await AppDbUpdater.CheckDb();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
MessageBox.Show($"Invalid Database:\n\n{e.Message}", "Invalid Database", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show($"Invalid Database:\n\n{e.Message}", "Invalid Database", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
Shutdown();
|
Shutdown();
|
||||||
@@ -104,6 +111,7 @@ namespace Elwig {
|
|||||||
|
|
||||||
Utils.RunBackground("HTML Initialization", () => Html.Init(PrintingReadyChanged));
|
Utils.RunBackground("HTML Initialization", () => Html.Init(PrintingReadyChanged));
|
||||||
Utils.RunBackground("PDF Initialization", () => Pdf.Init(PrintingReadyChanged));
|
Utils.RunBackground("PDF Initialization", () => Pdf.Init(PrintingReadyChanged));
|
||||||
|
Utils.RunBackground("JSON Schema Initialization", BillingData.Init);
|
||||||
|
|
||||||
var list = new List<IScale>();
|
var list = new List<IScale>();
|
||||||
foreach (var s in Config.Scales) {
|
foreach (var s in Config.Scales) {
|
||||||
@@ -219,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());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
@@ -74,7 +75,7 @@ namespace Elwig.Documents {
|
|||||||
);
|
);
|
||||||
var totalDict = buckets.GroupBy(b => b.Key[2..]).ToDictionary(g => g.Key, g => g.Sum(a => a.Value.DeliveryStrict));
|
var totalDict = buckets.GroupBy(b => b.Key[2..]).ToDictionary(g => g.Key, g => g.Sum(a => a.Value.DeliveryStrict));
|
||||||
var totals = attributes.Select(a => totalDict.TryGetValue(a, out int value) ? value : 0);
|
var totals = attributes.Select(a => totalDict.TryGetValue(a, out int value) ? value : 0);
|
||||||
tbl += "<tr class=\"sum\"><td></td>" + string.Join("", totals.Select(v => $"<td class=\"number\">{v:N0}</td>")) +
|
tbl += "<tr class=\"sum bold\"><td></td>" + string.Join("", totals.Select(v => $"<td class=\"number\">{v:N0}</td>")) +
|
||||||
$"<td class=\"number\">{totalDict.Values.Sum():N0}</td></tr>";
|
$"<td class=\"number\">{totalDict.Values.Sum():N0}</td></tr>";
|
||||||
|
|
||||||
return "<table class=\"sortenaufteilung small number cohere\">" + tbl + "</table>";
|
return "<table class=\"sortenaufteilung small number cohere\">" + tbl + "</table>";
|
||||||
@@ -125,7 +126,7 @@ namespace Elwig.Documents {
|
|||||||
<th>Lieferrecht</th>
|
<th>Lieferrecht</th>
|
||||||
{(includeDelivery ? "<th>Unterliefert</th>" : "")}
|
{(includeDelivery ? "<th>Unterliefert</th>" : "")}
|
||||||
{(includeDelivery ? "<th>Noch lieferbar</th>" : "")}
|
{(includeDelivery ? "<th>Noch lieferbar</th>" : "")}
|
||||||
{(includeDelivery ? "<th>Geliefert</th>" : "")}
|
{(includeDelivery ? "<th>Überliefert</th>" : "")}
|
||||||
{(includePayment ? "<th>Zugeteilt</th>" : "")}
|
{(includePayment ? "<th>Zugeteilt</th>" : "")}
|
||||||
{(includeDelivery ? "<th>Geliefert</th>" : "")}
|
{(includeDelivery ? "<th>Geliefert</th>" : "")}
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -1,33 +1,72 @@
|
|||||||
using Elwig.Helpers;
|
using Elwig.Helpers;
|
||||||
using Elwig.Models.Dtos;
|
using Elwig.Models.Dtos;
|
||||||
using Elwig.Models.Entities;
|
using Elwig.Models.Entities;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Elwig.Documents {
|
namespace Elwig.Documents {
|
||||||
public class CreditNote : BusinessDocument {
|
public class CreditNote : BusinessDocument {
|
||||||
|
|
||||||
|
public new static string Name => "Traubengutschrift";
|
||||||
|
|
||||||
public PaymentMember? Payment;
|
public PaymentMember? Payment;
|
||||||
public Credit? Credit;
|
public Credit? Credit;
|
||||||
public CreditNoteData Data;
|
public CreditNoteData Data;
|
||||||
public string? Text;
|
public string? Text;
|
||||||
public string CurrencySymbol;
|
public string CurrencySymbol;
|
||||||
public int Precision;
|
public int Precision;
|
||||||
|
public string MemberModifier;
|
||||||
|
public IEnumerable<(string Name, int Kg, decimal Amount)>? MemberUnderDeliveries;
|
||||||
|
public decimal MemberTotalUnderDelivery;
|
||||||
|
public decimal MemberAutoBusinessShares;
|
||||||
|
|
||||||
public CreditNote(AppDbContext ctx, PaymentMember p, CreditNoteData data) :
|
public CreditNote(AppDbContext ctx, PaymentMember p, CreditNoteData data, Dictionary<string, UnderDelivery>? underDeliveries = null) :
|
||||||
base($"Traubengutschrift {(p.Credit != null ? $"Nr. {p.Credit.Year}/{p.Credit.TgNr}" : 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) {
|
||||||
UseBillingAddress = true;
|
UseBillingAddress = true;
|
||||||
ShowDateAndLocation = true;
|
ShowDateAndLocation = true;
|
||||||
Data = data;
|
Data = data;
|
||||||
Payment = p;
|
Payment = p;
|
||||||
Credit = p.Credit;
|
Credit = p.Credit;
|
||||||
|
var season = p.Variant.Season;
|
||||||
|
var mod = App.Client.IsMatzen ? ctx.Modifiers.Where(m => m.Year == season.Year && m.Name.StartsWith("Treue")).FirstOrDefault() : null;
|
||||||
|
if (mod != null) {
|
||||||
|
MemberModifier = $"{mod.Name} ({mod.ValueStr})";
|
||||||
|
} else {
|
||||||
|
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}" : "-")}</td></tr>" +
|
$"<tr><th>TG-Nr.</th><td>{(p.Credit != null ? $"{p.Credit.Year}/{p.Credit.TgNr:000}" : "-")}</td></tr>" +
|
||||||
$"<tr><th>Überw. am</th><td>{p.Variant.TransferDate:dd.MM.yyyy}</td></tr>" +
|
$"<tr><th>Überw. am</th><td>{p.Variant.TransferDate:dd.MM.yyyy}</td></tr>" +
|
||||||
$"<tr><th>Datum/Zeit</th><td>{p.Credit?.ModifiedTimestamp:dd.MM.yyyy} / {p.Credit?.ModifiedTimestamp:HH:mm}</td></tr>" +
|
$"<tr><th>Datum/Zeit</th><td>{p.Credit?.ModifiedTimestamp:dd.MM.yyyy} / {p.Credit?.ModifiedTimestamp:HH:mm}</td></tr>" +
|
||||||
$"</tbody></table>";
|
$"</tbody></table>";
|
||||||
Text = App.Client.TextDeliveryNote;
|
Text = App.Client.TextDeliveryNote;
|
||||||
DocumentId = $"Tr.-Gutschr. " + (p.Credit != null ? $"{p.Credit.Year}/{p.Credit.TgNr}" : p.MgNr);
|
DocumentId = $"Tr.-Gutschr. " + (p.Credit != null ? $"{p.Credit.Year}/{p.Credit.TgNr:000}" : p.MgNr);
|
||||||
CurrencySymbol = p.Variant.Season.Currency.Symbol ?? p.Variant.Season.Currency.Code;
|
CurrencySymbol = season.Currency.Symbol ?? season.Currency.Code;
|
||||||
Precision = p.Variant.Season.Precision;
|
Precision = season.Precision;
|
||||||
|
|
||||||
|
var variants = ctx.WineVarieties.ToDictionary(v => v.SortId, v => v);
|
||||||
|
var attributes = ctx.WineAttributes.ToDictionary(a => a.AttrId, a => a);
|
||||||
|
var comTypes = ctx.AreaCommitmentTypes.ToDictionary(t => t.VtrgId, t => t);
|
||||||
|
MemberUnderDeliveries = underDeliveries?
|
||||||
|
.OrderBy(u => u.Key)
|
||||||
|
.Select(u => (
|
||||||
|
variants[u.Key[..2]].Name + (u.Key.Length > 2 ? " " + attributes[u.Key[2..]].Name : ""),
|
||||||
|
u.Value.Diff,
|
||||||
|
u.Value.Diff * (comTypes[u.Key].PenaltyPerKg ?? 0)
|
||||||
|
- (comTypes[u.Key].PenaltyAmount ?? 0)
|
||||||
|
- ((u.Value.Weight == 0 ? comTypes[u.Key].PenaltyNone : null) ?? 0)))
|
||||||
|
.Where(u => u.Item3 != 0)
|
||||||
|
.ToList();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -41,13 +41,12 @@
|
|||||||
<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);
|
||||||
var first = true;
|
|
||||||
@for (int i = 0; i < rows; i++) {
|
@for (int i = 0; i < rows; i++) {
|
||||||
<tr class="@(first ? "first" : "") @(rows > i + 1 ? "last" : "")">
|
<tr class="@(i == 0 ? "first" : "") @(rows == i + 1 ? "last" : "")">
|
||||||
@if (first) {
|
@if (i == 0) {
|
||||||
<td rowspan="@rows">@p.LsNr</td>
|
<td rowspan="@rows">@p.LsNr</td>
|
||||||
<td rowspan="@rows">@p.DPNr</td>
|
<td rowspan="@rows">@p.DPNr</td>
|
||||||
<td class="small">@p.Variant</td>
|
<td class="small">@p.Variant</td>
|
||||||
@@ -76,21 +75,86 @@
|
|||||||
<td colspan="2"></td>
|
<td colspan="2"></td>
|
||||||
}
|
}
|
||||||
</tr>
|
</tr>
|
||||||
first = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
</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 bold = false, bool subCat = false, bool noTopBorder = false) {
|
||||||
|
return $"<tr class=\"{(!add && !noTopBorder ? "sum" : !add ? "large" : "")} {(bold ? "large bold" : "")}\">"
|
||||||
|
+ $"<td class=\"{(subCat ? "small" : "")}\" style=\"overflow: visible;\">{name}:</td>"
|
||||||
|
+ $"<td class=\"number {(subCat ? "small" : "large")}\">{(value < 0 ? "–" : (add ? "+" : ""))}</td>"
|
||||||
|
+ $"<td class=\"number {(subCat ? "small" : "large")}\">"
|
||||||
|
+ $"<span class=\"fleft\">{Model.CurrencySymbol}</span>{Math.Abs(value ?? 0):N2}</td>"
|
||||||
|
+ $"</tr>\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<tbody style="break-inside: avoid;">
|
||||||
|
@{ var sum = Model.Data.Rows.Sum(p => p.Amount); }
|
||||||
@if (Model.Payment == null) {
|
@if (Model.Payment == null) {
|
||||||
<tr class="sum">
|
@Raw(FormatRow("Gesamt", sum, bold: true, noTopBorder: true))
|
||||||
<td colspan="7"></td>
|
|
||||||
<td colspan="2">Gesamt:</td>
|
|
||||||
<td colspan="2" class="number">@($"{Model.Data.Rows.Sum(p => p.Amount):N2}")</td>
|
|
||||||
</tr>
|
|
||||||
} else {
|
} else {
|
||||||
<tr class="sum">
|
var noBorder = true;
|
||||||
<td colspan="7"></td>
|
if (Model.Payment.NetAmount != Model.Payment.Amount) {
|
||||||
<td colspan="2">Gesamt:</td>
|
@Raw(FormatRow("Zwischensumme", Model.Payment.NetAmount, noTopBorder: noBorder))
|
||||||
<td colspan="2" class="number">@($"{Model.Payment.NetAmount:N2}")</td>
|
noBorder = false;
|
||||||
</tr>
|
@Raw(FormatRow(Model.MemberModifier, Model.Payment.Amount - Model.Payment.NetAmount, add: true))
|
||||||
|
}
|
||||||
|
if (Model.Credit == null) {
|
||||||
|
@Raw(FormatRow("Gesamtbetrag", Model.Payment.Amount, bold: true, noTopBorder: noBorder))
|
||||||
|
// TODO Mock VAT
|
||||||
|
} else {
|
||||||
|
var hasPrev = Model.Credit.PrevNetAmount != null;
|
||||||
|
@Raw(FormatRow(hasPrev ? "Gesamtbetrag" : "Nettobetrag", Model.Credit.NetAmount, bold: true, noTopBorder: noBorder))
|
||||||
|
if (hasPrev) {
|
||||||
|
@Raw(FormatRow("Bisher berücksichtigt", -Model.Credit.PrevNetAmount, add: true))
|
||||||
|
@Raw(FormatRow("Nettobetrag", Model.Credit.NetAmount - (Model.Credit.PrevNetAmount ?? 0)))
|
||||||
|
}
|
||||||
|
@Raw(FormatRow($"Mehrwertsteuer ({Model.Credit.Vat * 100} %)", Model.Credit.VatAmount, add: true))
|
||||||
|
@Raw(FormatRow("Bruttobetrag", Model.Credit.GrossAmount, bold: true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
<tbody style="break-inside: avoid;">
|
||||||
|
@{ decimal penalty = 0; }
|
||||||
|
|
||||||
|
@if (Model.MemberUnderDeliveries != null && Model.MemberUnderDeliveries.Count() > 0) {
|
||||||
|
<tr class="small">
|
||||||
|
<td colspan="2" style="padding-top: 5mm;">Anfallende Pönalen durch Unterlieferungen:</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
foreach (var u in Model.MemberUnderDeliveries) {
|
||||||
|
@Raw(FormatRow($"{u.Name} ({u.Kg:N0} kg)", u.Amount, add: true, subCat: true))
|
||||||
|
penalty += u.Amount;
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
@Raw(FormatRow("Auszahlungsbetrag", (Model.Payment?.Amount + penalty) ?? (sum + penalty), bold: true))
|
||||||
|
} else {
|
||||||
|
if (Model.Credit.Modifiers - penalty != 0) {
|
||||||
|
@Raw(FormatRow("Weitere Abzüge", Model.Credit.Modifiers - penalty, add: true))
|
||||||
|
}
|
||||||
|
if (Model.Credit.PrevModifiers != null && Model.Credit.PrevModifiers != 0) {
|
||||||
|
@Raw(FormatRow("Bereits berücksichtigte Abzüge", -Model.Credit.PrevModifiers, add: true))
|
||||||
|
}
|
||||||
|
@Raw(FormatRow("Auszahlungsbetrag", Model.Credit.Amount, bold: true))
|
||||||
}
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
|
|
||||||
|
table.credit {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
table.credit .mod {
|
table.credit .mod {
|
||||||
padding-left: 5mm;
|
padding-left: 5mm;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.credit tbody tr:not(.first):not(.last) {
|
table.credit tbody tr:not(.first) {
|
||||||
break-before: avoid;
|
break-before: avoid;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.credit tbody tr:not(.last) {
|
||||||
break-after: avoid;
|
break-after: avoid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,10 +23,26 @@ table.credit tr.last td {
|
|||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.credit tr.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,
|
||||||
padding-top: 1mm;
|
table.credit-sum td.sum {
|
||||||
|
padding-top: 1mm !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint {
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 8pt;
|
||||||
|
width: 74mm;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
margin: 2mm 4mm;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,15 @@ using System.Collections.Generic;
|
|||||||
namespace Elwig.Documents {
|
namespace Elwig.Documents {
|
||||||
public class DeliveryConfirmation : BusinessDocument {
|
public class DeliveryConfirmation : BusinessDocument {
|
||||||
|
|
||||||
|
public new static string Name => "Anlieferungsbestätigung";
|
||||||
|
|
||||||
public Season Season;
|
public Season Season;
|
||||||
public DeliveryConfirmationData Data;
|
public DeliveryConfirmationData Data;
|
||||||
public string? Text = App.Client.TextDeliveryConfirmation;
|
public string? Text = App.Client.TextDeliveryConfirmation;
|
||||||
public Dictionary<string, MemberBucket> MemberBuckets;
|
public Dictionary<string, MemberBucket> MemberBuckets;
|
||||||
|
|
||||||
public DeliveryConfirmation(AppDbContext ctx, int year, Member m, DeliveryConfirmationData data) :
|
public DeliveryConfirmation(AppDbContext ctx, int year, Member m, DeliveryConfirmationData data) :
|
||||||
base($"Anlieferungsbestätigung {year}", m) {
|
base($"{Name} {year}", m) {
|
||||||
Season = ctx.Seasons.Find(year) ?? throw new ArgumentException("invalid season");
|
Season = ctx.Seasons.Find(year) ?? throw new ArgumentException("invalid season");
|
||||||
ShowDateAndLocation = true;
|
ShowDateAndLocation = true;
|
||||||
UseBillingAddress = true;
|
UseBillingAddress = true;
|
||||||
|
|||||||
@@ -83,7 +83,7 @@
|
|||||||
lastVariant = p.Variant;
|
lastVariant = p.Variant;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<tr class="sum">
|
<tr class="sum bold">
|
||||||
<td colspan="8">Gesamt:</td>
|
<td colspan="8">Gesamt:</td>
|
||||||
<td colspan="2" class="number">@($"{Model.Data.Rows.Sum(p => p.Weight):N0}")</td>
|
<td colspan="2" class="number">@($"{Model.Data.Rows.Sum(p => p.Weight):N0}")</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
<td class="number">@($"{p.Weight:N0}")</td>
|
<td class="number">@($"{p.Weight:N0}")</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
<tr class="sum">
|
<tr class="sum bold">
|
||||||
@{
|
@{
|
||||||
var kmw = Elwig.Helpers.Utils.AggregateDeliveryPartsKmw(Model.Deliveries);
|
var kmw = Elwig.Helpers.Utils.AggregateDeliveryPartsKmw(Model.Deliveries);
|
||||||
var oe = Elwig.Helpers.Utils.KmwToOe(kmw);
|
var oe = Elwig.Helpers.Utils.KmwToOe(kmw);
|
||||||
|
|||||||
@@ -63,7 +63,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
@if (Model.Delivery.Parts.Count() > 1) {
|
@if (Model.Delivery.Parts.Count() > 1) {
|
||||||
<tr class="main sum">
|
<tr class="main sum bold">
|
||||||
<td colspan="6">Gesamt:</td>
|
<td colspan="6">Gesamt:</td>
|
||||||
<td class="center">@($"{Model.Delivery.Oe:N0}")</td>
|
<td class="center">@($"{Model.Delivery.Oe:N0}")</td>
|
||||||
<td class="center">@($"{Model.Delivery.Kmw:N1}")</td>
|
<td class="center">@($"{Model.Delivery.Kmw:N1}")</td>
|
||||||
|
|||||||
@@ -117,18 +117,16 @@ main table.number thead tr:first-child th:first-child {
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
main table tr.sum td {
|
main table tr.bold td {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
main table tr.sum,
|
main table tr.sum,
|
||||||
|
main table td.sum,
|
||||||
main table tr.new,
|
main table tr.new,
|
||||||
main table tr.border {
|
main table tr.border {
|
||||||
border-top: 0.5pt solid black;
|
border-top: 0.5pt solid black;
|
||||||
}
|
}
|
||||||
main table tr.sum {
|
|
||||||
break-before: avoid;
|
|
||||||
}
|
|
||||||
|
|
||||||
main table th.unit {
|
main table th.unit {
|
||||||
font-size: 8pt;
|
font-size: 8pt;
|
||||||
@@ -145,3 +143,11 @@ main table th.narrow {
|
|||||||
|
|
||||||
main table .lborder {border-left: 0.5pt solid black;}
|
main table .lborder {border-left: 0.5pt solid black;}
|
||||||
main table .rborder {border-right: 0.5pt solid black;}
|
main table .rborder {border-right: 0.5pt solid black;}
|
||||||
|
|
||||||
|
main table .fleft {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
main tbody.sum tr:last-child {
|
||||||
|
border-bottom: 0.5pt solid black;
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ using Elwig.Helpers.Printing;
|
|||||||
namespace Elwig.Documents {
|
namespace Elwig.Documents {
|
||||||
public abstract partial class Document : IDisposable {
|
public abstract partial class Document : IDisposable {
|
||||||
|
|
||||||
|
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;
|
||||||
@@ -93,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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,18 +2,20 @@
|
|||||||
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 {
|
||||||
|
|
||||||
|
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($"Stammdatenblatt {m.AdministrativeName}", m) {
|
public MemberDataSheet(Member m, AppDbContext ctx) : base($"{Name} {m.AdministrativeName}", m) {
|
||||||
DocumentId = $"Stammdatenblatt {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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -202,7 +202,7 @@
|
|||||||
lastContract = contractType.AreaComType.DisplayName;
|
lastContract = contractType.AreaComType.DisplayName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<tr class="sum">
|
<tr class="sum bold">
|
||||||
<td colspan="3">Gesamt:</td>
|
<td colspan="3">Gesamt:</td>
|
||||||
<td class="number">@($"{Model.Member.ActiveAreaCommitments.Select(a => a.Area).Sum():N0}")</td>
|
<td class="number">@($"{Model.Member.ActiveAreaCommitments.Select(a => a.Area).Sum():N0}")</td>
|
||||||
<td colspan="2"></td>
|
<td colspan="2"></td>
|
||||||
|
|||||||
+8
-5
@@ -7,13 +7,15 @@
|
|||||||
<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="Resources\Schemas\PaymentVariantData.json" />
|
||||||
|
<EmbeddedResource Include="Resources\Sql\*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||||
@@ -23,11 +25,12 @@
|
|||||||
<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="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" />
|
||||||
<PackageReference Include="System.IO.Ports" Version="8.0.0" />
|
<PackageReference Include="System.IO.Ports" Version="8.0.0" />
|
||||||
|
|||||||
@@ -10,10 +10,12 @@ 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 {
|
||||||
|
|
||||||
public record struct AreaComBucket(int Area, int Obligation, int Right);
|
public record struct AreaComBucket(int Area, int Obligation, int Right);
|
||||||
|
public record struct UnderDelivery(int Weight, int Diff);
|
||||||
public record struct MemberBucket(string Name, int Area, int Obligation, int Right, int Delivery, int DeliveryStrict, int Payment);
|
public record struct MemberBucket(string Name, int Area, int Obligation, int Right, int Delivery, int DeliveryStrict, int Payment);
|
||||||
|
|
||||||
public class AppDbContext : DbContext {
|
public class AppDbContext : DbContext {
|
||||||
@@ -42,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; }
|
||||||
@@ -62,12 +65,14 @@ 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 = new();
|
private readonly Dictionary<int, Dictionary<int, Dictionary<string, AreaComBucket>>> _memberAreaCommitmentBuckets = [];
|
||||||
private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberDeliveryBuckets = new();
|
private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberDeliveryBuckets = [];
|
||||||
private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberDeliveryBucketsStrict = new();
|
private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberDeliveryBucketsStrict = [];
|
||||||
private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberPaymentBuckets = new();
|
private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberPaymentBuckets = [];
|
||||||
|
private readonly Dictionary<int, Dictionary<int, Dictionary<string, UnderDelivery>>> _memberUnderDelivery = [];
|
||||||
|
|
||||||
public AppDbContext() {
|
public AppDbContext() {
|
||||||
if (App.Config.DatabaseLog != null) {
|
if (App.Config.DatabaseLog != null) {
|
||||||
@@ -98,6 +103,24 @@ namespace Elwig.Helpers {
|
|||||||
return cnx;
|
return cnx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task ExecuteBatch(SqliteConnection cnx, string sql) {
|
||||||
|
using var cmd = cnx.CreateCommand();
|
||||||
|
cmd.CommandText = sql;
|
||||||
|
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) {
|
||||||
|
using var cmd = cnx.CreateCommand();
|
||||||
|
cmd.CommandText = sql;
|
||||||
|
return await cmd.ExecuteScalarAsync();
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
|
||||||
optionsBuilder.UseSqlite(ConnectionString);
|
optionsBuilder.UseSqlite(ConnectionString);
|
||||||
optionsBuilder.UseLazyLoadingProxies();
|
optionsBuilder.UseLazyLoadingProxies();
|
||||||
@@ -178,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))
|
||||||
@@ -217,7 +247,7 @@ namespace Elwig.Helpers {
|
|||||||
while (await reader.ReadAsync()) {
|
while (await reader.ReadAsync()) {
|
||||||
var mgnr = reader.GetInt32(0);
|
var mgnr = reader.GetInt32(0);
|
||||||
var vtrgid = reader.GetString(1);
|
var vtrgid = reader.GetString(1);
|
||||||
if (!buckets.ContainsKey(mgnr)) buckets[mgnr] = new();
|
if (!buckets.ContainsKey(mgnr)) buckets[mgnr] = [];
|
||||||
buckets[mgnr][vtrgid] = new(reader.GetInt32(2), reader.GetInt32(3), reader.GetInt32(4));
|
buckets[mgnr][vtrgid] = new(reader.GetInt32(2), reader.GetInt32(3), reader.GetInt32(4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -235,7 +265,7 @@ namespace Elwig.Helpers {
|
|||||||
while (await reader.ReadAsync()) {
|
while (await reader.ReadAsync()) {
|
||||||
var mgnr = reader.GetInt32(0);
|
var mgnr = reader.GetInt32(0);
|
||||||
var bucket = reader.GetString(1);
|
var bucket = reader.GetString(1);
|
||||||
if (!buckets.ContainsKey(mgnr)) buckets[mgnr] = new();
|
if (!buckets.ContainsKey(mgnr)) buckets[mgnr] = [];
|
||||||
buckets[mgnr][bucket] = reader.GetInt32(2);
|
buckets[mgnr][bucket] = reader.GetInt32(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -253,7 +283,7 @@ namespace Elwig.Helpers {
|
|||||||
while (await reader.ReadAsync()) {
|
while (await reader.ReadAsync()) {
|
||||||
var mgnr = reader.GetInt32(0);
|
var mgnr = reader.GetInt32(0);
|
||||||
var bucket = reader.GetString(1);
|
var bucket = reader.GetString(1);
|
||||||
if (!buckets.ContainsKey(mgnr)) buckets[mgnr] = new();
|
if (!buckets.ContainsKey(mgnr)) buckets[mgnr] = [];
|
||||||
buckets[mgnr][bucket] = reader.GetInt32(2);
|
buckets[mgnr][bucket] = reader.GetInt32(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -271,7 +301,7 @@ namespace Elwig.Helpers {
|
|||||||
while (await reader.ReadAsync()) {
|
while (await reader.ReadAsync()) {
|
||||||
var mgnr = reader.GetInt32(0);
|
var mgnr = reader.GetInt32(0);
|
||||||
var bucket = reader.GetString(1);
|
var bucket = reader.GetString(1);
|
||||||
if (!buckets.ContainsKey(mgnr)) buckets[mgnr] = new();
|
if (!buckets.ContainsKey(mgnr)) buckets[mgnr] = [];
|
||||||
buckets[mgnr][bucket] = reader.GetInt32(2);
|
buckets[mgnr][bucket] = reader.GetInt32(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,28 +309,52 @@ namespace Elwig.Helpers {
|
|||||||
_memberPaymentBuckets[year] = buckets;
|
_memberPaymentBuckets[year] = buckets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task FetchMemberUnderDelivery(int year, SqliteConnection? cnx = null) {
|
||||||
|
var ownCnx = cnx == null;
|
||||||
|
cnx ??= await ConnectAsync();
|
||||||
|
var buckets = new Dictionary<int, Dictionary<string, UnderDelivery>>();
|
||||||
|
using (var cmd = cnx.CreateCommand()) {
|
||||||
|
cmd.CommandText = $"SELECT mgnr, bucket, weight, diff FROM v_under_delivery WHERE year = {year}";
|
||||||
|
using var reader = await cmd.ExecuteReaderAsync();
|
||||||
|
while (await reader.ReadAsync()) {
|
||||||
|
var mgnr = reader.GetInt32(0);
|
||||||
|
var bucket = reader.GetString(1);
|
||||||
|
if (!buckets.ContainsKey(mgnr)) buckets[mgnr] = [];
|
||||||
|
buckets[mgnr][bucket] = new(reader.GetInt32(2), reader.GetInt32(3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ownCnx) await cnx.DisposeAsync();
|
||||||
|
_memberUnderDelivery[year] = buckets;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<Dictionary<string, AreaComBucket>> GetMemberAreaCommitmentBuckets(int year, int mgnr, SqliteConnection? cnx = null) {
|
public async Task<Dictionary<string, AreaComBucket>> GetMemberAreaCommitmentBuckets(int year, int mgnr, SqliteConnection? cnx = null) {
|
||||||
if (!_memberAreaCommitmentBuckets.ContainsKey(year))
|
if (!_memberAreaCommitmentBuckets.ContainsKey(year))
|
||||||
await FetchMemberAreaCommitmentBuckets(year, cnx);
|
await FetchMemberAreaCommitmentBuckets(year, cnx);
|
||||||
return _memberAreaCommitmentBuckets[year].GetValueOrDefault(mgnr, new());
|
return _memberAreaCommitmentBuckets[year].GetValueOrDefault(mgnr, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Dictionary<string, int>> GetMemberDeliveryBuckets(int year, int mgnr, SqliteConnection? cnx = null) {
|
public async Task<Dictionary<string, int>> GetMemberDeliveryBuckets(int year, int mgnr, SqliteConnection? cnx = null) {
|
||||||
if (!_memberDeliveryBuckets.ContainsKey(year))
|
if (!_memberDeliveryBuckets.ContainsKey(year))
|
||||||
await FetchMemberDeliveryBuckets(year, cnx);
|
await FetchMemberDeliveryBuckets(year, cnx);
|
||||||
return _memberDeliveryBuckets[year].GetValueOrDefault(mgnr, new());
|
return _memberDeliveryBuckets[year].GetValueOrDefault(mgnr, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Dictionary<string, int>> GetMemberDeliveryBucketsStrict(int year, int mgnr, SqliteConnection? cnx = null) {
|
public async Task<Dictionary<string, int>> GetMemberDeliveryBucketsStrict(int year, int mgnr, SqliteConnection? cnx = null) {
|
||||||
if (!_memberDeliveryBucketsStrict.ContainsKey(year))
|
if (!_memberDeliveryBucketsStrict.ContainsKey(year))
|
||||||
await FetchMemberDeliveryBucketsStrict(year, cnx);
|
await FetchMemberDeliveryBucketsStrict(year, cnx);
|
||||||
return _memberDeliveryBucketsStrict[year].GetValueOrDefault(mgnr, new());
|
return _memberDeliveryBucketsStrict[year].GetValueOrDefault(mgnr, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Dictionary<string, int>> GetMemberPaymentBuckets(int year, int mgnr, SqliteConnection? cnx = null) {
|
public async Task<Dictionary<string, int>> GetMemberPaymentBuckets(int year, int mgnr, SqliteConnection? cnx = null) {
|
||||||
if (!_memberPaymentBuckets.ContainsKey(year))
|
if (!_memberPaymentBuckets.ContainsKey(year))
|
||||||
await FetchMemberPaymentBuckets(year, cnx);
|
await FetchMemberPaymentBuckets(year, cnx);
|
||||||
return _memberPaymentBuckets[year].GetValueOrDefault(mgnr, new());
|
return _memberPaymentBuckets[year].GetValueOrDefault(mgnr, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Dictionary<string, UnderDelivery>> GetMemberUnderDelivery(int year, int mgnr, SqliteConnection? cnx = null) {
|
||||||
|
if (!_memberUnderDelivery.ContainsKey(year))
|
||||||
|
await FetchMemberUnderDelivery(year, cnx);
|
||||||
|
return _memberUnderDelivery[year].GetValueOrDefault(mgnr, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Dictionary<string, MemberBucket>> GetMemberBuckets(int year, int mgnr, SqliteConnection? cnx = null) {
|
public async Task<Dictionary<string, MemberBucket>> GetMemberBuckets(int year, int mgnr, SqliteConnection? cnx = null) {
|
||||||
|
|||||||
+47
-760
@@ -1,45 +1,33 @@
|
|||||||
using Microsoft.Data.Sqlite;
|
using Microsoft.Data.Sqlite;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Elwig.Helpers {
|
namespace Elwig.Helpers {
|
||||||
public static class AppDbUpdater {
|
public static class AppDbUpdater {
|
||||||
|
|
||||||
public static readonly int RequiredSchemaVersion = 11;
|
// 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;
|
||||||
private static readonly Action<SqliteConnection>[] _updaters = new[] {
|
|
||||||
UpdateDbSchema_1_To_2, UpdateDbSchema_2_To_3, UpdateDbSchema_3_To_4, UpdateDbSchema_4_To_5,
|
|
||||||
UpdateDbSchema_5_To_6, UpdateDBSchema_6_To_7, UpdateDbSchema_7_To_8, UpdateDbSchema_8_To_9,
|
|
||||||
UpdateDbSchema_9_To_10, UpdateDbSchema_10_To_11
|
|
||||||
};
|
|
||||||
|
|
||||||
private static void ExecuteNonQuery(SqliteConnection cnx, string sql) {
|
public static async Task<string> CheckDb() {
|
||||||
using var cmd = cnx.CreateCommand();
|
|
||||||
cmd.CommandText = sql;
|
|
||||||
cmd.ExecuteNonQuery();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static object? ExecuteScalar(SqliteConnection cnx, string sql) {
|
|
||||||
using var cmd = cnx.CreateCommand();
|
|
||||||
cmd.CommandText = sql;
|
|
||||||
return cmd.ExecuteScalar();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string CheckDb() {
|
|
||||||
using var cnx = AppDbContext.Connect();
|
using var cnx = AppDbContext.Connect();
|
||||||
|
|
||||||
var applId = (long?)ExecuteScalar(cnx, "PRAGMA application_id") ?? 0;
|
var applId = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA application_id") ?? 0;
|
||||||
if (applId != 0x454C5747) throw new Exception("Invalid application_id of database");
|
if (applId != 0x454C5747) throw new Exception("Invalid application_id of database");
|
||||||
|
|
||||||
var schemaVers = (long?)ExecuteScalar(cnx, "PRAGMA schema_version") ?? 0;
|
var schemaVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA schema_version") ?? 0;
|
||||||
_versionOffset = (int)(schemaVers % 100);
|
VersionOffset = (int)(schemaVers % 100);
|
||||||
if (_versionOffset != 0) {
|
if (VersionOffset != 0) {
|
||||||
// schema was modified manually/externally
|
// schema was modified manually/externally
|
||||||
// TODO issue warning
|
// TODO issue warning
|
||||||
}
|
}
|
||||||
UpdateDbSchema(cnx, (int)(schemaVers / 100), RequiredSchemaVersion);
|
await UpdateDbSchema(cnx, (int)(schemaVers / 100), RequiredSchemaVersion);
|
||||||
|
|
||||||
var userVers = (long?)ExecuteScalar(cnx, "PRAGMA user_version") ?? 0;
|
var userVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA user_version") ?? 0;
|
||||||
var major = userVers >> 24;
|
var major = userVers >> 24;
|
||||||
var minor = (userVers >> 16) & 0xFF;
|
var minor = (userVers >> 16) & 0xFF;
|
||||||
var patch = userVers & 0xFFFF;
|
var patch = userVers & 0xFFFF;
|
||||||
@@ -48,757 +36,56 @@ namespace Elwig.Helpers {
|
|||||||
(App.VersionMajor == major && App.VersionMinor > minor) ||
|
(App.VersionMajor == major && App.VersionMinor > minor) ||
|
||||||
(App.VersionMajor == major && App.VersionMinor == minor && App.VersionPatch > patch)) {
|
(App.VersionMajor == major && App.VersionMinor == minor && App.VersionPatch > patch)) {
|
||||||
long vers = (App.VersionMajor << 24) | (App.VersionMinor << 16) | App.VersionPatch;
|
long vers = (App.VersionMajor << 24) | (App.VersionMinor << 16) | App.VersionPatch;
|
||||||
ExecuteNonQuery(cnx, $"PRAGMA user_version = {vers}");
|
await AppDbContext.ExecuteBatch(cnx, $"PRAGMA user_version = {vers}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return $"{major}.{minor}.{patch}";
|
return $"{major}.{minor}.{patch}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void 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 (toVersion - 1 > _updaters.Length) {
|
|
||||||
throw new Exception("Unable to update database schema: Updater not implemented");
|
|
||||||
} else if (fromVersion <= 0) {
|
} else if (fromVersion <= 0) {
|
||||||
throw new Exception("schema_version of database is invalid");
|
throw new Exception("schema_version of database is invalid");
|
||||||
}
|
}
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, "PRAGMA locking_mode = EXCLUSIVE");
|
var asm = Assembly.GetExecutingAssembly();
|
||||||
ExecuteNonQuery(cnx, "PRAGMA foreign_keys = OFF");
|
(int From, int To, string Name)[] scripts = asm.GetManifestResourceNames()
|
||||||
ExecuteNonQuery(cnx, "BEGIN EXCLUSIVE");
|
.Where(n => n.StartsWith("Elwig.Resources.Sql."))
|
||||||
for (int i = fromVersion; i < toVersion; i++) {
|
.Select(n => {
|
||||||
_updaters[i - 1](cnx);
|
var p = n.Split(".")[^2].Split("-");
|
||||||
|
return (int.Parse(p[0]), int.Parse(p[1]), n);
|
||||||
|
})
|
||||||
|
.OrderBy(s => s.Item1).ThenBy(s => s.Item2)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
List<string> toExecute = [];
|
||||||
|
var vers = fromVersion;
|
||||||
|
while (vers < toVersion) {
|
||||||
|
var (_, to, name) = scripts.Where(s => s.From == vers).Last();
|
||||||
|
toExecute.Add(name);
|
||||||
|
vers = to;
|
||||||
}
|
}
|
||||||
ExecuteNonQuery(cnx, "PRAGMA foreign_key_check");
|
if (toExecute.Count == 0)
|
||||||
ExecuteNonQuery(cnx, "COMMIT");
|
return;
|
||||||
ExecuteNonQuery(cnx, "PRAGMA foreign_keys = ON");
|
|
||||||
ExecuteNonQuery(cnx, "VACUUM");
|
|
||||||
ExecuteNonQuery(cnx, $"PRAGMA schema_version = {toVersion * 100 + _versionOffset}");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void UpdateDbSchema_1_To_2(SqliteConnection cnx) {
|
await AppDbContext.ExecuteBatch(cnx, """
|
||||||
ExecuteNonQuery(cnx, "DROP VIEW v_area_commitment");
|
PRAGMA locking_mode = EXCLUSIVE;
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE delivery_part DROP COLUMN weighing_reason");
|
PRAGMA foreign_keys = OFF;
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE delivery_part ADD COLUMN weighing_reason TEXT CHECK(NOT (manual_weighing = FALSE AND weighing_reason IS NOT NULL))");
|
BEGIN EXCLUSIVE;
|
||||||
}
|
|
||||||
|
|
||||||
private static void UpdateDbSchema_2_To_3(SqliteConnection cnx) {
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE TABLE delivery_part_bin (
|
|
||||||
year INTEGER NOT NULL,
|
|
||||||
did INTEGER NOT NULL,
|
|
||||||
dpnr INTEGER NOT NULL,
|
|
||||||
binnr INTEGER NOT NULL,
|
|
||||||
|
|
||||||
discr TEXT NOT NULL,
|
|
||||||
value INTEGER NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT pk_delivery_part_bin PRIMARY KEY (year, did, dpnr, binnr),
|
|
||||||
CONSTRAINT fk_delivery_part_bin_delivery_part FOREIGN KEY (year, did, dpnr) REFERENCES delivery_part (year, did, dpnr)
|
|
||||||
ON UPDATE CASCADE
|
|
||||||
ON DELETE CASCADE
|
|
||||||
) STRICT;
|
|
||||||
""");
|
""");
|
||||||
ExecuteNonQuery(cnx, """
|
foreach (var script in toExecute) {
|
||||||
INSERT INTO delivery_part_bin (year, did, dpnr, binnr, discr, value)
|
await AppDbContext.ExecuteEmbeddedScript(cnx, asm, script);
|
||||||
SELECT year, did, dpnr, 0, '_', bucket_2 + bucket_3
|
}
|
||||||
FROM payment_delivery_part
|
await AppDbContext.ExecuteBatch(cnx, $"""
|
||||||
WHERE COALESCE(bucket_1, bucket_2, bucket_3, bucket_4, bucket_5, bucket_6, bucket_7, bucket_8, bucket_9) IS NOT NULL
|
PRAGMA foreign_key_check;
|
||||||
ON CONFLICT DO NOTHING;
|
COMMIT;
|
||||||
|
PRAGMA foreign_keys = ON;
|
||||||
|
VACUUM;
|
||||||
|
PRAGMA schema_version = {toVersion * 100 + VersionOffset};
|
||||||
""");
|
""");
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
INSERT INTO delivery_part_bin (year, did, dpnr, binnr, discr, value)
|
|
||||||
SELECT d.year, d.did, d.dpnr, 1, COALESCE(attributes, ''), bucket_1
|
|
||||||
FROM payment_delivery_part p
|
|
||||||
JOIN v_delivery d ON (d.year, d.did, d.dpnr) = (p.year, p.did, p.dpnr)
|
|
||||||
WHERE COALESCE(bucket_1, bucket_2, bucket_3, bucket_4, bucket_5, bucket_6, bucket_7, bucket_8, bucket_9) IS NOT NULL
|
|
||||||
ON CONFLICT DO NOTHING;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_1");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_2");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_3");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_4");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_5");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_6");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_7");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_8");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN bucket_9");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_1_name");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_2_name");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_3_name");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_4_name");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_5_name");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_6_name");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_7_name");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_8_name");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_variant DROP COLUMN bucket_9_name");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE delivery_part ADD COLUMN gebunden INTEGER CHECK (gebunden IN (TRUE, FALSE)) DEFAULT NULL");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, "DROP VIEW v_delivery");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE VIEW v_delivery AS
|
|
||||||
SELECT s.*, GROUP_CONCAT(o.modid) AS modifiers
|
|
||||||
FROM (SELECT p.year, p.did, p.dpnr,
|
|
||||||
d.date, d.time, d.zwstid, d.lnr, d.lsnr,
|
|
||||||
m.mgnr, m.family_name, m.given_name,
|
|
||||||
p.sortid, p.weight, p.kmw, ROUND(p.kmw * (4.54 + 0.022 * p.kmw), 0) AS oe, p.qualid, p.hkid, p.kgnr, p.rdnr,
|
|
||||||
p.gerebelt, p.gebunden,
|
|
||||||
p.qualid IN (SELECT l.qualid FROM wine_quality_level l WHERE NOT l.predicate AND (p.kmw >= l.min_kmw OR l.min_kmw IS NULL) ORDER BY l.min_kmw DESC LIMIT 1,100) AS abgewertet,
|
|
||||||
p.qualid NOT IN ('WEI', 'RSW', 'LDW') AS min_quw,
|
|
||||||
GROUP_CONCAT(a.attrid) AS attributes,
|
|
||||||
COALESCE(SUM(a.fill_lower_bins), 0) AS attribute_prio,
|
|
||||||
d.comment, p.comment AS part_comment
|
|
||||||
FROM delivery_part p
|
|
||||||
JOIN delivery d ON (d.year, d.did) = (p.year, p.did)
|
|
||||||
JOIN member m ON m.mgnr = d.mgnr
|
|
||||||
LEFT JOIN delivery_part_attribute pa ON (pa.year, pa.did, pa.dpnr) = (p.year, p.did, p.dpnr)
|
|
||||||
LEFT JOIN wine_attribute a ON a.attrid = pa.attrid
|
|
||||||
GROUP BY p.year, p.did, p.dpnr
|
|
||||||
ORDER BY p.year, p.did, p.dpnr, a.attrid) s
|
|
||||||
LEFT JOIN delivery_part_modifier o ON (o.year, o.did, o.dpnr) = (s.year, s.did, s.dpnr)
|
|
||||||
GROUP BY s.year, s.lsnr, s.dpnr
|
|
||||||
ORDER BY s.year, s.lsnr, s.dpnr, o.modid;
|
|
||||||
""");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, "DROP VIEW v_bucket");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE VIEW v_delivery_bin AS
|
|
||||||
SELECT year, mgnr,
|
|
||||||
sortid || IIF(min_quw, REPLACE(COALESCE(attributes, ''), ',', ''), '_') AS bin,
|
|
||||||
SUM(weight) AS weight
|
|
||||||
FROM v_delivery
|
|
||||||
GROUP BY year, mgnr, bin
|
|
||||||
ORDER BY year, mgnr, LENGTH(bin) DESC, bin;
|
|
||||||
""");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE VIEW v_payment_bin AS
|
|
||||||
SELECT d.year, d.mgnr,
|
|
||||||
sortid || discr AS bin,
|
|
||||||
SUM(value) AS weight
|
|
||||||
FROM v_delivery d
|
|
||||||
JOIN delivery_part_bin b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr)
|
|
||||||
GROUP BY d.year, d.mgnr, bin
|
|
||||||
HAVING SUM(value) > 0
|
|
||||||
ORDER BY d.year, d.mgnr, bin;
|
|
||||||
""");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE wine_attribute ADD COLUMN fill_lower_bins INTEGER NOT NULL CHECK (fill_lower_bins IN (0, 1, 2)) DEFAULT 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void UpdateDbSchema_3_To_4(SqliteConnection cnx) {
|
|
||||||
ExecuteNonQuery(cnx, "DROP VIEW v_payment_bin");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE VIEW v_payment_bin AS
|
|
||||||
SELECT d.year, d.mgnr,
|
|
||||||
sortid || discr AS bin,
|
|
||||||
SUM(value) AS weight
|
|
||||||
FROM v_delivery d
|
|
||||||
JOIN delivery_part_bin b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr)
|
|
||||||
GROUP BY d.year, d.mgnr, bin
|
|
||||||
HAVING SUM(value) > 0
|
|
||||||
ORDER BY d.year, d.mgnr, LENGTH(bin) DESC, bin;
|
|
||||||
""");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE VIEW v_area_commitment_bin AS
|
|
||||||
SELECT s.year, c.mgnr,
|
|
||||||
c.vtrgid AS bin,
|
|
||||||
CAST(ROUND(SUM(COALESCE(area * min_kg_per_ha, 0)) / 10000.0, 0) AS INTEGER) AS min_kg,
|
|
||||||
CAST(ROUND(SUM(COALESCE(area * max_kg_per_ha, 0)) / 10000.0, 0) AS INTEGER) AS max_kg
|
|
||||||
FROM area_commitment c, season s
|
|
||||||
JOIN area_commitment_type t ON t.vtrgid = c.vtrgid
|
|
||||||
WHERE (year_from IS NULL OR year_from <= s.year) AND
|
|
||||||
(year_to IS NULL OR year_to >= s.year)
|
|
||||||
GROUP BY s.year, c.mgnr, c.vtrgid
|
|
||||||
ORDER BY s.year, c.mgnr, LENGTH(c.vtrgid) DESC, c.vtrgid;
|
|
||||||
""");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void UpdateDbSchema_4_To_5(SqliteConnection cnx) {
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE TABLE _area_commitment_type (
|
|
||||||
vtrgid TEXT NOT NULL CHECK (vtrgid = sortid || COALESCE(attrid, '') || disc),
|
|
||||||
sortid TEXT NOT NULL,
|
|
||||||
attrid TEXT,
|
|
||||||
disc TEXT DEFAULT NULL CHECK (disc REGEXP '^[A-Z0-9]+$'),
|
|
||||||
|
|
||||||
min_kg_per_ha INTEGER,
|
|
||||||
max_kg_per_ha INTEGER,
|
|
||||||
penalty_amount INTEGER,
|
|
||||||
|
|
||||||
CONSTRAINT pk_area_commitment_type PRIMARY KEY (vtrgid),
|
|
||||||
CONSTRAINT sk_area_commitment_type_sort_attr UNIQUE (sortid, attrid, disc),
|
|
||||||
CONSTRAINT fk_area_commitment_type_wine_variety FOREIGN KEY (sortid) REFERENCES wine_variety (sortid)
|
|
||||||
ON UPDATE CASCADE
|
|
||||||
ON DELETE RESTRICT,
|
|
||||||
CONSTRAINT fk_area_commitment_type_wine_attribute FOREIGN KEY (attrid) REFERENCES wine_attribute (attrid)
|
|
||||||
ON UPDATE CASCADE
|
|
||||||
ON DELETE RESTRICT
|
|
||||||
) STRICT;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
INSERT INTO _area_commitment_type (vtrgid, sortid, attrid, disc, min_kg_per_ha, max_kg_per_ha, penalty_amount)
|
|
||||||
SELECT vtrgid, sortid, attrid_1, disc, min_kg_per_ha, max_kg_per_ha, penalty_amount FROM area_commitment_type
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, "PRAGMA writable_schema = ON");
|
|
||||||
ExecuteNonQuery(cnx, "DROP TABLE area_commitment_type");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE _area_commitment_type RENAME TO area_commitment_type");
|
|
||||||
ExecuteNonQuery(cnx, "PRAGMA writable_schema = OFF");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
ALTER TABLE delivery_part ADD COLUMN attrid TEXT DEFAULT NULL
|
|
||||||
REFERENCES wine_attribute (attrid)
|
|
||||||
ON UPDATE CASCADE
|
|
||||||
ON DELETE RESTRICT
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
UPDATE delivery_part
|
|
||||||
SET attrid = (SELECT attrid
|
|
||||||
FROM delivery_part_attribute a
|
|
||||||
WHERE (delivery_part.year, delivery_part.did, delivery_part.dpnr) = (a.year, a.did, a.dpnr)
|
|
||||||
ORDER BY attrid DESC
|
|
||||||
LIMIT 1)
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, "DROP TRIGGER t_delivery_part_attribute_i_mtime_delivery_part");
|
|
||||||
ExecuteNonQuery(cnx, "DROP TRIGGER t_delivery_part_attribute_u_mtime_delivery_part");
|
|
||||||
ExecuteNonQuery(cnx, "DROP TRIGGER t_delivery_part_attribute_d_mtime_delivery_part");
|
|
||||||
ExecuteNonQuery(cnx, "DROP TABLE delivery_part_attribute");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, "DROP VIEW v_delivery");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE VIEW v_delivery AS
|
|
||||||
SELECT p.year, p.did, p.dpnr,
|
|
||||||
d.date, d.time, d.zwstid, d.lnr, d.lsnr,
|
|
||||||
m.mgnr, m.family_name, m.given_name,
|
|
||||||
p.sortid, a.attrid,
|
|
||||||
p.weight, p.kmw, ROUND(p.kmw * (4.54 + 0.022 * p.kmw), 0) AS oe, p.qualid,
|
|
||||||
p.hkid, p.kgnr, p.rdnr,
|
|
||||||
p.gerebelt, p.gebunden,
|
|
||||||
p.qualid IN (SELECT l.qualid FROM wine_quality_level l WHERE NOT l.predicate AND (p.kmw >= l.min_kmw OR l.min_kmw IS NULL) ORDER BY l.min_kmw DESC LIMIT 1,100) AS abgewertet,
|
|
||||||
p.qualid NOT IN ('WEI', 'RSW', 'LDW') AS min_quw,
|
|
||||||
COALESCE(a.fill_lower_bins, 0) AS attribute_prio,
|
|
||||||
GROUP_CONCAT(o.modid) AS modifiers,
|
|
||||||
d.comment, p.comment AS part_comment
|
|
||||||
FROM delivery_part p
|
|
||||||
JOIN delivery d ON (d.year, d.did) = (p.year, p.did)
|
|
||||||
JOIN member m ON m.mgnr = d.mgnr
|
|
||||||
LEFT JOIN wine_attribute a ON a.attrid = p.attrid
|
|
||||||
LEFT JOIN delivery_part_modifier o ON (o.year, o.did, o.dpnr) = (p.year, p.did, p.dpnr)
|
|
||||||
GROUP BY p.year, p.did, p.dpnr
|
|
||||||
ORDER BY p.year, p.did, p.dpnr, o.modid;
|
|
||||||
""");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, "DROP VIEW v_delivery_bin");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE VIEW v_delivery_bin AS
|
|
||||||
SELECT year, mgnr,
|
|
||||||
sortid || IIF(min_quw, COALESCE(attrid, ''), '_') AS bin,
|
|
||||||
SUM(weight) AS weight
|
|
||||||
FROM v_delivery
|
|
||||||
GROUP BY year, mgnr, bin
|
|
||||||
ORDER BY year, mgnr, LENGTH(bin) DESC, bin;
|
|
||||||
""");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, "DROP VIEW v_stat_attr");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE VIEW v_stat_attr AS
|
|
||||||
SELECT year, attrid,
|
|
||||||
SUM(weight) as sum,
|
|
||||||
ROUND(SUM(kmw * weight) / SUM(weight), 2) AS kmw,
|
|
||||||
ROUND(SUM(oe * weight) / SUM(weight), 1) AS oe,
|
|
||||||
COUNT(DISTINCT did) AS lieferungen,
|
|
||||||
COUNT(DISTINCT mgnr) AS mitglieder
|
|
||||||
FROM v_delivery
|
|
||||||
GROUP BY year, attrid
|
|
||||||
ORDER BY year, attrid;
|
|
||||||
""");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, "DROP VIEW v_stat_sort_attr");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE VIEW v_stat_sort_attr AS
|
|
||||||
SELECT year, sortid, attrid,
|
|
||||||
SUM(weight) as sum,
|
|
||||||
ROUND(SUM(kmw * weight) / SUM(weight), 2) AS kmw,
|
|
||||||
ROUND(SUM(oe * weight) / SUM(weight), 1) AS oe,
|
|
||||||
COUNT(DISTINCT did) AS lieferungen,
|
|
||||||
COUNT(DISTINCT mgnr) AS mitglieder
|
|
||||||
FROM v_delivery
|
|
||||||
GROUP BY year, sortid, attrid
|
|
||||||
ORDER BY year, sortid, attrid;
|
|
||||||
""");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void UpdateDbSchema_5_To_6(SqliteConnection cnx) {
|
|
||||||
ExecuteNonQuery(cnx, "DROP VIEW IF EXISTS v_area_commitment");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, "PRAGMA writable_schema = ON");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE wine_attribute DROP COLUMN fill_lower_bins");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE wine_attribute ADD COLUMN strict INTEGER NOT NULL CHECK (strict IN (TRUE, FALSE)) DEFAULT FALSE");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE wine_attribute ADD COLUMN fill_lower INTEGER NOT NULL CHECK (fill_lower IN (0, 1, 2)) DEFAULT 0");
|
|
||||||
ExecuteNonQuery(cnx, "DROP VIEW v_delivery");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE VIEW v_delivery AS
|
|
||||||
SELECT p.year, p.did, p.dpnr,
|
|
||||||
d.date, d.time, d.zwstid, d.lnr, d.lsnr,
|
|
||||||
m.mgnr, m.family_name, m.given_name,
|
|
||||||
p.sortid, a.attrid,
|
|
||||||
p.weight, p.kmw, ROUND(p.kmw * (4.54 + 0.022 * p.kmw), 0) AS oe, p.qualid,
|
|
||||||
p.hkid, p.kgnr, p.rdnr,
|
|
||||||
p.gerebelt, p.gebunden,
|
|
||||||
p.qualid IN (SELECT l.qualid FROM wine_quality_level l WHERE NOT l.predicate AND (p.kmw >= l.min_kmw OR l.min_kmw IS NULL) ORDER BY l.min_kmw DESC LIMIT 1,100) AS abgewertet,
|
|
||||||
p.qualid NOT IN ('WEI', 'RSW', 'LDW') AS min_quw,
|
|
||||||
IIF(a.strict, COALESCE(a.fill_lower, 0), 0) AS attribute_prio,
|
|
||||||
GROUP_CONCAT(o.modid) AS modifiers,
|
|
||||||
d.comment, p.comment AS part_comment
|
|
||||||
FROM delivery_part p
|
|
||||||
JOIN delivery d ON (d.year, d.did) = (p.year, p.did)
|
|
||||||
JOIN member m ON m.mgnr = d.mgnr
|
|
||||||
LEFT JOIN wine_attribute a ON a.attrid = p.attrid
|
|
||||||
LEFT JOIN delivery_part_modifier o ON (o.year, o.did, o.dpnr) = (p.year, p.did, p.dpnr)
|
|
||||||
GROUP BY p.year, p.did, p.dpnr
|
|
||||||
ORDER BY p.year, p.did, p.dpnr, o.modid;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, "PRAGMA writable_schema = OFF");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, "DROP VIEW v_area_commitment_bin");
|
|
||||||
ExecuteNonQuery(cnx, "DROP VIEW v_delivery_bin");
|
|
||||||
ExecuteNonQuery(cnx, "DROP VIEW v_payment_bin");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE area_commitment_type DROP COLUMN max_kg_per_ha");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE area_commitment_type ADD COLUMN penalty_per_kg INTEGER DEFAULT NULL");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE area_commitment_type ADD COLUMN penalty_none INTEGER DEFAULT NULL");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE wine_cultivation ADD COLUMN description TEXT DEFAULT NULL");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE member ADD COLUMN organic INTEGER NOT NULL CHECK (organic IN (TRUE, FALSE)) DEFAULT FALSE");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE season ADD COLUMN max_kg_per_ha INTEGER NOT NULL DEFAULT 10000");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE season ADD COLUMN vat_normal REAL NOT NULL DEFAULT 0.10");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE season ADD COLUMN vat_flatrate REAL NOT NULL DEFAULT 0.13");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE season ADD COLUMN min_kg_per_bs INTEGER NOT NULL DEFAULT 750");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE season ADD COLUMN max_kg_per_bs INTEGER NOT NULL DEFAULT 3000");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE season ADD COLUMN penalty_per_kg INTEGER DEFAULT NULL");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE season ADD COLUMN penalty_amount INTEGER DEFAULT NULL");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE season ADD COLUMN penalty_none INTEGER DEFAULT NULL");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, "DELETE FROM client_parameter WHERE param IN ('DELIVERY_RIGHT', 'DELIVERY_OBLIGATION', 'VAT_NORMAL', 'VAT_REDUCED', 'VAT_FLATRATE')");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE TABLE delivery_part_bucket (
|
|
||||||
year INTEGER NOT NULL,
|
|
||||||
did INTEGER NOT NULL,
|
|
||||||
dpnr INTEGER NOT NULL,
|
|
||||||
bktnr INTEGER NOT NULL,
|
|
||||||
|
|
||||||
discr TEXT NOT NULL,
|
|
||||||
value INTEGER NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT pk_delivery_part_bucket PRIMARY KEY (year, did, dpnr, bktnr),
|
|
||||||
CONSTRAINT fk_delivery_part_bucket_delivery_part FOREIGN KEY (year, did, dpnr) REFERENCES delivery_part (year, did, dpnr)
|
|
||||||
ON UPDATE CASCADE
|
|
||||||
ON DELETE CASCADE
|
|
||||||
) STRICT;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value)
|
|
||||||
SELECT year, did, dpnr, binnr, discr, value
|
|
||||||
FROM delivery_part_bin
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, "DROP TABLE delivery_part_bin");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE VIEW v_area_commitment_bucket_strict AS
|
|
||||||
SELECT s.year, c.mgnr,
|
|
||||||
t.sortid || COALESCE(a.attrid, '') AS bucket,
|
|
||||||
t.sortid, a.attrid,
|
|
||||||
CAST(ROUND(SUM(area) * COALESCE(t.min_kg_per_ha, 0) / 10000.0, 0) AS INTEGER) AS min_kg,
|
|
||||||
CAST(ROUND(SUM(area) * MIN(COALESCE(a.max_kg_per_ha, s.max_kg_per_ha), s.max_kg_per_ha) / 10000.0, 0) AS INTEGER) AS max_kg,
|
|
||||||
CAST(ROUND(SUM(area) * s.max_kg_per_ha / 10000.0, 0) AS INTEGER) AS upper_max_kg
|
|
||||||
FROM season s, area_commitment c
|
|
||||||
JOIN area_commitment_type t ON t.vtrgid = c.vtrgid
|
|
||||||
LEFT JOIN wine_attribute a ON a.attrid = t.attrid
|
|
||||||
WHERE (year_from IS NULL OR year_from <= s.year) AND
|
|
||||||
(year_to IS NULL OR year_to >= s.year)
|
|
||||||
GROUP BY s.year, c.mgnr, bucket
|
|
||||||
ORDER BY s.year, c.mgnr, bucket;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE VIEW v_area_commitment_bucket AS
|
|
||||||
SELECT year, mgnr, bucket, min_kg, max_kg
|
|
||||||
FROM v_area_commitment_bucket_strict
|
|
||||||
WHERE attrid IS NOT NULL
|
|
||||||
UNION ALL
|
|
||||||
SELECT b.year, b.mgnr, b.sortid,
|
|
||||||
SUM(b.min_kg) AS min_kg,
|
|
||||||
SUM(b.upper_max_kg) AS max_kg
|
|
||||||
FROM v_area_commitment_bucket_strict b
|
|
||||||
LEFT JOIN wine_attribute a ON a.attrid = b.attrid
|
|
||||||
WHERE a.strict IS NULL OR a.strict = FALSE
|
|
||||||
GROUP BY b.year, b.mgnr, b.sortid
|
|
||||||
ORDER BY year, mgnr, bucket;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE VIEW v_delivery_bucket_strict AS
|
|
||||||
SELECT year, mgnr,
|
|
||||||
sortid || IIF(min_quw, COALESCE(attrid, ''), '_') AS bucket,
|
|
||||||
sortid, IIF(min_quw, attrid, NULL) AS attrid,
|
|
||||||
SUM(weight) AS weight,
|
|
||||||
min_quw
|
|
||||||
FROM v_delivery
|
|
||||||
GROUP BY year, mgnr, bucket
|
|
||||||
ORDER BY year, mgnr, bucket;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE VIEW v_delivery_bucket AS
|
|
||||||
SELECT year, mgnr, bucket, weight
|
|
||||||
FROM v_delivery_bucket_strict
|
|
||||||
WHERE attrid IS NOT NULL OR NOT min_quw
|
|
||||||
UNION ALL
|
|
||||||
SELECT b.year, b.mgnr, b.sortid,
|
|
||||||
SUM(b.weight) AS weight
|
|
||||||
FROM v_delivery_bucket_strict b
|
|
||||||
LEFT JOIN wine_attribute a ON a.attrid = b.attrid
|
|
||||||
WHERE min_quw AND (a.strict IS NULL OR a.strict = FALSE)
|
|
||||||
GROUP BY b.year, b.mgnr, b.sortid
|
|
||||||
ORDER BY year, mgnr, bucket;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE VIEW v_payment_bucket_strict AS
|
|
||||||
SELECT d.year, d.mgnr,
|
|
||||||
d.sortid || b.discr AS bucket,
|
|
||||||
d.sortid, IIF(b.discr IN ('', '_'), NULL, b.discr) AS attrid,
|
|
||||||
SUM(b.value) AS weight,
|
|
||||||
b.discr != '_' AS gebunden
|
|
||||||
FROM v_delivery d
|
|
||||||
LEFT JOIN delivery_part_bucket b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr)
|
|
||||||
GROUP BY d.year, d.mgnr, bucket
|
|
||||||
HAVING SUM(b.value) > 0
|
|
||||||
ORDER BY d.year, d.mgnr, bucket;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE VIEW v_payment_bucket AS
|
|
||||||
SELECT year, mgnr, bucket, weight
|
|
||||||
FROM v_payment_bucket_strict
|
|
||||||
WHERE attrid IS NOT NULL OR NOT gebunden
|
|
||||||
UNION ALL
|
|
||||||
SELECT b.year, b.mgnr, b.sortid,
|
|
||||||
SUM(b.weight) AS weight
|
|
||||||
FROM v_payment_bucket_strict b
|
|
||||||
LEFT JOIN wine_attribute a ON a.attrid = b.attrid
|
|
||||||
WHERE gebunden AND (a.strict IS NULL OR a.strict = FALSE)
|
|
||||||
GROUP BY b.year, b.mgnr, b.sortid
|
|
||||||
ORDER BY year, mgnr, bucket;
|
|
||||||
""");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void UpdateDBSchema_6_To_7(SqliteConnection cnx) {
|
|
||||||
ExecuteNonQuery(cnx, "DROP VIEW v_area_commitment_bucket_strict");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE VIEW v_area_commitment_bucket_strict AS
|
|
||||||
SELECT s.year, c.mgnr,
|
|
||||||
t.sortid || COALESCE(a.attrid, '') AS bucket,
|
|
||||||
t.sortid, a.attrid,
|
|
||||||
SUM(area) AS area,
|
|
||||||
CAST(ROUND(SUM(area) * COALESCE(t.min_kg_per_ha, 0) / 10000.0, 0) AS INTEGER) AS min_kg,
|
|
||||||
CAST(ROUND(SUM(area) * MIN(COALESCE(a.max_kg_per_ha, s.max_kg_per_ha), s.max_kg_per_ha) / 10000.0, 0) AS INTEGER) AS max_kg,
|
|
||||||
CAST(ROUND(SUM(area) * s.max_kg_per_ha / 10000.0, 0) AS INTEGER) AS upper_max_kg
|
|
||||||
FROM season s, area_commitment c
|
|
||||||
JOIN area_commitment_type t ON t.vtrgid = c.vtrgid
|
|
||||||
LEFT JOIN wine_attribute a ON a.attrid = t.attrid
|
|
||||||
WHERE (year_from IS NULL OR year_from <= s.year) AND
|
|
||||||
(year_to IS NULL OR year_to >= s.year)
|
|
||||||
GROUP BY s.year, c.mgnr, bucket
|
|
||||||
ORDER BY s.year, c.mgnr, bucket;
|
|
||||||
""");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE VIEW v_under_delivery_bucket_strict AS
|
|
||||||
SELECT c.year, c.mgnr, c.bucket, c.min_kg, COALESCE(p.weight, 0) AS weight
|
|
||||||
FROM v_area_commitment_bucket_strict c
|
|
||||||
LEFT JOIN v_payment_bucket_strict p ON (p.year, p.mgnr, p.bucket) = (c.year, c.mgnr, c.bucket)
|
|
||||||
ORDER BY c.year, c.mgnr, c.bucket;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE VIEW v_under_delivery_bucket AS
|
|
||||||
SELECT u.year, u.mgnr, u.bucket, u.min_kg,
|
|
||||||
u.weight + SUM(MAX(COALESCE(p.weight - s.min_kg, 0), 0)) AS weight
|
|
||||||
FROM v_under_delivery_bucket_strict u
|
|
||||||
LEFT JOIN v_payment_bucket_strict p ON (p.year, p.mgnr, p.sortid) = (u.year, u.mgnr, u.bucket) AND p.attrid IS NOT NULL
|
|
||||||
LEFT JOIN wine_attribute a ON a.attrid = p.attrid
|
|
||||||
LEFT JOIN v_area_commitment_bucket_strict s ON (s.year, s.mgnr, s.bucket) = (p.year, p.mgnr, p.bucket)
|
|
||||||
WHERE (p.gebunden IS NULL OR p.gebunden) AND (a.strict IS NULL OR a.strict = FALSE)
|
|
||||||
GROUP BY u.year, u.mgnr, u.bucket
|
|
||||||
ORDER BY u.year, u.mgnr, u.bucket;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE VIEW v_under_delivery AS
|
|
||||||
SELECT year, mgnr, bucket, min_kg, weight, weight - min_kg AS diff
|
|
||||||
FROM v_under_delivery_bucket
|
|
||||||
WHERE diff < 0;
|
|
||||||
""");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void UpdateDbSchema_7_To_8(SqliteConnection cnx) {
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
INSERT INTO wb_gem
|
|
||||||
SELECT a.gkz, 'WLNO'
|
|
||||||
FROM AT_gem a
|
|
||||||
LEFT JOIN wb_gem w ON w.gkz = a.gkz
|
|
||||||
WHERE a.gkz / 10000 = 3 AND w.hkid IS NULL;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
INSERT INTO wb_gem
|
|
||||||
SELECT gkz, 'SLVL'
|
|
||||||
FROM AT_gem
|
|
||||||
WHERE gkz / 100 IN (617, 622, 623);
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
INSERT INTO wb_gem
|
|
||||||
SELECT gkz, 'SLSS'
|
|
||||||
FROM AT_gem
|
|
||||||
WHERE gkz / 100 = 610;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
UPDATE wb_gem
|
|
||||||
SET hkid = 'SLVL'
|
|
||||||
WHERE gkz IN (61007, 61052, 61001, 61055, 61027, 61057, 61008, 61057);
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
INSERT INTO wb_gem
|
|
||||||
SELECT gkz, 'SLWS'
|
|
||||||
FROM AT_gem
|
|
||||||
WHERE gkz / 100 IN (603, 616) OR gkz IN (60101, 60663, 60651, 60659, 60664, 60647, 60641, 60639, 60665, 60669, 60618, 60629, 60608, 60670, 60624, 60660, 60656, 60655);
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
INSERT INTO wb_gem
|
|
||||||
SELECT g.gkz, 'SLVL'
|
|
||||||
FROM AT_gem g
|
|
||||||
LEFT JOIN wb_gem w ON w.gkz = g.gkz
|
|
||||||
WHERE g.gkz / 100 = 606 AND w.hkid IS NULL;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
INSERT INTO wb_gem
|
|
||||||
SELECT g.gkz, 'SLST'
|
|
||||||
FROM AT_gem g
|
|
||||||
LEFT JOIN wb_gem w ON w.gkz = g.gkz
|
|
||||||
WHERE g.gkz / 10000 = 6 AND w.hkid IS NULL;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
INSERT INTO wb_gem
|
|
||||||
SELECT gkz, 'BLOO'
|
|
||||||
FROM AT_gem
|
|
||||||
WHERE gkz / 10000 = 4;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
INSERT INTO wb_gem
|
|
||||||
SELECT gkz, 'BLKA'
|
|
||||||
FROM AT_gem
|
|
||||||
WHERE gkz / 10000 = 2;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
INSERT INTO wb_gem
|
|
||||||
SELECT gkz, 'BLSB'
|
|
||||||
FROM AT_gem
|
|
||||||
WHERE gkz / 10000 = 5;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
INSERT INTO wb_gem
|
|
||||||
SELECT gkz, 'BLTI'
|
|
||||||
FROM AT_gem
|
|
||||||
WHERE gkz / 10000 = 7;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
INSERT INTO wb_gem
|
|
||||||
SELECT gkz, 'BLVO'
|
|
||||||
FROM AT_gem
|
|
||||||
WHERE gkz / 10000 = 8;
|
|
||||||
""");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void UpdateDbSchema_8_To_9(SqliteConnection cnx) {
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE TABLE payment_delivery_part_bucket (
|
|
||||||
year INTEGER NOT NULL,
|
|
||||||
did INTEGER NOT NULL,
|
|
||||||
dpnr INTEGER NOT NULL,
|
|
||||||
bktnr INTEGER NOT NULL,
|
|
||||||
avnr INTEGER NOT NULL,
|
|
||||||
|
|
||||||
price INTEGER NOT NULL,
|
|
||||||
amount INTEGER NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT pk_payment_delivery_part_bucket PRIMARY KEY (year, did, dpnr, bktnr, avnr),
|
|
||||||
CONSTRAINT fk_payment_delivery_part_bucket_delivery_part_bucket FOREIGN KEY (year, did, dpnr, bktnr) REFERENCES delivery_part_bucket (year, did, dpnr, bktnr)
|
|
||||||
ON UPDATE CASCADE
|
|
||||||
ON DELETE CASCADE,
|
|
||||||
CONSTRAINT fk_payment_delivery_part_bucket_payment_variant FOREIGN KEY (year, avnr) REFERENCES payment_variant (year, avnr)
|
|
||||||
ON UPDATE CASCADE
|
|
||||||
ON DELETE CASCADE
|
|
||||||
) STRICT;
|
|
||||||
""");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, "DROP TRIGGER IF EXISTS t_payment_delivery_part_i");
|
|
||||||
ExecuteNonQuery(cnx, "DROP TRIGGER IF EXISTS t_payment_delivery_part_u");
|
|
||||||
ExecuteNonQuery(cnx, "DROP TRIGGER IF EXISTS t_payment_delivery_part_d");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part RENAME COLUMN amount TO net_amount");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part ADD COLUMN amount INTEGER NOT NULL GENERATED ALWAYS AS (ROUND(net_amount * (1 + mod_rel) + mod_abs)) VIRTUAL");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE TRIGGER t_payment_delivery_part_bucket_i
|
|
||||||
AFTER INSERT ON payment_delivery_part_bucket FOR EACH ROW
|
|
||||||
BEGIN
|
|
||||||
INSERT INTO payment_delivery_part (year, did, dpnr, avnr, net_amount)
|
|
||||||
VALUES (NEW.year, NEW.did, NEW.dpnr, NEW.avnr, NEW.amount)
|
|
||||||
ON CONFLICT DO UPDATE SET net_amount = net_amount + NEW.amount;
|
|
||||||
END;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE TRIGGER t_payment_delivery_part_bucket_u
|
|
||||||
AFTER UPDATE OF amount ON payment_delivery_part_bucket FOR EACH ROW
|
|
||||||
BEGIN
|
|
||||||
UPDATE payment_delivery_part
|
|
||||||
SET net_amount = net_amount - OLD.amount
|
|
||||||
WHERE (year, did, dpnr, avnr) = (NEW.year, NEW.did, NEW.dpnr, NEW.avnr);
|
|
||||||
UPDATE payment_delivery_part
|
|
||||||
SET net_amount = net_amount + NEW.amount
|
|
||||||
WHERE (year, did, dpnr, avnr) = (NEW.year, NEW.did, NEW.dpnr, NEW.avnr);
|
|
||||||
END;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE TRIGGER t_payment_delivery_part_bucket_d
|
|
||||||
AFTER DELETE ON payment_delivery_part_bucket FOR EACH ROW
|
|
||||||
BEGIN
|
|
||||||
UPDATE payment_delivery_part
|
|
||||||
SET net_amount = net_amount - OLD.amount
|
|
||||||
WHERE (year, did, dpnr, avnr) = (OLD.year, OLD.did, OLD.dpnr, OLD.avnr);
|
|
||||||
END;
|
|
||||||
""");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_member RENAME COLUMN amount TO net_amount");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_member ADD COLUMN mod_abs INTEGER NOT NULL DEFAULT 0");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_member ADD COLUMN mod_rel REAL NOT NULL DEFAULT 0");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_member ADD COLUMN amount INTEGER NOT NULL GENERATED ALWAYS AS (ROUND(net_amount * (1 + mod_rel) + mod_rel)) VIRTUAL");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE TRIGGER t_payment_delivery_part_i
|
|
||||||
AFTER INSERT ON payment_delivery_part FOR EACH ROW
|
|
||||||
BEGIN
|
|
||||||
INSERT INTO payment_member (year, avnr, mgnr, net_amount)
|
|
||||||
VALUES (NEW.year, NEW.avnr, (SELECT mgnr FROM delivery WHERE (year, did) = (NEW.year, NEW.did)), NEW.amount)
|
|
||||||
ON CONFLICT DO UPDATE SET net_amount = net_amount + excluded.net_amount;
|
|
||||||
END;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE TRIGGER t_payment_delivery_part_u
|
|
||||||
AFTER UPDATE OF amount ON payment_delivery_part FOR EACH ROW
|
|
||||||
BEGIN
|
|
||||||
UPDATE payment_member
|
|
||||||
SET net_amount = net_amount - OLD.amount
|
|
||||||
WHERE (year, avnr, mgnr) = (NEW.year, NEW.avnr, (SELECT mgnr FROM delivery WHERE (year, did) = (NEW.year, NEW.did)));
|
|
||||||
UPDATE payment_member
|
|
||||||
SET net_amount = net_amount + NEW.amount
|
|
||||||
WHERE (year, avnr, mgnr) = (NEW.year, NEW.avnr, (SELECT mgnr FROM delivery WHERE (year, did) = (NEW.year, NEW.did)));
|
|
||||||
END;
|
|
||||||
""");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE TRIGGER t_payment_delivery_part_d
|
|
||||||
AFTER DELETE ON payment_delivery_part FOR EACH ROW
|
|
||||||
BEGIN
|
|
||||||
UPDATE payment_member
|
|
||||||
SET net_amount = net_amount - OLD.amount
|
|
||||||
WHERE (year, avnr, mgnr) = (OLD.year, OLD.avnr, (SELECT mgnr FROM delivery WHERE (year, did) = (OLD.year, OLD.did)));
|
|
||||||
END;
|
|
||||||
""");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void UpdateDbSchema_9_To_10(SqliteConnection cnx) {
|
|
||||||
ExecuteNonQuery(cnx, "UPDATE wine_quality_level SET min_kmw = 10.6 WHERE qualid = 'RSW'");
|
|
||||||
ExecuteNonQuery(cnx, "DROP VIEW v_area_commitment_bucket");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE VIEW v_area_commitment_bucket AS
|
|
||||||
SELECT year, mgnr, bucket, area, min_kg, max_kg
|
|
||||||
FROM v_area_commitment_bucket_strict
|
|
||||||
WHERE attrid IS NOT NULL
|
|
||||||
UNION ALL
|
|
||||||
SELECT b.year, b.mgnr, b.sortid,
|
|
||||||
SUM(b.area) AS area,
|
|
||||||
SUM(b.min_kg) AS min_kg,
|
|
||||||
SUM(b.upper_max_kg) AS max_kg
|
|
||||||
FROM v_area_commitment_bucket_strict b
|
|
||||||
LEFT JOIN wine_attribute a ON a.attrid = b.attrid
|
|
||||||
WHERE a.strict IS NULL OR a.strict = FALSE
|
|
||||||
GROUP BY b.year, b.mgnr, b.sortid
|
|
||||||
ORDER BY year, mgnr, bucket;
|
|
||||||
""");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void UpdateDbSchema_10_To_11(SqliteConnection cnx) {
|
|
||||||
// Drop columns, if they exist...
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_1");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_2");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_3");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_4");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_5");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_6");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_7");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_8");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_delivery_part DROP COLUMN price_9");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, "DROP TRIGGER t_payment_delivery_part_i");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE TRIGGER t_payment_delivery_part_i
|
|
||||||
AFTER INSERT ON payment_delivery_part FOR EACH ROW
|
|
||||||
BEGIN
|
|
||||||
INSERT INTO payment_member (year, avnr, mgnr, net_amount)
|
|
||||||
SELECT year, NEW.avnr, mgnr, NEW.amount FROM delivery WHERE (year, did) = (NEW.year, NEW.did)
|
|
||||||
ON CONFLICT DO UPDATE SET net_amount = net_amount + excluded.net_amount;
|
|
||||||
END;
|
|
||||||
""");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, "DROP TRIGGER t_payment_delivery_part_u");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE TRIGGER t_payment_delivery_part_u
|
|
||||||
AFTER UPDATE ON payment_delivery_part FOR EACH ROW
|
|
||||||
BEGIN
|
|
||||||
UPDATE payment_member
|
|
||||||
SET net_amount = net_amount - OLD.amount
|
|
||||||
WHERE (year, avnr, mgnr) IN (SELECT year, OLD.avnr, mgnr FROM delivery WHERE (year, did) = (OLD.year, OLD.did));
|
|
||||||
INSERT INTO payment_member (year, avnr, mgnr, net_amount)
|
|
||||||
SELECT year, NEW.avnr, mgnr, NEW.amount FROM delivery WHERE (year, did) = (NEW.year, NEW.did)
|
|
||||||
ON CONFLICT DO UPDATE SET net_amount = net_amount + excluded.net_amount;
|
|
||||||
END;
|
|
||||||
""");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, "DROP TRIGGER t_payment_delivery_part_d");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE TRIGGER t_payment_delivery_part_d
|
|
||||||
AFTER DELETE ON payment_delivery_part FOR EACH ROW
|
|
||||||
BEGIN
|
|
||||||
UPDATE payment_member
|
|
||||||
SET net_amount = net_amount - OLD.amount
|
|
||||||
WHERE (year, avnr, mgnr) IN (SELECT year, OLD.avnr, mgnr FROM delivery WHERE (year, did) = (OLD.year, OLD.did));
|
|
||||||
END;
|
|
||||||
""");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, "DROP TRIGGER t_payment_delivery_part_bucket_u");
|
|
||||||
ExecuteNonQuery(cnx, """
|
|
||||||
CREATE TRIGGER t_payment_delivery_part_bucket_u
|
|
||||||
AFTER UPDATE ON payment_delivery_part_bucket FOR EACH ROW
|
|
||||||
BEGIN
|
|
||||||
UPDATE payment_delivery_part
|
|
||||||
SET net_amount = net_amount - OLD.amount
|
|
||||||
WHERE (year, did, dpnr, avnr) = (OLD.year, OLD.did, OLD.dpnr, OLD.avnr);
|
|
||||||
UPDATE payment_delivery_part
|
|
||||||
SET net_amount = net_amount + NEW.amount
|
|
||||||
WHERE (year, did, dpnr, avnr) = (NEW.year, NEW.did, NEW.dpnr, NEW.avnr);
|
|
||||||
END;
|
|
||||||
""");
|
|
||||||
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_member DROP COLUMN amount");
|
|
||||||
ExecuteNonQuery(cnx, "ALTER TABLE payment_member ADD COLUMN amount INTEGER NOT NULL GENERATED ALWAYS AS (ROUND(net_amount * (1 + mod_rel) + mod_abs)) VIRTUAL");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,19 +22,25 @@ namespace Elwig.Helpers.Billing {
|
|||||||
|
|
||||||
public async Task FinishSeason() {
|
public async Task FinishSeason() {
|
||||||
using var cnx = await AppDbContext.ConnectAsync();
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
using (var cmd = cnx.CreateCommand()) {
|
await AppDbContext.ExecuteBatch(cnx, $"""
|
||||||
cmd.CommandText = $"""
|
UPDATE season
|
||||||
UPDATE season
|
SET (start_date, end_date) = (SELECT MIN(date), MAX(date) FROM delivery WHERE year = {Year})
|
||||||
SET (start_date, end_date) = (SELECT MIN(date), MAX(date) FROM delivery WHERE year = {Year})
|
WHERE year = {Year};
|
||||||
WHERE year = {Year}
|
|
||||||
""";
|
|
||||||
await cmd.ExecuteNonQueryAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
using (var cmd = cnx.CreateCommand()) {
|
DELETE FROM delivery_part_bucket WHERE year = {Year};
|
||||||
cmd.CommandText = $"DELETE FROM delivery_part_bucket WHERE year = {Year}";
|
""");
|
||||||
await cmd.ExecuteNonQueryAsync();
|
}
|
||||||
}
|
|
||||||
|
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) {
|
||||||
@@ -108,15 +114,12 @@ namespace Elwig.Helpers.Billing {
|
|||||||
lastMgNr = mgnr;
|
lastMgNr = mgnr;
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var cmd = cnx.CreateCommand()) {
|
await AppDbContext.ExecuteBatch(cnx, $"""
|
||||||
cmd.CommandText = $"""
|
INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value)
|
||||||
INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value)
|
VALUES {string.Join(",\n ", inserts.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '{i.Item4}', {i.Item5})"))}
|
||||||
VALUES {string.Join(",\n ", inserts.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '{i.Item4}', {i.Item5})"))}
|
ON CONFLICT DO UPDATE
|
||||||
ON CONFLICT DO UPDATE
|
SET discr = excluded.discr, value = value + excluded.value;
|
||||||
SET discr = excluded.discr, value = value + excluded.value
|
""");
|
||||||
""";
|
|
||||||
await cmd.ExecuteNonQueryAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!avoidUnderDeliveries)
|
if (!avoidUnderDeliveries)
|
||||||
return;
|
return;
|
||||||
@@ -186,25 +189,17 @@ namespace Elwig.Helpers.Billing {
|
|||||||
if (needed == 0) break;
|
if (needed == 0) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var cmd = cnx.CreateCommand()) {
|
await AppDbContext.ExecuteBatch(cnx, $"""
|
||||||
cmd.CommandText = $"""
|
INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value)
|
||||||
INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value)
|
VALUES {string.Join(",\n ", posChanges.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '', {i.Item4})"))}
|
||||||
VALUES {string.Join(",\n ", posChanges.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '', {i.Item4})"))}
|
ON CONFLICT DO UPDATE
|
||||||
ON CONFLICT DO UPDATE
|
SET value = value + excluded.value;
|
||||||
SET value = value + excluded.value
|
|
||||||
""";
|
|
||||||
await cmd.ExecuteNonQueryAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
using (var cmd = cnx.CreateCommand()) {
|
INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value)
|
||||||
cmd.CommandText = $"""
|
VALUES {string.Join(",\n ", negChanges.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '', {i.Item4})"))}
|
||||||
INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value)
|
ON CONFLICT DO UPDATE
|
||||||
VALUES {string.Join(",\n ", negChanges.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '', {i.Item4})"))}
|
SET value = excluded.value;
|
||||||
ON CONFLICT DO UPDATE
|
""");
|
||||||
SET value = excluded.value
|
|
||||||
""";
|
|
||||||
await cmd.ExecuteNonQueryAsync();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,149 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using NJsonSchema;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Elwig.Helpers.Billing {
|
||||||
|
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 async Task Init() {
|
||||||
|
var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Elwig.Resources.Schemas.PaymentVariantData.json");
|
||||||
|
Schema = await JsonSchema.FromJsonAsync(stream ?? throw new ArgumentException("JSON schema not found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
Data = data;
|
||||||
|
var mode = Data["mode"]?.GetValue<string>();
|
||||||
|
Mode = (mode == "elwig") ? CalculationMode.Elwig : CalculationMode.WgMaster;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static JsonObject ParseJson(string json) {
|
||||||
|
if (Schema == null) throw new InvalidOperationException("Schema has to be initialized first");
|
||||||
|
try {
|
||||||
|
var errors = Schema.Validate(json);
|
||||||
|
if (errors.Count != 0) throw new ArgumentException("Invalid JSON data");
|
||||||
|
return JsonNode.Parse(json)?.AsObject() ?? throw new ArgumentException("Invalid JSON data");
|
||||||
|
} catch (JsonReaderException) {
|
||||||
|
throw new ArgumentException("Invalid JSON data");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BillingData FromJson(string json) {
|
||||||
|
return new(ParseJson(json));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected JsonArray GetCurvesEntry() {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,41 +8,148 @@ 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 CalculatePrices();
|
using var tx = await cnx.BeginTransactionAsync();
|
||||||
await CalculateModifiers();
|
await DeleteInDb(cnx);
|
||||||
|
await SetCalcTime(cnx);
|
||||||
|
await CalculatePrices(cnx);
|
||||||
|
if (Data.ConsiderDelieryModifiers)
|
||||||
|
await CalculateDeliveryModifiers(cnx);
|
||||||
|
await CalculateMemberModifiers(cnx);
|
||||||
|
await tx.CommitAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task DeleteInDb() {
|
public async Task Commit() {
|
||||||
|
await Revert();
|
||||||
using var cnx = await AppDbContext.ConnectAsync();
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
using (var cmd = cnx.CreateCommand()) {
|
await AppDbContext.ExecuteBatch(cnx, $"""
|
||||||
cmd.CommandText = $"DELETE FROM payment_delivery_part_bucket WHERE (year, avnr) = ({Year}, {AvNr})";
|
INSERT INTO credit (year, tgnr, mgnr, avnr, net_amount, prev_net_amount, vat, modifiers, prev_modifiers)
|
||||||
await cmd.ExecuteNonQueryAsync();
|
SELECT s.year,
|
||||||
}
|
COALESCE(t.tgnr, 0) + ROW_NUMBER() OVER(ORDER BY m.mgnr) AS tgnr,
|
||||||
using (var cmd = cnx.CreateCommand()) {
|
m.mgnr,
|
||||||
cmd.CommandText = $"DELETE FROM payment_delivery_part WHERE (year, avnr) = ({Year}, {AvNr})";
|
v.avnr,
|
||||||
await cmd.ExecuteNonQueryAsync();
|
ROUND(p.amount / POW(10, s.precision - 2)) AS net_amount,
|
||||||
}
|
ROUND(lp.amount / POW(10, s.precision - 2)) AS prev_amount,
|
||||||
using (var cmd = cnx.CreateCommand()) {
|
IIF(m.buchführend, s.vat_normal, s.vat_flatrate) AS vat,
|
||||||
cmd.CommandText = $"DELETE FROM payment_member WHERE (year, avnr) = ({Year}, {AvNr})";
|
ROUND(
|
||||||
await cmd.ExecuteNonQueryAsync();
|
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
|
||||||
|
FROM season s
|
||||||
|
JOIN payment_variant v ON v.year = s.year
|
||||||
|
LEFT JOIN payment_variant l ON l.year = s.year
|
||||||
|
AND l.avnr = (SELECT avnr
|
||||||
|
FROM payment_variant
|
||||||
|
WHERE year = s.year AND NOT test_variant
|
||||||
|
ORDER BY COALESCE(transfer_date, date) DESC, avnr DESC
|
||||||
|
LIMIT 1)
|
||||||
|
LEFT JOIN (SELECT year, MAX(tgnr) AS tgnr FROM credit GROUP BY year) t ON t.year = s.year
|
||||||
|
JOIN (SELECT DISTINCT year, mgnr FROM delivery) d ON d.year = s.year
|
||||||
|
JOIN member m ON m.mgnr = d.mgnr
|
||||||
|
LEFT JOIN payment_member lp ON (lp.year, lp.avnr, lp.mgnr) = (l.year, l.avnr, m.mgnr)
|
||||||
|
LEFT JOIN payment_member p ON (p.year, p.avnr, p.mgnr) = (v.year, v.avnr, m.mgnr)
|
||||||
|
LEFT JOIN credit lc ON (lc.year, lc.avnr, lc.mgnr) = (l.year, l.avnr, m.mgnr)
|
||||||
|
LEFT JOIN (SELECT year, mgnr,
|
||||||
|
SUM(COALESCE(IIF(u.weight = 0, -t.penalty_none, 0), 0) +
|
||||||
|
COALESCE(IIF(u.diff < 0, -t.penalty_amount, 0), 0) +
|
||||||
|
COALESCE(u.diff * t.penalty_per_kg, 0)) AS total_penalty
|
||||||
|
FROM v_under_delivery u
|
||||||
|
JOIN area_commitment_type t ON t.vtrgid = u.bucket
|
||||||
|
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};
|
||||||
|
|
||||||
|
UPDATE payment_variant SET test_variant = FALSE WHERE (year, avnr) = ({Year}, {AvNr});
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Revert() {
|
||||||
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
|
await AppDbContext.ExecuteBatch(cnx, $"""
|
||||||
|
DELETE FROM credit WHERE (year, avnr) = ({Year}, {AvNr});
|
||||||
|
UPDATE payment_variant SET test_variant = TRUE WHERE (year, avnr) = ({Year}, {AvNr});
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task SetCalcTime(SqliteConnection cnx) {
|
||||||
|
await AppDbContext.ExecuteBatch(cnx, $"""
|
||||||
|
UPDATE payment_variant SET calc_time = UNIXEPOCH() WHERE (year, avnr) = ({Year}, {AvNr})
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task DeleteInDb(SqliteConnection cnx) {
|
||||||
|
await AppDbContext.ExecuteBatch(cnx, $"""
|
||||||
|
DELETE FROM payment_delivery_part_bucket WHERE (year, avnr) = ({Year}, {AvNr});
|
||||||
|
DELETE FROM payment_delivery_part WHERE (year, avnr) = ({Year}, {AvNr});
|
||||||
|
DELETE FROM payment_member WHERE (year, avnr) = ({Year}, {AvNr});
|
||||||
|
UPDATE payment_variant SET calc_time = NULL WHERE (year, avnr) = ({Year}, {AvNr});
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async Task CalculateMemberModifiers(SqliteConnection cnx) {
|
||||||
|
if (App.Client.IsMatzen) {
|
||||||
|
var lastYears = 3;
|
||||||
|
var multiplier = 0.50;
|
||||||
|
var modName = "Treue%";
|
||||||
|
await AppDbContext.ExecuteBatch(cnx, $"""
|
||||||
|
INSERT INTO payment_member (year, avnr, mgnr, net_amount, mod_abs, mod_rel)
|
||||||
|
SELECT c.year, {AvNr}, s.mgnr, 0,
|
||||||
|
ROUND(s.sum * COALESCE(m.abs, 0)),
|
||||||
|
COALESCE(m.rel, 0)
|
||||||
|
FROM (SELECT {Year} AS year, mgnr,
|
||||||
|
ROUND(AVG(sum) * {multiplier}) AS baseline,
|
||||||
|
COUNT(*) = {lastYears} AND MIN(sum) > 0 AS allowed
|
||||||
|
FROM v_stat_member
|
||||||
|
WHERE year > {Year} - {lastYears}
|
||||||
|
GROUP BY mgnr
|
||||||
|
HAVING allowed) c
|
||||||
|
JOIN v_stat_member s ON (s.year, s.mgnr) = (c.year, c.mgnr)
|
||||||
|
LEFT JOIN modifier m ON m.year = c.year AND m.name LIKE '{modName}'
|
||||||
|
WHERE sum >= baseline
|
||||||
|
ON CONFLICT DO UPDATE
|
||||||
|
SET mod_abs = mod_abs + excluded.mod_abs,
|
||||||
|
mod_rel = mod_rel + excluded.mod_rel
|
||||||
|
""");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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}
|
||||||
@@ -49,41 +159,37 @@ 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;
|
||||||
var priceL = Utils.DecToDb(price, 4);
|
var price = Data.CalculatePrice(part.SortId, attrId, part.QualId, part.Discr != "_", part.Oe, part.Kmw);
|
||||||
var amount = Utils.DecToDb(price * part.Value, 4);
|
var priceL = PaymentVariant.Season.DecToDb(price);
|
||||||
inserts.Add((part.Year, part.DId, part.DPNr, part.BktNr, priceL, amount));
|
inserts.Add((part.Year, part.DId, part.DPNr, part.BktNr, priceL, priceL * part.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var cmd = cnx.CreateCommand()) {
|
await AppDbContext.ExecuteBatch(cnx, $"""
|
||||||
cmd.CommandText = $"""
|
INSERT INTO payment_delivery_part_bucket (year, did, dpnr, bktnr, avnr, price, amount)
|
||||||
INSERT INTO payment_delivery_part_bucket (year, did, dpnr, bktnr, avnr, price, amount)
|
VALUES {string.Join(",\n ", inserts.Select(i => $"({i.Year}, {i.DId}, {i.DPNr}, {i.BktNr}, {AvNr}, {i.Price}, {i.Amount})"))};
|
||||||
VALUES {string.Join(",\n ", inserts.Select(i => $"({i.Year}, {i.DId}, {i.DPNr}, {i.BktNr}, {AvNr}, {i.Price}, {i.Amount})"))}
|
""");
|
||||||
""";
|
|
||||||
await cmd.ExecuteNonQueryAsync();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task CalculateModifiers() {
|
protected async Task CalculateDeliveryModifiers(SqliteConnection cnx) {
|
||||||
using var cnx = await AppDbContext.ConnectAsync();
|
await AppDbContext.ExecuteBatch(cnx, $"""
|
||||||
using var cmd = cnx.CreateCommand();
|
|
||||||
cmd.CommandText = $"""
|
|
||||||
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 SET mod_abs = mod_abs + excluded.mod_abs, mod_rel = mod_rel + excluded.mod_rel;
|
ON CONFLICT DO UPDATE
|
||||||
""";
|
SET mod_abs = mod_abs + excluded.mod_abs,
|
||||||
await cmd.ExecuteNonQueryAsync();
|
mod_rel = mod_rel + excluded.mod_rel
|
||||||
|
""");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
using Elwig.Models.Entities;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Elwig.Helpers.Billing {
|
|
||||||
public class Transaction {
|
|
||||||
|
|
||||||
public readonly Member Member;
|
|
||||||
public readonly long AmountCent;
|
|
||||||
public readonly string Currency;
|
|
||||||
public readonly int Nr;
|
|
||||||
|
|
||||||
public Transaction(Member m, decimal amount, string currency, int nr) {
|
|
||||||
Member = m;
|
|
||||||
AmountCent = (long)Math.Round(amount * 100);
|
|
||||||
Currency = currency;
|
|
||||||
Nr = nr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<Transaction> FromPaymentVariant(PaymentVar variant) {
|
|
||||||
var last = variant.Season.PaymentVariants.Where(v => v.TransferDate != null).OrderBy(v => v.TransferDate).LastOrDefault();
|
|
||||||
var dict = last?.MemberPayments.ToDictionary(m => m.MgNr, m => m.Amount) ?? new();
|
|
||||||
return variant.Credits
|
|
||||||
.OrderBy(c => c.MgNr)
|
|
||||||
.Select(c => new Transaction(c.Member, c.Amount, variant.Season.CurrencyCode, c.TgNr))
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string FormatAmountCent(long cents) => $"{cents / 100}.{cents % 100:00}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Elwig.Helpers.Billing;
|
using Elwig.Models.Dtos;
|
||||||
using Elwig.Models.Entities;
|
using Elwig.Models.Entities;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -7,32 +7,24 @@ using System.Linq;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Elwig.Helpers.Export {
|
namespace Elwig.Helpers.Export {
|
||||||
public class Ebics : IBankingExporter {
|
public class Ebics(PaymentVar variant, string filename) : IBankingExporter {
|
||||||
|
|
||||||
public static string FileExtension => "xml";
|
public static string FileExtension => "xml";
|
||||||
|
|
||||||
private readonly StreamWriter _writer;
|
private readonly StreamWriter Writer = new(filename, false, Utils.UTF8);
|
||||||
private readonly DateOnly _date;
|
private readonly DateOnly Date = variant.TransferDate ?? throw new ArgumentException("TransferDate has to be set in PaymentVar");
|
||||||
private readonly int _year;
|
private readonly int Year = variant.Year;
|
||||||
private readonly string _name;
|
private readonly string Name = variant.Name;
|
||||||
private readonly int _nr;
|
private readonly int AvNr = variant.AvNr;
|
||||||
|
|
||||||
public Ebics(PaymentVar variant, string filename) {
|
|
||||||
_writer = new(filename, false, Utils.UTF8);
|
|
||||||
_date = variant.TransferDate ?? DateOnly.Parse("2021-01-10"); //throw new ArgumentException("TransferDate has to be set in PaymentVar");
|
|
||||||
_year = variant.Year;
|
|
||||||
_name = variant.Name;
|
|
||||||
_nr = variant.AvNr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
_writer.Dispose();
|
Writer.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueTask DisposeAsync() {
|
public ValueTask DisposeAsync() {
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
return _writer.DisposeAsync();
|
return Writer.DisposeAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Export(IEnumerable<Transaction> transactions, IProgress<double>? progress = null) {
|
public void Export(IEnumerable<Transaction> transactions, IProgress<double>? progress = null) {
|
||||||
@@ -43,66 +35,66 @@ namespace Elwig.Helpers.Export {
|
|||||||
progress?.Report(0.0);
|
progress?.Report(0.0);
|
||||||
var nbOfTxs = transactions.Count();
|
var nbOfTxs = transactions.Count();
|
||||||
int count = nbOfTxs + 2, i = 0;
|
int count = nbOfTxs + 2, i = 0;
|
||||||
var ctrlSum = Transaction.FormatAmountCent(transactions.Sum(tx => tx.AmountCent));
|
var ctrlSum = transactions.Sum(tx => tx.Amount);
|
||||||
var msgId = $"ELWIG-{App.Client.NameToken}-{_year}-AV{_nr:00}";
|
var msgId = $"ELWIG-{App.Client.NameToken}-{Year}-AV{AvNr:00}";
|
||||||
var pmtInfId = $"{msgId}-1";
|
var pmtInfId = $"{msgId}-1";
|
||||||
|
|
||||||
await _writer.WriteLineAsync($"""
|
await Writer.WriteLineAsync($"""
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09"
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09 pain.001.001.09.xsd">
|
xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09 pain.001.001.09.xsd">
|
||||||
<CstmrCdtTrfInitn>
|
<CstmrCdtTrfInitn>
|
||||||
<GrpHdr>
|
<GrpHdr>
|
||||||
<MsgId>{msgId}</MsgId>
|
<MsgId>{msgId}</MsgId>
|
||||||
<CreDtTm>{DateTime.UtcNow:o}</CreDtTm>
|
<CreDtTm>{DateTime.UtcNow:o}</CreDtTm>
|
||||||
<NbOfTxs>{nbOfTxs}</NbOfTxs>
|
<NbOfTxs>{nbOfTxs}</NbOfTxs>
|
||||||
<CtrlSum>{ctrlSum}</CtrlSum>
|
<CtrlSum>{Transaction.FormatAmount(ctrlSum)}</CtrlSum>
|
||||||
<InitgPty><Nm>{App.Client.NameFull}</Nm></InitgPty>
|
<InitgPty><Nm>{App.Client.NameFull}</Nm></InitgPty>
|
||||||
</GrpHdr>
|
</GrpHdr>
|
||||||
<PmtInf>
|
<PmtInf>
|
||||||
<PmtInfId>{pmtInfId}</PmtInfId>
|
<PmtInfId>{pmtInfId}</PmtInfId>
|
||||||
<PmtMtd>TRF</PmtMtd>
|
<PmtMtd>TRF</PmtMtd>
|
||||||
<NbOfTxs>{nbOfTxs}</NbOfTxs>
|
<NbOfTxs>{nbOfTxs}</NbOfTxs>
|
||||||
<CtrlSum>{ctrlSum}</CtrlSum>
|
<CtrlSum>{Transaction.FormatAmount(ctrlSum)}</CtrlSum>
|
||||||
<ReqdExctnDt><Dt>{_date:yyyy-MM-dd}</Dt></ReqdExctnDt>
|
<ReqdExctnDt><Dt>{Date:yyyy-MM-dd}</Dt></ReqdExctnDt>
|
||||||
<Dbtr><Nm>{App.Client.NameFull}</Nm></Dbtr>
|
<Dbtr><Nm>{App.Client.NameFull}</Nm></Dbtr>
|
||||||
<DbtrAcct><Id><IBAN>{App.Client.Iban?.Replace(" ", "")}</IBAN></Id></DbtrAcct>
|
<DbtrAcct><Id><IBAN>{App.Client.Iban?.Replace(" ", "")}</IBAN></Id></DbtrAcct>
|
||||||
<DbtrAgt><FinInstnId><BICFI>{App.Client.Bic ?? "NOTPROVIDED"}</BICFI></FinInstnId></DbtrAgt>
|
<DbtrAgt><FinInstnId><BICFI>{App.Client.Bic ?? "NOTPROVIDED"}</BICFI></FinInstnId></DbtrAgt>
|
||||||
""");
|
""");
|
||||||
progress?.Report(100.0 * ++i / count);
|
progress?.Report(100.0 * ++i / count);
|
||||||
|
|
||||||
foreach (var tx in transactions) {
|
foreach (var tx in transactions) {
|
||||||
var a = (IAddress?)tx.Member.BillingAddress ?? tx.Member;
|
var a = (IAddress?)tx.Member.BillingAddress ?? tx.Member;
|
||||||
var (a1, a2) = Utils.SplitAddress(a.Address);
|
var (a1, a2) = Utils.SplitAddress(a.Address);
|
||||||
var id = $"ELWIG-{App.Client.NameToken}-{_year}-TG{tx.Nr:0000}";
|
var id = $"ELWIG-{App.Client.NameToken}-{Year}-TG{tx.Nr:0000}";
|
||||||
var info = $"{_name} - Traubengutschrift {_year}/{tx.Nr:000}";
|
var info = $"{Name} - Traubengutschrift Nr. {Year}/{tx.Nr:000}";
|
||||||
await _writer.WriteLineAsync($"""
|
await Writer.WriteLineAsync($"""
|
||||||
<CdtTrfTxInf>
|
<CdtTrfTxInf>
|
||||||
<PmtId><EndToEndId>{id}</EndToEndId></PmtId>
|
<PmtId><EndToEndId>{id}</EndToEndId></PmtId>
|
||||||
<Amt><InstdAmt Ccy="{tx.Currency}">{Transaction.FormatAmountCent(tx.AmountCent)}</InstdAmt></Amt>
|
<Amt><InstdAmt Ccy="{tx.Currency}">{Transaction.FormatAmount(tx.Amount)}</InstdAmt></Amt>
|
||||||
<Cdtr>
|
<Cdtr>
|
||||||
<Nm>{a.Name}</Nm>
|
<Nm>{a.Name}</Nm>
|
||||||
<PstlAdr>
|
<PstlAdr>
|
||||||
<StrtNm>{a1}</StrtNm><BldgNb>{a2}</BldgNb>
|
<StrtNm>{a1}</StrtNm><BldgNb>{a2}</BldgNb>
|
||||||
<PstCd>{a.PostalDest.AtPlz?.Plz}</PstCd><TwnNm>{a.PostalDest.AtPlz?.Ort.Name}</TwnNm>
|
<PstCd>{a.PostalDest.AtPlz?.Plz}</PstCd><TwnNm>{a.PostalDest.AtPlz?.Ort.Name}</TwnNm>
|
||||||
<Ctry>{a.PostalDest.Country.Alpha2}</Ctry>
|
<Ctry>{a.PostalDest.Country.Alpha2}</Ctry>
|
||||||
</PstlAdr>
|
</PstlAdr>
|
||||||
</Cdtr>
|
</Cdtr>
|
||||||
<CdtrAcct><Id><IBAN>{tx.Member.Iban}</IBAN></Id><CdtrAcct>
|
<CdtrAcct><Id><IBAN>{tx.Member.Iban}</IBAN></Id></CdtrAcct>
|
||||||
<CdtrAgt><FinInstnId><BICFI>{tx.Member.Bic ?? "NOTPROVIDED"}</BICFI></FinInstnId></CdtrAgt>
|
<CdtrAgt><FinInstnId><BICFI>{tx.Member.Bic ?? "NOTPROVIDED"}</BICFI></FinInstnId></CdtrAgt>
|
||||||
<RmtInf><Ustrd>{info}</Ustrd></RmtInf>
|
<RmtInf><Ustrd>{info}</Ustrd></RmtInf>
|
||||||
</CdtTrfTxInf>
|
</CdtTrfTxInf>
|
||||||
""");
|
""");
|
||||||
progress?.Report(100.0 * ++i / count);
|
progress?.Report(100.0 * ++i / count);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _writer.WriteLineAsync("""
|
await Writer.WriteLineAsync("""
|
||||||
</PmtInf>
|
</PmtInf>
|
||||||
</CstmrCdtTrfInitn>
|
</CstmrCdtTrfInitn>
|
||||||
</Document>
|
</Document>
|
||||||
""");
|
""");
|
||||||
await _writer.FlushAsync();
|
await Writer.FlushAsync();
|
||||||
progress?.Report(100.0);
|
progress?.Report(100.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Elwig.Helpers.Billing;
|
using Elwig.Models.Dtos;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Elwig.Helpers.Export {
|
namespace Elwig.Helpers.Export {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Elwig.Helpers.Billing;
|
using Elwig.Models.Dtos;
|
||||||
|
|
||||||
namespace Elwig.Helpers.Export {
|
namespace Elwig.Helpers.Export {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -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}>";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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) {
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ namespace Elwig.Models.Dtos {
|
|||||||
private static async Task<IEnumerable<AreaComUnderDeliveryRowSingle>> FromDbSet(DbSet<AreaComUnderDeliveryRowSingle> table, int year) {
|
private static async Task<IEnumerable<AreaComUnderDeliveryRowSingle>> FromDbSet(DbSet<AreaComUnderDeliveryRowSingle> table, int year) {
|
||||||
return await table.FromSqlRaw($"""
|
return await table.FromSqlRaw($"""
|
||||||
SELECT m.mgnr, m.family_name, m.given_name, p.plz, o.name AS ort, m.address,
|
SELECT m.mgnr, m.family_name, m.given_name, p.plz, o.name AS ort, m.address,
|
||||||
c.bucket, c.area, u.min_kg, u.weight
|
c.bucket, c.area, u.min_kg, u.weight
|
||||||
FROM member m
|
FROM member m
|
||||||
LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
|
LEFT JOIN AT_plz_dest p ON p.id = m.postal_dest
|
||||||
LEFT JOIN AT_ort o ON o.okz = p.okz
|
LEFT JOIN AT_ort o ON o.okz = p.okz
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace Elwig.Models.Dtos {
|
|||||||
LEFT JOIN delivery_part_bucket b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr)
|
LEFT JOIN delivery_part_bucket b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr)
|
||||||
LEFT JOIN payment_variant v ON v.year = d.year
|
LEFT JOIN payment_variant v ON v.year = d.year
|
||||||
LEFT JOIN payment_delivery_part p ON (p.year, p.did, p.dpnr, p.avnr) = (d.year, d.did, d.dpnr, v.avnr)
|
LEFT JOIN payment_delivery_part p ON (p.year, p.did, p.dpnr, p.avnr) = (d.year, d.did, d.dpnr, v.avnr)
|
||||||
LEFT JOIN payment_delivery_part_bucket pb ON (pb.year, pb.did, pb.dpnr, pb.bktnr) = (b.year, b.did, b.dpnr, b.bktnr)
|
LEFT JOIN payment_delivery_part_bucket pb ON (pb.year, pb.did, pb.dpnr, pb.bktnr, pb.avnr) = (b.year, b.did, b.dpnr, b.bktnr, v.avnr)
|
||||||
LEFT JOIN credit c ON (c.year, c.avnr, c.mgnr) = (d.year, v.avnr, d.mgnr)
|
LEFT JOIN credit c ON (c.year, c.avnr, c.mgnr) = (d.year, v.avnr, d.mgnr)
|
||||||
WHERE b.value > 0 AND (d.year = {y} OR {y} IS NULL) AND (v.avnr = {v} OR {v} IS NULL) AND (d.mgnr = {m} OR {m} IS NULL)
|
WHERE b.value > 0 AND (d.year = {y} OR {y} IS NULL) AND (v.avnr = {v} OR {v} IS NULL) AND (d.mgnr = {m} OR {m} IS NULL)
|
||||||
ORDER BY d.year, v.avnr, d.mgnr, d.lsnr, d.dpnr
|
ORDER BY d.year, v.avnr, d.mgnr, d.lsnr, d.dpnr
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using Elwig.Models.Entities;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Elwig.Models.Dtos {
|
||||||
|
public class Transaction(Member member, decimal amount, string currency, int nr) {
|
||||||
|
|
||||||
|
public readonly Member Member = member;
|
||||||
|
public readonly decimal Amount = amount;
|
||||||
|
public readonly string Currency = currency;
|
||||||
|
public readonly int Nr = nr;
|
||||||
|
|
||||||
|
public Transaction(Credit c) : this(c.Member, c.Amount, c.Variant.Season.CurrencyCode, c.TgNr) { }
|
||||||
|
|
||||||
|
public static IEnumerable<Transaction> FromPaymentVariant(PaymentVar variant) {
|
||||||
|
return variant.Credits
|
||||||
|
.OrderBy(c => c.TgNr)
|
||||||
|
.Select(c => new Transaction(c))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FormatAmountCent(long cents) => $"{cents / 100}.{cents % 100:00}";
|
||||||
|
|
||||||
|
public static string FormatAmount(decimal amount) => FormatAmountCent((int)(amount * 100));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,14 @@ namespace Elwig.Models.Entities {
|
|||||||
[Column("avnr")]
|
[Column("avnr")]
|
||||||
public int AvNr { get; set; }
|
public int AvNr { get; set; }
|
||||||
|
|
||||||
|
[Column("net_amount")]
|
||||||
|
public long NetAmountValue { get; set; }
|
||||||
|
[NotMapped]
|
||||||
|
public decimal NetAmount {
|
||||||
|
get => Variant.Season.DecFromDb(NetAmountValue);
|
||||||
|
set => NetAmountValue = Variant.Season.DecToDb(value);
|
||||||
|
}
|
||||||
|
|
||||||
[Column("mod_abs")]
|
[Column("mod_abs")]
|
||||||
public long ModAbsValue { get; set; }
|
public long ModAbsValue { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
@@ -33,12 +41,9 @@ namespace Elwig.Models.Entities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Column("amount")]
|
[Column("amount")]
|
||||||
public long? AmountValue { get; set; }
|
public long AmountValue { get; private set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public decimal? Amount {
|
public decimal Amount => Variant.Season.DecFromDb(AmountValue);
|
||||||
get => AmountValue != null ? Variant.Season.DecFromDb(AmountValue.Value) : null;
|
|
||||||
set => AmountValue = value != null ? Variant.Season.DecToDb(value.Value) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ForeignKey("Year, AvNr")]
|
[ForeignKey("Year, AvNr")]
|
||||||
public virtual PaymentVar Variant { get; private set; }
|
public virtual PaymentVar Variant { get; private set; }
|
||||||
|
|||||||
@@ -39,12 +39,9 @@ namespace Elwig.Models.Entities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Column("amount")]
|
[Column("amount")]
|
||||||
public long AmountValue { get; set; }
|
public long AmountValue { get; private set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public decimal Amount {
|
public decimal Amount => Variant.Season.DecFromDb(AmountValue);
|
||||||
get => Variant.Season.DecFromDb(AmountValue);
|
|
||||||
set => AmountValue = Variant.Season.DecToDb(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
[ForeignKey("Year, AvNr")]
|
[ForeignKey("Year, AvNr")]
|
||||||
public virtual PaymentVar Variant { get; private set; }
|
public virtual PaymentVar Variant { get; private set; }
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ namespace Elwig.Models.Entities {
|
|||||||
[InverseProperty("Variant")]
|
[InverseProperty("Variant")]
|
||||||
public virtual ISet<PaymentMember> MemberPayments { get; private set; }
|
public virtual ISet<PaymentMember> MemberPayments { get; private set; }
|
||||||
|
|
||||||
|
[InverseProperty("Variant")]
|
||||||
|
public virtual ISet<PaymentDeliveryPart> DeliveryPartPayments { get; private set; }
|
||||||
|
|
||||||
[InverseProperty("Variant")]
|
[InverseProperty("Variant")]
|
||||||
public virtual ISet<Credit> Credits { get; private set; }
|
public virtual ISet<Credit> Credits { get; private set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"type": "object",
|
||||||
|
"required": ["mode"],
|
||||||
|
"anyOf": [{
|
||||||
|
"required": ["version", "payment", "curves"],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"mode": {"enum": ["elwig"]},
|
||||||
|
"version": {"enum": [1]},
|
||||||
|
"consider_delivery_modifiers": {"type": "boolean"},
|
||||||
|
"consider_contract_penalties": {"type": "boolean"},
|
||||||
|
"consider_total_penalty": {"type": "boolean"},
|
||||||
|
"consider_auto_business_shares": {"type": "boolean"},
|
||||||
|
"payment": {"$ref": "#/definitions/payment_1"},
|
||||||
|
"quality": {"$ref": "#/definitions/quality_1"},
|
||||||
|
"curves": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/definitions/curve"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"required": ["AuszahlungSorten", "Kurven"],
|
||||||
|
"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]+$"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"patternProperties": {
|
||||||
|
"^([A-Z]{2})?(\/[A-Z]*)?$": {
|
||||||
|
"type": ["number", "string"],
|
||||||
|
"pattern": "^curve:[0-9]+$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"quality_1": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[A-Z]{3}$": {
|
||||||
|
"type": ["number", "string", "object"],
|
||||||
|
"pattern": "^curve:[0-9]+$",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"default": {
|
||||||
|
"type": ["number", "string"],
|
||||||
|
"pattern": "^curve:[0-9]+$"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"patternProperties": {
|
||||||
|
"^([A-Z]{2})?(\/[A-Z]*)?$": {
|
||||||
|
"type": ["number", "string"],
|
||||||
|
"pattern": "^curve:[0-9]+$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"curve": {
|
||||||
|
"type": "object",
|
||||||
|
"required": ["id", "mode", "data"],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"mode": {"enum": ["oe", "kmw"]},
|
||||||
|
"data": {
|
||||||
|
"anyOf": [
|
||||||
|
{"type": "number"},
|
||||||
|
{"$ref": "#/definitions/curve_data"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"geb": {
|
||||||
|
"anyOf": [
|
||||||
|
{"type": "number"},
|
||||||
|
{"$ref": "#/definitions/curve_data"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"curve_data": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"minProperties": 1,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[<>]?([0-9]+(\\.[0-9]+)?)(oe|kmw)$": {"type": "number"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
-- schema version 1 to 2
|
||||||
|
|
||||||
|
DROP VIEW v_area_commitment;
|
||||||
|
|
||||||
|
ALTER TABLE delivery_part DROP COLUMN weighing_reason;
|
||||||
|
|
||||||
|
ALTER TABLE delivery_part ADD COLUMN weighing_reason TEXT CHECK(NOT (manual_weighing = FALSE AND weighing_reason IS NOT NULL));
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
-- schema version 2 to 3
|
||||||
|
|
||||||
|
CREATE TABLE delivery_part_bin (
|
||||||
|
year INTEGER NOT NULL,
|
||||||
|
did INTEGER NOT NULL,
|
||||||
|
dpnr INTEGER NOT NULL,
|
||||||
|
binnr INTEGER NOT NULL,
|
||||||
|
|
||||||
|
discr TEXT NOT NULL,
|
||||||
|
value INTEGER NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT pk_delivery_part_bin PRIMARY KEY (year, did, dpnr, binnr),
|
||||||
|
CONSTRAINT fk_delivery_part_bin_delivery_part FOREIGN KEY (year, did, dpnr) REFERENCES delivery_part (year, did, dpnr)
|
||||||
|
ON UPDATE CASCADE
|
||||||
|
ON DELETE CASCADE
|
||||||
|
) STRICT;
|
||||||
|
|
||||||
|
INSERT INTO delivery_part_bin (year, did, dpnr, binnr, discr, value)
|
||||||
|
SELECT year, did, dpnr, 0, '_', bucket_2 + bucket_3
|
||||||
|
FROM payment_delivery_part
|
||||||
|
WHERE COALESCE(bucket_1, bucket_2, bucket_3, bucket_4, bucket_5, bucket_6, bucket_7, bucket_8, bucket_9) IS NOT NULL
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
INSERT INTO delivery_part_bin (year, did, dpnr, binnr, discr, value)
|
||||||
|
SELECT d.year, d.did, d.dpnr, 1, COALESCE(attributes, ''), bucket_1
|
||||||
|
FROM payment_delivery_part p
|
||||||
|
JOIN v_delivery d ON (d.year, d.did, d.dpnr) = (p.year, p.did, p.dpnr)
|
||||||
|
WHERE COALESCE(bucket_1, bucket_2, bucket_3, bucket_4, bucket_5, bucket_6, bucket_7, bucket_8, bucket_9) IS NOT NULL
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
ALTER TABLE payment_delivery_part DROP COLUMN bucket_1;
|
||||||
|
ALTER TABLE payment_delivery_part DROP COLUMN bucket_2;
|
||||||
|
ALTER TABLE payment_delivery_part DROP COLUMN bucket_3;
|
||||||
|
ALTER TABLE payment_delivery_part DROP COLUMN bucket_4;
|
||||||
|
ALTER TABLE payment_delivery_part DROP COLUMN bucket_5;
|
||||||
|
ALTER TABLE payment_delivery_part DROP COLUMN bucket_6;
|
||||||
|
ALTER TABLE payment_delivery_part DROP COLUMN bucket_7;
|
||||||
|
ALTER TABLE payment_delivery_part DROP COLUMN bucket_8;
|
||||||
|
ALTER TABLE payment_delivery_part DROP COLUMN bucket_9;
|
||||||
|
ALTER TABLE payment_variant DROP COLUMN bucket_1_name;
|
||||||
|
ALTER TABLE payment_variant DROP COLUMN bucket_2_name;
|
||||||
|
ALTER TABLE payment_variant DROP COLUMN bucket_3_name;
|
||||||
|
ALTER TABLE payment_variant DROP COLUMN bucket_4_name;
|
||||||
|
ALTER TABLE payment_variant DROP COLUMN bucket_5_name;
|
||||||
|
ALTER TABLE payment_variant DROP COLUMN bucket_6_name;
|
||||||
|
ALTER TABLE payment_variant DROP COLUMN bucket_7_name;
|
||||||
|
ALTER TABLE payment_variant DROP COLUMN bucket_8_name;
|
||||||
|
ALTER TABLE payment_variant DROP COLUMN bucket_9_name;
|
||||||
|
|
||||||
|
ALTER TABLE delivery_part ADD COLUMN gebunden INTEGER CHECK (gebunden IN (TRUE, FALSE)) DEFAULT NULL;
|
||||||
|
|
||||||
|
DROP VIEW v_delivery;
|
||||||
|
CREATE VIEW v_delivery AS
|
||||||
|
SELECT s.*, GROUP_CONCAT(o.modid) AS modifiers
|
||||||
|
FROM (SELECT p.year, p.did, p.dpnr,
|
||||||
|
d.date, d.time, d.zwstid, d.lnr, d.lsnr,
|
||||||
|
m.mgnr, m.family_name, m.given_name,
|
||||||
|
p.sortid, p.weight, p.kmw, ROUND(p.kmw * (4.54 + 0.022 * p.kmw), 0) AS oe, p.qualid, p.hkid, p.kgnr, p.rdnr,
|
||||||
|
p.gerebelt, p.gebunden,
|
||||||
|
p.qualid IN (SELECT l.qualid FROM wine_quality_level l WHERE NOT l.predicate AND (p.kmw >= l.min_kmw OR l.min_kmw IS NULL) ORDER BY l.min_kmw DESC LIMIT 1,100) AS abgewertet,
|
||||||
|
p.qualid NOT IN ('WEI', 'RSW', 'LDW') AS min_quw,
|
||||||
|
GROUP_CONCAT(a.attrid) AS attributes,
|
||||||
|
COALESCE(SUM(a.fill_lower_bins), 0) AS attribute_prio,
|
||||||
|
d.comment, p.comment AS part_comment
|
||||||
|
FROM delivery_part p
|
||||||
|
JOIN delivery d ON (d.year, d.did) = (p.year, p.did)
|
||||||
|
JOIN member m ON m.mgnr = d.mgnr
|
||||||
|
LEFT JOIN delivery_part_attribute pa ON (pa.year, pa.did, pa.dpnr) = (p.year, p.did, p.dpnr)
|
||||||
|
LEFT JOIN wine_attribute a ON a.attrid = pa.attrid
|
||||||
|
GROUP BY p.year, p.did, p.dpnr
|
||||||
|
ORDER BY p.year, p.did, p.dpnr, a.attrid) s
|
||||||
|
LEFT JOIN delivery_part_modifier o ON (o.year, o.did, o.dpnr) = (s.year, s.did, s.dpnr)
|
||||||
|
GROUP BY s.year, s.lsnr, s.dpnr
|
||||||
|
ORDER BY s.year, s.lsnr, s.dpnr, o.modid;
|
||||||
|
|
||||||
|
DROP VIEW v_bucket;
|
||||||
|
|
||||||
|
CREATE VIEW v_delivery_bin AS
|
||||||
|
SELECT year, mgnr,
|
||||||
|
sortid || IIF(min_quw, REPLACE(COALESCE(attributes, ''), ',', ''), '_') AS bin,
|
||||||
|
SUM(weight) AS weight
|
||||||
|
FROM v_delivery
|
||||||
|
GROUP BY year, mgnr, bin
|
||||||
|
ORDER BY year, mgnr, LENGTH(bin) DESC, bin;
|
||||||
|
|
||||||
|
CREATE VIEW v_payment_bin AS
|
||||||
|
SELECT d.year, d.mgnr,
|
||||||
|
sortid || discr AS bin,
|
||||||
|
SUM(value) AS weight
|
||||||
|
FROM v_delivery d
|
||||||
|
JOIN delivery_part_bin b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr)
|
||||||
|
GROUP BY d.year, d.mgnr, bin
|
||||||
|
HAVING SUM(value) > 0
|
||||||
|
ORDER BY d.year, d.mgnr, bin;
|
||||||
|
|
||||||
|
ALTER TABLE wine_attribute ADD COLUMN fill_lower_bins INTEGER NOT NULL CHECK (fill_lower_bins IN (0, 1, 2)) DEFAULT 0;
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
-- schema version 3 to 4
|
||||||
|
|
||||||
|
DROP VIEW v_payment_bin;
|
||||||
|
CREATE VIEW v_payment_bin AS
|
||||||
|
SELECT d.year, d.mgnr,
|
||||||
|
sortid || discr AS bin,
|
||||||
|
SUM(value) AS weight
|
||||||
|
FROM v_delivery d
|
||||||
|
JOIN delivery_part_bin b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr)
|
||||||
|
GROUP BY d.year, d.mgnr, bin
|
||||||
|
HAVING SUM(value) > 0
|
||||||
|
ORDER BY d.year, d.mgnr, LENGTH(bin) DESC, bin;
|
||||||
|
|
||||||
|
CREATE VIEW v_area_commitment_bin AS
|
||||||
|
SELECT s.year, c.mgnr,
|
||||||
|
c.vtrgid AS bin,
|
||||||
|
CAST(ROUND(SUM(COALESCE(area * min_kg_per_ha, 0)) / 10000.0, 0) AS INTEGER) AS min_kg,
|
||||||
|
CAST(ROUND(SUM(COALESCE(area * max_kg_per_ha, 0)) / 10000.0, 0) AS INTEGER) AS max_kg
|
||||||
|
FROM area_commitment c, season s
|
||||||
|
JOIN area_commitment_type t ON t.vtrgid = c.vtrgid
|
||||||
|
WHERE (year_from IS NULL OR year_from <= s.year) AND
|
||||||
|
(year_to IS NULL OR year_to >= s.year)
|
||||||
|
GROUP BY s.year, c.mgnr, c.vtrgid
|
||||||
|
ORDER BY s.year, c.mgnr, LENGTH(c.vtrgid) DESC, c.vtrgid;
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
-- schema version 4 to 5
|
||||||
|
|
||||||
|
CREATE TABLE _area_commitment_type (
|
||||||
|
vtrgid TEXT NOT NULL CHECK (vtrgid = sortid || COALESCE(attrid, '') || disc),
|
||||||
|
sortid TEXT NOT NULL,
|
||||||
|
attrid TEXT,
|
||||||
|
disc TEXT DEFAULT NULL CHECK (disc REGEXP '^[A-Z0-9]+$'),
|
||||||
|
|
||||||
|
min_kg_per_ha INTEGER,
|
||||||
|
max_kg_per_ha INTEGER,
|
||||||
|
penalty_amount INTEGER,
|
||||||
|
|
||||||
|
CONSTRAINT pk_area_commitment_type PRIMARY KEY (vtrgid),
|
||||||
|
CONSTRAINT sk_area_commitment_type_sort_attr UNIQUE (sortid, attrid, disc),
|
||||||
|
CONSTRAINT fk_area_commitment_type_wine_variety FOREIGN KEY (sortid) REFERENCES wine_variety (sortid)
|
||||||
|
ON UPDATE CASCADE
|
||||||
|
ON DELETE RESTRICT,
|
||||||
|
CONSTRAINT fk_area_commitment_type_wine_attribute FOREIGN KEY (attrid) REFERENCES wine_attribute (attrid)
|
||||||
|
ON UPDATE CASCADE
|
||||||
|
ON DELETE RESTRICT
|
||||||
|
) STRICT;
|
||||||
|
|
||||||
|
INSERT INTO _area_commitment_type (vtrgid, sortid, attrid, disc, min_kg_per_ha, max_kg_per_ha, penalty_amount)
|
||||||
|
SELECT vtrgid, sortid, attrid_1, disc, min_kg_per_ha, max_kg_per_ha, penalty_amount FROM area_commitment_type;
|
||||||
|
|
||||||
|
PRAGMA writable_schema = ON;
|
||||||
|
DROP TABLE area_commitment_type;
|
||||||
|
ALTER TABLE _area_commitment_type RENAME TO area_commitment_type;
|
||||||
|
PRAGMA writable_schema = OFF;
|
||||||
|
|
||||||
|
ALTER TABLE delivery_part ADD COLUMN attrid TEXT DEFAULT NULL REFERENCES wine_attribute (attrid)
|
||||||
|
ON UPDATE CASCADE
|
||||||
|
ON DELETE RESTRICT;
|
||||||
|
|
||||||
|
UPDATE delivery_part
|
||||||
|
SET attrid = (SELECT attrid
|
||||||
|
FROM delivery_part_attribute a
|
||||||
|
WHERE (delivery_part.year, delivery_part.did, delivery_part.dpnr) = (a.year, a.did, a.dpnr)
|
||||||
|
ORDER BY attrid DESC
|
||||||
|
LIMIT 1);
|
||||||
|
|
||||||
|
DROP TRIGGER t_delivery_part_attribute_i_mtime_delivery_part;
|
||||||
|
DROP TRIGGER t_delivery_part_attribute_u_mtime_delivery_part;
|
||||||
|
DROP TRIGGER t_delivery_part_attribute_d_mtime_delivery_part;
|
||||||
|
DROP TABLE delivery_part_attribute;
|
||||||
|
|
||||||
|
DROP VIEW v_delivery;
|
||||||
|
CREATE VIEW v_delivery AS
|
||||||
|
SELECT p.year, p.did, p.dpnr,
|
||||||
|
d.date, d.time, d.zwstid, d.lnr, d.lsnr,
|
||||||
|
m.mgnr, m.family_name, m.given_name,
|
||||||
|
p.sortid, a.attrid,
|
||||||
|
p.weight, p.kmw, ROUND(p.kmw * (4.54 + 0.022 * p.kmw), 0) AS oe, p.qualid,
|
||||||
|
p.hkid, p.kgnr, p.rdnr,
|
||||||
|
p.gerebelt, p.gebunden,
|
||||||
|
p.qualid IN (SELECT l.qualid FROM wine_quality_level l WHERE NOT l.predicate AND (p.kmw >= l.min_kmw OR l.min_kmw IS NULL) ORDER BY l.min_kmw DESC LIMIT 1,100) AS abgewertet,
|
||||||
|
p.qualid NOT IN ('WEI', 'RSW', 'LDW') AS min_quw,
|
||||||
|
COALESCE(a.fill_lower_bins, 0) AS attribute_prio,
|
||||||
|
GROUP_CONCAT(o.modid) AS modifiers,
|
||||||
|
d.comment, p.comment AS part_comment
|
||||||
|
FROM delivery_part p
|
||||||
|
JOIN delivery d ON (d.year, d.did) = (p.year, p.did)
|
||||||
|
JOIN member m ON m.mgnr = d.mgnr
|
||||||
|
LEFT JOIN wine_attribute a ON a.attrid = p.attrid
|
||||||
|
LEFT JOIN delivery_part_modifier o ON (o.year, o.did, o.dpnr) = (p.year, p.did, p.dpnr)
|
||||||
|
GROUP BY p.year, p.did, p.dpnr
|
||||||
|
ORDER BY p.year, p.did, p.dpnr, o.modid;
|
||||||
|
|
||||||
|
DROP VIEW v_delivery_bin;
|
||||||
|
CREATE VIEW v_delivery_bin AS
|
||||||
|
SELECT year, mgnr,
|
||||||
|
sortid || IIF(min_quw, COALESCE(attrid, ''), '_') AS bin,
|
||||||
|
SUM(weight) AS weight
|
||||||
|
FROM v_delivery
|
||||||
|
GROUP BY year, mgnr, bin
|
||||||
|
ORDER BY year, mgnr, LENGTH(bin) DESC, bin;
|
||||||
|
|
||||||
|
DROP VIEW v_stat_attr;
|
||||||
|
CREATE VIEW v_stat_attr AS
|
||||||
|
SELECT year, attrid,
|
||||||
|
SUM(weight) as sum,
|
||||||
|
ROUND(SUM(kmw * weight) / SUM(weight), 2) AS kmw,
|
||||||
|
ROUND(SUM(oe * weight) / SUM(weight), 1) AS oe,
|
||||||
|
COUNT(DISTINCT did) AS lieferungen,
|
||||||
|
COUNT(DISTINCT mgnr) AS mitglieder
|
||||||
|
FROM v_delivery
|
||||||
|
GROUP BY year, attrid
|
||||||
|
ORDER BY year, attrid;
|
||||||
|
|
||||||
|
DROP VIEW v_stat_sort_attr;
|
||||||
|
CREATE VIEW v_stat_sort_attr AS
|
||||||
|
SELECT year, sortid, attrid,
|
||||||
|
SUM(weight) as sum,
|
||||||
|
ROUND(SUM(kmw * weight) / SUM(weight), 2) AS kmw,
|
||||||
|
ROUND(SUM(oe * weight) / SUM(weight), 1) AS oe,
|
||||||
|
COUNT(DISTINCT did) AS lieferungen,
|
||||||
|
COUNT(DISTINCT mgnr) AS mitglieder
|
||||||
|
FROM v_delivery
|
||||||
|
GROUP BY year, sortid, attrid
|
||||||
|
ORDER BY year, sortid, attrid;
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
-- schema version 5 to 6
|
||||||
|
|
||||||
|
DROP VIEW IF EXISTS v_area_commitment;
|
||||||
|
|
||||||
|
PRAGMA writable_schema = ON;
|
||||||
|
ALTER TABLE wine_attribute DROP COLUMN fill_lower_bins;
|
||||||
|
ALTER TABLE wine_attribute ADD COLUMN strict INTEGER NOT NULL CHECK (strict IN (TRUE, FALSE)) DEFAULT FALSE;
|
||||||
|
ALTER TABLE wine_attribute ADD COLUMN fill_lower INTEGER NOT NULL CHECK (fill_lower IN (0, 1, 2)) DEFAULT 0;
|
||||||
|
DROP VIEW v_delivery;
|
||||||
|
CREATE VIEW v_delivery AS
|
||||||
|
SELECT p.year, p.did, p.dpnr,
|
||||||
|
d.date, d.time, d.zwstid, d.lnr, d.lsnr,
|
||||||
|
m.mgnr, m.family_name, m.given_name,
|
||||||
|
p.sortid, a.attrid,
|
||||||
|
p.weight, p.kmw, ROUND(p.kmw * (4.54 + 0.022 * p.kmw), 0) AS oe, p.qualid,
|
||||||
|
p.hkid, p.kgnr, p.rdnr,
|
||||||
|
p.gerebelt, p.gebunden,
|
||||||
|
p.qualid IN (SELECT l.qualid FROM wine_quality_level l WHERE NOT l.predicate AND (p.kmw >= l.min_kmw OR l.min_kmw IS NULL) ORDER BY l.min_kmw DESC LIMIT 1,100) AS abgewertet,
|
||||||
|
p.qualid NOT IN ('WEI', 'RSW', 'LDW') AS min_quw,
|
||||||
|
IIF(a.strict, COALESCE(a.fill_lower, 0), 0) AS attribute_prio,
|
||||||
|
GROUP_CONCAT(o.modid) AS modifiers,
|
||||||
|
d.comment, p.comment AS part_comment
|
||||||
|
FROM delivery_part p
|
||||||
|
JOIN delivery d ON (d.year, d.did) = (p.year, p.did)
|
||||||
|
JOIN member m ON m.mgnr = d.mgnr
|
||||||
|
LEFT JOIN wine_attribute a ON a.attrid = p.attrid
|
||||||
|
LEFT JOIN delivery_part_modifier o ON (o.year, o.did, o.dpnr) = (p.year, p.did, p.dpnr)
|
||||||
|
GROUP BY p.year, p.did, p.dpnr
|
||||||
|
ORDER BY p.year, p.did, p.dpnr, o.modid;
|
||||||
|
PRAGMA writable_schema = OFF;
|
||||||
|
|
||||||
|
DROP VIEW v_area_commitment_bin;
|
||||||
|
DROP VIEW v_delivery_bin;
|
||||||
|
DROP VIEW v_payment_bin;
|
||||||
|
|
||||||
|
ALTER TABLE area_commitment_type DROP COLUMN max_kg_per_ha;
|
||||||
|
ALTER TABLE area_commitment_type ADD COLUMN penalty_per_kg INTEGER DEFAULT NULL;
|
||||||
|
ALTER TABLE area_commitment_type ADD COLUMN penalty_none INTEGER DEFAULT NULL;
|
||||||
|
|
||||||
|
ALTER TABLE wine_cultivation ADD COLUMN description TEXT DEFAULT NULL;
|
||||||
|
ALTER TABLE member ADD COLUMN organic INTEGER NOT NULL CHECK (organic IN (TRUE, FALSE)) DEFAULT FALSE;
|
||||||
|
|
||||||
|
ALTER TABLE season ADD COLUMN max_kg_per_ha INTEGER NOT NULL DEFAULT 10000;
|
||||||
|
ALTER TABLE season ADD COLUMN vat_normal REAL NOT NULL DEFAULT 0.10;
|
||||||
|
ALTER TABLE season ADD COLUMN vat_flatrate REAL NOT NULL DEFAULT 0.13;
|
||||||
|
ALTER TABLE season ADD COLUMN min_kg_per_bs INTEGER NOT NULL DEFAULT 750;
|
||||||
|
ALTER TABLE season ADD COLUMN max_kg_per_bs INTEGER NOT NULL DEFAULT 3000;
|
||||||
|
ALTER TABLE season ADD COLUMN penalty_per_kg INTEGER DEFAULT NULL;
|
||||||
|
ALTER TABLE season ADD COLUMN penalty_amount INTEGER DEFAULT NULL;
|
||||||
|
ALTER TABLE season ADD COLUMN penalty_none INTEGER DEFAULT NULL;
|
||||||
|
|
||||||
|
DELETE FROM client_parameter WHERE param IN ('DELIVERY_RIGHT', 'DELIVERY_OBLIGATION', 'VAT_NORMAL', 'VAT_REDUCED', 'VAT_FLATRATE');
|
||||||
|
|
||||||
|
CREATE TABLE delivery_part_bucket (
|
||||||
|
year INTEGER NOT NULL,
|
||||||
|
did INTEGER NOT NULL,
|
||||||
|
dpnr INTEGER NOT NULL,
|
||||||
|
bktnr INTEGER NOT NULL,
|
||||||
|
|
||||||
|
discr TEXT NOT NULL,
|
||||||
|
value INTEGER NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT pk_delivery_part_bucket PRIMARY KEY (year, did, dpnr, bktnr),
|
||||||
|
CONSTRAINT fk_delivery_part_bucket_delivery_part FOREIGN KEY (year, did, dpnr) REFERENCES delivery_part (year, did, dpnr)
|
||||||
|
ON UPDATE CASCADE
|
||||||
|
ON DELETE CASCADE
|
||||||
|
) STRICT;
|
||||||
|
|
||||||
|
INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value)
|
||||||
|
SELECT year, did, dpnr, binnr, discr, value
|
||||||
|
FROM delivery_part_bin;
|
||||||
|
|
||||||
|
DROP TABLE delivery_part_bin;
|
||||||
|
|
||||||
|
CREATE VIEW v_area_commitment_bucket_strict AS
|
||||||
|
SELECT s.year, c.mgnr,
|
||||||
|
t.sortid || COALESCE(a.attrid, '') AS bucket,
|
||||||
|
t.sortid, a.attrid,
|
||||||
|
CAST(ROUND(SUM(area) * COALESCE(t.min_kg_per_ha, 0) / 10000.0, 0) AS INTEGER) AS min_kg,
|
||||||
|
CAST(ROUND(SUM(area) * MIN(COALESCE(a.max_kg_per_ha, s.max_kg_per_ha), s.max_kg_per_ha) / 10000.0, 0) AS INTEGER) AS max_kg,
|
||||||
|
CAST(ROUND(SUM(area) * s.max_kg_per_ha / 10000.0, 0) AS INTEGER) AS upper_max_kg
|
||||||
|
FROM season s, area_commitment c
|
||||||
|
JOIN area_commitment_type t ON t.vtrgid = c.vtrgid
|
||||||
|
LEFT JOIN wine_attribute a ON a.attrid = t.attrid
|
||||||
|
WHERE (year_from IS NULL OR year_from <= s.year) AND
|
||||||
|
(year_to IS NULL OR year_to >= s.year)
|
||||||
|
GROUP BY s.year, c.mgnr, bucket
|
||||||
|
ORDER BY s.year, c.mgnr, bucket;
|
||||||
|
|
||||||
|
CREATE VIEW v_area_commitment_bucket AS
|
||||||
|
SELECT year, mgnr, bucket, min_kg, max_kg
|
||||||
|
FROM v_area_commitment_bucket_strict
|
||||||
|
WHERE attrid IS NOT NULL
|
||||||
|
UNION ALL
|
||||||
|
SELECT b.year, b.mgnr, b.sortid,
|
||||||
|
SUM(b.min_kg) AS min_kg,
|
||||||
|
SUM(b.upper_max_kg) AS max_kg
|
||||||
|
FROM v_area_commitment_bucket_strict b
|
||||||
|
LEFT JOIN wine_attribute a ON a.attrid = b.attrid
|
||||||
|
WHERE a.strict IS NULL OR a.strict = FALSE
|
||||||
|
GROUP BY b.year, b.mgnr, b.sortid
|
||||||
|
ORDER BY year, mgnr, bucket;
|
||||||
|
|
||||||
|
CREATE VIEW v_delivery_bucket_strict AS
|
||||||
|
SELECT year, mgnr,
|
||||||
|
sortid || IIF(min_quw, COALESCE(attrid, ''), '_') AS bucket,
|
||||||
|
sortid, IIF(min_quw, attrid, NULL) AS attrid,
|
||||||
|
SUM(weight) AS weight,
|
||||||
|
min_quw
|
||||||
|
FROM v_delivery
|
||||||
|
GROUP BY year, mgnr, bucket
|
||||||
|
ORDER BY year, mgnr, bucket;
|
||||||
|
|
||||||
|
CREATE VIEW v_delivery_bucket AS
|
||||||
|
SELECT year, mgnr, bucket, weight
|
||||||
|
FROM v_delivery_bucket_strict
|
||||||
|
WHERE attrid IS NOT NULL OR NOT min_quw
|
||||||
|
UNION ALL
|
||||||
|
SELECT b.year, b.mgnr, b.sortid,
|
||||||
|
SUM(b.weight) AS weight
|
||||||
|
FROM v_delivery_bucket_strict b
|
||||||
|
LEFT JOIN wine_attribute a ON a.attrid = b.attrid
|
||||||
|
WHERE min_quw AND (a.strict IS NULL OR a.strict = FALSE)
|
||||||
|
GROUP BY b.year, b.mgnr, b.sortid
|
||||||
|
ORDER BY year, mgnr, bucket;
|
||||||
|
|
||||||
|
CREATE VIEW v_payment_bucket_strict AS
|
||||||
|
SELECT d.year, d.mgnr,
|
||||||
|
d.sortid || b.discr AS bucket,
|
||||||
|
d.sortid, IIF(b.discr IN ('', '_'), NULL, b.discr) AS attrid,
|
||||||
|
SUM(b.value) AS weight,
|
||||||
|
b.discr != '_' AS gebunden
|
||||||
|
FROM v_delivery d
|
||||||
|
LEFT JOIN delivery_part_bucket b ON (b.year, b.did, b.dpnr) = (d.year, d.did, d.dpnr)
|
||||||
|
GROUP BY d.year, d.mgnr, bucket
|
||||||
|
HAVING SUM(b.value) > 0
|
||||||
|
ORDER BY d.year, d.mgnr, bucket;
|
||||||
|
|
||||||
|
CREATE VIEW v_payment_bucket AS
|
||||||
|
SELECT year, mgnr, bucket, weight
|
||||||
|
FROM v_payment_bucket_strict
|
||||||
|
WHERE attrid IS NOT NULL OR NOT gebunden
|
||||||
|
UNION ALL
|
||||||
|
SELECT b.year, b.mgnr, b.sortid,
|
||||||
|
SUM(b.weight) AS weight
|
||||||
|
FROM v_payment_bucket_strict b
|
||||||
|
LEFT JOIN wine_attribute a ON a.attrid = b.attrid
|
||||||
|
WHERE gebunden AND (a.strict IS NULL OR a.strict = FALSE)
|
||||||
|
GROUP BY b.year, b.mgnr, b.sortid
|
||||||
|
ORDER BY year, mgnr, bucket;
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
-- schema version 6 to 7
|
||||||
|
|
||||||
|
DROP VIEW v_area_commitment_bucket_strict;
|
||||||
|
CREATE VIEW v_area_commitment_bucket_strict AS
|
||||||
|
SELECT s.year, c.mgnr,
|
||||||
|
t.sortid || COALESCE(a.attrid, '') AS bucket,
|
||||||
|
t.sortid, a.attrid,
|
||||||
|
SUM(area) AS area,
|
||||||
|
CAST(ROUND(SUM(area) * COALESCE(t.min_kg_per_ha, 0) / 10000.0, 0) AS INTEGER) AS min_kg,
|
||||||
|
CAST(ROUND(SUM(area) * MIN(COALESCE(a.max_kg_per_ha, s.max_kg_per_ha), s.max_kg_per_ha) / 10000.0, 0) AS INTEGER) AS max_kg,
|
||||||
|
CAST(ROUND(SUM(area) * s.max_kg_per_ha / 10000.0, 0) AS INTEGER) AS upper_max_kg
|
||||||
|
FROM season s, area_commitment c
|
||||||
|
JOIN area_commitment_type t ON t.vtrgid = c.vtrgid
|
||||||
|
LEFT JOIN wine_attribute a ON a.attrid = t.attrid
|
||||||
|
WHERE (year_from IS NULL OR year_from <= s.year) AND
|
||||||
|
(year_to IS NULL OR year_to >= s.year)
|
||||||
|
GROUP BY s.year, c.mgnr, bucket
|
||||||
|
ORDER BY s.year, c.mgnr, bucket;
|
||||||
|
|
||||||
|
CREATE VIEW v_under_delivery_bucket_strict AS
|
||||||
|
SELECT c.year, c.mgnr, c.bucket, c.min_kg, COALESCE(p.weight, 0) AS weight
|
||||||
|
FROM v_area_commitment_bucket_strict c
|
||||||
|
LEFT JOIN v_payment_bucket_strict p ON (p.year, p.mgnr, p.bucket) = (c.year, c.mgnr, c.bucket)
|
||||||
|
ORDER BY c.year, c.mgnr, c.bucket;
|
||||||
|
|
||||||
|
CREATE VIEW v_under_delivery_bucket AS
|
||||||
|
SELECT u.year, u.mgnr, u.bucket, u.min_kg,
|
||||||
|
u.weight + SUM(MAX(COALESCE(p.weight - s.min_kg, 0), 0)) AS weight
|
||||||
|
FROM v_under_delivery_bucket_strict u
|
||||||
|
LEFT JOIN v_payment_bucket_strict p ON (p.year, p.mgnr, p.sortid) = (u.year, u.mgnr, u.bucket) AND p.attrid IS NOT NULL
|
||||||
|
LEFT JOIN wine_attribute a ON a.attrid = p.attrid
|
||||||
|
LEFT JOIN v_area_commitment_bucket_strict s ON (s.year, s.mgnr, s.bucket) = (p.year, p.mgnr, p.bucket)
|
||||||
|
WHERE (p.gebunden IS NULL OR p.gebunden) AND (a.strict IS NULL OR a.strict = FALSE)
|
||||||
|
GROUP BY u.year, u.mgnr, u.bucket
|
||||||
|
ORDER BY u.year, u.mgnr, u.bucket;
|
||||||
|
|
||||||
|
CREATE VIEW v_under_delivery AS
|
||||||
|
SELECT year, mgnr, bucket, min_kg, weight, weight - min_kg AS diff
|
||||||
|
FROM v_under_delivery_bucket
|
||||||
|
WHERE diff < 0;
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
-- schema version 7 to 8
|
||||||
|
|
||||||
|
INSERT INTO wb_gem
|
||||||
|
SELECT a.gkz, 'WLNO'
|
||||||
|
FROM AT_gem a
|
||||||
|
LEFT JOIN wb_gem w ON w.gkz = a.gkz
|
||||||
|
WHERE a.gkz / 10000 = 3 AND w.hkid IS NULL;
|
||||||
|
|
||||||
|
INSERT INTO wb_gem
|
||||||
|
SELECT gkz, 'SLVL'
|
||||||
|
FROM AT_gem
|
||||||
|
WHERE gkz / 100 IN (617, 622, 623);
|
||||||
|
|
||||||
|
INSERT INTO wb_gem
|
||||||
|
SELECT gkz, 'SLSS'
|
||||||
|
FROM AT_gem
|
||||||
|
WHERE gkz / 100 = 610;
|
||||||
|
|
||||||
|
UPDATE wb_gem
|
||||||
|
SET hkid = 'SLVL'
|
||||||
|
WHERE gkz IN (61007, 61052, 61001, 61055, 61027, 61057, 61008, 61057);
|
||||||
|
|
||||||
|
INSERT INTO wb_gem
|
||||||
|
SELECT gkz, 'SLWS'
|
||||||
|
FROM AT_gem
|
||||||
|
WHERE gkz / 100 IN (603, 616) OR gkz IN (60101, 60663, 60651, 60659, 60664, 60647, 60641, 60639, 60665, 60669, 60618, 60629, 60608, 60670, 60624, 60660, 60656, 60655);
|
||||||
|
|
||||||
|
INSERT INTO wb_gem
|
||||||
|
SELECT g.gkz, 'SLVL'
|
||||||
|
FROM AT_gem g
|
||||||
|
LEFT JOIN wb_gem w ON w.gkz = g.gkz
|
||||||
|
WHERE g.gkz / 100 = 606 AND w.hkid IS NULL;
|
||||||
|
|
||||||
|
INSERT INTO wb_gem
|
||||||
|
SELECT g.gkz, 'SLST'
|
||||||
|
FROM AT_gem g
|
||||||
|
LEFT JOIN wb_gem w ON w.gkz = g.gkz
|
||||||
|
WHERE g.gkz / 10000 = 6 AND w.hkid IS NULL;
|
||||||
|
|
||||||
|
INSERT INTO wb_gem
|
||||||
|
SELECT gkz, 'BLOO'
|
||||||
|
FROM AT_gem
|
||||||
|
WHERE gkz / 10000 = 4;
|
||||||
|
|
||||||
|
INSERT INTO wb_gem
|
||||||
|
SELECT gkz, 'BLKA'
|
||||||
|
FROM AT_gem
|
||||||
|
WHERE gkz / 10000 = 2;
|
||||||
|
|
||||||
|
INSERT INTO wb_gem
|
||||||
|
SELECT gkz, 'BLSB'
|
||||||
|
FROM AT_gem
|
||||||
|
WHERE gkz / 10000 = 5;
|
||||||
|
|
||||||
|
INSERT INTO wb_gem
|
||||||
|
SELECT gkz, 'BLTI'
|
||||||
|
FROM AT_gem
|
||||||
|
WHERE gkz / 10000 = 7;
|
||||||
|
|
||||||
|
INSERT INTO wb_gem
|
||||||
|
SELECT gkz, 'BLVO'
|
||||||
|
FROM AT_gem
|
||||||
|
WHERE gkz / 10000 = 8;
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
-- schema version 8 to 9
|
||||||
|
|
||||||
|
CREATE TABLE payment_delivery_part_bucket (
|
||||||
|
year INTEGER NOT NULL,
|
||||||
|
did INTEGER NOT NULL,
|
||||||
|
dpnr INTEGER NOT NULL,
|
||||||
|
bktnr INTEGER NOT NULL,
|
||||||
|
avnr INTEGER NOT NULL,
|
||||||
|
|
||||||
|
price INTEGER NOT NULL,
|
||||||
|
amount INTEGER NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT pk_payment_delivery_part_bucket PRIMARY KEY (year, did, dpnr, bktnr, avnr),
|
||||||
|
CONSTRAINT fk_payment_delivery_part_bucket_delivery_part_bucket FOREIGN KEY (year, did, dpnr, bktnr) REFERENCES delivery_part_bucket (year, did, dpnr, bktnr)
|
||||||
|
ON UPDATE CASCADE
|
||||||
|
ON DELETE CASCADE,
|
||||||
|
CONSTRAINT fk_payment_delivery_part_bucket_payment_variant FOREIGN KEY (year, avnr) REFERENCES payment_variant (year, avnr)
|
||||||
|
ON UPDATE CASCADE
|
||||||
|
ON DELETE CASCADE
|
||||||
|
) STRICT;
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS t_payment_delivery_part_i;
|
||||||
|
DROP TRIGGER IF EXISTS t_payment_delivery_part_u;
|
||||||
|
DROP TRIGGER IF EXISTS t_payment_delivery_part_d;
|
||||||
|
ALTER TABLE payment_delivery_part RENAME COLUMN amount TO net_amount;
|
||||||
|
ALTER TABLE payment_delivery_part ADD COLUMN amount INTEGER NOT NULL GENERATED ALWAYS AS (ROUND(net_amount * (1 + mod_rel) + mod_abs)) VIRTUAL;
|
||||||
|
|
||||||
|
CREATE TRIGGER t_payment_delivery_part_bucket_i
|
||||||
|
AFTER INSERT ON payment_delivery_part_bucket FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO payment_delivery_part (year, did, dpnr, avnr, net_amount)
|
||||||
|
VALUES (NEW.year, NEW.did, NEW.dpnr, NEW.avnr, NEW.amount)
|
||||||
|
ON CONFLICT DO UPDATE SET net_amount = net_amount + NEW.amount;
|
||||||
|
END;
|
||||||
|
|
||||||
|
CREATE TRIGGER t_payment_delivery_part_bucket_u
|
||||||
|
AFTER UPDATE OF amount ON payment_delivery_part_bucket FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
UPDATE payment_delivery_part
|
||||||
|
SET net_amount = net_amount - OLD.amount
|
||||||
|
WHERE (year, did, dpnr, avnr) = (NEW.year, NEW.did, NEW.dpnr, NEW.avnr);
|
||||||
|
UPDATE payment_delivery_part
|
||||||
|
SET net_amount = net_amount + NEW.amount
|
||||||
|
WHERE (year, did, dpnr, avnr) = (NEW.year, NEW.did, NEW.dpnr, NEW.avnr);
|
||||||
|
END;
|
||||||
|
|
||||||
|
CREATE TRIGGER t_payment_delivery_part_bucket_d
|
||||||
|
AFTER DELETE ON payment_delivery_part_bucket FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
UPDATE payment_delivery_part
|
||||||
|
SET net_amount = net_amount - OLD.amount
|
||||||
|
WHERE (year, did, dpnr, avnr) = (OLD.year, OLD.did, OLD.dpnr, OLD.avnr);
|
||||||
|
END;
|
||||||
|
|
||||||
|
ALTER TABLE payment_member RENAME COLUMN amount TO net_amount;
|
||||||
|
ALTER TABLE payment_member ADD COLUMN mod_abs INTEGER NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE payment_member ADD COLUMN mod_rel REAL NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE payment_member ADD COLUMN amount INTEGER NOT NULL GENERATED ALWAYS AS (ROUND(net_amount * (1 + mod_rel) + mod_rel)) VIRTUAL;
|
||||||
|
|
||||||
|
CREATE TRIGGER t_payment_delivery_part_i
|
||||||
|
AFTER INSERT ON payment_delivery_part FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO payment_member (year, avnr, mgnr, net_amount)
|
||||||
|
VALUES (NEW.year, NEW.avnr, (SELECT mgnr FROM delivery WHERE (year, did) = (NEW.year, NEW.did)), NEW.amount)
|
||||||
|
ON CONFLICT DO UPDATE SET net_amount = net_amount + excluded.net_amount;
|
||||||
|
END;
|
||||||
|
|
||||||
|
CREATE TRIGGER t_payment_delivery_part_u
|
||||||
|
AFTER UPDATE OF amount ON payment_delivery_part FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
UPDATE payment_member
|
||||||
|
SET net_amount = net_amount - OLD.amount
|
||||||
|
WHERE (year, avnr, mgnr) = (NEW.year, NEW.avnr, (SELECT mgnr FROM delivery WHERE (year, did) = (NEW.year, NEW.did)));
|
||||||
|
UPDATE payment_member
|
||||||
|
SET net_amount = net_amount + NEW.amount
|
||||||
|
WHERE (year, avnr, mgnr) = (NEW.year, NEW.avnr, (SELECT mgnr FROM delivery WHERE (year, did) = (NEW.year, NEW.did)));
|
||||||
|
END;
|
||||||
|
|
||||||
|
CREATE TRIGGER t_payment_delivery_part_d
|
||||||
|
AFTER DELETE ON payment_delivery_part FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
UPDATE payment_member
|
||||||
|
SET net_amount = net_amount - OLD.amount
|
||||||
|
WHERE (year, avnr, mgnr) = (OLD.year, OLD.avnr, (SELECT mgnr FROM delivery WHERE (year, did) = (OLD.year, OLD.did)));
|
||||||
|
END;
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
-- schema version 9 to 10
|
||||||
|
|
||||||
|
UPDATE wine_quality_level SET min_kmw = 10.6 WHERE qualid = 'RSW';
|
||||||
|
|
||||||
|
DROP VIEW v_area_commitment_bucket;
|
||||||
|
CREATE VIEW v_area_commitment_bucket AS
|
||||||
|
SELECT year, mgnr, bucket, area, min_kg, max_kg
|
||||||
|
FROM v_area_commitment_bucket_strict
|
||||||
|
WHERE attrid IS NOT NULL
|
||||||
|
UNION ALL
|
||||||
|
SELECT b.year, b.mgnr, b.sortid,
|
||||||
|
SUM(b.area) AS area,
|
||||||
|
SUM(b.min_kg) AS min_kg,
|
||||||
|
SUM(b.upper_max_kg) AS max_kg
|
||||||
|
FROM v_area_commitment_bucket_strict b
|
||||||
|
LEFT JOIN wine_attribute a ON a.attrid = b.attrid
|
||||||
|
WHERE a.strict IS NULL OR a.strict = FALSE
|
||||||
|
GROUP BY b.year, b.mgnr, b.sortid
|
||||||
|
ORDER BY year, mgnr, bucket;
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
-- schema version 10 to 11
|
||||||
|
|
||||||
|
CREATE TABLE payment_delivery_part_new (
|
||||||
|
year INTEGER NOT NULL,
|
||||||
|
did INTEGER NOT NULL,
|
||||||
|
dpnr INTEGER NOT NULL,
|
||||||
|
avnr INTEGER NOT NULL,
|
||||||
|
|
||||||
|
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
|
||||||
|
AFTER INSERT ON payment_delivery_part FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO payment_member (year, avnr, mgnr, net_amount)
|
||||||
|
SELECT year, NEW.avnr, mgnr, NEW.amount FROM delivery WHERE (year, did) = (NEW.year, NEW.did)
|
||||||
|
ON CONFLICT DO UPDATE SET net_amount = net_amount + excluded.net_amount;
|
||||||
|
END;
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS t_payment_delivery_part_u;
|
||||||
|
CREATE TRIGGER t_payment_delivery_part_u
|
||||||
|
AFTER UPDATE ON payment_delivery_part FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
UPDATE payment_member
|
||||||
|
SET net_amount = net_amount - OLD.amount
|
||||||
|
WHERE (year, avnr, mgnr) IN (SELECT year, OLD.avnr, mgnr FROM delivery WHERE (year, did) = (OLD.year, OLD.did));
|
||||||
|
INSERT INTO payment_member (year, avnr, mgnr, net_amount)
|
||||||
|
SELECT year, NEW.avnr, mgnr, NEW.amount FROM delivery WHERE (year, did) = (NEW.year, NEW.did)
|
||||||
|
ON CONFLICT DO UPDATE SET net_amount = net_amount + excluded.net_amount;
|
||||||
|
END;
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS t_payment_delivery_part_d;
|
||||||
|
CREATE TRIGGER t_payment_delivery_part_d
|
||||||
|
AFTER DELETE ON payment_delivery_part FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
UPDATE payment_member
|
||||||
|
SET net_amount = net_amount - OLD.amount
|
||||||
|
WHERE (year, avnr, mgnr) IN (SELECT year, OLD.avnr, mgnr FROM delivery WHERE (year, did) = (OLD.year, OLD.did));
|
||||||
|
END;
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS t_payment_delivery_part_bucket_u;
|
||||||
|
CREATE TRIGGER t_payment_delivery_part_bucket_u
|
||||||
|
AFTER UPDATE ON payment_delivery_part_bucket FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
UPDATE payment_delivery_part
|
||||||
|
SET net_amount = net_amount - OLD.amount
|
||||||
|
WHERE (year, did, dpnr, avnr) = (OLD.year, OLD.did, OLD.dpnr, OLD.avnr);
|
||||||
|
UPDATE payment_delivery_part
|
||||||
|
SET net_amount = net_amount + NEW.amount
|
||||||
|
WHERE (year, did, dpnr, avnr) = (NEW.year, NEW.did, NEW.dpnr, NEW.avnr);
|
||||||
|
END;
|
||||||
|
|
||||||
|
ALTER TABLE payment_member DROP COLUMN amount;
|
||||||
|
ALTER TABLE payment_member ADD COLUMN amount INTEGER NOT NULL GENERATED ALWAYS AS (ROUND(net_amount * (1 + mod_rel) + mod_abs)) VIRTUAL;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
-- schema version 11 to 12
|
||||||
|
|
||||||
|
CREATE VIEW v_stat_member AS
|
||||||
|
SELECT year, mgnr,
|
||||||
|
SUM(weight) AS sum,
|
||||||
|
ROUND(SUM(kmw * weight) / SUM(weight), 2) AS kmw,
|
||||||
|
ROUND(SUM(oe * weight) / SUM(weight), 1) AS oe,
|
||||||
|
COUNT(DISTINCT did) AS lieferungen
|
||||||
|
FROM v_delivery
|
||||||
|
GROUP BY year, mgnr
|
||||||
|
ORDER BY year, mgnr;
|
||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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="" 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="" 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="" 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>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Text.Json.Nodes;
|
using System.Text.Json.Nodes;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
@@ -16,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;
|
||||||
@@ -37,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 = new Control[] {
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,170 +59,65 @@ namespace Elwig.Windows {
|
|||||||
await RefreshGraphListQuery();
|
await RefreshGraphListQuery();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RefreshGraphListQuery(bool updateSort = false) {
|
private async Task RefreshGraphListQuery() {
|
||||||
List<PaymentVar> paymentVars = await Context.PaymentVariants.Where(p => p.Year == Year && p.AvNr == AvNr).ToListAsync();
|
var attrVariants = Context.DeliveryParts
|
||||||
|
.Where(d => d.Year == Year)
|
||||||
|
.Select(d => $"{d.SortId}{d.AttrId}")
|
||||||
|
.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());
|
||||||
|
|
||||||
if (paymentVars.Count != 1) {
|
ControlUtils.RenewItemsSource(AppliedInput, attrVariants, g => g);
|
||||||
return;
|
ControlUtils.RenewItemsSource(GraphList, GraphEntries, g => (g as GraphEntry)?.Id, null, ControlUtils.RenewSourceDefault.IfOnly);
|
||||||
}
|
|
||||||
|
|
||||||
PaymentVar paymentVar = paymentVars[0];
|
|
||||||
var data = JsonNode.Parse(paymentVar.Data).AsObject();
|
|
||||||
|
|
||||||
var auszahlungsSorten = data["AuszahlungSorten"]?.AsObject();
|
|
||||||
if (auszahlungsSorten == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var Graphs = auszahlungsSorten["Kurven"]?.AsArray();
|
|
||||||
|
|
||||||
if (Graphs == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Graph> GraphsList = new();
|
|
||||||
|
|
||||||
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 = new();
|
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.ToArray());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> RemoveGraph(int num) {
|
|
||||||
List<PaymentVar> paymentVars = await Context.PaymentVariants.Where(p => p.Year == Year && p.AvNr == AvNr).ToListAsync();
|
|
||||||
|
|
||||||
if (paymentVars.Count != 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
PaymentVar paymentVar = paymentVars[0];
|
|
||||||
var data = JsonNode.Parse(paymentVar.Data).AsObject();
|
|
||||||
|
|
||||||
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;
|
||||||
@@ -264,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();
|
||||||
@@ -279,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,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;
|
||||||
@@ -389,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -404,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) {
|
||||||
@@ -411,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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -424,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) {
|
||||||
@@ -445,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) {
|
||||||
@@ -462,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;
|
||||||
|
|
||||||
@@ -486,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 {
|
||||||
@@ -508,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,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>();
|
||||||
@@ -741,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) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.

Filtern nach:
Sorte: z.B. GV, ZW, rr, sa, !gv (ausgenommen GV), ...
Qualitätsstufe: z.B. QUW, kab, ldw, ...
Gradation: z.B. >73, <15, 17-18, 15-, >17,5, 62-75, ...
Mitglied: z.B. 1234, 987, ...
Saison: z.B. 2020, >2015, 2017-2019, <2005, 2019-, ...
Zweigstelle: z.B. musterort, ...
Attribute: z.B. kabinett, !kabinett (alle außer kabinett), ...
Datum: z.B. 1.9., 15.9.-10.10., -15.10.2020, ...
Uhrzeit: z.B. 06:00-08:00, 18:00-, ...
Freitext: z.B. Lieferscheinnummern, "quw" (sucht nach dem Text "quw")"/>
|
<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. >73, <15, 17-18, 15-, >17,5, 62-75, ...<LineBreak/>
|
||||||
|
<Bold>Mitglied</Bold>: z.B. 1234, 987, ...<LineBreak/>
|
||||||
|
<Bold>Saison</Bold>: z.B. 2020, >2015, 2017-2019, <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"/>
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -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"/>
|
||||||
|
|||||||
@@ -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,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,45 +55,157 @@
|
|||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
</ListBox>
|
</ListBox>
|
||||||
<Button x:Name="AddButton" Content="" FontFamily="Segoe MDL2 Assets" FontSize="11" Padding="0,1.5,0,0"
|
<Button x:Name="AddButton" Content="" 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="" FontFamily="Segoe MDL2 Assets" FontSize="12" Padding="0,0,0,0" IsEnabled="False"
|
<Button x:Name="CopyButton" Content="" 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="" FontFamily="Segoe MDL2 Assets" FontSize="11" Padding="0,1.5,0,0" IsEnabled="False"
|
<Button x:Name="DeleteButton" Content="" 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"
|
||||||
<ProgressBar x:Name="ProgressBar" Margin="10,0,0,74" Height="27" Width="180"
|
Margin="110,95,10,10" Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Top"
|
||||||
VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
|
Checked="ConsiderModifiersInput_Changed" Unchecked="ConsiderModifiersInput_Changed"/>
|
||||||
<Button x:Name="ShowButton" Content="Vorschau" FontSize="14" Width="180" Margin="10,10,10,42" Height="27" Tag="Print" IsEnabled="False"
|
<CheckBox x:Name="ConsiderPenaltiesInput" Content="Pönalen bei Unterlieferungen (FB)"
|
||||||
Click="ShowButton_Click"
|
Margin="110,115,10,10" Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Top"
|
||||||
VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
|
Checked="ConsiderPenaltiesInput_Changed" Unchecked="ConsiderPenaltiesInput_Changed"/>
|
||||||
<Button x:Name="PrintButton" Content="Drucken" FontSize="14" Width="180" Margin="10,10,10,10" Height="27" Tag="Print" IsEnabled="False"
|
<CheckBox x:Name="ConsiderPenaltyInput" Content="Strafen bei Unterlieferungen (GA)"
|
||||||
Click="PrintButton_Click"
|
Margin="110,135,10,10" Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Top"
|
||||||
VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
|
Checked="ConsiderPenaltyInput_Changed" Unchecked="ConsiderPenaltyInput_Changed"/>
|
||||||
</Grid>
|
<CheckBox x:Name="ConsiderAutoInput" Content="Automatische Nachzeichnungen der GA"
|
||||||
</GroupBox>
|
Margin="110,155,10,10" Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Top"
|
||||||
|
Checked="ConsiderAutoInput_Changed" Unchecked="ConsiderAutoInput_Changed"/>
|
||||||
|
<Label Content="" FontFamily="Segoe MDL2 Assets" FontSize="16" Grid.Row="0" Grid.Column="2" Margin="108,175,10,10"/>
|
||||||
|
|
||||||
|
<Grid Grid.Column="1" Grid.ColumnSpan="2" VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="10,10,10,10">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="110"/>
|
||||||
|
<ColumnDefinition Width="27"/>
|
||||||
|
<ColumnDefinition Width="110"/>
|
||||||
|
<ColumnDefinition Width="27"/>
|
||||||
|
<ColumnDefinition Width="110"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<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="" 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="" 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="" 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>
|
||||||
|
|||||||
@@ -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() {
|
||||||
@@ -30,26 +42,121 @@ namespace Elwig.Windows {
|
|||||||
|
|
||||||
private void Update() {
|
private void Update() {
|
||||||
if (PaymentVariantList.SelectedItem is PaymentVar v) {
|
if (PaymentVariantList.SelectedItem is PaymentVar v) {
|
||||||
var locked = v.TransferDate != null || !v.TestVariant;
|
var locked = !v.TestVariant;
|
||||||
DeleteButton.IsEnabled = !locked;
|
DeleteButton.IsEnabled = !locked;
|
||||||
CalculateButton.IsEnabled = !locked;
|
CalculateButton.IsEnabled = !locked;
|
||||||
|
CommitButton.IsEnabled = !locked && !SeasonLocked;
|
||||||
|
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.Visibility = Visibility.Visible;
|
||||||
|
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 = "-";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,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) {
|
||||||
@@ -74,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;
|
||||||
}
|
}
|
||||||
@@ -94,6 +254,224 @@ namespace Elwig.Windows {
|
|||||||
await Generate(2);
|
await Generate(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void CommitButton_Click(object sender, RoutedEventArgs evt) {
|
||||||
|
if (PaymentVariantList.SelectedValue is not PaymentVar v)
|
||||||
|
return;
|
||||||
|
CommitButton.IsEnabled = false;
|
||||||
|
Mouse.OverrideCursor = Cursors.AppStarting;
|
||||||
|
try {
|
||||||
|
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;
|
||||||
|
RevertButton.IsEnabled = true;
|
||||||
|
await App.HintContextChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void RevertButton_Click(object sender, RoutedEventArgs evt) {
|
||||||
|
if (PaymentVariantList.SelectedValue is not PaymentVar v)
|
||||||
|
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;
|
||||||
|
Mouse.OverrideCursor = Cursors.AppStarting;
|
||||||
|
var b = new BillingVariant(v.Year, v.AvNr);
|
||||||
|
await b.Revert();
|
||||||
|
Mouse.OverrideCursor = null;
|
||||||
|
CommitButton.IsEnabled = true;
|
||||||
|
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) {
|
||||||
if (PaymentVariantList.SelectedItem is not PaymentVar v)
|
if (PaymentVariantList.SelectedItem is not PaymentVar v)
|
||||||
return;
|
return;
|
||||||
@@ -112,8 +490,9 @@ namespace Elwig.Windows {
|
|||||||
IEnumerable<Member> list = await members.ToListAsync();
|
IEnumerable<Member> list = await members.ToListAsync();
|
||||||
var data = await CreditNoteData.ForPaymentVariant(Context.CreditNoteRows, Context.Seasons, v.Year, v.AvNr);
|
var data = await CreditNoteData.ForPaymentVariant(Context.CreditNoteRows, Context.Seasons, v.Year, v.AvNr);
|
||||||
var payments = await Context.MemberPayments.Where(p => p.Year == v.Year && p.AvNr == v.AvNr).ToDictionaryAsync(c => c.MgNr);
|
var payments = await Context.MemberPayments.Where(p => p.Year == v.Year && p.AvNr == v.AvNr).ToDictionaryAsync(c => c.MgNr);
|
||||||
|
await Context.GetMemberAreaCommitmentBuckets(Year, 0);
|
||||||
using var doc = Document.Merge(list.Select(m =>
|
using var doc = Document.Merge(list.Select(m =>
|
||||||
new CreditNote(Context, payments[m.MgNr], data[m.MgNr])
|
new CreditNote(Context, payments[m.MgNr], data[m.MgNr], Context.GetMemberUnderDelivery(Year, m.MgNr).GetAwaiter().GetResult())
|
||||||
));
|
));
|
||||||
await doc.Generate(new Progress<double>(v => {
|
await doc.Generate(new Progress<double>(v => {
|
||||||
ProgressBar.Value = v;
|
ProgressBar.Value = v;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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() {
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
-- deletes for HelpersBillingTest
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
-- inserts for HelpersBillingTest
|
||||||
@@ -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
@@ -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>
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
curl -s "https://www.necronda.net/elwig/files/create.sql?v=13" -u "elwig:ganzGeheim123!" -o "Resources\Create.sql"
|
||||||
Reference in New Issue
Block a user